stage6: port to coroutine system (WIP)

Closes #323

Mostly faithful to the original with a few changes. Needs a design
iteration.

Squashed commit of the following:

commit 17232ad797
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Mon Feb 6 07:14:12 2023 +0100

    stage6: fix uninitialized variable

commit 46f7345d00
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Mon Feb 6 06:57:10 2023 +0100

    stage6: fix warnings

commit 2c5ebc1f6c
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Mon Feb 6 06:49:07 2023 +0100

    stage6: fix ricci grace period

commit f43e1069d5
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Mon Feb 6 06:48:56 2023 +0100

    stage6: formatting (ricci)

commit d6c21a7e45
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Mon Feb 6 06:38:41 2023 +0100

    stage6: remove dead code

commit 8cc8bc3358
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Mon Feb 6 06:38:27 2023 +0100

    stage6: fix spell practice background

commit 555feafccf
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Mon Feb 6 01:59:43 2023 +0100

    stage6: adjust elly's pre-ToE movement

commit c0e71ccf92
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Mon Feb 6 01:55:58 2023 +0100

    stage6: enable pre-ToE dialogue

commit 0902897b5a
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Mon Feb 6 01:24:49 2023 +0100

    stage6: fix spell practice

commit b4bd0c5902
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Mon Feb 6 01:24:33 2023 +0100

    stage6: fix broglie issues

commit 115c90273f
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Sun Feb 5 03:49:00 2023 +0100

    stage6: formatting

commit f0b564a290
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Sun Feb 5 03:47:16 2023 +0100

    stage6: fix a bunch of scythe-related issues

commit 8fcfb57080
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Sun Feb 5 03:47:46 2023 +0100

    stage6: define MAX_BARYON_PARTICLES

commit ff68532f9f
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Sat Feb 4 07:00:50 2023 +0100

    stage6: reset baryon target pos after attack

commit bb49d60a79
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Sat Feb 4 06:32:55 2023 +0100

    stage6: more optimal draw order for baryon effect

commit 3230d378c1
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Sat Feb 4 06:19:00 2023 +0100

    stage6: port baryon background effect

commit bf3df74eda
Author: Andrei Alexeyev <akari@taisei-project.org>
Date:   Sat Feb 4 04:35:49 2023 +0100

    stage6: fix scythe and baryon visuals not spinning

commit 2492711a00
Author: Alice D <alice@starwitch.productions>
Date:   Sat Feb 12 18:38:55 2022 -0500

    fix merge and compile issues, make stage end correctly

commit 4b04855932
Author: laochailan <laochailan@web.de>
Date:   Sat Sep 25 08:40:47 2021 +0200

    kepler: remove shadowing

commit 657345cce6
Author: laochailan <laochailan@web.de>
Date:   Sat Sep 25 08:39:24 2021 +0200

    move and rename scythe_nonspell_common task

commit 6a58fea718
Author: laochailan <laochailan@web.de>
Date:   Sat Sep 25 08:34:33 2021 +0200

    solve strange bug

commit 54df567170
Author: laochailan <laochailan@web.de>
Date:   Sat Sep 25 08:21:36 2021 +0200

    trigger weird bug in collision code by accident

commit 0bd3ceb9ac
Author: laochailan <laochailan@web.de>
Date:   Sat Sep 25 08:06:08 2021 +0200

    scythe and baryon logic loose ends

commit b4596a773e
Author: laochailan <laochailan@web.de>
Date:   Fri Sep 24 18:01:45 2021 +0200

    toe port

commit bc9137393c
Author: laochailan <laochailan@web.de>
Date:   Fri Sep 24 16:23:29 2021 +0200

    wip toe port: pretty much all but the lasers

commit 60fd678d63
Author: laochailan <laochailan@web.de>
Date:   Thu Sep 23 20:37:32 2021 +0200

    wip toe port (only top level)

commit e14a35ed05
Author: laochailan <laochailan@web.de>
Date:   Thu Sep 23 17:04:18 2021 +0200

    forgotten port

commit 0cb56aee4c
Author: laochailan <laochailan@web.de>
Date:   Thu Sep 23 14:34:51 2021 +0200

    baryons_explode port

commit c589a79bbf
Author: laochailan <laochailan@web.de>
Date:   Thu Sep 23 09:04:01 2021 +0200

    nonspell 5 port

commit aac688112f
Author: laochailan <laochailan@web.de>
Date:   Thu Sep 23 08:46:27 2021 +0200

    ricci: remove obsolete commandments

commit 146d591b4c
Author: laochailan <laochailan@web.de>
Date:   Thu Sep 23 08:43:29 2021 +0200

    lhc port

commit 2146ca342d
Author: laochailan <laochailan@web.de>
Date:   Wed Sep 22 22:00:39 2021 +0200

    broglie: implement Akari comments

commit 0222c67f60
Author: laochailan <laochailan@web.de>
Date:   Wed Sep 22 20:24:58 2021 +0200

    make ricci thicc

commit a04f122e99
Author: laochailan <laochailan@web.de>
Date:   Wed Sep 22 20:12:13 2021 +0200

    fix ricci timing and improve laser looks

commit d3330ecd21
Author: laochailan <laochailan@web.de>
Date:   Wed Sep 22 18:19:29 2021 +0200

    wip: ricci but timing still wrong

commit 6c2129607a
Author: laochailan <laochailan@web.de>
Date:   Wed Sep 22 16:53:42 2021 +0200

    wip: ricci

commit 96d6e4499c
Author: laochailan <laochailan@web.de>
Date:   Wed Sep 22 16:53:09 2021 +0200

    make circle laser turn speed rad/frame

commit d943e7ab88
Author: laochailan <laochailan@web.de>
Date:   Wed Sep 22 16:52:27 2021 +0200

    remove smoothreclamp

commit 7fbcd8fb43
Author: laochailan <laochailan@web.de>
Date:   Wed Sep 22 12:17:36 2021 +0200

    remove unused code

commit a6c8979107
Author: laochailan <laochailan@web.de>
Date:   Wed Sep 22 12:16:06 2021 +0200

    modernize broglie sign

commit 278bd104d3
Author: laochailan <laochailan@web.de>
Date:   Wed Sep 1 19:54:00 2021 +0200

    first baryon non

commit 20a7193de3
Author: laochailan <laochailan@web.de>
Date:   Tue Aug 31 22:03:54 2021 +0200

    elly eigenstate

commit c64ae7c792
Author: laochailan <laochailan@web.de>
Date:   Sat Aug 28 21:52:09 2021 +0200

    elly until baryons spawn

commit 3d1a1eae9c
Author: laochailan <laochailan@web.de>
Date:   Sun Aug 22 14:54:02 2021 +0200

    add double version of common_easing_animate

commit 16e6d51851
Author: laochailan <laochailan@web.de>
Date:   Sat Aug 7 21:52:58 2021 +0200

    kepler sign + tweaks

commit c460627e41
Author: laochailan <laochailan@web.de>
Date:   Thu Aug 5 20:31:15 2021 +0200

    newton difficulty scaling

commit be97924a53
Author: laochailan <laochailan@web.de>
Date:   Wed Aug 4 22:06:30 2021 +0200

    newton sign redesign

commit 20a4f3b4c7
Author: laochailan <laochailan@web.de>
Date:   Mon Aug 2 08:51:36 2021 +0200

    first elly non wip

commit a2a5fb6ada
Author: laochailan <laochailan@web.de>
Date:   Mon Jul 26 20:23:09 2021 +0200

    scythe wip

commit ae0eeec1ad
Author: laochailan <laochailan@web.de>
Date:   Mon Jul 26 18:16:59 2021 +0200

    scythe wip

commit f215d330d9
Author: laochailan <laochailan@web.de>
Date:   Fri Jul 23 22:18:51 2021 +0200

    wip entity scythe

commit 66ccf18c63
Author: laochailan <laochailan@web.de>
Date:   Sun May 9 14:33:02 2021 +0200

    wip spawn elly

commit c947f0c3f9
Author: laochailan <laochailan@web.de>
Date:   Thu May 6 07:30:53 2021 +0200

    modernize scythe mid

commit bfae46ed24
Author: laochailan <laochailan@web.de>
Date:   Sun May 2 18:15:45 2021 +0200

    more or less port flowermine fairy

commit 98370ed44b
Author: laochailan <laochailan@web.de>
Date:   Sun May 2 15:42:49 2021 +0200

    modernized side fairy

commit ece28c70f0
Author: laochailan <laochailan@web.de>
Date:   Sun May 2 15:14:08 2021 +0200

    modernize hacker fairy

Co-authored-by: Andrei Alexeyev <akari@taisei-project.org>
Co-authored-by: Alice D <alice@starwitch.productions>
This commit is contained in:
Lukas Weber 2023-02-06 07:40:24 +01:00 committed by Andrei Alexeyev
parent b1e19f7069
commit efa8e0b126
No known key found for this signature in database
GPG key ID: 72D26128040B9690
32 changed files with 2458 additions and 2319 deletions

View file

@ -381,3 +381,14 @@ void common_rotate_velocity(MoveParams *move, real angle, int duration) {
DEFINE_EXTERN_TASK(common_rotate_velocity) {
common_rotate_velocity(ARGS.move, ARGS.angle, ARGS.duration);
}
DEFINE_EXTERN_TASK(common_easing_animated) {
double from = *ARGS.value;
double scale = ARGS.to - from;
double ftime = ARGS.duration;
for(int t = 1; t <= ARGS.duration; t++) {
YIELD;
*ARGS.value = from + scale * ARGS.ease(t / ftime);
}
}

View file

@ -153,3 +153,13 @@ DECLARE_EXTERN_TASK(
int duration;
}
);
DECLARE_EXTERN_TASK(
common_easing_animated,
{
double *value;
double to;
int duration;
glm_ease_t ease;
}
);

View file

@ -595,8 +595,7 @@ cmplx las_circle(Laser *l, float t) {
return 0;
}
// XXX: should turn speed be in rad/sec or rad/frame? currently rad/sec.
double turn_speed = creal(l->args[0]) / 60;
double turn_speed = creal(l->args[0]);
double time_ofs = cimag(l->args[0]);
double radius = creal(l->args[1]);

View file

@ -79,9 +79,6 @@ TASK(stage6_bg_update) {
cam->rot.v[2] = phi + cam_rot_offset;
cam->pos[0] = r*cos(phi*M_TAU/360);
cam->pos[1] = r*sin(phi*M_TAU/360);
YIELD;
}
}
@ -95,5 +92,12 @@ void stage6_bg_init_fullstage(void) {
}
void stage6_bg_init_spellpractice(void) {
stage6_bg_init_fullstage();
Camera3D *cam = &stage_3d_context.cam;
cam->pos[0] = -6;
cam->pos[2] = 8;
cam->rot.v[0] = 90;
cam->rot.v[2] = 270;
cam->fovy = STAGE3D_DEFAULT_FOVY*1.5;
cam->far = 100;
INVOKE_TASK(stage6_bg_3d_update);
}

View file

@ -14,8 +14,6 @@
#include "util/glm.h"
#include "resource/model.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
static Stage6DrawData *stage6_draw_data;
Stage6DrawData *stage6_get_draw_data(void) {
@ -23,11 +21,10 @@ Stage6DrawData *stage6_get_draw_data(void) {
}
void stage6_drawsys_init(void) {
stage6_draw_data = calloc(1, sizeof(*stage6_draw_data));
stage6_draw_data = ALLOC(typeof(*stage6_draw_data));
// TODO: make this background slightly less horribly inefficient
stage3d_init(&stage_3d_context, 128);
for(int i = 0; i < NUM_STARS; i++) {
vec3 p = { 0 };
@ -67,7 +64,7 @@ void stage6_drawsys_init(void) {
void stage6_drawsys_shutdown(void) {
stage3d_shutdown(&stage_3d_context);
free(stage6_draw_data);
mem_free(stage6_draw_data);
stage6_draw_data = NULL;
}
@ -183,136 +180,6 @@ void stage6_draw(void) {
stage3d_draw(&stage_3d_context, 100, ARRAY_SIZE(segs), segs);
}
static void draw_baryon_connector(cmplx a, cmplx b) {
Sprite *spr = res_sprite("stage6/baryon_connector");
r_draw_sprite(&(SpriteParams) {
.sprite_ptr = spr,
.pos = { creal(a + b) * 0.5, cimag(a + b) * 0.5 },
.rotation.vector = { 0, 0, 1 },
.rotation.angle = carg(a - b),
.scale = { (cabs(a - b) - 70) / spr->w, 20 / spr->h },
});
}
void baryon(Enemy *e, int t, bool render) {
if(render) {
// the center piece draws the nodes; applying the postprocessing effect is easier this way.
return;
}
}
static void draw_baryons(Enemy *bcenter, int t) {
// r_color4(1, 1, 1, 0.8);
r_mat_mv_push();
r_mat_mv_translate(creal(bcenter->pos), cimag(bcenter->pos), 0);
r_draw_sprite(&(SpriteParams) {
.sprite = "stage6/baryon_center",
.rotation.angle = DEG2RAD * 2 * t,
.rotation.vector = { 0, 0, 1 },
});
r_draw_sprite(&(SpriteParams) {
.sprite = "stage6/baryon",
});
r_mat_mv_pop();
r_color4(1, 1, 1, 1);
Enemy *link0 = REF(creal(bcenter->args[1]));
Enemy *link1 = REF(cimag(bcenter->args[1]));
if(link0 && link1) {
draw_baryon_connector(bcenter->pos, link0->pos);
draw_baryon_connector(bcenter->pos, link1->pos);
}
for(Enemy *e = global.enemies.first; e; e = e->next) {
if(e->visual_rule == baryon) {
r_draw_sprite(&(SpriteParams) {
.pos = { creal(e->pos), cimag(e->pos) },
.sprite = "stage6/baryon",
});
Enemy *n = REF(e->args[1]);
if(n != NULL) {
draw_baryon_connector(e->pos, n->pos);
}
}
}
}
void baryon_center_draw(Enemy *bcenter, int t, bool render) {
if(!render) {
return;
}
if(config_get_int(CONFIG_POSTPROCESS) < 1) {
draw_baryons(bcenter, t);
return;
}
stage_draw_begin_noshake();
r_state_push();
r_shader("baryon_feedback");
r_uniform_vec2("blur_resolution", 0.5*VIEWPORT_W, 0.5*VIEWPORT_H);
r_uniform_float("hue_shift", 0);
r_uniform_float("time", t/60.0);
r_framebuffer(stage6_draw_data->baryon.aux_fb);
r_blend(BLEND_NONE);
r_uniform_vec2("blur_direction", 1, 0);
r_uniform_float("hue_shift", 0.01);
r_color4(0.95, 0.88, 0.9, 0.5);
draw_framebuffer_tex(stage6_draw_data->baryon.fbpair.front, VIEWPORT_W, VIEWPORT_H);
fbpair_swap(&stage6_draw_data->baryon.fbpair);
r_framebuffer(stage6_draw_data->baryon.fbpair.back);
r_uniform_vec2("blur_direction", 0, 1);
r_color4(1, 1, 1, 1);
draw_framebuffer_tex(stage6_draw_data->baryon.aux_fb, VIEWPORT_W, VIEWPORT_H);
r_state_pop();
stage_draw_end_noshake();
r_shader("sprite_default");
draw_baryons(bcenter, t);
stage_draw_begin_noshake();
r_shader_standard();
fbpair_swap(&stage6_draw_data->baryon.fbpair);
r_color4(0.7, 0.7, 0.7, 0.7);
draw_framebuffer_tex(stage6_draw_data->baryon.fbpair.front, VIEWPORT_W, VIEWPORT_H);
stage_draw_end_noshake();
r_color4(1, 1, 1, 1);
r_framebuffer(stage6_draw_data->baryon.fbpair.front);
r_shader("sprite_default");
draw_baryons(bcenter, t);
for(Enemy *e = global.enemies.first; e; e = e->next) {
if(e->visual_rule == baryon) {
cmplx p = e->pos; //+10*frand()*cexp(2.0*I*M_PI*frand());
r_draw_sprite(&(SpriteParams) {
.sprite = "part/myon",
.color = RGBA(1, 0.2, 1.0, 0.7),
.pos = { creal(p), cimag(p) },
.rotation.angle = (creal(e->args[0]) - t) / 16.0, // frand()*M_PI*2,
.scale.both = 2,
});
}
}
}
void elly_spellbg_classic(Boss *b, int t) {
fill_viewport(0,0,0.7,"stage6/spellbg_classic");
r_blend(BLEND_MOD);

View file

@ -55,9 +55,6 @@ void stage6_drawsys_init(void);
void stage6_drawsys_shutdown(void);
void stage6_draw(void);
void baryon_center_draw(Enemy*, int, bool);
void baryon(Enemy*, int, bool);
extern ShaderRule stage6_bg_effects[];
extern ShaderRule stage6_postprocess[];

View file

@ -11,178 +11,413 @@
#include "global.h"
#include "elly.h"
#include "draw.h"
#include "util/glm.h"
#include "spells/spells.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
void scythe_common(Enemy *e, int t) {
e->args[1] += cimag(e->args[1]);
Boss* stage6_spawn_elly(cmplx pos) {
Boss *b = create_boss("Elly", "elly", pos);
boss_set_portrait(b, "elly", NULL, "normal");
b->global_rule = elly_global_rule;
return b;
}
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_sfx_ex("redirect", 4, false);
spawn_projectile_highlight_effect(p);
}
p->angle = carg(p->args[0]);
p->pos += p->args[0];
}
return ACTION_NONE;
}
int scythe_mid(Enemy *e, int t) {
TIMER(&t);
cmplx n;
if(t < 0) {
scythe_common(e, t);
return 1;
}
if(creal(e->pos) > VIEWPORT_W + 100) {
return ACTION_DESTROY;
}
e->pos += (6-global.diff-0.005*I*t)*e->args[0];
n = cexp(cimag(e->args[1])*I*t);
FROM_TO_SND("shot1_loop",0,300,1) {}
PROJECTILE(
.proto = pp_bigball,
.pos = e->pos + 80*n,
.color = RGBA(0.2, 0.5-0.5*cimag(n), 0.5+0.5*creal(n), 0.0),
.rule = wait_proj,
.args = {
global.diff*cexp(0.6*I)*n,
100
},
);
if(global.diff > D_Normal && t&1) {
Projectile *p = PROJECTILE(
.proto = pp_ball,
.pos = e->pos + 80*n,
.color = RGBA(0, 0.2, 0.5, 0.0),
.rule = accelerated,
.args = {
n,
0.01*global.diff*cexp(I*carg(global.plr.pos - e->pos - 80*n))
},
);
if(projectile_in_viewport(p)) {
play_sfx("shot1");
}
}
scythe_common(e, t);
return 1;
}
DEPRECATED_DRAW_RULE
static void scythe_draw_trail(Projectile *p, int t, ProjDrawRuleArgs args) {
r_mat_mv_push();
r_mat_mv_translate(creal(p->pos), cimag(p->pos), 0);
r_mat_mv_rotate(p->angle + (M_PI * 0.5), 0, 0, 1);
r_mat_mv_scale(creal(p->args[1]), creal(p->args[1]), 1);
float a = (1.0 - t/p->timeout) * (1.0 - cimag(p->args[1]));
ProjDrawCore(p, RGBA(a, a, a, a));
r_mat_mv_pop();
}
void scythe_draw(Enemy *e, int t, bool render) {
if(render) {
static void scythe_particles(EllyScythe *s, real oldangle) {
if(s->scale == 0) {
return;
}
PARTICLE(
.sprite_ptr = get_sprite("stage6/scythe"),
.pos = e->pos+I*6*sin(global.frames/25.0),
.draw_rule = scythe_draw_trail,
.timeout = 8,
.args = { 0, e->args[2] },
.angle = creal(e->args[1]) - M_PI/2,
.flags = PFLAG_REQUIREDPARTICLE,
.sprite_ptr = res_sprite("stage6/scythe"),
.pos = s->pos+I*6*sin((global.frames - 0.5)/25.0),
.draw_rule = pdraw_timeout_fade(0.75, 0),
.timeout = 4,
.angle = (s->angle + oldangle) * 0.5,
.scale = s->scale,
.flags = PFLAG_REQUIREDPARTICLE | PFLAG_MANUALANGLE,
.layer = LAYER_PARTICLE_MID,
);
PARTICLE(
.sprite_ptr = res_sprite("stage6/scythe"),
.pos = s->pos+I*6*sin(global.frames/25.0),
.draw_rule = pdraw_timeout_fade(0.75, 0),
.timeout = 4,
.angle = s->angle,
.scale = s->scale,
.flags = PFLAG_REQUIREDPARTICLE | PFLAG_MANUALANGLE,
.layer = LAYER_PARTICLE_MID,
);
RNG_ARRAY(rand, 2);
PARTICLE(
.sprite = "smoothdot",
.pos = e->pos+100*creal(e->args[2])*frand()*cexp(2.0*I*M_PI*frand()),
.pos = s->pos + 100 * s->scale * vrng_real(rand[0]) * vrng_dir(rand[1]),
.color = RGBA(1.0, 0.1, 1.0, 0.0),
.draw_rule = GrowFade,
.rule = linear,
.draw_rule = pdraw_timeout_scalefade(0.4, 2, 1, 0),
.timeout = 60,
.args = { -I+1, -I+1 }, // XXX: what the fuck?
.flags = PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_LOW,
);
}
int scythe_infinity(Enemy *e, int t) {
if(t < 0) {
scythe_common(e, t);
return 1;
}
TIMER(&t);
FROM_TO(0, 40, 1) {
GO_TO(e, VIEWPORT_W/2+200.0*I, 0.01);
e->args[2] = fmin(0.8, creal(e->args[2])+0.0003*t*t);
e->args[1] = creal(e->args[1]) + I*fmin(0.2, cimag(e->args[1])+0.0001*t*t);
}
FROM_TO_SND("shot1_loop",40, 3000, 1) {
float w = fmin(0.15, 0.0001*(t-40));
e->pos = VIEWPORT_W/2 + 200.0*I + 200*cos(w*(t-40)+M_PI/2.0) + I*80*sin(creal(e->args[0])*w*(t-40));
PROJECTILE(
.proto = pp_ball,
.pos = e->pos+80*cexp(I*creal(e->args[1])),
.color = RGB(cos(creal(e->args[1])), sin(creal(e->args[1])), cos(creal(e->args[1])+2.1)),
.rule = asymptotic,
.args = {
(1+0.4*global.diff)*cexp(I*creal(e->args[1])),
3 + 0.2 * global.diff
}
);
}
scythe_common(e, t);
return 1;
INLINE real normalize_angle(real a) {
// [0 .. tau)
return a - M_TAU * floorf(a / M_TAU);
}
int scythe_reset(Enemy *e, int t) {
if(t < 0) {
scythe_common(e, t);
return 1;
}
TASK(scythe_update, { BoxedEllyScythe scythe; }) {
EllyScythe *scythe = TASK_BIND(ARGS.scythe);
if(t == 1)
e->args[1] = fmod(creal(e->args[1]), 2*M_PI) + I*cimag(e->args[1]);
real spin = 0;
real oldangle;
real return_speed_cap = 0;
bool was_return_mode = false;
GO_TO(e, BOSS_DEFAULT_GO_POS, 0.05);
e->args[2] = fmax(0.6, creal(e->args[2])-0.01*t);
e->args[1] += (0.19-creal(e->args[1]))*0.05;
e->args[1] = creal(e->args[1]) + I*0.9*cimag(e->args[1]);
for(;;) {
oldangle = scythe->angle;
YIELD;
scythe_common(e, t);
return 1;
}
approach_asymptotic_p(&spin, scythe->spin, 0.1, 1e-4);
real spin_sign = sign(spin);
real spin_abs = fabs(spin);
attr_returns_nonnull
Enemy* find_scythe(void) {
for(Enemy *e = global.enemies.first; e; e = e->next) {
if(e->visual_rule == scythe_draw) {
return e;
bool return_mode = scythe->spin == 0;
if(was_return_mode && !return_mode) {
return_speed_cap = 0;
}
if(spin_abs > return_speed_cap) {
return_speed_cap = spin_abs;
}
if(return_mode) {
real return_target = normalize_angle(scythe->resting_angle);
real scythe_angle = normalize_angle(scythe->angle);
real delta;
if(spin_sign > 0) {
if(return_target < scythe_angle) {
return_target += M_TAU;
}
delta = return_target - scythe_angle;
} else {
if(scythe_angle < return_target) {
return_target -= M_TAU;
}
delta = scythe_angle - return_target;
}
spin_abs = fmin(return_speed_cap, delta * 0.1);
}
was_return_mode = return_mode;
scythe->angle += spin_sign * spin_abs;
move_update(&scythe->pos, &scythe->move);
scythe_particles(scythe, oldangle);
}
}
static void stage6_init_elly_scythe(EllyScythe *scythe, cmplx pos) {
scythe->pos = pos;
scythe->scale = 0.7;
scythe->resting_angle = -M_PI/2;
scythe->angle = scythe->resting_angle;
INVOKE_TASK(scythe_update, ENT_BOX(scythe));
}
void stage6_despawn_elly_scythe(EllyScythe *scythe) {
coevent_signal_once(&scythe->events.despawned);
}
TASK(reset_scythe, { BoxedEllyScythe scythe; }) {
EllyScythe *s = TASK_BIND(ARGS.scythe);
s->resting_angle = -M_PI/2;
s->move = move_towards(ELLY_DEFAULT_POS + ELLY_SCYTHE_RESTING_OFS, 0.1);
s->spin = 0;
}
Boss *stage6_elly_init_scythe_attack(ScytheAttackTaskArgs *args) {
Boss *b = INIT_BOSS_ATTACK(&args->base);
if(ENT_UNBOX(args->scythe) == NULL) {
args->scythe = ENT_BOX(stage6_host_elly_scythe(b->pos));
}
UNREACHABLE;
INVOKE_TASK_AFTER(
&args->base.attack->events.finished,
reset_scythe,
args->scythe
);
return b;
}
EllyScythe *stage6_host_elly_scythe(cmplx pos) {
EllyScythe *scythe = TASK_HOST_ENT(EllyScythe);
TASK_HOST_EVENTS(scythe->events);
stage6_init_elly_scythe(scythe, pos);
return scythe;
}
static void baryons_connector_particles(cmplx a, cmplx b, BoxedProjectileArray *particles) {
Sprite *spr = res_sprite("stage6/baryon_connector");
cmplx connector_pos = (a + b)/2;
real connector_angle = carg(a - b) + M_PI/2;
cmplx connector_scale = (cabs(a - b) - 70) / spr->w + I * 20 / spr->h;
ENT_ARRAY_ADD(particles, PARTICLE(
.sprite_ptr = spr,
.pos = connector_pos,
.draw_rule = pdraw_timeout_fade(0.5, 0),
.timeout = 2,
.angle = connector_angle,
.flags = PFLAG_REQUIREDPARTICLE | PFLAG_MANUALANGLE,
.scale = connector_scale
));
}
static void baryons_particles(EllyBaryons *baryons, BoxedProjectileArray *particles) {
if(baryons->scale == 0) {
return;
}
ENT_ARRAY_ADD(particles, PARTICLE(
.sprite_ptr = res_sprite("stage6/baryon_center"),
.pos = baryons->center_pos,
.draw_rule = pdraw_timeout_fade(0.75, 0),
.timeout = 4,
.angle = baryons->center_angle,
.scale = baryons->scale,
.flags = PFLAG_REQUIREDPARTICLE | PFLAG_MANUALANGLE,
));
ENT_ARRAY_ADD(particles, PARTICLE(
.sprite_ptr = res_sprite("stage6/baryon"),
.pos = baryons->center_pos,
.angle = M_TAU/12,
.draw_rule = pdraw_timeout_fade(0.75, 0),
.timeout = 4,
.flags = PFLAG_REQUIREDPARTICLE | PFLAG_MANUALANGLE,
));
for(int i = 0; i < NUM_BARYONS; i++) {
cmplx pos = baryons->poss[i];
cmplx pos_next = baryons->poss[(i+1) % NUM_BARYONS];
ENT_ARRAY_ADD(particles, PARTICLE(
.sprite_ptr = res_sprite("stage6/baryon"),
.pos = pos,
.draw_rule = pdraw_timeout_fade(0.75, 0),
.timeout = 4,
.scale = baryons->scale,
.angle = baryons->angles[i]+M_TAU/12,
.flags = PFLAG_REQUIREDPARTICLE | PFLAG_MANUALANGLE,
));
baryons_connector_particles(pos, pos_next, particles);
}
baryons_connector_particles(baryons->center_pos, baryons->poss[0], particles);
baryons_connector_particles(baryons->center_pos, baryons->poss[3], particles);
}
static void baryons_bg_feedback(Stage6DrawData *draw_data, int t) {
stage_draw_begin_noshake();
r_state_push();
r_shader("baryon_feedback");
r_uniform_vec2("blur_resolution", 0.5*VIEWPORT_W, 0.5*VIEWPORT_H);
r_uniform_float("hue_shift", 0);
r_uniform_float("time", t/60.0);
r_framebuffer(draw_data->baryon.aux_fb);
r_blend(BLEND_NONE);
r_uniform_vec2("blur_direction", 1, 0);
r_uniform_float("hue_shift", 0.01);
r_color4(0.95, 0.88, 0.9, 0.5);
draw_framebuffer_tex(draw_data->baryon.fbpair.front, VIEWPORT_W, VIEWPORT_H);
fbpair_swap(&draw_data->baryon.fbpair);
r_framebuffer(draw_data->baryon.fbpair.back);
r_uniform_vec2("blur_direction", 0, 1);
r_color4(1, 1, 1, 1);
draw_framebuffer_tex(draw_data->baryon.aux_fb, VIEWPORT_W, VIEWPORT_H);
r_state_pop();
stage_draw_end_noshake();
}
static void baryons_bg_blend(Stage6DrawData *draw_data) {
stage_draw_begin_noshake();
r_state_push();
r_blend(BLEND_PREMUL_ALPHA);
r_shader_standard();
fbpair_swap(&draw_data->baryon.fbpair);
r_color4(0.7, 0.7, 0.7, 0.7);
draw_framebuffer_tex(draw_data->baryon.fbpair.front, VIEWPORT_W, VIEWPORT_H);
r_state_pop();
stage_draw_end_noshake();
}
static void baryons_bg_fill(Stage6DrawData *draw_data, int t, EllyBaryons *baryons, BoxedProjectileArray *particles) {
if(!particles->size) {
return;
}
r_state_push();
r_color4(1, 1, 1, 1);
r_framebuffer(draw_data->baryon.fbpair.front);
ENT_ARRAY_FOREACH(particles, Projectile *p, {
r_state_push();
p->ent.draw_func(&p->entity_interface);
r_state_pop();
});
if(baryons) {
ShaderProgram *shader = res_shader("sprite_default");
Sprite *sprite = res_sprite("part/myon");
for(int i = 0; i < NUM_BARYONS; ++i) {
r_draw_sprite(&(SpriteParams) {
.sprite_ptr = sprite,
.shader_ptr = shader,
.color = RGBA(1, 0.2, 1.0, 0.7),
.pos.as_cmplx = baryons->poss[i], //+10*frand()*cexp(2.0*I*M_PI*frand());
.rotation.angle = (i - t) / 16.0, // frand()*M_PI*2,
.scale.both = 2,
});
}
r_flush_sprites();
}
r_state_pop();
}
TASK(baryons_particle_spawner, { BoxedEllyBaryons baryons; BoxedProjectileArray *particles; }) {
EllyBaryons *baryons = TASK_BIND(ARGS.baryons);
for (;;) {
baryons_particles(baryons, ARGS.particles);
ENT_ARRAY_COMPACT(ARGS.particles);
YIELD;
}
}
TASK(baryons_visuals, { BoxedEllyBaryons baryons; }) {
DECLARE_ENT_ARRAY(Projectile, particles, MAX_BARYON_PARTICLES);
CoEvent *draw_event = &stage_get_draw_events()->background_drawn;
Stage6DrawData *draw_data = stage6_get_draw_data();
INVOKE_SUBTASK(baryons_particle_spawner, ARGS.baryons, &particles);
int timeout = 60;
int endtime = timeout;
for(int t = 0; t < endtime; ++t) {
WAIT_EVENT_OR_DIE(draw_event);
EllyBaryons *baryons = ENT_UNBOX(ARGS.baryons);
if(baryons != NULL) {
endtime = t + timeout;
}
if(config_get_int(CONFIG_POSTPROCESS) < 1) {
continue;
}
baryons_bg_fill(draw_data, t, baryons, &particles);
baryons_bg_feedback(draw_data, t);
baryons_bg_blend(draw_data);
}
}
TASK(baryons_update, { BoxedEllyBaryons baryons; }) {
EllyBaryons *baryons = TASK_BIND(ARGS.baryons);
for (;;) {
baryons->center_angle += 0.035;
for(int i = 0; i < NUM_BARYONS; i++) {
cmplx d = (baryons->target_poss[i] - baryons->poss[i]);
if(cabs2(d) > 0) {
baryons->poss[i] += baryons->relaxation_rate * (d / sqrt(cabs(d)) + 0.01*d);
}
}
YIELD;
}
}
TASK(baryons_quick_intro, { BoxedEllyBaryons baryons; }) {
EllyBaryons *baryons = TASK_BIND(ARGS.baryons);
int duration = 30;
for(int t = 0; t < duration; t++) {
baryons->scale = glm_ease_quad_in(t / (float) duration);
YIELD;
}
}
cmplx stage6_elly_baryon_default_offset(int i) {
return 150 * cdir(M_TAU / NUM_BARYONS * i);
}
void stage6_init_elly_baryons(BoxedEllyBaryons baryons, BoxedBoss boss) {
Boss *b = NOT_NULL(ENT_UNBOX(boss));
EllyBaryons *eb = NOT_NULL(ENT_UNBOX(baryons));
for(int i = 0; i < NUM_BARYONS; i++) {
eb->target_poss[i] = b->pos + stage6_elly_baryon_default_offset(i);
eb->poss[i] = b->pos;
}
eb->center_pos = b->pos;
eb->scale = 0;
eb->relaxation_rate = 0.5;
INVOKE_TASK(baryons_update, baryons);
INVOKE_TASK(baryons_visuals, baryons);
}
TASK(reset_baryons, { BoxedBoss boss; BoxedEllyBaryons baryons; }) {
auto baryons = TASK_BIND(ARGS.baryons);
auto boss = NOT_NULL(ENT_UNBOX(ARGS.boss));
for(int i = 0; i < NUM_BARYONS; ++i) {
baryons->target_poss[i] = boss->pos + stage6_elly_baryon_default_offset(i);
}
}
Boss *stage6_elly_init_baryons_attack(BaryonsAttackTaskArgs *args) {
if(ENT_UNBOX(args->baryons) == NULL) {
args->baryons = ENT_BOX(stage6_host_elly_baryons(args->base.boss));
stage6_init_elly_baryons(args->baryons, args->base.boss);
INVOKE_TASK(baryons_quick_intro, args->baryons);
}
auto boss = INIT_BOSS_ATTACK(&args->base);
INVOKE_TASK_AFTER(
&args->base.attack->events.finished,
reset_baryons,
ENT_BOX(boss), args->baryons
);
return boss;
}
EllyBaryons *stage6_host_elly_baryons(BoxedBoss boss) {
EllyBaryons *baryons = TASK_HOST_ENT(EllyBaryons);
TASK_HOST_EVENTS(baryons->events);
return baryons;
}
void elly_clap(Boss *b, int claptime) {
@ -192,128 +427,3 @@ void elly_clap(Boss *b, int claptime) {
aniplayer_queue(&b->ani, "main", 0);
}
static int baryon_unfold(Enemy *baryon, int t) {
if(t < 0)
return 1; // not catching death for references! because there will be no death!
TIMER(&t);
int extent = 100;
float timeout;
if(global.stage->type == STAGE_SPELL) {
timeout = 92;
} else {
timeout = 142;
}
FROM_TO(0, timeout, 1) {
for(Enemy *e = global.enemies.first; e; e = e->next) {
if(e->visual_rule == baryon_center_draw) {
float f = t / (float)timeout;
float x = f;
float g = sin(2 * M_PI * log(log(x + 1) + 1));
float a = g * pow(1 - x, 2 * x);
f = 1 - pow(1 - f, 3) + a;
baryon->pos = baryon->pos0 = e->pos + baryon->args[0] * f * extent;
return 1;
}
}
}
return 1;
}
static int elly_baryon_center(Enemy *e, int t) {
if(t == EVENT_DEATH) {
free_ref(creal(e->args[1]));
free_ref(cimag(e->args[1]));
}
if(global.boss) {
e->pos = global.boss->pos;
}
return 1;
}
int scythe_explode(Enemy *e, int t) {
if(t < 0) {
scythe_common(e, t);
return 0;
}
if(t < 50) {
e->args[1] += 0.001*I*t;
e->args[2] += 0.0015*t;
}
if(t >= 50)
e->args[2] -= 0.002*(t-50);
if(t == 100) {
petal_explosion(100, e->pos);
stage_shake_view(16);
play_sfx("boom");
scythe_common(e, t);
return ACTION_DESTROY;
}
scythe_common(e, t);
return 1;
}
void elly_spawn_baryons(cmplx pos) {
int i;
Enemy *e, *last = NULL, *first = NULL, *middle = NULL;
for(i = 0; i < 6; i++) {
e = create_enemy3c(pos, ENEMY_IMMUNE, baryon, baryon_unfold, 1.5*cexp(2.0*I*M_PI/6*i), i != 0 ? add_ref(last) : 0, i);
e->ent.draw_layer = LAYER_BACKGROUND;
if(i == 0) {
first = e;
} else if(i == 3) {
middle = e;
}
last = e;
}
first->args[1] = add_ref(last);
e = create_enemy2c(pos, ENEMY_IMMUNE, baryon_center_draw, elly_baryon_center, 0, add_ref(first) + I*add_ref(middle));
e->ent.draw_layer = LAYER_BACKGROUND;
}
void set_baryon_rule(EnemyLogicRule r) {
Enemy *e;
for(e = global.enemies.first; e; e = e->next) {
if(e->visual_rule == baryon) {
e->birthtime = global.frames;
e->logic_rule = r;
}
}
}
int baryon_reset(Enemy* baryon, int t) {
if(t < 0) {
return 1;
}
for(Enemy *e = global.enemies.first; e; e = e->next) {
if(e->visual_rule == baryon_center_draw) {
cmplx targ_pos = baryon->pos0 - e->pos0 + e->pos;
GO_TO(baryon, targ_pos, 0.1);
return 1;
}
}
GO_TO(baryon, baryon->pos0, 0.1);
return 1;
}

View file

@ -18,18 +18,67 @@
#define SYMMETRYTIME (HIGGSTIME+200)
#define BREAKTIME (YUKAWATIME+400)
void scythe_common(Enemy*, int t);
void scythe_draw(Enemy*, int, bool);
int scythe_mid(Enemy*, int);
int scythe_reset(Enemy*, int);
int scythe_infinity(Enemy*, int);
int scythe_explode(Enemy*, int);
#define ELLY_DEFAULT_POS (BOSS_DEFAULT_GO_POS)
#define ELLY_SCYTHE_RESTING_OFS (8)
Boss* stage6_spawn_elly(cmplx pos);
DEFINE_ENTITY_TYPE(EllyScythe, {
cmplx pos;
real angle;
real spin;
real resting_angle;
float scale;
MoveParams move;
COEVENTS_ARRAY(
despawned
) events;
});
EllyScythe *stage6_host_elly_scythe(cmplx pos);
void stage6_despawn_elly_scythe(EllyScythe *scythe);
DEFINE_TASK_INTERFACE_WITH_BASE(ScytheAttack, BossAttack, {
BoxedEllyScythe scythe;
});
typedef TASK_IFACE_ARGS_TYPE(ScytheAttack) ScytheAttackTaskArgs;
Boss *stage6_elly_init_scythe_attack(ScytheAttackTaskArgs *args);
#define NUM_BARYONS 6
#define MAX_BARYON_PARTICLES 128
DEFINE_ENTITY_TYPE(EllyBaryons, {
cmplx poss[NUM_BARYONS];
cmplx target_poss[NUM_BARYONS];
real relaxation_rate;
// angles are currently unused, broglie sign may use them. otherwise remove.
real angles[NUM_BARYONS];
cmplx center_pos;
float center_angle;
float scale;
COEVENTS_ARRAY(
despawned
) events;
});
EllyBaryons *stage6_host_elly_baryons(BoxedBoss boss);
void stage6_init_elly_baryons(BoxedEllyBaryons baryons, BoxedBoss boss);
DEFINE_TASK_INTERFACE_WITH_BASE(BaryonsAttack, BossAttack, {
BoxedEllyBaryons baryons;
});
typedef TASK_IFACE_ARGS_TYPE(BaryonsAttack) BaryonsAttackTaskArgs;
Boss *stage6_elly_init_baryons_attack(BaryonsAttackTaskArgs *args);
cmplx stage6_elly_baryon_default_offset(int i);
void elly_clap(Boss*, int);
void elly_spawn_baryons(cmplx);
void set_baryon_rule(EnemyLogicRule);
int baryon_reset(Enemy*, int);
int wait_proj(Projectile*, int);
Enemy* find_scythe(void);

View file

@ -10,4 +10,6 @@
#include "taisei.h"
#define ENTITIES_STAGE6(X, ...) \
X(EllyScythe, __VA_ARGS__) \
X(EllyBaryons, __VA_ARGS__) \
END_OF_ENTITIES

View file

@ -10,6 +10,7 @@ stage6_src = files(
'nonspells/boss_nonspell_3.c',
'nonspells/boss_nonspell_4.c',
'nonspells/boss_nonspell_5.c',
'nonspells/boss_nonspell_6.c',
'spells/lhc.c',
'spells/toe.c',
'spells/ricci.c',

View file

@ -9,25 +9,49 @@
#include "taisei.h"
#include "nonspells.h"
#include "../elly.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
DEFINE_EXTERN_TASK(stage6_boss_nonspell_scythe_common) {
EllyScythe *scythe = TASK_BIND(ARGS.scythe);
void elly_frequency(Boss *b, int t) {
TIMER(&t);
Enemy *scythe;
cmplx center = ELLY_DEFAULT_POS;
AT(EVENT_BIRTH) {
scythe = find_scythe();
aniplayer_queue(&b->ani, "snipsnip", 0);
scythe->birthtime = global.frames;
scythe->logic_rule = scythe_infinity;
scythe->args[0] = 2;
}
scythe->spin = 0.2;
scythe->move = move_towards(center, 0.1);
WAIT(40);
scythe->move.retention = 0.9;
AT(EVENT_DEATH) {
scythe = find_scythe();
scythe->birthtime = global.frames;
scythe->logic_rule = scythe_reset;
scythe->args[0] = 0;
for(int t = 0;; t++) {
play_sfx_loop("shot1_loop");
real theta = 0.01 * t * (1 + 0.001 * t) + M_PI/2;
scythe->pos = center + 200 * cos(theta) * (1 + sin(theta) * I) / (1 + pow(sin(theta), 2));
cmplx dir = cdir(scythe->angle);
real vel = difficulty_value(1.4, 1.8, 2.2, 2.6);
cmplx color_dir = cdir(3 * theta);
if(t > 50) {
PROJECTILE(
.proto = pp_ball,
.pos = scythe->pos + 60 * dir * cdir(1.2),
.color = RGB(creal(color_dir), cimag(color_dir), creal(color_dir * cdir(2.1))),
.move = move_accelerated(0, 0.01 *vel * dir),
);
}
YIELD;
}
}
DEFINE_EXTERN_TASK(stage6_boss_nonspell_1) {
STAGE_BOOKMARK(boss-non1);
stage6_elly_init_scythe_attack(&ARGS);
BEGIN_BOSS_ATTACK(&ARGS.base);
INVOKE_SUBTASK(stage6_boss_nonspell_scythe_common, ARGS.scythe);
STALL;
}

View file

@ -10,34 +10,27 @@
#include "nonspells.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
DEFINE_EXTERN_TASK(stage6_boss_nonspell_2) {
STAGE_BOOKMARK(boss-non2);
void elly_frequency2(Boss *b, int t) {
TIMER(&t);
Boss *boss = stage6_elly_init_scythe_attack(&ARGS);
BEGIN_BOSS_ATTACK(&ARGS.base);
aniplayer_queue(&boss->ani, "snipsnip", 0);
AT(0) {
Enemy *scythe = find_scythe();
aniplayer_queue(&b->ani, "snipsnip", 0);
scythe->birthtime = global.frames;
scythe->logic_rule = scythe_infinity;
scythe->args[0] = 4;
}
INVOKE_SUBTASK(stage6_boss_nonspell_scythe_common, ARGS.scythe);
AT(EVENT_DEATH) {
Enemy *scythe = find_scythe();
scythe->birthtime = global.frames;
scythe->logic_rule = scythe_reset;
scythe->args[0] = 0;
}
int interval = difficulty_value(3, 3, 2, 2);
for(int t = 1;; t++) {
cmplx dir = sin(t * 0.35) * cdir(t*0.02);
FROM_TO_SND("shot1_loop",0, 2000, 3-global.diff/2) {
cmplx n = sin(t*0.12*global.diff)*cexp(t*0.02*I*global.diff);
PROJECTILE(
.proto = pp_plainball,
.pos = b->pos+80*n,
.pos = boss->pos + 50 * dir,
.color = RGB(0,0,0.7),
.rule = asymptotic,
.args = { 2*n/cabs(n), 3 }
.move = move_asymptotic_simple(2*cnormalize(dir), 3)
);
WAIT(interval);
}
}

View file

@ -11,41 +11,59 @@
#include "nonspells.h"
#include "stagetext.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
#include "common_tasks.h"
void elly_paradigm_shift(Boss *b, int t) {
if(global.stage->type == STAGE_SPELL) {
GO_TO(b, BOSS_DEFAULT_GO_POS, 0.1)
} else if(t == 0) {
play_sfx("bossdeath");
}
TASK(scythe_explode, { BoxedEllyScythe scythe; }) {
EllyScythe *scythe = TASK_BIND(ARGS.scythe);
INVOKE_TASK(common_easing_animated, &scythe->angle, 1000, 100, glm_ease_quad_in);
INVOKE_TASK(common_easing_animate, &scythe->scale, 1.7, 70, glm_ease_quad_inout);
INVOKE_TASK_DELAYED(70, common_easing_animate, &scythe->scale, 0, 30, glm_ease_linear);
TIMER(&t);
WAIT(100);
petal_explosion(100, scythe->pos);
play_sfx("boom");
stage6_despawn_elly_scythe(scythe);
}
AT(0) {
Enemy *scythe = find_scythe();
scythe->birthtime = global.frames;
scythe->logic_rule = scythe_explode;
elly_clap(b,150);
}
TASK(baryons_spawn, { BoxedBoss boss; BoxedEllyBaryons baryons; }) {
stage6_init_elly_baryons(ARGS.baryons, ARGS.boss);
int duration = 100;
EllyBaryons *baryons = TASK_BIND(ARGS.baryons);
INVOKE_SUBTASK(common_easing_animate, &baryons->scale, 1, duration, glm_ease_bounce_out);
if(global.stage->type != STAGE_SPELL) {
AT(80) {
audio_bgm_stop(0.5);
real angle_offset = -5;
INVOKE_SUBTASK(common_easing_animated, &angle_offset, 0, duration, glm_ease_quad_out);
for(int t = 0; t < duration; t++) {
Boss *b = NOT_NULL(ENT_UNBOX(ARGS.boss));
for(int i = 0; i < NUM_BARYONS; i++) {
real radius = 3*200.0/duration * clamp(t - duration/(real)NUM_BARYONS/2*i, 0, duration/3.0);
baryons->target_poss[i] = b->pos + radius * cdir((1-2*(i&1)) * angle_offset + M_TAU/NUM_BARYONS*i);
}
}
AT(100) {
if(global.stage->type != STAGE_SPELL) {
stage_unlock_bgm("stage6boss_phase1");
stage_start_bgm("stage6boss_phase2");
stagetext_add("Paradigm Shift!", VIEWPORT_W/2+I*(VIEWPORT_H/2+64), ALIGN_CENTER, get_font("big"), RGB(1, 1, 1), 0, 120, 10, 30);
}
elly_spawn_baryons(b->pos);
}
AT(120) {
stage_shake_view(200);
YIELD;
}
}
DEFINE_EXTERN_TASK(stage6_boss_paradigm_shift) {
STAGE_BOOKMARK(boss-baryons);
Boss *boss = INIT_BOSS_ATTACK(&ARGS.base);
BEGIN_BOSS_ATTACK(&ARGS.base);
play_sfx("bossdeath");
INVOKE_TASK(scythe_explode, ARGS.scythe);
elly_clap(boss, 150);
WAIT(80);
audio_bgm_stop(0.5);
WAIT(20);
stage_unlock_bgm("stage6boss_phase1");
stage_start_bgm("stage6boss_phase2");
stagetext_add("Paradigm Shift!", VIEWPORT_W/2.0 + I * (VIEWPORT_H/2.0+64), ALIGN_CENTER, res_font("big"), RGB(1, 1, 1), 0, 120, 10, 30);
stage_shake_view(200);
INVOKE_TASK(baryons_spawn, ENT_BOX(boss), ARGS.baryons);
}

View file

@ -10,87 +10,51 @@
#include "nonspells.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
static int baryon_nattack(Enemy *e, int t) {
if(t < 0)
return 1;
TASK(baryons_nonspell_movement, { BoxedEllyBaryons baryons; }) {
EllyBaryons *baryons = TASK_BIND(ARGS.baryons);
TIMER(&t);
e->pos = global.boss->pos + (e->pos-global.boss->pos)*cexp(0.006*I);
FROM_TO_SND("shot1_loop",30, 10000, (7 - global.diff)) {
float a = 0.2*_i + creal(e->args[2]) + 0.006*t;
float ca = a + t/60.0f;
PROJECTILE(
.proto = pp_ball,
.pos = e->pos+40*cexp(I*a),
.color = RGB(cos(ca), sin(ca), cos(ca+2.1)),
.rule = asymptotic,
.args = {
(1+0.2*global.diff)*cexp(I*a),
3
}
);
}
return 1;
}
void elly_baryonattack(Boss *b, int t) {
TIMER(&t);
AT(0)
set_baryon_rule(baryon_nattack);
AT(EVENT_DEATH)
set_baryon_rule(baryon_reset);
}
void elly_baryonattack2(Boss *b, int t) {
TIMER(&t);
AT(0) {
aniplayer_queue(&b->ani, "snipsnip", 0);
set_baryon_rule(baryon_nattack);
}
AT(EVENT_DEATH)
set_baryon_rule(baryon_reset);
FROM_TO(100, 100000, 200-5*global.diff) {
play_sfx("shot_special1");
if(_i % 2) {
int cnt = 5;
for(int i = 0; i < cnt; ++i) {
float a = M_PI/4;
a = a * (i/(float)cnt) - a/2;
cmplx n = cexp(I*(a+carg(global.plr.pos-b->pos)));
for(int j = 0; j < 3; ++j) {
PROJECTILE(
.proto = pp_bigball,
.pos = b->pos,
.color = RGB(0,0.2,0.9),
.rule = asymptotic,
.args = { n, 2 * j }
);
}
}
} else {
int x, y;
int w = 1+(global.diff > D_Normal);
cmplx n = cexp(I*carg(global.plr.pos-b->pos));
for(x = -w; x <= w; x++) {
for(y = -w; y <= w; y++) {
PROJECTILE(
.proto = pp_bigball,
.pos = b->pos+25*(x+I*y)*n,
.color = RGB(0,0.2,0.9),
.rule = asymptotic,
.args = { n, 3 },
);
}
}
for(int t = 0;; t++) {
for(int i = 0; i < NUM_BARYONS; i++) {
baryons->target_poss[i] = baryons->center_pos + 150 * cdir(M_TAU/6 * i + 0.006 * t);
}
YIELD;
}
}
DEFINE_EXTERN_TASK(stage6_boss_nonspell_baryons_common) {
EllyBaryons *baryons = TASK_BIND(ARGS.baryons);
INVOKE_SUBTASK(baryons_nonspell_movement, ARGS.baryons);
int interval = difficulty_value(6, 5, 4, 3);
real speed = difficulty_value(1.2, 1.4, 1.6, 1.8);
WAIT(30)