800 lines
21 KiB
C
800 lines
21 KiB
C
/*
|
|
* This software is licensed under the terms of the MIT License.
|
|
* See COPYING for further information.
|
|
* ---
|
|
* Copyright (c) 2011-2024, Lukas Weber <laochailan@web.de>.
|
|
* Copyright (c) 2012-2024, Andrei Alexeyev <akari@taisei-project.org>.
|
|
*/
|
|
|
|
#include "timeline.h" // IWYU pragma: keep
|
|
|
|
#include "background_anim.h"
|
|
#include "cirno.h"
|
|
#include "nonspells/nonspells.h" // IWYU pragma: keep
|
|
#include "spells/spells.h" // IWYU pragma: keep
|
|
#include "stage1.h"
|
|
|
|
#include "stages/common_imports.h"
|
|
|
|
TASK(burst_fairy, { BoxedEnemy e; cmplx target_pos; cmplx exit_dir; }) {
|
|
Enemy *e = TASK_BIND(ARGS.e);
|
|
e->move = move_from_towards(e->pos, ARGS.target_pos, 0.03);
|
|
|
|
WAIT(difficulty_value(120, 80, 60, 60));
|
|
|
|
play_sfx("shot1");
|
|
int n = difficulty_value(0, 1, 3, 5);
|
|
|
|
for(int i = -n; i <= n; i++) {
|
|
cmplx aim = cdir(carg(global.plr.pos - e->pos) + 0.2 * i);
|
|
|
|
PROJECTILE(
|
|
.proto = pp_crystal,
|
|
.pos = e->pos,
|
|
.color = RGB(0.2, 0.3, 0.5),
|
|
.move = move_asymptotic_simple(aim * (2 + 0.5 * global.diff), 5),
|
|
);
|
|
}
|
|
|
|
WAIT(1);
|
|
|
|
e->move.attraction = 0;
|
|
e->move.acceleration = 0.04 * ARGS.exit_dir;
|
|
e->move.retention = 1;
|
|
}
|
|
|
|
TASK(circletoss_shoot_circle, { BoxedEnemy e; int duration; int interval; }) {
|
|
Enemy *e = TASK_BIND(ARGS.e);
|
|
|
|
int cnt = ARGS.duration / ARGS.interval;
|
|
double angle_step = M_TAU / cnt;
|
|
|
|
for(int i = 0; i < cnt; ++i) {
|
|
play_sfx_loop("shot1_loop");
|
|
e->move.velocity *= 0.8;
|
|
|
|
cmplx aim = cdir(angle_step * i);
|
|
|
|
PROJECTILE(
|
|
.proto = pp_rice,
|
|
.pos = e->pos,
|
|
.color = RGB(0.6, 0.2, 0.7),
|
|
.move = move_asymptotic_simple(2 * aim, i * 0.5),
|
|
);
|
|
|
|
WAIT(ARGS.interval);
|
|
}
|
|
}
|
|
|
|
TASK(circletoss_shoot_toss, { BoxedEnemy e; int times; int duration; int period; }) {
|
|
Enemy *e = TASK_BIND(ARGS.e);
|
|
|
|
real spread = difficulty_value(0.05, 0.1, 0.15, 0.2);
|
|
|
|
while(ARGS.times--) {
|
|
for(int i = ARGS.duration; i--;) {
|
|
play_sfx_loop("shot1_loop");
|
|
|
|
real aim_angle = carg(global.plr.pos - e->pos) + spread * 0.5 * rng_sreal();
|
|
cmplx aim = cdir(aim_angle) * rng_range(1, 3);
|
|
|
|
PROJECTILE(
|
|
.proto = pp_thickrice,
|
|
.pos = e->pos,
|
|
.color = RGB(0.2, 0.4, 0.8),
|
|
.move = move_asymptotic_simple(aim, 3),
|
|
);
|
|
|
|
WAIT(1);
|
|
}
|
|
|
|
WAIT(ARGS.period - ARGS.duration);
|
|
}
|
|
}
|
|
|
|
TASK(circletoss_fairy, { cmplx pos; cmplx velocity; cmplx exit_accel; int exit_time; }) {
|
|
Enemy *e = TASK_BIND(espawn_big_fairy(ARGS.pos, ITEMS(.points = 3, .power = 1)));
|
|
e->move = move_linear(ARGS.velocity);
|
|
|
|
ARGS.exit_time -= WAIT(difficulty_value(80, 80, 60, 60));
|
|
|
|
INVOKE_SUBTASK(circletoss_shoot_circle, ENT_BOX(e),
|
|
.duration = 40,
|
|
.interval = difficulty_value(3, 3, 2, 2)
|
|
);
|
|
|
|
if(global.diff > D_Easy) {
|
|
INVOKE_SUBTASK_DELAYED(30, circletoss_shoot_toss, ENT_BOX(e),
|
|
.times = 4,
|
|
.period = 150,
|
|
.duration = difficulty_value(-1, 15, 26, 33)
|
|
);
|
|
}
|
|
|
|
WAIT(ARGS.exit_time);
|
|
e->move.acceleration += ARGS.exit_accel;
|
|
STALL;
|
|
}
|
|
|
|
TASK(sinepass_swirl_move, { BoxedEnemy e; cmplx v; cmplx sv; }) {
|
|
Enemy *e = TASK_BIND(ARGS.e);
|
|
cmplx sv = ARGS.sv;
|
|
cmplx v = ARGS.v;
|
|
|
|
for(;;) {
|
|
sv -= im(e->pos - e->pos0) * 0.03 * I;
|
|
e->pos += sv * 0.4 + v;
|
|
YIELD;
|
|
}
|
|
}
|
|
|
|
TASK(sinepass_swirl, { cmplx pos; cmplx vel; cmplx svel; }) {
|
|
Enemy *e = TASK_BIND(espawn_swirl(ARGS.pos, ITEMS(.points = 1)));
|
|
INVOKE_TASK(sinepass_swirl_move, ENT_BOX(e), ARGS.vel, ARGS.svel);
|
|
|
|
WAIT(difficulty_value(40, 40, 15, 10));
|
|
|
|
int shot_interval = difficulty_value(155, 80, 30, 20);
|
|
|
|
for(;;) {
|
|
play_sfx("shot1");
|
|
|
|
cmplx aim = cnormalize(global.plr.pos - e->pos);
|
|
aim *= difficulty_value(2, 2, 2.5, 3);
|
|
|
|
PROJECTILE(
|
|
.proto = pp_ball,
|
|
.pos = e->pos,
|
|
.color = RGB(0.8, 0.8, 0.4),
|
|
.move = move_asymptotic_simple(aim, 5),
|
|
);
|
|
|
|
WAIT(shot_interval);
|
|
}
|
|
}
|
|
|
|
TASK(circle_fairy, { cmplx pos; cmplx target_pos; }) {
|
|
Enemy *e = TASK_BIND(espawn_fairy_red(ARGS.pos, ITEMS(.power = 2)));
|
|
|
|
e->move = move_from_towards(e->pos, ARGS.target_pos, 0.005);
|
|
e->move.retention = 0.8;
|
|
|
|
WAIT(60);
|
|
|
|
int shot_interval = 2;
|
|
int shot_count = difficulty_value(10, 10, 20, 25);
|
|
int round_interval = 120 - shot_interval * shot_count;
|
|
|
|
for(int round = 0; round < 2; ++round) {
|
|
double a_ofs = rng_angle();
|
|
|
|
for(int i = 0; i < shot_count; ++i) {
|
|
cmplx aim;
|
|
|
|
aim = circle_dir_ofs((round & 1) ? i : shot_count - i, shot_count, a_ofs);
|
|
aim *= difficulty_value(1.7, 2.0, 2.5, 2.5);
|
|
|
|
PROJECTILE(
|
|
.proto = pp_rice,
|
|
.pos = e->pos,
|
|
.color = RGB(0.6, 0.2, 0.7),
|
|
.move = move_asymptotic_simple(aim, i * 0.5),
|
|
);
|
|
|
|
play_sfx_loop("shot1_loop");
|
|
WAIT(shot_interval);
|
|
}
|
|
|
|
e->move.attraction_point += 30 * rng_dir();
|
|
WAIT(round_interval);
|
|
}
|
|
|
|
WAIT(10);
|
|
e->move.attraction = 0;
|
|
e->move.retention = 1;
|
|
e->move.acceleration = -0.04 * I * cdir(rng_range(0, M_TAU / 12));
|
|
STALL;
|
|
}
|
|
|
|
TASK(drop_swirl, { cmplx pos; cmplx vel; cmplx accel; }) {
|
|
Enemy *e = TASK_BIND(espawn_swirl(ARGS.pos, ITEMS(.points = 2)));
|
|
e->move = move_accelerated(ARGS.vel, ARGS.accel);
|
|
|
|
int shot_interval = difficulty_value(120, 120, 30, 20);
|
|
|
|
WAIT(difficulty_value(50, 35, 20, 20));
|
|
|
|
while(true) {
|
|
cmplx aim = cnormalize(global.plr.pos - e->pos);
|
|
aim *= 1 + 0.3 * global.diff + rng_real();
|
|
|
|
play_sfx("shot1");
|
|
PROJECTILE(
|
|
.proto = pp_ball,
|
|
.pos = e->pos,
|
|
.color = RGB(0.8, 0.8, 0.4),
|
|
.move = move_linear(aim),
|
|
);
|
|
|
|
WAIT(shot_interval);
|
|
}
|
|
}
|
|
|
|
TASK(multiburst_fairy, { BoxedEnemy e; cmplx target_pos; cmplx exit_accel; }) {
|
|
Enemy *e = TASK_BIND(ARGS.e);
|
|
e->move = move_from_towards(e->pos, ARGS.target_pos, 0.05);
|
|
|
|
WAIT(difficulty_value(120, 60, 60, 60));
|
|
|
|
int burst_interval = difficulty_value(40, 30, 18, 16);
|
|
int bursts = 4;
|
|
|
|
for(int i = 0; i < bursts; ++i) {
|
|
play_sfx("shot1");
|
|
int n = difficulty_value(0, 0, 2, 3);
|
|
|
|
for(int j = -n; j <= n; j++) {
|
|
cmplx aim = cdir(carg(global.plr.pos - e->pos) + j / 5.0);
|
|
aim *= 2.5;
|
|
|
|
PROJECTILE(
|
|
.proto = pp_crystal,
|
|
.pos = e->pos,
|
|
.color = RGB(0.2, 0.3, 0.5),
|
|
.move = move_linear(aim),
|
|
);
|
|
}
|
|
|
|
WAIT(burst_interval);
|
|
}
|
|
|
|
WAIT(10);
|
|
e->move.attraction = 0;
|
|
e->move.retention = 1;
|
|
e->move.acceleration = ARGS.exit_accel;
|
|
}
|
|
|
|
TASK(instantcircle_fairy_shoot, { BoxedEnemy e; int cnt; double speed; double boost; }) {
|
|
Enemy *e = TASK_BIND(ARGS.e);
|
|
play_sfx("shot_special1");
|
|
|
|
for(int i = 0; i < ARGS.cnt; ++i) {
|
|
cmplx vel = ARGS.speed * circle_dir(i, ARGS.cnt);
|
|
|
|
PROJECTILE(
|
|
.proto = pp_rice,
|
|
.pos = e->pos,
|
|
.color = RGB(0.6, 0.2, 0.7),
|
|
.move = move_asymptotic_simple(vel, ARGS.boost),
|
|
);
|
|
}
|
|
}
|
|
|
|
TASK(instantcircle_fairy, { cmplx pos; cmplx target_pos; cmplx exit_accel; }) {
|
|
Enemy *e = TASK_BIND(espawn_big_fairy(ARGS.pos, ITEMS(.points = 4, .power = 2)));
|
|
e->move = move_from_towards(e->pos, ARGS.target_pos, 0.04);
|
|
BoxedEnemy be = ENT_BOX(e);
|
|
|
|
INVOKE_TASK_DELAYED(75, instantcircle_fairy_shoot, be,
|
|
.cnt = difficulty_value(20, 22, 24, 28),
|
|
.speed = 1.5,
|
|
.boost = 2.0
|
|
);
|
|
|
|
if(global.diff > D_Easy) {
|
|
INVOKE_TASK_DELAYED(95, instantcircle_fairy_shoot, be,
|
|
.cnt = difficulty_value(0, 26, 29, 32),
|
|
.speed = difficulty_value(0, 2.0, 3.0, 3.0),
|
|
.boost = 3.0
|
|
);
|
|
}
|
|
|
|
WAIT(200);
|
|
e->move.attraction = 0;
|
|
e->move.retention = 1;
|
|
e->move.acceleration = ARGS.exit_accel;
|
|
}
|
|
|
|
TASK(waveshot, { cmplx pos; cmplx dir; real spread; real freq; int shots; int interval; } ) {
|
|
for(int i = 0; i < ARGS.shots; ++i) {
|
|
cmplx v = 4 * ARGS.dir * cdir(ARGS.spread * triangle(ARGS.freq * i));
|
|
|
|
play_sfx_loop("shot1_loop");
|
|
PROJECTILE(
|
|
.proto = pp_thickrice,
|
|
.pos = ARGS.pos,
|
|
.color = RGBA(0.0, 0.5 * (1.0 - i / (ARGS.shots - 1.0)), 1.0, 1),
|
|
.move = move_asymptotic(-4 * v, v, 0.9),
|
|
// .move = move_accelerated(-v, 0.02 * v),
|
|
);
|
|
|
|
WAIT(ARGS.interval);
|
|
}
|
|
}
|
|
|
|
TASK(waveshot_fairy, { cmplx pos; cmplx exit_accel; }) {
|
|
Enemy *e = TASK_BIND(espawn_big_fairy(ARGS.pos, ITEMS(.points = 4, .power = 2)));
|
|
ecls_anyfairy_summon(e, 120);
|
|
|
|
cmplx dir = cnormalize(global.plr.pos - e->pos);
|
|
cmplx ofs = -24 * dir;
|
|
|
|
common_charge(60, &e->pos, ofs, RGBA(0.0, 0.25, 0.5, 0));
|
|
|
|
real spread = difficulty_value(M_PI/20, M_PI/18, M_PI/16, M_PI/14);
|
|
real interval = difficulty_value(3, 2, 1, 1);
|
|
real shots = 60 / interval;
|
|
real frequency = 60 * (1.0/12.0) / shots;
|
|
shots += 1;
|
|
|
|
INVOKE_SUBTASK(waveshot, e->pos + ofs, dir, rng_sign() * spread, frequency, shots, interval);
|
|
|
|
WAIT(120);
|
|
|
|
e->move.attraction = 0;
|
|
e->move.retention = 0.8;
|
|
e->move.acceleration = ARGS.exit_accel;
|
|
}
|
|
|
|
TASK(explosion_fairy, { cmplx pos; cmplx exit_accel; }) {
|
|
Enemy *e = TASK_BIND(espawn_huge_fairy(ARGS.pos, ITEMS(.points = 8)));
|
|
INVOKE_SUBTASK_DELAYED(80, common_charge, {
|
|
.time = 120,
|
|
.pos = e->pos,
|
|
.color = RGBA(1.0, 0, 0.2, 0),
|
|
.sound = COMMON_CHARGE_SOUNDS,
|
|
});
|
|
ecls_anyfairy_summon(e, 120);
|
|
|
|
WAIT(80);
|
|
|
|
int cnt = difficulty_value(30, 30, 60, 60);
|
|
int trails = difficulty_value(0, 2, 3, 4);
|
|
real speed = difficulty_value(2, 2, 4, 4);
|
|
real ofs = rng_angle();
|
|
|
|
play_sfx("shot_special1");
|
|
|
|
for(int i = 0; i < cnt; ++i) {
|
|
cmplx aim = speed * circle_dir_ofs(i, cnt, ofs);
|
|
real s = 0.5 + 0.5 * triangle(0.1 * i);
|
|
|
|
Color clr;
|
|
|
|
if(s == 1) {
|
|
clr = *RGB(1, 0, 0);
|
|
} else {
|
|
clr = *color_lerp(
|
|
RGB(0.1, 0.6, 1.0),
|
|
RGB(1.0, 0.0, 0.3),
|
|
s * s
|
|
);
|
|
color_mul(&clr, &clr);
|
|
}
|
|
|
|
PROJECTILE(
|
|
.proto = s == 1 ? pp_bigball : pp_ball,
|
|
.pos = e->pos,
|
|
.color = &clr,
|
|
.move = move_asymptotic_simple(aim, 1 + 8 * s),
|
|
);
|
|
|
|
for(int j = 0; j < trails; ++j) {
|
|
aim *= 0.8;
|
|
PROJECTILE(
|
|
.proto = pp_rice,
|
|
.pos = e->pos,
|
|
.color = &clr,
|
|
.move = move_asymptotic_simple(aim, 1 + 8 * s),
|
|
);
|
|
}
|
|
}
|
|
|
|
WAIT(10);
|
|
|
|
e->move.attraction = 0;
|
|
e->move.retention = 0.8;
|
|
e->move.acceleration = ARGS.exit_accel;
|
|
}
|
|
|
|
// opening. projectile bursts
|
|
TASK(burst_fairies_1) {
|
|
for(int i = 3; i--;) {
|
|
INVOKE_TASK(burst_fairy,
|
|
.e = espawn_fairy_blue_box(VIEWPORT_W/2 + 70, ITEMS(.points = 1)),
|
|
.target_pos = VIEWPORT_W/2 + 70 + (120 + 42 * i)*I,
|
|
.exit_dir = 1 + 0.6*I
|
|
);
|
|
INVOKE_TASK(burst_fairy,
|
|
.e = espawn_fairy_red_box(VIEWPORT_W/2 - 70, ITEMS(.power = 1)),
|
|
.target_pos = VIEWPORT_W/2 - 70 + (120 + 42 * i)*I,
|
|
.exit_dir = -1 + 0.6*I
|
|
);
|
|
WAIT(25);
|
|
}
|
|
}
|
|
|
|
// more bursts. fairies move \ / like
|
|
TASK(burst_fairies_2) {
|
|
for(int i = 3; i--;) {
|
|
real ofs = 70 + i * 40;
|
|
|
|
EnemySpawner s0, s1;
|
|
ItemCounts i0, i1;
|
|
|
|
if(i & 1) {
|
|
s0 = espawn_fairy_red;
|
|
i0 = *ITEMS(.power = 1);
|
|
s1 = espawn_fairy_blue;
|
|
i1 = *ITEMS(.points = 1);
|
|
} else {
|
|
s0 = espawn_fairy_blue;
|
|
i0 = *ITEMS(.points = 1);
|
|
s1 = espawn_fairy_red;
|
|
i1 = *ITEMS(.power = 1);
|
|
}
|
|
|
|
cmplx p0 = ofs;
|
|
cmplx p1 = VIEWPORT_W - ofs;
|
|
|
|
INVOKE_TASK(burst_fairy, ENT_BOX(s0(p0, &i0)), p0 + 120*I, 0.6*I + 1);
|
|
WAIT(15);
|
|
INVOKE_TASK(burst_fairy, ENT_BOX(s1(p1, &i1)), p1 + 120*I, 0.6*I - 1);
|
|
WAIT(15);
|
|
}
|
|
}
|
|
|
|
TASK(burst_fairies_3) {
|
|
for(int i = 10; i--;) {
|
|
cmplx pos = VIEWPORT_W/2 - 200 * sin(1.17 * global.frames);
|
|
INVOKE_TASK(burst_fairy,
|
|
(i & 1)
|
|
? espawn_fairy_blue_box(pos, ITEMS(.points = 1))
|
|
: espawn_fairy_red_box(pos, ITEMS(.power = 1)),
|
|
pos + 120*I, rng_sign()
|
|
);
|
|
WAIT(60);
|
|
}
|
|
}
|
|
|
|
// swirl, sine pass
|
|
TASK(sinepass_swirls, { int duration; real level; real dir; }) {
|
|
int duration = ARGS.duration;
|
|
real dir = ARGS.dir;
|
|
cmplx pos = CMPLX(ARGS.dir < 0 ? VIEWPORT_W : 0, ARGS.level);
|
|
int delay = difficulty_value(30, 30, 15, 10);
|
|
|
|
for(int t = 0; t < duration; t += delay) {
|
|
INVOKE_TASK(sinepass_swirl, pos, 3.5 * dir, 7.0 * I);
|
|
WAIT(delay);
|
|
}
|
|
}
|
|
|
|
// big fairies, circle + projectile toss
|
|
TASK(circletoss_fairies_1) {
|
|
for(int i = 0; i < 2; ++i) {
|
|
INVOKE_TASK(circletoss_fairy,
|
|
.pos = VIEWPORT_W * i + VIEWPORT_H / 3 * I,
|
|
.velocity = 2 - 4 * i - 0.3 * I,
|
|
.exit_accel = 0.03 * (1 - 2 * i) - 0.04 * I ,
|
|
.exit_time = (global.diff > D_Easy) ? 500 : 240
|
|
);
|
|
|
|
WAIT(250);
|
|
}
|
|
}
|
|
|
|
TASK(drop_swirls, { int cnt; cmplx pos; cmplx vel; cmplx accel; }) {
|
|
for(int i = 0; i < ARGS.cnt; ++i) {
|
|
INVOKE_TASK(drop_swirl, ARGS.pos, ARGS.vel, ARGS.accel);
|
|
WAIT(20);
|
|
}
|
|
}
|
|
|
|
TASK(schedule_swirls) {
|
|
INVOKE_TASK(drop_swirls, 25, VIEWPORT_W/3, 2*I, 0.06);
|
|
WAIT(400);
|
|
INVOKE_TASK(drop_swirls, 25, 200*I, 4, -0.06*I);
|
|
}
|
|
|
|
TASK(circle_fairies_1) {
|
|
for(int i = 0; i < 3; ++i) {
|
|
for(int j = 0; j < 3; ++j) {
|
|
INVOKE_TASK(circle_fairy, VIEWPORT_W - 64, VIEWPORT_W/2 - 100 + 200 * I + 128 * j);
|
|
WAIT(60);
|
|
}
|
|
|
|
WAIT(90);
|
|
|
|
for(int j = 0; j < 3; ++j) {
|
|
INVOKE_TASK(circle_fairy, 64, VIEWPORT_W/2 + 100 + 200 * I - 128 * j);
|
|
WAIT(60);
|
|
}
|
|
|
|
WAIT(240);
|
|
}
|
|
}
|
|
|
|
TASK(multiburst_fairies_1) {
|
|
int rows = difficulty_value(1, 2, 3, 3);
|
|
int row_interval = difficulty_value(0, 160, 120, 120);
|
|
int cols = 5;
|
|
int col_interval = difficulty_value(30, 10, 10, 10);
|
|
|
|
for(int row = 0; row < rows; ++row) {
|
|
for(int col = 0; col < cols; ++col) {
|
|
cmplx pos = rng_range(0, VIEWPORT_W);
|
|
cmplx target_pos = 64 + 64 * col + I * (64 * row + 100);
|
|
cmplx exit_accel = 0.02 * I + 0.03;
|
|
Enemy *e;
|
|
|
|
if(row & 1) {
|
|
e = espawn_fairy_red(pos, ITEMS(.power = 2));
|
|
} else {
|
|
e = espawn_fairy_blue(pos, ITEMS(.points = 3));
|
|
}
|
|
|
|
INVOKE_TASK(multiburst_fairy, ENT_BOX(e), target_pos, exit_accel);
|
|
|
|
WAIT(col_interval);
|
|
}
|
|
|
|
WAIT(row_interval);
|
|
}
|
|
}
|
|
|
|
TASK(instantcircle_fairies, { int duration; int seq_ofs; }) {
|
|
int interval = difficulty_value(160, 130, 100, 70);
|
|
|
|
real xofs[] = { 0.32, -0.85, 0.93, -0.42, -0.9 };
|
|
real yofs[] = { -0.69, -0.93, 0.80, 0.21, -0.96, -0.53, 0.95 };
|
|
|
|
for(int t = ARGS.duration, i = ARGS.seq_ofs; t > 0; t -= interval, ++i) {
|
|
log_debug("i = %i; x=%f, y=%f", i, xofs[i % ARRAY_SIZE(xofs)], yofs[i % ARRAY_SIZE(yofs)]);
|
|
|
|
real x = VIEWPORT_W/2 + 205 * xofs[i % ARRAY_SIZE(xofs)];
|
|
real y = VIEWPORT_H/2 + 100 * yofs[i % ARRAY_SIZE(yofs)];
|
|
INVOKE_TASK(instantcircle_fairy, x, x+y*I, 0.2 * I);
|
|
WAIT(interval);
|
|
}
|
|
}
|
|
|
|
TASK(waveshot_fairies, { int duration; }) {
|
|
int interval = 200;
|
|
|
|
for(int t = ARGS.duration; t > 0; t -= interval) {
|
|
real x = VIEWPORT_W/2 + round(rng_sreal() * 69);
|
|
real y = rng_range(200, 240);
|
|
INVOKE_TASK(waveshot_fairy, x+y*I, 0.15 * I);
|
|
WAIT(interval);
|
|
}
|
|
}
|
|
|
|
TASK_WITH_INTERFACE(midboss_intro, BossAttack) {
|
|
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
|
BEGIN_BOSS_ATTACK(&ARGS);
|
|
boss->move = move_from_towards(boss->pos, VIEWPORT_W/2.0 + 200.0*I, 0.035);
|
|
}
|
|
|
|
TASK_WITH_INTERFACE(midboss_flee, BossAttack) {
|
|
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
|
BEGIN_BOSS_ATTACK(&ARGS);
|
|
boss->move = move_from_towards(boss->pos, -250 + 30 * I, 0.02);
|
|
}
|
|
|
|
TASK(spawn_midboss) {
|
|
STAGE_BOOKMARK_DELAYED(120, midboss);
|
|
|
|
Boss *boss = global.boss = stage1_spawn_cirno(VIEWPORT_W + 220 + 30.0*I);
|
|
|
|
boss_add_attack_task(boss, AT_Move, "Introduction", 2, 0, TASK_INDIRECT(BossAttack, midboss_intro), NULL);
|
|
boss_add_attack_task(boss, AT_Normal, "Icy Storm", 20, 24000, TASK_INDIRECT(BossAttack, stage1_midboss_nonspell_1), NULL);
|
|
if(global.diff > D_Normal) {
|
|
boss_add_attack_from_info(boss, &stage1_spells.mid.perfect_freeze, false);
|
|
}
|
|
boss_add_attack_task(boss, AT_Move, "Flee", 2, 0, TASK_INDIRECT(BossAttack, midboss_flee), NULL);
|
|
|
|
boss_engage(boss);
|
|
|
|
WAIT(60);
|
|
stage1_bg_enable_snow();
|
|
}
|
|
|
|
TASK(tritoss_fairy, { cmplx pos; cmplx end_velocity; }) {
|
|
Enemy *e = TASK_BIND(espawn_super_fairy(ARGS.pos, ITEMS(.points = 5, .power = 6)));
|
|
INVOKE_SUBTASK_DELAYED(120, common_charge, {
|
|
.pos = e->pos,
|
|
.time = 60,
|
|
.color = RGBA(0.1, 0.2, 1.0, 0),
|
|
.sound = COMMON_CHARGE_SOUNDS,
|
|
});
|
|
ecls_anyfairy_summon(e, 180);
|
|
|
|
int interval = difficulty_value(12, 9, 5, 3);
|
|
int rounds = 680/interval;
|
|
for(int k = 0; k < rounds; k++) {
|
|
play_sfx("shot1");
|
|
|
|
float a = M_PI / 30.0 * ((k/7) % 30) + 0.1 * rng_f32();
|
|
int n = difficulty_value(3,4,4,5);
|
|
|
|
for(int i = 0; i < n; i++) {
|
|
PROJECTILE(
|
|
.proto = pp_thickrice,
|
|
.pos = e->pos,
|
|
.color = RGB(0.2, 0.4, 0.8),
|
|
.move = move_asymptotic_simple(2*cdir(a+2.0*M_PI/n*i), 3),
|
|
);
|
|
}
|
|
|
|
if(k == rounds/2 || k == rounds-1) {
|
|
play_sfx("shot_special1");
|
|
int n2 = difficulty_value(20, 23, 26, 30);
|
|
for(int i = 0; i < n2; i++) {
|
|
PROJECTILE(
|
|
.proto = pp_rice,
|
|
.pos = e->pos,
|
|
.color = RGB(0.6, 0.2, 0.7),
|
|
.move = move_asymptotic_simple(1.5*cdir(2*M_PI/n2*i),2),
|
|
);
|
|
|
|
if(global.diff > D_Easy) {
|
|
PROJECTILE(
|
|
.proto = pp_rice,
|
|
.pos = e->pos,
|
|
.color = RGB(0.6, 0.2, 0.7),
|
|
.move = move_asymptotic_simple(3*cdir(2*M_PI/n2*i), 3.0),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
WAIT(interval);
|
|
}
|
|
|
|
WAIT(60);
|
|
e->move = move_asymptotic_simple(ARGS.end_velocity, -1);
|
|
}
|
|
|
|
TASK(boss_appear, { BoxedBoss boss; }) {
|
|
Boss *boss = NOT_NULL(ENT_UNBOX(ARGS.boss));
|
|
boss->move = move_from_towards(boss->pos, VIEWPORT_W/2.0 + 100.0*I, 0.05);
|
|
}
|
|
|
|
TASK(spawn_boss) {
|
|
STAGE_BOOKMARK(boss);
|
|
stage_unlock_bgm("stage1");
|
|
|
|
Boss *boss = global.boss = stage1_spawn_cirno(-230 + 100.0*I);
|
|
|
|
PlayerMode *pm = global.plr.mode;
|
|
Stage1PreBossDialogEvents *e;
|
|
INVOKE_TASK_INDIRECT(Stage1PreBossDialog, pm->dialog->Stage1PreBoss, &e);
|
|
INVOKE_TASK_WHEN(&e->boss_appears, boss_appear, ENT_BOX(boss));
|
|
INVOKE_TASK_WHEN(&e->music_changes, common_start_bgm, "stage1boss");
|
|
WAIT_EVENT(&global.dialog->events.fadeout_began);
|
|
|
|
STAGE_BOOKMARK(boss-postdialog);
|
|
|
|
boss_add_attack_task(boss, AT_Normal, "Iceplosion 0", 20, 26000, TASK_INDIRECT(BossAttack, stage1_boss_nonspell_1), NULL);
|
|
boss_add_attack_from_info(boss, &stage1_spells.boss.crystal_rain, false);
|
|
boss_add_attack_task(boss, AT_Normal, "Iceplosion 1", 20, 30000, TASK_INDIRECT(BossAttack, stage1_boss_nonspell_2), NULL);
|
|
|
|
if(global.diff > D_Normal) {
|
|
boss_add_attack_from_info(boss, &stage1_spells.boss.snow_halation, false);
|
|
}
|
|
|
|
boss_add_attack_from_info(boss, &stage1_spells.boss.icicle_cascade, false);
|
|
boss_add_attack_from_info(boss, &stage1_spells.extra.crystal_blizzard, false);
|
|
|
|
boss_engage(boss);
|
|
}
|
|
|
|
static void stage1_dialog_post_boss(void) {
|
|
PlayerMode *pm = global.plr.mode;
|
|
INVOKE_TASK_INDIRECT(Stage1PostBossDialog, pm->dialog->Stage1PostBoss);
|
|
}
|
|
|
|
DEFINE_EXTERN_TASK(stage1_timeline) {
|
|
INVOKE_TASK_DELAYED(100, burst_fairies_1);
|
|
INVOKE_TASK_DELAYED(240, burst_fairies_2);
|
|
INVOKE_TASK_DELAYED(440, sinepass_swirls, 180, 100, 1);
|
|
INVOKE_TASK_DELAYED(480, circletoss_fairies_1);
|
|
INVOKE_TASK_DELAYED(660, circle_fairies_1);
|
|
INVOKE_TASK_DELAYED(900, schedule_swirls);
|
|
INVOKE_TASK_DELAYED(1500, burst_fairies_3);
|
|
INVOKE_TASK_DELAYED(2200, multiburst_fairies_1);
|
|
|
|
INVOKE_TASK_DELAYED(2200, common_call_func, stage1_bg_raise_camera);
|
|
STAGE_BOOKMARK_DELAYED(2500, pre-midboss);
|
|
INVOKE_TASK_DELAYED(2700, spawn_midboss);
|
|
|
|
while(!global.boss) YIELD;
|
|
int midboss_time = WAIT_EVENT(&global.boss->events.defeated).frames;
|
|
int filler_time = 2180;
|
|
int time_ofs = -midboss_time;
|
|
|
|
log_debug("midboss_time = %i; filler_time = %i; time_ofs = %i", midboss_time, filler_time, time_ofs);
|
|
|
|
STAGE_BOOKMARK(post-midboss);
|
|
|
|
int swirl_spam_time = 1260;
|
|
|
|
for(int i = 0; i < /*swirl_spam_time*/ filler_time; i += 30) {
|
|
int o = ((int[]) { 0, 1, 0, -1 })[(i / 60) % 4];
|
|
INVOKE_TASK_DELAYED(i + time_ofs, sinepass_swirls, 40, 132 + 32 * o, 1 - 2 * ((i / 60) & 1));
|
|
}
|
|
|
|
time_ofs += swirl_spam_time;
|
|
|
|
INVOKE_TASK_DELAYED(time_ofs, burst_fairies_1);
|
|
|
|
int instacircle_time = filler_time - swirl_spam_time - 100;
|
|
|
|
for(int t = 0, i = 1; t < instacircle_time; t += 180, ++i) {
|
|
// INVOKE_TASK_DELAYED(i + time_ofs, sinepass_swirls, 80, 132, 1);
|
|
INVOKE_TASK_DELAYED(120 + t + time_ofs, instantcircle_fairies, 120, i);
|
|
}
|
|
|
|
WAIT(filler_time - midboss_time);
|
|
STAGE_BOOKMARK(post-midboss-filler);
|
|
|
|
INVOKE_TASK_DELAYED(100, circletoss_fairy, -25 + VIEWPORT_H/3*I, 1 - 0.5*I, 0.01 * ( 1 - I), 200);
|
|
INVOKE_TASK_DELAYED(125, circletoss_fairy, VIEWPORT_W+25 + VIEWPORT_H/3*I, -1 - 0.5*I, 0.01 * (-1 - I), 200);
|
|
|
|
if(global.diff > D_Normal) {
|
|
INVOKE_TASK_DELAYED(115, circletoss_fairy, -25 + 2*VIEWPORT_H/3*I, 1 - 0.5*I, 0.01 * ( 1 - I), 200);
|
|
INVOKE_TASK_DELAYED(140, circletoss_fairy, VIEWPORT_W+25 + 2*VIEWPORT_H/3*I, -1 - 0.5*I, 0.01 * (-1 - I), 200);
|
|
}
|
|
|
|
STAGE_BOOKMARK_DELAYED(180, waveshot-fairies);
|
|
|
|
INVOKE_TASK_DELAYED(180, waveshot_fairies, 600);
|
|
INVOKE_TASK_DELAYED(400, burst_fairies_3);
|
|
INVOKE_TASK_DELAYED(430, burst_fairies_3);
|
|
|
|
STAGE_BOOKMARK_DELAYED(1000, post-midboss-filler-2);
|
|
|
|
INVOKE_TASK_DELAYED(1000, burst_fairies_1);
|
|
INVOKE_TASK_DELAYED(1000, explosion_fairy, VIEWPORT_W-80 + 120*I, -0.2+0.1*I);
|
|
INVOKE_TASK_DELAYED(1140, explosion_fairy, 80 + 220*I, 0.2+0.1*I);
|
|
|
|
STAGE_BOOKMARK_DELAYED(1400, post-midboss-filler-3);
|
|
|
|
INVOKE_TASK_DELAYED(1400, drop_swirls, 25, 2*VIEWPORT_W/3, 2*I, -0.06);
|
|
|
|
if(global.diff > D_Easy) {
|
|
INVOKE_TASK_DELAYED(1600, drop_swirls, 25, VIEWPORT_W/3, 2*I, 0.06);
|
|
}
|
|
|
|
INVOKE_TASK_DELAYED(1420, tritoss_fairy, VIEWPORT_W / 2 + 220*I, -2.6 * I);
|
|
|
|
INVOKE_TASK_DELAYED(1820, circle_fairy, VIEWPORT_W + 42 + 300*I, VIEWPORT_W - 130 + 240*I);
|
|
INVOKE_TASK_DELAYED(1820, circle_fairy, - 42 + 300*I, 130 + 240*I);
|
|
|
|
if(global.diff > D_Normal) {
|
|
INVOKE_TASK_DELAYED(1880, instantcircle_fairy, VIEWPORT_W + 42 + 300*I, VIEWPORT_W - 84 + 260*I, 0.2 * (-2 - I));
|
|
INVOKE_TASK_DELAYED(1880, instantcircle_fairy, - 42 + 300*I, 84 + 260*I, 0.2 * ( 2 - I));
|
|
}
|
|
|
|
INVOKE_TASK_DELAYED(2060, waveshot_fairy, 130 + 140*I, 0.2 * (-2 - I));
|
|
INVOKE_TASK_DELAYED(2060, waveshot_fairy, VIEWPORT_W - 130 + 140*I, 0.2 * ( 2 - I));
|
|
|
|
STAGE_BOOKMARK_DELAYED(2300, pre-boss);
|
|
|
|
WAIT(2560);
|
|
INVOKE_TASK(spawn_boss);
|
|
while(!global.boss) YIELD;
|
|
WAIT_EVENT(&global.boss->events.defeated);
|
|
|
|
stage_unlock_bgm("stage1boss");
|
|
|
|
WAIT(120);
|
|
stage1_bg_disable_snow();
|
|
WAIT(120);
|
|
|
|
stage1_dialog_post_boss();
|
|
WAIT_EVENT(&global.dialog->events.fadeout_began);
|
|
|
|
WAIT(5);
|
|
stage_finish(GAMEOVER_SCORESCREEN);
|
|
}
|