portals 1
This commit is contained in:
+59
-2
@@ -19,6 +19,7 @@
|
||||
//! | `Board` | `Board` | Read-only world handle. |
|
||||
//! | `Queue` | `Queue` | This object's pending-action queue. |
|
||||
//! | `Me` | `Me` | Self-reference (id, name, x, y, tags, glyph). |
|
||||
//! | `Registry` | `Registry` | Board-scoped key→value store persisting across board transitions. |
|
||||
//!
|
||||
//! Direction and color constants are registered as a global Rhai module and
|
||||
//! are visible to all functions at any call depth, including Rhai-to-Rhai calls:
|
||||
@@ -46,7 +47,7 @@
|
||||
//!
|
||||
//! `move(dir)`, `delay(secs)`, `now()`, `set_tile(n)`, `log(msg)`, `say(msg)`,
|
||||
//! `set_fg(fg)`, `set_bg(bg)`, `set_color(fg, bg)`, `set_tag(target, tag, present)`,
|
||||
//! `send(target_id, fn_name [, arg])`
|
||||
//! `send(target_id, fn_name [, arg])`, `teleport(x, y)`
|
||||
//!
|
||||
//! ## Queue API
|
||||
//!
|
||||
@@ -57,7 +58,7 @@ use crate::game::SAY_DURATION;
|
||||
use crate::board::Board;
|
||||
use crate::log::LogLine;
|
||||
use crate::map_file::{color_to_hex, parse_color};
|
||||
use crate::utils::{Direction, ObjectId, ScriptArg};
|
||||
use crate::utils::{Direction, ObjectId, RegistryValue, ScriptArg};
|
||||
use rhai::{Array, CallFnOptions, Dynamic, Engine, FuncArgs, ImmutableString, Module, NativeCallContext, Scope, AST};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
@@ -133,6 +134,15 @@ struct Me {
|
||||
board: BoardRef,
|
||||
}
|
||||
|
||||
/// The board's script registry, pushed into scope as the constant `Registry`.
|
||||
/// `get`/`set`/`get_or` methods let scripts read and write per-board key→value pairs
|
||||
/// that persist across board transitions. Mutation goes through the inner
|
||||
/// `Rc<RefCell<Board>>`, so the struct itself can be shared as a scope constant.
|
||||
#[derive(Clone)]
|
||||
struct Registry {
|
||||
board: BoardRef,
|
||||
}
|
||||
|
||||
// ── ObjectInfo helper ─────────────────────────────────────────────────────────
|
||||
|
||||
fn object_info_from(id: ObjectId, board: &Board) -> Option<ObjectInfo> {
|
||||
@@ -187,6 +197,7 @@ impl ScriptHost {
|
||||
register_me_type(&mut engine);
|
||||
register_object_info_type(&mut engine);
|
||||
register_glyph_type(&mut engine);
|
||||
register_registry_type(&mut engine);
|
||||
register_global_constants(&mut engine);
|
||||
|
||||
let board = board_cell.borrow();
|
||||
@@ -449,6 +460,43 @@ fn register_me_type(engine: &mut Engine) {
|
||||
});
|
||||
}
|
||||
|
||||
// ── Register Registry type ────────────────────────────────────────────────────
|
||||
|
||||
fn register_registry_type(engine: &mut Engine) {
|
||||
engine.register_type_with_name::<Registry>("Registry");
|
||||
|
||||
// Registry.get(key) -> Dynamic — returns () if the key is absent.
|
||||
engine.register_fn("get", |r: &mut Registry, key: ImmutableString| -> Dynamic {
|
||||
r.board.borrow()
|
||||
.registry
|
||||
.get(key.as_str())
|
||||
.map(|v| v.clone().into())
|
||||
.unwrap_or(Dynamic::UNIT)
|
||||
});
|
||||
|
||||
// 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
|
||||
}
|
||||
});
|
||||
|
||||
// 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
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ── Register ObjectInfo type ──────────────────────────────────────────────────
|
||||
|
||||
fn register_object_info_type(engine: &mut Engine) {
|
||||
@@ -725,6 +773,14 @@ fn register_write_api(engine: &mut Engine, queues: &QueueMap) {
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
// teleport(x, y): move the calling object to an arbitrary cell. Zero time cost.
|
||||
// Blocked (with an error logged at resolve time) if the object is solid and the
|
||||
// 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 });
|
||||
});
|
||||
}
|
||||
|
||||
// ── Queue API ─────────────────────────────────────────────────────────────────
|
||||
@@ -790,6 +846,7 @@ fn new_object_scope(board: &BoardRef, queue: &ObjQueue, object_id: ObjectId) ->
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user