Projectile prototypes system (#118)

* wip projectile prototypes

* Partial fix for replay desyncs

* some YoumuA fixes

* fix various ToE problems

* fix MarisaB

* fix master spark

* fix iku slave particle position

* this timeout was somehow halved during the changes

* remove some v1.2 compat hacks
This commit is contained in:
Andrei Alexeyev 2018-05-02 07:46:48 +03:00 committed by GitHub
parent 0761377f41
commit f67396423e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 736 additions and 441 deletions

View file

@ -166,7 +166,7 @@ static bool attack_is_over(Attack *a) {
}
static void BossGlow(Projectile *p, int t) {
float s = 1.0+t/p->args[0]*0.5;
float s = 1.0+t/(double)p->timeout*0.5;
float fade = 1 - (1.5 - s);
float deform = 5 - 10 * fade * fade;
@ -185,7 +185,7 @@ static int boss_glow(Projectile *p, int t) {
if(t == EVENT_DEATH) {
free(p->sprite);
}
return timeout_linear(p,t);
return linear(p, t);
}
static Projectile* spawn_boss_glow(Boss *boss, Color clr, int timeout) {
@ -197,7 +197,7 @@ static Projectile* spawn_boss_glow(Boss *boss, Color clr, int timeout) {
.color = clr,
.rule = boss_glow,
.draw_rule = BossGlow,
.args = { timeout },
.timeout = timeout,
.angle = M_PI * 2 * frand(),
.layer = LAYER_PARTICLE_LOW,
);
@ -212,11 +212,14 @@ static void spawn_particle_effects(Boss *boss) {
bool is_extra = cur && cur->type == AT_ExtraSpell && global.frames >= cur->starttime;
if(!(global.frames % 13) && !is_extra) {
complex v = cexp(I*global.frames);
PARTICLE("smoke", v, shadowcolor, enemy_flare,
.draw_rule = EnemyFlareShrink,
.args = { 180, 0, add_ref(boss), },
PARTICLE(
.sprite = "smoke",
.pos = cexp(I*global.frames),
.color = shadowcolor,
.rule = enemy_flare,
.timeout = 180,
.draw_rule = Shrink,
.args = { 0, add_ref(boss), },
.angle = M_PI * 2 * frand(),
.flags = PFLAG_DRAWADD,
);
@ -412,10 +415,11 @@ void boss_rule_extra(Boss *boss, float alpha) {
0.5 + 0.2 * psina * (1-v),
0.5 + 0.5 * psina * v
),
.rule = timeout_linear,
.rule = linear,
.timeout = 30*lt,
.draw_rule = GrowFade,
.flags = PFLAG_DRAWADD,
.args = { 30*lt, vel * (1 - 2 * !(global.frames % 10)), 2.5 },
.args = { vel * (1 - 2 * !(global.frames % 10)), 2.5 },
);
}
}
@ -698,16 +702,18 @@ void process_boss(Boss **pboss) {
for(int i = 0; i < 256; i++) {
tsrand_fill(3);
PARTICLE("flare", boss->pos, 0, timeout_linear, .draw_rule = Fade,
.args = {
60 + 10 * afrand(2),
(3+afrand(0)*10)*cexp(I*tsrand_a(1))
},
PARTICLE(
.sprite = "flare",
.pos = boss->pos,
.timeout = 60 + 10 * afrand(2),
.rule = linear,
.draw_rule = Fade,
.args = { (3+afrand(0)*10)*cexp(I*tsrand_a(1)) },
);
}
PARTICLE("blast", boss->pos, 0, timeout, { 60, 3 }, .draw_rule = GrowFade);
PARTICLE("blast", boss->pos, 0, timeout, { 70, 2.5 }, .draw_rule = GrowFade);
PARTICLE("blast", boss->pos, 0, .timeout = 60, .args = { 0, 3.0 }, .draw_rule = GrowFade);
PARTICLE("blast", boss->pos, 0, .timeout = 70, .args = { 0, 2.5 }, .draw_rule = GrowFade);
}
play_sound_ex("bossdeath", BOSS_DEATH_DELAY * 2, false);
@ -818,9 +824,10 @@ void boss_start_attack(Boss *b, Attack *a) {
.sprite = "stain",
.pos = VIEWPORT_W/2 + VIEWPORT_W/4*anfrand(0)+I*VIEWPORT_H/2+I*anfrand(1)*30,
.color = rgb(0.2,0.3,0.4),
.rule = timeout_linear,
.rule = linear,
.timeout = 50,
.draw_rule = GrowFade,
.args = { 50, sign(anfrand(2))*10*(1+afrand(3)) },
.args = { sign(anfrand(2))*10*(1+afrand(3)) },
.flags = PFLAG_DRAWADD,
);
}

View file

@ -76,14 +76,19 @@ void* _delete_enemy(List **enemies, List* enemy, void *arg) {
for(int i = 0; i < 10; i++) {
tsrand_fill(2);
PARTICLE("flare", e->pos, 0, timeout_linear, .draw_rule = Fade,
.args = { 10, (3+afrand(0)*10)*cexp(I*afrand(1)*2*M_PI) },
PARTICLE(
.sprite = "flare",
.pos = e->pos,
.timeout = 10,
.rule = linear,
.draw_rule = Fade,
.args = { (3+afrand(0)*10)*cexp(I*afrand(1)*2*M_PI) },
);
}
PARTICLE("blast", e->pos, 0, blast_timeout, { 20 }, .draw_rule = Blast, .flags = PFLAG_REQUIREDPARTICLE);
PARTICLE("blast", e->pos, 0, blast_timeout, { 20 }, .draw_rule = Blast, .flags = PFLAG_REQUIREDPARTICLE);
PARTICLE("blast", e->pos, 0, blast_timeout, { 15 }, .draw_rule = GrowFade, .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 = 20, .draw_rule = Blast, .flags = PFLAG_REQUIREDPARTICLE);
PARTICLE(.proto = pp_blast, .pos = e->pos, .timeout = 15, .draw_rule = GrowFade, .flags = PFLAG_REQUIREDPARTICLE);
}
e->logic_rule(e, EVENT_DEATH);
@ -135,43 +140,30 @@ void killall(Enemy *enemies) {
e->hp = 0;
}
int enemy_flare(Projectile *p, int t) { // a[0] timeout, a[1] velocity, a[2] ref to enemy
if(t >= creal(p->args[0]) || REF(p->args[2]) == NULL) {
int enemy_flare(Projectile *p, int t) { // a[0] velocity, a[1] ref to enemy
if(t == EVENT_DEATH) {
free_ref(p->args[1]);
return ACTION_NONE;
}
Enemy *owner = REF(p->args[1]);
/*
if(REF(p->args[1]) == NULL) {
return ACTION_DESTROY;
} if(t == EVENT_DEATH) {
free_ref(p->args[2]);
return 1;
} else if(t < 0) {
return 1;
}
*/
if(t < 0) {
return ACTION_NONE;
}
p->pos += p->args[1];
return 1;
}
void EnemyFlareShrink(Projectile *p, int t) {
Enemy *e = (Enemy *)REF(p->args[2]);
if(e == NULL) {
return;
if(owner != NULL) {
p->args[3] = owner->pos;
}
r_mat_push();
float s = 2.0-t/p->args[0]*2;
r_mat_translate(creal(e->pos + p->pos), cimag(e->pos + p->pos), 0);
if(p->angle != M_PI*0.5) {
r_mat_rotate_deg(p->angle*180/M_PI+90, 0, 0, 1);
}
if(s != 1) {
r_mat_scale(s, s, 1);
}
ProjDrawCore(p, p->color);
r_mat_pop();
p->pos = p->pos0 + p->args[3] + p->args[0]*t;
return ACTION_NONE;
}
void BigFairy(Enemy *e, int t, bool render) {
@ -179,9 +171,14 @@ void BigFairy(Enemy *e, int t, bool render) {
if(!(t % 5)) {
complex offset = (frand()-0.5)*30 + (frand()-0.5)*20.0*I;
PARTICLE("smoothdot", offset, rgb(0,0.2,0.3), enemy_flare,
.draw_rule = EnemyFlareShrink,
.args = { 50, (-50.0*I-offset)/50.0, add_ref(e) },
PARTICLE(
.sprite = "smoothdot",
.pos = offset,
.color = rgb(0,0.2,0.3),
.rule = enemy_flare,
.draw_rule = Shrink,
.timeout = 50,
.args = { (-50.0*I-offset)/50.0, add_ref(e) },
.flags = PFLAG_DRAWADD,
);
}

View file

@ -78,6 +78,5 @@ void Swirl(Enemy*, int t, bool render);
void BigFairy(Enemy*, int t, bool render);
int enemy_flare(Projectile *p, int t);
void EnemyFlareShrink(Projectile *p, int t);
void enemies_preload(void);

View file

@ -71,6 +71,7 @@ enum {
EVENT_DEATH = -8999,
EVENT_BIRTH,
ACTION_DESTROY,
ACTION_NONE,
FPS = 60,

View file

@ -46,11 +46,6 @@ static void ent_draw_item(EntityInterface *ent) {
});
}
static int item_prio(List *litem) {
Item *item = (Item*)litem;
return item->type;
}
Item* create_item(complex pos, complex v, ItemType type) {
if((creal(pos) < 0 || creal(pos) > VIEWPORT_W)) {
// we need this because we clamp the item position to the viewport boundary during motion
@ -61,10 +56,7 @@ Item* create_item(complex pos, complex v, ItemType type) {
// type = 1 + floor(Life * frand());
Item *i = (Item*)objpool_acquire(stage_object_pools.items);
// FIXME: use simpler/faster insertions when v1.2 compat is not needed
// list_push(&global.items, i);
list_insert_at_priority_tail(&global.items, i, type, item_prio);
list_append(&global.items, i);
i->pos = pos;
i->pos0 = pos;
@ -89,7 +81,11 @@ Item* create_bpoint(complex pos) {
Item *i = create_item(pos, 0, BPoint);
if(i) {
PARTICLE("flare", pos, 0, timeout, { 30 }, .draw_rule = Fade);
PARTICLE(
.sprite = "flare",
.pos = pos, .timeout = 30,
.draw_rule = Fade
);
i->auto_collect = 10;
}

View file

@ -265,7 +265,12 @@ void process_lasers(void) {
}
if(kill_now) {
PARTICLE("flare", p, 0, timeout, { 20 }, .draw_rule = GrowFade);
PARTICLE(
.sprite = "flare",
.pos = p,
.timeout = 20,
.draw_rule = GrowFade
);
laser->deathtime = 0;
}
}

View file

@ -89,6 +89,7 @@ taisei_src = files(
'plrmodes.c',
'progress.c',
'projectile.c',
'projectile_prototypes.c',
'random.c',
'refs.c',
'replay.c',

View file

@ -402,9 +402,10 @@ void player_death(Player *plr) {
PARTICLE(
.sprite = "flare",
.pos = plr->pos,
.rule = timeout_linear,
.rule = linear,
.timeout = 40,
.draw_rule = Shrink,
.args = { 40, (3+afrand(0)*7)*cexp(I*tsrand_a(1)) },
.args = { (3+afrand(0)*7)*cexp(I*tsrand_a(1)) },
.flags = PFLAG_NOREFLECT,
);
}
@ -415,10 +416,11 @@ void player_death(Player *plr) {
.sprite = "blast",
.pos = plr->pos,
.color = rgba(1.0, 0.3, 0.3, 0.5),
.rule = timeout,
.timeout = 35,
.draw_rule = GrowFade,
.args = { 35, 2.4 },
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
.args = { 0, 2.4 },
.blend = BLEND_ADD,
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
);
plr->deathtime = global.frames + DEATHBOMB_TIME;
@ -759,9 +761,10 @@ void player_graze(Player *plr, complex pos, int pts, int effect_intensity) {
PARTICLE(
.sprite = "flare",
.pos = pos,
.rule = timeout_linear,
.rule = linear,
.timeout = 5 + 5 * afrand(2),
.draw_rule = Shrink,
.args = { 5 + 5 * afrand(2), (1+afrand(0)*5)*cexp(I*M_PI*2*afrand(1)) },
.args = { 0, (1+afrand(0)*5)*cexp(I*M_PI*2*afrand(1)) },
.flags = PFLAG_NOREFLECT,
);
}

View file

@ -35,15 +35,17 @@ void marisa_common_shot(Player *plr, int dmg) {
if(!(global.frames % 6)) {
Color c = rgb(1, 1, 1);
PROJECTILE("marisa", plr->pos + 10 - 15.0*I, c, linear, { -20.0*I },
.type = PlrProj+dmg,
.shader = "sprite_default",
);
PROJECTILE("marisa", plr->pos - 10 - 15.0*I, c, linear, { -20.0*I },
.type = PlrProj+dmg,
.shader = "sprite_default",
);
for(int i = -1; i < 2; i += 2) {
PROJECTILE(
.proto = pp_marisa,
.pos = plr->pos + 10 * i - 15.0*I,
.color = c,
.rule = linear,
.args = { -20.0*I },
.type = PlrProj+dmg,
.shader = "sprite_default",
);
}
}
}

View file

@ -91,12 +91,10 @@ static void trace_laser(Enemy *e, complex vel, int damage) {
PARTICLE(
.sprite = "flare",
.pos = col.location,
.rule = timeout_linear,
.rule = linear,
.timeout = 3 + 5 * afrand(2),
.draw_rule = Shrink,
.args = {
3 + 5 * afrand(2),
(2+afrand(0)*6)*cexp(I*M_PI*2*afrand(1))
},
.args = { (2+afrand(0)*6)*cexp(I*M_PI*2*afrand(1)) },
.flags = PFLAG_NOREFLECT,
);
@ -403,8 +401,11 @@ static void masterspark_visual(Enemy *e, int t2, bool render) {
}
static int masterspark_star(Projectile *p, int t) {
p->args[1] += 0.1*p->args[1]/cabs(p->args[1]);
return timeout_linear(p,t);
if(t >= 0) {
p->args[0] += 0.1*p->args[0]/cabs(p->args[0]);
}
return linear(p, t);
}
static int masterspark(Enemy *e, int t2) {
@ -424,35 +425,38 @@ static int masterspark(Enemy *e, int t2) {
complex dir = -cexp(1.2*I*nfrand())*I;
Color c = rgb(0.7+0.3*sin(t*30),0.7+0.3*cos(t*30),0.7+0.3*cos(t*3));
PARTICLE(
.sprite="maristar_orbit",
.pos=global.plr.pos+40*dir,
.color=c,
.rule=masterspark_star,
.args={50, 10*dir-10*I,3},
.angle=nfrand(),
.flags=PFLAG_DRAWADD,
.draw_rule=GrowFade
.sprite = "maristar_orbit",
.pos = global.plr.pos+40*dir,
.color = c,
.rule = masterspark_star,
.timeout = 50,
.args= { 10 * dir - 10*I, 3 },
.angle = nfrand(),
.blend = BLEND_ADD,
.draw_rule = GrowFade
);
dir = -conj(dir);
PARTICLE(
.sprite="maristar_orbit",
.pos=global.plr.pos+40*dir,
.color=c,
.rule=masterspark_star,
.args={50, 10*dir-10*I,3},
.angle=nfrand(),
.flags=PFLAG_DRAWADD,
.draw_rule=GrowFade
.sprite = "maristar_orbit",
.pos = global.plr.pos+40*dir,
.color = c,
.rule = masterspark_star,
.timeout = 50,
.args = { 10 * dir - 10*I, 3 },
.angle = nfrand(),
.flags = PFLAG_DRAWADD,
.draw_rule = GrowFade
);
PARTICLE(
.sprite="smoke",
.pos=global.plr.pos-40*I,
.color=rgb(0.9,1,1),
.rule=timeout_linear,
.args={50, -5*dir,3},
.angle=nfrand(),
.flags=PFLAG_DRAWADD,
.draw_rule=GrowFade
.sprite = "smoke",
.pos = global.plr.pos-40*I,
.color = rgb(0.9,1,1),
.rule = linear,
.timeout = 50,
.args = { -5*dir, 3 },
.angle = nfrand(),
.flags = PFLAG_DRAWADD,
.draw_rule = GrowFade
);
}

View file

@ -14,7 +14,7 @@
#include "renderer/api.h"
static void marisa_star_trail_draw(Projectile *p, int t) {
float s = 1 - t / creal(p->args[0]);
float s = 1 - t / (double)p->timeout;
Color clr = derive_color(p->color, CLRMASK_A, rgba(0, 0, 0, s*0.5));
@ -40,12 +40,12 @@ static int marisa_star_projectile(Projectile *p, int t) {
.sprite_ptr = get_sprite("proj/maristar"),
.pos = p->pos,
.color = p->color,
.rule = timeout,
.args = { 8 },
.timeout = 8,
.draw_rule = marisa_star_trail_draw,
.angle = p->angle,
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT,
.flags = PFLAG_NOREFLECT,
.layer = LAYER_PARTICLE_LOW,
.blend = BLEND_ADD,
);
if(t == EVENT_DEATH) {
@ -53,12 +53,13 @@ static int marisa_star_projectile(Projectile *p, int t) {
.sprite_ptr = get_sprite("proj/maristar"),
.pos = p->pos,
.color = p->color,
.rule = timeout,
.timeout = 40,
.draw_rule = GrowFade,
.args = { 40, 2 },
.args = { 0, 2 },
.angle = frand() * 2 * M_PI,
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT,
.flags = PFLAG_NOREFLECT,
.layer = LAYER_PARTICLE_HIGH,
.blend = BLEND_ADD,
);
}
@ -80,7 +81,7 @@ static int marisa_star_slave(Enemy *e, int t) {
a *= cexp(I*i*M_PI/20*sign(v)*focus);
PROJECTILE(
.sprite_ptr = get_sprite("proj/maristar"),
.proto = pp_maristar,
.pos = e->pos,
.color = rgb(1.0, 0.5, 1.0),
.rule = marisa_star_projectile,
@ -99,8 +100,11 @@ static int marisa_star_slave(Enemy *e, int t) {
static int marisa_star_orbit_star(Projectile *p, int t) { // XXX: because growfade is the worst
p->args[1] += p->args[1]/cabs(p->args[1])*0.05;
return timeout_linear(p,t);
if(t >= 0) {
p->args[0] += p->args[0]/cabs(p->args[0])*0.05;
}
return linear(p,t);
}
static Color marisa_slaveclr(int i, float low) {
@ -149,10 +153,10 @@ static int marisa_star_orbit(Enemy *e, int t) {
.sprite_ptr = get_sprite("part/lightningball"),
.pos = e->pos,
.color = rgba(clr[0],clr[1],clr[2],clr[3]/2),
.rule = timeout,
.timeout = 10,
.draw_rule = Fade,
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT,
.args = { 10, 2 },
.flags = PFLAG_NOREFLECT,
.blend = BLEND_ADD,
);
}
@ -163,8 +167,9 @@ static int marisa_star_orbit(Enemy *e, int t) {
.color = rgba(clr[0],clr[1],clr[2],1),
.rule = marisa_star_orbit_star,
.draw_rule = GrowFade,
.timeout = 150,
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT,
.args = { 150, -5*dir/cabs(dir), 3 },
.args = { -5*dir/cabs(dir), 3 },
);
}

View file

@ -26,18 +26,6 @@ PlayerCharacter character_youmu = {
},
};
int youmu_common_particle_spin(Projectile *p, int t) {
int i = timeout_linear(p, t);
if(t < 0)
return 1;
p->args[3] += 0.06;
p->angle = p->args[3];
return i;
}
void youmu_common_shot(Player *plr) {
if(!(global.frames % 4)) {
play_sound("generic_shot");
@ -46,12 +34,22 @@ void youmu_common_shot(Player *plr) {
if(!(global.frames % 6)) {
Color c = rgb(1, 1, 1);
PROJECTILE("youmu", plr->pos + 10 - I*20, c, linear, { -20.0*I },
PROJECTILE(
.proto = pp_youmu,
.pos = plr->pos + 10 - I*20,
.color = c,
.rule = linear,
.args = { -20.0*I },
.type = PlrProj+120,
.shader = "sprite_default",
);
PROJECTILE("youmu", plr->pos - 10 - I*20, c, linear, { -20.0*I },
PROJECTILE(
.proto = pp_youmu,
.pos = plr->pos - 10 - I*20,
.color = c,
.rule = linear,
.args = { -20.0*I },
.type = PlrProj+120,
.shader = "sprite_default",
);

View file

@ -15,7 +15,6 @@
extern PlayerCharacter character_youmu;
int youmu_common_particle_spin(Projectile *p, int t);
void youmu_common_shot(Player *plr);
void youmu_common_draw_proj(Projectile *p, Color c, float scale);
void youmu_common_bombbg(Player *plr);

View file

@ -20,23 +20,19 @@ static Color myon_color(float f, float a) {
}
static int myon_particle_rule(Projectile *p, int t) {
if(t >= creal(p->args[0])) {
return ACTION_DESTROY;
}
if(t < 0) {
return 1;
return ACTION_NONE;
}
p->pos += p->args[1];
p->pos += p->args[0];
p->angle += 0.03 * (1 - 2 * (p->birthtime & 1));
p->color = derive_color(approach_color(p->color, myon_color(0.5, 0), 0.04), CLRMASK_A, p->color);
return 1;
return ACTION_NONE;
}
static complex myon_tail_dir(void) {
double angle = carg(global.plr.slaves->args[1]);
double angle = carg(global.plr.slaves->args[0]);
complex dir = cexp(I*(0.1 * sin(global.frames * 0.05) + angle));
float f = abs(global.plr.focus) / 30.0;
return f * f * dir;
@ -44,23 +40,23 @@ static complex myon_tail_dir(void) {
static int myon_flare_particle_rule(Projectile *p, int t) {
if(t < 0) {
return 1;
return ACTION_NONE;
}
// wiggle wiggle
p->pos += 0.05 * (global.plr.slaves->pos - p->pos) * cexp(I * sin((t - global.frames * 2) * 0.1) * M_PI/8);
p->args[1] = 3 * myon_tail_dir();
p->color = derive_color(p->color, CLRMASK_A, rgba(1, 1, 1, pow(1 - min(1, t / p->args[0]), 2)));
p->args[0] = 3 * myon_tail_dir();
p->color = derive_color(p->color, CLRMASK_A, rgba(1, 1, 1, pow(1 - min(1, t / (double)p->timeout), 2)));
return myon_particle_rule(p, t);
}
static void myon_draw_trail(Projectile *p, int t) {
float fadein = clamp(t/10.0, p->args[3], 1);
float s = min(1, 1 - t / p->args[0]);
float fadein = clamp(t/10.0, p->args[2], 1);
float s = min(1, 1 - t / (double)p->timeout);
float a = color_component(p->color, CLR_A) * fadein;
Color c = derive_color(p->color, CLRMASK_A, rgba(0, 0, 0, a * s * s));
youmu_common_draw_proj(p, c, fadein * (2-s) * p->args[2]);
youmu_common_draw_proj(p, c, fadein * (2-s) * p->args[1]);
}
static void spawn_stardust(complex pos, Color clr, int timeout, complex v) {
@ -70,9 +66,11 @@ static void spawn_stardust(complex pos, Color clr, int timeout, complex v) {
.color = clr,
.draw_rule = myon_draw_trail,
.rule = myon_particle_rule,
.args = { timeout, v, 0.2 + 0.1 * frand(), 1 },
.timeout = timeout,
.args = { v, 0.2 + 0.1 * frand(), 1 },
.angle = M_PI*2*frand(),
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT,
.blend = BLEND_ADD,
.flags = PFLAG_NOREFLECT,
.layer = LAYER_PARTICLE_LOW | 1,
);
}
@ -92,8 +90,10 @@ static void myon_spawn_trail(Enemy *e, int t) {
.color = myon_color(f, (1 + f) * 0.05),
.draw_rule = myon_draw_trail,
.rule = myon_particle_rule,
.args = { 60, -I*0.0*cexp(I*M_PI/16*sin(t)), -0.2, 0 },
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT,
.timeout = 60,
.args = { -I*0.0*cexp(I*M_PI/16*sin(t)), -0.2, 0 },
.blend = BLEND_ADD,
.flags = PFLAG_NOREFLECT,
.angle = M_PI*2*frand(),
);
@ -103,8 +103,10 @@ static void myon_spawn_trail(Enemy *e, int t) {
.color = rgba(1, 1, 1, 0.2),
.draw_rule = Shrink,
.rule = myon_particle_rule,
.args = { 10, cexp(I*M_PI*2*frand())*0.5, 0.2, 0 },
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT,
.timeout = 10,
.args = { cexp(I*M_PI*2*frand())*0.5, 0.2, 0 },
.blend = BLEND_ADD,
.flags = PFLAG_NOREFLECT,
.angle = M_PI*2*frand(),
);
}
@ -114,7 +116,8 @@ static void myon_spawn_trail(Enemy *e, int t) {
.color = myon_color(f, 0.5),
.pos = pos,
.rule = myon_flare_particle_rule,
.args = { 40, f * stardust_v },
.timeout = 40,
.args = { f * stardust_v },
.draw_rule = Shrink,
.flags = PFLAG_NOREFLECT | PFLAG_REQUIREDPARTICLE,
.angle = M_PI*2*frand(),
@ -149,8 +152,9 @@ static int myon_proj(Projectile *p, int t) {
.pos = p->pos,
.color = derive_color(p->color, CLRMASK_A, rgba(0, 0, 0, 0.075)),
.draw_rule = myon_draw_proj_trail,
.rule = timeout_linear,
.args = { 10, p->args[0]*0.8, 0.6, 0 },
.rule = linear,
.timeout = 10,
.args = { p->args[0]*0.8, 0.6, 0 },
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT,
.angle = p->angle,
);

View file

@ -50,12 +50,12 @@ static void youmu_homing_draw_proj(Projectile *p, int t) {
}
static void youmu_homing_draw_trail(Projectile *p, int t) {
float a = clamp(1.0f - (float)t / p->args[0], 0, 1);
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->args[0], 0, 1);
float a = clamp(1.0f - (float)t / p->timeout, 0, 1);
youmu_homing_draw_common(p, a, 2 - a, 0.15f * a);
}
@ -76,9 +76,10 @@ static Projectile* youmu_homing_trail(Projectile *p, complex v, int to) {
.pos = p->pos,
.color = p->color,
.angle = p->angle,
.rule = timeout_linear,
.rule = linear,
.timeout = to,
.draw_rule = youmu_homing_draw_trail,
.args = { to, v },
.args = { v },
.flags = PFLAG_NOREFLECT,
.shader_ptr = p->shader,
.layer = LAYER_PARTICLE_LOW,
@ -123,7 +124,10 @@ static Projectile* youmu_trap_trail(Projectile *p, complex v, int t) {
static int youmu_trap(Projectile *p, int t) {
if(t == EVENT_DEATH) {
PARTICLE("blast", p->pos, 0, blast_timeout, { 15 },
PARTICLE(
.proto = pp_blast,
.pos = p->pos,
.timeout = 15,
.draw_rule = Blast,
.flags = PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_LOW,
@ -145,13 +149,19 @@ static int youmu_trap(Projectile *p, int t) {
p->shader_custom_param = charge;
if(!(global.plr.inputflags & INFLAG_FOCUS)) {
PARTICLE("blast", p->pos, 0, blast_timeout, { 20 },
PARTICLE(
.proto = pp_blast,
.pos = p->pos,
.timeout = 20,
.draw_rule = Blast,
.flags = PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_LOW,
);
PARTICLE("blast", p->pos, 0, blast_timeout, { 23 },
PARTICLE(
.proto = pp_blast,
.pos = p->pos,
.timeout = 23,
.draw_rule = Blast,
.flags = PFLAG_REQUIREDPARTICLE,
.layer = LAYER_PARTICLE_LOW,
@ -191,7 +201,7 @@ static int youmu_trap(Projectile *p, int t) {
}
static void youmu_particle_slice_draw(Projectile *p, int t) {
double lifetime = creal(p->args[0]);
double lifetime = p->timeout;
double tt = t/lifetime;
double f = 0;
if(tt > 0.1) {
@ -217,10 +227,10 @@ static void youmu_particle_slice_draw(Projectile *p, int t) {
static int youmu_particle_slice_logic(Projectile *p, int t) {
if(t < 0) {
return 1;
return ACTION_NONE;
}
double lifetime = creal(p->args[0]);
double lifetime = p->timeout;
double tt = t/lifetime;
double a = 0;
if(tt > 0.) {
@ -250,7 +260,7 @@ static int youmu_particle_slice_logic(Projectile *p, int t) {
);
}
return timeout(p, t);
return ACTION_NONE;
}
static void YoumuSlash(Enemy *e, int t, bool render) {
@ -275,8 +285,9 @@ static int youmu_slash(Enemy *e, int t) {
.pos = e->pos+pos,
.draw_rule = youmu_particle_slice_draw,
.rule = youmu_particle_slice_logic,
.flags = PFLAG_DRAWADD | PFLAG_NOREFLECT,
.args = { 100 },
.flags = PFLAG_NOREFLECT,
.blend = BLEND_ADD,
.timeout = 100,
.angle = carg(pos),
);
}

View file

@ -38,16 +38,37 @@ static ProjArgs defaults_part = {
};
static void process_projectile_args(ProjArgs *args, ProjArgs *defaults) {
int texargs = (bool)args->sprite + (bool)args->sprite_ptr + (bool)args->size;
// Detect the deprecated way to spawn projectiles and remap it to prototypes,
// if possible. This is so that I can conserve the remains of my sanity by
// not having to convert every single PROJECTILE call in the game manually
// and in one go. So, TODO: convert every single PROJECTILE call in the game
// and remove this mess.
if(texargs != 1) {
log_fatal("Exactly one of .sprite, .sprite_ptr, or .size is required");
if(!args->proto && args->sprite && args->size == 0) {
static struct {
const char *name;
ProjPrototype *proto;
} proto_map[] = {
#define PP(name) { #name, &_pp_##name },
#include "projectile_prototypes/all.inc.h"
};
for(int i = 0; i < sizeof(proto_map)/sizeof(*proto_map); ++i) {
if(!strcmp(args->sprite, proto_map[i].name)) {
args->proto = proto_map[i].proto;
args->sprite = NULL;
break;
}
}
}
if(!args->rule) {
log_fatal(".rule is required");
if(args->proto && args->proto->process_args) {
args->proto->process_args(args->proto, args);
return;
}
// TODO: move this stuff into prototypes along with the defaults?
if(args->sprite) {
args->sprite_ptr = prefix_get_sprite(args->sprite, defaults->sprite);
}
@ -93,40 +114,49 @@ static void process_projectile_args(ProjArgs *args, ProjArgs *defaults) {
}
}
static double projectile_rect_area(Projectile *p) {
if(p->sprite) {
return p->sprite->w * p->sprite->h;
} else {
return creal(p->size) * cimag(p->size);
}
}
static void projectile_size(Projectile *p, double *w, double *h) {
if(p->sprite) {
if(p->type == Particle && p->sprite != NULL) {
*w = p->sprite->w;
*h = p->sprite->h;
} else {
*w = creal(p->size);
*h = cimag(p->size);
}
assert(*w > 0);
assert(*h > 0);
}
static double projectile_rect_area(Projectile *p) {
double w, h;
projectile_size(p, &w, &h);
return w * h;
}
static void ent_draw_projectile(EntityInterface *ent);
static int projectile_sizeprio_func(List *vproj) {
// FIXME: remove this when v1.2 compat is not needed
Projectile *proj = (Projectile*)vproj;
if(proj->priority_override) {
return proj->priority_override;
static inline int proj_call_rule(Projectile *p, int t) {
if(p->timeout > 0 && t >= p->timeout) {
return ACTION_DESTROY;
}
return -rint(projectile_rect_area(proj));
if(p->rule) {
return p->rule(p, t);
}
return ACTION_NONE;
}
List* proj_insert_sizeprio(List **dest, List *elem) {
// FIXME: remove this when v1.2 compat is not needed
return list_insert_at_priority_tail(dest, elem, projectile_sizeprio_func(elem), projectile_sizeprio_func);
void projectile_set_prototype(Projectile *p, ProjPrototype *proto) {
if(p->proto && p->proto->deinit_projectile) {
p->proto->deinit_projectile(p->proto, p);
}
if(proto && proto->init_projectile) {
proto->init_projectile(proto, p);
}
p->proto = proto;
}
static Projectile* _create_projectile(ProjArgs *args) {
@ -151,11 +181,20 @@ static Projectile* _create_projectile(ProjArgs *args) {
p->max_viewport_dist = args->max_viewport_dist;
p->size = args->size;
p->flags = args->flags;
p->priority_override = args->priority_override;
p->timeout = args->timeout;
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);
if((p->type == EnemyProj || p->type >= PlrProj) && (creal(p->size) <= 0 || cimag(p->size) <= 0)) {
// FIXME: debug info is not actually attached at this point!
set_debug_info(&p->debug);
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)) {
switch(p->type) {
@ -168,16 +207,16 @@ static Projectile* _create_projectile(ProjArgs *args) {
sublayer <<= 1;
sublayer |= 1 * (p->blend == BLEND_ADD || p->flags & PFLAG_DRAWADD);
// If specific blending order is required, then set up the sublayer manually.
// If specific blending order is required, then you should set up the sublayer manually.
p->ent.draw_layer |= sublayer;
break;
}
case Particle: {
// 1. Additive particles go above others.
drawlayer_low_t sublayer = (p->blend == BLEND_ADD || p->flags & PFLAG_DRAWADD) * PARTICLE_ADDITIVE_SUBLAYER;
drawlayer_low_t sublayer = (p->blend == BLEND_ADD || p->flags & PFLAG_DRAWADD) * PARTICLE_ADDITIVE_SUBLAYER;
// If specific blending order is required, then set up the sublayer manually.
// If specific blending order is required, then you should set up the sublayer manually.
p->ent.draw_layer |= sublayer;
break;
}
@ -187,17 +226,13 @@ static Projectile* _create_projectile(ProjArgs *args) {
}
}
p->ent.draw_func = ent_draw_projectile;
ent_register(&p->ent, ENT_PROJECTILE);
// BUG: this currently breaks some projectiles
// enable this when they're fixed
// assert(rule != NULL);
// rule(p, EVENT_BIRTH);
// proj_call_rule(p, EVENT_BIRTH);
// FIXME: use simpler/faster insertions when v1.2 compat is not needed
// return (Projectile*)list_push(args->dest, p);
return (Projectile*)proj_insert_sizeprio((List**)args->dest, (List*)p);
return list_append(args->dest, p);
}
Projectile* create_projectile(ProjArgs *args) {
@ -221,7 +256,7 @@ Projectile* _proj_attach_dbginfo(Projectile *p, DebugInfo *dbg, const char *call
static void* _delete_projectile(List **projs, List *proj, void *arg) {
Projectile *p = (Projectile*)proj;
p->rule(p, EVENT_DEATH);
proj_call_rule(p, EVENT_DEATH);
del_ref(proj);
ent_unregister(&p->ent);
@ -413,14 +448,16 @@ Projectile* spawn_projectile_collision_effect(Projectile *proj) {
return PARTICLE(
.sprite_ptr = proj->sprite,
.size = proj->size,
.pos = proj->pos,
.color = proj->color,
.flags = proj->flags | PFLAG_NOREFLECT,
.shader_ptr = proj->shader,
.rule = timeout_linear,
.rule = linear,
.draw_rule = DeathShrink,
.angle = proj->angle,
.args = { 10, 5*cexp(I*proj->angle)},
.args = { 5*cexp(I*proj->angle) },
.timeout = 10,
);
}
@ -431,13 +468,14 @@ Projectile* spawn_projectile_clear_effect(Projectile *proj) {
return PARTICLE(
.sprite_ptr = proj->sprite,
.size = proj->size,
.pos = proj->pos,
.color = proj->color,
.flags = proj->flags | PFLAG_NOREFLECT,
.shader_ptr = proj->shader,
.rule = timeout,
.draw_rule = Shrink,
.angle = proj->angle,
.timeout = 10,
.args = { 10 },
);
}
@ -469,8 +507,7 @@ void process_projectiles(Projectile **projs, bool collision) {
for(Projectile *proj = *projs, *next; proj; proj = next) {
next = proj->next;
action = proj->rule(proj, global.frames - proj->birthtime);
action = proj_call_rule(proj, global.frames - proj->birthtime);
if(proj->type == DeadProj && killed < 5) {
killed++;
@ -601,23 +638,16 @@ void ProjDraw(Projectile *proj, int t) {
void ProjNoDraw(Projectile *proj, int t) {
}
int blast_timeout(Projectile *p, int t) {
if(t == 1) {
p->args[1] = frand()*360 + frand()*I;
p->args[2] = frand() + frand()*I;
}
return timeout(p, t);
}
void Blast(Projectile *p, int t) {
r_mat_push();
r_mat_translate(creal(p->pos), cimag(p->pos), 0);
r_mat_rotate_deg(creal(p->args[1]), cimag(p->args[1]), creal(p->args[2]), cimag(p->args[2]));
if(t != p->args[0] && p->args[0] != 0)
r_mat_scale(t/p->args[0], t/p->args[0], 1);
r_color4(0.3, 0.6, 1.0, 1.0 - t/p->args[0]);
if(t != p->timeout && p->timeout != 0) {
r_mat_scale(t/(double)p->timeout, t/(double)p->timeout, 1);
}
r_color4(0.3, 0.6, 1.0, 1.0 - t/(double)p->timeout);
draw_sprite_batched_p(0,0,p->sprite);
r_mat_scale(0.5+creal(p->args[2]),0.5+creal(p->args[2]),1);
@ -630,7 +660,7 @@ void Shrink(Projectile *p, int t) {
r_mat_push();
apply_common_transforms(p, t);
float s = 2.0-t/p->args[0]*2;
float s = 2.0-t/(double)p->timeout*2;
if(s != 1) {
r_mat_scale(s, s, 1);
}
@ -643,7 +673,7 @@ void DeathShrink(Projectile *p, int t) {
r_mat_push();
apply_common_transforms(p, t);
float s = 2.0-t/p->args[0]*2;
float s = 2.0-t/(double)p->timeout*2;
if(s != 1) {
r_mat_scale(s, 1, 1);
}
@ -656,19 +686,22 @@ void GrowFade(Projectile *p, int t) {
r_mat_push();
apply_common_transforms(p, t);
float s = t/p->args[0]*(1 + (creal(p->args[2])? p->args[2] : p->args[1]));
set_debug_info(&p->debug);
assert(p->timeout != 0);
float s = t/(double)p->timeout*(1 + (creal(p->args[2])? p->args[2] : p->args[1]));
if(s != 1) {
r_mat_scale(s, s, 1);
}
ProjDrawCore(p, multiply_colors(p->color, rgba(1, 1, 1, 1 - t/p->args[0])));
ProjDrawCore(p, multiply_colors(p->color, rgba(1, 1, 1, 1 - t/(double)p->timeout)));
r_mat_pop();
}
void Fade(Projectile *p, int t) {
r_mat_push();
apply_common_transforms(p, t);
ProjDrawCore(p, multiply_colors(p->color, rgba(1, 1, 1, 1 - t/p->args[0])));
ProjDrawCore(p, multiply_colors(p->color, rgba(1, 1, 1, 1 - t/(double)p->timeout)));
r_mat_pop();
}
@ -678,7 +711,7 @@ void ScaleFade(Projectile *p, int t) {
double scale_min = creal(p->args[2]);
double scale_max = cimag(p->args[2]);
double timefactor = t / creal(p->args[0]);
double timefactor = t / (double)p->timeout;
double scale = scale_min * (1 - timefactor) + scale_max * timefactor;
double alpha = pow(1 - timefactor, 2);
@ -691,36 +724,6 @@ void ScaleFade(Projectile *p, int t) {
r_mat_pop();
}
int timeout(Projectile *p, int t) {
if(t >= creal(p->args[0]))
return ACTION_DESTROY;
return 1;
}
int timeout_linear(Projectile *p, int t) {
if(t >= creal(p->args[0]))
return ACTION_DESTROY;
if(t < 0)
return 1;
p->angle = carg(p->args[1]);
p->pos = p->pos0 + p->args[1]*t;
return 1;
}
int timeout_linear_fixangle(Projectile *p, int t) {
if(t >= creal(p->args[0]))
return ACTION_DESTROY;
if(t < 0)
return 1;
p->pos = p->pos0 + p->args[1]*t;
return 1;
}
void Petal(Projectile *p, int t) {
float x = creal(p->args[2]);
float y = cimag(p->args[2]);
@ -743,7 +746,11 @@ void petal_explosion(int n, complex pos) {
float t = frand();
Color c = rgba(sin(5*t),cos(5*t),0.5,t);
PARTICLE("petal", pos, c, asymptotic,
PARTICLE(
.sprite = "petal",
.pos = pos,
.color = c,
.rule = asymptotic,
.draw_rule = Petal,
.args = {
(3+5*afrand(2))*cexp(I*M_PI*2*afrand(3)),
@ -781,19 +788,6 @@ void projectiles_preload(void) {
"part/lightning1",
"part/lightningball",
"part/smoothdot",
"proj/ball",
"proj/bigball",
"proj/bullet",
"proj/card",
"proj/crystal",
"proj/flea",
"proj/hghost",
"proj/plainball",
"proj/rice",
"proj/soul",
"proj/thickrice",
"proj/wave",
"proj/youhoming",
NULL);