mirror of https://github.com/nmlgc/ReC98.git
195 lines
4.8 KiB
C++
195 lines
4.8 KiB
C++
#if (GAME == 5)
|
||
#include "th05/sprites/main_pat.h"
|
||
#endif
|
||
#include "th04/main/player/player.hpp"
|
||
#include "th04/math/randring.hpp"
|
||
#include "th04/sprites/cels.h"
|
||
#include "th02/main/entity.hpp"
|
||
|
||
// Sets [velocity] to a vector with the given [angle] and a 12-pixel length.
|
||
//
|
||
// TH05 also insists on setting shot_t::angle via a ridiculous out-of-bounds
|
||
// access, and therefore *must* be called with [velocity] pointing inside a
|
||
// shot_t structure!
|
||
SPPoint pascal near shot_velocity_set(
|
||
SPPoint near* velocity, unsigned char angle
|
||
);
|
||
|
||
#if (GAME == 5)
|
||
static const int HITSHOT_FRAMES_PER_CEL = 3;
|
||
#else
|
||
static const int HITSHOT_FRAMES_PER_CEL = 4;
|
||
#endif
|
||
|
||
static const int HITSHOT_FRAMES = (HITSHOT_FRAMES_PER_CEL * HITSHOT_CELS);
|
||
|
||
#if (GAME == 4)
|
||
enum shot_flag_th04_t {
|
||
SF_FREE = 0,
|
||
SF_ALIVE = 1,
|
||
SF_HIT = 2,
|
||
SF_REMOVE = (SF_HIT + HITSHOT_FRAMES),
|
||
|
||
_shot_flag_th04_t_FORCE_UINT8 = 0xFF
|
||
};
|
||
#endif
|
||
|
||
struct Shot {
|
||
#if (GAME == 5)
|
||
entity_flag_t flag;
|
||
#else
|
||
shot_flag_th04_t flag;
|
||
#endif
|
||
char age;
|
||
PlayfieldMotion pos;
|
||
// The displayed sprite changes between this one and
|
||
// [patnum_base + 1] every two frames.
|
||
#if GAME == 5
|
||
char patnum_base;
|
||
char type;
|
||
#else
|
||
int patnum_base;
|
||
#endif
|
||
char damage;
|
||
char angle; // Unused in TH04
|
||
|
||
void from_option_l(float offset = 0.0f) {
|
||
this->pos.cur.x -= PLAYER_OPTION_DISTANCE + offset;
|
||
}
|
||
|
||
void from_option_r(float offset = 0.0f) {
|
||
this->pos.cur.x += PLAYER_OPTION_DISTANCE + offset;
|
||
}
|
||
|
||
#if (GAME == 5)
|
||
void set_option_sprite() {
|
||
this->patnum_base = PAT_SHOT_SUB;
|
||
}
|
||
|
||
void set_option_sprite_and_damage(char damage) {
|
||
set_option_sprite();
|
||
this->damage = damage;
|
||
}
|
||
#endif
|
||
|
||
void set_random_angle_forwards(
|
||
unsigned char min = -0x48, unsigned char max = -0x38
|
||
) {
|
||
shot_velocity_set(
|
||
(SPPoint near*)&this->pos.velocity,
|
||
randring1_next8_and_ge_lt(min, max)
|
||
);
|
||
}
|
||
};
|
||
|
||
#if (GAME == 5)
|
||
#define SHOT_COUNT 64
|
||
#else
|
||
#define SHOT_COUNT 68
|
||
#endif
|
||
|
||
extern unsigned char shot_time;
|
||
extern Shot near shots[SHOT_COUNT];
|
||
|
||
// Points to the next free entry in [shots].
|
||
extern Shot near *shot_ptr;
|
||
// Index of the last valid entry in [shots].
|
||
extern char shot_last_id;
|
||
|
||
// Current area on the playfield for which to check shot collisions.
|
||
extern SPPoint shot_hitbox_center;
|
||
extern SPPoint shot_hitbox_radius;
|
||
|
||
// If `true`, shot damage is divided by 4 during bombs. In TH05, `true` also
|
||
// slightly increases damage output for Reimu's and Yuuka's shots, and is also
|
||
// used against midbosses.
|
||
extern bool shots_hittest_against_boss;
|
||
|
||
// Only used for hit detection in TH04. TH05 also uses it in shots_render().
|
||
struct shot_alive_t {
|
||
// For TH05, [x] == Subpixel::None() indicates that this shot has been
|
||
// converted to a hitshot.
|
||
SPPoint pos;
|
||
|
||
Shot near *shot;
|
||
};
|
||
extern unsigned int shots_alive_count;
|
||
extern shot_alive_t shots_alive[SHOT_COUNT];
|
||
|
||
// Searches and returns the next free shot slot, or a nullptr if there are no
|
||
// more free ones.
|
||
Shot near* near shots_add(void);
|
||
|
||
// Processes collisions of all shots against the shot_hitbox, decays any
|
||
// colliding shots, and returns the total amount of damage dealt.
|
||
int shots_hittest(void);
|
||
|
||
inline int shots_hittest(
|
||
const PlayfieldPoint ¢er,
|
||
const subpixel_t &radius_x,
|
||
const subpixel_t &radius_y
|
||
) {
|
||
shot_hitbox_radius.x.v = radius_x;
|
||
shot_hitbox_radius.y.v = radius_y;
|
||
if(GAME == 5) {
|
||
shot_hitbox_center = center;
|
||
} else {
|
||
shot_hitbox_center.x.v = center.x.v;
|
||
shot_hitbox_center.y.v = center.y.v;
|
||
}
|
||
return shots_hittest();
|
||
}
|
||
|
||
// Also renders hitshots in TH05.
|
||
void near shots_render(void);
|
||
|
||
// Option laser
|
||
// ------------
|
||
// Unused in TH05, but still present in the code.
|
||
|
||
static const pixel_t SHOT_LASER_W = 8;
|
||
static const unsigned int SHOT_LASER_COOLDOWN_FRAMES = 32;
|
||
|
||
typedef enum {
|
||
SHOT_LASER_CEL_0,
|
||
SHOT_LASER_CEL_1,
|
||
SHOT_LASER_CEL_2,
|
||
SHOT_LASER_CEL_3,
|
||
SHOT_LASER_CEL_4,
|
||
SHOT_LASER_CELS,
|
||
} shot_laser_cel_t;
|
||
|
||
// Describes the longest width and segments of the laser in pixels. Has no
|
||
// effect on damage or hitbox width.
|
||
enum shot_laser_style_t {
|
||
SLS_2 = 0, // ||
|
||
SLS_4 = 1, // ||||
|
||
SLS_6 = 2, // ||||||
|
||
SLS_1_4_1 = 3, // | |||| |
|
||
SLS_8 = 4, // ||||||||
|
||
|
||
_shot_laser_style_t_FORCE_UINT8 = 0xFF
|
||
};
|
||
|
||
// A laser is active as long as this is > [SHOT_LASER_COOLDOWN_FRAMES].
|
||
extern unsigned int shot_laser_time;
|
||
|
||
extern shot_laser_style_t shot_laser_style;
|
||
|
||
// Timer for the 16×16 ring shots that are spawned on top of the laser. These
|
||
// retain the fixed trajectory they were spawned at, and don't follow the
|
||
// laser.
|
||
extern uint8_t shot_laser_ring_cycle;
|
||
|
||
// Equivalent to the position of the player.
|
||
extern PlayfieldMotion shot_laser_bottomcenter;
|
||
|
||
// Takes a Subpixel for [h].
|
||
#define shot_laser_put(left, top, h, cel) \
|
||
_SI = h; \
|
||
shot_laser_put_raw(left, top, cel);
|
||
void __fastcall near shot_laser_put_raw(
|
||
screen_x_t left, vram_y_t top, shot_laser_cel_t cel
|
||
);
|
||
// ------------
|