1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2024-11-22 21:52:46 +00:00

fix npc leaks, new mimiga village npcs

This commit is contained in:
Alula 2020-09-23 15:10:42 +02:00
parent 49461d66fa
commit ed94343da3
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
8 changed files with 246 additions and 20 deletions

View file

@ -183,6 +183,8 @@ pub struct NPCConsts {
pub n065_first_cave_bat: [Rect<usize>; 8],
pub n066_misery_bubble: [Rect<usize>; 4],
pub n067_misery_floating: [Rect<usize>; 16],
pub n068_balrog: [Rect<usize>; 18],
pub n069_pignon: [Rect<usize>; 12],
pub n070_sparkle: [Rect<usize>; 4],
pub n071_chinfish: [Rect<usize>; 6],
pub n072_sprinkler: [Rect<usize>; 2],
@ -222,7 +224,7 @@ pub struct TitleConsts {
pub menu_bottom: Rect<usize>,
pub menu_middle: Rect<usize>,
pub menu_left: Rect<usize>,
pub menu_right: Rect<usize>
pub menu_right: Rect<usize>,
}
#[derive(Debug)]
@ -791,8 +793,6 @@ impl EngineConstants {
Rect { left: 160, top: 0, right: 176, bottom: 16 },
Rect { left: 176, top: 0, right: 192, bottom: 16 },
Rect { left: 144, top: 0, right: 160, bottom: 16 },
Rect { left: 80, top: 16, right: 96, bottom: 32 }, // right
Rect { left: 96, top: 16, right: 112, bottom: 32 },
Rect { left: 112, top: 16, right: 128, bottom: 32 },
@ -802,6 +802,40 @@ impl EngineConstants {
Rect { left: 176, top: 16, right: 192, bottom: 32 },
Rect { left: 144, top: 16, right: 160, bottom: 32 },
],
n068_balrog: [
Rect { left: 0, top: 0, right: 40, bottom: 24 }, // left
Rect { left: 0, top: 48, right: 40, bottom: 72 },
Rect { left: 0, top: 0, right: 40, bottom: 24 },
Rect { left: 40, top: 48, right: 80, bottom: 72 },
Rect { left: 0, top: 0, right: 40, bottom: 24 },
Rect { left: 80, top: 48, right: 120, bottom: 72 },
Rect { left: 120, top: 48, right: 160, bottom: 72 },
Rect { left: 120, top: 0, right: 160, bottom: 24 },
Rect { left: 80, top: 0, right: 120, bottom: 24 },
Rect { left: 0, top: 24, right: 40, bottom: 48 }, // right
Rect { left: 0, top: 72, right: 40, bottom: 96 },
Rect { left: 0, top: 24, right: 40, bottom: 48 },
Rect { left: 40, top: 72, right: 80, bottom: 96 },
Rect { left: 0, top: 24, right: 40, bottom: 48 },
Rect { left: 80, top: 72, right: 120, bottom: 96 },
Rect { left: 120, top: 72, right: 160, bottom: 96 },
Rect { left: 120, top: 24, right: 160, bottom: 48 },
Rect { left: 80, top: 24, right: 120, bottom: 48 },
],
n069_pignon: [
Rect { left: 48, top: 0, right: 64, bottom: 16 }, // left
Rect { left: 64, top: 0, right: 80, bottom: 16 },
Rect { left: 80, top: 0, right: 96, bottom: 16 },
Rect { left: 96, top: 0, right: 112, bottom: 16 },
Rect { left: 48, top: 0, right: 64, bottom: 16 },
Rect { left: 112, top: 0, right: 128, bottom: 16 },
Rect { left: 48, top: 16, right: 64, bottom: 32 }, // right
Rect { left: 64, top: 16, right: 80, bottom: 32 },
Rect { left: 80, top: 16, right: 96, bottom: 32 },
Rect { left: 96, top: 16, right: 112, bottom: 32 },
Rect { left: 48, top: 16, right: 64, bottom: 32 },
Rect { left: 112, top: 16, right: 128, bottom: 32 },
],
n070_sparkle: [
Rect { left: 96, top: 48, right: 112, bottom: 64 },
Rect { left: 112, top: 48, right: 128, bottom: 64 },
@ -1093,8 +1127,6 @@ impl EngineConstants {
"Resource/BITMAP/Credit17" => (160, 240), // cse2
"Resource/BITMAP/Credit18" => (160, 240), // cse2
"Resource/BITMAP/pixel" => (160, 16), // cse2
"Resource/CURSOR/CURSOR_IKA" => (32, 32), // cse2
"Resource/CURSOR/CURSOR_NORMAL" => (32, 32), // cse2
"StageImage" => (256, 16),
"Stage/Prt0" => (32, 32),
"Stage/PrtAlmond" => (256, 96),

View file

@ -141,8 +141,8 @@ impl Inventory {
weapon.experience = 0;
tmp_exp = 0;
} else if weapon.experience < exp {
let max_level = lvl_table[weapon.level as usize - 1] as isize;
weapon.level = weapon.level.prev();
let max_level = lvl_table[weapon.level as usize - 1] as isize;
weapon.experience = (max_level + tmp_exp.max(-max_level)) as u16;
tmp_exp += max_level;

View file

@ -59,6 +59,12 @@ impl LiveDebugger {
game_scene.player.vel_y as f32 / 512.0,
));
ui.text(format!(
"NPC Count: {}/{}",
game_scene.npc_map.npcs.len(),
game_scene.npc_map.npc_ids.len(),
));
ui.text(format!(
"Booster fuel: ({})", game_scene.player.booster_fuel
));

View file

@ -9,6 +9,113 @@ use crate::player::Player;
use crate::shared_game_state::SharedGameState;
impl NPC {
pub(crate) fn tick_n069_pignon(&mut self, state: &mut SharedGameState) -> GameResult {
let dir_offset = if self.direction == Direction::Left { 0 } else { 6 };
match self.action_num {
0 | 1 => {
if self.action_num == 0 {
self.action_num = 1;
self.anim_num = 0;
self.anim_counter = 0;
self.vel_x = 0;
self.anim_rect = state.constants.npc.n069_pignon[self.anim_num as usize + dir_offset];
}
if state.game_rng.range(0..100) == 1 {
self.action_num = 2;
self.action_counter = 0;
self.anim_num = 1;
self.anim_rect = state.constants.npc.n069_pignon[self.anim_num as usize + dir_offset];
}
if state.game_rng.range(0..150) == 1 {
self.action_num = 3;
self.action_counter = 50;
self.anim_num = 0;
self.anim_rect = state.constants.npc.n069_pignon[self.anim_num as usize + dir_offset];
}
}
2 => {
self.action_counter += 1;
if self.action_counter > 8 {
self.action_num = 1;
self.anim_num = 0;
self.anim_rect = state.constants.npc.n069_pignon[self.anim_num as usize + dir_offset];
}
}
3 | 4 => {
if self.action_num == 3 {
self.action_num = 4;
self.anim_num = 2;
self.anim_counter = 0;
self.anim_rect = state.constants.npc.n069_pignon[self.anim_num as usize + dir_offset];
}
if self.action_counter > 0 {
self.action_counter -= 1;
} else {
self.action_num = 0;
}
self.anim_counter += 1;
if self.anim_counter > 2 {
self.anim_counter = 0;
self.anim_num += 1;
if self.anim_num > 4 {
self.anim_num = 2;
}
self.anim_rect = state.constants.npc.n069_pignon[self.anim_num as usize + dir_offset];
}
if self.flags.hit_left_wall() {
self.direction = Direction::Right;
}
if self.flags.hit_right_wall() {
self.direction = Direction::Left;
}
self.vel_x = match self.direction {
Direction::Left => { -0x100 } // -0.5fix9
Direction::Right => { 0x100 } // 0.5fix9
_ => { 0 }
};
}
5 => {
if self.flags.hit_bottom_wall() {
self.action_num = 0;
}
}
_ => {}
}
if self.shock > 0 && [1,2,4].contains(&self.action_num) {
self.vel_y = -0x200;
self.anim_num = 5;
self.action_num = 5;
self.anim_rect = state.constants.npc.n069_pignon[self.anim_num as usize + dir_offset];
}
self.vel_y += 0x40;
if self.vel_y > 0x5ff {
self.vel_y = 0x5ff;
}
self.x += self.vel_x;
self.y += self.vel_y;
Ok(())
}
pub(crate) fn tick_n071_chinfish(&mut self, state: &mut SharedGameState) -> GameResult {
if self.action_num == 0 {
self.action_num = 1;

View file

@ -4,6 +4,8 @@ use crate::ggez::GameResult;
use crate::npc::{NPC, NPCMap};
use crate::player::Player;
use crate::shared_game_state::SharedGameState;
use crate::stage::Stage;
use num_traits::real::Real;
impl NPC {
pub(crate) fn tick_n000_null(&mut self) -> GameResult {
@ -475,13 +477,75 @@ impl NPC {
}
pub(crate) fn tick_n072_sprinkler(&mut self, state: &mut SharedGameState) -> GameResult {
pub(crate) fn tick_n072_sprinkler(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult {
if self.direction == Direction::Left {
self.anim_counter = (self.anim_counter + 1) % 4;
self.anim_num = self.anim_counter / 2;
self.anim_rect = state.constants.npc.n072_sprinkler[self.anim_num as usize];
// todo: spawn water droplets
if self.anim_num % 2 == 0 && (player.x - self.x).abs() < 480 * 0x200 {
self.action_counter = self.action_counter.wrapping_add(1);
let mut droplet = NPCMap::create_npc(73, &state.npc_table);
droplet.cond.set_alive(true);
droplet.direction = Direction::Left;
droplet.x = self.x;
droplet.y = self.y;
droplet.vel_x = 2 * state.game_rng.range(-0x200..0x200) as isize;
droplet.vel_y = 3 * state.game_rng.range(-0x200..0x80) as isize;
state.new_npcs.push(droplet);
if self.action_counter % 2 == 0 {
droplet.vel_x = 2 * state.game_rng.range(-0x200..0x200) as isize;
droplet.vel_y = 3 * state.game_rng.range(-0x200..0x80) as isize;
state.new_npcs.push(droplet);
}
}
}
Ok(())
}
pub(crate) fn tick_n073_water_droplet(&mut self, state: &mut SharedGameState, stage: &Stage) -> GameResult {
self.vel_y += 0x20;
self.anim_rect = state.constants.npc.n073_water_droplet[state.game_rng.range(0..4) as usize];
if self.vel_y > 0x5ff {
self.vel_y = 0x5ff;
}
self.x += self.vel_x;
self.y += self.vel_y;
if self.direction == Direction::Right {
self.anim_rect.top += 2;
self.anim_rect.bottom += 2;
}
self.action_counter += 1;
if self.action_counter > 10 && (self.flags.hit_left_wall() || self.flags.hit_right_wall()
|| self.flags.hit_bottom_wall() || self.flags.in_water()) {
// hit something
self.cond.set_alive(false);
}
if self.y > stage.map.height as isize * 16 * 0x200 {
// out of map
self.cond.set_alive(false);
}
Ok(())
}
pub(crate) fn tick_n076_flowers(&mut self) -> GameResult {
if self.action_num == 0 {
self.action_num = 1;
self.anim_rect.left = self.event_num as usize * 16;
self.anim_rect.top = 0;
self.anim_rect.right = self.anim_rect.left + 16;
self.anim_rect.bottom = self.anim_rect.top + 16;
}
Ok(())

View file

@ -19,6 +19,7 @@ use crate::map::NPCData;
use crate::physics::PhysicalEntity;
use crate::player::Player;
use crate::shared_game_state::SharedGameState;
use crate::stage::Stage;
use crate::str;
pub mod characters;
@ -84,9 +85,11 @@ pub struct NPC {
pub anim_rect: Rect<usize>,
}
static PARTICLE_NPCS: [u16; 3] = [1, 4, 73];
impl NPC {
pub fn get_start_index(&self) -> u16 {
if self.npc_type == 1 || self.npc_type == 4 {
if PARTICLE_NPCS.contains(&self.npc_type) {
0x100
} else {
0
@ -94,8 +97,8 @@ impl NPC {
}
}
impl GameEntity<(&mut Player, &HashMap<u16, RefCell<NPC>>)> for NPC {
fn tick(&mut self, state: &mut SharedGameState, (player, map): (&mut Player, &HashMap<u16, RefCell<NPC>>)) -> GameResult {
impl GameEntity<(&mut Player, &HashMap<u16, RefCell<NPC>>, &Stage)> for NPC {
fn tick(&mut self, state: &mut SharedGameState, (player, map, stage): (&mut Player, &HashMap<u16, RefCell<NPC>>, &Stage)) -> GameResult {
match self.npc_type {
0 => { self.tick_n000_null() }
1 => { self.tick_n001_experience(state) }
@ -134,11 +137,14 @@ impl GameEntity<(&mut Player, &HashMap<u16, RefCell<NPC>>)> for NPC {
65 => { self.tick_n065_first_cave_bat(state, player) }
66 => { self.tick_n066_misery_bubble(state, map) }
67 => { self.tick_n067_misery_floating(state) }
69 => { self.tick_n069_pignon(state) }
70 => { self.tick_n070_sparkle(state) }
71 => { self.tick_n071_chinfish(state) }
72 => { self.tick_n072_sprinkler(state) }
72 => { self.tick_n072_sprinkler(state, player) }
73 => { self.tick_n073_water_droplet(state, stage) }
74 => { self.tick_n074_jack(state) }
75 => { self.tick_n075_kanpachi(state, player) }
76 => { self.tick_n076_flowers() }
77 => { self.tick_n077_yamashita(state) }
78 => { self.tick_n078_pot(state) }
79 => { self.tick_n079_mahin(state, player) }
@ -368,6 +374,10 @@ impl NPCMap {
}
}).collect_vec();
if !dead_npcs.is_empty() {
println!("deleting npcs: {:?}", dead_npcs);
}
for npc_id in dead_npcs.iter() {
self.npc_ids.remove(npc_id);
self.npcs.remove(npc_id);
@ -526,6 +536,7 @@ pub struct NPCTableEntry {
pub struct NPCTable {
entries: Vec<NPCTableEntry>,
pub tileset_name: String,
pub tex_npc1_name: String,
pub tex_npc2_name: String,
}
@ -535,6 +546,7 @@ impl NPCTable {
pub fn new() -> NPCTable {
NPCTable {
entries: Vec::new(),
tileset_name: str!("Stage/Prt0"),
tex_npc1_name: str!("Npc/Npc0"),
tex_npc2_name: str!("Npc/Npc0"),
}
@ -646,9 +658,12 @@ impl NPCTable {
pub fn get_texture_name(&self, npc_type: u16) -> &str {
if let Some(npc) = self.entries.get(npc_type as usize) {
match npc.spritesheet_id {
2 => &self.tileset_name,
17 => "Bullet",
19 => "Caret",
20 => "Npc/NpcSym",
21 => self.tex_npc1_name.as_str(),
22 => self.tex_npc2_name.as_str(),
21 => &self.tex_npc1_name,
22 => &self.tex_npc2_name,
23 => "Npc/NpcRegu",
_ => "Npc/Npc0"
}

View file

@ -689,6 +689,7 @@ impl Scene for GameScene {
}
}
state.npc_table.tileset_name = self.tex_tileset_name.to_owned();
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("");
@ -732,12 +733,12 @@ impl Scene for GameScene {
let mut npc = npc_cell.borrow_mut();
if npc.cond.alive() {
npc.tick(state, (&mut self.player, &self.npc_map.npcs))?;
npc.tick(state, (&mut self.player, &self.npc_map.npcs, &self.stage))?;
}
}
}
self.npc_map.process_npc_changes(state);
self.npc_map.process_npc_changes(state);
self.npc_map.garbage_collect();
self.player.flags.0 = 0;
@ -755,6 +756,7 @@ impl Scene for GameScene {
}
}
self.npc_map.process_npc_changes(state);
self.npc_map.garbage_collect();
self.tick_npc_bullet_collissions(state);
state.tick_carets();

View file

@ -12,7 +12,6 @@ use itertools::Itertools;
use num_derive::FromPrimitive;
use num_traits::{clamp, FromPrimitive};
use crate::str;
use crate::bitfield;
use crate::common::{Direction, FadeDirection, FadeState};
use crate::encoding::{read_cur_shift_jis, read_cur_wtf8};
@ -23,6 +22,7 @@ use crate::player::ControlMode;
use crate::scene::game_scene::GameScene;
use crate::scene::title_scene::TitleScene;
use crate::shared_game_state::SharedGameState;
use crate::str;
use crate::weapon::WeaponType;
/// Engine's text script VM operation codes.
@ -1112,7 +1112,7 @@ impl TextScriptVM {
if tick_npc != 0 {
if let Some(npc) = game_scene.npc_map.npcs.get(&tick_npc) {
npc.borrow_mut().tick(state, (&mut game_scene.player, &game_scene.npc_map.npcs))?;
npc.borrow_mut().tick(state, (&mut game_scene.player, &game_scene.npc_map.npcs, &game_scene.stage))?;
}
}