use num_derive::FromPrimitive; use crate::caret::CaretType; use crate::common::Direction; use crate::engine_constants::EngineConstants; use crate::player::{Player, TargetPlayer}; use crate::shared_game_state::SharedGameState; use crate::weapon::bullet::BulletManager; mod blade; mod bubbler; pub mod bullet; mod fireball; mod machine_gun; mod missile_launcher; mod nemesis; mod polar_star; mod snake; mod spur; mod super_missile_launcher; #[derive(Debug, PartialEq, Eq, Copy, Clone, FromPrimitive, bincode::Encode, bincode::Decode)] #[repr(u8)] pub enum WeaponType { None = 0, Snake = 1, PolarStar = 2, Fireball = 3, MachineGun = 4, MissileLauncher = 5, Bubbler = 7, Blade = 9, SuperMissileLauncher = 10, Nemesis = 12, Spur = 13, } #[derive(Debug, PartialEq, Eq, Copy, Clone, bincode::Encode, bincode::Decode)] #[repr(u8)] pub enum WeaponLevel { None = 0, Level1 = 1, Level2 = 2, Level3 = 3, } impl WeaponLevel { pub fn next(self) -> WeaponLevel { match self { WeaponLevel::None => WeaponLevel::Level1, WeaponLevel::Level1 => WeaponLevel::Level2, WeaponLevel::Level2 => WeaponLevel::Level3, WeaponLevel::Level3 => WeaponLevel::Level3, } } pub fn prev(self) -> WeaponLevel { match self { WeaponLevel::None => WeaponLevel::Level1, WeaponLevel::Level1 => WeaponLevel::Level1, WeaponLevel::Level2 => WeaponLevel::Level1, WeaponLevel::Level3 => WeaponLevel::Level2, } } } #[derive(Clone, bincode::Encode, bincode::Decode)] pub struct Weapon { pub wtype: WeaponType, pub level: WeaponLevel, pub experience: u16, pub ammo: u16, pub max_ammo: u16, refire_timer: u16, empty_counter: u16, counter1: u16, counter2: u16, } impl Weapon { pub fn new(wtype: WeaponType, level: WeaponLevel, experience: u16, ammo: u16, max_ammo: u16) -> Weapon { Weapon { wtype, level, experience, ammo, max_ammo, refire_timer: 0, empty_counter: 0, counter1: 0, counter2: 0 } } /// Consume a specified amount of bullets, returns true if there was enough ammo. pub fn consume_ammo(&mut self, amount: u16) -> bool { if self.max_ammo == 0 { return true; } if self.ammo >= amount { self.ammo -= amount; return true; } false } /// Draw empty! caret only once every 50 ticks pub fn draw_empty(&mut self, state: &mut SharedGameState, x: i32, y: i32) { state.sound_manager.play_sfx(37); if self.empty_counter == 0 { state.create_caret(x, y, CaretType::EmptyText, Direction::Left); self.empty_counter = 50; } } /// Set refire timer pub fn set_refire_timer(&mut self) { self.refire_timer = 4; } /// Refill a specified amount of bullets. pub fn refill_ammo(&mut self, amount: u16) { if self.max_ammo != 0 { self.ammo = self.ammo.saturating_add(amount).min(self.max_ammo); } } 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); } } player.xp_counter = if self.wtype != WeaponType::Spur { 30 } else { 10 }; } 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, bullet_manager: &mut BulletManager, ) { if !player.cond.alive() || player.cond.hidden() { return; } self.empty_counter = self.empty_counter.saturating_sub(1); self.refire_timer = self.refire_timer.saturating_sub(1); if player.controller.trigger_shoot() { if self.refire_timer > 0 { return; } self.refire_timer = 4; } // todo lua hook match self.wtype { WeaponType::None => {} WeaponType::Snake => self.tick_snake(player, player_id, bullet_manager, state), WeaponType::PolarStar => self.tick_polar_star(player, player_id, bullet_manager, state), WeaponType::Fireball => self.tick_fireball(player, player_id, bullet_manager, state), WeaponType::MachineGun => self.tick_machine_gun(player, player_id, bullet_manager, state), 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::Nemesis => self.tick_nemesis(player, player_id, bullet_manager, state), WeaponType::Spur => self.tick_spur(player, player_id, bullet_manager, state), } } }