1 #include "Directories.h"
2 #include "Font.h"
3 #include "Isometric_Utils.h"
4 #include "Local.h"
5 #include "HImage.h"
6 #include "MapScreen.h"
7 #include "Soldier_Find.h"
8 #include "TileDef.h"
9 #include "Timer_Control.h"
10 #include "VObject.h"
11 #include "SysUtil.h"
12 #include "Overhead.h"
13 #include "MouseSystem.h"
14 #include "Button_System.h"
15 #include "Interface.h"
16 #include "VSurface.h"
17 #include "Input.h"
18 #include "Handle_UI.h"
19 #include "Animation_Control.h"
20 #include "Animation_Data.h"
21 #include "RenderWorld.h"
22 #include "Cursors.h"
23 #include "Radar_Screen.h"
24 #include "WorldMan.h"
25 #include "Font_Control.h"
26 #include "Render_Dirty.h"
27 #include "Interface_Cursors.h"
28 #include "Sound_Control.h"
29 #include "Interface_Panels.h"
30 #include "PathAI.h"
31 #include "Faces.h"
32 #include "Interface_Control.h"
33 #include "Interface_Items.h"
34 #include "Soldier_Profile.h"
35 #include "MercTextBox.h"
36 #include "Soldier_Functions.h"
37 #include "Cursor_Control.h"
38 #include "Handle_Doors.h"
39 #include "Keys.h"
40 #include "Text.h"
41 #include "Points.h"
42 #include "Soldier_Macros.h"
43 #include "Game_Clock.h"
44 #include "Map_Screen_Interface_Map.h"
45 #include "Line.h"
46 #include "Vehicles.h"
47 #include "GameSettings.h"
48 #include "Squads.h"
49 #include "Message.h"
50 #include "Debug.h"
51 #include "Video.h"
52 #include "Items.h"
53 #include "GameScreen.h"
54 #include "MercProfile.h"
55 
56 #include "UILayout.h"
57 
58 #include "ContentManager.h"
59 #include "GameInstance.h"
60 
61 #include <string_theory/format>
62 #include <string_theory/string>
63 
64 #include <stdexcept>
65 
66 
67 #define ARROWS_X_OFFSET					10
68 #define ARROWS_HEIGHT						20
69 #define ARROWS_WIDTH						20
70 #define UPARROW_Y_OFFSET					-30
71 #define DOWNARROW_Y_OFFSET					-10
72 
73 #define BUTTON_PANEL_WIDTH					78
74 #define BUTTON_PANEL_HEIGHT					76
75 
76 
77 BOOLEAN	gfInMovementMenu = FALSE;
78 static INT32 giMenuAnchorX;
79 static INT32 giMenuAnchorY;
80 
81 static BOOLEAN gfProgBarActive   = FALSE;
82 static UINT8   gubProgNumEnemies = 0;
83 static UINT8   gubProgCurEnemy   = 0;
84 
85 struct TOP_MESSAGE
86 {
87 	SGPVSurface* uiSurface;
88 	UINT32  uiTimeSinceLastBeep;
89 	BOOLEAN fCreated;
90 	INT16   sWorldRenderX;
91 	INT16   sWorldRenderY;
92 };
93 
94 static TOP_MESSAGE gTopMessage;
95 BOOLEAN gfTopMessageDirty = FALSE;
96 
97 
98 static MOUSE_REGION gMenuOverlayRegion;
99 
100 
101 VIDEO_OVERLAY*       g_ui_message_overlay = NULL;
102 static UINT16        gusUIMessageWidth;
103 static UINT16        gusUIMessageHeight;
104 UINT32 guiUIMessageTime = 0;
105 static MercPopUpBox* g_ui_message_box;
106 UINT32 guiUIMessageTimeDelay = 0;
107 static BOOLEAN       gfUseSkullIconMessage = FALSE;
108 
109 static BOOLEAN gfPanelAllocated = FALSE;
110 
111 
112 enum
113 {
114 	WALK_IMAGES = 0,
115 	SNEAK_IMAGES,
116 	RUN_IMAGES,
117 	CRAWL_IMAGES,
118 	LOOK_IMAGES,
119 	TALK_IMAGES,
120 	HAND_IMAGES,
121 	CANCEL_IMAGES,
122 
123 	TARGETACTIONC_IMAGES,
124 	KNIFEACTIONC_IMAGES,
125 	AIDACTIONC_IMAGES,
126 	PUNCHACTIONC_IMAGES,
127 	BOMBACTIONC_IMAGES,
128 
129 	OPEN_DOOR_IMAGES,
130 	EXAMINE_DOOR_IMAGES,
131 	LOCKPICK_DOOR_IMAGES,
132 	BOOT_DOOR_IMAGES,
133 	CROWBAR_DOOR_IMAGES,
134 	USE_KEY_IMAGES,
135 	USE_KEYRING_IMAGES,
136 	EXPLOSIVE_DOOR_IMAGES,
137 
138 	TOOLKITACTIONC_IMAGES,
139 	WIRECUTACTIONC_IMAGES,
140 
141 	NUM_ICON_IMAGES
142 };
143 
144 static BUTTON_PICS* iIconImages[NUM_ICON_IMAGES];
145 
146 enum
147 {
148 	WALK_ICON,
149 	SNEAK_ICON,
150 	RUN_ICON,
151 	CRAWL_ICON,
152 	LOOK_ICON,
153 	ACTIONC_ICON,
154 	TALK_ICON,
155 	HAND_ICON,
156 
157 	OPEN_DOOR_ICON,
158 	EXAMINE_DOOR_ICON,
159 	LOCKPICK_DOOR_ICON,
160 	BOOT_DOOR_ICON,
161 	UNTRAP_DOOR_ICON,
162 	USE_KEY_ICON,
163 	USE_KEYRING_ICON,
164 	EXPLOSIVE_DOOR_ICON,
165 	USE_CROWBAR_ICON,
166 
167 	CANCEL_ICON,
168 	NUM_ICONS
169 };
170 
171 
172 static GUIButtonRef iActionIcons[NUM_ICONS];
173 
174 // GLOBAL INTERFACE SURFACES
175 SGPVObject* guiDEAD;
176 SGPVObject* guiHATCH;
177 static SGPVObject* guiBUTTONBORDER;
178 SGPVObject* guiRADIO;
179 static SGPVObject* guiRADIO2;
180 
181 // UI Globals
182 MOUSE_REGION	gViewportRegion;
183 MOUSE_REGION	gRadarRegion;
184 
185 
186 static UINT16 gsUpArrowX;
187 static UINT16 gsUpArrowY;
188 static UINT16 gsDownArrowX;
189 static UINT16 gsDownArrowY;
190 
191 static BACKGROUND_SAVE* giUpArrowRect   = NO_BGND_RECT;
192 static BACKGROUND_SAVE* giDownArrowRect = NO_BGND_RECT;
193 
194 
195 DirtyLevel         fInterfacePanelDirty = DIRTYLEVEL2;
196 INT16              gsInterfaceLevel     = I_GROUND_LEVEL;
197 InterfacePanelKind gsCurInterfacePanel  = TEAM_PANEL;
198 
199 
InitializeTacticalInterface()200 void InitializeTacticalInterface()
201 {
202 	// Load button Interfaces
203 	iIconImages[WALK_IMAGES ] = LoadButtonImage(INTERFACEDIR "/newicons3.sti", -1,3,4,5,-1 );
204 	iIconImages[SNEAK_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 6, 7, 8, -1 );
205 	iIconImages[RUN_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 0, 1, 2, -1 );
206 	iIconImages[CRAWL_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 9, 10, 11, -1 );
207 	iIconImages[LOOK_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 12, 13, 14, -1 );
208 	iIconImages[TALK_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 21, 22, 23, -1 );
209 	iIconImages[HAND_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 18, 19, 20, -1 );
210 	iIconImages[CANCEL_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 15, 16, 17, -1 );
211 
212 	iIconImages[TARGETACTIONC_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 24, 25, 26, -1 );
213 	iIconImages[KNIFEACTIONC_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 27, 28, 29, -1 );
214 	iIconImages[AIDACTIONC_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 30, 31, 32, -1 );
215 	iIconImages[PUNCHACTIONC_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 33, 34, 35, -1 );
216 	iIconImages[BOMBACTIONC_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 36, 37, 38, -1 );
217 	iIconImages[TOOLKITACTIONC_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 39, 40, 41, -1 );
218 	iIconImages[WIRECUTACTIONC_IMAGES] = UseLoadedButtonImage(iIconImages[WALK_IMAGES ], -1, 42, 43, 44, -1 );
219 
220 	iIconImages[OPEN_DOOR_IMAGES] = LoadButtonImage(INTERFACEDIR "/door_op2.sti", -1,9,10,11,-1 );
221 	iIconImages[EXAMINE_DOOR_IMAGES] = UseLoadedButtonImage(iIconImages[OPEN_DOOR_IMAGES ], -1, 12, 13, 14, -1 );
222 	iIconImages[LOCKPICK_DOOR_IMAGES] = UseLoadedButtonImage(iIconImages[OPEN_DOOR_IMAGES ], -1, 21, 22, 23, -1 );
223 	iIconImages[BOOT_DOOR_IMAGES] = UseLoadedButtonImage(iIconImages[OPEN_DOOR_IMAGES ], -1, 25, 26, 27, -1 );
224 	iIconImages[CROWBAR_DOOR_IMAGES] = UseLoadedButtonImage(iIconImages[OPEN_DOOR_IMAGES ], -1, 0, 1, 2, -1 );
225 	iIconImages[USE_KEY_IMAGES] = UseLoadedButtonImage(iIconImages[OPEN_DOOR_IMAGES ], -1, 3, 4, 5, -1 );
226 	iIconImages[USE_KEYRING_IMAGES] = UseLoadedButtonImage(iIconImages[OPEN_DOOR_IMAGES ], -1, 6, 7, 8, -1 );
227 	iIconImages[EXPLOSIVE_DOOR_IMAGES] = UseLoadedButtonImage(iIconImages[OPEN_DOOR_IMAGES ], -1, 15, 16, 17, -1 );
228 
229 	// Load interface panels
230 	guiDEAD         = AddVideoObjectFromFile(INTERFACEDIR "/p_dead.sti");
231 	guiHATCH        = AddVideoObjectFromFile(INTERFACEDIR "/hatch.sti");
232 	guiBUTTONBORDER = AddVideoObjectFromFile(INTERFACEDIR "/button_frame.sti");
233 	guiRADIO        = AddVideoObjectFromFile(INTERFACEDIR "/radio.sti");
234 	guiRADIO2       = AddVideoObjectFromFile(INTERFACEDIR "/radio2.sti");
235 
236 	gTopMessage.uiSurface = AddVideoSurface(SCREEN_WIDTH, 20, PIXEL_DEPTH);
237 
238 	InitRadarScreen( );
239 
240 	InitTEAMSlots( );
241 }
242 
243 
InitializeCurrentPanel()244 void InitializeCurrentPanel()
245 {
246 	MoveRadarScreen( );
247 
248 	switch( gsCurInterfacePanel )
249 	{
250 		case SM_PANEL:
251 			// Set new viewport
252 			gsVIEWPORT_WINDOW_END_Y = INV_INTERFACE_START_Y;
253 
254 			// Render full
255 			SetRenderFlags(RENDER_FLAG_FULL);
256 			InitializeSMPanel();
257 			break;
258 
259 		case TEAM_PANEL:
260 			gsVIEWPORT_WINDOW_END_Y = INTERFACE_START_Y;
261 			// Render full
262 			SetRenderFlags(RENDER_FLAG_FULL);
263 			InitializeTEAMPanel();
264 			break;
265 
266 		default:
267 			throw std::logic_error("Tried to initialise invalid tactical panel");
268 	}
269 
270 	//RefreshMouseRegions( );
271 	gfPanelAllocated = TRUE;
272 }
273 
274 
ShutdownCurrentPanel()275 void ShutdownCurrentPanel( )
276 {
277 	if ( gfPanelAllocated )
278 	{
279 
280 		switch( gsCurInterfacePanel )
281 		{
282 			case SM_PANEL:
283 				ShutdownSMPanel( );
284 				break;
285 
286 			case TEAM_PANEL:
287 				ShutdownTEAMPanel( );
288 				break;
289 			default:
290 				break;
291 		}
292 
293 		gfPanelAllocated = FALSE;
294 
295 	}
296 }
297 
298 
SetCurrentTacticalPanelCurrentMerc(SOLDIERTYPE * s)299 void SetCurrentTacticalPanelCurrentMerc(SOLDIERTYPE* s)
300 {
301 	// Disable faces
302 	SetAllAutoFacesInactive( );
303 
304 	if ( gsCurInterfacePanel == SM_PANEL )
305 	{
306 		// If we are not of merc bodytype, or am an epc, and going into inv, goto another....
307 		if (!IS_MERC_BODY_TYPE(s) || AM_AN_EPC(s))
308 		{
309 			SetCurrentInterfacePanel( TEAM_PANEL );
310 		}
311 	}
312 
313 	switch( gsCurInterfacePanel )
314 	{
315 		case SM_PANEL:
316 			gSelectSMPanelToMerc = s;
317 			break;
318 		case TEAM_PANEL:
319 			SetTEAMPanelCurrentMerc();
320 			break;
321 		default:
322 			break;
323 	}
324 }
325 
326 
CreateCurrentTacticalPanelButtons(void)327 void CreateCurrentTacticalPanelButtons(void)
328 {
329 	switch (gsCurInterfacePanel)
330 	{
331 		case SM_PANEL:
332 			CreateSMPanelButtons();
333 			break;
334 		case TEAM_PANEL:
335 			CreateTEAMPanelButtons();
336 			break;
337 		default:
338 			break;
339 	}
340 }
341 
342 
SetCurrentInterfacePanel(InterfacePanelKind const ubNewPanel)343 void SetCurrentInterfacePanel(InterfacePanelKind const ubNewPanel)
344 {
345 	if(gfEnteringMapScreen)
346 		return;
347 	if(gfPanelAllocated && gsCurInterfacePanel == ubNewPanel)
348 		return;
349 
350 	ShutdownCurrentPanel( );
351 
352 	// INit new panel
353 	gsCurInterfacePanel = ubNewPanel;
354 
355 	InitializeCurrentPanel( );
356 
357 
358 }
359 
360 
ToggleTacticalPanels()361 void ToggleTacticalPanels( )
362 {
363 	SetNewPanel(gsCurInterfacePanel == SM_PANEL ? 0 : GetSelectedMan());
364 }
365 
366 
RemoveCurrentTacticalPanelButtons(void)367 void RemoveCurrentTacticalPanelButtons(void)
368 {
369 	switch (gsCurInterfacePanel)
370 	{
371 		case SM_PANEL:
372 			RemoveSMPanelButtons();
373 			break;
374 		case TEAM_PANEL:
375 			RemoveTEAMPanelButtons();
376 			break;
377 		default:
378 			break;
379 	}
380 }
381 
382 
IsMercPortraitVisible(const SOLDIERTYPE * s)383 BOOLEAN IsMercPortraitVisible(const SOLDIERTYPE* s)
384 {
385 	switch (gsCurInterfacePanel)
386 	{
387 		case TEAM_PANEL:
388 			return TRUE;
389 		case SM_PANEL:
390 			return gpSMCurrentMerc == s;
391 		default:
392 			return FALSE;
393 	}
394 }
395 
396 
397 static void HandleUpDownArrowBackgrounds(void);
398 
399 
HandleInterfaceBackgrounds()400 void HandleInterfaceBackgrounds( )
401 {
402 	HandleUpDownArrowBackgrounds( );
403 }
404 
405 
406 static void BtnMovementCallback(GUI_BUTTON* btn, INT32 reason);
407 
408 
MakeButtonMove(UINT const idx,UINT const gfx,INT16 const x,INT16 const y,UI_EVENT * const event,const ST::string & help,bool const disabled)409 static void MakeButtonMove(UINT const idx, UINT const gfx, INT16 const x, INT16 const y, UI_EVENT* const event, const ST::string& help, bool const disabled)
410 {
411 	GUIButtonRef const btn = QuickCreateButton(iIconImages[gfx], x, y, MSYS_PRIORITY_HIGHEST - 1, BtnMovementCallback);
412 	iActionIcons[idx] = btn;
413 	btn->SetUserPtr(event);
414 	btn->SetFastHelpText(help);
415 	if (disabled) DisableButton(btn);
416 }
417 
418 
419 static void MovementMenuBackregionCallback(MOUSE_REGION* pRegion, INT32 iReason);
420 
421 
PopupMovementMenu(UI_EVENT * const ev)422 void PopupMovementMenu(UI_EVENT* const ev)
423 {
424 	EraseInterfaceMenus(TRUE);
425 
426 	// Create mouse region over all area to facilitate clicking to end
427 	MSYS_DefineRegion(&gMenuOverlayRegion, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_HIGHEST - 1, CURSOR_NORMAL, MSYS_NO_CALLBACK, MovementMenuBackregionCallback);
428 
429 	giMenuAnchorX = gusMouseXPos - 18;
430 	giMenuAnchorY = gusMouseYPos - 18;
431 
432 	// ATE: Check if we're going off the screen
433 	if (giMenuAnchorX < 0) giMenuAnchorX = 0;
434 	if (giMenuAnchorY < 0) giMenuAnchorY = 0;
435 
436 	// Check for boundaries
437 	if (giMenuAnchorX > SCREEN_WIDTH - BUTTON_PANEL_WIDTH)
438 	{
439 		giMenuAnchorX = SCREEN_WIDTH - BUTTON_PANEL_WIDTH;
440 	}
441 	if (giMenuAnchorY > gsVIEWPORT_WINDOW_END_Y - BUTTON_PANEL_HEIGHT)
442 	{
443 		giMenuAnchorY = gsVIEWPORT_WINDOW_END_Y - BUTTON_PANEL_HEIGHT;
444 	}
445 
446 	INT32              const x                     = giMenuAnchorX + 9;
447 	INT32              const y                     = giMenuAnchorY + 8;
448 	SOLDIERTYPE const* const s                     = GetSelectedMan();
449 	bool               const is_epc                = AM_AN_EPC(s);
450 	bool               const is_vehicle            = s->uiStatusFlags & SOLDIER_VEHICLE;
451 	bool               const is_robot              = s->uiStatusFlags & SOLDIER_ROBOT;
452 	bool               const is_uncontrolled_robot = is_robot && !CanRobotBeControlled(s);
453 
454 	MakeButtonMove(LOOK_ICON, LOOK_IMAGES, x, y, ev, TacticalStr[LOOK_CURSOR_POPUPTEXT],
455 			is_vehicle || is_uncontrolled_robot);
456 	MakeButtonMove(RUN_ICON, RUN_IMAGES, x + 20, y, ev, pTacticalPopupButtonStrings[RUN_ICON],
457 			is_vehicle || is_robot || MercInWater(s));
458 	ST::string help = is_vehicle ? TacticalStr[DRIVE_POPUPTEXT] : pTacticalPopupButtonStrings[WALK_ICON];
459 	MakeButtonMove(WALK_ICON, WALK_IMAGES, x + 40, y, ev, help, is_uncontrolled_robot);
460 
461 	UINT32         action_image;
462 	ST::string action_text;
463 	bool           disable_action = false;
464 	if (is_vehicle)
465 	{
466 		// Until we get mounted weapons
467 		action_image   = CANCEL_IMAGES;
468 		action_text    = TacticalStr[NOT_APPLICABLE_POPUPTEXT];
469 		disable_action = true;
470 	}
471 	else
472 	{
473 		// Create button based on what is in our hands at the moment!
474 		UINT16 const item = s->inv[HANDPOS].usItem;
475 		if (item == TOOLKIT)
476 		{
477 			action_image = TOOLKITACTIONC_IMAGES;
478 			action_text  = TacticalStr[NOT_APPLICABLE_POPUPTEXT];
479 		}
480 		else if (item == WIRECUTTERS)
481 		{
482 			action_image = WIRECUTACTIONC_IMAGES;
483 			action_text  = TacticalStr[NOT_APPLICABLE_POPUPTEXT];
484 		}
485 		else switch (GCM->getItem(item)->getItemClass())
486 		{
487 			case IC_PUNCH:
488 				action_image = PUNCHACTIONC_IMAGES;
489 				action_text  = TacticalStr[USE_HANDTOHAND_POPUPTEXT];
490 				break;
491 
492 			case IC_GUN:
493 				action_image = TARGETACTIONC_IMAGES;
494 				action_text  = TacticalStr[USE_FIREARM_POPUPTEXT];
495 				break;
496 
497 			case IC_BLADE:
498 				action_image = KNIFEACTIONC_IMAGES;
499 				action_text  = TacticalStr[USE_BLADE_POPUPTEXT];
500 				break;
501 
502 			case IC_GRENADE:
503 			case IC_BOMB:
504 				action_image = BOMBACTIONC_IMAGES;
505 				action_text  = TacticalStr[USE_EXPLOSIVE_POPUPTEXT];
506 				break;
507 
508 			case IC_MEDKIT:
509 				action_image = AIDACTIONC_IMAGES;
510 				action_text  = TacticalStr[USE_MEDKIT_POPUPTEXT];
511 				break;
512 
513 			default:
514 				action_image   = CANCEL_IMAGES;
515 				action_text    = TacticalStr[NOT_APPLICABLE_POPUPTEXT];
516 				disable_action = true;
517 				break;
518 		}
519 	}
520 
521 	MakeButtonMove(ACTIONC_ICON, action_image, x, y + 20, ev, action_text,
522 			is_epc || disable_action);
523 	MakeButtonMove(CANCEL_ICON, CANCEL_IMAGES, x + 20, y + 20, ev, pTacticalPopupButtonStrings[CANCEL_ICON],
524 			false);
525 	MakeButtonMove(SNEAK_ICON, SNEAK_IMAGES, x + 40, y + 20, ev, pTacticalPopupButtonStrings[SNEAK_ICON],
526 			!IsValidStance(s, ANIM_CROUCH));
527 	MakeButtonMove(TALK_ICON, TALK_IMAGES, x, y + 40, ev, pTacticalPopupButtonStrings[TALK_ICON],
528 			is_epc || is_vehicle);
529 	MakeButtonMove(HAND_ICON, HAND_IMAGES, x + 20, y + 40, ev, pTacticalPopupButtonStrings[HAND_ICON],
530 			is_epc || is_vehicle);
531 	MakeButtonMove(CRAWL_ICON, CRAWL_IMAGES, x + 40, y + 40, ev, pTacticalPopupButtonStrings[CRAWL_ICON],
532 			!IsValidStance(s, ANIM_PRONE));
533 
534 	gfInMovementMenu  = TRUE;
535 	gfIgnoreScrolling = TRUE;
536 }
537 
538 
PopDownMovementMenu()539 void PopDownMovementMenu( )
540 {
541 	if ( gfInMovementMenu )
542 	{
543 		RemoveButton( iActionIcons[ WALK_ICON  ] );
544 		RemoveButton( iActionIcons[ SNEAK_ICON  ] );
545 		RemoveButton( iActionIcons[ RUN_ICON  ] );
546 		RemoveButton( iActionIcons[ CRAWL_ICON  ] );
547 		RemoveButton( iActionIcons[ LOOK_ICON  ] );
548 		RemoveButton( iActionIcons[ ACTIONC_ICON  ] );
549 		RemoveButton( iActionIcons[ TALK_ICON  ] );
550 		RemoveButton( iActionIcons[ HAND_ICON  ] );
551 		RemoveButton( iActionIcons[ CANCEL_ICON  ] );
552 
553 		// Turn off Ignore scrolling
554 		gfIgnoreScrolling = FALSE;
555 
556 		// Rerender world
557 		SetRenderFlags( RENDER_FLAG_FULL );
558 
559 		fInterfacePanelDirty = DIRTYLEVEL2;
560 
561 		MSYS_RemoveRegion( &gMenuOverlayRegion );
562 	}
563 
564 	gfInMovementMenu = FALSE;
565 
566 }
567 
RenderMovementMenu()568 void RenderMovementMenu( )
569 {
570 	if ( gfInMovementMenu )
571 	{
572 		BltVideoObject(FRAME_BUFFER, guiBUTTONBORDER, 0, giMenuAnchorX, giMenuAnchorY);
573 
574 		// Mark buttons dirty!
575 		MarkAButtonDirty( iActionIcons[ WALK_ICON  ] );
576 		MarkAButtonDirty( iActionIcons[ SNEAK_ICON  ] );
577 		MarkAButtonDirty( iActionIcons[ RUN_ICON  ] );
578 		MarkAButtonDirty( iActionIcons[ CRAWL_ICON  ] );
579 		MarkAButtonDirty( iActionIcons[ LOOK_ICON  ] );
580 		MarkAButtonDirty( iActionIcons[ ACTIONC_ICON  ] );
581 		MarkAButtonDirty( iActionIcons[ TALK_ICON  ] );
582 		MarkAButtonDirty( iActionIcons[ HAND_ICON  ] );
583 		MarkAButtonDirty( iActionIcons[ CANCEL_ICON  ] );
584 
585 		InvalidateRegion(giMenuAnchorX, giMenuAnchorY, giMenuAnchorX + BUTTON_PANEL_WIDTH,
586 					giMenuAnchorY + BUTTON_PANEL_HEIGHT);
587 
588 	}
589 }
590 
CancelMovementMenu()591 void CancelMovementMenu( )
592 {
593 	// Signal end of event
594 	PopDownMovementMenu( );
595 	guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
596 }
597 
598 
BtnMovementCallback(GUI_BUTTON * btn,INT32 reason)599 static void BtnMovementCallback(GUI_BUTTON* btn, INT32 reason)
600 {
601 	if ( reason & MSYS_CALLBACK_REASON_LBUTTON_UP )
602 	{
603 		btn->uiFlags |= BUTTON_CLICKED_ON;
604 
605 		UI_EVENT* const pUIEvent = btn->GetUserPtr<UI_EVENT>();
606 
607 		if (btn == iActionIcons[WALK_ICON])
608 		{
609 			pUIEvent->uiParams[0] = MOVEMENT_MENU_WALK;
610 		}
611 		else if (btn == iActionIcons[RUN_ICON])
612 		{
613 			pUIEvent->uiParams[0] = MOVEMENT_MENU_RUN;
614 		}
615 		else if (btn == iActionIcons[SNEAK_ICON])
616 		{
617 			pUIEvent->uiParams[0] = MOVEMENT_MENU_SWAT;
618 		}
619 		else if (btn == iActionIcons[CRAWL_ICON])
620 		{
621 			pUIEvent->uiParams[0] = MOVEMENT_MENU_PRONE;
622 		}
623 		else if (btn == iActionIcons[LOOK_ICON])
624 		{
625 			pUIEvent->uiParams[2] = MOVEMENT_MENU_LOOK;
626 		}
627 		else if (btn == iActionIcons[ACTIONC_ICON])
628 		{
629 			pUIEvent->uiParams[2] = MOVEMENT_MENU_ACTIONC;
630 		}
631 		else if (btn == iActionIcons[TALK_ICON])
632 		{
633 			pUIEvent->uiParams[2] = MOVEMENT_MENU_TALK;
634 		}
635 		else if (btn == iActionIcons[HAND_ICON])
636 		{
637 			pUIEvent->uiParams[2] = MOVEMENT_MENU_HAND;
638 		}
639 		else if (btn == iActionIcons[CANCEL_ICON])
640 		{
641 			// Signal end of event
642 			EndMenuEvent( U_MOVEMENT_MENU );
643 			pUIEvent->uiParams[1] = FALSE;
644 			return;
645 		}
646 		else
647 		{
648 			return;
649 		}
650 
651 		// Signal end of event
652 		EndMenuEvent( U_MOVEMENT_MENU );
653 		pUIEvent->uiParams[1] = TRUE;
654 
655 	}
656 
657 }
658 
659 
660 static void GetArrowsBackground(void);
661 
662 
HandleUpDownArrowBackgrounds(void)663 static void HandleUpDownArrowBackgrounds(void)
664 {
665 	static UINT32 uiOldShowUpDownArrows = ARROWS_HIDE_UP | ARROWS_HIDE_DOWN;
666 
667 	// Check for change in mode
668 	if ( guiShowUPDownArrows != uiOldShowUpDownArrows || gfUIRefreshArrows )
669 	{
670 		gfUIRefreshArrows = FALSE;
671 
672 		// Hide position of new ones
673 		GetArrowsBackground( );
674 
675 		uiOldShowUpDownArrows = guiShowUPDownArrows;
676 
677 	}
678 
679 }
680 
RenderArrows()681 void RenderArrows( )
682 {
683 	TILE_ELEMENT TileElem;
684 
685 	if ( guiShowUPDownArrows & ARROWS_HIDE_UP && guiShowUPDownArrows & ARROWS_HIDE_DOWN )
686 	{
687 		return;
688 	}
689 
690 	if ( guiShowUPDownArrows & ARROWS_SHOW_UP_BESIDE )
691 	{
692 		TileElem = gTileDatabase[ SECONDPOINTERS3 ];
693 		BltVideoObject(  FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY);
694 	}
695 
696 	if ( guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_G )
697 	{
698 		TileElem = gTileDatabase[ SECONDPOINTERS1 ];
699 		BltVideoObject(  FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY);
700 	}
701 
702 	if ( guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_Y )
703 	{
704 		TileElem = gTileDatabase[ SECONDPOINTERS3 ];
705 		BltVideoObject(  FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY);
706 	}
707 
708 	if ( guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_YG )
709 	{
710 		TileElem = gTileDatabase[ SECONDPOINTERS3 ];
711 		BltVideoObject( FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY);
712 		TileElem = gTileDatabase[ SECONDPOINTERS1 ];
713 		BltVideoObject( FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY + 20);
714 	}
715 
716 	if ( guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_GG )
717 	{
718 		TileElem = gTileDatabase[ SECONDPOINTERS1 ];
719 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY);
720 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY + 20);
721 	}
722 
723 	if ( guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_YY )
724 	{
725 		TileElem = gTileDatabase[ SECONDPOINTERS3 ];
726 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY);
727 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY + 20);
728 	}
729 
730 	if ( guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_CLIMB )
731 	{
732 		TileElem = gTileDatabase[ SECONDPOINTERS8 ];
733 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY);
734 	}
735 
736 	if ( guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_CLIMB2 )
737 	{
738 		TileElem = gTileDatabase[ SECONDPOINTERS3 ];
739 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY + 20);
740 		TileElem = gTileDatabase[ SECONDPOINTERS8 ];
741 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY);
742 	}
743 
744 	if ( guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_CLIMB3 )
745 	{
746 		TileElem = gTileDatabase[ SECONDPOINTERS3 ];
747 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY);
748 		TileElem = gTileDatabase[ SECONDPOINTERS8 ];
749 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY + 20);
750 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsUpArrowX, gsUpArrowY + 40);
751 	}
752 
753 	if ( guiShowUPDownArrows & ARROWS_SHOW_DOWN_BESIDE )
754 	{
755 		TileElem = gTileDatabase[ SECONDPOINTERS4 ];
756 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsDownArrowX, gsDownArrowY);
757 	}
758 
759 	if ( guiShowUPDownArrows & ARROWS_SHOW_DOWN_BELOW_G )
760 	{
761 		TileElem = gTileDatabase[ SECONDPOINTERS2 ];
762 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsDownArrowX, gsDownArrowY);
763 	}
764 
765 	if ( guiShowUPDownArrows & ARROWS_SHOW_DOWN_BELOW_Y )
766 	{
767 		TileElem = gTileDatabase[ SECONDPOINTERS4 ];
768 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsDownArrowX, gsDownArrowY);
769 	}
770 
771 	if ( guiShowUPDownArrows & ARROWS_SHOW_DOWN_CLIMB )
772 	{
773 		TileElem = gTileDatabase[ SECONDPOINTERS7 ];
774 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsDownArrowX, gsDownArrowY);
775 	}
776 
777 	if ( guiShowUPDownArrows & ARROWS_SHOW_DOWN_BELOW_YG )
778 	{
779 		TileElem = gTileDatabase[ SECONDPOINTERS2 ];
780 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsDownArrowX, gsDownArrowY);
781 		TileElem = gTileDatabase[ SECONDPOINTERS4 ];
782 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsDownArrowX, gsDownArrowY + 20);
783 	}
784 
785 	if ( guiShowUPDownArrows & ARROWS_SHOW_DOWN_BELOW_GG )
786 	{
787 		TileElem = gTileDatabase[ SECONDPOINTERS2 ];
788 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsDownArrowX, gsDownArrowY);
789 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsDownArrowX, gsDownArrowY + 20);
790 	}
791 
792 	if ( guiShowUPDownArrows & ARROWS_SHOW_DOWN_BELOW_YY )
793 	{
794 		TileElem = gTileDatabase[ SECONDPOINTERS4 ];
795 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsDownArrowX, gsDownArrowY);
796 		BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, gsDownArrowX, gsDownArrowY + 20);
797 	}
798 
799 
800 }
801 
EraseRenderArrows()802 void EraseRenderArrows( )
803 {
804 	if (giUpArrowRect != NO_BGND_RECT)
805 		FreeBackgroundRect(giUpArrowRect);
806 	giUpArrowRect = NO_BGND_RECT;
807 
808 	if (giDownArrowRect != NO_BGND_RECT)
809 		FreeBackgroundRect(giDownArrowRect);
810 	giDownArrowRect = NO_BGND_RECT;
811 }
812 
813 
GetArrowsBackground(void)814 static void GetArrowsBackground(void)
815 {
816 	INT16  sMercScreenX, sMercScreenY;
817 	UINT16 sArrowHeight = ARROWS_HEIGHT, sArrowWidth = ARROWS_WIDTH;
818 
819 	if ( guiShowUPDownArrows & ARROWS_HIDE_UP && guiShowUPDownArrows & ARROWS_HIDE_DOWN )
820 	{
821 		return;
822 	}
823 
824 	const SOLDIERTYPE* const sel = GetSelectedMan();
825 	if (sel == NULL) return;
826 
827 	// Get screen position of our guy
828 	GetSoldierTRUEScreenPos(sel, &sMercScreenX, &sMercScreenY);
829 
830 	if (guiShowUPDownArrows & ARROWS_SHOW_UP_BESIDE)
831 	{
832 		// Setup blt rect
833 		gsUpArrowX = sMercScreenX + ARROWS_X_OFFSET;
834 		gsUpArrowY = sMercScreenY + UPARROW_Y_OFFSET;
835 	}
836 
837 	if (guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_G || guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_Y)
838 	{
839 		// Setup blt rect
840 		gsUpArrowX = sMercScreenX - 10;
841 		gsUpArrowY = sMercScreenY - 50;
842 	}
843 
844 	if (guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_YG || guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_GG || guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_YY)
845 	{
846 		// Setup blt rect
847 		gsUpArrowX = sMercScreenX - 10;
848 		gsUpArrowY = sMercScreenY - 70;
849 		sArrowHeight = 3 * ARROWS_HEIGHT;
850 	}
851 
852 	if (guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_CLIMB)
853 	{
854 		// Setup blt rect
855 		gsUpArrowX = sMercScreenX - 10;
856 		gsUpArrowY = sMercScreenY - 70;
857 		sArrowHeight = 2 * ARROWS_HEIGHT;
858 	}
859 
860 	if (guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_CLIMB2)
861 	{
862 		// Setup blt rect
863 		gsUpArrowX = sMercScreenX - 10;
864 		gsUpArrowY = sMercScreenY - 80;
865 		sArrowHeight = 3 * ARROWS_HEIGHT;
866 	}
867 
868 	if (guiShowUPDownArrows & ARROWS_SHOW_UP_ABOVE_CLIMB3)
869 	{
870 		// Setup blt rect
871 		gsUpArrowX = sMercScreenX - 10;
872 		gsUpArrowY = sMercScreenY - 900;
873 		sArrowHeight = 5 * ARROWS_HEIGHT;
874 	}
875 
876 	if (guiShowUPDownArrows & ARROWS_SHOW_DOWN_BESIDE)
877 	{
878 		gsDownArrowX = sMercScreenX + ARROWS_X_OFFSET;
879 		gsDownArrowY = sMercScreenY + DOWNARROW_Y_OFFSET;
880 	}
881 
882 	if (guiShowUPDownArrows & ARROWS_SHOW_DOWN_BELOW_Y || guiShowUPDownArrows & ARROWS_SHOW_DOWN_BELOW_G)
883 	{
884 		gsDownArrowX = sMercScreenX -10;
885 		gsDownArrowY = sMercScreenY + 10;
886 	}
887 
888 	if (guiShowUPDownArrows & ARROWS_SHOW_DOWN_CLIMB)
889 	{
890 		gsDownArrowX = sMercScreenX - 10;
891 		gsDownArrowY = sMercScreenY + 10;
892 		sArrowHeight = 3 * ARROWS_HEIGHT;
893 	}
894 
895 	if (guiShowUPDownArrows & ARROWS_SHOW_DOWN_BELOW_YG || guiShowUPDownArrows & ARROWS_SHOW_DOWN_BELOW_GG || guiShowUPDownArrows & ARROWS_SHOW_DOWN_BELOW_YY)
896 	{
897 		gsDownArrowX = sMercScreenX -10;
898 		gsDownArrowY = sMercScreenY + 10;
899 		sArrowHeight = 3 * ARROWS_HEIGHT;
900 	}
901 
902 	// Adjust arrows based on level
903 	if (gsInterfaceLevel == I_ROOF_LEVEL)
904 	{
905 		//gsDownArrowY -= ROOF_LEVEL_HEIGHT;
906 		//gsUpArrowY -= ROOF_LEVEL_HEIGHT;
907 	}
908 
909 	//Erase prevois ones...
910 	EraseRenderArrows();
911 
912 	// Register dirty rects
913 	giDownArrowRect = RegisterBackgroundRect(BGND_FLAG_PERMANENT, gsDownArrowX, gsDownArrowY,
914 							sArrowWidth, sArrowHeight);
915 	giUpArrowRect   = RegisterBackgroundRect(BGND_FLAG_PERMANENT, gsUpArrowX,gsUpArrowY,
916 							sArrowWidth, sArrowHeight);
917 }
918 
919 
GetSoldierAboveGuyPositions(SOLDIERTYPE const * const s,INT16 * const psX,INT16 * const psY,BOOLEAN const fRadio)920 void GetSoldierAboveGuyPositions(SOLDIERTYPE const* const s, INT16* const psX, INT16* const psY, BOOLEAN const fRadio)
921 {
922 	INT16 const sTextBodyTypeYOffset = 62;
923 
924 	INT16 sStanceOffset;
925 	// Adjust based on body type
926 	switch (s->ubBodyType)
927 	{
928 		case CROW:
929 			sStanceOffset = 30;
930 			break;
931 		case ROBOTNOWEAPON:
932 			sStanceOffset = 30;
933 			break;
934 
935 		default:
936 		{
937 			// Adjust based on stance
938 			ANIMCONTROLTYPE const& ac = gAnimControl[s->usAnimState];
939 			UINT8 const ubAnimUseHeight = ac.uiFlags & ANIM_NOMOVE_MARKER ?
940 							ac.ubHeight : ac.ubEndHeight;
941 			switch (ubAnimUseHeight)
942 			{
943 				case ANIM_PRONE:
944 					sStanceOffset = 25;
945 					break;
946 				case ANIM_CROUCH:
947 					sStanceOffset = 10;
948 					break;
949 				default:
950 					sStanceOffset =  0;
951 					break;
952 			}
953 			break;
954 		}
955 	}
956 
957 	INT16 sMercScreenX;
958 	INT16 sMercScreenY;
959 	GetSoldierTRUEScreenPos(s, &sMercScreenX, &sMercScreenY);
960 
961 	*psX = sMercScreenX - 80 / 2;
962 	*psY = sMercScreenY - sTextBodyTypeYOffset + sStanceOffset;
963 
964 	// OK, Check if we need to go below....
965 	// Can do this if 1) displaying damge or 2) above screen
966 	if (s->ubProfile != NO_PROFILE &&
967 		!fRadio &&
968 		(s->fDisplayDamage || *psY < gsVIEWPORT_WINDOW_START_Y))
969 	{
970 		*psY = sMercScreenY;
971 	}
972 }
973 
974 
PrintAboveGuy(INT16 const x,INT16 const y,const ST::string & text)975 static void PrintAboveGuy(INT16 const x, INT16 const y, const ST::string& text)
976 {
977 	INT16 cx;
978 	INT16 cy;
979 	FindFontCenterCoordinates(x, y, 80, 1, text, TINYFONT1, &cx, &cy);
980 	GDirtyPrint(cx, cy, text);
981 }
982 
983 
984 static void DrawBarsInUIBox(const SOLDIERTYPE* pSoldier, INT16 sXPos, INT16 sYPos, INT16 sWidth, INT16 sHeight);
985 
986 
DrawSelectedUIAboveGuy(SOLDIERTYPE & s)987 void DrawSelectedUIAboveGuy(SOLDIERTYPE& s)
988 {
989 	if (s.bVisible == -1 && !(gTacticalStatus.uiFlags & SHOW_ALL_MERCS)) return;
990 
991 	if (s.sGridNo == NOWHERE) return;
992 
993 	if (s.fFlashLocator)
994 	{
995 		if (s.bVisible == -1)
996 		{
997 			s.fFlashLocator = FALSE;
998 		}
999 		else
1000 		{
1001 			if (TIMECOUNTERDONE(s.BlinkSelCounter, 80))
1002 			{
1003 				RESETTIMECOUNTER(s.BlinkSelCounter, 80);
1004 
1005 				s.fShowLocator = TRUE;
1006 				if (++s.sLocatorFrame == 5)
1007 				{
1008 					// Update time we do this
1009 					++s.fFlashLocator;
1010 					s.sLocatorFrame = 0;
1011 				}
1012 			}
1013 
1014 			if (s.fFlashLocator == s.ubNumLocateCycles)
1015 			{
1016 				s.fFlashLocator = FALSE;
1017 				s.fShowLocator  = FALSE;
1018 			}
1019 
1020 			// Render the beastie
1021 			INT16 sXPos;
1022 			INT16 sYPos;
1023 			GetSoldierAboveGuyPositions(&s, &sXPos, &sYPos, TRUE);
1024 
1025 			// Adjust for bars
1026 			sXPos += 25;
1027 			sYPos += 25;
1028 
1029 			// Add bars
1030 			RegisterBackgroundRectSingleFilled(sXPos, sYPos, 40, 40);
1031 
1032 			SGPVObject const* const gfx = s.bNeutral || s.bSide == OUR_TEAM ?
1033 							guiRADIO : guiRADIO2;
1034 			BltVideoObject(FRAME_BUFFER, gfx, s.sLocatorFrame, sXPos, sYPos);
1035 		}
1036 	}
1037 
1038 	// If he is in the middle of a certain animation, ignore
1039 	if (gAnimControl[s.usAnimState].uiFlags & ANIM_NOSHOW_MARKER) return;
1040 
1041 	// Do not show if we are dead
1042 	if (s.uiStatusFlags & SOLDIER_DEAD) return;
1043 
1044 	UINT16                   usGraphicToUse;
1045 	SOLDIERTYPE const* const sel = GetSelectedMan();
1046 	if (&s == sel && !gRubberBandActive)
1047 	{
1048 		usGraphicToUse = THIRDPOINTERS2;
1049 	}
1050 	else if (s.fShowLocator ||
1051 		s.uiStatusFlags & SOLDIER_MULTI_SELECTED ||
1052 		(&s == gSelectedGuy && !gfIgnoreOnSelectedGuy))
1053 	{
1054 		usGraphicToUse = THIRDPOINTERS1;
1055 	}
1056 	else
1057 	{
1058 		return;
1059 	}
1060 
1061 	INT16 sXPos;
1062 	INT16 sYPos;
1063 	GetSoldierAboveGuyPositions(&s, &sXPos, &sYPos, FALSE);
1064 
1065 	// Display name
1066 	SetFontAttributes(TINYFONT1, FONT_MCOLOR_WHITE);
1067 
1068 	if (s.ubProfile != NO_PROFILE || s.uiStatusFlags & SOLDIER_VEHICLE)
1069 	{
1070 		ST::string action;
1071 		if (&s == gUIValidCatcher && gfUIMouseOnValidCatcher == 1)
1072 		{
1073 			action = TacticalStr[CATCH_STR];
1074 		}
1075 		else if (&s == gUIValidCatcher && gfUIMouseOnValidCatcher == 3)
1076 		{
1077 			action = TacticalStr[RELOAD_STR];
1078 		}
1079 		else if (&s == gUIValidCatcher && gfUIMouseOnValidCatcher == 4)
1080 		{
1081 			action = pMessageStrings[MSG_PASS];
1082 		}
1083 		else if (s.bAssignment >= ON_DUTY)
1084 		{
1085 			SetFontForeground(FONT_YELLOW);
1086 			action = ST::format("({})", pAssignmentStrings[s.bAssignment]);
1087 		}
1088 		else if (s.bTeam == OUR_TEAM &&
1089 			s.bAssignment < ON_DUTY &&
1090 			s.bAssignment != CurrentSquad() &&
1091 			!(s.uiStatusFlags & SOLDIER_MULTI_SELECTED))
1092 		{
1093 			action = st_format_printf(gzLateLocalizedString[STR_LATE_34], s.bAssignment + 1);
1094 		}
1095 
1096 		bool raise_name = false;
1097 		if (!action.empty())
1098 		{
1099 			PrintAboveGuy(sXPos, sYPos, action);
1100 			raise_name = true;
1101 		}
1102 
1103 		// If not in a squad
1104 		if (s.uiStatusFlags & SOLDIER_VEHICLE)
1105 		{
1106 			if (GetNumberInVehicle(GetVehicle(s.bVehicleID)) == 0)
1107 			{
1108 				SetFontForeground(FONT_GRAY4);
1109 			}
1110 		}
1111 		else if (s.bAssignment >= ON_DUTY)
1112 		{
1113 			SetFontForeground(FONT_YELLOW);
1114 		}
1115 
1116 		PrintAboveGuy(sXPos, raise_name ? sYPos - 10 : sYPos, s.name);
1117 
1118 		if ((s.ubProfile != NO_PROFILE && MercProfile(s.ubProfile).isPlayerMerc()) ||
1119 			RPC_RECRUITED(&s) ||
1120 			AM_AN_EPC(&s) ||
1121 			s.uiStatusFlags & SOLDIER_VEHICLE)
1122 		{
1123 			// Adjust for bars!
1124 			if (&s == sel)
1125 			{
1126 				sXPos += 28;
1127 				sYPos += 5;
1128 			}
1129 			else
1130 			{
1131 				sXPos += 30;
1132 				sYPos += 7;
1133 			}
1134 
1135 			// Add bars
1136 			RegisterBackgroundRectSingleFilled(sXPos, sYPos, 34, 11);
1137 			TILE_ELEMENT const& TileElem = gTileDatabase[usGraphicToUse];
1138 			BltVideoObject(FRAME_BUFFER, TileElem.hTileSurface, TileElem.usRegionIndex, sXPos, sYPos);
1139 
1140 			// Draw life, breath
1141 			if (&s == sel)
1142 			{
1143 				sXPos += 1;
1144 				sYPos += 2;
1145 			}
1146 			DrawBarsInUIBox(&s, sXPos, sYPos, 16, 1);
1147 			return;
1148 		}
1149 		else
1150 		{
1151 			sYPos += 10;
1152 
1153 			if (gfUIMouseOnValidCatcher == 2 && &s == gUIValidCatcher)
1154 			{
1155 				SetFontForeground(FONT_MCOLOR_WHITE);
1156 				PrintAboveGuy(sXPos, sYPos, TacticalStr[GIVE_STR]);
1157 				return;
1158 			}
1159 		}
1160 	}
1161 	else if (s.bLevel != 0)
1162 	{
1163 		SetFontForeground(FONT_YELLOW);
1164 		PrintAboveGuy(sXPos, sYPos + 10, gzLateLocalizedString[STR_LATE_15]);
1165 	}
1166 
1167 	SetFontForeground(FONT_MCOLOR_DKRED);
1168 	PrintAboveGuy(sXPos, sYPos, GetSoldierHealthString(&s));
1169 }
1170 
1171 
DrawBarsInUIBox(const SOLDIERTYPE * pSoldier,INT16 sXPos,INT16 sYPos,INT16 sWidth,INT16 sHeight)1172 static void DrawBarsInUIBox(const SOLDIERTYPE* pSoldier, INT16 sXPos, INT16 sYPos, INT16 sWidth, INT16 sHeight)
1173 {
1174 	FLOAT  dWidth, dPercentage;
1175 	//UINT16 usLineColor;
1176 	UINT16 usLineColor;
1177 	INT8   bBandage;
1178 
1179 	// Draw breath points
1180 
1181 	// Draw new size
1182 	SGPVSurface::Lock l(FRAME_BUFFER);
1183 	UINT16* const pDestBuf = l.Buffer<UINT16>();
1184 	SetClippingRegionAndImageWidth(l.Pitch(), 0, gsVIEWPORT_WINDOW_START_Y, SCREEN_WIDTH, gsVIEWPORT_WINDOW_END_Y - gsVIEWPORT_WINDOW_START_Y);
1185 
1186 	// get amt bandaged
1187 	bBandage = pSoldier->bLifeMax - pSoldier->bLife - pSoldier->bBleeding;
1188 
1189 
1190 
1191 	// NOW DO BLEEDING
1192 	if ( pSoldier->bBleeding )
1193 	{
1194 		dPercentage = (FLOAT)( pSoldier->bBleeding +  pSoldier->bLife + bBandage )/ (FLOAT)100;
1195 		dWidth = dPercentage * sWidth;
1196 		usLineColor = Get16BPPColor(FROMRGB(240, 240, 20));
1197 		RectangleDraw(TRUE, sXPos + 3, sYPos + 1, (INT32)(sXPos + dWidth + 3), sYPos + 1, usLineColor, pDestBuf);
1198 	}
1199 
1200 	if( bBandage )
1201 	{
1202 		dPercentage = (FLOAT)( pSoldier->bLife + bBandage ) / (FLOAT)100;
1203 		dWidth = dPercentage * sWidth;
1204 		usLineColor = Get16BPPColor( FROMRGB( 222, 132, 132 ) );
1205 		RectangleDraw(TRUE, sXPos + 3, sYPos + 1, (INT32)(sXPos + dWidth + 3), sYPos + 1, usLineColor, pDestBuf);
1206 	}
1207 
1208 	dPercentage = (FLOAT)pSoldier->bLife / (FLOAT)100;
1209 	dWidth = dPercentage * sWidth;
1210 	usLineColor = Get16BPPColor(FROMRGB(200, 0, 0));
1211 	RectangleDraw(TRUE, sXPos + 3, sYPos + 1, (INT32)(sXPos + dWidth + 3), sYPos + 1, usLineColor, pDestBuf);
1212 
1213 	dPercentage = (FLOAT)( pSoldier->bBreathMax ) / (FLOAT)100;
1214 	dWidth = dPercentage * sWidth;
1215 	usLineColor = Get16BPPColor(FROMRGB(20, 20, 150));
1216 	RectangleDraw(TRUE, sXPos + 3, sYPos + 4, (INT32)(sXPos + dWidth + 3), sYPos + 4, usLineColor, pDestBuf);
1217 
1218 	dPercentage = (FLOAT)( pSoldier->bBreath ) / (FLOAT)100;
1219 	dWidth = dPercentage * sWidth;
1220 	usLineColor = Get16BPPColor(FROMRGB(100, 100, 220));
1221 	RectangleDraw(TRUE, sXPos + 3, sYPos + 4, (INT32)(sXPos + dWidth + 3), sYPos + 4, usLineColor, pDestBuf);
1222 
1223 	/*
1224 	// morale
1225 	dPercentage = (FLOAT)pSoldier->bMorale / (FLOAT)100;
1226 	dWidth = dPercentage * sWidth;
1227 	usLineColor = Get16BPPColor(FROMRGB(0, 250, 0));
1228 	RectangleDraw(TRUE, sXPos + 1, sYPos + 7, (INT32)(sXPos + dWidth + 1), sYPos + 7, usLineColor, pDestBuf);*/
1229 }
1230 
ClearInterface()1231 void ClearInterface( )
1232 {
1233 	if (fInMapMode) return; // XXX necessary?
1234 
1235 	// Stop any UI menus!
1236 	if ( gCurrentUIMode == MENU_MODE )
1237 	{
1238 		EndMenuEvent( guiCurrentEvent );
1239 	}
1240 
1241 	if ( gfUIHandleShowMoveGrid )
1242 	{
1243 		RemoveTopmost( gsUIHandleShowMoveGridLocation, FIRSTPOINTERS4	);
1244 		RemoveTopmost( gsUIHandleShowMoveGridLocation, FIRSTPOINTERS2 );
1245 		RemoveTopmost( gsUIHandleShowMoveGridLocation, FIRSTPOINTERS13 );
1246 		RemoveTopmost( gsUIHandleShowMoveGridLocation, FIRSTPOINTERS15 );
1247 	}
1248 
1249 	// Remove any popup menus
1250 	if ( gCurrentUIMode == GETTINGITEM_MODE )
1251 	{
1252 		RemoveItemPickupMenu( );
1253 	}
1254 
1255 	// Remove any popup menus
1256 	if ( gCurrentUIMode == OPENDOOR_MENU_MODE )
1257 	{
1258 		PopDownOpenDoorMenu( );
1259 	}
1260 
1261 	// Remove UP/DOWN arrows...
1262 	//EraseRenderArrows( );
1263 	// Don't render arrows this frame!
1264 	guiShowUPDownArrows = ARROWS_HIDE_UP | ARROWS_HIDE_DOWN;
1265 
1266 	ResetPhysicsTrajectoryUI( );
1267 
1268 	// Remove any paths, cursors
1269 	ErasePath();
1270 
1271 	//gfPlotNewMovement = TRUE;
1272 
1273 	// Erase Interface cursors
1274 	HideUICursor( );
1275 
1276 	gViewportRegion.ChangeCursor(VIDEO_NO_CURSOR);
1277 
1278 	// Hide lock UI cursors...
1279 	gDisableRegion.ChangeCursor(VIDEO_NO_CURSOR);
1280 	gUserTurnRegion.ChangeCursor(VIDEO_NO_CURSOR);
1281 
1282 	// Remove special thing for south arrow...
1283 	if (gsGlobalCursorYOffset == SCREEN_HEIGHT - gsVIEWPORT_WINDOW_END_Y)
1284 	{
1285 		SetCurrentCursorFromDatabase( VIDEO_NO_CURSOR );
1286 	}
1287 
1288 }
1289 
RestoreInterface()1290 void RestoreInterface( )
1291 {
1292 	// Once we are done, plot path again!
1293 	gfPlotNewMovement = TRUE;
1294 
1295 	// OK, reset arrows too...
1296 	gfUIRefreshArrows = TRUE;
1297 
1298 	// SHow lock UI cursors...
1299 	gDisableRegion.ChangeCursor(CURSOR_WAIT);
1300 	gUserTurnRegion.ChangeCursor(CURSOR_WAIT);
1301 }
1302 
1303 
DirtyMercPanelInterface(SOLDIERTYPE const * const pSoldier,DirtyLevel const dirty_level)1304 void DirtyMercPanelInterface(SOLDIERTYPE const* const pSoldier, DirtyLevel const dirty_level)
1305 {
1306 	if ( pSoldier->bTeam == OUR_TEAM )
1307 	{
1308 		// ONly set to a higher level!
1309 		if (fInterfacePanelDirty < dirty_level)
1310 		{
1311 			fInterfacePanelDirty = dirty_level;
1312 		}
1313 	}
1314 
1315 }
1316 
1317 struct OPENDOOR_MENU
1318 {
1319 	SOLDIERTYPE *pSoldier;
1320 	INT16   sX;
1321 	INT16   sY;
1322 	BOOLEAN fMenuHandled;
1323 	BOOLEAN fClosingDoor;
1324 };
1325 
1326 static OPENDOOR_MENU gOpenDoorMenu;
1327 BOOLEAN gfInOpenDoorMenu = FALSE;
1328 
1329 
1330 static void PopupDoorOpenMenu(BOOLEAN fClosingDoor);
1331 
1332 
InitDoorOpenMenu(SOLDIERTYPE * const pSoldier,BOOLEAN const fClosingDoor)1333 void InitDoorOpenMenu(SOLDIERTYPE* const pSoldier, BOOLEAN const fClosingDoor)
1334 {
1335 	INT16 sScreenX, sScreenY;
1336 
1337 	// Erase other menus....
1338 	EraseInterfaceMenus( TRUE );
1339 
1340 	InterruptTime();
1341 	PauseGame();
1342 	LockPauseState(LOCK_PAUSE_DOOR_OPEN);
1343 	// Pause timers as well....
1344 	PauseTime( TRUE );
1345 
1346 
1347 	gOpenDoorMenu.pSoldier     = pSoldier;
1348 	gOpenDoorMenu.fClosingDoor = fClosingDoor;
1349 
1350 	// OK, Determine position...
1351 	// Center on guy
1352 	// Locate to guy first.....
1353 	LocateSoldier(pSoldier, FALSE);
1354 	GetSoldierScreenPos( pSoldier, &sScreenX, &sScreenY );
1355 	gOpenDoorMenu.sX = sScreenX - (BUTTON_PANEL_WIDTH  - pSoldier->sBoundingBoxWidth)  / 2;
1356 	gOpenDoorMenu.sY = sScreenY - (BUTTON_PANEL_HEIGHT - pSoldier->sBoundingBoxHeight) / 2;
1357 
1358 	// Alrighty, cancel lock UI if we havn't done so already
1359 	UnSetUIBusy(pSoldier);
1360 
1361 
1362 	// OK, CHECK FOR BOUNDARIES!
1363 	if (gOpenDoorMenu.sX + BUTTON_PANEL_WIDTH > SCREEN_WIDTH)
1364 	{
1365 		gOpenDoorMenu.sX = SCREEN_WIDTH - BUTTON_PANEL_WIDTH;
1366 	}
1367 	if ( ( gOpenDoorMenu.sY + BUTTON_PANEL_HEIGHT ) > gsVIEWPORT_WINDOW_END_Y )
1368 	{
1369 		gOpenDoorMenu.sY = ( gsVIEWPORT_WINDOW_END_Y - BUTTON_PANEL_HEIGHT );
1370 	}
1371 	if ( gOpenDoorMenu.sX < 0 )
1372 	{
1373 		gOpenDoorMenu.sX = 0;
1374 	}
1375 	if ( gOpenDoorMenu.sY < 0 )
1376 	{
1377 		gOpenDoorMenu.sY = 0;
1378 	}
1379 
1380 
1381 	gOpenDoorMenu.fMenuHandled = FALSE;
1382 
1383 	guiPendingOverrideEvent    = OP_OPENDOORMENU;
1384 	HandleTacticalUI( );
1385 
1386 	PopupDoorOpenMenu( fClosingDoor );
1387 }
1388 
1389 
1390 static void BtnDoorMenuCallback(GUI_BUTTON* btn, INT32 reason);
1391 
1392 
MakeButtonDoor(UINT idx,UINT gfx,INT16 x,INT16 y,INT16 ap,INT16 bp,BOOLEAN disable,const ST::string & help)1393 static void MakeButtonDoor(UINT idx, UINT gfx, INT16 x, INT16 y, INT16 ap, INT16 bp, BOOLEAN disable, const ST::string& help)
1394 {
1395 	GUIButtonRef const btn = QuickCreateButton(iIconImages[gfx], x, y, MSYS_PRIORITY_HIGHEST - 1, BtnDoorMenuCallback);
1396 	iActionIcons[idx] = btn;
1397 	if (ap == 0 || !(gTacticalStatus.uiFlags & INCOMBAT))
1398 	{
1399 		btn->SetFastHelpText(help);
1400 	}
1401 	else
1402 	{
1403 		ST::string zDisp = ST::format("{} ( {} )", help, ap);
1404 		btn->SetFastHelpText(zDisp);
1405 	}
1406 	if (disable || (ap != 0 && !EnoughPoints(gOpenDoorMenu.pSoldier, ap, bp, FALSE)))
1407 	{
1408 		DisableButton(btn);
1409 	}
1410 }
1411 
1412 
1413 static void DoorMenuBackregionCallback(MOUSE_REGION* pRegion, INT32 iReason);
1414 
1415 
PopupDoorOpenMenu(BOOLEAN fClosingDoor)1416 static void PopupDoorOpenMenu(BOOLEAN fClosingDoor)
1417 {
1418 	INT32 dx = gOpenDoorMenu.sX;
1419 	INT32 dy = gOpenDoorMenu.sY;
1420 
1421 	dx += 9;
1422 	dy += 8;
1423 
1424 	// Create mouse region over all area to facilitate clicking to end
1425 	MSYS_DefineRegion(&gMenuOverlayRegion, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_HIGHEST - 1, CURSOR_NORMAL, MSYS_NO_CALLBACK, DoorMenuBackregionCallback);
1426 
1427 	const BOOLEAN d0 = fClosingDoor || AM_AN_EPC(gOpenDoorMenu.pSoldier);
1428 	BOOLEAN d;
1429 
1430 	d = d0 || !SoldierHasKey(*gOpenDoorMenu.pSoldier, ANYKEY);
1431 	MakeButtonDoor(USE_KEYRING_ICON, USE_KEYRING_IMAGES, dx + 20, dy, AP_UNLOCK_DOOR, BP_UNLOCK_DOOR, d,
1432 			pTacticalPopupButtonStrings[USE_KEYRING_ICON]);
1433 
1434 	d = fClosingDoor || FindUsableObj(gOpenDoorMenu.pSoldier, CROWBAR) == NO_SLOT;
1435 	MakeButtonDoor(USE_CROWBAR_ICON, CROWBAR_DOOR_IMAGES, dx + 40, dy, AP_USE_CROWBAR, BP_USE_CROWBAR, d,
1436 			pTacticalPopupButtonStrings[USE_CROWBAR_ICON]);
1437 
1438 	d = d0 || FindObj(gOpenDoorMenu.pSoldier, LOCKSMITHKIT) == NO_SLOT;
1439 	MakeButtonDoor(LOCKPICK_DOOR_ICON, LOCKPICK_DOOR_IMAGES, dx + 40, dy + 20, AP_PICKLOCK, BP_PICKLOCK, d,
1440 			pTacticalPopupButtonStrings[LOCKPICK_DOOR_ICON]);
1441 
1442 	d = d0 || FindObj(gOpenDoorMenu.pSoldier, SHAPED_CHARGE) == NO_SLOT;
1443 	MakeButtonDoor(EXPLOSIVE_DOOR_ICON, EXPLOSIVE_DOOR_IMAGES, dx + 40, dy + 40, AP_EXPLODE_DOOR, BP_EXPLODE_DOOR,
1444 			d, pTacticalPopupButtonStrings[EXPLOSIVE_DOOR_ICON]);
1445 
1446 	ST::string help = pTacticalPopupButtonStrings[fClosingDoor ? CANCEL_ICON + 1 : OPEN_DOOR_ICON];
1447 	MakeButtonDoor(OPEN_DOOR_ICON, OPEN_DOOR_IMAGES, dx, dy, AP_OPEN_DOOR, BP_OPEN_DOOR, FALSE, help);
1448 
1449 	MakeButtonDoor(EXAMINE_DOOR_ICON, EXAMINE_DOOR_IMAGES, dx, dy + 20, AP_EXAMINE_DOOR, BP_EXAMINE_DOOR, d0,
1450 			pTacticalPopupButtonStrings[EXAMINE_DOOR_ICON]);
1451 	MakeButtonDoor(BOOT_DOOR_ICON, BOOT_DOOR_IMAGES, dx, dy + 40, AP_BOOT_DOOR, BP_BOOT_DOOR, d0,
1452 			pTacticalPopupButtonStrings[BOOT_DOOR_ICON]);
1453 	MakeButtonDoor(UNTRAP_DOOR_ICON, UNTRAP_DOOR_ICON, dx + 20, dy + 40, AP_UNTRAP_DOOR, BP_UNTRAP_DOOR, d0,
1454 			pTacticalPopupButtonStrings[UNTRAP_DOOR_ICON]);
1455 	MakeButtonDoor(CANCEL_ICON, CANCEL_IMAGES, dx + 20, dy + 20, 0, 0, FALSE,
1456 			pTacticalPopupButtonStrings[CANCEL_ICON]);
1457 
1458 	gfInOpenDoorMenu = TRUE;
1459 
1460 	// Ignore scrolling
1461 	gfIgnoreScrolling = TRUE;
1462 }
1463 
1464 
PopDownOpenDoorMenu()1465 void PopDownOpenDoorMenu( )
1466 {
1467 	if ( gfInOpenDoorMenu )
1468 	{
1469 		UnLockPauseState();
1470 		UnPauseGame();
1471 		// UnPause timers as well....
1472 		PauseTime( FALSE );
1473 
1474 		RemoveButton( iActionIcons[ USE_KEYRING_ICON  ] );
1475 		RemoveButton( iActionIcons[ USE_CROWBAR_ICON  ] );
1476 		RemoveButton( iActionIcons[ LOCKPICK_DOOR_ICON  ] );
1477 		RemoveButton( iActionIcons[ EXPLOSIVE_DOOR_ICON  ] );
1478 		RemoveButton( iActionIcons[ OPEN_DOOR_ICON  ] );
1479 		RemoveButton( iActionIcons[ EXAMINE_DOOR_ICON  ] );
1480 		RemoveButton( iActionIcons[ BOOT_DOOR_ICON  ] );
1481 		RemoveButton( iActionIcons[ UNTRAP_DOOR_ICON  ] );
1482 		RemoveButton( iActionIcons[ CANCEL_ICON  ] );
1483 
1484 		// Turn off Ignore scrolling
1485 		gfIgnoreScrolling = FALSE;
1486 
1487 		// Rerender world
1488 		SetRenderFlags( RENDER_FLAG_FULL );
1489 
1490 		fInterfacePanelDirty = DIRTYLEVEL2;
1491 
1492 		MSYS_RemoveRegion( &gMenuOverlayRegion);
1493 	}
1494 
1495 	gfInOpenDoorMenu = FALSE;
1496 
1497 }
1498 
RenderOpenDoorMenu()1499 void RenderOpenDoorMenu( )
1500 {
1501 	if ( gfInOpenDoorMenu )
1502 	{
1503 		BltVideoObject(FRAME_BUFFER, guiBUTTONBORDER, 0, gOpenDoorMenu.sX, gOpenDoorMenu.sY);
1504 
1505 		// Mark buttons dirty!
1506 		MarkAButtonDirty( iActionIcons[ USE_KEYRING_ICON  ] );
1507 		MarkAButtonDirty( iActionIcons[ USE_CROWBAR_ICON  ] );
1508 		MarkAButtonDirty( iActionIcons[ LOCKPICK_DOOR_ICON  ] );
1509 		MarkAButtonDirty( iActionIcons[ EXPLOSIVE_DOOR_ICON  ] );
1510 		MarkAButtonDirty( iActionIcons[ OPEN_DOOR_ICON  ] );
1511 		MarkAButtonDirty( iActionIcons[ EXAMINE_DOOR_ICON  ] );
1512 		MarkAButtonDirty( iActionIcons[ BOOT_DOOR_ICON  ] );
1513 		MarkAButtonDirty( iActionIcons[ UNTRAP_DOOR_ICON  ] );
1514 		MarkAButtonDirty( iActionIcons[ CANCEL_ICON  ] );
1515 
1516 		RenderButtons( );
1517 
1518 		// if game is paused, then render paused game text
1519 		RenderPausedGameBox( );
1520 
1521 		InvalidateRegion( gOpenDoorMenu.sX, gOpenDoorMenu.sY, gOpenDoorMenu.sX + BUTTON_PANEL_WIDTH, gOpenDoorMenu.sY + BUTTON_PANEL_HEIGHT );
1522 
1523 	}
1524 }
1525 
CancelOpenDoorMenu()1526 void CancelOpenDoorMenu( )
1527 {
1528 	// Signal end of event
1529 	gOpenDoorMenu.fMenuHandled = 2;
1530 }
1531 
1532 
DoorAction(INT16 const ap,INT16 const bp,HandleDoor const action)1533 static void DoorAction(INT16 const ap, INT16 const bp, HandleDoor const action)
1534 {
1535 	SOLDIERTYPE* const s = gOpenDoorMenu.pSoldier;
1536 	if (EnoughPoints(s, ap, bp, FALSE))
1537 	{
1538 		SetUIBusy(s);
1539 		InteractWithClosedDoor(s, action);
1540 	}
1541 	else
1542 	{
1543 		// set cancel code
1544 		gOpenDoorMenu.fMenuHandled = 2;
1545 	}
1546 }
1547 
1548 
BtnDoorMenuCallback(GUI_BUTTON * btn,INT32 reason)1549 static void BtnDoorMenuCallback(GUI_BUTTON* btn, INT32 reason)
1550 {
1551 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1552 	{
1553 		btn->uiFlags |= BUTTON_CLICKED_ON;
1554 
1555 		// Popdown menu
1556 		gOpenDoorMenu.fMenuHandled = TRUE;
1557 
1558 		if (btn == iActionIcons[CANCEL_ICON])
1559 		{
1560 			// OK, set cancle code!
1561 			gOpenDoorMenu.fMenuHandled = 2;
1562 		}
1563 		else if (btn == iActionIcons[OPEN_DOOR_ICON])
1564 		{
1565 			// Open door normally...
1566 			// Check APs
1567 			if (EnoughPoints(gOpenDoorMenu.pSoldier, AP_OPEN_DOOR, BP_OPEN_DOOR, FALSE))
1568 			{
1569 				// Set UI
1570 				SetUIBusy(gOpenDoorMenu.pSoldier);
1571 
1572 				if (gOpenDoorMenu.fClosingDoor)
1573 				{
1574 					ChangeSoldierState(gOpenDoorMenu.pSoldier, GetAnimStateForInteraction(*gOpenDoorMenu.pSoldier, TRUE, CLOSE_DOOR), 0, FALSE);
1575 				}
1576 				else
1577 				{
1578 					InteractWithClosedDoor(gOpenDoorMenu.pSoldier, HANDLE_DOOR_OPEN);
1579 				}
1580 			}
1581 			else
1582 			{
1583 				// OK, set cancle code!
1584 				gOpenDoorMenu.fMenuHandled = 2;
1585 			}
1586 		}
1587 		else if (btn == iActionIcons[BOOT_DOOR_ICON])
1588 		{
1589 			DoorAction(AP_BOOT_DOOR, BP_BOOT_DOOR, HANDLE_DOOR_FORCE);
1590 		}
1591 		else if (btn == iActionIcons[USE_KEYRING_ICON])
1592 		{
1593 			DoorAction(AP_UNLOCK_DOOR, BP_UNLOCK_DOOR, HANDLE_DOOR_UNLOCK);
1594 		}
1595 		else if (btn == iActionIcons[LOCKPICK_DOOR_ICON])
1596 		{
1597 			DoorAction(AP_PICKLOCK, BP_PICKLOCK, HANDLE_DOOR_LOCKPICK);
1598 		}
1599 		else if (btn == iActionIcons[EXAMINE_DOOR_ICON])
1600 		{
1601 			DoorAction(AP_EXAMINE_DOOR, BP_EXAMINE_DOOR, HANDLE_DOOR_EXAMINE);
1602 		}
1603 		else if (btn == iActionIcons[EXPLOSIVE_DOOR_ICON])
1604 		{
1605 			DoorAction(AP_EXPLODE_DOOR, BP_EXPLODE_DOOR, HANDLE_DOOR_EXPLODE);
1606 		}
1607 		else if (btn == iActionIcons[UNTRAP_DOOR_ICON])
1608 		{
1609 			DoorAction(AP_UNTRAP_DOOR, BP_UNTRAP_DOOR, HANDLE_DOOR_UNTRAP);
1610 		}
1611 		else if (btn == iActionIcons[USE_CROWBAR_ICON])
1612 		{
1613 			DoorAction(AP_USE_CROWBAR, BP_USE_CROWBAR, HANDLE_DOOR_CROWBAR);
1614 		}
1615 
1616 		HandleOpenDoorMenu();
1617 	}
1618 }
1619 
1620 
HandleOpenDoorMenu()1621 BOOLEAN HandleOpenDoorMenu( )
1622 {
1623 	if ( gOpenDoorMenu.fMenuHandled )
1624 	{
1625 		PopDownOpenDoorMenu( );
1626 		return( gOpenDoorMenu.fMenuHandled );
1627 	}
1628 
1629 	return( FALSE );
1630 }
1631 
1632 
RenderUIMessage(VIDEO_OVERLAY * pBlitter)1633 static void RenderUIMessage(VIDEO_OVERLAY* pBlitter)
1634 {
1635 	// Shade area first...
1636 	pBlitter->uiDestBuff->ShadowRect(pBlitter->sX, pBlitter->sY, pBlitter->sX + gusUIMessageWidth - 2,
1637 						pBlitter->sY + gusUIMessageHeight - 2);
1638 
1639 	RenderMercPopUpBox(g_ui_message_box, pBlitter->sX, pBlitter->sY,  pBlitter->uiDestBuff);
1640 
1641 	InvalidateRegion(pBlitter->sX, pBlitter->sY, pBlitter->sX + gusUIMessageWidth,
1642 				pBlitter->sY + gusUIMessageHeight);
1643 }
1644 
1645 
1646 static UINT32 CalcUIMessageDuration(const ST::string& str);
1647 
1648 
BeginUIMessage(BOOLEAN fUseSkullIcon,const ST::string & text)1649 void BeginUIMessage(BOOLEAN fUseSkullIcon, const ST::string& text)
1650 {
1651 	guiUIMessageTime = GetJA2Clock( );
1652 	guiUIMessageTimeDelay = CalcUIMessageDuration(text);
1653 
1654 	MercPopupBoxFlags const flags = fUseSkullIcon ?
1655 		MERC_POPUP_PREPARE_FLAGS_SKULLICON :
1656 		MERC_POPUP_PREPARE_FLAGS_STOPICON;
1657 
1658 	// Prepare text box
1659 	g_ui_message_box = PrepareMercPopupBox(g_ui_message_box, BASIC_MERC_POPUP_BACKGROUND, BASIC_MERC_POPUP_BORDER, text, 200, 10, 0, 0, &gusUIMessageWidth, &gusUIMessageHeight, flags);
1660 
1661 	if (g_ui_message_overlay != NULL)
1662 	{
1663 		RemoveVideoOverlay(g_ui_message_overlay);
1664 		g_ui_message_overlay = NULL;
1665 	}
1666 
1667 	if (g_ui_message_overlay == NULL)
1668 	{
1669 		INT16 const x = (SCREEN_WIDTH - gusUIMessageWidth) / 2;
1670 		g_ui_message_overlay = RegisterVideoOverlay(RenderUIMessage, x, 150, gusUIMessageWidth, gusUIMessageHeight);
1671 	}
1672 
1673 	gfUseSkullIconMessage = fUseSkullIcon;
1674 }
1675 
1676 
BeginMapUIMessage(INT16 delta_y,const ST::string & text)1677 void BeginMapUIMessage(INT16 delta_y, const ST::string& text)
1678 {
1679 	guiUIMessageTime      = GetJA2Clock();
1680 	guiUIMessageTimeDelay = CalcUIMessageDuration(text);
1681 
1682 	g_ui_message_box = PrepareMercPopupBox(g_ui_message_box, BASIC_MERC_POPUP_BACKGROUND, BASIC_MERC_POPUP_BORDER, text, 200, 10, 0, 0, &gusUIMessageWidth, &gusUIMessageHeight, MERC_POPUP_PREPARE_FLAGS_TRANS_BACK);
1683 
1684 	if (g_ui_message_overlay == NULL)
1685 	{
1686 		INT16 const x = MAP_VIEW_START_X + (MAP_VIEW_WIDTH  - gusUIMessageWidth)  / 2 + 20;
1687 		INT16 const y = MAP_VIEW_START_Y + (MAP_VIEW_HEIGHT - gusUIMessageHeight) / 2 + delta_y;
1688 		g_ui_message_overlay = RegisterVideoOverlay(RenderUIMessage, x, y, gusUIMessageWidth, gusUIMessageHeight);
1689 	}
1690 }
1691 
1692 
EndUIMessage()1693 void EndUIMessage( )
1694 {
1695 	UINT32	uiClock = GetJA2Clock();
1696 
1697 	if (g_ui_message_overlay != NULL)
1698 	{
1699 		if ( gfUseSkullIconMessage )
1700 		{
1701 			if ( ( uiClock - guiUIMessageTime ) < 300 )
1702 			{
1703 				return;
1704 			}
1705 		}
1706 
1707 		RemoveVideoOverlay(g_ui_message_overlay);
1708 		g_ui_message_overlay = NULL;
1709 
1710 		// Remove popup as well....
1711 		if (g_ui_message_box)
1712 		{
1713 			RemoveMercPopupBox(g_ui_message_box);
1714 			g_ui_message_box = 0;
1715 		}
1716 	}
1717 }
1718 
1719 #define PLAYER_TEAM_TIMER_INTTERUPT_GRACE			(15000 / PLAYER_TEAM_TIMER_SEC_PER_TICKS)
1720 #define PLAYER_TEAM_TIMER_GRACE_PERIOD				1000
1721 #define PLAYER_TEAM_TIMER_SEC_PER_TICKS			100
1722 #define PLAYER_TEAM_TIMER_TICKS_PER_OK_MERC			(15000 / PLAYER_TEAM_TIMER_SEC_PER_TICKS)
1723 #define PLAYER_TEAM_TIMER_TICKS_PER_NOTOK_MERC			(5000 / PLAYER_TEAM_TIMER_SEC_PER_TICKS)
1724 #define PLAYER_TEAM_TIMER_TICKS_FROM_END_TO_START_BEEP		(5000 / PLAYER_TEAM_TIMER_SEC_PER_TICKS)
1725 #define PLAYER_TEAM_TIMER_TIME_BETWEEN_BEEPS			500
1726 #define PLAYER_TEAM_TIMER_TICKS_PER_ENEMY			(2000 / PLAYER_TEAM_TIMER_SEC_PER_TICKS)
1727 
1728 
1729 static void CreateTopMessage(void);
1730 
1731 
AddTopMessage(const MESSAGE_TYPES ubType)1732 void AddTopMessage(const MESSAGE_TYPES ubType)
1733 {
1734 	gTopMessage.fCreated = TRUE;
1735 
1736 	gTacticalStatus.ubTopMessageType = ubType;
1737 	gTacticalStatus.fInTopMessage    = TRUE;
1738 
1739 	CreateTopMessage();
1740 }
1741 
1742 
CreateTopMessage(void)1743 static void CreateTopMessage(void)
1744 {
1745 	const TacticalStatusType* const ts  = &gTacticalStatus;
1746 	SGPVSurface*              const dst = gTopMessage.uiSurface;
1747 
1748 	SetFontDestBuffer(dst);
1749 
1750 	const char* bar_file;
1751 	UINT16      bar_gfx     = 0;
1752 	BOOLEAN     fDoLimitBar = FALSE;
1753 	UINT8       foreground;
1754 	UINT8       shadow;
1755 	switch (ts->ubTopMessageType)
1756 	{
1757 		case COMPUTER_TURN_MESSAGE:
1758 		case COMPUTER_INTERRUPT_MESSAGE:
1759 		case MILITIA_INTERRUPT_MESSAGE:
1760 		case AIR_RAID_TURN_MESSAGE:
1761 			bar_file    = INTERFACEDIR "/rect.sti";
1762 			foreground  = FONT_MCOLOR_WHITE;
1763 			shadow      = DEFAULT_SHADOW;
1764 			fDoLimitBar = TRUE;
1765 			break;
1766 
1767 		case PLAYER_INTERRUPT_MESSAGE:
1768 			bar_file   = INTERFACEDIR "/timebaryellow.sti";
1769 			foreground = FONT_MCOLOR_BLACK;
1770 			shadow     = NO_SHADOW;
1771 			if (gGameOptions.fTurnTimeLimit) fDoLimitBar = TRUE;
1772 			break;
1773 
1774 		case PLAYER_TURN_MESSAGE:
1775 			bar_file   = INTERFACEDIR "/timebargreen.sti";
1776 			foreground = FONT_MCOLOR_BLACK;
1777 			shadow     = NO_SHADOW;
1778 			if (gGameOptions.fTurnTimeLimit)
1779 			{
1780 				fDoLimitBar = TRUE;
1781 			}
1782 			else
1783 			{
1784 				//bar_gfx     = 13;
1785 			}
1786 			break;
1787 
1788 		default:
1789 			abort();
1790 	}
1791 	SetFontAttributes(TINYFONT1, foreground, shadow);
1792 
1793 	const SGPBox* const bar = &g_ui.m_progress_bar_box;
1794 	{
1795 		AutoSGPVObject bar_vo(AddVideoObjectFromFile(bar_file));
1796 
1797 		BltVideoObject(dst, bar_vo.get(), bar_gfx, STD_SCREEN_X, 0);
1798 
1799 		if (fDoLimitBar)
1800 		{
1801 			INT32 bar_x = bar->x;
1802 			// Render end piece
1803 			BltVideoObject(dst, bar_vo.get(), 1, bar_x, bar->y);
1804 
1805 			INT32  gfx    = 2;
1806 			// -3 for the end pieces
1807 			UINT32 length = (bar->w - 3) * ts->usTactialTurnLimitCounter / ts->usTactialTurnLimitMax;
1808 			while (length-- != 0)
1809 			{
1810 				BltVideoObject(dst, bar_vo.get(), gfx, ++bar_x, bar->y);
1811 				if (++gfx == 12) gfx = 2;
1812 			}
1813 
1814 			if (ts->usTactialTurnLimitCounter == ts->usTactialTurnLimitMax)
1815 			{
1816 				// Render end piece
1817 				BltVideoObject(dst, bar_vo.get(), gfx, ++bar_x, bar->y);
1818 				BltVideoObject(dst, bar_vo.get(), 12,  ++bar_x, bar->y);
1819 			}
1820 		}
1821 	}
1822 
1823 	ST::string msg;
1824 	switch (ts->ubTopMessageType)
1825 	{
1826 		case COMPUTER_TURN_MESSAGE:
1827 		{
1828 			const UINT8 team = ts->ubCurrentTeam;
1829 			msg = team == CREATURE_TEAM && HostileBloodcatsPresent() ?
1830 					g_langRes->Message[STR_BLOODCATS_TURN] :
1831 					TeamTurnString[team];
1832 			break;
1833 		}
1834 
1835 		case COMPUTER_INTERRUPT_MESSAGE:
1836 		case PLAYER_INTERRUPT_MESSAGE:
1837 		case MILITIA_INTERRUPT_MESSAGE:
1838 			msg = g_langRes->Message[STR_INTERRUPT];
1839 			break;
1840 		case AIR_RAID_TURN_MESSAGE:
1841 			msg = TacticalStr[AIR_RAID_TURN_MESSAGE];
1842 			break;
1843 		case PLAYER_TURN_MESSAGE:
1844 			msg = TeamTurnString[OUR_TEAM];
1845 			break;
1846 
1847 		default: abort();
1848 	}
1849 
1850 	INT16 sX;
1851 	INT16 sY;
1852 	FindFontCenterCoordinates(bar->x, bar->y, bar->w, bar->h, msg, TINYFONT1, &sX, &sY);
1853 	MPrint(sX, sY, msg);
1854 
1855 	SetFontDestBuffer(FRAME_BUFFER);
1856 	SetFontShadow(DEFAULT_SHADOW);
1857 
1858 	gfTopMessageDirty = TRUE;
1859 }
1860 
1861 
HandleTopMessages(void)1862 void HandleTopMessages(void)
1863 {
1864 	TacticalStatusType* const ts = &gTacticalStatus;
1865 
1866 	if (!ts->fInTopMessage)
1867 	{
1868 		gsVIEWPORT_WINDOW_START_Y = 0;
1869 		return;
1870 	}
1871 
1872 	// ATE: If we are told to go into top message, but we have not initialized it
1873 	// yet.  This is mostly for loading saved games.
1874 	if (!gTopMessage.fCreated)
1875 	{
1876 		gfTopMessageDirty = TRUE;
1877 		AddTopMessage((MESSAGE_TYPES)ts->ubTopMessageType);
1878 	}
1879 
1880 	switch (ts->ubTopMessageType)
1881 	{
1882 		case COMPUTER_TURN_MESSAGE:
1883 		case COMPUTER_INTERRUPT_MESSAGE:
1884 		case MILITIA_INTERRUPT_MESSAGE:
1885 		case AIR_RAID_TURN_MESSAGE:
1886 			// OK, update timer.....
1887 			if (TIMECOUNTERDONE(giTimerTeamTurnUpdate, PLAYER_TEAM_TIMER_SEC_PER_TICKS))
1888 			{
1889 				RESETTIMECOUNTER(giTimerTeamTurnUpdate, PLAYER_TEAM_TIMER_SEC_PER_TICKS);
1890 
1891 				// Update counter....
1892 				if (ts->usTactialTurnLimitCounter < ts->usTactialTurnLimitMax)
1893 				{
1894 					++ts->usTactialTurnLimitCounter;
1895 				}
1896 
1897 				// Check if we have reach limit...
1898 				if (ts->usTactialTurnLimitCounter > gubProgCurEnemy * PLAYER_TEAM_TIMER_TICKS_PER_ENEMY)
1899 				{
1900 					ts->usTactialTurnLimitCounter = gubProgCurEnemy * PLAYER_TEAM_TIMER_TICKS_PER_ENEMY;
1901 				}
1902 
1903 				CreateTopMessage();
1904 			}
1905 			break;
1906 
1907 		case PLAYER_TURN_MESSAGE:
1908 		case PLAYER_INTERRUPT_MESSAGE:
1909 			if (gGameOptions.fTurnTimeLimit &&
1910 				!gfUserTurnRegionActive &&
1911 				!AreWeInAUIMenu() &&
1912 				GetJA2Clock() - ts->uiTactialTurnLimitClock > PLAYER_TEAM_TIMER_GRACE_PERIOD) // Check Grace period...
1913 			{
1914 				ts->uiTactialTurnLimitClock = 0;
1915 
1916 				if (TIMECOUNTERDONE(giTimerTeamTurnUpdate, PLAYER_TEAM_TIMER_SEC_PER_TICKS))
1917 				{
1918 					RESETTIMECOUNTER(giTimerTeamTurnUpdate, PLAYER_TEAM_TIMER_SEC_PER_TICKS);
1919 
1920 					if (ts->fTactialTurnLimitStartedBeep)
1921 					{
1922 						if (GetJA2Clock() - gTopMessage.uiTimeSinceLastBeep > PLAYER_TEAM_TIMER_TIME_BETWEEN_BEEPS)
1923 						{
1924 							gTopMessage.uiTimeSinceLastBeep = GetJA2Clock();
1925 							PlayJA2SampleFromFile(SOUNDSDIR "/turn_near_end.wav", HIGHVOLUME, 1, MIDDLEPAN);
1926 						}
1927 					}
1928 					else
1929 					{
1930 						// OK, have we gone past the time to
1931 						if (ts->usTactialTurnLimitMax - ts->usTactialTurnLimitCounter < PLAYER_TEAM_TIMER_TICKS_FROM_END_TO_START_BEEP)
1932 						{
1933 							ts->fTactialTurnLimitStartedBeep = TRUE;
1934 							gTopMessage.uiTimeSinceLastBeep = GetJA2Clock();
1935 						}
1936 					}
1937 
1938 					// Update counter....
1939 					if (ts->usTactialTurnLimitCounter < ts->usTactialTurnLimitMax)
1940 					{
1941 						++ts->usTactialTurnLimitCounter;
1942 					}
1943 
1944 					CreateTopMessage();
1945 
1946 					// Have we reached max?
1947 					if (ts->usTactialTurnLimitCounter == ts->usTactialTurnLimitMax - 1)
1948 					{
1949 						// ATE: increase this so that we don't go into here again...
1950 						++ts->usTactialTurnLimitCounter;
1951 						UIHandleEndTurn(NULL);
1952 					}
1953 				}
1954 			}
1955 			break;
1956 	}
1957 
1958 	gsVIEWPORT_WINDOW_START_Y = 20;
1959 
1960 	if (gfTopMessageDirty ||
1961 		gTopMessage.sWorldRenderX != gsRenderCenterX ||
1962 		gTopMessage.sWorldRenderY != gsRenderCenterY)
1963 	{
1964 		gfTopMessageDirty         = FALSE;
1965 		gTopMessage.sWorldRenderX = gsRenderCenterX;
1966 		gTopMessage.sWorldRenderY = gsRenderCenterY;
1967 
1968 		SGPVSurface* const src = gTopMessage.uiSurface;
1969 		BltVideoSurface(FRAME_BUFFER,  src, 0, 0, 0);
1970 		BltVideoSurface(guiSAVEBUFFER, src, 0, 0, 0);
1971 		InvalidateRegion(0, 0, SCREEN_WIDTH, 20);
1972 	}
1973 }
1974 
1975 
EndTopMessage(void)1976 void EndTopMessage(void)
1977 {
1978 	if (!gTacticalStatus.fInTopMessage) return;
1979 
1980 	gsVIEWPORT_WINDOW_START_Y     = 0;
1981 	gTacticalStatus.fInTopMessage = FALSE;
1982 
1983 	SetRenderFlags(RENDER_FLAG_FULL);
1984 }
1985 
1986 
InitEnemyUIBar(UINT8 ubNumEnemies,UINT8 ubDoneEnemies)1987 void InitEnemyUIBar( UINT8 ubNumEnemies, UINT8 ubDoneEnemies )
1988 {
1989 	// OK, set value
1990 	gubProgNumEnemies = ubNumEnemies + ubDoneEnemies;
1991 	gubProgCurEnemy = ubDoneEnemies;
1992 	gfProgBarActive = TRUE;
1993 
1994 	gTacticalStatus.usTactialTurnLimitCounter = ubDoneEnemies * PLAYER_TEAM_TIMER_TICKS_PER_ENEMY;
1995 	gTacticalStatus.usTactialTurnLimitMax = ( (ubNumEnemies + ubDoneEnemies) * PLAYER_TEAM_TIMER_TICKS_PER_ENEMY );
1996 
1997 }
1998 
UpdateEnemyUIBar()1999 void UpdateEnemyUIBar( )
2000 {
2001 	// Are we active?
2002 	if ( gfProgBarActive )
2003 	{
2004 		// OK, update team limit counter....
2005 		gTacticalStatus.usTactialTurnLimitCounter = ( gubProgCurEnemy * PLAYER_TEAM_TIMER_TICKS_PER_ENEMY );
2006 
2007 		gubProgCurEnemy++;
2008 
2009 	}
2010 
2011 	// Do we have an active enemy bar?
2012 	if (gTacticalStatus.fInTopMessage &&
2013 		gTacticalStatus.ubTopMessageType == COMPUTER_TURN_MESSAGE)
2014 	{
2015 		// Update message!
2016 		CreateTopMessage();
2017 	}
2018 }
2019 
2020 
InitPlayerUIBar(BOOLEAN fInterrupt)2021 void InitPlayerUIBar( BOOLEAN fInterrupt )
2022 {
2023 	INT8 bNumOK = 0, bNumNotOK = 0;
2024 
2025 	if ( !gGameOptions.fTurnTimeLimit )
2026 	{
2027 		AddTopMessage(fInterrupt == TRUE ? PLAYER_INTERRUPT_MESSAGE : PLAYER_TURN_MESSAGE);
2028 		return;
2029 	}
2030 
2031 	// OK, calculate time....
2032 	if ( !fInterrupt || gTacticalStatus.usTactialTurnLimitMax == 0 )
2033 	{
2034 		gTacticalStatus.usTactialTurnLimitCounter = 0;
2035 
2036 		CFOR_EACH_IN_TEAM(s, OUR_TEAM)
2037 		{
2038 			if (s->bInSector)
2039 			{
2040 				if (s->bLife < OKLIFE)
2041 				{
2042 					bNumNotOK++;
2043 				}
2044 				else
2045 				{
2046 					bNumOK++;
2047 				}
2048 			}
2049 		}
2050 
2051 		gTacticalStatus.usTactialTurnLimitMax = (bNumOK * PLAYER_TEAM_TIMER_TICKS_PER_OK_MERC) +
2052 							(bNumNotOK * PLAYER_TEAM_TIMER_TICKS_PER_NOTOK_MERC);
2053 	}
2054 	else
2055 	{
2056 		if ( gTacticalStatus.usTactialTurnLimitCounter > PLAYER_TEAM_TIMER_INTTERUPT_GRACE )
2057 		{
2058 			gTacticalStatus.usTactialTurnLimitCounter -= PLAYER_TEAM_TIMER_INTTERUPT_GRACE;
2059 		}
2060 	}
2061 
2062 	gTacticalStatus.uiTactialTurnLimitClock = 0;
2063 	gTacticalStatus.fTactialTurnLimitStartedBeep = FALSE;
2064 
2065 	// RESET COIUNTER...
2066 	RESETTIMECOUNTER( giTimerTeamTurnUpdate, PLAYER_TEAM_TIMER_SEC_PER_TICKS );
2067 
2068 
2069 	// OK, set value
2070 	AddTopMessage(fInterrupt != TRUE ? PLAYER_TURN_MESSAGE : PLAYER_INTERRUPT_MESSAGE);
2071 }
2072 
2073 
MovementMenuBackregionCallback(MOUSE_REGION * pRegion,INT32 iReason)2074 static void MovementMenuBackregionCallback(MOUSE_REGION* pRegion, INT32 iReason)
2075 {
2076 	if ( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP )
2077 	{
2078 		CancelMovementMenu( );
2079 	}
2080 }
2081 
2082 
DoorMenuBackregionCallback(MOUSE_REGION * pRegion,INT32 iReason)2083 static void DoorMenuBackregionCallback(MOUSE_REGION* pRegion, INT32 iReason)
2084 {
2085 	if ( iReason & MSYS_CALLBACK_REASON_LBUTTON_UP )
2086 	{
2087 		CancelOpenDoorMenu( );
2088 	}
2089 }
2090 
2091 
GetSoldierHealthString(const SOLDIERTYPE * const s)2092 ST::string GetSoldierHealthString(const SOLDIERTYPE* const s)
2093 {
2094 	INT32 i;
2095 	const INT32 start = (s->bLife == s->bLifeMax ? 4 : 0);
2096 	for (i = start; i < 6; ++i)
2097 	{
2098 		if (s->bLife < bHealthStrRanges[i]) break;
2099 	}
2100 	return zHealthStr[i];
2101 }
2102 
2103 
2104 static BOOLEAN gfDisplayPhysicsUI = FALSE;
2105 static INT16   gsPhysicsImpactPointGridNo;
2106 static INT8    gbPhysicsImpactPointLevel;
2107 static BOOLEAN gfBadPhysicsCTGT = FALSE;
2108 
BeginPhysicsTrajectoryUI(INT16 sGridNo,INT8 bLevel,BOOLEAN fBadCTGT)2109 void BeginPhysicsTrajectoryUI( INT16 sGridNo, INT8 bLevel, BOOLEAN fBadCTGT )
2110 {
2111 	gfDisplayPhysicsUI = TRUE;
2112 	gsPhysicsImpactPointGridNo = sGridNo;
2113 	gbPhysicsImpactPointLevel = bLevel;
2114 	gfBadPhysicsCTGT = fBadCTGT;
2115 }
2116 
EndPhysicsTrajectoryUI()2117 void EndPhysicsTrajectoryUI( )
2118 {
2119 	gfDisplayPhysicsUI = FALSE;
2120 
2121 }
2122 
SetupPhysicsTrajectoryUI()2123 void SetupPhysicsTrajectoryUI( )
2124 {
2125 	if ( gfDisplayPhysicsUI && gfUIHandlePhysicsTrajectory )
2126 	{
2127 		const UINT16 idx = (gfBadPhysicsCTGT ? FIRSTPOINTERS12 : FIRSTPOINTERS8);
2128 		LEVELNODE* n;
2129 		if (gbPhysicsImpactPointLevel == 0)
2130 		{
2131 			n = AddTopmostToHead(gsPhysicsImpactPointGridNo, idx);
2132 		}
2133 		else
2134 		{
2135 			n = AddOnRoofToHead(gsPhysicsImpactPointGridNo, idx);
2136 		}
2137 		n->ubShadeLevel        = DEFAULT_SHADE_LEVEL;
2138 		n->ubNaturalShadeLevel = DEFAULT_SHADE_LEVEL;
2139 	}
2140 }
2141 
2142 
ResetPhysicsTrajectoryUI()2143 void ResetPhysicsTrajectoryUI( )
2144 {
2145 	if ( gfDisplayPhysicsUI )
2146 	{
2147 		RemoveTopmost( gsPhysicsImpactPointGridNo, FIRSTPOINTERS8 );
2148 		RemoveTopmost( gsPhysicsImpactPointGridNo, FIRSTPOINTERS12 );
2149 		RemoveOnRoof( gsPhysicsImpactPointGridNo, FIRSTPOINTERS8 );
2150 		RemoveOnRoof( gsPhysicsImpactPointGridNo, FIRSTPOINTERS12 );
2151 	}
2152 
2153 }
2154 
DirtyTopMessage()2155 void DirtyTopMessage( )
2156 {
2157 	gTopMessage.fCreated = FALSE;
2158 }
2159 
2160 
2161 
CalcUIMessageDuration(const ST::string & str)2162 static UINT32 CalcUIMessageDuration(const ST::string& str)
2163 {
2164 	// base + X per letter
2165 	return( 1000 + 50 * static_cast<UINT32>(str.to_utf32().size()) );
2166 }
2167 
2168 
2169 static BOOLEAN gfMultipurposeLocatorOn = FALSE;
2170 static UINT32  guiMultiPurposeLocatorLastUpdate;
2171 static INT8    gbMultiPurposeLocatorFrame;
2172 static INT16   gsMultiPurposeLocatorGridNo;
2173 static INT8    gbMultiPurposeLocatorLevel;
2174 static INT8    gbMultiPurposeLocatorCycles;
2175 
2176 
BeginMultiPurposeLocator(const INT16 sGridNo,const INT8 bLevel)2177 void BeginMultiPurposeLocator(const INT16 sGridNo, const INT8 bLevel)
2178 {
2179 	guiMultiPurposeLocatorLastUpdate = 0;
2180 	gbMultiPurposeLocatorCycles      = 0;
2181 	gbMultiPurposeLocatorFrame       = 0;
2182 	gfMultipurposeLocatorOn = TRUE;
2183 
2184 	gsMultiPurposeLocatorGridNo = sGridNo;
2185 	gbMultiPurposeLocatorLevel  = bLevel;
2186 }
2187 
2188 
HandleMultiPurposeLocator()2189 void HandleMultiPurposeLocator( )
2190 {
2191 	UINT32 uiClock;
2192 
2193 	if ( !gfMultipurposeLocatorOn )
2194 	{
2195 		return;
2196 	}
2197 
2198 	// Update radio locator
2199 	uiClock = GetJA2Clock( );
2200 
2201 	// Update frame values!
2202 	if ( ( uiClock - guiMultiPurposeLocatorLastUpdate ) > 80 )
2203 	{
2204 		guiMultiPurposeLocatorLastUpdate = uiClock;
2205 
2206 		// Update frame
2207 		gbMultiPurposeLocatorFrame++;
2208 
2209 		if ( gbMultiPurposeLocatorFrame == 5 )
2210 		{
2211 			gbMultiPurposeLocatorFrame = 0;
2212 			gbMultiPurposeLocatorCycles++;
2213 		}
2214 
2215 		if ( gbMultiPurposeLocatorCycles == 8 )
2216 		{
2217 			gfMultipurposeLocatorOn    = FALSE;
2218 		}
2219 	}
2220 }
2221 
2222 
2223 
RenderTopmostMultiPurposeLocator()2224 void RenderTopmostMultiPurposeLocator( )
2225 {
2226 	FLOAT dOffsetX, dOffsetY;
2227 	FLOAT dTempX_S, dTempY_S;
2228 	INT16 sX, sY, sXPos, sYPos;
2229 
2230 	if ( !gfMultipurposeLocatorOn )
2231 	{
2232 		return;
2233 	}
2234 
2235 	ConvertGridNoToCenterCellXY( gsMultiPurposeLocatorGridNo, &sX, &sY );
2236 
2237 	dOffsetX = (FLOAT)( sX - gsRenderCenterX );
2238 	dOffsetY = (FLOAT)( sY - gsRenderCenterY );
2239 
2240 	// Calculate guy's position
2241 	FloatFromCellToScreenCoordinates( dOffsetX, dOffsetY, &dTempX_S, &dTempY_S );
2242 
2243 	sXPos = ( g_ui.m_tacticalMapCenterX ) + (INT16)dTempX_S;
2244 	sYPos = ( g_ui.m_tacticalMapCenterY ) + (INT16)dTempY_S - gpWorldLevelData[ gsMultiPurposeLocatorGridNo ].sHeight;
2245 
2246 	// Adjust for offset position on screen
2247 	sXPos -= gsRenderWorldOffsetX;
2248 	sYPos -= gsRenderWorldOffsetY;
2249 
2250 	// Adjust for render height
2251 	sYPos += gsRenderHeight;
2252 
2253 	// Adjust for level height
2254 	if ( gbMultiPurposeLocatorLevel )
2255 	{
2256 		sYPos -= ROOF_LEVEL_HEIGHT;
2257 	}
2258 
2259 	// Center circle!
2260 	sXPos -= 20;
2261 	sYPos -= 20;
2262 
2263 	RegisterBackgroundRectSingleFilled(sXPos, sYPos, 40, 40);
2264 
2265 	BltVideoObject(FRAME_BUFFER, guiRADIO, gbMultiPurposeLocatorFrame, sXPos, sYPos);
2266 }
2267