mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
Finally fixed CalcMaxTossRange so that it pulls the correct grenade launcher item instead of just the first one, thereby allowing more than one range value per set of launcher items that launch the same grenades. git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5211 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2049 lines
50 KiB
C++
2049 lines
50 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
|
|
|
|
#include "Animation Control.h"
|
|
|
|
//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,
|
|
};
|
|
|
|
|
|
|
|
std::vector<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 );
|
|
void ReduceAttachmentsOnGunForNonPlayerChars(SOLDIERTYPE *pSoldier, OBJECTTYPE * pObj);
|
|
|
|
|
|
|
|
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;
|
|
INT32 sTileGridNo;
|
|
DB_STRUCTURE_TILE **ppTile;
|
|
UINT16 usStructIndex;
|
|
UINT32 uiDirectionUseFlag;
|
|
|
|
if (TileIsOutOfBounds(pCorpseDef->sGridNo))
|
|
{
|
|
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 (TileIsOutOfBounds(pSoldier->sGridNo))
|
|
{
|
|
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;
|
|
INT32 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;
|
|
///ddd{ ��� ����������� ������ ���������.
|
|
if ( pSoldier->bTeam != gbPlayerNum )
|
|
Corpse.ubAIWarningValue = 1;
|
|
//def.ubAIWarningValue = 1; //not used value!
|
|
///ddd}
|
|
|
|
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!
|
|
// HEADROCK HAM B2.8: Now also reveals equipment dropped by militia, if requirement is met.
|
|
if( pSoldier->bTeam == ENEMY_TEAM ||
|
|
( gGameExternalOptions.ubMilitiaDropEquipment == 2 && pSoldier->bTeam == MILITIA_TEAM ) ||
|
|
( gGameExternalOptions.ubMilitiaDropEquipment == 1 && pSoldier->bTeam == MILITIA_TEAM && Menptr[ pSoldier->ubAttackerID ].bTeam != OUR_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%
|
|
}
|
|
}
|
|
|
|
if(UsingNewAttachmentSystem()==true){
|
|
ReduceAttachmentsOnGunForNonPlayerChars(pSoldier, pObj);
|
|
}
|
|
// HEADROCK HAM B2.8: Militia will drop items only if allowed.
|
|
if (!(gGameExternalOptions.ubMilitiaDropEquipment == 0 && pSoldier->bTeam == MILITIA_TEAM ) &&
|
|
!(gGameExternalOptions.ubMilitiaDropEquipment == 1 && pSoldier->bTeam == MILITIA_TEAM && Menptr[ pSoldier->ubAttackerID ].bTeam == OUR_TEAM ))
|
|
{
|
|
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;
|
|
INT32 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;
|
|
INT32 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 (!TileIsOutOfBounds(sGridNo))
|
|
{
|
|
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;
|
|
INT32 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;
|
|
INT32 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 && !TileIsOutOfBounds(pSoldier->sGridNo) && 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[ pSoldier->ubProfile ].QuoteExpHeadShotOnly == 1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Every so often... do a corpse quote...
|
|
if ( Random( 400 ) <= 2 )
|
|
{
|
|
INT32 cnt;
|
|
INT32 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( INT32 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( INT32 sGridNo, UINT16 usStructureID )
|
|
{
|
|
#if 0
|
|
STRUCTURE *pStructure, *pBaseStructure;
|
|
ROTTING_CORPSE *pCorpse = NULL;
|
|
INT32 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( INT32 sGridNo, UINT16 usStructureID )
|
|
{
|
|
STRUCTURE *pStructure, *pBaseStructure;
|
|
ROTTING_CORPSE *pCorpse = NULL;
|
|
INT32 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 ) );
|
|
|
|
}
|
|
|
|
INT32 FindNearestAvailableGridNoForCorpse( ROTTING_CORPSE_DEFINITION *pDef, INT8 ubRadius )
|
|
{
|
|
INT32 sSweetGridNo;
|
|
INT16 sTop, sBottom;
|
|
INT16 sLeft, sRight;
|
|
INT16 cnt1, cnt2, cnt3;
|
|
INT32 sGridNo;
|
|
INT32 uiRange, uiLowestRange = 999999;
|
|
INT32 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, GRIDSIZE, 0, WALKING, COPYREACHABLE, 0 );//dnl ch50 071009
|
|
|
|
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( 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( INT32 sGridNo, INT8 bLevel )
|
|
{
|
|
STRUCTURE *pStructure, *pBaseStructure;
|
|
INT32 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, INT32 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 ReduceAttachmentsOnGunForNonPlayerChars(SOLDIERTYPE *pSoldier, OBJECTTYPE * pObj){
|
|
|
|
//Not meant for use in OAS.
|
|
Assert(UsingNewAttachmentSystem()==true);
|
|
|
|
//OBJECTTYPE * pObj = &(pSoldier->inv[ invSlot ]);
|
|
|
|
//If this item has any attachments, is not from a player, and is overwriteable. It's also only for guns.
|
|
if((*pObj)[0]->AttachmentListSize() > 0 && pSoldier->bTeam != gbPlayerNum && !((*pObj).fFlags & OBJECT_NO_OVERWRITE) && Item[pObj->usItem].usItemClass == IC_GUN && !gGameOptions.fEnemiesDropAllItems){
|
|
UINT8 slotCount = 0;
|
|
for(std::list<OBJECTTYPE>::iterator iter = (*pObj)[0]->attachments.begin(); iter != (*pObj)[0]->attachments.end(); ++iter, ++slotCount){
|
|
//A few conditions under which we will not delete this attachment
|
|
if(!iter->exists())
|
|
continue;
|
|
|
|
//Loop to find out if this attachment is a default one, if so, we don't want to remove it.
|
|
int i;
|
|
for(i = 0; i < MAX_DEFAULT_ATTACHMENTS && Item[pObj->usItem].defaultattachments[i] != iter->usItem; i++){}
|
|
|
|
if(Item[pObj->usItem].defaultattachments[i] != iter->usItem)
|
|
continue;
|
|
|
|
//Erase this attachment or not?
|
|
if(!Chance(gGameExternalOptions.usAttachmentDropRate)){
|
|
(*pObj).RemoveAttachment(&(*iter), 0, 0, pSoldier, 0, 1);
|
|
iter = (*pObj)[0]->attachments.begin();
|
|
advance(iter, slotCount);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
void LookForAndMayCommentOnSeeingCorpse( SOLDIERTYPE *pSoldier, INT32 sGridNo, UINT8 ubLevel )
|
|
{
|
|
ROTTING_CORPSE *pCorpse;
|
|
INT8 bToleranceThreshold = 0;
|
|
INT32 cnt;
|
|
SOLDIERTYPE *pTeamSoldier;
|
|
|
|
|
|
if ( QuoteExp[ pSoldier->ubProfile ].QuoteExpHeadShotOnly == 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 && !TileIsOutOfBounds(pTeamSoldier->sGridNo) && pTeamSoldier->bActive && pTeamSoldier->bInSector )
|
|
{
|
|
pTeamSoldier->bCorpseQuoteTolerance++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
INT32 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( INT32 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 );
|
|
}
|