Files
settled-reach/server/src/test_world/rooms/sprint_gauntlet.rs
T
jpmschweitzerandClaude Sonnet 4.6 8e00db2b2d feat(simulation): #94 #99 active tier filtering and transition system
#94 — Active tier simulation (complete):
- Add ActiveSim marker to all 9 test world room NPC spawns
- Fix test entities in routine.rs and path_follow.rs to include ActiveSim
  so With<ActiveSim> queries match correctly in unit tests

#99 — Tier transition logic (complete):
- Implement update_tier_markers system in tier.rs
- Promotes/demotes tier markers by manhattan distance from PlayerCharacter:
  ≤40 tiles → ActiveSim, ≤120 → BackgroundSim, beyond → StateSaved
- Handles cross-z-level as u32::MAX (effectively unreachable)
- No-op when no PlayerCharacter entity present (headless tests safe)
- 11 new unit tests covering all distance bands and boundary cases
- TierPlugin now registers the system after movement::validate_movement

Also picks up extended test coverage added by hoshe:
- observer/tests.rs — 230 lines of perception observer tests
- sound.rs — additional sound event integration tests

All 548 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-19 13:54:37 +01:00

76 lines
2.9 KiB
Rust

//! Sprint Gauntlet — Room 8 (28x20)
//!
//! Tests D-055 (sprint suppresses interaction buffer) and D-016 (anomaly
//! monologue fires retroactively after sprint ends).
//!
//! Layout: Open east-west corridor. Player sprints from the west end past a
//! visible NPC at the east end. A Readable zone-marker sign at the sprint
//! start position tests that the interaction buffer is empty during sprint.
//! After the sprint ends, the delayed anomaly monologue fires.
//!
//! Observer position: (4, 8) relative = (4, 10) absolute, facing East.
//!
//! Entities (StableId 56-57):
//! sprint_zone_marker (6, 8) rel = (6, 10) abs — Readable zone sign
//! npc_sprint_target (22, 8) rel = (22, 10) abs — Visible NPC during sprint
use bevy_app::prelude::*;
use crate::bridge::types::ObjectType;
use crate::knowledge::registry::{EntityRegistry, StableEntityId};
use crate::npc::{Contentment, Npc, ToleranceThreshold, Want, WantKind};
use crate::simulation::interaction::Interactable;
use crate::simulation::movement::TilePosition;
use crate::simulation::path_follow::MovementSpeed;
use crate::simulation::tier::ActiveSim;
/// Room origin (top-left corner including walls).
const ORIGIN_X: i32 = 0;
const ORIGIN_Y: i32 = 2;
/// Spawn Sprint Gauntlet entities in canonical order (StableId 56-57).
pub fn spawn_entities(app: &mut App, registry: &mut EntityRegistry) {
// Sprint zone marker sign (StableId 56)
// Placed at the sprint-start position. During sprint the interaction buffer
// must be empty — this Readable entity tests that suppression (D-055).
let sign_pos = TilePosition::new(ORIGIN_X + 6, ORIGIN_Y + 8, 0);
let entity = app
.world_mut()
.spawn((Interactable, ObjectType::Readable, sign_pos))
.id();
let sid = registry.register(entity);
app.world_mut()
.entity_mut(entity)
.insert(StableEntityId(sid));
// Sprint target NPC (StableId 57)
// Visible at the far (east) end of the gauntlet. The player sprints past
// this NPC; if it carries a Contradicted knowledge entry the anomaly
// monologue fires after ANOMALY_DELAY_TICKS (D-055, D-016).
let npc_pos = TilePosition::new(ORIGIN_X + 22, ORIGIN_Y + 8, 0);
let entity = app
.world_mut()
.spawn((
Npc,
ActiveSim,
Interactable,
npc_pos,
Want {
primary: WantKind::Safety,
intensity: 4,
description: "Sprint Gauntlet: visible NPC during sprint".to_string(),
},
Contentment { level: 0 }, // Neutral — test NPC, contentment not load-bearing here
ToleranceThreshold {
current_stress: 10,
threshold: 50,
},
MovementSpeed::default(),
))
.id();
let sid = registry.register(entity);
app.world_mut()
.entity_mut(entity)
.insert(StableEntityId(sid));
}