/** * Resource Spawner — creates ECS entities for harvestable world objects * * Scans the generated tile grid for mineral veins and geysers, * creates an entity at each with a randomly assigned element. */ import { addEntity, addComponent } from 'bitecs'; import type { World } from '../ecs/world'; import { Position, Resource, SpriteRef } from '../ecs/components'; import type { TileGrid, BiomeData } from './types'; import { pickResourceElement, MINERAL_ELEMENTS, GEYSER_ELEMENTS, type ResourceInfo, } from '../player/interaction'; /** Resource spawn configuration per tile type */ interface ResourceTileConfig { tileId: number; elements: readonly string[]; minQuantity: number; maxQuantity: number; interactRange: number; spriteColor: number; spriteRadius: number; } /** * Spawn resource entities for all resource tiles in the grid. * @returns Map of entity ID → ResourceInfo for string data */ export function spawnResources( world: World, grid: TileGrid, biome: BiomeData, seed: number, ): Map { const resourceData = new Map(); // Find tile IDs for resource types (generic: resource + interactive tiles) const mineralTile = biome.tiles.find(t => t.resource); const geyserTile = biome.tiles.find(t => t.interactive); const configs: ResourceTileConfig[] = []; if (mineralTile) { configs.push({ tileId: mineralTile.id, elements: MINERAL_ELEMENTS, minQuantity: 3, maxQuantity: 5, interactRange: 40, spriteColor: 0xffd700, // gold spriteRadius: 4, }); } if (geyserTile) { configs.push({ tileId: geyserTile.id, elements: GEYSER_ELEMENTS, minQuantity: 2, maxQuantity: 4, interactRange: 48, spriteColor: 0xff6600, // orange spriteRadius: 5, }); } const tileSize = biome.tileSize; for (let y = 0; y < grid.length; y++) { for (let x = 0; x < grid[y].length; x++) { const tileId = grid[y][x]; const config = configs.find(c => c.tileId === tileId); if (!config) continue; // Pick element deterministically const itemId = pickResourceElement(x, y, seed, config.elements); // Quantity from deterministic hash const qHash = ((x * 48611) ^ (y * 29423) ^ (seed * 61379)) >>> 0; const range = config.maxQuantity - config.minQuantity + 1; const quantity = config.minQuantity + (qHash % range); // Create entity at tile center const eid = addEntity(world); addComponent(world, eid, Position); addComponent(world, eid, Resource); addComponent(world, eid, SpriteRef); Position.x[eid] = x * tileSize + tileSize / 2; Position.y[eid] = y * tileSize + tileSize / 2; Resource.quantity[eid] = quantity; Resource.interactRange[eid] = config.interactRange; SpriteRef.color[eid] = config.spriteColor; SpriteRef.radius[eid] = config.spriteRadius; resourceData.set(eid, { itemId, tileX: x, tileY: y }); } } return resourceData; }