wip 4 remove enterresponse

This commit is contained in:
2026-08-11 01:33:15 -05:00
parent 1872db340c
commit 1a4d894336
6 changed files with 43 additions and 116 deletions
+6 -6
View File
@@ -6,7 +6,7 @@ use crate::utils::{Direction, Point};
use crate::utils::{ObjectId, RegistryValue}; use crate::utils::{ObjectId, RegistryValue};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use crate::portal::Portal; use crate::portal::Portal;
use crate::tile::{DrawLayer, EnterResponse, Hookable, IntoTile, LocatedObject, ScriptAttributes, Sensor, Tile, TileSpec}; use crate::tile::{DrawLayer, Hookable, IntoTile, LocatedObject, ScriptAttributes, Sensor, Tile, TileSpec};
/// The complete state of one game board (a single room or screen). /// The complete state of one game board (a single room or screen).
/// ///
@@ -502,7 +502,7 @@ pub(crate) mod tests {
use std::collections::HashMap; use std::collections::HashMap;
use crate::object_def::ObjectDef; use crate::object_def::ObjectDef;
use crate::tile::{ use crate::tile::{
DrawLayer, EnterResponse, IntoTile, Optics, ScriptAttributes, Sensor, SensorSpec, Tile, DrawLayer, IntoTile, Optics, ScriptAttributes, Sensor, SensorSpec, Tile,
TileSpec, TileSpec,
}; };
@@ -583,11 +583,11 @@ pub(crate) mod tests {
x: usize, x: usize,
y: usize, y: usize,
script: &str, script: &str,
enter: EnterResponse, mobile: bool,
) -> ObjectId { ) -> ObjectId {
let tile = TileSpec::Object { let tile = TileSpec::Object {
script: Some(script.to_string()), script: Some(script.to_string()),
enter, mobile,
glyph: ObjectDef::default_glyph(), glyph: ObjectDef::default_glyph(),
optics: Optics { opaque: true, glow: 0 }, optics: Optics { opaque: true, glow: 0 },
name: None, name: None,
@@ -612,11 +612,11 @@ pub(crate) mod tests {
board: &mut Board, board: &mut Board,
x: usize, x: usize,
y: usize, y: usize,
enter: EnterResponse, mobile: bool,
) -> ObjectId { ) -> ObjectId {
let tile = TileSpec::Object { let tile = TileSpec::Object {
script: None, script: None,
enter, mobile,
glyph: ObjectDef::default_glyph(), glyph: ObjectDef::default_glyph(),
optics: Optics { opaque: true, glow: 0 }, optics: Optics { opaque: true, glow: 0 },
name: None, name: None,
+15 -16
View File
@@ -5,7 +5,7 @@ use color::Rgba8;
use lazy_static::lazy_static; 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, ScriptKey}; use crate::tile::{Optics, ScriptKey};
/// Declares the set of script-backed archetype families. /// Declares the set of script-backed archetype families.
/// ///
@@ -27,7 +27,7 @@ macro_rules! builtins {
( (
$( $(
$variant:ident => [ $( $name:literal => $glyph:expr ),+ $(,)? ] { $variant:ident => [ $( $name:literal => $glyph:expr ),+ $(,)? ] {
enter: $enter:expr, mobile: $mobile:expr,
optics: $optics:expr, optics: $optics:expr,
script: $script:expr $(,)? script: $script:expr $(,)?
} }
@@ -63,10 +63,10 @@ macro_rules! builtins {
} }
} }
/// Returns the uniform enter response shared by all aliases in this family. /// Returns the uniform mobility flag shared by all aliases in this family.
pub fn enter_response(self) -> EnterResponse { pub fn mobile(self) -> bool {
match self { match self {
$( Builtin::$variant => $enter, )+ $( Builtin::$variant => $mobile, )+
} }
} }
@@ -112,12 +112,12 @@ const fn g(tile: char, r: u8, gr: u8, b: u8) -> Glyph {
builtins! { builtins! {
Gem => ["gem" => g('♦', 0x50, 0x50, 0xFF)] { Gem => ["gem" => g('♦', 0x50, 0x50, 0xFF)] {
enter: EnterResponse::Block, mobile: true,
optics: Optics { opaque: false, glow: 0 }, optics: Optics { opaque: false, glow: 0 },
script: ScriptKey::Builtin("gem"), script: ScriptKey::Builtin("gem"),
}, },
Heart => ["heart" => g('♡', 0xCC, 0x22, 0x22)] { Heart => ["heart" => g('♡', 0xCC, 0x22, 0x22)] {
enter: EnterResponse::Block, mobile: true,
optics: Optics { opaque: false, glow: 0 }, optics: Optics { opaque: false, glow: 0 },
script: ScriptKey::Builtin("heart"), script: ScriptKey::Builtin("heart"),
}, },
@@ -127,7 +127,7 @@ builtins! {
"pusher_east" => g('►', 0xAA, 0xAA, 0xAA), "pusher_east" => g('►', 0xAA, 0xAA, 0xAA),
"pusher_west" => g('◄', 0xAA, 0xAA, 0xAA), "pusher_west" => g('◄', 0xAA, 0xAA, 0xAA),
] { ] {
enter: EnterResponse::Block, mobile: true,
optics: Optics { opaque: true, glow: 0 }, optics: Optics { opaque: true, glow: 0 },
script: ScriptKey::Builtin("pusher"), script: ScriptKey::Builtin("pusher"),
}, },
@@ -135,7 +135,7 @@ builtins! {
"spinner_cw" => g('/', 0xAA, 0xAA, 0xAA), "spinner_cw" => g('/', 0xAA, 0xAA, 0xAA),
"spinner_ccw" => g('\\', 0xAA, 0xAA, 0xAA), "spinner_ccw" => g('\\', 0xAA, 0xAA, 0xAA),
] { ] {
enter: EnterResponse::Block, mobile: false,
optics: Optics { opaque: true, glow: 0 }, optics: Optics { opaque: true, glow: 0 },
script: ScriptKey::Builtin("spinner"), script: ScriptKey::Builtin("spinner"),
}, },
@@ -147,7 +147,7 @@ builtins! {
"transporter_east" => g(')', 0x55, 0xFF, 0xFF), // ')' "transporter_east" => g(')', 0x55, 0xFF, 0xFF), // ')'
"transporter_west" => g('(', 0x55, 0xFF, 0xFF), // '(' "transporter_west" => g('(', 0x55, 0xFF, 0xFF), // '('
] { ] {
enter: EnterResponse::Block, mobile: false,
optics: Optics { opaque: false, glow: 0 }, optics: Optics { opaque: false, glow: 0 },
script: ScriptKey::Builtin("transporter"), script: ScriptKey::Builtin("transporter"),
}, },
@@ -161,7 +161,7 @@ builtins! {
"key_yellow" => KeyType::Yellow.glyph(), "key_yellow" => KeyType::Yellow.glyph(),
"key_white" => KeyType::White.glyph(), "key_white" => KeyType::White.glyph(),
] { ] {
enter: EnterResponse::Grab, mobile: true,
optics: Optics { opaque: false, glow: 0 }, optics: Optics { opaque: false, glow: 0 },
script: ScriptKey::Builtin("key"), script: ScriptKey::Builtin("key"),
}, },
@@ -169,23 +169,22 @@ builtins! {
tile: '#', tile: '#',
fg: Rgba8 { r: 0x80, g: 0x80, b: 0x80, a: 255 }, fg: Rgba8 { r: 0x80, g: 0x80, b: 0x80, a: 255 },
bg: Rgba8 { r: 0x60, g: 0x60, b: 0x60, a: 255 }}] { bg: Rgba8 { r: 0x60, g: 0x60, b: 0x60, a: 255 }}] {
enter: EnterResponse::Block, mobile: false,
optics: Optics { opaque: true, glow: 0 }, optics: Optics { opaque: true, glow: 0 },
script: ScriptKey::None script: ScriptKey::None
}, },
Crate => ["crate" => g('■', 0xaa, 0xaa, 0xaa)] { // CP437 ■ (small filled square) Crate => ["crate" => g('■', 0xaa, 0xaa, 0xaa)] { // CP437 ■ (small filled square)
//enter: EnterResponse::Push(Pushable::Any), mobile: true,
enter: EnterResponse::Block,
optics: Optics { opaque: true, glow: 0 }, optics: Optics { opaque: true, glow: 0 },
script: ScriptKey::Builtin("crate") script: ScriptKey::Builtin("crate")
}, },
HCrate => ["hcrate" => g('↔', 0xaa, 0xaa, 0xaa)] { // CP437 ↔ (left-right arrow) — pushable east/west HCrate => ["hcrate" => g('↔', 0xaa, 0xaa, 0xaa)] { // CP437 ↔ (left-right arrow) — pushable east/west
enter: EnterResponse::Push(Pushable::Horizontal), mobile: true,
optics: Optics { opaque: true, glow: 0 }, optics: Optics { opaque: true, glow: 0 },
script: ScriptKey::Builtin("hcrate") script: ScriptKey::Builtin("hcrate")
}, },
VCrate => ["vcrate" => g('↕', 0xaa, 0xaa, 0xaa)] { // CP437 ↕ (up-down arrow) — pushable north/south VCrate => ["vcrate" => g('↕', 0xaa, 0xaa, 0xaa)] { // CP437 ↕ (up-down arrow) — pushable north/south
enter: EnterResponse::Push(Pushable::Vertical), mobile: true,
optics: Optics { opaque: true, glow: 0 }, optics: Optics { opaque: true, glow: 0 },
script: ScriptKey::Builtin("vcrate") script: ScriptKey::Builtin("vcrate")
}, },
+2 -4
View File
@@ -2,11 +2,9 @@ use crate::action::{apply_shift, apply_teleport, Action, BoardAction, SendArg};
use crate::board::Board; use crate::board::Board;
use crate::log::LogLine; use crate::log::LogLine;
use crate::script::ScriptHost; use crate::script::ScriptHost;
use crate::utils::{Direction, ObjectId, Point, Pushable}; use crate::utils::{Direction, ObjectId, Point};
use crate::world::World; use crate::world::World;
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
use std::collections::{BTreeSet, HashSet, VecDeque};
use std::fmt::format;
use std::hash::Hash; use std::hash::Hash;
use std::time::Duration; use std::time::Duration;
@@ -18,7 +16,7 @@ pub const SAY_DURATION: f64 = 3.0;
pub use crate::action::ScrollLine; pub use crate::action::ScrollLine;
use crate::player::{Player, PlayerRef}; use crate::player::{Player, PlayerRef};
use crate::portal::Portal; use crate::portal::Portal;
use crate::tile::{EnterResponse, Tile}; use crate::tile::Tile;
/// An active scroll overlay opened by a scripted object via `scroll()`. /// An active scroll overlay opened by a scripted object via `scroll()`.
/// ///
+5 -5
View File
@@ -1,7 +1,7 @@
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use crate::glyph::Glyph; use crate::glyph::Glyph;
use color::Rgba8; use color::Rgba8;
use crate::tile::{EnterResponse, Optics, ScriptAttributes}; use crate::tile::{Optics, ScriptAttributes};
/// A scripted object placed on the board, loaded from a map file. /// A scripted object placed on the board, loaded from a map file.
/// ///
@@ -24,8 +24,8 @@ use crate::tile::{EnterResponse, Optics, ScriptAttributes};
/// [`GameState::tick`]. Other event hooks (touch, shoot, …) are future work. /// [`GameState::tick`]. Other event hooks (touch, shoot, …) are future work.
#[derive(Clone)] #[derive(Clone)]
pub struct ObjectDef { pub struct ObjectDef {
/// How this object affects movement /// Can this be moved _by other objects_
pub enter_response: EnterResponse, pub mobile: bool,
/// Everything we need to run our script /// Everything we need to run our script
pub scripting: ScriptAttributes, pub scripting: ScriptAttributes,
} }
@@ -49,12 +49,12 @@ impl ObjectDef {
/// Creates a new object with default glyph and blocking behavior. /// Creates a new object with default glyph and blocking behavior.
/// ///
/// Defaults: block on enter, opaque no glow, no script /// Defaults: non-mobile, opaque no glow, no script
/// These match the serde defaults in the map file format so new objects /// These match the serde defaults in the map file format so new objects
/// round-trip correctly. /// round-trip correctly.
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
enter_response: EnterResponse::Block, mobile: false,
scripting: ScriptAttributes { scripting: ScriptAttributes {
id: 0, id: 0,
glyph: Self::default_glyph(), glyph: Self::default_glyph(),
+8 -78
View File
@@ -1,77 +1,12 @@
use std::collections::{HashMap, 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;
use crate::builtin::BUILTIN_SOURCES; 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;
use crate::utils::{ObjectId, Point, Pushable}; use crate::utils::{ObjectId, Point};
use crate::utils::Pushable::No;
/// The various ways that a tile might respond to another tile trying to move on top of it
#[derive(Serialize, Deserialize, PartialEq, Copy, Clone, Debug)]
#[serde(rename_all = "lowercase")]
pub enum EnterResponse {
/// Flat denial: don't let the move happen, block it.
Block,
/// If the player moves, call the `grab` hook and then remove this object. Anything else, act as
/// `Pushable::Any`.
/// TODO will do something different once inventory exists as a concept
Grab,
/// Attempt to exit the cell ourselves, the opposite direction. This of course recurses; if our move
/// is denied then this is the same as a block.
Push(Pushable),
/// Call an `enter` hook and do something. The hook is responsible for resolving the conflict:
/// placing the moving-object somewhere else, moving ourselves somewhere else, or whatever.
/// It should return `true` or `false`: this return value is passed back up a `Push` chain to
/// the original moving object, determining whether the moves should happen.
/// Example: a teleporter moves anything that enters it to another spot, if it's unblocked. It
/// will check if the target cell is open and move the object there if it is, returning true.
/// If you push a crate into it, it returning true means you should move (into the space the crate
/// left behind); it returning false means the crate wasn't teleported so your move is blocked
/// also.
/// The hook return value is only used for _other_ things in the chain: regardless of what's returned,
/// the engine won't touch the crate or the teleporter; the hook is responsible for handling the
/// actual collision. A teleporter that teleports the crate and returns false will leave an empty
/// space behind (the player won't move into it because it returned false); a teleporter that
/// returns true without teleporting the crate, the crate will be destroyed by the player moving
/// on top of it.
Hook,
/// Swap places with whatever moved on top of you, as long as it was the player. Anything else,
/// act as `Pushable::Any`.
Swap,
/// Get overwritten and destroyed by whatever moved on top of us. Right now, equivalent to `Grab`
/// if the hook does nothing (but in the future `Grab` will have other builtin behavior)
Squish
}
impl EnterResponse {
/// Returns whether this object will transmit a push of the given direction through it:
/// - `Push(p)`, if p allows that direction
/// - `Grab`, because grabbable things act like push if something pushes them
/// - `Swap`, same reason
pub fn transmits_push(self, dir: Direction) -> bool {
match self {
EnterResponse::Grab | EnterResponse::Swap => true,
EnterResponse::Push(p) if p.allows(dir) => true,
_ => false
}
}
/// Returns whether this object will sense a bump from the given direction:
/// - `Push(p)`, if p disallows that direction
/// - `Block`, because nothing can move through it
/// - `Hook`, because it's obligated to handle the entry and not pass it on
/// - but nothing else
pub fn bumpable(self, dir: Direction) -> bool {
match self {
EnterResponse::Block | EnterResponse::Hook => true,
EnterResponse::Push(p) if !p.allows(dir) => true,
_ => false
}
}
}
/// Where `Sensor`s are drawn, in relation to the grid: /// Where `Sensor`s are drawn, in relation to the grid:
/// - `Above` is above everything, including the player. If the glyph has a nonzero tile, and it's /// - `Above` is above everything, including the player. If the glyph has a nonzero tile, and it's
@@ -323,13 +258,7 @@ impl Tile {
pub fn shiftable(&self) -> bool { pub fn shiftable(&self) -> bool {
match self { match self {
Tile::Player => true, // Player will shift anywhere Tile::Player => true, // Player will shift anywhere
Tile::Object(obj) => { Tile::Object(obj) => obj.mobile
match obj.enter_response {
EnterResponse::Push(p) => p != No,
EnterResponse::Hook | EnterResponse::Block => false, // Blockers never shift
EnterResponse::Grab | EnterResponse::Swap | EnterResponse::Squish => true,
}
}
} }
} }
} }
@@ -342,7 +271,8 @@ pub enum TileSpec {
Object { Object {
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
script: Option<String>, script: Option<String>,
enter: EnterResponse, #[serde(default = "default_as_true")]
mobile: bool,
#[serde(flatten)] #[serde(flatten)]
glyph: Glyph, glyph: Glyph,
#[serde(flatten, default)] #[serde(flatten, default)]
@@ -375,9 +305,9 @@ impl IntoTile for TileSpec {
// }; // };
// Ok(Tile::Portal(Box::new(def))) // Ok(Tile::Portal(Box::new(def)))
// } // }
TileSpec::Object { script, enter, glyph, optics, name, tags } => { TileSpec::Object { script, mobile, glyph, optics, name, tags } => {
let def = ObjectDef { let def = ObjectDef {
enter_response: enter, mobile,
scripting: ScriptAttributes { scripting: ScriptAttributes {
id: *next_object_id, id: *next_object_id,
glyph, glyph,
@@ -395,7 +325,7 @@ impl IntoTile for TileSpec {
TileSpec::Builtin { kind, glyph } => { TileSpec::Builtin { kind, glyph } => {
if let Some((builtin, variant)) = Builtin::from_name(&kind) { if let Some((builtin, variant)) = Builtin::from_name(&kind) {
let def = ObjectDef { let def = ObjectDef {
enter_response: builtin.enter_response(), mobile: builtin.mobile(),
scripting: ScriptAttributes { scripting: ScriptAttributes {
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)),
+7 -7
View File
@@ -243,7 +243,7 @@ grid = """
[boards.start.palette.V] [boards.start.palette.V]
type = "object" type = "object"
script = "mover" script = "mover"
enter = "block" mobile = false
tile = 1 tile = 1
fg = "#33aa33" fg = "#33aa33"
bg = "#000000" bg = "#000000"
@@ -252,7 +252,7 @@ bg = "#000000"
type = "object" type = "object"
script = "muffin" script = "muffin"
name = "muffin" name = "muffin"
enter = "block" mobile = true
tile = 15 tile = 15
fg = "#ffdd88" fg = "#ffdd88"
bg = "#000000" bg = "#000000"
@@ -261,7 +261,7 @@ bg = "#000000"
type = "object" type = "object"
script = "noticeboard" script = "noticeboard"
name = "noticeboard" name = "noticeboard"
enter = "block" mobile = false
tile = 240 tile = 240
fg = "#cc9944" fg = "#cc9944"
bg = "#000000" bg = "#000000"
@@ -270,7 +270,7 @@ bg = "#000000"
type = "object" type = "object"
script = "shifter" script = "shifter"
name = "shifter" name = "shifter"
enter = "block" mobile = false
tile = 240 tile = 240
fg = "#cc9944" fg = "#cc9944"
bg = "#000000" bg = "#000000"
@@ -345,7 +345,7 @@ grid = """
type = "object" type = "object"
script = "bookshelf" script = "bookshelf"
name = "bookshelf" name = "bookshelf"
enter = "block" mobile = false
tile = 240 tile = 240
fg = "#aa7733" fg = "#aa7733"
bg = "#3d1c00" bg = "#3d1c00"
@@ -354,7 +354,7 @@ bg = "#3d1c00"
type = "object" type = "object"
script = "fireplace" script = "fireplace"
name = "fireplace" name = "fireplace"
enter = "block" mobile = false
tile = 15 tile = 15
fg = "#ff8800" fg = "#ff8800"
bg = "#220000" bg = "#220000"
@@ -363,7 +363,7 @@ bg = "#220000"
type = "object" type = "object"
script = "chest" script = "chest"
name = "chest" name = "chest"
enter = "block" mobile = true
tile = 240 tile = 240
fg = "#ccaa44" fg = "#ccaa44"
bg = "#222200" bg = "#222200"