implement some infrastructure for coroutine bosses

This commit is contained in:
Andrei Alexeyev 2019-08-31 00:59:41 +03:00
parent 4dc86d976c
commit e14eb36e5c
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
5 changed files with 88 additions and 21 deletions

View file

@ -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;
}

View file

@ -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)

View file

@ -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));
}

View file

@ -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

View file

@ -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) {