This commit is contained in:
2026-06-23 20:07:53 -05:00
parent 2ea2ce0212
commit cbbe522fb1
5 changed files with 143 additions and 9 deletions
+82 -8
View File
@@ -18,25 +18,43 @@ pub const SAY_DURATION: f64 = 3.0;
/// paired with its [`Rgba8`] color for rendering.
#[derive(Copy, Clone, Default)]
pub struct Keyring {
/// Blue key.
pub blue: bool,
/// Green key.
pub green: bool,
/// Cyan key.
pub cyan: bool,
/// Red key.
pub red: bool,
/// Purple key.
pub purple: bool,
/// Orange key (custom color, not in the standard EGA palette).
pub orange: bool,
/// Yellow key.
pub yellow: bool,
/// Green key.
pub green: bool,
/// Blue key.
pub blue: bool,
/// Cyan key.
pub cyan: bool,
/// Purple key.
pub purple: bool,
/// White key.
pub white: bool,
}
impl Keyring {
/// Sets the named key slot to `value`. Returns `false` if `name` is not a
/// recognized color (`"blue"`, `"green"`, `"cyan"`, `"red"`, `"purple"`,
/// `"orange"`, `"yellow"`, `"white"`).
pub fn set_by_name(&mut self, name: &str, value: bool) -> bool {
match name {
"blue" => self.blue = value,
"green" => self.green = value,
"cyan" => self.cyan = value,
"red" => self.red = value,
"purple" => self.purple = value,
"orange" => self.orange = value,
"yellow" => self.yellow = value,
"white" => self.white = value,
_ => return false,
}
true
}
/// Returns all eight keys in display order, each paired with its color.
///
/// Order: blue, green, cyan, red, purple, orange, yellow, white.
@@ -261,6 +279,7 @@ impl GameState {
// Net change to the player's gem count from AddGems actions; applied to
// `self` after the board borrow drops.
let mut gem_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();
// Collected outside the board borrow so we can assign to self.active_scroll.
@@ -362,6 +381,8 @@ impl GameState {
}
// Accumulated and applied to `self.player_gems` after the borrow drops.
Action::AddGems(n) => gem_delta += n,
// 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 => {
board.remove_object(ba.source);
@@ -383,6 +404,11 @@ impl GameState {
if gem_delta != 0 {
self.player_gems = (self.player_gems as i64 + gem_delta).max(0) 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:?}")));
}
}
for (bumped, bumper) in bumps {
self.scripts.run_bump(bumped, bumper);
}
@@ -762,4 +788,52 @@ mod tests {
}
assert_eq!(seq, vec![47, 0xC4, 92, 0xB3, 47]);
}
#[test]
fn set_key_gives_and_takes_keys() {
let mut sobj = ObjectDef::new(0, 0);
sobj.solid = false;
sobj.script_name = Some("s".to_string());
let board = open_board(2, 1, (1, 0), vec![sobj]);
let scripts = HashMap::from([(
"s".to_string(),
"fn init() { set_key(\"blue\", true); set_key(\"red\", true); }".to_string(),
)]);
let mut game = GameState::with_scripts(board, scripts);
game.run_init();
assert!(game.player_keys.blue);
assert!(game.player_keys.red);
assert!(!game.player_keys.cyan); // cyan was not set by the script
// A second script can take a key.
let mut sobj2 = ObjectDef::new(0, 0);
sobj2.solid = false;
sobj2.script_name = Some("t".to_string());
let board2 = open_board(2, 1, (1, 0), vec![sobj2]);
let scripts2 = HashMap::from([(
"t".to_string(),
"fn init() { set_key(\"blue\", true); set_key(\"blue\", false); }".to_string(),
)]);
let mut game2 = GameState::with_scripts(board2, scripts2);
game2.run_init();
assert!(!game2.player_keys.blue);
}
#[test]
fn set_key_unknown_color_logs_error() {
let mut sobj = ObjectDef::new(0, 0);
sobj.solid = false;
sobj.script_name = Some("s".to_string());
let board = open_board(2, 1, (1, 0), vec![sobj]);
let scripts = HashMap::from([(
"s".to_string(),
r#"fn init() { set_key("chartreuse", true); }"#.to_string(),
)]);
let mut game = GameState::with_scripts(board, scripts);
game.run_init();
assert!(game.log.iter().any(|l| l.spans.iter().any(|s| s.text.contains("set_key"))));
}
}