warzone2100/src/effects.cpp

2604 lines
68 KiB
C++

/*
This file is part of Warzone 2100.
Copyright (C) 1999-2004 Eidos Interactive
Copyright (C) 2005-2013 Warzone 2100 Project
Warzone 2100 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
Warzone 2100 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Warzone 2100; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
Spot FX code - will handle every miscellaneous imd render and update for temporary
entities except projectiles.
Handles stuff like
- Smoke sprites on the card.
- Explosions
- Building body kit - flashing lights etc etc
- Construction graphics
- Gravitons
- Dust
- Blood
It's now PSX friendly in that there's no floats
************************************************************
* STILL NEED TO REMOVE SOME MAGIC NUMBERS INTO #DEFINES!!! *
************************************************************
*/
#include "lib/framework/wzapp.h"
#include "lib/framework/wzconfig.h"
#include "lib/framework/frameresource.h"
#include "lib/framework/input.h"
#include "lib/framework/math_ext.h"
#include "lib/ivis_opengl/ivisdef.h" //ivis matrix code
#include "lib/ivis_opengl/piedef.h" //ivis matrix code
#include "lib/framework/fixedpoint.h"
#include "lib/ivis_opengl/piepalette.h"
#include "lib/ivis_opengl/piestate.h"
#include "lib/ivis_opengl/piematrix.h"
#include "lib/ivis_opengl/piemode.h"
#include "lib/gamelib/gtime.h"
#include "lib/sound/audio.h"
#include "lib/sound/audio_id.h"
#include "display3d.h"
#include "map.h"
#include "bucket3d.h"
#include "effects.h"
#include "mission.h"
#include "miscimd.h"
#include "hci.h"
#include "lighting.h"
#include "console.h"
#include "loop.h"
#include "multiplay.h"
#include "game.h"
#include "component.h"
#define GRAVITON_GRAVITY ((float)-800)
#define EFFECT_X_FLIP 0x1
#define EFFECT_Y_FLIP 0x2
#define EFFECT_CYCLIC 0x4
#define EFFECT_ESSENTIAL 0x8
#define EFFECT_FACING 0x10
#define EFFECT_SCALED 0x20
#define EFFECT_LIT 0x40
#define TEST_FLIPPED_X(x) (x->control & EFFECT_X_FLIP)
#define TEST_FLIPPED_Y(x) (x->control & EFFECT_Y_FLIP)
#define TEST_ESSENTIAL(x) (x->control & EFFECT_ESSENTIAL)
#define TEST_FACING(x) (x->control & EFFECT_FACING)
#define TEST_CYCLIC(x) (x->control & EFFECT_CYCLIC)
#define TEST_SCALED(x) (x->control & EFFECT_SCALED)
#define TEST_LIT(x) (x->control & EFFECT_LIT)
#define SET_FLIPPED_X(x) ((x->control) = (UBYTE)(x->control | EFFECT_X_FLIP))
#define SET_FLIPPED_Y(x) ((x->control) = (UBYTE)(x->control | EFFECT_Y_FLIP))
#define SET_ESSENTIAL(x) ((x->control) = (UBYTE)(x->control | EFFECT_ESSENTIAL))
#define SET_FACING(x) ((x->control) = (UBYTE)(x->control | EFFECT_FACING))
#define SET_CYCLIC(x) ((x->control) = (UBYTE)(x->control | EFFECT_CYCLIC))
#define SET_SCALED(x) ((x->control) = (UBYTE)(x->control | EFFECT_SCALED))
#define SET_LIT(x) ((x->control) = (UBYTE)(x->control | EFFECT_LIT))
#define NORMAL_SMOKE_LIFESPAN (6000 + rand()%3000)
#define SMALL_SMOKE_LIFESPAN (3000 + rand()%3000)
#define TRAIL_SMOKE_LIFESPAN (1200)
#define CONSTRUCTION_LIFESPAN (5000)
#define SMOKE_FRAME_DELAY (40 + rand()%30)
#define EXPLOSION_FRAME_DELAY (25 + rand()%40)
#define EXPLOSION_TESLA_FRAME_DELAY (65)
#define EXPLOSION_PLASMA_FRAME_DELAY (45)
#define BLOOD_FRAME_DELAY (150)
#define DESTRUCTION_FRAME_DELAY (200)
#define TESLA_SPEED (170)// + (30 - rand()%60))
#define TESLA_SIZE (100)// + (20 - rand()%40))
#define GRAVITON_FRAME_DELAY (100 + rand()%50)
#define GRAVITON_BLOOD_DELAY (200 + rand()%100)
#define CONSTRUCTION_FRAME_DELAY (40 + rand()%30)
#define EXPLOSION_SIZE (110+(30-rand()%60))
#define BLOOD_SIZE (100+(30-rand()%60))
#define BLOOD_FALL_SPEED (-(20+rand()%20))
#define GRAVITON_INIT_VEL_X (float)(200 - rand() % 300)
#define GRAVITON_INIT_VEL_Z (float)(200 - rand() % 300)
#define GRAVITON_INIT_VEL_Y (float)(300 + rand() % 100)
#define GIBLET_INIT_VEL_X (float)(50 - rand() % 100)
#define GIBLET_INIT_VEL_Z (float)(50 - rand() % 100)
#define GIBLET_INIT_VEL_Y 12.f
#define DROID_DESTRUCTION_DURATION (3*GAME_TICKS_PER_SEC/2) // 1.5 seconds
#define STRUCTURE_DESTRUCTION_DURATION ((7*GAME_TICKS_PER_SEC)/2) // 3.5 seconds
#define EFFECT_EXPLOSION_ADDITIVE 164
#define EFFECT_PLASMA_ADDITIVE 224
#define EFFECT_SMOKE_TRANSPARENCY 130
#define EFFECT_STEAM_TRANSPARENCY 128
#define EFFECT_BLOOD_TRANSPARENCY 128
#define EFFECT_DROID_DIVISION 101
#define EFFECT_STRUCTURE_DIVISION 103
#define DROID_UPDATE_INTERVAL 500
#define STRUCTURE_UPDATE_INTERVAL 1250
#define BASE_FLAME_SIZE 80
#define BASE_LASER_SIZE 10
#define BASE_PLASMA_SIZE 0
#define DISCOVERY_SIZE 60
#define FLARE_SIZE 100
#define SHOCKWAVE_SPEED (GAME_TICKS_PER_SEC)
#define MAX_SHOCKWAVE_SIZE 500
/*! Number of effects in one chunk */
#define EFFECT_CHUNK_SIZE 10000
/*! A memory chunk of effects */
struct EffectChunk
{
EFFECT effects[EFFECT_CHUNK_SIZE]; //!< Chunk of effects
EffectChunk *next; //!< Next element in list
};
/*! List containing all allocated effect chunks */
struct chunkList_t {
EffectChunk *first; //!< First element of list, used for iteration
EffectChunk *last; //!< Last element of list, used for appending
} chunkList = {
NULL, NULL
};
/*! Our lists of all game world effects */
struct activeList_t {
size_t num; //!< Number of effects in this list, used when saving
EFFECT *first; //!< First element of list, used for iteration / finding free effects
EFFECT *last; //!< Last element of list, used for appending
} activeList = { //!< List of all active effects
0, NULL, NULL
}, inactiveList = { //!< List of unused effects
0, NULL, NULL
};
/* Tick counts for updates on a particular interval */
static UDWORD lastUpdateDroids[EFFECT_DROID_DIVISION];
static UDWORD lastUpdateStructures[EFFECT_STRUCTURE_DIVISION];
static UDWORD auxVar; // dirty filthy hack - don't look for what this does.... //FIXME
static UDWORD auxVarSec; // dirty filthy hack - don't look for what this does.... //FIXME
static UDWORD specifiedSize;
static UDWORD ellSpec;
static uint8_t EffectForPlayer = 0;
// ----------------------------------------------------------------------------------------
/* PROTOTYPES */
// ----------------------------------------------------------------------------------------
// ---- Update functions - every group type of effect has one of these */
static void updateWaypoint(EFFECT *psEffect);
static void updateExplosion(EFFECT *psEffect);
static void updatePolySmoke(EFFECT *psEffect);
static void updateGraviton(EFFECT *psEffect);
static void updateConstruction(EFFECT *psEffect);
static void updateBlood (EFFECT *psEffect);
static void updateDestruction(EFFECT *psEffect);
static void updateFire(EFFECT *psEffect);
static void updateSatLaser(EFFECT *psEffect);
static void updateFirework(EFFECT *psEffect);
static void updateEffect(EFFECT *psEffect); // MASTER function
// ----------------------------------------------------------------------------------------
// ---- The render functions - every group type of effect has a distinct one
static void renderExplosionEffect ( const EFFECT *psEffect );
static void renderSmokeEffect ( const EFFECT *psEffect );
static void renderGravitonEffect ( const EFFECT *psEffect );
static void renderConstructionEffect( const EFFECT *psEffect );
static void renderWaypointEffect ( const EFFECT *psEffect );
static void renderBloodEffect ( const EFFECT *psEffect );
static void renderDestructionEffect ( const EFFECT *psEffect );
static void renderFirework ( const EFFECT *psEffect );
static void positionEffect(const EFFECT *psEffect);
/* There is no render destruction effect! */
// ----------------------------------------------------------------------------------------
// ---- The set up functions - every type has one
static void effectSetupSmoke ( EFFECT *psEffect );
static void effectSetupGraviton ( EFFECT *psEffect );
static void effectSetupExplosion ( EFFECT *psEffect );
static void effectSetupConstruction ( EFFECT *psEffect );
static void effectSetupWayPoint ( EFFECT *psEffect );
static void effectSetupBlood ( EFFECT *psEffect );
static void effectSetupDestruction ( EFFECT *psEffect );
static void effectSetupFire ( EFFECT *psEffect );
static void effectSetupSatLaser ( EFFECT *psEffect );
static void effectSetupFirework ( EFFECT *psEffect );
static void effectStructureUpdates();
static void effectDroidUpdates(void);
static UDWORD effectGetNumFrames(EFFECT *psEffect);
static void killEffect(EFFECT *e);
/*!
* Initialise memory between first and last as singly linked list
* \param first First element in memory chunk
* \param last Last element in memory chunk
*/
static void initEffectPool(EFFECT *first, EFFECT *last)
{
EFFECT *it;
for (it = first; it < last; it++)
{
// We do not need a double-linked-list for inactiveeffects, since we always pick from the front:
it->prev = NULL;
it->next = it+1;
}
last->prev = NULL;
last->next = NULL;
}
/*!
* Allocate a new effect from memory pool
* FIXME: Does not deal with out-of-memory conditions (yet)
* \return New, uninitialised effect
*/
static EFFECT *Effect_malloc(void)
{
/* Take the first item in inactiveList */
EFFECT *instance = inactiveList.first;
/* Remove from inactiveList */
inactiveList.first = instance->next; // Singly linked, so do not update prev
instance->next = NULL; // We are the last in activeList now
/* Append to activeList */
instance->prev = activeList.last;
if (instance->prev == NULL) {
activeList.first = instance; // activeList was empty, so fill it
}
else {
activeList.last->next = instance;
}
activeList.last = instance;
/* Adjust counts */
activeList.num++;
inactiveList.num--;
/* Ensure the next search will have something to feed its hunger with */
if (inactiveList.first == NULL)
{
/* Allocate new effect chunk */
EffectChunk *chunk = (EffectChunk *)calloc(1, sizeof(EffectChunk));
debug(LOG_MEMORY, "%lu effects in use, allocating %d extra", (unsigned long)activeList.num, EFFECT_CHUNK_SIZE);
/* Deal with out-of-memory conditions */
if (chunk == NULL) {
debug(LOG_ERROR, "Out of memory");
return NULL; // The next call relies on inactiveList being non-empty, and would thus segfault, so bail out early instead
}
/* Append to list */
chunk->next = NULL; // Last element
chunkList.last->next = chunk; // chunkList is never empty, thus we can rely on last!=NULL
/* Update inactiveList */
inactiveList.num = EFFECT_CHUNK_SIZE;
inactiveList.first = &chunk->effects[0];
inactiveList.last = &chunk->effects[EFFECT_CHUNK_SIZE-1];
/* Initialise list links between inactive effects */
initEffectPool(inactiveList.first, inactiveList.last);
}
return instance;
}
/*!
* Return an effect into memory pool
* \param self Effect to be freed
*/
static void Effect_free(EFFECT *instance)
{
instance->group = EFFECT_FREED;
/* Remove from activeList and fixup endings necessary */
if (instance->prev != NULL) {
instance->prev->next = instance->next;
}
else {
activeList.first = instance->next; // We were the first in activeList
}
if (instance->next != NULL) {
instance->next->prev = instance->prev;
}
else {
activeList.last = instance->prev; // We were the last in activeList
}
/* Append to inactiveList */
instance->prev = NULL; // Singly linked
instance->next = NULL; // Last element
// inactiveList is never empty (guaranteed in Effect_malloc), thus we can rely on last!=NULL
inactiveList.last->next = instance;
inactiveList.last = instance;
/* Adjust counts */
inactiveList.num++;
ASSERT_OR_RETURN(, activeList.num > 0, "Underflow");
activeList.num--;
}
void shutdownEffectsSystem(void)
{
EFFECT *eff;
/* Traverse the list */
for (eff = activeList.first; eff;)
{
EFFECT *effNext = eff->next;
killEffect(eff);
eff = effNext;
}
free(chunkList.first);
chunkList.first = NULL;
chunkList.last = NULL;
}
/*!
* Initialise effects system
* Cleans up old effects, allocates a first chunk of effects, initialises (in)active lists
*/
void initEffectsSystem(void)
{
EffectChunk *chunk;
/* Clean up old chunks */
shutdownEffectsSystem();
/* Allocate new chunk */
chunk = (EffectChunk *)calloc(1, sizeof(EffectChunk));
/* Deal with out-of-memory conditions */
if (chunk == NULL)
return; // FIXME We have no way to report an error here...
/* Create chunkList */
chunkList.first = chunk;
chunkList.last = chunk;
chunk->next = NULL; // Last element
/* activeList is empty at start */
activeList.num = 0;
activeList.first = NULL;
activeList.last = NULL;
/* inactiveList contains all elements */
inactiveList.num = EFFECT_CHUNK_SIZE;
inactiveList.first = &chunk->effects[0];
inactiveList.last = &chunk->effects[EFFECT_CHUNK_SIZE-1];
/* Initialise free-effects pool */
initEffectPool(inactiveList.first, inactiveList.last);
}
static void positionEffect(const EFFECT *psEffect)
{
/* Establish world position */
Vector3i dv(
psEffect->position.x - player.p.x,
psEffect->position.y,
-(psEffect->position.z - player.p.z)
);
/* Push the indentity matrix */
pie_MatBegin();
/* Move to position */
pie_TRANSLATE(dv.x, dv.y, dv.z);
}
static void killEffect(EFFECT *e)
{
/* Put effect back into pool */
Effect_free(e);
}
void effectSetLandLightSpec(LAND_LIGHT_SPEC spec)
{
ellSpec = spec;
}
void effectSetSize(UDWORD size)
{
specifiedSize = size;
}
void addMultiEffect(const Vector3i *basePos, Vector3i *scatter, EFFECT_GROUP group,
EFFECT_TYPE type, bool specified, iIMDShape *imd, unsigned int number, bool lit, unsigned int size, unsigned effectTime)
{
if (number == 0)
{
return;
}
/* Set up the scaling for specified ones */
specifiedSize = size;
/* If there's only one, make sure it's in the centre */
if (number == 1)
{
addEffect(basePos, group, type, specified, imd, lit, effectTime);
}
else
{
unsigned int i;
/* Fix for jim */
*scatter = *scatter / 10;
/* There are multiple effects - so scatter them around according to parameter */
for(i=0; i<number; i++)
{
// This scatters in a cube - is there a good reason for that, or just legacy?
Vector3i scatPos = *basePos + *scatter - Vector3i(rand()%(scatter->x*2 + 1),
rand()%(scatter->y*2 + 1),
rand()%(scatter->z*2 + 1)
);
addEffect(&scatPos, group, type, specified, imd, lit, effectTime);
}
}
}
// When we need to set the effect for the player's color
void SetEffectForPlayer(uint8_t player)
{
ASSERT(player < MAX_PLAYERS, "player is set to a invalid number of %d", (int) player);
EffectForPlayer = getPlayerColour(player);
}
void addEffect(const Vector3i *pos, EFFECT_GROUP group, EFFECT_TYPE type, bool specified, iIMDShape *imd, int lit)
{
return addEffect(pos, group, type, specified, imd, lit, graphicsTime);
}
void addEffect(const Vector3i *pos, EFFECT_GROUP group, EFFECT_TYPE type, bool specified, iIMDShape *imd, int lit, unsigned effectTime)
{
EFFECT *psEffect = NULL;
if(gamePaused())
{
return;
}
/* Retrieve a new effect from pool */
psEffect = Effect_malloc();
/* Deal with out-of-memory conditions */
if (psEffect == NULL) {
debug(LOG_ERROR, "Out of memory");
return; // FIXME We have no way to report an error here...
}
/* Reset control bits */
psEffect->control = 0;
/* Store away it's position - into FRACTS */
psEffect->position.x = pos->x;
psEffect->position.y = pos->y;
psEffect->position.z = pos->z;
/* Now, note group and type */
psEffect->group = group;
psEffect->type = type;
// and if the effect needs the player's color for certain things
psEffect->player = EffectForPlayer;
SetEffectForPlayer(0); // reset it
/* Set when it entered the world */
psEffect->birthTime = psEffect->lastFrame = effectTime;
if(group == EFFECT_GRAVITON && (type == GRAVITON_TYPE_GIBLET || type == GRAVITON_TYPE_EMITTING_DR))
{
psEffect->frameNumber = lit;
}
else
{
/* Starts off on frame zero */
psEffect->frameNumber = 0;
}
/*
See what kind of effect it is - the add fucnction is different for each,
although some things are shared
*/
psEffect->imd = NULL;
if(lit)
{
SET_LIT(psEffect);
}
if(specified)
{
/* We're specifying what the imd is - override */
psEffect->imd = imd;
psEffect->size = specifiedSize;
}
/* Do all the effect type specific stuff */
switch(group)
{
case EFFECT_SMOKE:
effectSetupSmoke(psEffect);
break;
case EFFECT_GRAVITON:
effectSetupGraviton(psEffect);
break;
case EFFECT_EXPLOSION:
effectSetupExplosion(psEffect);
break;
case EFFECT_CONSTRUCTION:
effectSetupConstruction(psEffect);
break;
case EFFECT_WAYPOINT:
effectSetupWayPoint(psEffect);
break;
case EFFECT_BLOOD:
effectSetupBlood(psEffect);
break;
case EFFECT_DESTRUCTION:
effectSetupDestruction(psEffect);
break;
case EFFECT_FIRE:
effectSetupFire(psEffect);
break;
case EFFECT_SAT_LASER:
effectSetupSatLaser(psEffect);
break;
case EFFECT_FIREWORK:
effectSetupFirework(psEffect);
break;
case EFFECT_FREED:
ASSERT( false,"Weirdy group type for an effect" );
break;
}
/* As of yet, it hasn't bounced (or whatever)... */
if(type!=EXPLOSION_TYPE_LAND_LIGHT)
{
psEffect->specific = 0;
}
ASSERT(psEffect->imd != NULL || group == EFFECT_DESTRUCTION || group == EFFECT_FIRE || group == EFFECT_SAT_LASER, "null effect imd");
}
/* Calls all the update functions for each different currently active effect */
void processEffects(void)
{
EFFECT *it;
/* Traverse the list */
for (it = activeList.first; it != NULL;)
{
EFFECT *itNext = it->next; // If updateEffect deletes something, we would be screwed...
if (it->birthTime <= graphicsTime) // Don't process, if it doesn't exist yet.
{
/* Run updates, effect may be deleted here */
updateEffect(it);
/* Is it on the grid */
if (it->group != EFFECT_FREED && clipXY(it->position.x, it->position.z))
{
/* Add it to the bucket */
bucketAddTypeToList(RENDER_EFFECT, it);
}
}
it = itNext;
}
/* Add any droid effects */
effectDroidUpdates();
/* Add any structure effects */
effectStructureUpdates();
}
/* The general update function for all effects - calls a specific one for each */
static void updateEffect(EFFECT *psEffect)
{
/* What type of effect are we dealing with? */
switch(psEffect->group)
{
case EFFECT_EXPLOSION:
updateExplosion(psEffect);
return;
case EFFECT_WAYPOINT:
if(!gamePaused()) updateWaypoint(psEffect);
return;
case EFFECT_CONSTRUCTION:
if(!gamePaused()) updateConstruction(psEffect);
return;
case EFFECT_SMOKE:
if(!gamePaused()) updatePolySmoke(psEffect);
return;
case EFFECT_GRAVITON:
if(!gamePaused()) updateGraviton(psEffect);
return;
case EFFECT_BLOOD:
if(!gamePaused()) updateBlood(psEffect);
return;
case EFFECT_DESTRUCTION:
if(!gamePaused()) updateDestruction(psEffect);
return;
case EFFECT_FIRE:
if(!gamePaused()) updateFire(psEffect);
return;
case EFFECT_SAT_LASER:
if(!gamePaused()) updateSatLaser(psEffect);
return;
case EFFECT_FIREWORK:
if(!gamePaused()) updateFirework(psEffect);
return;
case EFFECT_FREED:
break;
}
debug( LOG_ERROR, "Weirdy class of effect passed to updateEffect" );
abort();
}
// ----------------------------------------------------------------------------------------
// ALL THE UPDATE FUNCTIONS
// ----------------------------------------------------------------------------------------
/** Update the waypoint effects.*/
static void updateWaypoint(EFFECT *psEffect)
{
if(!(keyDown(KEY_LCTRL) || keyDown(KEY_RCTRL) ||
keyDown(KEY_LSHIFT) || keyDown(KEY_RSHIFT)))
{
killEffect(psEffect);
}
}
static void updateFirework(EFFECT *psEffect)
{
UDWORD height;
UDWORD xDif,yDif,radius,val;
Vector3i dv;
SDWORD dif;
UDWORD drop;
/* Move it */
psEffect->position.x += graphicsTimeAdjustedIncrement(psEffect->velocity.x);
psEffect->position.y += graphicsTimeAdjustedIncrement(psEffect->velocity.y);
psEffect->position.z += graphicsTimeAdjustedIncrement(psEffect->velocity.z);
if(psEffect->type == FIREWORK_TYPE_LAUNCHER)
{
height = psEffect->position.y;
if(height > psEffect->size)
{
dv.x = psEffect->position.x;
dv.z = psEffect->position.z;
dv.y = psEffect->position.y + (psEffect->radius / 2);
addEffect(&dv,EFFECT_EXPLOSION,EXPLOSION_TYPE_MEDIUM,false,NULL,0);
audio_PlayStaticTrack(psEffect->position.x, psEffect->position.z, ID_SOUND_EXPLOSION);
for(dif =0; dif < (psEffect->radius*2); dif+=20)
{
if(dif<psEffect->radius)
{
drop = psEffect->radius-dif;
}
else
{
drop = dif - psEffect->radius;
}
radius = (UDWORD)sqrtf(psEffect->radius * psEffect->radius - drop * drop);
//val = getStaticTimeValueRange(720,360); // grab an angle - 4 seconds cyclic
for(val = 0; val<=180; val+=20)
{
xDif = iSinR(DEG(val), radius);
yDif = iCosR(DEG(val), radius);
dv.x = psEffect->position.x + xDif;
dv.z = psEffect->position.z + yDif;
dv.y = psEffect->position.y + dif;
effectGiveAuxVar(100);
addEffect(&dv,EFFECT_FIREWORK, FIREWORK_TYPE_STARBURST,false,NULL,0);
dv.x = psEffect->position.x - xDif;
dv.z = psEffect->position.z - yDif;
dv.y = psEffect->position.y + dif;
effectGiveAuxVar(100);
addEffect(&dv,EFFECT_FIREWORK, FIREWORK_TYPE_STARBURST,false,NULL,0);
dv.x = psEffect->position.x + xDif;
dv.z = psEffect->position.z - yDif;
dv.y = psEffect->position.y + dif;
effectGiveAuxVar(100);
addEffect(&dv,EFFECT_FIREWORK, FIREWORK_TYPE_STARBURST,false,NULL,0);
dv.x = psEffect->position.x - xDif;
dv.z = psEffect->position.z + yDif;
dv.y = psEffect->position.y + dif;
effectGiveAuxVar(100);
addEffect(&dv,EFFECT_FIREWORK, FIREWORK_TYPE_STARBURST,false,NULL,0);
}
}
killEffect(psEffect);
}
else
{
/* Add an effect at the firework's position */
dv.x = psEffect->position.x;
dv.y = psEffect->position.y;
dv.z = psEffect->position.z;
/* Add a trail graphic */
addEffect(&dv,EFFECT_SMOKE,SMOKE_TYPE_TRAIL,false,NULL,0);
}
}
else // must be a startburst
{
/* Time to update the frame number on the smoke sprite */
if(graphicsTime - psEffect->lastFrame > psEffect->frameDelay)
{
/* Store away last frame change time */
psEffect->lastFrame = graphicsTime;
/* Are we on the last frame? */
if(++psEffect->frameNumber >= effectGetNumFrames(psEffect))
{
/* Does the anim wrap around? */
if(TEST_CYCLIC(psEffect))
{
psEffect->frameNumber = 0;
}
else
{
/* Kill it off */
killEffect(psEffect);
return;
}
}
}
/* If it doesn't get killed by frame number, then by age */
if(TEST_CYCLIC(psEffect))
{
/* Has it overstayed it's welcome? */
if(graphicsTime - psEffect->birthTime > psEffect->lifeSpan)
{
/* Kill it */
killEffect(psEffect);
}
}
}
}
static void updateSatLaser(EFFECT *psEffect)
{
Vector3i dv;
UDWORD val;
UDWORD radius;
UDWORD xDif,yDif;
UDWORD i;
UDWORD startHeight,endHeight;
UDWORD xPos,yPos;
LIGHT light;
// Do these here cause there used by the lighting code below this if.
xPos = psEffect->position.x;
startHeight = psEffect->position.y;
endHeight = startHeight+1064;
yPos = psEffect->position.z;
if(psEffect->baseScale)
{
psEffect->baseScale = 0;
/* Add some big explosions....! */
for(i=0; i<16; i++)
{
dv.x = xPos+(200-rand()%400);
dv.z = yPos+(200-rand()%400);
dv.y = startHeight + rand()%100;
addEffect(&dv,EFFECT_EXPLOSION,EXPLOSION_TYPE_MEDIUM,false,NULL,0);
}
/* Add a sound effect */
audio_PlayStaticTrack(psEffect->position.x, psEffect->position.z, ID_SOUND_EXPLOSION);
/* Add a shockwave */
dv.x = xPos;
dv.z = yPos;
dv.y = startHeight+SHOCK_WAVE_HEIGHT;
addEffect(&dv,EFFECT_EXPLOSION,EXPLOSION_TYPE_SHOCKWAVE,false,NULL,0);
/* Now, add the column of light */
for(i=startHeight; i<endHeight; i+=56)
{
radius = 80;
/* Add 36 around in a circle..! */
for(val = 0; val<=180; val+=30)
{
xDif = iSinR(DEG(val), radius);
yDif = iCosR(DEG(val), radius);
dv.x = xPos+xDif+i/64;
dv.z = yPos+yDif;
dv.y = startHeight+i;
effectGiveAuxVar(100);
addEffect(&dv,EFFECT_EXPLOSION, EXPLOSION_TYPE_MEDIUM,false,NULL,0);
dv.x = xPos-xDif+i/64;
dv.z = yPos-yDif;
dv.y = startHeight+i;
effectGiveAuxVar(100);
addEffect(&dv,EFFECT_EXPLOSION, EXPLOSION_TYPE_MEDIUM,false,NULL,0);
dv.x = xPos+xDif+i/64;
dv.z = yPos-yDif;
dv.y = startHeight+i;
effectGiveAuxVar(100);
addEffect(&dv,EFFECT_EXPLOSION, EXPLOSION_TYPE_MEDIUM,false,NULL,0);
dv.x = xPos-xDif+i/64;
dv.z = yPos+yDif;
dv.y = startHeight+i;
effectGiveAuxVar(100);
addEffect(&dv,EFFECT_EXPLOSION, EXPLOSION_TYPE_MEDIUM,false,NULL,0);
}
}
}
if(graphicsTime - psEffect->birthTime < 1000)
{
light.position.x = xPos;
light.position.y = startHeight;
light.position.z = yPos;
light.range = 800;
light.colour = LIGHT_BLUE;
processLight(&light);
}
else
{
killEffect(psEffect);
}
}
/** The update function for the explosions */
static void updateExplosion(EFFECT *psEffect)
{
LIGHT light;
UDWORD percent;
UDWORD range;
float scaling;
if (TEST_LIT(psEffect))
{
if (psEffect->lifeSpan)
{
percent = PERCENT(graphicsTime - psEffect->birthTime, psEffect->lifeSpan);
if (percent > 100)
{
percent = 100;
}
else if (percent > 50)
{
percent = 100 - percent;
}
}
else
{
percent = 100;
}
range = percent;
light.position.x = psEffect->position.x;
light.position.y = psEffect->position.y;
light.position.z = psEffect->position.z;
light.range = (3*range)/2;
light.colour = LIGHT_RED;
processLight(&light);
}
if (psEffect->type == EXPLOSION_TYPE_SHOCKWAVE)
{
psEffect->size += graphicsTimeAdjustedIncrement(SHOCKWAVE_SPEED);
scaling = (float)psEffect->size / (float)MAX_SHOCKWAVE_SIZE;
psEffect->frameNumber = scaling * effectGetNumFrames(psEffect);
light.position.x = psEffect->position.x;
light.position.y = psEffect->position.y;
light.position.z = psEffect->position.z;
light.range = psEffect->size+200;
light.colour = LIGHT_YELLOW;
processLight(&light);
if (psEffect->size > MAX_SHOCKWAVE_SIZE || light.range > 600)
{
/* Kill it off */
killEffect(psEffect);
return;
}
}
/* Time to update the frame number on the explosion */
else while (graphicsTime - psEffect->lastFrame > psEffect->frameDelay)
{
psEffect->lastFrame += psEffect->frameDelay;
/* Are we on the last frame? */
if (++psEffect->frameNumber >= effectGetNumFrames(psEffect))
{
if (psEffect->type != EXPLOSION_TYPE_LAND_LIGHT)
{
/* Kill it off */
killEffect(psEffect);
return;
}
else
{
psEffect->frameNumber = 0;
}
}
}
if(!gamePaused())
{
/* Tesla explosions are the only ones that rise, or indeed move */
if(psEffect->type == EXPLOSION_TYPE_TESLA)
{
psEffect->position.y += graphicsTimeAdjustedIncrement(psEffect->velocity.y);
}
}
}
/** The update function for blood */
static void updateBlood(EFFECT *psEffect)
{
/* Time to update the frame number on the blood */
if(graphicsTime - psEffect->lastFrame > psEffect->frameDelay)
{
psEffect->lastFrame = graphicsTime;
/* Are we on the last frame? */
if(++psEffect->frameNumber >= effectGetNumFrames(psEffect))
{
/* Kill it off */
killEffect(psEffect);
return;
}
}
/* Move it about in the world */
psEffect->position.x += graphicsTimeAdjustedIncrement(psEffect->velocity.x);
psEffect->position.y += graphicsTimeAdjustedIncrement(psEffect->velocity.y);
psEffect->position.z += graphicsTimeAdjustedIncrement(psEffect->velocity.z);
}
/** Processes all the drifting smoke
Handles the smoke puffing out the factory as well */
static void updatePolySmoke(EFFECT *psEffect)
{
/* Time to update the frame number on the smoke sprite */
while (graphicsTime - psEffect->lastFrame > psEffect->frameDelay)
{
/* Store away last frame change time */
psEffect->lastFrame += psEffect->frameDelay;
/* Are we on the last frame? */
if(++psEffect->frameNumber >= effectGetNumFrames(psEffect))
{
/* Does the anim wrap around? */
if(TEST_CYCLIC(psEffect))
{
/* Does it change drift direction? */
if(psEffect->type == SMOKE_TYPE_DRIFTING)
{
/* Make it change direction */
psEffect->velocity.x = (float)(rand() % 20);
psEffect->velocity.z = (float)(10 - rand() % 20);
psEffect->velocity.y = (float)(10 + rand() % 20);
}
/* Reset the frame */
psEffect->frameNumber = 0;
}
else
{
/* Kill it off */
killEffect(psEffect);
return;
}
}
}
/* Update position */
psEffect->position.x += graphicsTimeAdjustedIncrement(psEffect->velocity.x);
psEffect->position.y += graphicsTimeAdjustedIncrement(psEffect->velocity.y);
psEffect->position.z += graphicsTimeAdjustedIncrement(psEffect->velocity.z);
/* If it doesn't get killed by frame number, then by age */
if(TEST_CYCLIC(psEffect))
{
/* Has it overstayed it's welcome? */
if(graphicsTime - psEffect->birthTime > psEffect->lifeSpan)
{
/* Kill it */
killEffect(psEffect);
}
}
}
/**
Gravitons just fly up for a bit and then drop down and are
killed off when they hit the ground
*/
static void updateGraviton(EFFECT *psEffect)
{
float accel;
Vector3i dv;
MAPTILE *psTile;
LIGHT light;
if(psEffect->type!=GRAVITON_TYPE_GIBLET)
{
light.position.x = psEffect->position.x;
light.position.y = psEffect->position.y;
light.position.z = psEffect->position.z;
light.range = 128;
light.colour = LIGHT_YELLOW;
processLight(&light);
}
if(gamePaused())
{
/* Only update the lights if it's paused */
return;
}
/* Move it about in the world */
psEffect->position.x += graphicsTimeAdjustedIncrement(psEffect->velocity.x);
psEffect->position.y += graphicsTimeAdjustedIncrement(psEffect->velocity.y);
psEffect->position.z += graphicsTimeAdjustedIncrement(psEffect->velocity.z);
/* If it's bounced/drifted off the map then kill it */
if (map_coord(psEffect->position.x) >= mapWidth
|| map_coord(psEffect->position.z) >= mapHeight)
{
killEffect(psEffect);
return;
}
int groundHeight = map_Height(psEffect->position.x, psEffect->position.z);
/* If it's going up and it's still under the landscape, then remove it... */
if (psEffect->position.y<groundHeight
&& psEffect->velocity.y > 0)
{
killEffect(psEffect);
return;
}
/* Does it emit a trail? And is it high enough? */
if ((psEffect->type == GRAVITON_TYPE_EMITTING_DR)
|| ((psEffect->type == GRAVITON_TYPE_EMITTING_ST)
&& (psEffect->position.y>(groundHeight+10))))
{
/* Time to add another trail 'thing'? */
if(graphicsTime > psEffect->lastFrame + psEffect->frameDelay)
{
/* Store away last update */
psEffect->lastFrame = graphicsTime;
/* Add an effect at the gravitons's position */
dv.x = psEffect->position.x;
dv.y = psEffect->position.y;
dv.z = psEffect->position.z;
/* Add a trail graphic */
addEffect(&dv,EFFECT_SMOKE,SMOKE_TYPE_TRAIL,false,NULL,0);
}
}
else if(psEffect->type == GRAVITON_TYPE_GIBLET && (psEffect->position.y>(groundHeight+5)))
{
/* Time to add another trail 'thing'? */
if(graphicsTime > psEffect->lastFrame + psEffect->frameDelay)
{
/* Store away last update */
psEffect->lastFrame = graphicsTime;
/* Add an effect at the gravitons's position */
dv.x = psEffect->position.x;
dv.y = psEffect->position.y;
dv.z = psEffect->position.z;
addEffect(&dv,EFFECT_BLOOD,BLOOD_TYPE_NORMAL,false,NULL,0);
}
}
/* Spin it round a bit */
psEffect->rotation.x += graphicsTimeAdjustedIncrement(psEffect->spin.x);
psEffect->rotation.y += graphicsTimeAdjustedIncrement(psEffect->spin.y);
psEffect->rotation.z += graphicsTimeAdjustedIncrement(psEffect->spin.z);
/* Update velocity (and retarding of descent) according to present frame rate */
accel = graphicsTimeAdjustedIncrement(GRAVITON_GRAVITY);
psEffect->velocity.y += accel;
/* If it's bounced/drifted off the map then kill it */
if ((int)psEffect->position.x <= TILE_UNITS
|| (int)psEffect->position.z <= TILE_UNITS)
{
killEffect(psEffect);
return;
}
/* Are we below it? - Hit the ground? */
if ((int)psEffect->position.y < (int)groundHeight)
{
psTile = mapTile(map_coord(psEffect->position.x), map_coord(psEffect->position.z));
if (terrainType(psTile) == TER_WATER)
{
killEffect(psEffect);
return;
}
else
/* Are we falling - rather than rising? */
if((int)psEffect->velocity.y < 0)
{
/* Has it sufficient energy to keep bouncing? */
if (abs(psEffect->velocity.y) > 16
&& psEffect->specific <= 2)
{
psEffect->specific++;
/* Half it's velocity */
psEffect->velocity.y/=(float)(-2); // only y gets flipped
/* Set it at ground level - may have gone through */
psEffect->position.y = (float)groundHeight;
}
else
{
/* Giblets don't blow up when they hit the ground! */
if(psEffect->type!=GRAVITON_TYPE_GIBLET)
{
/* Remove the graviton and add an explosion */
dv.x = psEffect->position.x;
dv.y = psEffect->position.y + 10;
dv.z = psEffect->position.z;
addEffect(&dv,EFFECT_EXPLOSION,EXPLOSION_TYPE_VERY_SMALL,false,NULL,0);
}
killEffect(psEffect);
return;
}
}
}
}
/** This isn't really an on-screen effect itself - it just spawns other ones.... */
static void updateDestruction(EFFECT *psEffect)
{
Vector3i pos;
UDWORD effectType;
UDWORD widthScatter = 0, breadthScatter = 0, heightScatter = 0;
SDWORD iX, iY;
LIGHT light;
int percent;
UDWORD range;
float div;
UDWORD height;
percent = PERCENT(graphicsTime - psEffect->birthTime, psEffect->lifeSpan);
if(percent > 100)
{
percent = 100;
}
range = 50 - abs(50-percent);
light.position.x = psEffect->position.x;
light.position.y = psEffect->position.y;
light.position.z = psEffect->position.z;
if(psEffect->type == DESTRUCTION_TYPE_STRUCTURE)
{
light.range = range*10;
}
else
{
light.range = range*4;
}
if(psEffect->type == DESTRUCTION_TYPE_POWER_STATION)
{
light.range *=3;
light.colour = LIGHT_WHITE;
}
else
{
light.colour = LIGHT_RED;
}
processLight(&light);
if(graphicsTime > psEffect->birthTime + psEffect->lifeSpan)
{
/* Kill it - it's too old */
killEffect(psEffect);
return;
}
if(psEffect->type == DESTRUCTION_TYPE_SKYSCRAPER)
{
if(graphicsTime - psEffect->birthTime > psEffect->lifeSpan * 9/10)
{
pos.x = psEffect->position.x;
pos.z = psEffect->position.z;
pos.y = psEffect->position.y;
addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LARGE,false,NULL,0);
killEffect(psEffect);
return;
}
div = 1.f - (float)(graphicsTime - psEffect->birthTime) / psEffect->lifeSpan;
if(div < 0.f)
div = 0.f;
height = div * psEffect->imd->max.y;
}
else
{
height = 16;
}
/* Time to add another effect? */
if((graphicsTime - psEffect->lastFrame) > psEffect->frameDelay)
{
psEffect->lastFrame = graphicsTime;
switch(psEffect->type)
{
case DESTRUCTION_TYPE_SKYSCRAPER:
widthScatter = TILE_UNITS;
breadthScatter = TILE_UNITS;
heightScatter = TILE_UNITS;
break;
case DESTRUCTION_TYPE_POWER_STATION:
case DESTRUCTION_TYPE_STRUCTURE:
widthScatter = TILE_UNITS/2;
breadthScatter = TILE_UNITS/2;
heightScatter = TILE_UNITS/4;
break;
case DESTRUCTION_TYPE_DROID:
case DESTRUCTION_TYPE_WALL_SECTION:
case DESTRUCTION_TYPE_FEATURE:
widthScatter = TILE_UNITS/6;
breadthScatter = TILE_UNITS/6;
heightScatter = TILE_UNITS/6;
break;
default:
ASSERT( false,"Weirdy destruction type effect" );
break;
}
/* Find a position to dump it at */
pos.x = psEffect->position.x + widthScatter - rand() % (2 * widthScatter);
pos.z = psEffect->position.z + breadthScatter - rand() % (2 * breadthScatter);
pos.y = psEffect->position.y + height + rand() % heightScatter;
if(psEffect->type == DESTRUCTION_TYPE_SKYSCRAPER)
{
pos.y = psEffect->position.y + height;
}
/* Choose an effect */
effectType = rand()%15;
switch(effectType)
{
case 0:
addEffect(&pos,EFFECT_SMOKE,SMOKE_TYPE_DRIFTING,false,NULL,0);
break;
case 1:
case 2:
case 3:
case 4:
case 5:
if(psEffect->type == DESTRUCTION_TYPE_SKYSCRAPER)
{
addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_LARGE,false,NULL,0);
}
/* Only structures get the big explosions */
else if(psEffect->type==DESTRUCTION_TYPE_STRUCTURE)
{
addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_MEDIUM,false,NULL,0);
}
else
{
addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_SMALL,false,NULL,0);
}
break;
case 6:
case 7:
case 8:
case 9:
case 10:
if(psEffect->type == DESTRUCTION_TYPE_STRUCTURE)
{
addEffect(&pos,EFFECT_GRAVITON,GRAVITON_TYPE_EMITTING_ST,true,getRandomDebrisImd(),0);
}
else
{
addEffect(&pos,EFFECT_GRAVITON,GRAVITON_TYPE_EMITTING_DR,true,getRandomDebrisImd(),0);
}
break;
case 11:
addEffect(&pos,EFFECT_SMOKE,SMOKE_TYPE_DRIFTING,false,NULL,0);
break;
case 12:
case 13:
addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_SMALL,false,NULL,0);
break;
case 14:
/* Add sound effect, but only if we're less than 3/4 of the way thru' destruction */
if (graphicsTime < (psEffect->birthTime + psEffect->lifeSpan) * 3/4)
{
iX = psEffect->position.x;
iY = psEffect->position.z;
audio_PlayStaticTrack( iX, iY, ID_SOUND_EXPLOSION );
}
break;
}
}
}
/** Moves the construction graphic about - dust cloud or whatever.... */
static void updateConstruction(EFFECT *psEffect)
{
/* Time to update the frame number on the construction sprite */
if(graphicsTime - psEffect->lastFrame > psEffect->frameDelay)
{
psEffect->lastFrame = graphicsTime;
/* Are we on the last frame? */
if(++psEffect->frameNumber >= effectGetNumFrames(psEffect))
{
/* Is it a cyclic sprite? */
if(TEST_CYCLIC(psEffect))
{
psEffect->frameNumber = 0;
}
else
{
killEffect(psEffect);
return;
}
}
}
/* Move it about in the world */
psEffect->position.x += graphicsTimeAdjustedIncrement(psEffect->velocity.x);
psEffect->position.y += graphicsTimeAdjustedIncrement(psEffect->velocity.y);
psEffect->position.z += graphicsTimeAdjustedIncrement(psEffect->velocity.z);
/* If it doesn't get killed by frame number, then by height */
if(TEST_CYCLIC(psEffect))
{
/* Has it hit the ground */
if ((int)psEffect->position.y <=
map_Height(psEffect->position.x, psEffect->position.z))
{
killEffect(psEffect);
return;
}
if (graphicsTime - psEffect->birthTime > psEffect->lifeSpan)
{
killEffect(psEffect);
return;
}
}
}
/** Update fire sequences */
static void updateFire(EFFECT *psEffect)
{
Vector3i pos;
LIGHT light;
UDWORD percent;
percent = PERCENT(graphicsTime - psEffect->birthTime, std::max<unsigned>(psEffect->lifeSpan, 1));
if(percent > 100)
{
percent = 100;
}
light.position.x = psEffect->position.x;
light.position.y = psEffect->position.y;
light.position.z = psEffect->position.z;
light.range = (percent*psEffect->radius*3)/100;
light.colour = LIGHT_RED;
processLight(&light);
/* Time to update the frame number on the construction sprite */
if (graphicsTime - psEffect->lastFrame > psEffect->frameDelay)
{
psEffect->lastFrame = graphicsTime;
pos.x = (psEffect->position.x + ((rand() % psEffect->radius) - (rand() % (2 * psEffect->radius))));
pos.z = (psEffect->position.z + ((rand() % psEffect->radius) - (rand() % (2 * psEffect->radius))));
// Effect is off map, no need to update it anymore
if(!worldOnMap(pos.x, pos.z))
{
killEffect(psEffect);
return;
}
pos.y = map_Height(pos.x,pos.z);
if(psEffect->type == FIRE_TYPE_SMOKY_BLUE)
{
addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_FLAMETHROWER,false,NULL,0);
}
else
{
addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_SMALL,false,NULL,0);
}
if(psEffect->type == FIRE_TYPE_SMOKY || psEffect->type == FIRE_TYPE_SMOKY_BLUE)
{
pos.x = (psEffect->position.x + ((rand() % psEffect->radius / 2) - (rand() % (2 * psEffect->radius / 2))));
pos.z = (psEffect->position.z + ((rand() % psEffect->radius / 2) - (rand() % (2 * psEffect->radius / 2))));
pos.y = map_Height(pos.x,pos.z);
addEffect(&pos,EFFECT_SMOKE,SMOKE_TYPE_DRIFTING_HIGH,false,NULL,0);
}
else
{
pos.x = (psEffect->position.x + ((rand() % psEffect->radius) - (rand() % (2 * psEffect->radius))));
pos.z = (psEffect->position.z + ((rand() % psEffect->radius) - (rand() % (2 * psEffect->radius))));
// Effect is off map, no need to update it anymore
if(!worldOnMap(pos.x, pos.z))
{
killEffect(psEffect);
return;
}
pos.y = map_Height(pos.x,pos.z);
addEffect(&pos,EFFECT_EXPLOSION,EXPLOSION_TYPE_SMALL,false,NULL,0);
}
/*
pos.x = psEffect->position.x;
pos.y = psEffect->position.y;
pos.z = psEffect->position.z;
scatter.x = psEffect->radius; scatter.y = 0; scatter.z = psEffect->radius;
addMultiEffect(&pos,&scatter,EFFECT_EXPLOSION,EXPLOSION_TYPE_SMALL,false,NULL,2,0,0);
*/
}
if (graphicsTime - psEffect->birthTime > psEffect->lifeSpan)
{
killEffect(psEffect);
return;
}
}
// ----------------------------------------------------------------------------------------
// ALL THE RENDER FUNCTIONS
// ----------------------------------------------------------------------------------------
/** Calls the appropriate render routine for each type of effect */
void renderEffect(const EFFECT *psEffect)
{
/* What type of effect are we dealing with? */
switch(psEffect->group)
{
case EFFECT_WAYPOINT:
renderWaypointEffect(psEffect);
return;
case EFFECT_EXPLOSION:
renderExplosionEffect(psEffect);
return;
case EFFECT_CONSTRUCTION:
renderConstructionEffect(psEffect);
return;
case EFFECT_SMOKE:
renderSmokeEffect(psEffect);
return;
case EFFECT_GRAVITON:
renderGravitonEffect(psEffect);
return;
case EFFECT_BLOOD:
renderBloodEffect(psEffect);
return;
case EFFECT_DESTRUCTION:
/* There is no display func for a destruction effect -
it merely spawn other effects over time */
renderDestructionEffect(psEffect);
return;
case EFFECT_FIRE:
/* Likewise */
return;
case EFFECT_SAT_LASER:
/* Likewise */
return;
case EFFECT_FIREWORK:
renderFirework(psEffect);
return;
case EFFECT_FREED:
break;
}
debug( LOG_ERROR, "Weirdy class of effect passed to renderEffect" );
abort();
}
/** drawing func for wapypoints */
static void renderWaypointEffect(const EFFECT *psEffect)
{
positionEffect(psEffect);
pie_Draw3DShape(psEffect->imd, 0, 0, WZCOL_WHITE, 0, 0);
pie_MatEnd();
}
static void renderFirework(const EFFECT *psEffect)
{
/* these don't get rendered */
if(psEffect->type == FIREWORK_TYPE_LAUNCHER)
{
return;
}
positionEffect(psEffect);
pie_MatRotY(-player.r.y);
pie_MatRotX(-player.r.x);
pie_MatScale(psEffect->size / 100.f);
pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, WZCOL_WHITE, pie_ADDITIVE, EFFECT_EXPLOSION_ADDITIVE);
pie_MatEnd();
}
/** drawing func for blood. */
static void renderBloodEffect(const EFFECT *psEffect)
{
positionEffect(psEffect);
pie_MatRotY(-player.r.y);
pie_MatRotX(-player.r.x);
pie_MatScale(psEffect->size / 100.f);
pie_Draw3DShape(getImdFromIndex(MI_BLOOD), psEffect->frameNumber, 0, WZCOL_WHITE, pie_TRANSLUCENT, EFFECT_BLOOD_TRANSPARENCY);
pie_MatEnd();
}
static void renderDestructionEffect(const EFFECT *psEffect)
{
float div;
SDWORD percent;
if(psEffect->type!=DESTRUCTION_TYPE_SKYSCRAPER)
{
return;
}
positionEffect(psEffect);
div = (float)(graphicsTime - psEffect->birthTime) / psEffect->lifeSpan;
if(div>1.0) div = 1.0; //temporary!
{
div = 1.0 - div;
percent = (SDWORD)(div*pie_RAISE_SCALE);
}
if(!gamePaused())
{
pie_MatRotX(SKY_SHIMMY);
pie_MatRotY(SKY_SHIMMY);
pie_MatRotZ(SKY_SHIMMY);
}
pie_Draw3DShape(psEffect->imd, 0, 0, WZCOL_WHITE, pie_RAISE, percent);
pie_MatEnd();
}
static bool rejectLandLight(LAND_LIGHT_SPEC type)
{
unsigned int timeSlice = graphicsTime%2000;
if (timeSlice < 400)
{
return (type != LL_MIDDLE); // reject all expect middle
}
else if (timeSlice < 800)
{
return (type == LL_OUTER); // reject only outer
}
else if (timeSlice < 1200)
{
return(false); //reject none
}
else if (timeSlice < 1600)
{
return (type == LL_OUTER); // reject only outer
}
else
{
return (type != LL_MIDDLE); // reject all expect middle
}
}
/** Renders the standard explosion effect */
static void renderExplosionEffect(const EFFECT *psEffect)
{
const PIELIGHT brightness = WZCOL_WHITE;
if(psEffect->type == EXPLOSION_TYPE_LAND_LIGHT)
{
if(rejectLandLight((LAND_LIGHT_SPEC)psEffect->specific))
{
return;
}
}
positionEffect(psEffect);
/* Bit in comments - doesn't quite work yet? */
if(TEST_FACING(psEffect))
{
/* Always face the viewer! */
// TODO This only faces towards the viewer, if the effect is in the middle of the screen... It draws the effect parallel with the screens near/far planes.
pie_MatRotY(-player.r.y);
pie_MatRotX(-player.r.x);
}
/* Tesla explosions diminish in size */
if(psEffect->type == EXPLOSION_TYPE_TESLA)
{
float scale = (graphicsTime - psEffect->birthTime) / (float)psEffect->lifeSpan;
if (scale < 0.f)
scale = 0.f;
else if (scale > .45f)
scale = .45f;
pie_MatScale(psEffect->size / 100.f - scale);
}
else if(psEffect->type == EXPLOSION_TYPE_PLASMA)
{
float scale = (graphicsTime - psEffect->birthTime) / (float)psEffect->lifeSpan / 3.f;
pie_MatScale(BASE_PLASMA_SIZE / 100.f + scale);
}
else
{
pie_MatScale(psEffect->size / 100.f);
}
bool premultiplied = false;
if (psEffect->imd->flags & iV_IMD_PREMULTIPLIED)
{
premultiplied = true;
}
if (premultiplied)
{
pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, pie_PREMULTIPLIED, 0);
}
else if (psEffect->type == EXPLOSION_TYPE_PLASMA)
{
pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, pie_ADDITIVE, EFFECT_PLASMA_ADDITIVE);
}
else if(psEffect->type == EXPLOSION_TYPE_KICKUP)
{
pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, pie_TRANSLUCENT, 128);
}
else
{
pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, pie_ADDITIVE, EFFECT_EXPLOSION_ADDITIVE);
}
pie_MatEnd();
}
static void renderGravitonEffect(const EFFECT *psEffect)
{
positionEffect(psEffect);
pie_MatRotX(psEffect->rotation.x);
pie_MatRotY(psEffect->rotation.y);
pie_MatRotZ(psEffect->rotation.z);
/* Buildings emitted by gravitons are chunkier */
if(psEffect->type == GRAVITON_TYPE_EMITTING_ST)
{
/* Twice as big - 150 percent */
pie_MatScale(psEffect->size / 100.f);
}
pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, psEffect->player, WZCOL_WHITE, 0, 0);
/* Pop the matrix */
pie_MatEnd();
}
/** Renders the standard construction effect */
static void renderConstructionEffect(const EFFECT *psEffect)
{
Vector3i null;
int percent, translucency;
float size;
/* No rotation about arbitrary axis */
null.x = null.y = null.z = 0;
positionEffect(psEffect);
/* Bit in comments doesn't quite work yet? */
if(TEST_FACING(psEffect))
{
pie_MatRotY(-player.r.y);
pie_MatRotX(-player.r.x);
}
/* Scale size according to age */
percent = PERCENT(graphicsTime - psEffect->birthTime, psEffect->lifeSpan);
if(percent<0) percent = 0;
if(percent>100) percent = 100;
/* Make imds be transparent on 3dfx */
if(percent<50)
{
translucency = percent * 2;
}
else
{
translucency = (100 - percent) * 2;
}
translucency+=10;
size = MIN(2.f * translucency / 100.f, .90f);
pie_MatScale(size);
pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, WZCOL_WHITE, pie_TRANSLUCENT, translucency);
/* Pop the matrix */
pie_MatEnd();
}
/** Renders the standard smoke effect - it is now scaled in real-time as well */
static void renderSmokeEffect(const EFFECT *psEffect)
{
int transparency = 0;
const PIELIGHT brightness = WZCOL_WHITE;
positionEffect(psEffect);
/* Bit in comments doesn't quite work yet? */
if(TEST_FACING(psEffect))
{
/* Always face the viewer! */
pie_MatRotY(-player.r.y);
pie_MatRotX(-player.r.x);
}
if(TEST_SCALED(psEffect))
{
const unsigned int lifetime = graphicsTime - psEffect->birthTime;
unsigned int percent;
// Since psEffect->birthTime will be set to graphicsTime on
// creation, and graphicsTime only increments, birthTime should be
// smaller than or equal to graphicsTime. As a great man once said
// though (hehe, I just love exaggarating ;-) -- Giel):
// "Always assert your assumptions!". So here it goes:
assert(psEffect->birthTime <= graphicsTime);
ASSERT(psEffect->lifeSpan != 0, "Effect lifespan is zero (seconds); it really should be non-zero!");
// HACK: Work around a bug that bit me causing a "integer divide by zero" at this location -- Giel
if (psEffect->lifeSpan != 0)
percent = (lifetime * 100) / psEffect->lifeSpan;
else
percent = 100;
pie_MatScale((percent + psEffect->baseScale) / 100.f);
transparency = (EFFECT_SMOKE_TRANSPARENCY * (100 - percent))/100;
}
transparency = (transparency * 3) / 2; //JPS smoke strength increased for d3d 12 may 99
/* Make imds be transparent on 3dfx */
if(psEffect->type==SMOKE_TYPE_STEAM)
{
pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, pie_TRANSLUCENT, EFFECT_STEAM_TRANSPARENCY / 2);
}
else
{
if(psEffect->type == SMOKE_TYPE_TRAIL)
{
pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, pie_TRANSLUCENT, (2 * transparency) / 3);
}
else
{
pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, pie_TRANSLUCENT, transparency / 2);
}
}
/* Pop the matrix */
pie_MatEnd();
}
// ----------------------------------------------------------------------------------------
// ALL THE SETUP FUNCTIONS
// ----------------------------------------------------------------------------------------
void effectSetupFirework(EFFECT *psEffect)
{
if(psEffect->type == FIREWORK_TYPE_LAUNCHER)
{
psEffect->velocity.x = 200 - rand()%400;
psEffect->velocity.z = 200 - rand()%400;
psEffect->velocity.y = 400 + rand()%200; //height
psEffect->lifeSpan = GAME_TICKS_PER_SEC * 3;
psEffect->radius = 80 + rand()%150;
psEffect->size = 300+rand()%300; //height it goes off
psEffect->imd = getImdFromIndex(MI_FIREWORK); // not actually drawn
}
else
{
psEffect->velocity.x = 20 - rand()%40;
psEffect->velocity.z = 20 - rand()%40;
psEffect->velocity.y = 0-(20+rand()%40); //height
psEffect->lifeSpan = GAME_TICKS_PER_SEC * 4;
/* setup the imds */
switch(rand()%3)
{
case 0:
psEffect->imd = getImdFromIndex(MI_FIREWORK);
psEffect->size = 45; //size of graphic
break;
case 1:
psEffect->imd = getImdFromIndex(MI_SNOW);
SET_CYCLIC(psEffect);
psEffect->size = 60; //size of graphic
break;
default:
psEffect->imd = getImdFromIndex(MI_FLAME);
psEffect->size = 40; //size of graphic
break;
}
}
psEffect->frameDelay = (EXPLOSION_FRAME_DELAY*2);
}
void effectSetupSmoke(EFFECT *psEffect)
{
/* everything except steam drifts about */
if(psEffect->type==SMOKE_TYPE_STEAM)
{
/* Only upwards */
psEffect->velocity.x = 0.f;
psEffect->velocity.z = 0.f;
}
else if (psEffect->type == SMOKE_TYPE_BILLOW)
{
psEffect->velocity.x = (float)(10 - rand() % 20);
psEffect->velocity.z = (float)(10 - rand() % 20);
}
else
{
psEffect->velocity.x = (float)(rand() % 20);
psEffect->velocity.z = (float)(10 - rand() % 20);
}
/* Steam isn't cyclic - it doesn't grow with time either */
if(psEffect->type!=SMOKE_TYPE_STEAM)
{
SET_CYCLIC(psEffect);
SET_SCALED(psEffect);
}
switch(psEffect->type)
{
case SMOKE_TYPE_DRIFTING:
psEffect->imd = getImdFromIndex(MI_SMALL_SMOKE);
psEffect->lifeSpan = (UWORD)NORMAL_SMOKE_LIFESPAN;
psEffect->velocity.y = (float)(35 + rand() % 30);
psEffect->baseScale = 40;
break;
case SMOKE_TYPE_DRIFTING_HIGH:
psEffect->imd = getImdFromIndex(MI_SMALL_SMOKE);
psEffect->lifeSpan = (UWORD)NORMAL_SMOKE_LIFESPAN;
psEffect->velocity.y = (float)(40 + rand() % 45);
psEffect->baseScale = 25;
break;
case SMOKE_TYPE_DRIFTING_SMALL:
psEffect->imd = getImdFromIndex(MI_SMALL_SMOKE);
psEffect->lifeSpan = (UWORD)SMALL_SMOKE_LIFESPAN;
psEffect->velocity.y = (float)(25 + rand() % 35);
psEffect->baseScale = 17;
break;
case SMOKE_TYPE_BILLOW:
psEffect->imd = getImdFromIndex(MI_SMALL_SMOKE);
psEffect->lifeSpan = (UWORD)SMALL_SMOKE_LIFESPAN;
psEffect->velocity.y = (float)(10 + rand() % 20);
psEffect->baseScale = 80;
break;
case SMOKE_TYPE_STEAM:
psEffect->imd = getImdFromIndex(MI_SMALL_STEAM);
psEffect->velocity.y = (float)(rand() % 5);
break;
case SMOKE_TYPE_TRAIL:
psEffect->imd = getImdFromIndex(MI_TRAIL);
psEffect->lifeSpan = TRAIL_SMOKE_LIFESPAN;
psEffect->velocity.y = (float)(5 + rand() % 10);
psEffect->baseScale = 25;
break;
default:
ASSERT( false,"Weird smoke type" );
break;
}
/* It always faces you */
SET_FACING(psEffect);
psEffect->frameDelay = (UWORD)SMOKE_FRAME_DELAY;
/* Randomly flip gfx for variation */
if(ONEINTWO)
{
SET_FLIPPED_X(psEffect);
}
if(ONEINTWO)
{
SET_FLIPPED_Y(psEffect);
}
}
void effectSetupSatLaser(EFFECT *psEffect)
{
/* Does nothing at all..... Runs only for one frame! */
psEffect->baseScale = 1;
return;
}
void effectSetupGraviton(EFFECT *psEffect)
{
switch(psEffect->type)
{
case GRAVITON_TYPE_GIBLET:
psEffect->velocity.x = GIBLET_INIT_VEL_X;
psEffect->velocity.z = GIBLET_INIT_VEL_Z;
psEffect->velocity.y = GIBLET_INIT_VEL_Y;
break;
case GRAVITON_TYPE_EMITTING_ST:
psEffect->velocity.x = GRAVITON_INIT_VEL_X;
psEffect->velocity.z = GRAVITON_INIT_VEL_Z;
psEffect->velocity.y = (5*GRAVITON_INIT_VEL_Y)/4;
psEffect->size =(UWORD)( 120 + rand()%30);
break;
case GRAVITON_TYPE_EMITTING_DR:
psEffect->velocity.x = GRAVITON_INIT_VEL_X/2;
psEffect->velocity.z = GRAVITON_INIT_VEL_Z/2;
psEffect->velocity.y = GRAVITON_INIT_VEL_Y;
break;
default:
ASSERT( false,"Weirdy type of graviton" );
break;
}
psEffect->rotation.x = rand()%DEG(360);
psEffect->rotation.z = rand()%DEG(360);
psEffect->rotation.y = rand()%DEG(360);
psEffect->spin.x = rand()%DEG(100) + DEG(20);
psEffect->spin.z = rand()%DEG(100) + DEG(20);
psEffect->spin.y = rand()%DEG(100) + DEG(20);
/* Gravitons are essential */
SET_ESSENTIAL(psEffect);
if(psEffect->type == GRAVITON_TYPE_GIBLET)
{
psEffect->frameDelay = (UWORD)GRAVITON_BLOOD_DELAY;
}
else
{
psEffect->frameDelay = (UWORD)GRAVITON_FRAME_DELAY;
}
}
void effectSetupExplosion(EFFECT *psEffect)
{
/* Get an imd if it's not established */
if(psEffect->imd == NULL)
{
switch(psEffect->type)
{
case EXPLOSION_TYPE_SMALL:
psEffect->imd = getImdFromIndex(MI_EXPLOSION_SMALL);
psEffect->size = (UBYTE)((6*EXPLOSION_SIZE)/5);
break;
case EXPLOSION_TYPE_VERY_SMALL:
psEffect->imd = getImdFromIndex(MI_EXPLOSION_SMALL);
psEffect->size = (UBYTE)(BASE_FLAME_SIZE + auxVar);
break;
case EXPLOSION_TYPE_MEDIUM:
psEffect->imd = getImdFromIndex(MI_EXPLOSION_MEDIUM);
psEffect->size = (UBYTE)EXPLOSION_SIZE;
break;
case EXPLOSION_TYPE_LARGE:
psEffect->imd = getImdFromIndex(MI_EXPLOSION_MEDIUM);
psEffect->size = (UBYTE)EXPLOSION_SIZE*2;
break;
case EXPLOSION_TYPE_FLAMETHROWER:
psEffect->imd = getImdFromIndex(MI_FLAME);
psEffect->size = (UBYTE)(BASE_FLAME_SIZE + auxVar);
break;
case EXPLOSION_TYPE_LASER:
psEffect->imd = getImdFromIndex(MI_FLAME); // change this
psEffect->size = (UBYTE)(BASE_LASER_SIZE + auxVar);
break;
case EXPLOSION_TYPE_DISCOVERY:
psEffect->imd = getImdFromIndex(MI_TESLA); // change this
psEffect->size = DISCOVERY_SIZE;
break;
case EXPLOSION_TYPE_FLARE:
psEffect->imd = getImdFromIndex(MI_MFLARE);
psEffect->size = FLARE_SIZE;
break;
case EXPLOSION_TYPE_TESLA:
psEffect->imd = getImdFromIndex(MI_TESLA);
psEffect->size = TESLA_SIZE;
psEffect->velocity.y = (float)TESLA_SPEED;
break;
case EXPLOSION_TYPE_KICKUP:
psEffect->imd = getImdFromIndex(MI_KICK);
psEffect->size = 100;
break;
case EXPLOSION_TYPE_PLASMA:
psEffect->imd = getImdFromIndex(MI_PLASMA);
psEffect->size = BASE_PLASMA_SIZE;
psEffect->velocity.y = 0.0f;
break;
case EXPLOSION_TYPE_LAND_LIGHT:
psEffect->imd = getImdFromIndex(MI_LANDING);
psEffect->size = 120;
psEffect->specific = ellSpec;
psEffect->velocity.y = 0.0f;
SET_ESSENTIAL(psEffect); // Landing lights are permanent and cyclic
break;
case EXPLOSION_TYPE_SHOCKWAVE:
psEffect->imd = getImdFromIndex(MI_SHOCK);
psEffect->size = 50;
psEffect->velocity.y = 0.0f;
break;
default:
break;
}
}
if(psEffect->type == EXPLOSION_TYPE_FLAMETHROWER)
{
psEffect->frameDelay = 45;
}
/* Set how long it lasts */
else if(psEffect->type == EXPLOSION_TYPE_LASER)
{
psEffect->frameDelay = EXPLOSION_FRAME_DELAY/2;
}
else
if(psEffect->type==EXPLOSION_TYPE_TESLA)
{
psEffect->frameDelay = EXPLOSION_TESLA_FRAME_DELAY;
}
else
if(psEffect->type==EXPLOSION_TYPE_PLASMA)
{
psEffect->frameDelay = EXPLOSION_PLASMA_FRAME_DELAY;
}
else
if(psEffect->type == EXPLOSION_TYPE_LAND_LIGHT)
{
psEffect->frameDelay = 120;
}
else
{
psEffect->frameDelay = (UWORD)EXPLOSION_FRAME_DELAY;
}
if(psEffect->type == EXPLOSION_TYPE_SHOCKWAVE)
{
psEffect->lifeSpan = GAME_TICKS_PER_SEC;
}
else
{
psEffect->lifeSpan = (psEffect->frameDelay * psEffect->imd->numFrames);
}
if ( (psEffect->type!=EXPLOSION_TYPE_NOT_FACING) && (psEffect->type!=EXPLOSION_TYPE_SHOCKWAVE))
{
SET_FACING(psEffect);
}
/* Randomly flip x and y for variation */
if(ONEINTWO)
{
SET_FLIPPED_X(psEffect);
}
if(ONEINTWO)
{
SET_FLIPPED_Y(psEffect);
}
}
void effectSetupConstruction(EFFECT *psEffect)
{
psEffect->velocity.x = 0.f;//(1-rand()%3);
psEffect->velocity.z = 0.f;//(1-rand()%3);
psEffect->velocity.y = (float)(0 - rand() % 3);
psEffect->frameDelay = (UWORD)CONSTRUCTION_FRAME_DELAY;
psEffect->imd = getImdFromIndex(MI_CONSTRUCTION);
psEffect->lifeSpan = CONSTRUCTION_LIFESPAN;
/* These effects always face you */
SET_FACING(psEffect);
/* It's a cyclic anim - dies on age */
SET_CYCLIC(psEffect);
/* Randomly flip the construction graphics in x and y for variation */
if(ONEINTWO)
{
SET_FLIPPED_X(psEffect);
}
if(ONEINTWO)
{
SET_FLIPPED_Y(psEffect);
}
}
void effectSetupFire(EFFECT *psEffect)
{
psEffect->frameDelay = 300; // needs to be investigated...
psEffect->radius = auxVar; // needs to be investigated
psEffect->lifeSpan = (UWORD)auxVarSec;
psEffect->birthTime = graphicsTime;
SET_ESSENTIAL(psEffect);
}
void effectSetupWayPoint(EFFECT *psEffect)
{
psEffect->imd = pProximityMsgIMD;
/* These effects musnt make way for others */
SET_ESSENTIAL(psEffect);
}
static void effectSetupBlood(EFFECT *psEffect)
{
psEffect->frameDelay = BLOOD_FRAME_DELAY;
psEffect->velocity.y = (float)BLOOD_FALL_SPEED;
psEffect->imd = getImdFromIndex(MI_BLOOD);
psEffect->size = (UBYTE)BLOOD_SIZE;
}
static void effectSetupDestruction(EFFECT *psEffect)
{
if(psEffect->type == DESTRUCTION_TYPE_SKYSCRAPER)
{
psEffect->lifeSpan = (3*GAME_TICKS_PER_SEC)/2 + (rand()%GAME_TICKS_PER_SEC);
psEffect->frameDelay = DESTRUCTION_FRAME_DELAY/2;
}
else if(psEffect->type == DESTRUCTION_TYPE_DROID)
{
/* It's all over quickly for droids */
psEffect->lifeSpan = DROID_DESTRUCTION_DURATION;
psEffect->frameDelay = DESTRUCTION_FRAME_DELAY;
}
else if(psEffect->type == DESTRUCTION_TYPE_WALL_SECTION ||
psEffect->type == DESTRUCTION_TYPE_FEATURE)
{
psEffect->lifeSpan = STRUCTURE_DESTRUCTION_DURATION/4;
psEffect->frameDelay = DESTRUCTION_FRAME_DELAY/2;
}
else if(psEffect->type == DESTRUCTION_TYPE_POWER_STATION)
{
psEffect->lifeSpan = STRUCTURE_DESTRUCTION_DURATION/2;
psEffect->frameDelay = DESTRUCTION_FRAME_DELAY/4;
}
else
{
/* building's destruction is longer */
psEffect->lifeSpan = STRUCTURE_DESTRUCTION_DURATION;
psEffect->frameDelay = DESTRUCTION_FRAME_DELAY/2;
}
}
#define SMOKE_SHIFT (16 - (rand()%32))
void initPerimeterSmoke(iIMDShape *pImd, Vector3i base)
{
int i;
int shift = SMOKE_SHIFT;
int inStart = pImd->min.z - 16, inEnd = pImd->max.z + 16;
int varStart = pImd->min.x - 16, varEnd = pImd->max.x + 16, varStride = 24;
for (i = varStart; i < varEnd; i += varStride)
{
Vector3i pos = base + Vector3i(i + shift, 0, inStart + shift);
if (rand()%6 == 1)
{
addEffect(&pos, EFFECT_EXPLOSION, EXPLOSION_TYPE_SMALL, false, NULL, 0);
}
else
{
addEffect(&pos, EFFECT_SMOKE, SMOKE_TYPE_BILLOW, false, NULL, 0);
}
pos = base + Vector3i(i + shift, 0, inEnd + shift);
if (rand()%6 == 1)
{
addEffect(&pos, EFFECT_EXPLOSION, EXPLOSION_TYPE_SMALL, false, NULL, 0);
}
else
{
addEffect(&pos, EFFECT_SMOKE, SMOKE_TYPE_BILLOW, false, NULL, 0);
}
}
varStart = pImd->min.z - 16;
varEnd = pImd->max.z + 16;
varStride = 24;
inStart = pImd->min.x - 16;
inEnd = pImd->max.x + 16;
for (i = varStart; i < varEnd; i += varStride)
{
Vector3i pos = base + Vector3i(inStart + shift, 0, i + shift);
if (rand()%6 == 1)
{
addEffect(&pos, EFFECT_EXPLOSION, EXPLOSION_TYPE_SMALL, false, NULL, 0);
}
else
{
addEffect(&pos, EFFECT_SMOKE, SMOKE_TYPE_BILLOW, false, NULL, 0);
}
pos = base + Vector3i(inEnd + shift, 0, i + shift);
if (rand()%6 == 1)
{
addEffect(&pos, EFFECT_EXPLOSION, EXPLOSION_TYPE_SMALL, false, NULL, 0);
}
else
{
addEffect(&pos, EFFECT_SMOKE, SMOKE_TYPE_BILLOW, false, NULL, 0);
}
}
}
static UDWORD effectGetNumFrames(EFFECT *psEffect)
{
if (psEffect->imd)
{
return psEffect->imd->numFrames;
}
else
{
return 0;
}
}
void effectGiveAuxVar( UDWORD var)
{
auxVar = var;
}
void effectGiveAuxVarSec( UDWORD var)
{
auxVarSec = var;
}
/** Runs all the spot effect stuff for the droids - adding of dust and the like... */
static void effectDroidUpdates(void)
{
unsigned int i;
/* Go through all players */
for (i = 0; i < MAX_PLAYERS; i++)
{
DROID *psDroid;
/* Now go through all their droids */
for (psDroid = apsDroidLists[i]; psDroid; psDroid = psDroid->psNext)
{
/* Gets it's group number */
unsigned int partition = psDroid->id % EFFECT_DROID_DIVISION;
/* Right frame to process? */
if (partition == frameGetFrameNumber() % EFFECT_DROID_DIVISION && ONEINFOUR)
{
/* Sufficent time since last update? - The EQUALS comparison is needed */
if (graphicsTime >= lastUpdateDroids[partition] + DROID_UPDATE_INTERVAL)
{
/* Store away when we last processed this group */
lastUpdateDroids[partition] = graphicsTime;
/* Now add some dust at it's arse end if it's moving or skidding.
The check that it's not 0 is probably not sufficient.
*/
if ((int)psDroid->sMove.speed != 0)
{
/* Present direction is important */
Vector2i behind = iSinCosR(psDroid->rot.direction, 50);
Vector3i pos(
clip(psDroid->pos.x - behind.x, 0, mapWidth),
clip(psDroid->pos.y - behind.y, 0, mapHeight),
0
);
pos.z = map_Height(pos.x, pos.y);
// FIXME This does not do anything!!
}
}
}
}
}
}
/** Runs all the structure effect stuff - steam puffing out etc */
static void effectStructureUpdates()
{
unsigned curPartition = frameGetFrameNumber() % EFFECT_STRUCTURE_DIVISION;
// Is it the right time?
if (graphicsTime <= lastUpdateStructures[curPartition] + STRUCTURE_UPDATE_INTERVAL)
{
return;
}
// Store away the last update time.
lastUpdateStructures[curPartition] = graphicsTime;
/* Go thru' all players */
for (unsigned player = 0; player < MAX_PLAYERS; ++player)
{
for (STRUCTURE *psStructure = apsStructLists[player]; psStructure != nullptr; psStructure = psStructure->psNext)
{
// Find its group.
unsigned int partition = psStructure->id % EFFECT_STRUCTURE_DIVISION;
/* Is it the right frame? */
if (partition != curPartition)
{
continue;
}
if (psStructure->status != SS_BUILT || !psStructure->visible[selectedPlayer])
{
continue;
}
/* Factories puff out smoke, power stations puff out tesla stuff */
switch (psStructure->pStructureType->type)
{
case REF_FACTORY:
case REF_CYBORG_FACTORY:
case REF_VTOL_FACTORY:
/*
We're a factory, so better puff out a bit of steam
Complete hack with the magic numbers - just for IAN demo
*/
if (psStructure->sDisplay.imd->nconnectors == 1)
{
Vector3i eventPos = swapYZ(psStructure->pos) + Vector3i(
psStructure->sDisplay.imd->connectors->x,
psStructure->sDisplay.imd->connectors->z,
-psStructure->sDisplay.imd->connectors->y
);
addEffect(&eventPos, EFFECT_SMOKE, SMOKE_TYPE_STEAM, false, NULL, 0);
audio_PlayObjStaticTrack(psStructure, ID_SOUND_STEAM);
}
break;
case REF_POWER_GEN:
{
Vector3i eventPos = swapYZ(psStructure->pos);
// Add an effect over the central spire.
eventPos.y = psStructure->pos.z + 48;
addEffect(&eventPos, EFFECT_EXPLOSION, EXPLOSION_TYPE_TESLA, false, NULL, 0);
audio_PlayObjStaticTrack(psStructure, ID_SOUND_POWER_SPARK);
break;
}
default:
break;
}
}
}
}
void effectResetUpdates(void)
{
memset(lastUpdateDroids, 0, sizeof(lastUpdateDroids));
memset(lastUpdateStructures, 0, sizeof(lastUpdateStructures));
}
/** This will save out the effects data */
bool writeFXData(const char *fileName)
{
EFFECT *it;
int i = 0;
WzConfig ini(fileName);
for (it = activeList.first; it != NULL; it = it->next, i++)
{
ini.beginGroup("effect_" + QString::number(i));
ini.setValue("control", it->control);
ini.setValue("group", it->group);
ini.setValue("type", it->type);
ini.setValue("frameNumber", it->frameNumber);
ini.setValue("size", it->size);
ini.setValue("baseScale", it->baseScale);
ini.setValue("specific", it->specific);
ini.setVector3f("position", it->position);
ini.setVector3f("velocity", it->velocity);
ini.setVector3i("rotation", it->rotation);
ini.setVector3i("spin", it->spin);
ini.setValue("birthTime", it->birthTime);
ini.setValue("lastFrame", it->lastFrame);
ini.setValue("frameDelay", it->frameDelay);
ini.setValue("lifeSpan", it->lifeSpan);
ini.setValue("radius", it->radius);
const QString &imd_name = modelName(it->imd);
if (!imd_name.isEmpty())
{
ini.setValue("imd_name", imd_name);
}
// Move on to reading the next effect
ini.endGroup();
}
// Everything is just fine!
return true;
}
/** This will read in the effects data */
bool readFXData(const char *fileName)
{
// Clear out anything that's there already!
initEffectsSystem();
WzConfig ini(fileName, WzConfig::ReadOnly);
QStringList list = ini.childGroups();
for (int i = 0; i < list.size(); ++i)
{
ini.beginGroup(list[i]);
EFFECT *curEffect = Effect_malloc();
curEffect->control = ini.value("control").toInt();
curEffect->group = (EFFECT_GROUP)ini.value("group").toInt();
curEffect->type = (EFFECT_TYPE)ini.value("type").toInt();
curEffect->frameNumber = ini.value("frameNumber").toInt();
curEffect->size = ini.value("size").toInt();
curEffect->baseScale = ini.value("baseScale").toInt();
curEffect->specific = ini.value("specific").toInt();
curEffect->position = ini.vector3f("position");
curEffect->velocity = ini.vector3f("velocity");
curEffect->rotation = ini.vector3i("rotation");
curEffect->spin = ini.vector3i("spin");
curEffect->birthTime = ini.value("birthTime").toInt();
curEffect->lastFrame = ini.value("lastFrame").toInt();
curEffect->frameDelay = ini.value("frameDelay").toInt();
curEffect->lifeSpan = ini.value("lifeSpan").toInt();
curEffect->radius = ini.value("radius").toInt();
if (ini.contains("imd_name"))
{
QString imd_name = ini.value("imd_name").toString();
if (!imd_name.isEmpty())
{
curEffect->imd = modelGet(imd_name);
}
}
else
{
curEffect->imd = NULL;
}
// Move on to reading the next effect
ini.endGroup();
}
/* Hopefully everything's just fine by now */
return true;
}