strategem-hero 0.7.2

Simple CLI Game following Strategem Hero mini-game from Helldivers 2
use std::{io::Write, path::PathBuf, time::Duration};

use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind};
use event::Controls;
use utility::ScreenWriter;

use crate::{
    error::Result,
    game::Game,
    storage::LeaderboardStorage,
    utility::{GameTimer, Penalty},
};

mod error;
mod event;
mod game;
mod storage;
mod strategem;
mod utility;

const VERSION: &str = "0.7";

const LOGO: &str = r#"     _             _                                  _                    
    | |           | |                                | |                   
 ___| |_ _ __ __ _| |_ ___  __ _  ___ _ __ ___ ______| |__   ___ _ __ ___  
/ __| __| '__/ _` | __/ _ \/ _` |/ _ \ '_ ` _ \______| '_ \ / _ \ '__/ _ \ 
\__ \ |_| | | (_| | ||  __/ (_| |  __/ | | | | |     | | | |  __/ | | (_) |
|___/\__|_|  \__,_|\__\___|\__, |\___|_| |_| |_|     |_| |_|\___|_|  \___/ 
                            __/ |                                          
                           |___/ "#;

fn setup_data_dir() -> Result<PathBuf> {
    let datadir = utility::get_app_data_dir()?;
    if !datadir.exists() {
        std::fs::create_dir(&datadir)?;
    }

    let datadir = datadir.join(VERSION);
    if !datadir.exists() {
        std::fs::create_dir(&datadir)?;
    }

    Ok(datadir)
}

fn main() -> Result<()> {
    let mut store = match setup_data_dir() {
        Ok(dir) => LeaderboardStorage::open(dir.join("db.sqlite3")),
        Err(_e) => {
            println!("warning: unable to locate leaderboard data.");
            println!("warning: all progress will be lost after this session.");

            #[cfg(debug_assertions)]
            {
                eprintln!("{_e}");
            }

            LeaderboardStorage::open_in_memory()
        }
    };

    #[cfg(debug_assertions)]
    {
        store.drop_schema()?;
    }
    store.init_schema()?;
    store.seed_schema()?;

    if let Some(arg) = std::env::args().nth(1) {
        if arg.eq("leaderboard") {
            store
                .select_all()?
                .iter()
                .enumerate()
                .for_each(|(i, rec)| println!("  {}. {:<18} {}", i + 1, rec.nickname, rec.score));
            return Ok(());
        } else if arg.eq("delete-data") {
            store.close()?;
            let datadir = utility::get_app_data_dir()?;
            std::fs::remove_dir_all(datadir)?;
            println!("Deleted all game-related data successfully");
            return Ok(());
        }
    }

    let mut screen = ScreenWriter::new();
    writeln!(screen, "{LOGO}")?;
    writeln!(screen, "Press key in [] brackets to choose an action.")?;
    writeln!(screen, "1. [S]tart Game")?;
    writeln!(screen, "2. [L]eaderboard")?;
    writeln!(screen, "3. [D]elete Data")?;
    writeln!(screen, "4. [Q]uit")?;

    while let Event::Key(ev) = crossterm::event::read()? {
        match ev {
            KeyEvent {
                code: KeyCode::Char('s'),
                kind: KeyEventKind::Press,
                ..
            } => {
                let game_timer = GameTimer::start_from(Duration::from_secs(30));
                let penalty = Penalty::new(250, 10);
                let controls = if std::env::args().any(|arg| arg.eq("--wasd")) {
                    Controls::wasd()
                } else {
                    Controls::arrows()
                };
                let mut game = Game::new(store, game_timer, controls, penalty);
                drop(screen);
                ScreenWriter::clear()?;
                game.run()?;
                break;
            }

            KeyEvent {
                code: KeyCode::Char('l'),
                kind: KeyEventKind::Press,
                ..
            } => {
                drop(screen);
                ScreenWriter::clear()?;
                store.select_all()?.iter().enumerate().for_each(|(i, rec)| {
                    println!("  {}. {:<18} {}", i + 1, rec.nickname, rec.score)
                });
                break;
            }

            KeyEvent {
                code: KeyCode::Char('d'),
                kind: KeyEventKind::Press,
                ..
            } => {
                drop(screen);
                ScreenWriter::clear()?;
                store.close()?;
                let datadir = utility::get_app_data_dir()?;
                std::fs::remove_dir_all(datadir)?;
                println!("Deleted all game-related data successfully");
                break;
            }

            KeyEvent {
                code: KeyCode::Char('q'),
                kind: KeyEventKind::Press,
                ..
            } => break,

            _ => (),
        }
    }

    Ok(())
}