Various pre-release cosmetic fixes
This commit is contained in:
@@ -8,4 +8,7 @@ Third level is dictionaries. Each direction now leads to a feature with its own
|
||||
depth: 10
|
||||
straight:
|
||||
type: wall
|
||||
depth:
|
||||
depth:
|
||||
|
||||
The rendered description ends with a hint reminding the player that each
|
||||
feature's name goes under a `type:` key (the property keeps its own name).
|
||||
148
src/levels/l03_dict.rs
Normal file
148
src/levels/l03_dict.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
//! Level 3 — dictionaries. Each direction leads to a feature with its
|
||||
//! own type + one characteristic property.
|
||||
//!
|
||||
//! Paired design note: `l03.md`.
|
||||
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
use serde::Serialize;
|
||||
use serde_yaml::{Mapping, Value};
|
||||
|
||||
use crate::describe::Describer;
|
||||
|
||||
use super::{Generated, Level};
|
||||
|
||||
pub struct Dict;
|
||||
|
||||
const DIRECTIONS: &[&str] = &["left", "right", "straight", "back", "up", "down"];
|
||||
|
||||
enum Feature {
|
||||
Door,
|
||||
Tunnel,
|
||||
Pit,
|
||||
Stairs,
|
||||
Wall,
|
||||
Altar,
|
||||
}
|
||||
|
||||
impl Feature {
|
||||
fn name(&self) -> &'static str {
|
||||
match self {
|
||||
Feature::Door => "door",
|
||||
Feature::Tunnel => "tunnel",
|
||||
Feature::Pit => "pit",
|
||||
Feature::Stairs => "stairs",
|
||||
Feature::Wall => "wall",
|
||||
Feature::Altar => "altar",
|
||||
}
|
||||
}
|
||||
|
||||
fn random(rng: &mut ChaCha8Rng) -> Self {
|
||||
match rng.gen_range(0..6) {
|
||||
0 => Feature::Door,
|
||||
1 => Feature::Tunnel,
|
||||
2 => Feature::Pit,
|
||||
3 => Feature::Stairs,
|
||||
4 => Feature::Wall,
|
||||
_ => Feature::Altar,
|
||||
}
|
||||
}
|
||||
|
||||
/// One characteristic property: (key, value).
|
||||
fn property(&self, rng: &mut ChaCha8Rng) -> (&'static str, Value) {
|
||||
match self {
|
||||
Feature::Door => ("locked", Value::Bool(rng.gen_bool(0.5))),
|
||||
Feature::Tunnel => ("depth", Value::from(rng.gen_range(5..=30i64))),
|
||||
Feature::Pit => ("depth", Value::from(rng.gen_range(5..=30i64))),
|
||||
Feature::Stairs => {
|
||||
let going = if rng.gen_bool(0.5) { "up" } else { "down" };
|
||||
("going", Value::String(going.to_string()))
|
||||
}
|
||||
Feature::Wall => ("cracked", Value::Bool(rng.gen_bool(0.5))),
|
||||
Feature::Altar => ("blessed", Value::Bool(rng.gen_bool(0.5))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct DescCtx {
|
||||
entries: Vec<DescEntry>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct DescEntry {
|
||||
direction: String,
|
||||
feature: String,
|
||||
prop_name: String,
|
||||
prop_value: String,
|
||||
}
|
||||
|
||||
impl Level for Dict {
|
||||
fn id(&self) -> u8 {
|
||||
3
|
||||
}
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"Dictionaries"
|
||||
}
|
||||
|
||||
fn generate(&self, seed: u64) -> Generated {
|
||||
// Per-level constant so the same `current_seed` produces different
|
||||
// content per level.
|
||||
let mut rng = ChaCha8Rng::seed_from_u64(seed ^ 0x0000_0000_0000_0003);
|
||||
let n = rng.gen_range(2..=3);
|
||||
let directions: Vec<&'static str> =
|
||||
DIRECTIONS.choose_multiple(&mut rng, n).copied().collect();
|
||||
|
||||
let mut top = Mapping::new();
|
||||
let mut entries = Vec::with_capacity(directions.len());
|
||||
for d in &directions {
|
||||
let feature = Feature::random(&mut rng);
|
||||
let (prop_name, prop_value) = feature.property(&mut rng);
|
||||
|
||||
let mut inner = Mapping::new();
|
||||
inner.insert(
|
||||
Value::String("type".to_string()),
|
||||
Value::String(feature.name().to_string()),
|
||||
);
|
||||
inner.insert(
|
||||
Value::String(prop_name.to_string()),
|
||||
prop_value.clone(),
|
||||
);
|
||||
top.insert(Value::String((*d).to_string()), Value::Mapping(inner));
|
||||
|
||||
let prop_value_str = match &prop_value {
|
||||
Value::Bool(b) => b.to_string(),
|
||||
Value::Number(n) => n.to_string(),
|
||||
Value::String(s) => s.clone(),
|
||||
_ => String::new(),
|
||||
};
|
||||
entries.push(DescEntry {
|
||||
direction: (*d).to_string(),
|
||||
feature: feature.name().to_string(),
|
||||
prop_name: prop_name.to_string(),
|
||||
prop_value: prop_value_str,
|
||||
});
|
||||
}
|
||||
|
||||
let target_yaml =
|
||||
serde_yaml::to_string(&Value::Mapping(top)).expect("serialise mapping");
|
||||
|
||||
let mut d = Describer::new();
|
||||
d.register(
|
||||
"l03",
|
||||
"{% for e in entries %}- {{ e.direction }} → {{ e.feature }} ({{ e.prop_name }}: {{ e.prop_value }})\n{% endfor %}\n💡 Each feature is a dictionary — give it a `type:` key plus its property.",
|
||||
)
|
||||
.expect("register template");
|
||||
let description = d
|
||||
.render("l03", &DescCtx { entries })
|
||||
.expect("render template");
|
||||
|
||||
Generated {
|
||||
target_yaml,
|
||||
description,
|
||||
flavor: "🧭 You stand at a junction. Each path reveals its own detail.".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
pub mod l01_minimum;
|
||||
pub mod l02_kv;
|
||||
pub mod l03_dict;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -81,5 +82,6 @@ pub fn registry() -> Vec<Box<dyn Level>> {
|
||||
vec![
|
||||
Box::new(l01_minimum::Minimum),
|
||||
Box::new(l02_kv::KeyValue),
|
||||
Box::new(l03_dict::Dict),
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user