use rusting_engine::prelude::*;
const MOVES: [(&str, (i32, i32)); 4] = [
("up", (0, -1)),
("down", (0, 1)),
("left", (-1, 0)),
("right", (1, 0)),
];
const WIDTH: i32 = 9;
const DEPTH: i32 = 7;
const START: (i32, i32) = (1, 1);
const RUBBLE_ODDS: u64 = 2;
const ENEMIES: i32 = 2;
fn cell(position: [f32; 3]) -> (i32, i32) {
(position[0].round() as i32, position[2].round() as i32)
}
fn place(scene: &mut GameScene<'_>, name: &str, at: (i32, i32)) {
let y = scene.object(name).position()[1];
scene
.object(name)
.set_position([at.0 as f32, y, at.1 as f32]);
}
fn on_cell(
scene: &mut GameScene<'_>,
class: &str,
at: (i32, i32),
) -> Option<String> {
scene
.in_class(class)
.into_iter()
.find(|name| cell(scene.object(name).position()) == at)
}
fn open(scene: &mut GameScene<'_>, at: (i32, i32)) -> bool {
(1..=WIDTH).contains(&at.0)
&& (1..=DEPTH).contains(&at.1)
&& on_cell(scene, "rubble", at).is_none()
}
fn roll(scene: &GameScene<'_>, floor: i32, n: u64) -> u64 {
let mut z = scene
.seed()
.wrapping_add((floor as u64) << 32 | n)
.wrapping_mul(0x9E37_79B9_7F4A_7C15);
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
fn build(scene: &mut GameScene<'_>, floor: i32) {
for name in scene
.in_class("rubble")
.into_iter()
.chain(scene.in_class("enemy"))
{
scene.despawn(&name);
}
place(scene, "Player", START);
let mut draw = 0;
let mut next = |scene: &GameScene<'_>| {
draw += 1;
roll(scene, floor, draw)
};
for x in (2..WIDTH).step_by(2) {
for z in (2..DEPTH).step_by(2) {
if next(scene) % RUBBLE_ODDS == 0 {
let name = format!("Rubble {x} {z}");
scene.spawn_copy(
"Rubble Template",
&name,
[x as f32, 0.5, z as f32],
);
scene.set_visible(&name, true);
scene.add_class(&name, "rubble");
}
}
}
let mut free = |scene: &GameScene<'_>, far: i32| loop {
let (x, z) = (
1 + (next(scene) % WIDTH as u64) as i32,
1 + (next(scene) % DEPTH as u64) as i32,
);
if (x % 2 == 1 || z % 2 == 1) && (x - START.0) + (z - START.1) >= far {
break (x, z);
}
};
let stairs = free(scene, WIDTH + DEPTH - 6);
place(scene, "Stairs", stairs);
for n in 1..=ENEMIES + floor {
let at = free(scene, 4);
if at == stairs || on_cell(scene, "enemy", at).is_some() {
continue;
}
let name = format!("Enemy {n}");
scene.spawn_copy(
"Enemy Template",
&name,
[at.0 as f32, 0.4, at.1 as f32],
);
scene.set_visible(&name, true);
scene.add_class(&name, "enemy");
}
}
pub fn update(scene: &mut GameScene<'_>, _time: &FrameTime) {
if scene.pressed("restart") {
scene.restart();
return;
}
if scene.counter_complete("depth") || scene.counter_complete("wounds") {
return;
}
let floor = scene.counter_value("depth");
if scene.counter_or("built", -1) != floor {
build(scene, floor);
scene.set_counter("built", floor);
}
let Some((dx, dz)) = MOVES
.iter()
.find(|(action, _)| scene.pressed(action))
.map(|(_, step)| *step)
else {
return;
};
let (x, z) = cell(scene.object("Player").position());
let target = (x + dx, z + dz);
if !open(scene, target) {
return;
}
if let Some(enemy) = on_cell(scene, "enemy", target) {
scene.despawn(&enemy);
} else {
place(scene, "Player", target);
if cell(scene.object("Stairs").position()) == target {
scene.add_to_counter("depth", 1);
return;
}
}
let player = cell(scene.object("Player").position());
for enemy in scene.in_class("enemy") {
let (ex, ez) = cell(scene.object(&enemy).position());
let (gx, gz) = (player.0 - ex, player.1 - ez);
if gx.abs() + gz.abs() == 1 {
scene.add_to_counter("wounds", 1);
scene.flash(&enemy);
scene.add_trauma("Game Camera", 0.4);
continue;
}
let steps = [(gx.signum(), 0), (0, gz.signum())];
let order = if gx.abs() >= gz.abs() { [0, 1] } else { [1, 0] };
for (sx, sz) in order.map(|i| steps[i]) {
let to = (ex + sx, ez + sz);
if (sx, sz) != (0, 0)
&& open(scene, to)
&& on_cell(scene, "enemy", to).is_none()
{
place(scene, &enemy, to);
break;
}
}
}
}
rusting_game!(update);