fertig-classic-games/public/src/games/forbiddenisland/IslandData.js

149 lines
6.5 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.

// Forbidden Island — static data. No Phaser, no game state here: just the board
// silhouette, the tile/treasure/role catalog, and deck composition. Everything
// dynamic lives in IslandLogic.js.
// The island is a 6×6 grid with the four corners of each edge removed, giving
// the classic 24-tile diamond:
//
// . . X X . .
// . X X X X .
// X X X X X X
// X X X X X X
// . X X X X .
// . . X X . .
//
export const GRID = 6;
// Playable cells in reading order (row, col). 24 of them.
export const CELLS = [
[0, 2], [0, 3],
[1, 1], [1, 2], [1, 3], [1, 4],
[2, 0], [2, 1], [2, 2], [2, 3], [2, 4], [2, 5],
[3, 0], [3, 1], [3, 2], [3, 3], [3, 4], [3, 5],
[4, 1], [4, 2], [4, 3], [4, 4],
[5, 2], [5, 3],
];
export function isCell(r, c) {
return CELLS.some(([cr, cc]) => cr === r && cc === c);
}
// Treasures, each tied to two island tiles. `element` keys the treasure cards.
export const TREASURES = {
earth: { key: 'earth', name: 'The Earth Stone', tiles: ['temple-moon', 'temple-sun'], color: 0x8d6e3c },
wind: { key: 'wind', name: 'The Statue of the Wind', tiles: ['whispering-garden', 'howling-garden'], color: 0x9aa7b0 },
fire: { key: 'fire', name: 'The Crystal of Fire', tiles: ['cave-embers', 'cave-shadows'], color: 0xc0492f },
ocean: { key: 'ocean', name: "The Ocean's Chalice", tiles: ['coral-palace', 'tidal-palace'], color: 0x2f7fb0 },
};
export const TREASURE_KEYS = ['earth', 'wind', 'fire', 'ocean'];
// All 24 tiles. `treasure` links a tile to a treasure; `landing` marks the
// helipad you must escape from. The rest are neutral.
export const TILES = [
{ id: 'temple-moon', name: 'Temple of the Moon', treasure: 'earth' },
{ id: 'temple-sun', name: 'Temple of the Sun', treasure: 'earth' },
{ id: 'whispering-garden', name: 'Whispering Garden', treasure: 'wind' },
{ id: 'howling-garden', name: 'Howling Garden', treasure: 'wind' },
{ id: 'cave-embers', name: 'Cave of Embers', treasure: 'fire' },
{ id: 'cave-shadows', name: 'Cave of Shadows', treasure: 'fire' },
{ id: 'coral-palace', name: 'Coral Palace', treasure: 'ocean' },
{ id: 'tidal-palace', name: 'Tidal Palace', treasure: 'ocean' },
{ id: 'fools-landing', name: "Fools' Landing", landing: true },
{ id: 'bronze-gate', name: 'Bronze Gate' },
{ id: 'copper-gate', name: 'Copper Gate' },
{ id: 'silver-gate', name: 'Silver Gate' },
{ id: 'gold-gate', name: 'Gold Gate' },
{ id: 'iron-gate', name: 'Iron Gate' },
{ id: 'watchtower', name: 'Watchtower' },
{ id: 'observatory', name: 'Observatory' },
{ id: 'phantom-rock', name: 'Phantom Rock' },
{ id: 'misty-marsh', name: 'Misty Marsh' },
{ id: 'breakers-bridge', name: 'Breakers Bridge' },
{ id: 'crimson-forest', name: 'Crimson Forest' },
{ id: 'dunes-deception', name: 'Dunes of Deception' },
{ id: 'lost-lagoon', name: 'Lost Lagoon' },
{ id: 'twilight-hollow', name: 'Twilight Hollow' },
{ id: 'cliffs-abandon', name: 'Cliffs of Abandon' },
];
// The six adventurer roles. `power` is read by the engine/AI to branch special
// abilities; `color` is the pawn colour.
export const ROLES = {
pilot: { key: 'pilot', name: 'Pilot', color: 0x4a90d9, colorHex: '#4a90d9', power: 'Once per turn, fly to any tile.' },
engineer: { key: 'engineer', name: 'Engineer', color: 0xd0473a, colorHex: '#d0473a', power: 'Shore up two tiles for one action.' },
messenger: { key: 'messenger', name: 'Messenger', color: 0xb9bfc6, colorHex: '#b9bfc6', power: 'Give Treasure cards to anyone, any distance.' },
navigator: { key: 'navigator', name: 'Navigator', color: 0xe2b53c, colorHex: '#e2b53c', power: 'Move another adventurer up to 2 tiles.' },
diver: { key: 'diver', name: 'Diver', color: 0x2b2b30, colorHex: '#2b2b30', power: 'Swim through flooded and missing tiles.' },
explorer: { key: 'explorer', name: 'Explorer', color: 0x49a25a, colorHex: '#49a25a', power: 'Move and shore up diagonally.' },
};
export const ROLE_KEYS = ['pilot', 'engineer', 'messenger', 'navigator', 'diver', 'explorer'];
// Special (non-treasure) Treasure-deck cards.
export const SPECIAL = {
WATERS_RISE: 'waters-rise',
HELICOPTER: 'helicopter',
SANDBAGS: 'sandbags',
};
// Treasure deck: 5 of each treasure (20) + 3 Waters Rise! + 3 Helicopter Lift +
// 2 Sandbags = 28 cards. Cards are plain ids: `treasure:earth`, `waters-rise`, …
export function buildTreasureDeck() {
const deck = [];
for (const key of TREASURE_KEYS) for (let i = 0; i < 5; i++) deck.push(`treasure:${key}`);
for (let i = 0; i < 3; i++) deck.push(SPECIAL.WATERS_RISE);
for (let i = 0; i < 3; i++) deck.push(SPECIAL.HELICOPTER);
for (let i = 0; i < 2; i++) deck.push(SPECIAL.SANDBAGS);
return deck;
}
// Water-level track. Index = level (1..10); value = flood cards drawn per turn.
// Level 10 is the skull: reaching it loses the game.
export const WATER_TRACK = [null, 2, 2, 3, 3, 3, 4, 4, 5, 5, 'skull'];
export const MAX_WATER = 10;
export const DIFFICULTY = {
novice: { key: 'novice', name: 'Novice', start: 1 },
normal: { key: 'normal', name: 'Normal', start: 2 },
elite: { key: 'elite', name: 'Elite', start: 3 },
legendary: { key: 'legendary', name: 'Legendary', start: 4 },
};
export function floodDrawCount(waterLevel) {
const v = WATER_TRACK[Math.min(waterLevel, MAX_WATER)];
return typeof v === 'number' ? v : 5;
}
// Cards needed to capture a treasure.
export const CARDS_TO_CAPTURE = 4;
// Hand limit; over this at end of turn you must discard.
export const HAND_LIMIT = 5;
// Actions per turn.
export const ACTIONS_PER_TURN = 3;
export function tileById(id) {
return TILES.find((t) => t.id === id) ?? null;
}
// Tile id → its row in the `forbiddenisland-tiles` spritesheet. Dry side is
// frame 2·row, flooded side is frame 2·row+1.
export const TILE_FRAME_ROW = Object.fromEntries(TILES.map((t, i) => [t.id, i]));
// Card id → frame in the `forbiddenisland-cards` spritesheet (320×420 cells, a
// 4-col × 2-row grid). Top row: the four treasure cards. Bottom row: the three
// special cards (frame 7 is unused / a card back).
export const CARD_FRAME = {
'treasure:earth': 0,
'treasure:wind': 1,
'treasure:fire': 2,
'treasure:ocean': 3,
[SPECIAL.WATERS_RISE]: 4,
[SPECIAL.HELICOPTER]: 5,
[SPECIAL.SANDBAGS]: 6,
};
export function cardFrame(card) {
return CARD_FRAME[card] ?? null;
}