heart containers
This commit is contained in:
@@ -79,6 +79,9 @@ pub(crate) enum Action {
|
||||
/// Add `n` to the player's gem count (negative subtracts; the count is
|
||||
/// clamped at 0). Zero time cost. Applied to `GameState::player_gems`.
|
||||
AddGems(i64),
|
||||
/// Add `dh` to the player's health (clamped to `[0, max_health]`). Zero time
|
||||
/// cost. Applied to `GameState::player_health`.
|
||||
AlterHealth(i64),
|
||||
/// Give (`true`) or take (`false`) the named key color from the player.
|
||||
///
|
||||
/// Color must be one of `"blue"`, `"green"`, `"cyan"`, `"red"`, `"purple"`,
|
||||
@@ -197,6 +200,10 @@ pub(crate) fn action_to_map(action: &Action) -> rhai::Map {
|
||||
m.insert("type".into(), ds("AddGems"));
|
||||
m.insert("n".into(), Dynamic::from(*n));
|
||||
}
|
||||
Action::AlterHealth(dh) => {
|
||||
m.insert("type".into(), ds("AlterHealth"));
|
||||
m.insert("dh".into(), Dynamic::from(*dh));
|
||||
}
|
||||
Action::SetKey(color, present) => {
|
||||
m.insert("type".into(), ds("SetKey"));
|
||||
m.insert("color".into(), Dynamic::from(color.clone()));
|
||||
|
||||
@@ -103,6 +103,10 @@ builtins! {
|
||||
behavior: Behavior { solid: true, opaque: false, pushable: Pushable::Any, grab: true },
|
||||
script: include_str!("scripts/gem.rhai"),
|
||||
},
|
||||
Heart => ["heart" => g(3, 0xCC, 0x22, 0x22)] {
|
||||
behavior: Behavior { solid: true, opaque: false, pushable: Pushable::Any, grab: true },
|
||||
script: include_str!("scripts/heart.rhai"),
|
||||
},
|
||||
Pusher => [
|
||||
"pusher_north" => g(30, 0xAA, 0xAA, 0xAA),
|
||||
"pusher_south" => g(31, 0xAA, 0xAA, 0xAA),
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
/// space since a literal NUL is not displayable.
|
||||
const CP437: [char; 256] = [
|
||||
// 0x00–0x0F
|
||||
' ', '☺', '☻', '♥', '♦', '♣', '♠', '•', '◘', '○', '◙', '♂', '♀', '♪', '♫', '☼',
|
||||
' ', '☺', '☻', '♡', '♦', '♣', '♠', '•', '◘', '○', '◙', '♂', '♀', '♪', '♫', '☼',
|
||||
// 0x10–0x1F
|
||||
'►', '◄', '↕', '‼', '¶', '§', '▬', '↨', '↑', '↓', '→', '←', '∟', '↔', '▲', '▼',
|
||||
// 0x20–0x2F (ASCII space onward)
|
||||
|
||||
+17
-6
@@ -152,12 +152,14 @@ pub struct GameState {
|
||||
/// may block input or show a visual effect while it is `Some(t)` where `t > 0`.
|
||||
pub board_transition: Option<f64>,
|
||||
/// The player's current health. Game-global (not per-board), so it persists
|
||||
/// across board transitions. Players start with `5`. There is no runtime
|
||||
/// mutation path yet — front-ends only display it.
|
||||
/// 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 player_health: u32,
|
||||
/// 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: u32,
|
||||
/// The number of gems the player has collected. Game-global like
|
||||
/// [`player_health`](GameState::player_health); starts at `0` and is
|
||||
/// display-only for now.
|
||||
/// [`player_health`](GameState::player_health); starts at `0`.
|
||||
pub player_gems: u32,
|
||||
/// The player's key inventory. Game-global; persists across board transitions.
|
||||
pub player_keys: Keyring,
|
||||
@@ -188,6 +190,7 @@ impl GameState {
|
||||
active_scroll: None,
|
||||
board_transition: None,
|
||||
player_health: 5,
|
||||
max_health: 5,
|
||||
player_gems: 0,
|
||||
player_keys: Keyring::default(),
|
||||
};
|
||||
@@ -298,9 +301,10 @@ impl GameState {
|
||||
// below also borrows `self`.
|
||||
let mut logs: Vec<LogLine> = Vec::new();
|
||||
let mut bumps: Vec<(ObjectId, i64)> = Vec::new();
|
||||
// Net change to the player's gem count from AddGems actions; applied to
|
||||
// `self` after the board borrow drops.
|
||||
// Net change to player stats from AddGems / AlterHealth actions; applied
|
||||
// to `self` after the board borrow drops.
|
||||
let mut gem_delta: i64 = 0;
|
||||
let mut health_delta: i64 = 0;
|
||||
let mut key_changes: Vec<(String, bool)> = Vec::new();
|
||||
let mut sends: Vec<(ObjectId, String, Option<ScriptArg>)> = Vec::new();
|
||||
let mut new_bubbles: Vec<SpeechBubble> = Vec::new();
|
||||
@@ -403,6 +407,8 @@ impl GameState {
|
||||
}
|
||||
// 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.
|
||||
Action::AlterHealth(dh) => health_delta += dh,
|
||||
// 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.
|
||||
@@ -426,6 +432,11 @@ impl GameState {
|
||||
if gem_delta != 0 {
|
||||
self.player_gems = (self.player_gems as i64 + gem_delta).max(0) as u32;
|
||||
}
|
||||
// Apply the net health change (clamped to [0, max_health]).
|
||||
if health_delta != 0 {
|
||||
self.player_health = (self.player_health as i64 + health_delta)
|
||||
.clamp(0, self.max_health as i64) as u32;
|
||||
}
|
||||
for (color, present) in key_changes {
|
||||
if !self.player_keys.set_by_name(&color, present) {
|
||||
self.log.push(LogLine::error(format!("set_key: unknown color {color:?}")));
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
//! `move(dir)`, `delay(secs)`, `now()`, `set_tile(n)`, `log(msg)`, `say(msg)`,
|
||||
//! `set_fg(fg)`, `set_bg(bg)`, `set_color(fg, bg)`, `set_tag(target, tag, present)`,
|
||||
//! `send(target_id, fn_name [, arg])`, `teleport(x, y)`, `push(x, y, dir)`,
|
||||
//! `swap([[src_x, src_y, dst_x, dst_y], …])`, `add_gems(n)`, `set_key(color, present)`, `die()`
|
||||
//! `swap([[src_x, src_y, dst_x, dst_y], …])`, `add_gems(n)`, `alter_health(dh)`, `set_key(color, present)`, `die()`
|
||||
//!
|
||||
//! ## Queue API
|
||||
//!
|
||||
@@ -906,6 +906,12 @@ fn register_write_api(engine: &mut Engine, queues: &QueueMap) {
|
||||
emit(&q, source_of(&ctx), Action::AddGems(n));
|
||||
});
|
||||
|
||||
// alter_health(dh): change the player's health by dh (clamped to [0, max_health]).
|
||||
let q = queues.clone();
|
||||
engine.register_fn("alter_health", move |ctx: NativeCallContext, dh: i64| {
|
||||
emit(&q, source_of(&ctx), Action::AlterHealth(dh));
|
||||
});
|
||||
|
||||
// set_key(color, present): give (true) or take (false) the named key color.
|
||||
let q = queues.clone();
|
||||
engine.register_fn(
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Built-in script for the `heart` archetype.
|
||||
//
|
||||
// A heart is a grabbable collectible: walking onto it fires `grab()` instead
|
||||
// of blocking. It restores 1 health and removes itself from the board.
|
||||
fn grab() {
|
||||
alter_health(1);
|
||||
die();
|
||||
}
|
||||
Reference in New Issue
Block a user