mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-11-05 16:34:46 +00:00
add smoke
This commit is contained in:
parent
83dc7893f4
commit
2736f61e13
|
|
@ -6,6 +6,7 @@ use crate::engine_constants::{BulletData, EngineConstants};
|
|||
use crate::physics::{OFF_X, OFF_Y, PhysicalEntity};
|
||||
use crate::SharedGameState;
|
||||
use crate::stage::Stage;
|
||||
use crate::npc::NPCMap;
|
||||
|
||||
pub struct BulletManager {
|
||||
pub bullets: Vec<Bullet>,
|
||||
|
|
@ -541,7 +542,19 @@ impl PhysicalEntity for Bullet {
|
|||
|
||||
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
|
||||
// todo play sound 12
|
||||
// todo smoke
|
||||
|
||||
for _ in 0..4 {
|
||||
let mut npc = NPCMap::create_npc(4, &state.npc_table);
|
||||
|
||||
npc.cond.set_alive(true);
|
||||
npc.direction = Direction::Left;
|
||||
npc.x = x * 16 * 0x200;
|
||||
npc.y = y * 16 * 0x200;
|
||||
npc.vel_x = state.game_rng.range(-0x200..0x200) as isize;
|
||||
npc.vel_y = state.game_rng.range(-0x200..0x200) as isize;
|
||||
|
||||
state.new_npcs.push(npc);
|
||||
}
|
||||
|
||||
if let Some(tile) = stage.map.tiles.get_mut(stage.map.width * (y + oy) as usize + (x + ox) as usize) {
|
||||
*tile = tile.wrapping_sub(1);
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ impl EngineConstants {
|
|||
Rect { left: 80, top: 0, right: 96, bottom: 16 },
|
||||
Rect { left: 96, top: 0, right: 112, bottom: 16 },
|
||||
Rect { left: 112, top: 0, right: 128, bottom: 16 },
|
||||
Rect { left: 16, top: 0, right: 17, bottom: 1 }, // right
|
||||
Rect { left: 16, top: 0, right: 17, bottom: 1 }, // up
|
||||
Rect { left: 80, top: 48, right: 96, bottom: 64 },
|
||||
Rect { left: 0, top: 128, right: 16, bottom: 144 },
|
||||
Rect { left: 16, top: 128, right: 32, bottom: 144 },
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ use crate::ggez::graphics::DrawParam;
|
|||
use crate::ggez::input::keyboard;
|
||||
use crate::ggez::mint::ColumnMatrix4;
|
||||
use crate::ggez::nalgebra::Vector2;
|
||||
use crate::npc::NPCTable;
|
||||
use crate::npc::{NPCTable, NPC};
|
||||
use crate::rng::RNG;
|
||||
use crate::scene::loading_scene::LoadingScene;
|
||||
use crate::scene::Scene;
|
||||
|
|
@ -97,6 +97,7 @@ pub struct SharedGameState {
|
|||
pub stages: Vec<StageData>,
|
||||
pub sound_manager: SoundManager,
|
||||
pub constants: EngineConstants,
|
||||
pub new_npcs: Vec<NPC>,
|
||||
pub scale: f32,
|
||||
pub god_mode: bool,
|
||||
pub speed_hack: bool,
|
||||
|
|
@ -186,6 +187,7 @@ impl Game {
|
|||
stages: Vec::with_capacity(96),
|
||||
sound_manager: SoundManager::new(ctx)?,
|
||||
constants,
|
||||
new_npcs: Vec::with_capacity(8),
|
||||
scale,
|
||||
god_mode: false,
|
||||
speed_hack: false,
|
||||
|
|
|
|||
|
|
@ -207,6 +207,11 @@ impl NPC {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n006_green_beetle(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n007_basil(&mut self, state: &mut SharedGameState, player: &Player) -> GameResult {
|
||||
match self.action_num {
|
||||
0 => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::caret::CaretType;
|
||||
use crate::common::Direction;
|
||||
use crate::ggez::GameResult;
|
||||
use crate::npc::NPC;
|
||||
use crate::npc::{NPC, NPCMap};
|
||||
use crate::player::Player;
|
||||
use crate::SharedGameState;
|
||||
|
||||
|
|
@ -17,6 +17,66 @@ impl NPC {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n003_dead_enemy(&mut self) -> GameResult {
|
||||
if self.action_num != 0xffff {
|
||||
self.action_num = 0xffff;
|
||||
self.action_counter2 = 0;
|
||||
self.anim_rect.left = 0;
|
||||
self.anim_rect.top = 0;
|
||||
self.anim_rect.right = 0;
|
||||
self.anim_rect.bottom = 0;
|
||||
}
|
||||
|
||||
self.action_counter2 += 1;
|
||||
if self.action_counter2 == 100 {
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n004_smoke(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
self.anim_num = state.game_rng.range(0..4) as u16;
|
||||
self.anim_counter = state.game_rng.range(0..3) as u16;
|
||||
|
||||
if self.direction == Direction::Left || self.direction == Direction::Up {
|
||||
let angle = state.game_rng.range(0..31415) as f32 / 5000.0;
|
||||
self.vel_x = (angle.cos() * state.game_rng.range(0x200..0x5ff) as f32) as isize;
|
||||
self.vel_y = (angle.sin() * state.game_rng.range(0x200..0x5ff) as f32) as isize;
|
||||
}
|
||||
} else {
|
||||
self.vel_x = (self.vel_x * 20) / 21;
|
||||
self.vel_y = (self.vel_y * 20) / 21;
|
||||
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
}
|
||||
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 4 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
|
||||
if self.anim_num > 7 {
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
}
|
||||
|
||||
match self.direction {
|
||||
Direction::Left | Direction::Right => {
|
||||
self.anim_rect = state.constants.npc.n004_smoke[self.anim_num as usize];
|
||||
}
|
||||
Direction::Up => {
|
||||
self.anim_rect = state.constants.npc.n004_smoke[self.anim_num as usize + 8];
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tick_n015_chest_closed(&mut self, state: &mut SharedGameState) -> GameResult {
|
||||
match self.action_num {
|
||||
0 | 1 => {
|
||||
|
|
@ -27,7 +87,18 @@ impl NPC {
|
|||
if self.direction == Direction::Right {
|
||||
self.vel_y = -0x200;
|
||||
|
||||
// todo smoke
|
||||
for _ in 0..4 {
|
||||
let mut npc = NPCMap::create_npc(4, &state.npc_table);
|
||||
|
||||
npc.cond.set_alive(true);
|
||||
npc.direction = Direction::Left;
|
||||
npc.x = self.x + state.game_rng.range(-12..12) as isize * 0x200;
|
||||
npc.y = self.y + state.game_rng.range(-12..12) as isize * 0x200;
|
||||
npc.vel_x = state.game_rng.range(-0x155..0x155) as isize;
|
||||
npc.vel_y = state.game_rng.range(-0x600..0) as isize;
|
||||
|
||||
state.new_npcs.push(npc);
|
||||
}
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n015_closed_chest[0];
|
||||
|
|
@ -39,6 +110,10 @@ impl NPC {
|
|||
}
|
||||
}
|
||||
2 => {
|
||||
if self.anim_counter == 0 {
|
||||
self.anim_rect = state.constants.npc.n015_closed_chest[self.anim_num as usize];
|
||||
}
|
||||
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 1 {
|
||||
self.anim_counter = 0;
|
||||
|
|
@ -48,8 +123,6 @@ impl NPC {
|
|||
self.anim_num = 0;
|
||||
self.action_num = 1;
|
||||
}
|
||||
|
||||
self.anim_rect = state.constants.npc.n015_closed_chest[self.anim_num as usize];
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
|
|
@ -169,7 +242,19 @@ impl NPC {
|
|||
}
|
||||
}
|
||||
1 => {
|
||||
// todo smoke
|
||||
for _ in 0..4 {
|
||||
let mut npc = NPCMap::create_npc(4, &state.npc_table);
|
||||
|
||||
npc.cond.set_alive(true);
|
||||
npc.direction = Direction::Left;
|
||||
npc.x = self.x;
|
||||
npc.y = self.y;
|
||||
npc.vel_x = state.game_rng.range(-0x155..0x155) as isize;
|
||||
npc.vel_y = state.game_rng.range(-0x600..0) as isize;
|
||||
|
||||
state.new_npcs.push(npc);
|
||||
}
|
||||
|
||||
self.action_num = 0;
|
||||
self.anim_rect = state.constants.npc.n018_door[0]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::io::Cursor;
|
|||
|
||||
use bitvec::vec::BitVec;
|
||||
use byteorder::{LE, ReadBytesExt};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{bitfield, SharedGameState};
|
||||
use crate::caret::CaretType;
|
||||
|
|
@ -49,7 +50,7 @@ bitfield! {
|
|||
pub show_damage, set_show_damage: 15;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct NPC {
|
||||
pub id: u16,
|
||||
pub npc_type: u16,
|
||||
|
|
@ -80,6 +81,16 @@ pub struct NPC {
|
|||
pub anim_rect: Rect<usize>,
|
||||
}
|
||||
|
||||
impl NPC {
|
||||
pub fn get_start_index(&self) -> u16 {
|
||||
if self.npc_type == 1 || self.npc_type == 4 {
|
||||
0x100
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GameEntity<&mut Player> for NPC {
|
||||
fn tick(&mut self, state: &mut SharedGameState, player: &mut Player) -> GameResult {
|
||||
if !self.cond.alive() {
|
||||
|
|
@ -90,6 +101,8 @@ impl GameEntity<&mut Player> for NPC {
|
|||
0 => { self.tick_n000_null() }
|
||||
1 => { self.tick_n001_experience(state) }
|
||||
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, player) }
|
||||
7 => { self.tick_n007_basil(state, player) }
|
||||
15 => { self.tick_n015_chest_closed(state) }
|
||||
|
|
@ -302,7 +315,7 @@ impl NPCMap {
|
|||
self.npcs.get_mut(&data.id).unwrap().get_mut()
|
||||
}
|
||||
|
||||
pub fn create_npc(&self, npc_type: u16, table: &NPCTable) -> NPC {
|
||||
pub fn create_npc(npc_type: u16, table: &NPCTable) -> NPC {
|
||||
let display_bounds = table.get_display_bounds(npc_type);
|
||||
let hit_bounds = table.get_hit_bounds(npc_type);
|
||||
let (size, life, damage, flags, exp) = match table.get_entry(npc_type) {
|
||||
|
|
@ -343,7 +356,18 @@ impl NPCMap {
|
|||
}
|
||||
|
||||
pub fn garbage_collect(&mut self) {
|
||||
self.npcs.retain(|_, npc_cell| npc_cell.borrow().cond.alive());
|
||||
let dead_npcs = self.npcs.iter().filter_map(|(&id, npc_cell)| {
|
||||
if !npc_cell.borrow().cond.alive() {
|
||||
Some(id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect_vec();
|
||||
|
||||
for npc_id in dead_npcs.iter() {
|
||||
self.npc_ids.remove(npc_id);
|
||||
self.npcs.remove(npc_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_by_event(&mut self, event_num: u16, game_flags: &mut BitVec) {
|
||||
|
|
@ -367,40 +391,39 @@ impl NPCMap {
|
|||
unreachable!()
|
||||
}
|
||||
|
||||
pub fn create_death_effect(&self, x: isize, y: isize, radius: usize, count: usize, state: &mut SharedGameState) -> Vec<NPC> {
|
||||
let mut npcs = Vec::new();
|
||||
pub fn create_death_effect(&self, x: isize, y: isize, radius: usize, count: usize, state: &mut SharedGameState) {
|
||||
let radius = radius as i32 / 0x200;
|
||||
|
||||
for _ in 0..count {
|
||||
let off_x = state.game_rng.range(-radius..radius) * 0x200;
|
||||
let off_y = state.game_rng.range(-radius..radius) * 0x200;
|
||||
let off_x = state.game_rng.range(-radius..radius) as isize * 0x200;
|
||||
let off_y = state.game_rng.range(-radius..radius) as isize * 0x200;
|
||||
|
||||
// todo smoke
|
||||
let mut npc = NPCMap::create_npc(4, &state.npc_table);
|
||||
|
||||
npc.cond.set_alive(true);
|
||||
npc.direction = Direction::Left;
|
||||
npc.x = x + off_x;
|
||||
npc.y = y + off_y;
|
||||
|
||||
state.new_npcs.push(npc);
|
||||
}
|
||||
|
||||
state.create_caret(x, y, CaretType::Explosion, Direction::Left);
|
||||
npcs
|
||||
}
|
||||
|
||||
pub fn process_dead_npcs(&mut self, list: &Vec<u16>, state: &mut SharedGameState) {
|
||||
let mut new_npcs = Vec::new();
|
||||
|
||||
pub fn process_dead_npcs(&mut self, list: &[u16], state: &mut SharedGameState) {
|
||||
for id in list {
|
||||
let npc_cell = self.npcs.get(id);
|
||||
if npc_cell.is_some() {
|
||||
let mut npc = npc_cell.unwrap().borrow_mut();
|
||||
|
||||
let mut npcs = match npc.size {
|
||||
1 => { self.create_death_effect(npc.x, npc.y, npc.display_bounds.right, 3, state) }
|
||||
2 => { self.create_death_effect(npc.x, npc.y, npc.display_bounds.right, 7, state) }
|
||||
3 => { self.create_death_effect(npc.x, npc.y, npc.display_bounds.right, 12, state) }
|
||||
_ => { vec![] }
|
||||
match npc.size {
|
||||
1 => { self.create_death_effect(npc.x, npc.y, npc.display_bounds.right, 3, state); }
|
||||
2 => { self.create_death_effect(npc.x, npc.y, npc.display_bounds.right, 7, state); }
|
||||
3 => { self.create_death_effect(npc.x, npc.y, npc.display_bounds.right, 12, state); }
|
||||
_ => {}
|
||||
};
|
||||
|
||||
if !npcs.is_empty() {
|
||||
new_npcs.append(&mut npcs);
|
||||
}
|
||||
|
||||
if npc.exp != 0 {
|
||||
//if state.game_rng.range(0..4) == 0 {
|
||||
// health
|
||||
|
|
@ -420,14 +443,14 @@ impl NPCMap {
|
|||
1
|
||||
};
|
||||
|
||||
let mut xp_npc = self.create_npc(1, &state.npc_table);
|
||||
let mut xp_npc = NPCMap::create_npc(1, &state.npc_table);
|
||||
xp_npc.cond.set_alive(true);
|
||||
xp_npc.direction = Direction::Left;
|
||||
xp_npc.x = npc.x;
|
||||
xp_npc.y = npc.y;
|
||||
xp_npc.exp = exp_piece;
|
||||
|
||||
new_npcs.push(xp_npc);
|
||||
state.new_npcs.push(xp_npc);
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
|
@ -440,20 +463,24 @@ impl NPCMap {
|
|||
}
|
||||
}
|
||||
|
||||
for mut npc in new_npcs {
|
||||
let id = if npc.id == 0 {
|
||||
if npc.npc_type == 1 {
|
||||
self.allocate_id(0x100)
|
||||
} else {
|
||||
self.allocate_id(0)
|
||||
}
|
||||
} else {
|
||||
npc.id
|
||||
};
|
||||
self.process_npc_changes(state);
|
||||
}
|
||||
|
||||
npc.id = id;
|
||||
self.npc_ids.insert(id);
|
||||
self.npcs.insert(id, RefCell::new(npc));
|
||||
pub fn process_npc_changes(&mut self, state: &mut SharedGameState) {
|
||||
if !state.new_npcs.is_empty() {
|
||||
for mut npc in state.new_npcs.iter_mut() {
|
||||
let id = if npc.id == 0 {
|
||||
self.allocate_id(npc.get_start_index())
|
||||
} else {
|
||||
npc.id
|
||||
};
|
||||
|
||||
npc.id = id;
|
||||
self.npc_ids.insert(id);
|
||||
self.npcs.insert(id, RefCell::new(*npc));
|
||||
}
|
||||
|
||||
state.new_npcs.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -715,6 +715,7 @@ impl Scene for GameScene {
|
|||
self.player.flags.0 = 0;
|
||||
state.tick_carets();
|
||||
self.bullet_manager.tick_bullets(state, &self.player, &mut self.stage);
|
||||
self.npc_map.process_npc_changes(state);
|
||||
|
||||
self.player.tick_map_collisions(state, &mut self.stage);
|
||||
self.player.tick_npc_collisions(state, &mut self.npc_map, &mut self.inventory);
|
||||
|
|
@ -730,7 +731,7 @@ impl Scene for GameScene {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.npc_map.process_npc_changes(state);
|
||||
self.tick_npc_bullet_collissions(state);
|
||||
|
||||
self.frame.update(state, &self.player, &self.stage);
|
||||
|
|
|
|||
Loading…
Reference in a new issue