weapon refactor + bunch of minor stuff

This commit is contained in:
Alula 2021-03-09 15:05:38 +01:00
parent c0c48783a9
commit a1d546215f
No known key found for this signature in database
GPG Key ID: 3E00485503A1D8BA
28 changed files with 1396 additions and 1101 deletions

Binary file not shown.

View File

@ -1,6 +0,0 @@
#Thu Feb 18 11:02:22 CET 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

172
gradlew vendored
View File

@ -1,172 +0,0 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

84
gradlew.bat vendored
View File

@ -1,84 +0,0 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -55,6 +55,12 @@ bitfield! {
pub hit_right_bigger_half, set_hit_right_bigger_half: 19; // 0x80000
}
impl Flag {
pub fn hit_anything(&self) -> bool {
(self.0 & 0x2ff) != 0
}
}
bitfield! {
#[derive(Clone, Copy)]
pub struct Equipment(u16);

View File

@ -122,10 +122,17 @@ pub struct BulletRects {
pub b013_missile_l1: [Rect<u16>; 4],
pub b014_missile_l2: [Rect<u16>; 4],
pub b015_missile_l3: [Rect<u16>; 4],
pub b019_bubble_l1: [Rect<u16>; 4],
pub b020_bubble_l2: [Rect<u16>; 4],
pub b021_bubble_l3: [Rect<u16>; 4],
pub b022_bubble_spines: [Rect<u16>; 6],
pub b023_blade_slash: [Rect<u16>; 10],
pub b025_blade_l1: [Rect<u16>; 8],
pub b026_blade_l2: [Rect<u16>; 8],
pub b027_blade_l3: [Rect<u16>; 8],
pub b028_super_missile_l1: [Rect<u16>; 4],
pub b029_super_missile_l2: [Rect<u16>; 4],
pub b030_super_missile_l3: [Rect<u16>; 4],
pub b034_nemesis_l1: [Rect<u16>; 8],
pub b035_nemesis_l2: [Rect<u16>; 8],
pub b036_nemesis_l3: [Rect<u16>; 8],
@ -1015,14 +1022,12 @@ impl EngineConstants {
Rect { left: 112, top: 0, right: 128, bottom: 16 },
],
b011_machine_gun_l2: [
Rect { left: 64, top: 16, right: 80, bottom: 32 },
Rect { left: 80, top: 16, right: 96, bottom: 32 },
Rect { left: 96, top: 16, right: 112, bottom: 32 },
Rect { left: 112, top: 16, right: 128, bottom: 32 },
Rect { left: 64, top: 16, right: 80, bottom: 32 },
Rect { left: 80, top: 16, right: 96, bottom: 32 },
Rect { left: 96, top: 16, right: 112, bottom: 32 },
Rect { left: 112, top: 16, right: 128, bottom: 32 },
],
b012_machine_gun_l3: [
Rect { left: 64, top: 32, right: 80, bottom: 48 },
Rect { left: 80, top: 32, right: 96, bottom: 48 },
Rect { left: 96, top: 32, right: 112, bottom: 48 },
@ -1038,13 +1043,40 @@ impl EngineConstants {
Rect { left: 0, top: 16, right: 16, bottom: 32 },
Rect { left: 16, top: 16, right: 32, bottom: 32 },
Rect { left: 32, top: 16, right: 48, bottom: 32 },
Rect { left: 48, top: 16, right: 64, bottom: 32 },],
Rect { left: 48, top: 16, right: 64, bottom: 32 },
],
b015_missile_l3: [
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 },
],
b019_bubble_l1: [
Rect { left: 192, top: 0, right: 200, bottom: 8 },
Rect { left: 200, top: 0, right: 208, bottom: 8 },
Rect { left: 208, top: 0, right: 216, bottom: 8 },
Rect { left: 216, top: 0, right: 224, bottom: 8 },
],
b020_bubble_l2: [
Rect { left: 192, top: 8, right: 200, bottom: 16 },
Rect { left: 200, top: 8, right: 208, bottom: 16 },
Rect { left: 208, top: 8, right: 216, bottom: 16 },
Rect { left: 216, top: 8, right: 224, bottom: 16 },
],
b021_bubble_l3: [
Rect { left: 240, top: 16, right: 248, bottom: 24 },
Rect { left: 248, top: 16, right: 256, bottom: 24 },
Rect { left: 240, top: 24, right: 248, bottom: 32 },
Rect { left: 248, top: 24, right: 256, bottom: 32 },
],
b022_bubble_spines: [
Rect { left: 224, top: 0, right: 232, bottom: 8 },
Rect { left: 232, top: 0, right: 240, bottom: 8 },
Rect { left: 224, top: 0, right: 232, bottom: 8 },
Rect { left: 232, top: 0, right: 240, bottom: 8 },
Rect { left: 224, top: 8, right: 232, bottom: 16 },
Rect { left: 232, top: 8, right: 240, bottom: 16 },
],
b023_blade_slash: [
Rect { left: 0, top: 64, right: 24, bottom: 88 }, // left
Rect { left: 24, top: 64, right: 48, bottom: 88 },
@ -1087,6 +1119,24 @@ impl EngineConstants {
Rect { left: 296, top: 48, right: 320, bottom: 72 }, // down
Rect { left: 296, top: 24, right: 320, bottom: 48 },
],
b028_super_missile_l1: [
Rect { left: 120, top: 96, right: 136, bottom: 112 },
Rect { left: 136, top: 96, right: 152, bottom: 112 },
Rect { left: 152, top: 96, right: 168, bottom: 112 },
Rect { left: 168, top: 96, right: 184, bottom: 112 },
],
b029_super_missile_l2: [
Rect { left: 184, top: 96, right: 200, bottom: 112 },
Rect { left: 200, top: 96, right: 216, bottom: 112 },
Rect { left: 216, top: 96, right: 232, bottom: 112 },
Rect { left: 232, top: 96, right: 248, bottom: 112 },
],
b030_super_missile_l3: [
Rect { left: 120, top: 96, right: 136, bottom: 112 },
Rect { left: 136, top: 96, right: 152, bottom: 112 },
Rect { left: 152, top: 96, right: 168, bottom: 112 },
Rect { left: 168, top: 96, right: 184, bottom: 112 },
],
b034_nemesis_l1: [
Rect { left: 0, top: 112, right: 32, bottom: 128 }, // left
Rect { left: 0, top: 128, right: 32, bottom: 144 },
@ -1098,24 +1148,24 @@ impl EngineConstants {
Rect { left: 112, top: 112, right: 128, bottom: 144 },
],
b035_nemesis_l2: [
Rect { left: 0, top: 240, right: 32, bottom: 256 }, // left
Rect { left: 0, top: 256, right: 32, bottom: 272 },
Rect { left: 32, top: 240, right: 48, bottom: 272 }, // up
Rect { left: 48, top: 240, right: 64, bottom: 272 },
Rect { left: 64, top: 240, right: 96, bottom: 256 }, // right
Rect { left: 64, top: 256, right: 96, bottom: 272 },
Rect { left: 96, top: 240, right: 112, bottom: 272 }, // down
Rect { left: 112, top: 240, right: 128, bottom: 272 },
Rect { left: 128, top: 112, right: 160, bottom: 128 }, // left
Rect { left: 128, top: 128, right: 160, bottom: 144 },
Rect { left: 160, top: 112, right: 176, bottom: 144 }, // up
Rect { left: 176, top: 112, right: 192, bottom: 144 },
Rect { left: 192, top: 112, right: 224, bottom: 128 }, // right
Rect { left: 192, top: 128, right: 224, bottom: 144 },
Rect { left: 224, top: 112, right: 240, bottom: 144 }, // down
Rect { left: 240, top: 112, right: 256, bottom: 144 },
],
b036_nemesis_l3: [
Rect { left: 32, top: 112, right: 64, bottom: 128 }, // left
Rect { left: 32, top: 128, right: 64, bottom: 144 },
Rect { left: 64, top: 112, right: 80, bottom: 144 }, // up
Rect { left: 80, top: 112, right: 96, bottom: 144 },
Rect { left: 96, top: 112, right: 128, bottom: 128 }, // right
Rect { left: 96, top: 128, right: 128, bottom: 144 },
Rect { left: 128, top: 112, right: 144, bottom: 144 }, // down
Rect { left: 144, top: 112, right: 160, bottom: 144 },
Rect { left: 0, top: 144, right: 32, bottom: 160 }, // left
Rect { left: 0, top: 160, right: 32, bottom: 176 },
Rect { left: 32, top: 144, right: 48, bottom: 176 }, // up
Rect { left: 48, top: 144, right: 64, bottom: 176 },
Rect { left: 64, top: 144, right: 96, bottom: 160 }, // right
Rect { left: 64, top: 160, right: 96, bottom: 176 },
Rect { left: 96, top: 144, right: 112, bottom: 176 }, // down
Rect { left: 112, top: 144, right: 128, bottom: 176 },
],
b037_spur_l1: [
Rect { left: 128, top: 32, right: 144, bottom: 48 }, // horizontal

View File

@ -321,7 +321,7 @@ pub struct GlutinTexture {
context_active: Arc<RefCell<bool>>,
}
#[repr(C)]
// #[repr(C)] // since we determine field offset dynamically, doesn't really matter
#[derive(Copy, Clone)]
struct VertexData {
position: (f32, f32),

View File

@ -15,7 +15,6 @@ use std::time::Instant;
use directories::ProjectDirs;
use lazy_static::lazy_static;
use log::*;
use pretty_env_logger::env_logger::Env;
use crate::builtin_fs::BuiltinFS;
@ -33,7 +32,6 @@ use crate::texture_set::{G_MAG, I_MAG};
mod bmfont;
mod bmfont_renderer;
mod builtin_fs;
mod bullet;
mod caret;
mod common;
mod components;
@ -209,8 +207,8 @@ pub fn init() -> GameResult {
};
#[cfg(not(target_os = "android"))]
info!("Resource directory: {:?}", resource_dir);
info!("Initializing engine...");
log::info!("Resource directory: {:?}", resource_dir);
log::info!("Initializing engine...");
let mut context = Context::new();
mount_vfs(&mut context, Box::new(BuiltinFS::new()));

0
src/netplay/mod.rs Normal file
View File

View File

@ -1,6 +1,6 @@
use std::mem::MaybeUninit;
use crate::bullet::BulletManager;
use crate::weapon::bullet::BulletManager;
use crate::common::{Direction, interpolate_fix9_scale};
use crate::entity::GameEntity;
use crate::frame::Frame;

View File

@ -1,6 +1,6 @@
use crate::bullet::BulletManager;
use crate::weapon::bullet::BulletManager;
use crate::caret::CaretType;
use crate::common::{Direction, Rect};
use crate::npc::boss::BossNPC;

View File

@ -7,7 +7,7 @@ use crate::framework::error::GameResult;
use num_traits::abs;
use crate::bitfield;
use crate::bullet::BulletManager;
use crate::weapon::bullet::BulletManager;
use crate::common::{Condition, interpolate_fix9_scale, Rect};
use crate::common::Direction;
use crate::common::Flag;

View File

@ -2,7 +2,7 @@
use num_traits::abs;
use crate::bullet::Bullet;
use crate::weapon::bullet::Bullet;
use crate::caret::CaretType;
use crate::common::{Condition, Direction, Flag, Rect};
use crate::map::NPCData;

View File

@ -1,20 +1,20 @@
use std::clone::Clone;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use num_derive::FromPrimitive;
use num_traits::clamp;
use crate::caret::CaretType;
use crate::common::{Condition, Direction, Equipment, Flag, interpolate_fix9_scale, Rect};
use crate::common::{interpolate_fix9_scale, Condition, Direction, Equipment, Flag, Rect};
use crate::entity::GameEntity;
use crate::frame::Frame;
use crate::framework::context::Context;
use crate::framework::error::GameResult;
use crate::input::dummy_player_controller::DummyPlayerController;
use crate::input::player_controller::PlayerController;
use crate::npc::list::NPCList;
use crate::npc::NPC;
use crate::rng::RNG;
use crate::shared_game_state::SharedGameState;
use crate::npc::list::NPCList;
mod player_hit;
@ -82,8 +82,8 @@ pub struct Player {
pub appearance: PlayerAppearance,
pub controller: Box<dyn PlayerController>,
weapon_offset_y: i8,
index_x: i32,
index_y: i32,
camera_target_x: i32,
camera_target_y: i32,
splash: bool,
booster_switch: u8,
bubble: u8,
@ -119,8 +119,8 @@ impl Player {
control_mode: constants.my_char.control_mode,
question: false,
booster_fuel: 0,
index_x: 0,
index_y: 0,
camera_target_x: 0,
camera_target_y: 0,
splash: false,
up: false,
down: false,
@ -177,11 +177,7 @@ impl Player {
return Ok(());
}
let physics = if self.flags.in_water() {
state.constants.my_char.water_physics
} else {
state.constants.my_char.air_physics
};
let physics = if self.flags.in_water() { state.constants.my_char.water_physics } else { state.constants.my_char.air_physics };
self.question = false;
@ -202,15 +198,10 @@ impl Player {
}
if state.control_flags.control_enabled() {
let trigger_only_down = self.controller.trigger_down()
&& !self.controller.trigger_up()
&& !self.controller.trigger_left()
&& !self.controller.trigger_right();
let trigger_only_down =
self.controller.trigger_down() && !self.controller.trigger_up() && !self.controller.trigger_left() && !self.controller.trigger_right();
let only_down = self.controller.move_down()
&& !self.controller.move_up()
&& !self.controller.move_left()
&& !self.controller.move_right();
let only_down = self.controller.move_down() && !self.controller.move_up() && !self.controller.move_left() && !self.controller.move_right();
if trigger_only_down && only_down && !self.cond.interacted() && !state.control_flags.interactions_disabled() {
self.cond.set_interacted(true);
@ -250,13 +241,15 @@ impl Player {
}
}
}
} else { // air movement
} else {
// air movement
if state.control_flags.control_enabled() {
if self.controller.trigger_jump() && self.booster_fuel != 0 {
if self.equip.has_booster_0_8() {
self.booster_switch = 1;
if self.vel_y > 0x100 { // 0.5fix9
if self.vel_y > 0x100 {
// 0.5fix9
self.vel_y /= 2;
}
} else if state.settings.infinite_booster || self.equip.has_booster_2_0() {
@ -301,11 +294,13 @@ impl Player {
}
}
if (state.settings.infinite_booster || self.equip.has_booster_2_0()) && self.booster_switch != 0
&& (!self.controller.jump() || self.booster_fuel == 0) {
if (state.settings.infinite_booster || self.equip.has_booster_2_0())
&& self.booster_switch != 0
&& (!self.controller.jump() || self.booster_fuel == 0)
{
match self.booster_switch {
1 => { self.vel_x /= 2 }
2 => { self.vel_y /= 2 }
1 => self.vel_x /= 2,
2 => self.vel_y /= 2,
_ => {}
}
}
@ -320,21 +315,19 @@ impl Player {
self.up = self.controller.move_up();
self.down = self.controller.move_down() && !self.flags.hit_bottom_wall();
if self.controller.trigger_jump() && (self.flags.hit_bottom_wall()
|| self.flags.hit_right_slope()
|| self.flags.hit_left_slope())
&& !self.flags.force_up() {
if self.controller.trigger_jump()
&& (self.flags.hit_bottom_wall() || self.flags.hit_right_slope() || self.flags.hit_left_slope())
&& !self.flags.force_up()
{
self.vel_y = -physics.jump;
state.sound_manager.play_sfx(15);
}
}
// stop interacting when moved
if state.control_flags.control_enabled() && (self.controller.move_left()
|| self.controller.move_right()
|| self.controller.move_up()
|| self.controller.jump()
|| self.controller.shoot()) {
if state.control_flags.control_enabled()
&& (self.controller.move_left() || self.controller.move_right() || self.controller.move_up() || self.controller.jump() || self.controller.shoot())
{
self.cond.set_interacted(false);
}
@ -424,7 +417,8 @@ impl Player {
if (self.flags.hit_bottom_wall() && self.flags.hit_right_bigger_half() && self.vel_x < 0)
|| (self.flags.hit_bottom_wall() && self.flags.hit_left_bigger_half() && self.vel_x > 0)
|| (self.flags.hit_bottom_wall() && self.flags.hit_left_smaller_half() && self.flags.hit_right_smaller_half()) {
|| (self.flags.hit_bottom_wall() && self.flags.hit_left_smaller_half() && self.flags.hit_right_smaller_half())
{
self.vel_y = 0x400; // 2.0fix9
}
}
@ -450,14 +444,13 @@ impl Player {
for _ in 0..7 {
droplet.x = self.x + (state.game_rng.range(-8..8) * 0x200) as i32;
droplet.vel_x = if vertical_splash {
(self.vel_x + state.game_rng.range(-0x200..0x200) as i32) - (self.vel_x / 2)
} else if horizontal_splash {
self.vel_x + state.game_rng.range(-0x200..0x200) as i32
} else {
0 as i32
droplet.vel_x = self.vel_x + state.game_rng.range(-0x200..0x200);
droplet.vel_y = match () {
_ if vertical_splash => state.game_rng.range(-0x200..0x80) - (self.vel_y / 2),
_ if horizontal_splash => state.game_rng.range(-0x200..0x80),
_ => 0,
};
droplet.vel_y = state.game_rng.range(-0x200..0x80) as i32;
let _ = npc_list.spawn(0x100, droplet.clone());
}
@ -478,30 +471,34 @@ impl Player {
}
// camera
self.index_x = clamp(self.index_x + self.direction.vector_x() * 0x200, -0x8000, 0x8000);
self.camera_target_x = clamp(self.camera_target_x + self.direction.vector_x() * 0x200, -0x8000, 0x8000);
if state.control_flags.control_enabled() && self.controller.look_up() {
self.index_y -= 0x200; // 1.0fix9
if self.index_y < -0x8000 { // -64.0fix9
self.index_y = -0x8000;
self.camera_target_y -= 0x200; // 1.0fix9
if self.camera_target_y < -0x8000 {
// -64.0fix9
self.camera_target_y = -0x8000;
}
} else if state.control_flags.control_enabled() && self.controller.look_down() {
self.index_y += 0x200; // 1.0fix9
if self.index_y > 0x8000 { // -64.0fix9
self.index_y = 0x8000;
self.camera_target_y += 0x200; // 1.0fix9
if self.camera_target_y > 0x8000 {
// -64.0fix9
self.camera_target_y = 0x8000;
}
} else {
if self.index_y > 0x200 { // 1.0fix9
self.index_y -= 0x200;
if self.camera_target_y > 0x200 {
// 1.0fix9
self.camera_target_y -= 0x200;
}
if self.index_y < -0x200 { // 1.0fix9
self.index_y += 0x200;
if self.camera_target_y < -0x200 {
// 1.0fix9
self.camera_target_y += 0x200;
}
}
self.target_x = self.x + self.index_x;
self.target_y = self.y + self.index_y;
self.target_x = self.x + self.camera_target_x;
self.target_y = self.y + self.camera_target_y;
if self.vel_x > physics.resist || self.vel_x < -physics.resist {
self.x += self.vel_x;
@ -687,7 +684,7 @@ impl GameEntity<&NPCList> for Player {
Ok(())
}
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, frame: &Frame) -> GameResult<> {
fn draw(&self, state: &mut SharedGameState, ctx: &mut Context, frame: &Frame) -> GameResult {
if !self.cond.alive() || self.cond.hidden() || (self.shock_counter / 2 % 2 != 0) {
return Ok(());
}
@ -697,23 +694,31 @@ impl GameEntity<&NPCList> for Player {
match self.direction {
Direction::Left => {
batch.add_rect(
interpolate_fix9_scale(self.prev_x - self.display_bounds.left as i32 - frame.prev_x,
self.x - self.display_bounds.left as i32 - frame.x,
state.frame_time) - 8.0,
interpolate_fix9_scale(self.prev_y - self.display_bounds.left as i32 - frame.prev_y,
self.y - self.display_bounds.left as i32 - frame.y,
state.frame_time) + self.weapon_offset_y as f32,
interpolate_fix9_scale(
self.prev_x - self.display_bounds.left as i32 - frame.prev_x,
self.x - self.display_bounds.left as i32 - frame.x,
state.frame_time,
) - 8.0,
interpolate_fix9_scale(
self.prev_y - self.display_bounds.left as i32 - frame.prev_y,
self.y - self.display_bounds.left as i32 - frame.y,
state.frame_time,
) + self.weapon_offset_y as f32,
&self.weapon_rect,
);
}
Direction::Right => {
batch.add_rect(
interpolate_fix9_scale(self.prev_x - self.display_bounds.left as i32 - frame.prev_x,
self.x - self.display_bounds.left as i32 - frame.x,
state.frame_time),
interpolate_fix9_scale(self.prev_y - self.display_bounds.left as i32 - frame.prev_y,
self.y - self.display_bounds.left as i32 - frame.y,
state.frame_time) + self.weapon_offset_y as f32,
interpolate_fix9_scale(
self.prev_x - self.display_bounds.left as i32 - frame.prev_x,
self.x - self.display_bounds.left as i32 - frame.x,
state.frame_time,
),
interpolate_fix9_scale(
self.prev_y - self.display_bounds.left as i32 - frame.prev_y,
self.y - self.display_bounds.left as i32 - frame.y,
state.frame_time,
) + self.weapon_offset_y as f32,
&self.weapon_rect,
);
}
@ -726,12 +731,16 @@ impl GameEntity<&NPCList> for Player {
{
let batch = state.texture_set.get_or_load_batch(ctx, &state.constants, "MyChar")?;
batch.add_rect(
interpolate_fix9_scale(self.prev_x - self.display_bounds.left as i32 - frame.prev_x,
self.x - self.display_bounds.left as i32 - frame.x,
state.frame_time),
interpolate_fix9_scale(self.prev_y - self.display_bounds.left as i32 - frame.prev_y,
self.y - self.display_bounds.left as i32 - frame.y,
state.frame_time),
interpolate_fix9_scale(
self.prev_x - self.display_bounds.left as i32 - frame.prev_x,
self.x - self.display_bounds.left as i32 - frame.x,
state.frame_time,
),
interpolate_fix9_scale(
self.prev_y - self.display_bounds.left as i32 - frame.prev_y,
self.y - self.display_bounds.left as i32 - frame.y,
state.frame_time,
),
&self.anim_rect,
);
batch.draw(ctx)?;

View File

@ -1,7 +1,7 @@
use log::info;
use num_traits::{abs, clamp};
use crate::bullet::BulletManager;
use crate::weapon::bullet::BulletManager;
use crate::caret::CaretType;
use crate::common::{fix9_scale, interpolate_fix9_scale, Color, Direction, FadeDirection, FadeState, Rect};
use crate::components::boss_life_bar::BossLifeBar;

View File

@ -1,647 +0,0 @@
use num_derive::FromPrimitive;
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;
#[derive(Debug, PartialEq, Eq, Copy, Clone, FromPrimitive)]
#[repr(u8)]
pub enum WeaponType {
None = 0,
Snake = 1,
PolarStar = 2,
Fireball = 3,
MachineGun = 4,
MissileLauncher = 5,
Bubbler = 7,
Blade = 9,
SuperMissileLauncher = 10,
Nemesis = 12,
Spur = 13,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[repr(u8)]
pub enum WeaponLevel {
None = 0,
Level1 = 1,
Level2 = 2,
Level3 = 3,
}
impl WeaponLevel {
pub fn next(self) -> WeaponLevel {
match self {
WeaponLevel::None => WeaponLevel::Level1,
WeaponLevel::Level1 => WeaponLevel::Level2,
WeaponLevel::Level2 => WeaponLevel::Level3,
WeaponLevel::Level3 => WeaponLevel::Level3,
}
}
pub fn prev(self) -> WeaponLevel {
match self {
WeaponLevel::None => WeaponLevel::Level1,
WeaponLevel::Level1 => WeaponLevel::Level1,
WeaponLevel::Level2 => WeaponLevel::Level1,
WeaponLevel::Level3 => WeaponLevel::Level2,
}
}
}
#[derive(Clone)]
pub struct Weapon {
pub wtype: WeaponType,
pub level: WeaponLevel,
pub experience: u16,
pub ammo: u16,
pub max_ammo: u16,
counter1: u16,
counter2: u16,
}
impl Weapon {
pub fn new(wtype: WeaponType, level: WeaponLevel, experience: u16, ammo: u16, max_ammo: u16) -> Weapon {
Weapon { wtype, level, experience, ammo, max_ammo, counter1: 0, counter2: 0 }
}
/// Consume a specified amount of bullets, returns true if there was enough ammo.
pub fn consume_ammo(&mut self, amount: u16) -> bool {
if self.max_ammo == 0 {
return true;
}
if self.ammo >= amount {
self.ammo -= amount;
return true;
}
false
}
/// Refill a specified amount of bullets.
pub fn refill_ammo(&mut self, amount: u16) {
if self.max_ammo != 0 {
self.ammo = self.ammo.saturating_add(amount).min(self.max_ammo);
}
}
fn tick_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 {
let btype = match self.level {
WeaponLevel::Level1 => 1,
WeaponLevel::Level2 => 2,
WeaponLevel::Level3 => 3,
WeaponLevel::None => {
unreachable!()
}
};
if !self.consume_ammo(1) {
// todo switch to first weapon
return;
}
self.counter1 = self.counter1.wrapping_add(1);
if player.up {
match player.direction {
Direction::Left => {
let mut bullet = Bullet::new(player.x - 0x600, 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 - 0x600, player.y - 10 * 0x200, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
let mut bullet = Bullet::new(player.x + 0x600, 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 + 0x600, player.y - 10 * 0x200, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else if player.down {
match player.direction {
Direction::Left => {
let mut bullet = Bullet::new(player.x - 0x600, 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 - 0x600, player.y + 10 * 0x200, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
let mut bullet = Bullet::new(player.x + 0x600, 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 + 0x600, player.y + 10 * 0x200, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else {
match player.direction {
Direction::Left => {
let mut bullet = Bullet::new(player.x - 0xc00, player.y + 0x400, btype, player_id, Direction::Left, &state.constants);
bullet.target_x = self.counter1 as i32;
bullet_manager.push_bullet(bullet);
state.create_caret(player.x - 0x1800, player.y + 0x400, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
let mut bullet = Bullet::new(player.x + 0xc00, player.y + 0x400, btype, player_id, Direction::Right, &state.constants);
bullet.target_x = self.counter1 as i32;
bullet_manager.push_bullet(bullet);
state.create_caret(player.x + 0x1800, player.y + 0x400, CaretType::Shoot, Direction::Right);
}
_ => {}
}
}
state.sound_manager.play_sfx(33);
}
}
fn tick_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 {
let btype = match self.level {
WeaponLevel::Level1 => 4,
WeaponLevel::Level2 => 5,
WeaponLevel::Level3 => 6,
WeaponLevel::None => {
unreachable!()
}
};
if !self.consume_ammo(1) {
state.sound_manager.play_sfx(37);
return;
}
if player.up {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x - 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else if player.down {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x200, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x - 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x200, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0xc00, player.y + 0x600, btype, player_id, Direction::Left, &state.constants);
state.create_caret(player.x - 0xc00, player.y + 0x600, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0xc00, player.y + 0x600, btype, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 0xc00, player.y + 0x600, CaretType::Shoot, Direction::Right);
}
_ => {}
}
}
if self.level == WeaponLevel::Level3 {
state.sound_manager.play_sfx(49);
} else {
state.sound_manager.play_sfx(32);
}
}
}
fn tick_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 {
let btype = match self.level {
WeaponLevel::Level1 => 7,
WeaponLevel::Level2 => 8,
WeaponLevel::Level3 => 9,
WeaponLevel::None => {
unreachable!()
}
};
if !self.consume_ammo(1) {
// todo switch to first weapon
return;
}
if player.up {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x800, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x - 0x800, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x800, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x800, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else if player.down {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x800, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x - 0x800, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x800, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x800, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0xc00, player.y + 0x400, btype, player_id, Direction::Left, &state.constants);
state.create_caret(player.x - 0x1800, player.y + 0x400, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0xc00, player.y + 0x400, btype, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 0x1800, player.y + 0x400, CaretType::Shoot, Direction::Right);
}
_ => {}
}
}
state.sound_manager.play_sfx(34)
}
}
fn tick_machine_gun(&mut self, player: &mut Player, player_id: TargetPlayer, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
const BULLETS: [u16; 3] = [10, 11, 12];
if bullet_manager.count_bullets_multi(&BULLETS, player_id) >= 4 {
return;
}
if player.controller.shoot() {
self.counter2 = 0; // recharge time counter
self.counter1 += 1; // autofire counter
if self.counter1 > 5 {
self.counter1 = 0;
let btype = match self.level {
WeaponLevel::Level1 => 10,
WeaponLevel::Level2 => 11,
WeaponLevel::Level3 => 12,
WeaponLevel::None => {
unreachable!()
}
};
if !self.consume_ammo(1) {
state.sound_manager.play_sfx(37);
return;
}
if player.up {
if self.level == WeaponLevel::Level3 {
player.vel_y += 0x100;
}
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x600, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x - 0x600, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x600, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x600, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else if player.down {
if self.level == WeaponLevel::Level3 {
if player.vel_y > 0 {
player.vel_y /= 2;
}
if player.vel_y > -0x400 {
player.vel_y = (player.vel_y - 0x200).max(-0x400);
}
}
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x600, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x - 0x600, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x600, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x600, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x1800, player.y + 0x600, btype, player_id, Direction::Left, &state.constants);
state.create_caret(player.x - 0x1800, player.y + 0x600, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x1800, player.y + 0x600, btype, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 0x1800, player.y + 0x600, CaretType::Shoot, Direction::Right);
}
_ => {}
}
}
if self.level == WeaponLevel::Level3 {
state.sound_manager.play_sfx(49);
} else {
state.sound_manager.play_sfx(32);
}
}
} else {
self.counter1 = 6;
self.counter2 += 1;
if (player.equip.has_turbocharge() && self.counter2 > 1) || self.counter2 > 4 {
self.counter2 = 0;
self.refill_ammo(1);
}
}
}
fn tick_blade(&mut self, player: &Player, player_id: TargetPlayer, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
const BULLETS: [u16; 3] = [25, 26, 27];
if player.controller.trigger_shoot() && bullet_manager.count_bullets_multi(&BULLETS, player_id) == 0 {
let btype = match self.level {
WeaponLevel::Level1 => 25,
WeaponLevel::Level2 => 26,
WeaponLevel::Level3 => 27,
WeaponLevel::None => {
unreachable!()
}
};
if player.up {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x200, player.y + 0x800, btype, player_id, Direction::Up, &state.constants);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x200, player.y + 0x800, btype, player_id, Direction::Up, &state.constants);
}
_ => {}
}
} else if player.down {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x200, player.y - 0xc00, btype, player_id, Direction::Bottom, &state.constants);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x200, player.y - 0xc00, btype, player_id, Direction::Bottom, &state.constants);
}
_ => {}
}
} else {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x + 0xc00, player.y - 0x600, btype, player_id, Direction::Left, &state.constants);
}
Direction::Right => {
bullet_manager.create_bullet(player.x - 0xc00, player.y - 0x600, btype, player_id, Direction::Right, &state.constants);
}
_ => {}
}
}
state.sound_manager.play_sfx(34)
}
}
fn tick_nemesis(&mut self, player: &Player, player_id: TargetPlayer, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
const BULLETS: [u16; 3] = [34, 35, 36];
if player.controller.trigger_shoot() && bullet_manager.count_bullets_multi(&BULLETS, player_id) < 2 {
let btype = match self.level {
WeaponLevel::Level1 => 34,
WeaponLevel::Level2 => 35,
WeaponLevel::Level3 => 36,
WeaponLevel::None => {
unreachable!()
}
};
if !self.consume_ammo(1) {
state.sound_manager.play_sfx(37);
return;
}
if player.up {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x200, player.y - 0x1800, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x - 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x200, player.y - 0x1800, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else if player.down {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x200, player.y + 0x1800, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x - 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x200, player.y + 0x1800, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x2c00, player.y + 0x600, btype, player_id, Direction::Left, &state.constants);
state.create_caret(player.x - 0x2000, player.y + 0x600, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x2c00, player.y + 0x600, btype, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 0x2000, player.y + 0x600, CaretType::Shoot, Direction::Right);
}
_ => {}
}
}
match self.level {
WeaponLevel::Level1 => state.sound_manager.play_sfx(117),
WeaponLevel::Level2 => state.sound_manager.play_sfx(49),
WeaponLevel::Level3 => state.sound_manager.play_sfx(60),
_ => unreachable!(),
}
}
}
fn tick_spur(
&mut self,
player: &mut Player,
player_id: TargetPlayer,
inventory: &mut Inventory,
bullet_manager: &mut BulletManager,
state: &mut SharedGameState,
) {
let mut shoot = false;
let btype;
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 - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x - 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else if player.down {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x200, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x - 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x200, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0xc00, player.y + 0x600, btype, player_id, Direction::Left, &state.constants);
state.create_caret(player.x - 0xc00, player.y + 0x600, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0xc00, player.y + 0x600, btype, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 0xc00, player.y + 0x600, 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 tick(
&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;
}
// todo lua hook
match self.wtype {
WeaponType::None => {}
WeaponType::Snake => self.tick_snake(player, player_id, bullet_manager, state),
WeaponType::PolarStar => self.tick_polar_star(player, player_id, bullet_manager, state),
WeaponType::Fireball => self.tick_fireball(player, player_id, bullet_manager, state),
WeaponType::MachineGun => self.tick_machine_gun(player, player_id, bullet_manager, state),
WeaponType::MissileLauncher => {}
WeaponType::Bubbler => {}
WeaponType::Blade => self.tick_blade(player, player_id, bullet_manager, state),
WeaponType::SuperMissileLauncher => {}
WeaponType::Nemesis => self.tick_nemesis(player, player_id, bullet_manager, state),
WeaponType::Spur => self.tick_spur(player, player_id, inventory, bullet_manager, state),
}
}
}

46
src/weapon/blade.rs Normal file
View File

@ -0,0 +1,46 @@
use crate::common::Direction;
use crate::player::{Player, TargetPlayer};
use crate::shared_game_state::SharedGameState;
use crate::weapon::bullet::BulletManager;
use crate::weapon::{Weapon, WeaponLevel};
impl Weapon {
pub(in crate::weapon) fn tick_blade(&mut self, player: &Player, player_id: TargetPlayer, bullet_manager: &mut BulletManager, state: &mut SharedGameState) {
const BULLETS: [u16; 3] = [25, 26, 27];
if !player.controller.trigger_shoot() || bullet_manager.count_bullets_multi(&BULLETS, player_id) > 0 {
return;
}
let btype = match self.level {
WeaponLevel::Level1 => 25,
WeaponLevel::Level2 => 26,
WeaponLevel::Level3 => 27,
WeaponLevel::None => unreachable!(),
};
match player.direction {
Direction::Left if player.up => {
bullet_manager.create_bullet(player.x - 0x200, player.y + 0x800, btype, player_id, Direction::Up, &state.constants);
}
Direction::Right if player.up => {
bullet_manager.create_bullet(player.x + 0x200, player.y + 0x800, btype, player_id, Direction::Up, &state.constants);
}
Direction::Left if player.down => {
bullet_manager.create_bullet(player.x - 0x200, player.y - 0xc00, btype, player_id, Direction::Bottom, &state.constants);
}
Direction::Right if player.down => {
bullet_manager.create_bullet(player.x + 0x200, player.y - 0xc00, btype, player_id, Direction::Bottom, &state.constants);
}
Direction::Left => {
bullet_manager.create_bullet(player.x + 0xc00, player.y - 0x600, btype, player_id, Direction::Left, &state.constants);
}
Direction::Right => {
bullet_manager.create_bullet(player.x - 0xc00, player.y - 0x600, btype, player_id, Direction::Right, &state.constants);
}
_ => {}
}
state.sound_manager.play_sfx(34);
}
}

133
src/weapon/bubbler.rs Normal file
View File

@ -0,0 +1,133 @@
use crate::caret::CaretType;
use crate::common::Direction;
use crate::player::{Player, TargetPlayer};
use crate::shared_game_state::SharedGameState;
use crate::weapon::bullet::BulletManager;
use crate::weapon::WeaponLevel::Level1;
use crate::weapon::{Weapon, WeaponLevel};
impl Weapon {
pub(in crate::weapon) fn tick_bubbler(
&mut self,
player: &Player,
player_id: TargetPlayer,
bullet_manager: &mut BulletManager,
state: &mut SharedGameState,
) {
const BULLETS: [u16; 3] = [19, 20, 21];
if self.level == WeaponLevel::Level1 {
if !player.controller.trigger_shoot() {
self.counter2 += 1;
if self.counter2 > 20 {
self.counter2 = 0;
self.refill_ammo(1);
}
return;
}
if bullet_manager.count_bullets_multi(&BULLETS, player_id) > 3 {
return;
}
if !self.consume_ammo(1) {
state.sound_manager.play_sfx(37);
// todo spawn "empty" text
return;
}
let btype = 19;
match player.direction {
Direction::Left if player.up => {
bullet_manager.create_bullet(player.x - 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x - 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right if player.up => {
bullet_manager.create_bullet(player.x + 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Left if player.down => {
bullet_manager.create_bullet(player.x - 0x200, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x - 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right if player.down => {
bullet_manager.create_bullet(player.x + 0x200, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Left => {
bullet_manager.create_bullet(player.x - 0xc00, player.y + 0x600, btype, player_id, Direction::Left, &state.constants);
state.create_caret(player.x - 0xc00, player.y + 0x600, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0xc00, player.y + 0x600, btype, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 0xc00, player.y + 0x600, CaretType::Shoot, Direction::Right);
}
_ => {}
}
state.sound_manager.play_sfx(48);
} else {
if bullet_manager.count_bullets_multi(&BULLETS, player_id) > 15 {
return;
}
let btype = if self.level == WeaponLevel::Level2 { 20 } else { 21 };
if !player.controller.shoot() {
self.counter1 = 6;
self.counter2 += 1;
if self.counter2 > 1 {
self.counter2 = 0;
self.refill_ammo(1);
}
return;
}
self.counter2 = 0; // recharge time counter
self.counter1 += 1; // autofire counter
if self.counter1 > 6 {
self.counter1 = 0;
if !self.consume_ammo(1) {
state.sound_manager.play_sfx(37);
// todo spawn "empty" text
return;
}
match player.direction {
Direction::Left if player.up => {
bullet_manager.create_bullet(player.x - 0x600, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x - 0x600, player.y - 0x2000, CaretType::Shoot, Direction::Left);
}
Direction::Right if player.up => {
bullet_manager.create_bullet(player.x + 0x600, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x600, player.y - 0x2000, CaretType::Shoot, Direction::Left);
}
Direction::Left if player.down => {
bullet_manager.create_bullet(player.x - 0x600, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x - 0x600, player.y + 0x2000, CaretType::Shoot, Direction::Left);
}
Direction::Right if player.down => {
bullet_manager.create_bullet(player.x + 0x600, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x600, player.y + 0x2000, CaretType::Shoot, Direction::Left);
}
Direction::Left => {
bullet_manager.create_bullet(player.x - 0xc00, player.y + 0x600, btype, player_id, Direction::Left, &state.constants);
state.create_caret(player.x - 0x1800, player.y + 0x600, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0xc00, player.y + 0x600, btype, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 0x1800, player.y + 0x600, CaretType::Shoot, Direction::Right);
}
_ => {}
}
state.sound_manager.play_sfx(48);
}
}
}
}

View File

@ -8,7 +8,7 @@ use crate::engine_constants::{BulletData, EngineConstants};
use crate::npc::list::NPCList;
use crate::npc::NPC;
use crate::physics::{PhysicalEntity, OFF_X, OFF_Y};
use crate::player::TargetPlayer;
use crate::player::{Player, TargetPlayer};
use crate::rng::{XorShift, Xoroshiro32PlusPlus, RNG};
use crate::shared_game_state::SharedGameState;
use crate::stage::Stage;
@ -22,22 +22,10 @@ pub struct BulletManager {
impl BulletManager {
#[allow(clippy::new_without_default)]
pub fn new() -> BulletManager {
BulletManager {
bullets: Vec::with_capacity(64),
new_bullets: Vec::with_capacity(8),
seeder: XorShift::new(0x359c482f),
}
BulletManager { bullets: Vec::with_capacity(64), new_bullets: Vec::with_capacity(8), seeder: XorShift::new(0x359c482f) }
}
pub fn create_bullet(
&mut self,
x: i32,
y: i32,
btype: u16,
owner: TargetPlayer,
direction: Direction,
constants: &EngineConstants,
) {
pub fn create_bullet(&mut self, x: i32, y: i32, btype: u16, owner: TargetPlayer, direction: Direction, constants: &EngineConstants) {
let mut bullet = Bullet::new(x, y, btype, owner, direction, constants);
bullet.rng = Xoroshiro32PlusPlus::new(self.seeder.next_u32());
@ -49,13 +37,7 @@ impl BulletManager {
self.bullets.push(bullet);
}
pub fn tick_bullets(
&mut self,
state: &mut SharedGameState,
players: [&dyn PhysicalEntity; 2],
npc_list: &NPCList,
stage: &mut Stage,
) {
pub fn tick_bullets(&mut self, state: &mut SharedGameState, players: [&Player; 2], npc_list: &NPCList, stage: &mut Stage) {
let mut i = 0;
while i < self.bullets.len() {
{
@ -126,14 +108,7 @@ pub struct Bullet {
}
impl Bullet {
pub fn new(
x: i32,
y: i32,
btype: u16,
owner: TargetPlayer,
direction: Direction,
constants: &EngineConstants,
) -> Bullet {
pub fn new(x: i32, y: i32, btype: u16, owner: TargetPlayer, direction: Direction, constants: &EngineConstants) -> Bullet {
let bullet = constants.weapon.bullet_table.get(btype as usize).unwrap_or_else(|| &BulletData {
damage: 0,
life: 0,
@ -374,7 +349,7 @@ impl Bullet {
}
}
fn tick_fireball(&mut self, state: &mut SharedGameState, players: [&dyn PhysicalEntity; 2], npc_list: &NPCList) {
fn tick_fireball(&mut self, state: &mut SharedGameState, players: [&Player; 2], npc_list: &NPCList) {
self.action_counter += 1;
if self.action_counter > self.lifetime {
self.cond.set_alive(false);
@ -382,9 +357,7 @@ impl Bullet {
return;
}
if (self.flags.hit_left_wall() && self.flags.hit_right_wall())
|| (self.flags.hit_top_wall() && self.flags.hit_bottom_wall())
{
if (self.flags.hit_left_wall() && self.flags.hit_right_wall()) || (self.flags.hit_top_wall() && self.flags.hit_bottom_wall()) {
self.cond.set_alive(false);
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
state.sound_manager.play_sfx(28);
@ -470,8 +443,7 @@ impl Bullet {
let dir_offset = if self.direction == Direction::Left { 0 } else { 3 };
self.anim_rect =
state.constants.weapon.bullet_rects.b008_009_fireball_l2_3[self.anim_num as usize + dir_offset];
self.anim_rect = state.constants.weapon.bullet_rects.b008_009_fireball_l2_3[self.anim_num as usize + dir_offset];
let mut npc = NPC::create(129, &state.npc_table);
npc.cond.set_alive(true);
@ -566,6 +538,253 @@ impl Bullet {
}
}
fn tick_bubble_1(&mut self, state: &mut SharedGameState) {
if self.flags.hit_anything() {
self.cond.set_alive(false);
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
return;
}
if self.action_num == 0 {
self.action_num = 1;
match self.direction {
Direction::Left => self.vel_x = -0x600,
Direction::Up => self.vel_y = -0x600,
Direction::Right => self.vel_x = 0x600,
Direction::Bottom => self.vel_y = 0x600,
_ => unreachable!(),
}
}
match self.direction {
Direction::Left => self.vel_x += 0x2a,
Direction::Up => self.vel_y += 0x2a,
Direction::Right => self.vel_x -= 0x2a,
Direction::Bottom => self.vel_y -= 0x2a,
_ => unreachable!(),
}
self.x += self.vel_x;
self.y += self.vel_y;
self.action_counter += 1;
if self.action_counter > 40 {
self.cond.set_alive(false);
state.create_caret(self.x, self.y, CaretType::SmallProjectileDissipation, Direction::Left);
}
self.anim_counter += 1;
if self.anim_counter > 3 {
self.anim_counter = 0;
self.anim_num += 1;
if self.anim_num > 3 {
self.anim_num = 3;
}
}
self.anim_rect = state.constants.weapon.bullet_rects.b019_bubble_l1[self.anim_num as usize];
}
fn tick_bubble_2(&mut self, state: &mut SharedGameState) {
if (self.direction == Direction::Left && self.flags.hit_left_wall())
|| (self.direction == Direction::Right && self.flags.hit_right_wall())
|| (self.direction == Direction::Up && self.flags.hit_top_wall())
|| (self.direction == Direction::Bottom && self.flags.hit_bottom_wall())
{
self.cond.set_alive(false);
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
return;
}
if self.action_num == 0 {
self.action_num = 1;
match self.direction {
Direction::Left => {
self.vel_x = -0x600;
self.vel_y = self.rng.range(-0x100..0x100);
}
Direction::Up => {
self.vel_y = -0x600;
self.vel_x = self.rng.range(-0x100..0x100);
}
Direction::Right => {
self.vel_x = 0x600;
self.vel_y = self.rng.range(-0x100..0x100);
}
Direction::Bottom => {
self.vel_y = 0x600;
self.vel_x = self.rng.range(-0x100..0x100);
}
Direction::FacingPlayer => unreachable!(),
}
}
match self.direction {
Direction::Left => self.vel_x += 0x10,
Direction::Up => self.vel_y += 0x10,
Direction::Right => self.vel_x -= 0x10,
Direction::Bottom => self.vel_y -= 0x10,
_ => unreachable!(),
}
self.x += self.vel_x;
self.y += self.vel_y;
self.action_counter += 1;
if self.action_counter > 60 {
self.cond.set_alive(false);
state.create_caret(self.x, self.y, CaretType::SmallProjectileDissipation, Direction::Left);
}
self.anim_counter += 1;
if self.anim_counter > 3 {
self.anim_counter = 0;
self.anim_num += 1;
if self.anim_num > 3 {
self.anim_num = 3;
}
}
self.anim_rect = state.constants.weapon.bullet_rects.b020_bubble_l2[self.anim_num as usize];
}
fn tick_bubble_3(&mut self, state: &mut SharedGameState, players: [&Player; 2], new_bullets: &mut Vec<Bullet>) {
let player = players[self.owner.index()];
self.action_counter += 1;
if self.action_counter > 100 || !player.controller.shoot() {
self.cond.set_alive(false);
state.create_caret(self.x, self.y, CaretType::ProjectileDissipation, Direction::Left);
state.sound_manager.play_sfx(100);
match () {
_ if player.up => new_bullets.push(Bullet::new(self.x, self.y, 22, self.owner, Direction::Up, &state.constants)),
_ if player.down => new_bullets.push(Bullet::new(self.x, self.y, 22, self.owner, Direction::Bottom, &state.constants)),
_ => new_bullets.push(Bullet::new(self.x, self.y, 22, self.owner, player.direction, &state.constants)),
}
return;
}
if self.action_counter == 0 {
self.action_counter = 1;
match self.direction {
Direction::Left => {
self.vel_x = self.rng.range(-0x400..-0x200);
self.vel_y = self.rng.range(-4..4) * 0x200 / 2;
}
Direction::Up => {
self.vel_y = self.rng.range(-0x400..-0x200);
self.vel_x = self.rng.range(-4..4) * 0x200 / 2;
}
Direction::Right => {
self.vel_x = self.rng.range(0x200..0x400);
self.vel_y = self.rng.range(-4..4) * 0x200 / 2;
}
Direction::Bottom => {
self.vel_y = self.rng.range(0x80..0x100);
self.vel_x = self.rng.range(-4..4) * 0x200 / 2;
}
Direction::FacingPlayer => unreachable!(),
}
}
self.vel_x += (player.x - self.x).signum() * 0x20;
self.vel_y += (player.y - self.y).signum() * 0x20;
if self.vel_x < 0 && self.flags.hit_left_wall() {
self.vel_x = 0x400;
}
if self.vel_x > 0 && self.flags.hit_right_wall() {
self.vel_x = -0x400;
}
if self.vel_y < 0 && self.flags.hit_top_wall() {
self.vel_y = 0x400;
}
if self.vel_y > 0 && self.flags.hit_bottom_wall() {
self.vel_y = -0x400;
}
self.x += self.vel_x;
self.y += self.vel_y;
self.anim_counter += 1;
if self.anim_counter > 3 {
self.anim_counter = 0;
self.anim_num += 1;
if self.anim_num > 3 {
self.anim_num = 3;
}
}
self.anim_rect = state.constants.weapon.bullet_rects.b021_bubble_l3[self.anim_num as usize];
}
fn tick_bubble_spines(&mut self, state: &mut SharedGameState) {
self.action_counter += 1;
if self.action_counter > self.lifetime || self.flags.hit_bottom_wall() {
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 => {
let val = self.rng.range(10..16);
// what the fuck
self.vel_x = (((val * -0x200).rotate_left(1) & 1) - val * 0x200) / 2;
}
Direction::Up => {
let val = self.rng.range(10..16);
// what the fuck
self.vel_y = (((val * -0x200).rotate_left(1) & 1) - val * 0x200) / 2;
}
Direction::Right => {
self.vel_x = (self.rng.range(10..16) * 0x200) / 2;
}
Direction::Bottom => {
self.vel_y = (self.rng.range(10..16) * 0x200) / 2;
}
Direction::FacingPlayer => unreachable!(),
}
} else {
self.x += self.vel_x;
self.y += self.vel_y;
}
self.anim_counter += 1;
if self.anim_counter > 1 {
self.anim_counter = 0;
self.anim_num += 1;
if self.anim_num > 1 {
self.anim_num = 0;
}
}
match self.direction {
Direction::Left => {
self.anim_rect = state.constants.weapon.bullet_rects.b022_bubble_spines[self.anim_num as usize];
}
Direction::Right => {
self.anim_rect = state.constants.weapon.bullet_rects.b022_bubble_spines[self.anim_num as usize + 2];
}
Direction::Up | Direction::Bottom => {
self.anim_rect = state.constants.weapon.bullet_rects.b022_bubble_spines[self.anim_num as usize + 4];
}
Direction::FacingPlayer => unreachable!(),
}
}
fn tick_blade_slash(&mut self, state: &mut SharedGameState) {
if self.action_num == 0 {
self.action_num = 1;
@ -768,6 +987,84 @@ impl Bullet {
}
}
fn tick_nemesis(&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;
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!(),
}
if self.btype == 36 {
self.vel_x /= 3;
self.vel_y /= 3;
}
} else {
if self.btype == 34 && self.action_counter % 4 == 1 {
let mut npc = NPC::create(4, &state.npc_table);
npc.cond.set_alive(true);
npc.x = self.x;
npc.y = self.y;
match self.direction {
Direction::Left => {
npc.vel_y = self.rng.range(-0x200..0x200);
npc.vel_x = -0x200;
}
Direction::Up => {
npc.vel_x = self.rng.range(-0x200..0x200);
npc.vel_y = -0x200;
}
Direction::Right => {
npc.vel_y = self.rng.range(-0x200..0x200);
npc.vel_x = 0x200;
}
Direction::Bottom => {
npc.vel_x = self.rng.range(-0x200..0x200);
npc.vel_y = 0x200;
}
Direction::FacingPlayer => unreachable!(),
}
let _ = npc_list.spawn(256, npc);
}
self.x += self.vel_x;
self.y += self.vel_y;
}
self.anim_num += 1;
if self.anim_num > 1 {
self.anim_num = 0;
}
let dir_offset = match self.direction {
Direction::Left => 0,
Direction::Up => 2,
Direction::Right => 4,
Direction::Bottom => 6,
Direction::FacingPlayer => unreachable!(),
} + self.anim_num as usize;
self.anim_rect = match self.btype {
34 => state.constants.weapon.bullet_rects.b034_nemesis_l1[dir_offset],
35 => state.constants.weapon.bullet_rects.b035_nemesis_l2[dir_offset],
36 => state.constants.weapon.bullet_rects.b036_nemesis_l3[dir_offset],
_ => unreachable!(),
};
}
fn tick_spur(&mut self, state: &mut SharedGameState, new_bullets: &mut Vec<Bullet>) {
self.action_counter += 1;
if self.action_counter > self.lifetime {
@ -897,13 +1194,7 @@ impl Bullet {
}
}
pub fn tick(
&mut self,
state: &mut SharedGameState,
players: [&dyn PhysicalEntity; 2],
npc_list: &NPCList,
new_bullets: &mut Vec<Bullet>,
) {
pub fn tick(&mut self, state: &mut SharedGameState, players: [&Player; 2], npc_list: &NPCList, new_bullets: &mut Vec<Bullet>) {
if self.life == 0 {
self.cond.set_alive(false);
return;
@ -915,10 +1206,15 @@ impl Bullet {
4 | 5 | 6 => self.tick_polar_star(state),
7 | 8 | 9 => self.tick_fireball(state, players, npc_list),
10 | 11 | 12 => self.tick_machine_gun(state, npc_list),
19 => self.tick_bubble_1(state),
20 => self.tick_bubble_2(state),
21 => self.tick_bubble_3(state, players, new_bullets),
22 => self.tick_bubble_spines(state),
23 => self.tick_blade_slash(state),
25 => self.tick_blade_1(state),
26 => self.tick_blade_2(state),
27 => self.tick_blade_3(state, new_bullets),
34 | 35 | 36 => self.tick_nemesis(state, npc_list),
37 | 38 | 39 => self.tick_spur(state, new_bullets),
40 | 41 | 42 => self.tick_spur_trail(state),
_ => self.cond.set_alive(false),
@ -954,16 +1250,10 @@ impl Bullet {
self.flags.set_hit_left_wall(true);
}
} else if hits[0] && !hits[2] {
if (self.x - self.hit_bounds.left as i32) < block_x
&& (self.y - self.hit_bounds.top as i32) < block_y - (3 * 0x200)
{
if (self.x - self.hit_bounds.left as i32) < block_x && (self.y - self.hit_bounds.top as i32) < block_y - (3 * 0x200) {
self.flags.set_hit_left_wall(true);
}
} else if !hits[0]
&& hits[2]
&& (self.x - self.hit_bounds.left as i32) < block_x
&& (self.y + self.hit_bounds.top as i32) > block_y + (3 * 0x200)
{
} else if !hits[0] && hits[2] && (self.x - self.hit_bounds.left as i32) < block_x && (self.y + self.hit_bounds.top as i32) > block_y + (3 * 0x200) {
self.flags.set_hit_left_wall(true);
}
@ -973,16 +1263,10 @@ impl Bullet {
self.flags.set_hit_right_wall(true);
}
} else if hits[1] && !hits[3] {
if (self.x + self.hit_bounds.right as i32) > block_x
&& (self.y - self.hit_bounds.top as i32) < block_y - (3 * 0x200)
{
if (self.x + self.hit_bounds.right as i32) > block_x && (self.y - self.hit_bounds.top as i32) < block_y - (3 * 0x200) {
self.flags.set_hit_right_wall(true);
}
} else if !hits[1]
&& hits[3]
&& (self.x + self.hit_bounds.right as i32) > block_x
&& (self.y + self.hit_bounds.top as i32) > block_y + (3 * 0x200)
{
} else if !hits[1] && hits[3] && (self.x + self.hit_bounds.right as i32) > block_x && (self.y + self.hit_bounds.top as i32) > block_y + (3 * 0x200) {
self.flags.set_hit_right_wall(true);
}
@ -992,16 +1276,10 @@ impl Bullet {
self.flags.set_hit_top_wall(true);
}
} else if hits[0] && !hits[1] {
if (self.x - self.hit_bounds.left as i32) < block_x - (3 * 0x200)
&& (self.y - self.hit_bounds.top as i32) < block_y
{
if (self.x - self.hit_bounds.left as i32) < block_x - (3 * 0x200) && (self.y - self.hit_bounds.top as i32) < block_y {
self.flags.set_hit_top_wall(true);
}
} else if !hits[0]
&& hits[1]
&& (self.x + self.hit_bounds.right as i32) > block_x + (3 * 0x200)
&& (self.y - self.hit_bounds.top as i32) < block_y
{
} else if !hits[0] && hits[1] && (self.x + self.hit_bounds.right as i32) > block_x + (3 * 0x200) && (self.y - self.hit_bounds.top as i32) < block_y {
self.flags.set_hit_top_wall(true);
}
@ -1011,16 +1289,10 @@ impl Bullet {
self.flags.set_hit_bottom_wall(true);
}
} else if hits[2] && !hits[3] {
if (self.x - self.hit_bounds.left as i32) < block_x - (3 * 0x200)
&& (self.y + self.hit_bounds.bottom as i32) > block_y
{
if (self.x - self.hit_bounds.left as i32) < block_x - (3 * 0x200) && (self.y + self.hit_bounds.bottom as i32) > block_y {
self.flags.set_hit_bottom_wall(true);
}
} else if !hits[2]
&& hits[3]
&& (self.x + self.hit_bounds.right as i32) > block_x + (3 * 0x200)
&& (self.y + self.hit_bounds.bottom as i32) > block_y
{
} else if !hits[2] && hits[3] && (self.x + self.hit_bounds.right as i32) > block_x + (3 * 0x200) && (self.y + self.hit_bounds.bottom as i32) > block_y {
self.flags.set_hit_bottom_wall(true);
}
@ -1034,11 +1306,7 @@ impl Bullet {
} else if self.flags.hit_bottom_wall() {
self.y = block_y - self.hit_bounds.top as i32;
}
} else if self.flags.hit_left_wall()
|| self.flags.hit_top_wall()
|| self.flags.hit_right_wall()
|| self.flags.hit_bottom_wall()
{
} else if self.flags.hit_left_wall() || self.flags.hit_top_wall() || self.flags.hit_right_wall() || self.flags.hit_bottom_wall() {
self.vanish(state);
}
}
@ -1174,9 +1442,7 @@ impl PhysicalEntity for Bullet {
let _ = npc_list.spawn(0x100, npc.clone());
}
if let Some(tile) =
stage.map.tiles.get_mut(stage.map.width as usize * (y + oy) as usize + (x + ox) as usize)
{
if let Some(tile) = stage.map.tiles.get_mut(stage.map.width as usize * (y + oy) as usize + (x + ox) as usize) {
*tile = tile.wrapping_sub(1);
}
}

73
src/weapon/fireball.rs Normal file
View File

@ -0,0 +1,73 @@
use crate::caret::CaretType;
use crate::common::Direction;
use crate::player::{Player, TargetPlayer};
use crate::shared_game_state::SharedGameState;
use crate::weapon::{Weapon, WeaponLevel};
use crate::weapon::bullet::BulletManager;
impl Weapon {
pub(in crate::weapon) fn tick_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 {
let btype = match self.level {
WeaponLevel::Level1 => 7,
WeaponLevel::Level2 => 8,
WeaponLevel::Level3 => 9,
WeaponLevel::None => {
unreachable!()
}
};
if !self.consume_ammo(1) {
// todo switch to first weapon
return;
}
if player.up {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x800, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x - 0x800, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x800, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x800, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else if player.down {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x800, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x - 0x800, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x800, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x800, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0xc00, player.y + 0x400, btype, player_id, Direction::Left, &state.constants);
state.create_caret(player.x - 0x1800, player.y + 0x400, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0xc00, player.y + 0x400, btype, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 0x1800, player.y + 0x400, CaretType::Shoot, Direction::Right);
}
_ => {}
}
}
state.sound_manager.play_sfx(34)
}
}
}

112
src/weapon/machine_gun.rs Normal file
View File

@ -0,0 +1,112 @@
use crate::caret::CaretType;
use crate::common::Direction;
use crate::player::{Player, TargetPlayer};
use crate::shared_game_state::SharedGameState;
use crate::weapon::bullet::BulletManager;
use crate::weapon::{Weapon, WeaponLevel};
impl Weapon {
pub(in crate::weapon) fn tick_machine_gun(
&mut self,
player: &mut Player,
player_id: TargetPlayer,
bullet_manager: &mut BulletManager,
state: &mut SharedGameState,
) {
const BULLETS: [u16; 3] = [10, 11, 12];
if !player.controller.shoot() {
self.counter1 = 6;
self.counter2 += 1;
if (player.equip.has_turbocharge() && self.counter2 > 1) || self.counter2 > 4 {
self.counter2 = 0;
self.refill_ammo(1);
}
return;
}
if bullet_manager.count_bullets_multi(&BULLETS, player_id) > 4 {
return;
}
self.counter2 = 0; // recharge time counter
self.counter1 += 1; // autofire counter
if self.counter1 > 5 {
self.counter1 = 0;
let btype = match self.level {
WeaponLevel::Level1 => 10,
WeaponLevel::Level2 => 11,
WeaponLevel::Level3 => 12,
WeaponLevel::None => unreachable!(),
};
if !self.consume_ammo(1) {
state.sound_manager.play_sfx(37);
// todo spawn "empty" text
return;
}
match () {
_ if player.up => {
if self.level == WeaponLevel::Level3 {
player.vel_y += 0x100;
}
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x600, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x - 0x600, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x600, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x600, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
}
_ if player.down => {
if self.level == WeaponLevel::Level3 {
if player.vel_y > 0 {
player.vel_y /= 2;
}
if player.vel_y > -0x400 {
player.vel_y = (player.vel_y - 0x200).max(-0x400);
}
}
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x600, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x - 0x600, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x600, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x600, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
}
_ => match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x1800, player.y + 0x600, btype, player_id, Direction::Left, &state.constants);
state.create_caret(player.x - 0x1800, player.y + 0x600, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x1800, player.y + 0x600, btype, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 0x1800, player.y + 0x600, CaretType::Shoot, Direction::Right);
}
_ => {}
},
}
if self.level == WeaponLevel::Level3 {
state.sound_manager.play_sfx(49);
} else {
state.sound_manager.play_sfx(32);
}
}
}
}

View File

@ -0,0 +1,14 @@
use crate::player::{Player, TargetPlayer};
use crate::shared_game_state::SharedGameState;
use crate::weapon::bullet::BulletManager;
use crate::weapon::Weapon;
impl Weapon {
pub(in crate::weapon) fn tick_missile_launcher(
&mut self,
player: &mut Player,
player_id: TargetPlayer,
bullet_manager: &mut BulletManager,
state: &mut SharedGameState,
) {}
}

132
src/weapon/mod.rs Normal file
View File

@ -0,0 +1,132 @@
use num_derive::FromPrimitive;
use crate::caret::CaretType;
use crate::common::Direction;
use crate::inventory::Inventory;
use crate::player::{Player, TargetPlayer};
use crate::shared_game_state::SharedGameState;
use crate::weapon::bullet::{Bullet, BulletManager};
mod blade;
mod bubbler;
pub mod bullet;
mod fireball;
mod machine_gun;
mod missile_launcher;
mod nemesis;
mod polar_star;
mod snake;
mod spur;
mod super_missile_launcher;
#[derive(Debug, PartialEq, Eq, Copy, Clone, FromPrimitive)]
#[repr(u8)]
pub enum WeaponType {
None = 0,
Snake = 1,
PolarStar = 2,
Fireball = 3,
MachineGun = 4,
MissileLauncher = 5,
Bubbler = 7,
Blade = 9,
SuperMissileLauncher = 10,
Nemesis = 12,
Spur = 13,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[repr(u8)]
pub enum WeaponLevel {
None = 0,
Level1 = 1,
Level2 = 2,
Level3 = 3,
}
impl WeaponLevel {
pub fn next(self) -> WeaponLevel {
match self {
WeaponLevel::None => WeaponLevel::Level1,
WeaponLevel::Level1 => WeaponLevel::Level2,
WeaponLevel::Level2 => WeaponLevel::Level3,
WeaponLevel::Level3 => WeaponLevel::Level3,
}
}
pub fn prev(self) -> WeaponLevel {
match self {
WeaponLevel::None => WeaponLevel::Level1,
WeaponLevel::Level1 => WeaponLevel::Level1,
WeaponLevel::Level2 => WeaponLevel::Level1,
WeaponLevel::Level3 => WeaponLevel::Level2,
}
}
}
#[derive(Clone)]
pub struct Weapon {
pub wtype: WeaponType,
pub level: WeaponLevel,
pub experience: u16,
pub ammo: u16,
pub max_ammo: u16,
counter1: u16,
counter2: u16,
}
impl Weapon {
pub fn new(wtype: WeaponType, level: WeaponLevel, experience: u16, ammo: u16, max_ammo: u16) -> Weapon {
Weapon { wtype, level, experience, ammo, max_ammo, counter1: 0, counter2: 0 }
}
/// Consume a specified amount of bullets, returns true if there was enough ammo.
pub fn consume_ammo(&mut self, amount: u16) -> bool {
if self.max_ammo == 0 {
return true;
}
if self.ammo >= amount {
self.ammo -= amount;
return true;
}
false
}
/// Refill a specified amount of bullets.
pub fn refill_ammo(&mut self, amount: u16) {
if self.max_ammo != 0 {
self.ammo = self.ammo.saturating_add(amount).min(self.max_ammo);
}
}
pub fn tick(
&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;
}
// todo lua hook
match self.wtype {
WeaponType::None => {}
WeaponType::Snake => self.tick_snake(player, player_id, bullet_manager, state),
WeaponType::PolarStar => self.tick_polar_star(player, player_id, bullet_manager, state),
WeaponType::Fireball => self.tick_fireball(player, player_id, bullet_manager, state),
WeaponType::MachineGun => self.tick_machine_gun(player, player_id, bullet_manager, state),
WeaponType::MissileLauncher => self.tick_missile_launcher(player, player_id, bullet_manager, state),
WeaponType::Bubbler => self.tick_bubbler(player, player_id, bullet_manager, state),
WeaponType::Blade => self.tick_blade(player, player_id, bullet_manager, state),
WeaponType::SuperMissileLauncher => self.tick_super_missile_launcher(player, player_id, bullet_manager, state),
WeaponType::Nemesis => self.tick_nemesis(player, player_id, bullet_manager, state),
WeaponType::Spur => self.tick_spur(player, player_id, inventory, bullet_manager, state),
}
}
}

80
src/weapon/nemesis.rs Normal file
View File

@ -0,0 +1,80 @@
use crate::caret::CaretType;
use crate::common::Direction;
use crate::player::{Player, TargetPlayer};
use crate::shared_game_state::SharedGameState;
use crate::weapon::bullet::BulletManager;
use crate::weapon::{Weapon, WeaponLevel};
impl Weapon {
pub(in crate::weapon) fn tick_nemesis(
&mut self,
player: &Player,
player_id: TargetPlayer,
bullet_manager: &mut BulletManager,
state: &mut SharedGameState,
) {
const BULLETS: [u16; 3] = [34, 35, 36];
if !player.controller.trigger_shoot() || bullet_manager.count_bullets_multi(&BULLETS, player_id) > 1 {
return;
}
let btype = match self.level {
WeaponLevel::Level1 => 34,
WeaponLevel::Level2 => 35,
WeaponLevel::Level3 => 36,
WeaponLevel::None => unreachable!(),
};
if !self.consume_ammo(1) {
state.sound_manager.play_sfx(37);
// todo spawn "empty" text
return;
}
if player.up {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x200, player.y - 0x1800, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x - 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x200, player.y - 0x1800, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else if player.down {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x200, player.y + 0x1800, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x - 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x200, player.y + 0x1800, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
_ => {}
}
} else {
match player.direction {
Direction::Left => {
bullet_manager.create_bullet(player.x - 0x2c00, player.y + 0x600, btype, player_id, Direction::Left, &state.constants);
state.create_caret(player.x - 0x2000, player.y + 0x600, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0x2c00, player.y + 0x600, btype, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 0x2000, player.y + 0x600, CaretType::Shoot, Direction::Right);
}
_ => {}
}
}
match self.level {
WeaponLevel::Level1 => state.sound_manager.play_sfx(117),
WeaponLevel::Level2 => state.sound_manager.play_sfx(49),
WeaponLevel::Level3 => state.sound_manager.play_sfx(60),
_ => unreachable!(),
}
}
}

66
src/weapon/polar_star.rs Normal file
View File

@ -0,0 +1,66 @@
use crate::caret::CaretType;
use crate::common::Direction;
use crate::player::{Player, TargetPlayer};
use crate::shared_game_state::SharedGameState;
use crate::weapon::bullet::BulletManager;
use crate::weapon::{Weapon, WeaponLevel};
impl Weapon {
pub(in crate::weapon) fn tick_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) > 1 {
return;
}
let btype = match self.level {
WeaponLevel::Level1 => 4,
WeaponLevel::Level2 => 5,
WeaponLevel::Level3 => 6,
WeaponLevel::None => unreachable!(),
};
if !self.consume_ammo(1) {
state.sound_manager.play_sfx(37);
return;
}
match player.direction {
Direction::Left if player.up => {
bullet_manager.create_bullet(player.x - 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x - 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right if player.up => {
bullet_manager.create_bullet(player.x + 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Left if player.down => {
bullet_manager.create_bullet(player.x - 0x200, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x - 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right if player.down => {
bullet_manager.create_bullet(player.x + 0x200, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Left => {
bullet_manager.create_bullet(player.x - 0xc00, player.y + 0x600, btype, player_id, Direction::Left, &state.constants);
state.create_caret(player.x - 0xc00, player.y + 0x600, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0xc00, player.y + 0x600, btype, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 0xc00, player.y + 0x600, CaretType::Shoot, Direction::Right);
}
_ => {}
}
if self.level == WeaponLevel::Level3 {
state.sound_manager.play_sfx(49);
} else {
state.sound_manager.play_sfx(32);
}
}
}

70
src/weapon/snake.rs Normal file
View File

@ -0,0 +1,70 @@
use crate::caret::CaretType;
use crate::common::Direction;
use crate::player::{Player, TargetPlayer};
use crate::shared_game_state::SharedGameState;
use crate::weapon::bullet::{Bullet, BulletManager};
use crate::weapon::{Weapon, WeaponLevel};
impl Weapon {
pub(in crate::weapon) fn tick_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) > 3 {
return;
}
let btype = match self.level {
WeaponLevel::Level1 => 1,
WeaponLevel::Level2 => 2,
WeaponLevel::Level3 => 3,
WeaponLevel::None => unreachable!(),
};
if !self.consume_ammo(1) {
// todo switch to first weapon
return;
}
self.counter1 = self.counter1.wrapping_add(1);
match player.direction {
Direction::Left if player.up => {
let mut bullet = Bullet::new(player.x - 0x600, 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 - 0x600, player.y - 10 * 0x200, CaretType::Shoot, Direction::Left);
}
Direction::Right if player.up => {
let mut bullet = Bullet::new(player.x + 0x600, 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 + 0x600, player.y - 10 * 0x200, CaretType::Shoot, Direction::Left);
}
Direction::Left if player.down => {
let mut bullet = Bullet::new(player.x - 0x600, 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 - 0x600, player.y + 10 * 0x200, CaretType::Shoot, Direction::Left);
}
Direction::Right if player.down => {
let mut bullet = Bullet::new(player.x + 0x600, 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 + 0x600, player.y + 10 * 0x200, CaretType::Shoot, Direction::Left);
}
Direction::Left => {
let mut bullet = Bullet::new(player.x - 0xc00, player.y + 0x400, btype, player_id, Direction::Left, &state.constants);
bullet.target_x = self.counter1 as i32;
bullet_manager.push_bullet(bullet);
state.create_caret(player.x - 0x1800, player.y + 0x400, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
let mut bullet = Bullet::new(player.x + 0xc00, player.y + 0x400, btype, player_id, Direction::Right, &state.constants);
bullet.target_x = self.counter1 as i32;
bullet_manager.push_bullet(bullet);
state.create_caret(player.x + 0x1800, player.y + 0x400, CaretType::Shoot, Direction::Right);
}
_ => {}
}
state.sound_manager.play_sfx(33);
}
}

126
src/weapon/spur.rs Normal file
View File

@ -0,0 +1,126 @@
use crate::caret::CaretType;
use crate::common::Direction;
use crate::inventory::Inventory;
use crate::player::{Player, TargetPlayer};
use crate::shared_game_state::SharedGameState;
use crate::weapon::bullet::BulletManager;
use crate::weapon::{Weapon, WeaponLevel};
impl Weapon {
pub(in crate::weapon) fn tick_spur(
&mut self,
player: &mut Player,
player_id: TargetPlayer,
inventory: &mut Inventory,
bullet_manager: &mut BulletManager,
state: &mut SharedGameState,
) {
const BULLETS: [u16; 6] = [44, 45, 46, 47, 48, 49];
let mut shoot = false;
let btype;
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!(),
}
if bullet_manager.count_bullets_multi(&BULLETS, player_id) > 0 || !(player.controller.trigger_shoot() || shoot) {
return;
}
if !self.consume_ammo(1) {
state.sound_manager.play_sfx(37);
} else {
match player.direction {
Direction::Left if player.up => {
bullet_manager.create_bullet(player.x - 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x - 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right if player.up => {
bullet_manager.create_bullet(player.x + 0x200, player.y - 0x1000, btype, player_id, Direction::Up, &state.constants);
state.create_caret(player.x + 0x200, player.y - 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Left if player.down => {
bullet_manager.create_bullet(player.x - 0x200, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x - 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Right if player.down => {
bullet_manager.create_bullet(player.x + 0x200, player.y + 0x1000, btype, player_id, Direction::Bottom, &state.constants);
state.create_caret(player.x + 0x200, player.y + 0x1000, CaretType::Shoot, Direction::Left);
}
Direction::Left => {
bullet_manager.create_bullet(player.x - 0xc00, player.y + 0x600, btype, player_id, Direction::Left, &state.constants);
state.create_caret(player.x - 0xc00, player.y + 0x600, CaretType::Shoot, Direction::Left);
}
Direction::Right => {
bullet_manager.create_bullet(player.x + 0xc00, player.y + 0x600, btype, player_id, Direction::Right, &state.constants);
state.create_caret(player.x + 0xc00, player.y + 0x600, CaretType::Shoot, Direction::Right);
}
_ => {}
}
let sound = match btype {
6 => 49,
37 => 62,
38 => 63,
39 => 64,
_ => 0,
};
state.sound_manager.play_sfx(sound);
}
}
}

View File

@ -0,0 +1,23 @@
use crate::player::{Player, TargetPlayer};
use crate::shared_game_state::SharedGameState;
use crate::weapon::bullet::BulletManager;
use crate::weapon::{Weapon, WeaponLevel};
impl Weapon {
pub(in crate::weapon) fn tick_super_missile_launcher(
&mut self,
player: &mut Player,
player_id: TargetPlayer,
bullet_manager: &mut BulletManager,
state: &mut SharedGameState,
) {
const BULLETS: [u16; 6] = [28, 29, 30, 31, 32, 33];
let btype = match self.level {
WeaponLevel::Level1 => 28,
WeaponLevel::Level2 => 29,
WeaponLevel::Level3 => 30,
WeaponLevel::None => unreachable!(),
};
}
}