1 #include "Campaign_Types.h"
2 #include "Directories.h"
3 #include "Font.h"
4 #include "HImage.h"
5 #include "Laptop.h"
6 #include "MercPortrait.h"
7 #include "Personnel.h"
8 #include "VObject.h"
9 #include "Debug.h"
10 #include "WordWrap.h"
11 #include "Render_Dirty.h"
12 #include "Cursors.h"
13 #include "Overhead.h"
14 #include "Soldier_Profile.h"
15 #include "Text.h"
16 #include "MapScreen.h"
17 #include "Game_Clock.h"
18 #include "Finances.h"
19 #include "LaptopSave.h"
20 #include "Input.h"
21 #include "Random.h"
22 #include "Line.h"
23 #include "Assignments.h"
24 #include "Interface_Items.h"
25 #include "Weapons.h"
26 #include "StrategicMap.h"
27 #include "Animation_Data.h"
28 #include "Merc_Contract.h"
29 #include "Button_System.h"
30 #include "VSurface.h"
31 #include "Font_Control.h"
32 #include "EMail.h"
33 #include "Soldier_Macros.h"
34 
35 #include "CalibreModel.h"
36 #include "ContentManager.h"
37 #include "GameInstance.h"
38 #include "MagazineModel.h"
39 #include "WeaponModels.h"
40 #include "MercProfile.h"
41 
42 #include <string_theory/format>
43 #include <string_theory/string>
44 
45 #include <algorithm>
46 #include <iterator>
47 #include <stdexcept>
48 
49 #define INVENTORY_BOX_X (399 + STD_SCREEN_X)
50 #define INVENTORY_BOX_Y (205 + STD_SCREEN_Y)
51 #define INVENTORY_BOX_W 171
52 #define INVENTORY_BOX_H 232
53 
54 
55 #define IMAGE_BOX_X              (395 + STD_SCREEN_X)
56 #define IMAGE_BOX_Y              LAPTOP_SCREEN_UL_Y + 24
57 #define IMAGE_NAME_WIDTH         106
58 #define IMAGE_FULL_NAME_OFFSET_Y 111
59 #define TEXT_BOX_WIDTH           160
60 #define TEXT_DELTA_OFFSET          9
61 #define PERS_CURR_TEAM_X         LAPTOP_SCREEN_UL_X +  39 - 15
62 #define PERS_CURR_TEAM_Y         LAPTOP_SCREEN_UL_Y + 218
63 #define PERS_DEPART_TEAM_Y       LAPTOP_SCREEN_UL_Y + 247
64 
65 #define MAX_STATS                       20
66 #define PERS_FONT                      FONT10ARIAL
67 #define CHAR_NAME_FONT                 FONT12ARIAL
68 #define CHAR_NAME_LOC_X                (432 + STD_SCREEN_X)
69 #define CHAR_NAME_LOC_WIDTH            124
70 #define CHAR_NAME_Y                    (177 + STD_SCREEN_Y)
71 #define CHAR_LOC_Y                     (189 + STD_SCREEN_Y)
72 #define PERS_TEXT_FONT_COLOR           FONT_WHITE
73 #define PERS_TEXT_FONT_ALTERNATE_COLOR FONT_YELLOW
74 #define PERS_FONT_COLOR                FONT_WHITE
75 
76 
77 #define NEXT_MERC_FACE_X   LAPTOP_SCREEN_UL_X + 448
78 #define MERC_FACE_SCROLL_Y LAPTOP_SCREEN_UL_Y + 150
79 #define PREV_MERC_FACE_X   LAPTOP_SCREEN_UL_X + 285
80 
81 
82 #define DEPARTED_X LAPTOP_SCREEN_UL_X +  29 - 10
83 #define DEPARTED_Y LAPTOP_SCREEN_UL_Y + 207
84 
85 
86 #define PERSONNEL_PORTRAIT_NUMBER       20
87 #define PERSONNEL_PORTRAIT_NUMBER_WIDTH  5
88 
89 #define SMALL_PORTRAIT_WIDTH  46
90 #define SMALL_PORTRAIT_HEIGHT 42
91 
92 #define SMALL_PORT_WIDTH  52
93 #define SMALL_PORT_HEIGHT 45
94 
95 #define SMALL_PORTRAIT_WIDTH_NO_BORDERS 48
96 
97 #define SMALL_PORTRAIT_START_X (141 - 10 + STD_SCREEN_X)
98 #define SMALL_PORTRAIT_START_Y (53 + STD_SCREEN_Y)
99 
100 #define PERS_CURR_TEAM_COST_X LAPTOP_SCREEN_UL_X + 150 - 10
101 #define PERS_CURR_TEAM_COST_Y LAPTOP_SCREEN_UL_Y + 218
102 
103 #define PERS_CURR_TEAM_HIGHEST_Y PERS_CURR_TEAM_COST_Y    + 15
104 #define PERS_CURR_TEAM_LOWEST_Y  PERS_CURR_TEAM_HIGHEST_Y + 15
105 
106 #define PERS_CURR_TEAM_WIDTH 286 - 160
107 
108 #define PERS_DEPART_TEAM_WIDTH PERS_CURR_TEAM_WIDTH - 20
109 
110 #define PERS_STAT_AVG_X        LAPTOP_SCREEN_UL_X + 157 - 10
111 #define PERS_STAT_AVG_Y        LAPTOP_SCREEN_UL_Y + 274
112 #define PERS_STAT_AVG_WIDTH    202 - 159
113 #define PERS_STAT_LOWEST_X     LAPTOP_SCREEN_UL_X +  72 - 10
114 #define PERS_STAT_LOWEST_WIDTH 155 - 75
115 #define PERS_STAT_HIGHEST_X    LAPTOP_SCREEN_UL_X + 205 - 10
116 #define PERS_STAT_LIST_X       LAPTOP_SCREEN_UL_X +  33 - 10
117 
118 
119 #define PERS_TOGGLE_CUR_DEPART_WIDTH  106 -  35
120 #define PERS_TOGGLE_CUR_DEPART_HEIGHT 236 - 212
121 
122 #define PERS_TOGGLE_CUR_DEPART_X LAPTOP_SCREEN_UL_X +  35 - 10
123 #define PERS_TOGGLE_CUR_Y        LAPTOP_SCREEN_UL_Y + 208
124 #define PERS_TOGGLE_DEPART_Y     LAPTOP_SCREEN_UL_Y + 238
125 
126 #define PERS_DEPARTED_UP_X   LAPTOP_SCREEN_UL_X + 265 - 10
127 #define PERS_DEPARTED_UP_Y   LAPTOP_SCREEN_UL_Y + 210
128 #define PERS_DEPARTED_DOWN_Y LAPTOP_SCREEN_UL_Y + 237
129 
130 
131 #define PERS_TITLE_X (140 + STD_SCREEN_X)
132 #define PERS_TITLE_Y ( 33 + STD_SCREEN_Y)
133 
134 #define ATM_UL_X LAPTOP_SCREEN_UL_X + 397
135 #define ATM_UL_Y LAPTOP_SCREEN_UL_Y +  27
136 
137 #define ATM_FONT PERS_FONT
138 
139 // departed states
140 enum
141 {
142 	DEPARTED_DEAD = 0,
143 	DEPARTED_FIRED,
144 	DEPARTED_MARRIED,
145 	DEPARTED_CONTRACT_EXPIRED,
146 	DEPARTED_QUIT,
147 };
148 
149 // atm button positions
150 #define ATM_DISPLAY_X      (STD_SCREEN_X + 509)
151 #define ATM_DISPLAY_Y      (STD_SCREEN_Y +  58)
152 #define ATM_DISPLAY_HEIGHT  10
153 #define ATM_DISPLAY_WIDTH   81
154 
155 
156 // the number of inventory items per personnel page
157 #define NUMBER_OF_INVENTORY_PERSONNEL 8
158 #define Y_SIZE_OF_PERSONNEL_SCROLL_REGION (422 - 219)
159 #define X_SIZE_OF_PERSONNEL_SCROLL_REGION (589 - 573)
160 #define Y_OF_PERSONNEL_SCROLL_REGION (STD_SCREEN_Y + 219)
161 #define X_OF_PERSONNEL_SCROLL_REGION (STD_SCREEN_X + 573)
162 #define SIZE_OF_PERSONNEL_CURSOR 19
163 
164 
165 #define CFOR_EACH_PERSONNEL(iter) \
166 	CFOR_EACH_IN_TEAM(iter, OUR_TEAM)  \
167 		if (iter->uiStatusFlags & SOLDIER_VEHICLE) continue; else
168 
169 
170 // enums for the buttons in the information side bar (used with giPersonnelATMStartButton[])
171 enum
172 {
173 	PERSONNEL_STAT_BTN,
174 	PERSONNEL_EMPLOYMENT_BTN,
175 	PERSONNEL_INV_BTN
176 };
177 
178 
179 //enums for the current state of the information side bar (stat panel)
180 enum
181 {
182 	PRSNL_STATS,
183 	PRSNL_EMPLOYMENT,
184 	PRSNL_INV,
185 };
186 static UINT8 gubPersonnelInfoState = PRSNL_STATS;
187 
188 
189 //enums for the pPersonnelScreenStrings[]
190 enum
191 {
192 	PRSNL_TXT_MED_DEPOSIT,      // AMOUNT OF MEDICAL DEPOSIT PUT DOWN ON THE MERC
193 	PRSNL_TXT_CURRENT_CONTRACT, // COST OF CURRENT CONTRACT
194 	PRSNL_TXT_KILLS,            // NUMBER OF KILLS BY MERC
195 	PRSNL_TXT_ASSISTS,          // NUMBER OF ASSISTS ON KILLS BY MERC
196 	PRSNL_TXT_DAILY_COST,       // DAILY COST OF MERC
197 	PRSNL_TXT_TOTAL_COST,       // TOTAL COST OF MERC
198 	PRSNL_TXT_CONTRACT,         // COST OF CURRENT CONTRACT
199 	PRSNL_TXT_TOTAL_SERVICE,    // TOTAL SERVICE RENDERED BY MERC
200 	PRSNL_TXT_UNPAID_AMOUNT,    // AMOUNT LEFT ON MERC MERC TO BE PAID
201 	PRSNL_TXT_HIT_PERCENTAGE,   // PERCENTAGE OF SHOTS THAT HIT TARGET
202 	PRSNL_TXT_BATTLES,          // NUMBER OF BATTLES FOUGHT
203 	PRSNL_TXT_TIMES_WOUNDED,    // NUMBER OF TIMES MERC HAS BEEN WOUNDED
204 	PRSNL_TXT_SKILLS,
205 	PRSNL_TXT_NOSKILLS,
206 };
207 
208 static UINT8 uiCurrentInventoryIndex = 0;
209 
210 static UINT32 guiSliderPosition;
211 
212 
213 #define pers_stat_x       (STD_SCREEN_X + 407)
214 #define pers_stat_delta_x (pers_stat_x + TEXT_BOX_WIDTH - 20 + TEXT_DELTA_OFFSET)
215 #define pers_stat_data_x  (pers_stat_x + 36)
216 static const INT16 pers_stat_y[] =
217 {
218 	215,
219 	225,
220 	235,
221 	245,
222 	255,
223 	265,
224 	325,
225 	280,
226 	290,
227 	300,
228 	310, // 10
229 	405,
230 	395,
231 	425,
232 	435,
233 	455,
234 	390, // for contract price
235 	445,
236 	33, // Personnel Header // XXX unused
237 	340,
238 	350, // 20
239 	365,
240 	375,
241 	385,
242 	395,
243 	405
244 };
245 
246 
247 static SGPVObject* guiSCREEN;
248 static SGPVObject* guiTITLE;
249 static SGPVObject* guiDEPARTEDTEAM;
250 static SGPVObject* guiCURRENTTEAM;
251 static SGPVObject* guiPersonnelInventory;
252 
253 static struct
254 {
255 	GUIButtonRef prev;
256 	GUIButtonRef next;
257 	GUIButtonRef depart_up;
258 	GUIButtonRef depart_dn;
259 } g_personnel;
260 
261 static GUIButtonRef giPersonnelInventoryButtons[2];
262 
263 // buttons for ATM
264 static BUTTON_PICS* giPersonnelATMStartButtonImage[3];
265 static GUIButtonRef giPersonnelATMStartButton[3];
266 
267 // the id of currently displayed merc in right half of screen
268 static INT32 iCurrentPersonSelectedId = -1;
269 
270 static INT32 giCurrentUpperLeftPortraitNumber = 0;
271 
272 // which mode are we showing?..current team?...or deadly departed?
273 static BOOLEAN fCurrentTeamMode = TRUE;
274 
275 // mouse regions
276 static MOUSE_REGION gPortraitMouseRegions[PERSONNEL_PORTRAIT_NUMBER];
277 
278 static MOUSE_REGION gTogglePastCurrentTeam[2];
279 
280 static MOUSE_REGION gMouseScrollPersonnelINV;
281 
282 
283 static void InitPastCharactersList(void);
284 
285 
GameInitPersonnel(void)286 void GameInitPersonnel(void)
287 {
288 	// init past characters lists
289 	InitPastCharactersList();
290 }
291 
292 
293 static void CreateDestroyCurrentDepartedMouseRegions(BOOLEAN create);
294 static void CreateDestroyMouseRegionsForPersonnelPortraits(BOOLEAN create);
295 static void CreateDestroyStartATMButton(BOOLEAN create);
296 static void CreatePersonnelButtons(void);
297 static void SelectFirstDisplayedMerc(void);
298 static void LoadPersonnelGraphics(void);
299 static void LoadPersonnelScreenBackgroundGraphics(void);
300 static void SetPersonnelButtonStates(void);
301 
302 
EnterPersonnel(void)303 void EnterPersonnel(void)
304 {
305 	fReDrawScreenFlag=TRUE;
306 
307 	uiCurrentInventoryIndex = 0;
308 	guiSliderPosition = 0;
309 
310 	// load graphics for screen
311 	LoadPersonnelGraphics();
312 
313 	// show atm panel
314 	CreateDestroyStartATMButton(TRUE);
315 
316 	// load personnel
317 	LoadPersonnelScreenBackgroundGraphics();
318 
319 	SelectFirstDisplayedMerc();
320 
321 	// render screen
322 	RenderPersonnel();
323 
324 	CreateDestroyMouseRegionsForPersonnelPortraits(TRUE);
325 	CreateDestroyCurrentDepartedMouseRegions(TRUE);
326 
327 	// create buttons for screen
328 	CreatePersonnelButtons();
329 
330 	// set states of en- dis able buttons
331 	SetPersonnelButtonStates();
332 }
333 
334 
335 static void CreateDestroyButtonsForDepartedTeamList(BOOLEAN create);
336 static void CreateDestroyPersonnelInventoryScrollButtons(void);
337 static void DeletePersonnelButtons(void);
338 static void DeletePersonnelScreenBackgroundGraphics(void);
339 static void RemovePersonnelGraphics(void);
340 
341 
ExitPersonnel(void)342 void ExitPersonnel(void)
343 {
344 	CreateDestroyButtonsForDepartedTeamList(FALSE);
345 
346 	// get rid of atm panel buttons
347 	CreateDestroyStartATMButton(FALSE);
348 
349 	gubPersonnelInfoState = PRSNL_STATS;
350 
351 	CreateDestroyPersonnelInventoryScrollButtons();
352 
353 	// get rid of graphics
354 	RemovePersonnelGraphics();
355 
356 	DeletePersonnelScreenBackgroundGraphics();
357 
358 	// delete buttons
359 	DeletePersonnelButtons();
360 
361 	CreateDestroyMouseRegionsForPersonnelPortraits(FALSE);
362 	CreateDestroyCurrentDepartedMouseRegions(FALSE);
363 }
364 
365 
366 static void EnableDisableDeparturesButtons(void);
367 static void EnableDisableInventoryScrollButtons(void);
368 static void HandlePersonnelKeyboard(void);
369 
370 
HandlePersonnel(void)371 void HandlePersonnel(void)
372 {
373 	// create / destroy buttons for scrolling departed list
374 	CreateDestroyButtonsForDepartedTeamList(!fCurrentTeamMode);
375 
376 	// enable / disable departures buttons
377 	EnableDisableDeparturesButtons();
378 
379 	// create destroy inv buttons as needed
380 	CreateDestroyPersonnelInventoryScrollButtons();
381 
382 	// enable disable buttons as needed
383 	EnableDisableInventoryScrollButtons();
384 
385 	HandlePersonnelKeyboard();
386 }
387 
388 
LoadPersonnelGraphics(void)389 static void LoadPersonnelGraphics(void)
390 {
391 	// load graphics needed for personnel screen
392 
393 	// title bar
394 	guiTITLE = AddVideoObjectFromFile(LAPTOPDIR "/programtitlebar.sti");
395 
396 	// the background grpahics
397 	guiSCREEN = AddVideoObjectFromFile(LAPTOPDIR "/personnelwindow.sti");
398 
399 	guiPersonnelInventory = AddVideoObjectFromFile(LAPTOPDIR "/personnel_inventory.sti");
400 }
401 
402 
RemovePersonnelGraphics(void)403 static void RemovePersonnelGraphics(void)
404 {
405 	// delete graphics needed for personnel screen
406 	DeleteVideoObject(guiSCREEN);
407 	DeleteVideoObject(guiTITLE);
408 	DeleteVideoObject(guiPersonnelInventory);
409 }
410 
411 
412 static void DisplayFaceOfDisplayedMerc(void);
413 static void DisplayPastMercsPortraits();
414 static void DisplayPersonnelSummary(void);
415 static void DisplayPersonnelTextOnTitleBar(void);
416 static void DisplayPicturesOfCurrentTeam(void);
417 static void DisplayTeamStats(void);
418 static void RenderAtmPanel(void);
419 static void RenderPersonnelScreenBackground(void);
420 static void UpDateStateOfStartButton(void);
421 
422 
RenderPersonnel(void)423 void RenderPersonnel(void)
424 {
425 	// re-renders personnel screen
426 	// render main background
427 
428 	BltVideoObject(FRAME_BUFFER, guiTITLE,  0, LAPTOP_SCREEN_UL_X, LAPTOP_SCREEN_UL_Y -  2);
429 	BltVideoObject(FRAME_BUFFER, guiSCREEN, 0, LAPTOP_SCREEN_UL_X, LAPTOP_SCREEN_UL_Y + 22);
430 
431 	// render personnel screen background
432 	RenderPersonnelScreenBackground();
433 
434 	// show team
435 	DisplayPicturesOfCurrentTeam();
436 
437 	DisplayPastMercsPortraits();
438 
439 	RenderAtmPanel();
440 
441 	// show selected merc
442 	DisplayFaceOfDisplayedMerc();
443 
444 	DisplayPersonnelSummary();
445 	DisplayTeamStats();
446 
447 	// title bar
448 	BlitTitleBarIcons();
449 
450 	// show text on titlebar
451 	DisplayPersonnelTextOnTitleBar();
452 
453 	// en-dis-able start button
454 	UpDateStateOfStartButton();
455 }
456 
457 
458 static void DisplayCharStats(SOLDIERTYPE const& s);
459 static void DisplayEmploymentinformation(SOLDIERTYPE const& s);
460 static void DisplayCharInventory(SOLDIERTYPE const&);
461 
462 
RenderPersonnelStats(SOLDIERTYPE const & s)463 static void RenderPersonnelStats(SOLDIERTYPE const& s)
464 {
465 	// will render the stats of person iId
466 	SetFontAttributes(PERS_FONT, PERS_TEXT_FONT_COLOR);
467 
468 	switch (gubPersonnelInfoState)
469 	{
470 		case PRSNL_STATS:      DisplayCharStats(s);             break;
471 		case PRSNL_EMPLOYMENT: DisplayEmploymentinformation(s); break;
472 		case PRSNL_INV:        DisplayCharInventory(s);         break;
473 	}
474 }
475 
476 
RenderPersonnelFace(MERCPROFILESTRUCT const & p,BOOLEAN const alive)477 static void RenderPersonnelFace(MERCPROFILESTRUCT const& p, BOOLEAN const alive)
478 try
479 {
480 	{ AutoSGPVObject guiFACE(LoadBigPortrait(p));
481 		if (!alive)
482 		{
483 			guiFACE->pShades[0] = Create16BPPPaletteShaded(guiFACE->Palette(), DEAD_MERC_COLOR_RED, DEAD_MERC_COLOR_GREEN, DEAD_MERC_COLOR_BLUE, TRUE);
484 			//set the red pallete to the face
485 			guiFACE->CurrentShade(0);
486 		}
487 		BltVideoObject(FRAME_BUFFER, guiFACE.get(), 0, IMAGE_BOX_X, IMAGE_BOX_Y);
488 	}
489 
490 	// Display the merc's name on the portrait
491 	ST::string name = p.zName;
492 
493 	const UINT16 x = IMAGE_BOX_X;
494 	const UINT16 y = IMAGE_BOX_Y + IMAGE_FULL_NAME_OFFSET_Y;
495 	const UINT16 w = IMAGE_NAME_WIDTH;
496 	const INT32 iHeightOfText = DisplayWrappedString(x, y, w, 1, PERS_FONT, PERS_FONT_COLOR, name, 0, CENTER_JUSTIFIED | DONT_DISPLAY_TEXT);
497 
498 	const UINT16 line_height = GetFontHeight(PERS_FONT);
499 	if (iHeightOfText - 2 > line_height) // If the string will wrap
500 	{
501 		// Raise where we display it, and wrap it
502 		DisplayWrappedString(x, y - line_height, w, 1, PERS_FONT, PERS_FONT_COLOR, name, 0, CENTER_JUSTIFIED);
503 	}
504 	else
505 	{
506 		DrawTextToScreen(name, x, y, w, PERS_FONT, PERS_FONT_COLOR, 0, CENTER_JUSTIFIED);
507 	}
508 }
509 catch (...) { /* XXX ignore */ }
510 
511 
512 static INT32 GetNumberOfMercsDeadOrAliveOnPlayersTeam(void);
513 static INT32 GetNumberOfPastMercsOnPlayersTeam(void);
514 
515 
NextPersonnelFace(void)516 static void NextPersonnelFace(void)
517 {
518 	if (iCurrentPersonSelectedId == -1) return;
519 
520 	if (fCurrentTeamMode)
521 	{
522 		// wrap around?
523 		if (iCurrentPersonSelectedId == GetNumberOfMercsDeadOrAliveOnPlayersTeam() - 1)
524 		{
525 			iCurrentPersonSelectedId = 0;
526 		}
527 		else
528 		{
529 			iCurrentPersonSelectedId++;
530 		}
531 	}
532 	else
533 	{
534 		if (iCurrentPersonSelectedId + 1 == GetNumberOfPastMercsOnPlayersTeam() - giCurrentUpperLeftPortraitNumber)
535 		{
536 			// about to go off the end
537 			giCurrentUpperLeftPortraitNumber = 0;
538 			iCurrentPersonSelectedId         = 0;
539 		}
540 		else if (iCurrentPersonSelectedId == 19)
541 		{
542 			giCurrentUpperLeftPortraitNumber += PERSONNEL_PORTRAIT_NUMBER;
543 			iCurrentPersonSelectedId          = 0;
544 		}
545 		else
546 		{
547 			++iCurrentPersonSelectedId;
548 		}
549 		fReDrawScreenFlag = TRUE;
550 	}
551 }
552 
553 
PrevPersonnelFace(void)554 static void PrevPersonnelFace(void)
555 {
556 	if (iCurrentPersonSelectedId == -1) return;
557 
558 	if (fCurrentTeamMode)
559 	{
560 		// wrap around?
561 		if (iCurrentPersonSelectedId == 0)
562 		{
563 			iCurrentPersonSelectedId = GetNumberOfMercsDeadOrAliveOnPlayersTeam() - 1;
564 		}
565 		else
566 		{
567 			iCurrentPersonSelectedId--;
568 		}
569 	}
570 	else
571 	{
572 		if (iCurrentPersonSelectedId == 0 && giCurrentUpperLeftPortraitNumber == 0)
573 		{
574 			// about to go off the end
575 			INT32 count_past = GetNumberOfPastMercsOnPlayersTeam();
576 			giCurrentUpperLeftPortraitNumber = count_past - count_past % PERSONNEL_PORTRAIT_NUMBER;
577 			iCurrentPersonSelectedId = count_past % PERSONNEL_PORTRAIT_NUMBER - 1;
578 		}
579 		else if (iCurrentPersonSelectedId == 0)
580 		{
581 			giCurrentUpperLeftPortraitNumber -= PERSONNEL_PORTRAIT_NUMBER;
582 			iCurrentPersonSelectedId          = 19;
583 		}
584 		else
585 		{
586 			--iCurrentPersonSelectedId;
587 		}
588 		fReDrawScreenFlag = TRUE;
589 	}
590 }
591 
592 
MakeButton(char const * const gfx,INT32 const off_normal,INT32 const on_normal,INT16 const x,INT16 const y,GUI_CALLBACK const click)593 static GUIButtonRef MakeButton(char const* const gfx, INT32 const off_normal, INT32 const on_normal, INT16 const x, INT16 const y, GUI_CALLBACK const click)
594 {
595 	GUIButtonRef const b = QuickCreateButtonImg(gfx, off_normal, on_normal, x, y, MSYS_PRIORITY_HIGHEST - 1, click);
596 	b->SetCursor(CURSOR_LAPTOP_SCREEN);
597 	return b;
598 }
599 
600 
601 static void LeftButtonCallBack(GUI_BUTTON* btn, INT32 reason);
602 static void RightButtonCallBack(GUI_BUTTON* btn, INT32 reason);
603 
604 
CreatePersonnelButtons(void)605 static void CreatePersonnelButtons(void)
606 {
607 	// left/right buttons
608 	g_personnel.prev = MakeButton(LAPTOPDIR "/personnelbuttons.sti", 0, 1, PREV_MERC_FACE_X, MERC_FACE_SCROLL_Y, LeftButtonCallBack);
609 	g_personnel.next = MakeButton(LAPTOPDIR "/personnelbuttons.sti", 2, 3, NEXT_MERC_FACE_X, MERC_FACE_SCROLL_Y, RightButtonCallBack);
610 }
611 
612 
DeletePersonnelButtons(void)613 static void DeletePersonnelButtons(void)
614 {
615 	RemoveButton(g_personnel.prev);
616 	RemoveButton(g_personnel.next);
617 }
618 
619 
LeftButtonCallBack(GUI_BUTTON * btn,INT32 reason)620 static void LeftButtonCallBack(GUI_BUTTON* btn, INT32 reason)
621 {
622 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
623 	{
624 		fReDrawScreenFlag = TRUE;
625 		PrevPersonnelFace();
626 		uiCurrentInventoryIndex = 0;
627 		guiSliderPosition = 0;
628 	}
629 }
630 
631 
RightButtonCallBack(GUI_BUTTON * btn,INT32 reason)632 static void RightButtonCallBack(GUI_BUTTON* btn, INT32 reason)
633 {
634 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
635 	{
636 		fReDrawScreenFlag = TRUE;
637 		NextPersonnelFace();
638 		uiCurrentInventoryIndex = 0;
639 		guiSliderPosition = 0;
640 	}
641 }
642 
643 
DisplayCharName(SOLDIERTYPE const & s)644 static void DisplayCharName(SOLDIERTYPE const& s)
645 {
646 	// get merc's nickName, assignment, and sector location info
647 	INT16 sX, sY;
648 
649 	SetFontAttributes(CHAR_NAME_FONT, PERS_TEXT_FONT_COLOR);
650 
651 	ST::string sTownName;
652 	if (s.bAssignment != ASSIGNMENT_POW &&
653 			s.bAssignment != IN_TRANSIT)
654 	{
655 		// name of town, if any
656 		INT8 const bTownId = GetTownIdForSector(SECTOR(s.sSectorX, s.sSectorY));
657 		if (bTownId != BLANK_SECTOR) sTownName = GCM->getTownName(bTownId);
658 	}
659 
660 	ST::string sString;
661 	if (sTownName != NULL)
662 	{
663 		//nick name - town name
664 		sString = ST::format("{} - {}", s.name, sTownName);
665 	}
666 	else
667 	{
668 		//nick name
669 		sString = s.name;
670 	}
671 	FindFontCenterCoordinates(CHAR_NAME_LOC_X, 0, CHAR_NAME_LOC_WIDTH, 0, sString, CHAR_NAME_FONT, &sX, &sY);
672 	MPrint(sX, CHAR_NAME_Y, sString);
673 
674 	ST::string Assignment = pLongAssignmentStrings[s.bAssignment];
675 	FindFontCenterCoordinates(CHAR_NAME_LOC_X, 0, CHAR_NAME_LOC_WIDTH, 0, Assignment, CHAR_NAME_FONT, &sX, &sY);
676 	MPrint(sX, CHAR_LOC_Y, Assignment);
677 }
678 
679 
PrintStatWithDelta(UINT idx,INT8 stat,INT8 delta)680 static void PrintStatWithDelta(UINT idx, INT8 stat, INT8 delta)
681 {
682 	ST::string sString;
683 	INT16 sX;
684 	INT16 sY;
685 
686 	const INT32 y = STD_SCREEN_Y + pers_stat_y[idx];
687 	if (delta > 0)
688 	{
689 		sString = ST::format("( {+d} )", delta);
690 		FindFontRightCoordinates(pers_stat_delta_x, 0, 30, 0, sString, PERS_FONT, &sX, &sY);
691 		MPrint(sX, y, sString);
692 	}
693 	sString = ST::format("{}", stat);
694 	MPrint(pers_stat_x, y, ST::format("{}:", str_stat_list[idx]));
695 	FindFontRightCoordinates(pers_stat_x, 0, TEXT_BOX_WIDTH - 20, 0, sString, PERS_FONT, &sX, &sY);
696 	MPrint(sX, y, sString);
697 }
698 
699 
PrintStat(UINT16 stat,INT32 y,const ST::string & text)700 static void PrintStat(UINT16 stat, INT32 y, const ST::string& text)
701 {
702 	ST::string sString;
703 	INT16 sX;
704 	INT16 sY;
705 
706 	MPrint(pers_stat_x, y, ST::format("{}:", text));
707 	sString = ST::format("{}", stat);
708 	FindFontRightCoordinates(pers_stat_x, 0, TEXT_BOX_WIDTH - 20, 0, sString, PERS_FONT, &sX, &sY);
709 	MPrint(sX, y, sString);
710 }
711 
712 
DisplayCharStats(SOLDIERTYPE const & s)713 static void DisplayCharStats(SOLDIERTYPE const& s)
714 {
715 	ST::string sString;
716 	INT16 sX;
717 	INT16 sY;
718 
719 	MERCPROFILESTRUCT const& p          = GetProfile(s.ubProfile);
720 	BOOLEAN           const  fAmIaRobot = AM_A_ROBOT(&s);
721 
722 	// Health
723 	if (s.bAssignment != ASSIGNMENT_POW)
724 	{
725 		if (p.bLifeDelta > 0)
726 		{
727 			sString = ST::format("( {+d} )", p.bLifeDelta);
728 			FindFontRightCoordinates(pers_stat_delta_x, 0, 30, 0, sString, PERS_FONT, &sX, &sY);
729 			MPrint(sX, STD_SCREEN_Y + pers_stat_y[0], sString);
730 		}
731 		sString = ST::format("{}/{}", s.bLife, s.bLifeMax);
732 	}
733 	else
734 	{
735 		sString = pPOWStrings[1];
736 	}
737 	MPrint(pers_stat_x, STD_SCREEN_Y + pers_stat_y[0], ST::format("{}:", str_stat_health));
738 	FindFontRightCoordinates(pers_stat_x, 0, TEXT_BOX_WIDTH - 20, 0, sString, PERS_FONT, &sX, &sY);
739 	MPrint(sX, STD_SCREEN_Y + pers_stat_y[0], sString);
740 
741 	if  (fAmIaRobot)
742 	{
743 		for (INT32 i = 1; i < 11; ++i)
744 		{
745 			const INT32 y = STD_SCREEN_Y + pers_stat_y[i];
746 			MPrint(pers_stat_x, y, ST::format("{}:", str_stat_list[i]));
747 			ST::string na = gpStrategicString[STR_PB_NOTAPPLICABLE_ABBREVIATION];
748 			FindFontRightCoordinates(pers_stat_x, 0, TEXT_BOX_WIDTH - 20, 0, na, PERS_FONT, &sX, &sY);
749 			MPrint(sX, y, na);
750 		}
751 	}
752 	else
753 	{
754 		PrintStatWithDelta( 1, p.bAgility,      p.bAgilityDelta);
755 		PrintStatWithDelta( 2, p.bDexterity,    p.bDexterityDelta);
756 		PrintStatWithDelta( 3, p.bStrength,     p.bStrengthDelta);
757 		PrintStatWithDelta( 4, p.bLeadership,   p.bLeadershipDelta);
758 		PrintStatWithDelta( 5, p.bWisdom,       p.bWisdomDelta);
759 		PrintStatWithDelta( 6, p.bExpLevel,     p.bExpLevelDelta);
760 		PrintStatWithDelta( 7, p.bMarksmanship, p.bMarksmanshipDelta);
761 		PrintStatWithDelta( 8, p.bMechanical,   p.bMechanicDelta);
762 		PrintStatWithDelta( 9, p.bExplosive,    p.bExplosivesDelta);
763 		PrintStatWithDelta(10, p.bMedical,      p.bMedicalDelta);
764 	}
765 
766 	PrintStat(p.usKills,   STD_SCREEN_Y + pers_stat_y[21], pPersonnelScreenStrings[PRSNL_TXT_KILLS]);
767 	PrintStat(p.usAssists, STD_SCREEN_Y + pers_stat_y[22], pPersonnelScreenStrings[PRSNL_TXT_ASSISTS]);
768 
769 	// Shots/hits
770 	MPrint(pers_stat_x, STD_SCREEN_Y + pers_stat_y[23], pPersonnelScreenStrings[PRSNL_TXT_HIT_PERCENTAGE]);
771 	// check we have shot at least once
772 	const UINT32 fired = p.usShotsFired;
773 	const UINT32 hits  = (fired > 0 ? 100 * p.usShotsHit / fired : 0);
774 	sString = ST::format("{} %", hits);
775 	FindFontRightCoordinates(pers_stat_x, 0, TEXT_BOX_WIDTH - 20, 0, sString, PERS_FONT, &sX, &sY);
776 	MPrint(sX, STD_SCREEN_Y + pers_stat_y[23], sString);
777 
778 	PrintStat(p.usBattlesFought, STD_SCREEN_Y + pers_stat_y[24], pPersonnelScreenStrings[PRSNL_TXT_BATTLES]);
779 	PrintStat(p.usTimesWounded,  STD_SCREEN_Y + pers_stat_y[25], pPersonnelScreenStrings[PRSNL_TXT_TIMES_WOUNDED]);
780 
781 	//Display the 'Skills' text
782 	MPrint(pers_stat_x, STD_SCREEN_Y + pers_stat_y[19], pPersonnelScreenStrings[PRSNL_TXT_SKILLS]);
783 
784 	/* KM: April 16, 1999
785 	 * Added support for the German version, which has potential string
786 	 * overrun problems.  For example, the text "Skills:" can overlap
787 	 * "NightOps (Expert)" because the German strings are much longer.  In
788 	 * these cases, I ensure that the right justification of the traits
789 	 * do not overlap.  If it would, I move it over to the right. */
790 	const INT32 iWidth = StringPixLength(pPersonnelScreenStrings[PRSNL_TXT_SKILLS], PERS_FONT);
791 	const INT32 iMinimumX = iWidth + pers_stat_x + 2;
792 
793 	if (!fAmIaRobot)
794 	{
795 		INT8 bSkill1 = p.bSkillTrait;
796 		INT8 bSkill2 = p.bSkillTrait2;
797 
798 		if (bSkill1 == NO_SKILLTRAIT)
799 		{
800 			bSkill1 = bSkill2;
801 			bSkill2 = NO_SKILLTRAIT;
802 		}
803 
804 		if (bSkill1 != NO_SKILLTRAIT)
805 		{
806 			if (bSkill1 == bSkill2)
807 			{
808 				// The 2 skills are the same, add the '(expert)' at the end
809 				sString = ST::format("{} {}", gzMercSkillText[bSkill1], gzMercSkillText[NUM_SKILLTRAITS]);
810 				FindFontRightCoordinates(pers_stat_x, 0, TEXT_BOX_WIDTH - 20, 0, sString, PERS_FONT, &sX, &sY);
811 
812 				//KM: April 16, 1999
813 				//Perform the potential overrun check
814 				if (sX <= iMinimumX)
815 				{
816 					FindFontRightCoordinates(pers_stat_x + TEXT_BOX_WIDTH - 20 + TEXT_DELTA_OFFSET, 0, 30, 0, sString, PERS_FONT, &sX, &sY);
817 					sX = MAX(sX, iMinimumX);
818 				}
819 
820 				MPrint(sX, STD_SCREEN_Y + pers_stat_y[19], sString);
821 			}
822 			else
823 			{
824 				ST::string Skill = gzMercSkillText[bSkill1];
825 
826 				FindFontRightCoordinates(pers_stat_x, 0, TEXT_BOX_WIDTH - 20, 0, Skill, PERS_FONT, &sX, &sY);
827 
828 				//KM: April 16, 1999
829 				//Perform the potential overrun check
830 				sX = MAX(sX, iMinimumX);
831 
832 				MPrint(sX, STD_SCREEN_Y + pers_stat_y[19], Skill);
833 
834 				if (bSkill2 != NO_SKILLTRAIT)
835 				{
836 					ST::string Skill = gzMercSkillText[bSkill2];
837 
838 					FindFontRightCoordinates(pers_stat_x, 0, TEXT_BOX_WIDTH - 20, 0, Skill, PERS_FONT, &sX, &sY);
839 
840 					//KM: April 16, 1999
841 					//Perform the potential overrun check
842 					sX = MAX(sX, iMinimumX);
843 
844 					MPrint(sX, STD_SCREEN_Y + pers_stat_y[20], Skill);
845 				}
846 			}
847 		}
848 		else
849 		{
850 			ST::string NoSkill = pPersonnelScreenStrings[PRSNL_TXT_NOSKILLS];
851 			FindFontRightCoordinates(pers_stat_x, 0, TEXT_BOX_WIDTH - 20, 0, NoSkill, PERS_FONT, &sX, &sY);
852 			MPrint(sX, STD_SCREEN_Y + pers_stat_y[19], NoSkill);
853 		}
854 	}
855 }
856 
857 
SetPersonnelButtonStates(void)858 static void SetPersonnelButtonStates(void)
859 {
860 	// this function will look at what page we are viewing, enable and disable buttons as needed
861 	const INT32 merc_count = fCurrentTeamMode ?
862 		GetNumberOfMercsDeadOrAliveOnPlayersTeam() :
863 		GetNumberOfPastMercsOnPlayersTeam();
864 	bool const enable = merc_count > 1;
865 	EnableButton(g_personnel.prev, enable);
866 	EnableButton(g_personnel.next, enable);
867 }
868 
869 
RenderPersonnelScreenBackground(void)870 static void RenderPersonnelScreenBackground(void)
871 {
872 	// this fucntion will render the background for the personnel screen
873 	BltVideoObject(FRAME_BUFFER, fCurrentTeamMode ? guiCURRENTTEAM : guiDEPARTEDTEAM, 0, DEPARTED_X, DEPARTED_Y);
874 }
875 
876 
LoadPersonnelScreenBackgroundGraphics(void)877 static void LoadPersonnelScreenBackgroundGraphics(void)
878 {
879 	// will load the graphics for the personeel screen background
880 
881 	// departed bar
882 	guiDEPARTEDTEAM = AddVideoObjectFromFile(LAPTOPDIR "/departed.sti");
883 
884 	// current bar
885 	guiCURRENTTEAM = AddVideoObjectFromFile(LAPTOPDIR "/currentteam.sti");
886 }
887 
888 
DeletePersonnelScreenBackgroundGraphics(void)889 static void DeletePersonnelScreenBackgroundGraphics(void)
890 {
891 	// delete background V/O's
892 	DeleteVideoObject(guiCURRENTTEAM);
893 	DeleteVideoObject(guiDEPARTEDTEAM);
894 }
895 
896 
GetNumberOfMercsDeadOrAliveOnPlayersTeam(void)897 static INT32 GetNumberOfMercsDeadOrAliveOnPlayersTeam(void)
898 {
899 	INT32 iCounter = 0;
900 
901 	// grab number on team
902 	CFOR_EACH_PERSONNEL(s) ++iCounter;
903 	return iCounter;
904 }
905 
906 
907 static void PersonnelPortraitCallback(MOUSE_REGION* pRegion, INT32 iReason);
908 
909 
CreateDestroyMouseRegionsForPersonnelPortraits(BOOLEAN create)910 static void CreateDestroyMouseRegionsForPersonnelPortraits(BOOLEAN create)
911 {
912 	// creates/destroys mouse regions for portraits
913 	static BOOLEAN fCreated = FALSE;
914 
915 	if (!fCreated && create)
916 	{
917 		// create regions
918 		for (INT16 i = 0; i < PERSONNEL_PORTRAIT_NUMBER; i++)
919 		{
920 			const UINT16 tlx = SMALL_PORTRAIT_START_X + i % PERSONNEL_PORTRAIT_NUMBER_WIDTH * SMALL_PORT_WIDTH;
921 			const UINT16 tly = SMALL_PORTRAIT_START_Y + i / PERSONNEL_PORTRAIT_NUMBER_WIDTH * SMALL_PORT_HEIGHT;
922 			const UINT16 brx = tlx + SMALL_PORTRAIT_WIDTH;
923 			const UINT16 bry = tly + SMALL_PORTRAIT_HEIGHT;
924 			MSYS_DefineRegion(&gPortraitMouseRegions[i], tlx, tly, brx, bry, MSYS_PRIORITY_HIGHEST, CURSOR_LAPTOP_SCREEN, MSYS_NO_CALLBACK, PersonnelPortraitCallback);
925 			MSYS_SetRegionUserData(&gPortraitMouseRegions[i], 0, i);
926 		}
927 
928 		fCreated = TRUE;
929 	}
930 	else if (fCreated && !create)
931 	{
932 		// destroy regions
933 		for (INT16 i = 0; i < PERSONNEL_PORTRAIT_NUMBER; i++)
934 		{
935 			MSYS_RemoveRegion(&gPortraitMouseRegions[i]);
936 		}
937 
938 		fCreated = FALSE;
939 	}
940 }
941 
942 
DisplayPicturesOfCurrentTeam(void)943 static void DisplayPicturesOfCurrentTeam(void)
944 try
945 {
946 	// will display the small portraits of the current team
947 	if (!fCurrentTeamMode) return;
948 
949 	INT32 i = 0;
950 	CFOR_EACH_PERSONNEL(s)
951 	{
952 		// found the next actual guy
953 		INT32 const x = SMALL_PORTRAIT_START_X + i % PERSONNEL_PORTRAIT_NUMBER_WIDTH * SMALL_PORT_WIDTH;
954 		INT32 const y = SMALL_PORTRAIT_START_Y + i / PERSONNEL_PORTRAIT_NUMBER_WIDTH * SMALL_PORT_HEIGHT;
955 		{ AutoSGPVObject guiFACE(LoadSmallPortrait(GetProfile(s->ubProfile)));
956 			if (s->bLife <= 0)
957 			{
958 				guiFACE->pShades[0] = Create16BPPPaletteShaded(guiFACE->Palette(), DEAD_MERC_COLOR_RED, DEAD_MERC_COLOR_GREEN, DEAD_MERC_COLOR_BLUE, TRUE);
959 				//set the red pallete to the face
960 				guiFACE->CurrentShade(0);
961 			}
962 			BltVideoObject(FRAME_BUFFER, guiFACE.get(), 0, x, y);
963 		}
964 
965 		if (s->bLife <= 0)
966 		{
967 			//if the merc is dead, display it
968 			DrawTextToScreen(AimPopUpText[AIM_MEMBER_DEAD], x, y + SMALL_PORT_HEIGHT / 2, SMALL_PORTRAIT_WIDTH_NO_BORDERS, FONT10ARIAL, 145, FONT_MCOLOR_BLACK, CENTER_JUSTIFIED);
969 		}
970 
971 		i++;
972 	}
973 }
974 catch (...) { /* XXX ignore */ }
975 
976 
977 static SOLDIERTYPE const& GetSoldierOfCurrentSlot(void);
978 
979 
PersonnelPortraitCallback(MOUSE_REGION * pRegion,INT32 iReason)980 static void PersonnelPortraitCallback(MOUSE_REGION* pRegion, INT32 iReason)
981 {
982 	INT32 iPortraitId = 0;
983 	INT32 iOldPortraitId;
984 
985 	iPortraitId = MSYS_GetRegionUserData(pRegion, 0);
986 	iOldPortraitId = iCurrentPersonSelectedId;
987 
988 	// callback handler for the minize region that is attatched to the laptop program icon
989 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
990 	{
991 		// get id of portrait
992 		if (fCurrentTeamMode)
993 		{
994 			// valid portrait, set up id
995 			if (iPortraitId >= GetNumberOfMercsDeadOrAliveOnPlayersTeam())
996 			{
997 				// not a valid id, leave
998 				return;
999 			}
1000 
1001 			iCurrentPersonSelectedId = iPortraitId;
1002 			fReDrawScreenFlag = TRUE;
1003 
1004 			if (iCurrentPersonSelectedId != -1 &&
1005 					GetSoldierOfCurrentSlot().bAssignment == ASSIGNMENT_POW &&
1006 					gubPersonnelInfoState == PRSNL_INV)
1007 			{
1008 				gubPersonnelInfoState = PRSNL_STATS;
1009 			}
1010 		}
1011 		else
1012 		{
1013 			if (iPortraitId >= GetNumberOfPastMercsOnPlayersTeam() - giCurrentUpperLeftPortraitNumber)
1014 			{
1015 				return;
1016 			}
1017 			iCurrentPersonSelectedId = iPortraitId;
1018 			fReDrawScreenFlag = TRUE;
1019 		}
1020 
1021 		if (iOldPortraitId != iPortraitId)
1022 		{
1023 			uiCurrentInventoryIndex = 0;
1024 			guiSliderPosition = 0;
1025 		}
1026 	}
1027 
1028 	if (iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
1029 	{
1030 		if (fCurrentTeamMode)
1031 		{
1032 			// valid portrait, set up id
1033 			if (iPortraitId >= GetNumberOfMercsDeadOrAliveOnPlayersTeam())
1034 			{
1035 				// not a valid id, leave
1036 				return;
1037 			}
1038 
1039 			//if the user is rigt clicking on the same face
1040 			if (iCurrentPersonSelectedId == iPortraitId)
1041 			{
1042 				//increment the info page when the user right clicks
1043 				if (gubPersonnelInfoState < PRSNL_INV)
1044 					gubPersonnelInfoState++;
1045 				else
1046 					gubPersonnelInfoState = PRSNL_STATS;
1047 			}
1048 
1049 			iCurrentPersonSelectedId = iPortraitId;
1050 			fReDrawScreenFlag = TRUE;
1051 
1052 			uiCurrentInventoryIndex = 0;
1053 			guiSliderPosition = 0;
1054 
1055 			//if the selected merc is valid, and they are a POW, change to the inventory display
1056 			if (iCurrentPersonSelectedId != -1 &&
1057 					GetSoldierOfCurrentSlot().bAssignment == ASSIGNMENT_POW &&
1058 					gubPersonnelInfoState == PRSNL_INV)
1059 			{
1060 				gubPersonnelInfoState = PRSNL_STATS;
1061 			}
1062 		}
1063 	}
1064 }
1065 
1066 
1067 struct PastMercInfo
1068 {
PastMercInfoPastMercInfo1069 	PastMercInfo(MERCPROFILESTRUCT const* const profile_, INT8 const state_) :
1070 		profile(profile_),
1071 		state(state_)
1072 	{}
1073 
1074 	MERCPROFILESTRUCT const* profile;
1075 	INT8                     state;
1076 };
1077 
1078 
1079 static void DisplayAmountOnChar(SOLDIERTYPE const&);
1080 static void DisplayDepartedCharName(MERCPROFILESTRUCT const&, INT32 iState);
1081 static void DisplayDepartedCharStats(MERCPROFILESTRUCT const&, INT32 iState);
1082 static void DisplayHighLightBox(INT32 sel_id);
1083 static PastMercInfo GetSelectedPastMercInfo(void);
1084 
1085 
DisplayFaceOfDisplayedMerc(void)1086 static void DisplayFaceOfDisplayedMerc(void)
1087 {
1088 	// valid person?, display
1089 	if (iCurrentPersonSelectedId == -1) return;
1090 
1091 	// highlight it
1092 	DisplayHighLightBox(iCurrentPersonSelectedId);
1093 
1094 	// if showing inventory, leave
1095 	if (fCurrentTeamMode)
1096 	{
1097 		SOLDIERTYPE const& s = GetSoldierOfCurrentSlot();
1098 		RenderPersonnelFace(GetProfile(s.ubProfile), s.bLife > 0);
1099 		DisplayCharName(s);
1100 		RenderPersonnelStats(s);
1101 		DisplayAmountOnChar(s);
1102 	}
1103 	else
1104 	{
1105 		const PastMercInfo info = GetSelectedPastMercInfo();
1106 		const MERCPROFILESTRUCT* const p = info.profile;
1107 		if (p == NULL) return;
1108 		RenderPersonnelFace(     *p, info.state != DEPARTED_DEAD);
1109 		DisplayDepartedCharName( *p, info.state);
1110 		DisplayDepartedCharStats(*p, info.state);
1111 	}
1112 }
1113 
1114 
1115 static void RenderSliderBarForPersonnelInventory(void);
1116 
1117 
1118 // display the inventory for this merc
DisplayCharInventory(SOLDIERTYPE const & s)1119 static void DisplayCharInventory(SOLDIERTYPE const& s)
1120 {
1121 	CreateDestroyPersonnelInventoryScrollButtons();
1122 
1123 	BltVideoObject(FRAME_BUFFER, guiPersonnelInventory, 0, STD_SCREEN_X + 397, STD_SCREEN_Y +  200);
1124 
1125 	// render the bar for the character
1126 	RenderSliderBarForPersonnelInventory();
1127 
1128 	// if this is a robot, don't display any inventory
1129 	if (AM_A_ROBOT(&s)) return;
1130 
1131 	INT32 item_count = -(INT32)uiCurrentInventoryIndex;
1132 	for (UINT pos = 0; pos < NUM_INV_SLOTS; ++pos)
1133 	{
1134 		//if the character is a robot, only display the inv for the hand pos
1135 		if (s.ubProfile == ROBOT && pos != HANDPOS) continue; // XXX can this ever be true? before is if (AM_A_ROBOT()) return;
1136 
1137 		OBJECTTYPE const& o       = s.inv[pos];
1138 		INT32      const  o_count = o.ubNumberOfObjects;
1139 		if (o_count == 0) continue;
1140 
1141 		if (item_count < 0)
1142 		{
1143 			++item_count;
1144 			continue;
1145 		}
1146 
1147 		ST::string sString;
1148 		INT16   sX;
1149 		INT16   sY;
1150 
1151 		const INT16 PosX = STD_SCREEN_X + 397 + 3;
1152 		const INT16 PosY = STD_SCREEN_Y +  200 + 8 + item_count * 29;
1153 
1154 		UINT16  const  item_idx = o.usItem;
1155 		const ItemModel * item = GCM->getItem(item_idx);
1156 
1157 		SGPVObject  const& gfx   = GetInterfaceGraphicForItem(item);
1158 		ETRLEObject const& pTrav = gfx.SubregionProperties(item->getGraphicNum());
1159 		INT16       const  cen_x = PosX + ABS(57 - pTrav.usWidth)  / 2 - pTrav.sOffsetX;
1160 		INT16       const  cen_y = PosY + ABS(22 - pTrav.usHeight) / 2 - pTrav.sOffsetY;
1161 		BltVideoObjectOutline(FRAME_BUFFER, &gfx, item->getGraphicNum(), cen_x, cen_y, SGP_TRANSPARENT);
1162 
1163 		SetFontDestBuffer(FRAME_BUFFER);
1164 
1165 		sString = ItemNames[item_idx];
1166 		sString = ReduceStringLength(sString, 171 - 75, FONT10ARIAL);
1167 		MPrint(PosX + 65, PosY + 3, sString);
1168 
1169 		// condition
1170 		if (item->isAmmo())
1171 		{
1172 			INT32 total_ammo = 0;
1173 			for (INT32 i = 0; i < o_count; ++i) total_ammo += o.ubShotsLeft[i];
1174 			sString = ST::format("{}/{}", total_ammo, o_count * item->asAmmo()->capacity);
1175 		}
1176 		else
1177 		{
1178 			sString = ST::format("{2d}%", o.bStatus[0]);
1179 		}
1180 
1181 		FindFontRightCoordinates(PosX + 65, PosY + 15, 171 - 75, GetFontHeight(FONT10ARIAL), sString, FONT10ARIAL, &sX, &sY);
1182 		MPrint(sX, sY, sString);
1183 
1184 		if (item->isGun())
1185 		{
1186 			sString = *item->asWeapon()->calibre->getName();
1187 			sString = ReduceStringLength(sString, 171 - 75, FONT10ARIAL);
1188 			MPrint(PosX + 65, PosY + 15, sString);
1189 		}
1190 
1191 		// if more than 1?
1192 		if (o_count > 1)
1193 		{
1194 			sString = ST::format("x{}", o_count);
1195 			FindFontRightCoordinates(PosX, PosY + 15, 58, GetFontHeight(FONT10ARIAL), sString, FONT10ARIAL, &sX, &sY);
1196 			MPrint(sX, sY, sString);
1197 		}
1198 
1199 		if (++item_count == NUMBER_OF_INVENTORY_PERSONNEL) break;
1200 	}
1201 }
1202 
1203 
1204 static void FindPositionOfPersInvSlider(void);
1205 
1206 
InventoryUp(void)1207 static void InventoryUp(void)
1208 {
1209 	if (uiCurrentInventoryIndex == 0) return;
1210 	uiCurrentInventoryIndex--;
1211 	fReDrawScreenFlag = TRUE;
1212 	FindPositionOfPersInvSlider();
1213 }
1214 
1215 
1216 static INT32 GetNumberOfInventoryItemsOnCurrentMerc(void);
1217 
1218 
InventoryDown(void)1219 static void InventoryDown(void)
1220 {
1221 	if ((INT32)uiCurrentInventoryIndex >= (INT32)(GetNumberOfInventoryItemsOnCurrentMerc() - NUMBER_OF_INVENTORY_PERSONNEL))
1222 	{
1223 		return;
1224 	}
1225 	uiCurrentInventoryIndex++;
1226 	fReDrawScreenFlag = TRUE;
1227 	FindPositionOfPersInvSlider();
1228 }
1229 
1230 
InventoryUpButtonCallback(GUI_BUTTON * btn,INT32 reason)1231 static void InventoryUpButtonCallback(GUI_BUTTON *btn, INT32 reason)
1232 {
1233 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_REPEAT ||
1234 			reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1235 	{
1236 		InventoryUp();
1237 	}
1238 }
1239 
1240 
InventoryDownButtonCallback(GUI_BUTTON * btn,INT32 reason)1241 static void InventoryDownButtonCallback(GUI_BUTTON *btn, INT32 reason)
1242 {
1243 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_REPEAT ||
1244 			reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1245 	{
1246 		InventoryDown();
1247 	}
1248 }
1249 
1250 
1251 // decide which buttons can and can't be accessed based on what the current item is
EnableDisableInventoryScrollButtons(void)1252 static void EnableDisableInventoryScrollButtons(void)
1253 {
1254 	if (gubPersonnelInfoState != PRSNL_INV)
1255 	{
1256 		return;
1257 	}
1258 
1259 	if (uiCurrentInventoryIndex == 0)
1260 	{
1261 		giPersonnelInventoryButtons[0]->uiFlags &= ~BUTTON_CLICKED_ON;
1262 		DisableButton(giPersonnelInventoryButtons[0]);
1263 	}
1264 	else
1265 	{
1266 		EnableButton(giPersonnelInventoryButtons[0]);
1267 	}
1268 
1269 
1270 	if ((INT32)uiCurrentInventoryIndex >= (INT32)(GetNumberOfInventoryItemsOnCurrentMerc() - NUMBER_OF_INVENTORY_PERSONNEL))
1271 	{
1272 		giPersonnelInventoryButtons[1]->uiFlags &= ~BUTTON_CLICKED_ON;
1273 		DisableButton(giPersonnelInventoryButtons[1]);
1274 	}
1275 	else
1276 	{
1277 		EnableButton(giPersonnelInventoryButtons[1]);
1278 	}
1279 }
1280 
1281 
GetNumberOfInventoryItemsOnCurrentMerc(void)1282 static INT32 GetNumberOfInventoryItemsOnCurrentMerc(void)
1283 {
1284 	// in current team mode?..nope...move on
1285 	if (!fCurrentTeamMode) return 0;
1286 
1287 	UINT32             ubCount = 0;
1288 	SOLDIERTYPE const& s       = GetSoldierOfCurrentSlot();
1289 	CFOR_EACH_SOLDIER_INV_SLOT(i, s)
1290 	{
1291 		OBJECTTYPE const& o = *i;
1292 		if (o.ubNumberOfObjects != 0 && o.usItem != NOTHING) ubCount++;
1293 	}
1294 
1295 	return ubCount;
1296 }
1297 
1298 
HandleInventoryCallBack(MOUSE_REGION * pRegion,INT32 iReason)1299 static void HandleInventoryCallBack(MOUSE_REGION* pRegion, INT32 iReason)
1300 {
1301 	if (iReason & MSYS_CALLBACK_REASON_WHEEL_UP)
1302 	{
1303 		InventoryUp();
1304 	}
1305 	else if (iReason & MSYS_CALLBACK_REASON_WHEEL_DOWN)
1306 	{
1307 		InventoryDown();
1308 	}
1309 }
1310 
1311 
1312 static MOUSE_REGION InventoryRegion;
1313 
1314 
1315 static void HandleSliderBarClickCallback(MOUSE_REGION* pRegion, INT32 iReason);
1316 
1317 
CreateDestroyPersonnelInventoryScrollButtons(void)1318 static void CreateDestroyPersonnelInventoryScrollButtons(void)
1319 {
1320 	static BOOLEAN fCreated = FALSE;
1321 
1322 	if (gubPersonnelInfoState == PRSNL_INV && !fCreated)
1323 	{
1324 		giPersonnelInventoryButtons[0] = MakeButton(LAPTOPDIR "/personnel_inventory.sti", 1, 2, STD_SCREEN_X + 176 + 397, STD_SCREEN_Y +   2 + 200, InventoryUpButtonCallback);
1325 		giPersonnelInventoryButtons[1] = MakeButton(LAPTOPDIR "/personnel_inventory.sti", 3, 4, STD_SCREEN_X + 176 + 397, STD_SCREEN_Y + 200 + 223, InventoryDownButtonCallback);
1326 
1327 		MSYS_DefineRegion(&gMouseScrollPersonnelINV, X_OF_PERSONNEL_SCROLL_REGION, Y_OF_PERSONNEL_SCROLL_REGION, X_OF_PERSONNEL_SCROLL_REGION + X_SIZE_OF_PERSONNEL_SCROLL_REGION, Y_OF_PERSONNEL_SCROLL_REGION + Y_SIZE_OF_PERSONNEL_SCROLL_REGION, MSYS_PRIORITY_HIGHEST - 3, CURSOR_LAPTOP_SCREEN, MSYS_NO_CALLBACK, HandleSliderBarClickCallback);
1328 
1329 		const UINT16 x = INVENTORY_BOX_X;
1330 		const UINT16 y = INVENTORY_BOX_Y;
1331 		const UINT16 w = INVENTORY_BOX_W;
1332 		const UINT16 h = INVENTORY_BOX_H;
1333 		MSYS_DefineRegion(&InventoryRegion, x, y, x + w, y + h, MSYS_PRIORITY_HIGHEST - 3, CURSOR_LAPTOP_SCREEN, MSYS_NO_CALLBACK, HandleInventoryCallBack);
1334 
1335 		fCreated = TRUE;
1336 	}
1337 	else if (fCreated && gubPersonnelInfoState != PRSNL_INV)
1338 	{
1339 		// destroy buttons
1340 		RemoveButton(giPersonnelInventoryButtons[0]);
1341 		RemoveButton(giPersonnelInventoryButtons[1]);
1342 
1343 		MSYS_RemoveRegion(&gMouseScrollPersonnelINV);
1344 		MSYS_RemoveRegion(&InventoryRegion);
1345 
1346 		fCreated = FALSE;
1347 	}
1348 }
1349 
1350 
1351 static void DisplayCostOfCurrentTeam(void);
1352 static void DisplayStateOfPastTeamMembers(void);
1353 
1354 
DisplayPersonnelSummary(void)1355 static void DisplayPersonnelSummary(void)
1356 {
1357 	// display number on team
1358 	SetFontAttributes(FONT10ARIAL, PERS_TEXT_FONT_COLOR);
1359 
1360 	if (fCurrentTeamMode)
1361 	{
1362 		MPrint(PERS_CURR_TEAM_X, PERS_CURR_TEAM_Y, ST::format("{} ( {} )", pPersonelTeamStrings[0], GetNumberOfMercsDeadOrAliveOnPlayersTeam()));
1363 		DisplayCostOfCurrentTeam();
1364 
1365 		ST::string s = pPersonelTeamStrings[1];
1366 		INT16 sX = 0;
1367 		INT16 sY = 0;
1368 		FindFontCenterCoordinates(PERS_CURR_TEAM_X, 0, 65, 0, s, FONT10ARIAL, &sX, &sY);
1369 		MPrint(sX, PERS_DEPART_TEAM_Y, s);
1370 	}
1371 	else
1372 	{
1373 		ST::string s = pPersonelTeamStrings[0];
1374 		INT16 sX = 0;
1375 		INT16 sY = 0;
1376 		FindFontCenterCoordinates(PERS_CURR_TEAM_X, 0, 65, 0, s, FONT10ARIAL, &sX, &sY);
1377 		MPrint(sX, PERS_CURR_TEAM_Y, s);
1378 
1379 		MPrint(PERS_CURR_TEAM_X, PERS_DEPART_TEAM_Y, ST::format("{} ( {} )", pPersonelTeamStrings[1], GetNumberOfPastMercsOnPlayersTeam()));
1380 		DisplayStateOfPastTeamMembers();
1381 	}
1382 }
1383 
1384 
DisplayCostOfCurrentTeam(void)1385 static void DisplayCostOfCurrentTeam(void)
1386 {
1387 	INT32 min_cost = 999999;
1388 	INT32 max_cost = 0;
1389 	INT32 sum_cost = 0;
1390 
1391 	CFOR_EACH_PERSONNEL(s)
1392 	{
1393 		if (s->bLife <= 0) continue;
1394 
1395 		// valid soldier, get cost
1396 		INT32 cost;
1397 		MERCPROFILESTRUCT const& p = GetProfile(s->ubProfile);
1398 		switch (s->ubWhatKindOfMercAmI)
1399 		{
1400 			case MERC_TYPE__AIM_MERC:
1401 				switch (s->bTypeOfLastContract)
1402 				{
1403 					case CONTRACT_EXTEND_2_WEEK: cost = p.uiBiWeeklySalary / 14; break;
1404 					case CONTRACT_EXTEND_1_WEEK: cost = p.uiWeeklySalary   /  7; break;
1405 					default:                     cost = p.sSalary;               break;
1406 				}
1407 				break;
1408 
1409 			default: cost = p.sSalary; break;
1410 		}
1411 
1412 		if (cost > max_cost) max_cost = cost;
1413 		if (cost < min_cost) min_cost = cost;
1414 		sum_cost += cost;
1415 	}
1416 
1417 	if (min_cost == 999999) min_cost = 0;
1418 
1419 	INT16 sX;
1420 	INT16 sY;
1421 
1422 	// daily cost
1423 	MPrint(PERS_CURR_TEAM_COST_X, PERS_CURR_TEAM_COST_Y, pPersonelTeamStrings[2]);
1424 	ST::string sString = SPrintMoney(sum_cost);
1425 	FindFontRightCoordinates(PERS_CURR_TEAM_COST_X, 0, PERS_CURR_TEAM_WIDTH, 0, sString, PERS_FONT, &sX, &sY);
1426 	MPrint(sX, PERS_CURR_TEAM_COST_Y, sString);
1427 
1428 	// highest cost
1429 	MPrint(PERS_CURR_TEAM_COST_X, PERS_CURR_TEAM_HIGHEST_Y, pPersonelTeamStrings[3]);
1430 	sString = SPrintMoney(max_cost);
1431 	FindFontRightCoordinates(PERS_CURR_TEAM_COST_X, 0, PERS_CURR_TEAM_WIDTH, 0, sString, PERS_FONT, &sX, &sY);
1432 	MPrint(sX, PERS_CURR_TEAM_HIGHEST_Y, sString);
1433 
1434 	// the lowest cost
1435 	MPrint(PERS_CURR_TEAM_COST_X, PERS_CURR_TEAM_LOWEST_Y, pPersonelTeamStrings[4]);
1436 	sString = SPrintMoney(min_cost);
1437 	FindFontRightCoordinates(PERS_CURR_TEAM_COST_X, 0, PERS_CURR_TEAM_WIDTH, 0, sString, PERS_FONT, &sX, &sY);
1438 	MPrint(sX, PERS_CURR_TEAM_LOWEST_Y, sString);
1439 }
1440 
1441 
DisplayTeamStats(void)1442 static void DisplayTeamStats(void)
1443 {
1444 	INT16 sX;
1445 	INT16 sY;
1446 
1447 	SetFontAttributes(FONT10ARIAL, PERS_TEXT_FONT_COLOR);
1448 
1449 	// display headers
1450 	// lowest
1451 	FindFontCenterCoordinates(PERS_STAT_LOWEST_X,  0, PERS_STAT_LOWEST_WIDTH, 0, pPersonnelCurrentTeamStatsStrings[0], FONT10ARIAL, &sX, &sY);
1452 	MPrint(sX, PERS_STAT_AVG_Y, pPersonnelCurrentTeamStatsStrings[0]);
1453 	// average
1454 	FindFontCenterCoordinates(PERS_STAT_AVG_X,     0, PERS_STAT_AVG_WIDTH,    0, pPersonnelCurrentTeamStatsStrings[1], FONT10ARIAL, &sX, &sY);
1455 	MPrint(sX, PERS_STAT_AVG_Y, pPersonnelCurrentTeamStatsStrings[1]);
1456 	// highest
1457 	FindFontCenterCoordinates(PERS_STAT_HIGHEST_X, 0, PERS_STAT_LOWEST_WIDTH, 0, pPersonnelCurrentTeamStatsStrings[2], FONT10ARIAL, &sX, &sY);
1458 	MPrint(sX, PERS_STAT_AVG_Y, pPersonnelCurrentTeamStatsStrings[2]);
1459 
1460 	for (INT32 stat = 0; stat < 11; stat++)
1461 	{
1462 		// even or odd?..color black or yellow?
1463 		SetFontForeground(stat % 2 == 0 ? PERS_TEXT_FONT_ALTERNATE_COLOR : PERS_TEXT_FONT_COLOR);
1464 
1465 		const INT32 y = PERS_STAT_AVG_Y + (stat + 1) * (GetFontHeight(FONT10ARIAL) + 3);
1466 
1467 		// row header
1468 		MPrint(PERS_STAT_LIST_X, y, pPersonnelTeamStatsStrings[stat]);
1469 
1470 		ST::string min_name;
1471 		ST::string max_name;
1472 		INT32 min_val           = 100;
1473 		INT32 max_val           = 0;
1474 		INT32 sum_val           = 0;
1475 		INT32 count             = 0;
1476 		if (fCurrentTeamMode)
1477 		{
1478 			CFOR_EACH_PERSONNEL(s)
1479 			{
1480 				if (s->bLife <= 0 || AM_A_ROBOT(s)) continue;
1481 
1482 				INT32 val; // XXX HACK000E
1483 				switch (stat)
1484 				{
1485 					case  0: val = s->bLifeMax;      break;
1486 					case  1: val = s->bAgility;      break;
1487 					case  2: val = s->bDexterity;    break;
1488 					case  3: val = s->bStrength;     break;
1489 					case  4: val = s->bLeadership;   break;
1490 					case  5: val = s->bWisdom;       break;
1491 					case  6: val = s->bExpLevel;     break;
1492 					case  7: val = s->bMarksmanship; break;
1493 					case  8: val = s->bMechanical;   break;
1494 					case  9: val = s->bExplosive;    break;
1495 					case 10: val = s->bMedical;      break;
1496 
1497 					default: abort(); // HACK000E
1498 				}
1499 				if (min_val >= val)
1500 				{
1501 					min_name = s->name;
1502 					min_val  = val;
1503 				}
1504 				if (max_val <= val)
1505 				{
1506 					max_name = s->name;
1507 					max_val = val;
1508 				}
1509 				sum_val += val;
1510 				++count;
1511 			}
1512 		}
1513 		else
1514 		{
1515 			for (UINT CurrentList = 0; CurrentList < 3; ++CurrentList)
1516 			{
1517 				const INT16* CurrentListValue; // XXX HACK000E
1518 				switch (CurrentList)
1519 				{
1520 					case 0: CurrentListValue = LaptopSaveInfo.ubDeadCharactersList;  break;
1521 					case 1: CurrentListValue = LaptopSaveInfo.ubLeftCharactersList;  break;
1522 					case 2: CurrentListValue = LaptopSaveInfo.ubOtherCharactersList; break;
1523 
1524 					default: abort(); // HACK000E
1525 				}
1526 
1527 				for (UINT32 i = 0; i < 256; i++)
1528 				{
1529 					const INT32 id = CurrentListValue[i];
1530 					if (id == -1) continue;
1531 
1532 					INT32 val; // XXX HACK000E
1533 					MERCPROFILESTRUCT const& p = GetProfile(id);
1534 					switch (stat)
1535 					{
1536 						case  0: val = p.bLifeMax;      break;
1537 						case  1: val = p.bAgility;      break;
1538 						case  2: val = p.bDexterity;    break;
1539 						case  3: val = p.bStrength;     break;
1540 						case  4: val = p.bLeadership;   break;
1541 						case  5: val = p.bWisdom;       break;
1542 						case  6: val = p.bExpLevel;     break;
1543 						case  7: val = p.bMarksmanship; break;
1544 						case  8: val = p.bMechanical;   break;
1545 						case  9: val = p.bExplosive;    break;
1546 						case 10: val = p.bMedical;      break;
1547 
1548 						default: abort(); // HACK000E
1549 					}
1550 					if (min_val >= val)
1551 					{
1552 						min_name = p.zNickname;
1553 						min_val  = val;
1554 					}
1555 					if (max_val <= val)
1556 					{
1557 						max_name = p.zNickname;
1558 						max_val = val;
1559 					}
1560 					sum_val += val;
1561 					++count;
1562 				}
1563 			}
1564 		}
1565 
1566 		if (count == 0) continue;
1567 
1568 		MPrint(PERS_STAT_LOWEST_X,  y, min_name);
1569 		MPrint(PERS_STAT_HIGHEST_X, y, max_name);
1570 
1571 		ST::string val_str;
1572 
1573 		val_str = ST::format("{}", min_val);
1574 		FindFontRightCoordinates(PERS_STAT_LOWEST_X, 0, PERS_STAT_LOWEST_WIDTH, 0, val_str, FONT10ARIAL, &sX, &sY);
1575 		MPrint(sX, y, val_str);
1576 
1577 		val_str = ST::format("{}", sum_val / count);
1578 		FindFontCenterCoordinates(PERS_STAT_AVG_X, 0, PERS_STAT_AVG_WIDTH, 0, val_str, FONT10ARIAL, &sX, &sY);
1579 		MPrint(sX, y, val_str);
1580 
1581 		val_str = ST::format("{}", max_val);
1582 		FindFontRightCoordinates(PERS_STAT_HIGHEST_X, 0, PERS_STAT_LOWEST_WIDTH, 0, val_str, FONT10ARIAL, &sX, &sY);
1583 		MPrint(sX, y, val_str);
1584 	}
1585 }
1586 
1587 
1588 static INT32 GetNumberOfDeadOnPastTeam(void);
1589 static INT32 GetNumberOfLeftOnPastTeam(void);
1590 static INT32 GetNumberOfOtherOnPastTeam(void);
1591 
1592 
GetNumberOfPastMercsOnPlayersTeam(void)1593 static INT32 GetNumberOfPastMercsOnPlayersTeam(void)
1594 {
1595 	INT32 iPastNumberOfMercs = 0;
1596 	// will run through the list of past mercs on the players team and return their number
1597 
1598 	iPastNumberOfMercs += GetNumberOfDeadOnPastTeam();
1599 	iPastNumberOfMercs += GetNumberOfLeftOnPastTeam();
1600 	iPastNumberOfMercs += GetNumberOfOtherOnPastTeam();
1601 
1602 	return iPastNumberOfMercs;
1603 }
1604 
1605 
InitPastCharactersList(void)1606 static void InitPastCharactersList(void)
1607 {
1608 	// inits the past characters list
1609 	std::fill(std::begin(LaptopSaveInfo.ubDeadCharactersList), std::end(LaptopSaveInfo.ubDeadCharactersList), -1);
1610 	std::fill(std::begin(LaptopSaveInfo.ubLeftCharactersList), std::end(LaptopSaveInfo.ubLeftCharactersList), -1);
1611 	std::fill(std::begin(LaptopSaveInfo.ubOtherCharactersList), std::end(LaptopSaveInfo.ubOtherCharactersList), -1);
1612 }
1613 
1614 
CountList(const INT16 * const list)1615 static INT32 CountList(const INT16* const list)
1616 {
1617 	INT32 count = 0;
1618 	for (const INT16* i = list, * const end = list + 256; i != end; ++i)
1619 	{
1620 		if (*i != -1) ++count;
1621 	}
1622 	return count;
1623 }
1624 
1625 
GetNumberOfDeadOnPastTeam(void)1626 static INT32 GetNumberOfDeadOnPastTeam(void)
1627 {
1628 	return CountList(LaptopSaveInfo.ubDeadCharactersList);
1629 }
1630 
1631 
GetNumberOfLeftOnPastTeam(void)1632 static INT32 GetNumberOfLeftOnPastTeam(void)
1633 {
1634 	return CountList(LaptopSaveInfo.ubLeftCharactersList);
1635 }
1636 
1637 
GetNumberOfOtherOnPastTeam(void)1638 static INT32 GetNumberOfOtherOnPastTeam(void)
1639 {
1640 	return CountList(LaptopSaveInfo.ubOtherCharactersList);
1641 }
1642 
1643 
1644 // diplays numbers fired, dead and other
DisplayStateOfPastTeamMembers(void)1645 static void DisplayStateOfPastTeamMembers(void)
1646 {
1647 	INT16 sX, sY;
1648 	ST::string sString;
1649 
1650 	// dead
1651 	MPrint(PERS_CURR_TEAM_COST_X, PERS_CURR_TEAM_COST_Y, pPersonelTeamStrings[5]);
1652 	sString = ST::format("{}", GetNumberOfDeadOnPastTeam());
1653 	FindFontRightCoordinates(PERS_CURR_TEAM_COST_X, 0, PERS_DEPART_TEAM_WIDTH, 0, sString, PERS_FONT, &sX, &sY);
1654 	MPrint(sX, PERS_CURR_TEAM_COST_Y, sString);
1655 
1656 	// fired
1657 	MPrint(PERS_CURR_TEAM_COST_X, PERS_CURR_TEAM_HIGHEST_Y, pPersonelTeamStrings[6]);
1658 	sString = ST::format("{}", GetNumberOfLeftOnPastTeam());
1659 	FindFontRightCoordinates(PERS_CURR_TEAM_COST_X, 0, PERS_DEPART_TEAM_WIDTH, 0, sString, PERS_FONT, &sX, &sY);
1660 	MPrint(sX, PERS_CURR_TEAM_HIGHEST_Y, sString);
1661 
1662 	// other
1663 	MPrint(PERS_CURR_TEAM_COST_X, PERS_CURR_TEAM_LOWEST_Y, pPersonelTeamStrings[7]);
1664 	sString = ST::format("{}", GetNumberOfOtherOnPastTeam());
1665 	FindFontRightCoordinates(PERS_CURR_TEAM_COST_X, 0, PERS_DEPART_TEAM_WIDTH, 0, sString, PERS_FONT, &sX, &sY);
1666 	MPrint(sX, PERS_CURR_TEAM_LOWEST_Y, sString);
1667 }
1668 
1669 
1670 static void PersonnelCurrentTeamCallback(MOUSE_REGION* pRegion, INT32 iReason);
1671 static void PersonnelDepartedTeamCallback(MOUSE_REGION* pRegion, INT32 iReason);
1672 
1673 
CreateDestroyCurrentDepartedMouseRegions(BOOLEAN create)1674 static void CreateDestroyCurrentDepartedMouseRegions(BOOLEAN create)
1675 {
1676 	static BOOLEAN fCreated = FALSE;
1677 
1678 	// will arbitrate the creation/deletion of mouse regions for current/past team toggles
1679 
1680 	if (create && !fCreated)
1681 	{
1682 		// not created, create
1683 		UINT16 tlx = PERS_TOGGLE_CUR_DEPART_X;
1684 		UINT16 tly = PERS_TOGGLE_CUR_Y;
1685 		UINT16 brx = tlx + PERS_TOGGLE_CUR_DEPART_WIDTH;
1686 		UINT16 bry = tly + PERS_TOGGLE_CUR_DEPART_HEIGHT;
1687 		MSYS_DefineRegion(&gTogglePastCurrentTeam[0], tlx, tly, brx, bry, MSYS_PRIORITY_HIGHEST - 3, CURSOR_LAPTOP_SCREEN, MSYS_NO_CALLBACK, PersonnelCurrentTeamCallback);
1688 
1689 		tly = PERS_TOGGLE_DEPART_Y;
1690 		bry = tly + PERS_TOGGLE_CUR_DEPART_HEIGHT;
1691 		MSYS_DefineRegion(&gTogglePastCurrentTeam[1], tlx, tly, brx, bry, MSYS_PRIORITY_HIGHEST - 3, CURSOR_LAPTOP_SCREEN, MSYS_NO_CALLBACK, PersonnelDepartedTeamCallback);
1692 
1693 		fCreated = TRUE;
1694 	}
1695 	else if (!create && fCreated)
1696 	{
1697 		// created, get rid of
1698 
1699 		MSYS_RemoveRegion(&gTogglePastCurrentTeam[0]);
1700 		MSYS_RemoveRegion(&gTogglePastCurrentTeam[1]);
1701 		fCreated = FALSE;
1702 	}
1703 }
1704 
1705 
PersonnelCurrentTeamCallback(MOUSE_REGION * pRegion,INT32 iReason)1706 static void PersonnelCurrentTeamCallback(MOUSE_REGION* pRegion, INT32 iReason)
1707 {
1708 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1709 	{
1710 		if (fCurrentTeamMode) return;
1711 		fCurrentTeamMode = TRUE;
1712 
1713 		SelectFirstDisplayedMerc();
1714 		SetPersonnelButtonStates();
1715 		fReDrawScreenFlag = TRUE;
1716 	}
1717 }
1718 
1719 
PersonnelDepartedTeamCallback(MOUSE_REGION * pRegion,INT32 iReason)1720 static void PersonnelDepartedTeamCallback(MOUSE_REGION* pRegion, INT32 iReason)
1721 {
1722 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1723 	{
1724 		if (!fCurrentTeamMode) return;
1725 		fCurrentTeamMode = FALSE;
1726 
1727 		SelectFirstDisplayedMerc();
1728 		SetPersonnelButtonStates();
1729 
1730 		//Switch the panel on the right to be the stat panel
1731 		gubPersonnelInfoState = PRSNL_STATS;
1732 
1733 		fReDrawScreenFlag = TRUE;
1734 	}
1735 }
1736 
1737 
1738 static void DepartedDownCallBack(GUI_BUTTON* btn, INT32 reason);
1739 static void DepartedUpCallBack(GUI_BUTTON* btn, INT32 reason);
1740 
1741 
CreateDestroyButtonsForDepartedTeamList(const BOOLEAN create)1742 static void CreateDestroyButtonsForDepartedTeamList(const BOOLEAN create)
1743 {
1744 	// creates/ destroys the buttons for cdeparted team list
1745 	static BOOLEAN fCreated = FALSE;
1746 
1747 	if (create)
1748 	{
1749 		if (fCreated) return;
1750 		// not created. create
1751 		g_personnel.depart_up = MakeButton(LAPTOPDIR "/departuresbuttons.sti", 0, 2, PERS_DEPARTED_UP_X, PERS_DEPARTED_UP_Y,   DepartedUpCallBack);
1752 		g_personnel.depart_dn = MakeButton(LAPTOPDIR "/departuresbuttons.sti", 1, 3, PERS_DEPARTED_UP_X, PERS_DEPARTED_DOWN_Y, DepartedDownCallBack);
1753 	}
1754 	else
1755 	{
1756 		if (!fCreated) return;
1757 		// created. destroy
1758 		RemoveButton(g_personnel.depart_up);
1759 		RemoveButton(g_personnel.depart_dn);
1760 		fReDrawScreenFlag = TRUE;
1761 	}
1762 	fCreated = create;
1763 }
1764 
1765 
DepartedUpCallBack(GUI_BUTTON * btn,INT32 reason)1766 static void DepartedUpCallBack(GUI_BUTTON *btn, INT32 reason)
1767 {
1768 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1769 	{
1770 		if (giCurrentUpperLeftPortraitNumber - PERSONNEL_PORTRAIT_NUMBER >= 0)
1771 		{
1772 			giCurrentUpperLeftPortraitNumber -= PERSONNEL_PORTRAIT_NUMBER;
1773 			fReDrawScreenFlag = TRUE;
1774 		}
1775 	}
1776 }
1777 
1778 
DepartedDownCallBack(GUI_BUTTON * btn,INT32 reason)1779 static void DepartedDownCallBack(GUI_BUTTON *btn, INT32 reason)
1780 {
1781 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1782 	{
1783 		INT32 const n_past = GetNumberOfPastMercsOnPlayersTeam();
1784 		if (n_past - giCurrentUpperLeftPortraitNumber > PERSONNEL_PORTRAIT_NUMBER)
1785 		{
1786 			giCurrentUpperLeftPortraitNumber += PERSONNEL_PORTRAIT_NUMBER;
1787 			if (iCurrentPersonSelectedId >= n_past - giCurrentUpperLeftPortraitNumber)
1788 			{
1789 				iCurrentPersonSelectedId = n_past - giCurrentUpperLeftPortraitNumber - 1;
1790 			}
1791 			fReDrawScreenFlag = TRUE;
1792 		}
1793 	}
1794 }
1795 
1796 
1797 static void DisplayPortraitOfPastMerc(INT32 iId, INT32 iCounter, BOOLEAN fDead);
1798 
1799 
1800 /* Display past mercs portraits, starting at giCurrentUpperLeftPortraitNumber
1801  * and going up PERSONNEL_PORTRAIT_NUMBER mercs. Start at dead mercs, then
1802  * fired, then other */
DisplayPastMercsPortraits()1803 static void DisplayPastMercsPortraits()
1804 {
1805 	if (fCurrentTeamMode) return; // Not time to display
1806 
1807 	LaptopSaveInfoStruct const& l   = LaptopSaveInfo;
1808 	INT32                       pos = -giCurrentUpperLeftPortraitNumber;
1809 
1810 	FOR_EACH(INT16 const, i, l.ubDeadCharactersList)
1811 	{
1812 		if (*i == -1) continue;
1813 		if (pos >= 0) DisplayPortraitOfPastMerc(*i, pos, TRUE);
1814 		if (++pos == PERSONNEL_PORTRAIT_NUMBER) return;
1815 	}
1816 
1817 	FOR_EACH(INT16 const, i, l.ubLeftCharactersList)
1818 	{
1819 		if (*i == -1) continue;
1820 		if (pos >= 0) DisplayPortraitOfPastMerc(*i, pos, FALSE);
1821 		if (++pos == PERSONNEL_PORTRAIT_NUMBER) return;
1822 	}
1823 
1824 	FOR_EACH(INT16 const, i, l.ubOtherCharactersList)
1825 	{
1826 		if (*i == -1) continue;
1827 		if (pos >= 0) DisplayPortraitOfPastMerc(*i, pos, FALSE);
1828 		if (++pos == PERSONNEL_PORTRAIT_NUMBER) return;
1829 	}
1830 }
1831 
1832 
1833 // returns ID of Merc in this slot
GetSelectedPastMercInfo(void)1834 static PastMercInfo GetSelectedPastMercInfo(void)
1835 {
1836 	INT32 slot = giCurrentUpperLeftPortraitNumber + iCurrentPersonSelectedId;
1837 	Assert(slot < GetNumberOfPastMercsOnPlayersTeam());
1838 
1839 	LaptopSaveInfoStruct const& l = LaptopSaveInfo;
1840 	FOR_EACH(INT16 const, i, l.ubDeadCharactersList)
1841 	{
1842 		if (*i == -1 || slot-- != 0) continue;
1843 		return PastMercInfo(&GetProfile(*i), DEPARTED_DEAD);
1844 	}
1845 	FOR_EACH(INT16 const, i, l.ubLeftCharactersList)
1846 	{
1847 		if (*i == -1 || slot-- != 0) continue;
1848 		return PastMercInfo(&GetProfile(*i), DEPARTED_FIRED);
1849 	}
1850 	FOR_EACH(INT16 const, i, l.ubOtherCharactersList)
1851 	{
1852 		if (*i == -1 || slot-- != 0) continue;
1853 		MERCPROFILESTRUCT& p     = GetProfile(*i);
1854 		MercProfile profile      = MercProfile(p);
1855 		INT const          state =
1856 			p.ubMiscFlags2 & PROFILE_MISC_FLAG2_MARRIED_TO_HICKS ? DEPARTED_MARRIED          :
1857 			profile.isAIMMerc()                                  ? DEPARTED_CONTRACT_EXPIRED :
1858 			DEPARTED_QUIT;
1859 		return PastMercInfo(&p, state);
1860 	}
1861 	return PastMercInfo(0, -1);
1862 }
1863 
1864 
DisplayPortraitOfPastMerc(const INT32 iId,const INT32 iCounter,const BOOLEAN fDead)1865 static void DisplayPortraitOfPastMerc(const INT32 iId, const INT32 iCounter, const BOOLEAN fDead)
1866 try
1867 {
1868 	AutoSGPVObject guiFACE(LoadSmallPortrait(GetProfile(iId)));
1869 
1870 	if (fDead)
1871 	{
1872 		guiFACE->pShades[0] = Create16BPPPaletteShaded(guiFACE->Palette(), DEAD_MERC_COLOR_RED, DEAD_MERC_COLOR_GREEN, DEAD_MERC_COLOR_BLUE, TRUE);
1873 		//set the red pallete to the face
1874 		guiFACE->CurrentShade(0);
1875 	}
1876 
1877 	const INT32 x = SMALL_PORTRAIT_START_X + iCounter % PERSONNEL_PORTRAIT_NUMBER_WIDTH * SMALL_PORT_WIDTH;
1878 	const INT32 y = SMALL_PORTRAIT_START_Y + iCounter / PERSONNEL_PORTRAIT_NUMBER_WIDTH * SMALL_PORT_HEIGHT;
1879 	BltVideoObject(FRAME_BUFFER, guiFACE.get(), 0, x, y);
1880 }
1881 catch (...) { /* XXX ignore */ }
1882 
1883 
DisplayDepartedCharStats(MERCPROFILESTRUCT const & p,INT32 const iState)1884 static void DisplayDepartedCharStats(MERCPROFILESTRUCT const& p, INT32 const iState)
1885 {
1886 	ST::string sString;
1887 	INT16 sX;
1888 	INT16 sY;
1889 
1890 	SetFontAttributes(FONT10ARIAL, PERS_TEXT_FONT_COLOR);
1891 
1892 	INT8 const life = p.bLife;
1893 	INT8 const cur  = (iState == DEPARTED_DEAD ? 0 : life);
1894 	sString = ST::format("{}/{}", cur, life);
1895 	MPrint(pers_stat_x, STD_SCREEN_Y + pers_stat_y[0], ST::format("{}:", str_stat_health));
1896 	FindFontRightCoordinates(pers_stat_x, 0, TEXT_BOX_WIDTH - 20, 0, sString, PERS_FONT, &sX, &sY);
1897 	MPrint(sX, STD_SCREEN_Y + pers_stat_y[0], sString);
1898 
1899 	PrintStat(p.bAgility,     STD_SCREEN_Y + pers_stat_y[ 1], str_stat_agility);
1900 	PrintStat(p.bDexterity,   STD_SCREEN_Y + pers_stat_y[ 2], str_stat_dexterity);
1901 	PrintStat(p.bStrength,    STD_SCREEN_Y + pers_stat_y[ 3], str_stat_strength);
1902 	PrintStat(p.bLeadership,  STD_SCREEN_Y + pers_stat_y[ 4], str_stat_leadership);
1903 	PrintStat(p.bWisdom,      STD_SCREEN_Y + pers_stat_y[ 5], str_stat_wisdom);
1904 	PrintStat(p.bExpLevel,    STD_SCREEN_Y + pers_stat_y[ 6], str_stat_exp_level);
1905 	PrintStat(p.bMarksmanship,STD_SCREEN_Y + pers_stat_y[ 7], str_stat_marksmanship);
1906 	PrintStat(p.bMechanical,  STD_SCREEN_Y + pers_stat_y[ 8], str_stat_mechanical);
1907 	PrintStat(p.bExplosive,   STD_SCREEN_Y + pers_stat_y[ 9], str_stat_explosive);
1908 	PrintStat(p.bMedical,     STD_SCREEN_Y + pers_stat_y[10], str_stat_medical);
1909 	PrintStat(p.usKills,      STD_SCREEN_Y + pers_stat_y[21], pPersonnelScreenStrings[PRSNL_TXT_KILLS]);
1910 	PrintStat(p.usAssists,    STD_SCREEN_Y + pers_stat_y[22], pPersonnelScreenStrings[PRSNL_TXT_ASSISTS]);
1911 
1912 	// Shots/hits
1913 	MPrint(pers_stat_x, STD_SCREEN_Y + pers_stat_y[23], pPersonnelScreenStrings[PRSNL_TXT_HIT_PERCENTAGE]);
1914 	// check we have shot at least once
1915 	UINT32 const fired = p.usShotsFired;
1916 	UINT32 const hits  = (fired > 0 ? 100 * p.usShotsHit / fired : 0);
1917 	sString = ST::format("{} %", hits);
1918 	FindFontRightCoordinates(pers_stat_x, 0, TEXT_BOX_WIDTH - 20, 0, sString, PERS_FONT, &sX, &sY);
1919 	MPrint(sX, STD_SCREEN_Y + pers_stat_y[23], sString);
1920 
1921 	PrintStat(p.usBattlesFought, STD_SCREEN_Y + pers_stat_y[24], pPersonnelScreenStrings[PRSNL_TXT_BATTLES]);
1922 	PrintStat(p.usTimesWounded,  STD_SCREEN_Y + pers_stat_y[25], pPersonnelScreenStrings[PRSNL_TXT_TIMES_WOUNDED]);
1923 }
1924 
1925 
EnableDisableDeparturesButtons(void)1926 static void EnableDisableDeparturesButtons(void)
1927 {
1928 	// will enable or disable departures buttons based on upperleft picutre index value
1929 	if (fCurrentTeamMode || fNewMailFlag)
1930 	{
1931 		return;
1932 	}
1933 
1934 	// disable both buttons
1935 	DisableButton(g_personnel.depart_up);
1936 	DisableButton(g_personnel.depart_dn);
1937 
1938 
1939 	if (giCurrentUpperLeftPortraitNumber != 0)
1940 	{
1941 		// enable up button
1942 		EnableButton(g_personnel.depart_up);
1943 	}
1944 	if (GetNumberOfPastMercsOnPlayersTeam() - giCurrentUpperLeftPortraitNumber > PERSONNEL_PORTRAIT_NUMBER)
1945 	{
1946 		// enable down button
1947 		EnableButton(g_personnel.depart_dn);
1948 	}
1949 }
1950 
1951 
DisplayDepartedCharName(MERCPROFILESTRUCT const & p,const INT32 iState)1952 static void DisplayDepartedCharName(MERCPROFILESTRUCT const& p, const INT32 iState)
1953 {
1954 	// get merc's nickName, assignment, and sector location info
1955 	INT16 sX, sY;
1956 
1957 	SetFontAttributes(CHAR_NAME_FONT, PERS_TEXT_FONT_COLOR);
1958 
1959 	ST::string name = p.zNickname;
1960 	FindFontCenterCoordinates(CHAR_NAME_LOC_X, 0, CHAR_NAME_LOC_WIDTH, 0, name, CHAR_NAME_FONT, &sX, &sY);
1961 	MPrint(sX, CHAR_NAME_Y, name);
1962 
1963 	ST::string state_txt = pPersonnelDepartedStateStrings[iState];
1964 	FindFontCenterCoordinates(CHAR_NAME_LOC_X, 0, CHAR_NAME_LOC_WIDTH, 0, state_txt, CHAR_NAME_FONT, &sX, &sY);
1965 	MPrint(sX, CHAR_LOC_Y, state_txt);
1966 }
1967 
1968 
DisplayPersonnelTextOnTitleBar(void)1969 static void DisplayPersonnelTextOnTitleBar(void)
1970 {
1971 	SetFontAttributes(FONT14ARIAL, FONT_WHITE);
1972 	MPrint(PERS_TITLE_X, PERS_TITLE_Y, pPersTitleText);
1973 }
1974 
1975 
1976 // display box around currently selected merc
DisplayHighLightBox(INT32 const sel_id)1977 static void DisplayHighLightBox(INT32 const sel_id)
1978 {
1979 	// will display highlight box around selected merc
1980 	const INT32 x = SMALL_PORTRAIT_START_X + sel_id % PERSONNEL_PORTRAIT_NUMBER_WIDTH * SMALL_PORT_WIDTH  - 2;
1981 	const INT32 y = SMALL_PORTRAIT_START_Y + sel_id / PERSONNEL_PORTRAIT_NUMBER_WIDTH * SMALL_PORT_HEIGHT - 3;
1982 	BltVideoObjectOnce(FRAME_BUFFER, LAPTOPDIR "/picborde.sti", 0, x, y);
1983 }
1984 
1985 
1986 // add to dead list
AddCharacterToDeadList(SOLDIERTYPE * pSoldier)1987 void AddCharacterToDeadList(SOLDIERTYPE *pSoldier)
1988 {
1989 	for (INT32 i = 0; i < 256; i++)
1990 	{
1991 		if (LaptopSaveInfo.ubDeadCharactersList[i] == -1)
1992 		{
1993 			// valid slot, merc not found yet, inset here
1994 			LaptopSaveInfo.ubDeadCharactersList[i] = pSoldier->ubProfile;
1995 			return;
1996 		}
1997 
1998 		// are they already in the list?
1999 		if (LaptopSaveInfo.ubDeadCharactersList[i] == pSoldier->ubProfile)
2000 		{
2001 			return;
2002 		}
2003 	}
2004 }
2005 
2006 
AddCharacterToFiredList(SOLDIERTYPE * pSoldier)2007 void AddCharacterToFiredList(SOLDIERTYPE *pSoldier)
2008 {
2009 	for (INT32 i = 0; i < 256; i++)
2010 	{
2011 		if (LaptopSaveInfo.ubLeftCharactersList[i] == -1)
2012 		{
2013 			// valid slot, merc not found yet, inset here
2014 			LaptopSaveInfo.ubLeftCharactersList[i] = pSoldier->ubProfile;
2015 			return;
2016 		}
2017 
2018 		// are they already in the list?
2019 		if (LaptopSaveInfo.ubLeftCharactersList[i] == pSoldier->ubProfile)
2020 		{
2021 			return;
2022 		}
2023 	}
2024 }
2025 
2026 
AddCharacterToOtherList(SOLDIERTYPE * pSoldier)2027 void AddCharacterToOtherList(SOLDIERTYPE *pSoldier)
2028 {
2029 	for (INT32 i = 0; i < 256; i++)
2030 	{
2031 		if (LaptopSaveInfo.ubOtherCharactersList[i] == -1)
2032 		{
2033 			// valid slot, merc not found yet, inset here
2034 			LaptopSaveInfo.ubOtherCharactersList[i] = pSoldier->ubProfile;
2035 			return;
2036 		}
2037 
2038 		// are they already in the list?
2039 		if (LaptopSaveInfo.ubOtherCharactersList[i] == pSoldier->ubProfile)
2040 		{
2041 			return;
2042 		}
2043 	}
2044 }
2045 
2046 
2047 // If you have hired a merc before, then the they left for whatever reason, and now you are hiring them again,
2048 // we must get rid of them from the departed section in the personnel screen.  (wouldnt make sense for them
2049 //to be on your team list, and departed list)
RemoveNewlyHiredMercFromPersonnelDepartedList(UINT8 ubProfile)2050 BOOLEAN RemoveNewlyHiredMercFromPersonnelDepartedList(UINT8 ubProfile)
2051 {
2052 	for (INT32 i = 0; i < 256; i++)
2053 	{
2054 		// are they already in the Dead list?
2055 		if (LaptopSaveInfo.ubDeadCharactersList[i] == ubProfile)
2056 		{
2057 			//Reset the fact that they were once hired
2058 			LaptopSaveInfo.ubDeadCharactersList[i] = -1;
2059 			return TRUE;
2060 		}
2061 
2062 		// are they already in the other list?
2063 		if (LaptopSaveInfo.ubLeftCharactersList[i] == ubProfile)
2064 		{
2065 			//Reset the fact that they were once hired
2066 			LaptopSaveInfo.ubLeftCharactersList[i] = -1;
2067 			return TRUE;
2068 		}
2069 
2070 		// are they already in the list?
2071 		if (LaptopSaveInfo.ubOtherCharactersList[i] == ubProfile)
2072 		{
2073 			//Reset the fact that they were once hired
2074 			LaptopSaveInfo.ubOtherCharactersList[i] = -1;
2075 			return TRUE;
2076 		}
2077 	}
2078 
2079 	return FALSE;
2080 }
2081 
2082 
2083 // Select the first displayed merc, if there is any
SelectFirstDisplayedMerc(void)2084 static void SelectFirstDisplayedMerc(void)
2085 {
2086 	// set current soldier
2087 	if (fCurrentTeamMode)
2088 	{
2089 		CFOR_EACH_PERSONNEL(s)
2090 		{
2091 			iCurrentPersonSelectedId = 0;
2092 			return;
2093 		}
2094 		iCurrentPersonSelectedId = -1;
2095 	}
2096 	else
2097 	{
2098 		iCurrentPersonSelectedId = GetNumberOfPastMercsOnPlayersTeam() > 0 ? 0 : -1;
2099 	}
2100 }
2101 
2102 
GetSoldierOfCurrentSlot(void)2103 static SOLDIERTYPE const& GetSoldierOfCurrentSlot(void)
2104 {
2105 	Assert(fCurrentTeamMode);
2106 
2107 	INT32 slot = iCurrentPersonSelectedId;
2108 	CFOR_EACH_PERSONNEL(s)
2109 	{
2110 		if (slot-- == 0) return *s;
2111 	}
2112 
2113 	throw std::logic_error("nobody selected");
2114 }
2115 
2116 
RenderAtmPanel(void)2117 static void RenderAtmPanel(void)
2118 try
2119 {
2120 	// just show basic panel
2121 	// bounding
2122 	AutoSGPVObject uiBox(AddVideoObjectFromFile(LAPTOPDIR "/atmbuttons.sti"));
2123 	BltVideoObject(FRAME_BUFFER, uiBox.get(), 0, ATM_UL_X,     ATM_UL_Y);
2124 	BltVideoObject(FRAME_BUFFER, uiBox.get(), 1, ATM_UL_X + 1, ATM_UL_Y + 18);
2125 }
2126 catch (...) { /* XXX ignore */ }
2127 
2128 
MakeButton(UINT idx,INT16 y,GUI_CALLBACK click,const ST::string & text)2129 static void MakeButton(UINT idx, INT16 y, GUI_CALLBACK click, const ST::string& text)
2130 {
2131 	BUTTON_PICS* const img = LoadButtonImage(LAPTOPDIR "/atmbuttons.sti", 2, 3);
2132 	giPersonnelATMStartButtonImage[idx] = img;
2133 	GUIButtonRef const btn = QuickCreateButtonNoMove(img, STD_SCREEN_X + 519, STD_SCREEN_Y + y, MSYS_PRIORITY_HIGHEST - 1, click);
2134 	giPersonnelATMStartButton[idx] = btn;
2135 	btn->SpecifyGeneralTextAttributes(text, PERS_FONT, FONT_BLACK, FONT_BLACK);
2136 	btn->SetCursor(CURSOR_LAPTOP_SCREEN);
2137 }
2138 
2139 
2140 static void EmployementInfoButtonCallback(GUI_BUTTON* btn, INT32 reason);
2141 static void PersonnelINVStartButtonCallback(GUI_BUTTON* btn, INT32 reason);
2142 static void PersonnelStatStartButtonCallback(GUI_BUTTON* btn, INT32 reason);
2143 
2144 
CreateDestroyStartATMButton(const BOOLEAN create)2145 static void CreateDestroyStartATMButton(const BOOLEAN create)
2146 {
2147 	static BOOLEAN fCreated = FALSE;
2148 	// create/destroy atm start button as needed
2149 
2150 	if (!fCreated && create)
2151 	{
2152 		// not created, must create
2153 		MakeButton(PERSONNEL_STAT_BTN,        80, PersonnelStatStartButtonCallback, gsAtmStartButtonText[0]);
2154 		MakeButton(PERSONNEL_EMPLOYMENT_BTN, 110, EmployementInfoButtonCallback,    gsAtmStartButtonText[2]);
2155 		MakeButton(PERSONNEL_INV_BTN,        140, PersonnelINVStartButtonCallback,  gsAtmStartButtonText[1]);
2156 
2157 		fCreated = TRUE;
2158 	}
2159 	else if (fCreated && !create)
2160 	{
2161 		// stop showing
2162 		RemoveButton(giPersonnelATMStartButton[PERSONNEL_STAT_BTN]);
2163 		UnloadButtonImage(giPersonnelATMStartButtonImage[PERSONNEL_STAT_BTN]);
2164 		RemoveButton(giPersonnelATMStartButton[PERSONNEL_EMPLOYMENT_BTN]);
2165 		UnloadButtonImage(giPersonnelATMStartButtonImage[PERSONNEL_EMPLOYMENT_BTN]);
2166 		RemoveButton(giPersonnelATMStartButton[PERSONNEL_INV_BTN]);
2167 		UnloadButtonImage(giPersonnelATMStartButtonImage[PERSONNEL_INV_BTN]);
2168 
2169 		fCreated = FALSE;
2170 	}
2171 }
2172 
2173 
FindPositionOfPersInvSlider(void)2174 static void FindPositionOfPersInvSlider(void)
2175 {
2176 	const INT32 item_count   = GetNumberOfInventoryItemsOnCurrentMerc();
2177 	const INT32 scroll_count = item_count - NUMBER_OF_INVENTORY_PERSONNEL;
2178 	guiSliderPosition = (Y_SIZE_OF_PERSONNEL_SCROLL_REGION - SIZE_OF_PERSONNEL_CURSOR) * uiCurrentInventoryIndex / scroll_count;
2179 }
2180 
2181 
HandleSliderBarClickCallback(MOUSE_REGION * pRegion,INT32 iReason)2182 static void HandleSliderBarClickCallback(MOUSE_REGION* pRegion, INT32 iReason)
2183 {
2184 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_DWN || iReason & MSYS_CALLBACK_REASON_LBUTTON_REPEAT)
2185 	{
2186 		const INT32 item_count = GetNumberOfInventoryItemsOnCurrentMerc();
2187 		if (item_count <= NUMBER_OF_INVENTORY_PERSONNEL) return;
2188 
2189 		const INT32 scroll_count = item_count - NUMBER_OF_INVENTORY_PERSONNEL;
2190 
2191 		// get the actual item position
2192 		const INT16 new_item_idx = scroll_count * pRegion->RelativeYPos / (Y_SIZE_OF_PERSONNEL_SCROLL_REGION - SIZE_OF_PERSONNEL_CURSOR);
2193 
2194 		if (uiCurrentInventoryIndex != new_item_idx)
2195 		{
2196 			// get slider position
2197 			guiSliderPosition = (Y_SIZE_OF_PERSONNEL_SCROLL_REGION - SIZE_OF_PERSONNEL_CURSOR) * new_item_idx / scroll_count;
2198 
2199 			// set current inventory value
2200 			uiCurrentInventoryIndex = new_item_idx;
2201 
2202 			// force update
2203 			fReDrawScreenFlag = TRUE;
2204 		}
2205 	}
2206 	else if (iReason & MSYS_CALLBACK_REASON_WHEEL_UP)
2207 	{
2208 		InventoryUp();
2209 	}
2210 	else if (iReason & MSYS_CALLBACK_REASON_WHEEL_DOWN)
2211 	{
2212 		InventoryDown();
2213 	}
2214 }
2215 
2216 
RenderSliderBarForPersonnelInventory(void)2217 static void RenderSliderBarForPersonnelInventory(void)
2218 {
2219 	// render slider bar for personnel
2220 	BltVideoObject(FRAME_BUFFER, guiPersonnelInventory, 5, X_OF_PERSONNEL_SCROLL_REGION, guiSliderPosition + Y_OF_PERSONNEL_SCROLL_REGION);
2221 }
2222 
2223 
PersonnelINVStartButtonCallback(GUI_BUTTON * btn,INT32 reason)2224 static void PersonnelINVStartButtonCallback(GUI_BUTTON *btn, INT32 reason)
2225 {
2226 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
2227 	{
2228 		fReDrawScreenFlag = TRUE;
2229 		btn->uiFlags |= BUTTON_CLICKED_ON;
2230 		giPersonnelATMStartButton[PERSONNEL_STAT_BTN]->uiFlags       &= ~BUTTON_CLICKED_ON;
2231 		giPersonnelATMStartButton[PERSONNEL_EMPLOYMENT_BTN]->uiFlags &= ~BUTTON_CLICKED_ON;
2232 		gubPersonnelInfoState = PRSNL_INV;
2233 	}
2234 }
2235 
2236 
PersonnelStatStartButtonCallback(GUI_BUTTON * btn,INT32 reason)2237 static void PersonnelStatStartButtonCallback(GUI_BUTTON *btn, INT32 reason)
2238 {
2239 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
2240 	{
2241 		fReDrawScreenFlag = TRUE;
2242 		btn->uiFlags |= BUTTON_CLICKED_ON;
2243 		giPersonnelATMStartButton[PERSONNEL_EMPLOYMENT_BTN]->uiFlags &= ~BUTTON_CLICKED_ON;
2244 		giPersonnelATMStartButton[PERSONNEL_INV_BTN]->uiFlags        &= ~BUTTON_CLICKED_ON;
2245 		gubPersonnelInfoState = PRSNL_STATS;
2246 	}
2247 }
2248 
2249 
EmployementInfoButtonCallback(GUI_BUTTON * btn,INT32 reason)2250 static void EmployementInfoButtonCallback(GUI_BUTTON *btn, INT32 reason)
2251 {
2252 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_DWN)
2253 	{
2254 		fReDrawScreenFlag = TRUE;
2255 		btn->uiFlags |= BUTTON_CLICKED_ON;
2256 		giPersonnelATMStartButton[PERSONNEL_INV_BTN]->uiFlags  &= ~BUTTON_CLICKED_ON;
2257 		giPersonnelATMStartButton[PERSONNEL_STAT_BTN]->uiFlags &= ~BUTTON_CLICKED_ON;
2258 		gubPersonnelInfoState = PRSNL_EMPLOYMENT;
2259 	}
2260 }
2261 
2262 
2263 // get the total amt of money on this guy
GetFundsOnMerc(SOLDIERTYPE const & s)2264 static INT32 GetFundsOnMerc(SOLDIERTYPE const& s)
2265 {
2266 	INT32 iCurrentAmount = 0;
2267 	// run through mercs pockets, if any money in them, add to total
2268 
2269 	// run through grunts pockets and count all the spare change
2270 	CFOR_EACH_SOLDIER_INV_SLOT(i, s)
2271 	{
2272 		if (GCM->getItem(i->usItem)->getItemClass() == IC_MONEY)
2273 		{
2274 			iCurrentAmount += i->uiMoneyAmount;
2275 		}
2276 	}
2277 
2278 	return iCurrentAmount;
2279 }
2280 
2281 
2282 // check if current guy can have atm
UpDateStateOfStartButton(void)2283 static void UpDateStateOfStartButton(void)
2284 {
2285 	if (gubPersonnelInfoState == PRSNL_INV)
2286 	{
2287 		giPersonnelATMStartButton[PERSONNEL_INV_BTN       ]->uiFlags |=  BUTTON_CLICKED_ON;
2288 		giPersonnelATMStartButton[PERSONNEL_STAT_BTN      ]->uiFlags &= ~BUTTON_CLICKED_ON;
2289 		giPersonnelATMStartButton[PERSONNEL_EMPLOYMENT_BTN]->uiFlags &= ~BUTTON_CLICKED_ON;
2290 	}
2291 	else if (gubPersonnelInfoState == PRSNL_STATS)
2292 	{
2293 		giPersonnelATMStartButton[PERSONNEL_INV_BTN       ]->uiFlags &= ~BUTTON_CLICKED_ON;
2294 		giPersonnelATMStartButton[PERSONNEL_STAT_BTN      ]->uiFlags |=  BUTTON_CLICKED_ON;
2295 		giPersonnelATMStartButton[PERSONNEL_EMPLOYMENT_BTN]->uiFlags &= ~BUTTON_CLICKED_ON;
2296 	}
2297 	else
2298 	{
2299 		giPersonnelATMStartButton[PERSONNEL_STAT_BTN      ]->uiFlags &= ~BUTTON_CLICKED_ON;
2300 		giPersonnelATMStartButton[PERSONNEL_INV_BTN       ]->uiFlags &= ~BUTTON_CLICKED_ON;
2301 		giPersonnelATMStartButton[PERSONNEL_EMPLOYMENT_BTN]->uiFlags |= BUTTON_CLICKED_ON;
2302 	}
2303 
2304 	// if in current mercs and the currently selected guy is valid, enable button, else disable it
2305 	if (fCurrentTeamMode)
2306 	{
2307 		// is the current guy valid
2308 		if (GetNumberOfMercsDeadOrAliveOnPlayersTeam() > 0)
2309 		{
2310 			EnableButton(giPersonnelATMStartButton[PERSONNEL_STAT_BTN]);
2311 			EnableButton(giPersonnelATMStartButton[PERSONNEL_INV_BTN]);
2312 			EnableButton(giPersonnelATMStartButton[PERSONNEL_EMPLOYMENT_BTN]);
2313 
2314 			if (GetSoldierOfCurrentSlot().bAssignment == ASSIGNMENT_POW)
2315 			{
2316 				DisableButton(giPersonnelATMStartButton[PERSONNEL_INV_BTN]);
2317 
2318 				if (gubPersonnelInfoState == PRSNL_INV)
2319 				{
2320 					gubPersonnelInfoState = PRSNL_STATS;
2321 					fPausedReDrawScreenFlag = TRUE;
2322 				}
2323 			}
2324 		}
2325 		else
2326 		{
2327 			// not valid, disable
2328 			DisableButton(giPersonnelATMStartButton[PERSONNEL_STAT_BTN]);
2329 			DisableButton(giPersonnelATMStartButton[PERSONNEL_INV_BTN]);
2330 			DisableButton(giPersonnelATMStartButton[PERSONNEL_EMPLOYMENT_BTN]);
2331 		}
2332 	}
2333 	else
2334 	{
2335 		// disable button
2336 		EnableButton(giPersonnelATMStartButton[PERSONNEL_STAT_BTN]);
2337 		DisableButton(giPersonnelATMStartButton[PERSONNEL_INV_BTN]);
2338 		DisableButton(giPersonnelATMStartButton[PERSONNEL_EMPLOYMENT_BTN]);
2339 	}
2340 }
2341 
2342 
DisplayAmountOnChar(SOLDIERTYPE const & s)2343 static void DisplayAmountOnChar(SOLDIERTYPE const& s)
2344 {
2345 	// will display the amount that the merc is carrying on him or herself
2346 	ST::string sString = SPrintMoney(GetFundsOnMerc(s));
2347 
2348 	SetFontAttributes(ATM_FONT, FONT_WHITE);
2349 
2350 	INT16 sX;
2351 	INT16 sY;
2352 	FindFontRightCoordinates(ATM_DISPLAY_X, ATM_DISPLAY_Y, ATM_DISPLAY_WIDTH, ATM_DISPLAY_HEIGHT, sString, ATM_FONT, &sX, &sY);
2353 	MPrint(sX, sY, sString);
2354 }
2355 
2356 
HandlePersonnelKeyboard(void)2357 static void HandlePersonnelKeyboard(void)
2358 {
2359 	InputAtom InputEvent;
2360 	while (DequeueEvent(&InputEvent))
2361 	{
2362 		HandleKeyBoardShortCutsForLapTop(InputEvent.usEvent, InputEvent.usParam, InputEvent.usKeyState);
2363 	}
2364 }
2365 
2366 
DisplayEmploymentinformation(SOLDIERTYPE const & s)2367 static void DisplayEmploymentinformation(SOLDIERTYPE const& s)
2368 {
2369 	ST::string sString;
2370 	INT16 sX, sY;
2371 
2372 	MERCPROFILESTRUCT const& p = GetProfile(s.ubProfile);
2373 
2374 	// display the stats for a char
2375 	for (INT32 i = 0; i < MAX_STATS; i++)
2376 	{
2377 		switch (i)
2378 		{
2379 			case 0: //Remaining Contract:
2380 			{
2381 				if (s.ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC || s.ubProfile == SLAY)
2382 				{
2383 					const UINT32 uiMinutesInDay = 24 * 60;
2384 					INT32 iTimeLeftOnContract = s.iEndofContractTime - GetWorldTotalMin();
2385 					if (iTimeLeftOnContract < 0) iTimeLeftOnContract = 0;
2386 
2387 					//if the merc is in transit
2388 					if (s.bAssignment == IN_TRANSIT)
2389 					{
2390 						//and if the ttime left on the cotract is greater then the contract time
2391 						if (iTimeLeftOnContract > (INT32)(s.iTotalContractLength * uiMinutesInDay))
2392 						{
2393 							iTimeLeftOnContract = (s.iTotalContractLength * uiMinutesInDay);
2394 						}
2395 					}
2396 					// if there is going to be a both days and hours left on the contract
2397 					const INT days  = iTimeLeftOnContract / uiMinutesInDay;
2398 					const INT hours = iTimeLeftOnContract % uiMinutesInDay / 60;
2399 					if (days > 0)
2400 					{
2401 						sString = ST::format("{}{} {}{} / {}{}", days, gpStrategicString[STR_PB_DAYS_ABBREVIATION], hours, gpStrategicString[STR_PB_HOURS_ABBREVIATION], s.iTotalContractLength, gpStrategicString[STR_PB_DAYS_ABBREVIATION]);
2402 					}
2403 					else //else there is under a day left
2404 					{
2405 						//DEF: removed 2/7/99
2406 						sString = ST::format("{}{} / {}{}", hours, gpStrategicString[STR_PB_HOURS_ABBREVIATION], s.iTotalContractLength, gpStrategicString[STR_PB_DAYS_ABBREVIATION]);
2407 					}
2408 				}
2409 				else if (s.ubWhatKindOfMercAmI == MERC_TYPE__MERC)
2410 				{
2411 					sString = gpStrategicString[STR_PB_NOTAPPLICABLE_ABBREVIATION];
2412 				}
2413 				else
2414 				{
2415 					sString = gpStrategicString[STR_PB_NOTAPPLICABLE_ABBREVIATION];
2416 				}
2417 
2418 				MPrint(pers_stat_x, STD_SCREEN_Y + pers_stat_y[i], pPersonnelScreenStrings[PRSNL_TXT_CURRENT_CONTRACT]);
2419 				FindFontRightCoordinates(pers_stat_data_x, 0, TEXT_BOX_WIDTH - 20, 0, sString, PERS_FONT, &sX, &sY);
2420 				MPrint(sX, STD_SCREEN_Y + pers_stat_y[i], sString);
2421 			}
2422 			break;
2423 
2424 			case 1: // total contract time served
2425 				MPrint(pers_stat_x, STD_SCREEN_Y + pers_stat_y[i], pPersonnelScreenStrings[PRSNL_TXT_TOTAL_SERVICE]);
2426 				//./DEF 2/4/99: total service days used to be calced as 'days -1'
2427 				sString = ST::format("{} {}", p.usTotalDaysServed, gpStrategicString[STR_PB_DAYS_ABBREVIATION]);
2428 				FindFontRightCoordinates(pers_stat_data_x, 0, TEXT_BOX_WIDTH - 20, 0, sString, PERS_FONT, &sX, &sY);
2429 				MPrint(sX, STD_SCREEN_Y + pers_stat_y[i], sString);
2430 				break;
2431 
2432 			case 3: // cost (PRSNL_TXT_TOTAL_COST)
2433 			{
2434 				sString = SPrintMoney(p.uiTotalCostToDate);
2435 				FindFontRightCoordinates(pers_stat_data_x, 0, TEXT_BOX_WIDTH - 20, 0, sString, PERS_FONT, &sX, &sY);
2436 				MPrint(pers_stat_x, STD_SCREEN_Y + pers_stat_y[i], pPersonnelScreenStrings[PRSNL_TXT_TOTAL_COST]);
2437 
2438 				// print contract cost
2439 				MPrint(sX, STD_SCREEN_Y + pers_stat_y[i], sString);
2440 
2441 				INT32 salary;
2442 				switch (s.ubWhatKindOfMercAmI)
2443 				{
2444 					case MERC_TYPE__AIM_MERC:
2445 						switch (s.bTypeOfLastContract)
2446 						{
2447 							case CONTRACT_EXTEND_2_WEEK: salary = p.uiBiWeeklySalary / 14; break;
2448 							case CONTRACT_EXTEND_1_WEEK: salary = p.uiWeeklySalary   /  7; break;
2449 							default:                     salary = p.sSalary;               break;
2450 						}
2451 						break;
2452 
2453 					default: salary = p.sSalary; break;
2454 				}
2455 
2456 				ST::string sStringA = SPrintMoney(salary);
2457 				FindFontRightCoordinates(pers_stat_data_x, 0, TEXT_BOX_WIDTH - 20, 0, sStringA, PERS_FONT,  &sX, &sY);
2458 
2459 				i++;
2460 
2461 				// now print daily rate
2462 				MPrint(sX,          STD_SCREEN_Y + pers_stat_y[i + 1], sStringA);
2463 				MPrint(pers_stat_x, STD_SCREEN_Y + pers_stat_y[i + 1], pPersonnelScreenStrings[PRSNL_TXT_DAILY_COST]);
2464 				break;
2465 			}
2466 
2467 			case 5: // medical deposit
2468 				//if its a merc merc, display the salary oweing
2469 				if (s.ubWhatKindOfMercAmI == MERC_TYPE__MERC)
2470 				{
2471 					MPrint(pers_stat_x, STD_SCREEN_Y + pers_stat_y[i - 1], pPersonnelScreenStrings[PRSNL_TXT_UNPAID_AMOUNT]);
2472 					sString = SPrintMoney(p.sSalary * p.iMercMercContractLength);
2473 				}
2474 				else
2475 				{
2476 					MPrint(pers_stat_x, STD_SCREEN_Y + pers_stat_y[i - 1], pPersonnelScreenStrings[PRSNL_TXT_MED_DEPOSIT]);
2477 					sString = SPrintMoney(p.sMedicalDepositAmount);
2478 				}
2479 				FindFontRightCoordinates(pers_stat_data_x, 0, TEXT_BOX_WIDTH - 20, 0, sString, PERS_FONT, &sX, &sY);
2480 				MPrint(sX, STD_SCREEN_Y + pers_stat_y[i - 1], sString);
2481 				break;
2482 		}
2483 	}
2484 }
2485