2021-07-22 18:48:47 +00:00
|
|
|
|
#pragma option -G
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
#include "platform.h"
|
|
|
|
|
#include "pc98.h"
|
|
|
|
|
#include "planar.h"
|
2022-02-28 18:56:15 +00:00
|
|
|
|
#include "master.hpp"
|
2021-07-22 18:48:47 +00:00
|
|
|
|
#include "th01/math/overlap.hpp"
|
|
|
|
|
#include "th01/math/subpixel.hpp"
|
|
|
|
|
#include "th04/math/motion.hpp"
|
|
|
|
|
#include "th04/math/vector.hpp"
|
|
|
|
|
#include "th04/main/frames.h"
|
|
|
|
|
#include "th04/main/scroll.hpp"
|
|
|
|
|
#include "th04/main/playfld.hpp"
|
2022-02-28 18:56:15 +00:00
|
|
|
|
#include "th04/main/drawp.hpp"
|
2021-07-22 18:48:47 +00:00
|
|
|
|
#include "th04/main/playperf.hpp"
|
|
|
|
|
#include "th04/main/rank.hpp"
|
|
|
|
|
#include "th04/main/score.hpp"
|
|
|
|
|
#include "th04/main/slowdown.hpp"
|
|
|
|
|
#include "th04/main/spark.hpp"
|
2020-09-13 17:52:38 +00:00
|
|
|
|
#include "th04/main/bullet/bullet.hpp"
|
2021-07-22 18:48:47 +00:00
|
|
|
|
#include "th04/main/hud/hud.h"
|
|
|
|
|
#include "th04/main/hud/popup.hpp"
|
|
|
|
|
#include "th04/main/player/player.hpp"
|
|
|
|
|
#include "th04/main/pointnum/pointnum.hpp"
|
|
|
|
|
#include "th04/main/gather.hpp"
|
|
|
|
|
|
|
|
|
|
#if (GAME == 5)
|
2021-11-22 10:48:24 +00:00
|
|
|
|
#include "th04/main/item/item.hpp"
|
2021-07-22 18:48:47 +00:00
|
|
|
|
#include "th05/sprites/main_pat.h"
|
|
|
|
|
|
|
|
|
|
static const int SLOWDOWN_BULLET_THRESHOLD_UNUSED = 32;
|
2022-02-20 10:20:21 +00:00
|
|
|
|
|
|
|
|
|
// Updates [bullet]'s patnum based on its current angle.
|
|
|
|
|
void pascal near bullet_update_patnum(bullet_t near &bullet)
|
|
|
|
|
{
|
|
|
|
|
unsigned char patnum_base; // main_patnum_t, obviously
|
|
|
|
|
|
|
|
|
|
if(bullet.patnum < PAT_BULLET16_D) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(bullet.patnum < (PAT_BULLET16_D_BLUE_last + 1)) {
|
|
|
|
|
patnum_base = PAT_BULLET16_D_BLUE;
|
|
|
|
|
} else if(bullet.patnum < (PAT_BULLET16_D_GREEN_last + 1)) {
|
|
|
|
|
patnum_base = PAT_BULLET16_D_GREEN;
|
|
|
|
|
} else if(bullet.patnum < (PAT_BULLET16_V_RED_last + 1)) {
|
|
|
|
|
patnum_base = PAT_BULLET16_V_RED;
|
|
|
|
|
} else {
|
|
|
|
|
patnum_base = PAT_BULLET16_V_BLUE;
|
|
|
|
|
}
|
|
|
|
|
bullet.patnum = bullet_patnum_for_angle(patnum_base, bullet.angle);
|
|
|
|
|
}
|
2021-07-22 18:48:47 +00:00
|
|
|
|
#else
|
|
|
|
|
#include "th04/sprites/main_pat.h"
|
|
|
|
|
|
|
|
|
|
static const int SLOWDOWN_BULLET_THRESHOLD_UNUSED = 24;
|
2022-02-28 18:56:15 +00:00
|
|
|
|
|
|
|
|
|
inline void bullet_update_patnum(bullet_t near &bullet) {
|
|
|
|
|
}
|
2021-07-22 18:48:47 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#pragma option -a2
|
2020-09-13 17:52:38 +00:00
|
|
|
|
|
2022-02-28 18:56:15 +00:00
|
|
|
|
#define bullet_velocity_set_from_angle_and_speed(bullet) \
|
|
|
|
|
vector2_near(bullet.pos.velocity, bullet.angle, bullet.speed_cur)
|
|
|
|
|
|
2020-09-13 17:52:38 +00:00
|
|
|
|
void pascal near bullet_turn_x(bullet_t near &bullet)
|
2022-02-20 09:39:08 +00:00
|
|
|
|
{
|
|
|
|
|
bullet.ax.turns_done++;
|
|
|
|
|
bullet.angle = (0x80 - bullet.angle);
|
|
|
|
|
if(bullet.ax.turns_done >= bullet_special_motion.turns_max) {
|
|
|
|
|
bullet.move_state = BMS_REGULAR;
|
|
|
|
|
}
|
|
|
|
|
bullet_velocity_set_from_angle_and_speed(bullet);
|
|
|
|
|
bullet_update_patnum(bullet);
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-13 17:52:38 +00:00
|
|
|
|
void pascal near bullet_turn_y(bullet_t near &bullet)
|
2022-02-20 09:39:08 +00:00
|
|
|
|
{
|
|
|
|
|
bullet.ax.turns_done++;
|
|
|
|
|
bullet.angle = (/* 0x00 */ - bullet.angle);
|
|
|
|
|
if(bullet.ax.turns_done >= bullet_special_motion.turns_max) {
|
|
|
|
|
bullet.move_state = BMS_REGULAR;
|
|
|
|
|
}
|
|
|
|
|
bullet_velocity_set_from_angle_and_speed(bullet);
|
|
|
|
|
bullet_update_patnum(bullet);
|
|
|
|
|
}
|
2021-07-22 18:48:47 +00:00
|
|
|
|
|
2022-02-28 18:56:15 +00:00
|
|
|
|
#define bullet_turn_complete(bullet) \
|
|
|
|
|
bullet_update_patnum(bullet); \
|
|
|
|
|
bullet.speed_cur = bullet.speed_final; \
|
|
|
|
|
if(bullet.ax.turns_done >= bullet_special_motion.turns_max) { \
|
|
|
|
|
bullet.move_state = BMS_REGULAR; \
|
|
|
|
|
} \
|
|
|
|
|
bullet_velocity_set_from_angle_and_speed(bullet);
|
2021-07-22 18:48:47 +00:00
|
|
|
|
|
2022-02-28 18:56:15 +00:00
|
|
|
|
enum bullet_bounce_edge_t {
|
|
|
|
|
BBE_LEFT = (1 << 0),
|
|
|
|
|
BBE_RIGHT = (1 << 1),
|
|
|
|
|
BBE_TOP = (1 << 2),
|
|
|
|
|
BBE_BOTTOM = (1 << 3),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define bullet_bounce(bullet, edges) \
|
|
|
|
|
if( \
|
|
|
|
|
((edges & BBE_LEFT) && (bullet.pos.cur.x.v <= to_sp(0.0f))) || \
|
|
|
|
|
((edges & BBE_RIGHT) && (bullet.pos.cur.x.v >= to_sp(PLAYFIELD_W))) \
|
|
|
|
|
) { \
|
|
|
|
|
bullet_turn_x(bullet); \
|
|
|
|
|
} \
|
|
|
|
|
if(\
|
|
|
|
|
((edges & BBE_TOP) && (bullet.pos.cur.y.v <= to_sp(0.0f))) || \
|
|
|
|
|
((edges & BBE_BOTTOM) && (bullet.pos.cur.y.v >= to_sp(PLAYFIELD_H))) \
|
|
|
|
|
) { \
|
|
|
|
|
bullet_turn_y(bullet); \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculates [bullet.velocity] for the special motion types.
|
|
|
|
|
void pascal near bullet_update_special(bullet_t near &bullet)
|
|
|
|
|
{
|
|
|
|
|
switch(bullet.special_motion) {
|
|
|
|
|
case BSM_SLOWDOWN_THEN_TURN_AIMED:
|
|
|
|
|
if(bullet.speed_cur.v != to_sp(0.0f)) {
|
|
|
|
|
bullet_velocity_set_from_angle_and_speed(bullet);
|
|
|
|
|
bullet.speed_cur.v--; // -= to_sp(1 / 16.0f)
|
|
|
|
|
} else {
|
|
|
|
|
bullet.ax.turns_done++;
|
|
|
|
|
bullet.angle = iatan2(
|
|
|
|
|
(player_pos.cur.y.v - bullet.pos.cur.y.v),
|
|
|
|
|
(player_pos.cur.x.v - bullet.pos.cur.x.v)
|
|
|
|
|
);
|
|
|
|
|
bullet_turn_complete(bullet);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BSM_SLOWDOWN_THEN_TURN:
|
|
|
|
|
if(bullet.speed_cur.v != to_sp(0.0f)) {
|
|
|
|
|
bullet_velocity_set_from_angle_and_speed(bullet);
|
|
|
|
|
bullet.speed_cur.v--; // -= to_sp(1 / 16.0f)
|
|
|
|
|
} else {
|
|
|
|
|
bullet.ax.turns_done++;
|
|
|
|
|
bullet.angle += bullet.dx.angle.turn_by;
|
|
|
|
|
bullet_turn_complete(bullet);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BSM_SPEEDUP:
|
|
|
|
|
bullet_velocity_set_from_angle_and_speed(bullet);
|
|
|
|
|
bullet.speed_cur.v += bullet_special_motion.speed_delta.v;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BSM_SLOWDOWN_TO_ANGLE:
|
|
|
|
|
if(bullet.speed_cur.v != to_sp(0.0f)) {
|
|
|
|
|
bullet_velocity_set_from_angle_and_speed(bullet);
|
|
|
|
|
// >= to_sp(2 / 16.0f) would have been cleaner
|
|
|
|
|
if(bullet.speed_cur.v > to_sp8(1 / 16.0f)) {
|
|
|
|
|
bullet.speed_cur.v += to_sp(-2 / 16.0f);
|
|
|
|
|
} else {
|
|
|
|
|
bullet.speed_cur.set(0.0f);
|
|
|
|
|
}
|
|
|
|
|
if(bullet.speed_cur.v < to_sp8(2.0f)) {
|
|
|
|
|
bullet.angle += (static_cast<int8_t>(
|
|
|
|
|
bullet.dx.angle.target - bullet.angle
|
|
|
|
|
) / 4);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
bullet.angle = bullet.dx.angle.target;
|
|
|
|
|
bullet.speed_cur.v = bullet.speed_final.v;
|
|
|
|
|
bullet.move_state = BMS_REGULAR;
|
|
|
|
|
bullet_velocity_set_from_angle_and_speed(bullet);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BSM_BOUNCE_LEFT_RIGHT:
|
|
|
|
|
bullet_bounce(bullet, (BBE_LEFT | BBE_RIGHT));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BSM_BOUNCE_TOP_BOTTOM:
|
|
|
|
|
bullet_bounce(bullet, (BBE_TOP | BBE_BOTTOM));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BSM_BOUNCE_LEFT_RIGHT_TOP_BOTTOM:
|
|
|
|
|
bullet_bounce(bullet, (BBE_LEFT | BBE_RIGHT | BBE_TOP | BBE_BOTTOM));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BSM_BOUNCE_LEFT_RIGHT_TOP:
|
|
|
|
|
bullet_bounce(bullet, (BBE_LEFT | BBE_RIGHT | BBE_TOP));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case BSM_GRAVITY:
|
|
|
|
|
if(stage_frame_mod2) {
|
|
|
|
|
bullet.pos.velocity.y.v += bullet_special_motion.speed_delta;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
#if (GAME == 5)
|
|
|
|
|
case BSM_EXACT_LINEAR:
|
|
|
|
|
bullet.distance.v += bullet.speed_cur.v;
|
|
|
|
|
vector2_at(
|
|
|
|
|
drawpoint,
|
|
|
|
|
bullet.origin.x.v,
|
|
|
|
|
bullet.origin.y.v,
|
|
|
|
|
bullet.distance.v,
|
|
|
|
|
bullet.angle
|
|
|
|
|
);
|
|
|
|
|
bullet.pos.velocity.x.v = (drawpoint.x.v - bullet.pos.cur.x.v);
|
|
|
|
|
bullet.pos.velocity.y.v = (drawpoint.y.v - bullet.pos.cur.y.v);
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-22 18:48:47 +00:00
|
|
|
|
|
|
|
|
|
void bullets_update(void)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int bullets_seen = 0;
|
|
|
|
|
#if (GAME == 5)
|
|
|
|
|
pellet_clouds_render_count = 0;
|
|
|
|
|
#endif
|
|
|
|
|
pellets_render_count = 0;
|
|
|
|
|
bullet_t near *bullet = &bullets[BULLET_COUNT - 1];
|
|
|
|
|
|
|
|
|
|
// Since we iterate over the bullet array backwards, we encounter the 16×16
|
|
|
|
|
// bullets first.
|
|
|
|
|
#define is_bullet16(i) \
|
|
|
|
|
(i < BULLET16_COUNT)
|
|
|
|
|
|
|
|
|
|
#define is_pellet(i) \
|
|
|
|
|
!is_bullet16(i)
|
|
|
|
|
|
|
|
|
|
if(bullet_zap.active == false) {
|
|
|
|
|
for(i = 0; i < BULLET_COUNT; i++, bullet--) {
|
|
|
|
|
if(bullet->flag == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if(bullet->flag == 2) {
|
|
|
|
|
bullet->flag = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
bullets_seen++;
|
|
|
|
|
if(bullet_clear_time) {
|
|
|
|
|
if(bullet->move_state < BMS_DECAY) {
|
|
|
|
|
bullet->move_state = BMS_DECAY;
|
|
|
|
|
bullet->patnum = is_bullet16(i)
|
|
|
|
|
? PAT_DECAY_BULLET16
|
|
|
|
|
: PAT_DECAY_PELLET;
|
|
|
|
|
|
|
|
|
|
if(bullet->age != 0) {
|
|
|
|
|
score_delta += 100;
|
|
|
|
|
} else {
|
|
|
|
|
score_delta += 10;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
reinterpret_cast<unsigned char &>(bullet->move_state)++;
|
|
|
|
|
if(bullet->move_state >= BMS_DECAY_END) {
|
|
|
|
|
bullet->pos.update_seg3();
|
|
|
|
|
bullet->flag = 2;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if((bullet->move_state % BMS_DECAY_FRAMES_PER_CEL) == 0) {
|
|
|
|
|
bullet->patnum++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bullet->age++;
|
|
|
|
|
if(bullet->spawn_state >= BSS_ACTIVE) {
|
|
|
|
|
if(bullet->spawn_state == BSS_ACTIVE) {
|
|
|
|
|
bullet->spawn_state = BSS_GRAZEABLE;
|
|
|
|
|
} else {
|
|
|
|
|
// In delay cloud state
|
|
|
|
|
if(bullet->spawn_state == BSS_CLOUD_BACKWARDS) {
|
|
|
|
|
bullet->pos.prev = bullet->pos.cur;
|
|
|
|
|
bullet->pos.cur.x.v -= (bullet->pos.velocity.x.v << 3);
|
|
|
|
|
bullet->pos.cur.y.v -= (bullet->pos.velocity.y.v << 3);
|
|
|
|
|
bullet->spawn_state = BSS_CLOUD_FORWARDS;
|
|
|
|
|
} else if(bullet->spawn_state == BSS_CLOUD_FORWARDS) {
|
|
|
|
|
bullet->pos.update_seg3();
|
|
|
|
|
} else {
|
|
|
|
|
bullet->pos.prev = bullet->pos.cur;
|
|
|
|
|
bullet->pos.cur.x.v += (bullet->pos.velocity.x.v / 3);
|
|
|
|
|
bullet->pos.cur.y.v += (bullet->pos.velocity.y.v / 3);
|
|
|
|
|
}
|
|
|
|
|
reinterpret_cast<unsigned char &>(bullet->spawn_state)++;
|
|
|
|
|
if(bullet->spawn_state >= BSS_CLOUD_END) {
|
|
|
|
|
#if (GAME == 5)
|
|
|
|
|
if(!playfield_encloses_yx_lt_ge(
|
|
|
|
|
bullet->pos.cur.x,
|
|
|
|
|
bullet->pos.cur.y,
|
|
|
|
|
BULLET16_W,
|
|
|
|
|
BULLET16_H
|
|
|
|
|
)) {
|
|
|
|
|
bullet->flag = 2;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
bullet->spawn_state = BSS_ACTIVE;
|
|
|
|
|
}
|
|
|
|
|
#if (GAME == 5)
|
|
|
|
|
else if(is_pellet(i)) {
|
|
|
|
|
pellet_clouds_render[pellet_clouds_render_count++] =
|
|
|
|
|
bullet;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(bullet->move_state == BMS_SPECIAL) {
|
|
|
|
|
bullet_update_special(*bullet);
|
|
|
|
|
} else if(bullet->move_state == BMS_SLOWDOWN) {
|
|
|
|
|
bullet->ax.slowdown_time--;
|
|
|
|
|
bullet->speed_cur.v = (bullet->speed_final.v + ((
|
2022-02-26 23:36:22 +00:00
|
|
|
|
bullet->ax.slowdown_time * bullet->dx.slowdown_speed_delta.v
|
2021-07-22 18:48:47 +00:00
|
|
|
|
) / BMS_SLOWDOWN_FRAMES));
|
|
|
|
|
if(bullet->ax.slowdown_time == 0) {
|
|
|
|
|
bullet->speed_cur = bullet->speed_final;
|
|
|
|
|
bullet->move_state = BMS_REGULAR;
|
|
|
|
|
}
|
2022-02-28 18:56:15 +00:00
|
|
|
|
bullet_velocity_set_from_angle_and_speed((*bullet));
|
2021-07-22 18:48:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* DX:AX = */ bullet->pos.update_seg3();
|
|
|
|
|
if(!playfield_encloses(_AX, _DX, BULLET16_W, BULLET16_H)) {
|
|
|
|
|
bullet->flag = 2;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(bullet_clear_time != 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
_AX -= player_pos.cur.x.v;
|
|
|
|
|
_DX -= player_pos.cur.y.v;
|
|
|
|
|
if(player_invincibility_time == 0) {
|
|
|
|
|
// Yup, a bullet must have been grazed in a previous frame
|
|
|
|
|
// before it can be collided with.
|
|
|
|
|
if(bullet->spawn_state != BSS_GRAZEABLE) {
|
|
|
|
|
if(overlap_wh_inplace_fast(
|
|
|
|
|
_AX, _DX, BULLET_KILLBOX_W, BULLET_KILLBOX_H
|
|
|
|
|
)) {
|
|
|
|
|
bullet->flag = 2;
|
|
|
|
|
player_is_hit = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Yes, the graze box is biased to the right, and taller
|
|
|
|
|
// than wide.
|
|
|
|
|
if(overlap_offcenter_inplace_fast(
|
|
|
|
|
_AX, _DX,
|
|
|
|
|
to_sp(16.0f), to_sp(22.0f), to_sp(20.0f), to_sp(22.0f)
|
|
|
|
|
)) {
|
|
|
|
|
/* TODO: Replace with the decompiled call
|
|
|
|
|
* sparks_add_random(bullet->pos.cur.x, bullet->pos.cur.y, to_sp(2.0f), 2);
|
|
|
|
|
* once that function is part of this translation unit */
|
2022-02-12 22:36:00 +00:00
|
|
|
|
_asm {
|
2021-07-22 18:48:47 +00:00
|
|
|
|
db 0xFF, 0x74, 0x02;
|
|
|
|
|
db 0xFF, 0x74, 0x04;
|
|
|
|
|
db 0x66, 0x68, 2, 0x00, (2 * 16), 0x00;
|
|
|
|
|
nop;
|
|
|
|
|
push cs;
|
|
|
|
|
call near ptr sparks_add_random;
|
|
|
|
|
}
|
|
|
|
|
bullet->spawn_state = BSS_GRAZED;
|
|
|
|
|
if(stage_graze < STAGE_GRAZE_CAP) {
|
|
|
|
|
stage_graze++;
|
|
|
|
|
hud_graze_put();
|
|
|
|
|
score_delta += graze_score;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(is_pellet(i)) {
|
|
|
|
|
pellets_render[pellets_render_count].top.left = (
|
|
|
|
|
bullet->pos.cur.to_screen_left(PELLET_W)
|
|
|
|
|
);
|
|
|
|
|
pellets_render[pellets_render_count].top.top = (
|
|
|
|
|
bullet->pos.cur.to_vram_top_scrolled_seg3(PELLET_H)
|
|
|
|
|
);
|
|
|
|
|
pellets_render_count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(turbo_mode == false) {
|
|
|
|
|
#if (GAME == 5)
|
|
|
|
|
slowdown_caused_by_bullets = false;
|
|
|
|
|
i = 42;
|
|
|
|
|
#else
|
|
|
|
|
i = 24;
|
|
|
|
|
i += playperf;
|
|
|
|
|
#endif
|
|
|
|
|
i += (rank * 8);
|
|
|
|
|
if(bullets_seen >= i) {
|
|
|
|
|
if(!stage_frame_mod2) {
|
|
|
|
|
slowdown_factor = 2;
|
|
|
|
|
#if (GAME == 5)
|
|
|
|
|
slowdown_caused_by_bullets = true;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
} else if(bullets_seen >= (i + SLOWDOWN_BULLET_THRESHOLD_UNUSED)) {
|
|
|
|
|
// Yes, never executed, as the first condition would then have
|
|
|
|
|
// been true as well
|
|
|
|
|
slowdown_factor = 2;
|
|
|
|
|
#if (GAME == 5)
|
|
|
|
|
slowdown_caused_by_bullets = true;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// A bit wasteful to run all of this every frame, since no new bullets
|
|
|
|
|
// are spawned while [bullet_zap] is active; the bullet spawn wrapper
|
|
|
|
|
// functions prevent that. Then again, this means that the code here
|
|
|
|
|
// does kind of rely on bullets not being spawned through other methods.
|
|
|
|
|
|
|
|
|
|
unsigned int score_per_bullet;
|
|
|
|
|
unsigned int score_step;
|
|
|
|
|
|
|
|
|
|
// Without this cap, the [popup_bonus] formula would be
|
|
|
|
|
// 0.5n³ - n² + 1.5n
|
|
|
|
|
// with n = [bullets_seen].
|
|
|
|
|
unsigned int score_per_bullet_cap;
|
|
|
|
|
unsigned char patnum;
|
|
|
|
|
|
|
|
|
|
patnum =
|
|
|
|
|
(PAT_BULLET_ZAP + (bullet_zap.frames / BULLET_ZAP_FRAMES_PER_CEL)
|
|
|
|
|
);
|
|
|
|
|
score_per_bullet = 1;
|
|
|
|
|
score_step = 1;
|
|
|
|
|
#if (GAME == 5)
|
|
|
|
|
// ZUN bug: All code below uses TH04's patnum for that sprite. This
|
|
|
|
|
// causes the decay animation to never actually play in TH05.
|
|
|
|
|
#define PAT_BULLET_ZAP 72
|
|
|
|
|
|
|
|
|
|
score_per_bullet_cap = (rank == RANK_EXTRA)
|
|
|
|
|
? 1600
|
|
|
|
|
: select_for_rank(960, 1280, 1280, 1280);
|
|
|
|
|
#else
|
|
|
|
|
switch(rank) {
|
|
|
|
|
case RANK_EASY:
|
|
|
|
|
score_per_bullet_cap = 1000;
|
|
|
|
|
break;
|
|
|
|
|
case RANK_NORMAL:
|
|
|
|
|
case RANK_HARD:
|
|
|
|
|
case RANK_LUNATIC:
|
|
|
|
|
score_per_bullet_cap = 1600;
|
|
|
|
|
break;
|
|
|
|
|
case RANK_EXTRA:
|
|
|
|
|
score_per_bullet_cap = 2000;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
popup_bonus = 0;
|
|
|
|
|
for(i = 0; i < BULLET_COUNT; i++, bullet--) {
|
|
|
|
|
if(bullet->flag != 1) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
bullet->pos.velocity.set(0.0f, 0.0f);
|
|
|
|
|
bullet->pos.update_seg3();
|
|
|
|
|
|
|
|
|
|
// ZUN bug: Always false in TH05, see above
|
|
|
|
|
if(patnum < (PAT_BULLET_ZAP + BULLET_DECAY_CELS)) {
|
|
|
|
|
bullet->patnum = patnum;
|
|
|
|
|
#if (GAME == 5)
|
|
|
|
|
bullets_seen++;
|
|
|
|
|
#endif
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
popup_bonus += score_per_bullet;
|
|
|
|
|
score_delta += score_per_bullet;
|
|
|
|
|
pointnums_add_white(
|
|
|
|
|
bullet->pos.cur.x, bullet->pos.cur.y, score_per_bullet
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
score_per_bullet += score_step;
|
|
|
|
|
score_step += 3;
|
|
|
|
|
if(score_per_bullet > score_per_bullet_cap) {
|
|
|
|
|
score_per_bullet = score_per_bullet_cap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bullet->flag = 2;
|
|
|
|
|
#if (GAME == 5)
|
|
|
|
|
if(bullet_zap_drop_point_items && ((bullets_seen % 4) == 0)) {
|
|
|
|
|
items_add(bullet->pos.cur.x, bullet->pos.cur.y, IT_POINT);
|
|
|
|
|
}
|
|
|
|
|
bullets_seen++;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
// Note that this would show only one popup even *if* bullets could
|
|
|
|
|
// spawn during the zap frames: Popups can be changed at least every
|
|
|
|
|
// 64 frames, and BULLET_ZAP_FRAMES is smaller.
|
|
|
|
|
if(popup_bonus) {
|
|
|
|
|
popup_show(POPUP_ID_BONUS);
|
|
|
|
|
}
|
|
|
|
|
bullet_zap.frames++;
|
|
|
|
|
|
|
|
|
|
// ZUN bug: Always true in TH05, see above
|
|
|
|
|
if(patnum >= (PAT_BULLET_ZAP + BULLET_ZAP_CELS)) {
|
|
|
|
|
bullet_zap.active = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(bullet_clear_time) {
|
|
|
|
|
bullet_clear_time--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|