- main flashbang code added

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@462 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Lesh
2006-08-23 15:28:55 +00:00
parent eef9f7309d
commit 944df34a9e
7 changed files with 283 additions and 52 deletions
+3
View File
@@ -172,6 +172,9 @@ enum
#define FIRE_WEAPON_SLEEP_DART_SPECIAL 6
#define FIRE_WEAPON_BLINDED_BY_SPIT_SPECIAL 7
#define FIRE_WEAPON_TOSSED_OBJECT_SPECIAL 8
#define FIRE_WEAPON_BLINDED 9
#define FIRE_WEAPON_DEAFENED 10
#define FIRE_WEAPON_BLINDED_AND_DEAFENED 11
#define NO_INTERRUPTS 0
+131 -2
View File
@@ -3549,11 +3549,48 @@ void EVENT_SoldierGotHit( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDa
ubReason = TAKE_DAMAGE_HANDTOHAND;
}
// marke added one 'or' for explosive ammo. variation of: AmmoTypes[pSoldier->inv[pSoldier->ubAttackingHand ].ubGunAmmoType].explosionSize > 1
// marke need another attacker id assignment
// MercPtrs[ubAttackerID]->bLastAttackHit = TRUE;
// extracting attacker´s ammo type
else if ( Item[ usWeaponIndex ].usItemClass & IC_EXPLOSV || AmmoTypes[MercPtrs[ubAttackerID]->inv[MercPtrs[ubAttackerID]->ubAttackingHand ].ubGunAmmoType].explosionSize > 1)
{
INT8 bDeafValue;
bDeafValue = Explosive[ Item[ usWeaponIndex ].ubClassIndex ].ubVolume / 10;
if ( bDeafValue == 0 )
bDeafValue = 1;
// Lesh: flashbang does damage
switch ( ubSpecial )
{
case FIRE_WEAPON_BLINDED_AND_DEAFENED:
pSoldier->bDeafenedCounter = bDeafValue;
//ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Soldier is blinded and deafened" );
// if soldier in building OR underground
if ( InBuilding(sLocationGrid) || (gbWorldSectorZ) )
{
// deal max special damage
pSoldier->bBlindedCounter = (INT8)Explosive[ Item[ usWeaponIndex ].ubClassIndex ].ubDuration;
}
else if ( NightTime() ) // if soldier outside at night
{
// halve effect
pSoldier->bBlindedCounter = (INT8)Explosive[ Item[ usWeaponIndex ].ubClassIndex ].ubDuration / 2;
if ( pSoldier->bBlindedCounter == 0 )
pSoldier->bBlindedCounter = 1;
pSoldier->bDeafenedCounter /= 2;
}
DecayIndividualOpplist( pSoldier );
break;
case FIRE_WEAPON_BLINDED:
break;
case FIRE_WEAPON_DEAFENED:
//ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"Soldier is deafened" );
pSoldier->bDeafenedCounter = bDeafValue;
break;
};
if ( usWeaponIndex == STRUCTURE_EXPLOSION )
{
ubReason = TAKE_DAMAGE_STRUCTURE_EXPLOSION;
@@ -4110,12 +4147,98 @@ void SoldierGotHitExplosion( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16
return;
}
// Lesh: possible soldier behavior when affected by flashbang
// Soldier can:
// 1. stand as if there was no explosion at all
// 2. crouch. represent that soldier didn't expect such blow and instinctively
// made defensive movement to protect his body
// 3. fall forward. again, he didn't expect that something will explode behind
// him and deafens him
// 4. fall backward. unexpected blast, fear, clumsy moves and soldier flies backward.
// Based on stance, select generic hit animation
switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
{
case ANIM_STAND:
if ( ubSpecial == FIRE_WEAPON_DEAFENED )
{
switch( Random(10) )
{
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
// 6 of 10 - crouch
ChangeSoldierStance( pSoldier, ANIM_CROUCH );
break;
case 6:
case 7:
case 8:
// 3 of 10 - fall forward
BeginTyingToFall( pSoldier );
EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0, FALSE );
break;
case 9:
// 1 of 10 - still standing
DoGenericHit( pSoldier, 0, bDirection );
break;
};
break;
}
else if ( ubSpecial == FIRE_WEAPON_BLINDED_AND_DEAFENED )
{
switch( Random(10) )
{
case 0:
case 1:
case 2:
case 3:
case 4:
// 5 of 10 - crouch
ChangeSoldierStance( pSoldier, ANIM_CROUCH );
break;
case 5:
case 6:
case 7:
case 8:
// 4 of 10 - fall backward (if possible) either forward
// Check behind us!
sNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) );
if ( OKFallDirection( pSoldier, sNewGridNo, pSoldier->bLevel, gOppositeDirection[ bDirection ], FLYBACK_HIT ) )
{
EVENT_SetSoldierDirection( pSoldier, (INT8)bDirection );
EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->bDirection );
ChangeToFallbackAnimation( pSoldier, (INT8)bDirection );
}
else
{
BeginTyingToFall( pSoldier );
EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0, FALSE );
}
break;
case 9:
// 1 of 10 - still standing
DoGenericHit( pSoldier, 0, bDirection );
break;
};
break;
}
else if ( ubSpecial == FIRE_WEAPON_BLINDED )
{
}
case ANIM_CROUCH:
if ( ubSpecial == FIRE_WEAPON_BLINDED ||
ubSpecial == FIRE_WEAPON_BLINDED_AND_DEAFENED ||
ubSpecial == FIRE_WEAPON_DEAFENED )
{
DoGenericHit( pSoldier, 0, bDirection );
break;
}
EVENT_SetSoldierDirection( pSoldier, (INT8)bDirection );
EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->bDirection );
@@ -4920,6 +5043,12 @@ void EVENT_BeginMercTurn( SOLDIERTYPE *pSoldier, BOOLEAN fFromRealTime, INT32 iR
}
}
if ( pSoldier->bDeafenedCounter > 0 )
{
pSoldier->bDeafenedCounter--;
}
// ATE: To get around a problem...
// If an AI guy, and we have 0 life, and are still at higher hieght,
// Kill them.....
+1 -1
View File
@@ -927,7 +927,7 @@ typedef struct
BOOLEAN fRTInNonintAnim;
BOOLEAN fDoingExternalDeath;
INT8 bCorpseQuoteTolerance;
INT8 bYetAnotherPaddingSpace;
INT8 bDeafenedCounter;
INT32 iPositionSndID;
INT32 iTuringSoundID;
UINT8 ubLastDamageReason;
+1
View File
@@ -181,6 +181,7 @@ enum
EXPLOSV_SMOKE,
EXPLOSV_CREATUREGAS,
EXPLOSV_BURNABLEGAS,
EXPLOSV_FLASHBANG,
};
#define AMMO_DAMAGE_ADJUSTMENT_BUCKSHOT( x ) (x / 4)
+6
View File
@@ -5459,6 +5459,12 @@ UINT8 CalcEffVolume(SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bLevel, UINT8 ubN
{
INT32 iEffVolume, iDistance;
// Lesh: deafness
if ( pSoldier->bDeafenedCounter > 0 )
{
return( 0 );
}
if ( FindWalkman(pSoldier) != ITEM_NOT_FOUND )
{
return( 0 );
+134 -44
View File
@@ -65,6 +65,9 @@
// Spreads the effects of explosions...
BOOLEAN ExpAffect( INT16 sBombGridNo, INT16 sGridNo, UINT32 uiDist, UINT16 usItem, UINT8 ubOwner, INT16 sSubsequent, BOOLEAN *pfMercHit, INT8 bLevel, INT32 iSmokeEffectID );
// Flashbang effect on soldier
UINT8 DetermineFlashbangEffect( SOLDIERTYPE *pSoldier, INT8 ubExplosionDir, BOOLEAN fInBuilding);
extern INT8 gbSAMGraphicList[ NUMBER_OF_SAMS ];
extern void AddToShouldBecomeHostileOrSayQuoteList( UINT8 ubID );
extern void RecompileLocalMovementCostsForWall( INT16 sGridNo, UINT8 ubOrientation );
@@ -386,17 +389,17 @@ void GenerateExplosionFromExplosionPointer( EXPLOSIONTYPE *pExplosion )
// Setup explosion!
memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) );
AniParams.sGridNo = sGridNo;
AniParams.ubLevelID = ANI_TOPMOST_LEVEL;
AniParams.sGridNo = sGridNo;
AniParams.ubLevelID = ANI_TOPMOST_LEVEL;
AniParams.sDelay = gExpAniData[ ubTypeID ].sBlastSpeed; // Lesh: edit this line
AniParams.sStartFrame = pExplosion->sCurrentFrame;
AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_EXPLOSION;
AniParams.sStartFrame = pExplosion->sCurrentFrame;
AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_EXPLOSION;
if ( ubTerrainType == LOW_WATER || ubTerrainType == MED_WATER || ubTerrainType == DEEP_WATER )
{
// Change type to water explosion...
ubTypeID = WATER_BLAST;
AniParams.uiFlags |= ANITILE_ALWAYS_TRANSLUCENT;
AniParams.uiFlags |= ANITILE_ALWAYS_TRANSLUCENT;
}
@@ -415,35 +418,40 @@ void GenerateExplosionFromExplosionPointer( EXPLOSIONTYPE *pExplosion )
}
AniParams.ubKeyFrame1 = gExpAniData[ ubTypeID ].ubTransKeyFrame; // Lesh: edit this line
AniParams.uiKeyFrame1Code = ANI_KEYFRAME_BEGIN_TRANSLUCENCY;
AniParams.uiKeyFrame1Code = ANI_KEYFRAME_BEGIN_TRANSLUCENCY;
if ( !( uiFlags & EXPLOSION_FLAG_DISPLAYONLY ) )
{
AniParams.ubKeyFrame2 = gExpAniData[ ubTypeID ].ubDamageKeyFrame; // Lesh: edit this line
AniParams.uiKeyFrame2Code = ANI_KEYFRAME_BEGIN_DAMAGE;
AniParams.uiKeyFrame2Code = ANI_KEYFRAME_BEGIN_DAMAGE;
}
AniParams.uiUserData = usItem;
AniParams.ubUserData2 = ubOwner;
AniParams.uiUserData3 = pExplosion->iID;
AniParams.uiUserData = usItem;
AniParams.ubUserData2 = ubOwner;
AniParams.uiUserData3 = pExplosion->iID;
strcpy( AniParams.zCachedFile, gExpAniData[ ubTypeID ].zBlastFilename ); // Lesh: edit this line
CreateAnimationTile( &AniParams );
// set light source....
if ( pExplosion->iLightID == -1 )
// set light source for flashbangs.... or...
if ( pExplosion->Params.ubTypeID == FLASHBANG_EXP )
{
pExplosion->iLightID = LightSpriteCreate("FLSHBANG.LHT", 0 );
}
else
// generic light
// DO ONLY IF WE'RE AT A GOOD LEVEL
if ( ubAmbientLightLevel >= MIN_AMB_LEVEL_FOR_MERC_LIGHTS )
{
if( ( pExplosion->iLightID = LightSpriteCreate("L-R04.LHT", 0 ) ) != (-1) )
{
LightSpritePower( pExplosion->iLightID, TRUE );
LightSpritePosition( pExplosion->iLightID, (INT16)(sX/CELL_X_SIZE), (INT16)(sY/CELL_Y_SIZE) );
}
pExplosion->iLightID = LightSpriteCreate("L-R04.LHT", 0 );
}
if( pExplosion->iLightID != -1 )
{
LightSpritePower ( pExplosion->iLightID, TRUE );
LightSpriteRoofStatus( pExplosion->iLightID, pExplosion->Params.bLevel );
LightSpritePosition ( pExplosion->iLightID, (INT16)(sX/CELL_X_SIZE), (INT16)(sY/CELL_Y_SIZE) );
}
// Lesh: sound randomization
@@ -468,6 +476,18 @@ void GenerateExplosionFromExplosionPointer( EXPLOSIONTYPE *pExplosion )
void UpdateExplosionFrame( INT32 iIndex, INT16 sCurrentFrame )
{
gExplosionData[ iIndex ].sCurrentFrame = sCurrentFrame;
// Lesh: make sparkling effect
if ( gExplosionData[iIndex].Params.ubTypeID == FLASHBANG_EXP )
{
if ( gExplosionData[iIndex].iLightID != -1 )
{
INT16 iX, iY;
iX = gExplosionData[iIndex].Params.sX/CELL_X_SIZE + Random(3) - 1;
iY = gExplosionData[iIndex].Params.sY/CELL_Y_SIZE + Random(3) - 1;
LightSpritePosition( gExplosionData[iIndex].iLightID, iX, iY);
}
}
}
void RemoveExplosionData( INT32 iIndex )
@@ -1172,39 +1192,63 @@ BOOLEAN ExplosiveDamageStructureAtGridNo( STRUCTURE * pCurrent, STRUCTURE **ppNe
STRUCTURE *gStruct;
// Lesh: somewhere here once I got CTD when militia stepped on mine in cambria sam
void ExplosiveDamageGridNo( INT16 sGridNo, INT16 sWoundAmt, UINT32 uiDist, BOOLEAN *pfRecompileMovementCosts, BOOLEAN fOnlyWalls, INT8 bMultiStructSpecialFlag, BOOLEAN fSubSequentMultiTilesTransitionDamage, UINT8 ubOwner, INT8 bLevel )
{
STRUCTURE * pCurrent, *pNextCurrent, *pStructure;
STRUCTURE * pBaseStructure;
INT16 sDesiredLevel;
DB_STRUCTURE_TILE **ppTile;
UINT8 ubLoop, ubLoop2;
INT16 sNewGridNo, sNewGridNo2, sBaseGridNo;
BOOLEAN fToBreak = FALSE;
BOOLEAN fMultiStructure = FALSE;
UINT8 ubNumberOfTiles;
BOOLEAN fMultiStructSpecialFlag = FALSE;
BOOLEAN fExplodeDamageReturn = FALSE;
STRUCTURE * pCurrent, *pNextCurrent, *pStructure;
STRUCTURE * pBaseStructure;
INT16 sDesiredLevel;
DB_STRUCTURE_TILE **ppTile;
UINT8 ubLoop, ubLoop2;
INT16 sNewGridNo, sNewGridNo2, sBaseGridNo;
BOOLEAN fToBreak = FALSE;
BOOLEAN fMultiStructure = FALSE;
UINT8 ubNumberOfTiles;
BOOLEAN fMultiStructSpecialFlag = FALSE;
BOOLEAN fExplodeDamageReturn = FALSE;
FILE *pDebug;
pDebug = fopen("explosion.log", "wt");
fprintf(pDebug, "ExplosiveDamageGridNo start\n");
fclose(pDebug);
// Based on distance away, damage any struct at this gridno
// OK, loop through structures and damage!
pCurrent = gpWorldLevelData[ sGridNo ].pStructureHead;
pCurrent = gpWorldLevelData[ sGridNo ].pStructureHead;
sDesiredLevel = STRUCTURE_ON_GROUND;
pDebug = fopen("explosion.log", "a+t");
fprintf(pDebug, "sGridNo = %d\n", sGridNo);
fprintf(pDebug, "pCurrent = %08X\n", pCurrent);
fclose(pDebug);
// This code gets a little hairy because
// (1) we might need to destroy the currently-examined structure
while (pCurrent != NULL)
{
// ATE: These are for the chacks below for multi-structs....
pBaseStructure = FindBaseStructure( pCurrent );
pDebug = fopen("explosion.log", "a+t");
fprintf(pDebug, "pBaseStructure = %08X\n", pBaseStructure);
fclose(pDebug);
if ( pBaseStructure )
{
sBaseGridNo = pBaseStructure->sGridNo;
ubNumberOfTiles = pBaseStructure->pDBStructureRef->pDBStructure->ubNumberOfTiles;
fMultiStructure = ( ( pBaseStructure->fFlags & STRUCTURE_MULTI ) != 0 );
ppTile = (DB_STRUCTURE_TILE **) MemAlloc( sizeof( DB_STRUCTURE_TILE ) * ubNumberOfTiles );
memcpy( ppTile, pBaseStructure->pDBStructureRef->ppTile, sizeof( DB_STRUCTURE_TILE ) * ubNumberOfTiles );
ppTile = (DB_STRUCTURE_TILE **) MemAlloc( sizeof( DB_STRUCTURE_TILE ) * ubNumberOfTiles );
pDebug = fopen("explosion.log", "a+t");
fprintf(pDebug, "sBaseGridNo = %d\n", sBaseGridNo);
fprintf(pDebug, "ubNumberOfTiles = %d\n", ubNumberOfTiles);
fprintf(pDebug, "fMultiStructure = %d\n", fMultiStructure);
fprintf(pDebug, "ppTile = %08X\n", ppTile);
fprintf(pDebug, "pBaseStructure->pDBStructureRef->ppTile = %08X\n", pBaseStructure->pDBStructureRef->ppTile);
fclose(pDebug);
// Lesh: CTD was in next line once
memcpy( ppTile, pBaseStructure->pDBStructureRef->ppTile, sizeof( DB_STRUCTURE_TILE ) * ubNumberOfTiles );
if ( bMultiStructSpecialFlag == -1 )
{
@@ -1257,10 +1301,10 @@ void ExplosiveDamageGridNo( INT16 sGridNo, INT16 sWoundAmt, UINT32 uiDist, BOOLE
// ATE: Don't after first attack...
if ( uiDist > 1 )
{
if ( pBaseStructure )
{
MemFree( ppTile );
}
if ( pBaseStructure )
{
MemFree( ppTile );
}
return;
}
@@ -1312,12 +1356,16 @@ void ExplosiveDamageGridNo( INT16 sGridNo, INT16 sWoundAmt, UINT32 uiDist, BOOLE
if ( pBaseStructure )
{
MemFree( ppTile );
}
MemFree( ppTile );
}
pCurrent = pNextCurrent;
}
pDebug = fopen("explosion.log", "a+t");
fprintf(pDebug, "ExplosiveDamageGridNo finish\n");
fclose(pDebug);
}
@@ -1325,7 +1373,11 @@ BOOLEAN DamageSoldierFromBlast( UINT8 ubPerson, UINT8 ubOwner, INT16 sBombGridNo
{
SOLDIERTYPE *pSoldier;
INT16 sNewWoundAmt = 0;
UINT8 ubDirection;
UINT8 ubDirection;
UINT8 ubSpecial = 0;
BOOLEAN fInBuilding = InBuilding(sBombGridNo);
BOOLEAN fFlashbang = Explosive[Item[usItem].ubClassIndex].ubType == EXPLOSV_FLASHBANG;
UINT16 usHalfExplosionRadius;
pSoldier = MercPtrs[ ubPerson ]; // someone is here, and they're gonna get hurt
@@ -1338,6 +1390,15 @@ BOOLEAN DamageSoldierFromBlast( UINT8 ubPerson, UINT8 ubOwner, INT16 sBombGridNo
return( FALSE );
}
// Lesh: if flashbang
// check if soldier is outdoor and situated farther that half explosion radius and not underground
usHalfExplosionRadius = Explosive[Item[usItem].ubClassIndex].ubRadius / 2;
if ( fFlashbang && !gbWorldSectorZ && !fInBuilding && (UINT16)uiDist > usHalfExplosionRadius )
{
// then no effect
return( FALSE );
}
// Direction to center of explosion
ubDirection = (UINT8)GetDirectionFromGridNo( sBombGridNo, pSoldier );
@@ -1350,7 +1411,14 @@ BOOLEAN DamageSoldierFromBlast( UINT8 ubPerson, UINT8 ubOwner, INT16 sBombGridNo
{
sNewWoundAmt = 0;
}
EVENT_SoldierGotHit( pSoldier, usItem, sNewWoundAmt, sBreathAmt, ubDirection, (INT16)uiDist, ubOwner, 0, ANIM_CROUCH, sSubsequent, sBombGridNo );
// Lesh: flashbang does affect on soldier or not - check it
if ( (Item[usItem].usItemClass & IC_EXPLOSV) && fFlashbang )
{
ubSpecial = DetermineFlashbangEffect( pSoldier, ubDirection, fInBuilding);
}
EVENT_SoldierGotHit( pSoldier, usItem, sNewWoundAmt, sBreathAmt, ubDirection, (INT16)uiDist, ubOwner, ubSpecial, ANIM_CROUCH, sSubsequent, sBombGridNo );
pSoldier->ubMiscSoldierFlags |= SOLDIER_MISC_HURT_BY_EXPLOSION;
@@ -1531,12 +1599,12 @@ BOOLEAN ExpAffect( INT16 sBombGridNo, INT16 sGridNo, UINT32 uiDist, UINT16 usIte
EXPLOSIVETYPE *pExplosive;
INT16 sX, sY;
BOOLEAN fRecompileMovementCosts = FALSE;
BOOLEAN fSmokeEffect=FALSE;
BOOLEAN fStunEffect = FALSE;
INT8 bSmokeEffectType = 0;
BOOLEAN fSmokeEffect = FALSE;
BOOLEAN fStunEffect = FALSE;
BOOLEAN fBlastEffect = TRUE;
INT16 sNewGridNo;
BOOLEAN fBloodEffect = FALSE;
INT8 bSmokeEffectType = 0;
INT16 sNewGridNo;
ITEM_POOL * pItemPool, * pItemPoolNext;
UINT32 uiRoll;
@@ -3603,4 +3671,26 @@ void RemoveAllActiveTimedBombs( void )
}
} while( iItemIndex != -1 );
}
UINT8 DetermineFlashbangEffect( SOLDIERTYPE *pSoldier, INT8 ubExplosionDir, BOOLEAN fInBuilding)
{
INT8 bNumTurns;
UINT16 usHeadItem1, usHeadItem2;
bNumTurns = FindNumTurnsBetweenDirs(pSoldier->bDirection, ubExplosionDir);
usHeadItem1 = pSoldier->inv[ HEAD1POS ].usItem;
usHeadItem2 = pSoldier->inv[ HEAD2POS ].usItem;
// if soldier got in explosion area check if he is affected by flash
// if soldier wears sun goggles OR grenade behind him OR
// (he is not underground AND it is day AND he is outdoor)
if ( (usHeadItem1 == SUNGOGGLES || usHeadItem2 == SUNGOGGLES) || (bNumTurns > 1) ||
(!gbWorldSectorZ && !NightTime() && !fInBuilding) )
{
// soldier didn't see flash or wears protective sungogles or outdoor at day, so he is only deafened
return ( FIRE_WEAPON_DEAFENED );
}
return ( FIRE_WEAPON_BLINDED_AND_DEAFENED );
}
+7 -5
View File
@@ -16,7 +16,7 @@ typedef struct
UINT32 uiFlags;
UINT8 ubOwner;
UINT8 ubTypeID;
INT8 ubTypeID;
UINT16 usItem;
@@ -52,10 +52,12 @@ enum EXPLOSION_TYPES
BLAST_3,
STUN_BLAST,
WATER_BLAST,
TARGAS_EXP,
SMOKE_EXP,
MUSTARD_EXP,
TARGAS_EXP,
SMOKE_EXP,
MUSTARD_EXP,
BURN_EXP,
THERMOBARIC_EXP,
FLASHBANG_EXP, // Lesh: enum added
NUM_EXP_TYPES=50
} ;
@@ -91,8 +93,8 @@ typedef struct
#define NUM_EXPLOSION_SLOTS 100
extern EXPLOSIONTYPE gExplosionData[ NUM_EXPLOSION_SLOTS ];
extern EXPLOSIONTYPE gExplosionData[ NUM_EXPLOSION_SLOTS ];
extern UINT8 gubElementsOnExplosionQueue;
extern BOOLEAN gfExplosionQueueActive;