Split stage1 into multiple files (#228)
This commit is contained in:
parent
bd7bb3a351
commit
db9cb15d21
27 changed files with 2387 additions and 2003 deletions
|
@ -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"
|
||||
|
|
|
@ -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')
|
||||
|
|
77
src/stages/stage1/background_anim.c
Normal file
77
src/stages/stage1/background_anim.c
Normal 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;
|
||||
}
|
21
src/stages/stage1/background_anim.h
Normal file
21
src/stages/stage1/background_anim.h
Normal 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
39
src/stages/stage1/cirno.c
Normal 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
20
src/stages/stage1/cirno.h
Normal 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
|
|
@ -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
42
src/stages/stage1/draw.h
Normal 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
|
18
src/stages/stage1/meson.build
Normal file
18
src/stages/stage1/meson.build
Normal 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
22
src/stages/stage1/misc.c
Normal 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
18
src/stages/stage1/misc.h
Normal 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
|
94
src/stages/stage1/nonspells/boss_nonspell_1.c
Normal file
94
src/stages/stage1/nonspells/boss_nonspell_1.c
Normal 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);
|
||||
}
|
||||
}
|
172
src/stages/stage1/nonspells/boss_nonspell_2.c
Normal file
172
src/stages/stage1/nonspells/boss_nonspell_2.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
142
src/stages/stage1/nonspells/midboss_nonspell_1.c
Normal file
142
src/stages/stage1/nonspells/midboss_nonspell_1.c
Normal 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);
|
||||
}
|
||||
}
|
20
src/stages/stage1/nonspells/nonspells.h
Normal file
20
src/stages/stage1/nonspells/nonspells.h
Normal 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
|
55
src/stages/stage1/spells/benchmark.c
Normal file
55
src/stages/stage1/spells/benchmark.c
Normal 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;
|
||||
}
|
||||
}
|
117
src/stages/stage1/spells/crystal_blizzard.c
Normal file
117
src/stages/stage1/spells/crystal_blizzard.c
Normal 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);
|
||||
}
|
||||
}
|
95
src/stages/stage1/spells/crystal_rain.c
Normal file
95
src/stages/stage1/spells/crystal_rain.c
Normal 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);
|
||||
}
|
||||
}
|
61
src/stages/stage1/spells/icicle_cascade.c
Normal file
61
src/stages/stage1/spells/icicle_cascade.c
Normal 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));
|
||||
}
|
||||
}
|
||||
}
|
131
src/stages/stage1/spells/perfect_freeze.c
Normal file
131
src/stages/stage1/spells/perfect_freeze.c
Normal 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);
|
||||
}
|
||||
}
|
213
src/stages/stage1/spells/snow_halation.c
Normal file
213
src/stages/stage1/spells/snow_halation.c
Normal 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 can’t do it! Idiot!",
|
||||
"I spent so much time on this attack!",
|
||||
"Maybe it is too smart for secondaries!",
|
||||
"I think you don’t 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);
|
||||
}
|
||||
}
|
|
@ -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
137
src/stages/stage1/stage1.c
Normal 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,
|
||||
},
|
||||
};
|
|
@ -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
|
800
src/stages/stage1/timeline.c
Normal file
800
src/stages/stage1/timeline.c
Normal 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);
|
||||
}
|
18
src/stages/stage1/timeline.h
Normal file
18
src/stages/stage1/timeline.h
Normal 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
Loading…
Reference in a new issue