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