generated from GarandPLG/rust-flake-template
7541db79d8
App now receives an AppChannels struct; AppEvent enum removed. Event handling, tick generation, and audio communication are now performed through dedicated channels.
208 lines
5.6 KiB
Rust
208 lines
5.6 KiB
Rust
use crate::{
|
||
app::{
|
||
states::{
|
||
PerkDecks,
|
||
skirmish_states::{GameMode, ZoomLevel},
|
||
},
|
||
threads::Soundtrack,
|
||
view::View,
|
||
},
|
||
logs::init_logger,
|
||
};
|
||
use clap::{Error, Parser, error::ErrorKind, value_parser};
|
||
use std::num::ParseFloatError;
|
||
|
||
/// Holds all configurable options that can be passed via the command line.
|
||
///
|
||
/// Each field corresponds to a CLI flag (e.g. `--view`, `--username`, …).
|
||
/// The `clap` attributes describe the flag name, help text, default value,
|
||
/// and any validation constraints. The struct derives `Parser` so that
|
||
/// `Cli::parse()` can be called directly to obtain a populated instance.
|
||
#[derive(Parser, Debug, Clone)]
|
||
#[command(version, about = "War in Tunnels", long_about = "War in Tunnels")]
|
||
pub struct Cli {
|
||
/// The initial view/window to display.
|
||
#[arg(
|
||
long,
|
||
help = "Default window",
|
||
value_name = "...",
|
||
default_value_t = View::MainMenu,
|
||
value_enum
|
||
)]
|
||
pub view: View,
|
||
|
||
/// Player name shown in the UI.
|
||
#[arg(
|
||
long,
|
||
help = "Username",
|
||
value_name = "String",
|
||
default_value = "Player"
|
||
)]
|
||
pub username: String,
|
||
|
||
/// Game mode selection.
|
||
#[arg(
|
||
long,
|
||
help = "Game mode",
|
||
value_name = "...",
|
||
default_value_t = GameMode::LastManStanding,
|
||
value_enum
|
||
)]
|
||
pub game_mode: GameMode,
|
||
|
||
/// Width of the generated map (36–100 tiles).
|
||
#[arg(
|
||
long,
|
||
help = "Map width",
|
||
value_name = "Positive integer [36; 108]",
|
||
default_value = "42",
|
||
value_parser = value_parser!(u8).range(36..=108)
|
||
)]
|
||
pub map_width: u8,
|
||
|
||
/// Height of the generated map (11–50 tiles).
|
||
#[arg(
|
||
long,
|
||
help = "Map height",
|
||
value_name = "Positive integer [11; 39]",
|
||
default_value = "11",
|
||
value_parser = value_parser!(u8).range(11..=39)
|
||
)]
|
||
pub map_height: u8,
|
||
|
||
/// Zoom level used for the UI.
|
||
#[arg(
|
||
long,
|
||
help = "Zoom level",
|
||
value_name = "...",
|
||
default_value_t = ZoomLevel::Default,
|
||
value_enum
|
||
)]
|
||
pub zoom_level: ZoomLevel,
|
||
|
||
/// Which perk deck to use.
|
||
#[arg(
|
||
long,
|
||
help = "Perk Deck",
|
||
value_name = "...",
|
||
default_value_t = PerkDecks::Silesian,
|
||
value_enum
|
||
)]
|
||
pub perk_deck: PerkDecks,
|
||
|
||
/// Starting amount of wood (1–150).
|
||
#[arg(
|
||
long,
|
||
help = "Starting wood",
|
||
value_name = "Positive integer [1; 150]",
|
||
default_value = "50",
|
||
value_parser = value_parser!(u16).range(1..=150)
|
||
)]
|
||
pub starting_wood: u16,
|
||
|
||
/// Starting amount of iron (1–150).
|
||
#[arg(
|
||
long,
|
||
help = "Starting iron",
|
||
value_name = "Positive integer [1; 150]",
|
||
default_value = "25",
|
||
value_parser = value_parser!(u16).range(1..=150)
|
||
)]
|
||
pub starting_iron: u16,
|
||
|
||
/// Maximum amount of units (49–149).
|
||
#[arg(
|
||
long,
|
||
help = "Supply limit",
|
||
value_name = "Positive integer [49; 149]",
|
||
default_value = "99",
|
||
value_parser = value_parser!(u8).range(49..=149)
|
||
)]
|
||
pub supply_limit: u8,
|
||
|
||
/// Modifier applied to experience points (0.5–2.0).
|
||
#[arg(
|
||
long,
|
||
help = "XP modifier",
|
||
value_name = "Float [0.5; 2.0]",
|
||
default_value = "1.0",
|
||
value_parser = parse_xp
|
||
)]
|
||
pub xp_modifier: f32,
|
||
|
||
/// Upper bound for skill points that can be earned (90–690).
|
||
#[arg(
|
||
long,
|
||
help = "Skill points limit",
|
||
value_name = "Positive integer [90; 690]",
|
||
default_value = "120",
|
||
value_parser = value_parser!(u16).range(90..=690)
|
||
)]
|
||
pub skill_points_limit: u16,
|
||
|
||
/// Which soundtrack to play (default: default)
|
||
#[arg(
|
||
long,
|
||
help = "Soundtrack",
|
||
value_name = "...",
|
||
default_value_t = Soundtrack::Default,
|
||
value_enum
|
||
)]
|
||
pub sound_track: Soundtrack,
|
||
|
||
/// Mute soundtrack (default: disabled).
|
||
#[arg(
|
||
long,
|
||
help = "Mute soundtrack (default: disabled)",
|
||
default_value_t = false
|
||
)]
|
||
pub mute: bool,
|
||
|
||
/// Enable logging to a file (default: disabled).
|
||
#[arg(
|
||
long,
|
||
help = "Enable logging to file (default: disabled)",
|
||
default_value_t = false
|
||
)]
|
||
pub log: bool,
|
||
}
|
||
|
||
/// Parses the command line arguments and returns a fully populated `Cli`
|
||
/// instance. This function simply forwards to `Cli::parse()`, which
|
||
/// handles argument validation and displays helpful error messages if
|
||
/// the user supplies invalid input.
|
||
pub fn get_args() -> Cli {
|
||
let args: Cli = Cli::parse();
|
||
|
||
if args.log {
|
||
init_logger();
|
||
}
|
||
|
||
args
|
||
}
|
||
|
||
/// Parses a string into a floating‑point XP modifier and validates that it
|
||
/// lies within the inclusive range `0.5..=2.0`.
|
||
///
|
||
/// The function is used as a custom `value_parser` for the `--xp-modifier`
|
||
/// flag. On failure it returns a `clap::Error` with `ErrorKind::InvalidValue`,
|
||
/// allowing `clap` to present a clear error message to the user.
|
||
///
|
||
/// # Errors
|
||
///
|
||
/// * Returns an error if the string cannot be parsed as `f32`.
|
||
/// * Returns an error if the parsed value is outside the allowed range.
|
||
fn parse_xp(s: &str) -> Result<f32, Error> {
|
||
let v: f32 = s
|
||
.parse()
|
||
.map_err(|e: ParseFloatError| Error::raw(ErrorKind::InvalidValue, e.to_string()))?;
|
||
if (0.5..=2.0).contains(&v) {
|
||
Ok(v)
|
||
} else {
|
||
Err(Error::raw(
|
||
ErrorKind::InvalidValue,
|
||
format!("Value {} is out of range 0.5..=2.0", v),
|
||
))
|
||
}
|
||
}
|