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:
parent
2563c09b69
commit
8e810bf026
234
src/bullet.rs
234
src/bullet.rs
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
37
src/scripting/doukutsu.d.ts
vendored
Normal 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>;
|
||||
};
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
161
src/weapon.rs
161
src/weapon.rs
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue