Coroutinize ReimuB (#205)
This commit is contained in:
parent
b56f3eeb54
commit
b6a6496c39
11 changed files with 678 additions and 518 deletions
18
src/entity.h
18
src/entity.h
|
@ -34,6 +34,8 @@ typedef enum DrawLayer {
|
|||
// NOTE: you can bit-or a drawlayer_low_t value with one of the LAYER_x constants
|
||||
// for sub-layer ordering.
|
||||
|
||||
typedef struct CustomEntity CustomEntity;
|
||||
|
||||
#define ENT_TYPES \
|
||||
ENT_TYPE(Projectile, ENT_PROJECTILE) \
|
||||
ENT_TYPE(Laser, ENT_LASER) \
|
||||
|
@ -41,6 +43,7 @@ typedef enum DrawLayer {
|
|||
ENT_TYPE(Boss, ENT_BOSS) \
|
||||
ENT_TYPE(Player, ENT_PLAYER) \
|
||||
ENT_TYPE(Item, ENT_ITEM) \
|
||||
ENT_TYPE(CustomEntity, ENT_CUSTOM) \
|
||||
|
||||
typedef enum EntityType {
|
||||
_ENT_TYPE_ENUM_BEGIN,
|
||||
|
@ -111,7 +114,11 @@ struct EntityInterface {
|
|||
ENTITY_INTERFACE_BASE(EntityInterface);
|
||||
};
|
||||
|
||||
INLINE const char* ent_type_name(EntityType type) {
|
||||
struct CustomEntity {
|
||||
ENTITY_INTERFACE(CustomEntity);
|
||||
};
|
||||
|
||||
INLINE const char *ent_type_name(EntityType type) {
|
||||
switch(type) {
|
||||
#define ENT_TYPE(typename, id) case id: return #id;
|
||||
ENT_TYPES
|
||||
|
@ -123,7 +130,7 @@ INLINE const char* ent_type_name(EntityType type) {
|
|||
#define ENT_TYPE_ID(typename) (_ENT_TYPEID_##typename)
|
||||
|
||||
#ifdef USE_GNU_EXTENSIONS
|
||||
#define ENT_CAST(ent, typename) (__extension__ ({ \
|
||||
#define _internal_ENT_CAST(ent, typename, ent_type_id) (__extension__ ({ \
|
||||
__auto_type _ent = ent; \
|
||||
static_assert(__builtin_types_compatible_p(EntityInterface, __typeof__(*(_ent))), \
|
||||
"Expression is not an EntityInterface pointer"); \
|
||||
|
@ -131,15 +138,18 @@ INLINE const char* ent_type_name(EntityType type) {
|
|||
#typename " doesn't implement EntityInterface"); \
|
||||
static_assert(__builtin_offsetof(typename, entity_interface) == 0, \
|
||||
"entity_interface has non-zero offset in " #typename); \
|
||||
IF_DEBUG(if(_ent->type != _ENT_TYPEID_##typename) { \
|
||||
IF_DEBUG(if(_ent->type != ent_type_id) { \
|
||||
log_fatal("Invalid entity cast from %s to " #typename, ent_type_name(_ent->type)); \
|
||||
}); \
|
||||
CASTPTR_ASSUME_ALIGNED(_ent, typename); \
|
||||
}))
|
||||
#else
|
||||
#define ENT_CAST(ent, typename) CASTPTR_ASSUME_ALIGNED(ent, typename)
|
||||
#define _internal_ENT_CAST(ent, typename, ent_type_id) CASTPTR_ASSUME_ALIGNED(ent, typename)
|
||||
#endif
|
||||
|
||||
#define ENT_CAST(ent, typename) _internal_ENT_CAST(ent, typename, ENT_TYPE_ID(typename))
|
||||
#define ENT_CAST_CUSTOM(ent, typename) _internal_ENT_CAST(ent, typename, _ENT_TYPEID_CustomEntity)
|
||||
|
||||
void ent_init(void);
|
||||
void ent_shutdown(void);
|
||||
void ent_register(EntityInterface *ent, EntityType type) attr_nonnull(1);
|
||||
|
|
13
src/player.c
13
src/player.c
|
@ -55,8 +55,6 @@ void player_stage_post_init(Player *plr) {
|
|||
assert(plr->mode != NULL);
|
||||
|
||||
// ensure the essential callbacks are there. other code tests only for the optional ones
|
||||
assert(plr->mode->procs.shot != NULL);
|
||||
assert(plr->mode->procs.bomb != NULL);
|
||||
assert(plr->mode->procs.property != NULL);
|
||||
assert(plr->mode->character != NULL);
|
||||
assert(plr->mode->dialog != NULL);
|
||||
|
@ -626,7 +624,9 @@ DEFINE_TASK(player_logic) {
|
|||
|
||||
if(player_should_shoot(plr, false)) {
|
||||
coevent_signal(&plr->events.shoot);
|
||||
plr->mode->procs.shot(plr);
|
||||
if(plr->mode->procs.shot) {
|
||||
plr->mode->procs.shot(plr);
|
||||
}
|
||||
}
|
||||
|
||||
if(global.frames == plr->deathtime) {
|
||||
|
@ -655,7 +655,12 @@ static bool player_bomb(Player *plr) {
|
|||
// player_cancel_powersurge(plr);
|
||||
// stage_clear_hazards(CLEAR_HAZARDS_ALL);
|
||||
|
||||
plr->mode->procs.bomb(plr);
|
||||
coevent_signal(&plr->events.bomb_used);
|
||||
|
||||
if(plr->mode->procs.bomb) {
|
||||
plr->mode->procs.bomb(plr);
|
||||
}
|
||||
|
||||
plr->bombs--;
|
||||
|
||||
if(plr->deathtime >= global.frames) {
|
||||
|
|
|
@ -118,7 +118,8 @@ struct Player {
|
|||
COEVENTS_ARRAY(
|
||||
shoot,
|
||||
inputflags_changed,
|
||||
power_changed
|
||||
power_changed,
|
||||
bomb_used
|
||||
) events;
|
||||
|
||||
uint64_t points;
|
||||
|
|
|
@ -52,8 +52,17 @@ double reimu_common_property(Player *plr, PlrProperty prop) {
|
|||
UNREACHABLE;
|
||||
}
|
||||
|
||||
Projectile *reimu_common_ofuda_swawn_trail(Projectile *p, ProjectileList *dest) {
|
||||
return PARTICLE(
|
||||
TASK(reimu_ofuda_trail, { BoxedProjectile trail; }) {
|
||||
Projectile *trail = TASK_BIND(ARGS.trail);
|
||||
|
||||
for(;;) {
|
||||
trail->color.g *= 0.95;
|
||||
YIELD;
|
||||
}
|
||||
}
|
||||
|
||||
Projectile *reimu_common_ofuda_swawn_trail(Projectile *p) {
|
||||
Projectile *trail = PARTICLE(
|
||||
// .sprite_ptr = p->sprite,
|
||||
.sprite_ptr = get_sprite("proj/hghost"),
|
||||
.color = &p->color,
|
||||
|
@ -63,8 +72,11 @@ Projectile *reimu_common_ofuda_swawn_trail(Projectile *p, ProjectileList *dest)
|
|||
.draw_rule = pdraw_timeout_scalefade(1, 2, 1, 0),
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
.flags = PFLAG_NOREFLECT,
|
||||
.dest = dest,
|
||||
);
|
||||
|
||||
// TODO maybe replace this with something better looking and less task-hungry
|
||||
INVOKE_TASK(reimu_ofuda_trail, ENT_BOX(trail));
|
||||
return trail;
|
||||
}
|
||||
|
||||
void reimu_common_draw_yinyang(Enemy *e, int t, const Color *c) {
|
||||
|
@ -117,3 +129,22 @@ void reimu_common_bomb_buffer_init(void) {
|
|||
cfg.tex_params.wrap.t = TEX_WRAP_MIRROR;
|
||||
bomb_buffer = stage_add_background_framebuffer("Reimu bomb FB", 0.25, 1, 1, &cfg);
|
||||
}
|
||||
|
||||
DEFINE_EXTERN_TASK(reimu_common_slave_expire) {
|
||||
Enemy *e = TASK_BIND(ARGS.slave);
|
||||
cotask_cancel(cotask_unbox(ARGS.slave_main_task));
|
||||
|
||||
cmplx pos0 = e->pos;
|
||||
real retract_time = ARGS.retract_time;
|
||||
e->move = (MoveParams) { 0 };
|
||||
|
||||
Player *plr;
|
||||
|
||||
for(int i = 1; i <= retract_time; ++i) {
|
||||
YIELD;
|
||||
plr = NOT_NULL(ENT_UNBOX(ARGS.player));
|
||||
e->pos = clerp(pos0, plr->pos, i / retract_time);
|
||||
}
|
||||
|
||||
delete_enemy(&plr->slaves, e);
|
||||
}
|
||||
|
|
|
@ -20,9 +20,16 @@ extern PlayerMode plrmode_reimu_b;
|
|||
|
||||
double reimu_common_property(Player *plr, PlrProperty prop);
|
||||
int reimu_common_ofuda(Projectile *p, int t);
|
||||
Projectile *reimu_common_ofuda_swawn_trail(Projectile *p, ProjectileList *dest);
|
||||
Projectile *reimu_common_ofuda_swawn_trail(Projectile *p);
|
||||
void reimu_common_draw_yinyang(Enemy *e, int t, const Color *c);
|
||||
void reimu_common_bomb_bg(Player *p, float alpha);
|
||||
void reimu_common_bomb_buffer_init(void);
|
||||
|
||||
DECLARE_EXTERN_TASK(reimu_common_slave_expire, {
|
||||
BoxedPlayer player;
|
||||
BoxedEnemy slave;
|
||||
BoxedTask slave_main_task;
|
||||
int retract_time;
|
||||
});
|
||||
|
||||
#endif // IGUARD_plrmodes_reimu_h
|
||||
|
|
|
@ -413,16 +413,7 @@ static void reimu_spirit_bomb_bg(Player *p) {
|
|||
colorfill(0, 0.05 * alpha, 0.1 * alpha, alpha * 0.5);
|
||||
}
|
||||
|
||||
TASK(reimu_spirit_ofuda_trail, { BoxedProjectile ofuda; }) {
|
||||
Projectile *p = TASK_BIND_UNBOXED(reimu_common_ofuda_swawn_trail(NOT_NULL(ENT_UNBOX(ARGS.ofuda)), NULL));
|
||||
|
||||
for(;;) {
|
||||
p->color.g *= 0.95;
|
||||
YIELD;
|
||||
}
|
||||
}
|
||||
|
||||
TASK(reimu_spirit_ofuda, { cmplx pos; cmplx vel; real damage; }) {
|
||||
TASK(reimu_spirit_ofuda, { cmplx pos; cmplx vel; real damage; ShaderProgram *shader; }) {
|
||||
Projectile *ofuda = TASK_BIND_UNBOXED(PROJECTILE(
|
||||
.proto = pp_ofuda,
|
||||
.pos = ARGS.pos,
|
||||
|
@ -430,11 +421,11 @@ TASK(reimu_spirit_ofuda, { cmplx pos; cmplx vel; real damage; }) {
|
|||
.move = move_linear(ARGS.vel),
|
||||
.type = PROJ_PLAYER,
|
||||
.damage = ARGS.damage,
|
||||
.shader = "sprite_particle",
|
||||
.shader_ptr = ARGS.shader,
|
||||
));
|
||||
|
||||
for(int t = 0;; ++t) {
|
||||
INVOKE_TASK(reimu_spirit_ofuda_trail, ENT_BOX(ofuda));
|
||||
reimu_common_ofuda_swawn_trail(ofuda);
|
||||
YIELD;
|
||||
}
|
||||
}
|
||||
|
@ -454,25 +445,9 @@ static void reimu_spirit_yinyang_unfocused_visual(Enemy *e, int t, bool render)
|
|||
}
|
||||
}
|
||||
|
||||
TASK(reimu_spirit_slave_expire, { ReimuAController *ctrl; BoxedEnemy e; BoxedTask main_task; }) {
|
||||
Enemy *e = TASK_BIND(ARGS.e);
|
||||
Player *plr = ARGS.ctrl->plr;
|
||||
cotask_cancel(cotask_unbox(ARGS.main_task));
|
||||
|
||||
cmplx pos0 = e->pos;
|
||||
real retract_time = ORB_RETRACT_TIME;
|
||||
e->move = (MoveParams) { 0 };
|
||||
|
||||
for(int i = 1; i <= retract_time; ++i) {
|
||||
YIELD;
|
||||
e->pos = clerp(pos0, plr->pos, i / retract_time);
|
||||
}
|
||||
|
||||
delete_enemy(&plr->slaves, e);
|
||||
}
|
||||
|
||||
static Enemy *reimu_spirit_create_slave(ReimuAController *ctrl, EnemyVisualRule visual) {
|
||||
Player *plr = ctrl->plr;
|
||||
|
||||
Enemy *e = create_enemy_p(
|
||||
&plr->slaves,
|
||||
plr->pos,
|
||||
|
@ -480,8 +455,16 @@ static Enemy *reimu_spirit_create_slave(ReimuAController *ctrl, EnemyVisualRule
|
|||
visual,
|
||||
NULL, 0, 0, 0, 0
|
||||
);
|
||||
|
||||
e->ent.draw_layer = LAYER_PLAYER_SLAVE;
|
||||
INVOKE_TASK_WHEN(&ctrl->events.slaves_expired, reimu_spirit_slave_expire, ctrl, ENT_BOX(e), THIS_TASK);
|
||||
|
||||
INVOKE_TASK_WHEN(&ctrl->events.slaves_expired, reimu_common_slave_expire,
|
||||
.player = ENT_BOX(plr),
|
||||
.slave = ENT_BOX(e),
|
||||
.slave_main_task = THIS_TASK,
|
||||
.retract_time = ORB_RETRACT_TIME
|
||||
);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
@ -763,6 +746,7 @@ TASK(reimu_spirit_shot_forward, { ReimuAController *ctrl; }) {
|
|||
ReimuAController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
real dir = 1;
|
||||
ShaderProgram *shader = r_shader_get("sprite_particle");
|
||||
|
||||
for(;;) {
|
||||
WAIT_EVENT_OR_DIE(&plr->events.shoot);
|
||||
|
@ -770,7 +754,8 @@ TASK(reimu_spirit_shot_forward, { ReimuAController *ctrl; }) {
|
|||
INVOKE_TASK(reimu_spirit_ofuda,
|
||||
.pos = plr->pos + 10 * dir - 15.0*I,
|
||||
.vel = -20*I,
|
||||
.damage = SHOT_FORWARD_DMG
|
||||
.damage = SHOT_FORWARD_DMG,
|
||||
.shader = shader
|
||||
);
|
||||
dir = -dir;
|
||||
WAIT(SHOT_FORWARD_DELAY);
|
||||
|
@ -787,7 +772,6 @@ TASK(reimu_spirit_shot_volley_bullet, { Player *plr; cmplx offset; cmplx vel; re
|
|||
.move = move_linear(ARGS.vel),
|
||||
.type = PROJ_PLAYER,
|
||||
.damage = ARGS.damage,
|
||||
.shader = "sprite_particle",
|
||||
.shader_ptr = ARGS.shader,
|
||||
);
|
||||
}
|
||||
|
@ -807,7 +791,7 @@ TASK(reimu_spirit_shot_volley, { ReimuAController *ctrl; }) {
|
|||
real spread = M_PI/32 * (1 + 0.35 * pwr);
|
||||
|
||||
INVOKE_SUBTASK_DELAYED(delay, reimu_spirit_shot_volley_bullet,
|
||||
plr,
|
||||
.plr = plr,
|
||||
.offset = -I + 5,
|
||||
.vel = -18 * I * cdir(spread),
|
||||
.damage = damage,
|
||||
|
@ -815,7 +799,7 @@ TASK(reimu_spirit_shot_volley, { ReimuAController *ctrl; }) {
|
|||
);
|
||||
|
||||
INVOKE_SUBTASK_DELAYED(delay, reimu_spirit_shot_volley_bullet,
|
||||
plr,
|
||||
.plr = plr,
|
||||
.offset = -I - 5,
|
||||
.vel = -18 * I * cdir(-spread),
|
||||
.damage = damage,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -60,6 +60,8 @@ static struct {
|
|||
|
||||
ManagedFramebufferGroup *mfb_group;
|
||||
|
||||
StageDrawEvents events;
|
||||
|
||||
#ifdef DEBUG
|
||||
Sprite dummy;
|
||||
#endif
|
||||
|
@ -340,9 +342,12 @@ void stage_draw_init(void) {
|
|||
events_register_handler(&(EventHandler) {
|
||||
stage_draw_event, NULL, EPRIO_SYSTEM,
|
||||
});
|
||||
|
||||
COEVENT_INIT_ARRAY(stagedraw.events);
|
||||
}
|
||||
|
||||
void stage_draw_shutdown(void) {
|
||||
COEVENT_CANCEL_ARRAY(stagedraw.events);
|
||||
events_unregister_handler(stage_draw_event);
|
||||
stage_draw_destroy_framebuffers();
|
||||
}
|
||||
|
@ -997,6 +1002,8 @@ void stage_draw_scene(StageInfo *stage) {
|
|||
draw_framebuffer_tex(background->front, VIEWPORT_W, VIEWPORT_H);
|
||||
r_state_pop();
|
||||
|
||||
coevent_signal(&stagedraw.events.background_drawn);
|
||||
|
||||
// draw bomb background
|
||||
// FIXME: we need a more flexible and consistent way for entities to hook
|
||||
// into the various stages of scene drawing code.
|
||||
|
@ -1811,3 +1818,7 @@ void stage_display_clear_screen(const StageClearBonus *bonus) {
|
|||
|
||||
stagedraw.clear_screen.target_alpha = 1;
|
||||
}
|
||||
|
||||
StageDrawEvents *stage_get_draw_events(void) {
|
||||
return &stagedraw.events;
|
||||
}
|
||||
|
|
|
@ -22,21 +22,29 @@ typedef enum StageFBPair {
|
|||
NUM_FBPAIRS,
|
||||
} StageFBPair;
|
||||
|
||||
typedef COEVENTS_ARRAY(
|
||||
background_drawn
|
||||
) StageDrawEvents;
|
||||
|
||||
void stage_draw_pre_init(void);
|
||||
void stage_draw_init(void);
|
||||
void stage_draw_shutdown(void);
|
||||
|
||||
StageDrawEvents *stage_get_draw_events(void);
|
||||
|
||||
void stage_draw_hud(void);
|
||||
void stage_draw_viewport(void);
|
||||
void stage_draw_overlay(void);
|
||||
void stage_draw_scene(StageInfo *stage);
|
||||
void stage_draw_bottom_text(void);
|
||||
bool stage_should_draw_particle(Projectile *p);
|
||||
|
||||
void stage_display_clear_screen(const StageClearBonus *bonus);
|
||||
|
||||
void stage_draw_begin_noshake(void);
|
||||
void stage_draw_end_noshake(void);
|
||||
|
||||
bool stage_should_draw_particle(Projectile *p);
|
||||
|
||||
void stage_display_clear_screen(const StageClearBonus *bonus);
|
||||
|
||||
FBPair *stage_get_fbpair(StageFBPair id) attr_returns_nonnull;
|
||||
Framebuffer *stage_add_foreground_framebuffer(const char *label, float scale_worst, float scale_best, uint num_attachments, FBAttachmentConfig attachments[num_attachments]);
|
||||
Framebuffer *stage_add_background_framebuffer(const char *label, float scale_worst, float scale_best, uint num_attachments, FBAttachmentConfig attachments[num_attachments]);
|
||||
|
|
|
@ -113,6 +113,13 @@ cmplx cclampabs(cmplx c, double maxabs) {
|
|||
return c;
|
||||
}
|
||||
|
||||
cmplx cwclamp(cmplx c, cmplx cmin, cmplx cmax) {
|
||||
return CMPLX(
|
||||
clamp(creal(c), creal(cmin), creal(cmax)),
|
||||
clamp(cimag(c), cimag(cmin), cimag(cmax))
|
||||
);
|
||||
}
|
||||
|
||||
cmplx cdir(double angle) {
|
||||
// this is faster than cexp(I*angle)
|
||||
|
||||
|
@ -133,6 +140,10 @@ cmplx32 cwmulf(cmplx32 c0, cmplx32 c1) {
|
|||
return CMPLXF(crealf(c0)*crealf(c1), cimagf(c0)*cimagf(c1));
|
||||
}
|
||||
|
||||
cmplx cswap(cmplx c) {
|
||||
return CMPLX(cimag(c), creal(c));
|
||||
}
|
||||
|
||||
double psin(double x) {
|
||||
return 0.5 + 0.5 * sin(x);
|
||||
}
|
||||
|
|
|
@ -42,9 +42,11 @@ float fapproach_asymptotic_p(float *val, float target, float rate, float epsilon
|
|||
cmplx capproach_asymptotic_p(cmplx *val, cmplx target, double rate, double epsilon);
|
||||
cmplx cnormalize(cmplx c) attr_const;
|
||||
cmplx cclampabs(cmplx c, double maxabs) attr_const;
|
||||
cmplx cwclamp(cmplx c, cmplx cmin, cmplx cmax) attr_const;
|
||||
cmplx cdir(double angle) attr_const;
|
||||
cmplx cwmul(cmplx c0, cmplx c1) attr_const;
|
||||
cmplx32 cwmulf(cmplx32 c0, cmplx32 c1) attr_const;
|
||||
cmplx cswap(cmplx c) attr_const;
|
||||
double psin(double) attr_const;
|
||||
double pcos(double) attr_const;
|
||||
float psinf(float) attr_const;
|
||||
|
|
Loading…
Reference in a new issue