Points.c 55 KB

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