generated from GarandPLG/rust-flake-template
Add GameStates, typed enums, and App::new constructor
This commit is contained in:
+46
-8
@@ -1,4 +1,10 @@
|
|||||||
use crate::app::keybindings::{Action, event_to_action, main_menu_keybindings};
|
use crate::{
|
||||||
|
app::{
|
||||||
|
GameStates,
|
||||||
|
keybindings::{Action, event_to_action, main_menu_keybindings},
|
||||||
|
},
|
||||||
|
cli::Cli,
|
||||||
|
};
|
||||||
use clap::ValueEnum;
|
use clap::ValueEnum;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
DefaultTerminal, Frame,
|
DefaultTerminal, Frame,
|
||||||
@@ -9,14 +15,19 @@ use std::{
|
|||||||
sync::mpsc::{self, Receiver},
|
sync::mpsc::{self, Receiver},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub enum Event {
|
||||||
|
Input(KeyEvent),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
pub exit: bool,
|
pub exit: bool,
|
||||||
pub window: View,
|
pub view: View,
|
||||||
|
pub game_states: GameStates,
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub game_mode: String,
|
pub game_mode: GameMode,
|
||||||
pub map_width: u8,
|
pub map_width: u8,
|
||||||
pub map_height: u8,
|
pub map_height: u8,
|
||||||
pub perk_deck: String,
|
pub perk_deck: PerkDecks,
|
||||||
pub starting_wood: u8,
|
pub starting_wood: u8,
|
||||||
pub starting_iron: u8,
|
pub starting_iron: u8,
|
||||||
pub supply_limit: u8,
|
pub supply_limit: u8,
|
||||||
@@ -24,7 +35,7 @@ pub struct App {
|
|||||||
pub skill_points_limit: u16,
|
pub skill_points_limit: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, ValueEnum)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
|
||||||
pub enum View {
|
pub enum View {
|
||||||
MainMenu,
|
MainMenu,
|
||||||
Skirmish,
|
Skirmish,
|
||||||
@@ -33,11 +44,38 @@ pub enum View {
|
|||||||
Settings,
|
Settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Event {
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
|
||||||
Input(KeyEvent),
|
pub enum GameMode {
|
||||||
|
LastManStanding,
|
||||||
|
Frontlines,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
|
||||||
|
pub enum PerkDecks {
|
||||||
|
Silesian,
|
||||||
|
BogeyMan,
|
||||||
|
Anteater,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
|
pub fn new(args: Cli) -> Self {
|
||||||
|
Self {
|
||||||
|
exit: false,
|
||||||
|
game_states: GameStates::new(),
|
||||||
|
view: args.view,
|
||||||
|
username: args.username,
|
||||||
|
game_mode: args.game_mode,
|
||||||
|
map_width: args.map_width,
|
||||||
|
map_height: args.map_height,
|
||||||
|
perk_deck: args.perk_deck,
|
||||||
|
starting_wood: args.starting_wood,
|
||||||
|
starting_iron: args.starting_iron,
|
||||||
|
supply_limit: args.supply_limit,
|
||||||
|
xp_modifier: args.xp_modifier,
|
||||||
|
skill_points_limit: args.skill_points_limit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, terminal: &mut DefaultTerminal, rx: Receiver<Event>) -> Result<()> {
|
pub fn run(&mut self, terminal: &mut DefaultTerminal, rx: Receiver<Event>) -> Result<()> {
|
||||||
while !self.exit {
|
while !self.exit {
|
||||||
terminal.draw(|frame: &mut Frame<'_>| self.draw(frame))?;
|
terminal.draw(|frame: &mut Frame<'_>| self.draw(frame))?;
|
||||||
@@ -59,7 +97,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_key_event(&mut self, key_event: KeyEvent) -> Result<()> {
|
fn handle_key_event(&mut self, key_event: KeyEvent) -> Result<()> {
|
||||||
match self.window {
|
match self.view {
|
||||||
View::MainMenu => main_menu_keybindings(self, &key_event),
|
View::MainMenu => main_menu_keybindings(self, &key_event),
|
||||||
_ => {
|
_ => {
|
||||||
if let Some(action) = event_to_action(&key_event) {
|
if let Some(action) = event_to_action(&key_event) {
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct GameStates {
|
||||||
|
pub main_menu_state: MainMenuState,
|
||||||
|
pub skirmish_state: SkirmishState,
|
||||||
|
pub perk_decks_state: PerkDecksState,
|
||||||
|
pub skills_config_state: SkillsConfigState,
|
||||||
|
pub settings_state: SettingsState,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct MainMenuState {
|
||||||
|
pub id: usize,
|
||||||
|
pub name: &'static str,
|
||||||
|
pub selected_view: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct SkirmishState {
|
||||||
|
pub id: usize,
|
||||||
|
pub name: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct PerkDecksState {
|
||||||
|
pub id: usize,
|
||||||
|
pub name: &'static str,
|
||||||
|
pub selected_perk_deck: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct SkillsConfigState {
|
||||||
|
pub id: usize,
|
||||||
|
pub name: &'static str,
|
||||||
|
pub selected_skill: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct SettingsState {
|
||||||
|
pub id: usize,
|
||||||
|
pub name: &'static str,
|
||||||
|
pub selected_setting: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GameStates {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
main_menu_state: MainMenuState {
|
||||||
|
id: 0,
|
||||||
|
name: "Main Menu",
|
||||||
|
selected_view: 1,
|
||||||
|
},
|
||||||
|
skirmish_state: SkirmishState {
|
||||||
|
id: 1,
|
||||||
|
name: "Skirmish",
|
||||||
|
},
|
||||||
|
perk_decks_state: PerkDecksState {
|
||||||
|
id: 2,
|
||||||
|
name: "Perk Decks",
|
||||||
|
selected_perk_deck: 0,
|
||||||
|
},
|
||||||
|
skills_config_state: SkillsConfigState {
|
||||||
|
id: 3,
|
||||||
|
name: "Skills Config",
|
||||||
|
selected_skill: 0,
|
||||||
|
},
|
||||||
|
settings_state: SettingsState {
|
||||||
|
id: 4,
|
||||||
|
name: "Settings",
|
||||||
|
selected_setting: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::app::{
|
use crate::app::{
|
||||||
App,
|
App, View,
|
||||||
keybindings::{Action, event_to_action},
|
keybindings::{Action, event_to_action},
|
||||||
};
|
};
|
||||||
use ratatui::crossterm::event::KeyEvent;
|
use ratatui::crossterm::event::KeyEvent;
|
||||||
@@ -8,7 +8,35 @@ pub fn main_menu_keybindings(app: &mut App, event: &KeyEvent) {
|
|||||||
if let Some(action) = event_to_action(&event) {
|
if let Some(action) = event_to_action(&event) {
|
||||||
match action {
|
match action {
|
||||||
Action::Quit => app.exit = true,
|
Action::Quit => app.exit = true,
|
||||||
_ => (),
|
Action::ScrollUp => {
|
||||||
|
app.game_states.main_menu_state.selected_view = app
|
||||||
|
.game_states
|
||||||
|
.main_menu_state
|
||||||
|
.selected_view
|
||||||
|
.saturating_sub(1)
|
||||||
|
.max(1);
|
||||||
|
}
|
||||||
|
Action::ScrollDown => {
|
||||||
|
app.game_states.main_menu_state.selected_view = app
|
||||||
|
.game_states
|
||||||
|
.main_menu_state
|
||||||
|
.selected_view
|
||||||
|
.saturating_add(1)
|
||||||
|
.min(4);
|
||||||
|
}
|
||||||
|
Action::Select => {
|
||||||
|
let selected_view: usize = app.game_states.main_menu_state.selected_view;
|
||||||
|
|
||||||
|
if selected_view == 1 {
|
||||||
|
app.view = View::Skirmish;
|
||||||
|
} else if selected_view == 2 {
|
||||||
|
app.view = View::PerkDecks;
|
||||||
|
} else if selected_view == 3 {
|
||||||
|
app.view = View::SkillsConfig;
|
||||||
|
} else if selected_view == 4 {
|
||||||
|
app.view = View::Settings;
|
||||||
|
}
|
||||||
|
} // _ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -1,6 +1,10 @@
|
|||||||
pub mod app;
|
pub mod app;
|
||||||
|
pub mod game_states;
|
||||||
pub mod keybindings;
|
pub mod keybindings;
|
||||||
pub mod widget;
|
pub mod widget;
|
||||||
pub mod widgets;
|
pub mod widgets;
|
||||||
|
|
||||||
pub use app::{App, Event, View, handle_input_events};
|
pub use app::{App, Event, GameMode, PerkDecks, View, handle_input_events};
|
||||||
|
pub use game_states::{
|
||||||
|
GameStates, MainMenuState, PerkDecksState, SettingsState, SkillsConfigState, SkirmishState,
|
||||||
|
};
|
||||||
|
|||||||
+3
-3
@@ -6,9 +6,9 @@ impl Widget for &App {
|
|||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
match self.window {
|
match self.view {
|
||||||
View::MainMenu => main_menu_widget(area, buf),
|
View::MainMenu => main_menu_widget(self, area, buf),
|
||||||
_ => panic!("This window doesn't have widget."),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,86 @@
|
|||||||
use crate::app::{View, widgets::keybindings_widget};
|
use crate::app::{App, View, widgets::keybindings_widget};
|
||||||
|
use clap::ValueEnum;
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::{Alignment, Constraint, Layout, Rect},
|
layout::{Alignment, Constraint, Layout, Rect},
|
||||||
style::Color,
|
style::Stylize,
|
||||||
|
text::Line,
|
||||||
widgets::{Block, Borders, Paragraph, Widget},
|
widgets::{Block, Borders, Paragraph, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn main_menu_widget(area: Rect, buf: &mut Buffer) {
|
fn view_options() -> Vec<(usize, String)> {
|
||||||
|
View::value_variants()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, v)| {
|
||||||
|
let name = v
|
||||||
|
.to_possible_value()
|
||||||
|
.unwrap()
|
||||||
|
.get_name()
|
||||||
|
.replace('-', " ")
|
||||||
|
.to_uppercase()
|
||||||
|
.to_string();
|
||||||
|
(i, name)
|
||||||
|
})
|
||||||
|
.filter(|(_, v)| v != "MAIN MENU")
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_menu_widget(app: &App, area: Rect, buf: &mut Buffer) {
|
||||||
let vertical_layout: Layout =
|
let vertical_layout: Layout =
|
||||||
Layout::vertical([Constraint::Percentage(87), Constraint::Percentage(13)]);
|
Layout::vertical([Constraint::Percentage(88), Constraint::Percentage(12)]);
|
||||||
|
|
||||||
let [main_menu_area, keybindings_area] = vertical_layout.areas(area);
|
let [main_menu_area, keybindings_area] = vertical_layout.areas(area);
|
||||||
|
|
||||||
|
Block::new()
|
||||||
|
.borders(Borders::LEFT | Borders::TOP | Borders::RIGHT)
|
||||||
|
.render(main_menu_area, buf);
|
||||||
|
|
||||||
|
let main_menu_areas: Vec<Rect> = main_menu_area.layout_vec(&Layout::vertical([
|
||||||
|
Constraint::Percentage(50),
|
||||||
|
Constraint::Percentage(50),
|
||||||
|
]));
|
||||||
|
|
||||||
{
|
{
|
||||||
|
let title_area: Rect = main_menu_areas[0].centered_vertically(Constraint::Percentage(50));
|
||||||
|
|
||||||
let title_text: String = vec![
|
let title_text: String = vec![
|
||||||
r" __ __ _ _____ _ ",
|
r" __ __ _ _____ _ ",
|
||||||
r"/ / /\ \ \__ _ _ __ (_)_ __ /__ \_ _ _ __ _ __ ___| |___",
|
r"/ / /\ \ \__ _ _ __ (_)_ __ /__ \_ _ _ __ _ __ ___| |___ ",
|
||||||
r"\ \/ \/ / _` | '__| | | '_ \ / /\/ | | | '_ \| '_ \ / _ \ / __|",
|
r"\ \/ \/ / _` | '__| | | '_ \ / /\/ | | | '_ \| '_ \ / _ \ / __|",
|
||||||
r" \ /\ / (_| | | | | | | | / / | |_| | | | | | | | __/ \__ \",
|
r" \ /\ / (_| | | | | | | | / / | |_| | | | | | | | __/ \__ \",
|
||||||
r" \/ \/ \__,_|_| |_|_| |_| \/ \__,_|_| |_|_| |_|\___|_|___/",
|
r" \/ \/ \__,_|_| |_|_| |_| \/ \__,_|_| |_|_| |_|\___|_|___/",
|
||||||
]
|
]
|
||||||
.join("\n");
|
.join("\n");
|
||||||
|
|
||||||
let title: Paragraph<'_> = Paragraph::new(title_text)
|
Paragraph::new(title_text)
|
||||||
.alignment(Alignment::Center)
|
.alignment(Alignment::Center)
|
||||||
.style(Color::Yellow)
|
.yellow()
|
||||||
.block(
|
.block(Block::new().gray().borders(Borders::LEFT | Borders::RIGHT))
|
||||||
Block::default()
|
.render(title_area, buf);
|
||||||
.borders(Borders::LEFT | Borders::RIGHT | Borders::TOP)
|
|
||||||
.style(Color::Gray),
|
|
||||||
);
|
|
||||||
|
|
||||||
title.render(main_menu_area, buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
let keybindings: Paragraph<'_> = keybindings_widget(View::MainMenu);
|
let options_area: Rect = main_menu_areas[1];
|
||||||
keybindings.render(keybindings_area, buf);
|
|
||||||
|
let lines: Vec<Line<'_>> = view_options()
|
||||||
|
.into_iter()
|
||||||
|
.map(|(i, view)| {
|
||||||
|
let styled = if app.game_states.main_menu_state.selected_view == i {
|
||||||
|
Line::from(format!("> {}", view)).yellow()
|
||||||
|
} else {
|
||||||
|
Line::from(view).white()
|
||||||
|
};
|
||||||
|
styled
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Paragraph::new(lines)
|
||||||
|
.alignment(Alignment::Center)
|
||||||
|
.green()
|
||||||
|
.block(Block::new().gray().borders(Borders::LEFT | Borders::RIGHT))
|
||||||
|
.render(options_area, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keybindings_widget(View::MainMenu).render(keybindings_area, buf);
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -1,4 +1,4 @@
|
|||||||
use crate::app::View;
|
use crate::app::{GameMode, PerkDecks, View};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
@@ -11,7 +11,7 @@ pub struct Cli {
|
|||||||
default_value_t = View::MainMenu,
|
default_value_t = View::MainMenu,
|
||||||
value_enum
|
value_enum
|
||||||
)]
|
)]
|
||||||
pub window: View,
|
pub view: View,
|
||||||
|
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
@@ -24,10 +24,11 @@ pub struct Cli {
|
|||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
help = "Game mode",
|
help = "Game mode",
|
||||||
value_name = "LastManStanding or FrontLines",
|
value_name = "...",
|
||||||
default_value = "LastManStanding"
|
default_value_t = GameMode::LastManStanding,
|
||||||
|
value_enum
|
||||||
)]
|
)]
|
||||||
pub game_mode: String,
|
pub game_mode: GameMode,
|
||||||
|
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
@@ -48,10 +49,11 @@ pub struct Cli {
|
|||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
help = "Perk Deck",
|
help = "Perk Deck",
|
||||||
value_name = "String (check Perk Deck tab)",
|
value_name = "...",
|
||||||
default_value = "Silesian"
|
default_value_t = PerkDecks::Silesian,
|
||||||
|
value_enum
|
||||||
)]
|
)]
|
||||||
pub perk_deck: String,
|
pub perk_deck: PerkDecks,
|
||||||
|
|
||||||
#[arg(
|
#[arg(
|
||||||
long,
|
long,
|
||||||
|
|||||||
+1
-14
@@ -12,20 +12,7 @@ use war_in_tunnels::{
|
|||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let args: Cli = get_args();
|
let args: Cli = get_args();
|
||||||
let mut terminal: Terminal<CrosstermBackend<Stdout>> = ratatui::init();
|
let mut terminal: Terminal<CrosstermBackend<Stdout>> = ratatui::init();
|
||||||
let mut app: App = App {
|
let mut app: App = App::new(args);
|
||||||
exit: false,
|
|
||||||
window: args.window,
|
|
||||||
username: args.username,
|
|
||||||
game_mode: args.game_mode,
|
|
||||||
map_width: args.map_width,
|
|
||||||
map_height: args.map_height,
|
|
||||||
perk_deck: args.perk_deck,
|
|
||||||
starting_wood: args.starting_wood,
|
|
||||||
starting_iron: args.starting_iron,
|
|
||||||
supply_limit: args.supply_limit,
|
|
||||||
xp_modifier: args.xp_modifier,
|
|
||||||
skill_points_limit: args.skill_points_limit,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (event_tx, event_rx) = mpsc::channel::<Event>();
|
let (event_tx, event_rx) = mpsc::channel::<Event>();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user