mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-03-23 10:29:18 +00:00
physics abstraction and npc physics implementation
This commit is contained in:
parent
51b9184a85
commit
d79657bb6a
|
@ -64,6 +64,28 @@ bitfield! {
|
|||
pub alive, set_alive: 7; // 0x80
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
pub struct KeyState(u16);
|
||||
impl Debug;
|
||||
pub left, set_left: 0;
|
||||
pub right, set_right: 1;
|
||||
pub up, set_up: 2;
|
||||
pub down, set_down: 3;
|
||||
pub map, set_map: 4;
|
||||
pub jump, set_jump: 5;
|
||||
pub fire, set_fire: 6;
|
||||
pub weapon_next, set_weapon_next: 7;
|
||||
pub weapon_prev, set_weapon_prev: 8;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
pub struct ControlFlags(u16);
|
||||
impl Debug;
|
||||
pub flag_x01, set_flag_x01: 0;
|
||||
pub control_enabled, set_control_enabled: 1;
|
||||
pub flag_x04, set_flag_x04: 2;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum FadeDirection {
|
||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -24,7 +24,7 @@ use winit::{ElementState, Event, KeyboardInput, WindowEvent};
|
|||
use crate::bmfont_renderer::BMFontRenderer;
|
||||
use crate::builtin_fs::BuiltinFS;
|
||||
use crate::caret::{Caret, CaretType};
|
||||
use crate::common::{Direction, FadeState};
|
||||
use crate::common::{Direction, FadeState, KeyState, ControlFlags};
|
||||
use crate::engine_constants::EngineConstants;
|
||||
use crate::ggez::{Context, ContextBuilder, event, filesystem, GameResult};
|
||||
use crate::ggez::conf::{WindowMode, WindowSetup};
|
||||
|
@ -57,6 +57,7 @@ mod live_debugger;
|
|||
mod macros;
|
||||
mod map;
|
||||
mod npc;
|
||||
mod physics;
|
||||
mod player;
|
||||
mod player_hit;
|
||||
mod rng;
|
||||
|
@ -68,28 +69,6 @@ mod texture_set;
|
|||
mod ui;
|
||||
mod weapon;
|
||||
|
||||
bitfield! {
|
||||
pub struct KeyState(u16);
|
||||
impl Debug;
|
||||
left, set_left: 0;
|
||||
right, set_right: 1;
|
||||
up, set_up: 2;
|
||||
down, set_down: 3;
|
||||
map, set_map: 4;
|
||||
jump, set_jump: 5;
|
||||
fire, set_fire: 6;
|
||||
weapon_next, set_weapon_next: 7;
|
||||
weapon_prev, set_weapon_prev: 8;
|
||||
}
|
||||
|
||||
bitfield! {
|
||||
pub struct ControlFlags(u16);
|
||||
impl Debug;
|
||||
pub flag_x01, set_flag_x01: 0;
|
||||
pub control_enabled, set_control_enabled: 1;
|
||||
pub flag_x04, set_flag_x04: 2;
|
||||
}
|
||||
|
||||
struct Game {
|
||||
scene: Option<Box<dyn Scene>>,
|
||||
state: SharedGameState,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use num_traits::clamp;
|
||||
|
||||
use crate::common::Direction;
|
||||
use crate::ggez::GameResult;
|
||||
use crate::npc::NPC;
|
||||
use crate::player::Player;
|
||||
use crate::SharedGameState;
|
||||
use num_traits::clamp;
|
||||
|
||||
impl NPC {
|
||||
pub(crate) fn tick_n052_sitting_blue_robot(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
|
@ -82,7 +83,6 @@ impl NPC {
|
|||
|
||||
if (self.x - (16 * 0x200) < player.x) && (self.x + (16 * 0x200) > player.x)
|
||||
&& (self.y - (16 * 0x200) < player.y) && (self.y + (16 * 0x200) > player.y) {
|
||||
|
||||
if self.x > player.x {
|
||||
self.direction = Direction::Left;
|
||||
} else {
|
||||
|
@ -97,7 +97,13 @@ impl NPC {
|
|||
self.anim_num = 0;
|
||||
}
|
||||
}
|
||||
3 => {
|
||||
3 | 4 => {
|
||||
if self.action_num == 3 {
|
||||
self.action_num = 4;
|
||||
self.anim_num = 1;
|
||||
self.anim_counter = 0;
|
||||
}
|
||||
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 2 {
|
||||
self.anim_counter = 0;
|
||||
|
@ -110,10 +116,12 @@ impl NPC {
|
|||
|
||||
if self.flags.hit_left_wall() {
|
||||
self.direction = Direction::Right;
|
||||
self.vel_x = 0x200;
|
||||
}
|
||||
|
||||
if self.flags.hit_right_wall() {
|
||||
self.direction = Direction::Left;
|
||||
self.vel_x = -0x200;
|
||||
}
|
||||
|
||||
if self.direction == Direction::Left {
|
||||
|
@ -202,8 +210,8 @@ impl NPC {
|
|||
self.vel_y = 0x5ff;
|
||||
}
|
||||
|
||||
//self.x += self.vel_x;
|
||||
//self.y += self.vel_y;
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
if self.direction == Direction::Left {
|
||||
self.anim_rect = state.constants.npc.n060_toroko[self.anim_num as usize];
|
||||
|
@ -327,8 +335,8 @@ impl NPC {
|
|||
}
|
||||
}
|
||||
|
||||
//self.x += self.vel_x;
|
||||
//self.y += self.vel_y;
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
if self.direction == Direction::Left {
|
||||
self.anim_rect = state.constants.npc.n061_king[self.anim_num as usize];
|
||||
|
@ -456,8 +464,8 @@ impl NPC {
|
|||
self.vel_y = 0x5ff;
|
||||
}
|
||||
|
||||
//self.x += self.vel_x;
|
||||
//self.y += self.vel_y;
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
|
||||
if self.direction == Direction::Left {
|
||||
self.anim_rect = state.constants.npc.n074_jack[self.anim_num as usize];
|
||||
|
|
|
@ -149,7 +149,7 @@ impl NPC {
|
|||
self.vel_y = 0x5ff;
|
||||
}
|
||||
|
||||
//self.y += self.vel_y;
|
||||
self.y += self.vel_y;
|
||||
|
||||
if self.direction == Direction::Left {
|
||||
self.anim_rect = state.constants.npc.n079_mahin[self.anim_num as usize];
|
||||
|
|
102
src/npc/mod.rs
102
src/npc/mod.rs
|
@ -13,6 +13,7 @@ use crate::entity::GameEntity;
|
|||
use crate::frame::Frame;
|
||||
use crate::ggez::{Context, GameResult};
|
||||
use crate::map::NPCData;
|
||||
use crate::physics::PhysicalEntity;
|
||||
use crate::player::Player;
|
||||
use crate::str;
|
||||
use crate::text_script::TextScriptExecutionState;
|
||||
|
@ -54,6 +55,7 @@ pub struct NPC {
|
|||
pub vel_y: isize,
|
||||
pub target_x: isize,
|
||||
pub target_y: isize,
|
||||
pub size: u8,
|
||||
pub shock: u16,
|
||||
pub life: u16,
|
||||
pub cond: Condition,
|
||||
|
@ -128,6 +130,78 @@ impl GameEntity<&mut Player> for NPC {
|
|||
}
|
||||
}
|
||||
|
||||
impl PhysicalEntity for NPC {
|
||||
#[inline(always)]
|
||||
fn x(&self) -> isize {
|
||||
self.x
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn y(&self) -> isize {
|
||||
self.y
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn vel_x(&self) -> isize {
|
||||
self.vel_x
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn vel_y(&self) -> isize {
|
||||
self.vel_y
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn size(&self) -> u8 {
|
||||
self.size
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn hit_bounds(&self) -> &Rect<usize> {
|
||||
&self.hit_bounds
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_x(&mut self, x: isize) {
|
||||
self.x = x;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_y(&mut self, y: isize) {
|
||||
self.y = y;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_vel_x(&mut self, vel_x: isize) {
|
||||
self.vel_x = vel_x;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_vel_y(&mut self, vel_y: isize) {
|
||||
self.vel_y = vel_y;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn cond(&mut self) -> &mut Condition {
|
||||
&mut self.cond
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flags(&mut self) -> &mut Flag {
|
||||
&mut self.flags
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_player(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn ignore_tile_44(&self) -> bool {
|
||||
self.npc_flags.ignore_tile_44()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NPCMap {
|
||||
/// A sorted pool of free IDs to make ID assignment for new entities a bit cheaper.
|
||||
free_npc_ids: BTreeSet<u16>,
|
||||
|
@ -155,6 +229,13 @@ impl NPCMap {
|
|||
|
||||
pub fn create_npc_from_data(&mut self, table: &NPCTable, data: &NPCData) -> &mut NPC {
|
||||
let npc_flags = NPCFlag(data.flags);
|
||||
let display_bounds = table.get_display_bounds(data.npc_type);
|
||||
let hit_bounds = table.get_hit_bounds(data.npc_type);
|
||||
let (size, life) = match table.get_entry(data.npc_type) {
|
||||
Some(entry) => { (entry.size, entry.life) }
|
||||
None => { (1, 0) }
|
||||
};
|
||||
|
||||
let npc = NPC {
|
||||
id: data.id,
|
||||
npc_type: data.npc_type,
|
||||
|
@ -169,13 +250,14 @@ impl NPCMap {
|
|||
flag_num: data.flag_num,
|
||||
event_num: data.event_num,
|
||||
shock: 0,
|
||||
life: table.get_life(data.npc_type),
|
||||
size,
|
||||
life,
|
||||
cond: Condition(0x00),
|
||||
flags: Flag(data.flag_num as u32),
|
||||
direction: if npc_flags.spawn_facing_right() { Direction::Right } else { Direction::Left },
|
||||
npc_flags,
|
||||
display_bounds: table.get_display_bounds(data.npc_type),
|
||||
hit_bounds: table.get_hit_bounds(data.npc_type),
|
||||
display_bounds,
|
||||
hit_bounds,
|
||||
action_counter: 0,
|
||||
anim_counter: 0,
|
||||
anim_rect: Rect::new(0, 0, 0, 0),
|
||||
|
@ -204,7 +286,7 @@ pub struct NPCTableEntry {
|
|||
pub spritesheet_id: u8,
|
||||
pub death_sound: u8,
|
||||
pub hurt_sound: u8,
|
||||
pub death_smoke: u8,
|
||||
pub size: u8,
|
||||
pub experience: u32,
|
||||
pub damage: u32,
|
||||
pub display_bounds: Rect<u8>,
|
||||
|
@ -242,7 +324,7 @@ impl NPCTable {
|
|||
spritesheet_id: 0,
|
||||
death_sound: 0,
|
||||
hurt_sound: 0,
|
||||
death_smoke: 0,
|
||||
size: 0,
|
||||
experience: 0,
|
||||
damage: 0,
|
||||
display_bounds: Rect::new(0, 0, 0, 0),
|
||||
|
@ -271,7 +353,7 @@ impl NPCTable {
|
|||
}
|
||||
|
||||
for npc in table.entries.iter_mut() {
|
||||
npc.death_smoke = f.read_u8()?;
|
||||
npc.size = f.read_u8()?;
|
||||
}
|
||||
|
||||
for npc in table.entries.iter_mut() {
|
||||
|
@ -303,14 +385,6 @@ impl NPCTable {
|
|||
self.entries.get(npc_type as usize)
|
||||
}
|
||||
|
||||
pub fn get_life(&self, npc_type: u16) -> u16 {
|
||||
if let Some(npc) = self.entries.get(npc_type as usize) {
|
||||
npc.life
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_display_bounds(&self, npc_type: u16) -> Rect<usize> {
|
||||
if let Some(npc) = self.entries.get(npc_type as usize) {
|
||||
Rect {
|
||||
|
|
405
src/physics.rs
405
src/physics.rs
|
@ -1,3 +1,404 @@
|
|||
trait PhysicalEntity {
|
||||
|
||||
use num_traits::clamp;
|
||||
|
||||
use crate::common::{Condition, Flag, Rect};
|
||||
use crate::SharedGameState;
|
||||
use crate::stage::Stage;
|
||||
|
||||
const OFF_X: [isize; 9] = [0, 1, 0, 1, 2, 2, 2, 0, 1];
|
||||
const OFF_Y: [isize; 9] = [0, 0, 1, 1, 0, 1, 2, 2, 2];
|
||||
|
||||
pub trait PhysicalEntity {
|
||||
fn x(&self) -> isize;
|
||||
fn y(&self) -> isize;
|
||||
fn vel_x(&self) -> isize;
|
||||
fn vel_y(&self) -> isize;
|
||||
|
||||
fn size(&self) -> u8;
|
||||
|
||||
fn hit_bounds(&self) -> &Rect<usize>;
|
||||
|
||||
fn set_x(&mut self, x: isize);
|
||||
fn set_y(&mut self, y: isize);
|
||||
fn set_vel_x(&mut self, x: isize);
|
||||
fn set_vel_y(&mut self, y: isize);
|
||||
|
||||
fn cond(&mut self) -> &mut Condition;
|
||||
fn flags(&mut self) -> &mut Flag;
|
||||
|
||||
fn is_player(&self) -> bool;
|
||||
fn ignore_tile_44(&self) -> bool { true }
|
||||
|
||||
fn judge_hit_block(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
// left wall
|
||||
if (self.y() - self.hit_bounds().top as isize) < (y * 0x10 + 4) * 0x200
|
||||
&& self.y() + self.hit_bounds().bottom as isize > (y * 0x10 - 4) * 0x200
|
||||
&& (self.x() - self.hit_bounds().right as isize) < (x * 0x10 + 8) * 0x200
|
||||
&& (self.x() - self.hit_bounds().right as isize) > x * 0x10 * 0x200 {
|
||||
self.set_x(((x * 0x10 + 8) * 0x200) + self.hit_bounds().right as isize);
|
||||
|
||||
if self.is_player() {
|
||||
if self.vel_x() < -0x180 {
|
||||
self.set_vel_x(-0x180);
|
||||
}
|
||||
|
||||
if !state.key_state.left() && self.vel_x() < 0 {
|
||||
self.set_vel_x(0);
|
||||
}
|
||||
}
|
||||
|
||||
self.flags().set_hit_left_wall(true);
|
||||
}
|
||||
|
||||
// right wall
|
||||
if (self.y() - self.hit_bounds().top as isize) < (y * 0x10 + 4) * 0x200
|
||||
&& self.y() + self.hit_bounds().bottom as isize > (y * 0x10 - 4) * 0x200
|
||||
&& (self.x() + self.hit_bounds().right as isize) > (x * 0x10 - 8) * 0x200
|
||||
&& (self.x() + self.hit_bounds().right as isize) < x * 0x10 * 0x200 {
|
||||
self.set_x(((x * 0x10 - 8) * 0x200) - self.hit_bounds().right as isize);
|
||||
|
||||
if self.is_player() {
|
||||
if self.vel_x() > 0x180 {
|
||||
self.set_vel_x(0x180);
|
||||
}
|
||||
|
||||
if !state.key_state.right() && self.vel_x() > 0 {
|
||||
self.set_vel_x(0);
|
||||
}
|
||||
}
|
||||
|
||||
self.flags().set_hit_right_wall(true);
|
||||
}
|
||||
|
||||
// ceiling
|
||||
if (self.x() - self.hit_bounds().right as isize) < (x * 0x10 + 5) * 0x200
|
||||
&& (self.x() + self.hit_bounds().right as isize) > (x * 0x10 - 5) * 0x200
|
||||
&& (self.y() - self.hit_bounds().top as isize) < (y * 0x10 + 8) * 0x200
|
||||
&& (self.y() - self.hit_bounds().top as isize) > y * 0x10 * 0x200 {
|
||||
self.set_y(((y * 0x10 + 8) * 0x200) + self.hit_bounds().top as isize);
|
||||
|
||||
if self.is_player() {
|
||||
if !self.cond().hidden() && self.vel_y() < -0x200 {
|
||||
self.flags().set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y() < 0 {
|
||||
self.set_vel_y(0);
|
||||
}
|
||||
} else {
|
||||
self.set_vel_y(0);
|
||||
}
|
||||
|
||||
self.flags().set_hit_top_wall(true);
|
||||
}
|
||||
|
||||
// floor
|
||||
if ((self.x() - self.hit_bounds().right as isize) < (x * 0x10 + 5) * 0x200)
|
||||
&& ((self.x() + self.hit_bounds().right as isize) > (x * 0x10 - 5) * 0x200)
|
||||
&& ((self.y() + self.hit_bounds().bottom as isize) > (y * 0x10 - 8) * 0x200)
|
||||
&& ((self.y() + self.hit_bounds().bottom as isize) < y * 0x10 * 0x200) {
|
||||
self.set_y(((y * 0x10 - 8) * 0x200) - self.hit_bounds().bottom as isize);
|
||||
|
||||
if self.is_player() {
|
||||
if self.vel_y() > 0x400 {
|
||||
// PlaySoundObject(23, SOUND_MODE_PLAY); todo
|
||||
}
|
||||
|
||||
if self.vel_y() > 0 {
|
||||
self.set_vel_y(0);
|
||||
}
|
||||
} else {
|
||||
self.set_vel_y(0);
|
||||
}
|
||||
|
||||
self.flags().set_hit_bottom_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// upper left slope (bigger half)
|
||||
fn judge_hit_triangle_a(&mut self, x: isize, y: isize) {
|
||||
if self.x() < (x * 0x10 + 8) * 0x200
|
||||
&& self.x() > (x * 0x10 - 8) * 0x200
|
||||
&& (self.y() - self.hit_bounds().top as isize) < (y * 0x10 * 0x200) - (self.x() - x * 0x10 * 0x200) / 2 + 0x800
|
||||
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 0x10 - 8) * 0x200 {
|
||||
self.set_y((y * 0x10 * 0x200) - ((self.x() - x * 0x10 * 0x200) / 2) + 0x800 + self.hit_bounds().top as isize);
|
||||
|
||||
if self.is_player() && !self.cond().hidden() && self.vel_y() < -0x200 {
|
||||
self.flags().set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y() < 0 {
|
||||
self.set_vel_y(0);
|
||||
}
|
||||
|
||||
self.flags().set_hit_top_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// upper left slope (smaller half)
|
||||
fn judge_hit_triangle_b(&mut self, x: isize, y: isize) {
|
||||
if self.x() < (x * 0x10 + 8) * 0x200
|
||||
&& self.x() > (x * 0x10 - 8) * 0x200
|
||||
&& (self.y() - self.hit_bounds().top as isize) < (y * 0x10 * 0x200) - (self.x() - x * 0x10 * 0x200) / 2 - 0x800
|
||||
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 0x10 - 8) * 0x200 {
|
||||
self.set_y((y * 0x10 * 0x200) - ((self.x() - x * 0x10 * 0x200) / 2) - 0x800 + self.hit_bounds().top as isize);
|
||||
|
||||
if self.is_player() && !self.cond().hidden() && self.vel_y() < -0x200 {
|
||||
self.flags().set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y() < 0 {
|
||||
self.set_vel_y(0);
|
||||
}
|
||||
|
||||
self.flags().set_hit_top_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// upper right slope (smaller half)
|
||||
fn judge_hit_triangle_c(&mut self, x: isize, y: isize) {
|
||||
if self.x() < (x * 0x10 + 8) * 0x200
|
||||
&& self.x() > (x * 0x10 - 8) * 0x200
|
||||
&& (self.y() - self.hit_bounds().top as isize) < (y * 0x10 * 0x200) + (self.x() - x * 0x10 * 0x200) / 2 - 0x800
|
||||
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 0x10 - 8) * 0x200 {
|
||||
self.set_y((y * 0x10 * 0x200) + ((self.x() - x * 0x10 * 0x200) / 2) - 0x800 + self.hit_bounds().top as isize);
|
||||
|
||||
if self.is_player() && !self.cond().hidden() && self.vel_y() < -0x200 {
|
||||
self.flags().set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y() < 0 {
|
||||
self.set_vel_y(0);
|
||||
}
|
||||
|
||||
self.flags().set_hit_top_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// upper right slope (bigger half)
|
||||
fn judge_hit_triangle_d(&mut self, x: isize, y: isize) {
|
||||
if (self.x() < (x * 0x10 + 8) * 0x200)
|
||||
&& (self.x() > (x * 0x10 - 8) * 0x200)
|
||||
&& (self.y() - self.hit_bounds().top as isize) < (y * 0x10 * 0x200) + (self.x() - x * 0x10 * 0x200) / 2 + 0x800
|
||||
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 0x10 - 8) * 0x200 {
|
||||
self.set_y((y * 0x10 * 0x200) + ((self.x() - x * 0x10 * 0x200) / 2) + 0x800 + self.hit_bounds().top as isize);
|
||||
|
||||
if self.is_player() && !self.cond().hidden() && self.vel_y() < -0x200 {
|
||||
self.flags().set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y() < 0 {
|
||||
self.set_vel_y(0);
|
||||
}
|
||||
|
||||
self.flags().set_hit_top_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// lower left half (bigger)
|
||||
fn judge_hit_triangle_e(&mut self, x: isize, y: isize) {
|
||||
self.flags().set_hit_left_bigger_half(true);
|
||||
|
||||
if (self.x() < (x * 0x10 + 8) * 0x200)
|
||||
&& (self.x() > (x * 0x10 - 8) * 0x200)
|
||||
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 0x10 * 0x200) + (self.x() - x * 0x10 * 0x200) / 2 - 0x800
|
||||
&& (self.y() - self.hit_bounds().top as isize) < (y * 0x10 + 8) * 0x200 {
|
||||
self.set_y((y * 0x10 * 0x200) + ((self.x() - x * 0x10 * 0x200) / 2) - 0x800 - self.hit_bounds().bottom as isize);
|
||||
|
||||
if self.is_player() && self.vel_y() > 0x400 {
|
||||
// PlaySoundObject(23, SOUND_MODE_PLAY); todo
|
||||
}
|
||||
|
||||
if self.vel_y() > 0 {
|
||||
self.set_vel_y(0);
|
||||
}
|
||||
|
||||
self.flags().set_hit_left_slope(true);
|
||||
self.flags().set_hit_bottom_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// lower left half (smaller)
|
||||
fn judge_hit_triangle_f(&mut self, x: isize, y: isize) {
|
||||
self.flags().set_hit_left_smaller_half(true);
|
||||
|
||||
if (self.x() < (x * 0x10 + 8) * 0x200)
|
||||
&& (self.x() > (x * 0x10 - 8) * 0x200)
|
||||
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 0x10 * 0x200) + (self.x() - x * 0x10 * 0x200) / 2 + 0x800
|
||||
&& (self.y() - self.hit_bounds().top as isize) < (y * 0x10 + 8) * 0x200 {
|
||||
self.set_y((y * 0x10 * 0x200) + ((self.x() - x * 0x10 * 0x200) / 2) + 0x800 - self.hit_bounds().bottom as isize);
|
||||
|
||||
if self.is_player() && self.vel_y() > 0x400 {
|
||||
// PlaySoundObject(23, SOUND_MODE_PLAY); todo
|
||||
}
|
||||
|
||||
if self.vel_y() > 0 {
|
||||
self.set_vel_y(0);
|
||||
}
|
||||
|
||||
self.flags().set_hit_left_slope(true);
|
||||
self.flags().set_hit_bottom_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// lower right half (smaller)
|
||||
fn judge_hit_triangle_g(&mut self, x: isize, y: isize) {
|
||||
self.flags().set_hit_right_smaller_half(true);
|
||||
|
||||
if (self.x() < (x * 0x10 + 8) * 0x200)
|
||||
&& (self.x() > (x * 0x10 - 8) * 0x200)
|
||||
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 0x10 * 0x200) - (self.x() - x * 0x10 * 0x200) / 2 + 0x800
|
||||
&& (self.y() - self.hit_bounds().top as isize) < (y * 0x10 + 8) * 0x200 {
|
||||
self.set_y((y * 0x10 * 0x200) - ((self.x() - x * 0x10 * 0x200) / 2) + 0x800 - self.hit_bounds().bottom as isize);
|
||||
|
||||
if self.is_player() && self.vel_y() > 0x400 {
|
||||
// PlaySoundObject(23, SOUND_MODE_PLAY); todo
|
||||
}
|
||||
|
||||
if self.vel_y() > 0 {
|
||||
self.set_vel_y(0);
|
||||
}
|
||||
|
||||
self.flags().set_hit_right_slope(true);
|
||||
self.flags().set_hit_bottom_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// lower right half (bigger)
|
||||
fn judge_hit_triangle_h(&mut self, x: isize, y: isize) {
|
||||
self.flags().set_hit_right_bigger_half(true);
|
||||
|
||||
if (self.x() < (x * 0x10 + 8) * 0x200)
|
||||
&& (self.x() > (x * 0x10 - 8) * 0x200)
|
||||
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 0x10 * 0x200) - (self.x() - x * 0x10 * 0x200) / 2 - 0x800
|
||||
&& (self.y() - self.hit_bounds().top as isize) < (y * 0x10 + 8) * 0x200 {
|
||||
self.set_y((y * 0x10 * 0x200) - ((self.x() - x * 0x10 * 0x200) / 2) - 0x800 - self.hit_bounds().bottom as isize);
|
||||
|
||||
if self.is_player() && self.vel_y() > 0x400 {
|
||||
// PlaySoundObject(23, SOUND_MODE_PLAY); todo
|
||||
}
|
||||
|
||||
if self.vel_y() > 0 {
|
||||
self.set_vel_y(0);
|
||||
}
|
||||
|
||||
self.flags().set_hit_right_slope(true);
|
||||
self.flags().set_hit_bottom_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
fn judge_hit_water(&mut self, x: isize, y: isize) {
|
||||
if (self.x() - self.hit_bounds().right as isize) < (x * 0x10 + 5) * 0x200
|
||||
&& (self.x() + self.hit_bounds().right as isize) > (x * 0x10 - 5) * 0x200
|
||||
&& (self.y() - self.hit_bounds().top as isize) < (y * 0x10 + 5) * 0x200
|
||||
&& (self.y() + self.hit_bounds().bottom as isize) > y * 0x10 * 0x200 {
|
||||
self.flags().set_in_water(true);
|
||||
}
|
||||
}
|
||||
|
||||
fn judge_hit_spike(&mut self, x: isize, y: isize) {
|
||||
if (self.x() - self.hit_bounds().right as isize) < (x * 0x10 + 4) * 0x200
|
||||
&& (self.x() + self.hit_bounds().right as isize) > (x * 0x10 - 4) * 0x200
|
||||
&& (self.y() - self.hit_bounds().top as isize) < (y * 0x10 + 3) * 0x200
|
||||
&& (self.y() + self.hit_bounds().bottom as isize) > (y * 0x10 - 3) * 0x200 {
|
||||
self.flags().set_hit_by_spike(true);
|
||||
}
|
||||
}
|
||||
|
||||
fn tick_map_collisions(&mut self, state: &SharedGameState, stage: &Stage) {
|
||||
let big = self.size() >= 3;
|
||||
let x = clamp((self.x() - if big { 0x1000 } else { 0 }) / 0x10 / 0x200, 0, stage.map.width as isize);
|
||||
let y = clamp((self.y() - if big { 0x1000 } else { 0 }) / 0x10 / 0x200, 0, stage.map.height as isize);
|
||||
|
||||
for (idx, (&ox, &oy)) in OFF_X.iter().zip(OFF_Y.iter()).enumerate() {
|
||||
if idx == 4 && big {
|
||||
break;
|
||||
}
|
||||
|
||||
let attrib = stage.map.get_attribute((x + ox) as usize, (y + oy) as usize);
|
||||
match attrib {
|
||||
// Spikes
|
||||
0x62 | 0x42 if self.is_player() => {
|
||||
if attrib & 0x20 != 0 { self.flags().set_in_water(true); }
|
||||
self.judge_hit_spike(x + ox, y + ox);
|
||||
}
|
||||
|
||||
// Blocks
|
||||
0x02 | 0x60 | 0x62 => {
|
||||
self.judge_hit_water(x + ox, y + oy);
|
||||
}
|
||||
0x05 | 0x41 | 0x43 | 0x46 if self.is_player() => {
|
||||
self.judge_hit_block(state, x + ox, y + oy);
|
||||
}
|
||||
0x03 | 0x05 | 0x41 | 0x43 if !self.is_player() => {
|
||||
self.judge_hit_block(state, x + ox, y + oy);
|
||||
}
|
||||
0x44 => {
|
||||
if !self.ignore_tile_44() {
|
||||
self.judge_hit_block(state, x + ox, y + oy);
|
||||
}
|
||||
}
|
||||
|
||||
// Slopes
|
||||
0x50 | 0x70 => {
|
||||
self.judge_hit_triangle_a(x + ox, y + oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(x + ox, y + oy); }
|
||||
}
|
||||
0x51 | 0x71 => {
|
||||
self.judge_hit_triangle_b(x + ox, y + oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(x + ox, y + oy); }
|
||||
}
|
||||
0x52 | 0x72 => {
|
||||
self.judge_hit_triangle_c(x + ox, y + oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(x + ox, y + oy); }
|
||||
}
|
||||
0x53 | 0x73 => {
|
||||
self.judge_hit_triangle_d(x + ox, y + oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(x + ox, y + oy); }
|
||||
}
|
||||
0x54 | 0x74 => {
|
||||
self.judge_hit_triangle_e(x + ox, y + oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(x + ox, y + oy); }
|
||||
}
|
||||
0x55 | 0x75 => {
|
||||
self.judge_hit_triangle_f(x + ox, y + oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(x + ox, y + oy); }
|
||||
}
|
||||
0x56 | 0x76 => {
|
||||
self.judge_hit_triangle_g(x + ox, y + oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(x + ox, y + oy); }
|
||||
}
|
||||
0x57 | 0x77 => {
|
||||
self.judge_hit_triangle_h(x + ox, y + oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(x + ox, y + oy); }
|
||||
}
|
||||
0x61 => {
|
||||
self.judge_hit_water(x + ox, y + oy);
|
||||
self.judge_hit_block(state, x + ox, y + oy);
|
||||
}
|
||||
0x04 | 0x64 if !self.is_player() => {
|
||||
self.judge_hit_water(x + ox, y + oy);
|
||||
self.judge_hit_block(state, x + ox, y + oy);
|
||||
}
|
||||
|
||||
// Forces
|
||||
0x80 | 0xa0 => {
|
||||
if attrib & 0x20 != 0 { self.flags().set_in_water(true); }
|
||||
self.flags().set_force_left(true);
|
||||
}
|
||||
0x81 | 0xa1 => {
|
||||
if attrib & 0x20 != 0 { self.flags().set_in_water(true); }
|
||||
self.flags().set_force_up(true);
|
||||
}
|
||||
0x82 | 0xa2 => {
|
||||
if attrib & 0x20 != 0 { self.flags().set_in_water(true); }
|
||||
self.flags().set_force_right(true);
|
||||
}
|
||||
0x83 | 0xa3 => {
|
||||
if attrib & 0x20 != 0 { self.flags().set_in_water(true); }
|
||||
self.flags().set_force_down(true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,326 +1,81 @@
|
|||
use num_traits::clamp;
|
||||
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::{Direction, Flag};
|
||||
use crate::common::{Condition, Direction, Flag, Rect};
|
||||
use crate::npc::{NPC, NPCMap};
|
||||
use crate::physics::PhysicalEntity;
|
||||
use crate::player::Player;
|
||||
use crate::SharedGameState;
|
||||
use crate::stage::Stage;
|
||||
|
||||
const OFF_X: &[isize; 4] = &[0, 1, 0, 1];
|
||||
const OFF_Y: &[isize; 4] = &[0, 0, 1, 1];
|
||||
impl PhysicalEntity for Player {
|
||||
#[inline(always)]
|
||||
fn x(&self) -> isize {
|
||||
self.x
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn y(&self) -> isize {
|
||||
self.y
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn vel_x(&self) -> isize {
|
||||
self.vel_x
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn vel_y(&self) -> isize {
|
||||
self.vel_y
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn size(&self) -> u8 {
|
||||
1
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn hit_bounds(&self) -> &Rect<usize> {
|
||||
&self.hit_bounds
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_x(&mut self, x: isize) {
|
||||
self.x = x;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_y(&mut self, y: isize) {
|
||||
self.y = y;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_vel_x(&mut self, vel_x: isize) {
|
||||
self.vel_x = vel_x;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn set_vel_y(&mut self, vel_y: isize) {
|
||||
self.vel_y = vel_y;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn cond(&mut self) -> &mut Condition {
|
||||
&mut self.cond
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn flags(&mut self) -> &mut Flag {
|
||||
&mut self.flags
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_player(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Player {
|
||||
fn judge_hit_block(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
// left wall
|
||||
if (self.y - self.hit_bounds.top as isize) < (y * 0x10 + 4) * 0x200
|
||||
&& self.y + self.hit_bounds.bottom as isize > (y * 0x10 - 4) * 0x200
|
||||
&& (self.x - self.hit_bounds.right as isize) < (x * 0x10 + 8) * 0x200
|
||||
&& (self.x - self.hit_bounds.right as isize) > x * 0x10 * 0x200 {
|
||||
self.x = ((x * 0x10 + 8) * 0x200) + self.hit_bounds.right as isize;
|
||||
|
||||
if self.vel_x < -0x180 {
|
||||
self.vel_x = -0x180;
|
||||
}
|
||||
|
||||
if !state.key_state.left() && self.vel_x < 0 {
|
||||
self.vel_x = 0;
|
||||
}
|
||||
|
||||
self.flags.set_hit_left_wall(true);
|
||||
}
|
||||
|
||||
// right wall
|
||||
if (self.y - self.hit_bounds.top as isize) < (y * 0x10 + 4) * 0x200
|
||||
&& self.y + self.hit_bounds.bottom as isize > (y * 0x10 - 4) * 0x200
|
||||
&& (self.x + self.hit_bounds.right as isize) > (x * 0x10 - 8) * 0x200
|
||||
&& (self.x + self.hit_bounds.right as isize) < x * 0x10 * 0x200 {
|
||||
self.x = ((x * 0x10 - 8) * 0x200) - self.hit_bounds.right as isize;
|
||||
|
||||
if self.vel_x > 0x180 {
|
||||
self.vel_x = 0x180;
|
||||
}
|
||||
|
||||
if !state.key_state.right() && self.vel_x > 0 {
|
||||
self.vel_x = 0;
|
||||
}
|
||||
|
||||
self.flags.set_hit_right_wall(true);
|
||||
}
|
||||
|
||||
// ceiling
|
||||
if (self.x - self.hit_bounds.right as isize) < (x * 0x10 + 5) * 0x200
|
||||
&& (self.x + self.hit_bounds.right as isize) > (x * 0x10 - 5) * 0x200
|
||||
&& (self.y - self.hit_bounds.top as isize) < (y * 0x10 + 8) * 0x200
|
||||
&& (self.y - self.hit_bounds.top as isize) > y * 0x10 * 0x200 {
|
||||
self.y = ((y * 0x10 + 8) * 0x200) + self.hit_bounds.top as isize;
|
||||
|
||||
if !self.cond.hidden() && self.vel_y < -0x200 {
|
||||
self.flags.set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y < 0 {
|
||||
self.vel_y = 0;
|
||||
}
|
||||
|
||||
self.flags.set_hit_top_wall(true);
|
||||
}
|
||||
|
||||
// floor
|
||||
if ((self.x - self.hit_bounds.right as isize) < (x * 0x10 + 5) * 0x200)
|
||||
&& ((self.x + self.hit_bounds.right as isize) > (x * 0x10 - 5) * 0x200)
|
||||
&& ((self.y + self.hit_bounds.bottom as isize) > (y * 0x10 - 8) * 0x200)
|
||||
&& ((self.y + self.hit_bounds.bottom as isize) < y * 0x10 * 0x200) {
|
||||
self.y = ((y * 0x10 - 8) * 0x200) - self.hit_bounds.bottom as isize;
|
||||
|
||||
if self.vel_y > 0x400 {
|
||||
// PlaySoundObject(23, SOUND_MODE_PLAY); todo
|
||||
}
|
||||
|
||||
if self.vel_y > 0 {
|
||||
self.vel_y = 0;
|
||||
}
|
||||
|
||||
self.flags.set_hit_bottom_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// upper left slope (bigger half)
|
||||
fn judge_hit_triangle_a(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
if self.x < (x * 0x10 + 8) * 0x200
|
||||
&& self.x > (x * 0x10 - 8) * 0x200
|
||||
&& (self.y - self.hit_bounds.top as isize) < (y * 0x10 * 0x200) - (self.x - x * 0x10 * 0x200) / 2 + 0x800
|
||||
&& (self.y + self.hit_bounds.bottom as isize) > (y * 0x10 - 8) * 0x200 {
|
||||
self.y = (y * 0x10 * 0x200) - ((self.x - x * 0x10 * 0x200) / 2) + 0x800 + self.hit_bounds.top as isize;
|
||||
|
||||
if !self.cond.hidden() && self.vel_y < -0x200 {
|
||||
self.flags.set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y < 0 {
|
||||
self.vel_y = 0;
|
||||
}
|
||||
|
||||
self.flags.set_hit_top_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// upper left slope (smaller half)
|
||||
fn judge_hit_triangle_b(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
if self.x < (x * 0x10 + 8) * 0x200
|
||||
&& self.x > (x * 0x10 - 8) * 0x200
|
||||
&& (self.y - self.hit_bounds.top as isize) < (y * 0x10 * 0x200) - (self.x - x * 0x10 * 0x200) / 2 - 0x800
|
||||
&& (self.y + self.hit_bounds.bottom as isize) > (y * 0x10 - 8) * 0x200 {
|
||||
self.y = (y * 0x10 * 0x200) - ((self.x - x * 0x10 * 0x200) / 2) - 0x800 + self.hit_bounds.top as isize;
|
||||
|
||||
if !self.cond.hidden() && self.vel_y < -0x200 {
|
||||
self.flags.set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y < 0 {
|
||||
self.vel_y = 0;
|
||||
}
|
||||
|
||||
self.flags.set_hit_top_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// upper right slope (smaller half)
|
||||
fn judge_hit_triangle_c(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
if self.x < (x * 0x10 + 8) * 0x200
|
||||
&& self.x > (x * 0x10 - 8) * 0x200
|
||||
&& (self.y - self.hit_bounds.top as isize) < (y * 0x10 * 0x200) + (self.x - x * 0x10 * 0x200) / 2 - 0x800
|
||||
&& (self.y + self.hit_bounds.bottom as isize) > (y * 0x10 - 8) * 0x200 {
|
||||
self.y = (y * 0x10 * 0x200) + ((self.x - x * 0x10 * 0x200) / 2) - 0x800 + self.hit_bounds.top as isize;
|
||||
|
||||
if !self.cond.hidden() && self.vel_y < -0x200 {
|
||||
self.flags.set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y < 0 {
|
||||
self.vel_y = 0;
|
||||
}
|
||||
|
||||
self.flags.set_hit_top_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// upper right slope (bigger half)
|
||||
fn judge_hit_triangle_d(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
if (self.x < (x * 0x10 + 8) * 0x200)
|
||||
&& (self.x > (x * 0x10 - 8) * 0x200)
|
||||
&& (self.y - self.hit_bounds.top as isize) < (y * 0x10 * 0x200) + (self.x - x * 0x10 * 0x200) / 2 + 0x800
|
||||
&& (self.y + self.hit_bounds.bottom as isize) > (y * 0x10 - 8) * 0x200 {
|
||||
self.y = (y * 0x10 * 0x200) + ((self.x - x * 0x10 * 0x200) / 2) + 0x800 + self.hit_bounds.top as isize;
|
||||
|
||||
if !self.cond.hidden() && self.vel_y < -0x200 {
|
||||
self.flags.set_head_bounced(true);
|
||||
}
|
||||
|
||||
if self.vel_y < 0 {
|
||||
self.vel_y = 0;
|
||||
}
|
||||
|
||||
self.flags.set_hit_top_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// lower left half (bigger)
|
||||
fn judge_hit_triangle_e(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
self.flags.set_hit_left_bigger_half(true);
|
||||
|
||||
if (self.x < (x * 0x10 + 8) * 0x200)
|
||||
&& (self.x > (x * 0x10 - 8) * 0x200)
|
||||
&& (self.y + self.hit_bounds.bottom as isize) > (y * 0x10 * 0x200) + (self.x - x * 0x10 * 0x200) / 2 - 0x800
|
||||
&& (self.y - self.hit_bounds.top as isize) < (y * 0x10 + 8) * 0x200 {
|
||||
self.y = (y * 0x10 * 0x200) + ((self.x - x * 0x10 * 0x200) / 2) - 0x800 - self.hit_bounds.bottom as isize;
|
||||
|
||||
if self.vel_y > 0x400 {
|
||||
// PlaySoundObject(23, SOUND_MODE_PLAY); todo
|
||||
}
|
||||
|
||||
if self.vel_y > 0 {
|
||||
self.vel_y = 0;
|
||||
}
|
||||
|
||||
self.flags.set_hit_left_slope(true);
|
||||
self.flags.set_hit_bottom_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// lower left half (smaller)
|
||||
fn judge_hit_triangle_f(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
self.flags.set_hit_left_smaller_half(true);
|
||||
|
||||
if (self.x < (x * 0x10 + 8) * 0x200)
|
||||
&& (self.x > (x * 0x10 - 8) * 0x200)
|
||||
&& (self.y + self.hit_bounds.bottom as isize) > (y * 0x10 * 0x200) + (self.x - x * 0x10 * 0x200) / 2 + 0x800
|
||||
&& (self.y - self.hit_bounds.top as isize) < (y * 0x10 + 8) * 0x200 {
|
||||
self.y = (y * 0x10 * 0x200) + ((self.x - x * 0x10 * 0x200) / 2) + 0x800 - self.hit_bounds.bottom as isize;
|
||||
|
||||
if self.vel_y > 0x400 {
|
||||
// PlaySoundObject(23, SOUND_MODE_PLAY); todo
|
||||
}
|
||||
|
||||
if self.vel_y > 0 {
|
||||
self.vel_y = 0;
|
||||
}
|
||||
|
||||
self.flags.set_hit_left_slope(true);
|
||||
self.flags.set_hit_bottom_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// lower right half (smaller)
|
||||
fn judge_hit_triangle_g(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
self.flags.set_hit_right_smaller_half(true);
|
||||
|
||||
if (self.x < (x * 0x10 + 8) * 0x200)
|
||||
&& (self.x > (x * 0x10 - 8) * 0x200)
|
||||
&& (self.y + self.hit_bounds.bottom as isize) > (y * 0x10 * 0x200) - (self.x - x * 0x10 * 0x200) / 2 + 0x800
|
||||
&& (self.y - self.hit_bounds.top as isize) < (y * 0x10 + 8) * 0x200 {
|
||||
self.y = (y * 0x10 * 0x200) - ((self.x - x * 0x10 * 0x200) / 2) + 0x800 - self.hit_bounds.bottom as isize;
|
||||
|
||||
if self.vel_y > 0x400 {
|
||||
// PlaySoundObject(23, SOUND_MODE_PLAY); todo
|
||||
}
|
||||
|
||||
if self.vel_y > 0 {
|
||||
self.vel_y = 0;
|
||||
}
|
||||
|
||||
self.flags.set_hit_right_slope(true);
|
||||
self.flags.set_hit_bottom_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
// lower right half (bigger)
|
||||
fn judge_hit_triangle_h(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
self.flags.set_hit_right_bigger_half(true);
|
||||
|
||||
if (self.x < (x * 0x10 + 8) * 0x200)
|
||||
&& (self.x > (x * 0x10 - 8) * 0x200)
|
||||
&& (self.y + self.hit_bounds.bottom as isize) > (y * 0x10 * 0x200) - (self.x - x * 0x10 * 0x200) / 2 - 0x800
|
||||
&& (self.y - self.hit_bounds.top as isize) < (y * 0x10 + 8) * 0x200 {
|
||||
self.y = (y * 0x10 * 0x200) - ((self.x - x * 0x10 * 0x200) / 2) - 0x800 - self.hit_bounds.bottom as isize;
|
||||
|
||||
if self.vel_y > 0x400 {
|
||||
// PlaySoundObject(23, SOUND_MODE_PLAY); todo
|
||||
}
|
||||
|
||||
if self.vel_y > 0 {
|
||||
self.vel_y = 0;
|
||||
}
|
||||
|
||||
self.flags.set_hit_right_slope(true);
|
||||
self.flags.set_hit_bottom_wall(true);
|
||||
}
|
||||
}
|
||||
|
||||
fn judge_hit_water(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
if (self.x - self.hit_bounds.right as isize) < (x * 0x10 + 5) * 0x200
|
||||
&& (self.x + self.hit_bounds.right as isize) > (x * 0x10 - 5) * 0x200
|
||||
&& (self.y - self.hit_bounds.top as isize) < (y * 0x10 + 5) * 0x200
|
||||
&& (self.y + self.hit_bounds.bottom as isize) > y * 0x10 * 0x200 {
|
||||
self.flags.set_in_water(true);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick_map_collisions(&mut self, state: &SharedGameState, stage: &Stage) {
|
||||
let x = clamp(self.x / 0x10 / 0x200, 0, stage.map.width as isize);
|
||||
let y = clamp(self.y / 0x10 / 0x200, 0, stage.map.height as isize);
|
||||
|
||||
for (ox, oy) in OFF_X.iter().zip(OFF_Y) {
|
||||
let attrib = stage.map.get_attribute((x + *ox) as usize, (y + *oy) as usize);
|
||||
match attrib {
|
||||
// Block
|
||||
0x02 | 0x60 => {
|
||||
self.judge_hit_water(state, x + *ox, y + *oy);
|
||||
}
|
||||
0x05 | 0x41 | 0x43 | 0x46 => {
|
||||
self.judge_hit_block(state, x + *ox, y + *oy);
|
||||
}
|
||||
0x50 | 0x70 => {
|
||||
self.judge_hit_triangle_a(state, x + *ox, y + *oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(state, x + *ox, y + *oy); }
|
||||
}
|
||||
0x51 | 0x71 => {
|
||||
self.judge_hit_triangle_b(state, x + *ox, y + *oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(state, x + *ox, y + *oy); }
|
||||
}
|
||||
0x52 | 0x72 => {
|
||||
self.judge_hit_triangle_c(state, x + *ox, y + *oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(state, x + *ox, y + *oy); }
|
||||
}
|
||||
0x53 | 0x73 => {
|
||||
self.judge_hit_triangle_d(state, x + *ox, y + *oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(state, x + *ox, y + *oy); }
|
||||
}
|
||||
0x54 | 0x74 => {
|
||||
self.judge_hit_triangle_e(state, x + *ox, y + *oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(state, x + *ox, y + *oy); }
|
||||
}
|
||||
0x55 | 0x75 => {
|
||||
self.judge_hit_triangle_f(state, x + *ox, y + *oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(state, x + *ox, y + *oy); }
|
||||
}
|
||||
0x56 | 0x76 => {
|
||||
self.judge_hit_triangle_g(state, x + *ox, y + *oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(state, x + *ox, y + *oy); }
|
||||
}
|
||||
0x57 | 0x77 => {
|
||||
self.judge_hit_triangle_h(state, x + *ox, y + *oy);
|
||||
if attrib & 0x20 != 0 { self.judge_hit_water(state, x + *ox, y + *oy); }
|
||||
}
|
||||
0x61 => {
|
||||
self.judge_hit_water(state, x + *ox, y + *oy);
|
||||
self.judge_hit_block(state, x + *ox, y + *oy);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn judge_hit_npc_non_solid(&mut self, npc: &NPC) -> Flag {
|
||||
let mut flags = Flag(0);
|
||||
let hit_left = if npc.direction == Direction::Left { npc.hit_bounds.left } else { npc.hit_bounds.right } as isize;
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::ggez::{Context, GameResult, graphics, timer};
|
|||
use crate::ggez::graphics::{Color, Drawable, DrawParam, Text, TextFragment};
|
||||
use crate::ggez::nalgebra::clamp;
|
||||
use crate::npc::NPCMap;
|
||||
use crate::physics::PhysicalEntity;
|
||||
use crate::player::Player;
|
||||
use crate::scene::Scene;
|
||||
use crate::SharedGameState;
|
||||
|
@ -468,12 +469,17 @@ impl Scene for GameScene {
|
|||
|
||||
self.player.flags.0 = 0;
|
||||
state.tick_carets();
|
||||
|
||||
self.player.tick_map_collisions(state, &self.stage);
|
||||
self.player.tick_npc_collisions(state, &mut self.npc_map);
|
||||
|
||||
for npc_id in self.npc_map.npc_ids.iter() {
|
||||
if let Some(npc) = self.npc_map.npcs.get_mut(npc_id) {
|
||||
npc.tick(state, &mut self.player)?;
|
||||
|
||||
if npc.cond.alive() && !npc.npc_flags.ignore_solidity() {
|
||||
npc.tick_map_collisions(state, &self.stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -519,6 +519,8 @@ impl TextScriptVM {
|
|||
state.textscript_vm.flags.set_render(false);
|
||||
state.textscript_vm.flags.set_background_visible(false);
|
||||
|
||||
game_scene.player.update_target = true;
|
||||
|
||||
exec_state = TextScriptExecutionState::Ended;
|
||||
}
|
||||
OpCode::PRI => {
|
||||
|
@ -553,6 +555,24 @@ impl TextScriptVM {
|
|||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
OpCode::MYB => {
|
||||
let new_direction = read_cur_varint(&mut cursor)? as usize;
|
||||
|
||||
game_scene.player.vel_y = -0x200;
|
||||
|
||||
if let Some(direction) = Direction::from_int(new_direction) {
|
||||
match direction {
|
||||
Direction::Left => { game_scene.player.vel_x = 0x200 }
|
||||
Direction::Up => { game_scene.player.vel_y = 0x200 }
|
||||
Direction::Right => { game_scene.player.vel_x = -0x200 }
|
||||
Direction::Bottom => { game_scene.player.vel_y = -0x200 }
|
||||
}
|
||||
} else {
|
||||
// todo npc direction dependent bump
|
||||
}
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
OpCode::SMC => {
|
||||
game_scene.player.cond.set_hidden(false);
|
||||
|
||||
|
@ -864,7 +884,7 @@ impl TextScriptVM {
|
|||
}
|
||||
// One operand codes
|
||||
OpCode::BOA | OpCode::BSL | OpCode::FOB | OpCode::UNI |
|
||||
OpCode::MYB | OpCode::NUM | OpCode::DNA |
|
||||
OpCode::NUM | OpCode::DNA |
|
||||
OpCode::MPp | OpCode::SKm | OpCode::SKp | OpCode::EQp | OpCode::EQm |
|
||||
OpCode::ITp | OpCode::ITm | OpCode::AMm | OpCode::UNJ | OpCode::MPJ |
|
||||
OpCode::XX1 | OpCode::SIL | OpCode::LIp | OpCode::SOU |
|
||||
|
|
Loading…
Reference in a new issue