taisei/src/stages/stage1.c
Andrei "Akari" Alexeyev 1e6011433c Initial support for single-spell stages
Adapted all of the current spellcards into spellstages, which will
later be used in a spell practice mode a-la IN.
For now they are only accessible through the stage select menu or
by specifying their ID on the command line; both available only
if you built with -DTAISEI_DEBUG=1
2017-02-19 03:28:00 +01:00

707 lines
18 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (C) 2011, Lukas Weber <laochailan@web.de>
*/
#include "stage1.h"
#include "stage.h"
#include "global.h"
#include "stageutils.h"
static Stage3D bgcontext;
void cirno_perfect_freeze(Boss*, int);
void cirno_crystal_rain(Boss*, int);
void cirno_icicle_fall(Boss*, int);
void cirno_pfreeze_bg(Boss*, int);
AttackInfo stage1_spells[] = {
{AT_Spellcard, "Freeze Sign ~ Perfect Freeze", 32, 20000, cirno_perfect_freeze, cirno_pfreeze_bg, VIEWPORT_W/2.0+100.0I},
{AT_Spellcard, "Freeze Sign ~ Crystal Rain", 28, 28000, cirno_crystal_rain, cirno_pfreeze_bg, VIEWPORT_W/2.0+100.0I},
{AT_Spellcard, "Doom Sign ~ Icicle Fall", 35, 40000, cirno_icicle_fall, cirno_pfreeze_bg, VIEWPORT_W/2.0+100.0I},
};
Dialog *stage1_dialog(void) {
Dialog *d = create_dialog(global.plr.cha == Marisa ? "dialog/marisa" : "dialog/youmu", "dialog/cirno");
dadd_msg(d, Right, "Hey! Whos there?");
if(global.plr.cha == Marisa)
dadd_msg(d, Left, "Its me!");
else
dadd_msg(d, Left, "Just someone?");
dadd_msg(d, Right, "How dare you pass the lake of the fairies?!\nIts a dangerous place for weak humans!");
if(global.plr.cha == Marisa) {
dadd_msg(d, Left, "You call me weak?");
dadd_msg(d, Right, "I do!");
} else {
dadd_msg(d, Left, "Im just passing by. Got a problem with that?");
dadd_msg(d, Right, "Of course! You cant do that!");
}
dadd_msg(d, Right, "Ill freeze you where you stand!");
dadd_msg(d, BGM, "bgm_stage1boss");
return d;
}
void stage1_bg_draw(Vector pos) {
glPushMatrix();
glTranslatef(pos[0],pos[1],pos[0]);
glRotatef(180,1,0,0);
glScalef(1200,3000,1);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, get_tex("stage1/water")->gltex);
draw_quad();
glDisable(GL_TEXTURE_2D);
glPopMatrix();
}
Vector **stage1_bg_pos(Vector p, float maxrange) {
Vector q = {0,0,0};
Vector r = {0,3000,0};
return linear3dpos(p, maxrange, q, r);
}
void stage1_smoke_draw(Vector pos) {
float d = fabsf(pos[1]-bgcontext.cx[1]);
glDisable(GL_DEPTH_TEST);
glPushMatrix();
glTranslatef(pos[0]+200*sin(pos[1]), pos[1], pos[2]+200*sin(pos[1]/25.0));
glRotatef(90,-1,0,0);
glScalef(3.5,2,1);
glRotatef(global.frames,0,0,1);
glColor4f(.2,.2,.2,((d-500)*(d-500))/1.5e6);
draw_texture(0,0,"stage1/fog");
glColor4f(1,1,1,1);
glPopMatrix();
glEnable(GL_DEPTH_TEST);
}
Vector **stage1_smoke_pos(Vector p, float maxrange) {
Vector q = {0,0,-300};
Vector r = {0,300,0};
return linear3dpos(p, maxrange/2.0, q, r);
}
void stage1_fog(int fbonum) {
Shader *shader = get_shader("zbuf_fog");
glUseProgram(shader->prog);
glUniform1i(uniloc(shader, "tex"), 0);
glUniform1i(uniloc(shader, "depth"), 1);
glUniform4f(uniloc(shader, "fog_color"),0.1, 0.1, 0.1, 1.0);
glUniform1f(uniloc(shader, "start"),0.0);
glUniform1f(uniloc(shader, "end"),0.4);
glUniform1f(uniloc(shader, "exponent"),3.0);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, resources.fbg[fbonum].depth);
glActiveTexture(GL_TEXTURE0);
draw_fbo_viewport(&resources.fbg[fbonum]);
glUseProgram(0);
}
void stage1_draw(void) {
set_perspective(&bgcontext, 500, 5000);
draw_stage3d(&bgcontext, 7000);
}
void cirno_intro(Boss *c, int time) {
GO_TO(c, VIEWPORT_W/2.0 + 100.0I, 0.035);
}
void cirno_icy(Boss *c, int time) {
int t = time % 280;
TIMER(&t);
if(time < 0)
return;
FROM_TO(0, 200, 5-global.diff) {
tsrand_fill(6);
create_projectile2c("crystal", VIEWPORT_W/2.0 + 10*_i*(0.5-afrand(0)) + cimag(c->pos)*I, rgb(0.2,0.5,0.4+0.5*afrand(1)), accelerated, 1.7*cexp(I*_i/10.0)*(1-2*(_i&1)), 0.0001I*_i + (0.0025 - global.diff*0.005*afrand(2)));
create_projectile2c("crystal", VIEWPORT_W/2.0 + 10*_i*(0.5-afrand(3)) + cimag(c->pos)*I, rgb(0.2,0.5,0.4+0.5*afrand(4)), accelerated, 1.7*cexp(I*_i/10.0)*(1-2*(_i&1)), 0.0001I*_i + (0.0025 - global.diff*0.005*afrand(5)));
}
}
int cirno_pfreeze_frogs(Projectile *p, int t) {
if(t == EVENT_DEATH)
free_ref(p->args[1]);
if(t < 0)
return 1;
Boss *parent = REF(p->args[1]);
if(parent == NULL)
return ACTION_DESTROY;
int boss_t = (global.frames - parent->current->starttime) % 320;
if(boss_t < 110)
linear(p, t);
else if(boss_t == 110) {
free(p->clr);
p->clr = rgb(0.7,0.7,0.7);
}
if(t == 240) {
p->pos0 = p->pos;
p->args[0] = (1.8+0.2*global.diff)*cexp(I*2*M_PI*frand());
}
if(t > 240)
linear(p, t-240);
return 1;
}
void cirno_perfect_freeze(Boss *c, int time) {
int t = time % 320;
TIMER(&t);
if(time < 0)
return;
FROM_TO(-40, 0, 1)
GO_TO(c, VIEWPORT_W/2.0 + 100.0I, 0.04);
FROM_TO(20,80,1) {
float r = frand();
float g = frand();
float b = frand();
int i;
int n = global.diff;
for(i = 0; i < n; i++)
create_projectile2c("ball", c->pos, rgb(r, g, b), cirno_pfreeze_frogs, 4*cexp(I*tsrand()), add_ref(global.boss));
}
GO_AT(c, 160, 190, 2 + 1.0I);
FROM_TO(160, 220, 6-global.diff) {
create_projectile2c("rice", c->pos + 60, rgb(0.3, 0.4, 0.9), asymptotic, (2.5+0.5*global.diff)*cexp(I*(carg(global.plr.pos - c->pos) + 0.5*nfrand())), 2.5);
create_projectile2c("rice", c->pos - 60, rgb(0.3, 0.4, 0.9), asymptotic, (2.5+0.5*global.diff)*cexp(I*(carg(global.plr.pos - c->pos) + 0.5*nfrand())), 2.5);
}
GO_AT(c, 190, 220, -2);
FROM_TO(280, 320, 1)
GO_TO(c, VIEWPORT_W/2.0 + 100.0I, 0.04);
}
void cirno_pfreeze_bg(Boss *c, int time) {
glColor4f(0.5,0.5,0.5,1);
fill_screen(time/700.0, time/700.0, 1, "stage1/cirnobg");
glColor4f(0.7,0.7,0.7,0.5);
glBlendFunc(GL_ZERO, GL_SRC_COLOR);
fill_screen(-time/700.0 + 0.5, time/700.0+0.5, 0.4, "stage1/cirnobg");
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
fill_screen(0, -time/100.0, 0, "stage1/snowlayer");
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f(1,1,1,1);
}
Boss *create_cirno_mid(void) {
Boss* cirno = create_boss("Cirno", "cirno", VIEWPORT_W + 220 + 30.0I);
boss_add_attack(cirno, AT_Move, "Introduction", 2, 0, cirno_intro, NULL);
boss_add_attack(cirno, AT_Normal, "Icy Storm", 20, 20000, cirno_icy, NULL);
boss_add_attack_from_info(cirno, stage1_spells+0, false);
start_attack(cirno, cirno->attacks);
return cirno;
}
void cirno_intro_boss(Boss *c, int time) {
if(time < 0)
return;
TIMER(&time);
GO_TO(c, VIEWPORT_W/2.0 + 100.0I, 0.035);
AT(100)
global.dialog = stage1_dialog();
}
void cirno_iceplosion0(Boss *c, int time) {
int t = time % 350;
TIMER(&t);
if(time < 0)
return;
FROM_TO(20,30,2) {
int i;
int n = 8+global.diff;
for(i = 0; i < n; i++) {
create_projectile2c("plainball", c->pos, rgb(0,0,0.5), asymptotic, (3+_i/3.0)*cexp(I*(2*M_PI/n*i + carg(global.plr.pos-c->pos))), _i*0.7);
}
}
FROM_TO(40,100,1) {
create_projectile2c("crystal", c->pos, rgb(0.3,0.3,0.8), accelerated, 2*cexp(2.0I*M_PI*frand()) + 2.0I, 0.002*cexp(I*(M_PI/10.0*(_i%20))));
}
FROM_TO(150, 300, 30-5*global.diff) {
float dif = M_PI*2*frand();
int i;
for(i = 0; i < 20; i++) {
create_projectile2c("plainball", c->pos, rgb(0.04*_i,0.04*_i,0.4+0.04*_i), asymptotic, (3+_i/4.0)*cexp(I*(2*M_PI/8.0*i + dif)), 2.5);
}
}
}
void cirno_crystal_rain(Boss *c, int time) {
int t = time % 500;
TIMER(&t);
if(time < 0)
return;
if(frand() > 0.9-0.1*global.diff) {
tsrand_fill(2);
create_projectile2c("crystal", VIEWPORT_W*afrand(0), rgb(0.2,0.2,0.4), accelerated, 1.0I, 0.01I + (0.01+0.003*global.diff)*anfrand(1));
}
FROM_TO(100, 400, 120-20*global.diff) {
float i;
float n = global.diff/2.0;
for(i = -n; i <= n; i++)
create_projectile2c("bigball", c->pos, rgb(0.2,0.2,0.9), asymptotic, 2*cexp(I*carg(global.plr.pos-c->pos)+0.3I*i), 2.3);
}
GO_AT(c, 20, 70, 1+0.6I);
GO_AT(c, 120, 170, -1+0.2I);
GO_AT(c, 230, 300, -1+0.6I);
FROM_TO(400, 500, 1)
GO_TO(c, VIEWPORT_W/2.0 + 100.0I, 0.01);
}
void cirno_iceplosion1(Boss *c, int time) {
int t = time % 360;
TIMER(&t);
if(time < 0)
GO_TO(c, VIEWPORT_W/2.0 + 100.0I, 0.02);
FROM_TO(20,30,2) {
int i;
for(i = 0; i < 15+global.diff; i++) {
create_projectile2c("plainball", c->pos, rgb(0,0,0.5), asymptotic, (3+_i/3.0)*cexp(I*((2)*M_PI/8.0*i + (0.1+0.03*global.diff)*(1 - 2*frand()))), _i*0.7);
}
}
FROM_TO(40,100,2) {
create_projectile2c("crystal", c->pos + 100, rgb(0.3,0.3,0.8), accelerated, 1.5*cexp(2.0I*M_PI*frand()) - 0.4 + 2.0I, 0.002*cexp(I*(M_PI/10.0*(_i%20))));
create_projectile2c("crystal", c->pos - 100, rgb(0.3,0.3,0.8), accelerated, 1.5*cexp(2.0I*M_PI*frand()) + 0.4 + 2.0I, 0.002*cexp(I*(M_PI/10.0*(_i%20))));
}
FROM_TO(150, 300, 30) {
float dif = M_PI*2*frand();
int i;
for(i = 0; i < 20; i++) {
create_projectile2c("plainball", c->pos, rgb(0.04*_i,0.04*_i,0.4+0.04*_i), asymptotic, (3+_i/3.0)*cexp(I*(2*M_PI/8.0*i + dif)), 2.5);
}
}
}
int cirno_icicles(Projectile *p, int t) {
int turn = 60;
if(t < 0)
return 1;
if(t < turn) {
p->pos += p->args[0]*pow(0.9,t);
} else if(t == turn) {
p->args[0] = 2.5*cexp(I*(carg(p->args[0])-M_PI/2.0+M_PI*(creal(p->args[0]) > 0)));
} else if(t > turn) {
p->pos += p->args[0];
}
p->angle = carg(p->args[0]);
return 1;
}
void cirno_icicle_fall(Boss *c, int time) {
int t = time % 400;
TIMER(&t);
if(time < 0)
return;
GO_TO(c, VIEWPORT_W/2.0+120.0I, 0.01);
FROM_TO(20,200,30) {
int i;
for(i = 2; i < 5; i++) {
create_projectile1c("crystal", c->pos, rgb(0.3,0.3,0.9), cirno_icicles, 6*i*cexp(I*(-0.1+0.1*_i)));
create_projectile1c("crystal", c->pos, rgb(0.3,0.3,0.9), cirno_icicles, 6*i*cexp(I*(M_PI+0.1-0.1*_i)));
}
}
}
Boss *create_cirno(void) {
Boss* cirno = create_boss("Cirno", "cirno", -230 + 100.0I);
boss_add_attack(cirno, AT_Move, "Introduction", 2, 0, cirno_intro_boss, NULL);
boss_add_attack(cirno, AT_Normal, "Iceplosion 0", 20, 20000, cirno_iceplosion0, NULL);
boss_add_attack_from_info(cirno, stage1_spells+1, false);
boss_add_attack(cirno, AT_Normal, "Iceplosion 1", 20, 20000, cirno_iceplosion1, NULL);
boss_add_attack_from_info(cirno, stage1_spells+2, false);
start_attack(cirno, cirno->attacks);
return cirno;
}
int stage1_burst(Enemy *e, int time) {
TIMER(&time);
AT(EVENT_DEATH) {
spawn_items(e->pos, 3,0,0,0);
return 1;
}
FROM_TO(0, 60, 1)
e->pos += 2.0I;
AT(60) {
int i = 0;
int n = global.diff+1;
for(i = -n; i <= n; i++) {
create_projectile2c("crystal", e->pos, rgb(0.2, 0.3, 0.5), asymptotic, (2+0.1*global.diff)*cexp(I*(carg(global.plr.pos - e->pos) + 0.2*i)), 5);
}
e->moving = true;
e->dir = creal(e->args[0]) < 0;
e->pos0 = e->pos;
}
FROM_TO(70, 900, 1)
e->pos = e->pos0 + (0.04*e->args[0])*_i*_i;
return 1;
}
int stage1_circletoss(Enemy *e, int time) {
TIMER(&time);
AT(EVENT_DEATH) {
spawn_items(e->pos, 2,2,0,0);
return 1;
}
e->pos += e->args[0];
FROM_TO(60,100,2) {
e->args[0] = 0.5*e->args[0];
create_projectile2c("rice", e->pos, rgb(0.6, 0.2, 0.7), asymptotic, 2*cexp(I*M_PI/10*_i), _i/2.0);
}
if(global.diff > D_Easy) {
FROM_TO_INT(90,500,150,15+5*global.diff,1) {
tsrand_fill(2);
create_projectile2c("thickrice", e->pos, rgb(0.2, 0.4, 0.8), asymptotic, (1+afrand(0)*2)*cexp(I*carg(global.plr.pos - e->pos)+0.05I*global.diff*anfrand(1)), 3);
}
}
FROM_TO(global.diff > D_Easy ? 500 : 240, 900, 1)
e->args[0] += 0.03*e->args[1] - 0.04I;
return 1;
}
int stage1_sinepass(Enemy *e, int time) {
TIMER(&time);
AT(EVENT_DEATH) {
tsrand_fill(2);
spawn_items(e->pos, afrand(0)>0.5, afrand(1)>0.2,0,0);
return 1;
}
e->args[1] -= cimag(e->pos-e->pos0)*0.03I;
e->pos += e->args[1]*0.4 + e->args[0];
if(frand() > 0.993-0.002*global.diff)
create_projectile1c("ball", e->pos, rgb(0.8,0.8,0.4), linear, (1+0.2*global.diff+frand())*cexp(I*carg(global.plr.pos - e->pos)));
return 1;
}
int stage1_drop(Enemy *e, int t) {
TIMER(&t);
AT(EVENT_DEATH) {
spawn_items(e->pos, 2,1,0,0);
return 1;
}
if(t < 0)
return 1;
e->pos = e->pos0 + e->args[0]*t + e->args[1]*t*t;
FROM_TO(10,1000,1)
if(frand() > 0.995-0.006*global.diff)
create_projectile1c("ball", e->pos, rgb(0.8,0.8,0.4), linear, (1+0.3*global.diff+frand())*cexp(I*carg(global.plr.pos - e->pos)));
return 1;
}
int stage1_circle(Enemy *e, int t) {
TIMER(&t);
AT(EVENT_DEATH) {
spawn_items(e->pos, 3,4,0,0);
return 1;
}
FROM_TO(0, 150, 1)
e->pos += (e->args[0] - e->pos)*0.02;
FROM_TO_INT(150, 550, 40, 40, 2)
create_projectile2c("rice", e->pos, rgb(0.6, 0.2, 0.7), asymptotic, (2+0.1*global.diff)*cexp(I*M_PI/10*_ni), _ni/2.0);
FROM_TO(560,1000,1)
e->pos += e->args[1];
return 1;
}
int stage1_multiburst(Enemy *e, int t) {
TIMER(&t);
AT(EVENT_DEATH) {
spawn_items(e->pos, 3,4,0,0);
return 1;
}
FROM_TO(0, 50, 1)
e->pos += 2.0I;
FROM_TO_INT(60, 300, 70, 40, 12-global.diff) {
int i;
int n = 1+global.diff/2;
for(i = -n; i <= n; i++)
create_projectile1c("crystal", e->pos, rgb(0.2, 0.3, 0.5), linear, 2.5*cexp(I*(carg(global.plr.pos - e->pos) + i/5.0)));
}
FROM_TO(320, 700, 1) {
e->args[1] += 0.03;
e->pos += e->args[0]*e->args[1] + 1.4I;
}
return 1;
}
int stage1_instantcircle(Enemy *e, int t) {
TIMER(&t);
AT(EVENT_DEATH) {
spawn_items(e->pos, 2,4,0,0);
return 1;
}
FROM_TO(0, 110, 1) {
e->pos += e->args[0];
}
int i;
AT(150) {
for(i = 0; i < 20+2*global.diff; i++)
create_projectile2c("rice", e->pos, rgb(0.6, 0.2, 0.7), asymptotic, 1.5*cexp(I*2*M_PI/(20.0+global.diff)*i), 2.0);
}
AT(170) {
for(i = 0; i < 20+3*global.diff; i++)
create_projectile2c("rice", e->pos, rgb(0.6, 0.2, 0.7), asymptotic, 3*cexp(I*2*M_PI/(20.0+global.diff)*i), 3.0);
}
if(t > 200)
e->pos += e->args[1];
return 1;
}
int stage1_tritoss(Enemy *e, int t) {
TIMER(&t);
AT(EVENT_DEATH) {
spawn_items(e->pos, 5,5,0,0);
return 1;
}
FROM_TO(0, 100, 1) {
e->pos += e->args[0];
}
FROM_TO(120, 800,8-global.diff) {
float a = M_PI/30.0*((_i/7)%30)+0.1*nfrand();
int i;
int n = 3+global.diff/2;
for(i = 0; i < n; i++)
create_projectile2c("thickrice", e->pos, rgb(0.2, 0.4, 0.8), asymptotic, 2*cexp(I*a+2.0I*M_PI/n*i), 3);
}
FROM_TO(480, 800, 300) {
int i, n = 20 + global.diff*2;
for(i = 0; i < n; i++) {
create_projectile2c("rice", e->pos, rgb(0.6, 0.2, 0.7), asymptotic, 1.5*cexp(I*2*M_PI/n*i), 2.0);
create_projectile2c("rice", e->pos, rgb(0.6, 0.2, 0.7), asymptotic, 3*cexp(I*2*M_PI/n*i), 3.0);
}
}
if(t > 820)
e->pos += e->args[1];
return 1;
}
void stage1_events(void) {
TIMER(&global.timer);
/*
// graze testing
AT(0) {
create_projectile1c("rice", 0.5*(VIEWPORT_W+VIEWPORT_H*1.3*I), rgb(1, 0.3, 0.3), linear, 0);
create_projectile1c("ball", 0.5*(VIEWPORT_W+VIEWPORT_H*1.5*I), rgb(1, 0.3, 0.3), linear, 0);
create_projectile1c("soul", 0.5*(VIEWPORT_W+VIEWPORT_H*I), rgb(1, 0.3, 0.3), linear, 0);
int i;
for(i = 0; i < 20; ++i) {
create_projectile1c("crystal", VIEWPORT_W/2+30 + (30+10*i)*I, rgb(0.7, 0.7, 1), linear, 0.01I);
create_projectile1c("crystal", VIEWPORT_W/2-30 + (30+10*i)*I, rgb(0.7, 0.7, 1), linear, 0.01I);
}
}
return;
*/
// opening. projectile bursts
FROM_TO(100, 160, 25) {
create_enemy1c(VIEWPORT_W/2 + 70, 700, Fairy, stage1_burst, 1 + 0.6I);
create_enemy1c(VIEWPORT_W/2 - 70, 700, Fairy, stage1_burst, -1 + 0.6I);
}
// more bursts. fairies move / \ like
FROM_TO(240, 300, 30) {
create_enemy1c(70 + _i*40, 700, Fairy, stage1_burst, -1 + 0.6I);
create_enemy1c(VIEWPORT_W - (70 + _i*40), 700, Fairy, stage1_burst, 1 + 0.6I);
}
// big fairies, circle + projectile toss
FROM_TO(400, 460, 50)
create_enemy2c(VIEWPORT_W*_i + VIEWPORT_H/3*I, 1500, BigFairy, stage1_circletoss, 2-4*_i-0.3I, 1-2*_i);
// swirl, sine pass
FROM_TO(380, 1000, 20) {
tsrand_fill(2);
create_enemy2c(VIEWPORT_W*(_i&1) + afrand(0)*100.0I + 70.0I, 100, Swirl, stage1_sinepass, 3.5*(1-2*(_i&1)), afrand(1)*7.0I);
}
// swirl, drops
FROM_TO(1100, 1600, 20)
create_enemy2c(VIEWPORT_W/3, 100, Swirl, stage1_drop, 4.0I, 0.06);
FROM_TO(1500, 2000, 20)
create_enemy2c(VIEWPORT_W+200.0I, 100, Swirl, stage1_drop, -2, -0.04-0.03I);
// bursts
FROM_TO(1250, 1800, 60) {
tsrand_fill(2);
create_enemy1c(VIEWPORT_W/2 + afrand(0)*500-250, 500, Fairy, stage1_burst, afrand(1)*2-1);
}
// circle - multi burst combo
FROM_TO(1700, 2300, 300) {
tsrand_fill(3);
create_enemy2c(VIEWPORT_W/2, 1400, BigFairy, stage1_circle, VIEWPORT_W/4 + VIEWPORT_W/2*afrand(0)+200.0I, 3-6*(afrand(1)>0.5)+afrand(2)*2.0I);
}
FROM_TO(2000, 2500, 200) {
int i, t = global.diff + 1;
for(i = 0; i < t; i++)
create_enemy1c(VIEWPORT_W/2 - 40*t + 80*i, 1000, Fairy, stage1_multiburst, i - 2.5);
}
AT(2700)
global.boss = create_cirno_mid();
// some chaotic swirls + instant circle combo
FROM_TO(2760, 3800, 20) {
tsrand_fill(2);
create_enemy2c(VIEWPORT_W/2 - 200*anfrand(0), 250+40*global.diff, Swirl, stage1_drop, 1.0I, 0.001I + 0.02 + 0.06*anfrand(1));
}
FROM_TO(2900, 3750, 190-30*global.diff) {
tsrand_fill(2);
create_enemy2c(VIEWPORT_W*afrand(0), 1200, Fairy, stage1_instantcircle, 2.0I, 3.0 - 6*afrand(1) - 1.0I);
}
// multiburst + normal circletoss, later tri-toss
FROM_TO(3900, 4800, 200) {
tsrand_fill(2);
create_enemy1c(VIEWPORT_W*afrand(0), 1000, Fairy, stage1_multiburst, 2.5*afrand(1));
}
FROM_TO(4000, 4100, 20)
create_enemy2c(VIEWPORT_W*_i + VIEWPORT_H/3*I, 1700, Fairy, stage1_circletoss, 2-4*_i-0.3I, 1-2*_i);
AT(4200)
create_enemy2c(VIEWPORT_W/2.0, 4000, BigFairy, stage1_tritoss, 2.0I, -2.6I);
AT(5000)
global.boss = create_cirno();
}
void stage1_start(void) {
init_stage3d(&bgcontext);
start_bgm("bgm_stage1");
add_model(&bgcontext, stage1_bg_draw, stage1_bg_pos);
add_model(&bgcontext, stage1_smoke_draw, stage1_smoke_pos);
bgcontext.crot[0] = 60;
bgcontext.cx[2] = 700;
bgcontext.cv[1] = 7;
}
void stage1_end(void) {
free_stage3d(&bgcontext);
}
void stage1_loop(void) {
ShaderRule list[] = { stage1_fog, NULL };
stage_loop(stage1_start, stage1_end, stage1_draw, stage1_events, list, 5200);
}
void stage1_spellpractice_events(void) {
TIMER(&global.timer);
AT(0) {
Boss* cirno = create_boss("Cirno", "cirno", BOSS_DEFAULT_SPAWN_POS);
boss_add_attack_from_info(cirno, global.stage->spell, true);
start_attack(cirno, cirno->attacks);
global.boss = cirno;
}
}
void stage1_spellpractice_loop(void) {
ShaderRule list[] = { stage1_fog, NULL };
stage_loop(stage1_start, stage1_end, stage1_draw, stage1_spellpractice_events, list, 0);
}