stage3: Major redesign of the stage (WIP)
Still needs work, but good enough for now. Merges #203 Squashed commit of the following: commit7f30ef2393
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Fri Jan 27 23:50:42 2023 +0100 stageinfo: finally rename stage3 commit53ca691e68
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Fri Jan 27 23:38:36 2023 +0100 stage3: telegraph moonlight rockets commit4d247877e7
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Wed Jan 18 15:54:44 2023 +0100 stage3: moonlight rocket fixes commit684a167611
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Wed Jan 18 14:50:48 2023 +0100 stage3: update background commitbed836f56c
Author: laochailan <laochailan@web.de> Date: Sat Jan 7 19:52:51 2023 -0500 stage3: make easy mode easier commitbec659a6eb
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Wed Dec 7 20:20:18 2022 +0100 stage3: sprinkle some random difficulty_value() calls commit2090821188
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Sun Nov 20 07:57:33 2022 +0100 stage3: refactor and fix deadly dance (no design update yet) commit5dbdeae1dd
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Sun Nov 20 07:17:05 2022 +0100 stage3: redesign scuttle nonspell commit70666a4074
Author: Andrei Alexeyev <0x416b617269@gmail.com> Date: Mon Nov 14 01:32:11 2022 +0100 stage3: last pre-boss section draft commit42bb538ff5
Author: Andrei Alexeyev <0x416b617269@gmail.com> Date: Mon Nov 14 01:28:32 2022 +0100 stage3: bunch of swarm-trail fairy fixes commit5938783591
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Sat Nov 12 01:40:23 2022 +0100 stage3: more misery commit84342f1973
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Tue Nov 8 16:35:11 2022 +0100 stage3: post-midboss up to climax commitb40f71bbc6
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Wed Nov 2 18:43:54 2022 +0100 stage3: experimental post-midboss pattern commitb5710faa04
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Wed Nov 2 18:12:49 2022 +0100 stage3: deadly dance fixes commitf85aa25c00
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Wed Nov 2 18:08:02 2022 +0100 stage3: first sparks of difficulty balance commit33b65b7fbe
Author: laochailan <laochailan@web.de> Date: Sun Oct 16 10:27:24 2022 -0400 stage3: align swarm fairies commit2556a0de9e
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Mon Sep 26 05:35:53 2022 +0200 stage3: more progress on the first half commit86fc38255b
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Sat Sep 24 04:10:57 2022 +0200 stage3: add some swirls after superfairy commitb0134e82c7
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Sat Sep 24 04:10:11 2022 +0200 stage3: experimental laserball fairy commit88d7646927
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Sat Sep 24 04:09:14 2022 +0200 stage3: tweak horde fairies commitb97da7a9e9
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Tue Sep 13 17:08:12 2022 +0200 stage3: wip circletwist fairy tweaks commitee9234a72b
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Mon Sep 5 04:41:55 2022 +0200 wip swirls commit26c7a35e04
Author: laochailan <laochailan@web.de> Date: Sun Jan 2 11:29:41 2022 +0100 stage3 wip commitd1a263223f
Author: laochailan <laochailan@web.de> Date: Fri Nov 12 05:57:07 2021 +0100 wip wip wip commitc2527b05cf
Author: laochailan <laochailan@web.de> Date: Thu Sep 30 20:38:38 2021 +0200 stage3 redesign: some first prototypes commit0c2855e3ca
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Thu May 6 21:59:48 2021 +0300 stage3: some preliminary fixes commitfddf314db9
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Sun Feb 28 07:36:04 2021 +0200 stage3: port rest of night ignite commit1cd84d7834
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Sat Feb 27 04:50:46 2021 +0200 stage3: port night ignite slaves commitcb991be703
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Sat Feb 27 04:26:26 2021 +0200 stage3: finish porting firefly storm commit0fb9733829
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Wed Jun 23 02:44:14 2021 +0300 stage3: partial firefly storm port commitba174893a0
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Tue Feb 23 02:28:13 2021 +0200 stage3: enable all wriggle attacks commit03e1dd8078
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Tue Feb 23 02:22:35 2021 +0200 stage3: port wriggle nons commit3308255579
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Mon Feb 22 16:27:50 2021 +0200 stage3: fix some indent issues commitb1c1115d7f
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Mon Feb 22 16:23:07 2021 +0200 stage3: fix deprecations in moonlight rocket commit990489e960
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Thu Feb 4 08:53:00 2021 +0200 stage3: port moonlight rocket commit859592a89b
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Thu Feb 4 07:31:58 2021 +0200 stage3: basic port of wriggle slaves commitb88b4f5249
Author: Andrei Alexeyev <akari@taisei-project.org> Date: Mon Feb 1 06:28:30 2021 +0200 stage3: port Light Singularity commitdb6bb13326
Author: Alice D <34408664+StarWitch@users.noreply.github.com> Date: Wed Mar 18 23:25:25 2020 -0400 stage3: preliminary coroutinization (#197) * first enemy of Stage 3 converted * review suggestions * add interval for burst_swirls * mid-commit * fairy group (second spawned enemies) * forgot to use my own variable * comment changes * better understanding of second arg of create_enemy* * fix subtasks (wasn't using them the correct way) * swirls that go from one side to the other * burst fairies complete * all complete up until midboss (stage 3 coroutines) * remove sub-rule for projectiles in charge_fairy * entire stage on coroutines now (boss spellcards missing) * rip out more old/dead code, make file better organized * timing changes * remove old background (looks jank now with new timing) * PR review changes * slight readability changes * Scuttle lethal_bite implemented * PR changes (ignore wriggle, she's still a WIP) * revert Info.plist entry (for separate PR) * finish(?) Scuttle's deadly dance, plus some PR changes Co-authored-by: Alice D <34408664+StarWitch@users.noreply.github.com> Co-authored-by: Lukas Weber <laochailan@web.de>
This commit is contained in:
parent
12af051d48
commit
558541e2cc
49 changed files with 1624 additions and 1412 deletions
BIN
resources/00-taisei.pkgdir/gfx/stage3/envmap.basis
Normal file
BIN
resources/00-taisei.pkgdir/gfx/stage3/envmap.basis
Normal file
Binary file not shown.
|
@ -3,3 +3,5 @@ ambient_map = stage3/ground_ambient
|
|||
diffuse_map = stage3/ground_diffuse
|
||||
normal_map = stage3/ground_normal
|
||||
roughness_map = stage3/ground_roughness
|
||||
ao_map = stage3/ground_ao
|
||||
|
||||
|
|
Binary file not shown.
BIN
resources/00-taisei.pkgdir/gfx/stage3/ground_ao.basis
Normal file
BIN
resources/00-taisei.pkgdir/gfx/stage3/ground_ao.basis
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -3,3 +3,6 @@ ambient_map = stage3/leaves_ambient
|
|||
diffuse_map = stage3/leaves_diffuse
|
||||
normal_map = stage3/leaves_normal
|
||||
roughness_map = stage3/leaves_roughness
|
||||
roughness = 1.7
|
||||
ao_map = stage3/leaves_ao
|
||||
|
||||
|
|
Binary file not shown.
BIN
resources/00-taisei.pkgdir/gfx/stage3/leaves_ao.basis
Normal file
BIN
resources/00-taisei.pkgdir/gfx/stage3/leaves_ao.basis
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -3,3 +3,5 @@ ambient_map = stage3/rocks_ambient
|
|||
diffuse_map = stage3/rocks_diffuse
|
||||
normal_map = stage3/rocks_normal
|
||||
roughness_map = stage3/rocks_roughness
|
||||
ao_map = stage3/rocks_ao
|
||||
|
||||
|
|
Binary file not shown.
BIN
resources/00-taisei.pkgdir/gfx/stage3/rocks_ao.basis
Normal file
BIN
resources/00-taisei.pkgdir/gfx/stage3/rocks_ao.basis
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -3,3 +3,5 @@ ambient_map = stage3/trees_ambient
|
|||
diffuse_map = stage3/trees_diffuse
|
||||
normal_map = stage3/trees_normal
|
||||
roughness_map = stage3/trees_roughness
|
||||
ao_map = stage3/trees_ao
|
||||
|
||||
|
|
Binary file not shown.
BIN
resources/00-taisei.pkgdir/gfx/stage3/trees_ao.basis
Normal file
BIN
resources/00-taisei.pkgdir/gfx/stage3/trees_ao.basis
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -130,7 +130,7 @@ static void stageinfo_fill(StagesExports *e) {
|
|||
// id procs type title subtitle spells diff
|
||||
add_stage(1, e->stage1.procs, STAGE_STORY, "Stage 1", "Misty Lake", e->stage1.spells, D_Any);
|
||||
add_stage(2, e->stage2.procs, STAGE_STORY, "Stage 2", "Walk Along the Border", e->stage2.spells, D_Any);
|
||||
add_stage(3, e->stage3.procs, STAGE_STORY, "Stage 3", "Through the Tunnel of Light", e->stage3.spells, D_Any);
|
||||
add_stage(3, e->stage3.procs, STAGE_STORY, "Stage 3", "Mountain Ascent", e->stage3.spells, D_Any);
|
||||
add_stage(4, e->stage4.procs, STAGE_STORY, "Stage 4", "Forgotten Mansion", e->stage4.spells, D_Any);
|
||||
add_stage(5, e->stage5.procs, STAGE_STORY, "Stage 5", "Climbing the Tower of Babel", e->stage5.spells, D_Any);
|
||||
add_stage(6, e->stage6.procs, STAGE_STORY, "Stage 6", "Roof of the World", e->stage6.spells, D_Any);
|
||||
|
|
|
@ -13,13 +13,47 @@
|
|||
|
||||
#include "global.h"
|
||||
#include "stageutils.h"
|
||||
|
||||
// TODO
|
||||
#include "util/glm.h"
|
||||
|
||||
TASK(animate_bg) {
|
||||
for(;;) {
|
||||
YIELD;
|
||||
Stage3DrawData *dd = stage3_get_draw_data();
|
||||
Camera3D *cam = &stage_3d_context.cam;
|
||||
|
||||
dd->target_swing_strength = 0.2f;
|
||||
|
||||
for(int t = 0;; ++t, YIELD) {
|
||||
float f = 1.0f / sqrtf(1.0f + t / 500.0f);
|
||||
glm_vec3_copy((vec3) { f, f, sqrtf(f) }, dd->ambient_color);
|
||||
|
||||
float swing = sin(t / 100.0) * dd->swing_strength;
|
||||
cam->pos[0] = swing;
|
||||
cam->rot.yaw = swing * -8.0f;
|
||||
|
||||
fapproach_asymptotic_p(&dd->swing_strength, dd->target_swing_strength, 0.005f, 1e-3f);
|
||||
|
||||
vec4 f0 = { 0.60, 0.30, 0.60, 1.0 };
|
||||
vec4 f1 = { 0.20, 0.10, 0.30, 1.0 };
|
||||
glm_vec4_lerp(f0, f1, 1 - f, dd->fog_color);
|
||||
|
||||
vec3 e0 = { 2.50, 0.80, 0.50 };
|
||||
vec3 e1 = { 0.20, 0.30, 0.50 };
|
||||
glm_vec3_lerp(e0, e1, 1 - f, dd->environment_color);
|
||||
glm_vec3_scale(dd->environment_color, 0.5 * f, dd->environment_color);
|
||||
|
||||
stage3d_update(&stage_3d_context);
|
||||
|
||||
if(global.boss && !boss_is_fleeing(global.boss)) {
|
||||
fapproach_asymptotic_p(&dd->boss_light_alpha, 1, 0.02f, 1e-3f);
|
||||
} else {
|
||||
fapproach_asymptotic_p(&dd->boss_light_alpha, 0, 0.02f, 1e-3f);
|
||||
}
|
||||
|
||||
if(dd->boss_light_alpha > 0 && global.boss) {
|
||||
vec3 r;
|
||||
camera3d_unprojected_ray(cam, global.boss->pos, r);
|
||||
glm_vec3_scale(r, 14, r);
|
||||
glm_vec3_add(cam->pos, r, dd->boss_light.pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,42 +29,39 @@ static uint stage3_bg_pos(Stage3D *s3d, vec3 cam, float maxrange) {
|
|||
}
|
||||
|
||||
static void stage3_bg_setup_pbr_lighting(Camera3D *cam) {
|
||||
PointLight3D lights[] = {
|
||||
// TODO animate colors
|
||||
{ { 0, 0, 10000 }, { 10, 42, 30 } },
|
||||
{ { 0, 0, 0 }, { 10, 10, 10 } },
|
||||
PointLight3D lights[2] = {
|
||||
{ { 0, 0, 0 }, { 10, 10, 10 } },
|
||||
};
|
||||
|
||||
if(global.boss) {
|
||||
vec3 r;
|
||||
cmplx bpos = global.boss->pos;
|
||||
if(cimag(bpos) < 0) { // to make the light (dis)appear continuously
|
||||
bpos = creal(bpos) + I*pow(fabs(cimag(bpos))*0.1,2)*cimag(bpos);
|
||||
}
|
||||
camera3d_unprojected_ray(cam,bpos,r);
|
||||
glm_vec3_scale(r, 9, r);
|
||||
glm_vec3_add(cam->pos, r, lights[0].pos);
|
||||
PointLight3D *boss_light = &lights[1];
|
||||
uint nlights = 1;
|
||||
|
||||
if(stage3_draw_data->boss_light_alpha > 0) {
|
||||
glm_vec3_scale(
|
||||
stage3_draw_data->boss_light.radiance,
|
||||
stage3_draw_data->boss_light_alpha,
|
||||
boss_light->radiance
|
||||
);
|
||||
glm_vec3_copy(stage3_draw_data->boss_light.pos, boss_light->pos);
|
||||
nlights++;
|
||||
}
|
||||
|
||||
vec3 r;
|
||||
camera3d_unprojected_ray(cam, global.plr.pos,r);
|
||||
camera3d_unprojected_ray(cam, global.plr.pos, r);
|
||||
glm_vec3_scale(r, 5, r);
|
||||
glm_vec3_add(cam->pos, r, lights[1].pos);
|
||||
glm_vec3_add(cam->pos, r, lights[0].pos);
|
||||
|
||||
if(global.frames > 6000) { // wriggle
|
||||
lights[0].radiance[0] = 20;
|
||||
lights[0].radiance[1] = 10;
|
||||
lights[0].radiance[2] = 40;
|
||||
}
|
||||
|
||||
camera3d_set_point_light_uniforms(cam, ARRAY_SIZE(lights), lights);
|
||||
camera3d_set_point_light_uniforms(cam, nlights, lights);
|
||||
}
|
||||
|
||||
static void stage3_bg_setup_pbr_env(Camera3D *cam, PBREnvironment *env) {
|
||||
Stage3DrawData *dd = stage3_get_draw_data();
|
||||
stage3_bg_setup_pbr_lighting(cam);
|
||||
|
||||
float f = 1.0f / (1.0f + global.frames / 1000.0f);
|
||||
glm_vec3_copy((vec3) { f, f, sqrtf(f) }, env->ambient_color);
|
||||
glm_vec3_copy(dd->ambient_color, env->ambient_color);
|
||||
glm_vec3_copy(dd->environment_color, env->environment_color);
|
||||
camera3d_apply_inverse_transforms(cam, env->cam_inverse_transform);
|
||||
env->environment_map = stage3_draw_data->envmap;
|
||||
env->disable_tonemap = true;
|
||||
}
|
||||
|
||||
static void stage3_bg_ground_draw(vec3 pos) {
|
||||
|
@ -114,6 +111,8 @@ void stage3_drawsys_init(void) {
|
|||
pbr_load_model(&stage3_draw_data->models.ground, "stage3/ground", "stage3/ground"); pbr_load_model(&stage3_draw_data->models.leaves, "stage3/leaves", "stage3/leaves");
|
||||
pbr_load_model(&stage3_draw_data->models.rocks, "stage3/rocks", "stage3/rocks");
|
||||
pbr_load_model(&stage3_draw_data->models.trees, "stage3/trees", "stage3/trees");
|
||||
|
||||
stage3_draw_data->envmap = res_texture("stage3/envmap");
|
||||
}
|
||||
|
||||
void stage3_drawsys_shutdown(void) {
|
||||
|
@ -122,13 +121,16 @@ void stage3_drawsys_shutdown(void) {
|
|||
}
|
||||
|
||||
static bool stage3_fog(Framebuffer *fb) {
|
||||
r_shader("zbuf_fog");
|
||||
Stage3DrawData *dd = stage3_get_draw_data();
|
||||
r_shader("zbuf_fog_tonemap");
|
||||
r_uniform_sampler("depth", r_framebuffer_get_attachment(fb, FRAMEBUFFER_ATTACH_DEPTH));
|
||||
r_uniform_vec4("fog_color", 0.8, 0.5, 1, 1.0);
|
||||
r_uniform_float("start", 0.6);
|
||||
r_uniform_vec4_vec("fog_color", dd->fog_color);
|
||||
r_uniform_float("start", 0.9);
|
||||
r_uniform_float("end", 2);
|
||||
r_uniform_float("exponent", 10);
|
||||
r_uniform_float("exponent", 1);
|
||||
r_uniform_float("curvature", 0);
|
||||
float e = 1;
|
||||
r_uniform_vec3("exposure", e, e, e);
|
||||
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
|
||||
r_shader_standard();
|
||||
return true;
|
||||
|
|
|
@ -20,6 +20,18 @@ typedef struct Stage3DrawData {
|
|||
PBRModel rocks;
|
||||
PBRModel trees;
|
||||
} models;
|
||||
|
||||
Texture *envmap;
|
||||
|
||||
vec3 environment_color;
|
||||
vec3 ambient_color;
|
||||
vec4 fog_color;
|
||||
|
||||
float boss_light_alpha;
|
||||
PointLight3D boss_light;
|
||||
|
||||
float swing_strength;
|
||||
float target_swing_strength;
|
||||
} Stage3DrawData;
|
||||
|
||||
Stage3DrawData *stage3_get_draw_data(void)
|
||||
|
|
|
@ -10,4 +10,5 @@
|
|||
#include "taisei.h"
|
||||
|
||||
#define ENTITIES_STAGE3(X, ...) \
|
||||
X(WriggleSlave, __VA_ARGS__) \
|
||||
END_OF_ENTITIES
|
||||
|
|
|
@ -12,104 +12,95 @@
|
|||
#include "../wriggle.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "common_tasks.h"
|
||||
|
||||
MODERNIZE_THIS_FILE_AND_REMOVE_ME
|
||||
TASK(slave, { BoxedBoss boss; real rot_speed; real rot_initial; int level; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
WriggleSlave *slave = stage3_host_wriggle_slave(boss->pos);
|
||||
|
||||
static int wriggle_nonspell_slave(Enemy *e, int time) {
|
||||
TIMER(&time)
|
||||
INVOKE_SUBTASK(wriggle_slave_follow,
|
||||
.slave = ENT_BOX(slave),
|
||||
.boss = ENT_BOX(boss),
|
||||
.rot_speed = ARGS.rot_speed,
|
||||
.rot_initial = ARGS.rot_initial
|
||||
);
|
||||
|
||||
int level = e->args[3];
|
||||
float angle = e->args[2] * (time / 70.0 + e->args[1]);
|
||||
cmplx dir = cexp(I*angle);
|
||||
Boss *boss = (Boss*)REF(e->args[0]);
|
||||
int delay = difficulty_value(9, 8, 7, 6);
|
||||
|
||||
if(!boss)
|
||||
return ACTION_DESTROY;
|
||||
|
||||
AT(EVENT_DEATH) {
|
||||
free_ref(e->args[0]);
|
||||
return 1;
|
||||
if(ARGS.level > 2) {
|
||||
delay += 4;
|
||||
}
|
||||
|
||||
if(time < 0)
|
||||
return 1;
|
||||
WAIT(1);
|
||||
|
||||
GO_TO(e, boss->pos + (100 + 20 * e->args[2] * sin(time / 100.0)) * dir, 0.03)
|
||||
|
||||
int d = 10 - global.diff;
|
||||
if(level > 2)
|
||||
d += 4;
|
||||
|
||||
if(!(time % d)) {
|
||||
play_sound("shot1");
|
||||
for(int t = 0;; t += WAIT(delay)) {
|
||||
play_sfx("shot1");
|
||||
cmplx aim = cnormalize(boss->pos - slave->pos);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_rice,
|
||||
.pos = e->pos,
|
||||
.pos = slave->pos,
|
||||
.color = RGB(0.7, 0.2, 0.1),
|
||||
.rule = linear,
|
||||
.args = { 3 * cexp(I*carg(boss->pos - e->pos)) },
|
||||
.move = move_linear(3 * aim),
|
||||
);
|
||||
|
||||
if(!(time % (d*2)) || level > 1) {
|
||||
if(!(t % (delay * 2)) || ARGS.level > 1) {
|
||||
PROJECTILE(
|
||||
.proto = pp_thickrice,
|
||||
.pos = e->pos,
|
||||
.pos = slave->pos,
|
||||
.color = RGB(0.7, 0.7, 0.1),
|
||||
.rule = linear,
|
||||
.args = { 2.5 * cexp(I*carg(boss->pos - e->pos)) },
|
||||
.move = move_linear(2.5 * aim),
|
||||
);
|
||||
}
|
||||
|
||||
if(level > 2) {
|
||||
if(ARGS.level > 2) {
|
||||
PROJECTILE(
|
||||
.proto = pp_wave,
|
||||
.pos = e->pos,
|
||||
.color = RGB(0.3, 0.1 + 0.6 * psin(time / 25.0), 0.7),
|
||||
.rule = linear,
|
||||
.args = { 2 * cexp(I*carg(boss->pos - e->pos)) },
|
||||
.pos = slave->pos,
|
||||
.color = RGB(0.3, 0.1 + 0.6 * psin(t / 25.0), 0.7),
|
||||
.move = move_linear(2 * aim),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void wriggle_nonspell_common(Boss *boss, int time, int level) {
|
||||
TIMER(&time)
|
||||
int i, j, cnt = 3 + global.diff;
|
||||
static void wriggle_nonspell_common(Boss *boss, int level) {
|
||||
int n = difficulty_value(4, 5, 6, 7);
|
||||
|
||||
AT(0) for(j = -1; j < 2; j += 2) for(i = 0; i < cnt; ++i)
|
||||
create_enemy4c(boss->pos, ENEMY_IMMUNE, wriggle_slave_visual, wriggle_nonspell_slave, add_ref(boss), i*2*M_PI/cnt, j, level);
|
||||
|
||||
AT(EVENT_DEATH) {
|
||||
enemy_kill_all(&global.enemies);
|
||||
return;
|
||||
for(int i = 0; i < n; ++i) {
|
||||
INVOKE_SUBTASK(slave, ENT_BOX(boss), 1/70.0, i*M_TAU/n, level);
|
||||
INVOKE_SUBTASK(slave, ENT_BOX(boss), -1/70.0, -i*M_TAU/n, level);
|
||||
}
|
||||
|
||||
if(time < 0) {
|
||||
GO_TO(boss, VIEWPORT_W/2 + VIEWPORT_H*I/3, 0.05)
|
||||
return;
|
||||
Rect wander_bounds = viewport_bounds(120);
|
||||
wander_bounds.bottom = 180 + 20 * level;
|
||||
real wander_dist = 60 + 10 * level;
|
||||
|
||||
boss->move = move_towards(boss->pos, 0.02);
|
||||
|
||||
for(;;) {
|
||||
WAIT(120);
|
||||
boss->move.attraction_point = common_wander(boss->pos, wander_dist, wander_bounds);
|
||||
WAIT(120);
|
||||
}
|
||||
|
||||
FROM_TO(120, 240, 1)
|
||||
GO_TO(boss, VIEWPORT_W/3 + VIEWPORT_H*I/3, 0.03)
|
||||
|
||||
FROM_TO(360, 480, 1)
|
||||
GO_TO(boss, VIEWPORT_W - VIEWPORT_W/3 + VIEWPORT_H*I/3, 0.03)
|
||||
|
||||
FROM_TO(600, 720, 1)
|
||||
GO_TO(boss, VIEWPORT_W/2 + VIEWPORT_H*I/3, 0.03)
|
||||
STALL;
|
||||
}
|
||||
|
||||
void stage3_boss_nonspell1(Boss *boss, int time) {
|
||||
wriggle_nonspell_common(boss, time, 1);
|
||||
DEFINE_EXTERN_TASK(stage3_boss_nonspell_1) {
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
wriggle_nonspell_common(boss, 1);
|
||||
}
|
||||
|
||||
void stage3_boss_nonspell2(Boss *boss, int time) {
|
||||
wriggle_nonspell_common(boss, time, 2);
|
||||
DEFINE_EXTERN_TASK(stage3_boss_nonspell_2) {
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
wriggle_nonspell_common(boss, 2);
|
||||
}
|
||||
|
||||
void stage3_boss_nonspell3(Boss *boss, int time) {
|
||||
wriggle_nonspell_common(boss, time, 3);
|
||||
DEFINE_EXTERN_TASK(stage3_boss_nonspell_3) {
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
wriggle_nonspell_common(boss, 3);
|
||||
}
|
||||
|
|
|
@ -11,89 +11,104 @@
|
|||
#include "nonspells.h"
|
||||
#include "../scuttle.h"
|
||||
|
||||
#include "common_tasks.h"
|
||||
#include "global.h"
|
||||
|
||||
MODERNIZE_THIS_FILE_AND_REMOVE_ME
|
||||
TASK(bite_bullet, { cmplx pos; cmplx vel; }) {
|
||||
Color phase_colors[] = {
|
||||
{ 2.0, 0.0, 0.1, 1.0 },
|
||||
{ 1.2, 0.3, 0.1, 1.0 },
|
||||
{ 0.3, 0.7, 0.1, 1.0 },
|
||||
};
|
||||
|
||||
static int scuttle_lethbite_proj(Projectile *p, int time) {
|
||||
if(time < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
Projectile *p = TASK_BIND(PROJECTILE(
|
||||
.proto = pp_wave,
|
||||
.pos = ARGS.pos,
|
||||
.color = phase_colors,
|
||||
.max_viewport_dist = 50,
|
||||
));
|
||||
|
||||
#define A0_PROJ_START 120
|
||||
#define A0_PROJ_CHARGE 20
|
||||
TIMER(&time)
|
||||
int aimed_speed = difficulty_value(3.0, 3.5, 4.5, 5.0);
|
||||
|
||||
FROM_TO(A0_PROJ_START, A0_PROJ_START + A0_PROJ_CHARGE, 1)
|
||||
return 1;
|
||||
p->move = move_asymptotic_simple(ARGS.vel, 2.0);
|
||||
WAIT(120);
|
||||
|
||||
AT(A0_PROJ_START + A0_PROJ_CHARGE + 1) if(p->type != PROJ_DEAD) {
|
||||
p->args[1] = 3;
|
||||
p->args[0] = (3 + 2 * global.diff / (float)D_Lunatic) * cexp(I*carg(global.plr.pos - p->pos));
|
||||
for(int phase = 1; phase < ARRAY_SIZE(phase_colors); ++phase) {
|
||||
p->move.acceleration = 0;
|
||||
p->flags |= PFLAG_MANUALANGLE;
|
||||
|
||||
int cnt = 3, i;
|
||||
for(i = 0; i < cnt; ++i) {
|
||||
tsrand_fill(2);
|
||||
for(int i = 0; i < 20; ++i, YIELD) {
|
||||
fapproach_asymptotic_p(&p->angle, carg(global.plr.pos - p->pos), 0.2, 1e-3);
|
||||
}
|
||||
|
||||
play_sfx("redirect");
|
||||
play_sfx("shot1");
|
||||
|
||||
cmplx aim = cnormalize(global.plr.pos - p->pos);
|
||||
p->angle = carg(aim);
|
||||
p->color = phase_colors[phase];
|
||||
spawn_projectile_highlight_effect(p);
|
||||
p->move = move_asymptotic_simple(aimed_speed * cnormalize(aim), 3.0);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_thickrice,
|
||||
.pos = p->pos,
|
||||
.color = RGB(0.4, 0.3, 1.0),
|
||||
.move = move_linear(-aim * 0.5),
|
||||
);
|
||||
|
||||
for(int i = 0; i < 3; ++i) {
|
||||
cmplx v = rng_dir();
|
||||
v *= rng_range(1, 1.5);
|
||||
v += aim * aimed_speed;
|
||||
|
||||
PARTICLE(
|
||||
.pos = p->pos,
|
||||
.sprite = "smoothdot",
|
||||
.color = RGBA(0.8, 0.6, 0.6, 0),
|
||||
.draw_rule = Shrink,
|
||||
.rule = enemy_flare,
|
||||
.timeout = 100,
|
||||
.args = {
|
||||
cexp(I*(M_PI*anfrand(0))) * (1 + afrand(1)),
|
||||
add_ref(p)
|
||||
},
|
||||
);
|
||||
|
||||
float offset = global.frames/15.0;
|
||||
if(global.diff > D_Hard && global.boss) {
|
||||
offset = M_PI+carg(global.plr.pos-global.boss->pos);
|
||||
}
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_thickrice,
|
||||
.pos = p->pos,
|
||||
.color = RGB(0.4, 0.3, 1.0),
|
||||
.rule = linear,
|
||||
.args = {
|
||||
-cexp(I*(i*2*M_PI/cnt + offset)) * (1.0 + (global.diff > D_Normal))
|
||||
},
|
||||
.draw_rule = pdraw_timeout_scale(1, 0.01),
|
||||
.timeout = rng_irange(20, 40),
|
||||
.move = move_asymptotic_simple(v, 2),
|
||||
);
|
||||
}
|
||||
|
||||
play_sound("redirect");
|
||||
play_sound("shot1");
|
||||
spawn_projectile_highlight_effect(p);
|
||||
WAIT(60);
|
||||
}
|
||||
|
||||
return asymptotic(p, time);
|
||||
#undef A0_PROJ_START
|
||||
#undef A0_PROJ_CHARGE
|
||||
}
|
||||
|
||||
void stage3_midboss_nonspell1(Boss *boss, int time) {
|
||||
int i;
|
||||
TIMER(&time)
|
||||
DEFINE_EXTERN_TASK(stage3_midboss_nonspell_1) {
|
||||
STAGE_BOOKMARK(non1);
|
||||
|
||||
GO_TO(boss, VIEWPORT_W/2+VIEWPORT_W/3*sin(time/300) + I*cimag(boss->pos), 0.01)
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2 + 100.0*I, 0.03);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
FROM_TO_INT(0, 90000, 72 + 6 * (D_Lunatic - global.diff), 0, 1) {
|
||||
int cnt = 21 - 1 * (D_Lunatic - global.diff);
|
||||
Rect bounds = viewport_bounds(80);
|
||||
bounds.bottom = 210;
|
||||
|
||||
for(i = 0; i < cnt; ++i) {
|
||||
cmplx v = (2 - psin((fmax(3, global.diff+1)*2*M_PI*i/(float)cnt) + time)) * cexp(I*2*M_PI/cnt*i);
|
||||
PROJECTILE(
|
||||
.proto = pp_wave,
|
||||
.pos = boss->pos - v * 50,
|
||||
.color = _i % 2? RGB(0.7, 0.3, 0.0) : RGB(0.3, .7, 0.0),
|
||||
.rule = scuttle_lethbite_proj,
|
||||
.args = { v, 2.0 },
|
||||
int time = 0;
|
||||
int cnt = difficulty_value(18, 19, 20, 21);
|
||||
int shape = difficulty_value(3, 3, 4, 5);
|
||||
int delay = 120;
|
||||
|
||||
cmplx origin;
|
||||
Color charge_color = *RGBA(1, 0, 0, 0);
|
||||
|
||||
for(;;) {
|
||||
origin = boss->pos;
|
||||
boss->move.attraction_point = common_wander(boss->pos, 200, bounds);
|
||||
time += common_charge_static(delay, origin, &charge_color);
|
||||
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
play_sfx("shot_special1");
|
||||
|
||||
cmplx v = (2 - psin((shape*M_TAU*i/(real)cnt) + time)) * cdir(M_TAU/cnt*i);
|
||||
INVOKE_TASK(bite_bullet,
|
||||
.pos = origin - v * 25,
|
||||
.vel = v,
|
||||
);
|
||||
}
|
||||
|
||||
// FIXME: better sound
|
||||
play_sound("shot_special1");
|
||||
delay = imax(60, delay - 10);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,7 @@
|
|||
|
||||
#include "boss.h"
|
||||
|
||||
void stage3_midboss_nonspell1(Boss *boss, int time);
|
||||
void stage3_boss_nonspell1(Boss *boss, int time);
|
||||
void stage3_boss_nonspell2(Boss *boss, int time);
|
||||
void stage3_boss_nonspell3(Boss *boss, int time);
|
||||
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage3_midboss_nonspell_1, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage3_boss_nonspell_1, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage3_boss_nonspell_2, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage3_boss_nonspell_3, BossAttack);
|
||||
|
|
|
@ -39,5 +39,6 @@ Boss *stage3_spawn_scuttle(cmplx pos) {
|
|||
boss_set_portrait(scuttle, "scuttle", NULL, "normal");
|
||||
scuttle->glowcolor = *RGB(0.5, 0.6, 0.3);
|
||||
scuttle->shadowcolor = *RGBA_MUL_ALPHA(0.7, 0.3, 0.1, 0.5);
|
||||
scuttle->zoomcolor = *RGB(0.4, 0.1, 0.4);
|
||||
return scuttle;
|
||||
}
|
||||
|
|
|
@ -14,108 +14,114 @@
|
|||
#include "common_tasks.h"
|
||||
#include "global.h"
|
||||
|
||||
MODERNIZE_THIS_FILE_AND_REMOVE_ME
|
||||
TASK(spawner_proj, { cmplx pos; MoveParams move; Color *color; int t; int i; }) {
|
||||
Projectile *p = TASK_BIND(PROJECTILE(
|
||||
.proto = pp_wave,
|
||||
.pos = ARGS.pos,
|
||||
.color = ARGS.color,
|
||||
.move = ARGS.move,
|
||||
));
|
||||
|
||||
static int scuttle_poison(Projectile *p, int time) {
|
||||
int result = accelerated(p, time);
|
||||
int t = ARGS.t;
|
||||
int i = ARGS.i;
|
||||
|
||||
if(time < 0)
|
||||
return result;
|
||||
WAIT(150);
|
||||
real a = (M_PI/(5 + global.diff) * i * 2);
|
||||
|
||||
if(!(time % (57 - global.diff * 3)) && p->type != PROJ_DEAD) {
|
||||
float a = p->args[2];
|
||||
float t = p->args[3] + time;
|
||||
play_sfx("redirect");
|
||||
|
||||
PROJECTILE(
|
||||
.proto = (frand() > 0.5)? pp_thickrice : pp_rice,
|
||||
.pos = p->pos,
|
||||
.color = RGB(0.3, 0.7 + 0.3 * psin(a/3.0 + t/20.0), 0.3),
|
||||
.rule = accelerated,
|
||||
.args = {
|
||||
0,
|
||||
0.005*cexp(I*(M_PI*2 * sin(a/5.0 + t/20.0))),
|
||||
},
|
||||
);
|
||||
|
||||
play_sound("redirect");
|
||||
}
|
||||
|
||||
return result;
|
||||
PROJECTILE(
|
||||
.proto = rng_chance(0.5) ? pp_thickrice : pp_rice,
|
||||
.pos = p->pos,
|
||||
.color = RGB(0.3, 0.7 + 0.3 * psin(a/3.0 + t/20.0), 0.3),
|
||||
.move = move_accelerated(0, 0.005 * cdir((M_TAU * sin(a / 5.0 + t / 20.0))))
|
||||
);
|
||||
}
|
||||
|
||||
void scuttle_deadly_dance(Boss *boss, int time) {
|
||||
int i;
|
||||
TIMER(&time)
|
||||
TASK(spawners, { BoxedBoss boss; real tfactor; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
int count = difficulty_value(15, 18, 21, 24);
|
||||
real tfactor = ARGS.tfactor;
|
||||
|
||||
if(time < 0) {
|
||||
return;
|
||||
}
|
||||
for(int time = 0;; time += WAIT(70)) {
|
||||
real angle_ofs = rng_angle();
|
||||
play_sfx("shot_special1");
|
||||
|
||||
AT(0) {
|
||||
aniplayer_queue(&boss->ani, "dance", 0);
|
||||
}
|
||||
play_sfx_loop("shot1_loop");
|
||||
|
||||
FROM_TO(0, 120, 1)
|
||||
GO_TO(boss, VIEWPORT_W/2 + VIEWPORT_H*I/2, 0.03)
|
||||
|
||||
if(time > 30) {
|
||||
float angle_ofs = frand() * M_PI * 2;
|
||||
double t = time * 1.5 * (0.4 + 0.3 * global.diff);
|
||||
double moverad = fmin(160, time/2.7);
|
||||
GO_TO(boss, VIEWPORT_W/2 + VIEWPORT_H*I/2 + sin(t/50.0) * moverad * cexp(I * M_PI_2 * t/100.0), 0.03)
|
||||
|
||||
if(!(time % 70)) {
|
||||
for(i = 0; i < 15; ++i) {
|
||||
double a = M_PI/(5 + global.diff) * i * 2;
|
||||
PROJECTILE(
|
||||
.proto = pp_wave,
|
||||
.pos = boss->pos,
|
||||
.color = RGB(0.3, 0.3 + 0.7 * psin(a*3 + time/50.0), 0.3),
|
||||
.rule = scuttle_poison,
|
||||
.args = {
|
||||
0,
|
||||
0.02 * cexp(I*(angle_ofs+a+time/10.0)),
|
||||
a,
|
||||
time
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
play_sound("shot_special1");
|
||||
}
|
||||
|
||||
if(global.diff > D_Easy && !(time % 35)) {
|
||||
int cnt = global.diff * 2;
|
||||
for(i = 0; i < cnt; ++i) {
|
||||
PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = boss->pos,
|
||||
.color = RGB(1.0, 1.0, 0.3),
|
||||
.rule = asymptotic,
|
||||
.args = {
|
||||
(0.5 + 3 * psin(time + M_PI/3*2*i)) * cexp(I*(angle_ofs + time / 20.0 + M_PI/cnt*i*2)),
|
||||
1.5
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
play_sound("shot1");
|
||||
}
|
||||
}
|
||||
|
||||
if(!(time % 3)) {
|
||||
for(i = -1; i < 2; i += 2) {
|
||||
double c = psin(time/10.0);
|
||||
PROJECTILE(
|
||||
.proto = pp_crystal,
|
||||
for(int i = 0; i < count; ++i) {
|
||||
real a = (M_PI/(5 + global.diff) * i * 2);
|
||||
INVOKE_TASK(spawner_proj,
|
||||
.pos = boss->pos,
|
||||
.color = RGBA_MUL_ALPHA(0.3 + c * 0.7, 0.6 - c * 0.3, 0.3, 0.7),
|
||||
.rule = linear,
|
||||
.args = {
|
||||
10 * cexp(I*(carg(global.plr.pos - boss->pos) + (M_PI/4.0 * i * (1-time/2500.0)) * (1 - 0.5 * psin(time/15.0))))
|
||||
}
|
||||
.color = RGB(0.3, 0.3 + 0.7 * psin((M_PI/(5 + global.diff) * i * 2) * 3 + time/50.0), 0.3),
|
||||
.move = move_accelerated(0, 0.02 * cdir(angle_ofs + a + time/10.0)),
|
||||
.t = time * tfactor,
|
||||
.i = i,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TASK(balls, { BoxedBoss boss; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
int count = difficulty_value(2, 4, 6, 8);
|
||||
|
||||
for(int time = 0;; time += WAIT(35)) {
|
||||
real angle_ofs = rng_angle();
|
||||
play_sfx("shot1");
|
||||
|
||||
for(int i = 0; i < count; ++i) {
|
||||
PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = boss->pos,
|
||||
.color = RGB(1.0, 1.0, 0.3),
|
||||
.move = move_asymptotic_simple(
|
||||
(0.5 + 3 * psin(time + M_PI / 3 * 2 * i)) * cdir(angle_ofs + time / 20.0 + M_PI / count * i * 2),
|
||||
1.5
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TASK(limiters, { BoxedBoss boss; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
for(int time = 0;; time += WAIT(3)) {
|
||||
play_sfx_loop("shot1_loop");
|
||||
|
||||
for(int i = -1; i < 2; i += 2) {
|
||||
real c = psin(time/10.0);
|
||||
cmplx aim = cnormalize(global.plr.pos - boss->pos);
|
||||
cmplx v = 10 * (aim + (M_PI/4.0 * i * (1 - time / 2500.0)) * (1 - 0.5 * psin(time / 15.0)));
|
||||
PROJECTILE(
|
||||
.proto = pp_crystal,
|
||||
.pos = boss->pos,
|
||||
.color = RGBA_MUL_ALPHA(0.3 + c * 0.7, 0.6 - c * 0.3, 0.3, 0.7),
|
||||
.move = move_linear(v),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage3_spell_deadly_dance) {
|
||||
STAGE_BOOKMARK(dance);
|
||||
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2 + VIEWPORT_H*I/2, 0.08);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
boss->move.attraction = 0;
|
||||
|
||||
aniplayer_queue(&boss->ani, "dance", 0);
|
||||
real tfactor = difficulty_value(1.05, 1.5, 1.95, 2.4);
|
||||
|
||||
INVOKE_SUBTASK(limiters, ENT_BOX(boss));
|
||||
INVOKE_SUBTASK(spawners, ENT_BOX(boss), .tfactor = tfactor);
|
||||
|
||||
if(global.diff > D_Easy) {
|
||||
INVOKE_SUBTASK(balls, ENT_BOX(boss));
|
||||
}
|
||||
|
||||
for(int time = 0;; ++time, YIELD) {
|
||||
real t = time * tfactor;
|
||||
real moverad = fmin(160, time/2.7);
|
||||
boss->pos = VIEWPORT_W/2 + VIEWPORT_H*I/2 + sin(t/50.0) * moverad * cdir(M_PI/2 * t/100.0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
#include "common_tasks.h"
|
||||
#include "global.h"
|
||||
|
||||
MODERNIZE_THIS_FILE_AND_REMOVE_ME
|
||||
|
||||
DEPRECATED_DRAW_RULE
|
||||
static void wriggle_fstorm_proj_draw(Projectile *p, int time, ProjDrawRuleArgs args) {
|
||||
float f = 1-fmin(time/60.0,1);
|
||||
r_mat_mv_push();
|
||||
|
@ -29,7 +26,7 @@ static void wriggle_fstorm_proj_draw(Projectile *p, int time, ProjDrawRuleArgs a
|
|||
Sprite *s = p->sprite;
|
||||
Color c = p->color;
|
||||
c.a = 0;
|
||||
p->sprite = get_sprite("proj/ball");
|
||||
p->sprite = res_sprite("proj/ball");
|
||||
r_mat_mv_scale(f,f,f);
|
||||
ProjDrawCore(p, &c);
|
||||
p->sprite = s;
|
||||
|
@ -38,85 +35,79 @@ static void wriggle_fstorm_proj_draw(Projectile *p, int time, ProjDrawRuleArgs a
|
|||
r_mat_mv_pop();
|
||||
}
|
||||
|
||||
static int wriggle_fstorm_proj(Projectile *p, int time) {
|
||||
if(time < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
TASK(fstorm_bullet, { BoxedBoss boss; ProjPrototype *proto; cmplx pos; cmplx vel; int convert_time; }) {
|
||||
Projectile *p = TASK_BIND(PROJECTILE(
|
||||
.proto = ARGS.proto,
|
||||
.pos = ARGS.pos,
|
||||
.color = RGB(0.2, 0.2, 0.6),
|
||||
.move = move_linear(ARGS.vel),
|
||||
));
|
||||
|
||||
if(cabs(global.plr.pos-p->pos) > 100) {
|
||||
p->args[2]+=1;
|
||||
} else {
|
||||
p->args[2]-=1;
|
||||
if(creal(p->args[2]) < 0)
|
||||
p->args[2] = 0;
|
||||
}
|
||||
int t = 0;
|
||||
|
||||
int turntime = rint(creal(p->args[0]));
|
||||
int t = rint(creal(p->args[2]));
|
||||
if(t < turntime) {
|
||||
float f = t/(float)turntime;
|
||||
p->color = *RGB(0.3+0.7*(1 - pow(1 - f, 4)), 0.3+0.3*f*f, 0.7-0.7*f);
|
||||
}
|
||||
|
||||
if(t == turntime && global.boss) {
|
||||
p->args[1] = global.boss->pos-p->pos;
|
||||
p->args[1] *= 2/cabs(p->args[1]);
|
||||
p->angle = carg(p->args[1]);
|
||||
p->birthtime = global.frames;
|
||||
p->draw_rule = (ProjDrawRule) { wriggle_fstorm_proj_draw };
|
||||
p->sprite = NULL;
|
||||
projectile_set_prototype(p, pp_rice);
|
||||
spawn_projectile_highlight_effect(p);
|
||||
|
||||
for(int i = 0; i < 3; ++i) {
|
||||
tsrand_fill(2);
|
||||
PARTICLE(
|
||||
.sprite = "flare",
|
||||
.pos = p->pos,
|
||||
.rule = linear,
|
||||
.timeout = 60,
|
||||
.args = { (1+afrand(0))*cexp(I*tsrand_a(1)) },
|
||||
.draw_rule = Shrink,
|
||||
);
|
||||
for(;;YIELD) {
|
||||
if(cabs(global.plr.pos - p->pos) > 100) {
|
||||
++t;
|
||||
} else {
|
||||
t = imax(0, t - 1);
|
||||
}
|
||||
|
||||
play_sound_ex("redirect", 3, false);
|
||||
if(t >= ARGS.convert_time) {
|
||||
break;
|
||||
}
|
||||
|
||||
real f = t / (real)ARGS.convert_time;
|
||||
p->color = *RGB(0.3 + 0.7 * (1 - pow(1 - f, 4)), 0.3 + 0.3 * f * f, 0.7 - 0.7 * f);
|
||||
}
|
||||
|
||||
p->pos += p->args[1];
|
||||
return ACTION_NONE;
|
||||
Boss *boss = NOT_NULL(ENT_UNBOX(ARGS.boss));
|
||||
p->move.velocity = 2 * cnormalize(boss->pos - p->pos);
|
||||
p->birthtime = global.frames;
|
||||
p->draw_rule = (ProjDrawRule) { wriggle_fstorm_proj_draw };
|
||||
p->sprite = NULL;
|
||||
projectile_set_prototype(p, pp_rice);
|
||||
spawn_projectile_highlight_effect(p);
|
||||
|
||||
for(int i = 0; i < 3; ++i) {
|
||||
RNG_ARRAY(r, 2);
|
||||
PARTICLE(
|
||||
.sprite = "flare",
|
||||
.pos = p->pos,
|
||||
.timeout = 60,
|
||||
.move = move_linear((1 + vrng_real(r[0])) * vrng_dir(r[1])),
|
||||
.draw_rule = pdraw_timeout_scale(2, 0.01),
|
||||
);
|
||||
}
|
||||
|
||||
play_sfx_ex("redirect", 3, false);
|
||||
}
|
||||
|
||||
void wriggle_firefly_storm(Boss *boss, int time) {
|
||||
TIMER(&time)
|
||||
|
||||
if(time < 0) {
|
||||
GO_TO(boss, VIEWPORT_W/2 + VIEWPORT_H*I/2, 0.05)
|
||||
return;
|
||||
}
|
||||
DEFINE_EXTERN_TASK(stage3_spell_firefly_storm) {
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2 + VIEWPORT_H*I/2, 0.05);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
bool lun = global.diff == D_Lunatic;
|
||||
aniplayer_queue(&boss->ani, "fly", 0);
|
||||
|
||||
AT(0) {
|
||||
aniplayer_queue(&boss->ani,"fly",0);
|
||||
}
|
||||
int convert_time = difficulty_value(40, 55, 75, 100);
|
||||
cmplx bullet_rotation = cdir(difficulty_value(0.6, 0.6, 0.6, 0));
|
||||
|
||||
FROM_TO_SND("shot1_loop", 30, 9000, 2) {
|
||||
int i, cnt = 2;
|
||||
for(i = 0; i < cnt; ++i) {
|
||||
float r = tanh(sin(_i/200.));
|
||||
float v = lun ? cos(_i/150.)/pow(cosh(atanh(r)),2) : 0.5;
|
||||
cmplx pos = 230*cexp(I*(_i*0.301+2*M_PI/cnt*i))*r;
|
||||
WAIT(30);
|
||||
|
||||
PROJECTILE(
|
||||
for(int cycle = 0;; ++cycle, WAIT(2)) {
|
||||
int cnt = 2;
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
real r = fmax(0.05, tanh(sin(cycle / 200.0)));
|
||||
real v = lun ? cos(cycle / 150.0) / pow(cosh(atanh(r)), 2) : 0.5;
|
||||
cmplx pos = 230 * cdir(cycle * 0.301 + M_TAU / cnt * i) * r;
|
||||
|
||||
INVOKE_SUBTASK(fstorm_bullet,
|
||||
.boss = ENT_BOX(boss),
|
||||
.proto = (global.diff >= D_Hard) && !(i%10) ? pp_bigball : pp_ball,
|
||||
.pos = boss->pos+pos,
|
||||
.color = RGB(0.2,0.2,0.6),
|
||||
.rule = wriggle_fstorm_proj,
|
||||
.args = {
|
||||
(global.diff == D_Easy) ? 40 : 100-25*(!lun)-20*(global.diff == D_Normal),
|
||||
cexp(I*(!lun)*0.6)*pos/cabs(pos)*(1+v)
|
||||
},
|
||||
.pos = boss->pos + pos,
|
||||
.vel = bullet_rotation * cnormalize(pos) * (1 + v),
|
||||
.convert_time = convert_time
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,111 +14,95 @@
|
|||
#include "common_tasks.h"
|
||||
#include "global.h"
|
||||
|
||||
MODERNIZE_THIS_FILE_AND_REMOVE_ME
|
||||
TASK(singularity_laser, { cmplx pos; cmplx vel; real amp; real freq; }) {
|
||||
Laser *l = TASK_BIND(create_laser(ARGS.pos,
|
||||
200, 10000, RGBA(0.0, 0.2, 1.0, 0.0), las_sine_expanding,
|
||||
NULL, ARGS.vel, ARGS.amp, ARGS.freq, 0
|
||||
));
|
||||
|
||||
static void wriggle_singularity_laser_logic(Laser *l, int time) {
|
||||
if(time == EVENT_BIRTH) {
|
||||
l->width = 0;
|
||||
l->speed = 0;
|
||||
l->timeshift = l->timespan;
|
||||
l->unclearable = true;
|
||||
return;
|
||||
}
|
||||
laser_make_static(l);
|
||||
l->unclearable = true;
|
||||
|
||||
if(time == 140) {
|
||||
play_sound("laser1");
|
||||
}
|
||||
real spin_factor = difficulty_value(1.05, 1.4, 1.75, 2.1);
|
||||
|
||||
laser_charge(l, time, 150, 10 + 10 * psin(l->args[0] + time / 60.0));
|
||||
l->args[3] = time / 10.0;
|
||||
l->args[0] *= cexp(I*(M_PI/500.0) * (0.7 + 0.35 * global.diff));
|
||||
|
||||
l->color = *HSLA((carg(l->args[0]) + M_PI) / (M_PI * 2), 1.0, 0.5, 0.0);
|
||||
}
|
||||
|
||||
void wriggle_light_singularity(Boss *boss, int time) {
|
||||
TIMER(&time)
|
||||
|
||||
AT(EVENT_DEATH) {
|
||||
return;
|
||||
}
|
||||
|
||||
time -= 120;
|
||||
|
||||
if(time < 0) {
|
||||
GO_TO(boss, VIEWPORT_W/2 + VIEWPORT_H*I/2, 0.05)
|
||||
return;
|
||||
}
|
||||
|
||||
AT(0) {
|
||||
int cnt = 2 + global.diff;
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
double aofs = 0;
|
||||
|
||||
if(global.diff == D_Hard || global.diff == D_Easy) {
|
||||
aofs = 0.7;
|
||||
}
|
||||
|
||||
cmplx vel = 2 * cexp(I*(aofs + M_PI / 4 + M_PI * 2 * i / (double)cnt));
|
||||
double amp = (4.0/cnt) * (M_PI/5.0);
|
||||
double freq = 0.05;
|
||||
|
||||
create_laser(boss->pos, 200, 10000, RGBA(0.0, 0.2, 1.0, 0.0), las_sine_expanding,
|
||||
wriggle_singularity_laser_logic, vel, amp, freq, 0);
|
||||
for(int t = 0;; ++t, YIELD) {
|
||||
if(t == 140) {
|
||||
play_sfx("laser1");
|
||||
}
|
||||
|
||||
play_sound("charge_generic");
|
||||
aniplayer_queue(&boss->ani, "main", 0);
|
||||
laser_charge(l, t, 150, 10 + 10 * psin(l->args[0] + t / 60.0));
|
||||
l->args[3] = t / 10.0; // phase
|
||||
l->args[0] *= cexp(I*(M_PI/500.0) * spin_factor);
|
||||
|
||||
l->color = *HSLA((carg(l->args[0]) + M_PI) / (M_PI * 2), 1.0, 0.5, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage3_spell_light_singularity) {
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2 + VIEWPORT_H*I/2, 0.05);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
WAIT(100);
|
||||
aniplayer_queue(&boss->ani, "specialshot_charge", 1);
|
||||
aniplayer_queue(&boss->ani, "specialshot_release", 1);
|
||||
aniplayer_queue(&boss->ani, "main", 0);
|
||||
WAIT(20);
|
||||
|
||||
int nlasers = difficulty_value(3, 4, 5, 6);
|
||||
int nbullets = difficulty_value(8, 10, 12, 14);
|
||||
real laser_angle_ofs = difficulty_value(0.7, 0, 0.7, 0);
|
||||
real bullet_speed = difficulty_value(1, 0.8, 0.6, 0.4);
|
||||
|
||||
ProjPrototype *ptypes[] = {
|
||||
pp_thickrice,
|
||||
pp_rice,
|
||||
pp_bullet,
|
||||
pp_wave,
|
||||
pp_ball,
|
||||
pp_plainball,
|
||||
pp_bigball,
|
||||
pp_soul,
|
||||
};
|
||||
|
||||
int bullets_cycle = 0;
|
||||
|
||||
for(int i = 0; i < nlasers; ++i) {
|
||||
INVOKE_TASK(singularity_laser,
|
||||
.pos = boss->pos,
|
||||
.vel = 2 * cdir(laser_angle_ofs + M_PI / 4 + M_PI * 2 * i / (real)nlasers),
|
||||
.amp = (4.0 / nlasers) * (M_PI / 5),
|
||||
.freq = 0.05
|
||||
);
|
||||
}
|
||||
|
||||
if(time > 120) {
|
||||
play_sfx_loop("shot1_loop");
|
||||
}
|
||||
|
||||
if(time == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!((time+30) % 300)) {
|
||||
for(;;YIELD) {
|
||||
WAIT(270);
|
||||
aniplayer_queue(&boss->ani, "specialshot_charge", 1);
|
||||
aniplayer_queue(&boss->ani, "specialshot_release", 1);
|
||||
aniplayer_queue(&boss->ani, "main", 0);
|
||||
}
|
||||
WAIT(30);
|
||||
|
||||
if(!(time % 300)) {
|
||||
ProjPrototype *ptype = NULL;
|
||||
ProjPrototype *ptype = ptypes[bullets_cycle];
|
||||
|
||||
switch(time / 300 - 1) {
|
||||
case 0: ptype = pp_thickrice; break;
|
||||
case 1: ptype = pp_rice; break;
|
||||
case 2: ptype = pp_bullet; break;
|
||||
case 3: ptype = pp_wave; break;
|
||||
case 4: ptype = pp_ball; break;
|
||||
case 5: ptype = pp_plainball; break;
|
||||
case 6: ptype = pp_bigball; break;
|
||||
default: ptype = pp_soul; break;
|
||||
if(bullets_cycle < ARRAY_SIZE(ptypes) - 1) {
|
||||
++bullets_cycle;
|
||||
}
|
||||
|
||||
int cnt = 6 + 2 * global.diff;
|
||||
float colorofs = frand();
|
||||
real colorofs = rng_real();
|
||||
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
double a = ((M_PI*2.0*i)/cnt);
|
||||
cmplx dir = cexp(I*a);
|
||||
for(int i = 0; i < nbullets; ++i) {
|
||||
real f = i / (real)nbullets;
|
||||
cmplx dir = cdir(M_TAU * f);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = ptype,
|
||||
.pos = boss->pos,
|
||||
.color = HSLA(a/(M_PI*2) + colorofs, 1.0, 0.5, 0),
|
||||
.rule = asymptotic,
|
||||
.args = {
|
||||
dir * (1.2 - 0.2 * global.diff),
|
||||
20
|
||||
},
|
||||
.color = HSLA(f + colorofs, 1.0, 0.5, 0),
|
||||
.move = move_asymptotic_simple(dir * bullet_speed, 20),
|
||||
);
|
||||
}
|
||||
|
||||
play_sound("shot_special1");
|
||||
play_sfx("shot_special1");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,21 +14,192 @@
|
|||
#include "common_tasks.h"
|
||||
#include "global.h"
|
||||
|
||||
MODERNIZE_THIS_FILE_AND_REMOVE_ME
|
||||
TASK(aimcircle, { int lifetime; int focustime; }) {
|
||||
auto *p = TASK_BIND(PARTICLE(
|
||||
.sprite_ptr = res_sprite("fairy_circle"),
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
.flags = PFLAG_NOMOVE | PFLAG_REQUIREDPARTICLE | PFLAG_MANUALANGLE | PFLAG_NOAUTOREMOVE,
|
||||
.pos = global.plr.pos,
|
||||
.angle_delta = M_TAU/60,
|
||||
.color = RGB(2, 0.75, 0.5),
|
||||
));
|
||||
|
||||
void wriggle_moonlight_rocket(Boss *boss, int time) {
|
||||
int i, j, cnt = 1 + global.diff;
|
||||
TIMER(&time)
|
||||
int spawntime = 30;
|
||||
int despawntime = 30;
|
||||
|
||||
AT(EVENT_DEATH) {
|
||||
enemy_kill_all(&global.enemies);
|
||||
return;
|
||||
for(int t = 0; t < ARGS.lifetime; ++t, YIELD) {
|
||||
if(t < ARGS.focustime) {
|
||||
capproach_asymptotic_p(&p->pos, global.plr.pos, 0.2, 1e-3);
|
||||
} else if(t == ARGS.focustime) {
|
||||
p->pos = global.plr.pos;
|
||||
}
|
||||
|
||||
float sf = fminf(t / (float)spawntime, 1.0f);
|
||||
float sf2 = fminf(t / (float)(spawntime * 2), 1.0f);
|
||||
float df = fminf((ARGS.lifetime - t - 1) / (float)despawntime, 1.0f);
|
||||
|
||||
df = glm_ease_back_out(df);
|
||||
p->opacity = glm_ease_quad_out(sf) * glm_ease_quad_in(df) * 0.1f;
|
||||
sf = glm_ease_quad_in(sf);
|
||||
sf = glm_ease_back_out(sf) * 0.9 + sf * 0.1;
|
||||
|
||||
p->scale = (1+I) * (1.0f + 6 * (1 - sf));
|
||||
p->angle -= p->angle_delta * 10.0f * (1 - sf2);
|
||||
}
|
||||
|
||||
if(time < 0)
|
||||
GO_TO(boss, VIEWPORT_W/2 + VIEWPORT_H*I/2.5, 0.05)
|
||||
else if(time == 0) {
|
||||
for(j = -1; j < 2; j += 2) for(i = 0; i < cnt; ++i)
|
||||
create_enemy3c(boss->pos, ENEMY_IMMUNE, wriggle_slave_visual, wriggle_spell_slave, add_ref(boss), i*2*M_PI/cnt, j);
|
||||
kill_projectile(p);
|
||||
}
|
||||
|
||||
TASK(cancel_event, { CoEvent *event; }) {
|
||||
coevent_cancel(ARGS.event);
|
||||
}
|
||||
|
||||
TASK(laser_bullet, { BoxedProjectile p; BoxedLaser l; CoEvent *event; int event_time; }) {
|
||||
Laser *l = NOT_NULL(ENT_UNBOX(ARGS.l));
|
||||
|
||||
if(ARGS.event) {
|
||||
INVOKE_TASK_AFTER(&TASK_EVENTS(THIS_TASK)->finished, cancel_event, ARGS.event);
|
||||
}
|
||||
|
||||
Projectile *p = TASK_BIND(ARGS.p);
|
||||
|
||||
for(int t = 0; (l = ENT_UNBOX(ARGS.l)); ++t, YIELD) {
|
||||
p->pos = l->prule(l, t);
|
||||
|
||||
if(t == 0) {
|
||||
p->prevpos = p->pos;
|
||||
}
|
||||
|
||||
if(t == ARGS.event_time && ARGS.event) {
|
||||
coevent_signal(ARGS.event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
kill_projectile(p);
|
||||
}
|
||||
|
||||
TASK(rocket, { BoxedBoss boss; cmplx pos; cmplx dir; Color color; real phase; real accel_rate; int rocket_time; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
|
||||
real dt = ARGS.rocket_time;
|
||||
Laser *l = create_lasercurve4c(
|
||||
ARGS.pos, dt, dt, &ARGS.color, las_sine_expanding, 2.5*ARGS.dir, M_PI/20, 0.2, ARGS.phase
|
||||
);
|
||||
|
||||
Projectile *p = PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.color = RGB(1.0, 0.4, 0.6),
|
||||
.flags = PFLAG_NOMOVE,
|
||||
);
|
||||
|
||||
COEVENTS_ARRAY(phase2, explosion) events;
|
||||
TASK_HOST_EVENTS(events);
|
||||
|
||||
INVOKE_TASK(laser_bullet, ENT_BOX(p), ENT_BOX(l), &events.phase2, dt);
|
||||
WAIT_EVENT_OR_DIE(&events.phase2);
|
||||
|
||||
p = PROJECTILE(
|
||||
.pos = p->pos,
|
||||
.proto = pp_plainball,
|
||||
.color = RGB(1.0, 0.4, 0.6),
|
||||
.flags = PFLAG_NOMOVE,
|
||||
);
|
||||
|
||||
play_sfx("redirect");
|
||||
|
||||
cmplx dist = global.plr.pos - p->pos;
|
||||
cmplx accel = ARGS.accel_rate * cnormalize(dist);
|
||||
dt = sqrt(2 * cabs(dist) / ARGS.accel_rate);
|
||||
dt += 2 * rng_f64s();
|
||||
|
||||
l = create_lasercurve2c(p->pos, dt, dt, RGBA(0.4, 0.9, 1.0, 0.0), las_accel, 0, accel);
|
||||
l->width = 15;
|
||||
INVOKE_TASK(laser_bullet, ENT_BOX(p), ENT_BOX(l), &events.explosion, dt);
|
||||
WAIT_EVENT_OR_DIE(&events.explosion);
|
||||
// if we get here, p must be still alive and valid
|
||||
|
||||
int cnt = 22;
|
||||
real rot = (global.frames - NOT_NULL(boss->current)->starttime) * 0.0037 * global.diff;
|
||||
Color *c = HSLA(fmod(rot, M_TAU) / (M_TAU), 1.0, 0.5, 0);
|
||||
real boost = difficulty_value(4, 6, 8, 10);
|
||||
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
real f = (real)i/cnt;
|
||||
|
||||
cmplx dir = cdir(M_TAU * f + rot);
|
||||
cmplx v = (1.0 + psin(M_TAU * 9 * f)) * dir;
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_thickrice,
|
||||
.pos = p->pos,
|
||||
.color = c,
|
||||
.move = move_asymptotic_simple(v, boost),
|
||||
);
|
||||
}
|
||||
|
||||
real to = rng_range(30, 35);
|
||||
real scale = rng_range(2, 2.5);
|
||||
|
||||
PARTICLE(
|
||||
.proto = pp_blast,
|
||||
.pos = p->pos,
|
||||
.color = c,
|
||||
.timeout = to,
|
||||
.draw_rule = pdraw_timeout_scalefade(0.01, scale, 1, 0),
|
||||
.angle = rng_angle(),
|
||||
);
|
||||
|
||||
// FIXME: better sound
|
||||
play_sfx("enemydeath");
|
||||
play_sfx("shot1");
|
||||
play_sfx("shot1_special");
|
||||
|
||||
kill_projectile(p);
|
||||
}
|
||||
|
||||
TASK(rocket_slave, { BoxedBoss boss; real rot_speed; real rot_initial; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
|
||||
cmplx dir;
|
||||
|
||||
WriggleSlave *slave = stage3_host_wriggle_slave(boss->pos);
|
||||
INVOKE_SUBTASK(wriggle_slave_damage_trail, ENT_BOX(slave));
|
||||
INVOKE_SUBTASK(wriggle_slave_follow,
|
||||
.slave = ENT_BOX(slave),
|
||||
.boss = ENT_BOX(boss),
|
||||
.rot_speed = ARGS.rot_speed,
|
||||
.rot_initial = ARGS.rot_initial,
|
||||
.out_dir = &dir
|
||||
);
|
||||
|
||||
int rocket_time = 60;
|
||||
int warn_time = 20;
|
||||
int rperiod = difficulty_value(220, 200, 180, 160);
|
||||
real laccel = difficulty_value(0.15, 0.2, 0.25, 0.3);
|
||||
|
||||
WAIT(rperiod/2);
|
||||
|
||||
for(;;WAIT(rperiod)) {
|
||||
play_sfx("laser1");
|
||||
INVOKE_TASK_DELAYED(rocket_time - warn_time, aimcircle, 60 + warn_time, warn_time);
|
||||
INVOKE_TASK(rocket, ENT_BOX(boss), slave->pos, dir, *RGBA(1.0, 1.0, 0.5, 0.0), 0, laccel, rocket_time);
|
||||
INVOKE_TASK(rocket, ENT_BOX(boss), slave->pos, dir, *RGBA(0.5, 1.0, 0.5, 0.0), M_PI, laccel, rocket_time);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage3_spell_moonlight_rocket) {
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2 + VIEWPORT_H*I/2.5, 0.05);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
int nslaves = difficulty_value(2, 3, 4, 5);
|
||||
|
||||
for(int i = 0; i < nslaves; ++i) {
|
||||
INVOKE_SUBTASK(rocket_slave, ENT_BOX(boss), 1/70.0, i*M_TAU/nslaves);
|
||||
INVOKE_SUBTASK(rocket_slave, ENT_BOX(boss), -1/70.0, -i*M_TAU/nslaves);
|
||||
}
|
||||
|
||||
// keep subtasks alive
|
||||
STALL;
|
||||
}
|
||||
|
|
|
@ -11,110 +11,152 @@
|
|||
#include "spells.h"
|
||||
#include "../wriggle.h"
|
||||
|
||||
#include "refs.h"
|
||||
#include "common_tasks.h"
|
||||
#include "global.h"
|
||||
|
||||
MODERNIZE_THIS_FILE_AND_REMOVE_ME
|
||||
TASK(slave, { BoxedBoss boss; real rot_speed; real rot_initial; }) {
|
||||
Boss *boss = TASK_BIND(ARGS.boss);
|
||||
cmplx dir;
|
||||
|
||||
static int wriggle_ignite_laserbullet(Projectile *p, int time) {
|
||||
if(time == EVENT_DEATH) {
|
||||
free_ref(p->args[0]);
|
||||
return ACTION_ACK;
|
||||
} else if(time < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
WriggleSlave *slave = stage3_host_wriggle_slave(boss->pos);
|
||||
INVOKE_SUBTASK(wriggle_slave_damage_trail, ENT_BOX(slave));
|
||||
INVOKE_SUBTASK(wriggle_slave_follow,
|
||||
.slave = ENT_BOX(slave),
|
||||
.boss = ENT_BOX(boss),
|
||||
.rot_speed = ARGS.rot_speed,
|
||||
.rot_initial = ARGS.rot_initial,
|
||||
.out_dir = &dir
|
||||
);
|
||||
|
||||
Laser *laser = (Laser*)REF(p->args[0]);
|
||||
WAIT(300);
|
||||
|
||||
if(laser) {
|
||||
p->args[3] = laser->prule(laser, time - p->args[1]) - p->pos;
|
||||
}
|
||||
for(;;WAIT(180)) {
|
||||
int cnt = 5, i;
|
||||
|
||||
p->angle = carg(p->args[3]);
|
||||
p->pos = p->pos + p->args[3];
|
||||
for(i = 0; i < cnt; ++i) {
|
||||
PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = slave->pos,
|
||||
.color = RGBA(0.5, 1.0, 0.5, 0),
|
||||
.move = move_accelerated(0, 0.02 * cdir(i*M_TAU/cnt)),
|
||||
);
|
||||
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static void wriggle_ignite_warnlaser_logic(Laser *l, int time) {
|
||||
if(time == EVENT_BIRTH) {
|
||||
l->width = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if(time < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(time == 90) {
|
||||
play_sound_ex("laser1", 30, false);
|
||||
}
|
||||
|
||||
laser_charge(l, time, 90, 10);
|
||||
l->color = *color_lerp(RGBA(0.2, 0.2, 1, 0), RGBA(1, 0.2, 0.2, 0), time / l->deathtime);
|
||||
}
|
||||
|
||||
static void wriggle_ignite_warnlaser(Laser *l) {
|
||||
float f = 6;
|
||||
create_laser(l->pos, 90, 120, RGBA(1, 1, 1, 0), l->prule, wriggle_ignite_warnlaser_logic, f*l->args[0], l->args[1], f*l->args[2], l->args[3]);
|
||||
}
|
||||
|
||||
void wriggle_night_ignite(Boss *boss, int time) {
|
||||
TIMER(&time)
|
||||
|
||||
float dfactor = global.diff / (float)D_Lunatic;
|
||||
|
||||
if(time == EVENT_DEATH) {
|
||||
enemy_kill_all(&global.enemies);
|
||||
return;
|
||||
}
|
||||
|
||||
if(time < 0) {
|
||||
GO_TO(boss, VIEWPORT_W/2 + VIEWPORT_H*I/3, 0.05)
|
||||
return;
|
||||
}
|
||||
|
||||
AT(0) for(int j = -1; j < 2; j += 2) for(int i = 0; i < 7; ++i) {
|
||||
create_enemy4c(boss->pos, ENEMY_IMMUNE, wriggle_slave_visual, wriggle_spell_slave, add_ref(boss), i*2*M_PI/7, j, 1);
|
||||
}
|
||||
|
||||
FROM_TO_INT(0, 1000000, 180, 120, 10) {
|
||||
float dt = 200;
|
||||
float lt = 100 * dfactor;
|
||||
|
||||
float a = _ni*M_PI/2.5 + _i + time;
|
||||
float b = 0.3;
|
||||
float c = 0.3;
|
||||
|
||||
cmplx vel = 2 * cexp(I*a);
|
||||
double amp = M_PI/5;
|
||||
double freq = 0.05;
|
||||
|
||||
Laser *l1 = create_lasercurve3c(boss->pos, lt, dt, RGBA(b, b, 1.0, 0.0), las_sine_expanding, vel, amp, freq);
|
||||
wriggle_ignite_warnlaser(l1);
|
||||
|
||||
Laser *l2 = create_lasercurve3c(boss->pos, lt * 1.5, dt, RGBA(1.0, b, b, 0.0), las_sine_expanding, vel, amp, freq - 0.002 * fmin(global.diff, D_Hard));
|
||||
wriggle_ignite_warnlaser(l2);
|
||||
|
||||
Laser *l3 = create_lasercurve3c(boss->pos, lt, dt, RGBA(b, b, 1.0, 0.0), las_sine_expanding, vel, amp, freq - 0.004 * fmin(global.diff, D_Hard));
|
||||
wriggle_ignite_warnlaser(l3);
|
||||
|
||||
for(int i = 0; i < 5 + 15 * dfactor; ++i) {
|
||||
#define LASERBULLLET(pproto, clr, laser) \
|
||||
PROJECTILE(.proto = (pproto), .pos = boss->pos, .color = (clr), .rule = wriggle_ignite_laserbullet, .args = { add_ref(laser), i })
|
||||
|
||||
LASERBULLLET(pp_plainball, RGBA(c, c, 1.0, 0), l1);
|
||||
LASERBULLLET(pp_bigball, RGBA(1.0, c, c, 0), l2);
|
||||
LASERBULLLET(pp_plainball, RGBA(c, c, 1.0, 0), l3);
|
||||
|
||||
#undef LASERBULLLET
|
||||
|
||||
// FIXME: better sound
|
||||
play_sound("shot1");
|
||||
if(global.diff > D_Hard) {
|
||||
PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = slave->pos,
|
||||
.color = RGBA(1.0, 1.0, 0.5, 0),
|
||||
.move = move_accelerated(0, 0.01 * cdir(i*2*M_TAU/cnt)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: better sound
|
||||
play_sound_ex("shot_special1", 1, false);
|
||||
play_sfx("shot_special1");
|
||||
}
|
||||
}
|
||||
|
||||
TASK(warnlaser, { BoxedLaser base_laser; }) {
|
||||
Laser *b = NOT_NULL(ENT_UNBOX(ARGS.base_laser));
|
||||
|
||||
real f = 6;
|
||||
Laser *l = TASK_BIND(create_laser(
|
||||
b->pos, 90, 120, RGBA(1, 1, 1, 0), b->prule, NULL,
|
||||
f*b->args[0], b->args[1], f*b->args[2], b->args[3]
|
||||
));
|
||||
|
||||
laser_make_static(l);
|
||||
|
||||
for(int t = 0;; ++t, YIELD) {
|
||||
if(t == 90) {
|
||||
play_sfx_ex("laser1", 30, false);
|
||||
}
|
||||
|
||||
laser_charge(l, t, 90, 10);
|
||||
l->color = *color_lerp(RGBA(0.2, 0.2, 1, 0), RGBA(1, 0.2, 0.2, 0), t / l->deathtime);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(laserbullet, { ProjPrototype *proto; Color *color; BoxedLaser laser; cmplx pos; real time_offset; }) {
|
||||
Projectile *p = TASK_BIND(PROJECTILE(
|
||||
.proto = ARGS.proto,
|
||||
.color = ARGS.color,
|
||||
.pos = ARGS.pos,
|
||||
.move = move_linear(0),
|
||||
));
|
||||
|
||||
int t = 0;
|
||||
for(Laser *l; (l = ENT_UNBOX(ARGS.laser)); ++t, YIELD) {
|
||||
p->move.velocity = l->prule(l, t + ARGS.time_offset) - p->pos;
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage3_spell_night_ignite) {
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2 + VIEWPORT_H*I/3, 0.05);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
int nslaves = 7;
|
||||
|
||||
for(int i = 0; i < nslaves; ++i) {
|
||||
INVOKE_SUBTASK(slave, ENT_BOX(boss), 1/70.0, i*M_TAU/nslaves);
|
||||
INVOKE_SUBTASK(slave, ENT_BOX(boss), -1/70.0, -i*M_TAU/nslaves);
|
||||
}
|
||||
|
||||
real dt = 200;
|
||||
real lt = difficulty_value(25, 50, 75, 100);
|
||||
real amp = M_PI/5;
|
||||
real freq = 0.05;
|
||||
int laserbullets = difficulty_value(8, 12, 16, 20);
|
||||
|
||||
int t = 0;
|
||||
|
||||
for(int cycle = 0;; ++cycle, t += WAIT(180)) {
|
||||
for(int step = 0; step < 12; ++step, t += WAIT(10)) {
|
||||
real a = step * M_PI/2.5 + cycle + t;
|
||||
cmplx vel = 2 * cdir(a);
|
||||
|
||||
Laser *l1 = create_lasercurve3c(
|
||||
boss->pos, lt, dt,
|
||||
RGBA(0.3, 0.3, 1.0, 0.0),
|
||||
las_sine_expanding, vel, amp, freq
|
||||
);
|
||||
INVOKE_TASK(warnlaser, ENT_BOX(l1));
|
||||
|
||||
Laser *l2 = create_lasercurve3c(
|
||||
boss->pos, lt * 1.5, dt,
|
||||
RGBA(1.0, 0.3, 0.3, 0.0),
|
||||
las_sine_expanding, vel, amp, freq - 0.002 * imin(global.diff, D_Hard)
|
||||
);
|
||||
INVOKE_TASK(warnlaser, ENT_BOX(l2));
|
||||
|
||||
Laser *l3 = create_lasercurve3c(
|
||||
boss->pos, lt, dt,
|
||||
RGBA(0.3, 0.3, 1.0, 0.0),
|
||||
las_sine_expanding, vel, amp, freq - 0.004 * imin(global.diff, D_Hard)
|
||||
);
|
||||
INVOKE_TASK(warnlaser, ENT_BOX(l3));
|
||||
|
||||
for(int i = 0; i < laserbullets; ++i) {
|
||||
#define LASERBULLLET(_proto, _color, _laser) \
|
||||
INVOKE_TASK(laserbullet, \
|
||||
.proto = _proto, \
|
||||
.color = _color, \
|
||||
.laser = ENT_BOX(_laser), \
|
||||
.pos = boss->pos, \
|
||||
.time_offset = -i \
|
||||
)
|
||||
|
||||
LASERBULLLET(pp_plainball, RGBA(0.3, 0.3, 1.0, 0), l1);
|
||||
LASERBULLLET(pp_bigball, RGBA(1.0, 0.3, 0.3, 0), l2);
|
||||
LASERBULLLET(pp_plainball, RGBA(0.3, 0.3, 1.0, 0), l3);
|
||||
|
||||
play_sfx("shot1");
|
||||
}
|
||||
|
||||
play_sfx_ex("shot_special1", 1, false);
|
||||
}
|
||||
}
|
||||
|
||||
STALL;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
|
||||
#include "boss.h"
|
||||
|
||||
void scuttle_deadly_dance(Boss*, int t);
|
||||
void wriggle_moonlight_rocket(Boss*, int t);
|
||||
void wriggle_night_ignite(Boss*, int t);
|
||||
void wriggle_firefly_storm(Boss*, int t);
|
||||
void wriggle_light_singularity(Boss*, int t);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage3_spell_firefly_storm, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage3_spell_light_singularity, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage3_spell_moonlight_rocket, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage3_spell_night_ignite, BossAttack);
|
||||
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage3_spell_deadly_dance, BossAttack);
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
#include "scuttle.h"
|
||||
#include "spells/spells.h"
|
||||
#include "timeline.h"
|
||||
#include "wriggle.h"
|
||||
#include "scuttle.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "portrait.h"
|
||||
|
@ -31,28 +29,28 @@ struct stage3_spells_s stage3_spells = {
|
|||
.mid = {
|
||||
.deadly_dance = {
|
||||
{ 0, 1, 2, 3}, AT_SurvivalSpell, "Venom Sign “Deadly Dance”", 14, 40000,
|
||||
scuttle_deadly_dance, stage3_draw_scuttle_spellbg, BOSS_DEFAULT_GO_POS, 3
|
||||
NULL, stage3_draw_scuttle_spellbg, VIEWPORT_W/2.0+100*I, 1, TASK_INDIRECT_INIT(BossAttack, stage3_spell_deadly_dance)
|
||||
},
|
||||
},
|
||||
|
||||
.boss = {
|
||||
.moonlight_rocket = {
|
||||
{ 6, 7, 8, 9}, AT_Spellcard, "Firefly Sign “Moonlight Rocket”", 40, 40000,
|
||||
wriggle_moonlight_rocket, stage3_draw_wriggle_spellbg, BOSS_DEFAULT_GO_POS, 3
|
||||
NULL, stage3_draw_wriggle_spellbg, VIEWPORT_W/2.0+100*I, 1, TASK_INDIRECT_INIT(BossAttack, stage3_spell_moonlight_rocket)
|
||||
},
|
||||
.wriggle_night_ignite = {
|
||||
{10, 11, 12, 13}, AT_Spellcard, "Light Source “Wriggle Night Ignite”", 50, 46000,
|
||||
wriggle_night_ignite, stage3_draw_wriggle_spellbg, BOSS_DEFAULT_GO_POS, 3
|
||||
NULL, stage3_draw_wriggle_spellbg, VIEWPORT_W/2.0+100*I, 1, TASK_INDIRECT_INIT(BossAttack, stage3_spell_night_ignite)
|
||||
},
|
||||
.firefly_storm = {
|
||||
{14, 15, 16, 17}, AT_Spellcard, "Bug Sign “Firefly Storm”", 45, 45000,
|
||||
wriggle_firefly_storm, stage3_draw_wriggle_spellbg, BOSS_DEFAULT_GO_POS, 3
|
||||
NULL, stage3_draw_wriggle_spellbg, VIEWPORT_W/2.0+100*I, 1, TASK_INDIRECT_INIT(BossAttack, stage3_spell_firefly_storm)
|
||||
},
|
||||
},
|
||||
|
||||
.extra.light_singularity = {
|
||||
{ 0, 1, 2, 3}, AT_ExtraSpell, "Lamp Sign “Light Singularity”", 75, 45000,
|
||||
wriggle_light_singularity, stage3_draw_wriggle_spellbg, BOSS_DEFAULT_GO_POS, 3
|
||||
NULL, stage3_draw_wriggle_spellbg, VIEWPORT_W/2.0+100*I, 1, TASK_INDIRECT_INIT(BossAttack, stage3_spell_light_singularity)
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -61,6 +59,7 @@ static void stage3_start(void) {
|
|||
stage3_bg_init_fullstage();
|
||||
stage_start_bgm("stage3");
|
||||
stage_set_voltage_thresholds(50, 125, 300, 600);
|
||||
INVOKE_TASK(stage3_timeline);
|
||||
}
|
||||
|
||||
static void stage3_spellpractice_start(void) {
|
||||
|
@ -87,6 +86,8 @@ static void stage3_preload(void) {
|
|||
portrait_preload_face_sprite("scuttle", "normal", RESF_DEFAULT);
|
||||
preload_resources(RES_BGM, RESF_OPTIONAL, "stage3", "stage3boss", NULL);
|
||||
preload_resources(RES_TEXTURE, RESF_DEFAULT,
|
||||
"ibl_brdf_lut",
|
||||
"stage3/envmap",
|
||||
"stage3/spellbg1",
|
||||
"stage3/spellbg2",
|
||||
"stage3/wspellbg",
|
||||
|
@ -131,7 +132,6 @@ StageProcs stage3_procs = {
|
|||
.draw = stage3_draw,
|
||||
.end = stage3_end,
|
||||
.preload = stage3_preload,
|
||||
.event = stage3_events,
|
||||
.shader_rules = stage3_bg_effects,
|
||||
.postprocess_rules = stage3_postprocess,
|
||||
.spellpractice_procs = &(StageProcs) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,6 +9,6 @@
|
|||
#pragma once
|
||||
#include "taisei.h"
|
||||
|
||||
void stage3_events(void);
|
||||
#include "coroutine.h"
|
||||
|
||||
#define STAGE3_MIDBOSS_TIME 1765
|
||||
DECLARE_EXTERN_TASK(stage3_timeline);
|
||||
|
|
|
@ -38,228 +38,108 @@ Boss *stage3_spawn_wriggle(cmplx pos) {
|
|||
return wriggle;
|
||||
}
|
||||
|
||||
void wriggle_slave_visual(Enemy *e, int time, bool render) {
|
||||
if(time < 0)
|
||||
return;
|
||||
static void wriggle_slave_draw(EntityInterface *e) {
|
||||
WriggleSlave *slave = ENT_CAST(e, WriggleSlave);
|
||||
int time = global.frames - slave->spawn_time;
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.pos.as_cmplx = slave->pos,
|
||||
.sprite_ptr = slave->sprites.circle,
|
||||
.rotation.angle = DEG2RAD * 7 * time,
|
||||
.scale.as_cmplx = slave->scale,
|
||||
.color = &slave->color,
|
||||
});
|
||||
}
|
||||
|
||||
TASK(wriggle_slave_particles, { BoxedWriggleSlave slave; }) {
|
||||
WriggleSlave *slave = TASK_BIND(ARGS.slave);
|
||||
|
||||
int period = 5;
|
||||
WAIT(rng_irange(0, period));
|
||||
|
||||
for(;;WAIT(period)) {
|
||||
cmplx vel = 2 * rng_dir();
|
||||
|
||||
if(render) {
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite = "fairy_circle",
|
||||
.rotation.angle = DEG2RAD * 7 * time,
|
||||
.scale.both = 0.7,
|
||||
.color = RGBA(0.8, 1.0, 0.4, 0),
|
||||
.pos = { creal(e->pos), cimag(e->pos) },
|
||||
});
|
||||
} else if(time % 5 == 0) {
|
||||
tsrand_fill(2);
|
||||
PARTICLE(
|
||||
.sprite = "smoothdot",
|
||||
.pos = 5*cexp(2*I*M_PI*afrand(0)),
|
||||
.sprite_ptr = slave->sprites.particle,
|
||||
.pos = slave->pos,
|
||||
.color = RGBA(0.6, 0.6, 0.5, 0),
|
||||
.draw_rule = Shrink,
|
||||
.rule = enemy_flare,
|
||||
.timeout = 60,
|
||||
.args = {
|
||||
0.3*cexp(2*M_PI*I*afrand(1)),
|
||||
add_ref(e),
|
||||
},
|
||||
.draw_rule = pdraw_timeout_scale(2, 0.01),
|
||||
.timeout = 20,
|
||||
.move = move_linear(vel),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
DEPRECATED_DRAW_RULE
|
||||
static void wriggle_slave_part_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
|
||||
float b = 1 - t / (double)p->timeout;
|
||||
r_mat_mv_push();
|
||||
r_mat_mv_translate(creal(p->pos), cimag(p->pos), 0);
|
||||
ProjDrawCore(p, color_mul_scalar(COLOR_COPY(&p->color), b));
|
||||
r_mat_mv_pop();
|
||||
void stage3_init_wriggle_slave(WriggleSlave *slave, cmplx pos) {
|
||||
slave->pos = pos;
|
||||
slave->spawn_time = global.frames;
|
||||
slave->ent.draw_layer = LAYER_BOSS - 1;
|
||||
slave->ent.draw_func = wriggle_slave_draw;
|
||||
slave->sprites.circle = res_sprite("fairy_circle");
|
||||
slave->sprites.particle = res_sprite("part/smoothdot");
|
||||
|
||||
INVOKE_TASK(wriggle_slave_particles, ENT_BOX(slave));
|
||||
}
|
||||
|
||||
static int wriggle_rocket_laserbullet(Projectile *p, int time) {
|
||||
if(time == EVENT_DEATH) {
|
||||
free_ref(p->args[0]);
|
||||
return ACTION_ACK;
|
||||
} else if(time < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
WriggleSlave *stage3_host_wriggle_slave(cmplx pos) {
|
||||
WriggleSlave *slave = TASK_HOST_ENT(WriggleSlave);
|
||||
TASK_HOST_EVENTS(slave->events);
|
||||
stage3_init_wriggle_slave(slave, pos);
|
||||
|
||||
if(time >= creal(p->args[1])) {
|
||||
if(p->args[2]) {
|
||||
cmplx dist = global.plr.pos - p->pos;
|
||||
cmplx accel = (0.1 + 0.2 * (global.diff / (float)D_Lunatic)) * dist / cabs(dist);
|
||||
float deathtime = sqrt(2*cabs(dist)/cabs(accel));
|
||||
// TODO spawn animation
|
||||
// INVOKE_TASK(wriggle_slave_fadein, ENT_BOX(slave));
|
||||
slave->color = *RGBA(0.8, 1.0, 0.4, 0);
|
||||
slave->scale = (1 + I) * 0.7;
|
||||
|
||||
Laser *l = create_lasercurve2c(p->pos, deathtime, deathtime, RGBA(0.4, 0.9, 1.0, 0.0), las_accel, 0, accel);
|
||||
l->width = 15;
|
||||
// TODO despawn animation
|
||||
// INVOKE_TASK_AFTER(&slave->events.despawned, wriggle_slave_fadeout, ENT_BOX(slave));
|
||||
|
||||
PROJECTILE(
|
||||
.proto = p->proto,
|
||||
.pos = p->pos,
|
||||
.color = &p->color,
|
||||
.draw_rule = p->draw_rule,
|
||||
.rule = wriggle_rocket_laserbullet,
|
||||
.args = {
|
||||
add_ref(l),
|
||||
deathtime,
|
||||
}
|
||||
);
|
||||
|
||||
play_sound("redirect");
|
||||
play_sound("shot_special1");
|
||||
} else {
|
||||
int cnt = 22, i;
|
||||
float rot = (global.frames - global.boss->current->starttime) * 0.0037 * (global.diff);
|
||||
Color *c = HSLA(fmod(rot, M_PI*2)/(M_PI/2), 1.0, 0.5, 0);
|
||||
|
||||
for(i = 0; i < cnt; ++i) {
|
||||
float f = (float)i/cnt;
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_thickrice,
|
||||
.pos = p->pos,
|
||||
.color = c,
|
||||
.rule = asymptotic,
|
||||
.args = {
|
||||
(1.0 + psin(M_PI*18*f)) * cexp(I*(2.0*M_PI*f+rot)),
|
||||
2 + 2 * global.diff
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
PARTICLE(
|
||||
.proto = pp_blast,
|
||||
.pos = p->pos,
|
||||
.color = c,
|
||||
.timeout = 35 - 5 * frand(),
|
||||
.draw_rule = GrowFade,
|
||||
.args = { 0, 1 + 0.5 * frand() },
|
||||
.angle = M_PI * 2 * frand(),
|
||||
);
|
||||
|
||||
// FIXME: better sound
|
||||
play_sound("enemydeath");
|
||||
play_sound("shot1");
|
||||
play_sound("shot3");
|
||||
}
|
||||
|
||||
return ACTION_DESTROY;
|
||||
}
|
||||
|
||||
Laser *laser = (Laser*)REF(p->args[0]);
|
||||
|
||||
if(!laser)
|
||||
return ACTION_DESTROY;
|
||||
|
||||
p->pos = laser->prule(laser, time);
|
||||
|
||||
return 1;
|
||||
return slave;
|
||||
}
|
||||
|
||||
int wriggle_spell_slave(Enemy *e, int time) {
|
||||
TIMER(&time)
|
||||
void stage3_despawn_wriggle_slave(WriggleSlave *slave) {
|
||||
coevent_signal_once(&slave->events.despawned);
|
||||
}
|
||||
|
||||
float angle = e->args[2] * (time / 70.0 + e->args[1]);
|
||||
cmplx dir = cexp(I*angle);
|
||||
Boss *boss = (Boss*)REF(e->args[0]);
|
||||
DEFINE_EXTERN_TASK(wriggle_slave_damage_trail) {
|
||||
WriggleSlave *slave = TASK_BIND(ARGS.slave);
|
||||
ShaderProgram *pshader = res_shader("sprite_default");
|
||||
|
||||
if(!boss)
|
||||
return ACTION_DESTROY;
|
||||
|
||||
AT(EVENT_BIRTH) {
|
||||
e->ent.draw_layer = LAYER_BULLET - 1;
|
||||
}
|
||||
|
||||
AT(EVENT_DEATH) {
|
||||
free_ref(e->args[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
GO_TO(e, boss->pos + 100 * sin(time / 100.0) * dir, 0.03)
|
||||
|
||||
if(!(time % 2)) {
|
||||
float c = 0.5 * psin(time / 25.0);
|
||||
for(;;WAIT(2)) {
|
||||
float t = (global.frames - slave->spawn_time) / 25.0;
|
||||
float c = 0.5f * psinf(t);
|
||||
|
||||
PROJECTILE(
|
||||
// FIXME: add prototype, or shove it into the basic ones somehow,
|
||||
// or just replace this with some thing else
|
||||
.sprite_ptr = get_sprite("part/smoothdot"),
|
||||
// XXX: Do we want this to be a special snowflake without a ProjPrototype?
|
||||
.sprite_ptr = slave->sprites.particle,
|
||||
.size = 16 + 16*I,
|
||||
.collision_size = 7.2 + 7.2*I,
|
||||
|
||||
.pos = e->pos,
|
||||
.pos = slave->pos,
|
||||
.color = RGBA(1.0 - c, 0.5, 0.5 + c, 0),
|
||||
.draw_rule = wriggle_slave_part_draw,
|
||||
.draw_rule = pdraw_timeout_fade(1, 0),
|
||||
.timeout = 60,
|
||||
.shader = "sprite_default",
|
||||
.shader_ptr = pshader,
|
||||
.flags = PFLAG_NOCLEAR | PFLAG_NOCLEAREFFECT | PFLAG_NOCOLLISIONEFFECT | PFLAG_NOSPAWNEFFECTS,
|
||||
);
|
||||
}
|
||||
|
||||
// moonlight rocket rockets
|
||||
int rocket_period = (160 + 20 * (D_Lunatic - global.diff));
|
||||
|
||||
if(!creal(e->args[3]) && !((time + rocket_period/2) % rocket_period)) {
|
||||
Laser *l;
|
||||
float dt = 60;
|
||||
|
||||
l = create_lasercurve4c(e->pos, dt, dt, RGBA(1.0, 1.0, 0.5, 0.0), las_sine_expanding, 2.5*dir, M_PI/20, 0.2, 0);
|
||||
PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = e->pos,
|
||||
.color = RGB(1.0, 0.4, 0.6),
|
||||
.rule = wriggle_rocket_laserbullet,
|
||||
.args = {
|
||||
add_ref(l), dt-1, 1
|
||||
}
|
||||
);
|
||||
|
||||
l = create_lasercurve4c(e->pos, dt, dt, RGBA(0.5, 1.0, 0.5, 0.0), las_sine_expanding, 2.5*dir, M_PI/20, 0.2, M_PI);
|
||||
PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = e->pos,
|
||||
.color = RGB(1.0, 0.4, 0.6),
|
||||
.rule = wriggle_rocket_laserbullet,
|
||||
.args = {
|
||||
add_ref(l), dt-1, 1
|
||||
},
|
||||
);
|
||||
|
||||
play_sound("laser1");
|
||||
}
|
||||
|
||||
// night ignite balls
|
||||
if(creal(e->args[3]) && global.diff > D_Easy) {
|
||||
FROM_TO(300, 1000000, 180) {
|
||||
int cnt = 5, i;
|
||||
for(i = 0; i < cnt; ++i) {
|
||||
PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = e->pos,
|
||||
.color = RGBA(0.5, 1.0, 0.5, 0),
|
||||
.rule = accelerated,
|
||||
.args = {
|
||||
0, 0.02 * cexp(I*i*2*M_PI/cnt)
|
||||
},
|
||||
);
|
||||
|
||||
if(global.diff > D_Hard) {
|
||||
PROJECTILE(
|
||||
.proto = pp_ball,
|
||||
.pos = e->pos,
|
||||
.color = RGBA(1.0, 1.0, 0.5, 0),
|
||||
.rule = accelerated,
|
||||
.args = {
|
||||
0, 0.01 * cexp(I*i*2*M_PI/cnt)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: better sound
|
||||
play_sound("shot_special1");
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(wriggle_slave_follow) {
|
||||
WriggleSlave *slave = TASK_BIND(ARGS.slave);
|
||||
Boss *boss = NOT_NULL(ENT_UNBOX(ARGS.boss));
|
||||
|
||||
MoveParams move = move_towards(0, 0.03);
|
||||
cmplx dir = cdir(ARGS.rot_initial);
|
||||
cmplx r = cdir(ARGS.rot_speed);
|
||||
|
||||
for(;(boss = ENT_UNBOX(ARGS.boss)); YIELD) {
|
||||
real t = global.frames - slave->spawn_time;
|
||||
move.attraction_point = boss->pos + 100 * sin(t / 100) * dir;
|
||||
move_update(&slave->pos, &move);
|
||||
if(ARGS.out_dir) {
|
||||
*ARGS.out_dir = dir;
|
||||
}
|
||||
dir *= r;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,33 @@
|
|||
|
||||
#include "boss.h"
|
||||
|
||||
DEFINE_ENTITY_TYPE(WriggleSlave, {
|
||||
struct {
|
||||
Sprite *circle, *particle;
|
||||
} sprites;
|
||||
|
||||
cmplx pos;
|
||||
int spawn_time;
|
||||
Color color;
|
||||
cmplxf scale;
|
||||
|
||||
COEVENTS_ARRAY(
|
||||
despawned
|
||||
) events;
|
||||
});
|
||||
|
||||
Boss *stage3_spawn_wriggle(cmplx pos);
|
||||
void stage3_draw_wriggle_spellbg(Boss *boss, int time);
|
||||
|
||||
void wriggle_slave_visual(Enemy *e, int time, bool render);
|
||||
int wriggle_spell_slave(Enemy *e, int time);
|
||||
void stage3_init_wriggle_slave(WriggleSlave *slave, cmplx pos);
|
||||
WriggleSlave *stage3_host_wriggle_slave(cmplx pos);
|
||||
void stage3_despawn_wriggle_slave(WriggleSlave *slave);
|
||||
|
||||
DECLARE_EXTERN_TASK(wriggle_slave_damage_trail, { BoxedWriggleSlave slave; });
|
||||
DECLARE_EXTERN_TASK(wriggle_slave_follow, {
|
||||
BoxedWriggleSlave slave;
|
||||
BoxedBoss boss;
|
||||
real rot_speed;
|
||||
real rot_initial;
|
||||
cmplx *out_dir;
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue