Board layers
This commit is contained in:
+352
-162
@@ -53,13 +53,16 @@
|
||||
//!
|
||||
//! `Queue.length()`, `Queue.clear()`, `Queue.peek()`, `Queue.pop()`
|
||||
|
||||
use crate::action::{action_to_map, Action, ScrollLine, MOVE_COST};
|
||||
use crate::game::SAY_DURATION;
|
||||
use crate::action::{Action, MOVE_COST, ScrollLine, action_to_map};
|
||||
use crate::board::Board;
|
||||
use crate::game::SAY_DURATION;
|
||||
use crate::log::LogLine;
|
||||
use crate::map_file::{color_to_hex, parse_color};
|
||||
use crate::utils::{Direction, ObjectId, RegistryValue, ScriptArg};
|
||||
use rhai::{Array, CallFnOptions, Dynamic, Engine, FuncArgs, ImmutableString, Module, NativeCallContext, Scope, AST};
|
||||
use rhai::{
|
||||
AST, Array, CallFnOptions, Dynamic, Engine, FuncArgs, ImmutableString, Module,
|
||||
NativeCallContext, Scope,
|
||||
};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::rc::Rc;
|
||||
@@ -206,20 +209,28 @@ impl ScriptHost {
|
||||
let mut scripts: HashMap<String, CompiledScript> = HashMap::new();
|
||||
let mut failed: HashSet<String> = HashSet::new();
|
||||
for obj in board.objects.values() {
|
||||
let Some(name) = obj.script_name.as_ref() else { continue; };
|
||||
if scripts.contains_key(name) || failed.contains(name) { continue; }
|
||||
let Some(name) = obj.script_name.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
if scripts.contains_key(name) || failed.contains(name) {
|
||||
continue;
|
||||
}
|
||||
match script_sources.get(name) {
|
||||
Some(src) => match engine.compile(src) {
|
||||
Ok(ast) => {
|
||||
let defines = |n: &str, params: usize| {
|
||||
ast.iter_functions().any(|f| f.name == n && f.params.len() == params)
|
||||
ast.iter_functions()
|
||||
.any(|f| f.name == n && f.params.len() == params)
|
||||
};
|
||||
scripts.insert(name.clone(), CompiledScript {
|
||||
has_init: defines("init", 0),
|
||||
has_tick: defines("tick", 1),
|
||||
has_bump: defines("bump", 1),
|
||||
ast,
|
||||
});
|
||||
scripts.insert(
|
||||
name.clone(),
|
||||
CompiledScript {
|
||||
has_init: defines("init", 0),
|
||||
has_tick: defines("tick", 1),
|
||||
has_bump: defines("bump", 1),
|
||||
ast,
|
||||
},
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
failed.insert(name.clone());
|
||||
@@ -240,8 +251,12 @@ impl ScriptHost {
|
||||
// One runtime per object whose script compiled.
|
||||
let mut objects = Vec::new();
|
||||
for (&id, obj) in board.objects.iter() {
|
||||
let Some(name) = obj.script_name.as_ref() else { continue; };
|
||||
if !scripts.contains_key(name) { continue; }
|
||||
let Some(name) = obj.script_name.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
if !scripts.contains_key(name) {
|
||||
continue;
|
||||
}
|
||||
let queue: ObjQueue = Rc::new(RefCell::new(VecDeque::new()));
|
||||
queues.borrow_mut().insert(id, queue.clone());
|
||||
objects.push(ObjectRuntime {
|
||||
@@ -253,7 +268,13 @@ impl ScriptHost {
|
||||
}
|
||||
|
||||
drop(board);
|
||||
Self { engine, scripts, objects, board_queue, errors }
|
||||
Self {
|
||||
engine,
|
||||
scripts,
|
||||
objects,
|
||||
board_queue,
|
||||
errors,
|
||||
}
|
||||
}
|
||||
|
||||
/// Calls `init()` on every scripted object that defines it and drains each queue.
|
||||
@@ -273,16 +294,31 @@ impl ScriptHost {
|
||||
return;
|
||||
};
|
||||
{
|
||||
let Self { engine, scripts, objects, errors, .. } = self;
|
||||
let Self {
|
||||
engine,
|
||||
scripts,
|
||||
objects,
|
||||
errors,
|
||||
..
|
||||
} = self;
|
||||
let obj = &mut objects[i];
|
||||
let Some(compiled) = scripts.get(&obj.script_name) else { return; };
|
||||
if !compiled.has_bump { return; }
|
||||
let Some(compiled) = scripts.get(&obj.script_name) else {
|
||||
return;
|
||||
};
|
||||
if !compiled.has_bump {
|
||||
return;
|
||||
}
|
||||
let options = CallFnOptions::default().with_tag(object_id as i64);
|
||||
if let Err(err) = engine.call_fn_with_options::<()>(
|
||||
options, &mut obj.scope, &compiled.ast, "bump", (bumper,),
|
||||
options,
|
||||
&mut obj.scope,
|
||||
&compiled.ast,
|
||||
"bump",
|
||||
(bumper,),
|
||||
) {
|
||||
errors.borrow_mut().push(LogLine::error(format!(
|
||||
"script '{}' bump error: {err}", obj.script_name
|
||||
"script '{}' bump error: {err}",
|
||||
obj.script_name
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -300,13 +336,25 @@ impl ScriptHost {
|
||||
return;
|
||||
};
|
||||
{
|
||||
let Self { engine, scripts, objects, errors, .. } = self;
|
||||
let Self {
|
||||
engine,
|
||||
scripts,
|
||||
objects,
|
||||
errors,
|
||||
..
|
||||
} = self;
|
||||
let obj = &mut objects[i];
|
||||
let Some(compiled) = scripts.get(&obj.script_name) else { return; };
|
||||
let Some(compiled) = scripts.get(&obj.script_name) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let has_1 = compiled.ast.iter_functions()
|
||||
let has_1 = compiled
|
||||
.ast
|
||||
.iter_functions()
|
||||
.any(|f| f.name == fn_name && f.params.len() == 1);
|
||||
let has_0 = compiled.ast.iter_functions()
|
||||
let has_0 = compiled
|
||||
.ast
|
||||
.iter_functions()
|
||||
.any(|f| f.name == fn_name && f.params.is_empty());
|
||||
|
||||
let options = CallFnOptions::default().with_tag(obj.object_id as i64);
|
||||
@@ -318,11 +366,19 @@ impl ScriptHost {
|
||||
None => Dynamic::UNIT,
|
||||
};
|
||||
engine.call_fn_with_options::<()>(
|
||||
options, &mut obj.scope, &compiled.ast, fn_name, (dyn_arg,),
|
||||
options,
|
||||
&mut obj.scope,
|
||||
&compiled.ast,
|
||||
fn_name,
|
||||
(dyn_arg,),
|
||||
)
|
||||
} else if has_0 {
|
||||
engine.call_fn_with_options::<()>(
|
||||
options, &mut obj.scope, &compiled.ast, fn_name, (),
|
||||
options,
|
||||
&mut obj.scope,
|
||||
&compiled.ast,
|
||||
fn_name,
|
||||
(),
|
||||
)
|
||||
} else {
|
||||
return; // Function not defined on this object
|
||||
@@ -330,7 +386,8 @@ impl ScriptHost {
|
||||
|
||||
if let Err(err) = result {
|
||||
errors.borrow_mut().push(LogLine::error(format!(
|
||||
"script '{}' send('{}') error: {err}", obj.script_name, fn_name
|
||||
"script '{}' send('{}') error: {err}",
|
||||
obj.script_name, fn_name
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -357,7 +414,9 @@ impl ScriptHost {
|
||||
if is_delay {
|
||||
let expired = {
|
||||
let mut q = queue.borrow_mut();
|
||||
let Some(Action::Delay(r)) = q.front_mut() else { break };
|
||||
let Some(Action::Delay(r)) = q.front_mut() else {
|
||||
break;
|
||||
};
|
||||
*r -= dt;
|
||||
dt = 0.0;
|
||||
*r <= 0.0
|
||||
@@ -368,8 +427,13 @@ impl ScriptHost {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
let Some(action) = queue.borrow_mut().pop_front() else { break };
|
||||
self.board_queue.borrow_mut().push(BoardAction { source: oid, action });
|
||||
let Some(action) = queue.borrow_mut().pop_front() else {
|
||||
break;
|
||||
};
|
||||
self.board_queue.borrow_mut().push(BoardAction {
|
||||
source: oid,
|
||||
action,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -384,17 +448,28 @@ impl ScriptHost {
|
||||
) {
|
||||
for i in 0..self.objects.len() {
|
||||
{
|
||||
let Self { engine, scripts, objects, errors, .. } = self;
|
||||
let Self {
|
||||
engine,
|
||||
scripts,
|
||||
objects,
|
||||
errors,
|
||||
..
|
||||
} = self;
|
||||
let obj = &mut objects[i];
|
||||
if let Some(compiled) = scripts.get(&obj.script_name)
|
||||
&& defined(compiled)
|
||||
{
|
||||
let options = CallFnOptions::default().with_tag(obj.object_id as i64);
|
||||
if let Err(err) = engine.call_fn_with_options::<()>(
|
||||
options, &mut obj.scope, &compiled.ast, hook, args,
|
||||
options,
|
||||
&mut obj.scope,
|
||||
&compiled.ast,
|
||||
hook,
|
||||
args,
|
||||
) {
|
||||
errors.borrow_mut().push(LogLine::error(format!(
|
||||
"script '{}' {hook} error: {err}", obj.script_name
|
||||
"script '{}' {hook} error: {err}",
|
||||
obj.script_name
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -412,36 +487,46 @@ fn register_me_type(engine: &mut Engine) {
|
||||
engine.register_get("id", |me: &mut Me| me.object_id as i64);
|
||||
|
||||
engine.register_get("name", |me: &mut Me| {
|
||||
me.board.borrow()
|
||||
.objects.get(&me.object_id)
|
||||
me.board
|
||||
.borrow()
|
||||
.objects
|
||||
.get(&me.object_id)
|
||||
.and_then(|o| o.name.as_deref())
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
});
|
||||
|
||||
engine.register_get("x", |me: &mut Me| {
|
||||
me.board.borrow()
|
||||
.objects.get(&me.object_id)
|
||||
me.board
|
||||
.borrow()
|
||||
.objects
|
||||
.get(&me.object_id)
|
||||
.map(|o| o.x as i64)
|
||||
.unwrap_or(0)
|
||||
});
|
||||
|
||||
engine.register_get("y", |me: &mut Me| {
|
||||
me.board.borrow()
|
||||
.objects.get(&me.object_id)
|
||||
me.board
|
||||
.borrow()
|
||||
.objects
|
||||
.get(&me.object_id)
|
||||
.map(|o| o.y as i64)
|
||||
.unwrap_or(0)
|
||||
});
|
||||
|
||||
engine.register_fn("has_tag", |me: &mut Me, tag: ImmutableString| {
|
||||
me.board.borrow()
|
||||
.objects.get(&me.object_id)
|
||||
me.board
|
||||
.borrow()
|
||||
.objects
|
||||
.get(&me.object_id)
|
||||
.is_some_and(|o| o.tags.contains(tag.as_str()))
|
||||
});
|
||||
|
||||
engine.register_fn("tags", |me: &mut Me| -> rhai::Array {
|
||||
me.board.borrow()
|
||||
.objects.get(&me.object_id)
|
||||
me.board
|
||||
.borrow()
|
||||
.objects
|
||||
.get(&me.object_id)
|
||||
.map(|o| o.tags.iter().map(|t| Dynamic::from(t.clone())).collect())
|
||||
.unwrap_or_default()
|
||||
});
|
||||
@@ -455,7 +540,11 @@ fn register_me_type(engine: &mut Engine) {
|
||||
bg: color_to_hex(obj.glyph.bg),
|
||||
}
|
||||
} else {
|
||||
RhaiGlyph { tile: 0, fg: "#000000".to_string(), bg: "#000000".to_string() }
|
||||
RhaiGlyph {
|
||||
tile: 0,
|
||||
fg: "#000000".to_string(),
|
||||
bg: "#000000".to_string(),
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -467,7 +556,8 @@ fn register_registry_type(engine: &mut Engine) {
|
||||
|
||||
// Registry.get(key) -> Dynamic — returns () if the key is absent.
|
||||
engine.register_fn("get", |r: &mut Registry, key: ImmutableString| -> Dynamic {
|
||||
r.board.borrow()
|
||||
r.board
|
||||
.borrow()
|
||||
.registry
|
||||
.get(key.as_str())
|
||||
.map(|v| v.clone().into())
|
||||
@@ -476,25 +566,39 @@ fn register_registry_type(engine: &mut Engine) {
|
||||
|
||||
// Registry.set(key, value) — stores a primitive value; () removes the key;
|
||||
// unsupported types (closures, custom objects) are silently ignored.
|
||||
engine.register_fn("set", |r: &mut Registry, key: ImmutableString, value: Dynamic| {
|
||||
match RegistryValue::try_from(value.clone()) {
|
||||
Ok(rv) => { r.board.borrow_mut().registry.insert(key.to_string(), rv); }
|
||||
Err(()) if value.is_unit() => { r.board.borrow_mut().registry.remove(key.as_str()); }
|
||||
Err(()) => {} // unsupported type — silently ignore
|
||||
}
|
||||
});
|
||||
engine.register_fn(
|
||||
"set",
|
||||
|r: &mut Registry, key: ImmutableString, value: Dynamic| {
|
||||
match RegistryValue::try_from(value.clone()) {
|
||||
Ok(rv) => {
|
||||
r.board.borrow_mut().registry.insert(key.to_string(), rv);
|
||||
}
|
||||
Err(()) if value.is_unit() => {
|
||||
r.board.borrow_mut().registry.remove(key.as_str());
|
||||
}
|
||||
Err(()) => {} // unsupported type — silently ignore
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Registry.get_or(key, default) — returns the stored value when present and the
|
||||
// same Rhai type as `default`; falls back to `default` otherwise.
|
||||
engine.register_fn("get_or", |r: &mut Registry, key: ImmutableString, default: Dynamic| -> Dynamic {
|
||||
if let Some(stored) = r.board.borrow().registry.get(key.as_str()).cloned() {
|
||||
let candidate: Dynamic = stored.into();
|
||||
// Only return the stored value if it matches the type of the default.
|
||||
if candidate.type_id() == default.type_id() { candidate } else { default }
|
||||
} else {
|
||||
default
|
||||
}
|
||||
});
|
||||
engine.register_fn(
|
||||
"get_or",
|
||||
|r: &mut Registry, key: ImmutableString, default: Dynamic| -> Dynamic {
|
||||
if let Some(stored) = r.board.borrow().registry.get(key.as_str()).cloned() {
|
||||
let candidate: Dynamic = stored.into();
|
||||
// Only return the stored value if it matches the type of the default.
|
||||
if candidate.type_id() == default.type_id() {
|
||||
candidate
|
||||
} else {
|
||||
default
|
||||
}
|
||||
} else {
|
||||
default
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// ── Register ObjectInfo type ──────────────────────────────────────────────────
|
||||
@@ -502,9 +606,9 @@ fn register_registry_type(engine: &mut Engine) {
|
||||
fn register_object_info_type(engine: &mut Engine) {
|
||||
engine.register_type_with_name::<ObjectInfo>("ObjectInfo");
|
||||
|
||||
engine.register_get("id", |o: &mut ObjectInfo| o.id);
|
||||
engine.register_get("x", |o: &mut ObjectInfo| o.x);
|
||||
engine.register_get("y", |o: &mut ObjectInfo| o.y);
|
||||
engine.register_get("id", |o: &mut ObjectInfo| o.id);
|
||||
engine.register_get("x", |o: &mut ObjectInfo| o.x);
|
||||
engine.register_get("y", |o: &mut ObjectInfo| o.y);
|
||||
|
||||
engine.register_get("name", |o: &mut ObjectInfo| {
|
||||
o.name.as_deref().unwrap_or("").to_string()
|
||||
@@ -520,8 +624,8 @@ fn register_object_info_type(engine: &mut Engine) {
|
||||
fn register_glyph_type(engine: &mut Engine) {
|
||||
engine.register_type_with_name::<RhaiGlyph>("Glyph");
|
||||
engine.register_get("tile", |g: &mut RhaiGlyph| g.tile);
|
||||
engine.register_get("fg", |g: &mut RhaiGlyph| g.fg.clone());
|
||||
engine.register_get("bg", |g: &mut RhaiGlyph| g.bg.clone());
|
||||
engine.register_get("fg", |g: &mut RhaiGlyph| g.fg.clone());
|
||||
engine.register_get("bg", |g: &mut RhaiGlyph| g.bg.clone());
|
||||
}
|
||||
|
||||
// ── Board read API ────────────────────────────────────────────────────────────
|
||||
@@ -535,8 +639,8 @@ fn register_read_api(
|
||||
engine.register_type_with_name::<BoardRef>("Board");
|
||||
engine.register_get("player_x", |b: &mut BoardRef| b.borrow().player.x as i64);
|
||||
engine.register_get("player_y", |b: &mut BoardRef| b.borrow().player.y as i64);
|
||||
engine.register_get("width", |b: &mut BoardRef| b.borrow().width as i64);
|
||||
engine.register_get("height", |b: &mut BoardRef| b.borrow().height as i64);
|
||||
engine.register_get("width", |b: &mut BoardRef| b.borrow().width as i64);
|
||||
engine.register_get("height", |b: &mut BoardRef| b.borrow().height as i64);
|
||||
|
||||
// blocked(dir) — true if moving in dir would be impossible for the calling object.
|
||||
let b = board.clone();
|
||||
@@ -547,25 +651,35 @@ fn register_read_api(
|
||||
|
||||
// Board.tagged(tag) -> Array[ObjectInfo]
|
||||
let b = board.clone();
|
||||
engine.register_fn("tagged", move |_board: &mut BoardRef, tag: ImmutableString| -> rhai::Array {
|
||||
let board = b.borrow();
|
||||
board.objects.iter()
|
||||
.filter(|(_, o)| o.tags.contains(tag.as_str()))
|
||||
.filter_map(|(&id, _)| object_info_from(id, &board))
|
||||
.map(Dynamic::from)
|
||||
.collect()
|
||||
});
|
||||
engine.register_fn(
|
||||
"tagged",
|
||||
move |_board: &mut BoardRef, tag: ImmutableString| -> rhai::Array {
|
||||
let board = b.borrow();
|
||||
board
|
||||
.objects
|
||||
.iter()
|
||||
.filter(|(_, o)| o.tags.contains(tag.as_str()))
|
||||
.filter_map(|(&id, _)| object_info_from(id, &board))
|
||||
.map(Dynamic::from)
|
||||
.collect()
|
||||
},
|
||||
);
|
||||
|
||||
// Board.named(name) -> ObjectInfo | ()
|
||||
let b = board.clone();
|
||||
engine.register_fn("named", move |_board: &mut BoardRef, name: ImmutableString| -> Dynamic {
|
||||
let board = b.borrow();
|
||||
board.objects.iter()
|
||||
.find(|(_, o)| o.name.as_deref() == Some(name.as_str()))
|
||||
.and_then(|(&id, _)| object_info_from(id, &board))
|
||||
.map(Dynamic::from)
|
||||
.unwrap_or(Dynamic::UNIT)
|
||||
});
|
||||
engine.register_fn(
|
||||
"named",
|
||||
move |_board: &mut BoardRef, name: ImmutableString| -> Dynamic {
|
||||
let board = b.borrow();
|
||||
board
|
||||
.objects
|
||||
.iter()
|
||||
.find(|(_, o)| o.name.as_deref() == Some(name.as_str()))
|
||||
.and_then(|(&id, _)| object_info_from(id, &board))
|
||||
.map(Dynamic::from)
|
||||
.unwrap_or(Dynamic::UNIT)
|
||||
},
|
||||
);
|
||||
|
||||
// Board.get(id) -> ObjectInfo | () (-1 returns player; unknown id logs error)
|
||||
let b = board.clone();
|
||||
@@ -576,13 +690,15 @@ fn register_read_api(
|
||||
return Dynamic::from(object_info_player(&board));
|
||||
}
|
||||
if id <= 0 {
|
||||
errs.borrow_mut().push(LogLine::error(format!("Board.get: invalid id {id}")));
|
||||
errs.borrow_mut()
|
||||
.push(LogLine::error(format!("Board.get: invalid id {id}")));
|
||||
return Dynamic::UNIT;
|
||||
}
|
||||
match object_info_from(id as ObjectId, &board) {
|
||||
Some(info) => Dynamic::from(info),
|
||||
None => {
|
||||
errs.borrow_mut().push(LogLine::error(format!("Board.get: no object with id {id}")));
|
||||
errs.borrow_mut()
|
||||
.push(LogLine::error(format!("Board.get: no object with id {id}")));
|
||||
Dynamic::UNIT
|
||||
}
|
||||
}
|
||||
@@ -597,12 +713,18 @@ fn is_blocked(
|
||||
dir: Direction,
|
||||
) -> bool {
|
||||
let board = board.borrow();
|
||||
let Some(obj) = board.objects.get(&source) else { return true; };
|
||||
let Some(obj) = board.objects.get(&source) else {
|
||||
return true;
|
||||
};
|
||||
let (dx, dy): (i32, i32) = dir.into();
|
||||
let target = (obj.x as i32 + dx, obj.y as i32 + dy);
|
||||
if !board.in_bounds(target) { return true; }
|
||||
if !board.in_bounds(target) {
|
||||
return true;
|
||||
}
|
||||
let (tx, ty) = (target.0 as usize, target.1 as usize);
|
||||
if board.solid_at(tx, ty).is_some() { return true; }
|
||||
if board.solid_at(tx, ty).is_some() {
|
||||
return true;
|
||||
}
|
||||
board_queue.borrow().iter().any(|ba| {
|
||||
if let Action::Move(d) = &ba.action
|
||||
&& let Some(mover) = board.objects.get(&ba.source)
|
||||
@@ -663,37 +785,57 @@ fn register_write_api(engine: &mut Engine, queues: &QueueMap) {
|
||||
});
|
||||
|
||||
let q = queues.clone();
|
||||
engine.register_fn("log", move |ctx: NativeCallContext, msg: ImmutableString| {
|
||||
emit(&q, source_of(&ctx), Action::Log(LogLine::raw(msg.to_string())));
|
||||
});
|
||||
engine.register_fn(
|
||||
"log",
|
||||
move |ctx: NativeCallContext, msg: ImmutableString| {
|
||||
emit(
|
||||
&q,
|
||||
source_of(&ctx),
|
||||
Action::Log(LogLine::raw(msg.to_string())),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
let q = queues.clone();
|
||||
engine.register_fn("say", move |ctx: NativeCallContext, msg: ImmutableString| {
|
||||
emit(&q, source_of(&ctx), Action::Say(msg.to_string(), SAY_DURATION));
|
||||
});
|
||||
engine.register_fn(
|
||||
"say",
|
||||
move |ctx: NativeCallContext, msg: ImmutableString| {
|
||||
emit(
|
||||
&q,
|
||||
source_of(&ctx),
|
||||
Action::Say(msg.to_string(), SAY_DURATION),
|
||||
);
|
||||
},
|
||||
);
|
||||
let q = queues.clone();
|
||||
engine.register_fn("say", move |ctx: NativeCallContext, msg: ImmutableString, dur: f64| {
|
||||
emit(&q, source_of(&ctx), Action::Say(msg.to_string(), dur));
|
||||
});
|
||||
engine.register_fn(
|
||||
"say",
|
||||
move |ctx: NativeCallContext, msg: ImmutableString, dur: f64| {
|
||||
emit(&q, source_of(&ctx), Action::Say(msg.to_string(), dur));
|
||||
},
|
||||
);
|
||||
|
||||
// scroll(lines): open a full-screen scrollable overlay. Each element of `lines`
|
||||
// is either a plain string (text) or a 2-element array [choice_key, display_text].
|
||||
let q = queues.clone();
|
||||
engine.register_fn("scroll", move |ctx: NativeCallContext, arr: Array| {
|
||||
let lines = arr.into_iter().filter_map(|item| {
|
||||
if let Ok(s) = item.clone().into_string() {
|
||||
Some(ScrollLine::Text(s))
|
||||
} else {
|
||||
let pair: Vec<Dynamic> = item.into_array().ok()?;
|
||||
if pair.len() == 2 {
|
||||
let choice = pair[0].clone().into_string().ok()?;
|
||||
let display = pair[1].clone().into_string().ok()?;
|
||||
Some(ScrollLine::Choice { choice, display })
|
||||
let lines = arr
|
||||
.into_iter()
|
||||
.filter_map(|item| {
|
||||
if let Ok(s) = item.clone().into_string() {
|
||||
Some(ScrollLine::Text(s))
|
||||
} else {
|
||||
None
|
||||
let pair: Vec<Dynamic> = item.into_array().ok()?;
|
||||
if pair.len() == 2 {
|
||||
let choice = pair[0].clone().into_string().ok()?;
|
||||
let display = pair[1].clone().into_string().ok()?;
|
||||
Some(ScrollLine::Choice { choice, display })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
emit(&q, source_of(&ctx), Action::Scroll(lines));
|
||||
});
|
||||
|
||||
@@ -701,41 +843,63 @@ fn register_write_api(engine: &mut Engine, queues: &QueueMap) {
|
||||
engine.register_fn(
|
||||
"set_tag",
|
||||
move |ctx: NativeCallContext, target: i64, tag: ImmutableString, present: bool| {
|
||||
emit(&q, source_of(&ctx), Action::SetTag {
|
||||
target: target as ObjectId,
|
||||
tag: tag.to_string(),
|
||||
present,
|
||||
});
|
||||
emit(
|
||||
&q,
|
||||
source_of(&ctx),
|
||||
Action::SetTag {
|
||||
target: target as ObjectId,
|
||||
tag: tag.to_string(),
|
||||
present,
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// set_fg(fg): change foreground color only.
|
||||
let q = queues.clone();
|
||||
engine.register_fn("set_fg", move |ctx: NativeCallContext, fg: ImmutableString| {
|
||||
emit(&q, source_of(&ctx), Action::SetColor {
|
||||
fg: Some(parse_color(fg.as_str())),
|
||||
bg: None,
|
||||
});
|
||||
});
|
||||
engine.register_fn(
|
||||
"set_fg",
|
||||
move |ctx: NativeCallContext, fg: ImmutableString| {
|
||||
emit(
|
||||
&q,
|
||||
source_of(&ctx),
|
||||
Action::SetColor {
|
||||
fg: Some(parse_color(fg.as_str())),
|
||||
bg: None,
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// set_bg(bg): change background color only.
|
||||
let q = queues.clone();
|
||||
engine.register_fn("set_bg", move |ctx: NativeCallContext, bg: ImmutableString| {
|
||||
emit(&q, source_of(&ctx), Action::SetColor {
|
||||
fg: None,
|
||||
bg: Some(parse_color(bg.as_str())),
|
||||
});
|
||||
});
|
||||
engine.register_fn(
|
||||
"set_bg",
|
||||
move |ctx: NativeCallContext, bg: ImmutableString| {
|
||||
emit(
|
||||
&q,
|
||||
source_of(&ctx),
|
||||
Action::SetColor {
|
||||
fg: None,
|
||||
bg: Some(parse_color(bg.as_str())),
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
// set_color(fg, bg): change both colors.
|
||||
let q = queues.clone();
|
||||
engine.register_fn(
|
||||
"set_color",
|
||||
move |ctx: NativeCallContext, fg: ImmutableString, bg: ImmutableString| {
|
||||
emit(&q, source_of(&ctx), Action::SetColor {
|
||||
fg: Some(parse_color(fg.as_str())),
|
||||
bg: Some(parse_color(bg.as_str())),
|
||||
});
|
||||
emit(
|
||||
&q,
|
||||
source_of(&ctx),
|
||||
Action::SetColor {
|
||||
fg: Some(parse_color(fg.as_str())),
|
||||
bg: Some(parse_color(bg.as_str())),
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -744,11 +908,15 @@ fn register_write_api(engine: &mut Engine, queues: &QueueMap) {
|
||||
engine.register_fn(
|
||||
"send",
|
||||
move |ctx: NativeCallContext, target: i64, name: ImmutableString| {
|
||||
emit(&q, source_of(&ctx), Action::Send {
|
||||
target: target as ObjectId,
|
||||
fn_name: name.to_string(),
|
||||
arg: None,
|
||||
});
|
||||
emit(
|
||||
&q,
|
||||
source_of(&ctx),
|
||||
Action::Send {
|
||||
target: target as ObjectId,
|
||||
fn_name: name.to_string(),
|
||||
arg: None,
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -766,11 +934,15 @@ fn register_write_api(engine: &mut Engine, queues: &QueueMap) {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
emit(&q, source_of(&ctx), Action::Send {
|
||||
target: target as ObjectId,
|
||||
fn_name: name.to_string(),
|
||||
arg: script_arg,
|
||||
});
|
||||
emit(
|
||||
&q,
|
||||
source_of(&ctx),
|
||||
Action::Send {
|
||||
target: target as ObjectId,
|
||||
fn_name: name.to_string(),
|
||||
arg: script_arg,
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -779,7 +951,14 @@ fn register_write_api(engine: &mut Engine, queues: &QueueMap) {
|
||||
// destination already has a solid occupant.
|
||||
let q = queues.clone();
|
||||
engine.register_fn("teleport", move |ctx: NativeCallContext, x: i64, y: i64| {
|
||||
emit(&q, source_of(&ctx), Action::Teleport { x: x as i32, y: y as i32 });
|
||||
emit(
|
||||
&q,
|
||||
source_of(&ctx),
|
||||
Action::Teleport {
|
||||
x: x as i32,
|
||||
y: y as i32,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -788,7 +967,7 @@ fn register_write_api(engine: &mut Engine, queues: &QueueMap) {
|
||||
fn register_queue_api(engine: &mut Engine) {
|
||||
engine.register_type_with_name::<ObjQueue>("Queue");
|
||||
engine.register_fn("length", |q: &mut ObjQueue| q.borrow().len() as i64);
|
||||
engine.register_fn("clear", |q: &mut ObjQueue| q.borrow_mut().clear());
|
||||
engine.register_fn("clear", |q: &mut ObjQueue| q.borrow_mut().clear());
|
||||
|
||||
// peek(): front action as a Rhai map, or () if empty.
|
||||
engine.register_fn("peek", |q: &mut ObjQueue| -> Dynamic {
|
||||
@@ -817,25 +996,25 @@ fn register_global_constants(engine: &mut Engine) {
|
||||
let mut m = Module::new();
|
||||
m.set_var("North", Direction::North);
|
||||
m.set_var("South", Direction::South);
|
||||
m.set_var("East", Direction::East);
|
||||
m.set_var("West", Direction::West);
|
||||
m.set_var("East", Direction::East);
|
||||
m.set_var("West", Direction::West);
|
||||
// 16 EGA/VGA color constants as "#RRGGBB" strings.
|
||||
m.set_var("Black", "#000000");
|
||||
m.set_var("Blue", "#0000AA");
|
||||
m.set_var("Green", "#00AA00");
|
||||
m.set_var("Cyan", "#00AAAA");
|
||||
m.set_var("Red", "#AA0000");
|
||||
m.set_var("Magenta", "#AA00AA");
|
||||
m.set_var("Brown", "#AA5500");
|
||||
m.set_var("LightGray", "#AAAAAA");
|
||||
m.set_var("DarkGray", "#555555");
|
||||
m.set_var("BrightBlue", "#5555FF");
|
||||
m.set_var("BrightGreen", "#55FF55");
|
||||
m.set_var("BrightCyan", "#55FFFF");
|
||||
m.set_var("BrightRed", "#FF5555");
|
||||
m.set_var("Black", "#000000");
|
||||
m.set_var("Blue", "#0000AA");
|
||||
m.set_var("Green", "#00AA00");
|
||||
m.set_var("Cyan", "#00AAAA");
|
||||
m.set_var("Red", "#AA0000");
|
||||
m.set_var("Magenta", "#AA00AA");
|
||||
m.set_var("Brown", "#AA5500");
|
||||
m.set_var("LightGray", "#AAAAAA");
|
||||
m.set_var("DarkGray", "#555555");
|
||||
m.set_var("BrightBlue", "#5555FF");
|
||||
m.set_var("BrightGreen", "#55FF55");
|
||||
m.set_var("BrightCyan", "#55FFFF");
|
||||
m.set_var("BrightRed", "#FF5555");
|
||||
m.set_var("BrightMagenta", "#FF55FF");
|
||||
m.set_var("Yellow", "#FFFF55");
|
||||
m.set_var("White", "#FFFFFF");
|
||||
m.set_var("Yellow", "#FFFF55");
|
||||
m.set_var("White", "#FFFFFF");
|
||||
engine.register_global_module(m.into());
|
||||
}
|
||||
|
||||
@@ -845,8 +1024,19 @@ fn new_object_scope(board: &BoardRef, queue: &ObjQueue, object_id: ObjectId) ->
|
||||
let mut scope = Scope::new();
|
||||
scope.push_constant("Board", board.clone());
|
||||
scope.push_constant("Queue", queue.clone());
|
||||
scope.push_constant("Me", Me { object_id, board: board.clone() });
|
||||
scope.push_constant("Registry", Registry { board: board.clone() });
|
||||
scope.push_constant(
|
||||
"Me",
|
||||
Me {
|
||||
object_id,
|
||||
board: board.clone(),
|
||||
},
|
||||
);
|
||||
scope.push_constant(
|
||||
"Registry",
|
||||
Registry {
|
||||
board: board.clone(),
|
||||
},
|
||||
);
|
||||
scope
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user