1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-11-30 08:08:18 +00:00

Improved camera

This commit is contained in:
Alula 2020-11-01 20:39:57 +01:00
parent 8050beed56
commit 69e2b00cdc
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
5 changed files with 79 additions and 30 deletions

View file

@ -3,11 +3,22 @@ use crate::shared_game_state::SharedGameState;
use crate::stage::Stage;
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 x: isize,
pub y: isize,
pub prev_x: isize,
pub prev_y: isize,
pub update_target: UpdateTarget,
pub target_x: isize,
pub target_y: isize,
pub wait: isize,
}
@ -19,11 +30,11 @@ impl Frame {
(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 {
self.x = -(((state.canvas_size.0 as isize - ((stage.map.width - 1) * 16) as isize) * 0x200) / 2);
} 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 {
self.x = 0;
@ -38,7 +49,7 @@ impl Frame {
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);
} 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 {
self.y = 0;
@ -54,11 +65,11 @@ impl Frame {
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 {
self.x = -(((state.canvas_size.0 as isize - ((stage.map.width - 1) * 16) as isize) * 0x200) / 2);
} 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 {
self.x = 0;
@ -73,7 +84,7 @@ impl Frame {
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);
} 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 {
self.y = 0;

View file

@ -608,7 +608,7 @@ impl NPC {
} else {
self.action_counter2 += 1;
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;
let mut npc = NPCMap::create_npc(84, &state.npc_table);

View file

@ -44,7 +44,6 @@ pub struct Player {
pub down: bool,
pub shock_counter: u8,
pub current_weapon: u8,
pub update_target: bool,
pub stars: u8,
pub damage: u16,
pub air_counter: u16,
@ -90,7 +89,6 @@ impl Player {
index_x: 0,
index_y: 0,
splash: false,
update_target: true,
up: false,
down: false,
current_weapon: 0,
@ -448,10 +446,8 @@ impl Player {
}
}
if self.update_target {
self.target_x = self.x + self.index_x;
self.target_y = self.y + self.index_y;
}
self.target_x = self.x + self.index_x;
self.target_y = self.y + self.index_y;
if self.vel_x > physics.resist || self.vel_x < -physics.resist {
self.x += self.vel_x;

View file

@ -5,7 +5,7 @@ use crate::bullet::BulletManager;
use crate::caret::CaretType;
use crate::common::{Direction, FadeDirection, FadeState, fix9_scale, Rect};
use crate::entity::GameEntity;
use crate::frame::Frame;
use crate::frame::{Frame, UpdateTarget};
use crate::ggez::{Context, GameResult, graphics, timer};
use crate::ggez::graphics::{BlendMode, Color, Drawable, DrawParam, FilterMode, Vector2};
use crate::ggez::nalgebra::clamp;
@ -79,6 +79,9 @@ impl GameScene {
y: 0,
prev_x: 0,
prev_y: 0,
update_target: UpdateTarget::Player,
target_x: 0,
target_y: 0,
wait: 16,
},
stage_id: id,
@ -918,7 +921,7 @@ impl GameScene {
self.player.tick_npc_collisions(state, &mut self.npc_map, &mut self.inventory);
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();
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);
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 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_npc2_name = ["Npc/", &self.stage.data.npc2.filename()].join("");
self.player.target_x = self.player.x;
self.player.target_y = self.player.y;
self.frame.immediate_update(state, &self.player, &self.stage);
self.frame.target_x = self.player.x;
self.frame.target_y = self.player.y;
self.frame.immediate_update(state, &self.stage);
Ok(())
}

View file

@ -28,6 +28,7 @@ use crate::scene::title_scene::TitleScene;
use crate::shared_game_state::SharedGameState;
use crate::str;
use crate::weapon::WeaponType;
use crate::frame::UpdateTarget;
/// Engine's text script VM operation codes.
#[derive(EnumString, Debug, FromPrimitive, PartialEq)]
@ -49,11 +50,11 @@ pub enum OpCode {
/// <BSLxxxx, Starts boss fight
BSL,
/// <FOBxxxx, Focuses on boss
/// <FOBxxxx:yyyy, Focuses on boss part xxxx and sets speed to yyyy ticks
FOB,
/// <FOMxxxx, Focuses on me
/// <FOMxxxx, Focuses on player and sets speed to xxxx
FOM,
/// <FONxxxx:yyyy, Focuses on NPC for yyyy ticks
/// <FONxxxx:yyyy, Focuses on NPC tagged with event xxxx and sets speed to yyyy
FON,
/// <FLA, Flashes screen
FLA,
@ -354,6 +355,7 @@ pub enum TextScriptExecutionState {
WaitFade(u16, u32),
SaveProfile(u16, u32),
LoadProfile,
Reset,
}
pub struct TextScriptVM {
@ -639,6 +641,10 @@ impl TextScriptVM {
state.load_or_start_game(ctx)?;
break;
}
TextScriptExecutionState::Reset => {
state.start_intro(ctx)?;
break;
}
}
}
@ -689,7 +695,7 @@ impl TextScriptVM {
state.textscript_vm.stack.clear();
game_scene.player.cond.set_interacted(false);
game_scene.player.update_target = true;
game_scene.frame.update_target = UpdateTarget::Player;
exec_state = TextScriptExecutionState::Ended;
}
@ -894,7 +900,7 @@ impl TextScriptVM {
OpCode::SMP => {
let pos_x = read_cur_varint(&mut cursor)? as usize;
let pos_y = read_cur_varint(&mut cursor)? as usize;
let tile_type = game_scene.stage.tile_at(pos_x, pos_y);
game_scene.stage.change_tile(pos_x, pos_y, tile_type.wrapping_sub(1));
@ -1100,10 +1106,19 @@ impl TextScriptVM {
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 => {
let ticks = read_cur_varint(&mut cursor)? as isize;
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);
}
@ -1111,15 +1126,13 @@ impl TextScriptVM {
let event_num = read_cur_varint(&mut cursor)? as u16;
let ticks = read_cur_varint(&mut cursor)? as isize;
game_scene.frame.wait = ticks;
game_scene.player.update_target = false;
for npc_id in game_scene.npc_map.npc_ids.iter() {
if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) {
let npc = npc_cell.borrow();
if event_num == npc.event_num {
game_scene.player.target_x = npc.x;
game_scene.player.target_y = npc.y;
game_scene.frame.update_target = UpdateTarget::NPC(npc.id);
break;
}
}
@ -1355,8 +1368,10 @@ impl TextScriptVM {
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
// todo: INI starts intro
OpCode::INI | OpCode::ESC => {
OpCode::INI => {
exec_state = TextScriptExecutionState::Reset;
}
OpCode::ESC => {
state.next_scene = Some(Box::new(TitleScene::new()));
state.control_flags.set_tick_world(false);
state.control_flags.set_control_enabled(false);