Improved the boss system

by removing that strange waypoint system and replacing it by even more macro madness.
This commit is contained in:
laochailan 2011-08-23 17:37:14 +02:00
parent d04475591c
commit fe542e5378
6 changed files with 179 additions and 180 deletions

View file

@ -11,14 +11,14 @@ uniform vec2 blur_orig; // center
uniform vec2 fix_orig;
uniform float blur_rad; // radius of zoom effect
uniform float rad;
uniform float ratio; // texture w/h
uniform float ratio; // texture h/w
void main(void) {
vec2 pos = vec2(gl_TexCoord[0]);
pos -= blur_orig;
vec2 pos1 = pos;
pos1.y *= 2.0;
pos1.y *= ratio;
if(length(pos1) < blur_rad)
pos *= length(pos1)/blur_rad;

View file

@ -12,22 +12,14 @@
Boss *create_boss(char *name, char *ani, complex pos) {
Boss *buf = malloc(sizeof(Boss));
memset(buf, 0, sizeof(Boss));
buf->name = malloc(strlen(name) + 1);
strcpy(buf->name, name);
buf->pos = pos;
buf->pos0 = pos;
buf->time0 = 0;
buf->acount = 0;
buf->attacks = NULL;
buf->current = NULL;
buf->ani = get_ani(ani);
buf->anirow = 0;
buf->dmg = 0;
return buf;
}
@ -43,88 +35,70 @@ void spell_opening(Boss *b, int time) {
}
void draw_boss(Boss *boss) {
draw_animation_p(creal(boss->pos), cimag(boss->pos), boss->anirow, boss->ani);
if(boss->current && (boss->current->type == AT_Spellcard || boss->current->type == AT_SurvivalSpell))
spell_opening(boss, global.frames - boss->current->starttime);
draw_animation_p(creal(boss->pos), cimag(boss->pos) + 10*sin(global.frames/25.0), boss->anirow, boss->ani);
draw_text(AL_Left, 10, 20, boss->name, _fonts.standard);
if(boss->current) {
char buf[16];
snprintf(buf, 16, "%.2f", (boss->current->timeout - global.frames + boss->current->starttime)/(float)FPS);
draw_text(AL_Center, VIEWPORT_W - 20, 10, buf, _fonts.standard);
}
if(!boss->current)
return;
glPushMatrix();
glTranslatef(boss->attacks[boss->acount-1].dmglimit+10, 4,0);
if(boss->current->type == AT_Spellcard || boss->current->type == AT_SurvivalSpell)
spell_opening(boss, global.frames - boss->current->starttime);
if(boss->current->type != AT_Move) {
char buf[16];
snprintf(buf, sizeof(buf), "%.2f", (boss->current->timeout - global.frames + boss->current->starttime)/(float)FPS);
draw_text(AL_Center, VIEWPORT_W - 20, 10, buf, _fonts.standard);
int i;
for(i = boss->acount-1; i >= 0; i--) {
if(boss->dmg > boss->attacks[i].dmglimit)
continue;
glPushMatrix();
glTranslatef(boss->attacks[boss->acount-1].dmglimit+10, 4,0);
switch(boss->attacks[i].type) {
case AT_Normal:
glColor3f(1,1,1);
break;
case AT_Spellcard:
glColor3f(1,0.8,0.8);
break;
case AT_SurvivalSpell:
glColor3f(1,0.5,0.5);
}
glBegin(GL_QUADS);
glVertex3f(-boss->attacks[i].dmglimit, 0, 0);
glVertex3f(-boss->attacks[i].dmglimit, 2, 0);
glVertex3f(-boss->dmg, 2, 0);
glVertex3f(-boss->dmg, 0, 0);
glEnd();
int i;
for(i = boss->acount-1; i >= 0; i--) {
if(boss->dmg > boss->attacks[i].dmglimit)
continue;
switch(boss->attacks[i].type) {
case AT_Normal:
glColor3f(1,1,1);
break;
case AT_Spellcard:
glColor3f(1,0.8,0.8);
break;
case AT_SurvivalSpell:
glColor3f(1,0.5,0.5);
default:
break; // never happens
}
glBegin(GL_QUADS);
glVertex3f(-boss->attacks[i].dmglimit, 0, 0);
glVertex3f(-boss->attacks[i].dmglimit, 2, 0);
glVertex3f(-boss->dmg, 2, 0);
glVertex3f(-boss->dmg, 0, 0);
glEnd();
}
glPopMatrix();
}
glColor3f(1,1,1);
glPopMatrix();
glColor3f(1,1,1);
}
void process_boss(Boss *boss) {
if(boss->current && boss->current->waypoints) {
if(boss->current) {
int time = global.frames - boss->current->starttime;
struct Waypoint *wps = boss->current->waypoints;
int *i = &boss->current->wp;
if(time < 0) {
boss->pos = boss->pos0 + (wps[0].pos - boss->pos0)/ATTACK_START_DELAY * (ATTACK_START_DELAY + time);
} else {
int wtime = time % wps[boss->current->wpcount-1].time + 1;
int next = *i + 1;
if(next >= boss->current->wpcount)
next = 0;
boss->pos = wps[*i].pos + (wps[next].pos - wps[*i].pos)/(wps[*i].time - boss->time0) * (wtime - boss->time0);
if(wtime >= wps[*i].time) {
boss->pos0 = wps[next].pos;
boss->time0 = wps[*i].time % wps[boss->current->wpcount-1].time;
*i = next;
}
boss->current->rule(boss, time % wps[boss->current->wpcount-1].time);
}
if(time > boss->current->timeout)
boss->current->rule(boss, time);
if(boss->current->type != AT_Move && boss->dmg >= boss->current->dmglimit)
time = boss->current->timeout + 1;
if(time > boss->current->timeout) {
boss->dmg = boss->current->dmglimit + 1;
if(boss->dmg >= boss->current->dmglimit) {
boss->pos0 = boss->pos;
boss->time0 = 0;
boss->current++;
if(boss->current - boss->attacks < boss->acount)
start_attack(boss, boss->current);
else
boss->current = NULL;
}
}
}
}
void boss_death(Boss **boss) {
@ -152,7 +126,6 @@ void free_boss(Boss *boss) {
void free_attack(Attack *a) {
free(a->name);
free(a->waypoints);
}
void start_attack(Boss *b, Attack *a) {
@ -184,16 +157,5 @@ Attack *boss_add_attack(Boss *boss, AttackType type, char *name, float timeout,
a->starttime = global.frames;
a->wpcount = 0;
a->waypoints = NULL;
a->wp = 0;
return a;
}
void boss_add_waypoint(Attack *attack, complex pos, int time) {
attack->waypoints = realloc(attack->waypoints, (sizeof(struct Waypoint))*(++attack->wpcount));
struct Waypoint *w = &attack->waypoints[attack->wpcount - 1];
w->pos = pos;
w->time = time;
}

View file

@ -17,6 +17,7 @@ typedef void (*BossRule)(struct Boss*, int time);
typedef enum AttackType {
AT_Normal,
AT_Move,
AT_Spellcard,
AT_SurvivalSpell
} AttackType;
@ -31,14 +32,7 @@ typedef struct Attack {
int timeout;
int dmglimit;
struct Waypoint {
complex pos;
int time;
}* waypoints;
int wpcount;
int wp;
BossRule rule;
BossRule draw_rule;
} Attack;
@ -48,8 +42,6 @@ typedef struct Boss {
Attack *current;
complex pos;
complex pos0;
int time0;
char *name;
@ -71,7 +63,7 @@ void free_attack(Attack *a);
void start_attack(Boss *b, Attack *a);
Attack *boss_add_attack(Boss *boss, AttackType type, char *name, float timeout, int hp, BossRule rule, BossRule draw_rule);
void boss_add_waypoint(Attack *attack, complex pos, int time);
// void boss_add_waypoint(Attack *attack, complex pos, int time);
void boss_death(Boss **boss);

View file

@ -92,7 +92,7 @@ void stage_input() {
}
void draw_hud() {
draw_texture(SCREEN_W/2, SCREEN_H/2, "hud");
draw_texture(SCREEN_W/2.0, SCREEN_H/2.0, "hud");
char buf[16];
int i;
@ -121,7 +121,7 @@ void draw_hud() {
void stage_draw() {
set_ortho();
glPushMatrix();
glTranslatef(VIEWPORT_X,VIEWPORT_Y,0);
@ -169,21 +169,28 @@ void stage_draw() {
}
void apply_bg_shaders() {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, resources.fsec.fbo);
if(global.boss && global.boss->current && global.boss->current->draw_rule) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, resources.fbg.fbo);
global.boss->current->draw_rule(global.boss, global.frames - global.boss->current->starttime);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, resources.fsec.fbo);
if(global.boss) { // Boss background shader
GLuint shader = get_shader("boss_zoom");
glUseProgram(shader);
complex fpos = VIEWPORT_H*I + conj(global.boss->pos) + (VIEWPORT_X + VIEWPORT_Y*I)*0.5;
complex pos = fpos + 15*cexp(I*global.frames/5.0);
complex fpos = VIEWPORT_H*I + conj(global.boss->pos) + (VIEWPORT_X + VIEWPORT_Y*I);
complex pos = fpos + 15*cexp(I*global.frames/4.5);
glUniform2f(glGetUniformLocation(shader, "blur_orig"),
creal(pos)/resources.fbg.nw, cimag(pos)/resources.fbg.nh);
glUniform2f(glGetUniformLocation(shader, "fix_orig"),
creal(fpos)/resources.fbg.nw, cimag(fpos)/resources.fbg.nh);
glUniform1f(glGetUniformLocation(shader, "blur_rad"), 0.2+0.05*sin(global.frames/10.0));
glUniform1f(glGetUniformLocation(shader, "rad"), 0.3);
glUniform1f(glGetUniformLocation(shader, "blur_rad"), 0.2+0.025*sin(global.frames/15.0));
glUniform1f(glGetUniformLocation(shader, "rad"), 0.24);
glUniform1f(glGetUniformLocation(shader, "ratio"), (float)resources.fbg.nh/resources.fbg.nw);
}
@ -206,14 +213,6 @@ void apply_bg_shaders() {
fade_out(fade*0.6);
glPopMatrix();
}
if(global.boss && global.boss->current && global.boss->current->draw_rule) {
glPushMatrix();
glTranslatef(-VIEWPORT_X,0,0); // Some transformation for convenience. Don't ask why. it's because of rtt.
glScalef((VIEWPORT_W+VIEWPORT_X)/(float)VIEWPORT_W, (VIEWPORT_H+VIEWPORT_Y)/(float)VIEWPORT_H, 1);
global.boss->current->draw_rule(global.boss, global.frames - global.boss->current->starttime);
glPopMatrix();
}
}
void stage_logic() {
@ -230,7 +229,7 @@ void stage_logic() {
process_lasers();
process_projectiles(&global.particles, False);
if(global.boss) {
if(global.boss && !global.dialog) {
process_boss(global.boss);
if(global.boss->dmg > global.boss->attacks[global.boss->acount-1].dmglimit)
boss_death(&global.boss);
@ -273,7 +272,8 @@ void stage_loop(StageRule start, StageRule end, StageRule draw, StageRule event)
start();
while(global.game_over <= 0) {
event();
if(!global.boss && !global.dialog)
event();
stage_input();
stage_logic();

View file

@ -17,13 +17,15 @@
*
*/
#define TIMER(ptr) int *__timep = ptr; int _i = 0, _ni = 0;
#define AT(t) _i = _ni= _i; if(*__timep == t)
#define TIMER(ptr) int *__timep = ptr; int _i = 0, _ni = 0; _i = _ni = _i;
#define AT(t) if(*__timep == t)
#define FROM_TO(start,end,step) _ni = _ni; _i = (*__timep - (start))/(step); if(*__timep >= (start) && *__timep <= (end) && !((*__timep - (start)) % (step)))
#define FROM_TO_INT(start, end, step, dur, istep) \
_i = (*__timep - (start))/(step+dur); _ni = ((*__timep - (start)) % (step+dur))/istep; \
if(*__timep >= (start) && *__timep <= (end) && (*__timep - (start)) % ((dur) + (step)) <= dur && !((*__timep - (start)) % (istep)))
#define GO_AT(obj, start, end, vel) if(*__timep >= (start) && *__timep <= (end)) (obj)->pos += (vel);
#define GO_TO(obj, p, f) (obj)->pos += (f)*((p) - (obj)->pos);
typedef void (*StageRule)(void);

View file

@ -76,38 +76,73 @@ void stage0_draw() {
glViewport(0,0,SCREEN_W,SCREEN_H);
}
/*
void cirno_intro(Boss *c, int time) {
if(time == EVENT_BIRTH) {
boss_add_waypoint(c->current, VIEWPORT_W/2-100 + 30I, 100);
boss_add_waypoint(c->current, VIEWPORT_W/2+100 + 30I, 200);
return;
TIMER(&time);
GO_TO(c, VIEWPORT_W/2.0 + 100I, 0.03);
AT(220)
global.dialog = test_dialog();
}
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] = 2*cexp(I*rand());
}
if(t > 240)
linear(p, t-240);
return 1;
}
void cirno_perfect_freeze(Boss *c, int time) {
if(time == EVENT_BIRTH) {
boss_add_waypoint(c->current, VIEWPORT_W/2 + 100I, 110);
boss_add_waypoint(c->current, VIEWPORT_W/2 + 100I, 160);
boss_add_waypoint(c->current, VIEWPORT_W/2+90 + 130I, 190);
boss_add_waypoint(c->current, VIEWPORT_W/2-90 + 130I, 320);
return;
int t = time % 320;
TIMER(&t);
FROM_TO(-40, 0, 1)
GO_TO(c, VIEWPORT_W/2.0 + 100I, 0.04);
FROM_TO(20,80,1) {
float r = frand();
float g = frand();
float b = frand();
create_projectile2c("ball", c->pos, rgb(r, g, b), cirno_pfreeze_frogs, 4*cexp(I*rand()), add_ref(global.boss));
}
if(time > 10 && time < 80) {
float r = rand()/(float)RAND_MAX;
float g = rand()/(float)RAND_MAX;
float b = rand()/(float)RAND_MAX;
create_projectile2c("ball", c->pos, rgb(r, g, b), cirno_pfreeze_frogs, 4*cexp(I*rand()), add_ref(&global.boss))->parent=&global.boss;
GO_AT(c, 110, 150, 2 + 1I);
FROM_TO(160, 220, 6) {
create_projectile2c("rice", c->pos + 60, rgb(0.3, 0.4, 0.9), asymptotic, 4*cexp(I*(carg(global.plr.pos - c->pos) + frand() - 0.5)), 2.5);
create_projectile2c("rice", c->pos - 60, rgb(0.3, 0.4, 0.9), asymptotic, 4*cexp(I*(carg(global.plr.pos - c->pos) + frand() - 0.5)), 2.5);
}
if(time > 160 && time < 220 && !(time % 7)) {
create_projectile1c("rice", c->pos + 60, rgb(0.3, 0.4, 0.9), linear, 5*cexp(I*carg(global.plr.pos - c->pos)));
create_projectile1c("rice", c->pos - 60, rgb(0.3, 0.4, 0.9), linear, 5*cexp(I*carg(global.plr.pos - c->pos)));
}
GO_AT(c, 160, 220, -3)
FROM_TO(280, 320, 1)
GO_TO(c, VIEWPORT_W/2.0 + 100I, 0.04);
}
void cirno_pfreeze_bg(Boss *c, int time) {
@ -120,18 +155,16 @@ void cirno_pfreeze_bg(Boss *c, int time) {
glColor4f(1,1,1,1);
}
Boss *create_cirno() {
Boss* cirno = create_boss("Cirno", "cirno", VIEWPORT_W/2 + 30I);
boss_add_attack(cirno, AT_Normal, "Introduction", 10, 100, cirno_intro, NULL);
boss_add_attack(cirno, AT_Spellcard, "Freeze Sign ~ Perfect Freeze", 20, 300, cirno_perfect_freeze, cirno_pfreeze_bg);
Boss* cirno = create_boss("Cirno", "cirno", VIEWPORT_W + 30 + 30I);
boss_add_attack(cirno, AT_Move, "Introduction", 4, 0, cirno_intro, NULL);
boss_add_attack(cirno, AT_Spellcard, "Freeze Sign ~ Perfect Freeze", 22, 100, cirno_perfect_freeze, cirno_pfreeze_bg);
start_attack(cirno, cirno->attacks);
return cirno;
}*/
}
int stage0_enemy0(Enemy *e, int time) {
int stage0_burst(Enemy *e, int time) {
TIMER(&time);
AT(EVENT_DEATH) {
spawn_items(e->pos, 3,0,0,0);
@ -153,12 +186,12 @@ int stage0_enemy0(Enemy *e, int time) {
}
FROM_TO(70, 900, 1)
e->pos = e->pos0 + (0.04*e->args[0] + 0.06I)*_i*_i;
e->pos = e->pos0 + (0.04*e->args[0])*_i*_i;
return 1;
}
int stage0_enemy1(Enemy *e, int time) {
int stage0_circletoss(Enemy *e, int time) {
TIMER(&time);
AT(EVENT_DEATH) {
spawn_items(e->pos, 2,2,0,0);
@ -166,17 +199,14 @@ int stage0_enemy1(Enemy *e, int time) {
}
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);
}
FROM_TO(90, 500, 20);
if(!(_i % 9))
create_projectile2c("thickrice", e->pos, rgb(0.2, 0.4, 0.8), asymptotic, (1+frand()*3)*cexp(I*carg(global.plr.pos - e->pos)), 3);
FROM_TO_INT(90,500,150,15,1)
create_projectile2c("thickrice", e->pos, rgb(0.2, 0.4, 0.8), asymptotic, (1+frand()*2)*cexp(I*carg(global.plr.pos - e->pos)), 3);
FROM_TO(500, 900, 1)
e->args[0] += 0.03*e->args[1] - 0.04I;
@ -184,7 +214,7 @@ int stage0_enemy1(Enemy *e, int time) {
return 1;
}
int stage0_enemy2(Enemy *e, int time) {
int stage0_sinepass(Enemy *e, int time) {
TIMER(&time);
AT(EVENT_DEATH) {
spawn_items(e->pos, frand()>0.5, frand()>0.2,0,0);
@ -197,7 +227,7 @@ int stage0_enemy2(Enemy *e, int time) {
return 1;
}
int stage0_enemy3(Enemy *e, int t) {
int stage0_drop(Enemy *e, int t) {
TIMER(&t);
AT(EVENT_DEATH) {
spawn_items(e->pos, 2,1,0,0);
@ -215,7 +245,7 @@ int stage0_enemy3(Enemy *e, int t) {
return 1;
}
int stage0_enemy4(Enemy *e, int t) {
int stage0_circle(Enemy *e, int t) {
TIMER(&t);
AT(EVENT_DEATH) {
spawn_items(e->pos, 3,4,0,0);
@ -234,7 +264,7 @@ int stage0_enemy4(Enemy *e, int t) {
return 1;
}
int stage0_enemy5(Enemy *e, int t) {
int stage0_multiburst(Enemy *e, int t) {
TIMER(&t);
AT(EVENT_DEATH) {
spawn_items(e->pos, 3,4,0,0);
@ -257,47 +287,60 @@ int stage0_enemy5(Enemy *e, int t) {
return 1;
}
void stage0_events() {
if(global.dialog)
return;
TIMER(&global.timer);
/*
// opening. projectile bursts
FROM_TO(100, 160, 25) {
create_enemy1c(VIEWPORT_W/2 + 70, 5, Fairy, stage0_enemy0, 1);
create_enemy1c(VIEWPORT_W/2 - 70, 5, Fairy, stage0_enemy0, -1);
create_enemy1c(VIEWPORT_W/2 + 70, 5, Fairy, stage0_burst, 1 + 0.6I);
create_enemy1c(VIEWPORT_W/2 - 70, 5, Fairy, stage0_burst, -1 + 0.6I);
}
// more bursts. fairies move / \ like
FROM_TO(240, 300, 30) {
create_enemy1c(70 + _i*40, 6, Fairy, stage0_enemy0, -1);
create_enemy1c(VIEWPORT_W - (70 + _i*40), 6, Fairy, stage0_enemy0, 1);
create_enemy1c(70 + _i*40, 6, Fairy, stage0_burst, -1 + 0.6I);
create_enemy1c(VIEWPORT_W - (70 + _i*40), 6, Fairy, stage0_burst, 1 + 0.6I);
}
FROM_TO(400, 460, 50)
create_enemy2c(VIEWPORT_W*_i + VIEWPORT_H/3*I, 20, Fairy, stage0_enemy1, 2-4*_i-0.3I, 1-2*_i);
// big fairies, circle + projectile toss
FROM_TO(400, 460, 50)
create_enemy2c(VIEWPORT_W*_i + VIEWPORT_H/3*I, 20, Fairy, stage0_circletoss, 2-4*_i-0.3I, 1-2*_i);
// swirl, sine pass
FROM_TO(380, 1000, 20)
create_enemy2c(VIEWPORT_W*(_i&1) + frand()*100I + 70I, 2, Swirl, stage0_enemy2, 3.5*(1-2*(_i&1)), frand()*7I);
create_enemy2c(VIEWPORT_W*(_i&1) + frand()*100I + 70I, 2, Swirl, stage0_sinepass, 3.5*(1-2*(_i&1)), frand()*7I);
// swirl, drops
FROM_TO(1100, 1600, 20)
create_enemy2c(VIEWPORT_W/3, 1, Swirl, stage0_enemy3, 4I, 0.06);
create_enemy2c(VIEWPORT_W/3, 1, Swirl, stage0_drop, 4I, 0.06);
FROM_TO(1500, 2000, 20)
create_enemy2c(VIEWPORT_W+200I, 2, Swirl, stage0_enemy3, -2, -0.04-0.03I);
create_enemy2c(VIEWPORT_W+200I, 2, Swirl, stage0_drop, -2, -0.04-0.03I);
// bursts
FROM_TO(1250, 1800, 60)
create_enemy1c(VIEWPORT_W/2 + frand()*500-250 , 3, Fairy, stage0_enemy0, frand()*2-1);
FROM_TO(1700, 2700, 300)
create_enemy2c(VIEWPORT_W/2, 30, Fairy, stage0_enemy4, VIEWPORT_W/4 + VIEWPORT_W/2*frand()+200I, 3-6*(frand()>0.5)+frand()*2I);
create_enemy1c(VIEWPORT_W/2 + frand()*500-250 , 3, Fairy, stage0_burst, frand()*2-1);
// circle - multi burst combo
FROM_TO(1700, 2300, 300)
create_enemy2c(VIEWPORT_W/2, 30, Fairy, stage0_circle, VIEWPORT_W/4 + VIEWPORT_W/2*frand()+200I, 3-6*(frand()>0.5)+frand()*2I);
FROM_TO(2000,2800, 200) {
FROM_TO(2000, 2500, 200) {
int i;
for(i = 0; i < 5; i++)
create_enemy1c(VIEWPORT_W/4 + VIEWPORT_H/10*i, 5, Fairy, stage0_enemy5, i - 2.5);
create_enemy1c(VIEWPORT_W/4 + VIEWPORT_H/10*i, 5, Fairy, stage0_multiburst, i - 2.5);
}
*/
AT(200) {
global.boss = create_cirno();
// global.dialog = test_dialog();
}
// if(!(global.timer % 100))
// create_laser(LaserCurve, 300, 300, 60, 500, ((ColorA){0.6,0.6,1,0.4}), lolsin, 0);
}
void stage0_start() {