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