From fdd4f9f3884b23753dff37f0f7e09c02a560e75a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wo=C5=BAniak?= Date: Tue, 27 Dec 2022 20:33:01 +0100 Subject: [PATCH 1/4] Add translations --- assets/{mainmenu.png => main_menu.png} | Bin game/Cargo.toml | 4 +- game/build.rs | 1 + game/src/backend/constants.rs | 53 ++++--- game/src/backend/gamestate.rs | 76 +++++++--- game/src/backend/generate_machines.rs | 2 +- game/src/backend/movement.rs | 5 +- game/src/backend/screen.rs | 36 ++--- game/src/game_core/event.rs | 43 +++--- game/src/game_core/infoscreen.rs | 97 +++++++------ game/src/game_core/player.rs | 42 +++--- game/src/languages/english.rs | 135 ++++++++++++++++++ game/src/languages/german.rs | 16 ++- game/src/languages/mod.rs | 74 ++++++++++ game/src/machines/machine.rs | 35 +++-- game/src/main.rs | 25 ++-- game/src/main_menu/button.rs | 2 +- .../main_menu/{mainmenu.rs => main_menu.rs} | 85 ++++++++--- game/src/main_menu/mod.rs | 2 +- 19 files changed, 537 insertions(+), 196 deletions(-) rename assets/{mainmenu.png => main_menu.png} (100%) create mode 100644 game/src/languages/english.rs rename game/src/main_menu/{mainmenu.rs => main_menu.rs} (66%) diff --git a/assets/mainmenu.png b/assets/main_menu.png similarity index 100% rename from assets/mainmenu.png rename to assets/main_menu.png diff --git a/game/Cargo.toml b/game/Cargo.toml index 055c4b5..f23288d 100644 --- a/game/Cargo.toml +++ b/game/Cargo.toml @@ -10,7 +10,7 @@ ggez = { git="https://github.com/ggez/ggez", branch="devel" } serde = "1.0.145" serde_yaml = "0.9.13" tracing = "0.1.37" -tracing-subscriber = "0.3.16" +tracing-subscriber = { version = "0.3.16", features = ['env-filter'] } fastrand = "1.8.0" chrono = "0.4.23" @@ -23,4 +23,4 @@ codegen-units = 1 [profile.release] lto = true -panic = "abort" \ No newline at end of file +panic = "abort" diff --git a/game/build.rs b/game/build.rs index b59cb95..fc07a79 100644 --- a/game/build.rs +++ b/game/build.rs @@ -3,6 +3,7 @@ use std::env; extern crate fs_extra; use fs_extra::dir::{copy, CopyOptions}; + fn main() { let out_dir = env::var_os("OUT_DIR").unwrap(); // Copy our resources directory to OUT_DIR diff --git a/game/src/backend/constants.rs b/game/src/backend/constants.rs index dc3ba4c..39ea572 100644 --- a/game/src/backend/constants.rs +++ b/game/src/backend/constants.rs @@ -2,7 +2,7 @@ use crate::backend::rlcolor::RLColor; use crate::game_core::player::gen_inventory; use crate::game_core::resources::Resources; -use crate::languages::german::MACHINE_NAMES; +use crate::languages::{machine_names, Lang}; use crate::machines::machine::{Machine, State}; use crate::machines::trade::Trade; use ggez::graphics::{Color, Rect}; @@ -48,11 +48,12 @@ pub(crate) const SANDSTURM_CR: Resources = Resources { /// Generates all machines with all their name, position, trades and resources. /// # Returns /// A Vector of `Machine`s -pub(crate) fn gen_all_machines() -> Vec { +pub(crate) fn gen_all_machines(lng: Lang) -> Vec { vec![ // Oxygen machine Machine::new_by_const(( - MACHINE_NAMES[0].to_string(), + machine_names(lng)[0].to_string(), + machine_names(Lang::De)[0].to_string(), Rect { x: 280.0, y: 230.0, @@ -66,7 +67,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Broken, State::Idle, false, - gen_inventory(2, 0, 0), + gen_inventory(2, 0, 0, lng), ), Trade::new( "start_Oxygen".to_string(), @@ -74,7 +75,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Idle, State::Running, true, - gen_inventory(0, 0, 0), + gen_inventory(0, 0, 0, lng), ), Trade::new( "stop_Oxygen".to_string(), @@ -82,7 +83,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Running, State::Idle, true, - gen_inventory(0, 0, 0), + gen_inventory(0, 0, 0, lng), ), ], Resources { @@ -93,7 +94,8 @@ pub(crate) fn gen_all_machines() -> Vec { )), // Electricity machine Machine::new_by_const(( - MACHINE_NAMES[1].to_string(), + machine_names(lng)[1].to_string(), + machine_names(Lang::De)[1].to_string(), Rect { x: 282.0, y: 752.0, @@ -107,7 +109,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Broken, State::Running, true, - gen_inventory(0, 1, 0), + gen_inventory(0, 1, 0, lng), ), Trade::new( "start_Stromgenerator".to_string(), @@ -115,7 +117,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Idle, State::Running, true, - gen_inventory(0, 0, 0), + gen_inventory(0, 0, 0, lng), ), Trade::new( "stop_Stromgenerator".to_string(), @@ -123,7 +125,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Running, State::Idle, true, - gen_inventory(0, 0, 0), + gen_inventory(0, 0, 0, lng), ), ], Resources { @@ -134,7 +136,8 @@ pub(crate) fn gen_all_machines() -> Vec { )), // Worker machine Machine::new_by_const(( - MACHINE_NAMES[2].to_string(), + machine_names(lng)[2].to_string(), + machine_names(Lang::De)[2].to_string(), Rect { x: 1000.0, y: 780.0, @@ -148,7 +151,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Broken, State::Idle, false, - gen_inventory(0, 0, 1), + gen_inventory(0, 0, 1, lng), ), Trade::new( "produce_superglue".to_string(), @@ -156,7 +159,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Idle, State::Running, true, - gen_inventory(-1, 0, 0), + gen_inventory(-1, 0, 0, lng), ), ], Resources { @@ -167,7 +170,8 @@ pub(crate) fn gen_all_machines() -> Vec { )), // 3d Printer machine Machine::new_by_const(( - MACHINE_NAMES[3].to_string(), + machine_names(lng)[3].to_string(), + machine_names(Lang::De)[3].to_string(), Rect { x: 930.0, y: 230.0, @@ -181,7 +185,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Broken, State::Idle, false, - gen_inventory(2, 0, 0), + gen_inventory(2, 0, 0, lng), ), Trade::new( "produce_3d_teil".to_string(), @@ -189,7 +193,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Idle, State::Running, true, - gen_inventory(2, 0, -1), + gen_inventory(2, 0, -1, lng), ), ], Resources { @@ -200,7 +204,8 @@ pub(crate) fn gen_all_machines() -> Vec { )), // Communication module Machine::new_by_const(( - MACHINE_NAMES[4].to_string(), + machine_names(lng)[4].to_string(), + machine_names(Lang::De)[4].to_string(), Rect { x: 1640.0, y: 320.0, @@ -214,7 +219,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Broken, State::Idle, false, - gen_inventory(5, 0, 3), + gen_inventory(5, 0, 3, lng), ), Trade::new( "Notfall_signal_absetzen".to_string(), @@ -222,7 +227,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Idle, State::Running, true, - gen_inventory(1, 0, 1), + gen_inventory(1, 0, 1, lng), ), ], Resources { @@ -233,7 +238,8 @@ pub(crate) fn gen_all_machines() -> Vec { )), // First hole Machine::new_by_const(( - MACHINE_NAMES[5].to_string(), + machine_names(lng)[5].to_string(), + machine_names(Lang::De)[5].to_string(), Rect { x: 780.0, y: 230.0, @@ -246,7 +252,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Running, State::Idle, false, - gen_inventory(2, 0, 0), + gen_inventory(2, 0, 0, lng), )], Resources { oxygen: -15, @@ -256,7 +262,8 @@ pub(crate) fn gen_all_machines() -> Vec { )), // Second hole Machine::new_by_const(( - MACHINE_NAMES[6].to_string(), + machine_names(lng)[6].to_string(), + machine_names(Lang::De)[6].to_string(), Rect { x: 680.0, y: 900.0, @@ -269,7 +276,7 @@ pub(crate) fn gen_all_machines() -> Vec { State::Running, State::Idle, false, - gen_inventory(2, 0, 0), + gen_inventory(2, 0, 0, lng), )], Resources { oxygen: -15, diff --git a/game/src/backend/gamestate.rs b/game/src/backend/gamestate.rs index 2f06310..c09d8c7 100644 --- a/game/src/backend/gamestate.rs +++ b/game/src/backend/gamestate.rs @@ -13,10 +13,7 @@ use crate::game_core::infoscreen::InfoScreen; use crate::game_core::item::Item; use crate::game_core::player::Player; use crate::game_core::resources::Resources; -use crate::languages::german::{ - FIRST_MILESTONE_HANDBOOK_TEXT, MACHINE_NAMES, RESOURCE_NAME, SECOND_MILESTONE_HANDBOOK_TEXT, - TIME_NAME, -}; +use crate::languages::*; use crate::machines::machine::Machine; use crate::machines::machine::State::Broken; use crate::{draw, RLResult}; @@ -39,7 +36,7 @@ pub enum GameCommand { } /// This is the game state. It contains all the data that is needed to run the game. -#[derive(Debug, Default, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub struct GameState { /// Contains the current player position, resources(air, energy, life) and the inventory and their change rates pub player: Player, @@ -51,7 +48,7 @@ pub struct GameState { /// Contains all the images that are needed to draw the game on the canvas assets: HashMap, #[serde(skip)] - /// Needed to send Messages to the `Screenstack` to make changes to the screen + /// Needed to send Messages to the `Screen stack` to make changes to the screen pub(crate) screen_sender: Option>, #[serde(skip)] /// Needed to receive Messages from `machine` to make changes to the game @@ -61,6 +58,8 @@ pub struct GameState { pub(crate) sender: Option>, /// Defines if the handbook is currently open pub handbook_invisible: bool, + #[serde(default)] + pub lng: Lang, } impl PartialEq for GameState { @@ -71,6 +70,21 @@ impl PartialEq for GameState { } impl GameState { + pub fn new_with_lang(lng: Lang) -> Self { + let state = Self { + player: Player::new(lng), + events: vec![], + machines: vec![], + assets: Default::default(), + screen_sender: None, + receiver: None, + sender: None, + handbook_invisible: false, + lng, + }; + state + } + /// Gets the screen sender /// # Returns /// * `RLResult>`: The screen sender in a `RLResult` to handle Initialization errors @@ -92,13 +106,19 @@ impl GameState { /// It loads all the assets and creates the areas of the machines. /// # Returns /// * `RLResult`: The new game state initialized in a `RLResult` to handle setup errors - pub fn new(ctx: &mut Context) -> RLResult { - info!("Creating new gamestate"); + pub fn new(ctx: &mut Context, lng: Lang) -> RLResult { + info!("Creating new game state"); let (sender, receiver) = channel(); let mut result = GameState { + player: Player::new(lng), + events: vec![], + machines: vec![], + assets: Default::default(), sender: Some(sender), receiver: Some(receiver), - ..Default::default() + lng, + screen_sender: None, + handbook_invisible: false, }; result.init(ctx)?; Ok(result) @@ -109,6 +129,8 @@ impl GameState { /// # Returns /// * `RLResult`: A `RLResult` to validate the success of the tick function pub fn tick(&mut self) -> RLResult { + let lng = self.lng; + // Update Resources self.player.resources = self .player @@ -134,11 +156,12 @@ impl GameState { } }; if self.player.resources.life == 0 { - let gamestate = GameState::load(true).unwrap_or_default(); - gamestate.save(false)?; + let game_state = + GameState::load(true).unwrap_or_else(|_| GameState::new_with_lang(lng)); + game_state.save(false)?; let cloned_sender = self.get_screen_sender()?.clone(); self.get_screen_sender()?.send(StackCommand::Push(Box::new( - InfoScreen::new_deathscreen(empty_resource, cloned_sender), + InfoScreen::new_death_screen(empty_resource, cloned_sender, game_state.lng), )))?; }; } else if self.player.resources_change.life < 0 { @@ -162,7 +185,8 @@ impl GameState { GameCommand::Winning => match self.player.milestone { 1 => { let sender = self.get_screen_sender()?; - let popup = Popup::new(RLColor::GREEN, "Die Nachricht kann nicht gesendet werden solange das System nicht wiederhergestellt ist".to_string(), 5); + let popup = + Popup::new(RLColor::GREEN, send_msg_failure(lng).to_string(), 5); sender.send(StackCommand::Popup(popup))?; } 2 => { @@ -176,7 +200,7 @@ impl GameState { // Regenerate life if applicable self.player - .life_regeneration(&self.screen_sender.as_ref().unwrap().clone())?; + .life_regeneration(&self.screen_sender.as_ref().unwrap().clone(), lng)?; for machine in &mut self.machines { machine.tick()?; } @@ -207,7 +231,7 @@ impl GameState { draw!(canvas, &mesh, scale); let text = graphics::Text::new(format!( "{}: {:.1}", - RESOURCE_NAME[i], + resource_name(self.lng)[i], (resource as f32 / u16::MAX as f32) * 100.0 )); draw!( @@ -233,11 +257,11 @@ impl GameState { draw!(canvas, image, Vec2::new(700.0, 300.0), scale); match self.player.milestone { 1 => { - self.draw_handbook_text(canvas, scale, &FIRST_MILESTONE_HANDBOOK_TEXT); + self.draw_handbook_text(canvas, scale, first_milestone_handbook_text(self.lng)); } 2 => { - self.draw_handbook_text(canvas, scale, &SECOND_MILESTONE_HANDBOOK_TEXT); + self.draw_handbook_text(canvas, scale, second_milestone_handbook_text(self.lng)); } _ => {} } @@ -308,7 +332,7 @@ impl GameState { let time = self.player.time / DESIRED_FPS; let time_text = format!( "{}: {}h {}m {}s", - TIME_NAME[0], + time_name(self.lng)[0], time / 3600, time / 60, time % 60 @@ -349,7 +373,7 @@ impl GameState { let machine_assets: Vec> = self .machines .iter() - .map(|m| m.name.clone()) + .map(|m| m.asset_name()) .map(|name| { info!("Loading assets for {}", name); if self.assets.contains_key(&format!("{name}.png")) { @@ -489,6 +513,8 @@ impl GameState { /// Decides what happens if a certain milestone is reached /// divided into 3 milestones fn get_current_milestone(&mut self) -> RLResult { + let lng = self.lang(); + match self.player.milestone { 0 => { self.player.resources_change.oxygen = -1; @@ -498,8 +524,8 @@ impl GameState { } 1 => { if self.check_on_milestone_machines(&[ - MACHINE_NAMES[0].to_string(), - MACHINE_NAMES[1].to_string(), + machine_names(self.lng)[0].to_string(), + machine_names(self.lng)[1].to_string(), ]) { self.increase_milestone()?; } @@ -509,7 +535,7 @@ impl GameState { self.player.milestone += 1; let cloned_sender = self.get_screen_sender()?.clone(); self.get_screen_sender()?.send(StackCommand::Push(Box::new( - InfoScreen::new_winningscreen(cloned_sender), + InfoScreen::new_winning_screen(cloned_sender, lng), )))?; } _ => {} @@ -535,7 +561,7 @@ impl GameState { impl Screen for GameState { /// Updates the game and handles input. Returns `StackCommand::Pop` when Escape is pressed. - fn update(&mut self, ctx: &mut Context) -> RLResult { + fn update(&mut self, ctx: &mut Context, _lng: Lang) -> RLResult { if ctx.time.check_update_time(DESIRED_FPS) { self.tick()?; self.move_player(ctx)?; @@ -602,6 +628,10 @@ impl Screen for GameState { self.screen_sender = Some(sender); self.init_all_machines(); } + + fn lang(&self) -> Lang { + self.lng + } } #[cfg(test)] diff --git a/game/src/backend/generate_machines.rs b/game/src/backend/generate_machines.rs index 8880083..41ab764 100644 --- a/game/src/backend/generate_machines.rs +++ b/game/src/backend/generate_machines.rs @@ -13,7 +13,7 @@ impl GameState { /// Creates all Machines for initial creation and pushes them into a list pub fn create_machine(&mut self) { info!("Generating all Machines"); - self.machines = gen_all_machines(); + self.machines = gen_all_machines(self.lng); } /// Paints the machine sprites and if applicable it shows the state or time remaining diff --git a/game/src/backend/movement.rs b/game/src/backend/movement.rs index 927dcb0..82afe16 100644 --- a/game/src/backend/movement.rs +++ b/game/src/backend/movement.rs @@ -1,7 +1,7 @@ //! This file contains the movement system, which is responsible for moving the player around the map and to interact with objects. use crate::backend::constants::MOVEMENT_SPEED; use crate::backend::gamestate::GameState; -use crate::backend::screen::StackCommand; +use crate::backend::screen::{Screen, StackCommand}; use crate::RLResult; use ggez::winit::event::VirtualKeyCode; use ggez::Context; @@ -16,6 +16,7 @@ impl GameState { /// # Returns /// * `RLResult<()>` - Returns okay, if no Error occurred pub fn move_player(&mut self, ctx: &mut Context) -> RLResult { + let lng = self.lang(); if ctx.keyboard.is_key_just_pressed(VirtualKeyCode::Escape) { info!("Exiting..."); self.save(false)?; @@ -25,7 +26,7 @@ impl GameState { info!("Interacting with Area: {:?}", self.get_interactable()); let player_ref = &self.player.clone(); if let Some(interactable) = self.get_interactable() { - interactable.interact(player_ref)?; + interactable.interact(player_ref, lng)?; } } if ctx.keyboard.is_key_just_pressed(VirtualKeyCode::H) { diff --git a/game/src/backend/screen.rs b/game/src/backend/screen.rs index b4aa829..415b580 100644 --- a/game/src/backend/screen.rs +++ b/game/src/backend/screen.rs @@ -2,9 +2,10 @@ use crate::backend::rlcolor::RLColor; use crate::backend::utils::{get_draw_params, get_scale}; use crate::error::RLError; -use crate::main_menu::mainmenu::MainMenu; +use crate::main_menu::main_menu::MainMenu; use crate::{draw, RLResult}; +use crate::languages::Lang; use ggez::glam::vec2; use ggez::graphics::Color; use ggez::{event, graphics, Context}; @@ -21,7 +22,7 @@ pub trait Screen: Debug { /// * `ctx` - The ggez context /// # Returns /// `RLResult` - Returns an `RlResult`. - fn update(&mut self, ctx: &mut Context) -> RLResult; + fn update(&mut self, ctx: &mut Context, lng: Lang) -> RLResult; /// Used for drawing the screen. /// # Arguments /// * `ctx` - The ggez context @@ -32,11 +33,13 @@ pub trait Screen: Debug { /// # Arguments /// * `sender` - The sender of the screen. fn set_sender(&mut self, sender: Sender); + + fn lang(&self) -> Lang; } /// A Screenstack contains multiple `Screen`s and `Popup`s, the last one of which is drawn to the screen and /// updated. -pub struct Screenstack { +pub struct ScreenStack { screens: Vec>, popup: Vec, receiver: Receiver, @@ -104,7 +107,7 @@ impl Popup { } } } -impl Screenstack { +impl ScreenStack { /// Draws all `Popups` at the top left of the screen with their given text and color /// The popups will be removed after the given duration /// # Arguments @@ -190,7 +193,7 @@ pub enum StackCommand { Pop, } -impl event::EventHandler for Screenstack { +impl event::EventHandler for ScreenStack { /// Redirect the update function to the last screen and handle the returned `StackCommand` /// # Arguments /// * `ctx` - The ggez game context @@ -198,10 +201,9 @@ impl event::EventHandler for Screenstack { /// `RLResult` - Returns an `RlResult` fn update(&mut self, ctx: &mut Context) -> RLResult { self.remove_popups(); - self.screens - .last_mut() - .expect("Failed to get a screen") - .update(ctx)?; + let screen = self.screens.last_mut().expect("Failed to get a screen"); + let lng = screen.lang(); + screen.update(ctx, lng)?; if let Ok(message) = self.receiver.try_recv() { self.process_command(message); } @@ -230,15 +232,15 @@ impl event::EventHandler for Screenstack { } } -impl Default for Screenstack { - /// Creates a new `Screenstack` with a `MainMenu` screen. +impl ScreenStack { + /// Creates a new `Screen stack` with a `MainMenu` screen. /// # Returns - /// `Screenstack` - Returns a new `Screenstack`. - fn default() -> Self { + /// `Screen stack` - Returns a new `Screen stack`. + pub fn new_with_lang(lng: Lang) -> Self { info!("Default Screenstack created"); let (sender, receiver) = channel(); Self { - screens: vec![Box::new(MainMenu::new(sender.clone()))], + screens: vec![Box::new(MainMenu::new(sender.clone(), lng))], popup: vec![], receiver, sender, @@ -251,8 +253,8 @@ mod test { use super::*; #[test] - fn test_screenstack() { - let screenstack = Screenstack::default(); - assert_eq!(1, screenstack.screens.len()); + fn test_screen_stack() { + let screen_stack = ScreenStack::new_with_lang(Lang::De); + assert_eq!(1, screen_stack.screens.len()); } } diff --git a/game/src/game_core/event.rs b/game/src/game_core/event.rs index 8aad0c7..837967c 100644 --- a/game/src/game_core/event.rs +++ b/game/src/game_core/event.rs @@ -2,10 +2,7 @@ use crate::backend::constants::{DESIRED_FPS, SANDSTURM_CR}; use crate::backend::gamestate::GameState; use crate::backend::screen::{Popup, StackCommand}; use crate::game_core::resources::Resources; -use crate::languages::german::{ - INFORMATIONSPOPUP_MARS, INFORMATIONSPOPUP_NASA, KOMETENEINSCHLAG, SANDSTURM, STROMAUSFALL, -}; -use crate::languages::german::{MARS_INFO, NASA_INFO, WARNINGS}; +use crate::languages::*; use crate::machines::machine::State; use crate::RLResult; use ggez::graphics::Color; @@ -60,35 +57,41 @@ impl Event { } /// if no Event is active it either chooses a random event of the Event enum or nothing every 60 seconds - pub fn event_generator() -> Option { + pub fn event_generator(lng: Lang) -> Option { let rng = fastrand::Rng::new(); let event = rng.usize(..15); match event { 8 => Some(Event::new( - SANDSTURM, - WARNINGS[2], + *sandstorm(lng), + warnings(lng)[2], "warning", Some(SANDSTURM_CR), 5, )), 0 | 3 => Some(Event::new( - KOMETENEINSCHLAG, - WARNINGS[0], + *cometa_strike(lng), + warnings(lng)[0], "warning", None, 0, )), 1 => Some(Event::new( - INFORMATIONSPOPUP_NASA, - NASA_INFO[rng.usize(..4)], + *informations_popup_nasa(lng), + nasa_info(lng)[rng.usize(..4)], "nasa", None, 0, )), - 2 | 9 | 7 => Some(Event::new(STROMAUSFALL, WARNINGS[1], "warning", None, 0)), + 2 | 9 | 7 => Some(Event::new( + *power_failure(lng), + warnings(lng)[1], + "warning", + None, + 0, + )), 4 => Some(Event::new( - INFORMATIONSPOPUP_MARS, - MARS_INFO[rng.usize(..5)], + *informations_popup_mars(lng), + mars_info(lng)[rng.usize(..5)], "mars", None, 0, @@ -135,13 +138,14 @@ impl Event { /// * `restore` - If true the event will be deactivated and the resources will be restored /// * `gamestate` - The gamestate which is used to access the player and the machines pub fn action(&self, restore: bool, gamestate: &mut GameState) -> RLResult { - const KOMETENEINSCHLAG_NAME: &str = KOMETENEINSCHLAG[0]; - const STROMAUSFALL_NAME: &str = STROMAUSFALL[0]; + let lng = gamestate.lng; + let cometa_strike: &str = cometa_strike(lng)[0]; + let power_failure: &str = power_failure(lng)[0]; let sender = gamestate.get_screen_sender()?.clone(); // handle event effects match self.name.as_str() { - KOMETENEINSCHLAG_NAME => { + s if cometa_strike == s => { if let Some(one_hole) = gamestate .machines .iter_mut() @@ -153,7 +157,7 @@ impl Event { one_hole.change_state_to(&State::Running); } } - STROMAUSFALL_NAME => { + s if s == power_failure => { gamestate.machines.iter_mut().for_each(|machine| { // if machine is running it will b use tracing::{info, Id};e stopped // event not triggered if machine is broken or idling @@ -197,6 +201,7 @@ impl Event { /// * `gamestate` - The gamestate which is used to access the events vector /// * `context` - The game context which is used to access the current tick pub fn update_events(ctx: &Context, gamestate: &mut GameState) -> RLResult { + let lng = gamestate.lng; if ctx.time.ticks() % 20 == 0 { gamestate.events.iter_mut().for_each(|event| { event.duration = event.duration.saturating_sub(20); @@ -225,7 +230,7 @@ impl Event { if ctx.time.ticks() >= 400 && ctx.time.ticks() % 200 == 0 { // generate new event // might not return an event - let gen_event = Event::event_generator(); + let gen_event = Event::event_generator(lng); // if event is not none, add it to the gamestates events vector and activate apply its effect if let Some(event) = gen_event { event.action(false, gamestate)?; diff --git a/game/src/game_core/infoscreen.rs b/game/src/game_core/infoscreen.rs index bc3bea0..ef5c6b8 100644 --- a/game/src/game_core/infoscreen.rs +++ b/game/src/game_core/infoscreen.rs @@ -1,17 +1,12 @@ use crate::backend::gamestate::{GameCommand, GameState}; use crate::backend::screen::{Screen, StackCommand}; use crate::backend::utils::{get_draw_params, get_scale}; -use crate::languages::german::{ - ADDITIONAL_INFO_STRING, AIR_AND_ENERGY_STRING, AIR_STRING, BUTTON_INFO, DEATH_REASON_STRING, - ENERGY_STRING, INTRO_TEXT, TUTORIAL_TEXT, WINNING_TEXT, -}; - -use crate::main_menu::mainmenu::MainMenu; +use crate::languages::*; +use crate::main_menu::main_menu::MainMenu; use crate::{draw, RLResult}; use ggez::glam::Vec2; use ggez::winit::event::VirtualKeyCode; use ggez::{graphics, Context}; -use std::fmt::{Display, Formatter}; use std::fs; use std::sync::mpsc::Sender; use tracing::info; @@ -23,16 +18,17 @@ pub enum DeathReason { Energy, Both, } -impl Display for DeathReason { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + +impl DeathReason { + fn t(&self, lng: Lang) -> &'static str { match self { - DeathReason::Oxygen => write!(f, "{AIR_STRING}"), - DeathReason::Energy => write!(f, "{ENERGY_STRING}"), - DeathReason::Both => write!(f, "{AIR_AND_ENERGY_STRING}"), + DeathReason::Oxygen => air_string(lng), + DeathReason::Energy => energy_string(lng), + DeathReason::Both => air_and_energy_string(lng), } } } -/// Defines the type of Screen which is Infoscreen currently showing +/// Defines the type of Screen which is Info screen currently showing #[derive(Copy, Clone, Debug, PartialEq)] pub enum ScreenType { Death, @@ -40,76 +36,92 @@ pub enum ScreenType { Winning, } -/// Create `DeathScreen`, `IntroScreen` or `WinningSreen`. `DeathScreen` needs the reason of death from `DeathReason` enum. +/// Create `DeathScreen`, `IntroScreen` or `WinningScreen`. `DeathScreen` needs the reason of death from `DeathReason` enum. #[derive(Debug)] pub struct InfoScreen { - background: String, + background: &'static str, main_message: graphics::Text, additional_text: graphics::Text, sender: Sender, - screentype: ScreenType, + screen_type: ScreenType, background_image: Option, + lng: Lang, } impl InfoScreen { - /// Creates a new `DeathScreen` using `InfoScreen` with a `Deathreason` + /// Creates a new `DeathScreen` using `InfoScreen` with a `Death reason` /// # Arguments /// * `death_reason` - The reason for the death of the player /// * `sender` - The sender to send the command to the `ScreenStack` - pub fn new_deathscreen(death_reason: DeathReason, sender: Sender) -> Self { - info!("The player died due to a lack of : {:?}", death_reason); + pub fn new_death_screen( + death_reason: DeathReason, + sender: Sender, + lng: Lang, + ) -> Self { + info!( + "The player died due to a lack of : {:?}", + death_reason.t(lng) + ); - let mut main_message = graphics::Text::new(format!("{DEATH_REASON_STRING} {death_reason}")); + let mut main_message = graphics::Text::new(format!( + "{} {}", + death_reason_string(lng), + death_reason.t(lng) + )); main_message.set_scale(70.); - let mut additional_text = graphics::Text::new(ADDITIONAL_INFO_STRING); + let mut additional_text = graphics::Text::new(additional_info_string(lng)); additional_text.set_scale(70.); - let background = "deathscreen".to_string(); + let background = "deathscreen"; let screentype = ScreenType::Death; Self { background, main_message, additional_text, sender, - screentype, + screen_type: screentype, background_image: None, + lng, } } /// Creates a new `IntroScreen` using `InfoScreen` /// # Arguments /// * `sender` - The sender to send the command to the `ScreenStack` - pub fn new_introscreen(sender: Sender) -> Self { - let mut main_message = graphics::Text::new(format!("{INTRO_TEXT} \n{TUTORIAL_TEXT}")); + pub fn new_intro_screen(sender: Sender, lng: Lang) -> Self { + let mut main_message = + graphics::Text::new(format!("{} \n{}", intro_text(lng), tutorial_text(lng))); main_message.set_scale(50.); - let mut additional_text = graphics::Text::new(BUTTON_INFO); + let mut additional_text = graphics::Text::new(button_info(lng)); additional_text.set_scale(50.); - let background = "Introscreen".to_string(); - let screentype = ScreenType::Intro; + let background = "Introscreen"; + let screen_type = ScreenType::Intro; Self { background, main_message, additional_text, sender, - screentype, + screen_type, background_image: None, + lng, } } /// Creates a new Winning using `InfoScreen` /// # Arguments /// * `sender` - The sender to send the command to the `ScreenStack` - pub fn new_winningscreen(sender: Sender) -> Self { - let mut main_message = graphics::Text::new(WINNING_TEXT); + pub fn new_winning_screen(sender: Sender, lng: Lang) -> Self { + let mut main_message = graphics::Text::new(winning_text(lng)); main_message.set_scale(70.); - let mut additional_text = graphics::Text::new(ADDITIONAL_INFO_STRING); + let mut additional_text = graphics::Text::new(additional_info_string(lng)); additional_text.set_scale(70.); - let background = "Winningscreen".to_string(); - let screentype = ScreenType::Winning; + let background = "Winningscreen"; + let screen_type = ScreenType::Winning; Self { background, main_message, additional_text, sender, - screentype, + screen_type, background_image: None, + lng, } } } @@ -120,7 +132,7 @@ impl Screen for InfoScreen { /// * `ctx` - The ggez context /// # Returns /// `RLResult` - Returns an `RLResult`. - fn update(&mut self, ctx: &mut Context) -> RLResult { + fn update(&mut self, ctx: &mut Context, lng: Lang) -> RLResult { if self.background_image.is_none() { self.background_image = Some(graphics::Image::from_bytes( ctx, @@ -129,11 +141,11 @@ impl Screen for InfoScreen { } let keys = ctx.keyboard.pressed_keys(); // Here we only use the first pressed key, but in the infoscreen this is fine - match (self.screentype, keys.iter().next()) { + match (self.screen_type, keys.iter().next()) { (ScreenType::Intro, Some(&VirtualKeyCode::Space)) => { self.sender.send(StackCommand::Pop)?; self.sender.send(StackCommand::Push(Box::new({ - let mut gamestate = GameState::new(ctx)?; + let mut gamestate = GameState::new(ctx, lng)?; gamestate.init(ctx)?; gamestate.create_machine(); gamestate @@ -145,12 +157,13 @@ impl Screen for InfoScreen { })))?; } (ScreenType::Death | ScreenType::Winning, Some(&VirtualKeyCode::Escape)) => { - if self.screentype == ScreenType::Winning { + if self.screen_type == ScreenType::Winning { GameState::delete_saves()?; } self.sender.send(StackCommand::Pop)?; self.sender.send(StackCommand::Push(Box::new(MainMenu::new( self.sender.clone(), + lng, ))))?; } _ => {} @@ -169,7 +182,7 @@ impl Screen for InfoScreen { if let Some(background) = &self.background_image { canvas.draw(background, graphics::DrawParam::default().scale(scale)); } - if self.screentype == ScreenType::Intro { + if self.screen_type == ScreenType::Intro { draw!(canvas, &self.main_message, Vec2::new(300., 300.), scale); } else { draw!(canvas, &self.main_message, Vec2::new(220., 500.), scale); @@ -185,4 +198,8 @@ impl Screen for InfoScreen { fn set_sender(&mut self, sender: Sender) { self.sender = sender; } + + fn lang(&self) -> Lang { + self.lng + } } diff --git a/game/src/game_core/player.rs b/game/src/game_core/player.rs index f79e20f..8a7dee8 100644 --- a/game/src/game_core/player.rs +++ b/game/src/game_core/player.rs @@ -3,8 +3,7 @@ use crate::backend::rlcolor::RLColor; use crate::backend::screen::{Popup, StackCommand}; use crate::game_core::item::Item; use crate::game_core::resources::Resources; -use crate::languages::german::GAME_INFO; -use crate::languages::german::{BENZIN, GEDRUCKTESTEIL, SUPER_GLUE}; +use crate::languages::*; use crate::RLResult; use serde::{Deserialize, Serialize}; use std::sync::mpsc::Sender; @@ -28,14 +27,15 @@ pub struct Player { /// contains the current ingame time pub(crate) time: u32, } -impl Default for Player { - fn default() -> Self { + +impl Player { + pub fn new(lng: Lang) -> Self { info!("Default Player created"); Self { inventory: vec![ - (Item::new(SUPER_GLUE), 0), - (Item::new(BENZIN), 3), - (Item::new(GEDRUCKTESTEIL), 1), + (Item::new(*super_glue(lng)), 0), + (Item::new(*petrol(lng)), 3), + (Item::new(*printed_part(lng)), 1), ], position: (600, 500), resources: Resources { @@ -53,15 +53,16 @@ impl Default for Player { time: 0, } } -} - -impl Player { /// Checks whether the player has taken damage in the past few seconds and if not so start the regeneration /// # Arguments /// * `sender` - The sender of the screen, needed to send a `Popup` to the screen. /// # Returns /// * `RLResult` - validates if life regeneration was started correctly - pub(crate) fn life_regeneration(&mut self, sender: &Sender) -> RLResult { + pub(crate) fn life_regeneration( + &mut self, + sender: &Sender, + lng: Lang, + ) -> RLResult { match ( self.resources_change.life, self.last_damage, @@ -83,8 +84,8 @@ impl Player { (0, last_damage, _) if last_damage >= 8 * DESIRED_FPS => { self.resources_change.life += 5; self.last_damage = 0; - let popup = Popup::new(RLColor::GREEN, GAME_INFO[0].to_string(), 5); - info!("Player startet healing"); + let popup = Popup::new(RLColor::GREEN, game_info(lng)[0].to_string(), 5); + info!("Player started healing"); sender.send(StackCommand::Popup(popup))?; } // If player takes damage, increase last damage point @@ -127,11 +128,16 @@ impl Player { /// * `super_glue` - The amount of super glue /// * `benzin` - The amount of benzin /// * `gedrucktesteil` - The amount of the printed part -pub fn gen_inventory(super_glue: i32, benzin: i32, gedrucktesteil: i32) -> Vec<(Item, i32)> { +pub fn gen_inventory( + super_glue_amount: i32, + benzin_amount: i32, + printed_parts_amount: i32, + lng: Lang, +) -> Vec<(Item, i32)> { vec![ - (Item::new(SUPER_GLUE), super_glue), - (Item::new(BENZIN), benzin), - (Item::new(GEDRUCKTESTEIL), gedrucktesteil), + (Item::new(*super_glue(lng)), super_glue_amount), + (Item::new(*petrol(lng)), benzin_amount), + (Item::new(*printed_part(lng)), printed_parts_amount), ] } @@ -217,7 +223,7 @@ mod test { ..Player::default() }; player - .life_regeneration(&gamestate.get_screen_sender().unwrap().clone()) + .life_regeneration(&gamestate.get_screen_sender().unwrap().clone(), Lang::De) .unwrap(); assert_eq!(player.resources_change.life, -1); assert_eq!(player.last_damage, 0); diff --git a/game/src/languages/english.rs b/game/src/languages/english.rs new file mode 100644 index 0000000..d1e3ada --- /dev/null +++ b/game/src/languages/english.rs @@ -0,0 +1,135 @@ +//! Contains constants for the language "English". + +/// Constant for item `Gedrucktesteil`. +pub const GEDRUCKTESTEIL: [&str; 3] = [ + "3D-printed part", + "A 3D-printed part that can be used to repair the communication module", + "3D-gedrucktes-Teil.png", +]; +/// Constant for the item `Superglue` +pub const SUPER_GLUE: [&str; 3] = [ + "SuperGlue", + "SuperGlue can be used to repair the machines or holes", + "SuperGlue.png", +]; +/// Constant for the item `Benzin` +pub const PETROL: [&str; 3] = [ + "Petrol", + "Petrol can be used with the emergency generator to generate electricity", + "Benzin.png", +]; + +/// Constant for the resource names. +pub(crate) const RESOURCE_NAME: [&str; 3] = ["Air", "Energy", "Life"]; + +/// The text for the warning-`Popup`s that appears in the top left corner. +pub const WARNINGS: [&str; 4] = [ + "A comet is on its way!", + "The power's out!", + "A sandstorm is on its way!", + "A machine is down!", +]; +/// The text for the mars-info-`Popup`s that appears in the top left corner. +pub const MARS_INFO: [&str; 5] = [ + "Mars is the 4th planet in our solar system", + "Mars is one of the Earth-like planets", + "The diameter of Mars is just under 6800 km", + "The mass of Mars is about one-tenth that of Earth", + "The distance to Mars is on average 228 million km", +]; +/// The text for the nasa-info-`Popup`s that appears in the top left corner. +pub const NASA_INFO: [&str; 5] = [ + "NASA stands for: National Aeronautics and Space Administration", + "NASA was founded in 1958", + "NASA is headquartered in Washington, D.C.", + "As part of the Apollo missions, NASA succeeded in putting the first man on the moon", + " NASA has over 17,000 employees", +]; +/// The text for the game-info-`Popup`s that appears in the top left corner. +pub const GAME_INFO: [&'static str; 1] = ["Life regeneration started"]; + +/// Constants for all strings used in deathscreen +pub const AIR_STRING: &str = "too little air"; +pub const ENERGY_STRING: &str = "Cold"; +pub const AIR_AND_ENERGY_STRING: &str = "Cold and too little air"; +pub const DEATH_REASON_STRING: &str = "You died of"; +pub const ADDITIONAL_INFO_STRING: &str = "Please press ESC!"; +pub const RESUME_ERROR_STRING: &str = "You need a score first"; + +/// Constant for all strings used in `IntroScreen` +pub const INTRO_TEXT: &str = + "You are stranded on Mars and must survive.\nTo do that, you must restore \ +Hopefully, you will be able to repair the communications so you can be rescued."; + +pub const TUTORIAL_TEXT: &str = + "Move around with WASD. Interact with E.\nFor reference, you have your manual on H."; + +/// Constant for the Text used in the `Button` info +pub const BUTTON_INFO: &str = "Please press the space bar!"; + +/// Constants for all strings used in `WinningScreen` +pub const WINNING_TEXT: &str = "You've been saved!"; + +/// Constants for the events that can occur. +pub const COMETA_STRIKE: [&str; 2] = [ + "COMETA STRIKE", + "A cometa strike has hit the earth and created a hole in the wall", +]; +pub const SANDSTORM: [&str; 2] = [ + "Sandsturm", + "A sandstorm, which leads to a malfunction of the oxygen generator", +]; +pub const INFORMATIONS_POPUP_NASA: [&str; 2] = [ + "InformationspopupNASA", + "An information pop-up about NASA containing facts and information about NASA", +]; +pub const POWER_FAILURE: [&str; 2] = [ + "Power failure", + "A power failure resulting in a malfunction of the oxygen generator", +]; +pub const INFORMATIONS_POPUP_MARS: [&str; 2] = [ + "InformationspopupMars", + "An information popup about Mars containing facts and information about Mars", +]; +/// Constants for the trade conflict. +pub const TRADE_CONFLICT_POPUP: [&str; 1] = + ["The following items are missing to execute the trade:"]; +/// Constants for the `time_name`. +pub const TIME_NAME: [&str; 1] = ["Time"]; +/// Constants for the text of the button in the main menu +pub const BUTTON_TEXT: [&str; 4] = ["Continue", "New Game", "Exit", "German"]; +/// Contains all machine names as a vec of strings. +pub(crate) const MACHINE_NAMES: [&str; 7] = [ + "Oxygen generator", + "power generator", + "work machine", + "3D printer", + "communication module", + "hole", + "Hole", +]; +/// Contains the Messages that are displayed in the Handbook +pub(crate) const FIRST_MILESTONE_HANDBOOK_TEXT: [&str; 10] = [ + "- Repair the oxygen generator (top left)", + "- Repair the electricity generator (bottom left)", + "- Comets create holes in the walls", + "- Repair holes with SuperGlue", + "- In case of a power failure", + "you must restart the power generator", + "- Remember to use petrol sparingly", + "- You can stop the generator briefly,", + " if you have enough power", + "\n\n Press H to close", +]; + +pub(crate) const SECOND_MILESTONE_HANDBOOK_TEXT: [&str; 7] = [ + "- Repair the communication system (right)", + "- Send a message to be rescued", + "- Your power may still fail,", + "while you're sending the message!", + "- When you send the message,", + "you automatically win.", + "\n\n Press H to close", +]; +pub(crate) const SEND_MSG_FAILURE: &str = + "The message cannot be sent until the system is restored."; diff --git a/game/src/languages/german.rs b/game/src/languages/german.rs index 05f952f..2d3920c 100644 --- a/game/src/languages/german.rs +++ b/game/src/languages/german.rs @@ -13,7 +13,7 @@ pub const SUPER_GLUE: [&str; 3] = [ "SuperGlue.png", ]; /// Constant for the item `Benzin` -pub const BENZIN: [&str; 3] = [ +pub const PETROL: [&str; 3] = [ "Benzin", "Benzin kann mit dem Notstromgenerator verwendet werden um Strom zu generieren", "Benzin.png", @@ -68,23 +68,23 @@ pub const BUTTON_INFO: &str = "Bitte drücke die Leertaste!"; pub const WINNING_TEXT: &str = "Du wurdest gerettet!"; /// Constants for the events that can occur. -pub const KOMETENEINSCHLAG: [&str; 2] = [ +pub const COMETA_STRIKE: [&str; 2] = [ "KOMETENEINSCHLAG", "Ein KOMETENEINSCHLAG hat die Erde getroffen und hat ein Loch in der Wand erzeugt", ]; -pub const SANDSTURM: [&str; 2] = [ +pub const SANDSTORM: [&str; 2] = [ "Sandsturm", "Ein Sandsturm, welcher zu einer Störung des Sauerstoffgenerators führt", ]; -pub const INFORMATIONSPOPUP_NASA: [&str; 2] = [ +pub const INFORMATIONS_POPUP_NASA: [&str; 2] = [ "InformationspopupNASA", "Ein Informationspopup über die NASA, welches Fakten und Informationen über die NASA enthält", ]; -pub const STROMAUSFALL: [&str; 2] = [ +pub const POWER_FAILURE: [&str; 2] = [ "Stromausfall", "Ein Stromausfall, welcher zu einer Störung des Sauerstoffgenerators führt", ]; -pub const INFORMATIONSPOPUP_MARS: [&str; 2] = [ +pub const INFORMATIONS_POPUP_MARS: [&str; 2] = [ "InformationspopupMars", "Ein Informationspopup über Mars, welches Fakten und Informationen über den Mars enthält", ]; @@ -93,7 +93,7 @@ pub const TRADE_CONFLICT_POPUP: [&str; 1] = ["Es fehlen folgende Items, um den T /// Constants for the `time_name`. pub const TIME_NAME: [&str; 1] = ["Zeit"]; /// Constants for the text of the button in the main menu -pub const BUTTON_TEXT: [&str; 3] = ["Fortsetzen", "Neues Spiel", "Beenden"]; +pub const BUTTON_TEXT: [&str; 4] = ["Fortsetzen", "Neues Spiel", "Beenden", "English"]; /// Contains all machine names as a vec of strings. pub(crate) const MACHINE_NAMES: [&str; 7] = [ "Sauerstoffgenerator", @@ -127,3 +127,5 @@ pub(crate) const SECOND_MILESTONE_HANDBOOK_TEXT: [&str; 7] = [ " gewinnst du automatisch.", "\n\n Drücke H zum schließen", ]; +pub(crate) const SEND_MSG_FAILURE: &str = + "Die Nachricht kann nicht gesendet werden solange das System nicht wiederhergestellt ist"; diff --git a/game/src/languages/mod.rs b/game/src/languages/mod.rs index 789e328..063ed7b 100644 --- a/game/src/languages/mod.rs +++ b/game/src/languages/mod.rs @@ -1 +1,75 @@ +pub(crate) mod english; pub(crate) mod german; + +#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] +pub enum Lang { + De, + En, +} + +impl Default for Lang { + fn default() -> Self { + Self::En + } +} + +macro_rules! t { + ($lng: expr, $target: ident) => { + match $lng { + Lang::En => &english::$target, + Lang::De => &german::$target, + } + }; + + ($name: ident, $len: expr, $target: ident) => { + pub fn $name(lng: Lang) -> &'static [&'static str; $len] { + t!(lng, $target) + } + }; + + ($name: ident => $target: ident) => { + pub fn $name(lng: Lang) -> &'static str { + t!(lng, $target) + } + }; +} + +t!(petrol, 3, PETROL); +t!(printed_part, 3, GEDRUCKTESTEIL); +t!(super_glue, 3, SUPER_GLUE); +t!(machine_names, 7, MACHINE_NAMES); +t!(game_info, 1, GAME_INFO); +t!(mars_info, 5, MARS_INFO); +t!(nasa_info, 5, NASA_INFO); +t!(warnings, 4, WARNINGS); +t!(button_text, 4, BUTTON_TEXT); +t!(trade_conflict_popup, 1, TRADE_CONFLICT_POPUP); +t!( + first_milestone_handbook_text, + 10, + FIRST_MILESTONE_HANDBOOK_TEXT +); +t!( + second_milestone_handbook_text, + 7, + SECOND_MILESTONE_HANDBOOK_TEXT +); +t!(time_name, 1, TIME_NAME); +t!(informations_popup_mars, 2, INFORMATIONS_POPUP_MARS); +t!(informations_popup_nasa, 2, INFORMATIONS_POPUP_NASA); +t!(sandstorm, 2, SANDSTORM); +t!(cometa_strike, 2, COMETA_STRIKE); +t!(power_failure, 2, POWER_FAILURE); +t!(resource_name, 3, RESOURCE_NAME); + +t!(button_info => BUTTON_INFO); +t!(winning_text => WINNING_TEXT); +t!(additional_info_string => ADDITIONAL_INFO_STRING); +t!(resume_error_string => RESUME_ERROR_STRING); +t!(air_string => AIR_STRING); +t!(energy_string => ENERGY_STRING); +t!(air_and_energy_string => AIR_AND_ENERGY_STRING); +t!(death_reason_string => DEATH_REASON_STRING); +t!(intro_text => INTRO_TEXT); +t!(tutorial_text => TUTORIAL_TEXT); +t!(send_msg_failure => SEND_MSG_FAILURE); diff --git a/game/src/machines/machine.rs b/game/src/machines/machine.rs index 2d1c229..1422464 100644 --- a/game/src/machines/machine.rs +++ b/game/src/machines/machine.rs @@ -7,7 +7,7 @@ use crate::backend::utils::is_colliding; use crate::game_core::item::Item; use crate::game_core::player::Player; use crate::game_core::resources::Resources; -use crate::languages::german::TRADE_CONFLICT_POPUP; +use crate::languages::*; use crate::machines::machine::State::{Broken, Idle, Running}; use crate::machines::machine_sprite::MachineSprite; use crate::machines::trade::Trade; @@ -77,6 +77,7 @@ pub struct Machine { #[serde(skip)] /// Needed to send Messages to the `Screenstack` to make changes to the screen screen_sender: Option>, + asset_name: String, } impl Machine { @@ -90,19 +91,20 @@ impl Machine { /// * 'Machine' fn new( name: String, - hitbox: Rect, + asset_name: String, + hit_box: Rect, trades: Vec, running_resources: Resources, ) -> Self { info!("Creating new machine: name: {}", name); Self { name, - hitbox, + hitbox: hit_box, interaction_area: Rect { - x: hitbox.x - PLAYER_INTERACTION_RADIUS, - y: hitbox.y - PLAYER_INTERACTION_RADIUS, - w: hitbox.w + (PLAYER_INTERACTION_RADIUS * 2.), - h: hitbox.h + (PLAYER_INTERACTION_RADIUS * 2.), + x: hit_box.x - PLAYER_INTERACTION_RADIUS, + y: hit_box.y - PLAYER_INTERACTION_RADIUS, + w: hit_box.w + (PLAYER_INTERACTION_RADIUS * 2.), + h: hit_box.h + (PLAYER_INTERACTION_RADIUS * 2.), }, state: Broken, sprite: None, @@ -113,18 +115,29 @@ impl Machine { time_change: 0, sender: None, screen_sender: None, + asset_name, } } + pub fn asset_name(&self) -> &str { + &self.asset_name + } + /// Alternative new constructor for the machine using one parameter Tupel /// # Arguments /// * `(name, hit_box, trades, running_resources)` - Tupel containing the same arguments as `new()` /// # Returns /// * 'Machine' pub(crate) fn new_by_const( - (name, hit_box, trades, running_resources): (String, Rect, Vec, Resources), + (name, asset_name, hit_box, trades, running_resources): ( + String, + String, + Rect, + Vec, + Resources, + ), ) -> Self { - Machine::new(name, hit_box, trades, running_resources) + Self::new(name, asset_name, hit_box, trades, running_resources) } /// initialises the Maschine with the data that is not Serialize @@ -183,7 +196,7 @@ impl Machine { /// Handel's the interaction of the Maschine and the player /// # Arguments /// * `player` - of type `& Player` is a reference to the player - pub(crate) fn interact(&mut self, player: &Player) -> RLResult { + pub(crate) fn interact(&mut self, player: &Player, lng: Lang) -> RLResult { // Check if there is a possible trade let trade = match self.trades.iter().find(|t| t.initial_state == self.state) { Some(t) => t.clone(), @@ -211,7 +224,7 @@ impl Machine { dif.iter() .map(|(item, amount)| format!("*{} {}\n", amount * -1, item.name)) .for_each(|x| missing_items.push_str(&x)); - let popup = Popup::info(format!("{}\n{missing_items}", TRADE_CONFLICT_POPUP[0])); + let popup = Popup::info(format!("{}\n{missing_items}", trade_conflict_popup(lng)[0])); info!( "Popup for Trade conflict sent: Missing Items: {}", missing_items diff --git a/game/src/main.rs b/game/src/main.rs index 3404896..58c8020 100644 --- a/game/src/main.rs +++ b/game/src/main.rs @@ -24,15 +24,18 @@ mod machines; mod main_menu; use crate::backend::constants::SCREEN_RESOLUTION; -use crate::backend::{error, screen::Screenstack}; +use crate::backend::{error, screen::ScreenStack}; use chrono::Local; +use crate::languages::Lang; #[cfg_attr(debug_assertions, allow(unused_imports))] use ggez::conf::FullscreenType; use ggez::{event, Context}; use std::fs::File; use std::sync::Mutex; -use tracing::{info, Level}; +use tracing::info; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::util::SubscriberInitExt; /// Our own Result Type for custom Error handling. pub type RLResult = Result; @@ -54,18 +57,24 @@ pub fn main() -> RLResult { } let filename = format!("logs/RL-{}.log", Local::now().format("%Y-%m-%d_%H-%M-%S")); let log_file = File::create(filename)?; - let subscriber = tracing_subscriber::fmt() - .with_max_level(Level::TRACE) + let log_file = tracing_subscriber::fmt::layer() .with_writer(Mutex::new(log_file)) - .with_ansi(false) - .finish(); - tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); + .with_ansi(false); + + tracing_subscriber::registry() + .with(tracing_subscriber::EnvFilter::from_default_env()) + .with(log_file) + .with(tracing_subscriber::fmt::layer()) + .init(); + + // tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); // End logging info!("Starting Red Life"); let (mut ctx, event_loop) = cb.build()?; info!("New Event Loop created"); window_setup(&mut ctx)?; - let screen_stack = Screenstack::default(); + let lng = Lang::En; + let screen_stack = ScreenStack::new_with_lang(lng); event::run(ctx, event_loop, screen_stack); } /// Sets the window size to resizeable in debug mode and fullscreen mode for release mode diff --git a/game/src/main_menu/button.rs b/game/src/main_menu/button.rs index 0d3b725..faafbed 100644 --- a/game/src/main_menu/button.rs +++ b/game/src/main_menu/button.rs @@ -1,5 +1,5 @@ use crate::backend::utils::{get_draw_params, get_scale}; -use crate::main_menu::mainmenu::Message; +use crate::main_menu::main_menu::Message; use crate::{draw, RLResult}; use ggez::glam::f32::Vec2; use ggez::graphics::{Canvas, Color, Text, TextFragment}; diff --git a/game/src/main_menu/mainmenu.rs b/game/src/main_menu/main_menu.rs similarity index 66% rename from game/src/main_menu/mainmenu.rs rename to game/src/main_menu/main_menu.rs index 83af6bc..4669ed0 100644 --- a/game/src/main_menu/mainmenu.rs +++ b/game/src/main_menu/main_menu.rs @@ -5,12 +5,11 @@ use crate::backend::{ utils::get_scale, }; use crate::main_menu::button::Button; -use crate::main_menu::mainmenu::Message::{Exit, NewGame, Resume}; use crate::RLResult; use crate::backend::screen::Popup; use crate::game_core::infoscreen::InfoScreen; -use crate::languages::german::{BUTTON_TEXT, RESUME_ERROR_STRING}; +use crate::languages::*; use ggez::{graphics, Context}; use std::sync::mpsc::{channel, Receiver, Sender}; @@ -20,6 +19,7 @@ pub enum Message { Exit, NewGame, Resume, + ChangeLanguage, } /// Main menu screen of the game with buttons to start a new game, load a game or exit the game. @@ -27,8 +27,10 @@ pub enum Message { pub struct MainMenu { buttons: Vec