From 5efd3f3c92f76463c85495a5597e07c81d55ed03 Mon Sep 17 00:00:00 2001 From: Alula <6276139+alula@users.noreply.github.com> Date: Mon, 29 Mar 2021 23:19:07 +0200 Subject: [PATCH] general cleanup, hooks and android build fix --- Cargo.toml | 16 +- app/app/build.gradle | 9 +- app/app/src/main/AndroidManifest.xml | 2 +- build.rs | 2 - drsandroid/Cargo.toml | 14 + src/main_android.rs => drsandroid/src/lib.rs | 2 +- src/caret.rs | 26 ++ src/common.rs | 8 +- src/components/flash.rs | 2 +- src/components/inventory.rs | 71 ++++- src/engine_constants/mod.rs | 4 +- src/engine_constants/npcs.rs | 14 - src/framework/backend.rs | 4 +- src/framework/backend_null.rs | 16 +- src/framework/backend_sdl2.rs | 7 +- src/framework/filesystem.rs | 4 - src/framework/vfs.rs | 2 +- src/hooks.rs | 276 +++++++++++++++++++ src/inventory.rs | 2 - src/lib.rs | 2 + src/live_debugger.rs | 2 - src/map.rs | 1 - src/npc/ai/balrog.rs | 1 - src/npc/ai/booster.rs | 1 - src/npc/ai/chaco.rs | 1 - src/npc/ai/characters.rs | 1 - src/npc/ai/curly.rs | 2 +- src/npc/ai/egg_corridor.rs | 1 - src/npc/ai/first_cave.rs | 1 - src/npc/ai/grasstown.rs | 3 +- src/npc/ai/igor.rs | 1 - src/npc/ai/intro.rs | 1 - src/npc/ai/misc.rs | 2 +- src/npc/ai/outer_wall.rs | 1 - src/npc/ai/pickups.rs | 1 - src/npc/ai/quote.rs | 1 - src/npc/ai/sand_zone.rs | 7 +- src/npc/ai/santa.rs | 1 - src/npc/ai/sue.rs | 4 +- src/npc/ai/toroko.rs | 6 +- src/npc/ai/weapon_trail.rs | 1 - src/npc/boss/balfrog.rs | 1 - src/npc/boss/monster_x.rs | 1 - src/npc/boss/omega.rs | 1 - src/npc/list.rs | 1 - src/npc/mod.rs | 37 ++- src/npc/utils.rs | 13 +- src/physics.rs | 2 +- src/player/mod.rs | 4 +- src/player/player_hit.rs | 2 +- src/player/skin/basic.rs | 2 +- src/profile.rs | 2 +- src/scene/game_scene.rs | 163 +++++------ src/scene/no_data_scene.rs | 6 +- src/scene/title_scene.rs | 1 + src/shared_game_state.rs | 29 +- src/sound/mod.rs | 2 +- src/sound/ogg_playback.rs | 12 +- src/sound/org_playback.rs | 2 +- src/sound/pixtone.rs | 1 + src/texture_set.rs | 2 +- src/weapon/bubbler.rs | 1 - src/weapon/bullet.rs | 24 +- 63 files changed, 581 insertions(+), 251 deletions(-) create mode 100644 drsandroid/Cargo.toml rename src/main_android.rs => drsandroid/src/lib.rs (98%) create mode 100644 src/hooks.rs diff --git a/Cargo.toml b/Cargo.toml index 323be58..346b116 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,14 +15,6 @@ test = false bench = false required-features = ["exe"] -[[example]] -name = "drsandroid" -path = "src/main_android.rs" -crate-type = ["cdylib"] -test = false -bench = false -required-features = ["android"] - [profile.release] lto = 'thin' panic = 'abort' @@ -30,12 +22,6 @@ panic = 'abort' [profile.dev.package."*"] opt-level = 3 -[profile.dev.build-override] -opt-level = 1 - -[profile.release.build-override] -opt-level = 1 - [features] default = ["scripting", "backend-sdl", "ogg-playback", "netplay", "exe"] ogg-playback = ["lewton"] @@ -45,6 +31,7 @@ backend-glutin = ["winit", "glutin"] scripting = ["lua-ffi"] netplay = [] editor = [] +hooks = ["libc"] exe = [] android = [] @@ -64,6 +51,7 @@ image = { version = "0.23", default-features = false, features = ["png", "bmp"] itertools = "0.10" lazy_static = "1.4.0" lewton = { version = "0.10.2", optional = true } +libc = { version = "0.2", optional = true } log = "0.4" lua-ffi = { git = "https://github.com/doukutsu-rs/lua-ffi.git", rev = "1ef3caf772d72068297ddf75df06fd2ef8c1daab", optional = true } lru = "0.6.0" diff --git a/app/app/build.gradle b/app/app/build.gradle index 8c9fc1b..7d00ea2 100644 --- a/app/app/build.gradle +++ b/app/app/build.gradle @@ -18,7 +18,7 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" ndk { - abiFilters 'x86', 'arm64-v8a' + abiFilters 'x86', 'arm64-v8a', 'armeabi-v7a' } externalNativeBuild { @@ -69,13 +69,14 @@ println("cargo target: ${project.buildDir.getAbsolutePath()}/rust-target") cargoNdk { targets = [ "x86", + "arm", "arm64" ] - librariesNames = ["libdrsembedded.so"] + librariesNames = ["libdrsandroid.so"] //targetDirectory = "${project.buildDir.getAbsolutePath()}/rust-target" - module = "../" + module = "../drsandroid/" extraCargoEnv = ["ANDROID_NDK_HOME": android.ndkDirectory] - extraCargoBuildArguments = ["--no-default-features", "--features", "backend-glutin ogg-playback scripting"] + extraCargoBuildArguments = [] verbose = true buildTypes { diff --git a/app/app/src/main/AndroidManifest.xml b/app/app/src/main/AndroidManifest.xml index 0b31cbc..5bccf18 100644 --- a/app/app/src/main/AndroidManifest.xml +++ b/app/app/src/main/AndroidManifest.xml @@ -29,7 +29,7 @@ android:screenOrientation="sensorLandscape" android:launchMode="standard" android:configChanges="orientation|keyboardHidden|screenSize"> - + diff --git a/build.rs b/build.rs index 96a3ac1..d26903c 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,4 @@ use std::env; -use std::fs::File; -use std::path::PathBuf; // #[cfg(feature = "generate-gl")] // use gl_generator::{Api, Fallbacks, Profile, Registry}; diff --git a/drsandroid/Cargo.toml b/drsandroid/Cargo.toml new file mode 100644 index 0000000..9b0f207 --- /dev/null +++ b/drsandroid/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "drsandroid" +version = "0.1.0" +authors = ["Alula"] +edition = "2018" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +ndk = "0.2" +ndk-glue = "0.2" +ndk-sys = "0.2" +doukutsu-rs = { path = "../", default-features = false, features = ["android", "backend-glutin", "ogg-playback", "scripting"] } diff --git a/src/main_android.rs b/drsandroid/src/lib.rs similarity index 98% rename from src/main_android.rs rename to drsandroid/src/lib.rs index aaf1695..f1a2832 100644 --- a/src/main_android.rs +++ b/drsandroid/src/lib.rs @@ -2,4 +2,4 @@ #[cfg_attr(target_os = "android", ndk_glue::main())] pub fn android_main() { doukutsu_rs::init().unwrap(); -} \ No newline at end of file +} diff --git a/src/caret.rs b/src/caret.rs index 755bc07..05a9d67 100644 --- a/src/caret.rs +++ b/src/caret.rs @@ -24,6 +24,32 @@ pub enum CaretType { PushJumpKey, } +impl CaretType { + pub fn from_int(id: usize) -> Option { + match id { + 0 => Some(CaretType::None), + 1 => Some(CaretType::Bubble), + 2 => Some(CaretType::ProjectileDissipation), + 3 => Some(CaretType::Shoot), + 4 => Some(CaretType::SnakeAfterimage), + 5 => Some(CaretType::Zzz), + 6 => Some(CaretType::SnakeAfterimage2), + 7 => Some(CaretType::Exhaust), + 8 => Some(CaretType::DrownedQuote), + 9 => Some(CaretType::QuestionMark), + 10 => Some(CaretType::LevelUp), + 11 => Some(CaretType::HurtParticles), + 12 => Some(CaretType::Explosion), + 13 => Some(CaretType::LittleParticles), + 14 => Some(CaretType::Unknown), + 15 => Some(CaretType::SmallProjectileDissipation), + 16 => Some(CaretType::Empty), + 17 => Some(CaretType::PushJumpKey), + _ => None, + } + } +} + pub struct Caret { pub ctype: CaretType, pub x: i32, diff --git a/src/common.rs b/src/common.rs index 08a34fb..28ce1d1 100644 --- a/src/common.rs +++ b/src/common.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; use std::fmt; use lazy_static::lazy_static; -use num_traits::{abs, AsPrimitive, Num}; +use num_traits::{abs, Num}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde::de::{SeqAccess, Visitor}; use serde::ser::SerializeTupleStruct; @@ -21,6 +21,7 @@ lazy_static! { bitfield! { #[derive(Clone, Copy)] + #[repr(C)] pub struct Flag(u32); impl Debug; @@ -63,6 +64,7 @@ impl Flag { bitfield! { #[derive(Clone, Copy)] + #[repr(C)] pub struct Equipment(u16); impl Debug; @@ -88,6 +90,7 @@ bitfield! { bitfield! { #[derive(Clone, Copy)] + #[repr(C)] pub struct Condition(u16); impl Debug; @@ -106,6 +109,7 @@ bitfield! { bitfield! { #[derive(Clone, Copy, Serialize, Deserialize)] + #[repr(C)] pub struct ControlFlags(u16); impl Debug; @@ -121,6 +125,7 @@ bitfield! { bitfield! { #[derive(Clone, Copy)] + #[repr(C)] pub struct BulletFlag(u16); impl Debug; pub flag_x01, set_flag_x01: 0; // 0x01 @@ -257,6 +262,7 @@ impl Point { } #[derive(Debug, Clone, Copy)] +#[repr(C)] pub struct Rect { pub left: T, pub top: T, diff --git a/src/components/flash.rs b/src/components/flash.rs index 6dc4809..746051a 100644 --- a/src/components/flash.rs +++ b/src/components/flash.rs @@ -37,7 +37,7 @@ impl Flash { } impl GameEntity<()> for Flash { - fn tick(&mut self, state: &mut SharedGameState, _custom: ()) -> GameResult<()> { + fn tick(&mut self, _state: &mut SharedGameState, _custom: ()) -> GameResult<()> { match self.state { FlashState::None => {} FlashState::Cross(x, y, tick) => { diff --git a/src/components/inventory.rs b/src/components/inventory.rs index e825a1e..dda8e85 100644 --- a/src/components/inventory.rs +++ b/src/components/inventory.rs @@ -1,30 +1,79 @@ 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::context::Context; use crate::framework::error::GameResult; +use crate::inventory::Inventory; +use crate::player::Player; +use crate::shared_game_state::SharedGameState; +use crate::text_script::ScriptMode; + +#[derive(Copy, Clone, PartialEq, Eq)] +#[repr(u8)] +enum InventoryFocus { + None, + Weapons, + Items, +} pub struct InventoryUI { text_y_pos: usize, tick: usize, + current_script: usize, + focus: InventoryFocus, } impl InventoryUI { pub fn new() -> InventoryUI { - InventoryUI { - text_y_pos: 24, - tick: 0, + InventoryUI { text_y_pos: 24, tick: 0, current_script: 0, focus: InventoryFocus::None } + } +} + +impl GameEntity<(&mut Player, &mut Inventory)> for InventoryUI { + fn tick( + &mut self, + state: &mut SharedGameState, + (player, inventory): (&mut Player, &mut Inventory), + ) -> GameResult<()> { + if state.control_flags.control_enabled() + && (player.controller.trigger_inventory() || player.controller.trigger_menu_back()) + { + self.focus = InventoryFocus::None; + state.textscript_vm.reset(); + state.textscript_vm.set_mode(ScriptMode::Map); + return Ok(()); } - } -} -impl GameEntity<&mut Inventory> for InventoryUI { - fn tick(&mut self, state: &mut SharedGameState, custom: &mut Inventory) -> GameResult<()> { + match self.focus { + InventoryFocus::None => { + self.focus = InventoryFocus::Weapons; + state.textscript_vm.start_script(1004); + } + InventoryFocus::Weapons => {} + InventoryFocus::Items => {} + } + + self.tick = self.tick.wrapping_add(1); + Ok(()) } - fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, frame: &Frame) -> GameResult<()> { + fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, _frame: &Frame) -> GameResult<()> { + let mut y = 8.0; + + let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?; + for i in 0..=18 { + let rect = match i { + 0 => &state.constants.textscript.inventory_rect_top, + 18 => &state.constants.textscript.inventory_rect_bottom, + _ => &state.constants.textscript.inventory_rect_middle, + }; + + batch.add_rect(((state.canvas_size.0 - 244.0) / 2.0).floor(), y, rect); + y += 8.0; + } + + batch.draw(ctx)?; + Ok(()) } } diff --git a/src/engine_constants/mod.rs b/src/engine_constants/mod.rs index b8de0e5..afe3ca8 100644 --- a/src/engine_constants/mod.rs +++ b/src/engine_constants/mod.rs @@ -36,8 +36,8 @@ pub struct BoosterConsts { #[derive(Debug, Copy, Clone)] pub struct MyCharConsts { - pub display_bounds: Rect, - pub hit_bounds: Rect, + pub display_bounds: Rect, + pub hit_bounds: Rect, pub life: u16, pub max_life: u16, pub control_mode: ControlMode, diff --git a/src/engine_constants/npcs.rs b/src/engine_constants/npcs.rs index c4a422f..219211f 100644 --- a/src/engine_constants/npcs.rs +++ b/src/engine_constants/npcs.rs @@ -3,8 +3,6 @@ use serde::{Deserialize, Serialize}; use crate::common::Rect; #[derive(Debug, Copy, Clone, Serialize, Deserialize)] - -// 360/369 (97.56%) NPCs defined pub struct NPCConsts { // pub n000_null: () // Defined in code @@ -1062,9 +1060,6 @@ pub struct NPCConsts { #[serde(default = "default_n360_credits_thank_you")] pub n360_credits_thank_you: Rect, - #[serde(default = "default_n361_gaudi_dashing")] - pub n361_gaudi_dashing: [Rect; 4], - #[serde(default = "default_b01_omega")] pub b01_omega: [Rect; 10], @@ -4744,15 +4739,6 @@ fn default_n360_credits_thank_you() -> Rect { Rect { left: 0, top: 176, right: 48, bottom: 184 } } -fn default_n361_gaudi_dashing() -> [Rect; 4] { - [ - Rect { left: 48, top: 48, right: 72, bottom: 72 }, - Rect { left: 72, top: 48, right: 96, bottom: 72 }, - Rect { left: 48, top: 72, right: 72, bottom: 96 }, - Rect { left: 72, top: 72, right: 96, bottom: 96 }, - ] -} - fn default_b01_omega() -> [Rect; 10] { [ Rect { left: 0, top: 0, right: 80, bottom: 56 }, diff --git a/src/framework/backend.rs b/src/framework/backend.rs index 2359db8..c0250a3 100644 --- a/src/framework/backend.rs +++ b/src/framework/backend.rs @@ -1,6 +1,6 @@ use imgui::DrawData; -use crate::common::{Color, Point, Rect}; +use crate::common::{Color, Rect}; use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::framework::graphics::BlendMode; @@ -21,7 +21,7 @@ pub trait BackendRenderer { fn present(&mut self) -> GameResult; - fn prepare_draw(&mut self, width: f32, height: f32) -> GameResult { + fn prepare_draw(&mut self, _width: f32, _height: f32) -> GameResult { Ok(()) } diff --git a/src/framework/backend_null.rs b/src/framework/backend_null.rs index 52d6017..6a24cf2 100644 --- a/src/framework/backend_null.rs +++ b/src/framework/backend_null.rs @@ -66,7 +66,7 @@ impl BackendTexture for NullTexture { (self.0, self.1) } - fn add(&mut self, command: SpriteBatchCommand) { + fn add(&mut self, _command: SpriteBatchCommand) { } @@ -82,7 +82,7 @@ impl BackendTexture for NullTexture { pub struct NullRenderer(RefCell); impl BackendRenderer for NullRenderer { - fn clear(&mut self, color: Color) { + fn clear(&mut self, _color: Color) { } @@ -94,23 +94,23 @@ impl BackendRenderer for NullRenderer { Ok(Box::new(NullTexture(width, height))) } - fn create_texture(&mut self, width: u16, height: u16, data: &[u8]) -> GameResult> { + fn create_texture(&mut self, width: u16, height: u16, _data: &[u8]) -> GameResult> { Ok(Box::new(NullTexture(width, height))) } - fn set_blend_mode(&mut self, blend: BlendMode) -> GameResult { + fn set_blend_mode(&mut self, _blend: BlendMode) -> GameResult { Ok(()) } - fn set_render_target(&mut self, texture: Option<&Box>) -> GameResult { + fn set_render_target(&mut self, _texture: Option<&Box>) -> GameResult { Ok(()) } - fn draw_rect(&mut self, rect: Rect, color: Color) -> GameResult { + fn draw_rect(&mut self, _rect: Rect, _color: Color) -> GameResult { Ok(()) } - fn draw_outline_rect(&mut self, rect: Rect, line_width: usize, color: Color) -> GameResult { + fn draw_outline_rect(&mut self, _rect: Rect, _line_width: usize, _color: Color) -> GameResult { Ok(()) } @@ -118,7 +118,7 @@ impl BackendRenderer for NullRenderer { unsafe { Ok(&mut *self.0.as_ptr()) } } - fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult { + fn render_imgui(&mut self, _draw_data: &DrawData) -> GameResult { Ok(()) } } diff --git a/src/framework/backend_sdl2.rs b/src/framework/backend_sdl2.rs index 83816b3..93f82ab 100644 --- a/src/framework/backend_sdl2.rs +++ b/src/framework/backend_sdl2.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; use std::rc::Rc; use imgui::internal::RawWrapper; -use imgui::{ConfigFlags, DrawCmd, DrawData, ImString, Key, MouseCursor, TextureId, Ui}; +use imgui::{ConfigFlags, DrawCmd, DrawData, ImString, Key, MouseCursor, TextureId}; use sdl2::event::{Event, WindowEvent}; use sdl2::keyboard::Scancode; use sdl2::mouse::{Cursor, SystemCursor}; @@ -17,7 +17,7 @@ use crate::common::{Color, Rect}; use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand}; use crate::framework::context::Context; use crate::framework::error::{GameError, GameResult}; -use crate::framework::graphics::{imgui_context, BlendMode}; +use crate::framework::graphics::{BlendMode}; use crate::framework::keyboard::ScanCode; use crate::framework::ui::init_imgui; use crate::Game; @@ -121,7 +121,7 @@ impl BackendEventLoop for SDL2EventLoop { imgui.io_mut().display_size = [ctx.screen_size.0, ctx.screen_size.1]; } } - state.handle_resize(ctx); + state.handle_resize(ctx).unwrap(); } _ => {} }, @@ -858,7 +858,6 @@ impl ImguiSdl2 { } pub fn handle_event(&mut self, imgui: &mut imgui::Context, event: &Event) { - use sdl2::keyboard; use sdl2::mouse::MouseButton; fn set_mod(imgui: &mut imgui::Context, keymod: keyboard::Mod) { diff --git a/src/framework/filesystem.rs b/src/framework/filesystem.rs index f2cc450..4b99300 100644 --- a/src/framework/filesystem.rs +++ b/src/framework/filesystem.rs @@ -1,11 +1,7 @@ -use std::env; use std::fmt; use std::io; use std::io::SeekFrom; use std::path; -use std::path::PathBuf; - -use directories::ProjectDirs; use crate::framework::context::Context; use crate::framework::error::{GameError, GameResult}; diff --git a/src/framework/vfs.rs b/src/framework/vfs.rs index 0c73324..96f1f72 100644 --- a/src/framework/vfs.rs +++ b/src/framework/vfs.rs @@ -12,7 +12,7 @@ use std::collections::VecDeque; use std::fmt::{self, Debug}; use std::fs; -use std::io::{BufRead, Read, Seek, Write}; +use std::io::{Read, Seek, Write}; use std::path::{self, Path, PathBuf}; use crate::framework::error::{GameError, GameResult}; diff --git a/src/hooks.rs b/src/hooks.rs new file mode 100644 index 0000000..b5878ed --- /dev/null +++ b/src/hooks.rs @@ -0,0 +1,276 @@ +use crate::caret::CaretType; +use crate::common::Direction; +use crate::npc::list::NPCList; +use crate::npc::NPC; +use crate::player::Player; +use crate::rng::RNG; +use crate::shared_game_state::SharedGameState; +use crate::stage::Stage; +use crate::weapon::bullet::BulletManager; + +pub struct CHooks { + handle_npc: unsafe extern "C" fn(callbacks: *const Callbacks, ctx: *const CtxData), +} + +pub struct Callbacks { + random: unsafe extern "C" fn(ctx: *mut CtxData, min: i32, max: i32) -> i32, + play_sfx: unsafe extern "C" fn(ctx: *mut CtxData, id: u8), + set_quake: unsafe extern "C" fn(ctx: *mut CtxData, ticks: u16), + set_caret: unsafe extern "C" fn(ctx: *mut CtxData, x: i32, y: i32, id: u16, direction: u8), + get_flag: unsafe extern "C" fn(ctx: *mut CtxData, id: u16) -> bool, + get_map_data: unsafe extern "C" fn(ctx: *mut CtxData) -> MapData, + get_player_info: unsafe extern "C" fn(ctx: *mut CtxData) -> PlayerInfo, + update_player_info: unsafe extern "C" fn(ctx: *mut CtxData, player_info: *const PlayerInfo), + destroy_npc: unsafe extern "C" fn(ctx: *mut CtxData, npc: *mut NPC), + vanish_npc: unsafe extern "C" fn(ctx: *mut CtxData, npc: *mut NPC), + create_npc: unsafe extern "C" fn( + ctx: *mut CtxData, + npc_id: u16, + x: i32, + y: i32, + vel_x: i32, + vel_y: i32, + direction: u16, + parent: u16, + min_id: u16, + ), + get_npc: unsafe extern "C" fn(ctx: *mut CtxData, npc_id: u16) -> *mut NPC, + current_npc: unsafe extern "C" fn(ctx: *mut CtxData) -> *mut NPC, +} + +#[repr(C)] +pub struct PlayerInfo { + x: i32, + y: i32, + vel_x: i32, + vel_y: i32, + flags: u32, + anim_num: u16, + cond: u16, + shock: u8, + direct: u8, + up: bool, + down: bool, +} + +#[repr(C)] +pub struct MapData { + tiles: *const u8, + attrib: *const u8, + width: u16, + height: u16, +} + +static mut HOOKS: *mut CHooks = std::ptr::null_mut(); +struct CtxData<'a, 'b, 'c, 'd, 'e, 'f>( + &'a mut NPC, + &'b mut SharedGameState, + &'c mut Player, + &'d NPCList, + &'e mut Stage, + &'f BulletManager, +); + +pub fn init_hooks() { + #[cfg(target_os = "linux")] + unsafe { + let module: *mut libc::c_void = libc::dlopen(b"./libdrshooks.so\0".as_ptr() as *const _, libc::RTLD_NOW); + if module.is_null() { + let error = libc::dlerror(); + let message = std::ffi::CString::from_raw(error).to_string_lossy().to_string(); + + log::warn!("Cannot initialize hooks?: {}", message); + return; + } + + log::info!("Loaded libhooks..."); + + let symbol: *mut libc::c_void = libc::dlsym(module, b"drs_hooks_init\0".as_ptr() as *const _); + + if symbol.is_null() { + log::warn!("initialization function hasn't been found in libhooks."); + return; + } + + let init: unsafe extern "C" fn() -> *mut CHooks = std::mem::transmute(symbol); + HOOKS = (init)(); + } + + #[cfg(target_os = "windows")] + unsafe { + use winapi::um::libloaderapi::LoadLibraryA; + use winapi::um::libloaderapi::GetProcAddress; + use winapi::um::errhandlingapi::GetLastError; + + let module = LoadLibraryA(b"drshooks.dll\0".as_ptr() as *const _); + + if module.is_null() { + let error = GetLastError(); + + log::warn!("Cannot initialize hooks?: {:#x}", error); + return; + } + + log::info!("Loaded libhooks..."); + + let symbol = GetProcAddress(module, b"drs_hooks_init\0".as_ptr() as *const _); + + if symbol.is_null() { + log::warn!("initialization function hasn't been found in libhooks."); + return; + } + + let init: unsafe extern "C" fn() -> *mut CHooks = std::mem::transmute(symbol); + HOOKS = (init)(); + } +} + +pub fn reload_hooks() {} + +pub fn run_npc_hook( + npc: &mut NPC, + state: &mut SharedGameState, + players: [&mut Player; 2], + npc_list: &NPCList, + stage: &mut Stage, + bullet_manager: &BulletManager, +) { + unsafe { + let mut ctx_data = CtxData(npc, state, players[0], npc_list, stage, bullet_manager); + + unsafe extern "C" fn random(ctx: *mut CtxData, min: i32, max: i32) -> i32 { + let ctx = &*ctx; + + ctx.0.rng.range(min..max) + }; + + unsafe extern "C" fn play_sfx(ctx: *mut CtxData, id: u8) { + (*ctx).1.sound_manager.play_sfx(id); + } + + unsafe extern "C" fn set_quake(ctx: *mut CtxData, ticks: u16) { + (*ctx).1.quake_counter = ticks; + } + + unsafe extern "C" fn set_caret(ctx: *mut CtxData, x: i32, y: i32, id: u16, direction: u8) { + (*ctx).1.create_caret( + x, + y, + CaretType::from_int(id as usize).unwrap_or(CaretType::None), + Direction::from_int_facing(direction as usize).unwrap_or(Direction::Left), + ); + } + + unsafe extern "C" fn get_flag(ctx: *mut CtxData, id: u16) -> bool { + (*ctx).1.get_flag(id as usize) + } + + unsafe extern "C" fn get_map_data(ctx: *mut CtxData) -> MapData { + let stage = &(*ctx).4; + + MapData { + tiles: stage.map.tiles.as_ptr(), + attrib: stage.map.attrib.as_ptr(), + width: stage.map.width, + height: stage.map.height, + } + } + + unsafe extern "C" fn get_player_info(ctx: *mut CtxData) -> PlayerInfo { + let player = &(*ctx).2; + + PlayerInfo { + x: player.x, + y: player.y, + vel_x: player.vel_x, + vel_y: player.vel_y, + flags: player.flags.0, + anim_num: player.anim_num, + cond: player.cond.0, + shock: player.shock_counter, + direct: player.direction as u8, + up: player.up, + down: player.down + } + } + + unsafe extern "C" fn update_player_info(ctx: *mut CtxData, player_info: *const PlayerInfo) { + let mut player = &mut (*ctx).2; + let player_info = &(*player_info); + + player.x = player_info.x; + player.y = player_info.y; + player.vel_x = player_info.vel_x; + player.vel_y = player_info.vel_y; + player.flags.0 = player_info.flags; + player.cond.0 = player_info.cond; + player.direction = Direction::from_int(player_info.direct as usize).unwrap_or(Direction::Left); + } + + unsafe extern "C" fn create_npc( + ctx: *mut CtxData, + npc_id: u16, + x: i32, + y: i32, + vel_x: i32, + vel_y: i32, + direction: u16, + parent: u16, + min_id: u16, + ) { + let ctx = &*ctx; + + let mut npc = NPC::create(npc_id, &ctx.1.npc_table); + npc.cond.set_alive(true); + npc.x = x; + npc.y = y; + npc.vel_x = vel_x; + npc.vel_y = vel_y; + npc.direction = Direction::from_int(direction as usize).unwrap_or(Direction::Left); + npc.tsc_direction = direction; + npc.parent_id = parent; + + let _ = ctx.3.spawn(min_id, npc); + }; + + unsafe extern "C" fn get_npc(ctx: *mut CtxData, npc_id: u16) -> *mut NPC { + (*ctx).3.get_npc(npc_id as usize).unwrap() as *mut NPC + } + + unsafe extern "C" fn destroy_npc(ctx: *mut CtxData, npc: *mut NPC) { + let npc = &mut (*npc); + + npc.cond.set_explode_die(true); + } + + unsafe extern "C" fn vanish_npc(ctx: *mut CtxData, npc: *mut NPC) { + let npc = &mut (*npc); + + npc.vanish((*ctx).1); + } + + unsafe extern "C" fn current_npc(ctx: *mut CtxData) -> *mut NPC { + (*ctx).0 as *mut NPC + } + + let callbacks = Callbacks { + random, + play_sfx, + set_quake, + set_caret, + get_flag, + get_map_data, + get_player_info, + update_player_info, + destroy_npc, + vanish_npc, + create_npc, + get_npc, + current_npc, + }; + + if let Some(hook) = HOOKS.as_ref() { + (hook.handle_npc)(&callbacks as *const Callbacks, &mut ctx_data as *mut CtxData); + } + } +} diff --git a/src/inventory.rs b/src/inventory.rs index 35995fc..de1aaf5 100644 --- a/src/inventory.rs +++ b/src/inventory.rs @@ -4,8 +4,6 @@ use crate::engine_constants::EngineConstants; use crate::shared_game_state::SharedGameState; use crate::weapon::{Weapon, WeaponLevel, WeaponType}; use crate::player::{Player, TargetPlayer}; -use crate::caret::CaretType; -use crate::common::Direction; use crate::weapon::bullet::BulletManager; #[derive(Clone, Copy)] diff --git a/src/lib.rs b/src/lib.rs index 05755e2..ab5dc60 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,6 +43,8 @@ mod frame; mod framework; mod input; mod inventory; +#[cfg(feature = "hooks")] +mod hooks; mod live_debugger; mod macros; mod map; diff --git a/src/live_debugger.rs b/src/live_debugger.rs index 76b589a..117e930 100644 --- a/src/live_debugger.rs +++ b/src/live_debugger.rs @@ -19,7 +19,6 @@ pub enum ScriptType { pub struct LiveDebugger { map_selector_visible: bool, events_visible: bool, - hacks_visible: bool, flags_visible: bool, npc_inspector_visible: bool, last_stage_id: usize, @@ -37,7 +36,6 @@ impl LiveDebugger { Self { map_selector_visible: false, events_visible: false, - hacks_visible: false, flags_visible: false, npc_inspector_visible: false, last_stage_id: usize::MAX, diff --git a/src/map.rs b/src/map.rs index 53950d1..13fd6b3 100644 --- a/src/map.rs +++ b/src/map.rs @@ -2,7 +2,6 @@ use std::io; use byteorder::{LE, ReadBytesExt}; -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::str; diff --git a/src/npc/ai/balrog.rs b/src/npc/ai/balrog.rs index d76f668..fcf1fc8 100644 --- a/src/npc/ai/balrog.rs +++ b/src/npc/ai/balrog.rs @@ -1,4 +1,3 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; use num_traits::clamp; diff --git a/src/npc/ai/booster.rs b/src/npc/ai/booster.rs index 456acf4..d957c46 100644 --- a/src/npc/ai/booster.rs +++ b/src/npc/ai/booster.rs @@ -1,4 +1,3 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::common::Direction; diff --git a/src/npc/ai/chaco.rs b/src/npc/ai/chaco.rs index 2f7177e..5f636e3 100644 --- a/src/npc/ai/chaco.rs +++ b/src/npc/ai/chaco.rs @@ -1,4 +1,3 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::caret::CaretType; diff --git a/src/npc/ai/characters.rs b/src/npc/ai/characters.rs index 9c742bb..0b553a2 100644 --- a/src/npc/ai/characters.rs +++ b/src/npc/ai/characters.rs @@ -1,4 +1,3 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; use num_traits::{abs, clamp}; diff --git a/src/npc/ai/curly.rs b/src/npc/ai/curly.rs index 6db356c..5a16b75 100644 --- a/src/npc/ai/curly.rs +++ b/src/npc/ai/curly.rs @@ -49,7 +49,7 @@ impl NPC { 5 => { self.action_num = 6; self.anim_num = 5; - npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right, 8, state, &self.rng); + npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right as usize, 8, state, &self.rng); } 6 => { self.anim_num = 5; diff --git a/src/npc/ai/egg_corridor.rs b/src/npc/ai/egg_corridor.rs index b2697bb..452ae21 100644 --- a/src/npc/ai/egg_corridor.rs +++ b/src/npc/ai/egg_corridor.rs @@ -1,4 +1,3 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; use num_traits::{abs, clamp}; diff --git a/src/npc/ai/first_cave.rs b/src/npc/ai/first_cave.rs index 8225295..c84938a 100644 --- a/src/npc/ai/first_cave.rs +++ b/src/npc/ai/first_cave.rs @@ -1,4 +1,3 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; use num_traits::clamp; diff --git a/src/npc/ai/grasstown.rs b/src/npc/ai/grasstown.rs index 5945b3d..dc49ddb 100644 --- a/src/npc/ai/grasstown.rs +++ b/src/npc/ai/grasstown.rs @@ -3,7 +3,6 @@ use num_traits::clamp; use crate::caret::CaretType; use crate::common::{Direction, Rect}; -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::npc::{NPCList, NPC}; use crate::player::Player; @@ -451,7 +450,7 @@ impl NPC { self.damage = 0; self.npc_flags.set_shootable(false); - npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right, 8, state, &self.rng); + npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right as usize, 8, state, &self.rng); self.create_xp_drop(state, npc_list); state.sound_manager.play_sfx(71); diff --git a/src/npc/ai/igor.rs b/src/npc/ai/igor.rs index 1aca8aa..f2b9b9e 100644 --- a/src/npc/ai/igor.rs +++ b/src/npc/ai/igor.rs @@ -1,5 +1,4 @@ use crate::common::{CDEG_RAD, Direction}; -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::npc::list::NPCList; use crate::npc::NPC; diff --git a/src/npc/ai/intro.rs b/src/npc/ai/intro.rs index 51b36f5..c08a252 100644 --- a/src/npc/ai/intro.rs +++ b/src/npc/ai/intro.rs @@ -1,4 +1,3 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::caret::CaretType; diff --git a/src/npc/ai/misc.rs b/src/npc/ai/misc.rs index 56725ec..073bc1c 100644 --- a/src/npc/ai/misc.rs +++ b/src/npc/ai/misc.rs @@ -1045,7 +1045,7 @@ impl NPC { pub(crate) fn tick_n125_hidden_item(&mut self, state: &mut SharedGameState, npc_list: &NPCList) -> GameResult { if self.life < 990 { - npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right, 8, state, &self.rng); + npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right as usize, 8, state, &self.rng); self.cond.set_alive(false); state.sound_manager.play_sfx(70); diff --git a/src/npc/ai/outer_wall.rs b/src/npc/ai/outer_wall.rs index 3a53237..2f16a05 100644 --- a/src/npc/ai/outer_wall.rs +++ b/src/npc/ai/outer_wall.rs @@ -1,6 +1,5 @@ use num_traits::abs; -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::npc::NPC; use crate::player::Player; diff --git a/src/npc/ai/pickups.rs b/src/npc/ai/pickups.rs index a2804c6..61d712d 100644 --- a/src/npc/ai/pickups.rs +++ b/src/npc/ai/pickups.rs @@ -1,4 +1,3 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; use num_traits::clamp; diff --git a/src/npc/ai/quote.rs b/src/npc/ai/quote.rs index fc3f390..ae6d700 100644 --- a/src/npc/ai/quote.rs +++ b/src/npc/ai/quote.rs @@ -1,4 +1,3 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::common::Direction; diff --git a/src/npc/ai/sand_zone.rs b/src/npc/ai/sand_zone.rs index 9e72c0a..2b68d21 100644 --- a/src/npc/ai/sand_zone.rs +++ b/src/npc/ai/sand_zone.rs @@ -2,7 +2,6 @@ use num_traits::{abs, clamp}; use crate::caret::CaretType; use crate::common::{Direction, CDEG_RAD}; -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::npc::list::NPCList; use crate::npc::NPC; @@ -113,7 +112,7 @@ impl NPC { } if self.life <= 100 { - npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right, 8, state, &self.rng); + npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right as usize, 8, state, &self.rng); state.sound_manager.play_sfx(25); self.cond.set_alive(false); @@ -571,7 +570,7 @@ impl NPC { let parent = self.get_parent_ref_mut(npc_list); if parent.is_none() || parent.as_ref().unwrap().npc_type == 3 { self.vanish(state); - npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right, 4, state, &self.rng); + npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right as usize, 4, state, &self.rng); return Ok(()); } @@ -668,7 +667,7 @@ impl NPC { if self.action_counter > 50 { state.sound_manager.play_sfx(25); self.vanish(state); - npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right, 8, state, &self.rng); + npc_list.create_death_smoke(self.x, self.y, self.display_bounds.right as usize, 8, state, &self.rng); } } _ => {} diff --git a/src/npc/ai/santa.rs b/src/npc/ai/santa.rs index 1743ca0..cbc7da0 100644 --- a/src/npc/ai/santa.rs +++ b/src/npc/ai/santa.rs @@ -1,4 +1,3 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; use num_traits::abs; diff --git a/src/npc/ai/sue.rs b/src/npc/ai/sue.rs index 928b962..e1a0e10 100644 --- a/src/npc/ai/sue.rs +++ b/src/npc/ai/sue.rs @@ -1,6 +1,4 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; -use num_traits::clamp; use crate::common::Direction; use crate::npc::list::NPCList; @@ -213,7 +211,7 @@ impl NPC { if self.action_num != 14 { self.vel_y += 0x40; - self.vel_x = clamp(self.vel_x, -0x400, 0x400); + self.vel_x = self.vel_x.clamp(-0x400, 0x400); if self.vel_y > 0x5ff { self.vel_y = 0x5ff; } diff --git a/src/npc/ai/toroko.rs b/src/npc/ai/toroko.rs index 18802b9..71d6e41 100644 --- a/src/npc/ai/toroko.rs +++ b/src/npc/ai/toroko.rs @@ -1,6 +1,4 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; -use num_traits::clamp; use crate::common::Direction; use crate::npc::NPC; @@ -149,7 +147,7 @@ impl NPC { } self.vel_y += 0x40; - self.vel_x = clamp(self.vel_x, -0x400, 0x400); + self.vel_x = self.vel_x.clamp(-0x400, 0x400); if self.vel_y > 0x5ff { self.vel_y = 0x5ff; @@ -254,7 +252,7 @@ impl NPC { } self.vel_y += 0x40; - self.vel_x = clamp(self.vel_x, -0x400, 0x400); + self.vel_x = self.vel_x.clamp(-0x400, 0x400); if self.vel_y > 0x5ff { self.vel_y = 0x5ff; diff --git a/src/npc/ai/weapon_trail.rs b/src/npc/ai/weapon_trail.rs index d7c0a12..5d1d8a2 100644 --- a/src/npc/ai/weapon_trail.rs +++ b/src/npc/ai/weapon_trail.rs @@ -1,4 +1,3 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::npc::NPC; diff --git a/src/npc/boss/balfrog.rs b/src/npc/boss/balfrog.rs index 59a0ffe..6f7f598 100644 --- a/src/npc/boss/balfrog.rs +++ b/src/npc/boss/balfrog.rs @@ -1,4 +1,3 @@ -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::caret::CaretType; diff --git a/src/npc/boss/monster_x.rs b/src/npc/boss/monster_x.rs index f135a4d..66e64de 100644 --- a/src/npc/boss/monster_x.rs +++ b/src/npc/boss/monster_x.rs @@ -3,7 +3,6 @@ use num_traits::{abs, clamp}; use crate::caret::CaretType; use crate::common::{Direction, Rect, CDEG_RAD}; use crate::components::flash::Flash; -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::npc::boss::BossNPC; use crate::npc::list::NPCList; diff --git a/src/npc/boss/omega.rs b/src/npc/boss/omega.rs index c9b4fdd..c27e548 100644 --- a/src/npc/boss/omega.rs +++ b/src/npc/boss/omega.rs @@ -1,7 +1,6 @@ use crate::caret::CaretType; use crate::common::{Direction, Rect}; use crate::components::flash::Flash; -use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::npc::boss::BossNPC; use crate::npc::list::NPCList; diff --git a/src/npc/list.rs b/src/npc/list.rs index 84377fe..94a2adc 100644 --- a/src/npc/list.rs +++ b/src/npc/list.rs @@ -1,7 +1,6 @@ use std::cell::{Cell, UnsafeCell}; use std::mem::MaybeUninit; -use crate::framework::context::Context; use crate::framework::error::{GameResult, GameError}; use crate::npc::NPC; diff --git a/src/npc/mod.rs b/src/npc/mod.rs index 324f50f..255020f 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -51,6 +51,7 @@ bitfield! { /// Represents an NPC object. #[derive(Debug, Clone)] +#[repr(C)] pub struct NPC { pub id: u16, pub npc_type: u16, @@ -83,8 +84,6 @@ pub struct NPC { /// Raw direction value set by TSC because some NPCs have it set outside 0-4 range, /// breaking the direction type. pub tsc_direction: u16, - pub display_bounds: Rect, - pub hit_bounds: Rect, pub parent_id: u16, pub action_num: u16, pub anim_num: u16, @@ -92,8 +91,11 @@ pub struct NPC { pub event_num: u16, pub action_counter: u16, pub action_counter2: u16, + pub action_counter3: u16, pub anim_counter: u16, pub anim_rect: Rect, + pub display_bounds: Rect, + pub hit_bounds: Rect, pub rng: Xoroshiro32PlusPlus, } @@ -132,6 +134,7 @@ impl NPC { event_num: 0, action_counter: 0, action_counter2: 0, + action_counter3: 0, anim_counter: 0, anim_rect: Rect { left: 0, top: 0, right: 0, bottom: 0 }, rng: Xoroshiro32PlusPlus::new(0), @@ -319,7 +322,13 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager)> for NP 355 => self.tick_n355_quote_and_curly_on_balrog(state, npc_list), 358 => self.tick_n358_misery_credits(state), 359 => self.tick_n359_water_droplet_generator(state, players, npc_list), - _ => Ok(()), + _ => { + #[cfg(feature = "hooks")] + { + crate::hooks::run_npc_hook(self, state, players, npc_list, stage, bullet_manager); + } + Ok(()) + }, }?; if self.shock > 0 { @@ -394,7 +403,7 @@ impl PhysicalEntity for NPC { fn offset_y(&self) -> i32 { if self.size >= 3 && !self.cond.drs_boss() { -0x1000 } else { 0 } } #[inline(always)] - fn hit_bounds(&self) -> &Rect { + fn hit_bounds(&self) -> &Rect { &self.hit_bounds } @@ -552,26 +561,26 @@ impl NPCTable { self.entries.get(npc_type as usize) } - pub fn get_display_bounds(&self, npc_type: u16) -> Rect { + pub fn get_display_bounds(&self, npc_type: u16) -> Rect { if let Some(npc) = self.entries.get(npc_type as usize) { Rect { - left: npc.display_bounds.left as usize * 0x200, - top: npc.display_bounds.top as usize * 0x200, - right: npc.display_bounds.right as usize * 0x200, - bottom: npc.display_bounds.bottom as usize * 0x200, + left: npc.display_bounds.left as u32 * 0x200, + top: npc.display_bounds.top as u32 * 0x200, + right: npc.display_bounds.right as u32 * 0x200, + bottom: npc.display_bounds.bottom as u32 * 0x200, } } else { Rect { left: 0, top: 0, right: 0, bottom: 0 } } } - pub fn get_hit_bounds(&self, npc_type: u16) -> Rect { + pub fn get_hit_bounds(&self, npc_type: u16) -> Rect { if let Some(npc) = self.entries.get(npc_type as usize) { Rect { - left: npc.hit_bounds.left as usize * 0x200, - top: npc.hit_bounds.top as usize * 0x200, - right: npc.hit_bounds.right as usize * 0x200, - bottom: npc.hit_bounds.bottom as usize * 0x200, + left: npc.hit_bounds.left as u32 * 0x200, + top: npc.hit_bounds.top as u32 * 0x200, + right: npc.hit_bounds.right as u32 * 0x200, + bottom: npc.hit_bounds.bottom as u32 * 0x200, } } else { Rect { left: 0, top: 0, right: 0, bottom: 0 } diff --git a/src/npc/utils.rs b/src/npc/utils.rs index 551c765..f9fc4fa 100644 --- a/src/npc/utils.rs +++ b/src/npc/utils.rs @@ -75,6 +75,7 @@ impl NPC { parent_id: 0, action_counter: 0, action_counter2: 0, + action_counter3: 0, anim_counter: 0, anim_rect: Rect::new(0, 0, 0, 0), rng: Xoroshiro32PlusPlus::new(0), @@ -231,9 +232,9 @@ impl NPCList { } match npc.size { - 1 => { self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right, 3, state, &npc.rng); } - 2 => { self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right, 7, state, &npc.rng); } - 3 => { self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right, 12, state, &npc.rng); } + 1 => { self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 3, state, &npc.rng); } + 2 => { self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 7, state, &npc.rng); } + 3 => { self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 12, state, &npc.rng); } _ => {} }; @@ -297,9 +298,9 @@ impl NPCList { state.game_flags.set(npc.flag_num as usize, true); match npc.size { - 1 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right, 3, state, &npc.rng), - 2 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right, 7, state, &npc.rng), - 3 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right, 12, state, &npc.rng), + 1 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 3, state, &npc.rng), + 2 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 7, state, &npc.rng), + 3 => self.create_death_smoke(npc.x, npc.y, npc.display_bounds.right as usize, 12, state, &npc.rng), _ => {} }; } diff --git a/src/physics.rs b/src/physics.rs index 1b056f1..ae66e2e 100644 --- a/src/physics.rs +++ b/src/physics.rs @@ -25,7 +25,7 @@ pub trait PhysicalEntity { fn offset_x(&self) -> i32 { 0 } fn offset_y(&self) -> i32 { 0 } - fn hit_bounds(&self) -> &Rect; + fn hit_bounds(&self) -> &Rect; fn set_x(&mut self, x: i32); fn set_y(&mut self, y: i32); diff --git a/src/player/mod.rs b/src/player/mod.rs index 9f4e614..7609d12 100644 --- a/src/player/mod.rs +++ b/src/player/mod.rs @@ -57,8 +57,8 @@ pub struct Player { pub flags: Flag, pub equip: Equipment, pub direction: Direction, - pub display_bounds: Rect, - pub hit_bounds: Rect, + pub display_bounds: Rect, + pub hit_bounds: Rect, pub control_mode: ControlMode, pub question: bool, pub booster_fuel: u32, diff --git a/src/player/player_hit.rs b/src/player/player_hit.rs index 5be44fe..a164751 100644 --- a/src/player/player_hit.rs +++ b/src/player/player_hit.rs @@ -38,7 +38,7 @@ impl PhysicalEntity for Player { } #[inline(always)] - fn hit_bounds(&self) -> &Rect { + fn hit_bounds(&self) -> &Rect { &self.hit_bounds } diff --git a/src/player/skin/basic.rs b/src/player/skin/basic.rs index 45adf4d..5611520 100644 --- a/src/player/skin/basic.rs +++ b/src/player/skin/basic.rs @@ -49,7 +49,7 @@ impl PlayerSkin for BasicPlayerSkin { PlayerAnimationState::FallingUpsideDown => 10, }; - let y_offset = if self.direction == Direction::Left { 0 } else { 16 } + let y_offset = if direction == Direction::Left { 0 } else { 16 } + match self.appearance { PlayerAppearanceState::Default => 0, PlayerAppearanceState::MimigaMask => 32, diff --git a/src/profile.rs b/src/profile.rs index 238c280..572d3b5 100644 --- a/src/profile.rs +++ b/src/profile.rs @@ -63,7 +63,7 @@ impl GameProfile { let weapon_type: Option = FromPrimitive::from_u8(weapon.weapon_id as u8); if let Some(wtype) = weapon_type { - let w = game_scene.inventory_player1.add_weapon_data( + game_scene.inventory_player1.add_weapon_data( wtype, weapon.ammo as u16, weapon.max_ammo as u16, diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index e7f327c..bd57f19 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -1,12 +1,12 @@ use log::info; -use num_traits::{abs, clamp}; use crate::caret::CaretType; use crate::common::{Color, Direction, FadeDirection, FadeState, fix9_scale, interpolate_fix9_scale, Rect}; use crate::components::boss_life_bar::BossLifeBar; -use crate::components::draw_common::{Alignment, draw_number}; +use crate::components::draw_common::{Alignment}; use crate::components::flash::Flash; use crate::components::hud::HUD; +use crate::components::inventory::InventoryUI; use crate::components::stage_select::StageSelect; use crate::entity::GameEntity; use crate::frame::{Frame, UpdateTarget}; @@ -26,13 +26,12 @@ use crate::player::{Player, TargetPlayer}; use crate::rng::XorShift; use crate::scene::Scene; use crate::scene::title_scene::TitleScene; -use crate::shared_game_state::{Season, SharedGameState}; +use crate::shared_game_state::{SharedGameState}; use crate::stage::{BackgroundType, Stage}; use crate::text_script::{ConfirmSelection, ScriptMode, TextScriptExecutionState, TextScriptLine, TextScriptVM}; 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, @@ -53,7 +52,6 @@ pub struct GameScene { pub boss: BossNPC, pub bullet_manager: BulletManager, pub intro_mode: bool, - water_visible: bool, tex_background_name: String, tex_tileset_name: String, map_name_counter: u16, @@ -109,7 +107,6 @@ impl GameScene { boss: BossNPC::new(), bullet_manager: BulletManager::new(), intro_mode: false, - water_visible: true, tex_background_name, tex_tileset_name, map_name_counter: 0, @@ -471,7 +468,7 @@ impl GameScene { // switch version uses 1xxx flag to show a flipped version of face let flip = state.textscript_vm.face > 1000; // x1xx flag shows a talking animation - let talking = (state.textscript_vm.face % 1000) > 100; + let _talking = (state.textscript_vm.face % 1000) > 100; let face_num = state.textscript_vm.face % 100; batch.add_rect_flip( @@ -827,12 +824,12 @@ impl GameScene { let mut rect = Rect::new(0, 0, 16, 16); let (frame_x, frame_y) = self.frame.xy_interpolated(state.frame_time, state.scale); - let tile_start_x = clamp(frame_x as i32 / 16, 0, self.stage.map.width as i32) as usize; - let tile_start_y = clamp(frame_y as i32 / 16, 0, self.stage.map.height as i32) as usize; + let tile_start_x = (frame_x as i32 / 16).clamp(0, self.stage.map.width as i32) as usize; + let tile_start_y = (frame_y as i32 / 16).clamp(0, self.stage.map.height as i32) as usize; let tile_end_x = - clamp((frame_x as i32 + 8 + state.canvas_size.0 as i32) / 16 + 1, 0, self.stage.map.width as i32) as usize; + ((frame_x as i32 + 8 + state.canvas_size.0 as i32) / 16 + 1).clamp(0, self.stage.map.width as i32) as usize; let tile_end_y = - clamp((frame_y as i32 + 8 + state.canvas_size.1 as i32) / 16 + 1, 0, self.stage.map.height as i32) as usize; + ((frame_y as i32 + 8 + state.canvas_size.1 as i32) / 16 + 1).clamp(0, self.stage.map.height as i32) as usize; if layer == TileLayer::Snack { rect = state.constants.world.snack_rect; @@ -898,7 +895,7 @@ impl GameScene { } if npc.npc_flags.shootable() { - npc.life = clamp((npc.life as i32).saturating_sub(bullet.damage as i32), 0, u16::MAX as i32) as u16; + npc.life = (npc.life as i32).saturating_sub(bullet.damage as i32).clamp(0, u16::MAX as i32) as u16; if npc.life == 0 { if npc.npc_flags.show_damage() { @@ -1000,7 +997,7 @@ impl GameScene { npc = unsafe { self.boss.parts.get_unchecked_mut(0) }; } - npc.life = clamp((npc.life as i32).saturating_sub(bullet.damage as i32), 0, u16::MAX as i32) as u16; + npc.life = (npc.life as i32).saturating_sub(bullet.damage as i32).clamp(0, u16::MAX as i32) as u16; if npc.life == 0 { npc.life = npc.id; @@ -1017,7 +1014,7 @@ impl GameScene { self.npc_list.create_death_smoke( npc.x, npc.y, - npc.display_bounds.right, + npc.display_bounds.right as usize, destroy_count, state, &npc.rng, @@ -1156,14 +1153,14 @@ impl GameScene { UpdateTarget::Player => { if self.player2.cond.alive() && !self.player2.cond.hidden() - && abs(self.player1.x - self.player2.x) < 240 * 0x200 - && abs(self.player1.y - self.player2.y) < 200 * 0x200 + && (self.player1.x - self.player2.x).abs() < 240 * 0x200 + && (self.player1.y - self.player2.y).abs() < 200 * 0x200 { self.frame.target_x = (self.player1.target_x * 2 + self.player2.target_x) / 3; self.frame.target_y = (self.player1.target_y * 2 + self.player2.target_y) / 3; - self.frame.target_x = clamp(self.frame.target_x, self.player1.x - 0x8000, self.player1.x + 0x8000); - self.frame.target_y = clamp(self.frame.target_y, self.player1.y, self.player1.y); + self.frame.target_x = self.frame.target_x.clamp(self.player1.x - 0x8000, self.player1.x + 0x8000); + self.frame.target_y = self.frame.target_y.clamp(self.player1.y, self.player1.y); } else { self.frame.target_x = self.player1.target_x; self.frame.target_y = self.player1.target_y; @@ -1189,8 +1186,18 @@ impl GameScene { self.frame.update(state, &self.stage); if state.control_flags.control_enabled() { - 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.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))?; @@ -1214,7 +1221,7 @@ impl GameScene { } { - let hit_rect_size = clamp(npc.hit_rect_size(), 1, 4); + let hit_rect_size = npc.hit_rect_size().clamp(1, 4); let hit_rect_size = hit_rect_size * hit_rect_size; let x = (npc.x + npc.offset_x()) / (16 * 0x200); @@ -1246,10 +1253,11 @@ impl GameScene { } let text = format!("{}:{}:{}", npc.id, npc.npc_type, npc.action_num); - state.font.draw_colored_text( + state.font.draw_colored_text_scaled( text.chars(), ((npc.x - self.frame.x) / 0x200) as f32, ((npc.y - self.frame.y) / 0x200) as f32, + 0.5, ((npc.id & 0xf0) as u8, (npc.cond.0 >> 8) as u8, (npc.id & 0x0f << 4) as u8, 255), &state.constants, &mut state.texture_set, @@ -1354,7 +1362,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)?, + ScriptMode::Inventory => self.inventory_ui.tick(state, (&mut self.player1, &mut self.inventory_player1))?, _ => {} } @@ -1481,67 +1489,68 @@ impl Scene for GameScene { .scale(Vector2::new(1.0 / state.scale, 1.0 / state.scale)))?;*/ self.draw_black_bars(state, ctx)?; - if state.control_flags.control_enabled() { - self.hud_player1.draw(state, ctx, &self.frame)?; - self.hud_player2.draw(state, ctx, &self.frame)?; - self.boss_life_bar.draw(state, ctx, &self.frame)?; + match state.textscript_vm.mode { + ScriptMode::Map if state.control_flags.control_enabled() => { + self.hud_player1.draw(state, ctx, &self.frame)?; + self.hud_player2.draw(state, ctx, &self.frame)?; + self.boss_life_bar.draw(state, ctx, &self.frame)?; - if self.player2.cond.alive() && !self.player2.cond.hidden() { - let y = interpolate_fix9_scale( - self.player2.prev_y - self.frame.prev_y, - self.player2.y - self.frame.y, - state.frame_time, - ); - let y = clamp(y, 8.0, state.canvas_size.1 - 8.0 - state.font.line_height(&state.constants)); + if self.player2.cond.alive() && !self.player2.cond.hidden() { + let y = interpolate_fix9_scale( + self.player2.prev_y - self.frame.prev_y, + self.player2.y - self.frame.y, + state.frame_time, + ); + let y = y.clamp(8.0, state.canvas_size.1 - 8.0 - state.font.line_height(&state.constants)); - if self.player2.x + 8 * 0x200 < self.frame.x { - state.font.draw_colored_text( - P2_LEFT_TEXT.chars(), - 9.0, - y + 1.0, - (0, 0, 130, 255), - &state.constants, - &mut state.texture_set, - ctx, - )?; + if self.player2.x + 8 * 0x200 < self.frame.x { + state.font.draw_colored_text( + P2_LEFT_TEXT.chars(), + 9.0, + y + 1.0, + (0, 0, 130, 255), + &state.constants, + &mut state.texture_set, + ctx, + )?; - state.font.draw_colored_text( - P2_LEFT_TEXT.chars(), - 8.0, - y, - (96, 96, 255, 255), - &state.constants, - &mut state.texture_set, - ctx, - )?; - } else if self.player2.x - 8 * 0x200 > self.frame.x + state.canvas_size.0 as i32 * 0x200 { - let width = state.font.text_width(P2_RIGHT_TEXT.chars(), &state.constants); + state.font.draw_colored_text( + P2_LEFT_TEXT.chars(), + 8.0, + y, + (96, 96, 255, 255), + &state.constants, + &mut state.texture_set, + ctx, + )?; + } else if self.player2.x - 8 * 0x200 > self.frame.x + state.canvas_size.0 as i32 * 0x200 { + let width = state.font.text_width(P2_RIGHT_TEXT.chars(), &state.constants); - state.font.draw_colored_text( - P2_RIGHT_TEXT.chars(), - state.canvas_size.0 - width - 8.0 + 1.0, - y + 1.0, - (0, 0, 130, 255), - &state.constants, - &mut state.texture_set, - ctx, - )?; + state.font.draw_colored_text( + P2_RIGHT_TEXT.chars(), + state.canvas_size.0 - width - 8.0 + 1.0, + y + 1.0, + (0, 0, 130, 255), + &state.constants, + &mut state.texture_set, + ctx, + )?; - state.font.draw_colored_text( - P2_RIGHT_TEXT.chars(), - state.canvas_size.0 - width - 8.0, - y, - (96, 96, 255, 255), - &state.constants, - &mut state.texture_set, - ctx, - )?; + state.font.draw_colored_text( + P2_RIGHT_TEXT.chars(), + state.canvas_size.0 - width - 8.0, + y, + (96, 96, 255, 255), + &state.constants, + &mut state.texture_set, + ctx, + )?; + } } } - } - - if state.textscript_vm.mode == ScriptMode::StageSelect { - self.stage_select.draw(state, ctx, &self.frame)?; + ScriptMode::StageSelect => self.stage_select.draw(state, ctx, &self.frame)?, + ScriptMode::Inventory => self.inventory_ui.draw(state, ctx, &self.frame)?, + _ => {} } self.draw_fade(state, ctx)?; diff --git a/src/scene/no_data_scene.rs b/src/scene/no_data_scene.rs index 853aaab..0b83090 100644 --- a/src/scene/no_data_scene.rs +++ b/src/scene/no_data_scene.rs @@ -1,11 +1,11 @@ use crate::framework::context::Context; use crate::framework::error::{GameResult, GameError}; -use crate::common::Rect; use crate::scene::Scene; use crate::shared_game_state::SharedGameState; pub struct NoDataScene { + #[cfg(target_os = "android")] flag: bool, err: String, } @@ -13,6 +13,7 @@ pub struct NoDataScene { impl NoDataScene { pub fn new(err: GameError) -> Self { Self { + #[cfg(target_os = "android")] flag: false, err: err.to_string(), } @@ -23,9 +24,12 @@ impl NoDataScene { static REL_URL: &str = "https://github.com/doukutsu-rs/game-data/releases"; impl Scene for NoDataScene { + #[allow(unused)] fn tick(&mut self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { #[cfg(target_os = "android")] { + use crate::common::Rect; + if !self.flag { self.flag = true; let _ = std::fs::create_dir("/sdcard/doukutsu/"); diff --git a/src/scene/title_scene.rs b/src/scene/title_scene.rs index abe75b1..3371226 100644 --- a/src/scene/title_scene.rs +++ b/src/scene/title_scene.rs @@ -10,6 +10,7 @@ use crate::shared_game_state::{SharedGameState, TimingMode}; #[derive(PartialEq, Eq, Copy, Clone)] #[repr(u8)] +#[allow(unused)] enum CurrentMenu { MainMenu, OptionMenu, diff --git a/src/shared_game_state.rs b/src/shared_game_state.rs index ca3c46f..2673426 100644 --- a/src/shared_game_state.rs +++ b/src/shared_game_state.rs @@ -2,20 +2,20 @@ use std::ops::Div; use bitvec::vec::BitVec; use chrono::{Datelike, Local}; -use num_traits::clamp; -use num_traits::real::Real; use crate::bmfont_renderer::BMFontRenderer; use crate::caret::{Caret, CaretType}; use crate::common::{ControlFlags, Direction, FadeState}; use crate::engine_constants::EngineConstants; +use crate::framework::{filesystem, graphics}; use crate::framework::backend::BackendTexture; use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::framework::graphics::{create_texture_mutable, set_render_target}; use crate::framework::keyboard::ScanCode; use crate::framework::vfs::OpenOptions; -use crate::framework::{filesystem, graphics}; +#[cfg(feature = "hooks")] +use crate::hooks::init_hooks; use crate::input::touch_controls::TouchControls; use crate::npc::NPCTable; use crate::profile::GameProfile; @@ -153,6 +153,9 @@ impl SharedGameState { println!("lookup path: {:#?}", texture_set.paths); + #[cfg(feature = "hooks")] + init_hooks(); + Ok(SharedGameState { timing_mode: TimingMode::_50Hz, control_flags: ControlFlags(0), @@ -193,9 +196,7 @@ impl SharedGameState { ScanCode::F3 => self.settings.god_mode = !self.settings.god_mode, ScanCode::F4 => self.settings.infinite_booster = !self.settings.infinite_booster, ScanCode::F5 => self.settings.subpixel_coords = !self.settings.subpixel_coords, - ScanCode::F6 => { - self.settings.motion_interpolation = !self.settings.motion_interpolation - } + ScanCode::F6 => self.settings.motion_interpolation = !self.settings.motion_interpolation, ScanCode::F7 => self.set_speed(1.0), ScanCode::F8 => { if self.settings.speed > 0.2 { @@ -260,11 +261,7 @@ impl SharedGameState { } pub fn save_game(&mut self, game_scene: &mut GameScene, ctx: &mut Context) -> GameResult { - if let Ok(data) = filesystem::open_options( - ctx, - "/Profile.dat", - OpenOptions::new().write(true).create(true), - ) { + if let Ok(data) = filesystem::open_options(ctx, "/Profile.dat", OpenOptions::new().write(true).create(true)) { let profile = GameProfile::dump(self, game_scene); profile.write_save(data)?; } else { @@ -315,10 +312,7 @@ impl SharedGameState { pub fn handle_resize(&mut self, ctx: &mut Context) -> GameResult { self.screen_size = graphics::screen_size(ctx); self.scale = self.screen_size.1.div(230.0).floor().max(1.0); - self.canvas_size = ( - self.screen_size.0 / self.scale, - self.screen_size.1 / self.scale, - ); + self.canvas_size = (self.screen_size.0 / self.scale, self.screen_size.1 / self.scale); let (width, height) = (self.screen_size.0 as u16, self.screen_size.1 as u16); @@ -338,12 +332,11 @@ impl SharedGameState { } pub fn create_caret(&mut self, x: i32, y: i32, ctype: CaretType, direct: Direction) { - self.carets - .push(Caret::new(x, y, ctype, direct, &self.constants)); + self.carets.push(Caret::new(x, y, ctype, direct, &self.constants)); } pub fn set_speed(&mut self, value: f64) { - self.settings.speed = clamp(value, 0.1, 3.0); + self.settings.speed = value.clamp(0.1, 3.0); self.frame_time = 0.0; } diff --git a/src/sound/mod.rs b/src/sound/mod.rs index dc557fb..5dcfce6 100644 --- a/src/sound/mod.rs +++ b/src/sound/mod.rs @@ -248,7 +248,7 @@ where let mut state = PlaybackState::Stopped; let mut saved_state: PlaybackStateType = PlaybackStateType::None; let mut speed = 1.0; - let mut org_engine = OrgPlaybackEngine::new(&bank); + let mut org_engine = OrgPlaybackEngine::new(); #[cfg(feature = "ogg-playback")] let mut ogg_engine = OggPlaybackEngine::new(); let mut pixtone = PixTonePlayback::new(); diff --git a/src/sound/ogg_playback.rs b/src/sound/ogg_playback.rs index 6f391dc..a45b432 100644 --- a/src/sound/ogg_playback.rs +++ b/src/sound/ogg_playback.rs @@ -39,7 +39,9 @@ impl OggPlaybackEngine { } } - pub fn set_sample_rate(&mut self, sample_rate: usize) {} + pub fn set_sample_rate(&mut self, sample_rate: usize) { + self.output_format.sample_rate = sample_rate as u32; + } pub fn get_state(&self) -> SavedOggPlaybackState { SavedOggPlaybackState { @@ -120,13 +122,13 @@ impl OggPlaybackEngine { let mut buf = match music.read_dec_packet_itl() { Ok(Some(buf)) => buf, Ok(None) => { - if let Err(e) = music.seek_absgp_pg(0) { + if let Err(_) = music.seek_absgp_pg(0) { vec![0, 1000] } else { return; } } - Err(e) => { + Err(_) => { vec![0, 1000] } }; @@ -145,11 +147,11 @@ impl OggPlaybackEngine { } } - fn resample_buffer(&self, mut data: Vec, sample_rate: u32, channels: u8) -> Vec { + fn resample_buffer(&self, mut data: Vec, sample_rate: u32, _channels: u8) -> Vec { if sample_rate != self.output_format.sample_rate { let mut tmp_data = Vec::new(); let mut pos = 0.0; - let mut phase = sample_rate as f32 / self.output_format.sample_rate as f32; + let phase = sample_rate as f32 / self.output_format.sample_rate as f32; loop { if pos >= data.len() as f32 { diff --git a/src/sound/org_playback.rs b/src/sound/org_playback.rs index def61ce..918ce20 100644 --- a/src/sound/org_playback.rs +++ b/src/sound/org_playback.rs @@ -41,7 +41,7 @@ impl Clone for SavedOrganyaPlaybackState { } impl OrgPlaybackEngine { - pub fn new(samples: &SoundBank) -> Self { + pub fn new() -> Self { let mut buffers: [MaybeUninit; 136] = unsafe { MaybeUninit::uninit().assume_init() }; for buffer in buffers.iter_mut() { diff --git a/src/sound/pixtone.rs b/src/sound/pixtone.rs index 5c84cea..5582e32 100644 --- a/src/sound/pixtone.rs +++ b/src/sound/pixtone.rs @@ -206,6 +206,7 @@ pub struct PixTonePlayback { pub playback_state: Vec, } +#[allow(unused)] impl PixTonePlayback { pub fn new() -> PixTonePlayback { PixTonePlayback { diff --git a/src/texture_set.rs b/src/texture_set.rs index 9b9976a..e8943d0 100644 --- a/src/texture_set.rs +++ b/src/texture_set.rs @@ -128,7 +128,7 @@ impl SizedBatch { } pub fn add_rect_scaled(&mut self, mut x: f32, mut y: f32, scale_x: f32, scale_y: f32, rect: &common::Rect) { - if (rect.right - rect.left) == 0 || (rect.bottom - rect.top) == 0 { + if (rect.right.saturating_sub(rect.left)) == 0 || (rect.bottom.saturating_sub(rect.top)) == 0 { return; } diff --git a/src/weapon/bubbler.rs b/src/weapon/bubbler.rs index 5276e6e..8395604 100644 --- a/src/weapon/bubbler.rs +++ b/src/weapon/bubbler.rs @@ -3,7 +3,6 @@ use crate::common::Direction; use crate::player::{Player, TargetPlayer}; use crate::shared_game_state::SharedGameState; use crate::weapon::bullet::BulletManager; -use crate::weapon::WeaponLevel::Level1; use crate::weapon::{Weapon, WeaponLevel}; impl Weapon { diff --git a/src/weapon/bullet.rs b/src/weapon/bullet.rs index dc9c6ea..f641f6a 100644 --- a/src/weapon/bullet.rs +++ b/src/weapon/bullet.rs @@ -1,5 +1,3 @@ -use std::ops::{Add, Sub}; - use num_traits::clamp; use crate::caret::CaretType; @@ -105,8 +103,8 @@ pub struct Bullet { pub anim_counter: u16, pub action_num: u16, pub action_counter: u16, - pub hit_bounds: Rect, - pub display_bounds: Rect, + pub hit_bounds: Rect, + pub display_bounds: Rect, } impl Bullet { @@ -152,16 +150,16 @@ impl Bullet { action_num: 0, action_counter: 0, display_bounds: Rect::new( - bullet.display_bounds.left as usize * 0x200, - bullet.display_bounds.top as usize * 0x200, - bullet.display_bounds.right as usize * 0x200, - bullet.display_bounds.bottom as usize * 0x200, + bullet.display_bounds.left as u32 * 0x200, + bullet.display_bounds.top as u32 * 0x200, + bullet.display_bounds.right as u32 * 0x200, + bullet.display_bounds.bottom as u32 * 0x200, ), hit_bounds: Rect::new( - bullet.block_hit_width as usize * 0x200, - bullet.block_hit_height as usize * 0x200, - bullet.block_hit_width as usize * 0x200, - bullet.block_hit_height as usize * 0x200, + bullet.block_hit_width as u32 * 0x200, + bullet.block_hit_height as u32 * 0x200, + bullet.block_hit_width as u32 * 0x200, + bullet.block_hit_height as u32 * 0x200, ), } } @@ -1515,7 +1513,7 @@ impl PhysicalEntity for Bullet { } #[inline(always)] - fn hit_bounds(&self) -> &Rect { + fn hit_bounds(&self) -> &Rect { &self.hit_bounds }