Files
Verbatim/tests/collision_robust.rs
2026-06-21 22:27:20 +03:00

295 lines
7.0 KiB
Rust

use verbatim::ai::AiAction;
use verbatim::ai::GameSession;
fn setup() -> GameSession {
let mut s = GameSession::new_seeded(42);
s.init_empty();
s.clear_area(90, 90, 50, 50);
s.perform_action(&AiAction::FillRect {
x: 80,
y: 130,
w: 80,
h: 15,
material: "stone".into(),
});
s
}
#[test]
fn player_blocked_by_left_wall() {
let mut s = setup();
s.step(30);
let p = s.get_player().unwrap();
let wall_x = (p.pos[0] as i32) - 6;
s.perform_action(&AiAction::FillRect {
x: wall_x,
y: 125,
w: 1,
h: 10,
material: "stone".into(),
});
s.perform_action(&AiAction::MoveLeft);
s.step(20);
let p2 = s.get_player().unwrap();
assert!(
p2.pos[0] > wall_x as f32,
"player should not pass through left wall: wall={} player={}",
wall_x,
p2.pos[0]
);
}
#[test]
fn player_blocked_by_right_wall() {
let mut s = setup();
s.step(30);
let p = s.get_player().unwrap();
let wall_x = (p.pos[0] as i32) + 6;
s.perform_action(&AiAction::FillRect {
x: wall_x,
y: 125,
w: 1,
h: 10,
material: "stone".into(),
});
s.perform_action(&AiAction::MoveRight);
s.step(20);
let p2 = s.get_player().unwrap();
assert!(
p2.pos[0] < wall_x as f32,
"player should not pass through right wall: wall={} player={}",
wall_x,
p2.pos[0]
);
}
#[test]
fn player_slides_along_wall() {
let mut s = setup();
s.step(30);
let p = s.get_player().unwrap();
let wall_x = (p.pos[0] as i32) + 6;
s.perform_action(&AiAction::FillRect {
x: wall_x,
y: 125,
w: 1,
h: 10,
material: "stone".into(),
});
s.perform_action(&AiAction::MoveRight);
s.step(30);
let p2 = s.get_player().unwrap();
assert!(p2.pos[0] < wall_x as f32, "player should stay left of wall");
assert!(p2.alive, "player should be alive");
let y_diff = (p2.pos[1] - p.pos[1]).abs();
assert!(
y_diff < 5.0,
"player should not fall through floor while sliding: dy={}",
y_diff
);
}
#[test]
fn player_blocked_by_ceiling() {
let mut s = setup();
s.step(30);
let p = s.get_player().unwrap();
let ceiling_y = (p.pos[1] as i32) - 8;
s.perform_action(&AiAction::FillRect {
x: p.pos[0] as i32 - 5,
y: ceiling_y,
w: 10,
h: 1,
material: "stone".into(),
});
s.perform_action(&AiAction::Jump);
s.step(10);
let p2 = s.get_player().unwrap();
assert!(
p2.pos[1] > ceiling_y as f32,
"player should not pass through ceiling: ceiling={} player={}",
ceiling_y,
p2.pos[1]
);
}
#[test]
fn player_navigates_corridor() {
let mut s = GameSession::new_seeded(42);
s.init_empty();
s.clear_area(100, 100, 40, 30);
s.perform_action(&AiAction::FillRect {
x: 95,
y: 120,
w: 50,
h: 10,
material: "stone".into(),
});
s.perform_action(&AiAction::FillRect {
x: 115,
y: 110,
w: 1,
h: 10,
material: "stone".into(),
});
s.perform_action(&AiAction::FillRect {
x: 125,
y: 110,
w: 1,
h: 10,
material: "stone".into(),
});
s.perform_action(&AiAction::FillRect {
x: 115,
y: 108,
w: 11,
h: 1,
material: "stone".into(),
});
s.step(30);
let p = s.get_player().unwrap();
assert!(
p.pos[0] < 115.0 || p.pos[0] > 125.0,
"player should be outside corridor initially"
);
}
#[test]
fn player_does_not_stick_to_wall() {
let mut s = setup();
s.step(30);
let p = s.get_player().unwrap();
let wall_x = (p.pos[0] as i32) + 8;
s.perform_action(&AiAction::FillRect {
x: wall_x,
y: 125,
w: 1,
h: 10,
material: "stone".into(),
});
for _ in 0..10 {
s.perform_action(&AiAction::MoveRight);
s.step(2);
}
let p_at_wall = s.get_player().unwrap();
let x_at_wall = p_at_wall.pos[0];
for _ in 0..20 {
s.perform_action(&AiAction::MoveLeft);
s.step(2);
}
let p_away = s.get_player().unwrap();
assert!(
p_away.pos[0] < x_at_wall - 1.0,
"player should move away from wall: {} -> {}",
x_at_wall,
p_away.pos[0]
);
}
#[test]
fn player_squeezes_through_gap() {
let mut s = setup();
s.step(30);
let p = s.get_player().unwrap();
let px = p.pos[0] as i32;
s.perform_action(&AiAction::FillRect {
x: px + 8,
y: 128,
w: 1,
h: 2,
material: "stone".into(),
});
s.perform_action(&AiAction::FillRect {
x: px + 8,
y: 122,
w: 1,
h: 2,
material: "stone".into(),
});
s.perform_action(&AiAction::MoveRight);
s.step(20);
let p2 = s.get_player().unwrap();
assert!(p2.alive, "player should survive squeeze attempt");
}
#[test]
fn player_blocked_by_two_walls_both_sides() {
let mut s = setup();
s.step(30);
let p = s.get_player().unwrap();
let px = p.pos[0] as i32;
s.perform_action(&AiAction::FillRect {
x: px - 6,
y: 125,
w: 1,
h: 10,
material: "stone".into(),
});
s.perform_action(&AiAction::FillRect {
x: px + 6,
y: 125,
w: 1,
h: 10,
material: "stone".into(),
});
s.perform_action(&AiAction::MoveRight);
s.step(10);
let p_right = s.get_player().unwrap();
s.perform_action(&AiAction::MoveLeft);
s.step(10);
let p_left = s.get_player().unwrap();
assert!(p_right.pos[0] < (px + 6) as f32, "blocked right");
assert!(p_left.pos[0] > (px - 6) as f32, "blocked left");
assert!(
(p_left.pos[0] - p_right.pos[0]).abs() < 12.0,
"player should stay between walls"
);
}
#[test]
fn player_walks_up_slope() {
let mut s = GameSession::new_seeded(42);
s.init_empty();
s.clear_area(90, 100, 50, 35);
for x in 100..130 {
let h = ((x - 100) / 3).min(15);
for y in 0..h {
s.perform_action(&AiAction::SetCell {
x,
y: 135 - 1 - y,
material: "stone".into(),
});
}
}
s.step(40);
let p = s.get_player().unwrap();
assert!(p.alive, "player should survive on slope");
assert!(p.pos[1] < 135.0, "player should be above floor on slope");
}
#[test]
fn entity_collision_with_dirt_wall() {
let mut s = setup();
s.perform_action(&AiAction::FillRect {
x: 140,
y: 125,
w: 1,
h: 5,
material: "dirt".into(),
});
s.perform_action(&AiAction::Spawn {
kind: "goblin".into(),
x: 135.0,
y: 120.0,
});
s.step(40);
let entities = s.get_entities();
if let Some(g) = entities.into_iter().find(|e| e.kind == "Goblin" && e.alive) {
assert!(
g.pos[0] < 140.0,
"goblin should be blocked by dirt wall: x={}",
g.pos[0]
);
}
}