boss: support custom task parameters via interface inheritance

This commit is contained in:
Andrei Alexeyev 2021-05-19 03:20:03 +03:00
parent 49deb2bd83
commit 1db80d0905
No known key found for this signature in database
GPG key ID: 72D26128040B9690
20 changed files with 121 additions and 67 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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