further task-boss infrastructure WIP

This commit is contained in:
Andrei Alexeyev 2019-09-01 03:23:36 +03:00
parent e14eb36e5c
commit 9498e64dd1
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
10 changed files with 237 additions and 67 deletions

View file

@ -1036,8 +1036,6 @@ void process_boss(Boss **pboss) {
coevent_signal_once(&boss->current->events.started);
}
move_update(&boss->pos, &boss->move);
if(!boss->current->endtime) {
int remaining = boss->current->timeout - time;
@ -1048,6 +1046,8 @@ void process_boss(Boss **pboss) {
boss_call_rule(boss, time);
}
move_update(&boss->pos, &boss->move);
if(extra) {
float base = 0.2;
float ampl = 0.2;
@ -1193,11 +1193,10 @@ 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);
}
boss_reset_motion(boss);
coevent_signal_once(&boss->current->events.completed);
COEVENT_CANCEL_ARRAY(boss->current->events);
for(;;) {
if(boss->current == boss->attacks + boss->acount - 1) {
@ -1217,6 +1216,7 @@ void process_boss(Boss **pboss) {
coevent_signal_once(&boss->current->events.initiated);
coevent_signal_once(&boss->current->events.started);
coevent_signal_once(&boss->current->events.finished);
coevent_signal_once(&boss->current->events.completed);
COEVENT_CANCEL_ARRAY(boss->current->events);
if(dialog_is_active(global.dialog)) {
@ -1312,11 +1312,11 @@ void free_boss(Boss *boss) {
boss_set_portrait(boss, NULL, NULL);
aniplayer_free(&boss->ani);
free(boss->name);
free(boss->attacks);
free(boss);
}
void boss_start_attack(Boss *b, Attack *a) {
b->current = a;
log_debug("%s", a->name);
StageInfo *i;
@ -1336,7 +1336,7 @@ void boss_start_attack(Boss *b, Attack *a) {
b->shot_damage_multiplier = 1.0;
a->starttime = global.frames + (a->type == AT_ExtraSpell? ATTACK_START_DELAY_EXTRA : ATTACK_START_DELAY);
a->rule(b, EVENT_BIRTH);
boss_call_rule(b, EVENT_BIRTH);
if(ATTACK_IS_SPELL(a->type)) {
play_sound(a->type == AT_ExtraSpell ? "charge_extra" : "charge_generic");
@ -1357,15 +1357,16 @@ void boss_start_attack(Boss *b, Attack *a) {
}
stage_clear_hazards(CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_FORCE);
boss_reset_motion(b);
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) {
boss->attacks = realloc(boss->attacks, sizeof(Attack)*(++boss->acount));
Attack *a = &boss->attacks[boss->acount-1];
memset(a, 0, sizeof(Attack));
Attack *boss_add_attack(Boss *boss, AttackType type, char *name, float timeout, int hp, BossRule rule, BossRule draw_rule) {
assert(boss->acount < BOSS_MAX_ATTACKS);
boss->current = &boss->attacks[0];
Attack *a = &boss->attacks[boss->acount];
boss->acount += 1;
memset(a, 0, sizeof(Attack));
a->type = type;
a->name = strdup(name);
@ -1384,6 +1385,32 @@ Attack* boss_add_attack(Boss *boss, AttackType type, char *name, float timeout,
return a;
}
TASK(attack_task_helper, {
BossAttackTask task;
BossAttackTaskArgs task_args;
}) {
// NOTE: We could do INVOKE_TASK_INDIRECT here, but that's a bit wasteful.
// A better idea than both that and this contraption would be to come up
// with an INVOKE_TASK_INDIRECT_WHEN.
ARGS.task._cotask_BossAttack_thunk(&ARGS.task_args);
}
static void setup_attack_task(Boss *boss, Attack *a, BossAttackTask task) {
INVOKE_TASK_WHEN(&a->events.initiated, attack_task_helper,
.task = task,
.task_args = {
.boss = ENT_BOX(boss),
.attack = a
}
);
}
Attack *boss_add_attack_task(Boss *boss, AttackType type, char *name, float timeout, int hp, BossAttackTask task, BossRule draw_rule) {
Attack *a = boss_add_attack(boss, type, name, timeout, hp, NULL, draw_rule);
setup_attack_task(boss, a, task);
return a;
}
void boss_set_attack_bonus(Attack *a, int rank) {
if(!ATTACK_IS_SPELL(a->type)) {
return;
@ -1434,6 +1461,11 @@ Attack* boss_add_attack_from_info(Boss *boss, AttackInfo *info, char move) {
Attack *a = boss_add_attack(boss, info->type, info->name, info->timeout, info->hp, info->rule, info->draw_rule);
a->info = info;
boss_set_attack_bonus(a, info->bonus_rank);
if(info->task._cotask_BossAttack_thunk) {
setup_attack_task(boss, a, info->task);
}
return a;
}

View file

@ -20,6 +20,7 @@
#include "coroutine.h"
#define BOSS_HURT_RADIUS 16
#define BOSS_MAX_ATTACKS 24
enum {
ATTACK_START_DELAY = 60,
@ -33,7 +34,9 @@ enum {
BOSS_DEATH_DELAY = 120,
};
struct Boss;
typedef struct Boss Boss;
typedef struct Attack Attack;
typedef struct AttackInfo AttackInfo;
typedef void (*BossRule)(struct Boss*, int time) attr_nonnull(1);
@ -48,7 +51,15 @@ typedef enum AttackType {
#define ATTACK_IS_SPELL(a) ((a) == AT_Spellcard || (a) == AT_SurvivalSpell || (a) == AT_ExtraSpell)
typedef struct AttackInfo {
DEFINE_TASK_INTERFACE(BossAttack, {
BoxedBoss boss;
Attack *attack;
});
typedef TASK_INDIRECT_TYPE(BossAttack) BossAttackTask;
typedef TASK_IFACE_ARGS_TYPE(BossAttack) BossAttackTaskArgs;
struct AttackInfo {
/*
HOW TO SET UP THE IDMAP FOR DUMMIES
@ -79,9 +90,12 @@ typedef struct AttackInfo {
cmplx pos_dest;
int bonus_rank;
} AttackInfo;
typedef struct Attack {
// TODO: when we no longer need rule, this shall take its place
TASK_INDIRECT_TYPE(BossAttack) task;
};
struct Attack {
char *name;
AttackType type;
@ -102,19 +116,20 @@ typedef struct Attack {
BossRule draw_rule;
COEVENTS_ARRAY(
initiated,
started,
finished
initiated, // before start delay
started, // after start delay
finished, // before end delay
completed // after end delay
) events;
AttackInfo *info; // NULL for attacks created directly through boss_add_attack
} Attack;
};
typedef struct Boss {
ENTITY_INTERFACE_NAMED(struct Boss, ent);
struct Boss {
ENTITY_INTERFACE_NAMED(Boss, ent);
cmplx pos;
Attack *attacks;
Attack attacks[BOSS_MAX_ATTACKS];
Attack *current;
char *name;
@ -158,7 +173,7 @@ typedef struct Boss {
} hud;
COEVENTS_ARRAY(defeated);
} Boss;
};
Boss* create_boss(char *name, char *ani, cmplx pos) attr_nonnull(1, 2) attr_returns_nonnull;
void free_boss(Boss *boss) attr_nonnull(1);
@ -171,6 +186,8 @@ void draw_boss_fake_overlay(Boss *boss) attr_nonnull(1);
Attack* boss_add_attack(Boss *boss, AttackType type, char *name, float timeout, int hp, BossRule rule, BossRule draw_rule)
attr_nonnull(1) attr_returns_nonnull;
Attack *boss_add_attack_task(Boss *boss, AttackType type, char *name, float timeout, int hp, BossAttackTask task, BossRule draw_rule)
attr_nonnull(1) attr_returns_nonnull;
Attack* boss_add_attack_from_info(Boss *boss, AttackInfo *info, char move)
attr_nonnull(1, 2) attr_returns_nonnull;
void boss_set_attack_bonus(Attack *a, int rank) attr_nonnull(1);

View file

@ -20,3 +20,27 @@ DEFINE_EXTERN_TASK(common_drop_items) {
}
}
}
void common_move_loop(complex *restrict pos, MoveParams *restrict mp) {
for(;;) {
move_update(pos, mp);
YIELD;
}
}
DEFINE_EXTERN_TASK(common_move) {
if(ARGS.ent.ent) {
TASK_BIND(ARGS.ent);
}
MoveParams p = ARGS.move_params;
common_move_loop(ARGS.pos, &p);
}
DEFINE_EXTERN_TASK(common_move_ext) {
if(ARGS.ent.ent) {
TASK_BIND(ARGS.ent);
}
common_move_loop(ARGS.pos, ARGS.move_params);
}

View file

@ -13,10 +13,24 @@
#include "coroutine.h"
#include "item.h"
#include "move.h"
#include "entity.h"
DECLARE_EXTERN_TASK(
common_drop_items,
{ complex *pos; ItemCounts items; }
);
DECLARE_EXTERN_TASK(
common_move,
{ complex *pos; MoveParams move_params; BoxedEntity ent; }
);
DECLARE_EXTERN_TASK(
common_move_ext,
{ complex *pos; MoveParams *move_params; BoxedEntity ent; }
);
void common_move_loop(complex *restrict pos, MoveParams *restrict mp);
#endif // IGUARD_common_tasks_h

View file

@ -84,8 +84,7 @@ void cosched_finish(CoSched *sched);
INLINE void cosched_set_invoke_target(CoSched *sched) { _cosched_global = sched; }
#define TASK_ARGS_NAME(name) COARGS_##name
#define TASK_ARGS(name) struct TASK_ARGS_NAME(name)
#define TASK_ARGS_TYPE(name) COARGS_##name
#define TASK_ARGSDELAY_NAME(name) COARGSDELAY_##name
#define TASK_ARGSDELAY(name) struct TASK_ARGSDELAY_NAME(name)
@ -93,26 +92,38 @@ INLINE void cosched_set_invoke_target(CoSched *sched) { _cosched_global = sched;
#define TASK_ARGSCOND_NAME(name) COARGSCOND_##name
#define TASK_ARGSCOND(name) struct TASK_ARGSCOND_NAME(name)
#define TASK_IFACE_NAME(iface, suffix) COTASKIFACE_##iface##_##suffix
#define TASK_IFACE_ARGS_TYPE(iface) TASK_IFACE_NAME(iface, ARGS)
#define TASK_INDIRECT_TYPE(iface) TASK_IFACE_NAME(iface, HANDLE)
#define DEFINE_TASK_INTERFACE(iface, argstruct) \
typedef TASK_ARGS_STRUCT(argstruct) TASK_IFACE_ARGS_TYPE(iface); \
typedef struct { void *(*_cotask_##iface##_thunk)(void*); } TASK_INDIRECT_TYPE(iface) /* require semicolon */
#define TASK_INDIRECT_TYPE_ALIAS(task) TASK_IFACE_NAME(task, HANDLEALIAS)
#define ARGS (*_cotask_args)
#define NO_ARGS { char _dummy_0; }
#define ARGS_STRUCT(argstruct) { struct argstruct; char _dummy_1; }
#define TASK_ARGS_STRUCT(argstruct) struct { struct argstruct; char _dummy_1; }
#define TASK_COMMON_PRIVATE_DECLARATIONS(name) \
/* user-defined task body */ \
static void COTASK_##name(TASK_ARGS(name) *_cotask_args); \
static void COTASK_##name(TASK_ARGS_TYPE(name) *_cotask_args); \
/* called from the entry points before task body (inlined, hopefully) */ \
INLINE void COTASKPROLOGUE_##name(TASK_ARGS(name) *_cotask_args) /* require semicolon */ \
INLINE void COTASKPROLOGUE_##name(TASK_ARGS_TYPE(name) *_cotask_args) /* require semicolon */ \
#define TASK_COMMON_DECLARATIONS(name, argstruct, linkage) \
#define TASK_COMMON_DECLARATIONS(name, argstype, handletype, linkage) \
/* produce warning if the task is never used */ \
linkage char COTASK_UNUSED_CHECK_##name; \
/* type of indirect handle to a compatible task */ \
typedef handletype TASK_INDIRECT_TYPE_ALIAS(name); \
/* user-defined type of args struct */ \
struct TASK_ARGS_NAME(name) ARGS_STRUCT(argstruct); \
typedef argstype TASK_ARGS_TYPE(name); \
/* type of internal args struct for INVOKE_TASK_DELAYED */ \
struct TASK_ARGSDELAY_NAME(name) { TASK_ARGS(name) real_args; int delay; }; \
struct TASK_ARGSDELAY_NAME(name) { TASK_ARGS_TYPE(name) real_args; int delay; }; \
/* type of internal args struct for INVOKE_TASK_WHEN */ \
struct TASK_ARGSCOND_NAME(name) { TASK_ARGS(name) real_args; CoEvent *event; }; \
struct TASK_ARGSCOND_NAME(name) { TASK_ARGS_TYPE(name) real_args; CoEvent *event; }; \
/* task entry point for INVOKE_TASK */ \
attr_unused linkage void *COTASKTHUNK_##name(void *arg); \
/* task entry point for INVOKE_TASK_DELAYED */ \
@ -124,7 +135,7 @@ INLINE void cosched_set_invoke_target(CoSched *sched) { _cosched_global = sched;
/* task entry point for INVOKE_TASK */ \
attr_unused linkage void *COTASKTHUNK_##name(void *arg) { \
/* copy args to our coroutine stack so that they're valid after caller returns */ \
TASK_ARGS(name) args_copy = *(TASK_ARGS(name)*)arg; \
TASK_ARGS_TYPE(name) args_copy = *(TASK_ARGS_TYPE(name)*)arg; \
/* call prologue */ \
COTASKPROLOGUE_##name(&args_copy); \
/* call body */ \
@ -161,58 +172,71 @@ INLINE void cosched_set_invoke_target(CoSched *sched) { _cosched_global = sched;
}
#define TASK_COMMON_BEGIN_BODY_DEFINITION(name, linkage) \
linkage void COTASK_##name(TASK_ARGS(name) *_cotask_args)
linkage void COTASK_##name(TASK_ARGS_TYPE(name) *_cotask_args)
#define DECLARE_TASK_EXPLICIT_LINKAGE(name, argstruct, linkage) \
TASK_COMMON_DECLARATIONS(name, argstruct, linkage) /* require semicolon */
#define DECLARE_TASK_EXPLICIT(name, argstype, handletype, linkage) \
TASK_COMMON_DECLARATIONS(name, argstype, handletype, linkage) /* require semicolon */
#define DEFINE_TASK_EXPLICIT_LINKAGE(name, linkage) \
#define DEFINE_TASK_EXPLICIT(name, linkage) \
TASK_COMMON_PRIVATE_DECLARATIONS(name); \
TASK_COMMON_THUNK_DEFINITIONS(name, linkage) \
/* empty prologue */ \
INLINE void COTASKPROLOGUE_##name(TASK_ARGS(name) *_cotask_args) { } \
INLINE void COTASKPROLOGUE_##name(TASK_ARGS_TYPE(name) *_cotask_args) { } \
/* begin task body definition */ \
TASK_COMMON_BEGIN_BODY_DEFINITION(name, linkage)
/* declare a task with static linkage (needs to be defined later) */
#define DECLARE_TASK(name, argstruct) \
DECLARE_TASK_EXPLICIT_LINKAGE(name, argstruct, static) /* require semicolon */
DECLARE_TASK_EXPLICIT(name, TASK_ARGS_STRUCT(argstruct), void, static) /* require semicolon */
/* TODO document */
#define DECLARE_TASK_WITH_INTERFACE(name, iface) \
DECLARE_TASK_EXPLICIT(name, TASK_IFACE_ARGS_TYPE(iface), TASK_INDIRECT_TYPE(iface), static) /* require semicolon */
/* define a task with static linkage (needs to be declared first) */
#define DEFINE_TASK(name) \
DEFINE_TASK_EXPLICIT_LINKAGE(name, static)
DEFINE_TASK_EXPLICIT(name, static)
/* declare and define a task with static linkage */
#define TASK(name, argstruct) \
DECLARE_TASK(name, argstruct); \
DEFINE_TASK(name)
/* TODO document */
#define TASK_WITH_INTERFACE(name, iface) \
DECLARE_TASK_WITH_INTERFACE(name, iface); \
DEFINE_TASK(name)
/* declare a task with extern linkage (needs to be defined later) */
#define DECLARE_EXTERN_TASK(name, argstruct) \
DECLARE_TASK_EXPLICIT_LINKAGE(name, argstruct, extern) /* require semicolon */
DECLARE_TASK_EXPLICIT(name, TASK_ARGS_STRUCT(argstruct), void, extern) /* require semicolon */
/* TODO document */
#define DECLARE_EXTERN_TASK_WITH_INTERFACE(name, iface) \
DECLARE_TASK_EXPLICIT(name, TASK_IFACE_ARGS_TYPE(iface), TASK_INDIRECT_TYPE(iface), extern) /* require semicolon */
/* define a task with extern linkage (needs to be declared first) */
#define DEFINE_EXTERN_TASK(name) \
DEFINE_TASK_EXPLICIT_LINKAGE(name, extern)
DEFINE_TASK_EXPLICIT(name, extern)
#define DEFINE_TASK_WITH_FINALIZER_EXPLICIT_LINKAGE(name, linkage) \
#define DEFINE_TASK_WITH_FINALIZER_EXPLICIT(name, linkage) \
TASK_COMMON_PRIVATE_DECLARATIONS(name); \
TASK_COMMON_THUNK_DEFINITIONS(name, linkage) \
/* error out if using TASK_FINALIZER without TASK_WITH_FINALIZER */ \
struct COTASK__##name##__not_declared_using_TASK_WITH_FINALIZER { char dummy; }; \
/* user-defined finalizer function */ \
INLINE void COTASKFINALIZER_##name(TASK_ARGS(name) *_cotask_args); \
INLINE void COTASKFINALIZER_##name(TASK_ARGS_TYPE(name) *_cotask_args); \
/* real finalizer entry point */ \
static void *COTASKFINALIZERTHUNK_##name(void *arg) { \
COTASKFINALIZER_##name((TASK_ARGS(name)*)arg); \
COTASKFINALIZER_##name((TASK_ARGS_TYPE(name)*)arg); \
return NULL; \
} \
/* prologue; sets up finalizer before executing task body */ \
INLINE void COTASKPROLOGUE_##name(TASK_ARGS(name) *_cotask_args) { \
INLINE void COTASKPROLOGUE_##name(TASK_ARGS_TYPE(name) *_cotask_args) { \
cotask_set_finalizer(cotask_active(), COTASKFINALIZERTHUNK_##name, _cotask_args); \
} \
/* begin task body definition */ \
@ -220,11 +244,11 @@ INLINE void cosched_set_invoke_target(CoSched *sched) { _cosched_global = sched;
/* define a task that needs a finalizer with static linkage (needs to be declared first) */
#define DEFINE_TASK_WITH_FINALIZER(name) \
DEFINE_TASK_WITH_FINALIZER_EXPLICIT_LINKAGE(name, static)
DEFINE_TASK_WITH_FINALIZER_EXPLICIT(name, static)
/* define a task that needs a finalizer with static linkage (needs to be declared first) */
#define DEFINE_EXTERN_TASK_WITH_FINALIZER(name) \
DEFINE_TASK_WITH_FINALIZER_EXPLICIT_LINKAGE(name, extern)
DEFINE_TASK_WITH_FINALIZER_EXPLICIT(name, extern)
/* declare and define a task that needs a finalizer with static linkage */
#define TASK_WITH_FINALIZER(name, argstruct) \
@ -236,12 +260,12 @@ INLINE void cosched_set_invoke_target(CoSched *sched) { _cosched_global = sched;
/* error out if using TASK_FINALIZER without TASK_WITH_FINALIZER */ \
attr_unused struct COTASK__##name##__not_declared_using_TASK_WITH_FINALIZER COTASK__##name##__not_declared_using_TASK_WITH_FINALIZER; \
/* begin finalizer body definition */ \
static void COTASKFINALIZER_##name(TASK_ARGS(name) *_cotask_args)
static void COTASKFINALIZER_##name(TASK_ARGS_TYPE(name) *_cotask_args)
#define INVOKE_TASK_(name, ...) ( \
(void)COTASK_UNUSED_CHECK_##name, \
cosched_new_task(_cosched_global, COTASKTHUNK_##name, \
&(TASK_ARGS(name)) { __VA_ARGS__ } \
&(TASK_ARGS_TYPE(name)) { __VA_ARGS__ } \
) \
)
@ -269,6 +293,22 @@ DECLARE_EXTERN_TASK(_cancel_task_helper, { BoxedTask task; });
#define CANCEL_TASK_WHEN(_event, _task) INVOKE_TASK_WHEN(_event, _cancel_task_helper, _task)
#define TASK_INDIRECT(iface, task) ( \
(void)COTASK_UNUSED_CHECK_##task, \
(TASK_INDIRECT_TYPE_ALIAS(task)) { ._cotask_##iface##_thunk = COTASKTHUNK_##task } \
)
#define TASK_INDIRECT_INIT(iface, task) \
{ ._cotask_##iface##_thunk = COTASKTHUNK_##task } \
#define INVOKE_TASK_INDIRECT_(iface, taskhandle, ...) ( \
cosched_new_task(_cosched_global, taskhandle._cotask_##iface##_thunk, \
&(TASK_IFACE_ARGS_TYPE(iface)) { __VA_ARGS__ } \
) \
)
#define INVOKE_TASK_INDIRECT(iface, ...) INVOKE_TASK_INDIRECT_(iface, __VA_ARGS__, ._dummy_1 = 0)
#define THIS_TASK cotask_box(cotask_active())
#define YIELD cotask_yield(NULL)
@ -290,12 +330,12 @@ ENT_TYPES
#undef ENT_TYPE
#define cotask_bind_to_entity(task, ent) (_Generic((ent), \
Projectile*: _cotask_bind_to_entity_Projectile, \
Laser*: _cotask_bind_to_entity_Laser, \
Enemy*: _cotask_bind_to_entity_Enemy, \
Boss*: _cotask_bind_to_entity_Boss, \
Player*: _cotask_bind_to_entity_Player, \
Item*: _cotask_bind_to_entity_Item, \
struct Projectile*: _cotask_bind_to_entity_Projectile, \
struct Laser*: _cotask_bind_to_entity_Laser, \
struct Enemy*: _cotask_bind_to_entity_Enemy, \
struct Boss*: _cotask_bind_to_entity_Boss, \
struct Player*: _cotask_bind_to_entity_Player, \
struct Item*: _cotask_bind_to_entity_Item, \
EntityInterface*: cotask_bind_to_entity \
)(task, ent))

View file

@ -43,4 +43,11 @@ INLINE MoveParams move_asymptotic_simple(complex vel, double boost_factor) {
return move_asymptotic(vel * (1 + boost_factor * retention), vel, retention);
}
INLINE MoveParams move_towards(complex target, complex attraction) {
return (MoveParams) {
.attraction = attraction,
.attraction_point = target,
};
}
#endif // IGUARD_move_h

View file

@ -72,7 +72,7 @@ static void add_spellpractice_stages(int *spellnum, bool (*filter)(AttackInfo*),
break;
}
for(AttackInfo *a = s->spell; a->rule; ++a) {
for(AttackInfo *a = s->spell; a->name; ++a) {
if(!filter(a)) {
continue;
}

View file

@ -26,7 +26,7 @@ struct stage1_spells_s stage1_spells = {
.mid = {
.perfect_freeze = {
{ 0, 1, 2, 3}, AT_Spellcard, "Freeze Sign “Perfect Freeze”", 50, 24000,
cirno_perfect_freeze, cirno_pfreeze_bg, VIEWPORT_W/2.0+100.0*I, 1
NULL, cirno_pfreeze_bg, VIEWPORT_W/2.0+100.0*I, 1, TASK_INDIRECT_INIT(BossAttack, stage1_spell_perfect_freeze)
},
},

View file

@ -1619,16 +1619,50 @@ TASK(multiburst_fairies_1, NO_ARGS) {
}
}
TASK_WITH_INTERFACE(midboss_intro, BossAttack) {
Boss *boss = TASK_BIND(ARGS.boss);
WAIT_EVENT(&ARGS.attack->events.started);
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.035);
}
TASK_WITH_INTERFACE(icy_storm, BossAttack) {
Boss *boss = TASK_BIND(ARGS.boss);
Attack *a = ARGS.attack;
CANCEL_TASK_WHEN(&a->events.finished, THIS_TASK);
WAIT_EVENT(&a->events.started);
// TODO implement
(void)boss;
}
DEFINE_EXTERN_TASK(stage1_spell_perfect_freeze) {
Boss *boss = TASK_BIND(ARGS.boss);
Attack *a = ARGS.attack;
CANCEL_TASK_WHEN(&a->events.finished, THIS_TASK);
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.04);
WAIT_EVENT(&a->events.started);
// TODO implement
(void)boss;
}
TASK_WITH_INTERFACE(midboss_flee, BossAttack) {
Boss *boss = TASK_BIND(ARGS.boss);
WAIT_EVENT(&ARGS.attack->events.started);
boss->move = move_towards(-250 + 30 * I, 0.02);
}
TASK(spawn_midboss, NO_ARGS) {
Boss *cirno = stage1_spawn_cirno(VIEWPORT_W + 220 + 30.0*I);
Boss *boss = global.boss = 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_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, icy_storm), 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(cirno, cirno->attacks);
global.boss = cirno;
boss_start_attack(boss, boss->attacks);
}
TASK(stage_timeline, NO_ARGS) {

View file

@ -24,4 +24,6 @@ void cirno_benchmark(Boss*, int);
void stage1_events(void);
Boss* stage1_spawn_cirno(cmplx pos);
DECLARE_EXTERN_TASK_WITH_INTERFACE(stage1_spell_perfect_freeze, BossAttack);
#endif // IGUARD_stages_stage1_events_h