task system tweaks and early Stage 1 port attempt
This commit is contained in:
parent
76aef22e23
commit
4e4224dc59
10 changed files with 309 additions and 45 deletions
2
external/koishi
vendored
2
external/koishi
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 47e63b57018a62a300d797ea518578850c60730b
|
||||
Subproject commit 7d66fcd750285059aa3ece6094c3fae4f583d57e
|
|
@ -204,9 +204,15 @@ CoStatus cotask_status(CoTask *task) {
|
|||
return koishi_state(&task->ko);
|
||||
}
|
||||
|
||||
void cotask_bind_to_entity(CoTask *task, EntityInterface *ent) {
|
||||
EntityInterface *(cotask_bind_to_entity)(CoTask *task, EntityInterface *ent) {
|
||||
assert(task->bound_ent.ent == 0);
|
||||
|
||||
if(ent == NULL) {
|
||||
koishi_die(NULL);
|
||||
}
|
||||
|
||||
task->bound_ent = ent_box(ent);
|
||||
return ent;
|
||||
}
|
||||
|
||||
void cotask_set_finalizer(CoTask *task, CoTaskFunc finalizer, void *arg) {
|
||||
|
@ -346,3 +352,18 @@ DEFINE_EXTERN_TASK(_cancel_task_helper) {
|
|||
cotask_cancel(task);
|
||||
}
|
||||
}
|
||||
|
||||
#include <projectile.h>
|
||||
#include <laser.h>
|
||||
#include <item.h>
|
||||
#include <enemy.h>
|
||||
#include <boss.h>
|
||||
#include <player.h>
|
||||
|
||||
#define ENT_TYPE(typename, id) \
|
||||
typename *_cotask_bind_to_entity_##typename(CoTask *task, typename *ent) { \
|
||||
return ENT_CAST((cotask_bind_to_entity)(task, ent ? &ent->entity_interface : NULL), typename); \
|
||||
}
|
||||
|
||||
ENT_TYPES
|
||||
#undef ENT_TYPE
|
||||
|
|
|
@ -58,7 +58,7 @@ void *cotask_yield(void *arg);
|
|||
bool cotask_wait_event(CoEvent *evt, void *arg);
|
||||
CoStatus cotask_status(CoTask *task);
|
||||
CoTask *cotask_active(void);
|
||||
void cotask_bind_to_entity(CoTask *task, EntityInterface *ent);
|
||||
EntityInterface *cotask_bind_to_entity(CoTask *task, EntityInterface *ent) attr_returns_nonnull;
|
||||
void cotask_set_finalizer(CoTask *task, CoTaskFunc finalizer, void *arg);
|
||||
|
||||
BoxedTask cotask_box(CoTask *task);
|
||||
|
@ -87,6 +87,9 @@ static inline attr_must_inline void cosched_set_invoke_target(CoSched *sched) {
|
|||
|
||||
#define ARGS (*_cotask_args)
|
||||
|
||||
#define NO_ARGS { char _dummy_0; }
|
||||
#define ARGS_STRUCT(argstruct) { struct argstruct; char _dummy_1; }
|
||||
|
||||
#define TASK_COMMON_PRIVATE_DECLARATIONS(name) \
|
||||
/* user-defined task body */ \
|
||||
static void COTASK_##name(TASK_ARGS(name) *_cotask_args); \
|
||||
|
@ -97,7 +100,7 @@ static inline attr_must_inline void cosched_set_invoke_target(CoSched *sched) {
|
|||
/* produce warning if the task is never used */ \
|
||||
linkage char COTASK_UNUSED_CHECK_##name; \
|
||||
/* user-defined type of args struct */ \
|
||||
struct TASK_ARGS_NAME(name) argstruct; \
|
||||
struct TASK_ARGS_NAME(name) ARGS_STRUCT(argstruct); \
|
||||
/* type of internal args struct for INVOKE_TASK_DELAYED */ \
|
||||
struct TASK_ARGSDELAY_NAME(name) { TASK_ARGS(name) real_args; int delay; }; \
|
||||
/* type of internal args struct for INVOKE_TASK_WHEN */ \
|
||||
|
@ -227,27 +230,33 @@ static inline attr_must_inline void cosched_set_invoke_target(CoSched *sched) {
|
|||
/* begin finalizer body definition */ \
|
||||
static void COTASKFINALIZER_##name(TASK_ARGS(name) *_cotask_args)
|
||||
|
||||
#define INVOKE_TASK(name, ...) ( \
|
||||
#define INVOKE_TASK_(name, ...) ( \
|
||||
(void)COTASK_UNUSED_CHECK_##name, \
|
||||
cosched_new_task(_cosched_global, COTASKTHUNK_##name, \
|
||||
&(TASK_ARGS(name)) { __VA_ARGS__ } \
|
||||
) \
|
||||
)
|
||||
|
||||
#define INVOKE_TASK_WHEN(_event, name, ...) ( \
|
||||
#define INVOKE_TASK(...) INVOKE_TASK_(__VA_ARGS__, ._dummy_1 = 0)
|
||||
|
||||
#define INVOKE_TASK_WHEN_(_event, name, ...) ( \
|
||||
(void)COTASK_UNUSED_CHECK_##name, \
|
||||
cosched_new_task(_cosched_global, COTASKTHUNKCOND_##name, \
|
||||
&(TASK_ARGSCOND(name)) { .real_args = { __VA_ARGS__ }, .event = (_event) } \
|
||||
) \
|
||||
)
|
||||
|
||||
#define INVOKE_TASK_DELAYED(_delay, name, ...) ( \
|
||||
#define INVOKE_TASK_WHEN(_event, ...) INVOKE_TASK_WHEN_(_event, __VA_ARGS__, ._dummy_1 = 0)
|
||||
|
||||
#define INVOKE_TASK_DELAYED_(_delay, name, ...) ( \
|
||||
(void)COTASK_UNUSED_CHECK_##name, \
|
||||
cosched_new_task(_cosched_global, COTASKTHUNKDELAY_##name, \
|
||||
&(TASK_ARGSDELAY(name)) { .real_args = { __VA_ARGS__ }, .delay = (_delay) } \
|
||||
) \
|
||||
)
|
||||
|
||||
#define INVOKE_TASK_DELAYED(_delay, ...) INVOKE_TASK_DELAYED_(_delay, __VA_ARGS__, ._dummy_1 = 0)
|
||||
|
||||
DECLARE_EXTERN_TASK(_cancel_task_helper, { BoxedTask task; });
|
||||
|
||||
#define CANCEL_TASK_WHEN(_event, _task) INVOKE_TASK_WHEN(_event, _cancel_task_helper, _task)
|
||||
|
@ -263,6 +272,24 @@ DECLARE_EXTERN_TASK(_cancel_task_helper, { BoxedTask task; });
|
|||
#define BYIELD do { YIELD; CHECK_BREAK; } while(0)
|
||||
#define BWAIT(frames) do { WAIT(frames); CHECK_BREAK; } while(0)
|
||||
|
||||
#define TASK_BIND(ent) cotask_bind_to_entity(cotask_active(), &ent->entity_interface)
|
||||
#define ENT_TYPE(typename, id) \
|
||||
struct typename; \
|
||||
struct typename *_cotask_bind_to_entity_##typename(CoTask *task, struct typename *ent);
|
||||
|
||||
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, \
|
||||
EntityInterface*: cotask_bind_to_entity \
|
||||
)(task, ent))
|
||||
|
||||
#define TASK_BIND(box) cotask_bind_to_entity(cotask_active(), ENT_UNBOX(box))
|
||||
#define TASK_BIND_UNBOXED(ent) cotask_bind_to_entity(cotask_active(), ent)
|
||||
|
||||
#endif // IGUARD_coroutine_h
|
||||
|
|
12
src/entity.c
12
src/entity.c
|
@ -260,3 +260,15 @@ EntityInterface *ent_unbox(BoxedEntity box) {
|
|||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define ENT_TYPE(typename, id) \
|
||||
Boxed##typename _ent_box_##typename(struct typename *ent) { \
|
||||
return (Boxed##typename) { .as_generic = ent_box(&ent->entity_interface) };\
|
||||
} \
|
||||
struct typename *_ent_unbox_##typename(Boxed##typename box) { \
|
||||
EntityInterface *e = ent_unbox(box.as_generic); \
|
||||
return e ? ENT_CAST(e, typename) : NULL; \
|
||||
}
|
||||
|
||||
ENT_TYPES
|
||||
#undef ENT_TYPE
|
||||
|
|
52
src/entity.h
52
src/entity.h
|
@ -110,11 +110,6 @@ struct EntityInterface {
|
|||
ENTITY_INTERFACE_BASE(EntityInterface);
|
||||
};
|
||||
|
||||
struct BoxedEntity {
|
||||
uintptr_t ent; // not an actual pointer to avert the temptation to use it directly.
|
||||
uint_fast32_t spawn_id;
|
||||
};
|
||||
|
||||
INLINE const char* ent_type_name(EntityType type) {
|
||||
switch(type) {
|
||||
#define ENT_TYPE(typename, id) case id: return #id;
|
||||
|
@ -158,18 +153,47 @@ void ent_unhook_pre_draw(EntityDrawHookCallback callback);
|
|||
void ent_hook_post_draw(EntityDrawHookCallback callback, void *arg);
|
||||
void ent_unhook_post_draw(EntityDrawHookCallback callback);
|
||||
|
||||
struct BoxedEntity {
|
||||
uintptr_t ent; // not an actual pointer to avert the temptation to use it directly.
|
||||
uint_fast32_t spawn_id;
|
||||
};
|
||||
|
||||
BoxedEntity ent_box(EntityInterface *ent);
|
||||
EntityInterface *ent_unbox(BoxedEntity box);
|
||||
|
||||
#define ENT_BOX(ent) ent_box(&(ent)->entity_interface)
|
||||
#define ENT_TYPE(typename, id) \
|
||||
typedef union Boxed##typename { \
|
||||
BoxedEntity as_generic; \
|
||||
struct { \
|
||||
uintptr_t ent; \
|
||||
uint_fast32_t spawn_id; \
|
||||
}; \
|
||||
} Boxed##typename; \
|
||||
struct typename; \
|
||||
Boxed##typename _ent_box_##typename(struct typename *ent); \
|
||||
struct typename *_ent_unbox_##typename(Boxed##typename box); \
|
||||
|
||||
#if defined(USE_GNU_EXTENSIONS) && defined(DEBUG)
|
||||
#define ENT_UNBOX(h, typename) (__extension__ ({ \
|
||||
EntityInterface *_unboxed_ent = ent_unbox(h); \
|
||||
_unboxed_ent ? ENT_CAST(_unboxed_ent, typename) : NULL; \
|
||||
}))
|
||||
#else
|
||||
#define ENT_UNBOX(h, typename) CASTPTR_ASSUME_ALIGNED(ent_unbox(h), typename)
|
||||
#endif
|
||||
ENT_TYPES
|
||||
#undef ENT_TYPE
|
||||
|
||||
#define ENT_BOX(ent) (_Generic((ent), \
|
||||
Projectile*: _ent_box_Projectile, \
|
||||
Laser*: _ent_box_Laser, \
|
||||
Enemy*: _ent_box_Enemy, \
|
||||
Boss*: _ent_box_Boss, \
|
||||
Player*: _ent_box_Player, \
|
||||
Item*: _ent_box_Item, \
|
||||
EntityInterface*: ent_box \
|
||||
)(ent))
|
||||
|
||||
#define ENT_UNBOX(box) (_Generic((box), \
|
||||
BoxedProjectile: _ent_unbox_Projectile, \
|
||||
BoxedLaser: _ent_unbox_Laser, \
|
||||
BoxedEnemy: _ent_unbox_Enemy, \
|
||||
BoxedBoss: _ent_unbox_Boss, \
|
||||
BoxedPlayer: _ent_unbox_Player, \
|
||||
BoxedItem: _ent_unbox_Item, \
|
||||
BoxedEntity: ent_unbox \
|
||||
)(box))
|
||||
|
||||
#endif // IGUARD_entity_h
|
||||
|
|
|
@ -36,4 +36,10 @@ MOVE_FUNC move_asymptotic(complex vel0, complex vel1, complex retention) {
|
|||
return (MoveParams) { vel0, vel1 * (1 - retention), retention };
|
||||
}
|
||||
|
||||
MOVE_FUNC move_asymptotic_simple(complex vel, double boost_factor) {
|
||||
// NOTE: this matches the old asymptotic rule semantics exactly
|
||||
double retention = 0.8;
|
||||
return move_asymptotic(vel * (1 + boost_factor * retention), vel, retention);
|
||||
}
|
||||
|
||||
#endif // IGUARD_move_h
|
||||
|
|
15
src/stage.c
15
src/stage.c
|
@ -733,16 +733,25 @@ inline bool stage_should_yield(void) {
|
|||
return (global.boss && !boss_is_fleeing(global.boss)) || dialog_is_active(global.dialog);
|
||||
}
|
||||
|
||||
void stage_yield(void) {
|
||||
int stage_yield(void) {
|
||||
int num_yields = 0;
|
||||
|
||||
do {
|
||||
cotask_yield(NULL);
|
||||
++num_yields;
|
||||
} while(stage_should_yield());
|
||||
|
||||
return num_yields;
|
||||
}
|
||||
|
||||
void stage_wait(int delay) {
|
||||
int stage_wait(int delay) {
|
||||
int num_yields = 0;
|
||||
|
||||
while(delay-- > 0) {
|
||||
stage_yield();
|
||||
num_yields += stage_yield();
|
||||
}
|
||||
|
||||
return num_yields;
|
||||
}
|
||||
|
||||
static LogicFrameAction stage_logic_frame(void *arg) {
|
||||
|
|
|
@ -150,8 +150,8 @@ bool stage_is_cleared(void);
|
|||
void stage_unlock_bgm(const char *bgm);
|
||||
|
||||
bool stage_should_yield(void);
|
||||
void stage_yield(void);
|
||||
void stage_wait(int delay);
|
||||
int stage_yield(void);
|
||||
int stage_wait(int delay);
|
||||
|
||||
#include "stages/stage1.h"
|
||||
#include "stages/stage2.h"
|
||||
|
|
|
@ -17,19 +17,19 @@ TASK(laserproj_death, { Projectile *p; }) {
|
|||
spawn_projectile_clear_effect(ARGS.p);
|
||||
}
|
||||
|
||||
TASK(laserize_proj, { Projectile *p; int t; }) {
|
||||
TASK_BIND(ARGS.p);
|
||||
TASK(laserize_proj, { BoxedProjectile p; int t; }) {
|
||||
Projectile *p = TASK_BIND(ARGS.p);
|
||||
WAIT(ARGS.t);
|
||||
|
||||
complex pos = ARGS.p->pos;
|
||||
double a = ARGS.p->angle;
|
||||
Color clr = ARGS.p->color;
|
||||
kill_projectile(ARGS.p);
|
||||
complex pos = p->pos;
|
||||
double a = p->angle;
|
||||
Color clr = p->color;
|
||||
kill_projectile(p);
|
||||
|
||||
complex aim = 12 * cexp(I * a);
|
||||
create_laserline(pos, aim, 60, 80, &clr);
|
||||
|
||||
Projectile *p = PROJECTILE(
|
||||
p = PROJECTILE(
|
||||
.pos = pos,
|
||||
.proto = pp_ball,
|
||||
.color = &clr,
|
||||
|
@ -39,13 +39,15 @@ TASK(laserize_proj, { Projectile *p; int t; }) {
|
|||
INVOKE_TASK_WHEN(&p->events.killed, laserproj_death, p);
|
||||
}
|
||||
|
||||
TASK(wait_event_test, { Enemy *e; int rounds; int delay; int cnt; int cnt_inc; }) {
|
||||
TASK(wait_event_test, { BoxedEnemy e; int rounds; int delay; int cnt; int cnt_inc; }) {
|
||||
// WAIT_EVENT yields until the event fires.
|
||||
// Returns true if the event was signaled, false if it was canceled.
|
||||
// All waiting tasks will resume right after either of those occur, in the
|
||||
// order they started waiting.
|
||||
|
||||
if(!WAIT_EVENT(&ARGS.e->events.killed)) {
|
||||
Enemy *e = ENT_UNBOX(ARGS.e);
|
||||
|
||||
if(!WAIT_EVENT(&e->events.killed)) {
|
||||
// Event canceled? Nothing to do here.
|
||||
log_debug("[%p] leave (canceled)", (void*)cotask_active());
|
||||
return;
|
||||
|
@ -53,7 +55,7 @@ TASK(wait_event_test, { Enemy *e; int rounds; int delay; int cnt; int cnt_inc; }
|
|||
|
||||
// Event signaled. Since this is an enemy death event, e will be invalid
|
||||
// in the next frame. Let's save its position while we can.
|
||||
complex pos = ARGS.e->pos;
|
||||
complex pos = e->pos;
|
||||
|
||||
while(ARGS.rounds--) {
|
||||
WAIT(ARGS.delay);
|
||||
|
@ -81,8 +83,7 @@ TASK_WITH_FINALIZER(test_enemy, {
|
|||
double hp; complex pos; complex dir;
|
||||
struct { int x; } for_finalizer;
|
||||
}) {
|
||||
Enemy *e = create_enemy1c(ARGS.pos, ARGS.hp, BigFairy, NULL, 0);
|
||||
TASK_BIND(e);
|
||||
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, ARGS.hp, BigFairy, NULL, 0));
|
||||
|
||||
INVOKE_TASK_WHEN(&e->events.killed, common_drop_items, &e->pos, {
|
||||
.life_fragment = 1,
|
||||
|
@ -91,7 +92,7 @@ TASK_WITH_FINALIZER(test_enemy, {
|
|||
.points = 5,
|
||||
});
|
||||
|
||||
INVOKE_TASK(wait_event_test, e, 3, 10, 15, 3);
|
||||
INVOKE_TASK(wait_event_test, ENT_BOX(e), 3, 10, 15, 3);
|
||||
|
||||
for(;;) {
|
||||
YIELD;
|
||||
|
@ -122,7 +123,7 @@ TASK_WITH_FINALIZER(test_enemy, {
|
|||
.max_viewport_dist = 128,
|
||||
);
|
||||
|
||||
INVOKE_TASK(laserize_proj, p, 40);
|
||||
INVOKE_TASK(laserize_proj, ENT_BOX(p), 40);
|
||||
}
|
||||
|
||||
WAIT(2);
|
||||
|
@ -137,7 +138,7 @@ TASK_FINALIZER(test_enemy) {
|
|||
log_debug("finalizer called (x = %i)", ARGS.for_finalizer.x);
|
||||
}
|
||||
|
||||
TASK(stage_main, { int ignored; }) {
|
||||
TASK(stage_main, NO_ARGS) {
|
||||
YIELD;
|
||||
|
||||
stage_wait(30);
|
||||
|
@ -152,7 +153,7 @@ TASK(stage_main, { int ignored; }) {
|
|||
}
|
||||
|
||||
static void cotest_begin(void) {
|
||||
INVOKE_TASK(stage_main, 0);
|
||||
INVOKE_TASK(stage_main);
|
||||
}
|
||||
|
||||
StageProcs corotest_procs = {
|
||||
|
|
|
@ -1237,14 +1237,178 @@ static int proj_rotate(Projectile *p, int t) {
|
|||
}
|
||||
#endif
|
||||
|
||||
TASK(burst_fairy, { complex pos; complex dir; }) {
|
||||
Enemy *e = create_enemy1c(ARGS.pos, 700, Fairy, stage1_burst, ARGS.dir);
|
||||
TASK_BIND_UNBOXED(e);
|
||||
}
|
||||
|
||||
struct circletoss_shared {
|
||||
complex velocity;
|
||||
complex exit_accel;
|
||||
int exit_time;
|
||||
};
|
||||
|
||||
TASK(circletoss_move, { BoxedEnemy e; struct circletoss_shared *shared; }) {
|
||||
Enemy *e = TASK_BIND(ARGS.e);
|
||||
|
||||
for(int i = 0;; ++i) {
|
||||
e->pos += ARGS.shared->velocity;
|
||||
|
||||
if(i >= ARGS.shared->exit_time) {
|
||||
ARGS.shared->velocity += ARGS.shared->exit_accel;
|
||||
}
|
||||
|
||||
YIELD;
|
||||
}
|
||||
}
|
||||
|
||||
TASK(circletoss_shoot_circle, { BoxedEnemy e; int duration; int interval; struct circletoss_shared *shared; }) {
|
||||
Enemy *e = TASK_BIND(ARGS.e);
|
||||
|
||||
int cnt = ARGS.duration / ARGS.interval;
|
||||
double angle_step = 2 * M_PI / cnt;
|
||||
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
play_loop("shot1_loop");
|
||||
ARGS.shared->velocity *= 0.8;
|
||||
|
||||
complex aim = cdir(angle_step * i);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_rice,
|
||||
.pos = e->pos,
|
||||
.color = RGB(0.6, 0.2, 0.7),
|
||||
.move = move_asymptotic_simple(2 * aim, i * 0.5),
|
||||
);
|
||||
|
||||
WAIT(ARGS.interval);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(circletoss_shoot_toss, { BoxedEnemy e; int times; int duration; int period; }) {
|
||||
Enemy *e = TASK_BIND(ARGS.e);
|
||||
|
||||
while(ARGS.times--) {
|
||||
for(int i = ARGS.duration; i--;) {
|
||||
play_loop("shot1_loop");
|
||||
|
||||
double aim_angle = carg(global.plr.pos - e->pos);
|
||||
aim_angle += 0.05 * global.diff * nfrand();
|
||||
|
||||
complex aim = cdir(aim_angle);
|
||||
aim *= 1 + frand() * 2;
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_thickrice,
|
||||
.pos = e->pos,
|
||||
.color = RGB(0.2, 0.4, 0.8),
|
||||
.move = move_asymptotic_simple(aim, 3),
|
||||
);
|
||||
|
||||
WAIT(1);
|
||||
}
|
||||
|
||||
WAIT(ARGS.period - ARGS.duration);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(circletoss_fairy, { complex pos; complex velocity; complex exit_accel; int exit_time; }) {
|
||||
Enemy *e = TASK_BIND_UNBOXED(create_enemy1c(ARGS.pos, 1500, BigFairy, NULL, 0));
|
||||
|
||||
struct circletoss_shared shared = {
|
||||
.velocity = ARGS.velocity,
|
||||
.exit_accel = 0.03 * ARGS.exit_accel - 0.04 * I,
|
||||
.exit_time = ARGS.exit_time,
|
||||
};
|
||||
|
||||
INVOKE_TASK(circletoss_move, ENT_BOX(e), &shared);
|
||||
|
||||
INVOKE_TASK_DELAYED(60, circletoss_shoot_circle, ENT_BOX(e),
|
||||
.duration = 40,
|
||||
.interval = 2 + (global.diff < D_Hard),
|
||||
.shared = &shared
|
||||
);
|
||||
|
||||
if(global.diff > D_Easy) {
|
||||
INVOKE_TASK_DELAYED(90, circletoss_shoot_toss, ENT_BOX(e),
|
||||
.times = 4,
|
||||
.period = 150,
|
||||
.duration = 5 + 7 * global.diff
|
||||
);
|
||||
}
|
||||
|
||||
for(;;) YIELD;
|
||||
}
|
||||
|
||||
// opening. projectile bursts
|
||||
TASK(burst_fairies_1, NO_ARGS) {
|
||||
for(int i = 3; i--;) {
|
||||
INVOKE_TASK(burst_fairy, VIEWPORT_W/2 + 70, 1 + 0.6*I);
|
||||
INVOKE_TASK(burst_fairy, VIEWPORT_W/2 - 70, -1 + 0.6*I);
|
||||
stage_wait(25);
|
||||
}
|
||||
}
|
||||
|
||||
// more bursts. fairies move / \ like
|
||||
TASK(burst_fairies_2, NO_ARGS) {
|
||||
for(int i = 3; i--;) {
|
||||
double ofs = 70 + i * 40;
|
||||
INVOKE_TASK(burst_fairy, ofs, 1 + 0.6*I);
|
||||
stage_wait(15);
|
||||
INVOKE_TASK(burst_fairy, VIEWPORT_W - ofs, -1 + 0.6*I);
|
||||
stage_wait(15);
|
||||
}
|
||||
}
|
||||
|
||||
// swirl, sine pass
|
||||
TASK(sinepass_swirls, NO_ARGS) {
|
||||
const int cnt = 32;
|
||||
|
||||
for(int i = 0; i < cnt; ++i) {
|
||||
tsrand_fill(2);
|
||||
create_enemy2c(VIEWPORT_W*(i&1) + afrand(0)*100.0*I + 70.0*I, 100, Swirl, stage1_sinepass, 3.5*(1-2*(i&1)), afrand(1)*7.0*I);
|
||||
stage_wait(20);
|
||||
}
|
||||
}
|
||||
|
||||
// big fairies, circle + projectile toss
|
||||
TASK(circletoss_fairies_1, NO_ARGS) {
|
||||
for(int i = 0; i < 2; ++i) {
|
||||
INVOKE_TASK(circletoss_fairy,
|
||||
.pos = VIEWPORT_W * i + VIEWPORT_H / 3 * I,
|
||||
.velocity = 2 - 4 * i - 0.3 * I,
|
||||
.exit_accel = 1 - 2 * i,
|
||||
.exit_time = (global.diff > D_Easy) ? 500 : 240
|
||||
);
|
||||
|
||||
stage_wait(50);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(stage_timeline, NO_ARGS) {
|
||||
stage_start_bgm("stage1");
|
||||
stage_set_voltage_thresholds(50, 125, 300, 600);
|
||||
YIELD;
|
||||
|
||||
stage_wait(100);
|
||||
INVOKE_TASK(burst_fairies_1);
|
||||
stage_wait(140);
|
||||
INVOKE_TASK(burst_fairies_2);
|
||||
stage_wait(200);
|
||||
INVOKE_TASK(sinepass_swirls);
|
||||
stage_wait(20);
|
||||
INVOKE_TASK(circletoss_fairies_1);
|
||||
}
|
||||
|
||||
void stage1_events(void) {
|
||||
TIMER(&global.timer);
|
||||
|
||||
AT(0) {
|
||||
stage_start_bgm("stage1");
|
||||
stage_set_voltage_thresholds(50, 125, 300, 600);
|
||||
INVOKE_TASK(stage_timeline);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
#ifdef BULLET_TEST
|
||||
if(!global.projs) {
|
||||
PROJECTILE(
|
||||
|
|
Loading…
Reference in a new issue