generated from GarandPLG/rust-flake-template
Rename iron to metal, add mining and recruitment
- Updated CLI, README and documentation to use `--starting-metal` with `--starting-iron` retained as an alias. - Introduced `Resources` struct and constants for wood delivery and mine production. - Added new structures `Mine` and `MineConstruction` with cost, production and cancel logic. - Implemented building view for constructing mines and recruitment view for spawning miners. - Extended keybindings with `Building` and `Recruitment` actions and updated action availability logic. - Modified game state, board, units and UI widgets to handle metal resources and new construction mechanics. - Updated tests and increased total test count to 82, covering resources, construction, recruitment and context‑aware keybindings.
This commit is contained in:
1 parent
305cc2c4a2
commit
a981663152
35 files changed
+2160
-74
No files matched your search
@@ -4,7 +4,9 @@ use crate::app::{
|
||||
FocusedCell, Offset,
|
||||
skirmish_states::{
|
||||
CellSizes, MarkedCells, MoveFocusedCell, Players, ZoomLevel,
|
||||
structures::{BaseBuilding, Ore, Stone, Structure, Structures, Tunnel},
|
||||
structures::{
|
||||
BaseBuilding, BuildingTerrain, MineConstruction, Ore, Stone, Structure, Structures, Tunnel,
|
||||
},
|
||||
tasks::{DiggingTask, MovingTask, Task, Tasks},
|
||||
units::{MinerUnit, Unit, Units},
|
||||
},
|
||||
@@ -32,6 +34,138 @@ pub struct BoardState {
|
||||
}
|
||||
|
||||
impl BoardState {
|
||||
pub fn advance_recruitment(&mut self) -> () {
|
||||
for (row, cells) in self.cells.iter_mut().enumerate() {
|
||||
for (col, cell) in cells.iter_mut().enumerate() {
|
||||
if let Structures::Base(mut base) = cell.get_structure() {
|
||||
if base.advance_recruitment(cell.get_ref_option_unit().is_some()) {
|
||||
*cell.get_mut_option_unit() =
|
||||
Some(Units::Miner(MinerUnit::new(base.get_owner(), (row, col))));
|
||||
}
|
||||
cell.set_structure(Structures::Base(base));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn adjacent_cells(&self, (row, col): (usize, usize)) -> Vec<&CellWidget> {
|
||||
let coordinates: [(usize, usize); 4] = [
|
||||
(row.wrapping_sub(1), col),
|
||||
(row.saturating_add(1), col),
|
||||
(row, col.wrapping_sub(1)),
|
||||
(row, col.saturating_add(1)),
|
||||
];
|
||||
coordinates
|
||||
.into_iter()
|
||||
.filter_map(|(row, col): (usize, usize)| -> Option<&CellWidget> {
|
||||
self
|
||||
.cells
|
||||
.get(row)
|
||||
.and_then(|cells: &Vec<CellWidget>| -> Option<&CellWidget> { cells.get(col) })
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn has_adjacent_builder(&self, coords: (usize, usize), owner: Players) -> bool {
|
||||
owner != Players::Unclaimed
|
||||
&& self
|
||||
.adjacent_cells(coords)
|
||||
.iter()
|
||||
.any(|cell: &&CellWidget| -> bool {
|
||||
let unit: &Option<Units> = cell.get_ref_option_unit();
|
||||
unit.get_owner() == owner && unit.get_can_build()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn can_build_at(
|
||||
&self,
|
||||
(row, col): (usize, usize),
|
||||
owner: Players,
|
||||
terrain: BuildingTerrain,
|
||||
) -> bool {
|
||||
if owner == Players::Unclaimed {
|
||||
return false;
|
||||
}
|
||||
let Some(cell) = self
|
||||
.cells
|
||||
.get(row)
|
||||
.and_then(|cells: &Vec<CellWidget>| -> Option<&CellWidget> { cells.get(col) })
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
if cell.get_ref_option_unit().is_some() {
|
||||
return false;
|
||||
}
|
||||
if !self.has_adjacent_builder((row, col), owner) {
|
||||
return false;
|
||||
}
|
||||
match (terrain, cell.get_structure()) {
|
||||
(BuildingTerrain::Ore, Structures::Ore(ore)) => {
|
||||
ore.get_owner() == Players::Unclaimed && ore.get_ore_amount() > 0
|
||||
}
|
||||
(BuildingTerrain::Stone, Structures::Stone(_)) => {
|
||||
self
|
||||
.adjacent_cells((row, col))
|
||||
.iter()
|
||||
.any(|cell: &&CellWidget| -> bool {
|
||||
matches!(cell.get_structure(), Structures::Tunnel(_))
|
||||
|| matches!(cell.get_structure(), Structures::Base(_))
|
||||
&& cell.get_structure().get_owner() == owner
|
||||
})
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_mine_construction(&mut self, coords: (usize, usize), owner: Players) -> bool {
|
||||
if !self.can_build_at(coords, owner, BuildingTerrain::Ore) {
|
||||
return false;
|
||||
}
|
||||
let cell: &mut CellWidget = self.get_mut_cell(coords.0, coords.1);
|
||||
if let Structures::Ore(ore) = cell.get_structure()
|
||||
&& let Some(site) = MineConstruction::new(owner, ore)
|
||||
{
|
||||
cell.set_structure(Structures::MineConstruction(site));
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn cancel_mine_construction(&mut self, (row, col): (usize, usize), owner: Players) -> bool {
|
||||
let Some(cell): Option<&mut CellWidget> = self
|
||||
.cells
|
||||
.get_mut(row)
|
||||
.and_then(|cells: &mut Vec<CellWidget>| -> Option<&mut CellWidget> { cells.get_mut(col) })
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
if let Structures::MineConstruction(site) = cell.get_structure()
|
||||
&& site.get_owner() == owner
|
||||
{
|
||||
cell.set_structure(Structures::Ore(site.cancel()));
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn advance_construction(&mut self) -> () {
|
||||
for row in 0..self.cells.len() {
|
||||
for col in 0..self.cells[row].len() {
|
||||
if let Structures::MineConstruction(mut site) = self.cells[row][col].get_structure()
|
||||
&& self.has_adjacent_builder((row, col), site.get_owner())
|
||||
{
|
||||
site.advance();
|
||||
let structure: Structures = if let Some(mine) = site.into_mine() {
|
||||
Structures::Mine(mine)
|
||||
} else {
|
||||
Structures::MineConstruction(site)
|
||||
};
|
||||
self.cells[row][col].set_structure(structure);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
area: &Rect,
|
||||
map_width: usize,
|
||||
@@ -79,9 +213,9 @@ impl BoardState {
|
||||
} else if enemy_base {
|
||||
Structures::Base(BaseBuilding::new(Players::Enemy))
|
||||
} else if player_ore {
|
||||
Structures::Ore(Ore::new(Players::Player, 1))
|
||||
Structures::Ore(Ore::new(Players::Unclaimed, 1))
|
||||
} else if enemy_ore {
|
||||
Structures::Ore(Ore::new(Players::Enemy, 1))
|
||||
Structures::Ore(Ore::new(Players::Unclaimed, 1))
|
||||
} else {
|
||||
Structures::Stone(Stone::new())
|
||||
};
|
||||
@@ -286,10 +420,11 @@ impl BoardState {
|
||||
.get(row)
|
||||
.and_then(|cells| cells.get(col))
|
||||
.is_some_and(|cell| {
|
||||
matches!(
|
||||
cell.get_structure(),
|
||||
Structures::Tunnel(_) | Structures::Base(_)
|
||||
)
|
||||
self
|
||||
.marked_cells
|
||||
.selected_unit
|
||||
.as_ref()
|
||||
.is_some_and(|unit: &Units| -> bool { cell.get_structure().can_enter(unit) })
|
||||
})
|
||||
}
|
||||
|
||||
@@ -419,11 +554,10 @@ impl BoardState {
|
||||
let cell: &mut CellWidget = self.get_mut_cell(target_row, target_col);
|
||||
if cell.get_ref_option_unit().is_none() {
|
||||
let structure: Structures = cell.get_structure();
|
||||
let walkable: bool = structure.can_enter(&unit);
|
||||
// Previously planned stone may already be a tunnel when this task starts.
|
||||
task = match (task, structure) {
|
||||
(Tasks::Digging(dig), Structures::Tunnel(_) | Structures::Base(_)) => {
|
||||
Tasks::Moving(MovingTask::from_digging(dig))
|
||||
}
|
||||
(Tasks::Digging(dig), _) if walkable => Tasks::Moving(MovingTask::from_digging(dig)),
|
||||
(Tasks::Moving(moving), Structures::Stone(_)) => {
|
||||
Tasks::Digging(DiggingTask::from_moving(moving, unit.get_digging_power()))
|
||||
}
|
||||
@@ -444,7 +578,7 @@ impl BoardState {
|
||||
cell.set_structure(Structures::Stone(stone));
|
||||
}
|
||||
}
|
||||
(Tasks::Moving(moving), Structures::Tunnel(_) | Structures::Base(_)) => {
|
||||
(Tasks::Moving(moving), _) if walkable => {
|
||||
moving.progress();
|
||||
if moving.is_finished() {
|
||||
destination = (target_row, target_col);
|
||||
|
||||
@@ -4,6 +4,7 @@ mod game_mode;
|
||||
mod marked_cells;
|
||||
mod offset;
|
||||
mod players;
|
||||
pub mod resources;
|
||||
pub mod structures;
|
||||
pub mod tasks;
|
||||
pub mod units;
|
||||
@@ -15,4 +16,5 @@ pub use game_mode::GameMode;
|
||||
pub use marked_cells::MarkedCells;
|
||||
pub use offset::Offset;
|
||||
pub use players::Players;
|
||||
pub use resources::Resources;
|
||||
pub use zoom_level::{CellSizes, ZoomLevel};
|
||||
@@ -0,0 +1,41 @@
|
||||
use crate::app::threads::TICK_MS;
|
||||
|
||||
pub const WOOD_DELIVERY_AMOUNT: u32 = 5;
|
||||
pub const WOOD_DELIVERY_INTERVAL_MS: u64 = 5000;
|
||||
pub const WOOD_DELIVERY_TICKS: u64 = WOOD_DELIVERY_INTERVAL_MS.div_ceil(TICK_MS as u64);
|
||||
pub const MINE_PRODUCTION_TICKS: u64 = 1000_u64.div_ceil(TICK_MS as u64);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Resources {
|
||||
pub wood: u32,
|
||||
pub metal: u32,
|
||||
}
|
||||
|
||||
impl Resources {
|
||||
pub fn can_afford(&self, cost: Self) -> bool {
|
||||
self.wood >= cost.wood && self.metal >= cost.metal
|
||||
}
|
||||
|
||||
pub fn spend(&mut self, cost: Self) -> bool {
|
||||
if !self.can_afford(cost) {
|
||||
return false;
|
||||
}
|
||||
self.wood -= cost.wood;
|
||||
self.metal -= cost.metal;
|
||||
true
|
||||
}
|
||||
pub fn new(wood: u16, metal: u16) -> Self {
|
||||
Self {
|
||||
wood: u32::from(wood),
|
||||
metal: u32::from(metal),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_wood(&mut self, amount: u32) -> () {
|
||||
self.wood = self.wood.saturating_add(amount);
|
||||
}
|
||||
|
||||
pub fn add_metal(&mut self, amount: u32) -> () {
|
||||
self.metal = self.metal.saturating_add(amount);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::app::states::skirmish_states::{Players, structures::Structure};
|
||||
use ratatui::style::Color;
|
||||
use crate::app::threads::TICK_MS;
|
||||
use ratatui::{style::Color, text::Line};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct BaseBuilding {
|
||||
@@ -7,15 +8,78 @@ pub struct BaseBuilding {
|
||||
stress: u8,
|
||||
owner: Players,
|
||||
level: u8,
|
||||
queued_miners: u32,
|
||||
recruitment_ticks: u16,
|
||||
waiting_for_departure: bool,
|
||||
}
|
||||
|
||||
impl BaseBuilding {
|
||||
pub const RECRUITMENT_TICKS: u16 = 10000_u16.div_ceil(TICK_MS as u16);
|
||||
|
||||
pub fn queued_miners(&self) -> u32 {
|
||||
self.queued_miners
|
||||
}
|
||||
|
||||
pub fn recruitment_progress(&self) -> u16 {
|
||||
(u32::from(self.recruitment_ticks) * 100 / u32::from(Self::RECRUITMENT_TICKS)) as u16
|
||||
}
|
||||
|
||||
pub fn can_queue_miner(&self) -> bool {
|
||||
self.owner != Players::Unclaimed && self.queued_miners < u32::MAX
|
||||
}
|
||||
|
||||
pub fn queue_miner(&mut self) -> bool {
|
||||
if !self.can_queue_miner() {
|
||||
return false;
|
||||
}
|
||||
self.queued_miners += 1;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn advance_recruitment(&mut self, occupied: bool) -> bool {
|
||||
if self.waiting_for_departure {
|
||||
if occupied {
|
||||
return false;
|
||||
}
|
||||
self.waiting_for_departure = false;
|
||||
}
|
||||
if self.queued_miners == 0 {
|
||||
return false;
|
||||
}
|
||||
self.recruitment_ticks = self
|
||||
.recruitment_ticks
|
||||
.saturating_add(1)
|
||||
.min(Self::RECRUITMENT_TICKS);
|
||||
if self.recruitment_ticks < Self::RECRUITMENT_TICKS || occupied {
|
||||
return false;
|
||||
}
|
||||
self.queued_miners -= 1;
|
||||
self.recruitment_ticks = 0;
|
||||
self.waiting_for_departure = true;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn recruitment_status(&self, occupied: bool) -> &'static str {
|
||||
if self.queued_miners == 0 {
|
||||
"Queue empty"
|
||||
} else if self.waiting_for_departure && occupied {
|
||||
"Paused: waiting for Miner to leave base"
|
||||
} else if self.recruitment_ticks == Self::RECRUITMENT_TICKS && occupied {
|
||||
"Ready: waiting for free base cell"
|
||||
} else {
|
||||
"Recruiting"
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(owner: Players) -> Self {
|
||||
Self {
|
||||
durability: 1500,
|
||||
stress: 0,
|
||||
owner,
|
||||
level: 1,
|
||||
queued_miners: 0,
|
||||
recruitment_ticks: 0,
|
||||
waiting_for_departure: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -56,4 +120,19 @@ impl Structure for BaseBuilding {
|
||||
fn get_owner(&self) -> Players {
|
||||
self.owner
|
||||
}
|
||||
|
||||
fn text(&self) -> Vec<Line<'_>> {
|
||||
let mut lines: Vec<Line<'_>> = self.base_text();
|
||||
lines.push(Line::from(format!(
|
||||
"Recruitment queue: {}",
|
||||
self.queued_miners
|
||||
)));
|
||||
if self.queued_miners > 0 {
|
||||
lines.push(Line::from(format!(
|
||||
"Miner progress: {}%",
|
||||
self.recruitment_progress()
|
||||
)));
|
||||
}
|
||||
lines
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
use crate::app::states::skirmish_states::{
|
||||
Players, Resources,
|
||||
structures::{Ore, Structure},
|
||||
units::{Unit, Units},
|
||||
};
|
||||
use ratatui::{
|
||||
style::{Color, Stylize},
|
||||
text::Line,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Mine {
|
||||
owner: Players,
|
||||
ore: Ore,
|
||||
}
|
||||
|
||||
impl Mine {
|
||||
pub const COST: Resources = Resources {
|
||||
wood: 20,
|
||||
metal: 10,
|
||||
};
|
||||
pub const METAL_PER_CYCLE: u16 = 1;
|
||||
pub const STAFFED_METAL_PER_CYCLE: u16 = 2;
|
||||
|
||||
pub fn new(owner: Players, ore: Ore) -> Option<Self> {
|
||||
if owner == Players::Unclaimed || ore.get_owner() != Players::Unclaimed {
|
||||
return None;
|
||||
}
|
||||
Some(Self { owner, ore })
|
||||
}
|
||||
|
||||
pub fn get_ore_amount(&self) -> u16 {
|
||||
self.ore.get_ore_amount()
|
||||
}
|
||||
|
||||
pub fn into_ore(self) -> Ore {
|
||||
self.ore
|
||||
}
|
||||
|
||||
pub fn produce(&mut self) -> u16 {
|
||||
self.produce_with_miner(false)
|
||||
}
|
||||
|
||||
pub fn accepts_unit(&self, unit: &Units) -> bool {
|
||||
matches!(unit, Units::Miner(_)) && unit.get_owner() == self.owner
|
||||
}
|
||||
|
||||
pub fn production_per_cycle(&self, staffed: bool) -> u16 {
|
||||
let rate: u16 = if staffed {
|
||||
Self::STAFFED_METAL_PER_CYCLE
|
||||
} else {
|
||||
Self::METAL_PER_CYCLE
|
||||
};
|
||||
rate.min(self.get_ore_amount())
|
||||
}
|
||||
|
||||
pub fn produce_with_miner(&mut self, staffed: bool) -> u16 {
|
||||
self.ore.extract(self.production_per_cycle(staffed))
|
||||
}
|
||||
}
|
||||
|
||||
impl Structure for Mine {
|
||||
fn get_tag(&self) -> char {
|
||||
'M'
|
||||
}
|
||||
fn get_color(&self) -> Color {
|
||||
self.owner.get_color()
|
||||
}
|
||||
fn get_level(&self) -> char {
|
||||
self.ore.get_level()
|
||||
}
|
||||
fn get_stress(&self) -> u8 {
|
||||
self.ore.get_stress()
|
||||
}
|
||||
fn get_durability(&self) -> u16 {
|
||||
self.ore.get_durability()
|
||||
}
|
||||
fn get_max_durability(&self) -> u16 {
|
||||
self.ore.get_max_durability()
|
||||
}
|
||||
fn get_name(&self) -> &'static str {
|
||||
"Mine"
|
||||
}
|
||||
fn get_owner(&self) -> Players {
|
||||
self.owner
|
||||
}
|
||||
fn text(&self) -> Vec<Line<'_>> {
|
||||
let mut lines: Vec<Line<'_>> = self.base_text();
|
||||
lines.push(Line::from_iter([
|
||||
"Ore: ".gray(),
|
||||
self.get_ore_amount().to_string().cyan(),
|
||||
format!("/{}", self.ore.get_max_ore_amount()).gray(),
|
||||
]));
|
||||
lines
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
use crate::app::{
|
||||
states::skirmish_states::{
|
||||
Players,
|
||||
structures::{Mine, Ore, Structure},
|
||||
},
|
||||
threads::TICK_MS,
|
||||
};
|
||||
use ratatui::{style::Color, text::Line};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct MineConstruction {
|
||||
mine: Mine,
|
||||
ticks: u16,
|
||||
}
|
||||
|
||||
impl MineConstruction {
|
||||
pub const BUILD_TICKS: u16 = 10000_u16.div_ceil(TICK_MS as u16);
|
||||
|
||||
pub fn new(owner: Players, ore: Ore) -> Option<Self> {
|
||||
Mine::new(owner, ore).map(|mine: Mine| -> Self { Self { mine, ticks: 0 } })
|
||||
}
|
||||
|
||||
pub fn advance(&mut self) -> () {
|
||||
self.ticks = self.ticks.saturating_add(1).min(Self::BUILD_TICKS);
|
||||
}
|
||||
|
||||
pub fn is_finished(&self) -> bool {
|
||||
self.ticks == Self::BUILD_TICKS
|
||||
}
|
||||
|
||||
pub fn progress_percent(&self) -> u16 {
|
||||
(u32::from(self.ticks) * 100 / u32::from(Self::BUILD_TICKS)) as u16
|
||||
}
|
||||
|
||||
pub fn into_mine(self) -> Option<Mine> {
|
||||
self.is_finished().then_some(self.mine)
|
||||
}
|
||||
|
||||
pub fn cancel(self) -> Ore {
|
||||
self.mine.into_ore()
|
||||
}
|
||||
}
|
||||
|
||||
impl Structure for MineConstruction {
|
||||
fn get_tag(&self) -> char {
|
||||
'C'
|
||||
}
|
||||
fn get_color(&self) -> Color {
|
||||
self.mine.get_color()
|
||||
}
|
||||
fn get_level(&self) -> char {
|
||||
self.mine.get_level()
|
||||
}
|
||||
fn get_stress(&self) -> u8 {
|
||||
self.mine.get_stress()
|
||||
}
|
||||
fn get_durability(&self) -> u16 {
|
||||
self.mine.get_durability()
|
||||
}
|
||||
fn get_max_durability(&self) -> u16 {
|
||||
self.mine.get_max_durability()
|
||||
}
|
||||
fn get_name(&self) -> &'static str {
|
||||
"Mine construction"
|
||||
}
|
||||
fn get_owner(&self) -> Players {
|
||||
self.mine.get_owner()
|
||||
}
|
||||
fn text(&self) -> Vec<Line<'_>> {
|
||||
vec![
|
||||
Line::from(format!("Construction: {}%", self.progress_percent())),
|
||||
Line::from(format!("Ore: {}", self.mine.get_ore_amount())),
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
mod base;
|
||||
mod mine;
|
||||
mod mine_construction;
|
||||
mod ore;
|
||||
mod stone;
|
||||
mod structure_trait;
|
||||
@@ -6,8 +8,16 @@ mod structures_enum;
|
||||
mod tunnel;
|
||||
|
||||
pub use base::BaseBuilding;
|
||||
pub use mine::Mine;
|
||||
pub use mine_construction::MineConstruction;
|
||||
pub use ore::Ore;
|
||||
pub use stone::Stone;
|
||||
pub use structure_trait::Structure;
|
||||
pub use structures_enum::Structures;
|
||||
pub use tunnel::Tunnel;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum BuildingTerrain {
|
||||
Stone,
|
||||
Ore,
|
||||
}
|
||||
@@ -32,6 +32,12 @@ impl Ore {
|
||||
5000
|
||||
}
|
||||
|
||||
pub fn extract(&mut self, amount: u16) -> u16 {
|
||||
let extracted: u16 = amount.min(self.ore_amount);
|
||||
self.ore_amount -= extracted;
|
||||
extracted
|
||||
}
|
||||
|
||||
pub fn set_owner(&mut self, new_owner: Players) -> &mut Self {
|
||||
self.owner = new_owner;
|
||||
self
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::app::states::skirmish_states::{
|
||||
Players,
|
||||
structures::{BaseBuilding, Ore, Stone, Structure, Tunnel},
|
||||
structures::{BaseBuilding, Mine, MineConstruction, Ore, Stone, Structure, Tunnel},
|
||||
units::Units,
|
||||
};
|
||||
use ratatui::{
|
||||
style::{Color, Stylize},
|
||||
@@ -13,9 +14,28 @@ pub enum Structures {
|
||||
Tunnel(Tunnel),
|
||||
Stone(Stone),
|
||||
Ore(Ore),
|
||||
Mine(Mine),
|
||||
MineConstruction(MineConstruction),
|
||||
}
|
||||
|
||||
impl Structures {
|
||||
pub fn can_enter(&self, unit: &Units) -> bool {
|
||||
match self {
|
||||
Self::Base(_) | Self::Tunnel(_) => true,
|
||||
Self::Mine(mine) => mine.accepts_unit(unit),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
pub fn build_mine(&mut self, owner: Players) -> bool {
|
||||
if let Self::Ore(ore) = *self
|
||||
&& let Some(mine) = Mine::new(owner, ore)
|
||||
{
|
||||
*self = Self::Mine(mine);
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn s(&self) -> &dyn Structure {
|
||||
match self {
|
||||
@@ -23,6 +43,8 @@ impl Structures {
|
||||
Self::Tunnel(t) => t,
|
||||
Self::Stone(s) => s,
|
||||
Self::Ore(o) => o,
|
||||
Self::Mine(mine) => mine,
|
||||
Self::MineConstruction(site) => site,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use ratatui::{style::Stylize, text::Line};
|
||||
|
||||
use crate::app::states::skirmish_states::{
|
||||
Players,
|
||||
Players, Resources,
|
||||
tasks::{Task, Tasks},
|
||||
units::Unit,
|
||||
};
|
||||
@@ -18,6 +18,10 @@ pub struct MinerUnit {
|
||||
}
|
||||
|
||||
impl MinerUnit {
|
||||
pub const RECRUIT_COST: Resources = Resources {
|
||||
wood: 10,
|
||||
metal: 15,
|
||||
};
|
||||
pub fn new(owner: Players, coords: (usize, usize)) -> Self {
|
||||
Self {
|
||||
owner,
|
||||
@@ -71,6 +75,10 @@ impl Unit for MinerUnit {
|
||||
self.can_dig
|
||||
}
|
||||
|
||||
fn get_can_build(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn get_digging_power(&self) -> f32 {
|
||||
self.digging_power
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@ pub trait Unit {
|
||||
|
||||
fn get_can_dig(&self) -> bool;
|
||||
|
||||
fn get_can_build(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn get_digging_power(&self) -> f32;
|
||||
|
||||
fn get_coords(&self) -> (usize, usize);
|
||||
@@ -66,6 +70,10 @@ pub trait Unit {
|
||||
]));
|
||||
}
|
||||
|
||||
if self.get_can_build() {
|
||||
lines.push(Line::from("Can build: Yes").green());
|
||||
}
|
||||
|
||||
if !have_any_tasks {
|
||||
lines.push(Line::from("Tasks Queue: ".gray()));
|
||||
for task in tasks {
|
||||
|
||||
@@ -28,6 +28,9 @@ impl Units {
|
||||
}
|
||||
|
||||
impl Unit for Units {
|
||||
fn get_can_build(&self) -> bool {
|
||||
self.u().get_can_build()
|
||||
}
|
||||
fn get_tag(&self) -> char {
|
||||
self.u().get_tag()
|
||||
}
|
||||
@@ -82,6 +85,11 @@ impl Unit for Units {
|
||||
}
|
||||
|
||||
impl Unit for Option<Units> {
|
||||
fn get_can_build(&self) -> bool {
|
||||
self
|
||||
.as_ref()
|
||||
.is_some_and(|unit: &Units| -> bool { unit.get_can_build() })
|
||||
}
|
||||
fn is_unit(&self) -> bool {
|
||||
self.as_ref().map_or(false, |u| u.is_unit())
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user