Files
kiln/kiln-core/src/utils.rs
T
2026-06-07 00:19:53 -05:00

113 lines
4.5 KiB
Rust

use serde::{Deserialize, Serialize};
use crate::archetype::Archetype;
use crate::script::Direction;
/// Which directions a solid may be pushed in.
///
/// Only meaningful for `solid` cells (a non-solid cell is passable, so nothing is
/// ever pushed into it). `Crate` is [`Pushable::Any`]; the directional crates
/// constrain pushes to one axis.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Pushable {
/// Cannot be pushed.
No,
/// Pushable in any direction.
Any,
/// Pushable east/west only.
Horizontal,
/// Pushable north/south only.
Vertical,
}
impl Pushable {
/// Whether a push in `dir` is allowed.
pub fn allows(self, dir: Direction) -> bool {
match self {
Pushable::No => false,
Pushable::Any => true,
Pushable::Horizontal => matches!(dir, Direction::East | Direction::West),
Pushable::Vertical => matches!(dir, Direction::North | Direction::South),
}
}
}
/// The behavioral properties of a board cell at runtime.
///
/// `Behavior` is a plain data struct returned by [`Archetype::behavior`]. It
/// contains the properties the engine needs to simulate a cell — currently
/// solidity, opacity, and pushability. Future properties (shootable, etc.) can
/// be added here without changing call sites.
///
/// For scripted objects, solidity and opacity are stored directly on
/// [`ObjectDef`] and will eventually be overridable by Rhai scripts at runtime.
#[derive(Copy, Clone, Debug)]
pub struct Behavior {
/// Whether this cell blocks / participates in movement. A solid cell stops a
/// mover (and is the only kind of cell that can later be pushed or receive a
/// collision event). This is the inverse of the old `passable` flag.
pub solid: bool,
/// Whether this cell blocks line of sight (reserved for future rendering).
pub opaque: bool,
/// Which directions a mover can shove this cell in (only meaningful when `solid`).
pub pushable: Pushable,
}
/// A stable, unique identifier for a board object.
///
/// Ids are handed out by [`Board::add_object`] from the per-board
/// [`Board::next_object_id`] counter (starting at 1) and never reused, so a
/// reference to an object stays valid even as other objects are added or removed.
/// This replaces the old "index into `Board::objects`" scheme, which shifted when
/// objects were reordered/destroyed.
pub type ObjectId = u32;
/// The single solid occupant of a board cell, returned by [`Board::solid_at`].
///
/// At most one solid — the player, a grid [`Archetype`], *or* an [`ObjectDef`] — may
/// occupy a cell (the invariant enforced at load time), so this represents the one
/// thing a mover would collide with there.
pub enum Solid {
/// The player occupies the cell. The player is solid (it blocks movers) and
/// pushable in any direction (see [`Board::is_pushable`]).
Player,
/// The cell's grid archetype is itself solid (e.g. [`Archetype::Wall`]).
Cell(Archetype),
/// A solid [`ObjectId`] occupies the cell.
Object(ObjectId),
}
/// A portal that teleports the player to a named entry point on another board.
///
/// Portals are loaded from `[[portals]]` entries in `.toml` map files and
/// stored on [`Board`]. When the player steps onto a portal's cell, they
/// should be moved to `target_entry` on the board named by `target_map`.
///
/// **Not yet runtime-wired.** Portal navigation (multi-board loading and
/// switching) is a future feature.
#[derive(Deserialize, Serialize)]
pub struct PortalDef {
/// Column of this portal on the board (0-indexed).
pub x: usize,
/// Row of this portal on the board (0-indexed).
pub y: usize,
/// File name (without extension) of the target board, e.g. `"cave"`.
pub target_map: String,
/// Named entry point on the target board where the player arrives.
pub target_entry: String,
}
/// The player's current position on the board.
///
/// The player is currently a special entity rendered on top of the board
/// rather than being stored as a board cell. This is expected to change:
/// the player will eventually become a scripted object that responds to
/// input events, at which point this struct may be removed or made optional.
/// See the "player may become an object" notes in `CLAUDE.md` for the design
/// tensions this implies.
#[derive(Copy, Clone)]
pub struct Player {
/// Column position (0-indexed, increasing rightward).
pub x: i32,
/// Row position (0-indexed, increasing downward).
pub y: i32,
}