1
0
Fork 0
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:
Alula 2020-09-12 06:43:29 +02:00
parent 06aed8ad27
commit 519c7e6349
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
9 changed files with 501 additions and 66 deletions

View file

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

View file

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

View file

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

View file

@ -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() {

View file

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

View file

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

View file

@ -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() {

View file

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

View file

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