multi board
This commit is contained in:
+58
-24
@@ -1,10 +1,10 @@
|
||||
use crate::action::Action;
|
||||
use crate::board::Board;
|
||||
use crate::log::LogLine;
|
||||
use crate::script::ScriptHost;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::rc::Rc;
|
||||
use crate::world::World;
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::time::Duration;
|
||||
use crate::board::Board;
|
||||
use crate::utils::{Direction, ObjectId, ScriptArg, Solid};
|
||||
|
||||
/// How long a `say()` speech bubble stays on screen, in seconds.
|
||||
@@ -38,18 +38,19 @@ pub struct SpeechBubble {
|
||||
/// Holds the active game world and provides game-logic operations.
|
||||
///
|
||||
/// `GameState` is the boundary between the engine (rendering, input) and the
|
||||
/// game data. It owns the board (behind a shared `Rc<RefCell<Board>>`),
|
||||
/// the message log, and the [`ScriptHost`] driving object scripts. Front-ends and
|
||||
/// internal logic reach the board through [`board`](GameState::board) /
|
||||
/// [`board_mut`](GameState::board_mut). Scripts read the board directly and
|
||||
/// request mutations as commands, applied by [`apply_commands`](GameState::apply_commands)
|
||||
/// after each script batch.
|
||||
/// game data. It owns a [`World`] (all boards + world scripts) and tracks which
|
||||
/// board is currently active. Front-ends reach the active board through
|
||||
/// [`board`](GameState::board) / [`board_mut`](GameState::board_mut). Scripts
|
||||
/// read the board and queue mutations via commands applied by `resolve` after
|
||||
/// each script batch.
|
||||
pub struct GameState {
|
||||
/// The scriptable world, shared with the script host's read getters.
|
||||
board: Rc<RefCell<Board>>,
|
||||
/// All boards and world-level scripts.
|
||||
world: World,
|
||||
/// Key of the currently active board in [`world.boards`](World::boards).
|
||||
current_board_name: String,
|
||||
/// The in-game message log, oldest first (newest pushed at the end).
|
||||
pub log: Vec<LogLine>,
|
||||
/// The Rhai scripting runtime driving this board's scripted objects.
|
||||
/// The Rhai scripting runtime for the active board's objects.
|
||||
scripts: ScriptHost,
|
||||
/// Active speech bubbles from `say()` calls, displayed by the front-end over the board.
|
||||
pub speech_bubbles: Vec<SpeechBubble>,
|
||||
@@ -59,34 +60,67 @@ pub struct GameState {
|
||||
}
|
||||
|
||||
impl GameState {
|
||||
/// Creates a `GameState` from a pre-loaded [`Board`].
|
||||
/// Creates a [`GameState`] from a loaded [`World`], starting on `world.start`.
|
||||
///
|
||||
/// Compiles the board's object scripts but does **not** run any of them;
|
||||
/// call [`GameState::run_init`] once the game is ready to start. Any script
|
||||
/// compile errors are surfaced into the log here.
|
||||
pub fn new(board: Board) -> Self {
|
||||
let board = Rc::new(RefCell::new(board));
|
||||
let scripts = ScriptHost::new(&board);
|
||||
/// The starting board's objects are compiled and registered with the
|
||||
/// [`ScriptHost`] using `world.scripts`. No lifecycle hooks are run here;
|
||||
/// call [`run_init`](GameState::run_init) once the game is ready to start.
|
||||
/// Compile errors are surfaced into the log immediately.
|
||||
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.scripts,
|
||||
);
|
||||
let mut state = Self {
|
||||
board,
|
||||
world,
|
||||
current_board_name: name,
|
||||
log: Vec::new(),
|
||||
scripts,
|
||||
scripts: host,
|
||||
speech_bubbles: Vec::new(),
|
||||
active_scroll: None,
|
||||
};
|
||||
// Surface any compile-time script errors collected during ScriptHost::new.
|
||||
state.drain_errors();
|
||||
state
|
||||
}
|
||||
|
||||
/// Creates a `GameState` from a single [`Board`] with no scripts.
|
||||
///
|
||||
/// Test-only convenience. Use [`from_world`](GameState::from_world) in production.
|
||||
#[cfg(test)]
|
||||
pub fn new(board: Board) -> Self {
|
||||
Self::with_scripts(board, std::collections::HashMap::new())
|
||||
}
|
||||
|
||||
/// Creates a `GameState` from a single [`Board`] and an explicit script pool.
|
||||
///
|
||||
/// Test-only convenience. Use [`from_world`](GameState::from_world) in production.
|
||||
#[cfg(test)]
|
||||
pub fn with_scripts(board: Board, scripts: std::collections::HashMap<String, String>) -> Self {
|
||||
// Wrap the bare board in Rc<RefCell<Board>> to match World::boards storage.
|
||||
let board_ref = std::rc::Rc::new(std::cell::RefCell::new(board));
|
||||
let world = World {
|
||||
name: String::new(),
|
||||
start: "board".to_string(),
|
||||
scripts,
|
||||
boards: std::collections::HashMap::from([("board".to_string(), board_ref)]),
|
||||
};
|
||||
Self::from_world(world)
|
||||
}
|
||||
|
||||
/// Borrows the active board for reading (e.g. by a front-end renderer).
|
||||
pub fn board(&self) -> Ref<'_, Board> {
|
||||
self.board.borrow()
|
||||
self.world.boards[&self.current_board_name].borrow()
|
||||
}
|
||||
|
||||
/// Borrows the active board for mutation.
|
||||
pub fn board_mut(&self) -> RefMut<'_, Board> {
|
||||
self.board.borrow_mut()
|
||||
self.world.boards[&self.current_board_name].borrow_mut()
|
||||
}
|
||||
|
||||
/// The name (key) of the currently active board within the world.
|
||||
pub fn current_board_name(&self) -> &str {
|
||||
&self.current_board_name
|
||||
}
|
||||
|
||||
/// Appends a styled message to the log.
|
||||
|
||||
Reference in New Issue
Block a user