clone

an rts arena for scripts: submit tribe strategies over the web or mcp, run deterministic matches, watch them

main
tribes / web / src / sim / frames.ts
frames.ts 4.1K
1/** Messages exchanged between the viewer page and the sim Web Worker (v2). */2import type {3 MetricsFrame,4 Resource,5 SimBundle,6 SimEvent,7 SimResult,8 ThreatKind,9 WarbandKind,10} from '@tribes/protocol';1112/** A settlement as the map draws it (own or sighted-enemy). */13export interface FrameSettlement {14 id: number;15 tribe: number;16 x: number;17 y: number;18 /** 0 camp - 1 village - 2 town - 3 city. */19 tier: number;20 /** Wall building count/level when known (own view or enemy sighting). */21 wallLevel?: number;22 /** Population — 0 for fogged enemy settlements (not visible). */23 pop: number;24}2526/** A warband as the map draws it (own or sighted-enemy). */27export interface FrameWarband {28 id: number;29 tribe: number;30 x: number;31 y: number;32 size: number;33 kind: WarbandKind;34}3536export interface FrameCaravan {37 id: number;38 tribe: number;39 x: number;40 y: number;41 resource: Resource;42 amount: number;43}4445export interface FrameThreat {46 id: number;47 kind: ThreatKind;48 x: number;49 y: number;50 strength: number;51}5253/** Per-tribe summary line shown in the side panels every frame. */54export interface FrameTribe {55 id: number;56 alive: boolean;57 pop: number;58 settlements: number;59 warbands: number;60 coin: number;61 /** Latest headline score (from the most recent metrics frame; 0 early). */62 score: number;63 /** Prestige age index (0 wandering .. 3 high). */64 age: number;65 /** Script went over its gas budget this tick. */66 overBudget: boolean;67 /** Last runtime error thrown by the script, if any. */68 error: string | null;69}7071/** One api.log() line, tick-stamped by the worker when it was drained. */72export interface LogLine {73 t: number;74 text: string;75}7677/**78 * A viewer frame. Posted every `sampleEvery` ticks. The heavy map payload79 * (terrain + entity arrays) is only BUILT while the page is watching the map80 * tab ({type:'watch', on:true}); otherwise `hasMap` is false, `terrain` is ''81 * and the entity arrays are empty — the cheap parts (tick, tribes summary,82 * feed channels) always ride along so panels and feeds never stall.83 */84export interface MapFrame {85 tick: number;86 maxTicks: number;87 /** POV tribe id, or -1 for omniscient. */88 pov: number;89 mapSize: number;90 /** True when the map payload below is populated (watch mode on). */91 hasMap: boolean;92 /** Fog-filtered terrain (row-major mapSize² TerrainChars, '' when !hasMap). */93 terrain: string;94 /** Distant category glimpses (DistantChar per cell; rendered ~35% alpha). */95 distant: string;96 /** Territory claims ('.' none, '0'-'7' tribe) — organic, terrain-shaped. */97 claims: string;98 settlements: FrameSettlement[];99 warbands: FrameWarband[];100 caravans: FrameCaravan[];101 threats: FrameThreat[];102 tribes: FrameTribe[];103 /**104 * Feed channels accumulated since the previous posted frame (the worker105 * buffers between samples so nothing is lost, oldest dropped past a cap).106 * - eventsByTribe[t]: tribe t's own fog-filtered events, minus other107 * tribes' say broadcasts (those live in `world`).108 * - logsByTribe[t]: tribe t's api.log() lines — its internal monologue.109 * - world: high-level story beats — says, eliminations, forfeits,110 * settlement founds/captures, tech unlocks, threat spawns, tribal battles.111 */112 eventsByTribe: Record<number, SimEvent[]>;113 logsByTribe: Record<number, LogLine[]>;114 world: SimEvent[];115}116117export type WorkerInMsg =118 | { type: 'init'; bundle: SimBundle; mock: boolean; pov: number; logLevel?: 'debug' | 'info' | 'warn' | 'error' }119 /** Allow the worker to advance `n` more ticks; emit a frame every `sampleEvery` ticks. */120 | { type: 'credit'; n: number; sampleEvery: number }121 | { type: 'setPov'; pov: number }122 /** Map payloads are only built & streamed while watching. */123 | { type: 'watch'; on: boolean };124125export type WorkerOutMsg =126 | { type: 'ready' }127 | { type: 'frame'; frame: MapFrame }128 /** Posted whenever engine.latestMetrics() yields a new frame (every SLOW_TICK_INTERVAL). */129 | { type: 'metrics'; frame: MetricsFrame }130 | { type: 'done'; result: SimResult; stateHash: string }131 | { type: 'fatal'; error: string };