mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
Added missing #includes Added LUA scripting and console Changed the way tracers are visualized, so they work more like real tracers instead of a light fountain Fixed signedness of many grid variables used in GetMouseMapPos calls Fixed enemy weapon choosing: Sometimes a mortar is chosen but later rejected, but the grenade class was not reset. Caused assertion failure Added checks so enemies don't try to chuck RPG grenades with their hands Now possible to shoot a someone in the head if he's in water Fixed InitSightArrays to also clear soldier interrupt duel points. This was causing an assertion failure elsewhere in the code because the interrupt list still had soldiers on it sometimes when this function was called. Soldiers are much less willing to forfeit their turn over an attempt to use more APs than they have Removed early setting of muzzle flash. This would allow enemies to get an interrupt before you even fired. Fixed item dropping by AI. If AI tried to drop something while standing it would cause deadlock Change to greatly speed up closing the sector inventory window in an unloaded sector Added conditional compile flag to always give robot weapon ready advantage, even for 360 degree sighting Check builddefines.h for the conditional flags. Of greatest interest might be LUA_CONSOLE. This will cause the game to bring up a command console when run. However this console is severely lacking in many areas. If anybody knows of an open-source terminal/console that could be used instead, it would be appreciated. The existing console does bad things when you try to close it, and since it counts as a separate app, it pauses the game while it has focus. LUA scripting is very limited, basically just proof of concept. There is one global variable, the Soldiers array. An array index gives you the soldier in that slot in the currently loaded sector. The soldier has a few things that can be accessed: name (short name); fullname (long name); grid (current grid #, can be changed); walkto(grid) (function to walk to another grid); runto(grid) (function to run to another grid) git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@924 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2328 lines
64 KiB
C++
2328 lines
64 KiB
C++
#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"
|
|
#include "Soldier macros.h"
|
|
#include "Random.h"
|
|
#include "Campaign.h"
|
|
#include "drugs and alcohol.h"
|
|
#include "GameSettings.h"
|
|
#include "worldman.h"
|
|
#include "math.h"
|
|
#include "Interface Items.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;
|
|
}
|
|
|
|
switch ( EFFORT( gAnimControl[ usAnimState ].uiFlags) ) {
|
|
case 0: // No effort
|
|
sBreathPerAP = BP_PER_AP_NO_EFFORT;
|
|
fAnimTypeFound = TRUE;
|
|
break;
|
|
case 1: // Minimal effort
|
|
sBreathPerAP = BP_PER_AP_MIN_EFFORT;
|
|
fAnimTypeFound = TRUE;
|
|
break;
|
|
case 2: // Light effort
|
|
sBreathPerAP = BP_PER_AP_LT_EFFORT;
|
|
fAnimTypeFound = TRUE;
|
|
break;
|
|
case 3: // Moderate effort
|
|
sBreathPerAP = BP_PER_AP_MOD_EFFORT;
|
|
fAnimTypeFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
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, UINT8 ubForceRaiseGunCost)
|
|
{
|
|
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, ubForceRaiseGunCost );
|
|
}
|
|
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, UINT8 ubForceRaiseGunCost)
|
|
{
|
|
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 (ubForceRaiseGunCost)
|
|
{
|
|
fAddingRaiseGunCost = TRUE;
|
|
}
|
|
|
|
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 (ubForceRaiseGunCost || (( 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 );
|
|
}
|
|
|
|
//<SB> manual recharge
|
|
if( pSoldier->bTeam == OUR_TEAM )
|
|
{
|
|
if ( !( pSoldier->inv[ bInvPos ].ubGunState & GS_CARTRIDGE_IN_CHAMBER ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
//</SB>
|
|
}
|
|
}
|
|
|
|
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]);
|
|
|
|
//<SB> manual recharge
|
|
if (pObj->ubGunShotsLeft && !(pObj->ubGunState & GS_CARTRIDGE_IN_CHAMBER) )
|
|
{
|
|
return Weapon[Item[(pObj)->usItem].ubClassIndex].APsToReloadManually;
|
|
}
|
|
//</SB>
|
|
|
|
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 %d", 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 );
|
|
}
|