mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
-disabled tracer light effects on rooftops, since lights don't work on roofs yet git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@303 3b4a5df2-a311-0410-b5c6-a8a6f20db521
11462 lines
304 KiB
C++
11462 lines
304 KiB
C++
#ifdef PRECOMPILEDHEADERS
|
|
#include "Tactical All.h"
|
|
#else
|
|
#include <wchar.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "wcheck.h"
|
|
#include "Render Fun.h"
|
|
#include "stdlib.h"
|
|
#include "debug.h"
|
|
#include "MemMan.h"
|
|
#include "Overhead Types.h"
|
|
#include "Soldier Control.h"
|
|
#include "Animation Cache.h"
|
|
#include "Animation Data.h"
|
|
#include "Animation Control.h"
|
|
#include "container.h"
|
|
#include <math.h>
|
|
#include "pathai.h"
|
|
#include "Random.h"
|
|
#include "worldman.h"
|
|
#include "Isometric Utils.h"
|
|
#include "Render Dirty.h"
|
|
#include "renderworld.h"
|
|
#include "sys globals.h"
|
|
#include "video.h"
|
|
#include "points.h"
|
|
#include "Win util.h"
|
|
#include "Sound Control.h"
|
|
#include "weapons.h"
|
|
#include "vobject_blitters.h"
|
|
#include "Handle UI.h"
|
|
#include "soldier ani.h"
|
|
#include "Event pump.h"
|
|
#include "opplist.h"
|
|
#include "ai.h"
|
|
#include "interface.h"
|
|
#include "lighting.h"
|
|
#include "faces.h"
|
|
#include "Soldier Profile.h"
|
|
#include "gap.h"
|
|
#include "interface panels.h"
|
|
#include "campaign.h"
|
|
#include "soldier macros.h"
|
|
#include "english.h"
|
|
#include "Squads.h"
|
|
|
|
#ifdef NETWORKED
|
|
#include "Networking.h"
|
|
#include "NetworkEvent.h"
|
|
#endif
|
|
#include "structure wrap.h"
|
|
#include "items.h"
|
|
#include "Soundman.h"
|
|
#include "utilities.h"
|
|
#include "Strategic.h"
|
|
#include "soldier tile.h"
|
|
#include "Smell.h"
|
|
#include "Keys.h"
|
|
#include "dialogue control.h"
|
|
#include "soldier functions.h"
|
|
#include "rt time defines.h"
|
|
#include "Exit Grids.h"
|
|
#include "gamescreen.h"
|
|
#include "Quests.h"
|
|
#include "message.h"
|
|
#include "NPC.h"
|
|
#include "SkillCheck.h"
|
|
#include "handle doors.h"
|
|
#include "interface dialogue.h"
|
|
#include "smokeeffects.h"
|
|
#include "GameSettings.h"
|
|
#include "tile animation.h"
|
|
#include "ShopKeeper Interface.h"
|
|
#include "Arms Dealer Init.h"
|
|
#include "vehicles.h"
|
|
#include "rotting corpses.h"
|
|
#include "Interface Control.h"
|
|
#include "strategicmap.h"
|
|
#include "morale.h"
|
|
#include "meanwhile.h"
|
|
#include "drugs and alcohol.h"
|
|
#include "SkillCheck.h"
|
|
#include "boxing.h"
|
|
#include "overhead map.h"
|
|
#endif
|
|
|
|
//turnspeed
|
|
//UINT8 gubPlayerTurnSpeedUpFactor = 1;
|
|
//UINT8 gubEnemyTurnSpeedUpFactor = 1;
|
|
//UINT8 gubCreatureTurnSpeedUpFactor = 1;
|
|
//UINT8 gubMilitiaTurnSpeedUpFactor = 1;
|
|
//UINT8 gubCivilianTurnSpeedUpFactor = 1;
|
|
//turnspeed
|
|
|
|
//extern BOOLEAN fAllowTacticalMilitiaCommand; //lal
|
|
|
|
extern INT16 DirIncrementer[8];
|
|
|
|
#define PALETTEFILENAME "BINARYDATA\\ja2pal.dat"
|
|
|
|
#define LOW_MORALE_BATTLE_SND_THREASHOLD 35
|
|
|
|
|
|
#define TURNING_FROM_PRONE_OFF 0
|
|
#define TURNING_FROM_PRONE_ON 1
|
|
#define TURNING_FROM_PRONE_START_UP_FROM_MOVE 2
|
|
#define TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE 3
|
|
|
|
#define MIN_SUBSEQUENT_SNDS_DELAY 2000
|
|
|
|
// Enumerate extended directions
|
|
enum
|
|
{
|
|
EX_NORTH = 0,
|
|
EX_NORTHEAST = 4,
|
|
EX_EAST = 8,
|
|
EX_SOUTHEAST = 12,
|
|
EX_SOUTH = 16,
|
|
EX_SOUTHWEST = 20,
|
|
EX_WEST = 24,
|
|
EX_NORTHWEST = 28,
|
|
EX_NUM_WORLD_DIRECTIONS = 32,
|
|
EX_DIRECTION_IRRELEVANT
|
|
} ExtendedWorldDirections;
|
|
|
|
// LUT for conversion from 8-direction to extended direction
|
|
UINT8 ubExtDirection[] =
|
|
{
|
|
EX_NORTH,
|
|
EX_NORTHEAST,
|
|
EX_EAST,
|
|
EX_SOUTHEAST,
|
|
EX_SOUTH,
|
|
EX_SOUTHWEST,
|
|
EX_WEST,
|
|
EX_NORTHWEST
|
|
};
|
|
|
|
|
|
UINT8 gExtOneCDirection[ EX_NUM_WORLD_DIRECTIONS ] =
|
|
{
|
|
4,
|
|
5,
|
|
6,
|
|
7,
|
|
|
|
8,
|
|
9,
|
|
10,
|
|
11,
|
|
|
|
12,
|
|
13,
|
|
14,
|
|
15,
|
|
|
|
16,
|
|
17,
|
|
18,
|
|
19,
|
|
|
|
20,
|
|
21,
|
|
22,
|
|
23,
|
|
|
|
24,
|
|
25,
|
|
26,
|
|
27,
|
|
|
|
28,
|
|
29,
|
|
30,
|
|
31,
|
|
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
};
|
|
|
|
|
|
typedef struct
|
|
{
|
|
CHAR8 zName[20];
|
|
UINT8 ubRandomVal;
|
|
BOOLEAN fPreload;
|
|
BOOLEAN fBadGuy;
|
|
BOOLEAN fDontAllowTwoInRow;
|
|
BOOLEAN fStopDialogue;
|
|
|
|
} BATTLESNDS_STRUCT;
|
|
|
|
|
|
BATTLESNDS_STRUCT gBattleSndsData[] =
|
|
{
|
|
"ok1", 2, 1, 1, 1, 2,
|
|
"ok2", 0, 1, 1, 1, 2,
|
|
"cool", 0, 1, 0, 1, 0,
|
|
"curse", 0, 1, 1, 1, 0,
|
|
"hit1", 2, 1, 1, 1, 1,
|
|
"hit2", 0, 1, 1, 1, 1,
|
|
"laugh", 0, 1, 1, 1, 0,
|
|
"attn", 0, 1, 0, 1, 0,
|
|
"dying", 0, 1, 1, 1, 1,
|
|
"humm", 0, 0, 0, 1, 1,
|
|
"noth", 0, 0, 0, 1, 1,
|
|
"gotit", 0, 0, 0, 1, 1,
|
|
"lmok1", 2, 1, 0, 1, 2,
|
|
"lmok2", 0, 1, 0, 1, 2,
|
|
"lmattn", 0, 1, 0, 1, 0,
|
|
"locked", 0, 0, 0, 1, 0,
|
|
"enem", 0, 1, 1, 1, 0,
|
|
};
|
|
|
|
|
|
|
|
BOOLEAN IsValidSecondHandShot( SOLDIERTYPE *pSoldier );
|
|
|
|
UINT8 *gubpNumReplacementsPerRange;
|
|
PaletteSubRangeType *gpPaletteSubRanges;
|
|
|
|
|
|
UINT8 bHealthStrRanges[] =
|
|
{
|
|
15,
|
|
30,
|
|
45,
|
|
60,
|
|
75,
|
|
90,
|
|
101
|
|
};
|
|
|
|
|
|
INT16 gsTerrainTypeSpeedModifiers[] =
|
|
{
|
|
5, // Flat ground
|
|
5, // Floor
|
|
5, // Paved road
|
|
5, // Dirt road
|
|
10, // LOW GRASS
|
|
15, // HIGH GRASS
|
|
20, // TRAIN TRACKS
|
|
20, // LOW WATER
|
|
25, // MID WATER
|
|
30 // DEEP WATER
|
|
};
|
|
|
|
//Kris:
|
|
//Temporary for testing the speed of the translucency. Pressing Ctrl+L in turn based
|
|
//input will toggle this flag. When clear, the translucency checking is turned off to
|
|
//increase the speed of the game.
|
|
BOOLEAN gfCalcTranslucency = FALSE;
|
|
|
|
|
|
INT16 gsFullTileDirections[ MAX_FULLTILE_DIRECTIONS ] =
|
|
{
|
|
-1, -WORLD_COLS - 1, -WORLD_COLS
|
|
|
|
};
|
|
|
|
PaletteReplacementType *gpPalRep;
|
|
|
|
// Palette ranges
|
|
UINT32 guiNumPaletteSubRanges;
|
|
PaletteSubRangeType *guipPaletteSubRanges = NULL;
|
|
// Palette replacements
|
|
UINT32 guiNumReplacements;
|
|
PaletteReplacementType *guipPaletteReplacements = NULL;
|
|
|
|
extern BOOLEAN fReDrawFace;
|
|
extern UINT8 gubWaitingForAllMercsToExitCode;
|
|
BOOLEAN gfGetNewPathThroughPeople = FALSE;
|
|
|
|
// LOCAL FUNCTIONS
|
|
// DO NOT CALL UNLESS THROUGH EVENT_SetSoldierPosition
|
|
UINT16 PickSoldierReadyAnimation( SOLDIERTYPE *pSoldier, BOOLEAN fEndReady );
|
|
BOOLEAN CheckForFullStruct( INT16 sGridNo, UINT16 *pusIndex );
|
|
void SetSoldierLocatorOffsets( SOLDIERTYPE *pSoldier );
|
|
void CheckForFullStructures( SOLDIERTYPE *pSoldier );
|
|
BOOLEAN InitNewSoldierState( SOLDIERTYPE *pSoldier, UINT8 ubNewState, UINT16 usStartingAniCode );
|
|
UINT16 GetNewSoldierStateFromNewStance( SOLDIERTYPE *pSoldier, UINT8 ubDesiredStance );
|
|
void SetSoldierAniSpeed( SOLDIERTYPE *pSoldier );
|
|
void AdjustForFastTurnAnimation( SOLDIERTYPE *pSoldier );
|
|
UINT16 SelectFireAnimation( SOLDIERTYPE *pSoldier, UINT8 ubHeight );
|
|
void SelectFallAnimation( SOLDIERTYPE *pSoldier );
|
|
BOOLEAN FullStructAlone( INT16 sGridNo, UINT8 ubRadius );
|
|
void SoldierGotHitGunFire( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial , UINT8 ubHitLocation);
|
|
void SoldierGotHitBlade( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation );
|
|
void SoldierGotHitPunch( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation );
|
|
void SoldierGotHitExplosion( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation );
|
|
UINT8 CalcScreamVolume( SOLDIERTYPE * pSoldier, UINT8 ubCombinedLoss );
|
|
void PlaySoldierFootstepSound( SOLDIERTYPE *pSoldier );
|
|
void HandleSystemNewAISituation( SOLDIERTYPE *pSoldier, BOOLEAN fResetABC );
|
|
|
|
UINT16 *CreateEnemyGlow16BPPPalette( SGPPaletteEntry *pPalette, UINT32 rscale, UINT32 gscale, BOOLEAN fAdjustGreen );
|
|
UINT16 *CreateEnemyGreyGlow16BPPPalette( SGPPaletteEntry *pPalette, UINT32 rscale, UINT32 gscale, BOOLEAN fAdjustGreen );
|
|
|
|
void SoldierBleed( SOLDIERTYPE *pSoldier, BOOLEAN fBandagedBleed );
|
|
INT32 CheckBleeding( SOLDIERTYPE *pSoldier );
|
|
|
|
void EVENT_InternalSetSoldierDesiredDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection, BOOLEAN fInitalMove, UINT16 usAnimState );
|
|
|
|
#ifdef JA2BETAVERSION
|
|
extern void ValidatePlayersAreInOneGroupOnly();
|
|
extern void MapScreenDefaultOkBoxCallback( UINT8 bExitValue );
|
|
void SAIReportError( STR16 wErrorString );
|
|
#endif
|
|
|
|
UINT32 SleepDartSuccumbChance( SOLDIERTYPE * pSoldier );
|
|
|
|
void EnableDisableSoldierLightEffects( BOOLEAN fEnableLights );
|
|
void SetSoldierPersonalLightLevel( SOLDIERTYPE *pSoldier );
|
|
|
|
|
|
void HandleVehicleMovementSound( SOLDIERTYPE *pSoldier, BOOLEAN fOn )
|
|
{
|
|
VEHICLETYPE *pVehicle = &( pVehicleList[ pSoldier->bVehicleID ] );
|
|
|
|
if ( fOn )
|
|
{
|
|
if ( pVehicle->iMovementSoundID == NO_SAMPLE )
|
|
{
|
|
pVehicle->iMovementSoundID = PlayJA2Sample( pVehicle->iMoveSound, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pVehicle->iMovementSoundID != NO_SAMPLE )
|
|
{
|
|
SoundStop( pVehicle->iMovementSoundID );
|
|
pVehicle->iMovementSoundID = NO_SAMPLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void AdjustNoAPToFinishMove( SOLDIERTYPE *pSoldier, BOOLEAN fSet )
|
|
{
|
|
if ( pSoldier->ubBodyType == CROW )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Check if we are a vehicle first....
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
|
|
{
|
|
// Turn off sound effects....
|
|
if ( fSet )
|
|
{
|
|
HandleVehicleMovementSound( pSoldier, FALSE );
|
|
}
|
|
}
|
|
|
|
// Turn off sound effects....
|
|
if ( fSet )
|
|
{
|
|
// Position light....
|
|
// SetCheckSoldierLightFlag( pSoldier );
|
|
}
|
|
else
|
|
{
|
|
// DeleteSoldierLight( pSoldier );
|
|
}
|
|
|
|
pSoldier->fNoAPToFinishMove = fSet;
|
|
|
|
if ( !fSet )
|
|
{
|
|
// return reason to default value
|
|
pSoldier->ubReasonCantFinishMove = REASON_STOPPED_NO_APS;
|
|
}
|
|
}
|
|
|
|
void HandleCrowShadowVisibility( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( pSoldier->ubBodyType == CROW )
|
|
{
|
|
if ( pSoldier->usAnimState == CROW_FLY )
|
|
{
|
|
if ( pSoldier->pAniTile != NULL )
|
|
{
|
|
if ( pSoldier->bLastRenderVisibleValue != -1 )
|
|
{
|
|
HideAniTile( pSoldier->pAniTile, FALSE );
|
|
}
|
|
else
|
|
{
|
|
HideAniTile( pSoldier->pAniTile, TRUE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void HandleCrowShadowNewGridNo( SOLDIERTYPE *pSoldier )
|
|
{
|
|
ANITILE_PARAMS AniParams;
|
|
|
|
memset( &AniParams, 0, sizeof( ANITILE_PARAMS ) );
|
|
|
|
if ( pSoldier->ubBodyType == CROW )
|
|
{
|
|
if ( pSoldier->pAniTile != NULL )
|
|
{
|
|
DeleteAniTile( pSoldier->pAniTile );
|
|
pSoldier->pAniTile = NULL;
|
|
}
|
|
|
|
if ( pSoldier->sGridNo != NOWHERE )
|
|
{
|
|
if ( pSoldier->usAnimState == CROW_FLY )
|
|
{
|
|
AniParams.sGridNo = (INT16)pSoldier->sGridNo;
|
|
AniParams.ubLevelID = ANI_SHADOW_LEVEL;
|
|
AniParams.sDelay = pSoldier->sAniDelay;
|
|
AniParams.sStartFrame = 0;
|
|
AniParams.uiFlags = ANITILE_CACHEDTILE | ANITILE_FORWARD | ANITILE_LOOPING | ANITILE_USE_DIRECTION_FOR_START_FRAME;
|
|
AniParams.sX = pSoldier->sX;
|
|
AniParams.sY = pSoldier->sY;
|
|
AniParams.sZ = 0;
|
|
strcpy( AniParams.zCachedFile, "TILECACHE\\FLY_SHDW.STI" );
|
|
|
|
AniParams.uiUserData3 = pSoldier->bDirection;
|
|
|
|
pSoldier->pAniTile = CreateAnimationTile( &AniParams );
|
|
|
|
HandleCrowShadowVisibility( pSoldier );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void HandleCrowShadowRemoveGridNo( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( pSoldier->ubBodyType == CROW )
|
|
{
|
|
if ( pSoldier->usAnimState == CROW_FLY )
|
|
{
|
|
if ( pSoldier->pAniTile != NULL )
|
|
{
|
|
DeleteAniTile( pSoldier->pAniTile );
|
|
pSoldier->pAniTile = NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void HandleCrowShadowNewDirection( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( pSoldier->ubBodyType == CROW )
|
|
{
|
|
if ( pSoldier->usAnimState == CROW_FLY )
|
|
{
|
|
if ( pSoldier->pAniTile != NULL )
|
|
{
|
|
pSoldier->pAniTile->uiUserData3 = pSoldier->bDirection;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void HandleCrowShadowNewPosition( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( pSoldier->ubBodyType == CROW )
|
|
{
|
|
if ( pSoldier->usAnimState == CROW_FLY )
|
|
{
|
|
if ( pSoldier->pAniTile != NULL )
|
|
{
|
|
pSoldier->pAniTile->sRelativeX = pSoldier->sX;
|
|
pSoldier->pAniTile->sRelativeY = pSoldier->sY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
INT8 CalcActionPoints(SOLDIERTYPE *pSold)
|
|
{
|
|
UINT8 ubPoints,ubMaxAPs;
|
|
INT8 bBandage;
|
|
|
|
// dead guys don't get any APs (they shouldn't be here asking for them!)
|
|
if (!pSold->bLife)
|
|
return(0);
|
|
|
|
// people with sleep dart drug who have collapsed get no APs
|
|
if ( (pSold->bSleepDrugCounter > 0) && pSold->bCollapsed )
|
|
return( 0 );
|
|
|
|
// Calculate merc's action points at 100% capability (range is 10 - 25)
|
|
// round fractions of .5 up (that's why the +20 before the division!
|
|
ubPoints = 5 + (((10 * EffectiveExpLevel( pSold ) +
|
|
3 * EffectiveAgility( pSold ) +
|
|
2 * pSold->bLifeMax +
|
|
2 * EffectiveDexterity( pSold ) ) + 20) / 40);
|
|
|
|
//if (GameOption[INCREASEDAP] % 2 == 1)
|
|
//points += AP_INCREASE;
|
|
|
|
//Madd: Add in AP Bonuses (Penalties) from worn gear
|
|
ubPoints += GetGearAPBonus ( pSold );
|
|
|
|
// Calculate bandage
|
|
bBandage = pSold->bLifeMax - pSold->bLife - pSold->bBleeding;
|
|
|
|
// If injured, reduce action points accordingly (by up to 2/3rds)
|
|
if (pSold->bLife < pSold->bLifeMax)
|
|
{
|
|
ubPoints -= (2 * ubPoints * (pSold->bLifeMax - pSold->bLife + (bBandage / 2))) /
|
|
(3 * pSold->bLifeMax);
|
|
}
|
|
|
|
// If tired, reduce action points accordingly (by up to 1/2)
|
|
if (pSold->bBreath < 100)
|
|
ubPoints -= (ubPoints * (100 - pSold->bBreath)) / 200;
|
|
|
|
if (pSold->sWeightCarriedAtTurnStart > 100)
|
|
{
|
|
ubPoints = (UINT8) ( ((UINT32)ubPoints) * 100 / pSold->sWeightCarriedAtTurnStart );
|
|
}
|
|
|
|
// If resulting APs are below our permitted minimum, raise them to it!
|
|
if (ubPoints < AP_MINIMUM)
|
|
ubPoints = AP_MINIMUM;
|
|
|
|
// make sure action points doesn't exceed the permitted maximum
|
|
ubMaxAPs = gubMaxActionPoints[ pSold->ubBodyType ];
|
|
|
|
//if (GameOption[INCREASEDAP] % 2 == 1)
|
|
// maxAPs += AP_INCREASE;
|
|
|
|
|
|
// If resulting APs are below our permitted minimum, raise them to it!
|
|
if (ubPoints > ubMaxAPs)
|
|
ubPoints = ubMaxAPs;
|
|
|
|
if ( pSold->ubBodyType == BLOODCAT )
|
|
{
|
|
// use same as young monsters
|
|
ubPoints = (ubPoints * AP_YOUNG_MONST_FACTOR) / 10;
|
|
}
|
|
else if (pSold->uiStatusFlags & SOLDIER_MONSTER)
|
|
{
|
|
// young monsters get extra APs
|
|
if ( pSold->ubBodyType == YAF_MONSTER || pSold->ubBodyType == YAM_MONSTER || pSold->ubBodyType == INFANT_MONSTER )
|
|
{
|
|
ubPoints = (ubPoints * AP_YOUNG_MONST_FACTOR) / 10;
|
|
}
|
|
|
|
// if frenzied, female monsters get more APs! (for young females, cumulative!)
|
|
if (pSold->bFrenzied)
|
|
{
|
|
|
|
ubPoints = (ubPoints * AP_MONST_FRENZY_FACTOR) / 10;
|
|
}
|
|
}
|
|
|
|
// adjust APs for phobia situations
|
|
if ( pSold->ubProfile != NO_PROFILE )
|
|
{
|
|
if ( (gMercProfiles[ pSold->ubProfile ].bPersonalityTrait == CLAUSTROPHOBIC) && (gbWorldSectorZ > 0) )
|
|
{
|
|
ubPoints = (ubPoints * AP_CLAUSTROPHOBE) / 10;
|
|
}
|
|
else if ( (gMercProfiles[ pSold->ubProfile ].bPersonalityTrait == FEAR_OF_INSECTS) && (MercSeesCreature( pSold ) ) )
|
|
{
|
|
ubPoints = (ubPoints * AP_AFRAID_OF_INSECTS) / 10;
|
|
}
|
|
}
|
|
|
|
// Adjusat APs due to drugs...
|
|
HandleAPEffectDueToDrugs( pSold, &ubPoints );
|
|
|
|
// If we are a vehicle, adjust APS...
|
|
if ( pSold->uiStatusFlags & SOLDIER_VEHICLE )
|
|
{
|
|
AdjustVehicleAPs( pSold, &ubPoints );
|
|
}
|
|
|
|
//Madd
|
|
// if ( pSold->bTeam != CIV_TEAM && pSold->bTeam != gbPlayerNum && gGameOptions.ubDifficultyLevel == DIF_LEVEL_INSANE )
|
|
// ubPoints += 5; // everything and everyone moves a little faster against you on insane...
|
|
|
|
|
|
//Kaiden: Took your idea a step further adding the bonus for each difficulty level
|
|
// and then externalized it. AND added it to the Dont max out points section below.
|
|
if ( pSold->bTeam != CIV_TEAM && pSold->bTeam != gbPlayerNum)
|
|
{
|
|
|
|
switch( gGameOptions.ubDifficultyLevel )
|
|
{
|
|
case DIF_LEVEL_EASY:
|
|
|
|
ubPoints += gGameExternalOptions.iEasyAPBonus;
|
|
break;
|
|
|
|
case DIF_LEVEL_MEDIUM:
|
|
|
|
ubPoints += gGameExternalOptions.iExperiencedAPBonus;
|
|
break;
|
|
|
|
case DIF_LEVEL_HARD:
|
|
|
|
ubPoints += gGameExternalOptions.iExpertAPBonus;
|
|
break;
|
|
|
|
case DIF_LEVEL_INSANE:
|
|
|
|
ubPoints += gGameExternalOptions.iInsaneAPBonus;
|
|
break;
|
|
|
|
default:
|
|
ubPoints +=0;
|
|
}
|
|
}
|
|
|
|
// if we are in boxing mode, adjust APs... THIS MUST BE LAST!
|
|
if ( gTacticalStatus.bBoxingState == BOXING || gTacticalStatus.bBoxingState == PRE_BOXING )
|
|
{
|
|
ubPoints /= 2;
|
|
}
|
|
|
|
return (ubPoints);
|
|
}
|
|
|
|
void CalcNewActionPoints( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( gTacticalStatus.bBoxingState == BOXING || gTacticalStatus.bBoxingState == PRE_BOXING )
|
|
{
|
|
// if we are in boxing mode, carry 1/2 as many points
|
|
if (pSoldier->bActionPoints > MAX_AP_CARRIED / 2)
|
|
{
|
|
pSoldier->bActionPoints = MAX_AP_CARRIED / 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pSoldier->bActionPoints > MAX_AP_CARRIED)
|
|
{
|
|
pSoldier->bActionPoints = MAX_AP_CARRIED;
|
|
}
|
|
}
|
|
|
|
pSoldier->bActionPoints += CalcActionPoints( pSoldier);
|
|
|
|
// Don't max out if we are drugged....
|
|
if ( !GetDrugEffect( pSoldier, DRUG_TYPE_ADRENALINE ) )
|
|
{ //Kaiden: If Enemy, they can max out, but their Max is NOW = MAX + diffAPBonus
|
|
// No sense in giving them a bonus if some of the points are wasted because we
|
|
// Didn't raise their cap by the same amount.
|
|
if ( pSoldier->bTeam != CIV_TEAM && pSoldier->bTeam != gbPlayerNum)
|
|
{
|
|
switch( gGameOptions.ubDifficultyLevel )
|
|
{
|
|
case DIF_LEVEL_EASY:
|
|
|
|
pSoldier->bActionPoints = __min( pSoldier->bActionPoints, (gubMaxActionPoints[ pSoldier->ubBodyType ] + gGameExternalOptions.iEasyAPBonus));
|
|
break;
|
|
|
|
case DIF_LEVEL_MEDIUM:
|
|
|
|
pSoldier->bActionPoints = __min( pSoldier->bActionPoints, (gubMaxActionPoints[ pSoldier->ubBodyType ] + gGameExternalOptions.iExperiencedAPBonus));
|
|
break;
|
|
|
|
case DIF_LEVEL_HARD:
|
|
|
|
pSoldier->bActionPoints = __min( pSoldier->bActionPoints, (gubMaxActionPoints[ pSoldier->ubBodyType ] + gGameExternalOptions.iExpertAPBonus));
|
|
break;
|
|
|
|
case DIF_LEVEL_INSANE:
|
|
|
|
pSoldier->bActionPoints = __min( pSoldier->bActionPoints, (gubMaxActionPoints[ pSoldier->ubBodyType ] + gGameExternalOptions.iInsaneAPBonus));
|
|
break;
|
|
|
|
default:
|
|
pSoldier->bActionPoints = __min( pSoldier->bActionPoints, gubMaxActionPoints[ pSoldier->ubBodyType ] );
|
|
}
|
|
|
|
}
|
|
else
|
|
{ //Kaiden: Players just max out normally unless drugged
|
|
pSoldier->bActionPoints = __min( pSoldier->bActionPoints, gubMaxActionPoints[ pSoldier->ubBodyType ] );
|
|
}
|
|
|
|
}
|
|
|
|
pSoldier->bInitialActionPoints = pSoldier->bActionPoints;
|
|
}
|
|
|
|
|
|
void DoNinjaAttack( SOLDIERTYPE *pSoldier )
|
|
{
|
|
//UINT32 uiMercFlags;
|
|
UINT16 usSoldierIndex;
|
|
SOLDIERTYPE *pTSoldier;
|
|
UINT8 ubTDirection;
|
|
UINT8 ubTargetStance;
|
|
|
|
|
|
usSoldierIndex = WhoIsThere2( pSoldier->sTargetGridNo, pSoldier->bLevel );
|
|
if ( usSoldierIndex != NOBODY )
|
|
{
|
|
GetSoldier( &pTSoldier, usSoldierIndex );
|
|
|
|
// Look at stance of target
|
|
ubTargetStance = gAnimControl[ pTSoldier->usAnimState ].ubEndHeight;
|
|
|
|
// Get his life...if < certain value, do finish!
|
|
if ( (pTSoldier->bLife <= 30 || pTSoldier->bBreath <= 30) && ubTargetStance != ANIM_PRONE )
|
|
{
|
|
// Do finish!
|
|
ChangeSoldierState( pSoldier, NINJA_SPINKICK, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
if ( ubTargetStance != ANIM_PRONE )
|
|
{
|
|
if ( Random( 2 ) == 0 )
|
|
{
|
|
ChangeSoldierState( pSoldier, NINJA_LOWKICK, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
ChangeSoldierState( pSoldier, NINJA_PUNCH, 0 , FALSE );
|
|
}
|
|
|
|
// CHECK IF HE CAN SEE US, IF SO CHANGE DIRECTION
|
|
if ( pTSoldier->bOppList[ pSoldier->ubID ] == 0 && pTSoldier->bTeam != pSoldier->bTeam )
|
|
{
|
|
if ( !( pTSoldier->uiStatusFlags & ( SOLDIER_MONSTER | SOLDIER_ANIMAL | SOLDIER_VEHICLE ) ) )
|
|
{
|
|
ubTDirection = (UINT8)GetDirectionFromGridNo( pSoldier->sGridNo, pTSoldier );
|
|
SendSoldierSetDesiredDirectionEvent( pTSoldier, ubTDirection );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// CHECK OUR STANCE
|
|
if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH )
|
|
{
|
|
// SET DESIRED STANCE AND SET PENDING ANIMATION
|
|
SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
|
|
pSoldier->usPendingAnimation = PUNCH_LOW;
|
|
}
|
|
else
|
|
{
|
|
// USE crouched one
|
|
// NEED TO CHANGE STANCE IF NOT CROUCHD!
|
|
EVENT_InitNewSoldierAnim( pSoldier, PUNCH_LOW, 0 , FALSE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->ubProfile == 33 )
|
|
{
|
|
UINT32 uiSoundID;
|
|
SOUNDPARMS spParms;
|
|
INT32 iFaceIndex;
|
|
|
|
// Play sound!
|
|
memset(&spParms, 0xff, sizeof(SOUNDPARMS));
|
|
|
|
spParms.uiSpeed = RATE_11025;
|
|
spParms.uiVolume = (INT8)CalculateSpeechVolume( HIGHVOLUME );
|
|
|
|
// If we are an enemy.....reduce due to volume
|
|
if ( pSoldier->bTeam != gbPlayerNum )
|
|
{
|
|
spParms.uiVolume = SoundVolume( (UINT8)spParms.uiVolume, pSoldier->sGridNo );
|
|
}
|
|
spParms.uiLoop = 1;
|
|
spParms.uiPan = SoundDir( pSoldier->sGridNo );
|
|
spParms.uiPriority=GROUP_PLAYER;
|
|
|
|
if ( pSoldier->usAnimState == NINJA_SPINKICK )
|
|
{
|
|
uiSoundID = SoundPlay( "BATTLESNDS\\033_CHOP2.WAV", &spParms );
|
|
}
|
|
else
|
|
{
|
|
if ( Random( 2 ) == 0 )
|
|
{
|
|
uiSoundID = SoundPlay( "BATTLESNDS\\033_CHOP3.WAV", &spParms );
|
|
}
|
|
else
|
|
{
|
|
uiSoundID = SoundPlay( "BATTLESNDS\\033_CHOP1.WAV", &spParms );
|
|
}
|
|
}
|
|
|
|
|
|
if ( uiSoundID != SOUND_ERROR )
|
|
{
|
|
pSoldier->uiBattleSoundID = uiSoundID;
|
|
|
|
if ( pSoldier->ubProfile != NO_PROFILE )
|
|
{
|
|
// Get soldier's face ID
|
|
iFaceIndex = pSoldier->iFaceIndex;
|
|
|
|
// Check face index
|
|
if ( iFaceIndex != -1 )
|
|
{
|
|
ExternSetFaceTalking( iFaceIndex, uiSoundID );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN CreateSoldierCommon( UINT8 ubBodyType, SOLDIERTYPE *pSoldier, UINT16 usSoldierID, UINT16 usState )
|
|
{
|
|
BOOLEAN fSuccess = FALSE;
|
|
INT32 iCounter = 0;
|
|
|
|
//if we are loading a saved game, we DO NOT want to reset the opplist, look for enemies, or say a dying commnet
|
|
if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
|
|
{
|
|
// Set initial values for opplist!
|
|
InitSoldierOppList( pSoldier );
|
|
HandleSight( pSoldier, SIGHT_LOOK );
|
|
|
|
// Set some quote flags
|
|
if ( pSoldier->bLife >= OKLIFE )
|
|
{
|
|
pSoldier->fDyingComment = FALSE;
|
|
}
|
|
else
|
|
{
|
|
pSoldier->fDyingComment = TRUE;
|
|
}
|
|
}
|
|
|
|
// ATE: Reset some timer flags...
|
|
pSoldier->uiTimeSameBattleSndDone = 0;
|
|
// ATE: Reset every time.....
|
|
pSoldier->fSoldierWasMoving = TRUE;
|
|
pSoldier->iTuringSoundID = NO_SAMPLE;
|
|
pSoldier->uiTimeSinceLastBleedGrunt = 0;
|
|
|
|
if ( pSoldier->ubBodyType == QUEENMONSTER )
|
|
{
|
|
pSoldier->iPositionSndID = NewPositionSnd( NOWHERE, POSITION_SOUND_FROM_SOLDIER, (UINT32)pSoldier, QUEEN_AMBIENT_NOISE );
|
|
}
|
|
|
|
|
|
// ANYTHING AFTER HERE CAN FAIL
|
|
do
|
|
{
|
|
|
|
if (usSoldierID <= gTacticalStatus.Team[ OUR_TEAM ].bLastID)
|
|
{
|
|
pSoldier->pKeyRing = (KEY_ON_RING *) MemAlloc( NUM_KEYS * sizeof( KEY_ON_RING ) );
|
|
memset( pSoldier->pKeyRing , 0, NUM_KEYS * sizeof( KEY_ON_RING ) );
|
|
|
|
for( iCounter = 0; iCounter < NUM_KEYS; iCounter++ )
|
|
{
|
|
pSoldier->pKeyRing[ iCounter ].ubKeyID = INVALID_KEY_NUMBER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pSoldier->pKeyRing = NULL;
|
|
}
|
|
// Create frame cache
|
|
if( InitAnimationCache( usSoldierID, &( pSoldier->AnimCache) ) == FALSE )
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed animation cache creation" ) );
|
|
break;
|
|
}
|
|
|
|
if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
|
|
{
|
|
// Init new soldier state
|
|
// OFFSET FIRST ANIMATION FRAME FOR NEW MERCS
|
|
if ( usState != STANDING )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, usState, (UINT8)0, TRUE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, usState, (UINT8)Random( 10 ), TRUE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/// if we don't have a world loaded, and are in a bad anim, goto standing.
|
|
// bad anims are: HOPFENCE,
|
|
// CLIMBDOWNROOF, FALLFORWARD_ROOF,FALLOFF, CLIMBUPROOF
|
|
if( !gfWorldLoaded &&
|
|
( usState == HOPFENCE ||
|
|
usState == CLIMBDOWNROOF ||
|
|
usState == FALLFORWARD_ROOF ||
|
|
usState == FALLOFF ||
|
|
usState == CLIMBUPROOF ) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, STANDING, 0, TRUE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, usState, pSoldier->usAniCode, TRUE );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//if ( pSoldier->pBackGround != NULL )
|
|
// MemFree( pSoldier->pBackGround );
|
|
|
|
// INIT ANIMATION DATA
|
|
//if((pSoldier->pBackGround=MemAlloc(SOLDIER_UNBLIT_SIZE))==NULL)
|
|
//{
|
|
// DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed unblit memory allocation" ) );
|
|
// break;
|
|
//}
|
|
//memset(pSoldier->pBackGround, 0, SOLDIER_UNBLIT_SIZE);
|
|
|
|
//if((pSoldier->pZBackground=MemAlloc(SOLDIER_UNBLIT_SIZE))==NULL)
|
|
//{
|
|
// DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed unblit memory allocation" ) );
|
|
// break;
|
|
//}
|
|
//memset(pSoldier->pZBackground, 0, SOLDIER_UNBLIT_SIZE);
|
|
|
|
// Init palettes
|
|
if( CreateSoldierPalettes( pSoldier ) == FALSE )
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed in creating soldier palettes" ) );
|
|
break;
|
|
}
|
|
|
|
fSuccess = TRUE;
|
|
|
|
} while( FALSE );
|
|
|
|
if ( !fSuccess )
|
|
{
|
|
DeleteSoldier( (pSoldier ) );
|
|
}
|
|
|
|
return( fSuccess );
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN DeleteSoldier( SOLDIERTYPE *pSoldier )
|
|
{
|
|
UINT32 cnt;
|
|
INT32 iGridNo;
|
|
INT8 bDir;
|
|
BOOLEAN fRet;
|
|
|
|
if ( pSoldier != NULL )
|
|
{
|
|
//if(pSoldier->pBackGround!=NULL)
|
|
//MemFree(pSoldier->pBackGround);
|
|
|
|
//if(pSoldier->pZBackground!=NULL)
|
|
//MemFree(pSoldier->pZBackground);
|
|
|
|
if( pSoldier->sGridNo != NOWHERE )
|
|
{
|
|
// Remove adjacency records
|
|
for (bDir = 0; bDir < NUM_WORLD_DIRECTIONS; bDir++)
|
|
{
|
|
iGridNo = pSoldier->sGridNo + DirIncrementer[ bDir ];
|
|
if( iGridNo >= 0 && iGridNo < WORLD_MAX )
|
|
{
|
|
gpWorldLevelData[ iGridNo ].ubAdjacentSoldierCnt--;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete key ring
|
|
if (pSoldier->pKeyRing)
|
|
{
|
|
MemFree( pSoldier->pKeyRing );
|
|
pSoldier->pKeyRing = NULL;
|
|
}
|
|
|
|
// Delete faces
|
|
DeleteSoldierFace( pSoldier );
|
|
|
|
// FREE PALETTES
|
|
if ( pSoldier->p8BPPPalette != NULL )
|
|
{
|
|
MemFree( pSoldier->p8BPPPalette );
|
|
pSoldier->p8BPPPalette = NULL;
|
|
}
|
|
|
|
if ( pSoldier->p16BPPPalette != NULL )
|
|
{
|
|
MemFree( pSoldier->p16BPPPalette );
|
|
pSoldier->p16BPPPalette = NULL;
|
|
}
|
|
|
|
for ( cnt = 0; cnt < NUM_SOLDIER_SHADES; cnt++ )
|
|
{
|
|
if ( pSoldier->pShades[ cnt ] != NULL )
|
|
{
|
|
MemFree( pSoldier->pShades[ cnt ] );
|
|
pSoldier->pShades[ cnt ] = NULL;
|
|
}
|
|
}
|
|
for ( cnt = 0; cnt < NUM_SOLDIER_EFFECTSHADES; cnt++ )
|
|
{
|
|
if ( pSoldier->pEffectShades[ cnt ] != NULL )
|
|
{
|
|
MemFree( pSoldier->pEffectShades[ cnt ] );
|
|
pSoldier->pEffectShades[ cnt ] = NULL;
|
|
}
|
|
}
|
|
|
|
// Delete glows
|
|
for ( cnt = 0; cnt < 20; cnt++ )
|
|
{
|
|
if ( pSoldier->pGlowShades[ cnt ] != NULL )
|
|
{
|
|
MemFree( pSoldier->pGlowShades[ cnt ] );
|
|
pSoldier->pGlowShades[ cnt ] = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if ( pSoldier->ubBodyType == QUEENMONSTER )
|
|
{
|
|
DeletePositionSnd( pSoldier->iPositionSndID );
|
|
}
|
|
|
|
// Free any animations we may have locked...
|
|
UnLoadCachedAnimationSurfaces( pSoldier->ubID, &( pSoldier->AnimCache) );
|
|
|
|
// Free Animation cache
|
|
DeleteAnimationCache( pSoldier->ubID, &( pSoldier->AnimCache) );
|
|
|
|
// Soldier is not active
|
|
pSoldier->bActive = FALSE;
|
|
|
|
// Remove light
|
|
DeleteSoldierLight( pSoldier );
|
|
|
|
// Remove reseved movement value
|
|
UnMarkMovementReserved( pSoldier );
|
|
|
|
}
|
|
|
|
// REMOVE SOLDIER FROM SLOT!
|
|
fRet = RemoveMercSlot( pSoldier );
|
|
|
|
if (!fRet)
|
|
{
|
|
RemoveAwaySlot( pSoldier );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOLEAN CreateSoldierLight( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( pSoldier->bTeam != gbPlayerNum )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// DO ONLY IF WE'RE AT A GOOD LEVEL
|
|
if ( pSoldier->iLight == -1 )
|
|
{
|
|
// ATE: Check for goggles in headpos....
|
|
if ( GetTotalVisionRangeBonus( pSoldier, NORMAL_LIGHTLEVEL_NIGHT ) >= UVGOGGLES_BONUS )
|
|
{
|
|
if( ( pSoldier->iLight=LightSpriteCreate("Light4", 0 ) )==(-1))
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed loading light" ) );
|
|
return( FALSE );
|
|
}
|
|
else
|
|
{
|
|
LightSprites[ pSoldier->iLight ].uiFlags |= MERC_LIGHT;
|
|
}
|
|
}
|
|
else if ( GetTotalVisionRangeBonus( pSoldier, NORMAL_LIGHTLEVEL_NIGHT ) >= NIGHTSIGHTGOGGLES_BONUS )
|
|
{
|
|
if( ( pSoldier->iLight=LightSpriteCreate("Light3", 0 ) )==(-1))
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed loading light" ) );
|
|
return( FALSE );
|
|
}
|
|
else
|
|
{
|
|
LightSprites[ pSoldier->iLight ].uiFlags |= MERC_LIGHT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( ( pSoldier->iLight=LightSpriteCreate("Light2", 0 ) )==(-1))
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier: Failed loading light" ) );
|
|
return( FALSE );
|
|
}
|
|
else
|
|
{
|
|
LightSprites[ pSoldier->iLight ].uiFlags |= MERC_LIGHT;
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->bLevel != 0 )
|
|
{
|
|
LightSpriteRoofStatus(pSoldier->iLight, TRUE );
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOLEAN ReCreateSoldierLight( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( pSoldier->bTeam != gbPlayerNum )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( !pSoldier->bActive )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( !pSoldier->bInSector )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Delete Light!
|
|
DeleteSoldierLight( pSoldier );
|
|
|
|
if ( pSoldier->iLight == -1 )
|
|
{
|
|
CreateSoldierLight( pSoldier );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN ReCreateSelectedSoldierLight( )
|
|
{
|
|
SOLDIERTYPE *pSoldier;
|
|
|
|
if ( gusSelectedSoldier == NO_SOLDIER )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
pSoldier = MercPtrs[ gusSelectedSoldier ];
|
|
|
|
return( ReCreateSoldierLight( pSoldier ) );
|
|
}
|
|
|
|
|
|
BOOLEAN DeleteSoldierLight( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if( pSoldier->iLight!=(-1) )
|
|
{
|
|
LightSpriteDestroy( pSoldier->iLight );
|
|
pSoldier->iLight = -1;
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
// FUNCTIONS CALLED BY EVENT PUMP
|
|
/////////////////////////////////
|
|
|
|
BOOLEAN ChangeSoldierState( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT16 usStartingAniCode, BOOLEAN fForce )
|
|
{
|
|
EV_S_CHANGESTATE SChangeState;
|
|
|
|
// Send message that we have changed states
|
|
SChangeState.usNewState = usNewState;
|
|
SChangeState.usSoldierID = pSoldier->ubID;
|
|
SChangeState.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
|
|
SChangeState.usStartingAniCode = usStartingAniCode;
|
|
SChangeState.sXPos = pSoldier->sX;
|
|
SChangeState.sYPos = pSoldier->sY;
|
|
SChangeState.fForce = fForce;
|
|
SChangeState.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
|
|
|
|
//AddGameEvent( S_CHANGESTATE, 0, &SChangeState );
|
|
EVENT_InitNewSoldierAnim( pSoldier, SChangeState.usNewState, SChangeState.usStartingAniCode, SChangeState.fForce );
|
|
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
// This function reevaluates the stance if the guy sees us!
|
|
BOOLEAN ReevaluateEnemyStance( SOLDIERTYPE *pSoldier, UINT16 usAnimState )
|
|
{
|
|
INT32 cnt, iClosestEnemy = NOBODY;
|
|
INT16 sTargetXPos, sTargetYPos;
|
|
BOOLEAN fReturnVal = FALSE;
|
|
INT16 sDist, sClosestDist = 10000;
|
|
|
|
// make the chosen one not turn to face us
|
|
if ( OK_ENEMY_MERC( pSoldier ) && pSoldier->ubID != gTacticalStatus.ubTheChosenOne && gAnimControl[ usAnimState ].ubEndHeight == ANIM_STAND && !( pSoldier->uiStatusFlags & SOLDIER_UNDERAICONTROL) )
|
|
{
|
|
if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_OFF )
|
|
{
|
|
// If we are a queen and see enemies, goto ready
|
|
if ( pSoldier->ubBodyType == QUEENMONSTER )
|
|
{
|
|
if ( gAnimControl[ usAnimState ].uiFlags & ( ANIM_BREATH ) )
|
|
{
|
|
if ( pSoldier->bOppCnt > 0 )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, QUEEN_INTO_READY, 0 , TRUE );
|
|
return( TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
// ATE: Don't do this if we're not a merc.....
|
|
if ( !IS_MERC_BODY_TYPE( pSoldier ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( gAnimControl[ usAnimState ].uiFlags & ( ANIM_MERCIDLE | ANIM_BREATH ) )
|
|
{
|
|
if ( pSoldier->bOppCnt > 0 )
|
|
{
|
|
// Pick a guy this buddy sees and turn towards them!
|
|
for ( cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++ )
|
|
{
|
|
if ( pSoldier->bOppList[ cnt ] == SEEN_CURRENTLY )
|
|
{
|
|
sDist = PythSpacesAway( pSoldier->sGridNo, MercPtrs[ cnt ]->sGridNo );
|
|
if (sDist < sClosestDist)
|
|
{
|
|
sClosestDist = sDist;
|
|
iClosestEnemy = cnt;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iClosestEnemy != NOBODY)
|
|
{
|
|
// Change to fire ready animation
|
|
ConvertGridNoToXY( MercPtrs[ iClosestEnemy ]->sGridNo, &sTargetXPos, &sTargetYPos );
|
|
|
|
pSoldier->fDontChargeReadyAPs = TRUE;
|
|
|
|
// Ready weapon
|
|
fReturnVal = SoldierReadyWeapon( pSoldier, sTargetXPos, sTargetYPos, FALSE );
|
|
|
|
return( fReturnVal );
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
void CheckForFreeupFromHit( SOLDIERTYPE *pSoldier, UINT32 uiOldAnimFlags, UINT32 uiNewAnimFlags, UINT16 usOldAniState, UINT16 usNewState )
|
|
{
|
|
// THIS COULD POTENTIALLY CALL EVENT_INITNEWAnim() if the GUY was SUPPRESSED
|
|
// CHECK IF THE OLD ANIMATION WAS A HIT START THAT WAS NOT FOLLOWED BY A HIT FINISH
|
|
// IF SO, RELEASE ATTACKER FROM ATTACKING
|
|
|
|
// If old and new animations are the same, do nothing!
|
|
if ( usOldAniState == QUEEN_HIT && usNewState == QUEEN_HIT )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( usOldAniState != usNewState && ( uiOldAnimFlags & ANIM_HITSTART ) && !( uiNewAnimFlags & ANIM_HITFINISH ) && !( uiNewAnimFlags & ANIM_IGNOREHITFINISH ) && !(pSoldier->uiStatusFlags & SOLDIER_TURNINGFROMHIT ) )
|
|
{
|
|
// Release attacker
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Releasesoldierattacker, normal hit animation ended NEW: %s ( %d ) OLD: %s ( %d )", gAnimControl[ usNewState ].zAnimStr, usNewState, gAnimControl[ usOldAniState ].zAnimStr, pSoldier->usOldAniState ) );
|
|
ReleaseSoldiersAttacker( pSoldier );
|
|
|
|
//FREEUP GETTING HIT FLAG
|
|
pSoldier->fGettingHit = FALSE;
|
|
|
|
// ATE: if our guy, have 10% change of say damn, if still conscious...
|
|
if ( pSoldier->bTeam == gbPlayerNum && pSoldier->bLife >= OKLIFE )
|
|
{
|
|
if ( Random( 10 ) == 0 )
|
|
{
|
|
DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_CURSE1 ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// CHECK IF WE HAVE FINSIHED A HIT WHILE DOWN
|
|
// OBLY DO THIS IF 1 ) We are dead already or 2 ) We are alive still
|
|
if ( ( uiOldAnimFlags & ANIM_HITWHENDOWN ) && ( ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) || pSoldier->bLife != 0 ) )
|
|
{
|
|
// Release attacker
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Releasesoldierattacker, animation of kill on the ground ended") );
|
|
ReleaseSoldiersAttacker( pSoldier );
|
|
|
|
//FREEUP GETTING HIT FLAG
|
|
pSoldier->fGettingHit = FALSE;
|
|
|
|
if ( pSoldier->bLife == 0 )
|
|
{
|
|
//ATE: Set previous attacker's value!
|
|
// This is so that the killer can say their killed quote....
|
|
pSoldier->ubAttackerID = pSoldier->ubPreviousAttackerID;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// THIS IS CALLED FROM AN EVENT ( S_CHANGESTATE )!
|
|
BOOLEAN EVENT_InitNewSoldierAnim( SOLDIERTYPE *pSoldier, UINT16 usNewState, UINT16 usStartingAniCode, BOOLEAN fForce )
|
|
{
|
|
UINT16 usNewGridNo = 0;
|
|
INT16 sAPCost = 0;
|
|
INT16 sBPCost = 0;
|
|
UINT32 uiOldAnimFlags;
|
|
UINT32 uiNewAnimFlags;
|
|
UINT16 usSubState;
|
|
UINT16 usItem;
|
|
BOOLEAN fTryingToRestart = FALSE;
|
|
|
|
CHECKF( usNewState < NUMANIMATIONSTATES );
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// DO SOME CHECKS ON OUR NEW ANIMATION!
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
// If we are NOT loading a game, continue normally
|
|
if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
|
|
{
|
|
// CHECK IF WE ARE TRYING TO INTURRUPT A SCRIPT WHICH WE DO NOT WANT INTERRUPTED!
|
|
if ( pSoldier->fInNonintAnim )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( pSoldier->fRTInNonintAnim )
|
|
{
|
|
if ( !(gTacticalStatus.uiFlags & INCOMBAT) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
else
|
|
{
|
|
pSoldier->fRTInNonintAnim = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
// Check if we can restart this animation if it's the same as our current!
|
|
if ( usNewState == pSoldier->usAnimState )
|
|
{
|
|
if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_NORESTART ) && !fForce )
|
|
{
|
|
fTryingToRestart = TRUE;
|
|
}
|
|
}
|
|
|
|
// Check state, if we are not at the same height, set this ani as the pending one and
|
|
// change stance accordingly
|
|
// ATE: ONLY IF WE ARE STARTING AT START OF ANIMATION!
|
|
if ( usStartingAniCode == 0 )
|
|
{
|
|
if ( gAnimControl[ usNewState ].ubHeight != gAnimControl[ pSoldier->usAnimState ].ubEndHeight &&
|
|
!( gAnimControl[ usNewState ].uiFlags & ( ANIM_STANCECHANGEANIM | ANIM_IGNORE_AUTOSTANCE ) ) )
|
|
{
|
|
|
|
// Check if we are going from crouched height to prone height, and adjust fast turning accordingly
|
|
// Make guy turn while crouched THEN go into prone
|
|
if ( ( gAnimControl[ usNewState ].ubEndHeight == ANIM_PRONE && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH ) && !( gTacticalStatus.uiFlags & INCOMBAT ) )
|
|
{
|
|
pSoldier->fTurningUntilDone = TRUE;
|
|
pSoldier->ubPendingStanceChange = gAnimControl[ usNewState ].ubEndHeight;
|
|
pSoldier->usPendingAnimation = usNewState;
|
|
return( TRUE );
|
|
}
|
|
// Check if we are in realtime and we are going from stand to crouch
|
|
else if ( gAnimControl[ usNewState ].ubEndHeight == ANIM_CROUCH && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND && ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) && ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) )
|
|
{
|
|
pSoldier->ubDesiredHeight = gAnimControl[ usNewState ].ubEndHeight;
|
|
// Continue with this course of action IE: Do animation and skip from stand to crouch
|
|
}
|
|
// Check if we are in realtime and we are going from crouch to stand
|
|
else if ( gAnimControl[ usNewState ].ubEndHeight == ANIM_STAND && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH && ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) && ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) && pSoldier->usAnimState != HELIDROP )
|
|
{
|
|
pSoldier->ubDesiredHeight = gAnimControl[ usNewState ].ubEndHeight;
|
|
// Continue with this course of action IE: Do animation and skip from stand to crouch
|
|
}
|
|
else
|
|
{
|
|
// ONLY DO FOR EVERYONE BUT PLANNING GUYS
|
|
if ( pSoldier->ubID < MAX_NUM_SOLDIERS )
|
|
{
|
|
// Set our next moving animation to be pending, after
|
|
pSoldier->usPendingAnimation = usNewState;
|
|
// Set new state to be animation to move to new stance
|
|
SendChangeSoldierStanceEvent( pSoldier, gAnimControl[ usNewState ].ubHeight );
|
|
return( TRUE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( usNewState == ADJACENT_GET_ITEM )
|
|
{
|
|
if ( pSoldier->ubPendingDirection != NO_PENDING_DIRECTION )
|
|
{
|
|
EVENT_InternalSetSoldierDesiredDirection( pSoldier, pSoldier->ubPendingDirection, FALSE, pSoldier->usAnimState );
|
|
pSoldier->ubPendingDirection = NO_PENDING_DIRECTION;
|
|
pSoldier->usPendingAnimation = ADJACENT_GET_ITEM;
|
|
pSoldier->fTurningUntilDone = TRUE;
|
|
SoldierGotoStationaryStance( pSoldier );
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
|
|
if ( usNewState == CLIMBUPROOF )
|
|
{
|
|
if ( pSoldier->ubPendingDirection != NO_PENDING_DIRECTION )
|
|
{
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->ubPendingDirection );
|
|
pSoldier->ubPendingDirection = NO_PENDING_DIRECTION;
|
|
pSoldier->usPendingAnimation = CLIMBUPROOF;
|
|
pSoldier->fTurningUntilDone = TRUE;
|
|
SoldierGotoStationaryStance( pSoldier );
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
if ( usNewState == CLIMBDOWNROOF )
|
|
{
|
|
if ( pSoldier->ubPendingDirection != NO_PENDING_DIRECTION )
|
|
{
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->ubPendingDirection );
|
|
pSoldier->ubPendingDirection = NO_PENDING_DIRECTION;
|
|
pSoldier->usPendingAnimation = CLIMBDOWNROOF;
|
|
pSoldier->fTurningFromPronePosition = FALSE;
|
|
pSoldier->fTurningUntilDone = TRUE;
|
|
SoldierGotoStationaryStance( pSoldier );
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
// ATE: Don't raise/lower automatically if we are low on health,
|
|
// as our gun looks lowered anyway....
|
|
//if ( pSoldier->bLife > INJURED_CHANGE_THREASHOLD )
|
|
{
|
|
// Don't do some of this if we are a monster!
|
|
// ATE: LOWER AIMATION IS GOOD, RAISE ONE HOWEVER MAY CAUSE PROBLEMS FOR AI....
|
|
if ( !(pSoldier->uiStatusFlags & SOLDIER_MONSTER ) && pSoldier->ubBodyType != ROBOTNOWEAPON && pSoldier->bTeam == gbPlayerNum )
|
|
{
|
|
// If this animation is a raise_weapon animation
|
|
if ( ( gAnimControl[ usNewState ].uiFlags & ANIM_RAISE_WEAPON ) && !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_RAISE_WEAPON | ANIM_NOCHANGE_WEAPON ) ) )
|
|
{
|
|
// We are told that we need to rasie weapon
|
|
// Do so only if
|
|
// 1) We have a rifle in hand...
|
|
usItem = pSoldier->inv[ HANDPOS ].usItem;
|
|
|
|
// if ( usItem != NOTHING && (Item[ usItem ].fFlags & ITEM_TWO_HANDED) && usItem != ROCKET_LAUNCHER && usItem != RPG7 )
|
|
if ( usItem != NOTHING && (Item[ usItem ].twohanded ) && !Item[usItem].rocketlauncher )
|
|
{
|
|
// Switch on height!
|
|
switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
// 2) OK, all's fine... lower weapon first....
|
|
pSoldier->usPendingAnimation = usNewState;
|
|
// Set new state to be animation to move to new stance
|
|
usNewState = RAISE_RIFLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If this animation is a lower_weapon animation
|
|
if ( ( gAnimControl[ usNewState ].uiFlags & ANIM_LOWER_WEAPON ) && !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_LOWER_WEAPON | ANIM_NOCHANGE_WEAPON ) ) )
|
|
{
|
|
// We are told that we need to rasie weapon
|
|
// Do so only if
|
|
// 1) We have a rifle in hand...
|
|
usItem = pSoldier->inv[ HANDPOS ].usItem;
|
|
|
|
// if ( usItem != NOTHING && (Item[ usItem ].fFlags & ITEM_TWO_HANDED) && usItem != ROCKET_LAUNCHER && usItem != RPG7 )
|
|
if ( usItem != NOTHING && (Item[ usItem ].twohanded ) && !Item[usItem].rocketlauncher )
|
|
{
|
|
// Switch on height!
|
|
switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
// 2) OK, all's fine... lower weapon first....
|
|
pSoldier->usPendingAnimation = usNewState;
|
|
// Set new state to be animation to move to new stance
|
|
usNewState = LOWER_RIFLE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Are we cowering and are tyring to move, getup first...
|
|
if ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING && pSoldier->usAnimState == COWERING && gAnimControl[ usNewState ].ubEndHeight == ANIM_STAND )
|
|
{
|
|
pSoldier->usPendingAnimation = usNewState;
|
|
// Set new state to be animation to move to new stance
|
|
usNewState = END_COWER;
|
|
}
|
|
|
|
// If we want to start swatting, put a pending animation
|
|
if( pSoldier->usAnimState != START_SWAT && usNewState == SWATTING )
|
|
{
|
|
// Set new state to be animation to move to new stance
|
|
usNewState = START_SWAT;
|
|
}
|
|
|
|
if( pSoldier->usAnimState == SWATTING && usNewState == CROUCHING )
|
|
{
|
|
// Set new state to be animation to move to new stance
|
|
usNewState = END_SWAT;
|
|
}
|
|
|
|
if( pSoldier->usAnimState == WALKING && usNewState == STANDING && pSoldier->bLife < INJURED_CHANGE_THREASHOLD && pSoldier->ubBodyType <= REGFEMALE && !MercInWater( pSoldier ) )
|
|
{
|
|
// Set new state to be animation to move to new stance
|
|
usNewState = END_HURT_WALKING;
|
|
}
|
|
|
|
// Check if we are an enemy, and we are in an animation what should be overriden
|
|
// by if he sees us or not.
|
|
if ( ReevaluateEnemyStance( pSoldier, usNewState ) )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
|
|
// OK.......
|
|
if ( pSoldier->ubBodyType > REGFEMALE )
|
|
{
|
|
if ( pSoldier->bLife < INJURED_CHANGE_THREASHOLD )
|
|
{
|
|
if ( usNewState == READY_RIFLE_STAND )
|
|
{
|
|
// pSoldier->usPendingAnimation2 = usNewState;
|
|
// usNewState = FROM_INJURED_TRANSITION;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Alrighty, check if we should free buddy up!
|
|
if ( usNewState == GIVING_AID )
|
|
{
|
|
UnSetUIBusy( pSoldier->ubID );
|
|
}
|
|
|
|
|
|
// SUBSTITUDE VARIOUS REG ANIMATIONS WITH ODD BODY TYPES
|
|
if ( SubstituteBodyTypeAnimation( pSoldier, usNewState, &usSubState ) )
|
|
{
|
|
usNewState = usSubState;
|
|
}
|
|
|
|
// CHECK IF WE CAN DO THIS ANIMATION!
|
|
if ( IsAnimationValidForBodyType( pSoldier, usNewState ) == FALSE )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// OK, make guy transition if a big merc...
|
|
if ( pSoldier->uiAnimSubFlags & SUB_ANIM_BIGGUYTHREATENSTANCE )
|
|
{
|
|
if ( usNewState == KNEEL_DOWN && pSoldier->usAnimState != BIGMERC_CROUCH_TRANS_INTO )
|
|
{
|
|
UINT16 usItem;
|
|
|
|
// Do we have a rifle?
|
|
usItem = pSoldier->inv[ HANDPOS ].usItem;
|
|
|
|
if ( usItem != NOTHING )
|
|
{
|
|
if ( Item[ usItem ].usItemClass == IC_GUN && !Item[usItem].rocketlauncher )
|
|
{
|
|
// if ( (Item[ usItem ].fFlags & ITEM_TWO_HANDED) )
|
|
if ( (Item[ usItem ].twohanded ) )
|
|
{
|
|
usNewState = BIGMERC_CROUCH_TRANS_INTO;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( usNewState == KNEEL_UP && pSoldier->usAnimState != BIGMERC_CROUCH_TRANS_OUTOF )
|
|
{
|
|
UINT16 usItem;
|
|
|
|
// Do we have a rifle?
|
|
usItem = pSoldier->inv[ HANDPOS ].usItem;
|
|
|
|
if ( usItem != NOTHING )
|
|
{
|
|
if ( Item[ usItem ].usItemClass == IC_GUN && !Item[usItem].rocketlauncher )
|
|
{
|
|
// if ( (Item[ usItem ].fFlags & ITEM_TWO_HANDED) )
|
|
if ( (Item[ usItem ].twohanded ) )
|
|
{
|
|
usNewState = BIGMERC_CROUCH_TRANS_OUTOF;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// OK, if we have reverse set, do the side step!
|
|
if ( pSoldier->bReverse )
|
|
{
|
|
if ( usNewState == WALKING || usNewState == RUNNING || usNewState == SWATTING )
|
|
{
|
|
// CHECK FOR SIDEWAYS!
|
|
if ( pSoldier->bDirection == gPurpendicularDirection[ pSoldier->bDirection ][ pSoldier->usPathingData[ pSoldier->usPathIndex ] ] )
|
|
{
|
|
// We are perpendicular!
|
|
usNewState = SIDE_STEP;
|
|
}
|
|
else
|
|
{
|
|
if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH )
|
|
{
|
|
usNewState = SWAT_BACKWARDS;
|
|
}
|
|
else
|
|
{
|
|
// Here, change to opposite direction
|
|
usNewState = WALK_BACKWARDS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ATE: Patch hole for breath collapse for roofs, fences
|
|
if ( usNewState == CLIMBUPROOF || usNewState == CLIMBDOWNROOF || usNewState == HOPFENCE )
|
|
{
|
|
// Check for breath collapse if a given animation like
|
|
if ( CheckForBreathCollapse( pSoldier ) || pSoldier->bCollapsed )
|
|
{
|
|
// UNset UI
|
|
UnSetUIBusy( pSoldier->ubID );
|
|
|
|
SoldierCollapse( pSoldier );
|
|
|
|
pSoldier->bBreathCollapsed = FALSE;
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
}
|
|
|
|
// If we are in water.....and trying to run, change to run
|
|
if ( pSoldier->bOverTerrainType == LOW_WATER || pSoldier->bOverTerrainType == MED_WATER )
|
|
{
|
|
// Check animation
|
|
// Change to walking
|
|
if ( usNewState == RUNNING )
|
|
{
|
|
usNewState = WALKING;
|
|
}
|
|
}
|
|
|
|
// Turn off anipause flag for any anim!
|
|
pSoldier->uiStatusFlags &= (~SOLDIER_PAUSEANIMOVE);
|
|
|
|
// Unset paused for no APs.....
|
|
AdjustNoAPToFinishMove( pSoldier, FALSE );
|
|
|
|
if ( usNewState == CRAWLING && pSoldier->usDontUpdateNewGridNoOnMoveAnimChange == 1 )
|
|
{
|
|
if ( pSoldier->fTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE )
|
|
{
|
|
pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_START_UP_FROM_MOVE;
|
|
}
|
|
|
|
// ATE: IF we are starting to crawl, but have to getup to turn first......
|
|
if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_START_UP_FROM_MOVE )
|
|
{
|
|
usNewState = PRONE_UP;
|
|
pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE;
|
|
}
|
|
}
|
|
|
|
// We are about to start moving
|
|
// Handle buddy beginning to move...
|
|
// check new gridno, etc
|
|
// ATE: Added: Make check that old anim is not a moving one as well
|
|
if ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING && !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) || ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING && fForce ) )
|
|
{
|
|
BOOLEAN fKeepMoving;
|
|
|
|
if ( usNewState == CRAWLING && pSoldier->usDontUpdateNewGridNoOnMoveAnimChange == LOCKED_NO_NEWGRIDNO )
|
|
{
|
|
// Turn off lock once we are crawling once...
|
|
pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = 1;
|
|
}
|
|
|
|
// ATE: Additional check here if we have just been told to update animation ONLY, not goto gridno stuff...
|
|
if ( !pSoldier->usDontUpdateNewGridNoOnMoveAnimChange )
|
|
{
|
|
if ( usNewState != SWATTING )
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Handling New gridNo for %d: Old %s, New %s", pSoldier->ubID, gAnimControl[ pSoldier->usAnimState ].zAnimStr , gAnimControl[ usNewState ].zAnimStr) );
|
|
|
|
if ( !( gAnimControl[ usNewState ].uiFlags & ANIM_SPECIALMOVE ) )
|
|
{
|
|
// Handle goto new tile...
|
|
if ( HandleGotoNewGridNo( pSoldier, &fKeepMoving, TRUE, usNewState ) )
|
|
{
|
|
if ( !fKeepMoving )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Make sure desy = zeroed out...
|
|
// pSoldier->fPastXDest = pSoldier->fPastYDest = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if ( pSoldier->bBreathCollapsed )
|
|
{
|
|
// UNset UI
|
|
UnSetUIBusy( pSoldier->ubID );
|
|
|
|
SoldierCollapse( pSoldier );
|
|
|
|
pSoldier->bBreathCollapsed = FALSE;
|
|
}
|
|
return( FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Change desired direction
|
|
// Just change direction
|
|
EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, pSoldier->usAnimState );
|
|
}
|
|
|
|
//check for services
|
|
ReceivingSoldierCancelServices( pSoldier );
|
|
GivingSoldierCancelServices( pSoldier );
|
|
|
|
|
|
// Check if we are a vehicle, and start playing noise sound....
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
|
|
{
|
|
HandleVehicleMovementSound( pSoldier, TRUE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check for stopping movement noise...
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
|
|
{
|
|
HandleVehicleMovementSound( pSoldier, FALSE );
|
|
|
|
// If a vehicle, set hewight to 0
|
|
SetSoldierHeight( pSoldier, (FLOAT)( 0 ) );
|
|
}
|
|
|
|
}
|
|
|
|
// Reset to false always.....
|
|
// ( Unless locked )
|
|
if ( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING )
|
|
{
|
|
if ( pSoldier->usDontUpdateNewGridNoOnMoveAnimChange != LOCKED_NO_NEWGRIDNO )
|
|
{
|
|
pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = FALSE;
|
|
}
|
|
}
|
|
|
|
if ( fTryingToRestart )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// ATE: If this is an AI guy.. unlock him!
|
|
if ( gTacticalStatus.fEnemySightingOnTheirTurn )
|
|
{
|
|
if ( gTacticalStatus.ubEnemySightingOnTheirTurnEnemyID == pSoldier->ubID )
|
|
{
|
|
pSoldier->fPauseAllAnimation = FALSE;
|
|
gTacticalStatus.fEnemySightingOnTheirTurn = FALSE;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// HERE DOWN - WE HAVE MADE A DESCISION!
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
uiOldAnimFlags = gAnimControl[ pSoldier->usAnimState ].uiFlags;
|
|
uiNewAnimFlags = gAnimControl[ usNewState ].uiFlags;
|
|
|
|
usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc( pSoldier->usPathingData[ pSoldier->usPathIndex ] ) );
|
|
|
|
|
|
// CHECKING IF WE HAVE A HIT FINISH BUT NO DEATH IS DONE WITH A SPECIAL ANI CODE
|
|
// IN THE HIT FINSIH ANI SCRIPTS
|
|
|
|
// CHECKING IF WE HAVE FINISHED A DEATH ANIMATION IS DONE WITH A SPECIAL ANI CODE
|
|
// IN THE DEATH SCRIPTS
|
|
|
|
|
|
// CHECK IF THIS NEW STATE IS NON-INTERRUPTABLE
|
|
// IF SO - SET NON-INT FLAG
|
|
if ( uiNewAnimFlags & ANIM_NONINTERRUPT )
|
|
{
|
|
pSoldier->fInNonintAnim = TRUE;
|
|
}
|
|
|
|
if ( uiNewAnimFlags & ANIM_RT_NONINTERRUPT )
|
|
{
|
|
pSoldier->fRTInNonintAnim = TRUE;
|
|
}
|
|
|
|
// CHECK IF WE ARE NOT AIMING, IF NOT, RESET LAST TAGRET!
|
|
if ( !(gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY ) && !(gAnimControl[ usNewState ].uiFlags & ANIM_FIREREADY ) )
|
|
{
|
|
// ATE: Also check for the transition anims to not reset this
|
|
// this should have used a flag but we're out of them....
|
|
if ( usNewState != READY_RIFLE_STAND && usNewState != READY_RIFLE_PRONE && usNewState != READY_RIFLE_CROUCH && usNewState != ROBOT_SHOOT )
|
|
{
|
|
pSoldier->sLastTarget = NOWHERE;
|
|
}
|
|
}
|
|
|
|
// If a special move state, release np aps
|
|
if ( ( gAnimControl[ usNewState ].uiFlags & ANIM_SPECIALMOVE ) )
|
|
{
|
|
AdjustNoAPToFinishMove( pSoldier, FALSE );
|
|
}
|
|
|
|
if ( gAnimControl[ usNewState ].uiFlags & ANIM_UPDATEMOVEMENTMODE )
|
|
{
|
|
if ( pSoldier->bTeam == gbPlayerNum )
|
|
{
|
|
// pSoldier->usUIMovementMode = GetMoveStateBasedOnStance( pSoldier, gAnimControl[ usNewState ].ubEndHeight );
|
|
}
|
|
}
|
|
|
|
// ATE: If not a moving animation - turn off reverse....
|
|
if ( !( gAnimControl[ usNewState ].uiFlags & ANIM_MOVING ) )
|
|
{
|
|
pSoldier->bReverse = FALSE;
|
|
}
|
|
|
|
// ONLY DO FOR EVERYONE BUT PLANNING GUYS
|
|
if ( pSoldier->ubID < MAX_NUM_SOLDIERS )
|
|
{
|
|
|
|
// Do special things based on new state
|
|
switch( usNewState )
|
|
{
|
|
case STANDING:
|
|
|
|
// Update desired height
|
|
pSoldier->ubDesiredHeight = ANIM_STAND;
|
|
break;
|
|
|
|
case CROUCHING:
|
|
|
|
// Update desired height
|
|
pSoldier->ubDesiredHeight = ANIM_CROUCH;
|
|
break;
|
|
|
|
case PRONE:
|
|
|
|
// Update desired height
|
|
pSoldier->ubDesiredHeight = ANIM_PRONE;
|
|
break;
|
|
|
|
case READY_RIFLE_STAND:
|
|
case READY_RIFLE_PRONE:
|
|
case READY_RIFLE_CROUCH:
|
|
case READY_DUAL_STAND:
|
|
case READY_DUAL_CROUCH:
|
|
case READY_DUAL_PRONE:
|
|
|
|
// OK, get points to ready weapon....
|
|
if ( !pSoldier->fDontChargeReadyAPs )
|
|
{
|
|
sAPCost = GetAPsToReadyWeapon( pSoldier, usNewState );
|
|
DeductPoints( pSoldier, sAPCost, sBPCost );
|
|
}
|
|
else
|
|
{
|
|
pSoldier->fDontChargeReadyAPs = FALSE;
|
|
}
|
|
break;
|
|
|
|
case WALKING:
|
|
|
|
pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
|
|
pSoldier->ubPendingActionAnimCount = 0;
|
|
break;
|
|
|
|
case SWATTING:
|
|
|
|
pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
|
|
pSoldier->ubPendingActionAnimCount = 0;
|
|
break;
|
|
|
|
case CRAWLING:
|
|
|
|
// Turn off flag...
|
|
pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
|
pSoldier->ubPendingActionAnimCount = 0;
|
|
pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
|
|
break;
|
|
|
|
case RUNNING:
|
|
|
|
// Only if our previous is not running
|
|
if ( pSoldier->usAnimState != RUNNING )
|
|
{
|
|
sAPCost = AP_START_RUN_COST;
|
|
DeductPoints( pSoldier, sAPCost, sBPCost );
|
|
}
|
|
// Set pending action count to 0
|
|
pSoldier->ubPendingActionAnimCount = 0;
|
|
pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
|
|
break;
|
|
|
|
case ADULTMONSTER_WALKING:
|
|
pSoldier->ubPendingActionAnimCount = 0;
|
|
break;
|
|
|
|
case ROBOT_WALK:
|
|
pSoldier->ubPendingActionAnimCount = 0;
|
|
break;
|
|
|
|
case KNEEL_UP:
|
|
case KNEEL_DOWN:
|
|
case BIGMERC_CROUCH_TRANS_INTO:
|
|
case BIGMERC_CROUCH_TRANS_OUTOF:
|
|
|
|
if ( !pSoldier->fDontChargeAPsForStanceChange )
|
|
{
|
|
DeductPoints( pSoldier, AP_CROUCH, BP_CROUCH );
|
|
}
|
|
pSoldier->fDontChargeAPsForStanceChange = FALSE;
|
|
break;
|
|
|
|
case PRONE_UP:
|
|
case PRONE_DOWN:
|
|
|
|
// ATE: If we are NOT waiting for prone down...
|
|
if ( pSoldier->fTurningFromPronePosition < TURNING_FROM_PRONE_START_UP_FROM_MOVE && !pSoldier->fDontChargeAPsForStanceChange )
|
|
{
|
|
// ATE: Don't do this if we are still 'moving'....
|
|
if ( pSoldier->sGridNo == pSoldier->sFinalDestination || pSoldier->usPathIndex == 0 )
|
|
{
|
|
DeductPoints( pSoldier, AP_PRONE, BP_PRONE );
|
|
}
|
|
}
|
|
pSoldier->fDontChargeAPsForStanceChange = FALSE;
|
|
break;
|
|
|
|
//Deduct points for stance change
|
|
//sAPCost = GetAPsToChangeStance( pSoldier, gAnimControl[ usNewState ].ubEndHeight );
|
|
//DeductPoints( pSoldier, sAPCost, 0 );
|
|
//break;
|
|
|
|
case START_AID:
|
|
|
|
DeductPoints( pSoldier, AP_START_FIRST_AID, BP_START_FIRST_AID );
|
|
break;
|
|
|
|
case CUTTING_FENCE:
|
|
DeductPoints( pSoldier, AP_USEWIRECUTTERS, BP_USEWIRECUTTERS );
|
|
break;
|
|
|
|
case PLANT_BOMB:
|
|
|
|
DeductPoints( pSoldier, AP_DROP_BOMB, 0 );
|
|
break;
|
|
|
|
case STEAL_ITEM:
|
|
|
|
DeductPoints( pSoldier, AP_STEAL_ITEM, 0 );
|
|
break;
|
|
|
|
case CROW_DIE:
|
|
|
|
// Delete shadow of crow....
|
|
if ( pSoldier->pAniTile != NULL )
|
|
{
|
|
DeleteAniTile( pSoldier->pAniTile );
|
|
pSoldier->pAniTile = NULL;
|
|
}
|
|
break;
|
|
|
|
case CROW_FLY:
|
|
|
|
// Ate: startup a shadow ( if gridno is set )
|
|
HandleCrowShadowNewGridNo( pSoldier );
|
|
break;
|
|
|
|
case CROW_EAT:
|
|
|
|
// ATE: Make sure height level is 0....
|
|
SetSoldierHeight( pSoldier, (FLOAT)(0) );
|
|
HandleCrowShadowRemoveGridNo( pSoldier );
|
|
break;
|
|
|
|
case USE_REMOTE:
|
|
|
|
DeductPoints( pSoldier, AP_USE_REMOTE, 0 );
|
|
break;
|
|
|
|
//case PUNCH:
|
|
|
|
//Deduct points for punching
|
|
//sAPCost = MinAPsToAttack( pSoldier, pSoldier->sGridNo, FALSE );
|
|
//DeductPoints( pSoldier, sAPCost, 0 );
|
|
//break;
|
|
|
|
case HOPFENCE:
|
|
|
|
DeductPoints( pSoldier, AP_JUMPFENCE, BP_JUMPFENCE );
|
|
break;
|
|
|
|
// Deduct aps for falling down....
|
|
case FALLBACK_HIT_STAND:
|
|
case FALLFORWARD_FROMHIT_STAND:
|
|
|
|
DeductPoints( pSoldier, AP_FALL_DOWN, BP_FALL_DOWN );
|
|
break;
|
|
|
|
case FALLFORWARD_FROMHIT_CROUCH:
|
|
|
|
DeductPoints( pSoldier, (AP_FALL_DOWN/2), (BP_FALL_DOWN/2) );
|
|
break;
|
|
|
|
case QUEEN_SWIPE:
|
|
|
|
// ATE: set damage counter...
|
|
pSoldier->uiPendingActionData1 = 0;
|
|
break;
|
|
|
|
case CLIMBDOWNROOF:
|
|
|
|
// disable sight
|
|
gTacticalStatus.uiFlags |= DISALLOW_SIGHT;
|
|
|
|
DeductPoints( pSoldier, AP_CLIMBOFFROOF, BP_CLIMBOFFROOF );
|
|
break;
|
|
|
|
case CLIMBUPROOF:
|
|
|
|
// disable sight
|
|
gTacticalStatus.uiFlags |= DISALLOW_SIGHT;
|
|
|
|
DeductPoints( pSoldier, AP_CLIMBROOF, BP_CLIMBROOF );
|
|
break;
|
|
|
|
case JUMP_OVER_BLOCKING_PERSON:
|
|
|
|
// Set path....
|
|
{
|
|
UINT16 usNewGridNo;
|
|
|
|
DeductPoints( pSoldier, AP_JUMP_OVER, BP_JUMP_OVER );
|
|
|
|
usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( pSoldier->bDirection ) );
|
|
usNewGridNo = NewGridNo( (UINT16)usNewGridNo, DirectionInc( pSoldier->bDirection ) );
|
|
|
|
pSoldier->usPathDataSize = 0;
|
|
pSoldier->usPathIndex = 0;
|
|
pSoldier->usPathingData[ pSoldier->usPathDataSize ] = pSoldier->bDirection;
|
|
pSoldier->usPathDataSize++;
|
|
pSoldier->usPathingData[ pSoldier->usPathDataSize ] = pSoldier->bDirection;
|
|
pSoldier->usPathDataSize++;
|
|
pSoldier->sFinalDestination = usNewGridNo;
|
|
// Set direction
|
|
EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, JUMP_OVER_BLOCKING_PERSON );
|
|
}
|
|
break;
|
|
|
|
|
|
case GENERIC_HIT_STAND:
|
|
case GENERIC_HIT_CROUCH:
|
|
case STANDING_BURST_HIT:
|
|
case ADULTMONSTER_HIT:
|
|
case ADULTMONSTER_DYING:
|
|
case COW_HIT:
|
|
case COW_DYING:
|
|
case BLOODCAT_HIT:
|
|
case BLOODCAT_DYING:
|
|
case WATER_HIT:
|
|
case WATER_DIE:
|
|
case DEEP_WATER_HIT:
|
|
case DEEP_WATER_DIE:
|
|
case RIFLE_STAND_HIT:
|
|
case LARVAE_HIT:
|
|
case LARVAE_DIE:
|
|
case QUEEN_HIT:
|
|
case QUEEN_DIE:
|
|
case INFANT_HIT:
|
|
case INFANT_DIE:
|
|
case CRIPPLE_HIT:
|
|
case CRIPPLE_DIE:
|
|
case CRIPPLE_DIE_FLYBACK:
|
|
case ROBOTNW_HIT:
|
|
case ROBOTNW_DIE:
|
|
|
|
// Set getting hit flag to TRUE
|
|
pSoldier->fGettingHit = TRUE;
|
|
break;
|
|
|
|
case CHARIOTS_OF_FIRE:
|
|
case BODYEXPLODING:
|
|
|
|
// Merc on fire!
|
|
pSoldier->uiPendingActionData1 = PlaySoldierJA2Sample( pSoldier->ubID, ( FIRE_ON_MERC ), RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 5, SoundDir( pSoldier->sGridNo ), TRUE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Remove old animation profile
|
|
HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE );
|
|
|
|
|
|
// From animation control, set surface
|
|
if ( SetSoldierAnimationSurface( pSoldier, usNewState ) == FALSE )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
// Set state
|
|
pSoldier->usOldAniState = pSoldier->usAnimState;
|
|
pSoldier->sOldAniCode = pSoldier->usAniCode;
|
|
|
|
// Change state value!
|
|
pSoldier->usAnimState = usNewState;
|
|
|
|
pSoldier->sZLevelOverride = -1;
|
|
|
|
if ( !( pSoldier->uiStatusFlags & SOLDIER_LOCKPENDINGACTIONCOUNTER ) )
|
|
{
|
|
//ATE Cancel ANY pending action...
|
|
if ( pSoldier->ubPendingActionAnimCount > 0 && ( gAnimControl[ pSoldier->usOldAniState ].uiFlags & ANIM_MOVING ) )
|
|
{
|
|
// Do some special things for some actions
|
|
switch( pSoldier->ubPendingAction )
|
|
{
|
|
case MERC_GIVEITEM:
|
|
|
|
// Unset target as enaged
|
|
MercPtrs[ pSoldier->uiPendingActionData4 ]->uiStatusFlags &= (~SOLDIER_ENGAGEDINACTION);
|
|
break;
|
|
}
|
|
pSoldier->ubPendingAction = NO_PENDING_ACTION;
|
|
}
|
|
else
|
|
{
|
|
// Increment this for almost all animations except some movement ones...
|
|
// That's because this represents ANY animation other than the one we began when the pending action was started
|
|
// ATE: Added to ignore this count if we are waiting for someone to move out of our way...
|
|
if ( usNewState != START_SWAT && usNewState != END_SWAT && !( gAnimControl[ usNewState ].uiFlags & ANIM_NOCHANGE_PENDINGCOUNT ) && !pSoldier->fDelayedMovement && !( pSoldier->uiStatusFlags & SOLDIER_ENGAGEDINACTION ) )
|
|
{
|
|
pSoldier->ubPendingActionAnimCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set new animation profile
|
|
HandleAnimationProfile( pSoldier, usNewState, FALSE );
|
|
|
|
// Reset some animation values
|
|
pSoldier->fForceShade = FALSE;
|
|
|
|
CheckForFreeupFromHit( pSoldier, uiOldAnimFlags, uiNewAnimFlags, pSoldier->usOldAniState, usNewState );
|
|
|
|
// CHECK IF WE ARE AT AN IDLE ACTION
|
|
#if 0
|
|
if ( gAnimControl[ usNewState ].uiFlags & ANIM_IDLE )
|
|
{
|
|
pSoldier->bAction = ACTION_DONE;
|
|
}
|
|
else
|
|
{
|
|
pSoldier->bAction = ACTION_BUSY;
|
|
}
|
|
#endif
|
|
|
|
// Set current frame
|
|
pSoldier->usAniCode = usStartingAniCode;
|
|
|
|
// ATE; For some animations that could use some variations, do so....
|
|
if (usNewState == CHARIOTS_OF_FIRE || usNewState == BODYEXPLODING )
|
|
{
|
|
pSoldier->usAniCode = (UINT16)( Random( 10 ) );
|
|
}
|
|
|
|
// ATE: Default to first frame....
|
|
// Will get changed ( probably ) by AdjustToNextAnimationFrame()
|
|
ConvertAniCodeToAniFrame( pSoldier, (INT16)( 0 ) );
|
|
|
|
// Set delay speed
|
|
SetSoldierAniSpeed( pSoldier );
|
|
|
|
// Reset counters
|
|
RESETTIMECOUNTER( pSoldier->UpdateCounter, pSoldier->sAniDelay );
|
|
|
|
// Adjust to new animation frame ( the first one )
|
|
AdjustToNextAnimationFrame( pSoldier );
|
|
|
|
// Setup offset information for UI above guy
|
|
SetSoldierLocatorOffsets( pSoldier );
|
|
|
|
// If our own guy...
|
|
if ( pSoldier->bTeam == gbPlayerNum )
|
|
{
|
|
// Are we stationary?
|
|
if ( gAnimControl[ usNewState ].uiFlags & ANIM_STATIONARY )
|
|
{
|
|
// Position light....
|
|
// SetCheckSoldierLightFlag( pSoldier );
|
|
}
|
|
else
|
|
{
|
|
// Hide light.....
|
|
// DeleteSoldierLight( pSoldier );
|
|
}
|
|
}
|
|
|
|
// If we are certain animations, reload palette
|
|
if ( usNewState == VEHICLE_DIE || usNewState == CHARIOTS_OF_FIRE || usNewState == BODYEXPLODING )
|
|
{
|
|
CreateSoldierPalettes( pSoldier );
|
|
}
|
|
|
|
// ATE: if the old animation was a movement, and new is not, play sound...
|
|
// OK, play final footstep sound...
|
|
if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
|
|
{
|
|
if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STATIONARY ) &&
|
|
( gAnimControl[ pSoldier->usOldAniState ].uiFlags & ANIM_MOVING ) )
|
|
{
|
|
PlaySoldierFootstepSound( pSoldier );
|
|
}
|
|
}
|
|
|
|
// Free up from stance change
|
|
FreeUpNPCFromStanceChange( pSoldier );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
void InternalRemoveSoldierFromGridNo( SOLDIERTYPE *pSoldier, BOOLEAN fForce )
|
|
{
|
|
INT8 bDir;
|
|
INT32 iGridNo;
|
|
|
|
if((pSoldier->sGridNo!=NO_MAP_POS) )
|
|
{
|
|
if ( pSoldier->bInSector || fForce )
|
|
{
|
|
// Remove from world ( old pos )
|
|
RemoveMerc( pSoldier->sGridNo, pSoldier, FALSE );
|
|
HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE );
|
|
|
|
// Remove records of this guy being adjacent
|
|
for (bDir = 0; bDir < NUM_WORLD_DIRECTIONS; bDir++)
|
|
{
|
|
iGridNo = pSoldier->sGridNo + DirIncrementer[ bDir ];
|
|
if( iGridNo >= 0 && iGridNo < WORLD_MAX )
|
|
{
|
|
gpWorldLevelData[ iGridNo ].ubAdjacentSoldierCnt--;
|
|
}
|
|
}
|
|
|
|
HandlePlacingRoofMarker( pSoldier, pSoldier->sGridNo, FALSE , FALSE );
|
|
|
|
// Remove reseved movement value
|
|
UnMarkMovementReserved( pSoldier );
|
|
|
|
HandleCrowShadowRemoveGridNo( pSoldier );
|
|
|
|
// Reset gridno...
|
|
pSoldier->sGridNo = NO_MAP_POS;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RemoveSoldierFromGridNo( SOLDIERTYPE *pSoldier )
|
|
{
|
|
InternalRemoveSoldierFromGridNo( pSoldier, FALSE );
|
|
}
|
|
|
|
|
|
void EVENT_InternalSetSoldierPosition( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos ,BOOLEAN fUpdateDest, BOOLEAN fUpdateFinalDest, BOOLEAN fForceRemove )
|
|
{
|
|
INT16 sNewGridNo;
|
|
|
|
// Not if we're dead!
|
|
if ( ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Set new map index
|
|
sNewGridNo = GETWORLDINDEXFROMWORLDCOORDS(dNewYPos, dNewXPos );
|
|
|
|
if ( fUpdateDest )
|
|
{
|
|
pSoldier->sDestination = sNewGridNo;
|
|
}
|
|
|
|
if ( fUpdateFinalDest )
|
|
{
|
|
pSoldier->sFinalDestination = sNewGridNo;
|
|
}
|
|
|
|
// Copy old values
|
|
pSoldier->dOldXPos = pSoldier->dXPos;
|
|
pSoldier->dOldYPos = pSoldier->dYPos;
|
|
|
|
// Set New pos
|
|
pSoldier->dXPos = dNewXPos;
|
|
pSoldier->dYPos = dNewYPos;
|
|
|
|
pSoldier->sX = (INT16)dNewXPos;
|
|
pSoldier->sY = (INT16)dNewYPos;
|
|
|
|
HandleCrowShadowNewPosition( pSoldier );
|
|
|
|
SetSoldierGridNo( pSoldier, sNewGridNo, fForceRemove );
|
|
|
|
if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
|
|
{
|
|
if ( gGameSettings.fOptions[ TOPTION_MERC_ALWAYS_LIGHT_UP ] )
|
|
{
|
|
SetCheckSoldierLightFlag( pSoldier );
|
|
}
|
|
}
|
|
|
|
// ATE: Mirror calls if we are a vehicle ( for all our passengers )
|
|
UpdateAllVehiclePassengersGridNo( pSoldier );
|
|
|
|
}
|
|
|
|
void EVENT_SetSoldierPosition( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos )
|
|
{
|
|
EVENT_InternalSetSoldierPosition( pSoldier, dNewXPos, dNewYPos ,TRUE, TRUE, FALSE );
|
|
}
|
|
|
|
void EVENT_SetSoldierPositionForceDelete( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos )
|
|
{
|
|
EVENT_InternalSetSoldierPosition( pSoldier, dNewXPos, dNewYPos ,TRUE, TRUE, TRUE );
|
|
}
|
|
|
|
void EVENT_SetSoldierPositionAndMaybeFinalDest( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos, BOOLEAN fUpdateFinalDest )
|
|
{
|
|
EVENT_InternalSetSoldierPosition( pSoldier, dNewXPos, dNewYPos ,TRUE, fUpdateFinalDest, FALSE );
|
|
}
|
|
|
|
void EVENT_SetSoldierPositionAndMaybeFinalDestAndMaybeNotDestination( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos, BOOLEAN fUpdateDest, BOOLEAN fUpdateFinalDest )
|
|
{
|
|
EVENT_InternalSetSoldierPosition( pSoldier, dNewXPos, dNewYPos ,fUpdateDest, fUpdateFinalDest, FALSE );
|
|
}
|
|
|
|
|
|
void InternalSetSoldierHeight( SOLDIERTYPE *pSoldier, FLOAT dNewHeight, BOOLEAN fUpdateLevel )
|
|
{
|
|
|
|
INT8 bOldLevel = pSoldier->bLevel;
|
|
|
|
pSoldier->dHeightAdjustment = dNewHeight;
|
|
pSoldier->sHeightAdjustment = (INT16)pSoldier->dHeightAdjustment;
|
|
|
|
if ( !fUpdateLevel )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( pSoldier->sHeightAdjustment > 0 )
|
|
{
|
|
pSoldier->bLevel = SECOND_LEVEL;
|
|
|
|
ApplyTranslucencyToWalls((INT16)(pSoldier->dXPos/CELL_X_SIZE), (INT16)(pSoldier->dYPos/CELL_Y_SIZE));
|
|
//LightHideTrees((INT16)(pSoldier->dXPos/CELL_X_SIZE), (INT16)(pSoldier->dYPos/CELL_Y_SIZE));
|
|
//ConcealAllWalls();
|
|
|
|
//pSoldier->pLevelNode->ubShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubShadeLevel;
|
|
//pSoldier->pLevelNode->ubSumLights=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubSumLights;
|
|
//pSoldier->pLevelNode->ubMaxLights=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubMaxLights;
|
|
//pSoldier->pLevelNode->ubNaturalShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubNaturalShadeLevel;
|
|
}
|
|
else
|
|
{
|
|
pSoldier->bLevel = FIRST_LEVEL;
|
|
|
|
//pSoldier->pLevelNode->ubShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubShadeLevel;
|
|
//pSoldier->pLevelNode->ubSumLights=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubSumLights;
|
|
//pSoldier->pLevelNode->ubMaxLights=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubMaxLights;
|
|
//pSoldier->pLevelNode->ubNaturalShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubNaturalShadeLevel;
|
|
|
|
|
|
}
|
|
|
|
if ( bOldLevel == 0 && pSoldier->bLevel == 0 )
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
// Show room at new level
|
|
//HideRoom( pSoldier->sGridNo, pSoldier );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void SetSoldierHeight( SOLDIERTYPE *pSoldier, FLOAT dNewHeight )
|
|
{
|
|
InternalSetSoldierHeight( pSoldier, dNewHeight, TRUE );
|
|
}
|
|
|
|
|
|
void SetSoldierGridNo( SOLDIERTYPE *pSoldier, INT16 sNewGridNo, BOOLEAN fForceRemove )
|
|
{
|
|
BOOLEAN fInWaterValue;
|
|
INT8 bDir;
|
|
INT32 cnt;
|
|
SOLDIERTYPE * pEnemy;
|
|
|
|
//INT16 sX, sY, sWorldX, sZLevel;
|
|
|
|
// Not if we're dead!
|
|
if ( ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( sNewGridNo != pSoldier->sGridNo || pSoldier->pLevelNode == NULL )
|
|
{
|
|
// Check if we are moving AND this is our next dest gridno....
|
|
if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_MOVING | ANIM_SPECIALMOVE ) )
|
|
{
|
|
if( !(gTacticalStatus.uiFlags & LOADING_SAVED_GAME ) )
|
|
{
|
|
if ( sNewGridNo != pSoldier->sDestination )
|
|
{
|
|
// THIS MUST be our new one......MAKE IT SO
|
|
sNewGridNo = pSoldier->sDestination;
|
|
}
|
|
|
|
// Now check this baby....
|
|
if ( sNewGridNo == pSoldier->sGridNo )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
pSoldier->sOldGridNo = pSoldier->sGridNo;
|
|
|
|
if ( pSoldier->ubBodyType == QUEENMONSTER )
|
|
{
|
|
SetPositionSndGridNo( pSoldier->iPositionSndID, sNewGridNo );
|
|
}
|
|
|
|
if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
|
|
{
|
|
InternalRemoveSoldierFromGridNo( pSoldier, fForceRemove );
|
|
}
|
|
|
|
// CHECK IF OUR NEW GIRDNO IS VALID,IF NOT DONOT SET!
|
|
if ( !GridNoOnVisibleWorldTile( sNewGridNo ) )
|
|
{
|
|
pSoldier->sGridNo = sNewGridNo;
|
|
return;
|
|
}
|
|
|
|
// Alrighty, update UI for this guy, if he's the selected guy...
|
|
if ( gusSelectedSoldier == pSoldier->ubID )
|
|
{
|
|
if ( guiCurrentEvent == C_WAIT_FOR_CONFIRM )
|
|
{
|
|
// Update path!
|
|
gfPlotNewMovement = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
// Reset some flags for optimizations..
|
|
pSoldier->sWalkToAttackGridNo = NOWHERE;
|
|
|
|
// ATE: Make sure!
|
|
// RemoveMerc( pSoldier->sGridNo, pSoldier, FALSE );
|
|
|
|
pSoldier->sGridNo = sNewGridNo;
|
|
|
|
// OK, check for special code to close door...
|
|
if ( pSoldier->bEndDoorOpenCode == 2 )
|
|
{
|
|
pSoldier->bEndDoorOpenCode = 0;
|
|
|
|
HandleDoorChangeFromGridNo( pSoldier, pSoldier->sEndDoorOpenCodeData, FALSE );
|
|
}
|
|
|
|
// OK, Update buddy's strategic insertion code....
|
|
pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
|
|
pSoldier->usStrategicInsertionData = sNewGridNo;
|
|
|
|
|
|
// Remove this gridno as a reserved place!
|
|
if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
|
|
{
|
|
UnMarkMovementReserved( pSoldier );
|
|
}
|
|
|
|
if ( pSoldier->sInitialGridNo == 0 )
|
|
{
|
|
pSoldier->sInitialGridNo = sNewGridNo;
|
|
pSoldier->usPatrolGrid[0] = sNewGridNo;
|
|
}
|
|
|
|
// Add records of this guy being adjacent
|
|
for (bDir = 0; bDir < NUM_WORLD_DIRECTIONS; bDir++)
|
|
{
|
|
gpWorldLevelData[ pSoldier->sGridNo + DirIncrementer[ bDir ] ].ubAdjacentSoldierCnt++;
|
|
}
|
|
|
|
if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
|
|
{
|
|
DropSmell( pSoldier );
|
|
}
|
|
|
|
// HANDLE ANY SPECIAL RENDERING SITUATIONS
|
|
pSoldier->sZLevelOverride = -1;
|
|
// If we are over a fence ( hopping ), make us higher!
|
|
|
|
if ( IsJumpableFencePresentAtGridno( sNewGridNo ) )
|
|
{
|
|
//sX = MapX( sNewGridNo );
|
|
//sY = MapY( sNewGridNo );
|
|
//GetWorldXYAbsoluteScreenXY( sX, sY, &sWorldX, &sZLevel);
|
|
//pSoldier->sZLevelOverride = (sZLevel*Z_SUBLAYERS)+ROOF_Z_LEVEL;
|
|
pSoldier->sZLevelOverride = TOPMOST_Z_LEVEL;
|
|
}
|
|
|
|
|
|
// Add/ remove tree if we are near it
|
|
// CheckForFullStructures( pSoldier );
|
|
|
|
// Add merc at new pos
|
|
if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
|
|
{
|
|
AddMercToHead( pSoldier->sGridNo, pSoldier, TRUE );
|
|
|
|
// If we are in the middle of climbing the roof!
|
|
if ( pSoldier->usAnimState == CLIMBUPROOF )
|
|
{
|
|
if(pSoldier->iLight!=(-1))
|
|
LightSpriteRoofStatus(pSoldier->iLight, TRUE );
|
|
}
|
|
else if ( pSoldier->usAnimState == CLIMBDOWNROOF )
|
|
{
|
|
if(pSoldier->iLight!=(-1))
|
|
LightSpriteRoofStatus(pSoldier->iLight, FALSE );
|
|
}
|
|
|
|
//JA2Gold:
|
|
//if the player wants the merc to cast the fake light AND it is night
|
|
if( pSoldier->bTeam != OUR_TEAM || gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] && NightTime() )
|
|
{
|
|
if ( pSoldier->bLevel > 0 && gpWorldLevelData[pSoldier->sGridNo].pRoofHead != NULL )
|
|
{
|
|
gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubShadeLevel;
|
|
gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubSumLights=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubSumLights;
|
|
gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubMaxLights=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubMaxLights;
|
|
gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubNaturalShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pRoofHead->ubNaturalShadeLevel;
|
|
}
|
|
else
|
|
{
|
|
gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubShadeLevel;
|
|
gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubSumLights=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubSumLights;
|
|
gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubMaxLights=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubMaxLights;
|
|
gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubNaturalShadeLevel=gpWorldLevelData[pSoldier->sGridNo].pLandHead->ubNaturalShadeLevel;
|
|
}
|
|
}
|
|
|
|
///HandlePlacingRoofMarker( pSoldier, pSoldier->sGridNo, TRUE, FALSE );
|
|
|
|
HandleAnimationProfile( pSoldier, pSoldier->usAnimState, FALSE );
|
|
|
|
HandleCrowShadowNewGridNo( pSoldier );
|
|
}
|
|
|
|
pSoldier->bOldOverTerrainType = pSoldier->bOverTerrainType;
|
|
pSoldier->bOverTerrainType = GetTerrainType( pSoldier->sGridNo );
|
|
|
|
// OK, check that our animation is up to date!
|
|
// Check our water value
|
|
|
|
if ( !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
|
|
{
|
|
fInWaterValue = MercInWater( pSoldier );
|
|
|
|
// ATE: If ever in water MAKE SURE WE WALK AFTERWOODS!
|
|
if ( fInWaterValue )
|
|
{
|
|
pSoldier->usUIMovementMode = WALKING;
|
|
}
|
|
|
|
if ( fInWaterValue != pSoldier->fPrevInWater )
|
|
{
|
|
//Update Animation data
|
|
SetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
|
|
|
|
// Update flag
|
|
pSoldier->fPrevInWater = fInWaterValue;
|
|
|
|
// Update sound...
|
|
if ( fInWaterValue )
|
|
{
|
|
PlaySoldierJA2Sample( pSoldier->ubID, ENTER_WATER_1, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ), TRUE );
|
|
}
|
|
else
|
|
{
|
|
// ATE: Check if we are going from water to land - if so, resume
|
|
// with regular movement mode...
|
|
EVENT_InitNewSoldierAnim( pSoldier, pSoldier->usUIMovementMode, 0 , FALSE );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
// OK, If we were not in deep water but we are now, handle deep animations!
|
|
if ( pSoldier->bOverTerrainType == DEEP_WATER && pSoldier->bOldOverTerrainType != DEEP_WATER )
|
|
{
|
|
// Based on our current animation, change!
|
|
switch( pSoldier->usAnimState )
|
|
{
|
|
case WALKING:
|
|
case RUNNING:
|
|
|
|
// IN deep water, swim!
|
|
|
|
// Make transition from low to deep
|
|
EVENT_InitNewSoldierAnim( pSoldier, LOW_TO_DEEP_WATER, 0 , FALSE );
|
|
pSoldier->usPendingAnimation = DEEP_WATER_SWIM;
|
|
|
|
PlayJA2Sample( ENTER_DEEP_WATER_1, RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
|
|
|
|
}
|
|
}
|
|
|
|
// Damage water if in deep water....
|
|
if ( pSoldier->bOverTerrainType == MED_WATER || pSoldier->bOverTerrainType == DEEP_WATER )
|
|
{
|
|
WaterDamage( pSoldier );
|
|
}
|
|
|
|
// OK, If we were in deep water but we are NOT now, handle mid animations!
|
|
if ( pSoldier->bOverTerrainType != DEEP_WATER && pSoldier->bOldOverTerrainType == DEEP_WATER )
|
|
{
|
|
// Make transition from low to deep
|
|
EVENT_InitNewSoldierAnim( pSoldier, DEEP_TO_LOW_WATER, 0 , FALSE );
|
|
pSoldier->usPendingAnimation = pSoldier->usUIMovementMode;
|
|
}
|
|
}
|
|
|
|
// are we now standing in tear gas without a decently working gas mask?
|
|
if ( GetSmokeEffectOnTile( sNewGridNo, pSoldier->bLevel ) )
|
|
{
|
|
BOOLEAN fSetGassed = TRUE;
|
|
|
|
// If we have a functioning gas mask...
|
|
if ( FindGasMask ( pSoldier ) != NO_SLOT && pSoldier->inv[ HEAD1POS ].bStatus[ 0 ] >= GASMASK_MIN_STATUS )
|
|
{
|
|
fSetGassed = FALSE;
|
|
}
|
|
// if ( pSoldier->inv[ HEAD1POS ].usItem == GASMASK && pSoldier->inv[ HEAD1POS ].bStatus[ 0 ] >= GASMASK_MIN_STATUS )
|
|
//{
|
|
// fSetGassed = FALSE;
|
|
//}
|
|
// if ( pSoldier->inv[ HEAD2POS ].usItem == GASMASK && pSoldier->inv[ HEAD2POS ].bStatus[ 0 ] >= GASMASK_MIN_STATUS )
|
|
//{
|
|
// fSetGassed = FALSE;
|
|
//}
|
|
|
|
if ( fSetGassed )
|
|
{
|
|
pSoldier->uiStatusFlags |= SOLDIER_GASSED;
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->bTeam == gbPlayerNum && pSoldier->bStealthMode )
|
|
{
|
|
// Merc got to a new tile by "sneaking". Did we theoretically sneak
|
|
// past an enemy?
|
|
|
|
if ( pSoldier->bOppCnt > 0 ) // opponents in sight
|
|
{
|
|
// check each possible enemy
|
|
for ( cnt = 0; cnt < MAX_NUM_SOLDIERS; cnt++ )
|
|
{
|
|
pEnemy = MercPtrs[ cnt ];
|
|
// if this guy is here and alive enough to be looking for us
|
|
if ( pEnemy->bActive && pEnemy->bInSector && ( pEnemy->bLife >= OKLIFE ) )
|
|
{
|
|
// no points for sneaking by the neutrals & friendlies!!!
|
|
if ( !pEnemy->bNeutral && ( pSoldier->bSide != pEnemy->bSide ) && (pEnemy->ubBodyType != COW && pEnemy->ubBodyType != CROW) )
|
|
{
|
|
// if we SEE this particular oppponent, and he DOESN'T see us... and he COULD see us...
|
|
if ( (pSoldier->bOppList[ cnt ] == SEEN_CURRENTLY) &&
|
|
pEnemy->bOppList[ pSoldier->ubID ] != SEEN_CURRENTLY &&
|
|
PythSpacesAway( pSoldier->sGridNo, pEnemy->sGridNo ) < DistanceVisible( pEnemy, DIRECTION_IRRELEVANT, DIRECTION_IRRELEVANT, pSoldier->sGridNo, pSoldier->bLevel, pSoldier ) )
|
|
{
|
|
// AGILITY (5): Soldier snuck 1 square past unaware enemy
|
|
StatChange( pSoldier, AGILAMT, 5, FALSE );
|
|
// Keep looping, we'll give'em 1 point for EACH such enemy!
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Adjust speed based on terrain, etc
|
|
SetSoldierAniSpeed( pSoldier );
|
|
|
|
}
|
|
else
|
|
{
|
|
int i = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void EVENT_FireSoldierWeapon( SOLDIERTYPE *pSoldier, INT16 sTargetGridNo )
|
|
{
|
|
INT16 sTargetXPos, sTargetYPos;
|
|
BOOLEAN fDoFireRightAway = FALSE;
|
|
|
|
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("EVENT_FireSoldierWeapon"));
|
|
|
|
// CANNOT BE SAME GRIDNO!
|
|
if ( pSoldier->sGridNo == sTargetGridNo )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( pSoldier->ubID == 33 )
|
|
{
|
|
int i = 0;
|
|
}
|
|
|
|
//switch ( pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunAmmoType )
|
|
//{
|
|
// case AMMO_SLEEP_DART:
|
|
// pSoldier->fMuzzleFlash = FALSE;
|
|
// break;
|
|
// default:
|
|
// pSoldier->fMuzzleFlash = TRUE;
|
|
// break;
|
|
//}
|
|
|
|
if ( IsFlashSuppressor (&pSoldier->inv[ pSoldier->ubAttackingHand ], pSoldier ) )
|
|
pSoldier->fMuzzleFlash = FALSE;
|
|
else
|
|
pSoldier->fMuzzleFlash = TRUE;
|
|
|
|
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("EVENT_FireSoldierWeapon: Muzzle flash = %d",pSoldier->fMuzzleFlash));
|
|
|
|
// Increment the number of people busy doing stuff because of an attack
|
|
//if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) )
|
|
//{
|
|
gTacticalStatus.ubAttackBusyCount++;
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting attack, attack count now %d", gTacticalStatus.ubAttackBusyCount) );
|
|
//}
|
|
|
|
// Set soldier's target gridno
|
|
// This assignment was redundent because it's already set in
|
|
// the actual event call
|
|
pSoldier->sTargetGridNo = sTargetGridNo;
|
|
//pSoldier->sLastTarget = sTargetGridNo;
|
|
pSoldier->ubTargetID = WhoIsThere2( sTargetGridNo, pSoldier->bTargetLevel );
|
|
|
|
// if (Item[pSoldier->inv[HANDPOS].usItem].usItemClass & IC_GUN)
|
|
{
|
|
if (pSoldier->bDoBurst)
|
|
{
|
|
// Set the TOTAL number of bullets to be fired
|
|
// Can't shoot more bullets than we have in our magazine!
|
|
if(pSoldier->bDoAutofire)
|
|
pSoldier->bBulletsLeft = __min( pSoldier->bDoAutofire, pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunShotsLeft );
|
|
else
|
|
{
|
|
if ( pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST )
|
|
pSoldier->bBulletsLeft = __min( Weapon[GetAttachedGrenadeLauncher(&pSoldier->inv[pSoldier->ubAttackingHand])].ubShotsPerBurst, pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunShotsLeft );
|
|
else
|
|
pSoldier->bBulletsLeft = __min( GetShotsPerBurst(&pSoldier->inv[ pSoldier->ubAttackingHand ]), pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunShotsLeft );
|
|
}
|
|
}
|
|
else if ( IsValidSecondHandShot( pSoldier ) )
|
|
{
|
|
// two-pistol attack - two bullets!
|
|
pSoldier->bBulletsLeft = 2;
|
|
}
|
|
else
|
|
{
|
|
pSoldier->bBulletsLeft = 1;
|
|
}
|
|
|
|
if ( AmmoTypes[pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunAmmoType].numberOfBullets > 1 )
|
|
{
|
|
pSoldier->bBulletsLeft *= AmmoTypes[pSoldier->inv[ pSoldier->ubAttackingHand ].ubGunAmmoType].numberOfBullets;
|
|
}
|
|
}
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting attack, bullets left %d", pSoldier->bBulletsLeft) );
|
|
|
|
// Convert our grid-not into an XY
|
|
ConvertGridNoToXY( sTargetGridNo, &sTargetXPos, &sTargetYPos );
|
|
|
|
|
|
// Change to fire animation
|
|
// Ready weapon
|
|
SoldierReadyWeapon( pSoldier, sTargetXPos, sTargetYPos, FALSE );
|
|
|
|
// IF WE ARE AN NPC, SLIDE VIEW TO SHOW WHO IS SHOOTING
|
|
{
|
|
//if ( pSoldier->fDoSpread )
|
|
//{
|
|
// If we are spreading burst, goto right away!
|
|
//EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE );
|
|
|
|
//}
|
|
|
|
// else
|
|
{
|
|
if (pSoldier->uiStatusFlags & SOLDIER_MONSTER )
|
|
{
|
|
// Force our direction!
|
|
EVENT_SetSoldierDirection( pSoldier, pSoldier->bDesiredDirection );
|
|
EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE );
|
|
}
|
|
else
|
|
{
|
|
// IF WE ARE IN REAl-TIME, FIRE IMMEDIATELY!
|
|
if ( ( ( gTacticalStatus.uiFlags & REALTIME ) || !( gTacticalStatus.uiFlags & INCOMBAT ) ) )
|
|
{
|
|
//fDoFireRightAway = TRUE;
|
|
}
|
|
|
|
// Check if our weapon has no intermediate anim...
|
|
if (Item[pSoldier->inv[ HANDPOS ].usItem].rocketlauncher || Item[pSoldier->inv[ HANDPOS ].usItem].grenadelauncher || Item[pSoldier->inv[ HANDPOS ].usItem].mortar )
|
|
///* switch( pSoldier->inv[ HANDPOS ].usItem )
|
|
// {
|
|
//case RPG7:
|
|
//case ROCKET_LAUNCHER:
|
|
//case MORTAR:
|
|
//case GLAUNCHER:*/
|
|
|
|
fDoFireRightAway = TRUE;
|
|
// break;
|
|
//}
|
|
|
|
if ( fDoFireRightAway )
|
|
{
|
|
// Set to true so we don't get toasted twice for APs..
|
|
pSoldier->fDontUnsetLastTargetFromTurn = TRUE;
|
|
|
|
// Make sure we don't try and do fancy prone turning.....
|
|
pSoldier->fTurningFromPronePosition = FALSE;
|
|
|
|
// Force our direction!
|
|
EVENT_SetSoldierDirection( pSoldier, pSoldier->bDesiredDirection );
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE );
|
|
}
|
|
else
|
|
{
|
|
// Set flag indicating we are about to shoot once destination direction is hit
|
|
pSoldier->fTurningToShoot = TRUE;
|
|
|
|
if ( pSoldier->bTeam != gbPlayerNum && pSoldier->bVisible != -1)
|
|
{
|
|
LocateSoldier( pSoldier->ubID, DONTSETLOCATOR );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//gAnimControl[ pSoldier->usAnimState ].ubEndHeight
|
|
// ChangeSoldierState( pSoldier, SHOOT_RIFLE_STAND, 0 , FALSE );
|
|
|
|
UINT16 SelectFireAnimation( SOLDIERTYPE *pSoldier, UINT8 ubHeight )
|
|
{
|
|
INT16 sDist;
|
|
UINT16 usItem;
|
|
FLOAT dTargetX;
|
|
FLOAT dTargetY;
|
|
FLOAT dTargetZ;
|
|
BOOLEAN fDoLowShot = FALSE;
|
|
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("SelectFireAnimation"));
|
|
|
|
|
|
//Do different things if we are a monster
|
|
if (pSoldier->uiStatusFlags & SOLDIER_MONSTER)
|
|
{
|
|
switch( pSoldier->ubBodyType )
|
|
{
|
|
case ADULTFEMALEMONSTER:
|
|
case AM_MONSTER:
|
|
case YAF_MONSTER:
|
|
case YAM_MONSTER:
|
|
|
|
return( MONSTER_SPIT_ATTACK );
|
|
break;
|
|
|
|
case LARVAE_MONSTER:
|
|
|
|
break;
|
|
|
|
case INFANT_MONSTER:
|
|
|
|
return( INFANT_ATTACK );
|
|
break;
|
|
|
|
case QUEENMONSTER:
|
|
|
|
return( QUEEN_SPIT );
|
|
break;
|
|
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
if ( pSoldier->ubBodyType == ROBOTNOWEAPON )
|
|
{
|
|
if ( pSoldier->bDoBurst > 0 )
|
|
{
|
|
return( ROBOT_BURST_SHOOT );
|
|
}
|
|
else
|
|
{
|
|
return( ROBOT_SHOOT );
|
|
}
|
|
}
|
|
|
|
// Check for rocket laucncher....
|
|
if ( Item[pSoldier->inv[ HANDPOS ].usItem].rocketlauncher )
|
|
{
|
|
return( SHOOT_ROCKET );
|
|
}
|
|
|
|
// Check for mortar....
|
|
if ( Item[pSoldier->inv[ HANDPOS ].usItem].mortar )
|
|
{
|
|
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,"SelectFireAnimation: shoot_mortar");
|
|
return( SHOOT_MORTAR );
|
|
}
|
|
|
|
// Check for tank cannon
|
|
if ( Item[pSoldier->inv[ HANDPOS ].usItem].cannon )
|
|
{
|
|
return( TANK_SHOOT );
|
|
}
|
|
|
|
if ( pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE )
|
|
{
|
|
return( TANK_BURST );
|
|
}
|
|
|
|
// Determine which animation to do...depending on stance and gun in hand...
|
|
switch ( ubHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
usItem = pSoldier->inv[ HANDPOS ].usItem;
|
|
|
|
// CHECK 2ND HAND!
|
|
if ( IsValidSecondHandShot( pSoldier ) )
|
|
{
|
|
// Increment the number of people busy doing stuff because of an attack
|
|
//gTacticalStatus.ubAttackBusyCount++;
|
|
//DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting attack with 2 guns, attack count now %d", gTacticalStatus.ubAttackBusyCount) );
|
|
|
|
return( SHOOT_DUAL_STAND );
|
|
}
|
|
else
|
|
{
|
|
// OK, while standing check distance away from target, and shoot low if we should!
|
|
sDist = PythSpacesAway( pSoldier->sGridNo, pSoldier->sTargetGridNo );
|
|
|
|
//ATE: OK, SEE WERE WE ARE TARGETING....
|
|
GetTargetWorldPositions( pSoldier, pSoldier->sTargetGridNo, &dTargetX, &dTargetY, &dTargetZ );
|
|
|
|
//CalculateSoldierZPos( pSoldier, FIRING_POS, &dFirerZ );
|
|
|
|
if ( sDist <= 2 && dTargetZ <= 100 )
|
|
{
|
|
fDoLowShot = TRUE;
|
|
}
|
|
|
|
// ATE: Made distence away long for psitols such that they never use this....
|
|
//if ( !(Item[ usItem ].fFlags & ITEM_TWO_HANDED) )
|
|
//{
|
|
// fDoLowShot = FALSE;
|
|
//}
|
|
|
|
// Don't do any low shots if in water
|
|
if ( MercInWater( pSoldier ) )
|
|
{
|
|
fDoLowShot = FALSE;
|
|
}
|
|
|
|
|
|
if ( pSoldier->bDoBurst > 0 )
|
|
{
|
|
if ( fDoLowShot )
|
|
{
|
|
return( FIRE_BURST_LOW_STAND );
|
|
}
|
|
else
|
|
{
|
|
return( STANDING_BURST );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( fDoLowShot )
|
|
{
|
|
return( FIRE_LOW_STAND );
|
|
}
|
|
else
|
|
{
|
|
return( SHOOT_RIFLE_STAND );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
|
|
if ( pSoldier->bDoBurst > 0 )
|
|
{
|
|
// pSoldier->fBurstCompleted = FALSE;
|
|
return( PRONE_BURST );
|
|
}
|
|
else
|
|
{
|
|
if ( IsValidSecondHandShot( pSoldier ) )
|
|
{
|
|
return( SHOOT_DUAL_PRONE );
|
|
}
|
|
else
|
|
{
|
|
return( SHOOT_RIFLE_PRONE );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
|
|
if ( IsValidSecondHandShot( pSoldier ) )
|
|
{
|
|
// Increment the number of people busy doing stuff because of an attack
|
|
//gTacticalStatus.ubAttackBusyCount++;
|
|
//DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting attack with 2 guns, attack count now %d", gTacticalStatus.ubAttackBusyCount) );
|
|
|
|
return( SHOOT_DUAL_CROUCH );
|
|
}
|
|
else
|
|
{
|
|
if ( pSoldier->bDoBurst > 0 )
|
|
{
|
|
// pSoldier->fBurstCompleted = FALSE;
|
|
return( CROUCHED_BURST );
|
|
}
|
|
else
|
|
{
|
|
return( SHOOT_RIFLE_CROUCH );
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
AssertMsg( FALSE, String( "SelectFireAnimation: ERROR - Invalid height %d", ubHeight ) );
|
|
break;
|
|
}
|
|
|
|
|
|
// If here, an internal error has occured!
|
|
Assert( FALSE );
|
|
return ( 0 );
|
|
}
|
|
|
|
|
|
UINT16 GetMoveStateBasedOnStance( SOLDIERTYPE *pSoldier, UINT8 ubStanceHeight )
|
|
{
|
|
// Determine which animation to do...depending on stance and gun in hand...
|
|
switch ( ubStanceHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
if ( pSoldier->fUIMovementFast && !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
|
|
{
|
|
return( RUNNING );
|
|
}
|
|
else
|
|
{
|
|
return( WALKING );
|
|
}
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
if ( pSoldier->fUIMovementFast )
|
|
{
|
|
return( CRAWLING );
|
|
}
|
|
else
|
|
{
|
|
return( CRAWLING );
|
|
}
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
if ( pSoldier->fUIMovementFast )
|
|
{
|
|
return( SWATTING );
|
|
}
|
|
else
|
|
{
|
|
return( SWATTING );
|
|
}
|
|
break;
|
|
|
|
|
|
default:
|
|
AssertMsg( FALSE, String( "GetMoveStateBasedOnStance: ERROR - Invalid height %d", ubStanceHeight ) );
|
|
break;
|
|
}
|
|
|
|
|
|
// If here, an internal error has occured!
|
|
Assert( FALSE );
|
|
return ( 0 );
|
|
}
|
|
|
|
|
|
void SelectFallAnimation( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// Determine which animation to do...depending on stance and gun in hand...
|
|
switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
EVENT_InitNewSoldierAnim( pSoldier, FLYBACK_HIT, 0 , FALSE );
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
EVENT_InitNewSoldierAnim( pSoldier, FLYBACK_HIT, 0 , FALSE );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
BOOLEAN SoldierReadyWeapon( SOLDIERTYPE *pSoldier )
|
|
{
|
|
return( InternalSoldierReadyWeapon( pSoldier, (INT8)pSoldier->bDirection, FALSE ) );
|
|
}
|
|
|
|
BOOLEAN SoldierReadyWeapon( SOLDIERTYPE *pSoldier, INT16 sTargetXPos, INT16 sTargetYPos, BOOLEAN fEndReady )
|
|
{
|
|
INT16 sFacingDir;
|
|
|
|
sFacingDir = GetDirectionFromXY( sTargetXPos , sTargetYPos, pSoldier );
|
|
|
|
return( InternalSoldierReadyWeapon( pSoldier, (INT8)sFacingDir, fEndReady ) );
|
|
}
|
|
|
|
|
|
BOOLEAN InternalSoldierReadyWeapon( SOLDIERTYPE *pSoldier, UINT8 sFacingDir, BOOLEAN fEndReady )
|
|
{
|
|
UINT16 usAnimState;
|
|
BOOLEAN fReturnVal = FALSE;
|
|
|
|
// Handle monsters differently
|
|
if (pSoldier->uiStatusFlags & SOLDIER_MONSTER)
|
|
{
|
|
if ( !fEndReady )
|
|
{
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, sFacingDir );
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("InternalSoldierReadyWeapon: PickingAnimation"));
|
|
usAnimState = PickSoldierReadyAnimation( pSoldier, fEndReady );
|
|
|
|
if ( usAnimState != INVALID_ANIMATION )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, usAnimState, 0 , FALSE );
|
|
fReturnVal = TRUE;
|
|
}
|
|
|
|
if ( !fEndReady )
|
|
{
|
|
// Ready direction for new facing direction
|
|
if ( usAnimState == INVALID_ANIMATION )
|
|
{
|
|
usAnimState = pSoldier->usAnimState;
|
|
}
|
|
|
|
EVENT_InternalSetSoldierDesiredDirection( pSoldier, sFacingDir, FALSE, usAnimState );
|
|
|
|
// Check if facing dir is different from ours and change direction if so!
|
|
//if ( sFacingDir != pSoldier->bDirection )
|
|
//{
|
|
// DeductPoints( pSoldier, AP_CHANGE_FACING, 0 );
|
|
//}//
|
|
|
|
}
|
|
|
|
return( fReturnVal );
|
|
}
|
|
|
|
UINT16 PickSoldierReadyAnimation( SOLDIERTYPE *pSoldier, BOOLEAN fEndReady )
|
|
{
|
|
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("PickSoldierReadyAnimation"));
|
|
|
|
// Invalid animation if nothing in our hands
|
|
if ( pSoldier->inv[ HANDPOS ].usItem == NOTHING )
|
|
{
|
|
return( INVALID_ANIMATION );
|
|
}
|
|
|
|
if ( pSoldier->bOverTerrainType == DEEP_WATER )
|
|
{
|
|
return( INVALID_ANIMATION );
|
|
}
|
|
|
|
if ( pSoldier->ubBodyType == ROBOTNOWEAPON )
|
|
{
|
|
return( INVALID_ANIMATION );
|
|
}
|
|
|
|
// Check if we have a gun.....
|
|
if ( Item[ pSoldier->inv[ HANDPOS ].usItem ].usItemClass != IC_GUN && !Item[pSoldier->inv[ HANDPOS ].usItem].grenadelauncher )
|
|
{
|
|
return( INVALID_ANIMATION );
|
|
}
|
|
|
|
if ( Item[pSoldier->inv[ HANDPOS ].usItem].rocketlauncher )
|
|
{
|
|
return( INVALID_ANIMATION );
|
|
}
|
|
|
|
if ( pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE )
|
|
{
|
|
return( INVALID_ANIMATION );
|
|
}
|
|
|
|
if ( fEndReady )
|
|
{
|
|
// IF our gun is already drawn, do not change animation, just direction
|
|
if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_FIREREADY | ANIM_FIRE ))
|
|
{
|
|
|
|
switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
// CHECK 2ND HAND!
|
|
if ( IsValidSecondHandShot( pSoldier ) )
|
|
{
|
|
return( END_DUAL_STAND );
|
|
}
|
|
else
|
|
{
|
|
return( END_RIFLE_STAND );
|
|
}
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
|
|
if ( IsValidSecondHandShot( pSoldier ) )
|
|
{
|
|
return( END_DUAL_PRONE );
|
|
}
|
|
else
|
|
{
|
|
return( END_RIFLE_PRONE );
|
|
}
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
|
|
// CHECK 2ND HAND!
|
|
if ( IsValidSecondHandShot( pSoldier ) )
|
|
{
|
|
return( END_DUAL_CROUCH );
|
|
}
|
|
else
|
|
{
|
|
return( END_RIFLE_CROUCH );
|
|
}
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// IF our gun is already drawn, do not change animation, just direction
|
|
if ( !(gAnimControl[ pSoldier->usAnimState ].uiFlags & ( ANIM_FIREREADY | ANIM_FIRE ) ) )
|
|
{
|
|
|
|
{
|
|
switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
// CHECK 2ND HAND!
|
|
if ( IsValidSecondHandShot( pSoldier ) )
|
|
{
|
|
return( READY_DUAL_STAND );
|
|
}
|
|
else
|
|
{
|
|
return( READY_RIFLE_STAND );
|
|
}
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
// Go into crouch, turn, then go into prone again
|
|
//ChangeSoldierStance( pSoldier, ANIM_CROUCH );
|
|
//pSoldier->ubDesiredHeight = ANIM_PRONE;
|
|
//ChangeSoldierState( pSoldier, PRONE_UP );
|
|
if ( IsValidSecondHandShot( pSoldier ) )
|
|
{
|
|
return( READY_DUAL_PRONE );
|
|
}
|
|
else
|
|
{
|
|
return( READY_RIFLE_PRONE );
|
|
}
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
|
|
// CHECK 2ND HAND!
|
|
if ( IsValidSecondHandShot( pSoldier ) )
|
|
{
|
|
return( READY_DUAL_CROUCH );
|
|
}
|
|
else
|
|
{
|
|
return( READY_RIFLE_CROUCH );
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return( INVALID_ANIMATION );
|
|
}
|
|
|
|
extern SOLDIERTYPE * FreeUpAttackerGivenTarget( UINT8 ubID, UINT8 ubTargetID );
|
|
extern SOLDIERTYPE * ReduceAttackBusyGivenTarget( UINT8 ubID, UINT8 ubTargetID );
|
|
|
|
|
|
// ATE: THIS FUNCTION IS USED FOR ALL SOLDIER TAKE DAMAGE FUNCTIONS!
|
|
void EVENT_SoldierGotHit( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, INT16 sBreathLoss, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation, INT16 sSubsequent, INT16 sLocationGrid )
|
|
{
|
|
UINT8 ubCombinedLoss, ubVolume, ubReason;
|
|
SOLDIERTYPE * pNewSoldier;
|
|
|
|
ubReason = 0;
|
|
|
|
DebugMsg(TOPIC_JA2,DBG_LEVEL_3 , "EVENT_SoldierGotHit");
|
|
|
|
// ATE: If we have gotten hit, but are still in our attack animation, reduce count!
|
|
switch ( pSoldier->usAnimState )
|
|
{
|
|
case SHOOT_ROCKET:
|
|
case SHOOT_MORTAR:
|
|
case THROW_ITEM:
|
|
case LOB_ITEM:
|
|
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker - ATTACK ANIMATION %s ENDED BY HIT ANIMATION, Now %d", gAnimControl[ pSoldier->usAnimState ].zAnimStr, gTacticalStatus.ubAttackBusyCount ) );
|
|
ReduceAttackBusyCount( pSoldier->ubID, FALSE );
|
|
break;
|
|
}
|
|
|
|
// DO STUFF COMMON FOR ALL TYPES
|
|
if ( ubAttackerID != NOBODY)
|
|
{
|
|
MercPtrs[ubAttackerID]->bLastAttackHit = TRUE;
|
|
}
|
|
|
|
// Set attacker's ID
|
|
pSoldier->ubAttackerID = ubAttackerID;
|
|
|
|
if ( !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
|
|
{
|
|
// Increment being attacked count
|
|
pSoldier->bBeingAttackedCount++;
|
|
}
|
|
|
|
// if defender is a vehicle, there will be no hit animation played!
|
|
if ( !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
|
|
{
|
|
// Increment the number of people busy doing stuff because of an attack (busy doing hit anim!)
|
|
gTacticalStatus.ubAttackBusyCount++;
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Person got hit, attack count now %d", gTacticalStatus.ubAttackBusyCount) );
|
|
}
|
|
|
|
// ATE; Save hit location info...( for later anim determination stuff )
|
|
pSoldier->ubHitLocation = ubHitLocation;
|
|
|
|
// handle morale for heavy damage attacks
|
|
if ( sDamage > 25 )
|
|
{
|
|
if ( pSoldier->ubAttackerID != NOBODY && MercPtrs[ pSoldier->ubAttackerID ]->bTeam == gbPlayerNum )
|
|
{
|
|
HandleMoraleEvent( MercPtrs[ pSoldier->ubAttackerID ], MORALE_DID_LOTS_OF_DAMAGE, MercPtrs[ pSoldier->ubAttackerID ]->sSectorX, MercPtrs[ pSoldier->ubAttackerID ]->sSectorY, MercPtrs[ pSoldier->ubAttackerID ]->bSectorZ );
|
|
}
|
|
if (pSoldier->bTeam == gbPlayerNum)
|
|
{
|
|
HandleMoraleEvent( pSoldier, MORALE_TOOK_LOTS_OF_DAMAGE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
|
|
}
|
|
}
|
|
|
|
// SWITCH IN TYPE OF WEAPON
|
|
if ( ubSpecial == FIRE_WEAPON_TOSSED_OBJECT_SPECIAL )
|
|
{
|
|
ubReason = TAKE_DAMAGE_OBJECT;
|
|
}
|
|
else if ( Item[ usWeaponIndex ].usItemClass & IC_TENTACLES )
|
|
{
|
|
ubReason = TAKE_DAMAGE_TENTACLES;
|
|
}
|
|
// marke take out gunfire if ammotype is an explosive
|
|
else if ( Item[ usWeaponIndex ].usItemClass & ( IC_GUN | IC_THROWING_KNIFE ) && AmmoTypes[MercPtrs[ubAttackerID]->inv[MercPtrs[ubAttackerID]->ubAttackingHand ].ubGunAmmoType].explosionSize <= 1)
|
|
{
|
|
if ( ubSpecial == FIRE_WEAPON_SLEEP_DART_SPECIAL )
|
|
{
|
|
UINT32 uiChance;
|
|
|
|
// put the drug in!
|
|
pSoldier->bSleepDrugCounter = 10;
|
|
|
|
uiChance = SleepDartSuccumbChance( pSoldier );
|
|
|
|
if ( PreRandom( 100 ) < uiChance )
|
|
{
|
|
// succumb to the drug!
|
|
sBreathLoss = (INT16)( pSoldier->bBreathMax * 100 );
|
|
}
|
|
|
|
}
|
|
else if ( ubSpecial == FIRE_WEAPON_BLINDED_BY_SPIT_SPECIAL )
|
|
{
|
|
// blinded!!
|
|
if ( (pSoldier->bBlindedCounter == 0) )
|
|
{
|
|
// say quote
|
|
if (pSoldier->uiStatusFlags & SOLDIER_PC)
|
|
{
|
|
TacticalCharacterDialogue( pSoldier, QUOTE_BLINDED );
|
|
}
|
|
DecayIndividualOpplist( pSoldier );
|
|
}
|
|
// will always increase counter by at least 1
|
|
pSoldier->bBlindedCounter += (sDamage / 8) + 1;
|
|
|
|
// Dirty panel
|
|
fInterfacePanelDirty = DIRTYLEVEL2;
|
|
}
|
|
sBreathLoss += BP_GET_HIT;
|
|
ubReason = TAKE_DAMAGE_GUNFIRE;
|
|
}
|
|
else if ( Item[ usWeaponIndex ].usItemClass & IC_BLADE )
|
|
{
|
|
sBreathLoss = BP_GET_HIT;
|
|
ubReason = TAKE_DAMAGE_BLADE;
|
|
}
|
|
else if ( Item[ usWeaponIndex ].usItemClass & IC_PUNCH )
|
|
{
|
|
// damage from hand-to-hand is 1/4 normal, 3/4 breath.. the sDamage value
|
|
// is actually how much breath we'll take away
|
|
sBreathLoss = sDamage * 100;
|
|
sDamage = sDamage / PUNCH_REAL_DAMAGE_PORTION;
|
|
if ( AreInMeanwhile() && gCurrentMeanwhileDef.ubMeanwhileID == INTERROGATION )
|
|
{
|
|
sBreathLoss = 0;
|
|
sDamage /= 2;
|
|
}
|
|
ubReason = TAKE_DAMAGE_HANDTOHAND;
|
|
}
|
|
// marke added one 'or' for explosive ammo. variation of: AmmoTypes[pSoldier->inv[pSoldier->ubAttackingHand ].ubGunAmmoType].explosionSize > 1
|
|
// extracting attacker�s ammo type
|
|
else if ( Item[ usWeaponIndex ].usItemClass & IC_EXPLOSV || AmmoTypes[MercPtrs[ubAttackerID]->inv[MercPtrs[ubAttackerID]->ubAttackingHand ].ubGunAmmoType].explosionSize > 1)
|
|
{
|
|
if ( usWeaponIndex == STRUCTURE_EXPLOSION )
|
|
{
|
|
ubReason = TAKE_DAMAGE_STRUCTURE_EXPLOSION;
|
|
}
|
|
else
|
|
{
|
|
ubReason = TAKE_DAMAGE_EXPLOSION;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Soldier Control: Weapon class not handled in SoldierGotHit( ) %d", usWeaponIndex ) );
|
|
}
|
|
|
|
|
|
// CJC: moved to after SoldierTakeDamage so that any quotes from the defender
|
|
// will not be said if they are knocked out or killed
|
|
if ( ubReason != TAKE_DAMAGE_TENTACLES && ubReason != TAKE_DAMAGE_OBJECT )
|
|
{
|
|
// OK, OK: THis is hairy, however, it's ness. because the normal freeup call uses the
|
|
// attckers intended target, and here we want to use thier actual target....
|
|
|
|
// ATE: If it's from GUNFIRE damage, keep in mind bullets...
|
|
if ( Item[ usWeaponIndex ].usItemClass & IC_GUN )
|
|
{
|
|
pNewSoldier = FreeUpAttackerGivenTarget( pSoldier->ubAttackerID, pSoldier->ubID );
|
|
}
|
|
else
|
|
{
|
|
pNewSoldier = ReduceAttackBusyGivenTarget( pSoldier->ubAttackerID, pSoldier->ubID );
|
|
}
|
|
|
|
if (pNewSoldier != NULL)
|
|
{
|
|
pSoldier = pNewSoldier;
|
|
}
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Tried to free up attacker, attack count now %d", gTacticalStatus.ubAttackBusyCount) );
|
|
}
|
|
|
|
|
|
// OK, If we are a vehicle.... damage vehicle...( people inside... )
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
|
|
{
|
|
SoldierTakeDamage( pSoldier, ANIM_CROUCH, sDamage, sBreathLoss, ubReason, pSoldier->ubAttackerID, NOWHERE, FALSE, TRUE );
|
|
return;
|
|
}
|
|
|
|
// DEDUCT LIFE
|
|
ubCombinedLoss = SoldierTakeDamage( pSoldier, ANIM_CROUCH, sDamage, sBreathLoss, ubReason, pSoldier->ubAttackerID, NOWHERE, FALSE, TRUE );
|
|
|
|
// ATE: OK, Let's check our ASSIGNMENT state,
|
|
// If anything other than on a squad or guard, make them guard....
|
|
if ( pSoldier->bTeam == gbPlayerNum )
|
|
{
|
|
if ( pSoldier->bAssignment >= ON_DUTY && pSoldier->bAssignment != ASSIGNMENT_POW )
|
|
{
|
|
if( pSoldier->fMercAsleep )
|
|
{
|
|
pSoldier->fMercAsleep = FALSE;
|
|
pSoldier -> fForcedToStayAwake = FALSE;
|
|
|
|
// refresh map screen
|
|
fCharacterInfoPanelDirty = TRUE;
|
|
fTeamPanelDirty = TRUE;
|
|
}
|
|
|
|
AddCharacterToAnySquad( pSoldier );
|
|
}
|
|
}
|
|
|
|
|
|
// SCREAM!!!!
|
|
ubVolume = CalcScreamVolume( pSoldier, ubCombinedLoss );
|
|
|
|
// IF WE ARE AT A HIT_STOP ANIMATION
|
|
// DO APPROPRIATE HITWHILE DOWN ANIMATION
|
|
if ( !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_HITSTOP ) || pSoldier->usAnimState != JFK_HITDEATH_STOP )
|
|
{
|
|
MakeNoise( pSoldier->ubID, pSoldier->sGridNo, pSoldier->bLevel, pSoldier->bOverTerrainType, ubVolume, NOISE_SCREAM);
|
|
}
|
|
|
|
// IAN ADDED THIS SAT JUNE 14th : HAVE TO SHOW VICTIM!
|
|
if (gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT) && pSoldier->bVisible != -1 && pSoldier->bTeam == gbPlayerNum )
|
|
LocateSoldier(pSoldier->ubID,DONTSETLOCATOR);
|
|
|
|
|
|
if ( Item[ usWeaponIndex ].usItemClass & IC_BLADE )
|
|
{
|
|
PlayJA2Sample( (UINT32)( KNIFE_IMPACT ), RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
|
|
}
|
|
else
|
|
{
|
|
PlayJA2Sample( (UINT32)( BULLET_IMPACT_1 + Random(3) ), RATE_11025, SoundVolume( MIDVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
|
|
}
|
|
|
|
// PLAY RANDOM GETTING HIT SOUND
|
|
// ONLY IF WE ARE CONSCIOUS!
|
|
if ( pSoldier->bLife >= CONSCIOUSNESS )
|
|
{
|
|
if ( pSoldier->ubBodyType == CROW )
|
|
{
|
|
// Exploding crow...
|
|
PlayJA2Sample( CROW_EXPLODE_1, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
|
|
}
|
|
else
|
|
{
|
|
// ATE: This is to disallow large amounts of smaples being played which is load!
|
|
if ( pSoldier->fGettingHit && pSoldier->usAniCode != STANDING_BURST_HIT )
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_HIT1 + Random( 2 ) ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// CHECK FOR DOING HIT WHILE DOWN
|
|
if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_HITSTOP ) )
|
|
{
|
|
switch( pSoldier->usAnimState )
|
|
{
|
|
case FLYBACKHIT_STOP:
|
|
ChangeSoldierState( pSoldier, FALLBACK_DEATHTWICH, 0, FALSE );
|
|
break;
|
|
|
|
case STAND_FALLFORWARD_STOP:
|
|
ChangeSoldierState( pSoldier, GENERIC_HIT_DEATHTWITCHNB, 0, FALSE );
|
|
break;
|
|
|
|
case JFK_HITDEATH_STOP:
|
|
ChangeSoldierState( pSoldier, JFK_HITDEATH_TWITCHB, 0, FALSE );
|
|
break;
|
|
|
|
case FALLBACKHIT_STOP:
|
|
ChangeSoldierState( pSoldier, FALLBACK_HIT_DEATHTWITCHNB, 0, FALSE );
|
|
break;
|
|
|
|
case PRONE_LAYFROMHIT_STOP:
|
|
ChangeSoldierState( pSoldier, PRONE_HIT_DEATHTWITCHNB, 0, FALSE );
|
|
break;
|
|
|
|
case PRONE_HITDEATH_STOP:
|
|
ChangeSoldierState( pSoldier, PRONE_HIT_DEATHTWITCHB, 0 , FALSE );
|
|
break;
|
|
|
|
case FALLFORWARD_HITDEATH_STOP:
|
|
ChangeSoldierState( pSoldier, GENERIC_HIT_DEATHTWITCHB, 0 , FALSE );
|
|
break;
|
|
|
|
case FALLBACK_HITDEATH_STOP:
|
|
ChangeSoldierState( pSoldier, FALLBACK_HIT_DEATHTWITCHB, 0 , FALSE );
|
|
break;
|
|
|
|
case FALLOFF_DEATH_STOP:
|
|
ChangeSoldierState( pSoldier, FALLOFF_TWITCHB, 0 , FALSE );
|
|
break;
|
|
|
|
case FALLOFF_STOP:
|
|
ChangeSoldierState( pSoldier, FALLOFF_TWITCHNB, 0 , FALSE );
|
|
break;
|
|
|
|
case FALLOFF_FORWARD_DEATH_STOP:
|
|
ChangeSoldierState( pSoldier, FALLOFF_FORWARD_TWITCHB, 0 , FALSE );
|
|
break;
|
|
|
|
case FALLOFF_FORWARD_STOP:
|
|
ChangeSoldierState( pSoldier, FALLOFF_FORWARD_TWITCHNB, 0 , FALSE );
|
|
break;
|
|
|
|
default:
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "Soldier Control: Death state %d has no death hit", pSoldier->usAnimState ) );
|
|
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Set goback to aim after hit flag!
|
|
// Only if we were aiming!
|
|
if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY )
|
|
{
|
|
pSoldier->fGoBackToAimAfterHit = TRUE;
|
|
}
|
|
|
|
// IF COWERING, PLAY SPECIFIC GENERIC HIT STAND...
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_COWERING )
|
|
{
|
|
if ( pSoldier->bLife == 0 || IS_MERC_BODY_TYPE( pSoldier ) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, CIV_COWER_HIT, 0 , FALSE );
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Change based on body type
|
|
switch( pSoldier->ubBodyType )
|
|
{
|
|
case COW:
|
|
EVENT_InitNewSoldierAnim( pSoldier, COW_HIT, 0 , FALSE );
|
|
return;
|
|
break;
|
|
|
|
case BLOODCAT:
|
|
EVENT_InitNewSoldierAnim( pSoldier, BLOODCAT_HIT, 0 , FALSE );
|
|
return;
|
|
break;
|
|
|
|
case ADULTFEMALEMONSTER:
|
|
case AM_MONSTER:
|
|
case YAF_MONSTER:
|
|
case YAM_MONSTER:
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, ADULTMONSTER_HIT, 0 , FALSE );
|
|
return;
|
|
break;
|
|
|
|
case LARVAE_MONSTER:
|
|
EVENT_InitNewSoldierAnim( pSoldier, LARVAE_HIT, 0 , FALSE );
|
|
return;
|
|
break;
|
|
|
|
case QUEENMONSTER:
|
|
EVENT_InitNewSoldierAnim( pSoldier, QUEEN_HIT, 0 , FALSE );
|
|
return;
|
|
break;
|
|
|
|
case CRIPPLECIV:
|
|
|
|
{
|
|
// OK, do some code here to allow the fact that poor buddy can be thrown back if it's a big enough hit...
|
|
EVENT_InitNewSoldierAnim( pSoldier, CRIPPLE_HIT, 0 , FALSE );
|
|
|
|
//pSoldier->bLife = 0;
|
|
//EVENT_InitNewSoldierAnim( pSoldier, CRIPPLE_DIE_FLYBACK, 0 , FALSE );
|
|
|
|
|
|
}
|
|
return;
|
|
break;
|
|
|
|
case ROBOTNOWEAPON:
|
|
EVENT_InitNewSoldierAnim( pSoldier, ROBOTNW_HIT, 0 , FALSE );
|
|
return;
|
|
break;
|
|
|
|
|
|
case INFANT_MONSTER:
|
|
EVENT_InitNewSoldierAnim( pSoldier, INFANT_HIT, 0 , FALSE );
|
|
return;
|
|
|
|
case CROW:
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, CROW_DIE, 0 , FALSE );
|
|
return;
|
|
|
|
//case FATCIV:
|
|
case MANCIV:
|
|
case MINICIV:
|
|
case DRESSCIV:
|
|
case HATKIDCIV:
|
|
case KIDCIV:
|
|
|
|
// OK, if life is 0 and not set as dead ( this is a death hit... )
|
|
if ( !( pSoldier->uiStatusFlags & SOLDIER_DEAD ) && pSoldier->bLife == 0 )
|
|
{
|
|
// Randomize death!
|
|
if ( Random( 2 ) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, CIV_DIE2, 0 , FALSE );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// IF here, go generic hit ALWAYS.....
|
|
EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE );
|
|
return;
|
|
break;
|
|
}
|
|
|
|
// If here, we are a merc, check if we are in water
|
|
if ( pSoldier->bOverTerrainType == LOW_WATER )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, WATER_HIT, 0 , FALSE );
|
|
return;
|
|
}
|
|
if ( pSoldier->bOverTerrainType == DEEP_WATER )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, DEEP_WATER_HIT, 0 , FALSE );
|
|
return;
|
|
}
|
|
|
|
|
|
// SWITCH IN TYPE OF WEAPON
|
|
if ( Item[ usWeaponIndex ].usItemClass & ( IC_GUN | IC_THROWING_KNIFE ) )
|
|
{
|
|
SoldierGotHitGunFire( pSoldier, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation );
|
|
}
|
|
if ( Item[ usWeaponIndex ].usItemClass & IC_BLADE )
|
|
{
|
|
SoldierGotHitBlade( pSoldier, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation );
|
|
}
|
|
// marke setting ammo explosions included here with 3rd 'or' including ubReason
|
|
if ( Item[ usWeaponIndex ].usItemClass & IC_EXPLOSV || Item[ usWeaponIndex ].usItemClass & IC_TENTACLES || ubReason == TAKE_DAMAGE_EXPLOSION )
|
|
{
|
|
SoldierGotHitExplosion( pSoldier, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation );
|
|
}
|
|
if ( Item[ usWeaponIndex ].usItemClass & IC_PUNCH )
|
|
{
|
|
SoldierGotHitPunch( pSoldier, usWeaponIndex, sDamage, bDirection, sRange, ubAttackerID, ubSpecial, ubHitLocation );
|
|
}
|
|
|
|
}
|
|
|
|
UINT8 CalcScreamVolume( SOLDIERTYPE * pSoldier, UINT8 ubCombinedLoss )
|
|
{
|
|
// NB explosions are so loud they should drown out screams
|
|
UINT8 ubVolume;
|
|
|
|
if (ubCombinedLoss < 1)
|
|
{
|
|
ubVolume = 1;
|
|
}
|
|
else
|
|
{
|
|
ubVolume = ubCombinedLoss;
|
|
}
|
|
|
|
// Victim yells out in pain, making noise. Yelps are louder from greater
|
|
// wounds, but softer for more experienced soldiers.
|
|
|
|
if (ubVolume > (10 - EffectiveExpLevel( pSoldier ) ))
|
|
{
|
|
ubVolume = 10 - EffectiveExpLevel( pSoldier );
|
|
}
|
|
|
|
/*
|
|
// the "Speck factor"... He's a whiner, and extra-sensitive to pain!
|
|
if (ptr->trait == NERVOUS)
|
|
ubVolume += 2;
|
|
*/
|
|
|
|
if (ubVolume < 0)
|
|
{
|
|
ubVolume = 0;
|
|
}
|
|
|
|
return( ubVolume );
|
|
}
|
|
|
|
|
|
void DoGenericHit( SOLDIERTYPE *pSoldier, UINT8 ubSpecial, INT16 bDirection )
|
|
{
|
|
// Based on stance, select generic hit animation
|
|
switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
// For now, check if we are affected by a burst
|
|
// For now, if the weapon was a gun, special 1 == burst
|
|
// ATE: Only do this for mercs!
|
|
if ( ubSpecial == FIRE_WEAPON_BURST_SPECIAL && pSoldier->ubBodyType <= REGFEMALE )
|
|
{
|
|
//SetSoldierDesiredDirection( pSoldier, bDirection );
|
|
EVENT_SetSoldierDirection( pSoldier, (INT8)bDirection );
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->bDirection );
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, STANDING_BURST_HIT, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
// Check in hand for rifle
|
|
if ( SoldierCarriesTwoHandedWeapon( pSoldier ) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, RIFLE_STAND_HIT, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_PRONE, 0 , FALSE );
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_CROUCH, 0 , FALSE );
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
void SoldierGotHitGunFire( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation )
|
|
{
|
|
UINT16 usNewGridNo;
|
|
BOOLEAN fBlownAway = FALSE;
|
|
BOOLEAN fHeadHit = FALSE;
|
|
BOOLEAN fFallenOver = FALSE;
|
|
|
|
// MAYBE CHANGE TO SPECIAL ANIMATION BASED ON VALUE SET BY DAMAGE CALCULATION CODE
|
|
// ALL THESE ONLY WORK ON STANDING PEOPLE
|
|
if (!(pSoldier->uiStatusFlags & SOLDIER_MONSTER) && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND)
|
|
{
|
|
if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND )
|
|
{
|
|
if (ubSpecial == FIRE_WEAPON_HEAD_EXPLODE_SPECIAL)
|
|
{
|
|
if ( gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] )
|
|
{
|
|
if (SpacesAway( pSoldier->sGridNo, Menptr[ubAttackerID].sGridNo ) <= MAX_DISTANCE_FOR_MESSY_DEATH || (SpacesAway( pSoldier->sGridNo, Menptr[ubAttackerID].sGridNo ) <= MAX_BARRETT_DISTANCE_FOR_MESSY_DEATH && usWeaponIndex == BARRETT ))
|
|
{
|
|
usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (INT8)( DirectionInc( pSoldier->bDirection ) ) );
|
|
|
|
// CHECK OK DESTINATION!
|
|
if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, pSoldier->bDirection, JFK_HITDEATH ) )
|
|
{
|
|
usNewGridNo = NewGridNo( (UINT16)usNewGridNo, (INT8)( DirectionInc( pSoldier->bDirection ) ) );
|
|
|
|
if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, pSoldier->bDirection, pSoldier->usAnimState ) )
|
|
{
|
|
fHeadHit = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ubSpecial == FIRE_WEAPON_CHEST_EXPLODE_SPECIAL)
|
|
{
|
|
if ( gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] )
|
|
{
|
|
if (SpacesAway( pSoldier->sGridNo, Menptr[ubAttackerID].sGridNo ) <= MAX_DISTANCE_FOR_MESSY_DEATH || (SpacesAway( pSoldier->sGridNo, Menptr[ubAttackerID].sGridNo ) <= MAX_BARRETT_DISTANCE_FOR_MESSY_DEATH && usWeaponIndex == BARRETT ))
|
|
{
|
|
|
|
// possibly play torso explosion anim!
|
|
if (pSoldier->bDirection == bDirection)
|
|
{
|
|
usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ pSoldier->bDirection ] ) );
|
|
|
|
if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, gOppositeDirection[ bDirection ], FLYBACK_HIT ) )
|
|
{
|
|
usNewGridNo = NewGridNo( (UINT16)usNewGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) );
|
|
|
|
if ( OKFallDirection( pSoldier, usNewGridNo, pSoldier->bLevel, gOppositeDirection[ bDirection ], pSoldier->usAnimState ) )
|
|
{
|
|
fBlownAway = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ubSpecial == FIRE_WEAPON_LEG_FALLDOWN_SPECIAL)
|
|
{
|
|
// possibly play fall over anim!
|
|
// this one is NOT restricted by distance
|
|
if (IsValidStance( pSoldier, ANIM_PRONE ) )
|
|
{
|
|
// Can't be in water, or not standing
|
|
if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND && !MercInWater( pSoldier ) )
|
|
{
|
|
fFallenOver = TRUE;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, gzLateLocalizedString[ 20 ], pSoldier->name );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// IF HERE AND GUY IS DEAD, RETURN!
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_DEAD )
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Releasesoldierattacker,Dead soldier hit" ) );
|
|
ReleaseSoldiersAttacker( pSoldier );
|
|
return;
|
|
}
|
|
|
|
if ( fFallenOver )
|
|
{
|
|
SoldierCollapse( pSoldier );
|
|
return;
|
|
}
|
|
|
|
if ( fBlownAway )
|
|
{
|
|
// Only for mercs...
|
|
if ( pSoldier->ubBodyType < 4 )
|
|
{
|
|
ChangeToFlybackAnimation( pSoldier, (INT8)bDirection );
|
|
return;
|
|
}
|
|
}
|
|
|
|
if ( fHeadHit )
|
|
{
|
|
// Only for mercs ( or KIDS! )
|
|
if ( pSoldier->ubBodyType < 4 || pSoldier->ubBodyType == HATKIDCIV || pSoldier->ubBodyType == KIDCIV )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, JFK_HITDEATH, 0 , FALSE );
|
|
return;
|
|
}
|
|
}
|
|
|
|
DoGenericHit( pSoldier, ubSpecial, bDirection );
|
|
|
|
}
|
|
|
|
void SoldierGotHitExplosion( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation )
|
|
{
|
|
INT16 sNewGridNo;
|
|
|
|
// IF HERE AND GUY IS DEAD, RETURN!
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_DEAD )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//check for services
|
|
ReceivingSoldierCancelServices( pSoldier );
|
|
GivingSoldierCancelServices( pSoldier );
|
|
|
|
|
|
if ( gGameSettings.fOptions[ TOPTION_BLOOD_N_GORE ] )
|
|
{
|
|
if ( Explosive[ Item[ usWeaponIndex ].ubClassIndex ].ubRadius >= 3 && pSoldier->bLife == 0 && gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_PRONE )
|
|
{
|
|
if ( sRange >= 2 && sRange <= 4 )
|
|
{
|
|
DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_HIT1 + Random( 2 ) ) );
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, CHARIOTS_OF_FIRE, 0 , FALSE );
|
|
return;
|
|
}
|
|
else if ( sRange <= 1 )
|
|
{
|
|
DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_HIT1 + Random( 2 ) ) );
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, BODYEXPLODING, 0 , FALSE );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we can't fal back or such, so generic hit...
|
|
if ( pSoldier->ubBodyType >= 4 )
|
|
{
|
|
DoGenericHit( pSoldier, 0, bDirection );
|
|
return;
|
|
}
|
|
|
|
// Based on stance, select generic hit animation
|
|
switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
case ANIM_CROUCH:
|
|
|
|
EVENT_SetSoldierDirection( pSoldier, (INT8)bDirection );
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, pSoldier->bDirection );
|
|
|
|
// Check behind us!
|
|
sNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) );
|
|
|
|
if ( OKFallDirection( pSoldier, sNewGridNo, pSoldier->bLevel, gOppositeDirection[ bDirection ], FLYBACK_HIT ) )
|
|
{
|
|
ChangeToFallbackAnimation( pSoldier, (INT8)bDirection );
|
|
}
|
|
else
|
|
{
|
|
if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND )
|
|
{
|
|
BeginTyingToFall( pSoldier );
|
|
EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0, FALSE );
|
|
}
|
|
else
|
|
{
|
|
SoldierCollapse( pSoldier );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
|
|
SoldierCollapse( pSoldier );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void SoldierGotHitBlade( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation )
|
|
{
|
|
|
|
// IF HERE AND GUY IS DEAD, RETURN!
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_DEAD )
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
// Based on stance, select generic hit animation
|
|
switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
// Check in hand for rifle
|
|
if ( SoldierCarriesTwoHandedWeapon( pSoldier ) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, RIFLE_STAND_HIT, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE );
|
|
}
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_CROUCH, 0 , FALSE );
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_PRONE, 0 , FALSE );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void SoldierGotHitPunch( SOLDIERTYPE *pSoldier, UINT16 usWeaponIndex, INT16 sDamage, UINT16 bDirection, UINT16 sRange, UINT8 ubAttackerID, UINT8 ubSpecial, UINT8 ubHitLocation )
|
|
{
|
|
|
|
// IF HERE AND GUY IS DEAD, RETURN!
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_DEAD )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Based on stance, select generic hit animation
|
|
switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
// Check in hand for rifle
|
|
if ( SoldierCarriesTwoHandedWeapon( pSoldier ) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, RIFLE_STAND_HIT, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_STAND, 0 , FALSE );
|
|
}
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_CROUCH, 0 , FALSE );
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
EVENT_InitNewSoldierAnim( pSoldier, GENERIC_HIT_PRONE, 0 , FALSE );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN EVENT_InternalGetNewSoldierPath( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAnim, BOOLEAN fFromUI, BOOLEAN fForceRestartAnim )
|
|
{
|
|
INT32 iDest;
|
|
INT16 sNewGridNo;
|
|
BOOLEAN fContinue;
|
|
UINT32 uiDist;
|
|
UINT16 usAnimState;
|
|
UINT16 usMoveAnimState = usMovementAnim;
|
|
INT16 sMercGridNo;
|
|
UINT16 usPathingData[ MAX_PATH_LIST_SIZE ];
|
|
UINT8 ubPathingMaxDirection;
|
|
BOOLEAN fAdvancePath = TRUE;
|
|
UINT8 fFlags = 0;
|
|
|
|
// Ifd this code, make true if a player
|
|
if ( fFromUI == 3 )
|
|
{
|
|
if ( pSoldier->bTeam == gbPlayerNum )
|
|
{
|
|
fFromUI = 1;
|
|
}
|
|
else
|
|
{
|
|
fFromUI = 0;
|
|
}
|
|
}
|
|
|
|
// ATE: if a civ, and from UI, and were cowering, remove from cowering
|
|
if ( AM_AN_EPC( pSoldier ) && fFromUI )
|
|
{
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_COWERING )
|
|
{
|
|
SetSoldierCowerState( pSoldier, FALSE );
|
|
usMoveAnimState = WALKING;
|
|
}
|
|
}
|
|
|
|
|
|
pSoldier->bGoodContPath = FALSE;
|
|
|
|
if ( pSoldier->fDelayedMovement )
|
|
{
|
|
if ( pSoldier->ubDelayedMovementFlags & DELAYED_MOVEMENT_FLAG_PATH_THROUGH_PEOPLE )
|
|
{
|
|
fFlags = PATH_THROUGH_PEOPLE;
|
|
}
|
|
else
|
|
{
|
|
fFlags = PATH_IGNORE_PERSON_AT_DEST;
|
|
}
|
|
pSoldier->fDelayedMovement = FALSE;
|
|
}
|
|
|
|
if ( gfGetNewPathThroughPeople )
|
|
{
|
|
fFlags = PATH_THROUGH_PEOPLE;
|
|
}
|
|
|
|
// ATE: Some stuff here for realtime, going through interface....
|
|
if ( ( !( gTacticalStatus.uiFlags & INCOMBAT ) && ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING ) && fFromUI == 1 ) || fFromUI == 2 )
|
|
{
|
|
if ( pSoldier->bCollapsed )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
sMercGridNo = pSoldier->sGridNo;
|
|
pSoldier->sGridNo = pSoldier->sDestination;
|
|
|
|
// Check if path is good before copying it into guy's path...
|
|
if ( FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, fFlags ) == 0 )
|
|
{
|
|
// Set to old....
|
|
pSoldier->sGridNo = sMercGridNo;
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
uiDist = FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, COPYROUTE, fFlags );
|
|
|
|
pSoldier->sGridNo = sMercGridNo;
|
|
pSoldier->sFinalDestination = sDestGridNo;
|
|
|
|
if ( uiDist > 0 )
|
|
{
|
|
// Add one to path data size....
|
|
if ( fAdvancePath )
|
|
{
|
|
memcpy( usPathingData, pSoldier->usPathingData, sizeof( usPathingData ) );
|
|
ubPathingMaxDirection = (UINT8)usPathingData[ MAX_PATH_LIST_SIZE -1 ];
|
|
memcpy( &(pSoldier->usPathingData[1]), usPathingData, sizeof( usPathingData ) - sizeof( UINT16 ) );
|
|
|
|
// If we have reach the max, go back one sFinalDest....
|
|
if ( pSoldier->usPathDataSize == MAX_PATH_LIST_SIZE )
|
|
{
|
|
//pSoldier->sFinalDestination = NewGridNo( (UINT16)pSoldier->sFinalDestination, DirectionInc( gOppositeDirection[ ubPathingMaxDirection ] ) );
|
|
}
|
|
else
|
|
{
|
|
pSoldier->usPathDataSize++;
|
|
}
|
|
}
|
|
|
|
usMoveAnimState = pSoldier->usUIMovementMode;
|
|
|
|
if ( pSoldier->bOverTerrainType == DEEP_WATER )
|
|
{
|
|
usMoveAnimState = DEEP_WATER_SWIM;
|
|
}
|
|
|
|
// Change animation only.... set value to NOT call any goto new gridno stuff.....
|
|
if ( usMoveAnimState != pSoldier->usAnimState )
|
|
{
|
|
//
|
|
pSoldier->usDontUpdateNewGridNoOnMoveAnimChange = TRUE;
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, usMoveAnimState, 0, FALSE );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
// we can use the soldier's level here because we don't have pathing across levels right now...
|
|
if (pSoldier->bPathStored)
|
|
{
|
|
fContinue = TRUE;
|
|
}
|
|
else
|
|
{
|
|
iDest = FindBestPath( pSoldier, sDestGridNo, pSoldier->bLevel, usMovementAnim, COPYROUTE, fFlags );
|
|
fContinue = (iDest != 0);
|
|
}
|
|
|
|
// Only if we can get a path here
|
|
if ( fContinue )
|
|
{
|
|
// Debug messages
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Soldier %d: Get new path", pSoldier->ubID ) );
|
|
|
|
// Set final destination
|
|
pSoldier->sFinalDestination = sDestGridNo;
|
|
pSoldier->fPastXDest = 0;
|
|
pSoldier->fPastYDest = 0;
|
|
|
|
|
|
// CHECK IF FIRST TILE IS FREE
|
|
sNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( (UINT8)pSoldier->usPathingData[ pSoldier->usPathIndex ] ) );
|
|
|
|
// If true, we're OK, if not, WAIT for a guy to pass!
|
|
// If we are in deep water, we can only swim!
|
|
if ( pSoldier->bOverTerrainType == DEEP_WATER )
|
|
{
|
|
usMoveAnimState = DEEP_WATER_SWIM;
|
|
}
|
|
|
|
// If we were aiming, end aim!
|
|
usAnimState = PickSoldierReadyAnimation( pSoldier, TRUE );
|
|
|
|
// Add a pending animation first!
|
|
// Only if we were standing!
|
|
if ( usAnimState != INVALID_ANIMATION && gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, usAnimState, 0, FALSE );
|
|
pSoldier->usPendingAnimation = usMoveAnimState;
|
|
}
|
|
else
|
|
{
|
|
// Call local copy for change soldier state!
|
|
EVENT_InitNewSoldierAnim( pSoldier, usMoveAnimState, 0, fForceRestartAnim );
|
|
|
|
}
|
|
|
|
// Change desired direction
|
|
// ATE: Here we have a situation where in RT, we may have
|
|
// gotten a new path, but we are alreayd moving.. so
|
|
// at leasty change new dest. This will be redundent if the ANI is a totaly new one
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
void EVENT_GetNewSoldierPath( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAnim )
|
|
{
|
|
// ATE: Default restart of animation to TRUE
|
|
EVENT_InternalGetNewSoldierPath( pSoldier, sDestGridNo, usMovementAnim, FALSE, TRUE );
|
|
}
|
|
|
|
// Change our state based on stance, to stop!
|
|
void StopSoldier( SOLDIERTYPE *pSoldier )
|
|
{
|
|
ReceivingSoldierCancelServices( pSoldier );
|
|
GivingSoldierCancelServices( pSoldier );
|
|
|
|
if ( !( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STATIONARY ) )
|
|
{
|
|
//SoldierGotoStationaryStance( pSoldier );
|
|
EVENT_StopMerc( pSoldier, pSoldier->sGridNo, pSoldier->bDirection );
|
|
}
|
|
|
|
// Set desination
|
|
pSoldier->sFinalDestination = pSoldier->sGridNo;
|
|
|
|
}
|
|
|
|
void SoldierGotoStationaryStance( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// ATE: This is to turn off fast movement, that us used to change movement mode
|
|
// for ui display on stance changes....
|
|
if ( pSoldier->bTeam == gbPlayerNum )
|
|
{
|
|
//pSoldier->fUIMovementFast = FALSE;
|
|
}
|
|
|
|
// The queen, if she sees anybody, goes to ready, not normal breath....
|
|
if ( pSoldier->ubBodyType == QUEENMONSTER )
|
|
{
|
|
if ( pSoldier->bOppCnt > 0 || pSoldier->bTeam == gbPlayerNum )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, QUEEN_READY, 0 , TRUE );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check if we are in deep water!
|
|
if ( pSoldier->bOverTerrainType == DEEP_WATER )
|
|
{
|
|
// IN deep water, tred!
|
|
EVENT_InitNewSoldierAnim( pSoldier, DEEP_WATER_TRED, 0 , FALSE );
|
|
}
|
|
else if ( pSoldier->ubServicePartner != NOBODY && pSoldier->bLife >= OKLIFE && pSoldier->bBreath > 0 )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, GIVING_AID, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
// Change state back to stationary state for given height
|
|
switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
// If we are cowering....goto cower state
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_COWERING )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, START_COWER, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, STANDING, 0 , FALSE );
|
|
}
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
|
|
// If we are cowering....goto cower state
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_COWERING )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, COWERING, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, CROUCHING, 0 , FALSE );
|
|
}
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
EVENT_InitNewSoldierAnim( pSoldier, PRONE, 0 , FALSE );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void ChangeSoldierStance( SOLDIERTYPE *pSoldier, UINT8 ubDesiredStance )
|
|
{
|
|
UINT16 usNewState;
|
|
|
|
// Check if they are the same!
|
|
if ( ubDesiredStance == gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
// Free up from stance change
|
|
FreeUpNPCFromStanceChange( pSoldier );
|
|
return;
|
|
}
|
|
|
|
// Set UI Busy
|
|
SetUIBusy( pSoldier->ubID );
|
|
|
|
// ATE: If we are an NPC, cower....
|
|
if ( pSoldier->ubBodyType >= FATCIV && pSoldier->ubBodyType <= KIDCIV )
|
|
{
|
|
if ( ubDesiredStance == ANIM_STAND )
|
|
{
|
|
SetSoldierCowerState( pSoldier, FALSE );
|
|
}
|
|
else
|
|
{
|
|
SetSoldierCowerState( pSoldier, TRUE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
usNewState = GetNewSoldierStateFromNewStance( pSoldier, ubDesiredStance );
|
|
|
|
// Set desired stance
|
|
pSoldier->ubDesiredHeight = ubDesiredStance;
|
|
|
|
// Now change to appropriate animation
|
|
EVENT_InitNewSoldierAnim( pSoldier, usNewState, 0 , FALSE );
|
|
}
|
|
}
|
|
|
|
void EVENT_InternalSetSoldierDestination( SOLDIERTYPE *pSoldier, UINT16 usNewDirection, BOOLEAN fFromMove, UINT16 usAnimState )
|
|
{
|
|
UINT16 usNewGridNo;
|
|
INT16 sXPos, sYPos;
|
|
|
|
// Get dest gridno, convert to center coords
|
|
usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( usNewDirection ) );
|
|
|
|
ConvertMapPosToWorldTileCenter( usNewGridNo, &sXPos, &sYPos );
|
|
|
|
// Save new dest gridno, x, y
|
|
pSoldier->sDestination = usNewGridNo;
|
|
pSoldier->sDestXPos = sXPos;
|
|
pSoldier->sDestYPos = sYPos;
|
|
|
|
pSoldier->bMovementDirection = (INT8)usNewDirection;
|
|
|
|
|
|
// OK, ATE: If we are side_stepping, calculate a NEW desired direction....
|
|
if ( pSoldier->bReverse && usAnimState == SIDE_STEP )
|
|
{
|
|
UINT8 ubPerpDirection;
|
|
|
|
// Get a new desired direction,
|
|
ubPerpDirection = gPurpendicularDirection[ pSoldier->bDirection ][ usNewDirection ];
|
|
|
|
// CHange actual and desired direction....
|
|
EVENT_SetSoldierDirection( pSoldier, ubPerpDirection );
|
|
pSoldier->bDesiredDirection = pSoldier->bDirection;
|
|
}
|
|
else
|
|
{
|
|
if ( !( gAnimControl[ usAnimState ].uiFlags & ANIM_SPECIALMOVE ) )
|
|
{
|
|
EVENT_InternalSetSoldierDesiredDirection( pSoldier, usNewDirection, fFromMove, usAnimState );
|
|
}
|
|
}
|
|
}
|
|
|
|
void EVENT_SetSoldierDestination( SOLDIERTYPE *pSoldier, UINT16 usNewDirection )
|
|
{
|
|
EVENT_InternalSetSoldierDestination( pSoldier, usNewDirection, FALSE, pSoldier->usAnimState );
|
|
}
|
|
|
|
|
|
// function to determine which direction a creature can turn in
|
|
INT8 MultiTiledTurnDirection( SOLDIERTYPE * pSoldier, INT8 bStartDirection, INT8 bDesiredDirection )
|
|
{
|
|
INT8 bTurningIncrement;
|
|
INT8 bCurrentDirection;
|
|
INT8 bLoop;
|
|
UINT16 usStructureID, usAnimSurface;
|
|
STRUCTURE_FILE_REF * pStructureFileRef;
|
|
BOOLEAN fOk = FALSE;
|
|
|
|
// start by trying to turn in quickest direction
|
|
bTurningIncrement = (INT8) QuickestDirection( bStartDirection, bDesiredDirection );
|
|
|
|
usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, pSoldier->usUIMovementMode );
|
|
|
|
pStructureFileRef = GetAnimationStructureRef( pSoldier->ubID, usAnimSurface, pSoldier->usUIMovementMode );
|
|
if ( !pStructureFileRef )
|
|
{
|
|
// without structure data, well, assume quickest direction
|
|
return( bTurningIncrement );
|
|
}
|
|
|
|
// ATE: Only if we have a levelnode...
|
|
if ( pSoldier->pLevelNode != NULL && pSoldier->pLevelNode->pStructureData != NULL )
|
|
{
|
|
usStructureID = pSoldier->pLevelNode->pStructureData->usStructureID;
|
|
}
|
|
else
|
|
{
|
|
usStructureID = INVALID_STRUCTURE_ID;
|
|
}
|
|
|
|
bLoop = 0;
|
|
bCurrentDirection = bStartDirection;
|
|
|
|
while( bLoop < 2 )
|
|
{
|
|
while( bCurrentDirection != bDesiredDirection )
|
|
{
|
|
bCurrentDirection += bTurningIncrement;
|
|
|
|
// did we wrap directions?
|
|
if ( bCurrentDirection < 0 )
|
|
{
|
|
bCurrentDirection = (MAXDIR - 1);
|
|
}
|
|
else if ( bCurrentDirection >= MAXDIR )
|
|
{
|
|
bCurrentDirection = 0;
|
|
}
|
|
|
|
// check to see if we can add creature in that direction
|
|
fOk = OkayToAddStructureToWorld( pSoldier->sGridNo, pSoldier->bLevel, &(pStructureFileRef->pDBStructureRef[ gOneCDirection[ bCurrentDirection ] ]), usStructureID );
|
|
if (!fOk)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( (bCurrentDirection == bDesiredDirection) && fOk )
|
|
{
|
|
// success!!
|
|
return( bTurningIncrement );
|
|
}
|
|
|
|
bLoop++;
|
|
if ( bLoop < 2 )
|
|
{
|
|
// change direction of loop etc
|
|
bCurrentDirection = bStartDirection;
|
|
bTurningIncrement *= -1;
|
|
}
|
|
}
|
|
// nothing found... doesn't matter much what we return
|
|
return( bTurningIncrement );
|
|
}
|
|
|
|
|
|
|
|
void EVENT_InternalSetSoldierDesiredDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection, BOOLEAN fInitalMove, UINT16 usAnimState )
|
|
{
|
|
//if ( usAnimState == WALK_BACKWARDS )
|
|
if ( pSoldier->bReverse && usAnimState != SIDE_STEP )
|
|
{
|
|
// OK, check if we are going to go in the exact opposite than our facing....
|
|
usNewDirection = gOppositeDirection[ usNewDirection ];
|
|
}
|
|
|
|
|
|
pSoldier->bDesiredDirection = (INT8)usNewDirection;
|
|
|
|
// If we are prone, goto crouched first!
|
|
// ONly if we are stationary, and only if directions are differnet!
|
|
|
|
// ATE: If we are fNoAPsToFinnishMove, stop what we were doing and
|
|
// reset flag.....
|
|
if ( pSoldier->fNoAPToFinishMove && ( gAnimControl[ usAnimState ].uiFlags & ANIM_MOVING ) )
|
|
{
|
|
// ATE; Commented this out: NEVER, EVER, start a new anim from this function, as an eternal loop will result....
|
|
//SoldierGotoStationaryStance( pSoldier );
|
|
// Reset flag!
|
|
AdjustNoAPToFinishMove( pSoldier, FALSE );
|
|
}
|
|
|
|
if ( pSoldier->bDesiredDirection != pSoldier->bDirection )
|
|
{
|
|
if ( gAnimControl[ usAnimState ].uiFlags & ( ANIM_BREATH | ANIM_OK_CHARGE_AP_FOR_TURN | ANIM_FIREREADY ) && !fInitalMove && !pSoldier->fDontChargeTurningAPs )
|
|
{
|
|
// Deduct points for initial turn!
|
|
switch( gAnimControl[ usAnimState ].ubEndHeight )
|
|
{
|
|
// Now change to appropriate animation
|
|
case ANIM_STAND:
|
|
DeductPoints( pSoldier, AP_LOOK_STANDING, 0 );
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
DeductPoints( pSoldier, AP_LOOK_CROUCHED, 0 );
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
DeductPoints( pSoldier, AP_LOOK_PRONE, 0 );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
pSoldier->fDontChargeTurningAPs = FALSE;
|
|
|
|
if ( fInitalMove )
|
|
{
|
|
if ( gAnimControl[ usAnimState ].ubHeight == ANIM_PRONE )
|
|
{
|
|
if ( pSoldier->fTurningFromPronePosition != TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE )
|
|
{
|
|
pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_START_UP_FROM_MOVE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( gAnimControl[ usAnimState ].uiFlags & ANIM_STATIONARY || pSoldier->fNoAPToFinishMove || fInitalMove )
|
|
{
|
|
if ( gAnimControl[ usAnimState ].ubHeight == ANIM_PRONE )
|
|
{
|
|
// Set this beasty of a flag to allow us to go back down to prone if we choose!
|
|
// ATE: Alrighty, set flag to go back down only if we are not moving anywhere
|
|
//if ( pSoldier->sDestination == pSoldier->sGridNo )
|
|
if ( !fInitalMove )
|
|
{
|
|
pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_ON;
|
|
|
|
// Set a pending animation to change stance first...
|
|
SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set desired direction for the extended directions...
|
|
pSoldier->ubHiResDesiredDirection = ubExtDirection[ pSoldier->bDesiredDirection ];
|
|
|
|
if ( pSoldier->bDesiredDirection != pSoldier->bDirection )
|
|
{
|
|
if ( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE ) || CREATURE_OR_BLOODCAT( pSoldier ) )
|
|
{
|
|
pSoldier->uiStatusFlags |= SOLDIER_PAUSEANIMOVE;
|
|
}
|
|
}
|
|
|
|
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
|
|
{
|
|
pSoldier->bTurningIncrement = (INT8) ExtQuickestDirection( pSoldier->ubHiResDirection, pSoldier->ubHiResDesiredDirection );
|
|
}
|
|
else
|
|
{
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_MULTITILE )
|
|
{
|
|
pSoldier->bTurningIncrement = (INT8) MultiTiledTurnDirection( pSoldier, pSoldier->bDirection, pSoldier->bDesiredDirection );
|
|
}
|
|
else
|
|
{
|
|
pSoldier->bTurningIncrement = (INT8) QuickestDirection( pSoldier->bDirection, pSoldier->bDesiredDirection );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void EVENT_SetSoldierDesiredDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection )
|
|
{
|
|
EVENT_InternalSetSoldierDesiredDirection( pSoldier, usNewDirection, FALSE, pSoldier->usAnimState );
|
|
}
|
|
|
|
|
|
void EVENT_SetSoldierDirection( SOLDIERTYPE *pSoldier, UINT16 usNewDirection )
|
|
{
|
|
// Remove old location data
|
|
HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE );
|
|
|
|
pSoldier->bDirection = (INT8)usNewDirection;
|
|
|
|
// Updated extended direction.....
|
|
pSoldier->ubHiResDirection = ubExtDirection[ pSoldier->bDirection ];
|
|
|
|
// Add new stuff
|
|
HandleAnimationProfile( pSoldier, pSoldier->usAnimState, FALSE );
|
|
|
|
// If we are turning, we have chaanged our aim!
|
|
if ( !pSoldier->fDontUnsetLastTargetFromTurn )
|
|
{
|
|
pSoldier->sLastTarget = NOWHERE;
|
|
}
|
|
|
|
AdjustForFastTurnAnimation( pSoldier );
|
|
|
|
// Update structure info!
|
|
// if ( pSoldier->uiStatusFlags & SOLDIER_MULTITILE )
|
|
{
|
|
UpdateMercStructureInfo( pSoldier );
|
|
}
|
|
|
|
// Handle Profile data for hit locations
|
|
HandleAnimationProfile( pSoldier, pSoldier->usAnimState, TRUE );
|
|
|
|
HandleCrowShadowNewDirection( pSoldier );
|
|
|
|
// Change values!
|
|
SetSoldierLocatorOffsets( pSoldier );
|
|
|
|
}
|
|
|
|
|
|
void EVENT_BeginMercTurn( SOLDIERTYPE *pSoldier, BOOLEAN fFromRealTime, INT32 iRealTimeCounter )
|
|
{
|
|
// NB realtimecounter is not used, always passed in as 0 now!
|
|
|
|
INT32 iBlood;
|
|
|
|
if (pSoldier->bUnderFire)
|
|
{
|
|
// UnderFire now starts at 2 for "under fire this turn",
|
|
// down to 1 for "under fire last turn", to 0.
|
|
pSoldier->bUnderFire--;
|
|
}
|
|
|
|
// ATE: Add decay effect sfor drugs...
|
|
if ( fFromRealTime ) //&& iRealTimeCounter % 300 )
|
|
{
|
|
HandleEndTurnDrugAdjustments( pSoldier );
|
|
}
|
|
else
|
|
{
|
|
HandleEndTurnDrugAdjustments( pSoldier );
|
|
}
|
|
|
|
// ATE: Don't bleed if in AUTO BANDAGE!
|
|
if ( !gTacticalStatus.fAutoBandageMode )
|
|
{
|
|
// Blood is not for the weak of heart, or mechanical
|
|
if ( !( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE | SOLDIER_ROBOT ) ) )
|
|
{
|
|
if ( pSoldier->bBleeding || pSoldier->bLife < OKLIFE ) // is he bleeding or dying?
|
|
{
|
|
iBlood = CheckBleeding( pSoldier ); // check if he might lose another life point
|
|
|
|
// ATE: Only if in sector!
|
|
if ( pSoldier->bInSector )
|
|
{
|
|
if ( iBlood != NOBLOOD )
|
|
{
|
|
DropBlood( pSoldier, (INT8)iBlood, pSoldier->bVisible );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// survived bleeding, but is he out of breath?
|
|
if ( pSoldier->bLife && !pSoldier->bBreath && MercInWater( pSoldier ) )
|
|
{
|
|
// Drowning...
|
|
}
|
|
|
|
// if he is still alive (didn't bleed to death)
|
|
if ( pSoldier->bLife )
|
|
{
|
|
// reduce the effects of any residual shock from past injuries by half
|
|
pSoldier->bShock /= 2;
|
|
|
|
// if this person has heard a noise that hasn't been investigated
|
|
if (pSoldier->sNoiseGridno != NOWHERE)
|
|
{
|
|
if (pSoldier->ubNoiseVolume) // and the noise volume is still positive
|
|
{
|
|
pSoldier->ubNoiseVolume--; // the volume of the noise "decays" by 1 point
|
|
|
|
if (!pSoldier->ubNoiseVolume) // if the volume has reached zero
|
|
{
|
|
pSoldier->sNoiseGridno = NOWHERE; // forget about the noise!
|
|
}
|
|
}
|
|
}
|
|
|
|
// save unused action points up to a maximum
|
|
/*
|
|
if ((savedPts = pSoldier->bActionPts) > MAX_AP_CARRIED)
|
|
savedPts = MAX_AP_CARRIED;
|
|
*/
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_GASSED )
|
|
{
|
|
// then must get a gas mask or leave the gassed area to get over it
|
|
// if ( ( pSoldier->inv[ HEAD1POS ].usItem == GASMASK || pSoldier->inv[ HEAD2POS ].usItem == GASMASK ) || !( GetSmokeEffectOnTile( pSoldier->sGridNo, pSoldier->bLevel ) ) )
|
|
if ( FindGasMask( pSoldier ) != NO_SLOT || !( GetSmokeEffectOnTile( pSoldier->sGridNo, pSoldier->bLevel ) ) )
|
|
{
|
|
// Turn off gassed flag....
|
|
pSoldier->uiStatusFlags &= (~SOLDIER_GASSED );
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->bBlindedCounter > 0 )
|
|
{
|
|
pSoldier->bBlindedCounter--;
|
|
if (pSoldier->bBlindedCounter == 0)
|
|
{
|
|
// we can SEE!!!!!
|
|
HandleSight( pSoldier, SIGHT_LOOK );
|
|
// Dirty panel
|
|
fInterfacePanelDirty = DIRTYLEVEL2;
|
|
}
|
|
}
|
|
|
|
// ATE: To get around a problem...
|
|
// If an AI guy, and we have 0 life, and are still at higher hieght,
|
|
// Kill them.....
|
|
|
|
|
|
pSoldier->sWeightCarriedAtTurnStart = (INT16) CalculateCarriedWeight( pSoldier );
|
|
|
|
UnusedAPsToBreath( pSoldier );
|
|
|
|
// Set flag back to normal, after reaching a certain statge
|
|
if ( pSoldier->bBreath > 80 )
|
|
{
|
|
pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_LOW_BREATH );
|
|
}
|
|
if ( pSoldier->bBreath > 50 )
|
|
{
|
|
pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_DROWNING );
|
|
}
|
|
|
|
|
|
if ( pSoldier->ubTurnsUntilCanSayHeardNoise > 0)
|
|
{
|
|
pSoldier->ubTurnsUntilCanSayHeardNoise--;
|
|
}
|
|
|
|
if ( pSoldier->bInSector )
|
|
{
|
|
CheckForBreathCollapse( pSoldier );
|
|
}
|
|
|
|
CalcNewActionPoints( pSoldier );
|
|
|
|
pSoldier->bTilesMoved = 0;
|
|
|
|
if ( pSoldier->bInSector )
|
|
{
|
|
BeginSoldierGetup( pSoldier );
|
|
|
|
// CJC Nov 30: handle RT opplist decaying in another function which operates less often
|
|
if ( gTacticalStatus.uiFlags & INCOMBAT )
|
|
{
|
|
VerifyAndDecayOpplist( pSoldier );
|
|
|
|
// turn off xray
|
|
if ( pSoldier->uiXRayActivatedTime )
|
|
{
|
|
TurnOffXRayEffects( pSoldier );
|
|
}
|
|
}
|
|
|
|
if ( (pSoldier->bTeam == gbPlayerNum) && (pSoldier->ubProfile != NO_PROFILE) )
|
|
{
|
|
switch( gMercProfiles[ pSoldier->ubProfile ].bPersonalityTrait )
|
|
{
|
|
case FEAR_OF_INSECTS:
|
|
if ( MercSeesCreature( pSoldier ) )
|
|
{
|
|
HandleMoraleEvent( pSoldier, MORALE_INSECT_PHOBIC_SEES_CREATURE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
|
|
if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_PERSONALITY) )
|
|
{
|
|
TacticalCharacterDialogue( pSoldier, QUOTE_PERSONALITY_TRAIT );
|
|
pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_PERSONALITY;
|
|
}
|
|
}
|
|
break;
|
|
case CLAUSTROPHOBIC:
|
|
if ( gbWorldSectorZ > 0 && Random( 6 - gbWorldSectorZ ) == 0 )
|
|
{
|
|
// underground!
|
|
HandleMoraleEvent( pSoldier, MORALE_CLAUSTROPHOBE_UNDERGROUND, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
|
|
if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_PERSONALITY) )
|
|
{
|
|
TacticalCharacterDialogue( pSoldier, QUOTE_PERSONALITY_TRAIT );
|
|
pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_PERSONALITY;
|
|
}
|
|
|
|
}
|
|
break;
|
|
case NERVOUS:
|
|
if ( DistanceToClosestFriend( pSoldier ) > NERVOUS_RADIUS )
|
|
{
|
|
// augh!!
|
|
if ( pSoldier->bMorale < 50 )
|
|
{
|
|
HandleMoraleEvent( pSoldier, MORALE_NERVOUS_ALONE, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
|
|
if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_PERSONALITY) )
|
|
{
|
|
TacticalCharacterDialogue( pSoldier, QUOTE_PERSONALITY_TRAIT );
|
|
pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_PERSONALITY;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pSoldier->bMorale > 45 )
|
|
{
|
|
// turn flag off, so that we say it every two turns
|
|
pSoldier->usQuoteSaidFlags &= ~SOLDIER_QUOTE_SAID_PERSONALITY;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Reset quote flags for under heavy fire and close call!
|
|
pSoldier->usQuoteSaidFlags &= ( ~SOLDIER_QUOTE_SAID_BEING_PUMMELED );
|
|
pSoldier->usQuoteSaidExtFlags &= ( ~SOLDIER_QUOTE_SAID_EXT_CLOSE_CALL );
|
|
pSoldier->bNumHitsThisTurn = 0;
|
|
pSoldier->ubSuppressionPoints = 0;
|
|
pSoldier->fCloseCall = FALSE;
|
|
|
|
pSoldier->ubMovementNoiseHeard = 0;
|
|
|
|
// If soldier has new APs, reset flags!
|
|
if ( pSoldier->bActionPoints > 0 )
|
|
{
|
|
pSoldier->fUIFirstTimeNOAP = FALSE;
|
|
pSoldier->bMoved = FALSE;
|
|
pSoldier->bPassedLastInterrupt = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// UTILITY FUNCTIONS CALLED BY OVERHEAD.H
|
|
UINT8 gDirectionFrom8to2[ ] = { 0, 0, 1, 1, 0, 1, 1, 0 };
|
|
|
|
|
|
BOOLEAN ConvertAniCodeToAniFrame( SOLDIERTYPE *pSoldier, UINT16 usAniFrame )
|
|
{
|
|
UINT16 usAnimSurface;
|
|
UINT8 ubTempDir;
|
|
// Given ani code, adjust for facing direction
|
|
|
|
// get anim surface and determine # of frames
|
|
usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
|
|
|
|
CHECKF( usAnimSurface != INVALID_ANIMATION_SURFACE );
|
|
|
|
// COnvert world direction into sprite direction
|
|
ubTempDir = gOneCDirection[ pSoldier->bDirection ];
|
|
|
|
//If we are only one frame, ignore what the script is telling us!
|
|
if ( gAnimSurfaceDatabase[ usAnimSurface ].ubFlags & ANIM_DATA_FLAG_NOFRAMES )
|
|
{
|
|
usAniFrame = 0;
|
|
}
|
|
|
|
if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 32 )
|
|
{
|
|
ubTempDir = gExtOneCDirection[ pSoldier->ubHiResDirection ];
|
|
}
|
|
// Check # of directions /surface, adjust if ness.
|
|
else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 4 )
|
|
{
|
|
ubTempDir = ubTempDir / 2;
|
|
}
|
|
// Check # of directions /surface, adjust if ness.
|
|
else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 1 )
|
|
{
|
|
ubTempDir = 0;
|
|
}
|
|
// Check # of directions /surface, adjust if ness.
|
|
else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 3 )
|
|
{
|
|
if ( pSoldier->bDirection == NORTHWEST )
|
|
{
|
|
ubTempDir = 1;
|
|
}
|
|
if ( pSoldier->bDirection == WEST )
|
|
{
|
|
ubTempDir = 0;
|
|
}
|
|
if ( pSoldier->bDirection == EAST )
|
|
{
|
|
ubTempDir = 2;
|
|
}
|
|
}
|
|
else if ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumDirections == 2 )
|
|
{
|
|
ubTempDir = gDirectionFrom8to2[ pSoldier->bDirection ];
|
|
}
|
|
|
|
pSoldier->usAniFrame = usAniFrame + (UINT16) ( ( gAnimSurfaceDatabase[ usAnimSurface ].uiNumFramesPerDir * ubTempDir ) );
|
|
|
|
if ( gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject == NULL )
|
|
{
|
|
pSoldier->usAniFrame = 0;
|
|
return( TRUE );
|
|
}
|
|
|
|
if ( pSoldier->usAniFrame >= gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->usNumberOfObjects )
|
|
{
|
|
// Debug msg here....
|
|
// ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Soldier Animation: Wrong Number of frames per number of objects: %d vs %d, %S", gAnimSurfaceDatabase[ usAnimSurface ].uiNumFramesPerDir, gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->usNumberOfObjects, gAnimControl[ pSoldier->usAnimState ].zAnimStr );
|
|
|
|
pSoldier->usAniFrame = 0;
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
void TurnSoldier( SOLDIERTYPE *pSoldier)
|
|
{
|
|
INT16 sDirection;
|
|
BOOLEAN fDoDirectionChange = TRUE;
|
|
INT32 cnt;
|
|
|
|
// If we are a vehicle... DON'T TURN!
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
|
|
{
|
|
if ( pSoldier->ubBodyType != TANK_NW && pSoldier->ubBodyType != TANK_NE )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
// We handle sight now....
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_LOOK_NEXT_TURNSOLDIER )
|
|
{
|
|
if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STATIONARY && pSoldier->usAnimState != CLIMBUPROOF && pSoldier->usAnimState != CLIMBDOWNROOF ) )
|
|
{
|
|
// HANDLE SIGHT!
|
|
HandleSight( pSoldier,SIGHT_LOOK | SIGHT_RADIO );
|
|
}
|
|
// Turn off!
|
|
pSoldier->uiStatusFlags &= (~SOLDIER_LOOK_NEXT_TURNSOLDIER );
|
|
|
|
HandleSystemNewAISituation( pSoldier, FALSE );
|
|
}
|
|
|
|
|
|
if ( pSoldier->fTurningToShoot )
|
|
{
|
|
if ( pSoldier->bDirection == pSoldier->bDesiredDirection )
|
|
{
|
|
if ( ( (gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY ) && !pSoldier->fTurningFromPronePosition ) || pSoldier->ubBodyType == ROBOTNOWEAPON || pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, gAnimControl[ pSoldier->usAnimState ].ubEndHeight ), 0, FALSE );
|
|
pSoldier->fTurningToShoot = FALSE;
|
|
|
|
// Save last target gridno!
|
|
//pSoldier->sLastTarget = pSoldier->sTargetGridNo;
|
|
|
|
}
|
|
// Else check if we are trying to shoot and once was prone, but am now crouched because we needed to turn...
|
|
else if ( pSoldier->fTurningFromPronePosition )
|
|
{
|
|
if ( IsValidStance( pSoldier, ANIM_PRONE ) )
|
|
{
|
|
SendChangeSoldierStanceEvent( pSoldier, ANIM_PRONE );
|
|
pSoldier->usPendingAnimation = SelectFireAnimation( pSoldier, ANIM_PRONE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, SelectFireAnimation( pSoldier, ANIM_CROUCH ), 0, FALSE );
|
|
}
|
|
pSoldier->fTurningToShoot = FALSE;
|
|
pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->fTurningToFall )
|
|
{
|
|
if ( pSoldier->bDirection == pSoldier->bDesiredDirection )
|
|
{
|
|
SelectFallAnimation( pSoldier );
|
|
pSoldier->fTurningToFall = FALSE;
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->fTurningUntilDone && ( pSoldier->ubPendingStanceChange != NO_PENDING_STANCE ) )
|
|
{
|
|
if ( pSoldier->bDirection == pSoldier->bDesiredDirection )
|
|
{
|
|
SendChangeSoldierStanceEvent( pSoldier, pSoldier->ubPendingStanceChange );
|
|
pSoldier->ubPendingStanceChange = NO_PENDING_STANCE;
|
|
pSoldier->fTurningUntilDone = FALSE;
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->fTurningUntilDone && ( pSoldier->usPendingAnimation != NO_PENDING_ANIMATION ) )
|
|
{
|
|
if ( pSoldier->bDirection == pSoldier->bDesiredDirection )
|
|
{
|
|
UINT16 usPendingAnimation;
|
|
|
|
usPendingAnimation = pSoldier->usPendingAnimation;
|
|
pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, usPendingAnimation, 0 , FALSE );
|
|
pSoldier->fTurningUntilDone = FALSE;
|
|
}
|
|
}
|
|
|
|
// Don't do anything if we are at dest direction!
|
|
if ( pSoldier->bDirection == pSoldier->bDesiredDirection )
|
|
{
|
|
if ( pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE )
|
|
{
|
|
if ( pSoldier->iTuringSoundID != NO_SAMPLE )
|
|
{
|
|
SoundStop( pSoldier->iTuringSoundID );
|
|
pSoldier->iTuringSoundID = NO_SAMPLE;
|
|
|
|
PlaySoldierJA2Sample( pSoldier->ubID, TURRET_STOP, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ), TRUE );
|
|
}
|
|
}
|
|
|
|
// Turn off!
|
|
pSoldier->uiStatusFlags &= (~SOLDIER_LOOK_NEXT_TURNSOLDIER );
|
|
pSoldier->fDontUnsetLastTargetFromTurn = FALSE;
|
|
|
|
// Unset ui busy if from ui
|
|
if ( pSoldier->bTurningFromUI && ( pSoldier->fTurningFromPronePosition != 3 ) && ( pSoldier->fTurningFromPronePosition != 1 ) )
|
|
{
|
|
UnSetUIBusy( pSoldier->ubID );
|
|
pSoldier->bTurningFromUI = FALSE;
|
|
}
|
|
|
|
if ( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE ) || CREATURE_OR_BLOODCAT( pSoldier ) )
|
|
{
|
|
pSoldier->uiStatusFlags &= (~SOLDIER_PAUSEANIMOVE);
|
|
}
|
|
|
|
FreeUpNPCFromTurning( pSoldier, LOOK);
|
|
|
|
// Undo our flag for prone turning...
|
|
// Else check if we are trying to shoot and once was prone, but am now crouched because we needed to turn...
|
|
if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_ON )
|
|
{
|
|
// ATE: Don't do this if we have something in our hands we are going to throw!
|
|
if ( IsValidStance( pSoldier, ANIM_PRONE ) && pSoldier->pTempObject == NULL )
|
|
{
|
|
SendChangeSoldierStanceEvent( pSoldier, ANIM_PRONE );
|
|
}
|
|
pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
|
}
|
|
|
|
// If a special code, make guy crawl after stance change!
|
|
if ( pSoldier->fTurningFromPronePosition == TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE && pSoldier->usAnimState != PRONE_UP && pSoldier->usAnimState != PRONE_DOWN )
|
|
{
|
|
if ( IsValidStance( pSoldier, ANIM_PRONE ) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, CRAWLING, 0, FALSE );
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_TURNINGFROMHIT )
|
|
{
|
|
if ( pSoldier->fGettingHit == 1 )
|
|
{
|
|
if ( pSoldier->usPendingAnimation != FALLFORWARD_ROOF && pSoldier->usPendingAnimation != FALLOFF && pSoldier->usAnimState != FALLFORWARD_ROOF && pSoldier->usAnimState != FALLOFF )
|
|
{
|
|
// Go back to original direction
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, (INT8)pSoldier->uiPendingActionData1 );
|
|
|
|
//SETUP GETTING HIT FLAG TO 2
|
|
pSoldier->fGettingHit = 2;
|
|
}
|
|
else
|
|
{
|
|
pSoldier->uiStatusFlags &= (~SOLDIER_TURNINGFROMHIT );
|
|
}
|
|
}
|
|
else if ( pSoldier->fGettingHit == 2 )
|
|
{
|
|
// Turn off
|
|
pSoldier->uiStatusFlags &= (~SOLDIER_TURNINGFROMHIT );
|
|
|
|
// Release attacker
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Releasesoldierattacker, turning from hit animation ended") );
|
|
ReleaseSoldiersAttacker( pSoldier );
|
|
|
|
//FREEUP GETTING HIT FLAG
|
|
pSoldier->fGettingHit = FALSE;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// IF WE ARE HERE, WE ARE IN THE PROCESS OF TURNING
|
|
|
|
// DOUBLE CHECK TO UNSET fNOAPs...
|
|
if ( pSoldier->fNoAPToFinishMove )
|
|
{
|
|
AdjustNoAPToFinishMove( pSoldier, FALSE );
|
|
}
|
|
|
|
// Do something different for vehicles....
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
|
|
{
|
|
fDoDirectionChange = FALSE;
|
|
|
|
// Get new direction
|
|
/*
|
|
sDirection = pSoldier->ubHiResDirection + ExtQuickestDirection( pSoldier->ubHiResDirection, pSoldier->ubHiResDesiredDirection );
|
|
*/
|
|
sDirection = pSoldier->ubHiResDirection + pSoldier->bTurningIncrement;
|
|
if (sDirection > 31)
|
|
{
|
|
sDirection = 0;
|
|
}
|
|
else
|
|
{
|
|
if ( sDirection < 0 )
|
|
{
|
|
sDirection = 31;
|
|
}
|
|
}
|
|
pSoldier->ubHiResDirection = (UINT8)sDirection;
|
|
|
|
// Are we at a multiple of a 'cardnal' direction?
|
|
for ( cnt = 0; cnt < 8; cnt++ )
|
|
{
|
|
if ( sDirection == ubExtDirection[ cnt ] )
|
|
{
|
|
fDoDirectionChange = TRUE;
|
|
|
|
sDirection = (INT16)cnt;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->ubBodyType == TANK_NW || pSoldier->ubBodyType == TANK_NE )
|
|
{
|
|
if ( pSoldier->iTuringSoundID == NO_SAMPLE )
|
|
{
|
|
pSoldier->iTuringSoundID = PlaySoldierJA2Sample( pSoldier->ubID, TURRET_MOVE, RATE_11025, SoundVolume( HIGHVOLUME, pSoldier->sGridNo ), 100, SoundDir( pSoldier->sGridNo ), TRUE );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get new direction
|
|
//sDirection = pSoldier->bDirection + QuickestDirection( pSoldier->bDirection, pSoldier->bDesiredDirection );
|
|
sDirection = pSoldier->bDirection + pSoldier->bTurningIncrement;
|
|
if (sDirection > 7)
|
|
{
|
|
sDirection = 0;
|
|
}
|
|
else
|
|
{
|
|
if ( sDirection < 0 )
|
|
{
|
|
sDirection = 7;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// CHECK FOR A VALID TURN DIRECTION
|
|
// This is needed for prone animations as well as any multi-tiled structs
|
|
if ( fDoDirectionChange )
|
|
{
|
|
if ( OKToAddMercToWorld( pSoldier, (INT8)sDirection ) )
|
|
{
|
|
// Don't do this if we are walkoing off screen...
|
|
if ( gubWaitingForAllMercsToExitCode == WAIT_FOR_MERCS_TO_WALKOFF_SCREEN || gubWaitingForAllMercsToExitCode == WAIT_FOR_MERCS_TO_WALK_TO_GRIDNO )
|
|
{
|
|
|
|
}
|
|
else
|
|
{
|
|
// ATE: We should only do this if we are STATIONARY!
|
|
if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_STATIONARY ) )
|
|
{
|
|
pSoldier->uiStatusFlags |= SOLDIER_LOOK_NEXT_TURNSOLDIER;
|
|
}
|
|
// otherwise, it's handled next tile...
|
|
}
|
|
|
|
EVENT_SetSoldierDirection( pSoldier, sDirection );
|
|
|
|
if ( pSoldier->ubBodyType != LARVAE_MONSTER && !MercInWater( pSoldier ) && pSoldier->bOverTerrainType != DIRT_ROAD && pSoldier->bOverTerrainType != PAVED_ROAD )
|
|
{
|
|
PlaySoldierFootstepSound( pSoldier );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Are we prone crawling?
|
|
if ( pSoldier->usAnimState == CRAWLING )
|
|
{
|
|
// OK, we want to getup, turn and go prone again....
|
|
SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
|
|
pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_ENDING_UP_FROM_MOVE;
|
|
}
|
|
// If we are a creature, or multi-tiled, cancel AI action.....?
|
|
else if ( pSoldier->uiStatusFlags & SOLDIER_MULTITILE )
|
|
{
|
|
pSoldier->bDesiredDirection = pSoldier->bDirection;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
UINT8 gRedGlowR[]=
|
|
{
|
|
0, // Normal shades
|
|
25,
|
|
50,
|
|
75,
|
|
100,
|
|
125,
|
|
150,
|
|
175,
|
|
200,
|
|
225,
|
|
|
|
0, // For gray palettes
|
|
25,
|
|
50,
|
|
75,
|
|
100,
|
|
125,
|
|
150,
|
|
175,
|
|
200,
|
|
225,
|
|
|
|
};
|
|
|
|
|
|
#if 0
|
|
UINT8 gOrangeGlowR[]=
|
|
{
|
|
0, // Normal shades
|
|
20,
|
|
40,
|
|
60,
|
|
80,
|
|
100,
|
|
120,
|
|
140,
|
|
160,
|
|
180,
|
|
|
|
0, // For gray palettes
|
|
20,
|
|
40,
|
|
60,
|
|
80,
|
|
100,
|
|
120,
|
|
140,
|
|
160,
|
|
180,
|
|
};
|
|
#endif
|
|
|
|
UINT8 gOrangeGlowR[]=
|
|
{
|
|
0, // Normal shades
|
|
25,
|
|
50,
|
|
75,
|
|
100,
|
|
125,
|
|
150,
|
|
175,
|
|
200,
|
|
225,
|
|
|
|
0, // For gray palettes
|
|
25,
|
|
50,
|
|
75,
|
|
100,
|
|
125,
|
|
150,
|
|
175,
|
|
200,
|
|
225,
|
|
|
|
};
|
|
|
|
|
|
#if 0
|
|
UINT8 gOrangeGlowG[]=
|
|
{
|
|
0, // Normal shades
|
|
5,
|
|
10,
|
|
25,
|
|
30,
|
|
35,
|
|
40,
|
|
45,
|
|
50,
|
|
55,
|
|
|
|
0, // For gray palettes
|
|
5,
|
|
10,
|
|
25,
|
|
30,
|
|
35,
|
|
40,
|
|
45,
|
|
50,
|
|
55,
|
|
};
|
|
#endif
|
|
|
|
UINT8 gOrangeGlowG[]=
|
|
{
|
|
0, // Normal shades
|
|
20,
|
|
40,
|
|
60,
|
|
80,
|
|
100,
|
|
120,
|
|
140,
|
|
160,
|
|
180,
|
|
|
|
0, // For gray palettes
|
|
20,
|
|
40,
|
|
60,
|
|
80,
|
|
100,
|
|
120,
|
|
140,
|
|
160,
|
|
180,
|
|
|
|
};
|
|
|
|
|
|
BOOLEAN CreateSoldierPalettes( SOLDIERTYPE *pSoldier )
|
|
{
|
|
UINT16 usAnimSurface, usPaletteAnimSurface;
|
|
CHAR8 zColFilename[ 100 ];
|
|
INT32 iWhich;
|
|
INT32 cnt;
|
|
INT8 bBodyTypePalette;
|
|
SGPPaletteEntry Temp8BPPPalette[ 256 ];
|
|
|
|
//NT32 uiCount;
|
|
//PPaletteEntry Pal[256];
|
|
|
|
if ( pSoldier->p8BPPPalette != NULL )
|
|
{
|
|
MemFree( pSoldier->p8BPPPalette );
|
|
pSoldier->p8BPPPalette = NULL;
|
|
}
|
|
|
|
|
|
// Allocate mem for new palette
|
|
pSoldier->p8BPPPalette = (SGPPaletteEntry *) MemAlloc( sizeof( SGPPaletteEntry ) * 256 );
|
|
memset( pSoldier->p8BPPPalette, 0, sizeof( SGPPaletteEntry ) * 256 );
|
|
|
|
CHECKF( pSoldier->p8BPPPalette != NULL );
|
|
|
|
// --- TAKE FROM CURRENT ANIMATION HVOBJECT!
|
|
usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
|
|
|
|
CHECKF( usAnimSurface != INVALID_ANIMATION_SURFACE );
|
|
|
|
if ( ( bBodyTypePalette = GetBodyTypePaletteSubstitutionCode( pSoldier, pSoldier->ubBodyType, zColFilename ) ) == -1 )
|
|
{
|
|
// ATE: here we want to use the breath cycle for the palette.....
|
|
usPaletteAnimSurface = LoadSoldierAnimationSurface( pSoldier, STANDING );
|
|
|
|
if ( usPaletteAnimSurface != INVALID_ANIMATION_SURFACE )
|
|
{
|
|
// Use palette from HVOBJECT, then use substitution for pants, etc
|
|
memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[ usPaletteAnimSurface ].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 );
|
|
|
|
// Substitute based on head, etc
|
|
SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->HeadPal );
|
|
SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->VestPal );
|
|
SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->PantsPal );
|
|
SetPaletteReplacement( pSoldier->p8BPPPalette, pSoldier->SkinPal );
|
|
}
|
|
}
|
|
else if ( bBodyTypePalette == 0 )
|
|
{
|
|
// Use palette from hvobject
|
|
memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 );
|
|
}
|
|
else
|
|
{
|
|
// Use col file
|
|
if ( CreateSGPPaletteFromCOLFile( Temp8BPPPalette, zColFilename ) )
|
|
{
|
|
// Copy into palette
|
|
memcpy( pSoldier->p8BPPPalette, Temp8BPPPalette, sizeof( pSoldier->p8BPPPalette ) * 256 );
|
|
}
|
|
else
|
|
{
|
|
// Use palette from hvobject
|
|
memcpy( pSoldier->p8BPPPalette, gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pPaletteEntry, sizeof( pSoldier->p8BPPPalette ) * 256 );
|
|
}
|
|
}
|
|
|
|
|
|
if ( pSoldier->p16BPPPalette != NULL )
|
|
{
|
|
MemFree( pSoldier->p16BPPPalette );
|
|
pSoldier->p16BPPPalette = NULL;
|
|
}
|
|
|
|
// -- BUILD 16BPP Palette from this
|
|
pSoldier->p16BPPPalette = Create16BPPPalette( pSoldier->p8BPPPalette );
|
|
|
|
for ( iWhich = 0; iWhich < NUM_SOLDIER_SHADES; iWhich++ )
|
|
{
|
|
if ( pSoldier->pShades[ iWhich ] != NULL )
|
|
{
|
|
MemFree( pSoldier->pShades[ iWhich ] );
|
|
pSoldier->pShades[ iWhich ] = NULL;
|
|
}
|
|
}
|
|
|
|
for ( iWhich = 0; iWhich < NUM_SOLDIER_EFFECTSHADES; iWhich++ )
|
|
{
|
|
if ( pSoldier->pEffectShades[ iWhich ] != NULL )
|
|
{
|
|
MemFree( pSoldier->pEffectShades[ iWhich ] );
|
|
pSoldier->pEffectShades[ iWhich ] = NULL;
|
|
}
|
|
}
|
|
|
|
for ( iWhich = 0; iWhich < 20; iWhich++ )
|
|
{
|
|
if ( pSoldier->pGlowShades[ iWhich ] != NULL )
|
|
{
|
|
MemFree( pSoldier->pGlowShades[ iWhich ] );
|
|
pSoldier->pGlowShades[ iWhich ] = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
CreateSoldierPaletteTables( pSoldier, HVOBJECT_GLOW_GREEN );
|
|
|
|
|
|
// Build a grayscale palette for testing grayout of mercs
|
|
//for(uiCount=0; uiCount < 256; uiCount++)
|
|
//{
|
|
// Pal[uiCount].peRed=(UINT8)(uiCount%128)+128;
|
|
// Pal[uiCount].peGreen=(UINT8)(uiCount%128)+128;
|
|
// Pal[uiCount].peBlue=(UINT8)(uiCount%128)+128;
|
|
//}
|
|
pSoldier->pEffectShades[ 0 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 100, 100, 100, TRUE );
|
|
pSoldier->pEffectShades[ 1 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 100, 150, 100, TRUE );
|
|
|
|
// Build shades for glowing visible bad guy
|
|
|
|
// First do visible guy
|
|
pSoldier->pGlowShades[ 0 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 255, 255, 255, FALSE );
|
|
for ( cnt = 1; cnt < 10; cnt++ )
|
|
{
|
|
pSoldier->pGlowShades[ cnt ] = CreateEnemyGlow16BPPPalette( pSoldier->p8BPPPalette, gRedGlowR[cnt], 255, FALSE );
|
|
}
|
|
|
|
// Now for gray guy...
|
|
pSoldier->pGlowShades[ 10 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 100, 100, 100, TRUE );
|
|
for ( cnt = 11; cnt < 19; cnt++ )
|
|
{
|
|
pSoldier->pGlowShades[ cnt ] = CreateEnemyGreyGlow16BPPPalette( pSoldier->p8BPPPalette, gRedGlowR[cnt], 0, FALSE );
|
|
}
|
|
pSoldier->pGlowShades[ 19 ] = CreateEnemyGreyGlow16BPPPalette( pSoldier->p8BPPPalette, gRedGlowR[18], 0, FALSE );
|
|
|
|
|
|
// ATE: OK, piggyback on the shades we are not using for 2 colored lighting....
|
|
// ORANGE, VISIBLE GUY
|
|
pSoldier->pShades[ 20 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 255, 255, 255, FALSE );
|
|
for ( cnt = 21; cnt < 30; cnt++ )
|
|
{
|
|
pSoldier->pShades[ cnt ] = CreateEnemyGlow16BPPPalette( pSoldier->p8BPPPalette, gOrangeGlowR[ ( cnt - 20 )], gOrangeGlowG[ ( cnt - 20 ) ], TRUE );
|
|
}
|
|
|
|
// ORANGE, GREY GUY
|
|
pSoldier->pShades[ 30 ] = Create16BPPPaletteShaded( pSoldier->p8BPPPalette, 100, 100, 100, TRUE );
|
|
for ( cnt = 31; cnt < 39; cnt++ )
|
|
{
|
|
pSoldier->pShades[ cnt ] = CreateEnemyGreyGlow16BPPPalette( pSoldier->p8BPPPalette, gOrangeGlowR[ ( cnt - 20 ) ], gOrangeGlowG[ ( cnt - 20 ) ], TRUE );
|
|
}
|
|
pSoldier->pShades[ 39 ] = CreateEnemyGreyGlow16BPPPalette( pSoldier->p8BPPPalette, gOrangeGlowR[18], gOrangeGlowG[18], TRUE );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
|
|
void AdjustAniSpeed( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( ( gTacticalStatus.uiFlags & SLOW_ANIMATION ) )
|
|
{
|
|
if ( gTacticalStatus.bRealtimeSpeed == -1 )
|
|
{
|
|
pSoldier->sAniDelay = 10000;
|
|
}
|
|
else
|
|
{
|
|
pSoldier->sAniDelay = pSoldier->sAniDelay * ( 1 * gTacticalStatus.bRealtimeSpeed / 2 );
|
|
}
|
|
}
|
|
|
|
|
|
RESETTIMECOUNTER( pSoldier->UpdateCounter, pSoldier->sAniDelay );
|
|
}
|
|
|
|
|
|
void CalculateSoldierAniSpeed( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pStatsSoldier )
|
|
{
|
|
UINT32 uiTerrainDelay;
|
|
UINT32 uiSpeed = 0;
|
|
|
|
INT8 bBreathDef, bLifeDef, bAgilDef;
|
|
INT8 bAdditional = 0;
|
|
|
|
// for those animations which have a speed of zero, we have to calculate it
|
|
// here. Some animation, such as water-movement, have an ADDITIONAL speed
|
|
switch( pSoldier->usAnimState )
|
|
{
|
|
// Lesh: bursting animation delay control begins
|
|
// Add your animation ID to control it
|
|
case STANDING_BURST:
|
|
case FIRE_STAND_BURST_SPREAD:
|
|
case FIRE_BURST_LOW_STAND:
|
|
case TANK_BURST:
|
|
case CROUCHED_BURST:
|
|
case PRONE_BURST:
|
|
pSoldier->sAniDelay = Weapon[Item[pSoldier->inv[HANDPOS].usItem].ubClassIndex].sAniDelay;
|
|
AdjustAniSpeed( pSoldier );
|
|
return;
|
|
// Lesh: end
|
|
|
|
case PRONE:
|
|
case STANDING:
|
|
|
|
pSoldier->sAniDelay = ( pStatsSoldier->bBreath * 2 ) + (100 - pStatsSoldier->bLife );
|
|
|
|
// Limit it!
|
|
if ( pSoldier->sAniDelay < 40 )
|
|
{
|
|
pSoldier->sAniDelay = 40;
|
|
}
|
|
AdjustAniSpeed( pSoldier );
|
|
return;
|
|
|
|
case CROUCHING:
|
|
|
|
pSoldier->sAniDelay = ( pStatsSoldier->bBreath * 2 ) + ( (100 - pStatsSoldier->bLife ) );
|
|
|
|
// Limit it!
|
|
if ( pSoldier->sAniDelay < 40 )
|
|
{
|
|
pSoldier->sAniDelay = 40;
|
|
}
|
|
AdjustAniSpeed( pSoldier );
|
|
return;
|
|
|
|
case WALKING:
|
|
|
|
// Adjust based on body type
|
|
bAdditional = (UINT8)( gubAnimWalkSpeeds[ pStatsSoldier->ubBodyType ].sSpeed );
|
|
if ( bAdditional < 0 )
|
|
bAdditional = 0;
|
|
break;
|
|
|
|
case RUNNING:
|
|
|
|
// Adjust based on body type
|
|
bAdditional = (UINT8)gubAnimRunSpeeds[ pStatsSoldier->ubBodyType ].sSpeed;
|
|
if ( bAdditional < 0 )
|
|
bAdditional = 0;
|
|
break;
|
|
|
|
case SWATTING:
|
|
|
|
// Adjust based on body type
|
|
if ( pStatsSoldier->ubBodyType <= REGFEMALE )
|
|
{
|
|
bAdditional = (UINT8)gubAnimSwatSpeeds[ pStatsSoldier->ubBodyType ].sSpeed;
|
|
if ( bAdditional < 0 )
|
|
bAdditional = 0;
|
|
}
|
|
break;
|
|
|
|
case CRAWLING:
|
|
|
|
// Adjust based on body type
|
|
if ( pStatsSoldier->ubBodyType <= REGFEMALE )
|
|
{
|
|
bAdditional = (UINT8)gubAnimCrawlSpeeds[ pStatsSoldier->ubBodyType ].sSpeed;
|
|
if ( bAdditional < 0 )
|
|
bAdditional = 0;
|
|
}
|
|
break;
|
|
|
|
case READY_RIFLE_STAND:
|
|
|
|
// Raise rifle based on aim vs non-aim.
|
|
if ( pSoldier->bAimTime == 0 )
|
|
{
|
|
// Quick shot
|
|
pSoldier->sAniDelay = 70;
|
|
}
|
|
else
|
|
{
|
|
pSoldier->sAniDelay = 150;
|
|
}
|
|
AdjustAniSpeed( pSoldier );
|
|
return;
|
|
}
|
|
|
|
|
|
// figure out movement speed (terrspeed)
|
|
if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING )
|
|
{
|
|
uiSpeed = gsTerrainTypeSpeedModifiers[ pStatsSoldier->bOverTerrainType ];
|
|
|
|
uiTerrainDelay = uiSpeed;
|
|
}
|
|
else
|
|
{
|
|
uiTerrainDelay = 40; // standing still
|
|
}
|
|
|
|
bBreathDef = 50 - ( pStatsSoldier->bBreath / 2 );
|
|
|
|
if ( bBreathDef > 30 )
|
|
bBreathDef = 30;
|
|
|
|
bAgilDef = 50 - ( EffectiveAgility( pStatsSoldier ) / 4 );
|
|
bLifeDef = 50 - ( pStatsSoldier->bLife / 2 );
|
|
|
|
uiTerrainDelay += ( bLifeDef + bBreathDef + bAgilDef + bAdditional );
|
|
|
|
pSoldier->sAniDelay = (INT16)uiTerrainDelay;
|
|
|
|
// If a moving animation and w/re on drugs, increase speed....
|
|
if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING )
|
|
{
|
|
if ( GetDrugEffect( pSoldier, DRUG_TYPE_ADRENALINE ) )
|
|
{
|
|
pSoldier->sAniDelay = pSoldier->sAniDelay / 2;
|
|
}
|
|
}
|
|
|
|
// MODIFTY NOW BASED ON REAL-TIME, ETC
|
|
// Adjust speed, make twice as fast if in turn-based!
|
|
if ( gTacticalStatus.uiFlags & TURNBASED && ( gTacticalStatus.uiFlags & INCOMBAT ) )
|
|
{
|
|
pSoldier->sAniDelay = pSoldier->sAniDelay / 2;
|
|
}
|
|
|
|
// MODIFY IF REALTIME COMBAT
|
|
if ( !( gTacticalStatus.uiFlags & INCOMBAT ) )
|
|
{
|
|
// ATE: If realtime, and stealth mode...
|
|
if ( pStatsSoldier->bStealthMode )
|
|
{
|
|
pSoldier->sAniDelay = (INT16)( pSoldier->sAniDelay * 2 );
|
|
}
|
|
|
|
//pSoldier->sAniDelay = pSoldier->sAniDelay * ( 1 * gTacticalStatus.bRealtimeSpeed / 2 );
|
|
}
|
|
|
|
}
|
|
|
|
UINT8 GetSpeedUpFactor( )
|
|
{
|
|
switch( gTacticalStatus.ubCurrentTeam )
|
|
{
|
|
case OUR_TEAM:
|
|
return gGameExternalOptions.gubPlayerTurnSpeedUpFactor;
|
|
case ENEMY_TEAM:
|
|
return gGameExternalOptions.gubEnemyTurnSpeedUpFactor;
|
|
case CREATURE_TEAM:
|
|
return gGameExternalOptions.gubCreatureTurnSpeedUpFactor;
|
|
case MILITIA_TEAM:
|
|
return gGameExternalOptions.gubMilitiaTurnSpeedUpFactor;
|
|
case CIV_TEAM:
|
|
return gGameExternalOptions.gubCivilianTurnSpeedUpFactor;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void SetSoldierAniSpeed( SOLDIERTYPE *pSoldier )
|
|
{
|
|
SOLDIERTYPE *pStatsSoldier;
|
|
|
|
|
|
// ATE: If we are an enemy and are not visible......
|
|
// Set speed to 0
|
|
if ( ( gTacticalStatus.uiFlags & TURNBASED && ( gTacticalStatus.uiFlags & INCOMBAT ) ) || gTacticalStatus.fAutoBandageMode )
|
|
{
|
|
if ( ( ( pSoldier->bVisible == -1 && pSoldier->bVisible == pSoldier->bLastRenderVisibleValue ) || gTacticalStatus.fAutoBandageMode ) && pSoldier->usAnimState != MONSTER_UP )
|
|
{
|
|
if ( pSoldier->bDoBurst && !PTR_OURTEAM )
|
|
{
|
|
pSoldier->sAniDelay = 50;
|
|
}
|
|
else
|
|
{
|
|
pSoldier->sAniDelay = 0;
|
|
}
|
|
RESETTIMECOUNTER( pSoldier->UpdateCounter, pSoldier->sAniDelay );
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Default stats soldier to same as normal soldier.....
|
|
pStatsSoldier = pSoldier;
|
|
|
|
if ( pSoldier->fUseMoverrideMoveSpeed )
|
|
{
|
|
pStatsSoldier = MercPtrs[ pSoldier->bOverrideMoveSpeed ];
|
|
}
|
|
|
|
// Only calculate if set to zero
|
|
if ( ( pSoldier->sAniDelay = gAnimControl[ pSoldier->usAnimState ].sSpeed ) == 0 )
|
|
{
|
|
CalculateSoldierAniSpeed( pSoldier, pStatsSoldier );
|
|
}
|
|
|
|
AdjustAniSpeed( pSoldier );
|
|
|
|
if ( _KeyDown( SPACE ) )
|
|
{
|
|
//pSoldier->sAniDelay = 1000;
|
|
}
|
|
|
|
if ( gTacticalStatus.uiFlags & TURNBASED && gTacticalStatus.uiFlags & INCOMBAT )
|
|
if( GetSpeedUpFactor() )
|
|
pSoldier->sAniDelay /= GetSpeedUpFactor();
|
|
else
|
|
pSoldier->sAniDelay = 0;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////
|
|
//PALETTE REPLACEMENT FUNCTIONS
|
|
///////////////////////////////////////////////////////
|
|
BOOLEAN LoadPaletteData( )
|
|
{
|
|
HWFILE hFile;
|
|
UINT32 cnt, cnt2;
|
|
|
|
hFile = FileOpen( PALETTEFILENAME, FILE_ACCESS_READ, FALSE );
|
|
|
|
// Read # of types
|
|
if ( !FileRead( hFile, &guiNumPaletteSubRanges, sizeof( guiNumPaletteSubRanges ), (UINT32 *)NULL ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Malloc!
|
|
gpPaletteSubRanges = (PaletteSubRangeType *) MemAlloc( sizeof( PaletteSubRangeType ) * guiNumPaletteSubRanges );
|
|
gubpNumReplacementsPerRange = (UINT8 *) MemAlloc( sizeof( UINT8 ) * guiNumPaletteSubRanges );
|
|
|
|
// Read # of types for each!
|
|
for ( cnt = 0; cnt < guiNumPaletteSubRanges; cnt++ )
|
|
{
|
|
if ( !FileRead( hFile, &gubpNumReplacementsPerRange[ cnt ], sizeof( UINT8 ), (UINT32 *)NULL ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
// Loop for each one, read in data
|
|
for ( cnt = 0; cnt < guiNumPaletteSubRanges; cnt++ )
|
|
{
|
|
if ( !FileRead( hFile, &gpPaletteSubRanges[ cnt ].ubStart, sizeof( UINT8 ), (UINT32 *)NULL ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
if ( !FileRead( hFile, &gpPaletteSubRanges[ cnt ].ubEnd, sizeof( UINT8 ), (UINT32 *)NULL ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
|
|
// Read # of palettes
|
|
if ( !FileRead( hFile, &guiNumReplacements, sizeof( guiNumReplacements ), (UINT32 *)NULL ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Malloc!
|
|
gpPalRep = (PaletteReplacementType *) MemAlloc( sizeof( PaletteReplacementType ) * guiNumReplacements );
|
|
|
|
// Read!
|
|
for ( cnt = 0; cnt < guiNumReplacements; cnt++ )
|
|
{
|
|
// type
|
|
if ( !FileRead( hFile, &gpPalRep[ cnt ].ubType, sizeof( gpPalRep[ cnt ].ubType ), (UINT32 *)NULL ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( !FileRead( hFile, &gpPalRep[ cnt ].ID, sizeof( gpPalRep[ cnt ].ID ), (UINT32 *)NULL ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// # entries
|
|
if ( !FileRead( hFile, &gpPalRep[ cnt ].ubPaletteSize, sizeof( gpPalRep[ cnt ].ubPaletteSize ), (UINT32 *)NULL ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Malloc
|
|
gpPalRep[ cnt ].r = (UINT8 *) MemAlloc( gpPalRep[ cnt ].ubPaletteSize );
|
|
CHECKF( gpPalRep[ cnt ].r != NULL );
|
|
gpPalRep[ cnt ].g = (UINT8 *) MemAlloc( gpPalRep[ cnt ].ubPaletteSize );
|
|
CHECKF( gpPalRep[ cnt ].g != NULL );
|
|
gpPalRep[ cnt ].b = (UINT8 *) MemAlloc( gpPalRep[ cnt ].ubPaletteSize );
|
|
CHECKF( gpPalRep[ cnt ].b != NULL );
|
|
|
|
for( cnt2 = 0; cnt2 < gpPalRep[ cnt ].ubPaletteSize; cnt2++ )
|
|
{
|
|
if ( !FileRead( hFile, &gpPalRep[ cnt ].r[ cnt2 ], sizeof( UINT8 ), (UINT32 *)NULL ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
if ( !FileRead( hFile, &gpPalRep[ cnt ].g[ cnt2 ], sizeof( UINT8 ), (UINT32 *)NULL ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
if ( !FileRead( hFile, &gpPalRep[ cnt ].b[ cnt2 ], sizeof( UINT8 ), (UINT32 *)NULL ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
FileClose( hFile );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOLEAN SetPaletteReplacement( SGPPaletteEntry *p8BPPPalette, PaletteRepID aPalRep )
|
|
{
|
|
UINT32 cnt2;
|
|
UINT8 ubType;
|
|
UINT8 ubPalIndex;
|
|
|
|
CHECKF( GetPaletteRepIndexFromID( aPalRep, &ubPalIndex ) );
|
|
|
|
// Get range type
|
|
ubType = gpPalRep[ ubPalIndex ].ubType;
|
|
|
|
for ( cnt2 = gpPaletteSubRanges[ ubType ].ubStart; cnt2 <= gpPaletteSubRanges[ ubType ].ubEnd; cnt2++ )
|
|
{
|
|
p8BPPPalette[ cnt2 ].peRed = gpPalRep[ ubPalIndex ].r[ cnt2 - gpPaletteSubRanges[ ubType ].ubStart ];
|
|
p8BPPPalette[ cnt2 ].peGreen = gpPalRep[ ubPalIndex ].g[ cnt2 - gpPaletteSubRanges[ ubType ].ubStart ];
|
|
p8BPPPalette[ cnt2 ].peBlue = gpPalRep[ ubPalIndex ].b[ cnt2 - gpPaletteSubRanges[ ubType ].ubStart ];
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN DeletePaletteData( )
|
|
{
|
|
UINT32 cnt;
|
|
|
|
// Free!
|
|
if ( gpPaletteSubRanges != NULL )
|
|
{
|
|
MemFree( gpPaletteSubRanges );
|
|
gpPaletteSubRanges = NULL;
|
|
}
|
|
|
|
if ( gubpNumReplacementsPerRange != NULL )
|
|
{
|
|
MemFree( gubpNumReplacementsPerRange );
|
|
gubpNumReplacementsPerRange = NULL;
|
|
}
|
|
|
|
|
|
for ( cnt = 0; cnt < guiNumReplacements; cnt++ )
|
|
{
|
|
// Free
|
|
if ( gpPalRep[ cnt ].r != NULL )
|
|
{
|
|
MemFree( gpPalRep[ cnt ].r );
|
|
gpPalRep[ cnt ].r = NULL;
|
|
}
|
|
if ( gpPalRep[ cnt ].g != NULL )
|
|
{
|
|
MemFree( gpPalRep[ cnt ].g );
|
|
gpPalRep[ cnt ].g = NULL;
|
|
}
|
|
if ( gpPalRep[ cnt ].b != NULL )
|
|
{
|
|
MemFree( gpPalRep[ cnt ].b );
|
|
gpPalRep[ cnt ].b = NULL;
|
|
}
|
|
}
|
|
|
|
// Free
|
|
if ( gpPalRep != NULL )
|
|
{
|
|
MemFree( gpPalRep );
|
|
gpPalRep = NULL;
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN GetPaletteRepIndexFromID( PaletteRepID aPalRep, UINT8 *pubPalIndex )
|
|
{
|
|
UINT32 cnt;
|
|
|
|
// Check if type exists
|
|
for ( cnt = 0; cnt < guiNumReplacements; cnt++ )
|
|
{
|
|
if ( COMPARE_PALETTEREP_ID( aPalRep, gpPalRep[ cnt ].ID ) )
|
|
{
|
|
*pubPalIndex = ( UINT8 )cnt;
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, "Invalid Palette Replacement ID given");
|
|
return( FALSE );
|
|
}
|
|
|
|
UINT16 GetNewSoldierStateFromNewStance( SOLDIERTYPE *pSoldier, UINT8 ubDesiredStance )
|
|
{
|
|
UINT16 usNewState;
|
|
INT8 bCurrentHeight;
|
|
|
|
bCurrentHeight = ( ubDesiredStance - gAnimControl[ pSoldier->usAnimState ].ubEndHeight );
|
|
|
|
// Now change to appropriate animation
|
|
|
|
switch( bCurrentHeight )
|
|
{
|
|
case ANIM_STAND - ANIM_CROUCH:
|
|
usNewState = KNEEL_UP;
|
|
break;
|
|
case ANIM_CROUCH - ANIM_STAND:
|
|
usNewState = KNEEL_DOWN;
|
|
break;
|
|
|
|
case ANIM_STAND - ANIM_PRONE:
|
|
usNewState = PRONE_UP;
|
|
break;
|
|
case ANIM_PRONE - ANIM_STAND:
|
|
usNewState = KNEEL_DOWN;
|
|
break;
|
|
|
|
case ANIM_CROUCH - ANIM_PRONE:
|
|
usNewState = PRONE_UP;
|
|
break;
|
|
case ANIM_PRONE - ANIM_CROUCH:
|
|
usNewState = PRONE_DOWN;
|
|
break;
|
|
|
|
default:
|
|
|
|
// Cannot get here unless ub desired stance is bogus
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "GetNewSoldierStateFromNewStance bogus ubDesiredStance value %d", ubDesiredStance ) );
|
|
usNewState = pSoldier->usAnimState;
|
|
|
|
}
|
|
|
|
return( usNewState );
|
|
}
|
|
|
|
|
|
void MoveMercFacingDirection( SOLDIERTYPE *pSoldier, BOOLEAN fReverse, FLOAT dMovementDist )
|
|
{
|
|
FLOAT dAngle = (FLOAT)0;
|
|
|
|
// Determine which direction we are in
|
|
switch( pSoldier->bDirection )
|
|
{
|
|
case NORTH:
|
|
dAngle = (FLOAT)( -1 * PI );
|
|
break;
|
|
|
|
case NORTHEAST:
|
|
dAngle = (FLOAT)( PI * .75 );
|
|
break;
|
|
|
|
case EAST:
|
|
dAngle = (FLOAT)( PI / 2 );
|
|
break;
|
|
|
|
case SOUTHEAST:
|
|
dAngle = (FLOAT)( PI / 4 );
|
|
break;
|
|
|
|
case SOUTH:
|
|
dAngle = (FLOAT)0;
|
|
break;
|
|
|
|
case SOUTHWEST:
|
|
//dAngle = (FLOAT)( PI * -.25 );
|
|
dAngle = (FLOAT)-0.786;
|
|
break;
|
|
|
|
case WEST:
|
|
dAngle = (FLOAT) ( PI *-.5 );
|
|
break;
|
|
|
|
case NORTHWEST:
|
|
dAngle = (FLOAT) ( PI * -.75 );
|
|
break;
|
|
|
|
}
|
|
|
|
if ( fReverse )
|
|
{
|
|
dMovementDist = dMovementDist * -1;
|
|
}
|
|
|
|
MoveMerc( pSoldier, dMovementDist, dAngle, FALSE );
|
|
|
|
}
|
|
|
|
void BeginSoldierClimbUpRoof( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT8 bNewDirection;
|
|
UINT8 ubWhoIsThere;
|
|
|
|
|
|
if ( FindHeigherLevel( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bNewDirection ) && ( pSoldier->bLevel == 0 ) )
|
|
{
|
|
if ( EnoughPoints( pSoldier, GetAPsToClimbRoof( pSoldier, FALSE ), 0, TRUE ) )
|
|
{
|
|
//Kaiden: Helps if we look where we are going before we try to climb on top of someone
|
|
ubWhoIsThere = WhoIsThere2( NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bNewDirection ) ), 1 );
|
|
if ( ubWhoIsThere != NOBODY && ubWhoIsThere != pSoldier->ubID )
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
|
|
if (pSoldier->bTeam == gbPlayerNum)
|
|
{
|
|
// OK, SET INTERFACE FIRST
|
|
SetUIBusy( pSoldier->ubID );
|
|
}
|
|
|
|
pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bNewDirection ) );
|
|
|
|
pSoldier->ubPendingDirection = bNewDirection;
|
|
//pSoldier->usPendingAnimation = CLIMBUPROOF;
|
|
EVENT_InitNewSoldierAnim( pSoldier, CLIMBUPROOF, 0 , FALSE );
|
|
|
|
InternalReceivingSoldierCancelServices( pSoldier, FALSE );
|
|
InternalGivingSoldierCancelServices( pSoldier, FALSE );
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void BeginSoldierClimbFence( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT8 bDirection;
|
|
|
|
if ( FindFenceJumpDirection( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bDirection ) )
|
|
{
|
|
pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bDirection ) );
|
|
pSoldier->fDontChargeTurningAPs = TRUE;
|
|
EVENT_InternalSetSoldierDesiredDirection( pSoldier, bDirection, FALSE, pSoldier->usAnimState );
|
|
pSoldier->fTurningUntilDone = TRUE;
|
|
// ATE: Reset flag to go back to prone...
|
|
pSoldier->fTurningFromPronePosition = TURNING_FROM_PRONE_OFF;
|
|
pSoldier->usPendingAnimation = HOPFENCE;
|
|
}
|
|
|
|
}
|
|
|
|
UINT32 SleepDartSuccumbChance( SOLDIERTYPE * pSoldier )
|
|
{
|
|
UINT32 uiChance;
|
|
INT8 bEffectiveStrength;
|
|
|
|
// figure out base chance of succumbing,
|
|
bEffectiveStrength = EffectiveStrength( pSoldier );
|
|
|
|
if (bEffectiveStrength > 90)
|
|
{
|
|
uiChance = 110 - bEffectiveStrength;
|
|
}
|
|
else if (bEffectiveStrength > 80)
|
|
{
|
|
uiChance = 120 - bEffectiveStrength;
|
|
}
|
|
else if (bEffectiveStrength > 70)
|
|
{
|
|
uiChance = 130 - bEffectiveStrength;
|
|
}
|
|
else
|
|
{
|
|
uiChance = 140 - bEffectiveStrength;
|
|
}
|
|
|
|
// add in a bonus based on how long it's been since shot... highest chance at the beginning
|
|
uiChance += (10 - pSoldier->bSleepDrugCounter);
|
|
|
|
return( uiChance );
|
|
}
|
|
|
|
void BeginSoldierGetup( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// RETURN IF WE ARE BEING SERVICED
|
|
if ( pSoldier->ubServiceCount > 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// ATE: Don't getup if we are in a meanwhile
|
|
if ( AreInMeanwhile( ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( pSoldier->bCollapsed )
|
|
{
|
|
if ( pSoldier->bLife >= OKLIFE && pSoldier->bBreath >= OKBREATH && (pSoldier->bSleepDrugCounter == 0) )
|
|
{
|
|
// get up you hoser!
|
|
|
|
pSoldier->bCollapsed = FALSE;
|
|
pSoldier->bTurnsCollapsed = 0;
|
|
|
|
if ( IS_MERC_BODY_TYPE( pSoldier ) )
|
|
{
|
|
switch( pSoldier->usAnimState )
|
|
{
|
|
case FALLOFF_FORWARD_STOP:
|
|
case PRONE_LAYFROMHIT_STOP:
|
|
case STAND_FALLFORWARD_STOP:
|
|
ChangeSoldierStance( pSoldier, ANIM_CROUCH );
|
|
break;
|
|
|
|
case FALLBACKHIT_STOP:
|
|
case FALLOFF_STOP:
|
|
case FLYBACKHIT_STOP:
|
|
case FALLBACK_HIT_STAND:
|
|
case FALLOFF:
|
|
case FLYBACK_HIT:
|
|
|
|
// ROLL OVER
|
|
EVENT_InitNewSoldierAnim( pSoldier, ROLLOVER, 0 , FALSE );
|
|
break;
|
|
|
|
default:
|
|
|
|
ChangeSoldierStance( pSoldier, ANIM_CROUCH );
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, END_COWER, 0 , FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pSoldier->bTurnsCollapsed++;
|
|
if ( (gTacticalStatus.bBoxingState == BOXING) && (pSoldier->uiStatusFlags & SOLDIER_BOXER) )
|
|
{
|
|
if (pSoldier->bTurnsCollapsed > 1)
|
|
{
|
|
// We have a winnah! But it isn't this boxer!
|
|
EndBoxingMatch( pSoldier );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( pSoldier->bSleepDrugCounter > 0 )
|
|
{
|
|
UINT32 uiChance;
|
|
|
|
uiChance = SleepDartSuccumbChance( pSoldier );
|
|
|
|
if ( PreRandom( 100 ) < uiChance )
|
|
{
|
|
// succumb to the drug!
|
|
DeductPoints( pSoldier, 0, (INT16)( pSoldier->bBreathMax * 100 ) );
|
|
SoldierCollapse( pSoldier );
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->bSleepDrugCounter > 0 )
|
|
{
|
|
pSoldier->bSleepDrugCounter--;
|
|
}
|
|
}
|
|
|
|
|
|
void HandleTakeDamageDeath( SOLDIERTYPE *pSoldier, UINT8 bOldLife, UINT8 ubReason )
|
|
{
|
|
switch( ubReason )
|
|
{
|
|
case TAKE_DAMAGE_BLOODLOSS:
|
|
case TAKE_DAMAGE_ELECTRICITY:
|
|
case TAKE_DAMAGE_GAS:
|
|
|
|
if ( pSoldier->bInSector )
|
|
{
|
|
if ( pSoldier->bVisible != -1 )
|
|
{
|
|
if ( ubReason != TAKE_DAMAGE_BLOODLOSS )
|
|
{
|
|
DoMercBattleSound( pSoldier, BATTLE_SOUND_DIE1 );
|
|
pSoldier->fDeadSoundPlayed = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( ( ubReason == TAKE_DAMAGE_ELECTRICITY ) && pSoldier->bLife < OKLIFE )
|
|
{
|
|
pSoldier->fInNonintAnim = FALSE;
|
|
}
|
|
|
|
// Check for < OKLIFE
|
|
if ( pSoldier->bLife < OKLIFE && pSoldier->bLife != 0 && !pSoldier->bCollapsed)
|
|
{
|
|
SoldierCollapse( pSoldier );
|
|
}
|
|
|
|
// THis is for the die animation that will be happening....
|
|
if ( pSoldier->bLife == 0 )
|
|
{
|
|
pSoldier->fDoingExternalDeath = TRUE;
|
|
}
|
|
|
|
// Check if he is dead....
|
|
CheckForAndHandleSoldierDyingNotFromHit( pSoldier );
|
|
|
|
}
|
|
|
|
//if( !( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) )
|
|
{
|
|
HandleSoldierTakeDamageFeedback( pSoldier );
|
|
}
|
|
|
|
if(( guiTacticalInterfaceFlags & INTERFACE_MAPSCREEN ) || !pSoldier->bInSector )
|
|
{
|
|
if ( pSoldier->bLife == 0 && !( pSoldier->uiStatusFlags & SOLDIER_DEAD ) )
|
|
{
|
|
StrategicHandlePlayerTeamMercDeath( pSoldier );
|
|
|
|
// ATE: Here, force always to use die sound...
|
|
pSoldier->fDieSoundUsed = FALSE;
|
|
DoMercBattleSound( pSoldier, BATTLE_SOUND_DIE1 );
|
|
pSoldier->fDeadSoundPlayed = TRUE;
|
|
|
|
// ATE: DO death sound
|
|
PlayJA2Sample( (UINT8)DOORCR_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
PlayJA2Sample( (UINT8)HEADCR_1, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if ( ubReason == TAKE_DAMAGE_ELECTRICITY )
|
|
{
|
|
if ( pSoldier->bLife >= OKLIFE )
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Freeing up attacker from electricity damage") );
|
|
ReleaseSoldiersAttacker( pSoldier );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
UINT8 SoldierTakeDamage( SOLDIERTYPE *pSoldier, INT8 bHeight, INT16 sLifeDeduct, INT16 sBreathLoss, UINT8 ubReason, UINT8 ubAttacker, INT16 sSourceGrid, INT16 sSubsequent, BOOLEAN fShowDamage )
|
|
{
|
|
INT8 bOldLife;
|
|
UINT8 ubCombinedLoss;
|
|
INT8 bBandage;
|
|
INT16 sAPCost;
|
|
UINT8 ubBlood;
|
|
UINT16 usItemFlags=0; // Kaiden: Needed for the reveal all items after combat code from UB.
|
|
|
|
pSoldier->ubLastDamageReason = ubReason;
|
|
|
|
|
|
// CJC Jan 21 99: add check to see if we are hurting an enemy in an enemy-controlled
|
|
// sector; if so, this is a sign of player activity
|
|
switch ( pSoldier->bTeam )
|
|
{
|
|
case ENEMY_TEAM:
|
|
// if we're in the wilderness this always counts
|
|
if ( StrategicMap[ CALCULATE_STRATEGIC_INDEX( gWorldSectorX, gWorldSectorY ) ].fEnemyControlled || SectorInfo[ SECTOR( gWorldSectorX, gWorldSectorY ) ].ubTraversability[ THROUGH_STRATEGIC_MOVE ] != TOWN )
|
|
{
|
|
// update current day of activity!
|
|
UpdateLastDayOfPlayerActivity( (UINT16) GetWorldDay() );
|
|
}
|
|
break;
|
|
case CREATURE_TEAM:
|
|
// always a sign of activity?
|
|
UpdateLastDayOfPlayerActivity( (UINT16) GetWorldDay() );
|
|
break;
|
|
case CIV_TEAM:
|
|
if ( pSoldier->ubCivilianGroup == KINGPIN_CIV_GROUP && gubQuest[ QUEST_RESCUE_MARIA ] == QUESTINPROGRESS && gTacticalStatus.bBoxingState == NOT_BOXING )
|
|
{
|
|
SOLDIERTYPE * pMaria = FindSoldierByProfileID( MARIA, FALSE );
|
|
if ( pMaria && pMaria->bActive && pMaria->bInSector )
|
|
{
|
|
SetFactTrue( FACT_MARIA_ESCAPE_NOTICED );
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Deduct life!, Show damage if we want!
|
|
bOldLife = pSoldier->bLife;
|
|
|
|
// OK, If we are a vehicle.... damage vehicle...( people inside... )
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
|
|
{
|
|
if ( TANK( pSoldier ) )
|
|
{
|
|
//sLifeDeduct = (sLifeDeduct * 2) / 3;
|
|
}
|
|
else
|
|
{
|
|
if ( ubReason == TAKE_DAMAGE_GUNFIRE )
|
|
{
|
|
sLifeDeduct /= 3;
|
|
}
|
|
else if ( ubReason == TAKE_DAMAGE_EXPLOSION && sLifeDeduct > 50 )
|
|
{
|
|
// boom!
|
|
sLifeDeduct *= 2;
|
|
}
|
|
}
|
|
|
|
VehicleTakeDamage( pSoldier->bVehicleID, ubReason, sLifeDeduct, pSoldier->sGridNo, ubAttacker );
|
|
HandleTakeDamageDeath( pSoldier, bOldLife, ubReason );
|
|
return( 0 );
|
|
}
|
|
|
|
// ATE: If we are elloit being attacked in a meanwhile...
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_NPC_SHOOTING )
|
|
{
|
|
// Almost kill but not quite.....
|
|
sLifeDeduct = ( pSoldier->bLife - 1 );
|
|
// Turn off
|
|
pSoldier->uiStatusFlags &= ( ~SOLDIER_NPC_SHOOTING );
|
|
}
|
|
|
|
// CJC: make sure Elliot doesn't bleed to death!
|
|
if ( ubReason == TAKE_DAMAGE_BLOODLOSS && AreInMeanwhile() )
|
|
{
|
|
return( 0 );
|
|
}
|
|
|
|
|
|
// Calculate bandage
|
|
bBandage = pSoldier->bLifeMax - pSoldier->bLife - pSoldier->bBleeding;
|
|
|
|
if( guiCurrentScreen == MAP_SCREEN )
|
|
{
|
|
fReDrawFace = TRUE;
|
|
}
|
|
|
|
if ( CREATURE_OR_BLOODCAT( pSoldier ) )
|
|
{
|
|
INT16 sReductionFactor = 0;
|
|
|
|
if ( pSoldier->ubBodyType == BLOODCAT )
|
|
{
|
|
sReductionFactor = 2;
|
|
}
|
|
else if (pSoldier->uiStatusFlags & SOLDIER_MONSTER)
|
|
{
|
|
switch( pSoldier->ubBodyType )
|
|
{
|
|
case LARVAE_MONSTER:
|
|
case INFANT_MONSTER:
|
|
sReductionFactor = 1;
|
|
break;
|
|
case YAF_MONSTER:
|
|
case YAM_MONSTER:
|
|
sReductionFactor = 4;
|
|
break;
|
|
case ADULTFEMALEMONSTER:
|
|
case AM_MONSTER:
|
|
sReductionFactor = 6;
|
|
break;
|
|
case QUEENMONSTER:
|
|
// increase with range!
|
|
if ( ubAttacker == NOBODY )
|
|
{
|
|
sReductionFactor = 8;
|
|
}
|
|
else
|
|
{
|
|
sReductionFactor = 4 + PythSpacesAway( MercPtrs[ ubAttacker ]->sGridNo, pSoldier->sGridNo ) / 2;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ubReason == TAKE_DAMAGE_EXPLOSION )
|
|
{
|
|
sReductionFactor /= 4;
|
|
}
|
|
if ( sReductionFactor > 1 )
|
|
{
|
|
sLifeDeduct = (sLifeDeduct + (sReductionFactor / 2 ) ) / sReductionFactor;
|
|
}
|
|
else if ( ubReason == TAKE_DAMAGE_EXPLOSION )
|
|
{
|
|
// take at most 2/3rds
|
|
sLifeDeduct = (sLifeDeduct * 2) / 3;
|
|
}
|
|
|
|
// reduce breath loss to a smaller degree, except for the queen...
|
|
if ( pSoldier->ubBodyType == QUEENMONSTER )
|
|
{
|
|
// in fact, reduce breath loss by MORE!
|
|
sReductionFactor = __min( sReductionFactor, 8 );
|
|
sReductionFactor *= 2;
|
|
}
|
|
else
|
|
{
|
|
sReductionFactor /= 2;
|
|
}
|
|
if ( sReductionFactor > 1 )
|
|
{
|
|
sBreathLoss = (sBreathLoss + (sReductionFactor / 2 ) ) / sReductionFactor;
|
|
}
|
|
}
|
|
|
|
if (sLifeDeduct > pSoldier->bLife)
|
|
{
|
|
pSoldier->bLife = 0;
|
|
}
|
|
else
|
|
{
|
|
// Decrease Health
|
|
pSoldier->bLife -= sLifeDeduct;
|
|
}
|
|
|
|
// ATE: Put some logic in here to allow enemies to die quicker.....
|
|
// Are we an enemy?
|
|
if ( pSoldier->bSide != gbPlayerNum && !pSoldier->bNeutral && pSoldier->ubProfile == NO_PROFILE )
|
|
{
|
|
// ATE: Give them a chance to fall down...
|
|
if ( pSoldier->bLife > 0 && pSoldier->bLife < ( OKLIFE - 1 ) )
|
|
{
|
|
// Are we taking damage from bleeding?
|
|
if ( ubReason == TAKE_DAMAGE_BLOODLOSS )
|
|
{
|
|
// Fifty-fifty chance to die now!
|
|
if ( Random( 2 ) == 0 || gTacticalStatus.Team[ pSoldier->bTeam ].bMenInSector == 1 )
|
|
{
|
|
// Kill!
|
|
pSoldier->bLife = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// OK, see how far we are..
|
|
if ( pSoldier->bLife < ( OKLIFE - 3 ) )
|
|
{
|
|
// Kill!
|
|
pSoldier->bLife = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( fShowDamage )
|
|
{
|
|
pSoldier->sDamage += sLifeDeduct;
|
|
}
|
|
|
|
// Truncate life
|
|
if ( pSoldier->bLife < 0 )
|
|
{
|
|
pSoldier->bLife = 0;
|
|
}
|
|
|
|
|
|
// Calculate damage to our items if from an explosion!
|
|
if ( ubReason == TAKE_DAMAGE_EXPLOSION || ubReason == TAKE_DAMAGE_STRUCTURE_EXPLOSION)
|
|
{
|
|
CheckEquipmentForDamage( pSoldier, sLifeDeduct );
|
|
}
|
|
|
|
|
|
|
|
// Calculate bleeding
|
|
if ( ubReason != TAKE_DAMAGE_GAS && !AM_A_ROBOT( pSoldier ) )
|
|
{
|
|
if ( ubReason == TAKE_DAMAGE_HANDTOHAND )
|
|
{
|
|
if ( sLifeDeduct > 0 )
|
|
{
|
|
// HTH does 1 pt bleeding per hit
|
|
pSoldier->bBleeding = pSoldier->bBleeding + 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pSoldier->bBleeding = pSoldier->bLifeMax - ( pSoldier->bLife + bBandage );
|
|
}
|
|
|
|
}
|
|
|
|
// Deduct breath AND APs!
|
|
sAPCost = (sLifeDeduct / AP_GET_WOUNDED_DIVISOR); // + fallCost;
|
|
|
|
// ATE: if the robot, do not deduct
|
|
if ( !AM_A_ROBOT( pSoldier ) )
|
|
{
|
|
DeductPoints( pSoldier, sAPCost, sBreathLoss , FALSE);
|
|
}
|
|
|
|
ubCombinedLoss = (UINT8) sLifeDeduct / 10 + sBreathLoss / 2000;
|
|
|
|
// Add shock
|
|
if ( !AM_A_ROBOT( pSoldier ) )
|
|
{
|
|
pSoldier->bShock += ubCombinedLoss;
|
|
}
|
|
|
|
// start the stopwatch - the blood is gushing!
|
|
pSoldier->dNextBleed = CalcSoldierNextBleed( pSoldier );
|
|
|
|
if ( pSoldier->bInSector && pSoldier->bVisible != -1 )
|
|
{
|
|
// If we are already dead, don't show damage!
|
|
if ( bOldLife != 0 && fShowDamage && sLifeDeduct != 0 && sLifeDeduct < 1000 )
|
|
{
|
|
// Display damage
|
|
INT16 sOffsetX, sOffsetY;
|
|
|
|
// Set Damage display counter
|
|
pSoldier->fDisplayDamage = TRUE;
|
|
pSoldier->bDisplayDamageCount = 0;
|
|
|
|
if ( pSoldier->ubBodyType == QUEENMONSTER )
|
|
{
|
|
pSoldier->sDamageX = 0;
|
|
pSoldier->sDamageY = 0;
|
|
}
|
|
else
|
|
{
|
|
GetSoldierAnimOffsets( pSoldier, &sOffsetX, &sOffsetY );
|
|
pSoldier->sDamageX = sOffsetX;
|
|
pSoldier->sDamageY = sOffsetY;
|
|
}
|
|
}
|
|
}
|
|
|
|
// OK, if here, let's see if we should drop our weapon....
|
|
if ( ubReason != TAKE_DAMAGE_BLOODLOSS && !(AM_A_ROBOT( pSoldier )) )
|
|
{
|
|
INT16 sTestOne, sTestTwo, sChanceToDrop;
|
|
INT8 bVisible = -1;
|
|
|
|
sTestOne = EffectiveStrength( pSoldier );
|
|
sTestTwo = ( 2 * ( __max( sLifeDeduct, ( sBreathLoss / 100 ) ) ) );
|
|
|
|
|
|
if (pSoldier->ubAttackerID != NOBODY && MercPtrs[ pSoldier->ubAttackerID ]->ubBodyType == BLOODCAT )
|
|
{
|
|
// bloodcat boost, let them make people drop items more
|
|
sTestTwo += 20;
|
|
}
|
|
|
|
// If damage > effective strength....
|
|
sChanceToDrop = ( __max( 0, ( sTestTwo - sTestOne ) ) );
|
|
|
|
// ATE: Increase odds of NOT dropping an UNDROPPABLE OBJECT
|
|
if ( ( pSoldier->inv[ HANDPOS ].fFlags & OBJECT_UNDROPPABLE ) )
|
|
{
|
|
sChanceToDrop -= 30;
|
|
}
|
|
|
|
#ifdef JA2TESTVERSION
|
|
//ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_TESTVERSION, L"Chance To Drop Weapon: str: %d Dam: %d Chance: %d", sTestOne, sTestTwo, sChanceToDrop );
|
|
#endif
|
|
|
|
if ( Random( 100 ) < (UINT16) sChanceToDrop )
|
|
{
|
|
// OK, drop item in main hand...
|
|
if ( pSoldier->inv[ HANDPOS ].usItem != NOTHING )
|
|
{
|
|
if ( !( pSoldier->inv[ HANDPOS ].fFlags & OBJECT_UNDROPPABLE ) )
|
|
{
|
|
// ATE: if our guy, make visible....
|
|
if ( pSoldier->bTeam == gbPlayerNum )
|
|
{
|
|
bVisible = 1;
|
|
}
|
|
//if this soldier was an enemy
|
|
// Kaiden Added for UB reveal All items after combat feature!
|
|
else 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;
|
|
}
|
|
AddItemToPool( pSoldier->sGridNo, &(pSoldier->inv[ HANDPOS ]), bVisible, pSoldier->bLevel, usItemFlags, -1 ); //Madd: added usItemFlags to function arguments
|
|
DeleteObj( &(pSoldier->inv[HANDPOS]) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Drop some blood!
|
|
// decide blood amt, if any
|
|
ubBlood = ( sLifeDeduct / BLOODDIVISOR);
|
|
if ( ubBlood > MAXBLOODQUANTITY )
|
|
{
|
|
ubBlood = MAXBLOODQUANTITY;
|
|
}
|
|
|
|
if ( !( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE | SOLDIER_ROBOT ) ) )
|
|
{
|
|
if ( ubBlood != 0 )
|
|
{
|
|
if ( pSoldier->bInSector )
|
|
{
|
|
DropBlood( pSoldier, ubBlood, pSoldier->bVisible );
|
|
}
|
|
}
|
|
}
|
|
|
|
//Set UI Flag for unconscious, if it's our own guy!
|
|
if ( pSoldier->bTeam == gbPlayerNum )
|
|
{
|
|
if ( pSoldier->bLife < OKLIFE && pSoldier->bLife > 0 && bOldLife >= OKLIFE )
|
|
{
|
|
pSoldier->fUIFirstTimeUNCON = TRUE;
|
|
fInterfacePanelDirty = DIRTYLEVEL2;
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->bInSector )
|
|
{
|
|
CheckForBreathCollapse( pSoldier );
|
|
}
|
|
|
|
// EXPERIENCE CLASS GAIN (combLoss): Getting wounded in battle
|
|
|
|
DirtyMercPanelInterface( pSoldier, DIRTYLEVEL1 );
|
|
|
|
|
|
if ( ubAttacker != NOBODY )
|
|
{
|
|
// don't give exp for hitting friends!
|
|
if ( (MercPtrs[ ubAttacker ]->bTeam == gbPlayerNum) && (pSoldier->bTeam != gbPlayerNum) )
|
|
{
|
|
if ( ubReason == TAKE_DAMAGE_EXPLOSION )
|
|
{
|
|
// EXPLOSIVES GAIN (combLoss): Causing wounds in battle
|
|
StatChange( MercPtrs[ ubAttacker ], EXPLODEAMT, (UINT16)( 10 * ubCombinedLoss ), FROM_FAILURE );
|
|
}
|
|
/*
|
|
else if ( ubReason == TAKE_DAMAGE_GUNFIRE )
|
|
{
|
|
// MARKSMANSHIP GAIN (combLoss): Causing wounds in battle
|
|
StatChange( MercPtrs[ ubAttacker ], MARKAMT, (UINT16)( 5 * ubCombinedLoss ), FALSE );
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
|
|
if (PTR_OURTEAM)
|
|
{
|
|
// EXPERIENCE GAIN: Took some damage
|
|
StatChange( pSoldier, EXPERAMT, ( UINT16 )( 5 * ubCombinedLoss ), FROM_FAILURE );
|
|
|
|
// Check for quote
|
|
if ( !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_BEING_PUMMELED ) )
|
|
{
|
|
// Check attacker!
|
|
if ( ubAttacker != NOBODY && ubAttacker != pSoldier->ubID )
|
|
{
|
|
pSoldier->bNumHitsThisTurn++;
|
|
|
|
if ( (pSoldier->bNumHitsThisTurn >= 3) && ( pSoldier->bLife - pSoldier->bOldLife > 20 ) )
|
|
{
|
|
if ( Random(100) < (UINT16)((40 * ( pSoldier->bNumHitsThisTurn - 2))))
|
|
{
|
|
DelayedTacticalCharacterDialogue( pSoldier, QUOTE_TAKEN_A_BREATING );
|
|
pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_BEING_PUMMELED;
|
|
pSoldier->bNumHitsThisTurn = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((ubAttacker != NOBODY) && (Menptr[ubAttacker].bTeam == OUR_TEAM) && (pSoldier->ubProfile != NO_PROFILE) && (pSoldier->ubProfile >= FIRST_RPC && pSoldier->ubProfile < GASTON ))
|
|
{
|
|
gMercProfiles[pSoldier->ubProfile].ubMiscFlags |= PROFILE_MISC_FLAG_WOUNDEDBYPLAYER;
|
|
if (pSoldier->ubProfile == 114)
|
|
{
|
|
SetFactTrue( FACT_PACOS_KILLED );
|
|
}
|
|
}
|
|
|
|
HandleTakeDamageDeath( pSoldier, bOldLife, ubReason );
|
|
|
|
// Check if we are < unconscious, and shutup if so! also wipe sight
|
|
if ( pSoldier->bLife < CONSCIOUSNESS )
|
|
{
|
|
ShutupaYoFace( pSoldier->iFaceIndex );
|
|
}
|
|
|
|
if ( pSoldier->bLife < OKLIFE )
|
|
{
|
|
DecayIndividualOpplist( pSoldier );
|
|
}
|
|
|
|
|
|
return( ubCombinedLoss );
|
|
}
|
|
|
|
|
|
extern BOOLEAN IsMercSayingDialogue( UINT8 ubProfileID );
|
|
|
|
|
|
BOOLEAN InternalDoMercBattleSound( SOLDIERTYPE *pSoldier, UINT8 ubBattleSoundID, INT8 bSpecialCode )
|
|
{
|
|
SGPFILENAME zFilename;
|
|
SOUNDPARMS spParms;
|
|
UINT8 ubSoundID;
|
|
UINT32 uiSoundID;
|
|
UINT32 iFaceIndex;
|
|
BOOLEAN fDoSub = FALSE;
|
|
INT32 uiSubSoundID = 0;
|
|
BOOLEAN fSpeechSound = FALSE;
|
|
|
|
// DOUBLECHECK RANGE
|
|
CHECKF ( ubBattleSoundID < NUM_MERC_BATTLE_SOUNDS );
|
|
|
|
if ( ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
|
|
{
|
|
// Pick a passenger from vehicle....
|
|
pSoldier = PickRandomPassengerFromVehicle( pSoldier );
|
|
|
|
if ( pSoldier == NULL )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
}
|
|
|
|
// If a death sound, and we have already done ours...
|
|
if ( ubBattleSoundID == BATTLE_SOUND_DIE1 )
|
|
{
|
|
if ( pSoldier->fDieSoundUsed )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
|
|
// Are we mute?
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_MUTE )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
// uiTimeSameBattleSndDone
|
|
|
|
// If we are a creature, etc, pick a better sound...
|
|
if ( ubBattleSoundID == BATTLE_SOUND_HIT1 || ubBattleSoundID == BATTLE_SOUND_HIT2 )
|
|
{
|
|
switch ( pSoldier->ubBodyType )
|
|
{
|
|
case COW:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = COW_HIT_SND;
|
|
break;
|
|
|
|
case YAF_MONSTER:
|
|
case YAM_MONSTER:
|
|
case ADULTFEMALEMONSTER:
|
|
case AM_MONSTER:
|
|
|
|
fDoSub = TRUE;
|
|
|
|
if ( Random( 2 ) == 0 )
|
|
{
|
|
uiSubSoundID = ACR_DIE_PART1;
|
|
}
|
|
else
|
|
{
|
|
uiSubSoundID = ACR_LUNGE;
|
|
}
|
|
break;
|
|
|
|
case INFANT_MONSTER:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = BCR_SHRIEK;
|
|
break;
|
|
|
|
case QUEENMONSTER:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = LQ_SHRIEK;
|
|
break;
|
|
|
|
case LARVAE_MONSTER:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = BCR_SHRIEK;
|
|
break;
|
|
|
|
case BLOODCAT:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = BLOODCAT_HIT_1;
|
|
break;
|
|
|
|
case ROBOTNOWEAPON:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = (UINT32)( S_METAL_IMPACT1 + Random( 2 ) );
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ubBattleSoundID == BATTLE_SOUND_DIE1 )
|
|
{
|
|
switch ( pSoldier->ubBodyType )
|
|
{
|
|
case COW:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = COW_DIE_SND;
|
|
break;
|
|
|
|
case YAF_MONSTER:
|
|
case YAM_MONSTER:
|
|
case ADULTFEMALEMONSTER:
|
|
case AM_MONSTER:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = CREATURE_FALL_PART_2;
|
|
break;
|
|
|
|
case INFANT_MONSTER:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = BCR_DYING;
|
|
break;
|
|
|
|
case LARVAE_MONSTER:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = LCR_RUPTURE;
|
|
break;
|
|
|
|
case QUEENMONSTER:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = LQ_DYING;
|
|
break;
|
|
|
|
case BLOODCAT:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = BLOODCAT_DIE_1;
|
|
break;
|
|
|
|
case ROBOTNOWEAPON:
|
|
|
|
fDoSub = TRUE;
|
|
uiSubSoundID = (UINT32)( EXPLOSION_1 );
|
|
PlayJA2Sample( ROBOT_DEATH, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
// OK. any other sound, not hits, robot makes a beep
|
|
if ( pSoldier->ubBodyType == ROBOTNOWEAPON && !fDoSub )
|
|
{
|
|
fDoSub = TRUE;
|
|
if ( ubBattleSoundID == BATTLE_SOUND_ATTN1 )
|
|
{
|
|
uiSubSoundID = ROBOT_GREETING;
|
|
}
|
|
else
|
|
{
|
|
uiSubSoundID = ROBOT_BEEP;
|
|
}
|
|
}
|
|
|
|
if ( fDoSub )
|
|
{
|
|
if( guiCurrentScreen != GAME_SCREEN )
|
|
{
|
|
PlayJA2Sample( uiSubSoundID, RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
}
|
|
else
|
|
{
|
|
PlayJA2Sample( uiSubSoundID, RATE_11025, SoundVolume( (UINT8)CalculateSpeechVolume( HIGHVOLUME ), pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ) );
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
// Check if this is the same one we just played...
|
|
if ( pSoldier->bOldBattleSnd == ubBattleSoundID && gBattleSndsData[ ubBattleSoundID ].fDontAllowTwoInRow )
|
|
{
|
|
// Are we below the min delay?
|
|
if ( ( GetJA2Clock( ) - pSoldier->uiTimeSameBattleSndDone ) < MIN_SUBSEQUENT_SNDS_DELAY )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
// If a battle snd is STILL playing....
|
|
if ( SoundIsPlaying( pSoldier->uiBattleSoundID ) )
|
|
{
|
|
// We can do a few things here....
|
|
// Is this a crutial one...?
|
|
if ( gBattleSndsData[ ubBattleSoundID ].fStopDialogue == 1 )
|
|
{
|
|
// Stop playing origonal
|
|
SoundStop( pSoldier->uiBattleSoundID );
|
|
}
|
|
else
|
|
{
|
|
// Skip this one...
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
// If we are talking now....
|
|
if ( IsMercSayingDialogue( pSoldier->ubProfile ) )
|
|
{
|
|
// We can do a couple of things now...
|
|
if ( gBattleSndsData[ ubBattleSoundID ].fStopDialogue == 1 )
|
|
{
|
|
// Stop dialigue...
|
|
DialogueAdvanceSpeech( );
|
|
}
|
|
else if ( gBattleSndsData[ ubBattleSoundID ].fStopDialogue == 2 )
|
|
{
|
|
// Skip battle snd...
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
|
|
// Save this one we're doing...
|
|
pSoldier->bOldBattleSnd = ubBattleSoundID;
|
|
pSoldier->uiTimeSameBattleSndDone = GetJA2Clock( );
|
|
|
|
|
|
// Adjust based on morale...
|
|
if ( ubBattleSoundID == BATTLE_SOUND_OK1 && pSoldier->bMorale < LOW_MORALE_BATTLE_SND_THREASHOLD )
|
|
{
|
|
ubBattleSoundID = BATTLE_SOUND_LOWMARALE_OK1;
|
|
}
|
|
if ( ubBattleSoundID == BATTLE_SOUND_ATTN1 && pSoldier->bMorale < LOW_MORALE_BATTLE_SND_THREASHOLD )
|
|
{
|
|
ubBattleSoundID = BATTLE_SOUND_LOWMARALE_ATTN1;
|
|
}
|
|
|
|
ubSoundID = ubBattleSoundID;
|
|
|
|
//if the sound to be played is a confirmation, check to see if we are to play it
|
|
if( ubSoundID == BATTLE_SOUND_OK1 )
|
|
{
|
|
if( gGameSettings.fOptions[ TOPTION_MUTE_CONFIRMATIONS ] )
|
|
return( TRUE );
|
|
//else a speech sound is to be played
|
|
else
|
|
fSpeechSound = TRUE;
|
|
}
|
|
|
|
// Randomize between sounds, if appropriate
|
|
if ( gBattleSndsData[ ubSoundID ].ubRandomVal != 0 )
|
|
{
|
|
ubSoundID = ubSoundID + (UINT8)Random( gBattleSndsData[ ubSoundID ].ubRandomVal );
|
|
|
|
}
|
|
|
|
|
|
// OK, build file and play!
|
|
if ( pSoldier->ubProfile != NO_PROFILE )
|
|
{
|
|
sprintf( zFilename, "BATTLESNDS\\%03d_%s.wav", pSoldier->ubProfile, gBattleSndsData[ ubSoundID ].zName );
|
|
|
|
if ( !FileExists( zFilename ) )
|
|
{
|
|
// OK, temp build file...
|
|
if ( pSoldier->ubBodyType == REGFEMALE )
|
|
{
|
|
sprintf( zFilename, "BATTLESNDS\\f_%s.wav", gBattleSndsData[ ubSoundID ].zName );
|
|
}
|
|
else
|
|
{
|
|
sprintf( zFilename, "BATTLESNDS\\m_%s.wav", gBattleSndsData[ ubSoundID ].zName );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Check if we can play this!
|
|
if ( !gBattleSndsData[ ubSoundID ].fBadGuy )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( pSoldier->ubBodyType == HATKIDCIV || pSoldier->ubBodyType == KIDCIV )
|
|
{
|
|
if ( ubSoundID == BATTLE_SOUND_DIE1 )
|
|
{
|
|
sprintf( zFilename, "BATTLESNDS\\kid%d_dying.wav", pSoldier->ubBattleSoundID );
|
|
}
|
|
else
|
|
{
|
|
sprintf( zFilename, "BATTLESNDS\\kid%d_%s.wav", pSoldier->ubBattleSoundID, gBattleSndsData[ ubSoundID ].zName );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( ubSoundID == BATTLE_SOUND_DIE1 )
|
|
{
|
|
sprintf( zFilename, "BATTLESNDS\\bad%d_die.wav", pSoldier->ubBattleSoundID );
|
|
}
|
|
else
|
|
{
|
|
sprintf( zFilename, "BATTLESNDS\\bad%d_%s.wav", pSoldier->ubBattleSoundID, gBattleSndsData[ ubSoundID ].zName );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Play sound!
|
|
memset(&spParms, 0xff, sizeof(SOUNDPARMS));
|
|
|
|
spParms.uiSpeed = RATE_11025;
|
|
//spParms.uiVolume = CalculateSpeechVolume( pSoldier->bVocalVolume );
|
|
|
|
spParms.uiVolume = (INT8)CalculateSpeechVolume( HIGHVOLUME );
|
|
|
|
// ATE: Reduce volume for OK sounds...
|
|
// ( Only for all-moves or multi-selection cases... )
|
|
if ( bSpecialCode == BATTLE_SND_LOWER_VOLUME )
|
|
{
|
|
spParms.uiVolume = (INT8)CalculateSpeechVolume( MIDVOLUME );
|
|
}
|
|
|
|
// If we are an enemy.....reduce due to volume
|
|
if ( pSoldier->bTeam != gbPlayerNum )
|
|
{
|
|
spParms.uiVolume = SoundVolume( (UINT8)spParms.uiVolume, pSoldier->sGridNo );
|
|
}
|
|
|
|
spParms.uiLoop = 1;
|
|
spParms.uiPan = SoundDir( pSoldier->sGridNo );
|
|
spParms.uiPriority=GROUP_PLAYER;
|
|
|
|
if ( ( uiSoundID = SoundPlay( zFilename, &spParms ) ) == SOUND_ERROR )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
else
|
|
{
|
|
pSoldier->uiBattleSoundID = uiSoundID;
|
|
|
|
if ( pSoldier->ubProfile != NO_PROFILE )
|
|
{
|
|
// Get soldier's face ID
|
|
iFaceIndex = pSoldier->iFaceIndex;
|
|
|
|
// Check face index
|
|
if ( iFaceIndex != -1 )
|
|
{
|
|
ExternSetFaceTalking( iFaceIndex, uiSoundID );
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
BOOLEAN DoMercBattleSound( SOLDIERTYPE *pSoldier, UINT8 ubBattleSoundID )
|
|
{
|
|
// We WANT to play some RIGHT AWAY.....
|
|
if ( gBattleSndsData[ ubBattleSoundID ].fStopDialogue == 1 || ( pSoldier->ubProfile == NO_PROFILE ) || InOverheadMap( ) )
|
|
{
|
|
return( InternalDoMercBattleSound( pSoldier, ubBattleSoundID, 0 ) );
|
|
}
|
|
|
|
// So here, only if we were currently saying dialogue.....
|
|
if ( !IsMercSayingDialogue( pSoldier->ubProfile ) )
|
|
{
|
|
return( InternalDoMercBattleSound( pSoldier, ubBattleSoundID, 0 ) );
|
|
}
|
|
|
|
// OK, queue it up otherwise!
|
|
TacticalCharacterDialogueWithSpecialEvent( pSoldier, 0, DIALOGUE_SPECIAL_EVENT_DO_BATTLE_SND, ubBattleSoundID,0 );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN PreloadSoldierBattleSounds( SOLDIERTYPE *pSoldier, BOOLEAN fRemove )
|
|
{
|
|
UINT32 cnt;
|
|
|
|
CHECKF( pSoldier->bActive != FALSE );
|
|
|
|
for ( cnt = 0; cnt < NUM_MERC_BATTLE_SOUNDS; cnt++ )
|
|
{
|
|
// OK, build file and play!
|
|
if ( pSoldier->ubProfile != NO_PROFILE )
|
|
{
|
|
if ( gBattleSndsData[ cnt ].fPreload )
|
|
{
|
|
if ( fRemove )
|
|
{
|
|
SoundUnlockSample( gBattleSndsData[ cnt ].zName );
|
|
}
|
|
else
|
|
{
|
|
SoundLockSample( gBattleSndsData[ cnt ].zName );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( gBattleSndsData[ cnt ].fPreload && gBattleSndsData[ cnt ].fBadGuy )
|
|
{
|
|
if ( fRemove )
|
|
{
|
|
SoundUnlockSample( gBattleSndsData[ cnt ].zName );
|
|
}
|
|
else
|
|
{
|
|
SoundLockSample( gBattleSndsData[ cnt ].zName );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN CheckSoldierHitRoof( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// Check if we are near a lower level
|
|
INT8 bNewDirection;
|
|
BOOLEAN fReturnVal = FALSE;
|
|
INT16 sNewGridNo;
|
|
// Default to true
|
|
BOOLEAN fDoForwards = TRUE;
|
|
|
|
if ( pSoldier->bLife >= OKLIFE )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( FindLowerLevel( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bNewDirection ) && ( pSoldier->bLevel > 0 ) )
|
|
{
|
|
// ONly if standing!
|
|
if ( gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_STAND )
|
|
{
|
|
// We are near a lower level.
|
|
// Use opposite direction
|
|
bNewDirection = gOppositeDirection[ bNewDirection ];
|
|
|
|
// Alrighty, let's not blindly change here, look at whether the dest gridno is good!
|
|
sNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bNewDirection ] ) );
|
|
if ( !NewOKDestination( pSoldier, sNewGridNo, TRUE, 0 ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
sNewGridNo = NewGridNo( (UINT16)sNewGridNo, DirectionInc( gOppositeDirection[ bNewDirection ] ) );
|
|
if ( !NewOKDestination( pSoldier, sNewGridNo, TRUE, 0 ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Are wee near enough to fall forwards....
|
|
if ( pSoldier->bDirection == gOneCDirection[ bNewDirection ] ||
|
|
pSoldier->bDirection == gTwoCDirection[ bNewDirection ] ||
|
|
pSoldier->bDirection == bNewDirection ||
|
|
pSoldier->bDirection == gOneCCDirection[ bNewDirection ] ||
|
|
pSoldier->bDirection == gTwoCCDirection[ bNewDirection ] )
|
|
{
|
|
// Do backwards...
|
|
fDoForwards = FALSE;
|
|
}
|
|
|
|
// If we are facing the opposite direction, fall backwards
|
|
// ATE: Make this more usefull...
|
|
if ( fDoForwards )
|
|
{
|
|
pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (INT16)( -1 * DirectionInc(bNewDirection ) ) );
|
|
pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sTempNewGridNo, (INT16)( -1 * DirectionInc( bNewDirection ) ) );
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, gOppositeDirection[ bNewDirection ] );
|
|
pSoldier->fTurningUntilDone = TRUE;
|
|
pSoldier->usPendingAnimation = FALLFORWARD_ROOF;
|
|
//EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_ROOF, 0 , FALSE );
|
|
|
|
// Deduct hitpoints/breath for falling!
|
|
SoldierTakeDamage( pSoldier, ANIM_CROUCH, 100, 5000, TAKE_DAMAGE_FALLROOF, NOBODY, NOWHERE, 0, TRUE );
|
|
|
|
fReturnVal = TRUE;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (INT16)( -1 * DirectionInc( bNewDirection ) ) );
|
|
pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sTempNewGridNo, (INT16)( -1 * DirectionInc( bNewDirection ) ) );
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, bNewDirection );
|
|
pSoldier->fTurningUntilDone = TRUE;
|
|
pSoldier->usPendingAnimation = FALLOFF;
|
|
|
|
// Deduct hitpoints/breath for falling!
|
|
SoldierTakeDamage( pSoldier, ANIM_CROUCH, 100, 5000, TAKE_DAMAGE_FALLROOF, NOBODY, NOWHERE, 0, TRUE );
|
|
|
|
fReturnVal = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return( fReturnVal );
|
|
}
|
|
|
|
void BeginSoldierClimbDownRoof( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT8 bNewDirection;
|
|
UINT8 ubWhoIsThere;
|
|
|
|
|
|
if ( FindLowerLevel( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bNewDirection ) && ( pSoldier->bLevel > 0 ) )
|
|
{
|
|
if ( EnoughPoints( pSoldier, GetAPsToClimbRoof( pSoldier, TRUE ), 0, TRUE ) )
|
|
{
|
|
//Kaiden: Helps if we look where we are going before we try to climb on top of someone
|
|
ubWhoIsThere = WhoIsThere2( NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bNewDirection ) ), 0 );
|
|
if ( ubWhoIsThere != NOBODY && ubWhoIsThere != pSoldier->ubID )
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
|
|
if (pSoldier->bTeam == gbPlayerNum)
|
|
{
|
|
// OK, SET INTERFACE FIRST
|
|
SetUIBusy( pSoldier->ubID );
|
|
}
|
|
|
|
|
|
|
|
|
|
pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bNewDirection ) );
|
|
|
|
bNewDirection = gTwoCDirection[ bNewDirection ];
|
|
|
|
pSoldier->ubPendingDirection = bNewDirection;
|
|
EVENT_InitNewSoldierAnim( pSoldier, CLIMBDOWNROOF, 0 , FALSE );
|
|
|
|
InternalReceivingSoldierCancelServices( pSoldier, FALSE );
|
|
InternalGivingSoldierCancelServices( pSoldier, FALSE );
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
/*
|
|
void BeginSoldierClimbDownRoof( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT8 bNewDirection;
|
|
UINT8 ubWhoIsThere;
|
|
|
|
|
|
if ( FindLowerLevel( pSoldier, pSoldier->sGridNo, pSoldier->bDirection, &bNewDirection ) && ( pSoldier->bLevel > 0 ) )
|
|
{
|
|
if ( EnoughPoints( pSoldier, GetAPsToClimbRoof( pSoldier, TRUE ), 0, TRUE ) )
|
|
{
|
|
if (pSoldier->bTeam == gbPlayerNum)
|
|
{
|
|
// OK, SET INTERFACE FIRST
|
|
SetUIBusy( pSoldier->ubID );
|
|
}
|
|
|
|
|
|
|
|
|
|
pSoldier->sTempNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, (UINT16)DirectionInc(bNewDirection ) );
|
|
|
|
bNewDirection = gTwoCDirection[ bNewDirection ];
|
|
|
|
pSoldier->ubPendingDirection = bNewDirection;
|
|
EVENT_InitNewSoldierAnim( pSoldier, CLIMBDOWNROOF, 0 , FALSE );
|
|
|
|
InternalReceivingSoldierCancelServices( pSoldier, FALSE );
|
|
InternalGivingSoldierCancelServices( pSoldier, FALSE );
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
void MoveMerc( SOLDIERTYPE *pSoldier, FLOAT dMovementChange, FLOAT dAngle, BOOLEAN fCheckRange )
|
|
{
|
|
INT16 dDegAngle;
|
|
FLOAT dDeltaPos;
|
|
FLOAT dXPos , dYPos;
|
|
BOOLEAN fStop = FALSE;
|
|
|
|
|
|
dDegAngle = (INT16)( dAngle * 180 / PI );
|
|
//sprintf( gDebugStr, "Move Angle: %d", (int)dDegAngle );
|
|
|
|
// Find delta Movement for X pos
|
|
dDeltaPos = (FLOAT) (dMovementChange * sin( dAngle ));
|
|
|
|
// Find new position
|
|
dXPos = pSoldier->dXPos + dDeltaPos;
|
|
|
|
if ( fCheckRange )
|
|
{
|
|
fStop = FALSE;
|
|
|
|
switch( pSoldier->bMovementDirection )
|
|
{
|
|
case NORTHEAST:
|
|
case EAST:
|
|
case SOUTHEAST:
|
|
|
|
if ( dXPos >= pSoldier->sDestXPos )
|
|
{
|
|
fStop = TRUE;
|
|
}
|
|
break;
|
|
|
|
case NORTHWEST:
|
|
case WEST:
|
|
case SOUTHWEST:
|
|
|
|
if ( dXPos <= pSoldier->sDestXPos )
|
|
{
|
|
fStop = TRUE;
|
|
}
|
|
break;
|
|
|
|
case NORTH:
|
|
case SOUTH:
|
|
|
|
fStop = TRUE;
|
|
break;
|
|
|
|
}
|
|
|
|
if ( fStop )
|
|
{
|
|
//dXPos = pSoldier->sDestXPos;
|
|
pSoldier->fPastXDest = TRUE;
|
|
|
|
if ( pSoldier->sGridNo == pSoldier->sFinalDestination )
|
|
{
|
|
dXPos = pSoldier->sDestXPos;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find delta Movement for Y pos
|
|
dDeltaPos = (FLOAT) (dMovementChange * cos( dAngle ));
|
|
|
|
// Find new pos
|
|
dYPos = pSoldier->dYPos + dDeltaPos;
|
|
|
|
if ( fCheckRange )
|
|
{
|
|
fStop = FALSE;
|
|
|
|
switch( pSoldier->bMovementDirection )
|
|
{
|
|
case NORTH:
|
|
case NORTHEAST:
|
|
case NORTHWEST:
|
|
|
|
if ( dYPos <= pSoldier->sDestYPos )
|
|
{
|
|
fStop = TRUE;
|
|
}
|
|
break;
|
|
|
|
case SOUTH:
|
|
case SOUTHWEST:
|
|
case SOUTHEAST:
|
|
|
|
if ( dYPos >= pSoldier->sDestYPos )
|
|
{
|
|
fStop = TRUE;
|
|
}
|
|
break;
|
|
|
|
case EAST:
|
|
case WEST:
|
|
|
|
fStop = TRUE;
|
|
break;
|
|
|
|
}
|
|
|
|
if ( fStop )
|
|
{
|
|
//dYPos = pSoldier->sDestYPos;
|
|
pSoldier->fPastYDest = TRUE;
|
|
|
|
if ( pSoldier->sGridNo == pSoldier->sFinalDestination )
|
|
{
|
|
dYPos = pSoldier->sDestYPos;
|
|
}
|
|
}
|
|
}
|
|
|
|
// OK, set new position
|
|
EVENT_InternalSetSoldierPosition( pSoldier, dXPos, dYPos, FALSE, FALSE, FALSE );
|
|
|
|
// DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("X: %f Y: %f", dXPos, dYPos ) );
|
|
|
|
}
|
|
|
|
INT16 GetDirectionFromGridNo( INT16 sGridNo, SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT16 sXPos, sYPos;
|
|
|
|
ConvertGridNoToXY( sGridNo, &sXPos, &sYPos );
|
|
|
|
return( GetDirectionFromXY( sXPos, sYPos, pSoldier ) );
|
|
}
|
|
|
|
INT16 GetDirectionToGridNoFromGridNo( INT16 sGridNoDest, INT16 sGridNoSrc )
|
|
{
|
|
INT16 sXPos2, sYPos2;
|
|
INT16 sXPos, sYPos;
|
|
|
|
ConvertGridNoToXY( sGridNoSrc, &sXPos, &sYPos );
|
|
ConvertGridNoToXY( sGridNoDest, &sXPos2, &sYPos2 );
|
|
|
|
return( atan8( sXPos2, sYPos2, sXPos, sYPos ) );
|
|
|
|
}
|
|
|
|
INT16 GetDirectionFromXY( INT16 sXPos, INT16 sYPos, SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT16 sXPos2, sYPos2;
|
|
|
|
ConvertGridNoToXY( pSoldier->sGridNo, &sXPos2, &sYPos2 );
|
|
|
|
return( atan8( sXPos2, sYPos2, sXPos, sYPos ) );
|
|
}
|
|
|
|
#if 0
|
|
UINT8 atan8( INT16 x1, INT16 y1, INT16 x2, INT16 y2 )
|
|
{
|
|
static int trig[8] = { 2, 3, 4, 5, 6, 7, 8, 1 };
|
|
// returned values are N=1, NE=2, E=3, SE=4, S=5, SW=6, W=7, NW=8
|
|
double dx=(x2-x1);
|
|
double dy=(y2-y1);
|
|
double a;
|
|
int i,k;
|
|
if (dx==0)
|
|
dx=0.00390625; // 1/256th
|
|
#define PISLICES (8)
|
|
a=(atan2(dy,dx) + PI/PISLICES)/(PI/(PISLICES/2));
|
|
i=(int)a;
|
|
if (a>0)
|
|
k=i; else
|
|
if (a<0)
|
|
k=i+(PISLICES-1); else
|
|
k=0;
|
|
return(trig[k]);
|
|
}
|
|
#endif
|
|
|
|
//#if 0
|
|
UINT8 atan8( INT16 sXPos, INT16 sYPos, INT16 sXPos2, INT16 sYPos2 )
|
|
{
|
|
DOUBLE test_x = sXPos2 - sXPos;
|
|
DOUBLE test_y = sYPos2 - sYPos;
|
|
UINT8 mFacing = WEST;
|
|
INT16 dDegAngle;
|
|
DOUBLE angle;
|
|
|
|
if ( test_x == 0 )
|
|
{
|
|
test_x = 0.04;
|
|
}
|
|
|
|
angle = atan2( test_x, test_y );
|
|
|
|
|
|
dDegAngle = (INT16)( angle * 180 / PI );
|
|
//sprintf( gDebugStr, "Move Angle: %d", (int)dDegAngle );
|
|
|
|
do
|
|
{
|
|
if ( angle >=-PI*.375 && angle <= -PI*.125 )
|
|
{
|
|
mFacing = SOUTHWEST;
|
|
break;
|
|
}
|
|
|
|
if ( angle <= PI*.375 && angle >= PI*.125 )
|
|
{
|
|
mFacing = SOUTHEAST;
|
|
break;
|
|
}
|
|
|
|
if ( angle >=PI*.623 && angle <= PI*.875 )
|
|
{
|
|
mFacing = NORTHEAST;
|
|
break;
|
|
}
|
|
|
|
if ( angle <=-PI*.623 && angle >= -PI*.875 )
|
|
{
|
|
mFacing = NORTHWEST;
|
|
break;
|
|
}
|
|
|
|
if ( angle >-PI*0.125 && angle < PI*0.125 )
|
|
{
|
|
mFacing = SOUTH;
|
|
}
|
|
if ( angle > PI*0.375 && angle < PI*0.623 )
|
|
{
|
|
mFacing = EAST;
|
|
}
|
|
if ( ( angle > PI*0.875 && angle <= PI ) || ( angle > -PI && angle < -PI*0.875 ) )
|
|
{
|
|
mFacing = NORTH;
|
|
}
|
|
if ( angle > -PI*0.623 && angle < -PI*0.375 )
|
|
{
|
|
mFacing = WEST;
|
|
}
|
|
|
|
} while( FALSE );
|
|
|
|
return( mFacing );
|
|
}
|
|
|
|
|
|
UINT8 atan8FromAngle( DOUBLE angle )
|
|
{
|
|
UINT8 mFacing = WEST;
|
|
|
|
if ( angle > PI )
|
|
{
|
|
angle = ( angle - PI ) - PI;
|
|
}
|
|
if ( angle < -PI )
|
|
{
|
|
angle = ( PI - ( fabs( angle ) - PI ) );
|
|
}
|
|
|
|
do
|
|
{
|
|
if ( angle >=-PI*.375 && angle <= -PI*.125 )
|
|
{
|
|
mFacing = SOUTHWEST;
|
|
break;
|
|
}
|
|
|
|
if ( angle <= PI*.375 && angle >= PI*.125 )
|
|
{
|
|
mFacing = SOUTHEAST;
|
|
break;
|
|
}
|
|
|
|
if ( angle >=PI*.623 && angle <= PI*.875 )
|
|
{
|
|
mFacing = NORTHEAST;
|
|
break;
|
|
}
|
|
|
|
if ( angle <=-PI*.623 && angle >= -PI*.875 )
|
|
{
|
|
mFacing = NORTHWEST;
|
|
break;
|
|
}
|
|
|
|
if ( angle >-PI*0.125 && angle < PI*0.125 )
|
|
{
|
|
mFacing = SOUTH;
|
|
}
|
|
if ( angle > PI*0.375 && angle < PI*0.623 )
|
|
{
|
|
mFacing = EAST;
|
|
}
|
|
if ( ( angle > PI*0.875 && angle <= PI ) || ( angle > -PI && angle < -PI*0.875 ) )
|
|
{
|
|
mFacing = NORTH;
|
|
}
|
|
if ( angle > -PI*0.623 && angle < -PI*0.375 )
|
|
{
|
|
mFacing = WEST;
|
|
}
|
|
|
|
} while( FALSE );
|
|
|
|
return( mFacing );
|
|
}
|
|
|
|
|
|
void CheckForFullStructures( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// This function checks to see if we are near a specific structure type which requires us to blit a
|
|
// small obscuring peice
|
|
INT16 sGridNo;
|
|
UINT16 usFullTileIndex;
|
|
INT32 cnt;
|
|
|
|
|
|
// Check in all 'Above' directions
|
|
for ( cnt = 0; cnt < MAX_FULLTILE_DIRECTIONS; cnt++ )
|
|
{
|
|
sGridNo = pSoldier->sGridNo + gsFullTileDirections[ cnt ];
|
|
|
|
if ( CheckForFullStruct( sGridNo, &usFullTileIndex ) )
|
|
{
|
|
// Add one for the item's obsuring part
|
|
pSoldier->usFrontArcFullTileList[ cnt ] = usFullTileIndex + 1;
|
|
pSoldier->usFrontArcFullTileGridNos[ cnt ] = sGridNo;
|
|
AddTopmostToHead( sGridNo, pSoldier->usFrontArcFullTileList[ cnt ] );
|
|
}
|
|
else
|
|
{
|
|
if ( pSoldier->usFrontArcFullTileList[ cnt ] != 0 )
|
|
{
|
|
RemoveTopmost( pSoldier->usFrontArcFullTileGridNos[ cnt ], pSoldier->usFrontArcFullTileList[ cnt ] );
|
|
}
|
|
pSoldier->usFrontArcFullTileList[ cnt ] = 0;
|
|
pSoldier->usFrontArcFullTileGridNos[ cnt ] = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN CheckForFullStruct( INT16 sGridNo, UINT16 *pusIndex )
|
|
{
|
|
LEVELNODE *pStruct = NULL;
|
|
LEVELNODE *pOldStruct = NULL;
|
|
UINT32 fTileFlags;
|
|
|
|
pStruct = gpWorldLevelData[ sGridNo ].pStructHead;
|
|
|
|
// Look through all structs and Search for type
|
|
|
|
while( pStruct != NULL )
|
|
{
|
|
|
|
if ( pStruct->usIndex != NO_TILE && pStruct->usIndex < NUMBEROFTILES )
|
|
{
|
|
|
|
GetTileFlags( pStruct->usIndex, &fTileFlags );
|
|
|
|
// Advance to next
|
|
pOldStruct = pStruct;
|
|
pStruct = pStruct->pNext;
|
|
|
|
//if( (pOldStruct->pStructureData!=NULL) && ( pOldStruct->pStructureData->fFlags&STRUCTURE_TREE ) )
|
|
if ( fTileFlags & FULL3D_TILE )
|
|
{
|
|
// CHECK IF THIS TREE IS FAIRLY ALONE!
|
|
if ( FullStructAlone( sGridNo, 2 ) )
|
|
{
|
|
// Return true and return index
|
|
*pusIndex = pOldStruct->usIndex;
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// Advance to next
|
|
pOldStruct = pStruct;
|
|
pStruct = pStruct->pNext;
|
|
}
|
|
|
|
}
|
|
|
|
// Could not find it, return FALSE
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN FullStructAlone( INT16 sGridNo, UINT8 ubRadius )
|
|
{
|
|
INT16 sTop, sBottom;
|
|
INT16 sLeft, sRight;
|
|
INT16 cnt1, cnt2;
|
|
INT16 iNewIndex;
|
|
INT32 leftmost;
|
|
|
|
|
|
// Determine start end end indicies and num rows
|
|
sTop = ubRadius;
|
|
sBottom = -ubRadius;
|
|
sLeft = - ubRadius;
|
|
sRight = ubRadius;
|
|
|
|
for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
|
|
{
|
|
|
|
leftmost = ( ( sGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS;
|
|
|
|
for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
|
|
{
|
|
iNewIndex = sGridNo + ( WORLD_COLS * cnt1 ) + cnt2;
|
|
|
|
|
|
if ( iNewIndex >=0 && iNewIndex < WORLD_MAX &&
|
|
iNewIndex >= leftmost && iNewIndex < ( leftmost + WORLD_COLS ) )
|
|
{
|
|
if ( iNewIndex != sGridNo )
|
|
{
|
|
if ( FindStructure( iNewIndex, STRUCTURE_TREE ) != NULL )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
void AdjustForFastTurnAnimation( SOLDIERTYPE *pSoldier )
|
|
{
|
|
|
|
// CHECK FOR FASTTURN ANIMATIONS
|
|
// ATE: Mod: Only fastturn for OUR guys!
|
|
if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FASTTURN && pSoldier->bTeam == gbPlayerNum && !( pSoldier->uiStatusFlags & SOLDIER_TURNINGFROMHIT ) )
|
|
{
|
|
if ( pSoldier->bDirection != pSoldier->bDesiredDirection )
|
|
{
|
|
pSoldier->sAniDelay = FAST_TURN_ANIM_SPEED;
|
|
}
|
|
else
|
|
{
|
|
SetSoldierAniSpeed( pSoldier );
|
|
// FreeUpNPCFromTurning( pSoldier, LOOK);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
BOOLEAN IsActionInterruptable( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_NONINTERRUPT )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
// WRAPPER FUNCTIONS FOR SOLDIER EVENTS
|
|
void SendSoldierPositionEvent( SOLDIERTYPE *pSoldier, FLOAT dNewXPos, FLOAT dNewYPos )
|
|
{
|
|
// Sent event for position update
|
|
EV_S_SETPOSITION SSetPosition;
|
|
|
|
SSetPosition.usSoldierID = pSoldier->ubID;
|
|
SSetPosition.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
|
|
|
|
SSetPosition.dNewXPos = dNewXPos;
|
|
SSetPosition.dNewYPos = dNewYPos;
|
|
|
|
AddGameEvent( S_SETPOSITION, 0, &SSetPosition );
|
|
|
|
}
|
|
|
|
void SendSoldierDestinationEvent( SOLDIERTYPE *pSoldier, UINT16 usNewDestination )
|
|
{
|
|
// Sent event for position update
|
|
EV_S_CHANGEDEST SChangeDest;
|
|
|
|
SChangeDest.usSoldierID = pSoldier->ubID;
|
|
SChangeDest.usNewDestination = usNewDestination;
|
|
SChangeDest.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
|
|
|
|
AddGameEvent( S_CHANGEDEST, 0, &SChangeDest );
|
|
|
|
}
|
|
|
|
void SendSoldierSetDirectionEvent( SOLDIERTYPE *pSoldier, UINT16 usNewDirection )
|
|
{
|
|
// Sent event for position update
|
|
EV_S_SETDIRECTION SSetDirection;
|
|
|
|
SSetDirection.usSoldierID = pSoldier->ubID;
|
|
SSetDirection.usNewDirection = usNewDirection;
|
|
SSetDirection.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
|
|
|
|
AddGameEvent( S_SETDIRECTION, 0, &SSetDirection );
|
|
|
|
}
|
|
|
|
void SendSoldierSetDesiredDirectionEvent( SOLDIERTYPE *pSoldier, UINT16 usDesiredDirection )
|
|
{
|
|
// Sent event for position update
|
|
EV_S_SETDESIREDDIRECTION SSetDesiredDirection;
|
|
|
|
SSetDesiredDirection.usSoldierID = pSoldier->ubID;
|
|
SSetDesiredDirection.usDesiredDirection = usDesiredDirection;
|
|
SSetDesiredDirection.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
|
|
|
|
AddGameEvent( S_SETDESIREDDIRECTION, 0, &SSetDesiredDirection );
|
|
|
|
}
|
|
|
|
void SendGetNewSoldierPathEvent( SOLDIERTYPE *pSoldier, UINT16 sDestGridNo, UINT16 usMovementAnim )
|
|
{
|
|
EV_S_GETNEWPATH SGetNewPath;
|
|
|
|
SGetNewPath.usSoldierID = pSoldier->ubID;
|
|
SGetNewPath.sDestGridNo = sDestGridNo;
|
|
SGetNewPath.usMovementAnim = usMovementAnim;
|
|
SGetNewPath.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
|
|
|
|
AddGameEvent( S_GETNEWPATH, 0, &SGetNewPath );
|
|
}
|
|
|
|
|
|
void SendChangeSoldierStanceEvent( SOLDIERTYPE *pSoldier, UINT8 ubNewStance )
|
|
{
|
|
#if 0
|
|
EV_S_CHANGESTANCE SChangeStance;
|
|
|
|
#ifdef NETWORKED
|
|
if( !IsTheSolderUnderMyControl( pSoldier->ubID) )
|
|
return;
|
|
#endif
|
|
|
|
SChangeStance.ubNewStance = ubNewStance;
|
|
SChangeStance.usSoldierID = pSoldier->ubID;
|
|
SChangeStance.sXPos = pSoldier->sX;
|
|
SChangeStance.sYPos = pSoldier->sY;
|
|
SChangeStance.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
|
|
|
|
AddGameEvent( S_CHANGESTANCE, 0, &SChangeStance );
|
|
#endif
|
|
|
|
ChangeSoldierStance( pSoldier, ubNewStance );
|
|
}
|
|
|
|
|
|
void SendBeginFireWeaponEvent( SOLDIERTYPE *pSoldier, INT16 sTargetGridNo )
|
|
{
|
|
EV_S_BEGINFIREWEAPON SBeginFireWeapon;
|
|
|
|
SBeginFireWeapon.usSoldierID = pSoldier->ubID;
|
|
SBeginFireWeapon.sTargetGridNo = sTargetGridNo;
|
|
SBeginFireWeapon.bTargetLevel = pSoldier->bTargetLevel;
|
|
SBeginFireWeapon.bTargetCubeLevel = pSoldier->bTargetCubeLevel;
|
|
SBeginFireWeapon.uiUniqueId = pSoldier -> uiUniqueSoldierIdValue;
|
|
|
|
AddGameEvent( S_BEGINFIREWEAPON, 0, &SBeginFireWeapon );
|
|
|
|
}
|
|
|
|
// This function just encapolates the check for turnbased and having an attacker in the first place
|
|
void ReleaseSoldiersAttacker( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT32 cnt;
|
|
UINT8 ubNumToFree;
|
|
|
|
//if ( gTacticalStatus.uiFlags & TURNBASED && (gTacticalStatus.uiFlags & INCOMBAT) )
|
|
{
|
|
// ATE: Removed...
|
|
//if ( pSoldier->ubAttackerID != NOBODY )
|
|
{
|
|
// JA2 Gold
|
|
// set next-to-previous attacker, so long as this isn't a repeat attack
|
|
if (pSoldier->ubPreviousAttackerID != pSoldier->ubAttackerID)
|
|
{
|
|
pSoldier->ubNextToPreviousAttackerID = pSoldier->ubPreviousAttackerID;
|
|
}
|
|
|
|
// get previous attacker id
|
|
pSoldier->ubPreviousAttackerID = pSoldier->ubAttackerID;
|
|
|
|
// Copy BeingAttackedCount here....
|
|
ubNumToFree = pSoldier->bBeingAttackedCount;
|
|
// Zero it out BEFORE, as supression may increase it again...
|
|
pSoldier->bBeingAttackedCount = 0;
|
|
|
|
for ( cnt = 0; cnt < ubNumToFree; cnt++ )
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Freeing up attacker of %d (attacker is %d) - releasesoldierattacker num to free is %d", pSoldier->ubID, pSoldier->ubAttackerID, ubNumToFree ) );
|
|
ReduceAttackBusyCount( pSoldier->ubAttackerID, FALSE );
|
|
}
|
|
|
|
// ATE: Set to NOBODY if this person is NOT dead
|
|
// otherise, we keep it so the kill can be awarded!
|
|
if ( pSoldier->bLife != 0 && pSoldier->ubBodyType != QUEENMONSTER )
|
|
{
|
|
pSoldier->ubAttackerID = NOBODY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOLEAN MercInWater( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// Our water texture , for now is of a given type
|
|
if ( pSoldier->bOverTerrainType == LOW_WATER || pSoldier->bOverTerrainType == MED_WATER || pSoldier->bOverTerrainType == DEEP_WATER )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
|
|
void RevivePlayerTeam( )
|
|
{
|
|
INT32 cnt;
|
|
SOLDIERTYPE *pSoldier;
|
|
|
|
// End the turn of player charactors
|
|
cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
|
|
|
|
// look for all mercs on the same team,
|
|
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++)
|
|
{
|
|
ReviveSoldier( pSoldier );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void ReviveSoldier( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT16 sX, sY;
|
|
|
|
if ( pSoldier->bLife < OKLIFE && pSoldier->bActive )
|
|
{
|
|
// If dead or unconscious, revive!
|
|
pSoldier->uiStatusFlags &= ( ~SOLDIER_DEAD );
|
|
|
|
pSoldier->bLife = pSoldier->bLifeMax;
|
|
pSoldier->bBleeding = 0;
|
|
pSoldier->ubDesiredHeight = ANIM_STAND;
|
|
|
|
AddManToTeam( pSoldier->bTeam );
|
|
|
|
// Set to standing
|
|
pSoldier->fInNonintAnim = FALSE;
|
|
pSoldier->fRTInNonintAnim = FALSE;
|
|
|
|
// Change to standing,unless we can getup with an animation
|
|
EVENT_InitNewSoldierAnim( pSoldier, STANDING, 0, TRUE );
|
|
BeginSoldierGetup( pSoldier );
|
|
|
|
// Makesure center of tile
|
|
sX = CenterX( pSoldier->sGridNo );
|
|
sY = CenterY( pSoldier->sGridNo );
|
|
|
|
EVENT_SetSoldierPosition( pSoldier, (FLOAT) sX, (FLOAT) sY );
|
|
|
|
// Dirty INterface
|
|
fInterfacePanelDirty = DIRTYLEVEL2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void HandleAnimationProfile( SOLDIERTYPE *pSoldier, UINT16 usAnimState, BOOLEAN fRemove )
|
|
{
|
|
//#if 0
|
|
ANIM_PROF *pProfile;
|
|
ANIM_PROF_DIR *pProfileDir;
|
|
ANIM_PROF_TILE *pProfileTile;
|
|
INT8 bProfileID;
|
|
UINT32 iTileCount;
|
|
INT16 sGridNo;
|
|
UINT16 usAnimSurface;
|
|
|
|
// ATE
|
|
|
|
// Get Surface Index
|
|
usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState );
|
|
|
|
CHECKV( usAnimSurface != INVALID_ANIMATION_SURFACE );
|
|
|
|
bProfileID = gAnimSurfaceDatabase[ usAnimSurface ].bProfile;
|
|
|
|
// Determine if this animation has a profile
|
|
if ( bProfileID != -1 )
|
|
{
|
|
// Getprofile
|
|
pProfile = &(gpAnimProfiles[ bProfileID ] );
|
|
|
|
// Get direction
|
|
pProfileDir = &( pProfile->Dirs[ pSoldier->bDirection ] );
|
|
|
|
// Loop tiles and set accordingly into world
|
|
for( iTileCount = 0; iTileCount < pProfileDir->ubNumTiles; iTileCount++ )
|
|
{
|
|
pProfileTile = &( pProfileDir->pTiles[ iTileCount ] );
|
|
|
|
sGridNo = pSoldier->sGridNo + ( ( WORLD_COLS * pProfileTile->bTileY ) + pProfileTile->bTileX );
|
|
|
|
// Check if in bounds
|
|
if ( !OutOfBounds( pSoldier->sGridNo, sGridNo ) )
|
|
{
|
|
if ( fRemove )
|
|
{
|
|
// Remove from world
|
|
RemoveMerc( sGridNo, pSoldier, TRUE );
|
|
}
|
|
else
|
|
{
|
|
// PLace into world
|
|
AddMercToHead( sGridNo, pSoldier, FALSE );
|
|
//if ( pProfileTile->bTileY != 0 || pProfileTile->bTileX != 0 )
|
|
{
|
|
gpWorldLevelData[sGridNo].pMercHead->uiFlags |= LEVELNODE_MERCPLACEHOLDER;
|
|
gpWorldLevelData[sGridNo].pMercHead->uiAnimHitLocationFlags = pProfileTile->usTileFlags;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//#endif
|
|
|
|
}
|
|
|
|
|
|
LEVELNODE *GetAnimProfileFlags( UINT16 sGridNo, UINT16 *usFlags, SOLDIERTYPE **ppTargSoldier, LEVELNODE *pGivenNode )
|
|
{
|
|
LEVELNODE *pNode;
|
|
|
|
(*ppTargSoldier) = NULL;
|
|
(*usFlags ) = 0;
|
|
|
|
if ( pGivenNode == NULL )
|
|
{
|
|
pNode = gpWorldLevelData[sGridNo].pMercHead;
|
|
}
|
|
else
|
|
{
|
|
pNode = pGivenNode->pNext;
|
|
}
|
|
|
|
//#if 0
|
|
|
|
if ( pNode != NULL )
|
|
{
|
|
if ( pNode->uiFlags & LEVELNODE_MERCPLACEHOLDER )
|
|
{
|
|
(*usFlags) = (UINT16)pNode->uiAnimHitLocationFlags;
|
|
(*ppTargSoldier) = pNode->pSoldier;
|
|
}
|
|
}
|
|
|
|
//#endif
|
|
|
|
return( pNode );
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN GetProfileFlagsFromGridno( SOLDIERTYPE *pSoldier, UINT16 usAnimState, UINT16 sTestGridNo, UINT16 *usFlags )
|
|
{
|
|
ANIM_PROF *pProfile;
|
|
ANIM_PROF_DIR *pProfileDir;
|
|
ANIM_PROF_TILE *pProfileTile;
|
|
INT8 bProfileID;
|
|
UINT32 iTileCount;
|
|
INT16 sGridNo;
|
|
UINT16 usAnimSurface;
|
|
|
|
// Get Surface Index
|
|
usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState );
|
|
|
|
CHECKF( usAnimSurface != INVALID_ANIMATION_SURFACE );
|
|
|
|
bProfileID = gAnimSurfaceDatabase[ usAnimSurface ].bProfile;
|
|
|
|
*usFlags = 0;
|
|
|
|
// Determine if this animation has a profile
|
|
if ( bProfileID != -1 )
|
|
{
|
|
// Getprofile
|
|
pProfile = &(gpAnimProfiles[ bProfileID ] );
|
|
|
|
// Get direction
|
|
pProfileDir = &( pProfile->Dirs[ pSoldier->bDirection ] );
|
|
|
|
// Loop tiles and set accordingly into world
|
|
for( iTileCount = 0; iTileCount < pProfileDir->ubNumTiles; iTileCount++ )
|
|
{
|
|
pProfileTile = &( pProfileDir->pTiles[ iTileCount ] );
|
|
|
|
sGridNo = pSoldier->sGridNo + ( ( WORLD_COLS * pProfileTile->bTileY ) + pProfileTile->bTileX );
|
|
|
|
// Check if in bounds
|
|
if ( !OutOfBounds( pSoldier->sGridNo, sGridNo ) )
|
|
{
|
|
if ( sGridNo == sTestGridNo )
|
|
{
|
|
*usFlags = pProfileTile->usTileFlags;
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
void EVENT_SoldierBeginGiveItem( SOLDIERTYPE *pSoldier )
|
|
{
|
|
SOLDIERTYPE *pTSoldier;
|
|
|
|
if ( VerifyGiveItem( pSoldier, &pTSoldier ) )
|
|
{
|
|
// CHANGE DIRECTION AND GOTO ANIMATION NOW
|
|
pSoldier->bDesiredDirection = pSoldier->bPendingActionData3;
|
|
pSoldier->bDirection = pSoldier->bPendingActionData3;
|
|
|
|
// begin animation
|
|
EVENT_InitNewSoldierAnim( pSoldier, GIVE_ITEM, 0 , FALSE );
|
|
|
|
}
|
|
else
|
|
{
|
|
UnSetEngagedInConvFromPCAction( pSoldier );
|
|
|
|
MemFree( pSoldier->pTempObject );
|
|
}
|
|
}
|
|
|
|
|
|
void EVENT_SoldierBeginBladeAttack( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
|
|
{
|
|
SOLDIERTYPE *pTSoldier;
|
|
//UINT32 uiMercFlags;
|
|
UINT16 usSoldierIndex;
|
|
UINT8 ubTDirection;
|
|
BOOLEAN fChangeDirection = FALSE;
|
|
ROTTING_CORPSE *pCorpse;
|
|
|
|
// Increment the number of people busy doing stuff because of an attack
|
|
//if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) )
|
|
//{
|
|
gTacticalStatus.ubAttackBusyCount++;
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Begin blade attack: ATB %d", gTacticalStatus.ubAttackBusyCount) );
|
|
|
|
//}
|
|
|
|
// CHANGE DIRECTION AND GOTO ANIMATION NOW
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
|
|
EVENT_SetSoldierDirection( pSoldier, ubDirection );
|
|
// CHANGE TO ANIMATION
|
|
|
|
// DETERMINE ANIMATION TO PLAY
|
|
// LATER BASED ON IF TAREGT KNOWS OF US, STANCE, ETC
|
|
// GET POINTER TO TAREGT
|
|
if (pSoldier->uiStatusFlags & SOLDIER_MONSTER)
|
|
{
|
|
UINT8 ubTargetID;
|
|
|
|
// Is there an unconscious guy at gridno......
|
|
ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel );
|
|
|
|
if ( ubTargetID != NOBODY && ( ( MercPtrs[ ubTargetID ]->bLife < OKLIFE && MercPtrs[ ubTargetID ]->bLife > 0 ) || ( MercPtrs[ ubTargetID ]->bBreath < OKBREATH && MercPtrs[ ubTargetID ]->bCollapsed ) ) )
|
|
{
|
|
pSoldier->uiPendingActionData4 = ubTargetID;
|
|
// add regen bonus
|
|
pSoldier->bRegenerationCounter++;
|
|
EVENT_InitNewSoldierAnim( pSoldier, MONSTER_BEGIN_EATTING_FLESH, 0, FALSE );
|
|
}
|
|
else
|
|
{
|
|
if ( PythSpacesAway( pSoldier->sGridNo, sGridNo ) <= 1 )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, MONSTER_CLOSE_ATTACK, 0, FALSE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, ADULTMONSTER_ATTACKING, 0, FALSE );
|
|
}
|
|
}
|
|
}
|
|
else if (pSoldier->ubBodyType == BLOODCAT)
|
|
{
|
|
// Check if it's a claws or teeth...
|
|
if ( pSoldier->inv[ HANDPOS ].usItem == BLOODCAT_CLAW_ATTACK )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, BLOODCAT_SWIPE, 0, FALSE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, BLOODCAT_BITE_ANIM, 0, FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
usSoldierIndex = WhoIsThere2( sGridNo, pSoldier->bTargetLevel );
|
|
if ( usSoldierIndex != NOBODY )
|
|
{
|
|
GetSoldier( &pTSoldier, usSoldierIndex );
|
|
|
|
// Look at stance of target
|
|
switch( gAnimControl[ pTSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
case ANIM_CROUCH:
|
|
|
|
// CHECK IF HE CAN SEE US, IF SO RANDOMIZE
|
|
if ( pTSoldier->bOppList[ pSoldier->ubID ] == 0 && pTSoldier->bTeam != pSoldier->bTeam )
|
|
{
|
|
// WE ARE NOT SEEN
|
|
EVENT_InitNewSoldierAnim( pSoldier, STAB, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
// WE ARE SEEN
|
|
if ( Random( 50 ) > 25 )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, STAB, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, SLICE, 0 , FALSE );
|
|
}
|
|
|
|
// IF WE ARE SEEN, MAKE SURE GUY TURNS!
|
|
// Get direction to target
|
|
// IF WE ARE AN ANIMAL, CAR, MONSTER, DONT'T TURN
|
|
if ( !( pTSoldier->uiStatusFlags & ( SOLDIER_MONSTER | SOLDIER_ANIMAL | SOLDIER_VEHICLE ) ) )
|
|
{
|
|
// OK, stop merc....
|
|
EVENT_StopMerc( pTSoldier, pTSoldier->sGridNo, pTSoldier->bDirection );
|
|
|
|
if ( pTSoldier->bTeam != gbPlayerNum )
|
|
{
|
|
CancelAIAction( pTSoldier, TRUE );
|
|
}
|
|
|
|
ubTDirection = (UINT8)GetDirectionFromGridNo( pSoldier->sGridNo, pTSoldier );
|
|
SendSoldierSetDesiredDirectionEvent( pTSoldier, ubTDirection );
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
|
|
// CHECK OUR STANCE
|
|
if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH )
|
|
{
|
|
// SET DESIRED STANCE AND SET PENDING ANIMATION
|
|
SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
|
|
pSoldier->usPendingAnimation = CROUCH_STAB;
|
|
}
|
|
else
|
|
{
|
|
// USE crouched one
|
|
// NEED TO CHANGE STANCE IF NOT CROUCHD!
|
|
EVENT_InitNewSoldierAnim( pSoldier, CROUCH_STAB, 0 , FALSE );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// OK, SEE IF THERE IS AN OBSTACLE HERE...
|
|
if ( !NewOKDestination( pSoldier, sGridNo, FALSE, pSoldier->bLevel ) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, STAB, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
// Check for corpse!
|
|
pCorpse = GetCorpseAtGridNo( sGridNo, pSoldier->bLevel );
|
|
|
|
if ( pCorpse == NULL )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, CROUCH_STAB, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
if ( IsValidDecapitationCorpse( pCorpse ) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, DECAPITATE, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, CROUCH_STAB, 0 , FALSE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// SET TARGET GRIDNO
|
|
pSoldier->sTargetGridNo = sGridNo;
|
|
pSoldier->bTargetLevel = pSoldier->bLevel;
|
|
pSoldier->ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel );
|
|
}
|
|
|
|
|
|
void EVENT_SoldierBeginPunchAttack( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
|
|
{
|
|
BOOLEAN fMartialArtist = FALSE;
|
|
SOLDIERTYPE *pTSoldier;
|
|
//UINT32 uiMercFlags;
|
|
UINT16 usSoldierIndex;
|
|
UINT8 ubTDirection;
|
|
BOOLEAN fChangeDirection = FALSE;
|
|
UINT16 usItem;
|
|
|
|
// Get item in hand...
|
|
usItem = pSoldier->inv[ HANDPOS ].usItem;
|
|
|
|
|
|
// Increment the number of people busy doing stuff because of an attack
|
|
//if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) )
|
|
//{
|
|
gTacticalStatus.ubAttackBusyCount++;
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Begin HTH attack: ATB %d", gTacticalStatus.ubAttackBusyCount) );
|
|
|
|
//}
|
|
|
|
// get target.....
|
|
usSoldierIndex = WhoIsThere2( pSoldier->sTargetGridNo, pSoldier->bLevel );
|
|
if ( usSoldierIndex != NOBODY )
|
|
{
|
|
GetSoldier( &pTSoldier, usSoldierIndex );
|
|
|
|
fChangeDirection = TRUE;
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
if ( fChangeDirection )
|
|
{
|
|
// CHANGE DIRECTION AND GOTO ANIMATION NOW
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
|
|
EVENT_SetSoldierDirection( pSoldier, ubDirection );
|
|
}
|
|
|
|
|
|
// Are we a martial artist?
|
|
if ( HAS_SKILL_TRAIT( pSoldier, MARTIALARTS ) )
|
|
{
|
|
fMartialArtist = TRUE;
|
|
}
|
|
|
|
|
|
if ( fMartialArtist && !AreInMeanwhile( ) && !Item[usItem].crowbar )
|
|
{
|
|
// Are we in attack mode yet?
|
|
if ( pSoldier->usAnimState != NINJA_BREATH && gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_STAND && gAnimControl[ pTSoldier->usAnimState ].ubHeight != ANIM_PRONE )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, NINJA_GOTOBREATH, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
DoNinjaAttack( pSoldier );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Look at stance of target
|
|
switch( gAnimControl[ pTSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
case ANIM_CROUCH:
|
|
|
|
if ( !Item[usItem].crowbar )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, PUNCH, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, CROWBAR_ATTACK, 0 , FALSE );
|
|
}
|
|
|
|
// CHECK IF HE CAN SEE US, IF SO CHANGE DIR
|
|
if ( pTSoldier->bOppList[ pSoldier->ubID ] == 0 && pTSoldier->bTeam != pSoldier->bTeam )
|
|
{
|
|
// Get direction to target
|
|
// IF WE ARE AN ANIMAL, CAR, MONSTER, DONT'T TURN
|
|
if ( !( pTSoldier->uiStatusFlags & ( SOLDIER_MONSTER | SOLDIER_ANIMAL | SOLDIER_VEHICLE ) ) )
|
|
{
|
|
// OK, stop merc....
|
|
EVENT_StopMerc( pTSoldier, pTSoldier->sGridNo, pTSoldier->bDirection );
|
|
|
|
if ( pTSoldier->bTeam != gbPlayerNum )
|
|
{
|
|
CancelAIAction( pTSoldier, TRUE );
|
|
}
|
|
|
|
ubTDirection = (UINT8)GetDirectionFromGridNo( pSoldier->sGridNo, pTSoldier );
|
|
SendSoldierSetDesiredDirectionEvent( pTSoldier, ubTDirection );
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
|
|
// CHECK OUR STANCE
|
|
// ATE: Added this for CIV body types 'cause of elliot
|
|
if ( !IS_MERC_BODY_TYPE( pSoldier ) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, PUNCH, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH )
|
|
{
|
|
// SET DESIRED STANCE AND SET PENDING ANIMATION
|
|
SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
|
|
pSoldier->usPendingAnimation = PUNCH_LOW;
|
|
}
|
|
else
|
|
{
|
|
// USE crouched one
|
|
// NEED TO CHANGE STANCE IF NOT CROUCHD!
|
|
EVENT_InitNewSoldierAnim( pSoldier, PUNCH_LOW, 0 , FALSE );
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// SET TARGET GRIDNO
|
|
pSoldier->sTargetGridNo = sGridNo;
|
|
pSoldier->bTargetLevel = pSoldier->bLevel;
|
|
pSoldier->sLastTarget = sGridNo;
|
|
pSoldier->ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel );
|
|
}
|
|
|
|
|
|
void EVENT_SoldierBeginKnifeThrowAttack( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
|
|
{
|
|
// Increment the number of people busy doing stuff because of an attack
|
|
//if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) )
|
|
//{
|
|
gTacticalStatus.ubAttackBusyCount++;
|
|
//}
|
|
pSoldier->bBulletsLeft = 1;
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting knifethrow attack, bullets left %d", pSoldier->bBulletsLeft) );
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, THROW_KNIFE, 0 , FALSE );
|
|
|
|
// CHANGE DIRECTION AND GOTO ANIMATION NOW
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
|
|
EVENT_SetSoldierDirection( pSoldier, ubDirection );
|
|
|
|
|
|
// SET TARGET GRIDNO
|
|
pSoldier->sTargetGridNo = sGridNo;
|
|
pSoldier->sLastTarget = sGridNo;
|
|
pSoldier->fTurningFromPronePosition = 0;
|
|
// NB target level must be set by functions outside of here... but I think it
|
|
// is already set in HandleItem or in the AI code - CJC
|
|
pSoldier->ubTargetID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel );
|
|
}
|
|
|
|
|
|
void EVENT_SoldierBeginDropBomb( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// Increment the number of people busy doing stuff because of an attack
|
|
switch( gAnimControl[ pSoldier->usAnimState ].ubHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, PLANT_BOMB, 0 , FALSE );
|
|
break;
|
|
|
|
default:
|
|
|
|
// Call hander for planting bomb...
|
|
HandleSoldierDropBomb( pSoldier, pSoldier->sPendingActionData2 );
|
|
SoldierGotoStationaryStance( pSoldier );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void EVENT_SoldierBeginUseDetonator( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// Increment the number of people busy doing stuff because of an attack
|
|
switch( gAnimControl[ pSoldier->usAnimState ].ubHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, USE_REMOTE, 0 , FALSE );
|
|
break;
|
|
|
|
default:
|
|
|
|
// Call hander for planting bomb...
|
|
HandleSoldierUseRemote( pSoldier, pSoldier->sPendingActionData2 );
|
|
break;
|
|
}
|
|
}
|
|
|
|
void EVENT_SoldierBeginFirstAid( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
|
|
{
|
|
SOLDIERTYPE *pTSoldier;
|
|
//UINT32 uiMercFlags;
|
|
UINT16 usSoldierIndex;
|
|
BOOLEAN fRefused = FALSE;
|
|
|
|
usSoldierIndex = WhoIsThere2( sGridNo, pSoldier->bLevel );
|
|
if ( usSoldierIndex != NOBODY )
|
|
{
|
|
pTSoldier = MercPtrs[ usSoldierIndex ];
|
|
|
|
// OK, check if we should play quote...
|
|
if ( pTSoldier->bTeam != gbPlayerNum )
|
|
{
|
|
if ( pTSoldier->ubProfile != NO_PROFILE && pTSoldier->ubProfile >= FIRST_RPC && pTSoldier->ubProfile < GASTON && !RPC_RECRUITED( pTSoldier ) )
|
|
{
|
|
fRefused = PCDoesFirstAidOnNPC( pTSoldier->ubProfile );
|
|
}
|
|
|
|
if ( !fRefused )
|
|
{
|
|
if ( CREATURE_OR_BLOODCAT( pTSoldier ) )
|
|
{
|
|
// nope!!
|
|
fRefused = TRUE;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, Message[ STR_REFUSE_FIRSTAID_FOR_CREATURE ] );
|
|
}
|
|
else if ( !pTSoldier->bNeutral && pTSoldier->bLife >= OKLIFE && pTSoldier->bSide != pSoldier->bSide )
|
|
{
|
|
fRefused = TRUE;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, Message[ STR_REFUSE_FIRSTAID ] );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if ( fRefused )
|
|
{
|
|
UnSetUIBusy( pSoldier->ubID );
|
|
return;
|
|
}
|
|
|
|
// ATE: We can only give firsty aid to one perosn at a time... cancel
|
|
// any now...
|
|
InternalGivingSoldierCancelServices( pSoldier, FALSE );
|
|
|
|
// CHANGE DIRECTION AND GOTO ANIMATION NOW
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
|
|
EVENT_SetSoldierDirection( pSoldier, ubDirection );
|
|
|
|
// CHECK OUR STANCE AND GOTO CROUCH IF NEEDED
|
|
//if ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_CROUCH )
|
|
//{
|
|
// SET DESIRED STANCE AND SET PENDING ANIMATION
|
|
// SendChangeSoldierStanceEvent( pSoldier, ANIM_CROUCH );
|
|
// pSoldier->usPendingAnimation = START_AID;
|
|
//}
|
|
//else
|
|
{
|
|
// CHANGE TO ANIMATION
|
|
EVENT_InitNewSoldierAnim( pSoldier, START_AID, 0 , FALSE );
|
|
}
|
|
|
|
// SET TARGET GRIDNO
|
|
pSoldier->sTargetGridNo = sGridNo;
|
|
|
|
// SET PARTNER ID
|
|
pSoldier->ubServicePartner = (UINT8)usSoldierIndex;
|
|
|
|
// SET PARTNER'S COUNT REFERENCE
|
|
pTSoldier->ubServiceCount++;
|
|
|
|
// If target and doer are no the same guy...
|
|
if ( pTSoldier->ubID != pSoldier->ubID && !pTSoldier->bCollapsed )
|
|
{
|
|
SoldierGotoStationaryStance( pTSoldier );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void EVENT_SoldierEnterVehicle( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
|
|
{
|
|
SOLDIERTYPE *pTSoldier;
|
|
UINT32 uiMercFlags;
|
|
UINT16 usSoldierIndex;
|
|
|
|
if ( FindSoldier( sGridNo, &usSoldierIndex, &uiMercFlags, FIND_SOLDIER_GRIDNO ) )
|
|
{
|
|
pTSoldier = MercPtrs[ usSoldierIndex ];
|
|
|
|
// Enter vehicle...
|
|
EnterVehicle( pTSoldier, pSoldier );
|
|
}
|
|
|
|
UnSetUIBusy( pSoldier->ubID );
|
|
}
|
|
|
|
|
|
UINT32 SoldierDressWound( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pVictim, INT16 sKitPts, INT16 sStatus )
|
|
{
|
|
UINT32 uiDressSkill, uiPossible, uiActual, uiMedcost, uiDeficiency, uiAvailAPs, uiUsedAPs;
|
|
UINT8 ubBelowOKlife, ubPtsLeft;
|
|
BOOLEAN fRanOut = FALSE;
|
|
|
|
if (pVictim->bBleeding < 1 && pVictim->bLife >= OKLIFE )
|
|
{
|
|
return(0); // nothing to do, shouldn't have even been called!
|
|
}
|
|
|
|
if ( pVictim->bLife == 0 )
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
// in case he has multiple kits in hand, limit influence of kit status to 100%!
|
|
if (sStatus >= 100)
|
|
{
|
|
sStatus = 100;
|
|
}
|
|
|
|
// calculate wound-dressing skill (3x medical, 2x equip, 1x level, 1x dex)
|
|
uiDressSkill = ( ( 3 * EffectiveMedical( pSoldier ) ) + // medical knowledge
|
|
( 2 * sStatus) + // state of medical kit
|
|
(10 * EffectiveExpLevel( pSoldier ) ) + // battle injury experience
|
|
EffectiveDexterity( pSoldier ) ) / 7; // general "handiness"
|
|
|
|
// try to use every AP that the merc has left
|
|
uiAvailAPs = pSoldier->bActionPoints;
|
|
|
|
// OK, If we are in real-time, use another value...
|
|
if (!(gTacticalStatus.uiFlags & TURNBASED) || !(gTacticalStatus.uiFlags & INCOMBAT ) )
|
|
{
|
|
// Set to a value which looks good based on our tactical turns duration
|
|
uiAvailAPs = RT_FIRST_AID_GAIN_MODIFIER;
|
|
}
|
|
|
|
// calculate how much bandaging CAN be done this turn
|
|
uiPossible = ( uiAvailAPs * uiDressSkill ) / 50; // max rate is 2 * fullAPs
|
|
|
|
// if no healing is possible (insufficient APs or insufficient dressSkill)
|
|
if (!uiPossible)
|
|
return(0);
|
|
|
|
|
|
if (Item[pSoldier->inv[ HANDPOS ].usItem].medicalkit ) // using the GOOD medic stuff
|
|
{
|
|
uiPossible += ( uiPossible / 2); // add extra 50 %
|
|
}
|
|
|
|
uiActual = uiPossible; // start by assuming maximum possible
|
|
|
|
|
|
// figure out how far below OKLIFE the victim is
|
|
if (pVictim->bLife >= OKLIFE)
|
|
{
|
|
ubBelowOKlife = 0;
|
|
}
|
|
else
|
|
{
|
|
ubBelowOKlife = OKLIFE - pVictim->bLife;
|
|
}
|
|
|
|
// figure out how many healing pts we need to stop dying (2x cost)
|
|
uiDeficiency = (2 * ubBelowOKlife );
|
|
|
|
// if, after that, the patient will still be bleeding
|
|
if ( (pVictim->bBleeding - ubBelowOKlife ) > 0)
|
|
{
|
|
// then add how many healing pts we need to stop bleeding (1x cost)
|
|
uiDeficiency += ( pVictim->bBleeding - ubBelowOKlife );
|
|
}
|
|
|
|
// now, make sure we weren't going to give too much
|
|
if ( uiActual > uiDeficiency) // if we were about to apply too much
|
|
uiActual = uiDeficiency; // reduce actual not to waste anything
|
|
|
|
|
|
// now make sure we HAVE that much
|
|
if (Item[pSoldier->inv[ HANDPOS ].usItem].medicalkit )
|
|
{
|
|
uiMedcost = (uiActual + 1) / 2; // cost is only half, rounded up
|
|
|
|
if ( uiMedcost > (UINT32)sKitPts ) // if we can't afford this
|
|
{
|
|
fRanOut = TRUE;
|
|
uiMedcost = sKitPts; // what CAN we afford?
|
|
uiActual = uiMedcost * 2; // give double this as aid
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uiMedcost = uiActual;
|
|
|
|
if ( uiMedcost > (UINT32)sKitPts) // can't afford it
|
|
{
|
|
fRanOut = TRUE;
|
|
uiMedcost = uiActual = sKitPts; // recalc cost AND aid
|
|
}
|
|
}
|
|
|
|
ubPtsLeft = (UINT8)uiActual;
|
|
|
|
|
|
// heal real life points first (if below OKLIFE) because we don't want the
|
|
// patient still DYING if bandages run out, or medic is disabled/distracted!
|
|
// NOTE: Dressing wounds for life below OKLIFE now costs 2 pts/life point!
|
|
if ( ubPtsLeft && pVictim->bLife < OKLIFE)
|
|
{
|
|
// if we have enough points to bring him all the way to OKLIFE this turn
|
|
if ( ubPtsLeft >= (2 * ubBelowOKlife ) )
|
|
{
|
|
// raise life to OKLIFE
|
|
pVictim->bLife = OKLIFE;
|
|
|
|
// reduce bleeding by the same number of life points healed up
|
|
pVictim->bBleeding -= ubBelowOKlife;
|
|
|
|
// use up appropriate # of actual healing points
|
|
ubPtsLeft -= (2 * ubBelowOKlife);
|
|
}
|
|
else
|
|
{
|
|
pVictim->bLife += ( ubPtsLeft / 2);
|
|
pVictim->bBleeding -= ( ubPtsLeft / 2);
|
|
|
|
ubPtsLeft = ubPtsLeft % 2; // if ptsLeft was odd, ptsLeft = 1
|
|
}
|
|
|
|
// this should never happen any more, but make sure bleeding not negative
|
|
if (pVictim->bBleeding < 0)
|
|
{
|
|
pVictim->bBleeding = 0;
|
|
}
|
|
|
|
// if this healing brought the patient out of the worst of it, cancel dying
|
|
if (pVictim->bLife >= OKLIFE )
|
|
{
|
|
//pVictim->dying = pVictim->dyingComment = FALSE;
|
|
//pVictim->shootOn = TRUE;
|
|
|
|
// turn off merc QUOTE flags
|
|
pVictim->fDyingComment = FALSE;
|
|
|
|
}
|
|
|
|
// update patient's entire panel (could have regained consciousness, etc.)
|
|
}
|
|
|
|
|
|
// if any healing points remain, apply that to any remaining bleeding (1/1)
|
|
// DON'T spend any APs/kit pts to cure bleeding until merc is no longer dying
|
|
//if ( ubPtsLeft && pVictim->bBleeding && !pVictim->dying)
|
|
if ( ubPtsLeft && pVictim->bBleeding )
|
|
{
|
|
// if we have enough points to bandage all remaining bleeding this turn
|
|
if (ubPtsLeft >= pVictim->bBleeding )
|
|
{
|
|
ubPtsLeft -= pVictim->bBleeding;
|
|
pVictim->bBleeding = 0;
|
|
}
|
|
else // bandage what we can
|
|
{
|
|
pVictim->bBleeding -= ubPtsLeft;
|
|
ubPtsLeft = 0;
|
|
}
|
|
|
|
// update patient's life bar only
|
|
}
|
|
|
|
|
|
// if wound has been dressed enough so that bleeding won't occur, turn off
|
|
// the "warned about bleeding" flag so merc tells us about the next bleeding
|
|
if ( pVictim->bBleeding <= MIN_BLEEDING_THRESHOLD )
|
|
{
|
|
pVictim->fWarnedAboutBleeding = FALSE;
|
|
}
|
|
|
|
|
|
// if there are any ptsLeft now, then we didn't actually get to use them
|
|
uiActual -= ubPtsLeft;
|
|
|
|
// usedAPs equals (actionPts) * (%of possible points actually used)
|
|
uiUsedAPs = ( uiActual * uiAvailAPs ) / uiPossible;
|
|
|
|
if (Item[pSoldier->inv[ HANDPOS ].usItem].medicalkit ) // using the GOOD medic stuff
|
|
{
|
|
uiUsedAPs = ( uiUsedAPs * 2) / 3; // reverse 50% bonus by taking 2/3rds
|
|
}
|
|
|
|
DeductPoints( pSoldier, (INT16)uiUsedAPs, (INT16)( ( uiUsedAPs * BP_PER_AP_LT_EFFORT) ) );
|
|
|
|
|
|
if ( PTR_OURTEAM )
|
|
{
|
|
// MEDICAL GAIN (actual / 2): Helped someone by giving first aid
|
|
StatChange(pSoldier, MEDICALAMT, (UINT16)(uiActual / 2), FALSE);
|
|
|
|
// DEXTERITY GAIN (actual / 6): Helped someone by giving first aid
|
|
StatChange(pSoldier, DEXTAMT, (UINT16)(uiActual / 6), FALSE);
|
|
}
|
|
|
|
return( uiMedcost );
|
|
}
|
|
|
|
|
|
void InternalReceivingSoldierCancelServices( SOLDIERTYPE *pSoldier, BOOLEAN fPlayEndAnim )
|
|
{
|
|
SOLDIERTYPE *pTSoldier;
|
|
INT32 cnt;
|
|
|
|
if ( pSoldier->ubServiceCount > 0 )
|
|
{
|
|
// Loop through guys who have us as servicing
|
|
for ( pTSoldier = Menptr, cnt = 0; cnt < MAX_NUM_SOLDIERS; pTSoldier++, cnt++ )
|
|
{
|
|
if ( pTSoldier->bActive )
|
|
{
|
|
if ( pTSoldier->ubServicePartner == pSoldier->ubID )
|
|
{
|
|
// END SERVICE!
|
|
pSoldier->ubServiceCount--;
|
|
|
|
pTSoldier->ubServicePartner = NOBODY;
|
|
|
|
if ( gTacticalStatus.fAutoBandageMode )
|
|
{
|
|
pSoldier->ubAutoBandagingMedic = NOBODY;
|
|
|
|
ActionDone( pTSoldier );
|
|
}
|
|
else
|
|
{
|
|
// don't use end aid animation in autobandage
|
|
if ( pTSoldier->bLife >= OKLIFE && pTSoldier->bBreath > 0 && fPlayEndAnim )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pTSoldier, END_AID, 0 , FALSE );
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void ReceivingSoldierCancelServices( SOLDIERTYPE *pSoldier )
|
|
{
|
|
InternalReceivingSoldierCancelServices( pSoldier, TRUE );
|
|
}
|
|
|
|
|
|
void InternalGivingSoldierCancelServices( SOLDIERTYPE *pSoldier, BOOLEAN fPlayEndAnim )
|
|
{
|
|
SOLDIERTYPE *pTSoldier;
|
|
|
|
// GET TARGET SOLDIER
|
|
if ( pSoldier->ubServicePartner != NOBODY )
|
|
{
|
|
pTSoldier = MercPtrs[ pSoldier->ubServicePartner ];
|
|
|
|
// END SERVICE!
|
|
pTSoldier->ubServiceCount--;
|
|
|
|
pSoldier->ubServicePartner = NOBODY;
|
|
|
|
if ( gTacticalStatus.fAutoBandageMode )
|
|
{
|
|
pTSoldier->ubAutoBandagingMedic = NOBODY;
|
|
|
|
ActionDone( pSoldier );
|
|
}
|
|
else
|
|
{
|
|
if ( pSoldier->bLife >= OKLIFE && pSoldier->bBreath > 0 && fPlayEndAnim )
|
|
{
|
|
// don't use end aid animation in autobandage
|
|
EVENT_InitNewSoldierAnim( pSoldier, END_AID, 0 , FALSE );
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void GivingSoldierCancelServices( SOLDIERTYPE *pSoldier )
|
|
{
|
|
InternalGivingSoldierCancelServices( pSoldier, TRUE );
|
|
}
|
|
|
|
|
|
void HaultSoldierFromSighting( SOLDIERTYPE *pSoldier, BOOLEAN fFromSightingEnemy )
|
|
{
|
|
// SEND HUALT EVENT!
|
|
//EV_S_STOP_MERC SStopMerc;
|
|
|
|
//SStopMerc.sGridNo = pSoldier->sGridNo;
|
|
//SStopMerc.bDirection = pSoldier->bDirection;
|
|
//SStopMerc.usSoldierID = pSoldier->ubID;
|
|
//AddGameEvent( S_STOP_MERC, 0, &SStopMerc );
|
|
|
|
// If we are a 'specialmove... ignore...
|
|
if ( ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_SPECIALMOVE ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// OK, check if we were going to throw something, and give it back if so!
|
|
if ( pSoldier->pTempObject != NULL && fFromSightingEnemy )
|
|
{
|
|
// Place it back into inv....
|
|
AutoPlaceObject( pSoldier, pSoldier->pTempObject, FALSE );
|
|
MemFree( pSoldier->pTempObject );
|
|
pSoldier->pTempObject = NULL;
|
|
pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
|
|
pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION;
|
|
|
|
// Decrement attack counter...
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending throw because saw something") );
|
|
ReduceAttackBusyCount( pSoldier->ubID, FALSE );
|
|
|
|
// ATE: Goto stationary stance......
|
|
SoldierGotoStationaryStance( pSoldier );
|
|
|
|
DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
|
|
}
|
|
|
|
|
|
// Kaiden: Added fix from UB for seeing new enemies when throwing Knives.
|
|
// ATE: Dave, don't kill me
|
|
// Here, we need to handle the situation when we're throweing a knife and we see somebody
|
|
// cause for some reason throwing a knie does not use the pTempObject stuff that all other stuff does...
|
|
if ( pSoldier->usPendingAnimation == THROW_KNIFE )
|
|
{
|
|
// Decrement attack counter...
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending throw knife because saw something") );
|
|
ReduceAttackBusyCount( pSoldier->ubID, FALSE );
|
|
|
|
// ATE: Goto stationary stance......
|
|
SoldierGotoStationaryStance( pSoldier );
|
|
|
|
DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
|
|
}
|
|
|
|
|
|
|
|
if ( !( gTacticalStatus.uiFlags & INCOMBAT ) )
|
|
{
|
|
EVENT_StopMerc( pSoldier, pSoldier->sGridNo, pSoldier->bDirection );
|
|
}
|
|
else
|
|
{
|
|
// Pause this guy from no APS
|
|
AdjustNoAPToFinishMove( pSoldier, TRUE );
|
|
|
|
pSoldier->ubReasonCantFinishMove = REASON_STOPPED_SIGHT;
|
|
|
|
// ATE; IF turning to shoot, stop!
|
|
// ATE: We want to do this only for enemies, not items....
|
|
if ( pSoldier->fTurningToShoot && fFromSightingEnemy )
|
|
{
|
|
pSoldier->fTurningToShoot = FALSE;
|
|
// Release attacker
|
|
|
|
// OK - this is hightly annoying , but due to the huge combinations of
|
|
// things that can happen - 1 of them is that sLastTarget will get unset
|
|
// after turn is done - so set flag here to tell it not to...
|
|
pSoldier->fDontUnsetLastTargetFromTurn = TRUE;
|
|
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending fire because saw something") );
|
|
ReduceAttackBusyCount( pSoldier->ubID, FALSE );
|
|
}
|
|
|
|
// OK, if we are stopped at our destination, cancel pending action...
|
|
if ( fFromSightingEnemy )
|
|
{
|
|
if ( pSoldier->ubPendingAction != NO_PENDING_ACTION && pSoldier->sGridNo == pSoldier->sFinalDestination )
|
|
{
|
|
pSoldier->ubPendingAction = NO_PENDING_ACTION;
|
|
}
|
|
|
|
// Stop pending animation....
|
|
pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
|
|
pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION;
|
|
|
|
// Kaiden: Another UB Bug Fix.
|
|
// ATE: Nasty bug - clear out any fence jumping that may be in progress
|
|
pSoldier->fContinueMoveAfterStanceChange = FALSE;
|
|
}
|
|
|
|
if ( !pSoldier->fTurningToShoot )
|
|
{
|
|
pSoldier->fTurningFromPronePosition = FALSE;
|
|
}
|
|
}
|
|
|
|
// Unset UI!
|
|
if ( fFromSightingEnemy || ( pSoldier->pTempObject == NULL && !pSoldier->fTurningToShoot ) )
|
|
{
|
|
UnSetUIBusy( pSoldier->ubID );
|
|
}
|
|
|
|
pSoldier->bTurningFromUI = FALSE;
|
|
|
|
UnSetEngagedInConvFromPCAction( pSoldier );
|
|
}
|
|
|
|
|
|
// HUALT EVENT IS USED TO STOP A MERC - NETWORKING SHOULD CHECK / ADJUST TO GRIDNO?
|
|
void EVENT_StopMerc( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bDirection )
|
|
{
|
|
INT16 sX, sY;
|
|
|
|
// MOVE GUY TO GRIDNO--- SHOULD BE THE SAME UNLESS IN MULTIPLAYER
|
|
// Makesure center of tile
|
|
sX = CenterX( sGridNo );
|
|
sY = CenterY( sGridNo );
|
|
|
|
|
|
//Cancel pending events
|
|
if ( !pSoldier->fDelayedMovement )
|
|
{
|
|
pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
|
|
pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION;
|
|
pSoldier->ubPendingDirection = NO_PENDING_DIRECTION;
|
|
pSoldier->ubPendingAction = NO_PENDING_ACTION;
|
|
}
|
|
|
|
pSoldier->bEndDoorOpenCode = 0;
|
|
pSoldier->fTurningFromPronePosition = 0;
|
|
|
|
// Cancel path data!
|
|
pSoldier->usPathIndex = pSoldier->usPathDataSize = 0;
|
|
|
|
// Set ext tile waiting flag off!
|
|
pSoldier->fDelayedMovement = FALSE;
|
|
|
|
// Turn off reverse...
|
|
pSoldier->bReverse = FALSE;
|
|
|
|
EVENT_SetSoldierPosition( pSoldier, (FLOAT) sX, (FLOAT) sY );
|
|
pSoldier->sDestXPos = (INT16)pSoldier->dXPos;
|
|
pSoldier->sDestYPos = (INT16)pSoldier->dYPos;
|
|
EVENT_SetSoldierDirection(pSoldier, bDirection);
|
|
|
|
if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_MOVING )
|
|
{
|
|
SoldierGotoStationaryStance( pSoldier );
|
|
}
|
|
|
|
// ATE; IF turning to shoot, stop!
|
|
if ( pSoldier->fTurningToShoot )
|
|
{
|
|
pSoldier->fTurningToShoot = FALSE;
|
|
// Release attacker
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending fire because saw something") );
|
|
ReduceAttackBusyCount( pSoldier->ubID, FALSE );
|
|
}
|
|
|
|
// Turn off multi-move speed override....
|
|
if ( pSoldier->sGridNo == pSoldier->sFinalDestination )
|
|
{
|
|
pSoldier->fUseMoverrideMoveSpeed = FALSE;
|
|
}
|
|
|
|
// Unset UI!
|
|
UnSetUIBusy( pSoldier->ubID );
|
|
|
|
UnMarkMovementReserved( pSoldier );
|
|
|
|
}
|
|
|
|
|
|
void ReLoadSoldierAnimationDueToHandItemChange( SOLDIERTYPE *pSoldier, UINT16 usOldItem, UINT16 usNewItem )
|
|
{
|
|
// DON'T continue aiming!
|
|
// GOTO STANCE
|
|
// CHECK FOR AIMING ANIMATIONS
|
|
BOOLEAN fOldRifle = FALSE;
|
|
BOOLEAN fNewRifle = FALSE;
|
|
|
|
// Shutoff burst....
|
|
// ( we could be on, then change gun that does not have burst )
|
|
if ( Weapon[ usNewItem ].ubShotsPerBurst == 0 && !Weapon[pSoldier->inv[HANDPOS].usItem ].NoSemiAuto )
|
|
{
|
|
pSoldier->bDoBurst = FALSE;
|
|
pSoldier->bWeaponMode = WM_NORMAL;
|
|
pSoldier->bDoAutofire = 0;
|
|
}
|
|
else if ( Weapon[usNewItem].NoSemiAuto )
|
|
{
|
|
pSoldier->bDoBurst = TRUE;
|
|
pSoldier->bWeaponMode = WM_AUTOFIRE;
|
|
pSoldier->bDoAutofire = 1;
|
|
}
|
|
|
|
if ( gAnimControl[ pSoldier->usAnimState ].uiFlags & ANIM_FIREREADY )
|
|
{
|
|
// Stop aiming!
|
|
SoldierGotoStationaryStance( pSoldier );
|
|
}
|
|
|
|
// Cancel services...
|
|
GivingSoldierCancelServices( pSoldier );
|
|
|
|
// Did we have a rifle and do we now not have one?
|
|
if ( usOldItem != NOTHING )
|
|
{
|
|
if ( Item[ usOldItem ].usItemClass == IC_GUN )
|
|
{
|
|
// if ( (Item[ usOldItem ].fFlags & ITEM_TWO_HANDED) && usOldItem != ROCKET_LAUNCHER )
|
|
if ( (Item[ usOldItem ].twohanded ) && !Item[usOldItem].rocketlauncher )
|
|
{
|
|
fOldRifle = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( usNewItem != NOTHING )
|
|
{
|
|
if ( Item[ usNewItem ].usItemClass == IC_GUN )
|
|
{
|
|
// if ( (Item[ usNewItem ].fFlags & ITEM_TWO_HANDED) && usNewItem != ROCKET_LAUNCHER )
|
|
if ( (Item[ usNewItem ].twohanded ) && !Item[usNewItem].rocketlauncher )
|
|
{
|
|
fNewRifle = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Switch on stance!
|
|
switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
if ( fOldRifle && !fNewRifle )
|
|
{
|
|
// Put it away!
|
|
EVENT_InitNewSoldierAnim( pSoldier, LOWER_RIFLE, 0 , FALSE );
|
|
}
|
|
else if ( !fOldRifle && fNewRifle )
|
|
{
|
|
// Bring it up!
|
|
EVENT_InitNewSoldierAnim( pSoldier, RAISE_RIFLE, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
SetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
|
|
}
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
case ANIM_PRONE:
|
|
|
|
SetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UINT16 *CreateEnemyGlow16BPPPalette( SGPPaletteEntry *pPalette, UINT32 rscale, UINT32 gscale, BOOLEAN fAdjustGreen )
|
|
{
|
|
UINT16 *p16BPPPalette, r16, g16, b16, usColor;
|
|
UINT32 cnt;
|
|
UINT32 rmod, gmod, bmod;
|
|
UINT8 r,g,b;
|
|
|
|
Assert( pPalette != NULL );
|
|
|
|
p16BPPPalette = (UINT16 *) MemAlloc( sizeof( UINT16 ) * 256 );
|
|
|
|
for ( cnt = 0; cnt < 256; cnt++ )
|
|
{
|
|
gmod = (pPalette[ cnt ].peGreen);
|
|
bmod = (pPalette[ cnt ].peBlue);
|
|
|
|
rmod = __max( rscale, (pPalette[ cnt ].peRed) );
|
|
|
|
if ( fAdjustGreen )
|
|
{
|
|
gmod = __max( gscale, (pPalette[ cnt ].peGreen) );
|
|
}
|
|
|
|
r = (UINT8)__min(rmod, 255);
|
|
g = (UINT8)__min(gmod, 255);
|
|
b = (UINT8)__min(bmod, 255);
|
|
|
|
if(gusRedShift < 0)
|
|
r16=((UINT16)r>>(-gusRedShift));
|
|
else
|
|
r16=((UINT16)r<<gusRedShift);
|
|
|
|
if(gusGreenShift < 0)
|
|
g16=((UINT16)g>>(-gusGreenShift));
|
|
else
|
|
g16=((UINT16)g<<gusGreenShift);
|
|
|
|
|
|
if(gusBlueShift < 0)
|
|
b16=((UINT16)b>>(-gusBlueShift));
|
|
else
|
|
b16=((UINT16)b<<gusBlueShift);
|
|
|
|
// Prevent creation of pure black color
|
|
usColor = (r16&gusRedMask)|(g16&gusGreenMask)|(b16&gusBlueMask);
|
|
|
|
if((usColor==0) && ((r+g+b)!=0))
|
|
usColor=0x0001;
|
|
|
|
p16BPPPalette[ cnt ] = usColor;
|
|
}
|
|
return( p16BPPPalette );
|
|
}
|
|
|
|
|
|
UINT16 *CreateEnemyGreyGlow16BPPPalette( SGPPaletteEntry *pPalette, UINT32 rscale, UINT32 gscale, BOOLEAN fAdjustGreen )
|
|
{
|
|
UINT16 *p16BPPPalette, r16, g16, b16, usColor;
|
|
UINT32 cnt, lumin;
|
|
UINT32 rmod, gmod, bmod;
|
|
UINT8 r,g,b;
|
|
|
|
Assert( pPalette != NULL );
|
|
|
|
p16BPPPalette = (UINT16 *) MemAlloc( sizeof( UINT16 ) * 256 );
|
|
|
|
for ( cnt = 0; cnt < 256; cnt++ )
|
|
{
|
|
lumin=(pPalette[ cnt ].peRed*299/1000)+ (pPalette[ cnt ].peGreen*587/1000)+(pPalette[ cnt ].peBlue*114/1000);
|
|
rmod=(100*lumin)/256;
|
|
gmod=(100*lumin)/256;
|
|
bmod=(100*lumin)/256;
|
|
|
|
|
|
|
|
rmod = __max( rscale, rmod );
|
|
|
|
if ( fAdjustGreen )
|
|
{
|
|
gmod = __max( gscale, gmod );
|
|
}
|
|
|
|
|
|
r = (UINT8)__min(rmod, 255);
|
|
g = (UINT8)__min(gmod, 255);
|
|
b = (UINT8)__min(bmod, 255);
|
|
|
|
if(gusRedShift < 0)
|
|
r16=((UINT16)r>>(-gusRedShift));
|
|
else
|
|
r16=((UINT16)r<<gusRedShift);
|
|
|
|
if(gusGreenShift < 0)
|
|
g16=((UINT16)g>>(-gusGreenShift));
|
|
else
|
|
g16=((UINT16)g<<gusGreenShift);
|
|
|
|
|
|
if(gusBlueShift < 0)
|
|
b16=((UINT16)b>>(-gusBlueShift));
|
|
else
|
|
b16=((UINT16)b<<gusBlueShift);
|
|
|
|
// Prevent creation of pure black color
|
|
usColor = (r16&gusRedMask)|(g16&gusGreenMask)|(b16&gusBlueMask);
|
|
|
|
if((usColor==0) && ((r+g+b)!=0))
|
|
usColor=0x0001;
|
|
|
|
p16BPPPalette[ cnt ] = usColor;
|
|
}
|
|
return( p16BPPPalette );
|
|
}
|
|
|
|
|
|
void ContinueMercMovement( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT16 sAPCost;
|
|
INT16 sGridNo;
|
|
|
|
sGridNo = pSoldier->sFinalDestination;
|
|
|
|
// Can we afford this?
|
|
if ( pSoldier->bGoodContPath )
|
|
{
|
|
sGridNo = pSoldier->sContPathLocation;
|
|
}
|
|
else
|
|
{
|
|
// ATE: OK, don't cancel count, so pending actions are still valid...
|
|
pSoldier->ubPendingActionAnimCount = 0;
|
|
}
|
|
|
|
// get a path to dest...
|
|
if ( FindBestPath( pSoldier, sGridNo, pSoldier->bLevel, pSoldier->usUIMovementMode, NO_COPYROUTE, 0 ) )
|
|
{
|
|
sAPCost = PtsToMoveDirection( pSoldier, (UINT8)guiPathingData[ 0 ] );
|
|
|
|
if ( EnoughPoints( pSoldier, sAPCost, 0 , (BOOLEAN)( pSoldier->bTeam == gbPlayerNum ) ) )
|
|
{
|
|
// Acknowledge
|
|
if ( pSoldier->bTeam == gbPlayerNum )
|
|
{
|
|
DoMercBattleSound( pSoldier, BATTLE_SOUND_OK1 );
|
|
|
|
// If we have a face, tell text in it to go away!
|
|
if ( pSoldier->iFaceIndex != -1 )
|
|
{
|
|
gFacesData[ pSoldier->iFaceIndex ].fDisplayTextOver = FACE_ERASE_TEXT_OVER;
|
|
}
|
|
}
|
|
|
|
AdjustNoAPToFinishMove( pSoldier, FALSE );
|
|
|
|
SetUIBusy( pSoldier->ubID );
|
|
|
|
// OK, try and get a path to out dest!
|
|
EVENT_InternalGetNewSoldierPath( pSoldier, sGridNo, pSoldier->usUIMovementMode, FALSE, TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOLEAN CheckForBreathCollapse( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// Check if we are out of breath!
|
|
// Only check if > 70
|
|
if ( pSoldier->bBreathMax > 70 )
|
|
{
|
|
if ( pSoldier->bBreath < 20 && !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_LOW_BREATH ) &&
|
|
gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND )
|
|
{
|
|
// WARN!
|
|
TacticalCharacterDialogue( pSoldier, QUOTE_OUT_OF_BREATH );
|
|
|
|
// Set flag indicating we were warned!
|
|
pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_LOW_BREATH;
|
|
}
|
|
}
|
|
|
|
// Check for drowing.....
|
|
//if ( pSoldier->bBreath < 10 && !(pSoldier->usQuoteSaidFlags & SOLDIER_QUOTE_SAID_DROWNING ) && pSoldier->bOverTerrainType == DEEP_WATER )
|
|
//{
|
|
// WARN!
|
|
// TacticalCharacterDialogue( pSoldier, QUOTE_DROWNING );
|
|
|
|
// Set flag indicating we were warned!
|
|
// pSoldier->usQuoteSaidFlags |= SOLDIER_QUOTE_SAID_DROWNING;
|
|
|
|
// WISDOM GAIN (25): Starting to drown
|
|
// StatChange( pSoldier, WISDOMAMT, 25, FALSE );
|
|
|
|
//}
|
|
|
|
if ( pSoldier->bBreath == 0 && !pSoldier->bCollapsed && !( pSoldier->uiStatusFlags & ( SOLDIER_VEHICLE | SOLDIER_ANIMAL | SOLDIER_MONSTER ) ) )
|
|
{
|
|
// Collapse!
|
|
// OK, Set a flag, because we may still be in the middle of an animation what is not interruptable...
|
|
pSoldier->bBreathCollapsed = TRUE;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
BOOLEAN InternalIsValidStance( SOLDIERTYPE *pSoldier, INT8 bDirection, INT8 bNewStance )
|
|
{
|
|
UINT16 usOKToAddStructID=0;
|
|
STRUCTURE_FILE_REF *pStructureFileRef;
|
|
UINT16 usAnimSurface=0;
|
|
UINT16 usAnimState;
|
|
|
|
// Check, if dest is prone, we can actually do this!
|
|
|
|
// If we are a vehicle, we can only 'stand'
|
|
if ( ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) && bNewStance != ANIM_STAND )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Check if we are in water?
|
|
if ( MercInWater( pSoldier ) )
|
|
{
|
|
if ( bNewStance == ANIM_PRONE || bNewStance == ANIM_CROUCH )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
if ( pSoldier->ubBodyType == ROBOTNOWEAPON && bNewStance != ANIM_STAND )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Check if we are in water?
|
|
if ( AM_AN_EPC( pSoldier ) )
|
|
{
|
|
if ( bNewStance == ANIM_PRONE )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
else
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
|
|
if ( pSoldier->bCollapsed )
|
|
{
|
|
if ( bNewStance == ANIM_STAND || bNewStance == ANIM_CROUCH )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
// Check if we can do this....
|
|
if ( pSoldier->pLevelNode && pSoldier->pLevelNode->pStructureData != NULL )
|
|
{
|
|
usOKToAddStructID = pSoldier->pLevelNode->pStructureData->usStructureID;
|
|
}
|
|
else
|
|
{
|
|
usOKToAddStructID = INVALID_STRUCTURE_ID;
|
|
}
|
|
|
|
switch( bNewStance )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
usAnimState = STANDING;
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
|
|
usAnimState = CROUCHING;
|
|
break;
|
|
|
|
|
|
case ANIM_PRONE:
|
|
|
|
usAnimState = PRONE;
|
|
break;
|
|
|
|
default:
|
|
|
|
// Something gone funny here....
|
|
usAnimState = pSoldier->usAnimState;
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_BETAVERSION, L"Wrong desired stance given: %d, %d.", bNewStance, pSoldier->usAnimState );
|
|
}
|
|
|
|
usAnimSurface = DetermineSoldierAnimationSurface( pSoldier, usAnimState );
|
|
|
|
// Get structure ref........
|
|
pStructureFileRef = GetAnimationStructureRef( pSoldier->ubID, usAnimSurface, usAnimState );
|
|
|
|
if ( pStructureFileRef != NULL )
|
|
{
|
|
// Can we add structure data for this stance...?
|
|
if ( !OkayToAddStructureToWorld( pSoldier->sGridNo, pSoldier->bLevel, &(pStructureFileRef->pDBStructureRef[gOneCDirection[ bDirection ]]), usOKToAddStructID ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN IsValidStance( SOLDIERTYPE *pSoldier, INT8 bNewStance )
|
|
{
|
|
return( InternalIsValidStance( pSoldier, pSoldier->bDirection, bNewStance ) );
|
|
}
|
|
|
|
|
|
BOOLEAN IsValidMovementMode( SOLDIERTYPE *pSoldier, INT16 usMovementMode )
|
|
{
|
|
// Check, if dest is prone, we can actually do this!
|
|
|
|
// Check if we are in water?
|
|
if ( MercInWater( pSoldier ) )
|
|
{
|
|
if ( usMovementMode == RUNNING || usMovementMode == SWATTING || usMovementMode == CRAWLING )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
void SelectMoveAnimationFromStance( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// Determine which animation to do...depending on stance and gun in hand...
|
|
switch ( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
EVENT_InitNewSoldierAnim( pSoldier, WALKING, 0 , FALSE );
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
EVENT_InitNewSoldierAnim( pSoldier, CRAWLING, 0 , FALSE );
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
EVENT_InitNewSoldierAnim( pSoldier, SWATTING, 0 , FALSE );
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void GetActualSoldierAnimDims( SOLDIERTYPE *pSoldier, INT16 *psHeight, INT16 *psWidth )
|
|
{
|
|
UINT16 usAnimSurface;
|
|
ETRLEObject *pTrav;
|
|
|
|
usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
|
|
|
|
if ( usAnimSurface == INVALID_ANIMATION_SURFACE )
|
|
{
|
|
*psHeight = (INT16)5;
|
|
*psWidth = (INT16)5;
|
|
|
|
return;
|
|
}
|
|
|
|
if ( gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject == NULL )
|
|
{
|
|
*psHeight = (INT16)5;
|
|
*psWidth = (INT16)5;
|
|
return;
|
|
}
|
|
|
|
// OK, noodle here on what we should do... If we take each frame, it will be different slightly
|
|
// depending on the frame and the value returned here will vary thusly. However, for the
|
|
// uses of this function, we should be able to use just the first frame...
|
|
|
|
if ( pSoldier->usAniFrame >= gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->usNumberOfObjects )
|
|
{
|
|
//int i = 0;
|
|
return;
|
|
}
|
|
|
|
pTrav = &(gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pETRLEObject[ pSoldier->usAniFrame ] );
|
|
|
|
*psHeight = (INT16)pTrav->usHeight;
|
|
*psWidth = (INT16)pTrav->usWidth;
|
|
}
|
|
|
|
void GetActualSoldierAnimOffsets( SOLDIERTYPE *pSoldier, INT16 *sOffsetX, INT16 *sOffsetY )
|
|
{
|
|
UINT16 usAnimSurface;
|
|
ETRLEObject *pTrav;
|
|
|
|
usAnimSurface = GetSoldierAnimationSurface( pSoldier, pSoldier->usAnimState );
|
|
|
|
if ( usAnimSurface == INVALID_ANIMATION_SURFACE )
|
|
{
|
|
*sOffsetX = (INT16)0;
|
|
*sOffsetY = (INT16)0;
|
|
|
|
return;
|
|
}
|
|
|
|
if ( gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject == NULL )
|
|
{
|
|
*sOffsetX = (INT16)0;
|
|
*sOffsetY = (INT16)0;
|
|
return;
|
|
}
|
|
|
|
pTrav = &(gAnimSurfaceDatabase[ usAnimSurface ].hVideoObject->pETRLEObject[ pSoldier->usAniFrame ] );
|
|
|
|
*sOffsetX = (INT16)pTrav->sOffsetX;
|
|
*sOffsetY = (INT16)pTrav->sOffsetY;
|
|
}
|
|
|
|
|
|
void SetSoldierLocatorOffsets( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT16 sHeight, sWidth;
|
|
INT16 sOffsetX, sOffsetY;
|
|
|
|
|
|
// OK, from our animation, get height, width
|
|
GetActualSoldierAnimDims( pSoldier, &sHeight, &sWidth );
|
|
GetActualSoldierAnimOffsets( pSoldier, &sOffsetX, &sOffsetY );
|
|
|
|
// OK, here, use the difference between center of animation ( sWidth/2 ) and our offset!
|
|
//pSoldier->sLocatorOffX = ( abs( sOffsetX ) ) - ( sWidth / 2 );
|
|
|
|
pSoldier->sBoundingBoxWidth = sWidth;
|
|
pSoldier->sBoundingBoxHeight = sHeight;
|
|
pSoldier->sBoundingBoxOffsetX = sOffsetX;
|
|
pSoldier->sBoundingBoxOffsetY = sOffsetY;
|
|
|
|
}
|
|
|
|
BOOLEAN SoldierCarriesTwoHandedWeapon( SOLDIERTYPE *pSoldier )
|
|
{
|
|
UINT16 usItem;
|
|
|
|
usItem = pSoldier->inv[ HANDPOS ].usItem;
|
|
|
|
// if ( usItem != NOTHING && (Item[ usItem ].fFlags & ITEM_TWO_HANDED) )
|
|
if ( usItem != NOTHING && (Item[ usItem ].twohanded ) )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
|
|
|
|
INT32 CheckBleeding( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT8 bBandaged; //,savedOurTurn;
|
|
INT32 iBlood = NOBLOOD;
|
|
|
|
if ( pSoldier->bLife != 0 )
|
|
{
|
|
// if merc is hurt beyond the minimum required to bleed, or he's dying
|
|
if ( ( pSoldier->bBleeding > MIN_BLEEDING_THRESHOLD) || pSoldier->bLife < OKLIFE )
|
|
{
|
|
// if he's NOT in the process of being bandaged or DOCTORed
|
|
if ( ( pSoldier->ubServiceCount == 0 ) && ( AnyDoctorWhoCanHealThisPatient( pSoldier, HEALABLE_EVER ) == NULL ) )
|
|
{
|
|
// may drop blood whether or not any bleeding takes place this turn
|
|
if ( pSoldier->bTilesMoved < 1 )
|
|
{
|
|
iBlood = ( ( pSoldier->bBleeding - MIN_BLEEDING_THRESHOLD ) / BLOODDIVISOR ); // + pSoldier->dying;
|
|
if ( iBlood > MAXBLOODQUANTITY )
|
|
{
|
|
iBlood = MAXBLOODQUANTITY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iBlood = NOBLOOD;
|
|
}
|
|
|
|
// Are we in a different mode?
|
|
if ( !(gTacticalStatus.uiFlags & TURNBASED ) || !(gTacticalStatus.uiFlags & INCOMBAT ) )
|
|
{
|
|
pSoldier->dNextBleed -= (FLOAT)RT_NEXT_BLEED_MODIFIER;
|
|
}
|
|
else
|
|
{
|
|
// Do a single step descrease
|
|
pSoldier->dNextBleed--;
|
|
}
|
|
|
|
// if it's time to lose some blood
|
|
if ( pSoldier->dNextBleed <= 0)
|
|
{
|
|
// first, calculate if soldier is bandaged
|
|
bBandaged = pSoldier->bLifeMax - pSoldier->bBleeding - pSoldier->bLife;
|
|
|
|
// as long as he's bandaged and not "dying"
|
|
if ( bBandaged && pSoldier->bLife >= OKLIFE )
|
|
{
|
|
// just bleeding through existing bandages
|
|
pSoldier->bBleeding++;
|
|
|
|
SoldierBleed( pSoldier, TRUE );
|
|
}
|
|
else // soldier is either not bandaged at all or is dying
|
|
{
|
|
if ( pSoldier->bLife < OKLIFE ) // if he's dying
|
|
{
|
|
// if he's conscious, and he hasn't already, say his "dying quote"
|
|
if ( ( pSoldier->bLife >= CONSCIOUSNESS ) && !pSoldier->fDyingComment )
|
|
{
|
|
TacticalCharacterDialogue( pSoldier, QUOTE_SERIOUSLY_WOUNDED );
|
|
|
|
pSoldier->fDyingComment = TRUE;
|
|
}
|
|
|
|
// can't permit lifemax to ever bleed beneath OKLIFE, or that
|
|
// soldier might as well be dead!
|
|
if (pSoldier->bLifeMax >= OKLIFE)
|
|
{
|
|
// bleeding while "dying" costs a PERMANENT point of life each time!
|
|
pSoldier->bLifeMax--;
|
|
pSoldier->bBleeding--;
|
|
}
|
|
}
|
|
}
|
|
|
|
// either way, a point of life (health) is lost because of bleeding
|
|
// This will also update the life bar
|
|
|
|
SoldierBleed( pSoldier, FALSE );
|
|
|
|
|
|
// if he's not dying (which includes him saying the dying quote just
|
|
// now), and he hasn't warned us that he's bleeding yet, he does so
|
|
// Also, not if they are being bandaged....
|
|
if ( ( pSoldier->bLife >= OKLIFE ) && !pSoldier->fDyingComment && !pSoldier->fWarnedAboutBleeding && !gTacticalStatus.fAutoBandageMode && pSoldier->ubServiceCount == 0 )
|
|
{
|
|
TacticalCharacterDialogue( pSoldier, QUOTE_STARTING_TO_BLEED );
|
|
|
|
// "starting to bleed" quote
|
|
pSoldier->fWarnedAboutBleeding = TRUE;
|
|
}
|
|
|
|
pSoldier->dNextBleed = CalcSoldierNextBleed( pSoldier );
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return( iBlood );
|
|
}
|
|
|
|
|
|
void SoldierBleed( SOLDIERTYPE *pSoldier, BOOLEAN fBandagedBleed )
|
|
{
|
|
INT8 bOldLife;
|
|
|
|
// OK, here make some stuff happen for bleeding
|
|
// A banaged bleed does not show damage taken , just through existing bandages
|
|
|
|
// ATE: Do this ONLY if buddy is in sector.....
|
|
if ( ( pSoldier->bInSector && guiCurrentScreen == GAME_SCREEN ) || guiCurrentScreen != GAME_SCREEN )
|
|
{
|
|
pSoldier->fFlashPortrait = TRUE;
|
|
pSoldier->bFlashPortraitFrame = FLASH_PORTRAIT_STARTSHADE;
|
|
RESETTIMECOUNTER( pSoldier->PortraitFlashCounter, FLASH_PORTRAIT_DELAY );
|
|
|
|
// If we are in mapscreen, set this person as selected
|
|
if ( guiCurrentScreen == MAP_SCREEN )
|
|
{
|
|
SetInfoChar( pSoldier->ubID );
|
|
}
|
|
}
|
|
|
|
bOldLife = pSoldier->bLife;
|
|
|
|
// If we are already dead, don't show damage!
|
|
if ( !fBandagedBleed )
|
|
{
|
|
SoldierTakeDamage( pSoldier, ANIM_CROUCH, 1, 100, TAKE_DAMAGE_BLOODLOSS, NOBODY, NOWHERE, 0, TRUE );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void SoldierCollapse( SOLDIERTYPE *pSoldier )
|
|
{
|
|
BOOLEAN fMerc = FALSE;
|
|
|
|
if ( pSoldier->ubBodyType <= REGFEMALE )
|
|
{
|
|
fMerc = TRUE;
|
|
}
|
|
|
|
// If we are an animal, etc, don't do anything....
|
|
switch( pSoldier->ubBodyType )
|
|
{
|
|
case ADULTFEMALEMONSTER:
|
|
case AM_MONSTER:
|
|
case YAF_MONSTER:
|
|
case YAM_MONSTER:
|
|
case LARVAE_MONSTER:
|
|
case INFANT_MONSTER:
|
|
case QUEENMONSTER:
|
|
|
|
// Give breath back....
|
|
DeductPoints( pSoldier,0, (INT16)-5000 );
|
|
return;
|
|
break;
|
|
}
|
|
|
|
pSoldier->bCollapsed = TRUE;
|
|
|
|
ReceivingSoldierCancelServices( pSoldier );
|
|
|
|
// CC has requested - handle sight here...
|
|
HandleSight( pSoldier, SIGHT_LOOK );
|
|
|
|
// Check height
|
|
switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
if ( pSoldier->bOverTerrainType == DEEP_WATER )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, DEEP_WATER_DIE, 0, FALSE );
|
|
}
|
|
else if ( pSoldier->bOverTerrainType == LOW_WATER )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, WATER_DIE, 0, FALSE );
|
|
}
|
|
else
|
|
{
|
|
BeginTyingToFall( pSoldier );
|
|
EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0, FALSE );
|
|
}
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
|
|
// Crouched or prone, only for mercs!
|
|
BeginTyingToFall( pSoldier );
|
|
|
|
if ( fMerc )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_CROUCH, 0 , FALSE);
|
|
}
|
|
else
|
|
{
|
|
// For civs... use fall from stand...
|
|
EVENT_InitNewSoldierAnim( pSoldier, FALLFORWARD_FROMHIT_STAND, 0 , FALSE);
|
|
}
|
|
break;
|
|
|
|
case ANIM_PRONE:
|
|
|
|
switch( pSoldier->usAnimState )
|
|
{
|
|
case FALLFORWARD_FROMHIT_STAND:
|
|
case ENDFALLFORWARD_FROMHIT_CROUCH:
|
|
|
|
ChangeSoldierState( pSoldier, STAND_FALLFORWARD_STOP, 0, FALSE );
|
|
break;
|
|
|
|
case FALLBACK_HIT_STAND:
|
|
ChangeSoldierState( pSoldier, FALLBACKHIT_STOP, 0, FALSE );
|
|
break;
|
|
|
|
default:
|
|
EVENT_InitNewSoldierAnim( pSoldier, PRONE_LAY_FROMHIT, 0 , FALSE );
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (pSoldier->uiStatusFlags & SOLDIER_ENEMY)
|
|
{
|
|
|
|
if ( !(gTacticalStatus.bPanicTriggerIsAlarm) && (gTacticalStatus.ubTheChosenOne == pSoldier->ubID) )
|
|
{
|
|
// replace this guy as the chosen one!
|
|
gTacticalStatus.ubTheChosenOne = NOBODY;
|
|
MakeClosestEnemyChosenOne();
|
|
}
|
|
|
|
if ( (gTacticalStatus.uiFlags & TURNBASED) && (gTacticalStatus.uiFlags & INCOMBAT) && (pSoldier->uiStatusFlags & SOLDIER_UNDERAICONTROL))
|
|
{
|
|
#ifdef TESTAICONTROL
|
|
DebugAI( (STR)String("Ending turn for %d because of error from HandleItem", pSoldier->ubID ) );
|
|
#endif
|
|
|
|
EndAIGuysTurn( pSoldier );
|
|
}
|
|
}
|
|
|
|
// DON'T DE-SELECT GUY.....
|
|
//else
|
|
//{
|
|
// Check if this is our selected guy...
|
|
// if ( pSoldier->ubID == gusSelectedSoldier )
|
|
// {
|
|
// SelectNextAvailSoldier( pSoldier );
|
|
// }
|
|
//}
|
|
}
|
|
|
|
|
|
FLOAT CalcSoldierNextBleed( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT8 bBandaged;
|
|
|
|
// calculate how many turns before he bleeds again
|
|
// bleeding faster the lower life gets, and if merc is running around
|
|
//pSoldier->nextbleed = 2 + (pSoldier->life / (10 + pSoldier->tilesMoved)); // min = 2
|
|
|
|
// if bandaged, give 1/2 of the bandaged life points back into equation
|
|
bBandaged = pSoldier->bLifeMax - pSoldier->bLife - pSoldier->bBleeding;
|
|
|
|
return( (FLOAT)1 + (FLOAT)( (pSoldier->bLife + bBandaged / 2) / (10 + pSoldier->bTilesMoved) ) ); // min = 1
|
|
}
|
|
|
|
FLOAT CalcSoldierNextUnmovingBleed( SOLDIERTYPE *pSoldier )
|
|
{
|
|
INT8 bBandaged;
|
|
|
|
// calculate bleeding rate without the penalty for tiles moved
|
|
|
|
// if bandaged, give 1/2 of the bandaged life points back into equation
|
|
bBandaged = pSoldier->bLifeMax - pSoldier->bLife - pSoldier->bBleeding;
|
|
|
|
return( (FLOAT)1 + (FLOAT)( (pSoldier->bLife + bBandaged / 2) / 10 ) ); // min = 1
|
|
}
|
|
|
|
void HandlePlacingRoofMarker( SOLDIERTYPE *pSoldier, INT16 sGridNo, BOOLEAN fSet, BOOLEAN fForce )
|
|
{
|
|
LEVELNODE *pRoofNode;
|
|
LEVELNODE *pNode;
|
|
|
|
if ( pSoldier->bVisible == -1 && fSet )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( pSoldier->bTeam != gbPlayerNum )
|
|
{
|
|
//return;
|
|
}
|
|
|
|
// If we are on the roof, add roof UI peice!
|
|
if ( pSoldier->bLevel == SECOND_LEVEL )
|
|
{
|
|
// Get roof node
|
|
pRoofNode = gpWorldLevelData[ sGridNo ].pRoofHead;
|
|
|
|
// Return if we are still climbing roof....
|
|
if ( pSoldier->usAnimState == CLIMBUPROOF && !fForce )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( pRoofNode != NULL )
|
|
{
|
|
if ( fSet )
|
|
{
|
|
if ( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REVEALED )
|
|
{
|
|
// Set some flags on this poor thing
|
|
//pRoofNode->uiFlags |= ( LEVELNODE_USEBESTTRANSTYPE | LEVELNODE_REVEAL | LEVELNODE_DYNAMIC );
|
|
//pRoofNode->uiFlags |= ( LEVELNODE_DYNAMIC );
|
|
//pRoofNode->uiFlags &= ( ~LEVELNODE_HIDDEN );
|
|
//ResetSpecificLayerOptimizing( TILES_DYNAMIC_ROOF );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( gpWorldLevelData[ sGridNo ].uiFlags & MAPELEMENT_REVEALED )
|
|
{
|
|
// Remove some flags on this poor thing
|
|
//pRoofNode->uiFlags &= ~( LEVELNODE_USEBESTTRANSTYPE | LEVELNODE_REVEAL | LEVELNODE_DYNAMIC );
|
|
|
|
//pRoofNode->uiFlags |= LEVELNODE_HIDDEN;
|
|
}
|
|
}
|
|
|
|
if ( fSet )
|
|
{
|
|
// If it does not exist already....
|
|
if ( !IndexExistsInRoofLayer( sGridNo, FIRSTPOINTERS11 ) )
|
|
{
|
|
pNode = AddRoofToTail( sGridNo, FIRSTPOINTERS11 );
|
|
pNode->ubShadeLevel =DEFAULT_SHADE_LEVEL;
|
|
pNode->ubNaturalShadeLevel=DEFAULT_SHADE_LEVEL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RemoveRoof( sGridNo, FIRSTPOINTERS11 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PositionSoldierLight( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// DO ONLY IF WE'RE AT A GOOD LEVEL
|
|
if ( ubAmbientLightLevel < MIN_AMB_LEVEL_FOR_MERC_LIGHTS )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !pSoldier->bInSector )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( pSoldier->bTeam != gbPlayerNum )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( pSoldier->bLife < OKLIFE )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//if the player DOESNT want the merc to cast light
|
|
if( !gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( pSoldier->iLight == -1 )
|
|
{
|
|
CreateSoldierLight( pSoldier );
|
|
}
|
|
|
|
//if ( pSoldier->ubID == gusSelectedSoldier )
|
|
{
|
|
LightSpritePower(pSoldier->iLight, TRUE);
|
|
LightSpriteFake(pSoldier->iLight);
|
|
|
|
LightSpritePosition( pSoldier->iLight, (INT16)(pSoldier->sX/CELL_X_SIZE), (INT16)(pSoldier->sY/CELL_Y_SIZE));
|
|
}
|
|
}
|
|
|
|
void SetCheckSoldierLightFlag( SOLDIERTYPE *pSoldier )
|
|
{
|
|
PositionSoldierLight( pSoldier );
|
|
//pSoldier->uiStatusFlags |= SOLDIER_RECHECKLIGHT;
|
|
}
|
|
|
|
|
|
void PickPickupAnimation( SOLDIERTYPE *pSoldier, INT32 iItemIndex, INT16 sGridNo, INT8 bZLevel )
|
|
{
|
|
INT8 bDirection;
|
|
STRUCTURE *pStructure;
|
|
BOOLEAN fDoNormalPickup = TRUE;
|
|
|
|
|
|
// OK, Given the gridno, determine if it's the same one or different....
|
|
if ( sGridNo != pSoldier->sGridNo )
|
|
{
|
|
// Get direction to face....
|
|
bDirection = (INT8)GetDirectionFromGridNo( sGridNo, pSoldier );
|
|
pSoldier->ubPendingDirection = bDirection;
|
|
|
|
// Change to pickup animation
|
|
EVENT_InitNewSoldierAnim( pSoldier, ADJACENT_GET_ITEM, 0 , FALSE );
|
|
|
|
if (!(pSoldier->uiStatusFlags & SOLDIER_PC))
|
|
{
|
|
// set "pending action" value for AI so it will wait
|
|
pSoldier->bAction = AI_ACTION_PENDING_ACTION;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// If in water....
|
|
if ( MercInWater( pSoldier ) )
|
|
{
|
|
UnSetUIBusy( pSoldier->ubID );
|
|
HandleSoldierPickupItem( pSoldier, iItemIndex, sGridNo, bZLevel );
|
|
SoldierGotoStationaryStance( pSoldier );
|
|
if (!(pSoldier->uiStatusFlags & SOLDIER_PC))
|
|
{
|
|
// reset action value for AI because we're done!
|
|
ActionDone( pSoldier );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// Don't show animation of getting item, if we are not standing
|
|
switch ( gAnimControl[ pSoldier->usAnimState ].ubHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
// OK, if we are looking at z-level >0, AND
|
|
// we have a strucxture with items in it
|
|
// look for orientation and use angle accordingly....
|
|
if ( bZLevel > 0 )
|
|
{
|
|
//#if 0
|
|
// Get direction to face....
|
|
if ( ( pStructure = FindStructure( (INT16)sGridNo, ( STRUCTURE_HASITEMONTOP | STRUCTURE_OPENABLE ) ) ) != NULL )
|
|
{
|
|
fDoNormalPickup = FALSE;
|
|
|
|
// OK, look at orientation
|
|
switch( pStructure->ubWallOrientation )
|
|
{
|
|
case OUTSIDE_TOP_LEFT:
|
|
case INSIDE_TOP_LEFT:
|
|
|
|
bDirection = (INT8)NORTH;
|
|
break;
|
|
|
|
case OUTSIDE_TOP_RIGHT:
|
|
case INSIDE_TOP_RIGHT:
|
|
|
|
bDirection = (INT8)WEST;
|
|
break;
|
|
|
|
default:
|
|
|
|
bDirection = pSoldier->bDirection;
|
|
break;
|
|
}
|
|
|
|
//pSoldier->ubPendingDirection = bDirection;
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, bDirection );
|
|
EVENT_SetSoldierDirection( pSoldier, bDirection );
|
|
|
|
// Change to pickup animation
|
|
EVENT_InitNewSoldierAnim( pSoldier, ADJACENT_GET_ITEM, 0 , FALSE );
|
|
}
|
|
//#endif
|
|
}
|
|
|
|
if ( fDoNormalPickup )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, PICKUP_ITEM, 0 , FALSE );
|
|
}
|
|
|
|
if (!(pSoldier->uiStatusFlags & SOLDIER_PC))
|
|
{
|
|
// set "pending action" value for AI so it will wait
|
|
pSoldier->bAction = AI_ACTION_PENDING_ACTION;
|
|
}
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
case ANIM_PRONE:
|
|
|
|
UnSetUIBusy( pSoldier->ubID );
|
|
HandleSoldierPickupItem( pSoldier, iItemIndex, sGridNo, bZLevel );
|
|
SoldierGotoStationaryStance( pSoldier );
|
|
if (!(pSoldier->uiStatusFlags & SOLDIER_PC))
|
|
{
|
|
// reset action value for AI because we're done!
|
|
ActionDone( pSoldier );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PickDropItemAnimation( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// Don't show animation of getting item, if we are not standing
|
|
switch ( gAnimControl[ pSoldier->usAnimState ].ubHeight )
|
|
{
|
|
case ANIM_STAND:
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, DROP_ITEM, 0 , FALSE );
|
|
break;
|
|
|
|
case ANIM_CROUCH:
|
|
case ANIM_PRONE:
|
|
|
|
SoldierHandleDropItem( pSoldier );
|
|
SoldierGotoStationaryStance( pSoldier );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void EVENT_SoldierBeginCutFence( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
|
|
{
|
|
// Make sure we have a structure here....
|
|
if ( IsCuttableWireFenceAtGridNo( sGridNo ) )
|
|
{
|
|
// CHANGE DIRECTION AND GOTO ANIMATION NOW
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
|
|
EVENT_SetSoldierDirection( pSoldier, ubDirection );
|
|
|
|
//BOOLEAN CutWireFence( INT16 sGridNo )
|
|
|
|
// SET TARGET GRIDNO
|
|
pSoldier->sTargetGridNo = sGridNo;
|
|
|
|
// CHANGE TO ANIMATION
|
|
EVENT_InitNewSoldierAnim( pSoldier, CUTTING_FENCE, 0 , FALSE );
|
|
}
|
|
}
|
|
|
|
|
|
void EVENT_SoldierBeginRepair( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
|
|
{
|
|
INT8 bRepairItem;
|
|
UINT8 ubID;
|
|
|
|
// Make sure we have a structure here....
|
|
bRepairItem = IsRepairableStructAtGridNo( sGridNo, &ubID );
|
|
|
|
if ( bRepairItem )
|
|
{
|
|
// CHANGE DIRECTION AND GOTO ANIMATION NOW
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
|
|
EVENT_SetSoldierDirection( pSoldier, ubDirection );
|
|
|
|
//BOOLEAN CutWireFence( INT16 sGridNo )
|
|
|
|
// SET TARGET GRIDNO
|
|
//pSoldier->sTargetGridNo = sGridNo;
|
|
|
|
// CHANGE TO ANIMATION
|
|
EVENT_InitNewSoldierAnim( pSoldier, GOTO_REPAIRMAN, 0 , FALSE );
|
|
// SET BUDDY'S ASSIGNMENT TO REPAIR...
|
|
|
|
// Are we a SAM site? ( 3 == SAM )
|
|
if ( bRepairItem == 3 )
|
|
{
|
|
SetSoldierAssignment( pSoldier, REPAIR, TRUE, FALSE, -1 );
|
|
}
|
|
else if ( bRepairItem == 2 ) // ( 2 == VEHICLE )
|
|
{
|
|
SetSoldierAssignment( pSoldier, REPAIR, FALSE, FALSE, ubID );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void EVENT_SoldierBeginRefuel( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
|
|
{
|
|
INT8 bRefuelItem;
|
|
UINT8 ubID;
|
|
|
|
// Make sure we have a structure here....
|
|
bRefuelItem = IsRefuelableStructAtGridNo( sGridNo, &ubID );
|
|
|
|
if ( bRefuelItem )
|
|
{
|
|
// CHANGE DIRECTION AND GOTO ANIMATION NOW
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
|
|
EVENT_SetSoldierDirection( pSoldier, ubDirection );
|
|
|
|
//BOOLEAN CutWireFence( INT16 sGridNo )
|
|
|
|
// SET TARGET GRIDNO
|
|
//pSoldier->sTargetGridNo = sGridNo;
|
|
|
|
// CHANGE TO ANIMATION
|
|
EVENT_InitNewSoldierAnim( pSoldier, REFUEL_VEHICLE, 0 , FALSE );
|
|
// SET BUDDY'S ASSIGNMENT TO REPAIR...
|
|
}
|
|
}
|
|
|
|
|
|
void EVENT_SoldierBeginTakeBlood( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
|
|
{
|
|
ROTTING_CORPSE *pCorpse;
|
|
|
|
|
|
// See if these is a corpse here....
|
|
pCorpse = GetCorpseAtGridNo( sGridNo , pSoldier->bLevel );
|
|
|
|
if ( pCorpse != NULL )
|
|
{
|
|
pSoldier->uiPendingActionData4 = pCorpse->iID;
|
|
|
|
// CHANGE DIRECTION AND GOTO ANIMATION NOW
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
|
|
EVENT_SetSoldierDirection( pSoldier, ubDirection );
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, TAKE_BLOOD_FROM_CORPSE, 0 , FALSE );
|
|
}
|
|
else
|
|
{
|
|
// Say NOTHING quote...
|
|
DoMercBattleSound( pSoldier, BATTLE_SOUND_NOTHING );
|
|
}
|
|
}
|
|
|
|
|
|
void EVENT_SoldierBeginAttachCan( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection )
|
|
{
|
|
STRUCTURE * pStructure;
|
|
DOOR_STATUS * pDoorStatus;
|
|
|
|
// OK, find door, attach to door, do animation...., remove item....
|
|
|
|
// First make sure we still have item in hand....
|
|
if ( !Item[pSoldier->inv[ HANDPOS ].usItem].canandstring )
|
|
{
|
|
return;
|
|
}
|
|
|
|
pStructure = FindStructure( sGridNo, STRUCTURE_ANYDOOR );
|
|
|
|
if ( pStructure == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Modify door status to make sure one is created for this door
|
|
// Use the current door state for this
|
|
if ( !(pStructure->fFlags & STRUCTURE_OPEN) )
|
|
{
|
|
ModifyDoorStatus( sGridNo, FALSE, FALSE );
|
|
}
|
|
else
|
|
{
|
|
ModifyDoorStatus( sGridNo, TRUE, TRUE );
|
|
}
|
|
|
|
// Now get door status...
|
|
pDoorStatus = GetDoorStatus( sGridNo );
|
|
if ( pDoorStatus == NULL )
|
|
{
|
|
// SOmething wrong here...
|
|
return;
|
|
}
|
|
|
|
// OK set flag!
|
|
pDoorStatus->ubFlags |= DOOR_HAS_TIN_CAN;
|
|
|
|
// Do animation
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
|
|
EVENT_SetSoldierDirection( pSoldier, ubDirection );
|
|
|
|
EVENT_InitNewSoldierAnim( pSoldier, ATTACH_CAN_TO_STRING, 0 , FALSE );
|
|
|
|
// Remove item...
|
|
DeleteObj( &(pSoldier->inv[ HANDPOS ] ) );
|
|
fInterfacePanelDirty = DIRTYLEVEL2;
|
|
|
|
}
|
|
|
|
|
|
void EVENT_SoldierBeginReloadRobot( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubDirection, UINT8 ubMercSlot )
|
|
{
|
|
UINT8 ubPerson;
|
|
|
|
// Make sure we have a robot here....
|
|
ubPerson = WhoIsThere2( sGridNo, pSoldier->bLevel );
|
|
|
|
if ( ubPerson != NOBODY && MercPtrs[ ubPerson ]->uiStatusFlags & SOLDIER_ROBOT )
|
|
{
|
|
// CHANGE DIRECTION AND GOTO ANIMATION NOW
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
|
|
EVENT_SetSoldierDirection( pSoldier, ubDirection );
|
|
|
|
// CHANGE TO ANIMATION
|
|
EVENT_InitNewSoldierAnim( pSoldier, RELOAD_ROBOT, 0 , FALSE );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ResetSoldierChangeStatTimer( SOLDIERTYPE *pSoldier )
|
|
{
|
|
pSoldier->uiChangeLevelTime = 0;
|
|
pSoldier->uiChangeHealthTime = 0;
|
|
pSoldier->uiChangeStrengthTime= 0;
|
|
pSoldier->uiChangeDexterityTime= 0;
|
|
pSoldier->uiChangeAgilityTime= 0;
|
|
pSoldier->uiChangeWisdomTime= 0;
|
|
pSoldier->uiChangeLeadershipTime= 0;
|
|
pSoldier->uiChangeMarksmanshipTime= 0;
|
|
pSoldier->uiChangeExplosivesTime= 0;
|
|
pSoldier->uiChangeMedicalTime= 0;
|
|
pSoldier->uiChangeMechanicalTime= 0;
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void ChangeToFlybackAnimation( SOLDIERTYPE *pSoldier, INT8 bDirection )
|
|
{
|
|
UINT16 usNewGridNo;
|
|
|
|
// Get dest gridno, convert to center coords
|
|
usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) );
|
|
usNewGridNo = NewGridNo( (UINT16)usNewGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) );
|
|
|
|
// Remove any previous actions
|
|
pSoldier->ubPendingAction = NO_PENDING_ACTION;
|
|
|
|
// Set path....
|
|
pSoldier->usPathDataSize = 0;
|
|
pSoldier->usPathIndex = 0;
|
|
pSoldier->usPathingData[ pSoldier->usPathDataSize ] = gOppositeDirection[ pSoldier->bDirection ];
|
|
pSoldier->usPathDataSize++;
|
|
pSoldier->usPathingData[ pSoldier->usPathDataSize ] = gOppositeDirection[ pSoldier->bDirection ];
|
|
pSoldier->usPathDataSize++;
|
|
pSoldier->sFinalDestination = usNewGridNo;
|
|
EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, FLYBACK_HIT );
|
|
|
|
// Get a new direction based on direction
|
|
EVENT_InitNewSoldierAnim( pSoldier, FLYBACK_HIT, 0 , FALSE );
|
|
}
|
|
|
|
void ChangeToFallbackAnimation( SOLDIERTYPE *pSoldier, INT8 bDirection )
|
|
{
|
|
UINT16 usNewGridNo;
|
|
|
|
// Get dest gridno, convert to center coords
|
|
usNewGridNo = NewGridNo( (UINT16)pSoldier->sGridNo, DirectionInc( gOppositeDirection[ bDirection ] ) );
|
|
//usNewGridNo = NewGridNo( (UINT16)usNewGridNo, (UINT16)(-1 * DirectionInc( bDirection ) ) );
|
|
|
|
// Remove any previous actions
|
|
pSoldier->ubPendingAction = NO_PENDING_ACTION;
|
|
|
|
// Set path....
|
|
pSoldier->usPathDataSize = 0;
|
|
pSoldier->usPathIndex = 0;
|
|
pSoldier->usPathingData[ pSoldier->usPathDataSize ] = gOppositeDirection[ pSoldier->bDirection ];
|
|
pSoldier->usPathDataSize++;
|
|
pSoldier->sFinalDestination = usNewGridNo;
|
|
EVENT_InternalSetSoldierDestination( pSoldier, pSoldier->usPathingData[ pSoldier->usPathIndex ], FALSE, FALLBACK_HIT_STAND );
|
|
|
|
// Get a new direction based on direction
|
|
EVENT_InitNewSoldierAnim( pSoldier, FALLBACK_HIT_STAND, 0 , FALSE );
|
|
}
|
|
|
|
|
|
void SetSoldierCowerState( SOLDIERTYPE *pSoldier, BOOLEAN fOn )
|
|
{
|
|
// Robot's don't cower!
|
|
if ( pSoldier->ubBodyType == ROBOTNOWEAPON )
|
|
{
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("ERROR: Robot was told to cower!" ) );
|
|
return;
|
|
}
|
|
|
|
// OK< set flag and do anim...
|
|
if ( fOn )
|
|
{
|
|
if ( !( pSoldier->uiStatusFlags & SOLDIER_COWERING ) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, START_COWER, 0, FALSE );
|
|
|
|
pSoldier->uiStatusFlags |= SOLDIER_COWERING;
|
|
|
|
pSoldier->ubDesiredHeight = ANIM_CROUCH;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( (pSoldier->uiStatusFlags & SOLDIER_COWERING) )
|
|
{
|
|
EVENT_InitNewSoldierAnim( pSoldier, END_COWER, 0, FALSE );
|
|
|
|
pSoldier->uiStatusFlags &= (~SOLDIER_COWERING );
|
|
|
|
pSoldier->ubDesiredHeight = ANIM_STAND;
|
|
}
|
|
}
|
|
}
|
|
|
|
void MercStealFromMerc( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pTarget )
|
|
{
|
|
INT16 sActionGridNo, sGridNo, sAdjustedGridNo;
|
|
UINT8 ubDirection;
|
|
|
|
|
|
// OK, find an adjacent gridno....
|
|
sGridNo = pTarget->sGridNo;
|
|
|
|
// See if we can get there to punch
|
|
sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
|
|
if ( sActionGridNo != -1 )
|
|
{
|
|
// SEND PENDING ACTION
|
|
pSoldier->ubPendingAction = MERC_STEAL;
|
|
pSoldier->sPendingActionData2 = pTarget->sGridNo;
|
|
pSoldier->bPendingActionData3 = ubDirection;
|
|
pSoldier->ubPendingActionAnimCount = 0;
|
|
|
|
// CHECK IF WE ARE AT THIS GRIDNO NOW
|
|
if ( pSoldier->sGridNo != sActionGridNo )
|
|
{
|
|
// WALK UP TO DEST FIRST
|
|
SendGetNewSoldierPathEvent( pSoldier, sActionGridNo, pSoldier->usUIMovementMode );
|
|
}
|
|
else
|
|
{
|
|
EVENT_SetSoldierDesiredDirection( pSoldier, ubDirection );
|
|
EVENT_InitNewSoldierAnim( pSoldier, STEAL_ITEM, 0 , FALSE );
|
|
}
|
|
|
|
// OK, set UI
|
|
gTacticalStatus.ubAttackBusyCount++;
|
|
// reset attacking item (hand)
|
|
pSoldier->usAttackingWeapon = 0;
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("!!!!!!! Starting STEAL attack, attack count now %d", gTacticalStatus.ubAttackBusyCount) );
|
|
|
|
SetUIBusy( pSoldier->ubID );
|
|
}
|
|
}
|
|
|
|
SOLDIERTYPE *pTMilitiaSoldier;
|
|
BOOLEAN PlayerSoldierStartTalking( SOLDIERTYPE *pSoldier, UINT8 ubTargetID, BOOLEAN fValidate )
|
|
{
|
|
INT16 sFacingDir, sXPos, sYPos, sAPCost;
|
|
SOLDIERTYPE *pTSoldier;
|
|
UINT32 uiRange;
|
|
|
|
if ( ubTargetID == NOBODY )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
pTSoldier = MercPtrs[ ubTargetID ];
|
|
|
|
// Check distance again, to be sure
|
|
if ( fValidate )
|
|
{
|
|
// OK, since we locked this guy from moving
|
|
// we should be close enough, so talk ( unless he is now dead )
|
|
if ( !IsValidTalkableNPC( (UINT8)ubTargetID, FALSE, FALSE, FALSE ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
uiRange = GetRangeFromGridNoDiff( pSoldier->sGridNo, pTSoldier->sGridNo );
|
|
|
|
if ( uiRange > ( NPC_TALK_RADIUS * 2 ) )
|
|
{
|
|
// Todo here - should we follow dude?
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// Get APs...
|
|
sAPCost = AP_TALK;
|
|
|
|
if ( !(gTacticalStatus.uiFlags & INCOMBAT) || (gTacticalStatus.uiFlags & REALTIME) ) //lal
|
|
{
|
|
ConvertGridNoToXY( pTSoldier->sGridNo, &sXPos, &sYPos );
|
|
|
|
// Get direction from mouse pos
|
|
sFacingDir = GetDirectionFromXY( sXPos, sYPos, pSoldier );
|
|
|
|
// Set our guy facing
|
|
SendSoldierSetDesiredDirectionEvent( pSoldier, sFacingDir );
|
|
|
|
// Set NPC facing
|
|
SendSoldierSetDesiredDirectionEvent( pTSoldier, gOppositeDirection[ sFacingDir ] );
|
|
|
|
// Stop our guys...
|
|
EVENT_StopMerc( pSoldier, pSoldier->sGridNo, pSoldier->bDirection );
|
|
}
|
|
|
|
pTMilitiaSoldier = pTSoldier; //lal
|
|
|
|
//lal
|
|
// ATE; Check for normal civs...
|
|
if ( GetCivType( pTSoldier ) != CIV_TYPE_NA )
|
|
{
|
|
//lal
|
|
if ( ( pTSoldier->bTeam == MILITIA_TEAM ) && ( gGameExternalOptions.fAllowTacticalMilitiaCommand == TRUE ) && (pSoldier->bSide == pTSoldier->bSide) )
|
|
{
|
|
PopupMilitiaControlMenu( pTSoldier );
|
|
return( FALSE );
|
|
}
|
|
else
|
|
{
|
|
// Deduct points from our guy....
|
|
DeductPoints( pSoldier, sAPCost, 0 );
|
|
|
|
StartCivQuote( pTSoldier );
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// Are we an EPC that is being escorted?
|
|
if ( pTSoldier->ubProfile != NO_PROFILE && pTSoldier->ubWhatKindOfMercAmI == MERC_TYPE__EPC )
|
|
{
|
|
return( InitiateConversation( pTSoldier, pSoldier, APPROACH_EPC_WHO_IS_RECRUITED, 0 ) );
|
|
//Converse( pTSoldier->ubProfile, pSoldier->ubProfile, APPROACH_EPC_WHO_IS_RECRUITED, 0 );
|
|
}
|
|
else if (pTSoldier->bNeutral)
|
|
{
|
|
switch( pTSoldier->ubProfile )
|
|
{
|
|
case JIM:
|
|
case JACK:
|
|
case OLAF:
|
|
case RAY:
|
|
case OLGA:
|
|
case TYRONE:
|
|
// Start combat etc
|
|
DeleteTalkingMenu();
|
|
CancelAIAction( pTSoldier, TRUE );
|
|
AddToShouldBecomeHostileOrSayQuoteList( pTSoldier->ubID );
|
|
break;
|
|
default:
|
|
// Start talking!
|
|
return( InitiateConversation( pTSoldier, pSoldier, NPC_INITIAL_QUOTE, 0 ) );
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Start talking with hostile NPC
|
|
return( InitiateConversation( pTSoldier, pSoldier, APPROACH_ENEMY_NPC_QUOTE, 0 ) );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN IsValidSecondHandShot( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( Item[ pSoldier->inv[ SECONDHANDPOS ].usItem ].usItemClass == IC_GUN &&
|
|
!(Item[ pSoldier->inv[SECONDHANDPOS ].usItem ].twohanded ) &&
|
|
!pSoldier->bDoBurst &&
|
|
!Item[pSoldier->inv[ HANDPOS ].usItem].grenadelauncher &&
|
|
Item[ pSoldier->inv[HANDPOS].usItem ].usItemClass == IC_GUN &&
|
|
pSoldier->inv[SECONDHANDPOS].bGunStatus >= USABLE &&
|
|
pSoldier->inv[SECONDHANDPOS].ubGunShotsLeft > 0 )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
BOOLEAN IsValidSecondHandShotForReloadingPurposes( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// should be maintained as same as function above with line
|
|
// about ammo taken out!
|
|
if ( Item[ pSoldier->inv[ SECONDHANDPOS ].usItem ].usItemClass == IC_GUN &&
|
|
!pSoldier->bDoBurst &&
|
|
!Item[pSoldier->inv[ HANDPOS ].usItem].grenadelauncher &&
|
|
Item[ pSoldier->inv[HANDPOS].usItem ].usItemClass == IC_GUN &&
|
|
pSoldier->inv[SECONDHANDPOS].bGunStatus >= USABLE //&&
|
|
// pSoldier->inv[SECONDHANDPOS].ubGunShotsLeft > 0 &&
|
|
// gAnimControl[ pSoldier->usAnimState ].ubEndHeight != ANIM_PRONE )
|
|
)
|
|
{
|
|
return( TRUE );
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN CanRobotBeControlled( SOLDIERTYPE *pSoldier )
|
|
{
|
|
SOLDIERTYPE *pController;
|
|
|
|
if ( !( pSoldier->uiStatusFlags & SOLDIER_ROBOT ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( pSoldier->ubRobotRemoteHolderID == NOBODY )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
pController = MercPtrs[ pSoldier->ubRobotRemoteHolderID ];
|
|
|
|
if ( pController->bActive )
|
|
{
|
|
if ( ControllingRobot( pController ) )
|
|
{
|
|
// ALL'S OK!
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
BOOLEAN ControllingRobot( SOLDIERTYPE *pSoldier )
|
|
{
|
|
SOLDIERTYPE *pRobot;
|
|
INT8 bPos;
|
|
|
|
if ( !pSoldier->bActive )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// EPCs can't control the robot (no inventory to hold remote, for one)
|
|
if ( AM_AN_EPC( pSoldier ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Don't require pSoldier->bInSector here, it must work from mapscreen!
|
|
|
|
// are we in ok shape?
|
|
if ( pSoldier->bLife < OKLIFE || ( pSoldier->bTeam != gbPlayerNum ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// allow control from within vehicles - allows strategic travel in a vehicle with robot!
|
|
if ( ( pSoldier->bAssignment >= ON_DUTY ) && ( pSoldier->bAssignment != VEHICLE ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// is the soldier wearing a robot remote control?
|
|
bPos = FindRemoteControl( pSoldier );
|
|
if ( bPos == NO_SLOT )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// Find the robot
|
|
pRobot = FindSoldierByProfileID( ROBOT, TRUE );
|
|
if ( !pRobot )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( pRobot->bActive )
|
|
{
|
|
// Are we in the same sector....?
|
|
// ARM: CHANGED TO WORK IN MAPSCREEN, DON'T USE WorldSector HERE
|
|
if ( pRobot->sSectorX == pSoldier->sSectorX &&
|
|
pRobot->sSectorY == pSoldier->sSectorY &&
|
|
pRobot->bSectorZ == pSoldier->bSectorZ )
|
|
{
|
|
// they have to be either both in sector, or both on the road
|
|
if ( pRobot->fBetweenSectors == pSoldier->fBetweenSectors )
|
|
{
|
|
// if they're on the road...
|
|
if ( pRobot->fBetweenSectors )
|
|
{
|
|
// they have to be in the same squad or vehicle
|
|
if ( pRobot->bAssignment != pSoldier->bAssignment )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// if in a vehicle, must be the same vehicle
|
|
if ( pRobot->bAssignment == VEHICLE && ( pRobot->iVehicleId != pSoldier->iVehicleId ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
// all OK!
|
|
return( TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
SOLDIERTYPE *GetRobotController( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( pSoldier->ubRobotRemoteHolderID == NOBODY )
|
|
{
|
|
return( NULL );
|
|
}
|
|
else
|
|
{
|
|
return( MercPtrs[ pSoldier->ubRobotRemoteHolderID ] );
|
|
}
|
|
}
|
|
|
|
void UpdateRobotControllerGivenRobot( SOLDIERTYPE *pRobot )
|
|
{
|
|
SOLDIERTYPE *pTeamSoldier;
|
|
INT32 cnt = 0;
|
|
|
|
// Loop through guys and look for a controller!
|
|
|
|
// set up soldier ptr as first element in mercptrs list
|
|
cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
|
|
|
|
// run through list
|
|
for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++, pTeamSoldier++)
|
|
{
|
|
if ( pTeamSoldier->bActive )
|
|
{
|
|
if ( ControllingRobot( pTeamSoldier ) )
|
|
{
|
|
pRobot->ubRobotRemoteHolderID = pTeamSoldier->ubID;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
pRobot->ubRobotRemoteHolderID = NOBODY;
|
|
}
|
|
|
|
|
|
void UpdateRobotControllerGivenController( SOLDIERTYPE *pSoldier )
|
|
{
|
|
SOLDIERTYPE *pTeamSoldier;
|
|
INT32 cnt = 0;
|
|
|
|
// First see if are still controlling the robot
|
|
if ( !ControllingRobot( pSoldier ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// set up soldier ptr as first element in mercptrs list
|
|
cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
|
|
|
|
// Loop through guys to find the robot....
|
|
for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++, pTeamSoldier++)
|
|
{
|
|
if ( pTeamSoldier->bActive && ( pTeamSoldier->uiStatusFlags & SOLDIER_ROBOT ) )
|
|
{
|
|
pTeamSoldier->ubRobotRemoteHolderID = pSoldier->ubID;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void HandleSoldierTakeDamageFeedback( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// Do sound.....
|
|
// if ( pSoldier->bLife >= CONSCIOUSNESS )
|
|
{
|
|
// ATE: Limit how often we grunt...
|
|
if ( ( GetJA2Clock( ) - pSoldier->uiTimeSinceLastBleedGrunt ) > 1000 )
|
|
{
|
|
pSoldier->uiTimeSinceLastBleedGrunt = GetJA2Clock( );
|
|
|
|
DoMercBattleSound( pSoldier, (INT8)( BATTLE_SOUND_HIT1 + Random( 2 ) ) );
|
|
}
|
|
}
|
|
|
|
// Flash portrait....
|
|
pSoldier->fFlashPortrait = TRUE;
|
|
pSoldier->bFlashPortraitFrame = FLASH_PORTRAIT_STARTSHADE;
|
|
RESETTIMECOUNTER( pSoldier->PortraitFlashCounter, FLASH_PORTRAIT_DELAY );
|
|
}
|
|
|
|
|
|
void HandleSystemNewAISituation( SOLDIERTYPE *pSoldier, BOOLEAN fResetABC )
|
|
{
|
|
// Are we an AI guy?
|
|
if ( gTacticalStatus.ubCurrentTeam != gbPlayerNum && pSoldier->bTeam != gbPlayerNum )
|
|
{
|
|
if ( pSoldier->bNewSituation == IS_NEW_SITUATION )
|
|
{
|
|
// Cancel what they were doing....
|
|
pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
|
|
pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION;
|
|
pSoldier->fTurningFromPronePosition = FALSE;
|
|
pSoldier->ubPendingDirection = NO_PENDING_DIRECTION;
|
|
pSoldier->ubPendingAction = NO_PENDING_ACTION;
|
|
pSoldier->bEndDoorOpenCode = 0;
|
|
|
|
// if this guy isn't under direct AI control, WHO GIVES A FLYING FLICK?
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_UNDERAICONTROL )
|
|
{
|
|
if ( pSoldier->fTurningToShoot )
|
|
{
|
|
pSoldier->fTurningToShoot = FALSE;
|
|
// Release attacker
|
|
// OK - this is hightly annoying , but due to the huge combinations of
|
|
// things that can happen - 1 of them is that sLastTarget will get unset
|
|
// after turn is done - so set flag here to tell it not to...
|
|
pSoldier->fDontUnsetLastTargetFromTurn = TRUE;
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending fire because saw something: DONE IN SYSTEM NEW SITUATION") );
|
|
ReduceAttackBusyCount( pSoldier->ubID, FALSE );
|
|
}
|
|
|
|
if ( pSoldier->pTempObject != NULL )
|
|
{
|
|
// Place it back into inv....
|
|
AutoPlaceObject( pSoldier, pSoldier->pTempObject, FALSE );
|
|
MemFree( pSoldier->pTempObject );
|
|
pSoldier->pTempObject = NULL;
|
|
pSoldier->usPendingAnimation = NO_PENDING_ANIMATION;
|
|
pSoldier->usPendingAnimation2 = NO_PENDING_ANIMATION;
|
|
|
|
// Decrement attack counter...
|
|
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("@@@@@@@ Reducing attacker busy count..., ending throw because saw something: DONE IN SYSTEM NEW SITUATION") );
|
|
ReduceAttackBusyCount( pSoldier->ubID, FALSE );
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void InternalPlaySoldierFootstepSound( SOLDIERTYPE * pSoldier )
|
|
{
|
|
UINT8 ubRandomSnd;
|
|
INT8 bVolume = MIDVOLUME;
|
|
// Assume outside
|
|
UINT32 ubSoundBase = WALK_LEFT_OUT;
|
|
UINT8 ubRandomMax = 4;
|
|
|
|
// Determine if we are on the floor
|
|
if ( !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
|
|
{
|
|
if ( pSoldier->usAnimState == HOPFENCE )
|
|
{
|
|
bVolume = HIGHVOLUME;
|
|
}
|
|
|
|
if ( pSoldier->uiStatusFlags & SOLDIER_ROBOT )
|
|
{
|
|
PlaySoldierJA2Sample( pSoldier->ubID, ROBOT_BEEP, RATE_11025, SoundVolume( bVolume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ), TRUE );
|
|
return;
|
|
}
|
|
|
|
//if ( SoldierOnScreen( pSoldier->ubID ) )
|
|
{
|
|
if ( pSoldier->usAnimState == CRAWLING )
|
|
{
|
|
ubSoundBase = CRAWL_1;
|
|
}
|
|
else
|
|
{
|
|
// Pick base based on terrain over....
|
|
if ( pSoldier->bOverTerrainType == FLAT_FLOOR )
|
|
{
|
|
ubSoundBase = WALK_LEFT_IN;
|
|
}
|
|
else if ( pSoldier->bOverTerrainType == DIRT_ROAD || pSoldier->bOverTerrainType == PAVED_ROAD )
|
|
{
|
|
ubSoundBase = WALK_LEFT_ROAD;
|
|
}
|
|
else if ( pSoldier->bOverTerrainType == LOW_WATER || pSoldier->bOverTerrainType == MED_WATER )
|
|
{
|
|
ubSoundBase = WATER_WALK1_IN;
|
|
ubRandomMax = 2;
|
|
}
|
|
else if ( pSoldier->bOverTerrainType == DEEP_WATER )
|
|
{
|
|
ubSoundBase = SWIM_1;
|
|
ubRandomMax = 2;
|
|
}
|
|
}
|
|
|
|
// Pick a random sound...
|
|
do
|
|
{
|
|
ubRandomSnd = (UINT8)Random( ubRandomMax );
|
|
|
|
} while ( ubRandomSnd == pSoldier->ubLastFootPrintSound );
|
|
|
|
pSoldier->ubLastFootPrintSound = ubRandomSnd;
|
|
|
|
// OK, if in realtime, don't play at full volume, because too many people walking around
|
|
// sounds don't sound good - ( unless we are the selected guy, then always play at reg volume )
|
|
if ( ! ( gTacticalStatus.uiFlags & INCOMBAT ) && ( pSoldier->ubID != gusSelectedSoldier ) )
|
|
{
|
|
bVolume = LOWVOLUME;
|
|
}
|
|
|
|
PlaySoldierJA2Sample( pSoldier->ubID, ubSoundBase + pSoldier->ubLastFootPrintSound, RATE_11025, SoundVolume( bVolume, pSoldier->sGridNo ), 1, SoundDir( pSoldier->sGridNo ), TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlaySoldierFootstepSound( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// normally, not in stealth mode
|
|
if ( !pSoldier->bStealthMode )
|
|
{
|
|
InternalPlaySoldierFootstepSound( pSoldier );
|
|
}
|
|
}
|
|
|
|
void PlayStealthySoldierFootstepSound( SOLDIERTYPE *pSoldier )
|
|
{
|
|
// even if in stealth mode
|
|
InternalPlaySoldierFootstepSound( pSoldier );
|
|
}
|
|
|
|
|
|
|
|
void CrowsFlyAway( UINT8 ubTeam )
|
|
{
|
|
UINT32 cnt;
|
|
SOLDIERTYPE *pTeamSoldier;
|
|
|
|
for ( cnt = gTacticalStatus.Team[ ubTeam ].bFirstID, pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ ubTeam ].bLastID; cnt++, pTeamSoldier++ )
|
|
{
|
|
if ( pTeamSoldier->bActive && pTeamSoldier->bInSector )
|
|
{
|
|
if ( pTeamSoldier->ubBodyType == CROW && pTeamSoldier->usAnimState != CROW_FLY )
|
|
{
|
|
// fly away even if not seen!
|
|
HandleCrowFlyAway( pTeamSoldier );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef JA2BETAVERSION
|
|
void DebugValidateSoldierData( )
|
|
{
|
|
UINT32 cnt;
|
|
SOLDIERTYPE *pSoldier;
|
|
CHAR16 sString[ 1024 ];
|
|
BOOLEAN fProblemDetected = FALSE;
|
|
static uiFrameCount = 0;
|
|
|
|
|
|
// this function is too slow to run every frame, so do the check only every 50 frames
|
|
if ( uiFrameCount++ < 50 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// reset frame counter
|
|
uiFrameCount = 0;
|
|
|
|
|
|
// Loop through our team...
|
|
cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
|
|
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pSoldier++)
|
|
{
|
|
if ( pSoldier->bActive )
|
|
{
|
|
// OK, first check for alive people
|
|
// Don't do this check if we are a vehicle...
|
|
if ( pSoldier->bLife > 0 && !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
|
|
{
|
|
// Alive -- now check for proper group IDs
|
|
if ( pSoldier->ubGroupID == 0 && pSoldier->bAssignment != IN_TRANSIT && pSoldier->bAssignment != ASSIGNMENT_POW && !( pSoldier->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) ) )
|
|
{
|
|
// This is bad!
|
|
swprintf( sString, L"Soldier Data Error: Soldier %d is alive but has a zero group ID.", cnt );
|
|
fProblemDetected = TRUE;
|
|
}
|
|
else if ( ( pSoldier->ubGroupID != 0 ) && ( GetGroup( pSoldier->ubGroupID ) == NULL ) )
|
|
{
|
|
// This is bad!
|
|
swprintf( sString, L"Soldier Data Error: Soldier %d has an invalid group ID of %d.", cnt, pSoldier->ubGroupID );
|
|
fProblemDetected = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pSoldier->ubGroupID != 0 && ( pSoldier->uiStatusFlags & SOLDIER_DEAD ) )
|
|
{
|
|
// Dead guys should have 0 group IDs
|
|
//swprintf( sString, L"GroupID Error: Soldier %d is dead but has a non-zero group ID.", cnt );
|
|
//fProblemDetected = TRUE;
|
|
}
|
|
}
|
|
|
|
// check for invalid sector data
|
|
if ( ( pSoldier->bAssignment != IN_TRANSIT ) &&
|
|
( ( pSoldier->sSectorX <= 0 ) || ( pSoldier->sSectorX >= 17 ) ||
|
|
( pSoldier->sSectorY <= 0 ) || ( pSoldier->sSectorY >= 17 ) ||
|
|
( pSoldier->bSectorZ < 0 ) || ( pSoldier->bSectorZ > 3 ) ) )
|
|
{
|
|
swprintf( sString, L"Soldier Data Error: Soldier %d is located at %d/%d/%d.", cnt, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
|
|
fProblemDetected = TRUE;
|
|
}
|
|
}
|
|
|
|
if ( fProblemDetected )
|
|
{
|
|
SAIReportError( sString );
|
|
/*
|
|
if ( guiCurrentScreen == MAP_SCREEN )
|
|
DoMapMessageBox( MSG_BOX_BASIC_STYLE, sString, MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback );
|
|
else
|
|
DoMessageBox( MSG_BOX_BASIC_STYLE, sString, GAME_SCREEN, ( UINT8 )MSG_BOX_FLAG_OK, NULL, NULL );
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// also do this
|
|
ValidatePlayersAreInOneGroupOnly();
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
void BeginTyingToFall( SOLDIERTYPE *pSoldier )
|
|
{
|
|
pSoldier->bStartFallDir = pSoldier->bDirection;
|
|
pSoldier->fTryingToFall = TRUE;
|
|
|
|
// Randomize direction
|
|
if ( Random( 50 ) < 25 )
|
|
{
|
|
pSoldier->fFallClockwise = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pSoldier->fFallClockwise = FALSE;
|
|
}
|
|
}
|
|
|
|
void SetSoldierAsUnderAiControl( SOLDIERTYPE *pSoldierToSet )
|
|
{
|
|
SOLDIERTYPE *pSoldier=NULL;
|
|
INT32 cnt;
|
|
|
|
if ( pSoldierToSet == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Loop through ALL teams...
|
|
cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID;
|
|
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ LAST_TEAM ].bLastID; cnt++,pSoldier++)
|
|
{
|
|
if( pSoldier->bActive )
|
|
{
|
|
pSoldier->uiStatusFlags &= ~SOLDIER_UNDERAICONTROL;
|
|
}
|
|
}
|
|
|
|
pSoldierToSet->uiStatusFlags |= SOLDIER_UNDERAICONTROL;
|
|
}
|
|
|
|
void HandlePlayerTogglingLightEffects( BOOLEAN fToggleValue )
|
|
{
|
|
if( fToggleValue )
|
|
{
|
|
//Toggle light status
|
|
if ( gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] )
|
|
gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] = FALSE;
|
|
else
|
|
gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] = TRUE;
|
|
}
|
|
|
|
//Update all the mercs in the sector
|
|
EnableDisableSoldierLightEffects( gGameSettings.fOptions[ TOPTION_MERC_CASTS_LIGHT ] );
|
|
|
|
SetRenderFlags(RENDER_FLAG_FULL);
|
|
}
|
|
|
|
|
|
void EnableDisableSoldierLightEffects( BOOLEAN fEnableLights )
|
|
{
|
|
SOLDIERTYPE *pSoldier=NULL;
|
|
INT32 cnt;
|
|
|
|
// Loop through player teams...
|
|
cnt = gTacticalStatus.Team[ OUR_TEAM ].bFirstID;
|
|
for ( pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; cnt++,pSoldier++)
|
|
{
|
|
//if the soldier is in the sector
|
|
if( pSoldier->bActive && pSoldier->bInSector && pSoldier->bLife >= OKLIFE )
|
|
{
|
|
//if we are to enable the lights
|
|
if( fEnableLights )
|
|
{
|
|
//Add the light around the merc
|
|
PositionSoldierLight( pSoldier );
|
|
}
|
|
else
|
|
{
|
|
//Delete the fake light the merc casts
|
|
DeleteSoldierLight( pSoldier );
|
|
|
|
//Light up the merc though
|
|
SetSoldierPersonalLightLevel( pSoldier );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetSoldierPersonalLightLevel( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if( pSoldier == NULL )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( pSoldier->sGridNo == NOWHERE )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//THe light level for the soldier
|
|
gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubShadeLevel = 3;
|
|
gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubSumLights = 5;
|
|
gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubMaxLights = 5;
|
|
gpWorldLevelData[pSoldier->sGridNo].pMercHead->ubNaturalShadeLevel = 5;
|
|
}
|