mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-03-23 10:29:18 +00:00
weapons are finally functional and ready to be fully implemented
This commit is contained in:
parent
06aed8ad27
commit
519c7e6349
199
src/bullet.rs
199
src/bullet.rs
|
@ -1,7 +1,9 @@
|
|||
use num_traits::clamp;
|
||||
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::{Condition, Direction, Flag, Rect};
|
||||
use crate::engine_constants::{BulletData, EngineConstants};
|
||||
use crate::physics::PhysicalEntity;
|
||||
use crate::physics::{OFF_X, OFF_Y, PhysicalEntity};
|
||||
use crate::SharedGameState;
|
||||
use crate::stage::Stage;
|
||||
|
||||
|
@ -20,10 +22,10 @@ impl BulletManager {
|
|||
self.bullets.push(Bullet::new(x, y, btype, direction, constants));
|
||||
}
|
||||
|
||||
pub fn tick_bullets(&mut self, state: &mut SharedGameState, stage: &Stage) {
|
||||
pub fn tick_bullets(&mut self, state: &mut SharedGameState, stage: &mut Stage) {
|
||||
for bullet in self.bullets.iter_mut() {
|
||||
bullet.tick(state);
|
||||
bullet.flags.0 = 0;
|
||||
bullet.hit_flags.0 = 0;
|
||||
bullet.tick_map_collisions(state, stage);
|
||||
}
|
||||
|
||||
|
@ -52,6 +54,7 @@ pub struct Bullet {
|
|||
pub damage: u16,
|
||||
pub cond: Condition,
|
||||
pub flags: Flag,
|
||||
pub hit_flags: Flag,
|
||||
pub direction: Direction,
|
||||
pub anim_rect: Rect<usize>,
|
||||
pub enemy_hit_width: u32,
|
||||
|
@ -93,6 +96,7 @@ impl Bullet {
|
|||
damage: bullet.damage as u16,
|
||||
cond: Condition(0x80),
|
||||
flags: bullet.flags,
|
||||
hit_flags: Flag(0),
|
||||
direction,
|
||||
anim_rect: Rect::new(0, 0, 0, 0),
|
||||
enemy_hit_width: bullet.enemy_hit_width as u32 * 0x200,
|
||||
|
@ -215,6 +219,111 @@ impl Bullet {
|
|||
_ => { self.cond.set_alive(false); }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn vanish(&mut self, state: &mut SharedGameState) {
|
||||
if self.btype != 37 && self.btype != 38 && self.btype != 39 {
|
||||
// todo play sound 28
|
||||
} else {
|
||||
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Up);
|
||||
}
|
||||
|
||||
self.cond.set_alive(false);
|
||||
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Right);
|
||||
}
|
||||
|
||||
fn judge_hit_block_destroy(&mut self, x: isize, y: isize, hit_attribs: &[u8; 4], state: &mut SharedGameState) {
|
||||
let mut hits = [false; 4];
|
||||
let mut block_x = (x * 16 + 8) * 0x200;
|
||||
let mut block_y = (y * 16 + 8) * 0x200;
|
||||
|
||||
for (i, &attr) in hit_attribs.iter().enumerate() {
|
||||
if self.flags.snack_destroy() {
|
||||
hits[i] = attr == 0x41 || attr == 0x61;
|
||||
} else {
|
||||
hits[i] = attr == 0x41 || attr == 0x43 || attr == 0x61;
|
||||
}
|
||||
}
|
||||
|
||||
// left wall
|
||||
if hits[0] && hits[2] {
|
||||
if (self.x - self.hit_bounds.left as isize) < block_x {
|
||||
self.hit_flags.set_hit_left_wall(true);
|
||||
}
|
||||
} else if hits[0] && !hits[2] {
|
||||
if (self.x - self.hit_bounds.left as isize) < block_x
|
||||
&& (self.y - self.hit_bounds.top as isize) < block_y - (3 * 0x200) {
|
||||
self.hit_flags.set_hit_left_wall(true);
|
||||
}
|
||||
} else if !hits[0] && hits[2]
|
||||
&& (self.x - self.hit_bounds.left as isize) < block_x
|
||||
&& (self.y + self.hit_bounds.top as isize) > block_y + (3 * 0x200) {
|
||||
self.hit_flags.set_hit_left_wall(true);
|
||||
}
|
||||
|
||||
// right wall
|
||||
if hits[1] && hits[3] {
|
||||
if (self.x + self.hit_bounds.right as isize) > block_x {
|
||||
self.hit_flags.set_hit_right_wall(true);
|
||||
}
|
||||
} else if hits[1] && !hits[3] {
|
||||
if (self.x + self.hit_bounds.right as isize) > block_x
|
||||
&& (self.y - self.hit_bounds.top as isize) < block_y - (3 * 0x200) {
|
||||
self.hit_flags.set_hit_right_wall(true);
|
||||
}
|
||||
} else if !hits[1] && hits[3]
|
||||
&& (self.x + self.hit_bounds.right as isize) > block_x
|
||||
&& (self.y + self.hit_bounds.top as isize) > block_y + (3 * 0x200) {
|
||||
self.hit_flags.set_hit_right_wall(true);
|
||||
}
|
||||
|
||||
// ceiling
|
||||
if hits[0] && hits[1] {
|
||||
if (self.y - self.hit_bounds.top as isize) < block_y {
|
||||
self.hit_flags.set_hit_top_wall(true);
|
||||
}
|
||||
} else if hits[0] && !hits[1] {
|
||||
if (self.x - self.hit_bounds.left as isize) < block_x - (3 * 0x200)
|
||||
&& (self.y - self.hit_bounds.top as isize) < block_y {
|
||||
self.hit_flags.set_hit_top_wall(true);
|
||||
}
|
||||
} else if !hits[0] && hits[1]
|
||||
&& (self.x + self.hit_bounds.right as isize) > block_x + (3 * 0x200)
|
||||
&& (self.y - self.hit_bounds.top as isize) < block_y {
|
||||
self.hit_flags.set_hit_top_wall(true);
|
||||
}
|
||||
|
||||
// ground
|
||||
if hits[2] && hits[3] {
|
||||
if (self.y + self.hit_bounds.bottom as isize) > block_y {
|
||||
self.hit_flags.set_hit_bottom_wall(true);
|
||||
}
|
||||
} else if hits[2] && !hits[3] {
|
||||
if (self.x - self.hit_bounds.left as isize) < block_x - (3 * 0x200)
|
||||
&& (self.y + self.hit_bounds.bottom as isize) > block_y {
|
||||
self.hit_flags.set_hit_bottom_wall(true);
|
||||
}
|
||||
} else if !hits[2] && hits[3]
|
||||
&& (self.x + self.hit_bounds.right as isize) > block_x + (3 * 0x200)
|
||||
&& (self.y + self.hit_bounds.bottom as isize) > block_y {
|
||||
self.hit_flags.set_hit_bottom_wall(true);
|
||||
}
|
||||
|
||||
if self.flags.hit_bottom_wall() {
|
||||
if self.hit_flags.hit_left_wall() {
|
||||
self.x = block_x + self.hit_bounds.right as isize;
|
||||
} else if self.hit_flags.hit_right_wall() {
|
||||
self.x = block_x + self.hit_bounds.left as isize;
|
||||
} else if self.hit_flags.hit_left_wall() {
|
||||
self.x = block_x + self.hit_bounds.right as isize;
|
||||
} else if self.hit_flags.hit_right_wall() {
|
||||
self.x = block_x + self.hit_bounds.left as isize;
|
||||
}
|
||||
} else if self.hit_flags.hit_left_wall() || self.hit_flags.hit_top_wall()
|
||||
|| self.hit_flags.hit_right_wall() || self.hit_flags.hit_bottom_wall() {
|
||||
|
||||
self.vanish(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PhysicalEntity for Bullet {
|
||||
|
@ -263,14 +372,92 @@ impl PhysicalEntity for Bullet {
|
|||
}
|
||||
|
||||
fn flags(&mut self) -> &mut Flag {
|
||||
&mut self.flags
|
||||
&mut self.hit_flags
|
||||
}
|
||||
|
||||
fn is_player(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/*fn judge_hit_block(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
fn judge_hit_block(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
if (self.x - self.hit_bounds.left as isize) < (x * 16 + 8) * 0x200
|
||||
&& (self.x + self.hit_bounds.right as isize) > (x * 16 - 8) * 0x200
|
||||
&& (self.y - self.hit_bounds.top as isize) < (y * 16 + 8) * 0x200
|
||||
&& (self.y + self.hit_bounds.bottom as isize) > (y * 16 - 8) * 0x200
|
||||
{
|
||||
self.hit_flags.set_weapon_hit_block(true);
|
||||
}
|
||||
}
|
||||
|
||||
}*/
|
||||
|
||||
fn tick_map_collisions(&mut self, state: &mut SharedGameState, stage: &mut Stage) {
|
||||
let x = clamp(self.x() / 16 / 0x200, 0, stage.map.width as isize);
|
||||
let y = clamp(self.y() / 16 / 0x200, 0, stage.map.height as isize);
|
||||
let mut hit_attribs = [0u8; 4];
|
||||
|
||||
if self.flags.hit_right_wall() { // ???
|
||||
return;
|
||||
}
|
||||
|
||||
for (idx, (&ox, &oy)) in OFF_X.iter().zip(OFF_Y.iter()).enumerate() {
|
||||
if idx == 4 {
|
||||
break;
|
||||
}
|
||||
|
||||
let attrib = stage.map.get_attribute((x + ox) as usize, (y + oy) as usize);
|
||||
hit_attribs[idx] = attrib;
|
||||
|
||||
match attrib {
|
||||
// Blocks
|
||||
0x41 | 0x44 | 0x61 | 0x64 => {
|
||||
self.judge_hit_block(state, x + ox, y + oy);
|
||||
}
|
||||
0x43 => {
|
||||
self.judge_hit_block(state, x + ox, y + oy);
|
||||
|
||||
if self.hit_flags.0 != 0 && (self.flags.hit_left_slope() || self.flags.snack_destroy()) {
|
||||
if !self.flags.snack_destroy() {
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
|
||||
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
|
||||
// todo play sound 12
|
||||
// todo smoke
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Slopes
|
||||
0x50 | 0x70 => {
|
||||
self.judge_hit_triangle_a(x + ox, y + oy);
|
||||
}
|
||||
0x51 | 0x71 => {
|
||||
self.judge_hit_triangle_b(x + ox, y + oy);
|
||||
}
|
||||
0x52 | 0x72 => {
|
||||
self.judge_hit_triangle_c(x + ox, y + oy);
|
||||
}
|
||||
0x53 | 0x73 => {
|
||||
self.judge_hit_triangle_d(x + ox, y + oy);
|
||||
}
|
||||
0x54 | 0x74 => {
|
||||
self.judge_hit_triangle_e(x + ox, y + oy);
|
||||
}
|
||||
0x55 | 0x75 => {
|
||||
self.judge_hit_triangle_f(x + ox, y + oy);
|
||||
}
|
||||
0x56 | 0x76 => {
|
||||
self.judge_hit_triangle_g(x + ox, y + oy);
|
||||
}
|
||||
0x57 | 0x77 => {
|
||||
self.judge_hit_triangle_h(x + ox, y + oy);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
self.judge_hit_block_destroy(x, y, &hit_attribs, state);
|
||||
}
|
||||
}
|
||||
|
|
34
src/caret.rs
34
src/caret.rs
|
@ -76,7 +76,7 @@ impl Caret {
|
|||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
|
||||
if self.anim_num > constants.caret.projectile_dissipation_left_rects.len() as u16 {
|
||||
if self.anim_num >= constants.caret.projectile_dissipation_left_rects.len() as u16 {
|
||||
self.cond.set_alive(false);
|
||||
return;
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ impl Caret {
|
|||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
|
||||
if self.anim_num > constants.caret.projectile_dissipation_right_rects.len() as u16 {
|
||||
if self.anim_num >= constants.caret.projectile_dissipation_right_rects.len() as u16 {
|
||||
self.cond.set_alive(false);
|
||||
return;
|
||||
}
|
||||
|
@ -122,10 +122,10 @@ impl Caret {
|
|||
if self.anim_counter > 3 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
}
|
||||
|
||||
if self.anim_num == constants.caret.shoot_rects.len() as u16 {
|
||||
self.cond.set_alive(false);
|
||||
if self.anim_num >= constants.caret.shoot_rects.len() as u16 {
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
CaretType::SnakeAfterimage | CaretType::SnakeAfterimage2 => {} // dupe, unused
|
||||
|
@ -138,11 +138,11 @@ impl Caret {
|
|||
if self.anim_counter > 4 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
}
|
||||
|
||||
if self.anim_num == constants.caret.zzz_rects.len() as u16 {
|
||||
self.cond.set_alive(false);
|
||||
return;
|
||||
if self.anim_num >= constants.caret.zzz_rects.len() as u16 {
|
||||
self.cond.set_alive(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.x += 0x80; // 0.4fix9
|
||||
|
@ -200,7 +200,21 @@ impl Caret {
|
|||
}
|
||||
CaretType::LevelUp => {}
|
||||
CaretType::HurtParticles => {}
|
||||
CaretType::Explosion => {}
|
||||
CaretType::Explosion => {
|
||||
if self.anim_counter == 0 {
|
||||
self.anim_rect = constants.caret.explosion_rects[self.anim_num as usize];
|
||||
}
|
||||
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 2 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
|
||||
if self.anim_num >= constants.caret.explosion_rects.len() as u16 {
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
CaretType::LittleParticles => {
|
||||
if self.anim_num == 0 {
|
||||
match self.direction {
|
||||
|
|
|
@ -57,6 +57,7 @@ pub struct CaretConsts {
|
|||
pub drowned_quote_right_rect: Rect<usize>,
|
||||
pub level_up_rects: Vec<Rect<usize>>,
|
||||
pub level_down_rects: Vec<Rect<usize>>,
|
||||
pub explosion_rects: Vec<Rect<usize>>,
|
||||
pub little_particles_rects: Vec<Rect<usize>>,
|
||||
pub exhaust_rects: Vec<Rect<usize>>,
|
||||
pub question_left_rect: Rect<usize>,
|
||||
|
@ -78,6 +79,7 @@ impl Clone for CaretConsts {
|
|||
drowned_quote_right_rect: self.drowned_quote_right_rect,
|
||||
level_up_rects: self.level_up_rects.clone(),
|
||||
level_down_rects: self.level_down_rects.clone(),
|
||||
explosion_rects: self.explosion_rects.clone(),
|
||||
little_particles_rects: self.little_particles_rects.clone(),
|
||||
exhaust_rects: self.exhaust_rects.clone(),
|
||||
question_left_rect: self.question_left_rect,
|
||||
|
@ -375,6 +377,10 @@ impl EngineConstants {
|
|||
Rect { left: 0, top: 96, right: 56, bottom: 112 },
|
||||
Rect { left: 0, top: 112, right: 56, bottom: 128 },
|
||||
],
|
||||
explosion_rects: vec![
|
||||
Rect { left: 112, top: 0, right: 144, bottom: 32 },
|
||||
Rect { left: 144, top: 0, right: 176, bottom: 32 },
|
||||
],
|
||||
little_particles_rects: vec![
|
||||
Rect { left: 56, top: 24, right: 64, bottom: 32 },
|
||||
Rect { left: 0, top: 0, right: 0, bottom: 0 },
|
||||
|
|
109
src/npc/mod.rs
109
src/npc/mod.rs
|
@ -1,3 +1,4 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::{BTreeSet, HashMap};
|
||||
use std::io;
|
||||
use std::io::Cursor;
|
||||
|
@ -6,6 +7,7 @@ use bitvec::vec::BitVec;
|
|||
use byteorder::{LE, ReadBytesExt};
|
||||
|
||||
use crate::{bitfield, SharedGameState};
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::{Condition, Rect};
|
||||
use crate::common::Direction;
|
||||
use crate::common::Flag;
|
||||
|
@ -24,6 +26,7 @@ pub mod mimiga_village;
|
|||
pub mod misc;
|
||||
|
||||
bitfield! {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct NPCFlag(u16);
|
||||
impl Debug;
|
||||
|
||||
|
@ -45,7 +48,7 @@ bitfield! {
|
|||
pub show_damage, set_show_damage: 15;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NPC {
|
||||
pub id: u16,
|
||||
pub npc_type: u16,
|
||||
|
@ -211,12 +214,10 @@ impl PhysicalEntity for NPC {
|
|||
}
|
||||
|
||||
pub struct NPCMap {
|
||||
/// A sorted pool of free IDs to make ID assignment for new entities a bit cheaper.
|
||||
free_npc_ids: BTreeSet<u16>,
|
||||
/// A sorted pool of used IDs to used to iterate over NPCs in order, as original game does.
|
||||
pub npc_ids: BTreeSet<u16>,
|
||||
/// Do not iterate over this directly outside render pipeline.
|
||||
pub npcs: HashMap<u16, NPC>,
|
||||
pub npcs: HashMap<u16, RefCell<NPC>>,
|
||||
}
|
||||
|
||||
impl NPCMap {
|
||||
|
@ -224,25 +225,23 @@ impl NPCMap {
|
|||
pub fn new() -> NPCMap {
|
||||
NPCMap {
|
||||
npc_ids: BTreeSet::new(),
|
||||
free_npc_ids: BTreeSet::new(),
|
||||
npcs: HashMap::with_capacity(256),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.free_npc_ids.clear();
|
||||
self.npc_ids.clear();
|
||||
self.npcs.clear();
|
||||
}
|
||||
|
||||
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, damage) = match table.get_entry(data.npc_type) {
|
||||
Some(entry) => { (entry.size, entry.life, entry.damage as u16) }
|
||||
None => { (1, 0, 0) }
|
||||
let (size, life, damage, flags) = match table.get_entry(data.npc_type) {
|
||||
Some(entry) => { (entry.size, entry.life, entry.damage as u16, entry.npc_flags) }
|
||||
None => { (1, 0, 0, NPCFlag(0)) }
|
||||
};
|
||||
let npc_flags = NPCFlag(data.flags | flags.0);
|
||||
|
||||
let npc = NPC {
|
||||
id: data.id,
|
||||
|
@ -262,7 +261,7 @@ impl NPCMap {
|
|||
life,
|
||||
damage,
|
||||
cond: Condition(0x00),
|
||||
flags: Flag(data.flag_num as u32),
|
||||
flags: Flag(0),
|
||||
direction: if npc_flags.spawn_facing_right() { Direction::Right } else { Direction::Left },
|
||||
npc_flags,
|
||||
display_bounds,
|
||||
|
@ -272,15 +271,20 @@ impl NPCMap {
|
|||
anim_rect: Rect::new(0, 0, 0, 0),
|
||||
};
|
||||
|
||||
self.free_npc_ids.remove(&data.id);
|
||||
self.npc_ids.insert(data.id);
|
||||
self.npcs.insert(data.id, npc);
|
||||
self.npcs.insert(data.id, RefCell::new(npc));
|
||||
|
||||
self.npcs.get_mut(&data.id).unwrap()
|
||||
self.npcs.get_mut(&data.id).unwrap().get_mut()
|
||||
}
|
||||
|
||||
pub fn garbage_collect(&mut self) {
|
||||
self.npcs.retain(|_, npc_cell| npc_cell.borrow().cond.alive());
|
||||
}
|
||||
|
||||
pub fn remove_by_event(&mut self, event_num: u16, game_flags: &mut BitVec) {
|
||||
for npc in self.npcs.values_mut() {
|
||||
for npc_cell in self.npcs.values_mut() {
|
||||
let mut npc = npc_cell.borrow_mut();
|
||||
|
||||
if npc.event_num == event_num {
|
||||
npc.cond.set_alive(false);
|
||||
game_flags.set(npc.flag_num as usize, true);
|
||||
|
@ -288,16 +292,81 @@ impl NPCMap {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn allocate_id(&mut self) -> u16 {
|
||||
for i in 0..(u16::MAX) {
|
||||
if !self.npc_ids.contains(&i) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
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;
|
||||
|
||||
// todo smoke
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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![] }
|
||||
};
|
||||
|
||||
if !npcs.is_empty() {
|
||||
new_npcs.append(&mut npcs);
|
||||
}
|
||||
|
||||
state.game_flags.set(npc.flag_num as usize, true);
|
||||
|
||||
// todo vanish / show damage
|
||||
|
||||
npc.cond.set_alive(false);
|
||||
}
|
||||
}
|
||||
|
||||
for mut npc in new_npcs {
|
||||
let id = if npc.id == 0 {
|
||||
self.allocate_id()
|
||||
} else {
|
||||
npc.id
|
||||
};
|
||||
|
||||
npc.id = id;
|
||||
self.npcs.insert(id, RefCell::new(npc));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_alive(&self, npc_id: u16) -> bool {
|
||||
if let Some(npc) = self.npcs.get(&npc_id) {
|
||||
return npc.cond.alive();
|
||||
if let Some(npc_cell) = self.npcs.get(&npc_id) {
|
||||
return npc_cell.borrow().cond.alive();
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_alive_by_event(&self, event_num: u16) -> bool {
|
||||
for npc in self.npcs.values() {
|
||||
for npc_cell in self.npcs.values() {
|
||||
let npc = npc_cell.borrow();
|
||||
if npc.cond.alive() && npc.event_num == event_num {
|
||||
return true;
|
||||
}
|
||||
|
@ -373,11 +442,11 @@ impl NPCTable {
|
|||
}
|
||||
|
||||
for npc in table.entries.iter_mut() {
|
||||
npc.death_sound = f.read_u8()?;
|
||||
npc.hurt_sound = f.read_u8()?;
|
||||
}
|
||||
|
||||
for npc in table.entries.iter_mut() {
|
||||
npc.hurt_sound = f.read_u8()?;
|
||||
npc.death_sound = f.read_u8()?;
|
||||
}
|
||||
|
||||
for npc in table.entries.iter_mut() {
|
||||
|
|
|
@ -4,8 +4,8 @@ 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 const OFF_X: [isize; 9] = [0, 1, 0, 1, 2, 2, 2, 0, 1];
|
||||
pub const OFF_Y: [isize; 9] = [0, 0, 1, 1, 0, 1, 2, 2, 2];
|
||||
|
||||
pub trait PhysicalEntity {
|
||||
fn x(&self) -> isize;
|
||||
|
@ -304,7 +304,7 @@ pub trait PhysicalEntity {
|
|||
}
|
||||
}
|
||||
|
||||
fn tick_map_collisions(&mut self, state: &SharedGameState, stage: &Stage) {
|
||||
fn tick_map_collisions(&mut self, state: &mut SharedGameState, stage: &mut Stage) {
|
||||
let big = self.size() >= 3;
|
||||
let x = clamp((self.x() - if big { 0x1000 } else { 0 }) / 16 / 0x200, 0, stage.map.width as isize);
|
||||
let y = clamp((self.y() - if big { 0x1000 } else { 0 }) / 16 / 0x200, 0, stage.map.height as isize);
|
||||
|
|
|
@ -41,7 +41,9 @@ pub struct Player {
|
|||
pub up: bool,
|
||||
pub down: bool,
|
||||
pub shock_counter: u8,
|
||||
pub current_weapon: u8,
|
||||
pub update_target: bool,
|
||||
weapon_offset_y: i8,
|
||||
index_x: isize,
|
||||
index_y: isize,
|
||||
sprash: bool,
|
||||
|
@ -53,13 +55,14 @@ pub struct Player {
|
|||
anim_num: u16,
|
||||
anim_counter: u16,
|
||||
anim_rect: Rect<usize>,
|
||||
weapon_rect: Rect<usize>,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn new(state: &mut SharedGameState) -> Self {
|
||||
pub fn new(state: &mut SharedGameState) -> Player {
|
||||
let constants = &state.constants;
|
||||
|
||||
Self {
|
||||
Player {
|
||||
x: 0,
|
||||
y: 0,
|
||||
vel_x: 0,
|
||||
|
@ -83,6 +86,8 @@ impl Player {
|
|||
update_target: true,
|
||||
up: false,
|
||||
down: false,
|
||||
current_weapon: 0,
|
||||
weapon_offset_y: 0,
|
||||
shock_counter: 0,
|
||||
booster_switch: 0,
|
||||
star: 0,
|
||||
|
@ -92,6 +97,7 @@ impl Player {
|
|||
anim_num: 0,
|
||||
anim_counter: 0,
|
||||
anim_rect: constants.my_char.animations_right[0],
|
||||
weapon_rect: Rect::new(0, 0, 0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -474,15 +480,37 @@ impl Player {
|
|||
self.anim_num = if self.vel_y > 0 { 1 } else { 3 };
|
||||
}
|
||||
|
||||
self.weapon_offset_y = 0;
|
||||
self.weapon_rect.left = (self.current_weapon as usize % 13) * 24;
|
||||
self.weapon_rect.top = (self.current_weapon as usize / 13) * 96;
|
||||
self.weapon_rect.right = self.weapon_rect.left + 24;
|
||||
self.weapon_rect.bottom = self.weapon_rect.top + 16;
|
||||
|
||||
match self.direction {
|
||||
Direction::Left => {
|
||||
self.anim_rect = state.constants.my_char.animations_left[self.anim_num as usize];
|
||||
}
|
||||
Direction::Right => {
|
||||
self.weapon_rect.top += 16;
|
||||
self.weapon_rect.bottom += 16;
|
||||
self.anim_rect = state.constants.my_char.animations_right[self.anim_num as usize];
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if self.up {
|
||||
self.weapon_offset_y = -4;
|
||||
self.weapon_rect.top += 32;
|
||||
self.weapon_rect.bottom += 32;
|
||||
} else if self.down {
|
||||
self.weapon_offset_y = 4;
|
||||
self.weapon_rect.top += 64;
|
||||
self.weapon_rect.bottom += 64;
|
||||
}
|
||||
|
||||
if self.anim_num == 1 || self.anim_num == 3 || self.anim_num == 6 || self.anim_num == 8 {
|
||||
self.weapon_rect.top += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn damage(&mut self, hp: isize, state: &mut SharedGameState) {
|
||||
|
@ -550,18 +578,42 @@ impl GameEntity<()> for Player {
|
|||
}
|
||||
|
||||
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, frame: &Frame) -> GameResult<> {
|
||||
if !self.cond.alive() || self.cond.hidden() {
|
||||
if !self.cond.alive() || self.cond.hidden() || (self.shock_counter / 2 % 2 != 0) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// todo draw weapon
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "MyChar")?;
|
||||
batch.add_rect(
|
||||
(((self.x - self.display_bounds.left as isize) / 0x200) - (frame.x / 0x200)) as f32,
|
||||
(((self.y - self.display_bounds.top as isize) / 0x200) - (frame.y / 0x200)) as f32,
|
||||
&self.anim_rect,
|
||||
);
|
||||
batch.draw(ctx)?;
|
||||
{
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "MyChar")?;
|
||||
batch.add_rect(
|
||||
(((self.x - self.display_bounds.left as isize) / 0x200) - (frame.x / 0x200)) as f32,
|
||||
(((self.y - self.display_bounds.top as isize) / 0x200) - (frame.y / 0x200)) as f32,
|
||||
&self.anim_rect,
|
||||
);
|
||||
batch.draw(ctx)?;
|
||||
}
|
||||
|
||||
if self.current_weapon != 0 {
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "Arms")?;
|
||||
match self.direction {
|
||||
Direction::Left => {
|
||||
batch.add_rect(
|
||||
(((self.x - self.display_bounds.left as isize) / 0x200) - (frame.x / 0x200)) as f32 - 8.0,
|
||||
(((self.y - self.display_bounds.top as isize) / 0x200) - (frame.y / 0x200)) as f32 + self.weapon_offset_y as f32,
|
||||
&self.weapon_rect,
|
||||
);
|
||||
}
|
||||
Direction::Right => {
|
||||
batch.add_rect(
|
||||
(((self.x - self.display_bounds.left as isize) / 0x200) - (frame.x / 0x200)) as f32,
|
||||
(((self.y - self.display_bounds.top as isize) / 0x200) - (frame.y / 0x200)) as f32 + self.weapon_offset_y as f32,
|
||||
&self.weapon_rect,
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
batch.draw(ctx)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::physics::PhysicalEntity;
|
|||
use crate::player::Player;
|
||||
use crate::SharedGameState;
|
||||
use crate::stage::Stage;
|
||||
use std::borrow::Borrow;
|
||||
|
||||
impl PhysicalEntity for Player {
|
||||
#[inline(always)]
|
||||
|
@ -148,18 +149,19 @@ impl Player {
|
|||
|
||||
pub fn tick_npc_collisions(&mut self, state: &mut SharedGameState, npc_map: &mut NPCMap) {
|
||||
for npc_id in npc_map.npc_ids.iter() {
|
||||
if let Some(npc) = npc_map.npcs.get_mut(npc_id) {
|
||||
if let Some(npc_cell) = npc_map.npcs.get(npc_id) {
|
||||
let npc = npc_cell.borrow_mut();
|
||||
if !npc.cond.alive() { continue; }
|
||||
|
||||
let mut flags = Flag(0);
|
||||
|
||||
if npc.npc_flags.solid_soft() {
|
||||
flags = self.judge_hit_npc_solid_soft(npc);
|
||||
flags = self.judge_hit_npc_solid_soft(npc.borrow());
|
||||
self.flags.0 |= flags.0;
|
||||
} else if npc.npc_flags.solid_hard() {
|
||||
//
|
||||
} else {
|
||||
flags = self.judge_hit_npc_non_solid(npc);
|
||||
flags = self.judge_hit_npc_non_solid(npc.borrow());
|
||||
}
|
||||
|
||||
if npc.npc_flags.interactable() && !state.control_flags.interactions_disabled() && flags.0 != 0 && self.cond.interacted() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use log::info;
|
||||
|
||||
use crate::bullet::BulletManager;
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::{Direction, FadeDirection, FadeState, Rect};
|
||||
use crate::entity::GameEntity;
|
||||
use crate::frame::Frame;
|
||||
|
@ -564,6 +565,92 @@ impl GameScene {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn tick_npc_bullet_collissions(&mut self, state: &mut SharedGameState) {
|
||||
let mut dead_npcs = Vec::new();
|
||||
|
||||
for npc_id in self.npc_map.npc_ids.iter() {
|
||||
if let Some(npc_cell) = self.npc_map.npcs.get(npc_id) {
|
||||
let mut npc = npc_cell.borrow_mut();
|
||||
if !npc.cond.alive() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if npc.npc_flags.shootable() && npc.npc_flags.interactable() {
|
||||
continue;
|
||||
}
|
||||
|
||||
for bullet in self.bullet_manager.bullets.iter_mut() {
|
||||
if bullet.damage < 1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let hit = (
|
||||
npc.npc_flags.shootable()
|
||||
&& (npc.x - npc.hit_bounds.right as isize) < (bullet.x + bullet.enemy_hit_width as isize)
|
||||
&& (npc.x + npc.hit_bounds.right as isize) > (bullet.x - bullet.enemy_hit_width as isize)
|
||||
&& (npc.y - npc.hit_bounds.top as isize) < (bullet.y + bullet.enemy_hit_height as isize)
|
||||
&& (npc.y + npc.hit_bounds.bottom as isize) > (bullet.y - bullet.enemy_hit_height as isize)
|
||||
) || (
|
||||
npc.npc_flags.invulnerable()
|
||||
&& (npc.x - npc.hit_bounds.right as isize) < (bullet.x + bullet.hit_bounds.right as isize)
|
||||
&& (npc.x + npc.hit_bounds.right as isize) > (bullet.x - bullet.hit_bounds.left as isize)
|
||||
&& (npc.y - npc.hit_bounds.top as isize) < (bullet.y + bullet.hit_bounds.bottom as isize)
|
||||
&& (npc.y + npc.hit_bounds.bottom as isize) > (bullet.y - bullet.hit_bounds.top as isize)
|
||||
);
|
||||
|
||||
if !hit {
|
||||
continue;
|
||||
}
|
||||
println!("npc hit: {}", npc.id);
|
||||
|
||||
if npc.npc_flags.shootable() {
|
||||
log::info!("damage: {} {}", npc.life, -(bullet.damage.min(npc.life) as isize));
|
||||
npc.life -= bullet.damage.min(npc.life);
|
||||
|
||||
if npc.life == 0 {
|
||||
if npc.npc_flags.show_damage() {
|
||||
// todo show damage
|
||||
}
|
||||
|
||||
if self.player.cond.alive() && npc.npc_flags.event_when_killed() {
|
||||
state.textscript_vm.start_script(npc.event_num);
|
||||
} else {
|
||||
npc.cond.set_explode_die(true);
|
||||
}
|
||||
} else {
|
||||
if npc.shock < 14 {
|
||||
// todo play hurt sound
|
||||
npc.shock = 16;
|
||||
}
|
||||
|
||||
if npc.npc_flags.show_damage() {
|
||||
// todo show damage
|
||||
}
|
||||
}
|
||||
} else if !bullet.flags.hit_right_slope() {
|
||||
state.create_caret((bullet.x + npc.x) / 2, (bullet.y + npc.y) / 2, CaretType::ProjectileDissipation, Direction::Right);
|
||||
// todo play sound 31
|
||||
bullet.life = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if bullet.life > 0 {
|
||||
bullet.life -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if npc.cond.explode_die() {
|
||||
dead_npcs.push(npc.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !dead_npcs.is_empty() {
|
||||
self.npc_map.process_dead_npcs(&dead_npcs, state);
|
||||
self.npc_map.garbage_collect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scene for GameScene {
|
||||
|
@ -596,7 +683,7 @@ impl Scene for GameScene {
|
|||
self.player.target_y = self.player.y;
|
||||
self.frame.immediate_update(state, &self.player, &self.stage);
|
||||
|
||||
self.inventory.add_weapon(WeaponType::PolarStar, 0);
|
||||
//self.inventory.add_weapon(WeaponType::PolarStar, 0);
|
||||
//self.player.equip.set_booster_2_0(true);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -605,26 +692,36 @@ impl Scene for GameScene {
|
|||
state.update_key_trigger();
|
||||
|
||||
if self.tick == 0 || state.control_flags.flag_x01() {
|
||||
self.player.current_weapon = {
|
||||
if let Some(weapon) = self.inventory.get_current_weapon_mut() {
|
||||
weapon.wtype as u8
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
self.player.tick(state, ())?;
|
||||
|
||||
self.player.flags.0 = 0;
|
||||
state.tick_carets();
|
||||
self.bullet_manager.tick_bullets(state, &self.stage);
|
||||
self.bullet_manager.tick_bullets(state, &mut self.stage);
|
||||
|
||||
self.player.tick_map_collisions(state, &self.stage);
|
||||
self.player.tick_map_collisions(state, &mut 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) {
|
||||
if let Some(npc_cell) = self.npc_map.npcs.get_mut(npc_id) {
|
||||
let mut npc = npc_cell.borrow_mut();
|
||||
npc.tick(state, &mut self.player)?;
|
||||
|
||||
if npc.cond.alive() && !npc.npc_flags.ignore_solidity() {
|
||||
npc.flags.0 = 0;
|
||||
npc.tick_map_collisions(state, &self.stage);
|
||||
npc.tick_map_collisions(state, &mut self.stage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.tick_npc_bullet_collissions(state);
|
||||
|
||||
self.frame.update(state, &self.player, &self.stage);
|
||||
}
|
||||
|
||||
|
@ -683,8 +780,8 @@ impl Scene for GameScene {
|
|||
self.draw_background(state, ctx)?;
|
||||
self.draw_tiles(state, ctx, TileLayer::Background)?;
|
||||
for npc_id in self.npc_map.npc_ids.iter() {
|
||||
if let Some(npc) = self.npc_map.npcs.get(npc_id) {
|
||||
npc.draw(state, ctx, &self.frame)?;
|
||||
if let Some(npc_cell) = self.npc_map.npcs.get(npc_id) {
|
||||
npc_cell.borrow().draw(state, ctx, &self.frame)?;
|
||||
}
|
||||
}
|
||||
self.player.draw(state, ctx, &self.frame)?;
|
||||
|
|
|
@ -868,7 +868,9 @@ impl TextScriptVM {
|
|||
game_scene.player.update_target = false;
|
||||
|
||||
for npc_id in game_scene.npc_map.npc_ids.iter() {
|
||||
if let Some(npc) = game_scene.npc_map.npcs.get_mut(npc_id) {
|
||||
if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) {
|
||||
let npc = npc_cell.borrow();
|
||||
|
||||
if event_num == npc.event_num {
|
||||
game_scene.player.target_x = npc.x;
|
||||
game_scene.player.target_y = npc.y;
|
||||
|
@ -885,7 +887,9 @@ impl TextScriptVM {
|
|||
let direction = read_cur_varint(&mut cursor)? as usize;
|
||||
|
||||
for npc_id in game_scene.npc_map.npc_ids.iter() {
|
||||
if let Some(npc) = game_scene.npc_map.npcs.get_mut(npc_id) {
|
||||
if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) {
|
||||
let mut npc = npc_cell.borrow_mut();
|
||||
|
||||
if npc.cond.alive() && npc.event_num == event_num {
|
||||
npc.action_num = action_num;
|
||||
|
||||
|
@ -912,7 +916,9 @@ impl TextScriptVM {
|
|||
let direction = read_cur_varint(&mut cursor)? as usize;
|
||||
|
||||
for npc_id in game_scene.npc_map.npc_ids.iter() {
|
||||
if let Some(npc) = game_scene.npc_map.npcs.get_mut(npc_id) {
|
||||
if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) {
|
||||
let mut npc = npc_cell.borrow_mut();
|
||||
|
||||
if npc.cond.alive() && npc.event_num == event_num {
|
||||
npc.npc_flags.set_solid_soft(false);
|
||||
npc.npc_flags.set_ignore_tile_44(false);
|
||||
|
@ -969,7 +975,9 @@ impl TextScriptVM {
|
|||
let direction = read_cur_varint(&mut cursor)? as usize;
|
||||
|
||||
for npc_id in game_scene.npc_map.npc_ids.iter() {
|
||||
if let Some(npc) = game_scene.npc_map.npcs.get_mut(npc_id) {
|
||||
if let Some(npc_cell) = game_scene.npc_map.npcs.get(npc_id) {
|
||||
let mut npc = npc_cell.borrow_mut();
|
||||
|
||||
if npc.cond.alive() && npc.event_num == event_num {
|
||||
npc.x = x * 16 * 0x200;
|
||||
npc.y = y * 16 * 0x200;
|
||||
|
@ -1118,8 +1126,8 @@ impl TextScriptVM {
|
|||
}
|
||||
|
||||
if tick_npc != 0 {
|
||||
if let Some(npc) = game_scene.npc_map.npcs.get_mut(&tick_npc) {
|
||||
npc.tick(state, &mut game_scene.player)?;
|
||||
if let Some(npc) = game_scene.npc_map.npcs.get(&tick_npc) {
|
||||
npc.borrow_mut().tick(state, &mut game_scene.player)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue