diff --git a/Cargo.toml b/Cargo.toml index 67291ae..2bd2505 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,7 @@ opt-level = 1 [features] default = ["scripting", "backend-sdl"] -backend-sdl = ["sdl2", "sdl2-sys", "imgui-sdl2"] +backend-sdl = ["sdl2"] backend-gfx = ["winit", "imgui-gfx-renderer", "imgui-winit-support"] scripting = ["lua-ffi"] editor = [] @@ -53,10 +53,9 @@ case_insensitive_hashmap = "1.0.0" chrono = "0.4" cpal = { git = "https://github.com/doukutsu-rs/cpal.git", branch = "android-support" } directories = "3" -imgui = "0.6.0" -imgui-gfx-renderer = { version = "0.6.0", optional = true } -imgui-winit-support = { version = "0.6.0", default-features = false, features = ["winit-23"], optional = true } -imgui-sdl2 = { version = "0.13.0", optional = true } +imgui = "0.7.0" +imgui-gfx-renderer = { version = "0.7.0", optional = true } +imgui-winit-support = { version = "0.7.0", default-features = false, features = ["winit-23"], optional = true } image = { version = "0.22", default-features = false, features = ["png_codec", "pnm", "bmp"] } itertools = "0.9.0" lazy_static = "1.4.0" @@ -67,8 +66,7 @@ num-derive = "0.3.2" num-traits = "0.2.12" paste = "1.0.0" pretty_env_logger = "0.4.0" -sdl2 = { version = "0.34.3", optional = true, features = ["unsafe_textures", "bundled", "gfx"] } -sdl2-sys = { version = "0.34", optional = true, features = ["bundled", "gfx"] } +sdl2 = { version = "0.34", optional = true, features = ["unsafe_textures", "bundled", "gfx", "static-link"] } serde = { version = "1", features = ["derive"] } serde_derive = "1" serde_yaml = "0.8" diff --git a/src/common.rs b/src/common.rs index b2acea1..44f425c 100644 --- a/src/common.rs +++ b/src/common.rs @@ -388,22 +388,6 @@ pub struct Color { pub a: f32, } -/// White -pub const WHITE: Color = Color { - r: 1.0, - g: 1.0, - b: 1.0, - a: 1.0, -}; - -/// Black -pub const BLACK: Color = Color { - r: 0.0, - g: 0.0, - b: 0.0, - a: 1.0, -}; - impl Color { /// Create a new `Color` from four `f32`'s in the range `[0.0-1.0]` pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self { diff --git a/src/framework/backend.rs b/src/framework/backend.rs index be95261..13975fd 100644 --- a/src/framework/backend.rs +++ b/src/framework/backend.rs @@ -52,5 +52,6 @@ pub fn init_backend() -> GameResult> { pub enum SpriteBatchCommand { DrawRect(Rect, Rect), + DrawRectFlip(Rect, Rect, bool, bool), DrawRectTinted(Rect, Rect, Color), } diff --git a/src/framework/backend_sdl2.rs b/src/framework/backend_sdl2.rs index 168db77..e96aa48 100644 --- a/src/framework/backend_sdl2.rs +++ b/src/framework/backend_sdl2.rs @@ -3,22 +3,23 @@ use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; -use imgui::{DrawCmd, DrawData, ImString, TextureId, Ui}; use imgui::internal::RawWrapper; -use sdl2::{EventPump, keyboard, pixels, Sdl}; +use imgui::{ConfigFlags, DrawCmd, DrawData, ImString, Key, MouseCursor, TextureId, Ui}; use sdl2::event::{Event, WindowEvent}; -use sdl2::gfx::primitives::DrawRenderer; use sdl2::keyboard::Scancode; +use sdl2::mouse::{Cursor, SystemCursor}; use sdl2::pixels::PixelFormatEnum; use sdl2::render::{Texture, TextureCreator, WindowCanvas}; -use sdl2::surface::Surface; use sdl2::video::WindowContext; +use sdl2::{keyboard, pixels, EventPump, Sdl}; use crate::common::Color; -use crate::framework::backend::{Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand}; +use crate::framework::backend::{ + Backend, BackendEventLoop, BackendRenderer, BackendTexture, SpriteBatchCommand, +}; use crate::framework::context::Context; use crate::framework::error::{GameError, GameResult}; -use crate::framework::graphics::{BlendMode, imgui_context}; +use crate::framework::graphics::{imgui_context, BlendMode}; use crate::framework::keyboard::ScanCode; use crate::framework::ui::init_imgui; use crate::Game; @@ -31,9 +32,7 @@ impl SDL2Backend { pub fn new() -> GameResult> { let context = sdl2::init().map_err(|e| GameError::WindowError(e))?; - let backend = SDL2Backend { - context, - }; + let backend = SDL2Backend { context }; Ok(Box::new(backend)) } @@ -62,13 +61,15 @@ impl SDL2EventLoop { let event_pump = sdl.event_pump().map_err(|e| GameError::WindowError(e))?; let video = sdl.video().map_err(|e| GameError::WindowError(e))?; - let window = video.window("Cave Story (doukutsu-rs)", 640, 480) + let window = video + .window("Cave Story (doukutsu-rs)", 640, 480) .position_centered() .resizable() .build() .map_err(|e| GameError::WindowError(e.to_string()))?; - let canvas = window.into_canvas() + let canvas = window + .into_canvas() .accelerated() .present_vsync() .build() @@ -96,7 +97,10 @@ impl BackendEventLoop for SDL2EventLoop { let (imgui, imgui_sdl2) = unsafe { let renderer: &Box = std::mem::transmute(ctx.renderer.as_ref().unwrap()); - (&mut *renderer.imgui.as_ptr(), &mut *renderer.imgui_event.as_ptr()) + ( + &mut *renderer.imgui.as_ptr(), + &mut *renderer.imgui_event.as_ptr(), + ) }; { @@ -115,27 +119,30 @@ impl BackendEventLoop for SDL2EventLoop { Event::Quit { .. } => { state.shutdown(); } - Event::Window { win_event, .. } => { - match win_event { - WindowEvent::Shown => {} - WindowEvent::Hidden => {} - WindowEvent::SizeChanged(width, height) => { - ctx.screen_size = (width.max(1) as f32, height.max(1) as f32); + Event::Window { win_event, .. } => match win_event { + WindowEvent::Shown => {} + WindowEvent::Hidden => {} + WindowEvent::SizeChanged(width, height) => { + ctx.screen_size = (width.max(1) as f32, height.max(1) as f32); - if let Some(renderer) = ctx.renderer.as_ref() { - if let Ok(imgui) = renderer.imgui() { - imgui.io_mut().display_size = [ctx.screen_size.0, ctx.screen_size.1]; - } + if let Some(renderer) = ctx.renderer.as_ref() { + if let Ok(imgui) = renderer.imgui() { + imgui.io_mut().display_size = + [ctx.screen_size.0, ctx.screen_size.1]; } - state.handle_resize(ctx); } - _ => {} + state.handle_resize(ctx); } - } - Event::KeyDown { scancode, repeat, .. } => { + _ => {} + }, + Event::KeyDown { + scancode, repeat, .. + } => { if let Some(scancode) = scancode { if let Some(drs_scan) = conv_scancode(scancode) { - game.key_down_event(drs_scan, repeat); + if !repeat { + state.process_debug_keys(drs_scan); + } ctx.keyboard_context.set_key(drs_scan, true); } } @@ -167,7 +174,11 @@ impl BackendEventLoop for SDL2EventLoop { state.frame_time = 0.0; } - imgui_sdl2.prepare_frame(imgui.io_mut(), self.refs.borrow().canvas.window(), &self.event_pump.mouse_state()); + imgui_sdl2.prepare_frame( + imgui.io_mut(), + self.refs.borrow().canvas.window(), + &self.event_pump.mouse_state(), + ); game.draw(ctx).unwrap(); } } @@ -180,7 +191,7 @@ impl BackendEventLoop for SDL2EventLoop { struct SDL2Renderer { refs: Rc>, imgui: Rc>, - imgui_event: Rc>, + imgui_event: Rc>, imgui_textures: HashMap, } @@ -192,42 +203,49 @@ impl SDL2Renderer { imgui.set_renderer_name(ImString::new("SDL2Renderer")); { - let mut refs = refs.clone(); + let refs = refs.clone(); let mut fonts = imgui.fonts(); let id = fonts.tex_id; let font_tex = fonts.build_rgba32_texture(); - let mut texture = refs.borrow_mut().texture_creator + let mut texture = refs + .borrow_mut() + .texture_creator .create_texture_streaming(PixelFormatEnum::RGBA32, font_tex.width, font_tex.height) .map_err(|e| GameError::RenderError(e.to_string()))?; texture.set_blend_mode(sdl2::render::BlendMode::Blend); - texture.with_lock(None, |buffer: &mut [u8], pitch: usize| { - for y in 0..(font_tex.height as usize) { - for x in 0..(font_tex.width as usize) { - let offset = y * pitch + x * 4; - let data_offset = (y * font_tex.width as usize + x) * 4; + texture + .with_lock(None, |buffer: &mut [u8], pitch: usize| { + for y in 0..(font_tex.height as usize) { + for x in 0..(font_tex.width as usize) { + let offset = y * pitch + x * 4; + let data_offset = (y * font_tex.width as usize + x) * 4; - buffer[offset] = font_tex.data[data_offset]; - buffer[offset + 1] = font_tex.data[data_offset + 1]; - buffer[offset + 2] = font_tex.data[data_offset + 2]; - buffer[offset + 3] = font_tex.data[data_offset + 3]; + buffer[offset] = font_tex.data[data_offset]; + buffer[offset + 1] = font_tex.data[data_offset + 1]; + buffer[offset + 2] = font_tex.data[data_offset + 2]; + buffer[offset + 3] = font_tex.data[data_offset + 3]; + } } - } - }).map_err(|e| GameError::RenderError(e.to_string()))?; + }) + .map_err(|e| GameError::RenderError(e.to_string()))?; - imgui_textures.insert(id, SDL2Texture { - refs: refs.clone(), - texture: Some(texture), - width: font_tex.width as u16, - height: font_tex.height as u16, - commands: vec![], - }); + imgui_textures.insert( + id, + SDL2Texture { + refs: refs.clone(), + texture: Some(texture), + width: font_tex.width as u16, + height: font_tex.height as u16, + commands: vec![], + }, + ); } let imgui_sdl2 = unsafe { let refs = &mut *refs.as_ptr(); - imgui_sdl2::ImguiSdl2::new(&mut imgui, refs.canvas.window()) + ImguiSdl2::new(&mut imgui, refs.canvas.window()) }; Ok(Box::new(SDL2Renderer { @@ -244,7 +262,10 @@ fn to_sdl(color: Color) -> pixels::Color { pixels::Color::RGBA(r, g, b, a) } -unsafe fn set_raw_target(renderer: *mut sdl2::sys::SDL_Renderer, raw_texture: *mut sdl2::sys::SDL_Texture) -> GameResult { +unsafe fn set_raw_target( + renderer: *mut sdl2::sys::SDL_Renderer, + raw_texture: *mut sdl2::sys::SDL_Texture, +) -> GameResult { if sdl2::sys::SDL_SetRenderTarget(renderer, raw_texture) == 0 { Ok(()) } else { @@ -253,11 +274,23 @@ unsafe fn set_raw_target(renderer: *mut sdl2::sys::SDL_Renderer, raw_texture: *m } fn min3(x: f32, y: f32, z: f32) -> f32 { - if x < y && x < z { x } else if y < z { y } else { z } + if x < y && x < z { + x + } else if y < z { + y + } else { + z + } } fn max3(x: f32, y: f32, z: f32) -> f32 { - if x > y && x > z { x } else if y > z { y } else { z } + if x > y && x > z { + x + } else if y > z { + y + } else { + z + } } impl BackendRenderer for SDL2Renderer { @@ -276,10 +309,15 @@ impl BackendRenderer for SDL2Renderer { Ok(()) } - fn create_texture_mutable(&mut self, width: u16, height: u16) -> GameResult> { - let mut refs = self.refs.borrow_mut(); + fn create_texture_mutable( + &mut self, + width: u16, + height: u16, + ) -> GameResult> { + let refs = self.refs.borrow_mut(); - let mut texture = refs.texture_creator + let texture = refs + .texture_creator .create_texture_target(PixelFormatEnum::RGBA32, width as u32, height as u32) .map_err(|e| GameError::RenderError(e.to_string()))?; @@ -292,27 +330,35 @@ impl BackendRenderer for SDL2Renderer { })) } - fn create_texture(&mut self, width: u16, height: u16, data: &[u8]) -> GameResult> { - let mut refs = self.refs.borrow_mut(); + fn create_texture( + &mut self, + width: u16, + height: u16, + data: &[u8], + ) -> GameResult> { + let refs = self.refs.borrow_mut(); - let mut texture = refs.texture_creator + let mut texture = refs + .texture_creator .create_texture_streaming(PixelFormatEnum::RGBA32, width as u32, height as u32) .map_err(|e| GameError::RenderError(e.to_string()))?; texture.set_blend_mode(sdl2::render::BlendMode::Blend); - texture.with_lock(None, |buffer: &mut [u8], pitch: usize| { - for y in 0..(height as usize) { - for x in 0..(width as usize) { - let offset = y * pitch + x * 4; - let data_offset = (y * width as usize + x) * 4; + texture + .with_lock(None, |buffer: &mut [u8], pitch: usize| { + for y in 0..(height as usize) { + for x in 0..(width as usize) { + let offset = y * pitch + x * 4; + let data_offset = (y * width as usize + x) * 4; - buffer[offset] = data[data_offset]; - buffer[offset + 1] = data[data_offset + 1]; - buffer[offset + 2] = data[data_offset + 2]; - buffer[offset + 3] = data[data_offset + 3]; + buffer[offset] = data[data_offset]; + buffer[offset + 1] = data[data_offset + 1]; + buffer[offset + 2] = data[data_offset + 2]; + buffer[offset + 3] = data[data_offset + 3]; + } } - } - }).map_err(|e| GameError::RenderError(e.to_string()))?; + }) + .map_err(|e| GameError::RenderError(e.to_string()))?; Ok(Box::new(SDL2Texture { refs: self.refs.clone(), @@ -344,23 +390,21 @@ impl BackendRenderer for SDL2Renderer { let sdl2_texture: &Box = std::mem::transmute(texture); if let Some(target) = sdl2_texture.texture.as_ref() { - set_raw_target(renderer, target.raw()); + set_raw_target(renderer, target.raw())?; } else { - set_raw_target(renderer, std::ptr::null_mut()); + set_raw_target(renderer, std::ptr::null_mut())?; } - } + }, None => unsafe { - set_raw_target(renderer, std::ptr::null_mut()); - } + set_raw_target(renderer, std::ptr::null_mut())?; + }, } Ok(()) } fn imgui(&self) -> GameResult<&mut imgui::Context> { - unsafe { - Ok(&mut *self.imgui.as_ptr()) - } + unsafe { Ok(&mut *self.imgui.as_ptr()) } } fn render_imgui(&mut self, draw_data: &DrawData) -> GameResult { @@ -391,9 +435,12 @@ impl BackendRenderer for SDL2Renderer { continue; } - let v1 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i] as usize]; - let v2 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 1] as usize]; - let v3 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 2] as usize]; + let v1 = draw_list.vtx_buffer()[cmd_params.vtx_offset + + idx_buffer[cmd_params.idx_offset + i] as usize]; + let v2 = draw_list.vtx_buffer()[cmd_params.vtx_offset + + idx_buffer[cmd_params.idx_offset + i + 1] as usize]; + let v3 = draw_list.vtx_buffer()[cmd_params.vtx_offset + + idx_buffer[cmd_params.idx_offset + i + 2] as usize]; vert_x[0] = (v1.pos[0] - 0.5) as i16; vert_y[0] = (v1.pos[1] - 0.5) as i16; @@ -404,27 +451,30 @@ impl BackendRenderer for SDL2Renderer { #[allow(clippy::float_cmp)] if i < count - 3 { - let v4 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 3] as usize]; - let v5 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 4] as usize]; - let v6 = draw_list.vtx_buffer()[cmd_params.vtx_offset + idx_buffer[cmd_params.idx_offset + i + 5] as usize]; + let v4 = draw_list.vtx_buffer()[cmd_params.vtx_offset + + idx_buffer[cmd_params.idx_offset + i + 3] as usize]; + let v5 = draw_list.vtx_buffer()[cmd_params.vtx_offset + + idx_buffer[cmd_params.idx_offset + i + 4] as usize]; + let v6 = draw_list.vtx_buffer()[cmd_params.vtx_offset + + idx_buffer[cmd_params.idx_offset + i + 5] as usize]; min[0] = min3(v1.pos[0], v2.pos[0], v3.pos[0]); min[1] = min3(v1.pos[1], v2.pos[1], v3.pos[1]); max[0] = max3(v1.pos[0], v2.pos[0], v3.pos[0]); max[1] = max3(v1.pos[1], v2.pos[1], v3.pos[1]); - is_rect = (v1.pos[0] == min[0] || v1.pos[0] == max[0]) && - (v1.pos[1] == min[1] || v1.pos[1] == max[1]) && - (v2.pos[0] == min[0] || v2.pos[0] == max[0]) && - (v2.pos[1] == min[1] || v2.pos[1] == max[1]) && - (v3.pos[0] == min[0] || v3.pos[0] == max[0]) && - (v3.pos[1] == min[1] || v3.pos[1] == max[1]) && - (v4.pos[0] == min[0] || v4.pos[0] == max[0]) && - (v4.pos[1] == min[1] || v4.pos[1] == max[1]) && - (v5.pos[0] == min[0] || v5.pos[0] == max[0]) && - (v5.pos[1] == min[1] || v5.pos[1] == max[1]) && - (v6.pos[0] == min[0] || v6.pos[0] == max[0]) && - (v6.pos[1] == min[1] || v6.pos[1] == max[1]); + is_rect = (v1.pos[0] == min[0] || v1.pos[0] == max[0]) + && (v1.pos[1] == min[1] || v1.pos[1] == max[1]) + && (v2.pos[0] == min[0] || v2.pos[0] == max[0]) + && (v2.pos[1] == min[1] || v2.pos[1] == max[1]) + && (v3.pos[0] == min[0] || v3.pos[0] == max[0]) + && (v3.pos[1] == min[1] || v3.pos[1] == max[1]) + && (v4.pos[0] == min[0] || v4.pos[0] == max[0]) + && (v4.pos[1] == min[1] || v4.pos[1] == max[1]) + && (v5.pos[0] == min[0] || v5.pos[0] == max[0]) + && (v5.pos[1] == min[1] || v5.pos[1] == max[1]) + && (v6.pos[0] == min[0] || v6.pos[0] == max[0]) + && (v6.pos[1] == min[1] || v6.pos[1] == max[1]); if is_rect { tex_pos[0] = min3(v1.uv[0], v2.uv[0], v3.uv[0]); @@ -434,35 +484,40 @@ impl BackendRenderer for SDL2Renderer { } } - if let Some(surf) = self.imgui_textures.get_mut(&cmd_params.texture_id) { - unsafe { - if is_rect { - let src = sdl2::rect::Rect::new((tex_pos[0] * surf.width as f32) as i32, - (tex_pos[1] * surf.height as f32) as i32, - ((tex_pos[2] - tex_pos[0]) * surf.width as f32) as u32, - ((tex_pos[3] - tex_pos[1]) * surf.height as f32) as u32); - let dest = sdl2::rect::Rect::new(min[0] as i32, - min[1] as i32, - (max[0] - min[0]) as u32, - (max[1] - min[1]) as u32); + if let Some(surf) = self.imgui_textures.get_mut(&cmd_params.texture_id) + { + if is_rect { + let src = sdl2::rect::Rect::new( + (tex_pos[0] * surf.width as f32) as i32, + (tex_pos[1] * surf.height as f32) as i32, + ((tex_pos[2] - tex_pos[0]) * surf.width as f32) as u32, + ((tex_pos[3] - tex_pos[1]) * surf.height as f32) as u32, + ); + let dest = sdl2::rect::Rect::new( + min[0] as i32, + min[1] as i32, + (max[0] - min[0]) as u32, + (max[1] - min[1]) as u32, + ); - let tex = surf.texture.as_mut().unwrap(); - tex.set_color_mod(v1.col[0], v1.col[1], v1.col[2]); - tex.set_alpha_mod(v1.col[3]); + let tex = surf.texture.as_mut().unwrap(); + tex.set_color_mod(v1.col[0], v1.col[1], v1.col[2]); + tex.set_alpha_mod(v1.col[3]); - refs.canvas.copy(tex, src, dest); - } else { - sdl2::sys::gfx::primitives::filledPolygonRGBA( - refs.canvas.raw(), - vert_x.as_ptr(), - vert_y.as_ptr(), - 3, - v1.col[0], - v1.col[1], - v1.col[2], - v1.col[3], - ); - } + refs.canvas + .copy(tex, src, dest) + .map_err(|e| GameError::RenderError(e.to_string()))?; + } else { + /*sdl2::sys::gfx::primitives::filledPolygonRGBA( + refs.canvas.raw(), + vert_x.as_ptr(), + vert_y.as_ptr(), + 3, + v1.col[0], + v1.col[1], + v1.col[2], + v1.col[3], + );*/ } } } @@ -472,7 +527,7 @@ impl BackendRenderer for SDL2Renderer { DrawCmd::ResetRenderState => {} DrawCmd::RawCallback { callback, raw_cmd } => unsafe { callback(draw_list.raw(), raw_cmd) - } + }, } } } @@ -480,7 +535,7 @@ impl BackendRenderer for SDL2Renderer { Ok(()) } - fn prepare_frame<'ui>(&self, ui: &Ui<'ui>) -> GameResult { + fn prepare_frame<'ui>(&self, _ui: &Ui<'ui>) -> GameResult { Ok(()) } } @@ -520,9 +575,22 @@ impl BackendTexture for SDL2Texture { texture.set_alpha_mod(255); texture.set_blend_mode(refs.blend_mode); - refs.canvas.copy(texture, - Some(sdl2::rect::Rect::new(src.left.round() as i32, src.top.round() as i32, src.width().round() as u32, src.height().round() as u32)), - Some(sdl2::rect::Rect::new(dest.left.round() as i32, dest.top.round() as i32, dest.width().round() as u32, dest.height().round() as u32))) + refs.canvas + .copy( + texture, + Some(sdl2::rect::Rect::new( + src.left.round() as i32, + src.top.round() as i32, + src.width().round() as u32, + src.height().round() as u32, + )), + Some(sdl2::rect::Rect::new( + dest.left.round() as i32, + dest.top.round() as i32, + dest.width().round() as u32, + dest.height().round() as u32, + )), + ) .map_err(|e| GameError::RenderError(e.to_string()))?; } SpriteBatchCommand::DrawRectTinted(src, dest, color) => { @@ -531,9 +599,49 @@ impl BackendTexture for SDL2Texture { texture.set_alpha_mod(a); texture.set_blend_mode(refs.blend_mode); - refs.canvas.copy(texture, - Some(sdl2::rect::Rect::new(src.left.round() as i32, src.top.round() as i32, src.width().round() as u32, src.height().round() as u32)), - Some(sdl2::rect::Rect::new(dest.left.round() as i32, dest.top.round() as i32, dest.width().round() as u32, dest.height().round() as u32))) + refs.canvas + .copy( + texture, + Some(sdl2::rect::Rect::new( + src.left.round() as i32, + src.top.round() as i32, + src.width().round() as u32, + src.height().round() as u32, + )), + Some(sdl2::rect::Rect::new( + dest.left.round() as i32, + dest.top.round() as i32, + dest.width().round() as u32, + dest.height().round() as u32, + )), + ) + .map_err(|e| GameError::RenderError(e.to_string()))?; + } + SpriteBatchCommand::DrawRectFlip(src, dest, flip_x, flip_y) => { + texture.set_color_mod(255, 255, 255); + texture.set_alpha_mod(255); + texture.set_blend_mode(refs.blend_mode); + + refs.canvas + .copy_ex( + texture, + Some(sdl2::rect::Rect::new( + src.left.round() as i32, + src.top.round() as i32, + src.width().round() as u32, + src.height().round() as u32, + )), + Some(sdl2::rect::Rect::new( + dest.left.round() as i32, + dest.top.round() as i32, + dest.width().round() as u32, + dest.height().round() as u32, + )), + 0.0, + None, + *flip_x, + *flip_y, + ) .map_err(|e| GameError::RenderError(e.to_string()))?; } } @@ -551,7 +659,9 @@ impl Drop for SDL2Texture { mem::swap(&mut self.texture, &mut texture_opt); if let Some(texture) = texture_opt { - unsafe { texture.destroy(); } + unsafe { + texture.destroy(); + } } } } @@ -699,3 +809,219 @@ fn conv_scancode(code: keyboard::Scancode) -> Option { _ => None, } } + +// based on imgui-sdl2 crate +pub struct ImguiSdl2 { + mouse_press: [bool; 5], + ignore_mouse: bool, + ignore_keyboard: bool, + cursor: Option, + sdl_cursor: Option, +} + +struct Sdl2ClipboardBackend(sdl2::clipboard::ClipboardUtil); + +impl imgui::ClipboardBackend for Sdl2ClipboardBackend { + fn get(&mut self) -> Option { + if !self.0.has_clipboard_text() { + return None; + } + + self.0.clipboard_text().ok().map(imgui::ImString::new) + } + + fn set(&mut self, value: &imgui::ImStr) { + let _ = self.0.set_clipboard_text(value.to_str()); + } +} + +impl ImguiSdl2 { + pub fn new(imgui: &mut imgui::Context, window: &sdl2::video::Window) -> Self { + let clipboard_util = window.subsystem().clipboard(); + imgui.set_clipboard_backend(Box::new(Sdl2ClipboardBackend(clipboard_util))); + + imgui.io_mut().key_map[Key::Tab as usize] = Scancode::Tab as u32; + imgui.io_mut().key_map[Key::LeftArrow as usize] = Scancode::Left as u32; + imgui.io_mut().key_map[Key::RightArrow as usize] = Scancode::Right as u32; + imgui.io_mut().key_map[Key::UpArrow as usize] = Scancode::Up as u32; + imgui.io_mut().key_map[Key::DownArrow as usize] = Scancode::Down as u32; + imgui.io_mut().key_map[Key::PageUp as usize] = Scancode::PageUp as u32; + imgui.io_mut().key_map[Key::PageDown as usize] = Scancode::PageDown as u32; + imgui.io_mut().key_map[Key::Home as usize] = Scancode::Home as u32; + imgui.io_mut().key_map[Key::End as usize] = Scancode::End as u32; + imgui.io_mut().key_map[Key::Delete as usize] = Scancode::Delete as u32; + imgui.io_mut().key_map[Key::Backspace as usize] = Scancode::Backspace as u32; + imgui.io_mut().key_map[Key::Enter as usize] = Scancode::Return as u32; + imgui.io_mut().key_map[Key::Escape as usize] = Scancode::Escape as u32; + imgui.io_mut().key_map[Key::Space as usize] = Scancode::Space as u32; + imgui.io_mut().key_map[Key::A as usize] = Scancode::A as u32; + imgui.io_mut().key_map[Key::C as usize] = Scancode::C as u32; + imgui.io_mut().key_map[Key::V as usize] = Scancode::V as u32; + imgui.io_mut().key_map[Key::X as usize] = Scancode::X as u32; + imgui.io_mut().key_map[Key::Y as usize] = Scancode::Y as u32; + imgui.io_mut().key_map[Key::Z as usize] = Scancode::Z as u32; + + Self { + mouse_press: [false; 5], + ignore_keyboard: false, + ignore_mouse: false, + cursor: None, + sdl_cursor: None, + } + } + + pub fn ignore_event(&self, event: &Event) -> bool { + match *event { + Event::KeyDown { .. } + | Event::KeyUp { .. } + | Event::TextEditing { .. } + | Event::TextInput { .. } => self.ignore_keyboard, + Event::MouseMotion { .. } + | Event::MouseButtonDown { .. } + | Event::MouseButtonUp { .. } + | Event::MouseWheel { .. } + | Event::FingerDown { .. } + | Event::FingerUp { .. } + | Event::FingerMotion { .. } + | Event::DollarGesture { .. } + | Event::DollarRecord { .. } + | Event::MultiGesture { .. } => self.ignore_mouse, + _ => false, + } + } + + 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) { + let ctrl = keymod.intersects(keyboard::Mod::RCTRLMOD | keyboard::Mod::LCTRLMOD); + let alt = keymod.intersects(keyboard::Mod::RALTMOD | keyboard::Mod::LALTMOD); + let shift = keymod.intersects(keyboard::Mod::RSHIFTMOD | keyboard::Mod::LSHIFTMOD); + let super_ = keymod.intersects(keyboard::Mod::RGUIMOD | keyboard::Mod::LGUIMOD); + + imgui.io_mut().key_ctrl = ctrl; + imgui.io_mut().key_alt = alt; + imgui.io_mut().key_shift = shift; + imgui.io_mut().key_super = super_; + } + + match *event { + Event::MouseWheel { y, .. } => { + imgui.io_mut().mouse_wheel = y as f32; + } + Event::MouseButtonDown { mouse_btn, .. } => { + if mouse_btn != MouseButton::Unknown { + let index = match mouse_btn { + MouseButton::Left => 0, + MouseButton::Right => 1, + MouseButton::Middle => 2, + MouseButton::X1 => 3, + MouseButton::X2 => 4, + MouseButton::Unknown => unreachable!(), + }; + self.mouse_press[index] = true; + } + } + Event::TextInput { ref text, .. } => { + for chr in text.chars() { + imgui.io_mut().add_input_character(chr); + } + } + Event::KeyDown { + scancode, keymod, .. + } => { + set_mod(imgui, keymod); + if let Some(scancode) = scancode { + imgui.io_mut().keys_down[scancode as usize] = true; + } + } + Event::KeyUp { + scancode, keymod, .. + } => { + set_mod(imgui, keymod); + if let Some(scancode) = scancode { + imgui.io_mut().keys_down[scancode as usize] = false; + } + } + _ => {} + } + } + + pub fn prepare_frame( + &mut self, + io: &mut imgui::Io, + window: &sdl2::video::Window, + mouse_state: &sdl2::mouse::MouseState, + ) { + let mouse_util = window.subsystem().sdl().mouse(); + + let (win_w, win_h) = window.size(); + let (draw_w, draw_h) = window.drawable_size(); + + io.display_size = [win_w as f32, win_h as f32]; + io.display_framebuffer_scale = [ + (draw_w as f32) / (win_w as f32), + (draw_h as f32) / (win_h as f32), + ]; + + // Merging the mousedown events we received into the current state prevents us from missing + // clicks that happen faster than a frame + io.mouse_down = [ + self.mouse_press[0] || mouse_state.left(), + self.mouse_press[1] || mouse_state.right(), + self.mouse_press[2] || mouse_state.middle(), + self.mouse_press[3] || mouse_state.x1(), + self.mouse_press[4] || mouse_state.x2(), + ]; + self.mouse_press = [false; 5]; + + let any_mouse_down = io.mouse_down.iter().any(|&b| b); + mouse_util.capture(any_mouse_down); + + io.mouse_pos = [mouse_state.x() as f32, mouse_state.y() as f32]; + + self.ignore_keyboard = io.want_capture_keyboard; + self.ignore_mouse = io.want_capture_mouse; + } + + pub fn prepare_render(&mut self, ui: &imgui::Ui, window: &sdl2::video::Window) { + let io = ui.io(); + if !io + .config_flags + .contains(ConfigFlags::NO_MOUSE_CURSOR_CHANGE) + { + let mouse_util = window.subsystem().sdl().mouse(); + + match ui.mouse_cursor() { + Some(mouse_cursor) if !io.mouse_draw_cursor => { + mouse_util.show_cursor(true); + + let sdl_cursor = match mouse_cursor { + MouseCursor::Arrow => SystemCursor::Arrow, + MouseCursor::TextInput => SystemCursor::IBeam, + MouseCursor::ResizeAll => SystemCursor::SizeAll, + MouseCursor::ResizeNS => SystemCursor::SizeNS, + MouseCursor::ResizeEW => SystemCursor::SizeWE, + MouseCursor::ResizeNESW => SystemCursor::SizeNESW, + MouseCursor::ResizeNWSE => SystemCursor::SizeNWSE, + MouseCursor::Hand => SystemCursor::Hand, + MouseCursor::NotAllowed => SystemCursor::No, + }; + + if self.cursor != Some(mouse_cursor) { + let sdl_cursor = Cursor::from_system(sdl_cursor).unwrap(); + sdl_cursor.set(); + self.cursor = Some(mouse_cursor); + self.sdl_cursor = Some(sdl_cursor); + } + } + _ => { + self.cursor = None; + self.sdl_cursor = None; + mouse_util.show_cursor(false); + } + } + } + } +} diff --git a/src/framework/context.rs b/src/framework/context.rs index 0868543..3a2688d 100644 --- a/src/framework/context.rs +++ b/src/framework/context.rs @@ -1,4 +1,4 @@ -use crate::framework::backend::{Backend, init_backend, BackendRenderer}; +use crate::framework::backend::{init_backend, BackendRenderer}; use crate::framework::error::GameResult; use crate::framework::filesystem::Filesystem; use crate::Game; diff --git a/src/framework/filesystem.rs b/src/framework/filesystem.rs index aff2397..09d80b7 100644 --- a/src/framework/filesystem.rs +++ b/src/framework/filesystem.rs @@ -71,7 +71,7 @@ impl Filesystem { // Set up VFS to merge resource path, root path, and zip path. let overlay = vfs::OverlayFS::new(); // User data VFS. - let mut user_overlay = vfs::OverlayFS::new(); + let user_overlay = vfs::OverlayFS::new(); Filesystem { vfs: overlay, diff --git a/src/lib.rs b/src/lib.rs index b46ddbb..bae596c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,7 +138,7 @@ impl Game { let state_ref = unsafe { &mut *self.state.get() }; if state_ref.timing_mode != TimingMode::FrameSynchronized { - let mut elapsed = self.start_time.elapsed().as_nanos(); + let elapsed = self.start_time.elapsed().as_nanos(); #[cfg(target_os = "windows")] { // Even with the non-monotonic Instant mitigation at the start of the event loop, there's still a chance of it not working. @@ -177,31 +177,6 @@ impl Game { graphics::present(ctx)?; Ok(()) } - - fn key_down_event(&mut self, key_code: ScanCode, repeat: bool) { - if repeat { return; } - - let state = unsafe { &mut *self.state.get() }; - match key_code { - ScanCode::F5 => { state.settings.subpixel_coords = !state.settings.subpixel_coords } - ScanCode::F6 => { state.settings.motion_interpolation = !state.settings.motion_interpolation } - ScanCode::F7 => { state.set_speed(1.0) } - ScanCode::F8 => { - if state.settings.speed > 0.2 { - state.set_speed(state.settings.speed - 0.1); - } - } - ScanCode::F9 => { - if state.settings.speed < 3.0 { - state.set_speed(state.settings.speed + 0.1); - } - } - ScanCode::F10 => { state.settings.debug_outlines = !state.settings.debug_outlines } - ScanCode::F11 => { state.settings.god_mode = !state.settings.god_mode } - ScanCode::F12 => { state.settings.infinite_booster = !state.settings.infinite_booster } - _ => {} - } - } } #[cfg(target_os = "android")] @@ -321,28 +296,7 @@ pub fn init() -> GameResult { } state_ref.next_scene = Some(Box::new(LoadingScene::new())); - context.run(&mut game); - - /* loop { - game.update(&mut context)?; - - if state_ref.shutdown { - log::info!("Shutting down..."); - break; - } - - if state_ref.next_scene.is_some() { - mem::swap(&mut game.scene, &mut state_ref.next_scene); - state_ref.next_scene = None; - - game.scene.as_mut().unwrap().init(state_ref, &mut context).unwrap(); - game.loops = 0; - state_ref.frame_time = 0.0; - } - - std::thread::sleep(std::time::Duration::from_millis(10)); - game.draw(&mut context)?; - }*/ + context.run(&mut game)?; Ok(()) } diff --git a/src/live_debugger.rs b/src/live_debugger.rs index ac20093..f9ee7b0 100644 --- a/src/live_debugger.rs +++ b/src/live_debugger.rs @@ -59,7 +59,7 @@ impl LiveDebugger { Window::new(im_str!("Debugger")) .resizable(false) - .collapsed(false, Condition::FirstUseEver) + .collapsed(true, Condition::FirstUseEver) .position([5.0, 5.0], Condition::FirstUseEver) .size([400.0, 170.0], Condition::FirstUseEver) .build(ui, || { diff --git a/src/npc/ai/misc.rs b/src/npc/ai/misc.rs index 97bc61f..21243d2 100644 --- a/src/npc/ai/misc.rs +++ b/src/npc/ai/misc.rs @@ -1587,7 +1587,7 @@ impl NPC { npc.y = self.y - 0x1800; npc.tsc_direction = 10 * self.anim_num + 40; - npc_list.spawn(0, npc); + let _ = npc_list.spawn(0, npc); self.cond.set_explode_die(true); } } @@ -1630,7 +1630,7 @@ impl NPC { npc.direction = Direction::Right; npc.parent_id = self.id; - npc_list.spawn(0x100, npc); + let _ = npc_list.spawn(0x100, npc); } } diff --git a/src/npc/ai/sand_zone.rs b/src/npc/ai/sand_zone.rs index 35bdd52..8eae288 100644 --- a/src/npc/ai/sand_zone.rs +++ b/src/npc/ai/sand_zone.rs @@ -289,7 +289,7 @@ impl NPC { } pub(crate) fn tick_n049_skullhead(&mut self, state: &mut SharedGameState, players: [&mut Player; 2], npc_list: &NPCList) -> GameResult { - let mut parent = self.get_parent_ref_mut(npc_list); + let parent = self.get_parent_ref_mut(npc_list); if self.action_num > 9 && parent.as_ref().map(|n| n.npc_type == 3).unwrap_or(false) { self.action_num = 3; diff --git a/src/npc/mod.rs b/src/npc/mod.rs index dc77d79..a5c064b 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -284,6 +284,8 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager)> for NP 216 => self.tick_n216_debug_cat(state), 222 => self.tick_n222_prison_bars(state), 227 => self.tick_n227_bucket(state), + 229 => self.tick_n229_red_flowers_sprouts(state), + 230 => self.tick_n230_red_flowers_blooming(state), 234 => self.tick_n234_red_flowers_picked(state), 239 => self.tick_n239_cage_bars(state), 241 => self.tick_n241_critter_red(state, players), diff --git a/src/rng.rs b/src/rng.rs index 39a7fc1..5f65c9b 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -73,7 +73,7 @@ impl Xoroshiro32PlusPlus { pub fn next_u16(&self) -> u16 { let mut state = self.0.get(); - let mut result = (state.0.wrapping_add(state.1)).rotate_left(9).wrapping_add(state.0); + let result = (state.0.wrapping_add(state.1)).rotate_left(9).wrapping_add(state.0); state.1 ^= state.0; state.0 = state.0.rotate_left(13) ^ state.1 ^ (state.1 << 5); diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index 77aa954..f1a6b49 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -434,13 +434,14 @@ impl GameScene { }; let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, tex_name)?; - // switch version uses +1000 face offset to display a flipped version + // 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 face_num = state.textscript_vm.face % 100; - let (scale_x, scale_y) = batch.scale(); - batch.add_rect_scaled(left_pos + 14.0 + if flip { 48.0 } else { 0.0 }, top_pos + 8.0, - scale_x * if flip { -1.0 } else { 1.0 }, scale_y, + batch.add_rect_flip(left_pos + 14.0, top_pos + 8.0, + flip, false, &Rect::new_size( (face_num as u16 % 6) * 48, (face_num as u16 / 6) * 48, diff --git a/src/scene/no_data_scene.rs b/src/scene/no_data_scene.rs index a7b20a4..46037b5 100644 --- a/src/scene/no_data_scene.rs +++ b/src/scene/no_data_scene.rs @@ -19,6 +19,7 @@ impl NoDataScene { } } +#[cfg(target_os = "android")] static REL_URL: &str = "https://github.com/doukutsu-rs/game-data/releases"; impl Scene for NoDataScene { diff --git a/src/scripting/doukutsu.rs b/src/scripting/doukutsu.rs index d2c8326..6bbfebf 100644 --- a/src/scripting/doukutsu.rs +++ b/src/scripting/doukutsu.rs @@ -1,14 +1,13 @@ use lua_ffi::ffi::luaL_Reg; use lua_ffi::{LuaObject, State, c_int}; -use crate::scene::game_scene::GameScene; use crate::scripting::LuaScriptingState; -use crate::shared_game_state::SharedGameState; pub struct Doukutsu { pub ptr: *mut LuaScriptingState, } +#[allow(unused)] impl Doukutsu { pub fn new(ptr: *mut LuaScriptingState) -> Doukutsu { Doukutsu { diff --git a/src/scripting/mod.rs b/src/scripting/mod.rs index 5e24cc2..5c97342 100644 --- a/src/scripting/mod.rs +++ b/src/scripting/mod.rs @@ -1,4 +1,4 @@ -use std::io::{Read, Seek}; +use std::io::Read; use std::ptr::null_mut; @@ -6,8 +6,7 @@ use crate::framework::context::Context; use crate::framework::error::{GameResult, GameError}; -use lua_ffi::{c_int, LuaFunction, LuaObject, State, ThreadStatus}; -use lua_ffi::ffi::lua_pushcfunction; +use lua_ffi::{c_int, State, ThreadStatus}; use crate::scene::game_scene::GameScene; use crate::scripting::doukutsu::Doukutsu; @@ -117,7 +116,7 @@ impl LuaScriptingState { if filesystem::exists(ctx, "/scripts/") { let mut script_count = 0; - let mut files = filesystem::read_dir(ctx, "/scripts/")? + let files = filesystem::read_dir(ctx, "/scripts/")? .filter(|f| f.to_string_lossy().to_lowercase().ends_with(".lua")); for file in files { diff --git a/src/scripting/player.rs b/src/scripting/player.rs index 4f01ead..4edc822 100644 --- a/src/scripting/player.rs +++ b/src/scripting/player.rs @@ -12,6 +12,7 @@ pub struct LuaPlayer { inv_ptr: *mut Inventory, } +#[allow(unused)] impl LuaPlayer { fn check_ref(&self, state: &mut State) -> bool { if !self.valid_reference { diff --git a/src/shared_game_state.rs b/src/shared_game_state.rs index 340bb2b..e14c9e2 100644 --- a/src/shared_game_state.rs +++ b/src/shared_game_state.rs @@ -28,7 +28,8 @@ use crate::text_script::{ScriptMode, TextScriptExecutionState, TextScriptVM}; use crate::texture_set::TextureSet; use crate::framework::{filesystem, graphics}; use crate::framework::backend::BackendTexture; -use crate::framework::graphics::{create_texture, set_render_target, create_texture_mutable}; +use crate::framework::graphics::{set_render_target, create_texture_mutable}; +use crate::framework::keyboard::ScanCode; #[derive(PartialEq, Eq, Copy, Clone)] pub enum TimingMode { @@ -103,6 +104,7 @@ pub struct SharedGameState { pub npc_super_pos: (i32, i32), pub stages: Vec, pub frame_time: f64, + pub debugger: bool, pub scale: f32, pub canvas_size: (f32, f32), pub screen_size: (f32, f32), @@ -117,7 +119,7 @@ pub struct SharedGameState { pub lua: LuaScriptingState, pub sound_manager: SoundManager, pub settings: Settings, - pub shutdown: bool, + pub shutdown: bool } impl SharedGameState { @@ -168,6 +170,7 @@ impl SharedGameState { npc_super_pos: (0, 0), stages: Vec::with_capacity(96), frame_time: 0.0, + debugger: false, scale: 2.0, screen_size: (640.0, 480.0), canvas_size: (320.0, 240.0), @@ -186,6 +189,28 @@ impl SharedGameState { }) } + pub fn process_debug_keys(&mut self, key_code: ScanCode) { + match key_code { + 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::F7 => { self.set_speed(1.0) } + ScanCode::F8 => { + if self.settings.speed > 0.2 { + self.set_speed(self.settings.speed - 0.1); + } + } + ScanCode::F9 => { + if self.settings.speed < 3.0 { + self.set_speed(self.settings.speed + 0.1); + } + } + ScanCode::F10 => { self.settings.debug_outlines = !self.settings.debug_outlines } + _ => {} + } + } + pub fn reload_textures(&mut self) { let mut texture_set = TextureSet::new(self.base_path.as_str()); @@ -307,10 +332,6 @@ impl SharedGameState { pub fn set_speed(&mut self, value: f64) { self.settings.speed = clamp(value, 0.1, 3.0); self.frame_time = 0.0; - - if let Err(err) = self.sound_manager.set_speed(value as f32) { - log::error!("Error while sending a message to sound manager: {}", err); - } } pub fn current_tps(&self) -> f64 { diff --git a/src/sound/mod.rs b/src/sound/mod.rs index 4ce7c7d..6b1f170 100644 --- a/src/sound/mod.rs +++ b/src/sound/mod.rs @@ -8,7 +8,7 @@ use num_traits::clamp; use crate::engine_constants::EngineConstants; use crate::framework::context::Context; -use crate::framework::error::{GameResult, GameError}; +use crate::framework::error::{GameResult}; use crate::framework::filesystem; use crate::sound::organya::Song; use crate::sound::pixtone::PixTonePlayback; @@ -16,7 +16,6 @@ use crate::sound::playback::{PlaybackEngine, SavedPlaybackState}; use crate::sound::wave_bank::SoundBank; use crate::str; use crate::framework::error::GameError::{AudioError, ResourceLoadError, InvalidValue}; -use std::io::Error; mod wave_bank; mod organya; diff --git a/src/sound/playback.rs b/src/sound/playback.rs index e5cde85..3f51974 100644 --- a/src/sound/playback.rs +++ b/src/sound/playback.rs @@ -166,6 +166,7 @@ impl PlaybackEngine { self.play_pos = position; } + #[allow(unused)] pub fn get_total_samples(&self) -> u32 { let ticks_intro = self.song.time.loop_range.start; let ticks_loop = self.song.time.loop_range.end - self.song.time.loop_range.start; diff --git a/src/text_script.rs b/src/text_script.rs index 58340a3..91bf259 100644 --- a/src/text_script.rs +++ b/src/text_script.rs @@ -1751,6 +1751,7 @@ impl TextScript { } } + #[allow(unused)] fn read_varint>(iter: &mut I) -> GameResult { let mut result = 0u32; diff --git a/src/texture_set.rs b/src/texture_set.rs index 61a70d6..b206af7 100644 --- a/src/texture_set.rs +++ b/src/texture_set.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use log::info; use crate::common; -use crate::common::{FILE_TYPES, Point, Rect}; +use crate::common::{FILE_TYPES, Rect}; use crate::engine_constants::EngineConstants; use crate::framework::backend::{BackendTexture, SpriteBatchCommand}; use crate::framework::context::Context; @@ -94,12 +94,40 @@ impl SizedBatch { self.add_rect_scaled(x, y, 1.0, 1.0, rect) } + pub fn add_rect_flip(&mut self, mut x: f32, mut y: f32, flip_x: bool, flip_y: bool, rect: &common::Rect) { + if (rect.right - rect.left) == 0 || (rect.bottom - rect.top) == 0 { + return; + } + + unsafe { + x = (x * G_MAG).round() / G_MAG; + y = (y * G_MAG).round() / G_MAG; + } + let mag = unsafe { I_MAG }; + + self.batch.add(SpriteBatchCommand::DrawRectFlip( + Rect { + left: rect.left as f32 / self.scale_x, + top: rect.top as f32 / self.scale_y, + right: rect.right as f32 / self.scale_x, + bottom: rect.bottom as f32 / self.scale_y, + }, + Rect { + left: x * mag, + top: y * mag, + right: (x + rect.width() as f32) * mag, + bottom: (y + rect.height() as f32) * mag, + }, + flip_x, flip_y + )); + } + #[inline(always)] pub fn add_rect_tinted(&mut self, x: f32, y: f32, color: (u8, u8, u8, u8), rect: &common::Rect) { self.add_rect_scaled_tinted(x, y, color, 1.0, 1.0, rect) } - pub fn add_rect_scaled(&mut self, mut x: f32, mut y: f32, mut scale_x: f32, mut scale_y: f32, rect: &common::Rect) { + 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 { return; } @@ -126,7 +154,7 @@ impl SizedBatch { )); } - pub fn add_rect_scaled_tinted(&mut self, mut x: f32, mut y: f32, color: (u8, u8, u8, u8), mut scale_x: f32, mut scale_y: f32, rect: &common::Rect) { + pub fn add_rect_scaled_tinted(&mut self, mut x: f32, mut y: f32, color: (u8, u8, u8, u8), scale_x: f32, scale_y: f32, rect: &common::Rect) { if (rect.right - rect.left) == 0 || (rect.bottom - rect.top) == 0 { return; } diff --git a/src/weapon.rs b/src/weapon.rs index d2b2687..ce959cb 100644 --- a/src/weapon.rs +++ b/src/weapon.rs @@ -384,7 +384,7 @@ impl Weapon { fn tick_spur(&mut self, player: &mut Player, player_id: TargetPlayer, inventory: &mut Inventory, bullet_manager: &mut BulletManager, state: &mut SharedGameState) { let mut shoot = false; - let mut btype = 0; + let mut btype; if player.controller.shoot() { inventory.add_xp(if player.equip.has_turbocharge() { 3 } else { 2 }, player, state);