use crate::glyph::Glyph; use crate::utils::ObjectId; use color::Rgba8; use std::collections::HashSet; use crate::api::queue::ObjQueue; /// A scripted object placed on the board, loaded from a map file. /// /// `ObjectDef` represents a tile that has Rhai script attached to it. /// Scripts can respond to events like the player touching or shooting the /// tile. Objects are parsed from `[[objects]]` entries in `.toml` map files /// and stored on [`Board`]. /// /// Objects are rendered as an overlay on top of the board grid — the grid /// cell at `(x, y)` holds the background (floor) that is revealed if the /// object moves away. `glyph` is owned by the object itself and may be /// mutated by its Rhai script at runtime. /// /// Script text lives in [`Board::scripts`]; this struct holds only the name /// used to look it up. Two `ObjectDef`s with the same `script_name` share /// source text but run with independent Rhai scopes (see [`crate::script`]). /// /// Scripts are executed by [`crate::script::ScriptHost`]: an object's optional /// `init()` and `tick(dt)` functions are called via [`GameState::run_init`] and /// [`GameState::tick`]. Other event hooks (touch, shoot, …) are future work. #[derive(Clone)] pub struct ObjectDef { /// Stable identity assigned by [`crate::board::Board::add_object`]. /// `0` is the sentinel meaning "not yet inserted into a board". /// Real ids start at 1 and never change after assignment. /// Not serialized — the id is stamped on insert, not stored in map files. pub id: ObjectId, /// Column of this object on the board (0-indexed). pub x: usize, /// Row of this object on the board (0-indexed). pub y: usize, /// Visual representation of this object. Owned by the object (not derived /// from the grid cell), so scripts can change tile, fg, and bg at runtime. pub glyph: Glyph, /// Whether the object blocks / participates in movement. A solid object stops /// a mover walking into it; at most one solid (object or grid archetype) may /// occupy a cell. Inverse of the old `passable` flag. pub solid: bool, /// Whether the object blocks line of sight / FOV for the player pub opaque: bool, /// Whether the object can be shoved by a mover. Unused for now (no push /// mechanic yet); defaults to `false`. pub pushable: bool, /// Whether walking into this object **grabs** it: the player moves onto the /// cell and the object's `grab()` hook fires (the object is expected to /// remove itself via `die()`). Set when a grab archetype (e.g. a gem) is /// expanded into an object. Defaults to `false`. pub grab: bool, /// Light radius in cells that this object emits on a `dark` board (0 = no /// light). The emitted *color* is this object's own glyph foreground color, /// so a red glyph casts red light. See [`crate::fov`] for the lighting model. /// Scripts can change it at runtime via `set_light(n)`. Defaults to `0`. pub light: u32, /// Compile-key of the Rhai script that drives this object: a name in /// [`World::scripts`](crate::world::World::scripts) for a hand-authored object, /// or a synthetic `BUILTIN_*` name set when a script-backed archetype is /// expanded (see [`builtin_script`](ObjectDef::builtin_script)). `None` means /// this object has no script yet. pub script_name: Option, /// Embedded built-in script source, set when a script-backed archetype (e.g. a /// `pusher_*` or `gem`) is expanded into an object at load time (see /// [`crate::builtin_scripts`]). When set, this is the object's script *source* /// (its compile-key is the synthetic `BUILTIN_*` [`script_name`](ObjectDef::script_name) /// the same expansion assigns). Not part of the map file — it is regenerated /// from the archetype on load. pub builtin_script: Option<&'static str>, /// Open-ended string labels for this object. Serialized as a TOML array; /// not subject to any rate limit — mutations take effect immediately after /// the frame's action queue is drained. pub tags: HashSet, /// Optional unique human-readable name for this object. `None` if unnamed. /// Names are validated for uniqueness at map-load time; a duplicate name is /// cleared to `None` (the object survives but becomes anonymous). pub name: Option, /// The output queue of actions for this object pub queue: ObjQueue, } impl ObjectDef { /// Returns the default glyph for a newly placed object: tile 63 (`?`) in yellow on black. #[rustfmt::skip] pub fn default_glyph() -> Glyph { Glyph { tile: 63, fg: Rgba8 { r: 255, g: 255, b: 0, a: 255 }, // yellow bg: Rgba8 { r: 0, g: 0, b: 0, a: 255 }, } } /// Creates a new object at `(x, y)` with default glyph and blocking behavior. /// /// Defaults: `solid = true`, `opaque = true`, `pushable = false`, no script. /// These match the serde defaults in the map file format so new objects /// round-trip correctly. pub fn new(x: usize, y: usize) -> Self { Self { id: 0, x, y, glyph: Self::default_glyph(), solid: true, opaque: true, pushable: false, grab: false, light: 0, script_name: None, builtin_script: None, tags: HashSet::new(), queue: ObjQueue::new(), name: None, } } }