stage3: restructure into a folder-based layout

It's an absolute mess, but should make integrating stage3_revamp easier…
This commit is contained in:
Andrei Alexeyev 2021-06-23 02:36:44 +03:00
parent 4c203ed0bf
commit ad9228e0a0
No known key found for this signature in database
GPG key ID: 72D26128040B9690
26 changed files with 2357 additions and 2035 deletions

View file

@ -12,10 +12,10 @@
#include "stages/stage1/stage1.h"
#include "stages/stage2/stage2.h"
#include "stages/stage3/stage3.h"
#include "stages/stage4/stage4.h"
#include "stages/stage5/stage5.h"
#include "stages/stage6/stage6.h"
#include "stages/stage3.h"
#include "stages/extra.h"
#ifdef DEBUG

View file

@ -4,6 +4,7 @@ stages_src = []
stages = [
'stage1',
'stage2',
'stage3',
'stage4',
'stage5',
'stage6',
@ -18,8 +19,6 @@ endforeach
# them into the "stages" array
stages_src += files(
'stage3.c',
'stage3_events.c',
'extra.c',
)

View file

@ -1,333 +0,0 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "stage3.h"
#include "stage3_events.h"
#include "global.h"
#include "stage.h"
#include "stageutils.h"
#include "util/glm.h"
#include "portrait.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
/*
* See the definition of AttackInfo in boss.h for information on how to set up the idmaps.
* To add, remove, or reorder spells, see this stage's header file.
*/
struct stage3_spells_s stage3_spells = {
.mid = {
.deadly_dance = {
{ 0, 1, 2, 3}, AT_SurvivalSpell, "Venom Sign “Deadly Dance”", 14, 40000,
scuttle_deadly_dance, scuttle_spellbg, BOSS_DEFAULT_GO_POS, 3
},
},
.boss = {
.moonlight_rocket = {
{ 6, 7, 8, 9}, AT_Spellcard, "Firefly Sign “Moonlight Rocket”", 40, 40000,
wriggle_moonlight_rocket, wriggle_spellbg, BOSS_DEFAULT_GO_POS, 3
},
.wriggle_night_ignite = {
{10, 11, 12, 13}, AT_Spellcard, "Light Source “Wriggle Night Ignite”", 50, 46000,
wriggle_night_ignite, wriggle_spellbg, BOSS_DEFAULT_GO_POS, 3
},
.firefly_storm = {
{14, 15, 16, 17}, AT_Spellcard, "Bug Sign “Firefly Storm”", 45, 45000,
wriggle_firefly_storm, wriggle_spellbg, BOSS_DEFAULT_GO_POS, 3
},
},
.extra.light_singularity = {
{ 0, 1, 2, 3}, AT_ExtraSpell, "Lamp Sign “Light Singularity”", 75, 45000,
wriggle_light_singularity, wriggle_spellbg, BOSS_DEFAULT_GO_POS, 3
},
};
static uint stage3_bg_pos(Stage3D *s3d, vec3 pos, float maxrange) {
vec3 p = {
0,
0,
0,
};
// minus sign for drawing order
vec3 r = {0, -20, -10};
return linear3dpos(s3d, pos, maxrange, p, r);
}
static void stage3_bg_setup_pbr_lighting(void) {
Camera3D *cam = &stage_3d_context.cam;
PointLight3D lights[] = {
// TODO animate colors
{ { 0, 0, 10000 }, { 10, 42, 30 } },
{ { 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);
}
vec3 r;
camera3d_unprojected_ray(cam, global.plr.pos,r);
glm_vec3_scale(r, 5, r);
glm_vec3_add(cam->pos, r, lights[1].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);
real f = 1/(1+global.frames/1000.);
r_uniform_vec3("ambient_color",f,f,sqrt(f));
}
static void stage3_bg_ground_draw(vec3 pos) {
r_mat_mv_push();
r_mat_mv_translate(pos[0], pos[1], pos[2]);
r_shader("pbr");
//r_uniform_vec3_array("light_positions[0]", 0, 1, &stage_3d_context.cx);
stage3_bg_setup_pbr_lighting();
r_uniform_float("metallic", 0);
r_uniform_sampler("tex", "stage3/ground_diffuse");
r_uniform_sampler("roughness_map", "stage3/ground_roughness");
r_uniform_sampler("normal_map", "stage3/ground_normal");
r_uniform_sampler("ambient_map", "stage3/ground_ambient");
r_draw_model("stage3/ground");
r_uniform_sampler("tex", "stage3/trees_diffuse");
r_uniform_sampler("roughness_map", "stage3/trees_roughness");
r_uniform_sampler("normal_map", "stage3/trees_normal");
r_uniform_sampler("ambient_map", "stage3/trees_ambient");
r_draw_model("stage3/trees");
r_uniform_sampler("tex", "stage3/rocks_diffuse");
r_uniform_sampler("roughness_map", "stage3/rocks_roughness");
r_uniform_sampler("normal_map", "stage3/rocks_normal");
r_uniform_sampler("ambient_map", "stage3/rocks_ambient");
r_draw_model("stage3/rocks");
r_mat_mv_pop();
r_shader_standard();}
static void stage3_bg_leaves_draw(vec3 pos) {
r_mat_mv_push();
r_mat_mv_translate(pos[0], pos[1], pos[2]);
r_mat_mv_translate(0,0,-0.0002);
r_shader("pbr");
stage3_bg_setup_pbr_lighting();
r_uniform_float("metallic", 0);
r_uniform_sampler("tex", "stage3/leaves_diffuse");
r_uniform_sampler("roughness_map", "stage3/leaves_roughness");
r_uniform_sampler("normal_map", "stage3/leaves_normal");
r_uniform_sampler("ambient_map", "stage3/leaves_ambient");
r_draw_model("stage3/leaves");
r_mat_mv_pop();
r_shader_standard();
}
static bool stage3_fog(Framebuffer *fb) {
r_shader("zbuf_fog");
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_float("end", 2);
r_uniform_float("exponent", 10);
r_uniform_float("sphereness", 0);
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
return true;
}
static bool stage3_glitch(Framebuffer *fb) {
float strength;
if(global.boss && global.boss->current && ATTACK_IS_SPELL(global.boss->current->type) && !strcmp(global.boss->name, "Scuttle")) {
strength = 0.05 * fmax(0, (global.frames - global.boss->current->starttime) / (double)global.boss->current->timeout);
} else {
strength = 0.0;
}
if(strength > 0) {
r_shader("glitch");
r_uniform_float("strength", strength);
r_uniform_float("frames", global.frames + 15 * nfrand());
} else {
return false;
}
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
return true;
}
static void stage3_start(void) {
stage3d_init(&stage_3d_context, 16);
stage_3d_context.cx[1] = -16;
stage_3d_context.crot[0] = 80;
stage_3d_context.cv[1] = 0.1;
stage_3d_context.cv[2] = 0.05;
}
static void stage3_preload(void) {
portrait_preload_base_sprite("wriggle", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("wriggle", "proud", RESF_DEFAULT);
portrait_preload_base_sprite("scuttle", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("scuttle", "normal", RESF_DEFAULT);
preload_resources(RES_BGM, RESF_OPTIONAL, "stage3", "stage3boss", NULL);
preload_resources(RES_SPRITE, RESF_DEFAULT,
"stage3/ground_ambient",
"stage3/ground_normal",
"stage3/ground_roughness",
"stage3/ground_diffuse",
"stage3/trees_ambient",
"stage3/trees_normal",
"stage3/trees_roughness",
"stage3/trees_diffuse",
"stage3/rocks_ambient",
"stage3/rocks_normal",
"stage3/rocks_roughness",
"stage3/rocks_diffuse",
"stage3/leaves_ambient",
"stage3/leaves_normal",
"stage3/leaves_roughness",
"stage3/leaves_diffuse",
"stage3/spellbg1",
"stage3/spellbg2",
"stage3/wspellbg",
"stage3/wspellclouds",
"stage3/wspellswarm",
NULL);
preload_resources(RES_MODEL, RESF_DEFAULT,
"stage3/ground",
"stage3/rocks",
"stage3/trees",
"stage3/leaves",
NULL);
preload_resources(RES_SHADER_PROGRAM, RESF_DEFAULT,
"zbuf_fog",
"glitch",
"maristar_bombbg",
NULL);
preload_resources(RES_SHADER_PROGRAM, RESF_OPTIONAL,
"lasers/accelerated",
"lasers/sine_expanding",
NULL);
preload_resources(RES_ANIM, RESF_DEFAULT,
"boss/scuttle",
"boss/wriggleex",
NULL);
preload_resources(RES_SFX, RESF_OPTIONAL,
"laser1",
NULL);
}
static void stage3_end(void) {
stage3d_shutdown(&stage_3d_context);
}
static void stage3_draw(void) {
stage3d_draw(&stage_3d_context, 120, 2, (Stage3DSegment[]) { stage3_bg_ground_draw, stage3_bg_pos, stage3_bg_leaves_draw, stage3_bg_pos });
}
static void stage3_update(void) {
if(dialog_is_active(global.dialog)) {
stage3d_update(&stage_3d_context);
return;
}
// 2740 - MIDBOSS
stage3d_update(&stage_3d_context);
}
void scuttle_spellbg(Boss*, int t);
static void stage3_spellpractice_start(void) {
stage3_start();
if(global.stage->spell->draw_rule == scuttle_spellbg) {
skip_background_anim(stage3_update, 2800, &global.timer, NULL);
global.boss = stage3_spawn_scuttle(BOSS_DEFAULT_SPAWN_POS);
stage_unlock_bgm("scuttle");
stage_start_bgm("scuttle");
} else {
skip_background_anim(stage3_update, 5300 + STAGE3_MIDBOSS_TIME, &global.timer, NULL);
global.boss = stage3_spawn_wriggle_ex(BOSS_DEFAULT_SPAWN_POS);
stage_start_bgm("stage3boss");
}
boss_add_attack_from_info(global.boss, global.stage->spell, true);
boss_engage(global.boss);
}
static void stage3_spellpractice_events(void) {
}
void stage3_skip(int t) {
skip_background_anim(stage3_update, t, &global.timer, &global.frames);
audio_bgm_seek_realtime(global.timer / (double)FPS);
}
ShaderRule stage3_shaders[] = { stage3_fog, NULL };
ShaderRule stage3_postprocess[] = { stage3_glitch, NULL };
StageProcs stage3_procs = {
.begin = stage3_start,
.preload = stage3_preload,
.end = stage3_end,
.draw = stage3_draw,
.update = stage3_update,
.event = stage3_events,
.shader_rules = stage3_shaders,
.postprocess_rules = stage3_postprocess,
.spellpractice_procs = &stage3_spell_procs,
};
StageProcs stage3_spell_procs = {
.begin = stage3_spellpractice_start,
.preload = stage3_preload,
.end = stage3_end,
.draw = stage3_draw,
.update = stage3_update,
.event = stage3_spellpractice_events,
.shader_rules = stage3_shaders,
.postprocess_rules = stage3_postprocess,
};

View file

@ -0,0 +1,32 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "background_anim.h"
#include "draw.h"
#include "global.h"
#include "stageutils.h"
// TODO
TASK(animate_bg, NO_ARGS) {
for(;;) {
YIELD;
stage3d_update(&stage_3d_context);
}
}
void stage3_bg_init_fullstage(void) {
INVOKE_TASK(animate_bg);
}
void stage3_bg_init_spellpractice(void) {
INVOKE_TASK(animate_bg);
}

View file

@ -0,0 +1,17 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_stages_stage3_background_anim_h
#define IGUARD_stages_stage3_background_anim_h
#include "taisei.h"
void stage3_bg_init_fullstage(void);
void stage3_bg_init_spellpractice(void);
#endif // IGUARD_stages_stage3_background_anim_h

192
src/stages/stage3/draw.c Normal file
View file

@ -0,0 +1,192 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "draw.h"
#include "stagedraw.h"
#include "stageutils.h"
#include "global.h"
#include "util/glm.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
static uint stage3_bg_pos(Stage3D *s3d, vec3 pos, float maxrange) {
vec3 p = {
0,
0,
0,
};
// minus sign for drawing order
vec3 r = {0, -20, -10};
return linear3dpos(s3d, pos, maxrange, p, r);
}
static void stage3_bg_setup_pbr_lighting(void) {
Camera3D *cam = &stage_3d_context.cam;
PointLight3D lights[] = {
// TODO animate colors
{ { 0, 0, 10000 }, { 10, 42, 30 } },
{ { 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);
}
vec3 r;
camera3d_unprojected_ray(cam, global.plr.pos,r);
glm_vec3_scale(r, 5, r);
glm_vec3_add(cam->pos, r, lights[1].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);
real f = 1/(1+global.frames/1000.);
r_uniform_vec3("ambient_color",f,f,sqrt(f));
}
static void stage3_bg_ground_draw(vec3 pos) {
r_state_push();
r_mat_mv_push();
r_mat_mv_translate(pos[0], pos[1], pos[2]);
r_shader("pbr");
//r_uniform_vec3_array("light_positions[0]", 0, 1, &stage_3d_context.cx);
stage3_bg_setup_pbr_lighting();
r_uniform_float("metallic", 0);
r_uniform_sampler("tex", "stage3/ground_diffuse");
r_uniform_sampler("roughness_map", "stage3/ground_roughness");
r_uniform_sampler("normal_map", "stage3/ground_normal");
r_uniform_sampler("ambient_map", "stage3/ground_ambient");
r_draw_model("stage3/ground");
r_uniform_sampler("tex", "stage3/trees_diffuse");
r_uniform_sampler("roughness_map", "stage3/trees_roughness");
r_uniform_sampler("normal_map", "stage3/trees_normal");
r_uniform_sampler("ambient_map", "stage3/trees_ambient");
r_draw_model("stage3/trees");
r_uniform_sampler("tex", "stage3/rocks_diffuse");
r_uniform_sampler("roughness_map", "stage3/rocks_roughness");
r_uniform_sampler("normal_map", "stage3/rocks_normal");
r_uniform_sampler("ambient_map", "stage3/rocks_ambient");
r_draw_model("stage3/rocks");
r_mat_mv_pop();
r_state_pop();
}
static void stage3_bg_leaves_draw(vec3 pos) {
r_state_push();
r_mat_mv_push();
r_mat_mv_translate(pos[0], pos[1], pos[2]);
r_mat_mv_translate(0,0,-0.0002);
r_shader("pbr");
stage3_bg_setup_pbr_lighting();
r_uniform_float("metallic", 0);
r_uniform_sampler("tex", "stage3/leaves_diffuse");
r_uniform_sampler("roughness_map", "stage3/leaves_roughness");
r_uniform_sampler("normal_map", "stage3/leaves_normal");
r_uniform_sampler("ambient_map", "stage3/leaves_ambient");
r_draw_model("stage3/leaves");
r_mat_mv_pop();
r_state_pop();
}
void stage3_drawsys_init(void) {
stage3d_init(&stage_3d_context, 16);
stage_3d_context.cam.pos[1] = -16;
stage_3d_context.cam.rot.v[0] = 80;
stage_3d_context.cam.vel[1] = 0.1;
stage_3d_context.cam.vel[2] = 0.05;
}
void stage3_drawsys_shutdown(void) {
stage3d_shutdown(&stage_3d_context);
}
static bool stage3_fog(Framebuffer *fb) {
r_shader("zbuf_fog");
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_float("end", 2);
r_uniform_float("exponent", 10);
r_uniform_float("sphereness", 0);
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
return true;
}
static bool stage3_glitch(Framebuffer *fb) {
float strength;
if(global.boss && global.boss->current && ATTACK_IS_SPELL(global.boss->current->type) && !strcmp(global.boss->name, "Scuttle")) {
strength = 0.05 * fmax(0, (global.frames - global.boss->current->starttime) / (double)global.boss->current->timeout);
} else {
strength = 0.0;
}
if(strength > 0) {
r_shader("glitch");
r_uniform_float("strength", strength);
r_uniform_float("frames", global.frames + 15 * nfrand());
} else {
return false;
}
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
return true;
}
void stage3_draw(void) {
Stage3DSegment segments[] = {
{ stage3_bg_ground_draw, stage3_bg_pos },
{ stage3_bg_leaves_draw, stage3_bg_pos },
};
stage3d_draw(&stage_3d_context, 120, ARRAY_SIZE(segments), segments);
}
ShaderRule stage3_bg_effects[] = {
stage3_fog,
NULL
};
ShaderRule stage3_postprocess[] = {
stage3_glitch,
NULL
};

24
src/stages/stage3/draw.h Normal file
View file

@ -0,0 +1,24 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_stages_stage3_draw_h
#define IGUARD_stages_stage3_draw_h
#include "taisei.h"
#include "util/fbpair.h"
#include "stagedraw.h"
void stage3_drawsys_init(void);
void stage3_drawsys_shutdown(void);
void stage3_draw(void);
extern ShaderRule stage3_bg_effects[];
extern ShaderRule stage3_postprocess[];
#endif // IGUARD_stages_stage3_draw_h

View file

@ -0,0 +1,15 @@
stage3_src = files(
'background_anim.c',
'wriggle.c',
'scuttle.c',
'draw.c',
'nonspells/boss_nonspells.c',
'nonspells/midboss_nonspell_1.c',
'spells/deadly_dance.c',
'spells/firefly_storm.c',
'spells/light_singularity.c',
'spells/moonlight_rocket.c',
'spells/night_ignite.c',
'stage3.c',
'timeline.c',
)

View file

@ -0,0 +1,115 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "nonspells.h"
#include "../wriggle.h"
#include "global.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
static int wriggle_nonspell_slave(Enemy *e, int time) {
TIMER(&time)
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]);
if(!boss)
return ACTION_DESTROY;
AT(EVENT_DEATH) {
free_ref(e->args[0]);
return 1;
}
if(time < 0)
return 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");
PROJECTILE(
.proto = pp_rice,
.pos = e->pos,
.color = RGB(0.7, 0.2, 0.1),
.rule = linear,
.args = { 3 * cexp(I*carg(boss->pos - e->pos)) },
);
if(!(time % (d*2)) || level > 1) {
PROJECTILE(
.proto = pp_thickrice,
.pos = e->pos,
.color = RGB(0.7, 0.7, 0.1),
.rule = linear,
.args = { 2.5 * cexp(I*carg(boss->pos - e->pos)) },
);
}
if(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)) },
);
}
}
return 1;
}
static void wriggle_nonspell_common(Boss *boss, int time, int level) {
TIMER(&time)
int i, j, cnt = 3 + global.diff;
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;
}
if(time < 0) {
GO_TO(boss, VIEWPORT_W/2 + VIEWPORT_H*I/3, 0.05)
return;
}
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)
}
void stage3_boss_nonspell1(Boss *boss, int time) {
wriggle_nonspell_common(boss, time, 1);
}
void stage3_boss_nonspell2(Boss *boss, int time) {
wriggle_nonspell_common(boss, time, 2);
}
void stage3_boss_nonspell3(Boss *boss, int time) {
wriggle_nonspell_common(boss, time, 3);
}

View file

@ -0,0 +1,99 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "nonspells.h"
#include "../scuttle.h"
#include "global.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
static int scuttle_lethbite_proj(Projectile *p, int time) {
if(time < 0) {
return ACTION_ACK;
}
#define A0_PROJ_START 120
#define A0_PROJ_CHARGE 20
TIMER(&time)
FROM_TO(A0_PROJ_START, A0_PROJ_START + A0_PROJ_CHARGE, 1)
return 1;
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));
int cnt = 3, i;
for(i = 0; i < cnt; ++i) {
tsrand_fill(2);
PARTICLE(
.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))
},
);
}
play_sound("redirect");
play_sound("shot1");
spawn_projectile_highlight_effect(p);
}
return asymptotic(p, time);
#undef A0_PROJ_START
#undef A0_PROJ_CHARGE
}
void stage3_midboss_nonspell1(Boss *boss, int time) {
int i;
TIMER(&time)
GO_TO(boss, VIEWPORT_W/2+VIEWPORT_W/3*sin(time/300) + I*cimag(boss->pos), 0.01)
FROM_TO_INT(0, 90000, 72 + 6 * (D_Lunatic - global.diff), 0, 1) {
int cnt = 21 - 1 * (D_Lunatic - global.diff);
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 },
);
}
// FIXME: better sound
play_sound("shot_special1");
}
}

View file

@ -0,0 +1,23 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_stages_stage3_nonspells_nonspells_h
#define IGUARD_stages_stage3_nonspells_nonspells_h
#include "taisei.h"
#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);
#endif // IGUARD_stages_stage3_nonspells_nonspells_h

View file

@ -0,0 +1,43 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "scuttle.h"
#include "common_tasks.h"
void stage3_draw_scuttle_spellbg(Boss *h, int time) {
float a = 1.0;
if(time < 0)
a += (time / (float)ATTACK_START_DELAY);
float s = 0.3 + 0.7 * a;
r_color4(0.1*a, 0.1*a, 0.1*a, a);
draw_sprite(VIEWPORT_W/2, VIEWPORT_H/2, "stage3/spellbg2");
fill_viewport(-time/200.0 + 0.5, time/400.0+0.5, s, "stage3/spellbg1");
r_color4(0.1, 0.1, 0.1, 0);
fill_viewport(time/300.0 + 0.5, -time/340.0+0.5, s*0.5, "stage3/spellbg1");
r_shader("maristar_bombbg");
r_uniform_float("t", time/400.);
r_uniform_float("decay", 0.);
r_uniform_vec2("plrpos", 0.5,0.5);
fill_viewport(0.0, 0.0, 1, "stage3/spellbg1");
r_shader_standard();
r_color4(1, 1, 1, 1);
}
Boss *stage3_spawn_scuttle(cmplx pos) {
Boss *scuttle = create_boss("Scuttle", "scuttle", 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);
return scuttle;
}

View file

@ -0,0 +1,19 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_stages_stage3_scuttle_h
#define IGUARD_stages_stage3_scuttle_h
#include "taisei.h"
#include "boss.h"
Boss *stage3_spawn_scuttle(cmplx pos);
void stage3_draw_scuttle_spellbg(Boss *boss, int time);
#endif // IGUARD_stages_stage3_scuttle_h

View file

@ -0,0 +1,121 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "spells.h"
#include "../scuttle.h"
#include "common_tasks.h"
#include "global.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
static int scuttle_poison(Projectile *p, int time) {
int result = accelerated(p, time);
if(time < 0)
return result;
if(!(time % (57 - global.diff * 3)) && p->type != PROJ_DEAD) {
float a = p->args[2];
float t = p->args[3] + time;
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;
}
void scuttle_deadly_dance(Boss *boss, int time) {
int i;
TIMER(&time)
if(time < 0) {
return;
}
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,
.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))))
}
);
}
}
}

View file

@ -0,0 +1,123 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "spells.h"
#include "../wriggle.h"
#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();
r_mat_mv_translate(creal(p->pos), cimag(p->pos), 0);
r_mat_mv_rotate(p->angle + M_PI/2, 0, 0, 1);
ProjDrawCore(p, &p->color);
if(f > 0) {
// TODO: Maybe convert this into a particle effect?
Sprite *s = p->sprite;
Color c = p->color;
c.a = 0;
p->sprite = get_sprite("proj/ball");
r_mat_mv_scale(f,f,f);
ProjDrawCore(p, &c);
p->sprite = s;
}
r_mat_mv_pop();
}
static int wriggle_fstorm_proj(Projectile *p, int time) {
if(time < 0) {
return ACTION_ACK;
}
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 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,
);
}
play_sound_ex("redirect", 3, false);
}
p->pos += p->args[1];
return ACTION_NONE;
}
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;
}
bool lun = global.diff == D_Lunatic;
AT(0) {
aniplayer_queue(&boss->ani,"fly",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;
PROJECTILE(
.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)
},
);
}
}
}

View file

@ -0,0 +1,124 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "spells.h"
#include "../wriggle.h"
#include "common_tasks.h"
#include "global.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
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;
}
if(time == 140) {
play_sound("laser1");
}
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);
}
play_sound("charge_generic");
aniplayer_queue(&boss->ani, "main", 0);
}
if(time > 120) {
play_sfx_loop("shot1_loop");
}
if(time == 0) {
return;
}
if(!((time+30) % 300)) {
aniplayer_queue(&boss->ani, "specialshot_charge", 1);
aniplayer_queue(&boss->ani, "specialshot_release", 1);
aniplayer_queue(&boss->ani, "main", 0);
}
if(!(time % 300)) {
ProjPrototype *ptype = NULL;
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;
}
int cnt = 6 + 2 * global.diff;
float colorofs = frand();
for(int i = 0; i < cnt; ++i) {
double a = ((M_PI*2.0*i)/cnt);
cmplx dir = cexp(I*a);
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
},
);
}
play_sound("shot_special1");
}
}

View file

@ -0,0 +1,34 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "spells.h"
#include "../wriggle.h"
#include "common_tasks.h"
#include "global.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
void wriggle_moonlight_rocket(Boss *boss, int time) {
int i, j, cnt = 1 + global.diff;
TIMER(&time)
AT(EVENT_DEATH) {
enemy_kill_all(&global.enemies);
return;
}
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);
}
}

View file

@ -0,0 +1,120 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "spells.h"
#include "../wriggle.h"
#include "refs.h"
#include "common_tasks.h"
#include "global.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
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;
}
Laser *laser = (Laser*)REF(p->args[0]);
if(laser) {
p->args[3] = laser->prule(laser, time - p->args[1]) - p->pos;
}
p->angle = carg(p->args[3]);
p->pos = p->pos + p->args[3];
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");
}
// FIXME: better sound
play_sound_ex("shot_special1", 1, false);
}
}

View file

@ -4,28 +4,19 @@
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
*/
#ifndef IGUARD_stages_stage3_events_h
#define IGUARD_stages_stage3_events_h
#ifndef IGUARD_stages_stage3_spells_spells_h
#define IGUARD_stages_stage3_spells_spells_h
#include "taisei.h"
#include "boss.h"
void scuttle_spellbg(Boss*, int t);
void wriggle_spellbg(Boss*, int t);
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);
void stage3_events(void);
Boss* stage3_spawn_scuttle(cmplx pos);
Boss* stage3_spawn_wriggle_ex(cmplx pos);
#define STAGE3_MIDBOSS_TIME 1765
#endif // IGUARD_stages_stage3_events_h
#endif // IGUARD_stages_stage3_spells_spells_h

156
src/stages/stage3/stage3.c Normal file
View file

@ -0,0 +1,156 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "stage3.h"
#include "draw.h"
#include "background_anim.h"
#include "wriggle.h"
#include "scuttle.h"
#include "spells/spells.h"
#include "timeline.h"
#include "wriggle.h"
#include "scuttle.h"
#include "global.h"
#include "portrait.h"
#include "common_tasks.h"
/*
* See the definition of AttackInfo in boss.h for information on how to set up the idmaps.
* To add, remove, or reorder spells, see this stage's header file.
*/
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
},
},
.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
},
.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
},
.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
},
},
.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
},
};
static void stage3_start(void) {
stage3_drawsys_init();
stage3_bg_init_fullstage();
stage_start_bgm("stage3");
stage_set_voltage_thresholds(50, 125, 300, 600);
}
static void stage3_spellpractice_start(void) {
stage3_drawsys_init();
stage3_bg_init_spellpractice();
if(global.stage->spell->draw_rule == stage3_draw_scuttle_spellbg) {
global.boss = stage3_spawn_scuttle(BOSS_DEFAULT_SPAWN_POS);
stage_unlock_bgm("scuttle");
stage_start_bgm("scuttle");
} else {
global.boss = stage3_spawn_wriggle(BOSS_DEFAULT_SPAWN_POS);
stage_start_bgm("stage3boss");
}
boss_add_attack_from_info(global.boss, global.stage->spell, true);
boss_engage(global.boss);
}
static void stage3_preload(void) {
portrait_preload_base_sprite("wriggle", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("wriggle", "proud", RESF_DEFAULT);
portrait_preload_base_sprite("scuttle", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("scuttle", "normal", RESF_DEFAULT);
preload_resources(RES_BGM, RESF_OPTIONAL, "stage3", "stage3boss", NULL);
preload_resources(RES_SPRITE, RESF_DEFAULT,
"stage3/ground_ambient",
"stage3/ground_normal",
"stage3/ground_roughness",
"stage3/ground_diffuse",
"stage3/trees_ambient",
"stage3/trees_normal",
"stage3/trees_roughness",
"stage3/trees_diffuse",
"stage3/rocks_ambient",
"stage3/rocks_normal",
"stage3/rocks_roughness",
"stage3/rocks_diffuse",
"stage3/leaves_ambient",
"stage3/leaves_normal",
"stage3/leaves_roughness",
"stage3/leaves_diffuse",
"stage3/spellbg1",
"stage3/spellbg2",
"stage3/wspellbg",
"stage3/wspellclouds",
"stage3/wspellswarm",
NULL);
preload_resources(RES_MODEL, RESF_DEFAULT,
"stage3/ground",
"stage3/rocks",
"stage3/trees",
"stage3/leaves",
NULL);
preload_resources(RES_SHADER_PROGRAM, RESF_DEFAULT,
"zbuf_fog",
"glitch",
"maristar_bombbg",
NULL);
preload_resources(RES_SHADER_PROGRAM, RESF_OPTIONAL,
"lasers/accelerated",
"lasers/sine_expanding",
NULL);
preload_resources(RES_ANIM, RESF_DEFAULT,
"boss/scuttle",
"boss/wriggleex",
NULL);
preload_resources(RES_SFX, RESF_OPTIONAL,
"laser1",
NULL);
}
static void stage3_end(void) {
stage3_drawsys_shutdown();
}
StageProcs stage3_procs = {
.begin = stage3_start,
.draw = stage3_draw,
.end = stage3_end,
.preload = stage3_preload,
.event = stage3_events,
.shader_rules = stage3_bg_effects,
.postprocess_rules = stage3_postprocess,
.spellpractice_procs = &(StageProcs) {
.begin = stage3_spellpractice_start,
.draw = stage3_draw,
.end = stage3_end,
.preload = stage3_preload,
.shader_rules = stage3_bg_effects,
.postprocess_rules = stage3_postprocess,
},
};

View file

@ -6,8 +6,8 @@
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_stages_stage3_h
#define IGUARD_stages_stage3_h
#ifndef IGUARD_stages_stage3_stage3_h
#define IGUARD_stages_stage3_stage3_h
#include "taisei.h"
@ -36,8 +36,5 @@ extern struct stage3_spells_s {
} stage3_spells;
extern StageProcs stage3_procs;
extern StageProcs stage3_spell_procs;
void stage3_skip(int t);
#endif // IGUARD_stages_stage3_h
#endif // IGUARD_stages_stage3_stage3_h

View file

@ -0,0 +1,779 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "timeline.h"
#include "stage3.h"
#include "wriggle.h"
#include "scuttle.h"
#include "spells/spells.h"
#include "nonspells/nonspells.h"
#include "background_anim.h"
#include "global.h"
#include "stagetext.h"
#include "common_tasks.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
TASK(boss_appear_stub, NO_ARGS) {
log_warn("FIXME");
}
static void stage3_dialog_pre_boss(void) {
PlayerMode *pm = global.plr.mode;
Stage3PreBossDialogEvents *e;
INVOKE_TASK_INDIRECT(Stage3PreBossDialog, pm->dialog->Stage3PreBoss, &e);
INVOKE_TASK_WHEN(&e->boss_appears, boss_appear_stub);
INVOKE_TASK_WHEN(&e->music_changes, common_start_bgm, "stage3boss");
}
static void stage3_dialog_post_boss(void) {
PlayerMode *pm = global.plr.mode;
INVOKE_TASK_INDIRECT(Stage3PostBossDialog, pm->dialog->Stage3PostBoss);
}
static int stage3_enterswirl(Enemy *e, int t) {
TIMER(&t)
AT(EVENT_KILLED) {
spawn_items(e->pos, ITEM_POINTS, 1, ITEM_POWER, 1);
float r, g;
if(frand() > 0.5) {
r = 0.3;
g = 1.0;
} else {
r = 1.0;
g = 0.3;
}
int cnt = 24 - (D_Lunatic - global.diff) * 4;
for(int i = 0; i < cnt; ++i) {
double a = (M_PI * 2.0 * i) / cnt;
cmplx dir = cexp(I*a);
PROJECTILE(
.proto = e->args[1]? pp_ball : pp_rice,
.pos = e->pos,
.color = RGB(r, g, 1.0),
.rule = asymptotic,
.args = {
1.5 * dir,
10 - 10 * psin(2 * a + M_PI/2)
}
);
}
return 1;
}
AT(EVENT_BIRTH) {
e->alpha = 0;
}
AT(60) {
enemy_kill(e);
}
e->pos += e->args[0];
return 0;
}
static int stage3_slavefairy(Enemy *e, int t) {
TIMER(&t)
AT(EVENT_KILLED) {
spawn_items(e->pos, ITEM_POINTS, 1, ITEM_POWER, 3);
return 1;
}
AT(EVENT_BIRTH) {
e->alpha = 0;
}
if(t < 120)
GO_TO(e, e->args[0], 0.03)
FROM_TO_SND("shot1_loop", 30, 120, 5 - global.diff) {
float a = _i * 0.5;
cmplx dir = cexp(I*a);
PROJECTILE(
.proto = pp_wave,
.pos = e->pos + dir * 10,
.color = (_i % 2)? RGB(1.0, 0.3, 0.3) : RGB(0.3, 0.3, 1.0),
.rule = accelerated,
.args = {
dir * 2,
dir * -0.035,
},
);
if(global.diff > D_Easy && e->args[1]) {
PROJECTILE(
.proto = pp_ball,
.pos = e->pos + dir * 10,
.color = RGB(1.0, 0.6, 0.3),
.rule = linear,
.args = { dir * (1.0 + 0.5 * sin(a)) }
);
}
}
if(t >= 120) {
e->pos += 3 * e->args[2] + 2.0*I;
}
return 0;
}
static int stage3_slavefairy2(Enemy *e, int t) {
TIMER(&t)
AT(EVENT_KILLED) {
spawn_items(e->pos, ITEM_POINTS, 1, ITEM_POWER, 3);
return 1;
}
AT(EVENT_BIRTH) {
e->alpha = 0;
}
int lifetime = 160;
if(t < lifetime)
GO_TO(e, e->args[0], 0.03)
FROM_TO_SND("shot1_loop", 30, lifetime, 1) {
double a = _i/sqrt(global.diff);
if(t > 90) {
a *= -1;
}
cmplx dir = cexp(I*a);
PROJECTILE(
.proto = pp_wave,
.pos = e->pos,
.color = (_i&1) ? RGB(1.0,0.3,0.3) : RGB(0.3,0.3,1.0),
.rule = linear,
.args = { 2*dir },
);
if(global.diff > D_Normal && _i % 3 == 0) {
PROJECTILE(
.proto = pp_rice,
.pos = e->pos,
.color = !(_i&1) ? RGB(1.0,0.3,0.3) : RGB(0.3,0.3,1.0),
.rule = linear,
.args = { -2*dir }
);
}
}
if(t >= lifetime) {
e->pos += 3 * e->args[2] + 2.0*I;
}
return 1;
}
static void charge_effect(Enemy *e, int t, int chargetime) {
TIMER(&t);
FROM_TO(chargetime - 30, chargetime, 1) {
cmplx n = cexp(2.0*I*M_PI*frand());
float l = 50*frand()+25;
float s = 4+_i*0.01;
PARTICLE(
.sprite = "flare",
.pos = e->pos+l*n,
.color = RGBA(0.5, 0.5, 0.25, 0),
.draw_rule = Fade,
.rule = linear,
.timeout = l/s,
.args = { -s*n },
);
}
}
static int stage3_burstfairy(Enemy *e, int t) {
TIMER(&t)
AT(EVENT_KILLED) {
spawn_items(e->pos, ITEM_POINTS, 2, ITEM_POWER, 5);
return 1;
}
AT(EVENT_BIRTH) {
e->alpha = 0;
}
int bursttime = creal(e->args[3]);
int prebursttime = cimag(e->args[3]);
int burstspan = 30;
if(t < bursttime) {
GO_TO(e, e->args[0], 0.08)
}
if(prebursttime > 0) {
AT(prebursttime) {
play_sound("shot_special1");
int cnt = 6 + 4 * global.diff;
for(int p = 0; p < cnt; ++p) {
cmplx dir = cexp(I*M_PI*2*p/cnt);
PROJECTILE(
.proto = pp_ball,
.pos = e->args[0],
.color = RGB(0.2, 0.1, 0.5),
.rule = asymptotic,
.args = {
dir,
10 + 4 * global.diff
},
);
}
}
}
AT(bursttime) {
play_sound("shot_special1");
}
int step = 3 - (global.diff > D_Normal);
charge_effect(e, t, bursttime);
FROM_TO_SND("shot1_loop", bursttime, bursttime + burstspan, step) {
double phase = (t - bursttime) / (double)burstspan;
cmplx dir = cexp(I*M_PI*phase);
int cnt = 5 + global.diff;
for(int p = 0; p < cnt; ++p) {
for(int i = -1; i < 2; i += 2) {
PROJECTILE(
.proto = pp_bullet,
.pos = e->pos + dir * 10,
.color = color_lerp(RGB(0.0, 0.0, 1.0), RGB(1.0, 0.0, 0.0), psin(M_PI * phase)),
.rule = asymptotic,
.args = {
1.5 * dir * (1 + p / (cnt - 1.0)) * i,
3 * global.diff
},
);
}
}
}
if(t >= bursttime + burstspan) {
e->pos += 3 * e->args[2] + 2.0*I;
} else if(t > bursttime) {
// GO_TO(e, VIEWPORT_W+VIEWPORT_H*I - e->args[0], 0.05)
}
return 0;
}
static int stage3_chargefairy_proj(Projectile *p, int t) {
if(t < 0) {
return ACTION_ACK;
}
/*
if(t == 0) {
p->type = FakeProj;
} else if(t == 20) {
p->type = EnemyProj;
}
*/
p->angle = carg(p->args[0]);
t -= creal(p->args[2]);
if(t == 0) {
play_sound("redirect");
play_sound("shot_special1");
spawn_projectile_highlight_effect(p);
} else if(t > 0) {
p->args[1] *= 0.8;
p->pos += p->args[0] * (p->args[1] + 1);
}
return ACTION_NONE;
}
static int stage3_chargefairy(Enemy *e, int t) {
TIMER(&t)
AT(EVENT_KILLED) {
spawn_items(e->pos, ITEM_POINTS, 5, ITEM_POWER, 3);
return 1;
}
AT(EVENT_BIRTH) {
e->alpha = 0;
}
AT(30) {
e->args[3] = global.plr.pos;
}
int chargetime = e->args[1];
int cnt = 19 - 4 * (D_Lunatic - global.diff);
int step = 1;
charge_effect(e, t, chargetime);
FROM_TO_SND("shot1_loop", chargetime, chargetime + cnt * step - 1, step) {
cmplx aim = e->args[3] - e->pos;
aim /= cabs(aim);
cmplx aim_norm = -cimag(aim) + I*creal(aim);
int layers = 1 + global.diff;
int i = _i;
for(int layer = 0; layer < layers; ++layer) {
if(layer&1) {
i = cnt - 1 - i;
}
double f = i / (cnt - 1.0);
int w = 100 - 20 * layer;
cmplx o = e->pos + w * psin(M_PI*f) * aim + aim_norm * w*0.8 * (f - 0.5);
cmplx paim = e->pos + (w+1) * aim - o;
paim /= cabs(paim);
PROJECTILE(
.proto = pp_wave,
.pos = o,
.color = color_lerp(RGB(0.0, 0.0, 1.0), RGB(1.0, 0.0, 0.0), f),
.rule = stage3_chargefairy_proj,
.args = {
paim, 6 + global.diff - layer,
chargetime + 30 - t
},
);
}
}
if(t > chargetime + step * cnt * 2) {
/*
complex dir = e->pos - (VIEWPORT_W+VIEWPORT_H*I)/2;
dir /= cabs(dir);
e->pos += dir;
*/
e->pos += e->args[2];
} else {
GO_TO(e, e->args[0], 0.03);
}
return 0;
}
static int stage3_bigfairy(Enemy *e, int t) {
TIMER(&t)
AT(EVENT_KILLED) {
spawn_items(e->pos, ITEM_POINTS, 5, ITEM_POWER, 5);
if(creal(e->args[0]) && global.timer > 2800)
spawn_items(e->pos, ITEM_BOMB, 1);
return 1;
}
AT(EVENT_BIRTH) {
e->alpha = 0;
}
EnemyLogicRule slave = cimag(e->args[0]) ? stage3_slavefairy2 : stage3_slavefairy;
FROM_TO(30, 600, 270) {
create_enemy3c(e->pos, 900, Fairy, slave, e->pos + 70 + 50 * I, e->args[0], +1);
create_enemy3c(e->pos, 900, Fairy, slave, e->pos - 70 + 50 * I, e->args[0], -1);
}
FROM_TO(120, 600, 270) {
create_enemy3c(e->pos, 900, Fairy, slave, e->pos + 70 - 50 * I, e->args[0], +1);
create_enemy3c(e->pos, 900, Fairy, slave, e->pos - 70 - 50 * I, e->args[0], -1);
}
AT(creal(e->args[1])) {
enemy_kill(e);
}
return 0;
}
static int stage3_bitchswirl(Enemy *e, int t) {
TIMER(&t)
AT(EVENT_BIRTH) {
return 1;
}
AT(EVENT_KILLED) {
spawn_items(e->pos, ITEM_POINTS, 1, ITEM_POWER, 1);
return -1;
}
FROM_TO(0, 120, 20) {
PROJECTILE(
.proto = pp_flea,
.pos = e->pos,
.color = RGB(0.7, 0.0, 0.5),
.rule = accelerated,
.args = {
2*cexp(I*carg(global.plr.pos - e->pos)),
0.005*cexp(I*(M_PI*2 * frand())) * (global.diff > D_Easy)
},
);
play_sound("shot1");
}
e->pos += e->args[0] + e->args[1] * creal(e->args[2]);
e->args[2] = creal(e->args[2]) * cimag(e->args[2]) + I * cimag(e->args[2]);
return 0;
}
static int stage3_cornerfairy(Enemy *e, int t) {
TIMER(&t)
AT(EVENT_KILLED) {
spawn_items(e->pos, ITEM_POINTS, 5, ITEM_POWER, 5);
return -1;
}
AT(EVENT_BIRTH) {
e->alpha = 0;
}
FROM_TO(0, 120, 1)
GO_TO(e, e->args[0], 0.01)
FROM_TO_SND("shot1_loop", 140, 240, 1) {
GO_TO(e, e->args[1], 0.025 * fmin((t - 120) / 42.0, 1))
int d = 5; //(D_Lunatic - global.diff + 3);
if(!(t % d)) {
int i, cnt = 7+global.diff;
for(i = 0; i < cnt; ++i) {
float c = psin(t / 15.0);
bool wave = global.diff > D_Easy && cabs(e->args[2]);
PROJECTILE(
.proto = wave ? pp_wave : pp_thickrice,
.pos = e->pos,
.color = cabs(e->args[2])
? RGB(0.5 - c*0.2, 0.3 + c*0.7, 1.0)
: RGB(1.0 - c*0.5, 0.6, 0.5 + c*0.5),
.rule = asymptotic,
.args = {
(1.8-0.4*wave*!!(e->args[2]))*cexp(I*((2*i*M_PI/cnt)+carg((VIEWPORT_W+I*VIEWPORT_H)/2 - e->pos))),
1.5
},
);
}
}
}
AT(260) {
return ACTION_DESTROY;
}
return 0;
}
static void scuttle_intro(Boss *boss, int time) {
GO_TO(boss, VIEWPORT_W/2.0 + 100.0*I, 0.04);
}
static void scuttle_outro(Boss *boss, int time) {
if(time == 0) {
spawn_items(boss->pos, ITEM_POINTS, 10, ITEM_POWER, 10, ITEM_LIFE, 1);
}
boss->pos += pow(fmax(0, time)/30.0, 2) * cexp(I*(3*M_PI/2 + 0.5 * sin(time / 20.0)));
}
static Boss *stage3_create_midboss(void) {
Boss *scuttle = stage3_spawn_scuttle(VIEWPORT_W/2 - 200.0*I);
boss_add_attack(scuttle, AT_Move, "Introduction", 1, 0, scuttle_intro, NULL);
boss_add_attack(scuttle, AT_Normal, "Lethal Bite", 11, 20000, stage3_midboss_nonspell1, NULL);
boss_add_attack_from_info(scuttle, &stage3_spells.mid.deadly_dance, false);
boss_add_attack(scuttle, AT_Move, "Runaway", 2, 1, scuttle_outro, NULL);
scuttle->zoomcolor = *RGB(0.4, 0.1, 0.4);
boss_engage(scuttle);
return scuttle;
}
static void stage3_boss_intro(Boss *boss, int time) {
if(time == 110)
stage3_dialog_pre_boss();
GO_TO(boss, VIEWPORT_W/2.0 + 100.0*I, 0.03);
}
static Boss* stage3_create_boss(void) {
Boss *wriggle = stage3_spawn_wriggle(VIEWPORT_W/2 - 200.0*I);
boss_add_attack(wriggle, AT_Move, "Introduction", 2, 0, stage3_boss_intro, NULL);
boss_add_attack(wriggle, AT_Normal, "", 11, 35000, stage3_boss_nonspell1, NULL);
boss_add_attack_from_info(wriggle, &stage3_spells.boss.moonlight_rocket, false);
boss_add_attack(wriggle, AT_Normal, "", 40, 35000, stage3_boss_nonspell2, NULL);
boss_add_attack_from_info(wriggle, &stage3_spells.boss.wriggle_night_ignite, false);
boss_add_attack(wriggle, AT_Normal, "", 40, 35000, stage3_boss_nonspell3, NULL);
boss_add_attack_from_info(wriggle, &stage3_spells.boss.firefly_storm, false);
boss_add_attack_from_info(wriggle, &stage3_spells.extra.light_singularity, false);
boss_engage(wriggle);
return wriggle;
}
void stage3_events(void) {
TIMER(&global.timer);
AT(0) {
stage_start_bgm("stage3");
stage_set_voltage_thresholds(115, 250, 510, 860);
}
FROM_TO(160, 300, 10) {
tsrand_fill(2);
create_enemy1c(VIEWPORT_W/2 + 20 * anfrand(0) + (VIEWPORT_H/4 + 20 * anfrand(1))*I, 200, Swirl, stage3_enterswirl, 3 * (I + sin(M_PI*global.frames/15.0)));
}
AT(360) {
create_enemy2c(VIEWPORT_W/2 + (VIEWPORT_H/3)*I, 10000, BigFairy, stage3_bigfairy, 0+1*I, 600 - 30 * (D_Lunatic - global.diff));
}
FROM_TO(600, 800-30*(D_Lunatic-global.diff), 20) {
create_enemy3c(-20 + 20*I, 50, Swirl, stage3_bitchswirl, 5, 1*I, 5+0.95*I);
}
FROM_TO(800, 1000-30*(D_Lunatic-global.diff), 20) {
cmplx f = 5 + (0.93 + 0.01 * _i) * I;
create_enemy3c(-20 + 20*I, 50, Swirl, stage3_bitchswirl, 5, 1*I, f);
create_enemy3c(VIEWPORT_W+20 + 20*I, 50, Swirl, stage3_bitchswirl, -5, 1*I, f);
}
{
int cnt = 4;
int step = 20;
int start = 1030;
FROM_TO(start, start + step * cnt - 1, step) {
int i = _i % cnt;
double span = 300 - 60 * (i/2);
cmplx pos1 = VIEWPORT_W/2;
cmplx pos2 = VIEWPORT_W/2 + span * (-0.5 + (i&1)) + (VIEWPORT_H/3 + 100*(i/2))*I;
create_enemy4c(pos1, 700, Fairy, stage3_burstfairy, pos2, i&1, 0, start + step * 6 - global.timer);
}
}
{
int cnt = 6;
int step = 60;
int start = 1200;
FROM_TO(start, start + cnt * step - 1, step) {
int i = _i % 4;
double span = 300 - 60 * (i/2);
cmplx pos = VIEWPORT_W/2 + span * (-0.5 + (i&1)) + (VIEWPORT_H/3 + 100*(i/2))*I;
cmplx exitdir = pos - (VIEWPORT_W+VIEWPORT_H*I)/2;
exitdir /= cabs(exitdir);
create_enemy3c(pos, 1000, Fairy, stage3_chargefairy, pos, 30, exitdir);
}
}
FROM_TO(1530, 1575, 10) {
create_enemy3c(20 - 20*I, 50, Swirl, stage3_bitchswirl, 5*I, 0, 0);
}
FROM_TO(1575, 1620, 10) {
create_enemy3c(VIEWPORT_W-20 - 20*I, 50, Swirl, stage3_bitchswirl, 5*I, 0, 0);
}
AT(1600) {
create_enemy2c(VIEWPORT_W/2 + (VIEWPORT_H/3)*I, 10000, BigFairy, stage3_bigfairy, 1, 600 - 30 * (D_Lunatic - global.diff));
}
FROM_TO(1800, 2200, 10) {
if(global.enemies.first == NULL) {
int cnt = 2;
for(int i = 0; i <= cnt;i++) {
cmplx pos1 = VIEWPORT_W/2+VIEWPORT_W/3*nfrand() + VIEWPORT_H/5*I;
cmplx pos2 = VIEWPORT_W/2+50*(i-cnt/2)+VIEWPORT_H/3*I;
create_enemy3c(pos1, 700, Fairy, stage3_slavefairy2, pos2, i&1,0.5*(i-cnt/2));
}
}
}
{
int cnt = 3;
int step = 60;
int start = 2000;
FROM_TO(start, start + cnt * step - 1, step) {
create_enemy3c(-20 + (VIEWPORT_H+20)*I, 1000, Fairy, stage3_chargefairy, 30 + VIEWPORT_H/2.0*I, 60, -1*I);
create_enemy3c(VIEWPORT_W+20 + (VIEWPORT_H+20)*I, 1000, Fairy, stage3_chargefairy, VIEWPORT_W-30 + VIEWPORT_H/2.0*I, 60, -1*I);
}
}
AT(2400) {
double offs = -50;
cmplx p1 = 0+0*I;
cmplx p2 = VIEWPORT_W+0*I;
cmplx p3 = VIEWPORT_W+VIEWPORT_H*I;
cmplx p4 = 0+VIEWPORT_H*I;
create_enemy2c(p1, 500, Fairy, stage3_cornerfairy, p1 - offs - offs*I, p2 + offs - offs*I);
create_enemy2c(p2, 500, Fairy, stage3_cornerfairy, p2 + offs - offs*I, p3 + offs + offs*I);
create_enemy2c(p3, 500, Fairy, stage3_cornerfairy, p3 + offs + offs*I, p4 - offs + offs*I);
create_enemy2c(p4, 500, Fairy, stage3_cornerfairy, p4 - offs + offs*I, p1 - offs - offs*I);
}
if(global.diff > D_Normal) AT(2490) {
double offs = -50;
cmplx p1 = VIEWPORT_W/2+0*I;
cmplx p2 = VIEWPORT_W+VIEWPORT_H/2*I;
cmplx p3 = VIEWPORT_W/2+VIEWPORT_H*I;
cmplx p4 = 0+VIEWPORT_H/2*I;
create_enemy2c(p1, 500, Fairy, stage3_cornerfairy, p1 - offs*I, p2 + offs);
create_enemy2c(p2, 500, Fairy, stage3_cornerfairy, p2 + offs, p3 + offs*I);
create_enemy2c(p3, 500, Fairy, stage3_cornerfairy, p3 + offs*I, p4 - offs);
create_enemy2c(p4, 500, Fairy, stage3_cornerfairy, p4 - offs, p1 + offs*I);
}
int midboss_time = STAGE3_MIDBOSS_TIME;
AT(2740) {
global.boss = stage3_create_midboss();
}
AT(2741) {
if(global.boss) {
global.timer += global.frames - global.boss->birthtime - 1;
}
}
FROM_TO(2741, 2740 + midboss_time - 60, 10) {
if(_i&1) {
create_enemy3c(-20 + (VIEWPORT_H-20)*I, 50, Swirl, stage3_bitchswirl, 5, -1*I, 25+0.95*I);
} else {
create_enemy3c(VIEWPORT_W+20 + (VIEWPORT_H-20)*I, 50, Swirl, stage3_bitchswirl, -5, -1*I, 25+0.95*I);
}
}
FROM_TO(2740 + midboss_time, 3000 + midboss_time, 10+2*(D_Lunatic-global.diff)) {
tsrand_fill(3);
create_enemy2c(VIEWPORT_W/2 + 20 * anfrand(0) + (VIEWPORT_H/4 + 20 * anfrand(1))*I, 200, Swirl, stage3_enterswirl, I * 3 + anfrand(2) * 3, 1);
}
AT(3000 + midboss_time) {
create_enemy2c(VIEWPORT_W - VIEWPORT_W/3 + (VIEWPORT_H/5)*I, 8000, BigFairy, stage3_bigfairy, 2, 480 - 30 * (D_Lunatic - global.diff));
}
if(global.diff > D_Easy) {
FROM_TO(3000 + midboss_time, 3100 + midboss_time, 20+4*(D_Lunatic-global.diff)) {
create_enemy3c(VIEWPORT_W-20 + (VIEWPORT_H+20)*I, 50, Swirl, stage3_bitchswirl, -5*I, 0, 0);
}
}
AT(3500 + midboss_time) {
create_enemy2c(VIEWPORT_W/3 + (VIEWPORT_H/5)*I, 8000, BigFairy, stage3_bigfairy, 2, 480 - 30 * (D_Lunatic - global.diff));
}
{
int cnt = 4;
int step = 60;
FROM_TO(3500 + midboss_time, 3500 + cnt * step - 1 + midboss_time, step) {
cmplx pos = VIEWPORT_W - VIEWPORT_W/3 + (VIEWPORT_H/5)*I;
cmplx spos = creal(pos) - 20 * I;
create_enemy3c(spos, 1000, Fairy, stage3_chargefairy, pos, 30, 2*I);
}
}
{
int cnt = 4;
int step = 70;
int start = 4000 + midboss_time;
FROM_TO(start, start + step * cnt - 1, step) {
int i = _i % cnt;
double span = 300 - 60 * (1-i/2);
cmplx pos1 = VIEWPORT_W/2;
cmplx pos2 = VIEWPORT_W/2 + span * (-0.5 + (i&1)) + (VIEWPORT_H/3 + 100*(i/2))*I;
create_enemy4c(pos1, 3000, BigFairy, stage3_burstfairy, pos2, i&1, 0,
(start + 300 - global.timer) + I *
(30)
);
}
}
FROM_TO(3700 + midboss_time, 3800 + midboss_time, 20+4*(D_Lunatic-global.diff)) {
create_enemy3c(20 + (VIEWPORT_H+20)*I, 50, Swirl, stage3_bitchswirl, -5*I, 0, 0);
}
AT(4330 + midboss_time) {
double offs = -50;
cmplx p1 = 0+0*I;
cmplx p2 = VIEWPORT_W+0*I;
cmplx p3 = VIEWPORT_W+VIEWPORT_H*I;
cmplx p4 = 0+VIEWPORT_H*I;
create_enemy3c(p1, 500, Fairy, stage3_cornerfairy, p1 - offs - offs*I, p2 + offs - offs*I, 1);
create_enemy3c(p2, 500, Fairy, stage3_cornerfairy, p2 + offs - offs*I, p3 + offs + offs*I, 1);
create_enemy3c(p3, 500, Fairy, stage3_cornerfairy, p3 + offs + offs*I, p4 - offs + offs*I, 1);
create_enemy3c(p4, 500, Fairy, stage3_cornerfairy, p4 - offs + offs*I, p1 - offs - offs*I, 1);
}
if(global.diff > D_Normal) AT(4480 + midboss_time) {
double offs = -50;
cmplx p1 = VIEWPORT_W/2+0*I;
cmplx p2 = VIEWPORT_W+VIEWPORT_H/2*I;
cmplx p3 = VIEWPORT_W/2+VIEWPORT_H*I;
cmplx p4 = 0+VIEWPORT_H/2*I;
create_enemy3c(p1, 500, Fairy, stage3_cornerfairy, p1 - offs*I, p2 + offs, 0);
create_enemy3c(p2, 500, Fairy, stage3_cornerfairy, p2 + offs, p3 + offs*I, 0);
create_enemy3c(p3, 500, Fairy, stage3_cornerfairy, p3 + offs*I, p4 - offs, 0);
create_enemy3c(p4, 500, Fairy, stage3_cornerfairy, p4 - offs, p1 + offs*I, 0);
}
FROM_TO(4760 + midboss_time, 4940 + midboss_time, 10+2*(D_Lunatic-global.diff)) {
create_enemy3c(VIEWPORT_W-20 - 20.0*I, 50, Swirl, stage3_bitchswirl, 5*I, 0, 0);
create_enemy3c(20 + -20.0*I, 50, Swirl, stage3_bitchswirl, 5*I, 0, 0);
}
AT(5300 + midboss_time) {
stage_unlock_bgm("stage3");
global.boss = stage3_create_boss();
}
AT(5400 + midboss_time) {
stage_unlock_bgm("stage3boss");
stage3_dialog_post_boss();
}
AT(5405 + midboss_time) {
stage_finish(GAMEOVER_SCORESCREEN);
}
}

View file

@ -0,0 +1,18 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_stages_stage3_timeline_h
#define IGUARD_stages_stage3_timeline_h
#include "taisei.h"
void stage3_events(void);
#define STAGE3_MIDBOSS_TIME 1765
#endif // IGUARD_stages_stage3_timeline_h

272
src/stages/stage3/wriggle.c Normal file
View file

@ -0,0 +1,272 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "wriggle.h"
#include "common_tasks.h"
MODERNIZE_THIS_FILE_AND_REMOVE_ME
void stage3_draw_wriggle_spellbg(Boss *b, int time) {
r_color4(1,1,1,1);
fill_viewport(0, 0, 768.0/1024.0, "stage3/wspellbg");
r_color4(1,1,1,0.5);
r_blend(r_blend_compose(
BLENDFACTOR_SRC_ALPHA, BLENDFACTOR_ONE, BLENDOP_SUB,
BLENDFACTOR_ZERO, BLENDFACTOR_ONE, BLENDOP_ADD
));
fill_viewport(sin(time) * 0.015, time / 50.0, 1, "stage3/wspellclouds");
r_blend(BLEND_PREMUL_ALPHA);
r_color4(0.5, 0.5, 0.5, 0.0);
fill_viewport(0, time / 70.0, 1, "stage3/wspellswarm");
r_blend(r_blend_compose(
BLENDFACTOR_SRC_ALPHA, BLENDFACTOR_ONE, BLENDOP_SUB,
BLENDFACTOR_ZERO, BLENDFACTOR_ONE, BLENDOP_ADD
));
r_color4(1,1,1,0.4);
fill_viewport(cos(time) * 0.02, time / 30.0, 1, "stage3/wspellclouds");
r_blend(BLEND_PREMUL_ALPHA);
r_color4(1, 1, 1, 1);
}
Boss *stage3_spawn_wriggle(cmplx pos) {
Boss *wriggle = create_boss("Wriggle EX", "wriggleex", pos);
boss_set_portrait(wriggle, "wriggle", NULL, "proud");
wriggle->glowcolor = *RGBA_MUL_ALPHA(0.2, 0.4, 0.5, 0.5);
wriggle->shadowcolor = *RGBA_MUL_ALPHA(0.4, 0.2, 0.6, 0.5);
return wriggle;
}
void wriggle_slave_visual(Enemy *e, int time, bool render) {
if(time < 0)
return;
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)),
.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),
},
);
}
}
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();
}
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;
}
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));
Laser *l = create_lasercurve2c(p->pos, deathtime, deathtime, RGBA(0.4, 0.9, 1.0, 0.0), las_accel, 0, accel);
l->width = 15;
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;
}
int wriggle_spell_slave(Enemy *e, int time) {
TIMER(&time)
float angle = e->args[2] * (time / 70.0 + e->args[1]);
cmplx dir = cexp(I*angle);
Boss *boss = (Boss*)REF(e->args[0]);
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);
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"),
.size = 16 + 16*I,
.collision_size = 7.2 + 7.2*I,
.pos = e->pos,
.color = RGBA(1.0 - c, 0.5, 0.5 + c, 0),
.draw_rule = wriggle_slave_part_draw,
.timeout = 60,
.shader = "sprite_default",
.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;
}

View file

@ -0,0 +1,22 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_stages_stage3_wriggle_h
#define IGUARD_stages_stage3_wriggle_h
#include "taisei.h"
#include "boss.h"
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);
#endif // IGUARD_stages_stage3_wriggle_h

File diff suppressed because it is too large Load diff