core/agent-proxy.ts

import { Agent } from "./agent.ts";
import { Cell } from "./cell.ts";
import { Direction } from "./direction.ts";
import { GameEvent } from "./game-event.ts";
import { isAnimated } from "./animated.ts";
import { Item } from "./item.ts";

/**
 * Abstract base class for agent proxies (a kind of decorator class).
 *
 * Wraps another agent and forwards all behaviour to it, with the exception of
 * `onTurn` which is intentionally suppressed so that creature AI is disabled
 * for the duration of any proxy effect (giving paralysis for free for
 * non-player agents).
 *
 * Subclasses can override individual callbacks to add effect-specific behaviour.
 * The wrapped agent is accessible as `this.agent`.
 *
 * Mirrors Java's `AgentProxy` abstract class.
 */
export class AgentProxy extends Agent {
  protected agent: Agent;
  constructor(agent: Agent, flags: number) {
    super(agent.name, flags, agent.color, agent.symbol);
    this.agent = agent;
  }

  /**
   * Returns true if either the proxy's own flags OR the wrapped agent has the
   * given flag. This is critical so that `PLAYER` propagates through
   * a `Paralyzed` wrapper.
   */
  is(flag: number): boolean {
    return this.agent.is(flag) || (this.flags & flag) === flag;
  }

  /** Returns true only if neither the proxy flags nor the wrapped agent has the given flag. */
  not(flag: number): boolean {
    return this.agent.not(flag) && (this.flags & flag) !== flag;
  }

  changeHealth(value: number): number {
    return this.agent.changeHealth(value);
  }

  canEnter(direction: Direction, from: Cell, to: Cell): boolean {
    return this.agent.canEnter(direction, from, to);
  }

  onDie(event: GameEvent, cell: Cell) {
    this.agent.onDie(event, cell);
  }

  onHit(event: GameEvent, attackerCell: Cell, agentLoc: Cell, agent: Agent) {
    this.agent.onHit(event, attackerCell, agentLoc, agent);
  }

  onHitBy(event: GameEvent, agentLoc: Cell, agent: Agent, dir: Direction) {
    this.agent.onHitBy(event, agentLoc, agent, dir);
  }

  onHitByItem(event: GameEvent, itemLoc: Cell, item: Item, dir: Direction) {
    this.agent.onHitByItem(event, itemLoc, item, dir);
  }

  /** Delegates to the wrapped agent's onFrame if it defines one. */
  onFrame(event: GameEvent, cell: Cell, frame: number) {
    if (isAnimated(this.agent)) {
      this.agent.onFrame(event, cell, frame);
    }
  }

  // onTurn is intentionally NOT delegated — suppresses creature AI while any
  // proxy effect is active.
}