import type { RPGAction, RPGActions, RPGVariables } from "../types"; import { World } from "../core/world"; import type { EvalContext, Entity } from "../core/world"; export function resolveVariables(entity: Entity): RPGVariables; export function resolveVariables(world: World): RPGVariables; export function resolveVariables(entityOrWorld: Entity | World): RPGVariables { const result: RPGVariables = {}; if (entityOrWorld instanceof World) { for (const entity of entityOrWorld) { for (const [key, value] of Object.entries(resolveVariables(entity))) { result[`${entity.id}.${key}`] = value; } } } else { for (const [key, component] of entityOrWorld) { for (const [varKey, value] of Object.entries(component.getVariables())) { if (value != null) { if (varKey && varKey !== '.') { result[`${key}.${varKey}`] = value; } else { result[key] = value; } } } } } return result; } export function resolveVariable(name: string, ctx: EvalContext): RPGVariables[string] { // $. prefix → world globals if (name.startsWith('$.')) { return ctx.world.globals[name.slice(2)]; } // @entityId.component.variable → another entity if (name.startsWith('@')) { const dotIdx = name.indexOf('.', 1); if (dotIdx === -1) return undefined; const entityId = name.slice(1, dotIdx); const varName = name.slice(dotIdx + 1); const entity = ctx.world.getEntity(entityId); if (!entity) { console.warn(`[resolveVariable] entity '${entityId}' not found (referenced in '${name}')`); return undefined; } return resolveVariables(entity)[varName]; } // bare name → self entity return resolveVariables(ctx.self)[name]; } export function resolveActions(entity: Entity): RPGActions; export function resolveActions(world: World): RPGActions; export function resolveActions(entityOrWorld: Entity | World): RPGActions { const result: RPGActions = {}; if (entityOrWorld instanceof World) { for (const entity of entityOrWorld) { for (const [key, value] of Object.entries(resolveActions(entity))) { result[`${entity.id}.${key}`] = value; } } } else { for (const [key, component] of entityOrWorld) { for (const [actionKey, fn] of Object.entries(component.getActions())) { result[`${key}.${actionKey}`] = fn; } } } return result; } export async function executeAction(action: RPGAction, ctx: EvalContext): Promise { let entity = ctx.self; let actionType = action.type; // @entityId.component.action → dispatch to another entity if (action.type.startsWith('@')) { const dotIdx = action.type.indexOf('.', 1); if (dotIdx === -1) { console.warn(`[executeAction] malformed cross-entity action '${action.type}': missing '.' after entity id`); return; } const entityId = action.type.slice(1, dotIdx); const found = ctx.world.getEntity(entityId); if (!found) { console.warn(`[executeAction] entity '${entityId}' not found (action '${action.type}')`); return; } entity = found; actionType = action.type.slice(dotIdx + 1); } const actions = resolveActions(entity); if (!(actionType in actions)) { console.warn(`[executeAction] action '${actionType}' not found on entity '${entity.id}'`); return; } return actions[actionType](action.arg, ctx); }