fertig-classic-games/src/games/labyrinth/LabyrinthData.js

172 lines
7.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Labyrinth — static data. No Phaser, no game state: just the maze tile
// vocabulary, the fixed-tile skeleton, the movable-tile bag, the treasure
// catalog, player home corners, and spritesheet frame helpers. Everything
// dynamic (the shuffled board, spare tile, pawns, targets) lives in
// LabyrinthLogic.js.
// 7×7 board. The 49 cells hold 48 movable + the fixed skeleton; one extra
// "spare" tile is always held off-board and pushed in each turn.
export const GRID = 7;
// ── Directions ────────────────────────────────────────────────────────────────
// Side indices around a tile, clockwise from the top. A clockwise rotation by
// `r` quarter-turns maps side s → (s + r) % 4.
export const N = 0, E = 1, S = 2, W = 3;
export const DIRS = [N, E, S, W];
export const OPPOSITE = { [N]: S, [E]: W, [S]: N, [W]: E };
// Row/col delta when stepping out of a tile through a given side.
export const DELTA = {
[N]: { dr: -1, dc: 0 },
[E]: { dr: 0, dc: 1 },
[S]: { dr: 1, dc: 0 },
[W]: { dr: 0, dc: -1 },
};
// Base (unrotated) open sides per tile shape.
// I (straight) — corridor N↔S
// L (corner) — corridor N↔E
// T (junction) — open E,S,W (closed N)
export const BASE_SIDES = {
I: [N, S],
L: [N, E],
T: [E, S, W],
};
// The set of open sides for a tile of `type` rotated `rot` quarter-turns.
export function openSides(type, rot) {
return BASE_SIDES[type].map((s) => (s + rot) % 4);
}
// Does a tile of (type,rot) have an opening on `side`?
export function isOpen(type, rot, side) {
return openSides(type, rot).includes(side);
}
// ── Treasures ────────────────────────────────────────────────────────────────
// 24 treasures. The array index IS the spritesheet frame for both the on-tile
// overlay (labyrinth-treasures, 100×100) and the held card (labyrinth-cards,
// 270×390). 12 creatures (011) then 12 objects (1223).
export const TREASURES = [
{ key: 'dragon', name: 'Dragon' },
{ key: 'genie', name: 'Genie' },
{ key: 'ghost', name: 'Ghost' },
{ key: 'goblin', name: 'Goblin' },
{ key: 'witch', name: 'Witch' },
{ key: 'skeleton', name: 'Skeleton' },
{ key: 'bat', name: 'Bat' },
{ key: 'owl', name: 'Owl' },
{ key: 'spider', name: 'Spider' },
{ key: 'lizard', name: 'Lizard' },
{ key: 'beetle', name: 'Beetle' },
{ key: 'rat', name: 'Rat' },
{ key: 'crown', name: 'Crown' },
{ key: 'ring', name: 'Ring' },
{ key: 'keys', name: 'Keys' },
{ key: 'chest', name: 'Treasure Chest' },
{ key: 'gem', name: 'Gem' },
{ key: 'coins', name: 'Gold Coins' },
{ key: 'candelabra', name: 'Candelabra' },
{ key: 'grimoire', name: 'Grimoire' },
{ key: 'sword', name: 'Sword' },
{ key: 'helmet', name: 'Helmet' },
{ key: 'map', name: 'Map Scroll' },
{ key: 'chalice', name: 'Chalice' },
];
export const TREASURE_COUNT = TREASURES.length; // 24
// ── Fixed skeleton ──────────────────────────────────────────────────────────
// The 16 tiles anchored at even row/col never move. Corners are L-tiles opening
// inward; the rest are T-junctions whose closed (flat) side faces the board
// edge. The 12 non-corner fixed tiles each carry a treasure (frames 011).
export const FIXED = [
// four corners (no treasure)
{ r: 0, c: 0, type: 'L', rot: 1 }, // opens E,S
{ r: 0, c: 6, type: 'L', rot: 2 }, // opens S,W
{ r: 6, c: 0, type: 'L', rot: 0 }, // opens N,E
{ r: 6, c: 6, type: 'L', rot: 3 }, // opens N,W
// top edge — closed N
{ r: 0, c: 2, type: 'T', rot: 0, treasure: 0 },
{ r: 0, c: 4, type: 'T', rot: 0, treasure: 1 },
// bottom edge — closed S
{ r: 6, c: 2, type: 'T', rot: 2, treasure: 2 },
{ r: 6, c: 4, type: 'T', rot: 2, treasure: 3 },
// left edge — closed W
{ r: 2, c: 0, type: 'T', rot: 3, treasure: 4 },
{ r: 4, c: 0, type: 'T', rot: 3, treasure: 5 },
// right edge — closed E
{ r: 2, c: 6, type: 'T', rot: 1, treasure: 6 },
{ r: 4, c: 6, type: 'T', rot: 1, treasure: 7 },
// inner four — point inward
{ r: 2, c: 2, type: 'T', rot: 3, treasure: 8 }, // open N,E,S
{ r: 2, c: 4, type: 'T', rot: 1, treasure: 9 }, // open N,S,W
{ r: 4, c: 2, type: 'T', rot: 3, treasure: 10 },
{ r: 4, c: 4, type: 'T', rot: 1, treasure: 11 },
];
export function isFixed(r, c) {
return r % 2 === 0 && c % 2 === 0;
}
// ── Movable tile bag ──────────────────────────────────────────────────────────
// 34 tiles (33 fill the open cells, 1 is the starting spare). Treasures 1223
// ride on twelve of them (the six T's + six L's, matching the classic deck);
// straights carry none. Rotations are randomized at deal time in the logic.
export function buildMovableBag() {
const bag = [];
const push = (type, treasure = null) => bag.push({ type, treasure });
// 6 T-junctions, all treasured (1217)
for (let i = 0; i < 6; i++) push('T', 12 + i);
// 16 corners — six treasured (1823), ten plain
for (let i = 0; i < 6; i++) push('L', 18 + i);
for (let i = 0; i < 10; i++) push('L', null);
// 12 straights, no treasure
for (let i = 0; i < 12; i++) push('I', null);
return bag; // length 34
}
// ── Players ────────────────────────────────────────────────────────────────
// Home corners in seat order. Two-player games use opposite corners (seats 0 &
// 1 are diagonal), three/four fill in the remaining corners.
export const HOME_CORNERS = [
{ r: 0, c: 0 },
{ r: 6, c: 6 },
{ r: 0, c: 6 },
{ r: 6, c: 0 },
];
export const PLAYER_COLORS = [0xd0473a, 0x4a90d9, 0x49a25a, 0xe2b53c];
export const PLAYER_COLOR_HEX = ['#d0473a', '#4a90d9', '#49a25a', '#e2b53c'];
// ── Insertion slots ──────────────────────────────────────────────────────────
// The 12 places the spare can be pushed in: the shiftable rows/cols (1,3,5) on
// each of the four edges. `side` names where the spare enters; the row/col it
// affects is `index`. Pushing into a slot ejects the tile at the far end.
export const SHIFT_LINES = [1, 3, 5];
export const SLOTS = (() => {
const out = [];
for (const i of SHIFT_LINES) out.push({ id: `T${i}`, side: 'top', index: i });
for (const i of SHIFT_LINES) out.push({ id: `B${i}`, side: 'bottom', index: i });
for (const i of SHIFT_LINES) out.push({ id: `L${i}`, side: 'left', index: i });
for (const i of SHIFT_LINES) out.push({ id: `R${i}`, side: 'right', index: i });
return out;
})();
export const OPPOSITE_SIDE = { top: 'bottom', bottom: 'top', left: 'right', right: 'left' };
// The slot whose push would directly reverse the given one (same line, opposite
// edge). Returns a slot id or null.
export function reverseSlotId(slot) {
if (!slot) return null;
const side = OPPOSITE_SIDE[slot.side];
return `${side === 'top' ? 'T' : side === 'bottom' ? 'B' : side === 'left' ? 'L' : 'R'}${slot.index}`;
}
// ── Spritesheet frame helpers ──────────────────────────────────────────────
// Tile background: frame 0 = movable, 1 = fixed/anchored.
export function tileFrame(fixed) { return fixed ? 1 : 0; }
// Treasure overlay & card share the treasure index as their frame.
export function treasureFrame(idx) { return idx; }
export function cardFrame(idx) { return idx; }