Skip to main content

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

NeedUse
Persistent currency and per-character upgrade levelsHs2dProgressStore with Hs2dUpgradeCatalog
A paused upgrade menu over a gameHs2dShopScene
Weighted deterministic loot, including a no-drop outcomeHs2dDropTable with Hs2dLcg
Pooled coins or pickups attracted to one of several playersHs2dCollectibleField
Fixed-capacity temporary or permanent effectsHs2dTimedModifierSet

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 -1 from getNextCost() when a track is maxed;
  • persists after addCurrency() and a successful purchaseUpgrade();
  • 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():

  1. Read events from getEvents()[0..eventCount).
  2. Apply currency, ammo, or power-up effects.
  3. 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 0 means permanent until clear();
  • update(deltaMs) returns true when a modifier expires;
  • an apply returns false when all slots are occupied.

Recompute a flat effective loadout only when apply() succeeds or update() reports a change.

Source References

SourceWhat it proves
../games/hosanna-ui/src/hosanna-game/economy/Hs2dProgressStore.tsPersistence, normalization, currency cap, per-entity levels, and purchase rules.
../games/hosanna-ui/src/hosanna-game/economy/Hs2dUpgradeCatalog.tsTrack schema, level clamping, and next-cost behavior.
../games/hosanna-ui/src/hosanna-game/economy/Hs2dShopScene.tsReusable paused upgrade overlay.
../games/hosanna-ui/src/hosanna-game/economy/Hs2dDropTable.tsWeighted deterministic roll and no-drop outcome.
../games/hosanna-ui/src/hosanna-game/economy/Hs2dCollectibleField.tsFixed collectible slots, multi-collector attraction, culling, and event buffer.
../games/hosanna-ui/src/hosanna-game/economy/Hs2dTimedModifierSet.tsFixed modifier slots, refresh, expiry, and swap removal.
../hosanna-ui-game-samples-public/src/hosanna-game-examples/native-shoot-em-up/VerticalShooterEconomy.tsStore construction and game-specific ship loadout resolution.
../hosanna-ui-game-samples-public/src/hosanna-game-examples/native-shoot-em-up/VerticalShooterGameLogic.tsDrop table, collectible fields, and deterministic simulation use.
Talk to us