Files
cyberpunk/crates/citygen/src/net.rs
Jeffrey Wilcke a491841639 feat(net): nodes carry their cyberspace tile
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 21:34:59 +02:00

126 lines
3.3 KiB
Rust

//! The Net: subnets, nodes and links anchored to physical tiles.
use crate::geom::Pos;
use crate::ids::{BuildingId, DistrictId, NodeId, SubnetId};
use crate::world::StratumKind;
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Net {
pub subnets: Vec<Subnet>,
pub nodes: Vec<Node>,
pub links: Vec<Link>,
}
/// Undirected link between two nodes.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Link(pub NodeId, pub NodeId);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SubnetOwner {
Building(BuildingId),
District(DistrictId),
City,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Subnet {
pub id: SubnetId,
pub owner: SubnetOwner,
pub security: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum NodeKind {
Terminal,
Camera,
DoorLock,
LiftCtrl,
AdBoard,
LampGrid,
Server,
DataVault,
Ice,
Backbone,
}
impl NodeKind {
pub fn label(self) -> &'static str {
match self {
NodeKind::Terminal => "terminal",
NodeKind::Camera => "camera",
NodeKind::DoorLock => "door lock",
NodeKind::LiftCtrl => "lift control",
NodeKind::AdBoard => "ad board",
NodeKind::LampGrid => "lamp grid",
NodeKind::Server => "server",
NodeKind::DataVault => "data vault",
NodeKind::Ice => "ICE",
NodeKind::Backbone => "backbone",
}
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Node {
pub id: NodeId,
pub kind: NodeKind,
pub anchor: (StratumKind, Pos),
/// The node's tile on the Net stratum.
pub cyber: Pos,
pub subnet: SubnetId,
pub building: Option<BuildingId>,
}
impl Net {
pub fn nodes_on(&self, k: StratumKind) -> impl Iterator<Item = &Node> {
self.nodes.iter().filter(move |n| n.anchor.0 == k)
}
pub fn node_at(&self, k: StratumKind, p: Pos) -> Option<&Node> {
self.nodes.iter().find(|n| n.anchor == (k, p))
}
/// The node standing on a cyberspace tile.
pub fn node_in_cyber(&self, p: Pos) -> Option<&Node> {
self.nodes.iter().find(|n| n.cyber == p)
}
pub fn subnet(&self, id: SubnetId) -> &Subnet {
&self.subnets[id.index()]
}
pub fn node(&self, id: NodeId) -> &Node {
&self.nodes[id.index()]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nodes_are_found_by_cyber_tile() {
let mut net = Net::default();
net.subnets.push(Subnet {
id: SubnetId(0),
owner: SubnetOwner::City,
security: 10,
});
net.nodes.push(Node {
id: NodeId(0),
kind: NodeKind::Server,
anchor: (StratumKind::Street, Pos::new(5, 5)),
cyber: Pos::new(7, 5),
subnet: SubnetId(0),
building: None,
});
assert_eq!(
net.node_in_cyber(Pos::new(7, 5)).map(|n| n.id),
Some(NodeId(0))
);
assert_eq!(net.node_in_cyber(Pos::new(5, 5)), None);
assert!(net.node_at(StratumKind::Street, Pos::new(5, 5)).is_some());
}
}