diff --git a/src/builtin/builtin_data/crab_band.png b/src/builtin/builtin_data/crab_band.png new file mode 100644 index 0000000..2834ea2 Binary files /dev/null and b/src/builtin/builtin_data/crab_band.png differ diff --git a/src/builtin_fs.rs b/src/builtin_fs.rs index b1a544e..90b5ff5 100644 --- a/src/builtin_fs.rs +++ b/src/builtin_fs.rs @@ -111,6 +111,7 @@ impl BuiltinFS { "builtin_data", vec![ FSNode::File("buttons.png", include_bytes!("builtin/builtin_data/buttons.png")), + FSNode::File("crab_band.png", include_bytes!("builtin/builtin_data/crab_band.png")), FSNode::File("triangles.png", include_bytes!("builtin/builtin_data/triangles.png")), ], ), diff --git a/src/components/text_boxes.rs b/src/components/text_boxes.rs index 505a57a..6d532f6 100644 --- a/src/components/text_boxes.rs +++ b/src/components/text_boxes.rs @@ -164,13 +164,16 @@ impl GameEntity<()> for TextBoxes { let face_x = (4.0 + (6 - self.slide_in) as f32 * 8.0) - 52.0; - batch.add_rect_flip( - left_pos + 14.0 + face_x, - top_pos + 8.0, - flip, - false, - &Rect::new_size((face_num as u16 % 6) * 48, (face_num as u16 / 6) * 48, 48, 48), - ); + let final_x = left_pos + 14.0 + face_x; + let final_y = top_pos + 8.0; + let rect = Rect::new_size((face_num as u16 % 6) * 48, (face_num as u16 / 6) * 48, 48, 48); + + if face_num >= 1 && face_num <= 4 && state.more_rust { + // sue + batch.add_rect_flip_tinted(final_x, final_y, flip, false, (200, 200, 255, 255), &rect); + } else { + batch.add_rect_flip(final_x, final_y, flip, false, &rect); + } batch.draw(ctx)?; graphics::set_clip_rect(ctx, None)?; diff --git a/src/framework/backend.rs b/src/framework/backend.rs index 0160406..fe7808a 100644 --- a/src/framework/backend.rs +++ b/src/framework/backend.rs @@ -119,4 +119,5 @@ pub enum SpriteBatchCommand { DrawRect(Rect, Rect), DrawRectFlip(Rect, Rect, bool, bool), DrawRectTinted(Rect, Rect, Color), + DrawRectFlipTinted(Rect, Rect, bool, bool, Color), } diff --git a/src/framework/backend_sdl2.rs b/src/framework/backend_sdl2.rs index 7c30ac1..6e091d7 100644 --- a/src/framework/backend_sdl2.rs +++ b/src/framework/backend_sdl2.rs @@ -965,6 +965,34 @@ impl BackendTexture for SDL2Texture { texture.set_alpha_mod(255); texture.set_blend_mode(blend); + 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()))?; + } + SpriteBatchCommand::DrawRectFlipTinted(src, dest, flip_x, flip_y, color) => { + let (r, g, b, a) = color.to_rgba(); + texture.set_color_mod(r, g, b); + texture.set_alpha_mod(a); + texture.set_blend_mode(blend); + canvas .copy_ex( texture, diff --git a/src/framework/render_opengl.rs b/src/framework/render_opengl.rs index cc2f882..c1edb72 100644 --- a/src/framework/render_opengl.rs +++ b/src/framework/render_opengl.rs @@ -164,6 +164,51 @@ impl BackendTexture for OpenGLTexture { ]; self.vertices.extend_from_slice(&vertices); } + SpriteBatchCommand::DrawRectFlipTinted(mut src, dest, flip_x, flip_y, color) => { + if flip_x { + std::mem::swap(&mut src.left, &mut src.right); + } + + if flip_y { + std::mem::swap(&mut src.top, &mut src.bottom); + } + + let color = color.to_rgba(); + + let vertices = [ + VertexData { + position: (dest.left, dest.bottom), + uv: (src.left * tex_scale_x, src.bottom * tex_scale_y), + color, + }, + VertexData { + position: (dest.left, dest.top), + uv: (src.left * tex_scale_x, src.top * tex_scale_y), + color, + }, + VertexData { + position: (dest.right, dest.top), + uv: (src.right * tex_scale_x, src.top * tex_scale_y), + color, + }, + VertexData { + position: (dest.left, dest.bottom), + uv: (src.left * tex_scale_x, src.bottom * tex_scale_y), + color, + }, + VertexData { + position: (dest.right, dest.top), + uv: (src.right * tex_scale_x, src.top * tex_scale_y), + color, + }, + VertexData { + position: (dest.right, dest.bottom), + uv: (src.right * tex_scale_x, src.bottom * tex_scale_y), + color, + }, + ]; + self.vertices.extend_from_slice(&vertices); + } } } @@ -787,7 +832,7 @@ impl BackendRenderer for OpenGLRenderer { match mode { VSyncMode::Uncapped => { sdl2_sys::SDL_GL_SetSwapInterval(0); - }, + } VSyncMode::VSync => { sdl2_sys::SDL_GL_SetSwapInterval(1); } diff --git a/src/live_debugger.rs b/src/live_debugger.rs index 1ed2618..280d189 100644 --- a/src/live_debugger.rs +++ b/src/live_debugger.rs @@ -163,6 +163,7 @@ impl LiveDebugger { self.hotkey_list_visible = !self.hotkey_list_visible; } ui.checkbox("noclip", &mut state.settings.noclip); + ui.checkbox("more rust", &mut state.more_rust); }); if self.map_selector_visible { diff --git a/src/npc/mod.rs b/src/npc/mod.rs index d089460..90da326 100644 --- a/src/npc/mod.rs +++ b/src/npc/mod.rs @@ -218,6 +218,10 @@ impl NPC { Ok(()) } + + fn is_sue(&self) -> bool { + [42, 92, 280, 284].contains(&self.npc_type) + } } impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mut Flash, &mut BossNPC)> for NPC { @@ -658,16 +662,28 @@ impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &mut BulletManager, &mu let (frame_x, frame_y) = frame.xy_interpolated(state.frame_time); - batch.add_rect( - interpolate_fix9_scale(self.prev_x - off_x, self.x - off_x, state.frame_time) + shock - frame_x, - interpolate_fix9_scale( - self.prev_y - self.display_bounds.top as i32, - self.y - self.display_bounds.top as i32, - state.frame_time, - ) - frame_y, - &self.anim_rect, - ); - batch.draw(ctx)?; + let final_x = interpolate_fix9_scale(self.prev_x - off_x, self.x - off_x, state.frame_time) + shock - frame_x; + let final_y = interpolate_fix9_scale( + self.prev_y - self.display_bounds.top as i32, + self.y - self.display_bounds.top as i32, + state.frame_time, + ) - frame_y; + + if self.is_sue() && state.more_rust { + // tint sue blue + batch.add_rect_tinted(final_x, final_y, (200, 200, 255, 255), &self.anim_rect); + batch.draw(ctx)?; + } else { + batch.add_rect(final_x, final_y, &self.anim_rect); + batch.draw(ctx)?; + } + + if self.npc_type == 42 && state.more_rust { + // draw crab headband + let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "crab_band")?; + batch.add_rect(final_x, final_y, &self.anim_rect); + batch.draw(ctx)?; + } Ok(()) } diff --git a/src/settings.rs b/src/settings.rs index c14981a..71b30f7 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -78,6 +78,7 @@ pub struct Settings { pub debug_mode: bool, #[serde(skip)] pub noclip: bool, + pub more_rust: bool, } fn default_true() -> bool { @@ -86,7 +87,7 @@ fn default_true() -> bool { #[inline(always)] fn current_version() -> u32 { - 18 + 19 } #[inline(always)] @@ -285,6 +286,11 @@ impl Settings { self.player2_rumble = default_rumble(); } + if self.version == 18 { + self.version = 19; + self.more_rust = false; + } + if self.version != initial_version { log::info!("Upgraded configuration file from version {} to {}.", initial_version, self.version); } @@ -388,6 +394,7 @@ impl Default for Settings { screen_shake_intensity: ScreenShakeIntensity::Full, debug_mode: false, noclip: false, + more_rust: false, } } } diff --git a/src/shared_game_state.rs b/src/shared_game_state.rs index 81080b0..a546389 100644 --- a/src/shared_game_state.rs +++ b/src/shared_game_state.rs @@ -339,6 +339,7 @@ pub struct SharedGameState { pub replay_state: ReplayState, pub mod_requirements: ModRequirements, pub tutorial_counter: u16, + pub more_rust: bool, pub shutdown: bool, } @@ -427,6 +428,9 @@ impl SharedGameState { sound_manager.set_song_volume(settings.bgm_volume); sound_manager.set_sfx_volume(settings.sfx_volume); + let current_time = Local::now(); + let more_rust = (current_time.month() == 7 && current_time.day() == 7) || settings.more_rust; + #[cfg(feature = "hooks")] init_hooks(); @@ -480,6 +484,7 @@ impl SharedGameState { replay_state: ReplayState::None, mod_requirements, tutorial_counter: 0, + more_rust, shutdown: false, }) } diff --git a/src/texture_set.rs b/src/texture_set.rs index 8ee073c..3226673 100644 --- a/src/texture_set.rs +++ b/src/texture_set.rs @@ -52,6 +52,16 @@ pub trait SpriteBatch { fn add_rect_tinted(&mut self, x: f32, y: f32, color: (u8, u8, u8, u8), rect: &common::Rect); + fn add_rect_flip_tinted( + &mut self, + x: f32, + y: f32, + flip_x: bool, + flip_y: bool, + color: (u8, u8, u8, u8), + rect: &common::Rect, + ); + fn add_rect_scaled(&mut self, x: f32, y: f32, scale_x: f32, scale_y: f32, rect: &common::Rect); fn add_rect_scaled_tinted( @@ -116,6 +126,17 @@ impl SpriteBatch for DummyBatch { fn add_rect_tinted(&mut self, _x: f32, _y: f32, _color: (u8, u8, u8, u8), _rect: &Rect) {} + fn add_rect_flip_tinted( + &mut self, + _x: f32, + _y: f32, + _flip_x: bool, + _flip_y: bool, + _color: (u8, u8, u8, u8), + _rect: &Rect, + ) { + } + fn add_rect_scaled(&mut self, _x: f32, _y: f32, _scale_x: f32, _scale_y: f32, _rect: &Rect) {} fn add_rect_scaled_tinted( @@ -284,14 +305,19 @@ impl SpriteBatch for SubBatch { 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; } let mag = unsafe { I_MAG }; self.batch.add(SpriteBatchCommand::DrawRectTinted( - Rect { left: rect.left as f32, top: rect.top as f32, right: rect.right as f32, bottom: rect.bottom as f32 }, + 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, @@ -302,6 +328,40 @@ impl SpriteBatch for SubBatch { )); } + fn add_rect_flip_tinted( + &mut self, + x: f32, + y: f32, + flip_x: bool, + flip_y: bool, + color: (u8, u8, u8, u8), + rect: &common::Rect, + ) { + if (rect.right.saturating_sub(rect.left)) == 0 || (rect.bottom.saturating_sub(rect.top)) == 0 { + return; + } + + let mag = unsafe { I_MAG }; + + self.batch.add(SpriteBatchCommand::DrawRectFlipTinted( + 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, + color.into(), + )); + } + #[inline(always)] fn draw(&mut self, ctx: &mut Context) -> GameResult { self.draw_filtered(FilterMode::Nearest, ctx) @@ -392,6 +452,18 @@ impl SpriteBatch for CombinedBatch { self.main_batch.add_rect_scaled_tinted(x, y, color, scale_x, scale_y, rect) } + fn add_rect_flip_tinted( + &mut self, + x: f32, + y: f32, + flip_x: bool, + flip_y: bool, + color: (u8, u8, u8, u8), + rect: &common::Rect, + ) { + self.main_batch.add_rect_flip_tinted(x, y, flip_x, flip_y, color, rect) + } + fn draw(&mut self, ctx: &mut Context) -> GameResult { self.main_batch.draw(ctx) }