Fixed three script host bugs; scriptkey
This commit is contained in:
Generated
+1
@@ -764,6 +764,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"color",
|
"color",
|
||||||
"doryen-fov",
|
"doryen-fov",
|
||||||
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"rhai",
|
"rhai",
|
||||||
"serde",
|
"serde",
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ serde = { version = "1", features = ["derive"] }
|
|||||||
tinyrand = "0.5"
|
tinyrand = "0.5"
|
||||||
toml = { version = "0.8", features = ["preserve_order"] }
|
toml = { version = "0.8", features = ["preserve_order"] }
|
||||||
log = "0.4.33"
|
log = "0.4.33"
|
||||||
|
lazy_static = "1.5.0"
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ use crate::{Board, Direction};
|
|||||||
use crate::action::BoardAction;
|
use crate::action::BoardAction;
|
||||||
use crate::object_def::ObjectDef;
|
use crate::object_def::ObjectDef;
|
||||||
use crate::script::Registerable;
|
use crate::script::Registerable;
|
||||||
use crate::tile::{Hookable, Optics, ScriptAttributes, Tile};
|
use crate::tile::{Hookable, Optics, ScriptAttributes, ScriptKey, Tile};
|
||||||
use crate::utils::{LogSink, ObjectId};
|
use crate::utils::{LogSink, ObjectId};
|
||||||
|
|
||||||
/// A snapshot of one board object, returned by `Board.tagged`, `Board.named`,
|
/// A snapshot of one board object, returned by `Board.tagged`, `Board.named`,
|
||||||
@@ -42,7 +42,7 @@ pub struct ObjectInfo {
|
|||||||
pub x: i64,
|
pub x: i64,
|
||||||
pub y: i64,
|
pub y: i64,
|
||||||
pub board: BoardRef,
|
pub board: BoardRef,
|
||||||
pub script_name: Option<String>,
|
pub script_key: ScriptKey,
|
||||||
pub queue: ObjQueue
|
pub queue: ObjQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ impl ObjectInfo {
|
|||||||
x: x as i64,
|
x: x as i64,
|
||||||
y: y as i64,
|
y: y as i64,
|
||||||
board,
|
board,
|
||||||
script_name: hookable.scriptable().script_name.clone(),
|
script_key: hookable.scriptable().script_name.clone(),
|
||||||
queue: hookable.scriptable().queue.clone(),
|
queue: hookable.scriptable().queue.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ impl ObjectInfo {
|
|||||||
x: x as i64,
|
x: x as i64,
|
||||||
y: y as i64,
|
y: y as i64,
|
||||||
board: board.clone(),
|
board: board.clone(),
|
||||||
script_name: obj.scripting.script_name.clone(),
|
script_key: obj.scripting.script_name.clone(),
|
||||||
queue: obj.scripting.queue.clone()
|
queue: obj.scripting.queue.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -71,13 +71,16 @@ pub struct Board {
|
|||||||
impl Board {
|
impl Board {
|
||||||
/// Return a list of all `ObjectId`s currently on the board.
|
/// Return a list of all `ObjectId`s currently on the board.
|
||||||
pub fn all_ids(&self) -> Vec<ObjectId> {
|
pub fn all_ids(&self) -> Vec<ObjectId> {
|
||||||
self.grid.iter().filter_map(|cell| {
|
let mut grid_ids = self.grid.iter().filter_map(|cell| {
|
||||||
if let Some(Tile::Object(def)) = cell {
|
if let Some(Tile::Object(def)) = cell {
|
||||||
Some(def.scripting.id)
|
Some(def.scripting.id)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}).collect()
|
}).collect::<Vec<_>>();
|
||||||
|
grid_ids.extend(self.sensors.iter().map(|s| s.scripting.id));
|
||||||
|
grid_ids.sort();
|
||||||
|
grid_ids
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the `(Glyph, Archetype)` cell at `(x, y)`.
|
/// Returns a reference to the `(Glyph, Archetype)` cell at `(x, y)`.
|
||||||
@@ -234,6 +237,13 @@ impl Board {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Glowing sensors
|
||||||
|
for s in self.sensors.iter() {
|
||||||
|
if s.optics().glow > 0 {
|
||||||
|
add_source(&mut lighting, s.x, s.y, s.optics().glow, color_to_rgb(s.scripting.glyph.fg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Some(lighting)
|
Some(lighting)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+27
-12
@@ -1,9 +1,11 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
use crate::glyph::Glyph;
|
use crate::glyph::Glyph;
|
||||||
use crate::utils::Pushable;
|
use crate::utils::Pushable;
|
||||||
use color::Rgba8;
|
use color::Rgba8;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
use crate::keys::KeyType;
|
use crate::keys::KeyType;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
use crate::tile::{EnterResponse, Optics};
|
use crate::tile::{EnterResponse, Optics, ScriptKey};
|
||||||
|
|
||||||
/// Declares the set of script-backed archetype families.
|
/// Declares the set of script-backed archetype families.
|
||||||
///
|
///
|
||||||
@@ -89,7 +91,7 @@ macro_rules! builtins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the embedded Rhai source shared by all aliases in this family.
|
/// Returns the embedded Rhai source shared by all aliases in this family.
|
||||||
pub fn script(self) -> &'static str {
|
pub fn script(self) -> ScriptKey {
|
||||||
match self {
|
match self {
|
||||||
$( Builtin::$variant => $script, )+
|
$( Builtin::$variant => $script, )+
|
||||||
}
|
}
|
||||||
@@ -112,12 +114,12 @@ builtins! {
|
|||||||
Gem => ["gem" => g(4, 0x50, 0x50, 0xFF)] {
|
Gem => ["gem" => g(4, 0x50, 0x50, 0xFF)] {
|
||||||
enter: EnterResponse::Grab,
|
enter: EnterResponse::Grab,
|
||||||
optics: Optics { opaque: false, glow: 0 },
|
optics: Optics { opaque: false, glow: 0 },
|
||||||
script: include_str!("scripts/gem.rhai"),
|
script: ScriptKey::Builtin("gem"),
|
||||||
},
|
},
|
||||||
Heart => ["heart" => g(3, 0xCC, 0x22, 0x22)] {
|
Heart => ["heart" => g(3, 0xCC, 0x22, 0x22)] {
|
||||||
enter: EnterResponse::Grab,
|
enter: EnterResponse::Grab,
|
||||||
optics: Optics { opaque: false, glow: 0 },
|
optics: Optics { opaque: false, glow: 0 },
|
||||||
script: include_str!("scripts/heart.rhai"),
|
script: ScriptKey::Builtin("heart"),
|
||||||
},
|
},
|
||||||
Pusher => [
|
Pusher => [
|
||||||
"pusher_north" => g(30, 0xAA, 0xAA, 0xAA),
|
"pusher_north" => g(30, 0xAA, 0xAA, 0xAA),
|
||||||
@@ -127,7 +129,7 @@ builtins! {
|
|||||||
] {
|
] {
|
||||||
enter: EnterResponse::Block,
|
enter: EnterResponse::Block,
|
||||||
optics: Optics { opaque: true, glow: 0 },
|
optics: Optics { opaque: true, glow: 0 },
|
||||||
script: include_str!("scripts/pusher.rhai"),
|
script: ScriptKey::Builtin("pusher"),
|
||||||
},
|
},
|
||||||
Spinner => [
|
Spinner => [
|
||||||
"spinner_cw" => g(47, 0xAA, 0xAA, 0xAA),
|
"spinner_cw" => g(47, 0xAA, 0xAA, 0xAA),
|
||||||
@@ -135,7 +137,7 @@ builtins! {
|
|||||||
] {
|
] {
|
||||||
enter: EnterResponse::Block,
|
enter: EnterResponse::Block,
|
||||||
optics: Optics { opaque: true, glow: 0 },
|
optics: Optics { opaque: true, glow: 0 },
|
||||||
script: include_str!("scripts/spinner.rhai"),
|
script: ScriptKey::Builtin("spinner"),
|
||||||
},
|
},
|
||||||
// Solid, see-through (opaque: false), unpushable teleporters. Each direction's
|
// Solid, see-through (opaque: false), unpushable teleporters. Each direction's
|
||||||
// default glyph is the first frame of its animation loop (see transporter.rhai).
|
// default glyph is the first frame of its animation loop (see transporter.rhai).
|
||||||
@@ -147,7 +149,7 @@ builtins! {
|
|||||||
] {
|
] {
|
||||||
enter: EnterResponse::Hook,
|
enter: EnterResponse::Hook,
|
||||||
optics: Optics { opaque: false, glow: 0 },
|
optics: Optics { opaque: false, glow: 0 },
|
||||||
script: include_str!("scripts/transporter.rhai"),
|
script: ScriptKey::Builtin("transporter"),
|
||||||
},
|
},
|
||||||
Key => [ // TODO these should refer to the key colors in Keyring
|
Key => [ // TODO these should refer to the key colors in Keyring
|
||||||
"key_blue" => KeyType::Blue.glyph(),
|
"key_blue" => KeyType::Blue.glyph(),
|
||||||
@@ -161,7 +163,7 @@ builtins! {
|
|||||||
] {
|
] {
|
||||||
enter: EnterResponse::Grab,
|
enter: EnterResponse::Grab,
|
||||||
optics: Optics { opaque: false, glow: 0 },
|
optics: Optics { opaque: false, glow: 0 },
|
||||||
script: include_str!("scripts/key.rhai"),
|
script: ScriptKey::Builtin("key"),
|
||||||
},
|
},
|
||||||
Wall => ["wall" => Glyph {
|
Wall => ["wall" => Glyph {
|
||||||
tile: 35,
|
tile: 35,
|
||||||
@@ -169,25 +171,38 @@ builtins! {
|
|||||||
bg: Rgba8 { r: 0x60, g: 0x60, b: 0x60, a: 255 }}] {
|
bg: Rgba8 { r: 0x60, g: 0x60, b: 0x60, a: 255 }}] {
|
||||||
enter: EnterResponse::Block,
|
enter: EnterResponse::Block,
|
||||||
optics: Optics { opaque: true, glow: 0 },
|
optics: Optics { opaque: true, glow: 0 },
|
||||||
script: ""
|
script: ScriptKey::None
|
||||||
},
|
},
|
||||||
Crate => ["crate" => g(254, 0xaa, 0xaa, 0xaa)] { // CP437 ■ (small filled square)
|
Crate => ["crate" => g(254, 0xaa, 0xaa, 0xaa)] { // CP437 ■ (small filled square)
|
||||||
enter: EnterResponse::Push(Pushable::Any),
|
enter: EnterResponse::Push(Pushable::Any),
|
||||||
optics: Optics { opaque: true, glow: 0 },
|
optics: Optics { opaque: true, glow: 0 },
|
||||||
script: ""
|
script: ScriptKey::None
|
||||||
},
|
},
|
||||||
HCrate => ["hcrate" => g(29, 0xaa, 0xaa, 0xaa)] { // CP437 ↔ (left-right arrow) — pushable east/west
|
HCrate => ["hcrate" => g(29, 0xaa, 0xaa, 0xaa)] { // CP437 ↔ (left-right arrow) — pushable east/west
|
||||||
enter: EnterResponse::Push(Pushable::Horizontal),
|
enter: EnterResponse::Push(Pushable::Horizontal),
|
||||||
optics: Optics { opaque: true, glow: 0 },
|
optics: Optics { opaque: true, glow: 0 },
|
||||||
script: ""
|
script: ScriptKey::None
|
||||||
},
|
},
|
||||||
VCrate => ["vcrate" => g(18, 0xaa, 0xaa, 0xaa)] { // CP437 ↕ (up-down arrow) — pushable north/south
|
VCrate => ["vcrate" => g(18, 0xaa, 0xaa, 0xaa)] { // CP437 ↕ (up-down arrow) — pushable north/south
|
||||||
enter: EnterResponse::Push(Pushable::Vertical),
|
enter: EnterResponse::Push(Pushable::Vertical),
|
||||||
optics: Optics { opaque: true, glow: 0 },
|
optics: Optics { opaque: true, glow: 0 },
|
||||||
script: ""
|
script: ScriptKey::None
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub static ref BUILTIN_SOURCES: HashMap<ScriptKey, &'static str> = {
|
||||||
|
let mut m = HashMap::new();
|
||||||
|
m.insert(ScriptKey::Builtin("gem"), include_str!("scripts/gem.rhai"));
|
||||||
|
m.insert(ScriptKey::Builtin("heart"), include_str!("scripts/heart.rhai"));
|
||||||
|
m.insert(ScriptKey::Builtin("pusher"), include_str!("scripts/pusher.rhai"));
|
||||||
|
m.insert(ScriptKey::Builtin("spinner"), include_str!("scripts/spinner.rhai"));
|
||||||
|
m.insert(ScriptKey::Builtin("transporter"), include_str!("scripts/transporter.rhai"));
|
||||||
|
m.insert(ScriptKey::Builtin("key"), include_str!("scripts/key.rhai"));
|
||||||
|
m
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::Builtin;
|
use super::Builtin;
|
||||||
|
|||||||
+40
-47
@@ -48,10 +48,12 @@ use crate::api::object_info::ObjectInfo;
|
|||||||
use crate::api::player::PlayerWithPos;
|
use crate::api::player::PlayerWithPos;
|
||||||
use crate::api::queue::ObjQueue;
|
use crate::api::queue::ObjQueue;
|
||||||
use crate::api::registry::Registry;
|
use crate::api::registry::Registry;
|
||||||
|
use crate::builtin::BUILTIN_SOURCES;
|
||||||
use crate::colors::parse_color;
|
use crate::colors::parse_color;
|
||||||
use crate::glyph::Glyph;
|
use crate::glyph::Glyph;
|
||||||
use crate::keys::Keyring;
|
use crate::keys::Keyring;
|
||||||
use crate::player::PlayerRef;
|
use crate::player::PlayerRef;
|
||||||
|
use crate::tile::ScriptKey;
|
||||||
|
|
||||||
/// Types which can be registered to be sent to Rhai
|
/// Types which can be registered to be sent to Rhai
|
||||||
pub trait Registerable {
|
pub trait Registerable {
|
||||||
@@ -86,7 +88,7 @@ impl CompiledScript {
|
|||||||
/// Owns the Rhai engine and per-object script state for a board.
|
/// Owns the Rhai engine and per-object script state for a board.
|
||||||
pub struct ScriptHost {
|
pub struct ScriptHost {
|
||||||
engine: Engine,
|
engine: Engine,
|
||||||
scripts: HashMap<String, CompiledScript>,
|
scripts: HashMap<ScriptKey, CompiledScript>,
|
||||||
scopes: HashMap<ObjectId, Scope<'static>>,
|
scopes: HashMap<ObjectId, Scope<'static>>,
|
||||||
log_sink: LogSink,
|
log_sink: LogSink,
|
||||||
board: BoardRef
|
board: BoardRef
|
||||||
@@ -123,57 +125,50 @@ impl ScriptHost {
|
|||||||
// `script_name`: a world-pool name for named scripts, or a synthetic
|
// `script_name`: a world-pool name for named scripts, or a synthetic
|
||||||
// `BUILTIN_*` name for built-ins so identical ones share one AST). The
|
// `BUILTIN_*` name for built-ins so identical ones share one AST). The
|
||||||
// source for a built-in still comes from its embedded `builtin_script`.
|
// source for a built-in still comes from its embedded `builtin_script`.
|
||||||
let mut scripts: HashMap<String, CompiledScript> = HashMap::new();
|
let mut scripts: HashMap<ScriptKey, CompiledScript> = HashMap::new();
|
||||||
let mut failed: HashSet<String> = HashSet::new();
|
let mut failed: HashSet<ScriptKey> = HashSet::new();
|
||||||
let all_scriptables = board.sorted_hookables();
|
let all_scriptables = board.sorted_hookables();
|
||||||
for obj in all_scriptables.iter() {
|
for obj in all_scriptables.iter() {
|
||||||
let Some(key) = obj.script_key() else {
|
let script_key = obj.script_key();
|
||||||
continue;
|
// We've already compiled, or failed to compile, this script...
|
||||||
};
|
if scripts.contains_key(script_key) || failed.contains(script_key) {
|
||||||
if scripts.contains_key(key) || failed.contains(key) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Source: the embedded built-in, or a lookup in the world script pool.
|
|
||||||
let source: &str = if let Some(src) = obj.scriptable().builtin_script {
|
if let Some(source) = script_key.source(&script_sources) {
|
||||||
src
|
match engine.compile(source) {
|
||||||
} else {
|
Ok(ast) => {
|
||||||
match script_sources.get(key) {
|
let defines = |n: &str, params: usize| {
|
||||||
Some(src) => src,
|
ast.iter_functions()
|
||||||
None => {
|
.any(|f| f.name == n && f.params.len() == params)
|
||||||
failed.insert(key.clone());
|
};
|
||||||
log_sink.error(format!("object references unknown script '{key}'"));
|
scripts.insert(
|
||||||
|
script_key.clone(),
|
||||||
|
CompiledScript {
|
||||||
|
has_init: defines("init", 1),
|
||||||
|
has_tick: defines("tick", 2),
|
||||||
|
has_bump: defines("bump", 2),
|
||||||
|
has_grab: defines("grab", 1),
|
||||||
|
has_enter: defines("enter", 2),
|
||||||
|
ast,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => { // It didn't compile...
|
||||||
|
failed.insert(script_key.clone());
|
||||||
|
log_sink.error(format!("script '{}' failed to compile: {err}", script_key.name()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
} else {
|
||||||
match engine.compile(source) {
|
// This has no source...
|
||||||
Ok(ast) => {
|
continue;
|
||||||
let defines = |n: &str, params: usize| {
|
|
||||||
ast.iter_functions()
|
|
||||||
.any(|f| f.name == n && f.params.len() == params)
|
|
||||||
};
|
|
||||||
scripts.insert(
|
|
||||||
key.clone(),
|
|
||||||
CompiledScript {
|
|
||||||
has_init: defines("init", 1),
|
|
||||||
has_tick: defines("tick", 2),
|
|
||||||
has_bump: defines("bump", 2),
|
|
||||||
has_grab: defines("grab", 1),
|
|
||||||
has_enter: defines("enter", 2),
|
|
||||||
ast,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
failed.insert(key.clone());
|
|
||||||
log_sink.error(format!("script '{key}' failed to compile: {err}"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// One runtime per object whose script compiled.
|
// One runtime per object whose script compiled.
|
||||||
for obj in all_scriptables.iter() {
|
for obj in all_scriptables.iter() {
|
||||||
if let Some(key) = obj.script_key() && scripts.contains_key(key) {
|
if let key = obj.script_key() && scripts.contains_key(key) {
|
||||||
scopes.insert(obj.id(), Scope::new());
|
scopes.insert(obj.id(), Scope::new());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -219,8 +214,7 @@ impl ScriptHost {
|
|||||||
fn run_hook_on_one(&mut self, hook: Hook, id: ObjectId, arg: Option<Dynamic>, dt: f64) -> Vec<BoardAction> {
|
fn run_hook_on_one(&mut self, hook: Hook, id: ObjectId, arg: Option<Dynamic>, dt: f64) -> Vec<BoardAction> {
|
||||||
let mut actions = Vec::new();
|
let mut actions = Vec::new();
|
||||||
if let Some(mut info) = ObjectInfo::from_id(id, self.board.clone()) {
|
if let Some(mut info) = ObjectInfo::from_id(id, self.board.clone()) {
|
||||||
if let Some(script_key) = info.script_name.as_ref()
|
if let Some(script) = self.scripts.get(&info.script_key)
|
||||||
&& let Some(script) = self.scripts.get(script_key)
|
|
||||||
&& let Some(scope) = self.scopes.get_mut(&id) {
|
&& let Some(scope) = self.scopes.get_mut(&id) {
|
||||||
|
|
||||||
// `tick` only fires on an object whose previous output has fully
|
// `tick` only fires on an object whose previous output has fully
|
||||||
@@ -241,7 +235,7 @@ impl ScriptHost {
|
|||||||
hook.to_str(),
|
hook.to_str(),
|
||||||
args,
|
args,
|
||||||
) {
|
) {
|
||||||
self.log_sink.error(format!("script '{}' {} error: {err}", script_key, hook));
|
self.log_sink.error(format!("script '{}' {} error: {err}", info.script_key.name(), hook));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Run the drain regardless of if we have the hook, otherwise
|
// Run the drain regardless of if we have the hook, otherwise
|
||||||
@@ -294,8 +288,7 @@ impl ScriptHost {
|
|||||||
/// a send action we've already seen this tick.
|
/// a send action we've already seen this tick.
|
||||||
pub(crate) fn run_send(&mut self, id: ObjectId, fn_name: &str, arg: SendArg) {
|
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(mut info) = ObjectInfo::from_id(id, self.board.clone()) {
|
||||||
if let Some(script_key) = info.script_name.as_ref()
|
if let Some(script) = self.scripts.get(&info.script_key)
|
||||||
&& let Some(script) = self.scripts.get(script_key)
|
|
||||||
&& let Some(scope) = self.scopes.get_mut(&id) {
|
&& let Some(scope) = self.scopes.get_mut(&id) {
|
||||||
|
|
||||||
// Find the function:
|
// Find the function:
|
||||||
@@ -309,7 +302,7 @@ impl ScriptHost {
|
|||||||
|
|
||||||
// If it's not there at all, just bail:
|
// If it's not there at all, just bail:
|
||||||
if arities.is_empty() {
|
if arities.is_empty() {
|
||||||
self.log_sink.error(format!("script '{}' send({}) error: function not found", script_key, fn_name));
|
self.log_sink.error(format!("script '{}' send({}) error: function not found", info.script_key.name(), fn_name));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +324,7 @@ impl ScriptHost {
|
|||||||
fn_name,
|
fn_name,
|
||||||
args,
|
args,
|
||||||
) {
|
) {
|
||||||
self.log_sink.error(format!("script '{}' send({}) error: {err}", script_key, fn_name));
|
self.log_sink.error(format!("script '{}' send({}) error: {err}", info.script_key.name(), fn_name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+42
-12
@@ -1,7 +1,8 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::{HashMap, HashSet};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::api::queue::ObjQueue;
|
use crate::api::queue::ObjQueue;
|
||||||
use crate::{Builtin, Direction};
|
use crate::{Builtin, Direction};
|
||||||
|
use crate::builtin::BUILTIN_SOURCES;
|
||||||
use crate::floor::{Floor, FloorBiome};
|
use crate::floor::{Floor, FloorBiome};
|
||||||
use crate::glyph::Glyph;
|
use crate::glyph::Glyph;
|
||||||
use crate::object_def::ObjectDef;
|
use crate::object_def::ObjectDef;
|
||||||
@@ -117,6 +118,38 @@ const fn default_as_below() -> DrawLayer {
|
|||||||
DrawLayer::Below
|
DrawLayer::Below
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, PartialEq, Eq, Clone, Debug, Default)]
|
||||||
|
pub enum ScriptKey {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
World(String),
|
||||||
|
Builtin(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScriptKey {
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
ScriptKey::None => "<none>",
|
||||||
|
ScriptKey::World(name) => name.as_str(),
|
||||||
|
ScriptKey::Builtin(name) => *name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source<'a>(&self, sources: &'a HashMap<String, String>) -> Option<&'a str> {
|
||||||
|
match self {
|
||||||
|
ScriptKey::None => None,
|
||||||
|
ScriptKey::World(name) => sources.get(name).map(String::as_str),
|
||||||
|
key @ ScriptKey::Builtin(_) => BUILTIN_SOURCES.get(key).copied(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Option<String>> for ScriptKey {
|
||||||
|
fn from(s: Option<String>) -> Self {
|
||||||
|
s.map_or(Self::None, |s| Self::World(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Everything an object-or-sensor needs to have a Rhai script attached.
|
/// Everything an object-or-sensor needs to have a Rhai script attached.
|
||||||
/// TODO clean this up some, especially the script_name-vs-builtin_script dichotomy
|
/// TODO clean this up some, especially the script_name-vs-builtin_script dichotomy
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
@@ -136,14 +169,13 @@ pub struct ScriptAttributes {
|
|||||||
/// or a synthetic `BUILTIN_*` name set when a script-backed archetype is
|
/// or a synthetic `BUILTIN_*` name set when a script-backed archetype is
|
||||||
/// expanded (see [`builtin_script`](ObjectDef::builtin_script)). `None` means
|
/// expanded (see [`builtin_script`](ObjectDef::builtin_script)). `None` means
|
||||||
/// this object has no script yet.
|
/// this object has no script yet.
|
||||||
pub script_name: Option<String>,
|
pub script_name: ScriptKey,
|
||||||
/// Embedded built-in script source, set when a script-backed archetype (e.g. a
|
/// Embedded built-in script source, set when a script-backed archetype (e.g. a
|
||||||
/// `pusher_*` or `gem`) is expanded into an object at load time (see
|
/// `pusher_*` or `gem`) is expanded into an object at load time (see
|
||||||
/// [`crate::builtin_scripts`]). When set, this is the object's script *source*
|
/// [`crate::builtin_scripts`]). When set, this is the object's script *source*
|
||||||
/// (its compile-key is the synthetic `BUILTIN_*` [`script_name`](ObjectDef::script_name)
|
/// (its compile-key is the synthetic `BUILTIN_*` [`script_name`](ObjectDef::script_name)
|
||||||
/// the same expansion assigns). Not part of the map file — it is regenerated
|
/// the same expansion assigns). Not part of the map file — it is regenerated
|
||||||
/// from the archetype on load.
|
/// from the archetype on load.
|
||||||
pub builtin_script: Option<&'static str>,
|
|
||||||
/// Open-ended string labels for this object. Serialized as a TOML array;
|
/// Open-ended string labels for this object. Serialized as a TOML array;
|
||||||
/// not subject to any rate limit — mutations take effect immediately after
|
/// not subject to any rate limit — mutations take effect immediately after
|
||||||
/// the frame's action queue is drained.
|
/// the frame's action queue is drained.
|
||||||
@@ -202,7 +234,7 @@ impl SensorSpec {
|
|||||||
optics: self.optics,
|
optics: self.optics,
|
||||||
name: self.name,
|
name: self.name,
|
||||||
tags: self.tags.into_iter().collect(),
|
tags: self.tags.into_iter().collect(),
|
||||||
script_name: self.script,
|
script_name: self.script.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,8 +266,8 @@ pub trait Hookable {
|
|||||||
/// for an expanded built-in (so identical built-ins share one compiled AST,
|
/// for an expanded built-in (so identical built-ins share one compiled AST,
|
||||||
/// while the source still comes from `builtin_script`). `None` if the object has
|
/// while the source still comes from `builtin_script`). `None` if the object has
|
||||||
/// no script.
|
/// no script.
|
||||||
fn script_key(&self) -> Option<&String> {
|
fn script_key(&self) -> &ScriptKey {
|
||||||
self.scriptable().script_name.as_ref()
|
&self.scriptable().script_name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,8 +378,7 @@ impl IntoTile for TileSpec {
|
|||||||
id: *next_object_id,
|
id: *next_object_id,
|
||||||
glyph,
|
glyph,
|
||||||
optics,
|
optics,
|
||||||
script_name: script,
|
script_name: script.into(),
|
||||||
builtin_script: None,
|
|
||||||
tags: tags.into_iter().collect(),
|
tags: tags.into_iter().collect(),
|
||||||
name,
|
name,
|
||||||
queue: ObjQueue::new(),
|
queue: ObjQueue::new(),
|
||||||
@@ -365,8 +396,7 @@ impl IntoTile for TileSpec {
|
|||||||
id: *next_object_id,
|
id: *next_object_id,
|
||||||
glyph: glyph.unwrap_or(builtin.default_glyph_for(variant)),
|
glyph: glyph.unwrap_or(builtin.default_glyph_for(variant)),
|
||||||
optics: builtin.optics(),
|
optics: builtin.optics(),
|
||||||
script_name: None,
|
script_name: builtin.script(),
|
||||||
builtin_script: Some(builtin.script()),
|
|
||||||
tags: HashSet::from([format!("BUILTIN_{}", variant)]),
|
tags: HashSet::from([format!("BUILTIN_{}", variant)]),
|
||||||
name: None,
|
name: None,
|
||||||
queue: ObjQueue::new(),
|
queue: ObjQueue::new(),
|
||||||
@@ -389,10 +419,10 @@ impl TileSpec {
|
|||||||
TileSpec::Builtin { kind: "wall".to_string(), glyph: None }
|
TileSpec::Builtin { kind: "wall".to_string(), glyph: None }
|
||||||
}
|
}
|
||||||
pub fn player() -> Self {
|
pub fn player() -> Self {
|
||||||
TileSpec::Builtin { kind: "wall".to_string(), glyph: None }
|
TileSpec::Player
|
||||||
}
|
}
|
||||||
pub fn krate() -> Self {
|
pub fn krate() -> Self {
|
||||||
TileSpec::Builtin { kind: "wall".to_string(), glyph: None }
|
TileSpec::Builtin { kind: "crate".to_string(), glyph: None }
|
||||||
}
|
}
|
||||||
pub fn gem() -> Self {
|
pub fn gem() -> Self {
|
||||||
TileSpec::Builtin { kind: "gem".to_string(), glyph: None }
|
TileSpec::Builtin { kind: "gem".to_string(), glyph: None }
|
||||||
|
|||||||
Reference in New Issue
Block a user