1 #include "Directories.h"
2 #include "HImage.h"
3 #include "Laptop.h"
4 #include "Local.h"
5 #include "Mercs.h"
6 #include "Timer_Control.h"
7 #include "VObject.h"
8 #include "WordWrap.h"
9 #include "Cursors.h"
10 #include "Overhead.h"
11 #include "Soldier_Add.h"
12 #include "EMail.h"
13 #include "Game_Clock.h"
14 #include "Faces.h"
15 #include "Dialogue_Control.h"
16 #include "MercTextBox.h"
17 #include "Merc_Hiring.h"
18 #include "Random.h"
19 #include "LaptopSave.h"
20 #include "GameSettings.h"
21 #include "Text.h"
22 #include "Speck_Quotes.h"
23 #include "Mercs_Account.h"
24 #include "Soldier_Profile.h"
25 #include "Game_Event_Hook.h"
26 #include "Quests.h"
27 #include "Button_System.h"
28 #include "Video.h"
29 #include "VSurface.h"
30 #include "Debug.h"
31 #include "Font_Control.h"
32 #include "GameInstance.h"
33 #include "ContentManager.h"
34 #include "MERCListingModel.h"
35 #include <climits>
36 #include <string_theory/format>
37 #include <string_theory/string>
38
39
40 #define MERC_TEXT_FONT FONT12ARIAL
41 #define MERC_TEXT_COLOR FONT_MCOLOR_WHITE
42
43 #define MERC_VIDEO_TITLE_FONT FONT10ARIAL
44 #define MERC_VIDEO_TITLE_COLOR FONT_MCOLOR_LTYELLOW
45
46 #define MERC_BACKGROUND_WIDTH 125
47 #define MERC_BACKGROUND_HEIGHT 100
48
49 #define MERC_TITLE_X LAPTOP_SCREEN_UL_X + 135
50 #define MERC_TITLE_Y LAPTOP_SCREEN_WEB_UL_Y + 20
51
52 #define MERC_PORTRAIT_X LAPTOP_SCREEN_UL_X + 198
53 #define MERC_PORTRAIT_Y LAPTOP_SCREEN_WEB_UL_Y + 96
54 #define MERC_PORTRAIT_TEXT_X MERC_PORTRAIT_X
55 #define MERC_PORTRAIT_TEXT_Y MERC_PORTRAIT_Y + 109
56 #define MERC_PORTRAIT_TEXT_WIDTH 115
57
58 #define MERC_ACCOUNT_BOX_X LAPTOP_SCREEN_UL_X + 138
59 #define MERC_ACCOUNT_BOX_Y LAPTOP_SCREEN_WEB_UL_Y + 251
60
61 #define MERC_ACCOUNT_BOX_TEXT_X MERC_ACCOUNT_BOX_X
62 #define MERC_ACCOUNT_BOX_TEXT_Y MERC_ACCOUNT_BOX_Y + 20
63 #define MERC_ACCOUNT_BOX_TEXT_WIDTH 110
64
65 #define MERC_ACCOUNT_ARROW_X MERC_ACCOUNT_BOX_X + 125
66 #define MERC_ACCOUNT_ARROW_Y MERC_ACCOUNT_BOX_Y + 18
67
68 #define MERC_ACCOUNT_BUTTON_X MERC_ACCOUNT_BOX_X + 133
69 #define MERC_ACCOUNT_BUTTON_Y MERC_ACCOUNT_BOX_Y + 8
70
71 #define MERC_FILE_BOX_X MERC_ACCOUNT_BOX_X
72 #define MERC_FILE_BOX_Y LAPTOP_SCREEN_WEB_UL_Y + 321
73
74 #define MERC_FILE_BOX_TEXT_X MERC_FILE_BOX_X
75 #define MERC_FILE_BOX_TEXT_Y MERC_FILE_BOX_Y + 20
76 #define MERC_FILE_BOX_TEXT_WIDTH MERC_ACCOUNT_BOX_TEXT_WIDTH
77
78 #define MERC_FILE_ARROW_X MERC_FILE_BOX_X + 125
79 #define MERC_FILE_ARROW_Y MERC_FILE_BOX_Y + 18
80
81 #define MERC_FILE_BUTTON_X MERC_ACCOUNT_BUTTON_X
82 #define MERC_FILE_BUTTON_Y MERC_FILE_BOX_Y + 8
83
84
85
86 // Video Conference Defines
87 #define MERC_VIDEO_BACKGROUND_X MERC_PORTRAIT_X
88 #define MERC_VIDEO_BACKGROUND_Y MERC_PORTRAIT_Y
89 #define MERC_VIDEO_BACKGROUND_WIDTH 116
90 #define MERC_VIDEO_BACKGROUND_HEIGHT 108
91
92 #define MERC_VIDEO_FACE_X (UINT16)(MERC_VIDEO_BACKGROUND_X + 10)
93 #define MERC_VIDEO_FACE_Y (UINT16)(MERC_VIDEO_BACKGROUND_Y + 17)
94 #define MERC_VIDEO_FACE_WIDTH 96
95 #define MERC_VIDEO_FACE_HEIGHT 86
96 #define MERC_X_TO_CLOSE_VIDEO_X MERC_VIDEO_BACKGROUND_X + 104
97 #define MERC_X_TO_CLOSE_VIDEO_Y MERC_VIDEO_BACKGROUND_Y + 3
98 #define MERC_X_VIDEO_TITLE_X MERC_VIDEO_BACKGROUND_X + 5
99 #define MERC_X_VIDEO_TITLE_Y MERC_VIDEO_BACKGROUND_Y + 3
100
101 #define MERC_INTRO_TIME 1000
102 #define MERC_EXIT_TIME 500
103
104 #define MERC_TEXT_BOX_POS_Y (STD_SCREEN_Y + 255)
105
106 #define SPECK_IDLE_CHAT_DELAY 10000
107
108 #define MERC_NUMBER_OF_RANDOM_QUOTES 14
109
110 enum
111 {
112 MERC_ARRIVES_BUBBA,
113 MERC_ARRIVES_LARRY,
114 MERC_ARRIVES_NUMB,
115 MERC_ARRIVES_COUGAR,
116
117 NUM_MERC_ARRIVALS,
118 };
119
120
121 enum
122 {
123 MERC_DISTORTION_NO_DISTORTION,
124 MERC_DISTORTION_PIXELATE_UP,
125 MERC_DISTORTION_PIXELATE_DOWN,
126 MERC_DISRTORTION_DISTORT_IMAGE,
127 };
128
129
130 enum
131 {
132 MERC_SITE_NEVER_VISITED,
133 MERC_SITE_FIRST_VISIT,
134 MERC_SITE_SECOND_VISIT,
135 MERC_SITE_THIRD_OR_MORE_VISITS,
136 };
137
138
139 // Image Indetifiers
140
141 static SGPVObject* guiAccountBox;
142 static SGPVObject* guiArrow;
143 static SGPVObject* guiFilesBox;
144 static SGPVObject* guiMercSymbol;
145 static SGPVObject* guiSpecPortrait;
146 static SGPVObject* guiMercBackGround;
147 static SGPVSurface* guiMercVideoFaceBackground;
148 static SGPVObject* guiMercVideoPopupBackground;
149
150 UINT8 gubCurMercIndex;
151
152 static MercPopUpBox* g_merc_popup_box;
153
154 static ST::string gsSpeckDialogueTextPopUp;
155 static UINT16 gusSpeckDialogueX;
156 static UINT16 gusSpeckDialogueActualWidth;
157
158 static BOOLEAN gfInMercSite = FALSE; // this flag is set when inide of the merc site
159
160 //Merc Video Conferencing Mode
161 enum
162 {
163 MERC_VIDEO_NO_VIDEO_MODE,
164 MERC_VIDEO_INIT_VIDEO_MODE,
165 MERC_VIDEO_VIDEO_MODE,
166 MERC_VIDEO_EXIT_VIDEO_MODE,
167 };
168
169 static UINT8 gubCurrentMercVideoMode;
170 static BOOLEAN gfMercVideoIsBeingDisplayed;
171 static FACETYPE* g_video_speck_face;
172 UINT16 gusMercVideoSpeckSpeech;
173
174 static BOOLEAN gfDisplaySpeckTextBox = FALSE;
175
176 static BOOLEAN gfJustEnteredMercSite = FALSE;
177 UINT8 gubArrivedFromMercSubSite=MERC_CAME_FROM_OTHER_PAGE; //the merc is arriving from one of the merc sub pages
178 static BOOLEAN gfDoneIntroSpeech = TRUE;
179
180 static BOOLEAN gfMercSiteScreenIsReDrawn = FALSE;
181
182 BOOLEAN gfJustHiredAMercMerc=FALSE;
183
184 static BOOLEAN gfRedrawMercSite = FALSE;
185
186 static BOOLEAN gfFirstTimeIntoMERCSiteSinceEnteringLaptop = FALSE;
187
188 // This is not persisted but lasts for the entire game session, and never get reset
189 static std::map<UINT8, UINT32> gNumberOfTimesQuoteSaid = {};
190
191
192 //
193 // Buttons
194 //
195
196 // The Account Box button
197 static void BtnAccountBoxButtonCallback(GUI_BUTTON *btn, INT32 reason);
198 static BUTTON_PICS* guiAccountBoxButtonImage;
199 static GUIButtonRef guiAccountBoxButton;
200
201 //File Box
202 static void BtnFileBoxButtonCallback(GUI_BUTTON *btn, INT32 reason);
203 static GUIButtonRef guiFileBoxButton;
204
205 // The 'X' to close the video conf window button
206 static void BtnXToCloseMercVideoButtonCallback(GUI_BUTTON *btn, INT32 reason);
207 static GUIButtonRef guiXToCloseMercVideoButton;
208
209
210 //Mouse region for the subtitles region when the merc is talking
211 static MOUSE_REGION gMercSiteSubTitleMouseRegion;
212
213 static void MakeNewMercsAvailable(BOOLEAN fShouldNotifyPlayer);
214
GameInitMercs()215 void GameInitMercs()
216 {
217 LaptopSaveInfo.gubPlayersMercAccountStatus = MERC_NO_ACCOUNT;
218 gubCurMercIndex = 0;
219
220 MakeNewMercsAvailable(FALSE);
221
222 gubCurrentMercVideoMode = MERC_VIDEO_NO_VIDEO_MODE;
223 gfMercVideoIsBeingDisplayed = FALSE;
224
225 gusMercVideoSpeckSpeech = 0;
226 }
227
228
229 static BOOLEAN GetSpeckConditionalOpening(BOOLEAN fJustEnteredScreen);
230 static void HandleSpeckIdleConversation(BOOLEAN fReset);
231 static BOOLEAN HandleSpeckTalking(BOOLEAN fReset);
232 static BOOLEAN ShouldSpeckSayAQuote(void);
233
234
EnterMercs()235 void EnterMercs()
236 {
237 SetBookMark( MERC_BOOKMARK );
238
239 //Reset a static variable
240 HandleSpeckTalking( TRUE );
241
242 InitMercBackGround();
243
244 // load the Account box graphic and add it
245 guiAccountBox = AddVideoObjectFromFile(LAPTOPDIR "/accountbox.sti");
246
247 // load the files Box graphic and add it
248 guiFilesBox = AddVideoObjectFromFile(LAPTOPDIR "/filesbox.sti");
249
250 // load the MercSymbol graphic and add it
251 guiMercSymbol = AddVideoObjectFromFile(LAPTOPDIR "/mercsymbol.sti");
252
253 // load the SpecPortrait graphic and add it
254 guiSpecPortrait = AddVideoObjectFromFile(LAPTOPDIR "/specportrait.sti");
255
256 // load the Arrow graphic and add it
257 guiArrow = AddVideoObjectFromFile(LAPTOPDIR "/arrow.sti");
258
259 // load the Merc video conf background graphic and add it
260 guiMercVideoPopupBackground = AddVideoObjectFromFile(LAPTOPDIR "/speckcomwindow.sti");
261
262 // Account Box button
263 guiAccountBoxButtonImage = LoadButtonImage(LAPTOPDIR "/smallbuttons.sti", 0, 1);
264
265 guiAccountBoxButton = QuickCreateButton(guiAccountBoxButtonImage, MERC_ACCOUNT_BUTTON_X, MERC_ACCOUNT_BUTTON_Y, MSYS_PRIORITY_HIGH, BtnAccountBoxButtonCallback);
266 guiAccountBoxButton->SetCursor(CURSOR_LAPTOP_SCREEN);
267
268 guiFileBoxButton = QuickCreateButton(guiAccountBoxButtonImage, MERC_FILE_BUTTON_X, MERC_FILE_BUTTON_Y, MSYS_PRIORITY_HIGH, BtnFileBoxButtonCallback);
269 guiFileBoxButton->SetCursor(CURSOR_LAPTOP_SCREEN);
270 guiFileBoxButton->SpecifyDisabledStyle(GUI_BUTTON::DISABLED_STYLE_SHADED);
271
272 //if the player doesnt have an account disable it
273 if( LaptopSaveInfo.gubPlayersMercAccountStatus == MERC_NO_ACCOUNT )
274 {
275 DisableButton( guiFileBoxButton );
276 }
277
278
279 //
280 // Video Conferencing stuff
281 //
282
283 // Create a background video surface to blt the face onto
284 guiMercVideoFaceBackground = AddVideoSurface(MERC_VIDEO_FACE_WIDTH, MERC_VIDEO_FACE_HEIGHT, PIXEL_DEPTH);
285
286 RenderMercs();
287
288 //init the face
289
290 gfJustEnteredMercSite = TRUE;
291
292 //if NOT entering from a subsite
293 if( gubArrivedFromMercSubSite == MERC_CAME_FROM_OTHER_PAGE )
294 {
295 //Set that we have been here before
296 if( LaptopSaveInfo.ubPlayerBeenToMercSiteStatus == MERC_SITE_NEVER_VISITED )
297 LaptopSaveInfo.ubPlayerBeenToMercSiteStatus = MERC_SITE_FIRST_VISIT;
298 else if( LaptopSaveInfo.ubPlayerBeenToMercSiteStatus == MERC_SITE_FIRST_VISIT )
299 LaptopSaveInfo.ubPlayerBeenToMercSiteStatus = MERC_SITE_SECOND_VISIT;
300 else
301 LaptopSaveInfo.ubPlayerBeenToMercSiteStatus = MERC_SITE_THIRD_OR_MORE_VISITS;
302
303 //Reset the speech variable
304 gusMercVideoSpeckSpeech = MERC_VIDEO_SPECK_SPEECH_NOT_TALKING;
305 }
306
307
308 GetSpeckConditionalOpening( TRUE );
309 //gubArrivedFromMercSubSite = MERC_CAME_FROM_OTHER_PAGE;
310
311 //if Speck should start talking
312 if( ShouldSpeckSayAQuote() )
313 {
314 gubCurrentMercVideoMode = MERC_VIDEO_INIT_VIDEO_MODE;
315 }
316
317 //Reset the some variables
318 HandleSpeckIdleConversation( TRUE );
319
320 //Since we are in the site, set the flag
321 gfInMercSite = TRUE;
322 }
323
324
325 static void InitDestroyXToCloseVideoWindow(BOOLEAN fCreate);
326 static void RemoveSpeckPopupTextBox(void);
327 static void StopSpeckFromTalking(void);
328
329
ExitMercs()330 void ExitMercs()
331 {
332 StopSpeckFromTalking( );
333
334 if( gfMercVideoIsBeingDisplayed )
335 {
336 gfMercVideoIsBeingDisplayed = FALSE;
337 DeleteFace(g_video_speck_face);
338 InitDestroyXToCloseVideoWindow( FALSE );
339 gubCurrentMercVideoMode = MERC_VIDEO_NO_VIDEO_MODE;
340 }
341
342 DeleteVideoObject(guiAccountBox);
343 DeleteVideoObject(guiFilesBox);
344 DeleteVideoObject(guiMercSymbol);
345 DeleteVideoObject(guiSpecPortrait);
346 DeleteVideoObject(guiArrow);
347 DeleteVideoObject(guiMercVideoPopupBackground);
348
349 UnloadButtonImage( guiAccountBoxButtonImage );
350 RemoveButton( guiFileBoxButton );
351 RemoveButton( guiAccountBoxButton );
352
353 RemoveMercBackGround();
354
355 DeleteVideoSurface(guiMercVideoFaceBackground);
356
357 /*
358 //Set that we have been here before
359 if( LaptopSaveInfo.ubPlayerBeenToMercSiteStatus == MERC_SITE_FIRST_VISIT )
360 LaptopSaveInfo.ubPlayerBeenToMercSiteStatus = MERC_SITE_SECOND_VISIT;
361 else
362 LaptopSaveInfo.ubPlayerBeenToMercSiteStatus = MERC_SITE_THIRD_OR_MORE_VISITS;*/
363
364 gfJustEnteredMercSite = TRUE;
365 gusMercVideoSpeckSpeech = MERC_VIDEO_SPECK_SPEECH_NOT_TALKING;
366
367 //Remove the merc text box if one is available
368 RemoveSpeckPopupTextBox();
369
370 //Set up so next time we come in, we know we came from a differnt page
371 gubArrivedFromMercSubSite = MERC_CAME_FROM_OTHER_PAGE;
372
373 gfJustHiredAMercMerc = FALSE;
374
375 //Since we are leaving the site, set the flag
376 gfInMercSite = TRUE;
377
378 //Empty the Queue cause Speck could still have a quote in waiting
379 EmptyDialogueQueue( );
380 }
381
382
383 static void DrawMercVideoBackGround(void);
384 static void HandleTalkingSpeck(void);
385 static void InitMercVideoFace(void);
386
387
HandleMercs()388 void HandleMercs()
389 {
390 if( gfRedrawMercSite )
391 {
392 RenderMercs();
393 gfRedrawMercSite = FALSE;
394 gfMercSiteScreenIsReDrawn = TRUE;
395 }
396
397 //if Speck has something to say, say it
398 if( gusMercVideoSpeckSpeech != MERC_VIDEO_SPECK_SPEECH_NOT_TALKING )// && !gfDoneIntroSpeech )
399 {
400 //if the face isnt active, make it so
401 if( !gfMercVideoIsBeingDisplayed )
402 {
403 // Blt the video window background
404 DrawMercVideoBackGround();
405
406 InitDestroyXToCloseVideoWindow( TRUE );
407
408 InitMercVideoFace();
409 gubCurrentMercVideoMode = MERC_VIDEO_INIT_VIDEO_MODE;
410
411 //gfMercSiteScreenIsReDrawn = TRUE;
412 }
413 }
414
415 //if the page is redrawn, and we are in video conferencing, redraw the VC backgrund graphic
416 if( gfMercVideoIsBeingDisplayed && gfMercSiteScreenIsReDrawn )
417 {
418 // Blt the video window background
419 DrawMercVideoBackGround();
420
421 gfMercSiteScreenIsReDrawn = FALSE;
422 }
423
424
425 //if Specks should be video conferencing...
426 if( gubCurrentMercVideoMode != MERC_VIDEO_NO_VIDEO_MODE )
427 {
428 HandleTalkingSpeck();
429 }
430
431 //Reset the some variables
432 HandleSpeckIdleConversation( FALSE );
433
434 if (!fCurrentlyInLaptop)
435 {
436 //if we are exiting the laptop screen, shut up the speck
437 StopSpeckFromTalking( );
438 }
439 }
440
RenderMercs()441 void RenderMercs()
442 {
443 DrawMecBackGround();
444
445 BltVideoObject(FRAME_BUFFER, guiMercSymbol, 0, MERC_TITLE_X, MERC_TITLE_Y);
446 BltVideoObject(FRAME_BUFFER, guiSpecPortrait, 0, MERC_PORTRAIT_X, MERC_PORTRAIT_Y);
447 BltVideoObject(FRAME_BUFFER, guiAccountBox, 0, MERC_ACCOUNT_BOX_X, MERC_ACCOUNT_BOX_Y);
448 BltVideoObject(FRAME_BUFFER, guiFilesBox, 0, MERC_FILE_BOX_X, MERC_FILE_BOX_Y);
449
450 //Text on the Speck Portrait
451 DisplayWrappedString(MERC_PORTRAIT_TEXT_X, MERC_PORTRAIT_TEXT_Y, MERC_PORTRAIT_TEXT_WIDTH, 2, MERC_TEXT_FONT, MERC_TEXT_COLOR, MercHomePageText[MERC_SPECK_OWNER], FONT_MCOLOR_BLACK, CENTER_JUSTIFIED);
452
453 //Text on the Account Box
454 if( LaptopSaveInfo.gubPlayersMercAccountStatus == MERC_NO_ACCOUNT )
455 DisplayWrappedString(MERC_ACCOUNT_BOX_TEXT_X, MERC_ACCOUNT_BOX_TEXT_Y, MERC_ACCOUNT_BOX_TEXT_WIDTH, 2, MERC_TEXT_FONT, MERC_TEXT_COLOR, MercHomePageText[MERC_OPEN_ACCOUNT], FONT_MCOLOR_BLACK, RIGHT_JUSTIFIED);
456 else
457 DisplayWrappedString(MERC_ACCOUNT_BOX_TEXT_X, MERC_ACCOUNT_BOX_TEXT_Y, MERC_ACCOUNT_BOX_TEXT_WIDTH, 2, MERC_TEXT_FONT, MERC_TEXT_COLOR, MercHomePageText[MERC_VIEW_ACCOUNT], FONT_MCOLOR_BLACK, RIGHT_JUSTIFIED);
458
459 //Text on the Files Box
460 DisplayWrappedString(MERC_FILE_BOX_TEXT_X, MERC_FILE_BOX_TEXT_Y, MERC_FILE_BOX_TEXT_WIDTH, 2, MERC_TEXT_FONT, MERC_TEXT_COLOR, MercHomePageText[MERC_VIEW_FILES], FONT_MCOLOR_BLACK, RIGHT_JUSTIFIED);
461
462 //If the Specks popup dioalogue box is active, display it.
463 if (g_merc_popup_box)
464 {
465 guiAccountBoxButton->Draw();
466 guiAccountBoxButton->uiFlags |= BUTTON_FORCE_UNDIRTY;
467 RenderMercPopUpBox(g_merc_popup_box, gusSpeckDialogueX, MERC_TEXT_BOX_POS_Y, FRAME_BUFFER);
468 }
469
470 MarkButtonsDirty( );
471 RenderWWWProgramTitleBar( );
472
473 //if the page is redrawn, and we are in video conferencing, redraw the VC backgrund graphic
474 gfMercSiteScreenIsReDrawn = TRUE;
475
476 guiAccountBoxButton->uiFlags &= ~BUTTON_FORCE_UNDIRTY;
477
478 InvalidateRegion(LAPTOP_SCREEN_UL_X,LAPTOP_SCREEN_WEB_UL_Y,LAPTOP_SCREEN_LR_X,LAPTOP_SCREEN_WEB_LR_Y);
479 }
480
481
InitMercBackGround()482 void InitMercBackGround()
483 {
484 // load the Merc background graphic and add it
485 guiMercBackGround = AddVideoObjectFromFile(LAPTOPDIR "/mercbackground.sti");
486 }
487
488
DrawMecBackGround()489 void DrawMecBackGround()
490 {
491 WebPageTileBackground(4, 4, MERC_BACKGROUND_WIDTH, MERC_BACKGROUND_HEIGHT, guiMercBackGround);
492 }
493
494
RemoveMercBackGround()495 void RemoveMercBackGround()
496 {
497 DeleteVideoObject(guiMercBackGround);
498 }
499
500
BtnAccountBoxButtonCallback(GUI_BUTTON * btn,INT32 reason)501 static void BtnAccountBoxButtonCallback(GUI_BUTTON *btn, INT32 reason)
502 {
503 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
504 {
505 if (LaptopSaveInfo.gubPlayersMercAccountStatus == MERC_NO_ACCOUNT)
506 guiCurrentLaptopMode = LAPTOP_MODE_MERC_NO_ACCOUNT;
507 else
508 guiCurrentLaptopMode = LAPTOP_MODE_MERC_ACCOUNT;
509
510 if (g_merc_popup_box)
511 {
512 guiAccountBoxButton->uiFlags |= BUTTON_FORCE_UNDIRTY;
513 RenderMercPopUpBox(g_merc_popup_box, gusSpeckDialogueX, MERC_TEXT_BOX_POS_Y, FRAME_BUFFER);
514 }
515 }
516 }
517
518
BtnFileBoxButtonCallback(GUI_BUTTON * btn,INT32 reason)519 static void BtnFileBoxButtonCallback(GUI_BUTTON *btn, INT32 reason)
520 {
521 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
522 {
523 guiCurrentLaptopMode = LAPTOP_MODE_MERC_FILES;
524 }
525 }
526
527
528 static void ScheduleNewMercsToBeAvailable();
529 static BOOLEAN ShouldTheMercSiteServerGoDown(void);
530
531
DailyUpdateOfMercSite(UINT16 usDate)532 void DailyUpdateOfMercSite( UINT16 usDate)
533 {
534 INT32 iNumDays;
535
536 //if its the first day, leave
537 if( usDate == 1 )
538 return;
539
540 iNumDays = 0;
541
542 //loop through all of the hired mercs from M.E.R.C.
543 for (const MERCListingModel* m : GCM->getMERCListings())
544 {
545 const ProfileID pid = GetProfileIDFromMERCListing(m);
546 if (!IsMercOnTeam(pid)) continue;
547 MERCPROFILESTRUCT& p = GetProfile(pid);
548
549 //if the merc is dead, dont advance the contract length
550 if (!IsMercDead(p)) p.iMercMercContractLength += 1;
551
552 //Get the longest time
553 if (p.iMercMercContractLength > iNumDays) iNumDays = p.iMercMercContractLength;
554 }
555
556 //if the players hasnt paid for a while, get email him to tell him to pay
557 //iTotalContractLength
558 if( iNumDays > MERC_NUM_DAYS_TILL_ACCOUNT_INVALID )
559 {
560 if( LaptopSaveInfo.gubPlayersMercAccountStatus != MERC_ACCOUNT_INVALID )
561 {
562 LaptopSaveInfo.gubPlayersMercAccountStatus = MERC_ACCOUNT_INVALID;
563 AddEmail( MERC_INVALID, MERC_INVALID_LENGTH, SPECK_FROM_MERC, GetWorldTotalMin());
564 }
565 }
566 else if( iNumDays > MERC_NUM_DAYS_TILL_ACCOUNT_SUSPENDED )
567 {
568 if( LaptopSaveInfo.gubPlayersMercAccountStatus != MERC_ACCOUNT_SUSPENDED )
569 {
570 LaptopSaveInfo.gubPlayersMercAccountStatus = MERC_ACCOUNT_SUSPENDED;
571 AddEmail( MERC_WARNING, MERC_WARNING_LENGTH, SPECK_FROM_MERC, GetWorldTotalMin());
572
573 // Have speck complain next time player come to site
574 LaptopSaveInfo.uiSpeckQuoteFlags |= SPECK_QUOTE__SENT_EMAIL_ABOUT_LACK_OF_PAYMENT;
575 }
576 }
577 else if( iNumDays > MERC_NUM_DAYS_TILL_FIRST_WARNING)
578 {
579 if( LaptopSaveInfo.gubPlayersMercAccountStatus != MERC_ACCOUNT_VALID_FIRST_WARNING )
580 {
581 LaptopSaveInfo.gubPlayersMercAccountStatus = MERC_ACCOUNT_VALID_FIRST_WARNING;
582 AddEmail( MERC_FIRST_WARNING, MERC_FIRST_WARNING_LENGTH, SPECK_FROM_MERC, GetWorldTotalMin());
583
584 // Have speck complain next time player come to site
585 LaptopSaveInfo.uiSpeckQuoteFlags |= SPECK_QUOTE__SENT_EMAIL_ABOUT_LACK_OF_PAYMENT;
586 }
587 }
588
589
590 //Check and act if any new Merc Mercs should become available
591 ScheduleNewMercsToBeAvailable();
592
593 // If the merc site has never gone down, the number of MERC payment days is above 'X',
594 // and the players account status is ok ( cant have the merc site going down when the player owes him money, player may lose account that way )
595 if( ShouldTheMercSiteServerGoDown() )
596 {
597 UINT32 uiTimeInMinutes=0;
598
599
600 //Set the fact the site has gone down
601 LaptopSaveInfo.fMercSiteHasGoneDownYet = TRUE;
602
603 //Get the site up the next day at 6:00 pm
604 uiTimeInMinutes = GetMidnightOfFutureDayInMinutes( 1 ) + 18 * 60;
605
606 //Add an event that will get the site back up and running
607 AddStrategicEvent( EVENT_MERC_SITE_BACK_ONLINE, uiTimeInMinutes, 0 );
608 }
609 }
610
611
612 static BOOLEAN HasLarryRelapsed(void);
613
614 /**
615 * Returns the ProfileID of this profile listing.
616 *
617 * This method handles LARRY logic (LARRY_NORMAL becoming LARRY_DRUNK once
618 * relapsed), and should always be used instead of directly accessing the
619 * field profileID
620 */
GetProfileIDFromMERCListing(const MERCListingModel * listing)621 ProfileID GetProfileIDFromMERCListing(const MERCListingModel* listing)
622 {
623 ProfileID ubMercID = listing->profileID;
624 if (ubMercID == LARRY_NORMAL && HasLarryRelapsed())
625 {
626 return LARRY_DRUNK;
627 }
628 return ubMercID;
629 };
630
631 //Gets the actual merc id from the array
GetProfileIDFromMERCListingIndex(UINT8 ubMercIndex)632 ProfileID GetProfileIDFromMERCListingIndex(UINT8 ubMercIndex)
633 {
634 auto mercsListing = GCM->getMERCListings();
635 return GetProfileIDFromMERCListing(mercsListing.at(ubMercIndex));
636 }
637
InitMercVideoFace(void)638 static void InitMercVideoFace(void)
639 {
640 // Allocates space, and loads the sti for SPECK
641 FACETYPE& f = InitFace(SPECK, 0, 0);
642 g_video_speck_face = &f;
643
644 // Sets up the eyes blinking and the mouth moving
645 SetAutoFaceActive(guiMercVideoFaceBackground, FACE_AUTO_RESTORE_BUFFER, f, 0, 0);
646
647 //Renders the face to the background
648 RenderAutoFace(f);
649
650 //enables the global flag indicating the the video is being displayed
651 gfMercVideoIsBeingDisplayed = TRUE;
652
653 }
654
655
StartSpeckTalking(UINT16 usQuoteNum)656 static BOOLEAN StartSpeckTalking(UINT16 usQuoteNum)
657 {
658 if( usQuoteNum == MERC_VIDEO_SPECK_SPEECH_NOT_TALKING || usQuoteNum == MERC_VIDEO_SPECK_HAS_TO_TALK_BUT_QUOTE_NOT_CHOSEN_YET )
659 return( FALSE );
660
661 //Reset the time for when speck starts to do the random quotes
662 HandleSpeckIdleConversation( TRUE );
663
664 //Start Speck talking
665 CharacterDialogue(SPECK, usQuoteNum, g_video_speck_face, DIALOGUE_SPECK_CONTACT_PAGE_UI, FALSE);
666
667 gusMercVideoSpeckSpeech = MERC_VIDEO_SPECK_SPEECH_NOT_TALKING;
668
669 return(TRUE);
670 }
671
672
673 // Performs the frame by frame update
HandleSpeckTalking(BOOLEAN fReset)674 static BOOLEAN HandleSpeckTalking(BOOLEAN fReset)
675 {
676 static BOOLEAN fWasTheMercTalking=FALSE;
677 BOOLEAN fIsTheMercTalking;
678
679 if( fReset )
680 {
681 fWasTheMercTalking = FALSE;
682 return( TRUE );
683 }
684
685 HandleDialogue();
686 HandleAutoFaces( );
687 HandleTalkingAutoFaces( );
688
689 //Blt the face surface to the video background surface
690 SGPBox const SrcRect = { 0, 0, 48, 43 };
691 SGPBox const DestRect = { MERC_VIDEO_FACE_X, MERC_VIDEO_FACE_Y, MERC_VIDEO_FACE_WIDTH, MERC_VIDEO_FACE_HEIGHT };
692 BltStretchVideoSurface(FRAME_BUFFER, guiMercVideoFaceBackground, &SrcRect, &DestRect);
693
694 //HandleCurrentMercDistortion();
695
696 InvalidateRegion(MERC_VIDEO_BACKGROUND_X, MERC_VIDEO_BACKGROUND_Y, (MERC_VIDEO_BACKGROUND_X + MERC_VIDEO_BACKGROUND_WIDTH), (MERC_VIDEO_BACKGROUND_Y + MERC_VIDEO_BACKGROUND_HEIGHT) );
697
698 //find out if the merc just stopped talking
699 fIsTheMercTalking = g_video_speck_face->fTalking;
700
701 //if the merc just stopped talking
702 if(fWasTheMercTalking && !fIsTheMercTalking)
703 {
704 fWasTheMercTalking = FALSE;
705
706 if( DialogueQueueIsEmpty( ) )
707 {
708 RemoveSpeckPopupTextBox();
709
710 gfDisplaySpeckTextBox = FALSE;
711
712 gusMercVideoSpeckSpeech = MERC_VIDEO_SPECK_SPEECH_NOT_TALKING;
713
714 //Reset the time for when speck starts to do the random quotes
715 HandleSpeckIdleConversation( TRUE );
716 }
717 else
718 fIsTheMercTalking = TRUE;
719 }
720
721 fWasTheMercTalking = fIsTheMercTalking;
722
723 return(fIsTheMercTalking);
724 }
725
InitDestroyXToCloseVideoWindow(BOOLEAN fCreate)726 static void InitDestroyXToCloseVideoWindow(BOOLEAN fCreate)
727 {
728 static BOOLEAN fButtonCreated=FALSE;
729
730 //if we are asked to create the buttons and the button isnt already created
731 if( fCreate && !fButtonCreated )
732 {
733 guiXToCloseMercVideoButton = QuickCreateButtonImg(LAPTOPDIR "/closebutton.sti", 0, 1, MERC_X_TO_CLOSE_VIDEO_X, MERC_X_TO_CLOSE_VIDEO_Y, MSYS_PRIORITY_HIGH, BtnXToCloseMercVideoButtonCallback);
734 guiXToCloseMercVideoButton->SetCursor(CURSOR_LAPTOP_SCREEN);
735
736 fButtonCreated = TRUE;
737 }
738
739 //if we are asked to destroy the buttons and the buttons are created
740 if( !fCreate && fButtonCreated )
741 {
742 RemoveButton( guiXToCloseMercVideoButton );
743 fButtonCreated = FALSE;
744 }
745 }
746
747
BtnXToCloseMercVideoButtonCallback(GUI_BUTTON * btn,INT32 reason)748 static void BtnXToCloseMercVideoButtonCallback(GUI_BUTTON *btn, INT32 reason)
749 {
750 if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
751 {
752 //Stop speck from talking
753 //ShutupaYoFace(g_video_speck_face);
754 StopSpeckFromTalking();
755
756 //make sure we are done the intro speech
757 gfDoneIntroSpeech = TRUE;
758
759 //remove the video conf mode
760 gubCurrentMercVideoMode = MERC_VIDEO_EXIT_VIDEO_MODE;
761
762 gusMercVideoSpeckSpeech = MERC_VIDEO_SPECK_SPEECH_NOT_TALKING;
763 }
764 }
765
766
DisplayMercVideoIntro(UINT16 usTimeTillFinish)767 static BOOLEAN DisplayMercVideoIntro(UINT16 usTimeTillFinish)
768 {
769 UINT32 uiCurTime = GetJA2Clock();
770 static UINT32 uiLastTime=0;
771
772 //init variable
773 if( uiLastTime == 0 )
774 uiLastTime = uiCurTime;
775
776
777 ColorFillVideoSurfaceArea( FRAME_BUFFER, MERC_VIDEO_FACE_X, MERC_VIDEO_FACE_Y, MERC_VIDEO_FACE_X+MERC_VIDEO_FACE_WIDTH, MERC_VIDEO_FACE_Y+MERC_VIDEO_FACE_HEIGHT, Get16BPPColor( FROMRGB( 0, 0, 0 ) ) );
778
779 //if the intro is done
780 if( (uiCurTime - uiLastTime) > usTimeTillFinish )
781 {
782 uiLastTime = 0;
783 return(TRUE);
784 }
785 else
786 return(FALSE);
787 }
788
789
790 static BOOLEAN ShouldSpeckStartTalkingDueToActionOnSubPage(void);
791
792
HandleTalkingSpeck(void)793 static void HandleTalkingSpeck(void)
794 {
795 BOOLEAN fIsSpeckTalking = TRUE;
796
797 switch( gubCurrentMercVideoMode )
798 {
799 //Init the video conferencing
800 case MERC_VIDEO_INIT_VIDEO_MODE:
801 //perform some opening animation. When its done start Speck talking
802
803 //if the intro is finished
804 if( DisplayMercVideoIntro( MERC_INTRO_TIME ) )
805 {
806 //NULL out the string
807 gsSpeckDialogueTextPopUp = ST::null;
808
809 //Start speck talking
810 if( !StartSpeckTalking( gusMercVideoSpeckSpeech ) )
811 {
812 gusMercVideoSpeckSpeech = MERC_VIDEO_SPECK_SPEECH_NOT_TALKING;
813 }
814
815 gubCurrentMercVideoMode = MERC_VIDEO_VIDEO_MODE;
816 }
817 break;
818
819 //Display his talking and blinking face
820 case MERC_VIDEO_VIDEO_MODE:
821
822 //Make sure the accounts button does not overwrite the dialog text
823 //guiAccountBoxButton->uiFlags |= BUTTON_FORCE_UNDIRTY;
824 //def:
825
826 if( (gfJustEnteredMercSite && gubArrivedFromMercSubSite != MERC_CAME_FROM_OTHER_PAGE) || gfFirstTimeIntoMERCSiteSinceEnteringLaptop )
827 {
828 gfFirstTimeIntoMERCSiteSinceEnteringLaptop = FALSE;
829 GetSpeckConditionalOpening( FALSE );
830 gfJustEnteredMercSite = FALSE;
831 }
832 else
833 {
834 fIsSpeckTalking = HandleSpeckTalking( FALSE );
835
836 if( !fIsSpeckTalking )
837 fIsSpeckTalking = GetSpeckConditionalOpening( FALSE );
838
839 //if speck didnt start talking, see if he just hired someone
840 if( !fIsSpeckTalking )
841 {
842 fIsSpeckTalking = ShouldSpeckStartTalkingDueToActionOnSubPage();
843 }
844 }
845
846 if( !fIsSpeckTalking )
847 gubCurrentMercVideoMode = MERC_VIDEO_EXIT_VIDEO_MODE;
848
849 if( gfDisplaySpeckTextBox && gGameSettings.fOptions[ TOPTION_SUBTITLES ] )
850 {
851 if( !gfInMercSite )
852 {
853 StopSpeckFromTalking( );
854 return;
855 }
856
857
858 if (!gsSpeckDialogueTextPopUp.empty())
859 {
860 //guiAccountBoxButton->Draw();
861 //guiAccountBoxButton->uiFlags |= BUTTON_FORCE_UNDIRTY;
862
863 if (g_merc_popup_box)
864 {
865 guiAccountBoxButton->Draw();
866 guiAccountBoxButton->uiFlags |= BUTTON_FORCE_UNDIRTY;
867 RenderMercPopUpBox(g_merc_popup_box, gusSpeckDialogueX, MERC_TEXT_BOX_POS_Y, FRAME_BUFFER);
868 }
869 }
870 }
871
872 break;
873
874 // shut down the video conferencing
875 case MERC_VIDEO_EXIT_VIDEO_MODE:
876
877 //if the exit animation is finished, exit the video conf window
878 if( DisplayMercVideoIntro( MERC_EXIT_TIME ) )
879 {
880 StopSpeckFromTalking( );
881
882 //Delete the face
883 DeleteFace(g_video_speck_face);
884 InitDestroyXToCloseVideoWindow( FALSE );
885
886 gfRedrawMercSite = TRUE;
887 gfMercVideoIsBeingDisplayed = FALSE;
888
889 //Remove the merc popup
890 RemoveSpeckPopupTextBox();
891
892 //maybe display ending animation
893 gubCurrentMercVideoMode = MERC_VIDEO_NO_VIDEO_MODE;
894 }
895 else
896 {
897 //else we are done the exit animation. The area is not being invalidated anymore
898 InvalidateRegion( MERC_VIDEO_FACE_X, MERC_VIDEO_FACE_Y, MERC_VIDEO_FACE_X+MERC_VIDEO_FACE_WIDTH, MERC_VIDEO_FACE_Y+MERC_VIDEO_FACE_HEIGHT );
899 }
900 break;
901 }
902 }
903
904
905 static void MercSiteSubTitleRegionCallBack(MOUSE_REGION* pRegion, INT32 iReason);
906
907
DisplayTextForSpeckVideoPopUp(const ST::string & str)908 void DisplayTextForSpeckVideoPopUp(const ST::string& str)
909 {
910 UINT16 usActualHeight;
911
912 //If the user has selected no subtitles
913 if( !gGameSettings.fOptions[ TOPTION_SUBTITLES ] )
914 return;
915
916 //add the "" around the speech.
917 gsSpeckDialogueTextPopUp = ST::format("\"{}\"", str);
918
919 gfDisplaySpeckTextBox = TRUE;
920
921 //Set this so the popup box doesnt render in RenderMercs()
922 MercPopUpBox* const old_merc_popup_box = g_merc_popup_box;
923 g_merc_popup_box = 0;
924
925 //Render the screen to get rid of any old text popup boxes
926 RenderMercs();
927
928 g_merc_popup_box = old_merc_popup_box;
929
930 if( gfMercVideoIsBeingDisplayed && gfMercSiteScreenIsReDrawn )
931 {
932 DrawMercVideoBackGround();
933 }
934
935 //Create the popup box
936 g_merc_popup_box = PrepareMercPopupBox(g_merc_popup_box, BASIC_MERC_POPUP_BACKGROUND, BASIC_MERC_POPUP_BORDER, gsSpeckDialogueTextPopUp, 300, 0, 0, 0, &gusSpeckDialogueActualWidth, &usActualHeight);
937
938 gusSpeckDialogueX = ( LAPTOP_SCREEN_LR_X - gusSpeckDialogueActualWidth - LAPTOP_SCREEN_UL_X ) / 2 + LAPTOP_SCREEN_UL_X;
939
940 //Render the pop box
941 RenderMercPopUpBox(g_merc_popup_box, gusSpeckDialogueX, MERC_TEXT_BOX_POS_Y, FRAME_BUFFER);
942
943 //check to make sure the region is not already initialized
944 if( !( gMercSiteSubTitleMouseRegion.uiFlags & MSYS_REGION_EXISTS ) )
945 {
946 MSYS_DefineRegion(&gMercSiteSubTitleMouseRegion, gusSpeckDialogueX, MERC_TEXT_BOX_POS_Y,
947 (INT16)(gusSpeckDialogueX + gusSpeckDialogueActualWidth),
948 (INT16)(MERC_TEXT_BOX_POS_Y + usActualHeight), MSYS_PRIORITY_HIGH,
949 CURSOR_LAPTOP_SCREEN, MSYS_NO_CALLBACK, MercSiteSubTitleRegionCallBack);
950 }
951 }
952
953 static BOOLEAN AreAnyOfTheNewMercsAvailable(void);
954 static UINT32 CalcMercDaysServed(void);
955 static BOOLEAN CanMercBeAvailableYet(const MERCListingModel* mercToCheck);
956 static UINT8 CountNumberOfMercMercsHired(void);
957 static UINT8 CountNumberOfMercMercsWhoAreDead(void);
958 static BOOLEAN IsAnyMercMercsDead(void);
959 static BOOLEAN IsAnyMercMercsHired(void);
960 static void MakeBiffAwayForCoupleOfDays(void);
961
962
IsSpeckTryingToRecruit()963 static BOOLEAN IsSpeckTryingToRecruit()
964 {
965 auto listings = GCM->getMERCListings();
966 if (LaptopSaveInfo.gubLastMercIndex >= (listings.size() - 1))
967 {
968 // we have got all mercs already
969 return FALSE;
970 }
971
972 auto nextAvailable = listings[LaptopSaveInfo.gubLastMercIndex + 1];
973 return CanMercBeAvailableYet(nextAvailable);
974 }
975
GetSpeckConditionalOpening(BOOLEAN fJustEnteredScreen)976 static BOOLEAN GetSpeckConditionalOpening(BOOLEAN fJustEnteredScreen)
977 {
978 static UINT16 usQuoteToSay=MERC_VIDEO_SPECK_SPEECH_NOT_TALKING;
979 BOOLEAN fCanSayLackOfPaymentQuote = TRUE;
980 BOOLEAN fCanUseIdleTag = FALSE;
981
982 //If we just entered the screen, reset some variables
983 if( fJustEnteredScreen )
984 {
985 gfDoneIntroSpeech = FALSE;
986 usQuoteToSay = 0;
987 return( FALSE );
988 }
989
990 //if we are done the intro speech, or arrived from a sub page, get out of the function
991 if( gfDoneIntroSpeech || gubArrivedFromMercSubSite != MERC_CAME_FROM_OTHER_PAGE )
992 {
993 return( FALSE );
994 }
995
996 gfDoneIntroSpeech = TRUE;
997
998 //set the opening quote based on if the player has been here before
999 if( LaptopSaveInfo.ubPlayerBeenToMercSiteStatus == MERC_SITE_FIRST_VISIT && usQuoteToSay <= 8 )
1000 {
1001 StartSpeckTalking( usQuoteToSay );
1002 usQuoteToSay++;
1003 if( usQuoteToSay <= 8 )
1004 gfDoneIntroSpeech = FALSE;
1005 }
1006
1007 //if its the players second visit
1008 else if( LaptopSaveInfo.ubPlayerBeenToMercSiteStatus == MERC_SITE_SECOND_VISIT )
1009 {
1010 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_1_TOUGH_START );
1011 fCanUseIdleTag = TRUE;
1012 }
1013
1014 // We have been here at least 2 times before, Check which quote we should use
1015 else
1016 {
1017 //if the player has not hired any MERC mercs before
1018 // CJC Dec 1 2002: fixing this, so near-bankrupt msg will play
1019 if( !IsAnyMercMercsHired( ) && CalcMercDaysServed() == 0)
1020 {
1021 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_2_BUSINESS_BAD );
1022 }
1023
1024 //else if it is the first visit since the server went down
1025 else if( LaptopSaveInfo.fFirstVisitSinceServerWentDown == TRUE )
1026 {
1027 LaptopSaveInfo.fFirstVisitSinceServerWentDown = 2;
1028 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_9_FIRST_VISIT_SINCE_SERVER_WENT_DOWN );
1029 fCanUseIdleTag = TRUE;
1030 }
1031 /*
1032 //else if new mercs are available
1033 else if( LaptopSaveInfo.fNewMercsAvailableAtMercSite )
1034 {
1035 LaptopSaveInfo.fNewMercsAvailableAtMercSite = FALSE;
1036
1037 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_11_NEW_MERCS_AVAILABLE );
1038 }*/
1039 //else if lots of MERC mercs are DEAD, and speck can say the quote ( dont want him to continously say it )
1040 else if( CountNumberOfMercMercsWhoAreDead() >= 2 && LaptopSaveInfo.ubSpeckCanSayPlayersLostQuote )
1041 {
1042 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_12_PLAYERS_LOST_MERCS );
1043
1044 //Set it so speck Wont say the quote again till someone else dies
1045 LaptopSaveInfo.ubSpeckCanSayPlayersLostQuote = 0;
1046 }
1047
1048 //else if player owes lots of money
1049 else if( LaptopSaveInfo.gubPlayersMercAccountStatus == MERC_ACCOUNT_SUSPENDED )
1050 {
1051 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_5_PLAYER_OWES_SPECK_ACCOUNT_SUSPENDED );
1052
1053 fCanSayLackOfPaymentQuote = FALSE;
1054 }
1055
1056 //else if the player owes speck a large sum of money, have speck say so
1057 else if( CalculateHowMuchPlayerOwesSpeck() > 5000 )
1058 {
1059 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_6_PLAYER_OWES_SPECK_ALMOST_BANKRUPT_1 );
1060 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_6_PLAYER_OWES_SPECK_ALMOST_BANKRUPT_2 );
1061
1062 fCanSayLackOfPaymentQuote = FALSE;
1063 }
1064
1065 else
1066 {
1067 UINT8 ubRandom = ( UINT8 ) Random( 100 );
1068
1069 //if business is good
1070 if( ubRandom < 40 && AreAnyOfTheNewMercsAvailable() && CountNumberOfMercMercsHired() > 1 )
1071 {
1072 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_3_BUSINESS_GOOD );
1073 fCanUseIdleTag = TRUE;
1074 }
1075
1076 //or if still trying to recruit ( the last recruit hasnt arrived and the player has paid for some of his mercs )
1077 else if( ubRandom < 80 && IsSpeckTryingToRecruit())
1078 {
1079 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_4_TRYING_TO_RECRUIT );
1080 fCanUseIdleTag = TRUE;
1081 }
1082
1083 //else use the generic opening
1084 else
1085 {
1086 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_10_GENERIC_OPENING );
1087 fCanUseIdleTag = TRUE;
1088
1089 //if the merc hasnt said the line before
1090 if( !LaptopSaveInfo.fSaidGenericOpeningInMercSite )
1091 {
1092 LaptopSaveInfo.fSaidGenericOpeningInMercSite = TRUE;
1093 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_10_TAG_FOR_20 );
1094 }
1095 }
1096 }
1097
1098 if( fCanUseIdleTag )
1099 {
1100 UINT8 ubRandom = Random( 100 );
1101
1102 if( ubRandom < 50 )
1103 {
1104 ubRandom = Random( 4 );
1105
1106 switch( ubRandom )
1107 {
1108 case 0:
1109 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_TAG_ON_AFTER_OTHER_TAGS_1 );
1110 break;
1111 case 1:
1112 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_TAG_ON_AFTER_OTHER_TAGS_2 );
1113 break;
1114 case 2:
1115 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_TAG_ON_AFTER_OTHER_TAGS_3 );
1116 break;
1117 case 3:
1118 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_TAG_ON_AFTER_OTHER_TAGS_4 );
1119 break;
1120 default:
1121 SLOGA("GetSpeckConditionalOpening: Problem with random");
1122 }
1123 }
1124 }
1125 }
1126
1127
1128 //If Speck has sent an email to the player, and the player still hasnt paid, has speck complain about it.
1129 // CJC Dec 1 2002 ACTUALLY HOOKED IN THAT CHECK
1130 if (fCanSayLackOfPaymentQuote)
1131 {
1132 if( LaptopSaveInfo.uiSpeckQuoteFlags & SPECK_QUOTE__SENT_EMAIL_ABOUT_LACK_OF_PAYMENT )
1133 {
1134 LaptopSaveInfo.uiSpeckQuoteFlags &= ~SPECK_QUOTE__SENT_EMAIL_ABOUT_LACK_OF_PAYMENT;
1135
1136 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_TAG_PLAYER_OWES_MONEY );
1137 }
1138 }
1139
1140 //if new mercs are available
1141 if( LaptopSaveInfo.fNewMercsAvailableAtMercSite )
1142 {
1143 LaptopSaveInfo.fNewMercsAvailableAtMercSite = FALSE;
1144
1145 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_11_NEW_MERCS_AVAILABLE );
1146 }
1147
1148 //if any mercs are dead
1149 if( IsAnyMercMercsDead() )
1150 {
1151 //if no merc has died before
1152 if( !LaptopSaveInfo.fHasAMercDiedAtMercSite )
1153 {
1154 LaptopSaveInfo.fHasAMercDiedAtMercSite = TRUE;
1155 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_TAG_FIRST_MERC_DIES );
1156 }
1157
1158 //loop through all the mercs and see if any are dead and the quote is not said
1159 for (const MERCListingModel* listing : GCM->getMERCListings())
1160 {
1161 ProfileID profileID = GetProfileIDFromMERCListing(listing);
1162 MERCPROFILESTRUCT& p = GetProfile(profileID);
1163 if (!IsMercDead(p)) continue;
1164
1165 //if the quote has not been said
1166 if (p.ubMiscFlags3 & PROFILE_MISC_FLAG3_MERC_MERC_IS_DEAD_AND_QUOTE_SAID) continue;
1167 //set the flag
1168 p.ubMiscFlags3 |= PROFILE_MISC_FLAG3_MERC_MERC_IS_DEAD_AND_QUOTE_SAID;
1169
1170 if (profileID == FLO) // special handling for FLO
1171 {
1172 //if biff is dead
1173 if (IsMercDead(GetProfile(BIFF)))
1174 StartSpeckTalking(SPECK_QUOTE_ALTERNATE_OPENING_TAG_FLO_IS_DEAD_BIFF_IS_DEAD);
1175 else
1176 {
1177 StartSpeckTalking(SPECK_QUOTE_ALTERNATE_OPENING_TAG_FLO_IS_DEAD_BIFF_ALIVE);
1178 MakeBiffAwayForCoupleOfDays();
1179 }
1180 continue;
1181 }
1182
1183 const std::vector<SpeckQuote> quotes = listing->getQuotesByType(SpeckQuoteType::MERC_DEAD);
1184 if (!quotes.empty()) // other mercs
1185 {
1186 StartSpeckTalking(quotes[0]->quoteID);
1187 }
1188 }
1189 }
1190
1191
1192 //if flo has married the cousin
1193 if( gubFact[ FACT_PC_MARRYING_DARYL_IS_FLO ] )
1194 {
1195 //if speck hasnt said the quote before, and Biff is NOT dead
1196 if (!LaptopSaveInfo.fSpeckSaidFloMarriedCousinQuote && !IsMercDead(GetProfile(BIFF)))
1197 {
1198 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_TAG_FLO_MARRIED_A_COUSIN_BIFF_IS_ALIVE );
1199 LaptopSaveInfo.fSpeckSaidFloMarriedCousinQuote = TRUE;
1200
1201 MakeBiffAwayForCoupleOfDays();
1202 }
1203 }
1204
1205
1206 //if larry has relapsed
1207 if( HasLarryRelapsed() && !( LaptopSaveInfo.uiSpeckQuoteFlags & SPECK_QUOTE__ALREADY_TOLD_PLAYER_THAT_LARRY_RELAPSED ) )
1208 {
1209 LaptopSaveInfo.uiSpeckQuoteFlags |= SPECK_QUOTE__ALREADY_TOLD_PLAYER_THAT_LARRY_RELAPSED;
1210
1211 StartSpeckTalking( SPECK_QUOTE_ALTERNATE_OPENING_TAG_LARRY_RELAPSED );
1212 }
1213
1214 return( TRUE );
1215 }
1216
1217
IsAnyMercMercsHired(void)1218 static BOOLEAN IsAnyMercMercsHired(void)
1219 {
1220 //loop through all of the hired mercs from M.E.R.C.
1221 for (const MERCListingModel* m : GCM->getMERCListings())
1222 {
1223 ProfileID ubMercID = GetProfileIDFromMERCListing(m);
1224 if( IsMercOnTeam( ubMercID ) )
1225 {
1226 return( TRUE );
1227 }
1228 }
1229
1230 return( FALSE );
1231 }
1232
1233
IsAnyMercMercsDead(void)1234 static BOOLEAN IsAnyMercMercsDead(void)
1235 {
1236 //loop through all of the hired mercs from M.E.R.C.
1237 for (const MERCListingModel* m : GCM->getMERCListings())
1238 {
1239 ProfileID mercID = GetProfileIDFromMERCListing(m);
1240 if (GetProfile(mercID).bMercStatus == MERC_IS_DEAD)
1241 return( TRUE );
1242 }
1243
1244 return( FALSE );
1245 }
1246
1247
CountNumberOfMercMercsHired(void)1248 static UINT8 CountNumberOfMercMercsHired(void)
1249 {
1250 UINT8 ubCount=0;
1251
1252 //loop through all of the hired mercs from M.E.R.C
1253 for (const MERCListingModel* m : GCM->getMERCListings())
1254 {
1255 ProfileID ubMercID = GetProfileIDFromMERCListing(m);
1256 if( IsMercOnTeam( ubMercID ) )
1257 {
1258 ubCount++;
1259 }
1260 }
1261
1262 return( ubCount );
1263 }
1264
1265
CountNumberOfMercMercsWhoAreDead(void)1266 static UINT8 CountNumberOfMercMercsWhoAreDead(void)
1267 {
1268 UINT8 ubCount=0;
1269
1270 //loop through all of the hired mercs from M.E.R.C.
1271 for (const MERCListingModel* m : GCM->getMERCListings())
1272 {
1273 ProfileID ubMercID = GetProfileIDFromMERCListing(m);
1274 if (GetProfile(ubMercID).bMercStatus == MERC_IS_DEAD)
1275 {
1276 ubCount++;
1277 }
1278 }
1279
1280 return( ubCount );
1281 }
1282
1283
1284 //Mouse Call back for the pop up text box
MercSiteSubTitleRegionCallBack(MOUSE_REGION * pRegion,INT32 iReason)1285 static void MercSiteSubTitleRegionCallBack(MOUSE_REGION* pRegion, INT32 iReason)
1286 {
1287 if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP || iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
1288 {
1289 StopSpeckFromTalking( );
1290 }
1291 }
1292
1293
RemoveSpeckPopupTextBox(void)1294 static void RemoveSpeckPopupTextBox(void)
1295 {
1296 if (!g_merc_popup_box) return;
1297
1298 if( gMercSiteSubTitleMouseRegion.uiFlags & MSYS_REGION_EXISTS )
1299 MSYS_RemoveRegion( &gMercSiteSubTitleMouseRegion );
1300
1301 RemoveMercPopupBox(g_merc_popup_box);
1302 g_merc_popup_box = 0;
1303
1304 //redraw the screen
1305 gfRedrawMercSite = TRUE;
1306 }
1307
1308
1309 static BOOLEAN IsMercMercAvailable(UINT8 ubMercID);
1310
1311
HandlePlayerHiringMerc(const MERCListingModel * hired)1312 static void HandlePlayerHiringMerc(const MERCListingModel* hired)
1313 {
1314 gusMercVideoSpeckSpeech = MERC_VIDEO_SPECK_SPEECH_NOT_TALKING;
1315
1316 auto quotes = hired->getQuotesByType(SpeckQuoteType::CROSS_SELL);
1317 for (SpeckQuote quote : quotes)
1318 {
1319 if (IsMercMercAvailable(quote->relatedMercID))
1320 {
1321 StartSpeckTalking(quote->quoteID);
1322 }
1323 }
1324
1325 gubArrivedFromMercSubSite = MERC_CAME_FROM_HIRE_PAGE;
1326 }
1327
1328 // Checks if the merc is available for hire right now
IsMercMercAvailable(UINT8 ubMercID)1329 static BOOLEAN IsMercMercAvailable(UINT8 ubMercID)
1330 {
1331 for (const MERCListingModel* m : GCM->getMERCListings())
1332 {
1333 if (m->index > LaptopSaveInfo.gubLastMercIndex)
1334 {
1335 // Not available in M.E.R.C. yet
1336 break;
1337 }
1338
1339 //if this is the merc
1340 if (GetProfileIDFromMERCListing(m) == ubMercID)
1341 {
1342 //if the merc is available, and Not dead
1343 if (IsMercHireable(GetProfile(ubMercID)))
1344 return( TRUE );
1345 }
1346 }
1347
1348 return( FALSE );
1349 }
1350
1351
ShouldSpeckStartTalkingDueToActionOnSubPage(void)1352 static BOOLEAN ShouldSpeckStartTalkingDueToActionOnSubPage(void)
1353 {
1354 //if the merc came from the hire screen
1355 if( gfJustHiredAMercMerc )
1356 {
1357 auto listing = GCM->getMERCListings();
1358 HandlePlayerHiringMerc(listing[gubCurMercIndex]);
1359
1360 //get speck to say the thank you
1361 if( Random( 100 ) > 50 )
1362 StartSpeckTalking( SPECK_QUOTE_GENERIC_THANKS_FOR_HIRING_MERCS_1 );
1363 else
1364 StartSpeckTalking( SPECK_QUOTE_GENERIC_THANKS_FOR_HIRING_MERCS_2 );
1365
1366 gfJustHiredAMercMerc = FALSE;
1367 //gfDoneIntroSpeech = TRUE;
1368
1369 return( TRUE );
1370 }
1371
1372 return( FALSE );
1373 }
1374
1375
ShouldSpeckSayAQuote(void)1376 static BOOLEAN ShouldSpeckSayAQuote(void)
1377 {
1378 //if we are entering from anywhere except a sub page, and we should say the opening quote
1379 if( gfJustEnteredMercSite && gubArrivedFromMercSubSite == MERC_CAME_FROM_OTHER_PAGE )
1380 {
1381 //if the merc has something to say
1382 if( gusMercVideoSpeckSpeech != MERC_VIDEO_SPECK_SPEECH_NOT_TALKING )
1383 return( FALSE );
1384 }
1385
1386
1387 //if the player just hired a merc
1388 if( gfJustHiredAMercMerc )
1389 {
1390 gusMercVideoSpeckSpeech = MERC_VIDEO_SPECK_HAS_TO_TALK_BUT_QUOTE_NOT_CHOSEN_YET;
1391 return( TRUE );
1392
1393 /*
1394 //if the merc has something to say
1395 if( gusMercVideoSpeckSpeech != MERC_VIDEO_SPECK_SPEECH_NOT_TALKING )
1396 return( TRUE );
1397 else
1398 {
1399 gusMercVideoSpeckSpeech = MERC_VIDEO_SPECK_HAS_TO_TALK_BUT_QUOTE_NOT_CHOSEN_YET;
1400 return( TRUE );
1401 }*/
1402 }
1403
1404 //If it is the first time into the merc site
1405 if( gfFirstTimeIntoMERCSiteSinceEnteringLaptop )
1406 {
1407 //gfFirstTimeIntoMERCSiteSinceEnteringLaptop = FALSE;
1408 gusMercVideoSpeckSpeech = MERC_VIDEO_SPECK_HAS_TO_TALK_BUT_QUOTE_NOT_CHOSEN_YET;
1409 return( TRUE );
1410 }
1411
1412 /*
1413 //if we are entering from anywhere except a sub page
1414 if( gubArrivedFromMercSubSite == MERC_CAME_FROM_OTHER_PAGE )
1415 {
1416 GetSpeckConditionalOpening( FALSE );
1417 return( TRUE );
1418 }*/
1419 return( FALSE );
1420 }
1421
1422
1423 static INT16 GetRandomQuoteThatHasBeenSaidTheLeast(void);
1424 static void IncreaseMercRandomQuoteValue(UINT8 ubQuoteID, UINT8 ubValue);
1425
1426
HandleSpeckIdleConversation(BOOLEAN fReset)1427 static void HandleSpeckIdleConversation(BOOLEAN fReset)
1428 {
1429 static UINT32 uiLastTime=0;
1430 UINT32 uiCurTime = GetJA2Clock();
1431 INT16 sLeastSaidQuote;
1432
1433 //if we should reset the variables
1434 if( fReset )
1435 {
1436 uiLastTime = GetJA2Clock();
1437 return;
1438 }
1439
1440
1441
1442 if( ( uiCurTime - uiLastTime ) > SPECK_IDLE_CHAT_DELAY )
1443 {
1444
1445 //if Speck is not talking
1446 if( !gfMercVideoIsBeingDisplayed )
1447 {
1448 sLeastSaidQuote = GetRandomQuoteThatHasBeenSaidTheLeast( );
1449
1450 if( sLeastSaidQuote != -1 )
1451 gusMercVideoSpeckSpeech = (UINT8)sLeastSaidQuote;
1452
1453 //Say the aim slander quotes the least
1454 if( sLeastSaidQuote >= 47 && sLeastSaidQuote <= 57 )
1455 {
1456 IncreaseMercRandomQuoteValue( (UINT8)sLeastSaidQuote, 1 );
1457 }
1458 else if( sLeastSaidQuote != -1 )
1459 IncreaseMercRandomQuoteValue( (UINT8)sLeastSaidQuote, 3 );
1460 }
1461
1462 uiLastTime = GetJA2Clock();
1463 }
1464 }
1465
1466
1467 static BOOLEAN CanMercQuoteBeSaid(UINT32 uiQuoteID);
1468 static std::vector<UINT8> GetAvailableRandomQuotes();
1469
GetRandomQuoteThatHasBeenSaidTheLeast()1470 static INT16 GetRandomQuoteThatHasBeenSaidTheLeast()
1471 {
1472 // random as in random chatter, there has never been any random selection in this fnuction
1473 UINT32 sLeastSaidSoFar = UINT_MAX;
1474 std::vector<UINT8> quotes = GetAvailableRandomQuotes();
1475
1476 for (UINT32 quoteID : quotes)
1477 {
1478 if (sLeastSaidSoFar == UINT_MAX ||
1479 gNumberOfTimesQuoteSaid[quoteID] < gNumberOfTimesQuoteSaid[sLeastSaidSoFar])
1480 { // if this quote has been said less times then the last one, or this is the first one we check
1481 sLeastSaidSoFar = quoteID;
1482 }
1483 }
1484
1485 if (sLeastSaidSoFar == UINT_MAX)
1486 return -1;
1487 else
1488 return sLeastSaidSoFar;
1489 }
1490
1491
IncreaseMercRandomQuoteValue(UINT8 ubQuoteID,UINT8 ubValue)1492 static void IncreaseMercRandomQuoteValue(UINT8 ubQuoteID, UINT8 ubValue)
1493 {
1494 gNumberOfTimesQuoteSaid[ubQuoteID] += ubValue;
1495 }
1496
1497
StopSpeckFromTalking(void)1498 static void StopSpeckFromTalking(void)
1499 {
1500 if (g_video_speck_face == NULL) return;
1501
1502 //Stop speck from talking
1503 ShutupaYoFace(g_video_speck_face);
1504
1505 RemoveSpeckPopupTextBox();
1506
1507 gusMercVideoSpeckSpeech = MERC_VIDEO_SPECK_SPEECH_NOT_TALKING;
1508 }
1509
1510
HasLarryRelapsed(void)1511 static BOOLEAN HasLarryRelapsed(void)
1512 {
1513 return( CheckFact( FACT_LARRY_CHANGED, 0 ) );
1514 }
1515
1516
1517 //Gets Called on each enter into laptop.
EnterInitMercSite()1518 void EnterInitMercSite()
1519 {
1520 gfFirstTimeIntoMERCSiteSinceEnteringLaptop = TRUE;
1521 gubCurMercIndex = 0;
1522 }
1523
1524
ShouldTheMercSiteServerGoDown(void)1525 static BOOLEAN ShouldTheMercSiteServerGoDown(void)
1526 {
1527 UINT32 uiDay = GetWorldDay();
1528
1529 // If the merc site has never gone down, the first new merc has shown ( which shows the player is using the site ),
1530 // and the players account status is ok ( cant have the merc site going down when the player owes him money, player may lose account that way )
1531 if( !LaptopSaveInfo.fMercSiteHasGoneDownYet && LaptopSaveInfo.ubLastMercAvailableId >= 1 && LaptopSaveInfo.gubPlayersMercAccountStatus == MERC_ACCOUNT_VALID )
1532 {
1533 if( Random( 100 ) < ( uiDay * 2 + 10 ) )
1534 {
1535 return( TRUE );
1536 }
1537 else
1538 {
1539 return( FALSE );
1540 }
1541 }
1542
1543 return( FALSE );
1544 }
1545
1546
GetMercSiteBackOnline()1547 void GetMercSiteBackOnline()
1548 {
1549 //Add an email telling the user the site is back up
1550 AddEmail( MERC_NEW_SITE_ADDRESS, MERC_NEW_SITE_ADDRESS_LENGTH, SPECK_FROM_MERC, GetWorldTotalMin() );
1551
1552 //Set a flag indicating that the server just went up ( so speck can make a comment when the player next visits the site )
1553 LaptopSaveInfo.fFirstVisitSinceServerWentDown = TRUE;
1554 }
1555
1556
DrawMercVideoBackGround(void)1557 static void DrawMercVideoBackGround(void)
1558 {
1559 BltVideoObject(FRAME_BUFFER, guiMercVideoPopupBackground, 0, MERC_VIDEO_BACKGROUND_X, MERC_VIDEO_BACKGROUND_Y);
1560
1561 //put the title on the window
1562 DrawTextToScreen(MercHomePageText[MERC_SPECK_COM], MERC_X_VIDEO_TITLE_X, MERC_X_VIDEO_TITLE_Y, 0, MERC_VIDEO_TITLE_FONT, MERC_VIDEO_TITLE_COLOR, FONT_MCOLOR_BLACK, LEFT_JUSTIFIED);
1563 InvalidateRegion(MERC_VIDEO_BACKGROUND_X, MERC_VIDEO_BACKGROUND_Y, (MERC_VIDEO_BACKGROUND_X + MERC_VIDEO_BACKGROUND_WIDTH), (MERC_VIDEO_BACKGROUND_Y + MERC_VIDEO_BACKGROUND_HEIGHT) );
1564 }
1565
DisableMercSiteButton()1566 void DisableMercSiteButton()
1567 {
1568 if (!g_merc_popup_box) return;
1569 guiAccountBoxButton->uiFlags |= BUTTON_FORCE_UNDIRTY;
1570 }
1571
GetAvailableRandomQuotes()1572 static std::vector<UINT8> GetAvailableRandomQuotes()
1573 {
1574 std::vector<UINT8> quotes = {};
1575 for (const MERCListingModel* m : GCM->getMERCListings())
1576 {
1577 if (!IsMercMercAvailable(GetProfileIDFromMERCListing(m))) continue;
1578 for (auto q : m->getQuotesByType(SpeckQuoteType::ADVERTISE))
1579 {
1580 quotes.push_back(q->quoteID);
1581 }
1582 }
1583
1584 // A.I.M. slanders are always available
1585 quotes.push_back(SPECK_QUOTE_PLAYER_NOT_DOING_ANYTHING_AIM_SLANDER_1);
1586 quotes.push_back(SPECK_QUOTE_PLAYER_NOT_DOING_ANYTHING_AIM_SLANDER_2);
1587 quotes.push_back(SPECK_QUOTE_PLAYER_NOT_DOING_ANYTHING_AIM_SLANDER_3);
1588 quotes.push_back(SPECK_QUOTE_PLAYER_NOT_DOING_ANYTHING_AIM_SLANDER_4);
1589
1590 return quotes;
1591 }
1592
1593
MakeBiffAwayForCoupleOfDays(void)1594 static void MakeBiffAwayForCoupleOfDays(void)
1595 {
1596 gMercProfiles[ BIFF ].uiDayBecomesAvailable = Random( 2 ) + 2;
1597 }
1598
1599
1600 // used in determining Speck's welcome quote
AreAnyOfTheNewMercsAvailable(void)1601 static BOOLEAN AreAnyOfTheNewMercsAvailable(void)
1602 {
1603 if( LaptopSaveInfo.fNewMercsAvailableAtMercSite )
1604 return( FALSE );
1605
1606 for (const MERCListingModel* m : GCM->getMERCListings())
1607 {
1608 if (m->isAvailableAtStart()) continue;
1609
1610 ProfileID ubMercID = GetProfileIDFromMERCListing(m);
1611 if( IsMercMercAvailable( ubMercID ) )
1612 return( TRUE );
1613 }
1614
1615 return( FALSE );
1616 }
1617
1618 // Check if any new merc is about to become available
ScheduleNewMercsToBeAvailable(void)1619 static void ScheduleNewMercsToBeAvailable(void)
1620 {
1621 BOOLEAN fNewMercAreAvailable = FALSE;
1622 for (const MERCListingModel* m : GCM->getMERCListings())
1623 {
1624 if (CanMercBeAvailableYet(m)) fNewMercAreAvailable = TRUE;
1625 }
1626
1627 //if there is a new merc available
1628 if( fNewMercAreAvailable )
1629 {
1630 //Set up an event to add the merc in x days
1631 AddStrategicEvent( EVENT_MERC_SITE_NEW_MERC_AVAILABLE, GetMidnightOfFutureDayInMinutes( 1 ) + 420 + Random( 3 * 60 ), 0 );
1632 }
1633 }
1634
1635 // Checks if the merc is not yet available for hire, but the conditions for availablilty is already met
CanMercBeAvailableYet(const MERCListingModel * merc)1636 static BOOLEAN CanMercBeAvailableYet(const MERCListingModel* merc)
1637 {
1638 //if the merc is already available
1639 if (merc->index <= LaptopSaveInfo.gubLastMercIndex)
1640 {
1641 return( FALSE );
1642 }
1643
1644 //if the merc is already hired
1645 ProfileID profileID = GetProfileIDFromMERCListing(merc);
1646 if (!IsMercHireable(GetProfile(profileID)))
1647 return( FALSE );
1648
1649 //if player has paid enough money for the merc to be available, and the it is after the current day
1650 if (merc->minTotalSpending <= LaptopSaveInfo.uiTotalMoneyPaidToSpeck
1651 && merc->minDays <= GetWorldDay())
1652 {
1653 return( TRUE );
1654 }
1655
1656 return( FALSE );
1657 }
1658
1659 // update the value of ubLastMercAvailableId for save game compatability
SetLastMercArrival(const MERCListingModel * merc)1660 static void SetLastMercArrival(const MERCListingModel* merc)
1661 {
1662 switch (merc->profileID)
1663 {
1664 case BUBBA: LaptopSaveInfo.ubLastMercAvailableId = MERC_ARRIVES_BUBBA; break;
1665 case COUGAR: LaptopSaveInfo.ubLastMercAvailableId = MERC_ARRIVES_COUGAR; break;
1666 case NUMB: LaptopSaveInfo.ubLastMercAvailableId = MERC_ARRIVES_NUMB; break;
1667 case LARRY_NORMAL: LaptopSaveInfo.ubLastMercAvailableId = MERC_ARRIVES_LARRY; break;
1668 }
1669 }
1670
1671 // sync last available M.E.R.C. listing from ubLastMercAvailableId for vanilla save compatability
SyncLastMercFromSaveGame()1672 void SyncLastMercFromSaveGame()
1673 {
1674 // This allows us to load saves from old versions and vanilla
1675 //
1676 // In the old implementation, the M.E.R.C. list has 2 LARRY profiles (NORMAL and DRUNK), so
1677 // the index is off by 1 for mercs after LARRY
1678 //
1679 // But this only accounts version upgrade. If you are downgrading, you may need to wait for
1680 // a few days before the last merc becomes available again.
1681 int iLastMercID = -1;
1682 switch (LaptopSaveInfo.ubLastMercAvailableId)
1683 {
1684 case MERC_ARRIVES_NUMB: iLastMercID = NUMB; break;
1685 case MERC_ARRIVES_COUGAR: iLastMercID = COUGAR; break;
1686 }
1687
1688 if (iLastMercID <= 0) return;
1689
1690 for (const MERCListingModel* merc : GCM->getMERCListings())
1691 {
1692 // check for exactly the case that we are off by 1 due to LARRY
1693 if (merc->profileID == (UINT8)iLastMercID &&
1694 (LaptopSaveInfo.gubLastMercIndex - merc->index) == 1)
1695 {
1696 // adjust gubLastMercIndex to match the new listing
1697 LaptopSaveInfo.gubLastMercIndex -= 1;
1698 break;
1699 }
1700 }
1701 }
1702
1703 // make all can-be-available mercs available immediately
MakeNewMercsAvailable(BOOLEAN fShouldNotifyPlayer)1704 static void MakeNewMercsAvailable(BOOLEAN fShouldNotifyPlayer)
1705 {
1706 BOOLEAN fNewMercAvailable = FALSE;
1707 for (const MERCListingModel* merc : GCM->getMERCListings())
1708 {
1709 if (CanMercBeAvailableYet(merc) && LaptopSaveInfo.gubLastMercIndex < merc->index)
1710 {
1711 LaptopSaveInfo.gubLastMercIndex = merc->index;
1712 SetLastMercArrival(merc);
1713 fNewMercAvailable = TRUE;
1714 }
1715 }
1716
1717 if (fNewMercAvailable && fShouldNotifyPlayer)
1718 {
1719 AddEmail(NEW_MERCS_AT_MERC, NEW_MERCS_AT_MERC_LENGTH, SPECK_FROM_MERC, GetWorldTotalMin());
1720
1721 //new mercs are available
1722 LaptopSaveInfo.fNewMercsAvailableAtMercSite = TRUE;
1723 }
1724 }
1725
NewMercsAvailableAtMercSiteCallBack()1726 void NewMercsAvailableAtMercSiteCallBack()
1727 {
1728 MakeNewMercsAvailable(TRUE);
1729 }
1730
1731 //used for older saves
CalcAproximateAmountPaidToSpeck()1732 void CalcAproximateAmountPaidToSpeck()
1733 {
1734 //loop through all the mercs and tally up the amount speck should have been paid
1735 for (const MERCListingModel* merc : GCM->getMERCListings())
1736 {
1737 //get the id
1738 ProfileID ubMercID = GetProfileIDFromMERCListing(merc);
1739 //increment the amount
1740 LaptopSaveInfo.uiTotalMoneyPaidToSpeck += gMercProfiles[ ubMercID ].uiTotalCostToDate;
1741 }
1742 }
1743
1744
1745 // CJC Dec 1 2002: calculate whether any MERC characters have been used at all
CalcMercDaysServed(void)1746 static UINT32 CalcMercDaysServed(void)
1747 {
1748 UINT32 uiDaysServed = 0;
1749 for (const MERCListingModel* merc : GCM->getMERCListings())
1750 {
1751 //get the id
1752 ProfileID ubMercID = GetProfileIDFromMERCListing(merc);
1753 uiDaysServed += gMercProfiles[ ubMercID ].usTotalDaysServed;
1754
1755 }
1756 return( uiDaysServed );
1757 }
1758