use rusting_engine::prelude::*;
struct Patrol {
guard: &'static str,
from: [f32; 3],
to: [f32; 3],
ticks: u64,
}
const PATROLS: [Patrol; 2] = [
Patrol {
guard: "Guard A",
from: [-9.0, 0.9, 2.0],
to: [9.0, 0.9, 2.0],
ticks: 540,
},
Patrol {
guard: "Guard B",
from: [-12.0, 0.9, -6.0],
to: [-2.0, 0.9, -6.0],
ticks: 300,
},
];
const SIGHT_RANGE: f32 = 9.0;
const SIGHT_ANGLE: f32 = 0.5;
const COOL_DOWN_EVERY: u64 = 2;
const CALM: [f32; 3] = [0.0; 3];
const ALARMED: [f32; 3] = [1.6, 0.15, 0.1];
fn patrol_pose(patrol: &Patrol, tick: u64) -> ([f32; 3], f32) {
let phase = tick % (2 * patrol.ticks);
let (from, to, step) = if phase < patrol.ticks {
(patrol.from, patrol.to, phase)
} else {
(patrol.to, patrol.from, phase - patrol.ticks)
};
let t = step as f32 / patrol.ticks as f32;
let position =
[0, 1, 2].map(|axis| from[axis] + (to[axis] - from[axis]) * t);
let yaw = (-(to[0] - from[0])).atan2(-(to[2] - from[2]));
(position, yaw)
}
fn sees(
scene: &GameScene<'_>,
eye: [f32; 3],
yaw: f32,
target: [f32; 3],
) -> bool {
let offset = [0, 1, 2].map(|axis| target[axis] - eye[axis]);
let distance = offset.iter().map(|value| value * value).sum::<f32>().sqrt();
if distance > SIGHT_RANGE || distance < 1e-3 {
return false;
}
let facing = [-yaw.sin(), -yaw.cos()];
let flat = offset[0].hypot(offset[2]).max(1e-3);
let cosine = (facing[0] * offset[0] + facing[1] * offset[2]) / flat;
if cosine < SIGHT_ANGLE.cos() {
return false;
}
let direction = offset.map(|value| value / distance);
scene
.raycast(eye, direction, SIGHT_RANGE)
.is_some_and(|hit| hit.name == "Player")
}
fn update(scene: &mut GameScene<'_>, time: &FrameTime) {
if scene.pressed("restart") {
scene.restart();
return;
}
if scene.counter_complete("caught") || scene.counter_complete("escaped") {
return;
}
let tick = time.fixed_tick;
let player = scene.object("Player").position();
let mut seen = false;
for patrol in &PATROLS {
let (position, yaw) = patrol_pose(patrol, tick);
let mut guard = scene.object(patrol.guard);
guard.set_position(position);
guard.set_rotation([0.0, yaw, 0.0]);
let eye = [position[0], 1.5, position[2]];
let spotted = sees(scene, eye, yaw, player);
scene.set_emissive(patrol.guard, if spotted { ALARMED } else { CALM });
seen |= spotted;
}
if seen {
if scene.add_to_counter("alert", 1) >= 40 {
scene.set_counter("caught", 1);
}
} else if tick % COOL_DOWN_EVERY == 0 && scene.counter_value("alert") > 0 {
scene.add_to_counter("alert", -1);
}
let at_door = scene
.touching("Player")
.iter()
.any(|name| name == "Vault Door");
if at_door && scene.counter_complete("keys") {
scene.set_counter("escaped", 1);
}
}
rusting_game!(update);