generated from nhcarrigan/template
fix: resolve sync inflation, signature mismatch, CP accuracy, auto-buy cap, unlock hints
- #147: Guard all patch functions with hasChanged before incrementing sync counter to prevent inflation on no-op patches - #148: Clear stale HMAC signature after each boss fight so subsequent auto-saves do not send a mismatched signature - #146: Auto-unlock adventurer-specific upgrades in applyTick when their adventurer count > 0; show recruit hint in upgrade panel - #149: Add Essence/s row to resource bar dropdown - #150: Fix broken auto-quest CP reduce formula; centralise via computePartyCombatPower which applies all multipliers correctly - #151: Cap auto-buy at 100 for non-max-tier adventurers; max tier (highest level unlocked) remains uncapped - #152: Export computePartyCombatPower from tick, applying global upgrades, prestige, equipment, set bonuses, echo, crafted, and companion multipliers; use it in resource bar and boss panel
This commit is contained in:
@@ -642,6 +642,14 @@ const patchAdventurerStats = (state: GameState): number => {
|
||||
if (defaultAdventurer === undefined) {
|
||||
continue;
|
||||
}
|
||||
const hasChanged
|
||||
= savedAdventurer.baseCost !== defaultAdventurer.baseCost
|
||||
|| savedAdventurer.class !== defaultAdventurer.class
|
||||
|| savedAdventurer.combatPower !== defaultAdventurer.combatPower
|
||||
|| savedAdventurer.essencePerSecond !== defaultAdventurer.essencePerSecond
|
||||
|| savedAdventurer.goldPerSecond !== defaultAdventurer.goldPerSecond
|
||||
|| savedAdventurer.level !== defaultAdventurer.level
|
||||
|| savedAdventurer.name !== defaultAdventurer.name;
|
||||
savedAdventurer.baseCost = defaultAdventurer.baseCost;
|
||||
savedAdventurer.class = defaultAdventurer.class;
|
||||
savedAdventurer.combatPower = defaultAdventurer.combatPower;
|
||||
@@ -649,7 +657,9 @@ const patchAdventurerStats = (state: GameState): number => {
|
||||
savedAdventurer.goldPerSecond = defaultAdventurer.goldPerSecond;
|
||||
savedAdventurer.level = defaultAdventurer.level;
|
||||
savedAdventurer.name = defaultAdventurer.name;
|
||||
patched = patched + 1;
|
||||
if (hasChanged) {
|
||||
patched = patched + 1;
|
||||
}
|
||||
}
|
||||
return patched;
|
||||
};
|
||||
@@ -670,6 +680,15 @@ const patchQuestStats = (state: GameState): number => {
|
||||
if (defaultQuest === undefined) {
|
||||
continue;
|
||||
}
|
||||
const savedPrereqs = JSON.stringify(savedQuest.prerequisiteIds);
|
||||
const defaultPrereqs = JSON.stringify(defaultQuest.prerequisiteIds);
|
||||
const hasChanged
|
||||
= savedQuest.name !== defaultQuest.name
|
||||
|| savedQuest.description !== defaultQuest.description
|
||||
|| savedQuest.durationSeconds !== defaultQuest.durationSeconds
|
||||
|| savedPrereqs !== defaultPrereqs
|
||||
|| savedQuest.zoneId !== defaultQuest.zoneId
|
||||
|| savedQuest.combatPowerRequired !== defaultQuest.combatPowerRequired;
|
||||
savedQuest.name = defaultQuest.name;
|
||||
savedQuest.description = defaultQuest.description;
|
||||
savedQuest.durationSeconds = defaultQuest.durationSeconds;
|
||||
@@ -678,7 +697,9 @@ const patchQuestStats = (state: GameState): number => {
|
||||
if (defaultQuest.combatPowerRequired !== undefined) {
|
||||
savedQuest.combatPowerRequired = defaultQuest.combatPowerRequired;
|
||||
}
|
||||
patched = patched + 1;
|
||||
if (hasChanged) {
|
||||
patched = patched + 1;
|
||||
}
|
||||
}
|
||||
return patched;
|
||||
};
|
||||
@@ -689,6 +710,7 @@ const patchQuestStats = (state: GameState): number => {
|
||||
* @param state - The player's current game state (mutated in place).
|
||||
* @returns The number of boss entries whose stats were updated.
|
||||
*/
|
||||
/* eslint-disable-next-line complexity, max-statements -- Comparing many boss stat fields for change detection */
|
||||
const patchBossStats = (state: GameState): number => {
|
||||
const defaultBossMap = new Map(defaultBosses.map((boss) => {
|
||||
return [ boss.id, boss ] as const;
|
||||
@@ -699,6 +721,20 @@ const patchBossStats = (state: GameState): number => {
|
||||
if (defaultBoss === undefined) {
|
||||
continue;
|
||||
}
|
||||
const savedRewards = JSON.stringify(savedBoss.equipmentRewards);
|
||||
const defaultRewards = JSON.stringify(defaultBoss.equipmentRewards);
|
||||
const hasChanged
|
||||
= savedBoss.name !== defaultBoss.name
|
||||
|| savedBoss.description !== defaultBoss.description
|
||||
|| savedBoss.maxHp !== defaultBoss.maxHp
|
||||
|| savedBoss.damagePerSecond !== defaultBoss.damagePerSecond
|
||||
|| savedBoss.goldReward !== defaultBoss.goldReward
|
||||
|| savedBoss.essenceReward !== defaultBoss.essenceReward
|
||||
|| savedBoss.crystalReward !== defaultBoss.crystalReward
|
||||
|| savedRewards !== defaultRewards
|
||||
|| savedBoss.prestigeRequirement !== defaultBoss.prestigeRequirement
|
||||
|| savedBoss.zoneId !== defaultBoss.zoneId
|
||||
|| savedBoss.bountyRunestones !== defaultBoss.bountyRunestones;
|
||||
savedBoss.name = defaultBoss.name;
|
||||
savedBoss.description = defaultBoss.description;
|
||||
savedBoss.maxHp = defaultBoss.maxHp;
|
||||
@@ -710,7 +746,9 @@ const patchBossStats = (state: GameState): number => {
|
||||
savedBoss.prestigeRequirement = defaultBoss.prestigeRequirement;
|
||||
savedBoss.zoneId = defaultBoss.zoneId;
|
||||
savedBoss.bountyRunestones = defaultBoss.bountyRunestones;
|
||||
patched = patched + 1;
|
||||
if (hasChanged) {
|
||||
patched = patched + 1;
|
||||
}
|
||||
}
|
||||
return patched;
|
||||
};
|
||||
@@ -731,12 +769,20 @@ const patchZoneStats = (state: GameState): number => {
|
||||
if (defaultZone === undefined) {
|
||||
continue;
|
||||
}
|
||||
const hasChanged
|
||||
= savedZone.name !== defaultZone.name
|
||||
|| savedZone.description !== defaultZone.description
|
||||
|| savedZone.emoji !== defaultZone.emoji
|
||||
|| savedZone.unlockBossId !== defaultZone.unlockBossId
|
||||
|| savedZone.unlockQuestId !== defaultZone.unlockQuestId;
|
||||
savedZone.name = defaultZone.name;
|
||||
savedZone.description = defaultZone.description;
|
||||
savedZone.emoji = defaultZone.emoji;
|
||||
savedZone.unlockBossId = defaultZone.unlockBossId;
|
||||
savedZone.unlockQuestId = defaultZone.unlockQuestId;
|
||||
patched = patched + 1;
|
||||
if (hasChanged) {
|
||||
patched = patched + 1;
|
||||
}
|
||||
}
|
||||
return patched;
|
||||
};
|
||||
@@ -747,6 +793,7 @@ const patchZoneStats = (state: GameState): number => {
|
||||
* @param state - The player's current game state (mutated in place).
|
||||
* @returns The number of upgrade entries whose stats were updated.
|
||||
*/
|
||||
/* eslint-disable-next-line complexity -- Comparing many upgrade stat fields for change detection */
|
||||
const patchUpgradeStats = (state: GameState): number => {
|
||||
const defaultUpgradeMap = new Map(defaultUpgrades.map((upgrade) => {
|
||||
return [ upgrade.id, upgrade ] as const;
|
||||
@@ -757,6 +804,15 @@ const patchUpgradeStats = (state: GameState): number => {
|
||||
if (defaultUpgrade === undefined) {
|
||||
continue;
|
||||
}
|
||||
const hasChanged
|
||||
= savedUpgrade.name !== defaultUpgrade.name
|
||||
|| savedUpgrade.description !== defaultUpgrade.description
|
||||
|| savedUpgrade.target !== defaultUpgrade.target
|
||||
|| savedUpgrade.adventurerId !== defaultUpgrade.adventurerId
|
||||
|| savedUpgrade.multiplier !== defaultUpgrade.multiplier
|
||||
|| savedUpgrade.costGold !== defaultUpgrade.costGold
|
||||
|| savedUpgrade.costEssence !== defaultUpgrade.costEssence
|
||||
|| savedUpgrade.costCrystals !== defaultUpgrade.costCrystals;
|
||||
savedUpgrade.name = defaultUpgrade.name;
|
||||
savedUpgrade.description = defaultUpgrade.description;
|
||||
savedUpgrade.target = defaultUpgrade.target;
|
||||
@@ -767,7 +823,9 @@ const patchUpgradeStats = (state: GameState): number => {
|
||||
savedUpgrade.costGold = defaultUpgrade.costGold;
|
||||
savedUpgrade.costEssence = defaultUpgrade.costEssence;
|
||||
savedUpgrade.costCrystals = defaultUpgrade.costCrystals;
|
||||
patched = patched + 1;
|
||||
if (hasChanged) {
|
||||
patched = patched + 1;
|
||||
}
|
||||
}
|
||||
return patched;
|
||||
};
|
||||
@@ -778,6 +836,7 @@ const patchUpgradeStats = (state: GameState): number => {
|
||||
* @param state - The player's current game state (mutated in place).
|
||||
* @returns The number of equipment entries whose stats were updated.
|
||||
*/
|
||||
/* eslint-disable-next-line complexity, max-statements -- Comparing many equipment stat fields for change detection */
|
||||
const patchEquipmentStats = (state: GameState): number => {
|
||||
const defaultEquipmentMap = new Map(defaultEquipment.map((item) => {
|
||||
return [ item.id, item ] as const;
|
||||
@@ -788,6 +847,18 @@ const patchEquipmentStats = (state: GameState): number => {
|
||||
if (defaultItem === undefined) {
|
||||
continue;
|
||||
}
|
||||
const savedBonus = JSON.stringify(savedItem.bonus);
|
||||
const defaultBonus = JSON.stringify(defaultItem.bonus);
|
||||
const savedCost = JSON.stringify(savedItem.cost);
|
||||
const defaultCost = JSON.stringify(defaultItem.cost);
|
||||
const hasChanged
|
||||
= savedItem.name !== defaultItem.name
|
||||
|| savedItem.description !== defaultItem.description
|
||||
|| savedItem.type !== defaultItem.type
|
||||
|| savedItem.rarity !== defaultItem.rarity
|
||||
|| savedBonus !== defaultBonus
|
||||
|| savedCost !== defaultCost
|
||||
|| savedItem.setId !== defaultItem.setId;
|
||||
savedItem.name = defaultItem.name;
|
||||
savedItem.description = defaultItem.description;
|
||||
savedItem.type = defaultItem.type;
|
||||
@@ -799,7 +870,9 @@ const patchEquipmentStats = (state: GameState): number => {
|
||||
if (defaultItem.setId !== undefined) {
|
||||
savedItem.setId = defaultItem.setId;
|
||||
}
|
||||
patched = patched + 1;
|
||||
if (hasChanged) {
|
||||
patched = patched + 1;
|
||||
}
|
||||
}
|
||||
return patched;
|
||||
};
|
||||
@@ -820,6 +893,16 @@ const patchAchievementStats = (state: GameState): number => {
|
||||
if (defaultAchievement === undefined) {
|
||||
continue;
|
||||
}
|
||||
const savedCondition = JSON.stringify(savedAchievement.condition);
|
||||
const defaultCondition = JSON.stringify(defaultAchievement.condition);
|
||||
const savedReward = JSON.stringify(savedAchievement.reward);
|
||||
const defaultReward = JSON.stringify(defaultAchievement.reward);
|
||||
const hasChanged
|
||||
= savedAchievement.name !== defaultAchievement.name
|
||||
|| savedAchievement.description !== defaultAchievement.description
|
||||
|| savedAchievement.icon !== defaultAchievement.icon
|
||||
|| savedCondition !== defaultCondition
|
||||
|| savedReward !== defaultReward;
|
||||
savedAchievement.name = defaultAchievement.name;
|
||||
savedAchievement.description = defaultAchievement.description;
|
||||
savedAchievement.icon = defaultAchievement.icon;
|
||||
@@ -827,7 +910,9 @@ const patchAchievementStats = (state: GameState): number => {
|
||||
if (defaultAchievement.reward !== undefined) {
|
||||
savedAchievement.reward = { ...defaultAchievement.reward };
|
||||
}
|
||||
patched = patched + 1;
|
||||
if (hasChanged) {
|
||||
patched = patched + 1;
|
||||
}
|
||||
}
|
||||
return patched;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user