generated from GarandPLG/rust-flake-template
Add max durability and enhance side panel UI
- Introduce `max_durability` fields to BaseBuilding, Stone, and Tunnel structures. - Extend `Structure` trait with `get_max_durability` and implement it in all structures. - Update `Structures` enum to forward `get_max_durability`. - Add color utilities and `get_span` method to `Players` enum for styled text. - Refactor `CellWidget` method names (`get_option_unit`, `set_structure`). - Revise side panel widget to accept references to `Structures` and `Option<Units>`, render colored blocks and detailed stats. - Simplify skirmish view to use `BoardState` directly and adapt side panel rendering.
This commit is contained in:
@@ -1,5 +1,9 @@
|
|||||||
use crate::app::states::skirmish_states::BoardState;
|
use crate::app::states::skirmish_states::BoardState;
|
||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
|
use ratatui::{
|
||||||
|
style::{Color, Stylize},
|
||||||
|
text::Span,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct SkirmishState {
|
pub struct SkirmishState {
|
||||||
@@ -20,3 +24,26 @@ pub enum Players {
|
|||||||
Player,
|
Player,
|
||||||
Enemy,
|
Enemy,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Players {
|
||||||
|
pub fn get_text(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Player => "Player",
|
||||||
|
Self::Enemy => "Enemy",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_color(&self) -> Color {
|
||||||
|
match self {
|
||||||
|
Self::Player => Color::LightBlue,
|
||||||
|
Self::Enemy => Color::LightRed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_span(&self) -> Span<'static> {
|
||||||
|
match self {
|
||||||
|
Self::Player => self.get_text().fg(self.get_color()),
|
||||||
|
Self::Enemy => self.get_text().fg(self.get_color()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use ratatui::style::Color;
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct BaseBuilding {
|
pub struct BaseBuilding {
|
||||||
durability: u16,
|
durability: u16,
|
||||||
|
max_durability: u16,
|
||||||
stress: u8,
|
stress: u8,
|
||||||
owner: Players,
|
owner: Players,
|
||||||
level: u8,
|
level: u8,
|
||||||
@@ -13,6 +14,7 @@ impl BaseBuilding {
|
|||||||
pub fn new(owner: Players) -> Self {
|
pub fn new(owner: Players) -> Self {
|
||||||
Self {
|
Self {
|
||||||
durability: 1500,
|
durability: 1500,
|
||||||
|
max_durability: 1500,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
owner,
|
owner,
|
||||||
level: b'1',
|
level: b'1',
|
||||||
@@ -44,6 +46,10 @@ impl Structure for BaseBuilding {
|
|||||||
self.durability
|
self.durability
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_max_durability(&self) -> u16 {
|
||||||
|
self.max_durability
|
||||||
|
}
|
||||||
|
|
||||||
fn get_stress(&self) -> u8 {
|
fn get_stress(&self) -> u8 {
|
||||||
self.stress
|
self.stress
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use ratatui::style::Color;
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct Stone {
|
pub struct Stone {
|
||||||
durability: u16,
|
durability: u16,
|
||||||
|
max_durability: u16,
|
||||||
stress: u8,
|
stress: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11,6 +12,7 @@ impl Stone {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
durability: 1000,
|
durability: 1000,
|
||||||
|
max_durability: 1000,
|
||||||
stress: 0,
|
stress: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,6 +35,10 @@ impl Structure for Stone {
|
|||||||
self.durability
|
self.durability
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_max_durability(&self) -> u16 {
|
||||||
|
self.max_durability
|
||||||
|
}
|
||||||
|
|
||||||
fn get_stress(&self) -> u8 {
|
fn get_stress(&self) -> u8 {
|
||||||
self.stress
|
self.stress
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ impl Structure for Structures {
|
|||||||
self.structure().get_durability()
|
self.structure().get_durability()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_max_durability(&self) -> u16 {
|
||||||
|
self.structure().get_max_durability()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_stress(&self) -> u8 {
|
fn get_stress(&self) -> u8 {
|
||||||
self.structure().get_stress()
|
self.structure().get_stress()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ pub trait Structure {
|
|||||||
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_name(&self) -> &'static str;
|
fn get_name(&self) -> &'static str;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use ratatui::style::Color;
|
|||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct Tunnel {
|
pub struct Tunnel {
|
||||||
durability: u16,
|
durability: u16,
|
||||||
|
max_durability: u16,
|
||||||
stress: u8,
|
stress: u8,
|
||||||
roof_support: bool,
|
roof_support: bool,
|
||||||
rail: bool,
|
rail: bool,
|
||||||
@@ -14,6 +15,7 @@ impl Tunnel {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
durability: 500,
|
durability: 500,
|
||||||
|
max_durability: 500,
|
||||||
stress: 25,
|
stress: 25,
|
||||||
roof_support: false,
|
roof_support: false,
|
||||||
rail: false,
|
rail: false,
|
||||||
@@ -39,6 +41,10 @@ impl Structure for Tunnel {
|
|||||||
self.durability
|
self.durability
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_max_durability(&self) -> u16 {
|
||||||
|
self.max_durability
|
||||||
|
}
|
||||||
|
|
||||||
fn get_stress(&self) -> u8 {
|
fn get_stress(&self) -> u8 {
|
||||||
self.stress
|
self.stress
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-20
@@ -2,7 +2,7 @@ use crate::app::{
|
|||||||
App,
|
App,
|
||||||
helpers::block_title_helper,
|
helpers::block_title_helper,
|
||||||
keybindings::{Action, count_largest_group},
|
keybindings::{Action, count_largest_group},
|
||||||
states::skirmish_states::{structures::Structure, units::Unit},
|
states::skirmish_states::BoardState,
|
||||||
widgets::{BoardWidget, KeybindingsWidget, SidePanelWidget},
|
widgets::{BoardWidget, KeybindingsWidget, SidePanelWidget},
|
||||||
};
|
};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
@@ -57,6 +57,7 @@ pub fn skirmish_main_area_layout(area: Rect, side_panel: bool) -> [Rect; 2] {
|
|||||||
|
|
||||||
pub fn skirmish_view(app: &App, area: Rect, buf: &mut Buffer) {
|
pub fn skirmish_view(app: &App, area: Rect, buf: &mut Buffer) {
|
||||||
let Some(states) = app.states() else { return };
|
let Some(states) = app.states() else { return };
|
||||||
|
let board: &BoardState = &states.skirmish.board;
|
||||||
|
|
||||||
let [title_area, main_area, keybindings_area] = skirmish_layout(area);
|
let [title_area, main_area, keybindings_area] = skirmish_layout(area);
|
||||||
|
|
||||||
@@ -115,33 +116,25 @@ pub fn skirmish_view(app: &App, area: Rect, buf: &mut Buffer) {
|
|||||||
});
|
});
|
||||||
// .centered(
|
// .centered(
|
||||||
// Constraint::Length(
|
// Constraint::Length(
|
||||||
// (states.skirmish.board.cell_width * states.skirmish.board.cols) as u16,
|
// (board.cell_width * board.cols) as u16,
|
||||||
// ),
|
// ),
|
||||||
// Constraint::Length(
|
// Constraint::Length(
|
||||||
// (states.skirmish.board.cell_height * states.skirmish.board.rows) as u16,
|
// (board.cell_height * board.rows) as u16,
|
||||||
// ),
|
// ),
|
||||||
// );
|
// );
|
||||||
|
|
||||||
BoardWidget::new(&states.skirmish.board).render(cells_area, buf);
|
BoardWidget::new(&board).render(cells_area, buf);
|
||||||
|
|
||||||
if states.skirmish.side_panel {
|
if states.skirmish.side_panel {
|
||||||
let row: usize = states.skirmish.board.get_focused_cell().get_row();
|
let row: usize = board.get_focused_cell().get_row();
|
||||||
let col: usize = states.skirmish.board.get_focused_cell().get_col();
|
let col: usize = board.get_focused_cell().get_col();
|
||||||
let coords: (usize, usize) = (row, col);
|
|
||||||
let structure_name: &str = states
|
|
||||||
.skirmish
|
|
||||||
.board
|
|
||||||
.get_ref_cell(row, col)
|
|
||||||
.get_structure()
|
|
||||||
.get_name();
|
|
||||||
let unit_name: &str = states
|
|
||||||
.skirmish
|
|
||||||
.board
|
|
||||||
.get_ref_cell(row, col)
|
|
||||||
.get_unit_option()
|
|
||||||
.get_name();
|
|
||||||
|
|
||||||
SidePanelWidget::new(coords, structure_name, unit_name).render(side_panel_area, buf);
|
SidePanelWidget::new(
|
||||||
|
(row, col),
|
||||||
|
&board.get_ref_cell(row, col).get_structure(),
|
||||||
|
&board.get_ref_cell(row, col).get_option_unit(),
|
||||||
|
)
|
||||||
|
.render(side_panel_area, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,12 +56,12 @@ impl CellWidget {
|
|||||||
self.structure
|
self.structure
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_unit_option(&self) -> Option<Units> {
|
pub fn get_option_unit(&self) -> Option<Units> {
|
||||||
self.unit
|
self.unit
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_structure(&mut self, sctructure: Structures) -> &mut Self {
|
pub fn set_structure(&mut self, structure: Structures) -> &mut Self {
|
||||||
self.structure = sctructure;
|
self.structure = structure;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,31 @@
|
|||||||
|
use crate::app::{
|
||||||
|
helpers::block_single_title_helper,
|
||||||
|
states::skirmish_states::{
|
||||||
|
structures::{Structure, Structures},
|
||||||
|
units::{Unit, Units},
|
||||||
|
},
|
||||||
|
};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::{Alignment, Rect},
|
layout::{Alignment, Constraint, Layout, Margin, Rect},
|
||||||
style::Color,
|
style::{Color, Stylize},
|
||||||
text::Line,
|
text::{Line, Span},
|
||||||
widgets::{Block, BorderType, Borders, Paragraph, Widget},
|
widgets::{Block, BorderType, Borders, Padding, Paragraph, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::app::helpers::block_title_helper;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct SidePanelWidget {
|
pub struct SidePanelWidget<'a> {
|
||||||
coords: (usize, usize),
|
coords: (usize, usize),
|
||||||
structure_name: &'static str,
|
structure: &'a Structures,
|
||||||
unit_name: &'static str,
|
unit: &'a Option<Units>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SidePanelWidget {
|
impl<'a> SidePanelWidget<'a> {
|
||||||
pub fn new(
|
pub fn new(coords: (usize, usize), structure: &'a Structures, unit: &'a Option<Units>) -> Self {
|
||||||
coords: (usize, usize),
|
|
||||||
structure_name: &'static str,
|
|
||||||
unit_name: &'static str,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
coords,
|
coords,
|
||||||
structure_name,
|
structure,
|
||||||
unit_name,
|
unit,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,35 +41,94 @@ impl SidePanelWidget {
|
|||||||
letters.iter().rev().collect()
|
letters.iter().rev().collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_title(&self) -> Line<'_> {
|
fn get_outer_block(&self) -> Block<'_> {
|
||||||
let cell_coords: String = format!("{}{}", self.col_to_letters(), self.coords.0);
|
|
||||||
|
|
||||||
let mut texts: Vec<(String, Color)> = Vec::with_capacity(3);
|
|
||||||
|
|
||||||
texts.push((cell_coords, Color::Yellow));
|
|
||||||
texts.push((self.structure_name.to_string(), Color::Cyan));
|
|
||||||
|
|
||||||
if !self.unit_name.is_empty() {
|
|
||||||
texts.push((self.unit_name.to_string(), Color::Green));
|
|
||||||
}
|
|
||||||
|
|
||||||
block_title_helper(&texts, Some(" - "))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_block(&self) -> Block<'_> {
|
|
||||||
Block::default()
|
Block::default()
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_type(BorderType::Double)
|
.border_type(BorderType::Double)
|
||||||
.title(self.get_title())
|
.title(block_single_title_helper(
|
||||||
|
format!("{}{}", self.col_to_letters(), self.coords.0),
|
||||||
|
Color::Yellow,
|
||||||
|
))
|
||||||
.title_alignment(Alignment::Center)
|
.title_alignment(Alignment::Center)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_inner_block<'b>(&self, title: String, color: Color) -> Block<'b> {
|
||||||
|
Block::default()
|
||||||
|
.borders(Borders::LEFT | Borders::TOP)
|
||||||
|
.title(block_single_title_helper(title, color))
|
||||||
|
.title_alignment(Alignment::Center)
|
||||||
|
.padding(Padding::symmetric(1, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_area_constraints(&self) -> [Constraint; 2] {
|
||||||
|
if !self.unit.get_name().is_empty() {
|
||||||
|
[Constraint::Percentage(50), Constraint::Percentage(50)]
|
||||||
|
} else {
|
||||||
|
[Constraint::Percentage(100), Constraint::Percentage(0)]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Widget for SidePanelWidget {
|
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: u16 = durability * 100 / max_durability;
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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<'_> {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
Paragraph::default()
|
let block: Block<'_> = self.get_outer_block();
|
||||||
.alignment(Alignment::Center)
|
let inner_area: Rect = block.inner(area).inner(Margin {
|
||||||
.block(self.get_block())
|
horizontal: 2,
|
||||||
.render(area, buf);
|
vertical: 1,
|
||||||
|
});
|
||||||
|
block.render(area, buf);
|
||||||
|
|
||||||
|
let [structure_area, unit_area] =
|
||||||
|
Layout::vertical(self.get_area_constraints()).areas(inner_area);
|
||||||
|
|
||||||
|
Paragraph::new(self.structure_text())
|
||||||
|
.alignment(Alignment::Left)
|
||||||
|
.block(self.get_inner_block(self.structure.get_name().to_string(), Color::Cyan))
|
||||||
|
.render(structure_area, buf);
|
||||||
|
|
||||||
|
if !self.unit.get_name().is_empty() {
|
||||||
|
Paragraph::new(self.unit_text())
|
||||||
|
.alignment(Alignment::Left)
|
||||||
|
.block(self.get_inner_block(self.unit.get_name().to_string(), Color::Green))
|
||||||
|
.render(unit_area, buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user