From 931d5a58eea1efedf6fc475e435c5d3942f4c774 Mon Sep 17 00:00:00 2001 From: Alula Date: Thu, 10 Sep 2020 15:24:04 +0200 Subject: [PATCH] implement a lot of item related opcodes and some minor bug fixes --- src/inventory.rs | 37 +++++++++++ src/live_debugger.rs | 6 +- src/main.rs | 3 + src/npc/mod.rs | 18 ++++++ src/player.rs | 10 +-- src/text_script.rs | 146 ++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 203 insertions(+), 17 deletions(-) diff --git a/src/inventory.rs b/src/inventory.rs index a57001b..8ba1af4 100644 --- a/src/inventory.rs +++ b/src/inventory.rs @@ -42,4 +42,41 @@ impl Inventory { pub fn has_item(&self, item_id: u16) -> bool { self.items.iter().any(|item| item.0 == item_id) } + + pub fn add_weapon(&mut self, weapon_id: u16, max_ammo: u16) { + if !self.has_weapon(weapon_id) { + self.weapons.push(Weapon { + id: weapon_id, + level: 1, + experience: 0, + ammo: max_ammo, + max_ammo, + }); + } + } + + pub fn remove_weapon(&mut self, weapon_id: u16) { + self.weapons.retain(|weapon| weapon.id != weapon_id); + } + + pub fn get_current_weapon(&mut self) -> Option<&mut Weapon> { + self.weapons.get_mut(self.current_weapon as usize) + } + + pub fn refill_all_ammo(&mut self) { + for weapon in self.weapons.iter_mut() { + weapon.ammo = weapon.max_ammo; + } + } + + pub fn reset_all_weapon_xp(&mut self) { + for weapon in self.weapons.iter_mut() { + weapon.level = 1; + weapon.experience = 0; + } + } + + pub fn has_weapon(&self, weapon_id: u16) -> bool { + self.weapons.iter().any(|weapon| weapon.id == weapon_id) + } } diff --git a/src/live_debugger.rs b/src/live_debugger.rs index 0f2fac3..f286846 100644 --- a/src/live_debugger.rs +++ b/src/live_debugger.rs @@ -62,17 +62,17 @@ impl LiveDebugger { )); if ui.button(im_str!("Map Selector"), [0.0, 0.0]) { - self.map_selector_visible = true; + self.map_selector_visible = !self.map_selector_visible; } ui.same_line(0.0); if ui.button(im_str!("Events"), [0.0, 0.0]) { - self.events_visible = true; + self.events_visible = !self.events_visible; } ui.same_line(0.0); if ui.button(im_str!("Hacks"), [0.0, 0.0]) { - self.hacks_visible = true; + self.hacks_visible = !self.hacks_visible; } }); diff --git a/src/main.rs b/src/main.rs index 5a3ee3c..1c2e921 100644 --- a/src/main.rs +++ b/src/main.rs @@ -95,6 +95,7 @@ pub struct SharedGameState { pub sound_manager: SoundManager, pub constants: EngineConstants, pub scale: f32, + pub god_mode: bool, pub speed_hack: bool, pub canvas_size: (f32, f32), pub screen_size: (f32, f32), @@ -183,6 +184,7 @@ impl Game { sound_manager: SoundManager::new(ctx)?, constants, scale, + god_mode: false, speed_hack: false, screen_size, canvas_size, @@ -236,6 +238,7 @@ impl Game { KeyCode::X => { state.key_state.set_fire(true) } KeyCode::A => { state.key_state.set_weapon_prev(true) } KeyCode::S => { state.key_state.set_weapon_next(true) } + KeyCode::F11 => { state.god_mode = !state.god_mode } KeyCode::F12 => { state.set_speed_hack(!state.speed_hack) } _ => {} } diff --git a/src/npc/mod.rs b/src/npc/mod.rs index fb39936..0fb9a1a 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -287,6 +287,24 @@ impl NPCMap { } } } + + pub fn is_alive(&self, npc_id: u16) -> bool { + if let Some(npc) = self.npcs.get(&npc_id) { + return npc.cond.alive(); + } + + false + } + + pub fn is_alive_by_event(&self, event_num: u16) -> bool { + for npc in self.npcs.values() { + if npc.cond.alive() && npc.event_num == event_num { + return true; + } + } + + false + } } pub struct NPCTableEntry { diff --git a/src/player.rs b/src/player.rs index 1357f4c..e4ddea9 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,6 +1,7 @@ use std::clone::Clone; -use num_traits::clamp; +use num_derive::FromPrimitive; +use num_traits::{clamp, FromPrimitive}; use crate::caret::CaretType; use crate::common::{Condition, Equipment, Flag}; @@ -8,11 +9,10 @@ use crate::common::{Direction, Rect}; use crate::entity::GameEntity; use crate::frame::Frame; use crate::ggez::{Context, GameResult}; -use crate::SharedGameState; -use crate::str; use crate::inventory::Inventory; +use crate::SharedGameState; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive)] #[repr(u8)] pub enum ControlMode { Normal = 0, @@ -488,7 +488,7 @@ impl Player { } pub fn damage(&mut self, hp: isize, state: &mut SharedGameState) { - if self.shock_counter > 0 { + if state.god_mode || self.shock_counter > 0 { return; } diff --git a/src/text_script.rs b/src/text_script.rs index 6fc2758..e9133a4 100644 --- a/src/text_script.rs +++ b/src/text_script.rs @@ -10,7 +10,7 @@ use std::str::FromStr; use byteorder::ReadBytesExt; use itertools::Itertools; use num_derive::FromPrimitive; -use num_traits::{FromPrimitive, clamp}; +use num_traits::{clamp, FromPrimitive}; use crate::{SharedGameState, str}; use crate::bitfield; @@ -18,6 +18,7 @@ use crate::common::{Direction, FadeDirection, FadeState}; use crate::entity::GameEntity; use crate::ggez::{Context, GameResult}; use crate::ggez::GameError::ParseError; +use crate::player::ControlMode; use crate::scene::game_scene::GameScene; /// Engine's text script VM operation codes. @@ -216,6 +217,7 @@ pub enum TextScriptExecutionState { Msg(u16, u32, u32, u8), WaitTicks(u16, u32, u16), WaitInput(u16, u32), + WaitStanding(u16, u32), WaitConfirmation(u16, u32, u16, u8, ConfirmSelection), WaitFade(u16, u32), } @@ -480,6 +482,12 @@ impl TextScriptVM { break; } + TextScriptExecutionState::WaitStanding(event, ip) => { + if game_scene.player.flags.hit_bottom_wall() { + state.textscript_vm.state = TextScriptExecutionState::Running(event, ip); + } + break; + } TextScriptExecutionState::WaitInput(event, ip) => { if state.key_trigger.jump() || state.key_trigger.fire() { state.textscript_vm.state = TextScriptExecutionState::Running(event, ip); @@ -605,6 +613,9 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::WaitTicks(event, cursor.position() as u32, ticks); } + OpCode::WAS => { + exec_state = TextScriptExecutionState::WaitStanding(event, cursor.position() as u32); + } OpCode::NOD => { exec_state = TextScriptExecutionState::WaitInput(event, cursor.position() as u32); } @@ -622,6 +633,46 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } } + OpCode::ITJ => { + let item_id = read_cur_varint(&mut cursor)? as u16; + let event_num = read_cur_varint(&mut cursor)? as u16; + + if game_scene.player.inventory.has_item(item_id) { + exec_state = TextScriptExecutionState::Running(event_num, 0); + } else { + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } + } + OpCode::AMJ => { + let weapon = read_cur_varint(&mut cursor)? as u16; + let event_num = read_cur_varint(&mut cursor)? as u16; + + if game_scene.player.inventory.has_weapon(weapon) { + exec_state = TextScriptExecutionState::Running(event_num, 0); + } else { + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } + } + OpCode::NCJ => { + let npc_id = read_cur_varint(&mut cursor)? as u16; + let event_num = read_cur_varint(&mut cursor)? as u16; + + if game_scene.npc_map.is_alive(npc_id) { + exec_state = TextScriptExecutionState::Running(event_num, 0); + } else { + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } + } + OpCode::ECJ => { + let npc_event_num = read_cur_varint(&mut cursor)? as u16; + let event_num = read_cur_varint(&mut cursor)? as u16; + + if game_scene.npc_map.is_alive_by_event(npc_event_num) { + exec_state = TextScriptExecutionState::Running(event_num, 0); + } else { + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } + } OpCode::EVE => { let event_num = read_cur_varint(&mut cursor)? as u16; @@ -735,8 +786,19 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } + OpCode::UNI => { + let control_mode = read_cur_varint(&mut cursor)? as u8; + + let mode: Option = FromPrimitive::from_u8(control_mode); + if let Some(mode) = mode { + game_scene.player.control_mode = mode; + } + + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } OpCode::FAI => { let fade_type = read_cur_varint(&mut cursor)? as usize; + if let Some(direction) = FadeDirection::from_int(fade_type) { state.fade_state = FadeState::FadeIn(15, direction); } @@ -745,6 +807,7 @@ impl TextScriptVM { } OpCode::FAO => { let fade_type = read_cur_varint(&mut cursor)? as usize; + if let Some(direction) = FadeDirection::from_int(fade_type) { state.fade_state = FadeState::FadeOut(-15, direction.opposite()); } @@ -894,6 +957,35 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } + OpCode::MNP => { + let event_num = read_cur_varint(&mut cursor)? as u16; + let x = read_cur_varint(&mut cursor)? as isize; + let y = read_cur_varint(&mut cursor)? as isize; + let direction = read_cur_varint(&mut cursor)? as usize; + + for npc_id in game_scene.npc_map.npc_ids.iter() { + if let Some(npc) = game_scene.npc_map.npcs.get_mut(npc_id) { + if npc.cond.alive() && npc.event_num == event_num { + npc.x = x * 16 * 0x200; + npc.y = y * 16 * 0x200; + + if direction == 4 { + npc.direction = if game_scene.player.x < npc.x { + Direction::Right + } else { + Direction::Left + }; + } else if let Some(dir) = Direction::from_int(direction) { + npc.direction = dir; + } + + break; + } + } + } + + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } OpCode::LIp => { let life = read_cur_varint(&mut cursor)? as usize; @@ -901,23 +993,60 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } + OpCode::ITp => { + let item_id = read_cur_varint(&mut cursor)? as u16; + + game_scene.player.inventory.add_item(item_id); + + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } + OpCode::ITm => { + let item_id = read_cur_varint(&mut cursor)? as u16; + + game_scene.player.inventory.remove_item(item_id); + + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } + OpCode::AMp => { + let weapon_id = read_cur_varint(&mut cursor)? as u16; + let max_ammo = read_cur_varint(&mut cursor)? as u16; + + game_scene.player.inventory.add_weapon(weapon_id, max_ammo); + + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } + OpCode::AMm => { + let weapon_id = read_cur_varint(&mut cursor)? as u16; + + game_scene.player.inventory.remove_weapon(weapon_id); + + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } + OpCode::AEp => { + game_scene.player.inventory.refill_all_ammo(); + + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } + OpCode::ZAM => { + game_scene.player.inventory.reset_all_weapon_xp(); + + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } // unimplemented opcodes // Zero operands OpCode::AEp | OpCode::CAT | OpCode::CIL | OpCode::CPS | OpCode::CRE | OpCode::CSS | OpCode::ESC | OpCode::FLA | OpCode::INI | OpCode::LDP | OpCode::MLP | OpCode::SAT | OpCode::SLP | OpCode::SPS | - OpCode::STC | OpCode::SVP | OpCode::TUR | OpCode::WAS | OpCode::ZAM => { + OpCode::STC | OpCode::SVP | OpCode::TUR => { log::warn!("unimplemented opcode: {:?}", op); exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } // One operand codes - OpCode::BOA | OpCode::BSL | OpCode::FOB | OpCode::UNI | - OpCode::NUM | OpCode::DNA | + OpCode::BOA | OpCode::BSL | OpCode::FOB | OpCode::NUM | OpCode::DNA | OpCode::MPp | OpCode::SKm | OpCode::SKp | OpCode::EQp | OpCode::EQm | - OpCode::ITp | OpCode::ITm | OpCode::AMm | OpCode::UNJ | OpCode::MPJ | - OpCode::XX1 | OpCode::SIL | OpCode::SOU | + OpCode::UNJ | OpCode::MPJ | OpCode::XX1 | OpCode::SIL | OpCode::SOU | OpCode::SSS | OpCode::ACH => { let par_a = read_cur_varint(&mut cursor)?; @@ -926,8 +1055,7 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } // Two operand codes - OpCode::AMp | OpCode::NCJ | OpCode::ECJ | - OpCode::ITJ | OpCode::SKJ | OpCode::AMJ | OpCode::SMP | OpCode::PSp => { + OpCode::SKJ | OpCode::SMP | OpCode::PSp => { let par_a = read_cur_varint(&mut cursor)?; let par_b = read_cur_varint(&mut cursor)?; @@ -946,7 +1074,7 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } // Four operand codes - OpCode::MNP | OpCode::SNP => { + OpCode::SNP => { let par_a = read_cur_varint(&mut cursor)?; let par_b = read_cur_varint(&mut cursor)?; let par_c = read_cur_varint(&mut cursor)?;