1 #include "Directories.h"
2 #include "Font.h"
3 #include "HImage.h"
4 #include "Laptop.h"
5 #include "Insurance.h"
6 #include "Insurance_Contract.h"
7 #include "MercPortrait.h"
8 #include "Merc_Hiring.h"
9 #include "MessageBoxScreen.h"
10 #include "VObject.h"
11 #include "WordWrap.h"
12 #include "Cursors.h"
13 #include "Insurance_Text.h"
14 #include "Soldier_Profile.h"
15 #include "Overhead.h"
16 #include "Soldier_Add.h"
17 #include "Game_Clock.h"
18 #include "Finances.h"
19 #include "History.h"
20 #include "Game_Event_Hook.h"
21 #include "LaptopSave.h"
22 #include "Text.h"
23 #include "Random.h"
24 #include "Strategic_Status.h"
25 #include "Assignments.h"
26 #include "Map_Screen_Interface.h"
27 #include "Button_System.h"
28 #include "Video.h"
29 #include "VSurface.h"
30 #include "MemMan.h"
31 #include "Debug.h"
32 #include "ScreenIDs.h"
33 #include "JAScreens.h"
34 #include "Font_Control.h"
35 #include "EMail.h"
36 
37 #include <string_theory/format>
38 #include <string_theory/string>
39 
40 
41 #define INS_CTRCT_ORDER_GRID_WIDTH			132
42 #define INS_CTRCT_ORDER_GRID_HEIGHT			216
43 
44 #define INS_CTRCT_ORDER_GRID_X				(LAPTOP_SCREEN_UL_X         +  76)
45 #define INS_CTRCT_ORDER_GRID_Y				(LAPTOP_SCREEN_WEB_UL_Y     + 126)
46 #define INS_CTRCT_ORDER_GRID_OFFSET_X			(INS_CTRCT_ORDER_GRID_WIDTH +   2)
47 
48 #define INS_CTRCT_OG_FACE_OFFSET_X			5
49 #define INS_CTRCT_OG_FACE_OFFSET_Y			4
50 
51 
52 #define INS_CTRCT_OG_NICK_NAME_OFFSET_X			57
53 #define INS_CTRCT_OG_NICK_NAME_OFFSET_Y			13
54 
55 #define INS_CTRCT_OG_HAS_CONTRACT_OFFSET_X		INS_CTRCT_OG_NICK_NAME_OFFSET_X
56 #define INS_CTRCT_OG_HAS_CONTRACT_OFFSET_Y		INS_CTRCT_OG_NICK_NAME_OFFSET_Y + 13
57 
58 #define INS_CTRCT_TITLE_Y				(48 + LAPTOP_SCREEN_WEB_UL_Y)//52 + LAPTOP_SCREEN_WEB_UL_Y
59 
60 #define INS_CTRCT_FIRST_BULLET_TEXT_X			86 + LAPTOP_SCREEN_UL_X
61 #define INS_CTRCT_FIRST_BULLET_TEXT_Y			65 + LAPTOP_SCREEN_WEB_UL_Y
62 
63 #define INS_CTRCT_SECOND_BULLET_TEXT_X			INS_CTRCT_FIRST_BULLET_TEXT_X
64 #define INS_CTRCT_SECOND_BULLET_TEXT_Y			93 + LAPTOP_SCREEN_WEB_UL_Y
65 
66 #define INS_CTRCT_INTSRUCTION_TEXT_WIDTH		375
67 
68 #define INS_CTRCT_RED_BAR_UNDER_INSTRUCTION_TEXT_Y	123 + LAPTOP_SCREEN_WEB_UL_Y
69 
70 #define INS_CTRCT_EMPLYMNT_CNTRCT_TEXT_OFFSET_X		4
71 #define INS_CTRCT_EMPLYMNT_CNTRCT_TEXT_OFFSET_Y		54
72 
73 #define INS_CTRCT_LENGTH_OFFSET_X			INS_CTRCT_EMPLYMNT_CNTRCT_TEXT_OFFSET_X
74 #define INS_CTRCT_LENGTH_OFFSET_Y			71
75 
76 #define INS_CTRCT_DAYS_REMAINING_OFFSET_Y		87
77 
78 #define INS_CTRCT_INSURANCE_CNTRCT_OFFSET_Y		108
79 
80 #define INS_CTRCT_PREMIUM_OWING_OFFSET_Y		160
81 
82 
83 #define INS_CTRCT_OG_BOX_OFFSET_X			92
84 #define INS_CTRCT_OG_BOX_WIDTH				35
85 
86 #define INS_CTRCT_ACCEPT_BTN_X				( 132 / 2 - 43 / 2 ) //6
87 #define INS_CTRCT_ACCEPT_BTN_Y				193
88 
89 #define INS_CTRCT_CLEAR_BTN_X				86
90 
91 #define INS_CTRCT_BOTTON_LINK_Y				351 + LAPTOP_SCREEN_WEB_UL_Y
92 
93 #define INS_CTRCT_BOTTOM_LINK_RED_BAR_X			171 + LAPTOP_SCREEN_UL_X
94 #define INS_CTRCT_BOTTON_LINK_RED_BAR_Y			INS_CTRCT_BOTTON_LINK_Y + 41
95 
96 
97 #define INS_CTRCT_BOTTOM_LINK_RED_BAR_OFFSET		117
98 
99 #define INS_CTRCT_BOTTOM_LINK_RED_WIDTH			97
100 
101 #define INS_CTRCT_CONTRACT_STATUS_TEXT_WIDTH		74
102 
103 // this is the percentage of daily salary used as a base to calculate daily insurance premiums
104 #define INSURANCE_PREMIUM_RATE				5
105 
106 #define INS_CTRCT_SKILL_BASE				42
107 #define INS_CTRCT_FITNESS_BASE				85
108 #define INS_CTRCT_EXP_LEVEL_BASE			3
109 #define INS_CTRCT_SURVIVAL_BASE				90
110 
111 
112 static SGPVObject* guiInsOrderGridImage;
113 static SGPVObject* guiInsOrderBulletImage;
114 
115 static UINT8 gubNumberofDisplayedInsuranceGrids;
116 
117 static BOOLEAN gfChangeInsuranceFormButtons = FALSE;
118 
119 static SOLDIERTYPE* g_insurance_merc_array[20];
120 static UINT         g_n_insurable_mercs;
121 UINT16              gusCurrentInsuranceMercIndex;
122 
123 
124 //link to the varios pages
125 static MOUSE_REGION gSelectedInsuranceContractLinkRegion[2];
126 
127 static BUTTON_PICS* guiInsContractPrevButtonImage;
128 static void BtnInsContractPrevButtonCallback(GUI_BUTTON *btn, INT32 reason);
129 static GUIButtonRef guiInsContractPrevBackButton;
130 
131 static BUTTON_PICS* guiInsContractNextButtonImage;
132 static void BtnInsContractNextButtonCallBack(GUI_BUTTON *btn, INT32 reason);
133 static GUIButtonRef guiInsContractNextBackButton;
134 
135 
136 //Graphic for Accept, Clear button for form 1
137 static BUTTON_PICS* guiInsuranceAcceptClearFormButtonImage;
138 
139 
140 struct InsuranceInfo
141 {
142 	GUIButtonRef button;
143 	SOLDIERTYPE* soldier;
144 };
145 
146 
147 static InsuranceInfo insurance_info[3];
148 
149 
SetNumberOfDisplayedInsuranceMercs(void)150 static void SetNumberOfDisplayedInsuranceMercs(void)
151 {
152 	gubNumberofDisplayedInsuranceGrids = MIN(g_n_insurable_mercs - gusCurrentInsuranceMercIndex, 3);
153 }
154 
155 
MakeButtonBig(BUTTON_PICS * img,const ST::string & text,INT16 x,const GUI_CALLBACK click,INT8 offset_x)156 static GUIButtonRef MakeButtonBig(BUTTON_PICS* img, const ST::string& text, INT16 x, const GUI_CALLBACK click, INT8 offset_x)
157 {
158 	const INT16 text_col   = INS_FONT_COLOR;
159 	const INT16 shadow_col = INS_FONT_SHADOW;
160 	GUIButtonRef const btn = CreateIconAndTextButton(img, text, INS_FONT_BIG, text_col, shadow_col, text_col, shadow_col, x, INS_INFO_ARROW_BUTTON_Y, MSYS_PRIORITY_HIGH, click);
161 	btn->SetCursor(CURSOR_WWW);
162 	btn->SpecifyTextOffsets(offset_x, 16, FALSE);
163 	return btn;
164 }
165 
166 
167 static void BuildInsuranceArray(void);
168 static void CreateDestroyInsuranceContractFormButtons(BOOLEAN fCreate);
169 static void SelectInsuranceContractRegionCallBack(MOUSE_REGION* pRegion, INT32 iReason);
170 
171 
EnterInsuranceContract()172 void EnterInsuranceContract()
173 {
174 	UINT16 usPosX,i;
175 
176 	//build the list of mercs that are can be displayed
177 	BuildInsuranceArray();
178 
179 	SetNumberOfDisplayedInsuranceMercs();
180 
181 	InitInsuranceDefaults();
182 
183 
184 
185 	// load the Insurance title graphic and add it
186 	guiInsOrderGridImage = AddVideoObjectFromFile(LAPTOPDIR "/insordergrid.sti");
187 
188 	// load the Insurance bullet graphic and add it
189 	guiInsOrderBulletImage = AddVideoObjectFromFile(LAPTOPDIR "/bullet.sti");
190 
191 
192 	usPosX = INS_CTRCT_BOTTOM_LINK_RED_BAR_X;
193 	for(i=0; i<2; i++)
194 	{
195 		MSYS_DefineRegion(&gSelectedInsuranceContractLinkRegion[i], usPosX, INS_CTRCT_BOTTON_LINK_RED_BAR_Y-37,
196 					(UINT16)(usPosX + INS_CTRCT_BOTTOM_LINK_RED_WIDTH),
197 					INS_CTRCT_BOTTON_LINK_RED_BAR_Y+2, MSYS_PRIORITY_HIGH,
198 					CURSOR_WWW, MSYS_NO_CALLBACK, SelectInsuranceContractRegionCallBack);
199 		MSYS_SetRegionUserData( &gSelectedInsuranceContractLinkRegion[i], 0, i );
200 
201 		usPosX += INS_CTRCT_BOTTOM_LINK_RED_BAR_OFFSET;
202 	}
203 
204 
205 	//left arrow
206 	guiInsContractPrevButtonImage	= LoadButtonImage(LAPTOPDIR "/insleftbutton.sti", 2, 0, -1, 1, -1);
207 	guiInsContractPrevBackButton  = MakeButtonBig(guiInsContractPrevButtonImage, InsContractText[INS_CONTRACT_PREVIOUS], INS_INFO_LEFT_ARROW_BUTTON_X, BtnInsContractPrevButtonCallback, 17);
208 
209 	//Right arrow
210 	guiInsContractNextButtonImage	= LoadButtonImage(LAPTOPDIR "/insrightbutton.sti", 2, 0, -1, 1, -1);
211 	guiInsContractNextBackButton  = MakeButtonBig(guiInsContractNextButtonImage, InsContractText[INS_CONTRACT_NEXT], INS_INFO_RIGHT_ARROW_BUTTON_X, BtnInsContractNextButtonCallBack, 18);
212 
213 	//create the new set of buttons
214 	CreateDestroyInsuranceContractFormButtons( TRUE );
215 
216 	//RenderInsuranceContract();
217 }
218 
219 
ExitInsuranceContract()220 void ExitInsuranceContract()
221 {
222 	UINT8 i;
223 
224 	RemoveInsuranceDefaults();
225 
226 	DeleteVideoObject(guiInsOrderGridImage);
227 	DeleteVideoObject(guiInsOrderBulletImage);
228 
229 	for(i=0; i<2; i++)
230 		MSYS_RemoveRegion( &gSelectedInsuranceContractLinkRegion[i]);
231 
232 	//the previous button
233 	UnloadButtonImage( guiInsContractPrevButtonImage );
234 	RemoveButton( guiInsContractPrevBackButton );
235 
236 	//the next button
237 	UnloadButtonImage( guiInsContractNextButtonImage );
238 	RemoveButton( guiInsContractNextBackButton );
239 
240 	CreateDestroyInsuranceContractFormButtons( FALSE );
241 }
242 
243 
244 static void EnableDisableInsuranceContractAcceptButtons(void);
245 
246 
HandleInsuranceContract()247 void HandleInsuranceContract()
248 {
249 	if( gfChangeInsuranceFormButtons )
250 	{
251 		//remove the old buttons from the page
252 		CreateDestroyInsuranceContractFormButtons( FALSE );
253 
254 		//Get the new number of displayed insurance grids
255 		SetNumberOfDisplayedInsuranceMercs();
256 
257 		//create the new set of buttons
258 		CreateDestroyInsuranceContractFormButtons( TRUE );
259 
260 		//reset the flag
261 		gfChangeInsuranceFormButtons = FALSE;
262 
263 		//force a redraw of the screen to erase the old buttons
264 		fPausedReDrawScreenFlag = TRUE;
265 		RenderInsuranceContract();
266 
267 		MarkButtonsDirty();
268 	}
269 
270 	EnableDisableInsuranceContractAcceptButtons();
271 }
272 
273 
274 static BOOLEAN AreAnyAimMercsOnTeam(void);
275 static void DisableInsuranceContractNextPreviousbuttons(void);
276 static BOOLEAN DisplayOrderGrid(UINT8 ubGridNumber, SOLDIERTYPE*);
277 static void InsContractNoMercsPopupCallBack(MessageBoxReturnValue);
278 
279 
RenderInsuranceContract()280 void RenderInsuranceContract()
281 {
282 	ST::string sText;
283 	UINT16  usNextMercID;
284 	UINT16  usPosX;
285 
286 
287 	SetFontShadow( INS_FONT_SHADOW );
288 
289 	DisplayInsuranceDefaults();
290 
291 	//disable the next or previous button depending on how many more mercs we have to display
292 	DisableInsuranceContractNextPreviousbuttons();
293 
294 	usPosX = INS_CTRCT_BOTTOM_LINK_RED_BAR_X;
295 
296 	//Display the red bar under the link at the bottom.  and the text
297 	DisplaySmallRedLineWithShadow( usPosX, INS_CTRCT_BOTTON_LINK_RED_BAR_Y, (UINT16)(usPosX+INS_CTRCT_BOTTOM_LINK_RED_WIDTH), INS_CTRCT_BOTTON_LINK_RED_BAR_Y);
298 	DisplayWrappedString(usPosX, INS_CTRCT_BOTTON_LINK_Y + 18, INS_CTRCT_BOTTOM_LINK_RED_WIDTH, 2, INS_FONT_MED, INS_FONT_COLOR, pMessageStrings[MSG_HOMEPAGE], FONT_MCOLOR_BLACK, CENTER_JUSTIFIED);
299 
300 	usPosX += INS_CTRCT_BOTTOM_LINK_RED_BAR_OFFSET;
301 
302 	//Display the red bar under the link at the bottom.  and the text
303 	DisplaySmallRedLineWithShadow( usPosX, INS_CTRCT_BOTTON_LINK_RED_BAR_Y, (UINT16)(usPosX+INS_CTRCT_BOTTOM_LINK_RED_WIDTH), INS_CTRCT_BOTTON_LINK_RED_BAR_Y);
304 	sText = GetInsuranceText(INS_SNGL_HOW_DOES_INS_WORK);
305 	DisplayWrappedString( usPosX, INS_CTRCT_BOTTON_LINK_Y + 12, INS_CTRCT_BOTTOM_LINK_RED_WIDTH, 2, INS_FONT_MED, INS_FONT_COLOR, sText, FONT_MCOLOR_BLACK, CENTER_JUSTIFIED);
306 
307 
308 	//Display the title slogan
309 	sText = GetInsuranceText(INS_SNGL_ENTERING_REVIEWING_CLAIM);
310 	DrawTextToScreen(sText, LAPTOP_SCREEN_UL_X, INS_CTRCT_TITLE_Y, LAPTOP_SCREEN_LR_X - LAPTOP_SCREEN_UL_X, INS_FONT_BIG, INS_FONT_COLOR, FONT_MCOLOR_BLACK, CENTER_JUSTIFIED);
311 
312 	BltVideoObject(FRAME_BUFFER, guiInsOrderBulletImage, 0, INS_CTRCT_FIRST_BULLET_TEXT_X, INS_CTRCT_FIRST_BULLET_TEXT_Y);
313 
314 	//Display the first instruction sentence
315 	sText = GetInsuranceText(INS_MLTI_TO_PURCHASE_INSURANCE);
316 	DisplayWrappedString(INS_CTRCT_FIRST_BULLET_TEXT_X + INSURANCE_BULLET_TEXT_OFFSET_X, INS_CTRCT_FIRST_BULLET_TEXT_Y, INS_CTRCT_INTSRUCTION_TEXT_WIDTH, 2, INS_FONT_MED, INS_FONT_COLOR, sText, FONT_MCOLOR_BLACK, LEFT_JUSTIFIED);
317 
318 	BltVideoObject(FRAME_BUFFER, guiInsOrderBulletImage, 0, INS_CTRCT_FIRST_BULLET_TEXT_X, INS_CTRCT_SECOND_BULLET_TEXT_Y);
319 
320 	//Display the second instruction sentence
321 	sText = GetInsuranceText(INS_MLTI_ONCE_SATISFIED_CLICK_ACCEPT);
322 	DisplayWrappedString(INS_CTRCT_FIRST_BULLET_TEXT_X + INSURANCE_BULLET_TEXT_OFFSET_X, INS_CTRCT_SECOND_BULLET_TEXT_Y, INS_CTRCT_INTSRUCTION_TEXT_WIDTH, 2, INS_FONT_MED, INS_FONT_COLOR, sText, FONT_MCOLOR_BLACK, LEFT_JUSTIFIED);
323 
324 	//Display the red bar under the instruction text
325 	DisplaySmallRedLineWithShadow( INS_CTRCT_FIRST_BULLET_TEXT_X, INS_CTRCT_RED_BAR_UNDER_INSTRUCTION_TEXT_Y, INS_CTRCT_FIRST_BULLET_TEXT_X+INS_CTRCT_INTSRUCTION_TEXT_WIDTH, INS_CTRCT_RED_BAR_UNDER_INSTRUCTION_TEXT_Y);
326 
327 
328 	usNextMercID =	gusCurrentInsuranceMercIndex;
329 	const UINT count_insurance_grids = gubNumberofDisplayedInsuranceGrids;
330 	for (UINT i = 0; i < count_insurance_grids; ++i)
331 	{
332 		SOLDIERTYPE* const s = g_insurance_merc_array[usNextMercID++];
333 		DisplayOrderGrid(i, s);
334 	}
335 
336 	//if there are no valid mercs to insure
337 	if (count_insurance_grids == 0)
338 	{
339 		//if there where AIM mercs ( on short contract )
340 		if( AreAnyAimMercsOnTeam( ) )
341 		{
342 			//Display Error Message, all aim mercs are on short contract
343 			sText = GetInsuranceText(INS_MLTI_ALL_AIM_MERCS_ON_SHORT_CONTRACT);
344 		}
345 		else
346 		{
347 			//Display Error Message, no valid mercs
348 			sText = GetInsuranceText(INS_MLTI_NO_QUALIFIED_MERCS);
349 		}
350 		DoLapTopMessageBox(MSG_BOX_RED_ON_WHITE, sText, LAPTOP_SCREEN, MSG_BOX_FLAG_OK, InsContractNoMercsPopupCallBack);
351 	}
352 
353 
354 
355 	SetFontShadow(DEFAULT_SHADOW);
356 	MarkButtonsDirty( );
357 	RenderWWWProgramTitleBar( );
358 	InvalidateRegion(LAPTOP_SCREEN_UL_X,LAPTOP_SCREEN_WEB_UL_Y,LAPTOP_SCREEN_LR_X,LAPTOP_SCREEN_WEB_LR_Y);
359 }
360 
361 
BtnInsContractPrevButtonCallback(GUI_BUTTON * btn,INT32 reason)362 static void BtnInsContractPrevButtonCallback(GUI_BUTTON *btn, INT32 reason)
363 {
364 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
365 	{
366 		if (gusCurrentInsuranceMercIndex > 2) gusCurrentInsuranceMercIndex -= 3;
367 		// signal that we want to change the number of forms on the page
368 		gfChangeInsuranceFormButtons = TRUE;
369 	}
370 }
371 
372 
BtnInsContractNextButtonCallBack(GUI_BUTTON * btn,INT32 reason)373 static void BtnInsContractNextButtonCallBack(GUI_BUTTON *btn, INT32 reason)
374 {
375 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
376 	{
377 		gusCurrentInsuranceMercIndex += 3;
378 		// signal that we want to change the number of forms on the page
379 		gfChangeInsuranceFormButtons = TRUE;
380 	}
381 }
382 
383 
384 static INT32 CalculateSoldiersInsuranceContractLength(const SOLDIERTYPE* pSoldier);
385 static BOOLEAN CanSoldierExtendInsuranceContract(const SOLDIERTYPE* pSoldier);
386 static UINT32 GetTimeRemainingOnSoldiersContract(const SOLDIERTYPE* pSoldier);
387 static UINT32 GetTimeRemainingOnSoldiersInsuranceContract(const SOLDIERTYPE* pSoldier);
388 
389 
DisplayOrderGrid(const UINT8 ubGridNumber,SOLDIERTYPE * const pSoldier)390 static BOOLEAN DisplayOrderGrid(const UINT8 ubGridNumber, SOLDIERTYPE* const pSoldier)
391 try
392 {
393 	INT32   iCostOfContract=0;
394 	ST::string sText;
395 	BOOLEAN fDisplayMercContractStateTextColorInRed = FALSE;
396 
397 	Assert(ubGridNumber < 3);
398 
399 	insurance_info[ubGridNumber].soldier = pSoldier;
400 
401 	const INT32 dx = INS_CTRCT_ORDER_GRID_X + INS_CTRCT_ORDER_GRID_OFFSET_X * ubGridNumber;
402 	const INT32 dy = INS_CTRCT_ORDER_GRID_Y;
403 
404 	BltVideoObject(FRAME_BUFFER, guiInsOrderGridImage, 0, dx, dy);
405 
406 	MERCPROFILESTRUCT const& p       = GetProfile(pSoldier->ubProfile);
407 	bool              const  is_dead = IsMercDead(p);
408 	{
409 		// load the mercs face graphic and add it
410 		AutoSGPVObject uiInsMercFaceImage(LoadSmallPortrait(p));
411 
412 		//if the merc is dead, shade the face red
413 		if (is_dead)
414 		{
415 			//if the merc is dead
416 			//shade the face red, (to signify that he is dead)
417 			uiInsMercFaceImage->pShades[0] = Create16BPPPaletteShaded(uiInsMercFaceImage->Palette(), DEAD_MERC_COLOR_RED, DEAD_MERC_COLOR_GREEN, DEAD_MERC_COLOR_BLUE, TRUE);
418 
419 			//set the red pallete to the face
420 			uiInsMercFaceImage->CurrentShade(0);
421 		}
422 
423 		//Get and display the mercs face
424 		BltVideoObject(FRAME_BUFFER, uiInsMercFaceImage.get(), 0, dx + INS_CTRCT_OG_FACE_OFFSET_X, dy + INS_CTRCT_OG_FACE_OFFSET_Y);
425 	}
426 
427 	//display the mercs nickname
428 	DrawTextToScreen(p.zNickname, dx + INS_CTRCT_OG_NICK_NAME_OFFSET_X, dy + INS_CTRCT_OG_NICK_NAME_OFFSET_Y, 0, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, LEFT_JUSTIFIED);
429 
430 	//Get the text to display the mercs current insurance contract status
431 	if (is_dead)
432 	{
433 		//if the merc has a contract
434 		if( pSoldier->usLifeInsurance )
435 		{
436 			//Display the contract text
437 			sText = GetInsuranceText(INS_SNGL_DEAD_WITH_CONTRACT);
438 		}
439 		else
440 		{
441 			//Display the contract text
442 			sText = GetInsuranceText(INS_SNGL_DEAD_NO_CONTRACT);
443 		}
444 		DisplayWrappedString(dx + INS_CTRCT_OG_HAS_CONTRACT_OFFSET_X, dy + INS_CTRCT_OG_HAS_CONTRACT_OFFSET_Y, INS_CTRCT_CONTRACT_STATUS_TEXT_WIDTH, 2, INS_FONT_SMALL, INS_FONT_COLOR_RED, sText, FONT_MCOLOR_BLACK, LEFT_JUSTIFIED);
445 	}
446 	else
447 	{
448 		//if the merc has a contract
449 		if( pSoldier->usLifeInsurance )
450 		{
451 			//if the soldier can extend their insurance
452 			if( CanSoldierExtendInsuranceContract( pSoldier ) )
453 			{
454 				//Display the contract text
455 				sText = GetInsuranceText(INS_SNGL_PARTIALLY_INSURED);
456 				fDisplayMercContractStateTextColorInRed = TRUE;
457 			}
458 			else
459 			{
460 				//Display the contract text
461 				sText = GetInsuranceText(INS_SNGL_CONTRACT);
462 				fDisplayMercContractStateTextColorInRed = FALSE;
463 			}
464 		}
465 		else
466 		{
467 			//Display the contract text
468 			sText = GetInsuranceText(INS_SNGL_NOCONTRACT);
469 			fDisplayMercContractStateTextColorInRed = TRUE;
470 		}
471 		const UINT8 colour = (fDisplayMercContractStateTextColorInRed ? INS_FONT_COLOR_RED : INS_FONT_COLOR);
472 		DisplayWrappedString(dx + INS_CTRCT_OG_HAS_CONTRACT_OFFSET_X, dy + INS_CTRCT_OG_HAS_CONTRACT_OFFSET_Y, INS_CTRCT_CONTRACT_STATUS_TEXT_WIDTH, 2, INS_FONT_SMALL, colour, sText, FONT_MCOLOR_BLACK, LEFT_JUSTIFIED);
473 	}
474 
475 
476 
477 	//Display the Emplyment contract text
478 	sText = GetInsuranceText(INS_SNGL_EMPLOYMENT_CONTRACT);
479 	DrawTextToScreen(sText, dx + INS_CTRCT_EMPLYMNT_CNTRCT_TEXT_OFFSET_X, dy + INS_CTRCT_EMPLYMNT_CNTRCT_TEXT_OFFSET_Y, INS_CTRCT_ORDER_GRID_WIDTH, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, CENTER_JUSTIFIED);
480 
481 	//Display the merc contract Length text
482 	sText = GetInsuranceText(INS_SNGL_LENGTH);
483 	DrawTextToScreen(sText, dx + INS_CTRCT_LENGTH_OFFSET_X, dy + INS_CTRCT_LENGTH_OFFSET_Y, 0, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, LEFT_JUSTIFIED);
484 
485 	//Display the mercs contract length
486 	sText = ST::format("{}", pSoldier->iTotalContractLength);
487 	DrawTextToScreen(sText, dx + INS_CTRCT_OG_BOX_OFFSET_X, dy + INS_CTRCT_LENGTH_OFFSET_Y, INS_CTRCT_OG_BOX_WIDTH, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, RIGHT_JUSTIFIED);
488 
489 
490 	//Display the days remaining for the emplyment contract text
491 	sText = GetInsuranceText(INS_SNGL_DAYS_REMAINING);
492 	DrawTextToScreen(sText, dx + INS_CTRCT_LENGTH_OFFSET_X, dy + INS_CTRCT_DAYS_REMAINING_OFFSET_Y, 0, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, LEFT_JUSTIFIED);
493 
494 
495 	//display the amount of time the merc has left on their Regular contract
496 	if (is_dead)
497 		sText = pMessageStrings[MSG_LOWERCASE_NA];
498 	else
499 		sText = ST::format("{}", GetTimeRemainingOnSoldiersContract( pSoldier ));
500 
501 	DrawTextToScreen(sText, dx + INS_CTRCT_OG_BOX_OFFSET_X, dy + INS_CTRCT_DAYS_REMAINING_OFFSET_Y, INS_CTRCT_OG_BOX_WIDTH, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, RIGHT_JUSTIFIED);
502 
503 	//Display the Insurqance contract
504 	sText = GetInsuranceText(INS_SNGL_INSURANCE_CONTRACT);
505 	DrawTextToScreen(sText, dx + INS_CTRCT_EMPLYMNT_CNTRCT_TEXT_OFFSET_X, dy + INS_CTRCT_INSURANCE_CNTRCT_OFFSET_Y, INS_CTRCT_ORDER_GRID_WIDTH, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, CENTER_JUSTIFIED);
506 
507 
508 	sText = GetInsuranceText(INS_SNGL_LENGTH);
509 	DrawTextToScreen(sText, dx + INS_CTRCT_LENGTH_OFFSET_X, dy + INS_CTRCT_LENGTH_OFFSET_Y + 54, 0, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, LEFT_JUSTIFIED);
510 
511 	//Display the insurance days remaining text
512 	sText = GetInsuranceText(INS_SNGL_DAYS_REMAINING);
513 	DrawTextToScreen(sText, dx + INS_CTRCT_LENGTH_OFFSET_X, dy + INS_CTRCT_DAYS_REMAINING_OFFSET_Y + 54, 0, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, LEFT_JUSTIFIED);
514 
515 
516 	//
517 	//display the amount of time the merc has left on the insurance contract
518 	//
519 
520 	//if the soldier has insurance, disply the length of time the merc has left
521 	if (is_dead)
522 		sText = pMessageStrings[MSG_LOWERCASE_NA];
523 	else if( pSoldier->usLifeInsurance != 0 )
524 		sText = ST::format("{}", GetTimeRemainingOnSoldiersInsuranceContract( pSoldier ));
525 
526 	else
527 		sText = ST::format("{}", 0);
528 
529 	DrawTextToScreen(sText, dx + INS_CTRCT_OG_BOX_OFFSET_X, dy + INS_CTRCT_DAYS_REMAINING_OFFSET_Y + 54, INS_CTRCT_OG_BOX_WIDTH, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, RIGHT_JUSTIFIED);
530 
531 
532 	//
533 	// Calculate the insurance cost
534 	//
535 
536 	//if the soldier can get insurance, calculate a new cost
537 	if( CanSoldierExtendInsuranceContract( pSoldier ) )
538 	{
539 		iCostOfContract =CalculateInsuranceContractCost( CalculateSoldiersInsuranceContractLength( pSoldier ), pSoldier->ubProfile );
540 	}
541 
542 	else
543 	{
544 		iCostOfContract = 0;
545 	}
546 
547 	if( iCostOfContract < 0 )
548 	{
549 		//shouldnt get in here now since we can longer give refunds
550 		SLOGA("DisplayOrderGrid: negative contract cost");
551 	}
552 	else
553 	{
554 		//Display the premium owing text
555 		sText = GetInsuranceText(INS_SNGL_PREMIUM_OWING);
556 		DrawTextToScreen(sText, dx + INS_CTRCT_EMPLYMNT_CNTRCT_TEXT_OFFSET_X, dy + INS_CTRCT_PREMIUM_OWING_OFFSET_Y, INS_CTRCT_ORDER_GRID_WIDTH, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, CENTER_JUSTIFIED);
557 
558 		//display the amount of refund
559 		sText = SPrintMoney(iCostOfContract);
560 	}
561 
562 
563 	if (is_dead)
564 	{
565 		sText = "$0";
566 	}
567 	//display the amount owing
568 	DrawTextToScreen(sText, dx + 32, dy + 179, 72, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, RIGHT_JUSTIFIED);
569 
570 
571 	//
572 	// Get the insurance contract length for the merc
573 	//
574 	sText = ST::format("{}", CalculateSoldiersInsuranceContractLength( pSoldier ));
575 
576 
577 	//Display the length of time the player can get for the insurance contract
578 	DrawTextToScreen(sText, dx + INS_CTRCT_OG_BOX_OFFSET_X, dy + INS_CTRCT_LENGTH_OFFSET_Y + 52 + 2, INS_CTRCT_OG_BOX_WIDTH, INS_FONT_MED, INS_FONT_COLOR, FONT_MCOLOR_BLACK, RIGHT_JUSTIFIED);
579 
580 	return( TRUE );
581 }
582 catch (...) { return FALSE; }
583 
584 
585 static void HandleAcceptButton(SOLDIERTYPE* s);
586 
587 
BtnInsuranceAcceptClearFormButtonCallback(GUI_BUTTON * btn,INT32 reason)588 static void BtnInsuranceAcceptClearFormButtonCallback(GUI_BUTTON* btn, INT32 reason)
589 {
590 	if (reason & MSYS_CALLBACK_REASON_LBUTTON_UP)
591 	{
592 		UINT         const idx = btn->GetUserData();
593 		SOLDIERTYPE* const s   = insurance_info[idx].soldier;
594 
595 		HandleAcceptButton(s);
596 
597 		//redraw the screen
598 		fPausedReDrawScreenFlag = TRUE;
599 	}
600 }
601 
602 
SelectInsuranceContractRegionCallBack(MOUSE_REGION * pRegion,INT32 iReason)603 static void SelectInsuranceContractRegionCallBack(MOUSE_REGION* pRegion, INT32 iReason)
604 {
605 	if (iReason & MSYS_CALLBACK_REASON_LBUTTON_UP)
606 	{
607 		UINT32 uiInsuranceLink = MSYS_GetRegionUserData( pRegion, 0 );
608 
609 		if( uiInsuranceLink == 0 )
610 			guiCurrentLaptopMode = LAPTOP_MODE_INSURANCE;
611 		else if( uiInsuranceLink == 1 )
612 			guiCurrentLaptopMode = LAPTOP_MODE_INSURANCE_INFO;
613 	}
614 }
615 
616 
DisableInsuranceContractNextPreviousbuttons(void)617 static void DisableInsuranceContractNextPreviousbuttons(void)
618 {
619 	//disable the next button if there is no more mercs to display
620 	EnableButton(guiInsContractNextBackButton, gusCurrentInsuranceMercIndex + gubNumberofDisplayedInsuranceGrids < g_n_insurable_mercs);
621 	//if we are currently displaying the first set of mercs, disable the previous button
622 	EnableButton(guiInsContractPrevBackButton, gusCurrentInsuranceMercIndex >= 3);
623 }
624 
625 
CreateDestroyInsuranceContractFormButtons(BOOLEAN fCreate)626 static void CreateDestroyInsuranceContractFormButtons(BOOLEAN fCreate)
627 {
628 	static BOOLEAN	fButtonsCreated = FALSE;
629 
630 	if( fCreate && !fButtonsCreated )
631 	{
632 		//place the 3 accept buttons for the different forms
633 
634 		//The accept button image
635 		guiInsuranceAcceptClearFormButtonImage = LoadButtonImage(LAPTOPDIR "/acceptclearbox.sti", 0, 1);
636 
637 		ST::string text = InsContractText[INS_CONTRACT_ACCEPT];
638 		const INT16          text_col   = INS_FONT_BTN_COLOR;
639 		const INT16          shadow_col = INS_FONT_BTN_SHADOW_COLOR;
640 		const INT16          y          = INS_CTRCT_ORDER_GRID_Y + INS_CTRCT_ACCEPT_BTN_Y;
641 		for (UINT i = 0; i != gubNumberofDisplayedInsuranceGrids; ++i)
642 		{
643 			const INT16 x   = INS_CTRCT_ORDER_GRID_X + INS_CTRCT_ORDER_GRID_OFFSET_X * i + INS_CTRCT_ACCEPT_BTN_X;
644 			GUIButtonRef const btn = CreateIconAndTextButton(guiInsuranceAcceptClearFormButtonImage, text, INS_FONT_MED, text_col, shadow_col, text_col, shadow_col, x, y, MSYS_PRIORITY_HIGH, BtnInsuranceAcceptClearFormButtonCallback);
645 			insurance_info[i].button = btn;
646 			btn->SetCursor(CURSOR_LAPTOP_SCREEN);
647 			btn->SetUserData(i);
648 		}
649 
650 		fButtonsCreated = TRUE;
651 	}
652 
653 	if( fButtonsCreated && ! fCreate )
654 	{
655 		//the accept image
656 		UnloadButtonImage(guiInsuranceAcceptClearFormButtonImage);
657 
658 		for (UINT i = 0; i != gubNumberofDisplayedInsuranceGrids; ++i)
659 		{
660 			RemoveButton(insurance_info[i].button);
661 		}
662 
663 		fButtonsCreated = FALSE;
664 	}
665 }
666 
667 
HandleAcceptButton(SOLDIERTYPE * s)668 static void HandleAcceptButton(SOLDIERTYPE* s)
669 {
670 	PurchaseOrExtendInsuranceForSoldier(s, CalculateSoldiersInsuranceContractLength(s));
671 
672 	RenderInsuranceContract();
673 }
674 
675 
676 // determines if a merc will run out of their insurance contract
DailyUpdateOfInsuredMercs()677 void DailyUpdateOfInsuredMercs()
678 {
679 	FOR_EACH_IN_TEAM(s, OUR_TEAM)
680 	{
681 		//if the merc has life insurance
682 		if (s->usLifeInsurance)
683 		{
684 			//if the merc wasn't just hired
685 			if ((INT16)GetWorldDay() != s->iStartOfInsuranceContract)
686 			{
687 				//if the contract has run out of time
688 				if (GetTimeRemainingOnSoldiersInsuranceContract(s) <= 0)
689 				{
690 					//if the soldier isn't dead
691 					if (!IsMercDead(GetProfile(s->ubProfile)))
692 					{
693 						s->usLifeInsurance = 0;
694 						s->iTotalLengthOfInsuranceContract = 0;
695 						s->iStartOfInsuranceContract = 0;
696 					}
697 				}
698 			}
699 		}
700 	}
701 }
702 
703 
704 #define MIN_INSURANCE_RATIO	0.1f
705 #define MAX_INSURANCE_RATIO	10.0f
706 
707 
708 static FLOAT DiffFromNormRatio(INT16 sThisValue, INT16 sNormalValue);
709 
710 
CalculateInsuranceContractCost(INT32 iLength,UINT8 ubMercID)711 INT32	CalculateInsuranceContractCost( INT32 iLength, UINT8 ubMercID )
712 {
713 	INT16  sTotalSkill=0;
714 	FLOAT  flSkillFactor, flFitnessFactor, flExpFactor, flSurvivalFactor;
715 	FLOAT  flRiskFactor;
716 	UINT32 uiDailyInsurancePremium;
717 	UINT32 uiTotalInsurancePremium;
718 	const SOLDIERTYPE* const pSoldier = FindSoldierByProfileIDOnPlayerTeam(ubMercID);
719 
720 	// only mercs with at least 2 days to go on their employment contract are insurable
721 	// def: 2/5/99.  However if they already have insurance is SHOULD be ok
722 	if( GetTimeRemainingOnSoldiersContract( pSoldier ) < 2 && !( pSoldier->usLifeInsurance != 0 && GetTimeRemainingOnSoldiersContract( pSoldier ) >= 1 ) )
723 	{
724 		return( 0 );
725 	}
726 
727 	//If the merc is currently being held captive, get out
728 	if (pSoldier->bAssignment == ASSIGNMENT_POW)
729 	{
730 		return( 0 );
731 	}
732 
733 	/*
734 	replaced with the above check
735 
736 	if (iLength < 2)
737 	{
738 		return(0);
739 	}*/
740 
741 	MERCPROFILESTRUCT const& p = GetProfile(ubMercID);
742 
743 	// calculate the degree of training
744 	sTotalSkill = (p.bMarksmanship + p.bMedical + p.bMechanical + p.bExplosive + p.bLeadership) / 5;
745 	flSkillFactor = DiffFromNormRatio( sTotalSkill, INS_CTRCT_SKILL_BASE );
746 
747 	// calc relative fitness level
748 	flFitnessFactor = DiffFromNormRatio(p.bLife, INS_CTRCT_FITNESS_BASE);
749 
750 	// calc relative experience
751 	flExpFactor = DiffFromNormRatio(p.bExpLevel, INS_CTRCT_EXP_LEVEL_BASE);
752 
753 	// calc player's survival rate (death rate subtracted from 100)
754 	flSurvivalFactor = DiffFromNormRatio( (INT16) (100 - CalcDeathRate()), INS_CTRCT_SURVIVAL_BASE );
755 
756 	// calculate the overall insurability risk factor for this merc by combining all the subfactors
757 	flRiskFactor = flSkillFactor * flFitnessFactor * flExpFactor * flSurvivalFactor;
758 
759 	// restrict the overall factor to within reasonable limits
760 	if (flRiskFactor < MIN_INSURANCE_RATIO)
761 	{
762 		flRiskFactor = MIN_INSURANCE_RATIO;
763 	}
764 	else
765 	if (flRiskFactor > MAX_INSURANCE_RATIO)
766 	{
767 		flRiskFactor = MAX_INSURANCE_RATIO;
768 	}
769 
770 	// premium depend on merc's salary, the base insurance rate, and the individual's risk factor at this time
771 	uiDailyInsurancePremium = (UINT32)(p.sSalary * INSURANCE_PREMIUM_RATE * flRiskFactor / 100 + 0.5);
772 	// multiply by the insurance contract length
773 	uiTotalInsurancePremium = uiDailyInsurancePremium * iLength;
774 
775 	return( uiTotalInsurancePremium );
776 }
777 
778 
779 // values passed in must be such that exceeding the normal value REDUCES insurance premiums
DiffFromNormRatio(INT16 sThisValue,INT16 sNormalValue)780 static FLOAT DiffFromNormRatio(INT16 sThisValue, INT16 sNormalValue)
781 {
782 	FLOAT flRatio;
783 
784 	if (sThisValue > 0)
785 	{
786 		flRatio = (FLOAT) sNormalValue / sThisValue;
787 
788 		// restrict each ratio to within a reasonable range
789 		if (flRatio < MIN_INSURANCE_RATIO)
790 		{
791 			flRatio = MIN_INSURANCE_RATIO;
792 		}
793 		else
794 		if (flRatio > MAX_INSURANCE_RATIO)
795 		{
796 			flRatio = MAX_INSURANCE_RATIO;
797 		}
798 	}
799 	else
800 	{
801 		// use maximum allowable ratio
802 		flRatio = MAX_INSURANCE_RATIO;
803 	}
804 
805 	return( flRatio );
806 }
807 
808 
InsContractNoMercsPopupCallBack(MessageBoxReturnValue const bExitValue)809 static void InsContractNoMercsPopupCallBack(MessageBoxReturnValue const bExitValue)
810 {
811 	// yes, so start over, else stay here and do nothing for now
812 	if( bExitValue == MSG_BOX_RETURN_OK )
813 	{
814 		guiCurrentLaptopMode = LAPTOP_MODE_INSURANCE;
815 	}
816 }
817 
818 
819 static BOOLEAN MercIsInsurable(const SOLDIERTYPE*);
820 
821 
BuildInsuranceArray(void)822 static void BuildInsuranceArray(void)
823 {
824 	g_n_insurable_mercs = 0;
825 
826 	// store profile #s of all insurable mercs in an array
827 	FOR_EACH_IN_TEAM(s, OUR_TEAM)
828 	{
829 		if (MercIsInsurable(s))
830 		{
831 			g_insurance_merc_array[g_n_insurable_mercs++] = s;
832 		}
833 	}
834 }
835 
836 
AddLifeInsurancePayout(SOLDIERTYPE * const pSoldier)837 void AddLifeInsurancePayout(SOLDIERTYPE* const pSoldier)
838 {
839 	UINT8  ubPayoutID;
840 	UINT32 uiTimeInMinutes;
841 	UINT32 uiCostPerDay;
842 	UINT32 uiDaysToPay;
843 
844 
845 	Assert(pSoldier != NULL);
846 	Assert(pSoldier->ubProfile != NO_PROFILE);
847 
848 	MERCPROFILESTRUCT const& p = GetProfile(pSoldier->ubProfile);
849 
850 	//if we need to add more array elements
851 	if (LaptopSaveInfo.pLifeInsurancePayouts.size() <= LaptopSaveInfo.ubNumberLifeInsurancePayoutUsed)
852 	{
853 		LaptopSaveInfo.pLifeInsurancePayouts.push_back(LIFE_INSURANCE_PAYOUT{});
854 	}
855 
856 	for( ubPayoutID = 0; ubPayoutID < LaptopSaveInfo.ubNumberLifeInsurancePayoutUsed; ubPayoutID++ )
857 	{
858 		//get an empty element in the array
859 		if( !LaptopSaveInfo.pLifeInsurancePayouts[ ubPayoutID ].fActive )
860 			break;
861 	}
862 
863 	LIFE_INSURANCE_PAYOUT* const lip = &LaptopSaveInfo.pLifeInsurancePayouts[ubPayoutID];
864 	lip->ubSoldierID = pSoldier->ubID;
865 	lip->ubMercID    = pSoldier->ubProfile;
866 	lip->fActive     = TRUE;
867 
868 	// This uses the merc's latest salaries, ignoring that they may be higher than the salaries paid under the current
869 	// contract if the guy has recently gained a level.  We could store his daily salary when he was last contracted,
870 	// and use that, but it still doesn't easily account for the fact that renewing a leveled merc early means that the
871 	// first part of his contract is under his old salary and the second part is under his new one.  Therefore, I chose
872 	// to ignore this wrinkle, and let the player awlays get paid out using the higher amount.  ARM
873 
874 	// figure out which of the 3 salary rates the merc has is the cheapest, and use it to calculate the paid amount, to
875 	// avoid getting back more than the merc cost if he was on a 2-week contract!
876 
877 	// start with the daily salary
878 	uiCostPerDay = p.sSalary;
879 
880 	// consider weekly salary / day
881 	if (p.uiWeeklySalary / 7 < uiCostPerDay)
882 	{
883 		uiCostPerDay = p.uiWeeklySalary / 7;
884 	}
885 
886 	// consider biweekly salary / day
887 	if (p.uiBiWeeklySalary / 14 < uiCostPerDay)
888 	{
889 		uiCostPerDay = p.uiBiWeeklySalary / 14;
890 	}
891 
892 	// calculate how many full, insured days of work the merc is going to miss
893 	uiDaysToPay = pSoldier->iTotalLengthOfInsuranceContract - ( GetWorldDay() + 1 - pSoldier->iStartOfInsuranceContract );
894 
895 	// calculate & store how much is to be paid out
896 	lip->iPayOutPrice = uiDaysToPay * uiCostPerDay;
897 
898 	// 4pm next day
899 	uiTimeInMinutes = GetMidnightOfFutureDayInMinutes( 1 ) + 16 * 60;
900 
901 	// if the death was suspicious, or he's already been investigated twice or more
902 	if (p.ubSuspiciousDeath || (gStrategicStatus.ubInsuranceInvestigationsCnt >= 2))
903 	{
904 		// fraud suspected, claim will be investigated first
905 		AddStrategicEvent( EVENT_INSURANCE_INVESTIGATION_STARTED, uiTimeInMinutes, ubPayoutID );
906 	}
907 	else
908 	{
909 		// is ok, make a prompt payment
910 		AddStrategicEvent( EVENT_PAY_LIFE_INSURANCE_FOR_DEAD_MERC, uiTimeInMinutes, ubPayoutID );
911 	}
912 
913 	LaptopSaveInfo.ubNumberLifeInsurancePayoutUsed++;
914 }
915 
916 
StartInsuranceInvestigation(UINT8 ubPayoutID)917 void StartInsuranceInvestigation( UINT8	ubPayoutID )
918 {
919 	const LIFE_INSURANCE_PAYOUT* const lip = &LaptopSaveInfo.pLifeInsurancePayouts[ubPayoutID];
920 	UINT8 ubDays;
921 
922 	// send an email telling player an investigation is taking place
923 	if (gStrategicStatus.ubInsuranceInvestigationsCnt == 0)
924 	{
925 		// first offense
926 		AddEmailWithSpecialData(INSUR_SUSPIC, INSUR_SUSPIC_LENGTH, INSURANCE_COMPANY, GetWorldTotalMin(), lip->iPayOutPrice, lip->ubMercID);
927 	}
928 	else
929 	{
930 		// subsequent offense
931 		AddEmailWithSpecialData(INSUR_SUSPIC_2, INSUR_SUSPIC_2_LENGTH, INSURANCE_COMPANY, GetWorldTotalMin(), lip->iPayOutPrice, lip->ubMercID);
932 	}
933 
934 	if (GetProfile(lip->ubMercID).ubSuspiciousDeath == VERY_SUSPICIOUS_DEATH)
935 	{
936 		// the fact that you tried to cheat them gets realized very quickly. :-)
937 		ubDays = 1;
938 	}
939 	else
940 	{
941 		// calculate how many days the investigation will take
942 		ubDays = (UINT8) (2 + gStrategicStatus.ubInsuranceInvestigationsCnt + Random(3));		// 2-4 days, +1 for every previous investigation
943 	}
944 
945 	// post an event to end the investigation that many days in the future (at 4pm)
946 	AddStrategicEvent( EVENT_INSURANCE_INVESTIGATION_OVER, GetMidnightOfFutureDayInMinutes( ubDays ) + 16 * 60, ubPayoutID );
947 
948 	// increment counter of all investigations
949 	gStrategicStatus.ubInsuranceInvestigationsCnt++;
950 }
951 
952 
EndInsuranceInvestigation(UINT8 ubPayoutID)953 void EndInsuranceInvestigation( UINT8	ubPayoutID )
954 {
955 	const LIFE_INSURANCE_PAYOUT* const lip = &LaptopSaveInfo.pLifeInsurancePayouts[ubPayoutID];
956 	// send an email telling player the investigation is over
957 	if (GetProfile(lip->ubMercID).ubSuspiciousDeath == VERY_SUSPICIOUS_DEATH)
958 	{
959 		// fraud, no payout!
960 		AddEmailWithSpecialData(INSUR_1HOUR_FRAUD, INSUR_1HOUR_FRAUD_LENGTH, INSURANCE_COMPANY, GetWorldTotalMin(), lip->iPayOutPrice, lip->ubMercID);
961 	}
962 	else
963 	{
964 		AddEmailWithSpecialData(INSUR_INVEST_OVER, INSUR_INVEST_OVER_LENGTH, INSURANCE_COMPANY, GetWorldTotalMin(), lip->iPayOutPrice, lip->ubMercID);
965 
966 		// only now make a payment (immediately)
967 		InsuranceContractPayLifeInsuranceForDeadMerc( ubPayoutID );
968 	}
969 }
970 
971 
InsuranceContractPayLifeInsuranceForDeadMerc(UINT8 ubPayoutID)972 void InsuranceContractPayLifeInsuranceForDeadMerc( UINT8 ubPayoutID )
973 {
974 	LIFE_INSURANCE_PAYOUT* const lip = &LaptopSaveInfo.pLifeInsurancePayouts[ubPayoutID];
975 	//if the mercs id number is the same what is in the soldier array
976 	SOLDIERTYPE& s = GetMan(lip->ubSoldierID);
977 	if (lip->ubSoldierID == s.ubID)
978 	{
979 		// and if the soldier is still active ( player hasn't removed carcass yet ), reset insurance flag
980 		if (s.bActive) s.usLifeInsurance = 0;
981 	}
982 
983 	//add transaction to players account
984 	AddTransactionToPlayersBook(INSURANCE_PAYOUT, lip->ubMercID, GetWorldTotalMin(), lip->iPayOutPrice);
985 
986 	//add to the history log the fact that the we paid the insurance claim
987 	AddHistoryToPlayersLog(HISTORY_INSURANCE_CLAIM_PAYOUT, lip->ubMercID, GetWorldTotalMin(), -1, -1);
988 
989 	//if there WASNT an investigation
990 	if (GetProfile(lip->ubMercID).ubSuspiciousDeath == 0)
991 	{
992 		//Add an email telling the user that he received an insurance payment
993 		AddEmailWithSpecialData(INSUR_PAYMENT, INSUR_PAYMENT_LENGTH, INSURANCE_COMPANY, GetWorldTotalMin(), lip->iPayOutPrice, lip->ubMercID);
994 	}
995 
996 	LaptopSaveInfo.ubNumberLifeInsurancePayoutUsed--;
997 	lip->fActive = FALSE;
998 }
999 
1000 
1001 //Gets called at the very end of the game
InsuranceContractEndGameShutDown()1002 void InsuranceContractEndGameShutDown()
1003 {
1004 	//Free up the memory allocated to the insurance payouts
1005 	LaptopSaveInfo.pLifeInsurancePayouts.clear();
1006 }
1007 
1008 
MercIsInsurable(const SOLDIERTYPE * pSoldier)1009 static BOOLEAN MercIsInsurable(const SOLDIERTYPE* pSoldier)
1010 {
1011 	// only A.I.M. mercs currently on player's team
1012 	if (pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC)
1013 	{
1014 		// with more than one day left on their employment contract are eligible for insurance
1015 		// the second part is because the insurance doesn't pay for any working day already started at time of death
1016 		//if( ( (pSoldier->iEndofContractTime - GetWorldTotalMin()) > 24 * 60) || pSoldier->usLifeInsurance )
1017 		if( CanSoldierExtendInsuranceContract( pSoldier ) || pSoldier->usLifeInsurance )
1018 		{
1019 			// who aren't currently being held POW
1020 			// POWs are also uninsurable - if already insured, that insurance IS valid but no new contracts or extension allowed
1021 			if (pSoldier->bAssignment != ASSIGNMENT_POW)
1022 			{
1023 				return(TRUE);
1024 			}
1025 		}
1026 	}
1027 
1028 	return(FALSE);
1029 }
1030 
1031 
EnableDisableInsuranceContractAcceptButtons(void)1032 static void EnableDisableInsuranceContractAcceptButtons(void)
1033 {
1034 	for (UINT i = 0; i != gubNumberofDisplayedInsuranceGrids; ++i)
1035 	{
1036 		const InsuranceInfo* const ins = &insurance_info[i];
1037 		const SOLDIERTYPE* const s = ins->soldier;
1038 		if (s == NULL) continue;
1039 
1040 		EnableButton(ins->button, CanSoldierExtendInsuranceContract(s));
1041 	}
1042 }
1043 
1044 
GetTimeRemainingOnSoldiersInsuranceContract(const SOLDIERTYPE * pSoldier)1045 static UINT32 GetTimeRemainingOnSoldiersInsuranceContract(const SOLDIERTYPE* pSoldier)
1046 {
1047 	//if the soldier has life insurance
1048 	if( pSoldier->usLifeInsurance )
1049 	{
1050 		//if the insurance contract hasnt started yet
1051 		if( (INT32)GetWorldDay() < pSoldier->iStartOfInsuranceContract )
1052 			return( pSoldier->iTotalLengthOfInsuranceContract );
1053 		else
1054 			return( ( pSoldier->iTotalLengthOfInsuranceContract - ( GetWorldDay() - pSoldier->iStartOfInsuranceContract ) ) );
1055 	}
1056 	else
1057 		return( 0 );
1058 }
1059 
1060 
GetTimeRemainingOnSoldiersContract(const SOLDIERTYPE * pSoldier)1061 static UINT32 GetTimeRemainingOnSoldiersContract(const SOLDIERTYPE* pSoldier)
1062 {
1063 	INT32 iDayMercLeaves = ( pSoldier->iEndofContractTime / 1440 ) - 1;
1064 
1065 	//Since the merc is leaving in the afternoon, we must adjust since the time left would be different if we did the calc
1066 	//at 11:59 or 12:01 ( noon )
1067 	if( pSoldier->iEndofContractTime % 1440 )
1068 		iDayMercLeaves++;
1069 
1070 	// Subtract todays day number
1071 	iDayMercLeaves = iDayMercLeaves - GetWorldDay();
1072 
1073 	if( iDayMercLeaves > pSoldier->iTotalContractLength )
1074 		iDayMercLeaves = pSoldier->iTotalContractLength;
1075 
1076 	return( iDayMercLeaves );
1077 	//return( ( pSoldier->iEndofContractTime - (INT32)GetWorldTotalMin( ) ) / 1440 );
1078 }
1079 
1080 
1081 static INT32 CalcStartDayOfInsurance(SOLDIERTYPE* pSoldier);
1082 
1083 
PurchaseOrExtendInsuranceForSoldier(SOLDIERTYPE * pSoldier,UINT32 uiInsuranceLength)1084 void PurchaseOrExtendInsuranceForSoldier( SOLDIERTYPE *pSoldier, UINT32 uiInsuranceLength )
1085 {
1086 	INT32	iAmountOfMoneyTransfer = -1;
1087 
1088 	AssertMsg(pSoldier != NULL, "Soldier pointer is NULL!");
1089 
1090 	//if the user doesnt have insruance already,
1091 	if( !(pSoldier->usLifeInsurance ) )
1092 	{
1093 		//specify the start date of the contract
1094 		pSoldier->iStartOfInsuranceContract = CalcStartDayOfInsurance( pSoldier );
1095 		pSoldier->uiStartTimeOfInsuranceContract = GetWorldTotalMin();
1096 	}
1097 
1098 	//transfer money
1099 	iAmountOfMoneyTransfer = CalculateInsuranceContractCost( uiInsuranceLength, pSoldier->ubProfile );
1100 
1101 	//if the user did have insruance already,
1102 	if( pSoldier->usLifeInsurance )
1103 	{
1104 		//specify the start date of the contract
1105 		pSoldier->iStartOfInsuranceContract = CalcStartDayOfInsurance( pSoldier );
1106 	}
1107 
1108 	//add transaction to finaces page
1109 	//if the player has life insurance
1110 	if( pSoldier->usLifeInsurance )
1111 	{
1112 		//if the player is extending the contract
1113 		if( iAmountOfMoneyTransfer > 0 )
1114 			AddTransactionToPlayersBook(	EXTENDED_INSURANCE, pSoldier->ubProfile, GetWorldTotalMin(), -( iAmountOfMoneyTransfer ) );
1115 		else
1116 			SLOGA("negative Cost of Life Insurance, something went horribly wrong");
1117 	}
1118 	else
1119 	{
1120 		//if the player doesnt have enough money, tell him
1121 		if( LaptopSaveInfo.iCurrentBalance < iAmountOfMoneyTransfer )
1122 		{
1123 			ST::string sText = GetInsuranceText(INS_MLTI_NOT_ENOUGH_FUNDS);
1124 			if( guiCurrentScreen == LAPTOP_SCREEN )
1125 				DoLapTopMessageBox( MSG_BOX_RED_ON_WHITE, sText, LAPTOP_SCREEN, MSG_BOX_FLAG_OK, NULL);
1126 			else
1127 				DoMapMessageBox( MSG_BOX_RED_ON_WHITE, sText, MAP_SCREEN, MSG_BOX_FLAG_OK, NULL);
1128 		}
1129 		else
1130 		{
1131 			//else if the player has enought to cover the bill, let him
1132 
1133 			//the player just purchased life insurance
1134 			AddTransactionToPlayersBook(	PURCHASED_INSURANCE, pSoldier->ubProfile, GetWorldTotalMin(), -( iAmountOfMoneyTransfer ) );
1135 
1136 			//add an entry in the history page for the purchasing of life insurance
1137 			AddHistoryToPlayersLog(HISTORY_PURCHASED_INSURANCE, pSoldier->ubProfile, GetWorldTotalMin(), -1, -1 );
1138 
1139 			//Set that we have life insurance
1140 			pSoldier->usLifeInsurance = 1;
1141 		}
1142 	}
1143 
1144 	pSoldier->iTotalLengthOfInsuranceContract += uiInsuranceLength;
1145 
1146 	//make sure the length doesnt exceed the contract length
1147 	if( ( GetTimeRemainingOnSoldiersInsuranceContract( pSoldier ) ) > GetTimeRemainingOnSoldiersContract( pSoldier ) )
1148 	{
1149 		pSoldier->iTotalLengthOfInsuranceContract -= GetTimeRemainingOnSoldiersInsuranceContract( pSoldier ) - GetTimeRemainingOnSoldiersContract( pSoldier );
1150 	}
1151 }
1152 
1153 
CanSoldierExtendInsuranceContract(const SOLDIERTYPE * pSoldier)1154 static BOOLEAN CanSoldierExtendInsuranceContract(const SOLDIERTYPE* pSoldier)
1155 {
1156 	if( CalculateSoldiersInsuranceContractLength( pSoldier ) != 0 )
1157 		return( TRUE );
1158 	else
1159 		return( FALSE );
1160 }
1161 
1162 
CalculateSoldiersInsuranceContractLength(const SOLDIERTYPE * pSoldier)1163 static INT32 CalculateSoldiersInsuranceContractLength(const SOLDIERTYPE* pSoldier)
1164 {
1165 	INT32 iInsuranceContractLength=0;
1166 	UINT32 uiTimeRemainingOnSoldiersContract = GetTimeRemainingOnSoldiersContract( pSoldier );
1167 
1168 	if (IsMercDead(GetProfile(pSoldier->ubProfile))) return 0;
1169 
1170 	// only mercs with at least 2 days to go on their employment contract are insurable
1171 	// def: 2/5/99.  However if they already have insurance is SHOULD be ok
1172 	if( uiTimeRemainingOnSoldiersContract < 2 && !( pSoldier->usLifeInsurance != 0 && uiTimeRemainingOnSoldiersContract >= 1 ) )
1173 	{
1174 		return( 0 );
1175 	}
1176 
1177 	//
1178 	//Calculate the insurance contract length
1179 	//
1180 
1181 	//if the soldier has an insurance contract, dont deduct a day
1182 	if( pSoldier->usLifeInsurance || DidGameJustStart() )
1183 		iInsuranceContractLength = uiTimeRemainingOnSoldiersContract - GetTimeRemainingOnSoldiersInsuranceContract( pSoldier );
1184 
1185 	//else deduct a day
1186 	else
1187 		iInsuranceContractLength = uiTimeRemainingOnSoldiersContract - GetTimeRemainingOnSoldiersInsuranceContract( pSoldier ) - 1;
1188 
1189 	//make sure the length doesnt exceed the contract length
1190 	if( ( GetTimeRemainingOnSoldiersInsuranceContract( pSoldier ) + iInsuranceContractLength ) > uiTimeRemainingOnSoldiersContract )
1191 	{
1192 		iInsuranceContractLength = uiTimeRemainingOnSoldiersContract - GetTimeRemainingOnSoldiersInsuranceContract( pSoldier );
1193 	}
1194 
1195 	//Is the mercs insurace contract is less then a day, set it to 0
1196 	if( iInsuranceContractLength < 0 )
1197 		iInsuranceContractLength = 0;
1198 
1199 	if( pSoldier->usLifeInsurance && pSoldier->iStartOfInsuranceContract >= (INT32)GetWorldDay() && iInsuranceContractLength < 2 )
1200 		iInsuranceContractLength = 0;
1201 
1202 	return( iInsuranceContractLength );
1203 }
1204 
1205 
CalcStartDayOfInsurance(SOLDIERTYPE * pSoldier)1206 static INT32 CalcStartDayOfInsurance(SOLDIERTYPE* pSoldier)
1207 {
1208 	UINT32	uiDayToStartInsurance=0;
1209 
1210 	//if the soldier was just hired ( in transit ), and the game didnt just start
1211 	if( pSoldier->bAssignment == IN_TRANSIT && !DidGameJustStart() )
1212 	{
1213 		uiDayToStartInsurance = GetWorldDay( );
1214 	}
1215 	else
1216 	{
1217 		//Get tomorows date ( and convert it to days )
1218 		uiDayToStartInsurance = GetMidnightOfFutureDayInMinutes( 1 ) / 1440;
1219 	}
1220 
1221 	return( uiDayToStartInsurance );
1222 }
1223 
1224 
AreAnyAimMercsOnTeam(void)1225 static BOOLEAN AreAnyAimMercsOnTeam(void)
1226 {
1227 	CFOR_EACH_IN_TEAM(s, OUR_TEAM)
1228 	{
1229 		if (s->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC) return TRUE;
1230 	}
1231 	return FALSE;
1232 }
1233