Skip to content

HOW TO :: ADD SUPPORT FOR A NEW EXPANSION

ditzy edited this page Jul 5, 2024 · 5 revisions

several areas of the code need to be updated when a new major game patch (expansion) is released. when making these changes, please attempt to make the change as similar to existing code as possible. existing expansions should serve as examples of what each change should look like. you should attempt to replicate the case of existing code (PascalCase or lowercase, usually) to maintain consistency with existing code/data.

the following checklist summarizes the necessary updates, but the sections below have more specific detail about each change.

CHECKLIST

  1. fork the project (unless you are a collaborator) and create a new branch off of main.
  2. (details) update the Territory enum.
  3. (details) update the Patch enum.
  4. (details) obtain and update the siren data.
  5. (details) obtain and update the bear data.
  6. (details) obtain and update the turtle data.
  7. (details) please please please test your update. check the details, i promise it's not as hard as it seems 🥺.
  8. push your branch and open a PR for us to pull your changes into main.

TERRITORY ENUM

  1. add new values for each hunt region in the new patch, to the Territory enum.
  2. add entries to the end of this conversion dictionary with the full display name of each new territory.
  3. add entries to this list with the number of instances for each new territory with more than one instance.
  4. update the date here with today's date. don't worry about the time as it's only there for multiple updates in one day.

PATCH ENUM

  1. add a new value for the patch, to the Patch enum.
  2. add an entry to the patch->territory dictionary referencing the new territories added above.
  3. add an entry to the patch emote dictionary with the Light Trains discord emote for the new patch.

SIREN HUNTS DATA

data needs to be added for the siren spawn points and hunt mark order, so the urls can be generated correctly. follow these steps to add the new data:

  1. go to the siren hunts webpage.
  2. make sure the patch you are adding is selected at the top:
    image
  3. open the dev console (ctrl+shift+i in chrome).
  4. run the following code in the dev console, to extract the spawn point information for the new patch:
    const patchData = $$('div.htt.zonecnt')
        .map(zone => {
            const marks = [...zone.querySelectorAll(':scope>h2')]
                .map(mark => mark.textContent)
    
            const img = zone.querySelector(':scope>img.zone')
            const spawns = [...zone.querySelectorAll(':scope>div.mark')]
                .map((spawn, i) => {
                    const getPosition = (pos, size) => {
                        const mapBorder = img.computedStyleMap().get('border').toString().split(' ')[0].slice(0, -2);
                        // new maps seem to be 42x42 units in size, so multiply the % by 42
                        return 42 * (pos - mapBorder + size / 2) / img.width
                        // return 42 * parseFloat(style.match(/(\d+(?:\.\d+)?)%/)[1]) / 100;
                    }
                    return {
                        symbol: 'abcdefghijklmnopqrstuvwxyz'[i],
                        position: {
                            x: getPosition(spawn.offsetLeft, spawn.offsetWidth),
                            y: getPosition(spawn.offsetTop, spawn.offsetHeight),
                        },
                    }
                })
    
            return {
                mapName: zone.id.slice(0, -1),
                marks: marks,
                spawns: spawns,
            }
        })
        .reduce((acc, map) => {
            if (map.mapName in acc.maps) return acc
            
            const mobListStr = map.marks.map(m => `"${m.toLowerCase()}"`).join(', ');
            acc.mobOrder.push(`{"map": "${map.mapName}", "mobs": [${mobListStr}]}`)
            
            acc.maps[map.mapName] = map.spawns
                .reduce((acc, spawn) => {
                    const x = spawn.position.x.toFixed(1).padStart(4, ' ');
                    const y = spawn.position.y.toFixed(1).padStart(4, ' ');
                    acc.push(`"${spawn.symbol}": {"x": "${x}", "y": "${y}"}`)
                    return acc
                }, [])
            return acc
        }, {mobOrder: [], maps: {}})
    
    const mobOrder = patchData.mobOrder.map(line => `\t\t\t${line}`).join(',\n');
    const maps = Object.keys(patchData.maps).map(mapName => {
        const formattedSpawns = patchData.maps[mapName].map(line => `\t\t\t\t${line}`).join(',\n')
        return `\t\t\t"${mapName}": {
    ${formattedSpawns}
    \t\t\t}`
    });
    const formattedData = `\t"patch acronym": {
    \t\t"mobOrder": [
    ${mobOrder}
    \t\t],
    \t\t"maps": {
    ${maps.join(',\n')}
    \t\t}
    \t}`
    console.log(formattedData)
  5. copy the output from the dev console and paste it directly into Siren.json after all other patches (at the end of the file).
    • heads up, you may need to actually, like, drag select the console output in order to copy it.
    • also, your IDE might try to format things. please leave the formatting as is in the console output. you may need to do some kind of "paste as plain text" in order to accomplish this.
  6. replace "patch acronym" with the acronym for the new patch (like "ew" for endwalker).
  7. replace all map names with their actual names, in lowercase.
    • siren lists map names with short names, but we need the full name in the json.
    • make sure to replace the map names in both the "mobOrder" list and the "maps" object.
  8. update the siren patch name dictionary with an entry for the new patch.

BEAR TOOLKIT DATA

in order to support bear, we need the mob ids for each of the new patch hunt marks.

  1. obtain the mob id for each hunt mark introduced by the new patch.
    • you can obtain this in many ways, but one option is using xivapi. use the search api as follows:
      https://xivapi.com/search?indexes=BNpcName&string=gurangatch
      
      replacing gurangatch with the name of each hunt mark, one at a time. the results will contain values like:
      {"ID":10631,"Icon":"\/c\/BNpcName.png","Name":"Gurangatch","Url":"\/BNpcName\/10631","UrlType":"BNpcName","_":"bnpcname","_Score":1}
      
      and you need to remember the "ID" field.
    • note: on rare occasions, there are multiple mobs with the same name, but only one will be a hunt mark we care about. it's probably the mob with the larger id number, so you can just go with that.
  2. edit the bear json file and add an entry for the new patch, at the bottom.
  3. for each hunt mark, add an entry mapping their properly capitalized name to the id found in step 1.
    • for any mobs with apostrophes (') in their name, remove the apostrophe and include a comment as can be seen here.
  4. update the bear patch name dictionary with an entry for the new patch.

TURTLE SCOUTER DATA

NOTE: turtle is not yet implemented, but instructions are being added as we develop it.

  1. go to the turtle scouter webpage.
  2. open the dev console (ctrl+shift+i in chrome).
  3. run the following code in the dev console, to extract the spawn point information for all patches:
    const turtleData = JSON
        .parse($('body>div#app').getAttribute('data-page'))
        .props.expac.map(exp => {
            return {
                id: exp.id,
                name: exp.abbreviation,
                mobs: exp.zones.flatMap(zone => {
                    return zone.mobs.map(m => {
                        return {id: m.id, name: m.name}
                    })
                }),
                maps: exp.zones.map(zone => {
                    return {
                        id: zone.id,
                        name: zone.name,
                        points: zone.spawn_points.map(p => {
                            return {
                                id: p.id,
                                x: p.x.padStart(5, ' '),
                                y: p.y.padStart(5, ' '),
                            }
                        }),
                    }
                }),
            }
        })
    
    const f = (indent, ls) => {
        ts = '\t'.repeat(indent)
        return ls
            .map(lines => lines.split('\n').map(line => ts + line).join('\n'))
            .join(',\n')
    }
    
    const patches = turtleData.flatMap(patch => {
        const mobs = patch.mobs.map(mob => {
            return `"${mob.name.toLowerCase()}" : ${mob.id}`
        })
        const maps = patch.maps.flatMap(map => {
            const points = map.points.map(p => `"${p.id}": {"x": "${p.x}", "y": "${p.y}"}`)
            return `"${map.name.toLowerCase()}": {
    \t"id": ${map.id},
    \t"points": {
    ${f(2, points)}
    \t}
    }`
        })
        return `"${patch.name.toLowerCase()}" : {
    \t"id": ${patch.id},
    \t"mobs": {
    ${f(2, mobs)}
    \t},
    \t"maps": {
    ${f(2, maps)}
    \t}
    }`
    })
    console.log(`{
    ${f(1, patches)}
    }`)
  4. copy the output from the dev console and replace the entire contents of TBD, with the copied text.
    • heads up, you may need to actually, like, drag select the console output in order to copy it.
    • also, your IDE might try to format things. please leave the formatting as is in the console output. you may need to do some kind of "paste as plain text" in order to accomplish this.
  5. update TBD with an entry for the new patch.

TESTING

testing can't actually be done until at least Hunt Helper is updated for the patch. but it really helps to also have Coordinate Importer, and these instructions assume you have both plugins.

  1. build your change and ensure Hunt Helper, Coordinate Importer, and Scout Helper are all enabled in game.
  2. go to siren hunts or bear toolkit and create a train for all mobs in the new patch.
  3. copy the locations out of the tracking site you chose (like by clicking "COPY ROUTES" in bear) and paste them into Coordinate Importer.
  4. use Coordinate Importer to import the coordinates into Hunt Helper.
  5. use Scout Helper to generate a link for siren and one for bear.
  6. check both links and make sure all mobs are represented and at the locations you specified in step 2.