diff --git a/src/components/text_boxes.rs b/src/components/text_boxes.rs index ceaa4fd..e1aaa56 100644 --- a/src/components/text_boxes.rs +++ b/src/components/text_boxes.rs @@ -3,6 +3,7 @@ use crate::entity::GameEntity; use crate::frame::Frame; use crate::framework::context::Context; use crate::framework::error::GameResult; +use crate::graphics; use crate::graphics::draw_rect; use crate::scripting::tsc::text_script::{ConfirmSelection, TextScriptExecutionState, TextScriptLine}; use crate::shared_game_state::SharedGameState; @@ -156,15 +157,29 @@ impl GameEntity<()> for TextBoxes { let text_offset = if state.textscript_vm.face == 0 { 0.0 } else { 56.0 }; + let y_offset = if let TextScriptExecutionState::MsgNewLine(_, _, _, _, counter) = state.textscript_vm.state { + 16.0 - counter as f32 * 4.0 + } else { + 0.0 + }; + let lines = [&state.textscript_vm.line_1, &state.textscript_vm.line_2, &state.textscript_vm.line_3]; + let clip_rect = Rect::new_size( + 0, + ((top_pos + 6.0) * state.scale) as isize, + state.screen_size.0 as isize, + (48.0 * state.scale) as isize, + ); + + graphics::set_clip_rect(ctx, Some(clip_rect))?; for (idx, line) in lines.iter().enumerate() { if !line.is_empty() { if state.constants.textscript.text_shadow { state.font.draw_text_with_shadow( line.iter().copied(), left_pos + text_offset + 14.0, - top_pos + 10.0 + idx as f32 * 16.0, + top_pos + 10.0 + idx as f32 * 16.0 - y_offset, &state.constants, &mut state.texture_set, ctx, @@ -173,7 +188,7 @@ impl GameEntity<()> for TextBoxes { state.font.draw_text( line.iter().copied(), left_pos + text_offset + 14.0, - top_pos + 10.0 + idx as f32 * 16.0, + top_pos + 10.0 + idx as f32 * 16.0 - y_offset, &state.constants, &mut state.texture_set, ctx, @@ -181,6 +196,7 @@ impl GameEntity<()> for TextBoxes { } } } + graphics::set_clip_rect(ctx, None)?; if let TextScriptExecutionState::WaitInput(_, _, tick) = state.textscript_vm.state { if tick > 10 { diff --git a/src/framework/render_opengl.rs b/src/framework/render_opengl.rs index 1ce15dc..31c56d5 100644 --- a/src/framework/render_opengl.rs +++ b/src/framework/render_opengl.rs @@ -714,6 +714,7 @@ impl BackendRenderer for OpenGLRenderer { unsafe { let (width_u, height_u) = (width as u32, height as u32); if self.imgui_data.last_size != (width_u, height_u) { + self.imgui_data.last_size = (width_u, height_u); gl.gl.BindFramebuffer(gl::FRAMEBUFFER, 0); gl.gl.BindTexture(gl::TEXTURE_2D, self.imgui_data.surf_texture); @@ -1028,7 +1029,12 @@ impl BackendRenderer for OpenGLRenderer { unsafe { if let Some(rect) = &rect { gl.gl.Enable(gl::SCISSOR_TEST); - gl.gl.Scissor(rect.left as GLint, rect.top as GLint, rect.width() as GLint, rect.height() as GLint); + gl.gl.Scissor( + rect.left as GLint, + self.imgui_data.last_size.1 as GLint - rect.bottom as GLint, + rect.width() as GLint, + rect.height() as GLint, + ); } else { gl.gl.Disable(gl::SCISSOR_TEST); } diff --git a/src/scripting/tsc/text_script.rs b/src/scripting/tsc/text_script.rs index 342c725..da10540 100644 --- a/src/scripting/tsc/text_script.rs +++ b/src/scripting/tsc/text_script.rs @@ -11,8 +11,8 @@ use std::rc::Rc; use num_traits::{clamp, FromPrimitive}; use crate::bitfield; -use crate::common::Direction::{Left, Right}; use crate::common::{Direction, FadeDirection, FadeState, Rect}; +use crate::common::Direction::{Left, Right}; use crate::engine_constants::EngineConstants; use crate::entity::GameEntity; use crate::frame::UpdateTarget; @@ -87,6 +87,7 @@ pub enum TextScriptExecutionState { Ended, Running(u16, u32), Msg(u16, u32, u32, u8), + MsgNewLine(u16, u32, u32, u8, u8), WaitTicks(u16, u32, u16), WaitInput(u16, u32, u16), WaitStanding(u16, u32), @@ -324,6 +325,7 @@ impl TextScriptVM { if let Some((_, bytecode)) = cached_event { let mut cursor = Cursor::new(bytecode); + let mut new_line = false; cursor.seek(SeekFrom::Start(ip as u64))?; let chr = std::char::from_u32(read_cur_varint(&mut cursor)? as u32).unwrap_or('\u{fffd}'); @@ -336,9 +338,7 @@ impl TextScriptVM { state.textscript_vm.current_line = TextScriptLine::Line3; } '\n' => { - 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); + new_line = true; } '\r' => {} _ if state.textscript_vm.current_line == TextScriptLine::Line1 => { @@ -368,9 +368,7 @@ impl TextScriptVM { 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); + new_line = true; } } _ => {} @@ -395,8 +393,11 @@ impl TextScriptVM { state.sound_manager.play_sfx(2); } - state.textscript_vm.state = - TextScriptExecutionState::Msg(event, cursor.position() as u32, remaining - 1, ticks); + state.textscript_vm.state = if !new_line { + TextScriptExecutionState::Msg(event, cursor.position() as u32, remaining - 1, ticks) + } else { + TextScriptExecutionState::MsgNewLine(event, cursor.position() as u32, remaining - 1, ticks, 4) + }; } else { state.textscript_vm.state = TextScriptExecutionState::Running(event, cursor.position() as u32); @@ -405,6 +406,23 @@ impl TextScriptVM { state.textscript_vm.reset(); } } + TextScriptExecutionState::MsgNewLine(event, ip, remaining, ticks, mut counter) => { + counter = if state.textscript_vm.flags.fast() || state.textscript_vm.flags.cutscene_skip() { + 0 + } else { + counter.saturating_sub(1) + }; + + if counter == 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); + state.textscript_vm.state = TextScriptExecutionState::Msg(event, ip, remaining, ticks); + } else { + state.textscript_vm.state = TextScriptExecutionState::MsgNewLine(event, ip, remaining, ticks, counter); + } + break; + } TextScriptExecutionState::WaitTicks(event, ip, ticks) => { if ticks == 0 { state.textscript_vm.state = TextScriptExecutionState::Running(event, ip); @@ -543,7 +561,8 @@ impl TextScriptVM { pos_y += 0x33; } - state.textscript_vm.state = TextScriptExecutionState::FallingIsland(event, ip, pos_x, pos_y, tick, mode); + state.textscript_vm.state = + TextScriptExecutionState::FallingIsland(event, ip, pos_x, pos_y, tick, mode); break; } TextScriptExecutionState::SaveProfile(event, ip) => { @@ -1622,7 +1641,14 @@ impl TextScriptVM { TSCOpCode::XX1 => { let mode = read_cur_varint(&mut cursor)?; - exec_state = TextScriptExecutionState::FallingIsland(event, cursor.position() as u32, 0x15000, 0x8000, 0, mode != 0); + exec_state = TextScriptExecutionState::FallingIsland( + event, + cursor.position() as u32, + 0x15000, + 0x8000, + 0, + mode != 0, + ); } // unimplemented opcodes // Zero operands