UNPKG

shellquest

Version:

Terminal-based procedurally generated dungeon crawler

318 lines (275 loc) 14.9 kB
/** * Dungeon Tile Definitions * Maps tile names to coordinates in dungeon1.png */ import type {TileMap} from '../../TileMap.ts'; interface DungeonTile { name: string; x: number; y: number; layer: 'bottom' | 'sprite' | 'top'; solid: boolean; } export function setupDungeonTileDefinitions(tileMap: TileMap): void { // Load dungeon tileset as default tileMap.loadFromFile('dungeon1.png', true); // Also load the main tileset for entity sprites (player, zombie, weapons) tileMap.loadFromFile('tilemap_packed.png', false); const tiles: DungeonTile[] = [ // ========================================================================== // CEILING TILES (background for wall areas) // ========================================================================== {name: 'dungeon-ceiling-base', x: 0, y: 0, layer: 'bottom', solid: true}, {name: 'dungeon-ceiling-variant1', x: 0, y: 1, layer: 'bottom', solid: true}, {name: 'dungeon-ceiling-variant2', x: 0, y: 2, layer: 'bottom', solid: true}, // ========================================================================== // GROUND/FLOOR TILES // ========================================================================== {name: 'dungeon-ground-base', x: 0, y: 4, layer: 'bottom', solid: false}, {name: 'dungeon-ground-textured', x: 1, y: 4, layer: 'bottom', solid: false}, {name: 'dungeon-ground-stone', x: 6, y: 3, layer: 'bottom', solid: false}, // ========================================================================== // SHADOW TILES (cast by walls onto floor) // ========================================================================== {name: 'dungeon-shadow-from-north', x: 2, y: 4, layer: 'sprite', solid: false}, {name: 'dungeon-shadow-from-north-alt', x: 3, y: 4, layer: 'sprite', solid: false}, {name: 'dungeon-shadow-inner-corner', x: 4, y: 4, layer: 'sprite', solid: false}, {name: 'dungeon-shadow-outer-corner', x: 5, y: 4, layer: 'sprite', solid: false}, // ========================================================================== // WALL TILES - Horizontal (no wall below) // ========================================================================== {name: 'dungeon-wall-horizontal-middle', x: 4, y: 3, layer: 'sprite', solid: true}, {name: 'dungeon-wall-horizontal-left-cap', x: 9, y: 4, layer: 'sprite', solid: true}, {name: 'dungeon-wall-horizontal-right-cap', x: 11, y: 4, layer: 'sprite', solid: true}, // ========================================================================== // WALL TILES - Vertical (wall below, extends downward) // ========================================================================== {name: 'dungeon-wall-vertical-left', x: 1, y: 1, layer: 'sprite', solid: true}, {name: 'dungeon-wall-vertical-right', x: 3, y: 1, layer: 'sprite', solid: true}, // ========================================================================== // WALL CROWN TILES (decorative top, drawn 1 tile above wall face) // ========================================================================== {name: 'dungeon-wall-crown-middle', x: 2, y: 0, layer: 'top', solid: false}, {name: 'dungeon-wall-crown-left-cap', x: 4, y: 1, layer: 'top', solid: false}, {name: 'dungeon-wall-crown-right-cap', x: 5, y: 1, layer: 'top', solid: false}, {name: 'dungeon-wall-crown-vertical-left', x: 1, y: 1, layer: 'top', solid: false}, {name: 'dungeon-wall-crown-vertical-right', x: 3, y: 1, layer: 'top', solid: false}, // ========================================================================== // WALL TOP TILES (top edge of wall mass, when no wall above) // ========================================================================== {name: 'dungeon-wall-top-left', x: 4, y: 0, layer: 'top', solid: false}, {name: 'dungeon-wall-top-middle', x: 2, y: 2, layer: 'top', solid: false}, {name: 'dungeon-wall-top-right', x: 5, y: 0, layer: 'top', solid: false}, // ========================================================================== // WALL CORNER TILES (inside corners - L-shaped void cutting into wall mass) // ========================================================================== {name: 'dungeon-wall-corner-inside-sw', x: 3, y: 0, layer: 'sprite', solid: true}, {name: 'dungeon-wall-corner-inside-se', x: 1, y: 0, layer: 'sprite', solid: true}, {name: 'dungeon-wall-corner-inside-nw', x: 3, y: 2, layer: 'sprite', solid: true}, {name: 'dungeon-wall-corner-inside-ne', x: 1, y: 2, layer: 'sprite', solid: true}, // ========================================================================== // DECAL TILES - Ground // ========================================================================== {name: 'dungeon-decal-table', x: 0, y: 6, layer: 'sprite', solid: false}, {name: 'dungeon-decal-ground-knub', x: 1, y: 6, layer: 'sprite', solid: false}, {name: 'dungeon-decal-ground-anvil', x: 2, y: 6, layer: 'sprite', solid: false}, {name: 'dungeon-decal-ground-column-bottom', x: 3, y: 6, layer: 'sprite', solid: false}, {name: 'dungeon-decal-ground-column-top', x: 3, y: 5, layer: 'sprite', solid: false}, {name: 'dungeon-decal-chest-open', x: 8, y: 7, layer: 'sprite', solid: false}, // ========================================================================== // DECAL TILES - Wall // ========================================================================== {name: 'dungeon-decal-wall-crack1', x: 4, y: 2, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-crack2', x: 5, y: 2, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-face1-bottom', x: 7, y: 2, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-face1-top', x: 7, y: 1, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-face2-bottom', x: 8, y: 2, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-face2-top', x: 8, y: 1, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-moss', x: 8, y: 1, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-detail1', x: 9, y: 0, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-detail2', x: 9, y: 1, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-detail3', x: 9, y: 2, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-detail4', x: 9, y: 3, layer: 'sprite', solid: false}, // Wide wall decals (2 tiles wide) {name: 'dungeon-decal-wall-wide1-left', x: 10, y: 0, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-wide1-right', x: 11, y: 0, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-wide2-left', x: 10, y: 1, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-wide2-right', x: 11, y: 1, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-wide3-left', x: 10, y: 2, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-wide3-right', x: 11, y: 2, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-wide4-left', x: 10, y: 3, layer: 'sprite', solid: false}, {name: 'dungeon-decal-wall-wide4-right', x: 11, y: 3, layer: 'sprite', solid: false}, {name: 'dungeon-lantern', x: 4, y: 10, layer: 'sprite', solid: false}, // Pillar tiles (3 parts: base, middle, crown) {name: 'dungeon-decal-pillar-base', x: 6, y: 2, layer: 'sprite', solid: false}, {name: 'dungeon-decal-pillar-middle', x: 6, y: 1, layer: 'sprite', solid: false}, {name: 'dungeon-decal-pillar-crown', x: 6, y: 0, layer: 'top', solid: false}, // ========================================================================== // TRACK TILES (mine cart tracks) // ========================================================================== {name: 'dungeon-track-corner-up-right', x: 9, y: 5, layer: 'sprite', solid: false}, {name: 'dungeon-track-corner-left-down', x: 11, y: 5, layer: 'sprite', solid: false}, {name: 'dungeon-track-corner-up-left', x: 11, y: 7, layer: 'sprite', solid: false}, {name: 'dungeon-track-corner-right-down', x: 9, y: 7, layer: 'sprite', solid: false}, {name: 'dungeon-track-straight-vertical', x: 11, y: 6, layer: 'sprite', solid: false}, {name: 'dungeon-track-straight-horizontal', x: 10, y: 5, layer: 'sprite', solid: false}, {name: 'dungeon-track-end-left', x: 7, y: 6, layer: 'sprite', solid: false}, {name: 'dungeon-track-end-right', x: 8, y: 6, layer: 'sprite', solid: false}, {name: 'dungeon-track-end-top', x: 7, y: 5, layer: 'sprite', solid: false}, {name: 'dungeon-track-end-bottom', x: 8, y: 5, layer: 'sprite', solid: false}, {name: 'dungeon-track-barrel', x: 10, y: 6, layer: 'sprite', solid: false}, // ========================================================================== // FENCE TILES (horizontal fences) // ========================================================================== {name: 'dungeon-fence-left', x: 4, y: 6, layer: 'sprite', solid: true}, {name: 'dungeon-fence-middle', x: 5, y: 6, layer: 'sprite', solid: true}, {name: 'dungeon-fence-right', x: 6, y: 6, layer: 'sprite', solid: true}, ]; for (const tile of tiles) { tileMap.defineTile(tile.name, tile.x, tile.y, { layer: tile.layer, solid: tile.solid, }); } // ========================================================================== // ENTITY TILES (from tilemap_packed.png) // These are shared between dungeon and overworld levels // ========================================================================== const entityTiles = [ {name: 'player', x: 0, y: 18}, {name: 'zombie', x: 1, y: 18}, {name: 'wand', x: 10, y: 21}, {name: 'sword', x: 8, y: 19}, ]; for (const tile of entityTiles) { tileMap.defineTile(tile.name, tile.x, tile.y, { layer: 'sprite', solid: true, tilemap: 'tilemap_packed', // Load from character tileset }); } } // ============================================================================= // AUTOTILE RESULT TYPE // ============================================================================= export interface AutotileResult { face: string | null; // Tile name for face crown: string | null; // Tile name for crown (rendered above) } // ============================================================================= // AUTOTILE LOGIC - Determines which wall tiles to use based on neighbors // ============================================================================= import type {Neighbors} from './DungeonUtils.ts'; export function computeAutotile(neighbors: Neighbors): AutotileResult { const {n, s, e, w, ne, nw, se, sw} = neighbors; let face: string | null = null; let crown: string | null = null; // Priority 1: Inside corners (L-shaped floor cutting into wall mass) // Inside corner SW: walls to W and S, floor at SW if (w && s && !sw) { crown = 'dungeon-wall-corner-inside-sw'; face = 'dungeon-wall-corner-inside-sw'; return {face, crown}; } // Inside corner SE: walls to E and S, floor at SE if (e && s && !se) { crown = 'dungeon-wall-corner-inside-se'; return {face, crown}; } // Inside corner NW: walls to W and N, floor at NW if (w && n && !nw) { crown = 'dungeon-wall-corner-inside-nw'; return {face, crown}; } // Inside corner NE: walls to E and N, floor at NE if (e && n && !ne) { crown = 'dungeon-wall-corner-inside-ne'; return {face, crown}; } // Priority 2: Horizontal walls (no wall below) if (!s) { if (!w && e) { // Left end of horizontal wall face = 'dungeon-wall-horizontal-left-cap'; crown = 'dungeon-wall-crown-left-cap'; } else if (w && !e) { // Right end of horizontal wall face = 'dungeon-wall-horizontal-right-cap'; crown = 'dungeon-wall-crown-right-cap'; } else { // Middle of horizontal wall face = 'dungeon-wall-horizontal-middle'; crown = 'dungeon-wall-crown-middle'; } } // Priority 3: Vertical walls (wall below) else { if (!e) { // Left edge of vertical wall mass face = 'dungeon-wall-vertical-left'; crown = 'dungeon-wall-crown-vertical-left'; } else if (!w) { // Right edge of vertical wall mass face = 'dungeon-wall-vertical-right'; crown = 'dungeon-wall-crown-vertical-right'; } } // Priority 4: Top edge override (no wall above, wall below) if (!n && s) { if (!w && e) { crown = 'dungeon-wall-top-left'; } else if (w && !e) { crown = 'dungeon-wall-top-right'; } else { crown = 'dungeon-wall-top-middle'; } // Top edge walls don't show face tile face = null; } return {face, crown}; } // ============================================================================= // TILE NAME GETTERS FOR PROCEDURAL GENERATION // ============================================================================= export function getCeilingTile(noise: number): string { if (noise < 30) { return 'dungeon-ceiling-variant1'; } else if (noise < 40) { return 'dungeon-ceiling-variant2'; } return 'dungeon-ceiling-base'; } export function getGroundTile(noise: number): string { if (noise < 30) { return 'dungeon-ground-textured'; } else if (noise < 50) { return 'dungeon-ground-stone'; } return 'dungeon-ground-base'; } export function getShadowTile(noise: number): string { return noise < 70 ? 'dungeon-shadow-from-north' : 'dungeon-shadow-from-north-alt'; } // ============================================================================= // TRACK TILE HELPERS // ============================================================================= export const TRACK_TILE_NAMES = { cornerUpRight: 'dungeon-track-corner-up-right', cornerLeftDown: 'dungeon-track-corner-left-down', cornerUpLeft: 'dungeon-track-corner-up-left', cornerRightDown: 'dungeon-track-corner-right-down', straightVertical: 'dungeon-track-straight-vertical', straightHorizontal: 'dungeon-track-straight-horizontal', endLeft: 'dungeon-track-end-left', endRight: 'dungeon-track-end-right', endTop: 'dungeon-track-end-top', endBottom: 'dungeon-track-end-bottom', }; // ============================================================================= // FENCE TILE HELPERS // ============================================================================= export const FENCE_TILE_NAMES = { left: 'dungeon-fence-left', middle: 'dungeon-fence-middle', right: 'dungeon-fence-right', };