1 #include "Directories.h"
2 #include "Font.h"
3 #include "Interface_Control.h"
4 #include "Isometric_Utils.h"
5 #include "Local.h"
6 #include "Soldier_Ani.h"
7 #include "Timer_Control.h"
8 #include "VObject.h"
9 #include "SysUtil.h"
10 #include "Overhead.h"
11 #include "MouseSystem.h"
12 #include "Button_System.h"
13 #include "Interface.h"
14 #include "VSurface.h"
15 #include "Input.h"
16 #include "Handle_UI.h"
17 #include "RenderWorld.h"
18 #include "Cursors.h"
19 #include "Radar_Screen.h"
20 #include "Font_Control.h"
21 #include "Render_Dirty.h"
22 #include "Sound_Control.h"
23 #include "Interface_Panels.h"
24 #include "Animation_Control.h"
25 #include "Soldier_Control.h"
26 #include "PathAI.h"
27 #include "Weapons.h"
28 #include "Faces.h"
29 #include "MapScreen.h"
30 #include "Message.h"
31 #include "Text.h"
32 #include "Interface_Items.h"
33 #include "Interface_Utils.h"
34 #include "Game_Clock.h"
35 #include "Soldier_Macros.h"
36 #include "StrategicMap.h"
37 #include "Soldier_Functions.h"
38 #include "GameScreen.h"
39 #include "Assignments.h"
40 #include "Points.h"
41 #include "Squads.h"
42 #include "Strategic.h"
43 #include "Map_Screen_Interface_Map.h"
44 #include "Overhead_Map.h"
45 #include "Map_Screen_Interface.h"
46 #include "Options_Screen.h"
47 #include "ShopKeeper_Interface.h"
48 #include "English.h"
49 #include "Keys.h"
50 #include "Soldier_Add.h"
51 #include "Vehicles.h"
52 #include "GameSettings.h"
53 #include "Dialogue_Control.h"
54 #include "Items.h"
55 #include "Drugs_And_Alcohol.h"
56 #include "LOS.h"
57 #include "OppList.h"
58 #include "VObject_Blitters.h"
59 #include "Finances.h"
60 #include "LaptopSave.h"
61 #include "Cursor_Control.h"
62 #include "MessageBoxScreen.h"
63 #include "WordWrap.h"
64 #include "Boxing.h"
65 #include "Video.h"
66 #include "Debug.h"
67 #include "JAScreens.h"
68 #include "ScreenIDs.h"
69 #include "UILayout.h"
70
71 #include "ContentManager.h"
72 #include "GameInstance.h"
73 #include "WeaponModels.h"
74 #include "Logger.h"
75
76 #include "policy/GamePolicy.h"
77 #include "HImage.h"
78
79 #include <string_theory/format>
80 #include <string_theory/string>
81
82 #include <algorithm>
83 #include <iterator>
84
85 // DEFINES FOR VARIOUS PANELS
86 #define SM_ITEMDESC_START_X 214
87 #define SM_ITEMDESC_START_Y 1 + INV_INTERFACE_START_Y
88 #define SM_ITEMDESC_HEIGHT 128
89 #define SM_ITEMDESC_WIDTH 314
90
91 // SINGLE MERC SCREEN
92
93 #define SM_SELMERC_AP_X 67
94 #define SM_SELMERC_AP_Y 53
95 #define SM_SELMERC_AP_HEIGHT 10
96 #define SM_SELMERC_AP_WIDTH 17
97
98 #define SM_SELMERC_HEALTH_X 69
99 #define SM_SELMERC_HEALTH_Y 47
100
101 #define SM_SELMERCNAME_X 11
102 #define SM_SELMERCNAME_Y 53
103 #define SM_SELMERCNAME_WIDTH 53
104 #define SM_SELMERCNAME_HEIGHT 10
105
106 #define SM_SELMERC_FACE_X 13
107 #define SM_SELMERC_FACE_Y 6
108 #define SM_SELMERC_FACE_HEIGHT 42
109 #define SM_SELMERC_FACE_WIDTH 48
110
111 #define SM_SELMERC_PLATE_X 4
112 #define SM_SELMERC_PLATE_Y 2
113 #define SM_SELMERC_PLATE_HEIGHT 65
114 #define SM_SELMERC_PLATE_WIDTH 83
115
116
117 #define STATS_TITLE_FONT_COLOR 6
118 #define STATS_TEXT_FONT_COLOR 5
119
120
121 #define SM_TALKB_X 155
122 #define SM_TALKB_Y 108
123 #define SM_MUTEB_X 91
124 #define SM_MUTEB_Y 108
125 #define SM_STANCEUPB_X 187
126 #define SM_STANCEUPB_Y 40
127 #define SM_UPDOWNB_X 91
128 #define SM_UPDOWNB_Y 73
129 #define SM_CLIMBB_X 187
130 #define SM_CLIMBB_Y 8
131 #define SM_STANCEDOWNB_X 187
132 #define SM_STANCEDOWNB_Y 108
133 #define SM_HANDCURSORB_X 123
134 #define SM_HANDCURSORB_Y 73
135 #define SM_PREVMERCB_X 9
136 #define SM_PREVMERCB_Y 70
137 #define SM_NEXTMERCB_X 51
138 #define SM_NEXTMERCB_Y 70
139 #define SM_OPTIONSB_X 9
140 #define SM_OPTIONSB_Y 105
141 #define SM_BURSTMODEB_X 155
142 #define SM_BURSTMODEB_Y 73
143 #define SM_LOOKB_X 123
144 #define SM_LOOKB_Y 108
145 #define SM_STEALTHMODE_X 187
146 #define SM_STEALTHMODE_Y 73
147 #define SM_DONE_X 543
148 #define SM_DONE_Y 4
149 #define SM_MAPSCREEN_X 589
150 #define SM_MAPSCREEN_Y 4
151
152
153 #define SM_PERCENT_WIDTH 20
154 #define SM_PERCENT_HEIGHT 10
155 #define SM_ARMOR_X 347
156 #define SM_ARMOR_Y 79
157 #define SM_ARMOR_LABEL_X 363
158 #define SM_ARMOR_LABEL_Y 69
159 #define SM_ARMOR_PERCENT_X 368
160 #define SM_ARMOR_PERCENT_Y 79
161
162 #define SM_WEIGHT_LABEL_X 430
163 #define SM_WEIGHT_LABEL_Y 107
164 #define SM_WEIGHT_PERCENT_X 449
165 #define SM_WEIGHT_PERCENT_Y 106
166 #define SM_WEIGHT_X 428
167 #define SM_WEIGHT_Y 106
168
169 #define SM_CAMO_LABEL_X 430
170 #define SM_CAMO_LABEL_Y 122
171 #define SM_CAMO_PERCENT_X 449
172 #define SM_CAMO_PERCENT_Y 121
173 #define SM_CAMO_X 428
174 #define SM_CAMO_Y 121
175
176
177 #define SM_STATS_WIDTH 30
178 #define SM_STATS_HEIGHT 8
179 #define SM_AGI_X 99
180 #define SM_AGI_Y 7
181 #define SM_DEX_X 99
182 #define SM_DEX_Y 17
183 #define SM_STR_X 99
184 #define SM_STR_Y 27
185 #define SM_CHAR_X 99
186 #define SM_CHAR_Y 37
187 #define SM_WIS_X 99
188 #define SM_WIS_Y 47
189 #define SM_EXPLVL_X 148
190 #define SM_EXPLVL_Y 7
191 #define SM_MRKM_X 148
192 #define SM_MRKM_Y 17
193 #define SM_EXPL_X 148
194 #define SM_EXPL_Y 27
195 #define SM_MECH_X 148
196 #define SM_MECH_Y 37
197 #define SM_MED_X 148
198 #define SM_MED_Y 47
199
200 #define MONEY_X 460
201 #define MONEY_Y 105
202 #define MONEY_WIDTH 30
203 #define MONEY_HEIGHT 22
204
205 #define TM_FACE_X 14
206 #define TM_FACE_Y 6
207 #define TM_FACE_WIDTH 48
208 #define TM_FACE_HEIGHT 43
209
210 #define TM_ENDTURN_X 507
211 #define TM_ENDTURN_Y 9
212 #define TM_ROSTERMODE_X 507
213 #define TM_ROSTERMODE_Y 45
214 #define TM_DISK_X 507
215 #define TM_DISK_Y 81
216
217 #define TM_NAME_X 11
218 #define TM_NAME_Y 53
219 #define TM_NAME_WIDTH 53
220 #define TM_NAME_HEIGHT 10
221
222 #define TM_AP_X 67
223 #define TM_AP_Y 53
224 #define TM_AP_WIDTH 18
225 #define TM_AP_HEIGHT 10
226
227 #define TM_LIFEBAR_HEIGHT 42
228
229 #define TM_FACEHIGHTL_X 4
230 #define TM_FACEHIGHTL_Y 2
231 #define TM_FACEHIGHTL_WIDTH 84
232 #define TM_FACEHIGHTL_HEIGHT 114
233
234 #define TM_INV_WIDTH 58
235 #define TM_INV_HEIGHT 23
236 #define TM_INV_HAND1STARTX 8
237 #define TM_INV_HAND1STARTY 67
238 #define TM_INV_HAND_SEP 83
239 #define TM_INV_HAND_SEPY 24
240
241 #define TM_BARS_X 67
242 #define TM_BARS_Y 3
243 #define TM_BARS_WIDTH 19
244 #define TM_BARS_HEIGHT 47
245
246 #define INDICATOR_BOX_WIDTH 12
247 #define INDICATOR_BOX_HEIGHT 10
248
249
250 enum
251 {
252 STANCEUP_IMAGES = 0,
253 UPDOWN_IMAGES,
254 CLIMB_IMAGES,
255 STANCEDOWN_IMAGES,
256 HANDCURSOR_IMAGES,
257 PREVMERC_IMAGES,
258 NEXTMERC_IMAGES,
259 OPTIONS_IMAGES,
260 //BURSTMODE_IMAGES,
261 LOOK_IMAGES,
262 TALK_IMAGES,
263 MUTE_IMAGES,
264 STANCE_IMAGES,
265 DONE_IMAGES,
266 MAPSCREEN_IMAGES,
267 NUM_SM_BUTTON_IMAGES
268 };
269
270
271
272 enum
273 {
274 ENDTURN_IMAGES = 0,
275 ROSTERMODE_IMAGES,
276 DISK_IMAGES,
277 NUM_TEAM_BUTTON_IMAGES
278 };
279
280
281 static BUTTON_PICS* iSMPanelImages[NUM_SM_BUTTON_IMAGES];
282 static BUTTON_PICS* iBurstButtonImages[NUM_WEAPON_MODES];
283 static BUTTON_PICS* iTEAMPanelImages[NUM_TEAM_BUTTON_IMAGES];
284
285 static BUTTON_PICS* giSMStealthImages;
286 GUIButtonRef giSMStealthButton;
287
288 BOOLEAN gfUIStanceDifferent = FALSE;
289 static BOOLEAN gfAllDisabled = FALSE;
290
291 BOOLEAN gfSMDisableForItems = FALSE;
292
293 BOOLEAN gfDisableTacticalPanelButtons = FALSE;
294
295 BOOLEAN gfCheckForMouseOverItem = FALSE;
296 UINT32 guiMouseOverItemTime = 0;
297 INT8 gbCheckForMouseOverItemPos = 0;
298 SOLDIERTYPE* gSelectSMPanelToMerc = NULL;
299 static BOOLEAN gfReEvaluateDisabledINVPanelButtons = FALSE;
300
301
302 UINT8 gubHandPos;
303 UINT16 gusOldItemIndex;
304 UINT16 gusNewItemIndex;
305 BOOLEAN gfDeductPoints;
306
307
308 GUIButtonRef iSMPanelButtons[NUM_SM_BUTTONS];
309 GUIButtonRef iTEAMPanelButtons[NUM_TEAM_BUTTONS];
310
311 // Video Surface for Single Merc Panel
312 static SGPVObject* guiSMPanel;
313 static SGPVObject* guiSMObjects;
314 static SGPVObject* guiSMObjects2;
315
316 static SGPVObject* guiTEAMPanel;
317 static SGPVObject* guiTEAMObjects;
318 static SGPVObject* guiVEHINV;
319
320 static SGPVObject* guiCLOSE;
321
322 // Globals for various mouse regions
323 static MOUSE_REGION gSM_SELMERCPanelRegion;
324 static MOUSE_REGION gSM_SELMERCBarsRegion;
325 MOUSE_REGION gSM_SELMERCMoneyRegion;
326 static MOUSE_REGION gSM_SELMERCEnemyIndicatorRegion;
327 static MOUSE_REGION gTEAM_PanelRegion;
328
329
330 // Globals - for one - the current merc here
331 SOLDIERTYPE *gpSMCurrentMerc = NULL;
332
333 MOUSE_REGION gSMPanelRegion;
334
335
336 struct TeamPanelSlot
337 {
338 SOLDIERTYPE* merc;
339 MOUSE_REGION face;
340 MOUSE_REGION enemy_indicator;
341 MOUSE_REGION bars;
342 MOUSE_REGION left_bars;
343 MOUSE_REGION first_hand;
344 MOUSE_REGION second_hand;
345 };
346
347 static TeamPanelSlot gTeamPanel[NUM_TEAM_SLOTS];
348
349 #define FOR_EACH_TEAM_PANEL_SLOT(iter) FOR_EACH(TeamPanelSlot, iter, gTeamPanel)
350
351
352 // Wraps up check for AP-s get from a different soldier for in a vehicle...
GetUIApsToDisplay(SOLDIERTYPE const * s)353 static INT8 GetUIApsToDisplay(SOLDIERTYPE const* s)
354 {
355 if (s->uiStatusFlags & SOLDIER_DRIVER)
356 {
357 return GetSoldierStructureForVehicle(GetVehicle(s->iVehicleId)).bActionPoints;
358 }
359 return s->bActionPoints;
360 }
361
362
CheckForDisabledForGiveItem(void)363 void CheckForDisabledForGiveItem(void)
364 {
365 const SOLDIERTYPE* const cur = gpSMCurrentMerc;
366 Assert(cur != NULL);
367 Assert(cur->sGridNo != NOWHERE);
368
369 if (guiCurrentScreen == SHOPKEEPER_SCREEN)
370 {
371 gfSMDisableForItems = !CanMercInteractWithSelectedShopkeeper(cur);
372 return;
373 }
374
375 // Default to true
376 gfSMDisableForItems = TRUE;
377
378 // ATE: Is the current merc unconscious.....
379 if (cur->bLife < OKLIFE && gpItemPointer != NULL)
380 {
381 // Go through each merc and see if there is one closeby....
382 CFOR_EACH_IN_TEAM(s, OUR_TEAM)
383 {
384 if (s->bLife >= OKLIFE && !IsMechanical(*s) && s->bInSector && IsMercOnCurrentSquad(s))
385 {
386 const INT16 sDist = PythSpacesAway(cur->sGridNo, s->sGridNo);
387 const INT16 sDistVisible = DistanceVisible(s, DIRECTION_IRRELEVANT, DIRECTION_IRRELEVANT, cur->sGridNo, cur->bLevel);
388
389 // Check LOS....
390 if (SoldierTo3DLocationLineOfSightTest(s, cur->sGridNo, cur->bLevel, 3, sDistVisible, TRUE))
391 {
392 if (sDist <= PASSING_ITEM_DISTANCE_NOTOKLIFE)
393 {
394 gfSMDisableForItems = FALSE;
395 break; // found one, no need to keep looking
396 }
397 }
398 }
399 }
400 }
401 else
402 {
403 const SOLDIERTYPE* src = gpItemPointerSoldier;
404 if (src == NULL)
405 {
406 src = GetSelectedMan();
407 if (src == NULL)
408 {
409 gfSMDisableForItems = FALSE;
410 return;
411 }
412 }
413
414 // OK buddy, check our currently selected merc and disable/enable if not close enough...
415 if (cur == src)
416 {
417 gfSMDisableForItems = FALSE;
418 return;
419 }
420
421 const INT16 sDestGridNo = cur->sGridNo;
422 const INT8 bDestLevel = cur->bLevel;
423
424 // Get distance....
425 const INT16 sDist = PythSpacesAway(src->sGridNo, sDestGridNo);
426
427 // is he close enough to see that gridno if he turns his head?
428 const INT16 sDistVisible = DistanceVisible(src, DIRECTION_IRRELEVANT, DIRECTION_IRRELEVANT, sDestGridNo, bDestLevel);
429
430 // Check LOS....
431 if (SoldierTo3DLocationLineOfSightTest(src, sDestGridNo, bDestLevel, 3, sDistVisible, TRUE))
432 {
433 // UNCONSCIOUS GUYS ONLY 1 tile AWAY
434 if (cur->bLife < CONSCIOUSNESS)
435 {
436 if (sDist <= PASSING_ITEM_DISTANCE_NOTOKLIFE)
437 {
438 gfSMDisableForItems = FALSE;
439 }
440 }
441 else if (sDist <= PASSING_ITEM_DISTANCE_OKLIFE)
442 {
443 gfSMDisableForItems = FALSE;
444 }
445 }
446 }
447 }
448
449
450 static void UpdateSMPanel();
451
452
SetSMPanelCurrentMerc(SOLDIERTYPE * s)453 void SetSMPanelCurrentMerc(SOLDIERTYPE* s)
454 {
455 gSelectSMPanelToMerc = NULL;
456
457 gpSMCurrentMerc = s;
458
459 // Disable all faces
460 SetAllAutoFacesInactive( );
461
462 // Turn off compat ammo....
463 if ( gpItemPointer == NULL )
464 {
465 HandleCompatibleAmmoUI(s, HANDPOS, FALSE);
466 gfCheckForMouseOverItem = FALSE;
467 }
468 else
469 {
470 // Turn it all false first....
471 InternalHandleCompatibleAmmoUI(s, gpItemPointer, FALSE);
472 InternalHandleCompatibleAmmoUI(s, gpItemPointer, TRUE);
473 }
474
475 // Remove item desc panel if one up....
476 if ( gfInItemDescBox )
477 {
478 DeleteItemDescriptionBox( );
479 }
480
481 if ( gfInItemPickupMenu )
482 {
483 gfSMDisableForItems = TRUE;
484 }
485 else
486 {
487 if (gpItemPointer || guiCurrentScreen == SHOPKEEPER_SCREEN || s->bLife < OKLIFE)
488 {
489 CheckForDisabledForGiveItem( );
490 }
491 else
492 {
493 gfSMDisableForItems = FALSE;
494 }
495 }
496
497 if (gpItemPointer != NULL) ReevaluateItemHatches(s, FALSE);
498
499 DisableInvRegions( gfSMDisableForItems );
500
501 fInterfacePanelDirty = DIRTYLEVEL2;
502
503 gfUIStanceDifferent = TRUE;
504
505 UpdateSMPanel( );
506
507 }
508
509
510 static void HandleMouseOverSoldierFaceForContMove(SOLDIERTYPE* pSoldier, BOOLEAN fOn);
511 static bool IsMouseInRegion(MOUSE_REGION const&);
512
513
UpdateForContOverPortrait(SOLDIERTYPE * pSoldier,BOOLEAN fOn)514 void UpdateForContOverPortrait( SOLDIERTYPE *pSoldier, BOOLEAN fOn )
515 {
516 if ( gsCurInterfacePanel == SM_PANEL )
517 {
518 if ( gpSMCurrentMerc != NULL )
519 {
520 // Check if mouse is in region and if so, adjust...
521 if (IsMouseInRegion(gSM_SELMERCPanelRegion))
522 {
523 HandleMouseOverSoldierFaceForContMove( gpSMCurrentMerc, fOn );
524 }
525 }
526 }
527 else
528 {
529 FOR_EACH_TEAM_PANEL_SLOT(i)
530 {
531 if (i->merc != pSoldier)
532 continue;
533 if (!IsMouseInRegion(i->face))
534 continue;
535 HandleMouseOverSoldierFaceForContMove(pSoldier, fOn);
536 }
537 }
538 }
539
540
SetButtonState(UINT const idx,bool const clicked)541 static void SetButtonState(UINT const idx, bool const clicked)
542 {
543 GUI_BUTTON& b = *iSMPanelButtons[idx];
544 if (b.ubToggleButtonActivated)
545 return;
546 b.uiFlags &= ~BUTTON_CLICKED_ON;
547 b.uiFlags |= clicked ? BUTTON_CLICKED_ON : 0;
548 }
549
550
551 static void BtnStealthModeCallback(GUI_BUTTON* btn, INT32 reason);
552 static void CheckForReEvaluateDisabledINVPanelButtons(void);
553
554
UpdateSMPanel()555 static void UpdateSMPanel()
556 {
557 SOLDIERTYPE& s = *gpSMCurrentMerc;
558 if (s.sGridNo == NOWHERE) return;
559
560 UINT8 stance_state = s.ubDesiredHeight;
561 if (stance_state == NO_DESIRED_HEIGHT)
562 {
563 stance_state = gAnimControl[s.usAnimState].ubEndHeight;
564 }
565
566 INT32 stance_gfx; // XXX HACK000E
567 switch (stance_state)
568 {
569 case ANIM_STAND:
570 stance_gfx = 11;
571 DisableButton(iSMPanelButtons[STANCEUP_BUTTON]);
572 EnableButton(iSMPanelButtons[STANCEDOWN_BUTTON], IsValidStance(&s, ANIM_CROUCH));
573 break;
574
575 case ANIM_CROUCH:
576 stance_gfx = 5;
577 EnableButton(iSMPanelButtons[STANCEUP_BUTTON]);
578 EnableButton(iSMPanelButtons[STANCEDOWN_BUTTON], IsValidStance(&s, ANIM_PRONE));
579 break;
580
581 case ANIM_PRONE:
582 stance_gfx = 17;
583 EnableButton(iSMPanelButtons[STANCEUP_BUTTON]);
584 DisableButton(iSMPanelButtons[STANCEDOWN_BUTTON]);
585 break;
586
587 default: abort(); // HACK000E
588 }
589
590 // Stance button done whether we're disabled or not
591 if (gfUIStanceDifferent)
592 {
593 // Remove old
594 if (giSMStealthButton) RemoveButton(giSMStealthButton);
595 if (giSMStealthImages) UnloadButtonImage(giSMStealthImages);
596
597 // Make new
598 if (!s.bStealthMode) stance_gfx += 3;
599 giSMStealthImages = UseLoadedButtonImage(iSMPanelImages[STANCE_IMAGES], stance_gfx + 2, stance_gfx,
600 -1, stance_gfx + 1, -1);
601 giSMStealthButton = QuickCreateButton(giSMStealthImages, SM_STEALTHMODE_X,
602 INV_INTERFACE_START_Y + SM_STEALTHMODE_Y,
603 MSYS_PRIORITY_HIGH - 1, BtnStealthModeCallback);
604 giSMStealthButton->SetFastHelpText(TacticalStr[TOGGLE_STEALTH_MODE_POPUPTEXT]);
605
606 gfUIStanceDifferent = FALSE;
607
608 if (gfAllDisabled) DisableButton(giSMStealthButton);
609 }
610
611 if (gfAllDisabled) return;
612
613 CheckForReEvaluateDisabledINVPanelButtons();
614
615 // Check for any newly added items we need
616 if (s.fCheckForNewlyAddedItems)
617 {
618 // Startup any newly added items
619 CheckForAnyNewlyAddedItems(&s);
620 s.fCheckForNewlyAddedItems = FALSE;
621 }
622
623 // Set Disable/Enable UI based on buddy's stats
624 GUIButtonRef const burst = iSMPanelButtons[BURSTMODE_BUTTON];
625 if (burst->image != iBurstButtonImages[s.bWeaponMode])
626 {
627 burst->image = iBurstButtonImages[s.bWeaponMode];
628 burst->uiFlags |= BUTTON_DIRTY;
629 }
630
631 SetButtonState(MUTE_BUTTON, s.uiStatusFlags & SOLDIER_MUTE);
632
633 bool const enable_climb =
634 (FindLowerLevel(&s) && EnoughPoints(&s, GetAPsToClimbRoof(&s, TRUE), 0, FALSE)) ||
635 (FindHigherLevel(&s) && EnoughPoints(&s, GetAPsToClimbRoof(&s, FALSE), 0, FALSE)) ||
636 FindFenceJumpDirection(&s);
637 EnableButton(iSMPanelButtons[CLIMB_BUTTON], enable_climb);
638
639 EnableButton(iSMPanelButtons[SM_DONE_BUTTON], gTacticalStatus.ubCurrentTeam == OUR_TEAM && gTacticalStatus.uiFlags & INCOMBAT);
640
641 SetButtonState(UPDOWN_BUTTON, gsInterfaceLevel > 0);
642
643 SetButtonState(HANDCURSOR_BUTTON, gCurrentUIMode == HANDCURSOR_MODE);
644 SetButtonState(TALK_BUTTON, gCurrentUIMode == TALKCURSOR_MODE);
645 SetButtonState(LOOK_BUTTON, gCurrentUIMode == LOOKCURSOR_MODE);
646
647 // If not selected (or dead), disable/gray some buttons
648 if (&s != GetSelectedMan() ||
649 s.bLife< OKLIFE ||
650 gTacticalStatus.ubCurrentTeam != OUR_TEAM ||
651 gfSMDisableForItems)
652 {
653 DisableButton(iSMPanelButtons[CLIMB_BUTTON]);
654 DisableButton(iSMPanelButtons[BURSTMODE_BUTTON]);
655 DisableButton(iSMPanelButtons[STANCEUP_BUTTON]);
656 DisableButton(iSMPanelButtons[STANCEDOWN_BUTTON]);
657 DisableButton(iSMPanelButtons[LOOK_BUTTON]);
658 DisableButton(iSMPanelButtons[UPDOWN_BUTTON]);
659 DisableButton(iSMPanelButtons[HANDCURSOR_BUTTON]);
660 if (giSMStealthButton)
661 DisableButton(giSMStealthButton);
662 }
663 else
664 {
665 bool const enable_burst = IsGunBurstCapable(&s, HANDPOS) ||
666 FindAttachment(&s.inv[HANDPOS], UNDER_GLAUNCHER) != ITEM_NOT_FOUND;
667 EnableButton(iSMPanelButtons[BURSTMODE_BUTTON], enable_burst);
668 EnableButton(iSMPanelButtons[LOOK_BUTTON]);
669 EnableButton(iSMPanelButtons[UPDOWN_BUTTON]);
670 EnableButton(iSMPanelButtons[HANDCURSOR_BUTTON]);
671
672 if (giSMStealthButton)
673 EnableButton(giSMStealthButton);
674 }
675
676 // CJC Dec 4 2002: or if item pickup menu is up
677 EnableButton(iSMPanelButtons[SM_MAP_SCREEN_BUTTON],
678 !(gTacticalStatus.uiFlags & ENGAGED_IN_CONV) && !gfInItemPickupMenu);
679 }
680
681
ReevaluateItemHatches(SOLDIERTYPE * pSoldier,BOOLEAN fAllValid)682 void ReevaluateItemHatches(SOLDIERTYPE* pSoldier, BOOLEAN fAllValid)
683 {
684 INT32 cnt;
685
686 // if there's an item in the cursor and we're not supposed to just make them all valid
687 if ( ( gpItemPointer != NULL ) && !fAllValid )
688 {
689 // check all inventory positions and mark the ones where cursor item won't fit as invalid
690 for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
691 {
692 gbInvalidPlacementSlot[ cnt ] = !CanItemFitInPosition( pSoldier, gpItemPointer, (INT8)cnt, FALSE );
693
694 // !!! ATTACHING/MERGING ITEMS IN MAP SCREEN IS NOT SUPPORTED !!!
695 // CJC: seems to be supported now...
696 //if( guiCurrentScreen != MAP_SCREEN )
697 {
698 // Check attachments, override to valid placement if valid merge...
699 if ( ValidAttachment( gpItemPointer->usItem, pSoldier->inv[ cnt ].usItem ) )
700 {
701 gbInvalidPlacementSlot[ cnt ] = FALSE;
702 }
703
704 if ( ValidMerge( gpItemPointer->usItem, pSoldier->inv[ cnt ].usItem ) )
705 {
706 gbInvalidPlacementSlot[ cnt ] = FALSE;
707 }
708 }
709 }
710 }
711 else
712 {
713 // mark all inventory positions as valid
714 for ( cnt = 0; cnt < NUM_INV_SLOTS; cnt++ )
715 {
716 gbInvalidPlacementSlot[ cnt ] = FALSE;
717 }
718 }
719
720 fInterfacePanelDirty = DIRTYLEVEL2;
721 }
722
723
EnableSMPanelButtons(BOOLEAN fEnable,BOOLEAN fFromItemPickup)724 void EnableSMPanelButtons(BOOLEAN fEnable, BOOLEAN fFromItemPickup)
725 {
726 if ( fFromItemPickup )
727 {
728 // If we have the item pointer up...
729 // CJC Dec 4 2002: or if item pickup menu is up
730 //if ( gpItemPointer != NULL )
731 if ( gpItemPointer != NULL || gfInItemPickupMenu )
732 {
733 DisableTacticalTeamPanelButtons( TRUE );
734 }
735 else
736 {
737 DisableTacticalTeamPanelButtons( FALSE );
738 }
739
740 fInterfacePanelDirty = DIRTYLEVEL2;
741 }
742
743
744 if ( gsCurInterfacePanel == SM_PANEL )
745 {
746 if ( fFromItemPickup )
747 {
748 // If we have the item pointer up...
749 if ( gpItemPointer != NULL )
750 {
751 ReevaluateItemHatches( gpSMCurrentMerc, fEnable );
752
753 // Turn it all false first....
754 InternalHandleCompatibleAmmoUI( gpSMCurrentMerc, gpItemPointer, FALSE );
755 InternalHandleCompatibleAmmoUI( gpSMCurrentMerc, gpItemPointer, TRUE );
756
757 gfCheckForMouseOverItem = FALSE;
758
759 // Highlight guys ....
760 HandleAnyMercInSquadHasCompatibleStuff(gpItemPointer);
761
762 }
763 else
764 {
765 //InternalHandleCompatibleAmmoUI( gpSMCurrentMerc, gpItemPointer, FALSE );
766 gfCheckForMouseOverItem = FALSE;
767
768 HandleAnyMercInSquadHasCompatibleStuff(NULL);
769 }
770
771 if ( fEnable )
772 {
773 ReevaluateItemHatches( gpSMCurrentMerc, fEnable );
774 }
775
776 fInterfacePanelDirty = DIRTYLEVEL2;
777 }
778
779 if ( fEnable )
780 {
781 // only enable the following if NOT in shopkeeper's interface
782 if (guiCurrentScreen != SHOPKEEPER_SCREEN)
783 {
784 EnableButton( iSMPanelButtons[ CLIMB_BUTTON ] );
785 EnableButton( iSMPanelButtons[ BURSTMODE_BUTTON ] );
786 EnableButton( iSMPanelButtons[ STANCEUP_BUTTON ] );
787 EnableButton( iSMPanelButtons[ STANCEDOWN_BUTTON ] );
788 EnableButton( iSMPanelButtons[ LOOK_BUTTON ] );
789 EnableButton( iSMPanelButtons[ UPDOWN_BUTTON ] );
790 EnableButton( iSMPanelButtons[ HANDCURSOR_BUTTON ] );
791 if (giSMStealthButton) EnableButton(giSMStealthButton);
792
793 bool const enable = !gfDisableTacticalPanelButtons;
794 EnableButton(iSMPanelButtons[OPTIONS_BUTTON], enable);
795 EnableButton(iSMPanelButtons[SM_DONE_BUTTON], enable);
796 EnableButton(iSMPanelButtons[SM_MAP_SCREEN_BUTTON], enable);
797
798 //enable the radar map region
799 gRadarRegion.Enable();
800
801 gfSMDisableForItems = FALSE;
802
803 DisableInvRegions( gfSMDisableForItems );
804 }
805
806 if ( !fFromItemPickup )
807 {
808 EnableButton( iSMPanelButtons[ NEXTMERC_BUTTON ] );
809 EnableButton( iSMPanelButtons[ PREVMERC_BUTTON ] );
810 }
811 }
812 else
813 {
814 DisableButton( iSMPanelButtons[ CLIMB_BUTTON ] );
815 DisableButton( iSMPanelButtons[ BURSTMODE_BUTTON ] );
816 DisableButton( iSMPanelButtons[ STANCEUP_BUTTON ] );
817 DisableButton( iSMPanelButtons[ STANCEDOWN_BUTTON ] );
818 DisableButton( iSMPanelButtons[ LOOK_BUTTON ] );
819 DisableButton( iSMPanelButtons[ UPDOWN_BUTTON ] );
820 DisableButton( iSMPanelButtons[ HANDCURSOR_BUTTON ] );
821 if (giSMStealthButton) DisableButton(giSMStealthButton);
822
823 if ( !fFromItemPickup )
824 {
825 DisableButton( iSMPanelButtons[ NEXTMERC_BUTTON ] );
826 DisableButton( iSMPanelButtons[ PREVMERC_BUTTON ] );
827 }
828
829 DisableButton( iSMPanelButtons[ OPTIONS_BUTTON ] );
830 DisableButton( iSMPanelButtons[ SM_DONE_BUTTON ] );
831 DisableButton( iSMPanelButtons[ SM_MAP_SCREEN_BUTTON ] );
832
833 gRadarRegion.Disable();
834 }
835
836 gfAllDisabled = !fEnable;
837
838 }
839 }
840
841
842 static void SMInvClickCallback(MOUSE_REGION* pRegion, INT32 iReason);
843 static void SMInvClickCamoCallback(MOUSE_REGION* pRegion, INT32 iReason);
844 static void SMInvMoneyButtonCallback(MOUSE_REGION* pRegion, INT32 iReason);
845 static void SMInvMoveCallback(MOUSE_REGION* pRegion, INT32 iReason);
846 static void SMInvMoveCamoCallback(MOUSE_REGION* pRegion, INT32 iReason);
847 static void SelectedMercButtonCallback(MOUSE_REGION* pRegion, INT32 iReason);
848 static void SelectedMercButtonMoveCallback(MOUSE_REGION* pRegion, INT32 iReason);
849 static void SelectedMercEnemyIndicatorCallback(MOUSE_REGION* pRegion, INT32 iReason);
850
851
852 /** Fill empty space at the bottom of the screen. */
FillEmptySpaceAtBottom()853 static void FillEmptySpaceAtBottom()
854 {
855 if(g_ui.isBigScreen())
856 {
857 ColorFillVideoSurfaceArea(guiSAVEBUFFER, 640, g_ui.get_INV_INTERFACE_START_Y(),
858 g_ui.m_screenWidth, g_ui.m_screenHeight, 0);
859 }
860 }
861
InitializeSMPanel(void)862 void InitializeSMPanel(void)
863 {
864 guiSMPanel = AddVideoObjectFromFile(INTERFACEDIR "/inventory_bottom_panel.sti");
865 guiSMObjects = AddVideoObjectFromFile(INTERFACEDIR "/inventory_gold_front.sti");
866 guiSMObjects2 = AddVideoObjectFromFile(INTERFACEDIR "/inv_frn.sti");
867
868 FillEmptySpaceAtBottom();
869
870 // INit viewport region
871 // Set global mouse regions
872 // Define region for viewport
873 MSYS_DefineRegion(&gViewportRegion, 0, 0, gsVIEWPORT_END_X, gsVIEWPORT_WINDOW_END_Y, MSYS_PRIORITY_NORMAL, VIDEO_NO_CURSOR, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK);
874
875 // Create buttons
876 CreateSMPanelButtons();
877
878 const INT32 dy = INV_INTERFACE_START_Y;
879
880 // Set viewports
881 // Define region for panel
882 MSYS_DefineRegion(&gSMPanelRegion, 0, dy, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_NORMAL, CURSOR_NORMAL, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK);
883
884 INT32 x;
885 INT32 y;
886
887 x = SM_SELMERC_FACE_X;
888 y = dy + SM_SELMERC_FACE_Y;
889
890 //DEfine region for selected guy panel
891 MSYS_DefineRegion(&gSM_SELMERCPanelRegion, x, y, x + SM_SELMERC_FACE_WIDTH, y + SM_SELMERC_FACE_HEIGHT, MSYS_PRIORITY_NORMAL, MSYS_NO_CURSOR, SelectedMercButtonMoveCallback, SelectedMercButtonCallback);
892
893 //DEfine region for selected guy panel
894 MSYS_DefineRegion(&gSM_SELMERCEnemyIndicatorRegion, x + 1, y + 1, x + INDICATOR_BOX_WIDTH,
895 y + INDICATOR_BOX_HEIGHT, MSYS_PRIORITY_NORMAL, MSYS_NO_CURSOR,
896 MSYS_NO_CALLBACK, SelectedMercEnemyIndicatorCallback);
897
898 //DEfine region for money button
899 x = MONEY_X;
900 y = dy + MONEY_Y;
901 MSYS_DefineRegion(&gSM_SELMERCMoneyRegion, x, y, x + MONEY_WIDTH, y + MONEY_HEIGHT, MSYS_PRIORITY_HIGH, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, SMInvMoneyButtonCallback);
902 gSM_SELMERCMoneyRegion.SetFastHelpText(TacticalStr[MONEY_BUTTON_HELP_TEXT]);
903
904 // Check if mouse is in region and if so, adjust...
905 if (IsMouseInRegion(gSM_SELMERCPanelRegion))
906 {
907 HandleMouseOverSoldierFaceForContMove(gpSMCurrentMerc, TRUE);
908 }
909
910 //DEfine region for selected guy panel
911 MSYS_DefineRegion(&gSM_SELMERCBarsRegion, 62, dy + 2, 85, dy + 51, MSYS_PRIORITY_NORMAL, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, SelectedMercButtonCallback);
912
913 InitInvSlotInterface(g_ui.m_invSlotPositionTac, &g_ui.m_invCamoRegion, SMInvMoveCallback, SMInvClickCallback, SMInvMoveCamoCallback, SMInvClickCamoCallback);
914 InitKeyRingInterface(KeyRingItemPanelButtonCallback);
915
916 // this is important! It will disable buttons like SM_MAP_SCREEN_BUTTON when they're supposed to be
917 // disabled - the previous disabled state is lost everytime panel is reinitialized, because all the
918 // buttons are created from scratch!
919 if (gpItemPointer == NULL)
920 {
921 // empty cursor - enable, not from item pickup
922 EnableSMPanelButtons(TRUE, FALSE);
923 }
924 else
925 {
926 // full cursor - disable, from item pickup
927 EnableSMPanelButtons(FALSE, TRUE);
928 }
929 }
930
931
MakeButtonN(UINT idx,BUTTON_PICS * image,INT16 x,INT16 y,const GUI_CALLBACK click,const ST::string & help)932 static void MakeButtonN(UINT idx, BUTTON_PICS* image, INT16 x, INT16 y, const GUI_CALLBACK click, const ST::string& help)
933 try
934 {
935 GUIButtonRef const btn = QuickCreateButtonToggle(image, x, y, MSYS_PRIORITY_HIGH - 1, click);
936 iSMPanelButtons[idx] = btn;
937 btn->SetFastHelpText(help);
938 }
939 catch (...)
940 {
941 SLOGE("Cannot create Interface button");
942 throw;
943 }
944
945
MakeButtonT(UINT idx,BUTTON_PICS * image,INT16 x,INT16 y,const GUI_CALLBACK click,const ST::string & help)946 static void MakeButtonT(UINT idx, BUTTON_PICS* image, INT16 x, INT16 y, const GUI_CALLBACK click, const ST::string& help)
947 try
948 {
949 GUIButtonRef const btn = QuickCreateButton(image, x, y, MSYS_PRIORITY_HIGH - 1, click);
950 iSMPanelButtons[idx] = btn;
951 btn->SetFastHelpText(help);
952 }
953 catch (...)
954 {
955 SLOGE("Cannot create Interface button");
956 throw;
957 }
958
959
960 static void BtnBurstModeCallback(GUI_BUTTON* btn, INT32 reason);
961 static void BtnClimbCallback(GUI_BUTTON* btn, INT32 reason);
962 static void BtnHandCursorCallback(GUI_BUTTON* btn, INT32 reason);
963 static void BtnLookCallback(GUI_BUTTON* btn, INT32 reason);
964 static void BtnMapScreenCallback(GUI_BUTTON* btn, INT32 reason);
965 static void BtnMuteCallback(GUI_BUTTON* btn, INT32 reason);
966 static void BtnNextMercCallback(GUI_BUTTON* btn, INT32 reason);
967 static void BtnOptionsCallback(GUI_BUTTON* btn, INT32 reason);
968 static void BtnPrevMercCallback(GUI_BUTTON* btn, INT32 reason);
969 static void BtnSMDoneCallback(GUI_BUTTON* btn, INT32 reason);
970 static void BtnStanceDownCallback(GUI_BUTTON* btn, INT32 reason);
971 static void BtnStanceUpCallback(GUI_BUTTON* btn, INT32 reason);
972 static void BtnTalkCallback(GUI_BUTTON* btn, INT32 reason);
973 static void BtnUpdownCallback(GUI_BUTTON* btn, INT32 reason);
974
975
CreateSMPanelButtons(void)976 void CreateSMPanelButtons(void)
977 {
978 giSMStealthImages = NULL;
979 gfUIStanceDifferent = TRUE;
980 gfAllDisabled = FALSE;
981
982 // Load button Graphics
983 iSMPanelImages[STANCEUP_IMAGES] = LoadButtonImage(INTERFACEDIR "/inventory_buttons.sti", 0, 10);
984 iSMPanelImages[UPDOWN_IMAGES] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 9, 19);
985 iSMPanelImages[CLIMB_IMAGES] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 3, 13);
986 iSMPanelImages[STANCEDOWN_IMAGES] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 8, 18);
987 iSMPanelImages[HANDCURSOR_IMAGES] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 1, 11);
988 iSMPanelImages[PREVMERC_IMAGES] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 20, 22);
989 iSMPanelImages[NEXTMERC_IMAGES] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 21, 23);
990 #if 0
991 iSMPanelImages[BURSTMODE_IMAGES] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 7, 17);
992 #endif
993 iSMPanelImages[LOOK_IMAGES] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 2, 12);
994 iSMPanelImages[TALK_IMAGES] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 6, 16);
995 iSMPanelImages[MUTE_IMAGES] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 5, 15);
996 iSMPanelImages[OPTIONS_IMAGES] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 24, 25);
997
998 iBurstButtonImages[WM_NORMAL] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 7, 7);
999 iBurstButtonImages[WM_BURST] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 17, 17);
1000 iBurstButtonImages[WM_ATTACHED] = UseLoadedButtonImage(iSMPanelImages[STANCEUP_IMAGES], 26, 26);
1001
1002 iSMPanelImages[STANCE_IMAGES] = LoadButtonImage(INTERFACEDIR "/invadd-ons.sti", 0, 0, -1, 2, -1);
1003
1004 iSMPanelImages[DONE_IMAGES] = LoadButtonImage(INTERFACEDIR "/inventory_buttons_2.sti", 1, 3);
1005 iSMPanelImages[MAPSCREEN_IMAGES] = UseLoadedButtonImage(iSMPanelImages[DONE_IMAGES], 0, 2);
1006
1007
1008 // Create buttons
1009
1010 // SET BUTTONS TO -1
1011 std::fill(std::begin(iSMPanelButtons), std::end(iSMPanelButtons), GUIButtonRef::NoButton());
1012
1013 const INT32 dy = INV_INTERFACE_START_Y;
1014
1015 MakeButtonT(SM_MAP_SCREEN_BUTTON, iSMPanelImages[MAPSCREEN_IMAGES], SM_MAPSCREEN_X, dy + SM_MAPSCREEN_Y, BtnMapScreenCallback, TacticalStr[MAPSCREEN_POPUPTEXT]);
1016 MakeButtonT(SM_DONE_BUTTON, iSMPanelImages[DONE_IMAGES], SM_DONE_X, dy + SM_DONE_Y, BtnSMDoneCallback, TacticalStr[END_TURN_POPUPTEXT]);
1017 MakeButtonN(TALK_BUTTON, iSMPanelImages[TALK_IMAGES], SM_TALKB_X, dy + SM_TALKB_Y, BtnTalkCallback, TacticalStr[TALK_CURSOR_POPUPTEXT]);
1018 MakeButtonN(MUTE_BUTTON, iSMPanelImages[MUTE_IMAGES], SM_MUTEB_X, dy + SM_MUTEB_Y, BtnMuteCallback, TacticalStr[TOGGLE_MUTE_POPUPTEXT]);
1019 MakeButtonT(STANCEUP_BUTTON, iSMPanelImages[STANCEUP_IMAGES], SM_STANCEUPB_X, dy + SM_STANCEUPB_Y, BtnStanceUpCallback, TacticalStr[CHANGE_STANCE_UP_POPUPTEXT]);
1020 MakeButtonN(UPDOWN_BUTTON, iSMPanelImages[UPDOWN_IMAGES], SM_UPDOWNB_X, dy + SM_UPDOWNB_Y, BtnUpdownCallback, TacticalStr[CURSOR_LEVEL_POPUPTEXT]);
1021 MakeButtonT(CLIMB_BUTTON, iSMPanelImages[CLIMB_IMAGES], SM_CLIMBB_X, dy + SM_CLIMBB_Y, BtnClimbCallback, TacticalStr[JUMPCLIMB_POPUPTEXT]);
1022 MakeButtonT(STANCEDOWN_BUTTON, iSMPanelImages[STANCEDOWN_IMAGES], SM_STANCEDOWNB_X, dy + SM_STANCEDOWNB_Y, BtnStanceDownCallback, TacticalStr[CHANGE_STANCE_DOWN_POPUPTEXT]);
1023 MakeButtonN(HANDCURSOR_BUTTON, iSMPanelImages[HANDCURSOR_IMAGES], SM_HANDCURSORB_X, dy + SM_HANDCURSORB_Y, BtnHandCursorCallback, TacticalStr[EXAMINE_CURSOR_POPUPTEXT]);
1024 MakeButtonT(PREVMERC_BUTTON, iSMPanelImages[PREVMERC_IMAGES], SM_PREVMERCB_X, dy + SM_PREVMERCB_Y, BtnPrevMercCallback, TacticalStr[PREV_MERC_POPUPTEXT]);
1025 MakeButtonT(NEXTMERC_BUTTON, iSMPanelImages[NEXTMERC_IMAGES], SM_NEXTMERCB_X, dy + SM_NEXTMERCB_Y, BtnNextMercCallback, TacticalStr[NEXT_MERC_POPUPTEXT]);
1026 MakeButtonT(OPTIONS_BUTTON, iSMPanelImages[OPTIONS_IMAGES], SM_OPTIONSB_X, dy + SM_OPTIONSB_Y, BtnOptionsCallback, TacticalStr[CHANGE_OPTIONS_POPUPTEXT]);
1027 #if 0
1028 MakeButtonN(BURSTMODE_BUTTON, iSMPanelImages[BURSTMODE_IMAGES], SM_BURSTMODEB_X, dy + SM_BURSTMODEB_Y, BtnBurstModeCallback, TacticalStr[TOGGLE_BURSTMODE_POPUPTEXT]);
1029 #endif
1030 MakeButtonT(BURSTMODE_BUTTON, iBurstButtonImages[WM_NORMAL], SM_BURSTMODEB_X, dy + SM_BURSTMODEB_Y, BtnBurstModeCallback, TacticalStr[TOGGLE_BURSTMODE_POPUPTEXT]);
1031 MakeButtonN(LOOK_BUTTON, iSMPanelImages[LOOK_IMAGES], SM_LOOKB_X, dy + SM_LOOKB_Y, BtnLookCallback, TacticalStr[LOOK_CURSOR_POPUPTEXT]);
1032 }
1033
1034
RemoveSMPanelButtons(void)1035 void RemoveSMPanelButtons(void)
1036 {
1037 for (UINT32 cnt = 0; cnt < NUM_SM_BUTTONS; ++cnt)
1038 {
1039 if (iSMPanelButtons[cnt]) RemoveButton(iSMPanelButtons[cnt]);
1040 }
1041
1042 for (UINT32 cnt = 0; cnt < NUM_SM_BUTTON_IMAGES; ++cnt)
1043 {
1044 UnloadButtonImage(iSMPanelImages[cnt]);
1045 }
1046
1047 if (giSMStealthButton) RemoveButton(giSMStealthButton);
1048 if (giSMStealthImages) UnloadButtonImage(giSMStealthImages);
1049
1050 UnloadButtonImage(iBurstButtonImages[WM_NORMAL]);
1051 UnloadButtonImage(iBurstButtonImages[WM_BURST]);
1052 UnloadButtonImage(iBurstButtonImages[WM_ATTACHED]);
1053 }
1054
1055
ShutdownSMPanel(void)1056 void ShutdownSMPanel(void)
1057 {
1058 // All buttons and regions and video objects and video surfaces will be deleted at shutddown of SGM
1059 // We may want to delete them at the interm as well, to free up room for other panels
1060 DeleteVideoObject(guiSMPanel);
1061 DeleteVideoObject(guiSMObjects);
1062 DeleteVideoObject(guiSMObjects2);
1063
1064 gSelectSMPanelToMerc = NULL;
1065
1066 // CJC: delete key ring if open
1067 DeleteKeyRingPopup(); // function will abort if key ring is not up
1068
1069 // ATE: Delete desc panel if it was open....
1070 if (gfInItemDescBox)
1071 {
1072 DeleteItemDescriptionBox();
1073 }
1074
1075 ShutdownInvSlotInterface();
1076 ShutdownKeyRingInterface();
1077
1078 MSYS_RemoveRegion(&gSMPanelRegion);
1079 MSYS_RemoveRegion(&gSM_SELMERCPanelRegion);
1080 MSYS_RemoveRegion(&gSM_SELMERCBarsRegion);
1081 MSYS_RemoveRegion(&gSM_SELMERCMoneyRegion);
1082 MSYS_RemoveRegion(&gSM_SELMERCEnemyIndicatorRegion);
1083
1084 HandleMouseOverSoldierFaceForContMove(gpSMCurrentMerc, FALSE);
1085
1086 MSYS_RemoveRegion(&gViewportRegion);
1087
1088 RemoveSMPanelButtons();
1089 }
1090
1091
PrintAP(SOLDIERTYPE * const s,INT16 const x,INT16 const y,INT16 const w,INT16 const h)1092 static void PrintAP(SOLDIERTYPE* const s, INT16 const x, INT16 const y, INT16 const w, INT16 const h)
1093 {
1094 if (!(gTacticalStatus.uiFlags & INCOMBAT))
1095 return;
1096 if (s->bLife < OKLIFE)
1097 return;
1098
1099 INT8 const ap = GetUIApsToDisplay(s);
1100 UINT8 const min_ap = MinAPsToAttack(s, s->sLastTarget, TRUE);
1101 UINT8 const foreground =
1102 !EnoughPoints(s, min_ap, 0, FALSE) ? FONT_MCOLOR_DKRED :
1103 ap < 0 ? FONT_MCOLOR_DKRED :
1104 MercUnderTheInfluence(s) ? FONT_MCOLOR_LTBLUE :
1105 s->bStealthMode ? FONT_MCOLOR_LTYELLOW :
1106 FONT_MCOLOR_LTGRAY;
1107 SetFontAttributes(TINYFONT1, foreground);
1108
1109 RestoreExternBackgroundRect(x, y, w, h);
1110 ST::string buf = ST::format("{}", ap);
1111 INT16 sFontX;
1112 INT16 sFontY;
1113 FindFontCenterCoordinates(x, y, w, h, buf, TINYFONT1, &sFontX, &sFontY);
1114 MPrint(sFontX, sFontY, buf);
1115 }
1116
1117
SetStatsHelp(MOUSE_REGION & r,SOLDIERTYPE const & s)1118 static void SetStatsHelp(MOUSE_REGION& r, SOLDIERTYPE const& s)
1119 {
1120 ST::string help;
1121 ST::string text;
1122 if (s.bLife != 0)
1123 {
1124 if (s.uiStatusFlags & SOLDIER_VEHICLE)
1125 {
1126 text = st_format_printf(TacticalStr[VEHICLE_VITAL_STATS_POPUPTEXT], s.bLife, s.bLifeMax, s.bBreath, s.bBreathMax);
1127 }
1128 else if (s.uiStatusFlags & SOLDIER_ROBOT)
1129 {
1130 text = st_format_printf(gzLateLocalizedString[STR_LATE_16], s.bLife, s.bLifeMax);
1131 }
1132 else
1133 {
1134 text = st_format_printf(TacticalStr[MERC_VITAL_STATS_POPUPTEXT], s.bLife, s.bLifeMax, s.bBreath, s.bBreathMax, GetMoraleString(s));
1135 }
1136 help = text;
1137 }
1138 r.SetFastHelpText(help);
1139 }
1140
ProgressBarBackgroundRect(const INT16 sLeft,const INT16 sTop,const INT16 sWidth,const INT16 sHeight,const UINT32 rgb,const UINT8 scale_rgb)1141 void ProgressBarBackgroundRect(const INT16 sLeft, const INT16 sTop, const INT16 sWidth, const INT16 sHeight, const UINT32 rgb, const UINT8 scale_rgb)
1142 {
1143 SGPVSurface::Lock l(guiSAVEBUFFER);
1144
1145 #define s(a) ((a) / 2) + ((a) / 2) * (scale_rgb) / 100
1146
1147 const int r = s(0xff & rgb >> 16);
1148 const int g = s(0xff & rgb >> 8);
1149 const int b = s(0xff & rgb);
1150
1151 const UINT16 fill_color = Get16BPPColor((b << 16) + (g << 8) + r);
1152
1153 UINT16* const dst = l.Buffer<UINT16>();
1154
1155 for (int y = 0; y < sHeight; ++y)
1156 {
1157 for(int x = 0; x < sWidth; ++x)
1158 {
1159 dst[(y + sTop)*l.Pitch() / 2 + sLeft + x] = fill_color;
1160 }
1161 }
1162 }
1163
PrintStat(UINT32 const change_time,UINT16 const stat_bit,INT8 const stat_val,INT16 const x,INT16 const y,INT32 const progress)1164 static void PrintStat(UINT32 const change_time, UINT16 const stat_bit, INT8 const stat_val, INT16 const x, INT16 const y, INT32 const progress)
1165 {
1166 SOLDIERTYPE const& s = *gpSMCurrentMerc;
1167
1168 UINT8 const fg =
1169 s.bLife < OKLIFE ? FONT_MCOLOR_DKGRAY :
1170 GetJA2Clock() >= CHANGE_STAT_RECENTLY_DURATION + change_time ? STATS_TEXT_FONT_COLOR :
1171 change_time == 0 ? STATS_TEXT_FONT_COLOR :
1172 s.usValueGoneUp & stat_bit ? FONT_LTGREEN :
1173 FONT_RED;
1174
1175 SetFontForeground(fg);
1176
1177 ST::string str = ST::format("{3d}", stat_val);
1178 if (gamepolicy(gui_extras))
1179 {
1180 ProgressBarBackgroundRect(x + 16, y - 2, 15 * progress / 100, 10, 0x514A05, progress);
1181 }
1182
1183 DrawStringRight(str, x, y, SM_STATS_WIDTH, SM_STATS_HEIGHT, BLOCKFONT2);
1184 }
1185
RenderSMPanel(DirtyLevel * const dirty_level)1186 void RenderSMPanel(DirtyLevel* const dirty_level)
1187 {
1188 // Give him the panel
1189 if (gSelectSMPanelToMerc) SetSMPanelCurrentMerc(gSelectSMPanelToMerc);
1190
1191 // ATE: Don't do anything if we are in stack popup and are refreshing stuff
1192 if ((InItemStackPopup() || InKeyRingPopup()) && *dirty_level == DIRTYLEVEL1)
1193 return;
1194
1195 if (!gpSMCurrentMerc) return;
1196
1197 SOLDIERTYPE& s = *gpSMCurrentMerc;
1198
1199 if (gfCheckForMouseOverItem && GetJA2Clock() - guiMouseOverItemTime > 100)
1200 {
1201 if (HandleCompatibleAmmoUI(&s, (INT8)gbCheckForMouseOverItemPos, TRUE))
1202 {
1203 *dirty_level = DIRTYLEVEL2;
1204 }
1205
1206 gfCheckForMouseOverItem = FALSE;
1207 }
1208
1209 HandleNewlyAddedItems(s, dirty_level);
1210
1211 if (InItemDescriptionBox()) HandleItemDescriptionBox(dirty_level);
1212
1213 INT32 const dy = INV_INTERFACE_START_Y;
1214
1215 if (*dirty_level == DIRTYLEVEL2)
1216 {
1217 BltVideoObject(guiSAVEBUFFER, guiSMPanel, 0, INTERFACE_START_X, dy);
1218
1219 {
1220 SGPVObject const* gfx;
1221 if (gfSMDisableForItems)
1222 {
1223 gfx = guiSMObjects2;
1224 }
1225 else if (gTacticalStatus.ubCurrentTeam == OUR_TEAM &&
1226 &s == GetSelectedMan() &&
1227 OK_INTERRUPT_MERC(&s))
1228 {
1229 gfx = guiSMObjects;
1230 }
1231 else
1232 {
1233 goto no_plate;
1234 }
1235 INT32 const x = SM_SELMERC_PLATE_X;
1236 INT32 const y = SM_SELMERC_PLATE_Y + dy;
1237 BltVideoObject(guiSAVEBUFFER, gfx, 0, x, y);
1238 RestoreExternBackgroundRect(x, y, SM_SELMERC_PLATE_WIDTH, SM_SELMERC_PLATE_HEIGHT);
1239 }
1240 no_plate:
1241
1242 RenderSoldierFace(s, SM_SELMERC_FACE_X, dy + SM_SELMERC_FACE_Y);
1243
1244 if (InItemDescriptionBox())
1245 {
1246 RenderItemDescriptionBox();
1247 }
1248 else
1249 {
1250 RenderInvBodyPanel(&s, SM_BODYINV_X, SM_BODYINV_Y);
1251
1252 // Render Values for stats
1253 SetFontDestBuffer(guiSAVEBUFFER);
1254 SetFontAttributes(BLOCKFONT2, STATS_TITLE_FONT_COLOR);
1255 for (UINT32 i = 0; i != 5; ++i)
1256 {
1257 INT32 const y = dy + 7 + i * 10;
1258 MPrint( 92, y, pShortAttributeStrings[i]);
1259 MPrint(137, y, pShortAttributeStrings[i + 5]);
1260 }
1261
1262 MPrint(SM_ARMOR_LABEL_X - StringPixLength(pInvPanelTitleStrings[0], BLOCKFONT2) / 2, dy + SM_ARMOR_LABEL_Y, pInvPanelTitleStrings[0]);
1263 MPrint(SM_ARMOR_PERCENT_X, dy + SM_ARMOR_PERCENT_Y, "%");
1264
1265 MPrint(SM_WEIGHT_LABEL_X - StringPixLength(pInvPanelTitleStrings[1], BLOCKFONT2), dy + SM_WEIGHT_LABEL_Y, pInvPanelTitleStrings[1]);
1266 MPrint(SM_WEIGHT_PERCENT_X, dy + SM_WEIGHT_PERCENT_Y, "%");
1267
1268 MPrint(SM_CAMO_LABEL_X - StringPixLength(pInvPanelTitleStrings[2], BLOCKFONT2), dy + SM_CAMO_LABEL_Y, pInvPanelTitleStrings[2]);
1269 MPrint(SM_CAMO_PERCENT_X, dy + SM_CAMO_PERCENT_Y, "%");
1270
1271 MERCPROFILESTRUCT& p = GetProfile(s.ubProfile);
1272 PrintStat(s.uiChangeAgilityTime, AGIL_INCREASE, s.bAgility, SM_AGI_X, dy + SM_AGI_Y, p.sAgilityGain*2);
1273 PrintStat(s.uiChangeDexterityTime, DEX_INCREASE, s.bDexterity, SM_DEX_X, dy + SM_DEX_Y, p.sDexterityGain*2);
1274 PrintStat(s.uiChangeStrengthTime, STRENGTH_INCREASE, s.bStrength, SM_STR_X, dy + SM_STR_Y, p.sStrengthGain*2);
1275 PrintStat(s.uiChangeLeadershipTime, LDR_INCREASE, s.bLeadership, SM_CHAR_X, dy + SM_CHAR_Y, p.sLeadershipGain*2);
1276 PrintStat(s.uiChangeWisdomTime, WIS_INCREASE, s.bWisdom, SM_WIS_X, dy + SM_WIS_Y, p.sWisdomGain*2);
1277 PrintStat(s.uiChangeLevelTime, LVL_INCREASE, s.bExpLevel, SM_EXPLVL_X, dy + SM_EXPLVL_Y, p.sExpLevelGain*100/(350*p.bExpLevel));
1278 PrintStat(s.uiChangeMarksmanshipTime, MRK_INCREASE, s.bMarksmanship, SM_MRKM_X, dy + SM_MRKM_Y, p.sMarksmanshipGain*4);
1279 PrintStat(s.uiChangeExplosivesTime, EXP_INCREASE, s.bExplosive, SM_EXPL_X, dy + SM_EXPL_Y, p.sExplosivesGain*4);
1280 PrintStat(s.uiChangeMechanicalTime, MECH_INCREASE, s.bMechanical, SM_MECH_X, dy + SM_MECH_Y, p.sMechanicGain*4);
1281 PrintStat(s.uiChangeMedicalTime, MED_INCREASE, s.bMedical, SM_MED_X, dy + SM_MED_Y, p.sMedicalGain*4);
1282
1283 SetFontForeground(s.bLife >= OKLIFE ? STATS_TEXT_FONT_COLOR : FONT_MCOLOR_DKGRAY);
1284
1285 INT16 usX;
1286 INT16 usY;
1287 ST::string sString;
1288
1289 // Display armour value
1290 sString = ST::format("{3d}", ArmourPercent(&s));
1291 FindFontRightCoordinates(SM_ARMOR_X, dy + SM_ARMOR_Y, SM_PERCENT_WIDTH, SM_PERCENT_HEIGHT, sString, BLOCKFONT2, &usX, &usY);
1292 MPrint(usX, usY , sString);
1293
1294 // Display weight value
1295 sString = ST::format("{3d}", CalculateCarriedWeight(&s));
1296 FindFontRightCoordinates(SM_WEIGHT_X, dy + SM_WEIGHT_Y, SM_PERCENT_WIDTH, SM_PERCENT_HEIGHT, sString, BLOCKFONT2, &usX, &usY);
1297 MPrint(usX, usY, sString);
1298
1299 // Display camo value
1300 sString = ST::format("{3d}", s.bCamo);
1301 FindFontRightCoordinates(SM_CAMO_X, dy + SM_CAMO_Y, SM_PERCENT_WIDTH, SM_PERCENT_HEIGHT, sString, BLOCKFONT2, &usX, &usY);
1302 MPrint(usX, usY, sString);
1303
1304 SetFontDestBuffer(FRAME_BUFFER);
1305
1306 RestoreExternBackgroundRect(INTERFACE_START_X, INV_INTERFACE_START_Y, SCREEN_WIDTH - INTERFACE_START_X, SCREEN_HEIGHT - INV_INTERFACE_START_Y);
1307 }
1308
1309 // Render name
1310 UINT8 const fg = s.bStealthMode ? FONT_MCOLOR_LTYELLOW : FONT_MCOLOR_LTGRAY;
1311 SetFontAttributes(BLOCKFONT2, fg);
1312
1313 INT32 const x = SM_SELMERCNAME_X;
1314 INT32 const y = SM_SELMERCNAME_Y + dy;
1315 INT32 const w = SM_SELMERCNAME_WIDTH;
1316 INT32 const h = SM_SELMERCNAME_HEIGHT;
1317 RestoreExternBackgroundRect(x, y, w, h);
1318 INT16 sFontX;
1319 INT16 sFontY;
1320 FindFontCenterCoordinates(x, y, w, h, s.name, BLOCKFONT2, &sFontX, &sFontY);
1321 MPrint(sFontX, sFontY, s.name);
1322 }
1323
1324 if (*dirty_level != DIRTYLEVEL0)
1325 {
1326 SetStatsHelp(gSM_SELMERCBarsRegion, s);
1327
1328 // Display AP
1329 if (!(s.uiStatusFlags & SOLDIER_DEAD))
1330 {
1331 PrintAP(&s, SM_SELMERC_AP_X, dy + SM_SELMERC_AP_Y, SM_SELMERC_AP_WIDTH, SM_SELMERC_AP_HEIGHT);
1332 DrawSoldierUIBars(s, SM_SELMERC_HEALTH_X, dy + SM_SELMERC_HEALTH_Y, TRUE, FRAME_BUFFER);
1333 }
1334 }
1335
1336 UpdateSMPanel();
1337
1338 // Render items in guy's hand
1339 HandleRenderInvSlots(s, *dirty_level);
1340
1341 if (gfSMDisableForItems && *dirty_level != DIRTYLEVEL0)
1342 {
1343 SGPRect ClipRect;
1344 ClipRect.iLeft = 87;
1345 ClipRect.iRight = 536;
1346 ClipRect.iTop = INV_INTERFACE_START_Y;
1347 ClipRect.iBottom = SCREEN_HEIGHT;
1348 SGPVSurface::Lock l(FRAME_BUFFER);
1349 Blt16BPPBufferHatchRect(l.Buffer<UINT16>(), l.Pitch(), &ClipRect);
1350 }
1351 }
1352
1353
SMInvMoveCallback(MOUSE_REGION * pRegion,INT32 iReason)1354 static void SMInvMoveCallback(MOUSE_REGION* pRegion, INT32 iReason)
1355 {
1356 UINT32 uiHandPos;
1357
1358 uiHandPos = MSYS_GetRegionUserData( pRegion, 0 );
1359
1360 if ( gpSMCurrentMerc->inv[ uiHandPos ].usItem == NOTHING )
1361 return;
1362
1363 if (iReason == MSYS_CALLBACK_REASON_GAIN_MOUSE)
1364 {
1365 if ( gpItemPointer == NULL )
1366 {
1367 // Setup a timer....
1368 guiMouseOverItemTime = GetJA2Clock( );
1369 gfCheckForMouseOverItem = TRUE;
1370 gbCheckForMouseOverItemPos = (INT8)uiHandPos;
1371 }
1372 }
1373 if (iReason == MSYS_CALLBACK_REASON_LOST_MOUSE )
1374 {
1375 if ( gpItemPointer == NULL )
1376 {
1377 HandleCompatibleAmmoUI( gpSMCurrentMerc, (INT8)uiHandPos, FALSE );
1378 gfCheckForMouseOverItem = FALSE;
1379 fInterfacePanelDirty = DIRTYLEVEL2;
1380 }
1381 }
1382 }
1383
1384
SMInvMoveCamoCallback(MOUSE_REGION * const pRegion,const INT32 iReason)1385 static void SMInvMoveCamoCallback(MOUSE_REGION* const pRegion, const INT32 iReason)
1386 {
1387 if (iReason == MSYS_CALLBACK_REASON_GAIN_MOUSE )
1388 {
1389 // Setup a timer....
1390 guiMouseOverItemTime = GetJA2Clock( );
1391 gfCheckForMouseOverItem = TRUE;
1392 gbCheckForMouseOverItemPos = NO_SLOT;
1393 }
1394 if (iReason == MSYS_CALLBACK_REASON_LOST_MOUSE )
1395 {
1396 HandleCompatibleAmmoUI( gpSMCurrentMerc, (INT8)NO_SLOT, FALSE );
1397 gfCheckForMouseOverItem = FALSE;
1398 }
1399 }
1400
1401
SMInvClickCamoCallback(MOUSE_REGION * pRegion,INT32 iReason)1402 static void SMInvClickCamoCallback(MOUSE_REGION* pRegion, INT32 iReason)
1403 {
1404 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
1405 {
1406 // Apply camo (if we have something in cursor...)
1407 // If we do not have an item in hand, start moving it
1408 OBJECTTYPE* const obj = gpItemPointer;
1409 if (obj == NULL) return;
1410
1411 SOLDIERTYPE* const s = gpSMCurrentMerc;
1412 if (s->bLife < CONSCIOUSNESS) return;
1413
1414 BOOLEAN fGoodAPs;
1415 // Try to apply camo....
1416 if (ApplyCamo(s, obj, &fGoodAPs))
1417 {
1418 if (!fGoodAPs) return;
1419
1420 // Say OK acknowledge....
1421 DoMercBattleSound(s, BATTLE_SOUND_COOL1);
1422 }
1423 else if (ApplyCanteen(s, obj, &fGoodAPs))
1424 {
1425 if (!fGoodAPs) return;
1426 }
1427 else if (ApplyElixir(s, obj, &fGoodAPs))
1428 {
1429 if (!fGoodAPs) return;
1430
1431 // Say OK acknowledge....
1432 DoMercBattleSound(s, BATTLE_SOUND_COOL1);
1433 }
1434 else if (ApplyDrugs(s, obj))
1435 {
1436 // Say OK acknowledge....
1437 DoMercBattleSound(s, BATTLE_SOUND_COOL1);
1438 }
1439 else
1440 {
1441 ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, TacticalStr[CANNOT_DO_INV_STUFF_STR]);
1442 return;
1443 }
1444
1445 fInterfacePanelDirty = DIRTYLEVEL2;
1446
1447 // Check if it's the same now!
1448 if (obj->ubNumberOfObjects == 0)
1449 {
1450 gbCompatibleApplyItem = FALSE;
1451 EndItemPointer();
1452 }
1453 }
1454 }
1455
1456
HandleNailsVestFetish(const SOLDIERTYPE * const s,const UINT32 uiHandPos,const UINT16 usReplaceItem)1457 BOOLEAN HandleNailsVestFetish(const SOLDIERTYPE* const s, const UINT32 uiHandPos, const UINT16 usReplaceItem)
1458 {
1459 if (s->ubProfile != NAILS) return FALSE;
1460 if (uiHandPos != VESTPOS) return FALSE;
1461
1462 switch (usReplaceItem)
1463 {
1464 case LEATHER_JACKET:
1465 case LEATHER_JACKET_W_KEVLAR:
1466 case LEATHER_JACKET_W_KEVLAR_18:
1467 case LEATHER_JACKET_W_KEVLAR_Y:
1468 case COMPOUND18:
1469 case JAR_QUEEN_CREATURE_BLOOD:
1470 return FALSE;
1471
1472 default:
1473 TacticalCharacterDialogue(s, 61);
1474 return TRUE;
1475 }
1476 }
1477
1478
UIHandleItemPlacement(UINT8 ubHandPos,UINT16 usOldItemIndex,UINT16 usNewItemIndex,BOOLEAN fDeductPoints)1479 static BOOLEAN UIHandleItemPlacement(UINT8 ubHandPos, UINT16 usOldItemIndex, UINT16 usNewItemIndex, BOOLEAN fDeductPoints)
1480 {
1481 if ( _KeyDown(CTRL) )
1482 {
1483 CleanUpStack( &( gpSMCurrentMerc->inv[ ubHandPos ] ), gpItemPointer );
1484 if ( gpItemPointer->ubNumberOfObjects == 0 )
1485 {
1486 EndItemPointer( );
1487 }
1488 return( TRUE );
1489 }
1490
1491 // Try to place here
1492 if ( PlaceObject( gpSMCurrentMerc, ubHandPos, gpItemPointer ) )
1493 {
1494 if ( fDeductPoints )
1495 {
1496 // Deduct points
1497 if ( gpItemPointerSoldier->bLife >= CONSCIOUSNESS )
1498 {
1499 DeductPoints( gpItemPointerSoldier, 2, 0 );
1500 }
1501 if ( gpSMCurrentMerc->bLife >= CONSCIOUSNESS )
1502 {
1503 DeductPoints( gpSMCurrentMerc, 2, 0 );
1504 }
1505 }
1506
1507 HandleTacticalEffectsOfEquipmentChange( gpSMCurrentMerc, ubHandPos, usOldItemIndex, usNewItemIndex );
1508
1509 // Dirty
1510 fInterfacePanelDirty = DIRTYLEVEL2;
1511
1512 // Check if cursor is empty now
1513 if ( gpItemPointer->ubNumberOfObjects == 0 )
1514 {
1515 EndItemPointer( );
1516 }
1517
1518 if ( gpItemPointerSoldier != gpSMCurrentMerc )
1519 {
1520 ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, st_format_printf(pMessageStrings[ MSG_ITEM_PASSED_TO_MERC ], ShortItemNames[ usNewItemIndex ], gpSMCurrentMerc->name) );
1521 }
1522
1523 // UPDATE ITEM POINTER.....
1524 gpItemPointerSoldier = gpSMCurrentMerc;
1525
1526 if ( gpItemPointer != NULL )
1527 {
1528 ReevaluateItemHatches( gpSMCurrentMerc, FALSE );
1529 }
1530
1531 // Set cursor back to normal mode...
1532 guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
1533
1534 return( TRUE );
1535
1536 }
1537 else
1538 {
1539 return( FALSE );
1540 }
1541
1542 }
1543
1544
1545 static void MergeMessageBoxCallBack(MessageBoxReturnValue);
1546
1547
SMInvClickCallback(MOUSE_REGION * pRegion,INT32 iReason)1548 static void SMInvClickCallback(MOUSE_REGION* pRegion, INT32 iReason)
1549 {
1550 UINT32 uiHandPos;
1551 // Copyies of values
1552 UINT16 usOldItemIndex, usNewItemIndex;
1553 UINT16 usItemPrevInItemPointer;
1554 BOOLEAN fNewItem = FALSE;
1555 static BOOLEAN fRightDown = FALSE;
1556
1557
1558 uiHandPos = MSYS_GetRegionUserData( pRegion, 0 );
1559
1560 if (fInMapMode) return; // XXX necessary?
1561
1562 //if we are in the shop keeper interface
1563 if (guiCurrentScreen == SHOPKEEPER_SCREEN)
1564 {
1565 // and this inventory slot is hatched out
1566 if( ShouldSoldierDisplayHatchOnItem( gpSMCurrentMerc->ubProfile, (INT16)uiHandPos ) )
1567 {
1568 // it means that item is a copy of one in the player's offer area, so we treat it as if the slot was empty (ignore)
1569 // if the cursor has an item in it, we still ignore the click, because handling swaps in this situation would be
1570 // ugly, we'd have to the the swap, then make the bOwnerSlot of the item just picked up a -1 in its offer area spot.
1571 return;
1572 }
1573 }
1574
1575
1576 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
1577 {
1578 // If we do not have an item in hand, start moving it
1579 if ( gpItemPointer == NULL )
1580 {
1581
1582 // Return if empty
1583 if ( gpSMCurrentMerc->inv[ uiHandPos ].usItem == NOTHING )
1584 return;
1585
1586 SelectSoldier(gpSMCurrentMerc, SELSOLDIER_NONE);
1587
1588 // OK, check if this is Nails, and we're in the vest position , don't allow it to come off....
1589 if ( HandleNailsVestFetish( gpSMCurrentMerc, uiHandPos, NOTHING ) )
1590 {
1591 return;
1592 }
1593
1594 if ( _KeyDown(CTRL) )
1595 {
1596 CleanUpStack( &( gpSMCurrentMerc->inv[ uiHandPos ] ), NULL );
1597 return;
1598 }
1599
1600 // Turn off new item glow!
1601 gpSMCurrentMerc->bNewItemCount[ uiHandPos ] = 0;
1602
1603 usOldItemIndex = gpSMCurrentMerc->inv[ uiHandPos ].usItem;
1604
1605 // move item into the mouse cursor
1606 BeginItemPointer( gpSMCurrentMerc, (UINT8)uiHandPos );
1607
1608 //if we are in the shopkeeper interface
1609 if (guiCurrentScreen == SHOPKEEPER_SCREEN)
1610 {
1611 // pick up item from regular inventory slot into cursor OR try to sell it
1612 // ( unless CTRL is held down )
1613 BeginSkiItemPointer(PLAYERS_INVENTORY, (INT8)uiHandPos, !_KeyDown(CTRL));
1614 }
1615
1616 HandleTacticalEffectsOfEquipmentChange( gpSMCurrentMerc, uiHandPos, usOldItemIndex, NOTHING );
1617
1618 // HandleCompatibleAmmoUI( gpSMCurrentMerc, (INT8)uiHandPos, FALSE );
1619 }
1620 else // item in cursor
1621 {
1622 BOOLEAN fOKToGo = FALSE;
1623 BOOLEAN fDeductPoints = FALSE;
1624
1625 // ATE: OK, get source, dest guy if different... check for and then charge appropriate APs
1626 if (gpSMCurrentMerc == gpItemPointerSoldier)
1627 {
1628 // We are doing this ourselve, continue
1629 fOKToGo = TRUE;
1630 }
1631 else
1632 {
1633 // These guys are different....
1634 fDeductPoints = TRUE;
1635
1636 // First check points for src guy
1637 if ( gpItemPointerSoldier->bLife >= CONSCIOUSNESS )
1638 {
1639 if ( EnoughPoints( gpItemPointerSoldier, 3, 0, TRUE ) )
1640 {
1641 fOKToGo = TRUE;
1642 }
1643 }
1644 else
1645 {
1646 fOKToGo = TRUE;
1647 }
1648
1649 // Should we go on?
1650 if ( fOKToGo )
1651 {
1652 if ( gpSMCurrentMerc->bLife >= CONSCIOUSNESS )
1653 {
1654 if ( EnoughPoints( gpSMCurrentMerc, 3, 0, TRUE ) )
1655 {
1656 fOKToGo = TRUE;
1657 }
1658 else
1659 {
1660 fOKToGo = FALSE;
1661 }
1662 }
1663 }
1664 }
1665
1666 if ( fOKToGo )
1667 {
1668 // OK, check if this is Nails, and we're in the vest position , don't allow
1669 // it to come off....
1670 if ( HandleNailsVestFetish( gpSMCurrentMerc, uiHandPos, gpItemPointer->usItem ) )
1671 {
1672 return;
1673 }
1674
1675 usOldItemIndex = gpSMCurrentMerc->inv[ uiHandPos ].usItem;
1676 usNewItemIndex = gpItemPointer->usItem;
1677
1678 if ( uiHandPos == HANDPOS || uiHandPos == SECONDHANDPOS || uiHandPos == HELMETPOS || uiHandPos == VESTPOS || uiHandPos == LEGPOS )
1679 {
1680 //if ( ValidAttachmentClass( usNewItemIndex, usOldItemIndex ) )
1681 if ( ValidAttachment( usNewItemIndex, usOldItemIndex ) )
1682 {
1683 // it's an attempt to attach; bring up the inventory panel
1684 if ( !InItemDescriptionBox( ) )
1685 {
1686 InitItemDescriptionBox( gpSMCurrentMerc, (UINT8)uiHandPos, SM_ITEMDESC_START_X, SM_ITEMDESC_START_Y, 0 );
1687 }
1688 return;
1689 }
1690 else if ( ValidMerge( usNewItemIndex, usOldItemIndex ) )
1691 {
1692 // bring up merge requestor
1693 gubHandPos = (UINT8) uiHandPos;
1694 gusOldItemIndex = usOldItemIndex;
1695 gusNewItemIndex = usNewItemIndex;
1696 gfDeductPoints = fDeductPoints;
1697
1698 if (guiCurrentScreen == SHOPKEEPER_SCREEN)
1699 {
1700 //the only way to merge items is to pick them up. In SKI when you pick up an item, the cursor is
1701 //locked in a region, free it up.
1702 FreeMouseCursor();
1703
1704 DoMessageBox(MSG_BOX_BASIC_STYLE, g_langRes->Message[STR_MERGE_ITEMS], SHOPKEEPER_SCREEN, MSG_BOX_FLAG_YESNO, MergeMessageBoxCallBack, NULL);
1705 }
1706 else
1707 DoMessageBox(MSG_BOX_BASIC_STYLE, g_langRes->Message[STR_MERGE_ITEMS], GAME_SCREEN, MSG_BOX_FLAG_YESNO, MergeMessageBoxCallBack, NULL);
1708 return;
1709 }
1710 // else handle normally
1711 }
1712
1713
1714 // remember the item type currently in the item pointer
1715 usItemPrevInItemPointer = gpItemPointer->usItem;
1716
1717 if (guiCurrentScreen == SHOPKEEPER_SCREEN)
1718 {
1719 // If it's just been purchased or repaired, mark it as a "new item"
1720 fNewItem = ( BOOLEAN ) ( gMoveingItem.uiFlags & ( ARMS_INV_JUST_PURCHASED | ARMS_INV_ITEM_REPAIRED ) );
1721 }
1722
1723 // try to place the item in the cursor into this inventory slot
1724 if ( UIHandleItemPlacement( (UINT8) uiHandPos, usOldItemIndex, usNewItemIndex, fDeductPoints ) )
1725 {
1726 // it worked! if we're in the SKI...
1727 if (guiCurrentScreen == SHOPKEEPER_SCREEN)
1728 {
1729 SetNewItem( gpSMCurrentMerc, ( UINT8 ) uiHandPos, fNewItem );
1730
1731 // and the cursor is now empty
1732 if( gpItemPointer == NULL )
1733 {
1734 // clean up
1735 gMoveingItem = INVENTORY_IN_SLOT{};
1736 SetSkiCursor( CURSOR_NORMAL );
1737 }
1738 else
1739 {
1740 // if we're holding something else in the pointer now
1741 if ( usItemPrevInItemPointer != gpItemPointer->usItem )
1742 {
1743 // pick up item swapped out of inventory slot into
1744 // cursor (don't try to sell)
1745 BeginSkiItemPointer( PLAYERS_INVENTORY, -1, FALSE );
1746 }
1747 else
1748 {
1749 // otherwise, leave the cursor as is, means more items
1750 // were picked up at once than can be placed in this slot
1751 // we deal with this by leaving the remainder in the
1752 // cursor, to be put down elsewhere using subsequent
1753 // clicks
1754 }
1755 }
1756 }
1757
1758 // Setup a timer....
1759 //guiMouseOverItemTime = GetJA2Clock( );
1760 //gfCheckForMouseOverItem = TRUE;
1761 //gbCheckForMouseOverItemPos = (INT8)uiHandPos;
1762 }
1763
1764 /*
1765 // Try to place here
1766 if ( PlaceObject( gpSMCurrentMerc, (UINT8)uiHandPos, gpItemPointer ) )
1767 {
1768
1769 if ( fDeductPoints )
1770 {
1771 // Deduct points
1772 if ( gpItemPointerSoldier->bLife >= CONSCIOUSNESS )
1773 {
1774 DeductPoints( gpItemPointerSoldier, 2, 0 );
1775 }
1776 if ( gpSMCurrentMerc->bLife >= CONSCIOUSNESS )
1777 {
1778 DeductPoints( gpSMCurrentMerc, 2, 0 );
1779 }
1780 }
1781
1782 HandleTacticalEffectsOfEquipmentChange( gpSMCurrentMerc, uiHandPos, usOldItemIndex, usNewItemIndex );
1783
1784 // Dirty
1785 fInterfacePanelDirty = DIRTYLEVEL2;
1786
1787 // Check if it's the same now!
1788 if ( gpItemPointer->ubNumberOfObjects == 0 )
1789 {
1790 EndItemPointer( );
1791 }
1792
1793 // Setup a timer....
1794 guiMouseOverItemTime = GetJA2Clock( );
1795 gfCheckForMouseOverItem = TRUE;
1796 gbCheckForMouseOverItemPos = (INT8)uiHandPos;
1797
1798 }*/
1799 }
1800 }
1801 }
1802 else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_DWN)
1803 {
1804 fRightDown = TRUE;
1805 }
1806 else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP && fRightDown )
1807 {
1808 fRightDown = FALSE;
1809
1810 // Return if empty
1811 if ( gpSMCurrentMerc->inv[ uiHandPos ].usItem == NOTHING )
1812 return;
1813
1814 // Turn off new item glow!
1815 gpSMCurrentMerc->bNewItemCount[ uiHandPos ] = 0;
1816
1817 // Some global stuff here - for esc, etc
1818 // Check for # of slots in item
1819 if((gpSMCurrentMerc->inv[uiHandPos].ubNumberOfObjects > 1 &&
1820 ItemSlotLimit(gpSMCurrentMerc->inv[uiHandPos].usItem, (UINT8)uiHandPos) > 0) &&
1821 (guiCurrentScreen != MAP_SCREEN))
1822 {
1823 if ( !InItemStackPopup( ) )
1824 {
1825 InitItemStackPopup(gpSMCurrentMerc, (UINT8)uiHandPos, SM_ITEMDESC_START_X,
1826 INV_INTERFACE_START_Y, SM_ITEMDESC_WIDTH,
1827 SCREEN_HEIGHT - INV_INTERFACE_START_Y );
1828 }
1829 }
1830 else
1831 {
1832 if ( !InItemDescriptionBox( ) )
1833 {
1834 InitItemDescriptionBox(gpSMCurrentMerc, (UINT8)uiHandPos,
1835 SM_ITEMDESC_START_X, SM_ITEMDESC_START_Y, 0);
1836 }
1837 }
1838 }
1839 else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
1840 {
1841 fRightDown = FALSE;
1842 }
1843
1844 }
1845
1846
MergeMessageBoxCallBack(MessageBoxReturnValue const ubExitValue)1847 static void MergeMessageBoxCallBack(MessageBoxReturnValue const ubExitValue)
1848 {
1849 if ( ubExitValue == MSG_BOX_RETURN_YES )
1850 {
1851 AttachObject( gpItemPointerSoldier, &( gpSMCurrentMerc->inv[ gubHandPos ] ), gpItemPointer );
1852
1853 // re-evaluate repairs
1854 gfReEvaluateEveryonesNothingToDo = TRUE;
1855
1856 if (gpItemPointer->usItem == NOTHING)
1857 {
1858 // merge item consumed
1859 EndItemPointer();
1860 fInterfacePanelDirty = DIRTYLEVEL2;
1861 }
1862 }
1863 else
1864 {
1865 UIHandleItemPlacement( gubHandPos, gusOldItemIndex, gusNewItemIndex, gfDeductPoints );
1866 }
1867 }
1868
1869
HandleMouseOverSoldierFaceForContMove(SOLDIERTYPE * pSoldier,BOOLEAN fOn)1870 static void HandleMouseOverSoldierFaceForContMove(SOLDIERTYPE* pSoldier, BOOLEAN fOn)
1871 {
1872 INT16 sGridNo;
1873
1874 if ( pSoldier == NULL )
1875 {
1876 return;
1877 }
1878
1879 if ( fOn )
1880 {
1881 // Check if we are waiting to continue move...
1882 if ( CheckForMercContMove( pSoldier ) )
1883 {
1884 // Display 'cont' on face....
1885 // Get face
1886 FACETYPE* const pFace = pSoldier->face;
1887
1888 pFace->fDisplayTextOver = FACE_DRAW_TEXT_OVER;
1889 pFace->zDisplayText = TacticalStr[ CONTINUE_OVER_FACE_STR ];
1890
1891 sGridNo = pSoldier->sFinalDestination;
1892
1893 if ( pSoldier->bGoodContPath )
1894 {
1895 sGridNo = pSoldier->sContPathLocation;
1896 }
1897
1898 // While our mouse is here, draw a path!
1899 PlotPath(pSoldier, sGridNo, NO_COPYROUTE, PLOT, pSoldier->usUIMovementMode, pSoldier->bActionPoints);
1900 }
1901 }
1902 else
1903 {
1904 // Remove 'cont' on face....
1905 // Get face
1906 FACETYPE* const pFace = pSoldier->face;
1907
1908 pFace->fDisplayTextOver = FACE_ERASE_TEXT_OVER;
1909
1910 // Erase path!
1911 ErasePath();
1912 }
1913
1914 fInterfacePanelDirty = DIRTYLEVEL2;
1915 }
1916
1917
SelectedMercButtonMoveCallback(MOUSE_REGION * pRegion,INT32 iReason)1918 static void SelectedMercButtonMoveCallback(MOUSE_REGION* pRegion, INT32 iReason)
1919 {
1920 if ( gpSMCurrentMerc == NULL )
1921 {
1922 return;
1923 }
1924
1925 if (iReason & MSYS_CALLBACK_REASON_MOVE )
1926 {
1927 HandleMouseOverSoldierFaceForContMove( gpSMCurrentMerc, TRUE );
1928 }
1929 else if ( iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
1930 {
1931 HandleMouseOverSoldierFaceForContMove( gpSMCurrentMerc, FALSE );
1932 }
1933 }
1934
1935
SelectedMercButtonCallback(MOUSE_REGION * pRegion,INT32 iReason)1936 static void SelectedMercButtonCallback(MOUSE_REGION* pRegion, INT32 iReason)
1937 {
1938 if ( gpSMCurrentMerc == NULL )
1939 {
1940 return;
1941 }
1942
1943 //if we are in the shop keeper interface
1944 if (guiCurrentScreen == SHOPKEEPER_SCREEN) return;
1945
1946 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1947 {
1948 // ATE: Don't if this guy can't....
1949 if ( !gfSMDisableForItems )
1950 {
1951 if ( gpSMCurrentMerc->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) )
1952 {
1953 SOLDIERTYPE& vs = GetSoldierStructureForVehicle(GetVehicle(gpSMCurrentMerc->iVehicleId));
1954 HandleLocateSelectMerc(&vs, false);
1955 }
1956 else
1957 {
1958 if ( CheckForMercContMove( gpSMCurrentMerc ) )
1959 {
1960 // Continue
1961 ContinueMercMovement( gpSMCurrentMerc );
1962 ErasePath();
1963 }
1964 else
1965 {
1966 HandleLocateSelectMerc(gpSMCurrentMerc, false);
1967 }
1968 }
1969 }
1970 }
1971 else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_DWN)
1972 {
1973 // ATE: Cannot get out by right clicking...
1974 //if ( gpItemPointer == NULL )
1975 {
1976 //if ( !gfSMDisableForItems || ( gfSMDisableForItems && gpItemPointer == NULL ) )
1977 {
1978 // Delete desc
1979 if ( InItemDescriptionBox( ) )
1980 {
1981 DeleteItemDescriptionBox( );
1982 }
1983
1984 SetNewPanel(0);
1985 }
1986 }
1987 }
1988 }
1989
1990
SelectedMercEnemyIndicatorCallback(MOUSE_REGION * pRegion,INT32 iReason)1991 static void SelectedMercEnemyIndicatorCallback(MOUSE_REGION* pRegion, INT32 iReason)
1992 {
1993 if ( gpSMCurrentMerc == NULL )
1994 {
1995 return;
1996 }
1997
1998 //if we are in the shop keeper interface
1999 if (guiCurrentScreen == SHOPKEEPER_SCREEN) return;
2000
2001 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2002 {
2003 // ATE: Don't if this guy can't....
2004 if ( !gfSMDisableForItems )
2005 {
2006 if ( gpSMCurrentMerc->uiStatusFlags & ( SOLDIER_DRIVER | SOLDIER_PASSENGER ) )
2007 {
2008 }
2009 else
2010 {
2011 if ( gpSMCurrentMerc->bOppCnt > 0 )
2012 {
2013 CycleVisibleEnemies( gpSMCurrentMerc );
2014 }
2015 else
2016 {
2017 SelectedMercButtonCallback( pRegion, iReason );
2018 }
2019 }
2020 }
2021 }
2022 }
2023
2024
BtnStanceUpCallback(GUI_BUTTON * btn,INT32 reason)2025 static void BtnStanceUpCallback(GUI_BUTTON* btn, INT32 reason)
2026 {
2027 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2028 {
2029 INT8 bNewStance = gAnimControl[gpSMCurrentMerc->usAnimState].ubEndHeight;
2030 switch (bNewStance)
2031 {
2032 case ANIM_CROUCH:
2033 bNewStance = ANIM_STAND;
2034 break;
2035 case ANIM_PRONE:
2036 bNewStance = ANIM_CROUCH;
2037 break;
2038 }
2039 UIHandleSoldierStanceChange(gpSMCurrentMerc, bNewStance);
2040 }
2041 }
2042
2043
BtnUpdownCallback(GUI_BUTTON * btn,INT32 reason)2044 void BtnUpdownCallback(GUI_BUTTON* btn, INT32 reason)
2045 {
2046 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2047 {
2048 // Change interface level via HandleUI handler
2049 UIHandleChangeLevel(NULL);
2050 }
2051 }
2052
2053
BtnClimbCallback(GUI_BUTTON * const btn,INT32 const reason)2054 static void BtnClimbCallback(GUI_BUTTON* const btn, INT32 const reason)
2055 {
2056 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2057 {
2058 SOLDIERTYPE* const s = gpSMCurrentMerc;
2059 if (FindLowerLevel(s))
2060 {
2061 BeginSoldierClimbDownRoof(s);
2062 }
2063 else if (FindHigherLevel(s))
2064 {
2065 BeginSoldierClimbUpRoof(s);
2066 }
2067 else if (FindFenceJumpDirection(s))
2068 {
2069 BeginSoldierClimbFence(s);
2070 }
2071 }
2072 }
2073
2074
BtnStanceDownCallback(GUI_BUTTON * btn,INT32 reason)2075 static void BtnStanceDownCallback(GUI_BUTTON* btn, INT32 reason)
2076 {
2077 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2078 {
2079 INT8 bNewStance = gAnimControl[gpSMCurrentMerc->usAnimState].ubEndHeight;
2080 switch (bNewStance)
2081 {
2082 case ANIM_STAND:
2083 bNewStance = ANIM_CROUCH;
2084 break;
2085 case ANIM_CROUCH:
2086 bNewStance = ANIM_PRONE;
2087 break;
2088 }
2089 UIHandleSoldierStanceChange(gpSMCurrentMerc, bNewStance);
2090 }
2091 }
2092
2093
BtnStealthModeCallback(GUI_BUTTON * btn,INT32 reason)2094 static void BtnStealthModeCallback(GUI_BUTTON* btn, INT32 reason)
2095 {
2096 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2097 {
2098 gpSMCurrentMerc->bStealthMode = !gpSMCurrentMerc->bStealthMode;
2099 gfUIStanceDifferent = TRUE;
2100 gfPlotNewMovement = TRUE;
2101 fInterfacePanelDirty = DIRTYLEVEL2;
2102 }
2103 }
2104
2105
BtnHandCursorCallback(GUI_BUTTON * btn,INT32 reason)2106 static void BtnHandCursorCallback(GUI_BUTTON* btn, INT32 reason)
2107 {
2108 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2109 {
2110 ToggleHandCursorMode(&guiCurrentEvent);
2111 }
2112 }
2113
2114
BtnTalkCallback(GUI_BUTTON * btn,INT32 reason)2115 static void BtnTalkCallback(GUI_BUTTON* btn, INT32 reason)
2116 {
2117 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2118 {
2119 ToggleTalkCursorMode(&guiCurrentEvent);
2120 }
2121 }
2122
2123
BtnMuteCallback(GUI_BUTTON * btn,INT32 reason)2124 static void BtnMuteCallback(GUI_BUTTON* btn, INT32 reason)
2125 {
2126 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2127 {
2128 gpSMCurrentMerc->uiStatusFlags ^= SOLDIER_MUTE;
2129 ST::string msg = (gpSMCurrentMerc->uiStatusFlags & SOLDIER_MUTE ? TacticalStr[MUTE_ON_STR] : TacticalStr[MUTE_OFF_STR]);
2130 ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, st_format_printf(msg, gpSMCurrentMerc->name));
2131 }
2132 }
2133
2134
SelectMerc(SOLDIERTYPE * const s)2135 static void SelectMerc(SOLDIERTYPE* const s)
2136 {
2137 gSelectSMPanelToMerc = s;
2138
2139 if (!gfInItemPickupMenu)
2140 {
2141 BOOLEAN set_locator;
2142 if (guiCurrentScreen == SHOPKEEPER_SCREEN)
2143 {
2144 // Refresh background for player slots (in case item values change due to Flo's discount)
2145 gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
2146 set_locator = DONTSETLOCATOR;
2147 }
2148 else
2149 {
2150 set_locator = SETLOCATOR;
2151 }
2152 LocateSoldier(s, set_locator);
2153 }
2154
2155 // If the user is in the shop keeper interface and is in the item desc
2156 if (guiCurrentScreen == SHOPKEEPER_SCREEN && InItemDescriptionBox())
2157 {
2158 DeleteItemDescriptionBox();
2159 }
2160 }
2161
2162
BtnPrevMercCallback(GUI_BUTTON * btn,INT32 reason)2163 static void BtnPrevMercCallback(GUI_BUTTON* btn, INT32 reason)
2164 {
2165 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2166 {
2167 SelectMerc(FindPrevActiveAndAliveMerc(gpSMCurrentMerc, TRUE, TRUE));
2168 }
2169 }
2170
2171
BtnNextMercCallback(GUI_BUTTON * btn,INT32 reason)2172 static void BtnNextMercCallback(GUI_BUTTON* btn, INT32 reason)
2173 {
2174 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2175 {
2176 SelectMerc(FindNextActiveAndAliveMerc(gpSMCurrentMerc, TRUE, TRUE));
2177 }
2178 }
2179
2180
BtnOptionsCallback(GUI_BUTTON * btn,INT32 reason)2181 static void BtnOptionsCallback(GUI_BUTTON* btn, INT32 reason)
2182 {
2183 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2184 {
2185 guiPreviousOptionScreen = guiCurrentScreen;
2186 LeaveTacticalScreen(OPTIONS_SCREEN);
2187 }
2188 }
2189
2190
BtnSMDoneCallback(GUI_BUTTON * btn,INT32 reason)2191 static void BtnSMDoneCallback(GUI_BUTTON* btn, INT32 reason)
2192 {
2193 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2194 {
2195 gfBeginEndTurn = TRUE;
2196 }
2197 }
2198
2199
BtnMapScreenCallback(GUI_BUTTON * btn,INT32 reason)2200 static void BtnMapScreenCallback(GUI_BUTTON* btn, INT32 reason)
2201 {
2202 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2203 {
2204 // Enter mapscreen...
2205 //gfEnteringMapScreen = TRUE;
2206 GoToMapScreenFromTactical();
2207 }
2208 }
2209
2210
BtnBurstModeCallback(GUI_BUTTON * btn,INT32 reason)2211 static void BtnBurstModeCallback(GUI_BUTTON* btn, INT32 reason)
2212 {
2213 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2214 {
2215 ChangeWeaponMode(gpSMCurrentMerc);
2216 }
2217 }
2218
2219
BtnLookCallback(GUI_BUTTON * btn,INT32 reason)2220 static void BtnLookCallback(GUI_BUTTON* btn, INT32 reason)
2221 {
2222 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2223 {
2224 ToggleLookCursorMode();
2225 }
2226 }
2227
2228
MakeRegion(TeamPanelSlot & tp,MOUSE_REGION & r,INT32 const x,INT32 const y,INT32 const w,INT32 const h,MOUSE_CALLBACK const move,MOUSE_CALLBACK const click)2229 static void MakeRegion(TeamPanelSlot& tp, MOUSE_REGION& r, INT32 const x, INT32 const y, INT32 const w, INT32 const h, MOUSE_CALLBACK const move, MOUSE_CALLBACK const click)
2230 {
2231 MSYS_DefineRegion(&r, x, y , x + w, y + h, MSYS_PRIORITY_NORMAL, MSYS_NO_CURSOR, move, click);
2232 r.SetUserPtr(&tp);
2233 }
2234
2235
2236 static void EnemyIndicatorClickCallback(MOUSE_REGION* pRegion, INT32 iReason);
2237 static void MercFacePanelCallback(MOUSE_REGION* pRegion, INT32 iReason);
2238 static void MercFacePanelMoveCallback(MOUSE_REGION* pRegion, INT32 iReason);
2239 static void TMClickFirstHandInvCallback(MOUSE_REGION* pRegion, INT32 iReason);
2240 static void TMClickSecondHandInvCallback(MOUSE_REGION* pRegion, INT32 iReason);
2241
2242
InitializeTEAMPanel(void)2243 void InitializeTEAMPanel(void)
2244 {
2245 // INit viewport region
2246 // Set global mouse regions
2247 // Define region for viewport
2248 MSYS_DefineRegion(&gViewportRegion, 0, 0, gsVIEWPORT_END_X, gsVIEWPORT_END_Y, MSYS_PRIORITY_NORMAL, VIDEO_NO_CURSOR, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK);
2249
2250 guiTEAMPanel = AddVideoObjectFromFile(INTERFACEDIR "/bottom_bar.sti");
2251 guiTEAMObjects = AddVideoObjectFromFile(INTERFACEDIR "/gold_front.sti");
2252 guiVEHINV = AddVideoObjectFromFile(INTERFACEDIR "/inventor.sti");
2253
2254 FillEmptySpaceAtBottom();
2255
2256 // Create buttons
2257 CreateTEAMPanelButtons();
2258
2259 // Set viewports
2260 // Define region for panel
2261 MSYS_DefineRegion(&gTEAM_PanelRegion, 0, gsVIEWPORT_END_Y, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_NORMAL, CURSOR_NORMAL, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK);
2262
2263 INT32 dx = 0;
2264 INT32 const dy = INTERFACE_START_Y;
2265 FOR_EACH_TEAM_PANEL_SLOT(i)
2266 {
2267 TeamPanelSlot& tp = *i;
2268
2269 INT32 const face_x = dx + TM_FACE_X;
2270 INT32 const face_y = dy + TM_FACE_Y;
2271 MakeRegion(tp, tp.face, face_x, face_y, TM_FACE_WIDTH, TM_FACE_HEIGHT,
2272 MercFacePanelMoveCallback, MercFacePanelCallback);
2273 MakeRegion(tp, tp.enemy_indicator, face_x + 1, face_y + 1, INDICATOR_BOX_WIDTH - 1,
2274 INDICATOR_BOX_HEIGHT - 1, MSYS_NO_CALLBACK, EnemyIndicatorClickCallback);
2275
2276 if (IsMouseInRegion(tp.face))
2277 {
2278 SOLDIERTYPE* const s = tp.merc;
2279 if (s) HandleMouseOverSoldierFaceForContMove(s, TRUE);
2280 }
2281
2282 MakeRegion(tp, tp.bars, dx + TM_BARS_X, dy + TM_BARS_Y,
2283 TM_BARS_WIDTH, TM_BARS_HEIGHT, MSYS_NO_CALLBACK, MercFacePanelCallback);
2284 MakeRegion(tp, tp.left_bars, dx + TM_FACE_X - 8, dy + TM_FACE_Y, 8, TM_BARS_HEIGHT,
2285 MSYS_NO_CALLBACK, MercFacePanelCallback);
2286
2287 INT32 const hand_x = dx + TM_INV_HAND1STARTX;
2288 INT32 const hand_y = dy + TM_INV_HAND1STARTY;
2289 MakeRegion(tp, tp.first_hand, hand_x, hand_y, TM_INV_WIDTH, TM_INV_HEIGHT,
2290 MSYS_NO_CALLBACK, TMClickFirstHandInvCallback);
2291 MakeRegion(tp, tp.second_hand, hand_x, hand_y + TM_INV_HAND_SEPY, TM_INV_WIDTH,
2292 TM_INV_HEIGHT, MSYS_NO_CALLBACK, TMClickSecondHandInvCallback);
2293
2294 dx += TM_INV_HAND_SEP;
2295 }
2296 }
2297
2298
ShutdownTEAMPanel(void)2299 void ShutdownTEAMPanel(void)
2300 {
2301 // All buttons and regions and video objects and video surfaces will be deleted at shutddown of SGM
2302 // We may want to delete them at the interm as well, to free up room for other panels
2303 DeleteVideoObject(guiTEAMPanel);
2304 DeleteVideoObject(guiTEAMObjects);
2305 DeleteVideoObject(guiVEHINV);
2306
2307 MSYS_RemoveRegion(&gTEAM_PanelRegion);
2308 MSYS_RemoveRegion(&gViewportRegion);
2309
2310 FOR_EACH_TEAM_PANEL_SLOT(i)
2311 {
2312 MSYS_RemoveRegion(&i->face);
2313 MSYS_RemoveRegion(&i->enemy_indicator);
2314 MSYS_RemoveRegion(&i->bars);
2315 MSYS_RemoveRegion(&i->left_bars);
2316 MSYS_RemoveRegion(&i->first_hand);
2317 MSYS_RemoveRegion(&i->second_hand);
2318
2319 SOLDIERTYPE* const s = i->merc;
2320 if (s) HandleMouseOverSoldierFaceForContMove(s, FALSE);
2321 }
2322
2323 RemoveTEAMPanelButtons();
2324
2325 // start rendering radar region again,
2326 fRenderRadarScreen = TRUE;
2327 }
2328
2329
RenderTeamSlotBorder(const SOLDIERTYPE * const s,const INT32 dx,const INT32 dy)2330 static void RenderTeamSlotBorder(const SOLDIERTYPE* const s, const INT32 dx, const INT32 dy)
2331 {
2332 UINT16 region;
2333 if (gTacticalStatus.ubCurrentTeam != OUR_TEAM) region = 1; // Hatch out
2334 else if (INTERRUPT_QUEUED && (!s || s->bMoved)) region = 1; // Hatch out
2335 else if (s && s == GetSelectedMan()) region = 0; // Active border
2336 else return;
2337 const INT32 x = dx + TM_FACEHIGHTL_X;
2338 const INT32 y = dy + TM_FACEHIGHTL_Y;
2339 BltVideoObject(guiSAVEBUFFER, guiTEAMObjects, region, x, y);
2340 }
2341
2342
2343 static void RenderSoldierTeamInv(SOLDIERTYPE const&, INT16 x, INT16 y, DirtyLevel);
2344 static void UpdateTEAMPanel(void);
2345
2346
RenderTEAMPanel(DirtyLevel const dirty_level)2347 void RenderTEAMPanel(DirtyLevel const dirty_level)
2348 {
2349 if (dirty_level == DIRTYLEVEL2)
2350 {
2351 MarkAButtonDirty(iTEAMPanelButtons[TEAM_DONE_BUTTON]);
2352 MarkAButtonDirty(iTEAMPanelButtons[TEAM_MAP_SCREEN_BUTTON]);
2353 MarkAButtonDirty(iTEAMPanelButtons[CHANGE_SQUAD_BUTTON]);
2354
2355 BltVideoObject(guiSAVEBUFFER, guiTEAMPanel, 0, INTERFACE_START_X, INTERFACE_START_Y);
2356
2357 // LOOP THROUGH ALL MERCS ON TEAM PANEL
2358 INT32 dx = 0;
2359 INT32 const dy = INTERFACE_START_Y;
2360 FOR_EACH_TEAM_PANEL_SLOT(i)
2361 {
2362 SOLDIERTYPE const* const s = i->merc;
2363 if (s)
2364 {
2365 RenderSoldierFace(*s, dx + TM_FACE_X, dy + TM_FACE_Y);
2366 }
2367 else
2368 {
2369 //BLIT CLOSE PANEL
2370 BltVideoObject(guiSAVEBUFFER, guiCLOSE, 5, dx + TM_FACE_X, dy + TM_FACE_Y);
2371 }
2372
2373 RenderTeamSlotBorder(s, dx, dy);
2374
2375 if (s)
2376 {
2377 ST::string help;
2378 ST::string help_buf;
2379
2380 // Add text for first hand popup
2381 if (s->uiStatusFlags & SOLDIER_DRIVER)
2382 {
2383 // Get soldier pointer for vehicle.....
2384 SOLDIERTYPE const& vs = GetSoldierStructureForVehicle(GetVehicle(s->iVehicleId));
2385 help_buf = st_format_printf(TacticalStr[DRIVER_POPUPTEXT], vs.bLife,
2386 vs.bLifeMax, vs.bBreath, vs.bBreathMax);
2387 help = help_buf;
2388 }
2389 else if (s->uiStatusFlags & SOLDIER_DEAD)
2390 {
2391 help = ST::null;
2392 }
2393 else
2394 {
2395 help_buf = GetHelpTextForItem(s->inv[HANDPOS]);
2396 help = help_buf;
2397 }
2398 i->first_hand.SetFastHelpText(help);
2399
2400 // Add text for seonc hand popup
2401 if (s->uiStatusFlags & (SOLDIER_PASSENGER | SOLDIER_DRIVER))
2402 {
2403 help = TacticalStr[EXIT_VEHICLE_POPUPTEXT];
2404 }
2405 else if (s->uiStatusFlags & SOLDIER_DEAD)
2406 {
2407 help = ST::null;
2408 }
2409 else
2410 {
2411 help_buf = GetHelpTextForItem(s->inv[SECONDHANDPOS]);
2412 help = help_buf;
2413 }
2414 i->second_hand.SetFastHelpText(help);
2415
2416 // Restore AP/LIFE POSIITONS
2417
2418 // Render name!
2419 UINT8 const foreground = s->bStealthMode ? FONT_MCOLOR_LTYELLOW : FONT_MCOLOR_LTGRAY;
2420 SetFontAttributes(BLOCKFONT2, foreground);
2421
2422 // RENDER ON SAVE BUFFER!
2423 SetFontDestBuffer(guiSAVEBUFFER);
2424 INT16 sFontX;
2425 INT16 sFontY;
2426 FindFontCenterCoordinates(dx + TM_NAME_X, dy + TM_NAME_Y, TM_NAME_WIDTH, TM_NAME_HEIGHT,
2427 s->name, BLOCKFONT2, &sFontX, &sFontY);
2428 MPrint(sFontX, sFontY, s->name);
2429 // reset to frame buffer!
2430 SetFontDestBuffer(FRAME_BUFFER);
2431 }
2432
2433 dx += TM_INV_HAND_SEP;
2434 }
2435
2436 RestoreExternBackgroundRect(INTERFACE_START_X, INTERFACE_START_Y, SCREEN_WIDTH - INTERFACE_START_X,
2437 SCREEN_HEIGHT - INTERFACE_START_Y);
2438
2439 RenderTownIDString();
2440 }
2441
2442 // Loop through all mercs and make go
2443 INT32 dx = 0;
2444 INT32 const dy = INTERFACE_START_Y;
2445 FOR_EACH_TEAM_PANEL_SLOT(i)
2446 {
2447 SOLDIERTYPE* const s = i->merc;
2448 if (s)
2449 {
2450 // Update animations....
2451 if (s->fClosePanel || s->fClosePanelToDie)
2452 {
2453 s->sPanelFaceX = s->face->usFaceX;
2454 s->sPanelFaceY = s->face->usFaceY;
2455 }
2456
2457 if (dirty_level != DIRTYLEVEL0)
2458 {
2459 // Update stats!
2460 if (dirty_level == DIRTYLEVEL2) SetStatsHelp(i->bars, *s);
2461
2462 const INT32 x = dx + TM_AP_X;
2463 const INT32 y = dy + TM_AP_Y;
2464 const INT32 w = TM_AP_WIDTH;
2465 const INT32 h = TM_AP_HEIGHT;
2466 if (!(s->uiStatusFlags & SOLDIER_DEAD))
2467 {
2468 PrintAP(s, x, y, w, h);
2469 DrawSoldierUIBars(*s, dx + TM_BARS_X + 2, dy + TM_BARS_Y + 2 + TM_LIFEBAR_HEIGHT,
2470 TRUE, FRAME_BUFFER);
2471 }
2472 else
2473 {
2474 // Erase APs
2475 RestoreExternBackgroundRect(x, y, w, h);
2476 }
2477 }
2478
2479 RenderSoldierTeamInv(*s, dx + TM_INV_HAND1STARTX, dy + TM_INV_HAND1STARTY, dirty_level);
2480 }
2481 dx += TM_INV_HAND_SEP;
2482 }
2483 UpdateTEAMPanel();
2484
2485 if (fRenderRadarScreen)
2486 {
2487 CreateMouseRegionForPauseOfClock();
2488 }
2489 else
2490 {
2491 RemoveMouseRegionForPauseOfClock();
2492 }
2493 }
2494
2495
MakeButtonTeam(UINT idx,BUTTON_PICS * image,INT16 x,INT16 y,const GUI_CALLBACK click,const ST::string & help)2496 static void MakeButtonTeam(UINT idx, BUTTON_PICS* image, INT16 x, INT16 y, const GUI_CALLBACK click, const ST::string& help)
2497 try
2498 {
2499 GUIButtonRef const btn = QuickCreateButton(image, x, y, MSYS_PRIORITY_HIGH - 1, click);
2500 iTEAMPanelButtons[idx] = btn;
2501 btn->SetFastHelpText(help);
2502 }
2503 catch (...)
2504 {
2505 SLOGE("Cannot create Interface button");
2506 throw;
2507 }
2508
2509
2510 static void BtnEndTurnCallback(GUI_BUTTON* btn, INT32 reason);
2511 static void BtnRostermodeCallback(GUI_BUTTON* btn, INT32 reason);
2512 static void BtnSquadCallback(GUI_BUTTON* btn, INT32 reason);
2513
2514
CreateTEAMPanelButtons(void)2515 void CreateTEAMPanelButtons(void)
2516 {
2517 // Load button Graphics
2518 iTEAMPanelImages[ENDTURN_IMAGES] = LoadButtonImage(INTERFACEDIR "/bottom_bar_buttons.sti", 0, 3);
2519 iTEAMPanelImages[ROSTERMODE_IMAGES] = UseLoadedButtonImage(iTEAMPanelImages[ENDTURN_IMAGES], 1, 4);
2520 iTEAMPanelImages[DISK_IMAGES] = UseLoadedButtonImage(iTEAMPanelImages[ENDTURN_IMAGES], 2, 5);
2521
2522 const INT32 dy = INTERFACE_START_Y;
2523 MakeButtonTeam(TEAM_DONE_BUTTON, iTEAMPanelImages[ENDTURN_IMAGES], TM_ENDTURN_X, dy + TM_ENDTURN_Y,
2524 BtnEndTurnCallback, TacticalStr[END_TURN_POPUPTEXT]);
2525 MakeButtonTeam(TEAM_MAP_SCREEN_BUTTON, iTEAMPanelImages[ROSTERMODE_IMAGES], TM_ROSTERMODE_X,
2526 dy + TM_ROSTERMODE_Y, BtnRostermodeCallback, TacticalStr[MAPSCREEN_POPUPTEXT]);
2527 MakeButtonTeam(CHANGE_SQUAD_BUTTON, iTEAMPanelImages[DISK_IMAGES], TM_DISK_X, dy + TM_DISK_Y,
2528 BtnSquadCallback, TacticalStr[CHANGE_SQUAD_POPUPTEXT]);
2529 }
2530
2531
RemoveTEAMPanelButtons(void)2532 void RemoveTEAMPanelButtons(void)
2533 {
2534 for (UINT32 i = 0; i < NUM_TEAM_BUTTONS; ++i)
2535 {
2536 RemoveButton(iTEAMPanelButtons[i]);
2537 UnloadButtonImage(iTEAMPanelImages[i]);
2538 }
2539 }
2540
2541
BtnEndTurnCallback(GUI_BUTTON * btn,INT32 reason)2542 static void BtnEndTurnCallback(GUI_BUTTON* btn, INT32 reason)
2543 {
2544 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2545 {
2546 UIHandleEndTurn(NULL);
2547 }
2548 }
2549
2550
BtnRostermodeCallback(GUI_BUTTON * btn,INT32 reason)2551 static void BtnRostermodeCallback(GUI_BUTTON* btn, INT32 reason)
2552 {
2553 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2554 {
2555 if (guiCurrentScreen == GAME_SCREEN) GoToMapScreenFromTactical();
2556 }
2557 }
2558
2559
2560 // callback to handle squad switching callback
BtnSquadCallback(GUI_BUTTON * btn,INT32 reason)2561 static void BtnSquadCallback(GUI_BUTTON* btn, INT32 reason)
2562 {
2563 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
2564 {
2565 ToggleRadarScreenRender();
2566 }
2567 }
2568
2569
SetTEAMPanelCurrentMerc(void)2570 void SetTEAMPanelCurrentMerc(void)
2571 {
2572 fInterfacePanelDirty = DIRTYLEVEL2;
2573 UpdateTEAMPanel();
2574 }
2575
2576
UpdateTEAMPanel(void)2577 static void UpdateTEAMPanel(void)
2578 {
2579 EnableButton(iTEAMPanelButtons[TEAM_DONE_BUTTON], gTacticalStatus.ubCurrentTeam == OUR_TEAM &&
2580 gTacticalStatus.uiFlags & INCOMBAT);
2581
2582 EnableButton(iTEAMPanelButtons[TEAM_MAP_SCREEN_BUTTON], !(gTacticalStatus.uiFlags & ENGAGED_IN_CONV));
2583
2584 if (gfDisableTacticalPanelButtons)
2585 {
2586 DisableButton(iTEAMPanelButtons[TEAM_DONE_BUTTON]);
2587 DisableButton(iTEAMPanelButtons[TEAM_MAP_SCREEN_BUTTON]);
2588 DisableButton(iTEAMPanelButtons[CHANGE_SQUAD_BUTTON]);
2589
2590 // OK, disable item regions.......
2591 FOR_EACH_TEAM_PANEL_SLOT(i)
2592 {
2593 i->enemy_indicator.Disable();
2594 i->first_hand.Disable();
2595 i->second_hand.Disable();
2596 }
2597
2598 //disable the radar map region
2599 // If NOT in overhead map
2600 if (!InOverheadMap()) gRadarRegion.Disable();
2601 }
2602 else
2603 {
2604 EnableButton(iTEAMPanelButtons[CHANGE_SQUAD_BUTTON]);
2605
2606 FOR_EACH_TEAM_PANEL_SLOT(i)
2607 {
2608 i->enemy_indicator.Enable();
2609 i->first_hand.Enable();
2610 i->second_hand.Enable();
2611 }
2612
2613 gRadarRegion.Enable();
2614 }
2615 }
2616
2617
MercFacePanelMoveCallback(MOUSE_REGION * pRegion,INT32 iReason)2618 static void MercFacePanelMoveCallback(MOUSE_REGION* pRegion, INT32 iReason)
2619 {
2620 // If our flags are set to do this, gofoit!
2621 if (fInMapMode) return; // XXX necessary?
2622
2623 SOLDIERTYPE* const s = pRegion->GetUserPtr<TeamPanelSlot>()->merc;
2624 if (!s || !s->bActive) return;
2625
2626 if (iReason & MSYS_CALLBACK_REASON_MOVE)
2627 {
2628 HandleMouseOverSoldierFaceForContMove(s, TRUE);
2629 }
2630 else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE)
2631 {
2632 HandleMouseOverSoldierFaceForContMove(s, FALSE);
2633 }
2634 }
2635
2636
EnemyIndicatorClickCallback(MOUSE_REGION * pRegion,INT32 iReason)2637 static void EnemyIndicatorClickCallback(MOUSE_REGION* pRegion, INT32 iReason)
2638 {
2639 SOLDIERTYPE* const s = pRegion->GetUserPtr<TeamPanelSlot>()->merc;
2640 if (!s || !s->bActive) return;
2641
2642 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
2643 {
2644 if (!(s->uiStatusFlags & (SOLDIER_DRIVER | SOLDIER_PASSENGER)))
2645 {
2646 if (s->bOppCnt > 0)
2647 { // Cycle....
2648 CycleVisibleEnemies(s);
2649 }
2650 else
2651 {
2652 MercFacePanelCallback(pRegion, iReason);
2653 }
2654 }
2655 }
2656 }
2657
2658
MercFacePanelCallback(MOUSE_REGION * pRegion,INT32 iReason)2659 static void MercFacePanelCallback(MOUSE_REGION* pRegion, INT32 iReason)
2660 {
2661 SOLDIERTYPE* const s = pRegion->GetUserPtr<TeamPanelSlot>()->merc;
2662 if (!s || !s->bActive) return;
2663
2664 // If our flags are set to do this, gofoit!
2665 if (fInMapMode) // XXX necessary?
2666 {
2667 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN) SetInfoChar(s);
2668 return;
2669 }
2670
2671 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
2672 {
2673 if (!gfInItemPickupMenu && gpItemPointer == NULL)
2674 {
2675 if (s->uiStatusFlags & (SOLDIER_DRIVER | SOLDIER_PASSENGER))
2676 {
2677 SOLDIERTYPE& vs = GetSoldierStructureForVehicle(GetVehicle(s->iVehicleId));
2678 HandleLocateSelectMerc(&vs, false);
2679 }
2680 else
2681 {
2682 if (!InOverheadMap())
2683 {
2684 // If we can continue a move, do so!
2685 if (CheckForMercContMove(s))
2686 {
2687 // Continue
2688 ContinueMercMovement(s);
2689 ErasePath();
2690 }
2691 else
2692 {
2693 HandleLocateSelectMerc(s, false);
2694 }
2695 }
2696 else
2697 {
2698 SelectSoldier(s, SELSOLDIER_ACKNOWLEDGE);
2699 }
2700 }
2701 }
2702 }
2703 else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_DWN)
2704 {
2705 if (!InOverheadMap())
2706 {
2707 // Only if guy is not dead!
2708 if (!(s->uiStatusFlags & SOLDIER_DEAD) &&
2709 !AM_AN_EPC(s) &&
2710 !(s->uiStatusFlags & (SOLDIER_DRIVER | SOLDIER_PASSENGER)))
2711 {
2712 SetNewPanel(s);
2713 }
2714 }
2715 }
2716 }
2717
2718
HandleLocateSelectMerc(SOLDIERTYPE * const s,bool const force_select)2719 void HandleLocateSelectMerc(SOLDIERTYPE* const s, bool const force_select)
2720 {
2721 if (!s->bActive) return;
2722
2723 if (gpItemPointer != NULL)
2724 {
2725 // ATE: Disable for feel purposes....
2726 //return;
2727 }
2728
2729 // ATE: No matter what we do... if below OKLIFE, just locate....
2730 if (s->bLife < OKLIFE)
2731 {
2732 LocateSoldier(s, SETLOCATOR);
2733 return;
2734 }
2735
2736 if (_KeyDown(ALT))
2737 {
2738 if (gGameSettings.fOptions[TOPTION_OLD_SELECTION_METHOD])
2739 {
2740 // Select merc
2741 SelectSoldier(s, SELSOLDIER_ACKNOWLEDGE | SELSOLDIER_FROM_UI);
2742 s->fFlashLocator = FALSE;
2743 ResetMultiSelection();
2744 }
2745 else
2746 {
2747 // Just locate....
2748 LocateSoldier(s, SETLOCATOR);
2749 }
2750 }
2751 else
2752 {
2753 BOOLEAN fSelect = force_select;
2754
2755 if (s->fFlashLocator == FALSE)
2756 {
2757 // If we are currently selected, slide to location
2758 if (s == GetSelectedMan())
2759 {
2760 SlideTo(s, SETLOCATOR);
2761 }
2762 else
2763 {
2764 if (gGameSettings.fOptions[TOPTION_OLD_SELECTION_METHOD])
2765 {
2766 LocateSoldier(s, SETLOCATOR);
2767 }
2768 else
2769 {
2770 fSelect = TRUE;
2771 }
2772 }
2773 }
2774 else
2775 {
2776 if (gGameSettings.fOptions[TOPTION_OLD_SELECTION_METHOD])
2777 {
2778 // If we are currently selected, slide to location
2779 if (s == GetSelectedMan())
2780 {
2781 SlideTo(s, DONTSETLOCATOR);
2782 }
2783 else
2784 {
2785 LocateSoldier(s, DONTSETLOCATOR);
2786 }
2787
2788 fSelect = TRUE;
2789 }
2790 else
2791 {
2792 if (s == GetSelectedMan())
2793 {
2794 LocateSoldier(s, DONTSETLOCATOR);
2795 }
2796 else
2797 {
2798 fSelect = TRUE;
2799 }
2800 }
2801 }
2802
2803 if (fSelect)
2804 {
2805 // Select merc, only if alive!
2806 if (!(s->uiStatusFlags & SOLDIER_DEAD))
2807 {
2808 SelectSoldier(s, SELSOLDIER_ACKNOWLEDGE | SELSOLDIER_FROM_UI);
2809 }
2810 }
2811 }
2812
2813 ResetMultiSelection();
2814
2815 // Handle locate select merc....
2816 HandleMouseOverSoldierFaceForContMove(s, TRUE);
2817 }
2818
2819
ShowRadioLocator(SOLDIERTYPE * s,UINT8 ubLocatorSpeed)2820 void ShowRadioLocator(SOLDIERTYPE* s, UINT8 ubLocatorSpeed)
2821 {
2822 //LocateSoldier(s, FALSE); // IC - this is already being done outside of this function :)
2823 s->fFlashLocator = TRUE;
2824 s->sLocatorFrame = 0;
2825
2826 if ( ubLocatorSpeed == SHOW_LOCATOR_NORMAL )
2827 {
2828 s->ubNumLocateCycles = 5;
2829 }
2830 else
2831 {
2832 s->ubNumLocateCycles = 3;
2833 }
2834 }
2835
2836
EndRadioLocator(SOLDIERTYPE * s)2837 void EndRadioLocator(SOLDIERTYPE* s)
2838 {
2839 s->fFlashLocator = FALSE;
2840 s->fShowLocator = FALSE;
2841 }
2842
2843
FinishAnySkullPanelAnimations(void)2844 void FinishAnySkullPanelAnimations(void)
2845 {
2846 FOR_EACH_IN_TEAM(s, OUR_TEAM)
2847 {
2848 if (s->bLife == 0 &&
2849 (s->fUIdeadMerc || s->fClosePanelToDie))
2850 {
2851 HandlePlayerTeamMemberDeathAfterSkullAnimation(s);
2852
2853 s->fUIdeadMerc = FALSE;
2854 s->fClosePanelToDie = FALSE;
2855 }
2856 }
2857 }
2858
2859
HandlePanelFaceAnimations(SOLDIERTYPE * pSoldier)2860 void HandlePanelFaceAnimations(SOLDIERTYPE* pSoldier)
2861 {
2862 if ( pSoldier->bTeam != OUR_TEAM )
2863 {
2864 return;
2865 }
2866
2867
2868 if ( !pSoldier->bActive )
2869 {
2870 return;
2871 }
2872
2873 if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
2874 {
2875 // Don't do this for a vehice.
2876 return;
2877 }
2878
2879 if ( pSoldier->fUICloseMerc )
2880 {
2881 pSoldier->fUICloseMerc = FALSE;
2882 }
2883
2884 if ( pSoldier->fUIdeadMerc )
2885 {
2886 pSoldier->sPanelFaceX = pSoldier->face->usFaceX;
2887 pSoldier->sPanelFaceY = pSoldier->face->usFaceY;
2888
2889 pSoldier->fUIdeadMerc = FALSE;
2890 pSoldier->fClosePanel = TRUE;
2891 pSoldier->fClosePanelToDie = TRUE;
2892 pSoldier->ubClosePanelFrame = 0;
2893 pSoldier->ubDeadPanelFrame = 0;
2894 RESETTIMECOUNTER( pSoldier->PanelAnimateCounter, 160 );
2895 }
2896
2897 if ( pSoldier->fClosePanel )
2898 {
2899 if ( TIMECOUNTERDONE( pSoldier->PanelAnimateCounter, 160 ) )
2900 {
2901 pSoldier->ubClosePanelFrame++;
2902
2903 if ( pSoldier->ubClosePanelFrame > 5 )
2904 {
2905 pSoldier->fClosePanel = FALSE;
2906 pSoldier->ubClosePanelFrame = 5;
2907
2908 if ( pSoldier->fClosePanelToDie )
2909 {
2910 pSoldier->fDeadPanel = TRUE;
2911 //PlayJA2Sample(HEADCR_1, HIGHVOLUME, 1, MIDDLEPAN);
2912 }
2913 else
2914 {
2915 if (!pSoldier->face->fDisabled)
2916 {
2917 RestoreExternBackgroundRect( pSoldier->sPanelFaceX, pSoldier->sPanelFaceY, TM_FACE_WIDTH, TM_FACE_HEIGHT );
2918 }
2919 }
2920 }
2921 RESETTIMECOUNTER( pSoldier->PanelAnimateCounter, 160 );
2922 }
2923 }
2924
2925 if ( pSoldier->fClosePanel )
2926 {
2927 if (!pSoldier->face->fDisabled)
2928 {
2929 RestoreExternBackgroundRect(pSoldier->sPanelFaceX, pSoldier->sPanelFaceY, TM_FACE_WIDTH, TM_FACE_HEIGHT);
2930 BltVideoObject(FRAME_BUFFER, guiCLOSE, pSoldier->ubClosePanelFrame,
2931 pSoldier->sPanelFaceX, pSoldier->sPanelFaceY);
2932 InvalidateRegion(pSoldier->sPanelFaceX, pSoldier->sPanelFaceY,
2933 pSoldier->sPanelFaceX + TM_FACE_WIDTH,
2934 pSoldier->sPanelFaceY + TM_FACE_HEIGHT);
2935 }
2936 }
2937
2938
2939 if ( pSoldier->fDeadPanel )
2940 {
2941 if ( TIMECOUNTERDONE( pSoldier->PanelAnimateCounter, 160 ) )
2942 {
2943 pSoldier->ubDeadPanelFrame++;
2944
2945 if ( pSoldier->ubDeadPanelFrame == 4 )
2946 {
2947 ScreenMsg(FONT_RED, MSG_SKULL_UI_FEEDBACK, st_format_printf(pMercDeadString, pSoldier->name));
2948
2949 PlayJA2Sample(DOORCR_1, HIGHVOLUME, 1, MIDDLEPAN);
2950 PlayJA2Sample(HEADCR_1, HIGHVOLUME, 1, MIDDLEPAN);
2951 }
2952
2953 if ( pSoldier->ubDeadPanelFrame > 5 )
2954 {
2955 pSoldier->fDeadPanel = FALSE;
2956 pSoldier->ubDeadPanelFrame = 5;
2957 pSoldier->fClosePanelToDie = FALSE;
2958
2959 // Finish!
2960 if (!pSoldier->face->fDisabled)
2961 {
2962 BltVideoObject(guiSAVEBUFFER, guiDEAD, pSoldier->ubDeadPanelFrame,
2963 pSoldier->sPanelFaceX, pSoldier->sPanelFaceY);
2964
2965 // Blit hatch!
2966 BltVideoObject(guiSAVEBUFFER, guiHATCH, 0, pSoldier->sPanelFaceX,
2967 pSoldier->sPanelFaceY);
2968
2969 RestoreExternBackgroundRect(pSoldier->sPanelFaceX,
2970 pSoldier->sPanelFaceY, TM_FACE_WIDTH,
2971 TM_FACE_HEIGHT);
2972 }
2973 HandlePlayerTeamMemberDeathAfterSkullAnimation( pSoldier );
2974
2975 }
2976 RESETTIMECOUNTER( pSoldier->PanelAnimateCounter, 160 );
2977 }
2978 }
2979
2980 if ( pSoldier->fDeadPanel )
2981 {
2982 // Render panel!
2983 if (!pSoldier->face->fDisabled)
2984 {
2985 BltVideoObject(FRAME_BUFFER, guiDEAD, pSoldier->ubDeadPanelFrame,
2986 pSoldier->sPanelFaceX, pSoldier->sPanelFaceY);
2987
2988 // Blit hatch!
2989 BltVideoObject(guiSAVEBUFFER, guiHATCH, 0, pSoldier->sPanelFaceX, pSoldier->sPanelFaceY);
2990
2991 InvalidateRegion(pSoldier->sPanelFaceX, pSoldier->sPanelFaceY,
2992 pSoldier->sPanelFaceX + TM_FACE_WIDTH,
2993 pSoldier->sPanelFaceY + TM_FACE_HEIGHT);
2994 }
2995 }
2996 }
2997
2998
RenderSoldierTeamInv(SOLDIERTYPE const & s,INT16 const x,INT16 y,DirtyLevel const dirty_level)2999 static void RenderSoldierTeamInv(SOLDIERTYPE const& s, INT16 const x, INT16 y, DirtyLevel const dirty_level)
3000 {
3001 if (s.uiStatusFlags & SOLDIER_DEAD) return;
3002
3003 SGPVSurface* const buf = guiSAVEBUFFER;
3004 INT16 const w = TM_INV_WIDTH;
3005 INT16 const h = TM_INV_HEIGHT;
3006 if (s.uiStatusFlags & SOLDIER_DRIVER)
3007 {
3008 BltVideoObject(buf, guiVEHINV, 0, x, y);
3009 RestoreExternBackgroundRect(x, y, w, h);
3010 }
3011 else
3012 {
3013 // Look in primary hand
3014 INVRenderItem(buf, &s, s.inv[HANDPOS], x, y, w, h, dirty_level, 0, SGP_TRANSPARENT);
3015 }
3016
3017 y += TM_INV_HAND_SEPY;
3018 if (s.uiStatusFlags & (SOLDIER_PASSENGER | SOLDIER_DRIVER))
3019 {
3020 BltVideoObject(buf, guiVEHINV, 1, x, y);
3021 RestoreExternBackgroundRect(x, y, w, h);
3022 }
3023 else
3024 {
3025 // Do secondary hand
3026 INVRenderItem(buf, &s, s.inv[SECONDHANDPOS], x, y, w, h, dirty_level, 0, SGP_TRANSPARENT);
3027 }
3028 }
3029
3030
TMClickFirstHandInvCallback(MOUSE_REGION * pRegion,INT32 iReason)3031 static void TMClickFirstHandInvCallback(MOUSE_REGION* pRegion, INT32 iReason)
3032 {
3033 SOLDIERTYPE* const s = pRegion->GetUserPtr<TeamPanelSlot>()->merc;
3034 if (!s) return;
3035
3036 if (iReason == MSYS_CALLBACK_REASON_LBUTTON_UP )
3037 {
3038 // Change to use cursor mode...
3039 guiPendingOverrideEvent = A_ON_TERRAIN;
3040 }
3041
3042 if (iReason == MSYS_CALLBACK_REASON_RBUTTON_UP )
3043 {
3044 if (!AM_A_ROBOT(s))
3045 {
3046 const UINT16 usOldHandItem = s->inv[HANDPOS].usItem;
3047 SwapHandItems(s);
3048 ReLoadSoldierAnimationDueToHandItemChange(s, usOldHandItem, s->inv[HANDPOS].usItem);
3049 fInterfacePanelDirty = DIRTYLEVEL2;
3050 }
3051 }
3052 }
3053
3054
TMClickSecondHandInvCallback(MOUSE_REGION * pRegion,INT32 iReason)3055 static void TMClickSecondHandInvCallback(MOUSE_REGION* pRegion, INT32 iReason)
3056 {
3057 SOLDIERTYPE* const s = pRegion->GetUserPtr<TeamPanelSlot>()->merc;
3058 if (!s) return;
3059
3060 if (iReason == MSYS_CALLBACK_REASON_LBUTTON_UP )
3061 {
3062 if (s->uiStatusFlags & (SOLDIER_PASSENGER | SOLDIER_DRIVER))
3063 {
3064 ExitVehicle(s);
3065 }
3066 }
3067
3068 if (iReason == MSYS_CALLBACK_REASON_RBUTTON_UP )
3069 {
3070 if (s->uiStatusFlags & (SOLDIER_PASSENGER | SOLDIER_DRIVER))
3071 {
3072 }
3073 else
3074 {
3075 if (!AM_A_ROBOT(s))
3076 {
3077 const UINT16 usOldHandItem = s->inv[HANDPOS].usItem;
3078 SwapHandItems(s);
3079 ReLoadSoldierAnimationDueToHandItemChange(s, usOldHandItem, s->inv[HANDPOS].usItem);
3080 fInterfacePanelDirty = DIRTYLEVEL2;
3081 }
3082 }
3083 }
3084 }
3085
3086
PlayerExistsInSlot(const SOLDIERTYPE * const s)3087 static BOOLEAN PlayerExistsInSlot(const SOLDIERTYPE* const s)
3088 {
3089 FOR_EACH_TEAM_PANEL_SLOT(i)
3090 {
3091 if (i->merc == s) return TRUE;
3092 }
3093 return FALSE;
3094 }
3095
3096
3097 static void RemovePlayerFromInterfaceTeamSlot(TeamPanelSlot&);
3098
3099
RemovePlayerFromTeamSlot(const SOLDIERTYPE * const s)3100 BOOLEAN RemovePlayerFromTeamSlot(const SOLDIERTYPE* const s)
3101 {
3102 FOR_EACH_TEAM_PANEL_SLOT(i)
3103 {
3104 if (i->merc != s) continue;
3105 RemovePlayerFromInterfaceTeamSlot(*i);
3106 return TRUE;
3107 }
3108 return FALSE;
3109 }
3110
3111
AddPlayerToInterfaceTeamSlot(SOLDIERTYPE * const s)3112 static void AddPlayerToInterfaceTeamSlot(SOLDIERTYPE* const s)
3113 {
3114 if (PlayerExistsInSlot(s)) return;
3115
3116 // Find a free slot
3117 FOR_EACH_TEAM_PANEL_SLOT(i)
3118 {
3119 if (i->merc) continue;
3120 i->merc = s;
3121 fInterfacePanelDirty = DIRTYLEVEL2;
3122 break;
3123 }
3124 }
3125
3126
InitTEAMSlots(void)3127 void InitTEAMSlots(void)
3128 {
3129 FOR_EACH_TEAM_PANEL_SLOT(i) i->merc = 0;
3130 }
3131
3132
GetPlayerFromInterfaceTeamSlot(UINT8 ubPanelSlot)3133 SOLDIERTYPE* GetPlayerFromInterfaceTeamSlot(UINT8 ubPanelSlot)
3134 {
3135 if (ubPanelSlot >= NUM_TEAM_SLOTS) return NULL;
3136 return gTeamPanel[ubPanelSlot].merc;
3137 }
3138
3139
RemoveAllPlayersFromSlot(void)3140 void RemoveAllPlayersFromSlot(void)
3141 {
3142 FOR_EACH_TEAM_PANEL_SLOT(i)
3143 {
3144 if (!i->merc) continue;
3145 RemovePlayerFromInterfaceTeamSlot(*i);
3146 }
3147 }
3148
3149
RemovePlayerFromInterfaceTeamSlot(TeamPanelSlot & tp)3150 static void RemovePlayerFromInterfaceTeamSlot(TeamPanelSlot& tp)
3151 {
3152 SOLDIERTYPE* const s = tp.merc;
3153 tp.merc = 0;
3154
3155 if (!(s->uiStatusFlags & SOLDIER_DEAD))
3156 {
3157 // Set Id to close
3158 s->fUICloseMerc = TRUE;
3159 }
3160
3161 // Set face to inactive...
3162 if (s->face) SetAutoFaceInActive(*s->face);
3163
3164 // DIRTY INTERFACE
3165 fInterfacePanelDirty = DIRTYLEVEL2;
3166 }
3167
3168
RenderTownIDString(void)3169 void RenderTownIDString(void)
3170 {
3171 INT16 sFontX, sFontY;
3172
3173 // Render town, position
3174 SetFontAttributes(COMPFONT, 183);
3175 ST::string zTownIDString = GetSectorIDString(gWorldSectorX, gWorldSectorY, gbWorldSectorZ, TRUE);
3176 zTownIDString = ReduceStringLength(zTownIDString, 80, COMPFONT);
3177 FindFontCenterCoordinates(548, SCREEN_HEIGHT - 55, 80, 16, zTownIDString, COMPFONT, &sFontX, &sFontY);
3178 MPrint(sFontX, sFontY, zTownIDString);
3179 }
3180
3181
CheckForAndAddMercToTeamPanel(SOLDIERTYPE * const s)3182 void CheckForAndAddMercToTeamPanel(SOLDIERTYPE* const s)
3183 {
3184 if (!s->bActive) return;
3185 if (s->bTeam != OUR_TEAM) return;
3186
3187 // Are we in the loaded sector?
3188 if (s->sSectorX == gWorldSectorX &&
3189 s->sSectorY == gWorldSectorY &&
3190 s->bSectorZ == gbWorldSectorZ &&
3191 !s->fBetweenSectors &&
3192 s->bInSector)
3193 {
3194 // IF on duty....
3195 INT32 const cur_squad = CurrentSquad();
3196 if (s->bAssignment == cur_squad || SoldierIsDeadAndWasOnSquad(s, cur_squad))
3197 {
3198 if (s->bAssignment == ASSIGNMENT_DEAD) s->fUICloseMerc = FALSE;
3199
3200 if (s->uiStatusFlags & SOLDIER_VEHICLE)
3201 {
3202 VEHICLETYPE const& v = GetVehicle(s->bVehicleID);
3203 CFOR_EACH_PASSENGER(v, i) AddPlayerToInterfaceTeamSlot(*i);
3204 }
3205 else if (s->ubStrategicInsertionCode != INSERTION_CODE_CHOPPER)
3206 {
3207 // ATE: If we have the insertion code of helicopter, don't add just yet!
3208 // (will add in heli code)
3209 AddPlayerToInterfaceTeamSlot(s);
3210 }
3211 }
3212 }
3213 else
3214 {
3215 // Make sure we are NOT in this world!
3216 RemoveSoldierFromGridNo(*s);
3217 RemoveMercSlot(s);
3218 }
3219 }
3220
3221
FindNextMercInTeamPanel(SOLDIERTYPE * const prev)3222 SOLDIERTYPE* FindNextMercInTeamPanel(SOLDIERTYPE* const prev)
3223 {
3224 bool seen_prev = false;
3225 SOLDIERTYPE* before = 0;
3226 FOR_EACH_TEAM_PANEL_SLOT(i)
3227 {
3228 SOLDIERTYPE* const s = i->merc;
3229 if (!s) continue;
3230
3231 if (s == prev)
3232 {
3233 seen_prev = true;
3234 continue;
3235 }
3236
3237 if (!OK_CONTROLLABLE_MERC(s)) continue;
3238 if (!OK_INTERRUPT_MERC(s)) continue;
3239 if (s->bAssignment != prev->bAssignment) continue;
3240 if (seen_prev) return s;
3241 if (!before) before = s;
3242 }
3243 return seen_prev && before ? before : prev;
3244 }
3245
3246
DisableTacticalTeamPanelButtons(BOOLEAN fDisable)3247 void DisableTacticalTeamPanelButtons(BOOLEAN fDisable)
3248 {
3249 gfDisableTacticalPanelButtons = fDisable;
3250 }
3251
3252
BeginKeyPanelFromKeyShortcut(void)3253 void BeginKeyPanelFromKeyShortcut(void)
3254 {
3255 SOLDIERTYPE *pSoldier = NULL;
3256 INT16 sStartYPosition = 0;
3257 INT16 sWidth = 0, sHeight = 0;
3258
3259 if ( gsCurInterfacePanel != SM_PANEL )
3260 {
3261 return;
3262 }
3263
3264 if ( gpSMCurrentMerc == NULL )
3265 {
3266 return;
3267 }
3268
3269 if ( gfInKeyRingPopup )
3270 {
3271 return;
3272 }
3273
3274
3275 sStartYPosition = INV_INTERFACE_START_Y;
3276 sWidth = SCREEN_WIDTH;
3277 sHeight = SCREEN_HEIGHT - INV_INTERFACE_START_Y;
3278 pSoldier = gpSMCurrentMerc;
3279
3280 //if we are in the shop keeper interface
3281 if (guiCurrentScreen == SHOPKEEPER_SCREEN) return;
3282
3283 InitKeyRingPopup( pSoldier, 0, sStartYPosition, sWidth, sHeight );
3284 }
3285
3286
KeyRingItemPanelButtonCallback(MOUSE_REGION * pRegion,INT32 iReason)3287 void KeyRingItemPanelButtonCallback(MOUSE_REGION* pRegion, INT32 iReason)
3288 {
3289 SOLDIERTYPE *pSoldier = NULL;
3290 INT16 sStartYPosition = 0;
3291 INT16 sWidth = 0, sHeight = 0;
3292
3293 if( guiCurrentScreen == MAP_SCREEN )
3294 {
3295 pSoldier = GetSelectedInfoChar();
3296 if (pSoldier == NULL) return;
3297
3298 sStartYPosition = MAP_START_KEYRING_Y;
3299 sWidth = 261;
3300 sHeight = ( 359 - 107 );
3301 }
3302 else
3303 {
3304 if ( gpSMCurrentMerc == NULL )
3305 {
3306 return;
3307 }
3308
3309 sStartYPosition = INV_INTERFACE_START_Y;
3310 sWidth = SCREEN_WIDTH;
3311 sHeight = SCREEN_HEIGHT - INV_INTERFACE_START_Y;
3312 pSoldier = gpSMCurrentMerc;
3313 }
3314
3315 //if we are in the shop keeper interface
3316 if (guiCurrentScreen == SHOPKEEPER_SCREEN) return;
3317
3318 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
3319 {
3320 if( guiCurrentScreen == MAP_SCREEN )
3321 {
3322 // shade the background
3323 FRAME_BUFFER->ShadowRect(STD_SCREEN_X + 0, STD_SCREEN_Y + 107,
3324 STD_SCREEN_X + 261, STD_SCREEN_Y + 359);
3325 InvalidateRegion(STD_SCREEN_X + 0, STD_SCREEN_Y + 107, STD_SCREEN_X + 261, STD_SCREEN_Y + 359);
3326 InitKeyRingPopup(pSoldier, STD_SCREEN_X + 0, sStartYPosition, sWidth, sHeight);
3327 }
3328 else
3329 {
3330 InitKeyRingPopup( pSoldier, 0, sStartYPosition, sWidth, sHeight );
3331 }
3332 }
3333 }
3334
KeyRingSlotInvClickCallback(MOUSE_REGION * pRegion,INT32 iReason)3335 void KeyRingSlotInvClickCallback( MOUSE_REGION * pRegion, INT32 iReason )
3336 {
3337 UINT32 uiKeyRing;
3338 // Copyies of values
3339 UINT16 usOldItemIndex;
3340 static BOOLEAN fRightDown = FALSE;
3341
3342 uiKeyRing = MSYS_GetRegionUserData( pRegion, 0 );
3343
3344 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN )
3345 {
3346 //if we are in the shop keeper interface
3347 if (guiCurrentScreen == SHOPKEEPER_SCREEN)
3348 {
3349 INVENTORY_IN_SLOT InvSlot;
3350
3351 if( gMoveingItem.sItemIndex == 0 )
3352 {
3353 //Delete the contents of the item cursor
3354 gMoveingItem = INVENTORY_IN_SLOT{};
3355 }
3356 else
3357 {
3358 InvSlot = INVENTORY_IN_SLOT{};
3359
3360 // Return if empty
3361 //if ( gpSMCurrentMerc->inv[ uiHandPos ].usItem == NOTHING )
3362 // return;
3363
3364
3365 // Fill out the inv slot for the item
3366 //InvSlot.sItemIndex = gpSMCurrentMerc->inv[ uiHandPos ].usItem;
3367 //InvSlot.ubNumberOfItems = gpSMCurrentMerc->inv[ uiHandPos ].ubNumberOfObjects;
3368 //InvSlot.ubItemQuality = gpSMCurrentMerc->inv[ uiHandPos ].bGunStatus;
3369 //InvSlot.ItemObject = gpSMCurrentMerc->inv[uiHandPos];
3370 //InvSlot.ubLocationOfObject = PLAYERS_INVENTORY;
3371
3372 //InvSlot.ubIdOfMercWhoOwnsTheItem = gpSMCurrentMerc->ubProfile;
3373
3374
3375 //Add the item to the Players Offer Area
3376 //AddItemToPlayersOfferArea( gpSMCurrentMerc->ubProfile, &InvSlot, (UINT8)uiHandPos );
3377
3378 // Dirty
3379 fInterfacePanelDirty = DIRTYLEVEL2;
3380 }
3381 return;
3382 }
3383
3384 // If we do not have an item in hand, start moving it
3385 if ( gpItemPointer == NULL )
3386 {
3387 // Return if empty
3388 if( ( gpItemPopupSoldier->pKeyRing[ uiKeyRing ].ubKeyID == INVALID_KEY_NUMBER ) || ( gpItemPopupSoldier->pKeyRing[ uiKeyRing ].ubNumber == 0 ) )
3389 return;
3390
3391 // If our flags are set to do this, gofoit!
3392 if (!fInMapMode)
3393 {
3394 SelectSoldier(gpItemPopupSoldier, SELSOLDIER_NONE);
3395 }
3396
3397 usOldItemIndex = ( UINT16 )uiKeyRing ;
3398
3399 BeginKeyRingItemPointer( gpItemPopupSoldier, (UINT8)usOldItemIndex );
3400 //BeginItemPointer( gpSMCurrentMerc, (UINT8)uiHandPos );
3401
3402 }
3403 else
3404 {
3405 BOOLEAN fOKToGo = FALSE;
3406 BOOLEAN fDeductPoints = FALSE;
3407
3408 if( ( gpItemPointer ->usItem < FIRST_KEY ) || ( gpItemPointer ->usItem > KEY_32 ) )
3409 {
3410 return;
3411 }
3412
3413 // ATE: OK, get source, dest guy if different... check for and then charge appropriate APs
3414 if (gpItemPointerSoldier == NULL ||
3415 gpItemPointerSoldier == GetSelectedInfoChar())
3416 {
3417 // We are doing this ourselve, continue
3418 fOKToGo = TRUE;
3419 }
3420 else
3421 {
3422 // These guys are different....
3423 fDeductPoints = TRUE;
3424
3425 // First check points for src guy
3426 if ( gpItemPointerSoldier->bLife >= CONSCIOUSNESS )
3427 {
3428 if ( EnoughPoints( gpItemPointerSoldier, 2, 0, TRUE ) )
3429 {
3430 fOKToGo = TRUE;
3431 }
3432 }
3433 else
3434 {
3435 fOKToGo = TRUE;
3436 }
3437
3438 // Should we go on?
3439 if ( fOKToGo )
3440 {
3441 if ( gpSMCurrentMerc->bLife >= CONSCIOUSNESS )
3442 {
3443 if ( EnoughPoints( gpSMCurrentMerc, 2, 0, TRUE ) )
3444 {
3445 fOKToGo = TRUE;
3446 }
3447 else
3448 {
3449 fOKToGo = FALSE;
3450 }
3451 }
3452 }
3453 }
3454
3455 if ( fOKToGo )
3456 {
3457 //usOldItemIndex = gpSMCurrentMerc->inv[ uiHandPos ].usItem;
3458 //usNewItemIndex = gpItemPointer->usItem;
3459
3460 if ( gpItemPopupSoldier->pKeyRing[ uiKeyRing ].ubKeyID == INVALID_KEY_NUMBER || gpItemPopupSoldier->pKeyRing[ uiKeyRing ].ubKeyID == gpItemPointer->ubKeyID)
3461 {
3462 // Try to place here
3463 INT32 const iNumberOfKeysTaken = AddKeysToSlot(*gpItemPopupSoldier, (INT8)uiKeyRing, *gpItemPointer);
3464 if (iNumberOfKeysTaken != 0)
3465 {
3466
3467 if ( fDeductPoints )
3468 {
3469 // Deduct points
3470 if ( gpItemPointerSoldier->bLife >= CONSCIOUSNESS )
3471 {
3472 DeductPoints( gpItemPointerSoldier, 2, 0 );
3473 }
3474 if ( gpItemPopupSoldier->bLife >= CONSCIOUSNESS )
3475 {
3476 DeductPoints( gpItemPopupSoldier, 2, 0 );
3477 }
3478 }
3479
3480 // Dirty
3481 fInterfacePanelDirty = DIRTYLEVEL2;
3482
3483 gpItemPointer->ubNumberOfObjects -= ( UINT8 )iNumberOfKeysTaken;
3484
3485 // Check if it's the same now!
3486 if ( gpItemPointer->ubNumberOfObjects == 0 )
3487 {
3488 if (fInMapMode)
3489 {
3490 MAPEndItemPointer();
3491 }
3492 else
3493 {
3494 EndItemPointer();
3495 }
3496 }
3497
3498 // Setup a timer....
3499 //guiMouseOverItemTime = GetJA2Clock( );
3500 //gfCheckForMouseOverItem = TRUE;
3501 //gbCheckForMouseOverItemPos = (INT8)uiHandPos;
3502
3503 }
3504 }
3505 else
3506 {
3507 // Swap!
3508 SwapKeysToSlot(*gpItemPopupSoldier, (INT8)uiKeyRing, *gpItemPointer);
3509
3510 if ( fDeductPoints )
3511 {
3512 // Deduct points
3513 if ( gpItemPointerSoldier && gpItemPointerSoldier->bLife >= CONSCIOUSNESS )
3514 {
3515 DeductPoints( gpItemPointerSoldier, 2, 0 );
3516 }
3517 if ( gpSMCurrentMerc->bLife >= CONSCIOUSNESS )
3518 {
3519 DeductPoints( gpSMCurrentMerc, 2, 0 );
3520 }
3521 }
3522
3523 // Dirty
3524 fInterfacePanelDirty = DIRTYLEVEL2;
3525
3526 }
3527 }
3528 }
3529
3530 UpdateItemHatches();
3531 }
3532 else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_DWN)
3533 {
3534 fRightDown = TRUE;
3535 }
3536 else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP && fRightDown )
3537 {
3538 fRightDown = FALSE;
3539
3540 // Return if empty
3541 if( ( gpItemPopupSoldier->pKeyRing[ uiKeyRing ].ubKeyID == INVALID_KEY_NUMBER ) || ( gpItemPopupSoldier->pKeyRing[ uiKeyRing ].ubNumber == 0 ) )
3542 {
3543 DeleteKeyRingPopup( );
3544 fTeamPanelDirty = TRUE;
3545 return;
3546 }
3547 // Some global stuff here - for esc, etc
3548 // Check for # of slots in item
3549 if ( !InItemDescriptionBox( ) )
3550 {
3551 if (fInMapMode)
3552 {
3553 //InitKeyItemDescriptionBox(gpItemPopupSoldier, (UINT8)uiKeyRing, MAP_ITEMDESC_START_X, MAP_ITEMDESC_START_Y);
3554 }
3555 else
3556 {
3557 InitKeyItemDescriptionBox(gpItemPopupSoldier, (UINT8)uiKeyRing, SM_ITEMDESC_START_X, SM_ITEMDESC_START_Y);
3558 }
3559 }
3560 }
3561 else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
3562 {
3563 fRightDown = FALSE;
3564 }
3565
3566 }
3567
3568
ShopKeeperInterface_SetSMpanelButtonsState(bool const enabled)3569 void ShopKeeperInterface_SetSMpanelButtonsState(bool const enabled)
3570 {
3571 //Go through the buttons that will be under the ShopKeepers ATM panel and disable them
3572 EnableButton( iSMPanelButtons[ STANCEUP_BUTTON ], enabled );
3573 EnableButton( iSMPanelButtons[ UPDOWN_BUTTON ], enabled );
3574 EnableButton( iSMPanelButtons[ CLIMB_BUTTON ], enabled );
3575 EnableButton( iSMPanelButtons[ STANCEDOWN_BUTTON ], enabled );
3576 EnableButton( iSMPanelButtons[ HANDCURSOR_BUTTON ], enabled );
3577 EnableButton( iSMPanelButtons[ BURSTMODE_BUTTON ], enabled );
3578 EnableButton( iSMPanelButtons[ LOOK_BUTTON ], enabled );
3579 EnableButton( iSMPanelButtons[ TALK_BUTTON ], enabled );
3580 EnableButton( iSMPanelButtons[ MUTE_BUTTON ], enabled );
3581
3582 EnableButton( giSMStealthButton, enabled );
3583
3584 //Make sure the options button is disabled
3585 EnableButton( iSMPanelButtons[ OPTIONS_BUTTON ], enabled );
3586
3587 //Make sure the mapscreen button is disabled
3588 EnableButton( iSMPanelButtons[ SM_MAP_SCREEN_BUTTON ], enabled );
3589
3590 EnableButton( iSMPanelButtons[ STANCEUP_BUTTON ], enabled );
3591 EnableButton( iSMPanelButtons[ UPDOWN_BUTTON ], enabled );
3592 EnableButton( iSMPanelButtons[ CLIMB_BUTTON ], enabled );
3593 EnableButton( iSMPanelButtons[ STANCEDOWN_BUTTON ], enabled );
3594 EnableButton( iSMPanelButtons[ HANDCURSOR_BUTTON ], enabled );
3595 EnableButton( iSMPanelButtons[ BURSTMODE_BUTTON ], enabled );
3596 EnableButton( iSMPanelButtons[ LOOK_BUTTON ], enabled );
3597 EnableButton( iSMPanelButtons[ TALK_BUTTON ], enabled );
3598 EnableButton( iSMPanelButtons[ MUTE_BUTTON ], enabled );
3599
3600 EnableButton( giSMStealthButton, enabled );
3601 }
3602
3603
IsMouseInRegion(MOUSE_REGION const & r)3604 static bool IsMouseInRegion(MOUSE_REGION const& r)
3605 {
3606 return r.RegionTopLeftX <= gusMouseXPos && gusMouseXPos <= r.RegionBottomRightX &&
3607 r.RegionTopLeftY <= gusMouseYPos && gusMouseYPos <= r.RegionBottomRightY;
3608 }
3609
3610
3611 static void ConfirmationToDepositMoneyToPlayersAccount(MessageBoxReturnValue);
3612
3613
SMInvMoneyButtonCallback(MOUSE_REGION * pRegion,INT32 iReason)3614 static void SMInvMoneyButtonCallback(MOUSE_REGION* pRegion, INT32 iReason)
3615 {
3616 if (iReason == MSYS_CALLBACK_REASON_LBUTTON_DWN )
3617 {
3618 //If the current merc is to far away, dont allow anything to be done
3619 if( gfSMDisableForItems )
3620 return;
3621
3622 //if the player has an item in his hand,
3623 if( gpItemPointer != NULL )
3624 {
3625 //and the item is money
3626 if( GCM->getItem(gpItemPointer->usItem)->getItemClass() == IC_MONEY )
3627 {
3628 ST::string zText;
3629 ST::string zMoney;
3630
3631 // Make sure we go back to movement mode...
3632 guiPendingOverrideEvent = A_CHANGE_TO_MOVE;
3633 HandleTacticalUI( );
3634
3635 zMoney = SPrintMoney(gpItemPointer->uiMoneyAmount);
3636
3637 //ask the user if they are sure they want to deposit the money
3638 zText = st_format_printf(gzMoneyWithdrawMessageText[ CONFIRMATION_TO_DEPOSIT_MONEY_TO_ACCOUNT ], zMoney);
3639
3640 if( guiCurrentScreen == SHOPKEEPER_SCREEN )
3641 {
3642 //if we are in the shop keeper interface, free the cursor
3643 FreeMouseCursor();
3644 DoMessageBox(MSG_BOX_BASIC_STYLE, zText, SHOPKEEPER_SCREEN, MSG_BOX_FLAG_YESNO, ConfirmationToDepositMoneyToPlayersAccount, NULL);
3645 }
3646 else
3647 DoMessageBox(MSG_BOX_BASIC_STYLE, zText, GAME_SCREEN, MSG_BOX_FLAG_YESNO, ConfirmationToDepositMoneyToPlayersAccount, NULL);
3648 }
3649 }
3650
3651 //else bring up the money item description box to remove money from the players account
3652 else
3653 {
3654 //set the flag indicating we are removing money from the players account
3655 gfAddingMoneyToMercFromPlayersAccount = TRUE;
3656
3657 //create the temp object from the players account balance
3658 //if( LaptopSaveInfo.iCurrentBalance > MAX_MONEY_PER_SLOT )
3659 // CreateMoney( MAX_MONEY_PER_SLOT, &gItemPointer );
3660 //else
3661 CreateMoney( LaptopSaveInfo.iCurrentBalance, &gItemPointer );
3662
3663 InternalInitItemDescriptionBox(&gItemPointer, SM_ITEMDESC_START_X, SM_ITEMDESC_START_Y,
3664 0, gpSMCurrentMerc);
3665 }
3666 }
3667 }
3668
3669
ConfirmationToDepositMoneyToPlayersAccount(MessageBoxReturnValue const ubExitValue)3670 static void ConfirmationToDepositMoneyToPlayersAccount(MessageBoxReturnValue const ubExitValue)
3671 {
3672 if ( ubExitValue == MSG_BOX_RETURN_YES )
3673 {
3674 //add the money to the players account
3675 AddTransactionToPlayersBook(MERC_DEPOSITED_MONEY_TO_PLAYER_ACCOUNT, gpSMCurrentMerc->ubProfile,
3676 GetWorldTotalMin(), gpItemPointer->uiMoneyAmount);
3677
3678 EndItemPointer( );
3679 // remove contents of the moving item because object still is money and usable
3680 // you could add endlessly money to your bank account if not reset properly
3681 gMoveingItem = INVENTORY_IN_SLOT{};
3682 SetSkiCursor( CURSOR_NORMAL );
3683 // dirty shopkeeper
3684 gubSkiDirtyLevel = SKI_DIRTY_LEVEL2;
3685
3686
3687 }
3688 }
3689
3690
ReEvaluateDisabledINVPanelButtons(void)3691 void ReEvaluateDisabledINVPanelButtons(void)
3692 {
3693 gfReEvaluateDisabledINVPanelButtons = TRUE;
3694 }
3695
3696
CheckForReEvaluateDisabledINVPanelButtons(void)3697 static void CheckForReEvaluateDisabledINVPanelButtons(void)
3698 {
3699 // OK, if we currently have an item pointer up....
3700 if ( gfReEvaluateDisabledINVPanelButtons )
3701 {
3702 if ( gpItemPointer || gfInItemPickupMenu )
3703 {
3704 EnableSMPanelButtons( FALSE, TRUE );
3705 }
3706 else
3707 {
3708 EnableSMPanelButtons( TRUE, TRUE );
3709 }
3710
3711 gfReEvaluateDisabledINVPanelButtons = FALSE;
3712 }
3713 }
3714
3715
AbandonBoxingCallback(MessageBoxReturnValue const ubExitValue)3716 static void AbandonBoxingCallback(MessageBoxReturnValue const ubExitValue)
3717 {
3718 if ( ubExitValue == MSG_BOX_RETURN_YES )
3719 {
3720 // ok, proceed!
3721 SetBoxingState( NOT_BOXING );
3722 gfEnteringMapScreen = TRUE;
3723 }
3724 // otherwise do nothing
3725 }
3726
3727
GoToMapScreenFromTactical(void)3728 void GoToMapScreenFromTactical(void)
3729 {
3730 if ( gTacticalStatus.bBoxingState != NOT_BOXING )
3731 {
3732 // pop up dialogue asking whether the player wants to abandon the fight
3733 DoMessageBox(MSG_BOX_BASIC_STYLE, g_langRes->Message[STR_ABANDON_FIGHT], GAME_SCREEN,
3734 MSG_BOX_FLAG_YESNO, AbandonBoxingCallback, NULL);
3735 return;
3736 }
3737 // ok, proceed!
3738 gfEnteringMapScreen = TRUE;
3739 }
3740
3741
HandleTacticalEffectsOfEquipmentChange(SOLDIERTYPE * pSoldier,UINT32 uiInvPos,UINT16 usOldItem,UINT16 usNewItem)3742 void HandleTacticalEffectsOfEquipmentChange(SOLDIERTYPE* pSoldier, UINT32 uiInvPos, UINT16 usOldItem, UINT16 usNewItem)
3743 {
3744 // if in attached weapon mode and don't have weapon with GL attached in hand, reset weapon mode
3745 if (pSoldier->bWeaponMode == WM_ATTACHED &&
3746 FindAttachment(&(pSoldier->inv[HANDPOS]), UNDER_GLAUNCHER) == NO_SLOT)
3747 {
3748 pSoldier->bWeaponMode = WM_NORMAL;
3749 pSoldier->bDoBurst = FALSE;
3750 }
3751
3752 // if he is loaded tactically
3753 if ( pSoldier->bInSector )
3754 {
3755 // If this is our main hand
3756 if ( uiInvPos == HANDPOS || uiInvPos == SECONDHANDPOS )
3757 {
3758 // check if we need to change animation!
3759 ReLoadSoldierAnimationDueToHandItemChange( pSoldier, usOldItem, usNewItem );
3760 }
3761
3762 // if this is head gear
3763 if ( uiInvPos == HEAD1POS || uiInvPos == HEAD2POS )
3764 {
3765 // Could be because of GOGGLES change... Re-create light...
3766 DeleteSoldierLight( pSoldier );
3767 PositionSoldierLight( pSoldier );
3768 }
3769 }
3770 else
3771 {
3772 // as a minimum
3773 if ((GCM->getItem(pSoldier->inv[HANDPOS].usItem)->isWeapon()) &&
3774 GCM->getWeapon(pSoldier->inv[HANDPOS].usItem)->ubShotsPerBurst == 0)
3775 {
3776 pSoldier->bDoBurst = FALSE;
3777 pSoldier->bWeaponMode = WM_NORMAL;
3778 }
3779 }
3780 }
3781
3782
LoadInterfacePanelGraphics()3783 void LoadInterfacePanelGraphics()
3784 {
3785 guiCLOSE = AddVideoObjectFromFile(INTERFACEDIR "/p_close.sti");
3786 }
3787
3788
DeleteInterfacePanelGraphics()3789 void DeleteInterfacePanelGraphics()
3790 {
3791 DeleteVideoObject(guiCLOSE);
3792 }
3793