ReC98/th04/main/bullet/add.cpp

407 lines
8.7 KiB
C++
Raw Normal View History

void pascal near bullets_add_regular_raw(void);
void pascal near bullets_add_special_raw(void);
/// Per-spawn state
/// ---------------
/// Has no reason to be global.
extern bool group_fixedspeed;
// "(group_i * bullet_template.delta.spread_angle) is probably too expensive,
// let's rather do an addition for each additional spawned bullet :zunpet:"
extern unsigned char group_i_spread_angle;
extern unsigned char group_i_absolute_angle;
/// ---------------
#define tmpl bullet_template
void near tune_for_easy(void)
{
switch(tmpl.group) {
case BG_STACK:
case BG_STACK_AIMED:
tmpl.delta.stack_speed -= (tmpl.delta.stack_speed / 4);
if(tmpl.count >= 2) {
tmpl.count--;
}
break;
case BG_SPREAD:
case BG_SPREAD_AIMED:
if(tmpl.count >= 3) {
tmpl.count -= 2;
}
break;
case BG_RANDOM_ANGLE:
case BG_RING:
case BG_RING_AIMED:
tmpl.count /= 2;
break;
}
}
void near tune_for_hard(void)
{
switch(tmpl.group) {
case BG_SINGLE_AIMED:
tmpl.group = BG_STACK_AIMED;
tmpl.count = 2;
tmpl.delta.stack_speed = to_sp(0.375f);
break;
case BG_SINGLE:
tmpl.group = BG_STACK;
tmpl.count = 2;
tmpl.delta.stack_speed = to_sp(0.375f);
break;
case BG_STACK:
case BG_STACK_AIMED:
tmpl.delta.stack_speed += (tmpl.delta.stack_speed / 2);
break;
case BG_SPREAD:
case BG_SPREAD_AIMED:
tmpl.count += 2;
break;
case BG_RING:
case BG_RING_AIMED:
tmpl.count *= 3;
tmpl.count /= 2;
if(tmpl.count > 48) {
tmpl.count = 48;
}
break;
case BG_RANDOM_ANGLE:
case BG_RANDOM_ANGLE_AND_SPEED:
tmpl.count += (tmpl.count / 2);
break;
}
}
void near tune_for_lunatic(void)
{
switch(tmpl.group) {
case BG_SINGLE_AIMED:
tmpl.group = BG_SPREAD_AIMED;
tmpl.count = 3;
tmpl.delta.spread_angle = +0x06;
break;
case BG_SINGLE:
tmpl.group = BG_SPREAD;
tmpl.count = 3;
tmpl.delta.spread_angle = +0x06;
break;
case BG_STACK:
case BG_STACK_AIMED:
tmpl.delta.stack_speed += (tmpl.delta.stack_speed / 2);
tmpl.count++;
break;
case BG_SPREAD:
case BG_SPREAD_AIMED:
tmpl.count += 4;
break;
case BG_RING:
case BG_RING_AIMED:
tmpl.count *= 2;
if(tmpl.count > 48) {
tmpl.count = 48;
}
break;
case BG_RANDOM_ANGLE:
case BG_RANDOM_ANGLE_AND_SPEED:
tmpl.count *= 2;
break;
}
}
void near tune_for_playperf(void)
{
switch(tmpl.group) {
case BG_STACK:
case BG_STACK_AIMED:
if(playperf >= 24) {
tmpl.count++;
} else if((playperf <= 6) && (tmpl.count >= 2)) {
tmpl.count--;
}
break;
case BG_SPREAD:
case BG_SPREAD_AIMED:
if(playperf >= 24) {
tmpl.count += 2;
} else if((playperf <= 6) && (tmpl.count >= 3)) {
tmpl.count -= 2;
}
break;
case BG_RING:
case BG_RING_AIMED:
if(playperf >= 24) {
tmpl.count += 4;
} else if(playperf >= 20) {
tmpl.count += 2;
} else if(tmpl.count >= 5) {
if(playperf <= 10) {
tmpl.count -= 2;
}
if(playperf <= 4) {
tmpl.count -= 4;
}
}
break;
}
}
#undef tmpl
void pascal near bullet_template_tune_easy(void)
{
tune_for_playperf();
tune_for_easy();
}
void pascal near bullet_template_tune_normal(void)
{
tune_for_playperf();
}
void pascal near bullet_template_tune_hard(void)
{
tune_for_playperf();
tune_for_hard();
}
void pascal near bullet_template_tune_lunatic(void)
{
tune_for_playperf();
tune_for_lunatic();
}
void pascal near bullets_add_regular_easy(void)
{
unsigned char speed;
unsigned char count;
if(bullet_zap.active) {
return;
}
count = bullet_template.count;
speed = bullet_template.speed.v;
bullets_add_regular_raw();
bullet_template.count = count;
bullet_template.speed.v = speed;
}
inline void keep_speed_from_being_mutated_when_calling(nearfunc_t_near func) {
unsigned char speed = bullet_template.speed.v;
func();
bullet_template.speed.v = speed;
}
void pascal near bullets_add_regular_normal(void)
{
if(bullet_zap.active) {
return;
}
keep_speed_from_being_mutated_when_calling(bullets_add_regular_raw);
}
void pascal near bullets_add_regular_hard_lunatic(void)
{
if(bullet_zap.active) {
return;
}
keep_speed_from_being_mutated_when_calling(bullets_add_regular_raw);
}
void pascal near bullets_add_special_easy(void)
{
if(bullet_zap.active) {
return;
}
keep_speed_from_being_mutated_when_calling(bullets_add_special_raw);
}
void pascal near bullets_add_special_normal(void)
{
if(bullet_zap.active) {
return;
}
keep_speed_from_being_mutated_when_calling(bullets_add_special_raw);
}
void pascal near bullets_add_special_hard_lunatic(void)
{
if(bullet_zap.active) {
return;
}
keep_speed_from_being_mutated_when_calling(bullets_add_special_raw);
}
void near bullets_add_regular_fixedspeed(void)
{
group_fixedspeed = true;
bullets_add_regular();
group_fixedspeed = false;
}
void near bullets_add_special_fixedspeed(void)
{
group_fixedspeed = true;
bullets_add_special();
group_fixedspeed = false;
}
#define last_bullet_in_group(group_ii) \
(group_i >= (bullet_template.count - 1))
// Necessary to compile the switch statement in bullet_velocity_and_angle_set()
// to a binary search. Strangely, it's not used for the functions above?
#pragma option -G
// Sets the bullet template's velocity for bullet #[group_i] in the template's
// current group, as well as [group_i_absolute_angle]. Returns true if this
// was the last bullet for this group.
bool16 pascal near bullet_velocity_and_angle_set(int group_i)
{
int angle = 0x00;
unsigned char speed;
bool done;
// Due to this default, invalid group values lead to the spawn functions
// repeatedly calling this function, until they completely filled the
// pellet / 16×16 part of the array with identical bullets using the given
// angle and speed.
// (Not really a ZUN bug until we can discover a game state where this can
// actually happen.)
done = false;
speed = bullet_template.speed.v;
switch(bullet_template.group) {
case BG_SPREAD:
case BG_SPREAD_AIMED:
if(bullet_template.count & 1) {
// Odd-numbered spreads always contain a bullet in the center.
if(group_i == 0) {
group_i_spread_angle = 0x00;
angle = 0x00;
} else if(group_i & 1) {
// Symmetric version of even-numbered bullets
group_i_spread_angle += bullet_template.delta.spread_angle;
angle = (0x100 - group_i_spread_angle);
} else {
angle = group_i_spread_angle;
}
} else {
// Even-numbered spreads are aimed around the 0° point, and
// therefore need to be shifted by half of the angle. Yes, this
// whole separate branch, with its whole pointlessly mirrored
// logic, wouldn't have been necessary, and ZUN could have just
// added the angle offset after the fact...
if(group_i == 0) {
group_i_spread_angle = (bullet_template.delta.spread_angle / 2);
angle = group_i_spread_angle;
} else if(group_i & 1) {
// Symmetric version of even-numbered bullets
angle = (0x100 - group_i_spread_angle);
} else {
group_i_spread_angle += bullet_template.delta.spread_angle;
angle = group_i_spread_angle;
}
}
if(last_bullet_in_group(group_i)) {
done = true;
}
if(bullet_template.group == BG_SPREAD) {
goto no_aim;
}
goto aim;
case BG_RING:
angle = ((group_i * 0x100) / bullet_template.count);
if(last_bullet_in_group(group_i)) {
done = true;
}
goto no_aim;
case BG_RING_AIMED:
angle = ((group_i * 0x100) / bullet_template.count);
if(last_bullet_in_group(group_i)) {
done = true;
}
goto aim;
// All these 16-bit randring operations seem to waste 8 bits of randomness,
// but each next16 call only advances the pointer by one byte anyway.
case BG_FORCESINGLE_RANDOM_ANGLE:
angle = randring2_next16();
done = true;
goto no_aim;
case BG_FORCESINGLE:
case BG_SINGLE:
done = true;
goto no_aim;
case BG_RANDOM_ANGLE:
angle = randring2_next16();
if(last_bullet_in_group(group_i)) {
done = true;
}
goto no_aim;
case BG_RANDOM_ANGLE_AND_SPEED:
angle = randring2_next16();
speed += randring2_next16_and(to_sp(2.0f) - 1);
if(last_bullet_in_group(group_i)) {
done = true;
}
goto no_aim;
case BG_RANDOM_CONSTRAINED_ANGLE_AIMED:
angle = randring2_next16_and(0x1F);
angle -= 0x10;
if(last_bullet_in_group(group_i)) {
done = true;
}
goto aim;
case BG_FORCESINGLE_AIMED:
case BG_SINGLE_AIMED:
done = true;
goto aim;
case BG_STACK:
case BG_STACK_AIMED:
speed += (bullet_template.delta.stack_speed * group_i);
if(
last_bullet_in_group(group_i) ||
(bullet_template.speed >= to_sp8(10.0f))
) {
done = true;
}
if(bullet_template.group == BG_STACK) {
goto no_aim;
}
goto aim;
}
aim:
angle += iatan2(
(player_pos.cur.y - bullet_template.origin.y),
(player_pos.cur.x - bullet_template.origin.x)
);
no_aim:
vector2_near(
bullet_template.velocity, (angle + bullet_template.angle), speed
);
group_i_absolute_angle = (angle + bullet_template.angle);
return done;
}