implement some infrastructure for coroutine bosses
This commit is contained in:
parent
4dc86d976c
commit
e14eb36e5c
5 changed files with 88 additions and 21 deletions
43
src/boss.c
43
src/boss.c
|
@ -952,12 +952,18 @@ static int attack_end_delay(Boss *boss) {
|
|||
return delay;
|
||||
}
|
||||
|
||||
static void boss_call_rule(Boss *boss, int t) {
|
||||
if(boss->current->rule) {
|
||||
boss->current->rule(boss, t);
|
||||
}
|
||||
}
|
||||
|
||||
void boss_finish_current_attack(Boss *boss) {
|
||||
AttackType t = boss->current->type;
|
||||
|
||||
boss->current->hp = 0;
|
||||
boss->current->finished = true;
|
||||
boss->current->rule(boss, EVENT_DEATH);
|
||||
boss_call_rule(boss, EVENT_DEATH);
|
||||
|
||||
aniplayer_soft_switch(&boss->ani,"main",0);
|
||||
|
||||
|
@ -992,6 +998,8 @@ void boss_finish_current_attack(Boss *boss) {
|
|||
|
||||
boss->current->endtime = global.frames + attack_end_delay(boss);
|
||||
boss->current->endtime_undelayed = global.frames;
|
||||
|
||||
coevent_signal_once(&boss->current->events.finished);
|
||||
}
|
||||
|
||||
void process_boss(Boss **pboss) {
|
||||
|
@ -1024,6 +1032,12 @@ void process_boss(Boss **pboss) {
|
|||
bool extra = boss->current->type == AT_ExtraSpell;
|
||||
bool over = boss->current->finished && global.frames >= boss->current->endtime;
|
||||
|
||||
if(time == 0) {
|
||||
coevent_signal_once(&boss->current->events.started);
|
||||
}
|
||||
|
||||
move_update(&boss->pos, &boss->move);
|
||||
|
||||
if(!boss->current->endtime) {
|
||||
int remaining = boss->current->timeout - time;
|
||||
|
||||
|
@ -1031,7 +1045,7 @@ void process_boss(Boss **pboss) {
|
|||
play_sound(remaining <= 6*FPS ? "timeout2" : "timeout1");
|
||||
}
|
||||
|
||||
boss->current->rule(boss, time);
|
||||
boss_call_rule(boss, time);
|
||||
}
|
||||
|
||||
if(extra) {
|
||||
|
@ -1179,6 +1193,11 @@ void process_boss(Boss **pboss) {
|
|||
}
|
||||
|
||||
log_debug("Current attack [%s] is over", boss->current->name);
|
||||
COEVENT_CANCEL_ARRAY(boss->current->events);
|
||||
|
||||
if(ATTACK_IS_SPELL(boss->current->type)) {
|
||||
boss_reset_motion(boss);
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
if(boss->current == boss->attacks + boss->acount - 1) {
|
||||
|
@ -1193,7 +1212,12 @@ void process_boss(Boss **pboss) {
|
|||
|
||||
if(boss->current->type == AT_Immediate) {
|
||||
boss->current->starttime = global.frames;
|
||||
boss->current->rule(boss, EVENT_BIRTH);
|
||||
boss_call_rule(boss, EVENT_BIRTH);
|
||||
|
||||
coevent_signal_once(&boss->current->events.initiated);
|
||||
coevent_signal_once(&boss->current->events.started);
|
||||
coevent_signal_once(&boss->current->events.finished);
|
||||
COEVENT_CANCEL_ARRAY(boss->current->events);
|
||||
|
||||
if(dialog_is_active(global.dialog)) {
|
||||
break;
|
||||
|
@ -1203,6 +1227,7 @@ void process_boss(Boss **pboss) {
|
|||
}
|
||||
|
||||
if(boss_should_skip_attack(boss, boss->current)) {
|
||||
COEVENT_CANCEL_ARRAY(boss->current->events);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1212,6 +1237,14 @@ void process_boss(Boss **pboss) {
|
|||
}
|
||||
}
|
||||
|
||||
void boss_reset_motion(Boss *boss) {
|
||||
boss->move.acceleration = 0;
|
||||
boss->move.attraction = 0;
|
||||
boss->move.attraction_max_speed = 0;
|
||||
boss->move.attraction_point = 0;
|
||||
boss->move.retention = 0.8;
|
||||
}
|
||||
|
||||
static void boss_death_effect_draw_overlay(Projectile *p, int t) {
|
||||
FBPair *framebuffers = stage_get_fbpair(FBPAIR_FG);
|
||||
r_framebuffer(framebuffers->front);
|
||||
|
@ -1266,6 +1299,7 @@ void boss_death(Boss **boss) {
|
|||
}
|
||||
|
||||
static void free_attack(Attack *a) {
|
||||
COEVENT_CANCEL_ARRAY(a->events);
|
||||
free(a->name);
|
||||
}
|
||||
|
||||
|
@ -1323,6 +1357,7 @@ void boss_start_attack(Boss *b, Attack *a) {
|
|||
}
|
||||
|
||||
stage_clear_hazards(CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_FORCE);
|
||||
coevent_signal_once(&a->events.initiated);
|
||||
}
|
||||
|
||||
Attack* boss_add_attack(Boss *boss, AttackType type, char *name, float timeout, int hp, BossRule rule, BossRule draw_rule) {
|
||||
|
@ -1344,6 +1379,8 @@ Attack* boss_add_attack(Boss *boss, AttackType type, char *name, float timeout,
|
|||
|
||||
a->starttime = global.frames;
|
||||
|
||||
COEVENT_INIT_ARRAY(a->events);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
|
|
13
src/boss.h
13
src/boss.h
|
@ -17,6 +17,7 @@
|
|||
#include "color.h"
|
||||
#include "projectile.h"
|
||||
#include "entity.h"
|
||||
#include "coroutine.h"
|
||||
|
||||
#define BOSS_HURT_RADIUS 16
|
||||
|
||||
|
@ -100,6 +101,12 @@ typedef struct Attack {
|
|||
BossRule rule;
|
||||
BossRule draw_rule;
|
||||
|
||||
COEVENTS_ARRAY(
|
||||
initiated,
|
||||
started,
|
||||
finished
|
||||
) events;
|
||||
|
||||
AttackInfo *info; // NULL for attacks created directly through boss_add_attack
|
||||
} Attack;
|
||||
|
||||
|
@ -127,6 +134,8 @@ typedef struct Boss {
|
|||
|
||||
BossRule global_rule;
|
||||
|
||||
MoveParams move;
|
||||
|
||||
// These are publicly accessible damage multipliers *you* can use to buff your spells.
|
||||
// Just change the numbers. global.shake_view style. 1.0 is the default.
|
||||
// If a new attack starts, they are reset. Nothing can go wrong!
|
||||
|
@ -147,6 +156,8 @@ typedef struct Boss {
|
|||
float plrproximity_opacity;
|
||||
float attack_timer;
|
||||
} hud;
|
||||
|
||||
COEVENTS_ARRAY(defeated);
|
||||
} Boss;
|
||||
|
||||
Boss* create_boss(char *name, char *ani, cmplx pos) attr_nonnull(1, 2) attr_returns_nonnull;
|
||||
|
@ -175,6 +186,8 @@ bool boss_is_vulnerable(Boss *boss) attr_nonnull(1);
|
|||
|
||||
void boss_death(Boss **boss) attr_nonnull(1);
|
||||
|
||||
void boss_reset_motion(Boss *boss) attr_nonnull(1);
|
||||
|
||||
void boss_preload(void);
|
||||
|
||||
#define BOSS_DEFAULT_SPAWN_POS (VIEWPORT_W * 0.5 - I * VIEWPORT_H * 0.5)
|
||||
|
|
|
@ -285,6 +285,12 @@ void coevent_cancel(CoEvent* evt) {
|
|||
evt->subscribers = NULL;
|
||||
}
|
||||
|
||||
void _coevent_array_action(uint num, CoEvent *events, void (*func)(CoEvent*)) {
|
||||
for(uint i = 0; i < num; ++i) {
|
||||
func(events + i);
|
||||
}
|
||||
}
|
||||
|
||||
void cosched_init(CoSched *sched) {
|
||||
memset(sched, 0, sizeof(*sched));
|
||||
}
|
||||
|
|
|
@ -69,6 +69,14 @@ void coevent_signal(CoEvent *evt);
|
|||
void coevent_signal_once(CoEvent *evt);
|
||||
void coevent_cancel(CoEvent *evt);
|
||||
|
||||
void _coevent_array_action(uint num, CoEvent *events, void (*func)(CoEvent*));
|
||||
|
||||
#define COEVENTS_ARRAY(...) union { CoEvent _first_event_; struct { CoEvent __VA_ARGS__; }; }
|
||||
|
||||
#define COEVENT_ARRAY_ACTION(func, array) (_coevent_array_action(sizeof(array)/sizeof(CoEvent), &((array)._first_event_), func))
|
||||
#define COEVENT_INIT_ARRAY(array) COEVENT_ARRAY_ACTION(coevent_init, array)
|
||||
#define COEVENT_CANCEL_ARRAY(array) COEVENT_ARRAY_ACTION(coevent_cancel, array)
|
||||
|
||||
void cosched_init(CoSched *sched);
|
||||
CoTask *cosched_new_task(CoSched *sched, CoTaskFunc func, void *arg); // creates and runs the task, schedules it for resume on cosched_run_tasks if it's still alive
|
||||
uint cosched_run_tasks(CoSched *sched); // returns number of tasks ran
|
||||
|
|
|
@ -1619,10 +1619,21 @@ TASK(multiburst_fairies_1, NO_ARGS) {
|
|||
}
|
||||
}
|
||||
|
||||
TASK(spawn_midboss, NO_ARGS) {
|
||||
Boss *cirno = stage1_spawn_cirno(VIEWPORT_W + 220 + 30.0*I);
|
||||
|
||||
boss_add_attack(cirno, AT_Move, "Introduction", 2, 0, cirno_intro, NULL);
|
||||
boss_add_attack(cirno, AT_Normal, "Icy Storm", 20, 24000, cirno_icy, NULL);
|
||||
boss_add_attack_from_info(cirno, &stage1_spells.mid.perfect_freeze, false);
|
||||
boss_add_attack(cirno, AT_Move, "Flee", 5, 0, cirno_mid_flee, NULL);
|
||||
|
||||
boss_start_attack(cirno, cirno->attacks);
|
||||
global.boss = cirno;
|
||||
}
|
||||
|
||||
TASK(stage_timeline, NO_ARGS) {
|
||||
stage_start_bgm("stage1");
|
||||
stage_set_voltage_thresholds(50, 125, 300, 600);
|
||||
YIELD;
|
||||
|
||||
/*
|
||||
for(;;) {
|
||||
|
@ -1642,23 +1653,15 @@ TASK(stage_timeline, NO_ARGS) {
|
|||
return;
|
||||
*/
|
||||
|
||||
INVOKE_TASK(drop_swirls);
|
||||
stage_wait(100);
|
||||
INVOKE_TASK(burst_fairies_1);
|
||||
stage_wait(140);
|
||||
INVOKE_TASK(burst_fairies_2);
|
||||
stage_wait(200);
|
||||
INVOKE_TASK(sinepass_swirls, 180, 100, 1);
|
||||
stage_wait(40);
|
||||
INVOKE_TASK(circletoss_fairies_1);
|
||||
stage_wait(180);
|
||||
INVOKE_TASK(circle_fairies_1);
|
||||
stage_wait(240);
|
||||
INVOKE_TASK(schedule_swirls);
|
||||
stage_wait(600);
|
||||
INVOKE_TASK(burst_fairies_3);
|
||||
stage_wait(700);
|
||||
INVOKE_TASK(multiburst_fairies_1);
|
||||
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(2700, spawn_midboss);
|
||||
}
|
||||
|
||||
void stage1_events(void) {
|
||||
|
|
Loading…
Reference in a new issue