1 #include "MapScreen.h"
2 #include "Animated_ProgressBar.h"
3 #include "Campaign.h"
4 #include "Cheats.h"
5 #include "ContentManager.h"
6 #include "Creature_Spreading.h"
7 #include "Cursor_Control.h"
8 #include "Cursors.h"
9 #include "Dialogue_Control.h"
10 #include "Directories.h"
11 #include "EMail.h"
12 #include "English.h"
13 #include "Event_Pump.h"
14 #include "Explosion_Control.h"
15 #include "Faces.h"
16 #include "Fade_Screen.h"
17 #include "Finances.h"
18 #include "Font.h"
19 #include "Font_Control.h"
20 #include "Game_Clock.h"
21 #include "Game_Init.h"
22 #include "GameInstance.h"
23 #include "GameLoop.h"
24 #include "GamePolicy.h"
25 #include "GameRes.h"
26 #include "GameSettings.h"
27 #include "HelpScreen.h"
28 #include "HImage.h"
29 #include "Interface_Control.h"
30 #include "Interface_Items.h"
31 #include "Interface_Panels.h"
32 #include "Interface_Utils.h"
33 #include "Items.h"
34 #include "JAScreens.h"
35 #include "LaptopSave.h"
36 #include "Line.h"
37 #include "Map_Screen_Helicopter.h"
38 #include "Map_Screen_Interface.h"
39 #include "Map_Screen_Interface_Border.h"
40 #include "Map_Screen_Interface_Bottom.h"
41 #include "Map_Screen_Interface_Map_Inventory.h"
42 #include "Map_Screen_Interface_TownMine_Info.h"
43 #include "Meanwhile.h"
44 #include "Merc_Contract.h"
45 #include "Merc_Hiring.h"
46 #include "Message.h"
47 #include "NewStrings.h"
48 #include "Options_Screen.h"
49 #include "Overhead.h"
50 #include "Player_Command.h"
51 #include "PopUpBox.h"
52 #include "PreBattle_Interface.h"
53 #include "Queen_Command.h"
54 #include "Quests.h"
55 #include "Radar_Screen.h"
56 #include "Render_Dirty.h"
57 #include "RenderWorld.h"
58 #include "SaveLoadScreen.h"
59 #include "Soldier_Macros.h"
60 #include "Squads.h"
61 #include "StrategicMap_Secrets.h"
62 #include "Strategic_Movement_Costs.h"
63 #include "Strategic_Pathing.h"
64 #include "Strategic_Town_Loyalty.h"
65 #include "Strategic_Turns.h"
66 #include "Sys_Globals.h"
67 #include "SysUtil.h"
68 #include "Tactical_Save.h"
69 #include "Text.h"
70 #include "Timer_Control.h"
71 #include "Town_Militia.h"
72 #include "Video.h"
73 #include "VObject.h"
74 #include "VObject_Blitters.h"
75 #include "VSurface.h"
76
77 #include <string_theory/format>
78
79 struct PopUpBox;
80
81
82
83 #define MAX_SORT_METHODS 6
84
85 // Cursors
86 #define SCREEN_CURSOR CURSOR_NORMAL
87
88 // Fonts
89 #define CHAR_FONT BLOCKFONT2 // COMPFONT
90 #define ETA_FONT BLOCKFONT2
91
92 // Colors
93 #define FONT_MAP_DKYELLOW 170
94
95 #define CHAR_TITLE_FONT_COLOR 6
96 #define CHAR_TEXT_FONT_COLOR 5
97
98 #define STARTING_COLOR_NUM 5
99
100 #define MAP_TIME_UNDER_THIS_DISPLAY_AS_HOURS ( 3 * 24 * 60 )
101
102
103 #define DELAY_PER_FLASH_FOR_DEPARTING_PERSONNEL 500
104 #define GLOW_DELAY 70
105 #define ASSIGNMENT_DONE_FLASH_TIME 500
106
107 #define MINS_TO_FLASH_CONTRACT_TIME (4 * 60)
108
109 // Coordinate defines
110
111 #define TOWN_INFO_X (STD_SCREEN_X + 0)
112 #define TOWN_INFO_Y (STD_SCREEN_Y + 1)
113
114 #define PLAYER_INFO_X (STD_SCREEN_X + 0)
115 #define PLAYER_INFO_Y (STD_SCREEN_Y + 107)
116
117 // item description
118 #define MAP_ITEMDESC_START_X PLAYER_INFO_X
119 #define MAP_ITEMDESC_START_Y PLAYER_INFO_Y
120
121 #define INV_REGION_X PLAYER_INFO_X
122 #define INV_REGION_Y PLAYER_INFO_Y
123 #define INV_REGION_WIDTH 261
124 #define INV_REGION_HEIGHT 359-94
125 #define INV_BTN_X PLAYER_INFO_X + 217
126 #define INV_BTN_Y PLAYER_INFO_Y + 210
127
128 #define MAP_BG_WIDTH (640 - 261)
129
130 #define MAP_ARMOR_LABEL_X (STD_SCREEN_X + 208)
131 #define MAP_ARMOR_LABEL_Y (STD_SCREEN_Y + 179)
132 #define MAP_ARMOR_X (STD_SCREEN_X + 209)
133 #define MAP_ARMOR_Y (STD_SCREEN_Y + 188)
134 #define MAP_ARMOR_W 28
135 #define MAP_ARMOR_H 10
136
137 #define MAP_WEIGHT_LABEL_X (STD_SCREEN_X + 173)
138 #define MAP_WEIGHT_LABEL_Y (STD_SCREEN_Y + 256)
139 #define MAP_WEIGHT_X (STD_SCREEN_X + 176)
140 #define MAP_WEIGHT_Y (STD_SCREEN_Y + 265)
141 #define MAP_WEIGHT_W 28
142 #define MAP_WEIGHT_H 10
143
144 #define MAP_CAMO_LABEL_X (STD_SCREEN_X + 178)
145 #define MAP_CAMO_LABEL_Y (STD_SCREEN_Y + 283)
146 #define MAP_CAMO_X (STD_SCREEN_X + 176)
147 #define MAP_CAMO_Y (STD_SCREEN_Y + 292)
148 #define MAP_CAMO_W 28
149 #define MAP_CAMO_H 10
150
151 #define MAP_INV_STATS_TITLE_FONT_COLOR 6
152
153 #define PLAYER_INFO_FACE_START_X (STD_SCREEN_X + 9)
154 #define PLAYER_INFO_FACE_START_Y (STD_SCREEN_Y + 17)
155 #define PLAYER_INFO_FACE_END_X (STD_SCREEN_X + 60)
156 #define PLAYER_INFO_FACE_END_Y (STD_SCREEN_Y + 76)
157
158 #define PLAYER_INFO_HAND_START_X (STD_SCREEN_X + 4)
159 #define PLAYER_INFO_HAND_START_Y (STD_SCREEN_Y + 81)
160 #define PLAYER_INFO_HAND_END_X (STD_SCREEN_X + 62)
161 #define PLAYER_INFO_HAND_END_Y (STD_SCREEN_Y + 103)
162
163 #define INV_BODY_X (UINT16)(STD_SCREEN_X + 71)
164 #define INV_BODY_Y (UINT16)(STD_SCREEN_Y + 116)
165
166 //Text offsets
167 #define Y_OFFSET 2
168
169
170 // char stat positions
171 #define STR_X (STD_SCREEN_X + 112)
172 #define STR_Y (STD_SCREEN_Y + 42)
173 #define DEX_X STR_X
174 #define DEX_Y (STD_SCREEN_Y + 32)
175 #define AGL_X STR_X
176 #define AGL_Y (STD_SCREEN_Y + 22)
177 #define LDR_X STR_X
178 #define LDR_Y (STD_SCREEN_Y + 52)
179 #define WIS_X STR_X
180 #define WIS_Y (STD_SCREEN_Y + 62)
181 #define LVL_X (STD_SCREEN_X + 159)
182 #define LVL_Y AGL_Y
183 #define MRK_X LVL_X
184 #define MRK_Y DEX_Y
185 #define EXP_X LVL_X
186 #define EXP_Y STR_Y
187 #define MEC_X LVL_X
188 #define MEC_Y LDR_Y
189 #define MED_X LVL_X
190 #define MED_Y WIS_Y
191
192 #define STAT_WID 15
193 #define STAT_HEI GetFontHeight(CHAR_FONT)
194
195 #define PIC_NAME_X (STD_SCREEN_X + 8)
196 #define PIC_NAME_Y (STD_SCREEN_Y + 66 + 3)
197 #define PIC_NAME_WID (STD_SCREEN_X + 60 - PIC_NAME_X)
198 #define PIC_NAME_HEI (STD_SCREEN_Y + 75 - PIC_NAME_Y)
199 #define CHAR_NAME_X (STD_SCREEN_X + 14)
200 #define CHAR_NAME_Y (STD_SCREEN_Y + 2 + 3)
201 #define CHAR_NAME_WID (STD_SCREEN_X + 164 - CHAR_NAME_X)
202 #define CHAR_NAME_HEI (STD_SCREEN_Y + 11 - CHAR_NAME_Y)
203 #define CHAR_TIME_REMAINING_X (STD_SCREEN_X + 207)
204 #define CHAR_TIME_REMAINING_Y (STD_SCREEN_Y + 65)
205 #define CHAR_TIME_REMAINING_WID (STD_SCREEN_X + 258 - CHAR_TIME_REMAINING_X)
206 #define CHAR_TIME_REMAINING_HEI GetFontHeight(CHAR_FONT)
207 #define CHAR_SALARY_X CHAR_TIME_REMAINING_X
208 #define CHAR_SALARY_Y (STD_SCREEN_Y + 79)
209 #define CHAR_SALARY_WID CHAR_TIME_REMAINING_WID - 8 // for right justify
210 #define CHAR_SALARY_HEI CHAR_TIME_REMAINING_HEI
211 #define CHAR_MEDICAL_X CHAR_TIME_REMAINING_X
212 #define CHAR_MEDICAL_Y (STD_SCREEN_Y + 93)
213 #define CHAR_MEDICAL_WID CHAR_TIME_REMAINING_WID - 8 // for right justify
214 #define CHAR_MEDICAL_HEI CHAR_TIME_REMAINING_HEI
215 #define CHAR_ASSIGN_X (STD_SCREEN_X + 182)
216 #define CHAR_ASSIGN1_Y (STD_SCREEN_Y + 18)
217 #define CHAR_ASSIGN2_Y (STD_SCREEN_Y + 31)
218 #define CHAR_ASSIGN_WID 257 - 178
219 #define CHAR_ASSIGN_HEI 39 - 29
220 #define CHAR_HP_X (STD_SCREEN_X + 133)
221 #define CHAR_HP_Y (STD_SCREEN_Y + 77 + 3)
222 #define CHAR_HP_WID (STD_SCREEN_X + 175 - CHAR_HP_X)
223 #define CHAR_HP_HEI (STD_SCREEN_Y + 90 - CHAR_HP_Y)
224 #define CHAR_MORALE_X (STD_SCREEN_X + 133)
225 #define CHAR_MORALE_Y (STD_SCREEN_Y + 91 + 3)
226 #define CHAR_MORALE_WID (STD_SCREEN_X + 175 - CHAR_MORALE_X)
227 #define CHAR_MORALE_HEI (STD_SCREEN_Y + 101 - CHAR_MORALE_Y)
228
229 #define SOLDIER_PIC_X (STD_SCREEN_X + 9)
230 #define SOLDIER_PIC_Y (STD_SCREEN_Y + 20)
231 #define SOLDIER_HAND_X (STD_SCREEN_X + 6)
232 #define SOLDIER_HAND_Y (STD_SCREEN_Y + 81)
233
234 #define CLOCK_X (STD_SCREEN_X + 554)
235 #define CLOCK_Y (STD_SCREEN_Y + 459)
236
237
238 #define RGB_WHITE ( FROMRGB( 255, 255, 255 ) )
239 #define RGB_YELLOW ( FROMRGB( 255, 255, 0 ) )
240 #define RGB_NEAR_BLACK ( FROMRGB( 0, 0, 1 ) )
241
242
243 // ARM: NOTE that these map "events" are never actually saved in a player's game in any way
244 enum MapEvent
245 {
246 MAP_EVENT_NONE,
247 MAP_EVENT_CLICK_SECTOR,
248 MAP_EVENT_PLOT_PATH,
249 MAP_EVENT_CANCEL_PATH,
250 };
251
252
GlowColor(UINT i)253 static inline UINT16 GlowColor(UINT i)
254 {
255 Assert(i <= 10);
256 return Get16BPPColor(FROMRGB(25 * i, 0, 0));
257 }
258
259
260 static const SGPPoint gMapSortButtons[MAX_SORT_METHODS] =
261 {
262 { 12, 125 },
263 { 68, 125 },
264 { 124, 125 },
265 { 148, 125 },
266 { 185, 125 },
267 { 223, 125 }
268 };
269
270
271 // GLOBAL VARIABLES (OURS)
272
273
274 static BOOLEAN fFlashAssignDone = FALSE;
275 BOOLEAN fInMapMode = FALSE;
276 BOOLEAN fMapPanelDirty=TRUE;
277 BOOLEAN fTeamPanelDirty = TRUE;
278 BOOLEAN fCharacterInfoPanelDirty = TRUE;
279 BOOLEAN fReDrawFace=FALSE;
280 BOOLEAN fShowInventoryFlag = FALSE;
281 BOOLEAN fMapInventoryItem=FALSE;
282 BOOLEAN fShowDescriptionFlag=FALSE;
283
284 static BOOLEAN gfHotKeyEnterSector = FALSE;
285 static BOOLEAN fOneFrame = FALSE;
286 static BOOLEAN fShowItemHighLight = FALSE;
287 static BOOLEAN fJustFinishedPlotting = FALSE;
288
289 // for the flashing of the contract departure time...for when mercs are leaving in an hour or less
290 static BOOLEAN fFlashContractFlag = FALSE;
291
292 static BOOLEAN fShowTrashCanHighLight = FALSE;
293
294 // the flags for display of pop up boxes/menus
295 static BOOLEAN fEndPlotting = FALSE;
296
297 BOOLEAN gfInConfirmMapMoveMode = FALSE;
298 BOOLEAN gfInChangeArrivalSectorMode = FALSE;
299
300 // redraw character list
301 BOOLEAN fDrawCharacterList = TRUE;
302
303 // was the cursor set to the checkmark?
304 static BOOLEAN fCheckCursorWasSet = FALSE;
305
306 static BOOLEAN fEndShowInventoryFlag = FALSE;
307
308 // draw the temp path
309 static BOOLEAN fDrawTempPath = TRUE;
310
311 static BOOLEAN gfGlowTimerExpired = FALSE;
312
313 BOOLEAN gfSkyriderEmptyHelpGiven = FALSE;
314
315 static BOOLEAN gfRequestGiveSkyriderNewDestination = FALSE;
316
317 static BOOLEAN gfFirstMapscreenFrame = FALSE;
318
319 static BOOLEAN gfMapPanelWasRedrawn = FALSE;
320
321
322 // currently selected character's list index
323 INT8 bSelectedInfoChar = -1;
324
325 static GUIButtonRef giMapSortButton[MAX_SORT_METHODS];
326
327 GUIButtonRef giCharInfoButton[2];
328
329 GUIButtonRef giMapInvDoneButton;
330
331 GUIButtonRef giMapContractButton;
332
333 INT32 giSortStateForMapScreenList = 0;
334
335 INT32 giCommonGlowBaseTime = 0;
336 INT32 giFlashAssignBaseTime = 0;
337 INT32 giFlashContractBaseTime = 0;
338 UINT32 guiFlashCursorBaseTime = 0;
339 INT32 giPotCharPathBaseTime = 0;
340
341 static SGPVObject* guiCHARLIST;
342 static SGPVObject* guiCHARINFO;
343 static SGPVObject* guiSleepIcon;
344 static SGPVObject* guiMAPINV;
345 static SGPVObject* guiULICONS;
346 static SGPVObject* guiNewMailIcons;
347
348
349 // misc mouse regions
350 static MOUSE_REGION gCharInfoFaceRegion;
351 static MOUSE_REGION gCharInfoHandRegion;
352 static MOUSE_REGION gMPanelRegion;
353 static MOUSE_REGION gMapViewRegion;
354 static MOUSE_REGION gMapScreenMaskRegion;
355 static MOUSE_REGION gTrashCanRegion;
356
357 // mouse regions for team info panel
358 struct CharacterRegions
359 {
360 MOUSE_REGION name;
361 MOUSE_REGION assignment;
362 MOUSE_REGION sleep;
363 MOUSE_REGION location;
364 MOUSE_REGION destination;
365 MOUSE_REGION contract;
366 };
367
368 static CharacterRegions g_character_regions[MAX_CHARACTER_COUNT];
369
370
371 static PathSt* g_prev_path;
372
373
374 static bool fLockOutMapScreenInterface = false;
375
376
377 // GLOBAL VARIABLES (EXTERNAL)
378
379
380 extern BOOLEAN fDeletedNode;
381 extern BOOLEAN gfStartedFromMapScreen;
382
383
384 extern PathSt* pTempCharacterPath;
385 extern PathSt* pTempHelicopterPath;
386
387 extern BOOLEAN gfAutoAIAware;
388
389 extern OBJECTTYPE *gpItemDescObject;
390
391
392 // externs for highlighting of ammo/weapons
393 extern UINT32 guiMouseOverItemTime;
394 extern BOOLEAN gfCheckForMouseOverItem;
395 extern INT8 gbCheckForMouseOverItemPos;
396
397 //Autoresolve sets this variable which defaults to -1 when not needed.
398 extern INT16 gsEnemyGainedControlOfSectorID;
399 extern INT16 gsCiviliansEatenByMonsters;
400
401 extern BOOLEAN gfFadeOutDone;
402
403 extern BOOLEAN gfMilitiaPopupCreated;
404
405 void CancelMapUIMessage( void );
406
407
408
409 // the tries to select a mapscreen character by his soldier ID
SetInfoChar(SOLDIERTYPE const * const s)410 void SetInfoChar(SOLDIERTYPE const* const s)
411 {
412 for (INT8 i = 0; i != MAX_CHARACTER_COUNT; ++i)
413 {
414 if (gCharactersList[i].merc != s) continue;
415 ChangeSelectedInfoChar(i, TRUE);
416 break;
417 }
418 }
419
ContractListRegionBoxGlow(UINT16 usCount)420 static void ContractListRegionBoxGlow(UINT16 usCount)
421 {
422 static INT32 iColorNum =10;
423 static BOOLEAN fDelta=FALSE;
424 INT16 usY = 0;
425 INT16 sYAdd = 0;
426
427
428 // if not glowing right now, leave
429 if (fShowInventoryFlag)
430 {
431 iColorNum = 0;
432 fDelta = TRUE;
433 return;
434 }
435
436 // if not ready to change glow phase yet, leave
437 if ( !gfGlowTimerExpired )
438 return;
439
440
441 // change direction of glow?
442 if((iColorNum==0)||(iColorNum==10))
443 {
444 fDelta=!fDelta;
445 }
446
447 // increment color
448 if(!fDelta)
449 iColorNum++;
450 else
451 iColorNum--;
452
453
454 if( usCount >= FIRST_VEHICLE )
455 {
456 sYAdd = 6;
457 }
458 else
459 {
460 sYAdd = 0;
461 }
462
463 // y start position of box
464 usY=(Y_OFFSET*usCount-1)+(Y_START+(usCount*Y_SIZE) + sYAdd );
465
466 // glow contract box
467 UINT16 usColor = GlowColor(iColorNum);
468 SGPVSurface::Lock l(FRAME_BUFFER);
469 SetClippingRegionAndImageWidth(l.Pitch(), 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
470 RectangleDraw(TRUE, TIME_REMAINING_X, usY, TIME_REMAINING_X + TIME_REMAINING_WIDTH, usY + GetFontHeight(MAP_SCREEN_FONT) + 2, usColor, l.Buffer<UINT16>());
471 InvalidateRegion(TIME_REMAINING_X - 1, usY, TIME_REMAINING_X + TIME_REMAINING_WIDTH + 1, usY + GetFontHeight( MAP_SCREEN_FONT ) + 3 );
472 }
473
474
475 static void RenderHandPosItem();
476
477
GlowItem(void)478 static void GlowItem(void)
479 {
480 static INT32 iColorNum =10;
481 static BOOLEAN fDelta=FALSE;
482 static BOOLEAN fOldItemGlow = FALSE;
483
484 // not glowing right now, leave
485 if (!fShowItemHighLight)
486 {
487 iColorNum =0;
488 fDelta = TRUE;
489
490 if (fOldItemGlow)
491 {
492 RestoreExternBackgroundRect( STD_SCREEN_X + 3, STD_SCREEN_Y + 80, ( UINT16 )( 65 - 3 ), ( UINT16 )( 105 - 80 ) );
493 }
494
495 fOldItemGlow = FALSE;
496 return;
497 }
498
499 // if not ready to change glow phase yet, leave
500 if ( !gfGlowTimerExpired )
501 return;
502
503
504 fOldItemGlow = TRUE;
505
506 // change direction of glow?
507 if((iColorNum==0)||(iColorNum==10))
508 {
509 fDelta=!fDelta;
510 }
511
512 // increment color
513 if(!fDelta)
514 iColorNum++;
515 else
516 iColorNum--;
517
518 // restore background
519 if((iColorNum==0)||(iColorNum==1))
520 {
521 RestoreExternBackgroundRect( STD_SCREEN_X + 3, STD_SCREEN_Y + 80, ( UINT16 )( 65 - 3 ), ( UINT16 )( 105 - 80 ) );
522 RenderHandPosItem();
523 }
524
525 // glow contract box
526 UINT16 usColor = GlowColor(iColorNum);
527 SGPVSurface::Lock l(FRAME_BUFFER);
528 SetClippingRegionAndImageWidth(l.Pitch(), 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
529 RectangleDraw(TRUE, STD_SCREEN_X + 3, STD_SCREEN_Y + 80, STD_SCREEN_X + 64, STD_SCREEN_Y + 104, usColor, l.Buffer<UINT16>());
530 InvalidateRegion( STD_SCREEN_X + 3, STD_SCREEN_Y + 80, STD_SCREEN_X + 65, STD_SCREEN_Y + 105 );
531 }
532
533
GlowTrashCan(void)534 static void GlowTrashCan(void)
535 {
536 static INT32 iColorNum =10;
537 static BOOLEAN fOldTrashCanGlow = FALSE;
538
539 if (!fShowInventoryFlag) fShowTrashCanHighLight = FALSE;
540
541 // not glowing right now, leave
542 if (!fShowTrashCanHighLight)
543 {
544 iColorNum =0;
545
546 if (fOldTrashCanGlow)
547 {
548 RestoreExternBackgroundRect( TRASH_CAN_X, TRASH_CAN_Y, ( UINT16 )( TRASH_CAN_WIDTH + 2 ), ( UINT16 )( TRASH_CAN_HEIGHT + 2 ) );
549 }
550
551 fOldTrashCanGlow = FALSE;
552 return;
553 }
554
555 // if not ready to change glow phase yet, leave
556 if ( !gfGlowTimerExpired )
557 return;
558
559
560 fOldTrashCanGlow = TRUE;
561
562 // glow contract box
563 UINT16 usColor = GlowColor(iColorNum);
564 { SGPVSurface::Lock l(FRAME_BUFFER);
565 SetClippingRegionAndImageWidth(l.Pitch(), 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
566 RectangleDraw(TRUE, TRASH_CAN_X, TRASH_CAN_Y, TRASH_CAN_X + TRASH_CAN_WIDTH, TRASH_CAN_Y + TRASH_CAN_HEIGHT, usColor, l.Buffer<UINT16>());
567 InvalidateRegion( TRASH_CAN_X, TRASH_CAN_Y, TRASH_CAN_X + TRASH_CAN_WIDTH + 1, TRASH_CAN_Y + TRASH_CAN_HEIGHT + 1 );
568 }
569
570 // restore background
571 if((iColorNum==0)||(iColorNum==1))
572 RestoreExternBackgroundRect( TRASH_CAN_X, TRASH_CAN_Y, ( UINT16 )( TRASH_CAN_WIDTH + 2 ), ( UINT16 )( TRASH_CAN_HEIGHT + 2 ) );
573 }
574
575
DrawFace(void)576 void DrawFace(void)
577 {
578 static const SOLDIERTYPE* old_merc = NULL;
579
580 // draws the face of the currently selected merc, being displayed int he upper left hand corner
581
582 const SOLDIERTYPE* const pSoldier = GetSelectedInfoChar();
583 if( pSoldier == NULL )
584 {
585 return;
586 }
587
588 if (pSoldier == old_merc && !fReDrawFace)
589 {
590 // are the same, return
591 return;
592 }
593
594 // get old id value
595 old_merc = pSoldier;
596
597 // reset redraw of face
598 fReDrawFace = FALSE;
599
600 // render their face
601 RenderSoldierFace(*pSoldier, SOLDIER_PIC_X, SOLDIER_PIC_Y);
602 }
603
604
605 // Render the inventory item in char's main hand
RenderHandPosItem()606 static void RenderHandPosItem()
607 {
608 /* ARM: if already in the inventory panel, don't show the item again here,
609 * seeing it twice is confusing */
610 if (fShowInventoryFlag) return;
611
612 SOLDIERTYPE const* const s = GetSelectedInfoChar();
613 if (!s || s->bLife == 0) return;
614
615 INVRenderItem(guiSAVEBUFFER, s, s->inv[HANDPOS], SOLDIER_HAND_X, SOLDIER_HAND_Y, 58, 23, DIRTYLEVEL2, 0, SGP_TRANSPARENT);
616 }
617
618
RenderIconsForUpperLeftCornerPiece(const SOLDIERTYPE * const s)619 static void RenderIconsForUpperLeftCornerPiece(const SOLDIERTYPE* const s)
620 {
621 // if merc is an AIM merc
622 if (s->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC)
623 {
624 // finite contract length icon
625 BltVideoObject(guiSAVEBUFFER, guiULICONS, 0, CHAR_ICON_X, CHAR_ICON_CONTRACT_Y);
626 }
627
628 // if merc has life insurance
629 if (s->usLifeInsurance > 0)
630 {
631 // draw life insurance icon
632 BltVideoObject(guiSAVEBUFFER, guiULICONS, 2, CHAR_ICON_X, CHAR_ICON_CONTRACT_Y + CHAR_ICON_SPACING);
633 }
634
635 // if merc has a medical deposit
636 if (s->usMedicalDeposit > 0)
637 {
638 // draw medical deposit icon
639 BltVideoObject(guiSAVEBUFFER, guiULICONS, 1, CHAR_ICON_X, CHAR_ICON_CONTRACT_Y + 2 * CHAR_ICON_SPACING);
640 }
641 }
642
PrintStat(UINT32 change_time,UINT16 const stat_gone_up_bit,INT8 stat_val,INT16 x,INT16 y,INT16 w,INT32 progress)643 static void PrintStat(UINT32 change_time, UINT16 const stat_gone_up_bit, INT8 stat_val, INT16 x, INT16 y, INT16 w, INT32 progress)
644 {
645 UINT8 const colour =
646 change_time == 0 ||
647 GetJA2Clock() >= CHANGE_STAT_RECENTLY_DURATION + change_time ? CHAR_TEXT_FONT_COLOR :
648 stat_gone_up_bit != 0 ? FONT_LTGREEN :
649 FONT_RED;
650
651 SetFontForeground(colour);
652
653 ST::string str = ST::format("{3d}", stat_val);
654 if (gamepolicy(gui_extras))
655 {
656 ProgressBarBackgroundRect(x + 1, y - 2, w * progress / 100, 10, 0x514A05, progress);
657 }
658
659 DrawStringRight(str, x, y, STAT_WID, STAT_HEI, CHAR_FONT);
660 }
661
662 // Draw attributes & skills for given soldier
DrawCharStats(SOLDIERTYPE const & s)663 static void DrawCharStats(SOLDIERTYPE const& s)
664 {
665 SetFontAttributes(CHAR_FONT, CHAR_TEXT_FONT_COLOR);
666
667 UINT16 const up = s.usValueGoneUp;
668 MERCPROFILESTRUCT& p = GetProfile(s.ubProfile);
669
670 PrintStat(s.uiChangeLevelTime, up & LVL_INCREASE, s.bExpLevel, LVL_X, LVL_Y, STAT_WID, (100 * p.sExpLevelGain) / (350 * p.bExpLevel));
671 PrintStat(s.uiChangeAgilityTime, up & AGIL_INCREASE, s.bAgility, AGL_X, AGL_Y, STAT_WID, 2 * p.sAgilityGain);
672 PrintStat(s.uiChangeDexterityTime, up & DEX_INCREASE, s.bDexterity, DEX_X, DEX_Y, STAT_WID, 2 * p.sDexterityGain);
673 PrintStat(s.uiChangeStrengthTime, up & STRENGTH_INCREASE, s.bStrength, STR_X, STR_Y, STAT_WID, 2 * p.sStrengthGain);
674 PrintStat(s.uiChangeLeadershipTime, up & LDR_INCREASE, s.bLeadership, LDR_X, LDR_Y, STAT_WID, 2 * p.sLeadershipGain);
675 PrintStat(s.uiChangeWisdomTime, up & WIS_INCREASE, s.bWisdom, WIS_X, WIS_Y, STAT_WID, 2 * p.sWisdomGain);
676 PrintStat(s.uiChangeMarksmanshipTime, up & MRK_INCREASE, s.bMarksmanship, MRK_X, MRK_Y, STAT_WID, 4 * p.sMarksmanshipGain);
677 PrintStat(s.uiChangeExplosivesTime, up & EXP_INCREASE, s.bExplosive, EXP_X, EXP_Y, STAT_WID, 4 * p.sExplosivesGain);
678 PrintStat(s.uiChangeMechanicalTime, up & MECH_INCREASE, s.bMechanical, MEC_X, MEC_Y, STAT_WID, 4 * p.sMechanicGain);
679 PrintStat(s.uiChangeMedicalTime, up & MED_INCREASE, s.bMedical, MED_X, MED_Y, STAT_WID, 4 * p.sMedicalGain);
680 }
681
682 static void DrawString(const ST::string& str, UINT16 uiX, UINT16 uiY, SGPFont);
683 static void DrawStringCentered(const ST::string& str, UINT16 x, UINT16 y, UINT16 w, UINT16 h, SGPFont);
684
DrawCharHealth(SOLDIERTYPE const & s)685 static void DrawCharHealth(SOLDIERTYPE const& s)
686 {
687 if (s.bAssignment != ASSIGNMENT_POW)
688 {
689 INT8 const life = s.bLife;
690 INT8 const life_max = s.bLifeMax;
691
692 /* Find starting X coordinate by centering all 3 substrings together, then
693 * print them separately (different colors) */
694 ST::string buf = ST::format("{}/{}", life, life_max);
695 INT16 x;
696 INT16 y;
697 FindFontCenterCoordinates(CHAR_HP_X, CHAR_HP_Y, CHAR_HP_WID, CHAR_HP_HEI, buf, CHAR_FONT, &x, &y);
698
699 // How is character's life?
700 UINT32 const health_percent = life_max > 0 ? 100 * life / life_max : 0;
701 UINT8 const cur_colour =
702 health_percent == 0 ? FONT_METALGRAY : // Dead
703 health_percent < 25 ? FONT_RED : // Very bad
704 health_percent < 50 ? FONT_YELLOW : // Not good
705 CHAR_TEXT_FONT_COLOR; // Ok
706 SetFontForeground(cur_colour);
707
708 if (gamepolicy(gui_extras))
709 {
710 INT32 progress = 2 * GetProfile(s.ubProfile).sLifeGain;
711 ProgressBarBackgroundRect(CHAR_HP_X + 1, CHAR_HP_Y - 2, CHAR_HP_WID * progress / 100, 10, 0x514A05, progress);
712 }
713
714 // Current life
715 buf = ST::format("{}", life);
716 DrawString(buf, x, CHAR_HP_Y, CHAR_FONT);
717 x += StringPixLength(buf, CHAR_FONT);
718
719 // Slash
720 SetFontForeground(CHAR_TEXT_FONT_COLOR);
721 ST::string slash = "/";
722 DrawString(slash, x, CHAR_HP_Y, CHAR_FONT);
723 x += StringPixLength(slash, CHAR_FONT);
724
725 bool const recent_change =
726 GetJA2Clock() < CHANGE_STAT_RECENTLY_DURATION + s.uiChangeHealthTime &&
727 s.uiChangeHealthTime != 0;
728 UINT8 const max_colour =
729 !recent_change ? CHAR_TEXT_FONT_COLOR :
730 s.usValueGoneUp & HEALTH_INCREASE ? FONT_LTGREEN :
731 FONT_RED;
732 SetFontForeground(max_colour);
733
734 // Maximum life
735 buf = ST::format("{}", life_max);
736 DrawString(buf, x, CHAR_HP_Y, CHAR_FONT);
737 }
738 else
739 { // POW - health unknown
740 SetFontForeground(CHAR_TEXT_FONT_COLOR);
741 DrawStringCentered(pPOWStrings[1], CHAR_HP_X, CHAR_HP_Y, CHAR_HP_WID, CHAR_HP_HEI, CHAR_FONT);
742 }
743 }
744
745
746 static ST::string ConvertMinTimeToETADayHourMinString(UINT32 uiTimeInMin);
747
748
749 // "character" refers to hired people AND vehicles
DrawCharacterInfo(SOLDIERTYPE const & s)750 static void DrawCharacterInfo(SOLDIERTYPE const& s)
751 {
752 ST::string buf;
753
754 ProfileID const pid = s.ubProfile;
755 if (pid == NO_PROFILE) return;
756 MERCPROFILESTRUCT const& p = GetProfile(pid);
757
758 // Draw particular info about a character that are neither attributes nor skills
759 SetFontAttributes(CHAR_FONT, CHAR_TEXT_FONT_COLOR);
760
761 ST::string nickname; // Nickname (beneath picture)
762 ST::string name; // Full name (top box)
763 if (s.uiStatusFlags & SOLDIER_VEHICLE)
764 {
765 VEHICLETYPE const& v = GetVehicle(s.bVehicleID);
766 nickname = pShortVehicleStrings[v.ubVehicleType];
767 name = pVehicleStrings[ v.ubVehicleType];
768 }
769 else
770 {
771 nickname = p.zNickname;
772 name = p.zName;
773 }
774 DrawStringCentered(nickname, PIC_NAME_X, PIC_NAME_Y, PIC_NAME_WID, PIC_NAME_HEI, CHAR_FONT);
775 DrawStringCentered(name, CHAR_NAME_X, CHAR_NAME_Y, CHAR_NAME_WID, CHAR_NAME_HEI, CHAR_FONT);
776
777 ST::string assignment =
778 s.bAssignment == VEHICLE ? pShortVehicleStrings[GetVehicle(s.iVehicleId).ubVehicleType] : // Show vehicle type
779 pAssignmentStrings[s.bAssignment];
780 DrawStringCentered(assignment, CHAR_ASSIGN_X, CHAR_ASSIGN1_Y, CHAR_ASSIGN_WID, CHAR_ASSIGN_HEI, CHAR_FONT);
781
782 // Second assignment line
783 ST::string assignment2;
784 switch (s.bAssignment)
785 {
786 case TRAIN_SELF:
787 case TRAIN_TEAMMATE:
788 case TRAIN_BY_OTHER:
789 assignment2 = pAttributeMenuStrings[s.bTrainStat];
790 break;
791
792 case TRAIN_TOWN:
793 assignment2 = GCM->getTownName(GetTownIdForSector(SECTOR(s.sSectorX, s.sSectorY)));
794 break;
795
796 case REPAIR:
797 assignment2 =
798 s.fFixingRobot ? pRepairStrings[3] : // Robot
799 s.bVehicleUnderRepairID != -1 ? pShortVehicleStrings[GetVehicle(s.bVehicleUnderRepairID).ubVehicleType] : // Vehicle
800 pRepairStrings[0]; // Items
801 break;
802
803 case IN_TRANSIT:
804 // Show ETA
805 buf = ConvertMinTimeToETADayHourMinString(s.uiTimeSoldierWillArrive);
806 assignment2 = buf;
807 break;
808
809 default:
810 {
811 GROUP const* const g = GetSoldierGroup(s);
812 if (g && PlayerGroupInMotion(g))
813 { // Show ETA
814 UINT32 const arrival_time = GetWorldTotalMin() + CalculateTravelTimeOfGroup(g);
815 buf = ConvertMinTimeToETADayHourMinString(arrival_time);
816 }
817 else
818 { // Show location
819 buf = GetMapscreenMercLocationString(s);
820 }
821 assignment2 = buf;
822 break;
823 }
824 }
825 DrawStringCentered(assignment2, CHAR_ASSIGN_X, CHAR_ASSIGN2_Y, CHAR_ASSIGN_WID, CHAR_ASSIGN_HEI, CHAR_FONT);
826
827 DrawCharHealth(s);
828
829 // If a vehicle or robot, we're done - the remainder applies only to people
830 if (IsMechanical(s)) return;
831
832 DrawCharStats(s);
833
834 // Remaining contract length
835 ST::string contract = gpStrategicString[STR_PB_NOTAPPLICABLE_ABBREVIATION];
836 if (s.bLife > 0)
837 {
838 if (s.ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC || s.ubProfile == SLAY)
839 {
840 // Amount of time left on contract
841 INT32 time_remaining = s.iEndofContractTime - GetWorldTotalMin();
842
843 if (s.bAssignment == IN_TRANSIT &&
844 time_remaining > (INT32)(s.iTotalContractLength * NUM_MIN_IN_DAY))
845 { /* If the merc is in transit and if the time left on the contract is
846 * greater than the contract time */
847 time_remaining = s.iTotalContractLength * NUM_MIN_IN_DAY;
848 }
849
850 if (time_remaining >= 24 * 60)
851 { // More than a day, display in green
852 // Calculate the exact time left on the contract (e.g. 1.8 days)
853 float const time_left = time_remaining / (60 * 24.0);
854 ST::string days = gpStrategicString[STR_PB_DAYS_ABBREVIATION];
855 buf = ST::format("{.1f}{}/{}{}", time_left, days, s.iTotalContractLength, days);
856 }
857 else
858 { // Less than a day, display hours left in red
859 if (time_remaining > 5) time_remaining += 59;
860 time_remaining /= 60;
861 ST::string hours = gpStrategicString[STR_PB_HOURS_ABBREVIATION];
862 ST::string days = gpStrategicString[STR_PB_DAYS_ABBREVIATION];
863 buf = ST::format("{}{}/{}{}", time_remaining, hours, s.iTotalContractLength, days);
864 }
865 contract = buf;
866 }
867 else if (s.ubWhatKindOfMercAmI == MERC_TYPE__MERC)
868 {
869 INT32 const been_hired_for = GetWorldTotalMin() / NUM_MIN_IN_DAY - s.iStartContractTime;
870 ST::string days = gpStrategicString[STR_PB_DAYS_ABBREVIATION];
871 buf = ST::format("{}{}/{}{}", p.iMercMercContractLength, days, been_hired_for, days);
872 contract = buf;
873 }
874 }
875 SetFontForeground(CHAR_TEXT_FONT_COLOR);
876 DrawStringCentered(contract, CHAR_TIME_REMAINING_X, CHAR_TIME_REMAINING_Y, CHAR_TIME_REMAINING_WID, CHAR_TIME_REMAINING_HEI, CHAR_FONT);
877
878 // Salary
879 INT32 daily_cost;
880 if (s.ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC)
881 { // Daily rate
882 switch (s.bTypeOfLastContract)
883 {
884 case CONTRACT_EXTEND_2_WEEK: daily_cost = p.uiBiWeeklySalary / 14; break;
885 case CONTRACT_EXTEND_1_WEEK: daily_cost = p.uiWeeklySalary / 7; break;
886 default: daily_cost = p.sSalary; break;
887 }
888 }
889 else
890 {
891 daily_cost = p.sSalary;
892 }
893 buf = SPrintMoney(daily_cost);
894 DrawStringRight(buf, CHAR_SALARY_X, CHAR_SALARY_Y, CHAR_SALARY_WID, CHAR_SALARY_HEI, CHAR_FONT);
895
896 // Medical deposit
897 if (p.sMedicalDepositAmount > 0)
898 {
899 buf = SPrintMoney(p.sMedicalDepositAmount);
900 DrawStringRight(buf, CHAR_MEDICAL_X, CHAR_MEDICAL_Y, CHAR_MEDICAL_WID, CHAR_MEDICAL_HEI, CHAR_FONT);
901 }
902
903 ST::string morale =
904 s.bAssignment == ASSIGNMENT_POW ? pPOWStrings[1] : // POW - morale unknown
905 s.bLife == 0 ? ST::null :
906 GetMoraleString(s);
907 DrawStringCentered(morale, CHAR_MORALE_X, CHAR_MORALE_Y, CHAR_MORALE_WID, CHAR_MORALE_HEI, CHAR_FONT);
908 }
909
910
911 // this character is in transit has an item picked up
CharacterIsInTransitAndHasItemPickedUp(SOLDIERTYPE const * const s)912 static bool CharacterIsInTransitAndHasItemPickedUp(SOLDIERTYPE const* const s)
913 {
914 return s->bAssignment == IN_TRANSIT && fMapInventoryItem;
915 }
916
917
DisplayCharacterInfo(void)918 static void DisplayCharacterInfo(void)
919 {
920 const SOLDIERTYPE* const s = GetSelectedInfoChar();
921 if (s == NULL) return;
922
923 SetFontDestBuffer(guiSAVEBUFFER);
924
925 // draw character info and face
926 DrawCharacterInfo(*s);
927
928 RenderHandPosItem();
929
930 SetFontDestBuffer(FRAME_BUFFER);
931
932 RenderIconsForUpperLeftCornerPiece(s);
933
934 // mark all pop ups as dirty
935 MarkAllBoxesAsAltered();
936 }
937
938
939
GetPathTravelTimeDuringPlotting(PathSt * pPath)940 INT32 GetPathTravelTimeDuringPlotting(PathSt* pPath)
941 {
942 INT32 iTravelTime = 0;
943 WAYPOINT pCurrent;
944 WAYPOINT pNext;
945 GROUP *pGroup;
946 UINT8 ubGroupId = 0;
947 BOOLEAN fSkipFirstNode = FALSE;
948
949
950 if (bSelectedDestChar == -1 && !fPlotForHelicopter)
951 {
952 return( 0 );
953 }
954
955 if (!fTempPathAlreadyDrawn)
956 {
957 return( 0 );
958 }
959
960 if (pPath == NULL) return 0;
961 Assert(pPath->pPrev == NULL);
962
963 if (!fPlotForHelicopter)
964 {
965 // plotting for a character...
966 SOLDIERTYPE* const s = gCharactersList[bSelectedDestChar].merc;
967 if (s->bAssignment == VEHICLE)
968 {
969 ubGroupId = pVehicleList[s->iVehicleId].ubMovementGroup;
970 pGroup = GetGroup( ubGroupId );
971
972 if( pGroup == NULL )
973 {
974 SetUpMvtGroupForVehicle(s);
975
976 // get vehicle id
977 ubGroupId = pVehicleList[s->iVehicleId].ubMovementGroup;
978 pGroup = GetGroup( ubGroupId );
979 }
980 }
981 else if (s->uiStatusFlags & SOLDIER_VEHICLE)
982 {
983 ubGroupId = pVehicleList[s->bVehicleID].ubMovementGroup;
984 pGroup = GetGroup( ubGroupId );
985
986 if( pGroup == NULL )
987 {
988 SetUpMvtGroupForVehicle(s);
989
990 // get vehicle id
991 ubGroupId = pVehicleList[s->bVehicleID].ubMovementGroup;
992 pGroup = GetGroup( ubGroupId );
993 }
994 }
995 else
996 {
997 ubGroupId = s->ubGroupID;
998 pGroup = GetGroup( ( UINT8 )( ubGroupId ) );
999 }
1000 }
1001 else
1002 {
1003 ubGroupId = GetHelicopter().ubMovementGroup;
1004 pGroup = GetGroup( ubGroupId );
1005 }
1006
1007 Assert(pGroup);
1008
1009
1010 // if between sectors
1011 if ( pGroup->fBetweenSectors )
1012 {
1013 // arrival time should always be legal!
1014 Assert( pGroup->uiArrivalTime >= GetWorldTotalMin( ) );
1015
1016 // start with time to finish arriving in any traversal already in progress
1017 iTravelTime = pGroup->uiArrivalTime - GetWorldTotalMin();
1018 fSkipFirstNode = TRUE;
1019 }
1020 else
1021 {
1022 iTravelTime = 0;
1023 }
1024
1025 while( pPath->pNext )
1026 {
1027 if ( !fSkipFirstNode )
1028 {
1029 // grab the current location
1030 pCurrent.x = ( UINT8 )( pPath->uiSectorId % MAP_WORLD_X );
1031 pCurrent.y = ( UINT8 )( pPath->uiSectorId / MAP_WORLD_X );
1032
1033 // grab the next location
1034 pNext.x = ( UINT8 )( pPath->pNext->uiSectorId % MAP_WORLD_X );
1035 pNext.y = ( UINT8 )( pPath->pNext->uiSectorId / MAP_WORLD_X );
1036
1037 iTravelTime += FindTravelTimeBetweenWaypoints( &pCurrent, &pNext, pGroup );
1038 }
1039 else
1040 {
1041 fSkipFirstNode = FALSE;
1042 }
1043
1044 pPath = pPath->pNext;
1045 }
1046
1047 return( iTravelTime );
1048 }
1049
1050
1051 static INT32 GetGroundTravelTimeOfSoldier(const SOLDIERTYPE* s);
1052
1053
DisplayGroundEta(void)1054 static void DisplayGroundEta(void)
1055 {
1056 if (fPlotForHelicopter) return;
1057
1058 if( bSelectedDestChar == -1 )
1059 {
1060 return;
1061 }
1062
1063 const SOLDIERTYPE* const s = gCharactersList[bSelectedDestChar].merc;
1064 if (s == NULL) return;
1065
1066 const UINT32 iTotalTime = GetGroundTravelTimeOfSoldier(s);
1067
1068 // now display it
1069 SetFontAttributes(ETA_FONT, FONT_LTGREEN);
1070 MPrint(CLOCK_ETA_X, CLOCK_Y_START, pEtaString);
1071
1072 // if less than one day
1073 if (iTotalTime < 60 * 24)
1074 {
1075 // show hours and minutes
1076 UINT Minutes = iTotalTime % 60;
1077 UINT Hours = iTotalTime / 60;
1078 MPrint(CLOCK_MIN_X_START - 5, CLOCK_Y_START, ST::format("{2d}{}", Minutes, gsTimeStrings[1]));
1079 MPrint(CLOCK_HOUR_X_START - 8, CLOCK_Y_START, ST::format("{2d}{}", Hours, gsTimeStrings[0]));
1080 }
1081 else
1082 {
1083 // show days and hours
1084 UINT Hours = iTotalTime / 60 % 24;
1085 UINT Days = iTotalTime / (60 * 24);
1086 MPrint(CLOCK_MIN_X_START - 5, CLOCK_Y_START, ST::format("{2d}{}", Hours, gsTimeStrings[0]));
1087 MPrint(CLOCK_HOUR_X_START - 9, CLOCK_Y_START, ST::format("{2d}{}", Days, gsTimeStrings[3]));
1088 }
1089 }
1090
1091
1092 struct HighLightState
1093 {
1094 INT32 colour_idx;
1095 bool delta;
1096 INT32 old_highlight;
1097 };
1098
1099
HighLightSelection(HighLightState & state,INT32 const line,UINT16 const x,UINT16 const w,BOOLEAN (& predicate)(INT16))1100 static void HighLightSelection(HighLightState& state, INT32 const line, UINT16 const x, UINT16 const w, BOOLEAN (& predicate)(INT16))
1101 {
1102 // is this a valid line?
1103 if (line == -1 || fShowInventoryFlag)
1104 {
1105 state.old_highlight = MAX_CHARACTER_COUNT + 1;
1106 return;
1107 }
1108
1109 // if not ready to change glow phase yet, leave
1110 if (!gfGlowTimerExpired) return;
1111
1112 // check if we have moved lines, if so, reset
1113 if (state.old_highlight != line)
1114 {
1115 state.old_highlight = line;
1116 state.colour_idx = STARTING_COLOR_NUM;
1117 state.delta = false;
1118 }
1119
1120 if (state.colour_idx == 0 || state.colour_idx == 10) state.delta = !state.delta;
1121 state.colour_idx += state.delta ? -1 : +1;
1122
1123 SGPVSurface::Lock l(FRAME_BUFFER);
1124 SetClippingRegionAndImageWidth(l.Pitch(), 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
1125 UINT16* const pDestBuf = l.Buffer<UINT16>();
1126
1127 UINT16 const colour = GlowColor(state.colour_idx);
1128 INT32 const h = Y_SIZE + Y_OFFSET;
1129 for (INT16 i = 0; i != MAX_CHARACTER_COUNT; ++i)
1130 {
1131 if (!predicate(i)) continue;
1132
1133 UINT16 y = Y_START - 1 + i * h;
1134 if (i >= FIRST_VEHICLE) y += 6;
1135
1136 if (i == 0 || !predicate(i - 1) || i == FIRST_VEHICLE)
1137 {
1138 LineDraw(TRUE, x, y, x + w, y, colour, pDestBuf);
1139 }
1140 if (i == MAX_CHARACTER_COUNT - 1 || !predicate(i + 1) || i == FIRST_VEHICLE - 1)
1141 {
1142 LineDraw(TRUE, x, y + h, x + w, y + h, colour, pDestBuf);
1143 }
1144 LineDraw(TRUE, x, y, x, y + h, colour, pDestBuf);
1145 LineDraw(TRUE, x + w, y, x + w, y + h, colour, pDestBuf);
1146
1147 InvalidateRegion(x, y, x + w + 1, y + h + 1);
1148 }
1149 }
1150
1151
HighLightAssignLine(void)1152 static void HighLightAssignLine(void)
1153 {
1154 static HighLightState state = { STARTING_COLOR_NUM, false, MAX_CHARACTER_COUNT + 1 };
1155 HighLightSelection(state, giAssignHighLine, ASSIGN_X, ASSIGN_WIDTH, IsCharacterSelectedForAssignment);
1156 }
1157
1158
HighLightDestLine(void)1159 static void HighLightDestLine(void)
1160 {
1161 static HighLightState state = { STARTING_COLOR_NUM, false, MAX_CHARACTER_COUNT + 1 };
1162 HighLightSelection(state, giDestHighLine, DEST_ETA_X, DEST_ETA_WIDTH, CharacterIsGettingPathPlotted);
1163 }
1164
1165
HighLightSleepLine(void)1166 static void HighLightSleepLine(void)
1167 {
1168 static HighLightState state = { STARTING_COLOR_NUM, false, MAX_CHARACTER_COUNT + 1 };
1169 HighLightSelection(state, giSleepHighLine, SLEEP_X, SLEEP_WIDTH, IsCharacterSelectedForSleep);
1170 }
1171
1172
AddCharacter(SOLDIERTYPE * const s)1173 static void AddCharacter(SOLDIERTYPE* const s)
1174 {
1175 Assert(s != NULL);
1176 Assert(s->bActive);
1177
1178 const size_t start = (s->uiStatusFlags & SOLDIER_VEHICLE ? FIRST_VEHICLE : 0);
1179 for (MapScreenCharacterSt* i = gCharactersList + start;; ++i)
1180 {
1181 Assert(i != gCharactersList + MAX_CHARACTER_COUNT);
1182 if (i == gCharactersList + MAX_CHARACTER_COUNT) return;
1183 if (i->merc == NULL)
1184 {
1185 i->merc = s;
1186 return;
1187 }
1188 }
1189 }
1190
1191
LoadCharacters(void)1192 static void LoadCharacters(void)
1193 {
1194 UINT16 uiCount=0;
1195
1196 // fills array with pressence of player controlled characters
1197 FOR_EACH_IN_TEAM(s, OUR_TEAM)
1198 {
1199 AddCharacter(s);
1200 ++uiCount;
1201 }
1202
1203 // set info char if no selected
1204 if( bSelectedInfoChar == -1 )
1205 {
1206 if (!DialogueActive())
1207 {
1208 ChangeSelectedInfoChar( 0, TRUE );
1209 }
1210 }
1211
1212 // check if ANYONE was available
1213 if( uiCount == 0 )
1214 {
1215 // no one to show
1216 ChangeSelectedInfoChar( -1, TRUE );
1217 bSelectedDestChar = -1;
1218 bSelectedAssignChar = -1;
1219 bSelectedContractChar = -1;
1220 fPlotForHelicopter = FALSE;
1221 }
1222 }
1223
1224
1225 static void EnableDisableTeamListRegionsAndHelpText(void);
1226
1227
DisplayCharacterList(void)1228 static void DisplayCharacterList(void)
1229 {
1230 if (fShowAssignmentMenu && !fTeamPanelDirty)
1231 {
1232 SetFontDestBuffer(FRAME_BUFFER);
1233 return;
1234 }
1235
1236 SetFontDestBuffer(guiSAVEBUFFER);
1237 SetFont(MAP_SCREEN_FONT);
1238 SetFontBackground(FONT_BLACK);
1239
1240 for (INT16 i = 0; i != MAX_CHARACTER_COUNT; ++i)
1241 {
1242 if (!gCharactersList[i].merc) continue;
1243 SOLDIERTYPE const& s = *gCharactersList[i].merc;
1244
1245 UINT8 foreground =
1246 i == (INT16)giHighLine ? FONT_WHITE :
1247 s.bLife == 0 ? FONT_METALGRAY :
1248 CharacterIsGettingPathPlotted(i) ? FONT_LTBLUE :
1249 /* Not in current sector? */
1250 s.sSectorX != sSelMapX ||
1251 s.sSectorY != sSelMapY ||
1252 s.bSectorZ != iCurrentMapSectorZ ? 5 :
1253 /* Mobile? */
1254 s.bAssignment < ON_DUTY ||
1255 s.bAssignment == VEHICLE ? FONT_YELLOW :
1256 FONT_MAP_DKYELLOW;
1257 SetFontForeground(foreground);
1258
1259 UINT16 y = Y_START + i * (Y_SIZE + Y_OFFSET) + 1;
1260 if (i >= FIRST_VEHICLE) y += 6;
1261
1262 // Name
1263 DrawStringCentered(s.name, NAME_X + 1, y, NAME_WIDTH, Y_SIZE, MAP_SCREEN_FONT);
1264
1265 ST::string str;
1266
1267 // Location
1268 str = GetMapscreenMercLocationString(s);
1269 DrawStringCentered(str, LOC_X + 1, y, LOC_WIDTH, Y_SIZE, MAP_SCREEN_FONT);
1270
1271 // Destination
1272 str = GetMapscreenMercDestinationString(s);
1273 if (str[0] != '\0')
1274 {
1275 DrawStringCentered(str, DEST_ETA_X + 1, y, DEST_ETA_WIDTH, Y_SIZE, MAP_SCREEN_FONT);
1276 }
1277
1278 // Assignment
1279 if (fFlashAssignDone && s.fDoneAssignmentAndNothingToDoFlag)
1280 {
1281 SetFontForeground(FONT_RED);
1282 }
1283 ST::string assignment = GetMapscreenMercAssignmentString(s);
1284 DrawStringCentered(assignment, ASSIGN_X + 1, y, ASSIGN_WIDTH, Y_SIZE, MAP_SCREEN_FONT);
1285
1286 // Remaining contract time
1287 str = GetMapscreenMercDepartureString(s, foreground != FONT_WHITE ? &foreground : 0);
1288 SetFontForeground(foreground);
1289 DrawStringCentered(str, TIME_REMAINING_X + 1, y, TIME_REMAINING_WIDTH, Y_SIZE, MAP_SCREEN_FONT);
1290 }
1291
1292 HandleDisplayOfSelectedMercArrows();
1293 SetFontDestBuffer(FRAME_BUFFER);
1294
1295 EnableDisableTeamListRegionsAndHelpText();
1296
1297 /* Mark all pop ups as dirty, so that any open assigment menus get reblitted
1298 * over top of the team list */
1299 MarkAllBoxesAsAltered();
1300 }
1301
RefreshMapScreen()1302 static void RefreshMapScreen()
1303 {
1304 fMapPanelDirty = TRUE;
1305 }
1306
1307 // THIS IS STUFF THAT RUNS *ONCE* DURING APPLICATION EXECUTION, AT INITIAL STARTUP
MapScreenInit(void)1308 void MapScreenInit(void)
1309 {
1310 // init palettes for big map
1311 InitializePalettesForMap( );
1312
1313 InitMapScreenInterfaceMap();
1314
1315 // set up leave list arrays for dismissed mercs
1316 InitLeaveList( );
1317
1318 guiUpdatePanelTactical = AddVideoObjectFromFile(INTERFACEDIR "/group_confirm_tactical.sti");
1319
1320 OnMapSecretFound.addListener("default:MapScreen", [](UINT8) { RefreshMapScreen(); });
1321 }
1322
1323
MapScreenShutdown(void)1324 void MapScreenShutdown(void)
1325 {
1326 // free up alloced mapscreen messages
1327 FreeGlobalMessageList( );
1328
1329 ShutDownPalettesForMap( );
1330
1331 // free memory for leave list arrays for dismissed mercs
1332 ShutDownLeaveList( );
1333
1334 DeleteVideoObject(guiUpdatePanelTactical);
1335 }
1336
1337
1338 static void AddTeamPanelSortButtonsForMapScreen(void);
1339 static void BlitBackgroundToSaveBuffer(void);
1340 static void CheckForAndRenderNewMailOverlay(void);
1341 static void CheckForInventoryModeCancellation(void);
1342 static void CheckIfPlottingForCharacterWhileAirCraft(void);
1343 static void CheckToSeeIfMouseHasLeftMapRegionDuringPathPlotting(void);
1344 static void ContractButtonCallback(GUI_BUTTON* btn, INT32 reason);
1345 static void CreateDestroyMapCharacterScrollButtons(void);
1346 static void CreateDestroyTrashCanRegion(void);
1347 static void CreateMouseRegionsForTeamList(void);
1348 static void DetermineIfContractMenuCanBeShown(void);
1349 static void FaceRegionBtnCallback(MOUSE_REGION* pRegion, INT32 iReason);
1350 static void HandleAnimatedCursorsForMapScreen(void);
1351 static void HandleAssignmentsDoneAndAwaitingFurtherOrders(void);
1352 static void HandleChangeOfHighLightedLine(void);
1353 static void HandleChangeOfInfoChar(void);
1354 static void HandleCharBarRender(void);
1355 static void HandleCommonGlowTimer(void);
1356 static void HandleContractTimeFlashForMercThatIsAboutLeave(void);
1357 static void HandleCursorOverRifleAmmo(void);
1358 static void HandleHighLightingOfLinesInTeamPanel(void);
1359 static void HandleMapInventoryCursor(void);
1360 static UINT32 HandleMapUI(void);
1361 static void HandlePostAutoresolveMessages(void);
1362 static void HandlePreBattleInterfaceWithInventoryPanelUp(void);
1363 static void HandleSpontanousTalking(void);
1364 static void InitPreviousPaths(void);
1365 static void InterruptTimeForMenus(void);
1366 static void ItemRegionBtnCallback(MOUSE_REGION* pRegion, INT32 iReason);
1367 static void ItemRegionMvtCallback(MOUSE_REGION* pRegion, INT32 iReason);
1368 static void MapScreenMarkRegionBtnCallback(MOUSE_REGION* pRegion, INT32 iReason);
1369 static void MapscreenMarkButtonsDirty(void);
1370 static void MonitorMapUIMessage(void);
1371 static void PlotPermanentPaths(void);
1372 static void PlotTemporaryPaths(void);
1373 static void RenderMapCursorsIndexesAnims(void);
1374 static void RenderTeamRegionBackground(void);
1375 static BOOLEAN RequestGiveSkyriderNewDestination(void);
1376 static void ResetAllSelectedCharacterModes(void);
1377 static void UpDateStatusOfContractBox(void);
1378 static void UpdateBadAssignments(void);
1379 static void UpdateCursorIfInLastSector(void);
1380 static void UpdatePausedStatesDueToTimeCompression(void);
1381 static void UpdateStatusOfMapSortButtons(void);
1382 static void UpdateTheStateOfTheNextPrevMapScreenCharacterButtons(void);
1383
1384
MapScreenHandle(void)1385 ScreenID MapScreenHandle(void)
1386 try
1387 {
1388 UINT32 uiNewScreen;
1389 INT32 iCounter = 0;
1390
1391 //DO NOT MOVE THIS FUNCTION CALL!!!
1392 //This determines if the help screen should be active
1393 if( ShouldTheHelpScreenComeUp( HelpScreenDetermineWhichMapScreenHelpToShow(), FALSE ) )
1394 {
1395 // handle the help screen
1396 HelpScreenHandler();
1397 return( MAP_SCREEN );
1398 }
1399
1400
1401 if ( !fInMapMode )
1402 {
1403 gfFirstMapscreenFrame = TRUE;
1404
1405 InitPreviousPaths();
1406
1407 gfInConfirmMapMoveMode = FALSE;
1408 gfInChangeArrivalSectorMode = FALSE;
1409
1410 fLeavingMapScreen = FALSE;
1411 fFlashAssignDone = FALSE;
1412 gfEnteringMapScreen = 0;
1413
1414 // fDisabledMapBorder = FALSE;
1415
1416 // handle the sort buttons
1417 AddTeamPanelSortButtonsForMapScreen( );
1418
1419 // load bottom graphics
1420 LoadMapScreenInterfaceBottom( );
1421
1422 MoveToEndOfMapScreenMessageList( );
1423
1424
1425 // if the current time compression mode is something legal in mapscreen, keep it
1426 if ( ( giTimeCompressMode >= TIME_COMPRESS_5MINS ) && ( giTimeCompressMode <= TIME_COMPRESS_60MINS ) )
1427 {
1428 // leave the current time compression mode set, but DO stop it
1429 StopTimeCompression();
1430 }
1431 else
1432 {
1433 // set compressed mode to X0 (which also stops time compression)
1434 SetGameTimeCompressionLevel( TIME_COMPRESS_X0 );
1435 }
1436
1437 // disable video overlay for tactical scroll messages
1438 EnableDisableScrollStringVideoOverlay( FALSE );
1439
1440 CreateDestroyInsuranceMouseRegionForMercs( TRUE );
1441
1442 // ATE: Init tactical interface interface ( always to team panel )
1443 //SetCurrentInterfacePanel( TEAM_PANEL );
1444 // Do some things to this now that it's initialized
1445 //gViewportRegion.Disable();
1446 //gRadarRegion.Disable();
1447 //Disable all faces
1448 SetAllAutoFacesInactive( );
1449
1450 // create buttons
1451 CreateButtonsForMapBorder( );
1452
1453 // create mouse regions for level markers
1454 CreateMouseRegionsForLevelMarkers( );
1455
1456
1457 // change selected sector/level if necessary
1458 // NOTE: Must come after border buttons are created, since it may toggle them!
1459 if (!AnyMercsHired())
1460 {
1461 // Select starting sector.
1462 ChangeSelectedMapSector(SECTORX(START_SECTOR), SECTORY(START_SECTOR), 0);
1463 }
1464 else if( ( gWorldSectorX > 0 ) && ( gWorldSectorY > 0 ) && ( gbWorldSectorZ != -1 ) )
1465 {
1466 // select currently loaded sector as the map sector
1467 ChangeSelectedMapSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ );
1468 }
1469 else // no loaded sector
1470 {
1471 // Only select start sector, if there is no current selection, otherwise leave it as is.
1472 if ( ( sSelMapX == 0 ) || ( sSelMapY == 0 ) || ( iCurrentMapSectorZ == -1 ) )
1473 {
1474 ChangeSelectedMapSector(SECTORX(START_SECTOR), SECTORY(START_SECTOR), 0);
1475 }
1476 }
1477
1478 // we are in fact in the map, do not repeat this sequence
1479 fInMapMode = TRUE;
1480
1481 // Refreshing the whole screen, otherwise user could see remnants of
1482 // the tactical screen.
1483 if(g_ui.isBigScreen())
1484 {
1485 InvalidateRegion(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
1486 }
1487
1488 // dirty map
1489 fMapPanelDirty=TRUE;
1490
1491 // dirty team region
1492 fTeamPanelDirty = TRUE;
1493
1494 // dirty info region
1495 fCharacterInfoPanelDirty = TRUE;
1496
1497 // direty map bottom region
1498 fMapScreenBottomDirty = TRUE;
1499
1500 // tactical scroll of messages not allowed to beep until new message is added in tactical
1501 fOkToBeepNewMessage = FALSE;
1502
1503 // not in laptop, not about to go there either
1504 fLapTop=FALSE;
1505
1506 // reset show aircraft flag
1507 //fShowAircraftFlag = FALSE;
1508
1509 fShowItemHighLight = FALSE;
1510
1511 // reset all selected character flags
1512 ResetAllSelectedCharacterModes( );
1513
1514 fShowMapInventoryPool = FALSE;
1515
1516 // init character list - set all values in the list to 0
1517 InitalizeVehicleAndCharacterList( );
1518
1519 LoadCharacters();
1520
1521
1522 // set up regions
1523 MSYS_DefineRegion( &gMapViewRegion, MAP_VIEW_START_X + MAP_GRID_X, MAP_VIEW_START_Y + MAP_GRID_Y,MAP_VIEW_START_X + MAP_VIEW_WIDTH+MAP_GRID_X-1, MAP_VIEW_START_Y + MAP_VIEW_HEIGHT-1 + 8, MSYS_PRIORITY_HIGH - 3,
1524 MSYS_NO_CURSOR, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK );
1525
1526 MSYS_DefineRegion( &gCharInfoHandRegion,
1527 PLAYER_INFO_HAND_START_X, PLAYER_INFO_HAND_START_Y,
1528 PLAYER_INFO_HAND_END_X, PLAYER_INFO_HAND_END_Y,
1529 MSYS_PRIORITY_HIGH, MSYS_NO_CURSOR,
1530 ItemRegionMvtCallback , ItemRegionBtnCallback );
1531
1532 MSYS_DefineRegion( &gCharInfoFaceRegion, (INT16) PLAYER_INFO_FACE_START_X, (INT16) PLAYER_INFO_FACE_START_Y, (INT16) PLAYER_INFO_FACE_END_X, (INT16) PLAYER_INFO_FACE_END_Y, MSYS_PRIORITY_HIGH,
1533 MSYS_NO_CURSOR, MSYS_NO_CALLBACK, FaceRegionBtnCallback );
1534
1535 MSYS_DefineRegion(&gMPanelRegion, INV_REGION_X, INV_REGION_Y, INV_REGION_X + INV_REGION_WIDTH, INV_REGION_Y + INV_REGION_HEIGHT, MSYS_PRIORITY_HIGH, MSYS_NO_CURSOR, MSYS_NO_CALLBACK, MSYS_NO_CALLBACK);
1536 // screen mask for animated cursors
1537 MSYS_DefineRegion(&gMapScreenMaskRegion, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_LOW, CURSOR_NORMAL, MSYS_NO_CALLBACK, MapScreenMarkRegionBtnCallback);
1538
1539 // set help text for item glow region
1540 gCharInfoHandRegion.SetFastHelpText(pMiscMapScreenMouseRegionHelpText[0]);
1541
1542 // init the timer menus
1543 InitTimersForMoveMenuMouseRegions( );
1544
1545 giMapContractButton = QuickCreateButtonImg(INTERFACEDIR "/contractbutton.sti", 0, 1, CONTRACT_X + 5, CONTRACT_Y - 1, MSYS_PRIORITY_HIGHEST - 5, ContractButtonCallback);
1546 giMapContractButton->SpecifyGeneralTextAttributes(pContractButtonString, MAP_SCREEN_FONT, CHAR_TEXT_FONT_COLOR, FONT_BLACK);
1547 giMapContractButton->SpecifyTextSubOffsets(0, 0, TRUE);
1548 giMapContractButton->SpecifyHilitedTextColors(FONT_MCOLOR_WHITE, DEFAULT_SHADOW);
1549 giMapContractButton->SetFastHelpText(pMapScreenMouseRegionHelpText[3]);
1550
1551 CreateMouseRegionForPauseOfClock();
1552
1553 // create mouse regions
1554 CreateMouseRegionsForTeamList( );
1555
1556 ReBuildCharactersList( );
1557
1558 // create status bar region
1559 CreateMapStatusBarsRegion( );
1560
1561 if ( !gfFadeOutDone && !gfFadeIn )
1562 {
1563 MSYS_SetCurrentCursor(SCREEN_CURSOR);
1564 }
1565 gMPanelRegion.Disable();
1566
1567 // create contract box
1568 CreateContractBox( NULL );
1569
1570 // create the permanent boxes for assignment and its submenus
1571 fShowAssignmentMenu = TRUE;
1572 CreateDestroyAssignmentPopUpBoxes( );
1573 fShowAssignmentMenu = FALSE;
1574
1575
1576 // create merc remove box
1577 CreateMercRemoveAssignBox( );
1578
1579 guiSAVEBUFFER->Fill(Get16BPPColor(RGB_NEAR_BLACK));
1580 FRAME_BUFFER->Fill( Get16BPPColor(RGB_NEAR_BLACK));
1581
1582 if( gpCurrentTalkingFace != NULL )
1583 {
1584 ContinueDialogue(gpCurrentTalkingFace->soldier, FALSE);
1585 }
1586
1587 fOneFrame = FALSE;
1588
1589 if (fEnterMapDueToContract)
1590 {
1591 if( pContractReHireSoldier )
1592 {
1593 FindAndSetThisContractSoldier( pContractReHireSoldier );
1594 }
1595 fEnterMapDueToContract = FALSE;
1596 }
1597
1598 MoveRadarScreen();
1599 }
1600
1601
1602 // if not going anywhere else
1603 if ( guiPendingScreen == NO_PENDING_SCREEN )
1604 {
1605 if ( HandleFadeOutCallback( ) )
1606 {
1607 // force mapscreen to be reinitialized even though we're already in it
1608 EndMapScreen( TRUE );
1609 return( MAP_SCREEN );
1610 }
1611
1612 if ( HandleBeginFadeOut( MAP_SCREEN ) )
1613 {
1614 return( MAP_SCREEN );
1615 }
1616 }
1617
1618
1619 // check to see if we need to rebuild the characterlist for map screen
1620 HandleRebuildingOfMapScreenCharacterList( );
1621
1622 HandleStrategicTurn( );
1623
1624
1625 /*
1626 // update cursor based on state
1627 if (bSelectedDestChar == -1 && !fPlotForHelicopter && !gfInChangeArrivalSectorMode)
1628 {
1629 // reset cursor
1630 if ( !gfFadeIn )
1631 {
1632 ChangeMapScreenMaskCursor( CURSOR_NORMAL );
1633 }
1634 }*/
1635
1636 // check if we are going to create or destroy map border graphics?
1637 CreateDestroyMapInventoryPoolButtons( FALSE );
1638
1639 // set up buttons for mapscreen scroll
1640 //HandleMapScrollButtonStates( );
1641
1642
1643 // don't process any input until we've been through here once
1644 if (!gfFirstMapscreenFrame)
1645 {
1646 // Handle Interface
1647 uiNewScreen = HandleMapUI( );
1648 if ( uiNewScreen != MAP_SCREEN )
1649 {
1650 return( MAP_SCREEN );
1651 }
1652 }
1653
1654
1655 // handle flashing of contract column for any mercs leaving very soon
1656 HandleContractTimeFlashForMercThatIsAboutLeave( );
1657
1658 if (!fShownAssignmentMenu && fShowAssignmentMenu)
1659 {
1660 // need a one frame pause
1661 fShownAssignmentMenu = fShowAssignmentMenu;
1662 fShowAssignmentMenu = FALSE;
1663 fOneFrame = TRUE;
1664 }
1665 else if (!fShownContractMenu && fShowContractMenu)
1666 {
1667 fShownContractMenu = fShowContractMenu;
1668 fShowContractMenu = FALSE;
1669 fOneFrame = TRUE;
1670 }
1671 else if( fOneFrame )
1672 {
1673 // one frame passed
1674 fShowContractMenu = fShownContractMenu;
1675 fShowAssignmentMenu = fShownAssignmentMenu;
1676 fOneFrame = FALSE;
1677 }
1678
1679 if (!fShownAssignmentMenu && !fShowAssignmentMenu && !fShownContractMenu)
1680 {
1681 bSelectedAssignChar = -1;
1682 }
1683
1684 HandlePostAutoresolveMessages();
1685
1686 // UpdateLevelButtonStates( );
1687
1688 // NOTE: This must happen *before* UpdateTheStateOfTheNextPrevMapScreenCharacterButtons()
1689 CreateDestroyMapCharacterScrollButtons( );
1690
1691 // update the prev next merc buttons
1692 UpdateTheStateOfTheNextPrevMapScreenCharacterButtons( );
1693
1694 // handle for inventory
1695 HandleCursorOverRifleAmmo( );
1696
1697 // check contract times, update screen if they do change
1698 CheckAndUpdateBasedOnContractTimes( );
1699
1700 // handle flashing of assignment strings when merc's assignment is done
1701 HandleAssignmentsDoneAndAwaitingFurtherOrders( );
1702
1703 // handle timing for the various glowing higlights
1704 HandleCommonGlowTimer( );
1705
1706 // are we attempting to plot a foot/vehicle path during aircraft mode..if so, stop it
1707 CheckIfPlottingForCharacterWhileAirCraft( );
1708
1709 // check to see if helicopter is available
1710 //CheckIfHelicopterAvailable( );
1711 if( fShowMapInventoryPool )
1712 {
1713 HandleFlashForHighLightedItem( );
1714 }
1715
1716
1717 // CreateDestroyMovementBox( 0,0,0 );
1718
1719 // Deque all game events
1720 DequeAllGameEvents();
1721
1722 // Handle Interface Stuff
1723 SetUpInterface( );
1724
1725 // reset time compress has occured
1726 ResetTimeCompressHasOccured( );
1727
1728 // handle change in info char
1729 HandleChangeOfInfoChar( );
1730
1731 // update status of contract box
1732 UpDateStatusOfContractBox( );
1733
1734 // error check of assignments
1735 UpdateBadAssignments( );
1736
1737 // if cursor has left map..will need to update temp path plotting and cursor
1738 CheckToSeeIfMouseHasLeftMapRegionDuringPathPlotting( );
1739
1740 // update assignment menus and submenus
1741 HandleShadingOfLinesForAssignmentMenus( );
1742
1743 // check which menus can be shown right now
1744 DetermineWhichAssignmentMenusCanBeShown( );
1745
1746 // determine if contract menu can be shown
1747 DetermineIfContractMenuCanBeShown( );
1748
1749 // if pre battle and inventory up?..get rid of inventory
1750 HandlePreBattleInterfaceWithInventoryPanelUp( );
1751
1752 // create destroy trash can region
1753 CreateDestroyTrashCanRegion( );
1754
1755 // update these buttons
1756 UpdateStatusOfMapSortButtons( );
1757
1758 // if in inventory mode, make sure it's still ok
1759 CheckForInventoryModeCancellation();
1760
1761 // restore background rects
1762 RestoreBackgroundRects( );
1763
1764 InterruptTimeForMenus( );
1765
1766 // place down background
1767 BlitBackgroundToSaveBuffer( );
1768
1769 if (fLeavingMapScreen) return MAP_SCREEN;
1770
1771 if (!fDisableDueToBattleRoster)
1772 {
1773 /*
1774 // ATE: OK mark is rendering the item every frame - which isn't good
1775 // however, don't want to break the world here..
1776 // this line was added so that when the ItemGlow() is on,
1777 // we're not rendering also, else glow looks bad
1778 if ( !fShowItemHighLight )
1779 {
1780 RenderHandPosItem();
1781 }
1782 */
1783
1784 if( fDrawCharacterList )
1785 {
1786 if ( !fShowInventoryFlag )
1787 {
1788 // if we are not in inventory mode, show character list
1789 HandleHighLightingOfLinesInTeamPanel( );
1790
1791 DisplayCharacterList();
1792 }
1793
1794 fDrawCharacterList = FALSE;
1795 }
1796 }
1797
1798
1799 if (!fShowMapInventoryPool && !gfPauseDueToPlayerGamePause /* && !fDisabledMapBorder */)
1800 {
1801 RenderMapCursorsIndexesAnims( );
1802 }
1803
1804 if (!fDisableDueToBattleRoster)
1805 {
1806 // render status bar
1807 HandleCharBarRender( );
1808 }
1809
1810 if( fShowInventoryFlag || fDisableDueToBattleRoster )
1811 {
1812 for( iCounter = 0; iCounter < MAX_SORT_METHODS; iCounter++ )
1813 {
1814 UnMarkButtonDirty( giMapSortButton[ iCounter ] );
1815 }
1816 }
1817
1818 if( fShowContractMenu || fDisableDueToBattleRoster )
1819 {
1820 UnMarkButtonDirty( giMapContractButton );
1821 }
1822
1823 // handle any old messages
1824 ScrollString( );
1825
1826
1827 HandleSpontanousTalking( );
1828
1829 if (!fDisableDueToBattleRoster)
1830 {
1831 // remove the move box once user leaves it
1832 CreateDestroyMovementBox( 0,0,0 );
1833
1834 // this updates the move box contents when changes took place
1835 ReBuildMoveBox( );
1836 }
1837
1838 if (!fDisableDueToBattleRoster &&
1839 (fShowAssignmentMenu || fShowContractMenu))
1840 {
1841 // highlight lines?
1842 HandleHighLightingOfLinesInTeamPanel( );
1843
1844 // render glow for contract region
1845 GlowTrashCan( );
1846
1847 // handle changing of highlighted lines
1848 HandleChangeOfHighLightedLine( );
1849 }
1850
1851 if (!fDisableDueToBattleRoster)
1852 {
1853 // render face of current info char, for animation
1854 DrawFace();
1855
1856 // handle autofaces
1857 HandleAutoFaces( );
1858 HandleTalkingAutoFaces( );
1859
1860 //GlowItem( );
1861
1862 }
1863
1864
1865 // automatically turns off mapscreen ui overlay messages when appropriate
1866 MonitorMapUIMessage( );
1867
1868
1869 // if heli is around, show it
1870 if (iHelicopterVehicleId != -1 && fShowAircraftFlag && iCurrentMapSectorZ == 0 && !fShowMapInventoryPool)
1871 {
1872 // this is done on EVERY frame, I guess it beats setting entire map dirty all the time while he's moving...
1873 DisplayPositionOfHelicopter( );
1874 }
1875
1876
1877 // display town info
1878 DisplayTownInfo( sSelMapX, sSelMapY, ( INT8 ) iCurrentMapSectorZ );
1879
1880 if (fShowTownInfo)
1881 {
1882 // force update of town mine info boxes
1883 ForceUpDateOfBox( ghTownMineBox );
1884 MapscreenMarkButtonsDirty();
1885 }
1886
1887 if( fShowAttributeMenu )
1888 {
1889 // mark all popups as dirty
1890 MarkAllBoxesAsAltered( );
1891 }
1892
1893 // if plotting path
1894 if (bSelectedDestChar != -1 || fPlotForHelicopter)
1895 {
1896 // plot out paths
1897 PlotPermanentPaths( );
1898 PlotTemporaryPaths( );
1899
1900 // show ETA
1901 RenderMapBorderEtaPopUp( );
1902 DisplayGroundEta();
1903 }
1904
1905 HandleContractRenewalSequence( );
1906
1907 // handle dialog
1908 HandleDialogue( );
1909
1910
1911 // now the border corner piece
1912 //RenderMapBorderCorner( );
1913
1914
1915 // Display Framerate
1916 DisplayFrameRate( );
1917
1918 // update paused states
1919 UpdatePausedStatesDueToTimeCompression( );
1920
1921 // is there a description to be displayed?
1922 RenderItemDescriptionBox( );
1923
1924 RenderClock();
1925
1926 if (fEndShowInventoryFlag)
1927 {
1928 if (InKeyRingPopup())
1929 {
1930 DeleteKeyRingPopup( );
1931 }
1932 else
1933 {
1934 fShowInventoryFlag = FALSE;
1935 }
1936
1937 fTeamPanelDirty = TRUE;
1938 fEndShowInventoryFlag = FALSE;
1939 }
1940
1941 // handle animated cursor update
1942 if ( !gfFadeIn )
1943 {
1944 HandleAnimatedCursorsForMapScreen( );
1945 }
1946
1947 // if inventory is being manipulated, update cursor
1948 HandleMapInventoryCursor();
1949
1950 if (fShowDescriptionFlag)
1951 {
1952 // unmark done button
1953 if (gpItemDescObject->usItem == MONEY ||
1954 GCM->getItem(gpItemDescObject->usItem)->isGun())
1955 {
1956 MapscreenMarkButtonsDirty();
1957 }
1958
1959 UnMarkButtonDirty( giMapInvDoneButton );
1960 //UnMarkButtonDirty( giCharInfoButton[ 0 ] );
1961 //UnMarkButtonDirty( giCharInfoButton[ 1 ] );
1962 MarkAButtonDirty( giMapInvDescButton );
1963 }
1964 else
1965 {
1966 if (fShowInventoryFlag)
1967 {
1968 MarkAButtonDirty( giMapInvDoneButton );
1969 MarkAButtonDirty( giCharInfoButton[ 1 ] );
1970 MarkAButtonDirty( giCharInfoButton[ 0 ] );
1971 }
1972 }
1973
1974 DrawMilitiaPopUpBox( );
1975
1976 if (!fDisableDueToBattleRoster)
1977 {
1978 CreateDestroyTheUpdateBox( );
1979 DisplaySoldierUpdateBox( );
1980 }
1981
1982
1983 // pop up display boxes
1984 DisplayBoxes(FRAME_BUFFER);
1985
1986 // render buttons
1987 RenderButtons( );
1988
1989 if( fShowMapScreenMovementList )
1990 {
1991 // redisplay Movement box to blit it over any border buttons, since if long enough it can overlap them
1992 ForceUpDateOfBox( ghMoveBox );
1993 DisplayOnePopupBox( ghMoveBox, FRAME_BUFFER );
1994 }
1995
1996 if( fShowContractMenu )
1997 {
1998 // redisplay Contract box to blit it over any map sort buttons, since they overlap
1999 ForceUpDateOfBox( ghContractBox );
2000 DisplayOnePopupBox( ghContractBox, FRAME_BUFFER );
2001 }
2002
2003
2004 //If we have new email, blink the email icon on top of the laptop button.
2005 CheckForAndRenderNewMailOverlay();
2006
2007 // handle video overlays
2008 ExecuteVideoOverlays( );
2009
2010 if ( InItemStackPopup( ) )
2011 {
2012 RenderItemStackPopup( FALSE );
2013 }
2014
2015 if( InKeyRingPopup() )
2016 {
2017 RenderKeyRingPopup( FALSE );
2018 }
2019
2020 CheckForMeanwhileOKStart( );
2021
2022 // save background rects
2023 // ATE: DO this BEFORE rendering help text....
2024 SaveBackgroundRects( );
2025
2026 if (!fDisableDueToBattleRoster && !fShowAssignmentMenu && !fShowContractMenu)
2027 {
2028 // highlight lines?
2029 HandleHighLightingOfLinesInTeamPanel( );
2030
2031 // render glow for contract region
2032 GlowTrashCan( );
2033
2034 // handle changing of highlighted lines
2035 HandleChangeOfHighLightedLine( );
2036
2037 GlowItem( );
2038 }
2039
2040
2041 RenderButtonsFastHelp();
2042
2043 // execute dirty
2044 ExecuteBaseDirtyRectQueue( );
2045
2046 // update cursor
2047 UpdateCursorIfInLastSector( );
2048
2049 EndFrameBufferRender( );
2050
2051
2052 // if not going anywhere else
2053 if ( guiPendingScreen == NO_PENDING_SCREEN )
2054 {
2055 if ( HandleFadeInCallback( ) )
2056 {
2057 // force mapscreen to be reinitialized even though we're already in it
2058 EndMapScreen( TRUE );
2059 }
2060
2061 if ( HandleBeginFadeIn( MAP_SCREEN ) )
2062 {
2063 }
2064 }
2065
2066 HandlePreBattleInterfaceStates();
2067
2068 if( gfHotKeyEnterSector )
2069 {
2070 gfHotKeyEnterSector = FALSE;
2071 ActivatePreBattleEnterSectorAction();
2072 }
2073
2074 if ( gfRequestGiveSkyriderNewDestination )
2075 {
2076 RequestGiveSkyriderNewDestination();
2077 gfRequestGiveSkyriderNewDestination = FALSE;
2078 }
2079
2080
2081 if( gfFirstMapscreenFrame )
2082 {
2083 gfFirstMapscreenFrame = FALSE;
2084 }
2085 else
2086 {
2087 // handle exiting from mapscreen due to both exit button clicks and keyboard equivalents
2088 HandleExitsFromMapScreen( );
2089 }
2090
2091
2092 return( MAP_SCREEN );
2093 }
2094 catch (...) { return ERROR_SCREEN; /* XXX TODO001A originally returned FALSE */ }
2095
2096
DrawString(const ST::string & str,UINT16 uiX,UINT16 uiY,SGPFont font)2097 static void DrawString(const ST::string& str, UINT16 uiX, UINT16 uiY, SGPFont font)
2098 {
2099 // draw monochrome string
2100 SetFont(font);
2101 GDirtyPrint(uiX, uiY, str);
2102 }
2103
2104
DrawStringCentered(const ST::string & str,UINT16 x,UINT16 y,UINT16 w,UINT16 h,SGPFont font)2105 static void DrawStringCentered(const ST::string& str, UINT16 x, UINT16 y, UINT16 w, UINT16 h, SGPFont font)
2106 {
2107 INT16 cx;
2108 INT16 cy;
2109 FindFontCenterCoordinates(x, y, w, h, str, font, &cx, &cy);
2110 DrawString(str, cx, cy, font);
2111 }
2112
2113
DrawStringRight(const ST::string & str,UINT16 x,UINT16 y,UINT16 w,UINT16 h,SGPFont font)2114 void DrawStringRight(const ST::string& str, UINT16 x, UINT16 y, UINT16 w, UINT16 h, SGPFont font)
2115 {
2116 INT16 rx;
2117 INT16 ry;
2118 FindFontRightCoordinates(x, y, w, h, str, font, &rx, &ry);
2119 DrawString(str, rx, ry, font);
2120 }
2121
2122
2123 static void RenderMapHighlight(INT16 sMapX, INT16 sMapY, UINT16 usLineColor, BOOLEAN fStationary);
2124 static void RestoreMapSectorCursor(INT16 sMapX, INT16 sMapY);
2125
2126
RenderMapCursorsIndexesAnims(void)2127 static void RenderMapCursorsIndexesAnims(void)
2128 {
2129 BOOLEAN fSelectedSectorHighlighted = FALSE;
2130 BOOLEAN fSelectedCursorIsYellow = TRUE;
2131 UINT16 usCursorColor;
2132 UINT32 uiDeltaTime;
2133 static INT16 sPrevHighlightedMapX = -1, sPrevHighlightedMapY = -1;
2134 static INT16 sPrevSelectedMapX = -1, sPrevSelectedMapY = -1;
2135 static BOOLEAN fFlashCursorIsYellow = FALSE;
2136 BOOLEAN fDrawCursors;
2137 BOOLEAN fHighlightChanged = FALSE;
2138
2139
2140 HandleAnimationOfSectors( );
2141
2142 if( gfBlitBattleSectorLocator )
2143 {
2144 HandleBlitOfSectorLocatorIcon( gubPBSectorX, gubPBSectorY, gubPBSectorZ, LOCATOR_COLOR_RED );
2145 }
2146
2147 fDrawCursors = CanDrawSectorCursor( );
2148
2149 // if mouse cursor is over a map sector
2150 if ( fDrawCursors && ( GetMouseMapXY( &gsHighlightSectorX, &gsHighlightSectorY ) ) )
2151 {
2152 // handle highlighting of sector pointed at ( WHITE )
2153
2154 // if we're over a different sector than when we previously blitted this
2155 if ( ( gsHighlightSectorX != sPrevHighlightedMapX ) || ( gsHighlightSectorY != sPrevHighlightedMapY ) || gfMapPanelWasRedrawn )
2156 {
2157 if ( sPrevHighlightedMapX != -1 && sPrevHighlightedMapY != -1 )
2158 {
2159 RestoreMapSectorCursor( sPrevHighlightedMapX, sPrevHighlightedMapY );
2160 }
2161
2162 // draw WHITE highlight rectangle
2163 RenderMapHighlight( gsHighlightSectorX, gsHighlightSectorY, Get16BPPColor( RGB_WHITE ), FALSE );
2164
2165 sPrevHighlightedMapX = gsHighlightSectorX;
2166 sPrevHighlightedMapY = gsHighlightSectorY;
2167
2168 fHighlightChanged = TRUE;
2169 }
2170 }
2171 else
2172 {
2173 // nothing now highlighted
2174 gsHighlightSectorX = -1;
2175 gsHighlightSectorY = -1;
2176
2177 if ( sPrevHighlightedMapX != -1 && sPrevHighlightedMapY != -1 )
2178 {
2179 RestoreMapSectorCursor( sPrevHighlightedMapX, sPrevHighlightedMapY );
2180 fHighlightChanged = TRUE;
2181 }
2182
2183 sPrevHighlightedMapX = -1;
2184 sPrevHighlightedMapY = -1;
2185 }
2186
2187
2188 // handle highlighting of selected sector ( YELLOW ) - don't show it while plotting movement
2189 if (fDrawCursors && bSelectedDestChar == -1 && !fPlotForHelicopter)
2190 {
2191 // if mouse cursor is over the currently selected sector
2192 if( ( gsHighlightSectorX == sSelMapX ) && ( gsHighlightSectorY == sSelMapY ) )
2193 {
2194 fSelectedSectorHighlighted = TRUE;
2195
2196 // do we need to flash the cursor? get the delta in time
2197 uiDeltaTime = GetJA2Clock( ) - guiFlashCursorBaseTime;
2198
2199 if ( uiDeltaTime > 300 )
2200 {
2201 guiFlashCursorBaseTime = GetJA2Clock();
2202 fFlashCursorIsYellow = !fFlashCursorIsYellow;
2203
2204 fHighlightChanged = TRUE;
2205 }
2206 }
2207
2208 if ( !fSelectedSectorHighlighted || fFlashCursorIsYellow )
2209 {
2210 // draw YELLOW highlight rectangle
2211 usCursorColor = Get16BPPColor( RGB_YELLOW );
2212 }
2213 else
2214 {
2215 // draw WHITE highlight rectangle
2216 usCursorColor = Get16BPPColor( RGB_WHITE );
2217
2218 // index letters will also be white instead of yellow so that they flash in synch with the cursor
2219 fSelectedCursorIsYellow = FALSE;
2220 }
2221
2222 // always render this one, it's too much of a pain detecting overlaps with the white cursor otherwise
2223 RenderMapHighlight( sSelMapX, sSelMapY, usCursorColor, TRUE );
2224
2225 if ( ( sPrevSelectedMapX != sSelMapX ) || ( sPrevSelectedMapY != sSelMapY ) )
2226 {
2227 sPrevSelectedMapX = sSelMapX;
2228 sPrevSelectedMapY = sSelMapY;
2229
2230 fHighlightChanged = TRUE;
2231 }
2232 }
2233 else
2234 {
2235 // erase yellow highlight cursor
2236 if ( sPrevSelectedMapX != -1 && sPrevSelectedMapY != -1 )
2237 {
2238 RestoreMapSectorCursor( sPrevSelectedMapX, sPrevSelectedMapY );
2239 fHighlightChanged = TRUE;
2240 }
2241
2242 sPrevSelectedMapX = -1;
2243 sPrevSelectedMapY = -1;
2244 }
2245
2246
2247 if ( fHighlightChanged || gfMapPanelWasRedrawn )
2248 {
2249 // redraw sector index letters and numbers
2250 DrawMapIndexBigMap( fSelectedCursorIsYellow );
2251 }
2252 }
2253
2254
2255 static BOOLEAN AnyMovableCharsInOrBetweenThisSector(INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ);
2256 static BOOLEAN CanMoveBullseyeAndClickedOnIt(INT16 sMapX, INT16 sMapY);
2257 static void CancelChangeArrivalSectorMode(void);
2258 static void CancelOrShortenPlottedPath(void);
2259 static void CreateBullsEyeOrChopperSelectionPopup(void);
2260 static void GetMapKeyboardInput(MapEvent&);
2261 static void PollLeftButtonInMapView(MapEvent&);
2262 static void PollRightButtonInMapView(MapEvent&);
2263 static void StartChangeSectorArrivalMode(void);
2264 static void StartConfirmMapMoveMode(INT16 sMapY);
2265
2266
OpenSectorInventory()2267 static void OpenSectorInventory()
2268 {
2269 if(!fShowMapInventoryPool)
2270 {
2271 fShowMapInventoryPool = TRUE;
2272 CreateDestroyMapInventoryPoolButtons( TRUE );
2273 }
2274 }
2275
ToggleSectorInventory()2276 static void ToggleSectorInventory()
2277 {
2278 if(!fShowMapInventoryPool)
2279 {
2280 OpenSectorInventory();
2281 }
2282 else
2283 {
2284 CancelSectorInventoryDisplayIfOn(FALSE);
2285 }
2286 }
2287
2288 // Drawing the Map
HandleMapUI(void)2289 static UINT32 HandleMapUI(void)
2290 {
2291 MapEvent new_event = MAP_EVENT_NONE;
2292 INT16 sMapX = 0, sMapY = 0;
2293 INT16 sX, sY;
2294 UINT32 uiNewScreen = MAP_SCREEN;
2295 BOOLEAN fWasAlreadySelected;
2296
2297
2298 // Get Input from keyboard
2299 GetMapKeyboardInput(new_event);
2300
2301
2302 CreateDestroyMapInvButton();
2303
2304 // Get mouse
2305 PollLeftButtonInMapView(new_event);
2306 PollRightButtonInMapView(new_event);
2307
2308 // Switch on event
2309 switch (new_event)
2310 {
2311 case MAP_EVENT_NONE:
2312 break;
2313
2314 case MAP_EVENT_PLOT_PATH:
2315 GetMouseMapXY(&sMapX, &sMapY);
2316 // plotting for the chopper?
2317 if (fPlotForHelicopter)
2318 {
2319 PlotPathForHelicopter( sMapX, sMapY );
2320 fTeamPanelDirty = TRUE;
2321 }
2322 else
2323 {
2324 // plot for character
2325
2326 // check for valid character
2327 Assert ( bSelectedDestChar != -1 );
2328 if ( bSelectedDestChar == -1 )
2329 break;
2330
2331 // check if last sector in character's path is same as where mouse is
2332 SOLDIERTYPE* const s = gCharactersList[bSelectedDestChar].merc;
2333 const INT16 dst_sector = GetLastSectorIdInCharactersPath(s);
2334 if (dst_sector != sMapX + sMapY * MAP_WORLD_X)
2335 {
2336 sX = dst_sector % MAP_WORLD_X;
2337 sY = dst_sector / MAP_WORLD_X;
2338 RestoreBackgroundForMapGrid( sX, sY );
2339 // fMapPanelDirty = TRUE;
2340 }
2341
2342 if (SectorIsPassable(SECTOR(sMapX, sMapY)))
2343 {
2344 // Can we get go there? (NULL temp character path)
2345 if ( GetLengthOfPath( pTempCharacterPath ) > 0 )
2346 {
2347 PlotPathForCharacter(*s, sMapX, sMapY, false);
2348
2349 // copy the path to every other selected character
2350 CopyPathToAllSelectedCharacters(GetSoldierMercPathPtr(s));
2351
2352 StartConfirmMapMoveMode( sMapY );
2353 fMapPanelDirty = TRUE;
2354 fTeamPanelDirty = TRUE; // update team panel desinations
2355 }
2356 else
2357 {
2358 // means it's a vehicle and we've clicked an off-road sector
2359 BeginMapUIMessage(0, pMapErrorString[40]);
2360 }
2361 }
2362 }
2363 break;
2364
2365
2366 case MAP_EVENT_CANCEL_PATH:
2367 CancelOrShortenPlottedPath( );
2368 break;
2369
2370 case MAP_EVENT_CLICK_SECTOR:
2371
2372 // Get Current mouse position
2373 if ( GetMouseMapXY( &sMapX, &sMapY) )
2374 {
2375 // make sure this is a valid sector
2376 if (!IsTheCursorAllowedToHighLightThisSector(sMapX, sMapY))
2377 {
2378 // if we are in the change drop off sector mode
2379 if (gfInChangeArrivalSectorMode)
2380 {
2381 // display a message as to why we can't change
2382 const ST::string* sMsgString = GCM->getNewString(NS_INVALID_DROPOFF_SECTOR);
2383 BeginMapUIMessage(0, *sMsgString);
2384 }
2385 // nothing else to do, return
2386 return( MAP_SCREEN );
2387 }
2388
2389
2390 // while item in hand
2391 if ( fMapInventoryItem )
2392 {
2393 // if not showing item counts on the map
2394 if ( !fShowItemsFlag )
2395 {
2396 // turn that on
2397 ToggleItemsFilter( );
2398 }
2399
2400 // if item's owner is known
2401 if ( gpItemPointerSoldier != NULL )
2402 {
2403 // make sure it's the owner's sector that's selected
2404 if ( ( gpItemPointerSoldier->sSectorX != sSelMapX ) ||
2405 ( gpItemPointerSoldier->sSectorY != sSelMapY ) ||
2406 ( gpItemPointerSoldier->bSectorZ != iCurrentMapSectorZ ) )
2407 {
2408 ChangeSelectedMapSector( gpItemPointerSoldier->sSectorX, gpItemPointerSoldier->sSectorY, gpItemPointerSoldier->bSectorZ );
2409 }
2410 }
2411
2412 // if not already in sector inventory
2413 if ( !fShowMapInventoryPool )
2414 {
2415 // start it up ( NOTE: for the item OWNER'S sector, regardless of which sector player clicks )
2416 OpenSectorInventory();
2417 }
2418
2419 return( MAP_SCREEN );
2420 }
2421
2422
2423 // don't permit other click handling while item is in cursor (entering PBI would permit item teleports, etc.)
2424 Assert( !fMapInventoryItem );
2425
2426
2427 // this doesn't change selected sector
2428 if ( gfInChangeArrivalSectorMode )
2429 {
2430 if( SectorInfo[ ( SECTOR( sMapX, sMapY ) ) ].ubTraversability[ THROUGH_STRATEGIC_MOVE ] != GROUNDBARRIER )
2431 {
2432 // if it's not enemy air controlled
2433 if (!StrategicMap[CALCULATE_STRATEGIC_INDEX(sMapX, sMapY)].fEnemyAirControlled)
2434 {
2435 ST::string sMsgString;
2436 ST::string sMsgSubString;
2437
2438 // move the landing zone over here
2439 g_merc_arrive_sector = SECTOR(sMapX, sMapY);
2440
2441 // change arrival sector for all mercs currently in transit who are showing up at the landing zone
2442 UpdateAnyInTransitMercsWithGlobalArrivalSector();
2443
2444 // we're done, cancel this mode
2445 CancelChangeArrivalSectorMode();
2446
2447 // get the name of the sector
2448 sMsgSubString = GetSectorIDString(sMapX, sMapY, 0, FALSE);
2449
2450 // now build the string
2451 sMsgString = st_format_printf(pBullseyeStrings[ 1 ], sMsgSubString);
2452
2453 // confirm the change with overlay message
2454 BeginMapUIMessage(0, sMsgString);
2455
2456 // update destination column for any mercs in transit
2457 fTeamPanelDirty = TRUE;
2458 }
2459 else
2460 {
2461 // message: not allowed, don't have airspace secured
2462 BeginMapUIMessage(0, pBullseyeStrings[2]);
2463 }
2464 }
2465
2466 return( MAP_SCREEN );
2467 }
2468 else // not already changing arrival sector
2469 {
2470 if ( CanMoveBullseyeAndClickedOnIt( sMapX, sMapY ) )
2471 {
2472 // if the click is ALSO over the helicopter icon
2473 // NOTE: The helicopter icon is NOT necessarily directly over the helicopter's current sector!!!
2474 if (CheckForClickOverHelicopterIcon(sMapX, sMapY))
2475 {
2476 CreateBullsEyeOrChopperSelectionPopup();
2477 }
2478 else
2479 {
2480 StartChangeSectorArrivalMode();
2481 }
2482
2483 return( MAP_SCREEN );
2484 }
2485 }
2486
2487
2488 // if new map sector was clicked on
2489 if( ( sSelMapX != sMapX ) || ( sSelMapY != sMapY ) )
2490 {
2491 fWasAlreadySelected = FALSE;
2492
2493 // select the clicked sector, retaining the same sublevel depth
2494 ChangeSelectedMapSector( sMapX, sMapY, ( INT8 )iCurrentMapSectorZ );
2495 }
2496 else
2497 {
2498 fWasAlreadySelected = TRUE;
2499 }
2500
2501
2502 // if showing item counts on the map, and not already in sector inventory
2503 if ( fShowItemsFlag && !fShowMapInventoryPool )
2504 {
2505 // show sector inventory for this clicked sector
2506 ChangeSelectedMapSector( sMapX, sMapY, ( INT8 ) iCurrentMapSectorZ );
2507
2508 OpenSectorInventory();
2509
2510 return( MAP_SCREEN );
2511 }
2512
2513
2514 if( gfBlitBattleSectorLocator &&
2515 sMapX == gubPBSectorX && sMapY == gubPBSectorY && iCurrentMapSectorZ == gubPBSectorZ )
2516 { //Bring up a non-persistant version of mapscreen if the user clicks on the sector where a
2517 //battle is taking place.
2518 InitPreBattleInterface(0, false);
2519 return( MAP_SCREEN );
2520 }
2521
2522
2523 // if we're in airspace mode
2524 if (fShowAircraftFlag)
2525 {
2526 // if not moving soldiers, and not yet plotting the helicopter
2527 if (bSelectedDestChar == -1 && !fPlotForHelicopter)
2528 {
2529 // if we're on the surface level, and the click is over the helicopter icon
2530 // NOTE: The helicopter icon is NOT necessarily directly over the helicopter's current sector!!!
2531 if (iCurrentMapSectorZ == 0 && CheckForClickOverHelicopterIcon(sMapX, sMapY))
2532 {
2533 RequestGiveSkyriderNewDestination( );
2534 return( MAP_SCREEN );
2535 }
2536 }
2537 }
2538 else // not in airspace mode
2539 {
2540 // sector must be already selected to initiate movement plotting! This is to allow selecting sectors with
2541 // mercs in them without necessarily initiating movement right away.
2542 if( fWasAlreadySelected )
2543 {
2544 // if there are any movable characters here
2545 if ( AnyMovableCharsInOrBetweenThisSector( sMapX, sMapY, ( INT8 )iCurrentMapSectorZ ) )
2546 {
2547 // if showing the surface level map
2548 if( iCurrentMapSectorZ == 0 )
2549 {
2550 TurnOnShowTeamsMode( );
2551
2552 // NOTE: must allow move box to come up, since there may be movable characters between sectors which are
2553 // unaffected by combat / hostiles / air raid in the sector proper itself!!
2554 // This also allows all strategic movement error handling to be centralized in CanCharacterMoveInStrategic()
2555
2556 // start the move box menu
2557 SetUpMovingListsForSector( sMapX, sMapY, ( INT16 )iCurrentMapSectorZ );
2558 }
2559 else
2560 {
2561 // no strategic movement is possible from underground sectors
2562 DoMapMessageBox( MSG_BOX_BASIC_STYLE, pMapErrorString[ 1 ], MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback );
2563 return( MAP_SCREEN );
2564 }
2565 }
2566 }
2567 }
2568 }
2569 break;
2570
2571 }
2572
2573
2574 // if we pressed something that will cause a screen change
2575 if ( guiPendingScreen != NO_PENDING_SCREEN )
2576 {
2577 uiNewScreen = guiPendingScreen;
2578 }
2579
2580 return( uiNewScreen );
2581 }
2582
2583
HandleKeyESC()2584 static bool HandleKeyESC()
2585 {
2586 gfDontStartTransitionFromLaptop = TRUE;
2587
2588 if (gfPreBattleInterfaceActive && !gfPersistantPBI)
2589 { //Non persistant PBI. Allow ESC to close it and return to mapscreen.
2590 KillPreBattleInterface();
2591 gpBattleGroup = NULL;
2592 return true;
2593 }
2594
2595 if (gfInChangeArrivalSectorMode)
2596 {
2597 CancelChangeArrivalSectorMode();
2598 BeginMapUIMessage(0, pBullseyeStrings[3]);
2599 }
2600 // ESC cancels MAP UI messages, unless we're in confirm map move mode
2601 else if (g_ui_message_overlay != NULL && !gfInConfirmMapMoveMode)
2602 {
2603 CancelMapUIMessage();
2604 }
2605 else if (gpCurrentTalkingFace != NULL && gpCurrentTalkingFace->fTalking)
2606 {
2607 // ATE: We want to stop speech if somebody is talking...
2608 StopAnyCurrentlyTalkingSpeech();
2609 }
2610 else if (fShowUpdateBox)
2611 {
2612 EndUpdateBox(FALSE); // stop time compression
2613 }
2614 else if(fShowDescriptionFlag)
2615 {
2616 DeleteItemDescriptionBox();
2617 }
2618 // plotting movement?
2619 else if (bSelectedDestChar != -1 || fPlotForHelicopter)
2620 {
2621 AbortMovementPlottingMode();
2622 }
2623 else if (fShowAssignmentMenu)
2624 {
2625 // dirty region
2626 fTeamPanelDirty = TRUE;
2627 fMapPanelDirty = TRUE;
2628 fCharacterInfoPanelDirty = TRUE;
2629
2630 // stop showing current assignment box
2631 if (fShowAttributeMenu)
2632 {
2633 fShowAttributeMenu = FALSE;
2634 fMapPanelDirty = TRUE;
2635 }
2636 else if (fShowTrainingMenu)
2637 {
2638 fShowTrainingMenu = FALSE;
2639 }
2640 else if (fShowSquadMenu)
2641 {
2642 fShowSquadMenu = FALSE;
2643 }
2644 else if (fShowRepairMenu)
2645 {
2646 fShowRepairMenu = FALSE;
2647 }
2648 else
2649 {
2650 fShowAssignmentMenu = FALSE;
2651 }
2652 giAssignHighLine = -1;
2653 // restore background to glow region
2654 RestoreBackgroundForAssignmentGlowRegionList();
2655 }
2656 else if (fShowContractMenu)
2657 {
2658 fShowContractMenu = FALSE;
2659
2660 // restore contract glow region
2661 RestoreBackgroundForContractGlowRegionList();
2662 fTeamPanelDirty = TRUE;
2663 fCharacterInfoPanelDirty = TRUE;
2664 giContractHighLine = -1;
2665 }
2666 // in militia popup?
2667 else if (sSelectedMilitiaTown != 0 && sGreensOnCursor == 0 && sRegularsOnCursor == 0 && sElitesOnCursor == 0)
2668 {
2669 sSelectedMilitiaTown = 0;
2670 fMapPanelDirty = TRUE;
2671 }
2672 else if (fShowTownInfo)
2673 {
2674 fShowTownInfo = FALSE;
2675 CreateDestroyScreenMaskForAssignmentAndContractMenus();
2676 }
2677 else if (InKeyRingPopup())
2678 {
2679 DeleteKeyRingPopup( );
2680 fTeamPanelDirty = TRUE;
2681 }
2682 else if (fShowInventoryFlag)
2683 {
2684 if (!fMapInventoryItem && !InItemStackPopup())
2685 {
2686 fEndShowInventoryFlag = TRUE;
2687 }
2688 }
2689 else if (MultipleCharacterListEntriesSelected())
2690 {
2691 ResetSelectedListForMapScreen();
2692 if (bSelectedInfoChar != -1)
2693 {
2694 SetEntryInSelectedCharacterList(bSelectedInfoChar);
2695 }
2696 fTeamPanelDirty = TRUE;
2697 fCharacterInfoPanelDirty = TRUE;
2698 }
2699 else
2700 {
2701 RequestTriggerExitFromMapscreen(MAP_EXIT_TO_TACTICAL);
2702 }
2703 return false;
2704 }
2705
2706
Teleport()2707 static void Teleport()
2708 {
2709 if (bSelectedDestChar == -1) return;
2710 if (fPlotForHelicopter) return;
2711 if (iCurrentMapSectorZ != 0) return;
2712
2713 INT16 sMapX;
2714 INT16 sMapY;
2715 if (!GetMouseMapXY(&sMapX, &sMapY)) return;
2716
2717 SOLDIERTYPE& s = *gCharactersList[bSelectedDestChar].merc;
2718
2719 // can't teleport to where we already are
2720 if (sMapX == s.sSectorX && sMapY == s.sSectorY) return;
2721
2722 // cancel movement plotting
2723 AbortMovementPlottingMode();
2724
2725 // nuke the UI message generated by this
2726 CancelMapUIMessage();
2727
2728 // clear their strategic movement (mercpaths and waypoints)
2729 ClearMvtForThisSoldierAndGang(&s);
2730
2731 // select this sector
2732 ChangeSelectedMapSector(sMapX, sMapY, 0);
2733
2734 // check to see if this person is moving, if not...then assign them to mvt group
2735 if (s.ubGroupID == 0)
2736 {
2737 GROUP& g = *CreateNewPlayerGroupDepartingFromSector(s.sSectorX, s.sSectorY);
2738 AddPlayerToGroup(g, s);
2739 }
2740
2741 // figure out where they would've come from
2742 INT16 const sDeltaX = sMapX - s.sSectorX;
2743 INT16 const sDeltaY = sMapY - s.sSectorY;
2744 INT16 sPrevX;
2745 INT16 sPrevY;
2746 if (ABS(sDeltaX) >= ABS(sDeltaY))
2747 {
2748 // use East or West
2749 if (sDeltaX > 0)
2750 {
2751 // came in from the West
2752 sPrevX = sMapX - 1;
2753 sPrevY = sMapY;
2754 }
2755 else
2756 {
2757 // came in from the East
2758 sPrevX = sMapX + 1;
2759 sPrevY = sMapY;
2760 }
2761 }
2762 else
2763 {
2764 // use North or South
2765 if (sDeltaY > 0)
2766 {
2767 // came in from the North
2768 sPrevX = sMapX;
2769 sPrevY = sMapY - 1;
2770 }
2771 else
2772 {
2773 // came in from the South
2774 sPrevX = sMapX;
2775 sPrevY = sMapY + 1;
2776 }
2777 }
2778
2779 // set where they are, were/are going, then make them arrive there and check for battle
2780 PlaceGroupInSector(*GetGroup(s.ubGroupID), sPrevX, sPrevY, sMapX, sMapY, 0, true);
2781
2782 // unload the sector they teleported out of
2783 CheckAndHandleUnloadingOfCurrentWorld();
2784 }
2785
2786
2787 static void ChangeCharacterListSortMethod(INT32 iValue);
2788 static void RequestContractMenu(void);
2789 static void RequestToggleMercInventoryPanel(void);
2790 static void SelectAllCharactersInSquad(INT8 bSquadNumber);
2791
2792
HandleModNone(UINT32 const key,MapEvent & new_event)2793 static void HandleModNone(UINT32 const key, MapEvent& new_event)
2794 {
2795 switch (key)
2796 {
2797 case SDLK_ESCAPE: if (HandleKeyESC()) return; break;
2798
2799 case SDLK_PAUSE: HandlePlayerPauseUnPauseOfGame(); break;
2800
2801 case SDLK_LEFT: GoToPrevCharacterInList(); break;
2802 case SDLK_RIGHT: GoToNextCharacterInList(); break;
2803
2804 case SDLK_UP: MapScreenMsgScrollUp(1); break;
2805 case SDLK_DOWN: MapScreenMsgScrollDown(1); break;
2806
2807 case SDLK_PAGEUP: MapScreenMsgScrollUp(MAX_MESSAGES_ON_MAP_BOTTOM); break;
2808 case SDLK_PAGEDOWN: MapScreenMsgScrollDown(MAX_MESSAGES_ON_MAP_BOTTOM); break;
2809
2810 case SDLK_HOME: ChangeCurrentMapscreenMessageIndex(0); break;
2811 case SDLK_END: MoveToEndOfMapScreenMessageList(); break;
2812
2813 case SDLK_INSERT: GoUpOneLevelInMap(); break;
2814 case SDLK_DELETE: GoDownOneLevelInMap(); break;
2815
2816 case SDLK_RETURN: RequestToggleMercInventoryPanel(); break;
2817
2818 case SDLK_BACKSPACE: StopAnyCurrentlyTalkingSpeech(); break;
2819
2820 case SDLK_F1:
2821 case SDLK_F2:
2822 case SDLK_F3:
2823 case SDLK_F4:
2824 case SDLK_F5:
2825 case SDLK_F6: ChangeCharacterListSortMethod(key - SDLK_F1); break;
2826
2827 case '+':
2828 case '=':
2829 if (!CommonTimeCompressionChecks()) RequestIncreaseInTimeCompression();
2830 break;
2831
2832 case '-':
2833 case '_':
2834 if (!CommonTimeCompressionChecks()) RequestDecreaseInTimeCompression();
2835 break;
2836
2837 case SDLK_SPACE:
2838 if (fShowUpdateBox)
2839 { // Restart time compression
2840 EndUpdateBox(TRUE);
2841 }
2842 else
2843 { // Toggle time compression
2844 if (!CommonTimeCompressionChecks()) RequestToggleTimeCompression();
2845 }
2846 break;
2847
2848 case '0':
2849 case '1':
2850 case '2':
2851 case '3':
2852 case '4':
2853 case '5':
2854 case '6':
2855 case '7':
2856 case '8':
2857 case '9':
2858 { // Select all characters in squad 1-10
2859 UINT const squad_no = (key - SDLK_0 + 9) % 10U;
2860 SelectAllCharactersInSquad(squad_no);
2861 break;
2862 }
2863
2864 case 'a':
2865 if (gfPreBattleInterfaceActive)
2866 { // Activate autoresolve in prebattle interface.
2867 ActivatePreBattleAutoresolveAction();
2868 }
2869 else if (!fShowMapInventoryPool) // only handle border button keyboard equivalents if the button is visible!
2870 {
2871 ToggleAirspaceMode();
2872 }
2873 break;
2874
2875 case 'c': RequestContractMenu(); break;
2876
2877 case 'e':
2878 // Activate enter sector in prebattle interface.
2879 if (gfPreBattleInterfaceActive) gfHotKeyEnterSector = TRUE;
2880 break;
2881
2882 case 'h':
2883 // ARM: Feb01/98 - Cancel out of mapscreen movement plotting if Help subscreen is coming up
2884 if (bSelectedDestChar != -1 || fPlotForHelicopter)
2885 {
2886 AbortMovementPlottingMode();
2887 }
2888 ShouldTheHelpScreenComeUp(HELP_SCREEN_MAPSCREEN, TRUE);
2889 break;
2890
2891 case 'i':
2892 // Only handle border button keyboard equivalents if the button is visible
2893 if (!fShowMapInventoryPool) ToggleItemsFilter();
2894 break;
2895
2896 case 'l': RequestTriggerExitFromMapscreen(MAP_EXIT_TO_LAPTOP); break;
2897
2898 case 'm':
2899 // Only handle border button keyboard equivalents if the button is visible
2900 if (!fShowMapInventoryPool) ToggleShowMinesMode();
2901 break;
2902
2903 case 'o': RequestTriggerExitFromMapscreen(MAP_EXIT_TO_OPTIONS); break;
2904
2905 case 'r':
2906 if (gfPreBattleInterfaceActive) ActivatePreBattleRetreatAction();
2907 break;
2908
2909 case 't':
2910 // Only handle border button keyboard equivalents if the button is visible
2911 if (!fShowMapInventoryPool) ToggleShowTeamsMode();
2912 break;
2913
2914 case 'v': DisplayGameSettings(); break;
2915
2916 case 'w':
2917 // Only handle border button keyboard equivalents if the button is visible
2918 if (!fShowMapInventoryPool) ToggleShowTownsMode();
2919 break;
2920
2921 case 'z':
2922 // Only handle border button keyboard equivalents if the button is visible
2923 if (!fShowMapInventoryPool) ToggleShowMilitiaMode();
2924 break;
2925 }
2926 }
2927
2928
HandleModShift(UINT const key)2929 static void HandleModShift(UINT const key)
2930 {
2931 switch (key)
2932 {
2933 case '0':
2934 case '1':
2935 case '2':
2936 case '3':
2937 case '4':
2938 case '5':
2939 case '6':
2940 case '7':
2941 case '8':
2942 case '9':
2943 { // Select all characters in squad 11-20
2944 UINT const squad_no = 10 + (key - SDLK_0 + 9) % 10U;
2945 SelectAllCharactersInSquad(squad_no);
2946 break;
2947 }
2948 }
2949 }
2950
2951
HandleModCtrl(UINT const key)2952 static void HandleModCtrl(UINT const key)
2953 {
2954 switch (key)
2955 {
2956 case 'a':
2957 if (CHEATER_CHEAT_LEVEL())
2958 {
2959 gfAutoAmbush ^= 1;
2960 if(gfAutoAmbush)
2961 {
2962 SLOGD("Enemy ambush test mode enabled.");
2963 }
2964 else
2965 {
2966 SLOGD("Enemy ambush test mode disabled.");
2967 }
2968 }
2969 break;
2970
2971 case 'i':
2972 if (gamepolicy(isHotkeyEnabled(UI_Map, HKMOD_CTRL, 'i')))
2973 {
2974 ToggleSectorInventory();
2975 }
2976 break;
2977
2978 case 'l':
2979 // go to LOAD screen
2980 gfSaveGame = FALSE;
2981 RequestTriggerExitFromMapscreen(MAP_EXIT_TO_LOAD);
2982 break;
2983
2984 case 's':
2985 // go to SAVE screen
2986 gfSaveGame = TRUE;
2987 RequestTriggerExitFromMapscreen(MAP_EXIT_TO_SAVE);
2988 break;
2989
2990 case 't': if (CHEATER_CHEAT_LEVEL()) Teleport(); break;
2991
2992 #if defined SGP_VIDEO_DEBUGGING
2993 case 'v':
2994 ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, ST::format("VObjects: {}", guiVObjectSize));
2995 ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, ST::format("VSurfaces: {}", guiVSurfaceSize));
2996 ScreenMsg(FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, "SGPVideoDump.txt updated...");
2997 PerformVideoInfoDumpIntoFile("SGPVideoDump.txt", TRUE);
2998 break;
2999 #endif
3000
3001 case 'z':
3002 if (CHEATER_CHEAT_LEVEL())
3003 {
3004 gfAutoAIAware ^= 1;
3005 if(gfAutoAIAware)
3006 {
3007 SLOGD("Strategic AI awareness maxed.");
3008 }
3009 else
3010 {
3011 SLOGD("Strategic AI awareness normal.");
3012 }
3013 }
3014 break;
3015 }
3016 }
3017
3018
HandleModAlt(UINT32 const key)3019 static void HandleModAlt(UINT32 const key)
3020 {
3021 switch (key)
3022 {
3023 case 'a':
3024 if (giHighLine != -1)
3025 {
3026 if (gCharactersList[giHighLine].merc)
3027 {
3028 bSelectedAssignChar = (INT8)giHighLine;
3029 RebuildAssignmentsBox();
3030 ChangeSelectedInfoChar((INT8)giHighLine, FALSE);
3031 fShowAssignmentMenu = TRUE;
3032 }
3033 }
3034 else if (GetSelectedInfoChar())
3035 {
3036 bSelectedAssignChar = bSelectedInfoChar;
3037 RebuildAssignmentsBox();
3038 fShowAssignmentMenu = TRUE;
3039 }
3040 break;
3041
3042 case 'f':
3043 if (INFORMATION_CHEAT_LEVEL())
3044 { // Toggle Frame Rate Display
3045 gbFPSDisplay = !gbFPSDisplay;
3046 EnableFPSOverlay(gbFPSDisplay);
3047 }
3048 break;
3049
3050 case 'l':
3051 // Although we're not actually going anywhere, we must still be in a state where this is permitted
3052 if (AllowedToExitFromMapscreenTo(MAP_EXIT_TO_LOAD)) DoQuickLoad();
3053 break;
3054
3055 case 's':
3056 // although we're not actually going anywhere, we must still be in a state where this is permitted
3057 if (AllowedToExitFromMapscreenTo(MAP_EXIT_TO_SAVE))
3058 {
3059 //if the game CAN be saved
3060 if (CanGameBeSaved())
3061 {
3062 guiPreviousOptionScreen = guiCurrentScreen;
3063 DoQuickSave();
3064 }
3065 else
3066 {
3067 //Display a message saying the player cant save now
3068 DoMapMessageBox(MSG_BOX_BASIC_STYLE, zNewTacticalMessages[TCTL_MSG__IRON_MAN_CANT_SAVE_NOW], MAP_SCREEN, MSG_BOX_FLAG_OK, 0);
3069 }
3070 }
3071 break;
3072
3073 case 'x': HandleShortCutExitState(); break;
3074 }
3075 }
3076
3077
GetMapKeyboardInput(MapEvent & new_event)3078 static void GetMapKeyboardInput(MapEvent& new_event)
3079 {
3080 InputAtom InputEvent;
3081 while (DequeueEvent(&InputEvent))
3082 {
3083 SGPPoint MousePos;
3084 GetMousePos(&MousePos);
3085 MouseSystemHook(InputEvent.usEvent, MousePos.iX, MousePos.iY);
3086
3087 if (InputEvent.usEvent == KEY_DOWN)
3088 {
3089 // if game is paused because of player, unpause with any key
3090 if (gfPauseDueToPlayerGamePause)
3091 {
3092 HandlePlayerPauseUnPauseOfGame();
3093 continue;
3094 }
3095
3096 // handle for fast help text for interface stuff
3097 if (IsTheInterfaceFastHelpTextActive())
3098 {
3099 ShutDownUserDefineHelpTextRegions();
3100 }
3101
3102 UINT32 const key = InputEvent.usParam;
3103 switch (InputEvent.usKeyState)
3104 {
3105 case 0: HandleModNone( key, new_event); break;
3106 case SHIFT_DOWN: HandleModShift(key); break;
3107 case CTRL_DOWN: HandleModCtrl( key); break;
3108 case ALT_DOWN: HandleModAlt( key); break;
3109 }
3110 }
3111 else if (InputEvent.usEvent == KEY_REPEAT)
3112 {
3113 switch (InputEvent.usParam)
3114 {
3115 case SDLK_LEFT: GoToPrevCharacterInList(); break;
3116 case SDLK_RIGHT: GoToNextCharacterInList(); break;
3117
3118 case SDLK_UP: MapScreenMsgScrollUp(1); break;
3119 case SDLK_DOWN: MapScreenMsgScrollDown(1); break;
3120
3121 case SDLK_PAGEUP: MapScreenMsgScrollUp(MAX_MESSAGES_ON_MAP_BOTTOM); break;
3122 case SDLK_PAGEDOWN: MapScreenMsgScrollDown(MAX_MESSAGES_ON_MAP_BOTTOM); break;
3123 }
3124 }
3125 }
3126 }
3127
3128
3129 static void DestroyMouseRegionsForTeamList(void);
3130 static void RemoveTeamPanelSortButtonsForMapScreen();
3131
3132
EndMapScreen(BOOLEAN fDuringFade)3133 void EndMapScreen( BOOLEAN fDuringFade )
3134 {
3135 if (!fInMapMode) return;
3136
3137 fLeavingMapScreen = FALSE;
3138
3139 SetRenderFlags( RENDER_FLAG_FULL );
3140 //gViewportRegion.Enable();
3141 //gRadarRegion.Enable();
3142 // ATE: Shutdown tactical interface panel
3143 // ShutdownCurrentPanel( );
3144
3145 // still plotting movement?
3146 if (bSelectedDestChar != -1 || fPlotForHelicopter)
3147 {
3148 AbortMovementPlottingMode( );
3149 }
3150
3151 DestroyMouseRegionsForTeamList( );
3152
3153 MSYS_RemoveRegion( &gMapViewRegion );
3154 MSYS_RemoveRegion( &gCharInfoFaceRegion);
3155 MSYS_RemoveRegion( &gCharInfoHandRegion );
3156 MSYS_RemoveRegion( &gMPanelRegion);
3157 MSYS_RemoveRegion( &gMapScreenMaskRegion );
3158 fInMapMode = FALSE;
3159
3160 // remove team panel sort button
3161 RemoveTeamPanelSortButtonsForMapScreen( );
3162
3163 // for th merc insurance help text
3164 CreateDestroyInsuranceMouseRegionForMercs( FALSE );
3165
3166 // gonna need to remove the screen mask regions
3167 CreateDestroyMouseRegionMasksForTimeCompressionButtons( );
3168
3169 RemoveButton( giMapContractButton );
3170
3171 HandleShutDownOfMapScreenWhileExternfaceIsTalking( );
3172
3173 fShowInventoryFlag = FALSE;
3174 CreateDestroyMapInvButton();
3175
3176 // no longer can we show assignments menu
3177 fShowAssignmentMenu = FALSE;
3178
3179 // clear out mouse regions for pop up boxes
3180 DetermineWhichAssignmentMenusCanBeShown( );
3181
3182 sSelectedMilitiaTown = 0;
3183 CreateDestroyMilitiaPopUPRegions( );
3184 CreateDestroyMilitiaSectorButtons( );
3185
3186 // stop showing contract menu
3187 fShowContractMenu = FALSE;
3188 // clear out contract menu
3189 DetermineIfContractMenuCanBeShown( );
3190 // remove contract pop up box (always created upon mapscreen entry)
3191 RemoveBox(ghContractBox);
3192 ghContractBox = NO_POPUP_BOX;
3193
3194 CreateDestroyAssignmentPopUpBoxes( );
3195
3196 // shutdown movement box
3197 if ( fShowMapScreenMovementList )
3198 {
3199 fShowMapScreenMovementList = FALSE;
3200 CreateDestroyMovementBox( 0, 0, 0 );
3201 }
3202
3203 // the remove merc from team box
3204 RemoveBox( ghRemoveMercAssignBox );
3205 ghRemoveMercAssignBox = NO_POPUP_BOX;
3206
3207 // clear screen mask if needed
3208 ClearScreenMaskForMapScreenExit( );
3209
3210 // get rid of pause clock area
3211 RemoveMouseRegionForPauseOfClock( );
3212
3213 // get rid of pop up for town info, if being shown
3214 fShowTownInfo = FALSE;
3215 CreateDestroyTownInfoBox( );
3216
3217 // build squad list
3218 RebuildCurrentSquad( );
3219
3220 //
3221 DeleteMouseRegionsForLevelMarkers( );
3222
3223 if (!fShowMapInventoryPool)
3224 {
3225 // delete buttons
3226 DeleteMapBorderButtons( );
3227 }
3228
3229 if(fShowDescriptionFlag)
3230 {
3231 DeleteItemDescriptionBox( );
3232 }
3233
3234
3235 fShowInventoryFlag = FALSE;
3236 CreateDestroyTrashCanRegion( );
3237
3238 if ( !fDuringFade )
3239 {
3240 MSYS_SetCurrentCursor(SCREEN_CURSOR);
3241 }
3242
3243 RemoveMapStatusBarsRegion( );
3244
3245 fShowUpdateBox = FALSE;
3246 CreateDestroyTheUpdateBox( );
3247
3248
3249 // get rid of mapscreen bottom
3250 DeleteMapScreenInterfaceBottom( );
3251
3252
3253 // shutdown any mapscreen UI overlay message
3254 CancelMapUIMessage( );
3255
3256
3257 CreateDestroyMapCharacterScrollButtons( );
3258
3259 // if time was ever compressed while we were in mapscreen
3260 if ( HasTimeCompressOccured( ) )
3261 {
3262 // make sure everything tactical got cleared out
3263 ClearTacticalStuffDueToTimeCompression();
3264 }
3265
3266
3267 CancelSectorInventoryDisplayIfOn( TRUE );
3268
3269
3270 SetAllAutoFacesInactive( );
3271 if(fLapTop)
3272 {
3273 StopAnyCurrentlyTalkingSpeech( );
3274 guiCurrentScreen=LAPTOP_SCREEN;
3275 }
3276 else
3277 {
3278 guiCurrentScreen = GAME_SCREEN;
3279
3280 // remove the progress bar
3281 RemoveProgressBar( 0 );
3282
3283 // enable scroll string video overlays
3284 EnableDisableScrollStringVideoOverlay( TRUE );
3285 }
3286
3287 // if going to tactical next
3288 if ( guiPendingScreen == GAME_SCREEN )
3289 {
3290 // set compressed mode to Normal (X1)
3291 SetGameTimeCompressionLevel( TIME_COMPRESS_X1 );
3292 }
3293 else // going to another screen (options, laptop, save/load)
3294 {
3295 StopTimeCompression();
3296 }
3297
3298 // update paused states, we are exiting...need to reset for any pathing or menus displayed
3299 UnLockPauseState( );
3300 UpdatePausedStatesDueToTimeCompression( );
3301
3302 if( !gfDontStartTransitionFromLaptop )
3303 {
3304 //Load a tiny graphic of the on screen and draw it to the buffer.
3305 PlayJA2SampleFromFile(SOUNDSDIR "/initial power up (8-11).wav", HIGHVOLUME, 1, MIDDLEPAN);
3306 BltVideoObjectOnce(FRAME_BUFFER, INTERFACEDIR "/laptopon.sti", 0, 465, 417);
3307 InvalidateRegion( 465, 417, 480, 427 );
3308 ExecuteBaseDirtyRectQueue( );
3309 EndFrameBufferRender( );
3310 RefreshScreen();
3311 }
3312
3313 //Kris: Removes the pre battle interface, but only if it exists.
3314 // It is internally considered.
3315 KillPreBattleInterface();
3316
3317 // cancel request if we somehow leave first
3318 gfRequestGiveSkyriderNewDestination = FALSE;
3319 }
3320
3321
3322 static BOOLEAN GetMapXY(INT16 sX, INT16 sY, INT16* psMapWorldX, INT16* psMapWorldY);
3323
3324
GetMouseMapXY(INT16 * psMapWorldX,INT16 * psMapWorldY)3325 BOOLEAN GetMouseMapXY(INT16* psMapWorldX, INT16* psMapWorldY)
3326 {
3327 SGPPoint MousePos;
3328 GetMousePos(&MousePos);
3329
3330 return GetMapXY(MousePos.iX, MousePos.iY, psMapWorldX, psMapWorldY);
3331 }
3332
3333
GetMapXY(INT16 sX,INT16 sY,INT16 * psMapWorldX,INT16 * psMapWorldY)3334 static BOOLEAN GetMapXY(INT16 sX, INT16 sY, INT16* psMapWorldX, INT16* psMapWorldY)
3335 {
3336 INT16 sMapX, sMapY;
3337
3338 // Subtract start of map view
3339 sMapX = sX - MAP_VIEW_START_X;//+2*MAP_GRID_X;
3340 sMapY = sY - MAP_VIEW_START_Y;
3341
3342 if ( sMapX < MAP_GRID_X || sMapY < MAP_GRID_Y )
3343 {
3344 return( FALSE );
3345 }
3346
3347 if ( sMapX < 0 || sMapY < 0 )
3348 {
3349 return( FALSE );
3350 }
3351
3352 if ( sMapX > MAP_VIEW_WIDTH+MAP_GRID_X-1 || sMapY > MAP_VIEW_HEIGHT+7/* +MAP_VIEW_HEIGHT */ )
3353 {
3354 return( FALSE );
3355 }
3356 if(sMapX < 1 || sMapY <1)
3357 {
3358 return (FALSE);
3359 }
3360
3361 *psMapWorldX = ( sMapX / MAP_GRID_X );
3362 *psMapWorldY = ( sMapY / MAP_GRID_Y );
3363
3364 return( TRUE );
3365 }
3366
3367
RenderMapHighlight(INT16 sMapX,INT16 sMapY,UINT16 usLineColor,BOOLEAN fStationary)3368 static void RenderMapHighlight(INT16 sMapX, INT16 sMapY, UINT16 usLineColor, BOOLEAN fStationary)
3369 {
3370 INT16 sScreenX, sScreenY;
3371
3372 Assert( ( sMapX >= 1 ) && ( sMapX <= 16 ) );
3373 Assert( ( sMapY >= 1 ) && ( sMapY <= 16 ) );
3374
3375 // if we are not allowed to highlight, leave
3376 if (!IsTheCursorAllowedToHighLightThisSector(sMapX, sMapY))
3377 {
3378 return;
3379 }
3380
3381 GetScreenXYFromMapXY( sMapX, sMapY, &sScreenX, &sScreenY );
3382
3383 // blit in the highlighted sector
3384 SGPVSurface::Lock l(FRAME_BUFFER);
3385 UINT16* const pDestBuf = l.Buffer<UINT16>();
3386 UINT32 const uiDestPitchBYTES = l.Pitch();
3387
3388 // clip to view region
3389 ClipBlitsToMapViewRegionForRectangleAndABit( uiDestPitchBYTES );
3390
3391 // draw rectangle for zoom out
3392 RectangleDraw( TRUE, sScreenX, sScreenY - 1, sScreenX + MAP_GRID_X, sScreenY + MAP_GRID_Y - 1, usLineColor, pDestBuf );
3393 InvalidateRegion( sScreenX, sScreenY - 2, sScreenX + DMAP_GRID_X + 1, sScreenY + DMAP_GRID_Y - 1 );
3394
3395 RestoreClipRegionToFullScreenForRectangle( uiDestPitchBYTES );
3396 }
3397
3398
3399 static BOOLEAN CheckIfClickOnLastSectorInPath(INT16 sX, INT16 sY);
3400 static void DestinationPlottingCompleted(void);
3401
3402
PollLeftButtonInMapView(MapEvent & new_event)3403 static void PollLeftButtonInMapView(MapEvent& new_event)
3404 {
3405 static BOOLEAN fLBBeenPressedInMapView = FALSE;
3406 INT16 sMapX, sMapY;
3407
3408 // if the mouse is currently over the MAP area
3409 if ( gMapViewRegion.uiFlags & MSYS_MOUSE_IN_AREA )
3410 {
3411 // if L-button is down at the moment
3412 if ( gMapViewRegion.ButtonState & MSYS_LEFT_BUTTON )
3413 {
3414 if ( !fLBBeenPressedInMapView )
3415 {
3416 fLBBeenPressedInMapView = TRUE;
3417 RESETCOUNTER( LMOUSECLICK_DELAY_COUNTER );
3418 }
3419 }
3420 else // L-button is NOT down at the moment
3421 {
3422 if ( fLBBeenPressedInMapView )
3423 {
3424 fLBBeenPressedInMapView = FALSE;
3425 RESETCOUNTER( LMOUSECLICK_DELAY_COUNTER );
3426
3427 // if in militia redistribution popup
3428 if ( sSelectedMilitiaTown != 0 )
3429 {
3430 // ignore clicks outside the box
3431 return;
3432 }
3433
3434 // left click cancels MAP UI messages, unless we're in confirm map move mode
3435 if (g_ui_message_overlay != NULL && !gfInConfirmMapMoveMode)
3436 {
3437 CancelMapUIMessage( );
3438
3439 // return unless moving the bullseye
3440 if ( !gfInChangeArrivalSectorMode )
3441 return;
3442 }
3443
3444 // ignore left clicks in the map screen if:
3445 // game just started or we're in the prebattle interface or if we are about to hit pre-battle
3446 if (DidGameJustStart() ||
3447 gfPreBattleInterfaceActive ||
3448 fDisableMapInterfaceDueToBattle)
3449 {
3450 return;
3451 }
3452
3453 // if in "plot route" mode
3454 if (bSelectedDestChar != -1 || fPlotForHelicopter)
3455 {
3456 fEndPlotting = FALSE;
3457
3458 GetMouseMapXY(&sMapX, &sMapY);
3459
3460 // if he clicked on the last sector in his current path
3461 if( CheckIfClickOnLastSectorInPath( sMapX, sMapY ) )
3462 {
3463 DestinationPlottingCompleted();
3464 }
3465 else // clicked on a new sector
3466 {
3467 // draw new map route
3468 new_event = MAP_EVENT_PLOT_PATH;
3469 }
3470 }
3471 else // not plotting movement
3472 {
3473 // if not plotting a path
3474 if (!fEndPlotting && !fJustFinishedPlotting)
3475 {
3476 // make this sector selected / trigger movement box / start helicopter plotting / changing arrival sector
3477 new_event = MAP_EVENT_CLICK_SECTOR;
3478 }
3479
3480 fEndPlotting = FALSE;
3481 }
3482
3483 // reset town info flag
3484 fShowTownInfo = FALSE;
3485 }
3486 }
3487 }
3488
3489 fJustFinishedPlotting = FALSE;
3490 }
3491
3492
3493 static void HandleMilitiaRedistributionClick(void);
3494
3495
PollRightButtonInMapView(MapEvent & new_event)3496 static void PollRightButtonInMapView(MapEvent& new_event)
3497 {
3498 static BOOLEAN fRBBeenPressedInMapView = FALSE;
3499 INT16 sMapX, sMapY;
3500
3501 // if the mouse is currently over the MAP area
3502 if ( gMapViewRegion.uiFlags & MSYS_MOUSE_IN_AREA )
3503 {
3504 // if R-button is down at the moment
3505 if ( gMapViewRegion.ButtonState & MSYS_RIGHT_BUTTON )
3506 {
3507 if ( !fRBBeenPressedInMapView )
3508 {
3509 fRBBeenPressedInMapView = TRUE;
3510 RESETCOUNTER( RMOUSECLICK_DELAY_COUNTER );
3511 }
3512 }
3513 else // R-button is NOT down at the moment
3514 {
3515 if ( fRBBeenPressedInMapView )
3516 {
3517 fRBBeenPressedInMapView = FALSE;
3518 RESETCOUNTER( RMOUSECLICK_DELAY_COUNTER );
3519
3520 // if in militia redistribution popup
3521 if ( sSelectedMilitiaTown != 0 )
3522 {
3523 // ignore clicks outside the box
3524 return;
3525 }
3526
3527 if ( gfInChangeArrivalSectorMode )
3528 {
3529 CancelChangeArrivalSectorMode( );
3530 BeginMapUIMessage(0, pBullseyeStrings[3]);
3531 return;
3532 }
3533
3534 // right click cancels MAP UI messages, unless we're in confirm map move mode
3535 if (g_ui_message_overlay != NULL && !gfInConfirmMapMoveMode)
3536 {
3537 CancelMapUIMessage( );
3538 return;
3539 }
3540
3541 // ignore right clicks in the map area if:
3542 // game just started or we're in the prebattle interface or if we are about to hit pre-battle
3543 if (DidGameJustStart() ||
3544 gfPreBattleInterfaceActive ||
3545 fDisableMapInterfaceDueToBattle)
3546 {
3547 return;
3548 }
3549
3550
3551 if (bSelectedDestChar != -1 || fPlotForHelicopter)
3552 {
3553 // cancel/shorten the path
3554 new_event = MAP_EVENT_CANCEL_PATH;
3555 }
3556 else
3557 {
3558 if ( GetMouseMapXY( &sMapX, &sMapY ) )
3559 {
3560 if( ( sSelMapX != sMapX ) || ( sSelMapY != sMapY ) )
3561 {
3562 ChangeSelectedMapSector( sMapX, sMapY, ( INT8 )iCurrentMapSectorZ );
3563 }
3564 }
3565
3566 // sector must be selected to bring up militia or town info boxes for it
3567 if ( ( sMapX == sSelMapX ) && ( sSelMapY == sMapY ) )
3568 {
3569 if (fShowMilitia)
3570 {
3571 HandleMilitiaRedistributionClick();
3572 }
3573 else // show militia is OFF
3574 {
3575 // if on the surface, or a real underground sector (we've visited it)
3576 if ( ( iCurrentMapSectorZ == 0 ) ||
3577 GetSectorFlagStatus(sMapX, sMapY, iCurrentMapSectorZ, SF_ALREADY_VISITED))
3578 {
3579 // toggle sector info for this sector
3580 fShowTownInfo = !fShowTownInfo;
3581 fMapPanelDirty = TRUE;
3582 }
3583 }
3584
3585 // fMapScreenBottomDirty = TRUE;
3586
3587 CreateDestroyScreenMaskForAssignmentAndContractMenus( );
3588 if (!fShowTownInfo)
3589 {
3590 // destroy town info box
3591 CreateDestroyTownInfoBox( );
3592 }
3593 }
3594 }
3595 }
3596 }
3597 }
3598 }
3599
3600
3601 static void MAPInvClickCallback(MOUSE_REGION* pRegion, INT32 iReason);
3602 static void MAPInvClickCamoCallback(MOUSE_REGION* pRegion, INT32 iReason);
3603 static void MAPInvMoveCallback(MOUSE_REGION* pRegion, INT32 iReason);
3604 static void MAPInvMoveCamoCallback(MOUSE_REGION* pRegion, INT32 iReason);
3605
3606
CreateDestroyMapInvButton()3607 void CreateDestroyMapInvButton()
3608 {
3609 static BOOLEAN fOldShowInventoryFlag=FALSE;
3610
3611 if( fShowInventoryFlag && !fOldShowInventoryFlag )
3612 {
3613 // create inventory button
3614 fOldShowInventoryFlag=TRUE;
3615 // disable allmouse regions in this space
3616 fTeamPanelDirty=TRUE;
3617
3618 INV_REGION_DESC gSCamoXY = {INV_BODY_X, INV_BODY_Y};
3619
3620 InitInvSlotInterface(g_ui.m_invSlotPositionMap, &gSCamoXY, MAPInvMoveCallback, MAPInvClickCallback, MAPInvMoveCamoCallback, MAPInvClickCamoCallback);
3621 gMPanelRegion.Enable();
3622
3623 // switch hand region help text to "Exit Inventory"
3624 gCharInfoHandRegion.SetFastHelpText(pMiscMapScreenMouseRegionHelpText[2]);
3625
3626 // dirty character info panel ( Why? ARM )
3627 fCharacterInfoPanelDirty=TRUE;
3628 }
3629 else if( !fShowInventoryFlag && fOldShowInventoryFlag )
3630 {
3631 // destroy inventory button
3632 ShutdownInvSlotInterface( );
3633 fOldShowInventoryFlag=FALSE;
3634 fTeamPanelDirty=TRUE;
3635 gMPanelRegion.Disable();
3636
3637 // switch hand region help text to "Enter Inventory"
3638 gCharInfoHandRegion.SetFastHelpText(pMiscMapScreenMouseRegionHelpText[0]);
3639
3640 // force immediate reblit of item in HANDPOS now that it's not blitted while in inventory mode
3641 fCharacterInfoPanelDirty=TRUE;
3642 }
3643 }
3644
3645
BltCharInvPanel(void)3646 static void BltCharInvPanel(void)
3647 {
3648 ST::string sString;
3649
3650 const SOLDIERTYPE* const pSoldier = GetSelectedInfoChar();
3651 Assert(pSoldier);
3652 Assert(MapCharacterHasAccessibleInventory(*pSoldier));
3653
3654 BltVideoObject(guiSAVEBUFFER, guiMAPINV, 0, PLAYER_INFO_X, PLAYER_INFO_Y);
3655
3656 CreateDestroyMapInvButton();
3657
3658 if( gbCheckForMouseOverItemPos != -1 )
3659 {
3660 if (HandleCompatibleAmmoUIForMapScreen(pSoldier, gbCheckForMouseOverItemPos, TRUE, TRUE))
3661 {
3662 fMapPanelDirty = TRUE;
3663 }
3664 }
3665
3666 if( ( fShowMapInventoryPool ) )
3667 {
3668 if( iCurrentlyHighLightedItem != -1 )
3669 {
3670 HandleCompatibleAmmoUIForMapScreen( pSoldier, (INT32)( iCurrentlyHighLightedItem + ( iCurrentInventoryPoolPage * MAP_INVENTORY_POOL_SLOT_COUNT ) ) , TRUE, FALSE );
3671 }
3672 }
3673
3674 RenderInvBodyPanel(pSoldier, INV_BODY_X, INV_BODY_Y );
3675
3676 // reset font destination buffer to the save buffer
3677 SetFontDestBuffer(guiSAVEBUFFER);
3678
3679 // render items in each of chars slots
3680 HandleRenderInvSlots(*pSoldier, DIRTYLEVEL2);
3681
3682 // Render Values for stats!
3683 SetFontAttributes(BLOCKFONT2, MAP_INV_STATS_TITLE_FONT_COLOR);
3684
3685 INT16 usX;
3686 INT16 usY;
3687
3688 // Display armor
3689 MPrint(MAP_ARMOR_LABEL_X, MAP_ARMOR_LABEL_Y, pInvPanelTitleStrings[0]);
3690 sString = ST::format("{3d}%", ArmourPercent(pSoldier));
3691 FindFontRightCoordinates(MAP_ARMOR_X, MAP_ARMOR_Y, MAP_ARMOR_W, MAP_ARMOR_H, sString, BLOCKFONT2, &usX, &usY);
3692 MPrint(usX, usY, sString);
3693
3694 // Display weight
3695 MPrint(MAP_WEIGHT_LABEL_X, MAP_WEIGHT_LABEL_Y, pInvPanelTitleStrings[1]);
3696 sString = ST::format("{}%", CalculateCarriedWeight(pSoldier));
3697 FindFontRightCoordinates(MAP_WEIGHT_X, MAP_WEIGHT_Y, MAP_WEIGHT_W, MAP_WEIGHT_H, sString, BLOCKFONT2, &usX, &usY);
3698 MPrint(usX, usY, sString);
3699
3700 // Display camouflage
3701 MPrint(MAP_CAMO_LABEL_X, MAP_CAMO_LABEL_Y, pInvPanelTitleStrings[2]);
3702 sString = ST::format("{}%", pSoldier->bCamo);
3703 FindFontRightCoordinates(MAP_CAMO_X, MAP_CAMO_Y, MAP_CAMO_W, MAP_CAMO_H, sString, BLOCKFONT2, &usX, &usY);
3704 MPrint(usX, usY, sString);
3705
3706 if( InKeyRingPopup() || InItemStackPopup() )
3707 {
3708 // shade the background
3709 guiSAVEBUFFER->ShadowRect(PLAYER_INFO_X, PLAYER_INFO_Y, PLAYER_INFO_X + 261, PLAYER_INFO_Y + (359 - 107));
3710 }
3711 else
3712 {
3713 // blit gold key on top of key ring if key ring is not empty
3714 }
3715
3716 SetFontDestBuffer(FRAME_BUFFER);
3717 }
3718
3719
3720 // check for and highlight any ammo
HandleCursorOverRifleAmmo(void)3721 static void HandleCursorOverRifleAmmo(void)
3722 {
3723 if (!fShowInventoryFlag) return;
3724
3725 if( gbCheckForMouseOverItemPos == -1 )
3726 {
3727 return;
3728 }
3729
3730 if ( gfCheckForMouseOverItem )
3731 {
3732 if (HandleCompatibleAmmoUI(GetSelectedInfoChar(), gbCheckForMouseOverItemPos, TRUE))
3733 {
3734 if ( ( GetJA2Clock( ) - guiMouseOverItemTime ) > 100 )
3735 {
3736 fTeamPanelDirty = TRUE;
3737 }
3738 }
3739 }
3740 }
3741
3742
MAPInvClickCamoCallback(MOUSE_REGION * pRegion,INT32 iReason)3743 static void MAPInvClickCamoCallback(MOUSE_REGION* pRegion, INT32 iReason)
3744 {
3745 }
3746
3747
MAPInvMoveCamoCallback(MOUSE_REGION * pRegion,INT32 iReason)3748 static void MAPInvMoveCamoCallback(MOUSE_REGION* pRegion, INT32 iReason)
3749 {
3750 }
3751
3752
3753 // this is Map Screen's version of SMInvMoveCallback()
MAPInvMoveCallback(MOUSE_REGION * pRegion,INT32 iReason)3754 static void MAPInvMoveCallback(MOUSE_REGION* pRegion, INT32 iReason)
3755 {
3756 UINT32 uiHandPos;
3757
3758 const SOLDIERTYPE* const pSoldier = GetSelectedInfoChar();
3759 Assert(MapCharacterHasAccessibleInventory(*pSoldier));
3760
3761 uiHandPos = MSYS_GetRegionUserData( pRegion, 0 );
3762
3763 //gbCheckForMouseOverItemPos = -1;
3764
3765 if ( pSoldier->inv[ uiHandPos ].usItem == NOTHING )
3766 return;
3767
3768 if (iReason == MSYS_CALLBACK_REASON_GAIN_MOUSE )
3769 //if( ( iReason == MSYS_CALLBACK_REASON_MOVE ) || ( iReason == MSYS_CALLBACK_REASON_GAIN_MOUSE ) )
3770 {
3771 guiMouseOverItemTime = GetJA2Clock( );
3772 gfCheckForMouseOverItem = TRUE;
3773 HandleCompatibleAmmoUI( pSoldier, (INT8)uiHandPos, FALSE );
3774 gbCheckForMouseOverItemPos = (INT8)uiHandPos;
3775 }
3776 if (iReason == MSYS_CALLBACK_REASON_LOST_MOUSE )
3777 {
3778 HandleCompatibleAmmoUI( pSoldier, (INT8)uiHandPos, FALSE );
3779 gfCheckForMouseOverItem = FALSE;
3780 fTeamPanelDirty = TRUE;
3781 gbCheckForMouseOverItemPos = -1;
3782 }
3783 }
3784
3785
MAPInternalInitItemDescriptionBox(OBJECTTYPE * pObject,UINT8 ubStatusIndex,SOLDIERTYPE * pSoldier)3786 void MAPInternalInitItemDescriptionBox(OBJECTTYPE* pObject, UINT8 ubStatusIndex, SOLDIERTYPE* pSoldier)
3787 {
3788 InternalInitItemDescriptionBox(pObject, MAP_ITEMDESC_START_X, MAP_ITEMDESC_START_Y, ubStatusIndex, pSoldier);
3789
3790 fShowDescriptionFlag=TRUE;
3791 fTeamPanelDirty=TRUE;
3792 fInterfacePanelDirty = DIRTYLEVEL2;
3793 }
3794
3795
3796 static void MAPBeginItemPointer(SOLDIERTYPE* pSoldier, UINT8 ubHandPos);
3797
3798
3799 // this is Map Screen's version of SMInvClickCallback()
MAPInvClickCallback(MOUSE_REGION * pRegion,INT32 iReason)3800 static void MAPInvClickCallback(MOUSE_REGION* pRegion, INT32 iReason)
3801 {
3802 UINT32 uiHandPos;
3803 UINT16 usOldItemIndex, usNewItemIndex;
3804 static BOOLEAN fRightDown = FALSE;
3805
3806 SOLDIERTYPE* const pSoldier = GetSelectedInfoChar();
3807 Assert(MapCharacterHasAccessibleInventory(*pSoldier));
3808
3809 uiHandPos = MSYS_GetRegionUserData( pRegion, 0 );
3810
3811 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
3812 {
3813 // If we do not have an item in hand, start moving it
3814 if ( gpItemPointer == NULL )
3815 {
3816 // Return if empty
3817 if ( pSoldier->inv[ uiHandPos ].usItem == NOTHING )
3818 {
3819 return;
3820 }
3821
3822 //ATE: Put this here to handle Nails refusal....
3823 if ( HandleNailsVestFetish( pSoldier, uiHandPos, NOTHING ) )
3824 {
3825 return;
3826 }
3827
3828 if ( _KeyDown(CTRL) )
3829 {
3830 CleanUpStack( &( pSoldier->inv[ uiHandPos ] ), NULL );
3831 }
3832
3833 // remember what it was
3834 usOldItemIndex = pSoldier->inv[ uiHandPos ].usItem;
3835
3836 // pick it up
3837 MAPBeginItemPointer( pSoldier, (UINT8)uiHandPos );
3838
3839 // remember which gridno the object came from
3840 sObjectSourceGridNo = pSoldier->sGridNo;
3841
3842 HandleTacticalEffectsOfEquipmentChange( pSoldier, uiHandPos, usOldItemIndex, NOTHING );
3843
3844 fInterfacePanelDirty = DIRTYLEVEL2;
3845 fCharacterInfoPanelDirty = TRUE;
3846 }
3847 else // item in cursor
3848 {
3849 // can we pass this part due to booby traps
3850 if (!ContinuePastBoobyTrapInMapScreen(gpItemPointer, pSoldier))
3851 {
3852 return;
3853 }
3854
3855 usOldItemIndex = pSoldier->inv[ uiHandPos ].usItem;
3856 usNewItemIndex = gpItemPointer->usItem;
3857
3858 //ATE: Put this here to handle Nails refusal....
3859 if ( HandleNailsVestFetish( pSoldier, uiHandPos, usNewItemIndex ) )
3860 {
3861 return;
3862 }
3863
3864 if ( _KeyDown(CTRL) )
3865 {
3866 CleanUpStack( &( pSoldier->inv[ uiHandPos ] ), gpItemPointer );
3867 if ( gpItemPointer->ubNumberOfObjects == 0 )
3868 {
3869 MAPEndItemPointer( );
3870 }
3871 return;
3872 }
3873
3874 // !!! ATTACHING/MERGING ITEMS IN MAP SCREEN IS NOT SUPPORTED !!!
3875 if ( uiHandPos == HANDPOS || uiHandPos == SECONDHANDPOS || uiHandPos == HELMETPOS || uiHandPos == VESTPOS || uiHandPos == LEGPOS )
3876 {
3877 //if ( ValidAttachmentClass( usNewItemIndex, usOldItemIndex ) )
3878 if ( ValidAttachment( usNewItemIndex, usOldItemIndex ) )
3879 {
3880 // it's an attempt to attach; bring up the inventory panel
3881 if ( !InItemDescriptionBox( ) )
3882 {
3883 MAPInternalInitItemDescriptionBox( &(pSoldier->inv[ uiHandPos ]), 0, pSoldier );
3884 }
3885 return;
3886 }
3887 else if ( ValidMerge( usNewItemIndex, usOldItemIndex ) )
3888 {
3889 // bring up merge requestor
3890 // TOO PAINFUL TO DO!! --CC
3891 if ( !InItemDescriptionBox( ) )
3892 {
3893 MAPInternalInitItemDescriptionBox( &(pSoldier->inv[ uiHandPos ]), 0, pSoldier );
3894 }
3895
3896 /*
3897 gubHandPos = (UINT8) uiHandPos;
3898 gusOldItemIndex = usOldItemIndex;
3899 gusNewItemIndex = usNewItemIndex;
3900 gfDeductPoints = fDeductPoints;
3901
3902 DoScreenIndependantMessageBox( Message[ STR_MERGE_ITEMS ], MSG_BOX_FLAG_YESNO, MergeMessageBoxCallBack );
3903 return;
3904 */
3905 }
3906 // else handle normally
3907 }
3908
3909 // Else, try to place here
3910 if ( PlaceObject( pSoldier, (UINT8)uiHandPos, gpItemPointer ) )
3911 {
3912
3913 HandleTacticalEffectsOfEquipmentChange( pSoldier, uiHandPos, usOldItemIndex, usNewItemIndex );
3914
3915 // Dirty
3916 fInterfacePanelDirty = DIRTYLEVEL2;
3917 fCharacterInfoPanelDirty = TRUE;
3918 fMapPanelDirty = TRUE;
3919
3920 // Check if cursor is empty now
3921 if ( gpItemPointer->ubNumberOfObjects == 0 )
3922 {
3923 MAPEndItemPointer( );
3924 }
3925 else // items swapped
3926 {
3927 SetMapCursorItem();
3928 fTeamPanelDirty=TRUE;
3929
3930 // remember which gridno the object came from
3931 sObjectSourceGridNo = pSoldier->sGridNo;
3932 // and who owned it last
3933 gpItemPointerSoldier = pSoldier;
3934
3935 ReevaluateItemHatches( pSoldier, FALSE );
3936 }
3937
3938 // re-evaluate repairs
3939 gfReEvaluateEveryonesNothingToDo = TRUE;
3940
3941 // if item came from another merc
3942 if ( gpItemPointerSoldier != pSoldier )
3943 {
3944 ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, st_format_printf(pMessageStrings[ MSG_ITEM_PASSED_TO_MERC ], ShortItemNames[ usNewItemIndex ], pSoldier->name) );
3945 }
3946
3947 }
3948 }
3949 }
3950 else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_DWN)
3951 {
3952 // if there is a map UI message being displayed
3953 if (g_ui_message_overlay != NULL)
3954 {
3955 CancelMapUIMessage( );
3956 return;
3957 }
3958
3959 fRightDown = TRUE;
3960 }
3961 else if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP && fRightDown )
3962 {
3963 fRightDown = FALSE;
3964
3965 // Return if empty
3966 if (pSoldier->inv[ uiHandPos ].usItem == NOTHING )
3967 {
3968 return;
3969 }
3970
3971 // Some global stuff here - for esc, etc
3972 // Check for # of slots in item
3973 if ( ( pSoldier->inv[ uiHandPos ].ubNumberOfObjects > 1 ) && ( ItemSlotLimit( pSoldier->inv[ uiHandPos ].usItem, (UINT8)uiHandPos ) > 0 ) )
3974 {
3975 if ( !InItemStackPopup( ) )
3976 {
3977 InitItemStackPopup( pSoldier, (UINT8)uiHandPos, INV_REGION_X, INV_REGION_Y, 261, 248 );
3978 fTeamPanelDirty=TRUE;
3979 fInterfacePanelDirty = DIRTYLEVEL2;
3980 }
3981 }
3982 else
3983 {
3984 if ( !InItemDescriptionBox( ) )
3985 {
3986 InitItemDescriptionBox( pSoldier, (UINT8)uiHandPos, MAP_ITEMDESC_START_X, MAP_ITEMDESC_START_Y, 0 );
3987 fShowDescriptionFlag=TRUE;
3988 fTeamPanelDirty=TRUE;
3989 fInterfacePanelDirty = DIRTYLEVEL2;
3990 }
3991 }
3992 }
3993 else if (iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
3994 {
3995 fRightDown = FALSE;
3996 }
3997 }
3998
3999
InternalMAPBeginItemPointer(SOLDIERTYPE * pSoldier)4000 void InternalMAPBeginItemPointer(SOLDIERTYPE* pSoldier)
4001 {
4002 // If not null return
4003 if ( gpItemPointer != NULL )
4004 {
4005 return;
4006 }
4007
4008 SetItemPointer(&gItemPointer, pSoldier);
4009 SetMapCursorItem();
4010 fTeamPanelDirty=TRUE;
4011
4012 // hatch out incompatible inventory slots
4013 ReevaluateItemHatches( pSoldier, FALSE );
4014
4015 // re-evaluate repairs
4016 gfReEvaluateEveryonesNothingToDo = TRUE;
4017 }
4018
4019
MAPBeginItemPointer(SOLDIERTYPE * pSoldier,UINT8 ubHandPos)4020 static void MAPBeginItemPointer(SOLDIERTYPE* pSoldier, UINT8 ubHandPos)
4021 {
4022 BOOLEAN fOk;
4023
4024 // If not null return
4025 if ( gpItemPointer != NULL )
4026 {
4027 return;
4028 }
4029
4030 if (_KeyDown( SHIFT ))
4031 {
4032 // Remove all from soldier's slot
4033 fOk = RemoveObjectFromSlot( pSoldier, ubHandPos, &gItemPointer );
4034 }
4035 else
4036 {
4037 GetObjFrom( &(pSoldier->inv[ubHandPos]), 0, &gItemPointer );
4038 fOk = (gItemPointer.ubNumberOfObjects == 1);
4039 }
4040
4041 if ( fOk )
4042 {
4043 InternalMAPBeginItemPointer( pSoldier );
4044 }
4045 }
4046
4047
MAPEndItemPointer(void)4048 void MAPEndItemPointer(void)
4049 {
4050 if ( gpItemPointer != NULL )
4051 {
4052 gpItemPointer = NULL;
4053 gMPanelRegion.ChangeCursor(CURSOR_NORMAL);
4054 MSYS_SetCurrentCursor( CURSOR_NORMAL );
4055 fMapInventoryItem=FALSE;
4056 fTeamPanelDirty=TRUE;
4057
4058 if ( fShowMapInventoryPool )
4059 {
4060 HandleButtonStatesWhileMapInventoryActive();
4061 }
4062
4063 if (fShowInventoryFlag)
4064 {
4065 SOLDIERTYPE* const s = GetSelectedInfoChar();
4066 if (s != NULL) ReevaluateItemHatches(s, FALSE);
4067 }
4068 }
4069 }
4070
4071
4072 // handle cursor for invenetory mode..update to object selected, if needed
HandleMapInventoryCursor(void)4073 static void HandleMapInventoryCursor(void)
4074 {
4075 if(fMapInventoryItem)
4076 MSYS_SetCurrentCursor( EXTERN_CURSOR );
4077 }
4078
4079
4080 // will place down the upper left hand corner attribute strings
RenderAttributeStringsForUpperLeftHandCorner(SGPVSurface * const uiBufferToRenderTo)4081 static void RenderAttributeStringsForUpperLeftHandCorner(SGPVSurface* const uiBufferToRenderTo)
4082 {
4083 INT32 iCounter = 0;
4084
4085 SetFontAttributes(CHAR_FONT, CHAR_TITLE_FONT_COLOR);
4086 SetFontDestBuffer(uiBufferToRenderTo);
4087
4088 // assignment strings
4089 DrawString(pUpperLeftMapScreenStrings[0], STD_SCREEN_X + 220 - StringPixLength(pUpperLeftMapScreenStrings[0], CHAR_FONT) / 2, STD_SCREEN_Y + 6, CHAR_FONT);
4090
4091 // vehicles and robot don't have attributes, contracts, or morale
4092 const SOLDIERTYPE* const pSoldier = GetSelectedInfoChar();
4093 if (!pSoldier || !IsMechanical(*pSoldier))
4094 {
4095 // health
4096 DrawString(pUpperLeftMapScreenStrings[1], STD_SCREEN_X + 87, STD_SCREEN_Y + 80, CHAR_FONT);
4097
4098 for( iCounter = 0; iCounter < 5; iCounter++ )
4099 {
4100 DrawString(pShortAttributeStrings[iCounter], STD_SCREEN_X + 88, STD_SCREEN_Y + 22 + iCounter * 10, CHAR_FONT);
4101 DrawString(pShortAttributeStrings[iCounter + 5], STD_SCREEN_X + 133, STD_SCREEN_Y + 22 + iCounter * 10, CHAR_FONT);
4102 }
4103
4104 // morale
4105 DrawString(pUpperLeftMapScreenStrings[2], STD_SCREEN_X + 87, STD_SCREEN_Y + 94, CHAR_FONT);
4106 }
4107 else
4108 {
4109 // condition
4110 DrawString(pUpperLeftMapScreenStrings[3], STD_SCREEN_X + 87, STD_SCREEN_Y + 80, CHAR_FONT);
4111 }
4112
4113
4114 // restore buffer
4115 SetFontDestBuffer(FRAME_BUFFER);
4116 }
4117
4118
DisplayThePotentialPathForCurrentDestinationCharacterForMapScreenInterface(INT16 sMapX,INT16 sMapY)4119 static void DisplayThePotentialPathForCurrentDestinationCharacterForMapScreenInterface(INT16 sMapX, INT16 sMapY)
4120 {
4121 // simply check if we want to refresh the screen to display path
4122 static INT8 bOldDestChar = -1;
4123 static INT16 sPrevMapX, sPrevMapY;
4124 INT32 iDifference = 0;
4125
4126
4127 if( bOldDestChar != bSelectedDestChar )
4128 {
4129 bOldDestChar = bSelectedDestChar;
4130 giPotCharPathBaseTime = GetJA2Clock( );
4131
4132 sPrevMapX = sMapX;
4133 sPrevMapY = sMapY;
4134 fTempPathAlreadyDrawn = FALSE;
4135 fDrawTempPath = FALSE;
4136
4137 }
4138
4139 if( ( sMapX != sPrevMapX) || ( sMapY != sPrevMapY ) )
4140 {
4141 giPotCharPathBaseTime = GetJA2Clock( );
4142
4143 sPrevMapX = sMapX;
4144 sPrevMapY = sMapY;
4145
4146 // path was plotted and we moved, re draw map..to clean up mess
4147 if (fTempPathAlreadyDrawn) fMapPanelDirty = TRUE;
4148
4149 fTempPathAlreadyDrawn = FALSE;
4150 fDrawTempPath = FALSE;
4151 }
4152
4153 iDifference = GetJA2Clock( ) - giPotCharPathBaseTime ;
4154
4155 if (fTempPathAlreadyDrawn) return;
4156
4157 if( iDifference > MIN_WAIT_TIME_FOR_TEMP_PATH )
4158 {
4159 fDrawTempPath = TRUE;
4160 giPotCharPathBaseTime = GetJA2Clock( );
4161 fTempPathAlreadyDrawn = TRUE;
4162 }
4163 }
4164
4165
4166 static void ChangeMapScreenMaskCursor(UINT16 usCursor);
4167
4168
SetUpCursorForStrategicMap(void)4169 void SetUpCursorForStrategicMap(void)
4170 {
4171 if (!gfInChangeArrivalSectorMode)
4172 {
4173 // check if character is in destination plotting mode
4174 if (!fPlotForHelicopter)
4175 {
4176 if( bSelectedDestChar == -1 )
4177 {
4178 // no plot mode, reset cursor to normal
4179 ChangeMapScreenMaskCursor( CURSOR_NORMAL );
4180 }
4181 else // yes - by character
4182 {
4183 // set cursor based on foot or vehicle
4184 const SOLDIERTYPE* const s = gCharactersList[bSelectedDestChar].merc;
4185 if (s->bAssignment != VEHICLE && !(s->uiStatusFlags & SOLDIER_VEHICLE))
4186 {
4187 ChangeMapScreenMaskCursor( CURSOR_STRATEGIC_FOOT );
4188 }
4189 else
4190 {
4191 ChangeMapScreenMaskCursor( CURSOR_STRATEGIC_VEHICLE );
4192 }
4193 }
4194 }
4195 else // yes - by helicopter
4196 {
4197 // set cursor to chopper
4198 ChangeMapScreenMaskCursor( CURSOR_CHOPPER );
4199 }
4200 }
4201 else
4202 {
4203 // set cursor to bullseye
4204 ChangeMapScreenMaskCursor( CURSOR_STRATEGIC_BULLSEYE );
4205 }
4206 }
4207
4208
HandleAnimatedCursorsForMapScreen(void)4209 static void HandleAnimatedCursorsForMapScreen(void)
4210 {
4211 if ( COUNTERDONE( CURSORCOUNTER ) )
4212 {
4213 RESETCOUNTER( CURSORCOUNTER );
4214 UpdateAnimatedCursorFrames( gMapScreenMaskRegion.Cursor );
4215 SetCurrentCursorFromDatabase( gMapScreenMaskRegion.Cursor );
4216 }
4217 }
4218
4219
4220 static void ClearPreviousPaths(void);
4221 static void RestorePreviousPaths(void);
4222
4223
AbortMovementPlottingMode(void)4224 void AbortMovementPlottingMode( void )
4225 {
4226 // invalid if we're not plotting movement
4227 Assert(bSelectedDestChar != -1 || fPlotForHelicopter);
4228
4229 // make everybody go back to where they were going before this plotting session started
4230 RestorePreviousPaths();
4231
4232 // don't need the previous paths any more
4233 ClearPreviousPaths();
4234
4235 // clear the character's temporary path (this is the route being constantly updated on the map)
4236 pTempCharacterPath = ClearStrategicPathList(pTempCharacterPath, 0);
4237
4238 // clear the helicopter's temporary path (this is the route being constantly updated on the map)
4239 pTempHelicopterPath = ClearStrategicPathList(pTempHelicopterPath, 0);
4240
4241 EndConfirmMapMoveMode( );
4242
4243 // cancel destination line highlight
4244 giDestHighLine = -1;
4245
4246 // cancel movement mode
4247 bSelectedDestChar = -1;
4248 fPlotForHelicopter = FALSE;
4249
4250 // tell player the route was UNCHANGED
4251 BeginMapUIMessage(0, pMapPlotStrings[2]);
4252
4253 // reset cursors
4254 ChangeMapScreenMaskCursor( CURSOR_NORMAL );
4255 SetUpCursorForStrategicMap( );
4256
4257 // restore glow region
4258 RestoreBackgroundForDestinationGlowRegionList( );
4259
4260 // we might be on the map, redraw to remove old path stuff
4261 fMapPanelDirty = TRUE;
4262 fTeamPanelDirty = TRUE;
4263
4264 gfRenderPBInterface = TRUE;
4265 }
4266
4267
CheckToSeeIfMouseHasLeftMapRegionDuringPathPlotting(void)4268 static void CheckToSeeIfMouseHasLeftMapRegionDuringPathPlotting(void)
4269 {
4270 static BOOLEAN fInArea = FALSE;
4271
4272 if ( ( gMapViewRegion.uiFlags & MSYS_MOUSE_IN_AREA ) == 0 )
4273 {
4274 if (fInArea)
4275 {
4276 fInArea = FALSE;
4277
4278 // plotting path, clean up
4279 if (fTempPathAlreadyDrawn &&
4280 (
4281 bSelectedDestChar != -1 ||
4282 fPlotForHelicopter ||
4283 fDrawTempHeliPath
4284 ))
4285 {
4286 fDrawTempHeliPath = FALSE;
4287 fMapPanelDirty = TRUE;
4288 gfRenderPBInterface = TRUE;
4289
4290 // clear the temp path
4291 pTempCharacterPath = ClearStrategicPathList(pTempCharacterPath, 0);
4292 }
4293
4294 // reset fact temp path has been drawn
4295 fTempPathAlreadyDrawn = FALSE;
4296 }
4297 }
4298 else
4299 {
4300 fInArea = TRUE;
4301 }
4302 }
4303
4304
4305 static void RenderCharacterInfoBackground(void);
4306
4307
BlitBackgroundToSaveBuffer(void)4308 static void BlitBackgroundToSaveBuffer(void)
4309 {
4310 // render map
4311 RenderMapRegionBackground( );
4312
4313 if (!fDisableDueToBattleRoster)
4314 {
4315 // render team
4316 RenderTeamRegionBackground( );
4317
4318 // render character info
4319 RenderCharacterInfoBackground( );
4320 }
4321 else if( gfPreBattleInterfaceActive )
4322 {
4323 ForceButtonUnDirty( giMapContractButton );
4324 ForceButtonUnDirty( giCharInfoButton[ 0 ] );
4325 ForceButtonUnDirty( giCharInfoButton[ 1 ] );
4326 RenderPreBattleInterface();
4327 }
4328
4329 // now render lower panel
4330 RenderMapScreenInterfaceBottom( );
4331 }
4332
4333
MakeRegion(MOUSE_REGION * r,UINT idx,UINT16 x,UINT16 y,UINT16 w,MOUSE_CALLBACK move,MOUSE_CALLBACK click,const ST::string & help)4334 static void MakeRegion(MOUSE_REGION* r, UINT idx, UINT16 x, UINT16 y, UINT16 w, MOUSE_CALLBACK move, MOUSE_CALLBACK click, const ST::string& help)
4335 {
4336 MSYS_DefineRegion(r, x, y, x + w, y + Y_SIZE + 1, MSYS_PRIORITY_NORMAL + 1, MSYS_NO_CURSOR, move, click);
4337 MSYS_SetRegionUserData(r, 0, idx);
4338 r->SetFastHelpText(help);
4339 }
4340
4341
4342 static void TeamListAssignmentRegionBtnCallBack(MOUSE_REGION* pRegion, INT32 iReason);
4343 static void TeamListAssignmentRegionMvtCallBack(MOUSE_REGION* pRegion, INT32 iReason);
4344 static void TeamListContractRegionBtnCallBack(MOUSE_REGION* pRegion, INT32 iReason);
4345 static void TeamListContractRegionMvtCallBack(MOUSE_REGION* pRegion, INT32 iReason);
4346 static void TeamListDestinationRegionBtnCallBack(MOUSE_REGION* pRegion, INT32 iReason);
4347 static void TeamListDestinationRegionMvtCallBack(MOUSE_REGION* pRegion, INT32 iReason);
4348 static void TeamListInfoRegionBtnCallBack(MOUSE_REGION* pRegion, INT32 iReason);
4349 static void TeamListInfoRegionMvtCallBack(MOUSE_REGION* pRegion, INT32 iReason);
4350 static void TeamListSleepRegionBtnCallBack(MOUSE_REGION* pRegion, INT32 iReason);
4351 static void TeamListSleepRegionMvtCallBack(MOUSE_REGION* pRegion, INT32 iReason);
4352
4353
CreateMouseRegionsForTeamList(void)4354 static void CreateMouseRegionsForTeamList(void)
4355 {
4356 // will create mouse regions for assignments, path plotting, character info selection
4357
4358 // the info region...is the background for the list itself
4359 for (UINT i = 0; i < MAX_CHARACTER_COUNT; ++i)
4360 {
4361 const UINT16 y = Y_START + i * (Y_SIZE + 2) + (i >= FIRST_VEHICLE ? 6 : 0);
4362
4363 const UINT16 w = NAME_WIDTH;
4364 CharacterRegions& r = g_character_regions[i];
4365 MakeRegion(&r.name, i, NAME_X, y, w, TeamListInfoRegionMvtCallBack, TeamListInfoRegionBtnCallBack, pMapScreenMouseRegionHelpText[0]); // name region
4366 MakeRegion(&r.assignment, i, ASSIGN_X, y, ASSIGN_WIDTH, TeamListAssignmentRegionMvtCallBack, TeamListAssignmentRegionBtnCallBack, pMapScreenMouseRegionHelpText[1]); // assignment region
4367 MakeRegion(&r.sleep, i, SLEEP_X, y, SLEEP_WIDTH, TeamListSleepRegionMvtCallBack, TeamListSleepRegionBtnCallBack, pMapScreenMouseRegionHelpText[5]); // sleep region
4368 // same function as name regions, so uses the same callbacks
4369 MakeRegion(&r.location, i, LOC_X, y, LOC_WIDTH, TeamListInfoRegionMvtCallBack, TeamListInfoRegionBtnCallBack, pMapScreenMouseRegionHelpText[0]); // location region
4370 MakeRegion(&r.destination, i, DEST_ETA_X, y, DEST_ETA_WIDTH, TeamListDestinationRegionMvtCallBack, TeamListDestinationRegionBtnCallBack, pMapScreenMouseRegionHelpText[2]); // destination region
4371 MakeRegion(&r.contract, i, TIME_REMAINING_X, y, TIME_REMAINING_WIDTH, TeamListContractRegionMvtCallBack, TeamListContractRegionBtnCallBack, pMapScreenMouseRegionHelpText[3]); // contract region
4372 }
4373 }
4374
4375
DestroyMouseRegionsForTeamList(void)4376 static void DestroyMouseRegionsForTeamList(void)
4377 {
4378 // will destroy mouse regions overlaying the team list area
4379 for (UINT i = 0; i < MAX_CHARACTER_COUNT; ++i)
4380 {
4381 CharacterRegions& r = g_character_regions[i];
4382 MSYS_RemoveRegion(&r.name);
4383 MSYS_RemoveRegion(&r.assignment);
4384 MSYS_RemoveRegion(&r.sleep);
4385 MSYS_RemoveRegion(&r.location);
4386 MSYS_RemoveRegion(&r.destination);
4387 MSYS_RemoveRegion(&r.contract);
4388 }
4389 }
4390
4391
MapScreenMarkRegionBtnCallback(MOUSE_REGION * pRegion,INT32 iReason)4392 static void MapScreenMarkRegionBtnCallback(MOUSE_REGION* pRegion, INT32 iReason)
4393 {
4394 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4395 {
4396 // reset selected characters
4397 ResetAllSelectedCharacterModes( );
4398 }
4399 if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
4400 {
4401 // reset selected characters
4402 ResetAllSelectedCharacterModes( );
4403 }
4404 }
4405
4406
ContractButtonCallback(GUI_BUTTON * btn,INT32 reason)4407 static void ContractButtonCallback(GUI_BUTTON* btn, INT32 reason)
4408 {
4409 if (g_dialogue_box) return;
4410
4411 if (reason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
4412 {
4413 #if 0 // XXX was commented out
4414 if (bSelectedDestChar != -1 || fPlotForHelicopter)
4415 {
4416 AbortMovementPlottingMode();
4417 return;
4418 }
4419 #endif
4420
4421 // redraw region
4422 if (btn->Area.uiFlags & MSYS_HAS_BACKRECT) fCharacterInfoPanelDirty = TRUE;
4423 }
4424 else if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4425 {
4426 RequestContractMenu();
4427 }
4428 }
4429
4430
4431 static BOOLEAN HandleCtrlOrShiftInTeamPanel(INT8 bCharNumber);
4432
4433
TeamListInfoRegionBtnCallBack(MOUSE_REGION * pRegion,INT32 iReason)4434 static void TeamListInfoRegionBtnCallBack(MOUSE_REGION* pRegion, INT32 iReason)
4435 {
4436 INT32 iValue = 0;
4437
4438 if( fLockOutMapScreenInterface || gfPreBattleInterfaceActive )
4439 {
4440 return;
4441 }
4442
4443 iValue = MSYS_GetRegionUserData( pRegion, 0 );
4444
4445 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4446 {
4447 // set to new info character...make sure is valid
4448 const SOLDIERTYPE* const pSoldier = gCharactersList[iValue].merc;
4449 if (pSoldier != NULL)
4450 {
4451 if ( HandleCtrlOrShiftInTeamPanel( ( INT8 ) iValue ) )
4452 {
4453 return;
4454 }
4455
4456 ChangeSelectedInfoChar( ( INT8 ) iValue, TRUE );
4457
4458 // highlight
4459 giDestHighLine = -1;
4460
4461 // reset character
4462 bSelectedAssignChar = -1;
4463 bSelectedDestChar = -1;
4464 bSelectedContractChar = -1;
4465 fPlotForHelicopter = FALSE;
4466
4467 // if not dead or POW, select his sector
4468 if( ( pSoldier->bLife > 0 ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) )
4469 {
4470 ChangeSelectedMapSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
4471 }
4472
4473 // unhilight contract line
4474 giContractHighLine = -1;
4475
4476 // can't assign highlight line
4477 giAssignHighLine = -1;
4478
4479 // dirty team and map regions
4480 fTeamPanelDirty = TRUE;
4481 fMapPanelDirty = TRUE;
4482 //fMapScreenBottomDirty = TRUE;
4483 gfRenderPBInterface = TRUE;
4484 }
4485 else
4486 {
4487 // reset selected characters
4488 ResetAllSelectedCharacterModes( );
4489 }
4490 }
4491
4492 if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
4493 {
4494 ResetAllSelectedCharacterModes();
4495
4496 const SOLDIERTYPE* const pSoldier = gCharactersList[iValue].merc;
4497 if (pSoldier != NULL)
4498 {
4499 // select this character
4500 ChangeSelectedInfoChar( ( INT8 ) iValue, TRUE );
4501
4502
4503 RequestToggleMercInventoryPanel();
4504
4505 // if not dead or POW, select his sector
4506 if( ( pSoldier->bLife > 0 ) && ( pSoldier->bAssignment != ASSIGNMENT_POW ) )
4507 {
4508 ChangeSelectedMapSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
4509 }
4510 }
4511 }
4512 }
4513
4514
TeamListInfoRegionMvtCallBack(MOUSE_REGION * pRegion,INT32 iReason)4515 static void TeamListInfoRegionMvtCallBack(MOUSE_REGION* pRegion, INT32 iReason)
4516 {
4517 INT32 iValue = 0;
4518
4519
4520 if( fLockOutMapScreenInterface || gfPreBattleInterfaceActive )
4521 {
4522 return;
4523 }
4524
4525 iValue = MSYS_GetRegionUserData( pRegion, 0 );
4526
4527 if (iReason & MSYS_CALLBACK_REASON_MOVE)
4528 {
4529 giHighLine = (gCharactersList[iValue].merc != NULL ? iValue : -1);
4530 }
4531 else if( iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
4532 {
4533 giHighLine = -1;
4534 }
4535 }
4536
4537
TeamListAssignmentRegionBtnCallBack(MOUSE_REGION * pRegion,INT32 iReason)4538 static void TeamListAssignmentRegionBtnCallBack(MOUSE_REGION* pRegion, INT32 iReason)
4539 {
4540 INT32 iValue = 0;
4541
4542 if( fLockOutMapScreenInterface || gfPreBattleInterfaceActive )
4543 {
4544 return;
4545 }
4546
4547 iValue = MSYS_GetRegionUserData( pRegion, 0 );
4548
4549 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4550 {
4551 // set to new info character...make sure is valid
4552 const SOLDIERTYPE* const pSoldier = gCharactersList[iValue].merc;
4553 if (pSoldier != NULL)
4554 {
4555 if ( HandleCtrlOrShiftInTeamPanel( ( INT8 ) iValue ) )
4556 {
4557 return;
4558 }
4559
4560 // reset list if the clicked character isn't also selected
4561 ChangeSelectedInfoChar(iValue, !IsEntryInSelectedListSet(iValue));
4562
4563 // if alive (dead guys keep going, use remove menu instead),
4564 // and it's between sectors and it can be reassigned (non-vehicles)
4565 if ( ( pSoldier->bAssignment != ASSIGNMENT_DEAD ) && ( pSoldier->bLife > 0 ) && ( pSoldier->fBetweenSectors ) && !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
4566 {
4567 // can't reassign mercs while between sectors
4568 DoScreenIndependantMessageBox( pMapErrorString[ 41 ], MSG_BOX_FLAG_OK, NULL );
4569 return;
4570 }
4571
4572 bSelectedAssignChar = ( INT8 ) iValue;
4573 RebuildAssignmentsBox( );
4574
4575 // reset dest character
4576 bSelectedDestChar = -1;
4577 fPlotForHelicopter = FALSE;
4578
4579 // reset contract char
4580 bSelectedContractChar = -1;
4581 giContractHighLine = -1;
4582
4583 // can't highlight line, anymore..if we were
4584 giDestHighLine = -1;
4585
4586 // dirty team and map regions
4587 fTeamPanelDirty = TRUE;
4588 fMapPanelDirty = TRUE;
4589 gfRenderPBInterface = TRUE;
4590
4591 // if this thing can be re-assigned
4592 if( !( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
4593 {
4594 giAssignHighLine = iValue;
4595
4596 fShowAssignmentMenu = TRUE;
4597 }
4598 else
4599 {
4600 // can't highlight line
4601 giAssignHighLine = -1;
4602
4603 // we can't highlight this line
4604 // giHighLine = -1;
4605 }
4606 }
4607 else
4608 {
4609 // reset selected characters
4610 ResetAllSelectedCharacterModes( );
4611 }
4612 }
4613
4614 if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
4615 {
4616 // reset selected characters
4617 ResetAllSelectedCharacterModes( );
4618 }
4619 }
4620
4621
TeamListAssignmentRegionMvtCallBack(MOUSE_REGION * pRegion,INT32 iReason)4622 static void TeamListAssignmentRegionMvtCallBack(MOUSE_REGION* pRegion, INT32 iReason)
4623 {
4624 INT32 iValue = 0;
4625
4626
4627 if( fLockOutMapScreenInterface || gfPreBattleInterfaceActive )
4628 {
4629 return;
4630 }
4631
4632 iValue = MSYS_GetRegionUserData( pRegion, 0 );
4633
4634 if (iReason & MSYS_CALLBACK_REASON_MOVE)
4635 {
4636 const SOLDIERTYPE* const s = gCharactersList[iValue].merc;
4637 if (s != NULL)
4638 {
4639 giHighLine = iValue;
4640
4641 if (!(s->uiStatusFlags & SOLDIER_VEHICLE))
4642 {
4643 giAssignHighLine = iValue;
4644 }
4645 else
4646 {
4647 giAssignHighLine = -1;
4648 }
4649 }
4650 else
4651 {
4652 // can't highlight line
4653 giHighLine = -1;
4654 giAssignHighLine = -1;
4655 }
4656 }
4657 else if( iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
4658 {
4659 giHighLine = -1;
4660
4661 if( bSelectedAssignChar == -1 )
4662 {
4663 giAssignHighLine = -1;
4664 }
4665
4666 // restore background
4667 RestoreBackgroundForAssignmentGlowRegionList( );
4668 }
4669 else if( iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE )
4670 {
4671 const SOLDIERTYPE* const s = gCharactersList[iValue].merc;
4672 if (s != NULL && !(s->uiStatusFlags & SOLDIER_VEHICLE))
4673 {
4674 // play click
4675 PlayGlowRegionSound( );
4676 }
4677 }
4678 }
4679
4680
4681 static bool CanChangeDestinationForChar(SOLDIERTYPE&);
4682 static void MakeMapModesSuitableForDestPlotting(const SOLDIERTYPE*);
4683
4684
TeamListDestinationRegionBtnCallBack(MOUSE_REGION * pRegion,INT32 iReason)4685 static void TeamListDestinationRegionBtnCallBack(MOUSE_REGION* pRegion, INT32 iReason)
4686 {
4687 INT32 iValue = 0;
4688
4689
4690 if( fLockOutMapScreenInterface || gfPreBattleInterfaceActive || fShowMapInventoryPool )
4691 {
4692 return;
4693 }
4694
4695 iValue = MSYS_GetRegionUserData( pRegion, 0 );
4696
4697 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4698 {
4699 SOLDIERTYPE* const s = gCharactersList[iValue].merc;
4700 if (s != NULL)
4701 {
4702 if ( HandleCtrlOrShiftInTeamPanel( ( INT8 ) iValue ) )
4703 {
4704 return;
4705 }
4706
4707 // reset list if the clicked character isn't also selected
4708 ChangeSelectedInfoChar(iValue, !IsEntryInSelectedListSet(iValue));
4709
4710 // deselect any characters/vehicles that can't accompany the clicked merc
4711 DeselectSelectedListMercsWhoCantMoveWithThisGuy(s);
4712
4713 // select all characters/vehicles that MUST accompany the clicked merc (same squad/vehicle)
4714 SelectUnselectedMercsWhoMustMoveWithThisGuy( );
4715
4716 // Find out if this guy and everyone travelling with him is allowed to move strategically
4717 // NOTE: errors are reported within...
4718 if (CanChangeDestinationForChar(*s))
4719 {
4720 // turn off sector inventory, turn on show teams filter, etc.
4721 MakeMapModesSuitableForDestPlotting(s);
4722
4723 if (InHelicopter(*s))
4724 {
4725 TurnOnAirSpaceMode();
4726 if (!RequestGiveSkyriderNewDestination())
4727 {
4728 // not allowed to change destination of the helicopter
4729 return;
4730 }
4731 }
4732
4733 // select this character as the one plotting strategic movement
4734 bSelectedDestChar = ( INT8 )iValue;
4735
4736 // remember the current paths for all selected characters so we can restore them if need be
4737 RememberPreviousPathForAllSelectedChars();
4738
4739 // highlight
4740 giDestHighLine = iValue;
4741
4742 // can't assign highlight line
4743 giAssignHighLine = -1;
4744
4745 // reset assign character
4746 bSelectedAssignChar = -1;
4747
4748 // reset contract char
4749 bSelectedContractChar = -1;
4750 giContractHighLine = -1;
4751
4752 // dirty team and map regions
4753 fTeamPanelDirty = TRUE;
4754 fMapPanelDirty = TRUE;
4755 gfRenderPBInterface = TRUE;
4756
4757
4758 // set cursor
4759 SetUpCursorForStrategicMap( );
4760 }
4761 else // problem - this guy can't move
4762 {
4763 // cancel destination highlight
4764 giDestHighLine = -1;
4765 }
4766 }
4767 else // empty char list slot
4768 {
4769 // reset selected characters
4770 ResetAllSelectedCharacterModes( );
4771 }
4772 }
4773
4774 if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
4775 {
4776 CancelPathsOfAllSelectedCharacters();
4777 ResetAllSelectedCharacterModes();
4778 }
4779 }
4780
4781
TeamListDestinationRegionMvtCallBack(MOUSE_REGION * pRegion,INT32 iReason)4782 static void TeamListDestinationRegionMvtCallBack(MOUSE_REGION* pRegion, INT32 iReason)
4783 {
4784 INT32 iValue = -1;
4785
4786
4787 if( fLockOutMapScreenInterface || gfPreBattleInterfaceActive )
4788 {
4789 return;
4790 }
4791
4792 iValue = MSYS_GetRegionUserData( pRegion, 0 );
4793
4794 if (iReason & MSYS_CALLBACK_REASON_MOVE)
4795 {
4796 if (gCharactersList[iValue].merc != NULL)
4797 {
4798 giHighLine = iValue;
4799 giDestHighLine = iValue;
4800 }
4801 else
4802 {
4803 // can't highlight line
4804 giHighLine = -1;
4805 giDestHighLine = -1;
4806 }
4807 }
4808 else if( iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
4809 {
4810 giHighLine = -1;
4811
4812 if( bSelectedDestChar == -1 )
4813 {
4814 giDestHighLine = -1;
4815 }
4816
4817 // restore background
4818 RestoreBackgroundForDestinationGlowRegionList( );
4819 }
4820 else if( iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE )
4821 {
4822 if (gCharactersList[iValue].merc != NULL)
4823 {
4824 // play click
4825 PlayGlowRegionSound( );
4826 }
4827 }
4828 }
4829
4830
TeamListSleepRegionBtnCallBack(MOUSE_REGION * pRegion,INT32 iReason)4831 static void TeamListSleepRegionBtnCallBack(MOUSE_REGION* pRegion, INT32 iReason)
4832 {
4833 INT32 iValue = 0;
4834
4835 if( fLockOutMapScreenInterface || gfPreBattleInterfaceActive )
4836 {
4837 return;
4838 }
4839
4840 iValue = MSYS_GetRegionUserData( pRegion, 0 );
4841
4842 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4843 {
4844 // set to new info character...make sure is valid.. not in transit and alive and concious
4845 SOLDIERTYPE* const pSoldier = gCharactersList[iValue].merc;
4846 if (pSoldier != NULL)
4847 {
4848 if ( HandleCtrlOrShiftInTeamPanel( ( INT8 ) iValue ) )
4849 {
4850 return;
4851 }
4852
4853 // reset list if the clicked character isn't also selected
4854 ChangeSelectedInfoChar(iValue, !IsEntryInSelectedListSet(iValue));
4855
4856 if (CanChangeSleepStatusForSoldier(pSoldier))
4857 {
4858 if (pSoldier->fMercAsleep)
4859 {
4860 // try to wake him up
4861 if( SetMercAwake( pSoldier, TRUE, FALSE ) )
4862 {
4863 // propagate
4864 HandleSelectedMercsBeingPutAsleep( TRUE, TRUE );
4865 return;
4866 }
4867 else
4868 {
4869 HandleSelectedMercsBeingPutAsleep( TRUE, FALSE );
4870 }
4871 }
4872 else // awake
4873 {
4874 // try to put him to sleep
4875 if (SetMercAsleep(*pSoldier, true))
4876 {
4877 // propagate
4878 HandleSelectedMercsBeingPutAsleep( FALSE, TRUE );
4879 return;
4880 }
4881 else
4882 {
4883 HandleSelectedMercsBeingPutAsleep( FALSE, FALSE );
4884 }
4885 }
4886 }
4887 }
4888 else
4889 {
4890 // reset selected characters
4891 ResetAllSelectedCharacterModes( );
4892 }
4893 }
4894
4895 if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
4896 {
4897 // reset selected characters
4898 ResetAllSelectedCharacterModes( );
4899 }
4900 }
4901
4902
TeamListSleepRegionMvtCallBack(MOUSE_REGION * pRegion,INT32 iReason)4903 static void TeamListSleepRegionMvtCallBack(MOUSE_REGION* pRegion, INT32 iReason)
4904 {
4905 INT32 iValue = -1;
4906
4907
4908 if( fLockOutMapScreenInterface || gfPreBattleInterfaceActive )
4909 {
4910 return;
4911 }
4912
4913 iValue = MSYS_GetRegionUserData( pRegion, 0 );
4914
4915 if (iReason & MSYS_CALLBACK_REASON_MOVE)
4916 {
4917 const SOLDIERTYPE* const s = gCharactersList[iValue].merc;
4918 if (s != NULL)
4919 {
4920 giHighLine = iValue;
4921 giSleepHighLine = (CanChangeSleepStatusForSoldier(s) ? iValue : -1);
4922 }
4923 else
4924 {
4925 // can't highlight line
4926 giHighLine = -1;
4927 giSleepHighLine = -1;
4928 }
4929 }
4930 else if( iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
4931 {
4932 giHighLine = -1;
4933 giSleepHighLine = -1;
4934
4935 // restore background
4936 RestoreBackgroundForSleepGlowRegionList( );
4937 }
4938 else if( iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE )
4939 {
4940 const SOLDIERTYPE* const s = gCharactersList[iValue].merc;
4941 if (s != NULL && CanChangeSleepStatusForSoldier(s)) PlayGlowRegionSound();
4942 }
4943 }
4944
4945
4946 static void ContractRegionBtnCallback(MOUSE_REGION* pRegion, INT32 iReason);
4947
4948
TeamListContractRegionBtnCallBack(MOUSE_REGION * pRegion,INT32 iReason)4949 static void TeamListContractRegionBtnCallBack(MOUSE_REGION* pRegion, INT32 iReason)
4950 {
4951 INT32 iValue = 0;
4952
4953
4954 if( fLockOutMapScreenInterface || gfPreBattleInterfaceActive )
4955 {
4956 return;
4957 }
4958
4959 iValue = MSYS_GetRegionUserData( pRegion, 0 );
4960
4961 if (gCharactersList[iValue].merc != NULL)
4962 {
4963 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
4964 {
4965 // select ONLY this dude
4966 ChangeSelectedInfoChar( ( INT8 ) iValue, TRUE );
4967
4968 // reset character
4969 giDestHighLine = -1;
4970 bSelectedAssignChar = -1;
4971 bSelectedDestChar = -1;
4972 bSelectedContractChar = -1;
4973 fPlotForHelicopter = FALSE;
4974
4975 fTeamPanelDirty = TRUE;
4976 }
4977
4978 ContractRegionBtnCallback( pRegion, iReason );
4979 }
4980
4981 if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
4982 {
4983 // reset selected characters
4984 ResetAllSelectedCharacterModes( );
4985 }
4986 }
4987
4988
TeamListContractRegionMvtCallBack(MOUSE_REGION * pRegion,INT32 iReason)4989 static void TeamListContractRegionMvtCallBack(MOUSE_REGION* pRegion, INT32 iReason)
4990 {
4991 INT32 iValue = -1;
4992
4993
4994 if( fLockOutMapScreenInterface || gfPreBattleInterfaceActive )
4995 {
4996 return;
4997 }
4998
4999 iValue = MSYS_GetRegionUserData( pRegion, 0 );
5000
5001 if (iReason & MSYS_CALLBACK_REASON_MOVE)
5002 {
5003 const SOLDIERTYPE* const s = gCharactersList[iValue].merc;
5004 if (s != NULL)
5005 {
5006 giHighLine = iValue;
5007 giContractHighLine = (CanExtendContractForSoldier(s) ? iValue : -1);
5008 }
5009 else
5010 {
5011 // can't highlight line
5012 giHighLine = -1;
5013 giContractHighLine = -1;
5014 }
5015 }
5016 else if( iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
5017 {
5018 giHighLine = -1;
5019
5020 // no longer valid char?...reset display of highlight boxes
5021 if (!fShowContractMenu) giContractHighLine = -1;
5022
5023 // restore background
5024 RestoreBackgroundForContractGlowRegionList( );
5025 }
5026 else if( iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE )
5027 {
5028 const SOLDIERTYPE* const s = gCharactersList[iValue].merc;
5029 if (s != NULL && CanExtendContractForSoldier(s)) PlayGlowRegionSound();
5030 }
5031 }
5032
5033
HandleHighLightingOfLinesInTeamPanel(void)5034 static void HandleHighLightingOfLinesInTeamPanel(void)
5035 {
5036 if ( fShowInventoryFlag )
5037 {
5038 return;
5039 }
5040
5041 // will highlight or restore backgrounds to highlighted lines
5042
5043 // restore backgrounds, if need be
5044 RestoreBackgroundForAssignmentGlowRegionList( );
5045 RestoreBackgroundForDestinationGlowRegionList( );
5046 RestoreBackgroundForContractGlowRegionList( );
5047 RestoreBackgroundForSleepGlowRegionList( );
5048
5049 HighLightAssignLine();
5050 HighLightDestLine();
5051 HighLightSleepLine();
5052
5053 // contracts?
5054 if( giContractHighLine != -1 )
5055 {
5056 ContractListRegionBoxGlow( ( UINT16 ) giContractHighLine );
5057 }
5058 }
5059
5060
PlotPermanentPaths(void)5061 static void PlotPermanentPaths(void)
5062 {
5063 if (fPlotForHelicopter)
5064 {
5065 DisplayHelicopterPath( );
5066 }
5067 else if( bSelectedDestChar != -1 )
5068 {
5069 DisplaySoldierPath(gCharactersList[bSelectedDestChar].merc);
5070 }
5071 }
5072
5073
PlotTemporaryPaths(void)5074 static void PlotTemporaryPaths(void)
5075 {
5076 INT16 sMapX, sMapY;
5077
5078
5079 // check to see if we have in fact moved are are plotting a path?
5080 if ( GetMouseMapXY( &sMapX, &sMapY ) )
5081 {
5082 if (fPlotForHelicopter)
5083 {
5084 Assert(fShowAircraftFlag);
5085
5086 // plot temp path
5087 PlotATemporaryPathForHelicopter( sMapX, sMapY);
5088
5089 // check if potential path is allowed
5090 DisplayThePotentialPathForHelicopter( sMapX, sMapY );
5091
5092 if (fDrawTempHeliPath)
5093 {
5094 // clip region
5095 ClipBlitsToMapViewRegion( );
5096 // display heli temp path
5097 DisplayHelicopterTempPath( );
5098 //restore
5099 RestoreClipRegionToFullScreen( );
5100 }
5101 }
5102 else
5103 // dest char has been selected,
5104 if( bSelectedDestChar != -1 )
5105 {
5106 PlotATemporaryPathForCharacter(gCharactersList[bSelectedDestChar].merc, sMapX, sMapY);
5107
5108 // check to see if we are drawing path
5109 DisplayThePotentialPathForCurrentDestinationCharacterForMapScreenInterface( sMapX, sMapY );
5110
5111 // if we need to draw path, do it
5112 if (fDrawTempPath)
5113 {
5114 // clip region
5115 ClipBlitsToMapViewRegion( );
5116 // blit
5117 DisplaySoldierTempPath();
5118 // restore
5119 RestoreClipRegionToFullScreen( );
5120 }
5121 }
5122 }
5123 }
5124
5125
5126
RenderMapRegionBackground(void)5127 void RenderMapRegionBackground( void )
5128 {
5129 // renders to save buffer when dirty flag set
5130
5131 if (!fMapPanelDirty)
5132 {
5133 gfMapPanelWasRedrawn = FALSE;
5134
5135 // not dirty, leave
5136 return;
5137 }
5138
5139 // don't bother if showing sector inventory instead of the map!!!
5140 if( !fShowMapInventoryPool )
5141 {
5142 // draw map
5143 DrawMap( );
5144 }
5145
5146
5147 // blit in border
5148 RenderMapBorder( );
5149
5150 if (ghAttributeBox != NO_POPUP_BOX) ForceUpDateOfBox(ghAttributeBox);
5151 if (ghTownMineBox != NO_POPUP_BOX) ForceUpDateOfBox(ghTownMineBox);
5152
5153 MapscreenMarkButtonsDirty();
5154
5155 RestoreExternBackgroundRect(STD_SCREEN_X + 261, STD_SCREEN_Y + 0, MAP_BG_WIDTH, 359);
5156
5157 // don't bother if showing sector inventory instead of the map!!!
5158 if( !fShowMapInventoryPool )
5159 {
5160 // if Skyrider can and wants to talk to us
5161 if( IsHelicopterPilotAvailable( ) )
5162 {
5163 // see if Skyrider has anything new to tell us
5164 CheckAndHandleSkyriderMonologues( );
5165 }
5166 }
5167
5168 // reset dirty flag
5169 fMapPanelDirty = FALSE;
5170
5171 gfMapPanelWasRedrawn = TRUE;
5172 }
5173
5174
5175 static void DisplayIconsForMercsAsleep(void);
5176
5177
RenderTeamRegionBackground()5178 static void RenderTeamRegionBackground()
5179 {
5180 // Render to save buffer when dirty flag set
5181 if (!fTeamPanelDirty) return;
5182
5183 // Show inventory or the team list?
5184 if (!fShowInventoryFlag)
5185 {
5186 BltVideoObject(guiSAVEBUFFER, guiCHARLIST, 0, PLAYER_INFO_X, PLAYER_INFO_Y);
5187 HandleHighLightingOfLinesInTeamPanel();
5188 DisplayCharacterList();
5189 DisplayIconsForMercsAsleep();
5190 }
5191 else
5192 {
5193 BltCharInvPanel();
5194 }
5195
5196 fDrawCharacterList = FALSE;
5197 fTeamPanelDirty = FALSE;
5198 gfRenderPBInterface = TRUE;
5199
5200 MarkAllBoxesAsAltered();
5201 RestoreExternBackgroundRect(STD_SCREEN_X + 0, STD_SCREEN_Y + 107, 261 - 0, 359 - 107);
5202 MapscreenMarkButtonsDirty();
5203 }
5204
5205
RenderCharacterInfoBackground(void)5206 static void RenderCharacterInfoBackground(void)
5207 {
5208 // will render the background for the character info panel
5209
5210 if (!fCharacterInfoPanelDirty)
5211 {
5212 // not dirty, leave
5213 return;
5214 }
5215
5216 // the upleft hand corner character info panel
5217 BltVideoObject(guiSAVEBUFFER, guiCHARINFO, 0, TOWN_INFO_X, TOWN_INFO_Y);
5218
5219 UpdateHelpTextForMapScreenMercIcons( );
5220
5221 if (!fDisableDueToBattleRoster)
5222 {
5223 DisplayCharacterInfo();
5224 RenderAttributeStringsForUpperLeftHandCorner( guiSAVEBUFFER );
5225 }
5226
5227 // reset dirty flag
5228 fCharacterInfoPanelDirty = FALSE;
5229
5230 // redraw face
5231 fReDrawFace = TRUE;
5232
5233 MapscreenMarkButtonsDirty();
5234
5235 // mark all pop ups as dirty
5236 MarkAllBoxesAsAltered( );
5237
5238 // restore background for area
5239 RestoreExternBackgroundRect( STD_SCREEN_X + 0, STD_SCREEN_Y + 0, 261, 107 );
5240
5241 }
5242
5243
5244 static void HandleShadingOfLinesForContractMenu(void);
5245
5246
DetermineIfContractMenuCanBeShown(void)5247 static void DetermineIfContractMenuCanBeShown(void)
5248 {
5249 if (!fShowContractMenu)
5250 {
5251
5252 // destroy menus for contract region
5253 CreateDestroyMouseRegionsForContractMenu( );
5254
5255 // hide all boxes
5256 HideBox( ghContractBox );
5257
5258 // make sure, absolutly sure we want to hide this box
5259 if (!fShowAssignmentMenu)
5260 {
5261 HideBox( ghRemoveMercAssignBox );
5262 }
5263
5264
5265 return;
5266 }
5267
5268 // create mask, if needed
5269 CreateDestroyScreenMaskForAssignmentAndContractMenus( );
5270
5271 // create mouse regions for contract region
5272 CreateDestroyMouseRegionsForContractMenu( );
5273
5274 // determine which lines selectable
5275 HandleShadingOfLinesForContractMenu( );
5276
5277 if (GetSelectedInfoChar()->bLife == 0)
5278 {
5279 // show basic assignment menu
5280 ShowBox( ghRemoveMercAssignBox );
5281 }
5282 else
5283 {
5284 // show basic contract menu
5285 ShowBox( ghContractBox );
5286 }
5287 }
5288
5289
CheckIfPlottingForCharacterWhileAirCraft(void)5290 static void CheckIfPlottingForCharacterWhileAirCraft(void)
5291 {
5292 // if we are in aircraft mode and plotting for character, reset plotting character
5293 if (fShowAircraftFlag)
5294 {
5295 // if plotting, but not for heli
5296 if (bSelectedDestChar != -1 && !fPlotForHelicopter)
5297 {
5298 // abort
5299 AbortMovementPlottingMode();
5300 }
5301 }
5302 else // not in airspace mode
5303 {
5304 if (fPlotForHelicopter)
5305 {
5306 // abort
5307 AbortMovementPlottingMode();
5308 }
5309 }
5310 }
5311
5312
ContractRegionBtnCallback(MOUSE_REGION * pRegion,INT32 iReason)5313 static void ContractRegionBtnCallback(MOUSE_REGION* pRegion, INT32 iReason)
5314 {
5315 // btn callback handler for contract region
5316
5317 if (g_dialogue_box) return;
5318
5319 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
5320 {
5321 SOLDIERTYPE* const pSoldier = GetSelectedInfoChar();
5322 if (CanExtendContractForSoldier(pSoldier))
5323 {
5324 // create
5325 RebuildContractBoxForMerc( pSoldier );
5326
5327 // reset selected characters
5328 ResetAllSelectedCharacterModes( );
5329
5330 bSelectedContractChar = bSelectedInfoChar;
5331 giContractHighLine = bSelectedContractChar;
5332
5333 // if not triggered internally
5334 if (!CheckIfSalaryIncreasedAndSayQuote(pSoldier, TRUE))
5335 {
5336 // show contract box
5337 fShowContractMenu = TRUE;
5338
5339 // stop any active dialogue
5340 StopAnyCurrentlyTalkingSpeech( );
5341 }
5342
5343 //fCharacterInfoPanelDirty = TRUE;
5344 }
5345 else
5346 {
5347 // reset selected characters
5348 ResetAllSelectedCharacterModes( );
5349 }
5350 }
5351
5352 if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
5353 {
5354 // reset selected characters
5355 ResetAllSelectedCharacterModes( );
5356 }
5357 }
5358
5359
HandleShadingOfLinesForContractMenu(void)5360 static void HandleShadingOfLinesForContractMenu(void)
5361 {
5362 if (!fShowContractMenu) return;
5363
5364 PopUpBox* const box = ghContractBox;
5365 if (box == NO_POPUP_BOX) return;
5366
5367 // error check, return if not a valid character
5368 if (bSelectedContractChar == -1) return;
5369 const SOLDIERTYPE* const s = gCharactersList[bSelectedContractChar].merc;
5370 if (s == NULL) return;
5371
5372 Assert(CanExtendContractForSoldier(s));
5373
5374 // is guy in AIM? and well enough to talk and make such decisions?
5375 if (s->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC && s->bLife >= OKLIFE)
5376 {
5377 MERCPROFILESTRUCT const& p = GetProfile(s->ubProfile);
5378 INT32 const balance = LaptopSaveInfo.iCurrentBalance;
5379 ShadeStringInBox(box, CONTRACT_MENU_DAY, p.sSalary > balance);
5380 ShadeStringInBox(box, CONTRACT_MENU_WEEK, (INT32)p.uiWeeklySalary > balance);
5381 ShadeStringInBox(box, CONTRACT_MENU_TWO_WEEKS, (INT32)p.uiBiWeeklySalary > balance);
5382 }
5383 else
5384 {
5385 // can't extend contract duration
5386 ShadeStringInBox(box, CONTRACT_MENU_DAY, true);
5387 ShadeStringInBox(box, CONTRACT_MENU_WEEK, true);
5388 ShadeStringInBox(box, CONTRACT_MENU_TWO_WEEKS, true);
5389 }
5390
5391 // if THIS soldier is involved in a fight (dismissing in a hostile sector IS ok...)
5392 ShadeStringInBox(box, CONTRACT_MENU_TERMINATE, gTacticalStatus.uiFlags & INCOMBAT && s->bInSector);
5393 }
5394
5395
5396 static void SortListOfMercsInTeamPanel(BOOLEAN fRetainSelectedMercs);
5397
5398
ReBuildCharactersList(void)5399 void ReBuildCharactersList( void )
5400 {
5401 // rebuild character's list
5402
5403 // add in characters
5404 for (INT16 sCount = 0; sCount < MAX_CHARACTER_COUNT; ++sCount)
5405 {
5406 // clear this slot
5407 gCharactersList[sCount].merc = NULL;
5408 }
5409
5410 FOR_EACH_IN_TEAM(s, OUR_TEAM)
5411 {
5412 AddCharacter(s);
5413 }
5414
5415 // sort them according to current sorting method
5416 SortListOfMercsInTeamPanel( FALSE );
5417
5418
5419 // if nobody is selected, or the selected merc has somehow become invalid
5420 if (GetSelectedInfoChar() == NULL)
5421 {
5422 // is the first character in the list valid?
5423 if (gCharactersList[0].merc != NULL)
5424 {
5425 // select him
5426 ChangeSelectedInfoChar( 0, TRUE );
5427 }
5428 else
5429 {
5430 // select no one
5431 ChangeSelectedInfoChar( -1, TRUE );
5432 }
5433 }
5434
5435 // exit inventory mode
5436 fShowInventoryFlag = FALSE;
5437 }
5438
5439
5440 // handle change in info char
HandleChangeOfInfoChar(void)5441 static void HandleChangeOfInfoChar(void)
5442 {
5443 static INT8 bOldInfoChar = -1;
5444
5445 if( bSelectedInfoChar != bOldInfoChar )
5446 {
5447 // set auto faces inactive
5448
5449 // valid character?
5450 if( bOldInfoChar != -1 )
5451 {
5452 const SOLDIERTYPE* const s = gCharactersList[bOldInfoChar].merc;
5453 if (s && s->face) SetAutoFaceInActive(*s->face);
5454 }
5455
5456 // stop showing contract box
5457 //fShowContractMenu = FALSE;
5458
5459 // update old info char value
5460 bOldInfoChar = bSelectedInfoChar;
5461 }
5462 }
5463
5464
RebuildContractBoxForMerc(const SOLDIERTYPE * const pCharacter)5465 void RebuildContractBoxForMerc(const SOLDIERTYPE* const pCharacter)
5466 {
5467 // rebuild contractbox for this merc
5468 RemoveBox( ghContractBox );
5469 ghContractBox = NO_POPUP_BOX;
5470
5471 // recreate
5472 CreateContractBox( pCharacter );
5473 }
5474
5475
EnableDisableTeamListRegionsAndHelpText(void)5476 static void EnableDisableTeamListRegionsAndHelpText(void)
5477 {
5478 // check if valid character here, if so, then do nothing..other wise set help text timer to a gazillion
5479 INT8 bCharNum;
5480
5481
5482 for( bCharNum = 0; bCharNum < MAX_CHARACTER_COUNT; bCharNum++ )
5483 {
5484 SOLDIERTYPE const* const s = gCharactersList[bCharNum].merc;
5485 CharacterRegions& r = g_character_regions[bCharNum];
5486 if (s == NULL)
5487 {
5488 // disable regions in all team list columns
5489 r.name.Disable();
5490 r.assignment.Disable();
5491 r.sleep.Disable();
5492 r.location.Disable();
5493 r.destination.Disable();
5494 r.contract.Disable();
5495 }
5496 else
5497 {
5498 // always enable Name and Location regions
5499 r.name.Enable();
5500 r.location.Enable();
5501
5502 // valid character. If it's a vehicle, however
5503 if (s->uiStatusFlags & SOLDIER_VEHICLE)
5504 {
5505 // Can't change assignment for vehicles
5506 r.assignment.Disable();
5507 }
5508 else
5509 {
5510 r.assignment.Enable();
5511
5512 // POW or dead ?
5513 if (s->bAssignment == ASSIGNMENT_POW || s->bLife == 0)
5514 {
5515 // "Remove Merc"
5516 r.assignment.SetFastHelpText(pRemoveMercStrings[0]);
5517 r.destination.SetFastHelpText(ST::null);
5518 }
5519 else
5520 {
5521 // "Assign Merc"
5522 r.assignment.SetFastHelpText(pMapScreenMouseRegionHelpText[1]);
5523 // "Plot Travel Route"
5524 r.destination.SetFastHelpText(pMapScreenMouseRegionHelpText[2]);
5525 }
5526 }
5527
5528 if (CanExtendContractForSoldier(s))
5529 {
5530 r.contract.Enable();
5531 }
5532 else
5533 {
5534 r.contract.Disable();
5535 }
5536
5537 if (CanChangeSleepStatusForSoldier(s))
5538 {
5539 r.sleep.Enable();
5540 }
5541 else
5542 {
5543 r.sleep.Disable();
5544 }
5545
5546 // destination region is always enabled for all valid character slots.
5547 // if the character can't move at this time, then the region handler must be able to tell the player why not
5548 r.destination.Enable();
5549 }
5550 }
5551 }
5552
5553
ResetAllSelectedCharacterModes(void)5554 static void ResetAllSelectedCharacterModes(void)
5555 {
5556 // if in militia redistribution popup
5557 if ( sSelectedMilitiaTown != 0 )
5558 {
5559 sSelectedMilitiaTown = 0;
5560 }
5561
5562
5563 // cancel destination line highlight
5564 giDestHighLine = -1;
5565
5566 // cancel assign line highlight
5567 giAssignHighLine = -1;
5568
5569 // unhilight contract line
5570 giContractHighLine = -1;
5571
5572
5573 // if we were plotting movement
5574 if (bSelectedDestChar != -1 || fPlotForHelicopter)
5575 {
5576 AbortMovementPlottingMode();
5577 }
5578
5579 // reset assign character
5580 bSelectedAssignChar = -1;
5581
5582 // reset contract character
5583 bSelectedContractChar = -1;
5584
5585
5586 // reset map cursor to normal
5587 if ( !gfFadeOutDone && !gfFadeIn )
5588 {
5589 SetUpCursorForStrategicMap( );
5590 }
5591 }
5592
5593
5594 // automatically pause/unpause time compression during certain events
UpdatePausedStatesDueToTimeCompression(void)5595 static void UpdatePausedStatesDueToTimeCompression(void)
5596 {
5597 // this executes every frame, so keep it optimized for speed!
5598
5599 // if time is being compressed
5600 if( IsTimeBeingCompressed() )
5601 {
5602 // but it shouldn't be
5603 if ( !AllowedToTimeCompress( ) )
5604 {
5605 // pause game to (temporarily) stop time compression
5606 PauseGame( );
5607 }
5608 }
5609 else // time is NOT being compressed
5610 {
5611 // but the player would like it to be compressing
5612 if ( IsTimeCompressionOn() && !gfPauseDueToPlayerGamePause )
5613 {
5614 // so check if it's legal to start time compressing again
5615 if ( AllowedToTimeCompress( ) )
5616 {
5617 // unpause game to restart time compresssion
5618 UnPauseGame( );
5619 }
5620 }
5621 }
5622 }
5623
5624
ContinueDialogue(SOLDIERTYPE * pSoldier,BOOLEAN fDone)5625 BOOLEAN ContinueDialogue(SOLDIERTYPE* pSoldier, BOOLEAN fDone)
5626 {
5627 // continue this grunts dialogue, restore when done
5628 static BOOLEAN fTalkingingGuy = FALSE;
5629
5630 INT8 bCounter = 0;
5631
5632 if (fDone)
5633 {
5634 if (fTalkingingGuy)
5635 {
5636 fCharacterInfoPanelDirty = TRUE;
5637 fTalkingingGuy = FALSE;
5638 }
5639
5640 return( TRUE );
5641 }
5642
5643 // check if valid character talking?
5644 if( pSoldier == NULL )
5645 {
5646 return FALSE;
5647 }
5648
5649 // otherwise, find this character
5650 for( bCounter = 0; bCounter < MAX_CHARACTER_COUNT; bCounter++ )
5651 {
5652 if (gCharactersList[bCounter].merc == pSoldier)
5653 {
5654 if (bSelectedInfoChar != bCounter)
5655 {
5656 ChangeSelectedInfoChar(bCounter, TRUE);
5657 }
5658 fTalkingingGuy = TRUE;
5659 return FALSE;
5660 }
5661 }
5662
5663 return ( FALSE );
5664 }
5665
5666
HandleSpontanousTalking(void)5667 static void HandleSpontanousTalking(void)
5668 {
5669 // simply polls if the talking guy is done, if so...send an end command to continue dialogue
5670 if (!DialogueActive())
5671 {
5672 SOLDIERTYPE* const s = GetSelectedInfoChar();
5673 if (s != NULL) ContinueDialogue(s, TRUE);
5674 }
5675 }
5676
5677
5678 static void HandleNewDestConfirmation(INT16 sMapX, INT16 sMapY);
5679 static void RebuildWayPointsForAllSelectedCharsGroups(void);
5680
5681
CheckIfClickOnLastSectorInPath(INT16 sX,INT16 sY)5682 static BOOLEAN CheckIfClickOnLastSectorInPath(INT16 sX, INT16 sY)
5683 {
5684 PathSt*const* ppMovePath = NULL;
5685 BOOLEAN fLastSectorInPath = FALSE;
5686 INT32 iVehicleId = -1;
5687
5688 // see if we have clicked on the last sector in the characters path
5689
5690 // check if helicopter
5691 if (fPlotForHelicopter)
5692 {
5693 if( sX + ( sY * MAP_WORLD_X ) == GetLastSectorOfHelicoptersPath( ) )
5694 {
5695 // helicopter route confirmed - take off
5696 TakeOffHelicopter( );
5697
5698 // rebuild waypoints - helicopter
5699 VEHICLETYPE& v = GetHelicopter();
5700 ppMovePath = &v.pMercPath;
5701 RebuildWayPointsForGroupPath(*ppMovePath, *GetGroup(v.ubMovementGroup));
5702
5703 fLastSectorInPath = TRUE;
5704 }
5705 }
5706 else // not doing helicopter movement
5707 {
5708 // if not doing a soldier either, we shouldn't be here!
5709 if( bSelectedDestChar == -1 )
5710 {
5711 return( FALSE );
5712 }
5713
5714 MapScreenCharacterSt* const c = &gCharactersList[bSelectedDestChar];
5715 const SOLDIERTYPE* const s = c->merc;
5716
5717 // invalid soldier? we shouldn't be here!
5718 if (s == NULL)
5719 {
5720 bSelectedDestChar = -1;
5721 return( FALSE );
5722 }
5723
5724 if (sX + sY * MAP_WORLD_X == GetLastSectorIdInCharactersPath(s))
5725 {
5726 // clicked on last sector, reset plotting mode
5727
5728 // if he's IN a vehicle or IS a vehicle
5729 if (s->bAssignment == VEHICLE || s->uiStatusFlags & SOLDIER_VEHICLE)
5730 {
5731 if (s->bAssignment == VEHICLE)
5732 {
5733 // IN a vehicle
5734 iVehicleId = s->iVehicleId;
5735 }
5736 else
5737 {
5738 // IS a vehicle
5739 iVehicleId = s->bVehicleID;
5740 }
5741
5742 // rebuild waypoints - vehicles
5743 ppMovePath = &( pVehicleList[ iVehicleId ].pMercPath );
5744 }
5745 else
5746 {
5747 // rebuild waypoints - mercs on foot
5748 ppMovePath = &s->pMercPath;
5749 }
5750
5751 RebuildWayPointsForAllSelectedCharsGroups( );
5752
5753 fLastSectorInPath = TRUE;
5754 }
5755 }
5756
5757
5758 // if the click was over the last sector
5759 if ( fLastSectorInPath )
5760 {
5761 // route has been confirmed
5762 EndConfirmMapMoveMode( );
5763
5764 // if we really did plot a path (this will skip message if left click on current sector with no path)
5765 if ( GetLengthOfPath( *ppMovePath ) > 0 )
5766 {
5767 // then verbally confirm this destination!
5768 HandleNewDestConfirmation( sX, sY );
5769 }
5770 else // NULL path confirmed
5771 {
5772 // if previously there was a path
5773 if (g_prev_path)
5774 {
5775 // then this means we've CANCELED it
5776 BeginMapUIMessage(0, pMapPlotStrings[3]);
5777 }
5778 else // no previous path
5779 {
5780 // then it means the route was UNCHANGED
5781 BeginMapUIMessage(0, pMapPlotStrings[2]);
5782 }
5783 }
5784 }
5785
5786
5787 return( fLastSectorInPath );
5788 }
5789
5790
5791 // rebuild waypoints for selected character list
RebuildWayPointsForAllSelectedCharsGroups(void)5792 static void RebuildWayPointsForAllSelectedCharsGroups(void)
5793 {
5794 // rebuild the waypoints for everyone in the selected character list
5795 BOOLEAN fGroupIDRebuilt[ 256 ];
5796 INT32 iVehicleId;
5797 PathSt** ppMovePath = NULL;
5798 UINT8 ubGroupId;
5799
5800
5801 std::fill(std::begin(fGroupIDRebuilt), std::end(fGroupIDRebuilt), FALSE);
5802
5803 CFOR_EACH_SELECTED_IN_CHAR_LIST(c)
5804 {
5805 const SOLDIERTYPE* const pSoldier = c->merc;
5806
5807 // if he's IN a vehicle or IS a vehicle
5808 if( ( pSoldier->bAssignment == VEHICLE ) || ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE ) )
5809 {
5810 if( pSoldier->bAssignment == VEHICLE )
5811 {
5812 // IN a vehicle
5813 iVehicleId = pSoldier->iVehicleId;
5814 }
5815 else
5816 {
5817 // IS a vehicle
5818 iVehicleId = pSoldier->bVehicleID;
5819 }
5820
5821 // vehicles
5822 VEHICLETYPE* const v = &pVehicleList[iVehicleId];
5823 ppMovePath = &v->pMercPath;
5824 ubGroupId = v->ubMovementGroup;
5825 }
5826 else
5827 {
5828 // mercs on foot
5829 ppMovePath = &gCharactersList[bSelectedDestChar].merc->pMercPath;
5830 ubGroupId = pSoldier->ubGroupID;
5831 }
5832
5833 // if we haven't already rebuilt this group
5834 if ( !fGroupIDRebuilt[ ubGroupId ] )
5835 {
5836 // rebuild it now
5837 RebuildWayPointsForGroupPath(*ppMovePath, *GetGroup(ubGroupId));
5838
5839 // mark it as rebuilt
5840 fGroupIDRebuilt[ ubGroupId ] = TRUE;
5841 }
5842 }
5843 }
5844
5845
5846 // check if cursor needs to be set to checkmark or to the walking guy?
UpdateCursorIfInLastSector(void)5847 static void UpdateCursorIfInLastSector(void)
5848 {
5849
5850 INT16 sMapX = 0, sMapY = 0;
5851
5852 // check to see if we are plotting a path, if so, see if we are highlighting the last sector int he path, if so, change the cursor
5853 if (bSelectedDestChar != -1 || fPlotForHelicopter)
5854 {
5855 GetMouseMapXY(&sMapX, &sMapY);
5856
5857 if (!fShowAircraftFlag)
5858 {
5859 if( bSelectedDestChar != -1 )
5860 {
5861 //c heck if we are in the last sector of the characters path?
5862 if (sMapX + sMapY * MAP_WORLD_X == GetLastSectorIdInCharactersPath(gCharactersList[bSelectedDestChar].merc))
5863 {
5864 // set cursor to checkmark
5865 ChangeMapScreenMaskCursor( CURSOR_CHECKMARK );
5866 }
5867 else if( fCheckCursorWasSet )
5868 {
5869 // reset to walking guy/vehicle
5870 SetUpCursorForStrategicMap( );
5871 }
5872 }
5873 }
5874 else
5875 {
5876 // check for helicopter
5877 if( fPlotForHelicopter )
5878 {
5879 if( sMapX + ( sMapY * MAP_WORLD_X ) == GetLastSectorOfHelicoptersPath( ) )
5880 {
5881 // set cursor to checkmark
5882 ChangeMapScreenMaskCursor( CURSOR_CHECKMARK );
5883 }
5884 else if( fCheckCursorWasSet )
5885 {
5886 // reset to walking guy/vehicle
5887 SetUpCursorForStrategicMap( );
5888 }
5889 }
5890 else
5891 {
5892 // reset to walking guy/vehicle
5893 SetUpCursorForStrategicMap( );
5894 }
5895 }
5896 }
5897 }
5898
5899
FaceRegionBtnCallback(MOUSE_REGION * pRegion,INT32 iReason)5900 static void FaceRegionBtnCallback(MOUSE_REGION* pRegion, INT32 iReason)
5901 {
5902 // error checking, make sure someone is there
5903 if (GetSelectedInfoChar() == NULL) return;
5904
5905 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
5906 {
5907 if (gfPreBattleInterfaceActive) return;
5908
5909 // now stop any dialogue in progress
5910 StopAnyCurrentlyTalkingSpeech( );
5911 }
5912
5913 if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
5914 {
5915 RequestToggleMercInventoryPanel();
5916 }
5917 }
5918
5919
ItemRegionBtnCallback(MOUSE_REGION * pRegion,INT32 iReason)5920 static void ItemRegionBtnCallback(MOUSE_REGION* pRegion, INT32 iReason)
5921 {
5922 // left AND right button are handled the same way
5923 if (iReason & ( MSYS_CALLBACK_REASON_RBUTTON_UP | MSYS_CALLBACK_REASON_LBUTTON_UP ) )
5924 {
5925 RequestToggleMercInventoryPanel();
5926 }
5927 }
5928
5929
5930 static bool CanToggleSelectedCharInventory();
5931
5932
ItemRegionMvtCallback(MOUSE_REGION * pRegion,INT32 iReason)5933 static void ItemRegionMvtCallback(MOUSE_REGION* pRegion, INT32 iReason)
5934 {
5935 if ( !CanToggleSelectedCharInventory() )
5936 {
5937 fShowItemHighLight = FALSE;
5938 return;
5939 }
5940
5941 if( ( iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE ) )
5942 {
5943 fShowItemHighLight = TRUE;
5944 }
5945 else if( iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
5946 {
5947 fShowItemHighLight = FALSE;
5948 }
5949 }
5950
5951
5952 // handle highlighting of team panel lines
HandleChangeOfHighLightedLine(void)5953 static void HandleChangeOfHighLightedLine(void)
5954 {
5955 static INT32 iOldHighLine;
5956
5957 if ( fShowInventoryFlag )
5958 {
5959 return;
5960 }
5961
5962 // check if change in highlight line
5963 if( giHighLine != iOldHighLine )
5964 {
5965 iOldHighLine = giHighLine;
5966
5967 if( giHighLine == -1 )
5968 {
5969 giSleepHighLine = -1;
5970 giAssignHighLine = -1;
5971 giContractHighLine = -1;
5972 giSleepHighLine = -1;
5973
5974 // don't do during plotting, allowing selected character to remain highlighted and their destination column to glow!
5975 if (bSelectedDestChar == -1 && !fPlotForHelicopter)
5976 {
5977 giDestHighLine = -1;
5978 }
5979 }
5980
5981 fDrawCharacterList = TRUE;
5982 }
5983 }
5984
5985
HandleCharBarRender(void)5986 static void HandleCharBarRender(void)
5987 {
5988 if (fDisableDueToBattleRoster) return; // check if the panel is disbled, if so, do not render
5989
5990 const SOLDIERTYPE* const s = GetSelectedInfoChar();
5991 if (s == NULL) return;
5992
5993 if (s->bLife != 0 &&
5994 s->bAssignment != ASSIGNMENT_DEAD &&
5995 s->bAssignment != ASSIGNMENT_POW)
5996 {
5997 DrawSoldierUIBars(*s, BAR_INFO_X, BAR_INFO_Y, TRUE, FRAME_BUFFER);
5998 }
5999
6000 UpdateCharRegionHelpText();
6001 }
6002
6003
6004 // update the status of the contract box
UpDateStatusOfContractBox(void)6005 static void UpDateStatusOfContractBox(void)
6006 {
6007 if (fShowContractMenu)
6008 {
6009 ForceUpDateOfBox( ghContractBox );
6010
6011 const SOLDIERTYPE* const s = GetSelectedInfoChar();
6012 if (s->bLife == 0 || s->bAssignment == ASSIGNMENT_POW)
6013 {
6014 ForceUpDateOfBox( ghRemoveMercAssignBox );
6015 }
6016 }
6017 }
6018
6019
TrashItemMessageBoxCallBack(MessageBoxReturnValue const bExitValue)6020 static void TrashItemMessageBoxCallBack(MessageBoxReturnValue const bExitValue)
6021 {
6022 if( bExitValue == MSG_BOX_RETURN_YES )
6023 {
6024 // find the item and get rid of it
6025 MAPEndItemPointer();
6026
6027 // reset cursor
6028 gSMPanelRegion.ChangeCursor(CURSOR_NORMAL);
6029 SetCurrentCursorFromDatabase( CURSOR_NORMAL );
6030
6031 HandleButtonStatesWhileMapInventoryActive( );
6032 }
6033 }
6034
6035
TrashCanBtnCallback(MOUSE_REGION *,INT32 const reason)6036 static void TrashCanBtnCallback(MOUSE_REGION*, INT32 const reason)
6037 {
6038 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
6039 {
6040 // Check if an item is in the cursor, if so, warn player
6041 if (OBJECTTYPE* const o = gpItemPointer)
6042 {
6043 ST::string msg = o->ubMission ? pTrashItemText[1] : pTrashItemText[0];
6044 DoMapMessageBox(MSG_BOX_BASIC_STYLE, msg, MAP_SCREEN, MSG_BOX_FLAG_YESNO, TrashItemMessageBoxCallBack);
6045 }
6046 }
6047 }
6048
6049
TrashCanMoveCallback(MOUSE_REGION * pRegion,INT32 iReason)6050 static void TrashCanMoveCallback(MOUSE_REGION* pRegion, INT32 iReason)
6051 {
6052
6053 if (iReason & MSYS_CALLBACK_REASON_GAIN_MOUSE )
6054 {
6055 if (fMapInventoryItem)
6056 {
6057 fShowTrashCanHighLight = TRUE;
6058 }
6059 }
6060 else if( iReason & MSYS_CALLBACK_REASON_LOST_MOUSE )
6061 {
6062 fShowTrashCanHighLight = FALSE;
6063 }
6064 }
6065
6066
UpdateStatusOfMapSortButtons(void)6067 static void UpdateStatusOfMapSortButtons(void)
6068 {
6069 INT32 iCounter = 0;
6070 static BOOLEAN fShownLastTime = FALSE;
6071
6072
6073 if( ( gfPreBattleInterfaceActive ) || fShowInventoryFlag )
6074 {
6075 if ( fShownLastTime )
6076 {
6077 for( iCounter = 0; iCounter < MAX_SORT_METHODS; iCounter++ )
6078 {
6079 HideButton( giMapSortButton[ iCounter ] );
6080 }
6081 if ( gfPreBattleInterfaceActive )
6082 {
6083 HideButton( giCharInfoButton[ 0 ] );
6084 HideButton( giCharInfoButton[ 1 ] );
6085 }
6086
6087 fShownLastTime = FALSE;
6088 }
6089 }
6090 else
6091 {
6092 if ( !fShownLastTime )
6093 {
6094 for( iCounter = 0; iCounter < MAX_SORT_METHODS; iCounter++ )
6095 {
6096 ShowButton( giMapSortButton[ iCounter ] );
6097 }
6098
6099 ShowButton( giCharInfoButton[ 0 ] );
6100 ShowButton( giCharInfoButton[ 1 ] );
6101
6102 fShownLastTime = TRUE;
6103 }
6104 }
6105 }
6106
6107
6108 static void DoneInventoryMapBtnCallback(GUI_BUTTON* btn, INT32 reason);
6109
6110
CreateDestroyTrashCanRegion(void)6111 static void CreateDestroyTrashCanRegion(void)
6112 {
6113 static BOOLEAN fCreated = FALSE;
6114
6115 if (fShowInventoryFlag && !fCreated)
6116 {
6117
6118 fCreated = TRUE;
6119
6120 // trash can
6121 MSYS_DefineRegion( &gTrashCanRegion, TRASH_CAN_X, TRASH_CAN_Y, TRASH_CAN_X + TRASH_CAN_WIDTH, TRASH_CAN_Y + TRASH_CAN_HEIGHT , MSYS_PRIORITY_HIGHEST - 4 ,
6122 MSYS_NO_CURSOR, TrashCanMoveCallback, TrashCanBtnCallback );
6123
6124 // done inventory button define
6125 giMapInvDoneButton = QuickCreateButtonImg(INTERFACEDIR "/done_button2.sti", 0, 1, INV_BTN_X, INV_BTN_Y, MSYS_PRIORITY_HIGHEST - 1, DoneInventoryMapBtnCallback);
6126 giMapInvDoneButton->SetFastHelpText(pMiscMapScreenMouseRegionHelpText[2]);
6127
6128 gTrashCanRegion.SetFastHelpText(pMiscMapScreenMouseRegionHelpText[1]);
6129
6130 InitMapKeyRingInterface( KeyRingItemPanelButtonCallback );
6131
6132 // reset the compatable item array at this point
6133 ResetCompatibleItemArray( );
6134
6135 }
6136 else if (!fShowInventoryFlag && fCreated)
6137 {
6138 // trash can region
6139 fCreated = FALSE;
6140 MSYS_RemoveRegion( &gTrashCanRegion );
6141
6142 // map inv done button
6143 RemoveButton( giMapInvDoneButton );
6144
6145 ShutdownKeyRingInterface( );
6146
6147 if (fShowDescriptionFlag)
6148 {
6149 // kill description
6150 DeleteItemDescriptionBox( );
6151 }
6152
6153 }
6154 }
6155
6156
DoneInventoryMapBtnCallback(GUI_BUTTON * btn,INT32 reason)6157 static void DoneInventoryMapBtnCallback(GUI_BUTTON* btn, INT32 reason)
6158 {
6159 // prevent inventory from being closed while stack popup up!
6160 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
6161 {
6162 if (!fMapInventoryItem && !InItemStackPopup())
6163 {
6164 fEndShowInventoryFlag = TRUE;
6165 }
6166 }
6167 }
6168
6169
StartConfirmMapMoveMode(INT16 sMapY)6170 static void StartConfirmMapMoveMode(INT16 sMapY)
6171 {
6172 // tell player what to do - to click again to confirm move
6173 BeginMapUIMessage(sMapY < 8 ? 100 : -100, pMapPlotStrings[0]);
6174 gfInConfirmMapMoveMode = TRUE;
6175 }
6176
6177
EndConfirmMapMoveMode(void)6178 void EndConfirmMapMoveMode(void)
6179 {
6180 CancelMapUIMessage( );
6181
6182 gfInConfirmMapMoveMode = FALSE;
6183 }
6184
6185
CancelMapUIMessage(void)6186 void CancelMapUIMessage( void )
6187 {
6188 // and kill the message overlay
6189 EndUIMessage( );
6190
6191 fMapPanelDirty = TRUE;
6192 }
6193
6194
6195 // automatically turns off mapscreen ui overlay messages when appropriate
MonitorMapUIMessage(void)6196 static void MonitorMapUIMessage(void)
6197 {
6198 // if there is a map UI message being displayed
6199 if (g_ui_message_overlay != NULL)
6200 {
6201 // and if we're not in the middle of the "confirm move" sequence
6202 // if( !gfInConfirmMapMoveMode || bSelectedDestChar == -1 )
6203 {
6204 // and we've now exceed its period of maximum persistance (without user input)
6205 if ( ( GetJA2Clock( ) - guiUIMessageTime ) > guiUIMessageTimeDelay )
6206 {
6207 // then cancel the message now
6208 CancelMapUIMessage( );
6209 }
6210 }
6211 }
6212 }
6213
6214
6215 // handle pre battle interface in relation to inventory
HandlePreBattleInterfaceWithInventoryPanelUp(void)6216 static void HandlePreBattleInterfaceWithInventoryPanelUp(void)
6217 {
6218 if (gfPreBattleInterfaceActive && fShowInventoryFlag)
6219 {
6220 if (fShowDescriptionFlag)
6221 {
6222 // kill description
6223 DeleteItemDescriptionBox( );
6224 }
6225
6226 // kill inventory panel
6227 fShowInventoryFlag = FALSE;
6228 CreateDestroyMapInvButton();
6229 }
6230 }
6231
6232
6233 //update any bad assignments..error checking
6234 // this puts anyone who is on NO_ASSIGNMENT onto a free squad
UpdateBadAssignments(void)6235 static void UpdateBadAssignments(void)
6236 {
6237 CFOR_EACH_IN_CHAR_LIST(c)
6238 {
6239 CheckIfSoldierUnassigned(c->merc);
6240 }
6241 }
6242
6243
InterruptTimeForMenus(void)6244 static void InterruptTimeForMenus(void)
6245 {
6246 if (fShowAssignmentMenu || fShowContractMenu)
6247 {
6248 InterruptTime( );
6249 PauseTimeForInterupt( );
6250 }
6251 else if( fOneFrame )
6252 {
6253 InterruptTime( );
6254 PauseTimeForInterupt( );
6255 }
6256 }
6257
6258
6259 static bool AnyMercsLeavingRealSoon();
6260
6261
HandleContractTimeFlashForMercThatIsAboutLeave(void)6262 static void HandleContractTimeFlashForMercThatIsAboutLeave(void)
6263 {
6264 INT32 iCurrentTime;
6265
6266 // grab the current time
6267 iCurrentTime = GetJA2Clock();
6268
6269 // only bother checking once flash interval has elapsed
6270 if( ( iCurrentTime - giFlashContractBaseTime ) >= DELAY_PER_FLASH_FOR_DEPARTING_PERSONNEL )
6271 {
6272 // update timer so that we only run check so often
6273 giFlashContractBaseTime = iCurrentTime;
6274 fFlashContractFlag = !fFlashContractFlag;
6275
6276 // don't redraw unless we have to!
6277 if ( AnyMercsLeavingRealSoon() )
6278 {
6279 // redraw character list
6280 fDrawCharacterList = TRUE;
6281 }
6282 }
6283 }
6284
6285
6286 // merc about to leave, flash contract time
AnyMercsLeavingRealSoon()6287 static bool AnyMercsLeavingRealSoon()
6288 {
6289 UINT32 const now = GetWorldTotalMin();
6290 CFOR_EACH_IN_CHAR_LIST(c)
6291 {
6292 if (c->merc->iEndofContractTime - now > MINS_TO_FLASH_CONTRACT_TIME) continue;
6293 return true;
6294 }
6295 return false;
6296 }
6297
6298
HandlePreloadOfMapGraphics(void)6299 void HandlePreloadOfMapGraphics(void)
6300 {
6301 guiSleepIcon = AddVideoObjectFromFile(INTERFACEDIR "/sleepicon.sti");
6302 guiCHARINFO = AddVideoObjectFromFile(INTERFACEDIR "/charinfo.sti");
6303 guiCHARLIST = AddVideoObjectFromFile(INTERFACEDIR "/newgoldpiece3.sti");
6304
6305 guiMAPINV = AddVideoObjectFromFile(INTERFACEDIR "/mapinv.sti");
6306
6307 // the upper left corner piece icons
6308 guiULICONS = AddVideoObjectFromFile(INTERFACEDIR "/top_left_corner_icons.sti");
6309
6310 HandleLoadOfMapBottomGraphics( );
6311
6312 //Kris: Added this because I need to blink the icons button.
6313 guiNewMailIcons = AddVideoObjectFromFile(INTERFACEDIR "/newemail.sti");
6314
6315 // graphic for pool inventory
6316 LoadInventoryPoolGraphic( );
6317
6318 // load border graphics
6319 LoadMapBorderGraphics( );
6320
6321 LoadInterfaceItemsGraphics();
6322 LoadInterfaceUtilsGraphics();
6323 LoadMapScreenInterfaceGraphics();
6324 LoadMapScreenInterfaceMapGraphics();
6325 }
6326
6327
HandleRemovalOfPreLoadedMapGraphics(void)6328 void HandleRemovalOfPreLoadedMapGraphics( void )
6329 {
6330 DeleteMapBottomGraphics();
6331 DeleteVideoObject(guiSleepIcon);
6332
6333 DeleteVideoObject(guiCHARLIST);
6334 DeleteVideoObject(guiCHARINFO);
6335
6336 DeleteVideoObject(guiMAPINV);
6337 DeleteVideoObject(guiULICONS);
6338
6339 //Kris: Remove the email icons.
6340 DeleteVideoObject(guiNewMailIcons);
6341
6342 // remove inventory pool graphic
6343 RemoveInventoryPoolGraphic();
6344
6345 // get rid of border stuff
6346 DeleteMapBorderGraphics();
6347
6348 DeleteInterfaceItemsGraphics();
6349 DeleteInterfaceUtilsGraphics();
6350 DeleteMapScreenInterfaceGraphics();
6351 DeleteMapScreenInterfaceMapGraphics();
6352 }
6353
6354
CharacterIsInLoadedSectorAndWantsToMoveInventoryButIsNotAllowed(const SOLDIERTYPE * const s)6355 static BOOLEAN CharacterIsInLoadedSectorAndWantsToMoveInventoryButIsNotAllowed(const SOLDIERTYPE* const s)
6356 {
6357 // char is in loaded sector
6358 if (s->sSectorX != gWorldSectorX ||
6359 s->sSectorY != gWorldSectorY ||
6360 s->bSectorZ != gbWorldSectorZ)
6361 {
6362 return( FALSE );
6363 }
6364
6365 // not showing inventory?
6366 if (!fShowInventoryFlag)
6367 {
6368 // nope
6369 return( FALSE );
6370 }
6371
6372 // picked something up?
6373 if (!fMapInventoryItem) return FALSE; // no
6374
6375 // only disallow when enemies in sector
6376 if ( !gTacticalStatus.fEnemyInSector )
6377 {
6378 return( FALSE );
6379 }
6380
6381 return( TRUE );
6382 }
6383
6384
6385 // how many on team, if less than 2, disable prev/next merc buttons
UpdateTheStateOfTheNextPrevMapScreenCharacterButtons(void)6386 static void UpdateTheStateOfTheNextPrevMapScreenCharacterButtons(void)
6387 {
6388 if (gfPreBattleInterfaceActive) return;
6389
6390 const SOLDIERTYPE* const s = GetSelectedInfoChar();
6391 if (s == NULL)
6392 {
6393 DisableButton(giCharInfoButton[0]);
6394 DisableButton(giCharInfoButton[1]);
6395 DisableButton(giMapContractButton);
6396 }
6397 /* ARM: Commented out at KM's request, it won't reenabled them when coming back from PBI, on Feb. 22, 99
6398 else if (!fShowInventoryFlag) // make sure that we are in fact showing the mapscreen inventory
6399 {
6400 return;
6401 }
6402 */
6403 else
6404 {
6405 // standard checks
6406 bool const enable =
6407 !fShowDescriptionFlag &&
6408 GetNumberOfPeopleInCharacterList() >= 2 &&
6409 !CharacterIsInLoadedSectorAndWantsToMoveInventoryButIsNotAllowed(s) &&
6410 !CharacterIsInTransitAndHasItemPickedUp(s);
6411 EnableButton(giCharInfoButton[0], enable);
6412 EnableButton(giCharInfoButton[1], enable);
6413 }
6414 }
6415
6416
PrevInventoryMapBtnCallback(GUI_BUTTON * btn,INT32 reason)6417 static void PrevInventoryMapBtnCallback(GUI_BUTTON *btn, INT32 reason)
6418 {
6419 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
6420 {
6421 GoToPrevCharacterInList();
6422 }
6423 }
6424
6425
NextInventoryMapBtnCallback(GUI_BUTTON * btn,INT32 reason)6426 static void NextInventoryMapBtnCallback(GUI_BUTTON *btn, INT32 reason)
6427 {
6428 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
6429 {
6430 GoToNextCharacterInList();
6431 }
6432 }
6433
6434
CreateDestroyMapCharacterScrollButtons(void)6435 static void CreateDestroyMapCharacterScrollButtons(void)
6436 {
6437 static BOOLEAN fCreated = FALSE;
6438
6439 if (fInMapMode && !fCreated)
6440 {
6441 const INT16 prio = MSYS_PRIORITY_HIGHEST - 5;
6442
6443 giCharInfoButton[0] = QuickCreateButtonImg(INTERFACEDIR "/map_screen_bottom_arrows.sti", 11, 4, -1, 6, -1, STD_SCREEN_X + 67, STD_SCREEN_Y + 69, prio, PrevInventoryMapBtnCallback);
6444 giCharInfoButton[1] = QuickCreateButtonImg(INTERFACEDIR "/map_screen_bottom_arrows.sti", 12, 5, -1, 7, -1, STD_SCREEN_X + 67, STD_SCREEN_Y + 87, prio, NextInventoryMapBtnCallback);
6445
6446 giCharInfoButton[0]->SetFastHelpText(pMapScreenPrevNextCharButtonHelpText[0]);
6447 giCharInfoButton[1]->SetFastHelpText(pMapScreenPrevNextCharButtonHelpText[1]);
6448
6449 fCreated = TRUE;
6450
6451 }
6452 else if (!fInMapMode && fCreated)
6453 {
6454 RemoveButton( giCharInfoButton[ 0 ]);
6455 RemoveButton( giCharInfoButton[ 1 ]);
6456
6457 fCreated = FALSE;
6458 }
6459 }
6460
6461
6462
TellPlayerWhyHeCantCompressTime(void)6463 void TellPlayerWhyHeCantCompressTime( void )
6464 {
6465 // if we're locked into paused time compression by some event that enforces that
6466 if ( PauseStateLocked() )
6467 {
6468 SLOGD("Can't compress time, pause state locked (reason %d). OK unless permanent.\n\
6469 If permanent, take screenshot now, send with *previous* save & describe what happened since.",
6470 guiLockPauseStateLastReasonId);
6471 }
6472 else if (!gfAtLeastOneMercWasHired)
6473 {
6474 // no mercs hired, ever
6475 DoMapMessageBox(MSG_BOX_BASIC_STYLE, pMapScreenJustStartedHelpText, MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback);
6476 }
6477 else if ( !AnyUsableRealMercenariesOnTeam() )
6478 {
6479 // no usable mercs left on team
6480 DoMapMessageBox( MSG_BOX_BASIC_STYLE, pMapErrorString[ 39 ], MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback );
6481 }
6482 else if ( ActiveTimedBombExists() )
6483 {
6484 // can't time compress when a bomb is about to go off!
6485 DoMapMessageBox(MSG_BOX_BASIC_STYLE, gzLateLocalizedString[STR_LATE_02], MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback);
6486 }
6487 else if ( gfContractRenewalSquenceOn )
6488 {
6489 SLOGD("Can't compress time while contract renewal sequence is on.");
6490 }
6491 else if( fDisableMapInterfaceDueToBattle )
6492 {
6493 SLOGD("Can't compress time while disabled due to battle.");
6494 }
6495 else if( fDisableDueToBattleRoster )
6496 {
6497 SLOGD("Can't compress time while in battle roster.");
6498 }
6499 else if ( fMapInventoryItem )
6500 {
6501 SLOGD("Can't compress time while still holding an inventory item.");
6502 }
6503 else if( fShowMapInventoryPool )
6504 {
6505 DoMapMessageBox(MSG_BOX_BASIC_STYLE, gzLateLocalizedString[STR_LATE_55], MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback);
6506 }
6507 // ARM: THIS TEST SHOULD BE THE LAST ONE, BECAUSE IT ACTUALLY RESULTS IN SOMETHING HAPPENING NOW.
6508 // KM: Except if we are in a creature lair and haven't loaded the sector yet (no battle yet)
6509 else if( gTacticalStatus.uiFlags & INCOMBAT || gTacticalStatus.fEnemyInSector )
6510 {
6511 if( OnlyHostileCivsInSector() )
6512 {
6513 ST::string str;
6514 ST::string pSectorString;
6515 pSectorString = GetSectorIDString(gWorldSectorX, gWorldSectorY, gbWorldSectorZ, TRUE);
6516 str = st_format_printf(gzLateLocalizedString[STR_LATE_27], pSectorString);
6517 DoMapMessageBox( MSG_BOX_BASIC_STYLE, str, MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback );
6518 }
6519 else
6520 {
6521 //The NEW non-persistant PBI is used instead of a dialog box explaining why we can't compress time.
6522 InitPreBattleInterface(0, false);
6523 }
6524 }
6525 else if( PlayerGroupIsInACreatureInfestedMine() )
6526 {
6527 DoMapMessageBox(MSG_BOX_BASIC_STYLE, gzLateLocalizedString[STR_LATE_28], MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback);
6528 }
6529 }
6530
6531
MapScreenDefaultOkBoxCallback(MessageBoxReturnValue const bExitValue)6532 void MapScreenDefaultOkBoxCallback(MessageBoxReturnValue const bExitValue)
6533 {
6534 if( bExitValue == MSG_BOX_RETURN_OK )
6535 {
6536 fMapPanelDirty = TRUE;
6537 fTeamPanelDirty = TRUE;
6538 fCharacterInfoPanelDirty = TRUE;
6539 }
6540 }
6541
6542
MapSortBtnCallback(GUI_BUTTON * btn,INT32 reason)6543 static void MapSortBtnCallback(GUI_BUTTON *btn, INT32 reason)
6544 {
6545 // grab the button index value for the sort buttons
6546 INT32 const iValue = btn->GetUserData();
6547
6548 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
6549 {
6550 ChangeCharacterListSortMethod( iValue );
6551 }
6552 }
6553
6554
AddTeamPanelSortButtonsForMapScreen(void)6555 static void AddTeamPanelSortButtonsForMapScreen(void)
6556 {
6557 INT32 iImageIndex[ MAX_SORT_METHODS ] = { 0, 1, 5, 2, 3, 4 }; // sleep image is out or order (last)
6558
6559 const char* const filename = GetMLGFilename(MLG_GOLDPIECEBUTTONS);
6560
6561 for (INT32 i = 0; i < MAX_SORT_METHODS; ++i)
6562 {
6563 giMapSortButton[i] = QuickCreateButtonImg(filename, iImageIndex[i], iImageIndex[i] + 6, STD_SCREEN_X + gMapSortButtons[i].iX, STD_SCREEN_Y + gMapSortButtons[i].iY, MSYS_PRIORITY_HIGHEST - 5, MapSortBtnCallback);
6564 giMapSortButton[i]->SetUserData(i);
6565 giMapSortButton[i]->SetFastHelpText(wMapScreenSortButtonHelpText[i]);
6566 }
6567 }
6568
6569
6570 static INT16 CalcLocationValueForChar(const SOLDIERTYPE*);
6571 static INT32 GetContractExpiryTime(const SOLDIERTYPE* s);
6572 static void SwapCharactersInList(INT32 iCharA, INT32 iCharB);
6573
6574
SortListOfMercsInTeamPanel(BOOLEAN fRetainSelectedMercs)6575 static void SortListOfMercsInTeamPanel(BOOLEAN fRetainSelectedMercs)
6576 {
6577 INT32 iCounter = 0, iCounterA = 0;
6578 INT16 sEndSectorA, sEndSectorB;
6579 INT32 iExpiryTime, iExpiryTimeA;
6580
6581 SOLDIERTYPE* prev_selected_char = NULL;
6582 if (fRetainSelectedMercs) prev_selected_char = GetSelectedInfoChar();
6583
6584 // do the sort
6585 for( iCounter = 1; iCounter < FIRST_VEHICLE ; iCounter++ )
6586 {
6587 const SOLDIERTYPE* const a = gCharactersList[iCounter].merc;
6588 if (a == NULL) break;
6589
6590 switch( giSortStateForMapScreenList )
6591 {
6592 case( 0 ):
6593 // by name
6594 for( iCounterA = 0; iCounterA < FIRST_VEHICLE; iCounterA++ )
6595 {
6596 const SOLDIERTYPE* const b = gCharactersList[iCounterA].merc;
6597 if (b == NULL) break;
6598
6599 if (b->name.compare(a->name) > 0 && iCounterA < iCounter)
6600 {
6601 SwapCharactersInList( iCounter, iCounterA );
6602 }
6603 }
6604 break;
6605
6606 case( 1 ):
6607 // by assignment
6608 for( iCounterA = 0; iCounterA < FIRST_VEHICLE; iCounterA++ )
6609 {
6610 const SOLDIERTYPE* const b = gCharactersList[iCounterA].merc;
6611 if (b == NULL) break;
6612
6613 if (b->bAssignment > a->bAssignment && iCounterA < iCounter)
6614 {
6615 SwapCharactersInList( iCounter, iCounterA );
6616 }
6617 else if (b->bAssignment == a->bAssignment && iCounterA < iCounter)
6618 {
6619 // same assignment
6620
6621 // if it's in a vehicle
6622 if (b->bAssignment == VEHICLE)
6623 {
6624 // then also compare vehicle IDs
6625 if (b->iVehicleId > a->iVehicleId && iCounterA < iCounter)
6626 {
6627 SwapCharactersInList( iCounter, iCounterA );
6628 }
6629 }
6630 }
6631 }
6632 break;
6633
6634 case( 2 ):
6635 // by sleep status
6636 for( iCounterA = 0; iCounterA < FIRST_VEHICLE; iCounterA++ )
6637 {
6638 const SOLDIERTYPE* const b = gCharactersList[iCounterA].merc;
6639 if (b == NULL) break;
6640
6641 if (b->fMercAsleep && !a->fMercAsleep && iCounterA < iCounter)
6642 {
6643 SwapCharactersInList( iCounter, iCounterA );
6644 }
6645 }
6646 break;
6647
6648 case( 3 ):
6649 //by location
6650 sEndSectorA = CalcLocationValueForChar(a);
6651
6652 for( iCounterA = 0; iCounterA < FIRST_VEHICLE; iCounterA++ )
6653 {
6654 const SOLDIERTYPE* const b = gCharactersList[iCounterA].merc;
6655 if (b == NULL) break;
6656
6657 sEndSectorB = CalcLocationValueForChar(b);
6658
6659 if( ( sEndSectorB > sEndSectorA ) && ( iCounterA < iCounter ) )
6660 {
6661 SwapCharactersInList( iCounter, iCounterA );
6662 }
6663 }
6664 break;
6665
6666 case( 4 ):
6667 // by destination sector
6668 if (GetLengthOfMercPath(a) == 0)
6669 {
6670 sEndSectorA = 9999;
6671 }
6672 else
6673 {
6674 sEndSectorA = GetLastSectorIdInCharactersPath(a);
6675 }
6676
6677 for( iCounterA = 0; iCounterA < FIRST_VEHICLE; iCounterA++ )
6678 {
6679 const SOLDIERTYPE* const b = gCharactersList[iCounterA].merc;
6680 if (b == NULL) break;
6681
6682 if (GetLengthOfMercPath(b) == 0)
6683 {
6684 sEndSectorB = 9999;
6685 }
6686 else
6687 {
6688 sEndSectorB = GetLastSectorIdInCharactersPath(b);
6689 }
6690
6691 if( ( sEndSectorB > sEndSectorA ) && ( iCounterA < iCounter ) )
6692 {
6693 SwapCharactersInList( iCounter, iCounterA );
6694 }
6695 }
6696 break;
6697
6698 case( 5 ):
6699 iExpiryTime = GetContractExpiryTime(a);
6700
6701 //by contract expiry
6702 for( iCounterA = 0; iCounterA < FIRST_VEHICLE; iCounterA++ )
6703 {
6704 const SOLDIERTYPE* const b = gCharactersList[iCounterA].merc;
6705 if (b == NULL) break;
6706
6707 iExpiryTimeA = GetContractExpiryTime(b);
6708
6709 if( ( iExpiryTimeA > iExpiryTime ) && ( iCounterA < iCounter ) )
6710 {
6711 SwapCharactersInList( iCounter, iCounterA );
6712 }
6713 }
6714 break;
6715
6716 default:
6717 SLOGA("Invalid sorting mode for Merc List");
6718 return;
6719 }
6720 }
6721
6722
6723 if ( fRetainSelectedMercs )
6724 {
6725 for (size_t i = 0; i < MAX_CHARACTER_COUNT; ++i)
6726 {
6727 const SOLDIERTYPE* const s = gCharactersList[i].merc;
6728 if (s == NULL || !s->bActive) continue;
6729
6730 if (prev_selected_char == s) ChangeSelectedInfoChar(static_cast<INT8>(i), FALSE);
6731 }
6732 }
6733 else
6734 {
6735 // keep currently selected merc, but reset the selected list (which isn't saved/restored, that's why)
6736 ResetSelectedListForMapScreen( );
6737 }
6738
6739
6740 // reset blinking animations
6741 SetAllAutoFacesInactive( );
6742
6743 // dirty the screen parts affected
6744 fTeamPanelDirty = TRUE;
6745 fCharacterInfoPanelDirty = TRUE;
6746 }
6747
6748
SwapCharactersInList(INT32 iCharA,INT32 iCharB)6749 static void SwapCharactersInList(INT32 iCharA, INT32 iCharB)
6750 {
6751 const MapScreenCharacterSt temp = gCharactersList[iCharA];
6752 gCharactersList[iCharA] = gCharactersList[iCharB];
6753 gCharactersList[iCharB] = temp;
6754 }
6755
6756
RemoveTeamPanelSortButtonsForMapScreen()6757 static void RemoveTeamPanelSortButtonsForMapScreen()
6758 {
6759 FOR_EACH(GUIButtonRef, i, giMapSortButton) RemoveButton(*i);
6760 }
6761
6762
HandleCommonGlowTimer(void)6763 static void HandleCommonGlowTimer(void)
6764 {
6765 INT32 iCurrentTime = 0;
6766
6767 // grab the current time
6768 iCurrentTime = GetJA2Clock();
6769
6770 // only bother checking once flash interval has elapsed
6771 if( ( iCurrentTime - giCommonGlowBaseTime ) >= GLOW_DELAY )
6772 {
6773 // update timer so that we only run check so often
6774 giCommonGlowBaseTime = iCurrentTime;
6775
6776 // set flag to trigger glow higlight updates
6777 gfGlowTimerExpired = TRUE;
6778 }
6779 else
6780 {
6781 gfGlowTimerExpired = FALSE;
6782 }
6783 }
6784
6785
6786 // run through list of grunts and handle awating further orders
HandleAssignmentsDoneAndAwaitingFurtherOrders(void)6787 static void HandleAssignmentsDoneAndAwaitingFurtherOrders(void)
6788 {
6789 // update "nothing to do" flags if necessary
6790 if ( gfReEvaluateEveryonesNothingToDo )
6791 {
6792 ReEvaluateEveryonesNothingToDo();
6793 }
6794
6795 // grab the current time
6796 const INT32 iCurrentTime = GetJA2Clock();
6797
6798 // only bother checking once flash interval has elapsed
6799 if( ( iCurrentTime - giFlashAssignBaseTime ) >= ASSIGNMENT_DONE_FLASH_TIME )
6800 {
6801 // update timer so that we only run check so often
6802 giFlashAssignBaseTime = iCurrentTime;
6803
6804 CFOR_EACH_IN_CHAR_LIST(c)
6805 {
6806 // toggle and redraw if flash was left ON even though the flag is OFF
6807 if (c->merc->fDoneAssignmentAndNothingToDoFlag || fFlashAssignDone)
6808 {
6809 fFlashAssignDone = !fFlashAssignDone;
6810 fDrawCharacterList = TRUE;
6811
6812 // only need to find one
6813 break;
6814 }
6815 }
6816 }
6817 }
6818
6819
DisplayIconsForMercsAsleep(void)6820 static void DisplayIconsForMercsAsleep(void)
6821 {
6822 // run throught he list of grunts to see who is asleep and who isn't
6823 INT32 iCounter;
6824
6825 // if we are in inventory
6826 if (fShowInventoryFlag) return;
6827
6828 for( iCounter = 0; iCounter < MAX_CHARACTER_COUNT; iCounter++ )
6829 {
6830 const SOLDIERTYPE* const pSoldier = gCharactersList[iCounter].merc;
6831 if (pSoldier == NULL) continue;
6832
6833 if (pSoldier->bActive && pSoldier->fMercAsleep && CanChangeSleepStatusForSoldier(pSoldier))
6834 {
6835 BltVideoObject(guiSAVEBUFFER, guiSleepIcon, 0, STD_SCREEN_X + 125, Y_START + iCounter * (Y_SIZE + 2));
6836 }
6837 }
6838 }
6839
6840 //Kris: Added this function to blink the email icon on top of the laptop button whenever we are in
6841 // mapscreen and we have new email to read.
CheckForAndRenderNewMailOverlay(void)6842 static void CheckForAndRenderNewMailOverlay(void)
6843 {
6844 if( fNewMailFlag )
6845 {
6846 if( GetJA2Clock() % 1000 < 667 )
6847 {
6848 if (guiMapBottomExitButtons[MAP_EXIT_TO_LAPTOP]->Clicked())
6849 { //button is down, so offset the icon
6850 BltVideoObject(FRAME_BUFFER, guiNewMailIcons, 1, STD_SCREEN_X + 465, STD_SCREEN_Y + 418);
6851 InvalidateRegion( STD_SCREEN_X + 465, STD_SCREEN_Y + 418, STD_SCREEN_X + 480, STD_SCREEN_Y + 428 );
6852 }
6853 else
6854 { //button is up, so draw the icon normally
6855 BltVideoObject(FRAME_BUFFER, guiNewMailIcons, 0, STD_SCREEN_X + 464, STD_SCREEN_Y + 417);
6856 if (!guiMapBottomExitButtons[MAP_EXIT_TO_LAPTOP]->Enabled())
6857 {
6858 SGPRect area = { (UINT16)(STD_SCREEN_X + 463), (UINT16)(STD_SCREEN_Y + 417), (UINT16)(STD_SCREEN_X + 477), (UINT16)(STD_SCREEN_Y + 425) };
6859
6860 SGPVSurface::Lock l(FRAME_BUFFER);
6861 Blt16BPPBufferHatchRect(l.Buffer<UINT16>(), l.Pitch(), &area);
6862 }
6863 InvalidateRegion( STD_SCREEN_X + 463, STD_SCREEN_Y + 417, STD_SCREEN_X + 481, STD_SCREEN_Y + 430 );
6864
6865 }
6866 }
6867 else
6868 { //The blink is now off, so mark the button dirty so that it'll render next frame.
6869 MarkAButtonDirty( guiMapBottomExitButtons[ MAP_EXIT_TO_LAPTOP ] );
6870 }
6871 }
6872 }
6873
6874
CanToggleSelectedCharInventory()6875 static bool CanToggleSelectedCharInventory()
6876 {
6877 if (gfPreBattleInterfaceActive) return FALSE;
6878
6879 // already in inventory and an item picked up?
6880 if (fShowInventoryFlag && fMapInventoryItem)
6881 {
6882 return(FALSE);
6883 }
6884
6885 const SOLDIERTYPE* const pSoldier = GetSelectedInfoChar();
6886 // nobody selected?
6887 if (pSoldier == NULL) return FALSE;
6888
6889 // does the selected guy have inventory and can we get at it?
6890 if (!MapCharacterHasAccessibleInventory(*pSoldier)) return FALSE;
6891
6892 // if not in inventory, and holding an item from sector inventory
6893 if( !fShowInventoryFlag &&
6894 (gpItemPointer || fMapInventoryItem) &&
6895 ( gpItemPointerSoldier == NULL ) )
6896 {
6897 // make sure he's in that sector
6898 if ( ( pSoldier->sSectorX != sSelMapX ) ||
6899 ( pSoldier->sSectorY != sSelMapY ) ||
6900 ( pSoldier->bSectorZ != iCurrentMapSectorZ ) ||
6901 pSoldier->fBetweenSectors )
6902 {
6903 return(FALSE);
6904 }
6905 }
6906
6907
6908 // passed!
6909 return(TRUE);
6910 }
6911
6912
MapCharacterHasAccessibleInventory(SOLDIERTYPE const & s)6913 bool MapCharacterHasAccessibleInventory(SOLDIERTYPE const& s)
6914 {
6915 return
6916 s.bAssignment != IN_TRANSIT &&
6917 s.bAssignment != ASSIGNMENT_POW &&
6918 !IsMechanical(s) &&
6919 s.ubWhatKindOfMercAmI != MERC_TYPE__EPC &&
6920 s.bLife >= OKLIFE;
6921 }
6922
6923
CheckForInventoryModeCancellation(void)6924 static void CheckForInventoryModeCancellation(void)
6925 {
6926 if ( fShowInventoryFlag || fShowDescriptionFlag )
6927 {
6928 // can't bail while player has an item in hand...
6929 if (fMapInventoryItem) return;
6930
6931 if ( !CanToggleSelectedCharInventory( ) )
6932 {
6933 // get out of inventory mode if it's on! (could have just bled below OKLIFE)
6934 if ( fShowInventoryFlag )
6935 {
6936 fShowInventoryFlag = FALSE;
6937 fTeamPanelDirty = TRUE;
6938 }
6939
6940 // get out of inventory description if it's on!
6941 if ( fShowDescriptionFlag )
6942 {
6943 DeleteItemDescriptionBox( );
6944 }
6945 }
6946 }
6947 }
6948
6949
ChangeSelectedMapSector(INT16 sMapX,INT16 sMapY,INT8 bMapZ)6950 void ChangeSelectedMapSector( INT16 sMapX, INT16 sMapY, INT8 bMapZ )
6951 {
6952 // ignore while map inventory pool is showing, or else items can be replicated, since sector inventory always applies
6953 // only to the currently selected sector!!!
6954 if( fShowMapInventoryPool )
6955 return;
6956
6957 if ( gfPreBattleInterfaceActive )
6958 return;
6959
6960 if( !IsTheCursorAllowedToHighLightThisSector( sMapX, sMapY ) )
6961 return;
6962
6963 // disallow going underground while plotting (surface) movement
6964 if ( ( bMapZ != 0 ) && ( ( bSelectedDestChar != -1 ) || fPlotForHelicopter ) )
6965 return;
6966
6967
6968 sSelMapX = sMapX;
6969 sSelMapY = sMapY;
6970 iCurrentMapSectorZ = bMapZ;
6971
6972 // if going underground while in airspace mode
6973 if (bMapZ > 0 && fShowAircraftFlag)
6974 {
6975 // turn off airspace mode
6976 ToggleAirspaceMode( );
6977 }
6978
6979 fMapPanelDirty = TRUE;
6980 fMapScreenBottomDirty = TRUE;
6981
6982 // also need this, to update the text coloring of mercs in this sector
6983 fTeamPanelDirty = TRUE;
6984 }
6985
6986
CanChangeDestinationForChar(SOLDIERTYPE & s)6987 static bool CanChangeDestinationForChar(SOLDIERTYPE& s)
6988 {
6989 MoveError const ret = CanEntireMovementGroupMercIsInMove(s);
6990 if (ret == ME_OK) return true;
6991
6992 ReportMapScreenMovementError(ret);
6993 return false;
6994 }
6995
6996
CanExtendContractForSoldier(const SOLDIERTYPE * const s)6997 BOOLEAN CanExtendContractForSoldier(const SOLDIERTYPE* const s)
6998 {
6999 Assert(s);
7000 Assert(s->bActive);
7001
7002 // if a vehicle, in transit, or a POW
7003 if (s->uiStatusFlags & SOLDIER_VEHICLE ||
7004 s->bAssignment == IN_TRANSIT ||
7005 s->bAssignment == ASSIGNMENT_POW)
7006 {
7007 // can't extend contracts at this time
7008 return (FALSE);
7009 }
7010
7011 // mercs below OKLIFE, M.E.R.C. mercs, EPCs, and the Robot use the Contract menu so they can be DISMISSED/ABANDONED!
7012
7013 // everything OK
7014 return( TRUE );
7015 }
7016
7017
CanChangeSleepStatusForSoldier(const SOLDIERTYPE * const pSoldier)7018 BOOLEAN CanChangeSleepStatusForSoldier(const SOLDIERTYPE* const pSoldier)
7019 {
7020 // valid soldier?
7021 Assert( pSoldier );
7022 Assert( pSoldier->bActive );
7023
7024 // if a vehicle, robot, in transit, or a POW
7025 if (IsMechanical(*pSoldier) ||
7026 ( pSoldier->bAssignment == IN_TRANSIT ) || ( pSoldier->bAssignment == ASSIGNMENT_POW ) )
7027 {
7028 // can't change the sleep status of such mercs
7029 return ( FALSE );
7030 }
7031
7032 // if dead
7033 if( ( pSoldier->bLife <= 0 ) || ( pSoldier->bAssignment == ASSIGNMENT_DEAD ) )
7034 {
7035 return ( FALSE );
7036 }
7037
7038 // this merc MAY be able to sleep/wake up - we'll allow player to click and find out
7039 return( TRUE );
7040 }
7041
7042
ChangeMapScreenMaskCursor(UINT16 usCursor)7043 static void ChangeMapScreenMaskCursor(UINT16 usCursor)
7044 {
7045 MSYS_SetCurrentCursor( usCursor );
7046 gMapScreenMaskRegion.ChangeCursor(usCursor);
7047
7048 if ( usCursor == CURSOR_CHECKMARK )
7049 fCheckCursorWasSet = TRUE;
7050 else
7051 fCheckCursorWasSet = FALSE;
7052
7053 if ( usCursor == CURSOR_NORMAL )
7054 {
7055 if ( !InItemStackPopup( ) )
7056 {
7057 // cancel mouse restriction
7058 FreeMouseCursor();
7059 }
7060 }
7061 else
7062 {
7063 // restrict mouse cursor to the map area
7064 RestrictMouseCursor( &MapScreenRect );
7065 }
7066 }
7067
7068
7069 static void ExplainWhySkyriderCantFly(void);
7070
7071
CancelOrShortenPlottedPath(void)7072 static void CancelOrShortenPlottedPath(void)
7073 {
7074 INT16 sMapX, sMapY;
7075 UINT32 uiReturnValue;
7076
7077
7078 GetMouseMapXY(&sMapX, &sMapY);
7079
7080 // check if we are in aircraft mode
7081 if (fShowAircraftFlag)
7082 {
7083 // check for helicopter path being plotted
7084 if( !fPlotForHelicopter )
7085 return;
7086
7087 // if player can't redirect it
7088 if (!CanHelicopterFly())
7089 {
7090 // explain & ignore
7091 ExplainWhySkyriderCantFly();
7092 return;
7093 }
7094
7095
7096 // try to delete portion of path AFTER the current sector for the helicopter
7097 uiReturnValue = ClearPathAfterThisSectorForHelicopter( sMapX, sMapY );
7098 }
7099 else
7100 {
7101 // check for character path being plotted
7102 if( bSelectedDestChar == -1 )
7103 return;
7104
7105 // try to delete portion of path AFTER the current sector for the helicopter
7106 uiReturnValue = ClearPathAfterThisSectorForCharacter(gCharactersList[bSelectedDestChar].merc, sMapX, sMapY);
7107 }
7108
7109
7110 switch ( uiReturnValue )
7111 {
7112 case ABORT_PLOTTING:
7113 AbortMovementPlottingMode( );
7114 break;
7115
7116 case PATH_CLEARED: // movement was canceled
7117 // message was already issued when path was cleared
7118 DestinationPlottingCompleted();
7119 break;
7120
7121 case PATH_SHORTENED: // route was shortened but isn't completely gone
7122 // display "route shortened" message
7123 BeginMapUIMessage(0, pMapPlotStrings[4]);
7124 break;
7125
7126 default:
7127 Assert( FALSE );
7128 break;
7129 }
7130
7131 // this triggers the path node animation to reset itself back to the first node
7132 fDeletedNode = TRUE;
7133
7134 fMapPanelDirty = TRUE;
7135
7136 fTeamPanelDirty = TRUE;
7137 fCharacterInfoPanelDirty = TRUE; // to update ETAs if path reversed or shortened
7138 }
7139
7140
HandleCtrlOrShiftInTeamPanel(INT8 bCharNumber)7141 static BOOLEAN HandleCtrlOrShiftInTeamPanel(INT8 bCharNumber)
7142 {
7143 // check if shift or ctrl held down, if so, set values in list
7144 if (_KeyDown(CTRL))
7145 {
7146 ToggleEntryInSelectedList( bCharNumber );
7147
7148 fTeamPanelDirty = TRUE;
7149 fCharacterInfoPanelDirty = TRUE;
7150
7151 return( TRUE);
7152 }
7153 else if( _KeyDown(SHIFT) )
7154 {
7155 // build a list from the bSelectedInfoChar To here, reset everyone
7156
7157 //empty the list
7158 ResetSelectedListForMapScreen( );
7159 // rebuild the list
7160 BuildSelectedListFromAToB( bSelectedInfoChar, bCharNumber );
7161
7162 fTeamPanelDirty = TRUE;
7163 fCharacterInfoPanelDirty = TRUE;
7164
7165 return( TRUE );
7166 }
7167
7168 return( FALSE );
7169 }
7170
7171
GetContractExpiryTime(const SOLDIERTYPE * const pSoldier)7172 static INT32 GetContractExpiryTime(const SOLDIERTYPE* const pSoldier)
7173 {
7174 if( ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC ) || ( pSoldier->ubProfile == SLAY ) )
7175 {
7176 return ( pSoldier->iEndofContractTime );
7177 }
7178 else
7179 {
7180 // never - really high number
7181 return ( 999999 );
7182 }
7183 }
7184
7185
GetSelectedInfoChar(void)7186 SOLDIERTYPE* GetSelectedInfoChar(void)
7187 {
7188 if (bSelectedInfoChar == -1) return NULL;
7189 Assert(0 <= bSelectedInfoChar && bSelectedInfoChar < MAX_CHARACTER_COUNT);
7190 SOLDIERTYPE* const s = gCharactersList[bSelectedInfoChar].merc;
7191 if (s == NULL) return NULL;
7192 Assert(s->bActive);
7193 return s;
7194 }
7195
7196
ChangeSelectedInfoChar(INT8 bCharNumber,BOOLEAN fResetSelectedList)7197 void ChangeSelectedInfoChar( INT8 bCharNumber, BOOLEAN fResetSelectedList )
7198 {
7199 Assert( ( bCharNumber >= -1 ) && ( bCharNumber < MAX_CHARACTER_COUNT ) );
7200
7201 const SOLDIERTYPE* s;
7202 if (bCharNumber != -1)
7203 {
7204 s = gCharactersList[bCharNumber].merc;
7205 if (s == NULL) return;
7206 }
7207 else
7208 {
7209 s = NULL;
7210 }
7211
7212 // if holding an item
7213 if (gpItemPointer || fMapInventoryItem)
7214 {
7215 // make sure we can give it to this guy, otherwise don't allow the change
7216 if (s == NULL || !MapscreenCanPassItemToChar(s))
7217 {
7218 return;
7219 }
7220 }
7221
7222
7223 if ( fResetSelectedList )
7224 {
7225 // reset selections of all listed characters. Do this even if this guy is already selected.
7226 // NOTE: this keeps the currently selected info char selected
7227 ResetSelectedListForMapScreen( );
7228 }
7229
7230
7231 // if this is really a change
7232 if ( bSelectedInfoChar != bCharNumber )
7233 {
7234 // if resetting, and another guy was selected
7235 if ( fResetSelectedList && ( bSelectedInfoChar != -1 ) )
7236 {
7237 // deselect previously selected character
7238 ResetEntryForSelectedList( bSelectedInfoChar );
7239 }
7240
7241 bSelectedInfoChar = bCharNumber;
7242
7243 if ( bCharNumber != -1 )
7244 {
7245 // the selected guy must always be ON in the list of selected chars
7246 SetEntryInSelectedCharacterList( bCharNumber );
7247 }
7248
7249 // if we're in the inventory panel
7250 if ( fShowInventoryFlag )
7251 {
7252 // and we're changing to nobody or a guy whose inventory can't be accessed
7253 if (!s || !MapCharacterHasAccessibleInventory(*s))
7254 {
7255 // then get out of inventory mode
7256 fShowInventoryFlag = FALSE;
7257 }
7258 }
7259
7260 fCharacterInfoPanelDirty = TRUE;
7261
7262 // if showing sector inventory
7263 if ( fShowMapInventoryPool )
7264 {
7265 // redraw right side to update item hatches
7266 fMapPanelDirty = TRUE;
7267 }
7268 }
7269
7270 fTeamPanelDirty = TRUE;
7271 }
7272
7273
7274
CopyPathToAllSelectedCharacters(PathSt * pPath)7275 void CopyPathToAllSelectedCharacters(PathSt* pPath)
7276 {
7277 // run through list and copy paths for each selected character
7278 CFOR_EACH_IN_CHAR_LIST(c)
7279 {
7280 if (!c->selected) continue;
7281
7282 SOLDIERTYPE* const pSoldier = c->merc;
7283 PathSt* const cur_path = GetSoldierMercPathPtr(pSoldier);
7284 // skip itself!
7285 if (cur_path != pPath)
7286 {
7287 ClearStrategicPathList(cur_path, 0);
7288 if (pSoldier->uiStatusFlags & SOLDIER_VEHICLE)
7289 {
7290 pVehicleList[pSoldier->bVehicleID].pMercPath = CopyPaths(pPath);
7291 }
7292 else if( pSoldier->bAssignment == VEHICLE )
7293 {
7294 pVehicleList[pSoldier->iVehicleId].pMercPath = CopyPaths(pPath);
7295 }
7296 else
7297 {
7298 pSoldier->pMercPath = CopyPaths(pPath);
7299 }
7300
7301 // don't use CopyPathToCharactersSquadIfInOne(), it will whack the original pPath by replacing that merc's path!
7302 }
7303 }
7304 }
7305
7306
7307
CancelPathsOfAllSelectedCharacters()7308 void CancelPathsOfAllSelectedCharacters()
7309 {
7310 BOOLEAN fSkyriderMsgShown = FALSE;
7311
7312 // cancel destination for the clicked and ALL other valid & selected characters with a route set
7313 CFOR_EACH_SELECTED_IN_CHAR_LIST(c)
7314 {
7315 SOLDIERTYPE* const pSoldier = c->merc;
7316 // and he has a route set
7317 if ( GetLengthOfMercPath( pSoldier ) > 0 )
7318 {
7319 // if he's in the chopper, but player can't redirect it
7320 if (InHelicopter(*pSoldier) && !CanHelicopterFly())
7321 {
7322 if ( !fSkyriderMsgShown )
7323 {
7324 // explain
7325 ExplainWhySkyriderCantFly();
7326 fSkyriderMsgShown = TRUE;
7327 }
7328
7329 // don't cancel, ignore
7330 continue;
7331 }
7332
7333
7334 // cancel the entire path (also clears vehicles for any passengers selected, and handles reversing directions)
7335 if( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
7336 {
7337 CancelPathForVehicle(pVehicleList[pSoldier->bVehicleID], FALSE);
7338 }
7339 else
7340 {
7341 CancelPathForCharacter( pSoldier );
7342 }
7343 }
7344 }
7345 }
7346
7347
ConvertMinTimeToETADayHourMinString(const UINT32 uiTimeInMin)7348 static ST::string ConvertMinTimeToETADayHourMinString(const UINT32 uiTimeInMin)
7349 {
7350 UINT32 uiDay, uiHour, uiMin;
7351
7352 uiDay = ( uiTimeInMin / NUM_MIN_IN_DAY );
7353 uiHour = ( uiTimeInMin - ( uiDay * NUM_MIN_IN_DAY ) ) / NUM_MIN_IN_HOUR;
7354 uiMin = uiTimeInMin - ( ( uiDay * NUM_MIN_IN_DAY ) + ( uiHour * NUM_MIN_IN_HOUR ) );
7355
7356 // there ain't enough room to show both the day and ETA: and without ETA it's confused as the current time
7357 //return ST::format("{} {} {}, {02d}:{02d}", pEtaString, pDayStrings, uiDay, uiHour, uiMin);
7358 //return ST::format("{} {}, {02d}:{02d}", pDayStrings, uiDay, uiHour, uiMin);
7359 return ST::format("{} {02d}:{02d}", pEtaString, uiHour, uiMin);
7360 }
7361
7362
GetGroundTravelTimeOfSoldier(const SOLDIERTYPE * const s)7363 static INT32 GetGroundTravelTimeOfSoldier(const SOLDIERTYPE* const s)
7364 {
7365 INT32 iTravelTime = 0;
7366
7367 // get travel time for the last path segment (stored in pTempCharacterPath)
7368 iTravelTime = GetPathTravelTimeDuringPlotting( pTempCharacterPath );
7369
7370 // add travel time for any prior path segments (stored in the selected character's mercpath, but waypoints aren't built)
7371 iTravelTime += GetPathTravelTimeDuringPlotting(GetSoldierMercPathPtr(s));
7372
7373 return( iTravelTime );
7374 }
7375
7376
CalcLocationValueForChar(const SOLDIERTYPE * const s)7377 static INT16 CalcLocationValueForChar(const SOLDIERTYPE* const s)
7378 {
7379 // don't reveal location of POWs!
7380 if (s->bAssignment == ASSIGNMENT_POW) return 0;
7381
7382 return
7383 SECTOR(s->sSectorX, s->sSectorY) +
7384 s->bSectorZ * 1000; // underground: add 1000 per sublevel
7385 }
7386
7387
CancelChangeArrivalSectorMode(void)7388 static void CancelChangeArrivalSectorMode(void)
7389 {
7390 // "put him in change arrival sector" mode
7391 gfInChangeArrivalSectorMode = FALSE;
7392
7393 // change the cursor to that mode
7394 SetUpCursorForStrategicMap();
7395
7396 fMapPanelDirty = TRUE;
7397 }
7398
7399
MakeMapModesSuitableForDestPlotting(const SOLDIERTYPE * const pSoldier)7400 static void MakeMapModesSuitableForDestPlotting(const SOLDIERTYPE* const pSoldier)
7401 {
7402 CancelSectorInventoryDisplayIfOn(FALSE);
7403
7404 TurnOnShowTeamsMode();
7405
7406 if (InHelicopter(*pSoldier))
7407 {
7408 if (!fShowAircraftFlag)
7409 {
7410 // turn on airspace mode automatically
7411 ToggleAirspaceMode();
7412 }
7413 }
7414 else
7415 {
7416 if (fShowAircraftFlag)
7417 {
7418 // turn off airspace mode automatically
7419 ToggleAirspaceMode();
7420 }
7421 }
7422
7423 // if viewing a different sublevel
7424 if (iCurrentMapSectorZ != pSoldier->bSectorZ)
7425 {
7426 // switch to that merc's sublevel
7427 JumpToLevel(pSoldier->bSectorZ);
7428 }
7429 }
7430
7431
AnyMovableCharsInOrBetweenThisSector(INT16 sSectorX,INT16 sSectorY,INT8 bSectorZ)7432 static BOOLEAN AnyMovableCharsInOrBetweenThisSector(INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ)
7433 {
7434 CFOR_EACH_IN_TEAM(pSoldier, OUR_TEAM)
7435 {
7436 // POWs, dead guys, guys in transit can't move
7437 if ( ( pSoldier->bAssignment == IN_TRANSIT ) ||
7438 ( pSoldier->bAssignment == ASSIGNMENT_POW ) ||
7439 ( pSoldier->bAssignment == ASSIGNMENT_DEAD ) ||
7440 ( pSoldier->bLife == 0 ) )
7441 {
7442 continue;
7443 }
7444
7445 // don't count mercs aboard Skyrider
7446 if (InHelicopter(*pSoldier)) continue;
7447
7448 // don't count vehicles - in order for them to move, somebody must be in the sector with them
7449 if ( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
7450 {
7451 continue;
7452 }
7453
7454
7455 // is he here?
7456 if( ( pSoldier->sSectorX == sSectorX ) && ( pSoldier->sSectorY == sSectorY ) && ( pSoldier->bSectorZ == bSectorZ ) )
7457 {
7458 // NOTE that we consider mercs between sectors, mercs < OKLIFE, and sleeping mercs to be "movable".
7459 // This lets CanCharacterMoveInStrategic() itself report the appropriate error message when character is clicked
7460 return( TRUE );
7461 }
7462 }
7463
7464
7465 return( FALSE );
7466 }
7467
7468
7469 static UINT8 PlayerMercsInHelicopterSector();
7470
7471
RequestGiveSkyriderNewDestination(void)7472 static BOOLEAN RequestGiveSkyriderNewDestination(void)
7473 {
7474 // should we allow it?
7475 if (CanHelicopterFly())
7476 {
7477 // if not warned already, and chopper empty, but mercs are in this sector
7478 if ( !gfSkyriderEmptyHelpGiven &&
7479 ( GetNumberInVehicle(GetHelicopter()) == 0 ) &&
7480 ( PlayerMercsInHelicopterSector() > 0 ) )
7481 {
7482 DoMapMessageBox(MSG_BOX_BASIC_STYLE, pSkyriderText[2], MAP_SCREEN, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback);
7483 gfSkyriderEmptyHelpGiven = TRUE;
7484 return( FALSE );
7485 }
7486
7487 // say Yo!
7488 SkyRiderTalk( SKYRIDER_SAYS_HI );
7489
7490 // start plotting helicopter movement
7491 fPlotForHelicopter = TRUE;
7492
7493 // change cursor to the helicopter
7494 SetUpCursorForStrategicMap();
7495
7496 // remember the helicopter's current path so we can restore it if need be
7497 ClearStrategicPathList(g_prev_path, 0);
7498 g_prev_path = CopyPaths(GetHelicopter().pMercPath);
7499
7500 // affects Skyrider's dialogue
7501 SetFactTrue( FACT_SKYRIDER_USED_IN_MAPSCREEN );
7502
7503 return( TRUE );
7504 }
7505 else // not allowed to reroute the chopper right now
7506 {
7507 // tell player why not
7508 ExplainWhySkyriderCantFly();
7509
7510 return( FALSE );
7511 }
7512 }
7513
7514
ExplainWhySkyriderCantFly(void)7515 static void ExplainWhySkyriderCantFly(void)
7516 {
7517 // do we owe him money?
7518 if( gMercProfiles[ SKYRIDER ].iBalance < 0 )
7519 {
7520 // overdue cash
7521 SkyRiderTalk( OWED_MONEY_TO_SKYRIDER );
7522 return;
7523 }
7524
7525 // is he returning to base?
7526 if( fHeliReturnStraightToBase )
7527 {
7528 // returning to base
7529 SkyRiderTalk( RETURN_TO_BASE );
7530 return;
7531 }
7532
7533 // grounded by enemies in sector?
7534 if (!CanHelicopterTakeOff())
7535 {
7536 SkyRiderTalk( CHOPPER_NOT_ACCESSIBLE );
7537 return;
7538 }
7539
7540 // Drassen too disloyal to wanna help player?
7541 if ( CheckFact( FACT_LOYALTY_LOW, SKYRIDER ) )
7542 {
7543 SkyRiderTalk( DOESNT_WANT_TO_FLY );
7544 return;
7545 }
7546
7547 // no explainable reason
7548 }
7549
7550
PlayerMercsInHelicopterSector()7551 static UINT8 PlayerMercsInHelicopterSector()
7552 {
7553 GROUP& g = *GetGroup(GetHelicopter().ubMovementGroup);
7554 if (g.fBetweenSectors) return 0;
7555 return PlayerMercsInSector(g.ubSectorX, g.ubSectorY, 0);
7556 }
7557
7558
7559 static void RandomAwakeSelectedMercConfirmsStrategicMove(void);
7560 static void WakeUpAnySleepingSelectedMercsOnFootOrDriving();
7561
7562
HandleNewDestConfirmation(INT16 sMapX,INT16 sMapY)7563 static void HandleNewDestConfirmation(INT16 sMapX, INT16 sMapY)
7564 {
7565 UINT8 ubCurrentProgress;
7566
7567
7568 // if moving the chopper itself, or moving a character aboard the chopper
7569 if( fPlotForHelicopter )
7570 {
7571 // if there are no enemies in destination sector, or we don't know
7572 if ( ( NumEnemiesInSector( sMapX, sMapY ) == 0 ) ||
7573 ( WhatPlayerKnowsAboutEnemiesInSector( sMapX, sMapY ) == KNOWS_NOTHING ) )
7574 {
7575 // no problem
7576
7577 // get current player progress
7578 ubCurrentProgress = CurrentPlayerProgressPercentage();
7579
7580 // if we're doing a lot better than last time he said anything
7581 if ( ( ubCurrentProgress > gubPlayerProgressSkyriderLastCommentedOn ) &&
7582 ( ( ubCurrentProgress - gubPlayerProgressSkyriderLastCommentedOn ) >= MIN_PROGRESS_FOR_SKYRIDER_QUOTE_DOING_WELL ) )
7583 {
7584 // kicking ass!
7585 SkyRiderTalk( THINGS_ARE_GOING_WELL );
7586
7587 gubPlayerProgressSkyriderLastCommentedOn = ubCurrentProgress;
7588 }
7589 // if we're doing noticably worse than last time he said anything
7590 else if ( ( ubCurrentProgress < gubPlayerProgressSkyriderLastCommentedOn ) &&
7591 ( ( gubPlayerProgressSkyriderLastCommentedOn - ubCurrentProgress ) >= MIN_REGRESS_FOR_SKYRIDER_QUOTE_DOING_BADLY ) )
7592 {
7593 // sucking rocks!
7594 SkyRiderTalk( THINGS_ARE_GOING_BADLY );
7595
7596 gubPlayerProgressSkyriderLastCommentedOn = ubCurrentProgress;
7597 }
7598 else
7599 {
7600 // ordinary confirmation quote
7601 SkyRiderTalk( CONFIRM_DESTINATION );
7602 }
7603 }
7604 else
7605 {
7606 // ok, but... you know there are enemies there...
7607 SkyRiderTalk( BELIEVED_ENEMY_SECTOR );
7608 }
7609 }
7610 else
7611 {
7612 RandomAwakeSelectedMercConfirmsStrategicMove( );
7613
7614 // tell player the route was CONFIRMED
7615 // NOTE: We don't this this for the helicopter any more, since it clashes with Skyrider's own confirmation msg
7616 BeginMapUIMessage(0, pMapPlotStrings[1]);
7617 }
7618
7619
7620 // wake up anybody who needs to be awake to travel
7621 WakeUpAnySleepingSelectedMercsOnFootOrDriving();
7622 }
7623
7624
RandomAwakeSelectedMercConfirmsStrategicMove(void)7625 static void RandomAwakeSelectedMercConfirmsStrategicMove(void)
7626 {
7627 INT32 iCounter;
7628 SOLDIERTYPE* selected_merc[20];
7629 UINT8 ubSelectedMercIndex[ 20 ];
7630 UINT8 ubNumMercs = 0;
7631 UINT8 ubChosenMerc;
7632
7633 for( iCounter = 0; iCounter < MAX_CHARACTER_COUNT; iCounter++ )
7634 {
7635 const MapScreenCharacterSt* const c = &gCharactersList[iCounter];
7636 if (!c->selected) continue;
7637
7638 SOLDIERTYPE* const pSoldier = c->merc;
7639 if (pSoldier->bLife >= OKLIFE &&
7640 !IsMechanical(*pSoldier) &&
7641 !AM_AN_EPC(pSoldier) &&
7642 !pSoldier->fMercAsleep)
7643 {
7644 selected_merc[ubNumMercs] = pSoldier;
7645 ubSelectedMercIndex[ ubNumMercs ] = (UINT8)iCounter;
7646
7647 ubNumMercs++;
7648 }
7649 }
7650
7651 if ( ubNumMercs > 0 )
7652 {
7653 ubChosenMerc = (UINT8)Random( ubNumMercs );
7654
7655 // select that merc so that when he speaks we're showing his portrait and not someone else
7656 ChangeSelectedInfoChar( ubSelectedMercIndex[ ubChosenMerc ], FALSE );
7657
7658 DoMercBattleSound(selected_merc[ubChosenMerc], BATTLE_SOUND_OK1);
7659 //TacticalCharacterDialogue(selected_merc[ubChosenMerc], ubQuoteNum);
7660 }
7661 }
7662
7663
DestinationPlottingCompleted(void)7664 static void DestinationPlottingCompleted(void)
7665 {
7666 // clear previous paths for selected characters and helicopter
7667 ClearPreviousPaths();
7668
7669 fPlotForHelicopter = FALSE;
7670 bSelectedDestChar = - 1;
7671 giDestHighLine = -1;
7672
7673 fMapPanelDirty = TRUE;
7674
7675 // reset cursor
7676 SetUpCursorForStrategicMap( );
7677
7678 fJustFinishedPlotting = TRUE;
7679 fEndPlotting = TRUE;
7680 }
7681
7682
HandleMilitiaRedistributionClick(void)7683 static void HandleMilitiaRedistributionClick(void)
7684 {
7685 ST::string sString;
7686
7687
7688 // if on the surface
7689 if ( iCurrentMapSectorZ == 0 )
7690 {
7691 INT8 const bTownId = GetTownIdForSector(SECTOR(sSelMapX, sSelMapY));
7692 BOOLEAN fTownStillHidden = !IsTownFound(bTownId);
7693
7694 if( ( bTownId != BLANK_SECTOR ) && !fTownStillHidden )
7695 {
7696 if ( MilitiaTrainingAllowedInSector( sSelMapX, sSelMapY, ( INT8 )iCurrentMapSectorZ ) )
7697 {
7698 fShowTownInfo = FALSE;
7699 fMapPanelDirty = TRUE;
7700
7701 // check if there's combat in any of the town's sectors
7702 if ( CanRedistributeMilitiaInSector( sSelMapX, sSelMapY, bTownId ) )
7703 {
7704 // Nope, ok, set selected militia town
7705 sSelectedMilitiaTown = bTownId;
7706 }
7707 else
7708 {
7709 // can't redistribute militia during combat!
7710 DoScreenIndependantMessageBox( pMilitiaString[ 2 ], MSG_BOX_FLAG_OK, NULL );
7711 }
7712 }
7713 else
7714 {
7715 // can't have militia in this town
7716 sString = st_format_printf(pMapErrorString[ 31 ], GCM->getTownName(bTownId));
7717 DoScreenIndependantMessageBox( sString, MSG_BOX_FLAG_OK, NULL );
7718 }
7719 }
7720 else
7721 {
7722 INT8 const sam_id = GetSAMIdFromSector(sSelMapX, sSelMapY, 0);
7723 if (sam_id != -1 && IsSecretFoundAt(SECTOR(sSelMapX, sSelMapY)))
7724 { // Cannot move militia around sam sites.
7725 DoScreenIndependantMessageBox(pMapErrorString[30], MSG_BOX_FLAG_OK, NULL);
7726 }
7727 }
7728 }
7729 }
7730
StartChangeSectorArrivalMode(void)7731 static void StartChangeSectorArrivalMode(void)
7732 {
7733 // "put him in change arrival sector" mode
7734 gfInChangeArrivalSectorMode = TRUE;
7735
7736 // redraw map with bullseye removed
7737 fMapPanelDirty = TRUE;
7738
7739 // change the cursor to that mode
7740 SetUpCursorForStrategicMap();
7741
7742 // give instructions as overlay message
7743 BeginMapUIMessage(0, pBullseyeStrings[0]);
7744 }
7745
7746
CanMoveBullseyeAndClickedOnIt(INT16 sMapX,INT16 sMapY)7747 static BOOLEAN CanMoveBullseyeAndClickedOnIt(INT16 sMapX, INT16 sMapY)
7748 {
7749 // if in airspace mode, and not plotting paths
7750 if (fShowAircraftFlag &&
7751 bSelectedDestChar == -1 &&
7752 !fPlotForHelicopter)
7753 {
7754 // don't allow moving bullseye until after initial arrival
7755 if (!DidGameJustStart())
7756 {
7757 // if he clicked on the bullseye, and we're on the surface level
7758 if (g_merc_arrive_sector == SECTOR(sMapX, sMapY) && iCurrentMapSectorZ == 0)
7759 {
7760 return( TRUE );
7761 }
7762 }
7763 }
7764
7765 return( FALSE );
7766 }
7767
7768
7769 static void BullsEyeOrChopperSelectionPopupCallback(MessageBoxReturnValue);
7770
7771
CreateBullsEyeOrChopperSelectionPopup(void)7772 static void CreateBullsEyeOrChopperSelectionPopup(void)
7773 {
7774 gzUserDefinedButton1 = pHelicopterEtaStrings[ 8 ];
7775 gzUserDefinedButton2 = pHelicopterEtaStrings[ 9 ];
7776
7777 // do a BULLSEYE/CHOPPER message box
7778 DoScreenIndependantMessageBox( pHelicopterEtaStrings[ 7 ], MSG_BOX_FLAG_GENERIC, BullsEyeOrChopperSelectionPopupCallback );
7779 }
7780
7781
BullsEyeOrChopperSelectionPopupCallback(MessageBoxReturnValue const ubExitValue)7782 static void BullsEyeOrChopperSelectionPopupCallback(MessageBoxReturnValue const ubExitValue)
7783 {
7784 // button 1 pressed?
7785 if ( ubExitValue == MSG_BOX_RETURN_YES )
7786 {
7787 // chose chopper
7788 // have to set a flag 'cause first call to RequestGiveSkyriderNewDestination() triggers another msg box & won't work
7789 gfRequestGiveSkyriderNewDestination = TRUE;
7790 }
7791 // button 2 pressed?
7792 else if( ubExitValue == MSG_BOX_RETURN_NO )
7793 {
7794 // chose bullseye
7795 StartChangeSectorArrivalMode();
7796 }
7797 }
7798
7799
7800 // Wake up anybody who needs to be awake to travel
WakeUpAnySleepingSelectedMercsOnFootOrDriving()7801 static void WakeUpAnySleepingSelectedMercsOnFootOrDriving()
7802 {
7803 CFOR_EACH_SELECTED_IN_CHAR_LIST(i)
7804 {
7805 SOLDIERTYPE& s = *i->merc;
7806 if (!s.fMercAsleep) continue;
7807
7808 // On foot or driving?
7809 if (s.bAssignment >= ON_DUTY && (s.bAssignment != VEHICLE || !SoldierMustDriveVehicle(s, false))) continue;
7810
7811 /* We should be guaranteed that he CAN wake up to get this far, so report
7812 * errors, but don't force it */
7813 bool const success = SetMercAwake(&s, TRUE, FALSE);
7814 (void)success;
7815 Assert(success);
7816 }
7817 }
7818
7819
HandlePostAutoresolveMessages(void)7820 static void HandlePostAutoresolveMessages(void)
7821 {
7822 //KM: Autoresolve sets up this global sector value whenever the enemy gains control of a sector. As soon as
7823 //we leave autoresolve and enter mapscreen, then this gets called and handles ownership change for the sector.
7824 //It also brings up a dialog stating to the player what happened, however, the internals of those functions
7825 //breaks autoresolve and the game crashes after autoresolve is finished. The value doesn't need to be saved.
7826 //
7827 //An additional case is when creatures kill all opposition in the sector. For each surviving monster, civilians
7828 //are "virtually" murdered and loyalty hits will be processed.
7829 if( gsCiviliansEatenByMonsters >= 1 )
7830 {
7831 AdjustLoyaltyForCivsEatenByMonsters( (UINT8)SECTORX( gsEnemyGainedControlOfSectorID ),
7832 (UINT8)SECTORY( gsEnemyGainedControlOfSectorID ),
7833 (UINT8)gsCiviliansEatenByMonsters );
7834 gsCiviliansEatenByMonsters = -2;
7835 }
7836 else if( gsCiviliansEatenByMonsters == -2 )
7837 {
7838 fMapPanelDirty = TRUE;
7839 gsCiviliansEatenByMonsters = -1;
7840 gsEnemyGainedControlOfSectorID = -1;
7841 }
7842 else if( gsEnemyGainedControlOfSectorID >= 0 )
7843 { //bring up the dialog box
7844 SetThisSectorAsEnemyControlled(SECTORX(gsEnemyGainedControlOfSectorID), SECTORY(gsEnemyGainedControlOfSectorID), 0);
7845 gsEnemyGainedControlOfSectorID = -2;
7846 }
7847 else if( gsEnemyGainedControlOfSectorID == -2 )
7848 { //dirty the mapscreen after the dialog box goes away.
7849 fMapPanelDirty = TRUE;
7850 gsEnemyGainedControlOfSectorID = -1;
7851 }
7852 else if( gbMilitiaPromotions )
7853 {
7854 ST::string str = BuildMilitiaPromotionsString();
7855 DoScreenIndependantMessageBox( str, MSG_BOX_FLAG_OK, MapScreenDefaultOkBoxCallback );
7856 }
7857 }
7858
7859
GetMapscreenMercAssignmentString(SOLDIERTYPE const & s)7860 ST::string GetMapscreenMercAssignmentString(SOLDIERTYPE const& s)
7861 {
7862 return
7863 s.bAssignment == VEHICLE ? pShortVehicleStrings[GetVehicle(s.iVehicleId).ubVehicleType] :
7864 pAssignmentStrings[s.bAssignment];
7865 }
7866
7867
GetMapscreenMercLocationString(SOLDIERTYPE const & s)7868 ST::string GetMapscreenMercLocationString(SOLDIERTYPE const& s)
7869 {
7870 if (s.bAssignment == IN_TRANSIT)
7871 { // Show blank
7872 return "--";
7873 }
7874 else if (s.bAssignment == ASSIGNMENT_POW)
7875 { // POW - location unknown
7876 return pPOWStrings[1];
7877 }
7878 else
7879 { // Put parentheses around it when he's between sectors
7880 return ST::format(s.fBetweenSectors ? "({}{}{})" : "{}{}{}", pMapVertIndex[s.sSectorY], pMapHortIndex[s.sSectorX], pMapDepthIndex[s.bSectorZ]);
7881 }
7882 }
7883
7884
GetMapscreenMercDestinationString(SOLDIERTYPE const & s)7885 ST::string GetMapscreenMercDestinationString(SOLDIERTYPE const& s)
7886 {
7887 /* If dead or POW - has no destination (no longer part of a group, for that
7888 * matter) */
7889 if (s.bAssignment != ASSIGNMENT_DEAD &&
7890 s.bAssignment != ASSIGNMENT_POW &&
7891 s.bLife != 0)
7892 {
7893 INT32 x;
7894 INT32 y;
7895 if (s.bAssignment == IN_TRANSIT)
7896 { // Show the sector he'll be arriving in
7897 x = SECTORX(g_merc_arrive_sector);
7898 y = SECTORY(g_merc_arrive_sector);
7899 }
7900 else if (GetLengthOfMercPath(&s) > 0)
7901 { // He's going somewhere
7902 INT16 const sector = GetLastSectorIdInCharactersPath(&s);
7903 x = sector % MAP_WORLD_X;
7904 y = sector / MAP_WORLD_Y;
7905 }
7906 else // no movement path is set
7907 {
7908 if (!s.fBetweenSectors) goto no_destination;
7909 /* He must be returning to his previous (reversed so as to be the next)
7910 * sector, so show that as his destination individual soldiers don't
7911 * store previous/next sector coordinates, must go to his group for that
7912 */
7913 GROUP const& g = *GetSoldierGroup(s);
7914 x = g.ubNextX;
7915 y = g.ubNextY;
7916 }
7917 return ST::format("{}{}", pMapVertIndex[y], pMapHortIndex[x]);
7918 }
7919 else
7920 {
7921 no_destination:
7922 return ST::null;
7923 }
7924 }
7925
7926
GetMapscreenMercDepartureString(SOLDIERTYPE const & s,UINT8 * text_colour)7927 ST::string GetMapscreenMercDepartureString(SOLDIERTYPE const& s, UINT8* text_colour)
7928 {
7929 if ((s.ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC && s.ubProfile != SLAY) ||
7930 s.bLife == 0)
7931 {
7932 return gpStrategicString[STR_PB_NOTAPPLICABLE_ABBREVIATION];
7933 }
7934
7935 INT32 mins_remaining = s.iEndofContractTime - GetWorldTotalMin();
7936 if (s.bAssignment == IN_TRANSIT)
7937 {
7938 INT32 const contract_length = s.iTotalContractLength * NUM_MIN_IN_DAY;
7939 if (mins_remaining > contract_length)
7940 {
7941 mins_remaining = contract_length;
7942 }
7943 }
7944
7945 if (mins_remaining >= MAP_TIME_UNDER_THIS_DISPLAY_AS_HOURS)
7946 { // 3 or more days remain
7947 INT32 const days_remaining = mins_remaining / (24*60);
7948 if (text_colour) *text_colour = FONT_LTGREEN;
7949 return ST::format("{}{}", days_remaining, gpStrategicString[STR_PB_DAYS_ABBREVIATION]);
7950 }
7951
7952 // less than 3 days
7953 INT32 const hours_remaining = mins_remaining > 5 ? (mins_remaining + 59) / 60 : 0;
7954
7955 // last 3 days is Red, last 4 hours start flashing red/white!
7956 if (text_colour)
7957 {
7958 *text_colour = mins_remaining <= MINS_TO_FLASH_CONTRACT_TIME && fFlashContractFlag ?
7959 FONT_WHITE : FONT_RED;
7960 }
7961 return ST::format("{}{}", hours_remaining, gpStrategicString[STR_PB_HOURS_ABBREVIATION]);
7962 }
7963
7964
InitPreviousPaths(void)7965 static void InitPreviousPaths(void)
7966 {
7967 g_prev_path = NULL;
7968 }
7969
7970
RememberPreviousPathForAllSelectedChars(void)7971 void RememberPreviousPathForAllSelectedChars(void)
7972 {
7973 Assert(0 <= bSelectedDestChar && bSelectedDestChar < MAX_CHARACTER_COUNT);
7974 ClearStrategicPathList(g_prev_path, 0);
7975 g_prev_path = CopyPaths(GetSoldierMercPathPtr(gCharactersList[bSelectedDestChar].merc));
7976 }
7977
7978
RestorePath(PathSt * & path,UINT8 const group_id)7979 static bool RestorePath(PathSt*& path, UINT8 const group_id)
7980 {
7981 if (g_prev_path)
7982 {
7983 ClearStrategicPathList(path, group_id);
7984 path = CopyPaths(g_prev_path);
7985 }
7986 else if (path) // if he currently has a path
7987 {
7988 // wipe it out!
7989 path = ClearStrategicPathList(path, group_id);
7990 }
7991 else
7992 {
7993 return false;
7994 }
7995
7996 RebuildWayPointsForGroupPath(path, *GetGroup(group_id));
7997 return true;
7998 }
7999
8000
RestorePreviousPaths(void)8001 static void RestorePreviousPaths(void)
8002 {
8003 // invalid if we're not plotting movement
8004 Assert(bSelectedDestChar != -1 || fPlotForHelicopter);
8005
8006 if (fPlotForHelicopter)
8007 {
8008 VEHICLETYPE& v = GetHelicopter();
8009 PathSt** const path = &v.pMercPath;
8010
8011 if (RestorePath(*path, v.ubMovementGroup))
8012 {
8013 CopyPathToAllSelectedCharacters(*path);
8014 }
8015 }
8016 else // character(s) plotting
8017 {
8018 CFOR_EACH_SELECTED_IN_CHAR_LIST(c)
8019 {
8020 SOLDIERTYPE* const pSoldier = c->merc;
8021
8022 PathSt** ppMovePath;
8023 UINT8 ubGroupId;
8024 if( pSoldier->uiStatusFlags & SOLDIER_VEHICLE )
8025 {
8026 VEHICLETYPE* const v = &pVehicleList[pSoldier->bVehicleID];
8027 ppMovePath = &v->pMercPath;
8028 ubGroupId = v->ubMovementGroup;
8029 }
8030 else if( pSoldier->bAssignment == VEHICLE )
8031 {
8032 VEHICLETYPE* const v = &pVehicleList[pSoldier->iVehicleId];
8033 ppMovePath = &v->pMercPath;
8034 ubGroupId = v->ubMovementGroup;
8035 }
8036 else if( pSoldier->bAssignment < ON_DUTY )
8037 {
8038 ppMovePath = &( pSoldier->pMercPath );
8039 ubGroupId = pSoldier->ubGroupID;
8040 }
8041 else
8042 {
8043 // invalid pSoldier - that guy can't possibly be moving, he's on a non-vehicle assignment!
8044 SLOGA("RestorePreviousPaths: invalid pSoldier: %d", pSoldier->ubID);
8045 continue;
8046 }
8047
8048 RestorePath(*ppMovePath, ubGroupId);
8049 }
8050 }
8051 }
8052
8053
ClearPreviousPaths(void)8054 static void ClearPreviousPaths(void)
8055 {
8056 g_prev_path = ClearStrategicPathList(g_prev_path, 0);
8057 }
8058
8059
SelectAllCharactersInSquad(INT8 bSquadNumber)8060 static void SelectAllCharactersInSquad(INT8 bSquadNumber)
8061 {
8062 INT8 bCounter;
8063 BOOLEAN fFirstOne = TRUE;
8064
8065 // ignore if that squad is empty
8066 if (SquadIsEmpty(bSquadNumber)) return;
8067
8068 // select nobody & reset the selected list
8069 ChangeSelectedInfoChar( -1, TRUE );
8070
8071 // now select all the soldiers that are in this squad
8072 for( bCounter = 0; bCounter < MAX_CHARACTER_COUNT; bCounter++ )
8073 {
8074 const SOLDIERTYPE* const pSoldier = gCharactersList[bCounter].merc;
8075 if (pSoldier == NULL) continue;
8076
8077 // if this guy is on that squad or in a vehicle which is assigned to that squad
8078 // NOTE: There's no way to select everyone aboard Skyrider with this function...
8079 if (pSoldier->bAssignment == bSquadNumber ||
8080 IsSoldierInThisVehicleSquad(pSoldier, bSquadNumber))
8081 {
8082 if (fFirstOne)
8083 {
8084 // make the first guy in the list who is in this squad the selected info char
8085 ChangeSelectedInfoChar(bCounter, FALSE);
8086
8087 // select his sector
8088 ChangeSelectedMapSector(pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ);
8089
8090 fFirstOne = FALSE;
8091 }
8092
8093 SetEntryInSelectedCharacterList(bCounter);
8094 }
8095 }
8096 }
8097
8098
CanDrawSectorCursor(void)8099 BOOLEAN CanDrawSectorCursor(void)
8100 {
8101 return
8102 /* !fCursorIsOnMapScrollButtons && */
8103 !fShowTownInfo &&
8104 ghTownMineBox == NO_POPUP_BOX &&
8105 !fShowUpdateBox &&
8106 GetNumberOfMercsInUpdateList() == 0 &&
8107 sSelectedMilitiaTown == 0 &&
8108 !gfMilitiaPopupCreated &&
8109 !gfStartedFromMapScreen &&
8110 !fShowMapScreenMovementList &&
8111 ghMoveBox == NO_POPUP_BOX &&
8112 !fMapInventoryItem;
8113 }
8114
8115
RestoreMapSectorCursor(INT16 sMapX,INT16 sMapY)8116 static void RestoreMapSectorCursor(INT16 sMapX, INT16 sMapY)
8117 {
8118 INT16 sScreenX, sScreenY;
8119
8120
8121 Assert( ( sMapX >= 1 ) && ( sMapX <= 16 ) );
8122 Assert( ( sMapY >= 1 ) && ( sMapY <= 16 ) );
8123
8124 GetScreenXYFromMapXY( sMapX, sMapY, &sScreenX, &sScreenY );
8125
8126 sScreenY -= 1;
8127
8128 RestoreExternBackgroundRect( sScreenX, sScreenY, DMAP_GRID_X, DMAP_GRID_Y );
8129 }
8130
8131
RequestToggleMercInventoryPanel(void)8132 static void RequestToggleMercInventoryPanel(void)
8133 {
8134 if (bSelectedDestChar != -1 || fPlotForHelicopter)
8135 {
8136 AbortMovementPlottingMode( );
8137 }
8138
8139
8140 if ( !CanToggleSelectedCharInventory() )
8141 {
8142 return;
8143 }
8144
8145 if (fShowDescriptionFlag)
8146 {
8147 // turn off item description
8148 DeleteItemDescriptionBox( );
8149 }
8150 else
8151 {
8152 // toggle inventory mode
8153 fShowInventoryFlag = !fShowInventoryFlag;
8154 }
8155
8156 fTeamPanelDirty = TRUE;
8157 }
8158
8159
RequestContractMenu(void)8160 static void RequestContractMenu(void)
8161 {
8162 if (gfPreBattleInterfaceActive) return;
8163
8164 if (bSelectedDestChar != -1 || fPlotForHelicopter)
8165 {
8166 AbortMovementPlottingMode( );
8167 }
8168
8169
8170 // in case we have multiple guys selected, turn off everyone but the guy we're negotiating with
8171 ChangeSelectedInfoChar( bSelectedInfoChar, TRUE );
8172
8173 SOLDIERTYPE* const s = GetSelectedInfoChar();
8174 if (s && CanExtendContractForSoldier(s))
8175 {
8176 // create
8177 RebuildContractBoxForMerc(s);
8178
8179 // reset selected characters
8180 ResetAllSelectedCharacterModes( );
8181
8182 bSelectedContractChar = bSelectedInfoChar;
8183 giContractHighLine = bSelectedContractChar;
8184
8185 // if not triggered internally
8186 if (!CheckIfSalaryIncreasedAndSayQuote(s, TRUE))
8187 {
8188 // show contract box
8189 fShowContractMenu = TRUE;
8190
8191 // stop any dialogue by character
8192 StopAnyCurrentlyTalkingSpeech( );
8193 }
8194
8195 //fCharacterInfoPanelDirty = TRUE;
8196 }
8197 else
8198 {
8199 // reset selected characters
8200 ResetAllSelectedCharacterModes( );
8201 }
8202 }
8203
8204
ChangeCharacterListSortMethod(INT32 iValue)8205 static void ChangeCharacterListSortMethod(INT32 iValue)
8206 {
8207 Assert( iValue >= 0 );
8208 Assert( iValue < MAX_SORT_METHODS );
8209
8210 if (gfPreBattleInterfaceActive) return;
8211
8212 if (bSelectedDestChar != -1 || fPlotForHelicopter)
8213 {
8214 AbortMovementPlottingMode( );
8215 }
8216
8217
8218 giSortStateForMapScreenList = iValue;
8219 SortListOfMercsInTeamPanel( TRUE );
8220 }
8221
8222
MapscreenMarkButtonsDirty(void)8223 static void MapscreenMarkButtonsDirty(void)
8224 {
8225 // redraw buttons
8226 MarkButtonsDirty( );
8227
8228 // if border buttons are created
8229 if( !fShowMapInventoryPool )
8230 {
8231 // if the attribute assignment menu is showing
8232 if ( fShowAttributeMenu )
8233 {
8234 // don't redraw the town button, it would wipe out a chunk of the attribute menu
8235 UnMarkButtonDirty( giMapBorderButtons[ MAP_BORDER_TOWN_BTN ] );
8236 }
8237 }
8238 }
8239
8240
LockMapScreenInterface(bool const lock)8241 void LockMapScreenInterface(bool const lock)
8242 {
8243 class DialogueEventLockMapScreenInterface : public DialogueEvent
8244 {
8245 public:
8246 DialogueEventLockMapScreenInterface(bool const lock) : lock_(lock) {}
8247
8248 bool Execute()
8249 {
8250 fLockOutMapScreenInterface = lock_;
8251 return false;
8252 }
8253
8254 private:
8255 bool const lock_;
8256 };
8257
8258 DialogueEvent::Add(new DialogueEventLockMapScreenInterface(lock));
8259 }
8260
8261
MakeDialogueEventEnterMapScreen()8262 void MakeDialogueEventEnterMapScreen()
8263 {
8264 class DialogueEventEnterMapScreen : public DialogueEvent
8265 {
8266 public:
8267 bool Execute()
8268 {
8269 if (guiCurrentScreen != MAP_SCREEN)
8270 {
8271 gfEnteringMapScreen = TRUE;
8272 fEnterMapDueToContract = TRUE;
8273 }
8274 return false;
8275 }
8276 };
8277
8278 DialogueEvent::Add(new DialogueEventEnterMapScreen());
8279 }
8280
8281
SetMapCursorItem()8282 void SetMapCursorItem()
8283 {
8284 SetMouseCursorFromCurrentItem();
8285 gMPanelRegion.ChangeCursor(EXTERN_CURSOR);
8286 fMapInventoryItem = TRUE;
8287 }
8288