doukutsu-rs/src/npc/mod.rs

732 lines
26 KiB
Rust
Raw Normal View History

2020-09-04 23:08:33 +00:00
use std::io;
use std::io::Cursor;
2020-08-18 16:46:07 +00:00
use byteorder::{ReadBytesExt, LE};
use num_traits::abs;
2020-08-18 16:46:07 +00:00
use crate::bitfield;
2020-09-04 23:08:33 +00:00
use crate::common::Direction;
use crate::common::Flag;
use crate::common::{interpolate_fix9_scale, Condition, Rect};
use crate::components::flash::Flash;
use crate::components::number_popup::NumberPopup;
2020-09-04 23:08:33 +00:00
use crate::entity::GameEntity;
use crate::frame::Frame;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::npc::list::NPCList;
use crate::physics::PhysicalEntity;
2020-09-05 02:09:52 +00:00
use crate::player::Player;
use crate::rng::Xoroshiro32PlusPlus;
use crate::shared_game_state::SharedGameState;
2020-09-23 13:10:42 +00:00
use crate::stage::Stage;
2020-09-20 23:05:02 +00:00
use crate::str;
use crate::weapon::bullet::BulletManager;
2020-08-18 16:46:07 +00:00
pub mod ai;
2020-10-02 22:11:09 +00:00
pub mod boss;
pub mod list;
pub mod utils;
2020-08-18 16:46:07 +00:00
bitfield! {
#[derive(Clone, Copy)]
2020-09-04 23:08:33 +00:00
pub struct NPCFlag(u16);
2020-08-18 16:46:07 +00:00
impl Debug;
2020-09-30 03:11:25 +00:00
pub solid_soft, set_solid_soft: 0; // 0x01
pub ignore_tile_44, set_ignore_tile_44: 1; // 0x02
pub invulnerable, set_invulnerable: 2; // 0x04
pub ignore_solidity, set_ignore_solidity: 3; // 0x08
pub bouncy, set_bouncy: 4; // 0x10
pub shootable, set_shootable: 5; // 0x20
pub solid_hard, set_solid_hard: 6; // 0x40
pub rear_and_top_not_hurt, set_rear_and_top_not_hurt: 7; // 0x80
pub event_when_touched, set_event_when_touched: 8; // 0x100
pub event_when_killed, set_event_when_killed: 9; // 0x200
pub flag_x400, set_flag_x400: 10; // 0x400
pub appear_when_flag_set, set_appear_when_flag_set: 11; // 0x800
pub spawn_facing_right, set_spawn_facing_right: 12; // 0x1000
pub interactable, set_interactable: 13; // 0x2000
pub hide_unless_flag_set, set_hide_unless_flag_set: 14; // 0x4000
pub show_damage, set_show_damage: 15; // 0x8000
2020-08-18 16:46:07 +00:00
}
2020-09-04 23:08:33 +00:00
2021-04-27 22:39:31 +00:00
#[derive(Debug, Copy, Clone, Eq, PartialOrd, PartialEq)]
#[repr(u8)]
pub enum NPCLayer {
Background = 0,
Middleground = 1,
Foreground = 2,
}
/// Represents an NPC object.
#[derive(Debug, Clone)]
#[repr(C)]
2020-09-04 23:08:33 +00:00
pub struct NPC {
pub id: u16,
pub npc_type: u16,
2021-01-01 01:46:01 +00:00
pub x: i32,
pub y: i32,
2020-11-01 19:05:29 +00:00
/// X velocity, affected by physics code
2021-01-01 01:46:01 +00:00
pub vel_x: i32,
2020-11-01 19:05:29 +00:00
/// Y velocity, affected by physics code
2021-01-01 01:46:01 +00:00
pub vel_y: i32,
2020-11-01 19:05:29 +00:00
/// X velocity, unaffected by physics code
2021-01-01 01:46:01 +00:00
pub vel_x2: i32,
2020-11-01 19:05:29 +00:00
/// Y velocity, unaffected by physics code
2021-01-01 01:46:01 +00:00
pub vel_y2: i32,
pub target_x: i32,
pub target_y: i32,
2020-11-01 19:05:29 +00:00
/// Previous X position, used by frame interpolator
2021-01-01 01:46:01 +00:00
pub prev_x: i32,
2020-11-01 19:05:29 +00:00
/// Previous Y position, used by frame interpolator
2021-01-01 01:46:01 +00:00
pub prev_y: i32,
2020-09-12 21:43:27 +00:00
pub exp: u16,
2021-04-27 22:39:31 +00:00
pub layer: NPCLayer,
pub size: u8,
2020-09-06 00:37:42 +00:00
pub shock: u16,
2020-09-04 23:08:33 +00:00
pub life: u16,
2020-09-09 16:25:39 +00:00
pub damage: u16,
2021-01-18 19:31:35 +00:00
pub spritesheet_id: u16,
2020-09-04 23:08:33 +00:00
pub cond: Condition,
pub flags: Flag,
pub npc_flags: NPCFlag,
pub direction: Direction,
2020-11-01 19:05:29 +00:00
/// Raw direction value set by TSC because some NPCs have it set outside 0-4 range,
/// breaking the direction type.
pub tsc_direction: u16,
2020-09-30 03:11:25 +00:00
pub parent_id: u16,
2020-09-04 23:08:33 +00:00
pub action_num: u16,
pub anim_num: u16,
2020-09-05 02:09:52 +00:00
pub flag_num: u16,
2020-09-04 23:08:33 +00:00
pub event_num: u16,
pub action_counter: u16,
2020-09-12 21:43:27 +00:00
pub action_counter2: u16,
pub action_counter3: u16,
2020-09-04 23:08:33 +00:00
pub anim_counter: u16,
pub anim_rect: Rect<u16>,
pub display_bounds: Rect<u32>,
pub hit_bounds: Rect<u32>,
pub rng: Xoroshiro32PlusPlus,
2021-05-05 21:37:11 +00:00
pub popup: NumberPopup,
2020-09-04 23:08:33 +00:00
}
2020-09-13 05:18:24 +00:00
impl NPC {
2020-10-02 22:11:09 +00:00
pub fn empty() -> NPC {
NPC {
id: 0,
npc_type: 0,
x: 0,
y: 0,
vel_x: 0,
vel_y: 0,
2020-11-01 19:05:29 +00:00
vel_x2: 0,
vel_y2: 0,
2020-10-02 22:11:09 +00:00
target_x: 0,
target_y: 0,
prev_x: 0,
prev_y: 0,
2020-10-02 22:11:09 +00:00
exp: 0,
2021-04-27 22:39:31 +00:00
layer: NPCLayer::Middleground,
2020-10-02 22:11:09 +00:00
size: 0,
shock: 0,
life: 0,
damage: 0,
2021-01-18 19:31:35 +00:00
spritesheet_id: 0,
2020-10-02 22:11:09 +00:00
cond: Condition(0),
flags: Flag(0),
npc_flags: NPCFlag(0),
direction: Direction::Left,
2020-11-01 19:05:29 +00:00
tsc_direction: 0,
2020-10-02 22:11:09 +00:00
display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 },
hit_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 },
parent_id: 0,
action_num: 0,
anim_num: 0,
flag_num: 0,
event_num: 0,
action_counter: 0,
action_counter2: 0,
action_counter3: 0,
2020-10-02 22:11:09 +00:00
anim_counter: 0,
anim_rect: Rect { left: 0, top: 0, right: 0, bottom: 0 },
rng: Xoroshiro32PlusPlus::new(0),
2021-05-05 21:37:11 +00:00
popup: NumberPopup::new(),
2020-10-02 22:11:09 +00:00
}
}
2021-04-27 22:39:31 +00:00
pub fn draw_if_layer(
&self,
state: &mut SharedGameState,
ctx: &mut Context,
frame: &Frame,
layer: NPCLayer,
) -> GameResult {
2021-04-27 22:39:31 +00:00
if self.layer == layer {
self.draw(state, ctx, frame)?
}
Ok(())
}
2020-09-13 05:18:24 +00:00
}
2021-05-02 23:06:02 +00:00
impl GameEntity<([&mut Player; 2], &NPCList, &mut Stage, &BulletManager, &mut Flash)> for NPC {
fn tick(
&mut self,
state: &mut SharedGameState,
(players, npc_list, stage, bullet_manager, flash): (
[&mut Player; 2],
&NPCList,
&mut Stage,
&BulletManager,
&mut Flash,
),
) -> GameResult {
let mut npc_hook_ran = false;
#[cfg(feature = "scripting")]
{ npc_hook_ran = state.lua.try_run_npc_hook(self.id, self.npc_type); }
2020-09-04 23:08:33 +00:00
match self.npc_type {
_ if npc_hook_ran => Ok(()),
2020-10-02 23:53:32 +00:00
0 => self.tick_n000_null(),
2021-06-27 01:06:56 +00:00
1 => self.tick_n001_experience(state, stage),
2020-10-02 23:53:32 +00:00
2 => self.tick_n002_behemoth(state),
3 => self.tick_n003_dead_enemy(),
4 => self.tick_n004_smoke(state),
5 => self.tick_n005_green_critter(state, players),
2020-10-02 23:53:32 +00:00
6 => self.tick_n006_green_beetle(state),
7 => self.tick_n007_basil(state, players),
8 => self.tick_n008_blue_beetle(state, players),
9 => self.tick_n009_balrog_falling_in(state, npc_list),
10 => self.tick_n010_balrog_shooting(state, players, npc_list),
2020-10-02 23:53:32 +00:00
11 => self.tick_n011_balrogs_projectile(state),
12 => self.tick_n012_balrog_cutscene(state, players, npc_list, stage),
2020-10-02 23:53:32 +00:00
13 => self.tick_n013_forcefield(state),
14 => self.tick_n014_key(state),
15 => self.tick_n015_chest_closed(state, npc_list),
2020-10-02 23:53:32 +00:00
16 => self.tick_n016_save_point(state),
17 => self.tick_n017_health_refill(state),
18 => self.tick_n018_door(state, npc_list),
19 => self.tick_n019_balrog_bust_in(state, npc_list),
2020-10-02 23:53:32 +00:00
20 => self.tick_n020_computer(state),
21 => self.tick_n021_chest_open(state),
22 => self.tick_n022_teleporter(state),
2020-11-01 19:05:29 +00:00
23 => self.tick_n023_teleporter_lights(state),
24 => self.tick_n024_power_critter(state, players),
2020-11-01 19:05:29 +00:00
25 => self.tick_n025_lift(state),
26 => self.tick_n026_bat_flying(state, players),
2020-10-02 23:53:32 +00:00
27 => self.tick_n027_death_trap(state),
28 => self.tick_n028_flying_critter(state, players),
29 => self.tick_n029_cthulhu(state, players),
2020-10-02 23:53:32 +00:00
30 => self.tick_n030_gunsmith(state),
31 => self.tick_n031_bat_hanging(state, players),
2020-10-02 23:53:32 +00:00
32 => self.tick_n032_life_capsule(state),
2020-11-26 08:41:28 +00:00
33 => self.tick_n033_balrog_bouncing_projectile(state),
2020-10-02 23:53:32 +00:00
34 => self.tick_n034_bed(state),
35 => self.tick_n035_mannan(state, npc_list),
36 => self.tick_n036_balrog_hover(state, players, npc_list),
2020-10-02 23:53:32 +00:00
37 => self.tick_n037_sign(state),
38 => self.tick_n038_fireplace(state),
39 => self.tick_n039_save_sign(state),
40 => self.tick_n040_santa(state, players),
2020-10-02 23:53:32 +00:00
41 => self.tick_n041_busted_door(state),
42 => self.tick_n042_sue(state, players, npc_list),
2020-10-02 23:53:32 +00:00
43 => self.tick_n043_chalkboard(state),
44 => self.tick_n044_polish(state, npc_list),
45 => self.tick_n045_baby(state),
46 => self.tick_n046_hv_trigger(players),
47 => self.tick_n047_sandcroc(state, players),
2020-12-07 19:20:19 +00:00
48 => self.tick_n048_omega_projectiles(state),
2021-01-08 19:25:34 +00:00
49 => self.tick_n049_skullhead(state, players, npc_list),
50 => self.tick_n050_skeleton_projectile(state),
51 => self.tick_n051_crow_and_skullhead(state, players, npc_list),
2020-10-02 23:53:32 +00:00
52 => self.tick_n052_sitting_blue_robot(state),
2021-02-12 22:48:57 +00:00
53 => self.tick_n053_skullstep_leg(state, npc_list),
54 => self.tick_n054_skullstep(state, npc_list),
2020-10-02 23:53:32 +00:00
55 => self.tick_n055_kazuma(state),
2021-03-23 01:52:27 +00:00
56 => self.tick_n056_tan_beetle(state, players),
57 => self.tick_n057_crow(state, players),
58 => self.tick_n058_basu(state, players, npc_list),
59 => self.tick_n059_eye_door(state, players),
60 => self.tick_n060_toroko(state, players),
2021-05-05 12:04:59 +00:00
61 => self.tick_n061_king(state, npc_list),
2020-10-02 23:53:32 +00:00
62 => self.tick_n062_kazuma_computer(state),
63 => self.tick_n063_toroko_stick(state),
64 => self.tick_n064_first_cave_critter(state, players),
65 => self.tick_n065_first_cave_bat(state, players),
66 => self.tick_n066_misery_bubble(state, npc_list),
67 => self.tick_n067_misery_floating(state, npc_list),
68 => self.tick_n068_balrog_running(state, players, npc_list),
2020-10-02 23:53:32 +00:00
69 => self.tick_n069_pignon(state),
70 => self.tick_n070_sparkle(state),
71 => self.tick_n071_chinfish(state),
72 => self.tick_n072_sprinkler(state, players, npc_list),
2020-10-02 23:53:32 +00:00
73 => self.tick_n073_water_droplet(state, stage),
74 => self.tick_n074_jack(state),
75 => self.tick_n075_kanpachi(state, players),
2020-10-02 23:53:32 +00:00
76 => self.tick_n076_flowers(),
77 => self.tick_n077_yamashita(state),
78 => self.tick_n078_pot(state),
79 => self.tick_n079_mahin(state, players),
80 => self.tick_n080_gravekeeper(state, players),
81 => self.tick_n081_giant_pignon(state, players),
82 => self.tick_n082_misery_standing(state, npc_list),
2020-10-27 01:05:49 +00:00
83 => self.tick_n083_igor_cutscene(state),
84 => self.tick_n084_basu_projectile(state),
85 => self.tick_n085_terminal(state, players),
2021-06-27 01:06:56 +00:00
86 => self.tick_n086_missile_pickup(state, stage),
87 => self.tick_n087_heart_pickup(state, stage),
88 => self.tick_n088_igor_boss(state, players, npc_list),
89 => self.tick_n089_igor_dead(state, players, npc_list),
2021-01-18 19:31:35 +00:00
90 => self.tick_n090_background(state),
2020-10-27 01:05:49 +00:00
91 => self.tick_n091_mimiga_cage(state),
2020-11-24 23:08:27 +00:00
92 => self.tick_n092_sue_at_pc(state),
93 => self.tick_n093_chaco(state, players),
94 => self.tick_n094_kulala(state, players),
2020-11-04 15:37:00 +00:00
95 => self.tick_n095_jelly(state),
96 => self.tick_n096_fan_left(state, players, npc_list),
97 => self.tick_n097_fan_up(state, players, npc_list),
98 => self.tick_n098_fan_right(state, players, npc_list),
99 => self.tick_n099_fan_down(state, players, npc_list),
2020-11-26 08:41:28 +00:00
100 => self.tick_n100_grate(state),
101 => self.tick_n101_malco_screen(state),
102 => self.tick_n102_malco_computer_wave(state),
103 => self.tick_n103_mannan_projectile(state),
104 => self.tick_n104_frog(state, players),
2020-11-26 08:41:28 +00:00
105 => self.tick_n105_hey_bubble_low(state),
106 => self.tick_n106_hey_bubble_high(state, npc_list),
107 => self.tick_n107_malco_broken(state, npc_list),
108 => self.tick_n108_balfrog_projectile(state),
109 => self.tick_n109_malco_powered_on(state, players, npc_list),
110 => self.tick_n110_puchi(state, players),
111 => self.tick_n111_quote_teleport_out(state, players),
112 => self.tick_n112_quote_teleport_in(state, players),
113 => self.tick_n113_professor_booster(state),
114 => self.tick_n114_press(state, players, npc_list),
2021-03-23 14:05:09 +00:00
115 => self.tick_n115_ravil(state, players, npc_list),
2021-01-18 19:31:35 +00:00
116 => self.tick_n116_red_petals(state),
2021-03-15 21:11:40 +00:00
117 => self.tick_n117_curly(state, players, npc_list),
118 => self.tick_n118_curly_boss(state, players, npc_list, bullet_manager),
2021-01-18 19:31:35 +00:00
119 => self.tick_n119_table_chair(state),
120 => self.tick_n120_colon_a(state),
2021-03-23 01:52:27 +00:00
121 => self.tick_n121_colon_b(state),
2021-05-05 12:04:59 +00:00
122 => self.tick_n122_colon_enraged(state, players),
2021-03-23 01:52:27 +00:00
123 => self.tick_n123_curly_boss_bullet(state),
124 => self.tick_n124_sunstone(state),
125 => self.tick_n125_hidden_item(state, npc_list),
2021-03-23 01:52:27 +00:00
126 => self.tick_n126_puppy_running(state, players),
2021-02-26 20:03:35 +00:00
127 => self.tick_n127_machine_gun_trail_l2(state),
128 => self.tick_n128_machine_gun_trail_l3(state),
2020-10-02 23:53:32 +00:00
129 => self.tick_n129_fireball_snake_trail(state),
2021-05-02 23:06:02 +00:00
130 => self.tick_n130_puppy_sitting(state, players),
2021-01-18 19:31:35 +00:00
131 => self.tick_n131_puppy_sleeping(state),
2021-03-23 01:52:27 +00:00
132 => self.tick_n132_puppy_barking(state, players),
2021-05-02 23:06:02 +00:00
133 => self.tick_n133_jenka(state),
134 => self.tick_n134_armadillo(state, players, bullet_manager),
135 => self.tick_n135_skeleton(state, players, npc_list),
2021-03-23 01:52:27 +00:00
136 => self.tick_n136_puppy_carried(state, players),
2021-01-18 19:31:35 +00:00
137 => self.tick_n137_large_door_frame(state),
2021-05-02 23:06:02 +00:00
138 => self.tick_n138_large_door(state),
139 => self.tick_n139_doctor(state),
140 => self.tick_n140_toroko_frenzied(state, players, npc_list, bullet_manager),
141 => self.tick_n141_toroko_block_projectile(state, players, npc_list),
142 => self.tick_n142_flower_cub(state, players),
2021-01-18 19:31:35 +00:00
143 => self.tick_n143_jenka_collapsed(state),
2021-05-02 23:06:02 +00:00
144 => self.tick_n144_toroko_teleporting_in(state),
145 => self.tick_n145_king_sword(state, npc_list),
146 => self.tick_n146_lighting(state, npc_list, flash),
147 => self.tick_n147_critter_purple(state, players, npc_list),
148 => self.tick_n148_critter_purple_projectile(state),
149 => self.tick_n149_horizontal_moving_block(state, players, npc_list),
150 => self.tick_n150_quote(state, players, npc_list),
151 => self.tick_n151_blue_robot_standing(state),
2021-01-18 19:31:35 +00:00
152 => self.tick_n152_shutter_stuck(),
2021-05-02 23:06:02 +00:00
153 => self.tick_n153_gaudi(state, players),
2020-11-01 19:05:29 +00:00
154 => self.tick_n154_gaudi_dead(state),
2021-05-02 23:06:02 +00:00
155 => self.tick_n155_gaudi_flying(state, players, npc_list),
2020-12-15 22:09:14 +00:00
156 => self.tick_n156_gaudi_projectile(state),
157 => self.tick_n157_vertical_moving_block(state, players, npc_list),
2020-12-15 22:09:14 +00:00
158 => self.tick_n158_fish_missile(state, players),
2021-05-02 23:06:02 +00:00
159 => self.tick_n159_monster_x_defeated(state, npc_list),
160 => self.tick_n160_puu_black(state, players, npc_list),
161 => self.tick_n161_puu_black_projectile(state),
162 => self.tick_n162_puu_black_dead(state, players, npc_list),
163 => self.tick_n163_dr_gero(state),
164 => self.tick_n164_nurse_hasumi(state),
165 => self.tick_n165_curly_collapsed(state, players),
2021-01-18 19:31:35 +00:00
166 => self.tick_n166_chaba(state),
2021-05-05 12:04:59 +00:00
167 => self.tick_n167_booster_falling(state, npc_list),
168 => self.tick_n168_boulder(state),
2021-05-05 20:46:07 +00:00
169 => self.tick_n169_balrog_shooting_missiles(state, players, npc_list),
2021-05-05 12:04:59 +00:00
170 => self.tick_n170_balrog_missile(state, players, npc_list),
2021-05-05 20:46:07 +00:00
171 => self.tick_n171_fire_whirrr(state, players, npc_list),
172 => self.tick_n172_fire_whirrr_projectile(state),
2021-06-16 21:31:47 +00:00
173 => self.tick_n173_gaudi_armored(state, players, npc_list),
174 => self.tick_n174_gaudi_armored_projectile(state),
175 => self.tick_n175_gaudi_egg(state),
176 => self.tick_n176_buyo_buyo_base(state, players, npc_list),
177 => self.tick_n177_buyo_buyo(state, players),
184 => self.tick_n184_shutter(state, npc_list),
185 => self.tick_n185_small_shutter(state),
186 => self.tick_n186_lift_block(state),
187 => self.tick_n187_fuzz_core(state, players, npc_list),
188 => self.tick_n188_fuzz(state, players, npc_list),
2020-11-27 18:19:12 +00:00
192 => self.tick_n192_scooter(state),
193 => self.tick_n193_broken_scooter(state),
194 => self.tick_n194_broken_blue_robot(state),
2021-06-16 21:31:47 +00:00
195 => self.tick_n195_background_grate(state),
199 => self.tick_n199_wind_particles(state),
2021-01-18 19:31:35 +00:00
207 => self.tick_n207_counter_bomb_countdown(state),
2021-01-16 22:49:18 +00:00
208 => self.tick_n208_basu_destroyed_egg_corridor(state, players, npc_list),
209 => self.tick_n209_basu_projectile_destroyed_egg_corridor(state),
2020-10-02 23:53:32 +00:00
211 => self.tick_n211_small_spikes(state),
2021-01-16 22:49:18 +00:00
215 => self.tick_n215_sandcroc_outer_wall(state, players),
2021-01-18 19:31:35 +00:00
216 => self.tick_n216_debug_cat(state),
222 => self.tick_n222_prison_bars(state),
227 => self.tick_n227_bucket(state),
229 => self.tick_n229_red_flowers_sprouts(state),
230 => self.tick_n230_red_flowers_blooming(state),
234 => self.tick_n234_red_flowers_picked(state),
2021-01-18 19:31:35 +00:00
239 => self.tick_n239_cage_bars(state),
2021-01-16 13:50:46 +00:00
241 => self.tick_n241_critter_red(state, players),
2021-01-18 19:31:35 +00:00
249 => self.tick_n249_misery_boss_energy_shot(state),
258 => self.tick_n258_mimiga_sleeping(state),
292 => self.tick_n292_quake(state),
297 => self.tick_n297_sue_dragon_mouth(state, npc_list),
2020-10-30 13:50:10 +00:00
298 => self.tick_n298_intro_doctor(state),
299 => self.tick_n299_intro_balrog_misery(state),
300 => self.tick_n300_intro_demon_crown(state),
2021-01-18 19:31:35 +00:00
302 => self.tick_n302_camera_focus_marker(state, players, npc_list),
328 => self.tick_n328_human_transform_machine(state),
329 => self.tick_n329_laboratory_fan(state),
349 => self.tick_n349_statue(state),
351 => self.tick_n351_statue_shootable(state, npc_list),
352 => self.tick_n352_ending_characters(state, npc_list),
355 => self.tick_n355_quote_and_curly_on_balrog(state, npc_list),
358 => self.tick_n358_misery_credits(state),
2021-01-16 22:49:18 +00:00
359 => self.tick_n359_water_droplet_generator(state, players, npc_list),
_ => {
#[cfg(feature = "hooks")]
{
crate::hooks::run_npc_hook(self, state, players, npc_list, stage, bullet_manager);
}
Ok(())
}
2020-09-13 00:21:12 +00:00
}?;
2021-05-05 21:37:11 +00:00
self.popup.x = self.x;
self.popup.y = self.y;
self.popup.tick(state, ())?;
2020-09-13 00:21:12 +00:00
if self.shock > 0 {
self.shock -= 1;
2020-09-04 23:08:33 +00:00
}
2020-09-13 00:21:12 +00:00
if abs(self.prev_x - self.x) > 0x1000 {
self.prev_x = self.x;
}
if abs(self.prev_y - self.y) > 0x1000 {
self.prev_y = self.y;
}
2020-09-13 00:21:12 +00:00
Ok(())
2020-09-04 23:08:33 +00:00
}
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, frame: &Frame) -> GameResult {
if !self.cond.alive() || self.cond.hidden() {
return Ok(());
}
2021-01-18 19:31:35 +00:00
let texture = state.npc_table.get_texture_name(self.spritesheet_id);
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, texture)?;
2020-09-04 23:08:33 +00:00
let off_x =
if self.direction == Direction::Left { self.display_bounds.left } else { self.display_bounds.right } as i32;
let shock = if self.shock > 0 { (2 * ((self.shock as i32 / 2) % 2) - 1) as f32 } else { 0.0 };
2020-09-13 00:21:12 +00:00
2021-05-05 18:36:21 +00:00
let (frame_x, frame_y) = frame.xy_interpolated(state.frame_time);
2020-09-04 23:08:33 +00:00
batch.add_rect(
interpolate_fix9_scale(self.prev_x - off_x, self.x - off_x, state.frame_time) + shock - frame_x,
interpolate_fix9_scale(
self.prev_y - self.display_bounds.top as i32,
self.y - self.display_bounds.top as i32,
state.frame_time,
) - frame_y,
2020-09-04 23:08:33 +00:00
&self.anim_rect,
);
batch.draw(ctx)?;
2021-05-05 21:37:11 +00:00
self.popup.draw(state, ctx, frame)?;
2020-09-04 23:08:33 +00:00
Ok(())
}
}
impl PhysicalEntity for NPC {
#[inline(always)]
fn x(&self) -> i32 {
self.x
}
#[inline(always)]
fn y(&self) -> i32 {
self.y
}
#[inline(always)]
fn vel_x(&self) -> i32 {
self.vel_x
}
#[inline(always)]
fn vel_y(&self) -> i32 {
self.vel_y
}
#[inline(always)]
fn hit_rect_size(&self) -> usize {
if self.size >= 3 {
if self.cond.drs_boss() {
4
} else {
3
}
} else {
2
}
}
2020-10-01 03:06:18 +00:00
#[inline(always)]
fn offset_x(&self) -> i32 {
if self.size >= 3 && !self.cond.drs_boss() {
-0x1000
} else {
0
}
}
2020-10-01 03:06:18 +00:00
#[inline(always)]
fn offset_y(&self) -> i32 {
if self.size >= 3 && !self.cond.drs_boss() {
-0x1000
} else {
0
}
}
#[inline(always)]
fn hit_bounds(&self) -> &Rect<u32> {
&self.hit_bounds
}
2021-06-27 01:06:56 +00:00
#[inline(always)]
fn display_bounds(&self) -> &Rect<u32> {
&self.display_bounds
}
#[inline(always)]
2021-01-01 01:46:01 +00:00
fn set_x(&mut self, x: i32) {
self.x = x;
}
#[inline(always)]
2021-01-01 01:46:01 +00:00
fn set_y(&mut self, y: i32) {
self.y = y;
}
#[inline(always)]
2021-01-01 01:46:01 +00:00
fn set_vel_x(&mut self, vel_x: i32) {
self.vel_x = vel_x;
}
#[inline(always)]
2021-01-01 01:46:01 +00:00
fn set_vel_y(&mut self, vel_y: i32) {
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
}
2020-09-12 21:43:27 +00:00
#[inline(always)]
fn direction(&self) -> Direction {
self.direction
}
#[inline(always)]
fn is_player(&self) -> bool {
false
}
#[inline(always)]
fn ignore_tile_44(&self) -> bool {
self.npc_flags.ignore_tile_44()
}
}
2020-09-04 23:08:33 +00:00
pub struct NPCTableEntry {
2020-09-06 00:37:42 +00:00
pub npc_flags: NPCFlag,
pub life: u16,
pub spritesheet_id: u8,
pub death_sound: u8,
pub hurt_sound: u8,
pub size: u8,
2020-09-06 00:37:42 +00:00
pub experience: u32,
pub damage: u32,
pub display_bounds: Rect<u8>,
pub hit_bounds: Rect<u8>,
2020-09-04 23:08:33 +00:00
}
pub struct NPCTable {
entries: Vec<NPCTableEntry>,
2020-09-23 13:10:42 +00:00
pub tileset_name: String,
2020-09-04 23:08:33 +00:00
pub tex_npc1_name: String,
pub tex_npc2_name: String,
}
impl NPCTable {
#[allow(clippy::new_without_default)]
2020-09-04 23:08:33 +00:00
pub fn new() -> NPCTable {
NPCTable {
entries: Vec::new(),
2020-09-23 13:10:42 +00:00
tileset_name: str!("Stage/Prt0"),
2020-09-04 23:08:33 +00:00
tex_npc1_name: str!("Npc/Npc0"),
tex_npc2_name: str!("Npc/Npc0"),
}
}
pub fn load_from<R: io::Read>(mut data: R) -> GameResult<NPCTable> {
let mut table = NPCTable::new();
let mut buf = Vec::new();
data.read_to_end(&mut buf)?;
let count = buf.len() / 0x18;
let mut f = Cursor::new(buf);
for _ in 0..count {
table.entries.push(NPCTableEntry {
npc_flags: NPCFlag(0),
life: 0,
spritesheet_id: 0,
death_sound: 0,
hurt_sound: 0,
size: 0,
2020-09-04 23:08:33 +00:00
experience: 0,
damage: 0,
display_bounds: Rect::new(0, 0, 0, 0),
hit_bounds: Rect::new(0, 0, 0, 0),
});
}
for npc in table.entries.iter_mut() {
npc.npc_flags.0 = f.read_u16::<LE>()?;
}
for npc in table.entries.iter_mut() {
npc.life = f.read_u16::<LE>()?;
}
for npc in table.entries.iter_mut() {
npc.spritesheet_id = f.read_u8()?;
}
for npc in table.entries.iter_mut() {
2020-10-27 01:05:49 +00:00
npc.death_sound = f.read_u8()?;
2020-09-04 23:08:33 +00:00
}
for npc in table.entries.iter_mut() {
2020-10-27 01:05:49 +00:00
npc.hurt_sound = f.read_u8()?;
2020-09-04 23:08:33 +00:00
}
for npc in table.entries.iter_mut() {
npc.size = f.read_u8()?;
2020-09-04 23:08:33 +00:00
}
for npc in table.entries.iter_mut() {
npc.experience = f.read_u32::<LE>()?;
}
for npc in table.entries.iter_mut() {
npc.damage = f.read_u32::<LE>()?;
}
for npc in table.entries.iter_mut() {
npc.hit_bounds.left = f.read_u8()?;
npc.hit_bounds.top = f.read_u8()?;
npc.hit_bounds.right = f.read_u8()?;
npc.hit_bounds.bottom = f.read_u8()?;
}
for npc in table.entries.iter_mut() {
npc.display_bounds.left = f.read_u8()?;
npc.display_bounds.top = f.read_u8()?;
npc.display_bounds.right = f.read_u8()?;
npc.display_bounds.bottom = f.read_u8()?;
}
Ok(table)
}
2020-09-06 00:37:42 +00:00
pub fn get_entry(&self, npc_type: u16) -> Option<&NPCTableEntry> {
self.entries.get(npc_type as usize)
}
pub fn get_display_bounds(&self, npc_type: u16) -> Rect<u32> {
2020-09-04 23:08:33 +00:00
if let Some(npc) = self.entries.get(npc_type as usize) {
Rect {
left: npc.display_bounds.left as u32 * 0x200,
top: npc.display_bounds.top as u32 * 0x200,
right: npc.display_bounds.right as u32 * 0x200,
bottom: npc.display_bounds.bottom as u32 * 0x200,
2020-09-04 23:08:33 +00:00
}
} else {
Rect { left: 0, top: 0, right: 0, bottom: 0 }
}
}
pub fn get_hit_bounds(&self, npc_type: u16) -> Rect<u32> {
2020-09-04 23:08:33 +00:00
if let Some(npc) = self.entries.get(npc_type as usize) {
Rect {
left: npc.hit_bounds.left as u32 * 0x200,
top: npc.hit_bounds.top as u32 * 0x200,
right: npc.hit_bounds.right as u32 * 0x200,
bottom: npc.hit_bounds.bottom as u32 * 0x200,
2020-09-04 23:08:33 +00:00
}
} else {
Rect { left: 0, top: 0, right: 0, bottom: 0 }
}
}
2021-01-18 19:31:35 +00:00
pub fn get_texture_name(&self, spritesheet_id: u16) -> &str {
match spritesheet_id {
0 => "Title",
2 => &self.tileset_name,
6 => "Fade",
8 => "ItemImage",
11 => "Arms",
12 => "ArmsImage",
14 => "StageImage",
15 => "Loading",
16 => "MyChar",
17 => "Bullet",
19 => "Caret",
20 => "Npc/NpcSym",
21 => &self.tex_npc1_name,
22 => &self.tex_npc2_name,
23 => "Npc/NpcRegu",
26 => "TextBox",
27 => "Face",
_ => "Npc/Npc0",
2020-09-04 23:08:33 +00:00
}
}
}