feat: add number format config, resource cap, and modal scroll fix

- Add user-configurable number format (suffix/scientific/engineering)
  - Suffix: K/M/B/T through Dc (1e33), then letter-based a/b/c... indefinitely
  - Scientific: 1.23e15 style via toExponential
  - Engineering: exponent always a multiple of 3 (1.23E15)
  - Stored in ProfileSettings, fetched from profile API on load
  - Picker UI in EditProfileModal with live examples
- Cap all resource accumulation at 1e300 (RESOURCE_CAP constant)
  - Per-resource FULL badge with tooltip in ResourceBar
  - Amber notice strip when any resource is at cap
  - handleClick also respects the cap
- Make EditProfileModal scrollable with viewport margin
  - Flex column layout with sticky header, scrollable form body
  - Bio textarea preserved as resizable with min-height
- Fix ReferenceError: formatNumber not defined in BossPanel/AchievementPanel
  - Pass formatNumber as prop to BossCard and AchievementCard
  - Pass formatNumber as parameter to conditionDescription
This commit is contained in:
2026-03-06 18:59:43 -08:00
committed by Naomi Carrigan
parent 24beaf3131
commit 5ad2c44399
15 changed files with 290 additions and 65 deletions
+9 -4
View File
@@ -40,6 +40,11 @@ const checkAchievements = (state: GameState): Achievement[] => {
});
};
/** Maximum value any resource can accumulate to. Beyond this JS floats lose all useful precision. */
export const RESOURCE_CAP = 1e300;
const capResource = (value: number): number => Math.min(value, RESOURCE_CAP);
/**
* Pure function — applies one game tick to the state.
* deltaSeconds: time elapsed since last tick.
@@ -187,8 +192,8 @@ export const applyTick = (state: GameState, deltaSeconds: number): GameState =>
});
}
const newGold = state.resources.gold + goldGained + questGold;
const newEssence = state.resources.essence + essenceGained + questEssence;
const newGold = capResource(state.resources.gold + goldGained + questGold);
const newEssence = capResource(state.resources.essence + essenceGained + questEssence);
const newTotalGoldEarned = state.player.totalGoldEarned + goldGained + questGold;
const partialState: GameState = {
@@ -197,7 +202,7 @@ export const applyTick = (state: GameState, deltaSeconds: number): GameState =>
...state.resources,
gold: newGold,
essence: newEssence,
crystals: state.resources.crystals + questCrystals,
crystals: capResource(state.resources.crystals + questCrystals),
},
player: {
...state.player,
@@ -228,7 +233,7 @@ export const applyTick = (state: GameState, deltaSeconds: number): GameState =>
achievements: updatedAchievements,
resources: {
...partialState.resources,
crystals: partialState.resources.crystals + crystalsFromAchievements,
crystals: capResource(partialState.resources.crystals + crystalsFromAchievements),
},
};
};