diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index 757abb7..0ffdc73 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -3,9 +3,9 @@ use num_traits::{abs, clamp}; use crate::bullet::BulletManager; use crate::caret::CaretType; -use crate::common::{Color, Direction, FadeDirection, FadeState, fix9_scale, interpolate_fix9_scale, Rect}; +use crate::common::{fix9_scale, interpolate_fix9_scale, Color, Direction, FadeDirection, FadeState, Rect}; use crate::components::boss_life_bar::BossLifeBar; -use crate::components::draw_common::{Alignment, draw_number}; +use crate::components::draw_common::{draw_number, Alignment}; use crate::components::hud::HUD; use crate::components::stage_select::StageSelect; use crate::entity::GameEntity; @@ -14,7 +14,7 @@ use crate::framework::backend::SpriteBatchCommand; use crate::framework::context::Context; use crate::framework::error::GameResult; use crate::framework::graphics; -use crate::framework::graphics::{BlendMode, FilterMode}; +use crate::framework::graphics::{draw_rect, BlendMode, FilterMode}; use crate::framework::ui::Components; use crate::input::touch_controls::TouchControlType; use crate::inventory::{Inventory, TakeExperienceResult}; @@ -24,11 +24,11 @@ use crate::npc::NPC; use crate::physics::PhysicalEntity; use crate::player::{Player, PlayerAppearance, TargetPlayer}; use crate::rng::XorShift; -use crate::scene::Scene; use crate::scene::title_scene::TitleScene; +use crate::scene::Scene; use crate::shared_game_state::{Season, SharedGameState}; use crate::stage::{BackgroundType, Stage}; -use crate::text_script::{ConfirmSelection, ScriptMode, TextScriptExecutionState, TextScriptVM}; +use crate::text_script::{ConfirmSelection, ScriptMode, TextScriptExecutionState, TextScriptLine, TextScriptVM}; use crate::texture_set::SizedBatch; use crate::weapon::WeaponType; @@ -150,14 +150,11 @@ impl GameScene { graphics::clear(ctx, Color::from_rgb(0, 0, 0)); let (off_x, off_y) = if self.stage.data.background_type == BackgroundType::MoveNear { - ( - frame_x % (batch.width() as f32), - frame_y % (batch.height() as f32) - ) + (frame_x % (batch.width() as f32), frame_y % (batch.height() as f32)) } else { ( ((frame_x / 2.0 * scale).floor() / scale) % (batch.width() as f32), - ((frame_y / 2.0 * scale).floor() / scale) % (batch.height() as f32) + ((frame_y / 2.0 * scale).floor() / scale) % (batch.height() as f32), ) }; @@ -166,8 +163,7 @@ impl GameScene { for y in 0..count_y { for x in 0..count_x { - batch.add((x * batch.width()) as f32 - off_x, - (y * batch.height()) as f32 - off_y); + batch.add((x * batch.width()) as f32 - off_x, (y * batch.height()) as f32 - off_y); } } } @@ -184,31 +180,25 @@ impl GameScene { let offset = (self.tick % 640) as i32; for x in (0..(state.canvas_size.0 as i32)).step_by(200) { - batch.add_rect(x as f32, 0.0, - &Rect::new_size(0, 0, 200, 88)); + batch.add_rect(x as f32, 0.0, &Rect::new_size(0, 0, 200, 88)); } - batch.add_rect(state.canvas_size.0 - 320.0, 0.0, - &Rect::new_size(0, 0, 320, 88)); + batch.add_rect(state.canvas_size.0 - 320.0, 0.0, &Rect::new_size(0, 0, 320, 88)); for x in ((-offset / 2)..(state.canvas_size.0 as i32)).step_by(320) { - batch.add_rect(x as f32, 88.0, - &Rect::new_size(0, 88, 320, 35)); + batch.add_rect(x as f32, 88.0, &Rect::new_size(0, 88, 320, 35)); } for x in ((-offset % 320)..(state.canvas_size.0 as i32)).step_by(320) { - batch.add_rect(x as f32, 123.0, - &Rect::new_size(0, 123, 320, 23)); + batch.add_rect(x as f32, 123.0, &Rect::new_size(0, 123, 320, 23)); } for x in ((-offset * 2)..(state.canvas_size.0 as i32)).step_by(320) { - batch.add_rect(x as f32, 146.0, - &Rect::new_size(0, 146, 320, 30)); + batch.add_rect(x as f32, 146.0, &Rect::new_size(0, 146, 320, 30)); } for x in ((-offset * 4)..(state.canvas_size.0 as i32)).step_by(320) { - batch.add_rect(x as f32, 176.0, - &Rect::new_size(0, 176, 320, 64)); + batch.add_rect(x as f32, 176.0, &Rect::new_size(0, 176, 320, 64)); } } } @@ -254,13 +244,11 @@ impl GameScene { Direction::FacingPlayer => unreachable!(), } - batch.add_rect(interpolate_fix9_scale(prev_x - self.frame.prev_x, - x - self.frame.x, - state.frame_time), - interpolate_fix9_scale(prev_y - self.frame.prev_y, - y - self.frame.y, - state.frame_time), - &bullet.anim_rect); + batch.add_rect( + interpolate_fix9_scale(prev_x - self.frame.prev_x, x - self.frame.x, state.frame_time), + interpolate_fix9_scale(prev_y - self.frame.prev_y, y - self.frame.y, state.frame_time), + &bullet.anim_rect, + ); } batch.draw(ctx)?; @@ -271,13 +259,19 @@ impl GameScene { let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "Caret")?; for caret in state.carets.iter() { - batch.add_rect(interpolate_fix9_scale(caret.prev_x - caret.offset_x - self.frame.prev_x, - caret.x - caret.offset_x - self.frame.x, - state.frame_time), - interpolate_fix9_scale(caret.prev_y - caret.offset_y - self.frame.prev_y, - caret.y - caret.offset_y - self.frame.y, - state.frame_time), - &caret.anim_rect); + batch.add_rect( + interpolate_fix9_scale( + caret.prev_x - caret.offset_x - self.frame.prev_x, + caret.x - caret.offset_x - self.frame.x, + state.frame_time, + ), + interpolate_fix9_scale( + caret.prev_y - caret.offset_y - self.frame.prev_y, + caret.y - caret.offset_y - self.frame.y, + state.frame_time, + ), + &caret.anim_rect, + ); } batch.draw(ctx)?; @@ -286,7 +280,9 @@ impl GameScene { fn draw_fade(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { match state.fade_state { - FadeState::Visible => { return Ok(()); } + FadeState::Visible => { + return Ok(()); + } FadeState::Hidden => { graphics::clear(ctx, Color::from_rgb(0, 0, 32)); } @@ -299,7 +295,11 @@ impl GameScene { let mut frame = tick; for x in 0..(state.canvas_size.0 as i32 / 16 + 1) { - if frame >= 15 { frame = 15; } else { frame += 1; } + if frame >= 15 { + frame = 15; + } else { + frame += 1; + } if frame >= 0 { rect.left = frame.abs() as u16 * 16; @@ -319,7 +319,11 @@ impl GameScene { let mut frame = tick; for y in 0..(state.canvas_size.1 as i32 / 16 + 1) { - if frame >= 15 { frame = 15; } else { frame += 1; } + if frame >= 15 { + frame = 15; + } else { + frame += 1; + } if frame >= 0 { rect.left = frame.abs() as u16 * 16; @@ -344,7 +348,11 @@ impl GameScene { let mut frame = start_frame; for y in 0..(center_y / 16 + 2) { - if frame >= 15 { frame = 15; } else { frame += 1; } + if frame >= 15 { + frame = 15; + } else { + frame += 1; + } if frame >= 0 { rect.left = frame.abs() as u16 * 16; @@ -374,7 +382,9 @@ impl GameScene { } fn draw_text_boxes(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { - if !state.textscript_vm.flags.render() { return Ok(()); } + if !state.textscript_vm.flags.render() { + return Ok(()); + } let top_pos = if state.textscript_vm.flags.position_top() { 32.0 } else { state.canvas_size.1 as f32 - 66.0 }; let left_pos = (state.canvas_size.0 / 2.0 - 122.0).floor(); @@ -390,18 +400,36 @@ impl GameScene { } if state.textscript_vm.item != 0 { - batch.add_rect((state.canvas_size.0 / 2.0 - 40.0).floor(), state.canvas_size.1 - 112.0, - &state.constants.textscript.get_item_top_left); - batch.add_rect((state.canvas_size.0 / 2.0 - 40.0).floor(), state.canvas_size.1 - 96.0, - &state.constants.textscript.get_item_bottom_left); - batch.add_rect((state.canvas_size.0 / 2.0 + 32.0).floor(), state.canvas_size.1 - 112.0, - &state.constants.textscript.get_item_top_right); - batch.add_rect((state.canvas_size.0 / 2.0 + 32.0).floor(), state.canvas_size.1 - 104.0, - &state.constants.textscript.get_item_right); - batch.add_rect((state.canvas_size.0 / 2.0 + 32.0).floor(), state.canvas_size.1 - 96.0, - &state.constants.textscript.get_item_right); - batch.add_rect((state.canvas_size.0 / 2.0 + 32.0).floor(), state.canvas_size.1 - 88.0, - &state.constants.textscript.get_item_bottom_right); + batch.add_rect( + (state.canvas_size.0 / 2.0 - 40.0).floor(), + state.canvas_size.1 - 112.0, + &state.constants.textscript.get_item_top_left, + ); + batch.add_rect( + (state.canvas_size.0 / 2.0 - 40.0).floor(), + state.canvas_size.1 - 96.0, + &state.constants.textscript.get_item_bottom_left, + ); + batch.add_rect( + (state.canvas_size.0 / 2.0 + 32.0).floor(), + state.canvas_size.1 - 112.0, + &state.constants.textscript.get_item_top_right, + ); + batch.add_rect( + (state.canvas_size.0 / 2.0 + 32.0).floor(), + state.canvas_size.1 - 104.0, + &state.constants.textscript.get_item_right, + ); + batch.add_rect( + (state.canvas_size.0 / 2.0 + 32.0).floor(), + state.canvas_size.1 - 96.0, + &state.constants.textscript.get_item_right, + ); + batch.add_rect( + (state.canvas_size.0 / 2.0 + 32.0).floor(), + state.canvas_size.1 - 88.0, + &state.constants.textscript.get_item_bottom_right, + ); } if let TextScriptExecutionState::WaitConfirmation(_, _, _, wait, selection) = state.textscript_vm.state { @@ -411,15 +439,20 @@ impl GameScene { state.canvas_size.1 - 96.0 }; - batch.add_rect((state.canvas_size.0 / 2.0 + 56.0).floor(), pos_y, - &state.constants.textscript.textbox_rect_yes_no); + batch.add_rect( + (state.canvas_size.0 / 2.0 + 56.0).floor(), + pos_y, + &state.constants.textscript.textbox_rect_yes_no, + ); if wait == 0 { let pos_x = if selection == ConfirmSelection::No { 41.0 } else { 0.0 }; - batch.add_rect((state.canvas_size.0 / 2.0 + 51.0).floor() + pos_x, - state.canvas_size.1 - 86.0, - &state.constants.textscript.textbox_rect_cursor); + batch.add_rect( + (state.canvas_size.0 / 2.0 + 51.0).floor() + pos_x, + state.canvas_size.1 - 86.0, + &state.constants.textscript.textbox_rect_cursor, + ); } } @@ -427,11 +460,7 @@ impl GameScene { } if state.textscript_vm.face != 0 { - let tex_name = if state.constants.textscript.animated_face_pics { - SWITCH_FACE_TEX[0] - } else { - FACE_TEX - }; + let tex_name = if state.constants.textscript.animated_face_pics { SWITCH_FACE_TEX[0] } else { FACE_TEX }; let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, tex_name)?; // switch version uses 1xxx flag to show a flipped version of face @@ -440,13 +469,13 @@ impl GameScene { let talking = (state.textscript_vm.face % 1000) > 100; let face_num = state.textscript_vm.face % 100; - 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, - 48, 48, - )); + 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, 48, 48), + ); batch.draw(ctx)?; } @@ -482,25 +511,81 @@ impl GameScene { let text_offset = if state.textscript_vm.face == 0 { 0.0 } else { 56.0 }; if !state.textscript_vm.line_1.is_empty() { - state.font.draw_text(state.textscript_vm.line_1.iter().copied(), left_pos + text_offset + 14.0, top_pos + 10.0, &state.constants, &mut state.texture_set, ctx)?; + state.font.draw_text( + state.textscript_vm.line_1.iter().copied(), + left_pos + text_offset + 14.0, + top_pos + 10.0, + &state.constants, + &mut state.texture_set, + ctx, + )?; } if !state.textscript_vm.line_2.is_empty() { - state.font.draw_text(state.textscript_vm.line_2.iter().copied(), left_pos + text_offset + 14.0, top_pos + 10.0 + 16.0, &state.constants, &mut state.texture_set, ctx)?; + state.font.draw_text( + state.textscript_vm.line_2.iter().copied(), + left_pos + text_offset + 14.0, + top_pos + 10.0 + 16.0, + &state.constants, + &mut state.texture_set, + ctx, + )?; } if !state.textscript_vm.line_3.is_empty() { - state.font.draw_text(state.textscript_vm.line_3.iter().copied(), left_pos + text_offset + 14.0, top_pos + 10.0 + 32.0, &state.constants, &mut state.texture_set, ctx)?; + state.font.draw_text( + state.textscript_vm.line_3.iter().copied(), + left_pos + text_offset + 14.0, + top_pos + 10.0 + 32.0, + &state.constants, + &mut state.texture_set, + ctx, + )?; + } + + if let TextScriptExecutionState::WaitInput(_, _, tick) = state.textscript_vm.state { + if tick > 10 { + let (mut x, y) = match state.textscript_vm.current_line { + TextScriptLine::Line1 => ( + state.font.text_width(state.textscript_vm.line_1.iter().copied(), &state.constants), + top_pos + 10.0, + ), + TextScriptLine::Line2 => ( + state.font.text_width(state.textscript_vm.line_2.iter().copied(), &state.constants), + top_pos + 10.0 + 16.0, + ), + TextScriptLine::Line3 => ( + state.font.text_width(state.textscript_vm.line_3.iter().copied(), &state.constants), + top_pos + 10.0 + 32.0, + ), + }; + x += left_pos + text_offset + 14.0; + + draw_rect( + ctx, + Rect::new_size( + (x * state.scale) as isize, + (y * state.scale) as isize, + (5.0 * state.scale) as isize, + (10.0 * state.scale) as isize, + ), + Color::from_rgb(255, 255, 255), + )?; + } } Ok(()) } fn draw_light(&self, x: f32, y: f32, size: f32, color: (u8, u8, u8), batch: &mut SizedBatch) { - batch.add_rect_scaled_tinted(x - size * 32.0, y - size * 32.0, (color.0, color.1, color.2, 255), - size, - size, - &Rect::new(0, 0, 64, 64)) + batch.add_rect_scaled_tinted( + x - size * 32.0, + y - size * 32.0, + (color.0, color.1, color.2, 255), + size, + size, + &Rect::new(0, 0, 64, 64), + ) } fn draw_light_map(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { @@ -520,88 +605,151 @@ impl GameScene { let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "builtin/lightmap/spot")?; if !self.player1.cond.hidden() && self.inventory_player1.get_current_weapon().is_some() { - self.draw_light(fix9_scale(self.player1.x - self.frame.x, scale), - fix9_scale(self.player1.y - self.frame.y, scale), - 4.0, (140, 140, 140), batch); + self.draw_light( + fix9_scale(self.player1.x - self.frame.x, scale), + fix9_scale(self.player1.y - self.frame.y, scale), + 4.0, + (140, 140, 140), + batch, + ); } for bullet in self.bullet_manager.bullets.iter() { - self.draw_light(fix9_scale(bullet.x - self.frame.x, scale), - fix9_scale(bullet.y - self.frame.y, scale), - 0.3, (200, 200, 200), batch); + self.draw_light( + fix9_scale(bullet.x - self.frame.x, scale), + fix9_scale(bullet.y - self.frame.y, scale), + 0.3, + (200, 200, 200), + batch, + ); } for caret in state.carets.iter() { match caret.ctype { CaretType::ProjectileDissipation | CaretType::Shoot => { - self.draw_light(fix9_scale(caret.x - self.frame.x, scale), - fix9_scale(caret.y - self.frame.y, scale), - 1.0, (200, 200, 200), batch); + self.draw_light( + fix9_scale(caret.x - self.frame.x, scale), + fix9_scale(caret.y - self.frame.y, scale), + 1.0, + (200, 200, 200), + batch, + ); } _ => {} } } for npc in self.npc_list.iter_alive() { - if npc.cond.hidden() || (npc.x < (self.frame.x - 128 * 0x200 - npc.display_bounds.width() as i32 * 0x200) - || npc.x > (self.frame.x + 128 * 0x200 + (state.canvas_size.0 as i32 + npc.display_bounds.width() as i32) * 0x200) - && npc.y < (self.frame.y - 128 * 0x200 - npc.display_bounds.height() as i32 * 0x200) - || npc.y > (self.frame.y + 128 * 0x200 + (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200)) { + if npc.cond.hidden() + || (npc.x < (self.frame.x - 128 * 0x200 - npc.display_bounds.width() as i32 * 0x200) + || npc.x + > (self.frame.x + + 128 * 0x200 + + (state.canvas_size.0 as i32 + npc.display_bounds.width() as i32) * 0x200) + && npc.y < (self.frame.y - 128 * 0x200 - npc.display_bounds.height() as i32 * 0x200) + || npc.y + > (self.frame.y + + 128 * 0x200 + + (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200)) + { continue; } match npc.npc_type { 1 => { - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 0.4, (255, 255, 0), batch); + self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 0.4, + (255, 255, 0), + batch, + ); } - 7 => self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 1.0, (100, 100, 100), batch), + 7 => self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 1.0, + (100, 100, 100), + batch, + ), 17 if npc.anim_num == 0 => { - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 2.0, (160, 0, 0), batch); - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 0.5, (255, 0, 0), batch); + self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 2.0, + (160, 0, 0), + batch, + ); + self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 0.5, + (255, 0, 0), + batch, + ); } 20 if npc.direction == Direction::Right => { - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 2.0, (0, 0, 150), batch); + self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 2.0, + (0, 0, 150), + batch, + ); if npc.anim_num < 2 { - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 2.1, (0, 0, 30), batch); + self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 2.1, + (0, 0, 30), + batch, + ); } } - 22 if npc.action_num == 1 && npc.anim_num == 1 => - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 3.0, (0, 0, 255), batch), + 22 if npc.action_num == 1 && npc.anim_num == 1 => self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 3.0, + (0, 0, 255), + batch, + ), 32 | 87 | 211 => { - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 2.0, (255, 30, 30), batch); + self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 2.0, + (255, 30, 30), + batch, + ); } 38 => { let flicker = (npc.anim_num ^ 5 & 3) as u8 * 15; - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 3.5, (130 + flicker, 40 + flicker, 0), batch); + self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 3.5, + (130 + flicker, 40 + flicker, 0), + batch, + ); } 70 => { let flicker = 50 + npc.anim_num as u8 * 15; - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 2.0, (flicker, flicker, flicker), batch); + self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 2.0, + (flicker, flicker, flicker), + batch, + ); } - 75 | 77 => self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 3.0, (255, 100, 0), batch), + 75 | 77 => self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 3.0, + (255, 100, 0), + batch, + ), 85 if npc.action_num == 1 => { let (color, color2) = if npc.direction == Direction::Left { ((0, 150, 100), (0, 50, 30)) @@ -609,25 +757,41 @@ impl GameScene { ((150, 0, 0), (50, 0, 0)) }; - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 1.5, color, batch); + self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 1.5, + color, + batch, + ); if npc.anim_num < 2 { - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale) - 8.0, - 2.1, color2, batch); + self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale) - 8.0, + 2.1, + color2, + batch, + ); } } 299 => { - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 4.0, (30, 30, 200), batch); + self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 4.0, + (30, 30, 200), + batch, + ); } 300 => { - self.draw_light(fix9_scale(npc.x - self.frame.x, scale), - fix9_scale(npc.y - self.frame.y, scale), - 1.5, (200, 10, 10), batch); + self.draw_light( + fix9_scale(npc.x - self.frame.x, scale), + fix9_scale(npc.y - self.frame.y, scale), + 1.5, + (200, 10, 10), + batch, + ); } _ => {} } @@ -660,8 +824,10 @@ impl GameScene { 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_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; - 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; + 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; + 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; if layer == TileLayer::Snack { rect = state.constants.world.snack_rect; @@ -669,9 +835,7 @@ impl GameScene { for y in tile_start_y..tile_end_y { for x in tile_start_x..tile_end_x { - let tile = *self.stage.map.tiles - .get((y * self.stage.map.width as usize) + x) - .unwrap(); + let tile = *self.stage.map.tiles.get((y * self.stage.map.width as usize) + x).unwrap(); match layer { TileLayer::Background => { @@ -704,8 +868,7 @@ impl GameScene { _ => {} } - batch.add_rect((x as f32 * 16.0 - 8.0) - frame_x, - (y as f32 * 16.0 - 8.0) - frame_y, &rect); + batch.add_rect((x as f32 * 16.0 - 8.0) - frame_x, (y as f32 * 16.0 - 8.0) - frame_y, &rect); } } @@ -753,7 +916,12 @@ impl GameScene { npc.shock = 16; for _ in 0..3 { - state.create_caret((bullet.x + npc.x) / 2, (bullet.y + npc.y) / 2, CaretType::HurtParticles, Direction::Left); + state.create_caret( + (bullet.x + npc.x) / 2, + (bullet.y + npc.y) / 2, + CaretType::HurtParticles, + Direction::Left, + ); } } @@ -762,9 +930,19 @@ impl GameScene { } } } else if !bullet.weapon_flags.flag_x10() - && bullet.btype != 13 && bullet.btype != 14 && bullet.btype != 15 - && bullet.btype != 28 && bullet.btype != 29 && bullet.btype != 30 { - state.create_caret((bullet.x + npc.x) / 2, (bullet.y + npc.y) / 2, CaretType::ProjectileDissipation, Direction::Right); + && bullet.btype != 13 + && bullet.btype != 14 + && bullet.btype != 15 + && bullet.btype != 28 + && bullet.btype != 29 + && bullet.btype != 30 + { + state.create_caret( + (bullet.x + npc.x) / 2, + (bullet.y + npc.y) / 2, + CaretType::ProjectileDissipation, + Direction::Right, + ); state.sound_manager.play_sfx(31); bullet.life = 0; continue; @@ -776,10 +954,9 @@ impl GameScene { } if npc.cond.explode_die() { - let can_drop_missile = [&self.inventory_player1, &self.inventory_player2] - .iter() - .any(|inv| inv.has_weapon(WeaponType::MissileLauncher) || - inv.has_weapon(WeaponType::SuperMissileLauncher)); + let can_drop_missile = [&self.inventory_player1, &self.inventory_player2].iter().any(|inv| { + inv.has_weapon(WeaponType::MissileLauncher) || inv.has_weapon(WeaponType::SuperMissileLauncher) + }); self.npc_list.kill_npc(npc.id as usize, true, can_drop_missile, state); } @@ -797,19 +974,16 @@ impl GameScene { continue; } - let hit = ( - npc.npc_flags.shootable() - && (npc.x - npc.hit_bounds.right as i32) < (bullet.x + bullet.enemy_hit_width as i32) - && (npc.x + npc.hit_bounds.right as i32) > (bullet.x - bullet.enemy_hit_width as i32) - && (npc.y - npc.hit_bounds.top as i32) < (bullet.y + bullet.enemy_hit_height as i32) - && (npc.y + npc.hit_bounds.bottom as i32) > (bullet.y - bullet.enemy_hit_height as i32) - ) || ( - npc.npc_flags.invulnerable() + let hit = (npc.npc_flags.shootable() + && (npc.x - npc.hit_bounds.right as i32) < (bullet.x + bullet.enemy_hit_width as i32) + && (npc.x + npc.hit_bounds.right as i32) > (bullet.x - bullet.enemy_hit_width as i32) + && (npc.y - npc.hit_bounds.top as i32) < (bullet.y + bullet.enemy_hit_height as i32) + && (npc.y + npc.hit_bounds.bottom as i32) > (bullet.y - bullet.enemy_hit_height as i32)) + || (npc.npc_flags.invulnerable() && (npc.x - npc.hit_bounds.right as i32) < (bullet.x + bullet.hit_bounds.right as i32) && (npc.x + npc.hit_bounds.right as i32) > (bullet.x - bullet.hit_bounds.left as i32) && (npc.y - npc.hit_bounds.top as i32) < (bullet.y + bullet.hit_bounds.bottom as i32) - && (npc.y + npc.hit_bounds.bottom as i32) > (bullet.y - bullet.hit_bounds.top as i32) - ); + && (npc.y + npc.hit_bounds.bottom as i32) > (bullet.y - bullet.hit_bounds.top as i32)); if !hit { continue; @@ -835,7 +1009,14 @@ impl GameScene { let destroy_count = 4usize * (2usize).pow((npc.size as u32).saturating_sub(1)); - self.npc_list.create_death_smoke(npc.x, npc.y, npc.display_bounds.right, destroy_count, state, &npc.rng); + self.npc_list.create_death_smoke( + npc.x, + npc.y, + npc.display_bounds.right, + destroy_count, + state, + &npc.rng, + ); npc.cond.set_alive(false); } } else { @@ -915,17 +1096,34 @@ impl GameScene { self.player2.damage = 0; } - for npc in self.npc_list.iter_alive() { - npc.tick(state, ([&mut self.player1, &mut self.player2], &self.npc_list, &mut self.stage, &self.bullet_manager))?; + npc.tick( + state, + ([&mut self.player1, &mut self.player2], &self.npc_list, &mut self.stage, &self.bullet_manager), + )?; } - self.boss.tick(state, ([&mut self.player1, &mut self.player2], &self.npc_list, &mut self.stage, &self.bullet_manager))?; + self.boss.tick( + state, + ([&mut self.player1, &mut self.player2], &self.npc_list, &mut self.stage, &self.bullet_manager), + )?; self.player1.tick_map_collisions(state, &self.npc_list, &mut self.stage); - self.player1.tick_npc_collisions(TargetPlayer::Player1, state, &self.npc_list, &mut self.boss, &mut self.inventory_player1); + self.player1.tick_npc_collisions( + TargetPlayer::Player1, + state, + &self.npc_list, + &mut self.boss, + &mut self.inventory_player1, + ); self.player2.tick_map_collisions(state, &self.npc_list, &mut self.stage); - self.player2.tick_npc_collisions(TargetPlayer::Player2, state, &self.npc_list, &mut self.boss, &mut self.inventory_player2); + self.player2.tick_npc_collisions( + TargetPlayer::Player2, + state, + &self.npc_list, + &mut self.boss, + &mut self.inventory_player2, + ); for npc in self.npc_list.iter_alive() { if !npc.npc_flags.ignore_solidity() { @@ -945,9 +1143,11 @@ impl GameScene { match self.frame.update_target { UpdateTarget::Player => { - if self.player2.cond.alive() && !self.player2.cond.hidden() + 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 { + && abs(self.player1.y - self.player2.y) < 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; @@ -979,23 +1179,15 @@ impl GameScene { if state.control_flags.control_enabled() { #[allow(clippy::cast_ref_to_mut)] - let inventory = unsafe { &mut *(&self.inventory_player1 as *const Inventory as *mut Inventory) }; // fuck off + let inventory = unsafe { &mut *(&self.inventory_player1 as *const Inventory as *mut Inventory) }; // fuck off if let Some(weapon) = self.inventory_player1.get_current_weapon_mut() { - weapon.tick(&mut self.player1, - TargetPlayer::Player1, - inventory, - &mut self.bullet_manager, - state); + weapon.tick(&mut self.player1, TargetPlayer::Player1, inventory, &mut self.bullet_manager, state); } #[allow(clippy::cast_ref_to_mut)] - let inventory = unsafe { &mut *(&self.inventory_player2 as *const Inventory as *mut Inventory) }; + let inventory = unsafe { &mut *(&self.inventory_player2 as *const Inventory as *mut Inventory) }; if let Some(weapon) = self.inventory_player2.get_current_weapon_mut() { - weapon.tick(&mut self.player2, - TargetPlayer::Player2, - inventory, - &mut self.bullet_manager, - state); + weapon.tick(&mut self.player2, TargetPlayer::Player2, inventory, &mut self.bullet_manager, state); } self.hud_player1.tick(state, (&self.player1, &mut self.inventory_player1))?; @@ -1009,8 +1201,9 @@ impl GameScene { fn draw_debug_npc(&self, npc: &NPC, state: &mut SharedGameState, ctx: &mut Context) -> GameResult { if npc.x < (self.frame.x - 128 - npc.display_bounds.width() as i32 * 0x200) || npc.x > (self.frame.x + 128 + (state.canvas_size.0 as i32 + npc.display_bounds.width() as i32) * 0x200) - && npc.y < (self.frame.y - 128 - npc.display_bounds.height() as i32 * 0x200) - || npc.y > (self.frame.y + 128 + (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200) { + && npc.y < (self.frame.y - 128 - npc.display_bounds.height() as i32 * 0x200) + || npc.y > (self.frame.y + 128 + (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200) + { return Ok(()); } @@ -1025,29 +1218,37 @@ impl GameScene { let caret_rect = Rect::new_size(2, 74, 4, 4); let caret2_rect = Rect::new_size(65, 9, 6, 6); - for (idx, (&ox, &oy)) in crate::physics::OFF_X.iter() - .zip(crate::physics::OFF_Y.iter()).enumerate() { + for (idx, (&ox, &oy)) in crate::physics::OFF_X.iter().zip(crate::physics::OFF_Y.iter()).enumerate() { if idx == hit_rect_size { break; } - batch.add_rect(((x + ox) * 16 - self.frame.x / 0x200) as f32 - 2.0, - ((y + oy) * 16 - self.frame.y / 0x200) as f32 - 2.0, - &caret_rect); + batch.add_rect( + ((x + ox) * 16 - self.frame.x / 0x200) as f32 - 2.0, + ((y + oy) * 16 - self.frame.y / 0x200) as f32 - 2.0, + &caret_rect, + ); } - - batch.add_rect(((npc.x - self.frame.x) / 0x200) as f32 - 3.0, - ((npc.y - self.frame.y) / 0x200) as f32 - 3.0, - &caret2_rect); + batch.add_rect( + ((npc.x - self.frame.x) / 0x200) as f32 - 3.0, + ((npc.y - self.frame.y) / 0x200) as f32 - 3.0, + &caret2_rect, + ); batch.draw(ctx)?; } let text = format!("{}:{}:{}", npc.id, npc.npc_type, npc.action_num); - state.font.draw_colored_text(text.chars(), ((npc.x - self.frame.x) / 0x200) as f32, ((npc.y - self.frame.y) / 0x200) as f32, - ((npc.id & 0xf0) as u8, (npc.cond.0 >> 8) as u8, (npc.id & 0x0f << 4) as u8, 255), - &state.constants, &mut state.texture_set, ctx)?; + state.font.draw_colored_text( + text.chars(), + ((npc.x - self.frame.x) / 0x200) as f32, + ((npc.y - self.frame.y) / 0x200) as f32, + ((npc.id & 0xf0) as u8, (npc.cond.0 >> 8) as u8, (npc.id & 0x0f << 4) as u8, 255), + &state.constants, + &mut state.texture_set, + ctx, + )?; Ok(()) } @@ -1127,11 +1328,8 @@ impl Scene for GameScene { self.player2.controller.update(state, ctx)?; self.player2.controller.update_trigger(); - state.touch_controls.control_type = if state.control_flags.control_enabled() { - TouchControlType::Controls - } else { - TouchControlType::Dialog - }; + state.touch_controls.control_type = + if state.control_flags.control_enabled() { TouchControlType::Controls } else { TouchControlType::Dialog }; if state.settings.touch_controls { state.touch_controls.interact_icon = false; @@ -1170,7 +1368,7 @@ impl Scene for GameScene { TextScriptVM::run(state, self, ctx)?; #[cfg(feature = "scripting")] - state.lua.scene_tick(self); + state.lua.scene_tick(self); self.tick = self.tick.wrapping_add(1); Ok(()) @@ -1221,16 +1419,24 @@ impl Scene for GameScene { && self.stage.data.background_type != BackgroundType::Black && self.stage.data.background_type != BackgroundType::Outside && self.stage.data.background_type != BackgroundType::OutsideWind - && self.stage.data.background.name() != "bkBlack" { + && self.stage.data.background.name() != "bkBlack" + { self.draw_light_map(state, ctx)?; } self.boss.draw(state, ctx, &self.frame)?; for npc in self.npc_list.iter_alive() { if npc.x < (self.frame.x - 128 * 0x200 - npc.display_bounds.width() as i32 * 0x200) - || npc.x > (self.frame.x + 128 * 0x200 + (state.canvas_size.0 as i32 + npc.display_bounds.width() as i32) * 0x200) - && npc.y < (self.frame.y - 128 * 0x200 - npc.display_bounds.height() as i32 * 0x200) - || npc.y > (self.frame.y + 128 * 0x200 + (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200) { + || npc.x + > (self.frame.x + + 128 * 0x200 + + (state.canvas_size.0 as i32 + npc.display_bounds.width() as i32) * 0x200) + && npc.y < (self.frame.y - 128 * 0x200 - npc.display_bounds.height() as i32 * 0x200) + || npc.y + > (self.frame.y + + 128 * 0x200 + + (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200) + { continue; } @@ -1248,7 +1454,8 @@ impl Scene for GameScene { self.draw_carets(state, ctx)?; if state.settings.shader_effects && (self.stage.data.background_type == BackgroundType::Black - || self.stage.data.background.name() == "bkBlack") { + || self.stage.data.background.name() == "bkBlack") + { self.draw_light_map(state, ctx)?; } @@ -1263,27 +1470,55 @@ impl Scene for GameScene { 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 = 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.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(), + 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)?; + 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, + )?; } } } @@ -1294,16 +1529,18 @@ impl Scene for GameScene { self.draw_fade(state, ctx)?; if self.map_name_counter > 0 { - let map_name = if self.intro_mode { - state.constants.title.intro_text.chars() - } else { - self.stage.data.name.chars() - }; + let map_name = + if self.intro_mode { state.constants.title.intro_text.chars() } else { self.stage.data.name.chars() }; let width = state.font.text_width(map_name.clone(), &state.constants); - state.font.draw_text(map_name, - ((state.canvas_size.0 - width) / 2.0).floor(), 80.0, - &state.constants, &mut state.texture_set, ctx)?; + state.font.draw_text( + map_name, + ((state.canvas_size.0 - width) / 2.0).floor(), + 80.0, + &state.constants, + &mut state.texture_set, + ctx, + )?; } self.draw_text_boxes(state, ctx)?; @@ -1316,7 +1553,13 @@ impl Scene for GameScene { Ok(()) } - fn debug_overlay_draw(&mut self, components: &mut Components, state: &mut SharedGameState, ctx: &mut Context, ui: &mut imgui::Ui) -> GameResult { + fn debug_overlay_draw( + &mut self, + components: &mut Components, + state: &mut SharedGameState, + ctx: &mut Context, + ui: &mut imgui::Ui, + ) -> GameResult { components.live_debugger.run_ingame(self, state, ctx, ui)?; Ok(()) } diff --git a/src/text_script.rs b/src/text_script.rs index 52b918c..5845dcf 100644 --- a/src/text_script.rs +++ b/src/text_script.rs @@ -351,7 +351,7 @@ pub enum TextScriptExecutionState { Running(u16, u32), Msg(u16, u32, u32, u8), WaitTicks(u16, u32, u16), - WaitInput(u16, u32), + WaitInput(u16, u32, u16), WaitStanding(u16, u32), WaitConfirmation(u16, u32, u16, u8, ConfirmSelection), WaitFade(u16, u32), @@ -380,6 +380,7 @@ pub struct TextScriptVM { pub line_1: Vec, pub line_2: Vec, pub line_3: Vec, + prev_char: char, } impl Default for TextScriptVM { @@ -463,6 +464,7 @@ impl TextScriptVM { line_1: Vec::with_capacity(24), line_2: Vec::with_capacity(24), line_3: Vec::with_capacity(24), + prev_char: '\x00', } } @@ -560,13 +562,33 @@ impl TextScriptVM { } '\r' => {} _ if state.textscript_vm.current_line == TextScriptLine::Line1 => { + state.textscript_vm.prev_char = chr; state.textscript_vm.line_1.push(chr); + + let text_len = state.font.text_width(state.textscript_vm.line_1.iter().copied(), &state.constants); + if text_len >= 284.0 { + state.textscript_vm.current_line = TextScriptLine::Line2; + } } _ if state.textscript_vm.current_line == TextScriptLine::Line2 => { + state.textscript_vm.prev_char = chr; state.textscript_vm.line_2.push(chr); + + let text_len = state.font.text_width(state.textscript_vm.line_2.iter().copied(), &state.constants); + if text_len >= 284.0 { + state.textscript_vm.current_line = TextScriptLine::Line3; + } } _ if state.textscript_vm.current_line == TextScriptLine::Line3 => { + state.textscript_vm.prev_char = chr; state.textscript_vm.line_3.push(chr); + + let text_len = state.font.text_width(state.textscript_vm.line_3.iter().copied(), &state.constants); + if text_len >= 284.0 { + state.textscript_vm.line_1.clear(); + state.textscript_vm.line_1.append(&mut state.textscript_vm.line_2); + state.textscript_vm.line_2.append(&mut state.textscript_vm.line_3); + } } _ => {} } @@ -647,7 +669,9 @@ impl TextScriptVM { } break; } - TextScriptExecutionState::WaitInput(event, ip) => { + TextScriptExecutionState::WaitInput(event, ip, blink) => { + state.textscript_vm.state = TextScriptExecutionState::WaitInput(event, ip, (blink + 1) % 20); + if game_scene.player1.controller.trigger_jump() || game_scene.player1.controller.trigger_shoot() || game_scene.player1.controller.skip() @@ -714,6 +738,7 @@ impl TextScriptVM { OpCode::_STR => { let mut len = read_cur_varint(&mut cursor)? as u32; if state.textscript_vm.flags.render() { + state.textscript_vm.prev_char = '\x00'; exec_state = TextScriptExecutionState::Msg(event, cursor.position() as u32, len, 4); } else { while len > 0 { @@ -853,7 +878,7 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::WaitStanding(event, cursor.position() as u32); } OpCode::NOD => { - exec_state = TextScriptExecutionState::WaitInput(event, cursor.position() as u32); + exec_state = TextScriptExecutionState::WaitInput(event, cursor.position() as u32, 0); } OpCode::FLp | OpCode::FLm => { let flag_num = read_cur_varint(&mut cursor)? as usize;