no need for scriptstate any more
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
pub mod state;
|
||||
pub mod player;
|
||||
pub mod board;
|
||||
pub mod object_info;
|
||||
|
||||
+11
-10
@@ -1,21 +1,22 @@
|
||||
use rhai::Engine;
|
||||
use crate::player::Player;
|
||||
use crate::api::board::BoardRef;
|
||||
use crate::player::PlayerRef;
|
||||
use crate::script::Registerable;
|
||||
use crate::utils::{ErrorSink, PlayerPos};
|
||||
use crate::utils::ErrorSink;
|
||||
|
||||
/// GameState stores player state but Board stores its position, and we want one
|
||||
/// object to register with Rhai
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PlayerWithPos(pub Player, pub PlayerPos);
|
||||
#[derive(Clone)]
|
||||
pub struct PlayerWithPos(pub PlayerRef, pub BoardRef);
|
||||
|
||||
impl Registerable for PlayerWithPos {
|
||||
fn register(engine: &mut Engine, _error_sink: ErrorSink) {
|
||||
engine.register_type_with_name::<PlayerWithPos>("Player")
|
||||
.register_get("gems", |player: &mut PlayerWithPos| player.0.gems)
|
||||
.register_get("health", |player: &mut PlayerWithPos| player.0.health)
|
||||
.register_get("max_health", |player: &mut PlayerWithPos| player.0.max_health)
|
||||
.register_get("keys", |player: &mut PlayerWithPos| player.0.keys)
|
||||
.register_get("x", |player: &mut PlayerWithPos| player.1.x)
|
||||
.register_get("y", |player: &mut PlayerWithPos| player.1.y);
|
||||
.register_get("gems", |player: &mut PlayerWithPos| player.0.borrow().gems)
|
||||
.register_get("health", |player: &mut PlayerWithPos| player.0.borrow().health)
|
||||
.register_get("max_health", |player: &mut PlayerWithPos| player.0.borrow().max_health)
|
||||
.register_get("keys", |player: &mut PlayerWithPos| player.0.borrow().keys)
|
||||
.register_get("x", |player: &mut PlayerWithPos| player.1.borrow().player.x)
|
||||
.register_get("y", |player: &mut PlayerWithPos| player.1.borrow().player.y);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::utils::{ErrorSink, RegistryValue};
|
||||
pub struct Registry(pub BoardRef);
|
||||
|
||||
impl Registerable for Registry {
|
||||
fn register(engine: &mut Engine, error_sink: ErrorSink) {
|
||||
fn register(engine: &mut Engine, _error_sink: ErrorSink) {
|
||||
engine.register_type_with_name::<Registry>("Registry");
|
||||
|
||||
// Registry.get(key) -> Dynamic — returns () if the key is absent.
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
use rhai::Engine;
|
||||
use crate::api::board::BoardRef;
|
||||
use crate::api::player::PlayerWithPos;
|
||||
use crate::game::GameState;
|
||||
use crate::script::Registerable;
|
||||
use crate::utils::ErrorSink;
|
||||
|
||||
/// The host-provided context handed to every script hook for the duration of one
|
||||
/// call. Currently just the player snapshot, but it exists so more host state can
|
||||
/// be threaded through the `run_*` methods without changing each signature again.
|
||||
#[derive(Clone)]
|
||||
pub struct ScriptState {
|
||||
pub player: PlayerWithPos,
|
||||
pub board: BoardRef,
|
||||
}
|
||||
|
||||
impl ScriptState {
|
||||
pub fn from_game_state(game_state: &GameState) -> Self {
|
||||
Self {
|
||||
player: PlayerWithPos(game_state.player, game_state.board().player),
|
||||
board: game_state.board_rc(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Registerable for ScriptState {
|
||||
fn register(engine: &mut Engine, _error_sink: ErrorSink) {
|
||||
engine.register_type_with_name::<ScriptState>("State")
|
||||
.register_get("player", |state: &mut ScriptState| state.player)
|
||||
.register_get("board", |state: &mut ScriptState| state.board.clone());
|
||||
}
|
||||
}
|
||||
+33
-32
@@ -13,8 +13,7 @@ pub const SAY_DURATION: f64 = 3.0;
|
||||
// Re-export ScrollLine so kiln-tui can pattern-match scroll content without
|
||||
// accessing the private `action` module directly.
|
||||
pub use crate::action::ScrollLine;
|
||||
use crate::api::state::ScriptState;
|
||||
use crate::player::Player;
|
||||
use crate::player::{Player, PlayerRef};
|
||||
|
||||
/// An active scroll overlay opened by a scripted object via `scroll()`.
|
||||
///
|
||||
@@ -72,7 +71,7 @@ pub struct GameState {
|
||||
/// 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,
|
||||
pub player: PlayerRef,
|
||||
}
|
||||
|
||||
impl GameState {
|
||||
@@ -84,11 +83,13 @@ impl GameState {
|
||||
/// Compile errors are surfaced into the log immediately.
|
||||
pub fn from_world(world: World) -> Self {
|
||||
let name = world.start.clone();
|
||||
let player = Player::new_ref();
|
||||
let host = ScriptHost::new(
|
||||
world
|
||||
.boards
|
||||
.get(&name)
|
||||
.expect("world::load guarantees start board exists"),
|
||||
.expect("world::load guarantees start board exists").clone(),
|
||||
player.clone(),
|
||||
&world.scripts,
|
||||
);
|
||||
let mut state = Self {
|
||||
@@ -99,7 +100,7 @@ impl GameState {
|
||||
speech_bubbles: Vec::new(),
|
||||
active_scroll: None,
|
||||
board_transition: None,
|
||||
player: Player::default()
|
||||
player
|
||||
};
|
||||
state.drain_errors();
|
||||
state
|
||||
@@ -163,7 +164,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(ScriptState::from_game_state(self));
|
||||
self.scripts.run_init();
|
||||
self.resolve();
|
||||
}
|
||||
|
||||
@@ -177,7 +178,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(ScriptState::from_game_state(self), secs);
|
||||
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| {
|
||||
@@ -338,23 +339,22 @@ impl GameState {
|
||||
}
|
||||
// Apply the net gem change (clamped at 0, since the count is unsigned).
|
||||
if gem_delta != 0 {
|
||||
self.player.alter_gems(gem_delta);
|
||||
self.player.borrow_mut().alter_gems(gem_delta);
|
||||
}
|
||||
// Apply the net health change (clamped to [0, max_health]).
|
||||
if health_delta != 0 {
|
||||
self.player.alter_health(health_delta);
|
||||
self.player.borrow_mut().alter_health(health_delta);
|
||||
}
|
||||
for (color, present) in key_changes {
|
||||
if !self.player.keys.set_by_name(&color, present) {
|
||||
if !self.player.borrow_mut().keys.set_by_name(&color, present) {
|
||||
self.log.push(LogLine::error(format!("set_key: unknown color {color:?}")));
|
||||
}
|
||||
}
|
||||
let state = ScriptState::from_game_state(self);
|
||||
for (bumped, bumper) in bumps {
|
||||
self.scripts.run_bump(state.clone(), bumped, bumper);
|
||||
self.scripts.run_bump(bumped, bumper);
|
||||
}
|
||||
for (target, fn_name, arg) in sends {
|
||||
self.scripts.run_send(state.clone(), target, &fn_name, arg);
|
||||
self.scripts.run_send(target, &fn_name, arg);
|
||||
}
|
||||
self.drain_errors();
|
||||
}
|
||||
@@ -370,7 +370,7 @@ impl GameState {
|
||||
if let Some(scroll) = self.active_scroll.take()
|
||||
&& let Some(choice) = scroll.choice
|
||||
{
|
||||
self.scripts.run_send(ScriptState::from_game_state(self), scroll.source, &choice, SendArg::None);
|
||||
self.scripts.run_send(scroll.source, &choice, SendArg::None);
|
||||
self.drain_errors();
|
||||
}
|
||||
}
|
||||
@@ -416,7 +416,8 @@ impl GameState {
|
||||
self.board_mut().clear_all_queues();
|
||||
// Rebuild the script host for the new board's objects.
|
||||
self.scripts = ScriptHost::new(
|
||||
&self.world.boards[&self.current_board_name],
|
||||
self.world.boards[&self.current_board_name].clone(),
|
||||
self.player.clone(),
|
||||
&self.world.scripts,
|
||||
);
|
||||
// Stub hook for the front-end transition animation (1 second).
|
||||
@@ -477,13 +478,12 @@ 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.
|
||||
let state = ScriptState::from_game_state(self);
|
||||
if let Some(id) = grabbed {
|
||||
self.scripts.run_grab(state.clone(), id);
|
||||
self.scripts.run_grab(id);
|
||||
self.resolve();
|
||||
}
|
||||
if let Some(idx) = bumped {
|
||||
self.scripts.run_bump(state, idx, -1);
|
||||
self.scripts.run_bump(idx, -1);
|
||||
self.drain_errors();
|
||||
}
|
||||
}
|
||||
@@ -539,7 +539,7 @@ mod tests {
|
||||
game.try_move(Direction::East);
|
||||
|
||||
// The gem was grabbed: gem count up, gem object gone, player on its cell.
|
||||
assert_eq!(game.player.gems, 1);
|
||||
assert_eq!(game.player.borrow().gems, 1);
|
||||
assert!(game.board().objects.is_empty());
|
||||
assert_eq!((game.board().player.x, game.board().player.y), (1, 0));
|
||||
}
|
||||
@@ -567,7 +567,7 @@ mod tests {
|
||||
game.tick(Duration::from_millis(16));
|
||||
|
||||
// No grab: the gem is untouched and the player never moved.
|
||||
assert_eq!(game.player.gems, 0);
|
||||
assert_eq!(game.player.borrow().gems, 0);
|
||||
assert!(game.board().objects.values().any(|o| o.grab));
|
||||
assert_eq!((game.board().player.x, game.board().player.y), (2, 0));
|
||||
}
|
||||
@@ -591,7 +591,7 @@ mod tests {
|
||||
// crate moves to (3,0), empty moves back to (2,0).
|
||||
let mut game = game_with_object_script(
|
||||
5,
|
||||
"fn tick(m,s,dt) { if m.queue.length == 0 { shift([[2, 0], [3, 0]]); } }",
|
||||
"fn tick(m,dt) { if m.queue.length == 0 { shift([[2, 0], [3, 0]]); } }",
|
||||
);
|
||||
game.tick(Duration::from_millis(16));
|
||||
let b = game.board();
|
||||
@@ -609,10 +609,10 @@ mod tests {
|
||||
obj.script_name = Some("s".to_string());
|
||||
let mut board = open_board(4, 1, (3, 0), vec![obj]);
|
||||
crate_at(&mut board, 2, 0);
|
||||
let src = "fn init(m,s) { \
|
||||
log(if s.board.passable(1, 0) { \"empty:yes\" } else { \"empty:no\" }); \
|
||||
log(if s.board.passable(2, 0) { \"crate:yes\" } else { \"crate:no\" }); \
|
||||
log(if s.board.passable(9, 0) { \"off:yes\" } else { \"off:no\" }); }";
|
||||
let src = "fn init(m) { \
|
||||
log(if Board.passable(1, 0) { \"empty:yes\" } else { \"empty:no\" }); \
|
||||
log(if Board.passable(2, 0) { \"crate:yes\" } else { \"crate:no\" }); \
|
||||
log(if Board.passable(9, 0) { \"off:yes\" } else { \"off:no\" }); }";
|
||||
let scripts = HashMap::from([("s".to_string(), src.to_string())]);
|
||||
let mut game = GameState::with_scripts(board, scripts);
|
||||
game.run_init();
|
||||
@@ -727,14 +727,15 @@ mod tests {
|
||||
let board = open_board(2, 1, (1, 0), vec![sobj]);
|
||||
let scripts = HashMap::from([(
|
||||
"s".to_string(),
|
||||
"fn init(m, s) { set_key(\"blue\", true); set_key(\"red\", true); }".to_string(),
|
||||
"fn init(m) { 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
|
||||
let keys = game.player.borrow().keys;
|
||||
assert!(keys.blue);
|
||||
assert!(keys.red);
|
||||
assert!(!keys.cyan); // cyan was not set by the script
|
||||
|
||||
// A second script can take a key.
|
||||
let mut sobj2 = ObjectDef::new(0, 0);
|
||||
@@ -743,12 +744,12 @@ mod tests {
|
||||
let board2 = open_board(2, 1, (1, 0), vec![sobj2]);
|
||||
let scripts2 = HashMap::from([(
|
||||
"t".to_string(),
|
||||
"fn init(m, s) { set_key(\"blue\", true); set_key(\"blue\", false); }".to_string(),
|
||||
"fn init(m) { 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);
|
||||
assert!(!game2.player.borrow().keys.blue);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -759,7 +760,7 @@ mod tests {
|
||||
let board = open_board(2, 1, (1, 0), vec![sobj]);
|
||||
let scripts = HashMap::from([(
|
||||
"s".to_string(),
|
||||
r#"fn init(m,s) { set_key("chartreuse", true); }"#.to_string(),
|
||||
r#"fn init(m) { set_key("chartreuse", true); }"#.to_string(),
|
||||
)]);
|
||||
let mut game = GameState::with_scripts(board, scripts);
|
||||
game.run_init();
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use crate::keys::Keyring;
|
||||
|
||||
/// The game-global player state: stats that follow the player across boards.
|
||||
@@ -26,7 +28,14 @@ pub struct Player {
|
||||
pub gems: i64,
|
||||
}
|
||||
|
||||
pub type PlayerRef = Rc<RefCell<Player>>;
|
||||
|
||||
impl Player {
|
||||
/// Create a new PlayerRef from a default player
|
||||
pub fn new_ref() -> PlayerRef {
|
||||
Rc::new(RefCell::from(Player::default()))
|
||||
}
|
||||
|
||||
/// Attempt to modify the gem total by the given amount, but maintain a minimum of zero.
|
||||
/// If the modification would take up below zero, leave it alone and return false.
|
||||
pub fn alter_gems(&mut self, delta: i64) -> bool {
|
||||
|
||||
+35
-41
@@ -35,7 +35,7 @@ use crate::game::SAY_DURATION;
|
||||
use crate::log::LogLine;
|
||||
use crate::map_file::parse_color;
|
||||
use crate::object_def::ObjectDef;
|
||||
use crate::utils::{Direction, ErrorSink, Hook, ObjectId, RegistryValue};
|
||||
use crate::utils::{Direction, ErrorSink, Hook, ObjectId};
|
||||
use rhai::{
|
||||
Array, CallFnOptions, Dynamic, Engine, ImmutableString, Module, NativeCallContext,
|
||||
Scope, AST,
|
||||
@@ -48,9 +48,9 @@ use crate::api::object_info::ObjectInfo;
|
||||
use crate::api::player::PlayerWithPos;
|
||||
use crate::api::queue::ObjQueue;
|
||||
use crate::api::registry::Registry;
|
||||
use crate::api::state::ScriptState;
|
||||
use crate::glyph::Glyph;
|
||||
use crate::keys::Keyring;
|
||||
use crate::player::PlayerRef;
|
||||
|
||||
/// Types which can be registered to be sent to Rhai
|
||||
pub trait Registerable {
|
||||
@@ -100,6 +100,7 @@ pub struct ScriptHost {
|
||||
scopes: HashMap<ObjectId, Scope<'static>>,
|
||||
board_queue: BoardQueue,
|
||||
errors: ErrorSink,
|
||||
board: BoardRef
|
||||
}
|
||||
|
||||
impl ScriptHost {
|
||||
@@ -110,7 +111,7 @@ impl ScriptHost {
|
||||
///
|
||||
/// `scripts` is the world-level script pool (script name → Rhai source); it is
|
||||
/// read only during construction and not retained afterward.
|
||||
pub fn new(board_cell: &BoardRef, script_sources: &HashMap<String, String>) -> Self {
|
||||
pub fn new(board_ref: BoardRef, player: PlayerRef, script_sources: &HashMap<String, String>) -> Self {
|
||||
let board_queue: BoardQueue = Rc::new(RefCell::new(Vec::new()));
|
||||
let errors = ErrorSink::new();
|
||||
let mut scopes = HashMap::new();
|
||||
@@ -120,16 +121,15 @@ impl ScriptHost {
|
||||
PlayerWithPos::register(&mut engine, errors.clone());
|
||||
Keyring::register(&mut engine, errors.clone());
|
||||
BoardRef::register(&mut engine, errors.clone());
|
||||
ScriptState::register(&mut engine, errors.clone());
|
||||
ObjectInfo::register(&mut engine, errors.clone());
|
||||
Glyph::register(&mut engine, errors.clone());
|
||||
ObjQueue::register(&mut engine, errors.clone());
|
||||
Registry::register(&mut engine, errors.clone());
|
||||
|
||||
register_write_api(&mut engine, board_cell.clone());
|
||||
register_global_constants(&mut engine, board_cell.clone());
|
||||
register_write_api(&mut engine, board_ref.clone());
|
||||
register_global_constants(&mut engine, board_ref.clone(), player.clone());
|
||||
|
||||
let board = board_cell.borrow();
|
||||
let board = board_ref.borrow();
|
||||
|
||||
// Compile each referenced script once, keyed by `script_key` (the object's
|
||||
// `script_name`: a world-pool name for named scripts, or a synthetic
|
||||
@@ -166,10 +166,10 @@ impl ScriptHost {
|
||||
scripts.insert(
|
||||
key,
|
||||
CompiledScript {
|
||||
has_init: defines("init", 2),
|
||||
has_tick: defines("tick", 3),
|
||||
has_bump: defines("bump", 3),
|
||||
has_grab: defines("grab", 2),
|
||||
has_init: defines("init", 1),
|
||||
has_tick: defines("tick", 2),
|
||||
has_bump: defines("bump", 2),
|
||||
has_grab: defines("grab", 1),
|
||||
ast,
|
||||
},
|
||||
);
|
||||
@@ -199,43 +199,41 @@ impl ScriptHost {
|
||||
board_queue,
|
||||
errors,
|
||||
scopes,
|
||||
board: board_ref
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls `tick(dt)` on every scripted object that defines it, then drains queues.
|
||||
pub fn run_tick(&mut self, state: ScriptState, dt: f64) {
|
||||
self.run_hook_on_all(Hook::Tick, state, dt);
|
||||
pub fn run_tick(&mut self, dt: f64) {
|
||||
self.run_hook_on_all(Hook::Tick, dt);
|
||||
}
|
||||
|
||||
pub fn run_init(&mut self, state: ScriptState) {
|
||||
self.run_hook_on_all(Hook::Init, state, 0.0)
|
||||
pub fn run_init(&mut self) {
|
||||
self.run_hook_on_all(Hook::Init, 0.0)
|
||||
}
|
||||
|
||||
/// Run the given hook on every object that defines it. For hooks other than
|
||||
/// `Tick`, dt should just be 0.0
|
||||
fn run_hook_on_all(&mut self, hook: Hook, state: ScriptState, dt: f64) {
|
||||
let all_ids = state.board.borrow().all_ids();
|
||||
fn run_hook_on_all(&mut self, hook: Hook, dt: f64) {
|
||||
let all_ids = self.board.borrow().all_ids();
|
||||
let arg = match hook {
|
||||
Hook::Tick => Some(Dynamic::from(dt)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
for id in all_ids {
|
||||
self.run_hook_on_one(hook, state.clone(), id, arg.clone(), dt)
|
||||
self.run_hook_on_one(hook, id, arg.clone(), dt)
|
||||
}
|
||||
}
|
||||
|
||||
fn run_hook_on_one(&mut self, hook: Hook, state: ScriptState, id: ObjectId, arg: Option<Dynamic>, dt: f64) {
|
||||
if let Some(mut info) = ObjectInfo::from_id(id, state.board.clone()) {
|
||||
fn run_hook_on_one(&mut self, hook: Hook, id: ObjectId, arg: Option<Dynamic>, dt: f64) {
|
||||
if let Some(mut info) = ObjectInfo::from_id(id, self.board.clone()) {
|
||||
if let Some(script_key) = info.script_name.as_ref()
|
||||
&& let Some(script) = self.scripts.get(script_key)
|
||||
&& let Some(scope) = self.scopes.get_mut(&id) {
|
||||
|
||||
if script.has(hook) {
|
||||
let mut args = vec![
|
||||
Dynamic::from(info.clone()),
|
||||
Dynamic::from(state.clone())
|
||||
];
|
||||
let mut args = vec![Dynamic::from(info.clone())];
|
||||
if let Some(d) = arg { args.push(d) }
|
||||
|
||||
// Call this with opts tagging this call as our id. The write API will read
|
||||
@@ -261,8 +259,8 @@ impl ScriptHost {
|
||||
|
||||
/// Calls `bump(id)` on the object with [`ObjectId`] `object_id`, if it defines
|
||||
/// the hook. After the hook, drains the object's queue with `dt = 0`.
|
||||
pub fn run_bump(&mut self, state: ScriptState, id: ObjectId, bumper: i64) {
|
||||
self.run_hook_on_one(Hook::Bump, state, id, Some(Dynamic::from(bumper)), 0.0);
|
||||
pub fn run_bump(&mut self, id: ObjectId, bumper: i64) {
|
||||
self.run_hook_on_one(Hook::Bump, id, Some(Dynamic::from(bumper)), 0.0);
|
||||
}
|
||||
|
||||
/// Calls `grab()` on the object with [`ObjectId`] `object_id`, if it defines the
|
||||
@@ -271,21 +269,20 @@ impl ScriptHost {
|
||||
/// Fired when the player walks onto a grab object or a grab object is pushed
|
||||
/// into the player (see [`GameState`](crate::game::GameState)). The hook
|
||||
/// typically increments a player stat and removes the object via `die()`.
|
||||
pub fn run_grab(&mut self, state: ScriptState, object_id: ObjectId) {
|
||||
self.run_hook_on_one(Hook::Grab, state, object_id, None, 0.0);
|
||||
pub fn run_grab(&mut self, object_id: ObjectId) {
|
||||
self.run_hook_on_one(Hook::Grab, object_id, None, 0.0);
|
||||
}
|
||||
|
||||
/// Calls the named function on the object with [`ObjectId`] `target_id`.
|
||||
///
|
||||
/// What we pass depends on arity, in this order:
|
||||
/// - If it has arity 3, we pass `[ObjectInfo, ScriptState, SendArg]`
|
||||
/// - If it has arity 2, we pass `[ObjectInfo, ScriptState]`
|
||||
/// - If it has arity 1, we pass the arg
|
||||
/// - If it has arity 2, we pass `[ObjectInfo, arg]`
|
||||
/// - If it has arity 1, we pass the ObjectInfo alone
|
||||
/// - If it has arity 0, we pass nothing
|
||||
///
|
||||
/// In cases where the arg isn't provided but we have the arity, we pass `Dynamic::UNIT`
|
||||
pub(crate) fn run_send(&mut self, state: ScriptState, id: ObjectId, fn_name: &str, arg: SendArg) {
|
||||
if let Some(mut info) = ObjectInfo::from_id(id, state.board.clone()) {
|
||||
pub(crate) fn run_send(&mut self, id: ObjectId, fn_name: &str, arg: SendArg) {
|
||||
if let Some(mut info) = ObjectInfo::from_id(id, self.board.clone()) {
|
||||
if let Some(script_key) = info.script_name.as_ref()
|
||||
&& let Some(script) = self.scripts.get(script_key)
|
||||
&& let Some(scope) = self.scopes.get_mut(&id) {
|
||||
@@ -307,15 +304,11 @@ impl ScriptHost {
|
||||
|
||||
// Assemble the args
|
||||
let mut args = vec![];
|
||||
if arities.contains(&3) {
|
||||
if arities.contains(&2) {
|
||||
args.push(Dynamic::from(info.clone()));
|
||||
args.push(Dynamic::from(state.clone()));
|
||||
args.push(arg.into());
|
||||
} else if arities.contains(&2) {
|
||||
args.push(Dynamic::from(info.clone()));
|
||||
args.push(Dynamic::from(state.clone()));
|
||||
} else if arities.contains(&1) {
|
||||
args.push(arg.into());
|
||||
args.push(Dynamic::from(info.clone()));
|
||||
}
|
||||
|
||||
// Call this with opts tagging this call as our id. The write API will read
|
||||
@@ -614,9 +607,10 @@ fn read_coord_array(arr: &Array) -> Result<Vec<(i64, i64)>, ()> {
|
||||
/// Registers direction and color constants as a global Rhai module so they are
|
||||
/// visible to every function at any call depth, including Rhai-to-Rhai calls.
|
||||
/// (Scope-level constants are only visible to the top-level function Rust calls.)
|
||||
fn register_global_constants(engine: &mut Engine, board: BoardRef) {
|
||||
fn register_global_constants(engine: &mut Engine, board: BoardRef, player: PlayerRef) {
|
||||
let mut m = Module::new();
|
||||
m.set_var("Board", board);
|
||||
m.set_var("Board", board.clone());
|
||||
m.set_var("Player", PlayerWithPos(player.clone(), board));
|
||||
m.set_var("North", Direction::North);
|
||||
m.set_var("South", Direction::South);
|
||||
m.set_var("East", Direction::East);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// A gem is a grabbable collectible: walking onto it (or pushing it into the
|
||||
// player) fires this `grab()` hook instead of blocking. We bump the player's gem
|
||||
// count and remove ourselves from the board.
|
||||
fn grab(me, state) {
|
||||
fn grab(me) {
|
||||
add_gems(1);
|
||||
die();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//
|
||||
// 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(me, state) {
|
||||
fn grab(me) {
|
||||
alter_health(1);
|
||||
die();
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// A gem is a grabbable collectible: walking onto it (or pushing it into the
|
||||
// player) fires this `grab()` hook instead of blocking. We bump the player's gem
|
||||
// count and remove ourselves from the board.
|
||||
fn grab(me, state) {
|
||||
fn grab(me) {
|
||||
let colors = [
|
||||
"red",
|
||||
"orange",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// pusher shares one compiled copy and learns its direction from the
|
||||
// `BUILTIN_pusher_<dir>` tag the map loader attached to it.
|
||||
|
||||
fn tick(me, state, dt) {
|
||||
fn tick(me, dt) {
|
||||
// Only queue a step when idle. `move` shoves the chain ahead via step_object
|
||||
// and is a no-op when blocked; the delay pads each step out to ~0.5s (move
|
||||
// itself costs 0.25s), matching the old global pusher heartbeat.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// engine and the script just hands it the ring. The spin direction comes from the
|
||||
// `BUILTIN_spinner_*` tag the map loader attaches (it defaults to clockwise when no
|
||||
// tag is present).
|
||||
fn tick(me, state, dt) {
|
||||
fn tick(me, dt) {
|
||||
// Only start a new rotation when the previous one (and its delay) has drained,
|
||||
// exactly like the built-in pusher's pacing.
|
||||
if me.waiting { return; }
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::time::Duration;
|
||||
fn move_command_relocates_the_source_object() {
|
||||
let board = open_board(5, 3, (0, 0), vec![scripted_object(2, 1, "m")]);
|
||||
let mut game =
|
||||
GameState::with_scripts(board, scripts_from(&[("m", "fn init(m,s) { move(East); }")]));
|
||||
GameState::with_scripts(board, scripts_from(&[("m", "fn init(m) { move(East); }")]));
|
||||
game.run_init();
|
||||
let b = game.board();
|
||||
// East increments x by one; the object started at (2, 1).
|
||||
@@ -20,7 +20,7 @@ fn move_into_a_wall_or_edge_is_a_noop() {
|
||||
// Object at the west edge moving west: out of bounds, ignored.
|
||||
let board = open_board(5, 3, (0, 0), vec![scripted_object(0, 1, "m")]);
|
||||
let mut game =
|
||||
GameState::with_scripts(board, scripts_from(&[("m", "fn init(m,s) { move(West); }")]));
|
||||
GameState::with_scripts(board, scripts_from(&[("m", "fn init(m) { move(West); }")]));
|
||||
game.run_init();
|
||||
assert_eq!(game.board().objects[&1].x, 0);
|
||||
}
|
||||
@@ -29,7 +29,7 @@ fn move_into_a_wall_or_edge_is_a_noop() {
|
||||
fn set_tile_command_changes_the_source_glyph() {
|
||||
let board = open_board(5, 3, (0, 0), vec![scripted_object(2, 1, "s")]);
|
||||
let mut game =
|
||||
GameState::with_scripts(board, scripts_from(&[("s", "fn init(m,s) { set_tile(7); }")]));
|
||||
GameState::with_scripts(board, scripts_from(&[("s", "fn init(m) { set_tile(7); }")]));
|
||||
game.run_init();
|
||||
assert_eq!(game.board().objects[&1].glyph.tile, 7);
|
||||
}
|
||||
@@ -40,7 +40,7 @@ fn object_pushes_crate_on_init() {
|
||||
let mut board = open_board(4, 1, (0, 0), vec![scripted_object(1, 0, "m")]);
|
||||
crate_at(&mut board, 2, 0);
|
||||
let mut game =
|
||||
GameState::with_scripts(board, scripts_from(&[("m", "fn init(m,s) { move(East); }")]));
|
||||
GameState::with_scripts(board, scripts_from(&[("m", "fn init(m) { move(East); }")]));
|
||||
game.run_init();
|
||||
let b = game.board();
|
||||
assert_eq!((b.objects[&1].x, b.objects[&1].y), (2, 0));
|
||||
@@ -53,7 +53,7 @@ fn object_push_into_player() {
|
||||
// A scripted object moving into the player pushes the player when there's room.
|
||||
let board = open_board(5, 1, (2, 0), vec![scripted_object(1, 0, "m")]);
|
||||
let mut game =
|
||||
GameState::with_scripts(board, scripts_from(&[("m", "fn init(m,s) { move(East); }")]));
|
||||
GameState::with_scripts(board, scripts_from(&[("m", "fn init(m) { move(East); }")]));
|
||||
game.run_init();
|
||||
{
|
||||
let b = game.board();
|
||||
@@ -65,7 +65,7 @@ fn object_push_into_player() {
|
||||
let mut board = open_board(4, 1, (2, 0), vec![scripted_object(1, 0, "m")]);
|
||||
wall_at(&mut board, 3, 0);
|
||||
let mut game =
|
||||
GameState::with_scripts(board, scripts_from(&[("m", "fn init(m,s) { move(East); }")]));
|
||||
GameState::with_scripts(board, scripts_from(&[("m", "fn init(m) { move(East); }")]));
|
||||
game.run_init();
|
||||
let b = game.board();
|
||||
assert_eq!((b.objects[&1].x, b.objects[&1].y), (1, 0));
|
||||
@@ -79,7 +79,7 @@ fn move_cost_rate_limits_repeated_moves() {
|
||||
let board = open_board(5, 1, (0, 0), vec![scripted_object(1, 0, "m")]);
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
scripts_from(&[("m", "fn init(m,s) { move(East); move(East); }")]),
|
||||
scripts_from(&[("m", "fn init(m) { move(East); move(East); }")]),
|
||||
);
|
||||
game.run_init();
|
||||
assert_eq!(game.board().objects[&1].x, 2); // first move applied (1 -> 2)
|
||||
@@ -102,7 +102,7 @@ fn inline_delay_paces_subsequent_moves() {
|
||||
wall_at(&mut board, 2, 1);
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
scripts_from(&[("m", "fn init(m,s) { move(East); move(South); }")]),
|
||||
scripts_from(&[("m", "fn init(m) { move(East); move(South); }")]),
|
||||
);
|
||||
game.run_init();
|
||||
// First (eastward) move is blocked by the wall: object hasn't moved.
|
||||
@@ -136,7 +136,7 @@ fn queue_length_reports_pending_actions() {
|
||||
board,
|
||||
scripts_from(&[(
|
||||
"q",
|
||||
"fn init(m,s) { set_tile(5); set_tile(6); log(`len=${m.queue.length}`); }",
|
||||
"fn init(m) { set_tile(5); set_tile(6); log(`len=${m.queue.length}`); }",
|
||||
)]),
|
||||
);
|
||||
game.run_init();
|
||||
@@ -166,7 +166,7 @@ fn blocked_reports_solid_and_clear() {
|
||||
board,
|
||||
scripts_from(&[(
|
||||
"b",
|
||||
"fn init(m, s) { if m.blocked(East) { set_tile(9); } else { set_tile(7); } }",
|
||||
"fn init(m) { if m.blocked(East) { set_tile(9); } else { set_tile(7); } }",
|
||||
)]),
|
||||
);
|
||||
game.run_init();
|
||||
@@ -178,7 +178,7 @@ fn blocked_reports_solid_and_clear() {
|
||||
board,
|
||||
scripts_from(&[(
|
||||
"b",
|
||||
"fn init(m, s) { if m.blocked(East) { set_tile(9); } else { set_tile(7); } }",
|
||||
"fn init(m) { if m.blocked(East) { set_tile(9); } else { set_tile(7); } }",
|
||||
)]),
|
||||
);
|
||||
game.run_init();
|
||||
|
||||
@@ -18,11 +18,11 @@ fn collision_priority_resolves_in_array_order_and_bumps() {
|
||||
scripts_from(&[
|
||||
(
|
||||
"e",
|
||||
"fn init(m,s) { move(East); } fn bump(m,s,id) { log(`o0 by ${id}`); }",
|
||||
"fn init(m) { move(East); } fn bump(m,id) { log(`o0 by ${id}`); }",
|
||||
),
|
||||
(
|
||||
"w",
|
||||
"fn init(m,s) { move(West); } fn bump(m,s,id) { log(`o1 by ${id}`); }",
|
||||
"fn init(m) { move(West); } fn bump(m,id) { log(`o1 by ${id}`); }",
|
||||
),
|
||||
]),
|
||||
);
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::time::Duration;
|
||||
fn init_runs_only_on_run_init_not_at_construction() {
|
||||
let (board, scripts) = board_with_object(
|
||||
Some("greet"),
|
||||
&[("greet", r#"fn init(m,s) { log("hello"); }"#)],
|
||||
&[("greet", r#"fn init(m) { log("hello"); }"#)],
|
||||
);
|
||||
let mut game = GameState::with_scripts(board, scripts);
|
||||
// init must not fire during construction / deserialization.
|
||||
@@ -22,7 +22,7 @@ fn init_runs_only_on_run_init_not_at_construction() {
|
||||
fn tick_calls_script_tick_with_elapsed_seconds() {
|
||||
let (board, scripts) = board_with_object(
|
||||
Some("t"),
|
||||
&[("t", r#"fn tick(m,s,dt) { log(dt.to_string()); }"#)],
|
||||
&[("t", r#"fn tick(m,dt) { log(dt.to_string()); }"#)],
|
||||
);
|
||||
let mut game = GameState::with_scripts(board, scripts);
|
||||
game.run_init();
|
||||
@@ -69,7 +69,7 @@ fn script_reads_board_through_view() {
|
||||
let board = open_board(5, 3, (3, 1), vec![scripted_object(2, 1, "r")]);
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
scripts_from(&[("r", "fn init(m,s) { log(s.player.x.to_string()); }")]),
|
||||
scripts_from(&[("r", "fn init(m) { log(Player.x.to_string()); }")]),
|
||||
);
|
||||
game.run_init();
|
||||
assert_eq!(log_texts(&game), vec!["3"]);
|
||||
@@ -88,8 +88,8 @@ fn commands_are_routed_to_their_own_source_object() {
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
scripts_from(&[
|
||||
("e", "fn init(m,s) { move(East); }"),
|
||||
("w", "fn init(m,s) { move(West); }"),
|
||||
("e", "fn init(m) { move(East); }"),
|
||||
("w", "fn init(m) { move(West); }"),
|
||||
]),
|
||||
);
|
||||
game.run_init();
|
||||
@@ -127,7 +127,7 @@ fn set_tag_adds_and_removes_via_my_id() {
|
||||
// present on the object afterward.
|
||||
let (board, scripts) = board_with_object(
|
||||
Some("t"),
|
||||
&[("t", r#"fn init(m,s) { set_tag(m.id, "active", true); }"#)],
|
||||
&[("t", r#"fn init(m) { set_tag(m.id, "active", true); }"#)],
|
||||
);
|
||||
let mut game = GameState::with_scripts(board, scripts);
|
||||
game.run_init();
|
||||
@@ -136,7 +136,7 @@ fn set_tag_adds_and_removes_via_my_id() {
|
||||
// A script removes a pre-existing tag.
|
||||
let (mut board2, scripts2) = board_with_object(
|
||||
Some("t2"),
|
||||
&[("t2", r#"fn init(m,s) { set_tag(m.id, "active", false); }"#)],
|
||||
&[("t2", r#"fn init(m) { set_tag(m.id, "active", false); }"#)],
|
||||
);
|
||||
// Seed the tag before construction.
|
||||
board2
|
||||
@@ -158,8 +158,8 @@ fn has_tag_reads_own_tags() {
|
||||
Some("t"),
|
||||
&[(
|
||||
"t",
|
||||
r#"fn init(m,s) { set_tag(m.id, "active", true); }
|
||||
fn tick(m,s,dt) { log(m.has_tag("active").to_string()); }"#,
|
||||
r#"fn init(m) { set_tag(m.id, "active", true); }
|
||||
fn tick(m,dt) { log(m.has_tag("active").to_string()); }"#,
|
||||
)],
|
||||
);
|
||||
let mut game = GameState::with_scripts(board, scripts);
|
||||
@@ -182,8 +182,8 @@ fn objects_with_tag_returns_matching_ids() {
|
||||
scripts_from(&[
|
||||
(
|
||||
"q",
|
||||
r#"fn init(m,s) {
|
||||
let infos = s.board.tagged("enemy");
|
||||
r#"fn init(m) {
|
||||
let infos = Board.tagged("enemy");
|
||||
log(infos.len().to_string());
|
||||
log(infos[0].id.to_string());
|
||||
}"#,
|
||||
@@ -201,7 +201,7 @@ fn objects_with_tag_returns_matching_ids() {
|
||||
fn my_name_returns_name_or_empty_string() {
|
||||
// An object with a name set on its ObjectDef should see it via my_name().
|
||||
let (mut board, scripts) =
|
||||
board_with_object(Some("n"), &[("n", r#"fn init(m,s) { log(m.name); }"#)]);
|
||||
board_with_object(Some("n"), &[("n", r#"fn init(m) { log(m.name); }"#)]);
|
||||
board.objects.get_mut(&1).unwrap().name = Some("beacon".to_string());
|
||||
let mut game = GameState::with_scripts(board, scripts);
|
||||
game.run_init();
|
||||
@@ -209,7 +209,7 @@ fn my_name_returns_name_or_empty_string() {
|
||||
|
||||
// An unnamed object should get ().
|
||||
let (board2, scripts2) =
|
||||
board_with_object(Some("n"), &[("n", r#"fn init(m,s) { if m.name == () { log("null"); }}"#)]);
|
||||
board_with_object(Some("n"), &[("n", r#"fn init(m) { if m.name == () { log("null"); }}"#)]);
|
||||
let mut game2 = GameState::with_scripts(board2, scripts2);
|
||||
game2.run_init();
|
||||
assert_eq!(log_texts(&game2), vec!["null"]);
|
||||
@@ -228,9 +228,9 @@ fn object_id_for_name_finds_by_name() {
|
||||
scripts_from(&[
|
||||
(
|
||||
"q",
|
||||
r#"fn init(m,s) {
|
||||
log(s.board.named("target").id.to_string());
|
||||
let miss = s.board.named("missing");
|
||||
r#"fn init(m) {
|
||||
log(Board.named("target").id.to_string());
|
||||
let miss = Board.named("missing");
|
||||
log(if miss == () { "not_found" } else { miss.id.to_string() });
|
||||
}"#,
|
||||
),
|
||||
@@ -251,7 +251,7 @@ fn player_bump_fires_with_negative_one() {
|
||||
let board = open_board(3, 1, (0, 0), vec![scripted_object(1, 0, "b")]);
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
scripts_from(&[("b", "fn bump(m,s,id) { log(`bumped by ${id}`); }")]),
|
||||
scripts_from(&[("b", "fn bump(m,id) { log(`bumped by ${id}`); }")]),
|
||||
);
|
||||
game.run_init();
|
||||
game.try_move(Direction::East);
|
||||
@@ -271,7 +271,7 @@ fn scroll_opens_on_player_bump() {
|
||||
board,
|
||||
scripts_from(&[(
|
||||
"s",
|
||||
r#"fn bump(m,s,id) { scroll(["Hello world", ["eat", "Eat it"]]); }"#,
|
||||
r#"fn bump(m,id) { scroll(["Hello world", ["eat", "Eat it"]]); }"#,
|
||||
)]),
|
||||
);
|
||||
game.run_init();
|
||||
@@ -295,7 +295,7 @@ fn handle_scroll_without_choice_clears_it() {
|
||||
let board = open_board(3, 1, (0, 0), vec![scripted_object(1, 0, "s")]);
|
||||
let mut game = GameState::with_scripts(
|
||||
board,
|
||||
scripts_from(&[("s", r#"fn bump(m,s,id) { scroll(["Hello"]); }"#)]),
|
||||
scripts_from(&[("s", r#"fn bump(m,id) { scroll(["Hello"]); }"#)]),
|
||||
);
|
||||
game.run_init();
|
||||
game.try_move(Direction::East);
|
||||
@@ -317,7 +317,7 @@ fn handle_scroll_with_choice_dispatches_send_to_source() {
|
||||
scripts_from(&[(
|
||||
"s",
|
||||
r#"
|
||||
fn bump(m,s,id) { scroll(["Muffin?", ["eat", "Eat it"]]); }
|
||||
fn bump(m,id) { scroll(["Muffin?", ["eat", "Eat it"]]); }
|
||||
fn eat() { log("eaten"); }
|
||||
"#,
|
||||
)]),
|
||||
|
||||
@@ -342,7 +342,7 @@ fn draw_play(frame: &mut Frame, game: &GameState, ui: &mut Ui) {
|
||||
.spacing(Spacing::Overlap(1))
|
||||
.areas(frame.area());
|
||||
frame.render_widget(
|
||||
StatusSidebarWidget(game.player),
|
||||
StatusSidebarWidget(game.player.clone()),
|
||||
sidebar_area,
|
||||
);
|
||||
rest
|
||||
|
||||
@@ -15,7 +15,7 @@ use ratatui::style::{Color, Style};
|
||||
use ratatui::symbols::merge::MergeStrategy;
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::widgets::{Block, Paragraph, Widget};
|
||||
use kiln_core::player::Player;
|
||||
use kiln_core::player::PlayerRef;
|
||||
|
||||
/// A filled heart: bright red.
|
||||
const HEART_FULL: Color = Color::Rgb(255, 0, 0);
|
||||
@@ -27,11 +27,11 @@ const HEART_EMPTY: Color = Color::Rgb(90, 0, 0);
|
||||
/// Shows a `Health:` label above a row of [`MAX_HEARTS`] heart glyphs, a
|
||||
/// `Gems: ♦ N` line, and a `Keys:` row of 8 `♀` glyphs colored when held
|
||||
/// and dark gray when absent.
|
||||
pub struct StatusSidebarWidget(pub Player);
|
||||
pub struct StatusSidebarWidget(pub PlayerRef);
|
||||
|
||||
impl Widget for StatusSidebarWidget {
|
||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||
let player = self.0;
|
||||
let player = self.0.borrow();
|
||||
// One bright-red span per filled heart, dark-red for the rest, so a
|
||||
// partly-depleted bar reads at a glance.
|
||||
let filled = player.health;
|
||||
|
||||
+15
-15
@@ -6,15 +6,15 @@ start = "start"
|
||||
# Objects reference a script by name via `script_name`.
|
||||
[scripts]
|
||||
greeter = """
|
||||
fn init(me, state) {
|
||||
log(`hello from object — player at ${state.player.x}, ${state.player.y}`);
|
||||
fn init(me) {
|
||||
log(`hello from object — player at ${Player.x}, ${Player.y}`);
|
||||
set_tile(2); // change my glyph to ☻ — proves the write path
|
||||
say("Hello there,\\ntraveller!");
|
||||
log(`Player health: ${state.player.health}`);
|
||||
log(`Player health: ${Player.health}`);
|
||||
}
|
||||
|
||||
fn tick(me, state, dt) {
|
||||
if state.player.x == me.x && state.player.y == me.y && !me.waiting {
|
||||
fn tick(me, dt) {
|
||||
if Player.x == me.x && Player.y == me.y && !me.waiting {
|
||||
say("Hey! Get offa me!");
|
||||
me.delay(5.0);
|
||||
}
|
||||
@@ -22,7 +22,7 @@ fn tick(me, state, dt) {
|
||||
"""
|
||||
|
||||
mover = """
|
||||
fn init(me, state) {
|
||||
fn init(me) {
|
||||
log(`q: ${me.queue.length}`);
|
||||
if Board.registry.get("dancer_x") != () {
|
||||
let new_x = Board.registry.get("dancer_x");
|
||||
@@ -58,14 +58,14 @@ fn dance(me) {
|
||||
}
|
||||
// Fires when something steps into this object; id is the bumper's object id,
|
||||
// or -1 for the player.
|
||||
fn bump(me, state, id) {
|
||||
fn bump(me, id) {
|
||||
log(`mover bumped by ${id}`);
|
||||
say("Ow!");
|
||||
}
|
||||
"""
|
||||
|
||||
muffin = """
|
||||
fn bump(me, state, _id) {
|
||||
fn bump(me, _id) {
|
||||
scroll([
|
||||
"You find a small muffin on the ground, your favorite kind.",
|
||||
"It smells of cinnamon and warm mornings.",
|
||||
@@ -73,7 +73,7 @@ fn bump(me, state, _id) {
|
||||
["ignore", "This is obviously a trap — ignore it."]
|
||||
]);
|
||||
}
|
||||
fn eat(me, state) {
|
||||
fn eat(me) {
|
||||
say("Yeah it was poisoned.");
|
||||
alter_health(-2);
|
||||
}
|
||||
@@ -84,7 +84,7 @@ fn ignore() {
|
||||
"""
|
||||
|
||||
noticeboard = """
|
||||
fn bump(me, state, id) {
|
||||
fn bump(me, id) {
|
||||
scroll([
|
||||
" TOWN NOTICE BOARD",
|
||||
" ",
|
||||
@@ -117,7 +117,7 @@ fn bump(me, state, id) {
|
||||
"""
|
||||
|
||||
bookshelf = """
|
||||
fn bump(me, state, id) {
|
||||
fn bump(me, id) {
|
||||
scroll([
|
||||
" THE BOOKSHELF",
|
||||
" ",
|
||||
@@ -136,13 +136,13 @@ fn bump(me, state, id) {
|
||||
"""
|
||||
|
||||
fireplace = """
|
||||
fn bump(me, state, id) {
|
||||
fn bump(me, id) {
|
||||
say("The fire crackles warmly.\\nYou feel at ease.");
|
||||
}
|
||||
"""
|
||||
|
||||
chest = """
|
||||
fn bump(me, state, id) {
|
||||
fn bump(me, id) {
|
||||
scroll([
|
||||
" THE CHEST",
|
||||
" ",
|
||||
@@ -160,7 +160,7 @@ fn bump(me, state, id) {
|
||||
"""
|
||||
|
||||
yammerer = """
|
||||
fn tick(me, state, dt) {
|
||||
fn tick(me, dt) {
|
||||
if !me.waiting {
|
||||
say("blahblahblah");
|
||||
delay(1);
|
||||
@@ -169,7 +169,7 @@ fn tick(me, state, dt) {
|
||||
"""
|
||||
|
||||
shifter = """
|
||||
fn tick(me, state, dt) {
|
||||
fn tick(me, dt) {
|
||||
let x = me.x;
|
||||
let y = me.y;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user