plrmodes: fix bomb-stacking exploits

When a bomb is used, cancel all previous bomb-related tasks just before
invoking new bomb tasks. This prevents the possibility of stacking
multiple bombs at the same time.

Also make sure to YIELD before starting the bomb logic loops, because
the bomb task will be scheduled in the same frame after the first yield,
due to the initial invocation being from an event handler.
This commit is contained in:
Andrei Alexeyev 2021-06-10 12:57:45 +03:00
parent 1daf1377d9
commit f1f5a34a51
No known key found for this signature in database
GPG key ID: 72D26128040B9690
7 changed files with 160 additions and 105 deletions

View file

@ -512,7 +512,36 @@ static void marisa_laser_masterspark_damage(MarisaAMasterSpark *ms) {
// log_debug("%i", iter);
}
TASK(marisa_laser_masterspark, { MarisaAController *ctrl; }) {
TASK(marisa_laser_bomb_background, { MarisaAController *ctrl; }) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
CoEvent *draw_event = &stage_get_draw_events()->background_drawn;
for(;;) {
WAIT_EVENT_OR_DIE(draw_event);
float t = player_get_bomb_progress(plr);
float fade = 1;
if(t < 1.0f / 6.0f) {
fade = t * 6.0f;
}
if(t > 3.0f / 4.0f) {
fade = 1.0f - t * 4.0f + 3.0f;
}
if(fade <= 0.0f) {
break;
}
r_state_push();
r_color4(0.8f * fade, 0.8f * fade, 0.8f * fade, 0.8f * fade);
fill_viewport(sinf(t * 0.3f), t * 3.0f * (1.0f + t * 3.0f), 1.0f, "marisa_bombbg");
r_state_pop();
};
}
TASK(marisa_laser_bomb_masterspark, { MarisaAController *ctrl; }) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
@ -526,6 +555,10 @@ TASK(marisa_laser_masterspark, { MarisaAController *ctrl; }) {
Sprite *star_spr = res_sprite("part/maristar_orbit");
Sprite *smoke_spr = res_sprite("part/smoke");
BoxedTask bg_task = cotask_box(INVOKE_SUBTASK(marisa_laser_bomb_background, ctrl));
YIELD;
do {
real bomb_progress = player_get_bomb_progress(plr);
ms->alpha = marisa_laser_masterspark_width(bomb_progress);
@ -583,45 +616,26 @@ skip_particles:
YIELD;
} while(player_is_bomb_active(plr));
// should have faded out by now, but just in case…
CANCEL_TASK(bg_task);
while(ms->alpha > 0) {
approach_p(&ms->alpha, 0, 0.2);
YIELD;
}
}
TASK(marisa_laser_bomb_background, { MarisaAController *ctrl; }) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
CoEvent *draw_event = &stage_get_draw_events()->background_drawn;
do {
WAIT_EVENT_OR_DIE(draw_event);
float t = player_get_bomb_progress(plr);
float fade = 1;
if(t < 1./6) {
fade = t*6;
}
if(t > 3./4) {
fade = 1-t*4 + 3;
}
r_color4(0.8 * fade, 0.8 * fade, 0.8 * fade, 0.8 * fade);
fill_viewport(sin(t * 0.3), t * 3 * (1 + t * 3), 1, "marisa_bombbg");
r_color4(1, 1, 1, 1);
} while(player_is_bomb_active(plr));
}
TASK(marisa_laser_bomb_handler, { MarisaAController *ctrl; }) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
BoxedTask bomb_task = { 0 };
for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.bomb_used);
CANCEL_TASK(bomb_task);
play_sfx("bomb_marisa_a");
INVOKE_SUBTASK(marisa_laser_bomb_background, ctrl);
INVOKE_SUBTASK(marisa_laser_masterspark, ctrl);
bomb_task = cotask_box(INVOKE_SUBTASK(marisa_laser_bomb_masterspark, ctrl));
}
}

View file

@ -405,7 +405,7 @@ TASK(marisa_star_bomb_background, { MarisaBController *ctrl; }) {
Uniform *u_plrpos = r_shader_uniform(bg_shader, "plrpos");
Texture *bg_tex = res_texture("marisa_bombbg");
do {
for(;;) {
WAIT_EVENT_OR_DIE(&stage_get_draw_events()->background_drawn);
r_state_push();
@ -415,13 +415,15 @@ TASK(marisa_star_bomb_background, { MarisaBController *ctrl; }) {
r_uniform_vec2_complex(u_plrpos, cwmul(plr->pos, CMPLX(1.0/VIEWPORT_W, 1.0/VIEWPORT_H)));
fill_viewport_p(0, 0, 1, 1, 0, bg_tex);
r_state_pop();
} while(player_is_bomb_active(plr));
}
}
TASK(marisa_star_bomb_controller, { MarisaBController *ctrl; }) {
TASK(marisa_star_bomb, { MarisaBController *ctrl; }) {
MarisaBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
INVOKE_SUBTASK(marisa_star_bomb_background, ctrl);
YIELD;
MarisaBBeams *beams = TASK_HOST_ENT(MarisaBBeams);
@ -465,11 +467,13 @@ TASK(marisa_star_bomb_handler, { MarisaBController *ctrl; }) {
MarisaBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
BoxedTask bomb_task = { 0 };
for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.bomb_used);
CANCEL_TASK(bomb_task);
play_sfx("bomb_marisa_b");
INVOKE_SUBTASK(marisa_star_bomb_background, ctrl);
INVOKE_SUBTASK(marisa_star_bomb_controller, ctrl);
bomb_task = cotask_box(INVOKE_SUBTASK(marisa_star_bomb, ctrl));
}
}

View file

@ -403,41 +403,58 @@ TASK(reimu_spirit_bomb_background, { ReimuAController *ctrl; }) {
Player *plr = ctrl->plr;
CoEvent *draw_event = &stage_get_draw_events()->background_drawn;
do {
for(;;) {
WAIT_EVENT_OR_DIE(draw_event);
float t = player_get_bomb_progress(plr);
float alpha = 0;
if(t >= 1) {
break;
}
float alpha = 0.0f;
if(t > 0) {
alpha = fminf(1, 10*t);
alpha = fminf(1.0f, 10.0f * t);
}
if(t > 0.7) {
alpha *= 1 - powf((t - 0.7) / 0.3, 4);
alpha *= 1.0f - powf((t - 0.7f) / 0.3f, 4.0f);
}
reimu_common_bomb_bg(plr, alpha);
colorfill(0, 0.05 * alpha, 0.1 * alpha, alpha * 0.5);
} while(player_is_bomb_active(plr));
colorfill(0.0f, 0.05f * alpha, 0.1f * alpha, alpha * 0.5f);
}
}
TASK(reimu_spirit_bomb, { ReimuAController *ctrl; }) {
ReimuAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
INVOKE_SUBTASK(reimu_spirit_bomb_background, ctrl);
YIELD;
const int orb_count = 6;
for(int i = 0; i < orb_count; i++) {
INVOKE_TASK_DELAYED(1, reimu_spirit_bomb_orb, ENT_BOX(plr), i, M_TAU/orb_count*i);
}
// keep bg renderer alive
WAIT(plr->bomb_endtime - global.frames);
}
TASK(reimu_spirit_bomb_handler, { ReimuAController *ctrl; }) {
ReimuAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
int orb_count = 6;
BoxedTask bomb_task = { 0 };
for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.bomb_used);
INVOKE_SUBTASK(reimu_spirit_bomb_background, ctrl);
play_sfx("bomb_reimu_a");
play_sfx("bomb_marisa_b");
for(int i = 0; i < orb_count; i++) {
INVOKE_TASK_DELAYED(1, reimu_spirit_bomb_orb, ENT_BOX(plr), i, M_TAU/orb_count*i);
}
CANCEL_TASK(bomb_task);
bomb_task = cotask_box(INVOKE_SUBTASK(reimu_spirit_bomb, ctrl));
}
}

View file

@ -146,23 +146,35 @@ TASK(reimu_dream_gap_bomb_projectile, {
}
}
TASK(reimu_dream_bomb_noise, { BoxedPlayer plr; }) {
Player *plr = TASK_BIND(ARGS.plr);
do {
TASK(reimu_dream_bomb_noise, NO_ARGS) {
for(;;WAIT(16)) {
play_sfx("boon");
WAIT(16);
} while(player_is_bomb_active(plr));
}
}
TASK(reimu_dream_bomb_barrage, { ReimuBController *ctrl; }) {
TASK(reimu_dream_bomb_background, { ReimuBController *ctrl; }) {
ReimuBController *ctrl = ARGS.ctrl;
CoEvent *draw_event = &stage_get_draw_events()->background_drawn;
for(;;) {
WAIT_EVENT_OR_DIE(draw_event);
reimu_common_bomb_bg(ctrl->plr, ctrl->bomb_alpha);
}
}
TASK(reimu_dream_bomb, { ReimuBController *ctrl; }) {
ReimuBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
BoxedTask noise_task = cotask_box(INVOKE_SUBTASK(reimu_dream_bomb_noise));
INVOKE_SUBTASK(reimu_dream_bomb_background, ctrl);
Sprite *spr_proj = res_sprite("proj/glowball");
Sprite *spr_impact = res_sprite("part/blast");
int t = 0;
YIELD;
int t = 0;
do {
Color *pcolor = HSLA(t/30.0, 0.5, 0.5, 0.5);
@ -180,28 +192,26 @@ TASK(reimu_dream_bomb_barrage, { ReimuBController *ctrl; }) {
stage_shake_view(4);
t += WAIT(BOMB_PROJECTILE_FIRE_DELAY);
} while(player_is_bomb_active(plr));
}
TASK(reimu_dream_bomb_background, { ReimuBController *ctrl; }) {
ReimuBController *ctrl = ARGS.ctrl;
CoEvent *draw_event = &stage_get_draw_events()->background_drawn;
CANCEL_TASK(noise_task);
do {
WAIT_EVENT_OR_DIE(draw_event);
reimu_common_bomb_bg(ctrl->plr, ctrl->bomb_alpha);
} while(ctrl->bomb_alpha > 0);
while(ctrl->bomb_alpha > 0) {
// keep bg renderer alive
YIELD;
}
}
TASK(reimu_dream_bomb_handler, { ReimuBController *ctrl; }) {
ReimuBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
BoxedTask bomb_task = { 0 };
for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.bomb_used);
CANCEL_TASK(bomb_task);
play_sfx("bomb_marisa_a");
INVOKE_SUBTASK(reimu_dream_bomb_noise, ENT_BOX(plr));
INVOKE_SUBTASK(reimu_dream_bomb_barrage, ctrl);
INVOKE_SUBTASK(reimu_dream_bomb_background, ctrl);
bomb_task = cotask_box(INVOKE_SUBTASK(reimu_dream_bomb, ctrl));
}
}

View file

@ -92,7 +92,7 @@ DEFINE_EXTERN_TASK(youmu_common_bomb_background) {
YoumuBombBGData *bg_data = ARGS.bg_data;
CoEvent *draw_event = &stage_get_draw_events()->background_drawn;
do {
for(;;) {
WAIT_EVENT_OR_DIE(draw_event);
float t = player_get_bomb_progress(&global.plr);
@ -119,5 +119,5 @@ DEFINE_EXTERN_TASK(youmu_common_bomb_background) {
r_state_pop();
capture_frame(bg_data->buffer, r_framebuffer_current());
} while(player_is_bomb_active(plr));
}
}

View file

@ -457,11 +457,45 @@ static void youmu_mirror_draw_speed_trail(Projectile *p, int t, ProjDrawRuleArgs
r_draw_sprite(&sp);
}
TASK(youmu_mirror_bomb_controller, { YoumuAController *ctrl; }) {
TASK(youmu_mirror_bomb_postprocess, { YoumuAMyon *myon; }) {
YoumuAMyon *myon = ARGS.myon;
CoEvent *pp_event = &stage_get_draw_events()->postprocess_before_overlay;
ShaderProgram *shader = res_shader("youmua_bomb");
Uniform *u_tbomb = r_shader_uniform(shader, "tbomb");
Uniform *u_myon = r_shader_uniform(shader, "myon");
Uniform *u_fill_overlay = r_shader_uniform(shader, "fill_overlay");
for(;;) {
WAIT_EVENT_OR_DIE(pp_event);
float t = player_get_bomb_progress(&global.plr);
float f = fmaxf(0, 1 - 10 * t);
cmplx myonpos = CMPLX(creal(myon->pos)/VIEWPORT_W, 1 - cimag(myon->pos)/VIEWPORT_H);
FBPair *fbpair = stage_get_postprocess_fbpair();
r_framebuffer(fbpair->back);
r_state_push();
r_shader_ptr(shader);
r_uniform_float(u_tbomb, t);
r_uniform_vec2_complex(u_myon, myonpos);
r_uniform_vec4(u_fill_overlay, f, f, f, f);
draw_framebuffer_tex(fbpair->front, VIEWPORT_W, VIEWPORT_H);
r_state_pop();
fbpair_swap(fbpair);
}
}
TASK(youmu_mirror_bomb, { YoumuAController *ctrl; }) {
YoumuAController *ctrl = ARGS.ctrl;
YoumuAMyon *myon = &ctrl->myon;
Player *plr = ctrl->plr;
INVOKE_SUBTASK(youmu_common_bomb_background, ENT_BOX(plr), &ctrl->bomb_bg);
INVOKE_SUBTASK(youmu_mirror_bomb_postprocess, myon);
cmplx vel = -30 * myon->dir;
cmplx pos = myon->pos;
@ -470,6 +504,8 @@ TASK(youmu_mirror_bomb_controller, { YoumuAController *ctrl; }) {
ShaderProgram *silhouette_shader = res_shader("sprite_silhouette");
YIELD;
do {
pos += vel;
@ -507,49 +543,17 @@ TASK(youmu_mirror_bomb_controller, { YoumuAController *ctrl; }) {
} while(player_is_bomb_active(plr));
}
TASK(youmu_mirror_bomb_postprocess, { YoumuAController *ctrl; }) {
YoumuAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
YoumuAMyon *myon = &ctrl->myon;
CoEvent *pp_event = &stage_get_draw_events()->postprocess_before_overlay;
ShaderProgram *shader = res_shader("youmua_bomb");
Uniform *u_tbomb = r_shader_uniform(shader, "tbomb");
Uniform *u_myon = r_shader_uniform(shader, "myon");
Uniform *u_fill_overlay = r_shader_uniform(shader, "fill_overlay");
do {
WAIT_EVENT_OR_DIE(pp_event);
float t = player_get_bomb_progress(&global.plr);
float f = fmaxf(0, 1 - 10 * t);
cmplx myonpos = CMPLX(creal(myon->pos)/VIEWPORT_W, 1 - cimag(myon->pos)/VIEWPORT_H);
FBPair *fbpair = stage_get_postprocess_fbpair();
r_framebuffer(fbpair->back);
r_state_push();
r_shader_ptr(shader);
r_uniform_float(u_tbomb, t);
r_uniform_vec2_complex(u_myon, myonpos);
r_uniform_vec4(u_fill_overlay, f, f, f, f);
draw_framebuffer_tex(fbpair->front, VIEWPORT_W, VIEWPORT_H);
r_state_pop();
fbpair_swap(fbpair);
} while(player_is_bomb_active(plr));
}
TASK(youmu_mirror_bomb_handler, { YoumuAController *ctrl; }) {
YoumuAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
BoxedTask bomb_task = { 0 };
for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.bomb_used);
CANCEL_TASK(bomb_task);
play_sfx("bomb_youmu_b");
INVOKE_SUBTASK(youmu_mirror_bomb_controller, ctrl);
INVOKE_SUBTASK(youmu_common_bomb_background, ENT_BOX(plr), &ctrl->bomb_bg);
INVOKE_SUBTASK(youmu_mirror_bomb_postprocess, ctrl);
bomb_task = cotask_box(INVOKE_SUBTASK(youmu_mirror_bomb, ctrl));
}
}

View file

@ -535,11 +535,15 @@ TASK(youmu_haunting_bomb_slice, { YoumuBController *ctrl; cmplx pos; real angle;
}
}
TASK(youmu_haunting_bomb_controller, { YoumuBController *ctrl; }) {
TASK(youmu_haunting_bomb, { YoumuBController *ctrl; }) {
YoumuBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
cmplx origin = plr->pos;
INVOKE_SUBTASK(youmu_common_bomb_background, ENT_BOX(plr), &ctrl->bomb_bg);
YIELD;
int i = 0;
do {
cmplx ofs = cdir(i) * (100 + 10 * i * i * 0.01);
@ -554,11 +558,13 @@ TASK(youmu_haunting_bomb_handler, { YoumuBController *ctrl; }) {
YoumuBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
BoxedTask bomb_task = { 0 };
for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.bomb_used);
CANCEL_TASK(bomb_task);
play_sfx("bomb_youmu_b");
INVOKE_SUBTASK(youmu_haunting_bomb_controller, ctrl);
INVOKE_SUBTASK(youmu_common_bomb_background, ENT_BOX(plr), &ctrl->bomb_bg);
bomb_task = cotask_box(INVOKE_SUBTASK(youmu_haunting_bomb, ctrl));
}
}