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