diff --git a/game/src/game/tutorial.rs b/game/src/game/tutorial.rs index 1c17b66..e53f7e2 100644 --- a/game/src/game/tutorial.rs +++ b/game/src/game/tutorial.rs @@ -26,11 +26,10 @@ use super::{ janggu_state_with_tick::{self, JangguStateWithTick}, }, render_video::VideoFileRenderer, - util::confirm_dialog::{render_confirm_dialog, DialogButton}, + util::confirm_dialog::render_confirm_dialog, }; fn ask_for_tutorial(common_context: &mut GameCommonContext) -> bool { - let ask_started_at = Instant::now(); let mut selected = None; let mut janggu_state = JangguStateWithTick::new(); @@ -46,8 +45,15 @@ fn ask_for_tutorial(common_context: &mut GameCommonContext) -> bool { ) .unwrap(); - 'running: loop { - let tick = ask_started_at.elapsed().as_millis(); + // show video only + let video_only_duration = 150; + let video_only_started_at = Instant::now(); + loop { + let tick = video_only_started_at.elapsed().as_millis(); + + if tick > video_only_duration { + break; + } for i in common_context.event_pump.poll_iter() { if event_loop_common(&i, &mut common_context.coins) { @@ -56,9 +62,87 @@ fn ask_for_tutorial(common_context: &mut GameCommonContext) -> bool { } } + // Decode background video + background_video.wanted_time_in_second = Rational64::new( + common_context.game_initialized_at.elapsed().as_millis() as i64, + 1000, + ); + + common_context.canvas.clear(); + + // render bga + background_video.render_frame(&mut background_video_texture); + common_context + .canvas + .copy(&background_video_texture, None, None) + .unwrap(); + + render_common(common_context); + common_context.canvas.present(); + } + + // fade in + let fade_in_started_at = Instant::now(); + let fade_in_duration = 300; + let timeout = 10; + loop { + let fade_in_tick = fade_in_started_at.elapsed().as_millis(); + + if fade_in_tick > fade_in_duration { + break; + } + + for i in common_context.event_pump.poll_iter() { + if event_loop_common(&i, &mut common_context.coins) { + background_video.stop_decoding(); + return false; + } + } + + // Decode background video + background_video.wanted_time_in_second = Rational64::new( + common_context.game_initialized_at.elapsed().as_millis() as i64, + 1000, + ); + + common_context.canvas.clear(); + + // render bga + background_video.render_frame(&mut background_video_texture); + common_context + .canvas + .copy(&background_video_texture, None, None) + .unwrap(); + + // render dialog + render_confirm_dialog( + common_context, + format!("튜토리얼을 진행하시겠습니까?\n남은 시간: {}", timeout).as_str(), + None, + None, + (common_context.game_initialized_at.elapsed().as_millis() as f64 % 1000.0) / 1000.0, + Some((ezing::sine_out(fade_in_tick as f64 / fade_in_duration as f64) * 255.0) as u8), + ); + + render_common(common_context); + common_context.canvas.present(); + } + + // show dialog + let dialog_started_at = Instant::now(); + 'running: loop { + let tick = dialog_started_at.elapsed().as_millis(); + + for i in common_context.event_pump.poll_iter() { + if event_loop_common(&i, &mut common_context.coins) { + selected = Some(false); + break 'running; + } + } + // break when timeout - let elapsed_secs = ask_started_at.elapsed().as_secs(); - if elapsed_secs > 10 { + let elapsed_secs = dialog_started_at.elapsed().as_secs(); + if elapsed_secs > timeout { break 'running; } @@ -99,13 +183,68 @@ fn ask_for_tutorial(common_context: &mut GameCommonContext) -> bool { common_context, format!( "튜토리얼을 진행하시겠습니까?\n남은 시간: {}", - 10 - elapsed_secs + timeout - elapsed_secs + ) + .as_str(), + None, + None, + (common_context.game_initialized_at.elapsed().as_millis() as f64 % 1000.0) / 1000.0, + None, + ); + render_common(common_context); + common_context.canvas.present(); + } + + // fade out + let fade_out_started_at = Instant::now(); + let fade_out_duration = 300; + let last_elapsed_secs = timeout - dialog_started_at.elapsed().as_secs().min(10); + loop { + let fade_out_tick = fade_out_started_at.elapsed().as_millis(); + + if fade_out_tick > fade_out_duration { + break; + } + + for i in common_context.event_pump.poll_iter() { + if event_loop_common(&i, &mut common_context.coins) { + background_video.stop_decoding(); + return false; + } + } + + // Decode background video + background_video.wanted_time_in_second = Rational64::new( + common_context.game_initialized_at.elapsed().as_millis() as i64, + 1000, + ); + + common_context.canvas.clear(); + + // render bga + background_video.render_frame(&mut background_video_texture); + common_context + .canvas + .copy(&background_video_texture, None, None) + .unwrap(); + + // render dialog + render_confirm_dialog( + common_context, + format!( + "튜토리얼을 진행하시겠습니까?\n남은 시간: {}", + last_elapsed_secs ) .as_str(), None, None, (common_context.game_initialized_at.elapsed().as_millis() as f64 % 1000.0) / 1000.0, + Some( + ((1.0 - ezing::sine_out(fade_out_tick as f64 / fade_out_duration as f64)) * 255.0) + as u8, + ), ); + render_common(common_context); common_context.canvas.present(); } diff --git a/game/src/game/util/confirm_dialog.rs b/game/src/game/util/confirm_dialog.rs index b86d25a..bc39d87 100644 --- a/game/src/game/util/confirm_dialog.rs +++ b/game/src/game/util/confirm_dialog.rs @@ -98,11 +98,6 @@ macro_rules! create_button_texture { }}; } -pub enum DialogButton { - Yes, - No, -} - /// Renders dialog at center pub fn render_confirm_dialog( common_context: &mut GameCommonContext, @@ -110,6 +105,7 @@ pub fn render_confirm_dialog( yes_button_text: Option<&str>, no_button_text: Option<&str>, animation_progress: f64, + alpha: Option, ) { // Set font-size, line-height and load font let font_size = 38; @@ -130,7 +126,7 @@ pub fn render_confirm_dialog( .expect("Failed to load font"); let texture_creator = common_context.canvas.texture_creator(); - let font_textures: Vec = message + let mut font_textures: Vec = message .split('\n') .into_iter() .map(|x| render_font_texture!(texture_creator, font, font_color, x)) @@ -177,7 +173,7 @@ pub fn render_confirm_dialog( assert!(min_button_gap <= button_gap && button_gap <= max_button_gap); // Render button area - let button_area = texture_creator + let mut button_area = texture_creator .create_texture_from_surface({ // Calculate width and height let width = button_gap * 2 @@ -296,7 +292,7 @@ pub fn render_confirm_dialog( let dialog_y = ((viewport.height() - dialog_height) / 2) as i32; common_context .canvas - .set_draw_color(Color::RGBA(0x62, 0x62, 0x62, 255)); + .set_draw_color(Color::RGBA(0x62, 0x62, 0x62, alpha.unwrap_or(255))); common_context .canvas .fill_rect(Rect::new( @@ -308,9 +304,10 @@ pub fn render_confirm_dialog( .unwrap(); // Copy background - let background = texture_creator + let mut background = texture_creator .load_texture("assets/dialog/bg.png") .expect("Failed to load background texture"); + background.set_alpha_mod(alpha.unwrap_or(255)); common_context .canvas .copy( @@ -321,7 +318,11 @@ pub fn render_confirm_dialog( .unwrap(); // Copy font textures - for (line_idx, line_texture) in font_textures.iter().enumerate() { + for (line_idx, line_texture) in font_textures.iter_mut().enumerate() { + if let Some(alpha) = alpha { + line_texture.set_alpha_mod(alpha); + } + common_context .canvas .copy( @@ -342,6 +343,10 @@ pub fn render_confirm_dialog( // Copy button let button_area_bottom = dialog_y + (dialog_height - dialog_padding_y) as i32; let button_area_y = button_area_bottom - button_area.query().height as i32; + if let Some(alpha) = alpha { + button_area.set_alpha_mod(alpha); + } + common_context .canvas .copy(