1
0
Fork 0
tsgames/src/common/rpg/utils/variables.ts

100 lines
3.7 KiB
TypeScript

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<unknown> {
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);
}