use std::cell::RefCell; use std::rc::Rc; use crate::keys::Keyring; /// The game-global player state: stats that follow the player across boards. /// /// Owned by [`GameState::player`](crate::game::GameState) (a single value, not /// per-board), so health, gems, and keys persist through board transitions. /// Distinct from [`PlayerPos`](crate::utils::PlayerPos), which is the player's /// position *on a particular board*. A `Copy` snapshot is handed to each script /// hook and exposed to Rhai (read-only) as the `Player` constant. #[derive(Copy, Clone)] pub struct Player { /// The player's current health. Game-global (not per-board), so it persists /// across board transitions. Scripts change it via `alter_health(dh)`, which /// clamps to `[0, max_health]`. Front-ends display it via the status sidebar. pub health: i64, /// The maximum health the player can have. Scripts clamp `alter_health` to /// this ceiling; the status sidebar uses it to size the heart row. pub max_health: i64, /// The player's key inventory. Game-global; persists across board transitions. pub keys: Keyring, /// The number of gems the player has collected. Game-global like /// [`health`](Player::health); starts at `0`. pub gems: i64, } pub type PlayerRef = Rc>; impl Player { /// Create a new PlayerRef from a default player pub fn new_ref() -> PlayerRef { Rc::new(RefCell::from(Player::default())) } /// Attempt to modify the gem total by the given amount, but maintain a minimum of zero. /// If the modification would take up below zero, leave it alone and return false. pub fn alter_gems(&mut self, delta: i64) -> bool { if delta + self.gems < 0 { false } else { self.gems += delta; true } } /// Modify the player's health by the given delta, clamped to within the range `[0, max_health]` pub fn alter_health(&mut self, delta: i64) { self.health = (self.health + delta).clamp(0, self.max_health); } } impl Default for Player { fn default() -> Player { Self { health: 5, max_health: 5, gems: 0, keys: Keyring::default(), } } }