1 #include "Handle_Items.h"
2 #include "Items.h"
3 #include "Soldier_Find.h"
4 #include "Structure.h"
5 #include "TileDef.h"
6 #include "Timer_Control.h"
7 #include "Weapons.h"
8 #include "Soldier_Control.h"
9 #include "Overhead.h"
10 #include "Handle_UI.h"
11 #include "Animation_Control.h"
12 #include "Points.h"
13 #include "Sound_Control.h"
14 #include "Isometric_Utils.h"
15 #include "Animation_Data.h"
16 #include "UI_Cursors.h"
17 #include "LOS.h"
18 #include "Interface.h"
19 #include "Cursors.h"
20 #include "Cursor_Control.h"
21 #include "Structure_Wrap.h"
22 #include "Physics.h"
23 #include "Soldier_Macros.h"
24 #include "Text.h"
25 #include "Interactive_Tiles.h"
26 #include "PathAI.h"
27 #include "Debug.h"
28 
29 #include "ContentManager.h"
30 #include "GameInstance.h"
31 
32 #include <string_theory/string>
33 
34 
35 // FUNCTIONS FOR ITEM CURSOR HANDLING
36 static UICursorID HandleActivatedTargetCursor(   SOLDIERTYPE*, GridNo map_pos, BOOLEAN recalc);
37 static UICursorID HandleNonActivatedTargetCursor(SOLDIERTYPE*, GridNo map_pos, BOOLEAN show_APs, BOOLEAN fRecalc, MouseMoveState);
38 static UICursorID HandleKnifeCursor(             SOLDIERTYPE*, GridNo map_pos, BOOLEAN activated, MouseMoveState);
39 static UICursorID HandlePunchCursor(             SOLDIERTYPE*, GridNo map_pos, BOOLEAN activated, MouseMoveState);
40 static UICursorID HandleAidCursor(               SOLDIERTYPE*, GridNo map_pos, BOOLEAN activated, MouseMoveState);
41 static UICursorID HandleActivatedTossCursor();
42 static UICursorID HandleNonActivatedTossCursor(  SOLDIERTYPE*, GridNo map_pos, BOOLEAN recalc, MouseMoveState, ItemCursor);
43 static UICursorID HandleWirecutterCursor(        SOLDIERTYPE*, GridNo map_pos, MouseMoveState);
44 static UICursorID HandleRepairCursor(            SOLDIERTYPE*, GridNo map_pos, MouseMoveState);
45 static UICursorID HandleRefuelCursor(            SOLDIERTYPE*, GridNo map_pos, MouseMoveState);
46 static UICursorID HandleRemoteCursor(            SOLDIERTYPE*, BOOLEAN activated, MouseMoveState);
47 static UICursorID HandleBombCursor(              SOLDIERTYPE*, GridNo map_pos, BOOLEAN activated, MouseMoveState);
48 static UICursorID HandleJarCursor(               SOLDIERTYPE*, GridNo map_pos, MouseMoveState);
49 static UICursorID HandleTinCanCursor(            SOLDIERTYPE*, GridNo map_pos, MouseMoveState);
50 
51 
52 static BOOLEAN gfCannotGetThrough = FALSE;
53 static BOOLEAN gfDisplayFullCountRing = FALSE;
54 
55 
GetMouseRecalcAndShowAPFlags(MouseMoveState * const puiCursorFlags,BOOLEAN * const pfShowAPs)56 BOOLEAN GetMouseRecalcAndShowAPFlags(MouseMoveState* const puiCursorFlags, BOOLEAN* const pfShowAPs)
57 {
58 	static bool do_new_tile = false;
59 
60 	// Set flags for certain mouse movements
61 	MouseMoveState const cursor_flags = GetCursorMovementFlags();
62 	bool                 recalc       = false;
63 
64 	// Force if we are currently cycling guys
65 	if (gfUIForceReExamineCursorData)
66 	{
67 		do_new_tile                  = true;
68 		recalc                       = true;
69 		gfUIForceReExamineCursorData = FALSE;
70 	}
71 
72 	bool show_APs = false;
73 	if (cursor_flags != MOUSE_STATIONARY)
74 	{
75 		// If cursor was previously stationary, make the additional check of grid
76 		// pos change
77 		RESETCOUNTER(PATHFINDCOUNTER);
78 		do_new_tile = true;
79 	}
80 	else if (COUNTERDONE(PATHFINDCOUNTER)) // Only dipslay aps after a delay
81 	{
82 		// Don't reset counter: One when we move again do we do this!
83 		show_APs = true;
84 
85 		if (do_new_tile)
86 		{
87 			do_new_tile = false;
88 			recalc      = true;
89 		}
90 	}
91 
92 	if (puiCursorFlags) *puiCursorFlags = cursor_flags;
93 	if (pfShowAPs)      *pfShowAPs      = show_APs;
94 	return recalc;
95 }
96 
97 
98 // Functions for cursor determination
GetProperItemCursor(SOLDIERTYPE * const s,GridNo const map_pos,BOOLEAN const activated)99 UICursorID GetProperItemCursor(SOLDIERTYPE* const s, GridNo const map_pos, BOOLEAN const activated)
100 {
101 	MouseMoveState cursor_flags;
102 	BOOLEAN        show_APs;
103 	BOOLEAN const  recalc = GetMouseRecalcAndShowAPFlags(&cursor_flags, &show_APs);
104 
105 	// ATE: Update attacking weapon!
106 	// CC has added this attackingWeapon stuff and I need to update it constantly
107 	// for CTGH algorithms
108 	if (gTacticalStatus.ubAttackBusyCount == 0)
109 	{
110 		UINT16 const in_hand = s->inv[HANDPOS].usItem;
111 		if (GCM->getItem(in_hand)->isWeapon()) s->usAttackingWeapon = in_hand;
112 	}
113 
114 	UICursorID               cursor      = NO_UICURSOR;
115 	SOLDIERTYPE const* const tgt         = gUIFullTarget;
116 	GridNo             const tgt_grid_no = tgt ? tgt->sGridNo : map_pos;
117 	ItemCursor         const item_cursor = GetActionModeCursor(s);
118 	switch (item_cursor)
119 	{
120 		case TARGETCURS:
121 			cursor =
122 				activated ? HandleActivatedTargetCursor(s, tgt_grid_no, recalc) :
123 				HandleNonActivatedTargetCursor(s, tgt_grid_no, show_APs, recalc, cursor_flags);
124 
125 			if (gCurrentUIMode == ACTION_MODE       &&
126 				gTacticalStatus.uiFlags & INCOMBAT  &&
127 				recalc                              &&
128 				tgt                                 &&
129 				IsValidTargetMerc(tgt)              &&
130 				EnoughAmmo(s, FALSE, HANDPOS)       && // ATE: Check for ammo
131 				guiUIFullTargetFlags & ENEMY_MERC   && // IF it's an ememy, goto confirm action mode
132 				guiUIFullTargetFlags & VISIBLE_MERC &&
133 				!(guiUIFullTargetFlags & DEAD_MERC) &&
134 				!gfCannotGetThrough)
135 			{
136 				guiPendingOverrideEvent = A_CHANGE_TO_CONFIM_ACTION;
137 			}
138 			break;
139 
140 		case TOSSCURS:
141 		case TRAJECTORYCURS:
142 			cursor = activated && gfUIHandlePhysicsTrajectory ? HandleActivatedTossCursor() :
143 					HandleNonActivatedTossCursor(s, tgt_grid_no, recalc, cursor_flags, item_cursor);
144 			break;
145 
146 		case PUNCHCURS:   cursor = HandlePunchCursor(     s, tgt_grid_no, activated, cursor_flags); break;
147 		case KNIFECURS:   cursor = HandleKnifeCursor(     s, tgt_grid_no, activated, cursor_flags); break;
148 		case AIDCURS:     cursor = HandleAidCursor(       s, map_pos,     activated, cursor_flags); break;
149 		case BOMBCURS:    cursor = HandleBombCursor(      s, tgt_grid_no, activated, cursor_flags); break;
150 		case REMOTECURS:  cursor = HandleRemoteCursor(    s,              activated, cursor_flags); break;
151 		case WIRECUTCURS: cursor = HandleWirecutterCursor(s, tgt_grid_no,            cursor_flags); break;
152 		case REPAIRCURS:  cursor = HandleRepairCursor(    s, tgt_grid_no,            cursor_flags); break;
153 		case JARCURS:     cursor = HandleJarCursor(       s, tgt_grid_no,            cursor_flags); break;
154 		case TINCANCURS:  cursor = HandleTinCanCursor(    s, tgt_grid_no,            cursor_flags); break;
155 		case REFUELCURS:  cursor = HandleRefuelCursor(    s, tgt_grid_no,            cursor_flags); break;
156 		case INVALIDCURS: cursor = INVALID_ACTION_UICURSOR; break;
157 		default:
158 			break;
159 	}
160 
161 	return cursor;
162 }
163 
164 
165 static void DetermineCursorBodyLocation(SOLDIERTYPE*, BOOLEAN fDisplay, BOOLEAN fRecalc);
166 
167 
HandleActivatedTargetCursor(SOLDIERTYPE * const s,GridNo const map_pos,BOOLEAN const recalc)168 static UICursorID HandleActivatedTargetCursor(SOLDIERTYPE* const s, GridNo const map_pos, BOOLEAN const recalc)
169 {
170 	bool const is_throwing_knife = GCM->getItem(s->inv[HANDPOS].usItem)->getItemClass() == IC_THROWING_KNIFE;
171 	if (is_throwing_knife)
172 	{
173 		// If we are in realtime, follow!
174 		if (!(gTacticalStatus.uiFlags & INCOMBAT) &&
175 			gAnimControl[s->usAnimState].uiFlags & ANIM_STATIONARY &&
176 			gUITargetShotWaiting)
177 		{
178 			guiPendingOverrideEvent = CA_MERC_SHOOT;
179 		}
180 	}
181 
182 	// Determine where we are shooting/aiming
183 	DetermineCursorBodyLocation(s, TRUE, TRUE);
184 
185 	bool enough_points       = true;
186 	bool max_point_limit_hit = false;
187 	if (gTacticalStatus.uiFlags & INCOMBAT)
188 	{
189 		gsCurrentActionPoints         = CalcTotalAPsToAttack(s, map_pos, TRUE, s->bShownAimTime / 2);
190 		gfUIDisplayActionPoints       = TRUE;
191 		gfUIDisplayActionPointsCenter = TRUE;
192 
193 		// If we don't have any points and we are at the first refine, do nothing but warn!
194 		if (!EnoughPoints(s, gsCurrentActionPoints, 0 , FALSE))
195 		{
196 			gfUIDisplayActionPointsInvalid = TRUE;
197 			max_point_limit_hit            = true;
198 		}
199 		else
200 		{
201 			UINT8 const future_aim = s->bShownAimTime + 2;
202 			if (future_aim <= REFINE_AIM_5)
203 			{
204 				INT16 const AP_costs = MinAPsToAttack(s, map_pos, TRUE) + future_aim / 2;
205 				if (!EnoughPoints(s, AP_costs, 0, FALSE))
206 				{
207 					enough_points = false;
208 				}
209 			}
210 		}
211 	}
212 
213 	if (!(gTacticalStatus.uiFlags & INCOMBAT) && COUNTERDONE(TARGETREFINE))
214 	{
215 		RESETCOUNTER(TARGETREFINE);
216 
217 		if (s->bDoBurst)
218 		{
219 			s->bShownAimTime = REFINE_AIM_BURST;
220 		}
221 		else
222 		{
223 			++s->bShownAimTime;
224 			if (s->bShownAimTime > REFINE_AIM_5)
225 			{
226 				s->bShownAimTime = REFINE_AIM_5;
227 			}
228 			else if (s->bShownAimTime % 2 != 0)
229 			{
230 				PlayJA2Sample(TARG_REFINE_BEEP, MIDVOLUME, 1, MIDDLEPAN);
231 			}
232 		}
233 	}
234 
235 	if (recalc)
236 	{
237 		SOLDIERTYPE const* const tgt    = gUIFullTarget;
238 		UINT8              const chance =
239 			tgt ? SoldierToSoldierBodyPartChanceToGetThrough(s, tgt, s->bAimShotLocation) :
240 			SoldierToLocationChanceToGetThrough(s, map_pos, gsInterfaceLevel, s->bTargetCubeLevel, 0);
241 		gfCannotGetThrough = chance < OK_CHANCE_TO_GET_THROUGH;
242 	}
243 
244 	UICursorID cursor = NO_UICURSOR;
245 	if (max_point_limit_hit)
246 	{
247 		// Check if we're in burst mode!
248 		cursor = s->bDoBurst       ? ACTION_TARGETREDBURST_UICURSOR :
249 			is_throwing_knife ? RED_THROW_UICURSOR             :
250 			ACTION_TARGETRED_UICURSOR;
251 	}
252 	else if (s->bDoBurst)
253 	{
254 		cursor = s->fDoSpread ? ACTION_TARGETREDBURST_UICURSOR :
255 			ACTION_TARGETCONFIRMBURST_UICURSOR;
256 	}
257 	else
258 	{
259 		switch (s->bShownAimTime)
260 		{
261 			case REFINE_AIM_1:
262 				if (is_throwing_knife)
263 				{
264 					cursor = gfDisplayFullCountRing ? ACTION_THROWAIMYELLOW1_UICURSOR :
265 						enough_points          ? ACTION_THROWAIM1_UICURSOR       :
266 						ACTION_THROWAIMCANT1_UICURSOR;
267 				}
268 				else
269 				{
270 					cursor = gfDisplayFullCountRing ? ACTION_TARGETAIMYELLOW1_UICURSOR :
271 						enough_points          ? ACTION_TARGETAIM1_UICURSOR       :
272 						ACTION_TARGETAIMCANT1_UICURSOR;
273 				}
274 				break;
275 
276 			case REFINE_AIM_2:
277 				if (is_throwing_knife)
278 				{
279 					cursor = gfDisplayFullCountRing ? ACTION_THROWAIMYELLOW2_UICURSOR :
280 						enough_points          ? ACTION_THROWAIM3_UICURSOR       :
281 						ACTION_THROWAIMCANT2_UICURSOR;
282 				}
283 				else
284 				{
285 					cursor = gfDisplayFullCountRing ? ACTION_TARGETAIMYELLOW2_UICURSOR :
286 						enough_points          ? ACTION_TARGETAIM3_UICURSOR       :
287 						ACTION_TARGETAIMCANT2_UICURSOR;
288 				}
289 				break;
290 
291 			case REFINE_AIM_3:
292 				if (is_throwing_knife)
293 				{
294 					cursor = gfDisplayFullCountRing ? ACTION_THROWAIMYELLOW3_UICURSOR :
295 						enough_points          ? ACTION_THROWAIM5_UICURSOR       :
296 						ACTION_THROWAIMCANT3_UICURSOR;
297 				}
298 				else
299 				{
300 					cursor = gfDisplayFullCountRing ? ACTION_TARGETAIMYELLOW3_UICURSOR :
301 						enough_points          ? ACTION_TARGETAIM5_UICURSOR       :
302 						ACTION_TARGETAIMCANT3_UICURSOR;
303 				}
304 				break;
305 
306 			case REFINE_AIM_4:
307 				if (is_throwing_knife)
308 				{
309 					cursor = gfDisplayFullCountRing ? ACTION_THROWAIMYELLOW4_UICURSOR :
310 						enough_points          ? ACTION_THROWAIM7_UICURSOR       :
311 						ACTION_THROWAIMCANT4_UICURSOR;
312 				}
313 				else
314 				{
315 					cursor = gfDisplayFullCountRing ? ACTION_TARGETAIMYELLOW4_UICURSOR :
316 						enough_points          ? ACTION_TARGETAIM7_UICURSOR       :
317 						ACTION_TARGETAIMCANT4_UICURSOR;
318 				}
319 				break;
320 
321 			case REFINE_AIM_5:
322 				if (is_throwing_knife)
323 				{
324 					cursor = gfDisplayFullCountRing ? ACTION_THROWAIMFULL_UICURSOR :
325 						enough_points          ? ACTION_THROWAIM9_UICURSOR    :
326 						ACTION_THROWAIMCANT5_UICURSOR;
327 				}
328 				else
329 				{
330 					cursor = gfDisplayFullCountRing ? ACTION_TARGETAIMFULL_UICURSOR :
331 						enough_points          ? ACTION_TARGETAIM9_UICURSOR    :
332 						ACTION_TARGETAIMCANT5_UICURSOR;
333 				}
334 				break;
335 
336 			case REFINE_AIM_MID1: cursor = ACTION_TARGETAIM2_UICURSOR; break;
337 			case REFINE_AIM_MID2: cursor = ACTION_TARGETAIM4_UICURSOR; break;
338 			case REFINE_AIM_MID3: cursor = ACTION_TARGETAIM6_UICURSOR; break;
339 			case REFINE_AIM_MID4: cursor = ACTION_TARGETAIM8_UICURSOR; break;
340 		}
341 	}
342 
343 	if (!max_point_limit_hit)
344 	{
345 		UINT16 const free_cursor_name = gUICursors[cursor].usFreeCursorName;
346 		RemoveCursorFlags(free_cursor_name, CURSOR_TO_FLASH | CURSOR_TO_PLAY_SOUND);
347 		if (gfCannotGetThrough)
348 		{
349 			SetCursorSpecialFrame(free_cursor_name, 1);
350 		}
351 		else if (!InRange(s, map_pos))
352 		{
353 			// OK, make buddy flash!
354 			SetCursorFlags(free_cursor_name, CURSOR_TO_FLASH | CURSOR_TO_PLAY_SOUND);
355 		}
356 		else
357 		{
358 			SetCursorSpecialFrame(free_cursor_name, 0);
359 		}
360 	}
361 
362 	return cursor;
363 }
364 
365 
HandleNonActivatedTargetCursor(SOLDIERTYPE * const s,GridNo const map_pos,BOOLEAN const show_APs,BOOLEAN const fRecalc,MouseMoveState const uiCursorFlags)366 static UICursorID HandleNonActivatedTargetCursor(SOLDIERTYPE* const s, GridNo const map_pos, BOOLEAN const show_APs, BOOLEAN const fRecalc, MouseMoveState const uiCursorFlags)
367 {
368 	bool const is_throwing_knife = GCM->getItem(s->inv[HANDPOS].usItem)->getItemClass() == IC_THROWING_KNIFE;
369 	if (!is_throwing_knife)
370 	{
371 		if (!(gTacticalStatus.uiFlags & INCOMBAT))
372 		{
373 			DetermineCursorBodyLocation(GetSelectedMan(), show_APs, fRecalc);
374 		}
375 
376 		if (!EnoughAmmo(s, FALSE, HANDPOS))
377 		{
378 			// Check if ANY ammo exists
379 			if (FindAmmoToReload(s, HANDPOS, NO_SLOT) == NO_SLOT) return BAD_RELOAD_UICURSOR;
380 
381 			gsCurrentActionPoints   = GetAPsToAutoReload(s);
382 			gfUIDisplayActionPoints = TRUE;
383 			return GOOD_RELOAD_UICURSOR;
384 		}
385 	}
386 
387 	if (gTacticalStatus.uiFlags & INCOMBAT)
388 	{
389 		DetermineCursorBodyLocation(GetSelectedMan(), show_APs, fRecalc);
390 
391 		gsCurrentActionPoints = CalcTotalAPsToAttack(s, map_pos, TRUE, s->bShownAimTime / 2);
392 
393 		gfUIDisplayActionPoints       = TRUE;
394 		gfUIDisplayActionPointsCenter = TRUE;
395 
396 		if (!show_APs)
397 		{
398 			gfUIDisplayActionPoints = FALSE;
399 		}
400 		else if (!EnoughPoints(s, gsCurrentActionPoints, 0 , FALSE))
401 		{
402 			gfUIDisplayActionPointsInvalid = TRUE;
403 		}
404 	}
405 
406 	if (fRecalc)
407 	{
408 		gfCannotGetThrough = SoldierToLocationChanceToGetThrough(s, map_pos, gsInterfaceLevel, s->bTargetCubeLevel, 0) < OK_CHANCE_TO_GET_THROUGH;
409 	}
410 
411 	// If we begin to move, reset the cursor
412 	if (uiCursorFlags != MOUSE_STATIONARY) gfCannotGetThrough = FALSE;
413 
414 	if (gfCannotGetThrough)
415 	{
416 		return s->bDoBurst       ? ACTION_NOCHANCE_BURST_UICURSOR :
417 			is_throwing_knife ? BAD_THROW_UICURSOR             :
418 			ACTION_NOCHANCE_SHOOT_UICURSOR;
419 	}
420 	else if (!InRange(s, map_pos))
421 	{
422 		// Flash cursor!
423 		return s->bDoBurst       ? ACTION_FLASH_BURST_UICURSOR :
424 			is_throwing_knife ? FLASH_THROW_UICURSOR        :
425 			ACTION_FLASH_SHOOT_UICURSOR;
426 	}
427 	else
428 	{
429 		return s->bDoBurst       ? ACTION_TARGETBURST_UICURSOR :
430 			is_throwing_knife ? GOOD_THROW_UICURSOR         :
431 			ACTION_SHOOT_UICURSOR;
432 	}
433 }
434 
435 
DetermineCursorBodyLocation(SOLDIERTYPE * const s,BOOLEAN const display,BOOLEAN const recalc)436 static void DetermineCursorBodyLocation(SOLDIERTYPE* const s, BOOLEAN const display, BOOLEAN const recalc)
437 {
438 	if (gTacticalStatus.ubAttackBusyCount > 0)
439 	{
440 		// ATE: Return if attacker busy count > 0, this helps in RT with re-setting
441 		// the flag to random
442 		return;
443 	}
444 
445 	if (recalc)
446 	{
447 		// Always set aim location to nothing
448 		s->bAimShotLocation = AIM_SHOT_RANDOM;
449 
450 		GridNo const map_pos = GetMouseMapPos();
451 		if (map_pos == NOWHERE) return;
452 
453 		SOLDIERTYPE* tgt = 0;
454 		UINT16       flags;
455 
456 		// Determine which body part it's on
457 		for (LEVELNODE* n = gpWorldLevelData[map_pos].pMercHead; n; n = n->pNext)
458 		{
459 			if (!(n->uiFlags & LEVELNODE_MERCPLACEHOLDER)) continue;
460 
461 			SOLDIERTYPE* const potential_tgt = n->pSoldier;
462 			if (!potential_tgt) continue;
463 
464 			// ATE: Check their stance - if prone - return!
465 			if (gAnimControl[potential_tgt->usAnimState].ubHeight == ANIM_PRONE)
466 			{
467 				return;
468 			}
469 
470 			// Check if we have a half tile profile
471 			flags = n->uiAnimHitLocationFlags;
472 			if (flags & (TILE_FLAG_NORTH_HALF | TILE_FLAG_SOUTH_HALF | TILE_FLAG_WEST_HALF |
473 				TILE_FLAG_EAST_HALF | TILE_FLAG_TOP_HALF | TILE_FLAG_BOTTOM_HALF))
474 			{
475 				INT16 sCellX;
476 				INT16 sCellY;
477 				GetMouseWorldCoords(&sCellX, &sCellY);
478 				// We are only interested in the sub-tile coordinates
479 				sCellX %= CELL_X_SIZE;
480 				sCellY %= CELL_Y_SIZE;
481 
482 				if (flags & TILE_FLAG_NORTH_HALF && sCellY >  CELL_Y_SIZE / 2) continue;
483 				if (flags & TILE_FLAG_SOUTH_HALF && sCellY <= CELL_Y_SIZE / 2) continue;
484 				if (flags & TILE_FLAG_WEST_HALF  && sCellX >  CELL_X_SIZE / 2) continue;
485 				if (flags & TILE_FLAG_EAST_HALF  && sCellX <= CELL_X_SIZE / 2) continue;
486 
487 				if (flags & TILE_FLAG_TOP_HALF)
488 				{
489 					INT16 sScreenX;
490 					INT16 sScreenY;
491 					FromCellToScreenCoordinates(sCellX, sCellY, &sScreenX, &sScreenY);
492 
493 					// Check for Below
494 					if (sScreenX > WORLD_TILE_Y / 2) continue;
495 				}
496 
497 				if (flags & TILE_FLAG_BOTTOM_HALF)
498 				{
499 					INT16 sScreenX;
500 					INT16 sScreenY;
501 					FromCellToScreenCoordinates(sCellX, sCellY, &sScreenX, &sScreenY);
502 
503 					// Check for Below
504 					if (sScreenX <= WORLD_TILE_Y / 2) continue;
505 				}
506 			}
507 
508 			// Check if mouse is in bounding box of soldier
509 			if (!IsPointInSoldierBoundingBox(potential_tgt, gusMouseXPos, gusMouseYPos))
510 			{
511 				continue;
512 			}
513 
514 			tgt = potential_tgt;
515 			break;
516 		}
517 
518 		if (!tgt)
519 		{
520 			// Check if we can find a soldier here
521 			SOLDIERTYPE* const potential_tgt = gUIFullTarget;
522 			if (potential_tgt)
523 			{
524 				flags = FindRelativeSoldierPosition(potential_tgt, gusMouseXPos, gusMouseYPos);
525 				if (flags != 0) tgt = potential_tgt;
526 			}
527 		}
528 
529 		if (tgt && IsValidTargetMerc(tgt))
530 		{
531 			if (flags & TILE_FLAG_FEET) s->bAimShotLocation = AIM_SHOT_LEGS;
532 			if (flags & TILE_FLAG_MID)  s->bAimShotLocation = AIM_SHOT_TORSO;
533 			if (flags & TILE_FLAG_HEAD) s->bAimShotLocation = AIM_SHOT_HEAD;
534 		}
535 	}
536 
537 	if (!display)    return;
538 	if (s->bDoBurst) return;
539 
540 	SOLDIERTYPE* const tgt = gUIFullTarget;
541 	if (!tgt) return;
542 
543 	ST::string hit_location;
544 	if (tgt->ubBodyType == CROW)
545 	{
546 		s->bAimShotLocation = AIM_SHOT_LEGS;
547 		hit_location = TacticalStr[CROW_HIT_LOCATION_STR];
548 	}
549 	else
550 	{
551 		if (!IS_MERC_BODY_TYPE(tgt)) return;
552 
553 		switch (s->bAimShotLocation)
554 		{
555 			case AIM_SHOT_HEAD:
556 				hit_location = // If we have a knife in hand, change string
557 					GCM->getItem(s->inv[HANDPOS].usItem)->getItemClass() == IC_BLADE ?
558 					TacticalStr[NECK_HIT_LOCATION_STR] :
559 					TacticalStr[HEAD_HIT_LOCATION_STR];
560 				break;
561 
562 			case AIM_SHOT_TORSO: hit_location = TacticalStr[TORSO_HIT_LOCATION_STR]; break;
563 			case AIM_SHOT_LEGS:  hit_location = TacticalStr[LEGS_HIT_LOCATION_STR];  break;
564 
565 			default: return;
566 		}
567 	}
568 	SetHitLocationText(hit_location);
569 }
570 
571 
HandleKnifeCursor(SOLDIERTYPE * const s,GridNo const map_pos,BOOLEAN const activated,MouseMoveState const uiCursorFlags)572 static UICursorID HandleKnifeCursor(SOLDIERTYPE* const s, GridNo const map_pos, BOOLEAN const activated, MouseMoveState const uiCursorFlags)
573 {
574 	HandleUIMovementCursor(s, uiCursorFlags, map_pos, MOVEUI_TARGET_MERCS);
575 
576 	if (activated)
577 	{
578 		DetermineCursorBodyLocation(s, TRUE, TRUE);
579 
580 		if (gfUIHandleShowMoveGrid) gfUIHandleShowMoveGrid = 2;
581 
582 		// Calculate action points
583 		bool enough_points = true;
584 		if (gTacticalStatus.uiFlags & INCOMBAT)
585 		{
586 			gsCurrentActionPoints         = CalcTotalAPsToAttack(s, map_pos, TRUE, s->bShownAimTime / 2);
587 			gfUIDisplayActionPoints       = TRUE;
588 			gfUIDisplayActionPointsCenter = TRUE;
589 
590 			// If we don't have any points and we are at the first refine, do nothing but warn!
591 			if (!EnoughPoints(s, gsCurrentActionPoints, 0, FALSE))
592 			{
593 				gfUIDisplayActionPointsInvalid = TRUE;
594 				if (s->bShownAimTime == REFINE_KNIFE_1) return KNIFE_HIT_UICURSOR;
595 			}
596 
597 			INT8  const future_aim = REFINE_KNIFE_2;
598 			INT16 const ap_costs   = CalcTotalAPsToAttack(s, map_pos, TRUE, future_aim / 2);
599 			if (!EnoughPoints(s, ap_costs, 0, FALSE)) enough_points = false;
600 		}
601 
602 		if (!(gTacticalStatus.uiFlags & INCOMBAT) && COUNTERDONE(NONGUNTARGETREFINE))
603 		{
604 			RESETCOUNTER(NONGUNTARGETREFINE);
605 
606 			if (s->bShownAimTime == REFINE_KNIFE_1)
607 			{
608 				PlayJA2Sample(TARG_REFINE_BEEP, MIDVOLUME, 1, MIDDLEPAN);
609 			}
610 
611 			s->bShownAimTime = REFINE_KNIFE_2;
612 		}
613 
614 		switch (s->bShownAimTime)
615 		{
616 			case REFINE_KNIFE_1:
617 				return gfDisplayFullCountRing ? KNIFE_YELLOW_AIM1_UICURSOR :
618 					enough_points          ? KNIFE_HIT_AIM1_UICURSOR    :
619 					KNIFE_NOGO_AIM1_UICURSOR;
620 
621 			case REFINE_KNIFE_2:
622 				return gfDisplayFullCountRing ? KNIFE_YELLOW_AIM2_UICURSOR :
623 					enough_points          ? KNIFE_HIT_AIM2_UICURSOR    :
624 					KNIFE_NOGO_AIM2_UICURSOR;
625 
626 			default:
627 				Assert(FALSE);
628 				// no return value!
629 				return NO_UICURSOR;
630 		}
631 	}
632 	else
633 	{
634 		gfUIDisplayActionPointsCenter = TRUE;
635 
636 		// Check if we are on a guy (who's not selected)!
637 		if (gUIFullTarget && !(guiUIFullTargetFlags & SELECTED_MERC))
638 		{
639 			DetermineCursorBodyLocation(s, TRUE, TRUE);
640 			return KNIFE_HIT_UICURSOR;
641 		}
642 		else
643 		{
644 			return KNIFE_REG_UICURSOR;
645 		}
646 	}
647 }
648 
649 
HandlePunchCursor(SOLDIERTYPE * const s,GridNo const map_pos,BOOLEAN const activated,MouseMoveState const uiCursorFlags)650 static UICursorID HandlePunchCursor(SOLDIERTYPE* const s, GridNo const map_pos, BOOLEAN const activated, MouseMoveState const uiCursorFlags)
651 {
652 	HandleUIMovementCursor(s, uiCursorFlags, map_pos, MOVEUI_TARGET_MERCS);
653 
654 	if (activated)
655 	{
656 		DetermineCursorBodyLocation(s, TRUE, TRUE);
657 
658 		if (gfUIHandleShowMoveGrid) gfUIHandleShowMoveGrid = 2;
659 
660 		// Calculate action points
661 		bool enough_points = true;
662 
663 		gsCurrentActionPoints         = CalcTotalAPsToAttack(s, map_pos, TRUE, s->bShownAimTime / 2);
664 		gfUIDisplayActionPoints       = TRUE;
665 		gfUIDisplayActionPointsCenter = TRUE;
666 
667 		// If we don't have any points and we are at the first refine, do nothing but warn!
668 		if (!EnoughPoints(s, gsCurrentActionPoints, 0, FALSE))
669 		{
670 			gfUIDisplayActionPointsInvalid = TRUE;
671 			if (s->bShownAimTime == REFINE_PUNCH_1) return ACTION_PUNCH_RED;
672 		}
673 
674 		INT8  const future_aim = REFINE_PUNCH_2;
675 		INT16 const ap_costs   = CalcTotalAPsToAttack(s, map_pos, TRUE, future_aim / 2);
676 		if (!EnoughPoints(s, ap_costs, 0, FALSE)) enough_points = false;
677 
678 		if (!(gTacticalStatus.uiFlags & INCOMBAT) && COUNTERDONE(NONGUNTARGETREFINE))
679 		{
680 			RESETCOUNTER(NONGUNTARGETREFINE);
681 
682 			if (s->bShownAimTime == REFINE_PUNCH_1)
683 			{
684 				PlayJA2Sample(TARG_REFINE_BEEP, MIDVOLUME, 1, MIDDLEPAN);
685 			}
686 
687 			s->bShownAimTime = REFINE_PUNCH_2;
688 		}
689 
690 		switch (s->bShownAimTime)
691 		{
692 			case REFINE_PUNCH_1:
693 				return gfDisplayFullCountRing ? ACTION_PUNCH_YELLOW_AIM1_UICURSOR :
694 					enough_points          ? ACTION_PUNCH_RED_AIM1_UICURSOR    :
695 					ACTION_PUNCH_NOGO_AIM1_UICURSOR;
696 
697 			case REFINE_PUNCH_2:
698 				return gfDisplayFullCountRing ? ACTION_PUNCH_YELLOW_AIM2_UICURSOR :
699 					enough_points          ? ACTION_PUNCH_RED_AIM2_UICURSOR    :
700 					ACTION_PUNCH_NOGO_AIM2_UICURSOR;
701 
702 			default:
703 				Assert(FALSE);
704 				// no return value!
705 				return NO_UICURSOR;
706 		}
707 	}
708 	else
709 	{
710 		gfUIDisplayActionPointsCenter = TRUE;
711 
712 		// Check if we are on a guy (who's not selected)!
713 		if (gUIFullTarget && !(guiUIFullTargetFlags & SELECTED_MERC))
714 		{
715 			DetermineCursorBodyLocation(s, TRUE, TRUE);
716 			return ACTION_PUNCH_RED;
717 		}
718 		else
719 		{
720 			return ACTION_PUNCH_GRAY;
721 		}
722 	}
723 }
724 
725 
HandleAidCursor(SOLDIERTYPE * const s,GridNo const map_pos,BOOLEAN const activated,MouseMoveState const uiCursorFlags)726 static UICursorID HandleAidCursor(SOLDIERTYPE* const s, GridNo const map_pos, BOOLEAN const activated, MouseMoveState const uiCursorFlags)
727 {
728 	HandleUIMovementCursor(s, uiCursorFlags, map_pos, MOVEUI_TARGET_MERCSFORAID);
729 	return activated || gUIFullTarget ? ACTION_FIRSTAID_RED : ACTION_FIRSTAID_GRAY;
730 }
731 
732 
HandleActivatedTossCursor()733 static UICursorID HandleActivatedTossCursor()
734 {
735 	return ACTION_TOSS_UICURSOR;
736 }
737 
738 
HandleNonActivatedTossCursor(SOLDIERTYPE * const s,GridNo const map_pos,BOOLEAN const recalc,MouseMoveState const uiCursorFlags,ItemCursor const ubItemCursor)739 static UICursorID HandleNonActivatedTossCursor(SOLDIERTYPE* const s, GridNo const map_pos, BOOLEAN const recalc, MouseMoveState const uiCursorFlags, ItemCursor const ubItemCursor)
740 {
741 	static bool bad_ctgh = false;
742 
743 	// Check for enough ammo
744 	BOOLEAN armed = FALSE;
745 	if (ubItemCursor == TRAJECTORYCURS)
746 	{
747 		if (!EnoughAmmo(s, FALSE, HANDPOS))
748 		{
749 			// Check if ANY ammo exists
750 			if (FindAmmoToReload(s, HANDPOS, NO_SLOT) == NO_SLOT) return BAD_RELOAD_UICURSOR;
751 
752 			gsCurrentActionPoints   = GetAPsToAutoReload(s);
753 			gfUIDisplayActionPoints = TRUE;
754 			return GOOD_RELOAD_UICURSOR;
755 		}
756 
757 		armed = TRUE;
758 	}
759 
760 	// Add APs
761 	if (gTacticalStatus.uiFlags & INCOMBAT)
762 	{
763 		gsCurrentActionPoints =
764 			ubItemCursor == TRAJECTORYCURS ? CalcTotalAPsToAttack(s, map_pos, TRUE, s->bShownAimTime / 2) :
765 			MinAPsToThrow(*s, map_pos, TRUE);
766 
767 		gfUIDisplayActionPoints       = TRUE;
768 		gfUIDisplayActionPointsCenter = TRUE;
769 
770 		// If we don't have any points and we are at the first refine, do nothing
771 		// but warn!
772 		if (!EnoughPoints(s, gsCurrentActionPoints, 0, FALSE))
773 		{
774 			gfUIDisplayActionPointsInvalid = TRUE;
775 		}
776 	}
777 
778 	// If we begin to move, reset the cursor
779 	if (uiCursorFlags != MOUSE_STATIONARY) EndPhysicsTrajectoryUI();
780 
781 	gfUIHandlePhysicsTrajectory = TRUE;
782 
783 	if (recalc)
784 	{
785 		// Calculate chance to throw here
786 		if (map_pos == s->sGridNo)
787 		{
788 			bad_ctgh = false;
789 		}
790 		else
791 		{
792 			OBJECTTYPE const& o = s->inv[HANDPOS];
793 			// ATE: Find the object to use
794 			OBJECTTYPE TempObject = o;
795 
796 			// Do we have a launchable?
797 			for (INT8 i = 0; i != MAX_ATTACHMENTS; ++i)
798 			{
799 				UINT16 const attach_item = o.usAttachItem[i];
800 				if (attach_item == NOTHING) continue;
801 				if (!(GCM->getItem(attach_item)->isExplosive())) continue;
802 				CreateItem(attach_item, o.bAttachStatus[i], &TempObject);
803 				break;
804 			}
805 
806 			if (s->bWeaponMode == WM_ATTACHED)
807 			{
808 				INT8 const slot = FindAttachment(&o, UNDER_GLAUNCHER);
809 				if (slot != NO_SLOT)
810 				{
811 					CreateItem(UNDER_GLAUNCHER, o.bAttachStatus[slot], &TempObject);
812 				}
813 			}
814 
815 			INT16 final_grid_no;
816 			INT8  level;
817 			bad_ctgh = !CalculateLaunchItemChanceToGetThrough(s, &TempObject, map_pos, gsInterfaceLevel, gsInterfaceLevel * 256, &final_grid_no, armed, &level, TRUE);
818 			BeginPhysicsTrajectoryUI(final_grid_no, level, bad_ctgh);
819 		}
820 	}
821 
822 	return bad_ctgh ? BAD_THROW_UICURSOR : GOOD_THROW_UICURSOR;
823 }
824 
825 
HandleWirecutterCursor(SOLDIERTYPE * const s,GridNo const map_pos,MouseMoveState const uiCursorFlags)826 static UICursorID HandleWirecutterCursor(SOLDIERTYPE* const s, GridNo const map_pos, MouseMoveState const uiCursorFlags)
827 {
828 	HandleUIMovementCursor(s, uiCursorFlags, map_pos, MOVEUI_TARGET_WIREFENCE);
829 	return s->bLevel == 0 && IsCuttableWireFenceAtGridNo(map_pos) ? GOOD_WIRECUTTER_UICURSOR :
830 		BAD_WIRECUTTER_UICURSOR;
831 }
832 
833 
HandleRepairCursor(SOLDIERTYPE * const s,GridNo const map_pos,MouseMoveState const uiCursorFlags)834 static UICursorID HandleRepairCursor(SOLDIERTYPE* const s, GridNo const map_pos, MouseMoveState const uiCursorFlags)
835 {
836 	HandleUIMovementCursor(s, uiCursorFlags, map_pos, MOVEUI_TARGET_REPAIR);
837 	return s->bLevel == 0 && IsRepairableStructAtGridNo(map_pos, 0) ? GOOD_REPAIR_UICURSOR :
838 		BAD_REPAIR_UICURSOR;
839 }
840 
841 
HandleRefuelCursor(SOLDIERTYPE * const s,GridNo const map_pos,MouseMoveState const uiCursorFlags)842 static UICursorID HandleRefuelCursor(SOLDIERTYPE* const s, GridNo const map_pos, MouseMoveState const uiCursorFlags)
843 {
844 	HandleUIMovementCursor(s, uiCursorFlags, map_pos, MOVEUI_TARGET_REFUEL);
845 	return s->bLevel == 0 && GetRefuelableStructAtGridNo(map_pos) ? REFUEL_RED_UICURSOR :
846 		REFUEL_GREY_UICURSOR;
847 }
848 
849 
HandleJarCursor(SOLDIERTYPE * const s,GridNo const map_pos,MouseMoveState const uiCursorFlags)850 static UICursorID HandleJarCursor(SOLDIERTYPE* const s, GridNo const map_pos, MouseMoveState const uiCursorFlags)
851 {
852 	HandleUIMovementCursor(s, uiCursorFlags, map_pos, MOVEUI_TARGET_JAR);
853 	return IsCorpseAtGridNo(map_pos, s->bLevel) ? GOOD_JAR_UICURSOR :
854 		BAD_JAR_UICURSOR;
855 }
856 
857 
HandleTinCanCursor(SOLDIERTYPE * const s,GridNo const map_pos,MouseMoveState const uiCursorFlags)858 static UICursorID HandleTinCanCursor(SOLDIERTYPE* const s, GridNo const map_pos, MouseMoveState const uiCursorFlags)
859 {
860 	HandleUIMovementCursor(s, uiCursorFlags, map_pos, MOVEUI_TARGET_CAN);
861 
862 	// Check if a door exists here
863 	STRUCTURE*       structure;
864 	INT16            int_tile_grid_no;
865 	LEVELNODE* const int_tile = GetCurInteractiveTileGridNoAndStructure(&int_tile_grid_no, &structure);
866 	return int_tile && structure->fFlags & STRUCTURE_ANYDOOR ? PLACE_TINCAN_GREY_UICURSOR :
867 		PLACE_TINCAN_RED_UICURSOR;
868 }
869 
870 
HandleRemoteCursor(SOLDIERTYPE * const s,BOOLEAN const activated,MouseMoveState const uiCursorFlags)871 static UICursorID HandleRemoteCursor(SOLDIERTYPE* const s, BOOLEAN const activated, MouseMoveState const uiCursorFlags)
872 {
873 	if (gTacticalStatus.uiFlags & INCOMBAT)
874 	{
875 		gsCurrentActionPoints         = GetAPsToUseRemote(s);
876 		gfUIDisplayActionPoints       = TRUE;
877 		gfUIDisplayActionPointsCenter = TRUE;
878 
879 		// If we don't have any points and we are at the first refine, do nothing but warn!
880 		if (!EnoughPoints(s, gsCurrentActionPoints, 0, FALSE))
881 		{
882 			gfUIDisplayActionPointsInvalid = TRUE;
883 		}
884 	}
885 
886 	return activated ? PLACE_REMOTE_RED_UICURSOR : PLACE_REMOTE_GREY_UICURSOR;
887 }
888 
889 
HandleBombCursor(SOLDIERTYPE * const s,GridNo const map_pos,BOOLEAN const activated,MouseMoveState const uiCursorFlags)890 static UICursorID HandleBombCursor(SOLDIERTYPE* const s, GridNo const map_pos, BOOLEAN const activated, MouseMoveState const uiCursorFlags)
891 {
892 	HandleUIMovementCursor(s, uiCursorFlags, map_pos, MOVEUI_TARGET_BOMB);
893 
894 	if (gTacticalStatus.uiFlags & INCOMBAT)
895 	{
896 		gsCurrentActionPoints         = GetTotalAPsToDropBomb(s, map_pos);
897 		gfUIDisplayActionPoints       = TRUE;
898 		gfUIDisplayActionPointsCenter = TRUE;
899 
900 		// If we don't have any points and we are at the first refine, do nothing but warn!
901 		if (!EnoughPoints(s, gsCurrentActionPoints, 0, FALSE))
902 		{
903 			gfUIDisplayActionPointsInvalid = TRUE;
904 		}
905 	}
906 
907 	return activated ? PLACE_BOMB_RED_UICURSOR : PLACE_BOMB_GREY_UICURSOR;
908 }
909 
910 
HandleLeftClickCursor(SOLDIERTYPE * pSoldier)911 void HandleLeftClickCursor( SOLDIERTYPE *pSoldier )
912 {
913 	ItemCursor const ubItemCursor = GetActionModeCursor(pSoldier);
914 
915 	// OK, if we are i realtime.. goto directly to shoot
916 	if (!(gTacticalStatus.uiFlags & INCOMBAT) &&
917 		ubItemCursor != TOSSCURS && ubItemCursor != TRAJECTORYCURS)
918 	{
919 		// GOTO DIRECTLY TO USING ITEM
920 		// ( only if not burst mode.. )
921 		if ( !pSoldier->bDoBurst )
922 		{
923 			guiPendingOverrideEvent = CA_MERC_SHOOT;
924 		}
925 		return;
926 	}
927 
928 	const GridNo sGridNo = GetMouseMapPos();
929 	if (sGridNo == NOWHERE) return;
930 
931 	gfUIForceReExamineCursorData = TRUE;
932 
933 	gfDisplayFullCountRing = FALSE;
934 
935 	switch( ubItemCursor )
936 	{
937 		case TARGETCURS:
938 			pSoldier->bShownAimTime = REFINE_AIM_1;
939 
940 			// Reset counter
941 			RESETCOUNTER( TARGETREFINE );
942 			break;
943 
944 		case PUNCHCURS:
945 			pSoldier->bShownAimTime = REFINE_PUNCH_1;
946 
947 			// Reset counter
948 			RESETCOUNTER( NONGUNTARGETREFINE );
949 			break;
950 
951 
952 		case KNIFECURS:
953 			pSoldier->bShownAimTime = REFINE_KNIFE_1;
954 
955 			// Reset counter
956 			RESETCOUNTER( NONGUNTARGETREFINE );
957 			break;
958 
959 		default:
960 			// GOTO DIRECTLY TO USING ITEM
961 			guiPendingOverrideEvent = CA_MERC_SHOOT;
962 	}
963 }
964 
965 
966 
967 
HandleRightClickAdjustCursor(SOLDIERTYPE * pSoldier,INT16 usMapPos)968 void HandleRightClickAdjustCursor( SOLDIERTYPE *pSoldier, INT16 usMapPos )
969 {
970 	INT16 sAPCosts;
971 	INT8  bFutureAim;
972 	INT16 sGridNo;
973 	INT8  bTargetLevel;
974 
975 	ItemCursor const ubCursor = GetActionModeCursor(pSoldier);
976 
977 	// 'snap' cursor to target tile....
978 	if (gUIFullTarget != NULL) usMapPos = gUIFullTarget->sGridNo;
979 
980 	switch( ubCursor )
981 	{
982 		case TARGETCURS:
983 
984 			// CHECK IF GUY HAS IN HAND A WEAPON
985 			if ( pSoldier->bDoBurst )
986 			{
987 				// Do nothing!
988 				// pSoldier->bShownAimTime = REFINE_AIM_BURST;
989 			}
990 			else
991 			{
992 				sGridNo = usMapPos;
993 				bTargetLevel = (INT8)gsInterfaceLevel;
994 
995 				// Look for a target here...
996 				const SOLDIERTYPE* const tgt = gUIFullTarget;
997 				if (tgt != NULL)
998 				{
999 					// Get target soldier, if one exists
1000 					sGridNo = tgt->sGridNo;
1001 					bTargetLevel = pSoldier->bLevel;
1002 
1003 					if (!HandleCheckForBadChangeToGetThrough(pSoldier, tgt, sGridNo, bTargetLevel))
1004 					{
1005 						return;
1006 					}
1007 				}
1008 
1009 				bFutureAim = (INT8)( pSoldier->bShownAimTime + 2 );
1010 
1011 				if ( bFutureAim <= REFINE_AIM_5 )
1012 				{
1013 					sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, (INT8)(bFutureAim / 2) );
1014 
1015 					// Determine if we can afford!
1016 					if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) )
1017 					{
1018 						pSoldier->bShownAimTime+= 2;
1019 						if ( pSoldier->bShownAimTime > REFINE_AIM_5 )
1020 						{
1021 							pSoldier->bShownAimTime = REFINE_AIM_5;
1022 						}
1023 					}
1024 					// Else - goto first level!
1025 					else
1026 					{
1027 						if ( !gfDisplayFullCountRing )
1028 						{
1029 							gfDisplayFullCountRing = TRUE;
1030 						}
1031 						else
1032 						{
1033 							pSoldier->bShownAimTime = REFINE_AIM_1;
1034 							gfDisplayFullCountRing = FALSE;
1035 						}
1036 					}
1037 				}
1038 				else
1039 				{
1040 					if ( !gfDisplayFullCountRing )
1041 					{
1042 						gfDisplayFullCountRing = TRUE;
1043 					}
1044 					else
1045 					{
1046 						pSoldier->bShownAimTime = REFINE_AIM_1;
1047 						gfDisplayFullCountRing = FALSE;
1048 					}
1049 				}
1050 			}
1051 			break;
1052 
1053 
1054 		case PUNCHCURS:
1055 
1056 			bFutureAim = (INT8)( pSoldier->bShownAimTime + REFINE_PUNCH_2 );
1057 
1058 			if ( bFutureAim <= REFINE_PUNCH_2 )
1059 			{
1060 				sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, (INT8)(bFutureAim / 2) );
1061 
1062 				// Determine if we can afford!
1063 				if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) )
1064 				{
1065 					pSoldier->bShownAimTime+= REFINE_PUNCH_2;
1066 
1067 					if ( pSoldier->bShownAimTime > REFINE_PUNCH_2 )
1068 					{
1069 						pSoldier->bShownAimTime = REFINE_PUNCH_2;
1070 					}
1071 				}
1072 				// Else - goto first level!
1073 				else
1074 				{
1075 					if ( !gfDisplayFullCountRing )
1076 					{
1077 						gfDisplayFullCountRing = TRUE;
1078 					}
1079 					else
1080 					{
1081 						pSoldier->bShownAimTime = REFINE_PUNCH_1;
1082 						gfDisplayFullCountRing = FALSE;
1083 					}
1084 				}
1085 			}
1086 			else
1087 			{
1088 				if ( !gfDisplayFullCountRing )
1089 				{
1090 					gfDisplayFullCountRing = TRUE;
1091 				}
1092 				else
1093 				{
1094 					pSoldier->bShownAimTime = REFINE_PUNCH_1;
1095 					gfDisplayFullCountRing = FALSE;
1096 				}
1097 			}
1098 			break;
1099 
1100 
1101 		case KNIFECURS:
1102 
1103 			bFutureAim = (INT8)( pSoldier->bShownAimTime + REFINE_KNIFE_2 );
1104 
1105 			if ( bFutureAim <= REFINE_KNIFE_2 )
1106 			{
1107 				sAPCosts = CalcTotalAPsToAttack( pSoldier, usMapPos, TRUE, (INT8)(bFutureAim / 2) );
1108 
1109 				// Determine if we can afford!
1110 				if ( EnoughPoints( pSoldier, sAPCosts, 0, FALSE ) )
1111 				{
1112 					pSoldier->bShownAimTime+= REFINE_KNIFE_2;
1113 
1114 					if ( pSoldier->bShownAimTime > REFINE_KNIFE_2 )
1115 					{
1116 						pSoldier->bShownAimTime = REFINE_KNIFE_2;
1117 					}
1118 				}
1119 				// Else - goto first level!
1120 				else
1121 				{
1122 					if ( !gfDisplayFullCountRing )
1123 					{
1124 						gfDisplayFullCountRing = TRUE;
1125 					}
1126 					else
1127 					{
1128 						pSoldier->bShownAimTime = REFINE_KNIFE_1;
1129 						gfDisplayFullCountRing = FALSE;
1130 					}
1131 				}
1132 			}
1133 			else
1134 			{
1135 				if ( !gfDisplayFullCountRing )
1136 				{
1137 					gfDisplayFullCountRing = TRUE;
1138 				}
1139 				else
1140 				{
1141 					pSoldier->bShownAimTime = REFINE_KNIFE_1;
1142 					gfDisplayFullCountRing = FALSE;
1143 				}
1144 			}
1145 			break;
1146 
1147 		case TOSSCURS: break;
1148 
1149 		default:
1150 
1151 			ErasePath();
1152 
1153 	}
1154 
1155 }
1156 
1157 
GetActionModeCursor(SOLDIERTYPE const * const pSoldier)1158 ItemCursor GetActionModeCursor(SOLDIERTYPE const* const pSoldier)
1159 {
1160 	UINT16 usInHand;
1161 
1162 	// If we are an EPC, do nothing....
1163 	//if ( ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
1164 	//{
1165 	//	return( INVALIDCURS );
1166 	//}
1167 
1168 	// AN EPC is always not - attackable unless they are a robot!
1169 	if ( AM_AN_EPC( pSoldier ) && !( pSoldier->uiStatusFlags & SOLDIER_ROBOT ) )
1170 	{
1171 		return( INVALIDCURS );
1172 	}
1173 
1174 	// ATE: if a vehicle.... same thing
1175 	if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
1176 	{
1177 		return( INVALIDCURS );
1178 	}
1179 
1180 	// If we can't be controlled, returninvalid...
1181 	if ( pSoldier->uiStatusFlags & SOLDIER_ROBOT )
1182 	{
1183 		if ( !CanRobotBeControlled( pSoldier ) )
1184 		{
1185 			// Display message that robot cannot be controlled....
1186 			return( INVALIDCURS );
1187 		}
1188 	}
1189 
1190 
1191 	// If we are in attach shoot mode, use toss cursor...
1192 	if (pSoldier->bWeaponMode == WM_ATTACHED )
1193 	{
1194 		return( TRAJECTORYCURS );
1195 	}
1196 
1197 	usInHand = pSoldier->inv[HANDPOS].usItem;
1198 
1199 	// Start off with what is in our hand
1200 	ItemCursor ubCursor = GCM->getItem(usInHand)->getCursor();
1201 
1202 	// OK, check if what is in our hands has a detonator attachment...
1203 	// Detonators can only be on invalidcurs things...
1204 	if ( ubCursor == INVALIDCURS )
1205 	{
1206 		if ( FindAttachment( &(pSoldier->inv[HANDPOS]), DETONATOR) != ITEM_NOT_FOUND )
1207 		{
1208 			ubCursor = BOMBCURS;
1209 		}
1210 		else if ( FindAttachment( &(pSoldier->inv[HANDPOS]), REMDETONATOR) != ITEM_NOT_FOUND )
1211 		{
1212 			ubCursor = BOMBCURS;
1213 		}
1214 	}
1215 
1216 	// Now check our terrain to see if we cannot do the action now...
1217 	if ( pSoldier->bOverTerrainType == DEEP_WATER )
1218 	{
1219 		ubCursor = INVALIDCURS;
1220 	}
1221 
1222 	// If we are out of breath, no cursor...
1223 	if ( pSoldier->bBreath < OKBREATH && pSoldier->bCollapsed )
1224 	{
1225 		ubCursor = INVALIDCURS;
1226 	}
1227 
1228 	return( ubCursor );
1229 }
1230 
1231 // Switch on item, display appropriate feedback cursor for a click....
HandleUICursorRTFeedback(SOLDIERTYPE * pSoldier)1232 void HandleUICursorRTFeedback( SOLDIERTYPE *pSoldier )
1233 {
1234 	ItemCursor const ubItemCursor = GetActionModeCursor(pSoldier);
1235 	switch( ubItemCursor )
1236 	{
1237 		case TARGETCURS:
1238 
1239 			if ( pSoldier->bDoBurst )
1240 			{
1241 				//BeginDisplayTimedCursor( ACTION_TARGETREDBURST_UICURSOR, 500 );
1242 			}
1243 			else
1244 			{
1245 				if ( GCM->getItem(pSoldier->inv[ HANDPOS ].usItem)->getItemClass() == IC_THROWING_KNIFE )
1246 				{
1247 					BeginDisplayTimedCursor( RED_THROW_UICURSOR, 500 );
1248 				}
1249 				else
1250 				{
1251 					BeginDisplayTimedCursor( ACTION_TARGETRED_UICURSOR, 500 );
1252 				}
1253 			}
1254 			break;
1255 
1256 		default:
1257 
1258 			break;
1259 	}
1260 
1261 }
1262