Coroutinize MarisaA (#206)

This commit is contained in:
Andrei Alexeyev 2020-04-01 23:08:40 +03:00 committed by GitHub
parent 7b445fbc77
commit 431237f0f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 489 additions and 367 deletions

View file

@ -418,4 +418,7 @@ ENT_TYPES
#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)
#define TASK_BIND_CUSTOM(box, type) ENT_CAST_CUSTOM((cotask_bind_to_entity)(cotask_active(), ENT_UNBOX(box)), type)
#define TASK_BIND_UNBOXED_CUSTOM(ent, type) ENT_CAST_CUSTOM((cotask_bind_to_entity)(cotask_active(), &(ent)->entity_interface), type)
#endif // IGUARD_coroutine_h

View file

@ -138,7 +138,7 @@ 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_type_id) { \
IF_DEBUG(if(_ent && _ent->type != ent_type_id) { \
log_fatal("Invalid entity cast from %s to " #typename, ent_type_name(_ent->type)); \
}); \
CASTPTR_ASSUME_ALIGNED(_ent, typename); \
@ -228,6 +228,9 @@ INLINE BoxedEntity _ent_boxed_passthrough_helper_Entity(BoxedEntity box) { retur
#define ENT_UNBOX(box) ENT_BOXED_DISPATCH_FUNCTION(_ent_unbox_, box)
#define ENT_BOX_OR_PASSTHROUGH(ent) ENT_MIXED_DISPATCH_FUNCTION(_ent_box_, _ent_boxed_passthrough_helper_, ent)
#define ENT_BOX_CUSTOM(ent) _ent_box_Entity(&(ent)->entity_interface)
#define ENT_UNBOX_CUSTOM(box, type) ENT_CAST_CUSTOM(_ent_unbox_Entity(box), type)
typedef struct BoxedEntityArray {
BoxedEntity *array;
uint capacity;

View file

@ -400,12 +400,11 @@ static void player_fail_spell(Player *plr) {
}
}
bool player_should_shoot(Player *plr, bool extra) {
bool player_should_shoot(Player *plr) {
return
(plr->inputflags & INFLAG_SHOT) &&
!dialog_is_active(global.dialog) &&
player_is_alive(&global.plr)
/* && (!extra || !player_is_bomb_active(plr)) */;
player_is_alive(&global.plr);
}
void player_placeholder_bomb_logic(Player *plr) {
@ -622,7 +621,7 @@ DEFINE_TASK(player_logic) {
plr->mode->procs.think(plr);
}
if(player_should_shoot(plr, false)) {
if(player_should_shoot(plr)) {
coevent_signal(&plr->events.shoot);
if(plr->mode->procs.shot) {
plr->mode->procs.shot(plr);

View file

@ -193,7 +193,7 @@ void player_free(Player *plr);
void player_draw_overlay(Player *plr);
void player_logic(Player *plr);
bool player_should_shoot(Player *plr, bool extra);
bool player_should_shoot(Player *plr);
bool player_set_power(Player *plr, short npow);
bool player_add_power(Player *plr, short pdelta);

View file

@ -14,50 +14,61 @@
#include "renderer/api.h"
#include "stagedraw.h"
// args are pain
static int _laser_renderer_ref;
#define laser_renderer ((Enemy*)REF(_laser_renderer_ref))
#define SHOT_FORWARD_DAMAGE 100
#define SHOT_FORWARD_DELAY 6
#define SHOT_LASER_DAMAGE 10
typedef struct MarisaLaserData {
typedef struct MarisaAController MarisaAController;
typedef struct MarisaLaser MarisaLaser;
typedef struct MarisaSlave MarisaSlave;
typedef struct MasterSpark MasterSpark;
struct MarisaSlave {
ENTITY_INTERFACE_NAMED(MarisaAController, ent);
Sprite *sprite;
ShaderProgram *shader;
cmplx pos;
real lean;
real shot_angle;
real flare_alpha;
bool alive;
};
struct MarisaLaser {
LIST_INTERFACE(MarisaLaser);
cmplx pos;
struct {
cmplx first;
cmplx last;
} trace_hit;
cmplx prev_pos;
float lean;
} MarisaLaserData;
real alpha;
};
static void draw_laser_beam(cmplx src, cmplx dst, double size, double step, double t, Texture *tex, Uniform *u_length) {
cmplx dir = dst - src;
cmplx center = (src + dst) * 0.5;
struct MasterSpark {
ENTITY_INTERFACE_NAMED(MarisaAController, ent);
cmplx pos;
cmplx dir;
real alpha;
};
r_mat_mv_push();
struct MarisaAController {
ENTITY_INTERFACE_NAMED(MarisaAController, laser_renderer);
r_mat_mv_translate(creal(center), cimag(center), 0);
r_mat_mv_rotate(carg(dir), 0, 0, 1);
r_mat_mv_scale(cabs(dir), size, 1);
Player *plr;
LIST_ANCHOR(MarisaLaser) lasers;
r_mat_tex_push_identity();
r_mat_tex_translate(-cimag(src) / step + t, 0, 0);
r_mat_tex_scale(cabs(dir) / step, 1, 1);
COEVENTS_ARRAY(
slaves_expired
) events;
};
r_uniform_sampler("tex", tex);
r_uniform_float(u_length, cabs(dir) / step);
r_draw_quad();
r_mat_tex_pop();
r_mat_mv_pop();
}
static void trace_laser(Enemy *e, cmplx vel, float damage) {
static void trace_laser(MarisaLaser *laser, cmplx vel, real damage) {
ProjCollisionResult col;
ProjectileList lproj = { .first = NULL };
MarisaLaserData *ld = REF(e->args[3]);
PROJECTILE(
.dest = &lproj,
.pos = e->pos,
.pos = laser->pos,
.size = 28*(1+I),
.type = PROJ_PLAYER,
.damage = damage,
@ -81,7 +92,7 @@ static void trace_laser(Enemy *e, cmplx vel, float damage) {
struct enemy_col *c = NULL;
if(!first_found) {
ld->trace_hit.first = col.location;
laser->trace_hit.first = col.location;
first_found = true;
}
@ -125,7 +136,7 @@ static void trace_laser(Enemy *e, cmplx vel, float damage) {
enemy_collisions[i].enemy->hp = enemy_collisions[i].original_hp;
}
ld->trace_hit.last = col.location;
laser->trace_hit.last = col.location;
}
static float set_alpha(Uniform *u_alpha, float a) {
@ -140,59 +151,49 @@ static float set_alpha_dimmed(Uniform *u_alpha, float a) {
return set_alpha(u_alpha, a * a * 0.5);
}
static void marisa_laser_slave_visual(Enemy *e, int t, bool render) {
if(!render) {
marisa_common_slave_visual(e, t, render);
return;
}
float laser_alpha = laser_renderer->args[0];
static void marisa_laser_draw_slave(EntityInterface *ent) {
MarisaSlave *slave = ENT_CAST_CUSTOM(ent, MarisaSlave);
ShaderCustomParams shader_params;
shader_params.color = *RGBA(0.2, 0.4, 0.5, laser_renderer->args[1] * 0.75);
shader_params.color = *RGBA(0.2, 0.4, 0.5, slave->flare_alpha * 0.75);
float t = global.frames;
r_draw_sprite(&(SpriteParams) {
.sprite = "hakkero",
.shader = "sprite_hakkero",
.pos = { creal(e->pos), cimag(e->pos) },
.rotation.angle = t * 0.05,
.color = color_lerp(RGB(0.2, 0.4, 0.5), RGB(1.0, 1.0, 1.0), 0.25 * pow(psin(t / 6.0), 2) * laser_renderer->args[1]),
.sprite_ptr = slave->sprite,
.shader_ptr = slave->shader,
.pos.as_cmplx = slave->pos,
.rotation.angle = t * 0.05f,
.color = color_lerp(RGB(0.2, 0.4, 0.5), RGB(1.0, 1.0, 1.0), 0.25 * powf(psinf(t / 6.0f), 2.0f) * slave->flare_alpha),
.shader_params = &shader_params,
});
if(laser_alpha <= 0) {
return;
}
MarisaLaserData *ld = REF(e->args[3]);
r_draw_sprite(&(SpriteParams) {
.sprite = "part/smoothdot",
.color = RGBA_MUL_ALPHA(1, 1, 1, laser_alpha),
.pos = { creal(ld->trace_hit.first), cimag(ld->trace_hit.first) },
});
}
static void marisa_laser_fader_visual(Enemy *e, int t, bool render) {
static void draw_laser_beam(cmplx src, cmplx dst, real size, real step, real t, Texture *tex, Uniform *u_length) {
cmplx dir = dst - src;
cmplx center = (src + dst) * 0.5;
r_mat_mv_push();
r_mat_mv_translate(creal(center), cimag(center), 0);
r_mat_mv_rotate(carg(dir), 0, 0, 1);
r_mat_mv_scale(cabs(dir), size, 1);
r_mat_tex_push_identity();
r_mat_tex_translate(-cimag(src) / step + t, 0, 0);
r_mat_tex_scale(cabs(dir) / step, 1, 1);
r_uniform_sampler("tex", tex);
r_uniform_float(u_length, cabs(dir) / step);
r_draw_quad();
r_mat_tex_pop();
r_mat_mv_pop();
}
static float get_laser_alpha(Enemy *e, float a) {
if(e->visual_rule == marisa_laser_fader_visual) {
return e->args[1];
}
static void marisa_laser_draw_lasers(EntityInterface *ent) {
MarisaAController *ctrl = ENT_CAST_CUSTOM(ent, MarisaAController);
real t = global.frames;
return min(a, min(1, (global.frames - e->birthtime) * 0.1));
}
#define FOR_EACH_SLAVE(e) for(Enemy *e = global.plr.slaves.first; e; e = e->next) if(e->visual_rule == marisa_laser_fader_visual || e->visual_rule == marisa_laser_slave_visual)
#define FOR_EACH_REAL_SLAVE(e) FOR_EACH_SLAVE(e) if(e->visual_rule == marisa_laser_slave_visual)
static void marisa_laser_renderer_visual(Enemy *renderer, int t, bool render) {
if(!render) {
return;
}
double a = creal(renderer->args[0]);
ShaderProgram *shader = r_shader_get("marisa_laser");
Uniform *u_clr0 = r_shader_uniform(shader, "color0");
Uniform *u_clr1 = r_shader_uniform(shader, "color1");
@ -219,10 +220,9 @@ static void marisa_laser_renderer_visual(Enemy *renderer, int t, bool render) {
BLENDFACTOR_SRC_COLOR, BLENDFACTOR_ONE, BLENDOP_MAX
));
FOR_EACH_SLAVE(e) {
if(set_alpha(u_alpha, get_laser_alpha(e, a))) {
MarisaLaserData *ld = REF(e->args[3]);
draw_laser_beam(e->pos, ld->trace_hit.last, 32, 128, -0.02 * t, tex1, u_length);
for(MarisaLaser *laser = ctrl->lasers.first; laser; laser = laser->next) {
if(set_alpha(u_alpha, laser->alpha)) {
draw_laser_beam(laser->pos, laser->trace_hit.last, 32, 128, -0.02 * t, tex1, u_length);
}
}
@ -248,73 +248,44 @@ static void marisa_laser_renderer_visual(Enemy *renderer, int t, bool render) {
r_uniform_vec4(u_clr0, 0.5, 0.0, 0.0, 0.0);
r_uniform_vec4(u_clr1, 1.0, 0.0, 0.0, 0.0);
FOR_EACH_SLAVE(e) {
if(set_alpha_dimmed(u_alpha, get_laser_alpha(e, a))) {
MarisaLaserData *ld = REF(e->args[3]);
draw_laser_beam(e->pos, ld->trace_hit.first, 40, 128, t * -0.12, tex0, u_length);
for(MarisaLaser *laser = ctrl->lasers.first; laser; laser = laser->next) {
if(set_alpha_dimmed(u_alpha, laser->alpha)) {
draw_laser_beam(laser->pos, laser->trace_hit.first, 40, 128, t * -0.12, tex0, u_length);
}
}
r_uniform_vec4(u_clr0, 2.0, 1.0, 2.0, 0.0);
r_uniform_vec4(u_clr1, 0.1, 0.1, 1.0, 0.0);
FOR_EACH_SLAVE(e) {
if(set_alpha_dimmed(u_alpha, get_laser_alpha(e, a))) {
MarisaLaserData *ld = REF(e->args[3]);
draw_laser_beam(e->pos, ld->trace_hit.first, 42, 200, t * -0.03, tex0, u_length);
for(MarisaLaser *laser = ctrl->lasers.first; laser; laser = laser->next) {
if(set_alpha_dimmed(u_alpha, laser->alpha)) {
draw_laser_beam(laser->pos, laser->trace_hit.first, 42, 200, t * -0.03, tex0, u_length);
}
}
}
static int marisa_laser_fader(Enemy *e, int t) {
if(t == EVENT_DEATH) {
MarisaLaserData *ld = REF(e->args[3]);
free(ld);
free_ref(e->args[3]);
return ACTION_DESTROY;
SpriteParams sp = {
.sprite_ptr = get_sprite("part/smoothdot"),
.shader_ptr = r_shader_get("sprite_default"),
};
for(MarisaLaser *laser = ctrl->lasers.first; laser; laser = laser->next) {
float a = laser->alpha * 0.8f;
cmplx spread;
sp.pos.as_cmplx = laser->trace_hit.first;
spread = rng_dir(); spread *= rng_range(0, 3);
sp.pos.as_cmplx += spread;
sp.color = color_mul_scalar(RGBA(0.8, 0.3, 0.5, 0), a);
r_draw_sprite(&sp);
spread = rng_dir(); spread *= rng_range(0, 3);
sp.pos.as_cmplx += spread;
sp.color = color_mul_scalar(RGBA(0.2, 0.7, 0.5, 0), a);
r_draw_sprite(&sp);
}
e->args[1] = approach(e->args[1], 0.0, 0.1);
if(creal(e->args[1]) == 0) {
return ACTION_DESTROY;
}
return 1;
}
static Enemy* spawn_laser_fader(Enemy *e, double alpha) {
MarisaLaserData *ld = calloc(1, sizeof(MarisaLaserData));
memcpy(ld, (MarisaLaserData*)REF(e->args[3]), sizeof(MarisaLaserData));
return create_enemy_p(&global.plr.slaves, e->pos, ENEMY_IMMUNE, marisa_laser_fader_visual, marisa_laser_fader,
e->args[0], alpha, e->args[2], add_ref(ld));
}
static int marisa_laser_renderer(Enemy *renderer, int t) {
if(player_should_shoot(&global.plr, true) && renderer->next) {
renderer->args[0] = approach(renderer->args[0], 1.0, 0.2);
renderer->args[1] = approach(renderer->args[1], 1.0, 0.2);
renderer->args[2] = 1;
} else {
if(creal(renderer->args[2])) {
FOR_EACH_REAL_SLAVE(e) {
spawn_laser_fader(e, renderer->args[0]);
}
renderer->args[2] = 0;
}
renderer->args[0] = 0;
renderer->args[1] = approach(renderer->args[1], 0.0, 0.1);
}
return 1;
}
#undef FOR_EACH_SLAVE
#undef FOR_EACH_REAL_SLAVE
static void marisa_laser_flash_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
SpriteParamsBuffer spbuf;
SpriteParams sp = projectile_sprite_params(p, &spbuf);
@ -324,108 +295,220 @@ static void marisa_laser_flash_draw(Projectile *p, int t, ProjDrawRuleArgs args)
r_draw_sprite(&sp);
}
static int marisa_laser_slave(Enemy *e, int t) {
if(t == EVENT_BIRTH) {
return 1;
}
if(t == EVENT_DEATH) {
if(!(global.gameover > 0) && laser_renderer && creal(laser_renderer->args[0])) {
spawn_laser_fader(e, laser_renderer->args[0]);
}
MarisaLaserData *ld = REF(e->args[3]);
free_ref(e->args[3]);
free(ld);
return 1;
}
if(t < 0) {
return 1;
}
cmplx target_pos = global.plr.pos + (1 - global.plr.focus/30.0)*e->pos0 + (global.plr.focus/30.0)*e->args[0];
e->pos += (target_pos - e->pos) * 0.5;
MarisaLaserData *ld = REF(e->args[3]);
cmplx pdelta = e->pos - ld->prev_pos;
ld->prev_pos = e->pos;
ld->lean += (-0.01 * creal(pdelta) - ld->lean) * 0.2;
if(player_should_shoot(&global.plr, true)) {
float angle = creal(e->args[2]);
float f = smoothreclamp(global.plr.focus, 0, 30, 0, 1);
f = smoothreclamp(f, 0, 1, 0, 1);
float factor = (1.0 + 0.7 * psin(t/15.0)) * -(1-f) * !!angle;
cmplx dir = -cexp(I*(angle*factor + ld->lean + M_PI/2));
trace_laser(e, 5 * dir, creal(e->args[1]));
Animation *fire = get_ani("fire");
AniSequence *seq = get_ani_sequence(fire, "main");
Sprite *spr = animation_get_frame(fire, seq, global.frames);
PARTICLE(
.sprite_ptr = spr,
.size = 1+I,
.pos = e->pos + dir * 10,
.color = color_mul_scalar(RGBA(2, 0.2, 0.5, 0), 0.2),
.rule = linear,
.draw_rule = marisa_laser_flash_draw,
.timeout = 8,
.args = { dir, 0, 0.6 + 0.2*I, },
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
.scale = 0.4,
// .layer = LAYER_PARTICLE_LOW,
);
}
return 1;
TASK(marisa_laser_slave_cleanup, { BoxedEntity slave; }) {
MarisaSlave *slave = TASK_BIND_CUSTOM(ARGS.slave, MarisaSlave);
ent_unregister(&slave->ent);
}
static float masterspark_width(void) {
float t = player_get_bomb_progress(&global.plr);
float w = 1;
TASK(marisa_laser_slave_expire, { BoxedEntity slave; }) {
MarisaSlave *slave = TASK_BIND_CUSTOM(ARGS.slave, MarisaSlave);
slave->alive = false;
}
if(t < 1./6) {
w = t*6;
static void marisa_laser_show_laser(MarisaAController *ctrl, MarisaLaser *laser) {
alist_append(&ctrl->lasers, laser);
}
static void marisa_laser_hide_laser(MarisaAController *ctrl, MarisaLaser *laser) {
alist_unlink(&ctrl->lasers, laser);
}
TASK(marisa_laser_fader, {
MarisaAController *ctrl;
const MarisaLaser *ref_laser;
}) {
MarisaAController *ctrl = ARGS.ctrl;
MarisaLaser fader = *ARGS.ref_laser;
marisa_laser_show_laser(ctrl, &fader);
while(fader.alpha > 0) {
YIELD;
approach_p(&fader.alpha, 0, 0.1);
}
marisa_laser_hide_laser(ctrl, &fader);
}
static void marisa_laser_fade_laser(MarisaAController *ctrl, MarisaLaser *laser) {
marisa_laser_hide_laser(ctrl, laser);
INVOKE_TASK(marisa_laser_fader, ctrl, laser);
}
TASK(marisa_laser_slave_shot_cleanup, {
MarisaAController *ctrl;
MarisaLaser **active_laser;
}) {
MarisaLaser *active_laser = *ARGS.active_laser;
if(active_laser) {
marisa_laser_fade_laser(ARGS.ctrl, active_laser);
}
}
TASK(marisa_laser_slave_shot, {
MarisaAController *ctrl;
BoxedEntity slave;
}) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
MarisaSlave *slave = TASK_BIND_CUSTOM(ARGS.slave, MarisaSlave);
Animation *fire_anim = get_ani("fire");
AniSequence *fire_anim_seq = get_ani_sequence(fire_anim, "main");
MarisaLaser *active_laser = NULL;
INVOKE_TASK_AFTER(&TASK_EVENTS(THIS_TASK)->finished, marisa_laser_slave_shot_cleanup, ctrl, &active_laser);
for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.shoot);
assert(player_should_shoot(plr));
// We started shooting - register a laser for rendering
MarisaLaser laser = { 0 };
marisa_laser_show_laser(ctrl, &laser);
active_laser = &laser;
while(player_should_shoot(plr)) {
real angle = slave->shot_angle * (1.0 + 0.7 * psin(global.frames/15.0));
laser.pos = slave->pos;
approach_p(&laser.alpha, 1.0, 0.2);
cmplx dir = -cdir(angle + slave->lean + M_PI/2);
trace_laser(&laser, 5 * dir, SHOT_LASER_DAMAGE);
Sprite *spr = animation_get_frame(fire_anim, fire_anim_seq, global.frames);
PARTICLE(
.sprite_ptr = spr,
.size = 1+I,
.pos = laser.pos + dir * 10,
.color = color_mul_scalar(RGBA(2, 0.2, 0.5, 0), 0.2),
.rule = linear,
.draw_rule = marisa_laser_flash_draw,
.timeout = 8,
.args = { dir, 0, 0.6 + 0.2*I, },
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
.scale = 0.4,
);
YIELD;
}
// We stopped shooting - remove the laser, spawn fader
marisa_laser_fade_laser(ctrl, &laser);
active_laser = NULL;
}
}
TASK(marisa_laser_slave, {
MarisaAController *ctrl;
cmplx unfocused_offset;
cmplx focused_offset;
real shot_angle;
}) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
MarisaSlave slave = { 0 };
slave.shader = r_shader_get("sprite_hakkero");
slave.sprite = get_sprite("hakkero");
slave.ent.draw_layer = LAYER_PLAYER_SLAVE;
slave.ent.draw_func = marisa_laser_draw_slave;
slave.pos = plr->pos;
slave.alive = true;
ent_register(&slave.ent, ENT_CUSTOM);
INVOKE_TASK_AFTER(&TASK_EVENTS(THIS_TASK)->finished, marisa_laser_slave_cleanup, ENT_BOX_CUSTOM(&slave));
INVOKE_TASK_WHEN(&ctrl->events.slaves_expired, marisa_laser_slave_expire, ENT_BOX_CUSTOM(&slave));
BoxedTask shot_task = cotask_box(INVOKE_SUBTASK(marisa_laser_slave_shot,
.ctrl = ctrl,
.slave = ENT_BOX_CUSTOM(&slave)
));
/* unfocused focused */
cmplx offsets[] = { ARGS.unfocused_offset, ARGS.focused_offset, };
real shot_angles[] = { ARGS.shot_angle, 0 };
real formation_switch_rate = 0.3;
real lean_rate = 0.1;
real follow_rate = 0.5;
real lean_strength = 0.01;
real epsilon = 1e-5;
cmplx offset = 0;
cmplx prev_pos = slave.pos;
while(slave.alive) {
bool focused = plr->inputflags & INFLAG_FOCUS;
approach_asymptotic_p(&slave.shot_angle, shot_angles[focused], formation_switch_rate, epsilon);
capproach_asymptotic_p(&offset, offsets[focused], formation_switch_rate, epsilon);
capproach_asymptotic_p(&slave.pos, plr->pos + offset, follow_rate, epsilon);
approach_asymptotic_p(&slave.lean, -lean_strength * creal(slave.pos - prev_pos), lean_rate, epsilon);
prev_pos = slave.pos;
if(player_should_shoot(plr)) {
approach_asymptotic_p(&slave.flare_alpha, 1.0, 0.2, epsilon);
} else {
approach_asymptotic_p(&slave.flare_alpha, 0.0, 0.08, epsilon);
}
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;
}
ent_unregister(&slave.ent);
}
static real marisa_laser_masterspark_width(real progress) {
real w = 1;
if(progress < 1./6) {
w = progress * 6;
w = pow(w, 1.0/3.0);
}
if(t > 4./5) {
w = 1-t*5 + 4;
if(progress > 4./5) {
w = 1 - progress * 5 + 4;
w = pow(w, 5);
}
return w;
}
static void masterspark_visual(Enemy *e, int t, bool render) {
if(!render) {
return;
}
float fade = masterspark_width();
marisa_common_masterspark_draw(1, &(MarisaBeamInfo){global.plr.pos - 30 * I, 800 + I * VIEWPORT_H * 1.25, carg(e->args[0]), t}, fade);
static void marisa_laser_draw_masterspark(EntityInterface *ent) {
MasterSpark *ms = ENT_CAST_CUSTOM(ent, MasterSpark);
marisa_common_masterspark_draw(1, &(MarisaBeamInfo) {
ms->pos,
800 + I * VIEWPORT_H * 1.25,
carg(ms->dir),
global.frames
}, ms->alpha);
}
static int masterspark_star(Projectile *p, int t) {
if(t >= 0) {
p->args[0] += 0.1*p->args[0]/cabs(p->args[0]);
p->angle += 0.1;
}
return linear(p, t);
}
static void masterspark_damage(Enemy *e) {
static void marisa_laser_masterspark_damage(MasterSpark *ms) {
// lazy inefficient approximation of the beam parabola
float r = 96 * masterspark_width();
float r = 96 * ms->alpha;
float growth = 0.25;
cmplx v = e->args[0] * cexp(-I*M_PI*0.5);
cmplx p = global.plr.pos - 30 * I + r * v;
cmplx v = ms->dir * cdir(M_PI * -0.5);
cmplx p = ms->pos + v * r;
Rect vp_rect, seg_rect;
vp_rect.top_left = 0;
@ -451,173 +534,214 @@ static void masterspark_damage(Enemy *e) {
// log_debug("%i", iter);
}
static int masterspark(Enemy *e, int t2) {
// FIXME: This may interact badly with other view shake effects...
// We need a proper system for this stuff.
TASK(marisa_laser_masterspark_cleanup, { BoxedEntity ms; }) {
MasterSpark *ms = TASK_BIND_CUSTOM(ARGS.ms, MasterSpark);
ent_unregister(&ms->ent);
}
TASK(marisa_laser_masterspark, { MarisaAController *ctrl; }) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
MasterSpark ms = { 0 };
ms.dir = 1;
ms.ent.draw_func = marisa_laser_draw_masterspark;
ms.ent.draw_layer = LAYER_PLAYER_FOCUS - 1;
ent_register(&ms.ent, ENT_CUSTOM);
INVOKE_TASK_AFTER(&TASK_EVENTS(THIS_TASK)->finished, marisa_laser_masterspark_cleanup, ENT_BOX_CUSTOM(&ms));
int t = 0;
Sprite *star_spr = get_sprite("part/maristar_orbit");
Sprite *smoke_spr = get_sprite("part/smoke");
do {
real bomb_progress = player_get_bomb_progress(plr);
ms.alpha = marisa_laser_masterspark_width(bomb_progress);
ms.dir *= cdir(0.005 * (creal(plr->velocity) + 2 * rng_sreal()));
ms.pos = plr->pos - 30 * I;
marisa_laser_masterspark_damage(&ms);
if(bomb_progress >= 3.0/4.0) {
global.shake_view = 8 * (1 - bomb_progress * 4 + 3);
goto skip_particles;
}
if(t2 == EVENT_BIRTH) {
global.shake_view = 8;
return 1;
} else if(t2 == EVENT_DEATH) {
global.shake_view = 0;
return 1;
}
if(t2 < 0)
return 1;
uint pflags = PFLAG_NOREFLECT | PFLAG_MANUALANGLE;
e->args[0] *= cexp(I*(0.005*creal(global.plr.velocity) + rng_sreal() * 0.005));
cmplx diroffset = e->args[0];
if(t % 4 == 0) {
pflags |= PFLAG_REQUIREDPARTICLE;
}
float t = player_get_bomb_progress(&global.plr);
cmplx dir = -cdir(1.5 * sin(t * M_PI * 1.12)) * I;
Color *c = HSLA(-bomb_progress * 5.321, 1, 0.5, rng_range(0, 0.5));
cmplx pos = plr->pos + 40 * dir;
if(t >= 3.0/4.0) {
global.shake_view = 8 * (1 - t * 4 + 3);
} else if(t2 % 2 == 0) {
cmplx dir = -cexp(1.5*I*sin(t2*M_PI*1.12))*I;
Color *c = HSLA(-t*5.321, 1, 0.5, rng_range(0, 0.5));
uint flags = PFLAG_NOREFLECT;
if(t2 % 4 == 0) {
flags |= PFLAG_REQUIREDPARTICLE;
for(int i = 0; i < 2; ++i) {
cmplx v = 10 * (dir - I);
PARTICLE(
.angle = rng_angle(),
.angle_delta = 0.1,
.color = c,
.draw_rule = pdraw_timeout_scalefade(0, 5, 1, 0),
.flags = pflags,
.move = move_accelerated(v, 0.1 * cnormalize(v)),
.pos = pos,
.sprite_ptr = star_spr,
.timeout = 50,
);
dir = -conj(dir);
}
PARTICLE(
.sprite = "maristar_orbit",
.pos = global.plr.pos+40*dir,
.color = c,
.rule = masterspark_star,
.sprite_ptr = smoke_spr,
.pos = plr->pos - 40*I,
.color = HSLA(2 * bomb_progress, 1, 2, 0),
.timeout = 50,
.args= { (10 * dir - 10*I)*diroffset },
.angle = rng_angle(),
.draw_rule = pdraw_timeout_scalefade(0, 5, 1, 0),
.flags = flags,
);
dir = -conj(dir);
PARTICLE(
.sprite = "maristar_orbit",
.pos = global.plr.pos+40*dir,
.color = c,
.rule = masterspark_star,
.timeout = 50,
.args = { (10 * dir - 10*I)*diroffset },
.angle = rng_angle(),
.draw_rule = pdraw_timeout_scalefade(0, 5, 1, 0),
.flags = flags,
);
PARTICLE(
.sprite = "smoke",
.pos = global.plr.pos-40*I,
.color = HSLA(2*t,1,2,0), //RGBA(0.3, 0.6, 1, 0),
.timeout = 50,
.move = move_linear(-7*dir + 7*I),
.move = move_linear(7 * (I - dir)),
.angle = rng_angle(),
.draw_rule = pdraw_timeout_scalefade(0, 7, 1, 0),
.flags = flags | PFLAG_MANUALANGLE,
.flags = pflags,
);
skip_particles:
++t;
YIELD;
} while(player_is_bomb_active(plr));
global.shake_view = 0;
while(ms.alpha > 0) {
approach_p(&ms.alpha, 0, 0.2);
YIELD;
}
if(t >= 1 || !player_is_bomb_active(&global.plr)) {
return ACTION_DESTROY;
}
masterspark_damage(e);
return 1;
ent_unregister(&ms.ent);
}
static void marisa_laser_bombbg(Player *plr) {
if(!player_is_bomb_active(plr)) {
return;
}
TASK(marisa_laser_bomb_background, { MarisaAController *ctrl; }) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
CoEvent *draw_event = &stage_get_draw_events()->background_drawn;
float t = player_get_bomb_progress(&global.plr);
float fade = 1;
do {
WAIT_EVENT_OR_DIE(draw_event);
float t = player_get_bomb_progress(plr);
float fade = 1;
if(t < 1./6)
fade = t*6;
if(t > 3./4)
fade = 1-t*4 + 3;
r_color4(0.8 * fade, 0.8 * fade, 0.8 * fade, 0.8 * fade);
fill_viewport(sin(t * 0.3), t * 3 * (1 + t * 3), 1, "marisa_bombbg");
r_color4(1, 1, 1, 1);
}
static void marisa_laser_bomb(Player *plr) {
play_sound("bomb_marisa_a");
Enemy *e = create_enemy_p(&plr->slaves, 0.0*I, ENEMY_BOMB, masterspark_visual, masterspark, 1,0,0,0);
e->ent.draw_layer = LAYER_PLAYER_FOCUS - 1;
}
static Enemy* marisa_laser_spawn_slave(Player *plr, cmplx pos, cmplx a0, cmplx a1, cmplx a2, cmplx a3) {
Enemy *e = create_enemy_p(&plr->slaves, pos, ENEMY_IMMUNE, marisa_laser_slave_visual, marisa_laser_slave, a0, a1, a2, a3);
e->pos = plr->pos;
return e;
}
static void marisa_laser_respawn_slaves(Player *plr, short npow) {
Enemy *e = plr->slaves.first, *tmp;
double dmg = 8;
while(e != 0) {
tmp = e;
e = e->next;
if(tmp->logic_rule == marisa_laser_slave) {
delete_enemy(&plr->slaves, tmp);
if(t < 1./6) {
fade = t*6;
}
if(t > 3./4) {
fade = 1-t*4 + 3;
}
r_color4(0.8 * fade, 0.8 * fade, 0.8 * fade, 0.8 * fade);
fill_viewport(sin(t * 0.3), t * 3 * (1 + t * 3), 1, "marisa_bombbg");
r_color4(1, 1, 1, 1);
} while(player_is_bomb_active(plr));
}
TASK(marisa_laser_bomb_handler, { MarisaAController *ctrl; }) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.bomb_used);
play_sound("bomb_marisa_a");
INVOKE_SUBTASK(marisa_laser_bomb_background, ctrl);
INVOKE_SUBTASK(marisa_laser_masterspark, ctrl);
}
}
static void marisa_laser_respawn_slaves(MarisaAController *ctrl, int power_rank) {
coevent_signal(&ctrl->events.slaves_expired);
if(power_rank == 1) {
INVOKE_TASK(marisa_laser_slave, ctrl, -40.0*I, -40.0*I, 0);
}
if(npow / 100 == 1) {
marisa_laser_spawn_slave(plr, -40.0*I, -40.0*I, dmg, 0, 0);
if(power_rank >= 2) {
INVOKE_TASK(marisa_laser_slave, ctrl, 25-5.0*I, 9-40.0*I, M_PI/30);
INVOKE_TASK(marisa_laser_slave, ctrl, -25-5.0*I, -9-40.0*I, -M_PI/30);
}
if(npow >= 200) {
marisa_laser_spawn_slave(plr, 25-5.0*I, 9-40.0*I, dmg, -M_PI/30, 0);
marisa_laser_spawn_slave(plr, -25-5.0*I, -9-40.0*I, dmg, M_PI/30, 0);
if(power_rank == 3) {
INVOKE_TASK(marisa_laser_slave, ctrl, -30.0*I, -55.0*I, 0);
}
if(npow / 100 == 3) {
marisa_laser_spawn_slave(plr, -30.0*I, -55.0*I, dmg, 0, 0);
if(power_rank >= 4) {
INVOKE_TASK(marisa_laser_slave, ctrl, 17-30.0*I, 18-55.0*I, M_PI/60);
INVOKE_TASK(marisa_laser_slave, ctrl, -17-30.0*I, -18-55.0*I, -M_PI/60);
}
}
if(npow >= 400) {
marisa_laser_spawn_slave(plr, 17-30.0*I, 18-55.0*I, dmg, M_PI/60, 0);
marisa_laser_spawn_slave(plr, -17-30.0*I, -18-55.0*I, dmg, -M_PI/60, 0);
}
TASK(marisa_laser_power_handler, { MarisaAController *ctrl; }) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
int old_power = plr->power / 100;
for(e = plr->slaves.first; e; e = e->next) {
if(e->logic_rule == marisa_laser_slave) {
MarisaLaserData *ld = calloc(1, sizeof(MarisaLaserData));
ld->prev_pos = e->pos;
e->args[3] = add_ref(ld);
e->ent.draw_layer = LAYER_PLAYER_SLAVE;
marisa_laser_respawn_slaves(ctrl, old_power);
for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.power_changed);
int new_power = plr->power / 100;
if(old_power != new_power) {
marisa_laser_respawn_slaves(ctrl, new_power);
old_power = new_power;
}
}
}
static void marisa_laser_power(Player *plr, short npow) {
if(plr->power / 100 == npow / 100) {
return;
}
TASK(marisa_laser_shot_forward, { MarisaAController *ctrl; }) {
MarisaAController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
ShaderProgram *bolt_shader = r_shader_get("sprite_particle");
marisa_laser_respawn_slaves(plr, npow);
for(;;) {
WAIT_EVENT_OR_DIE(&plr->events.shoot);
play_loop("generic_shot");
for(int i = -1; i < 2; i += 2) {
PROJECTILE(
.proto = pp_marisa,
.pos = plr->pos + 10 * i - 15.0*I,
.move = move_linear(-20*I),
.type = PROJ_PLAYER,
.damage = SHOT_FORWARD_DAMAGE,
.shader_ptr = bolt_shader,
);
}
WAIT(SHOT_FORWARD_DELAY);
}
}
TASK(marisa_laser_controller, { BoxedPlayer plr; }) {
MarisaAController ctrl = { 0 };
ctrl.plr = TASK_BIND(ARGS.plr);
COEVENT_INIT_ARRAY(ctrl.events);
ctrl.laser_renderer.draw_func = marisa_laser_draw_lasers;
ctrl.laser_renderer.draw_layer = LAYER_PLAYER_FOCUS;
ent_register(&ctrl.laser_renderer, ENT_CUSTOM);
INVOKE_SUBTASK(marisa_laser_power_handler, &ctrl);
INVOKE_SUBTASK(marisa_laser_bomb_handler, &ctrl);
INVOKE_SUBTASK(marisa_laser_shot_forward, &ctrl);
WAIT_EVENT(&TASK_EVENTS(THIS_TASK)->finished);
COEVENT_CANCEL_ARRAY(ctrl.events);
ent_unregister(&ctrl.laser_renderer);
}
static void marisa_laser_init(Player *plr) {
Enemy *e = create_enemy_p(&plr->slaves, 0, ENEMY_IMMUNE, marisa_laser_renderer_visual, marisa_laser_renderer, 0, 0, 0, 0);
e->ent.draw_layer = LAYER_PLAYER_SHOT_HIGH;
_laser_renderer_ref = add_ref(e);
marisa_laser_respawn_slaves(plr, plr->power);
}
static void marisa_laser_free(Player *plr) {
free_ref(_laser_renderer_ref);
}
static void marisa_laser_shot(Player *plr) {
marisa_common_shot(plr, 175 - 4 * (plr->power / 100));
INVOKE_TASK(marisa_laser_controller, ENT_BOX(plr));
}
static double marisa_laser_property(Player *plr, PlrProperty prop) {
@ -679,13 +803,8 @@ PlayerMode plrmode_marisa_a = {
.dialog = &dialog_tasks_marisa,
.shot_mode = PLR_SHOT_MARISA_LASER,
.procs = {
.property = marisa_laser_property,
.bomb = marisa_laser_bomb,
.bombbg = marisa_laser_bombbg,
.shot = marisa_laser_shot,
.power = marisa_laser_power,
.preload = marisa_laser_preload,
.init = marisa_laser_init,
.free = marisa_laser_free,
.preload = marisa_laser_preload,
.property = marisa_laser_property,
},
};

View file

@ -29,7 +29,7 @@ static int marisa_star_projectile(Projectile *p, int t) {
e = (Enemy*)REF(p->args[1]);
}
if(e != NULL && !player_should_shoot(&global.plr, true)) {
if(e != NULL && !player_should_shoot(&global.plr)) {
free_ref(p->args[1]);
p->args[1] = -1;
e = NULL;
@ -85,7 +85,7 @@ static int marisa_star_projectile(Projectile *p, int t) {
static int marisa_star_slave(Enemy *e, int t) {
for(int i = 0; i < 2; ++i) {
if(player_should_shoot(&global.plr, true) && !((global.frames+2*i) % 5)) {
if(player_should_shoot(&global.plr) && !((global.frames+2*i) % 5)) {
float fac = e->args[0]/M_PI/2;
cmplx v = (1-2*i);
v = creal(v)/cabs(v);

View file

@ -561,7 +561,6 @@ TASK(reimu_dream_ofuda, { ReimuBController *ctrl; cmplx pos; cmplx vel; ShaderPr
TASK(reimu_dream_shot_forward, { ReimuBController *ctrl; }) {
ReimuBController *ctrl = ARGS.ctrl;
Player *plr = ctrl->plr;
real dir = 1;
ShaderProgram *ofuda_shader = r_shader_get("sprite_particle");
for(;;) {
@ -582,7 +581,6 @@ TASK(reimu_dream_shot_forward, { ReimuBController *ctrl; }) {
}
}
dir = -dir;
WAIT(SHOT_FORWARD_DELAY);
}
}

View file

@ -102,7 +102,7 @@ static void myon_spawn_trail(Enemy *e, int t) {
real f = abs(global.plr.focus) / 30.0;
stardust_v = f * stardust_v + (1 - f) * -I;
if(player_should_shoot(&global.plr, true)) {
if(player_should_shoot(&global.plr)) {
RNG_ARRAY(R, 7);
PARTICLE(
@ -285,7 +285,7 @@ static int youmu_mirror_myon(Enemy *e, int t) {
e->args[1] += (e->args[0] - e->args[1]) * 0.5;
if(player_should_shoot(&global.plr, true)) {
if(player_should_shoot(&global.plr)) {
int v1 = -10;
int v2 = -10;

View file

@ -441,7 +441,7 @@ TASK(youmu_orb_shot, { BoxedPlayer plr; }) {
static void youmu_haunting_shot(Player *plr) {
youmu_common_shot(plr);
if(player_should_shoot(plr, true)) {
if(player_should_shoot(plr)) {
if(plr->inputflags & INFLAG_FOCUS) {
int pwr = plr->power / 100;