mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-12-03 10:28:23 +00:00
Improved camera
This commit is contained in:
parent
8050beed56
commit
69e2b00cdc
23
src/frame.rs
23
src/frame.rs
|
|
@ -3,11 +3,22 @@ use crate::shared_game_state::SharedGameState;
|
||||||
use crate::stage::Stage;
|
use crate::stage::Stage;
|
||||||
use crate::common::interpolate_fix9_scale;
|
use crate::common::interpolate_fix9_scale;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum UpdateTarget {
|
||||||
|
Player,
|
||||||
|
NPC(u16),
|
||||||
|
Boss(u16),
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Frame {
|
pub struct Frame {
|
||||||
pub x: isize,
|
pub x: isize,
|
||||||
pub y: isize,
|
pub y: isize,
|
||||||
pub prev_x: isize,
|
pub prev_x: isize,
|
||||||
pub prev_y: isize,
|
pub prev_y: isize,
|
||||||
|
pub update_target: UpdateTarget,
|
||||||
|
pub target_x: isize,
|
||||||
|
pub target_y: isize,
|
||||||
pub wait: isize,
|
pub wait: isize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -19,11 +30,11 @@ impl Frame {
|
||||||
(x, y)
|
(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn immediate_update(&mut self, state: &mut SharedGameState, player: &Player, stage: &Stage) {
|
pub fn immediate_update(&mut self, state: &mut SharedGameState, stage: &Stage) {
|
||||||
if (stage.map.width - 1) * 16 < state.canvas_size.0 as usize {
|
if (stage.map.width - 1) * 16 < state.canvas_size.0 as usize {
|
||||||
self.x = -(((state.canvas_size.0 as isize - ((stage.map.width - 1) * 16) as isize) * 0x200) / 2);
|
self.x = -(((state.canvas_size.0 as isize - ((stage.map.width - 1) * 16) as isize) * 0x200) / 2);
|
||||||
} else {
|
} else {
|
||||||
self.x = player.target_x - (state.canvas_size.0 as isize * 0x200 / 2);
|
self.x = self.target_x - (state.canvas_size.0 as isize * 0x200 / 2);
|
||||||
|
|
||||||
if self.x < 0 {
|
if self.x < 0 {
|
||||||
self.x = 0;
|
self.x = 0;
|
||||||
|
|
@ -38,7 +49,7 @@ impl Frame {
|
||||||
if (stage.map.height - 1) * 16 < state.canvas_size.1 as usize {
|
if (stage.map.height - 1) * 16 < state.canvas_size.1 as usize {
|
||||||
self.y = -(((state.canvas_size.1 as isize - ((stage.map.height - 1) * 16) as isize) * 0x200) / 2);
|
self.y = -(((state.canvas_size.1 as isize - ((stage.map.height - 1) * 16) as isize) * 0x200) / 2);
|
||||||
} else {
|
} else {
|
||||||
self.y = player.target_y - (state.canvas_size.1 as isize * 0x200 / 2);
|
self.y = self.target_y - (state.canvas_size.1 as isize * 0x200 / 2);
|
||||||
|
|
||||||
if self.y < 0 {
|
if self.y < 0 {
|
||||||
self.y = 0;
|
self.y = 0;
|
||||||
|
|
@ -54,11 +65,11 @@ impl Frame {
|
||||||
self.prev_y = self.y;
|
self.prev_y = self.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, state: &mut SharedGameState, player: &Player, stage: &Stage) {
|
pub fn update(&mut self, state: &mut SharedGameState, stage: &Stage) {
|
||||||
if (stage.map.width - 1) * 16 < state.canvas_size.0 as usize {
|
if (stage.map.width - 1) * 16 < state.canvas_size.0 as usize {
|
||||||
self.x = -(((state.canvas_size.0 as isize - ((stage.map.width - 1) * 16) as isize) * 0x200) / 2);
|
self.x = -(((state.canvas_size.0 as isize - ((stage.map.width - 1) * 16) as isize) * 0x200) / 2);
|
||||||
} else {
|
} else {
|
||||||
self.x += (player.target_x - (state.canvas_size.0 as isize * 0x200 / 2) - self.x) / self.wait;
|
self.x += (self.target_x - (state.canvas_size.0 as isize * 0x200 / 2) - self.x) / self.wait;
|
||||||
|
|
||||||
if self.x < 0 {
|
if self.x < 0 {
|
||||||
self.x = 0;
|
self.x = 0;
|
||||||
|
|
@ -73,7 +84,7 @@ impl Frame {
|
||||||
if (stage.map.height - 1) * 16 < state.canvas_size.1 as usize {
|
if (stage.map.height - 1) * 16 < state.canvas_size.1 as usize {
|
||||||
self.y = -(((state.canvas_size.1 as isize - ((stage.map.height - 1) * 16) as isize) * 0x200) / 2);
|
self.y = -(((state.canvas_size.1 as isize - ((stage.map.height - 1) * 16) as isize) * 0x200) / 2);
|
||||||
} else {
|
} else {
|
||||||
self.y += (player.target_y - (state.canvas_size.1 as isize * 0x200 / 2) - self.y) / self.wait;
|
self.y += (self.target_y - (state.canvas_size.1 as isize * 0x200 / 2) - self.y) / self.wait;
|
||||||
|
|
||||||
if self.y < 0 {
|
if self.y < 0 {
|
||||||
self.y = 0;
|
self.y = 0;
|
||||||
|
|
|
||||||
|
|
@ -608,7 +608,7 @@ impl NPC {
|
||||||
} else {
|
} else {
|
||||||
self.action_counter2 += 1;
|
self.action_counter2 += 1;
|
||||||
if (self.action_counter2 % 8) == 0 && abs(self.x - player.x) < 160 * 0x200 {
|
if (self.action_counter2 % 8) == 0 && abs(self.x - player.x) < 160 * 0x200 {
|
||||||
let angle = ((player.y - self.y) as f64 / (player.x - self.x) as f64).atan();
|
let angle = ((player.y - self.y) as f64 / (player.x - self.x) as f64).atan()
|
||||||
+ (state.game_rng.range(-6..6) as u8) as f64 * CDEG_RAD;
|
+ (state.game_rng.range(-6..6) as u8) as f64 * CDEG_RAD;
|
||||||
|
|
||||||
let mut npc = NPCMap::create_npc(84, &state.npc_table);
|
let mut npc = NPCMap::create_npc(84, &state.npc_table);
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,6 @@ pub struct Player {
|
||||||
pub down: bool,
|
pub down: bool,
|
||||||
pub shock_counter: u8,
|
pub shock_counter: u8,
|
||||||
pub current_weapon: u8,
|
pub current_weapon: u8,
|
||||||
pub update_target: bool,
|
|
||||||
pub stars: u8,
|
pub stars: u8,
|
||||||
pub damage: u16,
|
pub damage: u16,
|
||||||
pub air_counter: u16,
|
pub air_counter: u16,
|
||||||
|
|
@ -90,7 +89,6 @@ impl Player {
|
||||||
index_x: 0,
|
index_x: 0,
|
||||||
index_y: 0,
|
index_y: 0,
|
||||||
splash: false,
|
splash: false,
|
||||||
update_target: true,
|
|
||||||
up: false,
|
up: false,
|
||||||
down: false,
|
down: false,
|
||||||
current_weapon: 0,
|
current_weapon: 0,
|
||||||
|
|
@ -448,10 +446,8 @@ impl Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.update_target {
|
self.target_x = self.x + self.index_x;
|
||||||
self.target_x = self.x + self.index_x;
|
self.target_y = self.y + self.index_y;
|
||||||
self.target_y = self.y + self.index_y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.vel_x > physics.resist || self.vel_x < -physics.resist {
|
if self.vel_x > physics.resist || self.vel_x < -physics.resist {
|
||||||
self.x += self.vel_x;
|
self.x += self.vel_x;
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use crate::bullet::BulletManager;
|
||||||
use crate::caret::CaretType;
|
use crate::caret::CaretType;
|
||||||
use crate::common::{Direction, FadeDirection, FadeState, fix9_scale, Rect};
|
use crate::common::{Direction, FadeDirection, FadeState, fix9_scale, Rect};
|
||||||
use crate::entity::GameEntity;
|
use crate::entity::GameEntity;
|
||||||
use crate::frame::Frame;
|
use crate::frame::{Frame, UpdateTarget};
|
||||||
use crate::ggez::{Context, GameResult, graphics, timer};
|
use crate::ggez::{Context, GameResult, graphics, timer};
|
||||||
use crate::ggez::graphics::{BlendMode, Color, Drawable, DrawParam, FilterMode, Vector2};
|
use crate::ggez::graphics::{BlendMode, Color, Drawable, DrawParam, FilterMode, Vector2};
|
||||||
use crate::ggez::nalgebra::clamp;
|
use crate::ggez::nalgebra::clamp;
|
||||||
|
|
@ -79,6 +79,9 @@ impl GameScene {
|
||||||
y: 0,
|
y: 0,
|
||||||
prev_x: 0,
|
prev_x: 0,
|
||||||
prev_y: 0,
|
prev_y: 0,
|
||||||
|
update_target: UpdateTarget::Player,
|
||||||
|
target_x: 0,
|
||||||
|
target_y: 0,
|
||||||
wait: 16,
|
wait: 16,
|
||||||
},
|
},
|
||||||
stage_id: id,
|
stage_id: id,
|
||||||
|
|
@ -918,7 +921,7 @@ impl GameScene {
|
||||||
self.player.tick_npc_collisions(state, &mut self.npc_map, &mut self.inventory);
|
self.player.tick_npc_collisions(state, &mut self.npc_map, &mut self.inventory);
|
||||||
|
|
||||||
for npc_id in self.npc_map.npc_ids.iter() {
|
for npc_id in self.npc_map.npc_ids.iter() {
|
||||||
if let Some(npc_cell) = self.npc_map.npcs.get_mut(npc_id) {
|
if let Some(npc_cell) = self.npc_map.npcs.get(npc_id) {
|
||||||
let mut npc = npc_cell.borrow_mut();
|
let mut npc = npc_cell.borrow_mut();
|
||||||
|
|
||||||
if npc.cond.alive() && !npc.npc_flags.ignore_solidity() {
|
if npc.cond.alive() && !npc.npc_flags.ignore_solidity() {
|
||||||
|
|
@ -935,7 +938,31 @@ impl GameScene {
|
||||||
self.bullet_manager.tick_bullets(state, &self.player, &mut self.stage);
|
self.bullet_manager.tick_bullets(state, &self.player, &mut self.stage);
|
||||||
state.tick_carets();
|
state.tick_carets();
|
||||||
|
|
||||||
self.frame.update(state, &self.player, &self.stage);
|
match self.frame.update_target {
|
||||||
|
UpdateTarget::Player => {
|
||||||
|
self.frame.target_x = self.player.target_x;
|
||||||
|
self.frame.target_y = self.player.target_y;
|
||||||
|
}
|
||||||
|
UpdateTarget::NPC(npc_id) => {
|
||||||
|
if let Some(npc_cell) = self.npc_map.npcs.get(&npc_id) {
|
||||||
|
let mut npc = npc_cell.borrow();
|
||||||
|
|
||||||
|
if npc.cond.alive() {
|
||||||
|
self.frame.target_x = npc.x;
|
||||||
|
self.frame.target_y = npc.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UpdateTarget::Boss(boss_id) => {
|
||||||
|
if let Some(boss) = self.npc_map.boss_map.parts.get(boss_id as usize) {
|
||||||
|
if boss.cond.alive() {
|
||||||
|
self.frame.target_x = boss.x;
|
||||||
|
self.frame.target_y = boss.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.frame.update(state, &self.stage);
|
||||||
|
|
||||||
if state.control_flags.control_enabled() {
|
if state.control_flags.control_enabled() {
|
||||||
if let Some(weapon) = self.inventory.get_current_weapon_mut() {
|
if let Some(weapon) = self.inventory.get_current_weapon_mut() {
|
||||||
|
|
@ -1162,9 +1189,9 @@ impl Scene for GameScene {
|
||||||
state.npc_table.tex_npc1_name = ["Npc/", &self.stage.data.npc1.filename()].join("");
|
state.npc_table.tex_npc1_name = ["Npc/", &self.stage.data.npc1.filename()].join("");
|
||||||
state.npc_table.tex_npc2_name = ["Npc/", &self.stage.data.npc2.filename()].join("");
|
state.npc_table.tex_npc2_name = ["Npc/", &self.stage.data.npc2.filename()].join("");
|
||||||
|
|
||||||
self.player.target_x = self.player.x;
|
self.frame.target_x = self.player.x;
|
||||||
self.player.target_y = self.player.y;
|
self.frame.target_y = self.player.y;
|
||||||
self.frame.immediate_update(state, &self.player, &self.stage);
|
self.frame.immediate_update(state, &self.stage);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ use crate::scene::title_scene::TitleScene;
|
||||||
use crate::shared_game_state::SharedGameState;
|
use crate::shared_game_state::SharedGameState;
|
||||||
use crate::str;
|
use crate::str;
|
||||||
use crate::weapon::WeaponType;
|
use crate::weapon::WeaponType;
|
||||||
|
use crate::frame::UpdateTarget;
|
||||||
|
|
||||||
/// Engine's text script VM operation codes.
|
/// Engine's text script VM operation codes.
|
||||||
#[derive(EnumString, Debug, FromPrimitive, PartialEq)]
|
#[derive(EnumString, Debug, FromPrimitive, PartialEq)]
|
||||||
|
|
@ -49,11 +50,11 @@ pub enum OpCode {
|
||||||
/// <BSLxxxx, Starts boss fight
|
/// <BSLxxxx, Starts boss fight
|
||||||
BSL,
|
BSL,
|
||||||
|
|
||||||
/// <FOBxxxx, Focuses on boss
|
/// <FOBxxxx:yyyy, Focuses on boss part xxxx and sets speed to yyyy ticks
|
||||||
FOB,
|
FOB,
|
||||||
/// <FOMxxxx, Focuses on me
|
/// <FOMxxxx, Focuses on player and sets speed to xxxx
|
||||||
FOM,
|
FOM,
|
||||||
/// <FONxxxx:yyyy, Focuses on NPC for yyyy ticks
|
/// <FONxxxx:yyyy, Focuses on NPC tagged with event xxxx and sets speed to yyyy
|
||||||
FON,
|
FON,
|
||||||
/// <FLA, Flashes screen
|
/// <FLA, Flashes screen
|
||||||
FLA,
|
FLA,
|
||||||
|
|
@ -354,6 +355,7 @@ pub enum TextScriptExecutionState {
|
||||||
WaitFade(u16, u32),
|
WaitFade(u16, u32),
|
||||||
SaveProfile(u16, u32),
|
SaveProfile(u16, u32),
|
||||||
LoadProfile,
|
LoadProfile,
|
||||||
|
Reset,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TextScriptVM {
|
pub struct TextScriptVM {
|
||||||
|
|
@ -639,6 +641,10 @@ impl TextScriptVM {
|
||||||
state.load_or_start_game(ctx)?;
|
state.load_or_start_game(ctx)?;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
TextScriptExecutionState::Reset => {
|
||||||
|
state.start_intro(ctx)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -689,7 +695,7 @@ impl TextScriptVM {
|
||||||
state.textscript_vm.stack.clear();
|
state.textscript_vm.stack.clear();
|
||||||
|
|
||||||
game_scene.player.cond.set_interacted(false);
|
game_scene.player.cond.set_interacted(false);
|
||||||
game_scene.player.update_target = true;
|
game_scene.frame.update_target = UpdateTarget::Player;
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Ended;
|
exec_state = TextScriptExecutionState::Ended;
|
||||||
}
|
}
|
||||||
|
|
@ -1100,10 +1106,19 @@ impl TextScriptVM {
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
|
OpCode::FOB => {
|
||||||
|
let part_id = read_cur_varint(&mut cursor)? as u16;
|
||||||
|
let ticks = read_cur_varint(&mut cursor)? as isize;
|
||||||
|
|
||||||
|
game_scene.frame.wait = ticks;
|
||||||
|
game_scene.frame.update_target = UpdateTarget::Boss(part_id);
|
||||||
|
|
||||||
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
|
}
|
||||||
OpCode::FOM => {
|
OpCode::FOM => {
|
||||||
let ticks = read_cur_varint(&mut cursor)? as isize;
|
let ticks = read_cur_varint(&mut cursor)? as isize;
|
||||||
game_scene.frame.wait = ticks;
|
game_scene.frame.wait = ticks;
|
||||||
game_scene.player.update_target = true;
|
game_scene.frame.update_target = UpdateTarget::Player;
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
|
|
@ -1111,15 +1126,13 @@ impl TextScriptVM {
|
||||||
let event_num = read_cur_varint(&mut cursor)? as u16;
|
let event_num = read_cur_varint(&mut cursor)? as u16;
|
||||||
let ticks = read_cur_varint(&mut cursor)? as isize;
|
let ticks = read_cur_varint(&mut cursor)? as isize;
|
||||||
game_scene.frame.wait = ticks;
|
game_scene.frame.wait = ticks;
|
||||||
game_scene.player.update_target = false;
|
|
||||||
|
|
||||||
for npc_id in game_scene.npc_map.npc_ids.iter() {
|
for npc_id in game_scene.npc_map.npc_ids.iter() {
|
||||||
if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) {
|
if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) {
|
||||||
let npc = npc_cell.borrow();
|
let npc = npc_cell.borrow();
|
||||||
|
|
||||||
if event_num == npc.event_num {
|
if event_num == npc.event_num {
|
||||||
game_scene.player.target_x = npc.x;
|
game_scene.frame.update_target = UpdateTarget::NPC(npc.id);
|
||||||
game_scene.player.target_y = npc.y;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1355,8 +1368,10 @@ impl TextScriptVM {
|
||||||
|
|
||||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||||
}
|
}
|
||||||
// todo: INI starts intro
|
OpCode::INI => {
|
||||||
OpCode::INI | OpCode::ESC => {
|
exec_state = TextScriptExecutionState::Reset;
|
||||||
|
}
|
||||||
|
OpCode::ESC => {
|
||||||
state.next_scene = Some(Box::new(TitleScene::new()));
|
state.next_scene = Some(Box::new(TitleScene::new()));
|
||||||
state.control_flags.set_tick_world(false);
|
state.control_flags.set_tick_world(false);
|
||||||
state.control_flags.set_control_enabled(false);
|
state.control_flags.set_control_enabled(false);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue