use crate::glyph::Glyph; use crate::utils::ObjectId; use color::Rgba8; use std::collections::HashSet; /// 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. 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, /// Index of the layer this object belongs to (0 = bottom). Determines its /// draw order: objects on higher layers render above lower-layer terrain and /// objects. Set at load from the layer the object's palette char appeared in. pub z: 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, /// Name of the Rhai script in [`Board::scripts`] that drives this object. /// `None` means this object has no script yet. pub script_name: Option, /// 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, } 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, z: 0, glyph: Self::default_glyph(), solid: true, opaque: true, pushable: false, script_name: None, tags: HashSet::new(), name: None, } } }