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