This commit is contained in:
2026-06-25 19:16:46 -05:00
parent db8c8e615d
commit db9a5d37b6
8 changed files with 328 additions and 213 deletions
+15 -12
View File
@@ -1,7 +1,7 @@
use crate::action::Action;
use crate::board::Board;
use crate::log::LogLine;
use crate::script::ScriptHost;
use crate::script::{ScriptHost, ScriptState};
use crate::utils::{Direction, ObjectId, PlayerPos, ScriptArg};
use crate::world::World;
use std::cell::{Ref, RefMut};
@@ -67,7 +67,10 @@ pub struct GameState {
/// by [`enter_board`](GameState::enter_board). Front-ends tick this down and
/// may block input or show a visual effect while it is `Some(t)` where `t > 0`.
pub board_transition: Option<f64>,
/// The player's state
/// The game-global player state (health, gems, keys) — see [`Player`]. Not
/// per-board: it persists across board transitions, unlike the per-board
/// position in [`Board::player`](crate::board::Board::player). Scripts mutate
/// it via `add_gems`/`alter_health`/`set_key` and read a snapshot of it.
pub player: Player,
}
@@ -159,7 +162,7 @@ impl GameState {
/// the game is about to start — never during map deserialization, since a script
/// may inspect the board.
pub fn run_init(&mut self) {
self.scripts.run_init(self.player);
self.scripts.run_init(ScriptState(self.player));
self.resolve();
}
@@ -173,7 +176,7 @@ impl GameState {
// this runs exactly once per player interaction with a scroll.
self.handle_scroll();
let secs = dt.as_secs_f64();
self.scripts.run_tick(self.player, secs);
self.scripts.run_tick(ScriptState(self.player), secs);
// Expire speech bubbles before resolving new actions so a fresh say()
// this frame isn't immediately culled.
self.speech_bubbles.retain_mut(|b| {
@@ -308,11 +311,11 @@ impl GameState {
Action::Shift(cells) => {
logs.extend(board.apply_shift(&cells));
}
// Accumulated and applied to `self.player_gems` after the borrow drops.
// Accumulated and applied to `self.player.gems` after the borrow drops.
Action::AddGems(n) => gem_delta += n,
// Accumulated and applied to `self.player_health` after the borrow drops.
// Accumulated and applied to `self.player.health` after the borrow drops.
Action::AlterHealth(dh) => health_delta += dh,
// Collected and applied to `self.player_keys` after the borrow drops.
// Collected and applied to `self.player.keys` after the borrow drops.
Action::SetKey(color, present) => key_changes.push((color, present)),
// A grab thing despawns itself from its grab() hook.
Action::Die => {
@@ -345,10 +348,10 @@ impl GameState {
}
}
for (bumped, bumper) in bumps {
self.scripts.run_bump(self.player, bumped, bumper);
self.scripts.run_bump(ScriptState(self.player), bumped, bumper);
}
for (target, fn_name, arg) in sends {
self.scripts.run_send(self.player, target, &fn_name, arg);
self.scripts.run_send(ScriptState(self.player), target, &fn_name, arg);
}
self.drain_errors();
}
@@ -364,7 +367,7 @@ impl GameState {
if let Some(scroll) = self.active_scroll.take()
&& let Some(choice) = scroll.choice
{
self.scripts.run_send(self.player, scroll.source, &choice, None);
self.scripts.run_send(ScriptState(self.player), scroll.source, &choice, None);
self.drain_errors();
}
}
@@ -471,11 +474,11 @@ impl GameState {
// Fire the grab hook and resolve it immediately so the grabbed thing's
// die()/add_gems() apply now — no player+object overlap survives this call.
if let Some(id) = grabbed {
self.scripts.run_grab(self.player, id);
self.scripts.run_grab(ScriptState(self.player), id);
self.resolve();
}
if let Some(idx) = bumped {
self.scripts.run_bump(self.player, idx, -1);
self.scripts.run_bump(ScriptState(self.player), idx, -1);
self.drain_errors();
}
}