Skip to main content

thedes_app/
root.rs

1use std::{fmt, path::PathBuf};
2
3use thedes_asset::Assets;
4use thedes_settings::AudioSinkType;
5use thedes_tui::{
6    core::{audio, event::Key},
7    menu::{self, Menu},
8};
9use thiserror::Error;
10
11use crate::{SAVE_EXTENSION, session, settings};
12
13pub mod new_game;
14pub mod game_creation;
15pub mod load_game;
16
17#[derive(Debug, Error)]
18pub enum InitError {
19    #[error("Failed to initialize main menu")]
20    MainMenu(
21        #[from]
22        #[source]
23        menu::Error,
24    ),
25    #[error("Failed to initialize new game component")]
26    NewGame(
27        #[from]
28        #[source]
29        new_game::InitError,
30    ),
31    #[error("Inconsistent main menu, missing quit")]
32    MissingQuit,
33    #[error("Failed to create settings component")]
34    LoadSettings(#[from] settings::LoadError),
35}
36
37#[derive(Debug, Error)]
38pub enum Error {
39    #[error("Failed to run main menu")]
40    MainMenu(
41        #[from]
42        #[source]
43        menu::Error,
44    ),
45    #[error("Failed to run new game component")]
46    NewGame(
47        #[from]
48        #[source]
49        new_game::Error,
50    ),
51    #[error("Failed to run game creation")]
52    GameCreation(
53        #[from]
54        #[source]
55        game_creation::Error,
56    ),
57    #[error("Failed to run game session component")]
58    Session(
59        #[from]
60        #[source]
61        session::Error,
62    ),
63    #[error("Failed to create a game session")]
64    SessionInit(
65        #[from]
66        #[source]
67        session::InitError,
68    ),
69    #[error("Failed to load game")]
70    LoadGame(#[from] load_game::Error),
71    #[error("Failed to load asset")]
72    LoadAsset(#[from] thedes_asset::LoadError),
73    #[error("Failed to run settings component")]
74    Settings(#[from] settings::Error),
75    #[error("Failed to flush commands to audio controller")]
76    FlushAudio(#[from] audio::FlushError),
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80enum MainMenuItem {
81    NewGame,
82    LoadGame,
83    Settings,
84    Quit,
85}
86
87impl fmt::Display for MainMenuItem {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        f.write_str(match self {
90            Self::NewGame => "New Game",
91            Self::LoadGame => "Load Game",
92            Self::Settings => "Settings",
93            Self::Quit => "Quit",
94        })
95    }
96}
97
98#[derive(Debug, Clone)]
99pub struct Config {
100    pub saves_dir: PathBuf,
101    pub settings_path: PathBuf,
102}
103
104// #[derive(Debug, Clone)]
105pub struct Component {
106    main_menu: Menu<MainMenuItem>,
107    new_game: new_game::Component,
108    game_creation: game_creation::Component,
109    load_game: load_game::Component,
110    session_config: session::Config,
111    saves_dir: PathBuf,
112    settings: settings::Component,
113}
114
115impl Component {
116    pub async fn new(config: Config) -> Result<Self, InitError> {
117        let main_menu_items = [
118            MainMenuItem::NewGame,
119            MainMenuItem::LoadGame,
120            MainMenuItem::Settings,
121            MainMenuItem::Quit,
122        ];
123
124        let quit_position = main_menu_items
125            .iter()
126            .position(|item| *item == MainMenuItem::Quit)
127            .ok_or(InitError::MissingQuit)?;
128
129        let main_menu_bindings = menu::default_key_bindings()
130            .with(Key::Char('q'), menu::Command::SelectConfirm(quit_position));
131
132        let main_menu = Menu::new("=== T H E D E S ===", main_menu_items)?
133            .with_keybindings(main_menu_bindings);
134
135        let new_game = new_game::Component::new()?;
136        let game_creation = game_creation::Component::new();
137
138        let load_game = load_game::Component::new();
139
140        let settings = settings::Component::load(config.settings_path).await?;
141
142        Ok(Self {
143            main_menu,
144            new_game,
145            game_creation,
146            load_game,
147            session_config: session::Config::new(),
148            saves_dir: config.saves_dir,
149            settings,
150        })
151    }
152
153    pub async fn run(
154        &mut self,
155        app: &mut thedes_tui::core::App,
156    ) -> Result<(), Error> {
157        for sink_type in [AudioSinkType::Music] {
158            app.audio_controller.queue([audio::Command::new_set_volume(
159                sink_type,
160                self.settings.values().audio().volume(sink_type),
161            )]);
162        }
163
164        let assets = Assets::get().await?;
165        app.audio_controller.queue([audio::Command::new_enter_repeated(
166            AudioSinkType::Music,
167            &assets.sound.main_theme[..],
168        )]);
169
170        app.audio_controller.flush()?;
171
172        loop {
173            self.main_menu.run(app).await?;
174
175            match self.main_menu.output() {
176                MainMenuItem::NewGame => {
177                    self.new_game.set_seed(rand::random());
178                    self.new_game.run(app).await?;
179                    if self.new_game.is_cancelling() {
180                        continue;
181                    }
182                    let seed = self.new_game.form().seed;
183                    let config = thedes_gen::Config::new().with_seed(seed);
184                    if let Some(game) =
185                        self.game_creation.run(app, config).await?
186                    {
187                        let mut save_path = self.saves_dir.clone();
188                        save_path.push(format!(
189                            "{}{}",
190                            self.new_game.form().name,
191                            SAVE_EXTENSION,
192                        ));
193                        let mut session = self
194                            .session_config
195                            .clone()
196                            .finish(save_path, game)?;
197                        session.run(&mut self.settings, app).await?;
198                    }
199                },
200
201                MainMenuItem::LoadGame => {
202                    if let Some(save_path) =
203                        self.load_game.run(&self.saves_dir, app).await?
204                    {
205                        let mut session = self
206                            .session_config
207                            .clone()
208                            .finish_loading(save_path)
209                            .await?;
210                        session.run(&mut self.settings, app).await?;
211                    }
212                },
213                MainMenuItem::Settings => {
214                    self.settings.run(app).await?;
215                },
216                MainMenuItem::Quit => break,
217            }
218        }
219
220        Ok(())
221    }
222}