2026-06-07 00:19:53 -05:00
|
|
|
use crate::glyph::Glyph;
|
2026-06-07 01:26:18 -05:00
|
|
|
use crate::utils::ObjectId;
|
2026-06-15 23:35:18 -05:00
|
|
|
use color::Rgba8;
|
|
|
|
|
use std::collections::HashSet;
|
2026-06-07 00:19:53 -05:00
|
|
|
|
|
|
|
|
/// 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.
|
2026-06-20 19:05:46 -05:00
|
|
|
#[derive(Clone)]
|
2026-06-07 00:19:53 -05:00
|
|
|
pub struct ObjectDef {
|
2026-06-07 01:26:18 -05:00
|
|
|
/// 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,
|
2026-06-07 00:19:53 -05:00
|
|
|
/// Column of this object on the board (0-indexed).
|
|
|
|
|
pub x: usize,
|
|
|
|
|
/// Row of this object on the board (0-indexed).
|
|
|
|
|
pub y: usize,
|
2026-06-15 23:35:18 -05:00
|
|
|
/// 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,
|
2026-06-07 00:19:53 -05:00
|
|
|
/// 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,
|
2026-06-21 18:27:45 -05:00
|
|
|
/// 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,
|
2026-06-21 18:44:42 -05:00
|
|
|
/// 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.
|
2026-06-07 00:19:53 -05:00
|
|
|
pub script_name: Option<String>,
|
2026-06-16 14:34:42 -05:00
|
|
|
/// Embedded built-in script source, set when a script-backed archetype (e.g. a
|
2026-06-21 18:44:42 -05:00
|
|
|
/// `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.
|
2026-06-16 14:34:42 -05:00
|
|
|
pub builtin_script: Option<&'static str>,
|
2026-06-07 01:26:18 -05:00
|
|
|
/// 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<String>,
|
2026-06-08 19:50:43 -05:00
|
|
|
/// 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<String>,
|
2026-06-07 00:19:53 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 {
|
2026-06-07 01:26:18 -05:00
|
|
|
id: 0,
|
2026-06-07 00:19:53 -05:00
|
|
|
x,
|
|
|
|
|
y,
|
2026-06-15 23:35:18 -05:00
|
|
|
z: 0,
|
2026-06-07 00:19:53 -05:00
|
|
|
glyph: Self::default_glyph(),
|
|
|
|
|
solid: true,
|
|
|
|
|
opaque: true,
|
|
|
|
|
pushable: false,
|
2026-06-21 18:27:45 -05:00
|
|
|
grab: false,
|
2026-06-07 00:19:53 -05:00
|
|
|
script_name: None,
|
2026-06-16 14:34:42 -05:00
|
|
|
builtin_script: None,
|
2026-06-07 01:26:18 -05:00
|
|
|
tags: HashSet::new(),
|
2026-06-08 19:50:43 -05:00
|
|
|
name: None,
|
2026-06-07 00:19:53 -05:00
|
|
|
}
|
|
|
|
|
}
|
2026-06-15 23:35:18 -05:00
|
|
|
}
|