2026-06-28 00:12:52 -05:00
|
|
|
//! ## Object Rhai API
|
|
|
|
|
//!
|
|
|
|
|
//! ### Getters
|
|
|
|
|
//!
|
|
|
|
|
//! - x, y -> Board location of object
|
|
|
|
|
//! - id -> The object's board-unique id
|
|
|
|
|
//! - name -> The object's name, or ()
|
|
|
|
|
//! - waiting -> bool for whether or not the front of this object's queue is a delay action
|
|
|
|
|
//! - queue -> the action queue for this object
|
|
|
|
|
//! - tags -> Array of tags for this object
|
|
|
|
|
//! - glyph -> The object's current glyph
|
|
|
|
|
//!
|
|
|
|
|
//! ### Functions
|
|
|
|
|
//!
|
|
|
|
|
//! - has_tag(tag) -> Whether the object has this tag
|
|
|
|
|
//! - delay(secs) -> Queue a delay action
|
|
|
|
|
//! - can_push(dir) -> Whether a push in this direction would succeed
|
|
|
|
|
//! - blocked(dir) -> Whether a solid neighbors me in this direction
|
|
|
|
|
//!
|
|
|
|
|
//! Note on blocking: can_push and blocked only take into account the contents of the board
|
|
|
|
|
//! at the start of the call; if another thing moves before your actions are flushed to the
|
|
|
|
|
//! gamestate, a move might still fail. This is a TODO.
|
|
|
|
|
|
|
|
|
|
use rhai::{Dynamic, Engine};
|
|
|
|
|
use crate::api::board::BoardRef;
|
|
|
|
|
use crate::api::queue::ObjQueue;
|
|
|
|
|
use crate::Direction;
|
2026-07-10 01:20:26 -05:00
|
|
|
use crate::action::BoardAction;
|
2026-06-28 00:12:52 -05:00
|
|
|
use crate::object_def::ObjectDef;
|
2026-07-10 01:20:26 -05:00
|
|
|
use crate::script::Registerable;
|
2026-06-28 00:12:52 -05:00
|
|
|
use crate::utils::{ErrorSink, ObjectId};
|
|
|
|
|
|
|
|
|
|
/// A snapshot of one board object, returned by `Board.tagged`, `Board.named`,
|
|
|
|
|
/// and `Board.get`. Passed by value — scripts read fields, not a live reference.
|
|
|
|
|
/// It also contains things like script_name used by ScriptHost, since we can't keep a live
|
|
|
|
|
/// borrow of the board while doing anything: we create one of these loose and then
|
|
|
|
|
/// it borrows the board only for the duration of what it needs.
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
|
pub struct ObjectInfo {
|
|
|
|
|
pub id: ObjectId,
|
|
|
|
|
pub x: i64,
|
|
|
|
|
pub y: i64,
|
|
|
|
|
pub board: BoardRef,
|
|
|
|
|
pub script_name: Option<String>,
|
|
|
|
|
pub queue: ObjQueue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ObjectInfo {
|
|
|
|
|
pub fn from_id(id: ObjectId, board: BoardRef) -> Option<ObjectInfo> {
|
|
|
|
|
let b = board.borrow();
|
|
|
|
|
let obj = b.objects.get(&id)?;
|
|
|
|
|
Some(ObjectInfo {
|
|
|
|
|
id,
|
|
|
|
|
x: obj.x as i64,
|
|
|
|
|
y: obj.y as i64,
|
|
|
|
|
board: board.clone(),
|
|
|
|
|
script_name: obj.script_name.clone(),
|
|
|
|
|
queue: obj.queue.clone()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn from_def(obj: &ObjectDef, board: BoardRef) -> ObjectInfo {
|
|
|
|
|
Self {
|
|
|
|
|
id: obj.id,
|
|
|
|
|
x: obj.x as i64,
|
|
|
|
|
y: obj.y as i64,
|
|
|
|
|
board: board.clone(),
|
|
|
|
|
script_name: obj.script_name.clone(),
|
|
|
|
|
queue: obj.queue.clone()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-10 01:20:26 -05:00
|
|
|
pub fn drain(&mut self, target: &mut Vec<BoardAction>, dt: f64) {
|
2026-06-28 00:12:52 -05:00
|
|
|
let mut b = self.board.borrow_mut();
|
|
|
|
|
if let Some(def) = b.objects.get_mut(&self.id) {
|
|
|
|
|
def.queue.drain(self.id, target, dt)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Registerable for ObjectInfo {
|
|
|
|
|
fn register(engine: &mut Engine, _error_sink: ErrorSink) {
|
|
|
|
|
engine.register_type_with_name::<ObjectInfo>("ObjectInfo")
|
|
|
|
|
.register_get("x", |obj: &mut ObjectInfo| obj.x)
|
|
|
|
|
.register_get("y", |obj: &mut ObjectInfo| obj.y)
|
|
|
|
|
.register_get("id", |obj: &mut ObjectInfo| obj.id as i64)
|
|
|
|
|
.register_get("waiting", |obj: &mut ObjectInfo| obj.queue.waiting())
|
|
|
|
|
.register_get("queue", |obj: &mut ObjectInfo| obj.queue.clone());
|
|
|
|
|
|
|
|
|
|
engine.register_get("name", |o: &mut ObjectInfo| {
|
|
|
|
|
let board = o.board.borrow();
|
|
|
|
|
let obj = board.objects.get(&o.id);
|
|
|
|
|
if let Some(ObjectDef { name: Some(name), ..}) = obj {
|
|
|
|
|
Dynamic::from(name.clone())
|
|
|
|
|
} else {
|
|
|
|
|
Dynamic::UNIT
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
engine.register_get("tags", |o: &mut ObjectInfo| -> rhai::Array {
|
|
|
|
|
let board = o.board.borrow();
|
|
|
|
|
let obj = board.objects.get(&o.id);
|
|
|
|
|
if let Some(ObjectDef { tags, .. }) = obj {
|
|
|
|
|
tags.iter().map(|t| Dynamic::from(t.clone())).collect()
|
|
|
|
|
} else {
|
|
|
|
|
rhai::Array::new()
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
engine.register_get("glyph", |o: &mut ObjectInfo| {
|
|
|
|
|
let board = o.board.borrow();
|
|
|
|
|
let obj = board.objects.get(&o.id).unwrap();
|
|
|
|
|
obj.glyph
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
engine.register_fn("has_tag", |o: &mut ObjectInfo, t: String| {
|
|
|
|
|
if let Some(ObjectDef { tags, .. }) = o.board.borrow().objects.get(&o.id) {
|
|
|
|
|
tags.contains(&t)
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
engine.register_fn("delay", |o: &mut ObjectInfo, dt: f64| o.queue.delay(dt));
|
|
|
|
|
|
|
|
|
|
engine.register_fn("can_push", move |o: &mut ObjectInfo, dir: Direction| {
|
|
|
|
|
let board = o.board.borrow();
|
|
|
|
|
board.in_bounds((o.x, o.y)) && board.can_push(o.x as usize, o.y as usize, dir)
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
engine.register_fn("blocked", move |o: &mut ObjectInfo, dir: Direction| -> bool {
|
|
|
|
|
let board = o.board.borrow();
|
|
|
|
|
let tx = o.x + dir.dx();
|
|
|
|
|
let ty = o.y + dir.dy();
|
|
|
|
|
board.in_bounds((o.x, o.y)) && !board.is_passable(tx as usize, ty as usize)
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|