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,
);
int cnt = round(creal(p->args[2]));
int dmg = cimag(p->args[2]);
cmplx aim = p->args[3];
for(int i = 0; i < cnt; ++i) {
int dur = 120; // 55 + 20 * nfrand();
float a = (i / (float)cnt) * M_PI * 2;
cmplx dir = cexp(I*(a));
PROJECTILE(
.proto = pp_youmu,
.pos = p->pos,
.color = RGBA(1, 1, 1, 0.85),
.rule = youmu_homing,
.args = { 5 * (1 + charge) * dir, aim, dur + charge*I, creal(p->pos) - VIEWPORT_H*I },
.type = PROJ_PLAYER,
.damage = dmg,
.draw_rule = youmu_trap_draw_child_proj,
.shader = "sprite_youmu_charged_shot",
.shader_params = &(ShaderCustomParams){{ 0, 1 }},
);
}
// TODO: dedicated sound for this?
play_sound("enemydeath");
play_sound("hit");
return ACTION_DESTROY;
}
p->angle = global.frames + t;
p->pos += p->args[0] * (0.01 + 0.99 * max(0, (10 - t) / 10.0));
youmu_trap_trail(p, cexp(I*p->angle), 30 * (1 + charge), true);
youmu_trap_trail(p, cexp(I*-p->angle), 30, false);
return 1;
}
static void youmu_particle_slice_draw(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void youmu_particle_slice_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
double lifetime = p->timeout;
double tt = t/lifetime;
double f = 0;
@ -286,18 +108,15 @@ static int youmu_particle_slice_logic(Projectile *p, int t) {
if(t%5 == 0) {
cmplx phase = cdir(p->angle);
RNG_ARRAY(R, 4);
PARTICLE(
.sprite = "petal",
.pos = p->pos-400*phase,
.rule = youmu_slice_petal,
.draw_rule = Petal,
.draw_rule = pdraw_petal_random(),
.args = {
phase,
phase*cdir(0.1),
vrng_real(R[0]) + vrng_real(R[1])*I,
vrng_real(R[2]) + vrng_range(R[3], 0, 360)*I,
},
.layer = LAYER_PARTICLE_HIGH | 0x2,
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
@ -380,8 +199,7 @@ static void youmu_haunting_power_shot(Player *plr, int p) {
.proto = pp_hghost,
.pos = plr->pos,
.rule = youmu_asymptotic,
.color = RGB(0.7 + 0.3 * (1-np), 0.8 + 0.2 * sqrt(1-np), 1.0),
.draw_rule = youmu_homing_draw_proj,
.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,
@ -390,6 +208,237 @@ static void youmu_haunting_power_shot(Player *plr, int p) {
}
}
TASK(youmu_homing_shot, { BoxedPlayer plr; }) {
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
.proto = pp_hghost,
.pos = ENT_UNBOX(ARGS.plr)->pos,
.color = RGB(0.75, 0.9, 1),
.type = PROJ_PLAYER,
.damage = 120,
.shader = "sprite_default",
));
real speed = 10;
real aim_strength = 0;
real aim_strength_accel = 0.02;
p->move = move_linear(-I * speed);
cmplx target = VIEWPORT_W * 0.5;
for(int i = 0; i < 60; ++i) {
target = youmu_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);
YIELD;
}
}
TASK(youmu_orb_homing_spirit_expire, { BoxedProjectile p; }) {
Projectile *p = ENT_UNBOX(ARGS.p);
PARTICLE(
.sprite_ptr = p->sprite,
.shader_ptr = p->shader,
.color = &p->color,
.timeout = 30,
.draw_rule = pdraw_timeout_scalefade(1+I, 0.1+I, 1, 0),
.pos = p->pos,
.move = p->move,
.angle = p->angle,
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PLAYER_SHOT,
);
}
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; }) {
int timeout = youmu_orb_homing_spirit_timeout(ENT_UNBOX(ARGS.orb));
if(timeout <= 0) {
return;
}
Projectile *p = TASK_BIND_UNBOXED(PROJECTILE(
.proto = pp_hghost,
.pos = ARGS.pos,
.color = color_mul_scalar(RGB(0.75, 0.9, 1), 0.5),
.type = PROJ_PLAYER,
.timeout = timeout,
.damage = ARGS.damage,
.shader = "sprite_particle",
));
INVOKE_TASK_AFTER(&p->events.killed, youmu_orb_homing_spirit_expire, ENT_BOX(p));
real speed = cabs(ARGS.velocity);
real speed_target = speed;
real aim_strength = -0.1;
p->move = move_accelerated(ARGS.velocity, -0.06 * ARGS.velocity);
p->move.retention = cdir(ARGS.spin);
cmplx target = ARGS.target;
bool aim_peaked = false;
bool orb_died = false;
Projectile *orb = NULL;
for(;;) {
if(!orb_died) {
orb = ENT_UNBOX(ARGS.orb);
if(orb == NULL) {
orb_died = true;
p->timeout = 0;
// p->move.velocity = 0;
p->move.retention *= 0.9;
aim_peaked = false;
aim_strength = -0.2;
speed *= 1.2;
}
}
if(orb) {
target = orb->pos;
} else {
target = youmu_homing_target(p->pos, creal(global.plr.pos) - 128*I);
}
cmplx aimdir = cnormalize(target - p->pos - p->move.velocity);
capproach_asymptotic_p(&p->move.retention, 0.8, 0.1, 1e-3);
p->move.acceleration *= 0.95;
p->move.acceleration += aim_strength * aimdir;
if(aim_peaked) {
if(!orb) {
approach_p(&aim_strength, 0.00, 0.001);
}
} else {
approach_p(&aim_strength, 0.1, 0.02);
if(aim_strength == 0.1) {
aim_peaked = true;
}
}
real s = max(speed, cabs(p->move.velocity));
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);
YIELD;
}
}
TASK(youmu_orb_update, { BoxedPlayer plr; BoxedProjectile orb; }) {
Player *plr = ENT_UNBOX(ARGS.plr);
Projectile *orb = TASK_BIND(ARGS.orb);
for(;;) {
float tf = glm_ease_bounce_out(1.0f - projectile_timeout_factor(orb));
orb->color.g = tf * tf;
orb->color.b = tf;
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);
break;
}
YIELD;
}
}
TASK(youmu_orb_death, { BoxedProjectile orb; BoxedTask control_task; }) {
cotask_cancel(cotask_unbox(ARGS.control_task));
Projectile *orb = ENT_UNBOX(ARGS.orb);
PARTICLE(
.proto = pp_blast,
.pos = orb->pos,
.timeout = 20,
.draw_rule = pdraw_blast(),
.flags = PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_LOW,
);
PARTICLE(
.proto = pp_blast,
.pos = orb->pos,
.timeout = 23,
.draw_rule = pdraw_blast(),
.flags = PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_LOW,
);
}
TASK(youmu_orb_shot, { BoxedPlayer plr; }) {
Player *plr = ENT_UNBOX(ARGS.plr);
int pwr = plr->power / 100;
Projectile *orb = TASK_BIND_UNBOXED(PROJECTILE(
.proto = pp_youhoming,
.pos = plr->pos,
.color = RGB(1, 1, 1),
.type = PROJ_PLAYER,
.damage = 1000,
.timeout = 100 + 10 * pwr,
.move = move_asymptotic(-30.0*I, -0.7*I, 0.8),
.flags = PFLAG_MANUALANGLE,
));
INVOKE_TASK(youmu_orb_update, ARGS.plr, 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));
cmplx v = 5 * I;
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));
}
}
static void youmu_haunting_shot(Player *plr) {
youmu_common_shot(plr);
@ -398,34 +447,11 @@ static void youmu_haunting_shot(Player *plr) {
int pwr = plr->power / 100;
if(!(global.frames % (45 - 4 * pwr))) {
int pcnt = 11 + pwr * 4;
int pdmg = 120 - 18 * 4 * (1 - pow(1 - pwr / 4.0, 1.5));
cmplx aim = 0.15*I;
PROJECTILE(
.proto = pp_youhoming,
.pos = plr->pos,
.color = RGB(1, 1, 1),
.rule = youmu_trap,
.args = { -30.0*I, 120, pcnt+pdmg*I, aim },
.type = PROJ_PLAYER,
.damage = 1000,
.shader = "sprite_youmu_charged_shot",
.shader_params = &(ShaderCustomParams){{ 0, 1 }},
);
INVOKE_TASK(youmu_orb_shot, ENT_BOX(plr));
}
} else {
if(!(global.frames % 6)) {
PROJECTILE(
.proto = pp_hghost,
.pos = plr->pos,
.color = RGB(0.75, 0.9, 1),
.rule = youmu_homing,
.args = { -10.0*I, 0.02*I, 60, VIEWPORT_W*0.5 },
.type = PROJ_PLAYER,
.damage = 120,
.shader = "sprite_default",
);
INVOKE_TASK(youmu_homing_shot, ENT_BOX(plr));
}
for(int p = 1; p <= 2*PLR_MAX_POWER/100; ++p) {
@ -448,10 +474,6 @@ static void youmu_haunting_preload(void) {
"part/youmu_slice",
NULL);
preload_resources(RES_SHADER_PROGRAM, flags,
"sprite_youmu_charged_shot",
NULL);
preload_resources(RES_TEXTURE, flags,
"youmu_bombbg1",
NULL);

View file

@ -13,12 +13,12 @@
#include "global.h"
#include "list.h"
#include "stageobjects.h"
#include "util/glm.h"
static ht_ptr2int_t shader_sublayer_map;
static ProjArgs defaults_proj = {
.sprite = "proj/",
.draw_rule = ProjDraw,
.dest = &global.projs,
.type = PROJ_ENEMY,
.damage_type = DMG_ENEMY_SHOT,
@ -30,13 +30,12 @@ static ProjArgs defaults_proj = {
static ProjArgs defaults_part = {
.sprite = "part/",
.draw_rule = ProjDraw,
.dest = &global.particles,
.type = PROJ_PARTICLE,
.damage_type = DMG_UNDEFINED,
.color = RGB(1, 1, 1),
.blend = BLEND_PREMUL_ALPHA,
.shader = "sprite_default",
.shader = "sprite_particle",
.layer = LAYER_PARTICLE_MID,
};
@ -60,8 +59,8 @@ static void process_projectile_args(ProjArgs *args, ProjArgs *defaults) {
}
}
if(!args->draw_rule) {
args->draw_rule = ProjDraw;
if(!args->draw_rule.func) {
args->draw_rule = pdraw_basic();
}
if(!args->blend) {
@ -100,6 +99,16 @@ static void process_projectile_args(ProjArgs *args, ProjArgs *defaults) {
}
}
if(args->scale == 0) {
args->scale = 1+I;
} else if(cimagf(args->scale) == 0) {
args->scale = CMPLXF(crealf(args->scale), crealf(args->scale));
}
if(args->opacity == 0) {
args->opacity = 1;
}
assert(args->type <= PROJ_PLAYER);
}
@ -196,12 +205,48 @@ cmplx projectile_graze_size(Projectile *p) {
return 0;
}
float32 projectile_timeout_factor(Projectile *p) {
return p->timeout ? (global.frames - p->birthtime) / p->timeout : 0;
}
static double projectile_rect_area(Projectile *p) {
double w, h;
projectile_size(p, &w, &h);
return w * h;
}
void projectile_set_layer(Projectile *p, drawlayer_t layer) {
if(!(layer & LAYER_LOW_MASK)) {
drawlayer_low_t sublayer;
switch(p->type) {
case PROJ_ENEMY:
// 1. Large projectiles go below smaller ones.
sublayer = LAYER_LOW_MASK - (drawlayer_low_t)projectile_rect_area(p);
sublayer = (sublayer << 4) & LAYER_LOW_MASK;
// 2. Group by shader (hardcoded precedence).
sublayer |= ht_get(&shader_sublayer_map, p->shader, 0) & 0xf;
// If specific blending order is required, then you should set up the sublayer manually.
layer |= sublayer;
break;
case PROJ_PARTICLE:
// 1. Group by shader (hardcoded precedence).
sublayer = ht_get(&shader_sublayer_map, p->shader, 0) & 0xf;
sublayer <<= 4;
sublayer |= 0x100;
// If specific blending order is required, then you should set up the sublayer manually.
layer |= sublayer;
break;
default:
break;
}
}
p->ent.draw_layer = layer;
}
static Projectile* _create_projectile(ProjArgs *args) {
if(IN_DRAW_CODE) {
log_fatal("Tried to spawn a projectile while in drawing code");
@ -228,14 +273,11 @@ static Projectile* _create_projectile(ProjArgs *args) {
p->damage_type = args->damage_type;
p->clear_flags = 0;
p->move = args->move;
if(args->shader_params != NULL) {
p->shader_params = *args->shader_params;
}
p->scale = args->scale;
p->opacity = args->opacity;
memcpy(p->args, args->args, sizeof(p->args));
p->ent.draw_layer = args->layer;
p->ent.draw_func = ent_draw_projectile;
projectile_set_prototype(p, args->proto);
@ -247,33 +289,7 @@ static Projectile* _create_projectile(ProjArgs *args) {
log_fatal("Tried to spawn a projectile with invalid size %f x %f", creal(p->size), cimag(p->size));
}
if(!(p->ent.draw_layer & LAYER_LOW_MASK)) {
drawlayer_low_t sublayer;
switch(p->type) {
case PROJ_ENEMY:
// 1. Large projectiles go below smaller ones.
sublayer = LAYER_LOW_MASK - (drawlayer_low_t)projectile_rect_area(p);
sublayer = (sublayer << 4) & LAYER_LOW_MASK;
// 2. Group by shader (hardcoded precedence).
sublayer |= ht_get(&shader_sublayer_map, p->shader, 0) & 0xf;
// If specific blending order is required, then you should set up the sublayer manually.
p->ent.draw_layer |= sublayer;
break;
case PROJ_PARTICLE:
// 1. Group by shader (hardcoded precedence).
sublayer = ht_get(&shader_sublayer_map, p->shader, 0) & 0xf;
sublayer <<= 4;
sublayer |= 0x100;
// If specific blending order is required, then you should set up the sublayer manually.
p->ent.draw_layer |= sublayer;
break;
default:
break;
}
}
projectile_set_layer(p, args->layer);
coevent_init(&p->events.killed);
ent_register(&p->ent, ENT_PROJECTILE);
@ -444,14 +460,14 @@ static void ent_draw_projectile(EntityInterface *ent) {
static Projectile prev_state;
memcpy(&prev_state, proj, sizeof(Projectile));
proj->draw_rule(proj, global.frames - proj->birthtime);
proj->draw_rule.func(proj, global.frames - proj->birthtime, proj->draw_rule.args);
if(memcmp(&prev_state, proj, sizeof(Projectile))) {
set_debug_info(&proj->debug);
log_fatal("Projectile modified its state in draw rule");
}
#else
proj->draw_rule(proj, global.frames - proj->birthtime);
proj->draw_rule.func(proj, global.frames - proj->birthtime, proj->draw_rule.args);
#endif
}
@ -481,10 +497,11 @@ Projectile* spawn_projectile_collision_effect(Projectile *proj) {
.flags = proj->flags | PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_HIGH,
.shader_ptr = proj->shader,
.rule = linear,
.draw_rule = DeathShrink,
.draw_rule = pdraw_timeout_scale(2+I, 0+I),
.angle = proj->angle,
.args = { 5*cexp(I*proj->angle) },
// .rule = linear,
// .args = { 5*cexp(I*proj->angle) },
.move = { .velocity = 5*cexp(I*proj->angle), .retention = 0.95 },
.timeout = 10,
);
}
@ -524,9 +541,10 @@ bool clear_projectile(Projectile *proj, uint flags) {
}
void kill_projectile(Projectile* proj) {
coevent_signal_once(&proj->events.killed);
proj->flags |= PFLAG_INTERNAL_DEAD | PFLAG_NOCOLLISION | PFLAG_NOCLEAR;
proj->draw_rule = ProjNoDraw;
proj->ent.draw_layer = LAYER_NODRAW;
// WARNING: must be done last, an event handler may cancel the task this function is running in!
coevent_signal_once(&proj->events.killed);
}
void process_projectiles(ProjectileList *projlist, bool collision) {
@ -617,6 +635,10 @@ bool projectile_is_clearable(Projectile *p) {
return false;
}
int projectile_time(Projectile *p) {
return global.frames - p->birthtime;
}
int linear(Projectile *p, int t) { // sure is physics in here; a[0]: velocity
if(t == EVENT_DEATH) {
return ACTION_ACK;
@ -702,24 +724,25 @@ static inline void apply_common_transforms(Projectile *proj, int t) {
*/
}
static void bullet_highlight_draw(Projectile *p, int t) {
static void bullet_highlight_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
float timefactor = t / p->timeout;
float sx = creal(p->args[0]);
float sy = cimag(p->args[0]);
float sx = args[0].as_float[0];
float sy = args[0].as_float[1];
float tex_angle = args[1].as_float[0];
float opacity = pow(1 - timefactor, 2);
opacity = min(1, 1.5 * opacity) * min(1, timefactor * 10);
opacity *= 1 - p->shader_params.vector[0];
opacity *= p->opacity;
r_mat_tex_push();
r_mat_tex_translate(0.5, 0.5, 0);
r_mat_tex_rotate(p->args[1], 0, 0, 1);
r_mat_tex_rotate(tex_angle, 0, 0, 1);
r_mat_tex_translate(-0.5, -0.5, 0);
r_draw_sprite(&(SpriteParams) {
.sprite_ptr = p->sprite,
.shader_ptr = p->shader,
.shader_params = &(ShaderCustomParams) {{ 1 - opacity }},
.shader_params = &(ShaderCustomParams) {{ opacity }},
.color = &p->color,
.scale = { .x = sx, .y = sy },
.rotation.angle = p->angle + M_PI * 0.5,
@ -757,8 +780,7 @@ static Projectile* spawn_projectile_highlight_effect_internal(Projectile *p, boo
.shader = "sprite_bullet",
.size = p->size * 4.5,
.layer = LAYER_PARTICLE_HIGH | 0x40,
.draw_rule = ScaleSquaredFade,
.args = { 0, 0, (0 + 2*I) * 0.1 * fmax(sx, sy) * (1 - 0.2 * vrng_real(R[0])) },
.draw_rule = pdraw_timeout_scalefade_exp(0, 0.2f * fmaxf(sx, sy) * vrng_f32_range(R[0], 0.8f, 1.0f), 1, 0, 2),
.angle = vrng_angle(R[1]),
.pos = p->pos + vrng_range(R[2], 0, 8) * vrng_dir(R[3]),
.flags = PFLAG_NOREFLECT,
@ -778,8 +800,11 @@ static Projectile* spawn_projectile_highlight_effect_internal(Projectile *p, boo
.size = p->size * 4.5,
.shader = "sprite_bullet",
.layer = LAYER_PARTICLE_HIGH | 0x80,
.draw_rule = bullet_highlight_draw,
.args = { 0.125 * (sx + I * sy), vrng_angle(R[0]) },
.draw_rule = {
bullet_highlight_draw,
.args[0].as_cmplx = 0.125 * (sx + I * sy),
.args[1].as_float = vrng_angle(R[0]),
},
.angle = p->angle,
.pos = p->pos + vrng_range(R[1], 0, 5) * vrng_dir(R[2]),
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
@ -800,25 +825,24 @@ static Projectile* spawn_bullet_spawning_effect(Projectile *p) {
return NULL;
}
static void projectile_clear_effect_draw(Projectile *p, int t) {
static void projectile_clear_effect_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
r_mat_mv_push();
apply_common_transforms(p, t);
float timefactor = t / p->timeout;
float plrfactor = clamp(1 - (cabs(p->pos - global.plr.pos) - 64) / 128, 0, 1);
plrfactor *= clamp(timefactor * 10, 0, 1);
float f = 1 - (1 - timefactor) * (1 - plrfactor);
float opacity = timefactor * plrfactor;
Sprite spr = *p->sprite;
Sprite *ispr = get_sprite("item/bullet_point");
spr.w = f * ispr->w + (1 - f) * spr.w;
spr.h = f * ispr->h + (1 - f) * spr.h;
spr.w = lerpf(spr.w, ispr->w, opacity);
spr.h = lerpf(spr.h, ispr->h, opacity);
r_draw_sprite(&(SpriteParams) {
.sprite_ptr = &spr,
.color = RGBA(p->color.r, p->color.g, p->color.b, p->color.a * (1 - 0)),
.shader_params = &(ShaderCustomParams){{ f }},
// .scale.both = 1 + 0.5 * sqrt(tf),
.color = &p->color,
.shader_params = &(ShaderCustomParams){{ opacity }},
});
r_mat_mv_pop();
@ -847,7 +871,7 @@ static int projectile_clear_effect_logic(Projectile *p, int t) {
return ACTION_NONE;
}
Projectile* spawn_projectile_clear_effect(Projectile *proj) {
Projectile *spawn_projectile_clear_effect(Projectile *proj) {
if((proj->flags & PFLAG_NOCLEAREFFECT) || proj->sprite == NULL) {
return NULL;
}
@ -873,80 +897,128 @@ Projectile* spawn_projectile_clear_effect(Projectile *proj) {
.rule = projectile_clear_effect_logic,
.draw_rule = projectile_clear_effect_draw,
.angle = proj->angle,
.opacity = proj->opacity,
.scale = proj->scale,
.timeout = 24,
.args = { -1 },
.layer = layer,
);
}
void ProjDrawCore(Projectile *proj, const Color *c) {
SpriteParams projectile_sprite_params(Projectile *proj, SpriteParamsBuffer *spbuf) {
spbuf->color = proj->color;
spbuf->shader_params = (ShaderCustomParams) {{ proj->opacity, 0, 0, 0 }};
SpriteParams sp = { 0 };
sp.blend = proj->blend;
sp.color = &spbuf->color;
sp.pos.x = creal(proj->pos);
sp.pos.y = cimag(proj->pos);
sp.rotation = (SpriteRotationParams) {
.angle = proj->angle + (float)(M_PI/2),
.vector = { 0, 0, 1 },
};
sp.scale.x = crealf(proj->scale);
sp.scale.y = cimagf(proj->scale);
sp.shader_params = &spbuf->shader_params;
sp.shader_ptr = proj->shader;
sp.sprite_ptr = proj->sprite;
return sp;
}
static void projectile_draw_sprite(Sprite *s, const Color *clr, float32 opacity, cmplx32 scale) {
if(opacity <= 0 || crealf(scale) == 0) {
return;
}
ShaderCustomParams p = {
opacity,
};
r_draw_sprite(&(SpriteParams) {
.sprite_ptr = proj->sprite,
.color = c,
.shader_params = &proj->shader_params,
.sprite_ptr = s,
.color = clr,
.shader_params = &p,
.scale = { crealf(scale), cimagf(scale) },
});
}
void ProjDraw(Projectile *proj, int t) {
r_mat_mv_push();
apply_common_transforms(proj, t);
void ProjDrawCore(Projectile *proj, const Color *c) {
projectile_draw_sprite(proj->sprite, c, proj->opacity, proj->scale);
}
void ProjDraw(Projectile *proj, int t, ProjDrawRuleArgs args) {
SpriteParamsBuffer spbuf;
SpriteParams sp = projectile_sprite_params(proj, &spbuf);
float eff = proj_spawn_effect_factor(proj, t);
/*
if(eff < 1 && proj->color.a > 0) {
Color c = proj->color;
c.a *= sqrt(eff);
ProjDrawCore(proj, &c);
} else {
ProjDrawCore(proj, &proj->color);
}
*/
if(eff < 1) {
float o = proj->shader_params.vector[0];
float a = min(1, eff * 2);
proj->shader_params.vector[0] = 1 - (1 - o) * a;
Color c = proj->color;
c.a *= eff;//sqrt(eff);
ProjDrawCore(proj, &c);
proj->shader_params.vector[0] = o;
} else {
ProjDrawCore(proj, &proj->color);
spbuf.color.a *= eff;
spbuf.shader_params.vector[0] *= fminf(1.0f, eff * 2.0f);
}
r_mat_mv_pop();
r_draw_sprite(&sp);
}
void ProjNoDraw(Projectile *proj, int t) {
ProjDrawRule pdraw_basic(void) {
return (ProjDrawRule) { ProjDraw };
}
void Blast(Projectile *p, int t) {
r_mat_mv_push();
r_mat_mv_translate(creal(p->pos), cimag(p->pos), 0);
r_mat_mv_rotate(creal(p->args[1]) * DEG2RAD, cimag(p->args[1]), creal(p->args[2]), cimag(p->args[2]));
if(t != p->timeout && p->timeout != 0) {
r_mat_mv_scale(t/(double)p->timeout, t/(double)p->timeout, 1);
}
float fade = 1.0 - t / (double)p->timeout;
r_color(RGBA_MUL_ALPHA(0.3, 0.6, 1.0, fade));
SpriteParams sp = {
.sprite_ptr = p->sprite,
.color = RGBA_MUL_ALPHA(0.3, 0.6, 1.0, fade),
static void pdraw_blast_func(Projectile *p, int t, ProjDrawRuleArgs args) {
vec3 rot_axis = {
args[0].as_float[0],
args[0].as_float[1],
args[1].as_float[0],
};
float32 rot_angle = args[1].as_float[1];
float32 secondary_scale = args[2].as_float[0];
float32 tf = projectile_timeout_factor(p);
float32 opacity = (1.0f - tf) * p->opacity;
if(tf <= 0 || opacity <= 0) {
return;
}
SpriteParamsBuffer spbuf;
SpriteParams sp = projectile_sprite_params(p, &spbuf);
sp.rotation.angle = rot_angle;
glm_vec3_copy(rot_axis, sp.rotation.vector);
sp.scale.x = tf;
sp.scale.y = tf;
spbuf.color = *RGBA(0.3, 0.6, 1.0, 1);
spbuf.shader_params.vector[0] = opacity;
r_disable(RCAP_CULL_FACE);
r_draw_sprite(&sp);
r_mat_mv_scale(0.5+creal(p->args[2]), 0.5+creal(p->args[2]), 1);
r_color4(0.3 * fade, 0.6 * fade, 1.0 * fade, 0);
sp.color = RGBA(0.3 * fade, 0.6 * fade, 1.0 * fade, 0);
sp.scale.as_cmplx *= secondary_scale;
spbuf.color.a = 0;
r_draw_sprite(&sp);
r_mat_mv_pop();
}
void Shrink(Projectile *p, int t) {
ProjDrawRule pdraw_blast(void) {
float32 rot_angle = rng_f32_angle();
float32 x = rng_f32();
float32 y = rng_f32();
float32 z = rng_f32();
float32 secondary_scale = rng_f32_range(0.5, 1.5);
vec3 rot_axis = { x, y, z };
glm_vec3_normalize(rot_axis);
return (ProjDrawRule) {
.func = pdraw_blast_func,
.args[0].as_float = { rot_axis[0], rot_axis[1] },
.args[1].as_float = { rot_axis[2], rot_angle },
.args[2].as_float = { secondary_scale, },
};
}
void Shrink(Projectile *p, int t, ProjDrawRuleArgs args) {
r_mat_mv_push();
apply_common_transforms(p, t);
@ -959,7 +1031,7 @@ void Shrink(Projectile *p, int t) {
r_mat_mv_pop();
}
void DeathShrink(Projectile *p, int t) {
void DeathShrink(Projectile *p, int t, ProjDrawRuleArgs args) {
r_mat_mv_push();
apply_common_transforms(p, t);
@ -972,7 +1044,7 @@ void DeathShrink(Projectile *p, int t) {
r_mat_mv_pop();
}
void GrowFade(Projectile *p, int t) {
void GrowFade(Projectile *p, int t, ProjDrawRuleArgs args) {
r_mat_mv_push();
apply_common_transforms(p, t);
@ -988,7 +1060,7 @@ void GrowFade(Projectile *p, int t) {
r_mat_mv_pop();
}
void Fade(Projectile *p, int t) {
void Fade(Projectile *p, int t, ProjDrawRuleArgs args) {
r_mat_mv_push();
apply_common_transforms(p, t);
ProjDrawCore(p, color_mul_scalar(COLOR_COPY(&p->color), 1 - t/(double)p->timeout));
@ -1010,33 +1082,111 @@ static void ScaleFadeImpl(Projectile *p, int t, int fade_exponent) {
r_mat_mv_pop();
}
void ScaleFade(Projectile *p, int t) {
void ScaleFade(Projectile *p, int t, ProjDrawRuleArgs args) {
ScaleFadeImpl(p, t, 1);
}
void ScaleSquaredFade(Projectile *p, int t) {
void ScaleSquaredFade(Projectile *p, int t, ProjDrawRuleArgs args) {
ScaleFadeImpl(p, t, 2);
}
void Petal(Projectile *p, int t) {
float x = creal(p->args[2]);
float y = cimag(p->args[2]);
float z = creal(p->args[3]);
static void pdraw_scalefade_func(Projectile *p, int t, ProjDrawRuleArgs args) {
cmplx32 scale0 = args[0].as_cmplx;
cmplx32 scale1 = args[1].as_cmplx;
float32 opacity0 = args[2].as_float[0];
float32 opacity1 = args[2].as_float[1];
float32 opacity_exp = args[3].as_float[0];
float r = sqrt(x*x+y*y+z*z);
x /= r; y /= r; z /= r;
float32 timefactor = t / p->timeout;
cmplx32 scale = clerpf(scale0, scale1, timefactor);
float32 opacity = lerpf(opacity0, opacity1, timefactor);
opacity = powf(opacity, opacity_exp);
SpriteParamsBuffer spbuf;
SpriteParams sp = projectile_sprite_params(p, &spbuf);
spbuf.shader_params.vector[0] *= opacity;
sp.scale.as_cmplx = cwmulf(sp.scale.as_cmplx, scale);
r_draw_sprite(&sp);
}
ProjDrawRule pdraw_timeout_scalefade_exp(cmplx32 scale0, cmplx32 scale1, float32 opacity0, float32 opacity1, float32 opacity_exp) {
if(cimagf(scale0) == 0) {
scale0 = CMPLXF(crealf(scale0), crealf(scale0));
}
if(cimagf(scale1) == 0) {
scale1 = CMPLXF(crealf(scale1), crealf(scale1));
}
return (ProjDrawRule) {
.func = pdraw_scalefade_func,
.args[0].as_cmplx = scale0,
.args[1].as_cmplx = scale1,
.args[2].as_float = { opacity0, opacity1 },
.args[3].as_float = { opacity_exp },
};
}
ProjDrawRule pdraw_timeout_scalefade(cmplx32 scale0, cmplx32 scale1, float32 opacity0, float32 opacity1) {
return pdraw_timeout_scalefade_exp(scale0, scale1, opacity0, opacity1, 1.0f);
}
ProjDrawRule pdraw_timeout_scale(cmplx32 scale0, cmplx32 scale1) {
return pdraw_timeout_scalefade(scale0, scale1, 1, 1);
}
ProjDrawRule pdraw_timeout_fade(float32 opacity0, float32 opacity1) {
return pdraw_timeout_scalefade(1+I, 1+I, opacity0, opacity1);
}
static void pdraw_petal_func(Projectile *p, int t, ProjDrawRuleArgs args) {
vec3 rot_axis = {
args[0].as_float[0],
args[0].as_float[1],
args[1].as_float[0],
};
float32 rot_angle = args[1].as_float[1];
SpriteParamsBuffer spbuf;
SpriteParams sp = projectile_sprite_params(p, &spbuf);
glm_vec3_copy(rot_axis, sp.rotation.vector);
sp.rotation.angle = DEG2RAD*t*4.0f + rot_angle;
spbuf.shader_params.vector[0] *= (1.0f - projectile_timeout_factor(p));
r_disable(RCAP_CULL_FACE);
r_mat_mv_push();
r_mat_mv_translate(creal(p->pos), cimag(p->pos),0);
r_mat_mv_rotate(DEG2RAD * (t*4.0 + cimag(p->args[3])), x, y, z);
ProjDrawCore(p, &p->color);
r_mat_mv_pop();
r_draw_sprite(&sp);
}
ProjDrawRule pdraw_petal(float32 rot_angle, vec3 rot_axis) {
glm_vec3_normalize(rot_axis);
float32 x = rot_axis[0];
float32 y = rot_axis[1];
float32 z = rot_axis[2];
return (ProjDrawRule) {
.func = pdraw_petal_func,
.args[0].as_float = { x, y },
.args[1].as_float = { z, rot_angle },
};
}
ProjDrawRule pdraw_petal_random(void) {
float32 x = rng_f32();
float32 y = rng_f32();
float32 z = rng_f32();
float32 rot_angle = rng_f32_angle();
return pdraw_petal(rot_angle, (vec3) { x, y, z });
}
void petal_explosion(int n, cmplx pos) {
for(int i = 0; i < n; i++) {
RNG_ARRAY(R, 6);
cmplx v = rng_dir();
v *= rng_range(3, 8);
real t = rng_real();
PARTICLE(
@ -1044,15 +1194,10 @@ void petal_explosion(int n, cmplx pos) {
.pos = pos,
.color = RGBA(sin(5*t) * t, cos(5*t) * t, 0.5 * t, 0),
.rule = asymptotic,
.draw_rule = Petal,
.args = {
vrng_range(R[0], 3, 8) * vrng_dir(R[1]),
5,
vrng_real(R[2]) + vrng_real(R[3])*I,
vrng_real(R[4]) + vrng_range(R[5], 0, 360)*I,
},
.move = move_asymptotic_simple(v, 5),
.draw_rule = pdraw_petal_random(),
// TODO: maybe remove this noreflect, there shouldn't be a cull mode mess anymore
.flags = PFLAG_NOREFLECT | (n % 2 ? 0 : PFLAG_REQUIREDPARTICLE),
.flags = PFLAG_NOREFLECT | (n % 2 ? 0 : PFLAG_REQUIREDPARTICLE) | PFLAG_MANUALANGLE,
.layer = LAYER_PARTICLE_PETAL,
);
}

View file

@ -34,9 +34,19 @@ typedef LIST_ANCHOR(Projectile) ProjectileList;
typedef LIST_INTERFACE(Projectile) ProjectileListInterface;
typedef int (*ProjRule)(Projectile *p, int t);
typedef void (*ProjDrawRule)(Projectile *p, int t);
// typedef void (*ProjDrawRule)(Projectile *p, int t);
typedef bool (*ProjPredicate)(Projectile *p);
typedef union {
float32 as_float[2];
cmplx32 as_cmplx;
} ProjDrawRuleArgs[RULE_ARGC];
typedef struct ProjDrawRule {
void (*func)(Projectile *p, int t, ProjDrawRuleArgs args);
ProjDrawRuleArgs args;
} ProjDrawRule;
typedef enum {
PROJ_INVALID,
@ -90,7 +100,7 @@ struct Projectile {
CoEvent killed;
} events;
Color color;
ShaderCustomParams shader_params;
attr_deprecated("this won't work") ShaderCustomParams shader_params;
BlendMode blend;
int birthtime;
float damage;
@ -101,6 +111,9 @@ struct Projectile {
ProjFlags flags;
uint clear_flags;
cmplx32 scale;
float32 opacity;
// XXX: this is in frames of course, but needs to be float
// to avoid subtle truncation and integer division gotchas.
float timeout;
@ -121,7 +134,7 @@ typedef struct ProjArgs {
Sprite *sprite_ptr;
const char *shader;
ShaderProgram *shader_ptr;
const ShaderCustomParams *shader_params;
attr_deprecated("this won't work") const ShaderCustomParams *shader_params;
ProjectileList *dest;
ProjRule rule;
cmplx args[RULE_ARGC];
@ -139,6 +152,9 @@ typedef struct ProjArgs {
int max_viewport_dist;
drawlayer_t layer;
cmplx32 scale;
float32 opacity;
// XXX: this is in frames of course, but needs to be float
// to avoid subtle truncation and integer division gotchas.
float timeout;
@ -204,6 +220,7 @@ Projectile* spawn_projectile_clear_effect(Projectile *proj);
Projectile* spawn_projectile_highlight_effect(Projectile *proj);
void projectile_set_prototype(Projectile *p, ProjPrototype *proto);
void projectile_set_layer(Projectile *p, drawlayer_t layer);
bool clear_projectile(Projectile *proj, uint flags);
void kill_projectile(Projectile *proj);
@ -212,16 +229,26 @@ int linear(Projectile *p, int t);
int accelerated(Projectile *p, int t);
int asymptotic(Projectile *p, int t);
void ProjDrawCore(Projectile *proj, const Color *c);
void ProjDraw(Projectile *p, int t);
void ProjNoDraw(Projectile *proj, int t);
#define DEPRECATED_DRAW_RULE attr_deprecated("")
void Shrink(Projectile *p, int t);
void DeathShrink(Projectile *p, int t);
void Fade(Projectile *p, int t);
void GrowFade(Projectile *p, int t);
void ScaleFade(Projectile *p, int t);
void ScaleSquaredFade(Projectile *p, int t);
void ProjDrawCore(Projectile *proj, const Color *c);
void ProjDraw(Projectile *proj, int t, ProjDrawRuleArgs args) DEPRECATED_DRAW_RULE;
void Shrink(Projectile *p, int t, ProjDrawRuleArgs) DEPRECATED_DRAW_RULE;
void DeathShrink(Projectile *p, int t, ProjDrawRuleArgs) DEPRECATED_DRAW_RULE;
void Fade(Projectile *p, int t, ProjDrawRuleArgs) DEPRECATED_DRAW_RULE;
void GrowFade(Projectile *p, int t, ProjDrawRuleArgs) DEPRECATED_DRAW_RULE;
void ScaleFade(Projectile *p, int t, ProjDrawRuleArgs) DEPRECATED_DRAW_RULE;
void ScaleSquaredFade(Projectile *p, int t, ProjDrawRuleArgs) DEPRECATED_DRAW_RULE;
ProjDrawRule pdraw_basic(void);
ProjDrawRule pdraw_timeout_scalefade_exp(cmplx32 scale0, cmplx32 scale1, float32 opacity0, float32 opacity1, float32 opacity_exp);
ProjDrawRule pdraw_timeout_scalefade(cmplx32 scale0, cmplx32 scale1, float32 opacity0, float32 opacity1);
ProjDrawRule pdraw_timeout_scale(cmplx32 scale0, cmplx32 scale1);
ProjDrawRule pdraw_timeout_fade(float32 opacity0, float32 opacity1);
ProjDrawRule pdraw_petal(float32 rot_angle, vec3 rot_axis);
ProjDrawRule pdraw_petal_random(void);
ProjDrawRule pdraw_blast(void);
void Petal(Projectile *p, int t);
void petal_explosion(int n, cmplx pos);
@ -232,5 +259,9 @@ void projectiles_preload(void);
void projectiles_free(void);
cmplx projectile_graze_size(Projectile *p);
float32 projectile_timeout_factor(Projectile *p);
int projectile_time(Projectile *p);
SpriteParams projectile_sprite_params(Projectile *proj, SpriteParamsBuffer *spbuf);
#endif // IGUARD_projectile_h

View file

@ -14,7 +14,8 @@
#include "util/crap.h"
#include "util/miscmath.h"
#define RNG_DEPRECATED attr_deprecated("Use the new rng_ API")
// #define RNG_DEPRECATED attr_deprecated("Use the new rng_ API")
#define RNG_DEPRECATED
typedef struct RandomState {
uint64_t state[4];

View file

@ -361,13 +361,17 @@ typedef struct SpriteStateParams {
ShaderProgram *shader;
} SpriteStateParams;
typedef struct SpriteScaleParams {
union {
float x;
float both;
typedef union SpriteScaleParams {
struct {
union {
float x;
float both;
};
float y;
};
float y;
cmplx32 as_cmplx;
} SpriteScaleParams;
typedef struct SpriteRotationParams {
@ -388,6 +392,8 @@ typedef struct SpriteParams {
ShaderProgram *shader_ptr;
Texture *aux_textures[R_NUM_SPRITE_AUX_TEXTURES];
// TODO: maybe embed these by value and get rid of SpriteParamsBuffer?
const Color *color;
const ShaderCustomParams *shader_params;
@ -399,6 +405,11 @@ typedef struct SpriteParams {
SpriteFlipParams flip;
} SpriteParams;
typedef struct SpriteParamsBuffer {
Color color;
ShaderCustomParams shader_params;
} SpriteParamsBuffer;
// Matches vertex buffer layout
typedef struct SpriteInstanceAttribs {
mat4 mv_transform;

View file

@ -1304,7 +1304,7 @@ TASK_WITH_INTERFACE(icy_storm, BossAttack) {
// play_sound("shot_special1");
ENT_ARRAY_FOREACH(&snowflake_projs, Projectile *p, {
spawn_projectile_highlight_effect(p)->shader_params.vector[0] = 0.75;
spawn_projectile_highlight_effect(p)->opacity = 0.25;
color_lerp(&p->color, RGB(0.5, 0.5, 0.5), 0.5);
p->move.velocity = 2 * cdir(p->angle);
p->move.acceleration = -cdir(p->angle) * difficulty_value(0.1, 0.15, 0.2, 0.2);

View file

@ -861,7 +861,8 @@ static int wriggle_rocket_laserbullet(Projectile *p, int time) {
return 1;
}
static void wriggle_slave_part_draw(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void wriggle_slave_part_draw(Projectile *p, int t, ProjDrawRuleArgs args) {
float b = 1 - t / (double)p->timeout;
r_mat_mv_push();
r_mat_mv_translate(creal(p->pos), cimag(p->pos), 0);
@ -1203,7 +1204,8 @@ void wriggle_light_singularity(Boss *boss, int time) {
}
static void wriggle_fstorm_proj_draw(Projectile *p, int time) {
DEPRECATED_DRAW_RULE
static void wriggle_fstorm_proj_draw(Projectile *p, int time, ProjDrawRuleArgs args) {
float f = 1-min(time/60.0,1);
r_mat_mv_push();
r_mat_mv_translate(creal(p->pos), cimag(p->pos), 0);
@ -1249,7 +1251,7 @@ static int wriggle_fstorm_proj(Projectile *p, int time) {
p->args[1] *= 2/cabs(p->args[1]);
p->angle = carg(p->args[1]);
p->birthtime = global.frames;
p->draw_rule = wriggle_fstorm_proj_draw;
p->draw_rule = (ProjDrawRule) { wriggle_fstorm_proj_draw };
p->sprite = NULL;
projectile_set_prototype(p, pp_rice);
spawn_projectile_highlight_effect(p);

View file

@ -1223,7 +1223,8 @@ static int kurumi_extra_bigfairy1(Enemy *e, int time) {
return 1;
}
static void kurumi_extra_drainer_draw(Projectile *p, int time) {
DEPRECATED_DRAW_RULE
static void kurumi_extra_drainer_draw(Projectile *p, int time, ProjDrawRuleArgs args) {
cmplx org = p->pos;
cmplx targ = p->args[1];
double a = 0.5 * creal(p->args[2]);

View file

@ -197,7 +197,8 @@ static int scythe_mid(Enemy *e, int t) {
return 1;
}
static void ScytheTrail(Projectile *p, int t) {
DEPRECATED_DRAW_RULE
static void ScytheTrail(Projectile *p, int t, ProjDrawRuleArgs args) {
r_mat_mv_push();
r_mat_mv_translate(creal(p->pos), cimag(p->pos), 0);
r_mat_mv_rotate(p->angle + (M_PI * 0.5), 0, 0, 1);
@ -1085,14 +1086,6 @@ static int broglie_particle(Projectile *p, int t) {
return ACTION_ACK;
}
/*
if(t == EVENT_BIRTH) {
// hidden and no collision detection until scattertime
p->type = FakeProj;
p->draw = ProjNoDraw;
}
*/
if(t < 0) {
return ACTION_ACK;
}
@ -1112,7 +1105,7 @@ static int broglie_particle(Projectile *p, int t) {
}
} else {
if(t == scattertime && p->type != PROJ_DEAD) {
p->draw_rule = ProjDraw;
projectile_set_layer(p, LAYER_BULLET);
p->flags &= ~(PFLAG_NOCLEARBONUS | PFLAG_NOCLEAREFFECT | PFLAG_NOCOLLISION);
double angle_ampl = creal(p->args[3]);
@ -1215,7 +1208,7 @@ static int broglie_charge(Projectile *p, int t) {
fast ? 2.0 : 1.5,
(1 + 2 * ((global.diff - 1) / (double)(D_Lunatic - 1))) * M_PI/11 + s_freq*10*I
},
.draw_rule = ProjNoDraw,
.layer = LAYER_NODRAW,
.flags = PFLAG_NOCLEARBONUS | PFLAG_NOCLEAREFFECT | PFLAG_NOSPAWNEFFECTS | PFLAG_NOCOLLISION,
);
}

View file

@ -184,35 +184,8 @@ typedef cmplx64 cmplx;
typedef struct { alignas(TAISEI_BUILDCONF_MALLOC_ALIGNMENT) char a; } max_align_t;
#endif
// In case the C11 CMPLX macro is not present, try our best to provide a substitute
#if !defined CMPLX
#undef HAS_BUILTIN_COMPLEX
#if defined __has_builtin
#if __has_builtin(__builtin_complex)
#define HAS_BUILTIN_COMPLEX
#endif
#else
#if defined __GNUC__ && defined __GNUC_MINOR__
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
#define HAS_BUILTIN_COMPLEX
#endif
#endif
#endif
#if defined HAS_BUILTIN_COMPLEX
#define CMPLX(re,im) __builtin_complex((double)(re), (double)(im))
#elif defined __clang__
#define CMPLX(re,im) (__extension__ (_Complex double){(double)(re), (double)(im)})
#elif defined _Imaginary_I
#define CMPLX(re,im) (_Complex double)((double)(re) + _Imaginary_I * (double)(im))
#else
#define CMPLX(re,im) (_Complex double)((double)(re) + _Complex_I * (double)(im))
#endif
#elif defined __EMSCRIPTEN__ && defined __clang__
// CMPLX from emscripten headers uses the clang-specific syntax without __extension__
#pragma clang diagnostic ignored "-Wcomplex-component-init"
#endif
// polyfill CMPLX macros
#include "compat_cmplx.h"
/*
* Abstract away the nasty GNU attribute syntax.

62
src/util/compat_cmplx.h Normal file
View file

@ -0,0 +1,62 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#ifndef IGUARD_util_compat_cmplx_h
#define IGUARD_util_compat_cmplx_h
#include "taisei.h"
#undef HAS_BUILTIN_COMPLEX
#if defined __has_builtin
#if __has_builtin(__builtin_complex)
#define HAS_BUILTIN_COMPLEX
#endif
#else
#if defined __GNUC__ && defined __GNUC_MINOR__
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
#define HAS_BUILTIN_COMPLEX
#endif
#endif
#endif
// In case the C11 CMPLX macro is not present, try our best to provide a substitute
#if !defined CMPLX
#if defined HAS_BUILTIN_COMPLEX
#define CMPLX(re,im) __builtin_complex((double)(re), (double)(im))
#elif defined __clang__
#define CMPLX(re,im) (__extension__ (_Complex double){ (double)(re), (double)(im) })
#elif defined _Imaginary_I
#define CMPLX(re,im) (_Complex double)((double)(re) + _Imaginary_I * (double)(im))
#else
#define CMPLX(re,im) (_Complex double)((double)(re) + _Complex_I * (double)(im))
#endif
#elif defined __EMSCRIPTEN__ && defined __clang__
// CMPLX from emscripten headers uses the clang-specific syntax without __extension__
#pragma clang diagnostic ignored "-Wcomplex-component-init"
#endif
// same for CMPLXF
#if !defined CMPLXF
#if defined HAS_BUILTIN_COMPLEX
#define CMPLXF(re,im) __builtin_complex((float)(re), (float)(im))
#elif defined __clang__
#define CMPLXF(re,im) (__extension__ (_Complex float){ (float)(re), (float)(im) })
#elif defined _Imaginary_I
#define CMPLXF(re,im) (_Complex float)((float)(re) + _Imaginary_I * (float)(im))
#else
#define CMPLXF(re,im) (_Complex float)((float)(re) + _Complex_I * (float)(im))
#endif
#elif defined __EMSCRIPTEN__ && defined __clang__
// CMPLXF from emscripten headers uses the clang-specific syntax without __extension__
#pragma clang diagnostic ignored "-Wcomplex-component-init"
#endif
#endif // IGUARD_util_compat_cmplx_h

View file

@ -29,4 +29,10 @@
PRAGMA(GCC diagnostic pop)
#endif
typedef float (*glm_ease_t)(float);
INLINE float glm_ease_apply(glm_ease_t ease, float f) {
return ease ? ease(f) : f;
}
#endif // IGUARD_util_glm_h

View file

@ -15,10 +15,18 @@ double lerp(double v0, double v1, double f) {
return f * (v1 - v0) + v0;
}
float lerpf(float v0, float v1, float f) {
return f * (v1 - v0) + v0;
}
cmplx clerp(cmplx v0, cmplx v1, double f) {
return f * (v1 - v0) + v0;
}
cmplx32 clerpf(cmplx32 v0, cmplx32 v1, float32 f) {
return f * (v1 - v0) + v0;
}
double approach(double v, double t, double d) {
if(v < t) {
v += d;
@ -117,6 +125,14 @@ cmplx cdir(double angle) {
#endif
}
cmplx cwmul(cmplx c0, cmplx c1) {
return CMPLX(creal(c0)*creal(c1), cimag(c0)*cimag(c1));
}
cmplx32 cwmulf(cmplx32 c0, cmplx32 c1) {
return CMPLXF(crealf(c0)*crealf(c1), cimagf(c0)*cimagf(c1));
}
double psin(double x) {
return 0.5 + 0.5 * sin(x);
}

View file

@ -17,7 +17,9 @@
#define M_TAU (M_PI * 2)
double lerp(double v0, double v1, double f) attr_const;
float lerpf(float v0, float v1, float f) attr_const;
cmplx clerp(cmplx v0, cmplx v1, double f) attr_const;
cmplx32 clerpf(cmplx32 v0, cmplx32 v1, float32 f) attr_const;
intmax_t imin(intmax_t, intmax_t) attr_const;
intmax_t imax(intmax_t, intmax_t) attr_const;
uintmax_t umin(uintmax_t, uintmax_t) attr_const;
@ -41,6 +43,8 @@ void capproach_asymptotic_p(cmplx *val, cmplx target, double rate, double epsilo
cmplx cnormalize(cmplx c) attr_const;
cmplx cclampabs(cmplx c, double maxabs) attr_const;
cmplx cdir(double angle) attr_const;
cmplx cwmul(cmplx c0, cmplx c1) attr_const;
cmplx32 cwmulf(cmplx32 c0, cmplx32 c1) attr_const;
double psin(double) attr_const;
int sign(double) attr_const;
double swing(double x, double s) attr_const;