2010-10-12 10:55:23 +02:00
|
|
|
/*
|
2011-03-05 19:03:32 +01:00
|
|
|
* This software is licensed under the terms of the MIT-License
|
2017-02-10 23:05:22 +01:00
|
|
|
* See COPYING for further information.
|
2011-03-05 19:03:32 +01:00
|
|
|
* ---
|
2018-01-04 18:14:31 +01:00
|
|
|
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
|
|
|
|
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
|
2010-10-12 10:55:23 +02:00
|
|
|
*/
|
|
|
|
|
2017-11-25 20:45:11 +01:00
|
|
|
#include "taisei.h"
|
|
|
|
|
2010-10-12 10:55:23 +02:00
|
|
|
#include "projectile.h"
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "global.h"
|
2010-10-27 19:51:49 +02:00
|
|
|
#include "list.h"
|
2017-12-13 20:05:12 +01:00
|
|
|
#include "stageobjects.h"
|
2011-04-26 16:55:18 +02:00
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
static ProjArgs defaults_proj = {
|
2018-02-06 07:19:25 +01:00
|
|
|
.sprite = "proj/",
|
2017-11-10 21:49:16 +01:00
|
|
|
.draw_rule = ProjDraw,
|
|
|
|
.dest = &global.projs,
|
|
|
|
.type = EnemyProj,
|
|
|
|
.color = RGB(1, 1, 1),
|
2018-04-12 16:08:48 +02:00
|
|
|
.blend = BLEND_ALPHA,
|
|
|
|
.shader = "sprite_bullet",
|
2018-04-13 21:13:48 +02:00
|
|
|
.layer = LAYER_BULLET,
|
2017-11-10 21:49:16 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static ProjArgs defaults_part = {
|
2018-02-06 07:19:25 +01:00
|
|
|
.sprite = "part/",
|
2017-11-10 21:49:16 +01:00
|
|
|
.draw_rule = ProjDraw,
|
|
|
|
.dest = &global.particles,
|
|
|
|
.type = Particle,
|
|
|
|
.color = RGB(1, 1, 1),
|
2018-04-12 16:08:48 +02:00
|
|
|
.blend = BLEND_ALPHA,
|
|
|
|
.shader = "sprite_default",
|
2018-04-13 21:13:48 +02:00
|
|
|
.layer = LAYER_PARTICLE_HIGH,
|
2017-11-10 21:49:16 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static void process_projectile_args(ProjArgs *args, ProjArgs *defaults) {
|
2018-05-02 06:46:48 +02:00
|
|
|
// 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(!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;
|
|
|
|
}
|
|
|
|
}
|
2017-11-10 21:49:16 +01:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
if(args->proto && args->proto->process_args) {
|
|
|
|
args->proto->process_args(args->proto, args);
|
|
|
|
return;
|
2017-11-10 21:49:16 +01:00
|
|
|
}
|
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
// TODO: move this stuff into prototypes along with the defaults?
|
|
|
|
|
2018-02-06 07:19:25 +01:00
|
|
|
if(args->sprite) {
|
|
|
|
args->sprite_ptr = prefix_get_sprite(args->sprite, defaults->sprite);
|
2017-11-10 21:49:16 +01:00
|
|
|
}
|
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
if(!args->shader_ptr) {
|
|
|
|
if(args->shader) {
|
|
|
|
args->shader_ptr = r_shader_get(args->shader);
|
|
|
|
} else {
|
|
|
|
args->shader_ptr = defaults->shader_ptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
if(!args->draw_rule) {
|
|
|
|
args->draw_rule = ProjDraw;
|
|
|
|
}
|
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
if(!args->blend) {
|
|
|
|
args->blend = defaults->blend;
|
|
|
|
}
|
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
if(!args->dest) {
|
|
|
|
args->dest = defaults->dest;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!args->type) {
|
|
|
|
args->type = defaults->type;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!args->color) {
|
|
|
|
args->color = defaults->color;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!args->max_viewport_dist && (args->type == Particle || args->type >= PlrProj)) {
|
|
|
|
args->max_viewport_dist = 300;
|
|
|
|
}
|
2018-04-13 21:13:48 +02:00
|
|
|
|
|
|
|
if(!args->layer) {
|
|
|
|
if(args->type >= PlrProj) {
|
|
|
|
args->layer = LAYER_PLAYER_SHOT;
|
|
|
|
} else {
|
|
|
|
args->layer = defaults->layer;
|
|
|
|
}
|
|
|
|
}
|
2017-11-10 21:49:16 +01:00
|
|
|
}
|
|
|
|
|
2017-11-23 16:27:41 +01:00
|
|
|
static void projectile_size(Projectile *p, double *w, double *h) {
|
2018-05-02 06:46:48 +02:00
|
|
|
if(p->type == Particle && p->sprite != NULL) {
|
2018-02-06 07:19:25 +01:00
|
|
|
*w = p->sprite->w;
|
|
|
|
*h = p->sprite->h;
|
2017-11-23 16:27:41 +01:00
|
|
|
} else {
|
|
|
|
*w = creal(p->size);
|
|
|
|
*h = cimag(p->size);
|
|
|
|
}
|
2018-05-02 06:46:48 +02:00
|
|
|
|
|
|
|
assert(*w > 0);
|
|
|
|
assert(*h > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static double projectile_rect_area(Projectile *p) {
|
|
|
|
double w, h;
|
|
|
|
projectile_size(p, &w, &h);
|
|
|
|
return w * h;
|
2017-11-10 21:49:16 +01:00
|
|
|
}
|
|
|
|
|
2018-04-13 21:13:48 +02:00
|
|
|
static void ent_draw_projectile(EntityInterface *ent);
|
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
static inline int proj_call_rule(Projectile *p, int t) {
|
|
|
|
if(p->timeout > 0 && t >= p->timeout) {
|
|
|
|
return ACTION_DESTROY;
|
|
|
|
}
|
2018-01-06 07:06:12 +01:00
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
if(p->rule) {
|
|
|
|
return p->rule(p, t);
|
2018-01-06 07:06:12 +01:00
|
|
|
}
|
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
return ACTION_NONE;
|
2017-11-23 16:27:41 +01:00
|
|
|
}
|
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
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;
|
2018-01-08 17:56:46 +01:00
|
|
|
}
|
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
static Projectile* _create_projectile(ProjArgs *args) {
|
2017-11-15 07:05:47 +01:00
|
|
|
if(IN_DRAW_CODE) {
|
|
|
|
log_fatal("Tried to spawn a projectile while in drawing code");
|
|
|
|
}
|
|
|
|
|
2017-12-13 20:05:12 +01:00
|
|
|
Projectile *p = (Projectile*)objpool_acquire(stage_object_pools.projectiles);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-03-07 07:26:43 +01:00
|
|
|
p->birthtime = global.frames;
|
2017-11-10 21:49:16 +01:00
|
|
|
p->pos = p->pos0 = args->pos;
|
|
|
|
p->angle = args->angle;
|
|
|
|
p->rule = args->rule;
|
|
|
|
p->draw_rule = args->draw_rule;
|
2018-04-12 16:08:48 +02:00
|
|
|
p->shader = args->shader_ptr;
|
|
|
|
p->shader_custom_param = args->shader_custom_param;
|
|
|
|
p->blend = args->blend;
|
2018-02-06 07:19:25 +01:00
|
|
|
p->sprite = args->sprite_ptr;
|
2017-11-10 21:49:16 +01:00
|
|
|
p->type = args->type;
|
|
|
|
p->color = args->color;
|
|
|
|
p->grazed = (bool)(args->flags & PFLAG_NOGRAZE);
|
|
|
|
p->max_viewport_dist = args->max_viewport_dist;
|
|
|
|
p->size = args->size;
|
|
|
|
p->flags = args->flags;
|
2018-05-02 06:46:48 +02:00
|
|
|
p->timeout = args->timeout;
|
2017-11-10 21:49:16 +01:00
|
|
|
|
|
|
|
memcpy(p->args, args->args, sizeof(p->args));
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2018-04-13 21:13:48 +02:00
|
|
|
p->ent.draw_layer = args->layer;
|
2018-05-02 06:46:48 +02:00
|
|
|
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));
|
|
|
|
}
|
2018-04-13 21:13:48 +02:00
|
|
|
|
|
|
|
if(!(p->ent.draw_layer & LAYER_LOW_MASK)) {
|
|
|
|
switch(p->type) {
|
|
|
|
case EnemyProj:
|
|
|
|
case FakeProj: {
|
|
|
|
// 1. Large projectiles go below smaller ones.
|
|
|
|
drawlayer_low_t sublayer = (LAYER_LOW_MASK - (drawlayer_low_t)projectile_rect_area(p));
|
|
|
|
|
|
|
|
// 2. Additive projectiles go below others.
|
|
|
|
sublayer <<= 1;
|
|
|
|
sublayer |= 1 * (p->blend == BLEND_ADD || p->flags & PFLAG_DRAWADD);
|
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
// If specific blending order is required, then you should set up the sublayer manually.
|
2018-04-13 21:13:48 +02:00
|
|
|
p->ent.draw_layer |= sublayer;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Particle: {
|
|
|
|
// 1. Additive particles go above others.
|
2018-05-02 06:46:48 +02:00
|
|
|
drawlayer_low_t sublayer = (p->blend == BLEND_ADD || p->flags & PFLAG_DRAWADD) * PARTICLE_ADDITIVE_SUBLAYER;
|
2018-04-13 21:13:48 +02:00
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
// If specific blending order is required, then you should set up the sublayer manually.
|
2018-04-13 21:13:48 +02:00
|
|
|
p->ent.draw_layer |= sublayer;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ent_register(&p->ent, ENT_PROJECTILE);
|
|
|
|
|
2017-10-20 20:31:21 +02:00
|
|
|
// BUG: this currently breaks some projectiles
|
2018-01-12 19:26:07 +01:00
|
|
|
// enable this when they're fixed
|
2018-05-02 06:46:48 +02:00
|
|
|
// proj_call_rule(p, EVENT_BIRTH);
|
2017-10-20 20:31:21 +02:00
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
return list_append(args->dest, p);
|
2010-10-12 10:55:23 +02:00
|
|
|
}
|
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
Projectile* create_projectile(ProjArgs *args) {
|
|
|
|
process_projectile_args(args, &defaults_proj);
|
|
|
|
return _create_projectile(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
Projectile* create_particle(ProjArgs *args) {
|
|
|
|
process_projectile_args(args, &defaults_part);
|
|
|
|
return _create_projectile(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef PROJ_DEBUG
|
2017-11-23 16:27:41 +01:00
|
|
|
Projectile* _proj_attach_dbginfo(Projectile *p, DebugInfo *dbg, const char *callsite_str) {
|
|
|
|
// log_debug("Spawn: [%s]", callsite_str);
|
2017-11-15 07:05:47 +01:00
|
|
|
memcpy(&p->debug, dbg, sizeof(DebugInfo));
|
|
|
|
set_debug_info(dbg);
|
2017-11-10 21:49:16 +01:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-11-21 15:45:01 +01:00
|
|
|
static void* _delete_projectile(List **projs, List *proj, void *arg) {
|
|
|
|
Projectile *p = (Projectile*)proj;
|
2018-05-02 06:46:48 +02:00
|
|
|
proj_call_rule(p, EVENT_DEATH);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-05-13 19:03:02 +02:00
|
|
|
del_ref(proj);
|
2018-04-13 21:13:48 +02:00
|
|
|
ent_unregister(&p->ent);
|
2017-12-13 20:05:12 +01:00
|
|
|
objpool_release(stage_object_pools.projectiles, (ObjectInterface*)list_unlink(projs, proj));
|
|
|
|
|
2017-11-21 15:45:01 +01:00
|
|
|
return NULL;
|
2011-04-26 22:39:50 +02:00
|
|
|
}
|
|
|
|
|
2011-05-06 17:09:43 +02:00
|
|
|
void delete_projectile(Projectile **projs, Projectile *proj) {
|
2017-11-21 15:45:01 +01:00
|
|
|
_delete_projectile((List**)projs, (List*)proj, NULL);
|
2010-10-12 10:55:23 +02:00
|
|
|
}
|
|
|
|
|
2011-05-06 17:09:43 +02:00
|
|
|
void delete_projectiles(Projectile **projs) {
|
2017-12-24 07:16:25 +01:00
|
|
|
list_foreach(projs, _delete_projectile, NULL);
|
2010-10-12 10:55:23 +02:00
|
|
|
}
|
|
|
|
|
2017-12-28 10:05:54 +01:00
|
|
|
void calc_projectile_collision(Projectile *p, ProjCollisionResult *out_col) {
|
|
|
|
assert(out_col != NULL);
|
|
|
|
|
|
|
|
out_col->type = PCOL_NONE;
|
|
|
|
out_col->entity = NULL;
|
|
|
|
out_col->fatal = false;
|
|
|
|
out_col->location = p->pos;
|
|
|
|
out_col->damage = 0;
|
|
|
|
|
2017-11-05 15:02:49 +01:00
|
|
|
if(p->type == EnemyProj) {
|
2017-11-01 05:47:36 +01:00
|
|
|
double w, h;
|
2017-11-23 16:27:41 +01:00
|
|
|
projectile_size(p, &w, &h);
|
2017-11-01 05:47:36 +01:00
|
|
|
|
2012-08-13 21:53:36 +02:00
|
|
|
double angle = carg(global.plr.pos - p->pos) + p->angle;
|
2017-11-11 16:38:32 +01:00
|
|
|
double projr = sqrt(pow(w/2*cos(angle), 2) + pow(h/2*sin(angle), 2)) * 0.45;
|
2017-11-01 05:47:36 +01:00
|
|
|
double grazer = max(w, h);
|
2012-08-13 21:53:36 +02:00
|
|
|
double dst = cabs(global.plr.pos - p->pos);
|
2017-02-10 23:05:22 +01:00
|
|
|
grazer = (0.9 * sqrt(grazer) + 0.1 * grazer) * 6;
|
|
|
|
|
2017-12-28 10:05:54 +01:00
|
|
|
if(dst < projr + 1) {
|
|
|
|
out_col->type = PCOL_PLAYER;
|
|
|
|
out_col->entity = &global.plr;
|
|
|
|
out_col->fatal = true;
|
|
|
|
} else if(!p->grazed && dst < grazer && global.frames - abs(global.plr.recovery) > 0) {
|
|
|
|
out_col->location = p->pos - grazer * 0.3 * cexp(I*carg(p->pos - global.plr.pos));
|
|
|
|
out_col->type = PCOL_PLAYER_GRAZE;
|
|
|
|
out_col->entity = &global.plr;
|
2012-08-12 21:29:10 +02:00
|
|
|
}
|
2011-07-06 14:33:53 +02:00
|
|
|
} else if(p->type >= PlrProj) {
|
2017-02-10 23:05:22 +01:00
|
|
|
int damage = p->type - PlrProj;
|
|
|
|
|
2017-12-28 10:05:54 +01:00
|
|
|
for(Enemy *e = global.enemies; e; e = e->next) {
|
2017-10-28 05:41:45 +02:00
|
|
|
if(e->hp != ENEMY_IMMUNE && cabs(e->pos - p->pos) < 30) {
|
2017-12-28 10:05:54 +01:00
|
|
|
out_col->type = PCOL_ENEMY;
|
|
|
|
out_col->entity = e;
|
|
|
|
out_col->fatal = true;
|
|
|
|
out_col->damage = damage;
|
2017-03-06 13:02:46 +01:00
|
|
|
|
2017-12-28 10:05:54 +01:00
|
|
|
return;
|
2010-10-17 10:39:07 +02:00
|
|
|
}
|
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-09-27 11:08:32 +02:00
|
|
|
if(global.boss && cabs(global.boss->pos - p->pos) < 42) {
|
2017-12-28 10:05:54 +01:00
|
|
|
if(boss_is_vulnerable(global.boss)) {
|
|
|
|
out_col->type = PCOL_BOSS;
|
|
|
|
out_col->entity = global.boss;
|
|
|
|
out_col->fatal = true;
|
|
|
|
out_col->damage = damage;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(out_col->type == PCOL_NONE && !projectile_in_viewport(p)) {
|
|
|
|
out_col->type = PCOL_VOID;
|
|
|
|
out_col->fatal = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void apply_projectile_collision(Projectile **projlist, Projectile *p, ProjCollisionResult *col) {
|
|
|
|
switch(col->type) {
|
|
|
|
case PCOL_NONE: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PCOL_PLAYER: {
|
|
|
|
if(global.frames - abs(((Player*)col->entity)->recovery) >= 0) {
|
|
|
|
player_death(col->entity);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PCOL_PLAYER_GRAZE: {
|
|
|
|
p->grazed = true;
|
2018-01-06 10:44:27 +01:00
|
|
|
|
|
|
|
if(p->flags & PFLAG_GRAZESPAM) {
|
|
|
|
player_graze(col->entity, col->location, 10, 2);
|
|
|
|
} else {
|
|
|
|
player_graze(col->entity, col->location, 50, 5);
|
|
|
|
}
|
|
|
|
|
2017-12-28 10:05:54 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PCOL_ENEMY: {
|
|
|
|
Enemy *e = col->entity;
|
|
|
|
player_add_points(&global.plr, col->damage * 0.5);
|
|
|
|
|
|
|
|
#ifdef PLR_DPS_STATS
|
|
|
|
global.plr.total_dmg += min(e->hp, col->damage);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
e->hp -= col->damage;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PCOL_BOSS: {
|
|
|
|
if(boss_damage(col->entity, col->damage)) {
|
|
|
|
player_add_points(&global.plr, col->damage * 0.2);
|
2017-03-06 13:02:46 +01:00
|
|
|
|
2017-09-27 11:08:32 +02:00
|
|
|
#ifdef PLR_DPS_STATS
|
2017-12-28 10:05:54 +01:00
|
|
|
global.plr.total_dmg += col->damage;
|
2017-09-27 11:08:32 +02:00
|
|
|
#endif
|
|
|
|
}
|
2017-12-28 10:05:54 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case PCOL_VOID: {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default: {
|
|
|
|
log_fatal("Invalid collision type %x", col->type);
|
2011-04-08 18:59:03 +02:00
|
|
|
}
|
2010-10-17 10:39:07 +02:00
|
|
|
}
|
2017-12-28 10:05:54 +01:00
|
|
|
|
|
|
|
if(col->fatal) {
|
|
|
|
delete_projectile(projlist, p);
|
|
|
|
}
|
2010-10-17 09:27:44 +02:00
|
|
|
}
|
|
|
|
|
2018-04-13 21:13:48 +02:00
|
|
|
static void ent_draw_projectile(EntityInterface *ent) {
|
|
|
|
Projectile *proj = ENT_CAST(ent, Projectile);
|
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
// TODO: get rid of these legacy flags
|
2018-01-11 08:33:51 +01:00
|
|
|
|
|
|
|
if(proj->flags & PFLAG_DRAWADD) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_blend(BLEND_ADD);
|
2018-01-11 08:33:51 +01:00
|
|
|
} else if(proj->flags & PFLAG_DRAWSUB) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_blend(BLEND_SUB);
|
2018-01-11 08:33:51 +01:00
|
|
|
} else {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_blend(proj->blend);
|
2018-01-11 08:33:51 +01:00
|
|
|
}
|
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
r_shader_ptr(proj->shader);
|
2018-01-11 08:33:51 +01:00
|
|
|
|
2017-11-15 07:05:47 +01:00
|
|
|
#ifdef PROJ_DEBUG
|
2018-01-11 21:16:18 +01:00
|
|
|
if(proj->type == PlrProj) {
|
|
|
|
set_debug_info(&proj->debug);
|
|
|
|
log_fatal("Projectile with type PlrProj");
|
|
|
|
}
|
|
|
|
|
2017-11-15 07:05:47 +01:00
|
|
|
static Projectile prev_state;
|
|
|
|
memcpy(&prev_state, proj, sizeof(Projectile));
|
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
proj->draw_rule(proj, global.frames - proj->birthtime);
|
2017-10-24 04:57:14 +02:00
|
|
|
|
2017-11-15 07:05:47 +01:00
|
|
|
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);
|
2017-11-10 21:49:16 +01:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
bool projectile_in_viewport(Projectile *proj) {
|
2017-11-01 05:47:36 +01:00
|
|
|
double w, h;
|
2017-11-10 21:49:16 +01:00
|
|
|
int e = proj->max_viewport_dist;
|
2017-11-23 16:27:41 +01:00
|
|
|
projectile_size(proj, &w, &h);
|
2017-11-01 05:47:36 +01:00
|
|
|
|
|
|
|
return !(creal(proj->pos) + w/2 + e < 0 || creal(proj->pos) - w/2 - e > VIEWPORT_W
|
|
|
|
|| cimag(proj->pos) + h/2 + e < 0 || cimag(proj->pos) - h/2 - e > VIEWPORT_H);
|
2017-10-24 04:57:14 +02:00
|
|
|
}
|
|
|
|
|
2018-01-20 11:18:11 +01:00
|
|
|
Projectile* spawn_projectile_collision_effect(Projectile *proj) {
|
|
|
|
if(proj->flags & PFLAG_NOCOLLISIONEFFECT) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2018-02-06 07:19:25 +01:00
|
|
|
if(proj->sprite == NULL) {
|
2018-01-06 10:24:46 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return PARTICLE(
|
2018-02-06 07:19:25 +01:00
|
|
|
.sprite_ptr = proj->sprite,
|
2018-05-02 06:46:48 +02:00
|
|
|
.size = proj->size,
|
2018-01-06 10:24:46 +01:00
|
|
|
.pos = proj->pos,
|
|
|
|
.color = proj->color,
|
2018-01-20 11:18:11 +01:00
|
|
|
.flags = proj->flags | PFLAG_NOREFLECT,
|
2018-04-12 16:08:48 +02:00
|
|
|
.shader_ptr = proj->shader,
|
2018-05-02 06:46:48 +02:00
|
|
|
.rule = linear,
|
2018-01-20 11:18:11 +01:00
|
|
|
.draw_rule = DeathShrink,
|
2018-01-20 11:25:38 +01:00
|
|
|
.angle = proj->angle,
|
2018-05-02 06:46:48 +02:00
|
|
|
.args = { 5*cexp(I*proj->angle) },
|
|
|
|
.timeout = 10,
|
2018-01-06 10:24:46 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Projectile* spawn_projectile_clear_effect(Projectile *proj) {
|
|
|
|
if(proj->flags & PFLAG_NOCLEAREFFECT) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-01-20 11:18:11 +01:00
|
|
|
return PARTICLE(
|
2018-02-06 07:19:25 +01:00
|
|
|
.sprite_ptr = proj->sprite,
|
2018-05-02 06:46:48 +02:00
|
|
|
.size = proj->size,
|
2018-01-20 11:18:11 +01:00
|
|
|
.pos = proj->pos,
|
|
|
|
.color = proj->color,
|
2018-01-20 13:37:27 +01:00
|
|
|
.flags = proj->flags | PFLAG_NOREFLECT,
|
2018-04-12 16:08:48 +02:00
|
|
|
.shader_ptr = proj->shader,
|
2018-01-20 13:37:27 +01:00
|
|
|
.draw_rule = Shrink,
|
|
|
|
.angle = proj->angle,
|
2018-05-02 06:46:48 +02:00
|
|
|
.timeout = 10,
|
2018-01-20 13:37:27 +01:00
|
|
|
.args = { 10 },
|
2018-01-20 11:18:11 +01:00
|
|
|
);
|
2018-01-06 10:24:46 +01:00
|
|
|
}
|
|
|
|
|
2018-01-06 19:23:38 +01:00
|
|
|
bool clear_projectile(Projectile **projlist, Projectile *proj, bool force, bool now) {
|
2018-01-09 19:52:46 +01:00
|
|
|
if(proj->type >= PlrProj || (!force && !projectile_is_clearable(proj))) {
|
2018-01-06 10:24:46 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(now) {
|
2018-01-06 10:29:14 +01:00
|
|
|
if(!(proj->flags & PFLAG_NOCLEARBONUS)) {
|
|
|
|
create_bpoint(proj->pos);
|
|
|
|
}
|
|
|
|
|
2018-01-06 10:24:46 +01:00
|
|
|
spawn_projectile_clear_effect(proj);
|
2018-01-06 19:23:38 +01:00
|
|
|
delete_projectile(projlist, proj);
|
2018-01-06 10:24:46 +01:00
|
|
|
} else {
|
|
|
|
proj->type = DeadProj;
|
|
|
|
}
|
|
|
|
|
2018-01-06 19:23:38 +01:00
|
|
|
return true;
|
2018-01-06 10:24:46 +01:00
|
|
|
}
|
|
|
|
|
2017-02-11 04:52:08 +01:00
|
|
|
void process_projectiles(Projectile **projs, bool collision) {
|
2017-12-28 10:05:54 +01:00
|
|
|
ProjCollisionResult col = { 0 };
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-05-13 19:03:02 +02:00
|
|
|
char killed = 0;
|
|
|
|
int action;
|
2017-12-28 10:05:54 +01:00
|
|
|
|
|
|
|
for(Projectile *proj = *projs, *next; proj; proj = next) {
|
|
|
|
next = proj->next;
|
2018-05-02 06:46:48 +02:00
|
|
|
action = proj_call_rule(proj, global.frames - proj->birthtime);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-07-20 11:45:29 +02:00
|
|
|
if(proj->type == DeadProj && killed < 5) {
|
2011-05-13 19:03:02 +02:00
|
|
|
killed++;
|
|
|
|
action = ACTION_DESTROY;
|
2018-01-06 19:23:38 +01:00
|
|
|
|
|
|
|
if(clear_projectile(projs, proj, true, true)) {
|
|
|
|
continue;
|
|
|
|
}
|
2011-05-13 19:03:02 +02:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-12-28 10:05:54 +01:00
|
|
|
if(collision) {
|
|
|
|
calc_projectile_collision(proj, &col);
|
|
|
|
|
2017-12-31 10:23:57 +01:00
|
|
|
if(col.fatal && col.type != PCOL_VOID) {
|
2018-01-06 10:24:46 +01:00
|
|
|
spawn_projectile_collision_effect(proj);
|
2017-12-28 10:05:54 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
memset(&col, 0, sizeof(col));
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-12-28 10:05:54 +01:00
|
|
|
if(!projectile_in_viewport(proj)) {
|
|
|
|
col.fatal = true;
|
|
|
|
}
|
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-12-28 10:05:54 +01:00
|
|
|
if(action == ACTION_DESTROY) {
|
|
|
|
col.fatal = true;
|
2010-10-17 10:39:07 +02:00
|
|
|
}
|
2017-12-28 10:05:54 +01:00
|
|
|
|
|
|
|
apply_projectile_collision(projs, proj, &col);
|
2010-10-27 19:51:49 +02:00
|
|
|
}
|
2010-10-12 10:55:23 +02:00
|
|
|
}
|
|
|
|
|
2017-12-28 11:40:40 +01:00
|
|
|
int trace_projectile(Projectile *p, ProjCollisionResult *out_col, ProjCollisionType stopflags, int timeofs) {
|
|
|
|
int t;
|
|
|
|
|
|
|
|
for(t = timeofs; p; ++t) {
|
2017-11-01 05:47:36 +01:00
|
|
|
int action = p->rule(p, t);
|
2017-12-28 10:05:54 +01:00
|
|
|
calc_projectile_collision(p, out_col);
|
2017-11-01 05:47:36 +01:00
|
|
|
|
2017-12-28 10:05:54 +01:00
|
|
|
if(out_col->type & stopflags || action == ACTION_DESTROY) {
|
2017-12-28 11:40:40 +01:00
|
|
|
return t;
|
2017-11-01 05:47:36 +01:00
|
|
|
}
|
|
|
|
}
|
2017-12-28 11:40:40 +01:00
|
|
|
|
|
|
|
return t;
|
2017-11-01 05:47:36 +01:00
|
|
|
}
|
2011-05-21 15:02:19 +02:00
|
|
|
|
2018-01-06 09:54:13 +01:00
|
|
|
bool projectile_is_clearable(Projectile *p) {
|
|
|
|
if(p->type == DeadProj) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(p->type == EnemyProj || p->type == FakeProj) {
|
|
|
|
return (p->flags & PFLAG_NOCLEAR) != PFLAG_NOCLEAR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-07-07 14:34:30 +02:00
|
|
|
int linear(Projectile *p, int t) { // sure is physics in here; a[0]: velocity
|
|
|
|
if(t < 0)
|
|
|
|
return 1;
|
2011-05-06 17:09:43 +02:00
|
|
|
p->angle = carg(p->args[0]);
|
|
|
|
p->pos = p->pos0 + p->args[0]*t;
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-05-13 19:03:02 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-06-27 13:36:35 +02:00
|
|
|
int accelerated(Projectile *p, int t) {
|
2011-07-07 14:34:30 +02:00
|
|
|
if(t < 0)
|
|
|
|
return 1;
|
2012-07-31 14:34:20 +02:00
|
|
|
p->angle = carg(p->args[0]);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2012-07-31 14:34:20 +02:00
|
|
|
p->pos += p->args[0];
|
|
|
|
p->args[0] += p->args[1];
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-06-27 13:36:35 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2011-06-28 19:23:02 +02:00
|
|
|
int asymptotic(Projectile *p, int t) { // v = a[0]*(a[1] + 1); a[1] -> 0
|
2011-07-07 14:34:30 +02:00
|
|
|
if(t < 0)
|
|
|
|
return 1;
|
2011-08-27 15:56:02 +02:00
|
|
|
p->angle = carg(p->args[0]);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-06-28 19:23:02 +02:00
|
|
|
p->args[1] *= 0.8;
|
|
|
|
p->pos += p->args[0]*(p->args[1] + 1);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2011-06-28 19:23:02 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
static inline void apply_common_transforms(Projectile *proj, int t) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_translate(creal(proj->pos), cimag(proj->pos), 0);
|
|
|
|
r_mat_rotate_deg(proj->angle*180/M_PI+90, 0, 0, 1);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
if(t >= 16) {
|
|
|
|
return;
|
2012-04-06 15:54:30 +02:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
if(proj->flags & PFLAG_NOSPAWNZOOM) {
|
|
|
|
return;
|
|
|
|
}
|
2017-04-20 18:37:46 +02:00
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
if(proj->type != EnemyProj && proj->type != FakeProj) {
|
|
|
|
return;
|
|
|
|
}
|
2017-04-20 18:37:46 +02:00
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
float s = 2.0-t/16.0;
|
|
|
|
if(s != 1) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_scale(s, s, 1);
|
2017-11-10 21:49:16 +01:00
|
|
|
}
|
2012-07-18 23:12:31 +02:00
|
|
|
}
|
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
void ProjDrawCore(Projectile *proj, Color c) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_draw_sprite(&(SpriteParams) {
|
|
|
|
.sprite_ptr = proj->sprite,
|
|
|
|
.color = c,
|
|
|
|
.custom = proj->shader_custom_param,
|
|
|
|
});
|
2017-11-10 21:49:16 +01:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
void ProjDraw(Projectile *proj, int t) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_push();
|
2017-11-10 21:49:16 +01:00
|
|
|
apply_common_transforms(proj, t);
|
|
|
|
ProjDrawCore(proj, proj->color);
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_pop();
|
2011-06-29 17:01:03 +02:00
|
|
|
}
|
2011-11-02 20:00:14 +01:00
|
|
|
|
2017-10-18 01:53:42 +02:00
|
|
|
void ProjNoDraw(Projectile *proj, int t) {
|
|
|
|
}
|
|
|
|
|
2017-11-15 07:05:47 +01:00
|
|
|
void Blast(Projectile *p, int t) {
|
2018-04-12 16:08:48 +02:00
|
|
|
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]));
|
2017-11-10 21:49:16 +01:00
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
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);
|
2017-11-10 21:49:16 +01:00
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
draw_sprite_batched_p(0,0,p->sprite);
|
|
|
|
r_mat_scale(0.5+creal(p->args[2]),0.5+creal(p->args[2]),1);
|
|
|
|
r_blend(BLEND_ADD);
|
|
|
|
draw_sprite_batched_p(0,0,p->sprite);
|
|
|
|
r_mat_pop();
|
2011-06-28 19:23:02 +02:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
|
|
|
void Shrink(Projectile *p, int t) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_push();
|
2017-11-10 21:49:16 +01:00
|
|
|
apply_common_transforms(p, t);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
float s = 2.0-t/(double)p->timeout*2;
|
2017-11-10 21:49:16 +01:00
|
|
|
if(s != 1) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_scale(s, s, 1);
|
2017-11-10 21:49:16 +01:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
ProjDrawCore(p, p->color);
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_pop();
|
2011-05-21 15:02:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void DeathShrink(Projectile *p, int t) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_push();
|
2017-11-10 21:49:16 +01:00
|
|
|
apply_common_transforms(p, t);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
float s = 2.0-t/(double)p->timeout*2;
|
2017-11-10 21:49:16 +01:00
|
|
|
if(s != 1) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_scale(s, 1, 1);
|
2017-11-10 21:49:16 +01:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
ProjDrawCore(p, p->color);
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_pop();
|
2011-05-13 19:03:02 +02:00
|
|
|
}
|
|
|
|
|
2011-06-28 19:23:02 +02:00
|
|
|
void GrowFade(Projectile *p, int t) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_push();
|
2017-11-10 21:49:16 +01:00
|
|
|
apply_common_transforms(p, t);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
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]));
|
2017-11-10 21:49:16 +01:00
|
|
|
if(s != 1) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_scale(s, s, 1);
|
2017-11-10 21:49:16 +01:00
|
|
|
}
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
ProjDrawCore(p, multiply_colors(p->color, rgba(1, 1, 1, 1 - t/(double)p->timeout)));
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_pop();
|
2017-02-10 23:05:22 +01:00
|
|
|
}
|
2011-06-28 19:23:02 +02:00
|
|
|
|
2011-05-13 19:03:02 +02:00
|
|
|
void Fade(Projectile *p, int t) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_push();
|
2017-11-10 21:49:16 +01:00
|
|
|
apply_common_transforms(p, t);
|
2018-05-02 06:46:48 +02:00
|
|
|
ProjDrawCore(p, multiply_colors(p->color, rgba(1, 1, 1, 1 - t/(double)p->timeout)));
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_pop();
|
2011-05-13 19:03:02 +02:00
|
|
|
}
|
|
|
|
|
2017-11-10 21:49:16 +01:00
|
|
|
void ScaleFade(Projectile *p, int t) {
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_push();
|
2017-11-10 21:49:16 +01:00
|
|
|
apply_common_transforms(p, t);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2018-01-08 17:56:46 +01:00
|
|
|
double scale_min = creal(p->args[2]);
|
|
|
|
double scale_max = cimag(p->args[2]);
|
2018-05-02 06:46:48 +02:00
|
|
|
double timefactor = t / (double)p->timeout;
|
2018-01-08 17:56:46 +01:00
|
|
|
double scale = scale_min * (1 - timefactor) + scale_max * timefactor;
|
|
|
|
double alpha = pow(1 - timefactor, 2);
|
|
|
|
|
|
|
|
// log_debug("%f %f %f %f", scale_min, scale_max, timefactor, scale);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2018-01-08 17:56:46 +01:00
|
|
|
Color c = multiply_colors(p->color, rgba(1, 1, 1, alpha));
|
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_scale(scale, scale, 1);
|
2017-11-10 21:49:16 +01:00
|
|
|
ProjDrawCore(p, c);
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_pop();
|
2012-08-01 17:32:11 +02:00
|
|
|
}
|
|
|
|
|
2011-08-28 10:57:45 +02:00
|
|
|
void Petal(Projectile *p, int t) {
|
|
|
|
float x = creal(p->args[2]);
|
|
|
|
float y = cimag(p->args[2]);
|
|
|
|
float z = creal(p->args[3]);
|
2017-02-10 23:05:22 +01:00
|
|
|
|
2017-03-24 14:53:57 +01:00
|
|
|
float r = sqrt(x*x+y*y+z*z);
|
|
|
|
x /= r; y /= r; z /= r;
|
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
r_disable(RCAP_CULL_FACE);
|
|
|
|
r_mat_push();
|
|
|
|
r_mat_translate(creal(p->pos), cimag(p->pos),0);
|
|
|
|
r_mat_rotate_deg(t*4.0 + cimag(p->args[3]), x, y, z);
|
2017-11-10 21:49:16 +01:00
|
|
|
ProjDrawCore(p, p->color);
|
2018-04-12 16:08:48 +02:00
|
|
|
r_mat_pop();
|
2011-08-28 10:57:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void petal_explosion(int n, complex pos) {
|
2017-11-21 10:26:11 +01:00
|
|
|
for(int i = 0; i < n; i++) {
|
2017-03-24 14:53:57 +01:00
|
|
|
tsrand_fill(6);
|
|
|
|
float t = frand();
|
|
|
|
Color c = rgba(sin(5*t),cos(5*t),0.5,t);
|
2017-11-10 21:49:16 +01:00
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
PARTICLE(
|
|
|
|
.sprite = "petal",
|
|
|
|
.pos = pos,
|
|
|
|
.color = c,
|
|
|
|
.rule = asymptotic,
|
2017-11-10 21:49:16 +01:00
|
|
|
.draw_rule = Petal,
|
|
|
|
.args = {
|
|
|
|
(3+5*afrand(2))*cexp(I*M_PI*2*afrand(3)),
|
|
|
|
5,
|
2017-11-21 10:26:11 +01:00
|
|
|
afrand(4) + afrand(5)*I,
|
|
|
|
afrand(1) + 360.0*I*afrand(0),
|
2017-11-10 21:49:16 +01:00
|
|
|
},
|
2018-04-12 16:08:48 +02:00
|
|
|
// TODO: maybe remove this noreflect, there shouldn't be a cull mode mess anymore
|
|
|
|
.flags = PFLAG_NOREFLECT,
|
|
|
|
.blend = BLEND_ADD,
|
2017-11-10 21:49:16 +01:00
|
|
|
);
|
2011-08-28 10:57:45 +02:00
|
|
|
}
|
2012-07-18 12:33:37 +02:00
|
|
|
}
|
2017-03-11 04:41:57 +01:00
|
|
|
|
|
|
|
void projectiles_preload(void) {
|
2018-02-06 07:19:25 +01:00
|
|
|
// XXX: maybe split this up into stage-specific preloads too?
|
|
|
|
// some of these are ubiquitous, but some only appear in very specific parts.
|
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
preload_resources(RES_SHADER_PROGRAM, RESF_PERMANENT,
|
|
|
|
defaults_proj.shader,
|
|
|
|
defaults_part.shader,
|
|
|
|
NULL);
|
|
|
|
|
2017-03-11 04:41:57 +01:00
|
|
|
preload_resources(RES_TEXTURE, RESF_PERMANENT,
|
2018-02-06 07:19:25 +01:00
|
|
|
"part/lasercurve",
|
|
|
|
NULL);
|
2017-03-20 07:38:07 +01:00
|
|
|
|
2018-02-06 07:19:25 +01:00
|
|
|
preload_resources(RES_SPRITE, RESF_PERMANENT,
|
2017-03-11 04:41:57 +01:00
|
|
|
"part/blast",
|
|
|
|
"part/flare",
|
|
|
|
"part/petal",
|
|
|
|
"part/smoke",
|
|
|
|
"part/stain",
|
2017-03-20 07:38:07 +01:00
|
|
|
"part/lightning0",
|
|
|
|
"part/lightning1",
|
|
|
|
"part/lightningball",
|
2018-02-06 07:19:25 +01:00
|
|
|
"part/smoothdot",
|
2017-03-11 04:41:57 +01:00
|
|
|
NULL);
|
|
|
|
|
2017-09-30 20:11:10 +02:00
|
|
|
preload_resources(RES_SFX, RESF_PERMANENT,
|
|
|
|
"shot1",
|
2017-10-29 04:43:32 +01:00
|
|
|
"shot2",
|
2017-09-30 20:11:10 +02:00
|
|
|
"shot1_loop",
|
2017-10-22 21:11:15 +02:00
|
|
|
"shot_special1",
|
|
|
|
"redirect",
|
2017-09-30 20:11:10 +02:00
|
|
|
NULL);
|
2018-04-12 16:08:48 +02:00
|
|
|
|
2018-05-02 06:46:48 +02:00
|
|
|
#define PP(name) (_pp_##name).preload(&_pp_##name);
|
|
|
|
#include "projectile_prototypes/all.inc.h"
|
|
|
|
|
2018-04-12 16:08:48 +02:00
|
|
|
defaults_proj.shader_ptr = r_shader_get(defaults_proj.shader);
|
|
|
|
defaults_part.shader_ptr = r_shader_get(defaults_part.shader);
|
2017-03-11 04:41:57 +01:00
|
|
|
}
|