use custom ents for Reimu; cleanup Marisa; etc.

This commit is contained in:
Andrei Alexeyev 2020-04-06 04:29:11 +03:00
parent 4223b023dc
commit 45a4283225
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
9 changed files with 145 additions and 147 deletions

View file

@ -173,6 +173,11 @@ DEFINE_EXTERN_TASK(common_charge) {
);
}
DEFINE_EXTERN_TASK(common_set_bitflags) {
assume(ARGS.pflags != NULL);
*ARGS.pflags = ((*ARGS.pflags & ARGS.mask) | ARGS.set);
}
cmplx common_wander(cmplx origin, double dist, Rect bounds) {
int attempts = 32;
double angle;

View file

@ -72,4 +72,13 @@ INLINE Rect viewport_bounds(double margin) {
cmplx common_wander(cmplx origin, double dist, Rect bounds);
DECLARE_EXTERN_TASK(
common_set_bitflags,
{
uint *pflags;
uint mask;
uint set;
}
);
#endif // IGUARD_common_tasks_h

View file

@ -1733,3 +1733,14 @@ cmplx plrutil_homing_target(cmplx org, cmplx fallback) {
return target;
}
void plrutil_slave_retract(BoxedPlayer bplr, cmplx *pos, real retract_time) {
cmplx pos0 = *pos;
Player *plr;
for(int i = 1; i <= retract_time; ++i) {
YIELD;
plr = NOT_NULL(ENT_UNBOX(bplr));
*pos = clerp(pos0, plr->pos, i / retract_time);
}
}

View file

@ -243,4 +243,6 @@ void player_preload(void);
// FIXME: where should this be?
cmplx plrutil_homing_target(cmplx org, cmplx fallback);
void plrutil_slave_retract(BoxedPlayer bplr, cmplx *pos, real retract_time);
#endif // IGUARD_player_h

View file

@ -18,6 +18,8 @@
#define SHOT_FORWARD_DELAY 6
#define SHOT_LASER_DAMAGE 10
#define HAKKERO_RETRACT_TIME 4
typedef struct MarisaAController MarisaAController;
typedef struct MarisaLaser MarisaLaser;
typedef struct MarisaSlave MarisaSlave;
@ -453,18 +455,8 @@ TASK(marisa_laser_slave, {
YIELD;
}
CoTask *t = cotask_unbox(shot_task);
if(t) {
cotask_cancel(t);
}
real retract_time = 4;
cmplx pos0 = slave->pos;
for(int i = 1; i <= retract_time; ++i) {
slave->pos = clerp(pos0, plr->pos, i / retract_time);
YIELD;
}
CANCEL_TASK(shot_task);
plrutil_slave_retract(ENT_BOX(plr), &slave->pos, HAKKERO_RETRACT_TIME);
}
static real marisa_laser_masterspark_width(real progress) {

View file

@ -79,17 +79,6 @@ Projectile *reimu_common_ofuda_swawn_trail(Projectile *p) {
return trail;
}
void reimu_common_draw_yinyang(Enemy *e, int t, const Color *c) {
r_draw_sprite(&(SpriteParams) {
.sprite = "yinyang",
.shader = "sprite_yinyang",
.pos = { creal(e->pos), cimag(e->pos) },
.rotation.angle = global.frames * -6 * DEG2RAD,
// .color = rgb(0.95, 0.75, 1.0),
.color = c,
});
}
static void capture_frame(Framebuffer *dest, Framebuffer *src) {
r_state_push();
r_framebuffer(dest);
@ -129,22 +118,3 @@ 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);
}

View file

@ -19,17 +19,8 @@ extern PlayerMode plrmode_reimu_a;
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);
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

View file

@ -12,6 +12,7 @@
#include "plrmodes.h"
#include "reimu.h"
#include "stagedraw.h"
#include "common_tasks.h"
#define SHOT_FORWARD_DMG 50
#define SHOT_FORWARD_DELAY 3
@ -31,6 +32,16 @@ typedef struct ReimuAController {
) events;
} ReimuAController;
typedef struct ReimuSlave ReimuSlave;
struct ReimuSlave {
ENTITY_INTERFACE_NAMED(ReimuSlave, ent);
Sprite *sprite;
ShaderProgram *shader;
cmplx pos;
Color color;
uint alive;
};
static void reimu_spirit_preload(void) {
const int flags = RESF_DEFAULT;
@ -447,50 +458,47 @@ TASK(reimu_spirit_ofuda, { cmplx pos; cmplx vel; real damage; ShaderProgram *sha
}
}
static void reimu_spirit_yinyang_focused_visual(Enemy *e, int t, bool render) {
if(render) {
reimu_common_draw_yinyang(e, t, RGB(1.0, 0.8, 0.8));
}
static void reimu_spirit_draw_slave(EntityInterface *ent) {
ReimuSlave *slave = ENT_CAST_CUSTOM(ent, ReimuSlave);
r_draw_sprite(&(SpriteParams) {
.color = &slave->color,
.pos.as_cmplx = slave->pos,
.rotation.angle = global.frames * -6 * DEG2RAD,
.shader_ptr = slave->shader,
.sprite_ptr = slave->sprite,
});
}
static void reimu_spirit_yinyang_unfocused_visual(Enemy *e, int t, bool render) {
if(render) {
reimu_common_draw_yinyang(e, t, RGB(0.95, 0.75, 1.0));
}
}
static Enemy *reimu_spirit_create_slave(ReimuAController *ctrl, EnemyVisualRule visual) {
static ReimuSlave *reimu_spirit_create_slave(ReimuAController *ctrl, const Color *color) {
Player *plr = ctrl->plr;
ReimuSlave *slave = TASK_HOST_CUSTOM_ENT(ReimuSlave);
slave->sprite = get_sprite("yinyang");
slave->shader = r_shader_get("sprite_yinyang");
slave->pos = plr->pos;
slave->color = *color;
slave->ent.draw_layer = LAYER_PLAYER_SLAVE;
slave->ent.draw_func = reimu_spirit_draw_slave;
slave->alive = 1;
Enemy *e = create_enemy_p(
&plr->slaves,
plr->pos,
ENEMY_IMMUNE,
visual,
NULL, 0, 0, 0, 0
INVOKE_SUBTASK_WHEN(&ctrl->events.slaves_expired, common_set_bitflags,
.pflags = &slave->alive,
.mask = 0, .set = 0
);
e->ent.draw_layer = LAYER_PLAYER_SLAVE;
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;
return slave;
}
TASK(reimu_slave_shot_particles, {
BoxedEnemy e;
BoxedEntity slave;
int num;
cmplx dir;
Color *color0;
Color *color1;
Sprite *sprite;
}) {
Enemy *e = TASK_BIND(ARGS.e);
ReimuSlave *slave = TASK_BIND_CUSTOM(ARGS.slave, ReimuSlave);
Color color0 = *ARGS.color0;
Color color1 = *ARGS.color1;
int num = ARGS.num;
@ -509,7 +517,7 @@ TASK(reimu_slave_shot_particles, {
.draw_rule = draw,
.flags = PFLAG_NOREFLECT | PFLAG_MANUALANGLE,
.move = move,
.pos = e->pos,
.pos = slave->pos,
.sprite_ptr = sprite,
.timeout = timeout,
);
@ -519,10 +527,10 @@ TASK(reimu_slave_shot_particles, {
TASK(reimu_spirit_slave_needle_shot, {
ReimuAController *ctrl;
BoxedEnemy e;
BoxedEntity e;
}) {
Player *plr = ARGS.ctrl->plr;
Enemy *e = TASK_BIND(ARGS.e);
ReimuSlave *slave = TASK_BIND_CUSTOM(ARGS.e, ReimuSlave);
ShaderProgram *shader = r_shader_get("sprite_particle");
Sprite *particle_spr = get_sprite("part/stain");
@ -534,7 +542,7 @@ TASK(reimu_spirit_slave_needle_shot, {
WAIT_EVENT_OR_DIE(&plr->events.shoot);
INVOKE_SUBTASK(reimu_slave_shot_particles,
.e = ENT_BOX(e),
.slave = ENT_BOX_CUSTOM(slave),
.color0 = RGBA(0.5, 0, 0, 0),
.color1 = RGBA(0.5, 0.25, 0, 0),
.num = delay,
@ -543,7 +551,7 @@ TASK(reimu_spirit_slave_needle_shot, {
);
INVOKE_TASK(reimu_spirit_needle,
.pos = e->pos - 25.0*I,
.pos = slave->pos - 25.0*I,
.vel = -20*I,
.damage = damage,
.shader = shader
@ -561,32 +569,35 @@ TASK(reimu_spirit_slave_needle, {
}) {
ReimuAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
Enemy *e = TASK_BIND_UNBOXED(reimu_spirit_create_slave(ctrl, reimu_spirit_yinyang_focused_visual));
ReimuSlave *slave = reimu_spirit_create_slave(ctrl, RGB(1.0, 0.8, 0.8));
INVOKE_SUBTASK(reimu_spirit_slave_needle_shot, ctrl, ENT_BOX(e));
INVOKE_SUBTASK(reimu_spirit_slave_needle_shot, ctrl, ENT_BOX_CUSTOM(slave));
real dist = ARGS.distance;
cmplx offset = dist * cdir(ARGS.rotation_offset);
cmplx rot = cdir(ARGS.rotation);
real target_speed = 0.005 * dist;
e->move = move_towards(plr->pos + offset, 0);
MoveParams move = move_towards(plr->pos + offset, 0);
for(;;) {
YIELD;
e->move.attraction = approach(e->move.attraction, target_speed, target_speed / 12.0);
e->move.attraction_point = plr->pos + offset;
do {
move.attraction = approach(move.attraction, target_speed, target_speed / 12.0);
move.attraction_point = plr->pos + offset;
offset *= rot;
}
move_update(&slave->pos, &move);
YIELD;
} while(slave->alive);
plrutil_slave_retract(ENT_BOX(plr), &slave->pos, ORB_RETRACT_TIME);
}
TASK(reimu_spirit_slave_homing_shot, {
ReimuAController *ctrl;
BoxedEnemy e;
BoxedEntity slave;
cmplx vel;
}) {
Player *plr = ARGS.ctrl->plr;
Enemy *e = TASK_BIND(ARGS.e);
ReimuSlave *slave = TASK_BIND_CUSTOM(ARGS.slave, ReimuSlave);
ShaderProgram *shader = r_shader_get("sprite_particle");
Sprite *particle_spr = get_sprite("part/stain");
@ -600,7 +611,7 @@ TASK(reimu_spirit_slave_homing_shot, {
WAIT_EVENT_OR_DIE(&plr->events.shoot);
INVOKE_SUBTASK(reimu_slave_shot_particles,
.e = ENT_BOX(e),
.slave = ENT_BOX_CUSTOM(slave),
.color0 = RGBA(0.5, 0.125, 0, 0),
.color1 = RGBA(0.5, 0.125, 0.25, 0),
.num = delay,
@ -609,7 +620,7 @@ TASK(reimu_spirit_slave_homing_shot, {
);
INVOKE_TASK(reimu_spirit_homing,
.pos = e->pos,
.pos = slave->pos,
.vel = vel,
.damage = damage,
.shader = shader
@ -626,19 +637,22 @@ TASK(reimu_spirit_slave_homing, {
}) {
ReimuAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
Enemy *e = TASK_BIND_UNBOXED(reimu_spirit_create_slave(ctrl, reimu_spirit_yinyang_unfocused_visual));
ReimuSlave *slave = reimu_spirit_create_slave(ctrl, RGB(0.95, 0.75, 1.0));
INVOKE_SUBTASK(reimu_spirit_slave_homing_shot, ctrl, ENT_BOX(e), ARGS.shot_vel);
INVOKE_SUBTASK(reimu_spirit_slave_homing_shot, ctrl, ENT_BOX_CUSTOM(slave), ARGS.shot_vel);
cmplx offset = ARGS.offset;
real target_speed = 0.005 * cabs(offset);
e->move = move_towards(plr->pos + offset, 0);
MoveParams move = move_towards(plr->pos + offset, 0);
for(;;) {
do {
move.attraction = approach(move.attraction, target_speed, target_speed / 12.0);
move.attraction_point = plr->pos + offset;
move_update(&slave->pos, &move);
YIELD;
e->move.attraction = approach(e->move.attraction, target_speed, target_speed / 12.0);
e->move.attraction_point = plr->pos + offset;
}
} while(slave->alive);
plrutil_slave_retract(ENT_BOX(plr), &slave->pos, ORB_RETRACT_TIME);
}
static void reimu_spirit_spawn_slaves_unfocused(ReimuAController *ctrl, int power_rank) {
@ -726,10 +740,8 @@ TASK(reimu_spirit_focus_handler, { ReimuAController *ctrl; }) {
// focus state changed?
while((prev_inputflags ^ plr->inputflags) & INFLAG_FOCUS) {
if(plr->slaves.first) {
reimu_spirit_kill_slaves(ctrl);
WAIT(ORB_RETRACT_TIME);
}
reimu_spirit_kill_slaves(ctrl);
WAIT(ORB_RETRACT_TIME);
// important to record these at the time of respawning
prev_inputflags = plr->inputflags;
@ -826,18 +838,17 @@ TASK(reimu_spirit_shot_volley, { ReimuAController *ctrl; }) {
}
TASK(reimu_spirit_controller, { BoxedPlayer plr; }) {
ReimuAController ctrl = { 0 };
ctrl.plr = TASK_BIND(ARGS.plr);
COEVENT_INIT_ARRAY(ctrl.events);
ReimuAController *ctrl = TASK_MALLOC(sizeof(ReimuAController));
ctrl->plr = TASK_BIND(ARGS.plr);
TASK_HOST_EVENTS(ctrl->events);
INVOKE_SUBTASK(reimu_spirit_focus_handler, &ctrl);
INVOKE_SUBTASK(reimu_spirit_power_handler, &ctrl);
INVOKE_SUBTASK(reimu_spirit_shot_forward, &ctrl);
INVOKE_SUBTASK(reimu_spirit_shot_volley, &ctrl);
INVOKE_SUBTASK(reimu_spirit_bomb_handler, &ctrl);
INVOKE_SUBTASK(reimu_spirit_focus_handler, ctrl);
INVOKE_SUBTASK(reimu_spirit_power_handler, ctrl);
INVOKE_SUBTASK(reimu_spirit_shot_forward, ctrl);
INVOKE_SUBTASK(reimu_spirit_shot_volley, ctrl);
INVOKE_SUBTASK(reimu_spirit_bomb_handler, ctrl);
WAIT_EVENT(&TASK_EVENTS(THIS_TASK)->finished);
COEVENT_CANCEL_ARRAY(ctrl.events);
STALL;
}
static void reimu_spirit_init(Player *plr) {

View file

@ -12,6 +12,7 @@
#include "plrmodes.h"
#include "reimu.h"
#include "stagedraw.h"
#include "common_tasks.h"
#define SHOT_FORWARD_DMG 60
#define SHOT_FORWARD_DELAY 6
@ -42,6 +43,7 @@
#define NUM_GAPS 4
typedef struct ReimuGap ReimuGap;
typedef struct ReimuSlave ReimuSlave;
typedef struct ReimuBController ReimuBController;
struct ReimuGap {
@ -51,6 +53,14 @@ struct ReimuGap {
cmplx parallel_axis; // normalized, parallel the gap, perpendicular to orientation
};
struct ReimuSlave {
ENTITY_INTERFACE_NAMED(ReimuSlave, ent);
Sprite *sprite;
ShaderProgram *shader;
cmplx pos;
uint alive;
};
struct ReimuBController {
ENTITY_INTERFACE_NAMED(ReimuBController, gap_renderer);
Player *plr;
@ -359,20 +369,16 @@ static void reimu_dream_bullet_warp(ReimuBController *ctrl, Projectile *p, int *
}
}
static void reimu_dream_slave_visual(Enemy *e, int t, bool render) {
if(render) {
r_draw_sprite(&(SpriteParams) {
.sprite = "yinyang",
.shader = "sprite_yinyang",
.pos = {
creal(e->pos),
cimag(e->pos),
},
.rotation.angle = global.frames * -6 * DEG2RAD,
.color = RGB(0.95, 0.75, 1.0),
.scale.both = 0.5,
});
}
static void reimu_dream_draw_slave(EntityInterface *ent) {
ReimuSlave *slave = ENT_CAST_CUSTOM(ent, ReimuSlave);
r_draw_sprite(&(SpriteParams) {
.sprite_ptr = slave->sprite,
.shader_ptr = slave->shader,
.pos.as_cmplx = slave->pos,
.rotation.angle = global.frames * -6 * DEG2RAD,
.color = RGB(0.95, 0.75, 1.0),
.scale.both = 0.5,
});
}
TASK(reimu_dream_needle, {
@ -415,12 +421,12 @@ TASK(reimu_dream_needle, {
TASK(reimu_dream_slave_shot, {
ReimuBController *ctrl;
BoxedEnemy e;
BoxedEntity slave;
cmplx vel;
}) {
ReimuBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
Enemy *e = TASK_BIND(ARGS.e);
ReimuSlave *slave = TASK_BIND_CUSTOM(ARGS.slave, ReimuSlave);
cmplx vel = ARGS.vel;
ShaderProgram *shader = r_shader_get("sprite_particle");
@ -429,7 +435,7 @@ TASK(reimu_dream_slave_shot, {
WAIT(SHOT_SLAVE_PRE_DELAY);
INVOKE_TASK(reimu_dream_needle,
.ctrl = ctrl,
.pos = e->pos,
.pos = slave->pos,
.vel = (plr->inputflags & INFLAG_FOCUS) ? cswap(vel) : vel,
.shader = shader
);
@ -445,16 +451,17 @@ TASK(reimu_dream_slave, {
}) {
ReimuBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
Enemy *e = TASK_BIND_UNBOXED(create_enemy_p(&plr->slaves, 0, ENEMY_IMMUNE, reimu_dream_slave_visual, NULL, 0, 0, 0, 0));
ReimuSlave *slave = TASK_HOST_CUSTOM_ENT(ReimuSlave);
slave->ent.draw_layer = LAYER_PLAYER_SLAVE;
slave->ent.draw_func = reimu_dream_draw_slave;
slave->sprite = get_sprite("yinyang"),
slave->shader = r_shader_get("sprite_yinyang"),
slave->pos = plr->pos;
slave->alive = 1;
e->ent.draw_layer = LAYER_PLAYER_SLAVE;
e->pos = plr->pos;
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
INVOKE_SUBTASK_WHEN(&ctrl->events.slaves_expired, common_set_bitflags,
.pflags = &slave->alive,
.mask = 0, .set = 0
);
real angle = ARGS.angle_offset + M_PI/2;
@ -462,17 +469,17 @@ TASK(reimu_dream_slave, {
INVOKE_SUBTASK(reimu_dream_slave_shot,
.ctrl = ctrl,
.e = ENT_BOX(e),
.slave = ENT_BOX_CUSTOM(slave),
.vel = 20 * ARGS.shot_dir
);
for(;;) {
do {
cmplx ofs = (plr->inputflags & INFLAG_FOCUS) ? cswap(offset) : offset;
ofs = cwmul(ofs, cdir(angle));
capproach_asymptotic_p(&e->pos, plr->pos + ofs, 0.5, 1e-5);
capproach_asymptotic_p(&slave->pos, plr->pos + ofs, 0.5, 1e-5);
angle += 0.1;
YIELD;
}
} while(slave->alive);
}
static void reimu_dream_respawn_slaves(ReimuBController *ctrl, int num_slaves) {