Split stage1 into multiple files (#228)

This commit is contained in:
Andrei Alexeyev 2020-05-18 22:44:11 +03:00 committed by GitHub
parent bd7bb3a351
commit db9cb15d21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 2387 additions and 2003 deletions

View file

@ -10,7 +10,7 @@
#include "stageinfo.h"
#include "stages/stage1.h"
#include "stages/stage1/stage1.h"
#include "stages/stage2.h"
#include "stages/stage3.h"
#include "stages/stage4.h"

View file

@ -1,7 +1,19 @@
stages_src = files(
'stage1.c',
'stage1_events.c',
stages_src = []
stages = [
'stage1'
]
foreach stage : stages
subdir(stage)
stages_src += get_variable('@0@_src'.format(stage))
endforeach
# TODO: reorganize all the remaining stages to use a separate directory and put
# them into the "stages" array
stages_src += files(
'stage2.c',
'stage2_events.c',
'stage3.c',
@ -18,7 +30,6 @@ stages_src = files(
if is_developer_build
stages_src += files(
'dpstest.c',
'corotest.c',
)
endif
stages_src += files('corotest.c')

View file

@ -0,0 +1,77 @@
/*
* 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 "stageutils.h"
void stage1_bg_raise_camera(void) {
Stage1DrawData *draw_data = stage1_get_draw_data();
draw_data->pitch_target = 75;
}
void stage1_bg_enable_snow(void) {
Stage1DrawData *draw_data = stage1_get_draw_data();
draw_data->snow.opacity_target = 1;
}
void stage1_bg_disable_snow(void) {
Stage1DrawData *draw_data = stage1_get_draw_data();
draw_data->snow.opacity_target = 0;
}
TASK(animate_bg, { Stage1DrawData *draw_data; }) {
Stage1DrawData *draw_data = ARGS.draw_data;
YIELD;
for(int t = 0;; t += WAIT(1)) {
stage3d_update(&stage_3d_context);
stage_3d_context.crot[1] = 2.0f * sinf(t/113.0f);
stage_3d_context.crot[2] = 1.0f * sinf(t/132.0f);
fapproach_asymptotic_p(&stage_3d_context.crot[0], draw_data->pitch_target, 0.01, 1e-5);
fapproach_asymptotic_p(&draw_data->fog.near, draw_data->fog.near_target, 0.001, 1e-5);
fapproach_asymptotic_p(&draw_data->fog.far, draw_data->fog.far_target, 0.001, 1e-5);
fapproach_p(&draw_data->snow.opacity, draw_data->snow.opacity_target, 1.0 / 180.0);
}
}
void stage1_bg_init_fullstage(void) {
Stage1DrawData *draw_data = stage1_get_draw_data();
draw_data->fog.near_target = 0.5;
draw_data->fog.far_target = 1.5;
draw_data->snow.opacity_target = 0.0;
draw_data->pitch_target = 60;
draw_data->fog.near = 0.2;
draw_data->fog.far = 1.0;
draw_data->snow.opacity = draw_data->snow.opacity_target;
stage_3d_context.crot[0] = draw_data->pitch_target;
stage_3d_context.cx[2] = 700;
stage_3d_context.cv[1] = 8;
INVOKE_TASK(animate_bg, draw_data);
}
void stage1_bg_init_spellpractice(void) {
stage1_bg_init_fullstage();
stage1_bg_raise_camera();
stage1_bg_enable_snow();
Stage1DrawData *draw_data = stage1_get_draw_data();
draw_data->fog.near = draw_data->fog.near_target;
draw_data->fog.far = draw_data->fog.far_target;
draw_data->snow.opacity = draw_data->snow.opacity_target;
stage_3d_context.crot[0] = draw_data->pitch_target;
}

View file

@ -0,0 +1,21 @@
/*
* 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_stage1_background_anim_h
#define IGUARD_stages_stage1_background_anim_h
#include "taisei.h"
void stage1_bg_raise_camera(void);
void stage1_bg_enable_snow(void);
void stage1_bg_disable_snow(void);
void stage1_bg_init_fullstage(void);
void stage1_bg_init_spellpractice(void);
#endif // IGUARD_stages_stage1_background_anim_h

39
src/stages/stage1/cirno.c Normal file
View file

@ -0,0 +1,39 @@
/*
* 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 "cirno.h"
#include "common_tasks.h"
void stage1_draw_cirno_spellbg(Boss *c, int time) {
r_color4(0.5, 0.5, 0.5, 1.0);
fill_viewport(time/700.0, time/700.0, 1, "stage1/cirnobg");
r_blend(BLEND_MOD);
r_color4(0.7, 0.7, 0.7, 0.5);
fill_viewport(-time/700.0 + 0.5, time/700.0+0.5, 0.4, "stage1/cirnobg");
r_blend(BLEND_PREMUL_ALPHA);
r_color4(0.35, 0.35, 0.35, 0.0);
fill_viewport(0, -time/100.0, 0, "stage1/snowlayer");
r_color4(1.0, 1.0, 1.0, 1.0);
}
Boss *stage1_spawn_cirno(cmplx pos) {
Boss *cirno = create_boss("Cirno", "cirno", pos);
boss_set_portrait(cirno, "cirno", NULL, "normal");
cirno->shadowcolor = *RGBA_MUL_ALPHA(0.6, 0.7, 1.0, 0.25);
cirno->glowcolor = *RGB(0.2, 0.35, 0.5);
return cirno;
}
void stage1_cirno_wander(Boss *boss, real dist, real lower_bound) {
Rect bounds = viewport_bounds(64);
bounds.bottom = lower_bound;
boss->move.attraction_point = common_wander(boss->pos, dist, bounds);
}

20
src/stages/stage1/cirno.h Normal file
View file

@ -0,0 +1,20 @@
/*
* 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_stage1_cirno_h
#define IGUARD_stages_stage1_cirno_h
#include "taisei.h"
#include "boss.h"
Boss *stage1_spawn_cirno(cmplx pos);
void stage1_draw_cirno_spellbg(Boss *boss, int time);
void stage1_cirno_wander(Boss *boss, real dist, real lower_bound);
#endif // IGUARD_stages_stage1_cirno_h

View file

@ -4,84 +4,50 @@
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
*/
#include "taisei.h"
#include "stage1.h"
#include "stage1_events.h"
#include "draw.h"
#include "global.h"
#include "stage.h"
#include "stageutils.h"
#include "stagedraw.h"
#include "resource/model.h"
#include "stageutils.h"
#include "global.h"
#include "util/glm.h"
#include "common_tasks.h"
#include "portrait.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.
*/
static Stage1DrawData *stage1_draw_data;
struct stage1_spells_s stage1_spells = {
.mid = {
.perfect_freeze = {
{ 0, 1, 2, 3}, AT_Spellcard, "Freeze Sign “Perfect Freeze”", 50, 24000,
NULL, cirno_pfreeze_bg, VIEWPORT_W/2.0+100.0*I, 1, TASK_INDIRECT_INIT(BossAttack, stage1_spell_perfect_freeze)
},
},
Stage1DrawData *stage1_get_draw_data(void) {
return NOT_NULL(stage1_draw_data);
}
.boss = {
.crystal_rain = {
{ 4, 5, 6, 7}, AT_Spellcard, "Freeze Sign “Crystal Rain”", 40, 33000,
NULL, cirno_pfreeze_bg, VIEWPORT_W/2.0+100.0*I, 1, TASK_INDIRECT_INIT(BossAttack, stage1_spell_crystal_rain)
},
.snow_halation = {
{-1, -1, 12, 13}, AT_Spellcard, "Winter Sign “Snow Halation”", 50, 40000,
NULL, cirno_pfreeze_bg, VIEWPORT_W/2.0+100.0*I, 1, TASK_INDIRECT_INIT(BossAttack, stage1_spell_snow_halation)
},
.icicle_cascade = {
{ 8, 9, 10, 11}, AT_Spellcard, "Doom Sign “Icicle Cascade”", 40, 40000,
NULL, cirno_pfreeze_bg, VIEWPORT_W/2.0+100.0*I, 1,
TASK_INDIRECT_INIT(BossAttack, stage1_spell_icicle_cascade)
},
},
void stage1_drawsys_init(void) {
stage1_draw_data = calloc(1, sizeof(*stage1_draw_data));
stage3d_init(&stage_3d_context, 64);
.extra.crystal_blizzard = {
{ 0, 1, 2, 3}, AT_ExtraSpell, "Frost Sign “Crystal Blizzard”", 60, 40000,
NULL, cirno_pfreeze_bg, VIEWPORT_W/2.0+100.0*I, 1, TASK_INDIRECT_INIT(BossAttack, stage1_spell_crystal_blizzard)
},
};
FBAttachmentConfig cfg = { 0 };
cfg.attachment = FRAMEBUFFER_ATTACH_COLOR0;
cfg.tex_params.filter.min = TEX_FILTER_LINEAR;
cfg.tex_params.filter.mag = TEX_FILTER_LINEAR;
cfg.tex_params.type = TEX_TYPE_RGBA;
cfg.tex_params.wrap.s = TEX_WRAP_CLAMP;
cfg.tex_params.wrap.t = TEX_WRAP_CLAMP;
static struct {
FBPair water_fbpair;
stage1_draw_data->water_fbpair.front = stage_add_background_framebuffer("Stage 1 water FB 1", 0.2, 0.5, 1, &cfg);
stage1_draw_data->water_fbpair.back = stage_add_background_framebuffer("Stage 1 water FB 2", 0.2, 0.5, 1, &cfg);
}
struct {
float near, near_target;
float far, far_target;
} fog;
void stage1_drawsys_shutdown(void) {
stage3d_shutdown(&stage_3d_context);
free(stage1_draw_data);
stage1_draw_data = NULL;
}
struct {
float opacity, opacity_target;
} snow;
float pitch_target;
} stage1_bg;
#ifdef SPELL_BENCHMARK
AttackInfo stage1_spell_benchmark = {
{-1, -1, -1, -1, 127}, AT_SurvivalSpell, "Profiling “ベンチマーク”", 40, 40000,
cirno_benchmark, cirno_pfreeze_bg, VIEWPORT_W/2.0+100.0*I
};
#endif
static bool particle_filter(Projectile *part) {
static bool reflect_particle_filter(Projectile *part) {
return !(part->flags & PFLAG_NOREFLECT) && stage_should_draw_particle(part);
}
static bool stage1_draw_predicate(EntityInterface *ent) {
static bool reflect_draw_predicate(EntityInterface *ent) {
switch(ent->draw_layer & ~LAYER_LOW_MASK) {
case LAYER_PLAYER_SLAVE:
case LAYER_PLAYER_FOCUS:
@ -100,7 +66,7 @@ static bool stage1_draw_predicate(EntityInterface *ent) {
Projectile *p = ENT_CAST(ent, Projectile);
if(p->type == PROJ_PARTICLE) {
return particle_filter(p);
return reflect_particle_filter(p);
}
return false;
@ -113,6 +79,8 @@ static bool stage1_draw_predicate(EntityInterface *ent) {
}
static void stage1_water_draw(vec3 pos) {
Stage1DrawData *draw_data = stage1_get_draw_data();
// don't even ask
r_state_push();
@ -151,7 +119,7 @@ static void stage1_water_draw(vec3 pos) {
r_disable(RCAP_CULL_FACE);
Framebuffer *bg_fb = r_framebuffer_current();
FBPair *fbpair = &stage1_bg.water_fbpair;
FBPair *fbpair = &draw_data->water_fbpair;
r_framebuffer(fbpair->back);
r_mat_proj_push();
set_ortho(VIEWPORT_W, VIEWPORT_H);
@ -167,7 +135,7 @@ static void stage1_water_draw(vec3 pos) {
r_clear(CLEAR_ALL, RGBA(0, 0, 0, 0), 1);
r_shader("sprite_default");
ent_draw(stage1_draw_predicate);
ent_draw(reflect_draw_predicate);
r_mat_mv_pop();
@ -228,13 +196,13 @@ static void stage1_water_draw(vec3 pos) {
r_state_pop();
}
static uint stage1_bg_pos(Stage3D *s3d, vec3 p, float maxrange) {
vec3 q = {0,0,0};
static uint stage1_water_pos(Stage3D *s3d, vec3 p, float maxrange) {
vec3 q = {0, 0, 0};
return single3dpos(s3d, p, INFINITY, q);
}
static void stage1_smoke_draw(vec3 pos) {
float d = fabsf(pos[1]-stage_3d_context.cx[1]);
float d = fabsf(pos[1] - stage_3d_context.cx[1]);
float o = ((d-500)*(d-500))/1.5e7;
o *= 5 * pow((5000 - d) / 5000, 3);
@ -269,15 +237,18 @@ static uint stage1_smoke_pos(Stage3D *s3d, vec3 p, float maxrange) {
}
static bool stage1_fog(Framebuffer *fb) {
Stage1DrawData *draw_data = stage1_get_draw_data();
r_shader("zbuf_fog");
r_uniform_sampler("depth", r_framebuffer_get_attachment(fb, FRAMEBUFFER_ATTACH_DEPTH));
r_uniform_vec4("fog_color", 0.78, 0.8, 0.85, 1.0);
r_uniform_float("start", stage1_bg.fog.near);
r_uniform_float("end", stage1_bg.fog.far);
r_uniform_float("start", draw_data->fog.near);
r_uniform_float("end", draw_data->fog.far);
r_uniform_float("exponent", 1.0);
r_uniform_float("sphereness", 0.2);
draw_framebuffer_tex(fb, VIEWPORT_W, VIEWPORT_H);
r_shader_standard();
return true;
}
@ -332,8 +303,8 @@ static uint stage1_waterplants_pos(Stage3D *s3d, vec3 p, float maxrange) {
}
static void stage1_snow_draw(vec3 pos) {
float o = stage1_bg.snow.opacity;
// float appear_time = 2760;
Stage1DrawData *draw_data = stage1_get_draw_data();
float o = draw_data->snow.opacity;
if(o < 1.0f/256.0f) {
return;
@ -354,7 +325,6 @@ static void stage1_snow_draw(vec3 pos) {
float height = 1 + sawtooth(h0 + global.frames/240.0);
o *= pow(1 - 1.5 * clamp(height - 1, 0, 1), 5) * x;
// o *= min(1, (global.frames - appear_time) / 180.0);
if(o < 1.0f/256.0f) {
return;
@ -383,7 +353,6 @@ static void stage1_snow_draw(vec3 pos) {
});
r_mat_mv_pop();
r_state_pop();
}
static uint stage1_snow_pos(Stage3D *s3d, vec3 p, float maxrange) {
@ -392,23 +361,11 @@ static uint stage1_snow_pos(Stage3D *s3d, vec3 p, float maxrange) {
return linear3dpos(s3d, p, maxrange, q, r);
}
void stage1_bg_raise_camera(void) {
stage1_bg.pitch_target = 75;
}
void stage1_bg_enable_snow(void) {
stage1_bg.snow.opacity_target = 1;
}
void stage1_bg_disable_snow(void) {
stage1_bg.snow.opacity_target = 0;
}
static void stage1_draw(void) {
void stage1_draw(void) {
r_mat_proj_perspective(STAGE3D_DEFAULT_FOVY, STAGE3D_DEFAULT_ASPECT, 500, 10000);
Stage3DSegment segs[] = {
{ stage1_water_draw, stage1_bg_pos },
{ stage1_water_draw, stage1_water_pos },
{ stage1_waterplants_draw, stage1_waterplants_pos },
{ stage1_snow_draw, stage1_snow_pos },
{ stage1_smoke_draw, stage1_smoke_pos },
@ -417,126 +374,7 @@ static void stage1_draw(void) {
stage3d_draw(&stage_3d_context, 10000, ARRAY_SIZE(segs), segs);
}
static void stage1_update(void) {
stage3d_update(&stage_3d_context);
stage_3d_context.crot[1] = 2 * sin(global.frames/113.0);
stage_3d_context.crot[2] = 1 * sin(global.frames/132.0);
fapproach_asymptotic_p(&stage_3d_context.crot[0], stage1_bg.pitch_target, 0.01, 1e-5);
fapproach_asymptotic_p(&stage1_bg.fog.near, stage1_bg.fog.near_target, 0.001, 1e-5);
fapproach_asymptotic_p(&stage1_bg.fog.far, stage1_bg.fog.far_target, 0.001, 1e-5);
fapproach_p(&stage1_bg.snow.opacity, stage1_bg.snow.opacity_target, 1.0 / 180.0);
}
static void stage1_init_background(void) {
stage3d_init(&stage_3d_context, 64);
FBAttachmentConfig cfg = { 0 };
cfg.attachment = FRAMEBUFFER_ATTACH_COLOR0;
cfg.tex_params.filter.min = TEX_FILTER_LINEAR;
cfg.tex_params.filter.mag = TEX_FILTER_LINEAR;
cfg.tex_params.type = TEX_TYPE_RGBA;
cfg.tex_params.wrap.s = TEX_WRAP_CLAMP;
cfg.tex_params.wrap.t = TEX_WRAP_CLAMP;
stage1_bg.water_fbpair.front = stage_add_background_framebuffer("Stage 1 water FB 1", 0.2, 0.5, 1, &cfg);
stage1_bg.water_fbpair.back = stage_add_background_framebuffer("Stage 1 water FB 2", 0.2, 0.5, 1, &cfg);
stage1_bg.fog.near_target = 0.5;
stage1_bg.fog.far_target = 1.5;
stage1_bg.snow.opacity_target = 0.0;
stage1_bg.pitch_target = 60;
stage1_bg.fog.near = 0.2; // stage1_bg.fog.near_target;
stage1_bg.fog.far = 1.0; // stage1_bg.fog.far_target;
stage1_bg.snow.opacity = stage1_bg.snow.opacity_target;
stage_3d_context.crot[0] = stage1_bg.pitch_target;
stage_3d_context.cx[2] = 700;
stage_3d_context.cv[1] = 8;
}
static void stage1_start(void) {
stage1_init_background();
stage_start_bgm("stage1");
stage_set_voltage_thresholds(50, 125, 300, 600);
INVOKE_TASK(stage1_main);
}
static void stage1_preload(void) {
// DIALOG_PRELOAD(&global.plr, Stage1PreBoss, RESF_DEFAULT);
portrait_preload_base_sprite("cirno", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("cirno", "normal", RESF_DEFAULT);
preload_resources(RES_BGM, RESF_OPTIONAL, "stage1", "stage1boss", NULL);
preload_resources(RES_SPRITE, RESF_DEFAULT,
"stage1/cirnobg",
"stage1/fog",
"stage1/snowlayer",
"stage1/waterplants",
NULL);
preload_resources(RES_TEXTURE, RESF_DEFAULT,
"stage1/horizon",
NULL);
preload_resources(RES_SHADER_PROGRAM, RESF_DEFAULT,
"blur5",
"stage1_water",
"zbuf_fog",
NULL);
preload_resources(RES_SHADER_PROGRAM, RESF_OPTIONAL,
"lasers/linear",
NULL);
preload_resources(RES_ANIM, RESF_DEFAULT,
"boss/cirno",
NULL);
preload_resources(RES_SFX, RESF_OPTIONAL,
"laser1",
NULL);
}
static void stage1_end(void) {
stage3d_shutdown(&stage_3d_context);
memset(&stage1_bg, 0, sizeof(stage1_bg));
}
static void stage1_spellpractice_start(void) {
stage1_init_background();
Boss *cirno = stage1_spawn_cirno(BOSS_DEFAULT_SPAWN_POS);
boss_add_attack_from_info(cirno, global.stage->spell, true);
boss_start_attack(cirno, cirno->attacks);
global.boss = cirno;
stage_start_bgm("stage1boss");
stage1_bg_raise_camera();
stage1_bg_enable_snow();
stage1_bg.fog.near = stage1_bg.fog.near_target;
stage1_bg.fog.far = stage1_bg.fog.far_target;
stage1_bg.snow.opacity = stage1_bg.snow.opacity_target;
stage_3d_context.crot[0] = stage1_bg.pitch_target;
INVOKE_TASK_WHEN(&cirno->events.defeated, common_call_func, stage1_bg_disable_snow);
}
ShaderRule stage1_shaders[] = { stage1_fog, NULL };
StageProcs stage1_procs = {
.begin = stage1_start,
.preload = stage1_preload,
.end = stage1_end,
.draw = stage1_draw,
.update = stage1_update,
.shader_rules = stage1_shaders,
.spellpractice_procs = &stage1_spell_procs,
};
StageProcs stage1_spell_procs = {
.begin = stage1_spellpractice_start,
.preload = stage1_preload,
.end = stage1_end,
.draw = stage1_draw,
.update = stage1_update,
.shader_rules = stage1_shaders,
ShaderRule stage1_bg_effects[] = {
stage1_fog,
NULL
};

42
src/stages/stage1/draw.h Normal file
View file

@ -0,0 +1,42 @@
/*
* 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_stage1_draw_h
#define IGUARD_stages_stage1_draw_h
#include "taisei.h"
#include "util/fbpair.h"
#include "stagedraw.h"
typedef struct Stage1DrawData {
FBPair water_fbpair;
struct {
float near, near_target;
float far, far_target;
} fog;
struct {
float opacity, opacity_target;
} snow;
float pitch_target;
} Stage1DrawData;
void stage1_drawsys_init(void);
void stage1_drawsys_shutdown(void);
Stage1DrawData *stage1_get_draw_data(void)
attr_returns_nonnull attr_returns_max_aligned;
void stage1_draw(void);
extern ShaderRule stage1_bg_effects[];
#endif // IGUARD_stages_stage1_draw_h

View file

@ -0,0 +1,18 @@
stage1_src = files(
'background_anim.c',
'cirno.c',
'draw.c',
'misc.c',
'nonspells/boss_nonspell_1.c',
'nonspells/boss_nonspell_2.c',
'nonspells/midboss_nonspell_1.c',
'spells/benchmark.c', # TODO exclude this conditionally, and maybe move out of stage1
'spells/crystal_blizzard.c',
'spells/crystal_rain.c',
'spells/icicle_cascade.c',
'spells/perfect_freeze.c',
'spells/snow_halation.c',
'stage1.c',
'timeline.c',
)

22
src/stages/stage1/misc.c Normal file
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>.
*/
#include "taisei.h"
#include "misc.h"
Projectile *stage1_spawn_stain(cmplx pos, float angle, int to) {
return PARTICLE(
.sprite = "stain",
.pos = pos,
.draw_rule = pdraw_timeout_scalefade(0, 0.8, 1, 0),
.timeout = to,
.angle = angle,
.color = RGBA(0.4, 0.4, 0.4, 0),
);
}

18
src/stages/stage1/misc.h Normal file
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_stage1_misc_h
#define IGUARD_stages_stage1_misc_h
#include "taisei.h"
#include "stage.h"
Projectile *stage1_spawn_stain(cmplx pos, float angle, int to);
#endif // IGUARD_stages_stage1_misc_h

View file

@ -0,0 +1,94 @@
/*
* 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 "../cirno.h"
#include "../misc.h"
#include "global.h"
DEFINE_EXTERN_TASK(stage1_boss_nonspell_1) {
Boss *boss = INIT_BOSS_ATTACK();
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.05);
BEGIN_BOSS_ATTACK();
for(;;) {
WAIT(20);
aniplayer_queue(&boss->ani, "(9)", 3);
aniplayer_queue(&boss->ani, "main", 0);
play_sound("shot_special1");
const int num_shots = 5;
const int num_projs = difficulty_value(9, 10, 11, 12);
for(int shot = 0; shot < num_shots; ++shot) {
for(int i = 0; i < num_projs; ++i) {
cmplx shot_org = boss->pos;
cmplx aim = cdir(i*M_TAU/num_projs + carg(global.plr.pos - shot_org));
real speed = 3 + shot / 3.0;
real boost = shot * 0.7;
PROJECTILE(
.proto = pp_plainball,
.pos = shot_org,
.color = RGB(0, 0, 0.5),
.move = move_asymptotic_simple(speed * aim, boost),
);
}
WAIT(2);
}
WAIT(10);
for(int t = 0, i = 0; t < 60; ++i) {
play_loop("shot1_loop");
real speed0 = difficulty_value(4.0, 6.0, 6.0, 6.0);
real speed1 = difficulty_value(3.0, 5.0, 6.0, 8.0);
real angle = rng_sreal() * M_PI/8.0;
real sign = 1 - 2 * (i & 1);
cmplx shot_org = boss->pos - 42*I + 30 * sign;
PROJECTILE(
.proto = pp_crystal,
.pos = shot_org,
.color = RGB(0.3, 0.3, 0.8),
.move = move_asymptotic_halflife(speed0 * -I * cdir(angle), speed1 * I, 30.0),
.max_viewport_dist = 256,
);
t += WAIT(difficulty_value(3, 3, 1, 1));
}
boss->move.attraction = 0.02;
WAIT(20);
stage1_cirno_wander(boss, 60, 200);
WAIT(30);
for(int t = 0, i = 0; t < 150; ++i) {
play_sound("shot1");
float dif = rng_angle();
for(int shot = 0; shot < 20; ++shot) {
cmplx aim = cdir(M_TAU/8 * shot + dif);
real speed = 3.0 + i / 4.0;
PROJECTILE(
.proto = pp_plainball,
.pos = boss->pos,
.color = RGB(0.04 * i, 0.04 * i, 0.4 + 0.04 * i),
.move = move_asymptotic_simple(speed * aim, 2.5),
);
}
t += WAIT(difficulty_value(25, 20, 15, 10));
}
boss->move.attraction = 0.04;
stage1_cirno_wander(boss, 180, 200);
}
}

View file

@ -0,0 +1,172 @@
/*
* 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 "../cirno.h"
#include "../misc.h"
#include "common_tasks.h"
#include "global.h"
TASK(snowburst, { BoxedBoss boss; }) {
Boss *boss = TASK_BIND(ARGS.boss);
play_sound("shot_special1");
aniplayer_queue(&boss->ani, "(9)", 0);
int rounds = difficulty_value(3, 4, 5, 6);
int shots_per_round = difficulty_value(4, 5, 6, 7);
real spread = difficulty_value(0.13, 0.16, 0.19, 0.22);
real angle_ofs = carg(global.plr.pos - boss->pos);
for(int round = 0; round < rounds; ++round) {
real speed = (3 + round / 3.0);
real boost = round * 0.7;
for(int k = 0; k < 8; ++k) {
real base_angle = k * M_TAU / 8 + angle_ofs;
int cnt_mod = round & 1;
int effective_count = shots_per_round - cnt_mod;
real effective_spread = spread * ((real)effective_count / shots_per_round);
for(int i = 0; i < effective_count; ++i) {
real spread_factor = rng_real();
spread_factor = 2.0 * (i / (effective_count - 1.0) - 0.5);
cmplx dir = cdir(base_angle + effective_spread * spread_factor);
PROJECTILE(
.proto = pp_plainball,
.pos = boss->pos,
.color = RGB(0, 0, 0.5),
.move = move_asymptotic_simple(speed * dir, boost),
);
}
}
WAIT(2);
}
aniplayer_queue(&boss->ani, "main", 0);
}
TASK(spiralshot, {
cmplx pos;
int count;
real spread;
real winding;
int interval;
real angle_ofs;
real bullet_speed;
} ) {
int count = ARGS.count;
real winding = ARGS.winding;
real angle_ofs = ARGS.angle_ofs;
real dist_per_bullet = ARGS.spread;
real angle_per_bullet = winding / (count - 1.0);
int interval = ARGS.interval;
cmplx pos = ARGS.pos;
DECLARE_ENT_ARRAY(Projectile, projs, count);
int fire_delay = 20;
int charge_time = 60;
int charge_delay = count * interval - charge_time + fire_delay;
INVOKE_SUBTASK_DELAYED(charge_delay, common_charge,
.pos = pos,
.color = RGBA(0.3, 0.3, 0.8, 0),
.time = charge_time,
.sound = COMMON_CHARGE_SOUNDS
);
for(int b = 0; b < count; ++b) {
play_loop("shot1_loop");
real dist = b * dist_per_bullet;
real angle = angle_ofs + b * angle_per_bullet;
ENT_ARRAY_ADD(&projs, PROJECTILE(
.proto = pp_crystal,
.pos = pos + dist * cdir(angle),
.color = RGB(0.3, 0.3, 0.8),
.angle = angle + M_PI,
));
WAIT(interval);
}
WAIT(fire_delay);
play_sound("shot_special1");
play_sound("redirect");
ENT_ARRAY_FOREACH(&projs, Projectile *p, {
spawn_projectile_highlight_effect(p);
p->move = move_linear(cdir(p->angle) * ARGS.bullet_speed);
});
ENT_ARRAY_CLEAR(&projs);
}
DEFINE_EXTERN_TASK(stage1_boss_nonspell_2) {
Boss *boss = INIT_BOSS_ATTACK();
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.02);
BEGIN_BOSS_ATTACK();
for(;;) {
WAIT(20);
INVOKE_SUBTASK(snowburst, ENT_BOX(boss));
WAIT(20);
int spiral_bullets = difficulty_value(40, 80, 100, 100);
int turns = difficulty_value(8, 4, 4, 5);
int interval = difficulty_value(2, 1, 1, 1);
real bullet_speed = difficulty_value(2, 3, 3, 4.5);
INVOKE_SUBTASK(spiralshot,
.pos = boss->pos + 100,
.count = spiral_bullets,
.spread = 1,
.winding = turns * M_TAU,
.interval = interval,
.angle_ofs = M_PI,
.bullet_speed = bullet_speed
);
INVOKE_SUBTASK(spiralshot,
.pos = boss->pos - 100,
.count = spiral_bullets,
.spread = 1,
.winding = -turns * M_TAU,
.interval = interval,
.angle_ofs = 0,
.bullet_speed = bullet_speed
);
WAIT(140);
interval = difficulty_value(24, 18, 12, 6);
for(int t = 0, i = 0; t < 150;) {
float dif = rng_angle();
play_sound("shot1");
for(int j = 0; j < 20; ++j) {
PROJECTILE(
.proto = pp_plainball,
.pos = boss->pos,
.color = RGB(0.04*i, 0.04*i, 0.4+0.04*i),
.move = move_asymptotic_simple((3+i/3.0)*cdir(M_TAU/8.0*j + dif), 2.5),
);
}
t += WAIT(interval);
if(i < 15) {
++i;
}
}
}
}

View file

@ -0,0 +1,142 @@
/*
* 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 "../cirno.h"
#include "../misc.h"
#include "common_tasks.h"
#include "global.h"
#define SNOWFLAKE_ARMS 6
static int snowflake_bullet_limit(int size) {
// >= number of bullets spawned per snowflake of this size
return SNOWFLAKE_ARMS * 4 * size;
}
TASK(make_snowflake, {
cmplx pos;
MoveParams move;
int size;
real rot_angle;
BoxedProjectileArray *array;
}) {
const real spacing = 12;
const int split = 3;
int t = 0;
for(int j = 0; j < ARGS.size; j++) {
play_loop("shot1_loop");
for(int i = 0; i < SNOWFLAKE_ARMS; i++) {
real ang = M_TAU / SNOWFLAKE_ARMS * i + ARGS.rot_angle;
cmplx phase = cdir(ang);
cmplx pos0 = ARGS.pos + spacing * j * phase;
Projectile *p;
for(int side = -1; side <= 1; side += 2) {
p = PROJECTILE(
.proto = pp_crystal,
.pos = pos0 + side * 5 * I * phase,
.color = RGB(0.0 + 0.05 * j, 0.1 + 0.1 * j, 0.9),
.move = ARGS.move,
.angle = ang + side * M_PI / 4,
.max_viewport_dist = 128,
.flags = PFLAG_MANUALANGLE,
);
move_update_multiple(t, &p->pos, &p->move);
p->pos0 = p->prevpos = p->pos;
ENT_ARRAY_ADD(ARGS.array, p);
}
WAIT(1);
++t;
if(j > split) {
cmplx pos1 = ARGS.pos + spacing * split * phase;
for(int side = -1; side <= 1; side += 2) {
cmplx phase2 = cdir(M_PI / 4 * side) * phase;
cmplx pos2 = pos1 + (spacing * (j - split)) * phase2;
p = PROJECTILE(
.proto = pp_crystal,
.pos = pos2,
.color = RGB(0.0, 0.3 * ARGS.size / 5, 1),
.move = ARGS.move,
.angle = ang + side * M_PI / 4,
.max_viewport_dist = 128,
.flags = PFLAG_MANUALANGLE,
);
move_update_multiple(t, &p->pos, &p->move);
p->pos0 = p->prevpos = p->pos;
ENT_ARRAY_ADD(ARGS.array, p);
}
WAIT(1);
++t;
}
}
}
}
DEFINE_EXTERN_TASK(stage1_midboss_nonspell_1) {
Boss *boss = INIT_BOSS_ATTACK();
boss->move = move_towards(CMPLX(VIEWPORT_W/2, 200), 0.02);
BEGIN_BOSS_ATTACK();
boss->move = move_stop(0.8);
int flake_spawn_interval = difficulty_value(11, 10, 9, 8);
int flakes_per_burst = difficulty_value(3, 5, 7, 9);
real launch_speed = difficulty_value(5, 6.25, 6.875, 8.75);
int size_base = 5;
int size_oscillation = 3;
int size_max = size_base + size_oscillation;
int burst_interval = difficulty_value(120, 80, 80, 80);
int charge_time = 60;
burst_interval -= charge_time;
int flakes_limit = flakes_per_burst * snowflake_bullet_limit(size_max);
DECLARE_ENT_ARRAY(Projectile, snowflake_projs, flakes_limit);
for(int burst = 0;; ++burst) {
aniplayer_queue(&boss->ani, "(9)", 0);
INVOKE_SUBTASK(common_charge, boss->pos, RGBA(0, 0.5, 1.0, 0.0), charge_time, .sound = COMMON_CHARGE_SOUNDS);
WAIT(charge_time);
aniplayer_queue(&boss->ani, "main", 0);
real angle_ofs = carg(global.plr.pos - boss->pos);
int size = size_base + size_oscillation * sin(burst * 2.21);
for(int flake = 0; flake < flakes_per_burst; ++flake) {
real angle = circle_angle(flake + flakes_per_burst / 2, flakes_per_burst) + angle_ofs;
MoveParams move = move_asymptotic(launch_speed * cdir(angle), 0, 0.95);
INVOKE_SUBTASK(make_snowflake, boss->pos, move, size, angle, &snowflake_projs);
WAIT(flake_spawn_interval);
}
WAIT(65 - 4 * (size_base + size_oscillation - size));
play_sound("redirect");
// play_sound("shot_special1");
ENT_ARRAY_FOREACH(&snowflake_projs, Projectile *p, {
spawn_projectile_highlight_effect(p)->opacity = 0.25;
color_lerp(&p->color, RGB(0.5, 0.5, 0.5), 0.5);
p->move.velocity = 2 * cdir(p->angle);
p->move.acceleration = -cdir(p->angle) * difficulty_value(0.1, 0.15, 0.2, 0.2);
});
ENT_ARRAY_CLEAR(&snowflake_projs);
WAIT(burst_interval);
}
}

View file

@ -0,0 +1,20 @@
/*
* 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_stage1_nonspells_nonspells_h
#define IGUARD_stages_stage1_nonspells_nonspells_h
#include "taisei.h"
#include "boss.h"
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_midboss_nonspell_1, BossAttack);
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_boss_nonspell_1, BossAttack);
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_boss_nonspell_2, BossAttack);
#endif // IGUARD_stages_stage1_nonspells_nonspells_h

View file

@ -0,0 +1,55 @@
/*
* 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 "../cirno.h"
#include "global.h"
void stage1_spell_benchmark_proc(Boss *b, int t) {
if(t < 0) {
return;
}
int N = 5000; // number of particles on the screen
if(t == 0) {
aniplayer_queue(&b->ani, "(9)", 0);
}
double speed = 10;
int c = N*speed/VIEWPORT_H;
for(int i = 0; i < c; i++) {
double x = rng_range(0, VIEWPORT_W);
double plrx = creal(global.plr.pos);
x = plrx + sqrt((x-plrx)*(x-plrx)+100)*(1-2*(x<plrx));
Projectile *p = PROJECTILE(
.proto = pp_ball,
.pos = x,
.color = RGB(0.1, 0.1, 0.5),
.rule = linear,
.args = { speed*I },
.flags = PFLAG_NOGRAZE,
);
if(rng_chance(0.1)) {
p->flags &= ~PFLAG_NOGRAZE;
}
if(t > 700 && rng_chance(0.5))
projectile_set_prototype(p, pp_plainball);
if(t > 1200 && rng_chance(0.5))
p->color = *RGB(1.0, 0.2, 0.8);
if(t > 350 && rng_chance(0.5))
p->color.a = 0;
}
}

View file

@ -0,0 +1,117 @@
/*
* 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 "../cirno.h"
#include "../misc.h"
#include "common_tasks.h"
#include "global.h"
TASK(crystal_wall, NO_ARGS) {
int num_crystals = difficulty_value(18, 21, 24, 27);
real spacing = VIEWPORT_W / (real)(num_crystals - 1);
real ofs = rng_real() - 1;
for(int i = 0; i < 30; ++i) {
play_sound("shot1");
for(int c = 0; c < num_crystals; ++c) {
cmplx accel = 0.02*I + 0.01*I * ((c % 2) ? 1 : -1) * sin((c * 3 + global.frames) / 30.0);
PROJECTILE(
.proto = pp_crystal,
.pos = (ofs + c) * spacing + 20,
.color = (c % 2) ? RGB(0.2, 0.2, 0.4) : RGB(0.5, 0.5, 0.5),
.move = move_accelerated(0, accel),
.max_viewport_dist = 16,
);
}
WAIT(10);
}
}
TASK(cirno_frostbolt_trail, { BoxedProjectile proj; }) {
Projectile *p = TASK_BIND(ARGS.proj);
int period = 12;
WAIT(rng_irange(0, period + 1));
for(;;) {
stage1_spawn_stain(p->pos, rng_f32_angle(), 20);
WAIT(period);
}
}
TASK(cirno_frostbolt, { cmplx pos; cmplx vel; }) {
Projectile *p = TASK_BIND(PROJECTILE(
.proto = pp_wave,
.pos = ARGS.pos,
.color = RGBA(0.2, 0.2, 0.4, 0.0),
.move = move_asymptotic_simple(ARGS.vel, 5),
));
INVOKE_TASK(cirno_frostbolt_trail, ENT_BOX(p));
WAIT(difficulty_value(200, 300, 400, 500));
p->move.retention = 1.03;
}
DEFINE_EXTERN_TASK(stage1_spell_crystal_blizzard) {
Boss *boss = INIT_BOSS_ATTACK();
boss->move = move_towards(VIEWPORT_W / 2.0 + 300 * I, 0.1);
BEGIN_BOSS_ATTACK();
int frostbolt_period = difficulty_value(4, 3, 2, 1);
for(;;) {
INVOKE_SUBTASK_DELAYED(60, crystal_wall);
int charge_time = 120;
WAIT(330 - charge_time);
aniplayer_queue(&boss->ani, "(9)" ,0);
INVOKE_SUBTASK(common_charge, boss->pos, RGBA(0.5, 0.6, 2.0, 0.0), charge_time, .sound = COMMON_CHARGE_SOUNDS);
WAIT(charge_time);
boss->move = move_towards(global.plr.pos, 0.01);
boss->move.attraction_max_speed = 128;
for(int t = 0; t < 370; ++t) {
play_loop("shot1_loop");
boss->move.attraction_point = global.plr.pos;
if(!(t % frostbolt_period)) {
cmplx aim = cnormalize(global.plr.pos - boss->pos) * cdir(rng_sreal() * 0.2);
cmplx vel = rng_range(0.01, 4) * aim;
INVOKE_TASK(cirno_frostbolt, boss->pos, vel);
}
if(!(t % 7)) {
play_sound("shot1");
int cnt = global.diff - 1;
for(int i = 0; i < cnt; ++i) {
PROJECTILE(
.proto = pp_ball,
.pos = boss->pos,
.color = RGBA(0.1, 0.1, 0.5, 0.0),
.move = move_accelerated(0, 0.01 * cdir(global.frames/20.0 + i*M_TAU/cnt)),
);
}
}
WAIT(1);
}
boss->move.attraction_point = 0;
boss->move.attraction = 0;
boss->move.retention = 0.8;
aniplayer_queue(&boss->ani, "main" ,0);
}
}

View file

@ -0,0 +1,95 @@
/*
* 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 "../cirno.h"
#include "stage.h"
#include "common_tasks.h"
#include "global.h"
TASK(crystal_rain_drops, NO_ARGS) {
const int nshots = difficulty_value(1, 2, 4, 5);
for(;;) {
play_sound("shot2");
for(int i = 0; i < nshots; ++i) {
RNG_ARRAY(rng, 2);
cmplx org = vrng_range(rng[0], 0, VIEWPORT_W);
cmplx aim = cnormalize(global.plr.pos - org);
PROJECTILE(
.proto = pp_crystal,
.pos = org,
.color = RGB(0.2,0.2,0.4),
.move = move_accelerated(1.0*I, 0.01*aim),
);
}
WAIT(10);
}
}
TASK(crystal_rain_cirno_shoot, { BoxedBoss boss; int charge_time; }) {
Boss *boss = TASK_BIND(ARGS.boss);
cmplx shot_ofs = -60*I;
aniplayer_queue(&boss->ani, "(9)", 0);
WAIT(10);
INVOKE_SUBTASK(common_charge, shot_ofs, RGBA(0.3, 0.5, 1, 0), ARGS.charge_time,
.anchor = &boss->pos,
.sound = COMMON_CHARGE_SOUNDS
);
WAIT(ARGS.charge_time);
int interval = difficulty_value(100, 80, 50, 20);
for(int t = 0, round = 0; t < 400; ++round) {
bool odd = (global.diff > D_Normal ? (round & 1) : 0);
real n = (difficulty_value(1, 2, 6, 11) + odd)/2.0;
cmplx org = boss->pos + shot_ofs;
play_sound("shot_special1");
for(real i = -n; i <= n; i++) {
PROJECTILE(
.proto = odd? pp_plainball : pp_bigball,
.pos = org,
.color = RGB(0.2, 0.2, 0.9),
.move = move_asymptotic_simple(2 * cdir(carg(global.plr.pos - org) + 0.3 * i), 2.3),
);
}
t += WAIT(interval);
}
aniplayer_queue(&boss->ani, "main", 0);
}
DEFINE_EXTERN_TASK(stage1_spell_crystal_rain) {
Boss *boss = INIT_BOSS_ATTACK();
BEGIN_BOSS_ATTACK();
INVOKE_SUBTASK(crystal_rain_drops);
for(;;) {
WAIT(20);
boss->move.attraction_max_speed = 40;
boss->move.attraction = 0.01;
stage1_cirno_wander(boss, 80, 230);
INVOKE_SUBTASK(crystal_rain_cirno_shoot, ENT_BOX(boss), 80);
WAIT(180);
stage1_cirno_wander(boss, 40, 230);
WAIT(120);
stage1_cirno_wander(boss, 40, 230);
WAIT(180);
}
}

View file

@ -0,0 +1,61 @@
/*
* 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 "../cirno.h"
#include "global.h"
TASK(cirno_icicle, { cmplx pos; cmplx vel; }) {
Projectile *p = TASK_BIND(PROJECTILE(
.proto = pp_crystal,
.pos = ARGS.pos,
.color = RGB(0.3, 0.3, 0.9),
.move = move_asymptotic(ARGS.vel, 0, 0.9),
.max_viewport_dist = 64,
.flags = PFLAG_MANUALANGLE,
));
cmplx v = p->move.velocity;
p->angle = carg(v);
WAIT(80);
v = 2.5 * cdir(carg(v) - M_PI/2.0 + M_PI * (creal(v) > 0));
p->move = move_asymptotic_simple(v, 2);
p->angle = carg(p->move.velocity);
play_sound("redirect");
spawn_projectile_highlight_effect(p);
}
DEFINE_EXTERN_TASK(stage1_spell_icicle_cascade) {
Boss *boss = INIT_BOSS_ATTACK();
boss->move = move_towards(VIEWPORT_W / 2.0 + 120.0*I, 0.01);
BEGIN_BOSS_ATTACK();
int icicle_interval = difficulty_value(30, 22, 16, 8);
int icicles = 4;
real turn = difficulty_value(0.4, 0.4, 0.2, 0.1);
WAIT(20);
aniplayer_queue(&boss->ani, "(9)", 0);
for(int round = 0;; ++round) {
WAIT(icicle_interval);
play_sound("shot1");
for(int i = 0; i < icicles; ++i) {
real speed = 8 + 3 * i;
INVOKE_TASK(cirno_icicle, boss->pos, speed * cdir(-0.1 + turn * (round - 1)));
INVOKE_TASK(cirno_icicle, boss->pos, speed * cdir(+0.1 + turn * (1 - round) + M_PI));
}
}
}

View file

@ -0,0 +1,131 @@
/*
* 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 "../cirno.h"
#include "../misc.h"
#include "common_tasks.h"
#include "global.h"
TASK(move_frozen, { BoxedProjectileArray *parray; }) {
DECLARE_ENT_ARRAY(Projectile, projs, ARGS.parray->size);
ENT_ARRAY_FOREACH(ARGS.parray, Projectile *p, {
ENT_ARRAY_ADD(&projs, p);
});
ENT_ARRAY_FOREACH(&projs, Projectile *p, {
p->color = *RGB(0.9, 0.9, 0.9);
p->move.retention = 1 + 0.002 * global.diff * rng_f64();
p->move.velocity = 2 * rng_dir();
stage1_spawn_stain(p->pos, p->angle, 30);
spawn_projectile_highlight_effect(p);
play_sound_ex("shot2", 0, false);
if(rng_chance(0.4)) {
YIELD;
}
});
}
DEFINE_EXTERN_TASK(stage1_spell_perfect_freeze) {
Boss *boss = INIT_BOSS_ATTACK();
BEGIN_BOSS_ATTACK();
for(int run = 1;;run++) {
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.04);
INVOKE_SUBTASK(common_charge, 0, RGBA(1.0, 0.5, 0.0, 0), 40, .anchor = &boss->pos, .sound = COMMON_CHARGE_SOUNDS);
WAIT(40);
int n = global.diff;
int nfrog = n*60;
DECLARE_ENT_ARRAY(Projectile, projs, nfrog);
for(int i = 0; i < nfrog/n; i++) {
play_loop("shot1_loop");
float r = rng_f32();
float g = rng_f32();
float b = rng_f32();
for(int j = 0; j < n; j++) {
float speed = rng_range(1.0f, 5.0f + 0.5f * global.diff);
ENT_ARRAY_ADD(&projs, PROJECTILE(
.proto = pp_ball,
.pos = boss->pos,
.color = RGB(r, g, b),
.move = move_linear(speed * rng_dir()),
));
}
YIELD;
}
WAIT(20);
ENT_ARRAY_FOREACH(&projs, Projectile *p, {
stage1_spawn_stain(p->pos, p->angle, 30);
stage1_spawn_stain(p->pos, p->angle, 30);
spawn_projectile_highlight_effect(p);
play_sound("shot_special1");
p->color = *RGB(0.9, 0.9, 0.9);
p->move.retention = 0.8 * rng_dir();
if(rng_chance(0.2)) {
YIELD;
}
});
WAIT(60);
double dir = rng_sign();
boss->move = (MoveParams){ .velocity = dir*2.7+I, .retention = 0.99, .acceleration = -dir*0.017 };
int charge_time = difficulty_value(85, 80, 75, 70);
aniplayer_queue(&boss->ani, "(9)", 0);
INVOKE_SUBTASK(common_charge, +60, RGBA(0.3, 0.4, 0.9, 0), charge_time, .anchor = &boss->pos, .sound = COMMON_CHARGE_SOUNDS);
INVOKE_SUBTASK(common_charge, -60, RGBA(0.3, 0.4, 0.9, 0), charge_time, .anchor = &boss->pos);
WAIT(charge_time);
INVOKE_SUBTASK_DELAYED(120, move_frozen, &projs);
int d = imax(0, global.diff - D_Normal);
for(int i = 0; i < 30+10*d; i++) {
play_loop("shot1_loop");
float r1, r2;
if(global.diff > D_Normal) {
r1 = sin(i/M_PI*5.3) * cos(2*i/M_PI*5.3);
r2 = cos(i/M_PI*5.3) * sin(2*i/M_PI*5.3);
} else {
r1 = rng_f32();
r2 = rng_f32();
}
cmplx aim = cnormalize(global.plr.pos - boss->pos);
float speed = 2+0.2*global.diff;
for(int sign = -1; sign <= 1; sign += 2) {
PROJECTILE(
.proto = pp_rice,
.pos = boss->pos + sign*60,
.color = RGB(0.3, 0.4, 0.9),
.move = move_asymptotic_simple(speed*aim*cdir(0.5*(sign > 0 ? r1 : r2)), 2.5+(global.diff>D_Normal)*0.1*sign*I),
);
}
WAIT(6-global.diff/2);
}
aniplayer_queue(&boss->ani,"main",0);
WAIT(20-5*global.diff);
}
}

View file

@ -0,0 +1,213 @@
/*
* 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 "../cirno.h"
#include "../misc.h"
#include "stagetext.h"
#include "global.h"
static Color *halation_color(Color *out_clr, float phase) {
if(phase < 0.5) {
*out_clr = *color_lerp(
RGBA(0.4, 0.4, 0.75, 0),
RGBA(0.4, 0.4, 0.3, 0),
phase * phase
);
} else {
*out_clr = *color_lerp(
RGBA(0.4, 0.4, 0.3, 0),
RGBA(1.0, 0.3, 0.2, 0),
(phase - 0.5) * 2
);
}
return out_clr;
}
TASK(halation_laser_color, { BoxedLaser laser; float max_width; }) {
Laser *l = TASK_BIND(ARGS.laser);
float max_width = ARGS.max_width;
for(;;) {
halation_color(&l->color, l->width / max_width);
YIELD;
}
}
static Laser *create_halation_laser(cmplx a, cmplx b, float width, float charge, float dur, const Color *clr) {
Laser *l;
if(clr == NULL) {
Color c;
l = create_laserline_ab(a, b, width, charge, dur, &c);
INVOKE_TASK(halation_laser_color, ENT_BOX(l), width);
} else {
l = create_laserline_ab(a, b, width, charge, dur, clr);
}
return l;
}
TASK(halation_orb_trail, { BoxedProjectile orb; }) {
Projectile *orb = TASK_BIND(ARGS.orb);
for(;;) {
stage1_spawn_stain(orb->pos, rng_angle(), 20);
WAIT(4);
}
}
TASK(halation_orb, {
cmplx pos[4];
int activation_time;
}) {
Projectile *orb = TASK_BIND(PROJECTILE(
.proto = pp_plainball,
.pos = ARGS.pos[0],
.max_viewport_dist = 200,
.flags = PFLAG_NOCLEAR | PFLAG_NOCOLLISION,
.move = move_towards(ARGS.pos[2], 0.1),
));
halation_color(&orb->color, 0);
INVOKE_SUBTASK(halation_orb_trail, ENT_BOX(orb));
CANCEL_TASK_AFTER(&orb->events.cleared, THIS_TASK);
int activation_time = ARGS.activation_time;
int phase_time = 60;
cmplx *pos = ARGS.pos;
// TODO modernize lasers
WAIT(activation_time);
create_halation_laser(pos[2], pos[3], 15, phase_time * 0.5, phase_time * 2.0, &orb->color);
create_halation_laser(pos[0], pos[2], 15, phase_time, phase_time * 1.5, NULL);
WAIT(phase_time / 2);
play_sound("laser1");
WAIT(phase_time / 2);
create_halation_laser(pos[0], pos[1], 12, phase_time, phase_time * 1.5, NULL);
WAIT(phase_time);
play_sound("shot1");
create_halation_laser(pos[0], pos[3], 15, phase_time, phase_time * 1.5, NULL);
create_halation_laser(pos[1], pos[3], 15, phase_time, phase_time * 1.5, NULL);
WAIT(phase_time);
play_sound("shot1");
create_halation_laser(pos[0], pos[1], 12, phase_time, phase_time * 1.5, NULL);
create_halation_laser(pos[0], pos[2], 15, phase_time, phase_time * 1.5, NULL);
WAIT(phase_time);
play_sound("shot1");
play_sound("shot_special1");
Color colors[] = {
// PRECISE colors, VERY important!!!
{ 226/255.0, 115/255.0, 45/255.0, 1 },
{ 54/255.0, 179/255.0, 221/255.0, 1 },
{ 140/255.0, 147/255.0, 149/255.0, 1 },
{ 22/255.0, 96/255.0, 165/255.0, 1 },
{ 241/255.0, 197/255.0, 31/255.0, 1 },
{ 204/255.0, 53/255.0, 84/255.0, 1 },
{ 116/255.0, 71/255.0, 145/255.0, 1 },
{ 84/255.0, 171/255.0, 72/255.0, 1 },
{ 213/255.0, 78/255.0, 141/255.0, 1 },
};
int pcount = sizeof(colors)/sizeof(Color);
float rot = rng_angle();
for(int i = 0; i < pcount; ++i) {
PROJECTILE(
.proto = pp_crystal,
.pos = orb->pos,
.color = colors+i,
.move = move_asymptotic_simple(cdir(rot + M_PI * 2 * (i + 1.0) / pcount), 3),
);
}
kill_projectile(orb);
}
TASK(halation_chase, { BoxedBoss boss; }) {
Boss *boss = TASK_BIND(ARGS.boss);
boss->move = move_towards(global.plr.pos, 0.05);
for(;;) {
aniplayer_queue(&boss->ani, "(9)", 0);
boss->move.attraction_point = global.plr.pos;
YIELD;
}
}
DEFINE_EXTERN_TASK(stage1_spell_snow_halation) {
Boss *boss = INIT_BOSS_ATTACK();
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.05);
BEGIN_BOSS_ATTACK();
cmplx center;
real rotation = 0;
real cage_radius = 200;
int cheater = 0;
const int orbs = difficulty_value(0, 0, 10, 14);
for(;;) {
WAIT(60);
center = global.plr.pos;
rotation += M_PI/2;
cmplx orb_positions[orbs];
for(int i = 0; i < orbs; ++i) {
orb_positions[i] = cage_radius * cdir(rotation + (i + 0.5) / orbs * M_TAU) + center;
}
for(int i = 0; i < orbs; ++i) {
play_loop("shot1_loop");
INVOKE_TASK(halation_orb,
.pos = {
orb_positions[i],
orb_positions[(i + orbs/2) % orbs],
orb_positions[(i + orbs/2 - 1) % orbs],
orb_positions[(i + orbs/2 - 2) % orbs],
},
.activation_time = 35 - i
);
YIELD;
}
WAIT(60);
if(cabs(global.plr.pos - center) > 200) {
static char *text[] = {
"What are you doing??",
"Dodge it properly!",
"I bet you cant do it! Idiot!",
"I spent so much time on this attack!",
"Maybe it is too smart for secondaries!",
"I think you dont even understand the timer!",
"You- You Idiootttt!",
};
if(cheater < ARRAY_SIZE(text)) {
stagetext_add(text[cheater], global.boss->pos+100*I, ALIGN_CENTER, get_font("standard"), RGB(1,1,1), 0, 100, 10, 20);
if(++cheater == ARRAY_SIZE(text)) {
INVOKE_SUBTASK(halation_chase, ENT_BOX(boss));
}
}
}
WAIT(240);
}
}

View file

@ -4,26 +4,21 @@
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
*/
#ifndef IGUARD_stages_stage1_events_h
#define IGUARD_stages_stage1_events_h
#ifndef IGUARD_stages_stage1_spells_spells_h
#define IGUARD_stages_stage1_spells_spells_h
#include "taisei.h"
#include "boss.h"
void cirno_pfreeze_bg(Boss*, int);
void cirno_benchmark(Boss*, int);
Boss *stage1_spawn_cirno(cmplx pos);
DECLARE_EXTERN_TASK(stage1_main, NO_ARGS);
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_spell_perfect_freeze, BossAttack);
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_spell_crystal_rain, BossAttack);
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_spell_snow_halation, BossAttack);
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_spell_icicle_cascade, BossAttack);
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_spell_crystal_blizzard, BossAttack);
#endif // IGUARD_stages_stage1_events_h
void stage1_spell_benchmark_proc(Boss *b, int t);
#endif // IGUARD_stages_stage1_spells_spells_h

137
src/stages/stage1/stage1.c Normal file
View file

@ -0,0 +1,137 @@
/*
* 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 "stage1.h"
#include "draw.h"
#include "background_anim.h"
#include "cirno.h"
#include "spells/spells.h"
#include "timeline.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 stage1_spells_s stage1_spells = {
.mid = {
.perfect_freeze = {
{ 0, 1, 2, 3}, AT_Spellcard, "Freeze Sign “Perfect Freeze”", 50, 24000,
NULL, stage1_draw_cirno_spellbg, VIEWPORT_W/2.0+100.0*I, 1,
TASK_INDIRECT_INIT(BossAttack, stage1_spell_perfect_freeze)
},
},
.boss = {
.crystal_rain = {
{ 4, 5, 6, 7}, AT_Spellcard, "Freeze Sign “Crystal Rain”", 40, 33000,
NULL, stage1_draw_cirno_spellbg, VIEWPORT_W/2.0+100.0*I, 1,
TASK_INDIRECT_INIT(BossAttack, stage1_spell_crystal_rain)
},
.snow_halation = {
{-1, -1, 12, 13}, AT_Spellcard, "Winter Sign “Snow Halation”", 50, 40000,
NULL, stage1_draw_cirno_spellbg, VIEWPORT_W/2.0+100.0*I, 1,
TASK_INDIRECT_INIT(BossAttack, stage1_spell_snow_halation)
},
.icicle_cascade = {
{ 8, 9, 10, 11}, AT_Spellcard, "Doom Sign “Icicle Cascade”", 40, 40000,
NULL, stage1_draw_cirno_spellbg, VIEWPORT_W/2.0+100.0*I, 1,
TASK_INDIRECT_INIT(BossAttack, stage1_spell_icicle_cascade)
},
},
.extra.crystal_blizzard = {
{ 0, 1, 2, 3}, AT_ExtraSpell, "Frost Sign “Crystal Blizzard”", 60, 40000,
NULL, stage1_draw_cirno_spellbg, VIEWPORT_W/2.0+100.0*I, 1,
TASK_INDIRECT_INIT(BossAttack, stage1_spell_crystal_blizzard)
},
};
#ifdef SPELL_BENCHMARK
AttackInfo stage1_spell_benchmark = {
{-1, -1, -1, -1, 127}, AT_SurvivalSpell, "Profiling “ベンチマーク”", 40, 40000,
stage1_spell_benchmark_proc, stage1_draw_cirno_spellbg, VIEWPORT_W/2.0+100.0*I
};
#endif
static void stage1_start(void) {
stage1_drawsys_init();
stage1_bg_init_fullstage();
stage_start_bgm("stage1");
stage_set_voltage_thresholds(50, 125, 300, 600);
INVOKE_TASK(stage1_timeline);
}
static void stage1_spellpractice_start(void) {
stage1_drawsys_init();
stage1_bg_init_spellpractice();
Boss *cirno = stage1_spawn_cirno(BOSS_DEFAULT_SPAWN_POS);
boss_add_attack_from_info(cirno, global.stage->spell, true);
boss_start_attack(cirno, cirno->attacks);
global.boss = cirno;
stage_start_bgm("stage1boss");
INVOKE_TASK_WHEN(&cirno->events.defeated, common_call_func, stage1_bg_disable_snow);
}
static void stage1_preload(void) {
// DIALOG_PRELOAD(&global.plr, Stage1PreBoss, RESF_DEFAULT);
portrait_preload_base_sprite("cirno", NULL, RESF_DEFAULT);
portrait_preload_face_sprite("cirno", "normal", RESF_DEFAULT);
preload_resources(RES_BGM, RESF_OPTIONAL, "stage1", "stage1boss", NULL);
preload_resources(RES_SPRITE, RESF_DEFAULT,
"stage1/cirnobg",
"stage1/fog",
"stage1/snowlayer",
"stage1/waterplants",
NULL);
preload_resources(RES_TEXTURE, RESF_DEFAULT,
"stage1/horizon",
NULL);
preload_resources(RES_SHADER_PROGRAM, RESF_DEFAULT,
"blur5",
"stage1_water",
"zbuf_fog",
NULL);
preload_resources(RES_SHADER_PROGRAM, RESF_OPTIONAL,
"lasers/linear",
NULL);
preload_resources(RES_ANIM, RESF_DEFAULT,
"boss/cirno",
NULL);
preload_resources(RES_SFX, RESF_OPTIONAL,
"laser1",
NULL);
}
static void stage1_end(void) {
stage1_drawsys_shutdown();
}
StageProcs stage1_procs = {
.begin = stage1_start,
.draw = stage1_draw,
.end = stage1_end,
.preload = stage1_preload,
.shader_rules = stage1_bg_effects,
.spellpractice_procs = &(StageProcs) {
.begin = stage1_spellpractice_start,
.draw = stage1_draw,
.end = stage1_end,
.preload = stage1_preload,
.shader_rules = stage1_bg_effects,
},
};

View file

@ -6,15 +6,15 @@
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_stages_stage1_h
#define IGUARD_stages_stage1_h
#ifndef IGUARD_stages_stage1_stage1_h
#define IGUARD_stages_stage1_stage1_h
#include "taisei.h"
#include "stageinfo.h"
#if defined(DEBUG) && !defined(SPELL_BENCHMARK)
#define SPELL_BENCHMARK
#define SPELL_BENCHMARK
#endif
extern struct stage1_spells_s {
@ -42,12 +42,8 @@ extern struct stage1_spells_s {
extern StageProcs stage1_procs;
extern StageProcs stage1_spell_procs;
void stage1_bg_raise_camera(void);
void stage1_bg_enable_snow(void);
void stage1_bg_disable_snow(void);
#ifdef SPELL_BENCHMARK
extern AttackInfo stage1_spell_benchmark;
#endif
#endif // IGUARD_stages_stage1_h
#endif // IGUARD_stages_stage1_stage1_h

View file

@ -0,0 +1,800 @@
/*
* 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 "stage1.h"
#include "cirno.h"
#include "spells/spells.h"
#include "nonspells/nonspells.h"
#include "background_anim.h"
#include "global.h"
#include "stagetext.h"
#include "common_tasks.h"
TASK(burst_fairy, { cmplx pos; cmplx dir; }) {
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 700, Fairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 1,
.power = 1,
});
e->move.attraction_point = ARGS.pos + 120*I;
e->move.attraction = 0.03;
WAIT(60);
play_sound("shot1");
int n = 1.5 * global.diff - 1;
for(int i = -n; i <= n; i++) {
cmplx aim = cdir(carg(global.plr.pos - e->pos) + 0.2 * i);
PROJECTILE(
.proto = pp_crystal,
.pos = e->pos,
.color = RGB(0.2, 0.3, 0.5),
.move = move_asymptotic_simple(aim * (2 + 0.5 * global.diff), 5),
);
}
WAIT(1);
e->move.attraction = 0;
e->move.acceleration = 0.04 * ARGS.dir;
e->move.retention = 1;
}
TASK(circletoss_shoot_circle, { BoxedEnemy e; int duration; int interval; }) {
Enemy *e = TASK_BIND(ARGS.e);
int cnt = ARGS.duration / ARGS.interval;
double angle_step = M_TAU / cnt;
for(int i = 0; i < cnt; ++i) {
play_loop("shot1_loop");
e->move.velocity *= 0.8;
cmplx aim = cdir(angle_step * i);
PROJECTILE(
.proto = pp_rice,
.pos = e->pos,
.color = RGB(0.6, 0.2, 0.7),
.move = move_asymptotic_simple(2 * aim, i * 0.5),
);
WAIT(ARGS.interval);
}
}
TASK(circletoss_shoot_toss, { BoxedEnemy e; int times; int duration; int period; }) {
Enemy *e = TASK_BIND(ARGS.e);
while(ARGS.times--) {
for(int i = ARGS.duration; i--;) {
play_loop("shot1_loop");
double aim_angle = carg(global.plr.pos - e->pos);
aim_angle += 0.05 * global.diff * rng_real();
cmplx aim = cdir(aim_angle);
aim *= rng_range(1, 3);
PROJECTILE(
.proto = pp_thickrice,
.pos = e->pos,
.color = RGB(0.2, 0.4, 0.8),
.move = move_asymptotic_simple(aim, 3),
);
WAIT(1);
}
WAIT(ARGS.period - ARGS.duration);
}
}
TASK(circletoss_fairy, { cmplx pos; cmplx velocity; cmplx exit_accel; int exit_time; }) {
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 1500, BigFairy, NULL, 0));
e->move = move_linear(ARGS.velocity);
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 2,
.power = 1,
});
INVOKE_SUBTASK_DELAYED(60, circletoss_shoot_circle, ENT_BOX(e),
.duration = 40,
.interval = 2 + (global.diff < D_Hard)
);
if(global.diff > D_Easy) {
INVOKE_SUBTASK_DELAYED(90, circletoss_shoot_toss, ENT_BOX(e),
.times = 4,
.period = 150,
.duration = 5 + 7 * global.diff
);
}
WAIT(ARGS.exit_time);
e->move.acceleration += ARGS.exit_accel;
STALL;
}
TASK(sinepass_swirl_move, { BoxedEnemy e; cmplx v; cmplx sv; }) {
Enemy *e = TASK_BIND(ARGS.e);
cmplx sv = ARGS.sv;
cmplx v = ARGS.v;
for(;;) {
sv -= cimag(e->pos - e->pos0) * 0.03 * I;
e->pos += sv * 0.4 + v;
YIELD;
}
}
TASK(sinepass_swirl, { cmplx pos; cmplx vel; cmplx svel; }) {
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 100, Swirl, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 1,
});
INVOKE_TASK(sinepass_swirl_move, ENT_BOX(e), ARGS.vel, ARGS.svel);
WAIT(difficulty_value(25, 20, 15, 10));
int shot_interval = difficulty_value(120, 40, 30, 20);
for(;;) {
play_sound("shot1");
cmplx aim = cnormalize(global.plr.pos - e->pos);
aim *= difficulty_value(2, 2, 2.5, 3);
PROJECTILE(
.proto = pp_ball,
.pos = e->pos,
.color = RGB(0.8, 0.8, 0.4),
.move = move_asymptotic_simple(aim, 5),
);
WAIT(shot_interval);
}
}
TASK(circle_fairy, { cmplx pos; cmplx target_pos; }) {
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 1400, BigFairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 3,
.power = 2,
});
e->move.attraction = 0.005;
e->move.retention = 0.8;
e->move.attraction_point = ARGS.target_pos;
WAIT(120);
int shot_interval = 2;
int shot_count = difficulty_value(10, 10, 20, 25);
int round_interval = 120 - shot_interval * shot_count;
for(int round = 0; round < 2; ++round) {
double a_ofs = rng_angle();
for(int i = 0; i < shot_count; ++i) {
cmplx aim;
aim = circle_dir_ofs((round & 1) ? i : shot_count - i, shot_count, a_ofs);
aim *= difficulty_value(1.7, 2.0, 2.5, 2.5);
PROJECTILE(
.proto = pp_rice,
.pos = e->pos,
.color = RGB(0.6, 0.2, 0.7),
.move = move_asymptotic_simple(aim, i * 0.5),
);
play_loop("shot1_loop");
WAIT(shot_interval);
}
e->move.attraction_point += 30 * rng_dir();
WAIT(round_interval);
}
WAIT(10);
e->move.attraction = 0;
e->move.retention = 1;
e->move.acceleration = -0.04 * I * cdir(rng_range(0, M_TAU / 12));
STALL;
}
TASK(drop_swirl, { cmplx pos; cmplx vel; cmplx accel; }) {
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 100, Swirl, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 2,
});
e->move = move_accelerated(ARGS.vel, ARGS.accel);
int shot_interval = difficulty_value(120, 60, 30, 20);
WAIT(difficulty_value(40, 30, 20, 20));
while(true) {
cmplx aim = cnormalize(global.plr.pos - e->pos);
aim *= 1 + 0.3 * global.diff + rng_real();
play_sound("shot1");
PROJECTILE(
.proto = pp_ball,
.pos = e->pos,
.color = RGB(0.8, 0.8, 0.4),
.move = move_linear(aim),
);
WAIT(shot_interval);
}
}
TASK(multiburst_fairy, { cmplx pos; cmplx target_pos; cmplx exit_accel; }) {
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 1000, Fairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 3,
.power = 2,
});
e->move.attraction = 0.05;
// e->move.retention = 0.8;
e->move.attraction_point = ARGS.target_pos;
WAIT(60);
int burst_interval = difficulty_value(22, 20, 18, 16);
int bursts = 4;
for(int i = 0; i < bursts; ++i) {
play_sound("shot1");
int n = global.diff - 1;
for(int j = -n; j <= n; j++) {
cmplx aim = cdir(carg(global.plr.pos - e->pos) + j / 5.0);
aim *= 2.5;
PROJECTILE(
.proto = pp_crystal,
.pos = e->pos,
.color = RGB(0.2, 0.3, 0.5),
.move = move_linear(aim),
);
}
WAIT(burst_interval);
}
WAIT(10);
e->move.attraction = 0;
e->move.retention = 1;
e->move.acceleration = ARGS.exit_accel;
}
TASK(instantcircle_fairy_shoot, { BoxedEnemy e; int cnt; double speed; double boost; }) {
Enemy *e = TASK_BIND(ARGS.e);
play_sound("shot_special1");
for(int i = 0; i < ARGS.cnt; ++i) {
cmplx vel = ARGS.speed * circle_dir(i, ARGS.cnt);
PROJECTILE(
.proto = pp_rice,
.pos = e->pos,
.color = RGB(0.6, 0.2, 0.7),
.move = move_asymptotic_simple(vel, ARGS.boost),
);
}
}
TASK(instantcircle_fairy, { cmplx pos; cmplx target_pos; cmplx exit_accel; }) {
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 1200, Fairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 2,
.power = 4,
});
e->move = move_towards(ARGS.target_pos, 0.04);
BoxedEnemy be = ENT_BOX(e);
INVOKE_TASK_DELAYED(75, instantcircle_fairy_shoot, be,
.cnt = difficulty_value(20, 22, 24, 28),
.speed = 1.5,
.boost = 2.0
);
if(global.diff > D_Easy) {
INVOKE_TASK_DELAYED(95, instantcircle_fairy_shoot, be,
.cnt = difficulty_value(0, 26, 29, 32),
.speed = 3,
.boost = 3.0
);
}
WAIT(200);
e->move.attraction = 0;
e->move.retention = 1;
e->move.acceleration = ARGS.exit_accel;
}
TASK(waveshot, { cmplx pos; real angle; real spread; real freq; int shots; int interval; } ) {
for(int i = 0; i < ARGS.shots; ++i) {
cmplx v = 4 * cdir(ARGS.angle + ARGS.spread * triangle(ARGS.freq * i));
play_loop("shot1_loop");
PROJECTILE(
.proto = pp_thickrice,
.pos = ARGS.pos,
.color = RGBA(0.0, 0.5 * (1.0 - i / (ARGS.shots - 1.0)), 1.0, 1),
.move = move_asymptotic(-4 * v, v, 0.9),
// .move = move_accelerated(-v, 0.02 * v),
);
WAIT(ARGS.interval);
}
}
TASK(waveshot_fairy, { cmplx pos; cmplx target_pos; cmplx exit_accel; }) {
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 4200, BigFairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 4,
.power = 2,
});
e->move = move_towards(ARGS.target_pos, 0.03);
WAIT(120);
cmplx orig_pos = e->pos;
real angle = carg(global.plr.pos - orig_pos);
cmplx pos = orig_pos - 24 * cdir(angle);
real spread = difficulty_value(M_PI/20, M_PI/18, M_PI/16, M_PI/14);
real interval = difficulty_value(3, 2, 1, 1);
real shots = 60 / interval;
real frequency = 60 * (1.0/12.0) / shots;
shots += 1;
INVOKE_SUBTASK(waveshot, pos, angle, rng_sign() * spread, frequency, shots, interval);
WAIT(120);
e->move.attraction = 0;
e->move.retention = 0.8;
e->move.acceleration = ARGS.exit_accel;
}
TASK(explosion_fairy, { cmplx pos; cmplx target_pos; cmplx exit_accel; }) {
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 6000, BigFairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 8,
});
e->move = move_towards(ARGS.target_pos, 0.06);
WAIT(40);
INVOKE_SUBTASK(common_charge, 0, RGBA(1.0, 0, 0.2, 0), 60,
.anchor = &e->pos,
.sound = COMMON_CHARGE_SOUNDS
);
WAIT(60);
int cnt = difficulty_value(30, 30, 60, 60);
int trails = difficulty_value(0, 2, 3, 4);
real speed = difficulty_value(2, 2, 4, 4);
real ofs = rng_angle();
play_sound("shot_special1");
for(int i = 0; i < cnt; ++i) {
cmplx aim = speed * circle_dir_ofs(i, cnt, ofs);
real s = 0.5 + 0.5 * triangle(0.1 * i);
Color clr;
if(s == 1) {
clr = *RGB(1, 0, 0);
} else {
clr = *color_lerp(
RGB(0.1, 0.6, 1.0),
RGB(1.0, 0.0, 0.3),
s * s
);
color_mul(&clr, &clr);
}
PROJECTILE(
.proto = s == 1 ? pp_bigball : pp_ball,
.pos = e->pos,
.color = &clr,
.move = move_asymptotic_simple(aim, 1 + 8 * s),
);
for(int j = 0; j < trails; ++j) {
aim *= 0.8;
PROJECTILE(
.proto = pp_rice,
.pos = e->pos,
.color = &clr,
.move = move_asymptotic_simple(aim, 1 + 8 * s),
);
}
}
WAIT(10);
e->move.attraction = 0;
e->move.retention = 0.8;
e->move.acceleration = ARGS.exit_accel;
}
// opening. projectile bursts
TASK(burst_fairies_1, NO_ARGS) {
for(int i = 3; i--;) {
INVOKE_TASK(burst_fairy, VIEWPORT_W/2 + 70, 1 + 0.6*I);
INVOKE_TASK(burst_fairy, VIEWPORT_W/2 - 70, -1 + 0.6*I);
WAIT(25);
}
}
// more bursts. fairies move / \ like
TASK(burst_fairies_2, NO_ARGS) {
for(int i = 3; i--;) {
double ofs = 70 + i * 40;
INVOKE_TASK(burst_fairy, ofs, 1 + 0.6*I);
WAIT(15);
INVOKE_TASK(burst_fairy, VIEWPORT_W - ofs, -1 + 0.6*I);
WAIT(15);
}
}
TASK(burst_fairies_3, NO_ARGS) {
for(int i = 10; i--;) {
cmplx pos = VIEWPORT_W/2 - 200 * sin(1.17 * global.frames);
INVOKE_TASK(burst_fairy, pos, rng_sign());
WAIT(60);
}
}
// swirl, sine pass
TASK(sinepass_swirls, { int duration; double level; double dir; }) {
int duration = ARGS.duration;
double dir = ARGS.dir;
cmplx pos = CMPLX(ARGS.dir < 0 ? VIEWPORT_W : 0, ARGS.level);
int delay = difficulty_value(30, 20, 15, 10);
for(int t = 0; t < duration; t += delay) {
INVOKE_TASK(sinepass_swirl, pos, 3.5 * dir, 7.0 * I);
WAIT(delay);
}
}
// big fairies, circle + projectile toss
TASK(circletoss_fairies_1, NO_ARGS) {
for(int i = 0; i < 2; ++i) {
INVOKE_TASK(circletoss_fairy,
.pos = VIEWPORT_W * i + VIEWPORT_H / 3 * I,
.velocity = 2 - 4 * i - 0.3 * I,
.exit_accel = 0.03 * (1 - 2 * i) - 0.04 * I ,
.exit_time = (global.diff > D_Easy) ? 500 : 240
);
WAIT(50);
}
}
TASK(drop_swirls, { int cnt; cmplx pos; cmplx vel; cmplx accel; }) {
for(int i = 0; i < ARGS.cnt; ++i) {
INVOKE_TASK(drop_swirl, ARGS.pos, ARGS.vel, ARGS.accel);
WAIT(20);
}
}
TASK(schedule_swirls, NO_ARGS) {
INVOKE_TASK(drop_swirls, 25, VIEWPORT_W/3, 2*I, 0.06);
WAIT(400);
INVOKE_TASK(drop_swirls, 25, 200*I, 4, -0.06*I);
}
TASK(circle_fairies_1, NO_ARGS) {
for(int i = 0; i < 3; ++i) {
for(int j = 0; j < 3; ++j) {
INVOKE_TASK(circle_fairy, VIEWPORT_W - 64, VIEWPORT_W/2 - 100 + 200 * I + 128 * j);
WAIT(60);
}
WAIT(90);
for(int j = 0; j < 3; ++j) {
INVOKE_TASK(circle_fairy, 64, VIEWPORT_W/2 + 100 + 200 * I - 128 * j);
WAIT(60);
}
WAIT(240);
}
}
TASK(multiburst_fairies_1, NO_ARGS) {
for(int row = 0; row < 3; ++row) {
for(int col = 0; col < 5; ++col) {
cmplx pos = rng_range(0, VIEWPORT_W);
cmplx target_pos = 64 + 64 * col + I * (64 * row + 100);
cmplx exit_accel = 0.02 * I + 0.03;
INVOKE_TASK(multiburst_fairy, pos, target_pos, exit_accel);
WAIT(10);
}
WAIT(120);
}
}
TASK(instantcircle_fairies, { int duration; }) {
int interval = difficulty_value(160, 130, 100, 70);
for(int t = ARGS.duration; t > 0; t -= interval) {
double x = VIEWPORT_W/2 + 205 * sin(2.13*global.frames);
double y = VIEWPORT_H/2 + 120 * cos(1.91*global.frames);
INVOKE_TASK(instantcircle_fairy, x, x+y*I, 0.2 * I);
WAIT(interval);
}
}
TASK(waveshot_fairies, { int duration; }) {
int interval = 200;
for(int t = ARGS.duration; t > 0; t -= interval) {
double x = VIEWPORT_W/2 + round(rng_sreal() * 69);
double y = rng_range(200, 240);
INVOKE_TASK(waveshot_fairy, x, x+y*I, 0.15 * I);
WAIT(interval);
}
}
TASK_WITH_INTERFACE(midboss_intro, BossAttack) {
Boss *boss = INIT_BOSS_ATTACK();
BEGIN_BOSS_ATTACK();
boss->move = move_towards(VIEWPORT_W/2.0 + 200.0*I, 0.035);
}
TASK_WITH_INTERFACE(midboss_flee, BossAttack) {
Boss *boss = INIT_BOSS_ATTACK();
BEGIN_BOSS_ATTACK();
boss->move = move_towards(-250 + 30 * I, 0.02);
}
TASK(spawn_midboss, NO_ARGS) {
STAGE_BOOKMARK_DELAYED(120, midboss);
Boss *boss = global.boss = stage1_spawn_cirno(VIEWPORT_W + 220 + 30.0*I);
boss_add_attack_task(boss, AT_Move, "Introduction", 2, 0, TASK_INDIRECT(BossAttack, midboss_intro), NULL);
boss_add_attack_task(boss, AT_Normal, "Icy Storm", 20, 24000, TASK_INDIRECT(BossAttack, stage1_midboss_nonspell_1), NULL);
boss_add_attack_from_info(boss, &stage1_spells.mid.perfect_freeze, false);
boss_add_attack_task(boss, AT_Move, "Introduction", 2, 0, TASK_INDIRECT(BossAttack, midboss_flee), NULL);
boss_start_attack(boss, boss->attacks);
WAIT(60);
stage1_bg_enable_snow();
}
TASK(tritoss_fairy, { cmplx pos; cmplx velocity; cmplx end_velocity; }) {
Enemy *e = TASK_BIND(create_enemy1c(ARGS.pos, 16000, BigFairy, NULL, 0));
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
.points = 5,
.power = 5,
});
e->move = move_linear(ARGS.velocity);
WAIT(60);
e->move.retention = 0.9;
WAIT(20);
int interval = difficulty_value(12, 9, 5, 3);
int rounds = 680/interval;
for(int k = 0; k < rounds; k++) {
play_sound("shot1");
float a = M_PI / 30.0 * ((k/7) % 30) + 0.1 * rng_f32();
int n = difficulty_value(3,4,4,5);
for(int i = 0; i < n; i++) {
PROJECTILE(
.proto = pp_thickrice,
.pos = e->pos,
.color = RGB(0.2, 0.4, 0.8),
.move = move_asymptotic_simple(2*cdir(a+2.0*M_PI/n*i), 3),
);
}
if(k == rounds/2 || k == rounds-1) {
play_sound("shot_special1");
int n2 = difficulty_value(20, 23, 26, 30);
for(int i = 0; i < n2; i++) {
PROJECTILE(
.proto = pp_rice,
.pos = e->pos,
.color = RGB(0.6, 0.2, 0.7),
.move = move_asymptotic_simple(1.5*cdir(2*M_PI/n2*i),2),
);
if(global.diff > D_Easy) {
PROJECTILE(
.proto = pp_rice,
.pos = e->pos,
.color = RGB(0.6, 0.2, 0.7),
.move = move_asymptotic_simple(3*cdir(2*M_PI/n2*i), 3.0),
);
}
}
}
WAIT(interval);
}
WAIT(20);
e->move = move_asymptotic_simple(ARGS.end_velocity, -1);
}
TASK(boss_appear, { BoxedBoss boss; }) {
Boss *boss = ENT_UNBOX(ARGS.boss);
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.05);
}
TASK(spawn_boss, NO_ARGS) {
STAGE_BOOKMARK(boss);
Boss *boss = global.boss = stage1_spawn_cirno(-230 + 100.0*I);
PlayerMode *pm = global.plr.mode;
Stage1PreBossDialogEvents *e;
INVOKE_TASK_INDIRECT(Stage1PreBossDialog, pm->dialog->Stage1PreBoss, &e);
INVOKE_TASK_WHEN(&e->boss_appears, boss_appear, ENT_BOX(boss));
INVOKE_TASK_WHEN(&e->music_changes, common_start_bgm, "stage1boss");
WAIT_EVENT(&global.dialog->events.fadeout_began);
boss_add_attack_task(boss, AT_Normal, "Iceplosion 0", 20, 23000, TASK_INDIRECT(BossAttack, stage1_boss_nonspell_1), NULL);
boss_add_attack_from_info(boss, &stage1_spells.boss.crystal_rain, false);
boss_add_attack_task(boss, AT_Normal, "Iceplosion 1", 20, 24000, TASK_INDIRECT(BossAttack, stage1_boss_nonspell_2), NULL);
if(global.diff > D_Normal) {
boss_add_attack_from_info(boss, &stage1_spells.boss.snow_halation, false);
}
boss_add_attack_from_info(boss, &stage1_spells.boss.icicle_cascade, false);
boss_add_attack_from_info(boss, &stage1_spells.extra.crystal_blizzard, false);
boss_start_attack(boss, boss->attacks);
}
static void stage1_dialog_post_boss(void) {
PlayerMode *pm = global.plr.mode;
INVOKE_TASK_INDIRECT(Stage1PostBossDialog, pm->dialog->Stage1PostBoss);
}
DEFINE_EXTERN_TASK(stage1_timeline) {
INVOKE_TASK_DELAYED(100, burst_fairies_1);
INVOKE_TASK_DELAYED(240, burst_fairies_2);
INVOKE_TASK_DELAYED(440, sinepass_swirls, 180, 100, 1);
INVOKE_TASK_DELAYED(480, circletoss_fairies_1);
INVOKE_TASK_DELAYED(660, circle_fairies_1);
INVOKE_TASK_DELAYED(900, schedule_swirls);
INVOKE_TASK_DELAYED(1500, burst_fairies_3);
INVOKE_TASK_DELAYED(2200, multiburst_fairies_1);
INVOKE_TASK_DELAYED(2200, common_call_func, stage1_bg_raise_camera);
STAGE_BOOKMARK_DELAYED(2500, pre-midboss);
INVOKE_TASK_DELAYED(2700, spawn_midboss);
while(!global.boss) YIELD;
int midboss_time = WAIT_EVENT(&global.boss->events.defeated).frames;
int filler_time = 2180;
int time_ofs = 500 - midboss_time;
log_debug("midboss_time = %i; filler_time = %i; time_ofs = %i", midboss_time, filler_time, time_ofs);
STAGE_BOOKMARK(post-midboss);
int swirl_spam_time = 760;
for(int i = 0; i < swirl_spam_time; i += 30) {
int o = ((int[]) { 0, 1, 0, -1 })[(i / 60) % 4];
INVOKE_TASK_DELAYED(i + time_ofs, sinepass_swirls, 40, 132 + 32 * o, 1 - 2 * ((i / 60) & 1));
}
time_ofs += swirl_spam_time;
INVOKE_TASK_DELAYED(40 + time_ofs, burst_fairies_1);
int instacircle_time = filler_time - swirl_spam_time - 600;
for(int i = 0; i < instacircle_time; i += 180) {
INVOKE_TASK_DELAYED(i + time_ofs, sinepass_swirls, 80, 132, 1);
INVOKE_TASK_DELAYED(120 + i + time_ofs, instantcircle_fairies, 120);
}
WAIT(filler_time - midboss_time);
STAGE_BOOKMARK(post-midboss-filler);
INVOKE_TASK_DELAYED(100, circletoss_fairy, -25 + VIEWPORT_H/3*I, 1 - 0.5*I, 0.01 * ( 1 - I), 200);
INVOKE_TASK_DELAYED(125, circletoss_fairy, VIEWPORT_W+25 + VIEWPORT_H/3*I, -1 - 0.5*I, 0.01 * (-1 - I), 200);
if(global.diff > D_Normal) {
INVOKE_TASK_DELAYED(115, circletoss_fairy, -25 + 2*VIEWPORT_H/3*I, 1 - 0.5*I, 0.01 * ( 1 - I), 200);
INVOKE_TASK_DELAYED(140, circletoss_fairy, VIEWPORT_W+25 + 2*VIEWPORT_H/3*I, -1 - 0.5*I, 0.01 * (-1 - I), 200);
}
STAGE_BOOKMARK_DELAYED(200, waveshot-fairies);
INVOKE_TASK_DELAYED(240, waveshot_fairies, 600);
INVOKE_TASK_DELAYED(400, burst_fairies_3);
STAGE_BOOKMARK_DELAYED(1000, post-midboss-filler-2);
INVOKE_TASK_DELAYED(1000, burst_fairies_1);
INVOKE_TASK_DELAYED(1120, explosion_fairy, 120*I, VIEWPORT_W-80 + 120*I, -0.2+0.1*I);
INVOKE_TASK_DELAYED(1280, explosion_fairy, VIEWPORT_W + 220*I, 80 + 220*I, 0.2+0.1*I);
STAGE_BOOKMARK_DELAYED(1400, post-midboss-filler-3);
INVOKE_TASK_DELAYED(1400, drop_swirls, 25, 2*VIEWPORT_W/3, 2*I, -0.06);
INVOKE_TASK_DELAYED(1600, drop_swirls, 25, VIEWPORT_W/3, 2*I, 0.06);
INVOKE_TASK_DELAYED(1520, tritoss_fairy, VIEWPORT_W / 2 - 30*I, 3 * I, -2.6 * I);
INVOKE_TASK_DELAYED(1820, circle_fairy, VIEWPORT_W + 42 + 300*I, VIEWPORT_W - 130 + 240*I);
INVOKE_TASK_DELAYED(1820, circle_fairy, - 42 + 300*I, 130 + 240*I);
INVOKE_TASK_DELAYED(1880, instantcircle_fairy, VIEWPORT_W + 42 + 300*I, VIEWPORT_W - 84 + 260*I, 0.2 * (-2 - I));
INVOKE_TASK_DELAYED(1880, instantcircle_fairy, - 42 + 300*I, 84 + 260*I, 0.2 * ( 2 - I));
INVOKE_TASK_DELAYED(2120, waveshot_fairy, VIEWPORT_W + 42 + 300*I, 130 + 140*I, 0.2 * (-2 - I));
INVOKE_TASK_DELAYED(2120, waveshot_fairy, - 42 + 300*I, VIEWPORT_W - 130 + 140*I, 0.2 * ( 2 - I));
STAGE_BOOKMARK_DELAYED(2300, pre-boss);
WAIT(2560);
INVOKE_TASK(spawn_boss);
while(!global.boss) YIELD;
WAIT_EVENT(&global.boss->events.defeated);
stage_unlock_bgm("stage1boss");
WAIT(120);
stage1_bg_disable_snow();
WAIT(120);
stage1_dialog_post_boss();
WAIT_EVENT(&global.dialog->events.fadeout_began);
WAIT(5);
stage_finish(GAMEOVER_SCORESCREEN);
}

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_stage1_timeline_h
#define IGUARD_stages_stage1_timeline_h
#include "taisei.h"
#include "coroutine.h"
DECLARE_EXTERN_TASK(stage1_timeline, NO_ARGS);
#endif // IGUARD_stages_stage1_timeline_h

File diff suppressed because it is too large Load diff