stage3 redesign: some first prototypes

This commit is contained in:
laochailan 2021-09-30 20:38:38 +02:00 committed by Andrei Alexeyev
parent 0c2855e3ca
commit c2527b05cf
No known key found for this signature in database
GPG key ID: 72D26128040B9690
2 changed files with 129 additions and 646 deletions

View file

@ -21,510 +21,133 @@
#include "common_tasks.h"
#include "enemy_classes.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
static void stage3_dialog_post_boss(void) {
PlayerMode *pm = global.plr.mode;
INVOKE_TASK_INDIRECT(Stage3PostBossDialog, pm->dialog->Stage3PostBoss);
}
TASK(destroy_enemy, { BoxedEnemy e; }) {
// used for when enemies should pop after a preset time
enemy_kill(TASK_BIND(ARGS.e));
TASK(swarm_trail_proj, { cmplx pos; cmplx vstart; cmplx vend; real x; real width;}) {
Projectile *p = TASK_BIND(PROJECTILE(
.proto = pp_rice,
.color = RGB(0.4, 0, 0.8),
.max_viewport_dist = 1000,
));
BoxedProjectile phead = ENT_BOX(PROJECTILE(
.proto = pp_flea,
.color = RGB(0.8, 0.1, 0.4),
.max_viewport_dist = 1000,
));
real turn_length = 1; // |x/turn_length| should be smaller than pi
cmplx prevpos = ARGS.pos;
for(int t = -70;; t++) {
if(t == 0) {
play_sfx("redirect");
}
cmplx z = t/ARGS.width + I * ARGS.x/turn_length;
p->pos = ARGS.pos + ARGS.width * z * (ARGS.vstart + (ARGS.vend-ARGS.vstart)/(cexp(-z) + 1));
p->angle = carg(p->pos-prevpos);
Projectile *head = ENT_UNBOX(phead);
if(head) {
head->pos = p->pos + 8*cdir(p->angle);
}
p->move = move_linear(p->pos-prevpos);
prevpos = p->pos;
YIELD;
}
}
TASK(death_burst, { BoxedEnemy e; ProjPrototype *shot_proj; }) {
// explode in a hail of bullets
Enemy *e = TASK_BIND(ARGS.e);
float r, g;
if(rng_chance(0.5)) {
r = 0.3;
g = 1.0;
} else {
r = 1.0;
g = 0.3;
}
int intensity = difficulty_value(12, 16, 20, 24);
for(int i = 0; i < intensity; ++i) {
real a = (M_PI * 2.0 * i) / intensity;
cmplx dir = cdir(a);
PROJECTILE(
.proto = ARGS.shot_proj, // pp_ball or pp_rice,
.pos = e->pos,
.color = RGB(r, g, 1.0),
.move = move_asymptotic_simple(1.5 * dir, 10 - 10 * psin(2 * a + M_PI/2)),
);
}
}
TASK(burst_swirl, { cmplx pos; cmplx dir; ProjPrototype *shot_proj; }) {
// swirls - fly in, explode when killed or after a preset time
Enemy *e = TASK_BIND(espawn_swirl(ARGS.pos, ITEMS(
TASK(swarm_trail_fairy, { cmplx pos; MoveParams move; }) {
Enemy *e = TASK_BIND(espawn_big_fairy(ARGS.pos, ITEMS(
.points = 1,
.power = 1,
)));
// die after preset time
INVOKE_TASK_DELAYED(60, destroy_enemy, ENT_BOX(e));
INVOKE_TASK_WHEN(&e->events.killed, death_burst, ENT_BOX(e), ARGS.shot_proj);
e->move = move_linear(ARGS.dir);
}
TASK(burst_swirls, { int count; int interval; ProjPrototype *shot_proj; }) {
for(int i = 0; i < ARGS.count; ++i) {
// spawn in a "pitchfork" pattern
cmplx pos = VIEWPORT_W/2 + 20 * rng_sreal();
pos += (VIEWPORT_H/4 + 20 * rng_sreal()) * I;
INVOKE_TASK(burst_swirl,
.pos = pos,
.dir = 3 * (I + sin(M_PI*global.frames/15.0)),
.shot_proj = ARGS.shot_proj
);
WAIT(ARGS.interval);
}
}
// side swirls
// typically move across a stage in a drive-by fashion
TASK(side_swirl, { MoveParams move; cmplx start_pos; }) {
Enemy *e = TASK_BIND(espawn_swirl(ARGS.start_pos, ITEMS(
.points = 1,
.power = 1,
.power = 2,
)));
e->move = ARGS.move;
WAIT(50);
int intensity = difficulty_value(1, 2, 3, 4);
for(int i = 0; i < intensity; ++i ) {
PROJECTILE(
.proto = pp_flea,
.pos = e->pos,
.color = RGB(0.7, 0.0, 0.5),
.move = move_accelerated(
2 * (cnormalize(global.plr.pos - e->pos)),
0.005 * rng_dir() * intensity
),
);
int shooting_time = 200;
real width = 30;
int interval = 10;
int nrow = 5;
WAIT(30);
e->move.retention = 0.9;
WAIT(10);
cmplx aim = cnormalize(global.plr.pos - e->pos);
for(int t = 0; t < shooting_time/interval; t++) {
play_sfx("shot1");
WAIT(20);
for(int i = 0; i < nrow - (t&1); i++) {
real x = ((i+0.5*(t&1))/(real)(nrow-1) - 0.5);
INVOKE_TASK(swarm_trail_proj, e->pos, ARGS.move.velocity, 3*aim, x, .width = width);
}
WAIT(interval);
}
play_sfx("redirect");
e->move = move_asymptotic_halflife(0, -ARGS.move.velocity , 120);
}
TASK(swarm_trail_fairy_spawn, { int count; }) {
for(int i = 0; i < ARGS.count; i++) {
cmplx pos = VIEWPORT_W / 2.0;
cmplx vel = 5*I + 4 * cdir(-M_PI * i / (real)(ARGS.count-1));
INVOKE_TASK(swarm_trail_fairy, pos, move_linear(vel));
WAIT(60);
}
}
TASK(side_swirls_procession, { int interval; int count; MoveParams move; cmplx start_pos; }) {
for(int x = 0; x < ARGS.count; ++x) {
INVOKE_TASK(side_swirl, ARGS.move, ARGS.start_pos);
TASK(flower_swirl, { cmplx pos; cmplx dir; }) {
Enemy *e = TASK_BIND(espawn_swirl(ARGS.pos, ITEMS(
.points = 1,
)));
real angular_velocity = 0.1;
real radius = 50;
cmplx retention = 0.99*cdir(angular_velocity);
e->move = (MoveParams){
.velocity = radius * cnormalize(ARGS.dir) * I * cabs(1 - retention),
.retention = retention,
.acceleration = ARGS.dir * (1 - retention)
};
int interval = 10;
int petals = 5;
for(int t = 0;; t++) {
play_sfx("shot2");
for(int i = 0; i < petals; i++) {
cmplx dir = cdir(M_TAU / petals * i);
PROJECTILE(
.proto = pp_wave,
.color = RGB(1, 0.5, 0.6),
.pos = e->pos + 4 * dir,
.move = move_asymptotic_halflife(0.1*dir, -2 * dir, 220),
);
}
WAIT(interval);
}
}
TASK(flower_swirl_spawn, { cmplx pos; cmplx dir; int count; int interval; cmplx spread; }) {
for(int i = 0; i < ARGS.count; i++) {
cmplx pos = ARGS.pos + creal(ARGS.spread) * rng_sreal();
pos += I * cimag(ARGS.spread) * rng_sreal();
INVOKE_TASK(flower_swirl, pos, ARGS.dir);
WAIT(ARGS.interval);
}
}
TASK(little_fairy_shot_ball, { BoxedEnemy e; int shot_interval; int intensity;} ) {
Enemy *e = TASK_BIND(ARGS.e);
e->move.attraction = 0.05;
WAIT(60);
for (int i = 0; i < ARGS.intensity; ++i) {
float a = global.timer * 0.5;
cmplx dir = cdir(a);
// fire out danmaku in all directions in a spiral-ish pattern
PROJECTILE(
.proto = pp_wave,
.pos = e->pos + dir * 10,
.color = (i % 2) ? RGB(1.0, 0.3, 0.3) : RGB(0.3, 0.3, 1.0),
.move = move_accelerated(dir, dir * 0.025),
);
// danmaku that only shows up above Easy difficulty
if(global.diff > D_Easy) {
PROJECTILE(
.proto = pp_ball,
.pos = e->pos + dir * 10,
.color = RGB(1.0, 0.6, 0.3),
.move = move_linear(dir * (1.0 + 0.5 * sin(a))),
);
}
play_sfx("shot1");
WAIT(ARGS.shot_interval);
}
WAIT(60);
}
TASK(little_fairy_shot_wave, { BoxedEnemy e; int shot_interval; int intensity; } ) {
Enemy *e = TASK_BIND(ARGS.e);
e->move.attraction = 0.03;
WAIT(60);
for (int i = 0; i < ARGS.intensity; ++i) {
double a = global.timer/sqrt(global.diff);
cmplx dir = cdir(a);
if (i > 90) {
a *= -1;
}
PROJECTILE(
.proto = pp_wave,
.pos = e->pos,
.color = (i & 3) ? RGB(1.0, 0.3, 0.3) : RGB(0.3, 0.3, 1.0),
.move = move_linear(2 * dir)
);
if (global.diff > D_Normal && global.timer % 3 == 0) {
PROJECTILE(
.proto = pp_wave,
.pos = e->pos,
.color = !(i & 3) ? RGB(1.0, 0.3, 0.3) : RGB(0.3, 0.3, 1.0),
.move = move_linear(-2 * dir)
);
}
play_sfx("shot1");
WAIT(ARGS.shot_interval);
}
WAIT(60);
}
TASK(little_fairy, { cmplx pos; cmplx target_pos; int shot_type; int side; }) {
// contains stage3_slavefairy and stage3_slavefairy2
Enemy *e = TASK_BIND(espawn_fairy_blue(ARGS.pos, ITEMS(
.points = 3,
.power = 1,
)));
// fade-in
e->alpha = 0;
e->move = move_towards(ARGS.target_pos, 0);
int shot_interval = 1;
int intensity = difficulty_value(30, 50, 70, 90);
if(ARGS.shot_type){
INVOKE_TASK(little_fairy_shot_ball, ENT_BOX(e), shot_interval, intensity);
} else {
INVOKE_TASK(little_fairy_shot_wave, ENT_BOX(e), shot_interval, intensity);
}
WAIT(120);
// fly out
cmplx exit_accel = 0.02 * I + (ARGS.side * 0.03);
e->move.attraction = 0;
e->move.acceleration = exit_accel;
e->move.retention = 1;
}
TASK(little_fairy_line, { int count; }) {
for(int i = 0; i < ARGS.count; ++i) {
cmplx pos1 = VIEWPORT_W/2 + VIEWPORT_W/3 * rng_f32() + VIEWPORT_H/5*I;
cmplx pos2 = VIEWPORT_W/2 + 100 * (i - ARGS.count/2) + VIEWPORT_H/3*I;
INVOKE_TASK(little_fairy,
.pos = pos1,
.target_pos = pos2,
.side = 2
);
WAIT(5);
}
}
TASK(big_fairy_group, { cmplx pos; int shot_type; } ) {
// big fairy in the middle does nothing
// 8 fairies (2 pairs in 4 waves - bottom/top/bottom/top) spawn around her and open fire
Enemy *e = TASK_BIND(espawn_huge_fairy(ARGS.pos, ITEMS(
.points = 3,
.power = 2,
)));
e->alpha = 0;
WAIT(100);
for(int x = 0; x < 2; ++x) {
// type, big fairy pos, little fairy pos (relative), danmaku type, side of big fairy
INVOKE_TASK(little_fairy,
.pos = e->pos,
.target_pos = e->pos + 70 + 50 * I,
.shot_type = ARGS.shot_type,
.side = 1
);
INVOKE_TASK(little_fairy,
.pos = e->pos,
.target_pos = e->pos - 70 + 50 * I,
.shot_type = ARGS.shot_type,
.side = -1
);
WAIT(100);
INVOKE_TASK(little_fairy,
.pos = e->pos,
.target_pos = e->pos + 70 - 50 * I,
.shot_type = ARGS.shot_type,
.side = 1
);
INVOKE_TASK(little_fairy,
.pos = e->pos,
.target_pos = e->pos - 70 - 50 * I,
.shot_type = ARGS.shot_type,
.side = -1
);
WAIT(200);
}
e->move.attraction = 0;
e->move.acceleration = 0.02 * I + 0;
e->move.retention = 1;
}
TASK(burst_fairy, { cmplx pos; cmplx target_pos; } ) {
Enemy *e = TASK_BIND(espawn_fairy_red(ARGS.pos, ITEMS(
.points = 1,
.power = 1,
)));
e->alpha = 0;
e->move = move_towards(ARGS.target_pos, 0.08);
WAIT(30);
int difficulty = difficulty_value(4, 8, 12, 16);
int count = 6 + difficulty;
for(int p = 0; p < count; ++p) {
cmplx dir = cdir(M_PI * 2 * p / count);
PROJECTILE(
.proto = pp_ball,
.pos = ARGS.target_pos,
.color = RGB(0.2, 0.1, 0.5),
.move = move_asymptotic_simple(dir, 10 + difficulty)
);
}
WAIT(60);
play_sfx("shot_special1");
INVOKE_TASK(common_charge, e->pos, RGBA(0.0, 0.5, 1.0, 0.5), 60, .sound = COMMON_CHARGE_SOUNDS);
int intensity = difficulty_value(4, 6, 8, 10);
for(int x = 0; x < intensity; ++x) {
double phase = 0.1 * x;
cmplx dir = cdir(M_PI * phase);
for(int p = 0; p < intensity; ++p) {
for(int i = -1; i < 2; i += 2) {
PROJECTILE(
.proto = pp_bullet,
.pos = e->pos + dir * 10,
.color = color_lerp(RGB(0.0, 0.0, 1.0), RGB(1.0, 0.0, 0.0), psin(M_PI * phase)),
.move = move_asymptotic_simple(1.5 * dir * (1 + p / (intensity - 1.0)) * i, 3 * difficulty),
);
}
}
WAIT(2);
}
WAIT(5);
cmplx exit_accel = 0.02 * I + 0.03;
e->move.attraction = 0;
e->move.acceleration = exit_accel;
e->move.retention = 1;
}
TASK(burst_fairy_squad, { int count; int step; } ) {
// fires a bunch of "lines" of bullets in a starburst pattern
// does so after waiting for a short amount of time
for(int i = 0; i < ARGS.count; ++i) {
double span = 300 - 60 * (i/2);
cmplx pos = VIEWPORT_W/2 + span * (-0.5 + (i & 1)) + (VIEWPORT_H/3 + 100 * (i / 2)) * I;
// type, starting_position, target_position
INVOKE_TASK(burst_fairy, VIEWPORT_W/2, pos);
WAIT(ARGS.step);
}
}
TASK(charge_fairy, { cmplx pos; cmplx target_pos; cmplx exit_dir; int charge_time; int move_first; }) {
// charges up some danmaku and then "releases" them
Enemy *e = TASK_BIND(espawn_fairy_red(ARGS.pos, ITEMS(
.points = 1,
.power = 4,
)));
e->alpha = 0;
if(ARGS.move_first) {
e->move = move_towards(ARGS.target_pos, 0.03);
WAIT(100);
}
play_sfx("shot_special1");
INVOKE_TASK(common_charge, e->pos, RGBA(0.0, 0.5, 1.0, 0.5), ARGS.charge_time, .sound = COMMON_CHARGE_SOUNDS);
WAIT(ARGS.charge_time + 20);
int intensity = difficulty_value(11, 15, 19, 23);
int layers = difficulty_value(2, 3, 4, 5);
int nproj = intensity * layers;
DECLARE_ENT_ARRAY(Projectile, projs, nproj);
for(int x = 0; x < intensity; ++x) {
cmplx aim = cnormalize(global.plr.pos - e->pos);
aim /= cabs(aim);
cmplx aim_norm = aim * I;
int i = x;
for(int layer = 0; layer < layers; ++layer) {
if(layer&1) {
i = intensity - 1 - i;
}
double f = i / (intensity - 1.0);
int w = 100 - 20 * layer;
cmplx o = e->pos + w * psin(M_PI * f) * aim + aim_norm * w * 0.8 * (f - 0.5);
cmplx paim = e->pos + (w+1) * aim - o;
paim /= cabs(paim);
cmplx v = paim * 0.2 * (6 + global.diff - layer);
ENT_ARRAY_ADD(&projs, PROJECTILE(
.proto = pp_wave,
.pos = o,
.color = color_lerp(RGB(0.0, 0.0, 1.0), RGB(1.0, 0.0, 0.0), f),
.move = move_linear(v),
.angle = carg(v),
.flags = PFLAG_NOMOVE,
));
}
}
ENT_ARRAY_FOREACH(&projs, Projectile *p, {
spawn_projectile_highlight_effect(p);
p->flags &= ~PFLAG_NOMOVE;
play_sfx("redirect");
play_sfx("shot_special1");
});
WAIT(100);
e->move = move_linear(ARGS.exit_dir);
ENT_ARRAY_CLEAR(&projs);
}
TASK(charge_fairy_squad_1, { int count; int step; int charge_time; } ) {
// squad of small fairies that fire tight groups of "charged shots"
// and then fly off screen in random directions
for(int x = 0; x < ARGS.count; ++x) {
int i = x % 4;
double span = 300 - 60 * (i/2);
cmplx pos = VIEWPORT_W/2 + span * (-0.5 + (i & 1)) + (VIEWPORT_H/3 + 100*(i / 2)) * I;
cmplx exitdir = pos - (VIEWPORT_W+VIEWPORT_H * I) / 2;
exitdir /= cabs(exitdir);
INVOKE_TASK(charge_fairy,
.pos = pos,
.target_pos = 0,
.exit_dir = exitdir,
.charge_time = ARGS.charge_time,
.move_first = 0
);
WAIT(ARGS.step);
}
}
TASK(charge_fairy_squad_2, { int count; cmplx start_pos; cmplx target_pos; cmplx exit_dir; int delay; int charge_time; } ) {
for(int x = 0; x < ARGS.count; ++x) {
INVOKE_TASK(charge_fairy, ARGS.start_pos, ARGS.target_pos, ARGS.exit_dir, ARGS.charge_time, 1);
WAIT(ARGS.delay);
}
}
TASK(corner_fairy, { cmplx pos; cmplx p1; cmplx p2; int type; } ) {
Enemy *e = TASK_BIND(espawn_fairy_red(ARGS.pos, ITEMS(
.power = 2,
)));
e->alpha = 0;
INVOKE_TASK_DELAYED(240, destroy_enemy, ENT_BOX(e));
e->move = move_towards(ARGS.p1, 0.02);
WAIT(100);
int intensity = difficulty_value(7, 14, 21, 28);
for(int x = 0; x < 100; ++x) {
int momentum = 140 + x;
e->move.attraction_point = ARGS.p2;
e->move.attraction = 0.025 * fmin((20 + x) / 42.0, 1);
int d = 5;
if(!(momentum % d)) {
for(int i = 0; i < intensity; ++i) {
float c = psin(momentum / 15.0);
bool wave = global.diff > D_Easy && ARGS.type;
PROJECTILE(
.proto = wave ? pp_wave : pp_thickrice,
.pos = e->pos,
.color = ARGS.type ? RGB(0.5 - c*0.2, 0.3 + c*0.7, 1.0) : RGB(1.0 - c*0.5, 0.6, 0.5 + c*0.5),
.move = move_asymptotic_simple(
((1.8 - 0.4 * wave * !!ARGS.p2) * cdir((2 * i * M_PI / intensity) + carg((VIEWPORT_W + I * VIEWPORT_H)/2 - e->pos))),
1.5
)
);
play_sfx("shot1_loop");
WAIT(1);
}
}
}
}
TASK(corner_fairies, NO_ARGS) {
double offs = -50;
cmplx p1 = 0+0*I;
cmplx p2 = VIEWPORT_W+0*I;
cmplx p3 = VIEWPORT_W+VIEWPORT_H*I;
cmplx p4 = 0+VIEWPORT_H*I;
INVOKE_TASK(corner_fairy, p1, p1 - offs - offs*I, p2 + offs - offs*I);
INVOKE_TASK(corner_fairy, p2, p2 + offs - offs*I, p3 + offs + offs*I);
INVOKE_TASK(corner_fairy, p3, p3 + offs + offs*I, p4 - offs + offs*I);
INVOKE_TASK(corner_fairy, p4, p4 - offs + offs*I, p1 - offs - offs*I);
WAIT(90);
if(global.diff > D_Normal) {
INVOKE_TASK(corner_fairy, p1, p1 - offs*I, p2 + offs);
INVOKE_TASK(corner_fairy, p2, p2 + offs, p3 + offs*I);
INVOKE_TASK(corner_fairy, p3, p3 + offs*I, p4 - offs);
INVOKE_TASK(corner_fairy, p4, p4 - offs, p1 + offs*I);
}
}
}
// bosses
@ -539,14 +162,13 @@ TASK_WITH_INTERFACE(midboss_outro, BossAttack) {
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
BEGIN_BOSS_ATTACK(&ARGS);
// TODO: this doesn't match the movement in the original
// but I have no clue how to replicate it cleanly...
// original:
// boss->pos += pow(max(0, time)/30.0, 2) * cexp(I*(3*M_PI/2 + 0.5 * sin(time / 20.0)));
boss->move = move_towards(VIEWPORT_W/2 - 200.0*I, 0.05);
for(int t = 0;; t++) {
boss->pos += t*t/900.0 * cdir(3*M_PI/2 + 0.5 * sin(t / 20.0));
YIELD;
}
}
TASK(spawn_midboss, NO_ARGS) {
TASK(spawn_midboss) {
STAGE_BOOKMARK_DELAYED(120, midboss);
Boss *boss = global.boss = stage3_spawn_scuttle(VIEWPORT_W/2 - 200.0*I);
@ -564,7 +186,7 @@ TASK(boss_appear, { BoxedBoss boss; }) {
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.05);
}
TASK(spawn_boss, NO_ARGS) {
TASK(spawn_boss) {
STAGE_BOOKMARK_DELAYED(120, boss);
Boss *boss = global.boss = stage3_spawn_wriggle(VIEWPORT_W/2 - 200.0*I);
@ -591,94 +213,23 @@ DEFINE_EXTERN_TASK(stage3_timeline) {
stage_start_bgm("stage3");
stage_set_voltage_thresholds(50, 125, 300, 600);
// 14 swirls that die in an explosion after a second
INVOKE_TASK_DELAYED(200, burst_swirls,
.count = 14,
.interval = 10,
.shot_proj = pp_rice
INVOKE_TASK(flower_swirl_spawn,
.pos = VIEWPORT_W + 10 + 200 * I,
.dir = -3 - 1 * I,
.count = 10,
.interval = 30,
.spread = 10 + 200*I
);
// big fairy with 4-8 sub-fairies
INVOKE_TASK_DELAYED(360, big_fairy_group,
.pos = VIEWPORT_W/2 + (VIEWPORT_H/3)*I,
.shot_type = 1
INVOKE_TASK_DELAYED(100, flower_swirl_spawn,
.pos = -10 + 200 * I,
.dir = 3 - 1 * I,
.count = 8,
.interval = 30,
.spread = 10 + 150*I
);
INVOKE_TASK_DELAYED(400, swarm_trail_fairy_spawn, 5);
// line of swirls that fly in across the screen in a shifting arc
INVOKE_TASK_DELAYED(600, side_swirls_procession,
.interval = 20,
.count = 6,
.move = move_asymptotic(5*I, 5, 0.95),
.start_pos = -20 + 20*I
);
INVOKE_TASK_DELAYED(800, side_swirls_procession,
.interval = 20,
.count = 6,
.move = move_asymptotic(1*I, 5, 0.95),
.start_pos = -20 + 20*I
);
INVOKE_TASK_DELAYED(820, side_swirls_procession,
.interval = 20,
.count = 6,
.move = move_asymptotic(1*I, -5, 0.95),
.start_pos = VIEWPORT_W+20 + 20*I
);
INVOKE_TASK_DELAYED(1030, burst_fairy_squad,
.count = 4,
.step = 20
);
INVOKE_TASK_DELAYED(1300, charge_fairy_squad_1,
.count = 6,
.step = 60,
.charge_time = 30
);
INVOKE_TASK_DELAYED(1530, side_swirls_procession,
.interval = 20,
.count = 5,
.move = move_linear(5*I),
.start_pos = 20 - 20*I
);
INVOKE_TASK_DELAYED(1575, side_swirls_procession,
.interval = 20,
.count = 5,
.move = move_linear(5*I),
.start_pos = VIEWPORT_W-20 - 20*I
);
INVOKE_TASK_DELAYED(1600, big_fairy_group,
.pos = VIEWPORT_W/2 + (VIEWPORT_H/3)*I,
.shot_type = 1
);
INVOKE_TASK_DELAYED(1800, little_fairy_line,
.count = 3
);
INVOKE_TASK_DELAYED(2000, charge_fairy_squad_2,
.count = 3,
.start_pos = -20 + (VIEWPORT_H+20)*I,
.target_pos = 30 + VIEWPORT_H/2.0*I,
.exit_dir = -1*I,
.delay = 60,
.charge_time = 30
);
INVOKE_TASK_DELAYED(2000, charge_fairy_squad_2,
.count = 3,
.start_pos = VIEWPORT_W+20 + (VIEWPORT_H+20)*I,
.target_pos = VIEWPORT_W-30 + VIEWPORT_H/2.0*I,
.exit_dir = -1*I,
.delay = 60,
.charge_time = 30
);
INVOKE_TASK_DELAYED(2400, corner_fairies);
STAGE_BOOKMARK_DELAYED(2500, pre-midboss);
@ -691,76 +242,8 @@ DEFINE_EXTERN_TASK(stage3_timeline) {
STAGE_BOOKMARK(post-midboss);
INVOKE_TASK_DELAYED(100, side_swirls_procession,
.interval = 20,
.count = 10,
.move = move_asymptotic(-25*I, 5, 0.95),
.start_pos = -20 + (VIEWPORT_H-20)*I
);
INVOKE_TASK_DELAYED(105, side_swirls_procession,
.interval = 20,
.count = 10,
.move = move_asymptotic(-25*I, -5, 0.95),
.start_pos = VIEWPORT_W+20 + (VIEWPORT_H-20)*I
);
INVOKE_TASK_DELAYED(250, burst_swirls,
.count = 14,
.interval = 10,
.shot_proj = pp_ball
);
INVOKE_TASK_DELAYED(400, big_fairy_group,
.pos = VIEWPORT_W - VIEWPORT_W/3 + (VIEWPORT_H/5)*I,
.shot_type = 2
);
INVOKE_TASK_DELAYED(500, side_swirls_procession,
.interval = 20,
.count = 10,
.move = move_linear(-5*I),
.start_pos = VIEWPORT_W-20 + (VIEWPORT_H+20)*I
);
INVOKE_TASK_DELAYED(1000, big_fairy_group,
.pos = VIEWPORT_W/3 + (VIEWPORT_H/5)*I,
.shot_type = 2
);
INVOKE_TASK_DELAYED(1200, charge_fairy_squad_1,
.count = 4,
.step = 60,
.charge_time = 30
);
INVOKE_TASK_DELAYED(1500, burst_fairy_squad,
.count = 4,
.step = 70
);
INVOKE_TASK_DELAYED(1700, side_swirls_procession,
.interval = 20,
.count = 10,
.move = move_linear(-5*I),
.start_pos = 20 + (VIEWPORT_H+20)*I
);
INVOKE_TASK_DELAYED(2200, corner_fairies);
INVOKE_TASK_DELAYED(2300, side_swirls_procession,
.interval = 20,
.count = 10,
.move = move_linear(5*I),
.start_pos = VIEWPORT_W-20 - 20.0*I
);
INVOKE_TASK_DELAYED(2600, side_swirls_procession,
.interval = 20,
.count = 10,
.move = move_linear(5*I),
.start_pos = 20 + -20.0*I
);
STAGE_BOOKMARK_DELAYED(2800, pre-boss);

View file

@ -11,4 +11,4 @@
#include "coroutine.h"
DECLARE_EXTERN_TASK(stage3_timeline, NO_ARGS);
DECLARE_EXTERN_TASK(stage3_timeline);