core/item.ts

import { Agent } from "./agent.ts";
import { Cell } from "./cell.ts";
import { GameEvent } from "./game-event.ts";
import { Piece } from "./piece.ts";

/**
 * Base class for all item types.
 *
 * Items can be picked up, carried, used, thrown, and fired. They do not block
 * movement into a cell. All callbacks are no-ops by default.
 */
export class Item extends Piece {
  /**
   * The player is about to throw this item.
   * Cancel event to prevent the throw.
   */
  onThrow(event: GameEvent, cell: Cell) { }

  /**
   * The player is firing this item (ranged weapon).
   * Return the ammunition piece to launch, or null if unable to fire.
   * Cancel event to abort firing.
   */
  onFire(event: GameEvent): Item | null {
    return null;
  }

  /**
   * This item has struck an agent (thrown or wielded).
   */
  onHit(event: GameEvent, agentLoc: Cell, agent: Agent) { }

  /**
   * This item, having been thrown, has landed at the given cell.
   * Cancel event to make the item disappear instead of landing.
   */
  onThrowEnd(event: GameEvent, cell: Cell) { }

  /**
   * The player used this item without specifying a direction.
   * Cancel event with a message if the item requires a direction.
   */
  onUse(event: GameEvent) { }

  /**
   * This item is about to be dropped.
   * Cancel event to prevent the drop.
   */
  onDrop(event: GameEvent, cell: Cell) { }

  /**
   * This item was selected in the player's inventory.
   */
  onSelect(event: GameEvent, cell: Cell) { }

  /**
   * This item is about to be deselected.
   * Cancel event to prevent deselection.
   */
  onDeselect(event: GameEvent, cell: Cell) { }

  /**
   * An agent has stepped onto the same cell as this item.
   */
  onSteppedOn(event: GameEvent, agentLoc: Cell, agent: Agent) { }

  /**
   * Return a phrase with the item name using a definite article (e.g., "the sword").
   */
  getDefiniteNoun(phrase: string): string {
    return phrase.replace("{0}", `the ${this.name}`);
  }

  /**
   * Return a phrase with the item name using an indefinite article (e.g., "a sword").
   */
  getIndefiniteNoun(phrase: string): string {
    const article = /^[aeiou]/i.test(this.name) ? "an" : "a";
    return phrase.replace("{0}", `${article} ${this.name}`);
  }
}