Board layers
This commit is contained in:
+56
-33
@@ -1,12 +1,11 @@
|
||||
use crate::action::Action;
|
||||
use crate::archetype::Archetype;
|
||||
use crate::board::Board;
|
||||
use crate::log::LogLine;
|
||||
use crate::script::ScriptHost;
|
||||
use crate::utils::{Direction, ObjectId, Player, ScriptArg, Solid};
|
||||
use crate::world::World;
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::time::Duration;
|
||||
use crate::utils::{Direction, ObjectId, Player, ScriptArg, Solid};
|
||||
|
||||
/// How long a `say()` speech bubble stays on screen, in seconds.
|
||||
pub const SAY_DURATION: f64 = 3.0;
|
||||
@@ -85,7 +84,10 @@ impl GameState {
|
||||
pub fn from_world(world: World) -> Self {
|
||||
let name = world.start.clone();
|
||||
let host = ScriptHost::new(
|
||||
world.boards.get(&name).expect("world::load guarantees start board exists"),
|
||||
world
|
||||
.boards
|
||||
.get(&name)
|
||||
.expect("world::load guarantees start board exists"),
|
||||
&world.scripts,
|
||||
);
|
||||
let mut state = Self {
|
||||
@@ -177,7 +179,10 @@ impl GameState {
|
||||
self.scripts.run_tick(secs);
|
||||
// Expire speech bubbles before resolving new actions so a fresh say()
|
||||
// this frame isn't immediately culled.
|
||||
self.speech_bubbles.retain_mut(|b| { b.remaining -= secs; b.remaining > 0.0 });
|
||||
self.speech_bubbles.retain_mut(|b| {
|
||||
b.remaining -= secs;
|
||||
b.remaining > 0.0
|
||||
});
|
||||
self.tick_pushers(secs);
|
||||
self.resolve();
|
||||
}
|
||||
@@ -193,18 +198,7 @@ impl GameState {
|
||||
return;
|
||||
}
|
||||
self.pusher_timer += PUSHER_INTERVAL;
|
||||
let pushers: Vec<(usize, usize)> = {
|
||||
let board = self.board();
|
||||
let mut v = Vec::new();
|
||||
for y in 0..board.height {
|
||||
for x in 0..board.width {
|
||||
if matches!(board.get(x, y).1, Archetype::Pusher(_)) {
|
||||
v.push((x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
v
|
||||
};
|
||||
let pushers: Vec<(usize, usize)> = self.board().pusher_cells();
|
||||
let mut board = self.board_mut();
|
||||
for (x, y) in pushers {
|
||||
board.advance_pusher(x, y);
|
||||
@@ -251,9 +245,17 @@ impl GameState {
|
||||
}
|
||||
}
|
||||
Action::Log(line) => logs.push(line),
|
||||
Action::SetTag { target, tag, present } => {
|
||||
Action::SetTag {
|
||||
target,
|
||||
tag,
|
||||
present,
|
||||
} => {
|
||||
if let Some(obj) = board.objects.get_mut(&target) {
|
||||
if present { obj.tags.insert(tag); } else { obj.tags.remove(&tag); }
|
||||
if present {
|
||||
obj.tags.insert(tag);
|
||||
} else {
|
||||
obj.tags.remove(&tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Replace any existing bubble from this object so repeated say() calls
|
||||
@@ -267,26 +269,38 @@ impl GameState {
|
||||
Action::Delay(_) => {}
|
||||
Action::SetColor { fg, bg } => {
|
||||
if let Some(obj) = board.objects.get_mut(&ba.source) {
|
||||
if let Some(c) = fg { obj.glyph.fg = c; }
|
||||
if let Some(c) = bg { obj.glyph.bg = c; }
|
||||
if let Some(c) = fg {
|
||||
obj.glyph.fg = c;
|
||||
}
|
||||
if let Some(c) = bg {
|
||||
obj.glyph.bg = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Collected and fired after the board borrow drops, like bumps.
|
||||
Action::Send { target, fn_name, arg } => {
|
||||
Action::Send {
|
||||
target,
|
||||
fn_name,
|
||||
arg,
|
||||
} => {
|
||||
sends.push((target, fn_name, arg));
|
||||
}
|
||||
// Later scrolls overwrite earlier ones from the same tick.
|
||||
Action::Scroll(lines) => {
|
||||
new_scroll = Some(Scroll { source: ba.source, lines, choice: None });
|
||||
new_scroll = Some(Scroll {
|
||||
source: ba.source,
|
||||
lines,
|
||||
choice: None,
|
||||
});
|
||||
}
|
||||
Action::Teleport { x, y } => {
|
||||
if !board.in_bounds((x, y)) {
|
||||
logs.push(LogLine::error(format!(
|
||||
"teleport({x},{y}): out of bounds"
|
||||
)));
|
||||
logs.push(LogLine::error(format!("teleport({x},{y}): out of bounds")));
|
||||
} else {
|
||||
let (ux, uy) = (x as usize, y as usize);
|
||||
let source_solid = board.objects.get(&ba.source)
|
||||
let source_solid = board
|
||||
.objects
|
||||
.get(&ba.source)
|
||||
.map(|o| o.solid)
|
||||
.unwrap_or(false);
|
||||
if source_solid && board.solid_at(ux, uy).is_some() {
|
||||
@@ -305,7 +319,8 @@ impl GameState {
|
||||
self.log.extend(logs);
|
||||
for bubble in new_bubbles {
|
||||
// One bubble per object: replace the existing one if present.
|
||||
self.speech_bubbles.retain(|b| b.object_id != bubble.object_id);
|
||||
self.speech_bubbles
|
||||
.retain(|b| b.object_id != bubble.object_id);
|
||||
self.speech_bubbles.push(bubble);
|
||||
}
|
||||
if let Some(scroll) = new_scroll {
|
||||
@@ -344,12 +359,16 @@ impl GameState {
|
||||
/// to play a visual effect during the switch.
|
||||
pub fn enter_board(&mut self, target_map: &str, target_entry: &str) {
|
||||
if !self.world.boards.contains_key(target_map) {
|
||||
self.log.push(LogLine::error(format!("portal target board {target_map:?} not found")));
|
||||
self.log.push(LogLine::error(format!(
|
||||
"portal target board {target_map:?} not found"
|
||||
)));
|
||||
return;
|
||||
}
|
||||
// Find the named arrival portal on the target board (borrow then release).
|
||||
let arrival = self.world.boards[target_map].borrow()
|
||||
.portals.iter()
|
||||
let arrival = self.world.boards[target_map]
|
||||
.borrow()
|
||||
.portals
|
||||
.iter()
|
||||
.find(|p| p.name == target_entry)
|
||||
.map(|p| (p.x, p.y));
|
||||
let (ax, ay) = match arrival {
|
||||
@@ -366,7 +385,10 @@ impl GameState {
|
||||
self.active_scroll = None;
|
||||
// Switch to the new board and place the player at the arrival portal.
|
||||
self.current_board_name = target_map.to_string();
|
||||
self.board_mut().player = Player { x: ax as i32, y: ay as i32 };
|
||||
self.board_mut().player = Player {
|
||||
x: ax as i32,
|
||||
y: ay as i32,
|
||||
};
|
||||
// Rebuild the script host for the new board's objects.
|
||||
self.scripts = ScriptHost::new(
|
||||
&self.world.boards[&self.current_board_name],
|
||||
@@ -405,7 +427,9 @@ impl GameState {
|
||||
board.player.x = nx as i32;
|
||||
board.player.y = ny as i32;
|
||||
// Check for a portal at the new position; clone strings to release the borrow.
|
||||
portal_target = board.portals.iter()
|
||||
portal_target = board
|
||||
.portals
|
||||
.iter()
|
||||
.find(|p| p.x == nx && p.y == ny)
|
||||
.map(|p| (p.target_map.clone(), p.target_entry.clone()));
|
||||
} else {
|
||||
@@ -453,4 +477,3 @@ fn step_object(board: &mut Board, id: ObjectId, dir: Direction) -> Option<Object
|
||||
}
|
||||
bumped
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user