wip 4 remove enterresponse
This commit is contained in:
@@ -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
@@ -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,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()`.
|
||||
///
|
||||
|
||||
@@ -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
@@ -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)),
|
||||
|
||||
Reference in New Issue
Block a user