Economy, Progression, Drops, And Modifiers
Hosanna Game supplies small, composable economy systems. They own storage and bounded simulation mechanics; each game still owns its balance tables and converts purchased levels or active modifiers into a flat, game-specific loadout.
Choose The Smallest System
| Need | Use |
|---|---|
| Persistent currency and per-character upgrade levels | Hs2dProgressStore with Hs2dUpgradeCatalog |
| A paused upgrade menu over a game | Hs2dShopScene |
| Weighted deterministic loot, including a no-drop outcome | Hs2dDropTable with Hs2dLcg |
| Pooled coins or pickups attracted to one of several players | Hs2dCollectibleField |
| Fixed-capacity temporary or permanent effects | Hs2dTimedModifierSet |
Persistent Progress
Define immutable upgrade tracks, construct a catalog, and give Hs2dProgressStore a game ID and persistence adapter:
const tracks: Hs2dUpgradeTrackDef[] = [
{
id: 'hull',
name: 'Hull',
description: 'Increase maximum health',
maxLevel: 3,
costs: [100, 250, 500],
},
];
const store = new Hs2dProgressStore(
'my-game',
new Hs2dUpgradeCatalog(tracks),
new Hs2dSettingsProgressPersistence(),
);
The store:
- normalizes untrusted saved currency and levels when it loads;
- keeps upgrade levels under an
entityId, allowing one catalog to serve several ships or heroes; - returns
-1fromgetNextCost()when a track is maxed; - persists after
addCurrency()and a successfulpurchaseUpgrade(); - returns a fresh level record from
getUpgradeLevels().
Keep stat resolution in the game:
const levels = store.getUpgradeLevels('scout');
const loadout = {
maxHealth: HULL_VALUES[levels.hull ?? 0],
speed: SPEED_VALUES[levels.speed ?? 0],
};
This keeps string-key lookups out of the frame loop. The vertical shooter and Hosanna Dungeon both follow this boundary.
Hs2dSettingsProgressPersistence is the engine's registry-backed adapter. A host may instead implement Hs2dProgressPersistence to store the same JSON-safe payload elsewhere.
Upgrade Shop
Push an Hs2dShopScene when a player should spend banked currency:
this.game?.pushScene(new Hs2dShopScene({
screenWidth: this.screenWidth,
screenHeight: this.screenHeight,
title: 'Upgrades',
store,
entityId: 'scout',
audioManager: this.audioManager,
}));
It pauses the scene below, renders one row per catalog track, purchases through the store, updates the selected row and balance, and closes on Back or Play.
Deterministic Drops
Hs2dDropTable returns an entry index or -1 for no drop:
const drops = new Hs2dDropTable([
{ token: 'repair', weight: 4 },
{ token: 'rapid-fire', weight: 1 },
], 10);
const index = drops.roll(lcg);
if (index >= 0) {
const token = drops.getEntry(index).token;
spawnPickup(token, enemy.x, enemy.y);
}
Supply the simulation's seeded Hs2dLcg; do not call Math.random() alongside it. The same seed and event order then produce the same result in replay and headless tests.
Pooled Collectibles
Hs2dCollectibleField preallocates collectible and event slots. It supports attracted bursts, floating pickups, time-to-live culling, and nearest-player collection.
After update():
- Read events from
getEvents()[0..eventCount). - Apply currency, ammo, or power-up effects.
- Synchronize active collectible slots into a fixed sprite pool.
The returned arrays and slots are reused. Do not retain an event as a historical record, and do not iterate beyond eventCount.
Size eventCapacity for the maximum number of collections one update can resolve. When that buffer is full, the field still deactivates an in-range collectible but cannot emit its event, so excess currency or pickup effects are silently lost.
Timed Modifiers
Hs2dTimedModifierSet stores a fixed number of active modifier IDs:
- applying an existing ID refreshes it instead of stacking it;
- duration
0means permanent untilclear(); update(deltaMs)returnstruewhen a modifier expires;- an apply returns
falsewhen all slots are occupied.
Recompute a flat effective loadout only when apply() succeeds or update() reports a change.
Source References
| Source | What it proves |
|---|---|
../games/hosanna-ui/src/hosanna-game/economy/Hs2dProgressStore.ts | Persistence, normalization, currency cap, per-entity levels, and purchase rules. |
../games/hosanna-ui/src/hosanna-game/economy/Hs2dUpgradeCatalog.ts | Track schema, level clamping, and next-cost behavior. |
../games/hosanna-ui/src/hosanna-game/economy/Hs2dShopScene.ts | Reusable paused upgrade overlay. |
../games/hosanna-ui/src/hosanna-game/economy/Hs2dDropTable.ts | Weighted deterministic roll and no-drop outcome. |
../games/hosanna-ui/src/hosanna-game/economy/Hs2dCollectibleField.ts | Fixed collectible slots, multi-collector attraction, culling, and event buffer. |
../games/hosanna-ui/src/hosanna-game/economy/Hs2dTimedModifierSet.ts | Fixed modifier slots, refresh, expiry, and swap removal. |
../hosanna-ui-game-samples-public/src/hosanna-game-examples/native-shoot-em-up/VerticalShooterEconomy.ts | Store construction and game-specific ship loadout resolution. |
../hosanna-ui-game-samples-public/src/hosanna-game-examples/native-shoot-em-up/VerticalShooterGameLogic.ts | Drop table, collectible fields, and deterministic simulation use. |