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