1 #include "Quests.h"
2
3 #include "Arms_Dealer_Init.h"
4 #include "Boxing.h"
5 #include "Campaign.h"
6 #include "ContentManager.h"
7 #include "FactParamsModel.h"
8 #include "FileMan.h"
9 #include "Game_Clock.h"
10 #include "GameInstance.h"
11 #include "GameSettings.h"
12 #include "History.h"
13 #include "Interface_Dialogue.h"
14 #include "Isometric_Utils.h"
15 #include "Items.h"
16 #include "Map_Screen_Helicopter.h"
17 #include "MapScreen.h"
18 #include "Overhead.h"
19 #include "Queen_Command.h"
20 #include "Render_Fun.h"
21 #include "ShippingDestinationModel.h"
22 #include "Soldier_Profile.h"
23 #include "Strategic_Event_Handler.h"
24 #include "Strategic_Mines.h"
25 #include "Strategic_Town_Loyalty.h"
26 #include "StrategicMap.h"
27 #include "Tactical_Save.h"
28 #include "Town_Militia.h"
29
30 #include <algorithm>
31 #include <iterator>
32
33 #define TESTQUESTS
34
35 extern SOLDIERTYPE * gpSrcSoldier;
36 extern SOLDIERTYPE * gpDestSoldier;
37
38 UINT8 gubQuest[MAX_QUESTS];
39 UINT8 gubFact[ NUM_FACTS ]; // this has to be updated when we figure out how many facts we have
40
41
SetFactTrue(Fact const usFact)42 void SetFactTrue(Fact const usFact)
43 {
44 // This function is here just for control flow purposes (debug breakpoints)
45 // and code is more readable that way
46
47 // must intercept when Jake is first trigered to start selling fuel
48 if (usFact == FACT_ESTONI_REFUELLING_POSSIBLE && !CheckFact(usFact, 0))
49 {
50 // give him some gas...
51 GuaranteeAtLeastXItemsOfIndex( ARMS_DEALER_JAKE, GAS_CAN, ( UINT8 ) ( 4 + Random( 3 ) ) );
52 }
53
54 gubFact[usFact] = TRUE;
55 }
56
57
SetFactFalse(Fact const usFact)58 void SetFactFalse(Fact const usFact)
59 {
60 gubFact[usFact] = FALSE;
61 }
62
63
CheckForNewShipment(void)64 static bool CheckForNewShipment(void)
65 {
66 auto shippingDest = GCM->getPrimaryShippingDestination();
67 if (gWorldSectorX != shippingDest->deliverySectorX) return false;
68 if (gWorldSectorY != shippingDest->deliverySectorY) return false;
69 if (gbWorldSectorZ != shippingDest->deliverySectorZ) return false;
70
71 ITEM_POOL const* const ip = GetItemPool(shippingDest->deliverySectorGridNo, 0);
72 return ip && !IsItemPoolVisible(ip);
73 }
74
75
CheckNPCWounded(UINT8 const ubProfileID,BOOLEAN const fByPlayerOnly)76 static BOOLEAN CheckNPCWounded(UINT8 const ubProfileID, BOOLEAN const fByPlayerOnly)
77 {
78 SOLDIERTYPE const* const s = FindSoldierByProfileID(ubProfileID);
79 return
80 s &&
81 s->bLife < s->bLifeMax && // is the NPC is wounded at all?
82 (
83 !fByPlayerOnly ||
84 GetProfile(ubProfileID).ubMiscFlags & PROFILE_MISC_FLAG_WOUNDEDBYPLAYER
85 );
86 }
87
88
CheckNPCInOkayHealth(UINT8 ubProfileID)89 static BOOLEAN CheckNPCInOkayHealth(UINT8 ubProfileID)
90 {
91 // is the NPC at better than half health?
92 const SOLDIERTYPE* const pSoldier = FindSoldierByProfileID(ubProfileID);
93 if (pSoldier && pSoldier->bLife > (pSoldier->bLifeMax / 2) && pSoldier->bLife > 30)
94 {
95 return( TRUE );
96 }
97 else
98 {
99 return( FALSE );
100 }
101 }
102
103
CheckNPCBleeding(UINT8 ubProfileID)104 static BOOLEAN CheckNPCBleeding(UINT8 ubProfileID)
105 {
106 // the NPC is wounded...
107 const SOLDIERTYPE* const pSoldier = FindSoldierByProfileID(ubProfileID);
108 if (pSoldier && pSoldier->bLife > 0 && pSoldier->bBleeding > 0)
109 {
110 return( TRUE );
111 }
112 else
113 {
114 return( FALSE );
115 }
116
117 }
118
119
CheckNPCWithin(UINT8 ubFirstNPC,UINT8 ubSecondNPC,UINT8 ubMaxDistance)120 static BOOLEAN CheckNPCWithin(UINT8 ubFirstNPC, UINT8 ubSecondNPC, UINT8 ubMaxDistance)
121 {
122 const SOLDIERTYPE* const pFirstNPC = FindSoldierByProfileID(ubFirstNPC);
123 const SOLDIERTYPE* const pSecondNPC = FindSoldierByProfileID(ubSecondNPC);
124 if (!pFirstNPC || !pSecondNPC)
125 {
126 return( FALSE );
127 }
128 return( PythSpacesAway( pFirstNPC->sGridNo, pSecondNPC->sGridNo ) <= ubMaxDistance );
129 }
130
131
CheckGuyVisible(UINT8 ubNPC,UINT8 ubGuy)132 static BOOLEAN CheckGuyVisible(UINT8 ubNPC, UINT8 ubGuy)
133 {
134 // NB ONLY WORKS IF ON DIFFERENT TEAMS
135 const SOLDIERTYPE* const pNPC = FindSoldierByProfileID(ubNPC);
136 const SOLDIERTYPE* const pGuy = FindSoldierByProfileID(ubGuy);
137 if (!pNPC || !pGuy)
138 {
139 return( FALSE );
140 }
141 if (pNPC->bOppList[ pGuy->ubID ] == SEEN_CURRENTLY )
142 {
143 return( TRUE );
144 }
145 else
146 {
147 return( FALSE );
148 }
149 }
150
151
CheckNPCAt(UINT8 ubNPC,INT16 sGridNo)152 static BOOLEAN CheckNPCAt(UINT8 ubNPC, INT16 sGridNo)
153 {
154 const SOLDIERTYPE* const pNPC = FindSoldierByProfileID(ubNPC);
155 if (!pNPC)
156 {
157 return( FALSE );
158 }
159 return( pNPC->sGridNo == sGridNo );
160 }
161
162
CheckNPCIsEnemy(UINT8 ubProfileID)163 static BOOLEAN CheckNPCIsEnemy(UINT8 ubProfileID)
164 {
165 const SOLDIERTYPE* const pNPC = FindSoldierByProfileID(ubProfileID);
166 if (!pNPC)
167 {
168 return( FALSE );
169 }
170 if (pNPC->bSide == OUR_TEAM || pNPC->bNeutral)
171 {
172 if (pNPC->ubCivilianGroup != NON_CIV_GROUP)
173 {
174 // although the soldier is NOW the same side, this civ group could be set to "will become hostile"
175 return( gTacticalStatus.fCivGroupHostile[ pNPC->ubCivilianGroup ] >= CIV_GROUP_WILL_BECOME_HOSTILE );
176 }
177 else
178 {
179 return( FALSE );
180 }
181 }
182 else
183 {
184 return( TRUE );
185 }
186 }
187
188
NumWoundedMercsNearby(ProfileID const pid)189 static INT8 NumWoundedMercsNearby(ProfileID const pid)
190 {
191 SOLDIERTYPE const* const npc = FindSoldierByProfileID(pid);
192 if (!npc) return 0;
193
194 INT8 n = 0;
195 GridNo const gridno = npc->sGridNo;
196 FOR_EACH_MERC(i)
197 {
198 SOLDIERTYPE const& s = **i;
199 if (s.bTeam != OUR_TEAM) continue;
200 if (s.bLife <= 0 || s.bLifeMax <= s.bLife) continue;
201 if (s.bAssignment == ASSIGNMENT_HOSPITAL) continue;
202 if (PythSpacesAway(gridno, s.sGridNo) > HOSPITAL_PATIENT_DISTANCE) continue;
203 ++n;
204 }
205 return n;
206 }
207
208
NumMercsNear(ProfileID const pid,UINT8 const max_dist)209 static INT8 NumMercsNear(ProfileID const pid, UINT8 const max_dist)
210 {
211 SOLDIERTYPE const* const npc = FindSoldierByProfileID(pid);
212 if (!npc) return 0;
213
214 INT8 n = 0;
215 GridNo const gridno = npc->sGridNo;
216 FOR_EACH_MERC(i)
217 {
218 SOLDIERTYPE const& s = **i;
219 if (s.bTeam != OUR_TEAM) continue;
220 if (s.bLife < OKLIFE) continue;
221 if (PythSpacesAway(gridno, s.sGridNo) <= max_dist) continue;
222 ++n;
223 }
224 return n;
225 }
226
227
CheckNPCIsEPC(UINT8 ubProfileID)228 static BOOLEAN CheckNPCIsEPC(UINT8 ubProfileID)
229 {
230 if ( gMercProfiles[ ubProfileID ].bMercStatus == MERC_IS_DEAD )
231 {
232 return( FALSE );
233 }
234
235 const SOLDIERTYPE* const pNPC = FindSoldierByProfileIDOnPlayerTeam(ubProfileID);
236 if (!pNPC)
237 {
238 return( FALSE );
239 }
240 return( (pNPC->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) );
241 }
242
243
NPCInRoom(UINT8 ubProfileID,UINT8 ubRoomID)244 BOOLEAN NPCInRoom(UINT8 ubProfileID, UINT8 ubRoomID)
245 {
246 const SOLDIERTYPE* const pNPC = FindSoldierByProfileID(ubProfileID);
247 if ( !pNPC || (gubWorldRoomInfo[ pNPC->sGridNo ] != ubRoomID) )
248 {
249 return( FALSE );
250 }
251 return( TRUE );
252 }
253
254
NPCInRoomRange(UINT8 ubProfileID,UINT8 ubRoomID1,UINT8 ubRoomID2)255 static BOOLEAN NPCInRoomRange(UINT8 ubProfileID, UINT8 ubRoomID1, UINT8 ubRoomID2)
256 {
257 const SOLDIERTYPE* const pNPC = FindSoldierByProfileID(ubProfileID);
258 if ( !pNPC || (gubWorldRoomInfo[ pNPC->sGridNo ] < ubRoomID1) || (gubWorldRoomInfo[ pNPC->sGridNo ] > ubRoomID2) )
259 {
260 return( FALSE );
261 }
262 return( TRUE );
263 }
264
265
PCInSameRoom(UINT8 ubProfileID)266 static BOOLEAN PCInSameRoom(UINT8 ubProfileID)
267 {
268 UINT8 ubRoom;
269
270 const SOLDIERTYPE* const pNPC = FindSoldierByProfileID(ubProfileID);
271 if ( !pNPC )
272 {
273 return( FALSE );
274 }
275 ubRoom = gubWorldRoomInfo[ pNPC->sGridNo ];
276
277 CFOR_EACH_IN_TEAM(s, OUR_TEAM)
278 {
279 if (s->bInSector && gubWorldRoomInfo[s->sGridNo] == ubRoom)
280 {
281 return TRUE;
282 }
283 }
284
285 return( FALSE );
286 }
287
288
CheckTalkerStrong(void)289 static BOOLEAN CheckTalkerStrong(void)
290 {
291 if (gpSrcSoldier && gpSrcSoldier->bTeam == OUR_TEAM)
292 {
293 return( gpSrcSoldier->bStrength >= 84 );
294 }
295 else if (gpDestSoldier && gpDestSoldier->bTeam == OUR_TEAM)
296 {
297 return( gpDestSoldier->bStrength >= 84 );
298 }
299 return( FALSE );
300 }
301
302
CheckTalkerFemale(void)303 static BOOLEAN CheckTalkerFemale(void)
304 {
305 if (gpSrcSoldier && gpSrcSoldier->bTeam == OUR_TEAM && gpSrcSoldier->ubProfile != NO_PROFILE)
306 {
307 return( gMercProfiles[ gpSrcSoldier->ubProfile ].bSex == FEMALE );
308 }
309 else if (gpDestSoldier && gpDestSoldier->bTeam == OUR_TEAM && gpDestSoldier->ubProfile != NO_PROFILE)
310 {
311 return( gMercProfiles[ gpDestSoldier->ubProfile ].bSex == FEMALE );
312 }
313 return( FALSE );
314 }
315
316
CheckTalkerUnpropositionedFemale(void)317 static BOOLEAN CheckTalkerUnpropositionedFemale(void)
318 {
319 if (gpSrcSoldier && gpSrcSoldier->bTeam == OUR_TEAM && gpSrcSoldier->ubProfile != NO_PROFILE)
320 {
321 if ( !(gMercProfiles[ gpSrcSoldier->ubProfile ].ubMiscFlags2 & PROFILE_MISC_FLAG2_ASKED_BY_HICKS) )
322 {
323 return( gMercProfiles[ gpSrcSoldier->ubProfile ].bSex == FEMALE );
324 }
325 }
326 else if (gpDestSoldier && gpDestSoldier->bTeam == OUR_TEAM && gpDestSoldier->ubProfile != NO_PROFILE)
327 {
328 if ( !(gMercProfiles[ gpDestSoldier->ubProfile ].ubMiscFlags2 & PROFILE_MISC_FLAG2_ASKED_BY_HICKS) )
329 {
330 return( gMercProfiles[ gpDestSoldier->ubProfile ].bSex == FEMALE );
331 }
332 }
333 return( FALSE );
334 }
335
336
NumMalesPresent(ProfileID const pid)337 static INT8 NumMalesPresent(ProfileID const pid)
338 {
339 SOLDIERTYPE const* const npc = FindSoldierByProfileID(pid);
340 if (!npc) return 0;
341
342 INT8 n = 0;
343 GridNo const gridno = npc->sGridNo;
344 FOR_EACH_MERC(i)
345 {
346 SOLDIERTYPE const& s = **i;
347 if (s.bTeam != OUR_TEAM) continue;
348 if (s.bLife < OKLIFE) continue;
349 if (s.ubProfile == NO_PROFILE) continue;
350 if (GetProfile(s.ubProfile).bSex != MALE) continue;
351 if (PythSpacesAway(gridno, s.sGridNo) > 8) continue;
352 ++n;
353 }
354 return n;
355 }
356
357
FemalePresent(ProfileID const pid)358 static bool FemalePresent(ProfileID const pid)
359 {
360 SOLDIERTYPE const* const npc = FindSoldierByProfileID(pid);
361 if (!npc) return false;
362
363 GridNo const gridno = npc->sGridNo;
364 FOR_EACH_MERC(i)
365 {
366 SOLDIERTYPE const& s = **i;
367 if (s.bTeam != OUR_TEAM) continue;
368 if (s.bLife < OKLIFE) continue;
369 if (s.ubProfile == NO_PROFILE) continue;
370 if (GetProfile(s.ubProfile).bSex != FEMALE) continue;
371 if (PythSpacesAway(gridno, s.sGridNo) > 10) continue;
372 return true;
373 }
374 return false;
375 }
376
377
CheckPlayerHasHead(void)378 static BOOLEAN CheckPlayerHasHead(void)
379 {
380 CFOR_EACH_IN_TEAM(s, OUR_TEAM)
381 {
382 if (s->bLife > 0 && FindObjInObjRange(s, HEAD_2, HEAD_7) != NO_SLOT)
383 {
384 return TRUE;
385 }
386 }
387 return FALSE;
388 }
389
390
CheckNPCSector(UINT8 ubProfileID,INT16 sSectorX,INT16 sSectorY,INT8 bSectorZ)391 static BOOLEAN CheckNPCSector(UINT8 ubProfileID, INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ)
392 {
393 const SOLDIERTYPE* const pSoldier = FindSoldierByProfileIDOnPlayerTeam(ubProfileID);
394 if( pSoldier )
395 {
396 if (pSoldier->sSectorX == sSectorX &&
397 pSoldier->sSectorY == sSectorY &&
398 pSoldier->bSectorZ == bSectorZ )
399 {
400 return( TRUE );
401 }
402 }
403 else if (gMercProfiles[ubProfileID].sSectorX == sSectorX &&
404 gMercProfiles[ubProfileID].sSectorY == sSectorY &&
405 gMercProfiles[ubProfileID].bSectorZ == bSectorZ )
406 {
407 return( TRUE );
408 }
409
410 return( FALSE );
411
412 }
413
414
AIMMercWithin(GridNo const gridno,INT16 const distance)415 static bool AIMMercWithin(GridNo const gridno, INT16 const distance)
416 {
417 FOR_EACH_MERC(i)
418 {
419 SOLDIERTYPE const& s = **i;
420 if (s.bTeam != OUR_TEAM) continue;
421 if (s.bLife < OKLIFE) continue;
422 if (s.ubWhatKindOfMercAmI != MERC_TYPE__AIM_MERC) continue;
423 if (PythSpacesAway(gridno, s.sGridNo) > distance) continue;
424 return true;
425 }
426 return false;
427 }
428
429
CheckNPCCowering(UINT8 ubProfileID)430 static BOOLEAN CheckNPCCowering(UINT8 ubProfileID)
431 {
432 const SOLDIERTYPE* const pNPC = FindSoldierByProfileID(ubProfileID);
433 if ( !pNPC )
434 {
435 return( FALSE );
436 }
437 return( ( (pNPC->uiStatusFlags & SOLDIER_COWERING) != 0) );
438 }
439
440
CountBartenders(void)441 static UINT8 CountBartenders(void)
442 {
443 UINT8 ubLoop;
444 UINT8 ubBartenders = 0;
445
446 for( ubLoop = HERVE; ubLoop <= CARLO; ubLoop++ )
447 {
448 if (gMercProfiles[ ubLoop ].bNPCData != 0 )
449 {
450 ubBartenders++;
451 }
452 }
453 return( ubBartenders );
454 }
455
456
CheckNPCIsUnderFire(UINT8 ubProfileID)457 static BOOLEAN CheckNPCIsUnderFire(UINT8 ubProfileID)
458 {
459 const SOLDIERTYPE* const pNPC = FindSoldierByProfileID(ubProfileID);
460 if ( !pNPC )
461 {
462 return( FALSE );
463 }
464 return( pNPC->bUnderFire != 0 );
465 }
466
467
NPCHeardShot(UINT8 ubProfileID)468 static BOOLEAN NPCHeardShot(UINT8 ubProfileID)
469 {
470 const SOLDIERTYPE* const pNPC = FindSoldierByProfileID(ubProfileID);
471 if ( !pNPC )
472 {
473 return( FALSE );
474 }
475 return( pNPC->ubMiscSoldierFlags & SOLDIER_MISC_HEARD_GUNSHOT );
476 }
477
478
InTownSectorWithTrainingLoyalty(UINT8 const sector)479 static bool InTownSectorWithTrainingLoyalty(UINT8 const sector)
480 {
481 UINT8 const town = GetTownIdForSector(sector);
482 return
483 town != BLANK_SECTOR &&
484 gfTownUsesLoyalty[town] &&
485 gTownLoyalty[town].fStarted &&
486 gTownLoyalty[town].ubRating >= MIN_RATING_TO_TRAIN_TOWN;
487 }
488
489
CheckFact(Fact const usFact,UINT8 const ubProfileID)490 BOOLEAN CheckFact(Fact const usFact, UINT8 const ubProfileID)
491 {
492 INT8 bTown = -1;
493 auto factParams = GCM->getFactParams(usFact);
494
495 switch( usFact )
496 {
497 case FACT_DIMITRI_DEAD:
498 gubFact[ usFact ] = (gMercProfiles[ DIMITRI ].bMercStatus == MERC_IS_DEAD );
499 break;
500 case FACT_CURRENT_SECTOR_IS_SAFE:
501 gubFact[FACT_CURRENT_SECTOR_IS_SAFE] = !( ( (gTacticalStatus.fEnemyInSector && NPCHeardShot( ubProfileID ) ) || gTacticalStatus.uiFlags & INCOMBAT ) );
502 break;
503 case FACT_BOBBYRAY_SHIPMENT_IN_TRANSIT:
504 case FACT_NEW_BOBBYRAY_SHIPMENT_WAITING:
505 if (gubFact[FACT_PABLO_PUNISHED_BY_PLAYER] == TRUE &&
506 !gubFact[FACT_PABLO_RETURNED_GOODS] &&
507 gMercProfiles[PABLO].bMercStatus != MERC_IS_DEAD)
508 {
509 gubFact[FACT_BOBBYRAY_SHIPMENT_IN_TRANSIT] = FALSE;
510 gubFact[FACT_NEW_BOBBYRAY_SHIPMENT_WAITING] = FALSE;
511 }
512 else
513 {
514 if (CheckForNewShipment()) // if new stuff waiting unseen in Drassen
515 {
516 gubFact[FACT_BOBBYRAY_SHIPMENT_IN_TRANSIT] = FALSE;
517 gubFact[FACT_NEW_BOBBYRAY_SHIPMENT_WAITING] = TRUE;
518 }
519 else if ( CountNumberOfBobbyPurchasesThatAreInTransit() > 0) // if stuff in transit
520 {
521 if ( gubFact[ FACT_PACKAGE_DAMAGED ] == TRUE )
522 {
523 gubFact[FACT_BOBBYRAY_SHIPMENT_IN_TRANSIT] = FALSE;
524 }
525 else
526 {
527 gubFact[FACT_BOBBYRAY_SHIPMENT_IN_TRANSIT] = TRUE;
528 }
529 gubFact[FACT_NEW_BOBBYRAY_SHIPMENT_WAITING] = FALSE;
530 }
531 else
532 {
533 gubFact[FACT_BOBBYRAY_SHIPMENT_IN_TRANSIT] = FALSE;
534 gubFact[FACT_NEW_BOBBYRAY_SHIPMENT_WAITING] = FALSE;
535 }
536 }
537 break;
538 case FACT_NPC_WOUNDED:
539 gubFact[FACT_NPC_WOUNDED] = CheckNPCWounded( ubProfileID, FALSE );
540 break;
541 case FACT_NPC_WOUNDED_BY_PLAYER:
542 gubFact[FACT_NPC_WOUNDED_BY_PLAYER] = CheckNPCWounded( ubProfileID, TRUE );
543 break;
544 case FACT_IRA_NOT_PRESENT:
545 gubFact[FACT_IRA_NOT_PRESENT] = !CheckNPCWithin( ubProfileID, IRA, 10 );
546 break;
547 case FACT_IRA_TALKING:
548 gubFact[FACT_IRA_TALKING] = (gubSrcSoldierProfile == IRA);
549 break;
550 case FACT_IRA_UNHIRED_AND_ALIVE:
551 if ( gMercProfiles[ IRA ].bMercStatus != MERC_IS_DEAD && CheckNPCSector( IRA, 10, 1, 1) && !(gMercProfiles[IRA].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED) )
552 {
553 gubFact[FACT_IRA_UNHIRED_AND_ALIVE] = TRUE;
554 }
555 else
556 {
557 gubFact[FACT_IRA_UNHIRED_AND_ALIVE] = FALSE;
558 }
559 break;
560 case FACT_NPC_BLEEDING:
561 gubFact[FACT_NPC_BLEEDING] = CheckNPCBleeding( ubProfileID );
562 break;
563 case FACT_NPC_BLEEDING_BUT_OKAY:
564 if ( CheckNPCBleeding( ubProfileID ) && CheckNPCInOkayHealth( ubProfileID ) )
565 {
566 gubFact[FACT_NPC_BLEEDING_BUT_OKAY] = TRUE;
567 }
568 else
569 {
570 gubFact[FACT_NPC_BLEEDING_BUT_OKAY] = FALSE;
571 }
572 break;
573
574 case FACT_PLAYER_HAS_HEAD_AND_CARMEN_IN_SAN_MONA:
575 gubFact[usFact] = (CheckNPCSector( CARMEN, 5, MAP_ROW_C, 0 ) && CheckPlayerHasHead() );
576 break;
577
578 case FACT_PLAYER_HAS_HEAD_AND_CARMEN_IN_CAMBRIA:
579 gubFact[usFact] = (CheckNPCSector( CARMEN, 9, MAP_ROW_G, 0 ) && CheckPlayerHasHead() );
580 break;
581
582 case FACT_PLAYER_HAS_HEAD_AND_CARMEN_IN_DRASSEN:
583 gubFact[usFact] = (CheckNPCSector( CARMEN, 13, MAP_ROW_C, 0 ) && CheckPlayerHasHead() );
584 break;
585
586 case FACT_NPC_OWED_MONEY:
587 gubFact[FACT_NPC_OWED_MONEY] = (ubProfileID != NO_PROFILE && gMercProfiles[ubProfileID].iBalance < 0);
588 break;
589
590 case FACT_FATHER_DRUNK:
591 gubFact[FACT_FATHER_DRUNK] = ( gMercProfiles[ FATHER ].bNPCData >= 5 );
592 break;
593
594 case FACT_MICKY_DRUNK:
595 gubFact[FACT_MICKY_DRUNK] = ( gMercProfiles[ MICKY ].bNPCData >= 5 );
596 break;
597
598 case FACT_BRENDA_IN_STORE_AND_ALIVE:
599 // ensure alive
600 if (GetProfile(BRENDA).bMercStatus == MERC_IS_DEAD)
601 {
602 gubFact[FACT_BRENDA_IN_STORE_AND_ALIVE] = FALSE;
603 }
604 // ensure in a building and nearby
605 else if (!(NPCInRoom(BRENDA, 47)))
606 {
607 gubFact[FACT_BRENDA_IN_STORE_AND_ALIVE] = FALSE;
608 }
609 else
610 {
611 gubFact[FACT_BRENDA_IN_STORE_AND_ALIVE] = CheckNPCWithin(ubProfileID, BRENDA, 12);
612 }
613 break;
614 case FACT_BRENDA_DEAD:
615 gubFact[FACT_BRENDA_DEAD] = (GetProfile(BRENDA).bMercStatus == MERC_IS_DEAD);
616 break;
617 case FACT_NPC_IS_ENEMY:
618 gubFact[FACT_NPC_IS_ENEMY] = CheckNPCIsEnemy( ubProfileID ) || gMercProfiles[ ubProfileID ].ubMiscFlags2 & PROFILE_MISC_FLAG2_NEEDS_TO_SAY_HOSTILE_QUOTE;
619 break;
620 /*
621 case FACT_SKYRIDER_CLOSE_TO_CHOPPER:
622 SetUpHelicopterForPlayer( 13, MAP_ROW_B );
623 break;
624 */
625 case FACT_SPIKE_AT_DOOR:
626 gubFact[FACT_SPIKE_AT_DOOR] = CheckNPCAt(SPIKE, factParams->getGridNo(9817));
627 break;
628 case FACT_WOUNDED_MERCS_NEARBY:
629 gubFact[usFact] = (NumWoundedMercsNearby( ubProfileID ) > 0);
630 break;
631 case FACT_ONE_WOUNDED_MERC_NEARBY:
632 gubFact[usFact] = (NumWoundedMercsNearby( ubProfileID ) == 1);
633 break;
634 case FACT_MULTIPLE_WOUNDED_MERCS_NEARBY:
635 gubFact[usFact] = (NumWoundedMercsNearby( ubProfileID ) > 1);
636 break;
637 case FACT_HANS_AT_SPOT:
638 gubFact[usFact] = CheckNPCAt(HANS, factParams->getGridNo(13523));
639 break;
640 case FACT_MULTIPLE_MERCS_CLOSE:
641 gubFact[usFact] = ( NumMercsNear( ubProfileID, 3 ) > 1 );
642 break;
643 case FACT_SOME_MERCS_CLOSE:
644 gubFact[usFact] = ( NumMercsNear( ubProfileID, 3 ) > 0 );
645 break;
646 case FACT_MARIA_ESCORTED:
647 gubFact[usFact] = CheckNPCIsEPC( MARIA );
648 break;
649 case FACT_JOEY_ESCORTED:
650 gubFact[usFact] = CheckNPCIsEPC( JOEY );
651 break;
652 case FACT_ESCORTING_SKYRIDER:
653 gubFact[usFact] = CheckNPCIsEPC( SKYRIDER );
654 break;
655 case FACT_MARIA_ESCORTED_AT_LEATHER_SHOP:
656 gubFact[usFact] = ( CheckNPCIsEPC( MARIA ) && (NPCInRoom( MARIA, 2 )) );
657 break;
658 case FACT_PC_STRONG_AND_LESS_THAN_3_MALES_PRESENT:
659 gubFact[usFact] = ( CheckTalkerStrong() && (NumMalesPresent( ubProfileID ) < 3) );
660 break;
661 case FACT_PC_STRONG_AND_3_PLUS_MALES_PRESENT:
662 gubFact[usFact] = ( CheckTalkerStrong() && (NumMalesPresent( ubProfileID ) >= 3) );
663 break;
664 case FACT_FEMALE_SPEAKING_TO_NPC:
665 gubFact[usFact] = CheckTalkerFemale();
666 break;
667 case FACT_CARMEN_IN_C5:
668 gubFact[usFact] = CheckNPCSector(CARMEN, 5, MAP_ROW_C, 0);
669 break;
670 case FACT_JOEY_IN_C5:
671 gubFact[usFact] = CheckNPCSector(JOEY, 5, MAP_ROW_C, 0);
672 break;
673 case FACT_JOEY_NEAR_MARTHA:
674 gubFact[usFact] = CheckNPCWithin(JOEY, MARTHA, 5) && (CheckGuyVisible(MARTHA, JOEY) || CheckGuyVisible(JOEY, MARTHA));
675 break;
676 case FACT_JOEY_DEAD:
677 gubFact[usFact] = gMercProfiles[ JOEY ].bMercStatus == MERC_IS_DEAD;
678 break;
679 case FACT_MERC_NEAR_MARTHA:
680 gubFact[usFact] = ( NumMercsNear( ubProfileID, 5 ) > 0 );
681 break;
682 case FACT_REBELS_HATE_PLAYER:
683 gubFact[usFact] = (gTacticalStatus.fCivGroupHostile[ REBEL_CIV_GROUP ] == CIV_GROUP_HOSTILE);
684 break;
685 case FACT_CURRENT_SECTOR_G9:
686 gubFact[usFact] = ( gWorldSectorX == 9 && gWorldSectorY == MAP_ROW_G && gbWorldSectorZ == 0 );
687 break;
688 case FACT_CURRENT_SECTOR_C5:
689 gubFact[usFact] = ( gWorldSectorX == 5 && gWorldSectorY == MAP_ROW_C && gbWorldSectorZ == 0 );
690 break;
691 case FACT_CURRENT_SECTOR_C13:
692 gubFact[usFact] = ( gWorldSectorX == 13 && gWorldSectorY == MAP_ROW_C && gbWorldSectorZ == 0 );
693 break;
694 case FACT_CARMEN_HAS_TEN_THOUSAND:
695 gubFact[usFact] = (GetProfile(CARMEN).uiMoney >= 10000);
696 break;
697 case FACT_SLAY_IN_SECTOR:
698 gubFact[usFact] = (gMercProfiles[ SLAY ].sSectorX == gWorldSectorX && gMercProfiles[ SLAY ].sSectorY == gWorldSectorY && gMercProfiles[ SLAY ].bSectorZ == gbWorldSectorZ );
699 break;
700 case FACT_SLAY_HIRED_AND_WORKED_FOR_48_HOURS:
701 gubFact[usFact] = ( ( gMercProfiles[ SLAY ].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED ) && ( gMercProfiles[ SLAY ].usTotalDaysServed > 1 ) );
702 break;
703
704 case FACT_SHANK_IN_SQUAD_BUT_NOT_SPEAKING:
705 gubFact[usFact] =
706 FindSoldierByProfileIDOnPlayerTeam(SHANK) != NULL &&
707 gMercProfiles[SHANK].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED &&
708 (gpSrcSoldier == NULL || gpSrcSoldier->ubProfile != SHANK);
709 break;
710
711 case FACT_SHANK_NOT_IN_SECTOR:
712 gubFact[usFact] = FindSoldierByProfileID(SHANK) == NULL;
713 break;
714 case FACT_QUEEN_DEAD:
715 gubFact[usFact] = (gMercProfiles[ QUEEN ].bMercStatus == MERC_IS_DEAD);
716 break;
717 case FACT_MINE_EMPTY:
718 gubFact[usFact] = IsHisMineEmpty( ubProfileID );
719 break;
720 case FACT_MINE_RUNNING_OUT:
721 gubFact[usFact] = IsHisMineRunningOut( ubProfileID );
722 break;
723 case FACT_MINE_PRODUCING_BUT_LOYALTY_LOW:
724 gubFact[usFact] = HasHisMineBeenProducingForPlayerForSomeTime( ubProfileID ) && IsHisMineDisloyal( ubProfileID );
725 break;
726 case FACT_CREATURES_IN_MINE:
727 gubFact[usFact] = IsHisMineInfested( ubProfileID );
728 break;
729 case FACT_PLAYER_LOST_MINE:
730 gubFact[usFact] = IsHisMineLostAndRegained( ubProfileID );
731 break;
732 case FACT_MINE_AT_FULL_PRODUCTION:
733 gubFact[usFact] = IsHisMineAtMaxProduction( ubProfileID );
734 break;
735 case FACT_DYNAMO_IN_J9:
736 gubFact[usFact] = CheckNPCSector( DYNAMO, 9, MAP_ROW_J, 0 ) && NumEnemiesInAnySector( 9, 10, 0 );
737 break;
738 case FACT_DYNAMO_ALIVE:
739 gubFact[usFact] = ( gMercProfiles[ DYNAMO ].bMercStatus != MERC_IS_DEAD );
740 break;
741 case FACT_DYNAMO_SPEAKING_OR_NEARBY:
742 gubFact[usFact] = ( gpSrcSoldier != NULL && (gpSrcSoldier->ubProfile == DYNAMO || ( CheckNPCWithin( gpSrcSoldier->ubProfile, DYNAMO, 10 ) && CheckGuyVisible( gpSrcSoldier->ubProfile, DYNAMO ) ) ) );
743 break;
744 case FACT_JOHN_EPC:
745 gubFact[usFact] = CheckNPCIsEPC( JOHN );
746 break;
747 case FACT_MARY_EPC:
748 gubFact[usFact] = CheckNPCIsEPC( MARY );
749 break;
750 case FACT_JOHN_AND_MARY_EPCS:
751 gubFact[usFact] = CheckNPCIsEPC( JOHN ) && CheckNPCIsEPC( MARY );
752 break;
753 case FACT_MARY_ALIVE:
754 gubFact[usFact] = ( gMercProfiles[ MARY ].bMercStatus != MERC_IS_DEAD );
755 break;
756 case FACT_MARY_BLEEDING:
757 gubFact[usFact] = CheckNPCBleeding( MARY );
758 break;
759 case FACT_JOHN_ALIVE:
760 gubFact[usFact] = ( gMercProfiles[ JOHN ].bMercStatus != MERC_IS_DEAD );
761 break;
762 case FACT_JOHN_BLEEDING:
763 gubFact[usFact] = CheckNPCBleeding( JOHN );
764 break;
765 case FACT_MARY_DEAD:
766 gubFact[usFact] = ( gMercProfiles[ MARY ].bMercStatus == MERC_IS_DEAD );
767 break;
768
769 case FACT_ANOTHER_FIGHT_POSSIBLE:
770 gubFact[usFact] = AnotherFightPossible();
771 break;
772
773 case FACT_RECEIVING_INCOME_FROM_DCAC:
774 gubFact[usFact] = (
775 ( PredictDailyIncomeFromAMine( MINE_DRASSEN ) > 0 ) &&
776 ( PredictDailyIncomeFromAMine( MINE_ALMA ) > 0 ) &&
777 ( PredictDailyIncomeFromAMine( MINE_CAMBRIA ) > 0 ) &&
778 ( PredictDailyIncomeFromAMine( MINE_CHITZENA ) > 0 ) );
779 break;
780
781 case FACT_PLAYER_BEEN_TO_K4:
782 gubFact[usFact] = GetSectorFlagStatus(4, MAP_ROW_K, 1, SF_ALREADY_VISITED);
783 break;
784
785 case FACT_WARDEN_DEAD:
786 gubFact[usFact] = ( gMercProfiles[ WARDEN ].bMercStatus == MERC_IS_DEAD );
787 break;
788
789 case FACT_PLAYER_PAID_FOR_TWO_IN_BROTHEL:
790 gubFact[usFact] = (gMercProfiles[ MADAME ].bNPCData > 1);
791 break;
792
793 case FACT_LOYALTY_OKAY:
794 bTown = ubProfileID != NO_PROFILE ? gMercProfiles[ubProfileID].bTown : BLANK_SECTOR;
795 if( ( bTown != BLANK_SECTOR ) && gTownLoyalty[ bTown ].fStarted && gfTownUsesLoyalty[ bTown ])
796 {
797 gubFact[usFact] = ( (gTownLoyalty[ bTown ].ubRating >= LOYALTY_LOW_THRESHOLD ) && (gTownLoyalty[ bTown ].ubRating < LOYALTY_OK_THRESHOLD ) );
798 }
799 else
800 {
801 gubFact[usFact] = FALSE;
802 }
803 break;
804
805 case FACT_LOYALTY_LOW:
806 bTown = ubProfileID != NO_PROFILE ? gMercProfiles[ubProfileID].bTown : BLANK_SECTOR;
807 if( ( bTown != BLANK_SECTOR ) && gTownLoyalty[ bTown ].fStarted && gfTownUsesLoyalty[ bTown ])
808 {
809 // if Skyrider, ignore low loyalty until he has monologues, and wait at least a day since the latest monologue to avoid a hot/cold attitude
810 if ( ( ubProfileID == SKYRIDER ) &&
811 ( ( guiHelicopterSkyriderTalkState == 0 ) || ( ( GetWorldTotalMin() - guiTimeOfLastSkyriderMonologue ) < ( 24 * 60 ) ) ) )
812 {
813 gubFact[usFact] = FALSE;
814 }
815 else
816 {
817 gubFact[usFact] = (gTownLoyalty[ bTown ].ubRating < LOYALTY_LOW_THRESHOLD );
818 }
819 }
820 else
821 {
822 gubFact[usFact] = FALSE;
823 }
824 break;
825
826 case FACT_LOYALTY_HIGH:
827 bTown = ubProfileID != NO_PROFILE ? gMercProfiles[ubProfileID].bTown : BLANK_SECTOR;
828 if( ( bTown != BLANK_SECTOR ) && gTownLoyalty[ bTown ].fStarted && gfTownUsesLoyalty[ bTown ])
829 {
830 gubFact[usFact] = (gTownLoyalty[ gMercProfiles[ ubProfileID ].bTown ].ubRating >= LOYALTY_HIGH_THRESHOLD );
831 }
832 else
833 {
834 gubFact[usFact] = FALSE;
835 }
836 break;
837
838 case FACT_ELGIN_ALIVE:
839 gubFact[usFact] = ( gMercProfiles[ DRUGGIST ].bMercStatus != MERC_IS_DEAD );
840 break;
841
842 case FACT_SPEAKER_AIM_OR_AIM_NEARBY:
843 gubFact[usFact] = gpDestSoldier && AIMMercWithin( gpDestSoldier->sGridNo, 10 );
844 break;
845
846 case FACT_MALE_SPEAKING_FEMALE_PRESENT:
847 gubFact[usFact] = ( !CheckTalkerFemale() && FemalePresent( ubProfileID ) );
848 break;
849
850 case FACT_PLAYER_OWNS_2_TOWNS_INCLUDING_OMERTA:
851 gubFact[usFact] = ( ( GetNumberOfWholeTownsUnderControl() == 3 ) && IsTownUnderCompleteControlByPlayer( OMERTA ) );
852 break;
853
854 case FACT_PLAYER_OWNS_3_TOWNS_INCLUDING_OMERTA:
855 gubFact[usFact] = ( ( GetNumberOfWholeTownsUnderControl() == 5 ) && IsTownUnderCompleteControlByPlayer( OMERTA ) );
856 break;
857
858 case FACT_PLAYER_OWNS_4_TOWNS_INCLUDING_OMERTA:
859 gubFact[usFact] = ( ( GetNumberOfWholeTownsUnderControl() >= 6 ) && IsTownUnderCompleteControlByPlayer( OMERTA ) );
860 break;
861
862 case FACT_PLAYER_FOUGHT_THREE_TIMES_TODAY:
863 gubFact[usFact] = !BoxerAvailable();
864 break;
865
866 case FACT_PLAYER_DOING_POORLY:
867 gubFact[usFact] = ( CurrentPlayerProgressPercentage() < 20 );
868 break;
869
870 case FACT_PLAYER_DOING_WELL:
871 gubFact[usFact] = ( CurrentPlayerProgressPercentage() > 50 );
872 break;
873
874 case FACT_PLAYER_DOING_VERY_WELL:
875 gubFact[usFact] = ( CurrentPlayerProgressPercentage() > 80 );
876 break;
877
878 case FACT_FATHER_DRUNK_AND_SCIFI_OPTION_ON:
879 gubFact[usFact] = ( ( gMercProfiles[ FATHER ].bNPCData >= 5 ) && gGameOptions.fSciFi );
880 break;
881
882 case FACT_BLOODCAT_QUEST_STARTED_TWO_DAYS_AGO:
883 gubFact[usFact] = ( (gubQuest[ QUEST_BLOODCATS ] != QUESTNOTSTARTED) && (GetWorldTotalMin() - GetTimeQuestWasStarted( QUEST_BLOODCATS ) > 2 * NUM_SEC_IN_DAY / NUM_SEC_IN_MIN) );
884 break;
885
886 case FACT_NOTHING_REPAIRED_YET:
887 gubFact[usFact] = RepairmanIsFixingItemsButNoneAreDoneYet( ubProfileID );
888 break;
889
890 case FACT_NPC_COWERING:
891 gubFact[usFact] = CheckNPCCowering( ubProfileID );
892 break;
893
894 case FACT_TOP_AND_BOTTOM_LEVELS_CLEARED:
895 gubFact[usFact] = ( gubFact[ FACT_TOP_LEVEL_CLEARED ] && gubFact[ FACT_BOTTOM_LEVEL_CLEARED ] );
896 break;
897
898 case FACT_FIRST_BARTENDER:
899 gubFact[ usFact ] = ubProfileID != NO_PROFILE && (gMercProfiles[ubProfileID].bNPCData == 1 || (gMercProfiles[ubProfileID].bNPCData == 0 && CountBartenders() == 0));
900 break;
901
902 case FACT_SECOND_BARTENDER:
903 gubFact[ usFact ] = ubProfileID != NO_PROFILE && (gMercProfiles[ubProfileID].bNPCData == 2 || (gMercProfiles[ubProfileID].bNPCData == 0 && CountBartenders() == 1));
904 break;
905
906 case FACT_THIRD_BARTENDER:
907 gubFact[ usFact ] = ubProfileID != NO_PROFILE && (gMercProfiles[ubProfileID].bNPCData == 3 || (gMercProfiles[ubProfileID].bNPCData == 0 && CountBartenders() == 2));
908 break;
909
910 case FACT_FOURTH_BARTENDER:
911 gubFact[ usFact ] = ubProfileID != NO_PROFILE && (gMercProfiles[ubProfileID].bNPCData == 4 || (gMercProfiles[ubProfileID].bNPCData == 0 && CountBartenders() == 3));
912 break;
913
914 case FACT_NPC_NOT_UNDER_FIRE:
915 gubFact[usFact] = !CheckNPCIsUnderFire( ubProfileID );
916 break;
917
918 case FACT_KINGPIN_NOT_IN_OFFICE:
919 gubFact[usFact] = !( gWorldSectorX == 5 && gWorldSectorY == MAP_ROW_D && NPCInRoomRange( KINGPIN, 30, 39 ) );
920 // 30 to 39
921 break;
922
923 case FACT_DONT_OWE_KINGPIN_MONEY:
924 gubFact[usFact] = (gubQuest[ QUEST_KINGPIN_MONEY ] != QUESTINPROGRESS);
925 break;
926
927 case FACT_NO_CLUB_FIGHTING_ALLOWED:
928 gubFact[usFact] = ( gubQuest[ QUEST_KINGPIN_MONEY ] == QUESTINPROGRESS || gfBoxersResting );// plus other conditions
929 break;
930
931 case FACT_MADDOG_IS_SPEAKER:
932 gubFact[usFact] = ( gubSrcSoldierProfile == MADDOG );
933 break;
934
935 case FACT_PC_HAS_CONRADS_RECRUIT_OPINION:
936 gubFact[usFact] = ( gpDestSoldier && (CalcDesireToTalk( gpDestSoldier->ubProfile, gubSrcSoldierProfile, APPROACH_RECRUIT ) >= 50) );
937 break;
938
939 case FACT_NPC_HOSTILE_OR_PISSED_OFF:
940 gubFact[usFact] = CheckNPCIsEnemy( ubProfileID ) || (gMercProfiles[ ubProfileID ].ubMiscFlags3 & PROFILE_MISC_FLAG3_NPC_PISSED_OFF);
941 break;
942
943 case FACT_TONY_IN_BUILDING:
944 gubFact[usFact] = CheckNPCSector( TONY, 5, MAP_ROW_C, 0 ) && NPCInRoom( TONY, 50 );
945 break;
946
947 case FACT_SHANK_SPEAKING:
948 gubFact[usFact] = ( gpSrcSoldier && gpSrcSoldier->ubProfile == SHANK );
949 break;
950
951 case FACT_ROCKET_RIFLE_EXISTS:
952 gubFact[usFact] = ItemTypeExistsAtLocation( 10472, ROCKET_RIFLE, 0, NULL );
953 break;
954
955 case FACT_DOREEN_ALIVE:
956 gubFact[usFact] = gMercProfiles[ DOREEN ].bMercStatus != MERC_IS_DEAD;
957 break;
958
959 case FACT_WALDO_ALIVE:
960 gubFact[usFact] = gMercProfiles[ WALDO ].bMercStatus != MERC_IS_DEAD;
961 break;
962
963 case FACT_PERKO_ALIVE:
964 gubFact[usFact] = gMercProfiles[ PERKO ].bMercStatus != MERC_IS_DEAD;
965 break;
966
967 case FACT_TONY_ALIVE:
968 gubFact[usFact] = gMercProfiles[ TONY ].bMercStatus != MERC_IS_DEAD;
969 break;
970
971 case FACT_VINCE_ALIVE:
972 gubFact[usFact] = gMercProfiles[ VINCE ].bMercStatus != MERC_IS_DEAD;
973 break;
974
975 case FACT_JENNY_ALIVE:
976 gubFact[usFact] = gMercProfiles[ JENNY ].bMercStatus != MERC_IS_DEAD;
977 break;
978
979 case FACT_ARNOLD_ALIVE:
980 gubFact[usFact] = gMercProfiles[ ARNIE ].bMercStatus != MERC_IS_DEAD;
981 break;
982
983 case FACT_I16_BLOODCATS_KILLED:
984 gubFact[usFact] = (SectorInfo[ SEC_I16 ].bBloodCats == 0);
985 break;
986
987 case FACT_NPC_BANDAGED_TODAY:
988 gubFact[usFact] = ubProfileID != NO_PROFILE && (gMercProfiles[ ubProfileID ].ubMiscFlags2 & PROFILE_MISC_FLAG2_BANDAGED_TODAY) != 0;
989 break;
990
991 case FACT_PLAYER_IN_SAME_ROOM:
992 gubFact[usFact] = PCInSameRoom( ubProfileID );
993 break;
994
995 case FACT_PLAYER_SPOKE_TO_DRASSEN_MINER:
996 gubFact[usFact] = SpokenToHeadMiner( MINE_DRASSEN );
997 break;
998 case FACT_PLAYER_IN_CONTROLLED_DRASSEN_MINE:
999 gubFact[usFact] = ( GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == MINE_DRASSEN && !(StrategicMap[ gWorldSectorX + MAP_WORLD_X * gWorldSectorY ].fEnemyControlled) );
1000 break;
1001 case FACT_PLAYER_SPOKE_TO_CAMBRIA_MINER:
1002 gubFact[usFact] = SpokenToHeadMiner( MINE_CAMBRIA );
1003 break;
1004 case FACT_PLAYER_IN_CONTROLLED_CAMBRIA_MINE:
1005 gubFact[usFact] = ( GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == MINE_CAMBRIA && !(StrategicMap[ gWorldSectorX + MAP_WORLD_X * gWorldSectorY ].fEnemyControlled) );
1006 break;
1007 case FACT_PLAYER_SPOKE_TO_CHITZENA_MINER:
1008 gubFact[usFact] = SpokenToHeadMiner( MINE_CHITZENA );
1009 break;
1010 case FACT_PLAYER_IN_CONTROLLED_CHITZENA_MINE:
1011 gubFact[usFact] = ( GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == MINE_CHITZENA && !(StrategicMap[ gWorldSectorX + MAP_WORLD_X * gWorldSectorY ].fEnemyControlled) );
1012 break;
1013 case FACT_PLAYER_SPOKE_TO_ALMA_MINER:
1014 gubFact[usFact] = SpokenToHeadMiner( MINE_ALMA );
1015 break;
1016 case FACT_PLAYER_IN_CONTROLLED_ALMA_MINE:
1017 gubFact[usFact] = ( GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == MINE_ALMA && !(StrategicMap[ gWorldSectorX + MAP_WORLD_X * gWorldSectorY ].fEnemyControlled) );
1018 break;
1019 case FACT_PLAYER_SPOKE_TO_GRUMM_MINER:
1020 gubFact[usFact] = SpokenToHeadMiner( MINE_GRUMM );
1021 break;
1022 case FACT_PLAYER_IN_CONTROLLED_GRUMM_MINE:
1023 gubFact[usFact] = ( GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == MINE_GRUMM && !(StrategicMap[ gWorldSectorX + MAP_WORLD_X * gWorldSectorY ].fEnemyControlled) );
1024 break;
1025
1026 case FACT_ENOUGH_LOYALTY_TO_TRAIN_MILITIA:
1027 gubFact[usFact] = InTownSectorWithTrainingLoyalty(SECTOR(gWorldSectorX, gWorldSectorY));
1028 break;
1029
1030 case FACT_WALKER_AT_BAR:
1031 gubFact[usFact] = (gMercProfiles[ FATHER ].sSectorX == 13 && gMercProfiles[ FATHER ].sSectorY == MAP_ROW_C);
1032 break;
1033
1034 case FACT_JOEY_ALIVE:
1035 gubFact[usFact] = gMercProfiles[ JOEY ].bMercStatus != MERC_IS_DEAD;
1036 break;
1037
1038 case FACT_UNPROPOSITIONED_FEMALE_SPEAKING_TO_NPC:
1039 gubFact[usFact] = CheckTalkerUnpropositionedFemale();
1040 break;
1041
1042 case FACT_84_AND_85_TRUE:
1043 gubFact[usFact] = CheckFact(FACT_84, ubProfileID) && CheckFact(FACT_HANS_AT_SPOT, ubProfileID);
1044 break;
1045
1046 case FACT_SKYRIDER_IN_B15:
1047 gubFact[ usFact ] = CheckNPCSector( SKYRIDER, 15, MAP_ROW_B, 0 );
1048 break;
1049
1050 case FACT_SKYRIDER_IN_C16:
1051 gubFact[ usFact ] = CheckNPCSector( SKYRIDER, 16, MAP_ROW_C, 0 );
1052 break;
1053 case FACT_SKYRIDER_IN_E14:
1054 gubFact[ usFact ] = CheckNPCSector( SKYRIDER, 14, MAP_ROW_E, 0 );
1055 break;
1056 case FACT_SKYRIDER_IN_D12:
1057 gubFact[ usFact ] = CheckNPCSector( SKYRIDER, 12, MAP_ROW_D, 0 );
1058 break;
1059
1060 case FACT_KINGPIN_IS_ENEMY:
1061 gubFact[usFact] = (gTacticalStatus.fCivGroupHostile[ KINGPIN_CIV_GROUP ] >= CIV_GROUP_WILL_BECOME_HOSTILE);
1062 break;
1063
1064 case FACT_DYNAMO_NOT_SPEAKER:
1065 gubFact[usFact] = !( gpSrcSoldier != NULL && (gpSrcSoldier->ubProfile == DYNAMO ) );
1066 break;
1067
1068 case FACT_PABLO_BRIBED:
1069 gubFact[usFact] = !CheckFact( FACT_PABLOS_BRIBED, ubProfileID );
1070 break;
1071
1072 case FACT_VEHICLE_PRESENT:
1073 gubFact[usFact] =
1074 CheckFact(FACT_OK_USE_HUMMER, ubProfileID) &&
1075 (
1076 FindSoldierByProfileIDOnPlayerTeam(PROF_HUMMER) != NULL ||
1077 FindSoldierByProfileIDOnPlayerTeam(PROF_ICECREAM) != NULL
1078 );
1079 break;
1080
1081 case FACT_PLAYER_KILLED_BOXERS:
1082 gubFact[usFact] = !BoxerExists();
1083 break;
1084
1085 case FACT_245: // Can dimitri be recruited? should be true if already true, OR if Miguel has been recruited already
1086 gubFact[usFact] = gubFact[usFact] || FindSoldierByProfileIDOnPlayerTeam(MIGUEL);
1087 /*
1088 case FACT_:
1089 gubFact[usFact] = ;
1090 break;
1091 */
1092
1093 default:
1094 break;
1095 }
1096 return( gubFact[usFact] );
1097 }
1098
StartQuest(UINT8 ubQuest,INT16 sSectorX,INT16 sSectorY)1099 void StartQuest( UINT8 ubQuest, INT16 sSectorX, INT16 sSectorY )
1100 {
1101 InternalStartQuest( ubQuest, sSectorX, sSectorY, TRUE );
1102 }
1103
1104
InternalStartQuest(UINT8 ubQuest,INT16 sSectorX,INT16 sSectorY,BOOLEAN fUpdateHistory)1105 void InternalStartQuest( UINT8 ubQuest, INT16 sSectorX, INT16 sSectorY, BOOLEAN fUpdateHistory )
1106 {
1107 if ( gubQuest[ubQuest ] == QUESTNOTSTARTED )
1108 {
1109 gubQuest[ubQuest] = QUESTINPROGRESS;
1110
1111 if ( fUpdateHistory )
1112 {
1113 AddHistoryToPlayersLog(HISTORY_QUEST_STARTED, ubQuest, GetWorldTotalMin(), sSectorX, sSectorY);
1114 }
1115 }
1116 else
1117 {
1118 gubQuest[ubQuest] = QUESTINPROGRESS;
1119 }
1120 }
1121
EndQuest(UINT8 ubQuest,INT16 sSectorX,INT16 sSectorY)1122 void EndQuest( UINT8 ubQuest, INT16 sSectorX, INT16 sSectorY )
1123 {
1124 InternalEndQuest( ubQuest, sSectorX, sSectorY, TRUE );
1125 }
1126
InternalEndQuest(UINT8 ubQuest,INT16 sSectorX,INT16 sSectorY,BOOLEAN fUpdateHistory)1127 void InternalEndQuest( UINT8 ubQuest, INT16 sSectorX, INT16 sSectorY, BOOLEAN fUpdateHistory )
1128 {
1129 if ( gubQuest[ubQuest ] == QUESTINPROGRESS )
1130 {
1131 gubQuest[ubQuest] = QUESTDONE;
1132
1133 if ( fUpdateHistory )
1134 {
1135 AddHistoryToPlayersLog(HISTORY_QUEST_FINISHED, ubQuest, GetWorldTotalMin(), sSectorX, sSectorY);
1136 }
1137 }
1138 else
1139 {
1140 gubQuest[ubQuest] = QUESTDONE;
1141 }
1142
1143 if ( ubQuest == QUEST_RESCUE_MARIA )
1144 {
1145 // cheap hack to try to prevent Madame Layla from thinking that you are
1146 // still in the brothel with Maria...
1147 gMercProfiles[ MADAME ].bNPCData = 0;
1148 gMercProfiles[ MADAME ].bNPCData2 = 0;
1149 }
1150 }
1151
1152
InitQuestEngine()1153 void InitQuestEngine()
1154 {
1155 std::fill(std::begin(gubQuest), std::end(gubQuest), 0);
1156 std::fill(std::begin(gubFact), std::end(gubFact), 0);
1157
1158 // semi-hack to make the letter quest start right away
1159 CheckForQuests( 1 );
1160
1161 if ( gGameOptions.fSciFi )
1162 {
1163 // 3 medical boosters
1164 gubCambriaMedicalObjects = 21;
1165 }
1166 else
1167 {
1168 gubCambriaMedicalObjects = 18;
1169 }
1170
1171 gubBoxingMatchesWon = 0;
1172 gubBoxersRests = 0;
1173 gfBoxersResting = FALSE;
1174 }
1175
1176
1177
CheckForQuests(UINT32 uiDay)1178 void CheckForQuests( UINT32 uiDay )
1179 {
1180 // This function gets called at 8:00 AM time of the day
1181
1182 SLOGD("Checking For Quests, Day %d", uiDay );
1183
1184 // -------------------------------------------------------------------------------
1185 // QUEST 0 : DELIVER LETTER
1186 // -------------------------------------------------------------------------------
1187 // The game always starts with DELIVER LETTER quest, so turn it on if it hasn't
1188 // already started
1189 if (gubQuest[QUEST_DELIVER_LETTER] == QUESTNOTSTARTED)
1190 {
1191 AddHistoryToPlayersLog(HISTORY_ACCEPTED_ASSIGNMENT_FROM_ENRICO, 0, GetWorldTotalMin(), -1, -1);
1192 StartQuest( QUEST_DELIVER_LETTER, -1, -1 );
1193 SLOGD("Started DELIVER LETTER quest");
1194 }
1195
1196 // This quest gets turned OFF through conversation with Miguel - when user hands
1197 // Miguel the letter
1198 }
1199
1200
SaveQuestInfoToSavedGameFile(HWFILE const hFile)1201 void SaveQuestInfoToSavedGameFile(HWFILE const hFile)
1202 {
1203 //Save all the states if the Quests
1204 FileWrite(hFile, gubQuest, MAX_QUESTS);
1205
1206 //Save all the states for the facts
1207 FileWrite(hFile, gubFact, NUM_FACTS);
1208 }
1209
1210
LoadQuestInfoFromSavedGameFile(HWFILE const hFile)1211 void LoadQuestInfoFromSavedGameFile(HWFILE const hFile)
1212 {
1213 //Save all the states if the Quests
1214 FileRead(hFile, gubQuest, MAX_QUESTS);
1215
1216 //Save all the states for the facts
1217 FileRead(hFile, gubFact, NUM_FACTS);
1218 }
1219