1 #include "Handle_Items.h"
2 #include "Soldier_Find.h"
3 #include "TileDef.h"
4 #include "WorldDef.h"
5 #include "Points.h"
6 #include "Overhead.h"
7 #include "Font_Control.h"
8 #include "Interface.h"
9 #include "Isometric_Utils.h"
10 #include "PathAI.h"
11 #include "Message.h"
12 #include "Animation_Control.h"
13 #include "Weapons.h"
14 #include "Structure_Wrap.h"
15 #include "Dialogue_Control.h"
16 #include "Items.h"
17 #include "RT_Time_Defines.h"
18 #include "AI.h"
19 #include "Handle_UI.h"
20 #include "Text.h"
21 #include "SkillCheck.h"
22 #include "WCheck.h"
23 #include "Soldier_Profile.h"
24 #include "Soldier_Macros.h"
25 #include "Drugs_And_Alcohol.h"
26 #include "WorldMan.h"
27 #include "Interface_Items.h"
28 #include "Debug.h"
29 
30 #include "ContentManager.h"
31 #include "GameInstance.h"
32 #include "MagazineModel.h"
33 #include "WeaponModels.h"
34 
35 #include "Logger.h"
36 
TerrainActionPoints(const SOLDIERTYPE * const pSoldier,const INT16 sGridno,const INT8 bDir,const INT8 bLevel)37 INT16 TerrainActionPoints(const SOLDIERTYPE* const pSoldier, const INT16 sGridno, const INT8 bDir, const INT8 bLevel)
38 {
39 	INT16 sAPCost = 0;
40 	INT16 sSwitchValue;
41 
42 	if ( pSoldier->bStealthMode )
43 		sAPCost += AP_STEALTH_MODIFIER;
44 
45 	if ( pSoldier->bReverse || gUIUseReverse )
46 		sAPCost += AP_REVERSE_MODIFIER;
47 
48 	sSwitchValue = gubWorldMovementCosts[sGridno][bDir][ bLevel ];
49 
50 	// Check reality vs what the player knows....
51 	if ( sSwitchValue == TRAVELCOST_NOT_STANDING )
52 	{
53 		// use the cost of the terrain!
54 		sSwitchValue = gTileTypeMovementCost[ gpWorldLevelData[ sGridno ].ubTerrainID ];
55 	}
56 	else if (IS_TRAVELCOST_DOOR( sSwitchValue ))
57 	{
58 		sSwitchValue = DoorTravelCost(pSoldier, sGridno, (UINT8)sSwitchValue, pSoldier->bTeam == OUR_TEAM, NULL);
59 	}
60 
61 	if (sSwitchValue >= TRAVELCOST_BLOCKED && sSwitchValue != TRAVELCOST_DOOR )
62 	{
63 		return(100); // Cost too much to be considered!
64 	}
65 
66 	switch( sSwitchValue )
67 	{
68 		case TRAVELCOST_DIRTROAD:
69 		case TRAVELCOST_FLAT:
70 			sAPCost += AP_MOVEMENT_FLAT;
71 			break;
72 		//case TRAVELCOST_BUMPY:
73 		case TRAVELCOST_GRASS:
74 			sAPCost += AP_MOVEMENT_GRASS;
75 			break;
76 		case TRAVELCOST_THICK:
77 			sAPCost += AP_MOVEMENT_BUSH;
78 			break;
79 		case TRAVELCOST_DEBRIS:
80 			sAPCost += AP_MOVEMENT_RUBBLE;
81 			break;
82 		case TRAVELCOST_SHORE:
83 			sAPCost += AP_MOVEMENT_SHORE; // wading shallow water
84 			break;
85 		case TRAVELCOST_KNEEDEEP:
86 			sAPCost += AP_MOVEMENT_LAKE; // wading waist/chest deep - very slow
87 			break;
88 
89 		case TRAVELCOST_DEEPWATER:
90 			sAPCost += AP_MOVEMENT_OCEAN; // can swim, so it's faster than wading
91 			break;
92 		case TRAVELCOST_DOOR:
93 			sAPCost += AP_MOVEMENT_FLAT;
94 			break;
95 
96 		// cost for jumping a fence REPLACES all other AP costs!
97 		case TRAVELCOST_FENCE:
98 			return( AP_JUMPFENCE );
99 
100 		case TRAVELCOST_NONE:
101 			return( 0 );
102 
103 		default:
104 			SLOGD(
105 				"Calc AP: Unrecongnized MP type %d in %d, direction %d",
106 				sSwitchValue, sGridno, bDir);
107 			break;
108 	}
109 
110 	if (bDir & 1)
111 	{
112 		sAPCost = (sAPCost * 14) / 10;
113 	}
114 
115 
116 	return(sAPCost);
117 
118 }
119 
120 
BreathPointAdjustmentForCarriedWeight(SOLDIERTYPE * pSoldier)121 static INT16 BreathPointAdjustmentForCarriedWeight(SOLDIERTYPE* pSoldier)
122 {
123 	UINT32 uiCarriedPercent;
124 	UINT32 uiPercentCost;
125 
126 	uiCarriedPercent = CalculateCarriedWeight( pSoldier );
127 	if (uiCarriedPercent < 101)
128 	{
129 		// normal BP costs
130 		uiPercentCost = 100;
131 	}
132 	else
133 	{
134 		if (uiCarriedPercent < 151)
135 		{
136 			// between 101 and 150% of max carried weight, extra BP cost
137 			// of 1% per % above 100... so at 150%, we pay 150%
138 			uiPercentCost = 100 + (uiCarriedPercent - 100) * 3;
139 		}
140 		else if (uiCarriedPercent < 201)
141 		{
142 			// between 151 and 200% of max carried weight, extra BP cost
143 			// of 2% per % above 150... so at 200%, we pay 250%
144 			uiPercentCost = 100 + (uiCarriedPercent - 100) * 3 + (uiCarriedPercent - 150);
145 		}
146 		else
147 		{
148 			// over 200%, extra BP cost of 3% per % above 200
149 			uiPercentCost = 100 + (uiCarriedPercent - 100) * 3 +
150 					(uiCarriedPercent - 150) + (uiCarriedPercent - 200);
151 			// so at 250% weight, we pay 400% breath!
152 		}
153 	}
154 	return( (INT16) uiPercentCost );
155 }
156 
157 
TerrainBreathPoints(SOLDIERTYPE * pSoldier,INT16 sGridno,INT8 bDir,UINT16 usMovementMode)158 INT16 TerrainBreathPoints(SOLDIERTYPE * pSoldier, INT16 sGridno,INT8 bDir, UINT16 usMovementMode)
159 {
160 	INT32 iPoints=0;
161 	UINT8 ubMovementCost;
162 
163 	ubMovementCost = gubWorldMovementCosts[sGridno][bDir][0];
164 
165 	switch( ubMovementCost )
166 	{
167 		case TRAVELCOST_DIRTROAD:
168 		case TRAVELCOST_FLAT:
169 			iPoints = BP_MOVEMENT_FLAT;
170 			break;
171 		//case TRAVELCOST_BUMPY:
172 		case TRAVELCOST_GRASS:
173 			iPoints = BP_MOVEMENT_GRASS;
174 			break;
175 		case TRAVELCOST_THICK:
176 			iPoints = BP_MOVEMENT_BUSH;
177 			break;
178 		case TRAVELCOST_DEBRIS:
179 			iPoints = BP_MOVEMENT_RUBBLE;
180 			break;
181 		case TRAVELCOST_SHORE:
182 			iPoints = BP_MOVEMENT_SHORE;
183 			break; // wading shallow water
184 		case TRAVELCOST_KNEEDEEP:
185 			iPoints = BP_MOVEMENT_LAKE;
186 			break; // wading waist/chest deep - very slow
187 		case TRAVELCOST_DEEPWATER:
188 			iPoints = BP_MOVEMENT_OCEAN;
189 			break; // can swim, so it's faster than wading
190 		default:
191 			if ( IS_TRAVELCOST_DOOR( ubMovementCost ) )
192 			{
193 				iPoints = BP_MOVEMENT_FLAT;
194 				break;
195 			}
196 			return(0);
197 	}
198 
199 	iPoints = iPoints * BreathPointAdjustmentForCarriedWeight( pSoldier ) / 100;
200 
201 	// ATE - MAKE MOVEMENT ALWAYS WALK IF IN WATER
202 	if (gpWorldLevelData[sGridno].ubTerrainID == DEEP_WATER ||
203 		gpWorldLevelData[sGridno].ubTerrainID == MED_WATER ||
204 		gpWorldLevelData[sGridno].ubTerrainID == LOW_WATER)
205 	{
206 		usMovementMode = WALKING;
207 	}
208 
209 	// so, then we must modify it for other movement styles and accumulate
210 	switch(usMovementMode)
211 	{
212 		case RUNNING:
213 		case ADULTMONSTER_WALKING:
214 		case BLOODCAT_RUN:
215 			iPoints *= BP_RUN_ENERGYCOSTFACTOR;
216 			break;
217 
218 		case SIDE_STEP:
219 		case WALK_BACKWARDS:
220 		case BLOODCAT_WALK_BACKWARDS:
221 		case MONSTER_WALK_BACKWARDS:
222 		case WALKING:
223 			iPoints *= BP_WALK_ENERGYCOSTFACTOR;
224 			break;
225 
226 		case START_SWAT:
227 		case SWATTING:
228 		case SWAT_BACKWARDS:
229 			iPoints *= BP_SWAT_ENERGYCOSTFACTOR;
230 			break;
231 		case CRAWLING:
232 			iPoints *= BP_CRAWL_ENERGYCOSTFACTOR;
233 			break;
234 
235 
236 	}
237 
238 	// ATE: Adjust these by realtime movement
239 	if (!(gTacticalStatus.uiFlags & INCOMBAT))
240 	{
241 		// ATE: ADJUST FOR RT - MAKE BREATH GO A LITTLE FASTER!
242 		iPoints = (INT32)( iPoints * TB_BREATH_DEDUCT_MODIFIER );
243 	}
244 
245 
246 	return( (INT16) iPoints);
247 }
248 
249 
ActionPointCost(const SOLDIERTYPE * const pSoldier,const INT16 sGridNo,const INT8 bDir,UINT16 usMovementMode)250 INT16 ActionPointCost(const SOLDIERTYPE* const pSoldier, const INT16 sGridNo, const INT8 bDir, UINT16 usMovementMode)
251 {
252 	INT16 sTileCost, sPoints, sSwitchValue;
253 
254 	sPoints = 0;
255 
256 	// get the tile cost for that tile based on WALKING
257 	sTileCost = TerrainActionPoints( pSoldier, sGridNo, bDir, pSoldier->bLevel );
258 
259 	// Get switch value...
260 	sSwitchValue = gubWorldMovementCosts[ sGridNo ][ bDir ][ pSoldier->bLevel ];
261 
262 	// Tile cost should not be reduced based on movement mode...
263 	if ( sSwitchValue == TRAVELCOST_FENCE )
264 	{
265 		return( sTileCost );
266 	}
267 
268 	// ATE - MAKE MOVEMENT ALWAYS WALK IF IN WATER
269 	if ( gpWorldLevelData[ sGridNo ].ubTerrainID == DEEP_WATER || gpWorldLevelData[ sGridNo ].ubTerrainID == MED_WATER || gpWorldLevelData[ sGridNo ].ubTerrainID == LOW_WATER )
270 	{
271 		usMovementMode = WALKING;
272 	}
273 
274 	// so, then we must modify it for other movement styles and accumulate
275 	if (sTileCost > 0)
276 	{
277 		switch(usMovementMode)
278 		{
279 			case RUNNING:
280 			case ADULTMONSTER_WALKING:
281 			case BLOODCAT_RUN:
282 				sPoints = (INT16)( ((DOUBLE)sTileCost) / RUNDIVISOR);
283 				break;
284 
285 			case CROW_FLY:
286 			case SIDE_STEP:
287 			case WALK_BACKWARDS:
288 			case ROBOT_WALK:
289 			case BLOODCAT_WALK_BACKWARDS:
290 			case MONSTER_WALK_BACKWARDS:
291 			case LARVAE_WALK:
292 			case KID_SKIPPING:
293 			case WALKING:
294 				sPoints = (sTileCost + WALKCOST);
295 				break;
296 
297 			case CROW_WALK:
298 			case START_SWAT:
299 			case SWAT_BACKWARDS:
300 			case SWATTING:
301 				sPoints = (sTileCost + SWATCOST);
302 				break;
303 			case CRAWLING:
304 				sPoints = (sTileCost + CRAWLCOST);
305 				break;
306 
307 			default:
308 
309 				// Invalid movement mode
310 				SLOGW(
311 					"Invalid movement mode %d used in ActionPointCost",
312 					usMovementMode);
313 				sPoints = 1;
314 		}
315 	}
316 
317 	if (sSwitchValue == TRAVELCOST_NOT_STANDING)
318 	{
319 		switch(usMovementMode)
320 		{
321 			case RUNNING:
322 			case WALKING :
323 			case LARVAE_WALK:
324 			case SIDE_STEP:
325 			case WALK_BACKWARDS:
326 				// charge crouch APs for ducking head!
327 				sPoints += AP_CROUCH;
328 				break;
329 
330 			default:
331 				break;
332 		}
333 	}
334 
335 	return( sPoints );
336 }
337 
EstimateActionPointCost(SOLDIERTYPE * pSoldier,INT16 sGridNo,INT8 bDir,UINT16 usMovementMode,INT8 bPathIndex,INT8 bPathLength)338 INT16 EstimateActionPointCost( SOLDIERTYPE *pSoldier, INT16 sGridNo, INT8 bDir, UINT16 usMovementMode, INT8 bPathIndex, INT8 bPathLength )
339 {
340 	// This action point cost code includes the penalty for having to change
341 	// stance after jumping a fence IF our path continues...
342 	INT16 sTileCost, sPoints, sSwitchValue;
343 	sPoints = 0;
344 
345 	// get the tile cost for that tile based on WALKING
346 	sTileCost = TerrainActionPoints( pSoldier, sGridNo, bDir, pSoldier->bLevel );
347 
348 	// so, then we must modify it for other movement styles and accumulate
349 	if (sTileCost > 0)
350 	{
351 		switch(usMovementMode)
352 		{
353 			case RUNNING:
354 			case ADULTMONSTER_WALKING:
355 			case BLOODCAT_RUN:
356 				sPoints = (INT16)(((DOUBLE)sTileCost) / RUNDIVISOR);
357 				break;
358 
359 			case CROW_FLY:
360 			case SIDE_STEP:
361 			case ROBOT_WALK:
362 			case WALK_BACKWARDS:
363 			case BLOODCAT_WALK_BACKWARDS:
364 			case MONSTER_WALK_BACKWARDS:
365 			case LARVAE_WALK:
366 			case KID_SKIPPING:
367 			case WALKING :
368 				sPoints = (sTileCost + WALKCOST);
369 				break;
370 
371 			case CROW_WALK:
372 			case START_SWAT:
373 			case SWAT_BACKWARDS:
374 			case SWATTING:
375 				sPoints = (sTileCost + SWATCOST);
376 				break;
377 			case CRAWLING:
378 				sPoints = (sTileCost + CRAWLCOST);
379 				break;
380 
381 			default:
382 
383 				// Invalid movement mode
384 				SLOGW(
385 					"Invalid movement mode %d used in EstimateActionPointCost",
386 					usMovementMode);
387 				sPoints = 1;
388 		}
389 	}
390 
391 	// Get switch value...
392 	sSwitchValue = gubWorldMovementCosts[ sGridNo ][ bDir ][ pSoldier->bLevel ];
393 
394 	// ATE: If we have a 'special cost, like jump fence...
395 	if ( sSwitchValue == TRAVELCOST_FENCE )
396 	{
397 		// If we are changeing stance ( either before or after getting there....
398 		// We need to reflect that...
399 		switch(usMovementMode)
400 		{
401 			case SIDE_STEP:
402 			case WALK_BACKWARDS:
403 			case RUNNING:
404 			case WALKING :
405 
406 				// Add here cost to go from crouch to stand AFTER fence hop....
407 				// Since it's AFTER.. make sure we will be moving after jump...
408 				if ( ( bPathIndex + 2 ) < bPathLength )
409 				{
410 					sPoints += AP_CROUCH;
411 				}
412 				break;
413 
414 			case SWATTING:
415 			case START_SWAT:
416 			case SWAT_BACKWARDS:
417 
418 				// Add cost to stand once there BEFORE....
419 				sPoints += AP_CROUCH;
420 				break;
421 
422 			case CRAWLING:
423 
424 				// Can't do it here.....
425 				break;
426 		}
427 	}
428 	else if (sSwitchValue == TRAVELCOST_NOT_STANDING)
429 	{
430 		switch(usMovementMode)
431 		{
432 			case RUNNING:
433 			case WALKING :
434 			case SIDE_STEP:
435 			case WALK_BACKWARDS:
436 				// charge crouch APs for ducking head!
437 				sPoints += AP_CROUCH;
438 				break;
439 
440 			default:
441 				break;
442 		}
443 	}
444 
445 	return( sPoints );
446 }
447 
448 
EnoughPoints(const SOLDIERTYPE * pSoldier,INT16 sAPCost,INT16 sBPCost,BOOLEAN fDisplayMsg)449 BOOLEAN EnoughPoints(const SOLDIERTYPE* pSoldier, INT16 sAPCost, INT16 sBPCost, BOOLEAN fDisplayMsg)
450 {
451 	INT16 sNewAP = 0;
452 
453 	// If this guy is on a special move... don't care about APS, OR BPSs!
454 	if ( pSoldier->ubWaitActionToDo  )
455 	{
456 		return( TRUE );
457 	}
458 
459 	// can't do anything while collapsed
460 	if (pSoldier->bCollapsed)
461 	{
462 		return FALSE;
463 	}
464 
465 	if ( pSoldier->ubQuoteActionID >=QUOTE_ACTION_ID_TRAVERSE_EAST && pSoldier->ubQuoteActionID <= QUOTE_ACTION_ID_TRAVERSE_NORTH )
466 	{
467 		// AI guy on special move off map
468 		return( TRUE );
469 	}
470 
471 	// IN realtime.. only care about BPs
472 	if (!(gTacticalStatus.uiFlags & INCOMBAT))
473 	{
474 		sAPCost = 0;
475 	}
476 
477 	// Get New points
478 	sNewAP = pSoldier->bActionPoints - sAPCost;
479 
480 	// If we cannot deduct points, return FALSE
481 	if ( sNewAP < 0 )
482 	{
483 		// Display message if it's our own guy
484 		if ( pSoldier->bTeam == OUR_TEAM && fDisplayMsg )
485 		{
486 			ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, TacticalStr[ NOT_ENOUGH_APS_STR ] );
487 		}
488 		return( FALSE );
489 	}
490 
491 	return( TRUE );
492 }
493 
494 
495 static INT16 AdjustBreathPts(SOLDIERTYPE* pSold, INT16 sBPCost);
496 
497 
DeductPoints(SOLDIERTYPE * pSoldier,INT16 sAPCost,INT16 sBPCost)498 void DeductPoints( SOLDIERTYPE *pSoldier, INT16 sAPCost, INT16 sBPCost )
499 {
500 	INT16 sNewAP = 0;
501 	INT8  bNewBreath;
502 
503 
504 	// in real time, there IS no AP cost, (only breath cost)
505 	if (!(gTacticalStatus.uiFlags & INCOMBAT))
506 	{
507 		sAPCost = 0;
508 	}
509 
510 	// Get New points
511 	sNewAP = pSoldier->bActionPoints - sAPCost;
512 
513 	// If this is the first time with no action points, set UI flag
514 	if ( sNewAP <= 0 && pSoldier->bActionPoints > 0 )
515 	{
516 		fInterfacePanelDirty = DIRTYLEVEL1;
517 	}
518 
519 	// If we cannot deduct points, return FALSE
520 	if ( sNewAP < 0 )
521 	{
522 		sNewAP = 0;
523 	}
524 
525 	pSoldier->bActionPoints = (INT8)sNewAP;
526 
527 	SLOGD("Deduct Points (%d at %d) %d %d",
528 				pSoldier->ubID, pSoldier->sGridNo, sAPCost, sBPCost);
529 
530 	if ( AM_A_ROBOT( pSoldier ) )
531 	{
532 		// zap all breath costs for robot
533 		sBPCost = 0;
534 	}
535 
536 	// is there a BREATH deduction/transaction to be made?  (REMEMBER: could be a GAIN!)
537 	if (sBPCost)
538 	{
539 		// Adjust breath changes due to spending or regaining of energy
540 		sBPCost = AdjustBreathPts(pSoldier,sBPCost);
541 		sBPCost *= -1;
542 
543 		pSoldier->sBreathRed -= sBPCost;
544 
545 		// CJC: moved check for high breathred to below so that negative breath can be detected
546 
547 		// cap breathred
548 		if ( pSoldier->sBreathRed < 0 )
549 		{
550 			pSoldier->sBreathRed = 0;
551 		}
552 		if ( pSoldier->sBreathRed > 10000 )
553 		{
554 			pSoldier->sBreathRed = 10000;
555 		}
556 
557 		// Get new breath
558 		bNewBreath = (UINT8)( pSoldier->bBreathMax - ( (FLOAT)pSoldier->sBreathRed / (FLOAT)100 ) );
559 
560 		if ( bNewBreath > 100 )
561 		{
562 			bNewBreath = 100;
563 		}
564 		if ( bNewBreath < 00 )
565 		{
566 			// Take off 1 AP per 5 breath... rem adding a negative subtracts
567 			pSoldier->bActionPoints += (bNewBreath / 5);
568 			if ( pSoldier->bActionPoints < 0 )
569 			{
570 				pSoldier->bActionPoints = 0;
571 			}
572 
573 			bNewBreath = 0;
574 		}
575 
576 		if( bNewBreath > pSoldier->bBreathMax )
577 		{
578 			bNewBreath = pSoldier->bBreathMax;
579 		}
580 		pSoldier->bBreath = bNewBreath;
581 	}
582 
583 	// UPDATE BAR
584 	DirtyMercPanelInterface( pSoldier, DIRTYLEVEL1 );
585 
586 }
587 
588 
AdjustBreathPts(SOLDIERTYPE * pSold,INT16 sBPCost)589 static INT16 AdjustBreathPts(SOLDIERTYPE* pSold, INT16 sBPCost)
590 {
591 	INT16 sBreathFactor = 100;
592 	UINT8 ubBandaged;
593 
594 	// in real time, there IS no AP cost, (only breath cost)
595 	/*
596 	if (!(gTacticalStatus.uiFlags & INCOMBAT))
597 	{
598 		// ATE: ADJUST FOR RT - MAKE BREATH GO A LITTLE FASTER!
599 		sBPCost	*= TB_BREATH_DEDUCT_MODIFIER;
600 	}*/
601 
602 
603 	// adjust breath factor for current breath deficiency
604 	sBreathFactor += (100 - pSold->bBreath);
605 
606 	// adjust breath factor for current life deficiency (but add 1/2 bandaging)
607 	ubBandaged = pSold->bLifeMax - pSold->bLife - pSold->bBleeding;
608 	//sBreathFactor += (pSold->bLifeMax - (pSold->bLife + (ubBandaged / 2)));
609 	sBreathFactor += 100 * (pSold->bLifeMax - (pSold->bLife + (ubBandaged / 2))) / pSold->bLifeMax;
610 
611 	if ( pSold->bStrength > 80 )
612 	{
613 		// give % reduction to breath costs for high strength mercs
614 		sBreathFactor -= (pSold->bStrength - 80) / 2;
615 	}
616 
617 	// if a non-swimmer type is thrashing around in deep water
618 	if ( (pSold->ubProfile != NO_PROFILE ) && (gMercProfiles[ pSold->ubProfile ].bPersonalityTrait == NONSWIMMER) )
619 	{
620 		if ( pSold->usAnimState == DEEP_WATER_TRED || pSold->usAnimState == DEEP_WATER_SWIM)
621 		{
622 			sBreathFactor *= 5;		// lose breath 5 times faster in deep water!
623 		}
624 	}
625 
626 	if ( sBreathFactor == 0 )
627 	{
628 			sBPCost = 0;
629 	}
630 	else if (sBPCost > 0) // breath DECREASE
631 	{
632 		// increase breath COST by breathFactor
633 		sBPCost = ((sBPCost * sBreathFactor) / 100);
634 	}
635 	else // breath INCREASE
636 	{
637 		// decrease breath GAIN by breathFactor
638 		sBPCost = ((sBPCost * 100) / sBreathFactor);
639 	}
640 
641 	return(sBPCost);
642 }
643 
644 
645 static INT16 GetBreathPerAP(SOLDIERTYPE* pSoldier, UINT16 usAnimState);
646 
647 
UnusedAPsToBreath(SOLDIERTYPE * pSold)648 void UnusedAPsToBreath(SOLDIERTYPE *pSold)
649 {
650 	INT16 sUnusedAPs, sBreathPerAP = 0, sBreathChange, sRTBreathMod;
651 
652 	// Note to Andrew (or whomever else it may concern):
653 
654 
655 	// This function deals with BETWEEN TURN breath/energy gains. The basic concept is:
656 	//
657 	//   - look at LAST (current) animation of merc to see what he's now doing
658 	//   - look at how many AP remain unspent (indicating duration of time doing that anim)
659 	//
660 	//  figure out how much breath/energy (if any) he should recover. Obviously if a merc
661 	//  is STANDING BREATHING and hasn't spent any AP then it means he *stood around* for
662 	//  the entire duration of one turn (which, instead of spending energy, REGAINS energy)
663 
664 
665 	// COMMENTED OUT FOR NOW SINCE MOST OF THE ANIMATION DEFINES DO NOT MATCH
666 
667 	// If we are not in turn-based combat...
668 
669 
670 	if ( pSold->uiStatusFlags & SOLDIER_VEHICLE )
671 	{
672 		return;
673 	}
674 
675 	if (!(gTacticalStatus.uiFlags & INCOMBAT))
676 	{
677 		// ALRIGHT, GIVE A FULL AMOUNT BACK, UNLES MODIFIED BY WHAT ACTIONS WE WERE DOING
678 		sBreathPerAP = GetBreathPerAP( pSold, pSold->usAnimState );
679 
680 		// adjust for carried weight
681 		sBreathPerAP = sBreathPerAP * 100 / BreathPointAdjustmentForCarriedWeight( pSold );
682 
683 		// If this value is -ve, we have a gain, else we have a loos which we should not really do
684 		// We just want to limit this to no gain if we were doing stuff...
685 		sBreathChange = 3 * sBreathPerAP;
686 
687 		// Adjust for on drugs
688 		HandleBPEffectDueToDrugs( pSold, &sBreathChange );
689 
690 		if ( sBreathChange > 0 )
691 		{
692 			sBreathChange = 0;
693 		}
694 		else
695 		{
696 			// We have a gain, now limit this depending on what we were doing...
697 			// OK for RT, look at how many tiles we have moved, our last move anim
698 			if ( pSold->ubTilesMovedPerRTBreathUpdate > 0 )
699 			{
700 				// How long have we done this for?
701 				// And what anim were we doing?
702 				sBreathPerAP = GetBreathPerAP( pSold, pSold->usLastMovementAnimPerRTBreathUpdate );
703 
704 				sRTBreathMod = sBreathPerAP * pSold->ubTilesMovedPerRTBreathUpdate;
705 
706 				// Deduct some if we were exerting ourselves
707 				// We add here because to gain breath, sBreathChange needs to be -ve
708 				if ( sRTBreathMod > 0 )
709 				{
710 					sBreathChange += sRTBreathMod;
711 				}
712 
713 				if ( sBreathChange < 0 )
714 				{
715 					sBreathChange = 0;
716 				}
717 			}
718 		}
719 
720 		// Divide by a number to adjust that in realtimer we do not want to recover as
721 		// as fast as the TB values do
722 		sBreathChange *= TB_BREATH_RECOVER_MODIFIER;
723 
724 
725 		// adjust breath only, don't touch action points!
726 		DeductPoints(pSold,0,(INT16)sBreathChange );
727 
728 		// Reset value for RT breath update
729 		pSold->ubTilesMovedPerRTBreathUpdate = 0;
730 
731 	}
732 	else
733 	{
734 		// if merc has any APs left unused this turn (that aren't carrying over)
735 		if (pSold->bActionPoints > MAX_AP_CARRIED)
736 		{
737 
738 			sUnusedAPs = pSold->bActionPoints - MAX_AP_CARRIED;
739 
740 			sBreathPerAP = GetBreathPerAP( pSold, pSold->usAnimState );
741 
742 			if (sBreathPerAP < 0)
743 			{
744 				// can't gain any breath when we've just been gassed, OR
745 				// if standing in tear gas without a gas mask on
746 				if ( pSold->uiStatusFlags & SOLDIER_GASSED )
747 				{
748 					return; // can't breathe here, so get no breath back!
749 				}
750 			}
751 
752 			// adjust for carried weight
753 			sBreathPerAP = sBreathPerAP * 100 / BreathPointAdjustmentForCarriedWeight( pSold );
754 
755 			sBreathChange = sUnusedAPs * sBreathPerAP;
756 		}
757 		else
758 		{
759 			sBreathChange = 0;
760 		}
761 		// Adjust for on drugs
762 		HandleBPEffectDueToDrugs( pSold, &sBreathChange );
763 
764 		// adjust breath only, don't touch action points!
765 		DeductPoints(pSold,0,(INT16)sBreathChange );
766 
767 	}
768 }
769 
770 
GetBreathPerAP(SOLDIERTYPE * pSoldier,UINT16 usAnimState)771 static INT16 GetBreathPerAP(SOLDIERTYPE* pSoldier, UINT16 usAnimState)
772 {
773 	INT16 sBreathPerAP = 0;
774 	BOOLEAN fAnimTypeFound = FALSE;
775 
776 	if ( gAnimControl[ usAnimState ].uiFlags & ANIM_VARIABLE_EFFORT )
777 	{
778 		// Default effort
779 		sBreathPerAP = BP_PER_AP_MIN_EFFORT;
780 
781 		// OK, check if we are in water and are waling/standing
782 		if ( MercInWater( pSoldier ) )
783 		{
784 			switch( usAnimState )
785 			{
786 				case STANDING:
787 
788 					sBreathPerAP = BP_PER_AP_LT_EFFORT;
789 					break;
790 
791 				case WALKING:
792 
793 					sBreathPerAP = BP_PER_AP_MOD_EFFORT;
794 					break;
795 			}
796 		}
797 		else
798 		{
799 
800 			switch( usAnimState )
801 			{
802 				case STANDING:
803 
804 					sBreathPerAP = BP_PER_AP_NO_EFFORT;
805 					break;
806 
807 				case WALKING:
808 
809 					sBreathPerAP = BP_PER_AP_LT_EFFORT;
810 					break;
811 			}
812 		}
813 		fAnimTypeFound = TRUE;
814 	}
815 
816 	if ( gAnimControl[ usAnimState ].uiFlags & ANIM_NO_EFFORT )
817 	{
818 		sBreathPerAP = BP_PER_AP_NO_EFFORT;
819 		fAnimTypeFound = TRUE;
820 	}
821 
822 	if ( gAnimControl[ usAnimState ].uiFlags & ANIM_MIN_EFFORT )
823 	{
824 		sBreathPerAP = BP_PER_AP_MIN_EFFORT;
825 		fAnimTypeFound = TRUE;
826 	}
827 
828 	if ( gAnimControl[ usAnimState ].uiFlags & ANIM_LIGHT_EFFORT )
829 	{
830 		sBreathPerAP = BP_PER_AP_LT_EFFORT;
831 		fAnimTypeFound = TRUE;
832 	}
833 
834 	if ( gAnimControl[ usAnimState ].uiFlags & ANIM_MODERATE_EFFORT )
835 	{
836 		sBreathPerAP = BP_PER_AP_MOD_EFFORT;
837 		fAnimTypeFound = TRUE;
838 	}
839 
840 	if ( !fAnimTypeFound )
841 	{
842 		SLOGD("Unknown end-of-turn breath anim: %s",
843 			gAnimControl[usAnimState].zAnimStr);
844 	}
845 
846 	return( sBreathPerAP );
847 }
848 
849 
CalcAPsToBurst(INT8 const bBaseActionPoints,OBJECTTYPE const & o)850 UINT8 CalcAPsToBurst(INT8 const bBaseActionPoints, OBJECTTYPE const& o)
851 {
852 	// base APs is what you'd get from CalcActionPoints();
853 	if (o.usItem == G11)
854 	{
855 		return( 1 );
856 	}
857 	else
858 	{
859 		// NB round UP, so 21-25 APs pay full
860 		INT8 const bAttachPos = FindAttachment(&o, SPRING_AND_BOLT_UPGRADE );
861 		if ( bAttachPos != -1 )
862 		{
863 			return (__max(3, (AP_BURST * bBaseActionPoints + (AP_MAXIMUM - 1)) / AP_MAXIMUM) * 100) / (100 + o.bAttachStatus[bAttachPos] / 5);
864 		}
865 		else
866 		{
867 			return __max(3, (AP_BURST * bBaseActionPoints + (AP_MAXIMUM - 1)) / AP_MAXIMUM);
868 		}
869 	}
870 }
871 
872 
CalcTotalAPsToAttack(SOLDIERTYPE * const s,INT16 const grid_no,UINT8 const ubAddTurningCost,INT8 const aim_time)873 UINT8 CalcTotalAPsToAttack(SOLDIERTYPE* const s, INT16 const grid_no, UINT8 const ubAddTurningCost, INT8 const aim_time)
874 {
875 	UINT16            ap_cost = 0;
876 	OBJECTTYPE const& in_hand = s->inv[HANDPOS];
877 	switch (GCM->getItem(in_hand.usItem)->getItemClass())
878 	{
879 		case IC_GUN:
880 		case IC_LAUNCHER:
881 		case IC_TENTACLES:
882 		case IC_THROWING_KNIFE:
883 			ap_cost  = MinAPsToAttack(s, grid_no, ubAddTurningCost);
884 			ap_cost +=
885 				s->bDoBurst ? CalcAPsToBurst(CalcActionPoints(s), in_hand) :
886 				aim_time;
887 			break;
888 
889 		case IC_GRENADE:
890 		case IC_BOMB:
891 #if 0 // XXX always was this way
892 			ap_cost = MinAPsToAttack(s, grid_no, ubAddTurningCost);
893 #else
894 			ap_cost = 5;
895 #endif
896 			break;
897 
898 		case IC_PUNCH:
899 		case IC_BLADE:
900 			// If we are at this gridno, calc min APs but if not, calc cost to goto this
901 			// location
902 			INT16 adjusted_grid_no;
903 			if (s->sGridNo == grid_no)
904 			{
905 				adjusted_grid_no = grid_no;
906 			}
907 			else
908 			{
909 				if (s->sWalkToAttackGridNo == grid_no)
910 				{
911 					// In order to avoid path calculations here all the time, save and check
912 					// if it's changed!
913 					adjusted_grid_no = grid_no;
914 				}
915 				else
916 				{
917 					GridNo got_location = NOWHERE;
918 
919 					if (SOLDIERTYPE const* const tgt = WhoIsThere2(grid_no, s->bLevel))
920 					{
921 						if (s->ubBodyType == BLOODCAT)
922 						{
923 							got_location = FindNextToAdjacentGridEx(s, grid_no, 0, &adjusted_grid_no, TRUE, FALSE);
924 							if (got_location == -1) got_location = NOWHERE;
925 						}
926 						else
927 						{
928 							got_location = FindAdjacentPunchTarget(s, tgt, &adjusted_grid_no);
929 						}
930 					}
931 
932 					bool got_adjacent = false;
933 					if (got_location == NOWHERE && s->ubBodyType != BLOODCAT)
934 					{
935 						got_location = FindAdjacentGridEx(s, grid_no, 0, &adjusted_grid_no, TRUE, FALSE);
936 						if (got_location == -1) got_location = NOWHERE;
937 						got_adjacent = true;
938 					}
939 
940 					if (got_location == NOWHERE) return 0;
941 
942 					if (s->sGridNo == got_location || !got_adjacent)
943 					{
944 						s->sWalkToAttackWalkToCost = 0;
945 					}
946 					else
947 					{
948 						// Save for next time
949 						s->sWalkToAttackWalkToCost = PlotPath(s, got_location, NO_COPYROUTE, NO_PLOT, s->usUIMovementMode, s->bActionPoints);
950 						if (s->sWalkToAttackWalkToCost == 0) return 99;
951 					}
952 
953 					// Save old location!
954 					s->sWalkToAttackGridNo = grid_no;
955 				}
956 				ap_cost += s->sWalkToAttackWalkToCost;
957 			}
958 			ap_cost += MinAPsToAttack(s, adjusted_grid_no, ubAddTurningCost);
959 			ap_cost += aim_time;
960 			break;
961 	}
962 
963 	return ap_cost;
964 }
965 
966 
967 static UINT8 MinAPsToPunch(SOLDIERTYPE const&, GridNo, bool add_turning_cost);
968 
969 
MinAPsToAttack(SOLDIERTYPE * const s,GridNo const grid_no,UINT8 const add_turning_cost)970 UINT8 MinAPsToAttack(SOLDIERTYPE* const s, GridNo const grid_no, UINT8 const add_turning_cost)
971 {
972 	OBJECTTYPE const& in_hand = s->inv[HANDPOS];
973 	UINT16            item    = in_hand.usItem;
974 	if (s->bWeaponMode == WM_ATTACHED)
975 	{ // Look for an attached grenade launcher
976 		INT8 const attach_slot = FindAttachment(&in_hand, UNDER_GLAUNCHER);
977 		if (attach_slot != NO_SLOT) item = UNDER_GLAUNCHER;
978 	}
979 
980 	switch (GCM->getItem(item)->getItemClass())
981 	{
982 		case IC_BLADE:
983 		case IC_GUN:
984 		case IC_LAUNCHER:
985 		case IC_TENTACLES:
986 		case IC_THROWING_KNIFE: return MinAPsToShootOrStab(*s, grid_no, add_turning_cost);
987 		case IC_GRENADE:
988 		case IC_THROWN:         return MinAPsToThrow(*s, grid_no, add_turning_cost);
989 		case IC_PUNCH:          return MinAPsToPunch(*s, grid_no, add_turning_cost);
990 		default:                return 0;
991 	}
992 }
993 
994 
CalcAimSkill(SOLDIERTYPE const & s,UINT16 const weapon)995 static INT8 CalcAimSkill(SOLDIERTYPE const& s, UINT16 const weapon)
996 {
997 	switch (GCM->getItem(weapon)->getItemClass())
998 	{
999 		case IC_GUN:
1000 		case IC_LAUNCHER:
1001 			// Guns: Modify aiming cost by shooter's marksmanship
1002 			return EffectiveMarksmanship(&s);
1003 
1004 		default:
1005 			// Knives: Modify aiming cost by avg of attacker's dexterity & agility
1006 			return (EffectiveDexterity(&s) + EffectiveAgility(&s)) / 2;
1007 	}
1008 }
1009 
1010 
BaseAPsToShootOrStab(INT8 const bAPs,INT8 const bAimSkill,OBJECTTYPE const & o)1011 UINT8 BaseAPsToShootOrStab(INT8 const bAPs, INT8 const bAimSkill, OBJECTTYPE const& o)
1012 {
1013 	INT16 sTop, sBottom;
1014 
1015 	// Calculate default top & bottom of the magic "aiming" formula!
1016 
1017 	// get this man's maximum possible action points (ignoring carryovers)
1018 	// the 2 times is here only to allow rounding off using integer math later
1019 	sTop = 2 * bAPs;//CalcActionPoints( pSoldier );
1020 
1021 	// Shots per turn rating is for max. aimSkill(100), drops down to 1/2 at = 0
1022 	// DIVIDE BY 4 AT THE END HERE BECAUSE THE SHOTS PER TURN IS NOW QUADRUPLED!
1023 	// NB need to define shots per turn for ALL Weapons then.
1024 	sBottom = ( ( 50 + (bAimSkill / 2) ) * GCM->getWeapon(o.usItem )->ubShotsPer4Turns ) / 4;
1025 
1026 	INT8 const bAttachPos = FindAttachment(&o, SPRING_AND_BOLT_UPGRADE);
1027 	if ( bAttachPos != -1 )
1028 	{
1029 		sBottom = sBottom * (100 + o.bAttachStatus[bAttachPos] / 5) / 100;
1030 	}
1031 
1032 	// add minimum aiming time to the overall minimum AP_cost
1033 	//     This here ROUNDS UP fractions of 0.5 or higher using integer math
1034 	//     This works because 'top' is 2x what it really should be throughout
1035 	return( ( ( ( 100 * sTop ) / sBottom ) + 1) / 2);
1036 }
1037 
1038 
GetAPChargeForShootOrStabWRTGunRaises(SOLDIERTYPE const * const s,GridNo grid_no,UINT8 const ubAddTurningCost,BOOLEAN * const charge_turning,BOOLEAN * const charge_raise)1039 void GetAPChargeForShootOrStabWRTGunRaises(SOLDIERTYPE const* const s, GridNo grid_no, UINT8 const ubAddTurningCost, BOOLEAN* const charge_turning, BOOLEAN* const charge_raise)
1040 {
1041 	bool adding_turning_cost = FALSE;
1042 	if (ubAddTurningCost)
1043 	{
1044 		if (grid_no != NOWHERE)
1045 		{
1046 			// Get direction and see if we need to turn
1047 			// Given a gridno here, check if we are on a guy - if so - get his gridno
1048 			SOLDIERTYPE const* const tgt = FindSoldier(grid_no, FIND_SOLDIER_GRIDNO);
1049 			if (tgt) grid_no = tgt->sGridNo;
1050 
1051 			// Is it the same as he's facing?
1052 			UINT8 const direction = GetDirectionFromGridNo(grid_no, s);
1053 			adding_turning_cost = direction != s->bDirection;
1054 		}
1055 		else
1056 		{
1057 			// Assume we need to add cost!
1058 			adding_turning_cost = true;
1059 		}
1060 	}
1061 	*charge_turning = adding_turning_cost;
1062 
1063 	// Do we need to ready weapon?
1064 	*charge_raise = GCM->getItem(s->inv[HANDPOS].usItem)->getItemClass() != IC_THROWING_KNIFE &&
1065 			!(gAnimControl[s->usAnimState].uiFlags & (ANIM_FIREREADY | ANIM_FIRE));
1066 }
1067 
1068 
MinAPsToShootOrStab(SOLDIERTYPE & s,GridNo gridno,bool const add_turning_cost)1069 UINT8 MinAPsToShootOrStab(SOLDIERTYPE& s, GridNo gridno, bool const add_turning_cost)
1070 {
1071 	OBJECTTYPE const& in_hand = s.inv[HANDPOS];
1072 	UINT16     const  item =
1073 	s.bWeaponMode == WM_ATTACHED ? UNDER_GLAUNCHER : in_hand.usItem;
1074 
1075 	BOOLEAN	adding_turning_cost;
1076 	BOOLEAN	adding_raise_gun_cost;
1077 	GetAPChargeForShootOrStabWRTGunRaises(&s, gridno, add_turning_cost, &adding_turning_cost, &adding_raise_gun_cost);
1078 
1079 	UINT8	ap_cost = AP_MIN_AIM_ATTACK;
1080 
1081 	if (GCM->getItem(item)->getItemClass() == IC_THROWING_KNIFE ||
1082 		item == ROCKET_LAUNCHER)
1083 	{
1084 		// Do we need to stand up?
1085 		ap_cost += GetAPsToChangeStance(&s, ANIM_STAND);
1086 	}
1087 
1088 	// ATE: Look at stance
1089 	if (gAnimControl[s.usAnimState].ubHeight == ANIM_STAND)
1090 	{
1091 		// Don't charge turning if gun-ready
1092 		if (adding_raise_gun_cost) adding_turning_cost = FALSE;
1093 	}
1094 	else
1095 	{
1096 		// Just charge turning costs
1097 		if (adding_turning_cost) adding_raise_gun_cost = FALSE;
1098 	}
1099 
1100 	if (AM_A_ROBOT(&s)) adding_raise_gun_cost = FALSE;
1101 
1102 	if (adding_turning_cost)
1103 	{
1104 		if (GCM->getItem(item)->getItemClass() == IC_THROWING_KNIFE)
1105 		{
1106 			ap_cost += AP_LOOK_STANDING;
1107 		}
1108 		else
1109 		{
1110 			ap_cost += GetAPsToLook(&s);
1111 		}
1112 	}
1113 
1114 	if (adding_raise_gun_cost)
1115 	{
1116 		ap_cost += GetAPsToReadyWeapon(&s, s.usAnimState);
1117 		s.fDontChargeReadyAPs = FALSE;
1118 	}
1119 
1120 	if (gridno != NOWHERE)
1121 	{
1122 		// Given a gridno here, check if we are on a guy - if so - get his gridno
1123 		SOLDIERTYPE const* const tgt = FindSoldier(gridno, FIND_SOLDIER_GRIDNO);
1124 		if (tgt) gridno = tgt->sGridNo;
1125 	}
1126 
1127 	// if attacking a new target (or if the specific target is uncertain)
1128 	if (gridno != s.sLastTarget && item != ROCKET_LAUNCHER)
1129 	{
1130 		ap_cost += AP_CHANGE_TARGET;
1131 	}
1132 
1133 	INT8 const full_aps  = CalcActionPoints(&s);
1134 	// Aim skill is the same whether we are using 1 or 2 guns
1135 	INT8 const aim_skill = CalcAimSkill(s, item);
1136 
1137 	if (s.bWeaponMode == WM_ATTACHED)
1138 	{
1139 		// Create temporary grenade launcher and use that
1140 		INT8 const attach_slot = FindAttachment(&in_hand, UNDER_GLAUNCHER);
1141 		INT8 const status      = attach_slot != NO_SLOT ? in_hand.bAttachStatus[attach_slot] :
1142 						100; // Fake it, use a 100 status
1143 		OBJECTTYPE grenade_launcher;
1144 		CreateItem(UNDER_GLAUNCHER, status, &grenade_launcher);
1145 
1146 		ap_cost += BaseAPsToShootOrStab(full_aps, aim_skill, grenade_launcher);
1147 	}
1148 	else if (IsValidSecondHandShot(&s))
1149 	{
1150 		// Charge the maximum of the two
1151 		UINT8 const ap_1st = BaseAPsToShootOrStab(full_aps, aim_skill, in_hand);
1152 		UINT8 const ap_2nd = BaseAPsToShootOrStab(full_aps, aim_skill, s.inv[SECONDHANDPOS]);
1153 		ap_cost += __max(ap_1st, ap_2nd);
1154 	}
1155 	else
1156 	{
1157 		ap_cost += BaseAPsToShootOrStab(full_aps, aim_skill, in_hand);
1158 	}
1159 
1160 	// The minimum AP cost of ANY shot can NEVER be more than merc's maximum APs
1161 	if (ap_cost > full_aps) ap_cost = full_aps;
1162 
1163 	// this SHOULD be impossible, but nevertheless
1164 	if (ap_cost < 1) ap_cost = 1;
1165 
1166 	return ap_cost;
1167 }
1168 
1169 
MinAPsToPunch(SOLDIERTYPE const & s,GridNo gridno,bool const add_turning_cost)1170 static UINT8 MinAPsToPunch(SOLDIERTYPE const& s, GridNo gridno, bool const add_turning_cost)
1171 {
1172 	UINT8 ap = 4;
1173 
1174 	// We return 0 for invalid attacks; will be handled as impossible
1175 	if (gridno == NOWHERE)
1176 	{
1177 		return 0;
1178 	}
1179 
1180 	if (SOLDIERTYPE const* const tgt = WhoIsThere2(gridno, s.bTargetLevel))
1181 	{	// On a guy, get his gridno
1182 		gridno = tgt->sGridNo;
1183 
1184 		// Check if target is prone, if so, calc cost
1185 		if (gAnimControl[tgt->usAnimState].ubEndHeight == ANIM_PRONE)
1186 		{
1187 			ap += GetAPsToChangeStance(&s, ANIM_CROUCH);
1188 		}
1189 		else if (s.sGridNo == gridno)
1190 		{
1191 			ap += GetAPsToChangeStance(&s, ANIM_STAND);
1192 		}
1193 	}
1194 
1195 	if (add_turning_cost && s.sGridNo == gridno)
1196 	{
1197 		// Is it the same as he's facing?
1198 		UINT8 const direction = GetDirectionFromGridNo(gridno, &s);
1199 		if (direction != s.bDirection) ap += AP_LOOK_STANDING; // ATE: Use standing turn cost
1200 	}
1201 
1202 	return ap;
1203 }
1204 
1205 
MinPtsToMove(const SOLDIERTYPE * const pSoldier)1206 INT8 MinPtsToMove(const SOLDIERTYPE* const pSoldier)
1207 {
1208 	// look around all 8 directions and return lowest terrain cost
1209 	UINT8 cnt;
1210 	INT16 sLowest=127;
1211 	INT16 sGridno,sCost;
1212 
1213 	if ( TANK( pSoldier ) )
1214 	{
1215 		return( (INT8)sLowest);
1216 	}
1217 
1218 	for (cnt=0; cnt <= 7; cnt++)
1219 	{
1220 		sGridno = NewGridNo(pSoldier->sGridNo,DirectionInc(cnt));
1221 		if (sGridno != pSoldier->sGridNo)
1222 		{
1223 			if ( (sCost=ActionPointCost( pSoldier, sGridno, cnt , pSoldier->usUIMovementMode ) ) < sLowest )
1224 			{
1225 				sLowest = sCost;
1226 			}
1227 		}
1228 	}
1229 	return( (INT8)sLowest);
1230 }
1231 
1232 
PtsToMoveDirection(const SOLDIERTYPE * const pSoldier,const UINT8 bDirection)1233 INT8 PtsToMoveDirection(const SOLDIERTYPE* const pSoldier, const UINT8 bDirection)
1234 {
1235 	INT16  sGridno,sCost;
1236 	INT8   bOverTerrainType;
1237 	UINT16 usMoveModeToUse;
1238 
1239 	sGridno = NewGridNo( pSoldier->sGridNo, DirectionInc( bDirection ) );
1240 
1241 	usMoveModeToUse = pSoldier->usUIMovementMode;
1242 
1243 	// ATE: Check if the new place is watter and we were tying to run....
1244 	bOverTerrainType = GetTerrainType( sGridno );
1245 
1246 	if ( bOverTerrainType == MED_WATER || bOverTerrainType == DEEP_WATER || bOverTerrainType == LOW_WATER )
1247 	{
1248 		usMoveModeToUse = WALKING;
1249 	}
1250 
1251 	sCost = ActionPointCost( pSoldier, sGridno, bDirection , usMoveModeToUse );
1252 
1253 	if ( gubWorldMovementCosts[ sGridno ][ bDirection ][ pSoldier->bLevel ] != TRAVELCOST_FENCE )
1254 	{
1255 		if ( usMoveModeToUse == RUNNING && pSoldier->usAnimState != RUNNING )
1256 		{
1257 			sCost += AP_START_RUN_COST;
1258 		}
1259 	}
1260 
1261 	return( (INT8)sCost );
1262 }
1263 
1264 
MinAPsToStartMovement(const SOLDIERTYPE * pSoldier,UINT16 usMovementMode)1265 INT8 MinAPsToStartMovement(const SOLDIERTYPE* pSoldier, UINT16 usMovementMode)
1266 {
1267 	INT8 bAPs = 0;
1268 
1269 	switch( usMovementMode )
1270 	{
1271 		case RUNNING:
1272 		case WALKING:
1273 			if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE)
1274 			{
1275 				bAPs += AP_CROUCH + AP_PRONE;
1276 			}
1277 			else if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH)
1278 			{
1279 				bAPs += AP_CROUCH;
1280 			}
1281 			break;
1282 		case SWATTING:
1283 			if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_PRONE)
1284 			{
1285 				bAPs += AP_PRONE;
1286 			}
1287 			else if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND)
1288 			{
1289 				bAPs += AP_CROUCH;
1290 			}
1291 			break;
1292 		case CRAWLING:
1293 			if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_STAND)
1294 			{
1295 				bAPs += AP_CROUCH + AP_PRONE;
1296 			}
1297 			else if (gAnimControl[ pSoldier->usAnimState ].ubEndHeight == ANIM_CROUCH)
1298 			{
1299 				bAPs += AP_CROUCH;
1300 			}
1301 			break;
1302 		default:
1303 			break;
1304 	}
1305 
1306 	if (usMovementMode == RUNNING && pSoldier->usAnimState != RUNNING )
1307 	{
1308 		bAPs += AP_START_RUN_COST;
1309 	}
1310 	return( bAPs );
1311 }
1312 
1313 
EnoughAmmo(SOLDIERTYPE * const s,BOOLEAN const fDisplay,INT8 const inv_pos)1314 BOOLEAN EnoughAmmo(SOLDIERTYPE* const s, BOOLEAN const fDisplay, INT8 const inv_pos)
1315 {
1316 	OBJECTTYPE const& o        = s->inv[inv_pos];
1317 	UINT16     const  item_idx = o.usItem;
1318 	if (item_idx == NOTHING) return FALSE;
1319 
1320 	if (s->bWeaponMode == WM_ATTACHED) return TRUE;
1321 
1322 	// hack... they turn empty afterwards anyways
1323 	if (item_idx == ROCKET_LAUNCHER) return TRUE;
1324 
1325 	const ItemModel * item = GCM->getItem(item_idx);
1326 	if (item->getItemClass() == IC_LAUNCHER || item_idx == TANK_CANNON)
1327 	{
1328 		if (FindAttachmentByClass(&o, IC_GRENADE) != ITEM_NOT_FOUND) return TRUE;
1329 		if (FindAttachmentByClass(&o, IC_BOMB)    != ITEM_NOT_FOUND) return TRUE;
1330 	}
1331 	else if (item->getItemClass() == IC_GUN)
1332 	{
1333 		if (o.ubGunShotsLeft != 0) return TRUE;
1334 	}
1335 	else
1336 	{
1337 		return TRUE;
1338 	}
1339 
1340 	if (fDisplay) TacticalCharacterDialogue(s, QUOTE_OUT_OF_AMMO);
1341 	return FALSE;
1342 }
1343 
1344 
DeductAmmo(SOLDIERTYPE * pSoldier,INT8 bInvPos)1345 void DeductAmmo( SOLDIERTYPE *pSoldier, INT8 bInvPos )
1346 {
1347 	OBJECTTYPE *pObj;
1348 
1349 	// tanks never run out of MG ammo!
1350 	// unlimited cannon ammo is handled in AI
1351 	if ( TANK( pSoldier ) && pSoldier->inv[bInvPos].usItem != TANK_CANNON )
1352 	{
1353 		return;
1354 	}
1355 
1356 	pObj = &(pSoldier->inv[ bInvPos ]);
1357 	if ( pObj->usItem != NOTHING )
1358 	{
1359 		if ( pObj->usItem == TANK_CANNON )
1360 		{
1361 		}
1362 		else if ( GCM->getItem(pObj->usItem)->getItemClass() == IC_GUN && pObj->usItem != TANK_CANNON )
1363 		{
1364 			if ( pSoldier->usAttackingWeapon == pObj->usItem)
1365 			{
1366 				// OK, let's see, don't overrun...
1367 				if ( pObj->ubGunShotsLeft != 0 )
1368 				{
1369 					pObj->ubGunShotsLeft--;
1370 				}
1371 			}
1372 			else
1373 			{
1374 				// firing an attachment?
1375 			}
1376 		}
1377 		else if ( GCM->getItem(pObj->usItem)->getItemClass() == IC_LAUNCHER || pObj->usItem == TANK_CANNON )
1378 		{
1379 			INT8 bAttachPos;
1380 
1381 			bAttachPos = FindAttachmentByClass( pObj, IC_GRENADE );
1382 			if (bAttachPos == ITEM_NOT_FOUND )
1383 			{
1384 				bAttachPos = FindAttachmentByClass( pObj, IC_BOMB );
1385 			}
1386 
1387 			if (bAttachPos != ITEM_NOT_FOUND)
1388 			{
1389 				RemoveAttachment( pObj, bAttachPos, NULL );
1390 			}
1391 		}
1392 
1393 		// Dirty Bars
1394 		DirtyMercPanelInterface( pSoldier, DIRTYLEVEL1 );
1395 
1396 	}
1397 }
1398 
1399 
GetMovePlusActionAPCosts(SOLDIERTYPE * const s,GridNo const pos,INT16 const action_ap)1400 static INT16 GetMovePlusActionAPCosts(SOLDIERTYPE* const s, GridNo const pos, INT16 const action_ap)
1401 {
1402 	if (s->sGridNo == pos) return action_ap;
1403 	INT16 const move_ap = PlotPath(s, pos, NO_COPYROUTE, NO_PLOT, s->usUIMovementMode, s->bActionPoints);
1404 	if (move_ap == 0) return 0; // Destination unreachable
1405 	return move_ap + action_ap;
1406 }
1407 
1408 
GetAPsToPickupItem(SOLDIERTYPE * const s,UINT16 const usMapPos)1409 UINT16 GetAPsToPickupItem(SOLDIERTYPE* const s, UINT16 const usMapPos)
1410 {
1411 	// Check if we are over an item pool
1412 	if (!GetItemPool(usMapPos, s->bLevel)) return 0;
1413 	INT16 const sActionGridNo = AdjustGridNoForItemPlacement(s, usMapPos);
1414 	return GetMovePlusActionAPCosts(s, sActionGridNo, AP_PICKUP_ITEM);
1415 }
1416 
1417 
GetAPsToGiveItem(SOLDIERTYPE * const s,UINT16 const usMapPos)1418 UINT16 GetAPsToGiveItem(SOLDIERTYPE* const s, UINT16 const usMapPos)
1419 {
1420 	return GetMovePlusActionAPCosts(s, usMapPos, AP_GIVE_ITEM);
1421 }
1422 
1423 
GetAPsToReloadGunWithAmmo(OBJECTTYPE * pGun,OBJECTTYPE * pAmmo)1424 INT8 GetAPsToReloadGunWithAmmo( OBJECTTYPE * pGun, OBJECTTYPE * pAmmo )
1425 {
1426 	if (GCM->getItem(pGun->usItem)->getItemClass() == IC_LAUNCHER)
1427 	{
1428 		// always standard AP cost
1429 		return( AP_RELOAD_GUN );
1430 	}
1431 	if ( GCM->getWeapon(pGun->usItem)->isSameMagCapacity(GCM->getItem(pAmmo->usItem)->asAmmo()))
1432 	{
1433 		// normal situation
1434 		return( AP_RELOAD_GUN );
1435 	}
1436 	else
1437 	{
1438 		// trying to reload with wrong size of magazine
1439 		return( AP_RELOAD_GUN + AP_RELOAD_GUN );
1440 	}
1441 }
1442 
GetAPsToAutoReload(SOLDIERTYPE * pSoldier)1443 INT8 GetAPsToAutoReload( SOLDIERTYPE * pSoldier )
1444 {
1445 	OBJECTTYPE *pObj;
1446 	INT8 bSlot, bSlot2, bExcludeSlot;
1447 	INT8 bAPCost  = 0;
1448 	INT8 bAPCost2 = 0;
1449 
1450 	CHECKF( pSoldier );
1451 	pObj = &(pSoldier->inv[HANDPOS]);
1452 
1453 	if (GCM->getItem(pObj->usItem)->getItemClass() == IC_GUN || GCM->getItem(pObj->usItem)->getItemClass() == IC_LAUNCHER)
1454 	{
1455 		bSlot = FindAmmoToReload( pSoldier, HANDPOS, NO_SLOT );
1456 		if (bSlot == NO_SLOT)
1457 		{
1458 			// we would not reload
1459 			return( 0 );
1460 		}
1461 
1462 		// we would reload using this ammo!
1463 		bAPCost += GetAPsToReloadGunWithAmmo( pObj, &(pSoldier->inv[bSlot] ) );
1464 		// if we are valid for two-pistol shooting (reloading) and we have enough APs still
1465 		// then we would do a reload of both guns!
1466 		if ( IsValidSecondHandShotForReloadingPurposes( pSoldier ) )
1467 		{
1468 			pObj = &(pSoldier->inv[SECONDHANDPOS]);
1469 			bExcludeSlot = NO_SLOT;
1470 			bSlot2 = NO_SLOT;
1471 
1472 			// if the ammo for the first gun is the same we have to do special checks
1473 			if ( ValidAmmoType( pObj->usItem, pSoldier->inv[ bSlot ].usItem ) )
1474 			{
1475 				if ( pSoldier->inv[ bSlot ].ubNumberOfObjects == 1 )
1476 				{
1477 					// we must not consider this slot for reloading!
1478 					bExcludeSlot = bSlot;
1479 				}
1480 				else
1481 				{
1482 					// we can reload the 2nd gun from the same pocket!
1483 					bSlot2 = bSlot;
1484 				}
1485 			}
1486 
1487 			if (bSlot2 == NO_SLOT)
1488 			{
1489 				bSlot2 = FindAmmoToReload( pSoldier, SECONDHANDPOS, bExcludeSlot );
1490 			}
1491 
1492 			if (bSlot2 != NO_SLOT)
1493 			{
1494 				// we would reload using this ammo!
1495 				bAPCost2 = GetAPsToReloadGunWithAmmo( pObj, &(pSoldier->inv[bSlot2] ) );
1496 				if ( EnoughPoints( pSoldier, (INT16) (bAPCost + bAPCost2), 0, FALSE ) )
1497 				{
1498 					// we can afford to reload both guns; otherwise display just for 1 gun
1499 					bAPCost += bAPCost2;
1500 				}
1501 			}
1502 
1503 		}
1504 
1505 	}
1506 
1507 	return( bAPCost );
1508 }
1509 
1510 
GetAPsToReloadRobot(SOLDIERTYPE * const s,SOLDIERTYPE const * const robot)1511 UINT16 GetAPsToReloadRobot(SOLDIERTYPE* const s, SOLDIERTYPE const* const robot)
1512 {
1513 	GridNo const sActionGridNo = FindAdjacentGridEx(s, robot->sGridNo, NULL, NULL, TRUE, FALSE);
1514 	return GetMovePlusActionAPCosts(s, sActionGridNo, 4);
1515 }
1516 
1517 
GetAPsToChangeStance(const SOLDIERTYPE * pSoldier,INT8 bDesiredHeight)1518 UINT16 GetAPsToChangeStance(const SOLDIERTYPE* pSoldier, INT8 bDesiredHeight)
1519 {
1520 	UINT16 sAPCost = 0;
1521 	INT8   bCurrentHeight;
1522 
1523 	bCurrentHeight = gAnimControl[ pSoldier->usAnimState ].ubEndHeight;
1524 
1525 	if ( bCurrentHeight == bDesiredHeight )
1526 	{
1527 		sAPCost = 0;
1528 	}
1529 
1530 	if ( bCurrentHeight == ANIM_STAND && bDesiredHeight == ANIM_PRONE )
1531 	{
1532 		sAPCost = AP_CROUCH + AP_PRONE;
1533 	}
1534 	if ( bCurrentHeight == ANIM_STAND && bDesiredHeight == ANIM_CROUCH )
1535 	{
1536 		sAPCost = AP_CROUCH;
1537 	}
1538 	if ( bCurrentHeight == ANIM_CROUCH && bDesiredHeight == ANIM_PRONE )
1539 	{
1540 		sAPCost = AP_PRONE;
1541 	}
1542 	if ( bCurrentHeight == ANIM_CROUCH && bDesiredHeight == ANIM_STAND )
1543 	{
1544 		sAPCost = AP_CROUCH;
1545 	}
1546 	if ( bCurrentHeight == ANIM_PRONE && bDesiredHeight == ANIM_STAND )
1547 	{
1548 		sAPCost = AP_PRONE + AP_CROUCH;
1549 	}
1550 	if ( bCurrentHeight == ANIM_PRONE && bDesiredHeight == ANIM_CROUCH )
1551 	{
1552 		sAPCost = AP_PRONE;
1553 	}
1554 
1555 	return( sAPCost );
1556 }
1557 
GetAPsToLook(const SOLDIERTYPE * pSoldier)1558 UINT16 GetAPsToLook(const SOLDIERTYPE* pSoldier)
1559 {
1560 	// Set # of APs
1561 	switch( gAnimControl[ pSoldier->usAnimState ].ubEndHeight )
1562 	{
1563 		// Now change to appropriate animation
1564 		case ANIM_STAND:  return AP_LOOK_STANDING;
1565 		case ANIM_CROUCH: return AP_LOOK_CROUCHED;
1566 
1567 		case ANIM_PRONE:
1568 			// 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
1569 			// crouched, turn, and then go back down.  Hence you go up (AP_PRONE), turn (AP_LOOK_PRONE) and down (AP_PRONE).
1570 			return( AP_LOOK_PRONE + AP_PRONE + AP_PRONE );
1571 
1572 		// no other values should be possible
1573 		default:
1574 			Assert( FALSE );
1575 			return(0);
1576 	}
1577 }
1578 
1579 
CheckForMercContMove(SOLDIERTYPE * const s)1580 BOOLEAN CheckForMercContMove(SOLDIERTYPE* const s)
1581 {
1582 	if (!(gTacticalStatus.uiFlags & INCOMBAT)) return FALSE;
1583 
1584 	if (gpItemPointer != NULL) return FALSE;
1585 
1586 	if (s->bLife < OKLIFE) return FALSE;
1587 
1588 	if (s->sGridNo == s->sFinalDestination && !s->bGoodContPath) return FALSE;
1589 
1590 	if (s != GetSelectedMan()) return FALSE;
1591 
1592 	if (!SoldierOnScreen(s)) return FALSE;
1593 
1594 	const INT16 sGridNo = (s->bGoodContPath ? s->sContPathLocation : s->sFinalDestination);
1595 	if (!FindBestPath(s, sGridNo, s->bLevel, s->usUIMovementMode, NO_COPYROUTE, 0)) return FALSE;
1596 
1597 	const INT16 sAPCost = PtsToMoveDirection(s, guiPathingData[0]);
1598 	if (!EnoughPoints(s, sAPCost, 0, FALSE)) return FALSE;
1599 
1600 	return TRUE;
1601 }
1602 
1603 
GetAPsToReadyWeapon(const SOLDIERTYPE * const pSoldier,const UINT16 usAnimState)1604 INT16 GetAPsToReadyWeapon(const SOLDIERTYPE* const pSoldier, const UINT16 usAnimState)
1605 {
1606 	UINT16 usItem;
1607 
1608 	// If this is a dwel pistol anim
1609 	// ATE: What was I thinking, hooking into animations like this....
1610 	//if ( usAnimState == READY_DUAL_STAND || usAnimState == READY_DUAL_CROUCH )
1611 	//{
1612 		//return( AP_READY_DUAL );
1613 	//}
1614 	if ( IsValidSecondHandShot( pSoldier ) )
1615 	{
1616 		return( AP_READY_DUAL );
1617 	}
1618 
1619 
1620 	// OK, now check type of weapon
1621 	usItem = pSoldier->inv[ HANDPOS ].usItem;
1622 
1623 	if ( usItem == NOTHING )
1624 	{
1625 		return( 0 );
1626 	}
1627 	else
1628 	{
1629 		// CHECK FOR RIFLE
1630 		if ( GCM->getItem(usItem)->getItemClass() == IC_GUN )
1631 		{
1632 			return( GCM->getWeapon( usItem )->ubReadyTime );
1633 		}
1634 	}
1635 
1636 	return( 0 );
1637 }
1638 
1639 
GetAPsToClimbRoof(SOLDIERTYPE * pSoldier,BOOLEAN fClimbDown)1640 INT8 GetAPsToClimbRoof( SOLDIERTYPE *pSoldier, BOOLEAN fClimbDown )
1641 {
1642 	if ( !fClimbDown )
1643 	{
1644 		// OK, add aps to goto stand stance...
1645 		return( (INT8)( AP_CLIMBROOF + GetAPsToChangeStance( pSoldier, ANIM_STAND ) ) );
1646 	}
1647 	else
1648 	{
1649 		// Add aps to goto crouch
1650 		return( (INT8)( AP_CLIMBOFFROOF + GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) ) );
1651 	}
1652 }
1653 
GetAPsToCutFence(SOLDIERTYPE * pSoldier)1654 INT8 GetAPsToCutFence( SOLDIERTYPE *pSoldier )
1655 {
1656 	// OK, it's normally just cost, but add some if different stance...
1657 	return(  GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) + AP_USEWIRECUTTERS );
1658 }
1659 
GetAPsToBeginFirstAid(SOLDIERTYPE * pSoldier)1660 INT8 GetAPsToBeginFirstAid( SOLDIERTYPE *pSoldier )
1661 {
1662 	// OK, it's normally just cost, but add some if different stance...
1663 	return(  GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) + AP_START_FIRST_AID );
1664 }
1665 
GetAPsToBeginRepair(SOLDIERTYPE * pSoldier)1666 INT8 GetAPsToBeginRepair( SOLDIERTYPE *pSoldier )
1667 {
1668 	// OK, it's normally just cost, but add some if different stance...
1669 	return(  GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) + AP_START_REPAIR );
1670 }
1671 
GetAPsToRefuelVehicle(SOLDIERTYPE * pSoldier)1672 INT8 GetAPsToRefuelVehicle( SOLDIERTYPE *pSoldier )
1673 {
1674 	// OK, it's normally just cost, but add some if different stance...
1675 	return( GetAPsToChangeStance( pSoldier, ANIM_CROUCH ) + AP_REFUEL_VEHICLE );
1676 }
1677 
1678 
1679 #define TOSSES_PER_10TURNS      18      // max # of grenades tossable in 10 turns
1680 #define AP_MIN_AIM_ATTACK       0       // minimum permitted extra aiming
1681 #define AP_MAX_AIM_ATTACK       4       // maximum permitted extra aiming
1682 
1683 
MinAPsToThrow(SOLDIERTYPE const & s,GridNo gridno,bool const add_turning_cost)1684 INT16 MinAPsToThrow(SOLDIERTYPE const& s, GridNo gridno, bool const add_turning_cost)
1685 {
1686 	INT32 ap = AP_MIN_AIM_ATTACK;
1687 
1688 	// Make sure the guy's actually got a throwable item in his hand
1689 	UINT16 const in_hand = s.inv[HANDPOS].usItem;
1690 	const ItemModel *item = GCM->getItem(in_hand);
1691 	if (!item)
1692 	{
1693 		SLOGW("MinAPsToThrow - in-hand item is missing");
1694 	}
1695 	else if (!(item->getItemClass() & (IC_GRENADE | IC_THROWN))) // match MinAPsToAttack
1696 	{
1697 		SLOGW("MinAPsToThrow - in-hand item '%s' has unexpected item class 0x%x", item->getInternalName().c_str(), item->getItemClass());
1698 	}
1699 
1700 	if (gridno != NOWHERE)
1701 	{
1702 		SOLDIERTYPE const* const tgt = FindSoldier(gridno, FIND_SOLDIER_GRIDNO);
1703 		if (tgt) gridno = tgt->sGridNo; // On a guy, get his gridno
1704 	}
1705 
1706 	// if attacking a new target (or if the specific target is uncertain)
1707 	if (gridno != s.sLastTarget) ap += AP_CHANGE_TARGET;
1708 
1709 	ap += GetAPsToChangeStance(&s, ANIM_STAND);
1710 
1711 	// Calculate default top & bottom of the magic "aiming" formula)
1712 
1713 	// Get this man's maximum possible action points (ignoring carryovers)
1714 	INT32 const full_ap = CalcActionPoints(&s);
1715 
1716 	// The 2 times is here only to around rounding off using integer math later
1717 	INT32 const top = 2 * full_ap;
1718 
1719 	// Tosses per turn is for max dexterity, drops down to 1/2 at dexterity = 0
1720 	INT32 const bottom = TOSSES_PER_10TURNS * (50 + s.bDexterity / 2) / 10;
1721 
1722 	// Add minimum aiming time to the overall minimum AP_cost
1723 	// This here ROUNDS UP fractions of 0.5 or higher using integer math
1724 	// This works because 'top' is 2x what it really should be throughout
1725 	ap += (100 * top / bottom + 1) / 2;
1726 
1727 	// The minimum AP cost of ANY throw can NEVER be more than merc has APs!
1728 	if (ap > full_ap) ap = full_ap;
1729 
1730 	// This SHOULD be impossible, but nevertheless
1731 	if (ap < 1) ap = 1;
1732 
1733 	return ap;
1734 }
1735 
1736 
GetAPsToDropBomb(SOLDIERTYPE * pSoldier)1737 UINT16 GetAPsToDropBomb( SOLDIERTYPE *pSoldier )
1738 {
1739 	return( AP_DROP_BOMB );
1740 }
1741 
1742 
GetTotalAPsToDropBomb(SOLDIERTYPE * const s,INT16 const sGridNo)1743 UINT16 GetTotalAPsToDropBomb(SOLDIERTYPE* const s, INT16 const sGridNo)
1744 {
1745 	return GetMovePlusActionAPCosts(s, sGridNo, AP_DROP_BOMB);
1746 }
1747 
1748 
1749 
GetAPsToUseRemote(SOLDIERTYPE * pSoldier)1750 UINT16 GetAPsToUseRemote( SOLDIERTYPE *pSoldier )
1751 {
1752 	return( AP_USE_REMOTE );
1753 }
1754 
1755 
GetAPsToStealItem(SOLDIERTYPE * pSoldier,INT16 usMapPos)1756 INT8 GetAPsToStealItem( SOLDIERTYPE *pSoldier, INT16 usMapPos )
1757 {
1758 	UINT16 sAPCost = PlotPath(pSoldier, usMapPos, NO_COPYROUTE, NO_PLOT, pSoldier->usUIMovementMode, pSoldier->bActionPoints);
1759 
1760 	// ADD APS TO PICKUP
1761 	sAPCost += AP_STEAL_ITEM;
1762 
1763 	// CJC August 13 2002: added cost to stand into equation
1764 	if (!(PTR_STANDING))
1765 	{
1766 		sAPCost += GetAPsToChangeStance( pSoldier, ANIM_STAND );
1767 	}
1768 
1769 	return( (INT8)sAPCost );
1770 
1771 }
1772 
GetAPsToUseJar(SOLDIERTYPE * pSoldier,INT16 usMapPos)1773 INT8 GetAPsToUseJar( SOLDIERTYPE *pSoldier, INT16 usMapPos )
1774 {
1775 	UINT16 sAPCost = PlotPath(pSoldier, usMapPos, NO_COPYROUTE, NO_PLOT, pSoldier->usUIMovementMode, pSoldier->bActionPoints);
1776 
1777 	// If point cost is zero, return 0
1778 	if ( sAPCost != 0 )
1779 	{
1780 		// ADD APS TO PICKUP
1781 		sAPCost += AP_TAKE_BLOOD;
1782 	}
1783 
1784 	return( (INT8)sAPCost );
1785 
1786 }
1787 
GetAPsToJumpOver(const SOLDIERTYPE * pSoldier)1788 INT8 GetAPsToJumpOver(const SOLDIERTYPE* pSoldier)
1789 {
1790 	return(  GetAPsToChangeStance( pSoldier, ANIM_STAND ) + AP_JUMP_OVER );
1791 }
1792 
GetAPsToJumpFence(const SOLDIERTYPE * pSoldier)1793 INT8 GetAPsToJumpFence(const SOLDIERTYPE* pSoldier)
1794 {
1795 	return(  GetAPsToChangeStance( pSoldier, ANIM_STAND ) + AP_JUMPFENCE );
1796 }
1797