Add ore level support and unify text rendering

Introduce a numeric `level` field for `BaseBuilding` and `Ore`, update
their
constructors and `get_level` implementations, and increase the default
ore
amount to the new maximum. Extend the `Structure` and `Unit` traits with
`base_text` and `text` methods, add `is_unit` to `Units`, and adjust the
`Structures` enum to delegate text rendering. Refactor `SidePanelWidget`
to
use the unified trait methods, removing duplicated rendering code.
Update
`BoardState` to create ore structures with an initial level of 1.
This commit is contained in:
2026-05-05 00:18:49 +02:00
parent 766b65b2fc
commit 55a260755c
8 changed files with 115 additions and 58 deletions
+2 -2
View File
@@ -81,9 +81,9 @@ impl BoardState {
} else if enemy_base { } else if enemy_base {
Structures::Base(BaseBuilding::new(Players::Enemy)) Structures::Base(BaseBuilding::new(Players::Enemy))
} else if player_ore { } else if player_ore {
Structures::Ore(Ore::new(Players::Player)) Structures::Ore(Ore::new(Players::Player, 1))
} else if enemy_ore { } else if enemy_ore {
Structures::Ore(Ore::new(Players::Enemy)) Structures::Ore(Ore::new(Players::Enemy, 1))
} else { } else {
Structures::Stone(Stone::new()) Structures::Stone(Stone::new())
}; };
@@ -15,7 +15,7 @@ impl BaseBuilding {
durability: 1500, durability: 1500,
stress: 0, stress: 0,
owner, owner,
level: b'1', level: 1,
} }
} }
} }
@@ -34,7 +34,7 @@ impl Structure for BaseBuilding {
} }
fn get_level(&self) -> char { fn get_level(&self) -> char {
self.level as char (self.level + b'0') as char
} }
fn get_durability(&self) -> u16 { fn get_durability(&self) -> u16 {
@@ -1,22 +1,26 @@
use crate::app::states::skirmish_states::{Players, structures::Structure}; use crate::app::states::skirmish_states::{Players, structures::Structure};
use ratatui::style::Color; use ratatui::{
style::{Color, Stylize},
text::Line,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Ore { pub struct Ore {
owner: Players, owner: Players,
ore_amount: u16, ore_amount: u16,
// ore_max_amount: u16,
durability: u16, durability: u16,
stress: u8, stress: u8,
level: u8,
} }
impl Ore { impl Ore {
pub fn new(owner: Players) -> Self { pub fn new(owner: Players, level: u8) -> Self {
Self { Self {
owner, owner,
ore_amount: 1000, ore_amount: 5000,
durability: 1000, durability: 1000,
stress: 0, stress: 0,
level,
} }
} }
@@ -25,7 +29,12 @@ impl Ore {
} }
pub fn get_max_ore_amount(&self) -> u16 { pub fn get_max_ore_amount(&self) -> u16 {
1000 5000
}
pub fn set_owner(&mut self, new_owner: Players) -> &mut Self {
self.owner = new_owner;
self
} }
} }
@@ -47,7 +56,11 @@ impl Structure for Ore {
} }
fn get_level(&self) -> char { fn get_level(&self) -> char {
' ' if self.level != 0 {
(self.level + b'0') as char
} else {
' '
}
} }
fn get_max_durability(&self) -> u16 { fn get_max_durability(&self) -> u16 {
@@ -65,4 +78,17 @@ impl Structure for Ore {
fn get_stress(&self) -> u8 { fn get_stress(&self) -> u8 {
self.stress self.stress
} }
fn text(&self) -> Vec<Line<'_>> {
let mut lines: Vec<Line<'_>> = Structure::base_text(self);
lines.push(Line::from_iter([
"Ore: ".gray(),
self.get_ore_amount().to_string().cyan(),
"/".gray(),
self.get_max_ore_amount().to_string().cyan(),
]));
lines
}
} }
@@ -2,7 +2,7 @@ use crate::app::states::skirmish_states::{
Players, Players,
structures::{BaseBuilding, Ore, Stone, Structure, Tunnel}, structures::{BaseBuilding, Ore, Stone, Structure, Tunnel},
}; };
use ratatui::style::Color; use ratatui::{style::Color, text::Line};
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Structures { pub enum Structures {
@@ -56,4 +56,8 @@ impl Structure for Structures {
fn get_owner(&self) -> Players { fn get_owner(&self) -> Players {
self.structure().get_owner() self.structure().get_owner()
} }
fn text(&self) -> Vec<Line<'_>> {
self.structure().text()
}
} }
@@ -1,13 +1,57 @@
use crate::app::states::skirmish_states::Players; use crate::app::states::skirmish_states::Players;
use ratatui::style::Color; use ratatui::{
style::{Color, Stylize},
text::Line,
};
pub trait Structure { pub trait Structure {
fn get_tag(&self) -> char; fn get_tag(&self) -> char;
fn get_color(&self) -> Color; fn get_color(&self) -> Color;
fn get_level(&self) -> char; fn get_level(&self) -> char;
fn get_stress(&self) -> u8; fn get_stress(&self) -> u8;
fn get_durability(&self) -> u16; fn get_durability(&self) -> u16;
fn get_max_durability(&self) -> u16; fn get_max_durability(&self) -> u16;
fn get_name(&self) -> &'static str; fn get_name(&self) -> &'static str;
fn get_owner(&self) -> Players; fn get_owner(&self) -> Players;
fn base_text(&self) -> Vec<Line<'_>> {
let durability: u16 = self.get_durability();
let max_durability: u16 = self.get_max_durability();
let durability_percent: u32 = durability as u32 * 100 / max_durability as u32;
let stress: u8 = self.get_stress();
let level: char = self.get_level();
let mut lines: Vec<Line<'_>> = vec![
Line::from_iter([
"Durability: ".gray(),
durability.to_string().cyan(),
"/".gray(),
max_durability.to_string().cyan(),
" ( ".gray(),
durability_percent.to_string().cyan(),
"% )".gray(),
]),
Line::from_iter(["Stress: ".gray(), stress.to_string().cyan(), "%".gray()]),
];
if level != ' ' {
lines.push(Line::from_iter([
"Level: ".gray(),
level.to_string().cyan(),
]));
}
lines
}
fn text(&self) -> Vec<Line<'_>> {
self.base_text()
}
} }
@@ -42,4 +42,8 @@ impl Unit for Option<Units> {
fn get_owner(&self) -> Players { fn get_owner(&self) -> Players {
self.map_or(Players::Unclaimed, |u| u.get_owner()) self.map_or(Players::Unclaimed, |u| u.get_owner())
} }
fn is_unit(&self) -> bool {
self.map_or(false, |u| u.is_unit())
}
} }
@@ -1,7 +1,27 @@
use crate::app::states::skirmish_states::Players; use crate::app::states::skirmish_states::Players;
use ratatui::{
style::Stylize,
text::{Line, Span},
};
pub trait Unit { pub trait Unit {
fn get_owner(&self) -> Players; fn get_owner(&self) -> Players;
fn get_tag(&self) -> char; fn get_tag(&self) -> char;
fn get_name(&self) -> &'static str; fn get_name(&self) -> &'static str;
fn is_unit(&self) -> bool {
true
}
fn base_text(&self) -> Vec<Line<'_>> {
let owner: Span<'_> = self.get_owner().get_span();
vec![Line::from_iter(vec!["Owner: ".gray(), owner])]
}
fn text(&self) -> Vec<Line<'_>> {
self.base_text()
}
} }
+5 -46
View File
@@ -8,8 +8,7 @@ use crate::app::{
use ratatui::{ use ratatui::{
buffer::Buffer, buffer::Buffer,
layout::{Alignment, Constraint, Layout, Margin, Rect}, layout::{Alignment, Constraint, Layout, Margin, Rect},
style::{Color, Stylize}, style::Color,
text::{Line, Span},
widgets::{Block, BorderType, Borders, Padding, Paragraph, Widget}, widgets::{Block, BorderType, Borders, Padding, Paragraph, Widget},
}; };
@@ -61,52 +60,12 @@ impl<'a> SidePanelWidget<'a> {
} }
fn get_area_constraints(&self) -> [Constraint; 2] { fn get_area_constraints(&self) -> [Constraint; 2] {
if !self.unit.get_name().is_empty() { if self.unit.is_unit() {
[Constraint::Percentage(50), Constraint::Percentage(50)] [Constraint::Percentage(50), Constraint::Percentage(50)]
} else { } else {
[Constraint::Percentage(100), Constraint::Percentage(0)] [Constraint::Percentage(100), Constraint::Percentage(0)]
} }
} }
// TODO: Relocate this to Structure trait implementations
fn structure_text(&self) -> Vec<Line<'_>> {
let s: &Structures = self.structure;
let durability: u16 = s.get_durability();
let max_durability: u16 = s.get_max_durability();
let durability_percent: u32 = durability as u32 * 100u32 / max_durability as u32;
let stress: u8 = s.get_stress();
let level: char = s.get_level();
let mut lines: Vec<Line<'_>> = vec![
Line::from_iter([
"Durability: ".gray(),
durability.to_string().cyan(),
"/".gray(),
max_durability.to_string().cyan(),
" ( ".gray(),
durability_percent.to_string().cyan(),
"% )".gray(),
]),
Line::from_iter(["Stress: ".gray(), stress.to_string().cyan(), "%".gray()]),
];
if level != ' ' {
lines.push(Line::from_iter([
"Level: ".gray(),
level.to_string().cyan(),
]));
}
lines
}
// TODO: Relocate this to Unit trait implementations
fn unit_text(&self) -> Vec<Line<'_>> {
let u: &Option<Units> = self.unit;
let owner: Span<'_> = u.get_owner().get_span();
vec![Line::from_iter(vec!["Owner: ".gray(), owner])]
}
} }
impl Widget for SidePanelWidget<'_> { impl Widget for SidePanelWidget<'_> {
@@ -121,13 +80,13 @@ impl Widget for SidePanelWidget<'_> {
let [structure_area, unit_area] = let [structure_area, unit_area] =
Layout::vertical(self.get_area_constraints()).areas(inner_area); Layout::vertical(self.get_area_constraints()).areas(inner_area);
Paragraph::new(self.structure_text()) Paragraph::new(self.structure.text())
.alignment(Alignment::Left) .alignment(Alignment::Left)
.block(self.get_inner_block(self.structure.get_name().to_string(), Color::Cyan)) .block(self.get_inner_block(self.structure.get_name().to_string(), Color::Cyan))
.render(structure_area, buf); .render(structure_area, buf);
if !self.unit.get_name().is_empty() { if self.unit.is_unit() {
Paragraph::new(self.unit_text()) Paragraph::new(self.unit.text())
.alignment(Alignment::Left) .alignment(Alignment::Left)
.block(self.get_inner_block(self.unit.get_name().to_string(), Color::Green)) .block(self.get_inner_block(self.unit.get_name().to_string(), Color::Green))
.render(unit_area, buf); .render(unit_area, buf);