export enum TileType { DEEP_WATER = 0, SHALLOW_WATER = 1, SAND = 2, GRASS = 3, DARK_GRASS = 4, FOREST = 5, ROCK = 6, FLOOR = 7, WALL = 8, TILLED_SOIL = 9, WATERED_SOIL = 10, } export const IMPASSABLE = new Set([ TileType.DEEP_WATER, TileType.SHALLOW_WATER, TileType.FOREST, TileType.ROCK, TileType.WALL, ]) /** Tiles on which tree seedlings may be planted. */ export const PLANTABLE_TILES = new Set([TileType.GRASS, TileType.DARK_GRASS]) export type ItemId = 'wood' | 'stone' | 'wheat_seed' | 'carrot_seed' | 'wheat' | 'carrot' | 'tree_seed' export type BuildingType = 'floor' | 'wall' | 'chest' | 'bed' | 'stockpile_zone' export type CropKind = 'wheat' | 'carrot' export type JobType = 'chop' | 'mine' | 'farm' export type AIState = 'idle' | 'walking' | 'working' | 'sleeping' export interface JobPriorities { chop: number // 0 = disabled, 1 = highest, 4 = lowest mine: number farm: number } export interface VillagerJob { type: JobType targetId: string tileX: number tileY: number carrying: Partial> } export interface VillagerState { id: string name: string x: number y: number bedId: string | null job: VillagerJob | null priorities: JobPriorities energy: number aiState: AIState } export interface ResourceNodeState { id: string tileX: number tileY: number kind: 'tree' | 'rock' hp: number } export interface BuildingState { id: string tileX: number tileY: number kind: BuildingType ownerId: string } export interface CropState { id: string tileX: number tileY: number kind: CropKind stage: number maxStage: number stageTimerMs: number watered: boolean } export interface PlayerState { id: string x: number y: number inventory: Partial> } export interface TreeSeedlingState { id: string tileX: number tileY: number /** Growth stage: 0 = sprout, 1 = sapling, 2 = mature (converts to resource). */ stage: number /** Time remaining until next stage advance, in milliseconds. */ stageTimerMs: number /** The tile type that was under the seedling when planted (GRASS or DARK_GRASS). */ underlyingTile: TileType } export interface WorldState { seed: number tiles: number[] resources: Record buildings: Record crops: Record villagers: Record stockpile: Partial> /** Planted tree seedlings, keyed by ID. */ treeSeedlings: Record /** * Recovery timers for DARK_GRASS tiles, keyed by "tileX,tileY". * Value is remaining milliseconds until the tile reverts to GRASS. */ tileRecovery: Record } export interface GameStateData { version: number world: WorldState player: PlayerState } export type GameAction = | { type: 'PLAYER_MOVE'; x: number; y: number } | { type: 'HARVEST_RESOURCE'; resourceId: string; rewards: Partial> } | { type: 'CHANGE_TILE'; tileX: number; tileY: number; tile: TileType } | { type: 'PLACE_BUILDING'; building: BuildingState; costs: Partial> } | { type: 'REMOVE_BUILDING'; buildingId: string } | { type: 'ADD_ITEMS'; items: Partial> } | { type: 'PLANT_CROP'; crop: CropState; seedItem: ItemId } | { type: 'WATER_CROP'; cropId: string } | { type: 'HARVEST_CROP'; cropId: string; rewards: Partial> } | { type: 'SPAWN_VILLAGER'; villager: VillagerState } | { type: 'VILLAGER_SET_JOB'; villagerId: string; job: VillagerJob | null } | { type: 'VILLAGER_SET_AI'; villagerId: string; aiState: AIState } | { type: 'VILLAGER_HARVEST_RESOURCE'; villagerId: string; resourceId: string } | { type: 'VILLAGER_HARVEST_CROP'; villagerId: string; cropId: string } | { type: 'VILLAGER_DEPOSIT'; villagerId: string } | { type: 'UPDATE_PRIORITIES'; villagerId: string; priorities: JobPriorities } | { type: 'PLANT_TREE_SEED'; seedling: TreeSeedlingState } | { type: 'REMOVE_TREE_SEEDLING'; seedlingId: string } | { type: 'SPAWN_RESOURCE'; resource: ResourceNodeState } | { type: 'TILE_RECOVERY_START'; tileX: number; tileY: number }