Logo Search packages:      
Sourcecode: warzone2100 version File versions

effects.c

/*
      This file is part of Warzone 2100.
      Copyright (C) 1999-2004  Eidos Interactive
      Copyright (C) 2005-2007  Warzone Resurrection 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/frame.h"
#include "lib/framework/frameresource.h"
#include "lib/framework/input.h"
#include "lib/framework/tagfile.h"
#include "lib/framework/math-help.h"

#include "lib/ivis_common/ivisdef.h" //ivis matrix code
#include "lib/ivis_common/piedef.h" //ivis matrix code
#include "lib/ivis_common/piepalette.h"
#include "lib/ivis_common/piestate.h"
#include "lib/ivis_opengl/piematrix.h"
#include "lib/ivis_common/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"


#define MAX_EFFECTS     2500

#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 SET_LITABS(x)               ((x.control) = (UBYTE)(x.control | EFFECT_LIT))

#define MINIMUM_IMPACT_VELOCITY           (16)
#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     LIMITED_EXPLOSION_SIZE        (100+(5-rand()%10))
#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


extern UWORD OffScreenEffects;


/* Our list of all game world effects */
static EFFECT        asEffectsList[MAX_EFFECTS];
static EFFECT_STATUS effectStatus[MAX_EFFECTS];

#define FIREWORK_EXPLODE_HEIGHT                 400
#define STARBURST_RADIUS                        150
#define STARBURST_DIAMETER                      300

#define     EFFECT_SMOKE_ADDITIVE               1
#define     EFFECT_STEAM_ADDITIVE               8
#define     EFFECT_WAYPOINT_ADDITIVE            32
#define     EFFECT_EXPLOSION_ADDITIVE           164
#define EFFECT_PLASMA_ADDITIVE                  224
#define     EFFECT_SMOKE_TRANSPARENCY           130
#define     EFFECT_STEAM_TRANSPARENCY           128
#define     EFFECT_WAYPOINT_TRANSPARENCY  128
#define     EFFECT_BLOOD_TRANSPARENCY           128
#define     EFFECT_EXPLOSION_TRANSPARENCY 164

#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

/* Tick counts for updates on a particular interval */
static      UDWORD      lastUpdateDroids[EFFECT_DROID_DIVISION];
static      UDWORD      lastUpdateStructures[EFFECT_STRUCTURE_DIVISION];

static      UDWORD      freeEffect; /* Current next slot to use - cyclic */
static      UDWORD      numEffects;
static      UDWORD      activeEffects;
static      UDWORD      missCount;
static      UDWORD      skipped,skippedEffects,letThrough;
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      aeCalls;
static      UDWORD      specifiedSize;
static  UDWORD    ellSpec;

// ----------------------------------------------------------------------------------------
/* 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   ( EFFECT *psEffect );
static void renderSmokeEffect       ( EFFECT *psEffect );
static void renderGravitonEffect    ( EFFECT *psEffect );
static void renderConstructionEffect( EFFECT *psEffect );
static void renderWaypointEffect    ( EFFECT *psEffect );
static void renderBloodEffect       ( EFFECT *psEffect );
static void renderDestructionEffect ( EFFECT *psEffect );
static void renderFirework                ( EFFECT *psEffect );
static void positionEffect(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 );
#ifdef DEBUG
static BOOL validatePie( EFFECT_GROUP group, iIMDShape *pie );
#endif
static void effectStructureUpdates(void);
static void effectDroidUpdates(void);

static void positionEffect(EFFECT *psEffect)
{
      Vector3i dv;
      SDWORD rx, rz;

      /* Establish world position */
      dv.x = (psEffect->position.x - player.p.x) - terrainMidX * TILE_UNITS;
      dv.y = psEffect->position.y;
      dv.z = terrainMidY * TILE_UNITS - (psEffect->position.z - player.p.z);

      /* Push the indentity matrix */
      iV_MatrixBegin();

      /* Move to position */
      iV_TRANSLATE(dv.x, dv.y, dv.z);

      /* Get the x,z translation components */
      rx = map_round(player.p.x);
      rz = map_round(player.p.z);

      /* Move to camera reference */
      iV_TRANSLATE(rx, 0, -rz);
}

static void killEffect(EFFECT *e)
{
      effectStatus[e-asEffectsList] = ES_INACTIVE;
      e->control = (UBYTE) 0;
}

// ----------------------------------------------------------------------------------------
static BOOL essentialEffect(EFFECT_GROUP group, EFFECT_TYPE type)
{
      switch(group)
      {
      case  EFFECT_FIRE:
      case  EFFECT_WAYPOINT:
      case  EFFECT_DESTRUCTION:
      case  EFFECT_SAT_LASER:
      case  EFFECT_STRUCTURE:
            return(true);
            break;
      case  EFFECT_EXPLOSION:
            if(type == EXPLOSION_TYPE_LAND_LIGHT)
            {
                  return(true);
            }
            else
            {
                  return(false);
            }
      default:
            return(false);
            break;
      }
}

static BOOL utterlyReject( EFFECT_GROUP group )
{
      switch(group)
      {
      case EFFECT_BLOOD:
      case EFFECT_DUST_BALL:
      case EFFECT_CONSTRUCTION:
            return(true);
      default:
            return(false);
            break;
      }
}

// ----------------------------------------------------------------------------------------
/*    Simply sets the free pointer to the start - actually this isn't necessary
      as it will work just fine anyway. This WOULD be necessary were we to change
      the system so that it seeks FREE slots rather than the oldest one. This is because
      different effects last for different times and the oldest effect may have
      just got going (if it was a long effect).
*/
void  initEffectsSystem( void )
{
UDWORD      i;
EFFECT      *psEffect;


      /* Set position to first */
      freeEffect = 0;

      /* None are active */
      numEffects = 0;

      activeEffects = 0;

      missCount=0;

      skipped = letThrough = 0;

      for(i=0; i<MAX_EFFECTS; i++)
      {
            /* Get a pointer - just cos our macro requires it, speeds not an issue here */
            psEffect = &asEffectsList[i];
            /* Clear all the control bits */
            psEffect->control = (UBYTE)0;
            /* All effects are initially inactive */
            effectStatus[i] = ES_INACTIVE;
      }
}

// ----------------------------------------------------------------------------------------
void  effectSetLandLightSpec(LAND_LIGHT_SPEC spec)
{
      ellSpec = spec;
}
// ----------------------------------------------------------------------------------------
void  effectSetSize(UDWORD size)
{
      specifiedSize = size;
}
// ----------------------------------------------------------------------------------------
void  addMultiEffect(Vector3i *basePos, Vector3i *scatter, EFFECT_GROUP group,
                                 EFFECT_TYPE type,BOOL specified, iIMDShape *imd, UDWORD number, BOOL lit, UDWORD size)
{
UDWORD      i;
Vector3i scatPos;


      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)
      {
            scatPos.x = basePos->x;
            scatPos.y = basePos->y;
            scatPos.z = basePos->z;
            addEffect(&scatPos,group,type,specified,imd,lit);
      }
      else
      {
            /* Fix for jim */
            scatter->x/=10;
            scatter->y/=10;
            scatter->z/=10;

            /* There are multiple effects - so scatter them around according to parameter */
            for(i=0; i<number; i++)
            {
                  scatPos.x = basePos->x + (scatter->x ? ( scatter->x   - (rand()%(2*scatter->x)) ) : 0 );
                  scatPos.y = basePos->y + (scatter->y ? ( scatter->y   - (rand()%(2*scatter->y)) ) : 0 );
                  scatPos.z = basePos->z + (scatter->z ? ( scatter->z   - (rand()%(2*scatter->z)) ) : 0 );
                  addEffect(&scatPos,group,type,specified,imd,lit);
            }
      }
}

// ----------------------------------------------------------------------------------------
UDWORD      getNumActiveEffects( void )
{
      return(activeEffects);
}
// ----------------------------------------------------------------------------------------
UDWORD      getMissCount( void )
{
      return(missCount);
}

UDWORD      getNumSkippedEffects(void)
{
      return(skippedEffects);
}

UDWORD      getNumEvenEffects(void)
{
      return(letThrough);
}
// ----------------------------------------------------------------------------------------

UDWORD Reject1;

void  addEffect(Vector3i *pos, EFFECT_GROUP group, EFFECT_TYPE type,BOOL specified, iIMDShape *imd, BOOL lit)
{
      UDWORD      essentialCount;
      UDWORD      i;
      BOOL  bSmoke;

      aeCalls++;

      if(gamePaused())
      {
            return;
      }

      /* Quick optimsation to reject every second non-essential effect if it's off grid */
      if(clipXY(pos->x, pos->z) == false)
      {
            /*    If effect is essentail - then let it through */
            if(!essentialEffect(group,type) )
            {
                  /* Some we can get rid of right away */
                  if ( utterlyReject( group ) )
                  {
                        skipped++;
                        return;
                  }
                  /* Smoke gets culled more than most off grid effects */
                  if(group == EFFECT_SMOKE)
                  {
                        bSmoke = true;
                  }
                  else
                  {
                        bSmoke = false;
                  }
                  /* Others intermittently (50/50 for most and 25/100 for smoke */
                  if(bSmoke ? (aeCalls & 0x03) : (aeCalls & 0x01) )
                  {
                        /* Do one */
                        skipped++;
                        return;
                  }
                  letThrough++;
            }
      }


      for(i=freeEffect,essentialCount=0; (asEffectsList[i].control & EFFECT_ESSENTIAL)
            && essentialCount<MAX_EFFECTS; i++)
      {
            /* Check for wrap around */
            if(i>= (MAX_EFFECTS-1))
            {
                  /* Go back to the first one */
                  i = 0;
            }
            essentialCount++;
            missCount++;
      }

      /* Check the list isn't just full of essential effects */
      if(essentialCount>=MAX_EFFECTS)
      {
            /* All of the effects are essential!?!? */
            return;
      }
      else
      {
            freeEffect = i;
      }

      /* Store away it's position - into FRACTS */
      asEffectsList[freeEffect].position.x = pos->x;
      asEffectsList[freeEffect].position.y = pos->y;
      asEffectsList[freeEffect].position.z = pos->z;


      /* Now, note group and type */
      asEffectsList[freeEffect].group = group;
      asEffectsList[freeEffect].type = type;

      /* Set when it entered the world */
      asEffectsList[freeEffect].birthTime = asEffectsList[freeEffect].lastFrame = gameTime;

      if(group == EFFECT_GRAVITON && (type == GRAVITON_TYPE_GIBLET || type == GRAVITON_TYPE_EMITTING_DR))
      {
            asEffectsList[freeEffect].frameNumber = lit;
      }

      else
      {
            /* Starts off on frame zero */
            asEffectsList[freeEffect].frameNumber = 0;
      }

      /*
            See what kind of effect it is - the add fucnction is different for each,
            although some things are shared
      */
      asEffectsList[freeEffect].imd = NULL;
      if(lit)
      {
            SET_LITABS(asEffectsList[freeEffect]);
      }

      if(specified)
      {
            /* We're specifying what the imd is - override */
            asEffectsList[freeEffect].imd = imd;
//          if(type == EXPLOSION_TYPE_SPECIFIED_FIXME)
//          {
//                asEffectsList[freeEffect].size = EXPLOSION_SIZE;
//          }
//          else
//          {
            asEffectsList[freeEffect].size =(UWORD) specifiedSize;
//          }
      }

      /* Do all the effect type specific stuff */
      switch(group)
      {
            case EFFECT_SMOKE:
                  effectSetupSmoke(&asEffectsList[freeEffect]);
                  break;
            case EFFECT_GRAVITON:
                  effectSetupGraviton(&asEffectsList[freeEffect]);
                  break;
            case EFFECT_EXPLOSION:
                  effectSetupExplosion(&asEffectsList[freeEffect]);
                  break;
            case EFFECT_CONSTRUCTION:
                  effectSetupConstruction(&asEffectsList[freeEffect]);
                  break;
            case EFFECT_WAYPOINT:
                  effectSetupWayPoint(&asEffectsList[freeEffect]);
                  break;
            case EFFECT_BLOOD:
                  effectSetupBlood(&asEffectsList[freeEffect]);
                  break;
            case EFFECT_DESTRUCTION:
                  effectSetupDestruction(&asEffectsList[freeEffect]);
                  break;
            case EFFECT_FIRE:
                  effectSetupFire(&asEffectsList[freeEffect]);
                  break;
            case EFFECT_SAT_LASER:
                  effectSetUpSatLaser(&asEffectsList[freeEffect]);
                  break;
            case EFFECT_FIREWORK:
                  effectSetUpFirework(&asEffectsList[freeEffect]);
                  break;
            default:
                  ASSERT( false,"Weirdy group type for an effect" );
                  break;
      }

      /* Make the effect active */
      effectStatus[freeEffect] = ES_ACTIVE;

      /* As of yet, it hasn't bounced (or whatever)... */
      if(type!=EXPLOSION_TYPE_LAND_LIGHT)
      {
            asEffectsList[freeEffect].specific = 0;
      }

      /* Looks like we didn't establish an imd for the effect */
      /*
      ASSERT( asEffectsList[freeEffect].imd != NULL || group == EFFECT_DESTRUCTION || group == EFFECT_FIRE || group == EFFECT_SAT_LASER,
            "null effect imd" );
      */

#ifdef DEBUG
      if ( validatePie( group, asEffectsList[freeEffect].imd ) == false )
      {
            ASSERT( false,"No PIE found or specified for an effect" );
      }
#endif

      /* No more slots available? */
      if(freeEffect++ >= (MAX_EFFECTS-1))
      {
            /* Go back to the first one */
            freeEffect = 0;
      }
}

#ifdef DEBUG
// ----------------------------------------------------------------------------------------
static BOOL validatePie( EFFECT_GROUP group, iIMDShape *pie )
{

      /* If we haven't got a pie */
      if(pie == NULL)
      {
            if(group == EFFECT_DESTRUCTION || group == EFFECT_FIRE || group == EFFECT_SAT_LASER)
            {
                  /* Ok in these cases */
                  return(true);
            }

            return(false);
      }
      else
      {
            return(true);
      }
}
// ----------------------------------------------------------------------------------------
#endif

/* Calls all the update functions for each different currently active effect */
void  processEffects(void)
{
      unsigned int i;

      /* Establish how long the last game frame took */
      missCount = 0;

      /* Reset counter */
      numEffects = 0;

      /* Traverse the list */
      for (i = 0; i < MAX_EFFECTS; i++)
      {
            /* Don't bother unless it's active */
            if(effectStatus[i] == ES_ACTIVE)
            {
                  updateEffect(&asEffectsList[i]);
                  /* One more is active */
                  numEffects++;
                  /* Is it on the grid */
                  if (clipXY(asEffectsList[i].position.x, asEffectsList[i].position.z))
                  {
                        /* Add it to the bucket */
                        bucketAddTypeToList(RENDER_EFFECT,&asEffectsList[i]);
                  }

            }
      }

      /* Add any droid effects */
      effectDroidUpdates();

      /* Add any structure effects */
      effectStructureUpdates();

      activeEffects = numEffects;
      skippedEffects = skipped;
}

// ----------------------------------------------------------------------------------------
/* 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);
            break;

      case EFFECT_WAYPOINT:
            if(!gamePaused()) updateWaypoint(psEffect);
            break;

      case EFFECT_CONSTRUCTION:
            if(!gamePaused()) updateConstruction(psEffect);
            break;

      case EFFECT_SMOKE:
            if(!gamePaused()) updatePolySmoke(psEffect);
            break;

      case EFFECT_STRUCTURE:
            break;

      case EFFECT_GRAVITON:
            if(!gamePaused()) updateGraviton(psEffect);
            break;

      case EFFECT_BLOOD:
            if(!gamePaused()) updateBlood(psEffect);
            break;

      case EFFECT_DESTRUCTION:
            if(!gamePaused()) updateDestruction(psEffect);
            break;

      case EFFECT_FIRE:
            if(!gamePaused()) updateFire(psEffect);
            break;

      case EFFECT_SAT_LASER:
            if(!gamePaused()) updateSatLaser(psEffect);
            break;
      case EFFECT_FIREWORK:
            if(!gamePaused()) updateFirework(psEffect);
            break;
      default:
            debug( LOG_ERROR, "Weirdy class of effect passed to updateEffect" );
            abort();
            break;
      }
}

// ----------------------------------------------------------------------------------------
// 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 += timeAdjustedIncrement(psEffect->velocity.x, true);
      psEffect->position.y += timeAdjustedIncrement(psEffect->velocity.y, true);
      psEffect->position.z += timeAdjustedIncrement(psEffect->velocity.z, true);

      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 = radius * (SIN(DEG(val)));
                              yDif = radius * (COS(DEG(val)));
                              xDif = xDif/4096;  // cos it's fixed point
                              yDif = yDif/4096;
                              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(gameTime - psEffect->lastFrame > psEffect->frameDelay)
            {
                  /* Store away last frame change time */
                  psEffect->lastFrame = gameTime;

                  /* 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(gameTime - 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;
      iIMDShape   *pie;
      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;
            pie = getImdFromIndex(MI_FLAME);

            /* 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 = radius * (SIN(DEG(val)));
                        yDif = radius * (COS(DEG(val)));
                        xDif = xDif/4096;  // cos it's fixed point
                        yDif = yDif/4096;
                        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(gameTime-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(gameTime-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_LAND_LIGHT)
      {
            light.position.x = psEffect->position.x;
            light.position.y = psEffect->position.y;
            light.position.z = psEffect->position.z;
            light.range = getTimeValueRange(1024,512);
            if(light.range>256) light.range = 512-light.range;
            light.colour = LIGHT_RED;
            processLight(&light);
      }
*/

      if(psEffect->type == EXPLOSION_TYPE_SHOCKWAVE)
      {
            psEffect->size += timeAdjustedIncrement(SHOCKWAVE_SPEED, true);
            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
            if(gameTime - psEffect->lastFrame > psEffect->frameDelay)
            {
                  psEffect->lastFrame = gameTime;
                  /* 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 += timeAdjustedIncrement(psEffect->velocity.y, true);
            }

      }
}

// ----------------------------------------------------------------------------------------
/* The update function for blood */
static void updateBlood(EFFECT *psEffect)
{
      /* Time to update the frame number on the blood */
      if(gameTime - psEffect->lastFrame > psEffect->frameDelay)
      {
            psEffect->lastFrame = gameTime;
            /* 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 += timeAdjustedIncrement(psEffect->velocity.x, true);
      psEffect->position.y += timeAdjustedIncrement(psEffect->velocity.y, true);
      psEffect->position.z += timeAdjustedIncrement(psEffect->velocity.z, true);
}

// ----------------------------------------------------------------------------------------
/* 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 */
      if(gameTime - psEffect->lastFrame > psEffect->frameDelay)
      {
            /* Store away last frame change time */
            psEffect->lastFrame = gameTime;

            /* 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 += timeAdjustedIncrement(psEffect->velocity.x, true);
      psEffect->position.y += timeAdjustedIncrement(psEffect->velocity.y, true);
      psEffect->position.z += timeAdjustedIncrement(psEffect->velocity.z, true);

      /* If it doesn't get killed by frame number, then by age */
      if(TEST_CYCLIC(psEffect))
      {
            /* Has it overstayed it's welcome? */
            if(gameTime - 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;
      UDWORD      groundHeight;
      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 += timeAdjustedIncrement(psEffect->velocity.x, true);
            psEffect->position.y += timeAdjustedIncrement(psEffect->velocity.y, true);
            psEffect->position.z += timeAdjustedIncrement(psEffect->velocity.z, true);

      /* 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;
      }

      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(gameTime>psEffect->lastFrame+psEffect->frameDelay)
            {
                  /* Store away last update */
                  psEffect->lastFrame = gameTime;

                  /* 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(gameTime>psEffect->lastFrame+psEffect->frameDelay)
            {
                  /* Store away last update */
                  psEffect->lastFrame = gameTime;

                  /* 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 += timeAdjustedIncrement(psEffect->spin.x, true);
      psEffect->rotation.y += timeAdjustedIncrement(psEffect->spin.y, true);
      psEffect->rotation.z += timeAdjustedIncrement(psEffect->spin.z, true);

      /* Update velocity (and retarding of descent) according to present frame rate */
      accel = timeAdjustedIncrement(GRAVITON_GRAVITY, true);
      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;
                  }
            }
      }
}


// ----------------------------------------------------------------------------------------
/* updateDestruction
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;
      UDWORD      percent;
      UDWORD      range;
      float div;
      UDWORD      height;

      percent = PERCENT(gameTime-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(gameTime > (psEffect->birthTime + psEffect->lifeSpan))
      {
            /* Kill it - it's too old */
            killEffect(psEffect);
            return;
      }

      if(psEffect->type == DESTRUCTION_TYPE_SKYSCRAPER)
      {

            if((gameTime - psEffect->birthTime) > (unsigned int)((9*psEffect->lifeSpan)/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)(gameTime - 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((gameTime - psEffect->lastFrame) > psEffect->frameDelay)
      {
            psEffect->lastFrame = gameTime;
            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( gameTime < ((3*(psEffect->birthTime + psEffect->lifeSpan)/4)) )
                  {
                        iX = psEffect->position.x;
                        iY = psEffect->position.z;
                        audio_PlayStaticTrack( iX, iY, ID_SOUND_EXPLOSION );
                  }
                  break;

            }
      }
}

// ----------------------------------------------------------------------------------------
/*
updateConstruction:-
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(gameTime - psEffect->lastFrame > psEffect->frameDelay)
      {
            psEffect->lastFrame = gameTime;
            /* 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 += timeAdjustedIncrement(psEffect->velocity.x, true);
      psEffect->position.y += timeAdjustedIncrement(psEffect->velocity.y, true);
      psEffect->position.z += timeAdjustedIncrement(psEffect->velocity.z, true);


      /* 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(gameTime - psEffect->birthTime > psEffect->lifeSpan)
            {
                  killEffect(psEffect);
                  return;
            }
      }
}

// ----------------------------------------------------------------------------------------
/* Update fire sequences */
static void updateFire(EFFECT *psEffect)
{
      Vector3i pos;
      LIGHT light;
      UDWORD      percent;

      percent = PERCENT(gameTime-psEffect->birthTime,psEffect->lifeSpan);
      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(gameTime - psEffect->lastFrame > psEffect->frameDelay)
      {
            psEffect->lastFrame = gameTime;
            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(gameTime - psEffect->birthTime > psEffect->lifeSpan)
      {
            killEffect(psEffect);
            return;
      }
}

// ----------------------------------------------------------------------------------------
// ALL THE RENDER FUNCTIONS
// ----------------------------------------------------------------------------------------
/*
renderEffect:-
Calls the appropriate render routine for each type of effect
*/
void  renderEffect(EFFECT *psEffect)
{
            /* What type of effect are we dealing with? */
      switch(psEffect->group)
      {
      case EFFECT_WAYPOINT:
            renderWaypointEffect(psEffect);
            break;

      case EFFECT_EXPLOSION:
            renderExplosionEffect(psEffect);
            break;

      case EFFECT_CONSTRUCTION:
            renderConstructionEffect(psEffect);
            break;

      case EFFECT_SMOKE:
            renderSmokeEffect(psEffect);
            break;

      case EFFECT_GRAVITON:
            renderGravitonEffect(psEffect);
            break;

      case EFFECT_BLOOD:
            renderBloodEffect(psEffect);
            break;

      case EFFECT_STRUCTURE:
            break;

      case EFFECT_DESTRUCTION:
            /*    There is no display func for a destruction effect -
                  it merely spawn other effects over time */
            renderDestructionEffect(psEffect);
            break;
      case EFFECT_FIRE:
            /* Likewise */
            break;
      case EFFECT_SAT_LASER:
            /* Likewise */
            break;
      case EFFECT_FIREWORK:
            renderFirework(psEffect);
            break;
      default:
            debug( LOG_ERROR, "Weirdy class of effect passed to renderEffect" );
            abort();
            break;
      }
}

// ----------------------------------------------------------------------------------------
/* drawing func for wapypoints . AJL. */
void  renderWaypointEffect(EFFECT *psEffect)
{
      positionEffect(psEffect);

      pie_Draw3DShape(psEffect->imd, 0, 0, WZCOL_WHITE, WZCOL_BLACK, 0, 0);
      iV_MatrixEnd();
}

// ----------------------------------------------------------------------------------------
void  renderFirework(EFFECT *psEffect)
{
      /* these don't get rendered */
      if(psEffect->type == FIREWORK_TYPE_LAUNCHER)
      {
            return;
      }

      positionEffect(psEffect);

      iV_MatrixRotateY(-player.r.y);
      iV_MatrixRotateX(-player.r.x);

      pie_MatScale(psEffect->size);
      pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, WZCOL_WHITE, WZCOL_BLACK, pie_ADDITIVE, EFFECT_EXPLOSION_ADDITIVE);
      iV_MatrixEnd();
}

// ----------------------------------------------------------------------------------------
/* drawing func for blood. */
void  renderBloodEffect(EFFECT *psEffect)
{
      positionEffect(psEffect);

      iV_MatrixRotateY(-player.r.y);
      iV_MatrixRotateX(-player.r.x);
      pie_MatScale(psEffect->size);

      pie_Draw3DShape(getImdFromIndex(MI_BLOOD), psEffect->frameNumber, 0, WZCOL_WHITE, WZCOL_BLACK, pie_TRANSLUCENT, EFFECT_BLOOD_TRANSPARENCY);
      iV_MatrixEnd();
}

// ----------------------------------------------------------------------------------------
void  renderDestructionEffect(EFFECT *psEffect)
{
      float div;
      SDWORD      percent;

      if(psEffect->type!=DESTRUCTION_TYPE_SKYSCRAPER)
      {
            return;
      }

      positionEffect(psEffect);

      div = (float)(gameTime - psEffect->birthTime) / psEffect->lifeSpan;
      if(div>1.0) div = 1.0;  //temporary!
      {
            div = 1.0 - div;
            percent = (SDWORD)(div*pie_RAISE_SCALE);
      }

      if(!gamePaused())
      {
            iV_MatrixRotateX(SKY_SHIMMY);
            iV_MatrixRotateY(SKY_SHIMMY);
            iV_MatrixRotateZ(SKY_SHIMMY);
      }
      pie_Draw3DShape(psEffect->imd, 0, 0, WZCOL_WHITE, WZCOL_BLACK, pie_RAISE, percent);

      iV_MatrixEnd();
}

// ----------------------------------------------------------------------------------------
static BOOL rejectLandLight(LAND_LIGHT_SPEC type)
{
UDWORD      timeSlice;

      timeSlice = gameTime%2000;
      if(timeSlice<400)
      {
            if(type == LL_MIDDLE) return(false); else return(true);     // reject all expect middle
      }
      else if(timeSlice<800)
      {
            if(type == LL_OUTER) return(true); else return(false);      // reject only outer
      }
      else if(timeSlice<1200)
      {
            return(false);    //reject none
      }
      else if(timeSlice<1600)
      {
            if(type == LL_OUTER) return(true); else return(false);      // reject only outer
      }
      else
      {
            if(type == LL_MIDDLE) return(false); else return(true);     // reject all expect middle
      }
}

// ----------------------------------------------------------------------------------------
/* Renders the standard explosion effect */
void  renderExplosionEffect(EFFECT *psEffect)
{
      SDWORD      percent;
      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! */
/*          TEST_FLIPPED_Y(psEffect) ? iV_MatrixRotateY(-player.r.y+iV_DEG(180)) :*/ iV_MatrixRotateY(-player.r.y);
/*          TEST_FLIPPED_X(psEffect) ? iV_MatrixRotateX(-player.r.x+iV_DEG(180)) :*/ iV_MatrixRotateX(-player.r.x);
      }

      /* Tesla explosions diminish in size */
      if(psEffect->type == EXPLOSION_TYPE_TESLA)
      {
            percent = PERCENT((gameTime - psEffect->birthTime),psEffect->lifeSpan);
            if(percent<0) percent = 0;
            if(percent>45) percent = 45;
            pie_MatScale(psEffect->size - percent);
      }
      else if(psEffect->type == EXPLOSION_TYPE_PLASMA)
      {
            percent = PERCENT(gameTime - psEffect->birthTime, psEffect->lifeSpan) / 3;
            pie_MatScale(BASE_PLASMA_SIZE + percent);
      }
      else
      {
            pie_MatScale(psEffect->size);
      }

      if(psEffect->type == EXPLOSION_TYPE_PLASMA)
      {
            pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, WZCOL_BLACK, pie_ADDITIVE, EFFECT_PLASMA_ADDITIVE);
      }
      else if(psEffect->type == EXPLOSION_TYPE_KICKUP)
      {
            pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, WZCOL_BLACK, pie_TRANSLUCENT, 128);
      }
      else
      {
            pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, WZCOL_BLACK, pie_ADDITIVE, EFFECT_EXPLOSION_ADDITIVE);
      }

      iV_MatrixEnd();
}

// ----------------------------------------------------------------------------------------
void  renderGravitonEffect(EFFECT *psEffect)
{

      positionEffect(psEffect);

      iV_MatrixRotateX(psEffect->rotation.x);
      iV_MatrixRotateY(psEffect->rotation.y);
      iV_MatrixRotateZ(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);
      }
      else
      {
            pie_MatScale(100);
      }

      pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, WZCOL_WHITE, WZCOL_BLACK, 0, 0);

      /* Pop the matrix */
      iV_MatrixEnd();
}

// ----------------------------------------------------------------------------------------
/*
renderConstructionEffect:-
Renders the standard construction effect */
void  renderConstructionEffect(EFFECT *psEffect)
{
      Vector3i null;
      SDWORD      percent;
      UDWORD      translucency;
      UDWORD      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))
      {
/*          TEST_FLIPPED_Y(psEffect) ? iV_MatrixRotateY(-player.r.y+iV_DEG(180)) :*/ iV_MatrixRotateY(-player.r.y);
/*          TEST_FLIPPED_X(psEffect) ? iV_MatrixRotateX(-player.r.x+iV_DEG(180)) :*/ iV_MatrixRotateX(-player.r.x);
      }

      /* Scale size according to age */
      percent = PERCENT(gameTime - 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 = 2*translucency;
      if(size>90) size = 90;
      pie_MatScale(size);

      pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, WZCOL_WHITE, WZCOL_BLACK, pie_TRANSLUCENT, (UBYTE)(translucency));

      /* Pop the matrix */
      iV_MatrixEnd();
}

// ----------------------------------------------------------------------------------------
/*
renderSmokeEffect:-
Renders the standard smoke effect - it is now scaled in real-time as well
*/
void  renderSmokeEffect(EFFECT *psEffect)
{
      UDWORD      transparency = 0;
      const PIELIGHT brightness = WZCOL_WHITE;
      const PIELIGHT specular = WZCOL_BLACK;

      positionEffect(psEffect);

      /* Bit in comments doesn't quite work yet? */
      if(TEST_FACING(psEffect))
      {
            /* Always face the viewer! */
/*          TEST_FLIPPED_Y(psEffect) ? iV_MatrixRotateY(-player.r.y+iV_DEG(180)) : */iV_MatrixRotateY(-player.r.y);
/*          TEST_FLIPPED_X(psEffect) ? iV_MatrixRotateX(-player.r.x+iV_DEG(180)) : */iV_MatrixRotateX(-player.r.x);
      }

      /* Small smoke - used for the droids */
//          if(psEffect->type == SMOKE_TYPE_DRIFTING_SMALL || psEffect->type == SMOKE_TYPE_TRAIL)

      if(TEST_SCALED(psEffect))
      {
            const unsigned int lifetime = gameTime - psEffect->birthTime;
            unsigned int percent;

            // Since psEffect->birthTime will be set to gameTime on
            // creation, and gameTime only increments, birthTime should be
            // smaller than or equal to gameTime. As a great man once said
            // though (hehe, I just love exaggarating ;-) -- Giel):
            // "Always assert your assumptions!". So here it goes:
            assert(psEffect->birthTime <= gameTime);

            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);
            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, specular, pie_TRANSLUCENT, (UBYTE)(EFFECT_STEAM_TRANSPARENCY)/2);
      }
      else
      {
            if(psEffect->type == SMOKE_TYPE_TRAIL)
            {
                  pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, specular, pie_TRANSLUCENT, (UBYTE)((2*transparency)/3));
            }
            else
            {
                  pie_Draw3DShape(psEffect->imd, psEffect->frameNumber, 0, brightness, specular, pie_TRANSLUCENT, (UBYTE)(transparency)/2);
            }
      }

      /* Pop the matrix */
      iV_MatrixEnd();
}


// ----------------------------------------------------------------------------------------
// ALL THE SETUP FUNCTIONS
// ----------------------------------------------------------------------------------------
void  effectSetUpFirework(EFFECT *psEffect)
{
      UDWORD      camExtra;

      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;
            camExtra = 0;
            if(getCampaignNumber()!=1)
            {
                  camExtra+=rand()%200;
            }
            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 = DEG((rand()%360));
      psEffect->rotation.z = DEG((rand()%360));
      psEffect->rotation.y = DEG((rand()%360));

      psEffect->spin.x = DEG((rand()%100)+20);
      psEffect->spin.z = DEG((rand()%100)+20);
      psEffect->spin.y = DEG((rand()%100)+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);//resGetData("IMD","blbhq.pie");
                  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 = (UWORD)(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 = gameTime;
      SET_ESSENTIAL(psEffect);

}

// ----------------------------------------------------------------------------------------
void  effectSetupWayPoint(EFFECT *psEffect)
{
      psEffect->imd = pProximityMsgIMD;

      /* These effects musnt make way for others */
      SET_ESSENTIAL(psEffect);
}

// ----------------------------------------------------------------------------------------
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;
}

// ----------------------------------------------------------------------------------------
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 FX_PER_EDGE 6
#define     SMOKE_SHIFT (16 - (rand()%32))
// ----------------------------------------------------------------------------------------
void  initPerimeterSmoke(iIMDShape *pImd, UDWORD x, UDWORD y, UDWORD z)
{
      SDWORD      i;
      SDWORD      inStart, inEnd;
      SDWORD      varStart, varEnd, varStride;
      SDWORD      shift = 0;
      Vector3i base;
      Vector3i pos;

      base.x = x;
      base.y = y;
      base.z = z;

      varStart = pImd->min.x -16;
      varEnd = pImd->max.x + 16;
      varStride = 24;//(varEnd-varStart)/FX_PER_EDGE;

      inStart = pImd->min.z - 16;
      inEnd = pImd->max.z + 16;

      for(i=varStart; i<varEnd; i+=varStride)
      {
            shift = SMOKE_SHIFT;
            pos.x = base.x + i + shift;
            pos.y = base.y;
            pos.z = base.z + 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.x = base.x + i + shift;
            pos.y = base.y;
            pos.z = base.z + 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;//(varEnd-varStart)/FX_PER_EDGE;

      inStart = pImd->min.x - 16;
      inEnd = pImd->max.x + 16;


      for(i=varStart; i<varEnd; i+=varStride)
      {
            pos.x = base.x + inStart + shift;
            pos.y = base.y;
            pos.z = base.z + 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.x = base.x + inEnd + shift;
            pos.y = base.y;
            pos.z = base.z + 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);
            }
      }
}

// ----------------------------------------------------------------------------------------
UDWORD      getNumEffects( void )
{
      return(numEffects);
}


// ----------------------------------------------------------------------------------------
UDWORD EffectGetNumFrames(EFFECT *psEffect)
{

      return psEffect->imd->numFrames;

}

UDWORD IMDGetNumFrames(iIMDShape *Shape)
{

      return Shape->numFrames;

}

UDWORD IMDGetAnimInterval(iIMDShape *Shape)
{

      return Shape->animInterval;

}

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(gameTime >= (lastUpdateDroids[partition] + DROID_UPDATE_INTERVAL))
                        {
                              /* Store away when we last processed this group */
                              lastUpdateDroids[partition] = gameTime;

                              /*    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( (SDWORD)psDroid->sMove.speed != 0 )
                              {
                                    /* Present direction is important */
                                    Vector2i behind = {
                                          ( 50 * SIN( DEG( psDroid->direction) ) ) >> FP12_SHIFT,
                                          ( 50 * COS( DEG( psDroid->direction) ) ) >> FP12_SHIFT
                                    };
                                    Vector3i pos;

                                    pos.x = MAX(0, psDroid->pos.x - behind.x);
                                    pos.z =     MAX(0, psDroid->pos.y - behind.y);
                                    pos.y = map_Height(pos.x, pos.z);
                              }
                        }
                  }
            }
      }
}

// ----------------------------------------------------------------------------------------
/* Runs all the structure effect stuff - steam puffing out etc */
static void effectStructureUpdates(void)
{
      UDWORD            i;
      UDWORD            partition;
      STRUCTURE   *psStructure;
      Vector3i eventPos;
      UDWORD            capacity;
      POWER_GEN   *psPowerGen;
      BOOL        active;

      /* Go thru' all players */
      for(i=0; i<MAX_PLAYERS; i++)
      {
            for(psStructure = apsStructLists[i]; psStructure; psStructure = psStructure->psNext)
            {
                  /* Find it's group */
                  partition = psStructure->id % EFFECT_STRUCTURE_DIVISION;
                  /* Is it the right frame? */
                  if(partition == frameGetFrameNumber() % EFFECT_STRUCTURE_DIVISION)
                  {
                        /* Is it the right time? */
                        if(gameTime > (lastUpdateStructures[partition] + STRUCTURE_UPDATE_INTERVAL))
                        {
                              /* Store away the last update time */
                              lastUpdateStructures[partition] = gameTime;
                              // -------------------------------------------------------------------------------
                              /* Factories puff out smoke, power stations puff out tesla stuff */

                              if( (psStructure->pStructureType->type == REF_FACTORY) ||
                                    (psStructure->pStructureType->type == REF_POWER_GEN) )
                              if( (bMultiPlayer && isHumanPlayer(psStructure->player))
                                    || (psStructure->player == 0) )

                              if(psStructure->status==SS_BUILT)
                              if(psStructure->visible[selectedPlayer])
                              {
                                    /*    We're a factory, so better puff out a bit of steam
                                          Complete hack with the magic numbers - just for IAN demo
                                    */
                              if(psStructure->pStructureType->type == REF_FACTORY)
                                    {
                                          if (psStructure->sDisplay.imd->nconnectors == 1)
                                          {
                                                eventPos.x = psStructure->pos.x+psStructure->sDisplay.imd->connectors->x;
                                                eventPos.z = psStructure->pos.y-psStructure->sDisplay.imd->connectors->y;
                                                eventPos.y = psStructure->pos.z+psStructure->sDisplay.imd->connectors->z;
                                                addEffect(&eventPos,EFFECT_SMOKE,SMOKE_TYPE_STEAM,false,NULL,0);


                                                if(selectedPlayer == psStructure->player)
                                                {
                                                      audio_PlayObjStaticTrack( (void *) psStructure, ID_SOUND_STEAM );
                                                }

                                          }
                                    }
                                    else if(psStructure->pStructureType->type == REF_POWER_GEN)
                                    {
                                          psPowerGen = &psStructure->pFunctionality->powerGenerator;
                                          eventPos.x = psStructure->pos.x;
                                          eventPos.z = psStructure->pos.y;
                                          if (psStructure->sDisplay.imd->nconnectors > 0)
                                          {
                                                eventPos.y = psStructure->pos.z+psStructure->sDisplay.imd->connectors->z;
                                          }
                                          else
                                          {
                                                eventPos.y = psStructure->pos.z;
                                          }
                                          capacity = psPowerGen->capacity;
                                          /*if(capacity)
                                          {
                                                eventPos.y = psStructure->pos.z + 48;
                                          }*/
                                          /* Add an effect over the central spire - if
                                          connected to Res Extractor and it is active*/
                                          //look through the list to see if any connected Res Extr
                                          active = false;
                                          for (i=0; i < NUM_POWER_MODULES; i++)
                                          {
                                                if (psPowerGen->apResExtractors[i]
                                                 && psPowerGen->apResExtractors[i]->pFunctionality->resourceExtractor.active)
                                                {
                                                      active = true;
                                                      break;
                                                }
                                          }
                                          /*
                                          if (((POWER_GEN*)psStructure->pFunctionality)->
                                                apResExtractors[0] && ((RES_EXTRACTOR *)((POWER_GEN*)
                                                psStructure->pFunctionality)->apResExtractors[0]->
                                                pFunctionality)->active)
                                          */
                                          //if (active)
                                          {
                                                eventPos.y = psStructure->pos.z + 48;
                                                addEffect(&eventPos,EFFECT_EXPLOSION,
                                                            EXPLOSION_TYPE_TESLA,false,NULL,0);

                                                if(selectedPlayer == psStructure->player)
                                                {
                                                      audio_PlayObjStaticTrack( (void *) psStructure, ID_SOUND_POWER_SPARK );
                                                }

                                          }
                                          /*    Work out how many spires it has. This is a particularly unpleasant
                                                hack and I'm not proud of it, but it needs to done. Honest. AM
                                          */
                                          //if(capacity)

                                    }



                              }
                        }
                  }
            }
      }
}

UDWORD      getFreeEffect( void )
{
      return(freeEffect);
}


// ----------------------------------------------------------------------------------------
void  effectResetUpdates( void )
{
UDWORD      i;

      for(i=0; i<EFFECT_DROID_DIVISION; i++)
      {
            lastUpdateDroids[i] = 0;
      }
      for(i=0; i<EFFECT_STRUCTURE_DIVISION; i++)
      {
            lastUpdateStructures[i] = 0;
      }
}


// -----------------------------------------------------------------------------------
bool fireOnLocation(unsigned int x, unsigned int y)
{
      unsigned int i;

      for(i = 0; i < MAX_EFFECTS; ++i)
      {
            if (effectStatus[i] == ES_ACTIVE
             && asEffectsList[i].group == EFFECT_FIRE)
            {
                  const unsigned int posX = asEffectsList[i].position.x;
                  const unsigned int posY = asEffectsList[i].position.z;

                  if (posX == x
                   && posY == y)
                  {
                        return true;
                  }
            }
      }

      return false;
}

static const char FXData_tag_definition[] = "tagdefinitions/savegame/effects.def";
static const char FXData_file_identifier[] = "FXData";

// -----------------------------------------------------------------------------------
/* This will save out the effects data */
bool writeFXData(const char* fileName)
{
      unsigned int count, i;

      if (!tagOpenWrite(FXData_tag_definition, fileName))
      {
            ASSERT(false, "writeFXData: error while opening file (%s)", fileName);
            return false;
      }

      tagWriteString(0x01, FXData_file_identifier);

      // Count all the active EFFECTs
      for (i = 0, count = 0; i < MAX_EFFECTS; ++i)
      {
            if(effectStatus[i] == ES_ACTIVE)
            {
                  ++count;
            }
      }

      // Enter effects group and dump all EFFECTs
      tagWriteEnter(0x02, count);
      for (i = 0; i < MAX_EFFECTS; ++i)
      {
            const EFFECT* curEffect = &asEffectsList[i];

            // Don't save inactive effects
            if (effectStatus[i] != ES_ACTIVE)
                  continue;

            tagWrite(0x01, curEffect->control);
            tagWrite(0x02, curEffect->group);
            tagWrite(0x03, curEffect->type);
            tagWrite(0x04, curEffect->frameNumber);
            tagWrite(0x05, curEffect->size);
            tagWrite(0x06, curEffect->baseScale);
            tagWrite(0x07, curEffect->specific);

            tagWritefv   (0x08, 3, &curEffect->position.x);
            tagWritefv   (0x09, 3, &curEffect->velocity.x);
            tagWrites32v (0x0A, 3, &curEffect->rotation.x);
            tagWrites32v (0x0B, 3, &curEffect->spin.x);

            tagWrite(0x0C, curEffect->birthTime);
            tagWrite(0x0D, curEffect->lastFrame);
            tagWrite(0x0E, curEffect->frameDelay);
            tagWrite(0x0F, curEffect->lifeSpan);
            tagWrite(0x10, curEffect->radius);

            tagWriteString(0x11, resGetNamefromData("IMD", curEffect->imd));

            // Move on to reading the next effect group
            tagWriteNext();
      }
      // Leave the effects group again...
      tagWriteLeave(0x02);

      // Close the file
      tagClose();

      // Everything is just fine!
      return true;
}

// -----------------------------------------------------------------------------------
/* This will read in the effects data */
bool readFXData(const char* fileName)
{
      unsigned int count, i;
      char strbuffer[25];

      if (!tagOpenRead(FXData_tag_definition, fileName))
      {
            debug(LOG_ERROR, "readFXData: error while opening file (%s)", fileName);
            return false;
      }

      // Read & verify the format header identifier
      tagReadString(0x01, sizeof(strbuffer), strbuffer);
      if (strncmp(strbuffer, FXData_file_identifier, sizeof(strbuffer)) != 0)
      {
            debug(LOG_ERROR, "readFXData: Weird file type found (in file %s)? Has header string: %s", fileName, strbuffer);
            return false;
      }

      // Clear out anything that's there already!
      initEffectsSystem();

      // Enter effects group and load all EFFECTs
      count = tagReadEnter(0x02);
      for(i = 0; i < count; ++i)
      {
            EFFECT* curEffect = &asEffectsList[i];
                char imd_name[PATH_MAX];

            ASSERT(i < MAX_EFFECTS, "readFXData: more effects in this file than our array can contain");

            curEffect->control      = tagRead(0x01);
            curEffect->group        = tagRead(0x02);
            curEffect->type         = tagRead(0x03);
            curEffect->frameNumber  = tagRead(0x04);
            curEffect->size         = tagRead(0x05);
            curEffect->baseScale    = tagRead(0x06);
            curEffect->specific     = tagRead(0x07);

            tagReadfv   (0x08, 3, &curEffect->position.x);
            tagReadfv   (0x09, 3, &curEffect->velocity.x);
            tagReads32v (0x0A, 3, &curEffect->rotation.x);
            tagReads32v (0x0B, 3, &curEffect->spin.x);

            curEffect->birthTime    = tagRead(0x0C);
            curEffect->lastFrame    = tagRead(0x0D);
            curEffect->frameDelay   = tagRead(0x0E);
            curEffect->lifeSpan     = tagRead(0x0F);
            curEffect->radius       = tagRead(0x10);
            tagReadString(0x11, sizeof(imd_name), imd_name);

            if (imd_name[0] != '\0')
            {
                  curEffect->imd = (iIMDShape*)resGetData("IMD", imd_name);
            }
            else
            {
                  curEffect->imd = NULL;
            }

            effectStatus[i] = ES_ACTIVE;

            // Move on to reading the next effect group
            tagReadNext();
      }
      // Leave the effects group again...
      tagReadLeave(0x02);

      /* Ensure free effects kept up to date */
      freeEffect = i;

      // Close the file
      tagClose();

      /* Hopefully everything's just fine by now */
      return true;
}

Generated by  Doxygen 1.6.0   Back to index