1
0
Fork 0
mirror of https://github.com/doukutsu-rs/doukutsu-rs synced 2025-05-06 06:14:29 +00:00

add spur and other stuff

This commit is contained in:
Alula 2021-01-16 14:51:52 +01:00
parent 2563c09b69
commit 8e810bf026
No known key found for this signature in database
GPG key ID: 3E00485503A1D8BA
9 changed files with 535 additions and 109 deletions

View file

@ -13,13 +13,15 @@ use crate::stage::Stage;
pub struct BulletManager {
pub bullets: Vec<Bullet>,
pub new_bullets: Vec<Bullet>,
}
impl BulletManager {
#[allow(clippy::new_without_default)]
pub fn new() -> BulletManager {
BulletManager {
bullets: Vec::with_capacity(32),
bullets: Vec::with_capacity(64),
new_bullets: Vec::with_capacity(8),
}
}
@ -27,15 +29,27 @@ impl BulletManager {
self.bullets.push(Bullet::new(x, y, btype, owner, direction, constants));
}
pub fn push_bullet(&mut self, bullet: Bullet) {
self.bullets.push(bullet);
}
pub fn tick_bullets(&mut self, state: &mut SharedGameState, players: [&dyn PhysicalEntity; 2], npc_list: &NPCList, stage: &mut Stage) {
for bullet in self.bullets.iter_mut() {
if bullet.life < 1 {
bullet.cond.set_alive(false);
continue;
let mut i = 0;
while i < self.bullets.len() {
{
let bullet = unsafe { self.bullets.get_unchecked_mut(i) };
i += 1;
if bullet.life < 1 {
bullet.cond.set_alive(false);
continue;
}
bullet.tick(state, players, npc_list, &mut self.new_bullets);
bullet.tick_map_collisions(state, npc_list, stage);
}
bullet.tick(state, players, npc_list);
bullet.tick_map_collisions(state, npc_list, stage);
self.bullets.append(&mut self.new_bullets);
}
self.bullets.retain(|b| !b.is_dead());
@ -49,7 +63,7 @@ impl BulletManager {
self.bullets.iter().filter(|b| (b.btype.saturating_sub(2) / 3) == type_idx).count()
}
pub fn count_bullets_multi(&self, btypes: [u16; 3], player_id: TargetPlayer) -> usize {
pub fn count_bullets_multi(&self, btypes: &[u16], player_id: TargetPlayer) -> usize {
self.bullets.iter().filter(|b| b.owner == player_id && btypes.contains(&b.btype)).count()
}
}
@ -168,13 +182,83 @@ impl Bullet {
self.y += self.vel_y;
}
self.anim_num = (self.anim_num + 1) % 3;
self.anim_num = (self.anim_num + 1) % 4;
let dir_offset = if self.direction == Direction::Left { 0 } else { 4 };
self.anim_rect = state.constants.weapon.bullet_rects.b001_snake_l1[self.anim_num as usize + dir_offset];
}
fn tick_snake_2(&mut self, state: &mut SharedGameState, npc_list: &NPCList) {
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;
self.anim_num = state.game_rng.range(0..2) as u16;
match self.direction {
Direction::Left => {
self.vel_x = -0x200;
self.vel_y = if self.target_x & 1 == 0 { -0x400 } else { 0x400 };
}
Direction::Up => {
self.vel_y = -0x200;
self.vel_x = if self.target_x & 1 == 0 { -0x400 } else { 0x400 };
}
Direction::Right => {
self.vel_x = 0x200;
self.vel_y = if self.target_x & 1 == 0 { -0x400 } else { 0x400 };
}
Direction::Bottom => {
self.vel_y = 0x200;
self.vel_x = if self.target_x & 1 == 0 { -0x400 } else { 0x400 };
}
Direction::FacingPlayer => unreachable!(),
};
} else {
match self.direction {
Direction::Left => self.vel_x += -0x80,
Direction::Up => self.vel_y += -0x80,
Direction::Right => self.vel_x += 0x80,
Direction::Bottom => self.vel_y += 0x80,
Direction::FacingPlayer => unreachable!(),
}
if self.action_counter % 5 == 2 {
match self.direction {
Direction::Left | Direction::Right => {
self.vel_y = if self.vel_y < 0 { 0x400 } else { -0x400 };
}
Direction::Up | Direction::Bottom => {
self.vel_x = if self.vel_x < 0 { 0x400 } else { -0x400 };
}
Direction::FacingPlayer => unreachable!(),
}
}
self.x += self.vel_x;
self.y += self.vel_y;
}
self.anim_num = (self.anim_num + 1) % 3;
self.anim_rect = state.constants.weapon.bullet_rects.b002_003_snake_l2_3[self.anim_num as usize];
let mut npc = NPC::create(129, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.x;
npc.y = self.y;
npc.vel_y = -0x200;
npc.action_counter2 = if self.btype == 3 { self.anim_num + 3 } else { self.anim_num };
let _ = npc_list.spawn(0x100, npc);
}
fn tick_polar_star(&mut self, state: &mut SharedGameState) {
self.action_counter += 1;
if self.action_counter > self.lifetime {
@ -374,7 +458,134 @@ impl Bullet {
}
}
pub fn tick(&mut self, state: &mut SharedGameState, players: [&dyn PhysicalEntity; 2], npc_list: &NPCList) {
fn tick_spur(&mut self, state: &mut SharedGameState, new_bullets: &mut Vec<Bullet>) {
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,
Direction::FacingPlayer => unreachable!(),
}
match self.btype {
37 => {
match self.direction {
Direction::Left | Direction::Right => self.enemy_hit_height = 0x400,
Direction::Up | Direction::Bottom => self.enemy_hit_width = 0x400,
Direction::FacingPlayer => unreachable!(),
}
}
38 => {
match self.direction {
Direction::Left | Direction::Right => {
self.enemy_hit_height = 0x800;
}
Direction::Up | Direction::Bottom => {
self.enemy_hit_width = 0x800;
}
Direction::FacingPlayer => unreachable!(),
}
}
39 => {
// level 3 uses default values
}
_ => { unreachable!() }
}
} else {
self.x += self.vel_x;
self.y += self.vel_y;
}
match self.btype {
37 => {
if self.direction == Direction::Up || self.direction == Direction::Bottom {
self.anim_num = 1;
self.anim_rect = state.constants.weapon.bullet_rects.b037_spur_l1[1];
} else {
self.anim_num = 0;
self.anim_rect = state.constants.weapon.bullet_rects.b037_spur_l1[0];
}
new_bullets.push(Bullet::new(self.x, self.y, 40, self.owner, self.direction, &state.constants));
}
38 => {
if self.direction == Direction::Up || self.direction == Direction::Bottom {
self.anim_num = 1;
self.anim_rect = state.constants.weapon.bullet_rects.b038_spur_l2[1];
} else {
self.anim_num = 0;
self.anim_rect = state.constants.weapon.bullet_rects.b038_spur_l2[0];
}
new_bullets.push(Bullet::new(self.x, self.y, 41, self.owner, self.direction, &state.constants));
}
39 => {
if self.direction == Direction::Up || self.direction == Direction::Bottom {
self.anim_num = 1;
self.anim_rect = state.constants.weapon.bullet_rects.b039_spur_l3[1];
} else {
self.anim_num = 0;
self.anim_rect = state.constants.weapon.bullet_rects.b039_spur_l3[0];
}
new_bullets.push(Bullet::new(self.x, self.y, 42, self.owner, self.direction, &state.constants));
}
_ => { unreachable!() }
}
}
fn tick_spur_trail(&mut self, state: &mut SharedGameState) {
self.action_counter += 1;
if self.action_counter > 20 {
self.anim_num = self.action_counter.wrapping_sub(20);
if self.anim_num > 2 {
self.cond.set_alive(false);
return;
}
}
if self.damage != 0 && self.life != 100 {
self.damage = 0;
}
match self.btype {
40 => {
if self.direction == Direction::Up || self.direction == Direction::Bottom {
self.anim_rect = state.constants.weapon.bullet_rects.b040_spur_trail_l1[self.anim_num as usize + 3];
} else {
self.anim_rect = state.constants.weapon.bullet_rects.b040_spur_trail_l1[self.anim_num as usize];
}
}
41 => {
if self.direction == Direction::Up || self.direction == Direction::Bottom {
self.anim_rect = state.constants.weapon.bullet_rects.b041_spur_trail_l2[self.anim_num as usize + 3];
} else {
self.anim_rect = state.constants.weapon.bullet_rects.b041_spur_trail_l2[self.anim_num as usize];
}
}
42 => {
if self.direction == Direction::Up || self.direction == Direction::Bottom {
self.anim_rect = state.constants.weapon.bullet_rects.b042_spur_trail_l3[self.anim_num as usize + 3];
} else {
self.anim_rect = state.constants.weapon.bullet_rects.b042_spur_trail_l3[self.anim_num as usize];
}
}
_ => { unreachable!() }
}
}
pub fn tick(&mut self, state: &mut SharedGameState, players: [&dyn PhysicalEntity; 2], npc_list: &NPCList, new_bullets: &mut Vec<Bullet>) {
if self.lifetime == 0 {
self.cond.set_alive(false);
return;
@ -382,8 +593,11 @@ impl Bullet {
match self.btype {
1 => self.tick_snake_1(state),
2 | 3 => self.tick_snake_2(state, npc_list),
4 | 5 | 6 => self.tick_polar_star(state),
7 | 8 | 9 => self.tick_fireball(state, players, npc_list),
37 | 38 | 39 => self.tick_spur(state, new_bullets),
40 | 41 | 42 => self.tick_spur_trail(state),
_ => self.cond.set_alive(false),
}
}

View file

@ -3,6 +3,9 @@ use std::cmp::Ordering;
use crate::engine_constants::EngineConstants;
use crate::shared_game_state::SharedGameState;
use crate::weapon::{Weapon, WeaponLevel, WeaponType};
use crate::player::Player;
use crate::caret::CaretType;
use crate::common::Direction;
#[derive(Clone, Copy)]
/// (id, amount)
@ -16,13 +19,6 @@ pub struct Inventory {
weapons: Vec<Weapon>,
}
#[derive(Clone, Copy)]
pub enum AddExperienceResult {
None,
LevelUp,
AddStar,
}
#[derive(Clone, Copy)]
pub enum TakeExperienceResult {
None,
@ -102,6 +98,18 @@ impl Inventory {
self.weapons.iter_mut().find(|w| w.wtype == weapon_id).unwrap()
}
pub fn trade_weapon(&mut self, old: Option<WeaponType>, new: WeaponType, max_ammo: u16) {
if let Some(wtype) = old {
if let Some(weapon) = self.get_weapon_by_type_mut(wtype) {
*weapon = Weapon::new(new, WeaponLevel::Level1, 0, max_ammo, max_ammo);
} else {
self.add_weapon(new, max_ammo);
}
} else {
self.add_weapon(new, max_ammo);
}
}
pub fn remove_weapon(&mut self, wtype: WeaponType) {
self.weapons.retain(|weapon| weapon.wtype != wtype);
}
@ -151,6 +159,13 @@ impl Inventory {
}
}
pub fn reset_current_weapon_xp(&mut self) {
if let Some(weapon) = self.get_current_weapon_mut() {
weapon.level = WeaponLevel::Level1;
weapon.experience = 0;
}
}
pub fn take_xp(&mut self, exp: u16, state: &mut SharedGameState) -> TakeExperienceResult {
let mut result = TakeExperienceResult::None;
@ -182,9 +197,7 @@ impl Inventory {
result
}
pub fn add_xp(&mut self, exp: u16, state: &mut SharedGameState) -> AddExperienceResult {
let mut result = AddExperienceResult::None;
pub fn add_xp(&mut self, exp: u16, player: &mut Player, state: &mut SharedGameState) {
if let Some(weapon) = self.get_current_weapon_mut() {
let curr_level_idx = weapon.level as usize - 1;
let lvl_table = state.constants.weapon.level_table[weapon.wtype as usize];
@ -194,19 +207,21 @@ impl Inventory {
if weapon.level == WeaponLevel::Level3 {
if weapon.experience > lvl_table[2] {
weapon.experience = lvl_table[2];
result = AddExperienceResult::AddStar;
if player.equip.has_whimsical_star() && player.stars < 3 {
player.stars += 1;
}
}
} else if weapon.experience > lvl_table[curr_level_idx] {
weapon.level = weapon.level.next();
weapon.experience = 0;
if weapon.wtype != WeaponType::Spur {
result = AddExperienceResult::LevelUp;
state.sound_manager.play_sfx(27);
state.create_caret(player.x, player.y, CaretType::LevelUp, Direction::Left);
}
}
}
result
}
/// Get current experience state. Returns a (exp, max exp, max level/exp) tuple.

View file

@ -248,10 +248,10 @@ pub fn android_main() {
request_perms().expect("Failed to attach to the JVM and request storage permissions.");
env::set_var("CAVESTORY_DATA_DIR", "/storage/emulated/0/doukutsu");
env::set_var("CAVESTORY_DATA_DIR", "/sdcard/doukutsu");
let _ = std::fs::create_dir("/storage/emulated/0/doukutsu/");
let _ = std::fs::write("/storage/emulated/0/doukutsu/.nomedia", b"");
let _ = std::fs::create_dir("/sdcard/doukutsu/");
let _ = std::fs::write("/sdcard/doukutsu/.nomedia", b"");
init().unwrap();
}

View file

@ -4,7 +4,7 @@ use num_traits::abs;
use crate::caret::CaretType;
use crate::common::{Condition, Direction, Flag, Rect};
use crate::inventory::{AddExperienceResult, Inventory};
use crate::inventory::Inventory;
use crate::npc::boss::BossNPC;
use crate::npc::list::NPCList;
use crate::npc::NPC;
@ -256,18 +256,7 @@ impl Player {
// experience pickup
1 => {
state.sound_manager.play_sfx(14);
match inventory.add_xp(npc.exp, state) {
AddExperienceResult::None => {}
AddExperienceResult::LevelUp => {
state.sound_manager.play_sfx(27);
state.create_caret(self.x, self.y, CaretType::LevelUp, Direction::Left);
}
AddExperienceResult::AddStar => {
if self.equip.has_whimsical_star() && self.stars < 3 {
self.stars += 1;
}
}
}
inventory.add_xp(npc.exp, self, state);
npc.cond.set_alive(false);
}
// missile pickup

View file

@ -519,7 +519,7 @@ impl GameScene {
for bullet in self.bullet_manager.bullets.iter() {
self.draw_light(fix9_scale(bullet.x - self.frame.x, scale),
fix9_scale(bullet.y - self.frame.y, scale),
0.7, (200, 200, 200), batch);
0.3, (200, 200, 200), batch);
}
for caret in state.carets.iter() {
@ -1055,12 +1055,24 @@ impl GameScene {
self.frame.update(state, &self.stage);
if state.control_flags.control_enabled() {
#[allow(clippy::cast_ref_to_mut)]
let inventory = unsafe { &mut *(&self.inventory_player1 as *const Inventory as *mut Inventory)}; // fuck off
if let Some(weapon) = self.inventory_player1.get_current_weapon_mut() {
weapon.shoot_bullet(&self.player1, TargetPlayer::Player1, &mut self.bullet_manager, state);
weapon.shoot_bullet(&mut self.player1,
TargetPlayer::Player1,
inventory,
&mut self.bullet_manager,
state);
}
#[allow(clippy::cast_ref_to_mut)]
let inventory = unsafe { &mut *(&self.inventory_player2 as *const Inventory as *mut Inventory)};
if let Some(weapon) = self.inventory_player2.get_current_weapon_mut() {
weapon.shoot_bullet(&self.player2, TargetPlayer::Player2, &mut self.bullet_manager, state);
weapon.shoot_bullet(&mut self.player2,
TargetPlayer::Player2,
inventory,
&mut self.bullet_manager,
state);
}
self.hud_player1.tick(state, (&self.player1, &mut self.inventory_player1))?;

37
src/scripting/doukutsu.d.ts vendored Normal file
View file

@ -0,0 +1,37 @@
declare type EventHandler<T> = (this: void, param: T) => void;
declare interface DoukutsuPlayer {
x(): number;
y(): number;
velX(): number;
velY(): number;
};
declare interface DoukutsuScene {
/**
* Returns the tick of current scene.
*/
tick(): number;
/**
* Returns player at specified index.
*/
player(index: number): DoukutsuPlayer | null;
};
declare namespace doukutsu {
/**
* Plays a PixTone sound effect with specified ID.
*/
function playSfx(id: number): void;
/**
* Changes current music to one with specified ID.
* If ID equals 0, the music is stopped.
*/
function playMusic(id: number): void;
function on(event: "tick", handler: EventHandler<DoukutsuScene>): EventHandler<DoukutsuScene>;
function on<T>(event: string, handler: EventHandler<T>): EventHandler<T>;
};

View file

@ -40,45 +40,45 @@ pub static PIXTONE_TABLE: [PixToneParameters; 160] = [
Channel::disabled(),
],
},
PixToneParameters { // fx2 (CS+)
channels: [
Channel {
enabled: true,
length: 2000,
carrier: Waveform {
waveform_type: 0,
pitch: 92.000000,
level: 32,
offset: 0,
},
frequency: Waveform {
waveform_type: 0,
pitch: 3.000000,
level: 44,
offset: 0,
},
amplitude: Waveform {
waveform_type: 0,
pitch: 0.000000,
level: 32,
offset: 0,
},
envelope: Envelope {
initial: 7,
time_a: 2,
value_a: 18,
time_b: 128,
value_b: 0,
time_c: 255,
value_c: 0,
},
},
Channel::disabled(),
Channel::disabled(),
Channel::disabled(),
],
},
/*PixToneParameters { // fx2
// PixToneParameters { // fx2 (CS+)
// channels: [
// Channel {
// enabled: true,
// length: 2000,
// carrier: Waveform {
// waveform_type: 0,
// pitch: 92.000000,
// level: 32,
// offset: 0,
// },
// frequency: Waveform {
// waveform_type: 0,
// pitch: 3.000000,
// level: 44,
// offset: 0,
// },
// amplitude: Waveform {
// waveform_type: 0,
// pitch: 0.000000,
// level: 32,
// offset: 0,
// },
// envelope: Envelope {
// initial: 7,
// time_a: 2,
// value_a: 18,
// time_b: 128,
// value_b: 0,
// time_c: 255,
// value_c: 0,
// },
// },
// Channel::disabled(),
// Channel::disabled(),
// Channel::disabled(),
// ],
// },
PixToneParameters { // fx2
channels: [
Channel {
enabled: true,
@ -115,7 +115,7 @@ pub static PIXTONE_TABLE: [PixToneParameters; 160] = [
Channel::disabled(),
Channel::disabled(),
],
},*/
},
PixToneParameters { // fx3
channels: [
Channel {

View file

@ -373,6 +373,7 @@ pub struct TextScriptVM {
/// while parsing no one noticed them.
pub strict_mode: bool,
pub suspend: bool,
pub numbers: [u16; 4],
pub face: u16,
pub item: u16,
pub current_line: TextScriptLine,
@ -455,6 +456,7 @@ impl TextScriptVM {
executor_player: TargetPlayer::Player1,
strict_mode: false,
suspend: true,
numbers: [0; 4],
face: 0,
item: 0,
current_line: TextScriptLine::Line1,
@ -565,8 +567,8 @@ impl TextScriptVM {
if remaining > 1 {
let ticks = if state.textscript_vm.flags.fast()
|| game_scene.player1.controller.skip()
|| game_scene.player2.controller.skip() {
|| game_scene.player1.controller.skip()
|| game_scene.player2.controller.skip() {
0
} else if game_scene.player1.controller.jump()
|| game_scene.player1.controller.shoot()
@ -715,7 +717,6 @@ impl TextScriptVM {
game_scene.player1.cond.set_interacted(false);
game_scene.player2.cond.set_interacted(false);
game_scene.frame.update_target = UpdateTarget::Player;
exec_state = TextScriptExecutionState::Ended;
}
@ -1026,6 +1027,21 @@ impl TextScriptVM {
exec_state = TextScriptExecutionState::WaitConfirmation(event, cursor.position() as u32, event_no, 16, ConfirmSelection::Yes);
}
OpCode::NUM => {
let index = read_cur_varint(&mut cursor)? as usize;
if let Some(num) = state.textscript_vm.numbers.get(index) {
let mut str = num.to_string().chars().collect_vec();
match state.textscript_vm.current_line {
TextScriptLine::Line1 => state.textscript_vm.line_1.append(&mut str),
TextScriptLine::Line2 => state.textscript_vm.line_2.append(&mut str),
TextScriptLine::Line3 => state.textscript_vm.line_3.append(&mut str),
}
}
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
OpCode::GIT => {
let item = read_cur_varint(&mut cursor)? as u16;
state.textscript_vm.item = item;
@ -1418,6 +1434,8 @@ impl TextScriptVM {
let max_ammo = read_cur_varint(&mut cursor)? as u16;
let weapon_type: Option<WeaponType> = FromPrimitive::from_u8(weapon_id);
state.textscript_vm.numbers[0] = max_ammo;
if let Some(wtype) = weapon_type {
game_scene.inventory_player1.add_weapon(wtype, max_ammo);
game_scene.inventory_player2.add_weapon(wtype, max_ammo);
@ -1442,6 +1460,20 @@ impl TextScriptVM {
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
OpCode::TAM => {
let old_weapon_id = read_cur_varint(&mut cursor)? as u8;
let new_weapon_id = read_cur_varint(&mut cursor)? as u8;
let max_ammo = read_cur_varint(&mut cursor)? as u16;
let old_weapon_type: Option<WeaponType> = FromPrimitive::from_u8(old_weapon_id);
let new_weapon_type: Option<WeaponType> = FromPrimitive::from_u8(new_weapon_id);
if let Some(wtype) = new_weapon_type {
game_scene.inventory_player1.trade_weapon(old_weapon_type, wtype, max_ammo);
game_scene.inventory_player2.trade_weapon(old_weapon_type, wtype, max_ammo);
}
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
OpCode::ZAM => {
game_scene.inventory_player1.reset_all_weapon_xp();
game_scene.inventory_player2.reset_all_weapon_xp();
@ -1494,7 +1526,7 @@ impl TextScriptVM {
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
// One operand codes
OpCode::NUM | OpCode::MPp | OpCode::SKm | OpCode::SKp |
OpCode::MPp | OpCode::SKm | OpCode::SKp |
OpCode::UNJ | OpCode::MPJ | OpCode::XX1 | OpCode::SIL |
OpCode::SSS | OpCode::ACH => {
let par_a = read_cur_varint(&mut cursor)?;
@ -1510,16 +1542,6 @@ impl TextScriptVM {
log::warn!("unimplemented opcode: {:?} {} {}", op, par_a, par_b);
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
// Three operand codes
OpCode::TAM => {
let par_a = read_cur_varint(&mut cursor)?;
let par_b = read_cur_varint(&mut cursor)?;
let par_c = read_cur_varint(&mut cursor)?;
log::warn!("unimplemented opcode: {:?} {} {} {}", op, par_a, par_b, par_c);
exec_state = TextScriptExecutionState::Running(event, cursor.position() as u32);
}
}

View file

@ -1,8 +1,9 @@
use num_derive::FromPrimitive;
use crate::bullet::BulletManager;
use crate::bullet::{Bullet, BulletManager};
use crate::caret::CaretType;
use crate::common::Direction;
use crate::inventory::Inventory;
use crate::player::{Player, TargetPlayer};
use crate::shared_game_state::SharedGameState;
@ -58,6 +59,8 @@ pub struct Weapon {
pub experience: u16,
pub ammo: u16,
pub max_ammo: u16,
counter1: u16,
counter2: u16,
}
impl Weapon {
@ -68,6 +71,8 @@ impl Weapon {
experience,
ammo,
max_ammo,
counter1: 0,
counter2: 0,
}
}
@ -85,7 +90,7 @@ impl Weapon {
}
fn shoot_bullet_snake(&mut self, player: &Player, player_id: TargetPlayer, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
if player.controller.trigger_shoot() && bullet_manager.count_bullets_multi([1, 2, 3], player_id) < 4 {
if player.controller.trigger_shoot() && bullet_manager.count_bullets_multi(&[1, 2, 3], player_id) < 4 {
let btype = match self.level {
WeaponLevel::Level1 => { 1 }
WeaponLevel::Level2 => { 2 }
@ -98,14 +103,20 @@ impl Weapon {
return;
}
self.counter1 = self.counter1.wrapping_add(1);
if player.up {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 3 * 0x200, player.y - 10 * 0x200, btype, player_id, Direction::Up, &state.constants);
let mut bullet = Bullet::new(player.x - 3 * 0x200, player.y - 10 * 0x200, btype, player_id, Direction::Up, &state.constants);
bullet.target_x = self.counter1 as i32;
bullet_manager.push_bullet(bullet);
state.create_caret(player.x - 3 * 0x200, player.y - 10 * 0x200, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 3 * 0x200, player.y - 10 * 0x200, btype, player_id, Direction::Up, &state.constants);
let mut bullet = Bullet::new(player.x + 3 * 0x200, player.y - 10 * 0x200, btype, player_id, Direction::Up, &state.constants);
bullet.target_x = self.counter1 as i32;
bullet_manager.push_bullet(bullet);
state.create_caret(player.x + 3 * 0x200, player.y - 10 * 0x200, CaretType::Shoot, Direction::Left);
}
_ => {}
@ -113,11 +124,15 @@ impl Weapon {
} else if player.down {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 3 * 0x200, player.y + 10 * 0x200, btype, player_id, Direction::Bottom, &state.constants);
let mut bullet = Bullet::new(player.x - 3 * 0x200, player.y + 10 * 0x200, btype, player_id, Direction::Bottom, &state.constants);
bullet.target_x = self.counter1 as i32;
bullet_manager.push_bullet(bullet);
state.create_caret(player.x - 3 * 0x200, player.y + 10 * 0x200, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 3 * 0x200, player.y + 10 * 0x200, btype, player_id, Direction::Bottom, &state.constants);
let mut bullet = Bullet::new(player.x + 3 * 0x200, player.y + 10 * 0x200, btype, player_id, Direction::Bottom, &state.constants);
bullet.target_x = self.counter1 as i32;
bullet_manager.push_bullet(bullet);
state.create_caret(player.x + 3 * 0x200, player.y + 10 * 0x200, CaretType::Shoot, Direction::Left);
}
_ => {}
@ -125,11 +140,15 @@ impl Weapon {
} else {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 6 * 0x200, player.y + 2 * 0x200, btype, player_id, Direction::Left, &state.constants);
let mut bullet = Bullet::new(player.x - 6 * 0x200, player.y + 2 * 0x200, btype, player_id, Direction::Left, &state.constants);
bullet.target_x = self.counter1 as i32;
bullet_manager.push_bullet(bullet);
state.create_caret(player.x - 12 * 0x200, player.y + 2 * 0x200, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 6 * 0x200, player.y + 2 * 0x200, btype, player_id, Direction::Right, &state.constants);
let mut bullet = Bullet::new(player.x + 6 * 0x200, player.y + 2 * 0x200, btype, player_id, Direction::Right, &state.constants);
bullet.target_x = self.counter1 as i32;
bullet_manager.push_bullet(bullet);
state.create_caret(player.x + 12 * 0x200, player.y + 2 * 0x200, CaretType::Shoot, Direction::Right);
}
_ => {}
@ -141,7 +160,7 @@ impl Weapon {
}
fn shoot_bullet_polar_star(&mut self, player: &Player, player_id: TargetPlayer, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
if player.controller.trigger_shoot() && bullet_manager.count_bullets_multi([4, 5, 6], player_id) < 2 {
if player.controller.trigger_shoot() && bullet_manager.count_bullets_multi(&[4, 5, 6], player_id) < 2 {
let btype = match self.level {
WeaponLevel::Level1 => { 4 }
WeaponLevel::Level2 => { 5 }
@ -203,7 +222,7 @@ impl Weapon {
fn shoot_bullet_fireball(&mut self, player: &Player, player_id: TargetPlayer, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
let max_bullets = self.level as usize + 1;
if player.controller.trigger_shoot() && bullet_manager.count_bullets_multi([7, 8, 9], player_id) < max_bullets {
if player.controller.trigger_shoot() && bullet_manager.count_bullets_multi(&[7, 8, 9], player_id) < max_bullets {
let btype = match self.level {
WeaponLevel::Level1 => { 7 }
WeaponLevel::Level2 => { 8 }
@ -258,7 +277,125 @@ impl Weapon {
}
}
pub fn shoot_bullet(&mut self, player: &Player, player_id: TargetPlayer, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
fn shoot_bullet_spur(&mut self, player: &mut Player, player_id: TargetPlayer, inventory: &mut Inventory, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
let mut shoot = false;
let mut btype = 0;
if player.controller.shoot() {
inventory.add_xp(if player.equip.has_turbocharge() { 3 } else { 2 }, player, state);
self.counter1 += 1;
if (self.counter1 / 2 % 2) != 0 {
match self.level {
WeaponLevel::Level1 => {
state.sound_manager.play_sfx(59);
}
WeaponLevel::Level2 => {
state.sound_manager.play_sfx(60);
}
WeaponLevel::Level3 => {
if let (_, _, false) = inventory.get_current_max_exp(&state.constants) {
state.sound_manager.play_sfx(61);
}
}
WeaponLevel::None => unreachable!(),
}
}
} else {
if self.counter1 > 0 {
shoot = true;
self.counter1 = 0;
}
}
if let (_, _, true) = inventory.get_current_max_exp(&state.constants) {
if self.counter2 == 0 {
self.counter2 = 1;
state.sound_manager.play_sfx(65);
}
} else {
self.counter2 = 0;
}
let level = self.level;
if !player.controller.shoot() {
inventory.reset_current_weapon_xp();
}
match level {
WeaponLevel::Level1 => {
btype = 6;
shoot = false;
}
WeaponLevel::Level2 => btype = 37,
WeaponLevel::Level3 => {
if self.counter2 == 1 {
btype = 39;
} else {
btype = 38;
}
}
WeaponLevel::None => unreachable!(),
}
const bullets: [u16; 6] = [44, 45, 46, 47, 48, 49];
if bullet_manager.count_bullets_multi(&bullets, player_id) == 0
&& (player.controller.trigger_shoot() || shoot) {
if !self.consume_ammo(1) {
state.sound_manager.play_sfx(37);
} else {
if player.up {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x200, player.y - 8 * 0x200, btype, player_id, 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, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x200, player.y - 8 * 0x200, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else if player.down {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x200, player.y + 8 * 0x200, btype, player_id, 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, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x200, player.y + 8 * 0x200, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 6 * 0x200, player.y + 3 * 0x200, btype, player_id, 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, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 6 * 0x200, player.y + 3 * 0x200, CaretType::Shoot, Direction::Right);
}
_ => {}
}
}
let sound = match btype {
6 => 49,
37 => 62,
38 => 63,
39 => 64,
_ => 0,
};
state.sound_manager.play_sfx(sound);
}
}
}
pub fn shoot_bullet(&mut self, player: &mut Player, player_id: TargetPlayer, inventory: &mut Inventory, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
if !player.cond.alive() || player.cond.hidden() {
return;
}
@ -274,7 +411,7 @@ impl Weapon {
WeaponType::Blade => {}
WeaponType::SuperMissileLauncher => {}
WeaponType::Nemesis => {}
WeaponType::Spur => {}
WeaponType::Spur => self.shoot_bullet_spur(player, player_id, inventory, bullet_manager, state),
}
}
}