mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-18 06:41:27 +02:00
feat(packs): Fancy dies as a mode → topdown packs; Roguelike Dungeon tile pack (B)
Modes are TECHNIQUES; 'Fancy' (tiles+FX) was really a topdown PACK, so: - Remove 'fancy' from RENDER_MODES (saved 'fancy' falls back to tiles; its dead Pixi branch is short-circuited by canUseMode). Modes = Tiled/ASCII/Iso/3D. - Modes now carry a ; a premium mode unlocks once the user has ANY unlocked pack for that projection (not a single hardcoded pack) — needed now that iso is multi-pack. - New 'roguelike-dungeon' topdown pack: same sheet, a DUNGEON palette (stone floors + barrels/crates vs the wood interior) — a distinct 2D tileset with zero new assets. Registered + catalog (migration 027, topdown tier 1 / 1 credit). The tiled renderer's lighting/shadows/vignette are the built-in FX. - Also: iso facing fix (Male 0=NW..6=NE) reflected in tests. So the Tiled pack picker now offers Roguelike Interior <-> Dungeon, and Iso offers Dungeon <-> Medieval — packs as products, proven in the best-looking mode too. Suite 58/419 green.
This commit is contained in:
@@ -161,6 +161,31 @@
|
||||
treasure: [42, 15], // 897 — item
|
||||
torch: [50, 10] // 620 — rubble / prop
|
||||
}
|
||||
},
|
||||
// Second topdown pack — same sheet, a DUNGEON palette (stone floors, barrels/crates instead
|
||||
// of the wood-interior furniture). A distinct look with zero new assets, and it's the
|
||||
// "interchangeable dungeon" style (the tiled renderer's lighting/shadows are the built-in FX).
|
||||
'roguelike-dungeon': {
|
||||
id: 'roguelike-dungeon',
|
||||
label: 'Roguelike Dungeon',
|
||||
tileset: { url: 'assets/kenney/roguelikeSheet.png', tile: 16, spacing: 1 },
|
||||
fx: true,
|
||||
tiles: {
|
||||
floor: [6, 2], // 120 — stone floor
|
||||
floor2: [13, 17], // 982 — plank accent
|
||||
wall: [13, 12], // 697 — masonry
|
||||
window: [42, 2], // 156 — window
|
||||
bar: [23, 0], // barrels (a dungeon has no bar)
|
||||
keg: [23, 0],
|
||||
shelf: [25, 0], // crate
|
||||
table: [25, 0], // crate as a table
|
||||
chair: [23, 0], // barrel as a stool
|
||||
barrel: [23, 0],
|
||||
crate: [25, 0],
|
||||
rug: [6, 2], // stone (no rug)
|
||||
door: [37, 9],
|
||||
entrance: [37, 9], exit: [37, 9], treasure: [42, 15], torch: [50, 10]
|
||||
}
|
||||
}
|
||||
};
|
||||
RK.activeThemeId = 'roguelike-interior';
|
||||
@@ -342,6 +367,7 @@
|
||||
// assets + a catalog row. Backward-compatible: one pack per projection resolves to these.
|
||||
if (RK.registerPack) {
|
||||
RK.registerPack({ id: 'roguelike-interior', label: 'Roguelike Interior', projection: 'topdown', kind: 'tiles', assets: RK.THEMES['roguelike-interior'] });
|
||||
RK.registerPack({ id: 'roguelike-dungeon', label: 'Roguelike Dungeon', projection: 'topdown', kind: 'tiles', assets: RK.THEMES['roguelike-dungeon'] });
|
||||
RK.registerPack({ id: 'iso-dungeon', label: 'Isometric Dungeon', projection: 'iso', kind: 'tiles', assets: RK.isoAssets });
|
||||
RK.registerPack({ id: 'iso-medieval', label: 'Medieval Town', projection: 'iso', kind: 'tiles', assets: RK.isoMedievalAssets });
|
||||
RK.registerPack({ id: 'kenney-3d-characters', label: 'Animated 3D', projection: '3d', kind: 'skin', assets: RK.threeAssets });
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
// Render tiers. `premium` marks modes intended to be unlocked with credits — a cosmetic
|
||||
// entitlement gated by Operator Policy, the same pattern as avatar unlocks. NOTE: rendering
|
||||
// is client-side, so this is a soft/honour gate for cosmetics, not a hard security boundary.
|
||||
// Modes are TECHNIQUES (how a scene is drawn). Packs are the SETS/styles within a projection
|
||||
// (chosen via the pack picker) — so "Fancy" (tiles + FX) is a topdown PACK, not a mode. A
|
||||
// premium mode unlocks once the user has ANY unlocked pack for its projection.
|
||||
RK.RENDER_MODES = [
|
||||
{ id: 'tiles', label: 'Tiled', premium: false },
|
||||
{ id: 'ascii', label: 'ASCII', premium: false },
|
||||
{ id: 'fancy', label: 'Fancy', premium: true, pack: 'generated-skins' },
|
||||
{ id: 'iso', label: 'Iso', premium: true, pack: 'iso-dungeon' },
|
||||
{ id: '3d', label: '3D', premium: true, pack: 'kenney-3d-characters' }
|
||||
// Planned: 'fancy-ascii' — shader-lit, animated glyphs.
|
||||
{ id: 'tiles', label: 'Tiled', premium: false, projection: 'topdown' },
|
||||
{ id: 'ascii', label: 'ASCII', premium: false, projection: 'topdown' },
|
||||
{ id: 'iso', label: 'Iso', premium: true, projection: 'iso' },
|
||||
{ id: '3d', label: '3D', premium: true, projection: '3d' }
|
||||
];
|
||||
|
||||
RK.entitlements = RK.entitlements || { premium: false, level: 'free', packs: {} };
|
||||
@@ -39,8 +40,11 @@
|
||||
var m = RK.modeMeta(id);
|
||||
if (!m) return false;
|
||||
if (RK.renderModeTestUnlocks && RK.renderModeTestUnlocks()) return true;
|
||||
if (!m.premium) return true;
|
||||
// A premium mode is available once the user has ANY unlocked pack for its projection.
|
||||
if (m.projection && RK.unlockedPacks) return RK.unlockedPacks(m.projection).length > 0;
|
||||
if (m.pack && RK.canUsePack) return RK.canUsePack(m.pack);
|
||||
return !m.premium || !!RK.entitlements.premium;
|
||||
return !!RK.entitlements.premium;
|
||||
};
|
||||
|
||||
RK.createRenderer = function (mode, host, opts) {
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
-- 027: add the Roguelike Dungeon topdown tile pack as a catalog product.
|
||||
-- Same sheet, a dungeon palette (a distinct 2D tileset). Cheap topdown-variety tier: 1 credit /
|
||||
-- supporter (tier 1). Idempotent.
|
||||
INSERT INTO cosmetic_catalog (pack_id, label, kind, projection, tier, unlock_min_credits, grant_only, active, sort_order, metadata)
|
||||
VALUES ('roguelike-dungeon', 'Roguelike Dungeon (Tiles)', 'render-pack', 'topdown', 1, 1, false, true, 15, '{}'::jsonb)
|
||||
ON CONFLICT (pack_id) DO UPDATE
|
||||
SET label = EXCLUDED.label, projection = EXCLUDED.projection, tier = EXCLUDED.tier,
|
||||
unlock_min_credits = EXCLUDED.unlock_min_credits, active = true;
|
||||
@@ -21,6 +21,10 @@ const DEFAULT_CATALOG = Object.freeze({
|
||||
id: 'generated-skins', label: 'Premium Generated Skins', kind: 'render-pack',
|
||||
projection: 'topdown', tier: 1, unlockMinCredits: 1, grantOnly: false, premium: true
|
||||
}),
|
||||
'roguelike-dungeon': Object.freeze({
|
||||
id: 'roguelike-dungeon', label: 'Roguelike Dungeon (Tiles)', kind: 'render-pack',
|
||||
projection: 'topdown', tier: 1, unlockMinCredits: 1, grantOnly: false, premium: true
|
||||
}),
|
||||
'iso-dungeon': Object.freeze({
|
||||
id: 'iso-dungeon', label: 'Kenney Isometric Dungeon', kind: 'render-pack',
|
||||
projection: 'iso', tier: 2, unlockMinCredits: 10, grantOnly: false, premium: true
|
||||
|
||||
@@ -105,10 +105,11 @@ describe('render pack visual resolver', () => {
|
||||
expect(RK.isoAssets.characters['char-merchant']).toBeUndefined();
|
||||
expect(RK.isoAssets.characters['char-wizard']).toBeUndefined();
|
||||
expect(RK.isoAssets.characters['char-goblin']).toBeUndefined();
|
||||
expect(RK.isoAssets.directions.down.idle).toContain('Male_3_Idle0.png');
|
||||
expect(RK.isoAssets.directions.up.idle).toContain('Male_7_Idle0.png');
|
||||
// 45° renders: 0=NW,2=SW,4=SE,6=NE. Facing follows visual movement (up->NE, down->SW, etc.).
|
||||
expect(RK.isoAssets.directions.down.idle).toContain('Male_2_Idle0.png');
|
||||
expect(RK.isoAssets.directions.up.idle).toContain('Male_6_Idle0.png');
|
||||
expect(RK.isoAssets.directions.left.idle).toContain('Male_0_Idle0.png');
|
||||
expect(RK.isoAssets.directions.right.idle).toContain('Male_5_Idle0.png');
|
||||
expect(RK.isoAssets.directions.right.idle).toContain('Male_4_Idle0.png');
|
||||
});
|
||||
|
||||
test('projection visuals expose the saved base tint as a render color', () => {
|
||||
@@ -142,8 +143,7 @@ describe('render pack visual resolver', () => {
|
||||
|
||||
// With test unlocks on, ALL premium modes and packs are available for QA
|
||||
RK.RENDER_MODE_TEST_UNLOCKS = true;
|
||||
expect(RK.canUseMode('fancy')).toBe(true);
|
||||
expect(RK.canUseMode('iso')).toBe(true);
|
||||
expect(RK.canUseMode('iso')).toBe(true); // 'fancy' is no longer a mode — it's a topdown pack
|
||||
expect(RK.canUseMode('3d')).toBe(true);
|
||||
expect(RK.canUsePack('iso-dungeon')).toBe(true);
|
||||
expect(RK.canUsePack('kenney-3d-characters')).toBe(true);
|
||||
|
||||
Reference in New Issue
Block a user