From 19b6905799eb08ef3a9a13c57b7fc1c7cb4a798a Mon Sep 17 00:00:00 2001 From: Orlando Valverde Date: Thu, 22 Dec 2022 13:40:47 -0600 Subject: [PATCH] Add limit scene --- assets/images/instructions.png | Bin 3476 -> 3617 bytes src/resources/input/action.rs | 7 +++++ src/resources/input/mod.rs | 1 + src/resources/level/mod.rs | 5 ++-- src/resources/save_file/mod.rs | 20 ++++++++++---- src/resources/scene.rs | 6 +++++ src/resources/sounds.rs | 4 ++- src/resources/state.rs | 2 ++ src/scenes/editor/mod.rs | 10 +++++++ src/scenes/limit/mod.rs | 46 +++++++++++++++++++++++++++++++++ src/scenes/limit/ui.rs | 30 +++++++++++++++++++++ src/scenes/mod.rs | 2 ++ src/scenes/passed/mod.rs | 4 ++- src/scenes/selection/mod.rs | 31 ++++++++++++++++++++-- src/scenes/selection/ui.rs | 19 +++++++++----- 15 files changed, 170 insertions(+), 17 deletions(-) create mode 100644 src/scenes/limit/mod.rs create mode 100644 src/scenes/limit/ui.rs diff --git a/assets/images/instructions.png b/assets/images/instructions.png index 116ceb087f1262043b1dbe4ab04f2ebf9675eab5..49edd4491523bf9ee788c8ffe286e1541693cce1 100644 GIT binary patch literal 3617 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 literal 3476 zcmcH+2~ZPRHViY)0HQM@s2l-7ga!c>jDQ>i3JF)#1dt#E)Cmwy3CNX5#sd`PHrxTk zgaC2`!!1EZ85DyAL4%?QazsEmOmO8e!gk{B)=ceg)ohj9RsH_!cfbE$_v@c@1PPay zRhET7Ao31~LnsL38x8~_W&eXTNIBH+;0!KO5h%DVq=u_H4L*G9Z{uPEf!s=y6P@@D zeBOG*+0|}sZLRTPZ%qfN3W-cEI9F0t+HeQNVIL132A3ZqQAji>BfcQR7uql&P3ANL z69Iv2xGTP;nlplCA&~U~heI~5G1%ExvzgRJCHv7GMM?8(sTUsW*V~l;q>=9LZ|OFt zq;4x)H)F12O>K{tHca13)xeqBa8946jD8Dr$eM)Fz5oS8n ze%-*Bi}snR3$&)_M>)O%N7+DaJ>=%2SrPYulpBfgfF0ce0*(l8KCg&%u&M6i%E&8~ z9j><~VA|NyD(uXAnU1Qz@BbyyuPWmXi=>%$rFAA88nn56=R1Xkp68w2z(jyBzzqcr zqBD0DchSZSiPQ(moy@A-6ay@BJNz2FGE?ZGbr!dL2&x!8IBqeZ8;|P@4i*q(&GIcg zvo=4FBWe!U-X`9%#HTHI(3Y}7kdLm_-g5#bJcL~vn(_t}1c|&?S7#l**xluw9=pjs z21rnKf)0+tjQ^y}d$z;d8@V-7U;VKN^9sYC3 z)B0)!_{!L>80`e@EYr(jUF&=P6gI-A(g{WVE%_Mga>cGjonR+%k`^aIVdK9L*%sSJ zJcrN8i#PC4XHsb^sWi@MQ)t7JsG6W1AN^9so@sjBm7S+w_qZ|kJJzq^c;@NK!o~=1 zty}xSom3i?b&t{c-}~UR=xoB9F&JvjKBTC0u=y&5D1%vzP%>CN$SL&F%ga8X$w$^? zSu|DYyoRFYCQd#q!C6WzrW17N7Ro@hEr*uz@2C*q`?Sa7>n~6aZ094b=oVR`5*%vo zBIQ6unMICQSe}G_6R4aB?4}RN0MRHXjN13+bvB$r&~O&TEu0<^Bw?_MS<1-Bs04{Z z6x#i69{2bcMhl68tr#N8HQ<^>K zLKyH@P_q9JDEE*uk(J$H@+D=T9M28c?tJ)m2;3Xe*-N{OmUi;3K>y77l>Dm~DJemAJudPEV(hA20wb}<&X%(dSo?(@oGv_U{|}Ue z44h=1a)3H5cjQX}D)eB|qwx7KCC4J?Wpplj{`Jc>LDrsq(>NJ4Ioig-#j z@b<1RWw)D%oz3W!W`9!%%uJR$1l`Ww$6&X@B;=i?nSSPOvfOHtZ4=vmU zN{T1y=CmURPOfQWRE>k-fYI$NEvliDmOfnvaB86$pzi$>D098~iE`S#o)!cspi|0- zJhGH~idq?7u^p#IK@4i(#t)_z6Gc=)Ze?HUIA!0kjX11g0=2Tg_H?=sehCO_z9df9 zYSL5}ml7p0YitH>HrpZt<|Vo&CRlF+RA{TiO$Q<}(ph^-!%Hi6hdlS%K@T2UK9C6) z7wLXsdrSP&FQoaTWJxUn`Onb>+@q=H=96<`jx%0FOdve%GmDOAHTun0T;CB; z(v!A`SpK{Gl8B!&{*N=_D~_ALZfeYw-_CqI^dKNF!enue=Nr|Yv&q?J@CbDpj@vhM zvxD+CYMsfa?Vc06dg3oDnK{*|->~EM&s++~s55DOqsxDPnamCJSX6@X-mcYGT%Z_L zuYBnCMORZp95ea_C(^86;fOls8mY)pkyD-5wdBrV&<@NKF z10Wk=c1jG!zkhngr9qRQECK3A^kKX!6k`d9|GE0Pt3HY=0ZUR1C(+f343U%2*Q{y3 zY{!=21f_y##f=N@CLXg(uhi2A8i)^H)?u0UtzGkebtS%sI4>RzN8Q)}1SRmS*?075 z`-c+-`fo3Un>V$<<+28T~;H_FgUYu}a1vM2d)PUc|MujuZu?x!m{>>&EAEkt` zQqgx>qvCm&z<3%HLT!6>*KwXV3g!3P)ZgYlP(Rn@BFL9YQnjpPz z146VYfXS_V4N+6X^5Vhn;SPr9=VI6yig2D8^xt2b-MH8O|M^!_l_AQ>J-VbVNNx#S z*Ne&aSjV^5dr7*KEk}GDZr(s@x4oLcGkHNYh^k+Y{qdPhaplqL-T3DdQ%FlAqfPK) zCU<0`qtjHYmFGyisy>^oBD?ZV!Z4HWyJQeTS-4$;Y z*y@E9E=;NUwc)o00T|+c{ejKWaxC=!@iW!igWhUL!vfv zsAW9DOOyQeS!6TRZqzho#P?bE`0S$Gn@%0#O9buxHbd^CmPvjC!>4a$h;aB?hf0}$ z&zMGGe*2j{DR1U~OI6>L(^b6UmV6^d1Z~Aa!`gjz8(9s$v{z2(Jss}HwKQUu`Y7`w z%A)$CPQ^!>zfd3Sg@@!+rk%HlyMzf-UZ>1JFD%N9p+=33ZmaPty8~7bix&7WUJs(! zB{g*Y;lh1sNCPsDb>u0i;QoZOtVL=U#$~d7J&5iqA#(*~*3M3D1JACX)raqY8}5ns z+tR0zW}-u~1elv=(oAYQc2CNC8nX4&%Fu!_lJGmfyR|X3>xE{-5zkHWR_}BQac2M~1U+zDiQntjet!`YMn>1* ActionInputEvent { + ActionInputEvent { + value: ActionInput::Delete, + } + } + pub fn exit() -> ActionInputEvent { ActionInputEvent { value: ActionInput::Exit, diff --git a/src/resources/input/mod.rs b/src/resources/input/mod.rs index 9bbfc37..c48e9f9 100644 --- a/src/resources/input/mod.rs +++ b/src/resources/input/mod.rs @@ -36,6 +36,7 @@ fn gather_input( KeyCode::Escape => action_event_writer.send(ActionInputEvent::exit()), KeyCode::Space => action_event_writer.send(ActionInputEvent::select()), KeyCode::Return => action_event_writer.send(ActionInputEvent::toggle()), + KeyCode::Delete => action_event_writer.send(ActionInputEvent::delete()), _ => (), }; } diff --git a/src/resources/level/mod.rs b/src/resources/level/mod.rs index 980cc3b..9a72d06 100644 --- a/src/resources/level/mod.rs +++ b/src/resources/level/mod.rs @@ -13,7 +13,7 @@ pub mod prelude { pub use super::map::{MapEntity, MapPosition}; pub use super::record::LevelRecord; pub use super::state::LevelState; - pub use super::{Level, TOTAL_STOCK_LEVELS}; + pub use super::{Level, TOTAL_CUSTOM_LEVELS, TOTAL_STOCK_LEVELS}; } use std::time::Duration; @@ -34,6 +34,7 @@ use self::{ use super::prelude::*; pub const TOTAL_STOCK_LEVELS: usize = 16; +pub const TOTAL_CUSTOM_LEVELS: usize = 16; pub struct Plugin; @@ -48,7 +49,7 @@ pub fn insert_custom_level_handles( mut level_handles: ResMut, asset_server: Res, ) { - for (_, (key, _)) in save_file.enumerated_custom_records() { + for (_, (key, _)) in save_file.ordered_custom_records() { let split_key: Vec<&str> = key.split('$').collect(); let uuid = Uuid::parse_str(split_key[1]).expect("Cannot parse uuid"); let path = format!("levels/custom/{}.lvl", &split_key[1]); diff --git a/src/resources/save_file/mod.rs b/src/resources/save_file/mod.rs index 0aa0b52..0318b08 100644 --- a/src/resources/save_file/mod.rs +++ b/src/resources/save_file/mod.rs @@ -1,9 +1,9 @@ mod handle; -use std::{env, fs::File, io::Write, iter::Enumerate, path::PathBuf, slice::Iter}; +use std::{env, fs::File, io::Write, iter::Enumerate, path::PathBuf, slice::Iter, vec::IntoIter}; use bevy::{asset::LoadState, prelude::*, reflect::TypeUuid}; -use hashbrown::{hash_map, HashMap}; +use hashbrown::HashMap; use ron::ser as serialize_ron; use serde::{Deserialize, Serialize}; @@ -95,6 +95,10 @@ impl SaveFile { self.custom_records.insert(key, level_record); } + pub fn delete_custom_level_record(&mut self, key: &str) { + self.custom_records.remove(key); + } + pub fn unlock_new_level(&mut self, level: &Level) { if let LevelKind::Stock(index) = level.kind { let unlocked_levels = self.unlocked_levels(); @@ -116,7 +120,7 @@ impl SaveFile { self.stock_records.len() } - pub fn total_custom_levels(&self) -> usize { + pub fn number_custom_levels(&self) -> usize { self.custom_records.len() } @@ -124,7 +128,13 @@ impl SaveFile { self.stock_records.iter().enumerate() } - pub fn enumerated_custom_records(&self) -> Enumerate> { - self.custom_records.iter().enumerate() + pub fn ordered_custom_records(&self) -> Enumerate> { + let mut x = self + .custom_records + .iter() + .collect::>(); + x.sort_by(|(a_key, _), (b_key, _)| a_key.cmp(b_key)); + + x.into_iter().enumerate() } } diff --git a/src/resources/scene.rs b/src/resources/scene.rs index dfdd0bf..096af00 100644 --- a/src/resources/scene.rs +++ b/src/resources/scene.rs @@ -29,6 +29,12 @@ impl SceneTransitionEvent { } } + pub fn limit() -> Self { + Self { + state: GameState::Limit, + } + } + pub fn options() -> Self { Self { state: GameState::Options, diff --git a/src/resources/sounds.rs b/src/resources/sounds.rs index 901eb22..9cbb669 100644 --- a/src/resources/sounds.rs +++ b/src/resources/sounds.rs @@ -107,7 +107,9 @@ fn play_music( music .play(match game_state.0 { GameState::Title | GameState::Instructions => sounds.music_title.clone(), - GameState::Selection { .. } | GameState::Options => sounds.music_selection.clone(), + GameState::Selection { .. } | GameState::Options | GameState::Limit => { + sounds.music_selection.clone() + } GameState::Level | GameState::Editor => sounds.music_level.clone(), GameState::Win | GameState::Passed => sounds.music_win.clone(), GameState::Loading => return, diff --git a/src/resources/state.rs b/src/resources/state.rs index 4db9d90..56cd583 100644 --- a/src/resources/state.rs +++ b/src/resources/state.rs @@ -21,6 +21,7 @@ impl SelectionKind { } } + #[must_use] pub fn toggle(&self) -> Self { match self { Self::Stock => Self::Custom, @@ -35,6 +36,7 @@ pub enum GameState { Title, Instructions, Editor, + Limit, Passed, Options, Selection { kind: SelectionKind }, diff --git a/src/scenes/editor/mod.rs b/src/scenes/editor/mod.rs index 047fe4f..6a114cf 100644 --- a/src/scenes/editor/mod.rs +++ b/src/scenes/editor/mod.rs @@ -19,6 +19,7 @@ impl BevyPlugin for Plugin { .add_enter_system_set( GameState::Editor, SystemSet::new() + .with_system(check_total_custom_levels) .with_system(self::ui::spawn) .with_system(Brush::insert) .with_system(setup_level), @@ -49,6 +50,15 @@ impl BevyPlugin for Plugin { } } +fn check_total_custom_levels( + save_file: Res, + mut scene_transition_event_writer: EventWriter, +) { + if save_file.number_custom_levels() == TOTAL_CUSTOM_LEVELS { + scene_transition_event_writer.send(SceneTransitionEvent::limit()); + } +} + fn setup_level( mut commands: Commands, images: Res, diff --git a/src/scenes/limit/mod.rs b/src/scenes/limit/mod.rs new file mode 100644 index 0000000..436d003 --- /dev/null +++ b/src/scenes/limit/mod.rs @@ -0,0 +1,46 @@ +mod ui; + +use bevy::{app::Plugin as BevyPlugin, prelude::*}; +use bevy_kira_audio::{AudioChannel, AudioControl}; +use iyes_loopless::prelude::*; + +use crate::{resources::prelude::*, ui::OverlayMarker}; + +pub struct Plugin; + +impl BevyPlugin for Plugin { + fn build(&self, app: &mut App) { + app.add_enter_system(GameState::Limit, self::ui::spawn) + .add_system_set( + ConditionSet::new() + .run_in_state(GameState::Limit) + .with_system(handle_input.run_on_event::()) + .with_system(play_sfx.run_on_event::()) + .into(), + ) + .add_exit_system(GameState::Limit, cleanup::); + } +} + +fn handle_input( + mut game_state_event_writer: EventWriter, + mut action_event_reader: EventReader, +) { + for action_event in action_event_reader.iter() { + if matches!(action_event.value, ActionInput::Select) { + game_state_event_writer.send(SceneTransitionEvent::selection(SelectionKind::Custom)); + } + } +} + +fn play_sfx( + mut action_event_reader: EventReader, + sounds: Res, + sfx: Res>, +) { + for action_event in action_event_reader.iter() { + if matches!(action_event.value, ActionInput::Select) { + sfx.play(sounds.sfx_push_box.clone()); + } + } +} diff --git a/src/scenes/limit/ui.rs b/src/scenes/limit/ui.rs new file mode 100644 index 0000000..7372d91 --- /dev/null +++ b/src/scenes/limit/ui.rs @@ -0,0 +1,30 @@ +use bevy::prelude::*; + +use crate::{ + resources::prelude::*, + ui::{Container, GameText, Overlay, SimpleText}, +}; + +pub fn spawn(mut commands: Commands, fonts: Res) { + let font = fonts.primary(); + + let overlay = Overlay::default(); + let mut center = Container::height(140.0); + + let mut reached_limit = + SimpleText::medium("You reached the limit\nfor the custom levels", font); + let press_button = SimpleText::small( + "Press SPACE to continue to the custom level selection", + font, + ); + + center.justify_between(); + reached_limit.primary(); + + overlay.spawn(&mut commands, |parent| { + center.spawn(parent, |parent| { + reached_limit.spawn(parent); + press_button.spawn(parent); + }); + }); +} diff --git a/src/scenes/mod.rs b/src/scenes/mod.rs index 0830401..c4d2f7a 100644 --- a/src/scenes/mod.rs +++ b/src/scenes/mod.rs @@ -1,6 +1,7 @@ mod editor; mod instructions; mod level; +mod limit; mod options; mod passed; mod selection; @@ -16,6 +17,7 @@ impl BevyPlugin for Plugin { app.add_plugin(title::Plugin) .add_plugin(instructions::Plugin) .add_plugin(editor::Plugin) + .add_plugin(limit::Plugin) .add_plugin(passed::Plugin) .add_plugin(options::Plugin) .add_plugin(selection::Plugin) diff --git a/src/scenes/passed/mod.rs b/src/scenes/passed/mod.rs index 5e41f50..27b3b64 100644 --- a/src/scenes/passed/mod.rs +++ b/src/scenes/passed/mod.rs @@ -144,8 +144,10 @@ pub fn handle_text_input( level_handles.insert_custom(uuid, asset_server.load(&levels_path)); + let lower_level_name = level_name.to_lowercase(); + save_file.insert_custom_level_record( - format!("{}${uuid}", *level_name), + format!("{lower_level_name}${uuid}"), level.get_set_record(), ); diff --git a/src/scenes/selection/mod.rs b/src/scenes/selection/mod.rs index ab342ee..c5c37d0 100644 --- a/src/scenes/selection/mod.rs +++ b/src/scenes/selection/mod.rs @@ -1,5 +1,7 @@ mod ui; +use std::{env, fs::remove_file, path::PathBuf}; + use bevy::{app::Plugin as BevyPlugin, prelude::*}; use bevy_kira_audio::{AudioChannel, AudioControl}; use iyes_loopless::prelude::*; @@ -51,7 +53,7 @@ fn handle_direction_input( let max_value = if is_stock { save_file.unlocked_levels() } else { - save_file.total_custom_levels() + save_file.number_custom_levels() }; selected_index = Some(if index < max_value { @@ -83,6 +85,7 @@ fn handle_action_input( mut scene_transition_event_writer: EventWriter, mut query: Query<&mut GameButtonData>, mut action_event_reader: EventReader, + mut save_file: ResMut, game_state: Res>, ) { let is_stock = game_state.0.get_selection_kind().is_stock(); @@ -114,6 +117,30 @@ fn handle_action_input( SelectionKind::Stock })); } + + ActionInput::Delete => { + for button in query.iter_mut() { + if button.selected && !is_stock { + let payload = button + .payload + .clone() + .expect("The button payload was empty"); + let parsed_payload: Vec<&str> = payload.split('$').collect(); + save_file.delete_custom_level_record(&payload); + save_file.save(); + let levels_path = format!("levels/custom/{}.lvl", parsed_payload[1]); + let assets_path = format!("assets/{}", &levels_path); + let path = if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") { + PathBuf::from(manifest_dir).join(assets_path) + } else { + PathBuf::from(assets_path) + }; + remove_file(path).expect("File cannot be removed"); + scene_transition_event_writer + .send(SceneTransitionEvent::selection(SelectionKind::Custom)); + } + } + } ActionInput::Exit => { scene_transition_event_writer.send(SceneTransitionEvent::title()); } @@ -129,7 +156,7 @@ fn play_action_sfx( ) { for action_event in action_event_reader.iter() { match action_event.value { - ActionInput::Exit => { + ActionInput::Exit | ActionInput::Delete => { sfx.play(sounds.sfx_push_box.clone()); } ActionInput::Toggle => { diff --git a/src/scenes/selection/ui.rs b/src/scenes/selection/ui.rs index f0413f3..fec5b78 100644 --- a/src/scenes/selection/ui.rs +++ b/src/scenes/selection/ui.rs @@ -33,7 +33,7 @@ fn spawn_stock_buttons(parent: &mut ChildBuilder, save_file: &SaveFile, font: &H } fn spawn_custom_buttons(parent: &mut ChildBuilder, save_file: &SaveFile, font: &Handle) { - for (index, (key, record)) in save_file.enumerated_custom_records() { + for (index, (key, record)) in save_file.ordered_custom_records() { let housing = Container::size_percentage(25.0, 25.0); let split_key: Vec<&str> = key.split('$').collect(); let mut button = GameButton::new(split_key[0], font); @@ -46,7 +46,7 @@ fn spawn_custom_buttons(parent: &mut ChildBuilder, save_file: &SaveFile, font: & }; button.id(index); - button.payload(key.clone()); + button.payload(key.clone().clone()); if index == 0 { button.selected(); @@ -70,14 +70,15 @@ pub fn spawn( let overlay = Overlay::extended(); let top = Container::auto_height(); let mut middle = Container::default(); - let bottom = Container::auto_height(); + let mut bottom = Container::auto_height(); let kind = game_state.0.get_selection_kind(); let mut title = SimpleText::medium(format!("Select a {} Level", kind.to_str()), font); - let press_button = SimpleText::small( - format!("Press ENTER to switch to {} levels", kind.toggle().to_str()), + let mut enter = SimpleText::small( + format!("(ENTER) - Switch to {} levels", kind.toggle().to_str()), font, ); + let mut delete = SimpleText::small("(DELETE) - Remove a custom level", font); title.primary(); middle @@ -87,6 +88,11 @@ pub fn spawn( .items_start() .content_start(); + enter.primary(); + delete.primary(); + + bottom.row().justify_between(); + overlay.spawn(&mut commands, |parent| { top.spawn(parent, |parent| { title.spawn(parent); @@ -99,7 +105,8 @@ pub fn spawn( } }); bottom.spawn(parent, |parent| { - press_button.spawn(parent); + enter.spawn(parent); + delete.spawn(parent); }); }); }