#ifdef PRECOMPILEDHEADERS
#include "Tactical All.h"
#else
#include "sgp.h"
#include "worlddef.h"
#include "points.h"
#include "overhead.h"
#include "Font control.h"
#include "interface.h"
#include "Isometric utils.h"
#include "pathai.h"
#include "interface.h"
#include "message.h"
#include "Animation Control.h"
#include "Weapons.h"
#include "structure wrap.h"
#include "dialogue control.h"
#include "items.h"
#include "rt time defines.h"
#include "ai.h"
#include "handle ui.h"
#include "text.h"
#include "SkillCheck.h"
#include "wcheck.h"
#include "Soldier Profile.h"
#endif
//rain
//#define BREATH_GAIN_REDUCTION_PER_RAIN_INTENSITY 25
//end rain
extern BOOLEAN IsValidSecondHandShot( SOLDIERTYPE *pSoldier );
INT16 GetBreathPerAP( SOLDIERTYPE *pSoldier, UINT16 usAnimState );
INT16 TerrainActionPoints( SOLDIERTYPE *pSoldier, INT16 sGridno, INT8 bDir, INT8 bLevel )
{
INT16 sAPCost = 0;
INT16 sSwitchValue;
BOOLEAN fHiddenStructVisible; // Used for hidden struct visiblity
if ( pSoldier->bStealthMode )
sAPCost += AP_STEALTH_MODIFIER;
if ( pSoldier->bReverse || gUIUseReverse )
sAPCost += AP_REVERSE_MODIFIER;
//if (GridCost[gridno] == NPCMINECOST)
// switchValue = BackupGridCost[gridno];
//else
sSwitchValue = gubWorldMovementCosts[sGridno][bDir][ bLevel ];
// Check reality vs what the player knows....
if ( pSoldier->bTeam == gbPlayerNum )
{
// Is this obstcale a hidden tile that has not been revealed yet?
if( DoesGridnoContainHiddenStruct( (UINT16)sGridno, &fHiddenStructVisible ) )
{
// Are we not visible, if so use terrain costs!
if ( !fHiddenStructVisible )
{
// Set cost of terrain!
sSwitchValue = gTileTypeMovementCost[ gpWorldLevelData[ sGridno ].ubTerrainID ];
}
}
}
if ( sSwitchValue == TRAVELCOST_NOT_STANDING )
{
// use the cost of the terrain!
sSwitchValue = gTileTypeMovementCost[ gpWorldLevelData[ sGridno ].ubTerrainID ];
}
else if (IS_TRAVELCOST_DOOR( sSwitchValue ))
{
sSwitchValue = DoorTravelCost( pSoldier, sGridno, (UINT8) sSwitchValue, (BOOLEAN) (pSoldier->bTeam == gbPlayerNum), NULL );
}
if (sSwitchValue >= TRAVELCOST_BLOCKED && sSwitchValue != TRAVELCOST_DOOR )
{
return(100); // Cost too much to be considered!
}
switch( sSwitchValue )
{
case TRAVELCOST_DIRTROAD :
case TRAVELCOST_FLAT : sAPCost += AP_MOVEMENT_FLAT;
break;
//case TRAVELCOST_BUMPY :
case TRAVELCOST_GRASS : sAPCost += AP_MOVEMENT_GRASS;
break;
case TRAVELCOST_THICK : sAPCost += AP_MOVEMENT_BUSH;
break;
case TRAVELCOST_DEBRIS : sAPCost += AP_MOVEMENT_RUBBLE;
break;
case TRAVELCOST_SHORE : sAPCost += AP_MOVEMENT_SHORE; // wading shallow water
break;
case TRAVELCOST_KNEEDEEP : sAPCost += AP_MOVEMENT_LAKE; // wading waist/chest deep - very slow
break;
case TRAVELCOST_DEEPWATER: sAPCost += AP_MOVEMENT_OCEAN; // can swim, so it's faster than wading
break;
/*
case TRAVELCOST_VEINEND :
case TRAVELCOST_VEINMID : sAPCost += AP_MOVEMENT_FLAT;
break;
*/
case TRAVELCOST_DOOR : sAPCost += AP_MOVEMENT_FLAT;
break;
// cost for jumping a fence REPLACES all other AP costs!
case TRAVELCOST_FENCE : return( AP_JUMPFENCE );
case TRAVELCOST_NONE : return( 0 );
default:
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Calc AP: Unrecongnized MP type %d in %d, direction %d", sSwitchValue, sGridno, bDir ) );
break;
}
if (bDir & 1)
{
sAPCost = (sAPCost * 14) / 10;
}
return(sAPCost);
}
INT16 BreathPointAdjustmentForCarriedWeight( SOLDIERTYPE * pSoldier )
{
UINT32 uiCarriedPercent;
UINT32 uiPercentCost;
uiCarriedPercent = CalculateCarriedWeight( pSoldier );
if (uiCarriedPercent < 101)
{
// normal BP costs
uiPercentCost = 100;
}
else
{
if (uiCarriedPercent < 151)
{
// between 101 and 150% of max carried weight, extra BP cost
// of 1% per % above 100... so at 150%, we pay 150%
uiPercentCost = 100 + (uiCarriedPercent - 100) * 3;
}
else if (uiCarriedPercent < 201)
{
// between 151 and 200% of max carried weight, extra BP cost
// of 2% per % above 150... so at 200%, we pay 250%
uiPercentCost = 100 + (uiCarriedPercent - 100) * 3 + (uiCarriedPercent - 150);
}
else
{
// over 200%, extra BP cost of 3% per % above 200
uiPercentCost = 100 + (uiCarriedPercent - 100) * 3 + (uiCarriedPercent - 150) + (uiCarriedPercent - 200);
// so at 250% weight, we pay 400% breath!
}
}
return( (INT16) uiPercentCost );
}
INT16 TerrainBreathPoints(SOLDIERTYPE * pSoldier, INT16 sGridno,INT8 bDir, UINT16 usMovementMode)
{
INT32 iPoints=0;
UINT8 ubMovementCost;
ubMovementCost = gubWorldMovementCosts[sGridno][bDir][0];
switch( ubMovementCost )
{
case TRAVELCOST_DIRTROAD :
case TRAVELCOST_FLAT : iPoints = BP_MOVEMENT_FLAT; break;
//case TRAVELCOST_BUMPY :
case TRAVELCOST_GRASS : iPoints = BP_MOVEMENT_GRASS; break;
case TRAVELCOST_THICK : iPoints = BP_MOVEMENT_BUSH; break;
case TRAVELCOST_DEBRIS : iPoints = BP_MOVEMENT_RUBBLE; break;
case TRAVELCOST_SHORE : iPoints = BP_MOVEMENT_SHORE; break; // wading shallow water
case TRAVELCOST_KNEEDEEP : iPoints = BP_MOVEMENT_LAKE; break; // wading waist/chest deep - very slow
case TRAVELCOST_DEEPWATER : iPoints = BP_MOVEMENT_OCEAN; break; // can swim, so it's faster than wading
// case TRAVELCOST_VEINEND :
// case TRAVELCOST_VEINMID : iPoints = BP_MOVEMENT_FLAT; break;
default:
if ( IS_TRAVELCOST_DOOR( ubMovementCost ) )
{
iPoints = BP_MOVEMENT_FLAT;
break;
}
/*
#ifdef TESTVERSION
NumMessage("ERROR: TerrainBreathPoints: Unrecognized grid cost = ",
GridCost[gridno]);
#endif
*/
return(0);
}
iPoints = iPoints * BreathPointAdjustmentForCarriedWeight( pSoldier ) / 100;
// ATE - MAKE MOVEMENT ALWAYS WALK IF IN WATER
if ( gpWorldLevelData[ sGridno ].ubTerrainID == DEEP_WATER || gpWorldLevelData[ sGridno ].ubTerrainID == MED_WATER || gpWorldLevelData[ sGridno ].ubTerrainID == LOW_WATER )
{
usMovementMode = WALKING;
}
// so, then we must modify it for other movement styles and accumulate
switch(usMovementMode)
{
case RUNNING:
case ADULTMONSTER_WALKING:
case BLOODCAT_RUN:
iPoints *= BP_RUN_ENERGYCOSTFACTOR; break;
case SIDE_STEP:
case WALK_BACKWARDS:
case BLOODCAT_WALK_BACKWARDS:
case MONSTER_WALK_BACKWARDS:
case WALKING : iPoints *= BP_WALK_ENERGYCOSTFACTOR; break;
case START_SWAT:
case SWATTING:
case SWAT_BACKWARDS:
iPoints *= BP_SWAT_ENERGYCOSTFACTOR; break;
case CRAWLING: iPoints *= BP_CRAWL_ENERGYCOSTFACTOR; break;
}
// ATE: Adjust these by realtime movement
if (!(gTacticalStatus.uiFlags & TURNBASED) || !(gTacticalStatus.uiFlags & INCOMBAT ) )
{
// ATE: ADJUST FOR RT - MAKE BREATH GO A LITTLE FASTER!
iPoints = (INT32)( iPoints * TB_BREATH_DEDUCT_MODIFIER );
}
return( (INT16) iPoints);
}
INT16 ActionPointCost( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bDir, UINT16 usMovementMode )
{
INT16 sTileCost, sPoints, sSwitchValue;
sPoints = 0;
// get the tile cost for that tile based on WALKING
sTileCost = TerrainActionPoints( pSoldier, sGridNo, bDir, pSoldier->bLevel );
// Get switch value...
sSwitchValue = gubWorldMovementCosts[ sGridNo ][ bDir ][ pSoldier->bLevel ];
// Tile cost should not be reduced based on movement mode...
if ( sSwitchValue == TRAVELCOST_FENCE )
{
return( sTileCost );
}
// ATE - MAKE MOVEMENT ALWAYS WALK IF IN WATER
if ( gpWorldLevelData[ sGridNo ].ubTerrainID == DEEP_WATER || gpWorldLevelData[ sGridNo ].ubTerrainID == MED_WATER || gpWorldLevelData[ sGridNo ].ubTerrainID == LOW_WATER )
{
usMovementMode = WALKING;
}
// so, then we must modify it for other movement styles and accumulate
if (sTileCost > 0)
{
switch(usMovementMode)
{
case RUNNING:
case ADULTMONSTER_WALKING:
case BLOODCAT_RUN:
sPoints = (INT16)(DOUBLE)( (sTileCost / RUNDIVISOR) ); break;
case CROW_FLY:
case SIDE_STEP:
case WALK_BACKWARDS:
case ROBOT_WALK:
case BLOODCAT_WALK_BACKWARDS:
case MONSTER_WALK_BACKWARDS:
case LARVAE_WALK:
case WALKING : sPoints = (sTileCost + WALKCOST); break;
case START_SWAT:
case SWAT_BACKWARDS:
case SWATTING: sPoints = (sTileCost + SWATCOST); break;
case CRAWLING: sPoints = (sTileCost + CRAWLCOST); break;
default:
// Invalid movement mode
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Invalid movement mode %d used in ActionPointCost", usMovementMode ) );
sPoints = 1;
}
}
if (sSwitchValue == TRAVELCOST_NOT_STANDING)
{
switch(usMovementMode)
{
case RUNNING:
case WALKING :
case LARVAE_WALK:
case SIDE_STEP:
case WALK_BACKWARDS:
// charge crouch APs for ducking head!
sPoints += AP_CROUCH;
break;
default:
break;
}
}
return( sPoints );
}
INT16 EstimateActionPointCost( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bDir, UINT16 usMovementMode, INT8 bPathIndex, INT8 bPathLength )
{
// This action point cost code includes the penalty for having to change
// stance after jumping a fence IF our path continues...
INT16 sTileCost, sPoints, sSwitchValue;
sPoints = 0;
// get the tile cost for that tile based on WALKING
sTileCost = TerrainActionPoints( pSoldier, sGridNo, bDir, pSoldier->bLevel );
// so, then we must modify it for other movement styles and accumulate
if (sTileCost > 0)
{
switch(usMovementMode)
{
case RUNNING:
case ADULTMONSTER_WALKING:
case BLOODCAT_RUN:
sPoints = (INT16)(DOUBLE)( (sTileCost / RUNDIVISOR) ); break;
case CROW_FLY:
case SIDE_STEP:
case ROBOT_WALK:
case WALK_BACKWARDS:
case BLOODCAT_WALK_BACKWARDS:
case MONSTER_WALK_BACKWARDS:
case LARVAE_WALK:
case WALKING : sPoints = (sTileCost + WALKCOST); break;
case START_SWAT:
case SWAT_BACKWARDS:
case SWATTING: sPoints = (sTileCost + SWATCOST); break;
case CRAWLING: sPoints = (sTileCost + CRAWLCOST); break;
default:
// Invalid movement mode
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Invalid movement mode %d used in ActionPointCost", usMovementMode ) );
sPoints = 1;
}
}
// Get switch value...
sSwitchValue = gubWorldMovementCosts[ sGridNo ][ bDir ][ pSoldier->bLevel ];
// ATE: If we have a 'special cost, like jump fence...
if ( sSwitchValue == TRAVELCOST_FENCE )
{
// If we are changeing stance ( either before or after getting there....
// We need to reflect that...
switch(usMovementMode)
{
case SIDE_STEP:
case WALK_BACKWARDS:
case RUNNING:
case WALKING :
// Add here cost to go from crouch to stand AFTER fence hop....
// Since it's AFTER.. make sure we will be moving after jump...
if ( ( bPathIndex + 2 ) < bPathLength )
{
sPoints += AP_CROUCH;
}
break;
case SWATTING:
case START_SWAT:
case SWAT_BACKWARDS:
// Add cost to stand once there BEFORE....
sPoints += AP_CROUCH;
break;
case CRAWLING:
// Can't do it here.....
break;
}
}
else if (sSwitchValue == TRAVELCOST_NOT_STANDING)
{
switch(usMovementMode)
{
case RUNNING:
case WALKING :
case SIDE_STEP:
case WALK_BACKWARDS:
// charge crouch APs for ducking head!
sPoints += AP_CROUCH;
break;
default:
break;
}
}
return( sPoints );
}
BOOLEAN EnoughPoints( SOLDIERTYPE *pSoldier, INT16 sAPCost, INT32 iBPCost, BOOLEAN fDisplayMsg )
{
INT16 sNewAP = 0;
// If this guy is on a special move... don't care about APS, OR BPSs!
if ( pSoldier->ubWaitActionToDo )
{
return( TRUE );
}
if ( pSoldier->ubQuoteActionID >=QUOTE_ACTION_ID_TRAVERSE_EAST && pSoldier->ubQuoteActionID <= QUOTE_ACTION_ID_TRAVERSE_NORTH )
{
// AI guy on special move off map
return( TRUE );
}
// IN realtime.. only care about BPs
if ( ( gTacticalStatus.uiFlags & REALTIME ) || !(gTacticalStatus.uiFlags & INCOMBAT ) )
{
sAPCost = 0;
}
#ifdef NETWORKED
if( !IsTheSolderUnderMyControl( pSoldier->ubID) )
{
return( TRUE );
}
#endif
// Get New points
sNewAP = pSoldier->bActionPoints - sAPCost;
// If we cannot deduct points, return FALSE
if ( sNewAP < 0 )
{
// Display message if it's our own guy
if ( pSoldier->bTeam == gbPlayerNum && fDisplayMsg )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, TacticalStr[ NOT_ENOUGH_APS_STR ] );
}
return( FALSE );
}
return( TRUE );
}
void DeductPoints( SOLDIERTYPE *pSoldier, INT16 sAPCost, INT32 iBPCost,BOOLEAN fProactive )
{
INT16 sNewAP = 0, sNewBP = 0;
INT8 bNewBreath;
// in real time, there IS no AP cost, (only breath cost)
if (!(gTacticalStatus.uiFlags & TURNBASED) || !(gTacticalStatus.uiFlags & INCOMBAT ) )
{
sAPCost = 0;
}
// Get New points
sNewAP = pSoldier->bActionPoints - sAPCost;
// If this is the first time with no action points, set UI flag
if ( sNewAP <= 0 && pSoldier->bActionPoints > 0 )
{
pSoldier->fUIFirstTimeNOAP = TRUE;
fInterfacePanelDirty = TRUE;
}
// If we cannot deduct points, return FALSE
if ( sNewAP < 0 )
{
sNewAP = 0;
}
pSoldier->bActionPoints = (INT8)sNewAP;
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Deduct Points (%d at %d) %d %d", pSoldier->ubID, pSoldier->sGridNo, sAPCost, iBPCost ) );
if ( AM_A_ROBOT( pSoldier ) )
{
// zap all breath costs for robot
iBPCost = 0;
}
// is there a BREATH deduction/transaction to be made? (REMEMBER: could be a GAIN!)
// NB: iBPCost > 0 - breath loss, iBPCost < 0 - breath gain
if (iBPCost)
{
// Adjust breath changes due to spending or regaining of energy
iBPCost = AdjustBreathPts(pSoldier, iBPCost);
// Snap: award some health and strength for exertion
// Do 4 StatChange rolls for health (2 for strength) per 10 breath points spent?
// NB: The scale of iBPCost is 100 per breath point (BP_RATIO_RED_PTS_TO_NORMAL)
if ( PTR_OURTEAM && iBPCost >= BP_MOVEMENT_GRASS && fProactive
&& (INT32) PreRandom( 10 * BP_RATIO_RED_PTS_TO_NORMAL ) < iBPCost )
{
StatChange(pSoldier, HEALTHAMT, 4, FALSE);
StatChange(pSoldier, STRAMT, 2, FALSE);
}
// CJC: moved check for high breathred to below so that negative breath can be detected
// Snap: kill the overflow bug already!
// NB: normally, sBreathRed == (100 - bBreath)*BP_RATIO_RED_PTS_TO_NORMAL
const INT16 BREATH_RED_MAX = 100 * BP_RATIO_RED_PTS_TO_NORMAL;
if ( pSoldier->sBreathRed + iBPCost < 0 )
{
pSoldier->sBreathRed = 0;
}
else if ( pSoldier->sBreathRed + iBPCost > BREATH_RED_MAX )
{
// Snap: moved this up, because it had no effect below
// Take off 1 AP per 5 (negative) breath...
pSoldier->bActionPoints -= ( pSoldier->sBreathRed + iBPCost - BREATH_RED_MAX )
/ ( 5 * BP_RATIO_RED_PTS_TO_NORMAL );
if ( pSoldier->bActionPoints < 0 )
{
pSoldier->bActionPoints = 0;
}
pSoldier->sBreathRed = BREATH_RED_MAX;
}
else
{
pSoldier->sBreathRed += iBPCost;
}
// Get new breath
bNewBreath = ( pSoldier->bBreathMax * BP_RATIO_RED_PTS_TO_NORMAL - pSoldier->sBreathRed )
/ BP_RATIO_RED_PTS_TO_NORMAL;
// Snap: This should never happen...
if ( bNewBreath > pSoldier->bBreathMax )
{
bNewBreath = pSoldier->bBreathMax;
}
else if ( bNewBreath < 0 )
{
bNewBreath = 0;
}
pSoldier->bBreath = bNewBreath;
}
// UPDATE BAR
DirtyMercPanelInterface( pSoldier, DIRTYLEVEL1 );
}
INT32 AdjustBreathPts(SOLDIERTYPE *pSold, INT32 iBPCost)
{
INT16 sBreathFactor = 100;
UINT8 ubBandaged;
//NumMessage("BEFORE adjustments, BREATH PTS = ",breathPts);
// in real time, there IS no AP cost, (only breath cost)
/*
if (!(gTacticalStatus.uiFlags & TURNBASED) || !(gTacticalStatus.uiFlags & INCOMBAT ) )
{
// ATE: ADJUST FOR RT - MAKE BREATH GO A LITTLE FASTER!
iBPCost *= TB_BREATH_DEDUCT_MODIFIER;
}
*/
// adjust breath factor for current breath deficiency
sBreathFactor += (100 - pSold->bBreath);
// adjust breath factor for current life deficiency (but add 1/2 bandaging)
ubBandaged = pSold->bLifeMax - pSold->bLife - pSold->bBleeding;
//sBreathFactor += (pSold->bLifeMax - (pSold->bLife + (ubBandaged / 2)));
sBreathFactor += 100 * (pSold->bLifeMax - (pSold->bLife + (ubBandaged / 2))) / pSold->bLifeMax;
if ( pSold->bStrength > 80 )
{
// give % reduction to breath costs for high strength mercs
sBreathFactor -= (pSold->bStrength - 80) / 2;
}
/* THIS IS OLD JAGGED ALLIANCE STUFF (left for possible future reference)
// apply penalty due to high temperature, heat, and hot Metaviran sun
// if INDOORS, in DEEP WATER, or possessing HEAT TOLERANCE trait
if ((ptr->terrtype == FLOORTYPE) || (ptr->terr >= OCEAN21) ||
(ptr->trait == HEAT_TOLERANT))
breathFactor += (Status.heatFactor / 5); // 20% of normal heat penalty
else
breathFactor += Status.heatFactor; // not used to this!
*/
// if a non-swimmer type is thrashing around in deep water
if ( (pSold->ubProfile != NO_PROFILE ) && (gMercProfiles[ pSold->ubProfile ].bPersonalityTrait == NONSWIMMER) )
{
if ( pSold->usAnimState == DEEP_WATER_TRED || pSold->usAnimState == DEEP_WATER_SWIM)
{
sBreathFactor *= 5; // lose breath 5 times faster in deep water!
}
}
if ( sBreathFactor == 0 )
{
iBPCost = 0;
}
else if (iBPCost > 0) // breath DECREASE
// increase breath COST by breathFactor
iBPCost = ((iBPCost * sBreathFactor) / 100);
else // breath INCREASE
// decrease breath GAIN by breathFactor
iBPCost = ((iBPCost * 100) / sBreathFactor);
return(iBPCost);
}
void UnusedAPsToBreath(SOLDIERTYPE *pSold)
{
INT16 sUnusedAPs, sBreathPerAP = 0, sBreathChange, sRTBreathMod;
BOOLEAN fAnimTypeFound = FALSE;
// Note to Andrew (or whomever else it may concern):
// This function deals with BETWEEN TURN breath/energy gains. The basic concept is:
//
// - look at LAST (current) animation of merc to see what he's now doing
// - look at how many AP remain unspent (indicating duration of time doing that anim)
//
// figure out how much breath/energy (if any) he should recover. Obviously if a merc
// is STANDING BREATHING and hasn't spent any AP then it means he *stood around* for
// the entire duration of one turn (which, instead of spending energy, REGAINS energy)
// COMMENTED OUT FOR NOW SINCE MOST OF THE ANIMATION DEFINES DO NOT MATCH
// If we are not in turn-based combat...
if ( pSold->uiStatusFlags & SOLDIER_VEHICLE )
{
return;
}
if ( !( gTacticalStatus.uiFlags & TURNBASED ) || !(gTacticalStatus.uiFlags & INCOMBAT ) )
{
// ALRIGHT, GIVE A FULL AMOUNT BACK, UNLES MODIFIED BY WHAT ACTIONS WE WERE DOING
sBreathPerAP = GetBreathPerAP( pSold, pSold->usAnimState );
// adjust for carried weight
sBreathPerAP = sBreathPerAP * 100 / BreathPointAdjustmentForCarriedWeight( pSold );
// If this value is -ve, we have a gain, else we have a loos which we should not really do
// We just want to limit this to no gain if we were doing stuff...
sBreathChange = 3 * sBreathPerAP;
// Adjust for on drugs
HandleBPEffectDueToDrugs( pSold, &sBreathChange );
if ( sBreathChange > 0 )
{
sBreathChange = 0;
}
else
{
// We have a gain, now limit this depending on what we were doing...
// OK for RT, look at how many tiles we have moved, our last move anim
if ( pSold->ubTilesMovedPerRTBreathUpdate > 0 )
{
// How long have we done this for?
// And what anim were we doing?
sBreathPerAP = GetBreathPerAP( pSold, pSold->usLastMovementAnimPerRTBreathUpdate );
sRTBreathMod = sBreathPerAP * pSold->ubTilesMovedPerRTBreathUpdate;
// Deduct some if we were exerting ourselves
// We add here because to gain breath, sBreathChange needs to be -ve
if ( sRTBreathMod > 0 )
{
sBreathChange += sRTBreathMod;
}
if ( sBreathChange < 0 )
{
sBreathChange = 0;
}
}
}
// Divide by a number to adjust that in realtimer we do not want to recover as
// as fast as the TB values do
sBreathChange *= TB_BREATH_RECOVER_MODIFIER;
// adjust breath only, don't touch action points!
DeductPoints(pSold,0,(INT16)sBreathChange );
// Reset value for RT breath update
pSold->ubTilesMovedPerRTBreathUpdate = 0;
}
else
{
// if merc has any APs left unused this turn (that aren't carrying over)
if (pSold->bActionPoints > MAX_AP_CARRIED)
{
sUnusedAPs = pSold->bActionPoints - MAX_AP_CARRIED;
sBreathPerAP = GetBreathPerAP( pSold, pSold->usAnimState );
if (sBreathPerAP < 0)
{
// can't gain any breath when we've just been gassed, OR
// if standing in tear gas without a gas mask on
if ( pSold->uiStatusFlags & SOLDIER_GASSED )
{
return; // can't breathe here, so get no breath back!
}
}
// adjust for carried weight
sBreathPerAP = sBreathPerAP * 100 / BreathPointAdjustmentForCarriedWeight( pSold );
sBreathChange = (AP_MAXIMUM - sUnusedAPs) * sBreathPerAP;
}
else
{
sBreathChange = 0;
}
// Adjust for on drugs
HandleBPEffectDueToDrugs( pSold, &sBreathChange );
// adjust breath only, don't touch action points!
DeductPoints(pSold,0,(INT16)sBreathChange );
}
}
//rain
extern INT8 gbCurrentRainIntensity;
//end rain
INT16 GetBreathPerAP( SOLDIERTYPE *pSoldier, UINT16 usAnimState )
{
INT16 sBreathPerAP = 0;
BOOLEAN fAnimTypeFound = FALSE;
if ( gAnimControl[ usAnimState ].uiFlags & ANIM_VARIABLE_EFFORT )
{
// Default effort
sBreathPerAP = BP_PER_AP_MIN_EFFORT;
// OK, check if we are in water and are waling/standing
if ( MercInWater( pSoldier ) )
{
switch( usAnimState )
{
case STANDING:
sBreathPerAP = BP_PER_AP_LT_EFFORT;
break;
case WALKING:
sBreathPerAP = BP_PER_AP_MOD_EFFORT;
break;
}
}
else
{
switch( usAnimState )
{
case STANDING:
sBreathPerAP = BP_PER_AP_NO_EFFORT;
break;
case WALKING:
sBreathPerAP = BP_PER_AP_LT_EFFORT;
break;
}
}
fAnimTypeFound = TRUE;
}
if ( gAnimControl[ usAnimState ].uiFlags & ANIM_NO_EFFORT )
{
sBreathPerAP = BP_PER_AP_NO_EFFORT;
fAnimTypeFound = TRUE;
}
if ( gAnimControl[ usAnimState ].uiFlags & ANIM_MIN_EFFORT )
{
sBreathPerAP = BP_PER_AP_MIN_EFFORT;
fAnimTypeFound = TRUE;
}
if ( gAnimControl[ usAnimState ].uiFlags & ANIM_LIGHT_EFFORT )
{
sBreathPerAP = BP_PER_AP_LT_EFFORT;
fAnimTypeFound = TRUE;
}
if ( gAnimControl[ usAnimState ].uiFlags & ANIM_MODERATE_EFFORT )
{
sBreathPerAP = BP_PER_AP_MOD_EFFORT;
fAnimTypeFound = TRUE;
}
if ( !fAnimTypeFound )
{
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String("Unknown end-of-turn breath anim: %s", gAnimControl[ usAnimState ].zAnimStr ) );
}
//rain
// Reduce breath gain on 25%/rain intensity
if( sBreathPerAP < 0 && ( pSoldier->bLevel ||!FindStructure( pSoldier->sGridNo, STRUCTURE_ROOF ) ) && pSoldier->bBreath > 1)
{
sBreathPerAP -= (INT16)( sBreathPerAP * gbCurrentRainIntensity * gGameExternalOptions.ubBreathGainReductionPerRainIntensity / 100 );
}
//end rain
return( sBreathPerAP );
}
//UINT8 CalcAPsToBurst( INT8 bBaseActionPoints, UINT16 usItem )
UINT8 CalcAPsToBurst( INT8 bBaseActionPoints, OBJECTTYPE * pObj )
{
INT32 aps;
// base APs is what you'd get from CalcActionPoints();
// NB round UP, so 21-25 APs pay full
aps = ( Weapon[ pObj->usItem ].bBurstAP * bBaseActionPoints + (AP_MAXIMUM - 1) ) / AP_MAXIMUM;
/*if ( GetPercentBurstFireAPReduction(pObj)>0 )
{
aps = (aps * 100) / (100 + GetBurstFireAPReductionStatus(pObj) / (100/GetPercentBurstFireAPReduction(pObj)));
}
if ( GetPercentAPReduction(pObj)>0 )
{
aps = (aps * 100) / (100 + GetAPReductionStatus(pObj) / (100/GetPercentAPReduction(pObj)));
}*/
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToBurst: before bonus aps = %d, std bonus = %d, burst bonus = %d", aps,GetPercentAPReduction(pObj),GetPercentBurstFireAPReduction(pObj)));
// Snap: do this a little differently: % reduction means
// aps <- aps * ( 100 - red ) / 100
aps = ( aps * ( 100 - GetPercentAPReduction(pObj) ) ) / 100;
// Snap: moved this up to allow burst AP reduction up to 100%
aps = __max( aps, ( Weapon[ pObj->usItem ].bBurstAP + 1 ) / 2 );
aps = ( aps * ( 100 - GetPercentBurstFireAPReduction(pObj) ) ) / 100;
if ( aps < 0 ) aps = 0;
else if ( aps > AP_MAXIMUM ) aps = AP_MAXIMUM;
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToBurst: return aps = %d", aps));
return (UINT8) aps;
}
UINT8 CalcAPsToAutofire( INT8 bBaseActionPoints, OBJECTTYPE * pObj, UINT8 bDoAutofire )
{
// INT8 bAttachPos;
INT32 aps=AP_MAXIMUM + 1;
if ( GetAutofireShotsPerFiveAPs (pObj) )
{
// Snap: Do this just like in CalcAPsToBurst
const INT32 autofireaps =
aps = ( ( 5 * bDoAutofire * bBaseActionPoints ) / GetAutofireShotsPerFiveAPs(pObj) + (AP_MAXIMUM - 1) ) / AP_MAXIMUM;
//DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToAutofire: base aps = %d, # shots = %d",aps,pSoldier->bDoAutofire ));
//check for spring and bolt
//bAttachPos = FindAttachment( &(pSoldier->inv[HANDPOS]), SPRING_AND_BOLT_UPGRADE );
//if ( bAttachPos != -1 )
//{
// aps = (aps * 100) / (100 + pSoldier->inv[HANDPOS].bAttachStatus[ bAttachPos ] / 5);
// DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToAutofire: found rod and spring, aps = %d, # shots = %d",aps,pSoldier->bDoAutofire ));
//}
//bAttachPos = FindAttachment( &(pSoldier->inv[HANDPOS]), REFLEX_SCOPED );
//if ( bAttachPos != -1 )
//{
// aps = (aps * 100) / (100 + pSoldier->inv[HANDPOS].bAttachStatus[ bAttachPos ] / 5);
// DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToAutofire: found reflex scope, aps = %d, # shots = %d",aps,pSoldier->bDoAutofire ));
//}
//bAttachPos = FindAttachment( &(pSoldier->inv[HANDPOS]), REFLEX_UNSCOPED );
//if ( bAttachPos != -1 )
//{
// aps = ( aps * 100 ) / (100 + pSoldier->inv[HANDPOS].bAttachStatus[ bAttachPos ] / 5);
// DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToAutofire: found reflex sight, aps = %d, # shots = %d",aps,pSoldier->bDoAutofire ));
//}
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToAutoFire: before bonus aps = %d, std bonus = %d, auto bonus = %d", aps,GetPercentAPReduction(pObj),GetPercentAutofireAPReduction(pObj)));
aps = ( aps * ( 100 - GetPercentAPReduction(pObj) ) ) / 100;
aps = __max( aps, ( autofireaps + 1 ) / 2 );
// Snap: extend the burst bonus to autofire as well?
aps = ( aps * ( 100 - GetPercentAutofireAPReduction(pObj) ) ) / 100;
if ( aps < 0 ) aps = 0;
else if ( aps > AP_MAXIMUM ) aps = AP_MAXIMUM;
}
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("CalcAPsToAutoFire: return aps = %d", aps));
return (UINT8) aps;
}
UINT8 CalcTotalAPsToAttack( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubAddTurningCost, INT8 bAimTime )
{
UINT16 sAPCost = 0;
UINT16 usItemNum;
INT16 sActionGridNo;
UINT8 ubDirection;
INT16 sAdjustedGridNo;
UINT32 uiItemClass;
// LOOK IN BUDDY'S HAND TO DETERMINE WHAT TO DO HERE
usItemNum = pSoldier->inv[HANDPOS].usItem;
uiItemClass = Item[ usItemNum ].usItemClass;
if ( uiItemClass == IC_GUN || uiItemClass == IC_LAUNCHER || uiItemClass == IC_TENTACLES || uiItemClass == IC_THROWING_KNIFE )
{
sAPCost = MinAPsToAttack( pSoldier, sGridNo, ubAddTurningCost );
if ( pSoldier->bDoBurst )
{
if(pSoldier->bDoAutofire && GetAutofireShotsPerFiveAPs(&pSoldier->inv[HANDPOS]) > 0 )
sAPCost += CalcAPsToAutofire( CalcActionPoints( pSoldier ), &(pSoldier->inv[HANDPOS]), pSoldier->bDoAutofire );
else
sAPCost += CalcAPsToBurst( CalcActionPoints( pSoldier ), &(pSoldier->inv[HANDPOS]) );
}
else
{
sAPCost += bAimTime;
}
}
//ATE: HERE, need to calculate APs!
if ( uiItemClass & IC_EXPLOSV )
{
sAPCost = MinAPsToAttack( pSoldier, sGridNo, ubAddTurningCost );
//sAPCost = 5;
}
if ( uiItemClass == IC_PUNCH || ( uiItemClass == IC_BLADE && uiItemClass != IC_THROWING_KNIFE ) )
{
// IF we are at this gridno, calc min APs but if not, calc cost to goto this lication
if ( pSoldier->sGridNo != sGridNo )
{
// OK, in order to avoid path calculations here all the time... save and check if it's changed!
if ( pSoldier->sWalkToAttackGridNo == sGridNo )
{
sAdjustedGridNo = sGridNo;
sAPCost += (UINT8)( pSoldier->sWalkToAttackWalkToCost );
}
else
{
//INT32 cnt;
//INT16 sSpot;
UINT8 ubGuyThere;
INT16 sGotLocation = NOWHERE;
BOOLEAN fGotAdjacent = FALSE;
SOLDIERTYPE *pTarget;
ubGuyThere = WhoIsThere2( sGridNo, pSoldier->bLevel );
if ( ubGuyThere != NOBODY )
{
pTarget = MercPtrs[ ubGuyThere ];
if ( pSoldier->ubBodyType == BLOODCAT )
{
sGotLocation = FindNextToAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
if ( sGotLocation == -1 )
{
sGotLocation = NOWHERE;
}
}
else
{
sGotLocation = FindAdjacentPunchTarget( pSoldier, pTarget, &sAdjustedGridNo, &ubDirection );
}
}
if ( sGotLocation == NOWHERE && pSoldier->ubBodyType != BLOODCAT )
{
sActionGridNo = FindAdjacentGridEx( pSoldier, sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
if ( sActionGridNo == -1 )
{
sGotLocation = NOWHERE;
}
else
{
sGotLocation = sActionGridNo;
}
fGotAdjacent = TRUE;
}
if ( sGotLocation != NOWHERE )
{
if (pSoldier->sGridNo == sGotLocation || !fGotAdjacent )
{
pSoldier->sWalkToAttackWalkToCost = 0;
}
else
{
// Save for next time...
pSoldier->sWalkToAttackWalkToCost = PlotPath( pSoldier, sGotLocation, NO_COPYROUTE, NO_PLOT, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints );
if (pSoldier->sWalkToAttackWalkToCost == 0)
{
return( 99 );
}
}
}
else
{
return( 0 );
}
sAPCost += pSoldier->sWalkToAttackWalkToCost;
}
// Save old location!
pSoldier->sWalkToAttackGridNo = sGridNo;
// Add points to attack
sAPCost += MinAPsToAttack( pSoldier, sAdjustedGridNo, ubAddTurningCost );
//sAPCost += APsToTurnAround(pSoldier, sAdjustedGridNo);
}
else
{
// Add points to attack
// Use our gridno
sAPCost += MinAPsToAttack( pSoldier, sGridNo, ubAddTurningCost );
}
// Add aim time...
sAPCost += bAimTime;
}
return( (INT8)sAPCost );
}
UINT8 MinAPsToAttack(SOLDIERTYPE *pSoldier, INT16 sGridno, UINT8 ubAddTurningCost)
{
UINT16 sAPCost = 0;
UINT32 uiItemClass;
if ( pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO )
{
INT16 glItem;
// look for an attached grenade launcher
glItem = GetAttachedGrenadeLauncher( &(pSoldier->inv[ HANDPOS ]) );
if ( glItem == NONE )
{
// default to hand
// LOOK IN BUDDY'S HAND TO DETERMINE WHAT TO DO HERE
uiItemClass = Item[ pSoldier->inv[HANDPOS].usItem ].usItemClass;
}
else
{
uiItemClass = Item[ glItem ].usItemClass;
}
}
else
{
// LOOK IN BUDDY'S HAND TO DETERMINE WHAT TO DO HERE
uiItemClass = Item[ pSoldier->inv[HANDPOS].usItem ].usItemClass;
}
if ( uiItemClass == IC_BLADE || uiItemClass == IC_GUN || uiItemClass == IC_LAUNCHER || uiItemClass == IC_TENTACLES || uiItemClass == IC_THROWING_KNIFE )
{
sAPCost = MinAPsToShootOrStab( pSoldier, sGridno, ubAddTurningCost );
}
else if ( uiItemClass & ( IC_GRENADE | IC_THROWN ) )
{
sAPCost = MinAPsToThrow( pSoldier, sGridno, ubAddTurningCost );
}
else if ( uiItemClass == IC_PUNCH )
{
sAPCost = MinAPsToPunch( pSoldier, sGridno, ubAddTurningCost );
}
return( (UINT8)sAPCost );
}
INT8 CalcAimSkill( SOLDIERTYPE * pSoldier, UINT16 usWeapon )
{
INT8 bAimSkill;
if ( Item[ usWeapon ].usItemClass == IC_GUN || Item[ usWeapon ].usItemClass == IC_LAUNCHER )
{
// GUNS: modify aiming cost by shooter's MARKSMANSHIP
bAimSkill = EffectiveMarksmanship( pSoldier );
}
else
// for now use this for all other weapons
//if ( Item[ usInHand ].usItemClass == IC_BLADE )
{
// KNIVES: modify aiming cost by avg of attacker's DEXTERITY & AGILITY
bAimSkill = ( EffectiveDexterity( pSoldier ) + EffectiveAgility( pSoldier ) ) / 2;
//return( 4 );
}
return( bAimSkill );
}
UINT8 BaseAPsToShootOrStab( INT8 bAPs, INT8 bAimSkill, OBJECTTYPE * pObj )
{
INT32 Top, Bottom, rof;
// INT8 bAttachPos;
// Calculate default top & bottom of the magic "aiming" formula!
// get this man's maximum possible action points (ignoring carryovers)
// the 2 times is here only to allow rounding off using integer math later
//Top = 2 * bAPs;//CalcActionPoints( pSoldier );
// Shots per turn rating is for max. aimSkill(100), drops down to 1/2 at = 0
// DIVIDE BY 4 AT THE END HERE BECAUSE THE SHOTS PER TURN IS NOW QUADRUPLED!
// NB need to define shots per turn for ALL Weapons then.
rof = Weapon[ pObj->usItem ].ubShotsPer4Turns + GetRateOfFireBonus(pObj);
//Bottom = ( ( 50 + (bAimSkill / 2) ) * rof ) / 4;
// add minimum aiming time to the overall minimum AP_cost
// This here ROUNDS UP fractions of 0.5 or higher using integer math
// This works because 'top' is 2x what it really should be throughout
//return( ( ( ( 100 * Top ) / Bottom ) + 1) / 2);
// Snap: Refactored the formula to reduce the number of integer divisions
Top = 8 * bAPs * ( 100 - GetPercentAPReduction(pObj) );
Bottom = ( 100 + bAimSkill ) * rof;
// (this will round to the nearest integer)
return ( Top + Bottom / 2 ) / Bottom;
}
void GetAPChargeForShootOrStabWRTGunRaises( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubAddTurningCost, BOOLEAN *pfChargeTurning, BOOLEAN *pfChargeRaise )
{
UINT8 ubDirection;
UINT32 uiMercFlags;
UINT16 usTargID;
BOOLEAN fAddingTurningCost = FALSE;
BOOLEAN fAddingRaiseGunCost = FALSE;
if ( sGridNo != NOWHERE )
{
// OK, get a direction and see if we need to turn...
if (ubAddTurningCost)
{
// Given a gridno here, check if we are on a guy - if so - get his gridno
if ( FindSoldier( sGridNo, &usTargID, &uiMercFlags, FIND_SOLDIER_GRIDNO ) )
{
sGridNo = MercPtrs[ usTargID ]->sGridNo;
}
ubDirection = (UINT8)GetDirectionFromGridNo( sGridNo, pSoldier );
// Is it the same as he's facing?
if ( ubDirection != pSoldier->bDirection )
{
fAddingTurningCost = TRUE;
}
}
}
else
{
if (ubAddTurningCost)
{
// Assume we need to add cost!
fAddingTurningCost = TRUE;
}
}
if ( Item[ pSoldier->inv[ HANDPOS ].usItem ].usItemClass == IC_THROWING_KNIFE )
{
}
else
{
// Do we need to ready weapon?
if ( !( gAnimControl[ pSoldier->usAnimState ].uiFlags &( ANIM_FIREREADY | ANIM_FIRE ) ) )
{
fAddingRaiseGunCost = TRUE;
}
}
(*pfChargeTurning) = fAddingTurningCost;
(*pfChargeRaise ) = fAddingRaiseGunCost;
}
UINT8 MinAPsToShootOrStab(SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubAddTurningCost)
{
UINT32 uiMercFlags;
UINT16 usTargID;
INT8 bFullAPs;
INT8 bAimSkill;
UINT8 bAPCost = AP_MIN_AIM_ATTACK;
BOOLEAN fAddingTurningCost = FALSE;
BOOLEAN fAddingRaiseGunCost = FALSE;
UINT16 usItem;
UINT16 usRaiseGunCost = 0;
UINT16 usTurningCost = 0;
if ( pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO )
{
usItem = GetAttachedGrenadeLauncher(&pSoldier->inv[HANDPOS] );//UNDER_GLAUNCHER;
}
else
{
usItem = pSoldier->inv[ HANDPOS ].usItem;
}
GetAPChargeForShootOrStabWRTGunRaises( pSoldier, sGridNo, ubAddTurningCost, &fAddingTurningCost, &fAddingRaiseGunCost );
if ( Item[ usItem ].usItemClass == IC_THROWING_KNIFE )
{
// Do we need to stand up?
bAPCost += GetAPsToChangeStance( pSoldier, ANIM_STAND );
}
if ( AM_A_ROBOT( pSoldier ) )
{
fAddingRaiseGunCost = FALSE;
}
//Calculate usTurningCost
if ( fAddingTurningCost )
{
if ( Item[ usItem ].usItemClass == IC_THROWING_KNIFE )
usTurningCost = 1;
else
usTurningCost = GetAPsToLook( pSoldier );
}
//Calculate usRaiseGunCost
if (fAddingRaiseGunCost )
{
usRaiseGunCost = GetAPsToReadyWeapon( pSoldier, pSoldier->usAnimState );
pSoldier->fDontChargeReadyAPs = FALSE;
}
//charge the maximum of the 2
bAPCost += __max(usTurningCost,usRaiseGunCost);
if ( sGridNo != NOWHERE )
{
// Given a gridno here, check if we are on a guy - if so - get his gridno
if ( FindSoldier( sGridNo, &usTargID, &uiMercFlags, FIND_SOLDIER_GRIDNO ) )
{
sGridNo = MercPtrs[ usTargID ]->sGridNo;
}
}
// if attacking a new target (or if the specific target is uncertain)
if (( sGridNo != pSoldier->sLastTarget ) && !Item[usItem].rocketlauncher )
{
bAPCost += AP_CHANGE_TARGET;
}
// Snap: reversed DIGICRAB's change.
// bFullAPs are BASE APs, which do not include APs caried over from the previous turn.
bFullAPs = CalcActionPoints( pSoldier );
// aim skill is the same whether we are using 1 or 2 guns
bAimSkill = CalcAimSkill( pSoldier, usItem );
if ( pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO )
{
INT16 glItem;
OBJECTTYPE GrenadeLauncher;
// look for an attached grenade launcher
glItem = GetAttachedGrenadeLauncher( &(pSoldier->inv[ HANDPOS ]));
// create temporary grenade launcher and use that
if ( glItem != NONE )
{
CreateItem( glItem, GetGrenadeLauncherStatus(&pSoldier->inv[ HANDPOS ]), &GrenadeLauncher );
}
else
{
// fake it, use a 100 status...
CreateItem( UNDER_GLAUNCHER, 100, &GrenadeLauncher );
}
bAPCost += BaseAPsToShootOrStab( bFullAPs, bAimSkill, &GrenadeLauncher );
}
else if ( IsValidSecondHandShot( pSoldier ) )
{
// charge the maximum of the two
bAPCost += __max(
BaseAPsToShootOrStab( bFullAPs, bAimSkill, &(pSoldier->inv[HANDPOS]) ),
BaseAPsToShootOrStab( bFullAPs, bAimSkill, &(pSoldier->inv[SECONDHANDPOS]) ) );
}
else
{
bAPCost += BaseAPsToShootOrStab( bFullAPs, bAimSkill, &(pSoldier->inv[HANDPOS]) );
}
// the minimum AP cost of ANY shot can NEVER be more than merc's maximum APs!
if ( bAPCost > bFullAPs )
bAPCost = bFullAPs;
// this SHOULD be impossible, but nevertheless...
if ( bAPCost < 1 )
bAPCost = 1;
if ( Item[pSoldier->inv[HANDPOS].usItem].rocketlauncher )
{
bAPCost += GetAPsToChangeStance( pSoldier, ANIM_STAND );
}
return ( bAPCost );
}
UINT8 MinAPsToPunch(SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubAddTurningCost)
{
UINT8 bAPCost = 0;
UINT16 usTargID;
UINT8 ubDirection;
// bAimSkill = ( pSoldier->bDexterity + pSoldier->bAgility) / 2;
if ( sGridNo != NOWHERE )
{
usTargID = WhoIsThere2( sGridNo, pSoldier->bTargetLevel );
// Given a gridno here, check if we are on a guy - if so - get his gridno
if ( usTargID != NOBODY )
{
sGridNo = MercPtrs[ usTargID ]->sGridNo;
// Check if target is prone, if so, calc cost...
if ( gAnimControl[ MercPtrs[ usTargID ]->usAnimState ].ubEndHeight == ANIM_PRONE )
{
bAPCost += GetAPsToChangeStance( pSoldier, ANIM_CROUCH );
}
else
{
if ( pSoldier->sGridNo == sGridNo )
{
bAPCost += GetAPsToChangeStance( pSoldier, ANIM_STAND );
}
}
}
if (ubAddTurningCost)
{
if ( pSoldier->sGridNo == sGridNo )
{
// ATE: Use standing turn cost....
ubDirection = (UINT8)GetDirectionFromGridNo( sGridNo, pSoldier );
// Is it the same as he's facing?
if ( ubDirection != pSoldier->bDirection )
{
bAPCost += AP_LOOK_STANDING;
}
}
}
}
bAPCost += 4;
return ( bAPCost );
}
INT8 MinPtsToMove(SOLDIERTYPE *pSoldier)
{
// look around all 8 directions and return lowest terrain cost
INT32 cnt;
INT16 sLowest=127;
INT16 sGridno,sCost;
if ( TANK( pSoldier ) )
{
return( (INT8)sLowest);
}
for (cnt=0; cnt <= 7; cnt++)
{
sGridno = NewGridNo(pSoldier->sGridNo,DirectionInc((INT16) cnt));
if (sGridno != pSoldier->sGridNo)
{
if ( (sCost=ActionPointCost( pSoldier, sGridno, (UINT8)cnt , pSoldier->usUIMovementMode ) ) < sLowest )
{
sLowest = sCost;
}
}
}
return( (INT8)sLowest);
}
INT8 PtsToMoveDirection(SOLDIERTYPE *pSoldier, INT8 bDirection )
{
INT16 sGridno,sCost;
INT8 bOverTerrainType;
UINT16 usMoveModeToUse;
sGridno = NewGridNo( pSoldier->sGridNo, DirectionInc((INT16) bDirection ) );
usMoveModeToUse = pSoldier->usUIMovementMode;
// ATE: Check if the new place is watter and we were tying to run....
bOverTerrainType = GetTerrainType( sGridno );
if ( bOverTerrainType == MED_WATER || bOverTerrainType == DEEP_WATER || bOverTerrainType == LOW_WATER )
{
usMoveModeToUse = WALKING;
}
sCost = ActionPointCost( pSoldier, sGridno, bDirection , usMoveModeToUse );
if ( gubWorldMovementCosts[ sGridno ][ bDirection ][ pSoldier->bLevel ] != TRAVELCOST_FENCE )
{
if ( usMoveModeToUse == RUNNING && pSoldier->usAnimState != RUNNING )
{
sCost += AP_START_RUN_COST;
}
}
return( (INT8)sCost );
}
INT8 MinAPsToStartMovement( SOLDIERTYPE * pSoldier, UINT16 usMovementMode )
{
INT8 bAPs = 0;
switch( usMovementMode )
{
case RUNNING:
case WALKING:
if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE)
{
bAPs += AP_CROUCH + AP_PRONE;
}
else if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH)
{
bAPs += AP_CROUCH;
}
break;
case SWATTING:
if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE)
{
bAPs += AP_PRONE;
}
else if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND)
{
bAPs += AP_CROUCH;
}
break;
case CRAWLING:
if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND)
{
bAPs += AP_CROUCH + AP_PRONE;
}
else if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH)
{
bAPs += AP_CROUCH;
}
break;
default:
break;
}
if (usMovementMode == RUNNING && pSoldier->usAnimState != RUNNING )
{
bAPs += AP_START_RUN_COST;
}
return( bAPs );
}
BOOLEAN EnoughAmmo( SOLDIERTYPE *pSoldier, BOOLEAN fDisplay, INT8 bInvPos )
{
if ( pSoldier->inv[ bInvPos ].usItem != NOTHING )
{
if ( pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO )
{
UINT16 glItem = GetAttachedGrenadeLauncher( &(pSoldier->inv[bInvPos]));
INT8 bAttachSlot = FindLaunchableAttachment( &(pSoldier->inv[bInvPos]), glItem);
if ( bAttachSlot != ITEM_NOT_FOUND )
{
return TRUE;
}
else
return FALSE;
}
else
{
if ( Item[pSoldier->inv[ bInvPos ].usItem].singleshotrocketlauncher )
{
// hack... they turn empty afterwards anyways
return( TRUE );
}
if (Item[ pSoldier->inv[ bInvPos ].usItem ].usItemClass == IC_LAUNCHER || Item[pSoldier->inv[ bInvPos ].usItem].cannon )
{
if ( FindAttachmentByClass( &(pSoldier->inv[ bInvPos ]), IC_GRENADE ) != ITEM_NOT_FOUND )
{
return( TRUE );
}
// ATE: Did an else if here...
if ( FindAttachmentByClass( &(pSoldier->inv[ bInvPos ]), IC_BOMB ) != ITEM_NOT_FOUND )
{
return( TRUE );
}
if ( fDisplay )
{
TacticalCharacterDialogue( pSoldier, QUOTE_OUT_OF_AMMO );
}
return( FALSE );
}
else if (Item[ pSoldier->inv[ bInvPos ].usItem ].usItemClass == IC_GUN)
{
if ( pSoldier->inv[ bInvPos ].ubGunShotsLeft == 0 )
{
if ( fDisplay )
{
TacticalCharacterDialogue( pSoldier, QUOTE_OUT_OF_AMMO );
}
return( FALSE );
}
// manual recharge
if( pSoldier->bTeam == OUR_TEAM )
{
if ( !( pSoldier->inv[ bInvPos ].ubGunState & GS_CARTRIDGE_IN_CHAMBER ) )
{
return( FALSE );
}
}
//
}
}
return( TRUE );
}
return( FALSE );
}
void DeductAmmo( SOLDIERTYPE *pSoldier, INT8 bInvPos )
{
OBJECTTYPE * pObj;
// tanks never run out of MG ammo!
// unlimited cannon ammo is handled in AI
if ( TANK( pSoldier ) && !Item[pSoldier->inv[bInvPos].usItem].cannon )
{
return;
}
pObj = &(pSoldier->inv[ bInvPos ]);
if ( pObj->usItem != NOTHING )
{
if ( Item[pObj->usItem].cannon )
{
}
else if ( Item[ pObj->usItem ].usItemClass == IC_GUN && !Item[pObj->usItem].cannon && pSoldier->bWeaponMode != WM_ATTACHED_GL && pSoldier->bWeaponMode != WM_ATTACHED_GL_BURST && pSoldier->bWeaponMode != WM_ATTACHED_GL_AUTO )
{
if ( pSoldier->usAttackingWeapon == pObj->usItem)
{
// OK, let's see, don't overrun...
if ( pObj->ubGunShotsLeft != 0 )
{
pObj->ubGunShotsLeft--;
//Pulmu: Update weight after firing gun to account for bullets fired
if( gGameExternalOptions.fAmmoDynamicWeight == TRUE && pObj->ubGunShotsLeft > 0)
{
pSoldier->inv[HANDPOS].ubWeight = CalculateObjectWeight( &(pSoldier->inv[HANDPOS]));
}
}
}
else
{
// firing an attachment?
}
}
else if ( Item[ pObj->usItem ].usItemClass == IC_LAUNCHER || Item[pObj->usItem].cannon || pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO )
{
INT8 bAttachPos;
bAttachPos = FindAttachmentByClass( pObj, IC_GRENADE );
if (bAttachPos == ITEM_NOT_FOUND )
{
bAttachPos = FindAttachmentByClass( pObj, IC_BOMB );
}
if (bAttachPos != ITEM_NOT_FOUND)
{
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DeductAmmo: deducting for GL: found attachment, about to decide whether to remove it"));
if ( Item[ pObj->usItem ].usItemClass == IC_LAUNCHER && GetMagSize(pObj) > 1 )
{
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DeductAmmo: deducting for milkor"));
pObj->bAttachStatus[bAttachPos] = pObj->bAttachStatus[bAttachPos] - (INT8) ceil((double)( 100 / GetMagSize(pObj) )) ;
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DeductAmmo: deducting for milkor: resulting status: %d, remove? = %d",pObj->bAttachStatus[bAttachPos],(pObj->bAttachStatus[bAttachPos] <= (INT8) ( 100 / Weapon[GetAttachedGrenadeLauncher(pObj)].ubMagSize ))));
if ( pObj->bAttachStatus[bAttachPos] <= (INT8) ceil((double)( 100 / GetMagSize(pObj) ) ))
{
RemoveAttachment( pObj, bAttachPos, NULL );
}
}
else if ( (pSoldier->bWeaponMode == WM_ATTACHED_GL || pSoldier->bWeaponMode == WM_ATTACHED_GL_BURST || pSoldier->bWeaponMode == WM_ATTACHED_GL_AUTO ) && Weapon[GetAttachedGrenadeLauncher(pObj)].ubMagSize > 1 )
{
pObj->bAttachStatus[bAttachPos] = pObj->bAttachStatus[bAttachPos] - (INT8) ceil((double)( 100 / Weapon[GetAttachedGrenadeLauncher(pObj)].ubMagSize )) ;
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DeductAmmo: deducting for OICW GL: resulting status: %d, remove? = %d",pObj->bAttachStatus[bAttachPos],(pObj->bAttachStatus[bAttachPos] <= (INT8) ( 100 / Weapon[GetAttachedGrenadeLauncher(pObj)].ubMagSize ))));
if ( pObj->bAttachStatus[bAttachPos] <= (INT8) ceil((double)( 100 / Weapon[GetAttachedGrenadeLauncher(pObj)].ubMagSize ) ))
{
RemoveAttachment( pObj, bAttachPos, NULL );
}
}
else
{
DebugMsg(TOPIC_JA2,DBG_LEVEL_3,String("DeductAmmo: deducting for GL: removing attachment"));
RemoveAttachment( pObj, bAttachPos, NULL );
}
}
}
// Dirty Bars
DirtyMercPanelInterface( pSoldier, DIRTYLEVEL1 );
}
}
UINT16 GetAPsToPickupItem( SOLDIERTYPE *pSoldier, UINT16 usMapPos )
{
ITEM_POOL *pItemPool;
UINT16 sAPCost = 0;
INT16 sActionGridNo;
// Check if we are over an item pool
if ( GetItemPool( usMapPos, &pItemPool, pSoldier->bLevel ) )
{
// If we are in the same tile, just return pickup cost
sActionGridNo = AdjustGridNoForItemPlacement( pSoldier, usMapPos );
if ( pSoldier->sGridNo != sActionGridNo )
{
sAPCost = PlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, NO_PLOT, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints );
// If point cost is zero, return 0
if ( sAPCost != 0 )
{
// ADD APS TO PICKUP
sAPCost += AP_PICKUP_ITEM;
}
}
else
{
sAPCost += AP_PICKUP_ITEM;
}
}
return( sAPCost );
}
UINT16 GetAPsToGiveItem( SOLDIERTYPE *pSoldier, UINT16 usMapPos )
{
UINT16 sAPCost = 0;
sAPCost = PlotPath( pSoldier, usMapPos, NO_COPYROUTE, NO_PLOT, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints );
// If point cost is zero, return 0
if ( sAPCost != 0 || pSoldier->sGridNo == usMapPos )
{
// ADD APS TO PICKUP
sAPCost += AP_GIVE_ITEM;
}
return( sAPCost );
}
INT8 GetAPsToReloadGunWithAmmo( OBJECTTYPE * pGun, OBJECTTYPE * pAmmo )
{
if (Item[ pGun->usItem ].usItemClass == IC_LAUNCHER)
{
// always standard AP cost
//return( AP_RELOAD_GUN );
return GetAPsToReload(pGun);
}
if ( GetMagSize(pGun) == Magazine[Item[pAmmo->usItem].ubClassIndex].ubMagSize )
{
// normal situation
// return( AP_RELOAD_GUN );
return GetAPsToReload(pGun) ;
}
else
{
// trying to reload with wrong size of magazine
return (GetAPsToReload(pGun) * 2);
// return( AP_RELOAD_GUN + AP_RELOAD_GUN );
}
}
INT8 GetAPsToAutoReload( SOLDIERTYPE * pSoldier )
{
OBJECTTYPE * pObj;
INT8 bSlot, bSlot2, bExcludeSlot;
INT8 bAPCost = 0, bAPCost2 = 0;;
CHECKF( pSoldier );
pObj = &(pSoldier->inv[HANDPOS]);
// manual recharge
if (pObj->ubGunShotsLeft && !(pObj->ubGunState & GS_CARTRIDGE_IN_CHAMBER) )
{
return Weapon[Item[(pObj)->usItem].ubClassIndex].APsToReloadManually;
}
//
if (Item[pObj->usItem].usItemClass == IC_GUN || Item[pObj->usItem].usItemClass == IC_LAUNCHER)
{
bSlot = FindAmmoToReload( pSoldier, HANDPOS, NO_SLOT );
if (bSlot != NO_SLOT)
{
// we would reload using this ammo!
bAPCost += GetAPsToReloadGunWithAmmo( pObj, &(pSoldier->inv[bSlot] ) );
}
if ( IsValidSecondHandShotForReloadingPurposes( pSoldier ) )
{
pObj = &(pSoldier->inv[SECONDHANDPOS]);
bExcludeSlot = NO_SLOT;
bSlot2 = NO_SLOT;
// if the ammo for the first gun is the same we have to do special checks
if ( ValidAmmoType( pObj->usItem, pSoldier->inv[ bSlot ].usItem ) )
{
if ( pSoldier->inv[ bSlot ].ubNumberOfObjects == 1 )
{
// we must not consider this slot for reloading!
bExcludeSlot = bSlot;
}
else
{
// we can reload the 2nd gun from the same pocket!
bSlot2 = bSlot;
}
}
if (bSlot2 == NO_SLOT)
{
bSlot2 = FindAmmoToReload( pSoldier, SECONDHANDPOS, bExcludeSlot );
}
if (bSlot2 != NO_SLOT)
{
// we would reload using this ammo!
bAPCost2 = GetAPsToReloadGunWithAmmo( pObj, &(pSoldier->inv[bSlot2] ) );
if ( EnoughPoints( pSoldier, (INT16) (bAPCost + bAPCost2), 0, FALSE ) )
{
// we can afford to reload both guns; otherwise display just for 1 gun
bAPCost += bAPCost2;
}
}
}
}
return( bAPCost );
}
UINT16 GetAPsToReloadRobot( SOLDIERTYPE *pSoldier, SOLDIERTYPE *pRobot )
{
UINT16 sAPCost = 0;
INT16 sActionGridNo;
UINT8 ubDirection;
INT16 sAdjustedGridNo;
sActionGridNo = FindAdjacentGridEx( pSoldier, pRobot->sGridNo, &ubDirection, &sAdjustedGridNo, TRUE, FALSE );
sAPCost = PlotPath( pSoldier, sActionGridNo, NO_COPYROUTE, NO_PLOT, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints );
// If point cost is zero, return 0
if ( sAPCost != 0 || sActionGridNo == pSoldier->sGridNo )
{
// ADD APS TO RELOAD
sAPCost += 4;
}
return( sAPCost );
}
UINT16 GetAPsToChangeStance( SOLDIERTYPE *pSoldier, INT8 bDesiredHeight )
{
UINT16 sAPCost = 0;
INT8 bCurrentHeight;
bCurrentHeight = gAnimControl[ pSoldier->usAnimState ].ubEndHeight;
if ( bCurrentHeight == bDesiredHeight )
{
sAPCost = 0;
}
if ( bCurrentHeight == ANIM_STAND && bDesiredHeight == ANIM_PRONE )
{
sAPCost = AP_CROUCH + AP_PRONE;
}
if ( bCurrentHeight == ANIM_STAND && bDesiredHeight == ANIM_CROUCH )
{
sAPCost = AP_CROUCH;
}
if ( bCurrentHeight == ANIM_CROUCH && bDesiredHeight == ANIM_PRONE )
{
sAPCost = AP_PRONE;
}
if ( bCurrentHeight == ANIM_CROUCH && bDesiredHeight == ANIM_STAND )
{
sAPCost = AP_CROUCH;
}
if ( bCurrentHeight == ANIM_PRONE && bDesiredHeight == ANIM_STAND )
{
sAPCost = AP_PRONE + AP_CROUCH;
}
if ( bCurrentHeight == ANIM_PRONE && bDesiredHeight == ANIM_CROUCH )
{
sAPCost = AP_PRONE;
}
return( sAPCost );
}
UINT16 GetBPsToChangeStance( SOLDIERTYPE *pSoldier, INT8 bDesiredHeight )
{
UINT16 sBPCost = 0;
INT8 bCurrentHeight;
bCurrentHeight = gAnimControl[ pSoldier->usAnimState ].ubEndHeight;
if ( bCurrentHeight == bDesiredHeight )
{
sBPCost = 0;
}
if ( bCurrentHeight == ANIM_STAND && bDesiredHeight == ANIM_PRONE )
{
sBPCost = BP_CROUCH + BP_PRONE;
}
if ( bCurrentHeight == ANIM_STAND && bDesiredHeight == ANIM_CROUCH )
{
sBPCost = BP_CROUCH;
}
if ( bCurrentHeight == ANIM_CROUCH && bDesiredHeight == ANIM_PRONE )
{
sBPCost = BP_PRONE;
}
if ( bCurrentHeight == ANIM_CROUCH && bDesiredHeight == ANIM_STAND )
{
sBPCost = BP_CROUCH;
}
if ( bCurrentHeight == ANIM_PRONE && bDesiredHeight == ANIM_STAND )
{
sBPCost = BP_PRONE + BP_CROUCH;
}
if ( bCurrentHeight == ANIM_PRONE && bDesiredHeight == ANIM_CROUCH )
{
sBPCost = BP_PRONE;
}
return( sBPCost );
}
UINT16 GetAPsToLook( SOLDIERTYPE *pSoldier )
{
// Set # of APs
switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
{
// Now change to appropriate animation
case ANIM_STAND:
return( AP_LOOK_STANDING );
break;
case ANIM_CROUCH:
return( AP_LOOK_CROUCHED );
break;
case ANIM_PRONE:
// AP_PRONE is the AP cost to go to or from the prone stance. To turn while prone, your merc has to get up to
// crouched, turn, and then go back down. Hence you go up (AP_PRONE), turn (AP_LOOK_PRONE) and down (AP_PRONE).
return( AP_LOOK_PRONE + AP_PRONE + AP_PRONE );
break;
// no other values should be possible
default:
Assert( FALSE );
return(0);
break;
}
}
BOOLEAN CheckForMercContMove( SOLDIERTYPE *pSoldier )
{
INT16 sAPCost;
INT16 sGridNo;
if ( !( gTacticalStatus.uiFlags & INCOMBAT ) )
{
return( FALSE );
}
if ( gpItemPointer != NULL )
{
return( FALSE );
}
if( pSoldier->bLife >= OKLIFE )
{
if( pSoldier->sGridNo != pSoldier->sFinalDestination || pSoldier->bGoodContPath )
{
// OK< check if we are the selected guy!
if ( pSoldier->ubID == gusSelectedSoldier )
{
if (SoldierOnScreen( pSoldier->ubID ) )
{
sGridNo = pSoldier->sFinalDestination;
if ( pSoldier->bGoodContPath )
{
sGridNo = pSoldier->sContPathLocation;
}
// Do a check if we can afford move here!
// 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 , FALSE ) )
{
return( TRUE );
}
}
else
{
return( FALSE );
}
}
}
}
}
return( FALSE );
}
INT16 GetAPsToReadyWeapon( SOLDIERTYPE *pSoldier, UINT16 usAnimState )
{
UINT16 usItem;
usItem = pSoldier->inv[ HANDPOS ].usItem;
// If this is a dwel pistol anim
// ATE: What was I thinking, hooking into animations like this....
//if ( usAnimState == READY_DUAL_STAND || usAnimState == READY_DUAL_CROUCH )
//{
//return( AP_READY_DUAL );
//}
if ( IsValidSecondHandShot( pSoldier ) )
{
//Madd: return the greater of the two weapons + 1:
UINT8 rt1, rt2;
rt1 = Weapon[usItem].ubReadyTime * ( 100 - GetPercentReadyTimeAPReduction(&pSoldier->inv[ HANDPOS ]) ) / 100;
rt2 = Weapon[pSoldier->inv[SECONDHANDPOS].usItem].ubReadyTime * ( 100 - GetPercentReadyTimeAPReduction(&pSoldier->inv[ SECONDHANDPOS ]) ) / 100;
return( max(rt1,rt2) + AP_READY_DUAL );
}
// OK, now check type of weapon
if ( usItem == NOTHING )
{
return( 0 );
}
else
{
// CHECK FOR RIFLE
if ( Item[ usItem ].usItemClass == IC_GUN )
{
return ( Weapon[ usItem ].ubReadyTime
* ( 100 - GetPercentReadyTimeAPReduction(&pSoldier->inv[ HANDPOS ]) ) ) / 100;
}
}
return( 0 );
}
INT8 GetAPsToClimbRoof( SOLDIERTYPE *pSoldier, BOOLEAN fClimbDown )
{
if ( !fClimbDown )
{
// OK, add aps to goto stand stance...
return( (INT8)( AP_CLIMBROOF + GetAPsToChangeStance( pSoldier, ANIM_STAND ) ) );
}
else
{
// Add aps to goto crouch
return( (INT8)( AP_CLIMBOFFROOF + GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) ) );
}
}
INT16 GetBPsToClimbRoof( SOLDIERTYPE *pSoldier, BOOLEAN fClimbDown )
{
if ( !fClimbDown )
{
return( BP_CLIMBROOF );
}
else
{
return( BP_CLIMBOFFROOF );
}
}
INT8 GetAPsToCutFence( SOLDIERTYPE *pSoldier )
{
// OK, it's normally just cost, but add some if different stance...
return( GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) + AP_USEWIRECUTTERS );
}
INT8 GetAPsToBeginFirstAid( SOLDIERTYPE *pSoldier )
{
// OK, it's normally just cost, but add some if different stance...
return( GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) + AP_START_FIRST_AID );
}
INT8 GetAPsToBeginRepair( SOLDIERTYPE *pSoldier )
{
// OK, it's normally just cost, but add some if different stance...
return( GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) + AP_START_REPAIR );
}
INT8 GetAPsToRefuelVehicle( SOLDIERTYPE *pSoldier )
{
// OK, it's normally just cost, but add some if different stance...
return( GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) + AP_REFUEL_VEHICLE );
}
#define TOSSES_PER_10TURNS 18 // max # of grenades tossable in 10 turns
#define AP_MIN_AIM_ATTACK 0 // minimum permitted extra aiming
#define AP_MAX_AIM_ATTACK 4 // maximum permitted extra aiming
INT16 MinAPsToThrow( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubAddTurningCost )
{
INT32 iTop, iBottom;
INT32 iFullAPs;
INT32 iAPCost = AP_MIN_AIM_ATTACK;
UINT16 usInHand;
UINT16 usTargID;
UINT32 uiMercFlags;
UINT8 ubDirection;
// make sure the guy's actually got a throwable item in his hand!
usInHand = pSoldier->inv[ HANDPOS ].usItem;
if ( ( !(Item[ usInHand ].usItemClass & IC_GRENADE) ) )
{
//AXP 25.03.2007: See if we are about to throw grenade (grenade was not in hand, but in temp object)
if ( pSoldier->pTempObject != NULL && pSoldier->pThrowParams != NULL &&
pSoldier->pThrowParams->ubActionCode == THROW_ARM_ITEM && (Item[ pSoldier->pTempObject->usItem ].usItemClass & IC_GRENADE) )
{
//nothing here
}
else
{
#ifdef JA2TESTVERSION
ScreenMsg( MSG_FONT_YELLOW, MSG_DEBUG, L"MinAPsToThrow - Called when in-hand item is %s", usInHand );
#endif
return(0);
}
}
if ( sGridNo != NOWHERE )
{
// Given a gridno here, check if we are on a guy - if so - get his gridno
if ( FindSoldier( sGridNo, &usTargID, &uiMercFlags, FIND_SOLDIER_GRIDNO ) )
{
sGridNo = MercPtrs[ usTargID ]->sGridNo;
}
// OK, get a direction and see if we need to turn...
if (ubAddTurningCost)
{
ubDirection = (UINT8)GetDirectionFromGridNo( sGridNo, pSoldier );
// Is it the same as he's facing?
if ( ubDirection != pSoldier->bDirection )
{
//Lalien: disabled it again
//AXP 25.03.2007: Reenabled look cost
//iAPCost += GetAPsToLook( pSoldier );
}
}
}
else
{
// Assume we need to add cost!
//iAPCost += GetAPsToLook( pSoldier );
}
// if attacking a new target (or if the specific target is uncertain)
//AXP 25.03.2007: Aim-at-same-tile AP cost/bonus doesn't make any sense for thrown objects
//if ( ( sGridNo != pSoldier->sLastTarget ) )
//{
// iAPCost += AP_CHANGE_TARGET;
//}
iAPCost += GetAPsToChangeStance( pSoldier, ANIM_STAND );
// Calculate default top & bottom of the magic "aiming" formula)
// get this man's maximum possible action points (ignoring carryovers)
iFullAPs = CalcActionPoints( pSoldier );
// the 2 times is here only to around rounding off using integer math later
iTop = 2 * iFullAPs;
// if it's anything but a mortar
// if ( usInHand != MORTAR)
// tosses per turn is for max dexterity, drops down to 1/2 at dexterity = 0
// bottom = (TOSSES_PER_10TURNS * (50 + (ptr->dexterity / 2)) / 10);
//else
iBottom = ( TOSSES_PER_10TURNS * (50 + ( pSoldier->bDexterity / 2 ) ) / 10 );
// add minimum aiming time to the overall minimum AP_cost
// This here ROUNDS UP fractions of 0.5 or higher using integer math
// This works because 'top' is 2x what it really should be throughout
iAPCost += ( ( ( 100 * iTop ) / iBottom) + 1) / 2;
// the minimum AP cost of ANY throw can NEVER be more than merc has APs!
if ( iAPCost > iFullAPs )
iAPCost = iFullAPs;
// this SHOULD be impossible, but nevertheless...
if ( iAPCost < 1 )
iAPCost = 1;
return ( (INT16)iAPCost );
}
UINT16 GetAPsToDropBomb( SOLDIERTYPE *pSoldier )
{
return( AP_DROP_BOMB );
}
UINT16 GetTotalAPsToDropBomb( SOLDIERTYPE *pSoldier, INT16 sGridNo )
{
INT16 sAPs = 0;
sAPs = PlotPath( pSoldier, sGridNo, NO_COPYROUTE, NO_PLOT, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints );
if ( sAPs > 0 )
{
sAPs += AP_DROP_BOMB;
}
return( sAPs );
}
UINT16 GetAPsToUseRemote( SOLDIERTYPE *pSoldier )
{
return( AP_USE_REMOTE );
}
INT8 GetAPsToStealItem( SOLDIERTYPE *pSoldier, INT16 usMapPos )
{
UINT16 sAPCost = 0;
if (usMapPos != -1)
{
sAPCost = PlotPath( pSoldier, usMapPos, NO_COPYROUTE, NO_PLOT, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints );
}
// ADD APS TO PICKUP
sAPCost += AP_STEAL_ITEM;
// CJC August 13 2002: added cost to stand into equation
if (!(PTR_STANDING))
{
sAPCost += GetAPsToChangeStance( pSoldier, ANIM_STAND );
}
return( (INT8)sAPCost );
}
INT8 GetBPsToStealItem( SOLDIERTYPE *pSoldier )
{
return( BP_STEAL_ITEM );
}
INT8 GetAPsToUseJar( SOLDIERTYPE *pSoldier, INT16 usMapPos )
{
UINT16 sAPCost = 0;
sAPCost = PlotPath( pSoldier, usMapPos, NO_COPYROUTE, NO_PLOT, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints );
// If point cost is zero, return 0
if ( sAPCost != 0 )
{
// ADD APS TO PICKUP
sAPCost += AP_TAKE_BLOOD;
}
return( (INT8)sAPCost );
}
INT8 GetAPsToUseCan( SOLDIERTYPE *pSoldier, INT16 usMapPos )
{
UINT16 sAPCost = 0;
sAPCost = PlotPath( pSoldier, usMapPos, NO_COPYROUTE, NO_PLOT, TEMPORARY, (UINT16)pSoldier->usUIMovementMode, NOT_STEALTH, FORWARD, pSoldier->bActionPoints );
// If point cost is zero, return 0
if ( sAPCost != 0 )
{
// ADD APS TO PICKUP
sAPCost += AP_ATTACH_CAN;
}
return( (INT8)sAPCost );
}
INT8 GetAPsToJumpOver( SOLDIERTYPE *pSoldier )
{
return( GetAPsToChangeStance( pSoldier, ANIM_STAND ) + AP_JUMP_OVER );
}