//! ## 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; use crate::action::BoardAction; use crate::object_def::ObjectDef; use crate::script::Registerable; 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, pub queue: ObjQueue } impl ObjectInfo { pub fn from_id(id: ObjectId, board: BoardRef) -> Option { 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() } } pub fn drain(&mut self, target: &mut Vec, dt: f64) { 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") .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) }); } }