core/animation-proxy.js

/**
 * AnimationProxy — bridges immutable piece singletons and their per-position
 * animation state (frame counter).
 *
 * Mirrors Java's AnimationProxy: every animated piece placed on the board gets
 * its own proxy that tracks (x, y) and an independent frame counter. The piece
 * itself stays immutable; the proxy carries the mutable frame state.
 *
 * The Board maintains a list of these; AnimationManager walks the list each
 * tick instead of scanning every cell.
 */
export class AnimationProxy {
  /**
   * @param {number} x
   * @param {number} y
   * @param {object} piece  - an agent or terrain with an `onFrame` method
   * @param {boolean} randomSeed  - if true, start at a random frame offset
   */
  constructor(x, y, piece, randomSeed = true) {
    this.x = x;
    this.y = y;
    this.piece = piece;
    this.frame = randomSeed ? Math.floor(Math.random() * 100) : 0;
  }

  /** True if this proxy represents the given piece at the given position. */
  proxyFor(x, y, piece) {
    return this.x === x && this.y === y && this.piece === piece;
  }

  /**
   * Update position without destroying/recreating the proxy (used when an
   * agent moves to an adjacent cell).
   */
  setXY(x, y) {
    this.x = x;
    this.y = y;
  }

  /**
   * Execute one animation frame: call piece.onFrame with the current counter,
   * then increment it.
   * @param {GameEvent} event
   * @param {Board} board
   */
  tick(event, board) {
    const cell = board.getCellAt(this.x, this.y);
    if (cell) {
      this.piece.onFrame(event, cell, this.frame);
      this.frame++;
    }
  }
}