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