2022-01-05 05:08:36 +00:00
|
|
|
use crate::common::{Color, Rect};
|
2022-02-07 02:52:19 +00:00
|
|
|
use crate::engine_constants::AnimatedFace;
|
2022-01-05 05:08:36 +00:00
|
|
|
use crate::entity::GameEntity;
|
|
|
|
use crate::framework::context::Context;
|
|
|
|
use crate::framework::error::GameResult;
|
2022-11-19 17:20:03 +00:00
|
|
|
use crate::framework::graphics;
|
|
|
|
use crate::game::frame::Frame;
|
|
|
|
use crate::game::scripting::tsc::text_script::{ConfirmSelection, TextScriptExecutionState, TextScriptLine};
|
2022-11-20 19:38:36 +00:00
|
|
|
use crate::game::shared_game_state::SharedGameState;
|
|
|
|
use crate::graphics::font::{Font, Symbols};
|
2022-01-05 05:08:36 +00:00
|
|
|
|
2022-02-06 17:22:26 +00:00
|
|
|
pub struct TextBoxes {
|
2022-02-07 02:52:19 +00:00
|
|
|
pub slide_in: u8,
|
2022-02-06 17:22:26 +00:00
|
|
|
pub anim_counter: usize,
|
2022-02-07 02:52:19 +00:00
|
|
|
animated_face: AnimatedFace,
|
2022-02-06 17:22:26 +00:00
|
|
|
}
|
2022-01-05 05:08:36 +00:00
|
|
|
|
|
|
|
const FACE_TEX: &str = "Face";
|
2022-02-07 02:52:19 +00:00
|
|
|
const SWITCH_FACE_TEX: [&str; 5] = ["Face1", "Face2", "Face3", "Face4", "Face5"];
|
2022-01-05 05:08:36 +00:00
|
|
|
|
|
|
|
impl TextBoxes {
|
|
|
|
pub fn new() -> TextBoxes {
|
2022-02-07 02:52:19 +00:00
|
|
|
TextBoxes {
|
|
|
|
slide_in: 7,
|
|
|
|
anim_counter: 0,
|
|
|
|
animated_face: AnimatedFace { face_id: 0, anim_id: 0, anim_frames: vec![(0, 0)] },
|
|
|
|
}
|
2022-01-05 05:08:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GameEntity<()> for TextBoxes {
|
2022-02-06 17:22:26 +00:00
|
|
|
fn tick(&mut self, state: &mut SharedGameState, _custom: ()) -> GameResult {
|
|
|
|
if state.textscript_vm.face != 0 {
|
2022-02-07 02:52:19 +00:00
|
|
|
self.slide_in = self.slide_in.saturating_sub(1);
|
2022-02-06 17:22:26 +00:00
|
|
|
self.anim_counter = self.anim_counter.wrapping_add(1);
|
2022-02-07 02:52:19 +00:00
|
|
|
|
|
|
|
let face_num = state.textscript_vm.face % 100;
|
|
|
|
let animation = state.textscript_vm.face % 1000 / 100;
|
|
|
|
|
|
|
|
if state.constants.textscript.animated_face_pics
|
2022-07-18 02:37:35 +00:00
|
|
|
&& !state.settings.original_textures
|
2022-02-07 02:52:19 +00:00
|
|
|
&& (self.animated_face.anim_id != animation || self.animated_face.face_id != face_num)
|
|
|
|
{
|
|
|
|
self.animated_face = state
|
|
|
|
.constants
|
|
|
|
.animated_face_table
|
|
|
|
.clone()
|
|
|
|
.into_iter()
|
|
|
|
.find(|face| face.face_id == face_num && face.anim_id == animation)
|
|
|
|
.unwrap_or_else(|| AnimatedFace { face_id: face_num, anim_id: 0, anim_frames: vec![(0, 0)] });
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.anim_counter > self.animated_face.anim_frames.first().unwrap().1 as usize {
|
|
|
|
self.animated_face.anim_frames.rotate_left(1);
|
|
|
|
self.anim_counter = 0;
|
|
|
|
}
|
2022-02-06 17:22:26 +00:00
|
|
|
}
|
2022-01-05 05:08:36 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, _frame: &Frame) -> GameResult {
|
|
|
|
if !state.textscript_vm.flags.render() {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
let (off_left, off_top, off_right, off_bottom) =
|
|
|
|
crate::framework::graphics::screen_insets_scaled(ctx, state.scale);
|
|
|
|
|
|
|
|
let center = ((state.canvas_size.0 - off_left - off_right) / 2.0).floor();
|
|
|
|
let top_pos = if state.textscript_vm.flags.position_top() {
|
|
|
|
32.0 + off_top
|
|
|
|
} else {
|
|
|
|
state.canvas_size.1 as f32 - off_bottom - 66.0
|
|
|
|
};
|
|
|
|
let left_pos = off_left + center - 122.0;
|
|
|
|
|
|
|
|
{
|
|
|
|
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;
|
|
|
|
if state.textscript_vm.flags.background_visible() {
|
|
|
|
batch.add_rect(left_pos, top_pos, &state.constants.textscript.textbox_rect_top);
|
|
|
|
for i in 1..7 {
|
|
|
|
batch.add_rect(left_pos, top_pos + i as f32 * 8.0, &state.constants.textscript.textbox_rect_middle);
|
|
|
|
}
|
|
|
|
batch.add_rect(left_pos, top_pos + 56.0, &state.constants.textscript.textbox_rect_bottom);
|
|
|
|
}
|
|
|
|
|
|
|
|
if state.textscript_vm.item != 0 {
|
|
|
|
batch.add_rect(
|
|
|
|
center - 40.0,
|
|
|
|
state.canvas_size.1 - off_bottom - 112.0,
|
|
|
|
&state.constants.textscript.get_item_top_left,
|
|
|
|
);
|
|
|
|
batch.add_rect(
|
|
|
|
center - 40.0,
|
|
|
|
state.canvas_size.1 - off_bottom - 96.0,
|
|
|
|
&state.constants.textscript.get_item_bottom_left,
|
|
|
|
);
|
|
|
|
batch.add_rect(
|
|
|
|
center + 32.0,
|
|
|
|
state.canvas_size.1 - off_bottom - 112.0,
|
|
|
|
&state.constants.textscript.get_item_top_right,
|
|
|
|
);
|
|
|
|
batch.add_rect(
|
|
|
|
center + 32.0,
|
|
|
|
state.canvas_size.1 - off_bottom - 104.0,
|
|
|
|
&state.constants.textscript.get_item_right,
|
|
|
|
);
|
|
|
|
batch.add_rect(
|
|
|
|
center + 32.0,
|
|
|
|
state.canvas_size.1 - off_bottom - 96.0,
|
|
|
|
&state.constants.textscript.get_item_right,
|
|
|
|
);
|
|
|
|
batch.add_rect(
|
|
|
|
center + 32.0,
|
|
|
|
state.canvas_size.1 - off_bottom - 88.0,
|
|
|
|
&state.constants.textscript.get_item_bottom_right,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let TextScriptExecutionState::WaitConfirmation(_, _, _, wait, selection) = state.textscript_vm.state {
|
|
|
|
let pos_y = if wait > 14 {
|
|
|
|
state.canvas_size.1 - off_bottom - 96.0 + 4.0 * (17 - wait) as f32
|
|
|
|
} else {
|
|
|
|
state.canvas_size.1 - off_bottom - 96.0
|
|
|
|
};
|
|
|
|
|
|
|
|
batch.add_rect(center + 56.0, 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(
|
|
|
|
center + 51.0 + pos_x,
|
|
|
|
pos_y + 10.0,
|
|
|
|
&state.constants.textscript.textbox_rect_cursor,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
batch.draw(ctx)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
if state.textscript_vm.face != 0 {
|
2022-02-06 17:22:26 +00:00
|
|
|
let clip_rect = Rect::new_size(
|
|
|
|
((left_pos + 14.0) * state.scale) as isize,
|
|
|
|
((top_pos + 8.0) * state.scale) as isize,
|
|
|
|
(48.0 * state.scale) as isize,
|
|
|
|
(48.0 * state.scale) as isize,
|
|
|
|
);
|
|
|
|
|
|
|
|
graphics::set_clip_rect(ctx, Some(clip_rect))?;
|
|
|
|
|
2022-01-05 05:08:36 +00:00
|
|
|
// switch version uses 1xxx flag to show a flipped version of face
|
|
|
|
let flip = state.textscript_vm.face > 1000;
|
|
|
|
let face_num = state.textscript_vm.face % 100;
|
2022-02-07 02:52:19 +00:00
|
|
|
let animation_frame = self.animated_face.anim_frames.first().unwrap().0 as usize;
|
|
|
|
|
2022-07-18 02:37:35 +00:00
|
|
|
let tex_name = if state.constants.textscript.animated_face_pics && !state.settings.original_textures {
|
|
|
|
SWITCH_FACE_TEX[animation_frame]
|
|
|
|
} else {
|
|
|
|
FACE_TEX
|
|
|
|
};
|
2022-02-07 02:52:19 +00:00
|
|
|
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, tex_name)?;
|
2022-01-05 05:08:36 +00:00
|
|
|
|
2022-02-07 02:52:19 +00:00
|
|
|
let face_x = (4.0 + (6 - self.slide_in) as f32 * 8.0) - 52.0;
|
2022-02-06 17:22:26 +00:00
|
|
|
|
2022-08-16 11:47:11 +00:00
|
|
|
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);
|
|
|
|
}
|
2022-01-05 05:08:36 +00:00
|
|
|
|
|
|
|
batch.draw(ctx)?;
|
2022-02-06 17:22:26 +00:00
|
|
|
graphics::set_clip_rect(ctx, None)?;
|
2022-01-05 05:08:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if state.textscript_vm.item != 0 {
|
|
|
|
let mut rect = Rect::new(0, 0, 0, 0);
|
|
|
|
|
|
|
|
if state.textscript_vm.item < 1000 {
|
|
|
|
let item_id = state.textscript_vm.item as u16;
|
|
|
|
|
|
|
|
rect.left = (item_id % 16) * 16;
|
|
|
|
rect.right = rect.left + 16;
|
|
|
|
rect.top = (item_id / 16) * 16;
|
|
|
|
rect.bottom = rect.top + 16;
|
|
|
|
|
|
|
|
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "ArmsImage")?;
|
|
|
|
batch.add_rect((center - 12.0).floor(), state.canvas_size.1 - off_bottom - 104.0, &rect);
|
|
|
|
batch.draw(ctx)?;
|
|
|
|
} else {
|
|
|
|
let item_id = state.textscript_vm.item as u16 - 1000;
|
|
|
|
|
|
|
|
rect.left = (item_id % 8) * 32;
|
|
|
|
rect.right = rect.left + 32;
|
|
|
|
rect.top = (item_id / 8) * 16;
|
|
|
|
rect.bottom = rect.top + 16;
|
|
|
|
|
|
|
|
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "ItemImage")?;
|
|
|
|
batch.add_rect((center - 20.0).floor(), state.canvas_size.1 - off_bottom - 104.0, &rect);
|
|
|
|
batch.draw(ctx)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let text_offset = if state.textscript_vm.face == 0 { 0.0 } else { 56.0 };
|
|
|
|
|
2022-01-16 01:35:36 +00:00
|
|
|
let y_offset = if let TextScriptExecutionState::MsgNewLine(_, _, _, _, counter) = state.textscript_vm.state {
|
|
|
|
16.0 - counter as f32 * 4.0
|
|
|
|
} else {
|
|
|
|
0.0
|
|
|
|
};
|
|
|
|
|
2022-01-05 05:08:36 +00:00
|
|
|
let lines = [&state.textscript_vm.line_1, &state.textscript_vm.line_2, &state.textscript_vm.line_3];
|
|
|
|
|
2022-01-16 01:35:36 +00:00
|
|
|
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))?;
|
2022-01-05 05:08:36 +00:00
|
|
|
for (idx, line) in lines.iter().enumerate() {
|
|
|
|
if !line.is_empty() {
|
2022-11-20 19:38:36 +00:00
|
|
|
let symbols = Symbols { symbols: &state.textscript_vm.substitution_rect_map, texture: "TextBox" };
|
|
|
|
|
|
|
|
state
|
|
|
|
.font
|
|
|
|
.builder()
|
|
|
|
.position(left_pos + text_offset + 14.0, top_pos + 10.0 + idx as f32 * 16.0 - y_offset)
|
|
|
|
.shadow(state.constants.textscript.text_shadow)
|
|
|
|
.with_symbols(Some(symbols))
|
|
|
|
.draw_iter(line.iter().copied(), ctx, &state.constants, &mut state.texture_set)?;
|
2022-01-05 05:08:36 +00:00
|
|
|
}
|
|
|
|
}
|
2022-01-16 01:35:36 +00:00
|
|
|
graphics::set_clip_rect(ctx, None)?;
|
2022-01-05 05:08:36 +00:00
|
|
|
|
|
|
|
if let TextScriptExecutionState::WaitInput(_, _, tick) = state.textscript_vm.state {
|
|
|
|
if tick > 10 {
|
2022-11-20 19:38:36 +00:00
|
|
|
let builder = state
|
|
|
|
.font
|
|
|
|
.builder()
|
|
|
|
.with_symbols(Some(Symbols { symbols: &state.textscript_vm.substitution_rect_map, texture: "" }));
|
|
|
|
|
2022-01-05 05:08:36 +00:00
|
|
|
let (mut x, y) = match state.textscript_vm.current_line {
|
2022-11-20 19:38:36 +00:00
|
|
|
TextScriptLine::Line1 => {
|
|
|
|
(builder.compute_width_iter(state.textscript_vm.line_1.iter().copied()), top_pos + 10.0)
|
|
|
|
}
|
|
|
|
TextScriptLine::Line2 => {
|
|
|
|
(builder.compute_width_iter(state.textscript_vm.line_2.iter().copied()), top_pos + 10.0 + 16.0)
|
|
|
|
}
|
|
|
|
TextScriptLine::Line3 => {
|
|
|
|
(builder.compute_width_iter(state.textscript_vm.line_3.iter().copied()), top_pos + 10.0 + 32.0)
|
|
|
|
}
|
2022-01-05 05:08:36 +00:00
|
|
|
};
|
|
|
|
x += left_pos + text_offset + 14.0;
|
|
|
|
|
2022-11-19 17:20:03 +00:00
|
|
|
graphics::draw_rect(
|
2022-01-05 05:08:36 +00:00
|
|
|
ctx,
|
|
|
|
Rect::new_size(
|
|
|
|
(x * state.scale) as isize,
|
|
|
|
(y * state.scale) as isize,
|
|
|
|
(5.0 * state.scale) as isize,
|
2022-11-20 19:38:36 +00:00
|
|
|
(state.font.line_height() * state.scale) as isize,
|
2022-01-05 05:08:36 +00:00
|
|
|
),
|
|
|
|
Color::from_rgb(255, 255, 255),
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|