Coroutinize YoumuB (#215)
This commit is contained in:
parent
eceecaf0c3
commit
2c00cec422
4 changed files with 408 additions and 316 deletions
|
@ -50,37 +50,34 @@ double youmu_common_property(Player *plr, PlrProperty prop) {
|
|||
UNREACHABLE;
|
||||
}
|
||||
|
||||
void youmu_common_shot(Player *plr) {
|
||||
play_loop("generic_shot");
|
||||
|
||||
if(!(global.frames % 6)) {
|
||||
Color *c = RGB(1, 1, 1);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_youmu,
|
||||
.pos = plr->pos + 10 - I*20,
|
||||
.color = c,
|
||||
.rule = linear,
|
||||
.args = { -20.0*I },
|
||||
.type = PROJ_PLAYER,
|
||||
.damage = 120,
|
||||
.shader = "sprite_default",
|
||||
);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_youmu,
|
||||
.pos = plr->pos - 10 - I*20,
|
||||
.color = c,
|
||||
.rule = linear,
|
||||
.args = { -20.0*I },
|
||||
.type = PROJ_PLAYER,
|
||||
.damage = 120,
|
||||
.shader = "sprite_default",
|
||||
);
|
||||
}
|
||||
Projectile *youmu_common_shot(cmplx pos, MoveParams move, real dmg, ShaderProgram *shader) {
|
||||
return PROJECTILE(
|
||||
.damage = dmg,
|
||||
.layer = LAYER_PLAYER_SHOT | 0x20,
|
||||
.move = move,
|
||||
.pos = pos,
|
||||
.proto = pp_youmu,
|
||||
.shader_ptr = shader,
|
||||
.type = PROJ_PLAYER,
|
||||
);
|
||||
}
|
||||
|
||||
static Framebuffer *bomb_buffer;
|
||||
void youmu_common_init_bomb_background(YoumuBombBGData *bg_data) {
|
||||
FBAttachmentConfig cfg;
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.attachment = FRAMEBUFFER_ATTACH_COLOR0;
|
||||
cfg.tex_params.type = TEX_TYPE_RGB;
|
||||
cfg.tex_params.filter.min = TEX_FILTER_LINEAR;
|
||||
cfg.tex_params.filter.mag = TEX_FILTER_LINEAR;
|
||||
cfg.tex_params.wrap.s = TEX_WRAP_MIRROR;
|
||||
cfg.tex_params.wrap.t = TEX_WRAP_MIRROR;
|
||||
|
||||
bg_data->buffer = stage_add_background_framebuffer("Youmu bomb FB", 0.25, 1, 1, &cfg);
|
||||
bg_data->shader = r_shader_get("youmu_bomb_bg");
|
||||
bg_data->uniforms.petals = r_shader_uniform(bg_data->shader, "petals");
|
||||
bg_data->uniforms.time = r_shader_uniform(bg_data->shader, "time");
|
||||
bg_data->texture = r_texture_get("youmu_bombbg1");
|
||||
}
|
||||
|
||||
static void capture_frame(Framebuffer *dest, Framebuffer *src) {
|
||||
r_state_push();
|
||||
|
@ -92,43 +89,37 @@ static void capture_frame(Framebuffer *dest, Framebuffer *src) {
|
|||
r_state_pop();
|
||||
}
|
||||
|
||||
void youmu_common_bombbg(Player *plr) {
|
||||
if(!player_is_bomb_active(plr)) {
|
||||
return;
|
||||
}
|
||||
DEFINE_EXTERN_TASK(youmu_common_bomb_background) {
|
||||
Player *plr = TASK_BIND(ARGS.plr);
|
||||
YoumuBombBGData *bg_data = ARGS.bg_data;
|
||||
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);
|
||||
|
||||
if(t < 1./12)
|
||||
fade = t*12;
|
||||
float t = player_get_bomb_progress(&global.plr);
|
||||
float fade = 1.0f;
|
||||
|
||||
if(t > 1./2)
|
||||
fade = 1-(t-1./2)*2;
|
||||
if(t < 1.0f / 12.0f) {
|
||||
fade = t * 12.0f;
|
||||
}
|
||||
|
||||
if(fade < 0)
|
||||
fade = 0;
|
||||
if(t > 1.0f / 2.0f) {
|
||||
fade = 1-(t-1./2)*2;
|
||||
}
|
||||
|
||||
r_state_push();
|
||||
r_color4(fade, fade, fade, fade);
|
||||
r_shader("youmu_bomb_bg");
|
||||
r_uniform_sampler("petals", "youmu_bombbg1");
|
||||
r_uniform_float("time", t);
|
||||
if(fade < 0.0f) {
|
||||
fade = 0.0f;
|
||||
}
|
||||
|
||||
draw_framebuffer_tex(bomb_buffer, VIEWPORT_W, VIEWPORT_H);
|
||||
r_state_pop();
|
||||
r_state_push();
|
||||
r_color4(fade, fade, fade, fade);
|
||||
r_shader_ptr(bg_data->shader);
|
||||
r_uniform_sampler(bg_data->uniforms.petals, bg_data->texture);
|
||||
r_uniform_float(bg_data->uniforms.time, t);
|
||||
draw_framebuffer_tex(bg_data->buffer, VIEWPORT_W, VIEWPORT_H);
|
||||
r_state_pop();
|
||||
|
||||
capture_frame(bomb_buffer, r_framebuffer_current());
|
||||
}
|
||||
|
||||
void youmu_common_bomb_buffer_init(void) {
|
||||
FBAttachmentConfig cfg;
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
cfg.attachment = FRAMEBUFFER_ATTACH_COLOR0;
|
||||
cfg.tex_params.type = TEX_TYPE_RGB;
|
||||
cfg.tex_params.filter.min = TEX_FILTER_LINEAR;
|
||||
cfg.tex_params.filter.mag = TEX_FILTER_LINEAR;
|
||||
cfg.tex_params.wrap.s = TEX_WRAP_MIRROR;
|
||||
cfg.tex_params.wrap.t = TEX_WRAP_MIRROR;
|
||||
bomb_buffer = stage_add_background_framebuffer("Youmu bomb FB", 0.25, 1, 1, &cfg);
|
||||
capture_frame(bg_data->buffer, r_framebuffer_current());
|
||||
} while(player_is_bomb_active(plr));
|
||||
}
|
||||
|
|
|
@ -18,9 +18,19 @@
|
|||
|
||||
extern PlayerCharacter character_youmu;
|
||||
|
||||
typedef struct YoumuBombBGData {
|
||||
Framebuffer *buffer;
|
||||
ShaderProgram *shader;
|
||||
struct {
|
||||
Uniform *petals, *time;
|
||||
} uniforms;
|
||||
Texture *texture;
|
||||
} YoumuBombBGData;
|
||||
|
||||
double youmu_common_property(Player *plr, PlrProperty prop);
|
||||
void youmu_common_shot(Player *plr);
|
||||
void youmu_common_bombbg(Player *plr);
|
||||
void youmu_common_bomb_buffer_init(void);
|
||||
Projectile *youmu_common_shot(cmplx pos, MoveParams move, real dmg, ShaderProgram *shader);
|
||||
|
||||
void youmu_common_init_bomb_background(YoumuBombBGData *bg_data);
|
||||
DECLARE_EXTERN_TASK(youmu_common_bomb_background, { BoxedPlayer plr; YoumuBombBGData *bg_data; });
|
||||
|
||||
#endif // IGUARD_plrmodes_youmu_h
|
||||
|
|
|
@ -45,6 +45,7 @@ struct YoumuAController {
|
|||
|
||||
Player *plr;
|
||||
YoumuAMyon myon;
|
||||
YoumuBombBGData bomb_bg;
|
||||
};
|
||||
|
||||
static Color *myon_color(Color *c, float f, float opacity, float alpha) {
|
||||
|
@ -536,17 +537,6 @@ TASK(youmu_mirror_bomb_postprocess, { YoumuAController *ctrl; }) {
|
|||
} while(player_is_bomb_active(plr));
|
||||
}
|
||||
|
||||
TASK(youmu_mirror_bomb_background, { YoumuAController *ctrl; }) {
|
||||
YoumuAController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
CoEvent *draw_event = &stage_get_draw_events()->background_drawn;
|
||||
|
||||
do {
|
||||
WAIT_EVENT_OR_DIE(draw_event);
|
||||
youmu_common_bombbg(plr);
|
||||
} while(player_is_bomb_active(plr));
|
||||
}
|
||||
|
||||
TASK(youmu_mirror_bomb_handler, { YoumuAController *ctrl; }) {
|
||||
YoumuAController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
|
@ -555,23 +545,11 @@ TASK(youmu_mirror_bomb_handler, { YoumuAController *ctrl; }) {
|
|||
WAIT_EVENT_OR_DIE(&plr->events.bomb_used);
|
||||
play_sound("bomb_youmu_b");
|
||||
INVOKE_SUBTASK(youmu_mirror_bomb_controller, ctrl);
|
||||
INVOKE_SUBTASK(youmu_mirror_bomb_background, ctrl);
|
||||
INVOKE_SUBTASK(youmu_common_bomb_background, ENT_BOX(plr), &ctrl->bomb_bg);
|
||||
INVOKE_SUBTASK(youmu_mirror_bomb_postprocess, ctrl);
|
||||
}
|
||||
}
|
||||
|
||||
static inline Projectile *youmu_mirror_self_shot(cmplx pos, MoveParams move, real dmg, ShaderProgram *shader) {
|
||||
return PROJECTILE(
|
||||
.damage = dmg,
|
||||
.layer = LAYER_PLAYER_SHOT | 0x20,
|
||||
.move = move,
|
||||
.pos = pos,
|
||||
.proto = pp_youmu,
|
||||
.shader_ptr = shader,
|
||||
.type = PROJ_PLAYER,
|
||||
);
|
||||
}
|
||||
|
||||
TASK(youmu_mirror_shot_forward, { YoumuAController *ctrl; }) {
|
||||
YoumuAController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
|
@ -589,12 +567,12 @@ TASK(youmu_mirror_shot_forward, { YoumuAController *ctrl; }) {
|
|||
for(int side = -1; side < 2; side += 2) {
|
||||
cmplx origin = plr->pos + 10*side + 5*I;
|
||||
|
||||
youmu_mirror_self_shot(origin, move_linear(v), SHOT_SELF_DAMAGE, shader);
|
||||
youmu_common_shot(origin, move_linear(v), SHOT_SELF_DAMAGE, shader);
|
||||
|
||||
for(int p = 0; p < power_rank; ++p) {
|
||||
cmplx v2 = -(20 - p) * I * cdir(side * (1 + p) * spread);
|
||||
|
||||
youmu_mirror_self_shot(
|
||||
youmu_common_shot(
|
||||
origin,
|
||||
move_asymptotic_halflife(
|
||||
0.2 * v2 * cdir(M_PI * 0.25 * side),
|
||||
|
@ -618,6 +596,8 @@ TASK(youmu_mirror_controller, { BoxedPlayer plr; }) {
|
|||
ctrl->sprites.petal = get_sprite("part/petal");
|
||||
ctrl->sprites.blast_huge_halo = get_sprite("part/blast_huge_halo");
|
||||
|
||||
youmu_common_init_bomb_background(&ctrl->bomb_bg);
|
||||
|
||||
INVOKE_SUBTASK(youmu_mirror_shot_forward, ctrl);
|
||||
INVOKE_SUBTASK(youmu_mirror_myon, ctrl);
|
||||
INVOKE_SUBTASK(youmu_mirror_bomb_handler, ctrl);
|
||||
|
@ -626,7 +606,6 @@ TASK(youmu_mirror_controller, { BoxedPlayer plr; }) {
|
|||
}
|
||||
|
||||
static void youmu_mirror_init(Player *plr) {
|
||||
youmu_common_bomb_buffer_init();
|
||||
INVOKE_TASK(youmu_mirror_controller, ENT_BOX(plr));
|
||||
}
|
||||
|
||||
|
|
|
@ -12,19 +12,50 @@
|
|||
#include "plrmodes.h"
|
||||
#include "youmu.h"
|
||||
#include "util/glm.h"
|
||||
#include "stagedraw.h"
|
||||
|
||||
static cmplx youmu_homing_target(cmplx org, cmplx fallback) {
|
||||
return plrutil_homing_target(org, fallback);
|
||||
}
|
||||
#define SHOT_BASIC_DAMAGE 60
|
||||
#define SHOT_BASIC_DELAY 6
|
||||
|
||||
static void youmu_homing_trail(Projectile *p, cmplx v, int to) {
|
||||
#define SHOT_HOMING_DAMAGE 120
|
||||
#define SHOT_HOMING_DELAY 6
|
||||
|
||||
#define SHOT_SPREAD_DAMAGE 40
|
||||
#define SHOT_SPREAD_DELAY 12
|
||||
|
||||
#define SHOT_ORBS_SPIRIT_DAMAGE 90
|
||||
#define SHOT_ORBS_SPIRIT_SPAWN_DELAY 11
|
||||
#define SHOT_ORBS_DELAY_BASE 45
|
||||
#define SHOT_ORBS_DELAY_PER_POWER -5
|
||||
#define SHOT_ORBS_LIFETIME_BASE 100
|
||||
#define SHOT_ORBS_LIFETIME_PER_POWER 10
|
||||
|
||||
#define BOMB_SLICE_DAMAGE 100
|
||||
#define BOMB_SLICE_PERIOD 2
|
||||
|
||||
typedef struct YoumuBController {
|
||||
struct {
|
||||
Sprite *blast_huge_halo;
|
||||
Sprite *blast_huge_rays;
|
||||
Sprite *petal;
|
||||
Sprite *smoothdot;
|
||||
Sprite *stardust;
|
||||
Sprite *youmu_slice;
|
||||
} sprites;
|
||||
ShaderProgram *shot_shader;
|
||||
|
||||
Player *plr;
|
||||
YoumuBombBGData bomb_bg;
|
||||
} YoumuBController;
|
||||
|
||||
static void youmu_homing_trail(YoumuBController *ctrl, Projectile *p, cmplx v, int to) {
|
||||
uint32_t tmp = p->ent.spawn_id;
|
||||
float u = M_PI * 2.0f * (float)(splitmix32(&tmp) / (double)UINT32_MAX);
|
||||
|
||||
RNG_ARRAY(R, 5);
|
||||
|
||||
PARTICLE(
|
||||
.sprite = "stardust",
|
||||
.sprite_ptr = ctrl->sprites.stardust,
|
||||
.pos = p->pos + vrng_range(R[0], 3, 12) * vrng_dir(R[1]),
|
||||
.color = RGBA(0.0, 0.3 * vrng_real(R[2]), 0.3, 0.0),
|
||||
// .draw_rule = pdraw_timeout_fade(1, 0),
|
||||
|
@ -38,7 +69,7 @@ static void youmu_homing_trail(Projectile *p, cmplx v, int to) {
|
|||
);
|
||||
|
||||
PARTICLE(
|
||||
.sprite = "smoothdot",
|
||||
.sprite_ptr = ctrl->sprites.smoothdot,
|
||||
.pos = p->pos,
|
||||
.color = color_mul(RGBA(0.2, 0.24, 0.3, 0.2), &p->color),
|
||||
.move = move_asymptotic_simple(-0.5*v*cdir(0.2*sin(u+3*creal(p->pos)/VIEWPORT_W*M_TAU) + 0.2*cos(u+3*cimag(p->pos)/VIEWPORT_H*M_TAU)), 2),
|
||||
|
@ -50,16 +81,20 @@ static void youmu_homing_trail(Projectile *p, cmplx v, int to) {
|
|||
}
|
||||
|
||||
static void youmu_particle_slice_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
|
||||
// TODO: pick animation frame based on slice direction?
|
||||
AniPlayer *player_ani = args[0].as_ptr;
|
||||
Sprite *player_frame = aniplayer_get_frame(player_ani);
|
||||
|
||||
float lifetime = p->timeout;
|
||||
float tt = t/lifetime;
|
||||
float f = 0;
|
||||
float tt = t / lifetime;
|
||||
float f = 0.0f;
|
||||
|
||||
if(tt > 0.1) {
|
||||
f = min(1,(tt-0.1)/0.2);
|
||||
f = fminf(1.0f, (tt - 0.1f) / 0.2f);
|
||||
}
|
||||
|
||||
if(tt > 0.5) {
|
||||
f = 1+(tt-0.5)/0.5;
|
||||
if(tt > 0.5f) {
|
||||
f = 1.0f + (tt - 0.5f) / 0.5f;
|
||||
}
|
||||
|
||||
SpriteParamsBuffer spbuf;
|
||||
|
@ -67,154 +102,96 @@ static void youmu_particle_slice_draw(Projectile *p, int t, ProjDrawRuleArgs arg
|
|||
sp.scale.x *= f;
|
||||
r_draw_sprite(&sp);
|
||||
|
||||
float slicelen = 500;
|
||||
cmplx slicepos = p->pos-(tt>0.1)*slicelen*I*cdir(p->angle)*(5*pow(tt-0.1,1.1)-0.5);
|
||||
float slicelen = 500.0f;
|
||||
cmplx slicepos = p->pos - (tt > 0.1f) * slicelen * I*cdir(p->angle) * (5 * powf(tt - 0.1f, 1.1f) - 0.5f);
|
||||
|
||||
r_draw_sprite(&(SpriteParams) {
|
||||
.sprite_ptr = aniplayer_get_frame(&global.plr.ani),
|
||||
.pos = { creal(slicepos), cimag(slicepos) },
|
||||
.sprite_ptr = player_frame,
|
||||
.pos.as_cmplx = slicepos,
|
||||
.shader_params = &spbuf.shader_params,
|
||||
});
|
||||
}
|
||||
|
||||
static int youmu_slice_petal(Projectile *p, int t) {
|
||||
int r = accelerated(p, t);
|
||||
TASK(youmu_haunting_shot_basic, { YoumuBController *ctrl; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
|
||||
if(t >= 0) {
|
||||
p->color = *color_mul_scalar(RGBA(0.2, 0.2, 1, 0), min(1, t / 40.0));
|
||||
}
|
||||
for(;;) {
|
||||
WAIT_EVENT_OR_DIE(&plr->events.shoot);
|
||||
play_loop("generic_shot");
|
||||
|
||||
return r;
|
||||
}
|
||||
cmplx v = -20 * I;
|
||||
|
||||
static int youmu_particle_slice_logic(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
for(int side = -1; side < 2; side += 2) {
|
||||
cmplx origin = plr->pos + 10*side + 5*I;
|
||||
youmu_common_shot(origin, move_linear(v), SHOT_BASIC_DAMAGE, ctrl->shot_shader);
|
||||
}
|
||||
|
||||
double lifetime = p->timeout;
|
||||
double tt = t/lifetime;
|
||||
double a = 0;
|
||||
|
||||
if(tt > 0.) {
|
||||
a = min(1,(tt-0.)/0.2);
|
||||
}
|
||||
if(tt > 0.5) {
|
||||
a = max(0,1-(tt-0.5)/0.5);
|
||||
}
|
||||
|
||||
p->color = *RGBA(a, a, a, 0);
|
||||
|
||||
|
||||
if(t%5 == 0) {
|
||||
cmplx phase = cdir(p->angle);
|
||||
|
||||
PARTICLE(
|
||||
.sprite = "petal",
|
||||
.pos = p->pos-400*phase,
|
||||
.rule = youmu_slice_petal,
|
||||
.draw_rule = pdraw_petal_random(),
|
||||
.args = {
|
||||
phase,
|
||||
phase*cdir(0.1),
|
||||
},
|
||||
.layer = LAYER_PARTICLE_HIGH | 0x2,
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
|
||||
);
|
||||
}
|
||||
|
||||
Ellipse e = {
|
||||
.origin = p->pos,
|
||||
.axes = CMPLX(512, 64),
|
||||
.angle = p->angle + M_PI * 0.5,
|
||||
};
|
||||
|
||||
// FIXME: this may still be too slow for lasers in some cases
|
||||
stage_clear_hazards_in_ellipse(e, CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_NOW);
|
||||
ent_area_damage_ellipse(e, &(DamageInfo) { 52, DMG_PLAYER_BOMB }, NULL, NULL);
|
||||
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static void YoumuSlash(Enemy *e, int t, bool render) {
|
||||
}
|
||||
|
||||
static int youmu_slash(Enemy *e, int t) {
|
||||
if(t > creal(e->args[0]))
|
||||
return ACTION_DESTROY;
|
||||
if(t < 0)
|
||||
return 1;
|
||||
|
||||
if(global.frames - global.plr.recovery > 0) {
|
||||
return ACTION_DESTROY;
|
||||
}
|
||||
|
||||
TIMER(&t);
|
||||
FROM_TO(0,10000,3) {
|
||||
cmplx pos = cexp(I*_i)*(100+10*_i*_i*0.01);
|
||||
PARTICLE(
|
||||
.sprite = "youmu_slice",
|
||||
.color = RGBA(1, 1, 1, 0),
|
||||
.pos = e->pos+pos,
|
||||
.draw_rule = youmu_particle_slice_draw,
|
||||
.rule = youmu_particle_slice_logic,
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
|
||||
.timeout = 100,
|
||||
.angle = carg(pos) + M_PI/2,
|
||||
.layer = LAYER_PARTICLE_HIGH | 0x1,
|
||||
);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int youmu_asymptotic(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->angle = carg(p->args[0]);
|
||||
p->args[1] *= 0.8;
|
||||
p->pos += p->args[0] * (p->args[1] + 1);
|
||||
|
||||
youmu_homing_trail(p, cexp(I*p->angle), 5);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void youmu_haunting_power_shot(Player *plr, int p) {
|
||||
int d = -2;
|
||||
double spread = 0.5 * (1 + 0.25 * sin(global.frames/10.0));
|
||||
double speed = 8;
|
||||
|
||||
if(2 * plr->power / 100 < p || (global.frames + d * p) % 12) {
|
||||
return;
|
||||
}
|
||||
|
||||
float np = (float)p / (2 * plr->power / 100);
|
||||
|
||||
for(int sign = -1; sign < 2; sign += 2) {
|
||||
cmplx dir = cexp(I*carg(sign*p*spread-speed*I));
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_hghost,
|
||||
.pos = plr->pos,
|
||||
.rule = youmu_asymptotic,
|
||||
.color = color_mul_scalar(RGB(0.7 + 0.3 * (1-np), 0.8 + 0.2 * sqrt(1-np), 1.0), 0.5),
|
||||
.args = { speed * dir * (1 - 0.25 * (1 - np)), 3 * (1 - pow(1 - np, 2)), 60, },
|
||||
.type = PROJ_PLAYER,
|
||||
.damage = 20,
|
||||
.shader = "sprite_default",
|
||||
);
|
||||
WAIT(SHOT_BASIC_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(youmu_homing_shot, { BoxedPlayer plr; }) {
|
||||
TASK(youmu_burst_shot, { YoumuBController *ctrl; int num_shots; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
ShaderProgram *shader = ctrl->shot_shader;
|
||||
|
||||
int nshots = ARGS.num_shots;
|
||||
real base_speed = 8;
|
||||
|
||||
for(int shot = 1; shot <= nshots; ++shot) {
|
||||
real np = shot / (real)nshots;
|
||||
|
||||
Color *clr = color_mul_scalar(RGB(0.7 + 0.3 * (1-np), 0.8 + 0.2 * sqrt(1-np), 1.0), 0.5);
|
||||
real spread = 0.5 * (1 + 0.25 * sin(global.frames/10.0));
|
||||
real speed = base_speed * (1 - 0.25 * (1 - np));
|
||||
real boost = 3 * (1 - pow(1 - np, 2));
|
||||
|
||||
for(int side = -1; side < 2; side += 2) {
|
||||
cmplx vel = speed * cnormalize(side * shot * spread - base_speed * I);
|
||||
|
||||
PROJECTILE(
|
||||
.color = clr,
|
||||
.damage = SHOT_SPREAD_DAMAGE,
|
||||
.move = move_asymptotic_simple(vel, boost),
|
||||
.pos = plr->pos,
|
||||
.proto = pp_hghost,
|
||||
.shader_ptr = shader,
|
||||
.type = PROJ_PLAYER,
|
||||
);
|
||||
}
|
||||
|
||||
YIELD;
|
||||
}
|
||||
}
|
||||
|
||||
TASK(youmu_haunting_shot_spread, { YoumuBController *ctrl; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
|
||||
for(;;) {
|
||||
WAIT_EVENT_OR_DIE(&plr->events.shoot);
|
||||
|
||||
if(plr->inputflags & INFLAG_FOCUS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
INVOKE_TASK(youmu_burst_shot, ctrl, 2 * plr->power / 100);
|
||||
WAIT(SHOT_SPREAD_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(youmu_homing_shot, { YoumuBController *ctrl; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
|
||||
Projectile *p = TASK_BIND(PROJECTILE(
|
||||
.proto = pp_hghost,
|
||||
.pos = ENT_UNBOX(ARGS.plr)->pos,
|
||||
.pos = plr->pos,
|
||||
.color = RGB(0.75, 0.9, 1),
|
||||
.type = PROJ_PLAYER,
|
||||
.damage = 120,
|
||||
.shader = "sprite_default",
|
||||
.damage = SHOT_HOMING_DAMAGE,
|
||||
.shader_ptr = ctrl->shot_shader,
|
||||
));
|
||||
|
||||
real speed = 10;
|
||||
|
@ -224,17 +201,33 @@ TASK(youmu_homing_shot, { BoxedPlayer plr; }) {
|
|||
cmplx target = VIEWPORT_W * 0.5;
|
||||
|
||||
for(int i = 0; i < 60; ++i) {
|
||||
target = youmu_homing_target(p->pos, target);
|
||||
target = plrutil_homing_target(p->pos, target);
|
||||
cmplx aimdir = cnormalize(target - p->pos - p->move.velocity*10);
|
||||
p->move.velocity += aim_strength * aimdir;
|
||||
p->move.velocity *= speed / cabs(p->move.velocity);
|
||||
aim_strength += aim_strength_accel;
|
||||
|
||||
youmu_homing_trail(p, 0.5 * p->move.velocity, 12);
|
||||
youmu_homing_trail(ctrl, p, 0.5 * p->move.velocity, 12);
|
||||
YIELD;
|
||||
}
|
||||
}
|
||||
|
||||
TASK(youmu_haunting_shot_homing, { YoumuBController *ctrl; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
|
||||
for(;;) {
|
||||
WAIT_EVENT_OR_DIE(&plr->events.shoot);
|
||||
|
||||
if(plr->inputflags & INFLAG_FOCUS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
INVOKE_TASK(youmu_homing_shot, ctrl);
|
||||
WAIT(SHOT_HOMING_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(youmu_orb_homing_spirit_expire, { BoxedProjectile p; }) {
|
||||
Projectile *p = ENT_UNBOX(ARGS.p);
|
||||
|
||||
|
@ -256,7 +249,8 @@ static int youmu_orb_homing_spirit_timeout(Projectile *orb) {
|
|||
return orb->timeout - projectile_time(orb);
|
||||
}
|
||||
|
||||
TASK(youmu_orb_homing_spirit, { cmplx pos; cmplx velocity; cmplx target; real charge; real damage; real spin; BoxedProjectile orb; }) {
|
||||
TASK(youmu_orb_homing_spirit, { YoumuBController *ctrl; cmplx pos; cmplx velocity; cmplx target; real charge; real damage; real spin; BoxedProjectile orb; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
int timeout = youmu_orb_homing_spirit_timeout(ENT_UNBOX(ARGS.orb));
|
||||
|
||||
if(timeout <= 0) {
|
||||
|
@ -270,7 +264,7 @@ TASK(youmu_orb_homing_spirit, { cmplx pos; cmplx velocity; cmplx target; real ch
|
|||
.type = PROJ_PLAYER,
|
||||
.timeout = timeout,
|
||||
.damage = ARGS.damage,
|
||||
.shader = "sprite_particle",
|
||||
.shader_ptr = ctrl->shot_shader,
|
||||
));
|
||||
|
||||
INVOKE_TASK_AFTER(&p->events.killed, youmu_orb_homing_spirit_expire, ENT_BOX(p));
|
||||
|
@ -304,7 +298,7 @@ TASK(youmu_orb_homing_spirit, { cmplx pos; cmplx velocity; cmplx target; real ch
|
|||
if(orb) {
|
||||
target = orb->pos;
|
||||
} else {
|
||||
target = youmu_homing_target(p->pos, creal(global.plr.pos) - 128*I);
|
||||
target = plrutil_homing_target(p->pos, creal(global.plr.pos) - 128*I);
|
||||
}
|
||||
|
||||
cmplx aimdir = cnormalize(target - p->pos - p->move.velocity);
|
||||
|
@ -327,13 +321,52 @@ TASK(youmu_orb_homing_spirit, { cmplx pos; cmplx velocity; cmplx target; real ch
|
|||
p->move.velocity = s * cnormalize(p->move.velocity + aim_strength * s * aimdir);
|
||||
approach_asymptotic_p(&speed, speed_target, 0.05, 1e-5);
|
||||
|
||||
youmu_homing_trail(p, 0.5 * p->move.velocity, 12);
|
||||
youmu_homing_trail(ctrl, p, 0.5 * p->move.velocity, 12);
|
||||
YIELD;
|
||||
}
|
||||
}
|
||||
|
||||
TASK(youmu_orb_update, { BoxedPlayer plr; BoxedProjectile orb; }) {
|
||||
Player *plr = ENT_UNBOX(ARGS.plr);
|
||||
static void youmu_orb_explode(YoumuBController *ctrl, Projectile *orb) {
|
||||
PARTICLE(
|
||||
.sprite_ptr = ctrl->sprites.blast_huge_rays,
|
||||
.pos = orb->pos,
|
||||
.timeout = 20,
|
||||
.color = RGBA(0.1, 0.5, 0.1, 0.0),
|
||||
.draw_rule = pdraw_timeout_scalefade_exp(0.01*(1+I), 1, 1, 0, 2),
|
||||
.flags = PFLAG_REQUIREDPARTICLE,
|
||||
.angle = rng_angle(),
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
|
||||
PARTICLE(
|
||||
.sprite_ptr = ctrl->sprites.blast_huge_halo,
|
||||
.pos = orb->pos,
|
||||
.timeout = 30,
|
||||
.color = RGBA(0.1, 0.1, 0.5, 0.0),
|
||||
.draw_rule = pdraw_timeout_scalefade_exp(1, 0.01*(1+I), 1, 0, 2),
|
||||
.flags = PFLAG_REQUIREDPARTICLE,
|
||||
.angle = rng_angle(),
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
|
||||
PARTICLE(
|
||||
.sprite_ptr = ctrl->sprites.blast_huge_halo,
|
||||
.pos = orb->pos,
|
||||
.timeout = 40,
|
||||
.color = RGBA(0.5, 0.1, 0.1, 0.0),
|
||||
.draw_rule = pdraw_timeout_scalefade_exp(0.8, -0.3*(1+I), 1, 0, 2),
|
||||
.flags = PFLAG_REQUIREDPARTICLE,
|
||||
.angle = rng_angle(),
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
|
||||
// TODO sound effect;
|
||||
kill_projectile(orb);
|
||||
}
|
||||
|
||||
TASK(youmu_orb_update, { YoumuBController *ctrl; BoxedProjectile orb; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
Projectile *orb = TASK_BIND(ARGS.orb);
|
||||
|
||||
for(;;) {
|
||||
|
@ -343,43 +376,8 @@ TASK(youmu_orb_update, { BoxedPlayer plr; BoxedProjectile orb; }) {
|
|||
orb->scale = 0.5 + 0.5 * tf;
|
||||
orb->angle += 0.2;
|
||||
|
||||
// TODO events for player input?
|
||||
if(!(plr->inputflags & INFLAG_FOCUS)) {
|
||||
PARTICLE(
|
||||
.sprite = "blast_huge_rays",
|
||||
.pos = orb->pos,
|
||||
.timeout = 20,
|
||||
.color = RGBA(0.1, 0.5, 0.1, 0.0),
|
||||
.draw_rule = pdraw_timeout_scalefade_exp(0.01*(1+I), 1, 1, 0, 2),
|
||||
.flags = PFLAG_REQUIREDPARTICLE,
|
||||
.angle = rng_angle(),
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
|
||||
PARTICLE(
|
||||
.sprite = "blast_huge_halo",
|
||||
.pos = orb->pos,
|
||||
.timeout = 30,
|
||||
.color = RGBA(0.1, 0.1, 0.5, 0.0),
|
||||
.draw_rule = pdraw_timeout_scalefade_exp(1, 0.01*(1+I), 1, 0, 2),
|
||||
.flags = PFLAG_REQUIREDPARTICLE,
|
||||
.angle = rng_angle(),
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
|
||||
PARTICLE(
|
||||
.sprite = "blast_huge_halo",
|
||||
.pos = orb->pos,
|
||||
.timeout = 40,
|
||||
.color = RGBA(0.5, 0.1, 0.1, 0.0),
|
||||
.draw_rule = pdraw_timeout_scalefade_exp(0.8, -0.3*(1+I), 1, 0, 2),
|
||||
.flags = PFLAG_REQUIREDPARTICLE,
|
||||
.angle = rng_angle(),
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
);
|
||||
|
||||
// TODO sound effect;
|
||||
kill_projectile(orb);
|
||||
youmu_orb_explode(ctrl, orb);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -388,7 +386,7 @@ TASK(youmu_orb_update, { BoxedPlayer plr; BoxedProjectile orb; }) {
|
|||
}
|
||||
|
||||
TASK(youmu_orb_death, { BoxedProjectile orb; BoxedTask control_task; }) {
|
||||
cotask_cancel(cotask_unbox(ARGS.control_task));
|
||||
CANCEL_TASK(ARGS.control_task);
|
||||
Projectile *orb = ENT_UNBOX(ARGS.orb);
|
||||
|
||||
PARTICLE(
|
||||
|
@ -410,59 +408,180 @@ TASK(youmu_orb_death, { BoxedProjectile orb; BoxedTask control_task; }) {
|
|||
);
|
||||
}
|
||||
|
||||
TASK(youmu_orb_shot, { BoxedPlayer plr; }) {
|
||||
Player *plr = ENT_UNBOX(ARGS.plr);
|
||||
int pwr = plr->power / 100;
|
||||
TASK(youmu_orb_shot, { YoumuBController *ctrl; int lifetime; real spirit_damage; int spirit_spawn_delay; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
|
||||
Projectile *orb = TASK_BIND(PROJECTILE(
|
||||
.proto = pp_youhoming,
|
||||
.pos = plr->pos,
|
||||
.color = RGB(1, 1, 1),
|
||||
.type = PROJ_PLAYER,
|
||||
.damage = 1000,
|
||||
.timeout = 100 + 10 * pwr,
|
||||
.timeout = ARGS.lifetime,
|
||||
.move = move_asymptotic(-30.0*I, -0.7*I, 0.8),
|
||||
.flags = PFLAG_MANUALANGLE,
|
||||
.flags = PFLAG_MANUALANGLE | PFLAG_NOCOLLISION,
|
||||
));
|
||||
|
||||
INVOKE_TASK(youmu_orb_update, ARGS.plr, ENT_BOX(orb));
|
||||
INVOKE_TASK(youmu_orb_update, ctrl, ENT_BOX(orb));
|
||||
INVOKE_TASK_AFTER(&orb->events.killed, youmu_orb_death, ENT_BOX(orb), THIS_TASK);
|
||||
|
||||
real pdmg = 120 - 18 * 4 * (1 - pow(1 - pwr / 4.0, 1.5));
|
||||
real pdmg = ARGS.spirit_damage;
|
||||
cmplx v = 5 * I;
|
||||
int spawn_delay = ARGS.spirit_spawn_delay;
|
||||
|
||||
for(;;) {
|
||||
WAIT(11);
|
||||
INVOKE_TASK(youmu_orb_homing_spirit, orb->pos, v, 0, 0, pdmg, 0.1, ENT_BOX(orb));
|
||||
INVOKE_TASK(youmu_orb_homing_spirit, orb->pos, v, 0, 0, pdmg, -0.1, ENT_BOX(orb));
|
||||
WAIT(spawn_delay);
|
||||
INVOKE_TASK(youmu_orb_homing_spirit, ctrl, orb->pos, v, 0, 0, pdmg, 0.1, ENT_BOX(orb));
|
||||
INVOKE_TASK(youmu_orb_homing_spirit, ctrl, orb->pos, v, 0, 0, pdmg, -0.1, ENT_BOX(orb));
|
||||
}
|
||||
}
|
||||
|
||||
static void youmu_haunting_shot(Player *plr) {
|
||||
youmu_common_shot(plr);
|
||||
TASK(youmu_haunting_shot_orbs, { YoumuBController *ctrl; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
|
||||
if(player_should_shoot(plr)) {
|
||||
if(plr->inputflags & INFLAG_FOCUS) {
|
||||
int pwr = plr->power / 100;
|
||||
for(;;) {
|
||||
WAIT_EVENT_OR_DIE(&plr->events.shoot);
|
||||
|
||||
if(!(global.frames % (45 - 4 * pwr))) {
|
||||
INVOKE_TASK(youmu_orb_shot, ENT_BOX(plr));
|
||||
}
|
||||
} else {
|
||||
if(!(global.frames % 6)) {
|
||||
INVOKE_TASK(youmu_homing_shot, ENT_BOX(plr));
|
||||
}
|
||||
|
||||
for(int p = 1; p <= 2*PLR_MAX_POWER/100; ++p) {
|
||||
youmu_haunting_power_shot(plr, p);
|
||||
}
|
||||
if(!(plr->inputflags & INFLAG_FOCUS)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int power_rank = plr->power / 100;
|
||||
|
||||
INVOKE_TASK(youmu_orb_shot,
|
||||
.ctrl = ctrl,
|
||||
.lifetime = SHOT_ORBS_LIFETIME_BASE + power_rank * SHOT_ORBS_LIFETIME_PER_POWER,
|
||||
.spirit_damage = SHOT_ORBS_SPIRIT_DAMAGE, // 120 - 18 * 4 * (1 - pow(1 - (plr->power / 100) / 4.0, 1.5));
|
||||
.spirit_spawn_delay = SHOT_ORBS_SPIRIT_SPAWN_DELAY
|
||||
);
|
||||
|
||||
WAIT(SHOT_ORBS_DELAY_BASE + power_rank * SHOT_ORBS_DELAY_PER_POWER);
|
||||
}
|
||||
}
|
||||
|
||||
static void youmu_haunting_bomb(Player *plr) {
|
||||
play_sound("bomb_youmu_b");
|
||||
create_enemy_p(&plr->slaves, global.plr.pos, ENEMY_BOMB, YoumuSlash, youmu_slash, 280,0,0,0);
|
||||
TASK(youmu_haunting_bomb_slice_petal, { YoumuBController *ctrl; cmplx pos; cmplx vel; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
Projectile *p = TASK_BIND(PARTICLE(
|
||||
.sprite_ptr = ctrl->sprites.petal,
|
||||
.pos = ARGS.pos,
|
||||
.draw_rule = pdraw_petal_random(),
|
||||
.move = move_accelerated(ARGS.vel, ARGS.vel * cdir(0.1)),
|
||||
.layer = LAYER_PARTICLE_HIGH | 0x2,
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
|
||||
));
|
||||
|
||||
real transition_time = 40;
|
||||
|
||||
for(real t = 0; t <= transition_time; ++t) {
|
||||
p->color = *color_mul_scalar(RGBA(0.2, 0.2, 1, 0), min(1, t / transition_time));
|
||||
YIELD;
|
||||
}
|
||||
}
|
||||
|
||||
TASK(youmu_haunting_bomb_slice, { YoumuBController *ctrl; cmplx pos; real angle; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
|
||||
Projectile *p = TASK_BIND(PARTICLE(
|
||||
.sprite_ptr = ctrl->sprites.youmu_slice,
|
||||
.color = RGBA(1, 1, 1, 0),
|
||||
.pos = ARGS.pos,
|
||||
.draw_rule = {
|
||||
.func = youmu_particle_slice_draw,
|
||||
.args[0].as_ptr = &ctrl->plr->ani,
|
||||
},
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
|
||||
.timeout = 100,
|
||||
.angle = ARGS.angle,
|
||||
.layer = LAYER_PARTICLE_HIGH | 0x1,
|
||||
));
|
||||
|
||||
cmplx petal_dir = cdir(ARGS.angle);
|
||||
|
||||
for(int t = 0;; ++t) {
|
||||
real lifetime = p->timeout;
|
||||
real tt = t/lifetime;
|
||||
real a = 0;
|
||||
|
||||
if(tt > 0.5) {
|
||||
a = max(0, 1 - (tt - 0.5) / 0.5);
|
||||
} else {
|
||||
a = min(1, tt / 0.2);
|
||||
}
|
||||
|
||||
p->color = *RGBA(a, a, a, 0);
|
||||
|
||||
if(t % 5 == 0) {
|
||||
INVOKE_TASK(youmu_haunting_bomb_slice_petal, ctrl, p->pos - 400 * petal_dir, petal_dir);
|
||||
}
|
||||
|
||||
if(t % BOMB_SLICE_PERIOD == 0) {
|
||||
Ellipse e = {
|
||||
.origin = p->pos,
|
||||
.axes = CMPLX(512, 64),
|
||||
.angle = p->angle + M_PI * 0.5,
|
||||
};
|
||||
|
||||
// FIXME: this may still be too slow for lasers in some cases
|
||||
stage_clear_hazards_in_ellipse(e, CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_NOW);
|
||||
ent_area_damage_ellipse(e, &(DamageInfo) { BOMB_SLICE_DAMAGE, DMG_PLAYER_BOMB }, NULL, NULL);
|
||||
}
|
||||
|
||||
YIELD;
|
||||
}
|
||||
}
|
||||
|
||||
TASK(youmu_haunting_bomb_controller, { YoumuBController *ctrl; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
cmplx origin = plr->pos;
|
||||
|
||||
int i = 0;
|
||||
do {
|
||||
cmplx ofs = cdir(i) * (100 + 10 * i * i * 0.01);
|
||||
real angle = carg(ofs) + M_PI/2;
|
||||
INVOKE_TASK(youmu_haunting_bomb_slice, ctrl, origin + ofs, angle);
|
||||
WAIT(3);
|
||||
++i;
|
||||
} while(player_is_bomb_active(plr));
|
||||
}
|
||||
|
||||
TASK(youmu_haunting_bomb_handler, { YoumuBController *ctrl; }) {
|
||||
YoumuBController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
|
||||
for(;;) {
|
||||
WAIT_EVENT_OR_DIE(&plr->events.bomb_used);
|
||||
play_sound("bomb_youmu_b");
|
||||
INVOKE_SUBTASK(youmu_haunting_bomb_controller, ctrl);
|
||||
INVOKE_SUBTASK(youmu_common_bomb_background, ENT_BOX(plr), &ctrl->bomb_bg);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(youmu_haunting_controller, { BoxedPlayer plr; }) {
|
||||
YoumuBController *ctrl = TASK_MALLOC(sizeof(*ctrl));
|
||||
ctrl->plr = TASK_BIND(ARGS.plr);
|
||||
ctrl->sprites.blast_huge_halo = get_sprite("part/blast_huge_halo");
|
||||
ctrl->sprites.blast_huge_rays = get_sprite("part/blast_huge_rays");
|
||||
ctrl->sprites.petal = get_sprite("part/petal");
|
||||
ctrl->sprites.smoothdot = get_sprite("part/smoothdot");
|
||||
ctrl->sprites.stardust = get_sprite("part/stardust");
|
||||
ctrl->sprites.youmu_slice = get_sprite("part/youmu_slice");
|
||||
ctrl->shot_shader = r_shader_get("sprite_particle");
|
||||
|
||||
youmu_common_init_bomb_background(&ctrl->bomb_bg);
|
||||
|
||||
INVOKE_SUBTASK(youmu_haunting_shot_basic, ctrl);
|
||||
INVOKE_SUBTASK(youmu_haunting_shot_homing, ctrl);
|
||||
INVOKE_SUBTASK(youmu_haunting_shot_spread, ctrl);
|
||||
INVOKE_SUBTASK(youmu_haunting_shot_orbs, ctrl);
|
||||
INVOKE_SUBTASK(youmu_haunting_bomb_handler, ctrl);
|
||||
|
||||
STALL;
|
||||
}
|
||||
|
||||
static void youmu_haunting_init(Player *plr) {
|
||||
INVOKE_TASK(youmu_haunting_controller, ENT_BOX(plr));
|
||||
}
|
||||
|
||||
static void youmu_haunting_preload(void) {
|
||||
|
@ -482,10 +601,6 @@ static void youmu_haunting_preload(void) {
|
|||
NULL);
|
||||
}
|
||||
|
||||
static void youmu_haunting_init(Player *plr) {
|
||||
youmu_common_bomb_buffer_init();
|
||||
}
|
||||
|
||||
PlayerMode plrmode_youmu_b = {
|
||||
.name = "Haunting Revelation",
|
||||
.description = "Ghosts are real, but they’re nothing to be afraid of. Unless, of course, you happen to stand in the way of a determined sword-wielding lady…",
|
||||
|
@ -495,10 +610,7 @@ PlayerMode plrmode_youmu_b = {
|
|||
.shot_mode = PLR_SHOT_YOUMU_HAUNTING,
|
||||
.procs = {
|
||||
.property = youmu_common_property,
|
||||
.bomb = youmu_haunting_bomb,
|
||||
.bombbg = youmu_common_bombbg,
|
||||
.init = youmu_haunting_init,
|
||||
.shot = youmu_haunting_shot,
|
||||
.preload = youmu_haunting_preload,
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue