rework cutscene skipping

This commit is contained in:
Alula 2021-05-02 06:06:51 +02:00
parent ad6a330ae0
commit ecabff27a8
No known key found for this signature in database
GPG Key ID: 3E00485503A1D8BA
3 changed files with 165 additions and 76 deletions

View File

@ -77,6 +77,19 @@ impl BMFontRenderer {
self.draw_colored_text(iter, x, y, (255, 255, 255, 255), constants, texture_set, ctx)
}
pub fn draw_text_with_shadow<I: Iterator<Item = char> + Clone>(
&self,
iter: I,
x: f32,
y: f32,
constants: &EngineConstants,
texture_set: &mut TextureSet,
ctx: &mut Context,
) -> GameResult {
self.draw_colored_text(iter.clone(), x + 1.0, y + 1.0, (0, 0, 0, 150), constants, texture_set, ctx)?;
self.draw_colored_text(iter, x, y, (255, 255, 255, 255), constants, texture_set, ctx)
}
pub fn draw_colored_text_scaled<I: Iterator<Item = char>>(
&self,
iter: I,

View File

@ -20,9 +20,9 @@ use crate::framework::graphics::{BlendMode, draw_rect, FilterMode};
use crate::framework::ui::Components;
use crate::input::touch_controls::TouchControlType;
use crate::inventory::{Inventory, TakeExperienceResult};
use crate::npc::{NPC, NPCLayer};
use crate::npc::boss::BossNPC;
use crate::npc::list::NPCList;
use crate::npc::{NPC, NPCLayer};
use crate::physics::PhysicalEntity;
use crate::player::{Player, TargetPlayer};
use crate::rng::XorShift;
@ -57,6 +57,7 @@ pub struct GameScene {
tex_background_name: String,
tex_tileset_name: String,
map_name_counter: u16,
skip_counter: u16,
}
#[derive(Debug, EnumIter, PartialEq, Eq, Hash, Copy, Clone)]
@ -71,6 +72,7 @@ const FACE_TEX: &str = "Face";
const SWITCH_FACE_TEX: [&str; 4] = ["Face1", "Face2", "Face3", "Face4"];
const P2_LEFT_TEXT: &str = "< P2";
const P2_RIGHT_TEXT: &str = "P2 >";
const CUTSCENE_SKIP_WAIT: u16 = 50;
impl GameScene {
pub fn new(state: &mut SharedGameState, ctx: &mut Context, id: usize) -> GameResult<Self> {
@ -112,6 +114,7 @@ impl GameScene {
tex_background_name,
tex_tileset_name,
map_name_counter: 0,
skip_counter: 0,
})
}
@ -212,18 +215,19 @@ impl GameScene {
Ok(())
}
fn draw_npc_layer(&self, state: &mut SharedGameState, ctx: &mut Context, layer: NPCLayer) -> GameResult {
fn draw_npc_layer(&self, state: &mut SharedGameState, ctx: &mut Context, layer: NPCLayer) -> GameResult {
for npc in self.npc_list.iter_alive() {
if npc.layer != layer || npc.x < (self.frame.x - 128 * 0x200 - npc.display_bounds.width() as i32 * 0x200)
if npc.layer != layer
|| 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)
> (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)
> (self.frame.y
+ 128 * 0x200
+ (state.canvas_size.1 as i32 + npc.display_bounds.height() as i32) * 0x200)
{
continue;
}
@ -412,10 +416,15 @@ impl GameScene {
return Ok(());
}
let (off_left, off_top, off_right, off_bottom) = crate::framework::graphics::screen_insets_scaled(ctx, state.scale);
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 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;
{
@ -446,7 +455,7 @@ impl GameScene {
);
batch.add_rect(
center + 32.0,
state.canvas_size.1 - off_bottom- 104.0,
state.canvas_size.1 - off_bottom - 104.0,
&state.constants.textscript.get_item_right,
);
batch.add_rect(
@ -468,11 +477,7 @@ impl GameScene {
state.canvas_size.1 - off_bottom - 96.0
};
batch.add_rect(
center + 56.0,
pos_y,
&state.constants.textscript.textbox_rect_yes_no,
);
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 };
@ -617,7 +622,15 @@ impl GameScene {
)
}
fn draw_light_raycast(&self, world_point_x: i32, world_point_y: i32, (br, bg, bb): (u8, u8, u8), att: u8, angle: Range<i32>, batch: &mut SizedBatch) {
fn draw_light_raycast(
&self,
world_point_x: i32,
world_point_y: i32,
(br, bg, bb): (u8, u8, u8),
att: u8,
angle: Range<i32>,
batch: &mut SizedBatch,
) {
let px = world_point_x as f32 / 512.0;
let py = world_point_y as f32 / 512.0;
@ -649,43 +662,43 @@ impl GameScene {
&& x >= (bx * 16 - 8) as f32
&& x <= (bx * 16 + 8) as f32
&& y >= (by * 16 - 8) as f32
&& y <= (by * 16 + 8) as f32) ||
((tile == 0x50 || tile == 0x70)
&& y <= (by * 16 + 8) as f32)
|| ((tile == 0x50 || tile == 0x70)
&& x >= (bx * 16 - 8) as f32
&& x <= (bx * 16 + 8) as f32
&& y <= ((by as f32 * 16.0) - (x - bx as f32 * 16.0) / 2.0 + 4.0)
&& y >= (by * 16 - 8) as f32) ||
((tile == 0x51 || tile == 0x71)
&& y >= (by * 16 - 8) as f32)
|| ((tile == 0x51 || tile == 0x71)
&& x >= (bx * 16 - 8) as f32
&& x <= (bx * 16 + 8) as f32
&& y <= ((by as f32 * 16.0) - (x - bx as f32 * 16.0) / 2.0 - 4.0)
&& y >= (by * 16 - 8) as f32) ||
((tile == 0x52 || tile == 0x72)
&& y >= (by * 16 - 8) as f32)
|| ((tile == 0x52 || tile == 0x72)
&& x >= (bx * 16 - 8) as f32
&& x <= (bx * 16 + 8) as f32
&& y <= ((by as f32 * 16.0) + (x - bx as f32 * 16.0) / 2.0 - 4.0)
&& y >= (by * 16 - 8) as f32) ||
((tile == 0x53 || tile == 0x73)
&& y >= (by * 16 - 8) as f32)
|| ((tile == 0x53 || tile == 0x73)
&& x >= (bx * 16 - 8) as f32
&& x <= (bx * 16 + 8) as f32
&& y <= ((by as f32 * 16.0) + (x - bx as f32 * 16.0) / 2.0 + 4.0)
&& y >= (by * 16 - 8) as f32) ||
((tile == 0x54 || tile == 0x74)
&& y >= (by * 16 - 8) as f32)
|| ((tile == 0x54 || tile == 0x74)
&& x >= (bx * 16 - 8) as f32
&& x <= (bx * 16 + 8) as f32
&& y >= ((by as f32 * 16.0) + (x - bx as f32 * 16.0) / 2.0 - 4.0)
&& y <= (by * 16 + 8) as f32) ||
((tile == 0x55 || tile == 0x75)
&& y <= (by * 16 + 8) as f32)
|| ((tile == 0x55 || tile == 0x75)
&& x >= (bx * 16 - 8) as f32
&& x <= (bx * 16 + 8) as f32
&& y >= ((by as f32 * 16.0) + (x - bx as f32 * 16.0) / 2.0 + 4.0)
&& y <= (by * 16 + 8) as f32) ||
((tile == 0x56 || tile == 0x76)
&& y <= (by * 16 + 8) as f32)
|| ((tile == 0x56 || tile == 0x76)
&& x >= (bx * 16 - 8) as f32
&& x <= (bx * 16 + 8) as f32
&& y >= ((by as f32 * 16.0) - (x - bx as f32 * 16.0) / 2.0 + 4.0)
&& y <= (by * 16 + 8) as f32) ||
((tile == 0x57 || tile == 0x77)
&& y <= (by * 16 + 8) as f32)
|| ((tile == 0x57 || tile == 0x77)
&& x >= (bx * 16 - 8) as f32
&& x <= (bx * 16 + 8) as f32
&& y >= ((by as f32 * 16.0) - (x - bx as f32 * 16.0) / 2.0 - 4.0)
@ -724,7 +737,9 @@ impl GameScene {
let scale = state.scale;
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "builtin/lightmap/spot")?;
for (player, inv) in [(&self.player1, &self.inventory_player1), (&self.player2, &self.inventory_player2)].iter() {
for (player, inv) in
[(&self.player1, &self.inventory_player1), (&self.player2, &self.inventory_player2)].iter()
{
if player.cond.alive() && !player.cond.hidden() && inv.get_current_weapon().is_some() {
let range = match () {
_ if player.up => 60..120,
@ -1496,42 +1511,73 @@ impl Scene for GameScene {
}
}
match state.textscript_vm.mode {
ScriptMode::Map if state.control_flags.tick_world() => self.tick_world(state)?,
ScriptMode::StageSelect => self.stage_select.tick(state, (ctx, &self.player1, &self.player2))?,
ScriptMode::Inventory => self.inventory_ui.tick(state, (ctx, &mut self.player1, &mut self.inventory_player1))?,
_ => {}
match state.textscript_vm.state {
TextScriptExecutionState::Running(_, _)
| TextScriptExecutionState::WaitTicks(_, _, _)
| TextScriptExecutionState::WaitInput(_, _, _)
| TextScriptExecutionState::Msg(_, _, _, _)
if !state.control_flags.control_enabled() && !state.textscript_vm.flags.cutscene_skip() =>
{
if self.player1.controller.inventory() {
self.skip_counter += 1;
if self.skip_counter >= CUTSCENE_SKIP_WAIT {
state.textscript_vm.flags.set_cutscene_skip(true);
}
} else if self.skip_counter > 0 {
self.skip_counter -= 1;
}
}
_ => {
self.skip_counter = 0;
}
}
if self.map_name_counter > 0 {
self.map_name_counter -= 1;
let mut ticks = 1;
if state.textscript_vm.mode == ScriptMode::Map && state.textscript_vm.flags.cutscene_skip() {
ticks = 4;
}
match state.fade_state {
FadeState::FadeOut(tick, direction) if tick < 15 => {
state.fade_state = FadeState::FadeOut(tick + 1, direction);
for _ in 0..ticks {
match state.textscript_vm.mode {
ScriptMode::Map if state.control_flags.tick_world() => self.tick_world(state)?,
ScriptMode::StageSelect => self.stage_select.tick(state, (ctx, &self.player1, &self.player2))?,
ScriptMode::Inventory => {
self.inventory_ui.tick(state, (ctx, &mut self.player1, &mut self.inventory_player1))?
}
_ => {}
}
FadeState::FadeOut(tick, _) if tick == 15 => {
state.fade_state = FadeState::Hidden;
if self.map_name_counter > 0 {
self.map_name_counter -= 1;
}
FadeState::FadeIn(tick, direction) if tick > -15 => {
state.fade_state = FadeState::FadeIn(tick - 1, direction);
match state.fade_state {
FadeState::FadeOut(tick, direction) if tick < 15 => {
state.fade_state = FadeState::FadeOut(tick + 1, direction);
}
FadeState::FadeOut(tick, _) if tick == 15 => {
state.fade_state = FadeState::Hidden;
}
FadeState::FadeIn(tick, direction) if tick > -15 => {
state.fade_state = FadeState::FadeIn(tick - 1, direction);
}
FadeState::FadeIn(tick, _) if tick == -15 => {
state.fade_state = FadeState::Visible;
}
_ => {}
}
FadeState::FadeIn(tick, _) if tick == -15 => {
state.fade_state = FadeState::Visible;
self.flash.tick(state, ())?;
TextScriptVM::run(state, self, ctx)?;
#[cfg(feature = "scripting")]
state.lua.scene_tick(self);
if state.control_flags.control_enabled() {
self.tick = self.tick.wrapping_add(1);
}
_ => {}
}
self.flash.tick(state, ())?;
TextScriptVM::run(state, self, ctx)?;
#[cfg(feature = "scripting")]
state.lua.scene_tick(self);
if state.control_flags.control_enabled() {
self.tick = self.tick.wrapping_add(1);
}
Ok(())
}
@ -1692,6 +1738,36 @@ impl Scene for GameScene {
}
self.draw_text_boxes(state, ctx)?;
if self.skip_counter > 0 {
let text = format!("Hold {:?} to skip the cutscene", state.settings.player1_key_map.inventory);
let width = state.font.text_width(text.chars(), &state.constants);
let pos_x = state.canvas_size.0 - width - 20.0;
let pos_y = 0.0;
let line_height = state.font.line_height(&state.constants);
let w = (self.skip_counter as f32 / CUTSCENE_SKIP_WAIT as f32) * (width + 20.0) / 2.0;
let mut rect = Rect::new_size((pos_x * state.scale) as isize,
(pos_y * state.scale) as isize,
((20.0 + width) * state.scale) as isize,
((20.0 + line_height) * state.scale) as isize);
draw_rect(ctx, rect, Color::from_rgb(0, 0, 32))?;
rect.right = rect.left + (w * state.scale) as isize;
draw_rect(ctx, rect, Color::from_rgb(160, 181, 222))?;
rect.left = ((state.canvas_size.0 - w) * state.scale) as isize;
rect.right = rect.left + (w * state.scale) as isize;
draw_rect(ctx, rect, Color::from_rgb(160, 181, 222))?;
state.font.draw_text_with_shadow(
text.chars(),
pos_x + 10.0,
pos_y + 10.0,
&state.constants,
&mut state.texture_set,
ctx,
)?;
}
if state.settings.debug_outlines {
self.draw_debug_outlines(state, ctx)?;

View File

@ -303,6 +303,7 @@ bitfield! {
pub fast, set_fast: 4;
pub position_top, set_position_top: 5;
pub perma_fast, set_perma_fast: 6;
pub cutscene_skip, set_cutscene_skip: 7;
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
@ -604,10 +605,7 @@ impl TextScriptVM {
}
if remaining > 1 {
let ticks = if state.textscript_vm.flags.fast()
|| game_scene.player1.controller.skip()
|| game_scene.player2.controller.skip()
{
let ticks = if state.textscript_vm.flags.fast() || state.textscript_vm.flags.cutscene_skip() {
0
} else if game_scene.player1.controller.jump()
|| game_scene.player1.controller.shoot()
@ -644,6 +642,8 @@ impl TextScriptVM {
}
}
TextScriptExecutionState::WaitConfirmation(event, ip, no_event, wait, selection) => {
state.textscript_vm.flags.set_cutscene_skip(false);
if wait > 0 {
state.textscript_vm.state =
TextScriptExecutionState::WaitConfirmation(event, ip, no_event, wait - 1, selection);
@ -731,12 +731,11 @@ impl TextScriptVM {
state.touch_controls.control_type = TouchControlType::Dialog;
}
if game_scene.player1.controller.trigger_jump()
if state.textscript_vm.flags.cutscene_skip()
|| game_scene.player1.controller.trigger_jump()
|| game_scene.player1.controller.trigger_shoot()
|| game_scene.player1.controller.skip()
|| game_scene.player2.controller.trigger_jump()
|| game_scene.player2.controller.trigger_shoot()
|| game_scene.player2.controller.skip()
{
state.textscript_vm.state = TextScriptExecutionState::Running(event, ip);
}
@ -809,9 +808,11 @@ impl TextScriptVM {
}
}
OpCode::_END => {
state.textscript_vm.flags.set_cutscene_skip(false);
exec_state = TextScriptExecutionState::Ended;
}
OpCode::END => {
state.textscript_vm.flags.set_cutscene_skip(false);
state.control_flags.set_tick_world(true);
state.control_flags.set_control_enabled(true);
@ -1218,8 +1219,10 @@ impl TextScriptVM {
new_scene.player2.x = pos_x;
new_scene.player2.y = pos_y;
let skip = state.textscript_vm.flags.cutscene_skip();
state.control_flags.set_tick_world(true);
state.textscript_vm.flags.0 = 0;
state.textscript_vm.flags.set_cutscene_skip(skip);
state.textscript_vm.face = 0;
state.textscript_vm.item = 0;
state.textscript_vm.current_line = TextScriptLine::Line1;
@ -1269,7 +1272,8 @@ impl TextScriptVM {
partner.vel_x = 0;
partner.vel_y = 0;
partner.x = executor.x + if (param % 10) == 1 { distance * 0x200 } else { -distance * 0x200 };
partner.x =
executor.x + if (param % 10) == 1 { distance * 0x200 } else { -distance * 0x200 };
partner.y = executor.y;
}
}
@ -1711,11 +1715,7 @@ impl TextScriptVM {
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
// One operand codes
OpCode::UNJ
| OpCode::XX1
| OpCode::SIL
| OpCode::SSS
| OpCode::ACH => {
OpCode::UNJ | OpCode::XX1 | OpCode::SIL | OpCode::SSS | OpCode::ACH => {
let par_a = read_cur_varint(&mut cursor)?;
log::warn!("unimplemented opcode: {:?} {}", op, par_a);
@ -1844,7 +1844,7 @@ impl TextScript {
) -> GameResult<Vec<u8>> {
let mut bytecode = Vec::new();
let mut char_buf = Vec::with_capacity(16);
let mut allow_next_event = false;
let mut allow_next_event = true;
while let Some(&chr) = iter.peek() {
match chr {