WIP proj draw rule revamp; YoumuB revamp; misc changes; giant mess

cursed commit
This commit is contained in:
Andrei Alexeyev 2019-12-11 11:25:57 +02:00
parent 5c6b7671ef
commit 3d1c0eee4e
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
46 changed files with 1032 additions and 704 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 503 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -44,6 +44,7 @@ atlases = [
['gray16', [preset_png]],
['huge', []],
['portraits', ['--width=4096', '--height=4096']],
['coroutines_temp', []], # TODO merge this into `common` when coroutines are merged
]
atlas_profiles = [

View file

@ -1,3 +1,2 @@
w = 22
h = 23
w = 21
h = 34

2
external/koishi vendored

@ -1 +1 @@
Subproject commit 88939b719021952864030ac9dbc1be4f9d3fa063
Subproject commit 76c7d3d3160da7a2fc73d558b3a0d759f48e8444

View file

@ -0,0 +1,7 @@
# Autogenerated by the atlas packer, do not modify
source = res/gfx/atlas_coroutines_temp_0.webp
# -- Pasted from the global override file --
anisotropy = 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -1,7 +1,7 @@
# Autogenerated by the atlas packer, do not modify
texture = atlas_common_0
region_x = 2362
region_y = 1724
texture = atlas_coroutines_temp_0
region_x = 2
region_y = 2
region_w = 100
region_h = 100

View file

@ -1,12 +1,12 @@
# Autogenerated by the atlas packer, do not modify
texture = atlas_common_0
region_x = 1632
region_y = 372
region_w = 22
region_h = 23
texture = atlas_coroutines_temp_0
region_x = 106
region_y = 2
region_w = 42
region_h = 68
# -- Pasted from the override file --
w = 22
h = 23
w = 21
h = 34

View file

@ -55,11 +55,10 @@ glsl_files = files(
'sprite_hakkero.frag.glsl',
'sprite_hakkero.vert.glsl',
'sprite_negative.frag.glsl',
'sprite_particle.frag.glsl',
'sprite_silhouette.frag.glsl',
'sprite_silhouette.vert.glsl',
'sprite_yinyang.frag.glsl',
'sprite_youmu_charged_shot.frag.glsl',
'sprite_youmu_charged_shot.vert.glsl',
'sprite_youmu_myon_shot.frag.glsl',
'stage1_water.frag.glsl',
'stage6_sky.frag.glsl',

View file

@ -9,5 +9,5 @@ void spriteMain(out vec4 fragColor) {
discard;
}
fragColor = (color * texel.g + vec4(texel.b)) * (1 - customParams.r);
fragColor = (color * texel.g + vec4(texel.b)) * customParams.r;
}

View file

@ -4,8 +4,8 @@
void spriteMain(out vec4 fragColor) {
vec4 texel = texture(tex, texCoord);
float oWhite = texel.b * (1 - clamp(2 * customParams.r, 0, 1));
float oColor = texel.g * (1 - clamp(2 * customParams.r - 1, 0, 1));
float oWhite = texel.b * (1 - clamp(2 * (1 - customParams.r), 0, 1));
float oColor = texel.g * (1 - clamp(2 * (1 - customParams.r) - 1, 0, 1));
float o = clamp(oWhite + oColor, 0, 1);
vec4 col = (texel.g * color + vec4(texel.b)) * o;
col.a *= o;

View file

@ -0,0 +1,7 @@
#version 330 core
#include "lib/sprite_main.frag.glslh"
void spriteMain(out vec4 fragColor) {
fragColor = color * texture(tex, texCoord) * customParams.r;
}

View file

@ -0,0 +1 @@
objects = sprite_bullet.vert sprite_particle.frag

View file

@ -1,24 +0,0 @@
#version 330 core
#include "lib/sprite_main.frag.glslh"
/*
ported from:
.R[1] = mix_colors(c_b, c_r, sqrt(charge)),
.G[1] = c_g,
.B[1] = mix_colors(multiply_colors(c_r, rgba(2, 0, 0, 0)), c_b, 0.75 * charge),
.A[1] = c_a,
*/
void spriteMain(out vec4 fragColor) {
vec4 texel = texture(tex, texCoord);
float charge = customParams.r;
fragColor = vec4(0.0);
fragColor.rgb += texel.r * mix(vec3(color.r, 0.0, 0.0), vec3(0.0, 0.0, color.b), sqrt(charge));
fragColor.g += texel.g * color.g;
fragColor.rgb += texel.b * mix(vec3(0.0, 0.0, color.b), vec3(2.0 * color.r, 0.0, 0.0), 0.75 * charge);
fragColor.a = texel.a * color.a;
fragColor *= customParams.g;
}

View file

@ -1 +0,0 @@
objects = sprite_youmu_charged_shot.vert sprite_youmu_charged_shot.frag

View file

@ -1,7 +0,0 @@
#version 330 core
#define SPRITE_OUT_COLOR
#define SPRITE_OUT_TEXCOORD
#define SPRITE_OUT_CUSTOM
#include "lib/sprite_default.vert.glslh"

View file

@ -9,5 +9,5 @@ void spriteMain(out vec4 fragColor) {
fragColor.rgb += vec3(texel.r);
fragColor.rgb += texel.b * vec3(color.r*color.r, color.g*color.g, color.b*color.b);
fragColor.a = texel.a * color.a;
fragColor *= (1 - customParams.r);
fragColor *= customParams.r;
}

View file

@ -578,7 +578,8 @@ static void draw_spell_portrait(Boss *b, int time) {
r_state_pop();
}
static void BossGlow(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void BossGlow(Projectile *p, int t, ProjDrawRuleArgs args) {
float s = 1.0+t/(double)p->timeout*0.5;
float fade = 1 - (1.5 - s);
float deform = 5 - 10 * fade * fade;
@ -632,7 +633,7 @@ static void spawn_particle_effects(Boss *boss) {
if(!(global.frames % 13) && !is_extra) {
PARTICLE(
.sprite = "smoke",
.pos = cexp(I*global.frames),
.pos = cdir(global.frames),
.color = RGBA(shadowcolor->r, shadowcolor->g, shadowcolor->b, 0.0),
.rule = enemy_flare,
.timeout = 180,
@ -1120,7 +1121,7 @@ void process_boss(Boss **pboss) {
if(dying) {
float t = (global.frames - boss->current->endtime)/(float)BOSS_DEATH_DELAY + 1;
RNG_ARRAY(rng, 6);
RNG_ARRAY(rng, 2);
Color *clr = RGBA_MUL_ALPHA(0.1 + sin(10*t), 0.1 + cos(10*t), 0.5, t);
clr->a = 0;
@ -1129,13 +1130,11 @@ void process_boss(Boss **pboss) {
.sprite = "petal",
.pos = boss->pos,
.rule = asymptotic,
.draw_rule = Petal,
.draw_rule = pdraw_petal_random(),
.color = clr,
.args = {
vrng_sign(rng[5]) * (3 + t * 5 * vrng_real(rng[0])) * cdir(M_PI*8*t),
vrng_sign(rng[0]) * (3 + t * 5 * vrng_real(rng[1])) * cdir(M_PI*8*t),
5+I,
vrng_real(rng[2]) + vrng_real(rng[3])*I,
vrng_real(rng[4]) + 360.0*I*vrng_real(rng[1])
},
.layer = LAYER_PARTICLE_PETAL,
.flags = PFLAG_REQUIREDPARTICLE,
@ -1254,7 +1253,8 @@ void boss_reset_motion(Boss *boss) {
boss->move.retention = 0.8;
}
static void boss_death_effect_draw_overlay(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void boss_death_effect_draw_overlay(Projectile *p, int t, ProjDrawRuleArgs args) {
FBPair *framebuffers = stage_get_fbpair(FBPAIR_FG);
r_framebuffer(framebuffers->front);
r_uniform_sampler("noise_tex", "static");
@ -1361,7 +1361,7 @@ void boss_start_attack(Boss *b, Attack *a) {
.color = RGBA(0.2, 0.3, 0.4, 0.0),
.rule = linear,
.timeout = 50,
.draw_rule = GrowFade,
.draw_rule = pdraw_timeout_scalefade(0, 1, 1, 0),
.args = { vrng_sign(rng[2]) * 10 * vrng_range(rng[3], 1, 4) },
);
}

View file

@ -34,11 +34,6 @@ enum {
COTASK_WAIT_EVENT,
};
typedef struct CoEventSnapshot {
uint32_t unique_id;
uint16_t num_signaled;
} CoEventSnapshot;
struct CoTask {
LIST_INTERFACE(CoTask);
koishi_coroutine_t ko;
@ -237,11 +232,18 @@ static void *cotask_wake_and_resume(CoTask *task, void *arg) {
return cotask_force_resume(task, arg);
}
static CoEventStatus coevent_poll(const CoEvent *evt, const CoEventSnapshot *snap) {
CoEventSnapshot coevent_snapshot(const CoEvent *evt) {
return (CoEventSnapshot) {
.unique_id = evt->unique_id,
.num_signaled = evt->num_signaled,
};
}
CoEventStatus coevent_poll(const CoEvent *evt, const CoEventSnapshot *snap) {
EVT_DEBUG("[%p]", (void*)evt);
EVT_DEBUG("evt->unique_id == %u", evt->unique_id);
EVT_DEBUG("evt->unique_id == %u", evt->unique_id);
EVT_DEBUG("snap->unique_id == %u", snap->unique_id);
EVT_DEBUG("evt->num_signaled == %u", evt->num_signaled);
EVT_DEBUG("evt->num_signaled == %u", evt->num_signaled);
EVT_DEBUG("snap->num_signaled == %u", snap->num_signaled);
if(
@ -351,13 +353,6 @@ int cotask_wait(int delay) {
return 0;
}
static inline CoEventSnapshot coevent_snapshot(CoEvent *evt) {
return (CoEventSnapshot) {
.unique_id = evt->unique_id,
.num_signaled = evt->num_signaled,
};
}
static void coevent_add_subscriber(CoEvent *evt, CoTask *task) {
evt->num_subscribers++;
assert(evt->num_subscribers != 0);

View file

@ -53,6 +53,11 @@ typedef struct CoEvent {
uint8_t num_subscribers_allocated;
} CoEvent;
typedef struct CoEventSnapshot {
uint32_t unique_id;
uint16_t num_signaled;
} CoEventSnapshot;
struct CoSched {
LIST_ANCHOR(CoTask) tasks, pending_tasks;
};
@ -99,6 +104,8 @@ void coevent_init(CoEvent *evt);
void coevent_signal(CoEvent *evt);
void coevent_signal_once(CoEvent *evt);
void coevent_cancel(CoEvent *evt);
CoEventSnapshot coevent_snapshot(const CoEvent *evt);
CoEventStatus coevent_poll(const CoEvent *evt, const CoEventSnapshot *snap);
void _coevent_array_action(uint num, CoEvent *events, void (*func)(CoEvent*));

View file

@ -1,4 +1,5 @@
LAYER(NODRAW)
LAYER(BACKGROUND)
LAYER(PLAYER_SHOT)
LAYER(PARTICLE_LOW)

View file

@ -115,27 +115,50 @@ Enemy *create_enemy_p(EnemyList *enemies, cmplx pos, float hp, EnemyVisualRule v
return e;
}
static void enemy_death_effect(cmplx pos) {
for(int i = 0; i < 10; i++) {
RNG_ARRAY(rng, 2);
PARTICLE(
.sprite = "flare",
.pos = pos,
.timeout = 10,
.rule = linear,
.draw_rule = pdraw_timeout_fade(1, 0),
.args = { vrng_range(rng[0], 3, 13) * vrng_dir(rng[1]) },
);
}
PARTICLE(
.proto = pp_blast,
.pos = pos,
.timeout = 20,
.draw_rule = pdraw_blast(),
.flags = PFLAG_REQUIREDPARTICLE
);
PARTICLE(
.proto = pp_blast,
.pos = pos,
.timeout = 20,
.draw_rule = pdraw_blast(),
.flags = PFLAG_REQUIREDPARTICLE
);
PARTICLE(
.proto = pp_blast,
.pos = pos,
.timeout = 15,
.draw_rule = pdraw_timeout_scalefade(0, rng_f32_range(1, 2), 1, 0),
.flags = PFLAG_REQUIREDPARTICLE
);
}
static void* _delete_enemy(ListAnchor *enemies, List* enemy, void *arg) {
Enemy *e = (Enemy*)enemy;
if(e->hp <= 0 && e->hp != ENEMY_IMMUNE && e->hp != ENEMY_BOMB) {
play_sound("enemydeath");
for(int i = 0; i < 10; i++) {
RNG_ARRAY(rng, 2);
PARTICLE(
.sprite = "flare",
.pos = e->pos,
.timeout = 10,
.rule = linear,
.draw_rule = Fade,
.args = { vrng_range(rng[0], 3, 13) * vrng_dir(rng[1]) },
);
}
PARTICLE(.proto = pp_blast, .pos = e->pos, .timeout = 20, .draw_rule = Blast, .flags = PFLAG_REQUIREDPARTICLE);
PARTICLE(.proto = pp_blast, .pos = e->pos, .timeout = 20, .draw_rule = Blast, .flags = PFLAG_REQUIREDPARTICLE);
PARTICLE(.proto = pp_blast, .pos = e->pos, .timeout = 15, .draw_rule = GrowFade, .flags = PFLAG_REQUIREDPARTICLE);
enemy_death_effect(e->pos);
for(Projectile *p = global.projs.first; p; p = p->next) {
if(p->type == PROJ_ENEMY && !(p->flags & PFLAG_NOCOLLISION) && cabs(p->pos - e->pos) < 64) {
@ -242,7 +265,8 @@ void BigFairy(Enemy *e, int t, bool render) {
.pos = offset,
.color = RGBA(0.0, 0.2, 0.3, 0.0),
.rule = enemy_flare,
.draw_rule = Shrink,
.draw_rule = pdraw_timeout_scalefade(2+2*I, 0.5+2*I, 1, 0),
.angle = M_PI/2,
.timeout = 50,
.args = { (-50.0*I-offset)/50.0, add_ref(e) },
);

View file

@ -129,6 +129,10 @@ static int ent_cmp(const void *ptr1, const void *ptr2) {
return r;
}
static inline bool ent_is_drawable(EntityInterface *ent) {
return (ent->draw_layer & ~LAYER_LOW_MASK) > LAYER_NODRAW && ent->draw_func;
}
void ent_draw(EntityPredicate predicate) {
call_hooks(&entities.hooks.pre_draw, NULL);
qsort(entities.array, entities.num, sizeof(EntityInterface*), ent_cmp);
@ -138,7 +142,7 @@ void ent_draw(EntityPredicate predicate) {
ent->index = _ent - entities.array;
assert(entities.array[ent->index] == ent);
if(ent->draw_func && predicate(ent)) {
if(ent_is_drawable(ent) && predicate(ent)) {
call_hooks(&entities.hooks.pre_draw, ent);
r_state_push();
ent->draw_func(ent);
@ -151,7 +155,7 @@ void ent_draw(EntityPredicate predicate) {
ent->index = _ent - entities.array;
assert(entities.array[ent->index] == ent);
if(ent->draw_func) {
if(ent_is_drawable(ent)) {
call_hooks(&entities.hooks.pre_draw, ent);
r_state_push();
ent->draw_func(ent);

View file

@ -274,6 +274,17 @@ INLINE void _ent_array_add_Entity(struct EntityInterface *ent, BoxedEntityArray
} \
} while(0)
#define ENT_ARRAY_FOREACH_COUNTER(_array, _cntr_var, _ent_var, _block) do { \
for(uint MACROHAX_ADDLINENUM(_ent_array_iterator) = 0; MACROHAX_ADDLINENUM(_ent_array_iterator) < (_array)->size; ++MACROHAX_ADDLINENUM(_ent_array_iterator)) { \
void *MACROHAX_ADDLINENUM(_ent_array_temp) = ENT_ARRAY_GET((_array), MACROHAX_ADDLINENUM(_ent_array_iterator)); \
if(MACROHAX_ADDLINENUM(_ent_array_temp) != NULL) { \
_cntr_var = MACROHAX_ADDLINENUM(_ent_array_iterator); \
_ent_var = MACROHAX_ADDLINENUM(_ent_array_temp); \
_block \
} \
} \
} while(0)
#define ENT_ARRAY_CLEAR(_array) ((_array)->size = 0)
#endif // IGUARD_entity_h

View file

@ -431,7 +431,8 @@ static void _powersurge_trail_draw(Projectile *p, float t, float cmul) {
});
}
static void powersurge_trail_draw(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void powersurge_trail_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
if(t > 0) {
_powersurge_trail_draw(p, t - 0.5, 0.25);
_powersurge_trail_draw(p, t, 0.25);
@ -709,7 +710,8 @@ static int powersurge_discharge(Projectile *p, int t) {
return ACTION_NONE;
}
static void powersurge_distortion_draw(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void powersurge_distortion_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
if(config_get_int(CONFIG_POSTPROCESS) < 1) {
return;
}
@ -774,7 +776,7 @@ static void player_powersurge_expired(Player *plr) {
PROJECTILE(
.pos = plr->pos,
.size = 1+I,
.draw_rule = ProjNoDraw,
.layer = LAYER_NODRAW,
.timeout = 10,
.type = PROJ_PLAYER,
.rule = powersurge_discharge,
@ -849,7 +851,8 @@ void player_realdeath(Player *plr) {
plr->lives--;
}
static void player_death_effect_draw_overlay(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void player_death_effect_draw_overlay(Projectile *p, int t, ProjDrawRuleArgs args) {
FBPair *framebuffers = stage_get_fbpair(FBPAIR_FG);
r_framebuffer(framebuffers->front);
r_uniform_sampler("noise_tex", "static");
@ -868,7 +871,8 @@ static void player_death_effect_draw_overlay(Projectile *p, int t) {
r_state_push();
}
static void player_death_effect_draw_sprite(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void player_death_effect_draw_sprite(Projectile *p, int t, ProjDrawRuleArgs args) {
float s = t / p->timeout;
float stretch_range = 3, sx, sy;

View file

@ -315,7 +315,8 @@ static int marisa_laser_renderer(Enemy *renderer, int t) {
#undef FOR_EACH_SLAVE
#undef FOR_EACH_REAL_SLAVE
static void marisa_laser_flash_draw(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void marisa_laser_flash_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
Animation *fire = get_ani("fire");
AniSequence *seq = get_ani_sequence(fire, "main");
Sprite *spr = animation_get_frame(fire, seq, p->birthtime);

View file

@ -77,15 +77,6 @@ static int reimu_spirit_needle(Projectile *p, int t) {
#define REIMU_SPIRIT_HOMING_SCALE 0.75
static void reimu_spirit_homing_draw(Projectile *p, int t) {
r_mat_mv_push();
r_mat_mv_translate(creal(p->pos), cimag(p->pos), 0);
r_mat_mv_rotate(p->angle + M_PI/2, 0, 0, 1);
r_mat_mv_scale(REIMU_SPIRIT_HOMING_SCALE, REIMU_SPIRIT_HOMING_SCALE, 1);
ProjDrawCore(p, &p->color);
r_mat_mv_pop();
}
static Projectile* reimu_spirit_spawn_ofuda_particle(Projectile *p, int t, double vfactor) {
Color *c = HSLA_MUL_ALPHA(t * 0.1, 0.6, 0.7, 0.3);
c->a = 0;
@ -173,24 +164,6 @@ static Color *reimu_spirit_orb_color(Color *c, int i) {
return c;
}
static void reimu_spirit_bomb_orb_visual(Projectile *p, int t) {
cmplx pos = p->pos;
for(int i = 0; i < 3; i++) {
cmplx offset = (10 + pow(t, 0.5)) * cexp(I * (2 * M_PI / 3*i + sqrt(1 + t * t / 300.0)));
Color c;
r_draw_sprite(&(SpriteParams) {
.sprite_ptr = p->sprite,
.shader_ptr = p->shader,
.pos = { creal(pos+offset), cimag(pos+offset) },
.color = reimu_spirit_orb_color(&c, i),
// .shader_params = &(ShaderCustomParams) {.vector = {0.3,0,0,0}},
});
}
}
static int reimu_spirit_bomb_orb_trail(Projectile *p, int t) {
if(t < 0) {
return ACTION_ACK;
@ -204,162 +177,219 @@ static int reimu_spirit_bomb_orb_trail(Projectile *p, int t) {
return ACTION_NONE;
}
static void reimu_spirit_bomb_orb_draw_impact(Projectile *p, int t) {
float attack = min(1, (7 + 5 * p->args[0]) * t / p->timeout);
float decay = t / p->timeout;
static void reimu_spirit_bomb_impact_balls(cmplx pos, int count) {
real offset = rng_real();
Color c = p->color;
color_lerp(&c, RGBA(0.2, 0.1, 0, 1.0), decay);
color_mul_scalar(&c, pow(1 - decay, 2) * 0.75);
for(int i = 0; i < count; i++) {
PARTICLE(
.sprite_ptr = get_sprite("proj/glowball"),
.shader = "sprite_bullet",
.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,
.draw_rule = Fade,
.layer = LAYER_BOSS,
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
);
}
}
r_draw_sprite(&(SpriteParams) {
.sprite_ptr = p->sprite,
.pos = { creal(p->pos), cimag(p->pos) },
.color = &c,
.shader_ptr = p->shader,
.shader_params = &p->shader_params,
.scale.both = (0.75 + 0.25 / (pow(decay, 3.0) + 1.0)) + sqrt(5 * (1 - attack)),
TASK(reimu_spirit_bomb_orb_impact, { BoxedProjectile orb; }) {
cmplx pos = ENT_UNBOX(ARGS.orb)->pos;
play_sound("boom");
play_sound("spellend");
global.shake_view = 20;
global.shake_view_fade = 0.6;
double damage = 2000;
double range = 300;
ent_area_damage(pos, range, &(DamageInfo){damage, DMG_PLAYER_BOMB}, NULL, NULL);
stage_clear_hazards_at(pos, range, CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_NOW);
reimu_spirit_bomb_impact_balls(pos, 21);
int num_impacts = 3;
int t = global.frames;
BoxedProjectileArray impact_effects = ENT_ARRAY(Projectile, 3);
RNG_ARRAY(rand, num_impacts);
Color base_colors[3];
for(int i = 0; i < 3; ++i) {
base_colors[i] = *reimu_spirit_orb_color(&(Color){0}, i);
PARTICLE(
.sprite = "blast",
.color = color_mul_scalar(COLOR_COPY(&base_colors[i]), 2),
.pos = pos + 30 * cexp(I*2*M_PI/num_impacts*(i+t*0.1)),
.timeout = 40,
.draw_rule = ScaleFade,
.layer = LAYER_BOSS + 2,
.args = { 0, 0, 7.5*I },
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
);
ENT_ARRAY_ADD(&impact_effects, PARTICLE(
.sprite = "fantasyseal_impact",
.color = reimu_spirit_orb_color(&(Color){0}, i),
.pos = pos + 2 * cexp(I*2*M_PI/num_impacts*(i+t*0.1)),
.timeout = 120,
.layer = LAYER_BOSS + 1,
.angle = -M_PI/2,
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
));
}
for(;;) {
int live = 0;
ENT_ARRAY_FOREACH_COUNTER(&impact_effects, int i, Projectile *p, {
float t = (global.frames - p->birthtime) / p->timeout;
float attack = min(1, vrng_range(rand[i], 7, 12) * t);
float decay = t;
Color c = base_colors[i];
color_lerp(&c, RGBA(0.2, 0.1, 0, 1.0), decay);
color_mul_scalar(&c, powf(1.0f - decay, 2.0f) * 0.75f);
p->color = c;
p->scale = (0.75f + 0.25f / (powf(decay, 3.0f) + 1.0f)) + sqrtf(5.0f * (1.0f - attack));
++live;
});
if(!live) {
break;
}
YIELD;
}
}
TASK(reimu_spirit_bomb_orb_visual_kill, { BoxedProjectileArray components; }) {
ENT_ARRAY_FOREACH(&ARGS.components, Projectile *p, {
kill_projectile(p);
});
}
static int reimu_spirit_bomb_orb(Projectile *p, int t) {
int index = creal(p->args[1]) + 0.5;
TASK(reimu_spirit_bomb_orb_visual, { BoxedProjectile orb; }) {
Projectile *orb = TASK_BIND(ARGS.orb);
DECLARE_ENT_ARRAY(Projectile, components, 3);
if(t == EVENT_BIRTH) {
if(index == 0)
global.shake_view = 4;
p->args[3] = global.plr.pos;
return ACTION_ACK;
Sprite *glowball = get_sprite("proj/glowball");
ShaderProgram *shader = r_shader_get("sprite_bullet");
for(int i = 0; i < components.capacity; ++i) {
ENT_ARRAY_ADD(&components, PARTICLE(
.sprite_ptr = glowball,
.shader_ptr = shader,
.color = reimu_spirit_orb_color(&(Color){0}, i),
.opacity = 0.7,
.layer = LAYER_PLAYER_FOCUS - 1,
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
));
}
if(t == EVENT_DEATH) {
if(global.gameover > 0) {
return ACTION_ACK;
INVOKE_TASK_AFTER(&orb->events.killed, reimu_spirit_bomb_orb_visual_kill, components);
CANCEL_TASK_AFTER(&orb->events.killed, THIS_TASK);
for(;;) {
cmplx pos = orb->pos;
ENT_ARRAY_FOREACH_COUNTER(&components, int i, Projectile *p, {
real t = global.frames - p->birthtime;
cmplx32 offset = (10 + pow(t, 0.5)) * cdir(2.0 * M_PI / 3*i + sqrt(1 + t * t / 300));
p->pos = pos + offset;
});
YIELD;
}
}
TASK(reimu_spirit_bomb_orb, { BoxedPlayer plr; int index; real angle; }) {
int index = ARGS.index;
Player *plr = ENT_UNBOX(ARGS.plr);
Projectile *orb = TASK_BIND_UNBOXED(PROJECTILE(
.pos = plr->pos,
.timeout = 200 + 20 * index,
.type = PROJ_PLAYER,
.damage = 1000,
.damage_type = DMG_PLAYER_BOMB,
.size = 10 * (1+I),
.layer = LAYER_NODRAW,
.flags = PFLAG_NOREFLECT | PFLAG_NOCOLLISION | PFLAG_NOMOVE | PFLAG_MANUALANGLE,
));
BoxedProjectile b_orb = ENT_BOX(orb);
INVOKE_TASK(reimu_spirit_bomb_orb_visual, b_orb);
INVOKE_TASK_WHEN(&orb->events.killed, reimu_spirit_bomb_orb_impact, b_orb);
CANCEL_TASK_AFTER(&orb->events.killed, THIS_TASK);
int circletime = 100 + 20 * index;
cmplx target_homing = plr->pos;
cmplx dir = cdir(ARGS.angle);
cmplx vel = 0;
for(int t = 0;; ++t) {
if(!player_is_bomb_active(plr)) {
kill_projectile(orb);
return;
}
global.shake_view = 20;
global.shake_view_fade = 0.6;
if(t == circletime) {
target_homing = global.plr.pos - 256*I;
orb->flags &= ~PFLAG_NOCOLLISION;
play_sound("redirect");
}
double damage = 2000;
double range = 300;
cmplx target_circle = plr->pos + 10 * sqrt(t) * dir * (1 + 0.1 * sin(0.2 * t));
dir *= cdir(0.12);
ent_area_damage(p->pos, range, &(DamageInfo){damage, DMG_PLAYER_BOMB}, NULL, NULL);
stage_clear_hazards_at(p->pos, range, CLEAR_HAZARDS_ALL | CLEAR_HAZARDS_NOW);
double circlestrength = 1.0 / (1 + exp(t - circletime));
int count = 21;
real offset = rng_real();
target_homing = plrutil_homing_target(orb->pos, target_homing);
cmplx homing = target_homing - orb->pos;
cmplx v = 0.3 * (circlestrength * (target_circle - orb->pos) + 0.2 * (1 - circlestrength) * (homing + 2*homing/(cabs(homing)+0.01)));
vel += (v - vel) * 0.1;
orb->pos += vel;
for(int i = 0; i < 3; i++) {
cmplx trail_pos = orb->pos + 10 * cdir(2*M_PI/3*(i+t*0.1));
cmplx trail_vel = global.plr.pos - trail_pos;
trail_vel *= 3 * circlestrength / cabs(trail_vel);
for(int i = 0; i < count; i++) {
PARTICLE(
.sprite_ptr = get_sprite("proj/glowball"),
.shader = "sprite_bullet",
.color = HSLA(3 * (float)i / count + offset, 1, 0.5, 0),
.timeout = 60,
.pos = p->pos,
.args = { cdir(2 * M_PI / count * (i + offset)) * 15 },
.sprite_ptr = get_sprite("part/stain"),
// .color = reimu_spirit_orb_color(&(Color){0}, i),
.color = HSLA(t/orb->timeout, 0.3, 0.3, 0.0),
.pos = trail_pos,
.angle = rng_angle(),
.rule = linear,
.draw_rule = Fade,
.layer = LAYER_BOSS,
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
);
}
for(int i = 0; i < 3; ++i) {
PARTICLE(
.sprite = "blast",
.color = color_mul_scalar(reimu_spirit_orb_color(&(Color){0}, i), 2),
.pos = p->pos + 30 * cexp(I*2*M_PI/3*(i+t*0.1)),
.timeout = 40,
.timeout = 30,
.draw_rule = ScaleFade,
.layer = LAYER_BOSS + 2,
.args = { 0, 0, 7.5*I },
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
);
PARTICLE(
.sprite = "fantasyseal_impact",
.color = reimu_spirit_orb_color(&(Color){0}, i),
.pos = p->pos + 2 * cexp(I*2*M_PI/3*(i+t*0.1)),
.timeout = 120,
.draw_rule = reimu_spirit_bomb_orb_draw_impact,
.layer = LAYER_BOSS + 1,
.args = { rng_real() },
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
.rule = reimu_spirit_bomb_orb_trail,
.args = { trail_vel, 0, 0.4 },
.flags = PFLAG_NOREFLECT,
);
}
play_sound("boom");
play_sound("spellend");
return ACTION_ACK;
YIELD;
}
if(!player_is_bomb_active(&global.plr) > 0) {
return ACTION_DESTROY;
}
double circletime = 100+20*index;
if(t == circletime) {
p->args[3] = global.plr.pos - 256*I;
p->flags &= ~PFLAG_NOCOLLISION;
play_sound("redirect");
}
cmplx target_circle = global.plr.pos + 10 * sqrt(t) * p->args[0]*(1 + 0.1 * sin(0.2*t));
p->args[0] *= cexp(I*0.12);
double circlestrength = 1.0 / (1 + exp(t-circletime));
p->args[3] = plrutil_homing_target(p->pos, p->args[3]);
cmplx target_homing = p->args[3];
cmplx homing = target_homing - p->pos;
cmplx v = 0.3 * (circlestrength * (target_circle - p->pos) + 0.2 * (1-circlestrength) * (homing + 2*homing/(cabs(homing)+0.01)));
p->args[2] += (v - p->args[2]) * 0.1;
p->pos += p->args[2];
for(int i = 0; i < 3 /*&& circlestrength < 1*/; i++) {
cmplx trail_pos = p->pos + 10 * cexp(I*2*M_PI/3*(i+t*0.1));
cmplx trail_vel = global.plr.pos - trail_pos;
trail_vel *= 3 * circlestrength / cabs(trail_vel);
PARTICLE(
.sprite_ptr = get_sprite("part/stain"),
// .color = reimu_spirit_orb_color(&(Color){0}, i),
.color = HSLA(t/p->timeout, 0.3, 0.3, 0.0),
.pos = trail_pos,
.angle = rng_angle(),
.timeout = 30,
.draw_rule = ScaleFade,
.rule = reimu_spirit_bomb_orb_trail,
.args = { trail_vel, 0, 0.4 },
.flags = PFLAG_NOREFLECT,
);
}
return ACTION_NONE;
}
static void reimu_spirit_bomb(Player *p) {
int count = 6;
for(int i = 0; i < count; i++) {
PROJECTILE(
.sprite = "glowball",
.pos = p->pos,
.draw_rule = reimu_spirit_bomb_orb_visual,
.rule = reimu_spirit_bomb_orb,
.args = { cexp(I*2*M_PI/count*i), i, 0, 0},
.timeout = 200 + 20 * i,
.type = PROJ_PLAYER,
.damage = 1000,
.size = 10 + 10*I,
.layer = LAYER_PLAYER_FOCUS - 1,
.flags = PFLAG_NOREFLECT | PFLAG_NOCOLLISION,
);
INVOKE_TASK_DELAYED(1, reimu_spirit_bomb_orb, ENT_BOX(p), i, 2*M_PI/count*i);
}
global.shake_view = 4;
play_sound("bomb_reimu_a");
play_sound("bomb_marisa_b");
}
@ -446,13 +476,13 @@ static void reimu_spirit_slave_shot(Enemy *e, int t) {
.pos = e->pos,
.color = RGBA_MUL_ALPHA(1, 0.9, 0.95, 0.7),
.rule = reimu_spirit_homing,
.draw_rule = reimu_spirit_homing_draw,
.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_default",
.scale = REIMU_SPIRIT_HOMING_SCALE,
.flags = PFLAG_NOCOLLISIONEFFECT,
);
}

View file

@ -108,12 +108,12 @@ static int reimu_dream_gap_bomb_projectile(Projectile *p, int t) {
return ACTION_NONE;
}
static void reimu_dream_gap_bomb_projectile_draw(Projectile *p, int t) {
static void reimu_dream_gap_bomb_projectile_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
r_draw_sprite(&(SpriteParams) {
.sprite_ptr = p->sprite,
.shader_ptr = p->shader,
.color = &p->color,
.shader_params = &p->shader_params,
.shader_params = &(ShaderCustomParams) {{ p->opacity }},
.pos = { creal(p->pos), cimag(p->pos) },
.scale.both = 0.75 * clamp(t / 5.0, 0.1, 1.0),
});
@ -341,10 +341,11 @@ static void reimu_dream_spawn_warp_effect(cmplx pos, bool exit) {
.layer = LAYER_PLAYER_FOCUS,
);
Color *clr = color_mul_scalar(RGBA(0.75, rng_range(0, 0.4), 0.4, 0), 0.8-0.4*exit);
PARTICLE(
.sprite = exit ? "stain" : "stardust",
.pos = pos,
.color = color_mul_scalar(RGBA(0.75, rng_range(0, 0.4), 0.4, 0), 0.8-0.4*exit),
.color = clr,
.timeout = 20,
.angle = rng_angle(),
.draw_rule = ScaleFade,

View file

@ -56,7 +56,8 @@ static int myon_flare_particle_rule(Projectile *p, int t) {
return r;
}
static void myon_draw_trail(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void myon_draw_trail(Projectile *p, int t, ProjDrawRuleArgs args) {
float fadein = clamp(t/10.0, p->args[2], 1);
float s = min(1, 1 - t / (double)p->timeout);
float a = p->color.r*fadein;
@ -128,7 +129,8 @@ static void myon_spawn_trail(Enemy *e, int t) {
spawn_stardust(pos, f, 60, stardust_v);
}
static void myon_draw_proj_trail(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void myon_draw_proj_trail(Projectile *p, int t, ProjDrawRuleArgs args) {
float time_progress = t / p->timeout;
float s = 2 * time_progress;
float a = min(1, s) * (1 - time_progress);
@ -166,12 +168,13 @@ static int myon_proj(Projectile *p, int t) {
.angle = p->angle,
);
p->shader_params.vector[0] = pow(1 - min(1, t / 10.0), 2);
p->opacity = 1.0f - powf(1.0f - fminf(1.0f, t / 10.0f), 2.0f);
return ACTION_NONE;
}
static void myon_proj_draw(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void myon_proj_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
youmu_common_draw_proj(p, &p->color, 1);
}
@ -402,13 +405,11 @@ static void youmu_mirror_bomb_damage_callback(EntityInterface *victim, cmplx vic
.sprite = "petal",
.pos = victim_origin,
.rule = asymptotic,
.draw_rule = Petal,
.draw_rule = pdraw_petal_random(),
.color = RGBA(sin(5*t) * t, cos(5*t) * t, 0.5 * t, 0),
.args = {
vrng_sign(R[0]) * vrng_range(R[1], 3, 3 + 5 * t) * cdir(M_PI*8*t),
5+I,
vrng_real(R[2]) + vrng_real(R[3])*I,
vrng_real(R[4]) + vrng_range(R[5], 0, 360)*I,
},
.layer = LAYER_PARTICLE_PETAL,
);

View file

@ -11,224 +11,46 @@
#include "global.h"
#include "plrmodes.h"
#include "youmu.h"
#include "util/glm.h"
static cmplx youmu_homing_target(cmplx org, cmplx fallback) {
return plrutil_homing_target(org, fallback);
}
static void youmu_homing_draw_common(Projectile *p, float clrfactor, float scale, float alpha) {
Color c = p->color;
color_mul(&c, RGBA(0.7f + 0.3f * clrfactor, 0.9f + 0.1f * clrfactor, 1, 1));
static void youmu_homing_trail(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);
if(alpha <= 0) {
return;
}
RNG_ARRAY(R, 5);
bool special_snowflake_shader_bullshit = p->shader_params.vector[1] != 0;
if(special_snowflake_shader_bullshit) {
// FIXME: maybe move this to logic someh-- nah. Don't even bother with this crap.
float old = p->shader_params.vector[1];
p->shader_params.vector[1] = alpha;
youmu_common_draw_proj(p, &c, scale);
p->shader_params.vector[1] = old;
} else {
color_mul_scalar(&c, alpha);
youmu_common_draw_proj(p, &c, scale);
}
}
static void youmu_homing_draw_proj(Projectile *p, int t) {
float a = clamp(1.0f - (float)t / p->args[2], 0, 1);
youmu_homing_draw_common(p, a, 1, 0.5f);
}
static void youmu_homing_draw_trail(Projectile *p, int t) {
float a = clamp(1.0f - (float)t / p->timeout, 0, 1);
youmu_homing_draw_common(p, a, 5 * (1 - a), 0.15f * a);
}
static void youmu_trap_draw_trail(Projectile *p, int t) {
float a = clamp(1.0f - (float)t / p->timeout, 0, 1);
youmu_homing_draw_common(p, a, 2 - a, 0.15f * a);
}
static void youmu_trap_draw_child_proj(Projectile *p, int t) {
float to = p->args[2];
float a = clamp(1.0 - 3 * ((t - (to - to/3)) / to), 0, 1);
a = 1 - pow(1 - a, 2);
youmu_homing_draw_common(p, a, 1 + 2 * pow(1 - a, 2), a);
}
static float youmu_trap_charge(int t) {
return pow(clamp(t / 60.0, 0, 1), 1.5);
}
static Projectile* youmu_homing_trail(Projectile *p, cmplx v, int to) {
return PARTICLE(
.sprite_ptr = p->sprite,
.pos = p->pos,
.color = &p->color,
.angle = p->angle,
.rule = linear,
PARTICLE(
.sprite = "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),
.draw_rule = pdraw_timeout_scalefade_exp(0.001*I, vrng_range(R[3], 0.5, 1.0)*(1+I), 2, 0, 2),
.move = move_linear(-v),
.timeout = to,
.scale = 0.5,
.angle = vrng_angle(R[4]),
.flags = PFLAG_NOREFLECT | PFLAG_PLRSPECIALPARTICLE | PFLAG_MANUALANGLE,
.layer = LAYER_PARTICLE_MID,
);
PARTICLE(
.sprite = "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),
.draw_rule = pdraw_timeout_scalefade_exp(0.5+0.5*I, 3+7*I, 1, 0, 2),
.timeout = to,
.draw_rule = youmu_homing_draw_trail,
.args = { v },
.flags = PFLAG_NOREFLECT,
.shader_ptr = p->shader,
.shader_params = &p->shader_params,
.layer = LAYER_PARTICLE_LOW,
);
}
static int youmu_homing(Projectile *p, int t) { // a[0]: velocity, a[1]: aim (r: base, i: gain), a[2]: (r: timeout, i: charge), a[3]: initial target
if(t == EVENT_BIRTH) {
return ACTION_ACK;
}
if(t == EVENT_DEATH) {
PARTICLE(
.sprite = "blast",
.color = color_lerp(RGBA(0.5, 0.7, 1.0, 0.5), RGBA(1.0, 0.65, 0.8, 0.5), cimag(p->args[2])),
.pos = p->pos,
.timeout = 20,
.draw_rule = ScaleFade,
.layer = LAYER_PARTICLE_HIGH,
.args = { 0, 0, 0.5 * I },
.flags = PFLAG_NOREFLECT,
.angle = rng_angle(),
);
return ACTION_ACK;
}
if(t > creal(p->args[2])) {
return ACTION_DESTROY;
}
p->args[3] = youmu_homing_target(p->pos, p->args[3]);
double v = cabs(p->args[0]);
cmplx aimdir = cexp(I*carg(p->args[3] - p->pos));
p->args[0] += creal(p->args[1]) * aimdir;
// p->args[0] = v * cexp(I*carg(p->args[0])) + cimag(p->args[1]) * aimdir;
p->args[0] *= v / cabs(p->args[0]);
p->args[1] = creal(p->args[1]) + cimag(p->args[1]) * (1 + I);
p->angle = carg(p->args[0]);
p->pos += p->args[0];
Projectile *trail = youmu_homing_trail(p, 0.5 * p->args[0], 12);
trail->args[2] = p->args[2];
p->shader_params.vector[0] = cimag(p->args[2]);
trail->shader_params.vector[0] = cimag(p->args[2]);
return 1;
}
static Projectile* youmu_trap_trail(Projectile *p, cmplx v, int t, bool additive) {
Projectile *trail = youmu_homing_trail(p, v, t);
trail->draw_rule = youmu_trap_draw_trail;
// trail->args[3] = global.frames - p->birthtime;
trail->shader_params.vector[0] = p->shader_params.vector[0];
trail->flags |= PFLAG_REQUIREDPARTICLE;
if(additive) {
trail->color.a = 0;
} else {
trail->flags |= PFLAG_PLRSPECIALPARTICLE;
}
return trail;
}
static int youmu_trap(Projectile *p, int t) {
if(t == EVENT_DEATH) {
PARTICLE(
.proto = pp_blast,
.pos = p->pos,
.timeout = 15,
.draw_rule = Blast,
.flags = PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_LOW,
);
return ACTION_ACK;
}
// FIXME: replace this with timeout?
double expiretime = creal(p->args[1]);
if(t > expiretime) {
return ACTION_DESTROY;
}
if(t < 0) {
return ACTION_ACK;
}
float charge = youmu_trap_charge(t);
p->shader_params.vector[0] = charge;
if(!(global.plr.inputflags & INFLAG_FOCUS)) {
PARTICLE(
.proto = pp_blast,
.pos = p->pos,
.timeout = 20,
.draw_rule = Blast,
.flags = PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_LOW,
);
PARTICLE(
.proto = pp_blast,
.pos = p->pos,
.timeout = 23,
.draw_rule = Blast,
.flags = PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_LOW,
);