Stage 2 revamp (#229)
* stage2: remake wriggle non * stage2: coroutinize first enemy * stage2: coroutinize small_spin_circle (renamed redwall_fairy) * stage2: remake aimshot fairies * stage2: remove dead code * stage2: blind-coroutinize flea-shooting fairy; not tested at all * stage2: some hina nonspell shenanigans * stage2: coroutinize amulet of harm (very lazily) * stage2: lame hina nons (no balance) * stage2: remake wheel of fortune (lunatic tuning only for now) * stage2: Add raz's Hina nonspell * coroutinize bad pick * bad pick tweaks * coroutinize and remake Monty Hall Danmaku * stage2: coroutinize turning fairies * it's big brain time * look ma, i'm doing level design! * stage2 second half (WIP) * stage2: use new enemy spawners * stage2: remove dead code * stage2: fix post-midboss turning fairies * stage2: swap post-midboss red/blue aimshot fairies order * stage2: amulet of harm redesign prototype * Amulet of Harm tweaks: * Less erratic boss movement * Improved "amulet" visuals * stage2: very bare-bones difficulty scaling (enemies only) * stage2: wheel of fortune balance tweaks * stage2: amulet of harm difficulty balancing * stage2: add hina spin animation to amulet of harm * stage2: remove dead code
This commit is contained in:
parent
9b5d515721
commit
48f0a21e81
17 changed files with 1799 additions and 727 deletions
|
@ -254,6 +254,14 @@ DEFINE_EXTERN_TASK(common_set_bitflags) {
|
|||
*ARGS.pflags = ((*ARGS.pflags & ARGS.mask) | ARGS.set);
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(common_kill_projectile) {
|
||||
kill_projectile(TASK_BIND(ARGS.proj));
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(common_kill_enemy) {
|
||||
enemy_kill(TASK_BIND(ARGS.enemy));
|
||||
}
|
||||
|
||||
cmplx common_wander(cmplx origin, double dist, Rect bounds) {
|
||||
int attempts = 32;
|
||||
double angle;
|
||||
|
|
|
@ -98,4 +98,18 @@ DECLARE_EXTERN_TASK(
|
|||
}
|
||||
);
|
||||
|
||||
DECLARE_EXTERN_TASK(
|
||||
common_kill_projectile,
|
||||
{
|
||||
BoxedProjectile proj;
|
||||
}
|
||||
);
|
||||
|
||||
DECLARE_EXTERN_TASK(
|
||||
common_kill_enemy,
|
||||
{
|
||||
BoxedEnemy enemy;
|
||||
}
|
||||
);
|
||||
|
||||
#endif // IGUARD_common_tasks_h
|
||||
|
|
|
@ -16,5 +16,6 @@
|
|||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_midboss_nonspell_1, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_boss_nonspell_1, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_boss_nonspell_2, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_boss_nonspell_3, BossAttack);
|
||||
|
||||
#endif // IGUARD_stages_stage1_nonspells_nonspells_h
|
||||
|
|
|
@ -5,6 +5,7 @@ stage2_src = files(
|
|||
'hina.c',
|
||||
'nonspells/boss_nonspell_1.c',
|
||||
'nonspells/boss_nonspell_2.c',
|
||||
'nonspells/boss_nonspell_3.c',
|
||||
'nonspells/midboss_nonspell_1.c',
|
||||
'spells/amulet_of_harm.c',
|
||||
'spells/bad_pick.c',
|
||||
|
|
|
@ -11,20 +11,55 @@
|
|||
#include "nonspells.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "common_tasks.h"
|
||||
|
||||
void hina_cards1(Boss *h, int time) {
|
||||
int t = time % 500;
|
||||
TIMER(&t);
|
||||
TASK(wander, { BoxedBoss boss; }) {
|
||||
Rect wander_bounds = viewport_bounds(80);
|
||||
wander_bounds.bottom = 130;
|
||||
|
||||
if(time < 0)
|
||||
return;
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
boss->move = move_towards(boss->pos, 0.03);
|
||||
|
||||
AT(0) {
|
||||
aniplayer_queue(&h->ani, "guruguru", 0);
|
||||
}
|
||||
FROM_TO(0, 500, 2-(global.diff > D_Normal)) {
|
||||
play_sound_ex("shot1", 4, false);
|
||||
PROJECTILE(.proto = pp_card, .pos = h->pos+50*cexp(I*t/10), .color = RGB(0.8,0.0,0.0), .rule = asymptotic, .args = { (1.6+0.4*global.diff)*cexp(I*t/5.0), 3 });
|
||||
PROJECTILE(.proto = pp_card, .pos = h->pos-50*cexp(I*t/10), .color = RGB(0.0,0.0,0.8), .rule = asymptotic, .args = {-(1.6+0.4*global.diff)*cexp(I*t/5.0), 3 });
|
||||
for(;;WAIT(300)) {
|
||||
boss->move.attraction_point = common_wander(boss->pos, 60, wander_bounds);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage2_boss_nonspell_1) {
|
||||
STAGE_BOOKMARK(boss-non1);
|
||||
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK();
|
||||
|
||||
INVOKE_SUBTASK_DELAYED(420, wander, ENT_BOX(boss));
|
||||
|
||||
aniplayer_queue(&boss->ani, "guruguru", 0);
|
||||
|
||||
int step = difficulty_value(3, 2, 1, 1);
|
||||
real speed0 = difficulty_value(2.0, 2.4, 2.8, 3.2);
|
||||
real speed1 = 0.2; // difficulty_value(0.1, 0.1, 0.1, 0.2);
|
||||
|
||||
for(int t = 0;; t += WAIT(step)) {
|
||||
// play_sfx_ex("shot1", 4, false);
|
||||
play_sfx_loop("shot1_loop");
|
||||
|
||||
cmplx dir = cdir(t / 5.0);
|
||||
cmplx ofs = 50 * cdir(t / 10.0);
|
||||
|
||||
real hl = 30 + 50 * psin(t/60.0);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_card,
|
||||
.pos = boss->pos + ofs,
|
||||
.color = RGB(0.8, 0.0, 0.0),
|
||||
.move = move_asymptotic_halflife(speed1 * dir, speed0 * dir * 2, hl),
|
||||
);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_card,
|
||||
.pos = boss->pos - ofs,
|
||||
.color = RGB(0.0, 0.0, 0.8),
|
||||
.move = move_asymptotic_halflife(speed0 * -dir, speed1 * -dir, hl),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,34 +11,93 @@
|
|||
#include "nonspells.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "common_tasks.h"
|
||||
|
||||
void hina_cards2(Boss *h, int time) {
|
||||
int t = time % 500;
|
||||
TIMER(&t);
|
||||
TASK(speen, { BoxedBoss boss; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
aniplayer_queue(&boss->ani, "guruguru", 0);
|
||||
|
||||
if(time < 0)
|
||||
return;
|
||||
boss->move = (MoveParams) { 0 };
|
||||
cmplx opos = boss->pos;
|
||||
|
||||
hina_cards1(h, time);
|
||||
for(int t = 0;; t += WAIT(1)) {
|
||||
cmplx target = VIEWPORT_W/2 + 150*I + cwmul(cdir(t*M_TAU/150), 200 + 42*I);
|
||||
boss->pos = clerp(opos, target, smoothmin(t / 300.0, 1.0, 0.5));
|
||||
}
|
||||
}
|
||||
|
||||
GO_AT(h, 100, 200, 2);
|
||||
GO_AT(h, 260, 460, -2);
|
||||
GO_AT(h, 460, 500, 5);
|
||||
TASK(balls, { BoxedBoss boss; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
|
||||
for(;;) {
|
||||
WAIT(75);
|
||||
play_sound("shot_special1");
|
||||
play_sound("redirect");
|
||||
|
||||
cmplx aim = cnormalize(global.plr.pos - boss->pos);
|
||||
|
||||
int n = 12;
|
||||
for(int i = 0; i < n; ++i) {
|
||||
cmplx d = aim * cdir(i * M_TAU / n);
|
||||
|
||||
AT(100) {
|
||||
int i;
|
||||
for(i = 0; i < 30; i++) {
|
||||
play_sound("shot_special1");
|
||||
PROJECTILE(
|
||||
.proto = pp_bigball,
|
||||
.pos = h->pos,
|
||||
.color = RGB(0.7, 0, 0.7),
|
||||
.rule = asymptotic,
|
||||
.args = {
|
||||
2*cexp(I*2*M_PI*i/20.0),
|
||||
3
|
||||
}
|
||||
.pos = boss->pos,
|
||||
.color = RGB(0.8, 0.0, 0.8),
|
||||
.move = move_asymptotic_halflife(8 * d, 3 * d, 60),
|
||||
);
|
||||
|
||||
int m = 8;
|
||||
for(int j = 0; j < m; ++j) {
|
||||
real s = (m - j) / (real)m;
|
||||
PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = boss->pos,
|
||||
.color = j & 1 ? RGBA(0.8, 0.0, 0.0, 0.0) : RGBA(0.0, 0.0, 0.8, 0.0),
|
||||
.move = move_asymptotic_halflife((8 - 3 * s) * d, 3 * d, 60),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage2_boss_nonspell_2) {
|
||||
STAGE_BOOKMARK(boss-non2);
|
||||
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
boss->move = move_towards(VIEWPORT_W/2 + 100*I, 0.01);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
|
||||
INVOKE_SUBTASK(speen, ENT_BOX(boss));
|
||||
INVOKE_SUBTASK_DELAYED(150, balls, ENT_BOX(boss));
|
||||
|
||||
int step = difficulty_value(3, 2, 1, 1);
|
||||
real speed0 = difficulty_value(2.0, 2.4, 2.8, 3.2);
|
||||
real speed1 = 0.2; // difficulty_value(0.1, 0.1, 0.1, 0.2);
|
||||
|
||||
for(int t = 0;; t += WAIT(step)) {
|
||||
// play_sfx_ex("shot1", 4, false);
|
||||
play_sfx_loop("shot1_loop");
|
||||
|
||||
cmplx dir = cdir(t / 5.0);
|
||||
cmplx ofs = 50 * cdir(t / 10.0);
|
||||
|
||||
real hl = 30 + 50 * psin(t/60.0);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_card,
|
||||
.pos = boss->pos + ofs,
|
||||
.color = RGB(0.8, 0.0, 0.0),
|
||||
.move = move_asymptotic_halflife(speed1 * dir, speed0 * dir * 2, hl),
|
||||
);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_card,
|
||||
.pos = boss->pos - ofs,
|
||||
.color = RGB(0.0, 0.0, 0.8),
|
||||
.move = move_asymptotic_halflife(speed1 * -dir, speed0 * -dir * 2, hl),
|
||||
);
|
||||
}
|
||||
|
||||
STALL;
|
||||
}
|
||||
|
|
216
src/stages/stage2/nonspells/boss_nonspell_3.c
Normal file
216
src/stages/stage2/nonspells/boss_nonspell_3.c
Normal file
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* This software is licensed under the terms of the MIT License.
|
||||
* See COPYING for further information.
|
||||
* ---
|
||||
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
||||
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
||||
*/
|
||||
|
||||
#include "taisei.h"
|
||||
|
||||
#include "nonspells.h"
|
||||
#include "../hina.h"
|
||||
|
||||
#include "common_tasks.h"
|
||||
#include "global.h"
|
||||
|
||||
// Pattern contributed by raz, originally written for danmakufu
|
||||
// The port is almost direct, and a bit rough
|
||||
|
||||
TASK(TColumn, { BoxedBoss boss; cmplx offset; cmplx aim; int num; real rank; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
|
||||
// NOTE: bullet flash warning dummied out, replaced with simple wait
|
||||
WAIT(20);
|
||||
|
||||
int num = ARGS.num;
|
||||
real rank = ARGS.rank;
|
||||
cmplx shot_origin = boss->pos + ARGS.offset;
|
||||
|
||||
for(int i = 0; i < num; ++i) {
|
||||
real spd = rank * (3.75 + (1.875 * i) / num);
|
||||
cmplx aim = ARGS.aim * cdir(M_PI/180 * rng_sreal());
|
||||
|
||||
play_sfx_loop("shot1_loop");
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_card,
|
||||
.color = RGB(1, 0, 0),
|
||||
.move = move_asymptotic_simple(spd * aim, 2),
|
||||
.pos = shot_origin,
|
||||
);
|
||||
|
||||
WAIT(5);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(XPattern, { BoxedBoss boss; real dir_sign; int num2; int num3; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
|
||||
real rank = difficulty_value(0.4, 0.65, 0.85, 1);
|
||||
int rof = lround(3 / rank);
|
||||
int num = 180 * rank;
|
||||
int num2 = ARGS.num2 * rank;
|
||||
int num3 = ARGS.num3 * rank;
|
||||
real odd = 1;
|
||||
real dir_sign = ARGS.dir_sign;
|
||||
cmplx rot = cnormalize(global.plr.pos - boss->pos) * cdir(M_PI/2 * -dir_sign);
|
||||
|
||||
for(int i = 0; i < num2; ++i) {
|
||||
for(int j = 0; j < 3; ++j) {
|
||||
cmplx dir = rot * cdir(dir_sign * (i * M_TAU*2 / (num - 1.0) - odd * (M_PI/6 + M_PI/3 * j)));
|
||||
|
||||
INVOKE_SUBTASK(TColumn,
|
||||
.boss = ENT_BOX(boss),
|
||||
.offset = 80 * dir,
|
||||
.aim = dir * cdir(M_PI/4 * dir_sign * odd),
|
||||
.num = num3,
|
||||
.rank = rank
|
||||
);
|
||||
}
|
||||
|
||||
odd = -odd;
|
||||
WAIT(rof);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(TShoot, { BoxedBoss boss; real ang; real ang_s; real dist; real spd_inc; real dir_sign; int t; int time; int num; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
|
||||
real ang = ARGS.ang;
|
||||
real ang_s = ARGS.ang_s;
|
||||
real dist = ARGS.dist;
|
||||
real spd_inc = ARGS.spd_inc;
|
||||
real dir_sign = ARGS.dir_sign;
|
||||
int t = ARGS.t;
|
||||
int time = ARGS.time;
|
||||
int num = ARGS.num;
|
||||
|
||||
for(int i = 0; i < num; ++i) {
|
||||
real ang2 = ang - ang_s * i * dir_sign;
|
||||
cmplx shot_origin = boss->pos + cdir(ang2) * dist;
|
||||
real spd = (1.875 - 0.625 * (i / num)) + (spd_inc * t) / time;
|
||||
cmplx dir = cdir(ang2 - M_PI/2 * dir_sign);
|
||||
|
||||
bool phase2 = t > 0.6 * time;
|
||||
|
||||
play_sfx(phase2 ? "shot1" : "shot2");
|
||||
|
||||
PROJECTILE(
|
||||
.proto = phase2 ? pp_crystal : pp_card,
|
||||
.color = RGB(t / (real)time, 0, 1),
|
||||
.pos = shot_origin,
|
||||
.move = move_linear(spd * dir),
|
||||
);
|
||||
|
||||
WAIT(3);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(SpinPattern, { BoxedBoss boss; real dir_sign; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
|
||||
int rof = difficulty_value(12, 5, 4, 3);
|
||||
real spd_inc = difficulty_value(1.25, 1.875, 2.5, 3.125);
|
||||
|
||||
int time = 240;
|
||||
int num = 3;
|
||||
real dir_sign = ARGS.dir_sign;
|
||||
real ang = M_PI/2 * (1 - dir_sign);
|
||||
real ang_d = dir_sign * rof * (2*M_TAU - M_PI/2) / time;
|
||||
real ang_s = M_TAU/3;
|
||||
real ang_s_d = -(M_TAU/3) / (time / (real)rof);
|
||||
real dist = 20;
|
||||
real dist_d = 320.0 / time;
|
||||
|
||||
// NOTE: bullet flash warning dummied out, replaced with simple wait
|
||||
|
||||
WAIT(20);
|
||||
|
||||
for(int t = 0; t < time;) {
|
||||
INVOKE_SUBTASK(TShoot,
|
||||
.boss = ENT_BOX(boss),
|
||||
.ang = ang,
|
||||
.ang_s = ang_s,
|
||||
.dist = dist,
|
||||
.spd_inc = spd_inc,
|
||||
.dir_sign = dir_sign,
|
||||
.t = t,
|
||||
.time = time,
|
||||
.num = num
|
||||
);
|
||||
|
||||
ang_s += ang_s_d;
|
||||
dist += dist_d;
|
||||
ang += ang_d;
|
||||
|
||||
t += WAIT(rof);
|
||||
}
|
||||
}
|
||||
|
||||
static cmplx random_boss_pos(Boss *boss) {
|
||||
Rect bounds = viewport_bounds(140);
|
||||
bounds.top = 140;
|
||||
bounds.bottom = 200;
|
||||
return common_wander(boss->pos, 60, bounds);
|
||||
// return VIEWPORT_W/2 + (rng_sreal() * 40 + I * rng_range(165, 180));
|
||||
}
|
||||
|
||||
static void random_move(Boss *boss) {
|
||||
boss->move.attraction_point = random_boss_pos(boss);
|
||||
aniplayer_soft_switch(&boss->ani, "guruguru", 1);
|
||||
aniplayer_queue(&boss->ani, "main", 0);
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage2_boss_nonspell_3) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.02);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
|
||||
real dir_sign = rng_sign();
|
||||
|
||||
random_move(boss);
|
||||
|
||||
WAIT(20);
|
||||
INVOKE_SUBTASK(SpinPattern, ENT_BOX(boss), dir_sign);
|
||||
WAIT(30);
|
||||
INVOKE_SUBTASK(SpinPattern, ENT_BOX(boss), dir_sign);
|
||||
WAIT(30);
|
||||
INVOKE_SUBTASK(SpinPattern, ENT_BOX(boss), dir_sign);
|
||||
WAIT(210);
|
||||
INVOKE_SUBTASK(XPattern,
|
||||
.boss = ENT_BOX(boss),
|
||||
.dir_sign = dir_sign,
|
||||
.num2 = 42,
|
||||
.num3 = 6
|
||||
);
|
||||
|
||||
for(;;) {
|
||||
dir_sign = -dir_sign;
|
||||
|
||||
random_move(boss);
|
||||
|
||||
WAIT(80);
|
||||
|
||||
INVOKE_SUBTASK(XPattern,
|
||||
.boss = ENT_BOX(boss),
|
||||
.dir_sign = dir_sign,
|
||||
.num2 = 42,
|
||||
.num3 = 6
|
||||
);
|
||||
|
||||
INVOKE_SUBTASK(SpinPattern, ENT_BOX(boss), dir_sign);
|
||||
WAIT(30);
|
||||
INVOKE_SUBTASK(SpinPattern, ENT_BOX(boss), dir_sign);
|
||||
WAIT(30);
|
||||
INVOKE_SUBTASK(SpinPattern, ENT_BOX(boss), dir_sign);
|
||||
WAIT(180);
|
||||
|
||||
INVOKE_SUBTASK(XPattern,
|
||||
.boss = ENT_BOX(boss),
|
||||
.dir_sign = dir_sign,
|
||||
.num2 = 42,
|
||||
.num3 = 9
|
||||
);
|
||||
}
|
||||
}
|
|
@ -11,61 +11,138 @@
|
|||
#include "nonspells.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "common_tasks.h"
|
||||
|
||||
static int wriggle_bug(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
TASK(spawn_bugs, { BoxedBoss boss; BoxedProjectileArray *bugs; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
|
||||
real speed = difficulty_value(2, 2, 3, 2.25);
|
||||
int delay = difficulty_value(3, 2, 1, 1);
|
||||
|
||||
for(int i = 0;; ++i) {
|
||||
play_sfx_loop("shot1_loop");
|
||||
|
||||
for(int j = 0; j < 2; ++j) {
|
||||
cmplx dir = cdir(i * M_TAU / 20 + M_PI * j);
|
||||
|
||||
ENT_ARRAY_ADD(ARGS.bugs, PROJECTILE(
|
||||
.proto = pp_rice,
|
||||
.pos = boss->pos + dir * 60,
|
||||
.color = RGB(1.0, 0.5, 0.2),
|
||||
.move = move_linear(speed * dir),
|
||||
.max_viewport_dist = 120,
|
||||
));
|
||||
}
|
||||
|
||||
WAIT(delay);
|
||||
}
|
||||
|
||||
p->pos += p->args[0];
|
||||
p->angle = carg(p->args[0]);
|
||||
|
||||
if(global.boss && global.boss->current && !((global.frames - global.boss->current->starttime - 30) % 200)) {
|
||||
play_sound("redirect");
|
||||
p->args[0] *= cexp(I*(M_PI/3)*nfrand());
|
||||
spawn_projectile_highlight_effect(p);
|
||||
}
|
||||
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
void wriggle_small_storm(Boss *w, int time) {
|
||||
int t = time % 400;
|
||||
TIMER(&t);
|
||||
TASK(scatter_bugs, { BoxedProjectileArray *bugs; }) {
|
||||
DECLARE_ENT_ARRAY(Projectile, bugs, ARGS.bugs->size);
|
||||
ENT_ARRAY_FOREACH(ARGS.bugs, Projectile *bug, {
|
||||
ENT_ARRAY_ADD(&bugs, bug);
|
||||
});
|
||||
|
||||
if(time < 0)
|
||||
return;
|
||||
int p = difficulty_value(3, 4, 6, 8);
|
||||
cmplx rot = cdir(M_TAU / difficulty_value(20, 20, 15, 10));
|
||||
|
||||
FROM_TO_SND("shot1_loop", 0,400,5-global.diff) {
|
||||
PROJECTILE(.proto = pp_rice, .pos = w->pos, .color = RGB(1,0.5,0.2), .rule = wriggle_bug, .args = { 2*cexp(I*_i*2*M_PI/20) });
|
||||
PROJECTILE(.proto = pp_rice, .pos = w->pos, .color = RGB(1,0.5,0.2), .rule = wriggle_bug, .args = { 2*cexp(I*_i*2*M_PI/20+I*M_PI) });
|
||||
}
|
||||
for(int i = bugs.size - 1; i >= 0; --i) {
|
||||
Projectile *bug = ENT_UNBOX(bugs.array[i]);
|
||||
|
||||
GO_AT(w, 60, 120, 1)
|
||||
GO_AT(w, 180, 240, -1)
|
||||
if(bug) {
|
||||
cmplx v = 1.1 * bug->move.velocity;
|
||||
|
||||
if(!((t+200-15)%200)) {
|
||||
aniplayer_queue(&w->ani,"specialshot_charge",1);
|
||||
aniplayer_queue(&w->ani,"specialshot_hold",20);
|
||||
aniplayer_queue(&w->ani,"specialshot_release",1);
|
||||
aniplayer_queue(&w->ani,"main",0);
|
||||
}
|
||||
|
||||
if(!(t%200)) {
|
||||
int i;
|
||||
play_sound("shot_special1");
|
||||
|
||||
for(i = 0; i < 10+global.diff; i++) {
|
||||
PROJECTILE(
|
||||
.proto = pp_bigball,
|
||||
.pos = w->pos,
|
||||
.color = RGB(0.1,0.3,0.0),
|
||||
.rule = asymptotic,
|
||||
.args = {
|
||||
2*cexp(I*i*2*M_PI/(10+global.diff)),
|
||||
2
|
||||
}
|
||||
.proto = pp_thickrice,
|
||||
.pos = bug->pos,
|
||||
.color = RGB(0.2, 1.0, 0.2),
|
||||
.move = move_asymptotic_simple(v * rot, -4)
|
||||
);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_thickrice,
|
||||
.pos = bug->pos,
|
||||
.color = RGB(0.2, 1.0, 0.2),
|
||||
.move = move_asymptotic_simple(v / rot, -4)
|
||||
);
|
||||
|
||||
kill_projectile(bug);
|
||||
}
|
||||
|
||||
if(i % p == 0) {
|
||||
play_sound("redirect");
|
||||
YIELD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void wriggle_anim_begin_charge(Boss *boss) {
|
||||
aniplayer_soft_switch(&boss->ani, "specialshot_charge", 1);
|
||||
aniplayer_queue(&boss->ani, "specialshot_hold", 0);
|
||||
}
|
||||
|
||||
static void wriggle_anim_end_charge(Boss *boss) {
|
||||
aniplayer_soft_switch(&boss->ani, "specialshot_release", 1);
|
||||
aniplayer_queue(&boss->ani, "main", 0);
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage2_midboss_nonspell_1) {
|
||||
STAGE_BOOKMARK(non1);
|
||||
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
boss->move = move_towards(VIEWPORT_W/2 + 100.0*I, 0.02);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
|
||||
DECLARE_ENT_ARRAY(Projectile, bugs, 512);
|
||||
|
||||
INVOKE_SUBTASK(spawn_bugs, ENT_BOX(boss), &bugs);
|
||||
|
||||
Rect wander_bounds = viewport_bounds(80);
|
||||
wander_bounds.bottom = 180;
|
||||
|
||||
for(;;) {
|
||||
// boss->move.attraction_point = common_wander(boss->pos, 20, wander_bounds);
|
||||
WAIT(60);
|
||||
boss->move.attraction_point = common_wander(boss->pos, 20, wander_bounds);
|
||||
|
||||
int delay = 110;
|
||||
int charge_time = 60;
|
||||
|
||||
WAIT(delay - charge_time);
|
||||
wriggle_anim_begin_charge(boss);
|
||||
INVOKE_SUBTASK(common_charge,
|
||||
.anchor = &boss->pos,
|
||||
.color = RGBA(0.2, 1.0, 0.2, 0),
|
||||
.time = charge_time,
|
||||
.sound = COMMON_CHARGE_SOUNDS
|
||||
);
|
||||
WAIT(charge_time);
|
||||
wriggle_anim_end_charge(boss);
|
||||
|
||||
INVOKE_TASK(scatter_bugs, &bugs);
|
||||
ENT_ARRAY_CLEAR(&bugs);
|
||||
WAIT(30);
|
||||
|
||||
play_sound("shot_special1");
|
||||
|
||||
int n = 20;
|
||||
int m = difficulty_value(1, 3, 4, 5);
|
||||
real a = 0.05;
|
||||
|
||||
for(int i = 0; i < n; i++) {
|
||||
for(int j = 0; j < m; ++j) {
|
||||
PROJECTILE(
|
||||
.proto = pp_bigball,
|
||||
.pos = boss->pos,
|
||||
.color = RGB(0.1, 0.3, 0.0),
|
||||
.move = move_accelerated(0, a * (1 + j) * cdir((i + 0.5) * M_TAU/n - M_PI/2)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
boss->move.attraction_point = common_wander(boss->pos, 120, wander_bounds);
|
||||
}
|
||||
|
||||
STALL;
|
||||
}
|
||||
|
|
|
@ -13,8 +13,12 @@
|
|||
|
||||
#include "boss.h"
|
||||
|
||||
void wriggle_small_storm(Boss *w, int time);
|
||||
void hina_cards1(Boss *h, int time);
|
||||
void hina_cards2(Boss *h, int time);
|
||||
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage2_midboss_nonspell_1, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage2_boss_nonspell_1, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage2_boss_nonspell_2, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage2_boss_nonspell_3, BossAttack);
|
||||
|
||||
#endif // IGUARD_stages_stage2_nonspells_nonspells_h
|
||||
|
|
|
@ -11,36 +11,206 @@
|
|||
#include "spells.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "common_tasks.h"
|
||||
|
||||
void hina_amulet(Boss *h, int time) {
|
||||
int t = time % 200;
|
||||
TASK(spinner_bullet_redirect, { BoxedProjectile p; MoveParams move; }) {
|
||||
Projectile *p = TASK_BIND(ARGS.p);
|
||||
cmplx ov = p->move.velocity;
|
||||
p->move = ARGS.move;
|
||||
p->move.velocity += ov;
|
||||
}
|
||||
|
||||
if(time < 0)
|
||||
return;
|
||||
static void amulet_visual(Enemy *e, int t, bool render) {
|
||||
if(render) {
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.color = RGBA(2, 1, 1, 0),
|
||||
.sprite = "fairy_circle_big",
|
||||
.pos.as_cmplx = e->pos,
|
||||
.rotation.angle = t * 5 * DEG2RAD,
|
||||
});
|
||||
|
||||
if(time < 100)
|
||||
GO_TO(h, VIEWPORT_W/2 + 200.0*I, 0.02);
|
||||
|
||||
TIMER(&t);
|
||||
|
||||
cmplx d = global.plr.pos - h->pos;
|
||||
int loopduration = 200*(global.diff+0.5)/(D_Lunatic+0.5);
|
||||
AT(0) {
|
||||
aniplayer_queue_frames(&h->ani,"guruguru",loopduration);
|
||||
}
|
||||
FROM_TO_SND("shot1_loop", 0,loopduration,1) {
|
||||
float f = _i/30.0;
|
||||
cmplx n = cexp(I*2*M_PI*f+I*carg(d)+0.7*time/200*I)/sqrt(0.5+global.diff);
|
||||
|
||||
float speed = 1.0 + 0.75 * imax(0, global.diff - D_Normal);
|
||||
float accel = 1.0 + 1.20 * imax(0, global.diff - D_Normal);
|
||||
|
||||
cmplx p = h->pos+30*log(1+_i/2.0)*n;
|
||||
|
||||
ProjPrototype *t0 = pp_ball;
|
||||
ProjPrototype *t1 = global.diff == D_Easy ? t0 : pp_crystal;
|
||||
|
||||
PROJECTILE(.proto = t0, .pos = p, .color = RGB(0.8, 0.0, 0.0), .rule = accelerated, .args = { speed * 2*n*I, accel * -0.01*n });
|
||||
PROJECTILE(.proto = t1, .pos = p, .color = RGB(0.8, 0.0, 0.5), .rule = accelerated, .args = { speed * -2*n*I, accel * -0.01*n });
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite = "enemy/swirl",
|
||||
.pos.as_cmplx = e->pos,
|
||||
.rotation.angle = t * -10 * DEG2RAD,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TASK(amulet_fire_spinners, { BoxedEnemy core; BoxedProjectileArray *spinners; }) {
|
||||
Enemy *core = TASK_BIND(ARGS.core);
|
||||
|
||||
int nshots = difficulty_value(1, 1, 3, 69);
|
||||
int charge_time = difficulty_value(90, 75, 60, 60);
|
||||
real accel = difficulty_value(0.05, 0.06, 0.085, 0.085);
|
||||
|
||||
for(int i = 0; i < nshots; ++i) {
|
||||
WAIT(60);
|
||||
common_charge(charge_time, &core->pos, 0, RGBA(0.6, 0.2, 0.5, 0));
|
||||
|
||||
ENT_ARRAY_FOREACH(ARGS.spinners, Projectile *p, {
|
||||
int cnt = difficulty_value(12, 16, 22, 24);
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
cmplx ca = circle_dir(i, cnt);
|
||||
cmplx o = p->pos + 42 * ca;
|
||||
cmplx aim = cnormalize(o - core->pos);
|
||||
|
||||
Projectile *c = PROJECTILE(
|
||||
.proto = pp_crystal,
|
||||
.pos = p->pos,
|
||||
.color = RGB(0.2 + 0.8 * tanh(cabs2(o - core->pos) / 4200), 0.2, 1.0),
|
||||
.max_viewport_dist = p->max_viewport_dist,
|
||||
.move = move_towards(o, 0.02),
|
||||
);
|
||||
|
||||
INVOKE_TASK_DELAYED(42, spinner_bullet_redirect, ENT_BOX(c), move_accelerated(0, aim * accel));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
WAIT(60);
|
||||
enemy_kill(core);
|
||||
}
|
||||
|
||||
TASK(amulet, {
|
||||
cmplx pos;
|
||||
MoveParams move;
|
||||
CoEvent *death_event;
|
||||
}) {
|
||||
Enemy *core = create_enemy_p(&global.enemies, ARGS.pos, 2000, amulet_visual, NULL, 0, 0, 0, 0);
|
||||
core->hurt_radius = 18;
|
||||
core->hit_radius = 36;
|
||||
core->flags |= EFLAG_NO_VISUAL_CORRECTION;
|
||||
core->move = ARGS.move;
|
||||
|
||||
INVOKE_TASK_AFTER(NOT_NULL(ARGS.death_event), common_kill_enemy, ENT_BOX(core));
|
||||
|
||||
int num_spinners = difficulty_value(2, 3, 4, 4);
|
||||
real spinner_ofs = 32;
|
||||
cmplx spin = cdir(M_PI/8);
|
||||
|
||||
DECLARE_ENT_ARRAY(Projectile, spinners, num_spinners);
|
||||
cmplx spinner_offsets[num_spinners];
|
||||
|
||||
cmplx init_orientation = cnormalize(core->move.velocity);
|
||||
|
||||
for(int i = 0; i < num_spinners; ++i) {
|
||||
spinner_offsets[i] = spinner_ofs * cdir(i * M_TAU / num_spinners) * init_orientation;
|
||||
|
||||
Projectile *p = PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = core->pos,
|
||||
.color = RGB(0.8, 0.2, 1.0),
|
||||
.max_viewport_dist = spinner_ofs * 2,
|
||||
.flags = PFLAG_NOCLEAR,
|
||||
);
|
||||
|
||||
INVOKE_TASK_AFTER(&core->events.killed, common_kill_projectile, ENT_BOX(p));
|
||||
ENT_ARRAY_ADD(&spinners, p);
|
||||
}
|
||||
|
||||
INVOKE_SUBTASK(amulet_fire_spinners, ENT_BOX(core), &spinners);
|
||||
|
||||
for(;;YIELD) {
|
||||
int live = 0;
|
||||
|
||||
ENT_ARRAY_FOREACH_COUNTER(&spinners, int i, Projectile *p, {
|
||||
p->pos = core->pos + spinner_offsets[i];
|
||||
spinner_offsets[i] *= spin;
|
||||
++live;
|
||||
});
|
||||
|
||||
if(!live) {
|
||||
enemy_kill(core);
|
||||
return;
|
||||
}
|
||||
|
||||
real s = approach_asymptotic(carg(spin), -M_PI/32, 0.02, 1e-5);
|
||||
spin = cabs(spin) * cdir(s);
|
||||
}
|
||||
}
|
||||
|
||||
static void fan_burst_side(cmplx o, cmplx ofs, real x) {
|
||||
int cnt = difficulty_value(2, 3, 3, 3);
|
||||
real speed = difficulty_value(2, 2.5, 3, 3);
|
||||
real sat = 0.75;
|
||||
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
real s = speed * (1 + 0.1 * i);
|
||||
|
||||
PROJECTILE(
|
||||
.pos = o + ofs,
|
||||
.color = RGB(sat * 1, sat * 0.25 * i / (cnt - 1.0), 0),
|
||||
.proto = pp_card,
|
||||
.move = move_asymptotic_simple(s * I*cnormalize(ofs) * cdir(x * (i+1) * -0.3), 3 - 0.1 * i),
|
||||
);
|
||||
|
||||
PROJECTILE(
|
||||
.pos = o + ofs,
|
||||
.color = RGB(0, sat * 0.25 * i / (cnt - 1.0), sat * 1),
|
||||
.proto = pp_card,
|
||||
.move = move_asymptotic_simple(s * cnormalize(ofs)/I * cdir(x * (i+1) * 0.3), 3 - 0.1 * i),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(fan_burst, { Boss *boss; }) {
|
||||
int nbursts = difficulty_value(1, 1, 2, 2);
|
||||
|
||||
for(int burst = 0; burst < nbursts; ++burst) {
|
||||
int cnt = 30;
|
||||
for(int i = 0; i < cnt; ++i, WAIT(3)) {
|
||||
play_sfx_loop("shot1_loop");
|
||||
|
||||
cmplx o = 32;
|
||||
real u = 32;
|
||||
|
||||
real x = i / (cnt - 1.0);
|
||||
|
||||
o += sin(4 * -x * M_PI) * u;
|
||||
o += cos(4 * -x * 1.23 * M_PI) * I * u;
|
||||
|
||||
fan_burst_side(ARGS.boss->pos, o, x);
|
||||
fan_burst_side(ARGS.boss->pos, -conj(o), x);
|
||||
}
|
||||
|
||||
WAIT(30);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage2_spell_amulet_of_harm) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
boss->move = move_towards(VIEWPORT_W/2 + 200.0*I, 0.02);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
|
||||
Rect wander_bounds = viewport_bounds(64);
|
||||
wander_bounds.top += 64;
|
||||
wander_bounds.bottom = VIEWPORT_H * 0.4;
|
||||
|
||||
for(;;) {
|
||||
boss->move.attraction_point = common_wander(boss->pos, VIEWPORT_W * 0.3, wander_bounds);
|
||||
|
||||
WAIT(20);
|
||||
|
||||
int cnt = 3;
|
||||
real arange = M_PI/3;
|
||||
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
real s = 4 * fmax(2, log1p(cabs(boss->move.velocity)));
|
||||
cmplx dir = cnormalize(-boss->move.velocity) * cdir(arange * (i / (cnt - 1.0) - 0.5));
|
||||
|
||||
play_sfx("redirect");
|
||||
INVOKE_TASK(amulet, boss->pos, move_asymptotic_halflife(s * dir, 0, 12), &ARGS.attack->events.finished);
|
||||
WAIT(15);
|
||||
}
|
||||
|
||||
WAIT(40);
|
||||
aniplayer_soft_switch(&boss->ani, "guruguru", 0);
|
||||
WAIT(20);
|
||||
boss->move.attraction_point = common_wander(boss->pos, VIEWPORT_W * 0.25, wander_bounds);
|
||||
play_sfx("shot_special1");
|
||||
INVOKE_SUBTASK(fan_burst, boss);
|
||||
WAIT(120);
|
||||
aniplayer_soft_switch(&boss->ani, "main", 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,99 +11,155 @@
|
|||
#include "spells.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "common_tasks.h"
|
||||
|
||||
#define SLOTS 5
|
||||
|
||||
static int bad_pick_bullet(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->pos += p->args[0];
|
||||
p->args[0] += p->args[1];
|
||||
|
||||
// reflection
|
||||
int slot = (int)(creal(p->pos)/(VIEWPORT_W/SLOTS)+1)-1; // correct rounding for slot == -1
|
||||
int targetslot = creal(p->args[2])+0.5;
|
||||
if(slot != targetslot)
|
||||
p->args[0] = copysign(creal(p->args[0]),targetslot-slot)+I*cimag(p->args[0]);
|
||||
|
||||
return ACTION_NONE;
|
||||
static int slot_of_position(cmplx pos) {
|
||||
return (int)(creal(pos) / (VIEWPORT_W / SLOTS) + 1) - 1; // correct rounding for slot == -1
|
||||
}
|
||||
|
||||
void hina_bad_pick(Boss *h, int time) {
|
||||
int t = time % 500;
|
||||
int i, j;
|
||||
TASK(bad_pick_bullet, { cmplx pos; cmplx vel; cmplx accel; int target_slot; }) {
|
||||
Projectile *p = TASK_BIND(PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = ARGS.pos,
|
||||
.color = RGBA(0.7, 0.0, 0.0, 0.0),
|
||||
.move = move_accelerated(ARGS.vel, ARGS.accel),
|
||||
));
|
||||
|
||||
TIMER(&t);
|
||||
for(;;YIELD) {
|
||||
// reflection
|
||||
int slot = slot_of_position(p->pos);
|
||||
|
||||
if(time < 0)
|
||||
return;
|
||||
if(slot != ARGS.target_slot) {
|
||||
p->move.velocity = copysign(creal(p->move.velocity), ARGS.target_slot - slot) + I*cimag(p->move.velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GO_TO(h, VIEWPORT_W/SLOTS*((113*(time/500))%SLOTS+0.5)+ 100.0*I, 0.02);
|
||||
static cmplx pick_boss_position(void) {
|
||||
return VIEWPORT_W/SLOTS * (rng_irange(0, SLOTS) + 0.5) + 100 * I;
|
||||
}
|
||||
|
||||
FROM_TO(100, 500, 5) {
|
||||
play_sound_ex("shot1", 4, false);
|
||||
TASK(walls, { int duration; }) {
|
||||
for(int t = 0; t < ARGS.duration; t += WAIT(5)) {
|
||||
play_sfx_loop("shot1_loop");
|
||||
|
||||
for(i = 1; i < SLOTS; i++) {
|
||||
for(int i = 1; i < SLOTS; i++) {
|
||||
PROJECTILE(
|
||||
.proto = pp_crystal,
|
||||
.pos = VIEWPORT_W/SLOTS*i,
|
||||
.color = RGB(0.5,0,0.6),
|
||||
.rule = linear,
|
||||
.args = { 7.0*I }
|
||||
.pos = VIEWPORT_W / SLOTS * i,
|
||||
.color = RGB(0.5, 0, 0.6),
|
||||
.move = move_linear(7*I),
|
||||
);
|
||||
}
|
||||
|
||||
if(global.diff >= D_Hard) {
|
||||
double shift = 0;
|
||||
if(global.diff == D_Lunatic)
|
||||
shift = 0.3*fmax(0,t-200);
|
||||
for(i = 1; i < SLOTS; i++) {
|
||||
double height = VIEWPORT_H/SLOTS*i+shift;
|
||||
if(height > VIEWPORT_H-40)
|
||||
height -= VIEWPORT_H-40;
|
||||
real shift = 0;
|
||||
|
||||
if(global.diff == D_Lunatic) {
|
||||
shift = 0.3 * fmax(0, t - 100);
|
||||
}
|
||||
|
||||
for(int i = 1; i < SLOTS; i++) {
|
||||
real height = VIEWPORT_H / SLOTS * i + shift;
|
||||
|
||||
if(height > VIEWPORT_H - 40) {
|
||||
height -= VIEWPORT_H -40;
|
||||
}
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_crystal,
|
||||
.pos = (i&1)*VIEWPORT_W+I*height,
|
||||
.color = RGB(0.5,0,0.6),
|
||||
.rule = linear,
|
||||
.args = { 5.0*(1-2*(i&1)) }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AT(190) {
|
||||
aniplayer_queue(&h->ani,"guruguru",2);
|
||||
aniplayer_queue(&h->ani,"main",0);
|
||||
}
|
||||
|
||||
AT(200) {
|
||||
play_sound("shot_special1");
|
||||
|
||||
int win = tsrand()%SLOTS;
|
||||
for(i = 0; i < SLOTS; i++) {
|
||||
if(i == win)
|
||||
continue;
|
||||
|
||||
float cnt = (1+imin(D_Normal,global.diff)) * 5;
|
||||
for(j = 0; j < cnt; j++) {
|
||||
cmplx o = VIEWPORT_W/SLOTS*(i + j/(cnt-1));
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = o,
|
||||
.color = RGBA(0.7, 0.0, 0.0, 0.0),
|
||||
.rule = bad_pick_bullet,
|
||||
.args = {
|
||||
0,
|
||||
0.005*nfrand() + 0.005*I * (1 + psin(i + j + global.frames)),
|
||||
i
|
||||
},
|
||||
.pos = (i & 1) * VIEWPORT_W + I * height,
|
||||
.color = RGB(0.5, 0, 0.6),
|
||||
.move = move_linear(5.0 * (1 - 2 * (i & 1))),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage2_spell_bad_pick) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
boss->move = move_towards(pick_boss_position(), 0.02);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
|
||||
int balls_per_slot = difficulty_value(15, 15, 20, 25);
|
||||
|
||||
for(int t = 0;;) {
|
||||
t += WAIT(100);
|
||||
INVOKE_SUBTASK(walls, 400);
|
||||
t += WAIT(30);
|
||||
|
||||
int win;
|
||||
int plr_slot = slot_of_position(global.plr.pos);
|
||||
|
||||
do {
|
||||
win = rng_irange(0, SLOTS);
|
||||
} while(win == plr_slot && global.diff > D_Easy);
|
||||
|
||||
for(int i = 0; i < SLOTS; i++) {
|
||||
if(i == win) {
|
||||
continue;
|
||||
}
|
||||
|
||||
INVOKE_SUBTASK(common_charge,
|
||||
.pos = VIEWPORT_W / (real)SLOTS * (i + 0.5),
|
||||
.color = RGBA(1.0, 0.1, 0.1, 0),
|
||||
.time = 65,
|
||||
.sound = COMMON_CHARGE_SOUNDS
|
||||
);
|
||||
|
||||
if(i == plr_slot && global.diff > D_Normal) {
|
||||
INVOKE_SUBTASK(common_charge,
|
||||
.pos = VIEWPORT_W / (real)SLOTS * (i + 0.5) + VIEWPORT_H*I,
|
||||
.color = RGBA(0.2, 0.2, 1.0, 0),
|
||||
.time = 65,
|
||||
.sound = COMMON_CHARGE_SOUNDS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
t += WAIT(60);
|
||||
aniplayer_queue(&boss->ani, "guruguru", 2);
|
||||
aniplayer_queue(&boss->ani, "main", 0);
|
||||
t += WAIT(10);
|
||||
|
||||
play_sfx("shot_special1");
|
||||
|
||||
for(int i = 0; i < SLOTS; i++) {
|
||||
if(i == win) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for(int j = 0; j < balls_per_slot; j++) {
|
||||
INVOKE_TASK(bad_pick_bullet,
|
||||
.pos = VIEWPORT_W / (real)SLOTS * (i + 0.1 + 0.8 * j / (balls_per_slot - 1.0)),
|
||||
.vel = 0,
|
||||
.accel = 0.005 * (rng_sreal() + I * (1 + psin(i + j + t))),
|
||||
.target_slot = i
|
||||
);
|
||||
}
|
||||
|
||||
if(i == plr_slot && global.diff > D_Normal) {
|
||||
for(int j = 0; j < balls_per_slot; j++) {
|
||||
Projectile *p = PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = VIEWPORT_W / (real)SLOTS * (i + 0.1 + 0.8 * j / (balls_per_slot - 1.0)) + VIEWPORT_H*I,
|
||||
.color = RGBA(0.2, 0.2, 0.7, 0.0),
|
||||
.move = move_accelerated(0, 0),
|
||||
);
|
||||
|
||||
INVOKE_TASK(common_move,
|
||||
.ent = ENT_BOX(p).as_generic,
|
||||
.pos = &p->move.acceleration,
|
||||
.move_params = move_linear(0.000001 * (rng_sreal() - I * (1 + psin(i + j + t))))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t += WAIT(300);
|
||||
boss->move.attraction_point = pick_boss_position();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,239 +11,198 @@
|
|||
#include "spells.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "common_tasks.h"
|
||||
|
||||
static int timeout_deadproj_linear(Projectile *p, int time) {
|
||||
// TODO: maybe add a "clear on timeout" flag?
|
||||
#define NUM_SLOTS 3
|
||||
#define SLOT_WIDTH (VIEWPORT_W / (real)NUM_SLOTS)
|
||||
|
||||
if(time < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
static void goat_bullets(
|
||||
int slot,
|
||||
int t,
|
||||
int cnt,
|
||||
bool top,
|
||||
ProjFlags flags,
|
||||
BoxedProjectileArray *array
|
||||
) {
|
||||
for(int i = 0; i < cnt; i++) {
|
||||
cmplx o = !top*VIEWPORT_H*I + SLOT_WIDTH * (slot + i/(cnt - 1.0));
|
||||
cmplx a = 0.007 * (sin((M_PI * 4 * i / (cnt - 1))) * 0.1 * global.diff - I * (1 + psin(i + t)));
|
||||
|
||||
if(time > creal(p->args[0])) {
|
||||
p->type = PROJ_DEAD;
|
||||
}
|
||||
if(top) {
|
||||
a *= -0.5;
|
||||
}
|
||||
|
||||
p->pos += p->args[1];
|
||||
p->angle = carg(p->args[1]);
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static int hina_monty_slave(Enemy *s, int time) {
|
||||
if(time < 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(time > 60 && time < 720-140 + 20*(global.diff-D_Lunatic) && !(time % (int)(fmax(2 + (global.diff < D_Normal), (120 - 0.5 * time))))) {
|
||||
play_sfx_loop("shot1_loop");
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_crystal,
|
||||
.pos = s->pos,
|
||||
.color = RGB(0.5 + 0.5 * psin(time*0.2), 0.3, 1.0 - 0.5 * psin(time*0.2)),
|
||||
.rule = asymptotic,
|
||||
.args = {
|
||||
5*I + 1 * (sin(time) + I * cos(time)),
|
||||
4
|
||||
}
|
||||
Projectile *p = PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = o,
|
||||
.color = top ? RGBA(0, 0, 0.7, 0) : RGBA(0.7, 0, 0, 0),
|
||||
.move = move_accelerated(a, a),
|
||||
.flags = flags,
|
||||
);
|
||||
|
||||
if(global.diff > D_Easy) {
|
||||
PROJECTILE(
|
||||
.proto = pp_crystal,
|
||||
.pos = s->pos,
|
||||
.color = RGB(0.5 + 0.5 * psin(time*0.2), 0.3, 1.0 - 0.5 * psin(time*0.2)),
|
||||
.rule = timeout_deadproj_linear,
|
||||
.args = {
|
||||
500,
|
||||
-0.5*I + 1 * (sin(time) + I * cos(time))
|
||||
}
|
||||
);
|
||||
if(array) {
|
||||
ENT_ARRAY_ADD(array, p);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void hina_monty_slave_visual(Enemy *s, int time, bool render) {
|
||||
Swirl(s, time, render);
|
||||
TASK(goat, { int slot; CoEvent *activation_event; }) {
|
||||
int cnt = difficulty_value(15, 20, 25, 30);
|
||||
int slot = ARGS.slot;
|
||||
int charge_time = 60;
|
||||
int period = 60;
|
||||
int timeout = 300;
|
||||
|
||||
if(!render) {
|
||||
return;
|
||||
bool top_enabled = global.diff > D_Hard;
|
||||
|
||||
INVOKE_SUBTASK(common_charge,
|
||||
.pos = SLOT_WIDTH * (slot + 0.5) + VIEWPORT_H*I,
|
||||
.color = RGBA(1.0, 0.2, 0.2, 0),
|
||||
.time = charge_time,
|
||||
.sound = COMMON_CHARGE_SOUNDS
|
||||
);
|
||||
|
||||
if(top_enabled) {
|
||||
INVOKE_SUBTASK(common_charge,
|
||||
.pos = SLOT_WIDTH * (slot + 0.5),
|
||||
.color = RGBA(0.2, 0.2, 1.0, 0),
|
||||
.time = charge_time,
|
||||
.sound = COMMON_CHARGE_SOUNDS
|
||||
);
|
||||
}
|
||||
|
||||
Sprite *soul = get_sprite("proj/soul");
|
||||
double scale = fabs(swing(clamp(time / 60.0, 0, 1), 3)) * 1.25;
|
||||
WAIT(charge_time);
|
||||
play_sfx("shot_special1");
|
||||
|
||||
Color *clr1 = RGBA(1.0, 0.0, 0.0, 0.0);
|
||||
Color *clr2 = RGBA(0.0, 0.0, 1.0, 0.0);
|
||||
Color *clr3 = RGBA(psin(time*0.05), 0.0, 1.0 - psin(time*0.05), 0.0);
|
||||
DECLARE_ENT_ARRAY(Projectile, initial_bullets_bottom, cnt);
|
||||
DECLARE_ENT_ARRAY(Projectile, initial_bullets_top, cnt);
|
||||
|
||||
r_mat_mv_push();
|
||||
r_mat_mv_translate(creal(s->pos), cimag(s->pos), 0);
|
||||
r_shader("sprite_bullet");
|
||||
goat_bullets(slot, 0, cnt, false, PFLAG_NOMOVE, &initial_bullets_bottom);
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite_ptr = soul,
|
||||
.rotation.angle = time * DEG2RAD,
|
||||
.scale.x = scale * (0.6 + 0.6 * psin(time*0.1)),
|
||||
.scale.y = scale * (0.7 + 0.5 * psin(time*0.1 + M_PI)),
|
||||
.color = clr1,
|
||||
if(top_enabled) {
|
||||
goat_bullets(slot, period, cnt, true, PFLAG_NOMOVE, &initial_bullets_top);
|
||||
}
|
||||
|
||||
WAIT_EVENT(ARGS.activation_event);
|
||||
|
||||
int t = 0;
|
||||
|
||||
play_sfx("redirect");
|
||||
ENT_ARRAY_FOREACH(&initial_bullets_bottom, Projectile *p, {
|
||||
p->flags &= ~PFLAG_NOMOVE;
|
||||
});
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite_ptr = soul,
|
||||
.rotation.angle = time * DEG2RAD,
|
||||
.scale.x = scale * (0.7 + 0.5 * psin(time*0.1 + M_PI)),
|
||||
.scale.y = scale * (0.6 + 0.6 * psin(time*0.1)),
|
||||
.color = clr2,
|
||||
});
|
||||
t += WAIT(period);
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite_ptr = soul,
|
||||
.rotation.angle = -time * DEG2RAD,
|
||||
.scale.both = scale,
|
||||
.color = clr3,
|
||||
});
|
||||
if(top_enabled) {
|
||||
play_sfx("redirect");
|
||||
ENT_ARRAY_FOREACH(&initial_bullets_top, Projectile *p, {
|
||||
p->flags &= ~PFLAG_NOMOVE;
|
||||
});
|
||||
|
||||
r_mat_mv_pop();
|
||||
t += WAIT(period);
|
||||
}
|
||||
|
||||
for(bool top = false; t < timeout; t += WAIT(period), top = !top) {
|
||||
play_sfx("shot_special1");
|
||||
goat_bullets(slot, t, cnt, top && top_enabled, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void hina_monty(Boss *h, int time) {
|
||||
int t = time % 720;
|
||||
TIMER(&t);
|
||||
TASK(cards, { BoxedBoss boss; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
|
||||
static short slave_pos, bad_pos, good_pos, plr_pos;
|
||||
static int cwidth = VIEWPORT_W / 3.0;
|
||||
static cmplx targetpos;
|
||||
int cnt = 10;
|
||||
int period = 30;
|
||||
int period_reduction = global.diff;
|
||||
real side = rng_sign();
|
||||
real w = SLOT_WIDTH * 0.5 * difficulty_value(1.1, 1.25, 1.5, 1.5);
|
||||
real ofs = (SLOT_WIDTH * 0.5 - w);
|
||||
|
||||
if(time == EVENT_DEATH) {
|
||||
enemy_kill_all(&global.enemies);
|
||||
return;
|
||||
for(int t = 0; t < 360;) {
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
play_sfx("shot3");
|
||||
cmplx o = creal(boss->pos) + side * (ofs + (w*i)/cnt) + I*cimag(boss->pos);
|
||||
PROJECTILE(
|
||||
.proto = pp_card,
|
||||
.pos = o,
|
||||
.color = RGB(1.0, 0.0, 1.0),
|
||||
.move = move_accelerated(-3*I, 0.06*I),
|
||||
);
|
||||
t += WAIT(1);
|
||||
}
|
||||
|
||||
side = -side;
|
||||
t += WAIT(period);
|
||||
period = imax(1, period - period_reduction);
|
||||
}
|
||||
}
|
||||
|
||||
if(time < 0) {
|
||||
targetpos = VIEWPORT_W/2.0 + VIEWPORT_H/2.0 * I;
|
||||
return;
|
||||
}
|
||||
DEFINE_EXTERN_TASK(stage2_spell_monty_hall_danmaku) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
|
||||
AT(0) {
|
||||
plr_pos = creal(global.plr.pos) / cwidth;
|
||||
bad_pos = tsrand() % 3;
|
||||
do good_pos = tsrand() % 3; while(good_pos == bad_pos);
|
||||
COEVENTS_ARRAY(goat_trigger) events;
|
||||
TASK_HOST_EVENTS(events);
|
||||
|
||||
play_sound("laser1");
|
||||
boss->move = move_towards(VIEWPORT_W/2.0 + VIEWPORT_H/2.0 * I, 0.06);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
|
||||
int plr_slot;
|
||||
int goat1_slot;
|
||||
int goat2_slot;
|
||||
int win_slot;
|
||||
|
||||
for(;;) {
|
||||
boss->move.attraction_point = VIEWPORT_W/2.0 + VIEWPORT_H/2.0 * I;
|
||||
|
||||
goat1_slot = rng_irange(0, 3);
|
||||
do {
|
||||
win_slot = rng_irange(0, 3);
|
||||
} while(win_slot == goat1_slot);
|
||||
|
||||
goat2_slot = 0;
|
||||
while(goat2_slot == win_slot || goat2_slot == goat1_slot) {
|
||||
goat2_slot++;
|
||||
}
|
||||
assert(goat2_slot < 3);
|
||||
|
||||
for(int i = 0; i < 2; ++i) {
|
||||
int x = cwidth * (1 + i);
|
||||
create_laserline_ab(x, x + VIEWPORT_H |