Files
jwinterm fad2a695bd Wownerogue
A browser-based roguelike synchronized with crypto block timing. A run is a race
against the next block: reach the exit before the block lands, or the house wins.
The same codebase runs the solo dungeon, the social Tavern, and operator-selected
PvP and race rulesets, with separate free and paid leaderboards.

Self-hostable under the MIT License. See README.md to run it, docs/ARCHITECTURE.md
for the design, and docs/DEPLOY.md to operate an instance.
2026-07-27 08:23:01 -04:00

76 lines
3.4 KiB
JavaScript

// Pack registry: the engine behind "add more 2D tilesets / iso packs / 3D skins as products".
//
// A PACK is a rich, self-contained render environment for a projection (topdown | iso | 3d). Because
// every scene (tavern OR dungeon) is kind-based (floor/wall/table/torch/monster…), the SAME pack
// renders both; packs are interchangeable across town and dungeon. Each pack maps 1:1 to a catalog
// entry (id/tier/threshold), so it's a monetizable product gated by entitlements.
//
// This module makes packs MULTI (like RK.THEMES already is for topdown) and adds active-pack
// selection per projection. Renderers ask for the active pack's assets; a picker lets an unlocked
// user switch. Adding a pack then = register(def) + drop assets + a catalog row.
(function (root) {
'use strict';
var RK = root.RK = root.RK || {};
// id -> { id, label, projection: 'topdown'|'iso'|'3d', kind, assets }
// `assets` is the render-kit payload for that projection (theme tiles / iso set / 3d models).
RK.PACK_REGISTRY = RK.PACK_REGISTRY || {};
// Storage: localStorage in the browser, an in-memory map under Node/tests.
var mem = {};
function store() {
try { if (root.localStorage) return root.localStorage; } catch (_) {}
return { getItem: function (k) { return k in mem ? mem[k] : null; }, setItem: function (k, v) { mem[k] = String(v); } };
}
function key(projection) { return 'rk_pack_' + projection; }
RK.registerPack = function (def) {
if (!def || !def.id || !def.projection) return null;
RK.PACK_REGISTRY[def.id] = def;
return def;
};
RK.getPackDef = function (id) { return (id && RK.PACK_REGISTRY[id]) || null; };
RK.packsForProjection = function (projection) {
var out = [];
for (var id in RK.PACK_REGISTRY) {
if (RK.PACK_REGISTRY[id].projection === projection) out.push(RK.PACK_REGISTRY[id]);
}
return out;
};
// Packs the user can actually use for a projection (free ones + entitlement-unlocked ones).
RK.unlockedPacks = function (projection) {
return RK.packsForProjection(projection).filter(function (p) {
return !RK.canUsePack || RK.canUsePack(p.id);
});
};
// The user's chosen pack if it's registered for this projection AND unlocked; otherwise the
// first unlocked pack; otherwise null.
RK.activePackId = function (projection) {
var saved = store().getItem(key(projection));
var def = saved && RK.PACK_REGISTRY[saved];
if (def && def.projection === projection && (!RK.canUsePack || RK.canUsePack(saved))) return saved;
var unlocked = RK.unlockedPacks(projection);
return unlocked.length ? unlocked[0].id : null;
};
RK.activePack = function (projection) { return RK.getPackDef(RK.activePackId(projection)); };
RK.activePackAssets = function (projection) {
var def = RK.activePack(projection);
return def ? def.assets : null;
};
// Select a pack (persisted per projection). Refused if unregistered or locked. Returns success.
RK.setActivePack = function (id) {
var def = RK.getPackDef(id);
if (!def) return false;
if (RK.canUsePack && !RK.canUsePack(id)) return false;
store().setItem(key(def.projection), id);
return true;
};
if (typeof module !== 'undefined' && module.exports) module.exports = RK;
})(typeof window !== 'undefined' ? window : this);