diff --git a/src/components/flash.rs b/src/components/flash.rs new file mode 100644 index 0000000..6dc4809 --- /dev/null +++ b/src/components/flash.rs @@ -0,0 +1,111 @@ +use crate::common::{Color, Rect}; +use crate::entity::GameEntity; +use crate::frame::Frame; +use crate::framework::context::Context; +use crate::framework::error::GameResult; +use crate::framework::graphics; +use crate::shared_game_state::SharedGameState; + +pub enum FlashState { + None, + Cross(i32, i32, u16), + Blink(u16), +} + +pub struct Flash { + state: FlashState, +} + +impl Flash { + pub fn new() -> Flash { + Flash { + state: FlashState::None + } + } + + pub fn set_cross(&mut self, x: i32, y: i32) { + self.state = FlashState::Cross(x, y, 0); + } + + pub fn set_blink(&mut self) { + self.state = FlashState::Blink(0); + } + + pub fn stop(&mut self) { + self.state = FlashState::None; + } +} + +impl GameEntity<()> for Flash { + fn tick(&mut self, state: &mut SharedGameState, _custom: ()) -> GameResult<()> { + match self.state { + FlashState::None => {} + FlashState::Cross(x, y, tick) => { + self.state = if tick > 128 { + FlashState::None + } else { + FlashState::Cross(x, y, tick + 1) + }; + } + FlashState::Blink(tick) => { + self.state = if tick > 20 { + FlashState::None + } else { + FlashState::Blink(tick + 1) + }; + } + } + Ok(()) + } + + fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, frame: &Frame) -> GameResult<()> { + const WHITE: Color = Color::new(1.0, 1.0, 1.0, 1.0); + + match self.state { + FlashState::None => {} + FlashState::Cross(x, y, tick) => { + let tick = tick as f32 + state.frame_time as f32; + let frame_pos = frame.xy_interpolated(state.frame_time, state.scale); + + let (cen_x, cen_y) = ( + (x as f32 / 512.0) - frame_pos.0, + (y as f32 / 512.0) - frame_pos.1 + ); + + let width = if tick > 100.0 { + (1.0 - (tick - 100.0).max(0.0) / 28.0).powf(2.0) * state.canvas_size.0 + } else { + (1.0 - (0.97f32).powf(tick)).max(0.0) * state.canvas_size.0 + }; + + + let mut rect = Rect { + left: 0, + top: ((cen_y - width) * state.scale) as isize, + right: (state.canvas_size.0 * state.scale) as isize, + bottom: ((cen_y + width) * state.scale) as isize + }; + + graphics::draw_rect(ctx, rect, WHITE)?; + + if tick <= 100.0 { + rect = Rect { + left: ((cen_x - width) * state.scale) as isize, + top: 0, + right: ((cen_x + width) * state.scale) as isize, + bottom: (state.canvas_size.1 * state.scale) as isize + }; + + graphics::draw_rect(ctx, rect, WHITE)?; + } + } + FlashState::Blink(tick) => { + if tick / 2 % 2 != 0 { + graphics::clear(ctx, WHITE); + } + } + } + + Ok(()) + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index 06aa8c3..00c3c68 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,5 +1,6 @@ pub mod boss_life_bar; pub mod draw_common; +pub mod flash; pub mod hud; pub mod inventory; pub mod stage_select; diff --git a/src/scene/game_scene.rs b/src/scene/game_scene.rs index 56b321d..48b1141 100644 --- a/src/scene/game_scene.rs +++ b/src/scene/game_scene.rs @@ -1,11 +1,11 @@ use log::info; use num_traits::{abs, clamp}; -use crate::weapon::bullet::BulletManager; use crate::caret::CaretType; 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::{draw_number, Alignment}; +use crate::components::flash::Flash; use crate::components::hud::HUD; use crate::components::stage_select::StageSelect; use crate::entity::GameEntity; @@ -30,6 +30,7 @@ use crate::shared_game_state::{Season, SharedGameState}; use crate::stage::{BackgroundType, Stage}; use crate::text_script::{ConfirmSelection, ScriptMode, TextScriptExecutionState, TextScriptLine, TextScriptVM}; use crate::texture_set::SizedBatch; +use crate::weapon::bullet::BulletManager; use crate::weapon::WeaponType; pub struct GameScene { @@ -37,6 +38,7 @@ pub struct GameScene { pub stage: Stage, pub boss_life_bar: BossLifeBar, pub stage_select: StageSelect, + pub flash: Flash, pub hud_player1: HUD, pub hud_player2: HUD, pub frame: Frame, @@ -86,6 +88,7 @@ impl GameScene { inventory_player2: Inventory::new(), boss_life_bar: BossLifeBar::new(), stage_select: StageSelect::new(), + flash: Flash::new(), hud_player1: HUD::new(Alignment::Left), hud_player2: HUD::new(Alignment::Right), frame: Frame { @@ -1104,7 +1107,13 @@ impl GameScene { } self.boss.tick( state, - ([&mut self.player1, &mut self.player2], &self.npc_list, &mut self.stage, &self.bullet_manager), + ( + [&mut self.player1, &mut self.player2], + &self.npc_list, + &mut self.stage, + &self.bullet_manager, + &mut self.flash, + ), )?; self.player1.tick_map_collisions(state, &self.npc_list, &mut self.stage); @@ -1365,12 +1374,15 @@ impl Scene for GameScene { _ => {} } + self.flash.tick(state, ())?; TextScriptVM::run(state, self, ctx)?; #[cfg(feature = "scripting")] state.lua.scene_tick(self); - self.tick = self.tick.wrapping_add(1); + if state.control_flags.control_enabled() { + self.tick = self.tick.wrapping_add(1); + } Ok(()) } @@ -1458,6 +1470,7 @@ impl Scene for GameScene { { self.draw_light_map(state, ctx)?; } + self.flash.draw(state, ctx, &self.frame)?; /*graphics::set_canvas(ctx, None); state.game_canvas.draw(ctx, DrawParam::new() diff --git a/src/text_script.rs b/src/text_script.rs index 3d16a44..5085927 100644 --- a/src/text_script.rs +++ b/src/text_script.rs @@ -1544,6 +1544,11 @@ impl TextScriptVM { exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); } + OpCode::FLA => { + game_scene.flash.set_blink(); + + exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32); + } OpCode::INI => { exec_state = TextScriptExecutionState::Reset; } @@ -1572,7 +1577,6 @@ impl TextScriptVM { | OpCode::KE2 | OpCode::CRE | OpCode::CSS - | OpCode::FLA | OpCode::MLP | OpCode::SPS | OpCode::FR2