mirror of
https://github.com/doukutsu-rs/doukutsu-rs
synced 2025-03-24 10:59:20 +00:00
initial implementation of weapons and bullets
This commit is contained in:
parent
3df8781c25
commit
e143185f5d
251
src/bullet.rs
251
src/bullet.rs
|
@ -1,4 +1,43 @@
|
|||
use crate::common::{Condition, Direction, Rect, Flag};
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::{Condition, Direction, Flag, Rect};
|
||||
use crate::engine_constants::{BulletData, EngineConstants};
|
||||
use crate::physics::PhysicalEntity;
|
||||
use crate::SharedGameState;
|
||||
use crate::stage::Stage;
|
||||
|
||||
pub struct BulletManager {
|
||||
pub bullets: Vec<Bullet>,
|
||||
}
|
||||
|
||||
impl BulletManager {
|
||||
pub fn new() -> BulletManager {
|
||||
BulletManager {
|
||||
bullets: Vec::with_capacity(32),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_bullet(&mut self, x: isize, y: isize, btype: u16, direction: Direction, constants: &EngineConstants) {
|
||||
self.bullets.push(Bullet::new(x, y, btype, direction, constants));
|
||||
}
|
||||
|
||||
pub fn tick_bullets(&mut self, state: &mut SharedGameState, stage: &Stage) {
|
||||
for bullet in self.bullets.iter_mut() {
|
||||
bullet.tick(state);
|
||||
bullet.flags.0 = 0;
|
||||
bullet.tick_map_collisions(state, stage);
|
||||
}
|
||||
|
||||
self.bullets.retain(|b| !b.is_dead());
|
||||
}
|
||||
|
||||
pub fn count_bullets(&self, btype: u16) -> usize {
|
||||
self.bullets.iter().filter(|b| b.btype == btype).count()
|
||||
}
|
||||
|
||||
pub fn count_bullets_multi(&self, btypes: [u16; 3]) -> usize {
|
||||
self.bullets.iter().filter(|b| btypes.contains(&b.btype)).count()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Bullet {
|
||||
pub btype: u16,
|
||||
|
@ -17,15 +56,221 @@ pub struct Bullet {
|
|||
pub anim_rect: Rect<usize>,
|
||||
pub enemy_hit_width: u32,
|
||||
pub enemy_hit_height: u32,
|
||||
pub block_hit_width: u32,
|
||||
pub block_hit_height: u32,
|
||||
pub anim_num: u16,
|
||||
pub anim_counter: u16,
|
||||
pub action_num: u16,
|
||||
pub action_counter: u16,
|
||||
pub hit_bounds: Rect<usize>,
|
||||
pub display_bounds: Rect<usize>,
|
||||
}
|
||||
|
||||
impl Bullet {
|
||||
pub fn new(x: isize, y: isize, btype: u16, direction: Direction, constants: &EngineConstants) -> Bullet {
|
||||
let bullet = constants.weapon.bullet_table
|
||||
.get(btype as usize)
|
||||
.unwrap_or_else(|| &BulletData {
|
||||
damage: 0,
|
||||
life: 0,
|
||||
lifetime: 0,
|
||||
flags: Flag(0),
|
||||
enemy_hit_width: 0,
|
||||
enemy_hit_height: 0,
|
||||
block_hit_width: 0,
|
||||
block_hit_height: 0,
|
||||
display_bounds: Rect { left: 0, top: 0, right: 0, bottom: 0 },
|
||||
});
|
||||
|
||||
Bullet {
|
||||
btype,
|
||||
x,
|
||||
y,
|
||||
vel_x: 0,
|
||||
vel_y: 0,
|
||||
target_x: 0,
|
||||
target_y: 0,
|
||||
life: bullet.life as u16,
|
||||
lifetime: bullet.lifetime,
|
||||
damage: bullet.damage as u16,
|
||||
cond: Condition(0x80),
|
||||
flags: bullet.flags,
|
||||
direction,
|
||||
anim_rect: Rect::new(0, 0, 0, 0),
|
||||
enemy_hit_width: bullet.enemy_hit_width as u32 * 0x200,
|
||||
enemy_hit_height: bullet.enemy_hit_height as u32 * 0x200,
|
||||
anim_num: 0,
|
||||
anim_counter: 0,
|
||||
action_num: 0,
|
||||
action_counter: 0,
|
||||
display_bounds: Rect::new(
|
||||
bullet.display_bounds.left as usize * 0x200,
|
||||
bullet.display_bounds.top as usize * 0x200,
|
||||
bullet.display_bounds.right as usize * 0x200,
|
||||
bullet.display_bounds.bottom as usize * 0x200,
|
||||
),
|
||||
hit_bounds: Rect::new(
|
||||
bullet.block_hit_width as usize * 0x200,
|
||||
bullet.block_hit_height as usize * 0x200,
|
||||
bullet.block_hit_width as usize * 0x200,
|
||||
bullet.block_hit_height as usize * 0x200,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_dead(&self) -> bool {
|
||||
!self.cond.alive()
|
||||
}
|
||||
|
||||
fn tick_polar_star(&mut self, state: &mut SharedGameState) {
|
||||
self.action_counter += 1;
|
||||
if self.action_counter > self.lifetime {
|
||||
self.cond.set_alive(false);
|
||||
state.create_caret(self.x, self.y, CaretType::Shoot, Direction::Left);
|
||||
return;
|
||||
}
|
||||
|
||||
if self.action_num == 0 {
|
||||
self.action_num = 1;
|
||||
|
||||
match self.direction {
|
||||
Direction::Left => { self.vel_x = -0x1000 }
|
||||
Direction::Up => { self.vel_y = -0x1000 }
|
||||
Direction::Right => { self.vel_x = 0x1000 }
|
||||
Direction::Bottom => { self.vel_y = 0x1000 }
|
||||
}
|
||||
|
||||
match self.btype {
|
||||
4 => {
|
||||
match self.direction {
|
||||
Direction::Left | Direction::Right => {
|
||||
self.enemy_hit_height = 0x400;
|
||||
}
|
||||
Direction::Up | Direction::Bottom => {
|
||||
self.enemy_hit_width = 0x400;
|
||||
}
|
||||
}
|
||||
}
|
||||
5 => {
|
||||
match self.direction {
|
||||
Direction::Left | Direction::Right => {
|
||||
self.enemy_hit_height = 0x800;
|
||||
}
|
||||
Direction::Up | Direction::Bottom => {
|
||||
self.enemy_hit_width = 0x800;
|
||||
}
|
||||
}
|
||||
}
|
||||
6 => {
|
||||
// level 3 uses default values
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
}
|
||||
} else {
|
||||
self.x += self.vel_x;
|
||||
self.y += self.vel_y;
|
||||
}
|
||||
|
||||
match self.btype {
|
||||
4 => {
|
||||
if self.direction == Direction::Up || self.direction == Direction::Bottom {
|
||||
self.anim_num = 1;
|
||||
self.anim_rect = state.constants.weapon.bullet_rects.b004_polar_star_l1[1];
|
||||
} else {
|
||||
self.anim_num = 0;
|
||||
self.anim_rect = state.constants.weapon.bullet_rects.b004_polar_star_l1[0];
|
||||
}
|
||||
}
|
||||
5 => {
|
||||
if self.direction == Direction::Up || self.direction == Direction::Bottom {
|
||||
self.anim_num = 1;
|
||||
self.anim_rect = state.constants.weapon.bullet_rects.b005_polar_star_l2[1];
|
||||
} else {
|
||||
self.anim_num = 0;
|
||||
self.anim_rect = state.constants.weapon.bullet_rects.b005_polar_star_l2[0];
|
||||
}
|
||||
}
|
||||
6 => {
|
||||
if self.direction == Direction::Up || self.direction == Direction::Bottom {
|
||||
self.anim_num = 1;
|
||||
self.anim_rect = state.constants.weapon.bullet_rects.b006_polar_star_l3[1];
|
||||
} else {
|
||||
self.anim_num = 0;
|
||||
self.anim_rect = state.constants.weapon.bullet_rects.b006_polar_star_l3[0];
|
||||
}
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, state: &mut SharedGameState) {
|
||||
if self.lifetime == 0 {
|
||||
self.cond.set_alive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
match self.btype {
|
||||
4 | 5 | 6 => {
|
||||
self.tick_polar_star(state);
|
||||
}
|
||||
_ => { self.cond.set_alive(false); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PhysicalEntity for Bullet {
|
||||
fn x(&self) -> isize {
|
||||
self.x
|
||||
}
|
||||
|
||||
fn y(&self) -> isize {
|
||||
self.y
|
||||
}
|
||||
|
||||
fn vel_x(&self) -> isize {
|
||||
self.vel_x
|
||||
}
|
||||
|
||||
fn vel_y(&self) -> isize {
|
||||
self.vel_y
|
||||
}
|
||||
|
||||
fn size(&self) -> u8 {
|
||||
1
|
||||
}
|
||||
|
||||
fn hit_bounds(&self) -> &Rect<usize> {
|
||||
&self.hit_bounds
|
||||
}
|
||||
|
||||
fn set_x(&mut self, x: isize) {
|
||||
self.x = x;
|
||||
}
|
||||
|
||||
fn set_y(&mut self, y: isize) {
|
||||
self.y = y;
|
||||
}
|
||||
|
||||
fn set_vel_x(&mut self, vel_x: isize) {
|
||||
self.vel_x = vel_x;
|
||||
}
|
||||
|
||||
fn set_vel_y(&mut self, vel_y: isize) {
|
||||
self.vel_y = vel_y;
|
||||
}
|
||||
|
||||
fn cond(&mut self) -> &mut Condition {
|
||||
&mut self.cond
|
||||
}
|
||||
|
||||
fn flags(&mut self) -> &mut Flag {
|
||||
&mut self.flags
|
||||
}
|
||||
|
||||
fn is_player(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/*fn judge_hit_block(&mut self, state: &SharedGameState, x: isize, y: isize) {
|
||||
|
||||
}*/
|
||||
}
|
||||
|
|
100
src/caret.rs
100
src/caret.rs
|
@ -2,6 +2,7 @@ use crate::bitfield;
|
|||
use crate::common::{Condition, Direction, Rect};
|
||||
use crate::engine_constants::EngineConstants;
|
||||
use crate::rng::RNG;
|
||||
use std::fs::read_to_string;
|
||||
|
||||
#[derive(Debug, EnumIter, PartialEq, Eq, Hash, Copy, Clone)]
|
||||
pub enum CaretType {
|
||||
|
@ -36,14 +37,15 @@ pub struct Caret {
|
|||
pub cond: Condition,
|
||||
pub direction: Direction,
|
||||
pub anim_rect: Rect<usize>,
|
||||
anim_num: usize,
|
||||
anim_counter: isize,
|
||||
anim_num: u16,
|
||||
anim_counter: u16,
|
||||
}
|
||||
|
||||
impl Caret {
|
||||
pub fn new(x: isize, y: isize, ctype: CaretType, direct: Direction, constants: &EngineConstants) -> Self {
|
||||
pub fn new(x: isize, y: isize, ctype: CaretType, direct: Direction, constants: &EngineConstants) -> Caret {
|
||||
let (offset_x, offset_y) = constants.caret.offsets[ctype as usize];
|
||||
Self {
|
||||
|
||||
Caret {
|
||||
ctype,
|
||||
x,
|
||||
y,
|
||||
|
@ -53,7 +55,7 @@ impl Caret {
|
|||
offset_y,
|
||||
cond: Condition(0x80),
|
||||
direction: direct,
|
||||
anim_rect: Rect::<usize>::new(0, 0, 0, 0),
|
||||
anim_rect: Rect::new(0, 0, 0, 0),
|
||||
anim_num: 0,
|
||||
anim_counter: 0,
|
||||
}
|
||||
|
@ -63,13 +65,73 @@ impl Caret {
|
|||
match self.ctype {
|
||||
CaretType::None => {}
|
||||
CaretType::Bubble => {}
|
||||
CaretType::ProjectileDissipation => {}
|
||||
CaretType::Shoot => {}
|
||||
CaretType::SnakeAfterimage | CaretType::SnakeAfterimage2 => { // dupe, unused
|
||||
CaretType::ProjectileDissipation => {
|
||||
match self.direction {
|
||||
Direction::Left => {
|
||||
self.vel_y -= 0x10;
|
||||
self.y += self.vel_y;
|
||||
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 5 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
|
||||
if self.anim_num > constants.caret.projectile_dissipation_left_rects.len() as u16 {
|
||||
self.cond.set_alive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
self.anim_rect = constants.caret.projectile_dissipation_left_rects[self.anim_num as usize];
|
||||
}
|
||||
},
|
||||
Direction::Up => {
|
||||
self.anim_counter += 1;
|
||||
|
||||
if self.anim_counter > 24 {
|
||||
self.cond.set_alive(false);
|
||||
}
|
||||
|
||||
let len = constants.caret.projectile_dissipation_up_rects.len();
|
||||
self.anim_rect = constants.caret.projectile_dissipation_up_rects[(self.anim_num as usize / 2) % len];
|
||||
},
|
||||
Direction::Right => {
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 2 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
|
||||
if self.anim_num > constants.caret.projectile_dissipation_right_rects.len() as u16 {
|
||||
self.cond.set_alive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
self.anim_rect = constants.caret.projectile_dissipation_right_rects[self.anim_num as usize];
|
||||
}
|
||||
},
|
||||
Direction::Bottom => {
|
||||
self.cond.set_alive(false);
|
||||
},
|
||||
}
|
||||
}
|
||||
CaretType::Shoot => {
|
||||
if self.anim_counter == 0 {
|
||||
self.anim_rect = constants.caret.shoot_rects[self.anim_num as usize];
|
||||
}
|
||||
|
||||
self.anim_counter += 1;
|
||||
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);
|
||||
}
|
||||
}
|
||||
CaretType::SnakeAfterimage | CaretType::SnakeAfterimage2 => {} // dupe, unused
|
||||
CaretType::Zzz => {
|
||||
if self.anim_counter == 0 {
|
||||
self.anim_rect = constants.caret.zzz_rects[self.anim_num];
|
||||
self.anim_rect = constants.caret.zzz_rects[self.anim_num as usize];
|
||||
}
|
||||
|
||||
self.anim_counter += 1;
|
||||
|
@ -78,27 +140,30 @@ impl Caret {
|
|||
self.anim_num += 1;
|
||||
}
|
||||
|
||||
if self.anim_num == constants.caret.zzz_rects.len() {
|
||||
if self.anim_num == constants.caret.zzz_rects.len() as u16 {
|
||||
self.cond.set_alive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
self.x += 0x80; // 0.4fix9
|
||||
self.y -= 0x80;
|
||||
}
|
||||
CaretType::Exhaust => {
|
||||
if self.anim_counter == 0 {
|
||||
self.anim_rect = constants.caret.exhaust_rects[self.anim_num as usize];
|
||||
}
|
||||
|
||||
self.anim_counter += 1;
|
||||
if self.anim_counter > 1 {
|
||||
self.anim_counter = 0;
|
||||
self.anim_num += 1;
|
||||
}
|
||||
|
||||
if self.anim_num >= constants.caret.exhaust_rects.len() {
|
||||
self.cond.set_alive(false);
|
||||
return;
|
||||
if self.anim_num >= constants.caret.exhaust_rects.len() as u16 {
|
||||
self.cond.set_alive(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.anim_rect = constants.caret.exhaust_rects[self.anim_num];
|
||||
|
||||
match self.direction {
|
||||
Direction::Left => { self.x -= 0x400; } // 2.0fix9
|
||||
Direction::Up => { self.y -= 0x400; }
|
||||
|
@ -165,7 +230,8 @@ impl Caret {
|
|||
return;
|
||||
}
|
||||
|
||||
self.anim_rect = constants.caret.little_particles_rects[self.anim_num / 2 % constants.caret.little_particles_rects.len()];
|
||||
let len = constants.caret.little_particles_rects.len();
|
||||
self.anim_rect = constants.caret.little_particles_rects[self.anim_num as usize / 2 % len];
|
||||
|
||||
if self.direction == Direction::Right {
|
||||
self.x -= 4 * 0x200;
|
||||
|
|
|
@ -13,10 +13,10 @@ bitfield! {
|
|||
pub hit_bottom_wall, set_hit_bottom_wall: 3; // 0x08
|
||||
pub hit_right_slope, set_hit_right_slope: 4; // 0x10
|
||||
pub hit_left_slope, set_hit_left_slope: 5; // 0x20
|
||||
pub flag_x40, set_flag_x40: 6; // 0x40
|
||||
pub snack_destroy, set_snack_destroy: 6; // 0x40
|
||||
pub flag_x80, set_flag_x80: 7; // 0x80
|
||||
pub in_water, set_in_water: 8; // 0x100
|
||||
pub flag_x200, set_flag_x200: 9; // 0x200
|
||||
pub weapon_hit_block, set_weapon_hit_block: 9; // 0x200
|
||||
pub hit_by_spike, set_hit_by_spike: 10; // 0x400
|
||||
pub water_splash_facing_right, set_water_splash_facing_right: 11; // 0x800
|
||||
pub force_left, set_force_left: 12; // 0x1000
|
||||
|
@ -84,7 +84,7 @@ bitfield! {
|
|||
#[derive(Clone, Copy)]
|
||||
pub struct ControlFlags(u16);
|
||||
impl Debug;
|
||||
|
||||
|
||||
pub flag_x01, set_flag_x01: 0;
|
||||
pub control_enabled, set_control_enabled: 1;
|
||||
pub interactions_disabled, set_interactions_disabled: 2;
|
||||
|
|
|
@ -48,6 +48,10 @@ pub struct CaretConsts {
|
|||
pub offsets: [(isize, isize); 18],
|
||||
pub bubble_left_rects: Vec<Rect<usize>>,
|
||||
pub bubble_right_rects: Vec<Rect<usize>>,
|
||||
pub projectile_dissipation_left_rects: Vec<Rect<usize>>,
|
||||
pub projectile_dissipation_right_rects: Vec<Rect<usize>>,
|
||||
pub projectile_dissipation_up_rects: Vec<Rect<usize>>,
|
||||
pub shoot_rects: Vec<Rect<usize>>,
|
||||
pub zzz_rects: Vec<Rect<usize>>,
|
||||
pub drowned_quote_left_rect: Rect<usize>,
|
||||
pub drowned_quote_right_rect: Rect<usize>,
|
||||
|
@ -65,6 +69,10 @@ impl Clone for CaretConsts {
|
|||
offsets: self.offsets,
|
||||
bubble_left_rects: self.bubble_left_rects.clone(),
|
||||
bubble_right_rects: self.bubble_right_rects.clone(),
|
||||
projectile_dissipation_left_rects: self.projectile_dissipation_left_rects.clone(),
|
||||
projectile_dissipation_right_rects: self.projectile_dissipation_right_rects.clone(),
|
||||
projectile_dissipation_up_rects: self.projectile_dissipation_up_rects.clone(),
|
||||
shoot_rects: self.shoot_rects.clone(),
|
||||
zzz_rects: self.zzz_rects.clone(),
|
||||
drowned_quote_left_rect: self.drowned_quote_left_rect,
|
||||
drowned_quote_right_rect: self.drowned_quote_right_rect,
|
||||
|
@ -92,15 +100,24 @@ pub struct BulletData {
|
|||
pub display_bounds: Rect<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct BulletRects {
|
||||
pub b004_polar_star_l1: [Rect<usize>; 2],
|
||||
pub b005_polar_star_l2: [Rect<usize>; 2],
|
||||
pub b006_polar_star_l3: [Rect<usize>; 2],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WeaponConsts {
|
||||
pub bullet_table: Vec<BulletData>,
|
||||
pub bullet_rects: BulletRects,
|
||||
}
|
||||
|
||||
impl Clone for WeaponConsts {
|
||||
fn clone(&self) -> WeaponConsts {
|
||||
WeaponConsts {
|
||||
bullet_table: self.bullet_table.clone(),
|
||||
bullet_rects: self.bullet_rects,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -316,6 +333,29 @@ impl EngineConstants {
|
|||
Rect { left: 80, top: 24, right: 88, bottom: 32 },
|
||||
Rect { left: 88, top: 24, right: 96, bottom: 32 },
|
||||
],
|
||||
projectile_dissipation_left_rects: vec![
|
||||
Rect { left: 0, top: 32, right: 16, bottom: 48 },
|
||||
Rect { left: 16, top: 32, right: 32, bottom: 48 },
|
||||
Rect { left: 32, top: 32, right: 48, bottom: 48 },
|
||||
Rect { left: 48, top: 32, right: 64, bottom: 48 },
|
||||
],
|
||||
projectile_dissipation_right_rects: vec![
|
||||
Rect { left: 176, top: 0, right: 192, bottom: 16 },
|
||||
Rect { left: 192, top: 0, right: 208, bottom: 16 },
|
||||
Rect { left: 208, top: 0, right: 224, bottom: 16 },
|
||||
Rect { left: 224, top: 0, right: 240, bottom: 16 },
|
||||
],
|
||||
projectile_dissipation_up_rects: vec![
|
||||
Rect { left: 0, top: 32, right: 16, bottom: 48 },
|
||||
Rect { left: 32, top: 32, right: 48, bottom: 48 },
|
||||
Rect { left: 16, top: 32, right: 32, bottom: 48 },
|
||||
],
|
||||
shoot_rects: vec![
|
||||
Rect { left: 0, top: 48, right: 16, bottom: 64 },
|
||||
Rect { left: 16, top: 48, right: 32, bottom: 64 },
|
||||
Rect { left: 32, top: 48, right: 48, bottom: 64 },
|
||||
Rect { left: 48, top: 48, right: 64, bottom: 64 },
|
||||
],
|
||||
zzz_rects: vec![
|
||||
Rect { left: 32, top: 64, right: 40, bottom: 72 },
|
||||
Rect { left: 32, top: 72, right: 40, bottom: 80 },
|
||||
|
@ -824,6 +864,20 @@ impl EngineConstants {
|
|||
// Whimsical Star
|
||||
BulletData { damage: 1, life: 1, lifetime: 1, flags: Flag(36), enemy_hit_width: 1, enemy_hit_height: 1, block_hit_width: 1, block_hit_height: 1, display_bounds: Rect { left: 1, top: 1, right: 1, bottom: 1 } },
|
||||
],
|
||||
bullet_rects: BulletRects {
|
||||
b004_polar_star_l1: [
|
||||
Rect { left: 128, top: 32, right: 144, bottom: 48 },
|
||||
Rect { left: 144, top: 32, right: 160, bottom: 48 },
|
||||
],
|
||||
b005_polar_star_l2: [
|
||||
Rect { left: 160, top: 32, right: 176, bottom: 48 },
|
||||
Rect { left: 176, top: 32, right: 192, bottom: 48 },
|
||||
],
|
||||
b006_polar_star_l3: [
|
||||
Rect { left: 128, top: 48, right: 144, bottom: 64 },
|
||||
Rect { left: 144, top: 48, right: 160, bottom: 64 },
|
||||
],
|
||||
},
|
||||
},
|
||||
tex_sizes: case_insensitive_hashmap! {
|
||||
"ArmsImage" => (256, 16),
|
||||
|
|
|
@ -1,11 +1,4 @@
|
|||
#[derive(Clone)]
|
||||
pub struct Weapon {
|
||||
pub id: u16,
|
||||
pub level: u16,
|
||||
pub experience: u16,
|
||||
pub ammo: u16,
|
||||
pub max_ammo: u16,
|
||||
}
|
||||
use crate::weapon::{Weapon, WeaponLevel, WeaponType};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Item(u16);
|
||||
|
@ -43,20 +36,20 @@ impl Inventory {
|
|||
self.items.iter().any(|item| item.0 == item_id)
|
||||
}
|
||||
|
||||
pub fn add_weapon(&mut self, weapon_id: u16, max_ammo: u16) {
|
||||
pub fn add_weapon(&mut self, weapon_id: WeaponType, max_ammo: u16) {
|
||||
if !self.has_weapon(weapon_id) {
|
||||
self.weapons.push(Weapon {
|
||||
id: weapon_id,
|
||||
level: 1,
|
||||
experience: 0,
|
||||
ammo: max_ammo,
|
||||
self.weapons.push(Weapon::new(
|
||||
weapon_id,
|
||||
WeaponLevel::Level1,
|
||||
0,
|
||||
max_ammo,
|
||||
});
|
||||
max_ammo,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_weapon(&mut self, weapon_id: u16) {
|
||||
self.weapons.retain(|weapon| weapon.id != weapon_id);
|
||||
pub fn remove_weapon(&mut self, wtype: WeaponType) {
|
||||
self.weapons.retain(|weapon| weapon.wtype != wtype);
|
||||
}
|
||||
|
||||
pub fn get_weapon(&self, idx: usize) -> Option<&Weapon> {
|
||||
|
@ -75,7 +68,7 @@ impl Inventory {
|
|||
|
||||
pub fn reset_all_weapon_xp(&mut self) {
|
||||
for weapon in self.weapons.iter_mut() {
|
||||
weapon.level = 1;
|
||||
weapon.level = WeaponLevel::Level1;
|
||||
weapon.experience = 0;
|
||||
}
|
||||
}
|
||||
|
@ -88,11 +81,11 @@ impl Inventory {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_current_level(&self) -> u16 {
|
||||
pub fn get_current_level(&self) -> WeaponLevel {
|
||||
if let Some(weapon) = self.weapons.get(self.current_weapon as usize) {
|
||||
weapon.level
|
||||
} else {
|
||||
0
|
||||
WeaponLevel::None
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +97,7 @@ impl Inventory {
|
|||
self.weapons.len()
|
||||
}
|
||||
|
||||
pub fn has_weapon(&self, weapon_id: u16) -> bool {
|
||||
self.weapons.iter().any(|weapon| weapon.id == weapon_id)
|
||||
pub fn has_weapon(&self, wtype: WeaponType) -> bool {
|
||||
self.weapons.iter().any(|weapon| weapon.wtype == wtype)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ use winit::{ElementState, Event, KeyboardInput, WindowEvent};
|
|||
use crate::bmfont_renderer::BMFontRenderer;
|
||||
use crate::builtin_fs::BuiltinFS;
|
||||
use crate::caret::{Caret, CaretType};
|
||||
use crate::common::{Direction, FadeState, KeyState, ControlFlags};
|
||||
use crate::common::{ControlFlags, Direction, FadeState, KeyState};
|
||||
use crate::engine_constants::EngineConstants;
|
||||
use crate::ggez::{Context, ContextBuilder, event, filesystem, GameResult};
|
||||
use crate::ggez::conf::{WindowMode, WindowSetup};
|
||||
|
@ -34,6 +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::rng::RNG;
|
||||
use crate::scene::loading_scene::LoadingScene;
|
||||
use crate::scene::Scene;
|
||||
|
@ -42,7 +43,6 @@ use crate::stage::StageData;
|
|||
use crate::text_script::TextScriptVM;
|
||||
use crate::texture_set::TextureSet;
|
||||
use crate::ui::UI;
|
||||
use crate::npc::NPCTable;
|
||||
|
||||
mod bmfont;
|
||||
mod bmfont_renderer;
|
||||
|
@ -53,6 +53,7 @@ mod common;
|
|||
mod engine_constants;
|
||||
mod entity;
|
||||
mod frame;
|
||||
mod inventory;
|
||||
mod ggez;
|
||||
mod live_debugger;
|
||||
mod macros;
|
||||
|
@ -68,7 +69,7 @@ mod sound;
|
|||
mod text_script;
|
||||
mod texture_set;
|
||||
mod ui;
|
||||
mod inventory;
|
||||
mod weapon;
|
||||
|
||||
struct Game {
|
||||
scene: Option<Box<dyn Scene>>,
|
||||
|
|
|
@ -327,6 +327,7 @@ pub struct NPCTable {
|
|||
}
|
||||
|
||||
impl NPCTable {
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> NPCTable {
|
||||
NPCTable {
|
||||
entries: Vec::new(),
|
||||
|
|
|
@ -32,7 +32,6 @@ pub struct Player {
|
|||
pub cond: Condition,
|
||||
pub flags: Flag,
|
||||
pub equip: Equipment,
|
||||
pub inventory: Inventory,
|
||||
pub direction: Direction,
|
||||
pub display_bounds: Rect<usize>,
|
||||
pub hit_bounds: Rect<usize>,
|
||||
|
@ -72,7 +71,6 @@ impl Player {
|
|||
cond: Condition(0x80),
|
||||
flags: Flag(0),
|
||||
equip: Equipment(0),
|
||||
inventory: Inventory::new(),
|
||||
direction: Direction::Right,
|
||||
display_bounds: constants.my_char.display_bounds,
|
||||
hit_bounds: constants.my_char.hit_bounds,
|
||||
|
|
|
@ -1,28 +1,32 @@
|
|||
use log::info;
|
||||
|
||||
use crate::common::{FadeDirection, FadeState, Rect};
|
||||
use crate::bullet::BulletManager;
|
||||
use crate::common::{Direction, FadeDirection, FadeState, Rect};
|
||||
use crate::entity::GameEntity;
|
||||
use crate::frame::Frame;
|
||||
use crate::ggez::{Context, GameResult, graphics, timer};
|
||||
use crate::ggez::graphics::{Color, Drawable, DrawParam, Text, TextFragment};
|
||||
use crate::ggez::graphics::Color;
|
||||
use crate::ggez::nalgebra::clamp;
|
||||
use crate::inventory::Inventory;
|
||||
use crate::npc::NPCMap;
|
||||
use crate::physics::PhysicalEntity;
|
||||
use crate::player::Player;
|
||||
use crate::scene::Scene;
|
||||
use crate::SharedGameState;
|
||||
use crate::stage::{BackgroundType, Stage};
|
||||
use crate::str;
|
||||
use crate::text_script::{ConfirmSelection, TextScriptExecutionState, TextScriptVM};
|
||||
use crate::ui::Components;
|
||||
use crate::weapon::WeaponType;
|
||||
|
||||
pub struct GameScene {
|
||||
pub tick: usize,
|
||||
pub stage: Stage,
|
||||
pub frame: Frame,
|
||||
pub player: Player,
|
||||
pub inventory: Inventory,
|
||||
pub stage_id: usize,
|
||||
pub npc_map: NPCMap,
|
||||
pub bullet_manager: BulletManager,
|
||||
tex_background_name: String,
|
||||
tex_tileset_name: String,
|
||||
life_bar: u16,
|
||||
|
@ -58,6 +62,7 @@ impl GameScene {
|
|||
tick: 0,
|
||||
stage,
|
||||
player: Player::new(state),
|
||||
inventory: Inventory::new(),
|
||||
frame: Frame {
|
||||
x: 0,
|
||||
y: 0,
|
||||
|
@ -65,6 +70,7 @@ impl GameScene {
|
|||
},
|
||||
stage_id: id,
|
||||
npc_map: NPCMap::new(),
|
||||
bullet_manager: BulletManager::new(),
|
||||
tex_background_name,
|
||||
tex_tileset_name,
|
||||
life_bar: 0,
|
||||
|
@ -95,7 +101,7 @@ impl GameScene {
|
|||
fn draw_hud(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
// none
|
||||
let weap_x = self.weapon_x_pos as f32;
|
||||
let (ammo, max_ammo) = self.player.inventory.get_current_ammo();
|
||||
let (ammo, max_ammo) = self.inventory.get_current_ammo();
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "TextBox")?;
|
||||
|
||||
if max_ammo == 0 {
|
||||
|
@ -133,9 +139,9 @@ impl GameScene {
|
|||
batch.draw(ctx)?;
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "ArmsImage")?;
|
||||
|
||||
let weapon_count = self.player.inventory.get_weapon_count();
|
||||
let weapon_count = self.inventory.get_weapon_count();
|
||||
if weapon_count != 0 {
|
||||
let current_weapon = self.player.inventory.get_current_weapon_idx() as usize;
|
||||
let current_weapon = self.inventory.get_current_weapon_idx() as usize;
|
||||
let mut rect = Rect::new(0, 0, 0, 16);
|
||||
|
||||
for a in 0..weapon_count {
|
||||
|
@ -153,8 +159,8 @@ impl GameScene {
|
|||
pos_x -= 48.0;
|
||||
}
|
||||
|
||||
if let Some(weapon) = self.player.inventory.get_weapon(a) {
|
||||
rect.left = weapon.id as usize * 16;
|
||||
if let Some(weapon) = self.inventory.get_weapon(a) {
|
||||
rect.left = weapon.wtype as usize * 16;
|
||||
rect.right = rect.left + 16;
|
||||
batch.add_rect(pos_x, 16.0, &rect);
|
||||
}
|
||||
|
@ -167,7 +173,7 @@ impl GameScene {
|
|||
self.draw_number(weap_x + 64.0, 16.0, ammo as usize, Alignment::Right, state, ctx)?;
|
||||
self.draw_number(weap_x + 64.0, 24.0, max_ammo as usize, Alignment::Right, state, ctx)?;
|
||||
}
|
||||
self.draw_number(40.0, 32.0, self.player.inventory.get_current_level() as usize, Alignment::Right, state, ctx)?;
|
||||
self.draw_number(40.0, 32.0, self.inventory.get_current_level() as usize, Alignment::Right, state, ctx)?;
|
||||
self.draw_number(40.0, 40.0, self.life_bar as usize, Alignment::Right, state, ctx)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -187,23 +193,18 @@ impl GameScene {
|
|||
}
|
||||
}
|
||||
}
|
||||
BackgroundType::MoveDistant => {
|
||||
let off_x = self.frame.x as usize / 2 % (batch.width() * 0x200);
|
||||
let off_y = self.frame.y as usize / 2 % (batch.height() * 0x200);
|
||||
|
||||
let count_x = state.canvas_size.0 as usize / batch.width() + 2;
|
||||
let count_y = state.canvas_size.1 as usize / batch.height() + 2;
|
||||
|
||||
for y in 0..count_y {
|
||||
for x in 0..count_x {
|
||||
batch.add((x * batch.width()) as f32 - (off_x / 0x200) as f32,
|
||||
(y * batch.height()) as f32 - (off_y / 0x200) as f32);
|
||||
}
|
||||
}
|
||||
}
|
||||
BackgroundType::MoveNear => {
|
||||
let off_x = self.frame.x as usize % (batch.width() * 0x200);
|
||||
let off_y = self.frame.y as usize % (batch.height() * 0x200);
|
||||
BackgroundType::MoveDistant | BackgroundType::MoveNear => {
|
||||
let (off_x, off_y) = if self.stage.data.background_type == BackgroundType::MoveDistant {
|
||||
(
|
||||
self.frame.x as usize % (batch.width() * 0x200),
|
||||
self.frame.y as usize % (batch.height() * 0x200)
|
||||
)
|
||||
} else {
|
||||
(
|
||||
self.frame.x as usize / 2 % (batch.width() * 0x200),
|
||||
self.frame.y as usize / 2 % (batch.height() * 0x200)
|
||||
)
|
||||
};
|
||||
|
||||
let count_x = state.canvas_size.0 as usize / batch.width() + 2;
|
||||
let count_y = state.canvas_size.1 as usize / batch.height() + 2;
|
||||
|
@ -253,6 +254,40 @@ impl GameScene {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_bullets(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "Bullet")?;
|
||||
let mut x: isize;
|
||||
let mut y: isize;
|
||||
|
||||
for bullet in self.bullet_manager.bullets.iter() {
|
||||
match bullet.direction {
|
||||
Direction::Left => {
|
||||
x = bullet.x - bullet.display_bounds.left as isize;
|
||||
y = bullet.y - bullet.display_bounds.top as isize;
|
||||
}
|
||||
Direction::Up => {
|
||||
x = bullet.x - bullet.display_bounds.top as isize;
|
||||
y = bullet.y - bullet.display_bounds.left as isize;
|
||||
}
|
||||
Direction::Right => {
|
||||
x = bullet.x - bullet.display_bounds.right as isize;
|
||||
y = bullet.y - bullet.display_bounds.top as isize;
|
||||
}
|
||||
Direction::Bottom => {
|
||||
x = bullet.x - bullet.display_bounds.top as isize;
|
||||
y = bullet.y - bullet.display_bounds.right as isize;
|
||||
}
|
||||
}
|
||||
|
||||
batch.add_rect(((x / 0x200) - (self.frame.x / 0x200)) as f32,
|
||||
((y / 0x200) - (self.frame.y / 0x200)) as f32,
|
||||
&bullet.anim_rect);
|
||||
}
|
||||
|
||||
batch.draw(ctx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn draw_carets(&self, state: &mut SharedGameState, ctx: &mut Context) -> GameResult {
|
||||
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "Caret")?;
|
||||
|
||||
|
@ -560,6 +595,8 @@ impl Scene for GameScene {
|
|||
self.player.target_x = self.player.x;
|
||||
self.player.target_y = self.player.y;
|
||||
self.frame.immediate_update(state, &self.player, &self.stage);
|
||||
|
||||
self.inventory.add_weapon(WeaponType::PolarStar, 0);
|
||||
//self.player.equip.set_booster_2_0(true);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -572,6 +609,7 @@ impl Scene for GameScene {
|
|||
|
||||
self.player.flags.0 = 0;
|
||||
state.tick_carets();
|
||||
self.bullet_manager.tick_bullets(state, &self.stage);
|
||||
|
||||
self.player.tick_map_collisions(state, &self.stage);
|
||||
self.player.tick_npc_collisions(state, &mut self.npc_map);
|
||||
|
@ -591,6 +629,10 @@ impl Scene for GameScene {
|
|||
}
|
||||
|
||||
if state.control_flags.control_enabled() {
|
||||
if let Some(weapon) = self.inventory.get_current_weapon_mut() {
|
||||
weapon.shoot_bullet(&self.player, &mut self.bullet_manager, state);
|
||||
}
|
||||
|
||||
// update health bar
|
||||
if self.life_bar < self.player.life as u16 {
|
||||
self.life_bar = self.player.life as u16;
|
||||
|
@ -646,6 +688,7 @@ impl Scene for GameScene {
|
|||
}
|
||||
}
|
||||
self.player.draw(state, ctx, &self.frame)?;
|
||||
self.draw_bullets(state, ctx)?;
|
||||
self.draw_tiles(state, ctx, TileLayer::Foreground)?;
|
||||
self.draw_tiles(state, ctx, TileLayer::Snack)?;
|
||||
self.draw_carets(state, ctx)?;
|
||||
|
|
|
@ -20,6 +20,7 @@ use crate::ggez::{Context, GameResult};
|
|||
use crate::ggez::GameError::ParseError;
|
||||
use crate::player::ControlMode;
|
||||
use crate::scene::game_scene::GameScene;
|
||||
use crate::weapon::WeaponType;
|
||||
|
||||
/// Engine's text script VM operation codes.
|
||||
#[derive(EnumString, Debug, FromPrimitive, PartialEq)]
|
||||
|
@ -639,17 +640,18 @@ impl TextScriptVM {
|
|||
let item_id = read_cur_varint(&mut cursor)? as u16;
|
||||
let event_num = read_cur_varint(&mut cursor)? as u16;
|
||||
|
||||
if game_scene.player.inventory.has_item(item_id) {
|
||||
if game_scene.inventory.has_item(item_id) {
|
||||
exec_state = TextScriptExecutionState::Running(event_num, 0);
|
||||
} else {
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
}
|
||||
OpCode::AMJ => {
|
||||
let weapon = read_cur_varint(&mut cursor)? as u16;
|
||||
let weapon = read_cur_varint(&mut cursor)? as u8;
|
||||
let event_num = read_cur_varint(&mut cursor)? as u16;
|
||||
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon);
|
||||
|
||||
if game_scene.player.inventory.has_weapon(weapon) {
|
||||
if weapon_type.is_some() && game_scene.inventory.has_weapon(weapon_type.unwrap()) {
|
||||
exec_state = TextScriptExecutionState::Running(event_num, 0);
|
||||
} else {
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
|
@ -743,7 +745,7 @@ impl TextScriptVM {
|
|||
OpCode::YNJ => {
|
||||
let event_no = read_cur_varint(&mut cursor)? as u16;
|
||||
|
||||
exec_state = TextScriptExecutionState::WaitConfirmation(event, cursor.position() as u32, event_no, 16, ConfirmSelection::No);
|
||||
exec_state = TextScriptExecutionState::WaitConfirmation(event, cursor.position() as u32, event_no, 16, ConfirmSelection::Yes);
|
||||
}
|
||||
OpCode::GIT => {
|
||||
let item = read_cur_varint(&mut cursor)? as u16;
|
||||
|
@ -758,6 +760,7 @@ impl TextScriptVM {
|
|||
let pos_y = read_cur_varint(&mut cursor)? as isize * 16 * 0x200;
|
||||
|
||||
let mut new_scene = GameScene::new(state, ctx, map_id)?;
|
||||
new_scene.inventory = game_scene.inventory.clone();
|
||||
new_scene.player = game_scene.player.clone();
|
||||
new_scene.player.vel_x = 0;
|
||||
new_scene.player.vel_y = 0;
|
||||
|
@ -998,39 +1001,45 @@ impl TextScriptVM {
|
|||
OpCode::ITp => {
|
||||
let item_id = read_cur_varint(&mut cursor)? as u16;
|
||||
|
||||
game_scene.player.inventory.add_item(item_id);
|
||||
game_scene.inventory.add_item(item_id);
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
OpCode::ITm => {
|
||||
let item_id = read_cur_varint(&mut cursor)? as u16;
|
||||
|
||||
game_scene.player.inventory.remove_item(item_id);
|
||||
game_scene.inventory.remove_item(item_id);
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
OpCode::AMp => {
|
||||
let weapon_id = read_cur_varint(&mut cursor)? as u16;
|
||||
let weapon_id = read_cur_varint(&mut cursor)? as u8;
|
||||
let max_ammo = read_cur_varint(&mut cursor)? as u16;
|
||||
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon_id);
|
||||
|
||||
game_scene.player.inventory.add_weapon(weapon_id, max_ammo);
|
||||
if let Some(wtype) = weapon_type {
|
||||
game_scene.inventory.add_weapon(wtype, max_ammo);
|
||||
}
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
OpCode::AMm => {
|
||||
let weapon_id = read_cur_varint(&mut cursor)? as u16;
|
||||
let weapon_id = read_cur_varint(&mut cursor)? as u8;
|
||||
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon_id);
|
||||
|
||||
game_scene.player.inventory.remove_weapon(weapon_id);
|
||||
if let Some(wtype) = weapon_type {
|
||||
game_scene.inventory.remove_weapon(wtype);
|
||||
}
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
OpCode::AEp => {
|
||||
game_scene.player.inventory.refill_all_ammo();
|
||||
game_scene.inventory.refill_all_ammo();
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
OpCode::ZAM => {
|
||||
game_scene.player.inventory.reset_all_weapon_xp();
|
||||
game_scene.inventory.reset_all_weapon_xp();
|
||||
|
||||
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
|
||||
}
|
||||
|
|
146
src/weapon.rs
Normal file
146
src/weapon.rs
Normal file
|
@ -0,0 +1,146 @@
|
|||
use num_derive::FromPrimitive;
|
||||
|
||||
use crate::bullet::BulletManager;
|
||||
use crate::caret::CaretType;
|
||||
use crate::common::Direction;
|
||||
use crate::player::Player;
|
||||
use crate::SharedGameState;
|
||||
|
||||
#[derive(PartialEq, Eq, Copy, Clone, FromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum WeaponType {
|
||||
None = 0,
|
||||
Snake = 1,
|
||||
PolarStar = 2,
|
||||
Fireball = 3,
|
||||
MachineGun = 4,
|
||||
MissileLauncher = 5,
|
||||
Bubbler = 7,
|
||||
Sword = 9,
|
||||
SuperMissileLauncher = 10,
|
||||
Nemesis = 12,
|
||||
Spur = 13,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Weapon {
|
||||
pub wtype: WeaponType,
|
||||
pub level: WeaponLevel,
|
||||
pub experience: u16,
|
||||
pub ammo: u16,
|
||||
pub max_ammo: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum WeaponLevel {
|
||||
None = 0,
|
||||
Level1 = 1,
|
||||
Level2 = 2,
|
||||
Level3 = 3,
|
||||
}
|
||||
|
||||
impl Weapon {
|
||||
pub fn new(wtype: WeaponType, level: WeaponLevel, experience: u16, ammo: u16, max_ammo: u16) -> Weapon {
|
||||
Weapon {
|
||||
wtype,
|
||||
level,
|
||||
experience,
|
||||
ammo,
|
||||
max_ammo,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn consume_ammo(&mut self, ammo: u16) -> bool {
|
||||
if self.max_ammo == 0 {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.ammo >= ammo {
|
||||
self.ammo -= ammo;
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn shoot_bullet_polar_star(&mut self, player: &Player, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
|
||||
if state.key_trigger.fire() && bullet_manager.count_bullets_multi([4, 5, 6]) < 2 {
|
||||
let btype = match self.level {
|
||||
WeaponLevel::Level1 => { 4 }
|
||||
WeaponLevel::Level2 => { 5 }
|
||||
WeaponLevel::Level3 => { 6 }
|
||||
WeaponLevel::None => { unreachable!() }
|
||||
};
|
||||
|
||||
if !self.consume_ammo(1) {
|
||||
// todo: play sound 37
|
||||
return;
|
||||
}
|
||||
|
||||
if player.up {
|
||||
match player.direction {
|
||||
Direction::Left => {
|
||||
bullet_manager.create_bullet(player.x - 0x200, player.y - 8 * 0x200, btype, Direction::Up, &state.constants);
|
||||
state.create_caret(player.x - 0x200, player.y - 8 * 0x200, CaretType::Shoot, Direction::Left);
|
||||
}
|
||||
Direction::Right => {
|
||||
bullet_manager.create_bullet(player.x + 0x200, player.y - 8 * 0x200, btype, Direction::Up, &state.constants);
|
||||
state.create_caret(player.x + 0x200, player.y - 8 * 0x200, CaretType::Shoot, Direction::Left);
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
}
|
||||
} else if player.down {
|
||||
match player.direction {
|
||||
Direction::Left => {
|
||||
bullet_manager.create_bullet(player.x - 0x200, player.y + 8 * 0x200, btype, Direction::Bottom, &state.constants);
|
||||
state.create_caret(player.x - 0x200, player.y + 8 * 0x200, CaretType::Shoot, Direction::Left);
|
||||
}
|
||||
Direction::Right => {
|
||||
bullet_manager.create_bullet(player.x + 0x200, player.y + 8 * 0x200, btype, Direction::Bottom, &state.constants);
|
||||
state.create_caret(player.x + 0x200, player.y + 8 * 0x200, CaretType::Shoot, Direction::Left);
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
}
|
||||
} else {
|
||||
match player.direction {
|
||||
Direction::Left => {
|
||||
bullet_manager.create_bullet(player.x - 6 * 0x200, player.y + 3 * 0x200, btype, Direction::Left, &state.constants);
|
||||
state.create_caret(player.x - 6 * 0x200, player.y + 3 * 0x200, CaretType::Shoot, Direction::Left);
|
||||
}
|
||||
Direction::Right => {
|
||||
bullet_manager.create_bullet(player.x + 6 * 0x200, player.y + 3 * 0x200, btype, Direction::Right, &state.constants);
|
||||
state.create_caret(player.x + 6 * 0x200, player.y + 3 * 0x200, CaretType::Shoot, Direction::Right);
|
||||
}
|
||||
_ => { unreachable!() }
|
||||
}
|
||||
}
|
||||
|
||||
if self.level == WeaponLevel::Level3 {
|
||||
// todo play sound 49
|
||||
} else {
|
||||
// todo play sound 32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shoot_bullet(&mut self, player: &Player, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
|
||||
if player.cond.hidden() {
|
||||
return;
|
||||
}
|
||||
|
||||
match self.wtype {
|
||||
WeaponType::None => {}
|
||||
WeaponType::Snake => {}
|
||||
WeaponType::PolarStar => { self.shoot_bullet_polar_star(player, bullet_manager, state) }
|
||||
WeaponType::Fireball => {}
|
||||
WeaponType::MachineGun => {}
|
||||
WeaponType::MissileLauncher => {}
|
||||
WeaponType::Bubbler => {}
|
||||
WeaponType::Sword => {}
|
||||
WeaponType::SuperMissileLauncher => {}
|
||||
WeaponType::Nemesis => {}
|
||||
WeaponType::Spur => {}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue