317 lines
8.2 KiB
C
317 lines
8.2 KiB
C
#include <SDL.h>
|
|
|
|
#include "lephisto.h"
|
|
#include "log.h"
|
|
#include "pilot.h"
|
|
#include "physics.h"
|
|
#include "opengl.h"
|
|
#include "pause.h"
|
|
#include "rng.h"
|
|
#include "spfx.h"
|
|
|
|
#define SPFX_GFX "../gfx/spfx/" /* Graphics location. */
|
|
#define SPFX_CHUNK 32 /* Chunk to allocate when needed. */
|
|
|
|
/* Special hardcoded effects.. */
|
|
|
|
/* Shake, AKA RUMBLE! */
|
|
static double shake_rad = 0.;
|
|
Vec2 shake_pos = { .x = 0., .y = 0. }; /* Used in nebulae.c */
|
|
static Vec2 shake_vel = { .x = 0., .y = 0. };
|
|
static int shake_off = 1;
|
|
|
|
/* Generic SPFX template. */
|
|
typedef struct SPFX_Base_ {
|
|
char* name;
|
|
|
|
double ttl; /* Time to live. */
|
|
double anim; /* Total duration in ms. */
|
|
|
|
glTexture* gfx; /* Will use each sprite as a frame. */
|
|
} SPFX_Base;
|
|
|
|
static SPFX_Base* spfx_effects = NULL;
|
|
static int spfx_neffects = 0;
|
|
|
|
typedef struct SPFX_ {
|
|
Vec2 pos, vel; /* They don't accelerate. */
|
|
|
|
int lastframe; /* Need when pausing. */
|
|
int effect; /* Actual effect. */
|
|
double timer; /* Time left. */
|
|
} SPFX;
|
|
|
|
/* Front stack is for effects on player. */
|
|
/* Back is for everything else. */
|
|
static SPFX* spfx_stack_front = NULL;
|
|
static int spfx_nstack_front = 0;
|
|
static int spfx_mstack_front = 0;
|
|
static SPFX* spfx_stack_back = NULL;
|
|
static int spfx_nstack_back = 0;
|
|
static int spfx_mstack_back = 0;
|
|
|
|
static int spfx_base_load(char* name, int ttl, int anim, char* gfx, int sx, int sy);
|
|
static void spfx_base_free(SPFX_Base* effect);
|
|
static void spfx_destroy(SPFX* layer, int* nlayer, int spfx);
|
|
static void spfx_update_layer(SPFX* layer, int* nlayer, const double dt);
|
|
|
|
/* Load the SPFX_Base. */
|
|
static int spfx_base_load(char* name, int ttl, int anim, char* gfx, int sx, int sy) {
|
|
SPFX_Base* cur;
|
|
char buf[PATH_MAX];
|
|
|
|
/* Create new effect. */
|
|
spfx_effects = realloc(spfx_effects, ++spfx_neffects*sizeof(SPFX_Base));
|
|
cur = &spfx_effects[spfx_neffects-1];
|
|
|
|
/* Fill it with the data. */
|
|
cur->name = strdup(name);
|
|
cur->anim = (double)anim / 1000.;
|
|
cur->ttl = (double)ttl / 1000.;
|
|
sprintf(buf, SPFX_GFX"%s", gfx);
|
|
cur->gfx = gl_newSprite(buf, sx, sy);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void spfx_base_free(SPFX_Base* effect) {
|
|
if(effect->name != NULL) free(effect->name);
|
|
if(effect->gfx != NULL) gl_freeTexture(effect->gfx);
|
|
}
|
|
|
|
int spfx_get(char* name) {
|
|
int i;
|
|
for(i = 0; i < spfx_neffects; i++)
|
|
if(strcmp(spfx_effects[i].name, name)==0)
|
|
return i;
|
|
WARN("SPFX '%s' not found!", name);
|
|
return 0;
|
|
}
|
|
|
|
/* Load/Unload.
|
|
* TODO: make it customizable?
|
|
*/
|
|
int spfx_load(void) {
|
|
/* Standard explosion effects. */
|
|
spfx_base_load("ExpS", 400, 400, "exps.png", 6, 5);
|
|
spfx_base_load("ExpM", 450, 450, "expm.png", 6, 5);
|
|
spfx_base_load("ExpL", 500, 500, "expl.png", 6, 5);
|
|
spfx_base_load("cargo", 15000, 5000, "cargo.png", 6, 6);
|
|
return 0;
|
|
}
|
|
|
|
void spfx_free(void) {
|
|
int i;
|
|
|
|
/* Get rid of all the particles and free the stacks. */
|
|
spfx_clear();
|
|
if(spfx_stack_front) free(spfx_stack_front);
|
|
spfx_stack_front = NULL;
|
|
spfx_mstack_front = 0;
|
|
if(spfx_stack_back) free(spfx_stack_back);
|
|
spfx_stack_back = NULL;
|
|
spfx_mstack_back = 0;
|
|
|
|
for(i = 0; i < spfx_neffects; i++)
|
|
spfx_base_free(&spfx_effects[i]);
|
|
free(spfx_effects);
|
|
spfx_effects = NULL;
|
|
spfx_neffects = 0;
|
|
}
|
|
|
|
void spfx_add(int effect,
|
|
const double px, const double py,
|
|
const double vx, const double vy,
|
|
const int layer) {
|
|
|
|
SPFX* cur_spfx;
|
|
double ttl, anim;
|
|
|
|
/* Select the layer. */
|
|
if(layer == SPFX_LAYER_FRONT) {
|
|
/* Front layer. */
|
|
if(spfx_mstack_front < spfx_nstack_front+1) {
|
|
/* We need more memory. */
|
|
spfx_mstack_front += SPFX_CHUNK;
|
|
spfx_stack_front = realloc(spfx_stack_front, spfx_mstack_front*sizeof(SPFX));
|
|
}
|
|
cur_spfx = &spfx_stack_front[spfx_nstack_front];
|
|
spfx_nstack_front++;
|
|
}
|
|
else if(layer == SPFX_LAYER_BACK) {
|
|
/* Back layer. */
|
|
if(spfx_mstack_back < spfx_nstack_back+1) {
|
|
/* Need more memory. */
|
|
spfx_mstack_back += SPFX_CHUNK;
|
|
spfx_stack_back = realloc(spfx_stack_back, spfx_mstack_back*sizeof(SPFX));
|
|
}
|
|
cur_spfx = &spfx_stack_back[spfx_nstack_back];
|
|
spfx_nstack_back++;
|
|
}
|
|
|
|
/* The actualy adding of the spfx. */
|
|
cur_spfx->effect = effect;
|
|
vect_csetmin(&cur_spfx->pos, px, py);
|
|
vect_csetmin(&cur_spfx->vel, vx, vy);
|
|
/* timer magic if ttl != anim. */
|
|
ttl = spfx_effects[effect].ttl;
|
|
anim = spfx_effects[effect].anim;
|
|
if(ttl != anim)
|
|
cur_spfx->timer = ttl + RNGF()*anim;
|
|
else
|
|
cur_spfx->timer = ttl;
|
|
}
|
|
|
|
void spfx_clear(void) {
|
|
int i;
|
|
|
|
/* Clear front layer. */
|
|
for(i = spfx_nstack_front-1; i >= 0; i--)
|
|
spfx_destroy(spfx_stack_front, &spfx_nstack_front, i);
|
|
|
|
/* Clear back layer. */
|
|
for(i = spfx_nstack_back-1; i >= 0; i--)
|
|
spfx_destroy(spfx_stack_back, &spfx_nstack_back, i);
|
|
|
|
/* Clear rumble. */
|
|
shake_rad = 0;
|
|
}
|
|
|
|
static void spfx_destroy(SPFX* layer, int* nlayer, int spfx) {
|
|
(*nlayer)--;
|
|
memmove(&layer[spfx], &layer[spfx+1], (*nlayer-spfx)*sizeof(SPFX));
|
|
}
|
|
|
|
void spfx_update(const double dt) {
|
|
spfx_update_layer(spfx_stack_front, &spfx_nstack_front, dt);
|
|
spfx_update_layer(spfx_stack_back, &spfx_nstack_back, dt);
|
|
}
|
|
|
|
static void spfx_update_layer(SPFX* layer, int* nlayer, const double dt) {
|
|
int i;
|
|
|
|
for(i = 0; i < *nlayer; i++) {
|
|
layer[i].timer -= dt; /* Less time to live. */
|
|
|
|
/* Time to die!!! */
|
|
if(layer[i].timer < 0.) {
|
|
spfx_destroy(layer, nlayer, i);
|
|
i--;
|
|
continue;
|
|
}
|
|
/* Mkay. Update it now. */
|
|
vect_cadd(&layer[i].pos, dt*VX(layer[i].vel), dt*VY(layer[i].vel));
|
|
}
|
|
}
|
|
|
|
/* Prepare the rendering for special affects. */
|
|
void spfx_start(double dt) {
|
|
GLdouble bx, by, x, y;
|
|
double inc;
|
|
|
|
if(shake_off == 1) return; /* Save the cycles. */
|
|
bx = SCREEN_W / 2;
|
|
by = SCREEN_H / 2;
|
|
|
|
if(!paused) {
|
|
inc = dt*100000.;
|
|
|
|
/* Calculate new position. */
|
|
if(shake_rad > 0.01) {
|
|
vect_cadd(&shake_pos, shake_vel.x * inc, shake_vel.y * inc);
|
|
|
|
if(VMOD(shake_pos) > shake_rad) {
|
|
/* Change direction. */
|
|
vect_pset(&shake_pos, shake_rad, VANGLE(shake_pos));
|
|
vect_pset(&shake_vel, shake_rad,
|
|
-VANGLE(shake_pos) + (RNGF()-0.5) * M_PI);
|
|
}
|
|
/* The shake decays over time. */
|
|
shake_rad -= SHAKE_DECAY * dt;
|
|
if(shake_rad < 0.) shake_rad = 0.;
|
|
|
|
x = shake_pos.x;
|
|
y = shake_pos.y;
|
|
} else {
|
|
shake_rad = 0.;
|
|
shake_off = 1;
|
|
x = 0.;
|
|
y = 0.;
|
|
}
|
|
}
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(-bx+x, bx+x, -by+y, by+y, -1., 1.);
|
|
}
|
|
|
|
/* Add ruuumble!! */
|
|
void spfx_shake(double mod) {
|
|
shake_rad += mod/5.;
|
|
if(shake_rad > SHAKE_MAX) shake_rad = SHAKE_MAX;
|
|
shake_off = 0;
|
|
|
|
vect_pset(&shake_vel, shake_rad, RNGF() * 2. * M_PI);
|
|
}
|
|
|
|
void spfx_cinematic(void) {
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPushMatrix(); /* Translation matrix. */
|
|
glTranslated(-(double)SCREEN_W/2., -(double)SCREEN_H/2., 0);
|
|
|
|
COLOUR(cBlack);
|
|
glBegin(GL_QUADS);
|
|
glVertex2d(0., 0.);
|
|
glVertex2d(0., SCREEN_H*0.2);
|
|
glVertex2d(SCREEN_W, SCREEN_H*0.2);
|
|
glVertex2d(SCREEN_W, 0.);
|
|
glVertex2d(0., SCREEN_H);
|
|
glVertex2d(SCREEN_W, SCREEN_H);
|
|
glVertex2d(SCREEN_W, SCREEN_H*0.8);
|
|
glVertex2d(0., SCREEN_H*0.8);
|
|
glEnd();
|
|
|
|
glPopMatrix(); /* Translation matrix. */
|
|
}
|
|
|
|
void spfx_render(const int layer) {
|
|
SPFX* spfx_stack;
|
|
int i, spfx_nstack;
|
|
SPFX_Base* effect;
|
|
int sx, sy;
|
|
double time;
|
|
|
|
/* Get the appropriate layer. */
|
|
switch(layer) {
|
|
case SPFX_LAYER_FRONT:
|
|
spfx_stack = spfx_stack_front;
|
|
spfx_nstack = spfx_nstack_front;
|
|
break;
|
|
case SPFX_LAYER_BACK:
|
|
spfx_stack = spfx_stack_back;
|
|
spfx_nstack = spfx_nstack_back;
|
|
break;
|
|
}
|
|
|
|
/* Now render the layer. */
|
|
for(i = 0; i < spfx_nstack; i++) {
|
|
effect = &spfx_effects[spfx_stack[i].effect];
|
|
|
|
/* Simplifies. */
|
|
sx = (int)effect->gfx->sx;
|
|
sy = (int)effect->gfx->sy;
|
|
|
|
if(!paused) { /* Don't calculate frame if paused. */
|
|
time = fmod(spfx_stack[i].timer, effect->anim) / effect->anim;
|
|
spfx_stack[i].lastframe = sx * sy * MIN(time, 1.);
|
|
}
|
|
|
|
/* Render. */
|
|
gl_blitSprite(effect->gfx,
|
|
VX(spfx_stack[i].pos), VY(spfx_stack[i].pos),
|
|
spfx_stack[i].lastframe % sx,
|
|
spfx_stack[i].lastframe / sx,
|
|
NULL);
|
|
}
|
|
}
|
|
|