feat(walker): jack in and out, body left at the port
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -4,16 +4,19 @@ pub mod fov;
|
|||||||
|
|
||||||
pub use crate::render::Visibility;
|
pub use crate::render::Visibility;
|
||||||
use citygen::rng::hash64;
|
use citygen::rng::hash64;
|
||||||
use citygen::{City, Dir, Grid, Pos, StratumKind, Theme};
|
use citygen::{City, ConnectorKind, Dir, Grid, Pos, StratumKind, Theme};
|
||||||
|
|
||||||
pub const RADIUS_STREET: i32 = 14;
|
pub const RADIUS_STREET: i32 = 14;
|
||||||
pub const RADIUS_UNDER: i32 = 8;
|
pub const RADIUS_UNDER: i32 = 8;
|
||||||
|
pub const RADIUS_NET: i32 = 24;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Player {
|
pub struct Player {
|
||||||
pub pos: Pos,
|
pub pos: Pos,
|
||||||
pub stratum: StratumKind,
|
pub stratum: StratumKind,
|
||||||
/// What has been seen on each physical stratum.
|
/// Where the body waits while jacked in: (stratum, tile). None when not jacked in.
|
||||||
|
pub body: Option<(StratumKind, Pos)>,
|
||||||
|
/// What has been seen on each stratum, indexed by `StratumKind::index`.
|
||||||
memory: Vec<Grid<Visibility>>,
|
memory: Vec<Grid<Visibility>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +57,8 @@ impl Player {
|
|||||||
let mut player = Player {
|
let mut player = Player {
|
||||||
pos,
|
pos,
|
||||||
stratum: street,
|
stratum: street,
|
||||||
memory: StratumKind::PHYSICAL
|
body: None,
|
||||||
|
memory: StratumKind::ALL
|
||||||
.iter()
|
.iter()
|
||||||
.map(|_| Grid::filled(city.size, Visibility::Unseen))
|
.map(|_| Grid::filled(city.size, Visibility::Unseen))
|
||||||
.collect(),
|
.collect(),
|
||||||
@@ -64,13 +68,51 @@ impl Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn radius(&self) -> i32 {
|
pub fn radius(&self) -> i32 {
|
||||||
if self.stratum == StratumKind::Undercity {
|
match self.stratum {
|
||||||
RADIUS_UNDER
|
StratumKind::Undercity => RADIUS_UNDER,
|
||||||
} else {
|
StratumKind::Net => RADIUS_NET,
|
||||||
RADIUS_STREET
|
_ => RADIUS_STREET,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn jacked_in(&self) -> bool {
|
||||||
|
self.body.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Jacks in at a Jack connector on this tile or an 8-neighbour of it, leaving the body here.
|
||||||
|
pub fn jack_in(&mut self, city: &City) -> Result<Pos, &'static str> {
|
||||||
|
if self.jacked_in() {
|
||||||
|
return Err("already jacked in");
|
||||||
|
}
|
||||||
|
let here = self.pos;
|
||||||
|
let k = self.stratum;
|
||||||
|
let port = std::iter::once(here)
|
||||||
|
.chain(here.neighbors8())
|
||||||
|
.find_map(|p| {
|
||||||
|
city.connectors_at(p)
|
||||||
|
.find(|c| c.kind == ConnectorKind::Jack && c.serves.contains(&k))
|
||||||
|
.map(|c| c.pos)
|
||||||
|
})
|
||||||
|
.ok_or("no terminal within reach")?;
|
||||||
|
if !city.walkable(StratumKind::Net, port) {
|
||||||
|
return Err("the port is dead");
|
||||||
|
}
|
||||||
|
self.body = Some((k, here));
|
||||||
|
self.stratum = StratumKind::Net;
|
||||||
|
self.pos = port;
|
||||||
|
self.update_fov(city);
|
||||||
|
Ok(port)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns to the body.
|
||||||
|
pub fn jack_out(&mut self, city: &City) -> Result<(), &'static str> {
|
||||||
|
let (k, p) = self.body.take().ok_or("not jacked in")?;
|
||||||
|
self.stratum = k;
|
||||||
|
self.pos = p;
|
||||||
|
self.update_fov(city);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Steps one tile if the target is walkable.
|
/// Steps one tile if the target is walkable.
|
||||||
pub fn try_move(&mut self, city: &City, dir: Dir) -> bool {
|
pub fn try_move(&mut self, city: &City, dir: Dir) -> bool {
|
||||||
let target = self.pos.step(dir);
|
let target = self.pos.step(dir);
|
||||||
@@ -86,6 +128,7 @@ impl Player {
|
|||||||
pub fn use_connector(&mut self, city: &City, up: bool) -> Option<StratumKind> {
|
pub fn use_connector(&mut self, city: &City, up: bool) -> Option<StratumKind> {
|
||||||
let dest = city
|
let dest = city
|
||||||
.connectors_at(self.pos)
|
.connectors_at(self.pos)
|
||||||
|
.filter(|c| c.kind != ConnectorKind::Jack)
|
||||||
.filter_map(|c| c.serves_from(self.stratum))
|
.filter_map(|c| c.serves_from(self.stratum))
|
||||||
.find_map(|(below, above)| if up { above } else { below })?;
|
.find_map(|(below, above)| if up { above } else { below })?;
|
||||||
if !city.walkable(dest, self.pos) {
|
if !city.walkable(dest, self.pos) {
|
||||||
@@ -179,4 +222,62 @@ mod tests {
|
|||||||
p.stratum = StratumKind::Undercity;
|
p.stratum = StratumKind::Undercity;
|
||||||
assert_eq!(p.radius(), 8);
|
assert_eq!(p.radius(), 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn jacking_in_leaves_the_body_at_the_port() {
|
||||||
|
let c = Generator::run(GenParams {
|
||||||
|
seed: 6,
|
||||||
|
size: SizePreset::Custom { w: 120, h: 60 },
|
||||||
|
districts: 5,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let jack = c
|
||||||
|
.connectors
|
||||||
|
.iter()
|
||||||
|
.find(|k| k.kind == citygen::ConnectorKind::Jack && k.public)
|
||||||
|
.unwrap();
|
||||||
|
let stand = jack
|
||||||
|
.pos
|
||||||
|
.neighbors8()
|
||||||
|
.find(|n| c.walkable(StratumKind::Street, *n))
|
||||||
|
.unwrap();
|
||||||
|
let mut p = Player::spawn(&c, 1);
|
||||||
|
p.pos = stand;
|
||||||
|
p.stratum = StratumKind::Street;
|
||||||
|
assert!(!p.jacked_in());
|
||||||
|
assert_eq!(p.jack_in(&c), Ok(jack.pos));
|
||||||
|
assert!(p.jacked_in());
|
||||||
|
assert_eq!(p.stratum, StratumKind::Net);
|
||||||
|
assert_eq!(p.pos, jack.pos);
|
||||||
|
assert_eq!(p.body, Some((StratumKind::Street, stand)));
|
||||||
|
assert_eq!(p.radius(), RADIUS_NET);
|
||||||
|
assert!(p.vis().count(|v| *v == Visibility::Visible) > 10);
|
||||||
|
assert_eq!(p.jack_in(&c), Err("already jacked in"));
|
||||||
|
assert_eq!(p.use_connector(&c, false), None, "jacks are not stairs");
|
||||||
|
assert_eq!(p.use_connector(&c, true), None);
|
||||||
|
assert_eq!(p.jack_out(&c), Ok(()));
|
||||||
|
assert_eq!(
|
||||||
|
(p.stratum, p.pos, p.body),
|
||||||
|
(StratumKind::Street, stand, None)
|
||||||
|
);
|
||||||
|
assert_eq!(p.jack_out(&c), Err("not jacked in"));
|
||||||
|
// Far from any terminal.
|
||||||
|
let far = c
|
||||||
|
.stratum(StratumKind::Street)
|
||||||
|
.unwrap()
|
||||||
|
.tiles
|
||||||
|
.iter()
|
||||||
|
.find(|(q, t)| {
|
||||||
|
t.is_walkable()
|
||||||
|
&& std::iter::once(*q).chain(q.neighbors8()).all(|n| {
|
||||||
|
c.connectors_at(n)
|
||||||
|
.all(|k| k.kind != citygen::ConnectorKind::Jack)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map(|(q, _)| q)
|
||||||
|
.unwrap();
|
||||||
|
p.pos = far;
|
||||||
|
assert_eq!(p.jack_in(&c), Err("no terminal within reach"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user