17,101
edits
(Update to support 2023 Birthday event) |
(Update for v1.3) |
||
Line 7: | Line 7: | ||
# Open the browser console/developer mode (usually by hitting the F12 key for most browsers) | # Open the browser console/developer mode (usually by hitting the F12 key for most browsers) | ||
# Within the browser console, enter the following code then hit enter. If successful, the game data should appear within the console | # Within the browser console, enter the following code then hit enter. If successful, the game data should appear within the console | ||
# Copy the game data & update [[Module:GameData/data]] | # Copy the game data & update [[Module:GameData/data]], [[Module:GameData/data2]], [[Module:GameData/data3]] accordingly | ||
{{SpoilerBox|color=default|title=Code|text=<syntaxhighlight lang="javascript" line>class Wiki { | {{SpoilerBox|color=default|title=Code|text=<syntaxhighlight lang="javascript" line>class Wiki { | ||
Line 13: | Line 13: | ||
this.debugMode = false; | this.debugMode = false; | ||
this.prettyPrint = false; | this.prettyPrint = false; | ||
this.baseDir = | this.baseDir = '/assets/data/'; | ||
this.namespaces = { | this.namespaces = { | ||
melvorD: { displayName: | melvorD: { displayName: 'Demo', url: 'https://' + location.hostname + this.baseDir + 'melvorDemo.json' }, | ||
melvorF: { displayName: | melvorF: { displayName: 'Full Version', url: 'https://' + location.hostname + this.baseDir + 'melvorFull.json' }, | ||
melvorTotH: { displayName: | melvorTotH: { | ||
melvorAoD: { displayName: | displayName: 'Throne of the Herald', | ||
melvorBirthday2023: { displayName: | url: 'https://' + location.hostname + this.baseDir + 'melvorTotH.json', | ||
}, | |||
melvorAoD: { | |||
displayName: 'Atlas of Discovery', | |||
url: 'https://' + location.hostname + this.baseDir + 'melvorExpansion2.json', | |||
}, | |||
melvorBirthday2023: { | |||
displayName: 'Melvor Birthday 2023', | |||
url: 'https://' + location.hostname + this.baseDir + 'melvorBirthday2023.json', | |||
}, | |||
melvorItA: { | |||
displayName: 'Into the Abyss', | |||
url: 'https://' + location.hostname + this.baseDir + 'melvorItA.json', | |||
}, | |||
}; | }; | ||
// Check all required namespaces are registered, as there are still some bits of data extracted from in-game rather than the data packages | // Check all required namespaces are registered, as there are still some bits of data extracted from in-game rather than the data packages | ||
Line 25: | Line 38: | ||
const nsTest = game.registeredNamespaces.getNamespace(nsID); | const nsTest = game.registeredNamespaces.getNamespace(nsID); | ||
if (nsTest === undefined) { | if (nsTest === undefined) { | ||
throw new Error(`Namespace ${ nsID } (${ this.namespaces[nsID].displayName }) is not registered - Ensure you are signed in and have the expansion.`); | throw new Error( | ||
`Namespace ${nsID} (${this.namespaces[nsID].displayName}) is not registered - Ensure you are signed in and have the expansion.` | |||
); | |||
} | } | ||
}); | }); | ||
Line 32: | Line 47: | ||
// pages (Module:GameData then combines the data into a single structure upon | // pages (Module:GameData then combines the data into a single structure upon | ||
// initialization). | // initialization). | ||
this.maxPageBytes = 2*1024**2; // 2048KB | this.maxPageBytes = 2 * 1024 ** 2; // 2048KB | ||
this.printPages = [ | this.printPages = [ | ||
{ includeCategories: '*', destination: 'Module:GameData/data' }, | { includeCategories: '*', destination: 'Module:GameData/data' }, | ||
{ includeCategories: ['items', 'itemUpgrades', 'itemSynergies', ' | { | ||
includeCategories: ['items'], | |||
destination: 'Module:GameData/data2', | |||
}, | |||
{ | |||
includeCategories: [ | |||
'itemUpgrades', | |||
'itemSynergies', | |||
'modifiers', | |||
'shopPurchases', | |||
'realms', | |||
'damageTypes', | |||
'combatTriangleSets', | |||
'randomAbyssalGems', | |||
'randomFragments', | |||
'randomFiremakingOils', | |||
'ancientRelics', | |||
'attackSpells', | |||
'attacks', | |||
'combatPassives', | |||
'monsters', | |||
'bankSortOrder', | |||
'combatEffects', | |||
'combatEffectTemplates', | |||
'combatEffectGroups' | |||
], | |||
destination: 'Module:GameData/data3', | |||
}, | |||
]; | ]; | ||
Line 41: | Line 83: | ||
this.gameData = {}; | this.gameData = {}; | ||
this.skillDataInit = {}; | this.skillDataInit = {}; | ||
} | } | ||
async getWikiData() { | async getWikiData() { | ||
if (!isLoaded) { | if (!isLoaded) { | ||
Line 49: | Line 91: | ||
const ns = Object.keys(this.namespaces)[nsIdx]; | const ns = Object.keys(this.namespaces)[nsIdx]; | ||
const dataURL = this.namespaces[ns].url; | const dataURL = this.namespaces[ns].url; | ||
console.log(`URL: ${ dataURL }`); | console.log(`URL: ${dataURL}`); | ||
const dataPackage = await this.getDataPackage(dataURL); | const dataPackage = await this.getDataPackage(dataURL); | ||
if (dataPackage.namespace === undefined) { | if (dataPackage.namespace === undefined) { | ||
throw new Error(`Data package has no namespace: ${ dataURL }`); | throw new Error(`Data package has no namespace: ${dataURL}`); | ||
} | } else if (dataPackage.data === undefined) { | ||
throw new Error(`Data package has no data: ${dataURL}`); | |||
throw new Error(`Data package has no data: ${ dataURL }`); | |||
} | } | ||
console.log(`Obtained data for namespace ${ dataPackage.namespace }, ${ JSON.stringify(dataPackage.data).length.toLocaleString() } bytes`); | console.log( | ||
`Obtained data for namespace ${dataPackage.namespace}, ${JSON.stringify( | |||
dataPackage.data | |||
).length.toLocaleString()} bytes` | |||
); | |||
this.processDataPackage(dataPackage); | this.processDataPackage(dataPackage); | ||
console.log(`After transformation: ${ JSON.stringify(dataPackage.data).length.toLocaleString() } bytes`); | console.log(`After transformation: ${JSON.stringify(dataPackage.data).length.toLocaleString()} bytes`); | ||
} | } | ||
// All data packages should now be within this.gameData | // All data packages should now be within this.gameData | ||
Line 65: | Line 110: | ||
getGameVersion() { | getGameVersion() { | ||
const fileDOM = document.querySelector('#sidebar ul.nav-main'); | const fileDOM = document.querySelector('#sidebar ul.nav-main'); | ||
let fileVer = | let fileVer = 'Unknown'; | ||
if (fileDOM !== null && fileDOM.dataset !== undefined) { | if (fileDOM !== null && fileDOM.dataset !== undefined) { | ||
fileVer = fileDOM.dataset.fileVersion; | fileVer = fileDOM.dataset.fileVersion; | ||
Line 72: | Line 117: | ||
} | } | ||
getObjectByID(data, objectID, idKey = 'id') { | getObjectByID(data, objectID, idKey = 'id') { | ||
if | if (data !== undefined && objectID !== undefined) { | ||
return data.find((obj) => obj[idKey] === objectID); | return data.find((obj) => obj[idKey] === objectID); | ||
} | } | ||
Line 79: | Line 124: | ||
if (Array.isArray(page.includeCategories)) { | if (Array.isArray(page.includeCategories)) { | ||
return page.includeCategories; | return page.includeCategories; | ||
} | } else if (page.includeCategories === '*') { | ||
// Special value, include all categories other than those included within | // Special value, include all categories other than those included within | ||
// other pages | // other pages | ||
return Object.keys(this.gameData).filter((cat) => !this.printPages.some((p) => Array.isArray(p.includeCategories) && p.includeCategories.includes(cat))); | return Object.keys(this.gameData).filter( | ||
(cat) => !this.printPages.some((p) => Array.isArray(p.includeCategories) && p.includeCategories.includes(cat)) | |||
); | |||
} | } | ||
} | } | ||
escapeQuotes(data) { | escapeQuotes(data) { | ||
var newData = data.replace(/\\/g, '\\\\') | var newData = data.replace(/\\/g, '\\\\'); | ||
newData = newData.replace(/'/g, "\\'"); | newData = newData.replace(/'/g, "\\'"); | ||
newData = newData.replace(/"/g, '\\"'); | newData = newData.replace(/"/g, '\\"'); | ||
Line 94: | Line 140: | ||
formatJSONData(category, data) { | formatJSONData(category, data) { | ||
if (data === undefined) { | if (data === undefined) { | ||
console.warn(`dataFormatter: Data for category ${ category } is undefined`); | console.warn(`dataFormatter: Data for category ${category} is undefined`); | ||
return ''; | return ''; | ||
} | } | ||
Line 102: | Line 148: | ||
if (category === 'skillData') { | if (category === 'skillData') { | ||
return '"' + category + '":[' + data.map((x) => this.escapeQuotes(JSON.stringify(x))).join(",' ..\n'") + ']'; | return '"' + category + '":[' + data.map((x) => this.escapeQuotes(JSON.stringify(x))).join(",' ..\n'") + ']'; | ||
} | } else { | ||
return '"' + category + '":' + this.escapeQuotes(JSON.stringify(data)); | return '"' + category + '":' + this.escapeQuotes(JSON.stringify(data)); | ||
} | } | ||
Line 118: | Line 163: | ||
const inclCat = this.getCategoriesForPage(page); | const inclCat = this.getCategoriesForPage(page); | ||
inclCat.forEach((cat) => { | inclCat.forEach((cat) => { | ||
dataLengths.push | dataLengths.push({ | ||
page: page.destination, | page: page.destination, | ||
category: cat, | category: cat, | ||
length: this.formatJSONData(cat, this.gameData[cat]).length | length: this.formatJSONData(cat, this.gameData[cat]).length, | ||
} | }); | ||
}); | }); | ||
}); | }); | ||
Line 139: | Line 184: | ||
const inclCat = this.getCategoriesForPage(page); | const inclCat = this.getCategoriesForPage(page); | ||
let gameDataFiltered = {}; | let gameDataFiltered = {}; | ||
inclCat.forEach((cat) => gameDataFiltered[cat] = this.gameData[cat]); | inclCat.forEach((cat) => (gameDataFiltered[cat] = this.gameData[cat])); | ||
// Convert game data into a JSON string for export | // Convert game data into a JSON string for export | ||
Line 145: | Line 190: | ||
if (this.prettyPrint) { | if (this.prettyPrint) { | ||
dataText = JSON.stringify(gameDataFiltered, undefined, '\t'); | dataText = JSON.stringify(gameDataFiltered, undefined, '\t'); | ||
} | } else { | ||
dataText = JSON.stringify(gameDataFiltered); | dataText = JSON.stringify(gameDataFiltered); | ||
} | } | ||
console.log(`For page "${ page.destination }" (${ dataText.length.toLocaleString() } bytes):`); | console.log(`For page "${page.destination}" (${dataText.length.toLocaleString()} bytes):`); | ||
if (dataText.length > this.maxPageBytes) { | if (dataText.length > this.maxPageBytes) { | ||
console.warn(`Page "${ page.destination }" exceeds max page size of ${ (this.maxPageBytes / 1024).toLocaleString() }KB by ${ (dataText.length - this.maxPageBytes).toLocaleString() } bytes. Consider amending the printPages configuration to move some data categories from this page onto other pages.`) | console.warn( | ||
`Page "${page.destination}" exceeds max page size of ${(this.maxPageBytes / 1024).toLocaleString()}KB by ${( | |||
dataText.length - this.maxPageBytes | |||
).toLocaleString()} bytes. Consider amending the printPages configuration to move some data categories from this page onto other pages.` | |||
); | |||
} | } | ||
console.log(dataText); | console.log(dataText); | ||
}); | |||
} | } | ||
async getDataPackage(url) { | async getDataPackage(url) { | ||
Line 163: | Line 211: | ||
return await fetch(url, { | return await fetch(url, { | ||
method: 'GET', | method: 'GET', | ||
headers | headers, | ||
}).then(function(response) { | }).then(function (response) { | ||
if (!response.ok) { | if (!response.ok) { | ||
throw new Error(`Couldn't fetch data package from URL: ${ url }`); | throw new Error(`Couldn't fetch data package from URL: ${url}`); | ||
} | } | ||
return response.json(); | return response.json(); | ||
Line 187: | Line 235: | ||
Object.keys(packData).forEach((categoryName) => { | Object.keys(packData).forEach((categoryName) => { | ||
switch(categoryName) { | switch (categoryName) { | ||
case 'pages': | case 'pages': | ||
case 'steamAchievements': | case 'steamAchievements': | ||
Line 213: | Line 261: | ||
// Recursive call to ensure all data is transformed, regardless of its depth | // Recursive call to ensure all data is transformed, regardless of its depth | ||
dataNode.forEach((entity, idx) => this.transformDataNode(ns, categoryName, dataNode, idx)); | dataNode.forEach((entity, idx) => this.transformDataNode(ns, categoryName, dataNode, idx)); | ||
} | } else if (typeof dataNode === 'object' && dataNode !== null) { | ||
// Iterate properties of object, checking if each should be deleted or transformed | // Iterate properties of object, checking if each should be deleted or transformed | ||
Object.keys(dataNode).forEach((key) => { | Object.keys(dataNode).forEach((key) => { | ||
Line 220: | Line 267: | ||
if (this.isPropertyFiltered(categoryName, dataNode, key)) { | if (this.isPropertyFiltered(categoryName, dataNode, key)) { | ||
delete dataNode[key]; | delete dataNode[key]; | ||
} | } else if (typeof dataNode[key] === 'object' && dataNode[key] !== null) { | ||
// If an object (either an array or key/value store) is within the current | // If an object (either an array or key/value store) is within the current | ||
// object then we must traverse this too | // object then we must traverse this too | ||
this.transformDataNode(ns, categoryName, dataNode, key); | this.transformDataNode(ns, categoryName, dataNode, key); | ||
} | } else { | ||
// Transform property, if a transformation is defined below | // Transform property, if a transformation is defined below | ||
switch(key) { | switch (key) { | ||
case 'id': | case 'id': | ||
// Add namespace to ID if it isn't already | // Add namespace to ID if it isn't already | ||
dataNode[key] = this.getNamespacedID(ns, dataNode[key]); | const id = dataNode[key]; | ||
if (!Number.isInteger(id)) dataNode[key] = this.getNamespacedID(ns, dataNode[key]); | |||
break; | break; | ||
} | } | ||
Line 244: | Line 290: | ||
// Special case for skillData so that certain values initialized when the various Skill | // Special case for skillData so that certain values initialized when the various Skill | ||
// classes are initialized may be added here also | // classes are initialized may be added here also | ||
if | if (categoryName === 'skillData' && dataNode.skillID !== undefined && dataNode.data !== undefined) { | ||
// We are currently at the topmost level of a skill object | // We are currently at the topmost level of a skill object | ||
const gameSkill = game.skills.getObjectByID(dataNode.skillID); | const gameSkill = game.skills.getObjectByID(dataNode.skillID); | ||
// For every skill with mastery, add mastery checkpoint descriptions | // For every skill with mastery, add mastery checkpoint descriptions | ||
if (gameSkill instanceof SkillWithMastery && dataNode.data.masteryTokenID !== undefined && dataNode.data.masteryCheckpoints === undefined) { | if ( | ||
gameSkill instanceof SkillWithMastery && | |||
dataNode.data.masteryTokenID !== undefined && | |||
dataNode.data.masteryCheckpoints === undefined | |||
) { | |||
const localID = this.getLocalID(dataNode.skillID); | const localID = this.getLocalID(dataNode.skillID); | ||
dataNode.data.baseMasteryPoolCap = gameSkill.baseMasteryPoolCap; | dataNode.data.baseMasteryPoolCap = gameSkill.baseMasteryPoolCap; | ||
dataNode.data.masteryCheckpoints = []; | dataNode.data.masteryCheckpoints = []; | ||
masteryCheckpoints.forEach((pct, idx) => { | masteryCheckpoints.forEach((pct, idx) => { | ||
dataNode.data.masteryCheckpoints[idx] = this.getLangString('MASTERY_CHECKPOINT', `${ localID }_${ idx }`); | dataNode.data.masteryCheckpoints[idx] = this.getLangString('MASTERY_CHECKPOINT', `${localID}_${idx}`); | ||
}); | }); | ||
} | } | ||
Line 260: | Line 310: | ||
// Import other attributes varying by skill | // Import other attributes varying by skill | ||
let importKeys = []; | let importKeys = []; | ||
switch(dataNode.skillID) { | switch (dataNode.skillID) { | ||
case 'melvorD: | case 'melvorD:Mining': | ||
importKeys = [ | importKeys = ['baseInterval', 'baseRockHP', 'passiveRegenInterval']; | ||
dataNode.data.baseGemChance = 1; | |||
dataNode.data.rockTypes = loadedLangJson.MINING_TYPE; | |||
break; | break; | ||
case ' | case 'melvorItA:Harvesting': | ||
importKeys = [ | importKeys = [ | ||
'baseInterval', | 'baseInterval', | ||
' | 'baseVeinIntensity', | ||
'passiveRegenInterval' | 'passiveRegenInterval', | ||
'uniqueProductChance', | |||
'hpCheckpoints', | |||
]; | ]; | ||
break; | break; | ||
case 'melvorD:Smithing': | case 'melvorD:Smithing': | ||
Line 282: | Line 330: | ||
case 'melvorD:Runecrafting': | case 'melvorD:Runecrafting': | ||
case 'melvorD:Herblore': | case 'melvorD:Herblore': | ||
importKeys = [ | importKeys = ['baseInterval']; | ||
break; | break; | ||
case 'melvorD:Thieving': | case 'melvorD:Thieving': | ||
importKeys = [ | importKeys = ['baseInterval', 'baseStunInterval', 'itemChance', 'baseAreaUniqueChance']; | ||
break; | break; | ||
case 'melvorD:Summoning': | case 'melvorD:Summoning': | ||
importKeys = [ | importKeys = ['baseInterval']; | ||
const sumKeys = ['recipeGPCost', 'markLevels']; | |||
sumKeys.forEach((k) => (dataNode.data[k] = Summoning[k])); | |||
const sumKeys = [ | |||
sumKeys.forEach((k) => dataNode.data[k] = Summoning[k]); | |||
break; | break; | ||
case 'melvorD:Astrology': | case 'melvorD:Astrology': | ||
Line 314: | Line 345: | ||
'standardModifierLevels', | 'standardModifierLevels', | ||
'uniqueModifierLevels', | 'uniqueModifierLevels', | ||
' | 'abyssalModifierLevels', | ||
' | 'baseInterval', | ||
]; | ]; | ||
astKeys.forEach((k) => dataNode.data[k] = Astrology[k]); | astKeys.forEach((k) => (dataNode.data[k] = Astrology[k])); | ||
break; | break; | ||
case 'melvorD:Township': | case 'melvorD:Township': | ||
// Remap a number of keys from their in-game names | // Remap a number of keys from their in-game names | ||
const townKeys = [ | const townKeys = [ | ||
{from: 'BASE_STORAGE', to: 'baseStorage'}, | { from: 'BASE_STORAGE', to: 'baseStorage' }, | ||
{from: 'BASE_TAX_RATE', to: 'baseTaxRate'}, | { from: 'BASE_TAX_RATE', to: 'baseTaxRate' }, | ||
{from: 'DECREASED_BUILDING_COST_CAP', to: 'decreasedBuildingCostCap' }, | { from: 'DECREASED_BUILDING_COST_CAP', to: 'decreasedBuildingCostCap' }, | ||
{from: 'GP_PER_CITIZEN', to: 'gpPerCitizen'}, | { from: 'GP_PER_CITIZEN', to: 'gpPerCitizen' }, | ||
{from: 'MAX_WORSHIP', to: 'maxWorship'}, | { from: 'MAX_WORSHIP', to: 'maxWorship' }, | ||
{from: 'MINIMUM_HEALTH', to: 'minimumHealth'}, | { from: 'MINIMUM_HEALTH', to: 'minimumHealth' }, | ||
{from: 'populationForTier', to: 'populationForTier'}, | { from: 'populationForTier', to: 'populationForTier' }, | ||
{from: 'TICK_LENGTH', to: 'tickLength'}, | { from: 'TICK_LENGTH', to: 'tickLength' }, | ||
{from: 'RARE_SEASON_CHANCE', to: 'rareSeasonChance'}, | { from: 'RARE_SEASON_CHANCE', to: 'rareSeasonChance' }, | ||
{from: 'WORSHIP_CHANGE_COST', to: 'worshipChangeCost'}, | { from: 'WORSHIP_CHANGE_COST', to: 'worshipChangeCost' }, | ||
{from: 'WORSHIP_CHECKPOINTS', to: 'worshipCheckpoints'}, | { from: 'WORSHIP_CHECKPOINTS', to: 'worshipCheckpoints' }, | ||
{ from: 'BASE_MAX_HEALTH', to: 'baseMaxHealth' }, | |||
{ from: 'abyssalTierRequirements', to: 'abyssalTierRequirements' }, | |||
{ from: 'BASE_SOUL_STORAGE', to: 'baseSoulStorage' }, | |||
]; | ]; | ||
townKeys.forEach((k) => dataNode.data[k.to] = gameSkill[k.from]); | townKeys.forEach((k) => (dataNode.data[k.to] = gameSkill[k.from])); | ||
// Add task categories & localization of name | // Add task categories & localization of name | ||
const taskCategories = Array.from(new Set(gameSkill.tasks.tasks.allObjects.map((t) => t.category))); | const taskCategories = Array.from(new Set(gameSkill.tasks.tasks.allObjects.map((t) => t.category))); | ||
dataNode.data.taskCategories = taskCategories.map(( | dataNode.data.taskCategories = taskCategories.map((category) => ({ | ||
id: category.id, | |||
name: category.name, | |||
})); | |||
break; | break; | ||
} | } | ||
if (importKeys.length > 0) { | if (importKeys.length > 0) { | ||
importKeys.forEach((k) => dataNode.data[k] = gameSkill[k]); | importKeys.forEach((k) => (dataNode.data[k] = gameSkill[k])); | ||
} | } | ||
} | } | ||
Line 358: | Line 392: | ||
const packData = this.packData[namespace].data; | const packData = this.packData[namespace].data; | ||
if (packData === undefined) { | if (packData === undefined) { | ||
throw new Error(`Couldn't find data for package ${ namespace }`); | throw new Error(`Couldn't find data for package ${namespace}`); | ||
} | } | ||
// Add data within the game but outside of data packs | // Add data within the game but outside of data packs | ||
Line 366: | Line 400: | ||
let categoryData = packData[categoryName]; | let categoryData = packData[categoryName]; | ||
// Some data is adjusted before combining - do this here | // Some data is adjusted before combining - do this here | ||
if (['combatAreas', 'dungeons', 'slayerAreas'].includes(categoryName)) { | if (['combatAreas', 'dungeons', 'slayerAreas', 'abyssDepths'].includes(categoryName)) { | ||
// Add area type to each area object | // Add area type to each area object | ||
const areaTypes = { | const areaTypes = { | ||
combatAreas: 'combatArea', | |||
dungeons: 'dungeon', | |||
' | slayerAreas: 'slayerArea', | ||
} | abyssDepths: 'abyssDepth', | ||
}; | |||
const areaType = areaTypes[categoryName]; | const areaType = areaTypes[categoryName]; | ||
const newData = structuredClone(categoryData); | const newData = structuredClone(categoryData); | ||
newData.forEach((x) => x.type = areaType); | newData.forEach((x) => (x.type = areaType)); | ||
categoryData = newData; | categoryData = newData; | ||
} | } /*else if ( | ||
['ancientSpells', 'archaicSpells', 'auroraSpells', 'curseSpells', 'standardSpells'].includes(categoryName) | |||
) { | |||
// For spell books, add the spell type to each spell object. | // For spell books, add the spell type to each spell object. | ||
// Alt Magic spells are handled elsewhere, as they are within a skill object | // Alt Magic spells are handled elsewhere, as they are within a skill object | ||
const spellType = categoryName.replace('Spells', ''); | const spellType = categoryName.replace('Spells', ''); | ||
const newData = structuredClone(categoryData); | const newData = structuredClone(categoryData); | ||
newData.forEach((x) => x.spellBook = spellType); | newData.forEach((x) => (x.spellBook = spellType)); | ||
categoryData = newData; | categoryData = newData; | ||
} | }*/ else if (categoryName === 'golbinRaid') { | ||
} | } | ||
// Data must be pushed into the consoldiated data, rules for vary | // Data must be pushed into the consoldiated data, rules for vary | ||
// depending on the category in question | // depending on the category in question | ||
switch(categoryName) { | switch (categoryName) { | ||
case 'realms': | |||
case 'attackSpellbooks': | |||
case 'damageTypes': | |||
case 'equipmentSlots': | |||
case 'combatAreaCategories': | |||
case 'combatEffects': | |||
case 'combatEffectGroups': | |||
case 'combatEffectTables': | |||
case 'combatEffectTemplates': | |||
case 'combatTriangleSets': | |||
case 'masterPoolBonuses': | |||
case 'masteryLevelUnlocks': | |||
case 'masteryLevelBonuses': | |||
case 'masterPoolBonuses': | |||
case 'ancientRelics': | case 'ancientRelics': | ||
case ' | case 'attackSpells': | ||
case 'attackStyles': | case 'attackStyles': | ||
case 'attacks': | case 'attacks': | ||
Line 403: | Line 450: | ||
case 'curseSpells': | case 'curseSpells': | ||
case 'dungeons': | case 'dungeons': | ||
case 'strongholds': | |||
case 'abyssDepths': | |||
case 'gamemodes': | case 'gamemodes': | ||
case 'itemEffects': | case 'itemEffects': | ||
Line 410: | Line 459: | ||
case 'items': | case 'items': | ||
case 'lore': | case 'lore': | ||
case 'modifiers': | |||
case 'monsters': | case 'monsters': | ||
case 'pages': | case 'pages': | ||
Line 416: | Line 466: | ||
case 'randomGems': | case 'randomGems': | ||
case 'randomSuperiorGems': | case 'randomSuperiorGems': | ||
case 'randomAbyssalGems': | |||
case 'randomFragments': | |||
case 'randomFiremakingOils': | |||
case 'shopCategories': | case 'shopCategories': | ||
case 'shopPurchases': | case 'shopPurchases': | ||
case 'shopUpgradeChains': | case 'shopUpgradeChains': | ||
case 'skillLevelCapIncreases': | |||
case 'slayerAreas': | case 'slayerAreas': | ||
case ' | case 'slayerTaskCategories': | ||
case 'steamAchievements': | case 'steamAchievements': | ||
case 'tutorialStages': | case 'tutorialStages': | ||
Line 429: | Line 482: | ||
// Category doesn't exist yet in consolidated data, so create it | // Category doesn't exist yet in consolidated data, so create it | ||
this.gameData[categoryName] = categoryData; | this.gameData[categoryName] = categoryData; | ||
} | } else { | ||
this.gameData[categoryName].push(...categoryData); | this.gameData[categoryName].push(...categoryData); | ||
} | } | ||
break; | break; | ||
case 'ancientRelicsDisplayOrder': | |||
case 'bankSortOrder': | case 'bankSortOrder': | ||
case 'combatAreaCategoryOrder': | |||
case 'combatAreaDisplayOrder': | case 'combatAreaDisplayOrder': | ||
case 'dungeonDisplayOrder': | case 'dungeonDisplayOrder': | ||
case 'shopCategoryOrder': | case 'shopCategoryOrder': | ||
case 'shopDisplayOrder': | case 'shopDisplayOrder': | ||
case 'skillTreesDisplayOrder': | |||
case 'slayerAreaDisplayOrder': | case 'slayerAreaDisplayOrder': | ||
case 'tutorialStageOrder': | case 'tutorialStageOrder': | ||
Line 450: | Line 505: | ||
this.gameData[categoryName] = categoryData; | this.gameData[categoryName] = categoryData; | ||
this.gameData.golbinRaid.possibleModifiers = RaidManager.possibleModifiers; | this.gameData.golbinRaid.possibleModifiers = RaidManager.possibleModifiers; | ||
} | } else { | ||
Object.keys(categoryData).forEach((dataKey) => { | Object.keys(categoryData).forEach((dataKey) => { | ||
if ( | if ( | ||
this.gameData[categoryName][dataKey] === undefined || | |||
!Array.isArray(this.gameData[categoryName][dataKey]) | |||
) { | |||
// Property is undefined or isn't an array | // Property is undefined or isn't an array | ||
this.gameData[categoryName][dataKey] = categoryData[dataKey]; | this.gameData[categoryName][dataKey] = categoryData[dataKey]; | ||
} | } else { | ||
// Property is an array | // Property is an array | ||
this.gameData[categoryName][dataKey].push(...categoryData[dataKey]); | this.gameData[categoryName][dataKey].push(...categoryData[dataKey]); | ||
Line 482: | Line 538: | ||
Object.keys(skillData.data).forEach((dataKey) => { | Object.keys(skillData.data).forEach((dataKey) => { | ||
// Special case for Township item conversions | // Special case for Township item conversions | ||
if ( | if ( | ||
skillObj[dataKey] !== undefined && | |||
skillData.skillID === 'melvorD:Township' && | |||
dataKey === 'itemConversions' | |||
) { | |||
Object.keys(skillData.data[dataKey]).forEach((convKey) => { | Object.keys(skillData.data[dataKey]).forEach((convKey) => { | ||
skillData.data[dataKey][convKey].forEach((resource) => { | skillData.data[dataKey][convKey].forEach((resource) => { | ||
// Find the resource if it already exists within the combined data | // Find the resource if it already exists within the combined data | ||
const resourceIdx = skillObj[dataKey][convKey].findIndex((res) => res.resourceID === resource.resourceID); | const resourceIdx = skillObj[dataKey][convKey].findIndex( | ||
(res) => res.resourceID === resource.resourceID | |||
); | |||
if (resourceIdx === -1) { | if (resourceIdx === -1) { | ||
skillObj[dataKey][convKey].push(resource); | skillObj[dataKey][convKey].push(resource); | ||
} | } else { | ||
skillObj[dataKey][convKey][resourceIdx].items.push(...resource.items); | skillObj[dataKey][convKey][resourceIdx].items.push(...resource.items); | ||
} | } | ||
}) | }); | ||
}); | }); | ||
} | } else if ( | ||
Array.isArray(skillData.data[dataKey]) && | |||
skillData.data[dataKey].length > 0 && | |||
skillData.data[dataKey][0].insertAt !== undefined | |||
) { | |||
// Data is ordered, special handling applies | // Data is ordered, special handling applies | ||
skillObj[dataKey] = this.combineOrderedData(skillObj[dataKey], skillData.data[dataKey]); | skillObj[dataKey] = this.combineOrderedData(skillObj[dataKey], skillData.data[dataKey]); | ||
} | } else if (skillObj[dataKey] === undefined || !Array.isArray(skillObj[dataKey])) { | ||
// Property is undefined or isn't an array | // Property is undefined or isn't an array | ||
skillObj[dataKey] = skillData.data[dataKey]; | |||
} | } else { | ||
// Property is an array | // Property is an array | ||
skillObj[dataKey].push(...skillData.data[dataKey]); | |||
} | } | ||
}); | }); | ||
Line 512: | Line 574: | ||
break; | break; | ||
default: | default: | ||
console.warn(`Skipping unknown category while registering data package: ${ categoryName }`); | console.warn(`Skipping unknown category while registering data package: ${categoryName}`); | ||
break; | break; | ||
} | } | ||
Line 527: | Line 589: | ||
} | } | ||
applyDataModifications(modData) { | applyDataModifications(modData) { | ||
const modDataKeys = Object.keys(modData) | // TODO: Handle modifications for the following: | ||
// equipmentSlots - Currently tweaks passive slot requirements | |||
// pages - Not so important, this is unused data | |||
// damageTypes | |||
// skillData - Adjusts Township season modifiers to include ItA resources | |||
const modDataKeys = Object.keys(modData); | |||
for (const modCatID in modDataKeys) { | for (const modCatID in modDataKeys) { | ||
const modCat = modDataKeys[modCatID]; | const modCat = modDataKeys[modCatID]; | ||
const catData = modData[modCat]; | const catData = modData[modCat]; | ||
if ((modCat === 'shopUpgradeChains' | if (modCat === 'combatAreaCategories') { | ||
// The 'areas' property of elements within the category data are ordered data | |||
catData.forEach((modItem) => { | |||
const modObjID = modItem.id; | |||
if (modObjID === undefined) { | |||
console.warn( | |||
`Could not apply data modification: ID of object to be modified not found, category "${modCat}"` | |||
); | |||
} else { | |||
const modObj = this.getObjectByID(this.gameData[modCat], modObjID); | |||
if (modObj === undefined) { | |||
console.warn( | |||
`Could not apply data modification: Object with ID "${modObjID}" not found for category "${modCat}"` | |||
); | |||
} else { | |||
modObj.areas = this.combineOrderedData(modObj.areas, modItem.areas.add); | |||
} | |||
} | |||
}); | |||
} else if (modCat === 'shopUpgradeChains' || modCat === 'shopPurchases') { | |||
// Modify the root upgrade ID of shop upgrade chains, and modify attributes of shop purchases | // Modify the root upgrade ID of shop upgrade chains, and modify attributes of shop purchases | ||
catData.forEach((modItem) => { | catData.forEach((modItem) => { | ||
const modObjID = modItem.id; | const modObjID = modItem.id; | ||
if (modObjID === undefined) { | if (modObjID === undefined) { | ||
console.warn(`Could not apply data modification: ID of object to be modified not found, category "${ modCat }"`); | console.warn( | ||
} | `Could not apply data modification: ID of object to be modified not found, category "${modCat}"` | ||
); | |||
} else { | |||
const modObj = this.getObjectByID(this.gameData[modCat], modObjID); | const modObj = this.getObjectByID(this.gameData[modCat], modObjID); | ||
if (modObj === undefined) { | if (modObj === undefined) { | ||
console.warn(`Could not apply data modification: Object with ID "${ modObjID }" not found for category "${ modCat }"`); | console.warn( | ||
} | `Could not apply data modification: Object with ID "${modObjID}" not found for category "${modCat}"` | ||
); | |||
} else { | |||
const overrideKeys = { | const overrideKeys = { | ||
purchaseRequirements: { | purchaseRequirements: { | ||
sourceKey: 'newRequirements', // Key that holds the data in the data package | sourceKey: 'newRequirements', // Key that holds the data in the data package | ||
destKey: 'purchaseRequirementsOverrides', // Key to insert into within this.gameData | destKey: 'purchaseRequirementsOverrides', // Key to insert into within this.gameData | ||
subKey: 'requirements' // Sub-key containing the override data | subKey: 'requirements', // Sub-key containing the override data | ||
}, | }, | ||
cost: { | cost: { | ||
sourceKey: 'newCosts', | sourceKey: 'newCosts', | ||
destKey: 'costOverrides', | destKey: 'costOverrides', | ||
subKey: 'cost' | subKey: 'cost', | ||
} | }, | ||
}; | }; | ||
Object.keys(modItem).filter((k) => k !== 'id').forEach((k) => { | Object.keys(modItem) | ||
.filter((k) => k !== 'id') | |||
.forEach((k) => { | |||
const overrideKey = overrideKeys[k]; | |||
if (overrideKey !== undefined) { | |||
// Is an override specific to a gamemode, do not replace | |||
// the key's existing data | |||
modObj[destKey] = []; | const destKey = overrideKey.destKey; | ||
if (modObj[destKey] === undefined) { | |||
modObj[destKey] = []; | |||
} | |||
modItem[k].forEach((gamemodeOverride) => { | |||
var newData = {}; | |||
newData.gamemodeID = gamemodeOverride.gamemodeID; | |||
newData[overrideKey.subKey] = gamemodeOverride[overrideKey.sourceKey]; | |||
modObj[destKey].push(newData); | |||
}); | |||
} else { | |||
modObj[k] = modItem[k]; | |||
} | } | ||
}); | |||
} | } | ||
} | } | ||
}); | }); | ||
} | } else if (modCat === 'cookingCategories') { | ||
// Append to the list of shop upgrade IDs for cooking utilities/categories | // Append to the list of shop upgrade IDs for cooking utilities/categories | ||
catData.forEach((modItem) => { | catData.forEach((modItem) => { | ||
Line 586: | Line 674: | ||
const cookingSkill = this.getObjectByID(this.gameData.skillData, 'melvorD:Cooking', 'skillID'); | const cookingSkill = this.getObjectByID(this.gameData.skillData, 'melvorD:Cooking', 'skillID'); | ||
if (modObjID === undefined) { | if (modObjID === undefined) { | ||
console.warn(`Could not apply data modification: ID of object to be modified not found, category "${ modCat }"`); | console.warn( | ||
} | `Could not apply data modification: ID of object to be modified not found, category "${modCat}"` | ||
); | |||
} else if (cookingSkill === undefined) { | |||
console.warn('Could not apply data modification: Data for skill "melvorD:Cooking" not found'); | console.warn('Could not apply data modification: Data for skill "melvorD:Cooking" not found'); | ||
} | } else { | ||
const modObj = this.getObjectByID(cookingSkill.data.categories, modObjID); | const modObj = this.getObjectByID(cookingSkill.data.categories, modObjID); | ||
if (modObj === undefined) { | if (modObj === undefined) { | ||
console.warn(`Could not apply data modification: Object with ID "${ modObjID }" not found for category "${ modCat }"`); | console.warn( | ||
} | `Could not apply data modification: Object with ID "${modObjID}" not found for category "${modCat}"` | ||
); | |||
Object.keys(modItem).filter((k) => k !== 'id').forEach((k) => { | } else { | ||
Object.keys(modItem) | |||
.filter((k) => k !== 'id') | |||
modObj[k] | .forEach((k) => { | ||
if (k === 'shopUpgradeIDs') { | |||
if (modObj[k] === undefined) { | |||
modObj[k] = modItem[k]; | |||
} else { | |||
modObj[k].push(...modItem[k]); | |||
} | |||
} else { | |||
console.warn( | |||
`Could not apply data modification: Unhandled key "${k}" for category "${modCat}", object "${mobObjID}"` | |||
); | |||
} | } | ||
}); | |||
} | } | ||
} | } | ||
}); | }); | ||
} | } else if (modCat === 'fletchingRecipes') { | ||
// Append to alternativeCosts property of recipes (e.g. Arrow shafts) | // Append to alternativeCosts property of recipes (e.g. Arrow shafts) | ||
catData.forEach((modItem) => { | catData.forEach((modItem) => { | ||
Line 620: | Line 710: | ||
const fletchingSkill = this.getObjectByID(this.gameData.skillData, 'melvorD:Fletching', 'skillID'); | const fletchingSkill = this.getObjectByID(this.gameData.skillData, 'melvorD:Fletching', 'skillID'); | ||
if (modObjID === undefined) { | if (modObjID === undefined) { | ||
console.warn(`Could not apply data modification: ID of object to be modified not found, category "${ modCat }"`); | console.warn( | ||
} | `Could not apply data modification: ID of object to be modified not found, category "${modCat}"` | ||
); | |||
} else if (fletchingSkill === undefined) { | |||
console.warn('Could not apply data modification: Data for skill "melvorD:Fletching" not found'); | console.warn('Could not apply data modification: Data for skill "melvorD:Fletching" not found'); | ||
} | } else { | ||
const modObj = this.getObjectByID(fletchingSkill.data.recipes, modObjID); | const modObj = this.getObjectByID(fletchingSkill.data.recipes, modObjID); | ||
if (modObj === undefined) { | if (modObj === undefined) { | ||
console.warn(`Could not apply data modification: Object with ID "${ modObjID }" not found for category "${ modCat }"`); | console.warn( | ||
} | `Could not apply data modification: Object with ID "${modObjID}" not found for category "${modCat}"` | ||
); | |||
Object.keys(modItem).filter((k) => k !== 'id').forEach((k) => { | } else { | ||
Object.keys(modItem) | |||
.filter((k) => k !== 'id') | |||
modObj[k] | .forEach((k) => { | ||
if (k === 'alternativeCosts') { | |||
if (modObj[k] === undefined) { | |||
modObj[k] = modItem[k]; | |||
} else { | |||
modObj[k].push(...modItem[k]); | |||
} | |||
} else { | |||
console.warn( | |||
`Could not apply data modification: Unhandled key "${k}" for category "${modCat}", object "${mobObjID}"` | |||
); | |||
} | } | ||
}); | |||
} | } | ||
} | } | ||
}); | }); | ||
} | } else if (modCat === 'dungeons') { | ||
catData.forEach((modItem) => { | catData.forEach((modItem) => { | ||
const modObjID = modItem.id; | const modObjID = modItem.id; | ||
if (modObjID === undefined) { | if (modObjID === undefined) { | ||
console.warn(`Could not apply data modification: ID of object to be modified not found, category "${ modCat }"`); | console.warn( | ||
} | `Could not apply data modification: ID of object to be modified not found, category "${modCat}"` | ||
); | |||
} else { | |||
const modObj = this.getObjectByID(this.gameData.dungeons, modObjID); | const modObj = this.getObjectByID(this.gameData.dungeons, modObjID); | ||
if (modObj === undefined) { | if (modObj === undefined) { | ||
console.warn(`Could not apply data modification: Object with ID "${ modObjID }" not found for category "${ modCat }"`); | console.warn( | ||
`Could not apply data modification: Object with ID "${modObjID}" not found for category "${modCat}"` | |||
); | |||
} else { | |||
Object.keys(modItem) | |||
.filter((k) => k !== 'id') | |||
.forEach((k) => { | |||
if (k === 'gamemodeRewardItemIDs') { | |||
// Add gamemode specific item rewards to dungeon data | |||
const itemRules = modItem[k]; | |||
Object.keys(itemRules).forEach((ruleKey) => { | |||
if (ruleKey === 'add') { | |||
if (modObj[k] === undefined) { | |||
modObj[k] = []; | |||
} | |||
itemRules[ruleKey].forEach((itemDef) => { | |||
let gamemodeRewards = this.getObjectByID(modObj[k], itemDef.gamemodeID, 'gamemodeID'); | |||
if (gamemodeRewards === undefined) { | |||
modObj[k].push({ | |||
gamemodeID: itemDef.gamemodeID, | |||
itemIDs: itemDef.rewardItemIDs, | |||
}); | |||
} else { | |||
gamemodeRewards.push(...itemDef.rewardItemIDs); | |||
} | |||
}); | |||
} else { | |||
console.warn( | |||
`Could not apply data modification: Unknown rule for gamemode item rewards: "${ruleKey}", object "${modObjID}"` | |||
); | |||
} | |||
}); | |||
} else if (k === 'gamemodeEntryRequirements') { | |||
// Add or remove gamemode specific entry requirements to dungeon data | |||
if (modObj[k] === undefined) { | |||
modObj[k] = []; | |||
} | |||
modObj[k].push(modItem[k]); | |||
} else { | |||
console.warn( | |||
`Could not apply data modification: Unhandled key "${k}" for category "${modCat}", object "${modObjID}"` | |||
); | |||
} | |||
}); | |||
} | } | ||
else { | } | ||
}); | |||
} else if (modCat === 'modifiers') { | |||
catData.forEach((modItem) => { | |||
const modObjID = modItem.id; | |||
if (modObjID === undefined) { | |||
console.warn( | |||
`Could not apply data modification: ID of object to be modified not found, category "${modCat}"`); | |||
} else { | |||
// Find modifier definition | |||
const modParentObj = this.getObjectByID(this.gameData.modifiers, modObjID); | |||
if ((modParentObj === undefined) || (modParentObj.allowedScopes === undefined)) { | |||
console.warn(`Could not apply data modification: Modifier with ID ${modObjID} not found or modifier has no scopes`); | |||
} else { | |||
modItem.allowedScopes.forEach((srcScope) => { | |||
// Find scope within modifier modParentObj with matching scopes definition | |||
const srcScopeKeys = Object.keys(srcScope.scopes); | |||
modParentObj.allowedScopes.forEach((destScope) => { | |||
const destScopeKeys = Object.keys(destScope.scopes); | |||
const scopeMatch = ( | |||
srcScopeKeys.length === destScopeKeys.length | |||
&& srcScopeKeys.every((k) => destScope.scopes[k] !== undefined && srcScope.scopes[k] == destScope.scopes[k]) | |||
); | |||
if (scopeMatch) { | |||
// Scopes match - add aliases to modifier allowedScope definition | |||
const aliasKeys = ['posAliases', 'negAliases']; | |||
aliasKeys.forEach((aliasKey) => { | |||
if (srcScope[aliasKey] !== undefined) { | |||
if (destScope[aliasKey] === undefined) { | |||
destScope[aliasKey] = []; | |||
} | |||
destScope[aliasKey].push(...srcScope[aliasKey]); | |||
} | } | ||
}); | |||
} | } | ||
}); | |||
}); | }); | ||
} | } | ||
} | } | ||
}); | }); | ||
} | } else { | ||
console.warn(`Could not apply data modification: Unhandled category "${modCat}"`); | |||
console.warn(`Could not apply data modification: Unhandled category "${ modCat }"`); | |||
} | } | ||
} | } | ||
Line 713: | Line 846: | ||
game.registeredNamespaces.forEach((ns) => { | game.registeredNamespaces.forEach((ns) => { | ||
if (ns.isModded) { | if (ns.isModded) { | ||
throw new Error(`Modded namespace '${ ns.displayName }' found, all mods must be disabled before game data can be generated`); | throw new Error( | ||
} | `Modded namespace '${ns.displayName}' found, all mods must be disabled before game data can be generated` | ||
); | |||
} else { | |||
nsData.push(ns); | nsData.push(ns); | ||
} | } | ||
Line 721: | Line 855: | ||
this.gameData.namespaces = nsData; | this.gameData.namespaces = nsData; | ||
} | } | ||
if (this.gameData.currencies === undefined) { | |||
this.gameData.currencies = game.currencies.allObjects.map((c) => ({ | |||
id: c.id, | |||
name: c.name, | |||
type: c.type | |||
})); | |||
} | |||
// Melvor realm exists outside of data packages | |||
if (this.gameData.realms === undefined) { | |||
this.gameData.realms = game.realms | |||
.filter((r) => r.id === 'melvorD:Melvor') | |||
.map((r) => ({ | |||
id: r.id, | |||
name: r.name, | |||
unlockRequirements: r.unlockRequirements | |||
})); | |||
} | |||
// Normal damage type exisst outside of data packages | |||
if (this.gameData.damageTypes === undefined) { | |||
this.gameData.damageTypes = game.damageTypes | |||
.filter((d) => d.id === 'melvorD:Normal') | |||
.map((d) => ({ | |||
id: d.id, | |||
name: d.name, | |||
resistanceCap: d._resistanceCap, | |||
resistanceName: d.resistanceName | |||
})); | |||
} | |||
/** | |||
if (this.gameData.combatTriangles === undefined) { | if (this.gameData.combatTriangles === undefined) { | ||
const ctData = []; | const ctData = []; | ||
Line 730: | Line 893: | ||
this.gameData.combatTriangles = ctData; | this.gameData.combatTriangles = ctData; | ||
} | } | ||
*/ | |||
/** | |||
if (this.gameData.masteryCheckpoints === undefined) { | if (this.gameData.masteryCheckpoints === undefined) { | ||
this.gameData.masteryCheckpoints = masteryCheckpoints; | this.gameData.masteryCheckpoints = masteryCheckpoints; | ||
} | } | ||
*/ | |||
if (this.gameData.combatAreaDifficulties === undefined) { | if (this.gameData.combatAreaDifficulties === undefined) { | ||
this.gameData.combatAreaDifficulties = CombatAreaMenuElement.difficulty.map((i) => i.name); | this.gameData.combatAreaDifficulties = CombatAreaMenuElement.difficulty.map((i) => i.name); | ||
} | } | ||
/** | |||
if (this.gameData.equipmentSlots === undefined) { | if (this.gameData.equipmentSlots === undefined) { | ||
const slotIDs = Object.keys(EquipmentSlots).filter((slotID) => !isNaN(parseInt(slotID))); | const slotIDs = Object.keys(EquipmentSlots).filter((slotID) => !isNaN(parseInt(slotID))); | ||
this.gameData.equipmentSlots = slotIDs.map((slotID) => ({id: EquipmentSlots[slotID], name: this.getLangString('EQUIP_SLOT', slotID)})); | this.gameData.equipmentSlots = slotIDs.map((slotID) => ({ | ||
id: EquipmentSlots[slotID], | |||
name: this.getLangString('EQUIP_SLOT', slotID), | |||
})); | |||
} | } | ||
*/ | |||
if (this.gameData.attackTypes === undefined) { | if (this.gameData.attackTypes === undefined) { | ||
this.gameData.attackTypes = AttackTypeID; | this.gameData.attackTypes = AttackTypeID; | ||
} | } | ||
/** | |||
if (this.gameData.slayerTiers === undefined) { | if (this.gameData.slayerTiers === undefined) { | ||
const newData = structuredClone(SlayerTask.data) | const newData = structuredClone(SlayerTask.data); | ||
newData.forEach((tier) => delete tier.engDisplay); | newData.forEach((tier) => delete tier.engDisplay); | ||
this.gameData.slayerTiers = newData; | this.gameData.slayerTiers = newData; | ||
} | } | ||
*/ | |||
/** | |||
if (this.gameData.modifierData === undefined && modifierData !== undefined) { | if (this.gameData.modifierData === undefined && modifierData !== undefined) { | ||
var wikiModData = {}; | var wikiModData = {}; | ||
Line 762: | Line 936: | ||
} | } | ||
wikiModData[modK][k] = funcName; | wikiModData[modK][k] = funcName; | ||
} | } else if (k === 'langDescription') { | ||
wikiModData[modK]['description'] = mod[k]; | wikiModData[modK]['description'] = mod[k]; | ||
} | } else if (k !== 'description') { | ||
wikiModData[modK][k] = mod[k]; | wikiModData[modK][k] = mod[k]; | ||
} | } | ||
Line 773: | Line 945: | ||
this.gameData.modifierData = wikiModData; | this.gameData.modifierData = wikiModData; | ||
} | } | ||
*/ | |||
} | } | ||
combineOrderedData(existingData, newData) { | combineOrderedData(existingData, newData) { | ||
Line 780: | Line 953: | ||
if (existingData === undefined) { | if (existingData === undefined) { | ||
resultData = []; | resultData = []; | ||
} | } else { | ||
resultData = structuredClone(existingData); | resultData = structuredClone(existingData); | ||
} | } | ||
newData.forEach((orderData) => { | newData.forEach((orderData) => { | ||
switch(orderData.insertAt) { | switch (orderData.insertAt) { | ||
case 'Start': | case 'Start': | ||
resultData.splice(0, 0, ...orderData.ids); | resultData.splice(0, 0, ...orderData.ids); | ||
Line 795: | Line 967: | ||
const beforeIdx = resultData.findIndex((item) => item === orderData.beforeID); | const beforeIdx = resultData.findIndex((item) => item === orderData.beforeID); | ||
if (beforeIdx === -1) { | if (beforeIdx === -1) { | ||
throw new Error(`Couldn't insert before: Item ${ orderData.beforeID } is not in the array.`); | throw new Error(`Couldn't insert before: Item ${orderData.beforeID} is not in the array.`); | ||
} | } | ||
resultData.splice(beforeIdx, 0, ...orderData.ids); | resultData.splice(beforeIdx, 0, ...orderData.ids); | ||
Line 802: | Line 974: | ||
const afterIdx = resultData.findIndex((item) => item === orderData.afterID); | const afterIdx = resultData.findIndex((item) => item === orderData.afterID); | ||
if (afterIdx === -1) { | if (afterIdx === -1) { | ||
throw new Error(`Couldn't insert after: Item ${ orderData.afterID } is not in the array.`); | throw new Error(`Couldn't insert after: Item ${orderData.afterID} is not in the array.`); | ||
} | } | ||
resultData.splice(afterIdx + 1, 0, ...orderData.ids); | resultData.splice(afterIdx + 1, 0, ...orderData.ids); | ||
Line 814: | Line 986: | ||
// Returns true if the property is to be removed, false if it is to be retained | // Returns true if the property is to be removed, false if it is to be retained | ||
isPropertyFiltered(entityType, entity, propertyName) { | isPropertyFiltered(entityType, entity, propertyName) { | ||
switch(propertyName) { | switch (propertyName) { | ||
case 'media': | case 'media': | ||
case 'altMedia': | case 'altMedia': | ||
Line 841: | Line 1,013: | ||
if (entityType === 'items') { | if (entityType === 'items') { | ||
return entity.tier === 'none'; | return entity.tier === 'none'; | ||
} | } else { | ||
return false; | return false; | ||
} | } | ||
Line 853: | Line 1,024: | ||
// Returns undefined if the property has no transformation | // Returns undefined if the property has no transformation | ||
transformProperty(entityType, entity, propertyName, namespace) { | transformProperty(entityType, entity, propertyName, namespace) { | ||
switch(propertyName) { | switch (propertyName) { | ||
case 'langHint': | case 'langHint': | ||
case 'langCustomDescription': | case 'langCustomDescription': | ||
Line 862: | Line 1,033: | ||
if (newStats[stat.key] === undefined) { | if (newStats[stat.key] === undefined) { | ||
newStats[stat.key] = stat.value; | newStats[stat.key] = stat.value; | ||
} | } else { | ||
newStats[stat.key] += stat.value; | newStats[stat.key] += stat.value; | ||
} | } | ||
Line 871: | Line 1,041: | ||
if (entityType !== 'skillData') { | if (entityType !== 'skillData') { | ||
return undefined; | return undefined; | ||
} | } else { | ||
const newData = structuredClone(entity); | const newData = structuredClone(entity); | ||
newData.forEach((i) => { | newData.forEach((i) => { | ||
Line 884: | Line 1,053: | ||
} | } | ||
langApply(parentNode, nodeKey, isSkill) { | langApply(parentNode, nodeKey, isSkill) { | ||
const nodeName = | const nodeName = isSkill ? parentNode[nodeKey].skillID : nodeKey; | ||
const altMagicDescIDKey = function(data) { | const altMagicDescIDKey = function (data) { | ||
// Accepts an Alt. Magic spell object, returns the ID format for that spell | // Accepts an Alt. Magic spell object, returns the ID format for that spell | ||
// Using a function for this as some spells (e.g. Superheat) have bespoke logic | // Using a function for this as some spells (e.g. Superheat) have bespoke logic | ||
Line 892: | Line 1,061: | ||
return 'HOLY_INVOCATION'; | return 'HOLY_INVOCATION'; | ||
} | } | ||
switch(data.specialCost.type) { | switch (data.specialCost.type) { | ||
case 'BarIngredientsWithCoal': | case 'BarIngredientsWithCoal': | ||
return 'SUPERHEAT'; | return 'SUPERHEAT'; | ||
Line 911: | Line 1,080: | ||
chainName: 'chainNameLang', | chainName: 'chainNameLang', | ||
defaultDescription: 'descriptionLang', | defaultDescription: 'descriptionLang', | ||
defaultName: 'defaultNameLang' | defaultName: 'defaultNameLang', | ||
}; | }; | ||
const langPropName = propToLang[dataKey]; | const langPropName = propToLang[dataKey]; | ||
Line 920: | Line 1,089: | ||
} | } | ||
} | } | ||
} | }; | ||
const itemDesc = (data) => { | const itemDesc = (data) => { | ||
const item = game.items.getObjectByID(data.id); | const item = game.items.getObjectByID(data.id); | ||
if (item !== undefined) { | if (item !== undefined && item.hasDescription) { | ||
return item.description; | |||
} else return ''; | |||
}; | |||
const shopPurchaseDesc = (data) => { | |||
const purchase = game.shop.purchases.getObjectByID(data.id); | |||
if (purchase !== undefined) { | |||
return purchase.description; | |||
} else return ''; | |||
}; | |||
} | |||
const relicDesc = (data) => { | const relicDesc = (data) => { | ||
const relic = game.ancientRelics.getObjectByID(data.id); | const relic = game.ancientRelics.getObjectByID(data.id); | ||
Line 947: | Line 1,107: | ||
return relic.name; | return relic.name; | ||
} | } | ||
} | }; | ||
const passiveDesc = (data) => { | const passiveDesc = (data) => { | ||
const passive = game.combatPassives.getObjectByID(data.id); | const passive = game.combatPassives.getObjectByID(data.id); | ||
Line 953: | Line 1,113: | ||
return passive.description; | return passive.description; | ||
} | } | ||
} | }; | ||
const spAttDesc = (data) => { | const spAttDesc = (data) => { | ||
const spAtt = game.specialAttacks.getObjectByID(data.id); | const spAtt = game.specialAttacks.getObjectByID(data.id); | ||
Line 959: | Line 1,119: | ||
return spAtt.description; | return spAtt.description; | ||
} | } | ||
} | }; | ||
const tsWorshipName = (data) => { | const tsWorshipName = (data) => { | ||
const worship = game.township.worships.getObjectByID(data.id); | const worship = game.township.worships.getObjectByID(data.id); | ||
Line 965: | Line 1,125: | ||
return worship.name; | return worship.name; | ||
} | } | ||
} | }; | ||
const tsWorshipStatueName = (data) => { | const tsWorshipStatueName = (data) => { | ||
const worship = game.township.worships.getObjectByID(data.id); | const worship = game.township.worships.getObjectByID(data.id); | ||
Line 971: | Line 1,131: | ||
return worship.statueName; | return worship.statueName; | ||
} | } | ||
} | }; | ||
const attackSpellbooksName = (data) => { | |||
const book = game.attackSpellbooks.getObjectByID(data.id); | |||
if (book !== undefined) { | |||
return book.name; | |||
} | |||
}; | |||
const attackSpellName = (data) => { | |||
const spell = game.attackSpells.getObjectByID(data.id); | |||
if (spell !== undefined) { | |||
return spell.name; | |||
} | |||
}; | |||
const hasNoLangData = [ | const hasNoLangData = [ | ||
// Categories that contain no localized text. Supresses warnings about no lang data | // Categories that contain no localized text. Supresses warnings about no lang data | ||
'ancientRelicsDisplayOrder', | |||
'bankSortOrder', | 'bankSortOrder', | ||
'combatAreaDisplayOrder', | 'combatAreaDisplayOrder', | ||
'combatAreaCategoryOrder', | |||
'combatEffectTemplates', | |||
'combatEvents', | 'combatEvents', | ||
'dungeonDisplayOrder', | 'dungeonDisplayOrder', | ||
Line 983: | Line 1,158: | ||
'itemUpgrades', | 'itemUpgrades', | ||
'itmMonsters', | 'itmMonsters', | ||
'modifiers', // TODO Does have lang data, supressing warning for now | |||
'randomAbyssalGems', | |||
'randomFiremakingOils', | |||
'randomFragments', | |||
'randomGems', | 'randomGems', | ||
'randomSuperiorGems', | 'randomSuperiorGems', | ||
'slayerAreaDisplayOrder', | 'slayerAreaDisplayOrder', | ||
'slayerTaskCategories', // TODO Does have lang data, supressing warning for now | |||
'shopCategoryOrder', | 'shopCategoryOrder', | ||
'shopDisplayOrder', | 'shopDisplayOrder', | ||
'skillLevelCapIncreases', | |||
'skillTreesDisplayOrder', | |||
'spiderLairMonsters', | 'spiderLairMonsters', | ||
'stackingEffects' | 'stackingEffects', | ||
]; | ]; | ||
const langKeys = { | const langKeys = { | ||
realms: { | |||
name: { stringSpecial: ' | name: { key: 'REALM', idFormat: 'NAME_{ID}' }, | ||
}, | |||
damageTypes: { | |||
name: { idFormat: 'DAMAGE_TYPE_{ID}' }, | |||
}, | |||
combatTriangleSets: { | |||
name: { key: 'COMBAT_TRIANGLE_NAME', idFormat: 'NAME_{ID}' }, | |||
}, | |||
attackSpellbooks: { | |||
name: { stringSpecial: 'attackSpellbooksName' }, | |||
}, | }, | ||
attackSpells: { | |||
name: { | name: { stringSpecial: 'attackSpellName' }, | ||
}, | }, | ||
ancientRelics: { | |||
name: { | name: { stringSpecial: 'relicDesc' }, | ||
}, | }, | ||
attackStyles: { | attackStyles: { | ||
name: { key: 'COMBAT_MISC', idFormat: 'ATTACK_STYLE_NAME_{ID}' } | name: { key: 'COMBAT_MISC', idFormat: 'ATTACK_STYLE_NAME_{ID}' }, | ||
}, | }, | ||
attacks: { | attacks: { | ||
name: { key: 'SPECIAL_ATTACK_NAME' }, | name: { key: 'SPECIAL_ATTACK_NAME' }, | ||
description: { stringSpecial: 'spAttDesc' } | description: { stringSpecial: 'spAttDesc' }, | ||
}, | }, | ||
auroraSpells: { | auroraSpells: { | ||
name: { key: 'MAGIC', idFormat: 'AURORA_NAME_{ID}' } | name: { key: 'MAGIC', idFormat: 'AURORA_NAME_{ID}' }, | ||
}, | |||
combatAreaCategories: { | |||
name: { key: 'COMBAT_AREA_CATEGORY' } | |||
}, | }, | ||
combatAreas: { | combatAreas: { | ||
name: { key: 'COMBAT_AREA', idFormat: 'NAME_{ID}'} | name: { key: 'COMBAT_AREA', idFormat: 'NAME_{ID}' }, | ||
}, | |||
combatEffectGroups: { | |||
name: { idKey: 'nameLang' } | |||
}, | |||
combatEffects: { | |||
name: { idKey: 'nameLang' } | |||
}, | }, | ||
combatPassives: { | combatPassives: { | ||
name: { key: 'PASSIVES', idFormat: 'NAME_{ID}' }, | name: { key: 'PASSIVES', idFormat: 'NAME_{ID}' }, | ||
customDescription: { stringSpecial: 'passiveDesc' } | customDescription: { stringSpecial: 'passiveDesc' }, | ||
//customDescription: { key: 'PASSIVES', idFormat: 'DESC_{ID}' } | //customDescription: { key: 'PASSIVES', idFormat: 'DESC_{ID}' } | ||
}, | }, | ||
curseSpells: { | curseSpells: { | ||
name: { key: 'MAGIC', idFormat: 'CURSE_NAME_{ID}' } | name: { key: 'MAGIC', idFormat: 'CURSE_NAME_{ID}' }, | ||
}, | }, | ||
dungeons: { | dungeons: { | ||
name: { key: 'DUNGEON', idFormat: 'NAME_{ID}' } | name: { key: 'DUNGEON', idFormat: 'NAME_{ID}' }, | ||
}, | |||
abyssDepths: { | |||
name: { key: 'THE_ABYSS', idFormat: 'NAME_{ID}' }, | |||
}, | |||
strongholds: { | |||
name: { key: 'STRONGHOLD_NAME', idFormat: 'NAME_{ID}' }, | |||
}, | }, | ||
gamemodes: { | gamemodes: { | ||
Line 1,029: | Line 1,235: | ||
description: { key: 'GAMEMODES', idFormat: 'GAMEMODE_DESC_{ID}' }, | description: { key: 'GAMEMODES', idFormat: 'GAMEMODE_DESC_{ID}' }, | ||
// Gamemodes have an array of rules | // Gamemodes have an array of rules | ||
rules: { key: 'GAMEMODES', idFormat: 'GAMEMODE_RULES_{ID}_{NUM}' } | rules: { key: 'GAMEMODES', idFormat: 'GAMEMODE_RULES_{ID}_{NUM}' }, | ||
}, | }, | ||
items: { | items: { | ||
name: { key: 'ITEM_NAME' }, | name: { key: 'ITEM_NAME' }, | ||
customDescription: { stringSpecial: 'itemDesc', onlyIfExists: | customDescription: { stringSpecial: 'itemDesc', onlyIfExists: false }, | ||
}, | }, | ||
lore: { | lore: { | ||
title: { key: 'LORE', idFormat: 'TITLE_{ID}' } | title: { key: 'LORE', idFormat: 'TITLE_{ID}' }, | ||
}, | }, | ||
monsters: { | monsters: { | ||
name: { key: 'MONSTER_NAME' }, | name: { key: 'MONSTER_NAME' }, | ||
description: { key: 'MONSTER_DESCRIPTION' } | description: { key: 'MONSTER_DESCRIPTION' }, | ||
}, | }, | ||
pets: { | pets: { | ||
name: { key: 'PET_NAME' } | name: { key: 'PET_NAME' }, | ||
hint: { idKey: 'langHint' } | |||
}, | }, | ||
prayers: { | prayers: { | ||
name: { key: 'PRAYER', idFormat: 'PRAYER_NAME_{ID}' } | name: { key: 'PRAYER', idFormat: 'PRAYER_NAME_{ID}' }, | ||
}, | }, | ||
shopCategories: { | shopCategories: { | ||
name: { key: 'SHOP_CAT' } | name: { key: 'SHOP_CAT' }, | ||
}, | }, | ||
shopPurchases: { | shopPurchases: { | ||
customName: { key: 'SHOP_NAME', onlyIfExists: true }, | customName: { key: 'SHOP_NAME', onlyIfExists: true }, | ||
customDescription: { | customDescription: { stringSpecial: 'shopPurchaseDesc', onlyIfExists: false }, | ||
}, | }, | ||
shopUpgradeChains: { | shopUpgradeChains: { | ||
chainName: { keySpecial: 'shopChainKey', idSpecial: 'shopChainID' }, | chainName: { keySpecial: 'shopChainKey', idSpecial: 'shopChainID' }, | ||
defaultDescription: { keySpecial: 'shopChainKey', idSpecial: 'shopChainID' }, | defaultDescription: { keySpecial: 'shopChainKey', idSpecial: 'shopChainID' }, | ||
defaultName: { keySpecial: 'shopChainKey', idSpecial: 'shopChainID' } | defaultName: { keySpecial: 'shopChainKey', idSpecial: 'shopChainID' }, | ||
}, | }, | ||
slayerAreas: { | slayerAreas: { | ||
name: { key: 'SLAYER_AREA', idFormat: 'NAME_{ID}' }, | name: { key: 'SLAYER_AREA', idFormat: 'NAME_{ID}' }, | ||
areaEffectDescription: { key: 'SLAYER_AREA', idFormat: 'EFFECT_{ID}' | areaEffectDescription: { key: 'SLAYER_AREA', idFormat: 'EFFECT_{ID}' }, | ||
}, | }, | ||
skillData: { | skillData: { | ||
Line 1,075: | Line 1,278: | ||
// for all skills | // for all skills | ||
_root: { | _root: { | ||
name: { key: 'SKILL_NAME', idFormat: '{SKILLID}' | name: { key: 'SKILL_NAME', idFormat: '{SKILLID}' }, | ||
}, | }, | ||
customMilestones: { | customMilestones: { | ||
name: { key: 'MILESTONES', idKey: 'milestoneID' } | name: { key: 'MILESTONES', idKey: 'milestoneID' }, | ||
}, | }, | ||
masteryLevelUnlocks: { | masteryLevelUnlocks: { | ||
description: { key: 'MASTERY_BONUS', idKey: 'descriptionID', idFormat: '{SKILLID}_{ID}' } | description: { key: 'MASTERY_BONUS', idKey: 'descriptionID', idFormat: '{SKILLID}_{ID}' }, | ||
} | }, | ||
}, | }, | ||
Archaeology: { | Archaeology: { | ||
digSites: { | digSites: { | ||
name: { key: 'POI_NAME_Melvor' } | name: { key: 'POI_NAME_Melvor' }, | ||
} | }, | ||
// TODO Tool names | // TODO Tool names | ||
}, | }, | ||
Agility: { | Agility: { | ||
elitePillars: { | elitePillars: { | ||
name: { key: 'AGILITY', idFormat: 'PILLAR_NAME_{ID}' } | name: { key: 'AGILITY', idFormat: 'PILLAR_NAME_{ID}' }, | ||
}, | }, | ||
obstacles: { | obstacles: { | ||
name: { key: 'AGILITY', idFormat: 'OBSTACLE_NAME_{ID}' } | name: { key: 'AGILITY', idFormat: 'OBSTACLE_NAME_{ID}' }, | ||
}, | }, | ||
pillars: { | pillars: { | ||
name: { key: 'AGILITY', idFormat: 'PILLAR_NAME_{ID}' } | name: { key: 'AGILITY', idFormat: 'PILLAR_NAME_{ID}' }, | ||
} | }, | ||
}, | }, | ||
Astrology: { | Astrology: { | ||
recipes: { | recipes: { | ||
name: { key: 'ASTROLOGY', idFormat: 'NAME_{ID}' } | name: { key: 'ASTROLOGY', idFormat: 'NAME_{ID}' }, | ||
} | }, | ||
}, | }, | ||
Cartography: { | Cartography: { | ||
mapPortals: { _handler: 'mapPortals' }, | mapPortals: { _handler: 'mapPortals' }, | ||
travelEvents: { | travelEvents: { | ||
description: { key: 'TRAVEL_EVENT' } | description: { key: 'TRAVEL_EVENT' }, | ||
}, | }, | ||
worldMaps: { _handler: 'cartoMaps' } | worldMaps: { _handler: 'cartoMaps' }, | ||
//name: { key: 'WORLD_MAP_NAME' }, | |||
//pointsOfInterest: { _handler: 'mapPOI' } | |||
//name: { key: 'POI_NAME', idFormat: '{MAPID}_{ID}' }, | |||
//description: { key: 'POI_DESCRIPTION', idFormat: '{MAPID}_{ID}' } | |||
}, | |||
Cooking: { | |||
categories: { | |||
name: { idFormat: 'SKILL_CATEGORY_{SKILLID}_{ID}'} | |||
} | |||
}, | |||
Crafting: { | |||
categories: { | |||
name: { idFormat: 'SKILL_CATEGORY_{SKILLID}_{ID}'} | |||
} | |||
}, | }, | ||
Farming: { | Farming: { | ||
categories: { | categories: { | ||
description: { key: 'SKILL_CATEGORY', idFormat: '{SKILLID}_{ID}_description' }, | description: { key: 'SKILL_CATEGORY', idFormat: '{SKILLID}_{ID}_description' }, | ||
name: { idFormat: 'SKILL_CATEGORY_{SKILLID}_{ID}'}, | |||
seedNotice: { key: 'SKILL_CATEGORY', idFormat: '{SKILLID}_{ID}_seedNotice' }, | seedNotice: { key: 'SKILL_CATEGORY', idFormat: '{SKILLID}_{ID}_seedNotice' }, | ||
singularName: { key: 'SKILL_CATEGORY', idFormat: '{SKILLID}_{ID}_singular' } | singularName: { key: 'SKILL_CATEGORY', idFormat: '{SKILLID}_{ID}_singular' }, | ||
}, | |||
}, | |||
Fletching: { | |||
categories: { | |||
name: { idFormat: 'SKILL_CATEGORY_{SKILLID}_{ID}'} | |||
} | } | ||
}, | }, | ||
Fishing: { | Fishing: { | ||
areas: { | areas: { | ||
name: { key: 'FISHING', idFormat: 'AREA_NAME_{ID}' } | name: { key: 'FISHING', idFormat: 'AREA_NAME_{ID}' }, | ||
} | }, | ||
}, | }, | ||
Herblore: { | Herblore: { | ||
categories: { | |||
name: { idFormat: 'SKILL_CATEGORY_{SKILLID}_{ID}'} | |||
}, | |||
recipes: { | recipes: { | ||
name: { key: 'POTION_NAME' } | name: { key: 'POTION_NAME' }, | ||
} | }, | ||
}, | }, | ||
Magic: { | Magic: { | ||
altSpells: { | altSpells: { | ||
name: { key: 'MAGIC', idFormat: 'ALTMAGIC_NAME_{ID}' }, | name: { key: 'MAGIC', idFormat: 'ALTMAGIC_NAME_{ID}' }, | ||
description: { key: 'MAGIC', idSpecial: 'altMagicDesc' } | description: { key: 'MAGIC', idSpecial: 'altMagicDesc' }, | ||
} | }, | ||
}, | }, | ||
Mining: { | Mining: { | ||
categories: { | |||
name: { idFormat: 'MINING_TYPE_{ID}' } | |||
}, | |||
rockData: { | rockData: { | ||
name: { key: 'ORE_NAME' } | name: { key: 'ORE_NAME' }, | ||
}, | |||
}, | |||
Runecrafting: { | |||
categories: { | |||
name: { idFormat: 'SKILL_CATEGORY_{SKILLID}_{ID}'} | |||
} | } | ||
}, | }, | ||
Summoning: { | Summoning: { | ||
categories: { | |||
name: { idFormat: 'SKILL_CATEGORY_{SKILLID}_{ID}'} | |||
}, | |||
synergies: { | synergies: { | ||
customDescription: { key: 'SUMMONING_SYNERGY', idKey: 'summonIDs', idFormat: 'DESC_{ID0}_{ID1}', onlyIfExists: true } | customDescription: { | ||
} | key: 'SUMMONING_SYNERGY', | ||
idKey: 'summonIDs', | |||
idFormat: 'DESC_{ID0}_{ID1}', | |||
onlyIfExists: true, | |||
}, | |||
}, | |||
}, | }, | ||
Thieving: { | Thieving: { | ||
areas: { | areas: { | ||
name: { key: 'THIEVING', idFormat: 'AREA_NAME_{ID}' } | name: { key: 'THIEVING', idFormat: 'AREA_NAME_{ID}' }, | ||
}, | }, | ||
npcs: { | npcs: { | ||
name: { key: 'THIEVING', idFormat: 'NPC_NAME_{ID}' } | name: { key: 'THIEVING', idFormat: 'NPC_NAME_{ID}' }, | ||
} | }, | ||
}, | }, | ||
Township: { | Township: { | ||
biomes: { | biomes: { | ||
// Can't locate biome description localization, don't think this is exposed in game UI | // Can't locate biome description localization, don't think this is exposed in game UI | ||
name: { key: 'TOWNSHIP', idFormat: 'BIOME_{ID}' } | name: { key: 'TOWNSHIP', idFormat: 'BIOME_{ID}' }, | ||
}, | }, | ||
buildings: { | buildings: { | ||
// Building description has no localization, as it is unused | // Building description has no localization, as it is unused | ||
name: { key: 'TOWNSHIP', idFormat: 'BUILDING_{ID}' } | name: { key: 'TOWNSHIP', idFormat: 'BUILDING_{ID}' }, | ||
}, | }, | ||
jobs: { | jobs: { | ||
name: { key: 'TOWNSHIP', idFormat: 'JOB_{ID}' } | name: { key: 'TOWNSHIP', idFormat: 'JOB_{ID}' }, | ||
}, | }, | ||
resources: { | resources: { | ||
name: { key: 'TOWNSHIP', idFormat: 'RESOURCE_{ID}' } | name: { key: 'TOWNSHIP', idFormat: 'RESOURCE_{ID}' }, | ||
}, | }, | ||
tasks: { | tasks: { | ||
// name is not exposed in game UI, and has no localization | // name is not exposed in game UI, and has no localization | ||
// category is localized in transformDataNode | // category is localized in transformDataNode | ||
description: { key: 'TOWNSHIP_TASKS', idFormat: '{ID}_description' } | description: { key: 'TOWNSHIP_TASKS', idFormat: '{ID}_description' }, | ||
}, | }, | ||
worships: { | worships: { | ||
name: { stringSpecial: 'tsWorshipName' }, | name: { stringSpecial: 'tsWorshipName' }, | ||
statueName: { stringSpecial: 'tsWorshipStatueName' } | statueName: { stringSpecial: 'tsWorshipStatueName' }, | ||
} | }, | ||
}, | }, | ||
Woodcutting: { | Woodcutting: { | ||
trees: { | trees: { | ||
name: { key: 'TREE_NAME' } | name: { key: 'TREE_NAME' }, | ||
} | }, | ||
} | }, | ||
} | }, | ||
}; | }; | ||
Line 1,216: | Line 1,451: | ||
} | } | ||
langKeyData = langSkill; | langKeyData = langSkill; | ||
} | } else if (langKeys[nodeKey] !== undefined) { | ||
langKeyData = { _root: langKeys[nodeKey] }; | langKeyData = { _root: langKeys[nodeKey] }; | ||
} | } else if (!hasNoLangData.includes(nodeKey)) { | ||
console.warn('No lang key data found for ' + nodeKey); | console.warn('No lang key data found for ' + nodeKey); | ||
} | } | ||
Line 1,230: | Line 1,463: | ||
} | } | ||
if (!Array.isArray(dataToTranslate)) { | if (!Array.isArray(dataToTranslate)) { | ||
dataToTranslate = [ dataToTranslate ]; | dataToTranslate = [dataToTranslate]; | ||
} | } | ||
dataToTranslate.forEach((tData) => { | dataToTranslate.forEach((tData) => { | ||
Object.keys(langKeyData).forEach((langKey) => { | Object.keys(langKeyData).forEach((langKey) => { | ||
const targetData = | const targetData = langKey === '_root' ? tData : tData[langKey]; | ||
if (targetData !== undefined) { | if (targetData !== undefined) { | ||
const targetArr = | const targetArr = Array.isArray(targetData) ? targetData : [targetData]; | ||
targetArr.forEach((target) => { | targetArr.forEach((target) => { | ||
const handlerFunc = langKeyData[langKey]['_handler']; | const handlerFunc = langKeyData[langKey]['_handler']; | ||
if (handlerFunc !== undefined) { | if (handlerFunc !== undefined) { | ||
switch(handlerFunc) { | switch (handlerFunc) { | ||
case 'mapPortals': | case 'mapPortals': | ||
Object.keys(target).forEach((portalKey) => { | Object.keys(target).forEach((portalKey) => { | ||
Line 1,261: | Line 1,494: | ||
break; | break; | ||
} | } | ||
} | } else { | ||
Object.keys(langKeyData[langKey]).forEach((langPropID) => { | Object.keys(langKeyData[langKey]).forEach((langPropID) => { | ||
const langProp = langKeyData[langKey][langPropID]; | const langProp = langKeyData[langKey][langPropID]; | ||
Line 1,271: | Line 1,503: | ||
// The ID key can sometimes be an array of IDs (e.g. Summoning synergies) | // The ID key can sometimes be an array of IDs (e.g. Summoning synergies) | ||
langIDValue = target[langIDKey].map((id) => this.getLocalID((id ?? '').toString())); | langIDValue = target[langIDKey].map((id) => this.getLocalID((id ?? '').toString())); | ||
} | } else { | ||
langIDValue = this.getLocalID((target[langIDKey] ?? '').toString()); | langIDValue = this.getLocalID((target[langIDKey] ?? '').toString()); | ||
} | } | ||
Line 1,278: | Line 1,509: | ||
if (langProp.idSpecial !== undefined) { | if (langProp.idSpecial !== undefined) { | ||
// Use a special method to determine the ID format | // Use a special method to determine the ID format | ||
switch(langProp.idSpecial) { | switch (langProp.idSpecial) { | ||
case 'altMagicDesc': | case 'altMagicDesc': | ||
langIdent = altMagicDescIDKey(target); | langIdent = altMagicDescIDKey(target); | ||
Line 1,289: | Line 1,520: | ||
if (langIdent === undefined) { | if (langIdent === undefined) { | ||
langIdent = langIDValue; | langIdent = langIDValue; | ||
} | } else { | ||
// langIdent is in a specific format | // langIdent is in a specific format | ||
const langTemplate = {} | const langTemplate = {}; | ||
if (isSkill) { | if (isSkill) { | ||
langTemplate.SKILLID = this.getLocalID(parentNode[nodeKey].skillID); | langTemplate.SKILLID = this.getLocalID(parentNode[nodeKey].skillID); | ||
Line 1,300: | Line 1,530: | ||
langTemplate['ID' + idx] = this.getLocalID(val); | langTemplate['ID' + idx] = this.getLocalID(val); | ||
}); | }); | ||
} | } else { | ||
langTemplate.ID = langIDValue; | langTemplate.ID = langIDValue; | ||
} | } | ||
Line 1,312: | Line 1,541: | ||
if (langProp.keySpecial !== undefined) { | if (langProp.keySpecial !== undefined) { | ||
// Use a special method to determine the category key | // Use a special method to determine the category key | ||
switch(langProp.keySpecial) { | switch (langProp.keySpecial) { | ||
case 'shopChainKey': | case 'shopChainKey': | ||
langCategoryKey = shopChainPropKey(target, langPropID, 'category'); | langCategoryKey = shopChainPropKey(target, langPropID, 'category'); | ||
Line 1,326: | Line 1,555: | ||
if (this.debugMode) { | if (this.debugMode) { | ||
if (langString !== undefined) { | if (langString !== undefined) { | ||
console.debug('Set value of property ' + langPropID + '[' + num.toString() + '] for ' + langIdentFinal + ' in node ' + nodeName + ' to: ' + langString); | console.debug( | ||
} | 'Set value of property ' + | ||
langPropID + | |||
console.debug('No translation: property ' + langPropID + ' for ' + langIdentFinal + ' in node ' + nodeName); | '[' + | ||
num.toString() + | |||
'] for ' + | |||
langIdentFinal + | |||
' in node ' + | |||
nodeName + | |||
' to: ' + | |||
langString | |||
); | |||
} else { | |||
console.debug( | |||
'No translation: property ' + | |||
langPropID + | |||
' for ' + | |||
langIdentFinal + | |||
' in node ' + | |||
nodeName | |||
); | |||
} | } | ||
} | } | ||
}); | }); | ||
} | } else { | ||
let langString; | let langString; | ||
if (langProp.stringSpecial !== undefined) { | if (langProp.stringSpecial !== undefined) { | ||
// Use a custom function to determine the string | // Use a custom function to determine the string | ||
switch(langProp.stringSpecial) { | switch (langProp.stringSpecial) { | ||
case 'itemDesc': | case 'itemDesc': | ||
langString = itemDesc(target); | langString = itemDesc(target); | ||
break; | |||
case 'shopPurchaseDesc': | |||
langString = shopPurchaseDesc(target); | |||
break; | break; | ||
case 'passiveDesc': | case 'passiveDesc': | ||
Line 1,356: | Line 1,604: | ||
case 'tsWorshipStatueName': | case 'tsWorshipStatueName': | ||
langString = tsWorshipStatueName(target); | langString = tsWorshipStatueName(target); | ||
break; | |||
case 'attackSpellbooksName': | |||
langString = attackSpellbooksName(target); | |||
break; | |||
case 'attackSpellName': | |||
langString = attackSpellName(target); | |||
break; | break; | ||
} | } | ||
} | } else { | ||
langString = this.getLangString(langCategoryKey, langIdent); | langString = this.getLangString(langCategoryKey, langIdent); | ||
} | } | ||
Line 1,365: | Line 1,618: | ||
if (this.debugMode) { | if (this.debugMode) { | ||
if (langString !== undefined) { | if (langString !== undefined) { | ||
console.debug('Set value of property ' + langPropID + ' for ' + langIdent + ' in node ' + nodeName + ' to: ' + langString); | console.debug( | ||
} | 'Set value of property ' + | ||
langPropID + | |||
console.debug('No translation: property ' + langPropID + ' for ' + langIdent + ' in node ' + nodeName); | ' for ' + | ||
langIdent + | |||
' in node ' + | |||
nodeName + | |||
' to: ' + | |||
langString | |||
); | |||
} else { | |||
console.debug( | |||
'No translation: property ' + langPropID + ' for ' + langIdent + ' in node ' + nodeName | |||
); | |||
} | } | ||
} | } | ||
Line 1,382: | Line 1,645: | ||
} | } | ||
getLangString(key, identifier) { | getLangString(key, identifier) { | ||
if ( | let lookupVal = ''; | ||
if (key !== undefined) { | |||
lookupVal = key; | |||
} | } | ||
if (identifier !== undefined) { | |||
lookupVal += (lookupVal.length > 0 ? '_' : '') + identifier; | |||
} | } | ||
return loadedLangJson[lookupVal]; | |||
} | } | ||
getNamespacedID(namespace, ID) { | getNamespacedID(namespace, ID) { | ||
if (ID.indexOf(':') > 0) { | if (ID.indexOf(':') > 0) { | ||
return ID; | return ID; | ||
} | } else { | ||
return namespace + ':' + ID; | return namespace + ':' + ID; | ||
} | } | ||
} | } | ||
getLocalID(ID) { | getLocalID(ID) { | ||
if (ID.indexOf(':') > 0) { | if (ID !== undefined && ID.indexOf(':') > 0) { | ||
return ID.split(':').pop(); | return ID.split(':').pop(); | ||
} | } else { | ||
return ID; | return ID; | ||
} | } | ||
Line 1,407: | Line 1,670: | ||
} | } | ||
let wd = new Wiki; | let wd = new Wiki(); | ||
wd.printWikiData();</syntaxhighlight>}} | wd.printWikiData();</syntaxhighlight>}} |