generated from GarandPLG/rust-flake-template
Update formatting to use 4‑space indentation
- Adjust rustfmt.toml to set tab_spaces = 4. - Reformat imports, struct definitions, function bodies, and match arms across the project to match the new style (aligned fields, added spacing, etc.). - Ensure all code complies with the updated rustfmt settings.
This commit is contained in:
1 parent
a981663152
commit
73f544d5d6
66 files changed
+5378
-5373
No files matched your search
+118
-119
@@ -1,162 +1,161 @@
|
||||
use clap::Parser;
|
||||
use ratatui::{
|
||||
Terminal,
|
||||
backend::TestBackend,
|
||||
crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
|
||||
layout::Rect,
|
||||
Terminal,
|
||||
backend::TestBackend,
|
||||
crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
|
||||
layout::Rect,
|
||||
};
|
||||
use std::sync::mpsc::{Receiver, channel};
|
||||
use war_in_tunnels::{
|
||||
app::{App, GameStates, View, handle_keybindings, threads::AudioCmd},
|
||||
cli::Cli,
|
||||
app::{App, GameStates, View, handle_keybindings, threads::AudioCmd},
|
||||
cli::Cli,
|
||||
};
|
||||
|
||||
fn app() -> (App, Receiver<AudioCmd>) {
|
||||
let args = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
||||
let (tx, rx) = channel();
|
||||
let mut app = App::new(args, tx);
|
||||
app.window_area = Rect::new(0, 0, 120, 40);
|
||||
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
||||
(app, rx)
|
||||
let args = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
||||
let (tx, rx) = channel();
|
||||
let mut app = App::new(args, tx);
|
||||
app.window_area = Rect::new(0, 0, 120, 40);
|
||||
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
||||
(app, rx)
|
||||
}
|
||||
|
||||
fn press(app: &mut App, code: KeyCode) {
|
||||
handle_keybindings(app, KeyEvent::new(code, KeyModifiers::NONE));
|
||||
handle_keybindings(app, KeyEvent::new(code, KeyModifiers::NONE));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn menu_selection_stays_in_bounds_and_opens_each_view() {
|
||||
let (mut app, _) = app();
|
||||
for _ in 0..10 {
|
||||
press(&mut app, KeyCode::Up);
|
||||
}
|
||||
assert_eq!(app.states_ref().unwrap().main_menu.selected_view, 1);
|
||||
for view in [View::Skirmish, View::PerkDecks, View::SkillsConfig] {
|
||||
press(&mut app, KeyCode::Char(' '));
|
||||
assert_eq!(app.view, view);
|
||||
press(&mut app, KeyCode::Esc);
|
||||
assert_eq!(app.view, View::MainMenu);
|
||||
press(&mut app, KeyCode::Down);
|
||||
}
|
||||
for _ in 0..10 {
|
||||
press(&mut app, KeyCode::Down);
|
||||
}
|
||||
assert_eq!(app.states_ref().unwrap().main_menu.selected_view, 3);
|
||||
let (mut app, _) = app();
|
||||
for _ in 0..10 {
|
||||
press(&mut app, KeyCode::Up);
|
||||
}
|
||||
assert_eq!(app.states_ref().unwrap().main_menu.selected_view, 1);
|
||||
for view in [View::Skirmish, View::PerkDecks, View::SkillsConfig] {
|
||||
press(&mut app, KeyCode::Char(' '));
|
||||
assert_eq!(app.view, view);
|
||||
press(&mut app, KeyCode::Esc);
|
||||
assert_eq!(app.view, View::MainMenu);
|
||||
press(&mut app, KeyCode::Down);
|
||||
}
|
||||
for _ in 0..10 {
|
||||
press(&mut app, KeyCode::Down);
|
||||
}
|
||||
assert_eq!(app.states_ref().unwrap().main_menu.selected_view, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn quit_keys_work_in_every_view() {
|
||||
for view in [
|
||||
View::MainMenu,
|
||||
View::Skirmish,
|
||||
View::PerkDecks,
|
||||
View::SkillsConfig,
|
||||
] {
|
||||
for key in [
|
||||
KeyEvent::new(KeyCode::Char('q'), KeyModifiers::NONE),
|
||||
KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL),
|
||||
for view in [
|
||||
View::MainMenu,
|
||||
View::Skirmish,
|
||||
View::PerkDecks,
|
||||
View::SkillsConfig,
|
||||
] {
|
||||
let (mut app, _) = app();
|
||||
app.view = view;
|
||||
handle_keybindings(&mut app, key);
|
||||
assert!(app.exit);
|
||||
for key in [
|
||||
KeyEvent::new(KeyCode::Char('q'), KeyModifiers::NONE),
|
||||
KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL),
|
||||
] {
|
||||
let (mut app, _) = app();
|
||||
app.view = view;
|
||||
handle_keybindings(&mut app, key);
|
||||
assert!(app.exit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn key_release_does_not_trigger_quit() {
|
||||
let (mut app, _) = app();
|
||||
handle_keybindings(
|
||||
&mut app,
|
||||
KeyEvent::new_with_kind(
|
||||
KeyCode::Char('q'),
|
||||
KeyModifiers::NONE,
|
||||
KeyEventKind::Release,
|
||||
),
|
||||
);
|
||||
assert!(!app.exit);
|
||||
let (mut app, _) = app();
|
||||
handle_keybindings(
|
||||
&mut app,
|
||||
KeyEvent::new_with_kind(
|
||||
KeyCode::Char('q'),
|
||||
KeyModifiers::NONE,
|
||||
KeyEventKind::Release,
|
||||
),
|
||||
);
|
||||
assert!(!app.exit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn music_keys_send_commands_without_opening_audio_device() {
|
||||
let (mut app, rx) = app();
|
||||
press(&mut app, KeyCode::Char('m'));
|
||||
press(&mut app, KeyCode::Char('b'));
|
||||
press(&mut app, KeyCode::Char('n'));
|
||||
assert!(matches!(rx.try_recv().unwrap(), AudioCmd::Mute));
|
||||
assert!(matches!(rx.try_recv().unwrap(), AudioCmd::VolumeUp));
|
||||
assert!(matches!(rx.try_recv().unwrap(), AudioCmd::VolumeDown));
|
||||
drop(rx);
|
||||
press(&mut app, KeyCode::Char('m'));
|
||||
assert!(!app.exit);
|
||||
let (mut app, rx) = app();
|
||||
press(&mut app, KeyCode::Char('m'));
|
||||
press(&mut app, KeyCode::Char('b'));
|
||||
press(&mut app, KeyCode::Char('n'));
|
||||
assert!(matches!(rx.try_recv().unwrap(), AudioCmd::Mute));
|
||||
assert!(matches!(rx.try_recv().unwrap(), AudioCmd::VolumeUp));
|
||||
assert!(matches!(rx.try_recv().unwrap(), AudioCmd::VolumeDown));
|
||||
drop(rx);
|
||||
press(&mut app, KeyCode::Char('m'));
|
||||
assert!(!app.exit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skirmish_keys_move_focus_toggle_panel_and_limit_zoom() {
|
||||
let (mut app, _) = app();
|
||||
app.view = View::Skirmish;
|
||||
press(&mut app, KeyCode::Right);
|
||||
assert_eq!(
|
||||
app
|
||||
.states_ref()
|
||||
.unwrap()
|
||||
.skirmish
|
||||
.board
|
||||
.get_focused_cell()
|
||||
.get_coords(),
|
||||
(5, 4)
|
||||
);
|
||||
press(&mut app, KeyCode::Tab);
|
||||
assert!(app.states_ref().unwrap().skirmish.side_panel);
|
||||
press(&mut app, KeyCode::Tab);
|
||||
assert!(!app.states_ref().unwrap().skirmish.side_panel);
|
||||
for _ in 0..4 {
|
||||
press(&mut app, KeyCode::Char(','));
|
||||
}
|
||||
assert_eq!(
|
||||
app.states_ref().unwrap().skirmish.board.zoom_level,
|
||||
war_in_tunnels::app::states::skirmish_states::ZoomLevel::ZoomedIn
|
||||
);
|
||||
for _ in 0..4 {
|
||||
press(&mut app, KeyCode::Char('.'));
|
||||
}
|
||||
assert_eq!(
|
||||
app.states_ref().unwrap().skirmish.board.zoom_level,
|
||||
war_in_tunnels::app::states::skirmish_states::ZoomLevel::ZoomedOut
|
||||
);
|
||||
let (mut app, _) = app();
|
||||
app.view = View::Skirmish;
|
||||
press(&mut app, KeyCode::Right);
|
||||
assert_eq!(
|
||||
app.states_ref()
|
||||
.unwrap()
|
||||
.skirmish
|
||||
.board
|
||||
.get_focused_cell()
|
||||
.get_coords(),
|
||||
(5, 4)
|
||||
);
|
||||
press(&mut app, KeyCode::Tab);
|
||||
assert!(app.states_ref().unwrap().skirmish.side_panel);
|
||||
press(&mut app, KeyCode::Tab);
|
||||
assert!(!app.states_ref().unwrap().skirmish.side_panel);
|
||||
for _ in 0..4 {
|
||||
press(&mut app, KeyCode::Char(','));
|
||||
}
|
||||
assert_eq!(
|
||||
app.states_ref().unwrap().skirmish.board.zoom_level,
|
||||
war_in_tunnels::app::states::skirmish_states::ZoomLevel::ZoomedIn
|
||||
);
|
||||
for _ in 0..4 {
|
||||
press(&mut app, KeyCode::Char('.'));
|
||||
}
|
||||
assert_eq!(
|
||||
app.states_ref().unwrap().skirmish.board.zoom_level,
|
||||
war_in_tunnels::app::states::skirmish_states::ZoomLevel::ZoomedOut
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_views_render_without_real_terminal() {
|
||||
let (mut app, _) = app();
|
||||
let mut terminal = Terminal::new(TestBackend::new(120, 40)).unwrap();
|
||||
for view in [
|
||||
View::MainMenu,
|
||||
View::Skirmish,
|
||||
View::PerkDecks,
|
||||
View::SkillsConfig,
|
||||
] {
|
||||
app.view = view;
|
||||
let before = app.states.clone();
|
||||
terminal
|
||||
.draw(|frame| frame.render_widget(&mut app, frame.area()))
|
||||
.unwrap();
|
||||
let buffer = terminal.backend().buffer();
|
||||
let text: String = buffer.content.iter().map(|cell| cell.symbol()).collect();
|
||||
assert!(!text.trim().is_empty(), "{view:?}");
|
||||
if matches!(view, View::PerkDecks | View::SkillsConfig) {
|
||||
assert!(text.contains("Work in progress"));
|
||||
let (mut app, _) = app();
|
||||
let mut terminal = Terminal::new(TestBackend::new(120, 40)).unwrap();
|
||||
for view in [
|
||||
View::MainMenu,
|
||||
View::Skirmish,
|
||||
View::PerkDecks,
|
||||
View::SkillsConfig,
|
||||
] {
|
||||
app.view = view;
|
||||
let before = app.states.clone();
|
||||
terminal
|
||||
.draw(|frame| frame.render_widget(&mut app, frame.area()))
|
||||
.unwrap();
|
||||
let buffer = terminal.backend().buffer();
|
||||
let text: String = buffer.content.iter().map(|cell| cell.symbol()).collect();
|
||||
assert!(!text.trim().is_empty(), "{view:?}");
|
||||
if matches!(view, View::PerkDecks | View::SkillsConfig) {
|
||||
assert!(text.contains("Work in progress"));
|
||||
}
|
||||
assert_eq!(app.states, before);
|
||||
}
|
||||
assert_eq!(app.states, before);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn common_keys_work_before_states_are_initialized() {
|
||||
let (mut app, _) = app();
|
||||
app.states = None;
|
||||
press(&mut app, KeyCode::Down);
|
||||
press(&mut app, KeyCode::Char('q'));
|
||||
assert!(app.exit);
|
||||
let (mut app, _) = app();
|
||||
app.states = None;
|
||||
press(&mut app, KeyCode::Down);
|
||||
press(&mut app, KeyCode::Char('q'));
|
||||
assert!(app.exit);
|
||||
}
|
||||
+51
-51
@@ -1,65 +1,65 @@
|
||||
use rodio::Decoder;
|
||||
use std::{
|
||||
fs::{self, read},
|
||||
io::{BufReader, Cursor},
|
||||
path::PathBuf,
|
||||
process::{Command, Output},
|
||||
fs::{self, read},
|
||||
io::{BufReader, Cursor},
|
||||
path::PathBuf,
|
||||
process::{Command, Output},
|
||||
};
|
||||
use war_in_tunnels::app::threads::{SoundrackParts, Soundtrack, load_audio};
|
||||
|
||||
#[test]
|
||||
fn audio_repeats_after_end_of_file() {
|
||||
let data = read("soundtracks/default/calm.ogg").unwrap();
|
||||
let sample_count = Decoder::new(BufReader::new(Cursor::new(data)))
|
||||
.unwrap()
|
||||
.count();
|
||||
assert!(sample_count > 0);
|
||||
let data = read("soundtracks/default/calm.ogg").unwrap();
|
||||
let sample_count = Decoder::new(BufReader::new(Cursor::new(data)))
|
||||
.unwrap()
|
||||
.count();
|
||||
assert!(sample_count > 0);
|
||||
|
||||
let mut source = load_audio(&Soundtrack::Default, &SoundrackParts::Calm);
|
||||
assert_eq!(source.by_ref().take(sample_count).count(), sample_count);
|
||||
assert_eq!(source.take(1024).count(), 1024);
|
||||
let mut source = load_audio(&Soundtrack::Default, &SoundrackParts::Calm);
|
||||
assert_eq!(source.by_ref().take(sample_count).count(), sample_count);
|
||||
assert_eq!(source.take(1024).count(), 1024);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn packaged_audio_loads_without_project_working_directory() -> () {
|
||||
if std::env::var_os("WAR_IN_TUNNELS_PACKAGED_AUDIO_TEST").is_some() {
|
||||
let mut source: rodio::source::Amplify<
|
||||
rodio::decoder::LoopedDecoder<BufReader<Cursor<Vec<u8>>>>,
|
||||
> = load_audio(&Soundtrack::Default, &SoundrackParts::Calm);
|
||||
assert_eq!(source.by_ref().take(1024).count(), 1024);
|
||||
return;
|
||||
}
|
||||
if std::env::var_os("WAR_IN_TUNNELS_PACKAGED_AUDIO_TEST").is_some() {
|
||||
let mut source: rodio::source::Amplify<
|
||||
rodio::decoder::LoopedDecoder<BufReader<Cursor<Vec<u8>>>>,
|
||||
> = load_audio(&Soundtrack::Default, &SoundrackParts::Calm);
|
||||
assert_eq!(source.by_ref().take(1024).count(), 1024);
|
||||
return;
|
||||
}
|
||||
|
||||
let directory: PathBuf = std::env::temp_dir().join(format!(
|
||||
"war-in-tunnels-packaged-audio-{}",
|
||||
std::process::id()
|
||||
));
|
||||
fs::create_dir_all(directory.join("soundtracks/default")).unwrap();
|
||||
fs::create_dir_all(directory.join("working-directory")).unwrap();
|
||||
let executable: PathBuf = directory.join("audio-test");
|
||||
fs::copy(std::env::current_exe().unwrap(), &executable).unwrap();
|
||||
for filename in ["calm.ogg", "test.ogg"] {
|
||||
fs::copy(
|
||||
PathBuf::from("soundtracks/default").join(filename),
|
||||
directory.join("soundtracks/default").join(filename),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
let output: Output = Command::new(executable)
|
||||
.args([
|
||||
"--exact",
|
||||
"packaged_audio_loads_without_project_working_directory",
|
||||
"--nocapture",
|
||||
])
|
||||
.env("WAR_IN_TUNNELS_PACKAGED_AUDIO_TEST", "1")
|
||||
.current_dir(directory.join("working-directory"))
|
||||
.output()
|
||||
.unwrap();
|
||||
fs::remove_dir_all(directory).unwrap();
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"{}{}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
let directory: PathBuf = std::env::temp_dir().join(format!(
|
||||
"war-in-tunnels-packaged-audio-{}",
|
||||
std::process::id()
|
||||
));
|
||||
fs::create_dir_all(directory.join("soundtracks/default")).unwrap();
|
||||
fs::create_dir_all(directory.join("working-directory")).unwrap();
|
||||
let executable: PathBuf = directory.join("audio-test");
|
||||
fs::copy(std::env::current_exe().unwrap(), &executable).unwrap();
|
||||
for filename in ["calm.ogg", "test.ogg"] {
|
||||
fs::copy(
|
||||
PathBuf::from("soundtracks/default").join(filename),
|
||||
directory.join("soundtracks/default").join(filename),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
let output: Output = Command::new(executable)
|
||||
.args([
|
||||
"--exact",
|
||||
"packaged_audio_loads_without_project_working_directory",
|
||||
"--nocapture",
|
||||
])
|
||||
.env("WAR_IN_TUNNELS_PACKAGED_AUDIO_TEST", "1")
|
||||
.current_dir(directory.join("working-directory"))
|
||||
.output()
|
||||
.unwrap();
|
||||
fs::remove_dir_all(directory).unwrap();
|
||||
assert!(
|
||||
output.status.success(),
|
||||
"{}{}",
|
||||
String::from_utf8_lossy(&output.stdout),
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
}
|
||||
+119
-119
@@ -1,155 +1,155 @@
|
||||
use ratatui::layout::Rect;
|
||||
use war_in_tunnels::app::states::skirmish_states::{
|
||||
BoardState, FocusedCell, MoveFocusedCell, Offset, Players, ZoomLevel,
|
||||
structures::{Structure, Structures},
|
||||
tasks::Task,
|
||||
units::Unit,
|
||||
BoardState, FocusedCell, MoveFocusedCell, Offset, Players, ZoomLevel,
|
||||
structures::{Structure, Structures},
|
||||
tasks::Task,
|
||||
units::Unit,
|
||||
};
|
||||
|
||||
fn board() -> BoardState {
|
||||
BoardState::new(&Rect::new(0, 0, 120, 40), 42, 11, ZoomLevel::Default, false)
|
||||
BoardState::new(&Rect::new(0, 0, 120, 40), 42, 11, ZoomLevel::Default, false)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_has_opposing_bases_ore_and_two_miners() {
|
||||
let board = board();
|
||||
assert_eq!(board.cells.len(), 11);
|
||||
assert!(board.cells.iter().all(|row| row.len() == 42));
|
||||
for (col, owner, ore_col) in [(2, Players::Player, 1), (39, Players::Enemy, 40)] {
|
||||
let cell = board.get_ref_cell(5, col);
|
||||
assert!(matches!(cell.get_structure(), Structures::Base(_)));
|
||||
assert_eq!(cell.get_structure().get_owner(), owner);
|
||||
let unit = cell.get_ref_option_unit().as_ref().unwrap();
|
||||
assert_eq!(unit.get_coords(), (5, col));
|
||||
assert_eq!(unit.get_owner(), owner);
|
||||
assert!(matches!(
|
||||
board.get_ref_cell(5, ore_col).get_structure(),
|
||||
Structures::Ore(_)
|
||||
));
|
||||
let board = board();
|
||||
assert_eq!(board.cells.len(), 11);
|
||||
assert!(board.cells.iter().all(|row| row.len() == 42));
|
||||
for (col, owner, ore_col) in [(2, Players::Player, 1), (39, Players::Enemy, 40)] {
|
||||
let cell = board.get_ref_cell(5, col);
|
||||
assert!(matches!(cell.get_structure(), Structures::Base(_)));
|
||||
assert_eq!(cell.get_structure().get_owner(), owner);
|
||||
let unit = cell.get_ref_option_unit().as_ref().unwrap();
|
||||
assert_eq!(unit.get_coords(), (5, col));
|
||||
assert_eq!(unit.get_owner(), owner);
|
||||
assert!(matches!(
|
||||
board.get_ref_cell(5, ore_col).get_structure(),
|
||||
Structures::Ore(_)
|
||||
));
|
||||
assert_eq!(
|
||||
board.get_ref_cell(5, ore_col).get_structure().get_owner(),
|
||||
Players::Unclaimed
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
board.get_ref_cell(5, ore_col).get_structure().get_owner(),
|
||||
Players::Unclaimed
|
||||
board
|
||||
.cells
|
||||
.iter()
|
||||
.flatten()
|
||||
.filter(|c| c.get_ref_option_unit().is_some())
|
||||
.count(),
|
||||
2
|
||||
);
|
||||
}
|
||||
assert_eq!(
|
||||
board
|
||||
.cells
|
||||
.iter()
|
||||
.flatten()
|
||||
.filter(|c| c.get_ref_option_unit().is_some())
|
||||
.count(),
|
||||
2
|
||||
);
|
||||
assert!(matches!(
|
||||
board.get_ref_cell(0, 0).get_structure(),
|
||||
Structures::Stone(_)
|
||||
));
|
||||
assert!(matches!(
|
||||
board.get_ref_cell(0, 0).get_structure(),
|
||||
Structures::Stone(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn focus_cannot_leave_map_even_with_extreme_coordinates() {
|
||||
let mut focus = FocusedCell::new(0, 0, 11, 42);
|
||||
assert_eq!(focus.move_focused_cell(MoveFocusedCell::Up), (0, 0));
|
||||
assert_eq!(focus.move_focused_cell(MoveFocusedCell::Left), (0, 0));
|
||||
focus.set_focused_cell((usize::MAX, usize::MAX));
|
||||
assert_eq!(focus.get_coords(), (10, 41));
|
||||
assert_eq!(focus.move_focused_cell(MoveFocusedCell::Down), (10, 41));
|
||||
assert_eq!(focus.move_focused_cell(MoveFocusedCell::Right), (10, 41));
|
||||
let mut focus = FocusedCell::new(0, 0, 11, 42);
|
||||
assert_eq!(focus.move_focused_cell(MoveFocusedCell::Up), (0, 0));
|
||||
assert_eq!(focus.move_focused_cell(MoveFocusedCell::Left), (0, 0));
|
||||
focus.set_focused_cell((usize::MAX, usize::MAX));
|
||||
assert_eq!(focus.get_coords(), (10, 41));
|
||||
assert_eq!(focus.move_focused_cell(MoveFocusedCell::Down), (10, 41));
|
||||
assert_eq!(focus.move_focused_cell(MoveFocusedCell::Right), (10, 41));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn offsets_remain_within_bounds_when_limits_change() {
|
||||
let mut offset = Offset::new(Some(100), Some(5));
|
||||
assert_eq!(offset.get_value(), 5);
|
||||
offset.next();
|
||||
assert_eq!(offset.get_value(), 5);
|
||||
offset.set_max(2);
|
||||
assert_eq!(offset.get_value(), 2);
|
||||
offset.set_value(usize::MAX);
|
||||
assert_eq!(offset.get_value(), 2);
|
||||
for _ in 0..4 {
|
||||
offset.prev();
|
||||
}
|
||||
assert_eq!(offset.get_value(), 0);
|
||||
let mut offset = Offset::new(Some(100), Some(5));
|
||||
assert_eq!(offset.get_value(), 5);
|
||||
offset.next();
|
||||
assert_eq!(offset.get_value(), 5);
|
||||
offset.set_max(2);
|
||||
assert_eq!(offset.get_value(), 2);
|
||||
offset.set_value(usize::MAX);
|
||||
assert_eq!(offset.get_value(), 2);
|
||||
for _ in 0..4 {
|
||||
offset.prev();
|
||||
}
|
||||
assert_eq!(offset.get_value(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confirming_path_queues_task_without_including_start_cell() {
|
||||
let mut board = board();
|
||||
board.change_focused_cell(MoveFocusedCell::Left);
|
||||
board.toggle_marking();
|
||||
board.change_focused_cell(MoveFocusedCell::Right);
|
||||
board.change_focused_cell(MoveFocusedCell::Down);
|
||||
board.toggle_marking();
|
||||
let unit = board
|
||||
.get_ref_cell(5, 2)
|
||||
.get_ref_option_unit()
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let tasks = unit.get_ref_tasks();
|
||||
assert_eq!(tasks.len(), 1);
|
||||
assert_eq!(
|
||||
tasks[0].get_path().iter().copied().collect::<Vec<_>>(),
|
||||
[(5, 3), (6, 3)]
|
||||
);
|
||||
assert!(!board.marked_cells.marking_cells);
|
||||
assert!(board.get_marked_cells().is_empty());
|
||||
assert!(board.cells.iter().flatten().all(|c| !c.get_marked()));
|
||||
let mut board = board();
|
||||
board.change_focused_cell(MoveFocusedCell::Left);
|
||||
board.toggle_marking();
|
||||
board.change_focused_cell(MoveFocusedCell::Right);
|
||||
board.change_focused_cell(MoveFocusedCell::Down);
|
||||
board.toggle_marking();
|
||||
let unit = board
|
||||
.get_ref_cell(5, 2)
|
||||
.get_ref_option_unit()
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let tasks = unit.get_ref_tasks();
|
||||
assert_eq!(tasks.len(), 1);
|
||||
assert_eq!(
|
||||
tasks[0].get_path().iter().copied().collect::<Vec<_>>(),
|
||||
[(5, 3), (6, 3)]
|
||||
);
|
||||
assert!(!board.marked_cells.marking_cells);
|
||||
assert!(board.get_marked_cells().is_empty());
|
||||
assert!(board.cells.iter().flatten().all(|c| !c.get_marked()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancelling_path_clears_marks_without_queuing_task() {
|
||||
let mut board = board();
|
||||
board.change_focused_cell(MoveFocusedCell::Left);
|
||||
board.toggle_marking();
|
||||
board.change_focused_cell(MoveFocusedCell::Right);
|
||||
board.marked_cells.marking_cells = false;
|
||||
board.end_marking_cells(true);
|
||||
assert!(
|
||||
board
|
||||
.get_ref_cell(5, 2)
|
||||
.get_ref_option_unit()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get_ref_tasks()
|
||||
.is_empty()
|
||||
);
|
||||
assert!(board.get_marked_cells().is_empty());
|
||||
assert!(board.marked_cells.selected_unit.is_none());
|
||||
assert!(board.cells.iter().flatten().all(|c| !c.get_marked()));
|
||||
let mut board = board();
|
||||
board.change_focused_cell(MoveFocusedCell::Left);
|
||||
board.toggle_marking();
|
||||
board.change_focused_cell(MoveFocusedCell::Right);
|
||||
board.marked_cells.marking_cells = false;
|
||||
board.end_marking_cells(true);
|
||||
assert!(
|
||||
board
|
||||
.get_ref_cell(5, 2)
|
||||
.get_ref_option_unit()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get_ref_tasks()
|
||||
.is_empty()
|
||||
);
|
||||
assert!(board.get_marked_cells().is_empty());
|
||||
assert!(board.marked_cells.selected_unit.is_none());
|
||||
assert!(board.cells.iter().flatten().all(|c| !c.get_marked()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backtracking_revisited_cell_preserves_earlier_mark() {
|
||||
let mut board = board();
|
||||
board.change_focused_cell(MoveFocusedCell::Left);
|
||||
board.toggle_marking();
|
||||
board.change_focused_cell(MoveFocusedCell::Right);
|
||||
board.change_focused_cell(MoveFocusedCell::Left);
|
||||
board.pop_marked_cell();
|
||||
assert_eq!(board.get_focused_cell().get_coords(), (5, 3));
|
||||
assert!(board.get_ref_cell(5, 2).get_marked());
|
||||
board.pop_marked_cell();
|
||||
board.pop_marked_cell();
|
||||
assert_eq!(board.get_focused_cell().get_coords(), (5, 2));
|
||||
assert_eq!(board.get_marked_cells().len(), 1);
|
||||
assert!(!board.get_ref_cell(5, 3).get_marked());
|
||||
let mut board = board();
|
||||
board.change_focused_cell(MoveFocusedCell::Left);
|
||||
board.toggle_marking();
|
||||
board.change_focused_cell(MoveFocusedCell::Right);
|
||||
board.change_focused_cell(MoveFocusedCell::Left);
|
||||
board.pop_marked_cell();
|
||||
assert_eq!(board.get_focused_cell().get_coords(), (5, 3));
|
||||
assert!(board.get_ref_cell(5, 2).get_marked());
|
||||
board.pop_marked_cell();
|
||||
board.pop_marked_cell();
|
||||
assert_eq!(board.get_focused_cell().get_coords(), (5, 2));
|
||||
assert_eq!(board.get_marked_cells().len(), 1);
|
||||
assert!(!board.get_ref_cell(5, 3).get_marked());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn large_viewport_and_zoom_keep_camera_inside_map() {
|
||||
for zoom in [
|
||||
ZoomLevel::ZoomedIn,
|
||||
ZoomLevel::Default,
|
||||
ZoomLevel::ZoomedOut,
|
||||
] {
|
||||
let mut board = BoardState::new(&Rect::new(0, 0, 240, 120), 36, 11, zoom, false);
|
||||
assert_eq!(board.vertical_offset.get_value(), 0);
|
||||
board.horizontal_offset.set_value(usize::MAX);
|
||||
board.change_resize(&Rect::new(0, 0, 500, 120), false);
|
||||
assert_eq!(board.horizontal_offset.get_value(), 0);
|
||||
board.change_zoom(ZoomLevel::ZoomedOut);
|
||||
assert!(board.is_focused_cell_visible());
|
||||
assert_eq!(board.cells.len(), 11);
|
||||
}
|
||||
for zoom in [
|
||||
ZoomLevel::ZoomedIn,
|
||||
ZoomLevel::Default,
|
||||
ZoomLevel::ZoomedOut,
|
||||
] {
|
||||
let mut board = BoardState::new(&Rect::new(0, 0, 240, 120), 36, 11, zoom, false);
|
||||
assert_eq!(board.vertical_offset.get_value(), 0);
|
||||
board.horizontal_offset.set_value(usize::MAX);
|
||||
board.change_resize(&Rect::new(0, 0, 500, 120), false);
|
||||
assert_eq!(board.horizontal_offset.get_value(), 0);
|
||||
board.change_zoom(ZoomLevel::ZoomedOut);
|
||||
assert!(board.is_focused_cell_visible());
|
||||
assert_eq!(board.cells.len(), 11);
|
||||
}
|
||||
}
|
||||
+72
-72
@@ -4,94 +4,94 @@ use war_in_tunnels::{app::View, cli::Cli};
|
||||
|
||||
#[test]
|
||||
fn defaults_start_in_menu_without_logging() {
|
||||
let args = Cli::try_parse_from(["game"]).unwrap();
|
||||
assert_eq!(args.view, View::MainMenu);
|
||||
assert_eq!((args.map_width, args.map_height), (42, 11));
|
||||
assert_eq!(args.username, "Player");
|
||||
assert!(!args.log);
|
||||
assert!(!args.disable_audio);
|
||||
assert!(!args.mute);
|
||||
let args = Cli::try_parse_from(["game"]).unwrap();
|
||||
assert_eq!(args.view, View::MainMenu);
|
||||
assert_eq!((args.map_width, args.map_height), (42, 11));
|
||||
assert_eq!(args.username, "Player");
|
||||
assert!(!args.log);
|
||||
assert!(!args.disable_audio);
|
||||
assert!(!args.mute);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numeric_options_accept_boundaries_and_reject_out_of_range_values() {
|
||||
for (flag, min, max) in [
|
||||
("--map-width", 36, 108),
|
||||
("--map-height", 11, 39),
|
||||
("--starting-wood", 1, 150),
|
||||
("--starting-metal", 1, 150),
|
||||
("--supply-limit", 49, 149),
|
||||
("--skill-points-limit", 90, 690),
|
||||
] {
|
||||
for value in [min, max] {
|
||||
assert!(
|
||||
Cli::try_parse_from(["game", flag, &value.to_string()]).is_ok(),
|
||||
"{flag}={value}"
|
||||
);
|
||||
for (flag, min, max) in [
|
||||
("--map-width", 36, 108),
|
||||
("--map-height", 11, 39),
|
||||
("--starting-wood", 1, 150),
|
||||
("--starting-metal", 1, 150),
|
||||
("--supply-limit", 49, 149),
|
||||
("--skill-points-limit", 90, 690),
|
||||
] {
|
||||
for value in [min, max] {
|
||||
assert!(
|
||||
Cli::try_parse_from(["game", flag, &value.to_string()]).is_ok(),
|
||||
"{flag}={value}"
|
||||
);
|
||||
}
|
||||
for value in [min - 1, max + 1] {
|
||||
assert!(
|
||||
Cli::try_parse_from(["game", flag, &value.to_string()]).is_err(),
|
||||
"{flag}={value}"
|
||||
);
|
||||
}
|
||||
}
|
||||
for value in [min - 1, max + 1] {
|
||||
assert!(
|
||||
Cli::try_parse_from(["game", flag, &value.to_string()]).is_err(),
|
||||
"{flag}={value}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn xp_modifier_rejects_non_finite_malformed_and_out_of_range_values() {
|
||||
for value in ["0.5", "1", "2.0"] {
|
||||
let args = Cli::try_parse_from(["game", "--xp-modifier", value]).unwrap();
|
||||
assert_eq!(args.xp_modifier, value.parse::<f32>().unwrap());
|
||||
}
|
||||
for value in ["0.49", "2.01", "NaN", "inf", "text"] {
|
||||
assert!(
|
||||
Cli::try_parse_from(["game", "--xp-modifier", value]).is_err(),
|
||||
"{value}"
|
||||
);
|
||||
}
|
||||
for value in ["0.5", "1", "2.0"] {
|
||||
let args = Cli::try_parse_from(["game", "--xp-modifier", value]).unwrap();
|
||||
assert_eq!(args.xp_modifier, value.parse::<f32>().unwrap());
|
||||
}
|
||||
for value in ["0.49", "2.01", "NaN", "inf", "text"] {
|
||||
assert!(
|
||||
Cli::try_parse_from(["game", "--xp-modifier", value]).is_err(),
|
||||
"{value}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn enum_options_and_audio_flags_are_parsed() {
|
||||
let args =
|
||||
Cli::try_parse_from(["game", "--view", "skirmish", "--disable-audio", "--mute"]).unwrap();
|
||||
assert_eq!(args.view, View::Skirmish);
|
||||
assert!(args.disable_audio && args.mute);
|
||||
for flag in [
|
||||
"--view",
|
||||
"--game-mode",
|
||||
"--zoom-level",
|
||||
"--perk-deck",
|
||||
"--soundtrack",
|
||||
] {
|
||||
assert_eq!(
|
||||
Cli::try_parse_from(["game", flag, "unknown"])
|
||||
.unwrap_err()
|
||||
.kind(),
|
||||
ErrorKind::InvalidValue
|
||||
);
|
||||
}
|
||||
let args =
|
||||
Cli::try_parse_from(["game", "--view", "skirmish", "--disable-audio", "--mute"]).unwrap();
|
||||
assert_eq!(args.view, View::Skirmish);
|
||||
assert!(args.disable_audio && args.mute);
|
||||
for flag in [
|
||||
"--view",
|
||||
"--game-mode",
|
||||
"--zoom-level",
|
||||
"--perk-deck",
|
||||
"--soundtrack",
|
||||
] {
|
||||
assert_eq!(
|
||||
Cli::try_parse_from(["game", flag, "unknown"])
|
||||
.unwrap_err()
|
||||
.kind(),
|
||||
ErrorKind::InvalidValue
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn binary_help_and_version_work_without_terminal_or_audio() {
|
||||
for (flag, expected) in [("--help", "Usage:"), ("--version", "0.1.0")] {
|
||||
for (flag, expected) in [("--help", "Usage:"), ("--version", "0.1.0")] {
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_war_in_tunnels"))
|
||||
.arg(flag)
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
assert!(String::from_utf8(output.stdout).unwrap().contains(expected));
|
||||
}
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_war_in_tunnels"))
|
||||
.arg(flag)
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(output.status.success());
|
||||
assert!(String::from_utf8(output.stdout).unwrap().contains(expected));
|
||||
}
|
||||
let output = Command::new(env!("CARGO_BIN_EXE_war_in_tunnels"))
|
||||
.args(["--map-width", "0"])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(!output.status.success());
|
||||
assert!(
|
||||
String::from_utf8(output.stderr)
|
||||
.unwrap()
|
||||
.contains("--map-width")
|
||||
);
|
||||
.args(["--map-width", "0"])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(!output.status.success());
|
||||
assert!(
|
||||
String::from_utf8(output.stderr)
|
||||
.unwrap()
|
||||
.contains("--map-width")
|
||||
);
|
||||
}
|
||||
+336
-333
@@ -1,420 +1,423 @@
|
||||
use clap::Parser;
|
||||
use ratatui::{
|
||||
Terminal,
|
||||
backend::TestBackend,
|
||||
buffer::Cell,
|
||||
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
||||
layout::Rect,
|
||||
Terminal,
|
||||
backend::TestBackend,
|
||||
buffer::Cell,
|
||||
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
||||
layout::Rect,
|
||||
};
|
||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||
use war_in_tunnels::{
|
||||
app::{
|
||||
Action, App, GameStates, View, available_skirmish_actions, handle_keybindings,
|
||||
states::{
|
||||
SkirmishState,
|
||||
skirmish_states::{
|
||||
BoardState, MoveFocusedCell, Players, Resources, ZoomLevel,
|
||||
resources::MINE_PRODUCTION_TICKS,
|
||||
structures::{
|
||||
BaseBuilding, BuildingTerrain, Mine, MineConstruction, Ore, Structure, Structures, Tunnel,
|
||||
app::{
|
||||
Action, App, GameStates, View, available_skirmish_actions, handle_keybindings,
|
||||
states::{
|
||||
SkirmishState,
|
||||
skirmish_states::{
|
||||
BoardState, MoveFocusedCell, Players, Resources, ZoomLevel,
|
||||
resources::MINE_PRODUCTION_TICKS,
|
||||
structures::{
|
||||
BaseBuilding, BuildingTerrain, Mine, MineConstruction, Ore, Structure,
|
||||
Structures, Tunnel,
|
||||
},
|
||||
tasks::Tasks,
|
||||
units::{MinerUnit, Unit, Units},
|
||||
},
|
||||
},
|
||||
tasks::Tasks,
|
||||
units::{MinerUnit, Unit, Units},
|
||||
},
|
||||
threads::{AudioCmd, TICK_MS},
|
||||
},
|
||||
threads::{AudioCmd, TICK_MS},
|
||||
},
|
||||
cli::Cli,
|
||||
cli::Cli,
|
||||
};
|
||||
|
||||
fn board() -> BoardState {
|
||||
BoardState::new(&Rect::new(0, 0, 160, 50), 42, 11, ZoomLevel::Default, false)
|
||||
BoardState::new(&Rect::new(0, 0, 160, 50), 42, 11, ZoomLevel::Default, false)
|
||||
}
|
||||
|
||||
fn complete_mine(board: &mut BoardState, coords: (usize, usize)) -> () {
|
||||
assert!(board.start_mine_construction(coords, Players::Player));
|
||||
for _ in 0..MineConstruction::BUILD_TICKS {
|
||||
board.advance_construction();
|
||||
}
|
||||
assert!(matches!(
|
||||
board.cells[coords.0][coords.1].get_structure(),
|
||||
Structures::Mine(_)
|
||||
));
|
||||
assert!(board.start_mine_construction(coords, Players::Player));
|
||||
for _ in 0..MineConstruction::BUILD_TICKS {
|
||||
board.advance_construction();
|
||||
}
|
||||
assert!(matches!(
|
||||
board.cells[coords.0][coords.1].get_structure(),
|
||||
Structures::Mine(_)
|
||||
));
|
||||
}
|
||||
|
||||
fn app() -> App {
|
||||
let args: Cli = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
||||
let (tx, _rx): (Sender<AudioCmd>, Receiver<AudioCmd>) = channel();
|
||||
let mut app: App = App::new(args, tx);
|
||||
app.view = View::Skirmish;
|
||||
app.window_area = Rect::new(0, 0, 160, 50);
|
||||
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
||||
app
|
||||
let args: Cli = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
||||
let (tx, _rx): (Sender<AudioCmd>, Receiver<AudioCmd>) = channel();
|
||||
let mut app: App = App::new(args, tx);
|
||||
app.view = View::Skirmish;
|
||||
app.window_area = Rect::new(0, 0, 160, 50);
|
||||
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
||||
app
|
||||
}
|
||||
|
||||
fn travel(board: &mut BoardState, direction: MoveFocusedCell) -> () {
|
||||
board.toggle_marking();
|
||||
board.change_focused_cell(direction);
|
||||
board.toggle_marking();
|
||||
for _ in 0..3000_u16.div_ceil(u16::from(TICK_MS)) {
|
||||
board.advance_turn();
|
||||
}
|
||||
board.toggle_marking();
|
||||
board.change_focused_cell(direction);
|
||||
board.toggle_marking();
|
||||
for _ in 0..3000_u16.div_ceil(u16::from(TICK_MS)) {
|
||||
board.advance_turn();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn miner_has_builder_permission_through_all_unit_wrappers() -> () {
|
||||
let miner: MinerUnit = MinerUnit::new(Players::Player, (5, 2));
|
||||
assert!(miner.get_can_build());
|
||||
let unit: Units = Units::Miner(miner);
|
||||
assert!(unit.get_can_build());
|
||||
assert!(Some(unit).get_can_build());
|
||||
let no_unit: Option<Units> = None;
|
||||
assert!(!no_unit.get_can_build());
|
||||
let miner: MinerUnit = MinerUnit::new(Players::Player, (5, 2));
|
||||
assert!(miner.get_can_build());
|
||||
let unit: Units = Units::Miner(miner);
|
||||
assert!(unit.get_can_build());
|
||||
assert!(Some(unit).get_can_build());
|
||||
let no_unit: Option<Units> = None;
|
||||
assert!(!no_unit.get_can_build());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mine_requires_a_cardinal_friendly_builder_and_preserves_target_on_failure() -> () {
|
||||
let mut board: BoardState = board();
|
||||
let original: Structures = board.cells[5][1].get_structure();
|
||||
board.cells[5][2].get_mut_option_unit().take();
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Player));
|
||||
*board.cells[5][2].get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(Players::Enemy, (5, 2))));
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Player));
|
||||
board.cells[5][2].get_mut_option_unit().take();
|
||||
*board.cells[4][2].get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(Players::Player, (4, 2))));
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Player));
|
||||
assert_eq!(board.cells[5][1].get_structure(), original);
|
||||
assert!(!board.start_mine_construction((usize::MAX, 1), Players::Player));
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Unclaimed));
|
||||
let mut board: BoardState = board();
|
||||
let original: Structures = board.cells[5][1].get_structure();
|
||||
board.cells[5][2].get_mut_option_unit().take();
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Player));
|
||||
*board.cells[5][2].get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(Players::Enemy, (5, 2))));
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Player));
|
||||
board.cells[5][2].get_mut_option_unit().take();
|
||||
*board.cells[4][2].get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(Players::Player, (4, 2))));
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Player));
|
||||
assert_eq!(board.cells[5][1].get_structure(), original);
|
||||
assert!(!board.start_mine_construction((usize::MAX, 1), Players::Player));
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Unclaimed));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ore_exception_does_not_require_a_base_or_tunnel_connection() -> () {
|
||||
let mut board: BoardState = board();
|
||||
board.cells[0][0].set_structure(Structures::Ore(Ore::new(Players::Unclaimed, 1)));
|
||||
*board.cells[0][1].get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(Players::Player, (0, 1))));
|
||||
assert!(board.can_build_at((0, 0), Players::Player, BuildingTerrain::Ore));
|
||||
complete_mine(&mut board, (0, 0));
|
||||
assert_eq!(
|
||||
board.cells[0][0].get_structure().get_owner(),
|
||||
Players::Player
|
||||
);
|
||||
assert!(!board.start_mine_construction((0, 0), Players::Player));
|
||||
let mut board: BoardState = board();
|
||||
board.cells[0][0].set_structure(Structures::Ore(Ore::new(Players::Unclaimed, 1)));
|
||||
*board.cells[0][1].get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(Players::Player, (0, 1))));
|
||||
assert!(board.can_build_at((0, 0), Players::Player, BuildingTerrain::Ore));
|
||||
complete_mine(&mut board, (0, 0));
|
||||
assert_eq!(
|
||||
board.cells[0][0].get_structure().get_owner(),
|
||||
Players::Player
|
||||
);
|
||||
assert!(!board.start_mine_construction((0, 0), Players::Player));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ordinary_building_sites_require_stone_connected_to_base_or_tunnel() -> () {
|
||||
let mut board: BoardState = board();
|
||||
assert!(board.can_build_at((5, 3), Players::Player, BuildingTerrain::Stone));
|
||||
assert!(!board.can_build_at((5, 3), Players::Player, BuildingTerrain::Ore));
|
||||
*board.cells[5][5].get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(Players::Player, (5, 5))));
|
||||
assert!(!board.can_build_at((5, 4), Players::Player, BuildingTerrain::Stone));
|
||||
board.cells[5][5].set_structure(Structures::Tunnel(Tunnel::new()));
|
||||
assert!(board.can_build_at((5, 4), Players::Player, BuildingTerrain::Stone));
|
||||
board.cells[5][5].set_structure(Structures::Base(BaseBuilding::new(Players::Enemy)));
|
||||
assert!(!board.can_build_at((5, 4), Players::Player, BuildingTerrain::Stone));
|
||||
board.cells[5][3].set_structure(Structures::Tunnel(Tunnel::new()));
|
||||
assert!(!board.can_build_at((5, 3), Players::Player, BuildingTerrain::Stone));
|
||||
let mut board: BoardState = board();
|
||||
assert!(board.can_build_at((5, 3), Players::Player, BuildingTerrain::Stone));
|
||||
assert!(!board.can_build_at((5, 3), Players::Player, BuildingTerrain::Ore));
|
||||
*board.cells[5][5].get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(Players::Player, (5, 5))));
|
||||
assert!(!board.can_build_at((5, 4), Players::Player, BuildingTerrain::Stone));
|
||||
board.cells[5][5].set_structure(Structures::Tunnel(Tunnel::new()));
|
||||
assert!(board.can_build_at((5, 4), Players::Player, BuildingTerrain::Stone));
|
||||
board.cells[5][5].set_structure(Structures::Base(BaseBuilding::new(Players::Enemy)));
|
||||
assert!(!board.can_build_at((5, 4), Players::Player, BuildingTerrain::Stone));
|
||||
board.cells[5][3].set_structure(Structures::Tunnel(Tunnel::new()));
|
||||
assert!(!board.can_build_at((5, 3), Players::Player, BuildingTerrain::Stone));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn occupied_depleted_or_claimed_deposit_cannot_be_built_on() -> () {
|
||||
let mut board: BoardState = board();
|
||||
*board.cells[5][1].get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(Players::Player, (5, 1))));
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Player));
|
||||
board.cells[5][1].get_mut_option_unit().take();
|
||||
let mut ore: Ore = Ore::new(Players::Unclaimed, 1);
|
||||
ore.extract(5000);
|
||||
board.cells[5][1].set_structure(Structures::Ore(ore));
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Player));
|
||||
board.cells[5][1].set_structure(Structures::Ore(Ore::new(Players::Enemy, 1)));
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Player));
|
||||
let mut board: BoardState = board();
|
||||
*board.cells[5][1].get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(Players::Player, (5, 1))));
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Player));
|
||||
board.cells[5][1].get_mut_option_unit().take();
|
||||
let mut ore: Ore = Ore::new(Players::Unclaimed, 1);
|
||||
ore.extract(5000);
|
||||
board.cells[5][1].set_structure(Structures::Ore(ore));
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Player));
|
||||
board.cells[5][1].set_structure(Structures::Ore(Ore::new(Players::Enemy, 1)));
|
||||
assert!(!board.start_mine_construction((5, 1), Players::Player));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construction_view_starts_paid_build_on_enter_and_stays_open() -> () {
|
||||
let mut app: App = app();
|
||||
for _ in 0..2 {
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Left, KeyModifiers::NONE));
|
||||
}
|
||||
handle_keybindings(
|
||||
&mut app,
|
||||
KeyEvent::new(KeyCode::Char(' '), KeyModifiers::CONTROL),
|
||||
);
|
||||
assert!(available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Enter));
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(160, 50)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Ready to build: Enter"));
|
||||
assert!(text.contains("Enter - Build Mine"));
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
|
||||
let state: &SkirmishState = &app.states_ref().unwrap().skirmish;
|
||||
assert!(state.building_view);
|
||||
assert!(matches!(
|
||||
state.board.cells[5][1].get_structure(),
|
||||
Structures::MineConstruction(_)
|
||||
));
|
||||
assert_eq!(state.player_resources.wood, 30);
|
||||
assert_eq!(state.player_resources.metal, 15);
|
||||
let mut app: App = app();
|
||||
for _ in 0..2 {
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Left, KeyModifiers::NONE));
|
||||
}
|
||||
handle_keybindings(
|
||||
&mut app,
|
||||
KeyEvent::new(KeyCode::Char(' '), KeyModifiers::CONTROL),
|
||||
);
|
||||
assert!(
|
||||
available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Enter)
|
||||
);
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(160, 50)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Ready to build: Enter"));
|
||||
assert!(text.contains("Enter - Build Mine"));
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
|
||||
let state: &SkirmishState = &app.states_ref().unwrap().skirmish;
|
||||
assert!(state.building_view);
|
||||
assert!(matches!(
|
||||
state.board.cells[5][1].get_structure(),
|
||||
Structures::MineConstruction(_)
|
||||
));
|
||||
assert_eq!(state.player_resources.wood, 30);
|
||||
assert_eq!(state.player_resources.metal, 15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn miner_can_travel_into_and_out_of_friendly_mine_with_moving_tasks() -> () {
|
||||
let mut board: BoardState = board();
|
||||
complete_mine(&mut board, (5, 1));
|
||||
board.change_focused_cell(MoveFocusedCell::Left);
|
||||
board.toggle_marking();
|
||||
board.change_focused_cell(MoveFocusedCell::Left);
|
||||
board.toggle_marking();
|
||||
let unit: &Units = board.cells[5][2].get_ref_option_unit().as_ref().unwrap();
|
||||
assert!(matches!(
|
||||
unit.get_ref_tasks().front(),
|
||||
Some(Tasks::Moving(_))
|
||||
));
|
||||
for _ in 1..3000_u16.div_ceil(u16::from(TICK_MS)) {
|
||||
let mut board: BoardState = board();
|
||||
complete_mine(&mut board, (5, 1));
|
||||
board.change_focused_cell(MoveFocusedCell::Left);
|
||||
board.toggle_marking();
|
||||
board.change_focused_cell(MoveFocusedCell::Left);
|
||||
board.toggle_marking();
|
||||
let unit: &Units = board.cells[5][2].get_ref_option_unit().as_ref().unwrap();
|
||||
assert!(matches!(
|
||||
unit.get_ref_tasks().front(),
|
||||
Some(Tasks::Moving(_))
|
||||
));
|
||||
for _ in 1..3000_u16.div_ceil(u16::from(TICK_MS)) {
|
||||
board.advance_turn();
|
||||
}
|
||||
assert!(board.cells[5][1].get_ref_option_unit().is_none());
|
||||
board.advance_turn();
|
||||
}
|
||||
assert!(board.cells[5][1].get_ref_option_unit().is_none());
|
||||
board.advance_turn();
|
||||
assert_eq!(board.cells[5][1].get_ref_option_unit().get_coords(), (5, 1));
|
||||
assert!(board.cells[5][2].get_ref_option_unit().is_none());
|
||||
travel(&mut board, MoveFocusedCell::Right);
|
||||
assert!(board.cells[5][1].get_ref_option_unit().is_none());
|
||||
assert_eq!(board.cells[5][2].get_ref_option_unit().get_coords(), (5, 2));
|
||||
assert_eq!(board.cells[5][1].get_ref_option_unit().get_coords(), (5, 1));
|
||||
assert!(board.cells[5][2].get_ref_option_unit().is_none());
|
||||
travel(&mut board, MoveFocusedCell::Right);
|
||||
assert!(board.cells[5][1].get_ref_option_unit().is_none());
|
||||
assert_eq!(board.cells[5][2].get_ref_option_unit().get_coords(), (5, 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn staffed_mine_doubles_production_and_bonus_stops_after_departure() -> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
complete_mine(&mut game.board, (5, 1));
|
||||
game.turn_counter = MINE_PRODUCTION_TICKS - 1;
|
||||
game.tick_update();
|
||||
assert_eq!(game.player_resources.metal, 26);
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
travel(&mut game.board, MoveFocusedCell::Left);
|
||||
game.turn_counter = MINE_PRODUCTION_TICKS - 1;
|
||||
game.tick_update();
|
||||
assert_eq!(game.player_resources.metal, 28);
|
||||
travel(&mut game.board, MoveFocusedCell::Right);
|
||||
game.turn_counter = MINE_PRODUCTION_TICKS - 1;
|
||||
game.tick_update();
|
||||
assert_eq!(game.player_resources.metal, 29);
|
||||
let Structures::Mine(mine) = game.board.cells[5][1].get_structure() else {
|
||||
panic!("mine missing");
|
||||
};
|
||||
assert_eq!(mine.get_ore_amount(), 4996);
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
complete_mine(&mut game.board, (5, 1));
|
||||
game.turn_counter = MINE_PRODUCTION_TICKS - 1;
|
||||
game.tick_update();
|
||||
assert_eq!(game.player_resources.metal, 26);
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
travel(&mut game.board, MoveFocusedCell::Left);
|
||||
game.turn_counter = MINE_PRODUCTION_TICKS - 1;
|
||||
game.tick_update();
|
||||
assert_eq!(game.player_resources.metal, 28);
|
||||
travel(&mut game.board, MoveFocusedCell::Right);
|
||||
game.turn_counter = MINE_PRODUCTION_TICKS - 1;
|
||||
game.tick_update();
|
||||
assert_eq!(game.player_resources.metal, 29);
|
||||
let Structures::Mine(mine) = game.board.cells[5][1].get_structure() else {
|
||||
panic!("mine missing");
|
||||
};
|
||||
assert_eq!(mine.get_ore_amount(), 4996);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mine_rejects_enemy_miners_and_staffing_does_not_overdraw_ore() -> () {
|
||||
let mut ore: Ore = Ore::new(Players::Unclaimed, 1);
|
||||
ore.extract(4999);
|
||||
let mut mine: Mine = Mine::new(Players::Player, ore).unwrap();
|
||||
let player: Units = Units::Miner(MinerUnit::new(Players::Player, (5, 2)));
|
||||
let enemy: Units = Units::Miner(MinerUnit::new(Players::Enemy, (5, 2)));
|
||||
assert!(mine.accepts_unit(&player));
|
||||
assert!(!mine.accepts_unit(&enemy));
|
||||
assert!(!Structures::Mine(mine).can_enter(&enemy));
|
||||
assert_eq!(mine.produce_with_miner(true), 1);
|
||||
assert_eq!(mine.produce_with_miner(true), 0);
|
||||
let mut ore: Ore = Ore::new(Players::Unclaimed, 1);
|
||||
ore.extract(4999);
|
||||
let mut mine: Mine = Mine::new(Players::Player, ore).unwrap();
|
||||
let player: Units = Units::Miner(MinerUnit::new(Players::Player, (5, 2)));
|
||||
let enemy: Units = Units::Miner(MinerUnit::new(Players::Enemy, (5, 2)));
|
||||
assert!(mine.accepts_unit(&player));
|
||||
assert!(!mine.accepts_unit(&enemy));
|
||||
assert!(!Structures::Mine(mine).can_enter(&enemy));
|
||||
assert_eq!(mine.produce_with_miner(true), 1);
|
||||
assert_eq!(mine.produce_with_miner(true), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_ore_cannot_start_construction_or_consume_resources() -> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
let ore: Structures = Structures::Ore(Ore::new(Players::Unclaimed, 1));
|
||||
game.board.cells[5][21].set_structure(ore);
|
||||
let before: Resources = game.player_resources;
|
||||
assert!(!game.start_mine_construction((5, 21), Players::Player));
|
||||
assert_eq!(game.player_resources, before);
|
||||
assert_eq!(game.board.cells[5][21].get_structure(), ore);
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
let ore: Structures = Structures::Ore(Ore::new(Players::Unclaimed, 1));
|
||||
game.board.cells[5][21].set_structure(ore);
|
||||
let before: Resources = game.player_resources;
|
||||
assert!(!game.start_mine_construction((5, 21), Players::Player));
|
||||
assert_eq!(game.player_resources, before);
|
||||
assert_eq!(game.board.cells[5][21].get_structure(), ore);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construction_payment_is_atomic_and_exact_funds_are_enough() -> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
for resources in [
|
||||
Resources {
|
||||
wood: 19,
|
||||
metal: 10,
|
||||
},
|
||||
Resources { wood: 20, metal: 9 },
|
||||
] {
|
||||
game.player_resources = resources;
|
||||
assert!(!game.can_start_mine_construction((5, 1), Players::Player));
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
for resources in [
|
||||
Resources {
|
||||
wood: 19,
|
||||
metal: 10,
|
||||
},
|
||||
Resources { wood: 20, metal: 9 },
|
||||
] {
|
||||
game.player_resources = resources;
|
||||
assert!(!game.can_start_mine_construction((5, 1), Players::Player));
|
||||
assert!(!game.start_mine_construction((5, 1), Players::Player));
|
||||
assert_eq!(game.player_resources, resources);
|
||||
assert!(matches!(
|
||||
game.board.cells[5][1].get_structure(),
|
||||
Structures::Ore(_)
|
||||
));
|
||||
}
|
||||
game.player_resources = Mine::COST;
|
||||
assert!(game.start_mine_construction((5, 1), Players::Player));
|
||||
assert_eq!(game.player_resources, Resources { wood: 0, metal: 0 });
|
||||
assert!(!game.start_mine_construction((5, 1), Players::Player));
|
||||
assert_eq!(game.player_resources, resources);
|
||||
assert!(matches!(
|
||||
game.board.cells[5][1].get_structure(),
|
||||
Structures::Ore(_)
|
||||
));
|
||||
}
|
||||
game.player_resources = Mine::COST;
|
||||
assert!(game.start_mine_construction((5, 1), Players::Player));
|
||||
assert_eq!(game.player_resources, Resources { wood: 0, metal: 0 });
|
||||
assert!(!game.start_mine_construction((5, 1), Players::Player));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construction_takes_ten_seconds_and_cannot_produce_or_accept_miners_early() -> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
assert!(game.start_mine_construction((5, 1), Players::Player));
|
||||
for _ in 1..MineConstruction::BUILD_TICKS {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
assert!(game.start_mine_construction((5, 1), Players::Player));
|
||||
for _ in 1..MineConstruction::BUILD_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
let structure: Structures = game.board.cells[5][1].get_structure();
|
||||
let Structures::MineConstruction(site) = structure else {
|
||||
panic!("completed too early");
|
||||
};
|
||||
assert_eq!(site.progress_percent(), 99);
|
||||
assert!(site.into_mine().is_none());
|
||||
assert_eq!(site.cancel().get_ore_amount(), 5000);
|
||||
assert!(
|
||||
!structure.can_enter(
|
||||
game.board.cells[5][2]
|
||||
.get_ref_option_unit()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
)
|
||||
);
|
||||
assert_eq!(game.player_resources.metal, 15);
|
||||
game.tick_update();
|
||||
}
|
||||
let structure: Structures = game.board.cells[5][1].get_structure();
|
||||
let Structures::MineConstruction(site) = structure else {
|
||||
panic!("completed too early");
|
||||
};
|
||||
assert_eq!(site.progress_percent(), 99);
|
||||
assert!(site.into_mine().is_none());
|
||||
assert_eq!(site.cancel().get_ore_amount(), 5000);
|
||||
assert!(
|
||||
!structure.can_enter(
|
||||
game.board.cells[5][2]
|
||||
.get_ref_option_unit()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
)
|
||||
);
|
||||
assert_eq!(game.player_resources.metal, 15);
|
||||
game.tick_update();
|
||||
assert!(matches!(
|
||||
game.board.cells[5][1].get_structure(),
|
||||
Structures::Mine(_)
|
||||
));
|
||||
assert_eq!(game.player_resources.metal, 15);
|
||||
assert!(matches!(
|
||||
game.board.cells[5][1].get_structure(),
|
||||
Structures::Mine(_)
|
||||
));
|
||||
assert_eq!(game.player_resources.metal, 15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn construction_pauses_without_nearby_builder_and_resumes_after_return() -> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
assert!(game.start_mine_construction((5, 1), Players::Player));
|
||||
for _ in 0..100 {
|
||||
game.tick_update();
|
||||
}
|
||||
let before: Structures = game.board.cells[5][1].get_structure();
|
||||
let builder: Option<Units> = game.board.cells[5][2].get_mut_option_unit().take();
|
||||
for _ in 0..MineConstruction::BUILD_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(game.board.cells[5][1].get_structure(), before);
|
||||
assert_eq!(game.player_resources.metal, 15);
|
||||
*game.board.cells[5][2].get_mut_option_unit() = builder;
|
||||
for _ in 100..MineConstruction::BUILD_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert!(matches!(
|
||||
game.board.cells[5][1].get_structure(),
|
||||
Structures::Mine(_)
|
||||
));
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
assert!(game.start_mine_construction((5, 1), Players::Player));
|
||||
for _ in 0..100 {
|
||||
game.tick_update();
|
||||
}
|
||||
let before: Structures = game.board.cells[5][1].get_structure();
|
||||
let builder: Option<Units> = game.board.cells[5][2].get_mut_option_unit().take();
|
||||
for _ in 0..MineConstruction::BUILD_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(game.board.cells[5][1].get_structure(), before);
|
||||
assert_eq!(game.player_resources.metal, 15);
|
||||
*game.board.cells[5][2].get_mut_option_unit() = builder;
|
||||
for _ in 100..MineConstruction::BUILD_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert!(matches!(
|
||||
game.board.cells[5][1].get_structure(),
|
||||
Structures::Mine(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancellation_restores_original_ore_and_refunds_cost_exactly_once() -> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
let mut ore: Ore = Ore::new(Players::Unclaimed, 1);
|
||||
ore.extract(123);
|
||||
game.board.cells[5][1].set_structure(Structures::Ore(ore));
|
||||
let before: Resources = game.player_resources;
|
||||
assert!(game.start_mine_construction((5, 1), Players::Player));
|
||||
for _ in 0..100 {
|
||||
game.tick_update();
|
||||
}
|
||||
game.board.cells[5][2].get_mut_option_unit().take();
|
||||
assert!(game.cancel_mine_construction((5, 1), Players::Player));
|
||||
assert_eq!(game.player_resources, before);
|
||||
assert_eq!(game.board.cells[5][1].get_structure(), Structures::Ore(ore));
|
||||
assert!(!game.cancel_mine_construction((5, 1), Players::Player));
|
||||
assert_eq!(game.player_resources, before);
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
let mut ore: Ore = Ore::new(Players::Unclaimed, 1);
|
||||
ore.extract(123);
|
||||
game.board.cells[5][1].set_structure(Structures::Ore(ore));
|
||||
let before: Resources = game.player_resources;
|
||||
assert!(game.start_mine_construction((5, 1), Players::Player));
|
||||
for _ in 0..100 {
|
||||
game.tick_update();
|
||||
}
|
||||
game.board.cells[5][2].get_mut_option_unit().take();
|
||||
assert!(game.cancel_mine_construction((5, 1), Players::Player));
|
||||
assert_eq!(game.player_resources, before);
|
||||
assert_eq!(game.board.cells[5][1].get_structure(), Structures::Ore(ore));
|
||||
assert!(!game.cancel_mine_construction((5, 1), Players::Player));
|
||||
assert_eq!(game.player_resources, before);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn panel_shows_progress_and_delete_cancels_while_escape_only_closes_panel() -> () {
|
||||
let mut app: App = app();
|
||||
for _ in 0..2 {
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Left, KeyModifiers::NONE));
|
||||
}
|
||||
handle_keybindings(
|
||||
&mut app,
|
||||
KeyEvent::new(KeyCode::Char(' '), KeyModifiers::CONTROL),
|
||||
);
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
|
||||
assert!(
|
||||
available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Delete)
|
||||
);
|
||||
assert!(
|
||||
!available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Enter)
|
||||
);
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
|
||||
assert_eq!(app.states_ref().unwrap().skirmish.player_resources.wood, 30);
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(160, 50)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Cost: 20 Wood + 10 Metal"));
|
||||
assert!(text.contains("Construction: 0%"));
|
||||
assert!(text.contains("Delete - Cancel construction"));
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE));
|
||||
assert!(matches!(
|
||||
app.states_ref().unwrap().skirmish.board.cells[5][1].get_structure(),
|
||||
Structures::MineConstruction(_)
|
||||
));
|
||||
handle_keybindings(
|
||||
&mut app,
|
||||
KeyEvent::new(KeyCode::Char(' '), KeyModifiers::CONTROL),
|
||||
);
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Delete, KeyModifiers::NONE));
|
||||
assert!(matches!(
|
||||
app.states_ref().unwrap().skirmish.board.cells[5][1].get_structure(),
|
||||
Structures::Ore(_)
|
||||
));
|
||||
assert_eq!(
|
||||
app.states_ref().unwrap().skirmish.player_resources,
|
||||
Resources::new(50, 25)
|
||||
);
|
||||
let mut app: App = app();
|
||||
for _ in 0..2 {
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Left, KeyModifiers::NONE));
|
||||
}
|
||||
handle_keybindings(
|
||||
&mut app,
|
||||
KeyEvent::new(KeyCode::Char(' '), KeyModifiers::CONTROL),
|
||||
);
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
|
||||
assert!(
|
||||
available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Delete)
|
||||
);
|
||||
assert!(
|
||||
!available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Enter)
|
||||
);
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Enter, KeyModifiers::NONE));
|
||||
assert_eq!(app.states_ref().unwrap().skirmish.player_resources.wood, 30);
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(160, 50)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Cost: 20 Wood + 10 Metal"));
|
||||
assert!(text.contains("Construction: 0%"));
|
||||
assert!(text.contains("Delete - Cancel construction"));
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Esc, KeyModifiers::NONE));
|
||||
assert!(matches!(
|
||||
app.states_ref().unwrap().skirmish.board.cells[5][1].get_structure(),
|
||||
Structures::MineConstruction(_)
|
||||
));
|
||||
handle_keybindings(
|
||||
&mut app,
|
||||
KeyEvent::new(KeyCode::Char(' '), KeyModifiers::CONTROL),
|
||||
);
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Delete, KeyModifiers::NONE));
|
||||
assert!(matches!(
|
||||
app.states_ref().unwrap().skirmish.board.cells[5][1].get_structure(),
|
||||
Structures::Ore(_)
|
||||
));
|
||||
assert_eq!(
|
||||
app.states_ref().unwrap().skirmish.player_resources,
|
||||
Resources::new(50, 25)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn player_cannot_cancel_enemy_site_or_refund_a_completed_mine() -> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
assert!(game.start_mine_construction((5, 40), Players::Enemy));
|
||||
assert!(!game.cancel_mine_construction((5, 40), Players::Player));
|
||||
assert_eq!(game.player_resources, Resources::new(50, 25));
|
||||
for _ in 0..MineConstruction::BUILD_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert!(!game.cancel_mine_construction((5, 40), Players::Enemy));
|
||||
assert_eq!(game.enemy_resources.metal, 15);
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
assert!(game.start_mine_construction((5, 40), Players::Enemy));
|
||||
assert!(!game.cancel_mine_construction((5, 40), Players::Player));
|
||||
assert_eq!(game.player_resources, Resources::new(50, 25));
|
||||
for _ in 0..MineConstruction::BUILD_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert!(!game.cancel_mine_construction((5, 40), Players::Enemy));
|
||||
assert_eq!(game.enemy_resources.metal, 15);
|
||||
}
|
||||
+131
-131
@@ -1,160 +1,160 @@
|
||||
use clap::Parser;
|
||||
use ratatui::{
|
||||
Terminal,
|
||||
backend::TestBackend,
|
||||
buffer::Cell,
|
||||
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
||||
layout::Rect,
|
||||
Terminal,
|
||||
backend::TestBackend,
|
||||
buffer::Cell,
|
||||
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
||||
layout::Rect,
|
||||
};
|
||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||
use war_in_tunnels::{
|
||||
app::{
|
||||
Action, App, GameStates, View, available_skirmish_actions, handle_keybindings,
|
||||
states::skirmish_states::structures::Structures, threads::AudioCmd,
|
||||
},
|
||||
cli::Cli,
|
||||
app::{
|
||||
Action, App, GameStates, View, available_skirmish_actions, handle_keybindings,
|
||||
states::skirmish_states::structures::Structures, threads::AudioCmd,
|
||||
},
|
||||
cli::Cli,
|
||||
};
|
||||
|
||||
fn app() -> App {
|
||||
let args: Cli = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
||||
let (tx, _rx): (Sender<AudioCmd>, Receiver<AudioCmd>) = channel();
|
||||
let mut app: App = App::new(args, tx);
|
||||
app.view = View::Skirmish;
|
||||
app.window_area = Rect::new(0, 0, 160, 40);
|
||||
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
||||
app
|
||||
let args: Cli = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
||||
let (tx, _rx): (Sender<AudioCmd>, Receiver<AudioCmd>) = channel();
|
||||
let mut app: App = App::new(args, tx);
|
||||
app.view = View::Skirmish;
|
||||
app.window_area = Rect::new(0, 0, 160, 40);
|
||||
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
||||
app
|
||||
}
|
||||
|
||||
fn press(app: &mut App, code: KeyCode, modifiers: KeyModifiers) -> () {
|
||||
handle_keybindings(app, KeyEvent::new(code, modifiers));
|
||||
handle_keybindings(app, KeyEvent::new(code, modifiers));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selecting_miner_switches_help_and_escape_cancels_only_the_route() -> () {
|
||||
let mut app: App = app();
|
||||
assert!(
|
||||
!available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Space)
|
||||
);
|
||||
press(&mut app, KeyCode::Left, KeyModifiers::NONE);
|
||||
assert!(available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Space));
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::NONE);
|
||||
let actions: Vec<Action> = available_skirmish_actions(&app.states_ref().unwrap().skirmish);
|
||||
assert!(actions.contains(&Action::Backspace));
|
||||
assert!(actions.contains(&Action::Delete));
|
||||
assert!(!actions.contains(&Action::Building));
|
||||
assert!(!actions.contains(&Action::ZoomIn));
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::CONTROL);
|
||||
assert!(!app.states_ref().unwrap().skirmish.building_view);
|
||||
press(&mut app, KeyCode::Esc, KeyModifiers::NONE);
|
||||
assert_eq!(app.view, View::Skirmish);
|
||||
assert!(
|
||||
!app
|
||||
.states_ref()
|
||||
.unwrap()
|
||||
.skirmish
|
||||
.board
|
||||
.marked_cells
|
||||
.marking_cells
|
||||
);
|
||||
assert!(
|
||||
app
|
||||
.states_ref()
|
||||
.unwrap()
|
||||
.skirmish
|
||||
.board
|
||||
.get_marked_cells()
|
||||
.is_empty()
|
||||
);
|
||||
let mut app: App = app();
|
||||
assert!(
|
||||
!available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Space)
|
||||
);
|
||||
press(&mut app, KeyCode::Left, KeyModifiers::NONE);
|
||||
assert!(
|
||||
available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Space)
|
||||
);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::NONE);
|
||||
let actions: Vec<Action> = available_skirmish_actions(&app.states_ref().unwrap().skirmish);
|
||||
assert!(actions.contains(&Action::Backspace));
|
||||
assert!(actions.contains(&Action::Delete));
|
||||
assert!(!actions.contains(&Action::Building));
|
||||
assert!(!actions.contains(&Action::ZoomIn));
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::CONTROL);
|
||||
assert!(!app.states_ref().unwrap().skirmish.building_view);
|
||||
press(&mut app, KeyCode::Esc, KeyModifiers::NONE);
|
||||
assert_eq!(app.view, View::Skirmish);
|
||||
assert!(
|
||||
!app.states_ref()
|
||||
.unwrap()
|
||||
.skirmish
|
||||
.board
|
||||
.marked_cells
|
||||
.marking_cells
|
||||
);
|
||||
assert!(
|
||||
app.states_ref()
|
||||
.unwrap()
|
||||
.skirmish
|
||||
.board
|
||||
.get_marked_cells()
|
||||
.is_empty()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn building_view_has_its_own_help_and_does_not_build_on_invalid_tile() -> () {
|
||||
let mut app: App = app();
|
||||
let before: war_in_tunnels::app::states::skirmish_states::BoardState =
|
||||
app.states_ref().unwrap().skirmish.board.clone();
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::CONTROL);
|
||||
assert!(app.states_ref().unwrap().skirmish.building_view);
|
||||
assert_eq!(
|
||||
available_skirmish_actions(&app.states_ref().unwrap().skirmish),
|
||||
[Action::Building, Action::Esc, Action::Quit2]
|
||||
);
|
||||
press(&mut app, KeyCode::Left, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Enter, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::NONE);
|
||||
assert_eq!(app.states_ref().unwrap().skirmish.board, before);
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(160, 40)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Construction unavailable"));
|
||||
assert!(text.contains("Close buildings"));
|
||||
assert!(!text.contains("Marking cells"));
|
||||
press(&mut app, KeyCode::Esc, KeyModifiers::NONE);
|
||||
assert!(!app.states_ref().unwrap().skirmish.building_view);
|
||||
assert_eq!(app.view, View::Skirmish);
|
||||
for row in &app.states_ref().unwrap().skirmish.board.cells {
|
||||
for cell in row {
|
||||
assert!(!matches!(cell.get_structure(), Structures::Mine(_)));
|
||||
let mut app: App = app();
|
||||
let before: war_in_tunnels::app::states::skirmish_states::BoardState =
|
||||
app.states_ref().unwrap().skirmish.board.clone();
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::CONTROL);
|
||||
assert!(app.states_ref().unwrap().skirmish.building_view);
|
||||
assert_eq!(
|
||||
available_skirmish_actions(&app.states_ref().unwrap().skirmish),
|
||||
[Action::Building, Action::Esc, Action::Quit2]
|
||||
);
|
||||
press(&mut app, KeyCode::Left, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Enter, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::NONE);
|
||||
assert_eq!(app.states_ref().unwrap().skirmish.board, before);
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(160, 40)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Construction unavailable"));
|
||||
assert!(text.contains("Close buildings"));
|
||||
assert!(!text.contains("Marking cells"));
|
||||
press(&mut app, KeyCode::Esc, KeyModifiers::NONE);
|
||||
assert!(!app.states_ref().unwrap().skirmish.building_view);
|
||||
assert_eq!(app.view, View::Skirmish);
|
||||
for row in &app.states_ref().unwrap().skirmish.board.cells {
|
||||
for cell in row {
|
||||
assert!(!matches!(cell.get_structure(), Structures::Mine(_)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_control_space_and_quit_work_in_building_view() -> () {
|
||||
let mut app: App = app();
|
||||
press(&mut app, KeyCode::Null, KeyModifiers::NONE);
|
||||
assert!(app.states_ref().unwrap().skirmish.building_view);
|
||||
press(&mut app, KeyCode::Null, KeyModifiers::CONTROL);
|
||||
assert!(!app.states_ref().unwrap().skirmish.building_view);
|
||||
press(&mut app, KeyCode::Null, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char('c'), KeyModifiers::CONTROL);
|
||||
assert!(app.exit);
|
||||
let mut app: App = app();
|
||||
press(&mut app, KeyCode::Null, KeyModifiers::NONE);
|
||||
assert!(app.states_ref().unwrap().skirmish.building_view);
|
||||
press(&mut app, KeyCode::Null, KeyModifiers::CONTROL);
|
||||
assert!(!app.states_ref().unwrap().skirmish.building_view);
|
||||
press(&mut app, KeyCode::Null, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char('c'), KeyModifiers::CONTROL);
|
||||
assert!(app.exit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn contextual_help_fits_normal_terminal_and_changes_when_route_is_selected() -> () {
|
||||
let mut app: App = app();
|
||||
app.window_area = Rect::new(0, 0, 120, 40);
|
||||
press(&mut app, KeyCode::Left, KeyModifiers::NONE);
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(120, 40)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Ctrl + Space - Build"));
|
||||
assert!(text.contains("Space - Select miner"));
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::NONE);
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Space - Confirm route"));
|
||||
assert!(text.contains("Esc - Cancel route"));
|
||||
assert!(!text.contains("Ctrl + Space"));
|
||||
assert!(!text.contains("Zoom in"));
|
||||
let mut app: App = app();
|
||||
app.window_area = Rect::new(0, 0, 120, 40);
|
||||
press(&mut app, KeyCode::Left, KeyModifiers::NONE);
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(120, 40)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Ctrl + Space - Build"));
|
||||
assert!(text.contains("Space - Select miner"));
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::NONE);
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Space - Confirm route"));
|
||||
assert!(text.contains("Esc - Cancel route"));
|
||||
assert!(!text.contains("Ctrl + Space"));
|
||||
assert!(!text.contains("Zoom in"));
|
||||
}
|
||||
+427
-427
@@ -1,534 +1,534 @@
|
||||
use clap::Parser;
|
||||
use ratatui::{
|
||||
Terminal,
|
||||
backend::TestBackend,
|
||||
buffer::Buffer,
|
||||
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
||||
layout::Rect,
|
||||
widgets::Widget,
|
||||
Terminal,
|
||||
backend::TestBackend,
|
||||
buffer::Buffer,
|
||||
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
||||
layout::Rect,
|
||||
widgets::Widget,
|
||||
};
|
||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||
use war_in_tunnels::{
|
||||
app::{
|
||||
App, GameStates, View, handle_keybindings,
|
||||
states::{
|
||||
SkirmishState,
|
||||
skirmish_states::{
|
||||
BoardState, CellSizes, MoveFocusedCell, Players, ZoomLevel,
|
||||
structures::{Structure, Structures, Tunnel},
|
||||
tasks::{DiggingTask, MovingTask, Task, Tasks},
|
||||
units::{MinerUnit, Unit, Units},
|
||||
},
|
||||
app::{
|
||||
App, GameStates, View, handle_keybindings,
|
||||
states::{
|
||||
SkirmishState,
|
||||
skirmish_states::{
|
||||
BoardState, CellSizes, MoveFocusedCell, Players, ZoomLevel,
|
||||
structures::{Structure, Structures, Tunnel},
|
||||
tasks::{DiggingTask, MovingTask, Task, Tasks},
|
||||
units::{MinerUnit, Unit, Units},
|
||||
},
|
||||
},
|
||||
threads::{AudioCmd, TICK_MS},
|
||||
},
|
||||
threads::{AudioCmd, TICK_MS},
|
||||
},
|
||||
cli::Cli,
|
||||
cli::Cli,
|
||||
};
|
||||
|
||||
fn game() -> SkirmishState {
|
||||
let args = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
||||
GameStates::new(&args, &Rect::new(0, 0, 120, 40)).skirmish
|
||||
let args = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
||||
GameStates::new(&args, &Rect::new(0, 0, 120, 40)).skirmish
|
||||
}
|
||||
|
||||
fn queue(board: &mut BoardState, origin: (usize, usize), path: &[(usize, usize)]) {
|
||||
let unit = board.cells[origin.0][origin.1]
|
||||
.get_mut_option_unit()
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let power = unit.get_digging_power();
|
||||
unit.set_task(Tasks::Digging(DiggingTask::new(
|
||||
path.iter().copied().collect(),
|
||||
power,
|
||||
)));
|
||||
let unit = board.cells[origin.0][origin.1]
|
||||
.get_mut_option_unit()
|
||||
.as_mut()
|
||||
.unwrap();
|
||||
let power = unit.get_digging_power();
|
||||
unit.set_task(Tasks::Digging(DiggingTask::new(
|
||||
path.iter().copied().collect(),
|
||||
power,
|
||||
)));
|
||||
}
|
||||
|
||||
fn tunnel(board: &mut BoardState, row: usize, col: usize) {
|
||||
board.cells[row][col].set_structure(Structures::Tunnel(Tunnel::new()));
|
||||
board.cells[row][col].set_structure(Structures::Tunnel(Tunnel::new()));
|
||||
}
|
||||
|
||||
fn unit(board: &BoardState, row: usize, col: usize) -> &Units {
|
||||
board.cells[row][col]
|
||||
.get_ref_option_unit()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
board.cells[row][col]
|
||||
.get_ref_option_unit()
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn travel_ticks() -> u16 {
|
||||
3000_u16.div_ceil(u16::from(TICK_MS))
|
||||
3000_u16.div_ceil(u16::from(TICK_MS))
|
||||
}
|
||||
|
||||
fn advance(game: &mut SkirmishState, ticks: u16) {
|
||||
for _ in 0..ticks {
|
||||
game.tick_update();
|
||||
}
|
||||
for _ in 0..ticks {
|
||||
game.tick_update();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tick_damages_stone_before_moving_and_finishes_entire_route() {
|
||||
let mut game = game();
|
||||
queue(&mut game.board, (5, 2), &[(5, 3), (5, 4)]);
|
||||
game.tick_update();
|
||||
assert_eq!(game.turn_counter, 1);
|
||||
let durability = game.board.cells[5][3].get_structure().get_durability();
|
||||
assert!(durability < 1000 && durability > 0);
|
||||
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
||||
assert!(game.board.cells[5][3].get_ref_option_unit().is_none());
|
||||
for _ in 0..500 {
|
||||
let mut game = game();
|
||||
queue(&mut game.board, (5, 2), &[(5, 3), (5, 4)]);
|
||||
game.tick_update();
|
||||
}
|
||||
for col in [3, 4] {
|
||||
assert_eq!(game.turn_counter, 1);
|
||||
let durability = game.board.cells[5][3].get_structure().get_durability();
|
||||
assert!(durability < 1000 && durability > 0);
|
||||
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
||||
assert!(game.board.cells[5][3].get_ref_option_unit().is_none());
|
||||
for _ in 0..500 {
|
||||
game.tick_update();
|
||||
}
|
||||
for col in [3, 4] {
|
||||
assert!(matches!(
|
||||
game.board.cells[5][col].get_structure(),
|
||||
Structures::Tunnel(_)
|
||||
));
|
||||
}
|
||||
assert!(matches!(
|
||||
game.board.cells[5][col].get_structure(),
|
||||
Structures::Tunnel(_)
|
||||
game.board.cells[5][2].get_structure(),
|
||||
Structures::Base(_)
|
||||
));
|
||||
}
|
||||
assert!(matches!(
|
||||
game.board.cells[5][2].get_structure(),
|
||||
Structures::Base(_)
|
||||
));
|
||||
assert_eq!(unit(&game.board, 5, 4).get_coords(), (5, 4));
|
||||
assert!(unit(&game.board, 5, 4).get_ref_tasks().is_empty());
|
||||
assert!(game.board.cells[5][2].get_ref_option_unit().is_none());
|
||||
assert!(game.board.cells[5][3].get_ref_option_unit().is_none());
|
||||
assert_eq!(unit(&game.board, 5, 4).get_coords(), (5, 4));
|
||||
assert!(unit(&game.board, 5, 4).get_ref_tasks().is_empty());
|
||||
assert!(game.board.cells[5][2].get_ref_option_unit().is_none());
|
||||
assert!(game.board.cells[5][3].get_ref_option_unit().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn each_tunnel_step_takes_three_seconds_in_both_directions() {
|
||||
for path in [[(5, 3), (5, 4)], [(4, 2), (3, 2)]] {
|
||||
let mut game = game();
|
||||
for (row, col) in path {
|
||||
tunnel(&mut game.board, row, col);
|
||||
for path in [[(5, 3), (5, 4)], [(4, 2), (3, 2)]] {
|
||||
let mut game = game();
|
||||
for (row, col) in path {
|
||||
tunnel(&mut game.board, row, col);
|
||||
}
|
||||
queue(&mut game.board, (5, 2), &path);
|
||||
advance(&mut game, travel_ticks() - 1);
|
||||
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
||||
game.tick_update();
|
||||
assert_eq!(
|
||||
unit(&game.board, path[0].0, path[0].1).get_coords(),
|
||||
path[0]
|
||||
);
|
||||
assert_eq!(
|
||||
unit(&game.board, path[0].0, path[0].1).get_ref_tasks()[0]
|
||||
.get_path()
|
||||
.len(),
|
||||
1
|
||||
);
|
||||
advance(&mut game, travel_ticks() - 1);
|
||||
assert_eq!(
|
||||
unit(&game.board, path[0].0, path[0].1).get_coords(),
|
||||
path[0]
|
||||
);
|
||||
game.tick_update();
|
||||
assert!(
|
||||
unit(&game.board, path[1].0, path[1].1)
|
||||
.get_ref_tasks()
|
||||
.is_empty()
|
||||
);
|
||||
}
|
||||
queue(&mut game.board, (5, 2), &path);
|
||||
advance(&mut game, travel_ticks() - 1);
|
||||
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
||||
game.tick_update();
|
||||
assert_eq!(
|
||||
unit(&game.board, path[0].0, path[0].1).get_coords(),
|
||||
path[0]
|
||||
);
|
||||
assert_eq!(
|
||||
unit(&game.board, path[0].0, path[0].1).get_ref_tasks()[0]
|
||||
.get_path()
|
||||
.len(),
|
||||
1
|
||||
);
|
||||
advance(&mut game, travel_ticks() - 1);
|
||||
assert_eq!(
|
||||
unit(&game.board, path[0].0, path[0].1).get_coords(),
|
||||
path[0]
|
||||
);
|
||||
game.tick_update();
|
||||
assert!(
|
||||
unit(&game.board, path[1].0, path[1].1)
|
||||
.get_ref_tasks()
|
||||
.is_empty()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn queued_tasks_execute_in_order_and_keep_unit_coordinates_current() {
|
||||
let mut game = game();
|
||||
tunnel(&mut game.board, 5, 3);
|
||||
tunnel(&mut game.board, 5, 4);
|
||||
queue(&mut game.board, (5, 2), &[(5, 3)]);
|
||||
queue(&mut game.board, (5, 2), &[(5, 4)]);
|
||||
advance(&mut game, travel_ticks());
|
||||
assert_eq!(unit(&game.board, 5, 3).get_ref_tasks().len(), 1);
|
||||
advance(&mut game, travel_ticks());
|
||||
assert_eq!(unit(&game.board, 5, 4).get_coords(), (5, 4));
|
||||
assert!(unit(&game.board, 5, 4).get_ref_tasks().is_empty());
|
||||
queue(&mut game.board, (5, 4), &[(5, 3), (5, 2)]);
|
||||
advance(&mut game, 2 * travel_ticks() - 1);
|
||||
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
||||
game.tick_update();
|
||||
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
||||
assert!(matches!(
|
||||
game.board.cells[5][2].get_structure(),
|
||||
Structures::Base(_)
|
||||
));
|
||||
let mut game = game();
|
||||
tunnel(&mut game.board, 5, 3);
|
||||
tunnel(&mut game.board, 5, 4);
|
||||
queue(&mut game.board, (5, 2), &[(5, 3)]);
|
||||
queue(&mut game.board, (5, 2), &[(5, 4)]);
|
||||
advance(&mut game, travel_ticks());
|
||||
assert_eq!(unit(&game.board, 5, 3).get_ref_tasks().len(), 1);
|
||||
advance(&mut game, travel_ticks());
|
||||
assert_eq!(unit(&game.board, 5, 4).get_coords(), (5, 4));
|
||||
assert!(unit(&game.board, 5, 4).get_ref_tasks().is_empty());
|
||||
queue(&mut game.board, (5, 4), &[(5, 3), (5, 2)]);
|
||||
advance(&mut game, 2 * travel_ticks() - 1);
|
||||
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
||||
game.tick_update();
|
||||
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
||||
assert!(matches!(
|
||||
game.board.cells[5][2].get_structure(),
|
||||
Structures::Base(_)
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn occupied_tunnel_waits_without_losing_units_or_task() {
|
||||
let mut game = game();
|
||||
tunnel(&mut game.board, 5, 3);
|
||||
*game.board.cells[5][3].get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(Players::Player, (5, 3))));
|
||||
queue(&mut game.board, (5, 2), &[(5, 3)]);
|
||||
for _ in 0..3 {
|
||||
let mut game = game();
|
||||
tunnel(&mut game.board, 5, 3);
|
||||
*game.board.cells[5][3].get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(Players::Player, (5, 3))));
|
||||
queue(&mut game.board, (5, 2), &[(5, 3)]);
|
||||
for _ in 0..3 {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(unit(&game.board, 5, 2).get_ref_tasks().len(), 1);
|
||||
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
||||
game.board.cells[5][3].get_mut_option_unit().take();
|
||||
advance(&mut game, travel_ticks() - 1);
|
||||
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(unit(&game.board, 5, 2).get_ref_tasks().len(), 1);
|
||||
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
||||
game.board.cells[5][3].get_mut_option_unit().take();
|
||||
advance(&mut game, travel_ticks() - 1);
|
||||
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
||||
game.tick_update();
|
||||
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
||||
assert!(unit(&game.board, 5, 3).get_ref_tasks().is_empty());
|
||||
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
||||
assert!(unit(&game.board, 5, 3).get_ref_tasks().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ore_is_not_destroyed_by_digging_task() {
|
||||
let mut game = game();
|
||||
let ore = game.board.cells[5][1].get_structure();
|
||||
queue(&mut game.board, (5, 2), &[(5, 1)]);
|
||||
for _ in 0..200 {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(game.board.cells[5][1].get_structure(), ore);
|
||||
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
||||
let mut game = game();
|
||||
let ore = game.board.cells[5][1].get_structure();
|
||||
queue(&mut game.board, (5, 2), &[(5, 1)]);
|
||||
for _ in 0..200 {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(game.board.cells[5][1].get_structure(), ore);
|
||||
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_or_empty_routes_do_not_teleport_or_remove_miner() {
|
||||
for path in [vec![], vec![(usize::MAX, usize::MAX)], vec![(0, 0)]] {
|
||||
let mut game = game();
|
||||
queue(&mut game.board, (5, 2), &path);
|
||||
game.tick_update();
|
||||
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
||||
assert!(unit(&game.board, 5, 2).get_ref_tasks().is_empty());
|
||||
}
|
||||
for path in [vec![], vec![(usize::MAX, usize::MAX)], vec![(0, 0)]] {
|
||||
let mut game = game();
|
||||
queue(&mut game.board, (5, 2), &path);
|
||||
game.tick_update();
|
||||
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
||||
assert!(unit(&game.board, 5, 2).get_ref_tasks().is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_player_miner_can_start_drawing_route() {
|
||||
let mut game = game();
|
||||
game.board.toggle_marking();
|
||||
assert!(!game.board.marked_cells.marking_cells);
|
||||
for _ in 0..36 {
|
||||
game.board.change_focused_cell(MoveFocusedCell::Right);
|
||||
}
|
||||
assert_eq!(game.board.get_focused_cell().get_coords(), (5, 39));
|
||||
game.board.toggle_marking();
|
||||
assert!(!game.board.marked_cells.marking_cells);
|
||||
assert!(game.board.get_marked_cells().is_empty());
|
||||
let mut game = game();
|
||||
game.board.toggle_marking();
|
||||
assert!(!game.board.marked_cells.marking_cells);
|
||||
for _ in 0..36 {
|
||||
game.board.change_focused_cell(MoveFocusedCell::Right);
|
||||
}
|
||||
assert_eq!(game.board.get_focused_cell().get_coords(), (5, 39));
|
||||
game.board.toggle_marking();
|
||||
assert!(!game.board.marked_cells.marking_cells);
|
||||
assert!(game.board.get_marked_cells().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drawing_route_pauses_selected_miner_and_boundary_moves_do_not_repeat_cells() {
|
||||
let mut game = game();
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
queue(&mut game.board, (5, 2), &[(5, 3)]);
|
||||
game.board.toggle_marking();
|
||||
game.tick_update();
|
||||
assert_eq!(
|
||||
game.board.cells[5][3].get_structure().get_durability(),
|
||||
1000
|
||||
);
|
||||
for _ in 0..10 {
|
||||
game.board.change_focused_cell(MoveFocusedCell::Up);
|
||||
}
|
||||
let coords: Vec<_> = game.board.get_marked_cells().iter().copied().collect();
|
||||
assert!(coords.windows(2).all(|pair| pair[0] != pair[1]));
|
||||
game.board.end_marking_cells(true);
|
||||
game.tick_update();
|
||||
assert!(game.board.cells[5][3].get_structure().get_durability() < 1000);
|
||||
let mut game = game();
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
queue(&mut game.board, (5, 2), &[(5, 3)]);
|
||||
game.board.toggle_marking();
|
||||
game.tick_update();
|
||||
assert_eq!(
|
||||
game.board.cells[5][3].get_structure().get_durability(),
|
||||
1000
|
||||
);
|
||||
for _ in 0..10 {
|
||||
game.board.change_focused_cell(MoveFocusedCell::Up);
|
||||
}
|
||||
let coords: Vec<_> = game.board.get_marked_cells().iter().copied().collect();
|
||||
assert!(coords.windows(2).all(|pair| pair[0] != pair[1]));
|
||||
game.board.end_marking_cells(true);
|
||||
game.tick_update();
|
||||
assert!(game.board.cells[5][3].get_structure().get_durability() < 1000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drawing_another_task_extends_the_end_of_queued_work() {
|
||||
let mut game = game();
|
||||
for col in [3, 4, 5] {
|
||||
tunnel(&mut game.board, 5, col);
|
||||
}
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
game.board.toggle_marking();
|
||||
game.board.change_focused_cell(MoveFocusedCell::Right);
|
||||
game.board.change_focused_cell(MoveFocusedCell::Right);
|
||||
game.board.toggle_marking();
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
game.board.toggle_marking();
|
||||
assert_eq!(game.board.get_focused_cell().get_coords(), (5, 4));
|
||||
game.board.change_focused_cell(MoveFocusedCell::Right);
|
||||
game.board.toggle_marking();
|
||||
assert_eq!(unit(&game.board, 5, 2).get_ref_tasks().len(), 2);
|
||||
advance(&mut game, 3 * travel_ticks());
|
||||
assert_eq!(unit(&game.board, 5, 5).get_coords(), (5, 5));
|
||||
assert!(unit(&game.board, 5, 5).get_ref_tasks().is_empty());
|
||||
let mut game = game();
|
||||
for col in [3, 4, 5] {
|
||||
tunnel(&mut game.board, 5, col);
|
||||
}
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
game.board.toggle_marking();
|
||||
game.board.change_focused_cell(MoveFocusedCell::Right);
|
||||
game.board.change_focused_cell(MoveFocusedCell::Right);
|
||||
game.board.toggle_marking();
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
game.board.toggle_marking();
|
||||
assert_eq!(game.board.get_focused_cell().get_coords(), (5, 4));
|
||||
game.board.change_focused_cell(MoveFocusedCell::Right);
|
||||
game.board.toggle_marking();
|
||||
assert_eq!(unit(&game.board, 5, 2).get_ref_tasks().len(), 2);
|
||||
advance(&mut game, 3 * travel_ticks());
|
||||
assert_eq!(unit(&game.board, 5, 5).get_coords(), (5, 5));
|
||||
assert!(unit(&game.board, 5, 5).get_ref_tasks().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn freshly_dug_cell_is_entered_immediately_then_tunnel_travel_takes_three_seconds() {
|
||||
let mut game = game();
|
||||
tunnel(&mut game.board, 5, 4);
|
||||
queue(&mut game.board, (5, 2), &[(5, 3), (5, 4)]);
|
||||
for _ in 0..200 {
|
||||
game.tick_update();
|
||||
if matches!(
|
||||
game.board.cells[5][3].get_structure(),
|
||||
Structures::Tunnel(_)
|
||||
) {
|
||||
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
||||
advance(&mut game, travel_ticks() - 1);
|
||||
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
||||
game.tick_update();
|
||||
assert!(unit(&game.board, 5, 4).get_ref_tasks().is_empty());
|
||||
return;
|
||||
let mut game = game();
|
||||
tunnel(&mut game.board, 5, 4);
|
||||
queue(&mut game.board, (5, 2), &[(5, 3), (5, 4)]);
|
||||
for _ in 0..200 {
|
||||
game.tick_update();
|
||||
if matches!(
|
||||
game.board.cells[5][3].get_structure(),
|
||||
Structures::Tunnel(_)
|
||||
) {
|
||||
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
||||
advance(&mut game, travel_ticks() - 1);
|
||||
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
||||
game.tick_update();
|
||||
assert!(unit(&game.board, 5, 4).get_ref_tasks().is_empty());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("miner did not finish digging the stone");
|
||||
panic!("miner did not finish digging the stone");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn miner_symbol_in_tunnel_uses_owner_color_even_when_selected_or_marked() {
|
||||
for owner in [Players::Player, Players::Enemy] {
|
||||
for zoom in [ZoomLevel::Default, ZoomLevel::ZoomedIn] {
|
||||
for (selected, marked) in [(false, false), (true, false), (false, true), (true, true)] {
|
||||
let mut game = game();
|
||||
let cell = &mut game.board.cells[5][3];
|
||||
cell.set_structure(Structures::Tunnel(Tunnel::new()));
|
||||
cell
|
||||
.set_selected(selected)
|
||||
.set_marked(marked)
|
||||
.set_zoom_level(zoom);
|
||||
*cell.get_mut_option_unit() = Some(Units::Miner(MinerUnit::new(owner, (5, 3))));
|
||||
let area = Rect::new(
|
||||
0,
|
||||
0,
|
||||
zoom.get_cell_size(CellSizes::Width) as u16,
|
||||
zoom.get_cell_size(CellSizes::Height) as u16,
|
||||
);
|
||||
let mut buffer = Buffer::empty(area);
|
||||
cell.clone().render(area, &mut buffer);
|
||||
let marker = buffer
|
||||
.content
|
||||
.iter()
|
||||
.find(|cell| cell.symbol() == "M")
|
||||
.unwrap();
|
||||
assert_eq!(marker.fg, owner.get_color(), "{owner:?} {zoom:?}");
|
||||
assert!(buffer.content.iter().any(|cell| cell.symbol() == "T"));
|
||||
}
|
||||
for owner in [Players::Player, Players::Enemy] {
|
||||
for zoom in [ZoomLevel::Default, ZoomLevel::ZoomedIn] {
|
||||
for (selected, marked) in [(false, false), (true, false), (false, true), (true, true)] {
|
||||
let mut game = game();
|
||||
let cell = &mut game.board.cells[5][3];
|
||||
cell.set_structure(Structures::Tunnel(Tunnel::new()));
|
||||
cell.set_selected(selected)
|
||||
.set_marked(marked)
|
||||
.set_zoom_level(zoom);
|
||||
*cell.get_mut_option_unit() = Some(Units::Miner(MinerUnit::new(owner, (5, 3))));
|
||||
let area = Rect::new(
|
||||
0,
|
||||
0,
|
||||
zoom.get_cell_size(CellSizes::Width) as u16,
|
||||
zoom.get_cell_size(CellSizes::Height) as u16,
|
||||
);
|
||||
let mut buffer = Buffer::empty(area);
|
||||
cell.clone().render(area, &mut buffer);
|
||||
let marker = buffer
|
||||
.content
|
||||
.iter()
|
||||
.find(|cell| cell.symbol() == "M")
|
||||
.unwrap();
|
||||
assert_eq!(marker.fg, owner.get_color(), "{owner:?} {zoom:?}");
|
||||
assert!(buffer.content.iter().any(|cell| cell.symbol() == "T"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tui_shows_movement_percentage_and_clears_it_after_each_step() {
|
||||
for zoom in [
|
||||
ZoomLevel::ZoomedIn,
|
||||
ZoomLevel::Default,
|
||||
ZoomLevel::ZoomedOut,
|
||||
] {
|
||||
let mut game = game();
|
||||
for col in [2, 3, 4] {
|
||||
tunnel(&mut game.board, 5, col);
|
||||
for zoom in [
|
||||
ZoomLevel::ZoomedIn,
|
||||
ZoomLevel::Default,
|
||||
ZoomLevel::ZoomedOut,
|
||||
] {
|
||||
let mut game = game();
|
||||
for col in [2, 3, 4] {
|
||||
tunnel(&mut game.board, 5, col);
|
||||
}
|
||||
queue(&mut game.board, (5, 2), &[(5, 3), (5, 4)]);
|
||||
let render = |board: &BoardState, col: usize| {
|
||||
let area = Rect::new(
|
||||
0,
|
||||
0,
|
||||
zoom.get_cell_size(CellSizes::Width) as u16,
|
||||
zoom.get_cell_size(CellSizes::Height) as u16,
|
||||
);
|
||||
let mut buffer = Buffer::empty(area);
|
||||
let mut cell = board.get_ref_cell(5, col).clone();
|
||||
cell.set_zoom_level(zoom);
|
||||
cell.render(area, &mut buffer);
|
||||
buffer
|
||||
};
|
||||
let text = |buffer: &Buffer| -> String {
|
||||
buffer.content.iter().map(|cell| cell.symbol()).collect()
|
||||
};
|
||||
assert!(!text(&render(&game.board, 2)).contains('%'));
|
||||
advance(&mut game, 46);
|
||||
let buffer = render(&game.board, 2);
|
||||
assert!(text(&buffer).contains("50%"), "{zoom:?}");
|
||||
if zoom != ZoomLevel::ZoomedOut {
|
||||
let marker = buffer
|
||||
.content
|
||||
.iter()
|
||||
.find(|cell| cell.symbol() == "M")
|
||||
.unwrap();
|
||||
assert_eq!(marker.fg, Players::Player.get_color());
|
||||
}
|
||||
advance(&mut game, travel_ticks() - 46);
|
||||
assert!(!text(&render(&game.board, 2)).contains('%'));
|
||||
assert!(!text(&render(&game.board, 3)).contains('%'));
|
||||
game.tick_update();
|
||||
assert!(text(&render(&game.board, 3)).contains("1%"));
|
||||
advance(&mut game, travel_ticks() - 1);
|
||||
assert!(!text(&render(&game.board, 4)).contains('%'));
|
||||
assert!(unit(&game.board, 5, 4).get_ref_tasks().is_empty());
|
||||
}
|
||||
queue(&mut game.board, (5, 2), &[(5, 3), (5, 4)]);
|
||||
let render = |board: &BoardState, col: usize| {
|
||||
let area = Rect::new(
|
||||
0,
|
||||
0,
|
||||
zoom.get_cell_size(CellSizes::Width) as u16,
|
||||
zoom.get_cell_size(CellSizes::Height) as u16,
|
||||
);
|
||||
let mut buffer = Buffer::empty(area);
|
||||
let mut cell = board.get_ref_cell(5, col).clone();
|
||||
cell.set_zoom_level(zoom);
|
||||
cell.render(area, &mut buffer);
|
||||
buffer
|
||||
};
|
||||
let text =
|
||||
|buffer: &Buffer| -> String { buffer.content.iter().map(|cell| cell.symbol()).collect() };
|
||||
assert!(!text(&render(&game.board, 2)).contains('%'));
|
||||
advance(&mut game, 46);
|
||||
let buffer = render(&game.board, 2);
|
||||
assert!(text(&buffer).contains("50%"), "{zoom:?}");
|
||||
if zoom != ZoomLevel::ZoomedOut {
|
||||
let marker = buffer
|
||||
.content
|
||||
.iter()
|
||||
.find(|cell| cell.symbol() == "M")
|
||||
.unwrap();
|
||||
assert_eq!(marker.fg, Players::Player.get_color());
|
||||
}
|
||||
advance(&mut game, travel_ticks() - 46);
|
||||
assert!(!text(&render(&game.board, 2)).contains('%'));
|
||||
assert!(!text(&render(&game.board, 3)).contains('%'));
|
||||
game.tick_update();
|
||||
assert!(text(&render(&game.board, 3)).contains("1%"));
|
||||
advance(&mut game, travel_ticks() - 1);
|
||||
assert!(!text(&render(&game.board, 4)).contains('%'));
|
||||
assert!(unit(&game.board, 5, 4).get_ref_tasks().is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confirmed_route_splits_into_moving_and_digging_segments() {
|
||||
let mut game = game();
|
||||
for col in [3, 4, 7] {
|
||||
tunnel(&mut game.board, 5, col);
|
||||
}
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
game.board.toggle_marking();
|
||||
for _ in 0..5 {
|
||||
game.board.change_focused_cell(MoveFocusedCell::Right);
|
||||
}
|
||||
game.board.toggle_marking();
|
||||
let tasks = unit(&game.board, 5, 2).get_ref_tasks();
|
||||
assert_eq!(tasks.len(), 3);
|
||||
assert!(matches!(tasks[0], Tasks::Moving(_)));
|
||||
assert_eq!(
|
||||
tasks[0].get_path().iter().copied().collect::<Vec<_>>(),
|
||||
[(5, 3), (5, 4)]
|
||||
);
|
||||
assert!(matches!(tasks[1], Tasks::Digging(_)));
|
||||
assert_eq!(
|
||||
tasks[1].get_path().iter().copied().collect::<Vec<_>>(),
|
||||
[(5, 5), (5, 6)]
|
||||
);
|
||||
assert!(matches!(tasks[2], Tasks::Moving(_)));
|
||||
assert_eq!(tasks[0].get_icon(), '→');
|
||||
assert_eq!(tasks[1].get_icon(), '⛏');
|
||||
advance(&mut game, 2 * travel_ticks());
|
||||
assert!(matches!(
|
||||
unit(&game.board, 5, 4).get_ref_tasks()[0],
|
||||
Tasks::Digging(_)
|
||||
));
|
||||
advance(&mut game, 2 * 182);
|
||||
assert!(matches!(
|
||||
unit(&game.board, 5, 6).get_ref_tasks()[0],
|
||||
Tasks::Moving(_)
|
||||
));
|
||||
advance(&mut game, travel_ticks());
|
||||
assert!(unit(&game.board, 5, 7).get_ref_tasks().is_empty());
|
||||
let mut game = game();
|
||||
for col in [3, 4, 7] {
|
||||
tunnel(&mut game.board, 5, col);
|
||||
}
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
game.board.toggle_marking();
|
||||
for _ in 0..5 {
|
||||
game.board.change_focused_cell(MoveFocusedCell::Right);
|
||||
}
|
||||
game.board.toggle_marking();
|
||||
let tasks = unit(&game.board, 5, 2).get_ref_tasks();
|
||||
assert_eq!(tasks.len(), 3);
|
||||
assert!(matches!(tasks[0], Tasks::Moving(_)));
|
||||
assert_eq!(
|
||||
tasks[0].get_path().iter().copied().collect::<Vec<_>>(),
|
||||
[(5, 3), (5, 4)]
|
||||
);
|
||||
assert!(matches!(tasks[1], Tasks::Digging(_)));
|
||||
assert_eq!(
|
||||
tasks[1].get_path().iter().copied().collect::<Vec<_>>(),
|
||||
[(5, 5), (5, 6)]
|
||||
);
|
||||
assert!(matches!(tasks[2], Tasks::Moving(_)));
|
||||
assert_eq!(tasks[0].get_icon(), '→');
|
||||
assert_eq!(tasks[1].get_icon(), '⛏');
|
||||
advance(&mut game, 2 * travel_ticks());
|
||||
assert!(matches!(
|
||||
unit(&game.board, 5, 4).get_ref_tasks()[0],
|
||||
Tasks::Digging(_)
|
||||
));
|
||||
advance(&mut game, 2 * 182);
|
||||
assert!(matches!(
|
||||
unit(&game.board, 5, 6).get_ref_tasks()[0],
|
||||
Tasks::Moving(_)
|
||||
));
|
||||
advance(&mut game, travel_ticks());
|
||||
assert!(unit(&game.board, 5, 7).get_ref_tasks().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn moving_task_does_not_damage_walkable_structure() {
|
||||
let mut game = game();
|
||||
tunnel(&mut game.board, 5, 3);
|
||||
let structure = game.board.cells[5][3].get_structure();
|
||||
game.board.cells[5][2]
|
||||
.get_mut_option_unit()
|
||||
.set_task(Tasks::Moving(MovingTask::new([(5, 3)].into())));
|
||||
advance(&mut game, travel_ticks());
|
||||
assert_eq!(game.board.cells[5][3].get_structure(), structure);
|
||||
assert!(unit(&game.board, 5, 3).get_ref_tasks().is_empty());
|
||||
let mut game = game();
|
||||
tunnel(&mut game.board, 5, 3);
|
||||
let structure = game.board.cells[5][3].get_structure();
|
||||
game.board.cells[5][2]
|
||||
.get_mut_option_unit()
|
||||
.set_task(Tasks::Moving(MovingTask::new([(5, 3)].into())));
|
||||
advance(&mut game, travel_ticks());
|
||||
assert_eq!(game.board.cells[5][3].get_structure(), structure);
|
||||
assert!(unit(&game.board, 5, 3).get_ref_tasks().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn previously_dug_route_becomes_moving_without_instant_steps() {
|
||||
let mut game = game();
|
||||
queue(&mut game.board, (5, 2), &[(5, 3), (5, 4), (5, 3)]);
|
||||
advance(&mut game, 2 * 182);
|
||||
assert_eq!(unit(&game.board, 5, 4).get_coords(), (5, 4));
|
||||
game.tick_update();
|
||||
assert!(matches!(
|
||||
unit(&game.board, 5, 4).get_ref_tasks()[0],
|
||||
Tasks::Moving(_)
|
||||
));
|
||||
advance(&mut game, travel_ticks() - 2);
|
||||
assert_eq!(unit(&game.board, 5, 4).get_coords(), (5, 4));
|
||||
game.tick_update();
|
||||
assert!(unit(&game.board, 5, 3).get_ref_tasks().is_empty());
|
||||
let mut game = game();
|
||||
queue(&mut game.board, (5, 2), &[(5, 3), (5, 4), (5, 3)]);
|
||||
advance(&mut game, 2 * 182);
|
||||
assert_eq!(unit(&game.board, 5, 4).get_coords(), (5, 4));
|
||||
game.tick_update();
|
||||
assert!(matches!(
|
||||
unit(&game.board, 5, 4).get_ref_tasks()[0],
|
||||
Tasks::Moving(_)
|
||||
));
|
||||
advance(&mut game, travel_ticks() - 2);
|
||||
assert_eq!(unit(&game.board, 5, 4).get_coords(), (5, 4));
|
||||
game.tick_update();
|
||||
assert!(unit(&game.board, 5, 3).get_ref_tasks().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn route_reclassifies_as_digging_if_target_is_stone() {
|
||||
let mut game = game();
|
||||
game.board.cells[5][2]
|
||||
.get_mut_option_unit()
|
||||
.set_task(Tasks::Moving(MovingTask::new([(5, 3)].into())));
|
||||
game.tick_update();
|
||||
assert!(matches!(
|
||||
unit(&game.board, 5, 2).get_ref_tasks()[0],
|
||||
Tasks::Digging(_)
|
||||
));
|
||||
assert!(game.board.cells[5][3].get_structure().get_durability() < 1000);
|
||||
advance(&mut game, 181);
|
||||
assert!(unit(&game.board, 5, 3).get_ref_tasks().is_empty());
|
||||
let mut game = game();
|
||||
game.board.cells[5][2]
|
||||
.get_mut_option_unit()
|
||||
.set_task(Tasks::Moving(MovingTask::new([(5, 3)].into())));
|
||||
game.tick_update();
|
||||
assert!(matches!(
|
||||
unit(&game.board, 5, 2).get_ref_tasks()[0],
|
||||
Tasks::Digging(_)
|
||||
));
|
||||
assert!(game.board.cells[5][3].get_structure().get_durability() < 1000);
|
||||
advance(&mut game, 181);
|
||||
assert!(unit(&game.board, 5, 3).get_ref_tasks().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tab_panel_shows_current_step_and_total_for_active_and_pending_tasks() -> () {
|
||||
let args: Cli = Cli::try_parse_from(["game", "--disable-audio", "--view", "skirmish"]).unwrap();
|
||||
let channels: (Sender<AudioCmd>, Receiver<AudioCmd>) = channel();
|
||||
let mut app: App = App::new(args, channels.0);
|
||||
app.window_area = Rect::new(0, 0, 120, 60);
|
||||
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
||||
let state: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
tunnel(&mut state.board, 5, 3);
|
||||
tunnel(&mut state.board, 5, 4);
|
||||
state.board.cells[5][2]
|
||||
.get_mut_option_unit()
|
||||
.set_task(Tasks::Moving(MovingTask::new([(5, 3), (5, 4)].into())));
|
||||
state.board.cells[5][2]
|
||||
.get_mut_option_unit()
|
||||
.set_task(Tasks::Digging(DiggingTask::new([(5, 5)].into(), 0.55)));
|
||||
advance(state, travel_ticks() + 46);
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE));
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(120, 60)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &ratatui::buffer::Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Step: 50% | Total: 75%"));
|
||||
assert!(text.contains("Step: 0% | Total: 0%"));
|
||||
let args: Cli = Cli::try_parse_from(["game", "--disable-audio", "--view", "skirmish"]).unwrap();
|
||||
let channels: (Sender<AudioCmd>, Receiver<AudioCmd>) = channel();
|
||||
let mut app: App = App::new(args, channels.0);
|
||||
app.window_area = Rect::new(0, 0, 120, 60);
|
||||
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
||||
let state: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
tunnel(&mut state.board, 5, 3);
|
||||
tunnel(&mut state.board, 5, 4);
|
||||
state.board.cells[5][2]
|
||||
.get_mut_option_unit()
|
||||
.set_task(Tasks::Moving(MovingTask::new([(5, 3), (5, 4)].into())));
|
||||
state.board.cells[5][2]
|
||||
.get_mut_option_unit()
|
||||
.set_task(Tasks::Digging(DiggingTask::new([(5, 5)].into(), 0.55)));
|
||||
advance(state, travel_ticks() + 46);
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE));
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(120, 60)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &ratatui::buffer::Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Step: 50% | Total: 75%"));
|
||||
assert!(text.contains("Step: 0% | Total: 0%"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn space_route_tick_and_tui_show_remaining_durability_at_every_zoom() {
|
||||
let args = Cli::try_parse_from(["game", "--disable-audio", "--view", "skirmish"]).unwrap();
|
||||
let (tx, _) = channel();
|
||||
let mut app = App::new(args, tx);
|
||||
app.window_area = Rect::new(0, 0, 120, 40);
|
||||
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
||||
for code in [
|
||||
KeyCode::Left,
|
||||
KeyCode::Char(' '),
|
||||
KeyCode::Right,
|
||||
KeyCode::Char(' '),
|
||||
] {
|
||||
handle_keybindings(&mut app, KeyEvent::new(code, KeyModifiers::NONE));
|
||||
}
|
||||
app.states_mut().unwrap().skirmish.tick_update();
|
||||
let durability = app.states_ref().unwrap().skirmish.board.cells[5][3]
|
||||
.get_structure()
|
||||
.get_durability();
|
||||
let mut terminal = Terminal::new(TestBackend::new(120, 40)).unwrap();
|
||||
for zoom in [
|
||||
ZoomLevel::ZoomedIn,
|
||||
ZoomLevel::Default,
|
||||
ZoomLevel::ZoomedOut,
|
||||
] {
|
||||
let state = &mut app.states_mut().unwrap().skirmish;
|
||||
state.board.change_zoom(zoom);
|
||||
state.board.vertical_offset.set_value(5);
|
||||
let args = Cli::try_parse_from(["game", "--disable-audio", "--view", "skirmish"]).unwrap();
|
||||
let (tx, _) = channel();
|
||||
let mut app = App::new(args, tx);
|
||||
app.window_area = Rect::new(0, 0, 120, 40);
|
||||
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
||||
for code in [
|
||||
KeyCode::Left,
|
||||
KeyCode::Char(' '),
|
||||
KeyCode::Right,
|
||||
KeyCode::Char(' '),
|
||||
] {
|
||||
handle_keybindings(&mut app, KeyEvent::new(code, KeyModifiers::NONE));
|
||||
}
|
||||
app.states_mut().unwrap().skirmish.tick_update();
|
||||
let durability = app.states_ref().unwrap().skirmish.board.cells[5][3]
|
||||
.get_structure()
|
||||
.get_durability();
|
||||
let mut terminal = Terminal::new(TestBackend::new(120, 40)).unwrap();
|
||||
for zoom in [
|
||||
ZoomLevel::ZoomedIn,
|
||||
ZoomLevel::Default,
|
||||
ZoomLevel::ZoomedOut,
|
||||
] {
|
||||
let state = &mut app.states_mut().unwrap().skirmish;
|
||||
state.board.change_zoom(zoom);
|
||||
state.board.vertical_offset.set_value(5);
|
||||
terminal
|
||||
.draw(|frame| frame.render_widget(&mut app, frame.area()))
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell| cell.symbol())
|
||||
.collect();
|
||||
assert!(text.contains("99%"), "{zoom:?}");
|
||||
}
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE));
|
||||
terminal
|
||||
.draw(|frame| frame.render_widget(&mut app, frame.area()))
|
||||
.unwrap();
|
||||
.draw(|frame| frame.render_widget(&mut app, frame.area()))
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell| cell.symbol())
|
||||
.collect();
|
||||
assert!(text.contains("99%"), "{zoom:?}");
|
||||
}
|
||||
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE));
|
||||
terminal
|
||||
.draw(|frame| frame.render_widget(&mut app, frame.area()))
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell| cell.symbol())
|
||||
.collect();
|
||||
assert!(text.contains(&format!("{durability}/1000")));
|
||||
assert_eq!(app.view, View::Skirmish);
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell| cell.symbol())
|
||||
.collect();
|
||||
assert!(text.contains(&format!("{durability}/1000")));
|
||||
assert_eq!(app.view, View::Skirmish);
|
||||
}
|
||||
+279
-280
@@ -1,338 +1,337 @@
|
||||
use clap::Parser;
|
||||
use ratatui::{
|
||||
Terminal,
|
||||
backend::TestBackend,
|
||||
buffer::Cell,
|
||||
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
||||
layout::Rect,
|
||||
Terminal,
|
||||
backend::TestBackend,
|
||||
buffer::Cell,
|
||||
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
||||
layout::Rect,
|
||||
};
|
||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||
use war_in_tunnels::{
|
||||
app::{
|
||||
Action, App, GameStates, View, available_skirmish_actions, handle_keybindings,
|
||||
states::{
|
||||
SkirmishState,
|
||||
skirmish_states::{
|
||||
MoveFocusedCell, Players, Resources,
|
||||
structures::{BaseBuilding, Structures, Tunnel},
|
||||
units::{MinerUnit, Unit, Units},
|
||||
},
|
||||
app::{
|
||||
Action, App, GameStates, View, available_skirmish_actions, handle_keybindings,
|
||||
states::{
|
||||
SkirmishState,
|
||||
skirmish_states::{
|
||||
MoveFocusedCell, Players, Resources,
|
||||
structures::{BaseBuilding, Structures, Tunnel},
|
||||
units::{MinerUnit, Unit, Units},
|
||||
},
|
||||
},
|
||||
threads::{AudioCmd, TICK_MS},
|
||||
},
|
||||
threads::{AudioCmd, TICK_MS},
|
||||
},
|
||||
cli::Cli,
|
||||
cli::Cli,
|
||||
};
|
||||
|
||||
fn app() -> App {
|
||||
let args: Cli = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
||||
let (tx, _rx): (Sender<AudioCmd>, Receiver<AudioCmd>) = channel();
|
||||
let mut app: App = App::new(args, tx);
|
||||
app.view = View::Skirmish;
|
||||
app.window_area = Rect::new(0, 0, 160, 50);
|
||||
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
||||
app
|
||||
let args: Cli = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
||||
let (tx, _rx): (Sender<AudioCmd>, Receiver<AudioCmd>) = channel();
|
||||
let mut app: App = App::new(args, tx);
|
||||
app.view = View::Skirmish;
|
||||
app.window_area = Rect::new(0, 0, 160, 50);
|
||||
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
||||
app
|
||||
}
|
||||
|
||||
fn base(game: &SkirmishState, coords: (usize, usize)) -> BaseBuilding {
|
||||
let Structures::Base(base) = game.board.cells[coords.0][coords.1].get_structure() else {
|
||||
panic!("base missing");
|
||||
};
|
||||
base
|
||||
let Structures::Base(base) = game.board.cells[coords.0][coords.1].get_structure() else {
|
||||
panic!("base missing");
|
||||
};
|
||||
base
|
||||
}
|
||||
|
||||
fn press(app: &mut App, code: KeyCode, modifiers: KeyModifiers) -> () {
|
||||
handle_keybindings(app, KeyEvent::new(code, modifiers));
|
||||
handle_keybindings(app, KeyEvent::new(code, modifiers));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recruitment_is_owned_by_base_and_invalid_requests_do_not_charge() -> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
let before: Resources = game.player_resources;
|
||||
for coords in [(5, 39), (0, 0), (usize::MAX, 2)] {
|
||||
assert!(!game.recruit_miner(coords, Players::Player));
|
||||
assert_eq!(game.player_resources, before);
|
||||
}
|
||||
assert!(!game.recruit_miner((5, 2), Players::Unclaimed));
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
assert_eq!(game.player_resources, Resources::new(40, 10));
|
||||
assert_eq!(game.enemy_resources, Resources::new(50, 25));
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 1);
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
let before: Resources = game.player_resources;
|
||||
for coords in [(5, 39), (0, 0), (usize::MAX, 2)] {
|
||||
assert!(!game.recruit_miner(coords, Players::Player));
|
||||
assert_eq!(game.player_resources, before);
|
||||
}
|
||||
assert!(!game.recruit_miner((5, 2), Players::Unclaimed));
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
assert_eq!(game.player_resources, Resources::new(40, 10));
|
||||
assert_eq!(game.enemy_resources, Resources::new(50, 25));
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recruitment_requires_both_resources_and_charges_each_queue_entry() -> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
for resources in [
|
||||
Resources { wood: 9, metal: 15 },
|
||||
Resources {
|
||||
wood: 10,
|
||||
metal: 14,
|
||||
},
|
||||
] {
|
||||
game.player_resources = resources;
|
||||
assert!(!game.can_recruit_miner((5, 2), Players::Player));
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
for resources in [
|
||||
Resources { wood: 9, metal: 15 },
|
||||
Resources {
|
||||
wood: 10,
|
||||
metal: 14,
|
||||
},
|
||||
] {
|
||||
game.player_resources = resources;
|
||||
assert!(!game.can_recruit_miner((5, 2), Players::Player));
|
||||
assert!(!game.recruit_miner((5, 2), Players::Player));
|
||||
assert_eq!(game.player_resources, resources);
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 0);
|
||||
}
|
||||
game.player_resources = Resources::new(20, 30);
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 2);
|
||||
assert_eq!(game.player_resources, Resources::new(0, 0));
|
||||
assert!(!game.recruit_miner((5, 2), Players::Player));
|
||||
assert_eq!(game.player_resources, resources);
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 0);
|
||||
}
|
||||
game.player_resources = Resources::new(20, 30);
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 2);
|
||||
assert_eq!(game.player_resources, Resources::new(0, 0));
|
||||
assert!(!game.recruit_miner((5, 2), Players::Player));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_recruit_waits_at_full_progress_and_following_recruits_pause_until_departure() -> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
game.player_resources = Resources::new(100, 100);
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
let original: Option<Units> = game.board.cells[5][2].get_option_unit();
|
||||
for _ in 0..2 * BaseBuilding::RECRUITMENT_TICKS {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
game.player_resources = Resources::new(100, 100);
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
let original: Option<Units> = game.board.cells[5][2].get_option_unit();
|
||||
for _ in 0..2 * BaseBuilding::RECRUITMENT_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 2);
|
||||
assert_eq!(base(game, (5, 2)).recruitment_progress(), 100);
|
||||
assert_eq!(game.board.cells[5][2].get_option_unit(), original);
|
||||
game.board.cells[5][2].get_mut_option_unit().take();
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 2);
|
||||
assert_eq!(base(game, (5, 2)).recruitment_progress(), 100);
|
||||
assert_eq!(game.board.cells[5][2].get_option_unit(), original);
|
||||
game.board.cells[5][2].get_mut_option_unit().take();
|
||||
game.tick_update();
|
||||
let spawned: Option<Units> = game.board.cells[5][2].get_option_unit();
|
||||
assert_eq!(spawned.get_owner(), Players::Player);
|
||||
assert_eq!(spawned.get_coords(), (5, 2));
|
||||
assert!(spawned.get_ref_tasks().is_empty());
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 1);
|
||||
for _ in 0..2 * BaseBuilding::RECRUITMENT_TICKS {
|
||||
let spawned: Option<Units> = game.board.cells[5][2].get_option_unit();
|
||||
assert_eq!(spawned.get_owner(), Players::Player);
|
||||
assert_eq!(spawned.get_coords(), (5, 2));
|
||||
assert!(spawned.get_ref_tasks().is_empty());
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 1);
|
||||
for _ in 0..2 * BaseBuilding::RECRUITMENT_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 1);
|
||||
assert_eq!(base(game, (5, 2)).recruitment_progress(), 0);
|
||||
assert_eq!(game.board.cells[5][2].get_option_unit(), spawned);
|
||||
game.board.cells[5][2].get_mut_option_unit().take();
|
||||
for _ in 1..BaseBuilding::RECRUITMENT_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert!(game.board.cells[5][2].get_ref_option_unit().is_none());
|
||||
assert_eq!(base(game, (5, 2)).recruitment_progress(), 99);
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 1);
|
||||
assert_eq!(base(game, (5, 2)).recruitment_progress(), 0);
|
||||
assert_eq!(game.board.cells[5][2].get_option_unit(), spawned);
|
||||
game.board.cells[5][2].get_mut_option_unit().take();
|
||||
for _ in 1..BaseBuilding::RECRUITMENT_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert!(game.board.cells[5][2].get_ref_option_unit().is_none());
|
||||
assert_eq!(base(game, (5, 2)).recruitment_progress(), 99);
|
||||
game.tick_update();
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 0);
|
||||
assert_eq!(
|
||||
game.board.cells[5][2].get_ref_option_unit().get_owner(),
|
||||
Players::Player
|
||||
);
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 0);
|
||||
assert_eq!(
|
||||
game.board.cells[5][2].get_ref_option_unit().get_owner(),
|
||||
Players::Player
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn walking_out_of_base_resumes_recruitment_and_new_miner_can_receive_tasks() -> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
game.board.cells[5][3].set_structure(Structures::Tunnel(Tunnel::new()));
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
game.board.toggle_marking();
|
||||
game.board.change_focused_cell(MoveFocusedCell::Right);
|
||||
game.board.toggle_marking();
|
||||
for _ in 0..3000_u16.div_ceil(u16::from(TICK_MS)) {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
game.board.cells[5][3].set_structure(Structures::Tunnel(Tunnel::new()));
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
game.board.toggle_marking();
|
||||
game.board.change_focused_cell(MoveFocusedCell::Right);
|
||||
game.board.toggle_marking();
|
||||
for _ in 0..3000_u16.div_ceil(u16::from(TICK_MS)) {
|
||||
game.tick_update();
|
||||
}
|
||||
assert!(game.board.cells[5][2].get_ref_option_unit().is_none());
|
||||
for _ in 3000_u16.div_ceil(u16::from(TICK_MS))..BaseBuilding::RECRUITMENT_TICKS - 1 {
|
||||
game.tick_update();
|
||||
}
|
||||
assert!(game.board.cells[5][2].get_ref_option_unit().is_none());
|
||||
assert_eq!(base(game, (5, 2)).recruitment_progress(), 99);
|
||||
game.tick_update();
|
||||
}
|
||||
assert!(game.board.cells[5][2].get_ref_option_unit().is_none());
|
||||
for _ in 3000_u16.div_ceil(u16::from(TICK_MS))..BaseBuilding::RECRUITMENT_TICKS - 1 {
|
||||
game.tick_update();
|
||||
}
|
||||
assert!(game.board.cells[5][2].get_ref_option_unit().is_none());
|
||||
assert_eq!(base(game, (5, 2)).recruitment_progress(), 99);
|
||||
game.tick_update();
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 0);
|
||||
assert_eq!(
|
||||
game.board.cells[5][2].get_ref_option_unit().get_coords(),
|
||||
(5, 2)
|
||||
);
|
||||
assert_eq!(
|
||||
game.board.cells[5][3].get_ref_option_unit().get_coords(),
|
||||
(5, 3)
|
||||
);
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
game.board.toggle_marking();
|
||||
game.board.change_focused_cell(MoveFocusedCell::Down);
|
||||
game.board.toggle_marking();
|
||||
assert_eq!(
|
||||
game.board.cells[5][2]
|
||||
.get_ref_option_unit()
|
||||
.get_ref_tasks()
|
||||
.len(),
|
||||
1
|
||||
);
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 0);
|
||||
assert_eq!(
|
||||
game.board.cells[5][2].get_ref_option_unit().get_coords(),
|
||||
(5, 2)
|
||||
);
|
||||
assert_eq!(
|
||||
game.board.cells[5][3].get_ref_option_unit().get_coords(),
|
||||
(5, 3)
|
||||
);
|
||||
game.board.change_focused_cell(MoveFocusedCell::Left);
|
||||
game.board.toggle_marking();
|
||||
game.board.change_focused_cell(MoveFocusedCell::Down);
|
||||
game.board.toggle_marking();
|
||||
assert_eq!(
|
||||
game.board.cells[5][2]
|
||||
.get_ref_option_unit()
|
||||
.get_ref_tasks()
|
||||
.len(),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arrival_during_training_does_not_overwrite_visitor_and_ready_recruit_spawns_after_departure()
|
||||
-> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
game.board.cells[5][2].get_mut_option_unit().take();
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
for _ in 0..100 {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
game.board.cells[5][2].get_mut_option_unit().take();
|
||||
assert!(game.recruit_miner((5, 2), Players::Player));
|
||||
for _ in 0..100 {
|
||||
game.tick_update();
|
||||
}
|
||||
let visitor: Units = Units::Miner(MinerUnit::new(Players::Enemy, (5, 2)));
|
||||
*game.board.cells[5][2].get_mut_option_unit() = Some(visitor.clone());
|
||||
for _ in 0..BaseBuilding::RECRUITMENT_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(base(game, (5, 2)).recruitment_progress(), 100);
|
||||
assert_eq!(game.board.cells[5][2].get_option_unit(), Some(visitor));
|
||||
game.board.cells[5][2].get_mut_option_unit().take();
|
||||
game.tick_update();
|
||||
}
|
||||
let visitor: Units = Units::Miner(MinerUnit::new(Players::Enemy, (5, 2)));
|
||||
*game.board.cells[5][2].get_mut_option_unit() = Some(visitor.clone());
|
||||
for _ in 0..BaseBuilding::RECRUITMENT_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(base(game, (5, 2)).recruitment_progress(), 100);
|
||||
assert_eq!(game.board.cells[5][2].get_option_unit(), Some(visitor));
|
||||
game.board.cells[5][2].get_mut_option_unit().take();
|
||||
game.tick_update();
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 0);
|
||||
assert_eq!(
|
||||
game.board.cells[5][2].get_ref_option_unit().get_owner(),
|
||||
Players::Player
|
||||
);
|
||||
assert_eq!(base(game, (5, 2)).queued_miners(), 0);
|
||||
assert_eq!(
|
||||
game.board.cells[5][2].get_ref_option_unit().get_owner(),
|
||||
Players::Player
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bases_have_independent_queues_costs_and_spawn_ownership() -> () {
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
for (coords, owner) in [((5, 2), Players::Player), ((5, 39), Players::Enemy)] {
|
||||
game.board.cells[coords.0][coords.1]
|
||||
.get_mut_option_unit()
|
||||
.take();
|
||||
assert!(game.recruit_miner(coords, owner));
|
||||
}
|
||||
assert_eq!(game.player_resources.metal, 10);
|
||||
assert_eq!(game.enemy_resources.metal, 10);
|
||||
for _ in 0..BaseBuilding::RECRUITMENT_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
for (coords, owner) in [((5, 2), Players::Player), ((5, 39), Players::Enemy)] {
|
||||
assert_eq!(base(game, coords).queued_miners(), 0);
|
||||
assert_eq!(
|
||||
game.board.cells[coords.0][coords.1]
|
||||
.get_ref_option_unit()
|
||||
.get_owner(),
|
||||
owner
|
||||
);
|
||||
assert_eq!(
|
||||
game.board.cells[coords.0][coords.1]
|
||||
.get_ref_option_unit()
|
||||
.get_coords(),
|
||||
coords
|
||||
);
|
||||
}
|
||||
let mut app: App = app();
|
||||
let game: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
||||
for (coords, owner) in [((5, 2), Players::Player), ((5, 39), Players::Enemy)] {
|
||||
game.board.cells[coords.0][coords.1]
|
||||
.get_mut_option_unit()
|
||||
.take();
|
||||
assert!(game.recruit_miner(coords, owner));
|
||||
}
|
||||
assert_eq!(game.player_resources.metal, 10);
|
||||
assert_eq!(game.enemy_resources.metal, 10);
|
||||
for _ in 0..BaseBuilding::RECRUITMENT_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
for (coords, owner) in [((5, 2), Players::Player), ((5, 39), Players::Enemy)] {
|
||||
assert_eq!(base(game, coords).queued_miners(), 0);
|
||||
assert_eq!(
|
||||
game.board.cells[coords.0][coords.1]
|
||||
.get_ref_option_unit()
|
||||
.get_owner(),
|
||||
owner
|
||||
);
|
||||
assert_eq!(
|
||||
game.board.cells[coords.0][coords.1]
|
||||
.get_ref_option_unit()
|
||||
.get_coords(),
|
||||
coords
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recruitment_panel_queues_with_enter_shows_ready_state_and_closes_without_losing_queue() -> () {
|
||||
let mut app: App = app();
|
||||
press(&mut app, KeyCode::Left, KeyModifiers::NONE);
|
||||
assert!(
|
||||
available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Recruitment)
|
||||
);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::ALT);
|
||||
assert!(app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
press(&mut app, KeyCode::Enter, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Enter, KeyModifiers::NONE);
|
||||
assert_eq!(
|
||||
base(&app.states_ref().unwrap().skirmish, (5, 2)).queued_miners(),
|
||||
1
|
||||
);
|
||||
assert!(
|
||||
!available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Enter)
|
||||
);
|
||||
press(&mut app, KeyCode::Right, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::CONTROL);
|
||||
assert_eq!(
|
||||
app
|
||||
.states_ref()
|
||||
.unwrap()
|
||||
.skirmish
|
||||
.board
|
||||
.get_focused_cell()
|
||||
.get_coords(),
|
||||
(5, 2)
|
||||
);
|
||||
assert!(!app.states_ref().unwrap().skirmish.building_view);
|
||||
for _ in 0..BaseBuilding::RECRUITMENT_TICKS {
|
||||
app.states_mut().unwrap().skirmish.tick_update();
|
||||
}
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(160, 50)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Cost: 10 Wood + 15 Metal"));
|
||||
assert!(text.contains("Queued Miners: 1"));
|
||||
assert!(text.contains("Current progress: 100%"));
|
||||
assert!(text.contains("Ready: waiting for free base cell"));
|
||||
assert!(text.contains("Close recruitment"));
|
||||
press(&mut app, KeyCode::Esc, KeyModifiers::NONE);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
assert_eq!(app.view, View::Skirmish);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::ALT);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::ALT);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
assert_eq!(
|
||||
base(&app.states_ref().unwrap().skirmish, (5, 2)).queued_miners(),
|
||||
1
|
||||
);
|
||||
let mut app: App = app();
|
||||
press(&mut app, KeyCode::Left, KeyModifiers::NONE);
|
||||
assert!(
|
||||
available_skirmish_actions(&app.states_ref().unwrap().skirmish)
|
||||
.contains(&Action::Recruitment)
|
||||
);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::ALT);
|
||||
assert!(app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
press(&mut app, KeyCode::Enter, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Enter, KeyModifiers::NONE);
|
||||
assert_eq!(
|
||||
base(&app.states_ref().unwrap().skirmish, (5, 2)).queued_miners(),
|
||||
1
|
||||
);
|
||||
assert!(
|
||||
!available_skirmish_actions(&app.states_ref().unwrap().skirmish).contains(&Action::Enter)
|
||||
);
|
||||
press(&mut app, KeyCode::Right, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::CONTROL);
|
||||
assert_eq!(
|
||||
app.states_ref()
|
||||
.unwrap()
|
||||
.skirmish
|
||||
.board
|
||||
.get_focused_cell()
|
||||
.get_coords(),
|
||||
(5, 2)
|
||||
);
|
||||
assert!(!app.states_ref().unwrap().skirmish.building_view);
|
||||
for _ in 0..BaseBuilding::RECRUITMENT_TICKS {
|
||||
app.states_mut().unwrap().skirmish.tick_update();
|
||||
}
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(160, 50)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Cost: 10 Wood + 15 Metal"));
|
||||
assert!(text.contains("Queued Miners: 1"));
|
||||
assert!(text.contains("Current progress: 100%"));
|
||||
assert!(text.contains("Ready: waiting for free base cell"));
|
||||
assert!(text.contains("Close recruitment"));
|
||||
press(&mut app, KeyCode::Esc, KeyModifiers::NONE);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
assert_eq!(app.view, View::Skirmish);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::ALT);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::ALT);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
assert_eq!(
|
||||
base(&app.states_ref().unwrap().skirmish, (5, 2)).queued_miners(),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recruitment_panel_is_unavailable_off_base_and_during_route_selection() -> () {
|
||||
let mut app: App = app();
|
||||
press(&mut app, KeyCode::Char('r'), KeyModifiers::NONE);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::ALT);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
press(&mut app, KeyCode::Left, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char('r'), KeyModifiers::NONE);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::ALT);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
assert!(
|
||||
app
|
||||
.states_ref()
|
||||
.unwrap()
|
||||
.skirmish
|
||||
.board
|
||||
.marked_cells
|
||||
.marking_cells
|
||||
);
|
||||
press(&mut app, KeyCode::Esc, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::ALT);
|
||||
press(&mut app, KeyCode::Char('c'), KeyModifiers::CONTROL);
|
||||
assert!(app.exit);
|
||||
let mut app: App = app();
|
||||
press(&mut app, KeyCode::Char('r'), KeyModifiers::NONE);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::ALT);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
press(&mut app, KeyCode::Left, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char('r'), KeyModifiers::NONE);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::ALT);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
assert!(
|
||||
app.states_ref()
|
||||
.unwrap()
|
||||
.skirmish
|
||||
.board
|
||||
.marked_cells
|
||||
.marking_cells
|
||||
);
|
||||
press(&mut app, KeyCode::Esc, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char(' '), KeyModifiers::ALT);
|
||||
press(&mut app, KeyCode::Char('c'), KeyModifiers::CONTROL);
|
||||
assert!(app.exit);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recruitment_fallback_opens_queues_and_closes_on_own_base() -> () {
|
||||
let mut app: App = app();
|
||||
press(&mut app, KeyCode::Left, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char('r'), KeyModifiers::NONE);
|
||||
assert!(app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
press(&mut app, KeyCode::Enter, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char('r'), KeyModifiers::NONE);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
assert_eq!(
|
||||
base(&app.states_ref().unwrap().skirmish, (5, 2)).queued_miners(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
app.states_ref().unwrap().skirmish.player_resources,
|
||||
Resources::new(40, 10)
|
||||
);
|
||||
let mut app: App = app();
|
||||
press(&mut app, KeyCode::Left, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char('r'), KeyModifiers::NONE);
|
||||
assert!(app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
press(&mut app, KeyCode::Enter, KeyModifiers::NONE);
|
||||
press(&mut app, KeyCode::Char('r'), KeyModifiers::NONE);
|
||||
assert!(!app.states_ref().unwrap().skirmish.recruitment_view);
|
||||
assert_eq!(
|
||||
base(&app.states_ref().unwrap().skirmish, (5, 2)).queued_miners(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
app.states_ref().unwrap().skirmish.player_resources,
|
||||
Resources::new(40, 10)
|
||||
);
|
||||
}
|
||||
+123
-121
@@ -2,160 +2,162 @@ use clap::Parser;
|
||||
use ratatui::{Terminal, backend::TestBackend, buffer::Cell, layout::Rect};
|
||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||
use war_in_tunnels::{
|
||||
app::{
|
||||
App, GameStates, View,
|
||||
states::{
|
||||
SkirmishState,
|
||||
skirmish_states::{
|
||||
Players, Resources,
|
||||
resources::{MINE_PRODUCTION_TICKS, WOOD_DELIVERY_AMOUNT, WOOD_DELIVERY_TICKS},
|
||||
structures::{Mine, Ore, Stone, Structure, Structures},
|
||||
},
|
||||
app::{
|
||||
App, GameStates, View,
|
||||
states::{
|
||||
SkirmishState,
|
||||
skirmish_states::{
|
||||
Players, Resources,
|
||||
resources::{MINE_PRODUCTION_TICKS, WOOD_DELIVERY_AMOUNT, WOOD_DELIVERY_TICKS},
|
||||
structures::{Mine, Ore, Stone, Structure, Structures},
|
||||
},
|
||||
},
|
||||
threads::AudioCmd,
|
||||
},
|
||||
threads::AudioCmd,
|
||||
},
|
||||
cli::Cli,
|
||||
cli::Cli,
|
||||
};
|
||||
|
||||
fn game() -> SkirmishState {
|
||||
let args: Cli =
|
||||
Cli::try_parse_from(["game", "--starting-wood", "70", "--starting-metal", "40"]).unwrap();
|
||||
GameStates::new(&args, &Rect::new(0, 0, 160, 40)).skirmish
|
||||
let args: Cli =
|
||||
Cli::try_parse_from(["game", "--starting-wood", "70", "--starting-metal", "40"]).unwrap();
|
||||
GameStates::new(&args, &Rect::new(0, 0, 160, 40)).skirmish
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resources_start_from_cli_and_legacy_iron_flag_remains_an_alias() -> () {
|
||||
let game: SkirmishState = game();
|
||||
assert_eq!(game.player_resources, Resources::new(70, 40));
|
||||
assert_eq!(game.enemy_resources, Resources::new(70, 40));
|
||||
let args: Cli = Cli::try_parse_from(["game", "--starting-iron", "33"]).unwrap();
|
||||
assert_eq!(args.starting_metal, 33);
|
||||
let game: SkirmishState = game();
|
||||
assert_eq!(game.player_resources, Resources::new(70, 40));
|
||||
assert_eq!(game.enemy_resources, Resources::new(70, 40));
|
||||
let args: Cli = Cli::try_parse_from(["game", "--starting-iron", "33"]).unwrap();
|
||||
assert_eq!(args.starting_metal, 33);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn passive_wood_arrives_once_per_interval_without_free_metal() -> () {
|
||||
let mut game: SkirmishState = game();
|
||||
for _ in 1..WOOD_DELIVERY_TICKS {
|
||||
let mut game: SkirmishState = game();
|
||||
for _ in 1..WOOD_DELIVERY_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(game.player_resources.wood, 70);
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(game.player_resources.wood, 70);
|
||||
game.tick_update();
|
||||
assert_eq!(game.player_resources.wood, 70 + WOOD_DELIVERY_AMOUNT);
|
||||
for _ in 0..WOOD_DELIVERY_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(game.player_resources.wood, 70 + 2 * WOOD_DELIVERY_AMOUNT);
|
||||
assert_eq!(game.player_resources.metal, 40);
|
||||
assert_eq!(game.enemy_resources, Resources::new(70, 40));
|
||||
assert_eq!(game.player_resources.wood, 70 + WOOD_DELIVERY_AMOUNT);
|
||||
for _ in 0..WOOD_DELIVERY_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(game.player_resources.wood, 70 + 2 * WOOD_DELIVERY_AMOUNT);
|
||||
assert_eq!(game.player_resources.metal, 40);
|
||||
assert_eq!(game.enemy_resources, Resources::new(70, 40));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ore_is_neutral_and_no_mines_spawn_on_the_board() -> () {
|
||||
let game: SkirmishState = game();
|
||||
let mut deposits: usize = 0;
|
||||
for row in &game.board.cells {
|
||||
for cell in row {
|
||||
let structure: Structures = cell.get_structure();
|
||||
assert!(!matches!(structure, Structures::Mine(_)));
|
||||
if matches!(structure, Structures::Ore(_)) {
|
||||
deposits += 1;
|
||||
assert_eq!(structure.get_owner(), Players::Unclaimed);
|
||||
}
|
||||
let game: SkirmishState = game();
|
||||
let mut deposits: usize = 0;
|
||||
for row in &game.board.cells {
|
||||
for cell in row {
|
||||
let structure: Structures = cell.get_structure();
|
||||
assert!(!matches!(structure, Structures::Mine(_)));
|
||||
if matches!(structure, Structures::Ore(_)) {
|
||||
deposits += 1;
|
||||
assert_eq!(structure.get_owner(), Players::Unclaimed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(deposits, 2);
|
||||
assert_eq!(deposits, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mine_can_only_replace_neutral_ore_and_preserves_its_reserve() -> () {
|
||||
let mut ore: Ore = Ore::new(Players::Unclaimed, 1);
|
||||
assert_eq!(ore.extract(4999), 4999);
|
||||
let mut structure: Structures = Structures::Ore(ore);
|
||||
assert!(structure.build_mine(Players::Player));
|
||||
let Structures::Mine(mut mine) = structure else {
|
||||
panic!("expected mine");
|
||||
};
|
||||
assert_eq!(mine.get_owner(), Players::Player);
|
||||
assert_eq!(mine.get_ore_amount(), 1);
|
||||
assert_eq!(mine.produce(), 1);
|
||||
assert_eq!(mine.produce(), 0);
|
||||
assert_eq!(mine.get_ore_amount(), 0);
|
||||
assert!(!structure.build_mine(Players::Enemy));
|
||||
let mut stone: Structures = Structures::Stone(Stone::new());
|
||||
assert!(!stone.build_mine(Players::Player));
|
||||
assert_eq!(stone, Structures::Stone(Stone::new()));
|
||||
assert!(Mine::new(Players::Unclaimed, ore).is_none());
|
||||
assert!(Mine::new(Players::Player, Ore::new(Players::Enemy, 1)).is_none());
|
||||
let mut ore: Ore = Ore::new(Players::Unclaimed, 1);
|
||||
assert_eq!(ore.extract(4999), 4999);
|
||||
let mut structure: Structures = Structures::Ore(ore);
|
||||
assert!(structure.build_mine(Players::Player));
|
||||
let Structures::Mine(mut mine) = structure else {
|
||||
panic!("expected mine");
|
||||
};
|
||||
assert_eq!(mine.get_owner(), Players::Player);
|
||||
assert_eq!(mine.get_ore_amount(), 1);
|
||||
assert_eq!(mine.produce(), 1);
|
||||
assert_eq!(mine.produce(), 0);
|
||||
assert_eq!(mine.get_ore_amount(), 0);
|
||||
assert!(!structure.build_mine(Players::Enemy));
|
||||
let mut stone: Structures = Structures::Stone(Stone::new());
|
||||
assert!(!stone.build_mine(Players::Player));
|
||||
assert_eq!(stone, Structures::Stone(Stone::new()));
|
||||
assert!(Mine::new(Players::Unclaimed, ore).is_none());
|
||||
assert!(Mine::new(Players::Player, Ore::new(Players::Enemy, 1)).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mining_credits_each_owner_and_stops_when_deposit_is_empty() -> () {
|
||||
let mut game: SkirmishState = game();
|
||||
for (col, owner) in [(1, Players::Player), (40, Players::Enemy)] {
|
||||
let mut ore: Ore = Ore::new(Players::Unclaimed, 1);
|
||||
ore.extract(4999);
|
||||
let mut structure: Structures = Structures::Ore(ore);
|
||||
assert!(structure.build_mine(owner));
|
||||
game.board.cells[5][col].set_structure(structure);
|
||||
}
|
||||
for _ in 1..MINE_PRODUCTION_TICKS {
|
||||
let mut game: SkirmishState = game();
|
||||
for (col, owner) in [(1, Players::Player), (40, Players::Enemy)] {
|
||||
let mut ore: Ore = Ore::new(Players::Unclaimed, 1);
|
||||
ore.extract(4999);
|
||||
let mut structure: Structures = Structures::Ore(ore);
|
||||
assert!(structure.build_mine(owner));
|
||||
game.board.cells[5][col].set_structure(structure);
|
||||
}
|
||||
for _ in 1..MINE_PRODUCTION_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(game.player_resources.metal, 40);
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(game.player_resources.metal, 40);
|
||||
game.tick_update();
|
||||
assert_eq!(game.player_resources.metal, 41);
|
||||
assert_eq!(game.enemy_resources.metal, 41);
|
||||
for _ in 0..MINE_PRODUCTION_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(game.player_resources.metal, 41);
|
||||
assert_eq!(game.enemy_resources.metal, 41);
|
||||
let structure: Structures = game.board.cells[5][1].get_structure();
|
||||
assert!(
|
||||
structure
|
||||
.text()
|
||||
.iter()
|
||||
.any(|line: &ratatui::text::Line<'_>| -> bool { line.to_string().contains("Ore: 0/5000") })
|
||||
);
|
||||
assert_eq!(game.player_resources.metal, 41);
|
||||
assert_eq!(game.enemy_resources.metal, 41);
|
||||
for _ in 0..MINE_PRODUCTION_TICKS {
|
||||
game.tick_update();
|
||||
}
|
||||
assert_eq!(game.player_resources.metal, 41);
|
||||
assert_eq!(game.enemy_resources.metal, 41);
|
||||
let structure: Structures = game.board.cells[5][1].get_structure();
|
||||
assert!(
|
||||
structure
|
||||
.text()
|
||||
.iter()
|
||||
.any(|line: &ratatui::text::Line<'_>| -> bool {
|
||||
line.to_string().contains("Ore: 0/5000")
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resource_additions_do_not_overflow() -> () {
|
||||
let mut resources: Resources = Resources {
|
||||
wood: u32::MAX - 1,
|
||||
metal: u32::MAX - 1,
|
||||
};
|
||||
resources.add_wood(5);
|
||||
resources.add_metal(5);
|
||||
assert_eq!(resources.wood, u32::MAX);
|
||||
assert_eq!(resources.metal, u32::MAX);
|
||||
let mut resources: Resources = Resources {
|
||||
wood: u32::MAX - 1,
|
||||
metal: u32::MAX - 1,
|
||||
};
|
||||
resources.add_wood(5);
|
||||
resources.add_metal(5);
|
||||
assert_eq!(resources.wood, u32::MAX);
|
||||
assert_eq!(resources.metal, u32::MAX);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tui_reads_current_resources_instead_of_starting_settings() -> () {
|
||||
let args: Cli = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
||||
let (tx, _rx): (Sender<AudioCmd>, Receiver<AudioCmd>) = channel();
|
||||
let mut app: App = App::new(args, tx);
|
||||
app.view = View::Skirmish;
|
||||
app.states = Some(GameStates::new(&app.args, &Rect::new(0, 0, 200, 40)));
|
||||
app.states_mut().unwrap().skirmish.player_resources = Resources {
|
||||
wood: 777,
|
||||
metal: 888,
|
||||
};
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(200, 40)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Wood: 777 (+5/5s)"));
|
||||
assert!(text.contains("Metal: 888"));
|
||||
assert!(!text.contains("Iron:"));
|
||||
let args: Cli = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
||||
let (tx, _rx): (Sender<AudioCmd>, Receiver<AudioCmd>) = channel();
|
||||
let mut app: App = App::new(args, tx);
|
||||
app.view = View::Skirmish;
|
||||
app.states = Some(GameStates::new(&app.args, &Rect::new(0, 0, 200, 40)));
|
||||
app.states_mut().unwrap().skirmish.player_resources = Resources {
|
||||
wood: 777,
|
||||
metal: 888,
|
||||
};
|
||||
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(200, 40)).unwrap();
|
||||
terminal
|
||||
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
||||
frame.render_widget(&mut app, frame.area());
|
||||
})
|
||||
.unwrap();
|
||||
let text: String = terminal
|
||||
.backend()
|
||||
.buffer()
|
||||
.content
|
||||
.iter()
|
||||
.map(|cell: &Cell| -> &str { cell.symbol() })
|
||||
.collect();
|
||||
assert!(text.contains("Wood: 777 (+5/5s)"));
|
||||
assert!(text.contains("Metal: 888"));
|
||||
assert!(!text.contains("Iron:"));
|
||||
}
|
||||
+91
-91
@@ -1,123 +1,123 @@
|
||||
use war_in_tunnels::app::states::skirmish_states::{
|
||||
Players,
|
||||
tasks::{DiggingTask, MovingTask, Task, Tasks},
|
||||
units::{MinerUnit, Unit},
|
||||
Players,
|
||||
tasks::{DiggingTask, MovingTask, Task, Tasks},
|
||||
units::{MinerUnit, Unit},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn digging_progress_accumulates_and_stops_at_completion() {
|
||||
let mut task = DiggingTask::new([(5, 3)].into(), 40.0);
|
||||
assert!(!task.is_finished());
|
||||
task.progress();
|
||||
assert_eq!(task.get_progress(), 40.0);
|
||||
assert!(!task.is_finished());
|
||||
for _ in 0..3 {
|
||||
let mut task = DiggingTask::new([(5, 3)].into(), 40.0);
|
||||
assert!(!task.is_finished());
|
||||
task.progress();
|
||||
}
|
||||
assert_eq!(task.get_progress(), 100.0);
|
||||
assert!(task.is_finished());
|
||||
assert_eq!(task.get_progress(), 40.0);
|
||||
assert!(!task.is_finished());
|
||||
for _ in 0..3 {
|
||||
task.progress();
|
||||
}
|
||||
assert_eq!(task.get_progress(), 100.0);
|
||||
assert!(task.is_finished());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn advancing_path_resets_progress_and_handles_empty_path() {
|
||||
let mut task = DiggingTask::new([(5, 3), (5, 4)].into(), 25.0);
|
||||
task.progress();
|
||||
task.pop_front();
|
||||
assert_eq!(task.current_target(), Some((5, 4)));
|
||||
assert_eq!(task.get_progress(), 0.0);
|
||||
task.pop_front();
|
||||
task.pop_front();
|
||||
assert_eq!(task.current_target(), None);
|
||||
assert!(task.get_path().is_empty());
|
||||
let mut task = DiggingTask::new([(5, 3), (5, 4)].into(), 25.0);
|
||||
task.progress();
|
||||
task.pop_front();
|
||||
assert_eq!(task.current_target(), Some((5, 4)));
|
||||
assert_eq!(task.get_progress(), 0.0);
|
||||
task.pop_front();
|
||||
task.pop_front();
|
||||
assert_eq!(task.current_target(), None);
|
||||
assert!(task.get_path().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn miners_keep_tasks_in_submission_order() {
|
||||
let mut miner = MinerUnit::new(Players::Player, (5, 2));
|
||||
for target in [(5, 3), (6, 3)] {
|
||||
miner.set_task(Tasks::Digging(DiggingTask::new([target].into(), 1.0)));
|
||||
}
|
||||
assert_eq!(miner.get_ref_tasks().len(), 2);
|
||||
assert_eq!(
|
||||
*miner
|
||||
.get_mut_tasks()
|
||||
.pop_front()
|
||||
.unwrap()
|
||||
.get_path_front_coords(),
|
||||
(5, 3)
|
||||
);
|
||||
assert_eq!(*miner.get_ref_tasks()[0].get_path_front_coords(), (6, 3));
|
||||
let mut miner = MinerUnit::new(Players::Player, (5, 2));
|
||||
for target in [(5, 3), (6, 3)] {
|
||||
miner.set_task(Tasks::Digging(DiggingTask::new([target].into(), 1.0)));
|
||||
}
|
||||
assert_eq!(miner.get_ref_tasks().len(), 2);
|
||||
assert_eq!(
|
||||
*miner
|
||||
.get_mut_tasks()
|
||||
.pop_front()
|
||||
.unwrap()
|
||||
.get_path_front_coords(),
|
||||
(5, 3)
|
||||
);
|
||||
assert_eq!(*miner.get_ref_tasks()[0].get_path_front_coords(), (6, 3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn movement_progress_is_separate_from_digging_and_resets_for_next_cell() {
|
||||
let mut task = MovingTask::new([(5, 3), (5, 4)].into());
|
||||
assert_eq!(task.get_movement_progress(), None);
|
||||
assert_eq!(task.get_progress(), 0.0);
|
||||
assert!(!task.is_finished());
|
||||
for _ in 0..46 {
|
||||
let mut task = MovingTask::new([(5, 3), (5, 4)].into());
|
||||
assert_eq!(task.get_movement_progress(), None);
|
||||
assert_eq!(task.get_progress(), 0.0);
|
||||
assert!(!task.is_finished());
|
||||
for _ in 0..46 {
|
||||
task.progress();
|
||||
}
|
||||
assert_eq!(task.get_movement_progress(), Some(50));
|
||||
for _ in 46..91 {
|
||||
task.progress();
|
||||
}
|
||||
assert_eq!(task.get_movement_progress(), Some(100));
|
||||
assert_eq!(task.get_progress(), 100.0);
|
||||
assert!(task.is_finished());
|
||||
task.progress();
|
||||
}
|
||||
assert_eq!(task.get_movement_progress(), Some(50));
|
||||
for _ in 46..91 {
|
||||
task.progress();
|
||||
}
|
||||
assert_eq!(task.get_movement_progress(), Some(100));
|
||||
assert_eq!(task.get_progress(), 100.0);
|
||||
assert!(task.is_finished());
|
||||
task.progress();
|
||||
assert_eq!(task.get_progress(), 100.0);
|
||||
task.pop_front();
|
||||
assert_eq!(task.get_movement_progress(), None);
|
||||
assert_eq!(task.get_progress(), 0.0);
|
||||
assert!(!task.is_finished());
|
||||
assert_eq!(task.get_progress(), 100.0);
|
||||
task.pop_front();
|
||||
assert_eq!(task.get_movement_progress(), None);
|
||||
assert_eq!(task.get_progress(), 0.0);
|
||||
assert!(!task.is_finished());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overall_digging_progress_counts_completed_cells_and_current_step() -> () {
|
||||
let mut task: DiggingTask = DiggingTask::new([(5, 3), (5, 4)].into(), 25.0);
|
||||
assert_eq!(task.get_total_progress(), 0.0);
|
||||
task.progress();
|
||||
assert_eq!(task.get_progress(), 25.0);
|
||||
assert_eq!(task.get_total_progress(), 12.5);
|
||||
task.pop_front();
|
||||
assert_eq!(task.get_progress(), 0.0);
|
||||
assert_eq!(task.get_total_progress(), 50.0);
|
||||
task.progress();
|
||||
assert_eq!(task.get_total_progress(), 62.5);
|
||||
task.pop_front();
|
||||
assert_eq!(task.get_total_progress(), 100.0);
|
||||
let empty: DiggingTask = DiggingTask::new([].into(), 1.0);
|
||||
assert_eq!(empty.get_total_progress(), 100.0);
|
||||
let mut task: DiggingTask = DiggingTask::new([(5, 3), (5, 4)].into(), 25.0);
|
||||
assert_eq!(task.get_total_progress(), 0.0);
|
||||
task.progress();
|
||||
assert_eq!(task.get_progress(), 25.0);
|
||||
assert_eq!(task.get_total_progress(), 12.5);
|
||||
task.pop_front();
|
||||
assert_eq!(task.get_progress(), 0.0);
|
||||
assert_eq!(task.get_total_progress(), 50.0);
|
||||
task.progress();
|
||||
assert_eq!(task.get_total_progress(), 62.5);
|
||||
task.pop_front();
|
||||
assert_eq!(task.get_total_progress(), 100.0);
|
||||
let empty: DiggingTask = DiggingTask::new([].into(), 1.0);
|
||||
assert_eq!(empty.get_total_progress(), 100.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn task_conversion_keeps_completed_steps_and_original_length() -> () {
|
||||
let mut digging: DiggingTask = DiggingTask::new([(5, 3), (5, 4)].into(), 25.0);
|
||||
digging.pop_front();
|
||||
let mut moving: MovingTask = MovingTask::from_digging(digging);
|
||||
assert_eq!(moving.get_total_steps(), 2);
|
||||
assert_eq!(moving.get_total_progress(), 50.0);
|
||||
for _ in 0..91 {
|
||||
moving.progress();
|
||||
}
|
||||
assert_eq!(moving.get_total_progress(), 100.0);
|
||||
let mut digging: DiggingTask = DiggingTask::from_moving(moving, 25.0);
|
||||
assert_eq!(digging.get_total_steps(), 2);
|
||||
assert_eq!(digging.get_total_progress(), 50.0);
|
||||
digging.progress();
|
||||
assert_eq!(digging.get_total_progress(), 62.5);
|
||||
let mut digging: DiggingTask = DiggingTask::new([(5, 3), (5, 4)].into(), 25.0);
|
||||
digging.pop_front();
|
||||
let mut moving: MovingTask = MovingTask::from_digging(digging);
|
||||
assert_eq!(moving.get_total_steps(), 2);
|
||||
assert_eq!(moving.get_total_progress(), 50.0);
|
||||
for _ in 0..91 {
|
||||
moving.progress();
|
||||
}
|
||||
assert_eq!(moving.get_total_progress(), 100.0);
|
||||
let mut digging: DiggingTask = DiggingTask::from_moving(moving, 25.0);
|
||||
assert_eq!(digging.get_total_steps(), 2);
|
||||
assert_eq!(digging.get_total_progress(), 50.0);
|
||||
digging.progress();
|
||||
assert_eq!(digging.get_total_progress(), 62.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn digging_queue_formats_step_total_and_pending_progress() -> () {
|
||||
let mut miner: MinerUnit = MinerUnit::new(Players::Player, (5, 2));
|
||||
let mut task: DiggingTask = DiggingTask::new([(5, 3), (5, 4)].into(), 25.0);
|
||||
task.pop_front();
|
||||
task.progress();
|
||||
miner.set_task(Tasks::Digging(task));
|
||||
miner.set_task(Tasks::Moving(MovingTask::new([(5, 5)].into())));
|
||||
let lines: Vec<ratatui::text::Line<'_>> = miner.get_tasks_formatted();
|
||||
assert!(lines[1].to_string().contains("Step: 25% | Total: 62%"));
|
||||
assert!(lines[3].to_string().contains("Step: 0% | Total: 0%"));
|
||||
let mut miner: MinerUnit = MinerUnit::new(Players::Player, (5, 2));
|
||||
let mut task: DiggingTask = DiggingTask::new([(5, 3), (5, 4)].into(), 25.0);
|
||||
task.pop_front();
|
||||
task.progress();
|
||||
miner.set_task(Tasks::Digging(task));
|
||||
miner.set_task(Tasks::Moving(MovingTask::new([(5, 5)].into())));
|
||||
let lines: Vec<ratatui::text::Line<'_>> = miner.get_tasks_formatted();
|
||||
assert!(lines[1].to_string().contains("Step: 25% | Total: 62%"));
|
||||
assert!(lines[3].to_string().contains("Step: 0% | Total: 0%"));
|
||||
}
|
||||
Reference in new issue
Block a user