Files
source/Tactical/Rotting Corpses.cpp
T
2008-11-06 00:46:54 +00:00

2000 lines
47 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "Tactical All.h"
#else
#include "builddefines.h"
#include <stdio.h>
#include <string.h>
#include "wcheck.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 "rotting corpses.h"
#include "tile cache.h"
#include "isometric utils.h"
#include "animation control.h"
#include "utilities.h"
#include "game clock.h"
#include "soldier create.h"
#include "renderworld.h"
#include "soldier add.h"
#include "strategicmap.h"
#include "los.h"
#include "opplist.h"
#include "structure.h"
#include "message.h"
#include "Sound Control.h"
#include "pathai.h"
#include "Random.h"
#include "dialogue control.h"
#include "items.h"
#include "smell.h"
#include "World Items.h"
#include "explosion control.h"
#include "GameSettings.h"
#include "Interface Items.h"
#include "Soldier Profile.h"
#include "Soldier macros.h"
#include "Keys.h"
#include "Render Fun.h"
#include "strategic.h"
#include "qarray.h"
#include "Interface.h"
#endif
//forward declarations of common classes to eliminate includes
class OBJECTTYPE;
class SOLDIERTYPE;
#define CORPSE_WARNING_MAX 5
#define CORPSE_WARNING_DIST 5
#define CORPSE_INDEX_OFFSET 10000
//#define DELAY_UNTIL_ROTTING ( 1 * NUM_SEC_IN_DAY )
#define DELAY_UNTIL_ROTTING ( 1 * NUM_SEC_IN_DAY / 60 )
#define DELAY_UNTIL_DONE_ROTTING ( 3 * NUM_SEC_IN_DAY / 60 )
#define MAX_NUM_CROWS 6
// From lighting
extern SGPPaletteEntry gpLightColors[3];
extern UINT16 gusShadeLevels[16][3];
void MakeCorpseVisible( SOLDIERTYPE *pSoldier, ROTTING_CORPSE *pCorpse );
// When adding a corpse, add struct data...
CHAR8 zCorpseFilenames[ NUM_CORPSES ][70] =
{
"",
"ANIMS\\CORPSES\\S_D_JFK.STI",
"ANIMS\\CORPSES\\S_D_BCK.STI",
"ANIMS\\CORPSES\\S_D_FWD.STI",
"ANIMS\\CORPSES\\S_D_DHD.STI",
"ANIMS\\CORPSES\\S_D_PRN.STI",
"ANIMS\\CORPSES\\S_D_WTR.STI",
"ANIMS\\CORPSES\\S_D_FALL.STI",
"ANIMS\\CORPSES\\S_D_FALLF.STI",
"ANIMS\\CORPSES\\M_D_JFK.STI",
"ANIMS\\CORPSES\\M_D_BCK.STI",
"ANIMS\\CORPSES\\M_D_FWD.STI",
"ANIMS\\CORPSES\\M_D_DHD.STI",
"ANIMS\\CORPSES\\M_D_PRN.STI",
"ANIMS\\CORPSES\\S_D_WTR.STI",
"ANIMS\\CORPSES\\M_D_FALL.STI",
"ANIMS\\CORPSES\\M_D_FALLF.STI",
"ANIMS\\CORPSES\\F_D_JFK.STI",
"ANIMS\\CORPSES\\F_D_BCK.STI",
"ANIMS\\CORPSES\\F_D_FWD.STI",
"ANIMS\\CORPSES\\F_D_DHD.STI",
"ANIMS\\CORPSES\\F_D_PRN.STI",
"ANIMS\\CORPSES\\S_D_WTR.STI",
"ANIMS\\CORPSES\\F_D_FALL.STI",
"ANIMS\\CORPSES\\F_D_FALLF.STI",
// Civs....
"ANIMS\\CORPSES\\M_DEAD1.STI",
"ANIMS\\CORPSES\\K_DEAD2.STI",
"ANIMS\\CORPSES\\H_DEAD2.STI",
"ANIMS\\CORPSES\\FT_DEAD1.STI",
"ANIMS\\CORPSES\\S_DEAD1.STI",
"ANIMS\\CORPSES\\W_DEAD1.STI",
"ANIMS\\CORPSES\\CP_DEAD1.STI",
"ANIMS\\CORPSES\\M_DEAD2.STI",
"ANIMS\\CORPSES\\K_DEAD1.STI",
"ANIMS\\CORPSES\\H_DEAD1.STI",
"ANIMS\\CORPSES\\FT_DEAD2.STI",
"ANIMS\\CORPSES\\S_DEAD2.STI",
"ANIMS\\CORPSES\\W_DEAD2.STI",
"ANIMS\\CORPSES\\CP_DEAD2.STI",
"ANIMS\\CORPSES\\CT_DEAD.STI",
"ANIMS\\CORPSES\\CW_DEAD1.STI",
"ANIMS\\CORPSES\\MN_DEAD2.STI",
"ANIMS\\CORPSES\\I_DEAD1.STI",
"ANIMS\\CORPSES\\L_DEAD1.STI",
"ANIMS\\CORPSES\\P_DECOMP2.STI",
"ANIMS\\CORPSES\\TK_WREK.STI",
"ANIMS\\CORPSES\\TK2_WREK.STI",
"ANIMS\\CORPSES\\HM_WREK.STI",
"ANIMS\\CORPSES\\IC_WREK.STI",
"ANIMS\\CORPSES\\QN_DEAD.STI",
"ANIMS\\CORPSES\\J_DEAD.STI",
"ANIMS\\CORPSES\\S_BURNT.STI",
"ANIMS\\CORPSES\\S_EXPLD.STI",
};
// When adding a corpse, add struct data...
CHAR8 zNoBloodCorpseFilenames[ NUM_CORPSES ][70] =
{
"",
"ANIMS\\CORPSES\\M_D_JFK_NB.STI",
"ANIMS\\CORPSES\\S_D_BCK_NB.STI",
"ANIMS\\CORPSES\\S_D_FWD_NB.STI",
"ANIMS\\CORPSES\\S_D_DHD_NB.STI",
"ANIMS\\CORPSES\\S_D_PRN_NB.STI",
"ANIMS\\CORPSES\\S_D_WTR.STI",
"ANIMS\\CORPSES\\S_D_FALL_NB.STI",
"ANIMS\\CORPSES\\S_D_FALLF_NB.STI",
"ANIMS\\CORPSES\\M_D_JFK_NB.STI",
"ANIMS\\CORPSES\\M_D_BCK_NB.STI",
"ANIMS\\CORPSES\\M_D_FWD_NB.STI",
"ANIMS\\CORPSES\\M_D_DHD_NB.STI",
"ANIMS\\CORPSES\\M_D_PRN_NB.STI",
"ANIMS\\CORPSES\\S_D_WTR.STI",
"ANIMS\\CORPSES\\M_D_FALL_NB.STI",
"ANIMS\\CORPSES\\M_D_FALLF_NB.STI",
"ANIMS\\CORPSES\\F_D_JFK_NB.STI",
"ANIMS\\CORPSES\\F_D_BCK_NB.STI",
"ANIMS\\CORPSES\\F_D_FWD_NB.STI",
"ANIMS\\CORPSES\\F_D_DHD_NB.STI",
"ANIMS\\CORPSES\\F_D_PRN_NB.STI",
"ANIMS\\CORPSES\\S_D_WTR.STI",
"ANIMS\\CORPSES\\F_D_FALL_NB.STI",
"ANIMS\\CORPSES\\F_D_FALLF_NB.STI",
// Civs....
"ANIMS\\CORPSES\\M_DEAD1_NB.STI",
"ANIMS\\CORPSES\\K_DEAD2_NB.STI",
"ANIMS\\CORPSES\\H_DEAD2_NB.STI",
"ANIMS\\CORPSES\\FT_DEAD1_NB.STI",
"ANIMS\\CORPSES\\S_DEAD1_NB.STI",
"ANIMS\\CORPSES\\W_DEAD1_NB.STI",
"ANIMS\\CORPSES\\CP_DEAD1_NB.STI",
"ANIMS\\CORPSES\\M_DEAD2_NB.STI",
"ANIMS\\CORPSES\\K_DEAD1_NB.STI",
"ANIMS\\CORPSES\\H_DEAD1_NB.STI",
"ANIMS\\CORPSES\\FT_DEAD2_NB.STI",
"ANIMS\\CORPSES\\S_DEAD2_NB.STI",
"ANIMS\\CORPSES\\W_DEAD2_NB.STI",
"ANIMS\\CORPSES\\CP_DEAD2_NB.STI",
"ANIMS\\CORPSES\\CT_DEAD.STI",
"ANIMS\\CORPSES\\CW_DEAD1.STI",
"ANIMS\\CORPSES\\MN_DEAD2.STI",
"ANIMS\\CORPSES\\I_DEAD1.STI",
"ANIMS\\CORPSES\\L_DEAD1.STI",
"ANIMS\\CORPSES\\P_DECOMP2.STI",
"ANIMS\\CORPSES\\TK_WREK.STI",
"ANIMS\\CORPSES\\TK2_WREK.STI",
"ANIMS\\CORPSES\\HM_WREK.STI",
"ANIMS\\CORPSES\\IC_WREK.STI",
"ANIMS\\CORPSES\\QN_DEAD.STI",
"ANIMS\\CORPSES\\J_DEAD.STI",
"ANIMS\\CORPSES\\S_BURNT.STI",
"ANIMS\\CORPSES\\S_EXPLD.STI",
};
UINT8 gb4DirectionsFrom8[8] =
{
// 7, // NORTH
0,
0, // NE
1, // E
0, // SE
2, // S
0, // SW,
3, // W,
0 // NW
};
UINT8 gb2DirectionsFrom8[8] =
{
0, // NORTH
7, // NE
7, // E
7, // SE
0, // S
7, // SW,
7, // W,
7 // NW
};
BOOLEAN gbCorpseValidForDecapitation[ NUM_CORPSES ] =
{
0,
0,
1,
1,
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
1,
1,
// Civs....
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
1,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
0,
0,
0,
0,
};
INT8 gDecapitatedCorpse[ NUM_CORPSES ] =
{
0,
SMERC_JFK,
SMERC_JFK,
SMERC_JFK,
SMERC_JFK,
SMERC_JFK,
SMERC_JFK,
SMERC_JFK,
SMERC_JFK,
MMERC_JFK,
MMERC_JFK,
MMERC_JFK,
MMERC_JFK,
MMERC_JFK,
MMERC_JFK,
MMERC_JFK,
MMERC_JFK,
FMERC_JFK,
FMERC_JFK,
FMERC_JFK,
FMERC_JFK,
FMERC_JFK,
FMERC_JFK,
FMERC_JFK,
FMERC_JFK,
// Civs....
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
};
ROTTING_CORPSE gRottingCorpse[ MAX_ROTTING_CORPSES ];
INT32 giNumRottingCorpse = 0;
BOOLEAN CreateCorpsePalette( ROTTING_CORPSE *pCorpse );
BOOLEAN CreateCorpseShadedPalette( ROTTING_CORPSE *pCorpse, UINT32 uiBase, SGPPaletteEntry *pShadePal);
void ReduceAmmoDroppedByNonPlayerSoldiers( SOLDIERTYPE *pSoldier, INT32 iInvSlot );
INT32 GetFreeRottingCorpse(void)
{
INT32 iCount;
for(iCount=0; iCount < giNumRottingCorpse; iCount++)
{
if(( gRottingCorpse[iCount].fActivated == FALSE ) )
return((INT32)iCount);
}
if(giNumRottingCorpse < MAX_ROTTING_CORPSES )
return((INT32)giNumRottingCorpse++);
return(-1);
}
void RecountRottingCorpses(void)
{
INT32 uiCount;
if ( giNumRottingCorpse > 0 )
{
for(uiCount=giNumRottingCorpse-1; (uiCount >=0) ; uiCount--)
{
if( ( gRottingCorpse[uiCount].fActivated == FALSE ) )
{
giNumRottingCorpse=(UINT32)(uiCount+1);
break;
}
}
}
}
UINT16 GetCorpseStructIndex( ROTTING_CORPSE_DEFINITION *pCorpseDef, BOOLEAN fForImage )
{
INT8 bDirection;
switch( pCorpseDef->ubType )
{
case QUEEN_MONSTER_DEAD:
case BURNT_DEAD:
case EXPLODE_DEAD:
bDirection = 0;
break;
case ICECREAM_DEAD:
case HUMMER_DEAD:
// OK , these have 2 directions....
bDirection = gb2DirectionsFrom8[ pCorpseDef->ubDirection ];
if ( fForImage )
{
bDirection = gOneCDirection[ bDirection ];
}
break;
case SMERC_FALL:
case SMERC_FALLF:
case MMERC_FALL:
case MMERC_FALLF:
case FMERC_FALL:
case FMERC_FALLF:
// OK , these have 4 directions....
bDirection = gb4DirectionsFrom8[ pCorpseDef->ubDirection ];
// if ( fForImage )
// {
// bDirection = gOneCDirection[ bDirection ];
// }
break;
default:
// Uses 8
bDirection = pCorpseDef->ubDirection;
if ( fForImage )
{
bDirection = gOneCDirection[ bDirection ];
}
break;
}
return( bDirection );
}
INT32 AddRottingCorpse( ROTTING_CORPSE_DEFINITION *pCorpseDef )
{
INT32 iIndex;
ROTTING_CORPSE *pCorpse;
ANITILE_PARAMS AniParams;
UINT8 ubLevelID;
STRUCTURE_FILE_REF * pStructureFileRef = NULL;
CHAR8 zFilename[150];
DB_STRUCTURE_REF *pDBStructureRef;
UINT8 ubLoop;
INT16 sTileGridNo;
DB_STRUCTURE_TILE ** ppTile;
UINT16 usStructIndex;
UINT32 uiDirectionUseFlag;
if ( pCorpseDef->sGridNo == NOWHERE )
{
return( -1 );
}
if ( pCorpseDef->ubType == NO_CORPSE )
{
return( -1 );
}
if( ( iIndex = GetFreeRottingCorpse() )==(-1) )
return(-1);
pCorpse = &gRottingCorpse[ iIndex ];
// Copy elements in
memcpy( pCorpse, pCorpseDef, sizeof( ROTTING_CORPSE_DEFINITION ) );
uiDirectionUseFlag = ANITILE_USE_DIRECTION_FOR_START_FRAME;
// If we are a soecial type...
switch( pCorpseDef->ubType )
{
case SMERC_FALL:
case SMERC_FALLF:
case MMERC_FALL:
case MMERC_FALLF:
case FMERC_FALL:
case FMERC_FALLF:
uiDirectionUseFlag = ANITILE_USE_4DIRECTION_FOR_START_FRAME;
}
if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
{
// OK, AS WE ADD, CHECK FOR TOD AND DECAY APPROPRIATELY
if ( ( ( GetWorldTotalMin( ) - pCorpse->def.uiTimeOfDeath ) > DELAY_UNTIL_ROTTING ) && ( pCorpse->def.ubType < ROTTING_STAGE2 ) )
{
if ( pCorpse->def.ubType <= FMERC_FALLF )
{
// Rott!
pCorpse->def.ubType = ROTTING_STAGE2;
}
}
// If time of death is a few days, now, don't add at all!
if ( ( ( GetWorldTotalMin( ) - pCorpse->def.uiTimeOfDeath ) > DELAY_UNTIL_DONE_ROTTING ) )
{
return( -1 );
}
}
// Check if on roof or not...
if ( pCorpse->def.bLevel == 0 )
{
//ubLevelID = ANI_OBJECT_LEVEL;
ubLevelID = ANI_STRUCT_LEVEL;
}
else
{
ubLevelID = ANI_ONROOF_LEVEL;
}
memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) );
AniParams.sGridNo = pCorpse->def.sGridNo;
AniParams.ubLevelID = ubLevelID;
AniParams.sDelay = (INT16)( 150 );
AniParams.sStartFrame = 0;
AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_PAUSED | ANITILE_OPTIMIZEFORSLOWMOVING | ANITILE_ANIMATE_Z | ANITILE_ERASEITEMFROMSAVEBUFFFER | uiDirectionUseFlag;
AniParams.sX = CenterX( pCorpse->def.sGridNo );
AniParams.sY = CenterY( pCorpse->def.sGridNo );
AniParams.sZ = (INT16)pCorpse->def.sHeightAdjustment;
AniParams.uiUserData3 = pCorpse->def.ubDirection;
if ( !gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] )
{
strcpy( AniParams.zCachedFile, zNoBloodCorpseFilenames[ pCorpse->def.ubType ] );
}
else
{
strcpy( AniParams.zCachedFile, zCorpseFilenames[ pCorpse->def.ubType ] );
}
pCorpse->pAniTile = CreateAnimationTile( &AniParams );
if ( pCorpse->pAniTile == NULL )
{
pCorpse->fActivated = FALSE;
return( -1 );
}
// Set flag and index values
pCorpse->pAniTile->pLevelNode->uiFlags |= ( LEVELNODE_ROTTINGCORPSE );
pCorpse->pAniTile->pLevelNode->ubShadeLevel = gpWorldLevelData[ pCorpse->def.sGridNo ].pLandHead->ubShadeLevel;
pCorpse->pAniTile->pLevelNode->ubSumLights = gpWorldLevelData[ pCorpse->def.sGridNo ].pLandHead->ubSumLights;
pCorpse->pAniTile->pLevelNode->ubMaxLights = gpWorldLevelData[ pCorpse->def.sGridNo ].pLandHead->ubMaxLights;
pCorpse->pAniTile->pLevelNode->ubNaturalShadeLevel = gpWorldLevelData[ pCorpse->def.sGridNo ].pLandHead->ubNaturalShadeLevel;
pCorpse->pAniTile->uiUserData = iIndex;
pCorpse->iID = iIndex;
pCorpse->fActivated = TRUE;
if ( Random( 100 ) > 50 )
{
pCorpse->fAttractCrowsOnlyWhenOnScreen = TRUE;
}
else
{
pCorpse->fAttractCrowsOnlyWhenOnScreen = FALSE;
}
pCorpse->iCachedTileID = pCorpse->pAniTile->sCachedTileID;
if ( pCorpse->iCachedTileID == -1 )
{
DeleteAniTile( pCorpse->pAniTile );
pCorpse->fActivated = FALSE;
return( -1 );
}
// Get palette and create palettes and do substitutions
if ( !CreateCorpsePalette( pCorpse ) )
{
DeleteAniTile( pCorpse->pAniTile );
pCorpse->fActivated = FALSE;
return( -1 );
}
SetRenderFlags(RENDER_FLAG_FULL);
if ( pCorpse->def.usFlags & ROTTING_CORPSE_VEHICLE )
{
pCorpse->pAniTile->uiFlags |= ( ANITILE_FORWARD | ANITILE_LOOPING );
// Turn off pause...
pCorpse->pAniTile->uiFlags &= (~ANITILE_PAUSED);
}
InvalidateWorldRedundency( );
// OK, loop through gridnos for corpse and remove blood.....
// Get root filename... this removes path and extension
// USed to find struct data fo rthis corpse...
GetRootName( zFilename, AniParams.zCachedFile );
// Add structure data.....
CheckForAndAddTileCacheStructInfo( pCorpse->pAniTile->pLevelNode, pCorpse->def.sGridNo, ( UINT16 ) ( pCorpse->iCachedTileID ), GetCorpseStructIndex( pCorpseDef, TRUE ) );
pStructureFileRef = GetCachedTileStructureRefFromFilename( zFilename );
if ( pStructureFileRef != NULL )
{
usStructIndex = GetCorpseStructIndex( pCorpseDef, TRUE );
pDBStructureRef = &(pStructureFileRef->pDBStructureRef[ usStructIndex ] );
for (ubLoop = 0; ubLoop < pDBStructureRef->pDBStructure->ubNumberOfTiles; ubLoop++)
{
ppTile = pDBStructureRef->ppTile;
sTileGridNo = pCorpseDef->sGridNo + ppTile[ ubLoop ]->sPosRelToBase;
//Remove blood
RemoveBlood( sTileGridNo, pCorpseDef->bLevel );
}
}
// OK, we're done!
return( iIndex );
}
void FreeCorpsePalettes( ROTTING_CORPSE *pCorpse )
{
INT32 cnt;
// Free palettes
MemFree( pCorpse->p8BPPPalette );
MemFree( pCorpse->p16BPPPalette );
for ( cnt = 0; cnt < NUM_CORPSE_SHADES; cnt++ )
{
if ( pCorpse->pShades[ cnt ] != NULL )
{
MemFree( pCorpse->pShades[ cnt ] );
pCorpse->pShades[ cnt ] = NULL;
}
}
}
void RemoveCorpses( )
{
INT32 iCount;
for(iCount=0; iCount < giNumRottingCorpse; iCount++)
{
if( ( gRottingCorpse[iCount].fActivated ) )
{
RemoveCorpse( iCount );
}
}
giNumRottingCorpse = 0;
}
void RemoveCorpse( INT32 iCorpseID )
{
// Remove!
gRottingCorpse[ iCorpseID ].fActivated = FALSE;
DeleteAniTile( gRottingCorpse[ iCorpseID ].pAniTile );
FreeCorpsePalettes( &( gRottingCorpse[ iCorpseID ] ) );
}
BOOLEAN CreateCorpsePalette( ROTTING_CORPSE *pCorpse )
{
CHAR8 zColFilename[ 100 ];
INT8 bBodyTypePalette;
SGPPaletteEntry Temp8BPPPalette[ 256 ];
pCorpse->p8BPPPalette = (SGPPaletteEntry *) MemAlloc( sizeof( SGPPaletteEntry ) * 256 );
CHECKF( pCorpse->p8BPPPalette != NULL );
bBodyTypePalette = GetBodyTypePaletteSubstitutionCode( NULL, pCorpse->def.ubBodyType, zColFilename );
// If this corpse has cammo,
if ( pCorpse->def.ubType == ROTTING_STAGE2 )
{
bBodyTypePalette = 0;
}
else if ( pCorpse->def.usFlags & ROTTING_CORPSE_USE_CAMO_PALETTE )
{
strcpy( zColFilename, "ANIMS\\camo.COL" );
bBodyTypePalette = 1;
}
else if ( pCorpse->def.usFlags & ROTTING_CORPSE_USE_URBAN_CAMO_PALETTE )
{
strcpy( zColFilename, "ANIMS\\urban.col" );
bBodyTypePalette = 1;
}
else if ( pCorpse->def.usFlags & ROTTING_CORPSE_USE_DESERT_CAMO_PALETTE )
{
strcpy( zColFilename, "ANIMS\\desert.col" );
bBodyTypePalette = 1;
}
else if ( pCorpse->def.usFlags & ROTTING_CORPSE_USE_SNOW_CAMO_PALETTE )
{
strcpy( zColFilename, "ANIMS\\snow.col" );
bBodyTypePalette = 1;
}
else if ( pCorpse->def.usFlags & ROTTING_CORPSE_USE_STEALTH_PALETTE )
{
strcpy( zColFilename, "ANIMS\\stealth.col" );
bBodyTypePalette = 1;
}
if ( bBodyTypePalette == -1 )
{
// Use palette from HVOBJECT, then use substitution for pants, etc
memcpy( pCorpse->p8BPPPalette, gpTileCache[ pCorpse->iCachedTileID ].pImagery->vo->pPaletteEntry, sizeof( pCorpse->p8BPPPalette ) * 256 );
// Substitute based on head, etc
SetPaletteReplacement( pCorpse->p8BPPPalette, pCorpse->def.HeadPal );
SetPaletteReplacement( pCorpse->p8BPPPalette, pCorpse->def.VestPal );
SetPaletteReplacement( pCorpse->p8BPPPalette, pCorpse->def.PantsPal );
SetPaletteReplacement( pCorpse->p8BPPPalette, pCorpse->def.SkinPal );
}
else if ( bBodyTypePalette == 0 )
{
// Use palette from hvobject
memcpy( pCorpse->p8BPPPalette, gpTileCache[ pCorpse->iCachedTileID ].pImagery->vo->pPaletteEntry, sizeof( pCorpse->p8BPPPalette ) * 256 );
}
else
{
// Use col file
if ( CreateSGPPaletteFromCOLFile( Temp8BPPPalette, zColFilename ) )
{
// Copy into palette
memcpy( pCorpse->p8BPPPalette, Temp8BPPPalette, sizeof( pCorpse->p8BPPPalette ) * 256 );
}
else
{
// Use palette from hvobject
memcpy( pCorpse->p8BPPPalette, gpTileCache[ pCorpse->iCachedTileID ].pImagery->vo->pPaletteEntry, sizeof( pCorpse->p8BPPPalette ) * 256 );
}
}
// -- BUILD 16BPP Palette from this
pCorpse->p16BPPPalette = Create16BPPPalette( pCorpse->p8BPPPalette );
CreateCorpsePaletteTables( pCorpse );
return( TRUE );
}
BOOLEAN TurnSoldierIntoCorpse( SOLDIERTYPE *pSoldier, BOOLEAN fRemoveMerc, BOOLEAN fCheckForLOS )
{
if ( pSoldier->sGridNo == NOWHERE )
{
return( FALSE );
}
ROTTING_CORPSE_DEFINITION Corpse;
UINT8 ubType;
UINT32 cnt;
UINT16 usItemFlags = 0; //WORLD_ITEM_DONTRENDER;
INT32 iCorpseID;
INT8 bVisible = -1;
OBJECTTYPE *pObj;
UINT8 ubNumGoo;
INT16 sNewGridNo;
// ATE: Change to fix crash when item in hand
if ( gpItemPointer != NULL && gpItemPointerSoldier == pSoldier )
{
CancelItemPointer( );
}
// Setup some values!
memset( &Corpse, 0, sizeof( Corpse ) );
Corpse.ubBodyType = pSoldier->ubBodyType;
Corpse.sGridNo = pSoldier->sGridNo;
Corpse.dXPos = pSoldier->dXPos;
Corpse.dYPos = pSoldier->dYPos;
Corpse.bLevel = pSoldier->pathing.bLevel;
Corpse.ubProfile = pSoldier->ubProfile;
if ( Corpse.bLevel > 0 )
{
Corpse.sHeightAdjustment = (INT16)( pSoldier->sHeightAdjustment - WALL_HEIGHT );
}
SET_PALETTEREP_ID ( Corpse.HeadPal, pSoldier->HeadPal );
SET_PALETTEREP_ID ( Corpse.VestPal, pSoldier->VestPal );
SET_PALETTEREP_ID ( Corpse.SkinPal, pSoldier->SkinPal );
SET_PALETTEREP_ID ( Corpse.PantsPal, pSoldier->PantsPal );
int urban = pSoldier->urbanCamo + pSoldier->wornUrbanCamo;
int jungle = pSoldier->bCamo + pSoldier->wornCamo;
int desert = pSoldier->desertCamo + pSoldier->wornDesertCamo;
int snow = pSoldier->snowCamo + pSoldier->wornSnowCamo;
int total = urban + jungle + desert + snow;
if ( GetWornStealth(pSoldier) >= 50 )
{
Corpse.usFlags |= ROTTING_CORPSE_USE_STEALTH_PALETTE;
}
else if ( total >= 50 )
{
// display camo depending on which is higher
if ( jungle >= urban && jungle >= desert && jungle >= snow )
Corpse.usFlags |= ROTTING_CORPSE_USE_CAMO_PALETTE;
else if ( urban >= jungle && urban >= desert && urban >= snow )
Corpse.usFlags |= ROTTING_CORPSE_USE_URBAN_CAMO_PALETTE;
else if ( desert >= urban && desert >= jungle && desert >= snow )
Corpse.usFlags |= ROTTING_CORPSE_USE_DESERT_CAMO_PALETTE;
else if ( snow >= urban && snow >= desert && snow >= jungle )
Corpse.usFlags |= ROTTING_CORPSE_USE_SNOW_CAMO_PALETTE;
}
// Determine corpse type!
ubType = (UINT8)gubAnimSurfaceCorpseID[ pSoldier->ubBodyType][ pSoldier->usAnimState ];
Corpse.ubDirection = pSoldier->ubDirection;
// If we are a vehicle.... only use 1 direction....
if ( pSoldier->flags.uiStatusFlags & SOLDIER_VEHICLE )
{
Corpse.usFlags |= ROTTING_CORPSE_VEHICLE;
if ( pSoldier->ubBodyType != ICECREAMTRUCK && pSoldier->ubBodyType != HUMVEE )
{
Corpse.ubDirection = 7;
}
else
{
Corpse.ubDirection = gb2DirectionsFrom8[ Corpse.ubDirection ];
}
}
if ( ubType == QUEEN_MONSTER_DEAD || ubType == BURNT_DEAD || ubType == EXPLODE_DEAD )
{
Corpse.ubDirection = 7;
}
// ATE: If bDirection, get opposite
// if ( ubType == SMERC_FALLF || ubType == MMERC_FALLF || ubType == FMERC_FALLF )
//{
// Corpse.bDirection = gOppositeDirection[ Corpse.bDirection ];
// }
// Set time of death
Corpse.uiTimeOfDeath = GetWorldTotalMin( );
// If corpse is not valid. make items visible
if ( ubType == NO_CORPSE && pSoldier->bTeam != gbPlayerNum )
{
usItemFlags &= (~WORLD_ITEM_DONTRENDER );
}
// ATE: If the queen is killed, she should
// make items visible because it ruins end sequence....
if ( pSoldier->ubProfile == QUEEN || pSoldier->bTeam == gbPlayerNum )
{
bVisible = 1;
}
// Not for a robot...
if ( AM_A_ROBOT( pSoldier ) )
{
}
else if ( ubType == QUEEN_MONSTER_DEAD )
{
gTacticalStatus.fLockItemLocators = FALSE;
ubNumGoo = 6 - ( gGameOptions.ubDifficultyLevel - DIF_LEVEL_EASY );
sNewGridNo = pSoldier->sGridNo + ( WORLD_COLS * 2 );
for ( cnt = 0; cnt < ubNumGoo; cnt++ )
{
CreateItem( JAR_QUEEN_CREATURE_BLOOD, 100, &gTempObject );
AddItemToPool( sNewGridNo, &gTempObject, bVisible , pSoldier->pathing.bLevel, usItemFlags, -1 );
}
}
else
{
// OK, Place what objects this guy was carrying on the ground!
for ( cnt = 0; cnt < pSoldier->inv.size(); cnt++ )
{
pObj = &( pSoldier->inv[ cnt ] );
if ( pObj->exists() == true )
{
// Check if it's supposed to be dropped
if ( !( (*pObj).fFlags & OBJECT_UNDROPPABLE ) || pSoldier->bTeam == gbPlayerNum )
{
// and make sure that it really is a droppable item type
// if ( !(Item[ pObj->usItem ].fFlags & ITEM_DEFAULT_UNDROPPABLE) )
if ( !(Item[ pObj->usItem ].defaultundroppable ) )
{
ReduceAmmoDroppedByNonPlayerSoldiers( pSoldier, cnt );
//if this soldier was an enemy
// Kaiden: Added from UB's reveal all items after combat feature!
if( pSoldier->bTeam == ENEMY_TEAM )
{
//add a flag to the item so when all enemies are killed, we can run through and reveal all the enemies items
usItemFlags |= WORLD_ITEM_DROPPED_FROM_ENEMY;
if ( Item[pObj->usItem].damageable && Item[pObj->usItem].usItemClass != IC_THROWING_KNIFE ) // Madd: drop crappier items from enemies on higher difficulty levels - note the quick fix for throwing knives
{
(*pObj)[0]->data.objectStatus -= (gGameOptions.ubDifficultyLevel - 1) * Random(20);
(*pObj)[0]->data.objectStatus = min(max((*pObj)[0]->data.objectStatus,1),100); // never below 1% or above 100%
}
}
AddItemToPool( pSoldier->sGridNo, pObj, bVisible , pSoldier->pathing.bLevel, usItemFlags, -1 );
}
}
}
}
DropKeysInKeyRing( pSoldier, pSoldier->sGridNo, pSoldier->pathing.bLevel, bVisible, FALSE, 0, FALSE );
}
// Make team look for items
AllSoldiersLookforItems( TRUE );
//Madd: set warning value to signal other enemies
if( pSoldier->bTeam == ENEMY_TEAM )
Corpse.ubAIWarningValue = 20;
// This should free up ABC for death codes
if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_ATTACK )
{
DebugAttackBusy( "%%%%%%%%% Freeing up attacker because soldier became a corpse.\n");
ReduceAttackBusyCount( );
}
//if we are to call TacticalRemoveSoldier after adding the corpse
if( fRemoveMerc )
{
// If not a player, you can completely remove soldiertype
// otherwise, just remove their graphic
if ( pSoldier->bTeam != gbPlayerNum )
{
// Remove merc!
// ATE: Remove merc slot first - will disappear if no corpse data found!
TacticalRemoveSoldier( pSoldier->ubID );
}
else
{
pSoldier->RemoveSoldierFromGridNo( );
}
if ( ubType == NO_CORPSE )
{
return( TRUE );
}
// Set type
Corpse.ubType = ubType;
// Add corpse!
iCorpseID = AddRottingCorpse( &Corpse );
}
else
{
if ( ubType == NO_CORPSE )
{
return( TRUE );
}
// Set type
Corpse.ubType = ubType;
// Add corpse!
iCorpseID = AddRottingCorpse( &Corpse );
}
// If this is our guy......make visible...
//if ( pSoldier->bTeam == gbPlayerNum )
{
MakeCorpseVisible( pSoldier, &( gRottingCorpse[ iCorpseID ] ) );
}
return( TRUE );
}
INT16 FindNearestRottingCorpse( SOLDIERTYPE *pSoldier )
{
INT32 uiRange, uiLowestRange = 999999;
INT16 sLowestGridNo = NOWHERE;
INT32 cnt;
ROTTING_CORPSE *pCorpse;
// OK, loop through our current listing of bodies
for ( cnt = 0; cnt < giNumRottingCorpse; cnt++ )
{
pCorpse = &(gRottingCorpse[ cnt ] );
if ( pCorpse->fActivated )
{
// Check rotting state
if ( pCorpse->def.ubType == ROTTING_STAGE2 )
{
uiRange = GetRangeInCellCoordsFromGridNoDiff( pSoldier->sGridNo, pCorpse->def.sGridNo );
if ( uiRange < uiLowestRange )
{
sLowestGridNo = pCorpse->def.sGridNo;
uiLowestRange = uiRange;
}
}
}
}
return( sLowestGridNo );
}
void AddCrowToCorpse( ROTTING_CORPSE *pCorpse )
{
SOLDIERCREATE_STRUCT MercCreateStruct;
INT8 bBodyType = CROW;
UINT8 iNewIndex;
INT16 sGridNo;
UINT8 ubDirection;
SOLDIERTYPE *pSoldier;
UINT8 ubRoomNum;
// No crows inside :(
if ( InARoom( pCorpse->def.sGridNo, &ubRoomNum ) )
{
return;
}
// Put him flying over corpse pisition
MercCreateStruct.ubProfile = NO_PROFILE;
MercCreateStruct.sSectorX = gWorldSectorX;
MercCreateStruct.sSectorY = gWorldSectorY;
MercCreateStruct.bSectorZ = gbWorldSectorZ;
MercCreateStruct.bBodyType = bBodyType;
MercCreateStruct.ubDirection = SOUTH;
MercCreateStruct.bTeam = CIV_TEAM;
MercCreateStruct.sInsertionGridNo = pCorpse->def.sGridNo;
RandomizeNewSoldierStats( &MercCreateStruct );
if ( TacticalCreateSoldier( &MercCreateStruct, &iNewIndex ) != NULL )
{
pSoldier = MercPtrs[ iNewIndex ];
sGridNo = FindRandomGridNoFromSweetSpot( pSoldier, pCorpse->def.sGridNo, 2, &ubDirection );
if ( sGridNo != NOWHERE )
{
pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
pSoldier->usStrategicInsertionData = sGridNo;
pSoldier->sInsertionGridNo = sGridNo;
pSoldier->sDesiredHeight = 0;
// Add to sector
AddSoldierToSector( iNewIndex );
// Change to fly animation
//sGridNo = FindRandomGridNoFromSweetSpot( pSoldier, pCorpse->def.sGridNo, 5, &ubDirection );
//pSoldier->usUIMovementMode = CROW_FLY;
//pSoldier->EVENT_GetNewSoldierPath( sGridNo, pSoldier->usUIMovementMode );
// Setup action data to point back to corpse....
pSoldier->aiData.uiPendingActionData1 = pCorpse->iID;
pSoldier->aiData.sPendingActionData2 = pCorpse->def.sGridNo;
pCorpse->def.bNumServicingCrows++;
}
}
}
void HandleCrowLeave( SOLDIERTYPE *pSoldier )
{
ROTTING_CORPSE *pCorpse;
// Check if this crow is still referencing the same corpse...
pCorpse = &(gRottingCorpse[ pSoldier->aiData.uiPendingActionData1 ] );
// Double check grindo...
if ( pSoldier->aiData.sPendingActionData2 == pCorpse->def.sGridNo )
{
// We have a match
// Adjust crow servicing count...
pCorpse->def.bNumServicingCrows--;
if ( pCorpse->def.bNumServicingCrows < 0 )
{
pCorpse->def.bNumServicingCrows = 0;
}
}
}
void HandleCrowFlyAway( SOLDIERTYPE *pSoldier )
{
UINT8 ubDirection;
INT16 sGridNo;
// Set desired height
pSoldier->sDesiredHeight = 100;
// Change to fly animation
sGridNo = FindRandomGridNoFromSweetSpot( pSoldier, pSoldier->sGridNo, 5, &ubDirection );
pSoldier->usUIMovementMode = CROW_FLY;
SendGetNewSoldierPathEvent( pSoldier, sGridNo, pSoldier->usUIMovementMode );
}
void HandleRottingCorpses( )
{
ROTTING_CORPSE *pCorpse;
INT8 bNumCrows = 0;
UINT32 uiChosenCorpseID;
// Don't allow crows here if flags not set
if ( !gTacticalStatus.fGoodToAllowCrows )
{
return;
}
// ATE: If it's too late, don't!
if( NightTime() )
{
return;
}
if ( gbWorldSectorZ > 0 )
{
return;
}
// ATE: Check for multiple crows.....
// Couint how many we have now...
{
UINT8 bLoop;
SOLDIERTYPE * pSoldier;
for (bLoop=gTacticalStatus.Team[ CIV_TEAM ].bFirstID, pSoldier=MercPtrs[bLoop]; bLoop <= gTacticalStatus.Team[ CIV_TEAM ].bLastID; bLoop++, pSoldier++)
{
if (pSoldier->bActive && pSoldier->bInSector && (pSoldier->stats.bLife >= OKLIFE) && !( pSoldier->flags.uiStatusFlags & SOLDIER_GASSED ) )
{
if ( pSoldier->ubBodyType == CROW )
{
bNumCrows++;
}
}
}
}
// Once population gets down to 0, we can add more again....
if ( bNumCrows == 0 )
{
gTacticalStatus.fDontAddNewCrows = FALSE;
}
if ( gTacticalStatus.fDontAddNewCrows )
{
return;
}
if ( bNumCrows >= gTacticalStatus.ubNumCrowsPossible )
{
gTacticalStatus.fDontAddNewCrows = TRUE;
return;
}
if ( gTacticalStatus.Team[ CREATURE_TEAM ].bTeamActive )
{
// don't add any crows while there are predators around
return;
}
// Pick one to attact a crow...
{
uiChosenCorpseID = Random( giNumRottingCorpse );
pCorpse = &(gRottingCorpse[ uiChosenCorpseID ] );
if ( pCorpse->fActivated )
{
if ( !( pCorpse->def.usFlags & ROTTING_CORPSE_VEHICLE ) )
{
if ( pCorpse->def.ubType == ROTTING_STAGE2 )
{
if ( GridNoOnScreen( pCorpse->def.sGridNo ) )
{
return;
}
AddCrowToCorpse( pCorpse );
AddCrowToCorpse( pCorpse );
}
}
}
}
}
void MakeCorpseVisible( SOLDIERTYPE *pSoldier, ROTTING_CORPSE *pCorpse )
{
pCorpse->def.bVisible = 1;
SetRenderFlags( RENDER_FLAG_FULL );
}
void AllMercsOnTeamLookForCorpse( ROTTING_CORPSE *pCorpse, INT8 bTeam )
{
INT32 cnt;
SOLDIERTYPE *pSoldier;
INT16 sGridNo;
// If this cump is already visible, return
if ( pCorpse->def.bVisible == 1 )
{
return;
}
if ( !pCorpse->fActivated )
{
return;
}
// IF IT'S THE SELECTED GUY, MAKE ANOTHER SELECTED!
cnt = gTacticalStatus.Team[ bTeam ].bFirstID;
sGridNo = pCorpse->def.sGridNo;
// look for all mercs on the same team,
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ bTeam ].bLastID; cnt++,pSoldier++ )
{
// ATE: Ok, lets check for some basic things here!
if ( pSoldier->stats.bLife >= OKLIFE && pSoldier->sGridNo != NOWHERE && pSoldier->bActive && pSoldier->bInSector )
{
// and we can trace a line of sight to his x,y coordinates?
// (taking into account we are definitely aware of this guy now)
if ( SoldierTo3DLocationLineOfSightTest( pSoldier, sGridNo, pCorpse->def.bLevel, 3, TRUE, CALC_FROM_ALL_DIRS ) )
{
MakeCorpseVisible( pSoldier, pCorpse );
return;
}
}
}
}
void MercLooksForCorpses( SOLDIERTYPE *pSoldier )
{
// Should we say disgust quote?
if ( ( pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_ROTTINGCORPSE ) )
{
return;
}
if ( pSoldier->ubProfile == NO_PROFILE )
{
return;
}
if ( AM_AN_EPC( pSoldier ) )
{
return;
}
if ( QuoteExp_HeadShotOnly[ pSoldier->ubProfile ] == 1 )
{
return;
}
// Every so often... do a corpse quote...
if ( Random( 400 ) <= 2 )
{
INT32 cnt;
INT16 sGridNo;
ROTTING_CORPSE *pCorpse;
// Loop through all corpses....
for ( cnt = 0; cnt < giNumRottingCorpse; cnt++ )
{
pCorpse = &(gRottingCorpse[ cnt ] );
if ( !pCorpse->fActivated )
{
continue;
}
// Has this corpse rotted enough?
if ( pCorpse->def.ubType == ROTTING_STAGE2 )
{
sGridNo = pCorpse->def.sGridNo;
// and we can trace a line of sight to his x,y coordinates?
// (taking into account we are definitely aware of this guy now)
if ( SoldierTo3DLocationLineOfSightTest( pSoldier, sGridNo, pCorpse->def.bLevel, 3, TRUE, CALC_FROM_ALL_DIRS ) )
{
TacticalCharacterDialogue( pSoldier, QUOTE_HEADSHOT );
pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_ROTTINGCORPSE;
BeginMultiPurposeLocator( sGridNo, pCorpse->def.bLevel, FALSE );
// Slide to...
SlideToLocation( 0, sGridNo );
return;
}
}
}
}
}
void RebuildAllCorpseShadeTables( )
{
INT32 cnt;
ROTTING_CORPSE *pCorpse;
// Loop through all corpses....
for ( cnt = 0; cnt < giNumRottingCorpse; cnt++ )
{
pCorpse = &(gRottingCorpse[ cnt ] );
// If this cump is already visible, continue
if ( pCorpse->def.bVisible == 1 )
{
continue;
}
if ( !pCorpse->fActivated )
{
continue;
}
// Rebuild shades....
}
}
UINT16 CreateCorpsePaletteTables( ROTTING_CORPSE *pCorpse )
{
SGPPaletteEntry LightPal[256];
UINT32 uiCount;
// create the basic shade table
for(uiCount=0; uiCount < 256; uiCount++)
{
// combine the rgb of the light color with the object's palette
LightPal[uiCount].peRed=(UINT8)(__min((UINT16)pCorpse->p8BPPPalette[uiCount].peRed+(UINT16)gpLightColors[0].peRed, 255));
LightPal[uiCount].peGreen=(UINT8)(__min((UINT16)pCorpse->p8BPPPalette[uiCount].peGreen+(UINT16)gpLightColors[0].peGreen, 255));
LightPal[uiCount].peBlue=(UINT8)(__min((UINT16)pCorpse->p8BPPPalette[uiCount].peBlue+(UINT16)gpLightColors[0].peBlue, 255));
}
// build the shade tables
CreateCorpseShadedPalette( pCorpse, 0, LightPal );
// build neutral palette as well!
// Set current shade table to neutral color
return(TRUE);
}
BOOLEAN CreateCorpseShadedPalette( ROTTING_CORPSE *pCorpse, UINT32 uiBase, SGPPaletteEntry *pShadePal )
{
UINT32 uiCount;
pCorpse->pShades[uiBase] = Create16BPPPaletteShaded( pShadePal, gusShadeLevels[0][0],
gusShadeLevels[0][1],
gusShadeLevels[0][2], TRUE);
for(uiCount=1; uiCount < 16; uiCount++)
{
pCorpse->pShades[uiBase+uiCount]=Create16BPPPaletteShaded( pShadePal, gusShadeLevels[uiCount][0],
gusShadeLevels[uiCount][1],
gusShadeLevels[uiCount][2], FALSE);
}
return(TRUE);
}
ROTTING_CORPSE *FindCorpseBasedOnStructure( INT16 sGridNo, STRUCTURE *pStructure )
{
LEVELNODE *pLevelNode;
ROTTING_CORPSE *pCorpse = NULL;
pLevelNode = gpWorldLevelData[ sGridNo ].pStructHead;
while( pLevelNode != NULL )
{
if (pLevelNode->pStructureData == pStructure )
{
break;
}
pLevelNode = pLevelNode->pNext;
}
if ( pLevelNode != NULL )
{
// Get our corpse....
pCorpse = &( gRottingCorpse[ pLevelNode->pAniTile->uiUserData ] );
}
return( pCorpse );
}
void CorpseHit( INT16 sGridNo, UINT16 usStructureID )
{
#if 0
STRUCTURE *pStructure, *pBaseStructure;
ROTTING_CORPSE *pCorpse = NULL;
INT16 sBaseGridNo;
pStructure = FindStructureByID( sGridNo, usStructureID );
// Get base....
pBaseStructure = FindBaseStructure( pStructure );
// Find base gridno...
sBaseGridNo = pBaseStructure->sGridNo;
// Get corpse ID.....
pCorpse = FindCorpseBasedOnStructure( sBaseGridNo, pBaseStructure );
if ( pCorpse == NULL )
{
#ifdef JA2TESTVERSION
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Bullet hit corpse but corpse cannot be found at: %d", sBaseGridNo );
#endif
return;
}
// Twitch the bugger...
#ifdef JA2BETAVERSION
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Corpse hit" );
#endif
if ( GridNoOnScreen( sBaseGridNo ) )
{
// Twitch....
// Set frame...
SetAniTileFrame( pCorpse->pAniTile, 1 );
// Go reverse...
pCorpse->pAniTile->uiFlags |= ( ANITILE_BACKWARD | ANITILE_PAUSE_AFTER_LOOP );
// Turn off pause...
pCorpse->pAniTile->uiFlags &= (~ANITILE_PAUSED);
}
// PLay a sound....
PlayJA2Sample( (UINT32)( BULLET_IMPACT_2 ), RATE_11025, SoundVolume( MIDVOLUME, sGridNo ), 1, SoundDir( sGridNo ) );
#endif
}
void VaporizeCorpse( INT16 sGridNo, UINT16 usStructureID )
{
STRUCTURE *pStructure, *pBaseStructure;
ROTTING_CORPSE *pCorpse = NULL;
INT16 sBaseGridNo;
ANITILE_PARAMS AniParams;
pStructure = FindStructureByID( sGridNo, usStructureID );
// Get base....
pBaseStructure = FindBaseStructure( pStructure );
// Find base gridno...
sBaseGridNo = pBaseStructure->sGridNo;
// Get corpse ID.....
pCorpse = FindCorpseBasedOnStructure( sBaseGridNo, pBaseStructure );
if ( pCorpse == NULL )
{
#ifdef JA2TESTVERSION
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Vaporize corpse but corpse cannot be found at: %d", sBaseGridNo );
#endif
return;
}
if ( pCorpse->def.usFlags & ROTTING_CORPSE_VEHICLE )
{
return;
}
if ( GridNoOnScreen( sBaseGridNo ) )
{
// Add explosion
memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) );
AniParams.sGridNo = sBaseGridNo;
AniParams.ubLevelID = ANI_STRUCT_LEVEL;
AniParams.sDelay = (INT16)( 80 );
AniParams.sStartFrame = 0;
AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD;
AniParams.sX = CenterX( sBaseGridNo );
AniParams.sY = CenterY( sBaseGridNo );
AniParams.sZ = (INT16)pCorpse->def.sHeightAdjustment;
strcpy( AniParams.zCachedFile, "TILECACHE\\GEN_BLOW.STI" );
CreateAnimationTile( &AniParams );
// Remove....
RemoveCorpse( pCorpse->iID );
SetRenderFlags( RENDER_FLAG_FULL );
if ( pCorpse->def.bLevel == 0 )
{
// Set some blood......
SpreadEffect( sBaseGridNo, (UINT8)( ( 2 ) ), 0, NOBODY, BLOOD_SPREAD_EFFECT, 0, -1 );
}
}
// PLay a sound....
PlayJA2Sample( (UINT32)( BODY_EXPLODE_1 ), RATE_11025, SoundVolume( HIGHVOLUME, sGridNo ), 1, SoundDir( sGridNo ) );
}
INT16 FindNearestAvailableGridNoForCorpse( ROTTING_CORPSE_DEFINITION *pDef, INT8 ubRadius )
{
INT16 sSweetGridNo;
INT16 sTop, sBottom;
INT16 sLeft, sRight;
INT16 cnt1, cnt2, cnt3;
INT16 sGridNo;
INT32 uiRange, uiLowestRange = 999999;
INT16 sLowestGridNo=0;
INT32 leftmost;
BOOLEAN fFound = FALSE;
SOLDIERTYPE soldier;
INT16 ubSaveNPCAPBudget;
UINT8 ubSaveNPCDistLimit;
STRUCTURE_FILE_REF * pStructureFileRef = NULL;
CHAR8 zFilename[150];
UINT8 ubBestDirection=0;
BOOLEAN fSetDirection = FALSE;
cnt3 = 0;
// Get root filename... this removes path and extension
// USed to find struct data fo rthis corpse...
GetRootName( zFilename, zCorpseFilenames[pDef->ubType ] );
pStructureFileRef = GetCachedTileStructureRefFromFilename( zFilename );
sSweetGridNo = pDef->sGridNo;
//Save AI pathing vars. changing the distlimit restricts how
//far away the pathing will consider.
ubSaveNPCAPBudget = gubNPCAPBudget;
ubSaveNPCDistLimit = gubNPCDistLimit;
gubNPCAPBudget = 0;
gubNPCDistLimit = ubRadius;
//create dummy soldier, and use the pathing to determine which nearby slots are
//reachable.
soldier.bTeam = 1;
soldier.sGridNo = sSweetGridNo;
sTop = ubRadius;
sBottom = -ubRadius;
sLeft = - ubRadius;
sRight = ubRadius;
//clear the mapelements of potential residue MAPELEMENT_REACHABLE flags
//in the square region.
for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
{
for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
{
sGridNo = sSweetGridNo + (WORLD_COLS * cnt1) + cnt2;
if ( sGridNo >=0 && sGridNo < WORLD_MAX )
{
gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE);
}
}
}
//Now, find out which of these gridnos are reachable
//(use the fake soldier and the pathing settings)
FindBestPath( &soldier, NOWHERE, 0, WALKING, COPYREACHABLE, 0 );
uiLowestRange = 999999;
for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
{
leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS;
for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
{
sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2;
if ( sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost && sGridNo < ( leftmost + WORLD_COLS ) &&
gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REACHABLE )
{
// Go on sweet stop
if ( NewOKDestination( &soldier, sGridNo, TRUE, soldier.pathing.bLevel ) )
{
BOOLEAN fDirectionFound = FALSE;
BOOLEAN fCanSetDirection = FALSE;
// Check each struct in each direction
if ( pStructureFileRef == NULL )
{
fDirectionFound = TRUE;
}
else
{
for( cnt3 = 0; cnt3 < 8; cnt3++ )
{
if ( OkayToAddStructureToWorld( (INT16)sGridNo, pDef->bLevel, &(pStructureFileRef->pDBStructureRef[gOneCDirection[ cnt3 ]]), INVALID_STRUCTURE_ID ) )
{
fDirectionFound = TRUE;
fCanSetDirection = TRUE;
break;
}
}
}
if ( fDirectionFound )
{
uiRange = GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo );
if ( uiRange < uiLowestRange )
{
if ( fCanSetDirection )
{
ubBestDirection = (UINT8)cnt3;
fSetDirection = TRUE;
}
sLowestGridNo = sGridNo;
uiLowestRange = uiRange;
fFound = TRUE;
}
}
}
}
}
}
gubNPCAPBudget = ubSaveNPCAPBudget;
gubNPCDistLimit = ubSaveNPCDistLimit;
if ( fFound )
{
if ( fSetDirection )
{
pDef->ubDirection = ubBestDirection;
}
return sLowestGridNo;
}
return NOWHERE;
}
BOOLEAN IsValidDecapitationCorpse( ROTTING_CORPSE *pCorpse )
{
if ( pCorpse->def.fHeadTaken )
{
return( FALSE );
}
return( gbCorpseValidForDecapitation[ pCorpse->def.ubType ] );
}
ROTTING_CORPSE *GetCorpseAtGridNo( INT16 sGridNo, INT8 bLevel )
{
STRUCTURE *pStructure, *pBaseStructure;
INT16 sBaseGridNo;
pStructure = FindStructure( sGridNo, STRUCTURE_CORPSE );
if ( pStructure != NULL )
{
// Get base....
pBaseStructure = FindBaseStructure( pStructure );
// Find base gridno...
sBaseGridNo = pBaseStructure->sGridNo;
if ( pBaseStructure != NULL )
{
return( FindCorpseBasedOnStructure( sBaseGridNo, pBaseStructure ) );
}
}
return( NULL );
}
void DecapitateCorpse( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bLevel )
{
ROTTING_CORPSE *pCorpse;
ROTTING_CORPSE_DEFINITION CorpseDef;
UINT16 usHeadIndex = HEAD_1;
pCorpse = GetCorpseAtGridNo( sGridNo, bLevel );
if ( pCorpse == NULL )
{
return;
}
if ( IsValidDecapitationCorpse( pCorpse ) )
{
// Decapitate.....
// Copy corpse definition...
memcpy( &CorpseDef, &(pCorpse->def), sizeof( ROTTING_CORPSE_DEFINITION ) );
// Add new one...
CorpseDef.ubType = gDecapitatedCorpse[ CorpseDef.ubType ];
pCorpse->def.fHeadTaken = TRUE;
if ( CorpseDef.ubType != 0 )
{
// Remove old one...
RemoveCorpse( pCorpse->iID );
AddRottingCorpse( &CorpseDef );
}
// Add head item.....
// Pick the head based on profile type...
switch( pCorpse->def.ubProfile )
{
case 83:
usHeadIndex = HEAD_2;
break;
case 111:
usHeadIndex = HEAD_3;
break;
case 64:
usHeadIndex = HEAD_4;
break;
case 112:
usHeadIndex = HEAD_5;
break;
case 82:
usHeadIndex = HEAD_6;
break;
case 110:
usHeadIndex = HEAD_7;
break;
}
CreateItem( usHeadIndex, 100, &gTempObject );
AddItemToPool( sGridNo, &gTempObject, INVISIBLE, 0, 0, 0 );
// All teams lok for this...
NotifySoldiersToLookforItems( );
}
}
void GetBloodFromCorpse( SOLDIERTYPE *pSoldier )
{
ROTTING_CORPSE *pCorpse;
INT8 bObjSlot;
//OBJECTTYPE Object = {};
// OK, get corpse
pCorpse = &( gRottingCorpse[ pSoldier->aiData.uiPendingActionData4 ] );
bObjSlot = FindObj( pSoldier, JAR );
// What kind of corpse ami I?
switch( pCorpse->def.ubType )
{
case ADULTMONSTER_DEAD:
case INFANTMONSTER_DEAD:
// Can get creature blood....
CreateItem( JAR_CREATURE_BLOOD, 100, &gTempObject );
break;
case QUEEN_MONSTER_DEAD:
CreateItem( JAR_QUEEN_CREATURE_BLOOD, 100, &gTempObject );
break;
default:
CreateItem( JAR_HUMAN_BLOOD, 100, &gTempObject );
break;
}
if ( bObjSlot != NO_SLOT )
{
SwapObjs( pSoldier, bObjSlot, &gTempObject, TRUE );
}
}
void ReduceAmmoDroppedByNonPlayerSoldiers( SOLDIERTYPE *pSoldier, INT32 iInvSlot )
{
Assert( pSoldier );
Assert( ( iInvSlot >= 0 ) && ( iInvSlot < (INT32)pSoldier->inv.size() ) );
// if not a player soldier
if ( pSoldier->bTeam != gbPlayerNum )
{
OBJECTTYPE *pObj = &( pSoldier->inv[ iInvSlot ] );
// if it's ammo
if ( Item[ pObj->usItem ].usItemClass == IC_AMMO )
{
//don't drop all the clips, just a random # of them between 1 and how many there are
pObj->ubNumberOfObjects = ( UINT8 ) ( 1 + Random( pObj->ubNumberOfObjects ) );
// recalculate the weight
//ADB ubWeight has been removed, see comments in OBJECTTYPE
//pObj->ubWeight = CalculateObjectWeight( pObj );
}
}
}
void LookForAndMayCommentOnSeeingCorpse( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubLevel )
{
ROTTING_CORPSE *pCorpse;
INT8 bToleranceThreshold = 0;
INT32 cnt;
SOLDIERTYPE *pTeamSoldier;
if ( QuoteExp_HeadShotOnly[ pSoldier->ubProfile ] == 1 )
{
return;
}
pCorpse = GetCorpseAtGridNo( sGridNo, ubLevel );
if ( pCorpse == NULL )
{
return;
}
if ( pCorpse->def.ubType != ROTTING_STAGE2 )
{
return;
}
// If servicing qrows, tolerance is now 1
if ( pCorpse->def.bNumServicingCrows > 0 )
{
bToleranceThreshold++;
}
// Check tolerance
if ( pSoldier->bCorpseQuoteTolerance <= bToleranceThreshold )
{
// Say quote...
TacticalCharacterDialogue( pSoldier, QUOTE_HEADSHOT );
BeginMultiPurposeLocator( sGridNo, ubLevel, FALSE );
// Reset values....
pSoldier->bCorpseQuoteTolerance = (INT8)( Random(3) + 1 );
// 50% chance of adding 1 to other mercs....
if ( Random( 2 ) == 1 )
{
// IF IT'S THE SELECTED GUY, MAKE ANOTHER SELECTED!
cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
// look for all mercs on the same team,
for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pTeamSoldier++ )
{
// ATE: Ok, lets check for some basic things here!
if ( pTeamSoldier->stats.bLife >= OKLIFE && pTeamSoldier->sGridNo != NOWHERE && pTeamSoldier->bActive && pTeamSoldier->bInSector )
{
pTeamSoldier->bCorpseQuoteTolerance++;
}
}
}
}
}
INT16 GetGridNoOfCorpseGivenProfileID( UINT8 ubProfileID )
{
INT32 cnt;
ROTTING_CORPSE *pCorpse;
// Loop through all corpses....
for ( cnt = 0; cnt < giNumRottingCorpse; cnt++ )
{
pCorpse = &(gRottingCorpse[ cnt ] );
if ( pCorpse->fActivated )
{
if ( pCorpse->def.ubProfile == ubProfileID )
{
return( pCorpse->def.sGridNo );
}
}
}
return( NOWHERE );
}
void DecayRottingCorpseAIWarnings( void )
{
INT32 cnt;
ROTTING_CORPSE * pCorpse;
for ( cnt = 0; cnt < giNumRottingCorpse; cnt++ )
{
pCorpse = &(gRottingCorpse[ cnt ] );
if ( pCorpse->fActivated && pCorpse->def.ubAIWarningValue > 0 )
{
pCorpse->def.ubAIWarningValue--;
}
}
}
UINT8 GetNearestRottingCorpseAIWarning( INT16 sGridNo )
{
INT32 cnt;
ROTTING_CORPSE * pCorpse;
UINT8 ubHighestWarning = 0;
for ( cnt = 0; cnt < giNumRottingCorpse; cnt++ )
{
pCorpse = &(gRottingCorpse[ cnt ] );
if ( pCorpse->fActivated && pCorpse->def.ubAIWarningValue > 0 )
{
if ( PythSpacesAway( sGridNo, pCorpse->def.sGridNo ) <= CORPSE_WARNING_DIST )
{
if ( pCorpse->def.ubAIWarningValue > ubHighestWarning )
{
ubHighestWarning = pCorpse->def.ubAIWarningValue;
}
}
}
}
return( ubHighestWarning );
}