Allow projectile rules to delete other projectiles; add stage_clear_hazards enhancements

This commit is contained in:
Andrei Alexeyev 2018-08-05 20:58:50 +03:00
parent a562ccb87f
commit d4650d4b92
No known key found for this signature in database
GPG key ID: 363707CD4C7FE8A4
7 changed files with 112 additions and 19 deletions

View file

@ -344,6 +344,42 @@ static bool collision_laser_curve(Laser *l) {
return lineseg_circle_intersect(segment, collision_area) >= 0;
}
bool laser_intersects_circle(Laser *l, Circle circle) {
// TODO: lots of copypasta from the function above here, maybe refactor both somehow.
float t_end = (global.frames - l->birthtime) * l->speed + l->timeshift; // end of the laser based on length
float t_death = l->deathtime * l->speed + l->timeshift; // end of the laser based on lifetime
float t = t_end - l->timespan;
if(t < 0) {
t = 0;
}
LineSegment segment = { .a = l->prule(l, t) };
double orig_radius = circle.radius;
for(t += l->collision_step; t <= min(t_end, t_death); t += l->collision_step) {
float t1 = t - l->timespan / 2; // i have no idea
float tail = l->timespan / 1.9;
float widthfac = -0.75 / pow(tail, 2) * (t1 - tail) * (t1 + tail);
widthfac = max(0.25, pow(widthfac, l->width_exponent));
segment.b = l->prule(l, t);
circle.radius = orig_radius + widthfac * l->width * 0.5 + 1;
if(lineseg_circle_intersect(segment, circle) >= 0) {
return true;
}
segment.a = segment.b;
}
segment.b = l->prule(l, min(t_end, t_death));
circle.radius = orig_radius + l->width * 0.5; // WTF: what is this sorcery?
return lineseg_circle_intersect(segment, circle) >= 0;
}
complex las_linear(Laser *l, float t) {
if(t == EVENT_BIRTH) {
l->shader = r_shader_get_optional("lasers/linear");

View file

@ -77,3 +77,5 @@ complex las_circle(Laser *l, float t);
float laser_charge(Laser *l, int t, float charge, float width);
void static_laser(Laser *l, int t);
bool laser_intersects_circle(Laser *l, Circle circle);

View file

@ -112,7 +112,7 @@ static void trace_laser(Enemy *e, complex vel, float damage) {
col.fatal = false;
}
apply_projectile_collision(&lproj, lproj.first, &col);
apply_projectile_collision(&lproj, lproj.first, &col, NULL);
if(c) {
c->original_hp = ((Enemy*)col.entity)->hp;

View file

@ -269,8 +269,13 @@ Projectile* _proj_attach_dbginfo(Projectile *p, DebugInfo *dbg, const char *call
static void* _delete_projectile(ListAnchor *projlist, List *proj, void *arg) {
Projectile *p = (Projectile*)proj;
ProjectileListInterface *out_list_pointers = arg;
proj_call_rule(p, EVENT_DEATH);
if(out_list_pointers) {
*&out_list_pointers->list_interface = p->list_interface;
}
del_ref(proj);
ent_unregister(&p->ent);
objpool_release(stage_object_pools.projectiles, (ObjectInterface*)alist_unlink(projlist, proj));
@ -278,8 +283,8 @@ static void* _delete_projectile(ListAnchor *projlist, List *proj, void *arg) {
return NULL;
}
void delete_projectile(ProjectileList *projlist, Projectile *proj) {
_delete_projectile((ListAnchor*)projlist, (List*)proj, NULL);
void delete_projectile(ProjectileList *projlist, Projectile *proj, ProjectileListInterface *out_list_pointers) {
_delete_projectile((ListAnchor*)projlist, (List*)proj, out_list_pointers);
}
void delete_projectiles(ProjectileList *projlist) {
@ -361,7 +366,7 @@ void calc_projectile_collision(Projectile *p, ProjCollisionResult *out_col) {
}
}
void apply_projectile_collision(ProjectileList *projlist, Projectile *p, ProjCollisionResult *col) {
void apply_projectile_collision(ProjectileList *projlist, Projectile *p, ProjCollisionResult *col, ProjectileListInterface *out_list_pointers) {
switch(col->type) {
case PCOL_NONE: {
break;
@ -398,7 +403,9 @@ void apply_projectile_collision(ProjectileList *projlist, Projectile *p, ProjCol
}
if(col->fatal) {
delete_projectile(projlist, p);
delete_projectile(projlist, p, out_list_pointers);
} else {
*&out_list_pointers->list_interface = p->list_interface;
}
}
@ -474,8 +481,13 @@ Projectile* spawn_projectile_clear_effect(Projectile *proj) {
);
}
bool clear_projectile(ProjectileList *projlist, Projectile *proj, bool force, bool now) {
bool clear_projectile(ProjectileList *projlist, Projectile *proj, bool force, bool now, ProjectileListInterface *out_list_pointers) {
if(proj->type == PlrProj || (!force && !projectile_is_clearable(proj))) {
if(out_list_pointers) {
*&out_list_pointers->list_interface = proj->list_interface;
}
return false;
}
@ -485,9 +497,13 @@ bool clear_projectile(ProjectileList *projlist, Projectile *proj, bool force, bo
}
spawn_projectile_clear_effect(proj);
delete_projectile(projlist, proj);
delete_projectile(projlist, proj, out_list_pointers);
} else {
proj->type = DeadProj;
if(out_list_pointers) {
*&out_list_pointers->list_interface = proj->list_interface;
}
}
return true;
@ -495,14 +511,15 @@ bool clear_projectile(ProjectileList *projlist, Projectile *proj, bool force, bo
void process_projectiles(ProjectileList *projlist, bool collision) {
ProjCollisionResult col = { 0 };
ProjectileListInterface list_ptrs;
char killed = 0;
int action;
for(Projectile *proj = projlist->first, *next; proj; proj = next) {
next = proj->next;
for(Projectile *proj = projlist->first; proj; proj = list_ptrs.next) {
proj->prevpos = proj->pos;
action = proj_call_rule(proj, global.frames - proj->birthtime);
*&list_ptrs.list_interface = proj->list_interface;
if(proj->graze_counter && proj->graze_counter_reset_timer - global.frames <= -90) {
proj->graze_counter--;
@ -513,7 +530,7 @@ void process_projectiles(ProjectileList *projlist, bool collision) {
killed++;
action = ACTION_DESTROY;
if(clear_projectile(projlist, proj, true, true)) {
if(clear_projectile(projlist, proj, true, true, &list_ptrs)) {
continue;
}
}
@ -536,7 +553,7 @@ void process_projectiles(ProjectileList *projlist, bool collision) {
col.fatal = true;
}
apply_projectile_collision(projlist, proj, &col);
apply_projectile_collision(projlist, proj, &col, &list_ptrs);
}
}

View file

@ -27,6 +27,7 @@ enum {
typedef struct Projectile Projectile;
typedef LIST_ANCHOR(Projectile) ProjectileList;
typedef LIST_INTERFACE(Projectile) ProjectileListInterface;
typedef int (*ProjRule)(Projectile *p, int t);
typedef void (*ProjDrawRule)(Projectile *p, int t);
@ -169,11 +170,11 @@ Projectile* create_particle(ProjArgs *args);
#define PROJECTILE(...) _PROJ_GENERIC_SPAWN(create_projectile, __VA_ARGS__)
#define PARTICLE(...) _PROJ_GENERIC_SPAWN(create_particle, __VA_ARGS__)
void delete_projectile(ProjectileList *projlist, Projectile *proj);
void delete_projectile(ProjectileList *projlist, Projectile *proj, ProjectileListInterface *out_list_pointers);
void delete_projectiles(ProjectileList *projlist);
void calc_projectile_collision(Projectile *p, ProjCollisionResult *out_col);
void apply_projectile_collision(ProjectileList *projlist, Projectile *p, ProjCollisionResult *col);
void apply_projectile_collision(ProjectileList *projlist, Projectile *p, ProjCollisionResult *col, ProjectileListInterface *out_list_pointers);
int trace_projectile(Projectile *p, ProjCollisionResult *out_col, ProjCollisionType stopflags, int timeofs);
bool projectile_in_viewport(Projectile *proj);
void process_projectiles(ProjectileList *projlist, bool collision);
@ -184,7 +185,7 @@ Projectile* spawn_projectile_clear_effect(Projectile *proj);
void projectile_set_prototype(Projectile *p, ProjPrototype *proto);
bool clear_projectile(ProjectileList *projlist, Projectile *proj, bool force, bool now);
bool clear_projectile(ProjectileList *projlist, Projectile *proj, bool force, bool now, ProjectileListInterface *out_list_pointers);
int linear(Projectile *p, int t);
int accelerated(Projectile *p, int t);

View file

@ -432,22 +432,57 @@ static void stage_logic(void) {
}
}
void stage_clear_hazards(ClearHazardsFlags flags) {
void stage_clear_hazards_predicate(bool (*predicate)(EntityInterface *ent, void *arg), void *arg, ClearHazardsFlags flags) {
if(flags & CLEAR_HAZARDS_BULLETS) {
for(Projectile *p = global.projs.first, *next; p; p = next) {
next = p->next;
clear_projectile(&global.projs, p, flags & CLEAR_HAZARDS_FORCE, flags & CLEAR_HAZARDS_NOW);
ProjectileListInterface list_ptrs;
for(Projectile *p = global.projs.first; p; p = list_ptrs.next) {
if(!predicate || predicate(&p->ent, arg)) {
clear_projectile(&global.projs, p, flags & CLEAR_HAZARDS_FORCE, flags & CLEAR_HAZARDS_NOW, &list_ptrs);
} else {
*&list_ptrs.list_interface = p->list_interface;
}
}
}
if(flags & CLEAR_HAZARDS_LASERS) {
for(Laser *l = global.lasers.first, *next; l; l = next) {
next = l->next;
clear_laser(&global.lasers, l, flags & CLEAR_HAZARDS_FORCE, flags & CLEAR_HAZARDS_NOW);
if(!predicate || predicate(&l->ent, arg)) {
clear_laser(&global.lasers, l, flags & CLEAR_HAZARDS_FORCE, flags & CLEAR_HAZARDS_NOW);
}
}
}
}
void stage_clear_hazards(ClearHazardsFlags flags) {
stage_clear_hazards_predicate(NULL, NULL, flags);
}
static bool proximity_predicate(EntityInterface *ent, void *varg) {
Circle *area = varg;
switch(ent->type) {
case ENT_PROJECTILE: {
Projectile *p = ENT_CAST(ent, Projectile);
return cabs(p->pos - area->origin) < area->radius;
}
case ENT_LASER: {
Laser *l = ENT_CAST(ent, Laser);
return laser_intersects_circle(l, *area);
}
default: UNREACHABLE;
}
}
void stage_clear_hazards_at(complex origin, double radius, ClearHazardsFlags flags) {
Circle area = { origin, radius };
stage_clear_hazards_predicate(proximity_predicate, &area, flags);
}
static void stage_free(void) {
delete_enemies(&global.enemies);
delete_enemies(&global.plr.slaves);

View file

@ -125,6 +125,8 @@ typedef enum ClearHazardsFlags {
} ClearHazardsFlags;
void stage_clear_hazards(ClearHazardsFlags flags);
void stage_clear_hazards_at(complex origin, double radius, ClearHazardsFlags flags);
void stage_clear_hazards_predicate(bool (*predicate)(EntityInterface *ent, void *arg), void *arg, ClearHazardsFlags flags);
#include "stages/stage1.h"
#include "stages/stage2.h"