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 std::collections::{HashMap, HashSet};
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).
///
@@ -502,7 +502,7 @@ pub(crate) mod tests {
use std::collections::HashMap;
use crate::object_def::ObjectDef;
use crate::tile::{
DrawLayer, EnterResponse, IntoTile, Optics, ScriptAttributes, Sensor, SensorSpec, Tile,
DrawLayer, IntoTile, Optics, ScriptAttributes, Sensor, SensorSpec, Tile,
TileSpec,
};
@@ -583,11 +583,11 @@ pub(crate) mod tests {
x: usize,
y: usize,
script: &str,
enter: EnterResponse,
mobile: bool,
) -> ObjectId {
let tile = TileSpec::Object {
script: Some(script.to_string()),
enter,
mobile,
glyph: ObjectDef::default_glyph(),
optics: Optics { opaque: true, glow: 0 },
name: None,
@@ -612,11 +612,11 @@ pub(crate) mod tests {
board: &mut Board,
x: usize,
y: usize,
enter: EnterResponse,
mobile: bool,
) -> ObjectId {
let tile = TileSpec::Object {
script: None,
enter,
mobile,
glyph: ObjectDef::default_glyph(),
optics: Optics { opaque: true, glow: 0 },
name: None,
+15 -16
View File
@@ -5,7 +5,7 @@ use color::Rgba8;
use lazy_static::lazy_static;
use crate::keys::KeyType;
use serde::{Serialize, Deserialize};
use crate::tile::{EnterResponse, Optics, ScriptKey};
use crate::tile::{Optics, ScriptKey};
/// Declares the set of script-backed archetype families.
///
@@ -27,7 +27,7 @@ macro_rules! builtins {
(
$(
$variant:ident => [ $( $name:literal => $glyph:expr ),+ $(,)? ] {
enter: $enter:expr,
mobile: $mobile:expr,
optics: $optics:expr,
script: $script:expr $(,)?
}
@@ -63,10 +63,10 @@ macro_rules! builtins {
}
}
/// Returns the uniform enter response shared by all aliases in this family.
pub fn enter_response(self) -> EnterResponse {
/// Returns the uniform mobility flag shared by all aliases in this family.
pub fn mobile(self) -> bool {
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! {
Gem => ["gem" => g('♦', 0x50, 0x50, 0xFF)] {
enter: EnterResponse::Block,
mobile: true,
optics: Optics { opaque: false, glow: 0 },
script: ScriptKey::Builtin("gem"),
},
Heart => ["heart" => g('♡', 0xCC, 0x22, 0x22)] {
enter: EnterResponse::Block,
mobile: true,
optics: Optics { opaque: false, glow: 0 },
script: ScriptKey::Builtin("heart"),
},
@@ -127,7 +127,7 @@ builtins! {
"pusher_east" => g('►', 0xAA, 0xAA, 0xAA),
"pusher_west" => g('◄', 0xAA, 0xAA, 0xAA),
] {
enter: EnterResponse::Block,
mobile: true,
optics: Optics { opaque: true, glow: 0 },
script: ScriptKey::Builtin("pusher"),
},
@@ -135,7 +135,7 @@ builtins! {
"spinner_cw" => g('/', 0xAA, 0xAA, 0xAA),
"spinner_ccw" => g('\\', 0xAA, 0xAA, 0xAA),
] {
enter: EnterResponse::Block,
mobile: false,
optics: Optics { opaque: true, glow: 0 },
script: ScriptKey::Builtin("spinner"),
},
@@ -147,7 +147,7 @@ builtins! {
"transporter_east" => g(')', 0x55, 0xFF, 0xFF), // ')'
"transporter_west" => g('(', 0x55, 0xFF, 0xFF), // '('
] {
enter: EnterResponse::Block,
mobile: false,
optics: Optics { opaque: false, glow: 0 },
script: ScriptKey::Builtin("transporter"),
},
@@ -161,7 +161,7 @@ builtins! {
"key_yellow" => KeyType::Yellow.glyph(),
"key_white" => KeyType::White.glyph(),
] {
enter: EnterResponse::Grab,
mobile: true,
optics: Optics { opaque: false, glow: 0 },
script: ScriptKey::Builtin("key"),
},
@@ -169,23 +169,22 @@ builtins! {
tile: '#',
fg: Rgba8 { r: 0x80, g: 0x80, b: 0x80, a: 255 },
bg: Rgba8 { r: 0x60, g: 0x60, b: 0x60, a: 255 }}] {
enter: EnterResponse::Block,
mobile: false,
optics: Optics { opaque: true, glow: 0 },
script: ScriptKey::None
},
Crate => ["crate" => g('■', 0xaa, 0xaa, 0xaa)] { // CP437 ■ (small filled square)
//enter: EnterResponse::Push(Pushable::Any),
enter: EnterResponse::Block,
mobile: true,
optics: Optics { opaque: true, glow: 0 },
script: ScriptKey::Builtin("crate")
},
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 },
script: ScriptKey::Builtin("hcrate")
},
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 },
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::log::LogLine;
use crate::script::ScriptHost;
use crate::utils::{Direction, ObjectId, Point, Pushable};
use crate::utils::{Direction, ObjectId, Point};
use crate::world::World;
use std::cell::{Ref, RefMut};
use std::collections::{BTreeSet, HashSet, VecDeque};
use std::fmt::format;
use std::hash::Hash;
use std::time::Duration;
@@ -18,7 +16,7 @@ pub const SAY_DURATION: f64 = 3.0;
pub use crate::action::ScrollLine;
use crate::player::{Player, PlayerRef};
use crate::portal::Portal;
use crate::tile::{EnterResponse, Tile};
use crate::tile::Tile;
/// 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 crate::glyph::Glyph;
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.
///
@@ -24,8 +24,8 @@ use crate::tile::{EnterResponse, Optics, ScriptAttributes};
/// [`GameState::tick`]. Other event hooks (touch, shoot, …) are future work.
#[derive(Clone)]
pub struct ObjectDef {
/// How this object affects movement
pub enter_response: EnterResponse,
/// Can this be moved _by other objects_
pub mobile: bool,
/// Everything we need to run our script
pub scripting: ScriptAttributes,
}
@@ -49,12 +49,12 @@ impl ObjectDef {
/// 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
/// round-trip correctly.
pub fn new() -> Self {
Self {
enter_response: EnterResponse::Block,
mobile: false,
scripting: ScriptAttributes {
id: 0,
glyph: Self::default_glyph(),
+8 -78
View File
@@ -1,77 +1,12 @@
use std::collections::{HashMap, HashSet};
use serde::{Deserialize, Serialize};
use crate::api::queue::ObjQueue;
use crate::{Builtin, Direction};
use crate::Builtin;
use crate::builtin::BUILTIN_SOURCES;
use crate::floor::{Floor, FloorBiome};
use crate::glyph::Glyph;
use crate::object_def::ObjectDef;
use crate::utils::{ObjectId, Point, Pushable};
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
}
}
}
use crate::utils::{ObjectId, Point};
/// 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
@@ -323,13 +258,7 @@ impl Tile {
pub fn shiftable(&self) -> bool {
match self {
Tile::Player => true, // Player will shift anywhere
Tile::Object(obj) => {
match obj.enter_response {
EnterResponse::Push(p) => p != No,
EnterResponse::Hook | EnterResponse::Block => false, // Blockers never shift
EnterResponse::Grab | EnterResponse::Swap | EnterResponse::Squish => true,
}
}
Tile::Object(obj) => obj.mobile
}
}
}
@@ -342,7 +271,8 @@ pub enum TileSpec {
Object {
#[serde(default, skip_serializing_if = "Option::is_none")]
script: Option<String>,
enter: EnterResponse,
#[serde(default = "default_as_true")]
mobile: bool,
#[serde(flatten)]
glyph: Glyph,
#[serde(flatten, default)]
@@ -375,9 +305,9 @@ impl IntoTile for TileSpec {
// };
// 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 {
enter_response: enter,
mobile,
scripting: ScriptAttributes {
id: *next_object_id,
glyph,
@@ -395,7 +325,7 @@ impl IntoTile for TileSpec {
TileSpec::Builtin { kind, glyph } => {
if let Some((builtin, variant)) = Builtin::from_name(&kind) {
let def = ObjectDef {
enter_response: builtin.enter_response(),
mobile: builtin.mobile(),
scripting: ScriptAttributes {
id: *next_object_id,
glyph: glyph.unwrap_or(builtin.default_glyph_for(variant)),
+7 -7
View File
@@ -243,7 +243,7 @@ grid = """
[boards.start.palette.V]
type = "object"
script = "mover"
enter = "block"
mobile = false
tile = 1
fg = "#33aa33"
bg = "#000000"
@@ -252,7 +252,7 @@ bg = "#000000"
type = "object"
script = "muffin"
name = "muffin"
enter = "block"
mobile = true
tile = 15
fg = "#ffdd88"
bg = "#000000"
@@ -261,7 +261,7 @@ bg = "#000000"
type = "object"
script = "noticeboard"
name = "noticeboard"
enter = "block"
mobile = false
tile = 240
fg = "#cc9944"
bg = "#000000"
@@ -270,7 +270,7 @@ bg = "#000000"
type = "object"
script = "shifter"
name = "shifter"
enter = "block"
mobile = false
tile = 240
fg = "#cc9944"
bg = "#000000"
@@ -345,7 +345,7 @@ grid = """
type = "object"
script = "bookshelf"
name = "bookshelf"
enter = "block"
mobile = false
tile = 240
fg = "#aa7733"
bg = "#3d1c00"
@@ -354,7 +354,7 @@ bg = "#3d1c00"
type = "object"
script = "fireplace"
name = "fireplace"
enter = "block"
mobile = false
tile = 15
fg = "#ff8800"
bg = "#220000"
@@ -363,7 +363,7 @@ bg = "#220000"
type = "object"
script = "chest"
name = "chest"
enter = "block"
mobile = true
tile = 240
fg = "#ccaa44"
bg = "#222200"