further task-boss infrastructure WIP
This commit is contained in:
parent
e14eb36e5c
commit
9498e64dd1
10 changed files with 237 additions and 67 deletions
58
src/boss.c
58
src/boss.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
41
src/boss.h
41
src/boss.h
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
106
src/coroutine.h
106
src/coroutine.h
|
@ -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))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue