Files
Oxide/engine/src/layer/registry.rs
T
Homer Simpson f56a1eea3b Import Oxide engine (Stages 0–10) under MIT license
Full project snapshot migrated to new Gitea remote without history:
engine, editor, physics, script, examples, tests, docs, and assets.
Relicensed from GPLv3 to MIT and updated repo URLs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 20:41:02 +02:00

164 lines
5.0 KiB
Rust

//! [`LayerRegistry`]: human-readable names for the 32 layers.
use serde::{Deserialize, Serialize};
use super::{LayerMask, MAX_LAYERS};
/// Maps layer indices (`0..32`) to project-defined names.
///
/// A [`LayerMask`] is just bits; the registry is what lets a project, the
/// editor, and scripts talk about layer **3** as `"Enemy"` instead of a magic
/// number. It is project-level data (serialized with the project, later stages)
/// and changing a name never moves an entity between layers — only the label
/// changes.
///
/// Index `0` is seeded with the name `"Default"`, the layer every entity starts
/// on (see [`Layer`](super::Layer)). The remaining slots are unnamed until a
/// project assigns them.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LayerRegistry {
/// One slot per layer; `None` means the layer has no assigned name.
names: Vec<Option<String>>,
}
impl LayerRegistry {
/// A registry with only layer 0 named (`"Default"`).
pub fn new() -> Self {
let mut names = vec![None; MAX_LAYERS as usize];
names[0] = Some("Default".to_string());
Self { names }
}
/// Assigns `name` to layer `index`.
///
/// # Panics
/// Panics if `index >= 32`.
pub fn set(&mut self, index: u32, name: impl Into<String>) {
assert!(
index < MAX_LAYERS,
"layer index out of range (must be 0..32)"
);
self.names[index as usize] = Some(name.into());
}
/// Clears the name of layer `index`, leaving it unnamed.
///
/// # Panics
/// Panics if `index >= 32`.
pub fn clear(&mut self, index: u32) {
assert!(
index < MAX_LAYERS,
"layer index out of range (must be 0..32)"
);
self.names[index as usize] = None;
}
/// The name of layer `index`, or `None` if it is out of range or unnamed.
pub fn name(&self, index: u32) -> Option<&str> {
self.names.get(index as usize).and_then(|n| n.as_deref())
}
/// The index of the layer named `name`, or `None` if no layer has it.
///
/// Names are not required to be unique; the lowest matching index wins.
pub fn index_of(&self, name: &str) -> Option<u32> {
self.names
.iter()
.position(|n| n.as_deref() == Some(name))
.map(|i| i as u32)
}
/// A [`LayerMask`] built from layer names, skipping any that are unknown.
///
/// Convenient for authoring filters by name, e.g.
/// `registry.mask_of(["Player", "NPC"])`.
pub fn mask_of<'a, I>(&self, names: I) -> LayerMask
where
I: IntoIterator<Item = &'a str>,
{
names.into_iter().filter_map(|n| self.index_of(n)).collect()
}
/// Iterates `(index, name)` for every *named* layer, ascending by index.
pub fn iter(&self) -> impl Iterator<Item = (u32, &str)> {
self.names
.iter()
.enumerate()
.filter_map(|(i, n)| n.as_deref().map(|name| (i as u32, name)))
}
/// The number of named layers.
pub fn named_count(&self) -> usize {
self.names.iter().filter(|n| n.is_some()).count()
}
}
impl Default for LayerRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_names_only_layer_zero() {
let reg = LayerRegistry::new();
assert_eq!(reg.name(0), Some("Default"));
assert_eq!(reg.name(1), None);
assert_eq!(reg.named_count(), 1);
}
#[test]
fn set_and_look_up_by_name() {
let mut reg = LayerRegistry::new();
reg.set(1, "Player");
reg.set(2, "NPC");
reg.set(5, "Water");
assert_eq!(reg.name(2), Some("NPC"));
assert_eq!(reg.index_of("Water"), Some(5));
assert_eq!(reg.index_of("Missing"), None);
assert_eq!(reg.named_count(), 4);
}
#[test]
fn mask_of_names_builds_a_filter() {
let mut reg = LayerRegistry::new();
reg.set(1, "Player");
reg.set(2, "NPC");
let mask = reg.mask_of(["Player", "NPC", "Unknown"]);
assert_eq!(mask, LayerMask::NONE.with(1).with(2));
}
#[test]
fn clear_unsets_a_name() {
let mut reg = LayerRegistry::new();
reg.set(3, "Trigger");
assert_eq!(reg.index_of("Trigger"), Some(3));
reg.clear(3);
assert_eq!(reg.name(3), None);
assert_eq!(reg.index_of("Trigger"), None);
}
#[test]
fn iter_visits_named_layers_in_order() {
let mut reg = LayerRegistry::new();
reg.set(4, "B");
reg.set(2, "A");
let pairs: Vec<_> = reg.iter().collect();
assert_eq!(pairs, vec![(0, "Default"), (2, "A"), (4, "B")]);
}
#[test]
fn round_trips_through_ron() {
let mut reg = LayerRegistry::new();
reg.set(1, "Player");
reg.set(7, "Foliage");
let ron = ron::to_string(&reg).unwrap();
let back: LayerRegistry = ron::from_str(&ron).unwrap();
assert_eq!(reg, back);
}
}