1 #include "Directories.h"
2 #include "Font.h"
3 #include "Laptop.h"
4 #include "EMail.h"
5 #include "Local.h"
6 #include "VObject.h"
7 #include "Debug.h"
8 #include "WordWrap.h"
9 #include "Render_Dirty.h"
10 #include "Cursors.h"
11 #include "Soldier_Profile.h"
12 #include "IMP_Compile_Character.h"
13 #include "IMP_Portraits.h"
14 #include "Game_Clock.h"
15 #include "Environment.h"
16 #include "AIMMembers.h"
17 #include "Random.h"
18 #include "Text.h"
19 #include "LaptopSave.h"
20 #include "Finances.h"
21 #include "Button_System.h"
22 #include "Video.h"
23 #include "VSurface.h"
24 #include "MemMan.h"
25 #include "Font_Control.h"
26 #include "UILayout.h"
27 
28 #include "ContentManager.h"
29 #include "GameInstance.h"
30 
31 #include <string_theory/format>
32 #include <string_theory/string>
33 
34 
35 #define MAX_MESSAGES_PAGE 18 // max number of messages per page
36 
37 #define VIEWER_X (155 + STD_SCREEN_X)
38 #define VIEWER_Y (70 + 21 + STD_SCREEN_Y)
39 #define MAIL_STRING_SIZE 320
40 
41 
42 enum EMailSortCriteria
43 {
44 	SENDER,
45 	RECEIVED,
46 	SUBJECT,
47 	READ
48 };
49 
50 
51 struct Page
52 {
53 	Email* Mail[MAX_MESSAGES_PAGE];
54 	Page* Next;
55 };
56 
57 
58 struct Record
59 {
60 	ST::string pRecord;
61 	Record* Next;
62 };
63 
64 
65 struct EmailPageInfoStruct
66 {
67 	Record* pFirstRecord;
68 	Record* pLastRecord;
69 	INT32 iPageNumber;
70 };
71 
72 
73 Email* pEmailList;
74 static Page* pPageList;
75 static INT32 iLastPage=-1;
76 static INT32 iCurrentPage=0;
77 Email* MailToDelete;
78 BOOLEAN fUnReadMailFlag=FALSE;
79 BOOLEAN fNewMailFlag=FALSE;
80 BOOLEAN fOldNewMailFlag=FALSE;
81 BOOLEAN fDisplayMessageFlag=FALSE;
82 static BOOLEAN fOldDisplayMessageFlag = FALSE;
83 static BOOLEAN fReDrawMessageFlag = FALSE;
84 BOOLEAN fOpenMostRecentUnReadFlag = FALSE;
85 static INT32 iViewerPositionY = 0;
86 
87 static Email* CurrentMail;
88 static Email* PreviousMail;
89 static INT32 giMessagePage = -1;
90 static INT32 giNumberOfPagesToCurrentEmail = -1;
91 SGPVObject* guiEmailWarning;
92 
93 #define EMAIL_TOP_BAR_HEIGHT 22
94 
95 #define MIDDLE_X 0+LAPTOP_SCREEN_UL_X
96 #define MIDDLE_Y (72 + EMAIL_TOP_BAR_HEIGHT + STD_SCREEN_Y)
97 #define MIDDLE_WIDTH 19
98 
99 
100 // new graphics
101 #define EMAIL_LIST_WINDOW_Y 22
102 
103 // email columns
104 #define SENDER_X LAPTOP_SCREEN_UL_X+65
105 
106 #define DATE_X LAPTOP_SCREEN_UL_X+428
107 
108 #define SUBJECT_X LAPTOP_SCREEN_UL_X+175
109 #define SUBJECT_WIDTH					254	//526-245
110 #define INDIC_X (128 + STD_SCREEN_X)
111 
112 #define LINE_WIDTH 592-121
113 
114 #define MESSAGE_X 5 //17
115 #define MESSAGE_WIDTH 528-125//150
116 #define MESSAGE_COLOR FONT_BLACK
117 #define MESSAGE_GAP 2
118 
119 
120 #define MESSAGE_HEADER_WIDTH 209-151
121 #define MESSAGE_HEADER_X VIEWER_X+4
122 
123 
124 #define EMAIL_WARNING_X (210 + STD_SCREEN_X)
125 #define EMAIL_WARNING_Y (140 + STD_SCREEN_Y)
126 #define EMAIL_WARNING_WIDTH 254
127 #define EMAIL_WARNING_HEIGHT 138
128 
129 
130 #define NEW_BTN_X EMAIL_WARNING_X +(338-245)
131 #define NEW_BTN_Y EMAIL_WARNING_Y +(278-195)
132 
133 #define EMAIL_TEXT_FONT			FONT10ARIAL
134 #define MESSAGE_FONT			EMAIL_TEXT_FONT
135 #define EMAIL_HEADER_FONT		FONT14ARIAL
136 #define EMAIL_WARNING_FONT		FONT12ARIAL
137 
138 
139 // the max number of pages to an email
140 #define MAX_NUMBER_EMAIL_PAGES 100
141 
142 #define NEXT_PAGE_X LAPTOP_UL_X + 562
143 #define NEXT_PAGE_Y (51 + STD_SCREEN_Y)
144 
145 #define PREVIOUS_PAGE_X NEXT_PAGE_X - 21
146 
147 #define ENVELOPE_BOX_X (116 + STD_SCREEN_X)
148 
149 #define FROM_BOX_X (166 + STD_SCREEN_X)
150 
151 #define SUBJECT_BOX_X (276 + STD_SCREEN_X)
152 
153 #define DATE_BOX_X (530 + STD_SCREEN_X)
154 
155 #define FROM_BOX_Y (51 + EMAIL_TOP_BAR_HEIGHT + STD_SCREEN_Y)
156 
157 #define EMAIL_TITLE_FONT FONT14ARIAL
158 #define EMAIL_TITLE_X (140 + STD_SCREEN_X)
159 #define EMAIL_TITLE_Y (33 + STD_SCREEN_Y)
160 #define VIEWER_MESSAGE_BODY_START_Y VIEWER_Y+72
161 #define MIN_MESSAGE_HEIGHT_IN_LINES 5
162 
163 
164 #define INDENT_Y_OFFSET 310
165 #define INDENT_X_OFFSET 325
166 #define INDENT_X_WIDTH ( 544 - 481 )
167 
168 // the position of the page number being displayed in the email program
169 #define PAGE_NUMBER_X (516 + STD_SCREEN_X)
170 #define PAGE_NUMBER_Y (58 + STD_SCREEN_Y)
171 
172 // defines for location of message 'title'/'headers'
173 
174 #define MESSAGE_FROM_Y VIEWER_Y+28
175 
176 #define MESSAGE_DATE_Y MESSAGE_FROM_Y
177 
178 #define MESSAGE_SUBJECT_Y MESSAGE_DATE_Y+16
179 
180 
181 #define SUBJECT_LINE_X VIEWER_X+47
182 #define SUBJECT_LINE_Y VIEWER_Y+42
183 #define SUBJECT_LINE_WIDTH 278-47
184 
185 
186 // maximum size of a email message page, so not to overrun the bottom of the screen
187 #define MAX_EMAIL_MESSAGE_PAGE_SIZE ( GetFontHeight( MESSAGE_FONT ) + MESSAGE_GAP ) * 20
188 
189 
190 // X button position
191 #define BUTTON_X VIEWER_X + 396
192 #define BUTTON_Y VIEWER_Y + 3 // was + 25
193 #define BUTTON_LOWER_Y BUTTON_Y + 22
194 #define PREVIOUS_PAGE_BUTTON_X VIEWER_X + 302
195 #define NEXT_PAGE_BUTTON_X VIEWER_X +395
196 #define DELETE_BUTTON_X NEXT_PAGE_BUTTON_X
197 #define LOWER_BUTTON_Y BUTTON_Y + 299
198 
199 
200 static BOOLEAN fSortDateUpwards = FALSE;
201 static BOOLEAN fSortSenderUpwards = FALSE;
202 static BOOLEAN fSortSubjectUpwards = FALSE;
203 static BOOLEAN gfPageButtonsWereCreated = FALSE;
204 
205 // mouse regions
206 static MOUSE_REGION pEmailRegions[MAX_MESSAGES_PAGE];
207 static MOUSE_REGION pScreenMask;
208 static MOUSE_REGION pDeleteScreenMask;
209 
210 // the email info struct to speed up email
211 static EmailPageInfoStruct pEmailPageInfo[MAX_NUMBER_EMAIL_PAGES];
212 
213 //buttons
214 static GUIButtonRef giMessageButton;
215 static GUIButtonRef giDeleteMailButton[2];
216 static GUIButtonRef giSortButton[4];
217 static GUIButtonRef giNewMailButton;
218 static GUIButtonRef giMailMessageButtons[3];
219 static GUIButtonRef giMailPageButtons[2];
220 static MOUSE_REGION g_mail_scroll_region;
221 
222 
223 // the message record list, for the currently displayed message
224 static Record* pMessageRecordList = NULL;
225 
226 // video handles
227 static SGPVObject* guiEmailTitle;
228 static SGPVObject* guiEmailBackground;
229 static SGPVObject* guiEmailIndicator;
230 static SGPVObject* guiEmailMessage;
231 static SGPVObject* guiMAILDIVIDER;
232 
233 
234 // the enumeration of headers
235 enum{
236 	FROM_HEADER=0,
237 	SUBJECT_HEADER,
238 	RECD_HEADER,
239 };
240 
241 
242 // whther or not we need to redraw the new mail box
243 BOOLEAN fReDrawNewMailFlag = FALSE;
244 static INT32 iTotalHeight = 0;
245 
246 
247 static void CreateNextPreviousEmailPageButtons(void);
248 static void EmailBtnCallBack(MOUSE_REGION* pRegion, INT32 iReason);
249 
250 
InitializeMouseRegions(void)251 static void InitializeMouseRegions(void)
252 {
253 	// init mouseregions
254 	for (INT32 i = 0; i < MAX_MESSAGES_PAGE; ++i)
255 	{
256 		const UINT16 x = MIDDLE_X;
257 		const UINT16 y = MIDDLE_Y + MIDDLE_WIDTH * i;
258 		const UINT16 w = LINE_WIDTH;
259 		const UINT16 h = MIDDLE_WIDTH;
260 		MOUSE_REGION* const r = &pEmailRegions[i];
261 		MSYS_DefineRegion(r, x, y, x + w, y + h, MSYS_PRIORITY_NORMAL + 2, MSYS_NO_CURSOR, NULL, EmailBtnCallBack);
262 		MSYS_SetRegionUserData(r, 0, i);
263 	}
264 
265 	CreateNextPreviousEmailPageButtons();
266 }
267 
268 
DeleteEmailMouseRegions()269 static void DeleteEmailMouseRegions()
270 {
271 	FOR_EACH(MOUSE_REGION, i, pEmailRegions)     MSYS_RemoveRegion(&*i);
272 	FOR_EACH(GUIButtonRef, i, giMailPageButtons) RemoveButton(*i);
273 }
274 
275 
GameInitEmail()276 void GameInitEmail()
277 {
278 	pEmailList=NULL;
279 	pPageList=NULL;
280 
281 	iLastPage=-1;
282 
283 	iCurrentPage=0;
284 	MailToDelete = NULL;
285 
286 	// reset display message flag
287 	fDisplayMessageFlag=FALSE;
288 
289 	// reset page being displayed
290 	giMessagePage = 0;
291 }
292 
293 
294 static void CreateMailScreenButtons(void);
295 
296 
EnterEmail()297 void EnterEmail()
298 {
299 	// load graphics
300 
301 	iCurrentPage = LaptopSaveInfo.iCurrentEmailPage;
302 
303 	// title bar
304 	guiEmailTitle = AddVideoObjectFromFile(LAPTOPDIR "/programtitlebar.sti");
305 
306 	// the list background
307 	guiEmailBackground = AddVideoObjectFromFile(LAPTOPDIR "/mailwindow.sti");
308 
309 	// the indication/notification box
310 	guiEmailIndicator = AddVideoObjectFromFile(LAPTOPDIR "/mailindicator.sti");
311 
312 	// the message background
313 	guiEmailMessage = AddVideoObjectFromFile(LAPTOPDIR "/emailviewer.sti");
314 
315 	// the message background
316 	guiMAILDIVIDER = AddVideoObjectFromFile(LAPTOPDIR "/maillistdivider.sti");
317 
318 	// initialize mouse regions
319 	InitializeMouseRegions();
320 
321 	// create buttons
322 	CreateMailScreenButtons( );
323 
324 	// marks these buttons dirty
325 	MarkButtonsDirty( );
326 
327 	// reset current page of the message being displayed
328 	giMessagePage = 0;
329 
330 	// render email background and text
331 	RenderEmail();
332 }
333 
334 
335 static void AddDeleteRegionsToMessageRegion(INT32 iViewerY);
336 static void ClearOutEmailMessageRecordsList(void);
337 static void DestroyMailScreenButtons(void);
338 
339 
ExitEmail()340 void ExitEmail()
341 {
342 	LaptopSaveInfo.iCurrentEmailPage = iCurrentPage;
343 
344 	// clear out message record list
345 	ClearOutEmailMessageRecordsList( );
346 
347 	// displayed message?...get rid of it
348 	if(fDisplayMessageFlag)
349 	{
350 		fDisplayMessageFlag = FALSE;
351 		AddDeleteRegionsToMessageRegion( 0 );
352 		fDisplayMessageFlag = TRUE;
353 		fReDrawMessageFlag = TRUE;
354 	}
355 	else
356 	{
357 		CurrentMail = NULL;
358 	}
359 
360 	// delete mail notice?...get rid of it
361 	if (MailToDelete != NULL)
362 	{
363 		MailToDelete = NULL;
364 		CreateDestroyDeleteNoticeMailButton();
365 	}
366 
367 	// remove all mouse regions in use in email
368 	DeleteEmailMouseRegions();
369 
370 	// remove video objects being used by email screen
371 	DeleteVideoObject(guiEmailTitle);
372 	DeleteVideoObject(guiEmailBackground);
373 	DeleteVideoObject(guiMAILDIVIDER);
374 	DeleteVideoObject(guiEmailIndicator);
375 	DeleteVideoObject(guiEmailMessage);
376 
377 	// remove buttons
378 	DestroyMailScreenButtons( );
379 }
380 
381 
382 static BOOLEAN DisplayDeleteNotice(Email* pMail);
383 static void DisplayEmailList(void);
384 static INT32 DisplayEmailMessage(Email* pMail);
385 static void HandleEmailViewerButtonStates(void);
386 static void OpenMostRecentUnreadEmail(void);
387 static void UpDateMessageRecordList(void);
388 static void UpdateStatusOfNextPreviousButtons(void);
389 
390 
HandleEmail(void)391 void HandleEmail( void )
392 {
393 
394 	INT32 iViewerY = 0;
395 	static BOOLEAN fEmailListBeenDrawAlready = FALSE;
396 
397 
398 	// check if email message record list needs to be updated
399 	UpDateMessageRecordList( );
400 
401 	// does email list need to be draw, or can be drawn
402 	if (!fDisplayMessageFlag && !fNewMailFlag && MailToDelete == NULL && !fEmailListBeenDrawAlready)
403 	{
404 		DisplayEmailList();
405 		fEmailListBeenDrawAlready = TRUE;
406 	}
407 	// if the message flag, show message
408 	else if((fDisplayMessageFlag)&&(fReDrawMessageFlag))
409 	{
410 		// redisplay list
411 		DisplayEmailList();
412 
413 		// this simply redraws message without button manipulation
414 		iViewerY = DisplayEmailMessage(CurrentMail);
415 		fEmailListBeenDrawAlready = FALSE;
416 
417 	}
418 	else if((fDisplayMessageFlag)&&(!fOldDisplayMessageFlag))
419 	{
420 
421 		// redisplay list
422 		DisplayEmailList();
423 
424 		// this simply redraws message with button manipulation
425 		iViewerY = DisplayEmailMessage(CurrentMail);
426 		AddDeleteRegionsToMessageRegion( iViewerY );
427 		fEmailListBeenDrawAlready = FALSE;
428 
429 	}
430 
431 	// not displaying anymore?
432 	if (!fDisplayMessageFlag && fOldDisplayMessageFlag)
433 	{
434 		// then clear it out
435 		ClearOutEmailMessageRecordsList( );
436 	}
437 
438 
439 	// if new message is being displayed...check to see if it's buttons need to be created or destroyed
440 	AddDeleteRegionsToMessageRegion( 0 );
441 
442 	// same with delete notice
443 	CreateDestroyDeleteNoticeMailButton();
444 
445 	// if delete notice needs to be displayed?...display it
446 	if (MailToDelete != NULL) DisplayDeleteNotice(MailToDelete);
447 
448 
449 	// update buttons
450 	HandleEmailViewerButtonStates( );
451 
452 	// handle buttons states
453 	UpdateStatusOfNextPreviousButtons( );
454 
455 	if (fOpenMostRecentUnReadFlag)
456 	{
457 		// enter email due to email icon on program panel
458 		OpenMostRecentUnreadEmail( );
459 		fOpenMostRecentUnReadFlag = FALSE;
460 
461 	}
462 }
463 
464 
465 static void DisplayTextOnTitleBar(void);
466 static void DisplayWhichPageOfEmailProgramIsDisplayed(void);
467 static void DrawLineDividers(void);
468 static void ReDisplayBoxes(void);
469 
470 
RenderEmail(void)471 void RenderEmail( void )
472 {
473 	BltVideoObject(FRAME_BUFFER, guiEmailBackground, 0, LAPTOP_SCREEN_UL_X, EMAIL_LIST_WINDOW_Y + LAPTOP_SCREEN_UL_Y);
474 	BltVideoObject(FRAME_BUFFER, guiEmailTitle,      0, LAPTOP_SCREEN_UL_X, LAPTOP_SCREEN_UL_Y - 2);
475 
476 	// show text on titlebar
477 	DisplayTextOnTitleBar( );
478 
479 	DisplayEmailList( );
480 
481 	// redraw line dividers
482 	DrawLineDividers( );
483 
484 	BltVideoObject(FRAME_BUFFER, guiLaptopBACKGROUND, 0, STD_SCREEN_X + 108, STD_SCREEN_Y + 23);
485 
486 	ReDisplayBoxes( );
487 
488 	BlitTitleBarIcons(  );
489 
490 	// show which page we are on
491 	DisplayWhichPageOfEmailProgramIsDisplayed( );
492 
493 	InvalidateScreen();
494 }
495 
496 
AddEmailWithSpecialData(INT32 iMessageOffset,INT32 iMessageLength,UINT8 ubSender,INT32 iDate,INT32 iFirstData,UINT32 uiSecondData)497 void AddEmailWithSpecialData(INT32 iMessageOffset, INT32 iMessageLength, UINT8 ubSender, INT32 iDate, INT32 iFirstData, UINT32 uiSecondData )
498 {
499 	AddEmailMessage(iMessageOffset, iMessageLength, iDate, ubSender, FALSE, iFirstData, uiSecondData);
500 }
501 
502 
AddEmail(INT32 iMessageOffset,INT32 iMessageLength,UINT8 ubSender,INT32 iDate)503 void AddEmail(INT32 iMessageOffset, INT32 iMessageLength, UINT8 ubSender, INT32 iDate)
504 {
505 	AddEmailMessage(iMessageOffset, iMessageLength, iDate, ubSender, FALSE, 0, 0);
506 }
507 
508 
AddPreReadEmail(INT32 iMessageOffset,INT32 iMessageLength,UINT8 ubSender,INT32 iDate)509 void AddPreReadEmail(INT32 iMessageOffset, INT32 iMessageLength, UINT8 ubSender, INT32 iDate)
510 {
511 	AddEmailMessage(iMessageOffset, iMessageLength, iDate, ubSender, TRUE, 0, 0);
512 }
513 
514 
LoadEMailText(UINT32 entry)515 static ST::string LoadEMailText(UINT32 entry)
516 {
517 	return GCM->loadEncryptedString(BINARYDATADIR "/email.edt", MAIL_STRING_SIZE * entry, MAIL_STRING_SIZE);
518 }
519 
520 
521 static void AddMessageToPages(Email* Mail);
522 static ST::string ReplaceMercNameAndAmountWithProperData(const ST::string& pFinishedString, const Email* pMail);
523 
524 
AddEmailMessage(INT32 iMessageOffset,INT32 iMessageLength,INT32 iDate,UINT8 ubSender,BOOLEAN fAlreadyRead,INT32 iFirstData,UINT32 uiSecondData)525 void AddEmailMessage(INT32 iMessageOffset, INT32 iMessageLength, INT32 iDate, UINT8 ubSender, BOOLEAN fAlreadyRead, INT32 iFirstData, UINT32 uiSecondData)
526 {
527 	// will add a message to the list of messages
528 
529 	// add new element onto list
530 	Email* const pTempEmail = new Email{};
531 
532 	// copy offset and length of the actual message in email.edt
533 	pTempEmail->usOffset =(UINT16)iMessageOffset;
534 	pTempEmail->usLength =(UINT16)iMessageLength;
535 
536 	// copy date and sender id's
537 	pTempEmail->iDate=iDate;
538 	pTempEmail->ubSender=ubSender;
539 
540 	// the special data
541 	pTempEmail->iFirstData = iFirstData;
542 	pTempEmail->uiSecondData = uiSecondData;
543 
544 	ST::string pSubject = LoadEMailText(iMessageOffset);
545 	pSubject = ReplaceMercNameAndAmountWithProperData(pSubject, pTempEmail);
546 	pTempEmail->pSubject = ST::format(" {}", pSubject);
547 
548 	// place into list
549 	Email* pEmail = pEmailList;
550 	if(pEmail)
551 	{
552 		// list exists, place at end
553 		while (pEmail->Next != NULL) pEmail = pEmail->Next;
554 		pEmail->Next = pTempEmail;
555 	}
556 	else
557 	{
558 		// no list, becomes head of a new list
559 		pEmailList = pTempEmail;
560 	}
561 	pTempEmail->Prev = pEmail;
562 
563 	// reset Next ptr
564 	pTempEmail->Next=NULL;
565 
566 	// set flag that new mail has arrived
567 	fNewMailFlag=TRUE;
568 
569 	// add this message to the pages of email
570 	AddMessageToPages(pTempEmail);
571 
572 	// reset read flag of this particular message
573 	pTempEmail->fRead=fAlreadyRead;
574 }
575 
576 
RemoveEmailMessage(Email * Mail)577 static void RemoveEmailMessage(Email* Mail)
578 {
579 	Email* Next = Mail->Next;
580 	Email* Prev = Mail->Prev;
581 	if (Next != NULL) Next->Prev = Prev;
582 	if (Prev != NULL)
583 	{
584 		Prev->Next = Next;
585 	}
586 	else
587 	{
588 		Assert(pEmailList == Mail);
589 		pEmailList = Next;
590 	}
591 	delete Mail;
592 }
593 
594 
AddEmailPage(void)595 static void AddEmailPage(void)
596 {
597 	Page* const p = new Page{};
598 	FOR_EACH(Email*, i, p->Mail) *i = 0;
599 	p->Next = NULL;
600 
601 	if (pPageList)
602 	{
603 		Page* last = pPageList;
604 		while (last->Next) last = last->Next;
605 		last->Next = p;
606 	}
607 	else
608 	{
609 		pPageList = p;
610 	}
611 
612 	iLastPage++;
613 }
614 
615 
AddMessageToPages(Email * Mail)616 static void AddMessageToPages(Email* Mail)
617 {
618 	// go to end of page list
619 	Page* pPage = pPageList;
620 	INT32 iCounter=0;
621 	if(!pPage)
622 		AddEmailPage();
623 	pPage=pPageList;
624 	while (pPage->Next != NULL && pPage->Mail[MAX_MESSAGES_PAGE - 1] != NULL)
625 		pPage=pPage->Next;
626 	// if list is full, add new page
627 	while(iCounter <MAX_MESSAGES_PAGE)
628 	{
629 		if (pPage->Mail[iCounter] == NULL) break;
630 		iCounter++;
631 	}
632 	if(iCounter==MAX_MESSAGES_PAGE)
633 	{
634 		AddEmailPage();
635 		AddMessageToPages(Mail);
636 	}
637 	else
638 	{
639 		pPage->Mail[iCounter] = Mail;
640 	}
641 }
642 
643 
SortMessages(EMailSortCriteria Criterium)644 static void SortMessages(EMailSortCriteria Criterium)
645 {
646 	Email* NewList = NULL;
647 
648 	for (Email* List = pEmailList; List != NULL;)
649 	{
650 		Email* Mail = List;
651 		List = List->Next;
652 
653 		Email* InsAfter = NULL;
654 		for (Email* Other = NewList; Other != NULL; Other = Other->Next)
655 		{
656 			INT Order; // XXX HACK000E
657 			switch (Criterium)
658 			{
659 				case RECEIVED:
660 					Order = Mail->iDate - Other->iDate;
661 					if (fSortDateUpwards) Order = -Order;
662 					break;
663 
664 				case SENDER:
665 					Order = pSenderNameList[Mail->ubSender].compare(pSenderNameList[Other->ubSender]);
666 					if (fSortSenderUpwards) Order = -Order;
667 					break;
668 
669 				case SUBJECT:
670 					Order = Mail->pSubject.compare(Other->pSubject);
671 					if (fSortSubjectUpwards) Order = -Order;
672 					break;
673 
674 				case READ:
675 					Order = Other->fRead - Mail->fRead;
676 					break;
677 
678 				default: abort(); // HACK000E
679 			}
680 			if (Order > 0) break;
681 			InsAfter = Other;
682 		}
683 		Mail->Prev = InsAfter;
684 		if (InsAfter == NULL)
685 		{
686 			Mail->Next = NewList;
687 			NewList = Mail;
688 		}
689 		else
690 		{
691 			Mail->Next = InsAfter->Next;
692 			InsAfter->Next = Mail;
693 		}
694 		if (Mail->Next != NULL) Mail->Next->Prev = Mail;
695 	}
696 	pEmailList = NewList;
697 
698 	fReDrawScreenFlag = TRUE;
699 }
700 
701 
ClearPages(void)702 static void ClearPages(void)
703 {
704 	// run through list of message pages and set to -1
705 	for (Page* i = pPageList; i;)
706 	{
707 		Page* const next = i->Next;
708 		delete i;
709 		i = next;
710 	}
711 
712 	pPageList = NULL;
713 	iLastPage = -1;
714 }
715 
716 
PlaceMessagesinPages(void)717 static void PlaceMessagesinPages(void)
718 {
719 	Email* pEmail = pEmailList;
720 	// run through the list of messages and add to pages
721 	ClearPages();
722 	while(pEmail)
723 	{
724 		AddMessageToPages(pEmail);
725 		pEmail=pEmail->Next;
726 
727 	}
728 	if(iCurrentPage >iLastPage)
729 		iCurrentPage=iLastPage;
730 }
731 
732 
733 // Draw the icon, sender, date, subject
DrawEmailSummary(INT32 y,const Email * e)734 static void DrawEmailSummary(INT32 y, const Email* e)
735 {
736 	const BOOLEAN read = e->fRead;
737 	const SGPFont font = read ? MESSAGE_FONT : FONT10ARIALBOLD;
738 
739 	// will draw the icon for letter in mail list depending if the mail has been read or not
740 	BltVideoObject(FRAME_BUFFER, guiEmailIndicator, read ? 0 : 1, INDIC_X, y + 2);
741 
742 	SetFont(font);
743 
744 	ST::string pTempSubject = e->pSubject;
745 	pTempSubject = ReduceStringLength(pTempSubject, SUBJECT_WIDTH - 10, font);
746 	MPrint(SUBJECT_X, y + 4, pTempSubject);
747 	MPrint(SENDER_X,  y + 4, pSenderNameList[e->ubSender]);
748 
749 	// draw date of message being displayed in mail viewer
750 	MPrint(DATE_X, y + 4, ST::format("{} {}", pDayStrings, e->iDate / (24 * 60)));
751 }
752 
753 
GetCurrentPage(void)754 static Page* GetCurrentPage(void)
755 {
756 	Page* i = pPageList;
757 	if (i == NULL) return i;
758 
759 	INT32 PageID = 0;
760 	while (i->Next != NULL && PageID++ != iCurrentPage) i = i->Next;
761 	return i;
762 }
763 
764 
DisplayEmailList(void)765 static void DisplayEmailList(void)
766 {
767 	// look at current page, and display
768 
769 	// if current page ever ends up negative, reset to 0
770 	if (iCurrentPage == -1) iCurrentPage = 0;
771 
772 	const Page* const p = GetCurrentPage();
773 	if (p == NULL) return;
774 
775 	// now we have current page, display it
776 	SetFontForeground(FONT_BLACK);
777 	SetFontBackground(FONT_BLACK);
778 	SetFontShadow(NO_SHADOW);
779 
780 	// draw each line of the list for this page
781 	INT32 y = MIDDLE_Y;
782 	FOR_EACH(Email* const, e, p->Mail)
783 	{
784 		if (!*e) break;
785 		DrawEmailSummary(y, *e);
786 		y += MIDDLE_WIDTH;
787 	}
788 
789 	InvalidateRegion(LAPTOP_SCREEN_UL_X,LAPTOP_SCREEN_UL_Y,LAPTOP_SCREEN_LR_X,LAPTOP_SCREEN_LR_Y);
790 
791 	SetFontShadow(DEFAULT_SHADOW);
792 }
793 
794 
LookForUnread()795 void LookForUnread()
796 {
797 	BOOLEAN fStatusOfNewEmailFlag = fUnReadMailFlag;
798 
799 	// simply runrs through list of messages, if any unread, set unread flag
800 
801 	Email* pA = pEmailList;
802 
803 	// reset unread flag
804 	fUnReadMailFlag=FALSE;
805 
806 	// look for unread mail
807 	while(pA)
808 	{
809 		// unread mail found, set flag
810 		if(!(pA->fRead))
811 			fUnReadMailFlag=TRUE;
812 		pA=pA->Next;
813 	}
814 
815 	if( fStatusOfNewEmailFlag != fUnReadMailFlag )
816 	{
817 		//Since there is no new email, get rid of the hepl text
818 		CreateFileAndNewEmailIconFastHelpText( LAPTOP_BN_HLP_TXT_YOU_HAVE_NEW_MAIL, (BOOLEAN )!fUnReadMailFlag );
819 	}
820 }
821 
822 
PrevListPage()823 static void PrevListPage()
824 {
825 	if (iCurrentPage == 0) return;
826 	--iCurrentPage;
827 	RenderEmail();
828 	MarkButtonsDirty();
829 }
830 
831 
NextListPage()832 static void NextListPage()
833 {
834 	if (iCurrentPage == iLastPage) return;
835 	++iCurrentPage;
836 	RenderEmail();
837 	MarkButtonsDirty();
838 }
839 
840 
EmailBtnCallBack(MOUSE_REGION * pRegion,INT32 iReason)841 static void EmailBtnCallBack(MOUSE_REGION* pRegion, INT32 iReason)
842 {
843 	INT32 iCount;
844 	if(fDisplayMessageFlag)
845 		return;
846 	if(iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
847 	{
848 		Page* pPage = GetCurrentPage();
849 		if (pPage == NULL) return;
850 
851 		// error check
852 		iCount=MSYS_GetRegionUserData(pRegion, 0);
853 
854 		Email* Mail = pPage->Mail[iCount];
855 
856 		// invalid message
857 		if (Mail == NULL)
858 		{
859 			fDisplayMessageFlag=FALSE;
860 			return;
861 		}
862 		// Get email and display
863 		fDisplayMessageFlag=TRUE;
864 		giMessagePage = 0;
865 		PreviousMail = CurrentMail;
866 		CurrentMail = Mail;
867 	}
868 	else if(iReason & MSYS_CALLBACK_REASON_RBUTTON_UP)
869 	{
870 		Page* pPage = GetCurrentPage();
871 		if (pPage == NULL)
872 		{
873 			HandleRightButtonUpEvent();
874 			return;
875 		}
876 
877 		iCount=MSYS_GetRegionUserData(pRegion, 0);
878 
879 		giMessagePage = 0;
880 
881 		Email* Mail = pPage->Mail[iCount];
882 		if (Mail == NULL)
883 		{
884 			// no mail here, handle right button up event
885 			HandleRightButtonUpEvent( );
886 			return;
887 		}
888 		else
889 		{
890 			MailToDelete = Mail;
891 		}
892 	}
893 	else if (iReason & MSYS_CALLBACK_REASON_WHEEL_UP)
894 	{
895 		PrevListPage();
896 	}
897 	else if (iReason & MSYS_CALLBACK_REASON_WHEEL_DOWN)
898 	{
899 		NextListPage();
900 	}
901 }
902 
903 
BtnMessageXCallback(GUI_BUTTON * btn,INT32 reason)904 static void BtnMessageXCallback(GUI_BUTTON *btn, INT32 reason)
905 {
906 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP || reason & MSYS_CALLBACK_REASON_RBUTTON_UP)
907 	{
908 		// X button has been pressed and let up, this means to stop displaying the currently displayed message
909 
910 		// reset display message flag
911 		fDisplayMessageFlag = FALSE;
912 
913 		// reset page being displayed
914 		giMessagePage = 0;
915 
916 		// force update of entire screen
917 		fPausedReDrawScreenFlag = TRUE;
918 	}
919 }
920 
921 
GetFirstRecordOnThisPage(Record * const RecordList,INT32 const iPage)922 static Record* GetFirstRecordOnThisPage(Record* const RecordList, INT32 const iPage)
923 {
924 	// get the first record on this page - build pages up until this point
925 
926 	Record* CurrentRecord = NULL;
927 
928 	INT32 iCurrentPositionOnThisPage = 0;
929 	INT32 iCurrentPage =0;
930 
931 
932 
933 	// null record list, nothing to do
934 	if( RecordList == NULL )
935 	{
936 		return ( CurrentRecord );
937 	}
938 
939 	CurrentRecord = RecordList;
940 
941 	// while we are not on the current page
942 	SGPFont const font = MESSAGE_FONT;
943 	while( iCurrentPage < iPage )
944 	{
945 		// build record list to this point
946 		for (;;)
947 		{
948 			UINT16 const h = IanWrappedStringHeight(MESSAGE_WIDTH, MESSAGE_GAP, font, CurrentRecord->pRecord);
949 			if (iCurrentPositionOnThisPage + h > MAX_EMAIL_MESSAGE_PAGE_SIZE) break;
950 
951 			// still room on this page
952 			iCurrentPositionOnThisPage += h;
953 
954 			// next record
955 			CurrentRecord = CurrentRecord -> Next;
956 
957 			// check if we have gone too far?
958 			if( CurrentRecord == NULL )
959 			{
960 				return( CurrentRecord );
961 			}
962 		}
963 
964 		// reset position
965 		iCurrentPositionOnThisPage = 0;
966 
967 		// next page
968 		iCurrentPage++;
969 	}
970 
971 	return ( CurrentRecord );
972 }
973 
974 
975 static void DisplayEmailMessageSubjectDateFromLines(Email* pMail, INT32 iViewerY);
976 static void DisplayNumberOfPagesToThisEmail(INT32 iViewerY);
977 static void DrawEmailMessageDisplayTitleText(INT32 iViewerY);
978 static void HandleAnySpecialEmailMessageEvents(INT32 iMessageId);
979 static void HandleMailSpecialMessages(UINT16 usMessageId, Email* pMail);
980 static void PreProcessEmail(Email* pMail);
981 
982 
DisplayEmailMessage(Email * const m)983 static INT32 DisplayEmailMessage(Email* const m)
984 {
985 	if (!m) return 0;
986 
987 	// reset redraw email message flag
988 	fReDrawMessageFlag = FALSE;
989 
990 	// we KNOW the player is going to "read" this, so mark it as so
991 	m->fRead = TRUE;
992 
993 	INT32 const offset = m->usOffset;
994 	HandleAnySpecialEmailMessageEvents(offset);
995 	HandleMailSpecialMessages(offset, m);
996 
997 	PreProcessEmail(m);
998 
999 	INT32 const by = iViewerPositionY;
1000 
1001 	BltVideoObject(FRAME_BUFFER, guiEmailMessage,  0, VIEWER_X,     VIEWER_Y + by);
1002 	BltVideoObject(FRAME_BUFFER, guiTITLEBARICONS, 0, VIEWER_X + 5, VIEWER_Y + by + 2);
1003 
1004 	DisplayEmailMessageSubjectDateFromLines(m, by);
1005 	DrawEmailMessageDisplayTitleText(by);
1006 
1007 	UINT16 const h = GetFontHeight(MESSAGE_FONT);
1008 	INT32        y = VIEWER_MESSAGE_BODY_START_Y + by;
1009 
1010 	BltVideoObject(FRAME_BUFFER, guiEmailMessage, 1, VIEWER_X, y);
1011 	y += h;
1012 
1013 	// Blit the text background based on height
1014 	for (INT32 i = 1; i < iTotalHeight / h; ++i)
1015 	{
1016 		BltVideoObject(FRAME_BUFFER, guiEmailMessage, 1, VIEWER_X, y);
1017 		y += h;
1018 	}
1019 
1020 	BOOLEAN onlyOnePage = giNumberOfPagesToCurrentEmail <= 2;
1021 
1022 	// The bottom piece to the message viewer
1023 	BltVideoObject(FRAME_BUFFER, guiEmailMessage, onlyOnePage ? 2 : 3, VIEWER_X, y);
1024 
1025 	// Draw body of text. Any particular email can encompass more than one
1026 	// "record" in the email file. Draw each record (length is number of records)
1027 	if (Record const* i = pEmailPageInfo[giMessagePage].pFirstRecord)
1028 	{
1029 		for (INT32 y = VIEWER_MESSAGE_BODY_START_Y + by + h;;)
1030 		{
1031 			y += IanDisplayWrappedString(VIEWER_X + MESSAGE_X + 4, y, MESSAGE_WIDTH, MESSAGE_GAP, MESSAGE_FONT, MESSAGE_COLOR, i->pRecord, 0, IAN_WRAP_NO_SHADOW);
1032 
1033 			i = i->Next;
1034 			if (!i) break;
1035 			if ( pEmailPageInfo[giMessagePage    ].pLastRecord != i) continue;
1036 			if (!pEmailPageInfo[giMessagePage + 1].pFirstRecord)     continue;
1037 			break;
1038 		}
1039 	}
1040 
1041 	if(!onlyOnePage)
1042 	{
1043 		DisplayNumberOfPagesToThisEmail(by);
1044 	}
1045 
1046 	InvalidateRegion(LAPTOP_SCREEN_UL_X, LAPTOP_SCREEN_UL_Y, LAPTOP_SCREEN_LR_X, LAPTOP_SCREEN_LR_Y);
1047 
1048 	return by;
1049 }
1050 
1051 
BtnNewOkback(GUI_BUTTON * btn,INT32 reason)1052 static void BtnNewOkback(GUI_BUTTON *btn, INT32 reason)
1053 {
1054 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1055 	{
1056 		fNewMailFlag=FALSE;
1057 	}
1058 }
1059 
1060 
MakeButtonNewMail(INT32 image,INT16 x,INT16 y,GUI_CALLBACK click)1061 static GUIButtonRef MakeButtonNewMail(INT32 image, INT16 x, INT16 y, GUI_CALLBACK click)
1062 {
1063 	GUIButtonRef const btn = QuickCreateButtonImg(LAPTOPDIR "/newmailbuttons.sti", image, image + 3, x, y, MSYS_PRIORITY_HIGHEST - 1, click);
1064 	btn->SetCursor(CURSOR_LAPTOP_SCREEN);
1065 	return btn;
1066 }
1067 
1068 
PrevMailPage()1069 static void PrevMailPage()
1070 {
1071 	if (giMessagePage == 0) return;
1072 	--giMessagePage;
1073 	MarkButtonsDirty();
1074 	fReDrawScreenFlag = TRUE;
1075 }
1076 
1077 
NextMailPage()1078 static void NextMailPage()
1079 {
1080 	if (giMessagePage + 1 >= giNumberOfPagesToCurrentEmail - 1) return;
1081 	++giMessagePage;
1082 	MarkButtonsDirty();
1083 	fReDrawScreenFlag = TRUE;
1084 }
1085 
1086 
MailScrollRegionCallback(MOUSE_REGION * const,INT32 const reason)1087 static void MailScrollRegionCallback(MOUSE_REGION* const, INT32 const reason)
1088 {
1089 	if (reason & MSYS_CALLBACK_REASON_WHEEL_UP)
1090 	{
1091 		PrevMailPage();
1092 	}
1093 	else if (reason & MSYS_CALLBACK_REASON_WHEEL_DOWN)
1094 	{
1095 		NextMailPage();
1096 	}
1097 }
1098 
1099 
1100 static void BtnDeleteCallback(GUI_BUTTON* btn, INT32 iReason);
1101 static void BtnNextEmailPageCallback(GUI_BUTTON* btn, INT32 reason);
1102 static void BtnPreviousEmailPageCallback(GUI_BUTTON* btn, INT32 reason);
1103 
1104 
AddDeleteRegionsToMessageRegion(INT32 iViewerY)1105 static void AddDeleteRegionsToMessageRegion(INT32 iViewerY)
1106 {
1107 	// will create/destroy mouse region for message display
1108 
1109 	if((fDisplayMessageFlag)&&(!fOldDisplayMessageFlag))
1110 	{
1111 
1112 		// set old flag
1113 		fOldDisplayMessageFlag=TRUE;
1114 
1115 		// add X button
1116 		giMessageButton = QuickCreateButtonImg(LAPTOPDIR "/x.sti", 0, 1, BUTTON_X + 2, BUTTON_Y + iViewerY + 1, MSYS_PRIORITY_HIGHEST - 1, BtnMessageXCallback);
1117 		giMessageButton->SetCursor(CURSOR_LAPTOP_SCREEN);
1118 
1119 		if( giNumberOfPagesToCurrentEmail > 2 )
1120 		{
1121 			// add next and previous mail page buttons
1122 			{
1123 				INT16 const y = LOWER_BUTTON_Y + iViewerY + 2;
1124 				giMailMessageButtons[0] = MakeButtonNewMail(0, PREVIOUS_PAGE_BUTTON_X, y, BtnPreviousEmailPageCallback);
1125 				giMailMessageButtons[1] = MakeButtonNewMail(1, NEXT_PAGE_BUTTON_X,     y, BtnNextEmailPageCallback);
1126 			}
1127 			{
1128 				UINT16 const x = VIEWER_X + MESSAGE_X + 1;
1129 				UINT16 const y = VIEWER_MESSAGE_BODY_START_Y + iViewerPositionY;
1130 				UINT16 const w = MESSAGE_WIDTH + 3;
1131 				UINT16 const h = 227;
1132 				MSYS_DefineRegion(&g_mail_scroll_region, x, y, x + w, y + h, MSYS_PRIORITY_HIGHEST - 2, MSYS_NO_CURSOR, NULL, MailScrollRegionCallback);
1133 			}
1134 			gfPageButtonsWereCreated = TRUE;
1135 		}
1136 
1137 		giMailMessageButtons[2] = MakeButtonNewMail(2, DELETE_BUTTON_X, BUTTON_LOWER_Y + iViewerY + 2, BtnDeleteCallback);
1138 
1139 		// force update of screen
1140 		fReDrawScreenFlag=TRUE;
1141 	}
1142 	else if((!fDisplayMessageFlag)&&(fOldDisplayMessageFlag))
1143 	{
1144 		// delete region
1145 		fOldDisplayMessageFlag=FALSE;
1146 		RemoveButton(giMessageButton);
1147 
1148 		// net/previous email page buttons
1149 		if( gfPageButtonsWereCreated )
1150 		{
1151 			MSYS_RemoveRegion(&g_mail_scroll_region);
1152 			RemoveButton(giMailMessageButtons[0] );
1153 			RemoveButton(giMailMessageButtons[1] );
1154 			gfPageButtonsWereCreated = FALSE;
1155 		}
1156 		RemoveButton(giMailMessageButtons[2] );
1157 		// force update of screen
1158 		fReDrawScreenFlag=TRUE;
1159 	}
1160 }
1161 
1162 
MakeButtonYesNo(INT32 image,INT16 x,GUI_CALLBACK click)1163 static GUIButtonRef MakeButtonYesNo(INT32 image, INT16 x, GUI_CALLBACK click)
1164 {
1165 	GUIButtonRef const btn = QuickCreateButtonImg(LAPTOPDIR "/yesnobuttons.sti", image, image + 1, x, NEW_BTN_Y, MSYS_PRIORITY_HIGHEST - 2, click);
1166 	btn->SetCursor(CURSOR_LAPTOP_SCREEN);
1167 	return btn;
1168 }
1169 
1170 
CreateDestroyNewMailButton()1171 void CreateDestroyNewMailButton()
1172 {
1173 	static BOOLEAN fOldNewMailFlag=FALSE;
1174 
1175 	// check if we are video conferencing, if so, do nothing
1176 	if (gubVideoConferencingMode != AIM_VIDEO_NOT_DISPLAYED_MODE)
1177 	{
1178 		return ;
1179 	}
1180 
1181 
1182 	if((fNewMailFlag)&&(!fOldNewMailFlag))
1183 	{
1184 		// create new mail dialog box button
1185 
1186 		// set old flag (stating button has been created)
1187 		fOldNewMailFlag=TRUE;
1188 
1189 		giNewMailButton = MakeButtonYesNo(0, NEW_BTN_X + 10, BtnNewOkback);
1190 
1191 		// set up screen mask region
1192 		MSYS_DefineRegion(&pScreenMask, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_HIGHEST - 3, CURSOR_LAPTOP_SCREEN, MSYS_NO_CALLBACK, LapTopScreenCallBack);
1193 		MarkAButtonDirty(giNewMailButton);
1194 		fReDrawScreenFlag = TRUE;
1195 	}
1196 	else if((!fNewMailFlag)&&(fOldNewMailFlag))
1197 	{
1198 		// reset old flag
1199 		fOldNewMailFlag=FALSE;
1200 
1201 		// remove the button
1202 		RemoveButton(giNewMailButton);
1203 
1204 		// remove screen mask
1205 		MSYS_RemoveRegion( &pScreenMask );
1206 
1207 		// redraw screen
1208 		fPausedReDrawScreenFlag=TRUE;
1209 	}
1210 }
1211 
1212 
DisplayNewMailBox(void)1213 void DisplayNewMailBox(void)
1214 {
1215 	// will display a new mail box whenever new mail has arrived
1216 
1217 	// check if we are video conferencing, if so, do nothing
1218 	if (gubVideoConferencingMode != AIM_VIDEO_NOT_DISPLAYED_MODE) return;
1219 
1220 	// not even set, leave NOW!
1221 	if (!fNewMailFlag) return;
1222 
1223 	BltVideoObject(FRAME_BUFFER, guiEmailWarning,  0, EMAIL_WARNING_X,     EMAIL_WARNING_Y);
1224 	BltVideoObject(FRAME_BUFFER, guiTITLEBARICONS, 0, EMAIL_WARNING_X + 5, EMAIL_WARNING_Y + 2);
1225 
1226 	SetFontAttributes(EMAIL_HEADER_FONT, FONT_WHITE);
1227 
1228 	// print warning
1229 	MPrint(EMAIL_WARNING_X + 30, EMAIL_WARNING_Y + 8, pEmailTitleText);
1230 
1231 	SetFontAttributes(EMAIL_WARNING_FONT, FONT_BLACK, NO_SHADOW);
1232 
1233 	// printf warning string
1234 	MPrint(EMAIL_WARNING_X + 60, EMAIL_WARNING_Y + 63, pNewMailStrings);
1235 
1236 	// invalidate region
1237 	InvalidateRegion( EMAIL_WARNING_X, EMAIL_WARNING_Y, EMAIL_WARNING_X + 270, EMAIL_WARNING_Y + 200 );
1238 
1239 	// mark button
1240 	MarkAButtonDirty(giNewMailButton);
1241 
1242 	// reset shadow
1243 	SetFontShadow( DEFAULT_SHADOW );
1244 }
1245 
1246 
ReDrawNewMailBox(void)1247 void ReDrawNewMailBox(void)
1248 { // check to see if the new mail region needs to be redrawn
1249 	if (!fReDrawNewMailFlag) return;
1250 	fReDrawNewMailFlag = FALSE;
1251 	if (!fNewMailFlag) return;
1252 	DisplayNewMailBox();
1253 }
1254 
1255 
NextRegionButtonCallback(GUI_BUTTON * btn,INT32 reason)1256 static void NextRegionButtonCallback(GUI_BUTTON *btn, INT32 reason)
1257 {
1258 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1259 	{
1260 		NextListPage();
1261 	}
1262 }
1263 
1264 
BtnPreviousEmailPageCallback(GUI_BUTTON * btn,INT32 reason)1265 static void BtnPreviousEmailPageCallback(GUI_BUTTON *btn, INT32 reason)
1266 {
1267 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1268 	{
1269 		PrevMailPage();
1270 	}
1271 }
1272 
1273 
BtnNextEmailPageCallback(GUI_BUTTON * btn,INT32 reason)1274 static void BtnNextEmailPageCallback(GUI_BUTTON *btn, INT32 reason)
1275 {
1276 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1277 	{
1278 		NextMailPage();
1279 	}
1280 }
1281 
1282 
PreviousRegionButtonCallback(GUI_BUTTON * btn,INT32 reason)1283 static void PreviousRegionButtonCallback(GUI_BUTTON *btn, INT32 reason)
1284 {
1285 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1286 	{
1287 		PrevListPage();
1288 	}
1289 }
1290 
1291 
BtnDeleteNoback(GUI_BUTTON * btn,INT32 reason)1292 static void BtnDeleteNoback(GUI_BUTTON* btn, INT32 reason)
1293 {
1294 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1295 	{
1296 		MailToDelete = NULL;
1297 		fReDrawScreenFlag = TRUE;
1298 	}
1299 }
1300 
1301 
1302 static void DeleteEmail(void);
1303 
1304 
BtnDeleteYesback(GUI_BUTTON * btn,INT32 reason)1305 static void BtnDeleteYesback(GUI_BUTTON* btn, INT32 reason)
1306 {
1307 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1308 	{
1309 		fReDrawScreenFlag = TRUE;
1310 		DeleteEmail();
1311 	}
1312 }
1313 
1314 
CreateDestroyDeleteNoticeMailButton()1315 void CreateDestroyDeleteNoticeMailButton()
1316 {
1317 	static BOOLEAN fOldDeleteMailFlag=FALSE;
1318 	if (MailToDelete != NULL && !fOldDeleteMailFlag)
1319 	{
1320 		// confirm delete email buttons
1321 
1322 		// YES/NO buttons
1323 		fOldDeleteMailFlag=TRUE;
1324 		giDeleteMailButton[0] = MakeButtonYesNo(0, NEW_BTN_X +  1, BtnDeleteYesback);
1325 		giDeleteMailButton[1] = MakeButtonYesNo(2, NEW_BTN_X + 40, BtnDeleteNoback);
1326 
1327 		// set up screen mask to prevent other actions while delete mail box is destroyed
1328 		MSYS_DefineRegion(&pDeleteScreenMask, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, MSYS_PRIORITY_HIGHEST - 3, CURSOR_LAPTOP_SCREEN, MSYS_NO_CALLBACK, LapTopScreenCallBack);
1329 
1330 		// force update
1331 		fReDrawScreenFlag = TRUE;
1332 
1333 	}
1334 	else if (MailToDelete == NULL && fOldDeleteMailFlag)
1335 	{
1336 		// clear out the buttons and screen mask
1337 		fOldDeleteMailFlag=FALSE;
1338 		RemoveButton( giDeleteMailButton[0] );
1339 		RemoveButton( giDeleteMailButton[1] );
1340 
1341 		// the region
1342 		MSYS_RemoveRegion(&pDeleteScreenMask);
1343 
1344 		// force refresh
1345 		fReDrawScreenFlag=TRUE;
1346 	}
1347 }
1348 
1349 
DisplayDeleteNotice(Email * pMail)1350 static BOOLEAN DisplayDeleteNotice(Email* pMail)
1351 {
1352 	// will display a delete mail box whenever delete mail has arrived
1353 	if( !fReDrawScreenFlag )
1354 	{
1355 		// no redraw flag, leave
1356 		return( FALSE );
1357 	}
1358 
1359 	// error check.. no valid message passed
1360 	if( pMail == NULL )
1361 	{
1362 		return ( FALSE );
1363 	}
1364 
1365 	BltVideoObject(FRAME_BUFFER, guiEmailWarning, 0, EMAIL_WARNING_X, EMAIL_WARNING_Y);
1366 
1367 	SetFontAttributes(EMAIL_HEADER_FONT, FONT_WHITE);
1368 
1369 	BltVideoObject(FRAME_BUFFER, guiTITLEBARICONS, 0, EMAIL_WARNING_X + 5, EMAIL_WARNING_Y + 2);
1370 
1371 	// title
1372 	MPrint(EMAIL_WARNING_X + 30, EMAIL_WARNING_Y + 8, pEmailTitleText);
1373 
1374 	SetFontAttributes(EMAIL_WARNING_FONT, FONT_BLACK, NO_SHADOW);
1375 
1376 	// draw text based on mail being read or not
1377 	if((pMail->fRead))
1378 		MPrint(EMAIL_WARNING_X + 95, EMAIL_WARNING_Y + 65, pDeleteMailStrings[0]);
1379 	else
1380 		MPrint(EMAIL_WARNING_X + 70, EMAIL_WARNING_Y + 65, pDeleteMailStrings[1]);
1381 
1382 
1383 	// invalidate screen area, for refresh
1384 
1385 	if( ! fNewMailFlag )
1386 	{
1387 		// draw buttons
1388 		MarkButtonsDirty( );
1389 		InvalidateRegion(EMAIL_WARNING_X, EMAIL_WARNING_Y ,EMAIL_WARNING_X+EMAIL_WARNING_WIDTH,EMAIL_WARNING_Y+EMAIL_WARNING_HEIGHT);
1390 	}
1391 
1392 	// reset font shadow
1393 	SetFontShadow(DEFAULT_SHADOW);
1394 
1395 	return ( TRUE );
1396 }
1397 
1398 
DeleteEmail(void)1399 static void DeleteEmail(void)
1400 {
1401 
1402 	// error check, invalid mail, or not time to delete mail
1403 	if (MailToDelete == NULL) return;
1404 	// remove the message
1405 	RemoveEmailMessage(MailToDelete);
1406 	MailToDelete = NULL;
1407 
1408 	// stop displaying message, if so
1409 	fDisplayMessageFlag = FALSE;
1410 
1411 	// upadte list
1412 	PlaceMessagesinPages();
1413 
1414 	// if all of a sudden we are beyond last page, move back one
1415 	if(iCurrentPage > iLastPage)
1416 		iCurrentPage=iLastPage;
1417 
1418 	// rerender mail list
1419 	RenderEmail();
1420 
1421 	fReDrawScreenFlag=TRUE;
1422 
1423 	InvalidateScreen();
1424 }
1425 
1426 
FromCallback(GUI_BUTTON * btn,INT32 iReason)1427 static void FromCallback(GUI_BUTTON *btn, INT32 iReason)
1428 {
1429 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1430 	{
1431 		// sort messages based on sender name, then replace into pages of email
1432 		fSortSenderUpwards = !fSortSenderUpwards;
1433 		SortMessages(SENDER);
1434 		PlaceMessagesinPages();
1435 	}
1436 }
1437 
1438 
SubjectCallback(GUI_BUTTON * btn,INT32 iReason)1439 static void SubjectCallback(GUI_BUTTON *btn, INT32 iReason)
1440 {
1441 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1442 	{
1443 		// sort message on subject and reorder list
1444 		fSortSubjectUpwards = !fSortSubjectUpwards;
1445 		SortMessages(SUBJECT);
1446 		PlaceMessagesinPages();
1447 	}
1448 }
1449 
1450 
BtnDeleteCallback(GUI_BUTTON * btn,INT32 iReason)1451 static void BtnDeleteCallback(GUI_BUTTON *btn, INT32 iReason)
1452 {
1453 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1454 	{
1455 		MailToDelete = CurrentMail;
1456 	}
1457 }
1458 
1459 
DateCallback(GUI_BUTTON * btn,INT32 iReason)1460 static void DateCallback(GUI_BUTTON *btn, INT32 iReason)
1461 {
1462 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1463 	{
1464 		// sort messages based on date recieved and reorder lsit
1465 		fSortDateUpwards = !fSortDateUpwards;
1466 		SortMessages(RECEIVED);
1467 		PlaceMessagesinPages();
1468 	}
1469 }
1470 
1471 
ReadCallback(GUI_BUTTON * btn,INT32 iReason)1472 static void ReadCallback(GUI_BUTTON *btn, INT32 iReason)
1473 {
1474 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
1475 	{
1476 		// sort messages based on date recieved and reorder lsit
1477 		SortMessages(READ);
1478 		PlaceMessagesinPages();
1479 	}
1480 }
1481 
1482 
DisplayTextOnTitleBar(void)1483 static void DisplayTextOnTitleBar(void)
1484 {
1485 	// draw email screen title text
1486 	SetFontAttributes(EMAIL_TITLE_FONT, FONT_WHITE);
1487 	MPrint(EMAIL_TITLE_X, EMAIL_TITLE_Y, pEmailTitleText);
1488 }
1489 
1490 
DestroyMailScreenButtons(void)1491 static void DestroyMailScreenButtons(void)
1492 {
1493 	// this function will destory the buttons used in the email screen
1494 
1495 	// the sort email buttons
1496 	RemoveButton( giSortButton[0] );
1497 	RemoveButton( giSortButton[1] );
1498 	RemoveButton( giSortButton[2] );
1499 	RemoveButton( giSortButton[3] );
1500 }
1501 
1502 
MakeButton(UINT idx,INT16 x,GUI_CALLBACK click,const ST::string & text)1503 static void MakeButton(UINT idx, INT16 x, GUI_CALLBACK click, const ST::string& text)
1504 {
1505 	GUIButtonRef const btn = QuickCreateButtonImg(LAPTOPDIR "/mailbuttons.sti", idx, idx + 4, x, FROM_BOX_Y, MSYS_PRIORITY_HIGHEST - 1, click);
1506 	giSortButton[idx] = btn;
1507 	btn->SetCursor(CURSOR_LAPTOP_SCREEN);
1508 	if (!text.empty())
1509 	{
1510 		btn->SpecifyGeneralTextAttributes(text, EMAIL_WARNING_FONT, FONT_BLACK, FONT_BLACK);
1511 	}
1512 }
1513 
1514 
CreateMailScreenButtons(void)1515 static void CreateMailScreenButtons(void)
1516 {
1517 	// create sort buttons, right now - not finished
1518 	MakeButton(0, ENVELOPE_BOX_X, ReadCallback,    ST::null);
1519 	MakeButton(1, FROM_BOX_X,     FromCallback,    pEmailHeaders[FROM_HEADER]);
1520 	MakeButton(2, SUBJECT_BOX_X,  SubjectCallback, pEmailHeaders[SUBJECT_HEADER]);
1521 	MakeButton(3, DATE_BOX_X,     DateCallback,    pEmailHeaders[RECD_HEADER]);
1522 }
1523 
1524 
DisplayEmailMessageSubjectDateFromLines(Email * pMail,INT32 iViewerY)1525 static void DisplayEmailMessageSubjectDateFromLines(Email* pMail, INT32 iViewerY)
1526 {
1527 	// this procedure will draw the title/headers to From, Subject, Date fields in the display
1528 	// message box
1529 
1530 	SetFontAttributes(MESSAGE_FONT, FONT_BLACK, NO_SHADOW);
1531 
1532 	// all headers, but not info are right justified
1533 	INT16 usX;
1534 	INT16 usY;
1535 
1536 	// print from
1537 	FindFontRightCoordinates(MESSAGE_HEADER_X - 20, MESSAGE_FROM_Y + iViewerY, MESSAGE_HEADER_WIDTH, MESSAGE_FROM_Y + GetFontHeight(MESSAGE_FONT), pEmailHeaders[0], MESSAGE_FONT, &usX, &usY);
1538 	MPrint(usX, MESSAGE_FROM_Y + iViewerY, pEmailHeaders[0]);
1539 
1540 	// the actual from info
1541 	MPrint( MESSAGE_HEADER_X+MESSAGE_HEADER_WIDTH-13, MESSAGE_FROM_Y + iViewerY, pSenderNameList[pMail->ubSender]);
1542 
1543 
1544 	// print date
1545 	FindFontRightCoordinates(MESSAGE_HEADER_X + 168, MESSAGE_DATE_Y + iViewerY, MESSAGE_HEADER_WIDTH, MESSAGE_DATE_Y + GetFontHeight(MESSAGE_FONT), pEmailHeaders[2], MESSAGE_FONT, &usX, &usY);
1546 	MPrint(usX, MESSAGE_DATE_Y + iViewerY, pEmailHeaders[2]);
1547 
1548 	// the actual date info
1549 	MPrint(MESSAGE_HEADER_X + 235, MESSAGE_DATE_Y + iViewerY, ST::format("{}", pMail->iDate / (24 * 60)));
1550 
1551 	// print subject
1552 	FindFontRightCoordinates(MESSAGE_HEADER_X - 20, MESSAGE_SUBJECT_Y, MESSAGE_HEADER_WIDTH, MESSAGE_SUBJECT_Y + GetFontHeight(MESSAGE_FONT), pEmailHeaders[1], MESSAGE_FONT, &usX, &usY);
1553 	MPrint(usX, MESSAGE_SUBJECT_Y + iViewerY, pEmailHeaders[1]);
1554 
1555  	// the actual subject info
1556 	IanDisplayWrappedString(SUBJECT_LINE_X + 2, SUBJECT_LINE_Y + 2 + iViewerY, SUBJECT_LINE_WIDTH, MESSAGE_GAP, MESSAGE_FONT, MESSAGE_COLOR, pMail->pSubject, 0, 0);
1557 
1558 	// reset shadow
1559 	SetFontShadow(DEFAULT_SHADOW);
1560 }
1561 
1562 
DrawEmailMessageDisplayTitleText(INT32 iViewerY)1563 static void DrawEmailMessageDisplayTitleText(INT32 iViewerY)
1564 {
1565 	// this procedure will display the title of the email message display box
1566 	SetFontAttributes(EMAIL_HEADER_FONT, FONT_WHITE);
1567 	MPrint(VIEWER_X + 30, VIEWER_Y + 8 + iViewerY, pEmailTitleText);
1568 }
1569 
1570 
DrawLineDividers(void)1571 static void DrawLineDividers(void)
1572 {
1573 	// this function draws divider lines between lines of text
1574 	INT32 iCounter=0;
1575 
1576 	for(iCounter=1; iCounter < 19; iCounter++)
1577 	{
1578 		BltVideoObject(FRAME_BUFFER, guiMAILDIVIDER, 0, INDIC_X - 10, MIDDLE_Y + iCounter * MIDDLE_WIDTH - 1);
1579 	}
1580 }
1581 
1582 
ClearOutEmailMessageRecordsList(void)1583 static void ClearOutEmailMessageRecordsList(void)
1584 {
1585 	Record* pTempRecord;
1586 	INT32 iCounter = 0;
1587 
1588 	// runt hrough list freeing records up
1589 	while(pMessageRecordList)
1590 	{
1591 		// set temp to current
1592 		pTempRecord = pMessageRecordList;
1593 
1594 		// next element
1595 		pMessageRecordList = pMessageRecordList -> Next;
1596 
1597 		delete pTempRecord;
1598 	}
1599 
1600 	for( iCounter = 0; iCounter < MAX_NUMBER_EMAIL_PAGES; iCounter++ )
1601 	{
1602 		pEmailPageInfo[ iCounter ].pFirstRecord = NULL;
1603 		pEmailPageInfo[ iCounter ].pLastRecord = NULL;
1604 		pEmailPageInfo[ iCounter ].iPageNumber = iCounter;
1605 	}
1606 
1607 	// null out list
1608 	pMessageRecordList = NULL;
1609 }
1610 
1611 
AddEmailRecordToList(const ST::string & text)1612 static void AddEmailRecordToList(const ST::string& text)
1613 {
1614 	Record* const e = new Record{};
1615 	e->Next = NULL;
1616 	e->pRecord = text;
1617 
1618 	// Append node to list
1619 	Record** anchor = &pMessageRecordList;
1620 	while (*anchor != NULL) anchor = &(*anchor)->Next;
1621 	*anchor = e;
1622 }
1623 
1624 
UpDateMessageRecordList(void)1625 static void UpDateMessageRecordList(void)
1626 {
1627 
1628 	// simply checks to see if old and new message ids are the same, if so, do nothing
1629 	// otherwise clear list
1630 
1631 	if (CurrentMail != PreviousMail)
1632 	{
1633 		// if chenged, clear list
1634 		ClearOutEmailMessageRecordsList( );
1635 
1636 		// set prev to current
1637 		PreviousMail = CurrentMail;
1638 	}
1639 
1640 }
1641 
1642 
HandleAnySpecialEmailMessageEvents(INT32 iMessageId)1643 static void HandleAnySpecialEmailMessageEvents(INT32 iMessageId)
1644 {
1645 	// handles any special message events
1646 	switch( iMessageId )
1647 	{
1648 		case( IMP_EMAIL_AGAIN ):
1649 			SetBookMark(IMP_BOOKMARK);
1650 			break;
1651 		case( IMP_EMAIL_INTRO ):
1652 			SetBookMark(IMP_BOOKMARK);
1653 			break;
1654 	}
1655 }
1656 
1657 
ReDisplayBoxes(void)1658 static void ReDisplayBoxes(void)
1659 {
1660 	// the email message itself
1661 	if(fDisplayMessageFlag)
1662 	{
1663 		// this simply redraws message with button manipulation
1664 		DisplayEmailMessage(CurrentMail);
1665 	}
1666 
1667 	if (MailToDelete != NULL) DisplayDeleteNotice(MailToDelete);
1668 
1669 	if(fNewMailFlag)
1670 	{
1671 		// if new mail, redisplay box
1672 		DisplayNewMailBox( );
1673 	}
1674 }
1675 
1676 
1677 static void HandleIMPCharProfileResultsMessage(void);
1678 static void ModifyInsuranceEmails(UINT16 usMessageId, Email* pMail, UINT8 ubNumberOfRecords);
1679 
1680 
HandleMailSpecialMessages(UINT16 usMessageId,Email * pMail)1681 static void HandleMailSpecialMessages(UINT16 usMessageId, Email* pMail)
1682 {
1683 	// this procedure will handle special cases of email messages that are not stored in email.edt, or need special processing
1684 	switch( usMessageId )
1685 	{
1686 		case( IMP_EMAIL_PROFILE_RESULTS ):
1687 
1688 			HandleIMPCharProfileResultsMessage( );
1689 		break;
1690 		case( MERC_INTRO ):
1691 			SetBookMark( MERC_BOOKMARK );
1692 			fReDrawScreenFlag = TRUE;
1693 		break;
1694 
1695 
1696 		case INSUR_PAYMENT:
1697 		case INSUR_SUSPIC:
1698 		case INSUR_SUSPIC_2:
1699 		case INSUR_INVEST_OVER:
1700 			ModifyInsuranceEmails(usMessageId, pMail, INSUR_PAYMENT_LENGTH);
1701 			break;
1702 
1703 		case INSUR_1HOUR_FRAUD:
1704 			ModifyInsuranceEmails(usMessageId, pMail, INSUR_1HOUR_FRAUD_LENGTH);
1705 			break;
1706 
1707 		case MERC_NEW_SITE_ADDRESS:
1708 			//Set the book mark so the player can access the site
1709 			SetBookMark( MERC_BOOKMARK );
1710 			break;
1711 
1712 		case MERC_DIED_ON_OTHER_ASSIGNMENT:
1713 			ModifyInsuranceEmails(usMessageId, pMail, MERC_DIED_ON_OTHER_ASSIGNMENT_LENGTH);
1714 			break;
1715 
1716 		case AIM_MEDICAL_DEPOSIT_REFUND:
1717 		case AIM_MEDICAL_DEPOSIT_NO_REFUND:
1718 		case AIM_MEDICAL_DEPOSIT_PARTIAL_REFUND:
1719 			ModifyInsuranceEmails(usMessageId, pMail, AIM_MEDICAL_DEPOSIT_REFUND_LENGTH);
1720 			break;
1721 	}
1722 }
1723 
1724 
1725 
1726 #define IMP_RESULTS_INTRO_LENGTH 9
1727 
1728 #define IMP_RESULTS_PERSONALITY_INTRO IMP_RESULTS_INTRO_LENGTH
1729 #define IMP_RESULTS_PERSONALITY_INTRO_LENGTH 5
1730 #define IMP_PERSONALITY_NORMAL IMP_RESULTS_PERSONALITY_INTRO + IMP_RESULTS_PERSONALITY_INTRO_LENGTH
1731 #define IMP_PERSONALITY_LENGTH 4
1732 #define IMP_PERSONALITY_HEAT IMP_PERSONALITY_NORMAL + IMP_PERSONALITY_LENGTH
1733 #define IMP_PERSONALITY_NERVOUS IMP_PERSONALITY_HEAT + IMP_PERSONALITY_LENGTH
1734 #define IMP_PERSONALITY_CLAUSTROPHOBIC IMP_PERSONALITY_NERVOUS + IMP_PERSONALITY_LENGTH
1735 #define IMP_PERSONALITY_NONSWIMMER IMP_PERSONALITY_CLAUSTROPHOBIC + IMP_PERSONALITY_LENGTH
1736 #define IMP_PERSONALITY_FEAR_OF_INSECTS IMP_PERSONALITY_NONSWIMMER + IMP_PERSONALITY_LENGTH
1737 #define IMP_PERSONALITY_FORGETFUL IMP_PERSONALITY_FEAR_OF_INSECTS + IMP_PERSONALITY_LENGTH + 1
1738 #define IMP_PERSONALITY_PSYCHO IMP_PERSONALITY_FORGETFUL + IMP_PERSONALITY_LENGTH
1739 #define IMP_RESULTS_ATTITUDE_INTRO IMP_PERSONALITY_PSYCHO + IMP_PERSONALITY_LENGTH + 1
1740 #define IMP_RESULTS_ATTITUDE_LENGTH 5
1741 #define IMP_ATTITUDE_LENGTH 5
1742 #define IMP_ATTITUDE_NORMAL IMP_RESULTS_ATTITUDE_INTRO + IMP_RESULTS_ATTITUDE_LENGTH
1743 #define IMP_ATTITUDE_FRIENDLY IMP_ATTITUDE_NORMAL + IMP_ATTITUDE_LENGTH
1744 #define IMP_ATTITUDE_LONER IMP_ATTITUDE_FRIENDLY + IMP_ATTITUDE_LENGTH + 1
1745 #define IMP_ATTITUDE_OPTIMIST IMP_ATTITUDE_LONER + IMP_ATTITUDE_LENGTH + 1
1746 #define IMP_ATTITUDE_PESSIMIST IMP_ATTITUDE_OPTIMIST + IMP_ATTITUDE_LENGTH + 1
1747 #define IMP_ATTITUDE_AGGRESSIVE IMP_ATTITUDE_PESSIMIST + IMP_ATTITUDE_LENGTH + 1
1748 #define IMP_ATTITUDE_ARROGANT IMP_ATTITUDE_AGGRESSIVE + IMP_ATTITUDE_LENGTH + 1
1749 #define IMP_ATTITUDE_ASSHOLE IMP_ATTITUDE_ARROGANT + IMP_ATTITUDE_LENGTH + 1
1750 #define IMP_ATTITUDE_COWARD IMP_ATTITUDE_ASSHOLE + IMP_ATTITUDE_LENGTH
1751 #define IMP_RESULTS_SKILLS IMP_ATTITUDE_COWARD + IMP_ATTITUDE_LENGTH + 1
1752 #define IMP_RESULTS_SKILLS_LENGTH 7
1753 #define IMP_SKILLS_IMPERIAL_SKILLS IMP_RESULTS_SKILLS + IMP_RESULTS_SKILLS_LENGTH + 1
1754 #define IMP_SKILLS_IMPERIAL_MARK IMP_SKILLS_IMPERIAL_SKILLS + 1
1755 #define IMP_SKILLS_IMPERIAL_MECH IMP_SKILLS_IMPERIAL_SKILLS + 2
1756 #define IMP_SKILLS_IMPERIAL_EXPL IMP_SKILLS_IMPERIAL_SKILLS + 3
1757 #define IMP_SKILLS_IMPERIAL_MED  IMP_SKILLS_IMPERIAL_SKILLS + 4
1758 
1759 #define IMP_SKILLS_NEED_TRAIN_SKILLS IMP_SKILLS_IMPERIAL_MED + 1
1760 #define IMP_SKILLS_NEED_TRAIN_MARK IMP_SKILLS_NEED_TRAIN_SKILLS + 1
1761 #define IMP_SKILLS_NEED_TRAIN_MECH IMP_SKILLS_NEED_TRAIN_SKILLS + 2
1762 #define IMP_SKILLS_NEED_TRAIN_EXPL IMP_SKILLS_NEED_TRAIN_SKILLS + 3
1763 #define IMP_SKILLS_NEED_TRAIN_MED IMP_SKILLS_NEED_TRAIN_SKILLS + 4
1764 
1765 #define IMP_SKILLS_NO_SKILL IMP_SKILLS_NEED_TRAIN_MED + 1
1766 #define IMP_SKILLS_NO_SKILL_MARK  IMP_SKILLS_NO_SKILL + 1
1767 #define IMP_SKILLS_NO_SKILL_MECH  IMP_SKILLS_NO_SKILL + 2
1768 #define IMP_SKILLS_NO_SKILL_EXPL  IMP_SKILLS_NO_SKILL + 3
1769 #define IMP_SKILLS_NO_SKILL_MED   IMP_SKILLS_NO_SKILL + 4
1770 
1771 #define IMP_SKILLS_SPECIAL_INTRO IMP_SKILLS_NO_SKILL_MED + 1
1772 #define IMP_SKILLS_SPECIAL_INTRO_LENGTH 2
1773 #define IMP_SKILLS_SPECIAL_LOCK IMP_SKILLS_SPECIAL_INTRO + IMP_SKILLS_SPECIAL_INTRO_LENGTH
1774 #define IMP_SKILLS_SPECIAL_HAND IMP_SKILLS_SPECIAL_LOCK + 1
1775 #define IMP_SKILLS_SPECIAL_ELEC IMP_SKILLS_SPECIAL_HAND + 1
1776 #define IMP_SKILLS_SPECIAL_NIGHT IMP_SKILLS_SPECIAL_ELEC + 1
1777 #define IMP_SKILLS_SPECIAL_THROW IMP_SKILLS_SPECIAL_NIGHT + 1
1778 #define IMP_SKILLS_SPECIAL_TEACH IMP_SKILLS_SPECIAL_THROW + 1
1779 #define IMP_SKILLS_SPECIAL_HEAVY IMP_SKILLS_SPECIAL_TEACH + 1
1780 #define IMP_SKILLS_SPECIAL_AUTO IMP_SKILLS_SPECIAL_HEAVY + 1
1781 #define IMP_SKILLS_SPECIAL_STEALTH IMP_SKILLS_SPECIAL_AUTO + 1
1782 #define IMP_SKILLS_SPECIAL_AMBI IMP_SKILLS_SPECIAL_STEALTH + 1
1783 #define IMP_SKILLS_SPECIAL_THIEF IMP_SKILLS_SPECIAL_AMBI + 1
1784 #define IMP_SKILLS_SPECIAL_MARTIAL IMP_SKILLS_SPECIAL_THIEF + 1
1785 #define IMP_SKILLS_SPECIAL_KNIFE IMP_SKILLS_SPECIAL_MARTIAL + 1
1786 
1787 #define IMP_RESULTS_PHYSICAL IMP_SKILLS_SPECIAL_KNIFE + 1
1788 #define IMP_RESULTS_PHYSICAL_LENGTH 7
1789 
1790 #define IMP_PHYSICAL_SUPER IMP_RESULTS_PHYSICAL + IMP_RESULTS_PHYSICAL_LENGTH
1791 #define IMP_PHYSICAL_SUPER_LENGTH 1
1792 
1793 #define IMP_PHYSICAL_SUPER_HEALTH IMP_PHYSICAL_SUPER + IMP_PHYSICAL_SUPER_LENGTH
1794 #define IMP_PHYSICAL_SUPER_AGILITY IMP_PHYSICAL_SUPER_HEALTH + 1
1795 #define IMP_PHYSICAL_SUPER_DEXTERITY IMP_PHYSICAL_SUPER_AGILITY + 1
1796 #define IMP_PHYSICAL_SUPER_STRENGTH IMP_PHYSICAL_SUPER_DEXTERITY + 1
1797 #define IMP_PHYSICAL_SUPER_LEADERSHIP IMP_PHYSICAL_SUPER_STRENGTH + 1
1798 #define IMP_PHYSICAL_SUPER_WISDOM IMP_PHYSICAL_SUPER_LEADERSHIP + 1
1799 
1800 #define IMP_PHYSICAL_LOW IMP_PHYSICAL_SUPER_WISDOM + 1
1801 #define IMP_PHYSICAL_LOW_LENGTH 1
1802 
1803 #define IMP_PHYSICAL_LOW_HEALTH IMP_PHYSICAL_LOW + IMP_PHYSICAL_LOW_LENGTH
1804 #define IMP_PHYSICAL_LOW_AGILITY IMP_PHYSICAL_LOW_HEALTH + 1
1805 #define IMP_PHYSICAL_LOW_DEXTERITY IMP_PHYSICAL_LOW_AGILITY + 2
1806 #define IMP_PHYSICAL_LOW_STRENGTH IMP_PHYSICAL_LOW_DEXTERITY + 1
1807 #define IMP_PHYSICAL_LOW_LEADERSHIP IMP_PHYSICAL_LOW_STRENGTH + 1
1808 #define IMP_PHYSICAL_LOW_WISDOM IMP_PHYSICAL_LOW_LEADERSHIP + 1
1809 
1810 
1811 #define IMP_PHYSICAL_VERY_LOW IMP_PHYSICAL_LOW_WISDOM + 1
1812 #define IMP_PHYSICAL_VERY_LOW_LENGTH 1
1813 
1814 #define IMP_PHYSICAL_VERY_LOW_HEALTH IMP_PHYSICAL_VERY_LOW + IMP_PHYSICAL_VERY_LOW_LENGTH
1815 #define IMP_PHYSICAL_VERY_LOW_AGILITY IMP_PHYSICAL_VERY_LOW_HEALTH + 1
1816 #define IMP_PHYSICAL_VERY_LOW_DEXTERITY IMP_PHYSICAL_VERY_LOW_AGILITY + 1
1817 #define IMP_PHYSICAL_VERY_LOW_STRENGTH IMP_PHYSICAL_VERY_LOW_DEXTERITY + 1
1818 #define IMP_PHYSICAL_VERY_LOW_LEADERSHIP IMP_PHYSICAL_VERY_LOW_STRENGTH + 1
1819 #define IMP_PHYSICAL_VERY_LOW_WISDOM IMP_PHYSICAL_VERY_LOW_LEADERSHIP + 1
1820 
1821 
1822 #define IMP_PHYSICAL_END IMP_PHYSICAL_VERY_LOW_WISDOM + 1
1823 #define IMP_PHYSICAL_END_LENGTH 3
1824 
1825 #define IMP_RESULTS_PORTRAIT  IMP_PHYSICAL_END + IMP_PHYSICAL_END_LENGTH
1826 #define IMP_RESULTS_PORTRAIT_LENGTH 6
1827 
1828 
1829 #define IMP_PORTRAIT_MALE_1 IMP_RESULTS_PORTRAIT + IMP_RESULTS_PORTRAIT_LENGTH
1830 #define IMP_PORTRAIT_MALE_2 IMP_PORTRAIT_MALE_1 + 4
1831 #define IMP_PORTRAIT_MALE_3 IMP_PORTRAIT_MALE_2 + 4
1832 #define IMP_PORTRAIT_MALE_4 IMP_PORTRAIT_MALE_3 + 4
1833 #define IMP_PORTRAIT_MALE_5 IMP_PORTRAIT_MALE_4 + 4
1834 #define IMP_PORTRAIT_MALE_6 IMP_PORTRAIT_MALE_5 + 4
1835 
1836 #define IMP_PORTRAIT_FEMALE_1 IMP_PORTRAIT_MALE_6 + 4
1837 #define IMP_PORTRAIT_FEMALE_2 IMP_PORTRAIT_FEMALE_1 + 4
1838 #define IMP_PORTRAIT_FEMALE_3 IMP_PORTRAIT_FEMALE_2 + 4
1839 #define IMP_PORTRAIT_FEMALE_4 IMP_PORTRAIT_FEMALE_3 + 4
1840 #define IMP_PORTRAIT_FEMALE_5 IMP_PORTRAIT_FEMALE_4 + 4
1841 #define IMP_PORTRAIT_FEMALE_6 IMP_PORTRAIT_FEMALE_5 + 4
1842 
1843 
1844 
1845 #define IMP_RESULTS_END IMP_PORTRAIT_FEMALE_6 + 1
1846 #define IMP_RESULTS_END_LENGTH 3
1847 
1848 
1849 enum SkillBits
1850 {
1851 	SKILL_NONE = 0,
1852 	SKILL_MECH = 1 << 0,
1853 	SKILL_MARK = 1 << 1,
1854 	SKILL_MED  = 1 << 2,
1855 	SKILL_EXPL = 1 << 3
1856 };
1857 ENUM_BITSET(SkillBits)
1858 
1859 
1860 enum PhysicalBits
1861 {
1862 	PHYS_NONE = 0,
1863 	PHYS_HLTH = 1 << 0,
1864 	PHYS_DEX  = 1 << 1,
1865 	PHYS_STR  = 1 << 2,
1866 	PHYS_AGI  = 1 << 3,
1867 	PHYS_WIS  = 1 << 4,
1868 	PHYS_LDR  = 1 << 5
1869 };
ENUM_BITSET(PhysicalBits)1870 ENUM_BITSET(PhysicalBits)
1871 
1872 
1873 static ST::string LoadIMPResultText(UINT32 Offset)
1874 {
1875 	return GCM->loadEncryptedString(BINARYDATADIR "/impass.edt", MAIL_STRING_SIZE * Offset, MAIL_STRING_SIZE);
1876 }
1877 
1878 
AddIMPResultText(UINT32 Offset)1879 static void AddIMPResultText(UINT32 Offset)
1880 {
1881 	ST::string Text = LoadIMPResultText(Offset);
1882 	AddEmailRecordToList(Text);
1883 }
1884 
1885 
AddSkillTraitText(MERCPROFILESTRUCT const & imp,SkillTrait const Skill,UINT32 const Offset)1886 static void AddSkillTraitText(MERCPROFILESTRUCT const& imp, SkillTrait const Skill, UINT32 const Offset)
1887 {
1888 	if (HasSkillTrait(imp, Skill)) AddIMPResultText(Offset);
1889 }
1890 
1891 
HandleIMPCharProfileResultsMessage(void)1892 static void HandleIMPCharProfileResultsMessage(void)
1893 {
1894 	// special case, IMP profile return
1895 	INT32 iOffSet;
1896 	INT32 iEndOfSection;
1897 
1898 	INT32 iRand = Random(32767);
1899 
1900 	if (pMessageRecordList != NULL) return;
1901 	// list doesn't exist, reload
1902 
1903 	MERCPROFILESTRUCT const& imp = GetProfile(PLAYER_GENERATED_CHARACTER_ID + LaptopSaveInfo.iVoiceId);
1904 
1905 	// load intro
1906 	iEndOfSection = IMP_RESULTS_INTRO_LENGTH;
1907 	for (INT32 i = 0; i < iEndOfSection; ++i)
1908 	{
1909 		ST::string pString = LoadIMPResultText(i);
1910 
1911 		// have to place players name into string for first record
1912 		if (i == 0)
1913 		{
1914 			ST::string zTemp = ST::format(" {}", imp.zName);
1915 			pString += zTemp;
1916 		}
1917 
1918 		AddEmailRecordToList(pString);
1919 	}
1920 
1921 	// now the personality intro
1922 	iOffSet = IMP_RESULTS_PERSONALITY_INTRO;
1923 	iEndOfSection = IMP_RESULTS_PERSONALITY_INTRO_LENGTH + 1;
1924 	for (INT32 i = 0; i < iEndOfSection; ++i) AddIMPResultText(iOffSet + i);
1925 
1926 	// personality itself
1927 	switch (imp.bPersonalityTrait)
1928 	{
1929 		// normal as can be
1930 		case NO_PERSONALITYTRAIT: iOffSet = IMP_PERSONALITY_NORMAL;          break;
1931 		case HEAT_INTOLERANT:     iOffSet = IMP_PERSONALITY_HEAT;            break;
1932 		case NERVOUS:             iOffSet = IMP_PERSONALITY_NERVOUS;         break;
1933 		case CLAUSTROPHOBIC:      iOffSet = IMP_PERSONALITY_CLAUSTROPHOBIC;  break;
1934 		case NONSWIMMER:          iOffSet = IMP_PERSONALITY_NONSWIMMER;      break;
1935 		case FEAR_OF_INSECTS:     iOffSet = IMP_PERSONALITY_FEAR_OF_INSECTS; break;
1936 		case FORGETFUL:           iOffSet = IMP_PERSONALITY_FORGETFUL;       break;
1937 		case PSYCHO:              iOffSet = IMP_PERSONALITY_PSYCHO;          break;
1938 	}
1939 
1940 	// personality tick
1941 	//  DEF: removed 1/12/99, cause it was changing the length of email that were already calculated
1942 	//               AddIMPResultText(iOffSet + Random(IMP_PERSONALITY_LENGTH - 1) + 1);
1943 	AddIMPResultText(iOffSet + 1);
1944 
1945 	// persoanlity paragraph
1946 	AddIMPResultText(iOffSet + IMP_PERSONALITY_LENGTH);
1947 
1948 	// extra paragraph for bugs
1949 	if (imp.bPersonalityTrait == FEAR_OF_INSECTS)
1950 	{
1951 		// persoanlity paragraph
1952 		AddIMPResultText(iOffSet + IMP_PERSONALITY_LENGTH + 1);
1953 	}
1954 
1955 	// attitude intro
1956 	// now the personality intro
1957 	iOffSet = IMP_RESULTS_ATTITUDE_INTRO;
1958 	iEndOfSection = IMP_RESULTS_ATTITUDE_LENGTH;
1959 	for (INT32 i = 0; i < iEndOfSection; ++i) AddIMPResultText(iOffSet + i);
1960 
1961 		// personality itself
1962 	switch (imp.bAttitude)
1963 	{
1964 		// normal as can be
1965 		case ATT_NORMAL:     iOffSet = IMP_ATTITUDE_NORMAL;     break;
1966 		case ATT_FRIENDLY:   iOffSet = IMP_ATTITUDE_FRIENDLY;   break;
1967 		case ATT_LONER:      iOffSet = IMP_ATTITUDE_LONER;      break;
1968 		case ATT_OPTIMIST:   iOffSet = IMP_ATTITUDE_OPTIMIST;   break;
1969 		case ATT_PESSIMIST:  iOffSet = IMP_ATTITUDE_PESSIMIST;  break;
1970 		case ATT_AGGRESSIVE: iOffSet = IMP_ATTITUDE_AGGRESSIVE; break;
1971 		case ATT_ARROGANT:   iOffSet = IMP_ATTITUDE_ARROGANT;   break;
1972 		case ATT_ASSHOLE:    iOffSet = IMP_ATTITUDE_ASSHOLE;    break;
1973 		case ATT_COWARD:     iOffSet = IMP_ATTITUDE_COWARD;     break;
1974 	}
1975 
1976 	// attitude title
1977 	AddIMPResultText(iOffSet);
1978 
1979 	// attitude tick
1980 	//  DEF: removed 1/12/99, cause it was changing the length of email that were already calculated
1981 	//               AddIMPResultText(iOffSet + Random(IMP_ATTITUDE_LENGTH - 2) + 1);
1982 	AddIMPResultText(iOffSet + 1);
1983 
1984 	// attitude paragraph
1985 	AddIMPResultText(iOffSet + IMP_ATTITUDE_LENGTH - 1);
1986 
1987 	//check for second paragraph
1988 	if (iOffSet != IMP_ATTITUDE_NORMAL)
1989 	{
1990 		// attitude paragraph
1991 		AddIMPResultText(iOffSet + IMP_ATTITUDE_LENGTH);
1992 	}
1993 
1994 
1995 	// skills
1996 	// now the skills intro
1997 	iOffSet = IMP_RESULTS_SKILLS;
1998 	iEndOfSection = IMP_RESULTS_SKILLS_LENGTH;
1999 	for (INT32 i = 0; i < iEndOfSection; ++i) AddIMPResultText(iOffSet + i);
2000 
2001 
2002 	SkillBits Skill;
2003 
2004 	Skill = SKILL_NONE;
2005 	if (imp.bMarksmanship >= SUPER_SKILL_VALUE) Skill |= SKILL_MARK;
2006 	if (imp.bMedical      >= SUPER_SKILL_VALUE) Skill |= SKILL_MED;
2007 	if (imp.bMechanical   >= SUPER_SKILL_VALUE) Skill |= SKILL_MECH;
2008 	if (imp.bExplosive    >= SUPER_SKILL_VALUE) Skill |= SKILL_EXPL;
2009 
2010 	if (Skill != SKILL_NONE) AddIMPResultText(IMP_SKILLS_IMPERIAL_SKILLS);
2011 
2012 	if (Skill & SKILL_MARK) AddIMPResultText(IMP_SKILLS_IMPERIAL_MARK);
2013 	if (Skill & SKILL_MED)  AddIMPResultText(IMP_SKILLS_IMPERIAL_MED);
2014 	if (Skill & SKILL_MECH) AddIMPResultText(IMP_SKILLS_IMPERIAL_MECH);
2015 	if (Skill & SKILL_EXPL) AddIMPResultText(IMP_SKILLS_IMPERIAL_EXPL);
2016 
2017 
2018 	// now the needs training values
2019 	Skill = SKILL_NONE;
2020 	if (imp.bMarksmanship > NO_CHANCE_IN_HELL_SKILL_VALUE && imp.bMarksmanship <= NEEDS_TRAINING_SKILL_VALUE) Skill |= SKILL_MARK;
2021 	if (imp.bMedical      > NO_CHANCE_IN_HELL_SKILL_VALUE && imp.bMedical      <= NEEDS_TRAINING_SKILL_VALUE) Skill |= SKILL_MED;
2022 	if (imp.bMechanical   > NO_CHANCE_IN_HELL_SKILL_VALUE && imp.bMechanical   <= NEEDS_TRAINING_SKILL_VALUE) Skill |= SKILL_MECH;
2023 	if (imp.bExplosive    > NO_CHANCE_IN_HELL_SKILL_VALUE && imp.bExplosive    <= NEEDS_TRAINING_SKILL_VALUE) Skill |= SKILL_EXPL;
2024 
2025 	if (Skill != SKILL_NONE) AddIMPResultText(IMP_SKILLS_NEED_TRAIN_SKILLS);
2026 
2027 	if (Skill & SKILL_MARK) AddIMPResultText(IMP_SKILLS_NEED_TRAIN_MARK);
2028 	if (Skill & SKILL_MED)  AddIMPResultText(IMP_SKILLS_NEED_TRAIN_MED);
2029 	if (Skill & SKILL_MECH) AddIMPResultText(IMP_SKILLS_NEED_TRAIN_MECH);
2030 	if (Skill & SKILL_EXPL) AddIMPResultText(IMP_SKILLS_NEED_TRAIN_EXPL);
2031 
2032 
2033 	// and the no chance in hell of doing anything useful values
2034 	Skill = SKILL_NONE;
2035 	if (imp.bMarksmanship <= NO_CHANCE_IN_HELL_SKILL_VALUE) Skill |= SKILL_MARK;
2036 	if (imp.bMedical      <= NO_CHANCE_IN_HELL_SKILL_VALUE) Skill |= SKILL_MED;
2037 	if (imp.bMechanical   <= NO_CHANCE_IN_HELL_SKILL_VALUE) Skill |= SKILL_MECH;
2038 	if (imp.bExplosive    <= NO_CHANCE_IN_HELL_SKILL_VALUE) Skill |= SKILL_EXPL;
2039 
2040 	if (Skill != SKILL_NONE) AddIMPResultText(IMP_SKILLS_NO_SKILL);
2041 
2042 	if (Skill & SKILL_MECH) AddIMPResultText(IMP_SKILLS_NO_SKILL_MECH);
2043 	if (Skill & SKILL_MARK) AddIMPResultText(IMP_SKILLS_NO_SKILL_MARK);
2044 	if (Skill & SKILL_MED)  AddIMPResultText(IMP_SKILLS_NO_SKILL_MED);
2045 	if (Skill & SKILL_EXPL) AddIMPResultText(IMP_SKILLS_NO_SKILL_EXPL);
2046 
2047 
2048 	// now the specialized skills
2049 	// imperial skills
2050 	iOffSet = IMP_SKILLS_SPECIAL_INTRO;
2051 	iEndOfSection = IMP_SKILLS_SPECIAL_INTRO_LENGTH;
2052 	for (INT32 i = 0; i < iEndOfSection; ++i) AddIMPResultText(iOffSet + i);
2053 
2054 	AddSkillTraitText(imp, KNIFING,     IMP_SKILLS_SPECIAL_KNIFE);
2055 	AddSkillTraitText(imp, LOCKPICKING, IMP_SKILLS_SPECIAL_LOCK);
2056 	AddSkillTraitText(imp, HANDTOHAND,  IMP_SKILLS_SPECIAL_HAND);
2057 	AddSkillTraitText(imp, ELECTRONICS, IMP_SKILLS_SPECIAL_ELEC);
2058 	AddSkillTraitText(imp, NIGHTOPS,    IMP_SKILLS_SPECIAL_NIGHT);
2059 	AddSkillTraitText(imp, THROWING,    IMP_SKILLS_SPECIAL_THROW);
2060 	AddSkillTraitText(imp, TEACHING,    IMP_SKILLS_SPECIAL_TEACH);
2061 	AddSkillTraitText(imp, HEAVY_WEAPS, IMP_SKILLS_SPECIAL_HEAVY);
2062 	AddSkillTraitText(imp, AUTO_WEAPS,  IMP_SKILLS_SPECIAL_AUTO);
2063 	AddSkillTraitText(imp, STEALTHY,    IMP_SKILLS_SPECIAL_STEALTH);
2064 	AddSkillTraitText(imp, AMBIDEXT,    IMP_SKILLS_SPECIAL_AMBI);
2065 	AddSkillTraitText(imp, THIEF,       IMP_SKILLS_SPECIAL_THIEF);
2066 	AddSkillTraitText(imp, MARTIALARTS, IMP_SKILLS_SPECIAL_MARTIAL);
2067 
2068 
2069 	// now the physical
2070 	// imperial physical
2071 	iOffSet = IMP_RESULTS_PHYSICAL;
2072 	iEndOfSection = IMP_RESULTS_PHYSICAL_LENGTH;
2073 	for (INT32 i = 0; i < iEndOfSection; ++i) AddIMPResultText(iOffSet + i);
2074 
2075 	PhysicalBits Phys;
2076 
2077 	// super physical
2078 	Phys = PHYS_NONE;
2079 	if (imp.bLife       >= SUPER_STAT_VALUE) Phys |= PHYS_HLTH;
2080 	if (imp.bDexterity  >= SUPER_STAT_VALUE) Phys |= PHYS_DEX;
2081 	if (imp.bAgility    >= SUPER_STAT_VALUE) Phys |= PHYS_AGI;
2082 	if (imp.bStrength   >= SUPER_STAT_VALUE) Phys |= PHYS_STR;
2083 	if (imp.bWisdom     >= SUPER_STAT_VALUE) Phys |= PHYS_WIS;
2084 	if (imp.bLeadership >= SUPER_STAT_VALUE) Phys |= PHYS_LDR;
2085 
2086 	if (Phys != PHYS_NONE) AddIMPResultText(IMP_PHYSICAL_SUPER);
2087 
2088 	if (Phys & PHYS_HLTH) AddIMPResultText(IMP_PHYSICAL_SUPER_HEALTH);
2089 	if (Phys & PHYS_DEX)  AddIMPResultText(IMP_PHYSICAL_SUPER_DEXTERITY);
2090 	if (Phys & PHYS_STR)  AddIMPResultText(IMP_PHYSICAL_SUPER_STRENGTH);
2091 	if (Phys & PHYS_AGI)  AddIMPResultText(IMP_PHYSICAL_SUPER_AGILITY);
2092 	if (Phys & PHYS_WIS)  AddIMPResultText(IMP_PHYSICAL_SUPER_WISDOM);
2093 	if (Phys & PHYS_LDR)  AddIMPResultText(IMP_PHYSICAL_SUPER_LEADERSHIP);
2094 
2095 
2096 	// now the low attributes
2097 	Phys = PHYS_NONE;
2098 	if (imp.bLife       < NEEDS_TRAINING_STAT_VALUE && imp.bLife       > NO_CHANCE_IN_HELL_STAT_VALUE) Phys |= PHYS_HLTH;
2099 	if (imp.bStrength   < NEEDS_TRAINING_STAT_VALUE && imp.bStrength   > NO_CHANCE_IN_HELL_STAT_VALUE) Phys |= PHYS_STR;
2100 	if (imp.bAgility    < NEEDS_TRAINING_STAT_VALUE && imp.bAgility    > NO_CHANCE_IN_HELL_STAT_VALUE) Phys |= PHYS_AGI;
2101 	if (imp.bWisdom     < NEEDS_TRAINING_STAT_VALUE && imp.bWisdom     > NO_CHANCE_IN_HELL_STAT_VALUE) Phys |= PHYS_WIS;
2102 	if (imp.bLeadership < NEEDS_TRAINING_STAT_VALUE && imp.bLeadership > NO_CHANCE_IN_HELL_STAT_VALUE) Phys |= PHYS_LDR;
2103 	if (imp.bDexterity  < NEEDS_TRAINING_STAT_VALUE && imp.bDexterity  > NO_CHANCE_IN_HELL_STAT_VALUE) Phys |= PHYS_DEX;
2104 
2105 	if (Phys != PHYS_NONE) AddIMPResultText(IMP_PHYSICAL_LOW);
2106 
2107 	if (Phys & PHYS_HLTH) AddIMPResultText(IMP_PHYSICAL_LOW_HEALTH);
2108 	if (Phys & PHYS_DEX)  AddIMPResultText(IMP_PHYSICAL_LOW_DEXTERITY);
2109 	if (Phys & PHYS_STR)  AddIMPResultText(IMP_PHYSICAL_LOW_STRENGTH);
2110 	if (Phys & PHYS_AGI)  AddIMPResultText(IMP_PHYSICAL_LOW_AGILITY);
2111 	if (Phys & PHYS_WIS)  AddIMPResultText(IMP_PHYSICAL_LOW_WISDOM);
2112 	if (Phys & PHYS_LDR)  AddIMPResultText(IMP_PHYSICAL_LOW_LEADERSHIP);
2113 
2114 
2115 	// very low physical
2116 	Phys = PHYS_NONE;
2117 	if (imp.bLife       <= NO_CHANCE_IN_HELL_STAT_VALUE) Phys |= PHYS_HLTH;
2118 	if (imp.bDexterity  <= NO_CHANCE_IN_HELL_STAT_VALUE) Phys |= PHYS_DEX;
2119 	if (imp.bStrength   <= NO_CHANCE_IN_HELL_STAT_VALUE) Phys |= PHYS_STR;
2120 	if (imp.bAgility    <= NO_CHANCE_IN_HELL_STAT_VALUE) Phys |= PHYS_AGI;
2121 	if (imp.bWisdom     <= NO_CHANCE_IN_HELL_STAT_VALUE) Phys |= PHYS_WIS;
2122 	if (imp.bLeadership <= NO_CHANCE_IN_HELL_STAT_VALUE) Phys |= PHYS_LDR;
2123 
2124 	if (Phys != PHYS_NONE) AddIMPResultText(IMP_PHYSICAL_VERY_LOW);
2125 
2126 	if (Phys & PHYS_HLTH) AddIMPResultText(IMP_PHYSICAL_VERY_LOW_HEALTH);
2127 	if (Phys & PHYS_DEX)  AddIMPResultText(IMP_PHYSICAL_VERY_LOW_DEXTERITY);
2128 	if (Phys & PHYS_STR)  AddIMPResultText(IMP_PHYSICAL_VERY_LOW_STRENGTH);
2129 	if (Phys & PHYS_AGI)  AddIMPResultText(IMP_PHYSICAL_VERY_LOW_AGILITY);
2130 	if (Phys & PHYS_WIS)  AddIMPResultText(IMP_PHYSICAL_VERY_LOW_WISDOM);
2131 	if (Phys & PHYS_LDR) AddIMPResultText(IMP_PHYSICAL_VERY_LOW_LEADERSHIP);
2132 
2133 
2134 	iOffSet = IMP_RESULTS_PORTRAIT;
2135 	iEndOfSection = IMP_RESULTS_PORTRAIT_LENGTH;
2136 	for (INT32 i = 0; i < iEndOfSection; ++i) AddIMPResultText(iOffSet + i);
2137 
2138 	switch (iPortraitNumber)
2139 	{
2140 		case  0: iOffSet = IMP_PORTRAIT_MALE_1;   break;
2141 		case  1: iOffSet = IMP_PORTRAIT_MALE_2;   break;
2142 		case  2: iOffSet = IMP_PORTRAIT_MALE_3;   break;
2143 		case  3: iOffSet = IMP_PORTRAIT_MALE_4;   break;
2144 		case  4:
2145 		case  5: iOffSet = IMP_PORTRAIT_MALE_5;   break;
2146 		case  6:
2147 		case  7: iOffSet = IMP_PORTRAIT_MALE_6;   break;
2148 		case  8: iOffSet = IMP_PORTRAIT_FEMALE_1; break;
2149 		case  9: iOffSet = IMP_PORTRAIT_FEMALE_2; break;
2150 		case 10: iOffSet = IMP_PORTRAIT_FEMALE_3; break;
2151 		case 11:
2152 		case 12: iOffSet = IMP_PORTRAIT_FEMALE_4; break;
2153 		case 13:
2154 		case 14: iOffSet = IMP_PORTRAIT_FEMALE_5; break;
2155 	}
2156 
2157 	if (iRand % 2 == 0) iOffSet += 2;
2158 
2159 	iEndOfSection = 2;
2160 	for (INT32 i = 0; i < iEndOfSection; ++i) AddIMPResultText(iOffSet + i);
2161 
2162 	iOffSet = IMP_RESULTS_END;
2163 	iEndOfSection = IMP_RESULTS_END_LENGTH;
2164 	for (INT32 i = 0; i < iEndOfSection; ++i) AddIMPResultText(iOffSet + i);
2165 
2166 	PreviousMail = CurrentMail;
2167 }
2168 
2169 
HandleEmailViewerButtonStates(void)2170 static void HandleEmailViewerButtonStates(void)
2171 {
2172 	// handle state of email viewer buttons
2173 
2174 	// leave, if not displaying message
2175 	if (!fDisplayMessageFlag) return;
2176 
2177 	if(  giNumberOfPagesToCurrentEmail <= 2 )
2178 	{
2179 		return;
2180 	}
2181 
2182 	// Turn on/off previous page button
2183 	EnableButton(giMailMessageButtons[0], giMessagePage != 0);
2184 	// Turn on/off next page button
2185 	EnableButton(giMailMessageButtons[1], pEmailPageInfo[giMessagePage + 1].pFirstRecord);
2186 }
2187 
2188 
CreateNextPreviousEmailPageButtons(void)2189 static void CreateNextPreviousEmailPageButtons(void)
2190 {
2191 	// this function will create the buttons to advance and go back email pages
2192 	giMailPageButtons[0] = MakeButtonNewMail(1, NEXT_PAGE_X,     NEXT_PAGE_Y, NextRegionButtonCallback);
2193 	giMailPageButtons[1] = MakeButtonNewMail(0, PREVIOUS_PAGE_X, NEXT_PAGE_Y, PreviousRegionButtonCallback);
2194 }
2195 
2196 
UpdateStatusOfNextPreviousButtons(void)2197 static void UpdateStatusOfNextPreviousButtons(void)
2198 {
2199 	// set the states of the page advance buttons
2200 	EnableButton(giMailPageButtons[0], iCurrentPage < iLastPage);
2201 	EnableButton(giMailPageButtons[1], iCurrentPage > 0);
2202 }
2203 
2204 
DisplayWhichPageOfEmailProgramIsDisplayed(void)2205 static void DisplayWhichPageOfEmailProgramIsDisplayed(void)
2206 {
2207 	// will draw the number of the email program we are viewing right now
2208 	SetFontAttributes(MESSAGE_FONT, FONT_BLACK, NO_SHADOW);
2209 
2210 	// page number
2211 	INT32 CPage;
2212 	INT32 LPage;
2213 	if (iLastPage < 0)
2214 	{
2215 		CPage = 1;
2216 		LPage = 1;
2217 	}
2218 	else
2219 	{
2220 		CPage = iCurrentPage + 1;
2221 		LPage = iLastPage + 1;
2222 	}
2223 	MPrint(PAGE_NUMBER_X, PAGE_NUMBER_Y, ST::format("{} / {}", CPage, LPage));
2224 
2225 	// restore shadow
2226 	SetFontShadow( DEFAULT_SHADOW );
2227 }
2228 
2229 
OpenMostRecentUnreadEmail(void)2230 static void OpenMostRecentUnreadEmail(void)
2231 {
2232 	// will open the most recent email the player has recieved and not read
2233 	Email* MostRecentMail = NULL;
2234 	Email* pB = pEmailList;
2235 	UINT32 iLowestDate = 9999999;
2236 
2237 	while( pB )
2238 	{
2239 		// if date is lesser and unread , swap
2240 		if (pB->iDate < iLowestDate && !pB->fRead)
2241 		{
2242 			MostRecentMail = pB;
2243 			iLowestDate = pB -> iDate;
2244 		}
2245 
2246 		// next in B's list
2247 		pB=pB->Next;
2248 	}
2249 
2250 	CurrentMail = MostRecentMail;
2251 
2252 	// valid message, show it
2253 	if (MostRecentMail != NULL) fDisplayMessageFlag = TRUE;
2254 }
2255 
2256 
DisplayNumberOfPagesToThisEmail(INT32 const iViewerY)2257 static void DisplayNumberOfPagesToThisEmail(INT32 const iViewerY)
2258 {
2259 	// display the indent for the display of pages to this email..along with the current page/number of pages
2260 	SetFontAttributes(FONT12ARIAL, FONT_BLACK, NO_SHADOW);
2261 
2262 	ST::string str = ST::format("{} / {}", giMessagePage + 1, giNumberOfPagesToCurrentEmail - 1);
2263 	INT16 sX;
2264 	INT16 sY;
2265 	FindFontCenterCoordinates(VIEWER_X + INDENT_X_OFFSET, 0, INDENT_X_WIDTH, 0, str, FONT12ARIAL, &sX, &sY);
2266 	MPrint(sX, VIEWER_Y + iViewerY + INDENT_Y_OFFSET - 2, str);
2267 
2268 	SetFontShadow(DEFAULT_SHADOW);
2269 }
2270 
2271 
GetNumberOfPagesToEmail(void)2272 static INT32 GetNumberOfPagesToEmail(void)
2273 {
2274 	Record* pTempRecord;
2275 	INT32 iNumberOfPagesToEmail = 0;
2276 
2277 
2278 	// set temp record to head of list
2279 	pTempRecord=pMessageRecordList;
2280 
2281 	// run through messages, and find out how many
2282 	while( pTempRecord )
2283 	{
2284 		pTempRecord = GetFirstRecordOnThisPage(pMessageRecordList, iNumberOfPagesToEmail);
2285 		iNumberOfPagesToEmail++;
2286 	}
2287 
2288 
2289 	return( iNumberOfPagesToEmail );
2290 }
2291 
2292 
ShutDownEmailList()2293 void ShutDownEmailList()
2294 {
2295 	// Loop through all the emails to delete them
2296 	Email* i = pEmailList;
2297 	pEmailList = 0;
2298 	while (i)
2299 	{
2300 		Email* const del = i;
2301 		i = i->Next;
2302 		delete del;
2303 	}
2304 	ClearPages();
2305 }
2306 
2307 
PreProcessEmail(Email * const m)2308 static void PreProcessEmail(Email* const m)
2309 {
2310 	// already processed?
2311 	if (pEmailPageInfo[0].pFirstRecord) return;
2312 
2313 	if (!pMessageRecordList)
2314 	{ // List doesn't exist, reload
2315 		INT32 const offset = m->usOffset;
2316 		for (INT32 i = 0; i != m->usLength; ++i)
2317 		{
2318 			// read one record from email file
2319 			ST::string str = LoadEMailText(offset + i);
2320 			AddEmailRecordToList(str);
2321 		}
2322 		PreviousMail = CurrentMail;
2323 	}
2324 
2325 	Record* start = pMessageRecordList;
2326 	if (start && m->usOffset != IMP_EMAIL_PROFILE_RESULTS)
2327 	{ // pass the subject line
2328 		start = start->Next;
2329 	}
2330 
2331 	// get number of pages to this email
2332 	giNumberOfPagesToCurrentEmail = GetNumberOfPagesToEmail();
2333 
2334 	INT32 h = 0;
2335 	for (Record const* i = start; i; i = i->Next)
2336 	{
2337 		// get the height of the string, ONLY!...must redisplay ON TOP OF background graphic
2338 		h += IanWrappedStringHeight(MESSAGE_WIDTH, MESSAGE_GAP, MESSAGE_FONT, i->pRecord);
2339 	}
2340 
2341 	// set iViewerY so to center the viewer
2342 	iViewerPositionY = (LAPTOP_SCREEN_LR_Y - 2 * VIEWER_Y - 2 * VIEWER_MESSAGE_BODY_START_Y  - h) / 2;
2343 	if (iViewerPositionY < 0) iViewerPositionY = 0;
2344 
2345 	UINT16 const line_h = GetFontHeight(MESSAGE_FONT);
2346 	if (h < line_h * MIN_MESSAGE_HEIGHT_IN_LINES)
2347 	{
2348 		// Use minimum height
2349 		h = line_h * MIN_MESSAGE_HEIGHT_IN_LINES;
2350 	}
2351 	else if (h > MAX_EMAIL_MESSAGE_PAGE_SIZE)
2352 	{
2353 		// Message to big to fit on page
2354 		h = MAX_EMAIL_MESSAGE_PAGE_SIZE;
2355 	}
2356 
2357 	// set total height to height of records displayed
2358 	iTotalHeight = h + 10;
2359 
2360 	INT32 page = 0;
2361 	if (iTotalHeight < MAX_EMAIL_MESSAGE_PAGE_SIZE)
2362 	{
2363 		EmailPageInfoStruct& info = pEmailPageInfo[page];
2364 		info.pFirstRecord = start;
2365 		info.iPageNumber  = 0;
2366 
2367 		Record* last_record = 0;
2368 		for (Record* i = start; i; i = i->Next)
2369 		{
2370 			last_record = i;
2371 		}
2372 
2373 		// only one record to this email?..then set next to null
2374 		info.pLastRecord = last_record == info.pFirstRecord ? 0 : last_record;
2375 		++page;
2376 	}
2377 	else
2378 	{
2379 		// more than one page
2380 		for (Record* i; (i = GetFirstRecordOnThisPage(start, page)); ++page)
2381 		{
2382 			EmailPageInfoStruct& info = pEmailPageInfo[page];
2383 			info.pFirstRecord = i;
2384 			info.iPageNumber  = page;
2385 
2386 			// go to the right record
2387 			Record* last_record = 0;
2388 			INT32   y           = 0;
2389 			for (; i; i = last_record = i->Next)
2390 			{
2391 				UINT16 const h = IanWrappedStringHeight(MESSAGE_WIDTH, MESSAGE_GAP, MESSAGE_FONT, i->pRecord);
2392 				// Gonna get cut off?  End now
2393 				if (y + h > MAX_EMAIL_MESSAGE_PAGE_SIZE) break;
2394 				y += h;
2395 			}
2396 
2397 			info.pLastRecord = last_record == info.pFirstRecord ? 0 : last_record;
2398 		}
2399 	}
2400 
2401 	EmailPageInfoStruct& info = pEmailPageInfo[page];
2402 	info.pFirstRecord = 0;
2403 	info.pLastRecord  = 0;
2404 	info.iPageNumber  = page;
2405 }
2406 
2407 
ModifyInsuranceEmails(UINT16 usMessageId,Email * pMail,UINT8 ubNumberOfRecords)2408 static void ModifyInsuranceEmails(UINT16 usMessageId, Email* pMail, UINT8 ubNumberOfRecords)
2409 {
2410 	UINT8	ubCnt;
2411 
2412 	for( ubCnt=0; ubCnt<ubNumberOfRecords; ubCnt++)
2413 	{
2414 		// read one record from email file
2415 		ST::string pString = LoadEMailText(usMessageId);
2416 
2417 		//Replace the $MERCNAME$ and $AMOUNT$ with the mercs name and the amountm if the string contains the keywords.
2418 		pString = ReplaceMercNameAndAmountWithProperData( pString, pMail );
2419 
2420 		// add to list
2421 		AddEmailRecordToList( pString );
2422 
2423 		usMessageId++;
2424 	}
2425 
2426 	PreviousMail = CurrentMail;
2427 }
2428 
2429 
ReplaceMercNameAndAmountWithProperData(const ST::string & pFinishedString,const Email * pMail)2430 static ST::string ReplaceMercNameAndAmountWithProperData(const ST::string& pFinishedString, const Email* pMail)
2431 {
2432 	const ST::string sMercName = "$MERCNAME$"; //Doesnt need to be translated, inside Email.txt and will be replaced by the mercs name
2433 	const ST::string sAmount = "$AMOUN$"; //Doesnt need to be translated, inside Email.txt and will be replaced by a dollar amount
2434 
2435 	ST::string mercName = gMercProfiles[ pMail->uiSecondData ].zName;
2436 	ST::string amount = SPrintMoney(pMail->iFirstData);
2437 	return pFinishedString.replace(sAmount, amount).replace(sMercName, mercName);
2438 }
2439 
2440