From dfcf2e2f3f1ae37adaf07b867cc1674ef10c86e0 Mon Sep 17 00:00:00 2001 From: dawnDus <96957561+dawndus@users.noreply.github.com> Date: Sat, 12 Feb 2022 13:32:48 -0500 Subject: [PATCH] Initial jukebox scene --- src/engine_constants/mod.rs | 1 + src/input/combined_menu_controller.rs | 24 ++- src/scene/jukebox_scene.rs | 277 ++++++++++++++++++++++++++ src/scene/mod.rs | 1 + src/scene/title_scene.rs | 9 + 5 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 src/scene/jukebox_scene.rs diff --git a/src/engine_constants/mod.rs b/src/engine_constants/mod.rs index 9729dcc..36aeebb 100644 --- a/src/engine_constants/mod.rs +++ b/src/engine_constants/mod.rs @@ -1667,6 +1667,7 @@ impl EngineConstants { self.tex_sizes.insert("bkMoon".to_owned(), (427, 240)); self.tex_sizes.insert("bkFog".to_owned(), (427, 240)); self.tex_sizes.insert("ui".to_owned(), (128, 32)); + self.tex_sizes.insert("uimusic".to_owned(), (192, 144)); self.title.logo_rect = Rect { left: 0, top: 0, right: 214, bottom: 62 }; self.inventory_dim_color = Color::from_rgba(0, 0, 32, 150); self.textscript.encoding = TextScriptEncoding::UTF8; diff --git a/src/input/combined_menu_controller.rs b/src/input/combined_menu_controller.rs index 874d0d9..38ce604 100644 --- a/src/input/combined_menu_controller.rs +++ b/src/input/combined_menu_controller.rs @@ -9,9 +9,7 @@ pub struct CombinedMenuController { impl CombinedMenuController { pub fn new() -> CombinedMenuController { - CombinedMenuController { - controllers: Vec::new(), - } + CombinedMenuController { controllers: Vec::new() } } pub fn update(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { @@ -91,4 +89,24 @@ impl CombinedMenuController { false } + + pub fn trigger_shift_left(&self) -> bool { + for cont in &self.controllers { + if cont.trigger_prev_weapon() { + return true; + } + } + + false + } + + pub fn trigger_shift_right(&self) -> bool { + for cont in &self.controllers { + if cont.trigger_next_weapon() { + return true; + } + } + + false + } } diff --git a/src/scene/jukebox_scene.rs b/src/scene/jukebox_scene.rs new file mode 100644 index 0000000..8e9b48b --- /dev/null +++ b/src/scene/jukebox_scene.rs @@ -0,0 +1,277 @@ +use itertools::Itertools; + +use crate::common::Color; +use crate::common::Rect; +use crate::components::background::Background; +use crate::frame::Frame; +use crate::framework::context::Context; +use crate::framework::error::GameResult; +use crate::framework::filesystem; +use crate::input::combined_menu_controller::CombinedMenuController; +use crate::input::touch_controls::TouchControlType; +use crate::map::Map; +use crate::scene::title_scene::TitleScene; +use crate::scene::Scene; +use crate::shared_game_state::{SharedGameState, TileSize}; +use crate::stage::{BackgroundType, NpcType, Stage, StageData, StageTexturePaths, Tileset}; +pub struct JukeboxScene { + selected_song: u16, + song_list: Vec, + soundtracks: Vec, + controller: CombinedMenuController, + background: Background, + frame: Frame, + stage: Stage, + textures: StageTexturePaths, +} + +impl JukeboxScene { + pub fn new() -> JukeboxScene { + let fake_stage = Stage { + map: Map { width: 0, height: 0, tiles: vec![], attrib: [0; 0x100], tile_size: TileSize::Tile16x16 }, + data: StageData { + name: "".to_string(), + map: "".to_string(), + boss_no: 0, + tileset: Tileset { name: "0".to_string() }, + pxpack_data: None, + background: crate::stage::Background::new("bkMoon"), + background_type: BackgroundType::Outside, + background_color: Color { r: 0.0, g: 0.0, b: 0.0, a: 0.0 }, + npc1: NpcType::new("0"), + npc2: NpcType::new("0"), + }, + }; + + let mut textures = StageTexturePaths::new(); + textures.update(&fake_stage); + + JukeboxScene { + selected_song: 0, + song_list: Vec::new(), + soundtracks: Vec::new(), + controller: CombinedMenuController::new(), + background: Background::new(), + frame: Frame::new(), + stage: fake_stage, + textures, + } + } +} + +impl Scene for JukeboxScene { + fn init(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { + self.controller.add(state.settings.create_player1_controller()); + self.controller.add(state.settings.create_player2_controller()); + + self.song_list = state.constants.music_table.iter().filter(|song| !song.contains("fanfale")).cloned().collect(); + + let mut soundtrack_entries = state.constants.soundtracks.keys().map(|s| s.to_owned()).collect_vec(); + soundtrack_entries.push("Organya".to_owned()); + + if let Ok(dir) = filesystem::read_dir(ctx, "/Soundtracks/") { + for entry in dir { + if filesystem::is_dir(ctx, &entry) { + let filename = entry.file_name().unwrap().to_string_lossy().to_string(); + + if !soundtrack_entries.contains(&filename) { + soundtrack_entries.push(filename); + } + } + } + } + + self.soundtracks = soundtrack_entries.clone(); + state.settings.soundtrack = "Organya".to_owned(); + + Ok(()) + } + + fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { + self.controller.update(state, ctx)?; + self.controller.update_trigger(); + + self.background.tick()?; + + let mut song = self.selected_song as i16 + + if self.controller.trigger_right() { + 1 + } else if self.controller.trigger_left() { + -1 + } else if self.controller.trigger_down() { + 8 + } else if self.controller.trigger_up() { + -8 + } else { + 0 + }; + + if song < 0 { + song += self.song_list.len() as i16; + } else { + song %= self.song_list.len() as i16; + }; + + self.selected_song = song as u16; + + if self.controller.trigger_ok() { + let song_id = state + .constants + .music_table + .iter() + .position(|song_comp| song_comp == &self.song_list[song as usize]) + .unwrap_or(0); + + state.sound_manager.play_song(song_id, &state.constants, &state.settings, ctx)?; + } + + if self.controller.trigger_shift_left() { + self.soundtracks.rotate_left(1); + state.settings.soundtrack = self.soundtracks.last().unwrap().to_string(); + state.sound_manager.reload_songs(&state.constants, &state.settings, ctx)?; + } + + if self.controller.trigger_shift_right() { + self.soundtracks.rotate_right(1); + state.settings.soundtrack = self.soundtracks.last().unwrap().to_string(); + state.sound_manager.reload_songs(&state.constants, &state.settings, ctx)?; + } + + if self.controller.trigger_back() { + state.next_scene = Some(Box::new(TitleScene::new())); + } + + // todo Touch controls + + Ok(()) + } + + fn draw(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { + self.background.draw(state, ctx, &self.frame, &self.textures, &self.stage)?; + + let block_size = 32.0; + let buffer = 4.0; + let init_x = (state.canvas_size.0 / 2.0) - (block_size * 4.0) - 10.0; + let init_y = (state.canvas_size.1 / 2.0) - (block_size * 2.0); + + let num_songs = self.song_list.len(); + let mut rect = Rect { left: 0_u16, top: 0, right: 0, bottom: 0 }; + + fn selected(mut rect: Rect, offset: u16) -> Rect { + rect.left += offset; + rect.right += offset; + rect + } + + // Draw Song Boxes + let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "uimusic")?; + + for iter in 0..num_songs { + rect.left = (iter as u16 % 8) * 24; + rect.top = (iter as u16 / 8) * 24; + rect.right = rect.left + 24; + rect.bottom = rect.top + 24; + + batch.add_rect( + init_x + (iter % 8) as f32 * (block_size + buffer), + init_y + (iter / 8) as f32 * (block_size + buffer), + &rect, + ); + } + + batch.draw(ctx)?; + + // Draw Selection Boxes + let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "ui")?; + + for iter in 0..num_songs { + let left = (iter as u16 % 8) as f32 * (block_size + buffer) - 4.0; + let top = (iter as u16 / 8) as f32 * (block_size + buffer) - 4.0; + let right = left + block_size - buffer; + let bottom = top + block_size - buffer; + + let selected_offset = if iter == self.selected_song as usize { 16 } else { 0 }; + + batch.add_rect( + init_x + left, + init_y + top, + &selected(state.constants.title.menu_left_top, selected_offset), + ); + batch.add_rect( + init_x + right, + init_y + top, + &selected(state.constants.title.menu_right_top, selected_offset), + ); + batch.add_rect( + init_x + left, + init_y + bottom, + &selected(state.constants.title.menu_left_bottom, selected_offset), + ); + batch.add_rect( + init_x + right, + init_y + bottom, + &selected(state.constants.title.menu_right_bottom, selected_offset), + ); + + let mut rect = state.constants.title.menu_top; + let mut rect2 = state.constants.title.menu_bottom; + let mut x = init_x + left + rect.height() as f32; + let mut y = init_y + top + rect.height() as f32; + let mut width = (block_size - buffer) as u16 - rect2.height(); + let mut height = (block_size - buffer) as u16 - rect2.height(); + + while width > 0 { + rect.right = if width >= rect.width() { + width = width.saturating_sub(rect.width()); + rect.right + } else { + let old_width = width; + width = 0; + rect.left + old_width + }; + rect2.right = rect.right; + + batch.add_rect(x, y - rect.height() as f32, &selected(rect, selected_offset)); + batch.add_rect(x, y + block_size - buffer - rect.height() as f32, &selected(rect2, selected_offset)); + x += rect.width() as f32; + } + + x = init_x + left; + rect = state.constants.title.menu_left; + rect2 = state.constants.title.menu_right; + while height > 0 { + rect.bottom = if height >= rect.height() { + height = height.saturating_sub(rect.height()); + rect.bottom + } else { + let old_height = height; + height = 0; + rect.top + old_height + }; + rect2.bottom = rect.bottom; + + batch.add_rect(x, y, &selected(rect, selected_offset)); + batch.add_rect(x + block_size - buffer, y, &selected(rect2, selected_offset)); + y += rect.height() as f32; + } + } + + batch.draw(ctx)?; + + // Write Soundtrack name + + let text = &state.settings.soundtrack; + + let width = state.font.text_width(text.chars(), &state.constants); + state.font.draw_text( + text.chars(), + ((state.canvas_size.0 - width) / 2.0).floor(), + 20.0, + &state.constants, + &mut state.texture_set, + ctx, + )?; + + Ok(()) + } +} diff --git a/src/scene/mod.rs b/src/scene/mod.rs index 2791db1..bf3fd87 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -6,6 +6,7 @@ use crate::shared_game_state::SharedGameState; #[cfg(feature = "editor")] pub mod editor_scene; pub mod game_scene; +pub mod jukebox_scene; pub mod loading_scene; pub mod no_data_scene; pub mod title_scene; diff --git a/src/scene/title_scene.rs b/src/scene/title_scene.rs index 1d11468..d109a7b 100644 --- a/src/scene/title_scene.rs +++ b/src/scene/title_scene.rs @@ -12,6 +12,7 @@ use crate::map::Map; use crate::menu::save_select_menu::SaveSelectMenu; use crate::menu::settings_menu::SettingsMenu; use crate::menu::{Menu, MenuEntry, MenuSelectionResult}; +use crate::scene::jukebox_scene::JukeboxScene; use crate::scene::Scene; use crate::shared_game_state::{MenuCharacter, SharedGameState, TileSize}; use crate::stage::{BackgroundType, NpcType, Stage, StageData, StageTexturePaths, Tileset}; @@ -149,6 +150,11 @@ impl Scene for TitleScene { } else { self.main_menu.push_entry(MenuEntry::Hidden); } + if state.constants.is_switch { + self.main_menu.push_entry(MenuEntry::Active("Jukebox".to_string())); + } else { + self.main_menu.push_entry(MenuEntry::Hidden); + } self.main_menu.push_entry(MenuEntry::Active("Quit".to_string())); self.settings_menu.init(state, ctx)?; @@ -204,6 +210,9 @@ impl Scene for TitleScene { } } MenuSelectionResult::Selected(4, _) => { + state.next_scene = Some(Box::new(JukeboxScene::new())); + } + MenuSelectionResult::Selected(5, _) => { state.shutdown(); } _ => {}