Files
source/TileEngine/SmokeEffects.cpp
T
Sevenfm 5b8f91912f First decay light effects and then decay smoke effects, because fire can create light.
Dynamic creation of light effects for fire and signal smoke (ADD_LIGHT_AFTER_EXPLOSION option).
Fixed/improved code to decay light effects so that ubDuration works correctly.
Note: <ubRadius> tag is not used for light effects as they cannot spread, light is always created with <ubStartRadius> and can only decay later.
Improved code to decay smoke effects.

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@8767 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2020-03-03 17:48:43 +00:00

1248 lines
30 KiB
C++

#include "builddefines.h"
#ifdef PRECOMPILEDHEADERS
#include "TileEngine All.h"
#else
#include <stdio.h>
#include <string.h>
#include "stdlib.h"
#include "debug.h"
//#include "soldier control.h"
#include "weapons.h"
#include "handle items.h"
#include "worlddef.h"
#include "worldman.h"
#include "animation control.h"
#include "tile animation.h"
#include "handle items.h"
#include "smokeeffects.h"
#include "message.h"
#include "isometric utils.h"
#include "renderworld.h"
#include "explosion control.h"
#include "Random.h"
#include "Game Clock.h"
#include "opplist.h"
#include "Campaign Types.h"
#include "Tactical Save.h"
#include "strategicmap.h"
// sevenfm
#include "environment.h"
#include "Render Fun.h"
#endif
#include "SaveLoadGame.h"
#include "debug control.h"
#include "connect.h"
//forward declarations of common classes to eliminate includes
class OBJECTTYPE;
class SOLDIERTYPE;
INT8 FromWorldFlagsToSmokeType( UINT16 ubWorldFlags );
UINT16 FromSmokeTypeToWorldFlags( INT8 bType );
// GLOBAL FOR SMOKE LISTING
SMOKEEFFECT gSmokeEffectData[ NUM_SMOKE_EFFECT_SLOTS ];
UINT32 guiNumSmokeEffects = 0;
INT32 GetFreeSmokeEffect( void );
void RecountSmokeEffects( void );
INT32 GetFreeSmokeEffect( void )
{
UINT32 uiCount;
for(uiCount=0; uiCount < guiNumSmokeEffects; uiCount++)
{
if(( gSmokeEffectData[uiCount].fAllocated==FALSE ) )
return( (INT32)uiCount );
}
if( guiNumSmokeEffects < NUM_SMOKE_EFFECT_SLOTS )
return( (INT32) guiNumSmokeEffects++ );
return( -1 );
}
void RecountSmokeEffects( void )
{
INT32 uiCount;
for(uiCount=guiNumSmokeEffects-1; (uiCount >=0) ; uiCount--)
{
if( ( gSmokeEffectData[uiCount].fAllocated ) )
{
guiNumSmokeEffects=(UINT32)(uiCount+1);
break;
}
}
}
// Returns NO_SMOKE_EFFECT if none there...
INT8 GetSmokeEffectOnTile( INT32 sGridNo, INT8 bLevel )
{
UINT16 ubExtFlags;
ubExtFlags = gpWorldLevelData[ sGridNo ].ubExtFlags[ bLevel ];
// Look at worldleveldata to find flags..
if ( ubExtFlags & ANY_SMOKE_EFFECT )
{
// Which smoke am i?
return( FromWorldFlagsToSmokeType( ubExtFlags ) );
}
return( NO_SMOKE_EFFECT );
}
INT8 FromWorldFlagsToSmokeType( UINT16 ubWorldFlags )
{
if ( ubWorldFlags & MAPELEMENT_EXT_SMOKE )
{
return( NORMAL_SMOKE_EFFECT );
}
else if ( ubWorldFlags & MAPELEMENT_EXT_TEARGAS )
{
return( TEARGAS_SMOKE_EFFECT );
}
else if ( ubWorldFlags & MAPELEMENT_EXT_MUSTARDGAS )
{
return( MUSTARDGAS_SMOKE_EFFECT );
}
else if ( ubWorldFlags & MAPELEMENT_EXT_CREATUREGAS )
{
return( CREATURE_SMOKE_EFFECT );
}
else if ( ubWorldFlags & MAPELEMENT_EXT_BURNABLEGAS )
{
return( BURNABLEGAS_SMOKE_EFFECT );
}
else if ( ubWorldFlags & MAPELEMENT_EXT_SIGNAL_SMOKE )
{
return( SIGNAL_SMOKE_EFFECT );
}
else if ( ubWorldFlags & MAPELEMENT_EXT_DEBRIS_SMOKE )
{
return(DEBRIS_SMOKE_EFFECT);
}
return(NO_SMOKE_EFFECT);
}
UINT16 FromSmokeTypeToWorldFlags( INT8 bType )
{
switch( bType )
{
case NORMAL_SMOKE_EFFECT:
return( MAPELEMENT_EXT_SMOKE );
break;
case TEARGAS_SMOKE_EFFECT:
return( MAPELEMENT_EXT_TEARGAS );
break;
case MUSTARDGAS_SMOKE_EFFECT:
return( MAPELEMENT_EXT_MUSTARDGAS );
break;
case BURNABLEGAS_SMOKE_EFFECT:
return( MAPELEMENT_EXT_BURNABLEGAS );
break;
case CREATURE_SMOKE_EFFECT:
return( MAPELEMENT_EXT_CREATUREGAS );
break;
case SIGNAL_SMOKE_EFFECT:
return( MAPELEMENT_EXT_SIGNAL_SMOKE );
break;
case DEBRIS_SMOKE_EFFECT:
return(MAPELEMENT_EXT_DEBRIS_SMOKE);
break;
default:
return( 0 );
}
}
INT32 NewSmokeEffect( INT32 sGridNo, UINT16 usItem, INT8 bLevel, UINT8 ubOwner, BOOL fFromRemoteClient )
{
SMOKEEFFECT *pSmoke;
INT32 iSmokeIndex;
INT8 bSmokeEffectType=0;
if( ( iSmokeIndex = GetFreeSmokeEffect() )==(-1) )
return(-1);
// OJW - 20091027 - Syncronising smoke effect start for multiplayer
if (is_networked && is_client)
{
SOLDIERTYPE* pSoldier = MercPtrs[ubOwner];
if (pSoldier != NULL)
{
if (pSoldier->bTeam == 0 || (pSoldier->bTeam == 1 && is_server))
{
// let all the other clients know we are spawning this effect
// and align them with our random number generator
send_newsmokeeffect(sGridNo,usItem,ubOwner,bLevel,iSmokeIndex);
}
else if (!fFromRemoteClient)
{
// skip executing locally because we want the random number generator to be aligned
// with the client that spawns set off the smoke effect
return -1;
}
}
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"NewSmokeEffect ( sGridNo : %i , usItem : %i , ubOwner : %i , bLevel : %i , iSmokeEffectID : %i )\n",sGridNo, usItem , ubOwner , bLevel , iSmokeIndex );
MPDebugMsg(tmpMPDbgString);
gfMPDebugOutputRandoms = true;
#endif
}
memset( &gSmokeEffectData[ iSmokeIndex ], 0, sizeof( SMOKEEFFECT ) );
pSmoke = &gSmokeEffectData[ iSmokeIndex ];
// Set some values...
pSmoke->sGridNo = sGridNo;
pSmoke->usItem = usItem;
pSmoke->uiTimeOfLastUpdate = GetWorldTotalSeconds( );
// Are we indoors?
// 0verhaul: Are we on the floor level?
if ( GetTerrainType( sGridNo ) == FLAT_FLOOR && bLevel)
{
pSmoke->bFlags |= SMOKE_EFFECT_INDOORS;
}
switch( Explosive[Item[usItem].ubClassIndex].ubType )
{
case EXPLOSV_MUSTGAS:
bSmokeEffectType = MUSTARDGAS_SMOKE_EFFECT;
break;
case EXPLOSV_BURNABLEGAS:
bSmokeEffectType = BURNABLEGAS_SMOKE_EFFECT;
break;
case EXPLOSV_TEARGAS:
bSmokeEffectType = TEARGAS_SMOKE_EFFECT;
break;
case EXPLOSV_SMOKE:
bSmokeEffectType = NORMAL_SMOKE_EFFECT;
break;
// case SMALL_CREATURE_GAS:
//bSmokeEffectType = CREATURE_SMOKE_EFFECT;
//ubDuration = 3;
//ubStartRadius = 1;
//break;
case EXPLOSV_CREATUREGAS:
bSmokeEffectType = CREATURE_SMOKE_EFFECT;
break;
// case VERY_SMALL_CREATURE_GAS:
//bSmokeEffectType = CREATURE_SMOKE_EFFECT;
//ubDuration = 2;
//ubStartRadius = 0;
//break;
case EXPLOSV_SIGNAL_SMOKE:
bSmokeEffectType = SIGNAL_SMOKE_EFFECT;
break;
case EXPLOSV_SMOKE_DEBRIS:
bSmokeEffectType = DEBRIS_SMOKE_EFFECT;
break;
}
pSmoke->ubDuration = (UINT8)Explosive[ Item[ usItem ].ubClassIndex ].ubDuration;
pSmoke->ubRadius = (UINT8)Explosive[ Item[ usItem ].ubClassIndex ].ubStartRadius;
pSmoke->bAge = 0;
pSmoke->fAllocated = TRUE;
pSmoke->bType = bSmokeEffectType;
pSmoke->ubOwner = ubOwner;
if ( pSmoke->bFlags & SMOKE_EFFECT_INDOORS )
{
// Duration is increased by 2 turns...indoors
pSmoke->ubDuration += 3;
}
else
{
switch ( SectorInfo[SECTOR( gWorldSectorX, gWorldSectorY )].usWeather )
{
case WEATHER_FORECAST_RAIN: pSmoke->ubDuration = max( 0, pSmoke->ubDuration - 2 ); break;
case WEATHER_FORECAST_THUNDERSHOWERS: pSmoke->ubDuration = max( 0, pSmoke->ubDuration - 3 ); break;
case WEATHER_FORECAST_SANDSTORM: pSmoke->ubDuration = max( 0, pSmoke->ubDuration - 1 ); break;
case WEATHER_FORECAST_SNOW: pSmoke->ubDuration = max( 0, pSmoke->ubDuration - 1 ); break;
}
}
if ( bLevel )
{
pSmoke->bFlags |= SMOKE_EFFECT_ON_ROOF;
}
// ATE: FALSE into subsequent-- it's the first one!
SpreadEffect( pSmoke->sGridNo, pSmoke->ubRadius, pSmoke->usItem, pSmoke->ubOwner, FALSE, bLevel, iSmokeIndex , fFromRemoteClient , TRUE );
return( iSmokeIndex );
}
INT32 NewSmokeEffect(INT32 sGridNo, UINT16 usItem, INT8 bLevel, UINT8 ubOwner, BOOLEAN fFromRemoteClient, UINT8 ubDuration, UINT8 ubRadius, UINT8 ubGeneration)
{
SMOKEEFFECT *pSmoke;
INT32 iSmokeIndex;
INT8 bSmokeEffectType = 0;
iSmokeIndex = GetFreeSmokeEffect();
if (iSmokeIndex == -1)
{
return -1;
}
// OJW - 20091027 - Synchronizing smoke effect start for multiplayer
if (is_networked && is_client)
{
SOLDIERTYPE* pSoldier = MercPtrs[ubOwner];
if (pSoldier != NULL)
{
if (pSoldier->bTeam == 0 || (pSoldier->bTeam == 1 && is_server))
{
// let all the other clients know we are spawning this effect
// and align them with our random number generator
send_newsmokeeffect(sGridNo, usItem, ubOwner, bLevel, iSmokeIndex);
}
else if (!fFromRemoteClient)
{
// skip executing locally because we want the random number generator to be aligned
// with the client that spawns set off the smoke effect
return -1;
}
}
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString, "NewSmokeEffect ( sGridNo : %i , usItem : %i , ubOwner : %i , bLevel : %i , iSmokeEffectID : %i )\n", sGridNo, usItem, ubOwner, bLevel, iSmokeIndex);
MPDebugMsg(tmpMPDbgString);
gfMPDebugOutputRandoms = true;
#endif
}
memset(&gSmokeEffectData[iSmokeIndex], 0, sizeof(SMOKEEFFECT));
pSmoke = &gSmokeEffectData[iSmokeIndex];
// Set some values...
pSmoke->sGridNo = sGridNo;
pSmoke->usItem = usItem;
pSmoke->uiTimeOfLastUpdate = GetWorldTotalSeconds();
switch (Explosive[Item[usItem].ubClassIndex].ubType)
{
case EXPLOSV_MUSTGAS:
bSmokeEffectType = MUSTARDGAS_SMOKE_EFFECT;
break;
case EXPLOSV_BURNABLEGAS:
bSmokeEffectType = BURNABLEGAS_SMOKE_EFFECT;
break;
case EXPLOSV_TEARGAS:
bSmokeEffectType = TEARGAS_SMOKE_EFFECT;
break;
case EXPLOSV_SMOKE:
bSmokeEffectType = NORMAL_SMOKE_EFFECT;
break;
case EXPLOSV_CREATUREGAS:
bSmokeEffectType = CREATURE_SMOKE_EFFECT;
break;
case EXPLOSV_SIGNAL_SMOKE:
bSmokeEffectType = SIGNAL_SMOKE_EFFECT;
break;
case EXPLOSV_SMOKE_DEBRIS:
bSmokeEffectType = DEBRIS_SMOKE_EFFECT;
break;
}
// sevenfm: use custom duration/radius
if (ubDuration)
pSmoke->ubDuration = ubDuration;
else
pSmoke->ubDuration = (UINT8)Explosive[Item[usItem].ubClassIndex].ubDuration;
if (ubRadius)
pSmoke->ubRadius = ubRadius;
else
pSmoke->ubRadius = (UINT8)Explosive[Item[usItem].ubClassIndex].ubStartRadius;
pSmoke->bAge = 0;
pSmoke->fAllocated = TRUE;
pSmoke->bType = bSmokeEffectType;
pSmoke->ubOwner = ubOwner;
pSmoke->ubGeneration = ubGeneration;
// Are we indoors?
// sevenfm: check also roof above
if (GetTerrainType(sGridNo) == FLAT_FLOOR && bLevel == 0 && FindStructure(sGridNo, STRUCTURE_ROOF) != NULL)
{
pSmoke->bFlags |= SMOKE_EFFECT_INDOORS;
}
if (pSmoke->bFlags & SMOKE_EFFECT_INDOORS)
{
// Duration is increased by 2 turns...indoors
pSmoke->ubDuration += 2;
}
else
{
// limit smoke effects in the rain
switch (SectorInfo[SECTOR(gWorldSectorX, gWorldSectorY)].usWeather)
{
case WEATHER_FORECAST_RAIN: pSmoke->ubDuration = max(0, pSmoke->ubDuration - 2); break;
case WEATHER_FORECAST_THUNDERSHOWERS: pSmoke->ubDuration = max(0, pSmoke->ubDuration - 3); break;
case WEATHER_FORECAST_SANDSTORM: pSmoke->ubDuration = max(0, pSmoke->ubDuration - 1); break;
case WEATHER_FORECAST_SNOW: pSmoke->ubDuration = max(0, pSmoke->ubDuration - 1); break;
}
}
if (bLevel)
{
pSmoke->bFlags |= SMOKE_EFFECT_ON_ROOF;
}
// ATE: FALSE into subsequent-- it's the first one!
SpreadEffect(pSmoke->sGridNo, pSmoke->ubRadius, pSmoke->usItem, pSmoke->ubOwner, FALSE, bLevel, iSmokeIndex, fFromRemoteClient, TRUE);
return(iSmokeIndex);
}
// Add smoke to gridno
// ( Replacement algorithm uses distance away )
void AddSmokeEffectToTile( INT32 iSmokeEffectID, INT8 bType, INT32 sGridNo, INT8 bLevel )
{
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"AddSmokeEffectToTile ( iSmokeEffectID : %i , bType : %i , sGridNo : %i , bLevel : %i )\n", iSmokeEffectID, bType , sGridNo , bLevel );
MPDebugMsg(tmpMPDbgString);
#endif
ANITILE_PARAMS AniParams;
ANITILE *pAniTile;
SMOKEEFFECT *pSmoke;
BOOLEAN fDissipating = FALSE;
pSmoke = &gSmokeEffectData[ iSmokeEffectID ];
if ( ( pSmoke->ubDuration - pSmoke->bAge ) < 2 )
{
fDissipating = TRUE;
// Remove old one...
RemoveSmokeEffectFromTile( sGridNo, bLevel );
}
// If smoke effect exists already.... stop
// silversurfer: Why? What if we throw another grenade of different type? Set at least the flag for the gas type...
if ( gpWorldLevelData[ sGridNo ].ubExtFlags[ bLevel ] & ANY_SMOKE_EFFECT )
{
// Set world flags
gpWorldLevelData[ sGridNo ].ubExtFlags[ bLevel ] |= FromSmokeTypeToWorldFlags( bType );
return;
}
// OK, Create anitile....
memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) );
AniParams.sGridNo = sGridNo;
if ( bLevel == 0 )
{
AniParams.ubLevelID = ANI_STRUCT_LEVEL;
}
else
{
AniParams.ubLevelID = ANI_ONROOF_LEVEL;
}
AniParams.sDelay = (INT16)( 300 + Random( 300 ) );
if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) )
{
AniParams.sStartFrame = (INT16)0;
}
else
{
AniParams.sStartFrame = (INT16)Random( 5 );
}
// Bare bones flags are...
// AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_OPTIMIZEFORSMOKEEFFECT | ANITILE_SMOKE_EFFECT | ANITILE_LOOPING;
//AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_SMOKE_EFFECT | ANITILE_LOOPING;
if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) )
{
AniParams.uiFlags = ANITILE_PAUSED | ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_SMOKE_EFFECT | ANITILE_LOOPING;
}
else
{
AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_SMOKE_EFFECT | ANITILE_LOOPING | ANITILE_ALWAYS_TRANSLUCENT;
}
AniParams.sX = CenterX( sGridNo );
AniParams.sY = CenterY( sGridNo );
AniParams.sZ = (INT16)0;
// Use the right graphic based on type..
switch( bType )
{
case NORMAL_SMOKE_EFFECT:
if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\smkechze.STI" );
}
else
{
if ( fDissipating )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\smalsmke.STI" );
}
else
{
strcpy( AniParams.zCachedFile, "TILECACHE\\SMOKE.STI" );
}
}
break;
case TEARGAS_SMOKE_EFFECT:
if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\tearchze.STI" );
}
else
{
if ( fDissipating )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\smaltear.STI" );
}
else
{
strcpy( AniParams.zCachedFile, "TILECACHE\\TEARGAS.STI" );
}
}
break;
case MUSTARDGAS_SMOKE_EFFECT:
if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\mustchze.STI" );
}
else
{
if ( fDissipating )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\smalmust.STI" );
}
else
{
strcpy( AniParams.zCachedFile, "TILECACHE\\MUSTARD2.STI" );
}
}
break;
case BURNABLEGAS_SMOKE_EFFECT:
if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\FLAMCHZE.STI" );
}
else
{
if ( fDissipating )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\smalflam.STI" );
}
else
{
strcpy( AniParams.zCachedFile, "TILECACHE\\FLAMETH2.STI" );
}
}
break;
case CREATURE_SMOKE_EFFECT:
if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\spit_gas.STI" );
}
else
{
if ( fDissipating )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\spit_gas.STI" );
}
else
{
strcpy( AniParams.zCachedFile, "TILECACHE\\spit_gas.STI" );
}
}
break;
case SIGNAL_SMOKE_EFFECT:
if ( !( gGameSettings.fOptions[ TOPTION_ANIMATE_SMOKE ] ) )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\signal_gas.STI" );
}
else
{
if ( fDissipating )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\signal_gas.STI" );
}
else
{
strcpy( AniParams.zCachedFile, "TILECACHE\\signal_gas.STI" );
}
}
break;
case DEBRIS_SMOKE_EFFECT:
if ( !(gGameSettings.fOptions[TOPTION_ANIMATE_SMOKE]) )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\debris_smoke.STI" );
}
else
{
if ( fDissipating )
{
strcpy( AniParams.zCachedFile, "TILECACHE\\debris_smoke.STI" );
}
else
{
strcpy( AniParams.zCachedFile, "TILECACHE\\debris_smoke.STI" );
}
}
break;
}
// Create tile...
pAniTile = CreateAnimationTile( &AniParams );
// Set world flags
gpWorldLevelData[ sGridNo ].ubExtFlags[ bLevel ] |= FromSmokeTypeToWorldFlags( bType );
// All done...
// Re-draw..... :(
SetRenderFlags(RENDER_FLAG_FULL);
}
void RemoveSmokeEffectFromTile( INT32 sGridNo, INT8 bLevel )
{
#ifdef JA2BETAVERSION
CHAR tmpMPDbgString[512];
sprintf(tmpMPDbgString,"RemoveSmokeEffectFromTile ( sGridNo : %i , bLevel : %i )\n", sGridNo, bLevel );
MPDebugMsg(tmpMPDbgString);
#endif
ANITILE *pAniTile;
UINT8 ubLevelID;
// Get ANI tile...
if ( bLevel == 0 )
{
ubLevelID = ANI_STRUCT_LEVEL;
}
else
{
ubLevelID = ANI_ONROOF_LEVEL;
}
pAniTile = GetCachedAniTileOfType( sGridNo, ubLevelID, ANITILE_SMOKE_EFFECT );
if ( pAniTile != NULL )
{
DeleteAniTile( pAniTile );
SetRenderFlags( RENDER_FLAG_FULL );
}
// Unset flags in world....
// ( // check to see if we are the last one....
if ( GetCachedAniTileOfType( sGridNo, ubLevelID, ANITILE_SMOKE_EFFECT ) == NULL )
{
gpWorldLevelData[ sGridNo ].ubExtFlags[ bLevel ] &= ( ~ANY_SMOKE_EFFECT );
}
}
void DecaySmokeEffects( UINT32 uiTime )
{
SMOKEEFFECT *pSmoke;
UINT32 cnt, cnt2;
BOOLEAN fUpdate = FALSE;
BOOLEAN fSpreadEffect;
INT8 bLevel;
UINT16 usNumUpdates = 1;
for ( cnt = 0; cnt < guiNumMercSlots; ++cnt )
{
if ( MercSlots[ cnt ] )
{
// reset 'hit by gas' flags
MercSlots[ cnt ]->flags.fHitByGasFlags = 0;
}
}
// ATE: 1 ) make first pass and delete/mark any smoke effect for update
// all the deleting has to be done first///
// age all active tear gas clouds, deactivate those that are just dispersing
for ( cnt = 0; cnt < guiNumSmokeEffects; ++cnt )
{
fSpreadEffect = TRUE;
pSmoke = &gSmokeEffectData[ cnt ];
if ( pSmoke->fAllocated )
{
if ( pSmoke->bFlags & SMOKE_EFFECT_ON_ROOF )
{
bLevel = 1;
}
else
{
bLevel = 0;
}
// Do things differently for combat /vs realtime
// always try to update during combat
if (gTacticalStatus.uiFlags & INCOMBAT )
{
fUpdate = TRUE;
}
else
{
// ATE: Do this every so ofte, to acheive the effect we want...
if ( ( uiTime - pSmoke->uiTimeOfLastUpdate ) >= 10 )
{
fUpdate = TRUE;
usNumUpdates = ( UINT16 ) ( ( uiTime - pSmoke->uiTimeOfLastUpdate ) / 10 );
}
}
if ( fUpdate )
{
pSmoke->uiTimeOfLastUpdate = uiTime;
for ( cnt2 = 0; cnt2 < usNumUpdates; ++cnt2 )
{
pSmoke->bAge++;
if ( pSmoke->bAge == 1 )
{
// ATE: At least mark for update!
pSmoke->bFlags |= SMOKE_EFFECT_MARK_FOR_UPDATE;
fSpreadEffect = FALSE;
}
else
{
fSpreadEffect = TRUE;
}
if ( fSpreadEffect )
{
// if this cloud remains effective (duration not reached)
if ( pSmoke->bAge <= pSmoke->ubDuration)
{
// ATE: Only mark now and increse radius - actual drawing is done
// in another pass cause it could
// just get erased...
pSmoke->bFlags |= SMOKE_EFFECT_MARK_FOR_UPDATE;
// calculate the new cloud radius
// cloud expands by 1 every turn outdoors, and every other turn indoors
// ATE: If radius is < maximun, increase radius, otherwise keep at max
if ( pSmoke->ubRadius < Explosive[ Item[ pSmoke->usItem ].ubClassIndex ].ubRadius )
{
pSmoke->ubRadius++;
}
}
else
{
// deactivate tear gas cloud (use last known radius)
SpreadEffect( pSmoke->sGridNo, pSmoke->ubRadius, pSmoke->usItem, pSmoke->ubOwner, ERASE_SPREAD_EFFECT, bLevel, cnt );
pSmoke->fAllocated = FALSE;
break;
}
}
}
}
else
{
// damage anyone standing in cloud
SpreadEffect( pSmoke->sGridNo, pSmoke->ubRadius, pSmoke->usItem, pSmoke->ubOwner, REDO_SPREAD_EFFECT, bLevel, cnt );
}
}
}
for ( cnt = 0; cnt < guiNumSmokeEffects; ++cnt )
{
pSmoke = &gSmokeEffectData[ cnt ];
if ( pSmoke->fAllocated )
{
// if this cloud remains effective (duration not reached)
if ( pSmoke->bFlags & SMOKE_EFFECT_MARK_FOR_UPDATE )
{
if ( pSmoke->bFlags & SMOKE_EFFECT_ON_ROOF )
{
bLevel = 1;
}
else
{
bLevel = 0;
}
SpreadEffect( pSmoke->sGridNo, pSmoke->ubRadius, pSmoke->usItem, pSmoke->ubOwner, TRUE, bLevel, cnt );
pSmoke->bFlags &= (~SMOKE_EFFECT_MARK_FOR_UPDATE);
}
}
}
AllTeamsLookForAll( TRUE );
}
BOOLEAN SaveSmokeEffectsToSaveGameFile( HWFILE hFile )
{
/*
UINT32 uiNumBytesWritten;
UINT32 uiCnt=0;
UINT32 uiNumSmokeEffects=0;
//loop through and count the number of smoke effects
for( uiCnt=0; uiCnt<guiNumSmokeEffects; uiCnt++)
{
if( gSmokeEffectData[ uiCnt ].fAllocated )
uiNumSmokeEffects++;
}
//Save the Number of Smoke Effects
FileWrite( hFile, &uiNumSmokeEffects, sizeof( UINT32 ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( UINT32 ) )
{
return( FALSE );
}
if( uiNumSmokeEffects != 0 )
{
//loop through and save the number of smoke effects
for( uiCnt=0; uiCnt < guiNumSmokeEffects; uiCnt++)
{
//if the smoke is active
if( gSmokeEffectData[ uiCnt ].fAllocated )
{
//Save the Smoke effect Data
FileWrite( hFile, &gSmokeEffectData[ uiCnt ], sizeof( SMOKEEFFECT ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( SMOKEEFFECT ) )
{
return( FALSE );
}
}
}
}
*/
return( TRUE );
}
BOOLEAN LoadSmokeEffectsFromLoadGameFile( HWFILE hFile )
{
UINT32 uiNumBytesRead;
UINT32 uiCount;
UINT32 uiCnt=0;
INT8 bLevel;
//no longer need to load smoke effects. They are now in temp files
if( guiCurrentSaveGameVersion < 75 )
{
//Clear out the old list
memset( gSmokeEffectData, 0, sizeof( SMOKEEFFECT ) * NUM_SMOKE_EFFECT_SLOTS );
//Load the Number of Smoke Effects
FileRead( hFile, &guiNumSmokeEffects, sizeof( UINT32 ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( UINT32 ) )
{
return( FALSE );
}
//This is a TEMP hack to allow us to use the saves
if( guiCurrentSaveGameVersion < 37 && guiNumSmokeEffects == 0 )
{
//Load the Smoke effect Data
FileRead( hFile, gSmokeEffectData, sizeof( SMOKEEFFECT ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( SMOKEEFFECT ) )
{
return( FALSE );
}
}
//loop through and load the list
for( uiCnt=0; uiCnt<guiNumSmokeEffects;uiCnt++)
{
//Load the Smoke effect Data
FileRead( hFile, &gSmokeEffectData[ uiCnt ], sizeof( SMOKEEFFECT ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( SMOKEEFFECT ) )
{
return( FALSE );
}
//This is a TEMP hack to allow us to use the saves
if( guiCurrentSaveGameVersion < 37 )
break;
}
//loop through and apply the smoke effects to the map
for(uiCount=0; uiCount < guiNumSmokeEffects; uiCount++)
{
//if this slot is allocated
if( gSmokeEffectData[uiCount].fAllocated )
{
if ( gSmokeEffectData[uiCount].bFlags & SMOKE_EFFECT_ON_ROOF )
{
bLevel = 1;
}
else
{
bLevel = 0;
}
SpreadEffect( gSmokeEffectData[uiCount].sGridNo, gSmokeEffectData[uiCount].ubRadius, gSmokeEffectData[uiCount].usItem, gSmokeEffectData[uiCount].ubOwner, TRUE, bLevel, uiCount );
}
}
}
return( TRUE );
}
BOOLEAN SaveSmokeEffectsToMapTempFile( INT16 sMapX, INT16 sMapY, INT8 bMapZ )
{
UINT32 uiNumSmokeEffects=0;
HWFILE hFile;
UINT32 uiNumBytesWritten=0;
CHAR8 zMapName[ 128 ];
UINT32 uiCnt;
//get the name of the map
GetMapTempFileName( SF_SMOKE_EFFECTS_TEMP_FILE_EXISTS, zMapName, sMapX, sMapY, bMapZ );
//delete file the file.
FileDelete( zMapName );
//loop through and count the number of smoke effects
for( uiCnt=0; uiCnt<guiNumSmokeEffects; uiCnt++)
{
if( gSmokeEffectData[ uiCnt ].fAllocated )
uiNumSmokeEffects++;
}
//if there are no smoke effects
if( uiNumSmokeEffects == 0 )
{
//set the fact that there are no smoke effects for this sector
ReSetSectorFlag( sMapX, sMapY, bMapZ, SF_SMOKE_EFFECTS_TEMP_FILE_EXISTS );
return( TRUE );
}
//Open the file for writing
hFile = FileOpen( zMapName, FILE_ACCESS_WRITE | FILE_OPEN_ALWAYS, FALSE );
if( hFile == 0 )
{
//Error opening file
return( FALSE );
}
//Save the Number of Smoke Effects
FileWrite( hFile, &uiNumSmokeEffects, sizeof( UINT32 ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( UINT32 ) )
{
//Close the file
FileClose( hFile );
return( FALSE );
}
//loop through and save the number of smoke effects
for( uiCnt=0; uiCnt < guiNumSmokeEffects; uiCnt++)
{
//if the smoke is active
if( gSmokeEffectData[ uiCnt ].fAllocated )
{
//Save the Smoke effect Data
FileWrite( hFile, &gSmokeEffectData[ uiCnt ], sizeof( SMOKEEFFECT ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( SMOKEEFFECT ) )
{
//Close the file
FileClose( hFile );
return( FALSE );
}
}
}
//Close the file
FileClose( hFile );
SetSectorFlag( sMapX, sMapY, bMapZ, SF_SMOKE_EFFECTS_TEMP_FILE_EXISTS );
return( TRUE );
}
BOOLEAN LoadSmokeEffectsFromMapTempFile( INT16 sMapX, INT16 sMapY, INT8 bMapZ )
{
UINT32 uiNumBytesRead;
UINT32 uiCount;
UINT32 uiCnt=0;
HWFILE hFile;
CHAR8 zMapName[ 128 ];
INT8 bLevel;
GetMapTempFileName( SF_SMOKE_EFFECTS_TEMP_FILE_EXISTS, zMapName, sMapX, sMapY, bMapZ );
//Open the file for reading, Create it if it doesnt exist
hFile = FileOpen( zMapName, FILE_ACCESS_READ | FILE_OPEN_EXISTING, FALSE );
if( hFile == 0 )
{
//Error opening map modification file
return( FALSE );
}
//Clear out the old list
ResetSmokeEffects();
//Load the Number of Smoke Effects
FileRead( hFile, &guiNumSmokeEffects, sizeof( UINT32 ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( UINT32 ) )
{
FileClose( hFile );
return( FALSE );
}
//loop through and load the list
for( uiCnt=0; uiCnt<guiNumSmokeEffects;uiCnt++)
{
//Load the Smoke effect Data
FileRead( hFile, &gSmokeEffectData[ uiCnt ], sizeof( SMOKEEFFECT ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( SMOKEEFFECT ) )
{
FileClose( hFile );
return( FALSE );
}
}
//loop through and apply the smoke effects to the map
for(uiCount=0; uiCount < guiNumSmokeEffects; uiCount++)
{
//if this slot is allocated
if( gSmokeEffectData[uiCount].fAllocated )
{
if ( gSmokeEffectData[uiCount].bFlags & SMOKE_EFFECT_ON_ROOF )
{
bLevel = 1;
}
else
{
bLevel = 0;
}
SpreadEffect( gSmokeEffectData[uiCount].sGridNo, gSmokeEffectData[uiCount].ubRadius, gSmokeEffectData[uiCount].usItem, gSmokeEffectData[uiCount].ubOwner, TRUE, bLevel, uiCount );
}
}
FileClose( hFile );
return( TRUE );
}
void ResetSmokeEffects()
{
//Clear out the old list
memset( gSmokeEffectData, 0, sizeof( SMOKEEFFECT ) * NUM_SMOKE_EFFECT_SLOTS );
guiNumSmokeEffects = 0;
}
void UpdateSmokeEffectGraphics( )
{
UINT32 uiCnt;
SMOKEEFFECT *pSmoke;
INT8 bLevel;
//loop through and save the number of smoke effects
for( uiCnt=0; uiCnt < guiNumSmokeEffects; uiCnt++)
{
pSmoke = &gSmokeEffectData[ uiCnt ];
//if the smoke is active
if( gSmokeEffectData[ uiCnt ].fAllocated )
{
if ( gSmokeEffectData[uiCnt].bFlags & SMOKE_EFFECT_ON_ROOF )
{
bLevel = 1;
}
else
{
bLevel = 0;
}
SpreadEffect( pSmoke->sGridNo, pSmoke->ubRadius, pSmoke->usItem, pSmoke->ubOwner, ERASE_SPREAD_EFFECT, bLevel, uiCnt );
SpreadEffect( pSmoke->sGridNo, pSmoke->ubRadius, pSmoke->usItem, pSmoke->ubOwner, TRUE, bLevel, uiCnt );
}
}
}
BOOL GetRandomSignalSmokeGridNo(INT32* psGridNo)
{
UINT32 uiCnt;
//SMOKEEFFECT *pSmoke;
//INT8 bLevel;
INT32 smokearray[50];
for (UINT8 i = 0; i < 50; ++i)
smokearray[i] = -1;
UINT8 cnt = 0;
//loop through and save the number of smoke effects
for( uiCnt=0; uiCnt < guiNumSmokeEffects; uiCnt++)
{
if ( gSmokeEffectData[ uiCnt ].fAllocated && gSmokeEffectData[ uiCnt ].bType == SIGNAL_SMOKE_EFFECT )
{
smokearray[cnt++] = uiCnt;
if ( cnt >= 50 )
break;
}
}
if ( !cnt )
return FALSE;
UINT8 target = Random(cnt);
(*psGridNo) = gSmokeEffectData[ smokearray[target] ].sGridNo;
return TRUE;
}
// sevenfm: check smoke effect of certain type at gridno
BOOLEAN CheckSmokeEffect(INT32 sGridNo, INT8 bLevel, INT8 bType)
{
UINT32 uiCnt;
INT8 bSmokeEffectLevel;
//loop through all smoke effects
for (uiCnt = 0; uiCnt < guiNumSmokeEffects; uiCnt++)
{
if (gSmokeEffectData[uiCnt].fAllocated &&
gSmokeEffectData[uiCnt].sGridNo == sGridNo &&
gSmokeEffectData[uiCnt].bType == bType)
{
if (gSmokeEffectData[uiCnt].bFlags & SMOKE_EFFECT_ON_ROOF)
bSmokeEffectLevel = 1;
else
bSmokeEffectLevel = 0;
if (bSmokeEffectLevel == bLevel)
{
return TRUE;
}
}
}
return FALSE;
}
BOOLEAN FindVisibleSmokeEffect(INT8 bType)
{
UINT32 uiCnt;
INT8 bSmokeEffectLevel;
//loop through all smoke effects
for (uiCnt = 0; uiCnt < guiNumSmokeEffects; uiCnt++)
{
if (gSmokeEffectData[uiCnt].fAllocated &&
gSmokeEffectData[uiCnt].bType == bType)
{
if (gSmokeEffectData[uiCnt].bFlags & SMOKE_EFFECT_ON_ROOF)
bSmokeEffectLevel = 1;
else
bSmokeEffectLevel = 0;
if (!TileIsOutOfBounds(gSmokeEffectData[uiCnt].sGridNo) &&
GridNoOnScreen(gSmokeEffectData[uiCnt].sGridNo))
{
return TRUE;
}
}
}
return FALSE;
}