Board layers

This commit is contained in:
2026-06-15 23:35:18 -05:00
parent d1d0824d37
commit 01cd73ca3b
29 changed files with 2166 additions and 2051 deletions
+352 -162
View File
@@ -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
}