17,376
edits
(Update JS - Support localization overrides) |
(Amend equipmentStat handling for resistances) |
||
Line 612: | Line 612: | ||
// TODO Handle dependentData | // TODO Handle dependentData | ||
} | } | ||
} | |||
getDataToModify(modCat) { | |||
switch (modCat) { | |||
case 'combatAreaCategories': | |||
case 'dungeons': | |||
case 'equipmentSlots': | |||
case 'modifiers': | |||
case 'shopUpgradeChains': | |||
case 'shopPurchases': | |||
return this.gameData[modCat]; | |||
case 'cookingCategories': | |||
const cookingSkill = this.getObjectByID(this.gameData.skillData, 'melvorD:Cooking', 'skillID'); | |||
return cookingSkill.data.categories; | |||
case 'fletchingRecipes': | |||
const fletchingSkill = this.getObjectByID(this.gameData.skillData, 'melvorD:Fletching', 'skillID'); | |||
return fletchingSkill.data.recipes; | |||
} | |||
return undefined; | |||
} | } | ||
applyDataModifications(modData) { | applyDataModifications(modData) { | ||
Line 623: | Line 641: | ||
const modCat = modDataKeys[modCatID]; | const modCat = modDataKeys[modCatID]; | ||
const catData = modData[modCat]; | const catData = modData[modCat]; | ||
if ( | const dataToModify = this.getDataToModify(modCat); | ||
if (dataToModify === undefined) { | |||
console.warn( | |||
`Could not apply data modification for category "${modCat}": Unable to retrieve category data to be modified` | |||
); | |||
} | |||
else { | |||
catData.forEach((modItem) => { | catData.forEach((modItem) => { | ||
const modObjID = modItem.id; | const modObjID = modItem.id; | ||
if (modObjID === undefined) { | if (modObjID === undefined) { | ||
console.warn( | console.warn( | ||
`Could not apply data modification: ID of object to be modified not found | `Could not apply data modification for category "${modCat}": ID of object to be modified not found` | ||
); | ); | ||
} else { | } else { | ||
const | const objToModify = this.getObjectByID(dataToModify, modObjID); | ||
if ( | if (objToModify === undefined) { | ||
console.warn( | console.warn( | ||
`Could not apply data modification: Object with ID "${modObjID}" not found for | `Could not apply data modification: Object with ID "${modObjID}" not found for ctaegory "${modCat}"` | ||
); | ); | ||
} | } | ||
else { | |||
switch (modCat) { | |||
case 'combatAreaCategories': | |||
// The 'areas' property of elements within the category data are ordered data | |||
objToModify.areas = this.combineOrderedData(objToModify.areas, modItem.areas.add); | |||
break; | |||
case 'shopPurchases': | |||
case 'shopUpgradeChains': | |||
// Modify the root upgrade ID of shop upgrade chains, and modify attributes of shop purchases | |||
const overrideKeys = { | |||
purchaseRequirements: { | |||
sourceKey: 'newRequirements', // Key that holds the data in the data package | |||
destKey: 'purchaseRequirementsOverrides', // Key to insert into within this.gameData | |||
subKey: 'requirements', // Sub-key containing the override data | |||
}, | |||
cost: { | |||
sourceKey: 'newCosts', | |||
destKey: 'costOverrides', | |||
subKey: 'cost', | |||
}, | |||
}; | |||
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 | |||
const destKey = overrideKey.destKey; | |||
if (objToModify[destKey] === undefined) { | |||
objToModify[destKey] = []; | |||
} | |||
modItem[k].forEach((gamemodeOverride) => { | |||
var newData = {}; | |||
newData.gamemodeID = gamemodeOverride.gamemodeID; | |||
newData[overrideKey.subKey] = gamemodeOverride[overrideKey.sourceKey]; | |||
objToModify[destKey].push(newData); | |||
}); | |||
} else { | |||
objToModify[k] = modItem[k]; | |||
} | |||
}); | }); | ||
break; | |||
case 'cookingCategories': | |||
// Append to the list of shop upgrade IDs for cooking utilities/categories | |||
case 'fletchingRecipes': | |||
// Append to alternativeCosts property of recipes (e.g. Arrow shafts) | |||
Object.keys(modItem) | |||
.filter((k) => k !== 'id') | |||
.forEach((k) => { | |||
if ((k === 'shopUpgradeIDs') || (k === 'alternativeCosts')) { | |||
if (objToModify[k] === undefined) { | |||
objToModify[k] = modItem[k]; | |||
} else { | |||
objToModify[k].push(...modItem[k]); | |||
} | } | ||
itemRules[ruleKey].forEach((itemDef) => { | } else { | ||
console.warn( | |||
`Could not apply data modification: Unhandled key "${k}" for category "${modCat}", object "${mobObjID}"` | |||
); | |||
} | |||
}); | |||
break; | |||
case 'dungeons': | |||
// Add gamemode specific data to dungeons | |||
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 (objToModify[k] === undefined) { | |||
objToModify[k] = []; | |||
} | |||
itemRules[ruleKey].forEach((itemDef) => { | |||
let gamemodeRewards = this.getObjectByID(objToModify[k], itemDef.gamemodeID, 'gamemodeID'); | |||
if (gamemodeRewards === undefined) { | |||
objToModify[k].push({ | |||
gamemodeID: itemDef.gamemodeID, | |||
itemIDs: itemDef.rewardItemIDs, | |||
}); | |||
} else { | |||
gamemodeRewards.push(...itemDef.rewardItemIDs); | |||
} | |||
}); | }); | ||
} else { | } 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 (objToModify[k] === undefined) { | |||
objToModify[k] = []; | |||
} | |||
objToModify[k].push(modItem[k]); | |||
} else { | } else { | ||
console.warn( | console.warn( | ||
`Could not apply data modification: | `Could not apply data modification: Unhandled key "${k}" for category "${modCat}", object "${modObjID}"` | ||
); | ); | ||
} | } | ||
}); | }); | ||
break; | |||
case 'modifiers': | |||
// Add modifier aliases to existing mod scopes | |||
if (objToModify.allowedScopes === undefined) { | |||
console.warn(`Could not apply data modification: Modifier with ID ${modObjID} not found or modifier has no scopes`); | |||
} else { | } else { | ||
modItem.allowedScopes.forEach((srcScope) => { | |||
// Find scope within modifier objToModify with matching scopes definition | |||
const srcScopeKeys = Object.keys(srcScope.scopes); | |||
objToModify.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]); | |||
} | |||
}); | |||
} | } | ||
}); | |||
}); | }); | ||
} | } | ||
}); | break; | ||
} | // case 'equipmentSlots': | ||
// TODO | |||
default: | |||
console.warn( | |||
`Could not apply data modification: Unhandled category "${modCat}"` | |||
); | |||
} | |||
} | } | ||
} | } | ||
}); | }); | ||
} | } | ||
} | } | ||
Line 1,064: | Line 1,009: | ||
const newStats = {}; | const newStats = {}; | ||
entity.forEach((stat) => { | entity.forEach((stat) => { | ||
if (newStats[ | let statKey = stat.key; | ||
newStats[ | if (stat.damageType !== undefined) { | ||
statKey += this.getLocalID(stat.damageType); | |||
} | |||
if (newStats[statKey] === undefined) { | |||
newStats[statKey] = stat.value; | |||
} else { | } else { | ||
newStats[ | newStats[statKey] += stat.value; | ||
} | } | ||
}); | }); |