boss: support custom task parameters via interface inheritance
This commit is contained in:
parent
49deb2bd83
commit
1db80d0905
20 changed files with 121 additions and 67 deletions
97
src/boss.c
97
src/boss.c
|
@ -1435,13 +1435,64 @@ static Attack *_boss_add_attack(Boss *boss, AttackType type, char *name, float t
|
|||
return a;
|
||||
}
|
||||
|
||||
static Attack *_boss_add_attack_from_info(Boss *boss, AttackInfo *info){
|
||||
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);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
TASK(attack_task_helper_custom, {
|
||||
CoEvent *evt;
|
||||
BossAttackTask task;
|
||||
BossAttackTaskArgs *task_args;
|
||||
size_t task_args_size;
|
||||
BossAttackTaskArgs task_args_header;
|
||||
}) {
|
||||
size_t args_size = ARGS.task_args_size;
|
||||
BossAttackTaskArgs *orig_args = NOT_NULL(ARGS.task_args);
|
||||
char *args = TASK_MALLOC(args_size);
|
||||
size_t header_ofs = sizeof(ARGS.task_args_header);
|
||||
assume(args_size >= header_ofs);
|
||||
|
||||
memcpy(args, &ARGS.task_args_header, sizeof(ARGS.task_args_header));
|
||||
memcpy(args + header_ofs, (char*)orig_args + header_ofs, args_size - header_ofs);
|
||||
|
||||
WAIT_EVENT_OR_DIE(ARGS.evt);
|
||||
|
||||
ARGS.task._cotask_BossAttack_thunk(args, args_size);
|
||||
}
|
||||
|
||||
static void setup_attack_task_with_custom_args(
|
||||
Boss *boss, Attack *a, BossAttackTask task,
|
||||
BossAttackTaskArgs *args, size_t args_size
|
||||
) {
|
||||
INVOKE_TASK(attack_task_helper_custom,
|
||||
.evt = &a->events.initiated,
|
||||
.task = task,
|
||||
.task_args = args,
|
||||
.task_args_size = args_size,
|
||||
.task_args_header = {
|
||||
.boss = ENT_BOX(boss),
|
||||
.attack = 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, sizeof(ARGS.task_args));
|
||||
}
|
||||
|
||||
|
@ -1462,8 +1513,8 @@ Attack *boss_add_attack_task(Boss *boss, AttackType type, char *name, float time
|
|||
}
|
||||
|
||||
TASK_WITH_INTERFACE(legacy_attack_helper, BossAttack) {
|
||||
INIT_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK();
|
||||
INIT_BOSS_ATTACK(&ARGS);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
}
|
||||
|
||||
static void setup_legacy_attack(Boss *boss, Attack *atk) {
|
||||
|
@ -1525,23 +1576,12 @@ static void boss_generic_move(Boss *b, int time) {
|
|||
b->pos = atck->info->pos_dest * f + BOSS_DEFAULT_SPAWN_POS * (1 - f);
|
||||
}
|
||||
|
||||
Attack *boss_add_attack_from_info(Boss *boss, AttackInfo *info, char move) {
|
||||
Attack *boss_add_attack_from_info(Boss *boss, AttackInfo *info, bool move) {
|
||||
if(move) {
|
||||
boss_add_attack(boss, AT_Move, "Generic Move", 0.5, 0, boss_generic_move, NULL)->info = info;
|
||||
}
|
||||
|
||||
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);
|
||||
Attack *a = _boss_add_attack_from_info(boss, info);
|
||||
|
||||
if(info->task._cotask_BossAttack_thunk) {
|
||||
setup_attack_task(boss, a, info->task);
|
||||
|
@ -1552,15 +1592,24 @@ Attack *boss_add_attack_from_info(Boss *boss, AttackInfo *info, char move) {
|
|||
return a;
|
||||
}
|
||||
|
||||
Boss *_init_boss_attack_task(BoxedBoss boss, Attack *attack) {
|
||||
Boss *pboss = TASK_BIND(boss);
|
||||
CANCEL_TASK_AFTER(&attack->events.finished, THIS_TASK);
|
||||
Attack *boss_add_attack_from_info_with_args(
|
||||
Boss *boss, AttackInfo *info, BossAttackTaskCustomArgs args
|
||||
) {
|
||||
Attack *a = _boss_add_attack_from_info(boss, info);
|
||||
assume(info->task._cotask_BossAttack_thunk != NULL);
|
||||
setup_attack_task_with_custom_args(boss, a, info->task, args.ptr, args.size);
|
||||
return a;
|
||||
}
|
||||
|
||||
Boss *_init_boss_attack_task(const BossAttackTaskArgs *args) {
|
||||
Boss *pboss = TASK_BIND(args->boss);
|
||||
CANCEL_TASK_AFTER(&args->attack->events.finished, THIS_TASK);
|
||||
return pboss;
|
||||
}
|
||||
|
||||
void _begin_boss_attack_task(BoxedBoss boss, Attack *attack) {
|
||||
boss_start_next_attack(NOT_NULL(ENT_UNBOX(boss)), attack);
|
||||
WAIT_EVENT_OR_DIE(&attack->events.started);
|
||||
void _begin_boss_attack_task(const BossAttackTaskArgs *args) {
|
||||
boss_start_next_attack(NOT_NULL(ENT_UNBOX(args->boss)), args->attack);
|
||||
WAIT_EVENT_OR_DIE(&args->attack->events.started);
|
||||
}
|
||||
|
||||
void boss_preload(void) {
|
||||
|
|
15
src/boss.h
15
src/boss.h
|
@ -57,6 +57,7 @@ DEFINE_TASK_INTERFACE(BossAttack, {
|
|||
|
||||
typedef TASK_INDIRECT_TYPE(BossAttack) BossAttackTask;
|
||||
typedef TASK_IFACE_ARGS_TYPE(BossAttack) BossAttackTaskArgs;
|
||||
typedef TASK_IFACE_ARGS_SIZED_PTR_TYPE(BossAttack) BossAttackTaskCustomArgs;
|
||||
|
||||
struct AttackInfo {
|
||||
/*
|
||||
|
@ -203,7 +204,9 @@ Attack *boss_add_attack(Boss *boss, AttackType type, char *name, float timeout,
|
|||
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)
|
||||
Attack *boss_add_attack_from_info(Boss *boss, AttackInfo *info, bool move)
|
||||
attr_nonnull(1, 2) attr_returns_nonnull;
|
||||
Attack *boss_add_attack_from_info_with_args(Boss *boss, AttackInfo *info, BossAttackTaskCustomArgs args)
|
||||
attr_nonnull(1, 2) attr_returns_nonnull;
|
||||
void boss_set_attack_bonus(Attack *a, int rank) attr_nonnull(1);
|
||||
|
||||
|
@ -228,10 +231,12 @@ void boss_preload(void);
|
|||
#define BOSS_DEFAULT_GO_POS (VIEWPORT_W * 0.5 + 200.0*I)
|
||||
#define BOSS_NOMOVE (-3142-39942.0*I)
|
||||
|
||||
Boss *_init_boss_attack_task(BoxedBoss boss, Attack *attack);
|
||||
void _begin_boss_attack_task(BoxedBoss boss, Attack *attack);
|
||||
#define INIT_BOSS_ATTACK() _init_boss_attack_task(ARGS.boss, ARGS.attack)
|
||||
#define BEGIN_BOSS_ATTACK() _begin_boss_attack_task(ARGS.boss, ARGS.attack)
|
||||
Boss *_init_boss_attack_task(const BossAttackTaskArgs *args)
|
||||
attr_nonnull_all attr_returns_nonnull;
|
||||
void _begin_boss_attack_task(const BossAttackTaskArgs *args)
|
||||
attr_nonnull_all;
|
||||
#define INIT_BOSS_ATTACK(_args) _init_boss_attack_task(_args)
|
||||
#define BEGIN_BOSS_ATTACK(_args) _begin_boss_attack_task(_args)
|
||||
|
||||
int attacktype_start_delay(AttackType t) attr_const;
|
||||
int attacktype_end_delay(AttackType t) attr_const;
|
||||
|
|
|
@ -60,8 +60,8 @@ static void dpstest_multi(void) {
|
|||
}
|
||||
|
||||
TASK_WITH_INTERFACE(boss_regen, BossAttack) {
|
||||
INIT_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK();
|
||||
INIT_BOSS_ATTACK(&ARGS);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
Attack *a = ARGS.attack;
|
||||
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
#include "global.h"
|
||||
|
||||
DEFINE_EXTERN_TASK(stage1_boss_nonspell_1) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.05);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
for(;;) {
|
||||
WAIT(20);
|
||||
|
|
|
@ -112,9 +112,9 @@ TASK(spiralshot, {
|
|||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage1_boss_nonspell_2) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.02);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
for(;;) {
|
||||
WAIT(20);
|
||||
|
|
|
@ -90,9 +90,9 @@ TASK(make_snowflake, {
|
|||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage1_midboss_nonspell_1) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(CMPLX(VIEWPORT_W/2, 200), 0.02);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_stop(0.8);
|
||||
|
||||
int flake_spawn_interval = difficulty_value(11, 10, 9, 8);
|
||||
|
|
|
@ -64,9 +64,9 @@ TASK(cirno_frostbolt, { cmplx pos; cmplx vel; }) {
|
|||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage1_spell_crystal_blizzard) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W / 2.0 + 300 * I, 0.1);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
int frostbolt_period = difficulty_value(4, 3, 2, 1);
|
||||
|
||||
|
|
|
@ -76,8 +76,8 @@ TASK(crystal_rain_cirno_shoot, { BoxedBoss boss; int charge_time; }) {
|
|||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage1_spell_crystal_rain) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
INVOKE_SUBTASK(crystal_rain_drops);
|
||||
boss->move = move_towards_power(boss->pos, 0.1, 0.5);
|
||||
|
|
|
@ -38,9 +38,9 @@ TASK(cirno_icicle, { cmplx pos; cmplx vel; }) {
|
|||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage1_spell_icicle_cascade) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W / 2.0 + 120.0*I, 0.01);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
int icicle_interval = difficulty_value(30, 22, 16, 8);
|
||||
int icicles = 4;
|
||||
|
|
|
@ -37,8 +37,8 @@ TASK(move_frozen, { BoxedProjectileArray *parray; }) {
|
|||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage1_spell_perfect_freeze) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
for(int run = 1;;run++) {
|
||||
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.04);
|
||||
|
|
|
@ -151,9 +151,9 @@ TASK(halation_chase, { BoxedBoss boss; }) {
|
|||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage1_spell_snow_halation) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.05);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
cmplx center;
|
||||
real rotation = 0;
|
||||
|
|
|
@ -572,14 +572,14 @@ TASK(waveshot_fairies, { int duration; }) {
|
|||
}
|
||||
|
||||
TASK_WITH_INTERFACE(midboss_intro, BossAttack) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2.0 + 200.0*I, 0.035);
|
||||
}
|
||||
|
||||
TASK_WITH_INTERFACE(midboss_flee, BossAttack) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(-250 + 30 * I, 0.02);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ TASK(wander, { BoxedBoss boss; }) {
|
|||
DEFINE_EXTERN_TASK(stage2_boss_nonspell_1) {
|
||||
STAGE_BOOKMARK(boss-non1);
|
||||
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
INVOKE_SUBTASK_DELAYED(420, wander, ENT_BOX(boss));
|
||||
|
||||
|
|
|
@ -67,9 +67,9 @@ TASK(balls, { BoxedBoss boss; }) {
|
|||
DEFINE_EXTERN_TASK(stage2_boss_nonspell_2) {
|
||||
STAGE_BOOKMARK(boss-non2);
|
||||
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2 + 100*I, 0.01);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
INVOKE_SUBTASK(speen, ENT_BOX(boss));
|
||||
INVOKE_SUBTASK_DELAYED(150, balls, ENT_BOX(boss));
|
||||
|
|
|
@ -163,9 +163,9 @@ static void random_move(Boss *boss) {
|
|||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage2_boss_nonspell_3) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2.0 + 100.0*I, 0.02);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
real dir_sign = rng_sign();
|
||||
|
||||
|
|
|
@ -90,9 +90,9 @@ static void wriggle_anim_end_charge(Boss *boss) {
|
|||
DEFINE_EXTERN_TASK(stage2_midboss_nonspell_1) {
|
||||
STAGE_BOOKMARK(non1);
|
||||
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2 + 100.0*I, 0.02);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
DECLARE_ENT_ARRAY(Projectile, bugs, 512);
|
||||
|
||||
|
|
|
@ -179,9 +179,9 @@ TASK(fan_burst, { Boss *boss; }) {
|
|||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage2_spell_amulet_of_harm) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2 + 200.0*I, 0.02);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
Rect wander_bounds = viewport_bounds(64);
|
||||
wander_bounds.top += 64;
|
||||
|
|
|
@ -80,9 +80,9 @@ TASK(walls, { int duration; }) {
|
|||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage2_spell_bad_pick) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(pick_boss_position(), 0.02);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
int balls_per_slot = difficulty_value(15, 15, 20, 25);
|
||||
|
||||
|
|
|
@ -139,13 +139,13 @@ TASK(cards, { BoxedBoss boss; }) {
|
|||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage2_spell_monty_hall_danmaku) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
|
||||
COEVENTS_ARRAY(goat_trigger) events;
|
||||
TASK_HOST_EVENTS(events);
|
||||
|
||||
boss->move = move_towards(VIEWPORT_W/2.0 + VIEWPORT_H/2.0 * I, 0.06);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
int plr_slot;
|
||||
int goat1_slot;
|
||||
|
|
|
@ -66,9 +66,9 @@ TASK(wheel, { BoxedBoss boss; real spin_dir; int duration; }) {
|
|||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(stage2_spell_wheel_of_fortune) {
|
||||
Boss *boss = INIT_BOSS_ATTACK();
|
||||
Boss *boss = INIT_BOSS_ATTACK(&ARGS);
|
||||
boss->move = move_towards(VIEWPORT_W/2 + VIEWPORT_H/2*I, 0.02);
|
||||
BEGIN_BOSS_ATTACK();
|
||||
BEGIN_BOSS_ATTACK(&ARGS);
|
||||
|
||||
aniplayer_queue(&boss->ani, "guruguru", 0);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue