1 /*
2 * This software is licensed under the terms of the MIT License.
3 * See COPYING for further information.
4 * ---
5 * Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
6 * Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
7 */
8
9 #include "taisei.h"
10
11 #include "stage5_events.h"
12 #include "stage5.h"
13 #include "global.h"
14
stage5_dialog_post_midboss(void)15 static Dialog *stage5_dialog_post_midboss(void) {
16 PlayerMode *pm = global.plr.mode;
17 Dialog *d = dialog_create();
18 dialog_set_image(d, DIALOG_LEFT, pm->character->dialog_base_sprite_name);
19 pm->dialog->stage5_post_midboss(d);
20 assert(d->count == 1);
21 d->actions->timeout = global.frames + 120;
22 return d;
23 }
24
stage5_dialog_pre_boss(void)25 static Dialog *stage5_dialog_pre_boss(void) {
26 PlayerMode *pm = global.plr.mode;
27 Dialog *d = dialog_create();
28 dialog_set_image(d, DIALOG_LEFT, pm->character->dialog_base_sprite_name);
29 dialog_set_image(d, DIALOG_RIGHT, "dialog/iku");
30 pm->dialog->stage5_pre_boss(d);
31 dialog_add_action(d, DIALOG_SET_BGM, "stage5boss");
32 return d;
33 }
34
stage5_dialog_post_boss(void)35 static Dialog *stage5_dialog_post_boss(void) {
36 PlayerMode *pm = global.plr.mode;
37 Dialog *d = dialog_create();
38 dialog_set_image(d, DIALOG_LEFT, pm->character->dialog_base_sprite_name);
39 dialog_set_image(d, DIALOG_RIGHT, "dialog/iku");
40 pm->dialog->stage5_post_boss(d);
41 return d;
42 }
43
stage5_greeter(Enemy * e,int t)44 static int stage5_greeter(Enemy *e, int t) {
45 TIMER(&t)
46 AT(EVENT_KILLED) {
47 spawn_items(e->pos, ITEM_POINTS, 2, ITEM_POWER, 2);
48 return 1;
49 }
50
51 e->moving = true;
52 e->dir = creal(e->args[0]) < 0;
53
54 FROM_TO(0, 50, 1) {
55 GO_TO(e, e->pos0 + e->args[0] * 50, 0.05);
56 }
57
58 if(t > 200)
59 e->pos += e->args[0];
60
61 FROM_TO(80, 180, 20) {
62 for(int i = -(int)global.diff; i <= (int)global.diff; i++) {
63 PROJECTILE(
64 .proto = pp_bullet,
65 .pos = e->pos,
66 .color = RGB(0.0, 0.0, 1.0),
67 .rule = asymptotic,
68 .args = {
69 (3.5+(global.diff == D_Lunatic))*cexp(I*carg(global.plr.pos-e->pos) + 0.06*I*i),
70 5
71 }
72 );
73 }
74
75 play_sound("shot1");
76 }
77
78 return 1;
79 }
80
stage5_lightburst(Enemy * e,int t)81 static int stage5_lightburst(Enemy *e, int t) {
82 TIMER(&t);
83 AT(EVENT_KILLED) {
84 spawn_items(e->pos, ITEM_POINTS, 4, ITEM_POWER, 2);
85 return 1;
86 }
87
88 FROM_TO(0, 70, 1) {
89 GO_TO(e, e->pos0 + e->args[0] * 70, 0.05);
90 }
91
92 if(t > 200)
93 e->pos += e->args[0];
94
95 FROM_TO_SND("shot1_loop", 20, 300, 5) {
96 int c = 5+global.diff;
97 for(int i = 0; i < c; i++) {
98 cmplx n = cexp(I*carg(global.plr.pos) + 2.0*I*M_PI/c*i);
99 PROJECTILE(
100 .proto = pp_ball,
101 .pos = e->pos + 50*n*cexp(-0.4*I*_i*global.diff),
102 .color = RGB(0.3, 0, 0.7),
103 .rule = asymptotic,
104 .args = { 3*n, 3 }
105 );
106 }
107
108 play_sound("shot2");
109 }
110
111 return 1;
112 }
113
stage5_swirl(Enemy * e,int t)114 static int stage5_swirl(Enemy *e, int t) {
115 TIMER(&t);
116 AT(EVENT_KILLED) {
117 spawn_items(e->pos, ITEM_POINTS, 1);
118 return 1;
119 }
120
121 if(t > creal(e->args[1]) && t < cimag(e->args[1]))
122 e->args[0] *= e->args[2];
123
124 e->pos += e->args[0];
125
126 FROM_TO(0, 400, 26-global.diff*4) {
127 for(int i = 1; i >= -1; i -= 2) {
128 PROJECTILE(
129 .proto = pp_bullet,
130 .pos = e->pos,
131 .color = RGB(0.3, 0.4, 0.5),
132 .rule = asymptotic,
133 .args = { i*2*e->args[0]*I/cabs(e->args[0]), 3 }
134 );
135 }
136
137 play_sound("shot1");
138 }
139
140 return 1;
141 }
142
stage5_limiter(Enemy * e,int t)143 static int stage5_limiter(Enemy *e, int t) {
144 TIMER(&t);
145 AT(EVENT_KILLED) {
146 spawn_items(e->pos, ITEM_POINTS, 4, ITEM_POWER, 4);
147 return 1;
148 }
149
150 e->pos += e->args[0];
151
152 FROM_TO_SND("shot1_loop", 0, 1200, 3) {
153 uint f = PFLAG_NOSPAWNFADE;
154 double base_angle = carg(global.plr.pos - e->pos);
155
156 for(int i = 1; i >= -1; i -= 2) {
157 double a = i * 0.2 - 0.1 * (global.diff / 4) + i * 3.0 / (_i + 1);
158 cmplx aim = cexp(I * (base_angle + a));
159
160 PROJECTILE(
161 .proto = pp_rice,
162 .pos = e->pos,
163 .color = RGB(0.5,0.1,0.2),
164 .rule = asymptotic,
165 .args = { 10*aim, 2 },
166 .flags = f,
167 );
168 }
169 }
170
171 return 1;
172 }
173
stage5_laserfairy(Enemy * e,int t)174 static int stage5_laserfairy(Enemy *e, int t) {
175 TIMER(&t)
176 AT(EVENT_KILLED) {
177 spawn_items(e->pos, ITEM_POINTS, 5, ITEM_POWER, 5);
178 return 1;
179 }
180
181 FROM_TO(0, 100, 1) {
182 GO_TO(e, e->pos0 + e->args[0] * 100, 0.05);
183 }
184
185 if(t > 700)
186 e->pos -= e->args[0];
187
188 FROM_TO(100, 700, (7-global.diff)*(1+(int)creal(e->args[1]))) {
189 cmplx n = cexp(I*carg(global.plr.pos-e->pos)+(0.2-0.02*global.diff)*I*_i);
190 float fac = (0.5+0.2*global.diff);
191 create_lasercurve2c(e->pos, 100, 300, RGBA(0.7, 0.3, 1, 0), las_accel, fac*4*n, fac*0.05*n);
192 PROJECTILE(
193 .proto = pp_plainball,
194 .pos = e->pos,
195 .color = RGBA(0.7, 0.3, 1, 0),
196 .rule = accelerated,
197 .args = { fac*4*n, fac*0.05*n }
198 );
199 play_sound_ex("shot_special1", 0, true);
200 }
201
202 return 1;
203 }
204
stage5_miner(Enemy * e,int t)205 static int stage5_miner(Enemy *e, int t) {
206 TIMER(&t);
207 AT(EVENT_KILLED) {
208 spawn_items(e->pos, ITEM_POINTS, 2);
209 return 1;
210 }
211
212 e->pos += e->args[0];
213
214 FROM_TO(0, 600, 5-global.diff/2) {
215 tsrand_fill(2);
216 PROJECTILE(
217 .proto = pp_rice,
218 .pos = e->pos + 20*cexp(2.0*I*M_PI*afrand(0)),
219 .color = RGB(0,0,cabs(e->args[0])),
220 .rule = linear,
221 .args = { cexp(2.0*I*M_PI*afrand(1)) }
222 );
223 play_sound_ex("shot3", 0, false);
224 }
225
226 return 1;
227 }
228
lightning_particle(cmplx pos,int t)229 static void lightning_particle(cmplx pos, int t) {
230 if(!(t % 5)) {
231 char *part = frand() > 0.5 ? "lightning0" : "lightning1";
232 PARTICLE(
233 .sprite = part,
234 .pos = pos,
235 .color = RGBA(1.0, 1.0, 1.0, 0.0),
236 .timeout = 20,
237 .draw_rule = Fade,
238 .flags = PFLAG_REQUIREDPARTICLE,
239 .angle = frand()*2*M_PI,
240 );
241 }
242 }
243
stage5_magnetto(Enemy * e,int t)244 static int stage5_magnetto(Enemy *e, int t) {
245 TIMER(&t);
246
247 AT(EVENT_KILLED) {
248 spawn_items(e->pos, ITEM_POINTS, 5, ITEM_POWER, 5);
249 return 1;
250 }
251
252 if(t < 0) {
253 return 1;
254 }
255
256 cmplx offset = (frand()-0.5)*10 + (frand()-0.5)*10.0*I;
257 lightning_particle(e->pos + 3*offset, t);
258
259 FROM_TO(0, 70, 1) {
260 GO_TO(e, e->args[0], 0.1);
261 }
262
263 AT(140) {
264 play_sound("redirect");
265 play_sound_delayed("redirect", 0, false, 180);
266 }
267
268 FROM_TO(140, 320, 1) {
269 e->pos += 3 * cexp(I*(carg(e->args[1] - e->pos) + M_PI/2));
270 GO_TO(e, e->args[1], pow((t - 140) / 300.0, 3));
271 }
272
273 FROM_TO_SND("shot1_loop", 140, 280, 1 + 2 * (1 + D_Lunatic - global.diff)) {
274 // cmplx dir = cexp(I*carg(global.plr.pos - e->pos));
275
276 for(int i = 0; i < 2 - (global.diff == D_Easy); ++i) {
277 cmplx dir = cexp(I*(M_PI*i + M_PI/8*sin(2*(t-140)/70.0 * M_PI) + carg(e->args[1] - e->pos)));
278
279 PROJECTILE(
280 .proto = pp_ball,
281 .pos = e->pos,
282 .color = RGBA(0.1 + 0.5 * pow((t - 140) / 140.0, 2), 0.0, 0.8, 0.0),
283 .rule = accelerated,
284 .args = {
285 (-2 + (global.diff == D_Hard)) * dir,
286 0.02 * dir * (!i || global.diff != D_Lunatic),
287 },
288 );
289 }
290 }
291
292 AT(380) {
293 return ACTION_DESTROY;
294 }
295
296 return 1;
297 }
298
stage5_explosion(Enemy * e,int t)299 static int stage5_explosion(Enemy *e, int t) {
300 TIMER(&t)
301 AT(EVENT_KILLED) {
302 spawn_items(e->pos,
303 ITEM_POINTS, 5,
304 ITEM_POWER, 5,
305 ITEM_LIFE, creal(e->args[1])
306 );
307
308 PARTICLE(
309 .sprite = "blast_huge_rays",
310 .color = color_add(RGBA(0, 0.2 + 0.5 * frand(), 0.5 + 0.5 * frand(), 0.0), RGBA(1, 1, 1, 0)),
311 .pos = e->pos,
312 .timeout = 60 + 10 * frand(),
313 .draw_rule = ScaleFade,
314 .args = { 0, 0, (0 + 3*I) * (1 + 0.2 * frand()) },
315 .angle = frand() * 2 * M_PI,
316 .layer = LAYER_PARTICLE_HIGH | 0x42,
317 .flags = PFLAG_REQUIREDPARTICLE,
318 );
319
320 PARTICLE(
321 .sprite = "blast_huge_halo",
322 .pos = e->pos,
323 .color = RGBA(0.3 * frand(), 0.3 * frand(), 1.0, 0),
324 .timeout = 200 + 24 * frand(),
325 .draw_rule = ScaleFade,
326 .args = { 0, 0, (0.25 + 2.5*I) * (1 + 0.2 * frand()) },
327 .layer = LAYER_PARTICLE_HIGH | 0x41,
328 .angle = frand() * 2 * M_PI,
329 .flags = PFLAG_REQUIREDPARTICLE,
330 );
331
332 play_sound("boom");
333 return 1;
334 }
335
336 FROM_TO(0, 80, 1) {
337 GO_TO(e, e->pos0 + e->args[0] * 80, 0.05);
338 }
339
340 FROM_TO(90, 300, 7-global.diff) {
341 PROJECTILE(
342 .proto = pp_soul,
343 .pos = e->pos,
344 .color = RGBA(0, 0, 1, 0),
345 .rule = asymptotic,
346 .args = { 4*cexp(0.5*I*_i), 3 }
347 );
348 play_sound("shot_special1");
349 }
350
351 FROM_TO(200, 720, 6-global.diff) {
352 for(int i = 1; i >= -1; i -= 2) {
353 PROJECTILE(
354 .proto = pp_rice,
355 .pos = e->pos,
356 .color = RGB(1,0,0),
357 .rule = asymptotic,
358 .args = { i*2*cexp(-0.3*I*_i+frand()*I), 3 }
359 );
360 }
361
362 play_sound("shot3");
363 }
364
365 FROM_TO(500-30*(global.diff-D_Easy), 800, 100-10*global.diff) {
366 create_laserline(e->pos, 10*cexp(I*carg(global.plr.pos-e->pos)+0.04*I*(1-2*frand())), 60, 120, RGBA(1, 0.3, 1, 0));
367 play_sound_delayed("laser1", 0, true, 45);
368 }
369
370 return 1;
371 }
372
iku_slave_visual(Enemy * e,int t,bool render)373 static void iku_slave_visual(Enemy *e, int t, bool render) {
374 if(render) {
375 return;
376 }
377
378 cmplx offset = (frand()-0.5)*10 + (frand()-0.5)*10.0*I;
379
380 if(e->args[2] && !(t % 5)) {
381 lightning_particle(e->pos + 3*offset, t);
382 }
383
384 if(!(t % 3)) {
385 float alpha = 1;
386
387 if(!e->args[2]) {
388 alpha *= 0.03;
389 }
390
391 Color *clr = RGBA_MUL_ALPHA(0.1*alpha, 0.1*alpha, 0.6*alpha, 0.5*alpha);
392 clr->a = 0;
393
394 PARTICLE(
395 .sprite = "lightningball",
396 .pos = 0,
397 .color = clr,
398 .draw_rule = Fade,
399 .rule = enemy_flare,
400 .timeout = 50,
401 .args = { offset*0.1, add_ref(e) },
402 .flags = PFLAG_REQUIREDPARTICLE,
403 );
404 }
405 }
406
iku_mid_intro(Boss * b,int t)407 static void iku_mid_intro(Boss *b, int t) {
408 TIMER(&t);
409
410 b->pos += -1-7.0*I+10*t*(cimag(b->pos)<-200);
411
412 FROM_TO(90, 110, 10) {
413 create_enemy3c(b->pos, ENEMY_IMMUNE, iku_slave_visual, stage5_explosion, -2-0.5*_i+I*_i, _i == 1,1);
414 }
415
416 AT(960)
417 enemy_kill_all(&global.enemies);
418 }
419
midboss_dummy(Boss * b,int t)420 static void midboss_dummy(Boss *b, int t) { }
421
create_iku_mid(void)422 static Boss *create_iku_mid(void) {
423 Boss *b = create_boss("Bombs?", "iku_mid", 0, VIEWPORT_W+800.0*I);
424 b->glowcolor = *RGB(0.2, 0.4, 0.5);
425 b->shadowcolor = *RGBA_MUL_ALPHA(0.65, 0.2, 0.75, 0.5);
426
427 Attack *a = boss_add_attack(b, AT_SurvivalSpell, "Discharge Bombs", 16, 10, iku_mid_intro, NULL);
428 boss_set_attack_bonus(a, 5);
429
430 // suppress the boss death effects (this triggers the "boss fleeing" case)
431 boss_add_attack(b, AT_Move, "", 0, 0, midboss_dummy, NULL);
432
433 return b;
434 }
435
stage5_lightburst2(Enemy * e,int t)436 static int stage5_lightburst2(Enemy *e, int t) {
437 TIMER(&t);
438 AT(EVENT_KILLED) {
439 spawn_items(e->pos, ITEM_POINTS, 4, ITEM_POWER, 4);
440 return 1;
441 }
442
443 FROM_TO(0, 70, 1) {
444 GO_TO(e, e->pos0 + e->args[0] * 70, 0.05);
445 }
446
447 if(t > 200)
448 e->pos += e->args[0];
449
450 FROM_TO_SND("shot1_loop", 20, 170, 5) {
451 int i;
452 int c = 4+global.diff-(global.diff==D_Easy);
453 for(i = 0; i < c; i++) {
454 tsrand_fill(2);
455 cmplx n = cexp(I*carg(global.plr.pos-e->pos) + 2.0*I*M_PI/c*i);
456 PROJECTILE(
457 .proto = pp_bigball,
458 .pos = e->pos + 50*n*cexp(-1.0*I*_i*global.diff),
459 .color = RGB(0.3, 0, 0.7+0.3*(_i&1)),
460 .rule = asymptotic,
461 .args = {
462 2.5*n+0.25*global.diff*afrand(0)*cexp(2.0*I*M_PI*afrand(1)),
463 3
464 }
465 );
466 }
467
468 play_sound("shot2");
469 }
470
471 return 1;
472 }
473
stage5_superbullet(Enemy * e,int t)474 static int stage5_superbullet(Enemy *e, int t) {
475 TIMER(&t);
476 AT(EVENT_KILLED) {
477 spawn_items(e->pos, ITEM_POINTS, 4, ITEM_POWER, 3);
478 return 1;
479 }
480
481 FROM_TO(0, 70, 1) {
482 GO_TO(e, e->pos0 + e->args[0] * 70, 0.05);
483 }
484
485 FROM_TO(60, 200, 1) {
486 cmplx n = cexp(I*M_PI*sin(_i/(8.0+global.diff)+frand()*0.1)+I*carg(global.plr.pos-e->pos));
487 PROJECTILE(
488 .proto = pp_bullet,
489 .pos = e->pos + 50*n,
490 .color = RGB(0.6, 0, 0),
491 .rule = asymptotic,
492 .args = { 2*n, 10 }
493 );
494 play_sound("shot1");
495 }
496
497 FROM_TO(260, 400, 1)
498 e->pos -= e->args[0];
499 return 1;
500 }
501
iku_intro(Boss * b,int t)502 static void iku_intro(Boss *b, int t) {
503 GO_TO(b, VIEWPORT_W/2+240.0*I, 0.015);
504
505 if(t == 160)
506 global.dialog = stage5_dialog_pre_boss();
507 }
508
cloud_common(void)509 static void cloud_common(void) {
510 tsrand_fill(4);
511 float v = (afrand(2)+afrand(3))*0.5+1.0;
512
513 PROJECTILE(
514 // FIXME: add prototype, or shove it into the basic ones somehow,
515 // or just replace this with some thing else
516 .sprite_ptr = get_sprite("part/lightningball"),
517 .size = 48 * (1+I),
518 .collision_size = 21.6 * (1+I),
519
520 .pos = VIEWPORT_W*afrand(0)-15.0*I,
521 .color = RGBA_MUL_ALPHA(0.2, 0.0, 0.4, 0.6),
522 .rule = accelerated,
523 .args = {
524 1-2*afrand(1)+v*I,
525 -0.01*I
526 },
527 .shader = "sprite_default",
528 );
529 }
530
iku_bolts(Boss * b,int time)531 static void iku_bolts(Boss *b, int time) {
532 int t = time % 400;
533 TIMER(&t);
534
535 FROM_TO(0, 400, 2) {
536 cloud_common();
537 }
538
539 FROM_TO(60, 400, 50) {
540 int i, c = 10+global.diff;
541
542 for(i = 0; i < c; i++) {
543 PROJECTILE(
544 .proto = pp_ball,
545 .pos = b->pos,
546 .color = RGBA(0.4, 1.0, 1.0, 0),
547 .rule = asymptotic,
548 .args = {
549 (i+2)*0.4*cexp(I*carg(global.plr.pos-b->pos))+0.2*(global.diff-1)*frand(),
550 3
551 },
552 );
553 }
554
555 play_sound("shot2");
556 play_sound("redirect");
557 }
558
559 FROM_TO(0, 70, 1)
560 GO_TO(b, 100+300.0*I, 0.02);
561
562 FROM_TO(100, 200, 1)
563 GO_TO(b, VIEWPORT_W/2+100.0*I, 0.02);
564
565 FROM_TO(230, 300, 1)
566 GO_TO(b, VIEWPORT_W-100+300.0*I, 0.02);
567
568 FROM_TO(330, 400, 1)
569 GO_TO(b, VIEWPORT_W/2+100.0*I, 0.02);
570
571 }
572
iku_atmospheric(Boss * b,int time)573 void iku_atmospheric(Boss *b, int time) {
574 if(time < 0) {
575 GO_TO(b, VIEWPORT_W/2+200.0*I, 0.06);
576 return;
577 }
578
579 int t = time % 500;
580 TIMER(&t);
581
582 GO_TO(b,VIEWPORT_W/2+tanh(sin(time/100))*(200-100*(global.diff==D_Easy))+I*VIEWPORT_H/3+I*(cos(t/200)-1)*50,0.03);
583
584 FROM_TO(0, 500, 23-2*global.diff) {
585 tsrand_fill(4);
586 cmplx p1 = VIEWPORT_W*afrand(0) + VIEWPORT_H/2*I*afrand(1);
587 cmplx p2 = p1 + (120+20*global.diff)*cexp(0.5*I-afrand(2)*I)*(1-2*(afrand(3) > 0.5));
588
589 int i;
590 int c = 6+global.diff;
591
592 for(i = -c*0.5; i <= c*0.5; i++) {
593 PROJECTILE(
594 .proto = pp_ball,
595 .pos = p1+(p2-p1)/c*i,
596 .color = RGBA(1-1/(1+fabs(0.1*i)), 0.5-0.1*abs(i), 1, 0),
597 .rule = accelerated,
598 .args = {
599 0, (0.004+0.001*global.diff)*cexp(I*carg(p2-p1)+I*M_PI/2+0.2*I*i)
600 },
601 );
602 }
603
604 play_sound("shot_special1");
605 play_sound("redirect");
606 }
607
608 FROM_TO(0, 500, 7-global.diff) {
609 if(global.diff >= D_Hard) {
610 PROJECTILE(
611 .proto = pp_thickrice,
612 .pos = VIEWPORT_W*frand(),
613 .color = RGB(0,0.3,0.7),
614 .rule = accelerated,
615 .args = { 0, 0.01*I }
616 );
617 }
618 else {
619 PROJECTILE(
620 .proto = pp_rice,
621 .pos = VIEWPORT_W*frand(),
622 .color = RGB(0,0.3,0.7),
623 .rule = linear,
624 .args = { 2*I }
625 );
626 }
627 }
628 }
629
bolts2_laser(Laser * l,float t)630 static cmplx bolts2_laser(Laser *l, float t) {
631 if(t == EVENT_BIRTH) {
632 l->shader = r_shader_get_optional("lasers/iku_lightning");
633 return 0;
634 }
635
636 double diff = creal(l->args[2]);
637 return creal(l->args[0])+I*cimag(l->pos) + sign(cimag(l->args[0]-l->pos))*0.06*I*t*t + (20+4*diff)*sin(t*0.025*diff+creal(l->args[0]))*l->args[1];
638 }
639
iku_bolts2(Boss * b,int time)640 static void iku_bolts2(Boss *b, int time) {
641 int t = time % 400;
642 TIMER(&t);
643
644 // FIXME: ANOTHER one of these... get rid of this hack when attacks have proper state
645 static bool flip_laser;
646
647 if(time == EVENT_BIRTH) {
648 flip_laser = true;
649 }
650
651 FROM_TO(0, 400, 2) {
652 cloud_common();
653 }
654
655 FROM_TO(0, 400, 60) {
656 flip_laser = !flip_laser;
657 aniplayer_queue(&b->ani, flip_laser ? "dashdown_left" : "dashdown_right", 1);
658 aniplayer_queue(&b->ani, "main", 0);
659 create_lasercurve3c(creal(global.plr.pos), 100, 200, RGBA(0.3, 1, 1, 0), bolts2_laser, global.plr.pos, flip_laser*2-1, global.diff);
660 play_sound_ex("laser1", 0, false);
661 }
662
663 FROM_TO_SND("shot1_loop", 0, 400, 5-global.diff)
664 if(frand() < 0.9) {
665 PROJECTILE(
666 .proto = pp_plainball,
667 .pos = b->pos,
668 .color = RGB(0.2,0,0.8),
669 .rule = linear,
670 .args = { cexp(0.1*I*_i) }
671 );
672 }
673
674 FROM_TO(0, 70, 1)
675 GO_TO(b, 100+200.0*I, 0.02);
676
677 FROM_TO(230, 300, 1)
678 GO_TO(b, VIEWPORT_W-100+200.0*I, 0.02);
679
680 }
681
lightning_slave(Enemy * e,int t)682 static int lightning_slave(Enemy *e, int t) {
683 if(t < 0)
684 return 1;
685 if(t > 200)
686 return ACTION_DESTROY;
687
688 TIMER(&t);
689
690 e->pos += e->args[0];
691
692 FROM_TO(0,200,20)
693 e->args[0] *= cexp(I * (0.25 + 0.25 * frand() * M_PI));
694
695 FROM_TO(0, 200, 3)
696 if(cabs(e->pos-global.plr.pos) > 60) {
697 Color *clr = RGBA(1-1/(1+0.01*_i), 0.5-0.01*_i, 1, 0);
698
699 Projectile *p = PROJECTILE(
700 .proto = pp_wave,
701 .pos = e->pos,
702 .color = clr,
703 .rule = asymptotic,
704 .args = {
705 0.75*e->args[0]/cabs(e->args[0])*I,
706 10
707 },
708 );
709
710 if(projectile_in_viewport(p)) {
711 for(int i = 0; i < 3; ++i) {
712 tsrand_fill(2);
713 lightning_particle(p->pos + 5 * afrand(0) * cexp(I*M_PI*2*afrand(1)), 0);
714 }
715
716 play_sound_ex("shot3", 0, false);
717 // play_sound_ex("redirect", 0, true);
718 }
719 }
720
721 return 1;
722 }
723
zigzag_bullet(Projectile * p,int t)724 static int zigzag_bullet(Projectile *p, int t) {
725 if(t < 0) {
726 return ACTION_ACK;
727 }
728
729 int l = 50;
730 p->pos = p->pos0+(abs(((2*t)%l)-l/2)*I+t)*2*p->args[0];
731
732 if(t%2 == 0) {
733 PARTICLE(
734 .sprite = "lightningball",
735 .pos = p->pos,
736 .color = RGBA(0.1, 0.1, 0.6, 0.0),
737 .timeout = 15,
738 .draw_rule = Fade,
739 );
740 }
741
742 return ACTION_NONE;
743 }
744
iku_lightning(Boss * b,int time)745 void iku_lightning(Boss *b, int time) {
746 int t = time % 141;
747
748 if(time == EVENT_DEATH) {
749 enemy_kill_all(&global.enemies);
750 return;
751 }
752
753 if(time < 0) {
754 GO_TO(b, BOSS_DEFAULT_GO_POS, 0.03);
755 return;
756 }
757
758 TIMER(&t);
759
760 GO_TO(b,VIEWPORT_W/2+tanh(sin(time/100))*200+I*VIEWPORT_H/3+I*(cos(t/200)-1)*50,0.03);
761
762 AT(0) {
763 play_sound("charge_generic");
764 }
765
766 FROM_TO(0, 60, 1) {
767 cmplx n = cexp(2.0*I*M_PI*frand());
768 float l = 150*frand()+50;
769 float s = 4+_i*0.01;
770 float alpha = 0.5;
771
772 PARTICLE(
773 .sprite = "lightningball",
774 .pos = b->pos+l*n,
775 .color = RGBA(0.1*alpha, 0.1*alpha, 0.6*alpha, 0),
776 .draw_rule = Fade,
777 .rule = linear,
778 .timeout = l/s,
779 .args = { -s*n },
780 );
781 }
782
783 if(global.diff >= D_Hard && time > 0 && !(time%100)) {
784 int c = 7 + 2 * (global.diff == D_Lunatic);
785 for(int i = 0; i<c; i++) {
786 PROJECTILE(
787 .proto = pp_bigball,
788 .pos = b->pos,
789 .color = RGBA(0.5, 0.1, 1.0, 0.0),
790 .rule = zigzag_bullet,
791 .args = { cexp(2*M_PI*I/c*i+I*carg(global.plr.pos-b->pos)) },
792 );
793 }
794
795 play_sound("redirect");
796 play_sound("shot_special1");
797 }
798
799 AT(100) {
800 aniplayer_hard_switch(&b->ani, ((time/141)&1) ? "dashdown_left" : "dashdown_right",1);
801 aniplayer_queue(&b->ani, "main", 0);
802 int c = 40;
803 int l = 200;
804 int s = 10;
805
806 for(int i=0; i < c; i++) {
807 cmplx n = cexp(2.0*I*M_PI*frand());
808 PARTICLE(
809 .sprite = "smoke",
810 .pos = b->pos,
811 .color = RGBA(0.4, 0.4, 1.0, 0.0),
812 .draw_rule = Fade,
813 .rule = linear,
814 .timeout = l/s,
815 .args = { s*n },
816 );
817 }
818
819 for(int i = 0; i < global.diff+1; i++){
820 create_enemy1c(b->pos, ENEMY_IMMUNE, NULL, lightning_slave, 10*cexp(I*carg(global.plr.pos - b->pos)+2.0*I*M_PI/(global.diff+1)*i));
821 }
822
823 play_sound("shot_special1");
824 }
825 }
826
iku_bolts3(Boss * b,int time)827 static void iku_bolts3(Boss *b, int time) {
828 int t = time % 400;
829 TIMER(&t);
830
831 FROM_TO(0, 400, 2) {
832 cloud_common();
833 }
834
835 FROM_TO(60, 400, 60) {
836 aniplayer_queue(&b->ani, (_i&1) ? "dashdown_left" : "dashdown_right",1);
837 aniplayer_queue(&b->ani, "main", 0);
838 int i, c = 10+global.diff;
839 cmplx n = cexp(I*carg(global.plr.pos-b->pos)+0.1*I-0.2*I*frand());
840 for(i = 0; i < c; i++) {
841 PROJECTILE(
842 .proto = pp_ball,
843 .pos = b->pos,
844 .color = RGBA(0.4, 1.0, 1.0, 0.0),
845 .rule = asymptotic,
846 .args = {
847 (i+2)*0.4*n+0.2*(global.diff-1)*frand(),
848 3
849 },
850 );
851 }
852
853 play_sound("shot2");
854 play_sound("redirect");
855 }
856
857 FROM_TO_SND("shot1_loop", 0, 400, 5-global.diff)
858 if(frand() < 0.9) {
859 PROJECTILE(
860 .proto = pp_plainball,
861 .pos = b->pos,
862 .color = RGB(0.2,0,0.8),
863 .rule = linear,
864 .args = { cexp(0.1*I*_i) }
865 );
866 }
867
868 FROM_TO(0, 70, 1)
869 GO_TO(b, 100+200.0*I, 0.02);
870
871 FROM_TO(230, 300, 1)
872 GO_TO(b, VIEWPORT_W-100+200.0*I, 0.02);
873
874 }
875
induction_bullet_traj(Projectile * p,float t)876 static cmplx induction_bullet_traj(Projectile *p, float t) {
877 return p->pos0 + p->args[0]*t*cexp(p->args[1]*t);
878 }
879
induction_bullet(Projectile * p,int time)880 static int induction_bullet(Projectile *p, int time) {
881 if(time < 0) {
882 return ACTION_ACK;
883 }
884
885 float t = time*sqrt(global.diff);
886
887 if(global.diff > D_Normal && !p->args[2]) {
888 t = time*0.6;
889 t = 230-t;
890 if(t < 0)
891 return ACTION_DESTROY;
892 }
893
894 p->pos = induction_bullet_traj(p,t);
895
896 if(time == 0) {
897 // don't lerp; the spawn position is very different on hard/lunatic and would cause false hits
898 p->prevpos = p->pos;
899 }
900
901 p->angle = carg(p->args[0]*cexp(p->args[1]*t)*(1+p->args[1]*t));
902 return 1;
903 }
904
cathode_laser(Laser * l,float t)905 static cmplx cathode_laser(Laser *l, float t) {
906 if(t == EVENT_BIRTH) {
907 l->shader = r_shader_get_optional("lasers/iku_cathode");
908 return 0;
909 }
910
911 l->args[1] = I*cimag(l->args[1]);
912
913 return l->pos + l->args[0]*t*cexp(l->args[1]*t);
914 }
915
iku_cathode(Boss * b,int t)916 void iku_cathode(Boss *b, int t) {
917 GO_TO(b, VIEWPORT_W/2+200.0*I, 0.02);
918
919 TIMER(&t)
920
921 FROM_TO(50, 18000, 70-global.diff*10) {
922 aniplayer_hard_switch(&b->ani, (_i&1) ? "dashdown_left" : "dashdown_right",1);
923 aniplayer_queue(&b->ani,"main",0);
924
925 int i;
926 int c = 7+global.diff/2;
927
928 double speedmod = 1-0.3*(global.diff == D_Lunatic);
929 for(i = 0; i < c; i++) {
930 PROJECTILE(
931 .proto = pp_bigball,
932 .pos = b->pos,
933 .color = RGBA(0.2, 0.4, 1.0, 0.0),
934 .rule = induction_bullet,
935 .args = {
936 speedmod*2*cexp(2.0*I*M_PI*frand()),
937 speedmod*0.01*I*(1-2*(_i&1)),
938 1
939 },
940 );
941 if(i < c*3/4)
942 create_lasercurve2c(b->pos, 60, 200, RGBA(0.4, 1, 1, 0), cathode_laser, 2*cexp(2.0*I*M_PI*M_PI*frand()), 0.015*I*(1-2*(_i&1)));
943 }
944
945 // XXX: better ideas?
946 play_sound("shot_special1");
947 play_sound("redirect");
948 play_sound("shot3");
949 play_sound("shot2");
950 }
951 }
952
iku_induction(Boss * b,int t)953 void iku_induction(Boss *b, int t) {
954 // thwarf safespots
955 cmplx ofs = global.diff > D_Normal ? 10*I : 0;
956
957 GO_TO(b, VIEWPORT_W/2+200.0*I + ofs, 0.03);
958
959 if(t < 0) {
960 return;
961 }
962
963 TIMER(&t);
964 AT(0) {
965 aniplayer_queue(&b->ani, "dashdown_wait", 0);
966 }
967
968 FROM_TO_SND("shot1_loop", 0, 18000, 8) {
969 play_sound("redirect");
970
971 int i,j;
972 int c = 6;
973 int c2 = 6-(global.diff/4);
974 for(i = 0; i < c; i++) {
975 for(j = 0; j < 2; j++) {
976 Color *clr = RGBA(1-1/(1+0.1*(_i%c2)), 0.5-0.1*(_i%c2), 1.0, 0.0);
977 float shift = 0.6*(_i/c2);
978 float a = -0.0002*(global.diff-D_Easy);
979 if(global.diff == D_Hard)
980 a += 0.0005;
981 PROJECTILE(
982 .proto = pp_ball,
983 .pos = b->pos,
984 .color = clr,
985 .rule = induction_bullet,
986 .args = {
987 2*cexp(2.0*I*M_PI/c*i+I*M_PI/2+I*shift),
988 (0.01+0.001*global.diff)*I*(1-2*j)+a
989 },
990 .max_viewport_dist = 400*(global.diff>=D_Hard),
991 );
992 }
993 }
994
995 }
996 }
997
998 void iku_spell_bg(Boss *b, int t);
999
iku_extra_find_next_slave(cmplx from,double playerbias)1000 static Enemy* iku_extra_find_next_slave(cmplx from, double playerbias) {
1001 Enemy *nearest = NULL, *e;
1002 double dist, mindist = INFINITY;
1003
1004 cmplx org = from + playerbias * cexp(I*(carg(global.plr.pos - from)));
1005
1006 for(e = global.enemies.first; e; e = e->next) {
1007 if(e->args[2]) {
1008 continue;
1009 }
1010
1011 dist = cabs(e->pos - org);
1012
1013 if(dist < mindist) {
1014 nearest = e;
1015 mindist = dist;
1016 }
1017 }
1018
1019 return nearest;
1020 }
1021
iku_extra_slave_visual(Enemy * e,int t,bool render)1022 static void iku_extra_slave_visual(Enemy *e, int t, bool render) {
1023 iku_slave_visual(e, t, render);
1024
1025 if(render) {
1026 return;
1027 }
1028
1029 if(e->args[2] && !(t % 5)) {
1030 cmplx offset = (frand()-0.5)*30 + (frand()-0.5)*20.0*I;
1031 PARTICLE(
1032 .sprite = "smoothdot",
1033 .pos = offset,
1034 .color = e->args[1] ? RGBA(1.0, 0.5, 0.0, 0.0) : RGBA(0.0, 0.5, 0.5, 0.0),
1035 .draw_rule = Shrink,
1036 .rule = enemy_flare,
1037 .timeout = 50,
1038 .args = {
1039 (-50.0*I-offset)/50.0,
1040 add_ref(e)
1041 },
1042 );
1043 }
1044 }
1045
iku_extra_trigger_bullet(Projectile * p,int t)1046 static int iku_extra_trigger_bullet(Projectile *p, int t) {
1047 if(t == EVENT_DEATH) {
1048 free_ref(p->args[1]);
1049 return ACTION_ACK;
1050 }
1051
1052 if(t < 0) {
1053 return ACTION_ACK;
1054 }
1055
1056 Enemy *target = REF(p->args[1]);
1057
1058 if(!target) {
1059 return ACTION_DESTROY;
1060 }
1061
1062 if(creal(p->args[2]) < 0) {
1063 linear(p, t);
1064 if(cabs(p->pos - target->pos) < 5) {
1065 p->pos = target->pos;
1066 target->args[1] = 1;
1067 p->args[2] = 55 - 5 * global.diff;
1068 target->args[3] = global.frames + p->args[2];
1069 play_sound("shot_special1");
1070 }
1071 } else {
1072 p->args[2] = approach(creal(p->args[2]), 0, 1);
1073 play_loop("charge_generic");
1074 }
1075
1076 if(creal(p->args[2]) == 0) {
1077 int cnt = 6 + 2 * global.diff;
1078 for(int i = 0; i < cnt; ++i) {
1079 cmplx dir = cexp(I*(t + i*2*M_PI/cnt));
1080
1081 PROJECTILE(
1082 .proto = pp_bigball,
1083 .pos = p->pos,
1084 .color = RGBA(1.0, 0.5, 0.0, 0.0),
1085 .rule = asymptotic,
1086 .args = { 1.1*dir, 5 },
1087 );
1088
1089 PROJECTILE(
1090 .proto = pp_bigball,
1091 .pos = p->pos,
1092 .color = RGBA(0.0, 0.5, 1.0, 0.0),
1093 .rule = asymptotic,
1094 .args = { dir, 10 },
1095 );
1096 }
1097 global.shake_view += 5;
1098 global.shake_view_fade = 0.2;
1099 aniplayer_hard_switch(&global.boss->ani,"main_mirror",0);
1100 play_sound("boom");
1101 return ACTION_DESTROY;
1102 }
1103
1104 p->angle = global.frames + t;
1105
1106 tsrand_fill(5);
1107
1108 PARTICLE(
1109 .sprite = afrand(0) > 0.5 ? "lightning0" : "lightning1",
1110 .pos = p->pos + 3 * (anfrand(1)+I*anfrand(2)),
1111 .angle = afrand(3) * 2 * M_PI,
1112 .color = RGBA(1.0, 0.7 + 0.2 * anfrand(4), 0.4, 0.0),
1113 .timeout = 20,
1114 .draw_rule = GrowFade,
1115 .args = { 0, 2.4 },
1116 );
1117
1118 return ACTION_NONE;
1119 }
1120
iku_extra_fire_trigger_bullet(void)1121 static void iku_extra_fire_trigger_bullet(void) {
1122 Enemy *e = iku_extra_find_next_slave(global.boss->pos, 250);
1123
1124 aniplayer_hard_switch(&global.boss->ani,"dashdown_left",1);
1125 aniplayer_queue(&global.boss->ani,"main",0);
1126 if(!e) {
1127 return;
1128 }
1129
1130 Boss *b = global.boss;
1131
1132 PROJECTILE(
1133 .proto = pp_soul,
1134 .pos = b->pos,
1135 .color = RGBA(0.2, 0.2, 1.0, 0.0),
1136 .rule = iku_extra_trigger_bullet,
1137 .args = {
1138 3*cexp(I*carg(e->pos - b->pos)),
1139 add_ref(e),
1140 -1
1141 },
1142 .flags = PFLAG_NOCLEAR,
1143 );
1144
1145 play_sound("shot_special1");
1146 play_sound("enemydeath");
1147 play_sound("shot2");
1148 }
1149
iku_extra_slave(Enemy * e,int t)1150 static int iku_extra_slave(Enemy *e, int t) {
1151 GO_TO(e, e->args[0], 0.05);
1152
1153 if(e->args[1]) {
1154 if(creal(e->args[1]) < 2) {
1155 e->args[1] += 1;
1156 return 0;
1157 }
1158
1159 if(global.frames == creal(e->args[3])) {
1160 cmplx o2 = e->args[2];
1161 e->args[2] = 0;
1162 Enemy *new = iku_extra_find_next_slave(e->pos, 75);
1163 e->args[2] = o2;
1164
1165 if(new && e != new) {
1166 e->args[1] = 0;
1167 e->args[2] = new->args[2] = 600;
1168 new->args[1] = 1;
1169 new->args[3] = global.frames + 55 - 5 * global.diff;
1170
1171 Laser *l = create_laserline_ab(e->pos, new->pos, 10, 30, e->args[2], RGBA(0.3, 1, 1, 0));
1172 l->ent.draw_layer = LAYER_LASER_LOW;
1173 l->unclearable = true;
1174
1175 if(global.diff > D_Easy) {
1176 int cnt = floor(global.diff * 2.5), i;
1177 double r = frand() * 2 * M_PI;
1178
1179 for(i = 0; i < cnt; ++i) {
1180 PROJECTILE(
1181 .proto = pp_rice,
1182 .pos = e->pos,
1183 .color = RGBA(1, 1, 0, 0),
1184 .rule = asymptotic,
1185 .args = { 2*cexp(I*(r+i*2*M_PI/cnt)), 2 },
1186 );
1187 }
1188
1189 play_sound("shot2");
1190 }
1191
1192 play_sound("redirect");
1193 } else {
1194 Enemy *o;
1195 Laser *l;
1196 int cnt = 6 + 2 * global.diff, i;
1197
1198 global.shake_view = 0;
1199 global.shake_view_fade = 0.2;
1200
1201 e->args[2] = 1;
1202
1203 for(o = global.enemies.first; o; o = o->next) {
1204 if(!o->args[2])
1205 continue;
1206
1207 for(i = 0; i < cnt; ++i) {
1208 PROJECTILE(
1209 .proto = pp_ball,
1210 .pos = o->pos,
1211 .color = RGBA(0, 1, 1, 0),
1212 .rule = asymptotic,
1213 .args = { 1.5*cexp(I*(t + i*2*M_PI/cnt)), 8},
1214 );
1215 }
1216
1217 o->args[1] = 0;
1218 o->args[2] = 0;
1219
1220 global.shake_view += 1;
1221 }
1222
1223 for(l = global.lasers.first; l; l = l->next) {
1224 l->deathtime = global.frames - l->birthtime + 20;
1225 }
1226 play_sound("boom");
1227 iku_extra_fire_trigger_bullet();
1228 }
1229 }
1230 }
1231
1232 if(e->args[2]) {
1233 e->args[2] -= 1;
1234 }
1235
1236 return 0;
1237 }
1238
iku_extra(Boss * b,int t)1239 void iku_extra(Boss *b, int t) {
1240 TIMER(&t);
1241
1242 AT(EVENT_DEATH) {
1243 enemy_kill_all(&global.enemies);
1244 }
1245
1246 if(t < 0) {
1247 return;
1248 }
1249
1250 GO_TO(b, VIEWPORT_W/2+50.0*I, 0.02);
1251
1252 AT(0) {
1253 int i, j;
1254 int cnt = 5;
1255 double step = VIEWPORT_W / (double)cnt;
1256
1257 for(i = 0; i < cnt; ++i) {
1258 for(j = 0; j < cnt; ++j) {
1259 cmplx epos = step * (0.5 + i) + (step * j + 125) * I;
1260 create_enemy4c(b->pos, ENEMY_IMMUNE, iku_extra_slave_visual, iku_extra_slave, epos, 0, 0, 1);
1261 }
1262 }
1263 }
1264
1265 AT(60) {
1266 iku_extra_fire_trigger_bullet();
1267 }
1268 }
1269
stage5_spawn_iku(cmplx pos)1270 Boss* stage5_spawn_iku(cmplx pos) {
1271 Boss *b = create_boss("Nagae Iku", "iku", "dialog/iku", pos);
1272 b->glowcolor = *RGBA_MUL_ALPHA(0.2, 0.4, 0.5, 0.5);
1273 b->shadowcolor = *RGBA_MUL_ALPHA(0.65, 0.2, 0.75, 0.5);
1274 return b;
1275 }
1276
create_iku(void)1277 static Boss* create_iku(void) {
1278 Boss *b = stage5_spawn_iku(VIEWPORT_W/2-200.0*I);
1279
1280 boss_add_attack(b, AT_Move, "Introduction", 4, 0, iku_intro, NULL);
1281 boss_add_attack(b, AT_Normal, "Bolts1", 40, 24000, iku_bolts, NULL);
1282 boss_add_attack_from_info(b, &stage5_spells.boss.atmospheric_discharge, false);
1283 boss_add_attack(b, AT_Normal, "Bolts2", 45, 27000, iku_bolts2, NULL);
1284 boss_add_attack_from_info(b, &stage5_spells.boss.artificial_lightning, false);
1285 boss_add_attack(b, AT_Normal, "Bolts3", 50, 30000, iku_bolts3, NULL);
1286
1287 if(global.diff < D_Hard) {
1288 boss_add_attack_from_info(b, &stage5_spells.boss.induction_field, false);
1289 } else {
1290 boss_add_attack_from_info(b, &stage5_spells.boss.inductive_resonance, false);
1291 }
1292 boss_add_attack_from_info(b, &stage5_spells.boss.natural_cathode, false);
1293
1294 boss_add_attack_from_info(b, &stage5_spells.extra.overload, false);
1295
1296 return b;
1297 }
1298
stage5_events(void)1299 void stage5_events(void) {
1300 TIMER(&global.timer);
1301
1302 AT(0) {
1303 stage_start_bgm("stage5");
1304 stage5_skip(env_get("STAGE5_TEST", 0));
1305 stage_set_voltage_thresholds(255, 480, 860, 1250);
1306 }
1307
1308 FROM_TO(60, 150, 15) {
1309 create_enemy1c(VIEWPORT_W*(_i&1)+70.0*I+50*_i*I, 400, Fairy, stage5_greeter, 3-6*(_i&1));
1310 }
1311
1312 FROM_TO(270, 320, 40)
1313 create_enemy1c(VIEWPORT_W/4+VIEWPORT_W/2*_i, 2000, BigFairy, stage5_lightburst, 2.0*I);
1314
1315 FROM_TO(400, 600, 10) {
1316 tsrand_fill(2);
1317 create_enemy3c(200.0*I*afrand(0), 500, Swirl, stage5_swirl, 4+I, 70+20*afrand(1)+200.0*I, cexp(-0.05*I));
1318 }
1319
1320 FROM_TO(700, 800, 10) {
1321 tsrand_fill(3);
1322 create_enemy3c(VIEWPORT_W+200.0*I*afrand(0), 500, Swirl, stage5_swirl, -4+afrand(1)*I, 70+20*afrand(2)+200.0*I, cexp(0.05*I));
1323 }
1324
1325 FROM_TO(870+50*(global.diff==D_Easy), 1000, 50)
1326 create_enemy1c(VIEWPORT_W/4+VIEWPORT_W/2*(_i&1), 2000, BigFairy, stage5_limiter, I);
1327
1328 AT(1000)
1329 create_enemy1c(VIEWPORT_W/2, 9000, BigFairy, stage5_laserfairy, 2.0*I);
1330
1331 FROM_TO(1400+100*(D_Lunatic - global.diff), 2560, 60-5*global.diff) {
1332 tsrand_fill(2);
1333 create_enemy1c(VIEWPORT_W+200.0*I*afrand(0), 500, Swirl, stage5_miner, -3+2.0*I*afrand(1));
1334 }
1335
1336 FROM_TO(1500, 2400, 80)
1337 create_enemy1c(VIEWPORT_W*(_i&1)+100.0*I, 300, Fairy, stage5_greeter, 3-6*(_i&1));
1338
1339 AT(2500) {
1340 create_enemy1c(VIEWPORT_W/2, 2000, BigFairy, stage5_lightburst, 2.0*I);
1341 }
1342
1343 FROM_TO(2200, 2600, 60-8*global.diff)
1344 create_enemy1c(VIEWPORT_W/(6+global.diff)*_i, 200, Swirl, stage5_miner, 3.0*I);
1345
1346 AT(2700) {
1347 if(global.diff > D_Easy) {
1348 create_enemy1c(VIEWPORT_W-20+120*I, 2000, BigFairy, stage5_lightburst, -2.0);
1349 }
1350 }
1351
1352 AT(2900)
1353 global.boss = create_iku_mid();
1354
1355 AT(2920) {
1356 global.dialog = stage5_dialog_post_midboss();
1357
1358 // XXX: this shitty hack is needed to force the dialog to not reopen immediately when it's closed with the shot button
1359 global.timer++;
1360 }
1361
1362 FROM_TO(3000, 3200, 100) {
1363 create_enemy1c(VIEWPORT_W/2 + VIEWPORT_W/6*(1-2*(_i&1)), 2000, BigFairy, stage5_lightburst2, -1+2*(_i&1) + 2.0*I);
1364 }
1365
1366 FROM_TO(3300, 4000, 90-10*global.diff)
1367 create_enemy1c(200.0*I+VIEWPORT_W*(_i&1), 1500, Fairy, stage5_superbullet, 3-6*(_i&1));
1368
1369 AT(3400) {
1370 create_enemy2c(VIEWPORT_W/4, 6000, BigFairy, stage5_laserfairy, 2.0*I, 1);
1371 create_enemy2c(VIEWPORT_W/4*3, 6000, BigFairy, stage5_laserfairy, 2.0*I, 1);
1372 }
1373
1374 FROM_TO(4200, 5000, 20-3*global.diff) {
1375 float f = frand();
1376 create_enemy3c(
1377 VIEWPORT_W/2+300*sin(global.frames)*cos(2*global.frames),
1378 400,
1379 Swirl,
1380 stage5_swirl,
1381 2*cexp(I*M_PI*f)+I,
1382 60 + 100.0*I,
1383 cexp(0.01*I*(1-2*(f<0.5)))
1384 );
1385 }
1386
1387 FROM_TO(4320, 4400, 60) {
1388 double ofs = 32;
1389 create_enemy1c(ofs + (VIEWPORT_W-2*ofs)*(_i&1) + VIEWPORT_H*I, 200, Swirl, stage5_miner, -I);
1390 }
1391
1392 FROM_TO(4260, 5000, 60) {
1393 create_enemy1c(VIEWPORT_W*(_i&1)+120*I, 400, Fairy, stage5_greeter, 6 * (1-2*(_i&1)) + I);
1394 }
1395
1396 AT(5000) {
1397 create_enemy1c(VIEWPORT_W/2, 2000, BigFairy, stage5_lightburst, 2.0*I);
1398 }
1399
1400 FROM_TO(5030, 5060, 30) {
1401 create_enemy1c(30.0*I+VIEWPORT_W*(_i&1), 1500, Fairy, stage5_superbullet, 2-4*(_i&1) + I);
1402 }
1403
1404 AT(5180) {
1405 create_enemy1c(VIEWPORT_W/2+100, 2000, BigFairy, stage5_lightburst2, 2.0*I - 0.25);
1406 }
1407
1408 FROM_TO(5060, 5300, 60-10*global.diff) {
1409 tsrand_fill(2);
1410 create_enemy1c(VIEWPORT_W+200.0*I*afrand(0), 500, Swirl, stage5_miner, -3+2.0*I*afrand(1));
1411 }
1412
1413 AT(5360) {
1414 create_enemy1c(30.0*I+VIEWPORT_W, 1500, Fairy, stage5_superbullet, -2 + I);
1415
1416 }
1417
1418 AT(5390) {
1419 create_enemy1c(30.0*I, 1500, Fairy, stage5_superbullet, 2 + I);
1420 }
1421
1422 AT(5500) {
1423 create_enemy1c(VIEWPORT_W+20 + VIEWPORT_H*0.6*I, 2000, BigFairy, stage5_lightburst, -2*I - 2);
1424 create_enemy1c( -20 + VIEWPORT_H*0.6*I, 2000, BigFairy, stage5_lightburst, -2*I + 2);
1425 }
1426
1427 AT(5600) {
1428 enemy_kill_all(&global.enemies);
1429 }
1430
1431 {
1432 int cnt = 5;
1433 int step = 10;
1434 double ofs = 42*2;
1435
1436 FROM_TO(5620, 5620 + step*cnt-1, step) {
1437 cmplx src1 = -ofs/4 + (-ofs/4 + _i * (VIEWPORT_H-2*ofs)/(cnt-1))*I;
1438 cmplx src2 = (VIEWPORT_W + ofs/4) + (-ofs/4 + (cnt-_i-1) * (VIEWPORT_H-2*ofs)/(cnt-1))*I;
1439 cmplx dst1 = ofs + ( ofs + _i * (VIEWPORT_H-2*ofs)/(cnt-1))*I;
1440 cmplx dst2 = (VIEWPORT_W - ofs) + ( ofs + (cnt-_i-1) * (VIEWPORT_H-2*ofs)/(cnt-1))*I;
1441
1442 create_enemy2c(src1, 2000, Swirl, stage5_magnetto, dst1, dst2);
1443 create_enemy2c(src2, 2000, Swirl, stage5_magnetto, dst2, dst1);
1444 }
1445 }
1446
1447 FROM_TO(6100, 6350, 60-12*global.diff) {
1448 tsrand_fill(2);
1449 create_enemy1c(VIEWPORT_W+200.0*I*afrand(0), 500, Swirl, stage5_miner, -3+2.0*I*afrand(1));
1450 }
1451
1452 FROM_TO(6300, 6350, 50) {
1453 create_enemy1c(VIEWPORT_W/4+VIEWPORT_W/2*!(_i&1), 2000, BigFairy, stage5_limiter, 2*I);
1454 }
1455
1456 AT(6960) {
1457 stage_unlock_bgm("stage5");
1458 global.boss = create_iku();
1459 }
1460
1461 AT(6980) {
1462 stage_unlock_bgm("stage5boss");
1463 global.dialog = stage5_dialog_post_boss();
1464 }
1465
1466 AT(6985) {
1467 stage_finish(GAMEOVER_SCORESCREEN);
1468 }
1469 }
1470