1
0
Fork 0
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:
Alula 2020-09-13 07:18:24 +02:00
parent 83dc7893f4
commit 2736f61e13
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
7 changed files with 178 additions and 45 deletions

View file

@ -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);

View file

@ -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 },

View file

@ -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,

View file

@ -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 => {

View file

@ -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]
}

View file

@ -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();
}
}

View file

@ -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);