diff --git a/src/components/inventory.rs b/src/components/inventory.rs index 900809b..e825a1e 100644 --- a/src/components/inventory.rs +++ b/src/components/inventory.rs @@ -1,14 +1,30 @@ +use crate::entity::GameEntity; +use crate::inventory::Inventory; +use crate::framework::context::Context; +use crate::frame::Frame; +use crate::shared_game_state::SharedGameState; +use crate::framework::error::GameResult; -pub struct Inventory { +pub struct InventoryUI { text_y_pos: usize, tick: usize, } -impl Inventory { - pub fn new() -> Inventory { - Inventory { +impl InventoryUI { + pub fn new() -> InventoryUI { + InventoryUI { text_y_pos: 24, tick: 0, } } } + +impl GameEntity<&mut Inventory> for InventoryUI { + fn tick(&mut self, state: &mut SharedGameState, custom: &mut Inventory) -> GameResult<()> { + Ok(()) + } + + fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, frame: &Frame) -> GameResult<()> { + Ok(()) + } +} diff --git a/src/engine_constants/mod.rs b/src/engine_constants/mod.rs index 5036927..b8de0e5 100644 --- a/src/engine_constants/mod.rs +++ b/src/engine_constants/mod.rs @@ -177,6 +177,9 @@ pub struct TextScriptConsts { pub textbox_rect_bottom: Rect, pub textbox_rect_yes_no: Rect, pub textbox_rect_cursor: Rect, + pub inventory_rect_top: Rect, + pub inventory_rect_middle: Rect, + pub inventory_rect_bottom: Rect, pub get_item_top_left: Rect, pub get_item_bottom_left: Rect, pub get_item_top_right: Rect, @@ -1356,6 +1359,9 @@ impl EngineConstants { textbox_rect_bottom: Rect { left: 0, top: 16, right: 244, bottom: 24 }, textbox_rect_yes_no: Rect { left: 152, top: 48, right: 244, bottom: 80 }, textbox_rect_cursor: Rect { left: 112, top: 88, right: 128, bottom: 104 }, + inventory_rect_top: Rect { left: 0, top: 0, right: 244, bottom: 8 }, + inventory_rect_middle: Rect { left: 0, top: 8, right: 244, bottom: 16 }, + inventory_rect_bottom: Rect { left: 0, top: 16, right: 244, bottom: 24 }, get_item_top_left: Rect { left: 0, top: 0, right: 72, bottom: 16 }, get_item_bottom_left: Rect { left: 0, top: 8, right: 72, bottom: 24 }, get_item_top_right: Rect { left: 240, top: 0, right: 244, bottom: 8 }, diff --git a/src/inventory.rs b/src/inventory.rs index 658e2d4..35995fc 100644 --- a/src/inventory.rs +++ b/src/inventory.rs @@ -3,9 +3,10 @@ use std::cmp::Ordering; use crate::engine_constants::EngineConstants; use crate::shared_game_state::SharedGameState; use crate::weapon::{Weapon, WeaponLevel, WeaponType}; -use crate::player::Player; +use crate::player::{Player, TargetPlayer}; use crate::caret::CaretType; use crate::common::Direction; +use crate::weapon::bullet::BulletManager; #[derive(Clone, Copy)] /// (id, amount) @@ -183,8 +184,7 @@ impl Inventory { pub fn reset_current_weapon_xp(&mut self) { if let Some(weapon) = self.get_current_weapon_mut() { - weapon.level = WeaponLevel::Level1; - weapon.experience = 0; + weapon.reset_xp(); } } @@ -221,43 +221,14 @@ impl Inventory { pub fn add_xp(&mut self, exp: u16, player: &mut Player, state: &mut SharedGameState) { if let Some(weapon) = self.get_current_weapon_mut() { - let curr_level_idx = weapon.level as usize - 1; - let lvl_table = state.constants.weapon.level_table[weapon.wtype as usize]; - - weapon.experience += exp; - - if weapon.level == WeaponLevel::Level3 { - if weapon.experience > lvl_table[2] { - weapon.experience = lvl_table[2]; - - if player.equip.has_whimsical_star() && player.stars < 3 { - player.stars += 1; - } - } - } else if weapon.experience > lvl_table[curr_level_idx] { - weapon.level = weapon.level.next(); - weapon.experience = 0; - - if weapon.wtype != WeaponType::Spur { - state.sound_manager.play_sfx(27); - state.create_caret(player.x, player.y, CaretType::LevelUp, Direction::Left); - } - } + weapon.add_xp(exp, player, state); } } /// Get current experience state. Returns a (exp, max exp, max level/exp) tuple. pub fn get_current_max_exp(&self, constants: &EngineConstants) -> (u16, u16, bool) { if let Some(weapon) = self.weapons.get(self.current_weapon as usize) { - if weapon.level == WeaponLevel::None { - return (0, 0, false); - } - - let level_idx = weapon.level as usize - 1; - let max_exp = constants.weapon.level_table[weapon.wtype as usize][level_idx]; - let max = weapon.level == WeaponLevel::Level3 && weapon.experience == max_exp; - - (weapon.experience, max_exp, max) + weapon.get_max_exp(constants) } else { (0, 0, false) } @@ -291,6 +262,12 @@ impl Inventory { pub fn has_weapon(&self, wtype: WeaponType) -> bool { self.weapons.iter().any(|weapon| weapon.wtype == wtype) } + + pub fn tick_weapons(&mut self, state: &mut SharedGameState, player: &mut Player, player_id: TargetPlayer, bullet_manager: &mut BulletManager) { + if let Some(weapon) = self.get_current_weapon_mut() { + weapon.tick(state, player, player_id, bullet_manager); + } + } } #[test] diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index c9376bc..e7f327c 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -32,6 +32,7 @@ use crate::text_script::{ConfirmSelection, ScriptMode, TextScriptExecutionState, use crate::texture_set::SizedBatch; use crate::weapon::bullet::BulletManager; use crate::weapon::WeaponType; +use crate::components::inventory::InventoryUI; pub struct GameScene { pub tick: u32, @@ -39,6 +40,7 @@ pub struct GameScene { pub boss_life_bar: BossLifeBar, pub stage_select: StageSelect, pub flash: Flash, + pub inventory_ui: InventoryUI, pub hud_player1: HUD, pub hud_player2: HUD, pub frame: Frame, @@ -89,6 +91,7 @@ impl GameScene { boss_life_bar: BossLifeBar::new(), stage_select: StageSelect::new(), flash: Flash::new(), + inventory_ui: InventoryUI::new(), hud_player1: HUD::new(Alignment::Left), hud_player2: HUD::new(Alignment::Right), frame: Frame { @@ -1186,21 +1189,16 @@ impl GameScene { self.frame.update(state, &self.stage); if state.control_flags.control_enabled() { - #[allow(clippy::cast_ref_to_mut)] - let inventory = unsafe { &mut *(&self.inventory_player1 as *const Inventory as *mut Inventory) }; // fuck off - if let Some(weapon) = self.inventory_player1.get_current_weapon_mut() { - weapon.tick(&mut self.player1, TargetPlayer::Player1, inventory, &mut self.bullet_manager, state); - } - - #[allow(clippy::cast_ref_to_mut)] - let inventory = unsafe { &mut *(&self.inventory_player2 as *const Inventory as *mut Inventory) }; - if let Some(weapon) = self.inventory_player2.get_current_weapon_mut() { - weapon.tick(&mut self.player2, TargetPlayer::Player2, inventory, &mut self.bullet_manager, state); - } + self.inventory_player1.tick_weapons(state, &mut self.player1, TargetPlayer::Player1, &mut self.bullet_manager); + self.inventory_player2.tick_weapons(state, &mut self.player2, TargetPlayer::Player2, &mut self.bullet_manager); self.hud_player1.tick(state, (&self.player1, &mut self.inventory_player1))?; self.hud_player2.tick(state, (&self.player2, &mut self.inventory_player2))?; self.boss_life_bar.tick(state, (&self.npc_list, &self.boss))?; + + if self.player1.controller.trigger_inventory() { + state.textscript_vm.set_mode(ScriptMode::Inventory); + } } Ok(()) @@ -1356,6 +1354,7 @@ impl Scene for GameScene { match state.textscript_vm.mode { ScriptMode::Map if state.control_flags.tick_world() => self.tick_world(state)?, ScriptMode::StageSelect => self.stage_select.tick(state, (&self.player1, &self.player2))?, + ScriptMode::Inventory => self.inventory_ui.tick(state, &mut self.inventory_player1)?, _ => {} } diff --git a/src/sound/pixtone.rs b/src/sound/pixtone.rs index 7a13df0..5c84cea 100644 --- a/src/sound/pixtone.rs +++ b/src/sound/pixtone.rs @@ -17,29 +17,16 @@ lazy_static! { let mut random = [0i8; 0x100]; let ref_data = include_bytes!("pixtone_ref.dat"); + unsafe { - sine.copy_from_slice(&*(&ref_data[0..0x100] as *const [u8] as *const [i8])); - triangle.copy_from_slice(&*(&ref_data[0x100..0x200] as *const [u8] as *const [i8])); - saw_up.copy_from_slice(&*(&ref_data[0x200..0x300] as *const [u8] as *const [i8])); - saw_down.copy_from_slice(&*(&ref_data[0x300..0x400] as *const [u8] as *const [i8])); - square.copy_from_slice(&*(&ref_data[0x400..0x500] as *const [u8] as *const [i8])); - random.copy_from_slice(&*(&ref_data[0x500..0x600] as *const [u8] as *const [i8])); + sine.copy_from_slice(std::mem::transmute(&ref_data[0..0x100])); + triangle.copy_from_slice(std::mem::transmute(&ref_data[0x100..0x200])); + saw_up.copy_from_slice(std::mem::transmute(&ref_data[0x200..0x300])); + saw_down.copy_from_slice(std::mem::transmute(&ref_data[0x300..0x400])); + square.copy_from_slice(std::mem::transmute(&ref_data[0x400..0x500])); + random.copy_from_slice(std::mem::transmute(&ref_data[0x500..0x600])); } - // todo i can't get this shit right -/* - let mut seed = 0i32; - - for i in 0..255 { - seed = seed.wrapping_mul(214013).wrapping_add(2531011); - sine[i] = (64.0 * (i as f64 * std::f64::consts::PI).sin()) as i8; - triangle[i] = (if (0x40i32.wrapping_add(i as i32)) & 0x80 != 0 { 0x80i32.wrapping_sub(i as i32) } else { i as i32 }) as i8; - saw_up[i] = (-0x40i32).wrapping_add(i as i32 / 2) as i8; - saw_down[i] = (0x40i32.wrapping_sub(i as i32 / 2)) as i8; - square[i] = (0x40i32.wrapping_sub(i as i32 & 0x80)) as i8; - random[i] = (seed >> 16) as i8 / 2; - }*/ - [sine, triangle, saw_up, saw_down, square, random] }; } diff --git a/src/weapon/mod.rs b/src/weapon/mod.rs index 9c7e141..2114bc6 100644 --- a/src/weapon/mod.rs +++ b/src/weapon/mod.rs @@ -2,10 +2,10 @@ use num_derive::FromPrimitive; use crate::caret::CaretType; use crate::common::Direction; -use crate::inventory::Inventory; +use crate::engine_constants::EngineConstants; use crate::player::{Player, TargetPlayer}; use crate::shared_game_state::SharedGameState; -use crate::weapon::bullet::{Bullet, BulletManager}; +use crate::weapon::bullet::BulletManager; mod blade; mod bubbler; @@ -101,13 +101,54 @@ impl Weapon { } } + pub fn get_max_exp(&self, constants: &EngineConstants) -> (u16, u16, bool) { + if self.level == WeaponLevel::None { + return (0, 0, false); + } + + let level_idx = self.level as usize - 1; + let max_exp = constants.weapon.level_table[self.wtype as usize][level_idx]; + let max = self.level == WeaponLevel::Level3 && self.experience == max_exp; + + (self.experience, max_exp, max) + } + + pub fn add_xp(&mut self, exp: u16, player: &mut Player, state: &mut SharedGameState) { + let curr_level_idx = self.level as usize - 1; + let lvl_table = state.constants.weapon.level_table[self.wtype as usize]; + + self.experience = self.experience.saturating_add(exp); + + if self.level == WeaponLevel::Level3 { + if self.experience > lvl_table[2] { + self.experience = lvl_table[2]; + + if player.equip.has_whimsical_star() && player.stars < 3 { + player.stars += 1; + } + } + } else if self.experience > lvl_table[curr_level_idx] { + self.level = self.level.next(); + self.experience = 0; + + if self.wtype != WeaponType::Spur { + state.sound_manager.play_sfx(27); + state.create_caret(player.x, player.y, CaretType::LevelUp, Direction::Left); + } + } + } + + pub fn reset_xp(&mut self) { + self.level = WeaponLevel::Level1; + self.experience = 0; + } + pub fn tick( &mut self, + state: &mut SharedGameState, player: &mut Player, player_id: TargetPlayer, - inventory: &mut Inventory, bullet_manager: &mut BulletManager, - state: &mut SharedGameState, ) { if !player.cond.alive() || player.cond.hidden() { return; @@ -124,9 +165,11 @@ impl Weapon { WeaponType::MissileLauncher => self.tick_missile_launcher(player, player_id, bullet_manager, state), WeaponType::Bubbler => self.tick_bubbler(player, player_id, bullet_manager, state), WeaponType::Blade => self.tick_blade(player, player_id, bullet_manager, state), - WeaponType::SuperMissileLauncher => self.tick_super_missile_launcher(player, player_id, bullet_manager, state), + WeaponType::SuperMissileLauncher => { + self.tick_super_missile_launcher(player, player_id, bullet_manager, state) + } WeaponType::Nemesis => self.tick_nemesis(player, player_id, bullet_manager, state), - WeaponType::Spur => self.tick_spur(player, player_id, inventory, bullet_manager, state), + WeaponType::Spur => self.tick_spur(player, player_id, bullet_manager, state), } } } diff --git a/src/weapon/spur.rs b/src/weapon/spur.rs index 6c159eb..3d506b0 100644 --- a/src/weapon/spur.rs +++ b/src/weapon/spur.rs @@ -1,6 +1,5 @@ use crate::caret::CaretType; use crate::common::Direction; -use crate::inventory::Inventory; use crate::player::{Player, TargetPlayer}; use crate::shared_game_state::SharedGameState; use crate::weapon::bullet::BulletManager; @@ -11,7 +10,6 @@ impl Weapon { &mut self, player: &mut Player, player_id: TargetPlayer, - inventory: &mut Inventory, bullet_manager: &mut BulletManager, state: &mut SharedGameState, ) { @@ -21,7 +19,7 @@ impl Weapon { let btype; if player.controller.shoot() { - inventory.add_xp(if player.equip.has_turbocharge() { 3 } else { 2 }, player, state); + self.add_xp(if player.equip.has_turbocharge() { 3 } else { 2 }, player, state); self.counter1 += 1; if (self.counter1 / 2 % 2) != 0 { @@ -33,7 +31,7 @@ impl Weapon { state.sound_manager.play_sfx(60); } WeaponLevel::Level3 => { - if let (_, _, false) = inventory.get_current_max_exp(&state.constants) { + if let (_, _, false) = self.get_max_exp(&state.constants) { state.sound_manager.play_sfx(61); } } @@ -47,7 +45,7 @@ impl Weapon { } } - if let (_, _, true) = inventory.get_current_max_exp(&state.constants) { + if let (_, _, true) = self.get_max_exp(&state.constants) { if self.counter2 == 0 { self.counter2 = 1; state.sound_manager.play_sfx(65); @@ -58,7 +56,7 @@ impl Weapon { let level = self.level; if !player.controller.shoot() { - inventory.reset_current_weapon_xp(); + self.reset_xp(); } match level { @@ -77,7 +75,8 @@ impl Weapon { WeaponLevel::None => unreachable!(), } - if bullet_manager.count_bullets_multi(&BULLETS, player_id) > 0 || !(player.controller.trigger_shoot() || shoot) { + if bullet_manager.count_bullets_multi(&BULLETS, player_id) > 0 || !(player.controller.trigger_shoot() || shoot) + { return; } @@ -86,27 +85,69 @@ impl Weapon { } else { match player.direction { Direction::Left if player.up => { - bullet_manager.create_bullet(player.x - 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants); + bullet_manager.create_bullet( + player.x - 0x200, + player.y - 0x1000, + btype, + player_id, + Direction::Up, + &state.constants, + ); state.create_caret(player.x - 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left); } Direction::Right if player.up => { - bullet_manager.create_bullet(player.x + 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants); + bullet_manager.create_bullet( + player.x + 0x200, + player.y - 0x1000, + btype, + player_id, + Direction::Up, + &state.constants, + ); state.create_caret(player.x + 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left); } Direction::Left if player.down => { - bullet_manager.create_bullet(player.x - 0x200, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants); + bullet_manager.create_bullet( + player.x - 0x200, + player.y + 0x1000, + btype, + player_id, + Direction::Bottom, + &state.constants, + ); state.create_caret(player.x - 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left); } Direction::Right if player.down => { - bullet_manager.create_bullet(player.x + 0x200, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants); + bullet_manager.create_bullet( + player.x + 0x200, + player.y + 0x1000, + btype, + player_id, + Direction::Bottom, + &state.constants, + ); state.create_caret(player.x + 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left); } Direction::Left => { - bullet_manager.create_bullet(player.x - 0xc00, player.y + 0x600, btype, player_id, Direction::Left, &state.constants); + bullet_manager.create_bullet( + player.x - 0xc00, + player.y + 0x600, + btype, + player_id, + Direction::Left, + &state.constants, + ); state.create_caret(player.x - 0xc00, player.y + 0x600, CaretType::Shoot, Direction::Left); } Direction::Right => { - bullet_manager.create_bullet(player.x + 0xc00, player.y + 0x600, btype, player_id, Direction::Right, &state.constants); + bullet_manager.create_bullet( + player.x + 0xc00, + player.y + 0x600, + btype, + player_id, + Direction::Right, + &state.constants, + ); state.create_caret(player.x + 0xc00, player.y + 0x600, CaretType::Shoot, Direction::Right); } _ => {}