Files
source/TileEngine/SmokeEffects.cpp
T
a5c514d2be fix mismatched upper/lowercase filenames
Fix case of WinFont.h include in WinFont.cpp

Fix case of "Strategic Status.h" include in Queen Command.cpp

Fix case of "mapscreen.h" include in Strategic Merc Handler.cpp

Fix case of "Handle Items.h" include across Tactical/TileEngine/Utils

Mechanical: normalized every #include of handle items.h (any case) to
match the actual filename "Handle Items.h". Verify with:
grep -rn "handle items.h" -i --include=*.cpp --include=*.h . | grep -v "\"Handle Items.h\""

Fix case of "World Items.h" include across Tactical/Editor

Mechanical: normalized every #include of world items.h (any case) to
match the actual filename "World Items.h". Verify with:
grep -rn "world items.h" -i --include=*.cpp --include=*.h . | grep -v "\"World Items.h\""

Fix case of "Arms Dealer Init.h" include in Overhead.cpp

Fix case of "Meanwhile.h" include in TeamTurns.cpp

Fix case of "timer.h" include in Utils All.h and Event Pump.cpp

Fix case of "Store Inventory.h" include in XML_Items.cpp

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 15:53:25 -03:00

1229 lines
30 KiB
C++

#include "builddefines.h"
#include <stdio.h>
#include <string.h>
#include "stdlib.h"
#include "DEBUG.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"
#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);
}
else if ( ubWorldFlags & MAPELEMENT_EXT_FIRERETARDANT_SMOKE )
{
return FIRERETARDANT_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;
case FIRERETARDANT_SMOKE_EFFECT:
return MAPELEMENT_EXT_FIRERETARDANT_SMOKE;
break;
default:
return( 0 );
}
}
INT32 NewSmokeEffect( INT32 sGridNo, UINT16 usItem, INT8 bLevel, SoldierID 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 = 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, bLevel, ubOwner, 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.i , 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;
case EXPLOSV_SMOKE_FIRERETARDANT:
bSmokeEffectType = FIRERETARDANT_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, SoldierID 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 = 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, bLevel, ubOwner, 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.i, 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;
case EXPLOSV_SMOKE_FIRERETARDANT:
bSmokeEffectType = FIRERETARDANT_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;
}
INT16 sX, sY;
ConvertGridNoToCenterCellXY(sGridNo, &sX, &sY);
AniParams.sX = sX;
AniParams.sY = sY;
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:
strcpy( AniParams.zCachedFile, "TILECACHE\\spit_gas.STI" );
break;
case SIGNAL_SMOKE_EFFECT:
strcpy( AniParams.zCachedFile, "TILECACHE\\signal_gas.STI" );
break;
case DEBRIS_SMOKE_EFFECT:
strcpy( AniParams.zCachedFile, "TILECACHE\\debris_smoke.STI" );
break;
case FIRERETARDANT_SMOKE_EFFECT:
strcpy( AniParams.zCachedFile, "TILECACHE\\white_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 );
}
}
// Flugente: if tile has a fire retardant effect, don't create new fire
if ( Explosive[Item[pSmoke->usItem].ubClassIndex].ubType == EXPLOSV_BURNABLEGAS )
{
if ( gpWorldLevelData[pSmoke->sGridNo].ubExtFlags[bLevel] & MAPELEMENT_EXT_FIRERETARDANT_SMOKE )
{
pSmoke->bAge = pSmoke->ubDuration + 1;
}
}
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;
}