Coroutinize ReimuA (#199)
This commit is contained in:
parent
76a60d92a9
commit
01e7ea8a33
5 changed files with 557 additions and 348 deletions
|
@ -355,9 +355,13 @@ bool enemy_in_viewport(Enemy *enemy) {
|
|||
cimag(enemy->pos) <= VIEWPORT_H + s;
|
||||
}
|
||||
|
||||
void enemy_kill(Enemy *enemy) {
|
||||
enemy->hp = ENEMY_KILLED;
|
||||
}
|
||||
|
||||
void enemy_kill_all(EnemyList *enemies) {
|
||||
for(Enemy *e = enemies->first; e; e = e->next) {
|
||||
e->hp = ENEMY_KILLED;
|
||||
enemy_kill(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ void process_enemies(EnemyList *enemies);
|
|||
|
||||
bool enemy_is_vulnerable(Enemy *enemy);
|
||||
bool enemy_in_viewport(Enemy *enemy);
|
||||
void enemy_kill(Enemy *enemy);
|
||||
void enemy_kill_all(EnemyList *enemies);
|
||||
|
||||
void Fairy(Enemy*, int t, bool render);
|
||||
|
|
148
src/player.c
148
src/player.c
|
@ -49,6 +49,8 @@ static DamageResult ent_damage_player(EntityInterface *ent, const DamageInfo *dm
|
|||
|
||||
static void player_spawn_focus_circle(Player *plr);
|
||||
|
||||
DECLARE_TASK(player_logic, { BoxedPlayer plr; });
|
||||
|
||||
void player_stage_post_init(Player *plr) {
|
||||
assert(plr->mode != NULL);
|
||||
|
||||
|
@ -61,10 +63,6 @@ void player_stage_post_init(Player *plr) {
|
|||
|
||||
delete_enemies(&global.plr.slaves);
|
||||
|
||||
if(plr->mode->procs.init != NULL) {
|
||||
plr->mode->procs.init(plr);
|
||||
}
|
||||
|
||||
plrchar_make_bomb_portrait(plr->mode->character, &plr->bomb_portrait);
|
||||
aniplayer_create(&plr->ani, get_ani(plr->mode->character->player_sprite_name), "main");
|
||||
|
||||
|
@ -73,6 +71,14 @@ void player_stage_post_init(Player *plr) {
|
|||
plr->ent.damage_func = ent_damage_player;
|
||||
ent_register(&plr->ent, ENT_PLAYER);
|
||||
|
||||
COEVENT_INIT_ARRAY(plr->events);
|
||||
|
||||
INVOKE_TASK_DELAYED(1, player_logic, ENT_BOX(plr));
|
||||
|
||||
if(plr->mode->procs.init != NULL) {
|
||||
plr->mode->procs.init(plr);
|
||||
}
|
||||
|
||||
player_spawn_focus_circle(plr);
|
||||
|
||||
plr->extralife_threshold = player_next_extralife_threshold(plr->extralives_given);
|
||||
|
@ -83,6 +89,8 @@ void player_stage_post_init(Player *plr) {
|
|||
}
|
||||
|
||||
void player_free(Player *plr) {
|
||||
COEVENT_CANCEL_ARRAY(plr->events);
|
||||
|
||||
if(plr->mode->procs.free) {
|
||||
plr->mode->procs.free(plr);
|
||||
}
|
||||
|
@ -120,7 +128,13 @@ bool player_set_power(Player *plr, short npow) {
|
|||
player_full_power(plr);
|
||||
}
|
||||
|
||||
return (oldpow + oldpow_over) != (plr->power + plr->power_overflow);
|
||||
bool change = (oldpow + oldpow_over) != (plr->power + plr->power_overflow);
|
||||
|
||||
if(change) {
|
||||
coevent_signal(&plr->events.power_changed);
|
||||
}
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
bool player_add_power(Player *plr, short pdelta) {
|
||||
|
@ -553,61 +567,77 @@ static void player_powersurge_logic(Player *plr) {
|
|||
plr->powersurge.power += plr->powersurge.bonus.gain_rate;
|
||||
}
|
||||
|
||||
DEFINE_TASK(player_logic) {
|
||||
Player *plr = TASK_BIND(ARGS.plr);
|
||||
uint prev_inputflags = 0;
|
||||
|
||||
for(;;) {
|
||||
YIELD;
|
||||
|
||||
if(prev_inputflags != plr->inputflags) {
|
||||
coevent_signal(&plr->events.inputflags_changed);
|
||||
prev_inputflags = plr->inputflags;
|
||||
}
|
||||
|
||||
fapproach_p(&plr->bomb_cutin_alpha, 0, 1/200.0);
|
||||
|
||||
if(plr->respawntime - PLR_RESPAWN_TIME/2 == global.frames && plr->lives < 0 && global.replaymode != REPLAY_PLAY) {
|
||||
stage_gameover();
|
||||
}
|
||||
|
||||
if(plr->continuetime == global.frames) {
|
||||
plr->lives = PLR_START_LIVES;
|
||||
plr->bombs = PLR_START_BOMBS;
|
||||
plr->point_item_value = PLR_START_PIV;
|
||||
plr->life_fragments = 0;
|
||||
plr->bomb_fragments = 0;
|
||||
plr->continues_used += 1;
|
||||
player_set_power(plr, 0);
|
||||
stage_clear_hazards(CLEAR_HAZARDS_ALL);
|
||||
spawn_items(plr->deathpos, ITEM_POWER, (int)ceil(PLR_MAX_POWER/(double)POWER_VALUE));
|
||||
}
|
||||
|
||||
process_enemies(&plr->slaves);
|
||||
aniplayer_update(&plr->ani);
|
||||
|
||||
if(plr->lives < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(plr->respawntime > global.frames) {
|
||||
double x = PLR_SPAWN_POS_X;
|
||||
double y = lerp(PLR_SPAWN_POS_Y, VIEWPORT_H + 64, smoothstep(0.0, 1.0, (plr->respawntime - global.frames) / (double)PLR_RESPAWN_TIME));
|
||||
plr->pos = CMPLX(x, y);
|
||||
stage_clear_hazards(CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_NOW);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(player_is_powersurge_active(plr)) {
|
||||
player_powersurge_logic(plr);
|
||||
}
|
||||
|
||||
plr->focus = approach(plr->focus, (plr->inputflags & INFLAG_FOCUS) ? 30 : 0, 1);
|
||||
plr->focus_circle.first->pos = plr->pos;
|
||||
process_enemies(&plr->focus_circle);
|
||||
|
||||
if(plr->mode->procs.think) {
|
||||
plr->mode->procs.think(plr);
|
||||
}
|
||||
|
||||
if(player_should_shoot(plr, false)) {
|
||||
coevent_signal(&plr->events.shoot);
|
||||
plr->mode->procs.shot(plr);
|
||||
}
|
||||
|
||||
if(global.frames == plr->deathtime) {
|
||||
player_realdeath(plr);
|
||||
} else if(plr->deathtime > global.frames) {
|
||||
stage_clear_hazards(CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_NOW);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void player_logic(Player* plr) {
|
||||
fapproach_p(&plr->bomb_cutin_alpha, 0, 1/200.0);
|
||||
|
||||
if(plr->respawntime - PLR_RESPAWN_TIME/2 == global.frames && plr->lives < 0 && global.replaymode != REPLAY_PLAY) {
|
||||
stage_gameover();
|
||||
}
|
||||
|
||||
if(plr->continuetime == global.frames) {
|
||||
plr->lives = PLR_START_LIVES;
|
||||
plr->bombs = PLR_START_BOMBS;
|
||||
plr->point_item_value = PLR_START_PIV;
|
||||
plr->life_fragments = 0;
|
||||
plr->bomb_fragments = 0;
|
||||
plr->continues_used += 1;
|
||||
player_set_power(plr, 0);
|
||||
stage_clear_hazards(CLEAR_HAZARDS_ALL);
|
||||
spawn_items(plr->deathpos, ITEM_POWER, (int)ceil(PLR_MAX_POWER/(double)POWER_VALUE));
|
||||
}
|
||||
|
||||
process_enemies(&plr->slaves);
|
||||
aniplayer_update(&plr->ani);
|
||||
|
||||
if(plr->lives < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(plr->respawntime > global.frames) {
|
||||
double x = PLR_SPAWN_POS_X;
|
||||
double y = lerp(PLR_SPAWN_POS_Y, VIEWPORT_H + 64, smoothstep(0.0, 1.0, (plr->respawntime - global.frames) / (double)PLR_RESPAWN_TIME));
|
||||
plr->pos = CMPLX(x, y);
|
||||
stage_clear_hazards(CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_NOW);
|
||||
return;
|
||||
}
|
||||
|
||||
if(player_is_powersurge_active(plr)) {
|
||||
player_powersurge_logic(plr);
|
||||
}
|
||||
|
||||
plr->focus = approach(plr->focus, (plr->inputflags & INFLAG_FOCUS) ? 30 : 0, 1);
|
||||
plr->focus_circle.first->pos = plr->pos;
|
||||
process_enemies(&plr->focus_circle);
|
||||
|
||||
if(plr->mode->procs.think) {
|
||||
plr->mode->procs.think(plr);
|
||||
}
|
||||
|
||||
if(player_should_shoot(plr, false)) {
|
||||
plr->mode->procs.shot(plr);
|
||||
}
|
||||
|
||||
if(global.frames == plr->deathtime) {
|
||||
player_realdeath(plr);
|
||||
} else if(plr->deathtime > global.frames) {
|
||||
stage_clear_hazards(CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_NOW);
|
||||
}
|
||||
}
|
||||
|
||||
static bool player_bomb(Player *plr) {
|
||||
|
|
|
@ -115,6 +115,12 @@ struct Player {
|
|||
PowerSurgeBonus bonus;
|
||||
} powersurge;
|
||||
|
||||
COEVENTS_ARRAY(
|
||||
shoot,
|
||||
inputflags_changed,
|
||||
power_changed
|
||||
) events;
|
||||
|
||||
uint64_t points;
|
||||
|
||||
uint64_t extralife_threshold;
|
||||
|
@ -138,7 +144,8 @@ struct Player {
|
|||
int respawntime;
|
||||
int bombtotaltime;
|
||||
|
||||
int inputflags;
|
||||
uint inputflags;
|
||||
|
||||
int lastmovesequence; // used for animation
|
||||
int axis_ud;
|
||||
int axis_lr;
|
||||
|
|
|
@ -12,12 +12,14 @@
|
|||
#include "plrmodes.h"
|
||||
#include "reimu.h"
|
||||
|
||||
// FIXME: We probably need a better way to store shot-specific state.
|
||||
// See also MarisaA.
|
||||
static struct {
|
||||
uint prev_inputflags;
|
||||
bool respawn_slaves;
|
||||
} reimu_spirit_state;
|
||||
#define ORB_RETRACT_TIME 4
|
||||
|
||||
typedef struct ReimuAController {
|
||||
Player *plr;
|
||||
COEVENTS_ARRAY(
|
||||
slaves_expired
|
||||
) events;
|
||||
} ReimuAController;
|
||||
|
||||
static void reimu_spirit_preload(void) {
|
||||
const int flags = RESF_DEFAULT;
|
||||
|
@ -48,30 +50,40 @@ static void reimu_spirit_preload(void) {
|
|||
NULL);
|
||||
}
|
||||
|
||||
static int reimu_spirit_needle(Projectile *p, int t) {
|
||||
int r = linear(p, t);
|
||||
TASK(reimu_spirit_needle, { cmplx pos; cmplx vel; real damage; ShaderProgram *shader; }) {
|
||||
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
|
||||
.proto = pp_needle,
|
||||
.pos = ARGS.pos,
|
||||
.color = RGBA(0.5, 0.5, 0.5, 0.5),
|
||||
.move = move_linear(ARGS.vel),
|
||||
.type = PROJ_PLAYER,
|
||||
.damage_type = DMG_PLAYER_SHOT,
|
||||
.damage = ARGS.damage,
|
||||
.shader_ptr = ARGS.shader,
|
||||
));
|
||||
|
||||
if(t < 0) {
|
||||
return r;
|
||||
Color trail_color = p->color;
|
||||
color_mul(&trail_color, RGBA_MUL_ALPHA(0.75, 0.5, 1, 0.5));
|
||||
color_mul_scalar(&trail_color, 0.6);
|
||||
trail_color .a = 0;
|
||||
|
||||
MoveParams trail_move = move_linear(p->move.velocity * 0.8);
|
||||
ProjDrawRule trail_draw = pdraw_timeout_scalefade(0, 2, 1, 0);
|
||||
|
||||
for(;;) {
|
||||
YIELD;
|
||||
|
||||
PARTICLE(
|
||||
.sprite_ptr = p->sprite,
|
||||
.color = &trail_color,
|
||||
.timeout = 12,
|
||||
.pos = p->pos,
|
||||
.move = trail_move,
|
||||
.draw_rule = trail_draw,
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
.flags = PFLAG_NOREFLECT,
|
||||
);
|
||||
}
|
||||
|
||||
Color c = p->color;
|
||||
color_mul(&c, RGBA_MUL_ALPHA(0.75, 0.5, 1, 0.5));
|
||||
color_mul_scalar(&c, 0.6);
|
||||
c.a = 0;
|
||||
|
||||
PARTICLE(
|
||||
.sprite_ptr = p->sprite,
|
||||
.color = &c,
|
||||
.timeout = 12,
|
||||
.pos = p->pos,
|
||||
.move = move_linear(p->args[0] * 0.8),
|
||||
.draw_rule = pdraw_timeout_scalefade(0, 2, 1, 0),
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
.flags = PFLAG_NOREFLECT,
|
||||
);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#define REIMU_SPIRIT_HOMING_SCALE 0.75
|
||||
|
@ -87,7 +99,7 @@ static Projectile *reimu_spirit_spawn_ofuda_particle(Projectile *p, int t, doubl
|
|||
.timeout = 12,
|
||||
.pos = p->pos,
|
||||
.angle = p->angle,
|
||||
.move = move_linear(p->args[0] * rng_range(0.6, 1.0) * vfactor),
|
||||
.move = move_linear(p->move.velocity * rng_range(0.6, 1.0) * vfactor),
|
||||
.draw_rule = pdraw_timeout_scalefade(1, 1.5, 1, 0),
|
||||
.layer = LAYER_PARTICLE_LOW,
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
|
||||
|
@ -95,67 +107,65 @@ static Projectile *reimu_spirit_spawn_ofuda_particle(Projectile *p, int t, doubl
|
|||
);
|
||||
}
|
||||
|
||||
static int reimu_spirit_homing_impact(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
TASK(reimu_spirit_homing_impact, { BoxedProjectile p; }) {
|
||||
Projectile *ref = NOT_NULL(ENT_UNBOX(ARGS.p));
|
||||
|
||||
Projectile *trail = reimu_spirit_spawn_ofuda_particle(p, global.frames, 1);
|
||||
trail->rule = NULL;
|
||||
trail->timeout = 6;
|
||||
trail->angle = p->angle;
|
||||
trail->ent.draw_layer = LAYER_PLAYER_FOCUS - 1; // TODO: add a layer for "super high" particles?
|
||||
trail->args[2] *= 1.5 * (1 - t/p->timeout);
|
||||
p->angle += 0.2;
|
||||
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static Projectile* reimu_spirit_spawn_homing_impact(Projectile *p, int t) {
|
||||
return PARTICLE(
|
||||
.proto = p->proto,
|
||||
.color = &p->color,
|
||||
Projectile *p = TASK_BIND_UNBOXED(PARTICLE(
|
||||
.proto = ref->proto,
|
||||
.color = &ref->color,
|
||||
.timeout = 32,
|
||||
.pos = p->pos,
|
||||
.angle = p->angle,
|
||||
.rule = reimu_spirit_homing_impact,
|
||||
.pos = ref->pos,
|
||||
.angle = ref->angle,
|
||||
.angle_delta = 0.2,
|
||||
.draw_rule = pdraw_timeout_scalefade(1, 1.5, 1, 0),
|
||||
.layer = LAYER_PARTICLE_HIGH,
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE | PFLAG_MANUALANGLE,
|
||||
.scale = REIMU_SPIRIT_HOMING_SCALE,
|
||||
);
|
||||
));
|
||||
|
||||
for(int t = global.frames - ref->birthtime;; ++t) {
|
||||
Projectile *trail = reimu_spirit_spawn_ofuda_particle(p, t, 0);
|
||||
trail->timeout = 6;
|
||||
trail->angle = p->angle;
|
||||
trail->ent.draw_layer = LAYER_PLAYER_FOCUS - 1; // TODO: add a layer for "super high" particles?
|
||||
YIELD;
|
||||
}
|
||||
}
|
||||
|
||||
static inline double reimu_spirit_homing_aimfactor(double t, double maxt) {
|
||||
t = clamp(t, 0, maxt);
|
||||
double q = pow(1 - t / maxt, 3);
|
||||
static inline real reimu_spirit_homing_aimfactor(real t, real maxt) {
|
||||
real q = pow(t / maxt, 3);
|
||||
return 4 * q * (1 - q);
|
||||
}
|
||||
|
||||
static int reimu_spirit_homing(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
if(t == EVENT_DEATH && !global.gameover && projectile_in_viewport(p)) {
|
||||
reimu_spirit_spawn_homing_impact(p, t);
|
||||
}
|
||||
TASK(reimu_spirit_homing, { cmplx pos; cmplx vel; real damage; ShaderProgram *shader; }) {
|
||||
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
|
||||
.proto = pp_ofuda,
|
||||
.pos = ARGS.pos,
|
||||
.color = RGBA(0.7, 0.63, 0.665, 0.7),
|
||||
.move = move_linear(ARGS.vel),
|
||||
.type = PROJ_PLAYER,
|
||||
.damage_type = DMG_PLAYER_SHOT,
|
||||
.damage = ARGS.damage,
|
||||
.shader_ptr = ARGS.shader,
|
||||
.scale = REIMU_SPIRIT_HOMING_SCALE,
|
||||
.flags = PFLAG_NOCOLLISIONEFFECT,
|
||||
));
|
||||
|
||||
return ACTION_ACK;
|
||||
INVOKE_TASK_WHEN(&p->events.killed, reimu_spirit_homing_impact, ENT_BOX(p));
|
||||
|
||||
cmplx target = p->pos + p->move.velocity * hypot(VIEWPORT_W, VIEWPORT_H);
|
||||
real speed = cabs(p->move.velocity);
|
||||
real homing_time = 60;
|
||||
|
||||
for(real t = 0; t < homing_time; ++t) {
|
||||
target = plrutil_homing_target(p->pos, target);
|
||||
cmplx aim = cnormalize(target - p->pos);
|
||||
real s = speed * (0.5 + 0.5 * pow((t + 1) / homing_time, 2));
|
||||
aim *= s * 0.25 * reimu_spirit_homing_aimfactor(t, homing_time);
|
||||
p->move.velocity = s * cnormalize(p->move.velocity + aim);
|
||||
reimu_spirit_spawn_ofuda_particle(p, t, 0.25);
|
||||
YIELD;
|
||||
}
|
||||
|
||||
p->args[3] = plrutil_homing_target(p->pos, p->args[3]);
|
||||
double v = cabs(p->args[0]);
|
||||
|
||||
cmplx aimdir = cexp(I*carg(p->args[3] - p->pos));
|
||||
double aim = reimu_spirit_homing_aimfactor(t, p->args[1]);
|
||||
|
||||
p->args[0] += v * 0.25 * aim * aimdir;
|
||||
p->args[0] = v * cexp(I*carg(p->args[0]));
|
||||
p->angle = carg(p->args[0]);
|
||||
|
||||
double s = 1;// max(pow(2*t/creal(p->args[1]), 2), 0.1); //(0.25 + 0.75 * (1 - aim));
|
||||
p->pos += p->args[0] * s;
|
||||
reimu_spirit_spawn_ofuda_particle(p, t, 0.5);
|
||||
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static Color *reimu_spirit_orb_color(Color *c, int i) {
|
||||
|
@ -163,19 +173,6 @@ static Color *reimu_spirit_orb_color(Color *c, int i) {
|
|||
return c;
|
||||
}
|
||||
|
||||
static int reimu_spirit_bomb_orb_trail(Projectile *p, int t) {
|
||||
if(t < 0) {
|
||||
return ACTION_ACK;
|
||||
}
|
||||
|
||||
p->pos += p->args[0];
|
||||
p->angle -= 0.05;
|
||||
|
||||
// p->color = *HSLA(2.0*t/p->timeout, 0.5, 0.5, 0.0);
|
||||
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static void reimu_spirit_bomb_impact_balls(cmplx pos, int count) {
|
||||
real offset = rng_real();
|
||||
|
||||
|
@ -186,12 +183,11 @@ static void reimu_spirit_bomb_impact_balls(cmplx pos, int count) {
|
|||
.color = HSLA(3 * (float)i / count + offset, 1, 0.5, 0),
|
||||
.timeout = 60,
|
||||
.pos = pos,
|
||||
.args = { cdir(2 * M_PI / count * (i + offset)) * 15 },
|
||||
.angle = rng_angle(),
|
||||
.rule = linear,
|
||||
.move = move_linear(cdir(2 * M_PI / count * (i + offset)) * 15),
|
||||
.draw_rule = pdraw_timeout_fade(1, 0),
|
||||
.layer = LAYER_BOSS,
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE | PFLAG_MANUALANGLE,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -368,11 +364,11 @@ TASK(reimu_spirit_bomb_orb, { BoxedPlayer plr; int index; real angle; }) {
|
|||
.color = HSLA(t/orb->timeout, 0.3, 0.3, 0.0),
|
||||
.pos = trail_pos,
|
||||
.angle = rng_angle(),
|
||||
.angle_delta = -0.05,
|
||||
.timeout = 30,
|
||||
.draw_rule = pdraw_timeout_scalefade(0.4, 0, 1, 0),
|
||||
.rule = reimu_spirit_bomb_orb_trail,
|
||||
.args = { trail_vel },
|
||||
.flags = PFLAG_NOREFLECT,
|
||||
.move = move_linear(trail_vel),
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_MANUALANGLE,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -439,245 +435,418 @@ TASK(reimu_spirit_ofuda, { cmplx pos; cmplx vel; real damage; }) {
|
|||
}
|
||||
|
||||
static void reimu_spirit_shot(Player *p) {
|
||||
play_loop("generic_shot");
|
||||
|
||||
if(!(global.frames % 3)) {
|
||||
int i = 1 - 2 * (bool)(global.frames % 6);
|
||||
INVOKE_TASK(reimu_spirit_ofuda, p->pos + 10 * i - 15.0*I, -20*I, 100 - 8 * (p->power / 100));
|
||||
}
|
||||
|
||||
for(int pwr = 0; pwr <= p->power/100; ++pwr) {
|
||||
int t = (global.frames - 5 * pwr);
|
||||
|
||||
if(!(t % 16)) {
|
||||
for(int i = -1; i < 2; i += 2) {
|
||||
float spread = i * M_PI/32 * (1 + 0.35 * pwr);
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_hakurei_seal,
|
||||
.pos = p->pos - I + 5 * i,
|
||||
.color = color_mul_scalar(RGBA(1, 1, 1, 0.5), 0.7),
|
||||
.rule = linear,
|
||||
.args = { -18.0*I*cexp(I*spread) },
|
||||
.type = PROJ_PLAYER,
|
||||
.damage = 60 - 5 * (p->power / 100),
|
||||
.shader = "sprite_particle",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void reimu_spirit_slave_shot(Enemy *e, int t) {
|
||||
int st = t;
|
||||
|
||||
if(st % 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(global.plr.inputflags & INFLAG_FOCUS) {
|
||||
PROJECTILE(
|
||||
.proto = pp_needle,
|
||||
.pos = e->pos - 25.0*I,
|
||||
.color = RGBA_MUL_ALPHA(1, 1, 1, 0.5),
|
||||
.rule = reimu_spirit_needle,
|
||||
.args = { -20.0*I },
|
||||
.type = PROJ_PLAYER,
|
||||
.damage_type = DMG_PLAYER_SHOT,
|
||||
.damage = cimag(e->args[2]),
|
||||
.shader = "sprite_particle",
|
||||
);
|
||||
} else if(!(st % 12)) {
|
||||
cmplx v = -10 * I * cexp(I*cimag(e->args[0]));
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_ofuda,
|
||||
.pos = e->pos,
|
||||
.color = RGBA_MUL_ALPHA(1, 0.9, 0.95, 0.7),
|
||||
.rule = reimu_spirit_homing,
|
||||
.args = { v , 60, 0, e->pos + v * VIEWPORT_H * VIEWPORT_W /*creal(e->pos)*/ },
|
||||
.type = PROJ_PLAYER,
|
||||
.damage_type = DMG_PLAYER_SHOT,
|
||||
.damage = creal(e->args[2]),
|
||||
// .timeout = 60,
|
||||
.shader = "sprite_particle",
|
||||
.scale = REIMU_SPIRIT_HOMING_SCALE,
|
||||
.flags = PFLAG_NOCOLLISIONEFFECT,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static int reimu_spirit_slave(Enemy *e, int t) {
|
||||
TIMER(&t);
|
||||
|
||||
AT(EVENT_BIRTH) {
|
||||
e->pos = global.plr.pos;
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
if(t < 0) {
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
if(player_should_shoot(&global.plr, true)) {
|
||||
reimu_spirit_slave_shot(e, t);
|
||||
}
|
||||
|
||||
if(creal(e->args[3]) != 0) {
|
||||
int death_begin_time = creal(e->args[3]);
|
||||
int death_duration = cimag(e->args[3]);
|
||||
double death_progress = (t - death_begin_time) / (double)death_duration;
|
||||
|
||||
e->pos = global.plr.pos * death_progress + e->pos0 * (1 - death_progress);
|
||||
|
||||
if(death_progress >= 1) {
|
||||
return ACTION_DESTROY;
|
||||
}
|
||||
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
double speed = 0.005 * min(1, t / 12.0);
|
||||
|
||||
if(global.plr.inputflags & INFLAG_FOCUS) {
|
||||
GO_TO(e, global.plr.pos + cimag(e->args[1]) * cexp(I*(creal(e->args[0]) + t * creal(e->args[1]))), speed * cabs(e->args[1]));
|
||||
} else {
|
||||
GO_TO(e, global.plr.pos + e->pos0, speed * cabs(e->pos0));
|
||||
}
|
||||
|
||||
return ACTION_NONE;
|
||||
}
|
||||
|
||||
static void reimu_spirit_yinyang_focused_visual(Enemy *e, int t, bool render) {
|
||||
if(!render && player_should_shoot(&global.plr, true)) {
|
||||
RNG_ARRAY(R, 4);
|
||||
PARTICLE(
|
||||
.sprite = "stain",
|
||||
.color = RGBA(0.5, vrng_range(R[0], 0, 0.25), 0, 0),
|
||||
.timeout = vrng_range(R[1], 8, 10),
|
||||
.pos = e->pos,
|
||||
.angle = vrng_angle(R[3]),
|
||||
.move = move_linear(-I * vrng_range(R[2], 5, 10)),
|
||||
.draw_rule = pdraw_timeout_scalefade(0.25, 0, 1, 0),
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_MANUALANGLE,
|
||||
);
|
||||
}
|
||||
|
||||
if(render) {
|
||||
reimu_common_draw_yinyang(e, t, RGB(1.0, 0.8, 0.8));
|
||||
}
|
||||
}
|
||||
|
||||
static void reimu_spirit_yinyang_unfocused_visual(Enemy *e, int t, bool render) {
|
||||
if(!render && player_should_shoot(&global.plr, true)) {
|
||||
RNG_ARRAY(R, 4);
|
||||
PARTICLE(
|
||||
.sprite = "stain",
|
||||
.color = RGBA(0.5, 0.125, vrng_range(R[0], 0, 0.25), 0),
|
||||
.timeout = vrng_range(R[1], 8, 10),
|
||||
.pos = e->pos,
|
||||
.args = { -I * vrng_range(R[2], 5, 10), 0, 0.25 + 0*I },
|
||||
.angle = vrng_angle(R[3]),
|
||||
.move = move_linear(-I * vrng_range(R[2], 5, 10)),
|
||||
.draw_rule = pdraw_timeout_scalefade(0.25, 0, 1, 0),
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_MANUALANGLE,
|
||||
);
|
||||
}
|
||||
|
||||
if(render) {
|
||||
reimu_common_draw_yinyang(e, t, RGB(0.95, 0.75, 1.0));
|
||||
}
|
||||
}
|
||||
|
||||
static inline Enemy* reimu_spirit_spawn_slave(Player *plr, cmplx pos, cmplx a0, cmplx a1, cmplx a2, cmplx a3, EnemyVisualRule visual) {
|
||||
Enemy *e = create_enemy_p(&plr->slaves, pos, ENEMY_IMMUNE, visual, reimu_spirit_slave, a0, a1, a2, a3);
|
||||
TASK(reimu_spirit_slave_expire, { ReimuAController *ctrl; BoxedEnemy e; BoxedTask main_task; }) {
|
||||
Enemy *e = TASK_BIND(ARGS.e);
|
||||
Player *plr = ARGS.ctrl->plr;
|
||||
cotask_cancel(cotask_unbox(ARGS.main_task));
|
||||
|
||||
cmplx pos0 = e->pos;
|
||||
real retract_time = ORB_RETRACT_TIME;
|
||||
e->move = (MoveParams) { 0 };
|
||||
|
||||
for(int i = 1; i <= retract_time; ++i) {
|
||||
YIELD;
|
||||
e->pos = clerp(pos0, plr->pos, i / retract_time);
|
||||
}
|
||||
|
||||
delete_enemy(&plr->slaves, e);
|
||||
}
|
||||
|
||||
static Enemy *reimu_spirit_create_slave(ReimuAController *ctrl, EnemyVisualRule visual) {
|
||||
Player *plr = ctrl->plr;
|
||||
Enemy *e = create_enemy_p(
|
||||
&plr->slaves,
|
||||
plr->pos,
|
||||
ENEMY_IMMUNE,
|
||||
visual,
|
||||
NULL, 0, 0, 0, 0
|
||||
);
|
||||
e->ent.draw_layer = LAYER_PLAYER_SLAVE;
|
||||
INVOKE_TASK_WHEN(&ctrl->events.slaves_expired, reimu_spirit_slave_expire, ctrl, ENT_BOX(e), THIS_TASK);
|
||||
return e;
|
||||
}
|
||||
|
||||
static void reimu_spirit_kill_slaves(EnemyList *slaves) {
|
||||
for(Enemy *e = slaves->first, *next; e; e = next) {
|
||||
next = e->next;
|
||||
TASK(reimu_slave_shot_particles, {
|
||||
BoxedEnemy e;
|
||||
int num;
|
||||
cmplx dir;
|
||||
Color *color0;
|
||||
Color *color1;
|
||||
Sprite *sprite;
|
||||
}) {
|
||||
Enemy *e = TASK_BIND(ARGS.e);
|
||||
Color color0 = *ARGS.color0;
|
||||
Color color1 = *ARGS.color1;
|
||||
int num = ARGS.num;
|
||||
Sprite *sprite = ARGS.sprite;
|
||||
ProjDrawRule draw = pdraw_timeout_scalefade(0.25, 0, 1, 0);
|
||||
cmplx dir = ARGS.dir;
|
||||
|
||||
if(e->hp == ENEMY_IMMUNE && creal(e->args[3]) == 0) {
|
||||
// delete_enemy(slaves, e);
|
||||
// e->args[3] = 1;
|
||||
e->args[3] = global.frames - e->birthtime + 3 * I;
|
||||
e->pos0 = e->pos;
|
||||
}
|
||||
for(int i = 0; i < num; ++i) {
|
||||
MoveParams move = move_linear(dir * rng_range(5, 10));
|
||||
real timeout = rng_range(8, 10);
|
||||
real angle = rng_angle();
|
||||
|
||||
PARTICLE(
|
||||
.angle = angle,
|
||||
.color = COLOR_COPY(color_lerp(&color0, &color1, rng_f32())),
|
||||
.draw_rule = draw,
|
||||
.flags = PFLAG_NOREFLECT | PFLAG_MANUALANGLE,
|
||||
.move = move,
|
||||
.pos = e->pos,
|
||||
.sprite_ptr = sprite,
|
||||
.timeout = timeout,
|
||||
);
|
||||
YIELD;
|
||||
}
|
||||
}
|
||||
|
||||
static void reimu_spirit_respawn_slaves(Player *plr, short npow, cmplx param) {
|
||||
double dmg_homing = 120 - 12 * plr->power / 100; // every 12 frames
|
||||
double dmg_needle = 92 - 10 * plr->power / 100; // every 3 frames
|
||||
cmplx dmg = dmg_homing + I * dmg_needle;
|
||||
EnemyVisualRule visual;
|
||||
TASK(reimu_spirit_slave_needle_shot, {
|
||||
ReimuAController *ctrl;
|
||||
BoxedEnemy e;
|
||||
real damage;
|
||||
}) {
|
||||
Player *plr = ARGS.ctrl->plr;
|
||||
Enemy *e = TASK_BIND(ARGS.e);
|
||||
|
||||
if(plr->inputflags & INFLAG_FOCUS) {
|
||||
visual = reimu_spirit_yinyang_focused_visual;
|
||||
} else {
|
||||
visual = reimu_spirit_yinyang_unfocused_visual;
|
||||
ShaderProgram *shader = r_shader_get("sprite_particle");
|
||||
Sprite *particle_spr = get_sprite("part/stain");
|
||||
|
||||
real damage = ARGS.damage;
|
||||
real delay = 3;
|
||||
|
||||
for(;;) {
|
||||
WAIT_EVENT_OR_DIE(&plr->events.shoot);
|
||||
|
||||
INVOKE_SUBTASK(reimu_slave_shot_particles,
|
||||
.e = ENT_BOX(e),
|
||||
.color0 = RGBA(0.5, 0, 0, 0),
|
||||
.color1 = RGBA(0.5, 0.25, 0, 0),
|
||||
.num = delay,
|
||||
.dir = -I,
|
||||
.sprite = particle_spr
|
||||
);
|
||||
|
||||
INVOKE_TASK(reimu_spirit_needle,
|
||||
.pos = e->pos - 25.0*I,
|
||||
.vel = -20*I,
|
||||
.damage = damage,
|
||||
.shader = shader
|
||||
);
|
||||
|
||||
WAIT(delay);
|
||||
}
|
||||
}
|
||||
|
||||
reimu_spirit_kill_slaves(&plr->slaves);
|
||||
TASK(reimu_spirit_slave_needle, {
|
||||
ReimuAController *ctrl;
|
||||
real distance;
|
||||
real rotation;
|
||||
real rotation_offset;
|
||||
real shot_damage;
|
||||
}) {
|
||||
ReimuAController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
Enemy *e = TASK_BIND_UNBOXED(reimu_spirit_create_slave(ctrl, reimu_spirit_yinyang_focused_visual));
|
||||
|
||||
switch(npow / 100) {
|
||||
INVOKE_SUBTASK(reimu_spirit_slave_needle_shot, ctrl, ENT_BOX(e), ARGS.shot_damage);
|
||||
|
||||
real dist = ARGS.distance;
|
||||
cmplx offset = dist * cdir(ARGS.rotation_offset);
|
||||
cmplx rot = cdir(ARGS.rotation);
|
||||
|
||||
real target_speed = 0.005 * dist;
|
||||
e->move = move_towards(plr->pos + offset, 0);
|
||||
|
||||
for(;;) {
|
||||
YIELD;
|
||||
e->move.attraction = approach(e->move.attraction, target_speed, target_speed / 12.0);
|
||||
e->move.attraction_point = plr->pos + offset;
|
||||
offset *= rot;
|
||||
}
|
||||
}
|
||||
|
||||
TASK(reimu_spirit_slave_homing_shot, {
|
||||
ReimuAController *ctrl;
|
||||
BoxedEnemy e;
|
||||
cmplx vel;
|
||||
real damage;
|
||||
}) {
|
||||
Player *plr = ARGS.ctrl->plr;
|
||||
Enemy *e = TASK_BIND(ARGS.e);
|
||||
|
||||
ShaderProgram *shader = r_shader_get("sprite_particle");
|
||||
Sprite *particle_spr = get_sprite("part/stain");
|
||||
cmplx vel = ARGS.vel;
|
||||
cmplx pdir = cnormalize(vel);
|
||||
|
||||
real damage = ARGS.damage;
|
||||
real delay = 12;
|
||||
|
||||
for(;;) {
|
||||
WAIT_EVENT_OR_DIE(&plr->events.shoot);
|
||||
|
||||
INVOKE_SUBTASK(reimu_slave_shot_particles,
|
||||
.e = ENT_BOX(e),
|
||||
.color0 = RGBA(0.5, 0.125, 0, 0),
|
||||
.color1 = RGBA(0.5, 0.125, 0.25, 0),
|
||||
.num = delay,
|
||||
.dir = pdir,
|
||||
.sprite = particle_spr
|
||||
);
|
||||
|
||||
INVOKE_TASK(reimu_spirit_homing,
|
||||
.pos = e->pos,
|
||||
.vel = vel,
|
||||
.damage = damage,
|
||||
.shader = shader
|
||||
);
|
||||
|
||||
WAIT(delay);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(reimu_spirit_slave_homing, {
|
||||
ReimuAController *ctrl;
|
||||
cmplx offset;
|
||||
cmplx shot_vel;
|
||||
real shot_damage;
|
||||
}) {
|
||||
ReimuAController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
Enemy *e = TASK_BIND_UNBOXED(reimu_spirit_create_slave(ctrl, reimu_spirit_yinyang_unfocused_visual));
|
||||
|
||||
INVOKE_SUBTASK(reimu_spirit_slave_homing_shot, ctrl, ENT_BOX(e), ARGS.shot_vel, ARGS.shot_damage);
|
||||
|
||||
cmplx offset = ARGS.offset;
|
||||
real target_speed = 0.005 * cabs(offset);
|
||||
e->move = move_towards(plr->pos + offset, 0);
|
||||
|
||||
for(;;) {
|
||||
YIELD;
|
||||
e->move.attraction = approach(e->move.attraction, target_speed, target_speed / 12.0);
|
||||
e->move.attraction_point = plr->pos + offset;
|
||||
}
|
||||
}
|
||||
|
||||
static void reimu_spirit_spawn_slaves_unfocused(ReimuAController *ctrl, int power_rank) {
|
||||
real dmg_homing = 100; // 120 - 12 * power_rank;
|
||||
cmplx sv = -10 * I;
|
||||
|
||||
switch(power_rank) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
reimu_spirit_spawn_slave(plr, 50.0*I, 0 , +0.10 + 60*I, dmg, 0, visual);
|
||||
INVOKE_TASK(reimu_spirit_slave_homing, ctrl, 50*I, sv, dmg_homing);
|
||||
break;
|
||||
case 2:
|
||||
reimu_spirit_spawn_slave(plr, +40, 0 +M_PI/24*I, +0.10 + 60*I, dmg, 0, visual);
|
||||
reimu_spirit_spawn_slave(plr, -40, M_PI -M_PI/24*I, +0.10 + 60*I, dmg, 0, visual);
|
||||
INVOKE_TASK(reimu_spirit_slave_homing, ctrl, +40, sv * cdir(+M_PI/24), dmg_homing);
|
||||
INVOKE_TASK(reimu_spirit_slave_homing, ctrl, -40, sv * cdir(-M_PI/24), dmg_homing);
|
||||
break;
|
||||
case 3:
|
||||
reimu_spirit_spawn_slave(plr, 50.0*I, 0*2*M_PI/3 , +0.10 + 60*I, dmg, 0, visual);
|
||||
reimu_spirit_spawn_slave(plr, +40, 1*2*M_PI/3 +M_PI/24*I, +0.10 + 60*I, dmg, 0, visual);
|
||||
reimu_spirit_spawn_slave(plr, -40, 2*2*M_PI/3 -M_PI/24*I, +0.10 + 60*I, dmg, 0, visual);
|
||||
INVOKE_TASK(reimu_spirit_slave_homing, ctrl, 50*I, sv, dmg_homing);
|
||||
INVOKE_TASK(reimu_spirit_slave_homing, ctrl, +40, sv * cdir(+M_PI/24), dmg_homing);
|
||||
INVOKE_TASK(reimu_spirit_slave_homing, ctrl, -40, sv * cdir(-M_PI/24), dmg_homing);
|
||||
break;
|
||||
case 4:
|
||||
reimu_spirit_spawn_slave(plr, +40, 0 +M_PI/32*I, +0.10 + 60*I, dmg, 0, visual);
|
||||
reimu_spirit_spawn_slave(plr, -40, M_PI -M_PI/32*I, +0.10 + 60*I, dmg, 0, visual);
|
||||
reimu_spirit_spawn_slave(plr, +80, 0 +M_PI/16*I, -0.05 + 80*I, dmg, 0, visual);
|
||||
reimu_spirit_spawn_slave(plr, -80, M_PI -M_PI/16*I, -0.05 + 80*I, dmg, 0, visual);
|
||||
INVOKE_TASK(reimu_spirit_slave_homing, ctrl, +40, sv * cdir(+M_PI/24), dmg_homing);
|
||||
INVOKE_TASK(reimu_spirit_slave_homing, ctrl, -40, sv * cdir(-M_PI/24), dmg_homing);
|
||||
INVOKE_TASK(reimu_spirit_slave_homing, ctrl, +80, sv * cdir(+M_PI/16), dmg_homing);
|
||||
INVOKE_TASK(reimu_spirit_slave_homing, ctrl, -80, sv * cdir(-M_PI/16), dmg_homing);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
static void reimu_spirit_init(Player *plr) {
|
||||
memset(&reimu_spirit_state, 0, sizeof(reimu_spirit_state));
|
||||
reimu_spirit_state.prev_inputflags = plr->inputflags;
|
||||
reimu_spirit_respawn_slaves(plr, plr->power, 0);
|
||||
reimu_common_bomb_buffer_init();
|
||||
static void reimu_spirit_spawn_slaves_focused(ReimuAController *ctrl, int power_rank) {
|
||||
real dmg_needle = 90; // 92 - 10 * power_rank;
|
||||
|
||||
switch(power_rank) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
INVOKE_TASK(reimu_spirit_slave_needle, ctrl, 60, 0.10, 0, dmg_needle);
|
||||
break;
|
||||
case 2:
|
||||
INVOKE_TASK(reimu_spirit_slave_needle, ctrl, 60, 0.10, 0, dmg_needle);
|
||||
INVOKE_TASK(reimu_spirit_slave_needle, ctrl, 60, 0.10, M_TAU/2, dmg_needle);
|
||||
break;
|
||||
case 3:
|
||||
INVOKE_TASK(reimu_spirit_slave_needle, ctrl, 60, 0.10, 0*M_TAU/3, dmg_needle);
|
||||
INVOKE_TASK(reimu_spirit_slave_needle, ctrl, 60, 0.10, 1*M_TAU/3, dmg_needle);
|
||||
INVOKE_TASK(reimu_spirit_slave_needle, ctrl, 60, 0.10, 2*M_TAU/3, dmg_needle);
|
||||
break;
|
||||
case 4:
|
||||
INVOKE_TASK(reimu_spirit_slave_needle, ctrl, 60, 0.10, 0, dmg_needle);
|
||||
INVOKE_TASK(reimu_spirit_slave_needle, ctrl, 60, 0.10, M_PI, dmg_needle);
|
||||
INVOKE_TASK(reimu_spirit_slave_needle, ctrl, 80, -0.05, 0, dmg_needle);
|
||||
INVOKE_TASK(reimu_spirit_slave_needle, ctrl, 80, -0.05, M_PI, dmg_needle);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
static void reimu_spirit_think(Player *plr) {
|
||||
if((bool)(reimu_spirit_state.prev_inputflags & INFLAG_FOCUS) ^ (bool)(plr->inputflags & INFLAG_FOCUS)) {
|
||||
reimu_spirit_state.respawn_slaves = true;
|
||||
}
|
||||
static void reimu_spirit_kill_slaves(ReimuAController *ctrl) {
|
||||
coevent_signal(&ctrl->events.slaves_expired);
|
||||
}
|
||||
|
||||
if(reimu_spirit_state.respawn_slaves) {
|
||||
if(plr->slaves.first) {
|
||||
reimu_spirit_kill_slaves(&plr->slaves);
|
||||
} else {
|
||||
reimu_spirit_respawn_slaves(plr, plr->power, 0);
|
||||
reimu_spirit_state.respawn_slaves = false;
|
||||
static void reimu_spirit_respawn_slaves(ReimuAController *ctrl) {
|
||||
Player *plr = ctrl->plr;
|
||||
int power_rank = plr->power / 100;
|
||||
|
||||
reimu_spirit_kill_slaves(ctrl);
|
||||
|
||||
if(plr->inputflags & INFLAG_FOCUS) {
|
||||
reimu_spirit_spawn_slaves_focused(ctrl, power_rank);
|
||||
} else {
|
||||
reimu_spirit_spawn_slaves_unfocused(ctrl, power_rank);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(reimu_spirit_focus_handler, { ReimuAController *ctrl; }) {
|
||||
ReimuAController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
reimu_spirit_respawn_slaves(ctrl);
|
||||
|
||||
uint prev_inputflags = 0;
|
||||
|
||||
for(;;) {
|
||||
WAIT_EVENT_OR_DIE(&plr->events.inputflags_changed);
|
||||
|
||||
// focus state changed?
|
||||
while((prev_inputflags ^ plr->inputflags) & INFLAG_FOCUS) {
|
||||
if(plr->slaves.first) {
|
||||
reimu_spirit_kill_slaves(ctrl);
|
||||
WAIT(ORB_RETRACT_TIME);
|
||||
}
|
||||
|
||||
// important to record these at the time of respawning
|
||||
prev_inputflags = plr->inputflags;
|
||||
reimu_spirit_respawn_slaves(ctrl);
|
||||
|
||||
// "shift-spam" protection - minimum time until next respawn
|
||||
WAIT(ORB_RETRACT_TIME * 2);
|
||||
}
|
||||
}
|
||||
|
||||
reimu_spirit_state.prev_inputflags = plr->inputflags;
|
||||
}
|
||||
|
||||
static void reimu_spirit_power(Player *plr, short npow) {
|
||||
if(plr->power / 100 != npow / 100) {
|
||||
reimu_spirit_respawn_slaves(plr, npow, 0);
|
||||
TASK(reimu_spirit_power_handler, { ReimuAController *ctrl; }) {
|
||||
ReimuAController *ctrl = ARGS.ctrl;
|
||||
Player *plr = ctrl->plr;
|
||||
int old_power = plr->power / 100;
|
||||
|
||||
for(;;) {
|
||||
WAIT_EVENT_OR_DIE(&plr->events.power_changed);
|
||||
int new_power = plr->power / 100;
|
||||
if(old_power != new_power) {
|
||||
reimu_spirit_respawn_slaves(ctrl);
|
||||
old_power = new_power;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TASK(reimu_spirit_shot_forward, { ReimuAController *ctrl; }) {
|
||||
Player *plr = ARGS.ctrl->plr;
|
||||
real dir = 1;
|
||||
|
||||
for(;;) {
|
||||
WAIT_EVENT_OR_DIE(&plr->events.shoot);
|
||||
play_loop("generic_shot");
|
||||
INVOKE_TASK(reimu_spirit_ofuda,
|
||||
.pos = plr->pos + 10 * dir - 15.0*I,
|
||||
.vel = -20*I,
|
||||
.damage = 100 - 8 * (plr->power / 100)
|
||||
);
|
||||
dir = -dir;
|
||||
WAIT(3);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(reimu_spirit_shot_volley_bullet, { Player *plr; cmplx offset; cmplx vel; real damage; ShaderProgram *shader; }) {
|
||||
play_loop("generic_shot");
|
||||
|
||||
PROJECTILE(
|
||||
.proto = pp_hakurei_seal,
|
||||
.pos = ARGS.plr->pos + ARGS.offset,
|
||||
.color = color_mul_scalar(RGBA(1, 1, 1, 0.5), 0.7),
|
||||
.move = move_linear(ARGS.vel),
|
||||
.type = PROJ_PLAYER,
|
||||
.damage = ARGS.damage,
|
||||
.shader = "sprite_particle",
|
||||
.shader_ptr = ARGS.shader,
|
||||
);
|
||||
}
|
||||
|
||||
TASK(reimu_spirit_shot_volley, { ReimuAController *ctrl; }) {
|
||||
Player *plr = ARGS.ctrl->plr;
|
||||
ShaderProgram *shader = r_shader_get("sprite_particle");
|
||||
|
||||
for(;;) {
|
||||
WAIT_EVENT_OR_DIE(&plr->events.shoot);
|
||||
|
||||
int power_rank = plr->power / 100;
|
||||
real damage = 60 - 5 * power_rank;
|
||||
|
||||
for(int pwr = 0; pwr <= power_rank; ++pwr) {
|
||||
int delay = 5 * pwr;
|
||||
real spread = M_PI/32 * (1 + 0.35 * pwr);
|
||||
|
||||
INVOKE_SUBTASK_DELAYED(delay, reimu_spirit_shot_volley_bullet,
|
||||
plr,
|
||||
.offset = -I + 5,
|
||||
.vel = -18 * I * cdir(spread),
|
||||
.damage = damage,
|
||||
.shader = shader
|
||||
);
|
||||
|
||||
INVOKE_SUBTASK_DELAYED(delay, reimu_spirit_shot_volley_bullet,
|
||||
plr,
|
||||
.offset = -I - 5,
|
||||
.vel = -18 * I * cdir(-spread),
|
||||
.damage = damage,
|
||||
.shader = shader
|
||||
);
|
||||
}
|
||||
|
||||
WAIT(16);
|
||||
}
|
||||
}
|
||||
|
||||
TASK(reimu_spirit_controller, { BoxedPlayer plr; }) {
|
||||
ReimuAController ctrl;
|
||||
ctrl.plr = TASK_BIND(ARGS.plr);
|
||||
COEVENT_INIT_ARRAY(ctrl.events);
|
||||
|
||||
INVOKE_SUBTASK(reimu_spirit_focus_handler, &ctrl);
|
||||
INVOKE_SUBTASK(reimu_spirit_power_handler, &ctrl);
|
||||
INVOKE_SUBTASK(reimu_spirit_shot_forward, &ctrl);
|
||||
INVOKE_SUBTASK(reimu_spirit_shot_volley, &ctrl);
|
||||
|
||||
WAIT_EVENT(&TASK_EVENTS(THIS_TASK)->finished);
|
||||
COEVENT_CANCEL_ARRAY(ctrl.events);
|
||||
}
|
||||
|
||||
static void reimu_spirit_init(Player *plr) {
|
||||
reimu_common_bomb_buffer_init();
|
||||
INVOKE_TASK(reimu_spirit_controller, ENT_BOX(plr));
|
||||
}
|
||||
|
||||
static double reimu_spirit_property(Player *plr, PlrProperty prop) {
|
||||
double base_value = reimu_common_property(plr, prop);
|
||||
|
||||
|
@ -707,11 +876,9 @@ PlayerMode plrmode_reimu_a = {
|
|||
.procs = {
|
||||
.property = reimu_spirit_property,
|
||||
.init = reimu_spirit_init,
|
||||
.think = reimu_spirit_think,
|
||||
.bomb = reimu_spirit_bomb,
|
||||
.bombbg = reimu_spirit_bomb_bg,
|
||||
.shot = reimu_spirit_shot,
|
||||
.power = reimu_spirit_power,
|
||||
.preload = reimu_spirit_preload,
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue