Give projectiles EVENT_BIRTH and call them with t==0 more consistently
The only case of t==0 being skipped is if the projectile was somehow created after the projectile processing loop for this frame has been finished. Currently that is only possible if a particle spawns a non-particle projectile, which, ideally, should never happen. The same problem exists with other types of entities. For example, if you have a funny projectile rule that spawns an Enemy, that Enemy will have the 0th frame skipped. One way to fix this is to maintain a list of all entities in the game and process them all in a single loop, where newly spawned entities would be appended to the tail of the list. This is also probably the only good way to fix this, too. Handling of all projectile events has been made mandatory to facilitate easier debugging of subtle and/or hard to track bugs. If t<0, then the projectile rule MUST return ACTION_ACK, signifying that it acknowledged the event and handled it appropriately. Otherwise, it SHOULD return ACTION_NONE or ACTION_DESTROY. In a perfect world, those just wouldn't be conflated in the same function with update logic. I've also cleaned up the stage_logic routine a bit. Moved most of the dialog handling into dialog.c and gave higher priority to boss processing. The later is currently necessary to let boss-spawned projectiles and particles to get called with t==0.
This commit is contained in:
parent
05a41c5047
commit
eb43370c1d
15 changed files with 258 additions and 130 deletions
|
@ -185,6 +185,7 @@ static int boss_glow(Projectile *p, int t) {
|
|||
if(t == EVENT_DEATH) {
|
||||
free(p->sprite);
|
||||
}
|
||||
|
||||
return linear(p, t);
|
||||
}
|
||||
|
||||
|
@ -576,6 +577,10 @@ void boss_finish_current_attack(Boss *boss) {
|
|||
void process_boss(Boss **pboss) {
|
||||
Boss *boss = *pboss;
|
||||
|
||||
if(!boss) {
|
||||
return;
|
||||
}
|
||||
|
||||
aniplayer_update(&boss->ani);
|
||||
|
||||
if(boss->global_rule) {
|
||||
|
|
22
src/dialog.c
22
src/dialog.c
|
@ -130,6 +130,10 @@ void draw_dialog(Dialog *dialog) {
|
|||
}
|
||||
|
||||
bool page_dialog(Dialog **d) {
|
||||
if(!*d) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int to = (*d)->messages[(*d)->pos].timeout;
|
||||
|
||||
if(to && to > global.frames) {
|
||||
|
@ -146,7 +150,25 @@ bool page_dialog(Dialog **d) {
|
|||
// XXX: maybe this can be handled elsewhere?
|
||||
if(!global.boss)
|
||||
global.timer++;
|
||||
} else if((*d)->messages[(*d)->pos].side == BGM) {
|
||||
stage_start_bgm((*d)->messages[(*d)->pos].msg);
|
||||
return page_dialog(d);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void process_dialog(Dialog **d) {
|
||||
if(!*d) {
|
||||
return;
|
||||
}
|
||||
|
||||
int to = (*d)->messages[(*d)->pos].timeout;
|
||||
|
||||
if(
|
||||
(to && to >= global.frames) ||
|
||||
((global.plr.inputflags & INFLAG_SKIP) && global.frames - (*d)->page_time > 3)
|
||||
) {
|
||||
page_dialog(d);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,4 +53,5 @@ void delete_dialog(Dialog *d)
|
|||
void draw_dialog(Dialog *dialog)
|
||||
attr_nonnull(1);
|
||||
|
||||
bool page_dialog(Dialog **d);
|
||||
bool page_dialog(Dialog **d) attr_nonnull(1);
|
||||
void process_dialog(Dialog **d) attr_nonnull(1);
|
||||
|
|
11
src/enemy.c
11
src/enemy.c
|
@ -143,7 +143,7 @@ void killall(Enemy *enemies) {
|
|||
int enemy_flare(Projectile *p, int t) { // a[0] velocity, a[1] ref to enemy
|
||||
if(t == EVENT_DEATH) {
|
||||
free_ref(p->args[1]);
|
||||
return ACTION_NONE;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
Enemy *owner = REF(p->args[1]);
|
||||
|
@ -154,8 +154,11 @@ int enemy_flare(Projectile *p, int t) { // a[0] velocity, a[1] ref to enemy
|
|||
}
|
||||
*/
|
||||
|
||||
if(t < 0) {
|
||||
return ACTION_NONE;
|
||||
int result = ACTION_NONE;
|
||||
|
||||
if(t == EVENT_BIRTH) {
|
||||
t = 0;
|
||||
result = ACTION_ACK;
|
||||
}
|
||||
|
||||
if(owner != NULL) {
|
||||
|
@ -163,7 +166,7 @@ int enemy_flare(Projectile *p, int t) { // a[0] velocity, a[1] ref to enemy
|
|||
}
|
||||
|
||||
p->pos = p->pos0 + p->args[3] + p->args[0]*t;
|
||||
return ACTION_NONE;
|
||||
return result;
|
||||
}
|
||||
|
||||
void BigFairy(Enemy *e, int t, bool render) {
|
||||
|
|
|
@ -71,6 +71,7 @@ enum {
|
|||
EVENT_DEATH = -8999,
|
||||
EVENT_BIRTH,
|
||||
ACTION_DESTROY,
|
||||
ACTION_ACK,
|
||||
ACTION_NONE,
|
||||
|
||||
FPS = 60,
|
||||
|
|
|
@ -21,7 +21,7 @@ static Color myon_color(float f, float a) {
|
|||
|
||||
static int myon_particle_rule(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_NONE;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->pos += p->args[0];
|
||||
|
@ -40,7 +40,7 @@ static complex myon_tail_dir(void) {
|
|||
|
||||
static int myon_flare_particle_rule(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_NONE;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
// wiggle wiggle
|
||||
|
@ -137,7 +137,7 @@ static void myon_draw_proj_trail(Projectile *p, int t) {
|
|||
|
||||
static int myon_proj(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
linear(p, t);
|
||||
|
@ -159,7 +159,7 @@ static int myon_proj(Projectile *p, int t) {
|
|||
.angle = p->angle,
|
||||
);
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static void myon_proj_draw(Projectile *p, int t) {
|
||||
|
@ -293,7 +293,7 @@ static int youmu_mirror_myon(Enemy *e, int t) {
|
|||
|
||||
static int youmu_mirror_self_proj(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
complex v0 = p->args[0];
|
||||
|
|
|
@ -87,8 +87,8 @@ static Projectile* youmu_homing_trail(Projectile *p, complex v, int to) {
|
|||
}
|
||||
|
||||
static int youmu_homing(Projectile *p, int t) { // a[0]: velocity, a[1]: aim (r: linear, i: accelerated), a[2]: timeout, a[3]: initial target
|
||||
if(t == EVENT_DEATH) {
|
||||
return 1;
|
||||
if(t == EVENT_DEATH || t == EVENT_BIRTH) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(t > creal(p->args[2])) {
|
||||
|
@ -132,9 +132,10 @@ static int youmu_trap(Projectile *p, int t) {
|
|||
.flags = PFLAG_REQUIREDPARTICLE,
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
// FIXME: replace this with timeout?
|
||||
double expiretime = creal(p->args[1]);
|
||||
|
||||
if(t > expiretime) {
|
||||
|
@ -142,7 +143,7 @@ static int youmu_trap(Projectile *p, int t) {
|
|||
}
|
||||
|
||||
if(t < 0) {
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
float charge = youmu_trap_charge(t);
|
||||
|
@ -227,7 +228,7 @@ static void youmu_particle_slice_draw(Projectile *p, int t) {
|
|||
|
||||
static int youmu_particle_slice_logic(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_NONE;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
double lifetime = p->timeout;
|
||||
|
@ -297,7 +298,7 @@ static int youmu_slash(Enemy *e, int t) {
|
|||
|
||||
static int youmu_asymptotic(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->angle = carg(p->args[0]);
|
||||
|
|
|
@ -135,13 +135,34 @@ static double projectile_rect_area(Projectile *p) {
|
|||
|
||||
static void ent_draw_projectile(EntityInterface *ent);
|
||||
|
||||
static inline char* event_name(int ev) {
|
||||
switch(ev) {
|
||||
case EVENT_BIRTH: return "EVENT_BIRTH";
|
||||
case EVENT_DEATH: return "EVENT_DEATH";
|
||||
}
|
||||
|
||||
log_fatal("Bad event %i", ev);
|
||||
}
|
||||
|
||||
static inline int proj_call_rule(Projectile *p, int t) {
|
||||
if(p->timeout > 0 && t >= p->timeout) {
|
||||
return ACTION_DESTROY;
|
||||
}
|
||||
|
||||
if(p->rule) {
|
||||
return p->rule(p, t);
|
||||
int a = p->rule(p, t);
|
||||
|
||||
if(t < 0 && a != ACTION_ACK) {
|
||||
set_debug_info(&p->debug);
|
||||
log_fatal(
|
||||
"Projectile rule didn't acknowledge %s (returned %i, expected %i)",
|
||||
event_name(t),
|
||||
a,
|
||||
ACTION_ACK
|
||||
);
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
return ACTION_NONE;
|
||||
|
@ -239,9 +260,9 @@ static Projectile* _create_projectile(ProjArgs *args) {
|
|||
|
||||
ent_register(&p->ent, ENT_PROJECTILE);
|
||||
|
||||
// BUG: this currently breaks some projectiles
|
||||
// enable this when they're fixed
|
||||
// proj_call_rule(p, EVENT_BIRTH);
|
||||
// TODO: Maybe allow ACTION_DESTROY here?
|
||||
// But in that case, code that uses this function's return value must be careful to not dereference a NULL pointer.
|
||||
proj_call_rule(p, EVENT_BIRTH);
|
||||
|
||||
return list_append(args->dest, p);
|
||||
}
|
||||
|
@ -591,19 +612,31 @@ bool projectile_is_clearable(Projectile *p) {
|
|||
}
|
||||
|
||||
int linear(Projectile *p, int t) { // sure is physics in here; a[0]: velocity
|
||||
if(t < 0)
|
||||
return 1;
|
||||
if(t == EVENT_DEATH) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->angle = carg(p->args[0]);
|
||||
p->pos = p->pos0 + p->args[0]*t;
|
||||
|
||||
return 1;
|
||||
if(t == EVENT_BIRTH) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
int accelerated(Projectile *p, int t) {
|
||||
if(t < 0)
|
||||
return 1;
|
||||
if(t == EVENT_DEATH) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->angle = carg(p->args[0]);
|
||||
|
||||
if(t == EVENT_BIRTH) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->pos += p->args[0];
|
||||
p->args[0] += p->args[1];
|
||||
|
||||
|
@ -611,10 +644,17 @@ int accelerated(Projectile *p, int t) {
|
|||
}
|
||||
|
||||
int asymptotic(Projectile *p, int t) { // v = a[0]*(a[1] + 1); a[1] -> 0
|
||||
if(t < 0)
|
||||
return 1;
|
||||
if(t == EVENT_DEATH) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->angle = carg(p->args[0]);
|
||||
|
||||
|
||||
if(t == EVENT_BIRTH) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->args[1] *= 0.8;
|
||||
p->pos += p->args[0]*(p->args[1] + 1);
|
||||
|
||||
|
|
26
src/stage.c
26
src/stage.c
|
@ -405,45 +405,27 @@ void stage_input(void) {
|
|||
static void stage_logic(void) {
|
||||
player_logic(&global.plr);
|
||||
|
||||
process_boss(&global.boss);
|
||||
process_enemies(&global.enemies);
|
||||
process_projectiles(&global.projs, true);
|
||||
process_items();
|
||||
process_lasers();
|
||||
process_projectiles(&global.particles, false);
|
||||
process_dialog(&global.dialog);
|
||||
|
||||
update_sounds();
|
||||
|
||||
if(global.dialog) {
|
||||
int to = global.dialog->messages[global.dialog->pos].timeout;
|
||||
|
||||
if(
|
||||
(to && to >= global.frames) ||
|
||||
((global.plr.inputflags & INFLAG_SKIP) && global.frames - global.dialog->page_time > 3)
|
||||
) {
|
||||
page_dialog(&global.dialog);
|
||||
}
|
||||
}
|
||||
|
||||
if(global.boss) {
|
||||
process_boss(&global.boss);
|
||||
}
|
||||
|
||||
global.frames++;
|
||||
|
||||
if(!global.dialog && (!global.boss || boss_is_fleeing(global.boss)))
|
||||
if(!global.dialog && (!global.boss || boss_is_fleeing(global.boss))) {
|
||||
global.timer++;
|
||||
}
|
||||
|
||||
if(global.replaymode == REPLAY_PLAY &&
|
||||
global.frames == global.replay_stage->events[global.replay_stage->numevents-1].frame - FADE_TIME &&
|
||||
global.game_over != GAMEOVER_TRANSITIONING) {
|
||||
stage_finish(GAMEOVER_DEFEAT);
|
||||
}
|
||||
|
||||
// BGM handling
|
||||
if(global.dialog && global.dialog->messages[global.dialog->pos].side == BGM) {
|
||||
stage_start_bgm(global.dialog->messages[global.dialog->pos].msg);
|
||||
page_dialog(&global.dialog);
|
||||
}
|
||||
}
|
||||
|
||||
void stage_clear_hazards(ClearHazardsFlags flags) {
|
||||
|
|
|
@ -60,7 +60,7 @@ void cirno_intro(Boss *c, int time) {
|
|||
|
||||
int cirno_snowflake_proj(Projectile *p, int time) {
|
||||
if(time < 0)
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
|
||||
int split_time = 200 - 20*global.diff - creal(p->args[1]) * 3;
|
||||
|
||||
|
@ -174,7 +174,7 @@ static Projectile* spawn_stain(complex pos, float angle, int to) {
|
|||
|
||||
int cirno_pfreeze_frogs(Projectile *p, int t) {
|
||||
if(t < 0)
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
|
||||
Boss *parent = global.boss;
|
||||
|
||||
|
@ -507,12 +507,8 @@ static complex halation_calc_orb_pos(complex center, float rotation, int proj, i
|
|||
}
|
||||
|
||||
static int halation_orb(Projectile *p, int time) {
|
||||
if(time == EVENT_DEATH) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(time < 0) {
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(!(time % 4)) {
|
||||
|
@ -669,8 +665,13 @@ void cirno_snow_halation(Boss *c, int time) {
|
|||
int cirno_icicles(Projectile *p, int t) {
|
||||
int turn = 60;
|
||||
|
||||
if(t < 0)
|
||||
return 1;
|
||||
if(t < 0) {
|
||||
if(t == EVENT_BIRTH) {
|
||||
p->angle = carg(p->args[0]);
|
||||
}
|
||||
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(t < turn) {
|
||||
p->pos += p->args[0]*pow(0.9,t);
|
||||
|
@ -685,7 +686,7 @@ int cirno_icicles(Projectile *p, int t) {
|
|||
|
||||
p->angle = carg(p->args[0]);
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
void cirno_icicle_fall(Boss *c, int time) {
|
||||
|
@ -742,6 +743,10 @@ void cirno_icicle_fall(Boss *c, int time) {
|
|||
}
|
||||
|
||||
int cirno_crystal_blizzard_proj(Projectile *p, int time) {
|
||||
if(time < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(!(time % 12)) {
|
||||
PARTICLE(
|
||||
.sprite = "stain",
|
||||
|
@ -1144,6 +1149,10 @@ int stage1_tritoss(Enemy *e, int t) {
|
|||
|
||||
#ifdef BULLET_TEST
|
||||
static int proj_rotate(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->angle = global.frames / 60.0;
|
||||
// p->angle = M_PI/2;
|
||||
return ACTION_NONE;
|
||||
|
|
|
@ -253,8 +253,9 @@ static void wriggle_intro_stage2(Boss *w, int t) {
|
|||
}
|
||||
|
||||
int wriggle_bug(Projectile *p, int t) {
|
||||
if(t < 0)
|
||||
return 1;
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->pos += p->args[0];
|
||||
p->angle = carg(p->args[0]);
|
||||
|
@ -271,7 +272,7 @@ int wriggle_bug(Projectile *p, int t) {
|
|||
);
|
||||
}
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
void wriggle_small_storm(Boss *w, int time) {
|
||||
|
@ -417,8 +418,9 @@ void hina_cards2(Boss *h, int time) {
|
|||
#define SLOTS 5
|
||||
|
||||
static int bad_pick_bullet(Projectile *p, int t) {
|
||||
if(t < 0)
|
||||
return 1;
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->pos += p->args[0];
|
||||
p->args[0] += p->args[1];
|
||||
|
@ -429,7 +431,7 @@ static int bad_pick_bullet(Projectile *p, int t) {
|
|||
if(slot != targetslot)
|
||||
p->args[0] = copysign(creal(p->args[0]),targetslot-slot)+I*cimag(p->args[0]);
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
void hina_bad_pick(Boss *h, int time) {
|
||||
|
@ -533,11 +535,19 @@ void hina_wheel(Boss *h, int time) {
|
|||
}
|
||||
|
||||
static int timeout_deadproj_linear(Projectile *p, int time) {
|
||||
if(time > creal(p->args[0]))
|
||||
// TODO: maybe add a "clear on timeout" flag?
|
||||
|
||||
if(time < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(time > creal(p->args[0])) {
|
||||
p->type = DeadProj;
|
||||
}
|
||||
|
||||
p->pos += p->args[1];
|
||||
p->angle = carg(p->args[1]);
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
int hina_monty_slave(Enemy *s, int time) {
|
||||
|
|
|
@ -268,7 +268,7 @@ static int stage3_burstfairy(Enemy *e, int t) {
|
|||
|
||||
static int stage3_chargefairy_proj(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return 0;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -291,7 +291,7 @@ static int stage3_chargefairy_proj(Projectile *p, int t) {
|
|||
p->pos += p->args[0] * (p->args[1] + 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static int stage3_chargefairy(Enemy *e, int t) {
|
||||
|
@ -483,7 +483,7 @@ static int scuttle_poison(Projectile *p, int time) {
|
|||
int result = accelerated(p, time);
|
||||
|
||||
if(time < 0)
|
||||
return 1;
|
||||
return result;
|
||||
|
||||
if(!(time % (57 - global.diff * 3)) && p->type != DeadProj) {
|
||||
float a = p->args[2];
|
||||
|
@ -507,6 +507,10 @@ static int scuttle_poison(Projectile *p, int time) {
|
|||
}
|
||||
|
||||
static int scuttle_lethbite_proj(Projectile *p, int time) {
|
||||
if(time < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
#define A0_PROJ_START 120
|
||||
#define A0_PROJ_CHARGE 20
|
||||
TIMER(&time)
|
||||
|
@ -755,9 +759,9 @@ static void wriggle_slave_visual(Enemy *e, int time, bool render) {
|
|||
static int wriggle_rocket_laserbullet(Projectile *p, int time) {
|
||||
if(time == EVENT_DEATH) {
|
||||
free_ref(p->args[0]);
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
} else if(time < 0) {
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(time >= creal(p->args[1])) {
|
||||
|
@ -950,12 +954,10 @@ void wriggle_moonlight_rocket(Boss *boss, int time) {
|
|||
static int wriggle_ignite_laserbullet(Projectile *p, int time) {
|
||||
if(time == EVENT_DEATH) {
|
||||
free_ref(p->args[0]);
|
||||
return 1;
|
||||
} else if(time < 0)
|
||||
return 1;
|
||||
|
||||
if(time < 0)
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
} else if(time < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
Laser *laser = (Laser*)REF(p->args[0]);
|
||||
|
||||
|
@ -966,7 +968,7 @@ static int wriggle_ignite_laserbullet(Projectile *p, int time) {
|
|||
p->angle = carg(p->args[3]);
|
||||
p->pos = p->pos + p->args[3];
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static void wriggle_ignite_warnlaser_logic(Laser *l, int time) {
|
||||
|
@ -1180,8 +1182,9 @@ static void wriggle_fstorm_proj_draw(Projectile *p, int time) {
|
|||
}
|
||||
|
||||
static int wriggle_fstorm_proj(Projectile *p, int time) {
|
||||
if(time < 0)
|
||||
return 1;
|
||||
if(time < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(cabs(global.plr.pos-p->pos) > 100) {
|
||||
p->args[2]+=1;
|
||||
|
@ -1223,7 +1226,7 @@ static int wriggle_fstorm_proj(Projectile *p, int time) {
|
|||
}
|
||||
|
||||
p->pos += p->args[1];
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
void wriggle_firefly_storm(Boss *boss, int time) {
|
||||
|
|
|
@ -500,10 +500,15 @@ Boss* create_kurumi_mid(void) {
|
|||
}
|
||||
|
||||
int splitcard(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(t == creal(p->args[2])) {
|
||||
p->color = rgb(0, color_component(p->color,CLR_B), color_component(p->color, CLR_G));
|
||||
play_sound_ex("redirect", 10, false);
|
||||
}
|
||||
|
||||
if(t > creal(p->args[2])) {
|
||||
p->args[0] += 0.01*p->args[3];
|
||||
}
|
||||
|
@ -556,8 +561,12 @@ void kurumi_boss_intro(Boss *b, int t) {
|
|||
}
|
||||
|
||||
static int splitcard_elly(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(t == creal(p->args[2])) {
|
||||
p->args[0]+=p->args[3];
|
||||
p->args[0]+=p->args[3];
|
||||
p->color = derive_color(p->color, CLRMASK_B, rgb(0,0,-color_component(p->color,CLR_B)));
|
||||
play_sound_ex("redirect", 10, false);
|
||||
}
|
||||
|
@ -609,8 +618,9 @@ void kurumi_breaker(Boss *b, int time) {
|
|||
}
|
||||
|
||||
int aniwall_bullet(Projectile *p, int t) {
|
||||
if(t < 0)
|
||||
return 1;
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(t > creal(p->args[1])) {
|
||||
if(global.diff > D_Normal) {
|
||||
|
@ -623,7 +633,7 @@ int aniwall_bullet(Projectile *p, int t) {
|
|||
}
|
||||
|
||||
p->color = derive_color(p->color, CLRMASK_R, rgb(cimag(p->pos)/VIEWPORT_H, 0, 0));
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
int aniwall_slave(Enemy *e, int t) {
|
||||
|
@ -862,6 +872,10 @@ static Projectile* vapor_particle(complex pos, Color clr) {
|
|||
}
|
||||
|
||||
static int kdanmaku_proj(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
int time = creal(p->args[0]);
|
||||
|
||||
if(t == time) {
|
||||
|
@ -889,7 +903,8 @@ static int kdanmaku_proj(Projectile *p, int t) {
|
|||
|
||||
p->pos += p->args[1];
|
||||
p->angle = carg(p->args[1]);
|
||||
return 1;
|
||||
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
int kdanmaku_slave(Enemy *e, int t) {
|
||||
|
@ -981,7 +996,7 @@ bool kurumi_extra_shield_expire(Enemy *e, int time) {
|
|||
|
||||
int kurumi_extra_dead_shield_proj(Projectile *p, int time) {
|
||||
if(time < 0) {
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->color = mix_colors(
|
||||
|
@ -1100,11 +1115,11 @@ void kurumi_extra_drainer_draw(Projectile *p, int time) {
|
|||
int kurumi_extra_drainer(Projectile *p, int time) {
|
||||
if(time == EVENT_DEATH) {
|
||||
free_ref(p->args[0]);
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(time < 0) {
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
Enemy *e = REF(p->args[0]);
|
||||
|
@ -1128,7 +1143,7 @@ int kurumi_extra_drainer(Projectile *p, int time) {
|
|||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
void kurumi_extra_create_drainer(Enemy *e) {
|
||||
|
|
|
@ -633,6 +633,10 @@ int lightning_slave(Enemy *e, int t) {
|
|||
}
|
||||
|
||||
static int zigzag_bullet(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
int l = 50;
|
||||
p->pos = p->pos0+(abs(((2*t)%l)-l/2)*I+t)*2*p->args[0];
|
||||
|
||||
|
@ -647,7 +651,7 @@ static int zigzag_bullet(Projectile *p, int t) {
|
|||
);
|
||||
}
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
void iku_lightning(Boss *b, int time) {
|
||||
|
@ -775,8 +779,10 @@ static complex induction_bullet_traj(Projectile *p, float t) {
|
|||
}
|
||||
|
||||
int induction_bullet(Projectile *p, int time) {
|
||||
if(time < 0)
|
||||
return 1;
|
||||
if(time < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
float t = time*sqrt(global.diff);
|
||||
|
||||
if(global.diff > D_Normal && !p->args[2]) {
|
||||
|
@ -938,7 +944,11 @@ void iku_extra_slave_visual(Enemy *e, int t, bool render) {
|
|||
int iku_extra_trigger_bullet(Projectile *p, int t) {
|
||||
if(t == EVENT_DEATH) {
|
||||
free_ref(p->args[1]);
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
Enemy *target = REF(p->args[1]);
|
||||
|
@ -989,7 +999,7 @@ int iku_extra_trigger_bullet(Projectile *p, int t) {
|
|||
.blend = BLEND_ADD,
|
||||
);
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
void iku_extra_fire_trigger_bullet(void) {
|
||||
|
|
|
@ -108,6 +108,10 @@ int stage6_side(Enemy *e, int t) {
|
|||
}
|
||||
|
||||
int wait_proj(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(t > creal(p->args[1])) {
|
||||
if(t == creal(p->args[1]) + 1) {
|
||||
play_sound_ex("redirect", 4, false);
|
||||
|
@ -124,7 +128,7 @@ int wait_proj(Projectile *p, int t) {
|
|||
p->pos += p->args[0];
|
||||
}
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
int stage6_flowermine(Enemy *e, int t) {
|
||||
|
@ -522,7 +526,7 @@ int kepler_bullet(Projectile *p, int t) {
|
|||
}
|
||||
|
||||
if(t < 0) {
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
complex pos = p->pos0;
|
||||
|
@ -567,7 +571,7 @@ int kepler_bullet(Projectile *p, int t) {
|
|||
);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
@ -889,6 +893,10 @@ static void set_baryon_rule(EnemyLogicRule r) {
|
|||
}
|
||||
|
||||
int eigenstate_proj(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(t == creal(p->args[2])) {
|
||||
p->args[0] += p->args[3];
|
||||
}
|
||||
|
@ -965,7 +973,7 @@ void elly_eigenstate(Boss *b, int t) {
|
|||
int broglie_particle(Projectile *p, int t) {
|
||||
if(t == EVENT_DEATH) {
|
||||
free_ref(p->args[0]);
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -977,7 +985,7 @@ int broglie_particle(Projectile *p, int t) {
|
|||
*/
|
||||
|
||||
if(t < 0) {
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
int scattertime = creal(p->args[1]);
|
||||
|
@ -1035,12 +1043,12 @@ void broglie_laser_logic(Laser *l, int t) {
|
|||
}
|
||||
|
||||
int broglie_charge(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return 1;
|
||||
if(t == EVENT_BIRTH) {
|
||||
play_sound_ex("shot3", 10, false);
|
||||
}
|
||||
|
||||
if(t == 1) {
|
||||
play_sound_ex("shot3", 10, false);
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
int firetime = creal(p->args[1]);
|
||||
|
@ -1130,7 +1138,7 @@ int broglie_charge(Projectile *p, int t) {
|
|||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
int baryon_broglie(Enemy *e, int t) {
|
||||
|
@ -1311,7 +1319,7 @@ static void ricci_laser_logic(Laser *l, int t) {
|
|||
static int ricci_proj2(Projectile *p, int t) {
|
||||
TIMER(&t);
|
||||
|
||||
if(t == 1) {
|
||||
AT(EVENT_BIRTH) {
|
||||
play_sound("shot3");
|
||||
}
|
||||
|
||||
|
@ -1334,11 +1342,11 @@ static int ricci_proj2(Projectile *p, int t) {
|
|||
create_laser(p->pos, 1, 60, rgb(1.0, 0.0, 0), las_circle, ricci_laser_logic, -6*M_PI + 30*I, rad, add_ref(e), p->pos - e->pos)->width = 10;
|
||||
|
||||
free_ref(p->args[1]);
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(t < 0) {
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
Enemy *e = (Enemy*)REF(p->args[1]);
|
||||
|
@ -1352,7 +1360,7 @@ static int ricci_proj2(Projectile *p, int t) {
|
|||
if(t > 30)
|
||||
return ACTION_DESTROY;
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
int baryon_ricci(Enemy *e, int t) {
|
||||
|
@ -1401,8 +1409,9 @@ int baryon_ricci(Enemy *e, int t) {
|
|||
}
|
||||
|
||||
static int ricci_proj(Projectile *p, int t) {
|
||||
if(t < 0)
|
||||
return 1;
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(!global.boss)
|
||||
return ACTION_DESTROY;
|
||||
|
@ -1443,7 +1452,7 @@ static int ricci_proj(Projectile *p, int t) {
|
|||
rgba(0, 0, cabs(shift)/20.0, a)
|
||||
);
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1663,6 +1672,10 @@ int baryon_curvature(Enemy *e, int t) {
|
|||
}
|
||||
|
||||
int curvature_bullet(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(REF(p->args[1]) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -1681,10 +1694,14 @@ int curvature_bullet(Projectile *p, int t) {
|
|||
if(t == EVENT_DEATH)
|
||||
free_ref(p->args[1]);
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
int curvature_orbiter(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
const double w = 0.03;
|
||||
if(REF(p->args[1]) != 0 && p->args[3] == 0) {
|
||||
p->pos = ((Projectile *)REF(p->args[1]))->pos+p->args[0]*cexp(I*t*w);
|
||||
|
@ -1694,11 +1711,10 @@ int curvature_orbiter(Projectile *p, int t) {
|
|||
p->pos += p->args[2];
|
||||
}
|
||||
|
||||
|
||||
if(t == EVENT_DEATH)
|
||||
free_ref(p->args[1]);
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static double saw(double t) {
|
||||
|
@ -1852,6 +1868,10 @@ static complex wrap_around(complex *pos) {
|
|||
}
|
||||
|
||||
static int elly_toe_boson_effect(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->angle = creal(p->args[3]) * max(0, t) / (double)p->timeout;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
@ -1864,7 +1884,7 @@ static Color boson_color(int pos, int warps) {
|
|||
|
||||
static int elly_toe_boson(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return 1;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->angle = carg(p->args[0]);
|
||||
|
@ -1897,7 +1917,7 @@ static int elly_toe_boson(Projectile *p, int t) {
|
|||
);
|
||||
}
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
if(t == activate_time) {
|
||||
|
@ -2007,7 +2027,7 @@ static int elly_toe_boson(Projectile *p, int t) {
|
|||
);
|
||||
}
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
#define FERMIONTIME 1000
|
||||
|
@ -2034,7 +2054,10 @@ static bool elly_toe_its_yukawatime(complex pos) {
|
|||
static int elly_toe_fermion_yukawa_effect(Projectile *p, int t) {
|
||||
if(t == EVENT_DEATH) {
|
||||
free_ref(p->args[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
Projectile *master = REF(p->args[0]);
|
||||
|
@ -2047,10 +2070,13 @@ static int elly_toe_fermion_yukawa_effect(Projectile *p, int t) {
|
|||
}
|
||||
|
||||
static int elly_toe_fermion(Projectile *p, int t) {
|
||||
if(t < 0)
|
||||
return 1;
|
||||
if(t == 1)
|
||||
if(t == EVENT_BIRTH) {
|
||||
p->pos0 = 0;
|
||||
}
|
||||
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
int speeduptime = creal(p->args[2]);
|
||||
if(t > speeduptime)
|
||||
|
@ -2075,7 +2101,7 @@ static int elly_toe_fermion(Projectile *p, int t) {
|
|||
}
|
||||
|
||||
if(creal(p->args[3]) < 0) // add a way to disable the yukawa phase
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
|
||||
if(elly_toe_its_yukawatime(p->pos)) {
|
||||
if(!p->args[3]) {
|
||||
|
@ -2102,12 +2128,13 @@ static int elly_toe_fermion(Projectile *p, int t) {
|
|||
p->args[3]=0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static int elly_toe_higgs(Projectile *p, int t) {
|
||||
if(t < 0)
|
||||
return 1;
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
if(elly_toe_its_yukawatime(p->pos)) {
|
||||
if(!p->args[3]) {
|
||||
|
@ -2132,7 +2159,7 @@ static int elly_toe_higgs(Projectile *p, int t) {
|
|||
p->pos = p->pos0 + t * vel;
|
||||
p->angle = carg(vel);
|
||||
|
||||
return 1;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static complex elly_toe_laser_pos(Laser *l, float t) { // a[0]: direction, a[1]: type, a[2]: width
|
||||
|
@ -2183,11 +2210,10 @@ static complex elly_toe_laser_pos(Laser *l, float t) { // a[0]: direction, a[1]:
|
|||
static int elly_toe_laser_particle_rule(Projectile *p, int t) {
|
||||
if(t == EVENT_DEATH) {
|
||||
free_ref(p->args[3]);
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
if(t < 0) {
|
||||
return ACTION_NONE;
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
Laser *l = REF(p->args[3]);
|
||||
|
|
Loading…
Reference in a new issue