mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
Added missing #includes Added LUA scripting and console Changed the way tracers are visualized, so they work more like real tracers instead of a light fountain Fixed signedness of many grid variables used in GetMouseMapPos calls Fixed enemy weapon choosing: Sometimes a mortar is chosen but later rejected, but the grenade class was not reset. Caused assertion failure Added checks so enemies don't try to chuck RPG grenades with their hands Now possible to shoot a someone in the head if he's in water Fixed InitSightArrays to also clear soldier interrupt duel points. This was causing an assertion failure elsewhere in the code because the interrupt list still had soldiers on it sometimes when this function was called. Soldiers are much less willing to forfeit their turn over an attempt to use more APs than they have Removed early setting of muzzle flash. This would allow enemies to get an interrupt before you even fired. Fixed item dropping by AI. If AI tried to drop something while standing it would cause deadlock Change to greatly speed up closing the sector inventory window in an unloaded sector Added conditional compile flag to always give robot weapon ready advantage, even for 360 degree sighting Check builddefines.h for the conditional flags. Of greatest interest might be LUA_CONSOLE. This will cause the game to bring up a command console when run. However this console is severely lacking in many areas. If anybody knows of an open-source terminal/console that could be used instead, it would be appreciated. The existing console does bad things when you try to close it, and since it counts as a separate app, it pauses the game while it has focus. LUA scripting is very limited, basically just proof of concept. There is one global variable, the Soldiers array. An array index gives you the soldier in that slot in the currently loaded sector. The soldier has a few things that can be accessed: name (short name); fullname (long name); grid (current grid #, can be changed); walkto(grid) (function to walk to another grid); runto(grid) (function to run to another grid) git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@924 3b4a5df2-a311-0410-b5c6-a8a6f20db521
1395 lines
39 KiB
C++
1395 lines
39 KiB
C++
#ifdef PRECOMPILEDHEADERS
|
|
#include "Strategic All.h"
|
|
#include "GameSettings.h"
|
|
#else
|
|
#include "sgp.h"
|
|
#include "message.h"
|
|
#include "quests.h"
|
|
#include "game clock.h"
|
|
#include "StrategicMap.h"
|
|
#include "soldier profile.h"
|
|
#include "LaptopSave.h"
|
|
#include "Handle Items.h"
|
|
#include "overhead.h"
|
|
#include "Interface Dialogue.h"
|
|
#include "Soldier Profile.h"
|
|
#include "Isometric Utils.h"
|
|
#include "Render Fun.h"
|
|
#include "History.h"
|
|
#include "Map Screen Helicopter.h"
|
|
#include "Overhead.h"
|
|
#include "Strategic Mines.h"
|
|
#include "Boxing.h"
|
|
#include "Campaign Types.h"
|
|
#include "Strategic Town Loyalty.h"
|
|
#include "Queen Command.h"
|
|
#include "Campaign.h"
|
|
#include "GameSettings.h"
|
|
#include "Arms Dealer Init.h"
|
|
#include "GameSettings.h"
|
|
#include "Random.h"
|
|
#include "Assignments.h"
|
|
#include "strategic.h"
|
|
#include "Strategic Event Handler.h"
|
|
#endif
|
|
|
|
#include "BobbyRMailOrder.h"
|
|
|
|
|
|
#define TESTQUESTS
|
|
|
|
extern SOLDIERTYPE * gpSrcSoldier;
|
|
extern SOLDIERTYPE * gpDestSoldier;
|
|
|
|
UINT8 gubQuest[MAX_QUESTS];
|
|
UINT8 gubFact[ NUM_FACTS ]; // this has to be updated when we figure out how many facts we have
|
|
|
|
INT16 gsFoodQuestSectorX;
|
|
INT16 gsFoodQuestSectorY;
|
|
|
|
extern void GuaranteeAtLeastXItemsOfIndex( UINT8 ubArmsDealer, UINT16 usItemIndex, UINT8 ubHowMany );
|
|
|
|
|
|
|
|
|
|
void SetFactTrue( UINT16 usFact )
|
|
{
|
|
// This function is here just for control flow purposes (debug breakpoints)
|
|
// and code is more readable that way
|
|
|
|
// must intercept when Jake is first trigered to start selling fuel
|
|
if ( ( usFact == FACT_ESTONI_REFUELLING_POSSIBLE ) && ( CheckFact( usFact, 0 ) == FALSE ) )
|
|
{
|
|
// give him some gas...
|
|
GuaranteeAtLeastXItemsOfIndex( ARMS_DEALER_JAKE, GAS_CAN, ( UINT8 ) ( 4 + Random( 3 ) ) );
|
|
}
|
|
|
|
gubFact[usFact] = TRUE;
|
|
}
|
|
|
|
void SetFactFalse( UINT16 usFact )
|
|
{
|
|
gubFact[usFact] = FALSE;
|
|
}
|
|
|
|
BOOLEAN CheckForNewShipment( void )
|
|
{
|
|
ITEM_POOL * pItemPool;
|
|
|
|
if ( ( gWorldSectorX == BOBBYR_SHIPPING_DEST_SECTOR_X ) && ( gWorldSectorY == BOBBYR_SHIPPING_DEST_SECTOR_Y ) && ( gbWorldSectorZ == BOBBYR_SHIPPING_DEST_SECTOR_Z ) )
|
|
{
|
|
if ( GetItemPool( BOBBYR_SHIPPING_DEST_GRIDNO, &pItemPool, 0 ) )
|
|
{
|
|
return( !( ITEMPOOL_VISIBLE( pItemPool ) ) );
|
|
}
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
BOOLEAN CheckNPCWounded( UINT8 ubProfileID, BOOLEAN fByPlayerOnly )
|
|
{
|
|
SOLDIERTYPE * pSoldier;
|
|
|
|
// is the NPC is wounded at all?
|
|
pSoldier = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if (pSoldier && pSoldier->bLife < pSoldier->bLifeMax)
|
|
{
|
|
if (fByPlayerOnly)
|
|
{
|
|
if (gMercProfiles[ubProfileID].ubMiscFlags & PROFILE_MISC_FLAG_WOUNDEDBYPLAYER)
|
|
{
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
|
|
}
|
|
}
|
|
|
|
BOOLEAN CheckNPCInOkayHealth( UINT8 ubProfileID )
|
|
{
|
|
SOLDIERTYPE * pSoldier;
|
|
|
|
// is the NPC at better than half health?
|
|
pSoldier = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if (pSoldier && pSoldier->bLife > (pSoldier->bLifeMax / 2) && pSoldier->bLife > 30)
|
|
{
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
BOOLEAN CheckNPCBleeding( UINT8 ubProfileID )
|
|
{
|
|
SOLDIERTYPE * pSoldier;
|
|
|
|
// the NPC is wounded...
|
|
pSoldier = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if (pSoldier && pSoldier->bLife > 0 && pSoldier->bBleeding > 0)
|
|
{
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
}
|
|
|
|
BOOLEAN CheckNPCWithin( UINT8 ubFirstNPC, UINT8 ubSecondNPC, UINT8 ubMaxDistance )
|
|
{
|
|
SOLDIERTYPE * pFirstNPC, * pSecondNPC;
|
|
|
|
pFirstNPC = FindSoldierByProfileID( ubFirstNPC, FALSE );
|
|
pSecondNPC = FindSoldierByProfileID( ubSecondNPC, FALSE );
|
|
if (!pFirstNPC || !pSecondNPC)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
return( PythSpacesAway( pFirstNPC->sGridNo, pSecondNPC->sGridNo ) <= ubMaxDistance );
|
|
}
|
|
|
|
BOOLEAN CheckGuyVisible( UINT8 ubNPC, UINT8 ubGuy )
|
|
{
|
|
// NB ONLY WORKS IF ON DIFFERENT TEAMS
|
|
SOLDIERTYPE * pNPC, * pGuy;
|
|
|
|
pNPC = FindSoldierByProfileID( ubNPC, FALSE );
|
|
pGuy = FindSoldierByProfileID( ubGuy, FALSE );
|
|
if (!pNPC || !pGuy)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
if (pNPC->bOppList[ pGuy->ubID ] == SEEN_CURRENTLY )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
BOOLEAN CheckNPCAt( UINT8 ubNPC, INT16 sGridNo )
|
|
{
|
|
SOLDIERTYPE * pNPC;
|
|
|
|
pNPC = FindSoldierByProfileID( ubNPC, FALSE );
|
|
if (!pNPC)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
return( pNPC->sGridNo == sGridNo );
|
|
}
|
|
|
|
BOOLEAN CheckNPCIsEnemy( UINT8 ubProfileID )
|
|
{
|
|
SOLDIERTYPE * pNPC;
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if (!pNPC)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
if (pNPC->bSide == gbPlayerNum || pNPC->bNeutral)
|
|
{
|
|
if (pNPC->ubCivilianGroup != NON_CIV_GROUP)
|
|
{
|
|
// although the soldier is NOW the same side, this civ group could be set to "will become hostile"
|
|
return( gTacticalStatus.fCivGroupHostile[ pNPC->ubCivilianGroup ] >= CIV_GROUP_WILL_BECOME_HOSTILE );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
|
|
BOOLEAN CheckIfMercIsNearNPC( SOLDIERTYPE *pMerc, UINT8 ubProfileId )
|
|
{
|
|
SOLDIERTYPE * pNPC;
|
|
INT16 sGridNo;
|
|
|
|
// no merc nearby?
|
|
if ( pMerc == NULL )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileId, FALSE );
|
|
if ( pNPC == NULL )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
sGridNo = pNPC->sGridNo;
|
|
|
|
// is the merc and NPC close enough?
|
|
if (PythSpacesAway( sGridNo, pMerc->sGridNo ) <= 9)
|
|
{
|
|
return( TRUE );
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
INT8 NumWoundedMercsNearby( UINT8 ubProfileID )
|
|
{
|
|
INT8 bNumber = 0;
|
|
UINT32 uiLoop;
|
|
SOLDIERTYPE * pNPC;
|
|
SOLDIERTYPE * pSoldier;
|
|
INT16 sGridNo;
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if (!pNPC)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
sGridNo = pNPC->sGridNo;
|
|
|
|
for ( uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
|
|
{
|
|
pSoldier = MercSlots[ uiLoop ];
|
|
|
|
if ( pSoldier && pSoldier->bTeam == gbPlayerNum && pSoldier->bLife > 0 && pSoldier->bLife < pSoldier->bLifeMax && pSoldier->bAssignment != ASSIGNMENT_HOSPITAL )
|
|
{
|
|
if (PythSpacesAway( sGridNo, pSoldier->sGridNo ) <= HOSPITAL_PATIENT_DISTANCE)
|
|
{
|
|
bNumber++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return( bNumber );
|
|
}
|
|
|
|
INT8 NumMercsNear( UINT8 ubProfileID, UINT8 ubMaxDist )
|
|
{
|
|
INT8 bNumber = 0;
|
|
UINT32 uiLoop;
|
|
SOLDIERTYPE * pNPC;
|
|
SOLDIERTYPE * pSoldier;
|
|
INT16 sGridNo;
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if (!pNPC)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
sGridNo = pNPC->sGridNo;
|
|
|
|
for ( uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
|
|
{
|
|
pSoldier = MercSlots[ uiLoop ];
|
|
|
|
if ( pSoldier && pSoldier->bTeam == gbPlayerNum && pSoldier->bLife >= OKLIFE )
|
|
{
|
|
if (PythSpacesAway( sGridNo, pSoldier->sGridNo ) <= ubMaxDist)
|
|
{
|
|
bNumber++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return( bNumber );
|
|
}
|
|
|
|
BOOLEAN CheckNPCIsEPC( UINT8 ubProfileID )
|
|
{
|
|
SOLDIERTYPE * pNPC;
|
|
|
|
if ( gMercProfiles[ ubProfileID ].bMercStatus == MERC_IS_DEAD )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileID, TRUE );
|
|
if (!pNPC)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
return( (pNPC->ubWhatKindOfMercAmI == MERC_TYPE__EPC ) );
|
|
}
|
|
|
|
BOOLEAN NPCInRoom( UINT8 ubProfileID, UINT8 ubRoomID )
|
|
{
|
|
SOLDIERTYPE * pNPC;
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if ( !pNPC || (gubWorldRoomInfo[ pNPC->sGridNo ] != ubRoomID) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOLEAN NPCInRoomRange( UINT8 ubProfileID, UINT8 ubRoomID1, UINT8 ubRoomID2 )
|
|
{
|
|
SOLDIERTYPE * pNPC;
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if ( !pNPC || (gubWorldRoomInfo[ pNPC->sGridNo ] < ubRoomID1) || (gubWorldRoomInfo[ pNPC->sGridNo ] > ubRoomID2) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOLEAN PCInSameRoom( UINT8 ubProfileID )
|
|
{
|
|
SOLDIERTYPE * pNPC;
|
|
UINT8 ubRoom;
|
|
INT8 bLoop;
|
|
SOLDIERTYPE * pSoldier;
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if ( !pNPC )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
ubRoom = gubWorldRoomInfo[ pNPC->sGridNo ];
|
|
|
|
for ( bLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; bLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; bLoop++ )
|
|
{
|
|
pSoldier = MercPtrs[ bLoop ];
|
|
if ( pSoldier && pSoldier->bActive && pSoldier->bInSector )
|
|
{
|
|
if ( gubWorldRoomInfo[ pSoldier->sGridNo ] == ubRoom )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
BOOLEAN CheckTalkerStrong( void )
|
|
{
|
|
if (gpSrcSoldier && gpSrcSoldier->bTeam == gbPlayerNum)
|
|
{
|
|
return( gpSrcSoldier->bStrength >= 84 );
|
|
}
|
|
else if (gpDestSoldier && gpDestSoldier->bTeam == gbPlayerNum)
|
|
{
|
|
return( gpDestSoldier->bStrength >= 84 );
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
BOOLEAN CheckTalkerFemale( void )
|
|
{
|
|
if (gpSrcSoldier && gpSrcSoldier->bTeam == gbPlayerNum && gpSrcSoldier->ubProfile != NO_PROFILE)
|
|
{
|
|
return( gMercProfiles[ gpSrcSoldier->ubProfile ].bSex == FEMALE );
|
|
}
|
|
else if (gpDestSoldier && gpDestSoldier->bTeam == gbPlayerNum && gpDestSoldier->ubProfile != NO_PROFILE)
|
|
{
|
|
return( gMercProfiles[ gpDestSoldier->ubProfile ].bSex == FEMALE );
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
BOOLEAN CheckTalkerUnpropositionedFemale( void )
|
|
{
|
|
if (gpSrcSoldier && gpSrcSoldier->bTeam == gbPlayerNum && gpSrcSoldier->ubProfile != NO_PROFILE)
|
|
{
|
|
if ( !(gMercProfiles[ gpSrcSoldier->ubProfile ].ubMiscFlags2 & PROFILE_MISC_FLAG2_ASKED_BY_HICKS) )
|
|
{
|
|
return( gMercProfiles[ gpSrcSoldier->ubProfile ].bSex == FEMALE );
|
|
}
|
|
}
|
|
else if (gpDestSoldier && gpDestSoldier->bTeam == gbPlayerNum && gpDestSoldier->ubProfile != NO_PROFILE)
|
|
{
|
|
if ( !(gMercProfiles[ gpDestSoldier->ubProfile ].ubMiscFlags2 & PROFILE_MISC_FLAG2_ASKED_BY_HICKS) )
|
|
{
|
|
return( gMercProfiles[ gpDestSoldier->ubProfile ].bSex == FEMALE );
|
|
}
|
|
}
|
|
return( FALSE );
|
|
}
|
|
|
|
INT8 NumMalesPresent( UINT8 ubProfileID )
|
|
{
|
|
INT8 bNumber = 0;
|
|
UINT32 uiLoop;
|
|
SOLDIERTYPE * pNPC;
|
|
SOLDIERTYPE * pSoldier;
|
|
INT16 sGridNo;
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if (!pNPC)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
sGridNo = pNPC->sGridNo;
|
|
|
|
for ( uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
|
|
{
|
|
pSoldier = MercSlots[ uiLoop ];
|
|
|
|
if ( pSoldier && pSoldier->bTeam == gbPlayerNum && pSoldier->bLife >= OKLIFE)
|
|
{
|
|
if ( pSoldier->ubProfile != NO_PROFILE && gMercProfiles[ pSoldier->ubProfile].bSex == MALE )
|
|
{
|
|
if (PythSpacesAway( sGridNo, pSoldier->sGridNo ) <= 8)
|
|
{
|
|
bNumber++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( bNumber );
|
|
}
|
|
|
|
|
|
BOOLEAN FemalePresent( UINT8 ubProfileID )
|
|
{
|
|
UINT32 uiLoop;
|
|
SOLDIERTYPE * pNPC;
|
|
SOLDIERTYPE * pSoldier;
|
|
INT16 sGridNo;
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if (!pNPC)
|
|
{
|
|
return( FALSE );
|
|
}
|
|
sGridNo = pNPC->sGridNo;
|
|
|
|
for ( uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
|
|
{
|
|
pSoldier = MercSlots[ uiLoop ];
|
|
|
|
if ( pSoldier && pSoldier->bTeam == gbPlayerNum && pSoldier->bLife >= OKLIFE)
|
|
{
|
|
if ( pSoldier->ubProfile != NO_PROFILE && gMercProfiles[ pSoldier->ubProfile].bSex == FEMALE )
|
|
{
|
|
if (PythSpacesAway( sGridNo, pSoldier->sGridNo ) <= 10)
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
BOOLEAN CheckPlayerHasHead( void )
|
|
{
|
|
INT8 bLoop;
|
|
SOLDIERTYPE * pSoldier;
|
|
|
|
for ( bLoop = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; bLoop <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; bLoop++ )
|
|
{
|
|
pSoldier = MercPtrs[ bLoop ];
|
|
|
|
if ( pSoldier->bActive && pSoldier->bLife > 0 )
|
|
{
|
|
if ( FindObjInObjRange( pSoldier, HEAD_2, HEAD_7 ) != NO_SLOT )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
BOOLEAN CheckNPCSector( UINT8 ubProfileID, INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ )
|
|
{
|
|
SOLDIERTYPE * pSoldier;
|
|
|
|
pSoldier = FindSoldierByProfileID( ubProfileID, TRUE );
|
|
|
|
if( pSoldier )
|
|
{
|
|
if (pSoldier->sSectorX == sSectorX &&
|
|
pSoldier->sSectorY == sSectorY &&
|
|
pSoldier->bSectorZ == bSectorZ )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
else if (gMercProfiles[ubProfileID].sSectorX == sSectorX &&
|
|
gMercProfiles[ubProfileID].sSectorY == sSectorY &&
|
|
gMercProfiles[ubProfileID].bSectorZ == bSectorZ )
|
|
{
|
|
return( TRUE );
|
|
}
|
|
|
|
return( FALSE );
|
|
|
|
}
|
|
|
|
BOOLEAN AIMMercWithin( INT16 sGridNo, INT16 sDistance )
|
|
{
|
|
UINT32 uiLoop;
|
|
SOLDIERTYPE * pSoldier;
|
|
|
|
for ( uiLoop = 0; uiLoop < guiNumMercSlots; uiLoop++)
|
|
{
|
|
pSoldier = MercSlots[ uiLoop ];
|
|
|
|
if ( pSoldier && (pSoldier->bTeam == gbPlayerNum) && (pSoldier->bLife >= OKLIFE) && ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__AIM_MERC ) )
|
|
{
|
|
if (PythSpacesAway( sGridNo, pSoldier->sGridNo ) <= sDistance)
|
|
{
|
|
return( TRUE );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
BOOLEAN CheckNPCCowering( UINT8 ubProfileID )
|
|
{
|
|
SOLDIERTYPE * pNPC;
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if ( !pNPC )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
return( ( (pNPC->uiStatusFlags & SOLDIER_COWERING) != 0) );
|
|
}
|
|
|
|
UINT8 CountBartenders( void )
|
|
{
|
|
UINT8 ubLoop;
|
|
UINT8 ubBartenders = 0;
|
|
|
|
for( ubLoop = HERVE; ubLoop <= CARLO; ubLoop++ )
|
|
{
|
|
if (gMercProfiles[ ubLoop ].bNPCData != 0 )
|
|
{
|
|
ubBartenders++;
|
|
}
|
|
}
|
|
return( ubBartenders );
|
|
}
|
|
|
|
BOOLEAN CheckNPCIsUnderFire( UINT8 ubProfileID )
|
|
{
|
|
SOLDIERTYPE * pNPC;
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if ( !pNPC )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
return( pNPC->bUnderFire != 0 );
|
|
}
|
|
|
|
BOOLEAN NPCHeardShot( UINT8 ubProfileID )
|
|
{
|
|
SOLDIERTYPE * pNPC;
|
|
|
|
pNPC = FindSoldierByProfileID( ubProfileID, FALSE );
|
|
if ( !pNPC )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
return( pNPC->ubMiscSoldierFlags & SOLDIER_MISC_HEARD_GUNSHOT );
|
|
}
|
|
|
|
BOOLEAN InTownSectorWithTrainingLoyalty( INT16 sSectorX, INT16 sSectorY )
|
|
{
|
|
UINT8 ubTown;
|
|
INT32 iMinLoyaltyToTrain = gGameExternalOptions.iMinLoyaltyToTrain;
|
|
DebugMsg (TOPIC_JA2,DBG_LEVEL_3,"Quests");
|
|
|
|
ubTown = GetTownIdForSector( sSectorX, sSectorY );
|
|
if ( ( ubTown != BLANK_SECTOR ) && gTownLoyalty[ ubTown ].fStarted && gfTownUsesLoyalty[ ubTown ])
|
|
{
|
|
return( gTownLoyalty[ ubTown ].ubRating >= iMinLoyaltyToTrain );
|
|
}
|
|
else
|
|
{
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
BOOLEAN CheckFact( UINT16 usFact, UINT8 ubProfileID )
|
|
{
|
|
INT8 bTown = -1;
|
|
|
|
|
|
switch( usFact )
|
|
{
|
|
case FACT_DIMITRI_DEAD:
|
|
gubFact[ usFact ] = (gMercProfiles[ DIMITRI ].bMercStatus == MERC_IS_DEAD );
|
|
break;
|
|
case FACT_CURRENT_SECTOR_IS_SAFE:
|
|
gubFact[FACT_CURRENT_SECTOR_IS_SAFE] = !( ( (gTacticalStatus.fEnemyInSector && NPCHeardShot( ubProfileID ) ) || gTacticalStatus.uiFlags & INCOMBAT ) );
|
|
break;
|
|
case FACT_BOBBYRAY_SHIPMENT_IN_TRANSIT:
|
|
case FACT_NEW_BOBBYRAY_SHIPMENT_WAITING:
|
|
if ( gubFact[FACT_PABLO_PUNISHED_BY_PLAYER] == TRUE && gubFact[FACT_PABLO_RETURNED_GOODS] == FALSE && gMercProfiles[ PABLO ].bMercStatus != MERC_IS_DEAD )
|
|
{
|
|
gubFact[FACT_BOBBYRAY_SHIPMENT_IN_TRANSIT] = FALSE;
|
|
gubFact[FACT_NEW_BOBBYRAY_SHIPMENT_WAITING] = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (CheckForNewShipment()) // if new stuff waiting unseen in Drassen
|
|
{
|
|
gubFact[FACT_BOBBYRAY_SHIPMENT_IN_TRANSIT] = FALSE;
|
|
gubFact[FACT_NEW_BOBBYRAY_SHIPMENT_WAITING] = TRUE;
|
|
}
|
|
else if ( CountNumberOfBobbyPurchasesThatAreInTransit() > 0) // if stuff in transit
|
|
{
|
|
if ( gubFact[ FACT_PACKAGE_DAMAGED ] == TRUE )
|
|
{
|
|
gubFact[FACT_BOBBYRAY_SHIPMENT_IN_TRANSIT] = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gubFact[FACT_BOBBYRAY_SHIPMENT_IN_TRANSIT] = TRUE;
|
|
}
|
|
gubFact[FACT_NEW_BOBBYRAY_SHIPMENT_WAITING] = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gubFact[FACT_BOBBYRAY_SHIPMENT_IN_TRANSIT] = FALSE;
|
|
gubFact[FACT_NEW_BOBBYRAY_SHIPMENT_WAITING] = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case FACT_NPC_WOUNDED:
|
|
gubFact[FACT_NPC_WOUNDED] = CheckNPCWounded( ubProfileID, FALSE );
|
|
break;
|
|
case FACT_NPC_WOUNDED_BY_PLAYER:
|
|
gubFact[FACT_NPC_WOUNDED_BY_PLAYER] = CheckNPCWounded( ubProfileID, TRUE );
|
|
break;
|
|
case FACT_IRA_NOT_PRESENT:
|
|
gubFact[FACT_IRA_NOT_PRESENT] = !CheckNPCWithin( ubProfileID, IRA, 10 );
|
|
break;
|
|
case FACT_IRA_TALKING:
|
|
gubFact[FACT_IRA_TALKING] = ( gubSrcSoldierProfile == 59 );
|
|
break;
|
|
case FACT_IRA_UNHIRED_AND_ALIVE:
|
|
if ( gMercProfiles[ IRA ].bMercStatus != MERC_IS_DEAD && CheckNPCSector( IRA, 10, 1, 1) && !(gMercProfiles[IRA].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED) )
|
|
{
|
|
gubFact[FACT_IRA_UNHIRED_AND_ALIVE] = TRUE;
|
|
}
|
|
else
|
|
{
|
|
gubFact[FACT_IRA_UNHIRED_AND_ALIVE] = FALSE;
|
|
}
|
|
break;
|
|
case FACT_NPC_BLEEDING:
|
|
gubFact[FACT_NPC_BLEEDING] = CheckNPCBleeding( ubProfileID );
|
|
break;
|
|
case FACT_NPC_BLEEDING_BUT_OKAY:
|
|
if ( CheckNPCBleeding( ubProfileID ) && CheckNPCInOkayHealth( ubProfileID ) )
|
|
{
|
|
gubFact[FACT_NPC_BLEEDING_BUT_OKAY] = TRUE;
|
|
}
|
|
else
|
|
{
|
|
gubFact[FACT_NPC_BLEEDING_BUT_OKAY] = FALSE;
|
|
}
|
|
break;
|
|
|
|
case FACT_PLAYER_HAS_HEAD_AND_CARMEN_IN_SAN_MONA:
|
|
gubFact[usFact] = (CheckNPCSector( CARMEN, 5, MAP_ROW_C, 0 ) && CheckPlayerHasHead() );
|
|
break;
|
|
|
|
case FACT_PLAYER_HAS_HEAD_AND_CARMEN_IN_CAMBRIA:
|
|
gubFact[usFact] = (CheckNPCSector( CARMEN, 9, MAP_ROW_G, 0 ) && CheckPlayerHasHead() );
|
|
break;
|
|
|
|
case FACT_PLAYER_HAS_HEAD_AND_CARMEN_IN_DRASSEN:
|
|
gubFact[usFact] = (CheckNPCSector( CARMEN, 13, MAP_ROW_C, 0 ) && CheckPlayerHasHead() );
|
|
break;
|
|
|
|
case FACT_NPC_OWED_MONEY:
|
|
gubFact[FACT_NPC_OWED_MONEY] = (gMercProfiles[ubProfileID].iBalance < 0);
|
|
break;
|
|
|
|
case FACT_FATHER_DRUNK:
|
|
gubFact[FACT_FATHER_DRUNK] = ( gMercProfiles[ FATHER ].bNPCData >= 5 );
|
|
break;
|
|
|
|
case FACT_MICKY_DRUNK:
|
|
gubFact[FACT_MICKY_DRUNK] = ( gMercProfiles[ MICKY ].bNPCData >= 5 );
|
|
break;
|
|
|
|
case FACT_BRENDA_IN_STORE_AND_ALIVE:
|
|
// ensure alive
|
|
if ( gMercProfiles[ 85 ].bMercStatus == MERC_IS_DEAD)
|
|
{
|
|
gubFact[FACT_BRENDA_IN_STORE_AND_ALIVE] = FALSE;
|
|
}
|
|
// ensure in a building and nearby
|
|
else if ( !(NPCInRoom( 85, 47 ) ) )
|
|
{
|
|
gubFact[FACT_BRENDA_IN_STORE_AND_ALIVE] = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gubFact[FACT_BRENDA_IN_STORE_AND_ALIVE] = CheckNPCWithin( ubProfileID, 85, 12 );
|
|
}
|
|
break;
|
|
case FACT_BRENDA_DEAD:
|
|
gubFact[FACT_BRENDA_DEAD] = (gMercProfiles[ 85 ].bMercStatus == MERC_IS_DEAD);
|
|
break;
|
|
case FACT_NPC_IS_ENEMY:
|
|
gubFact[FACT_NPC_IS_ENEMY] = CheckNPCIsEnemy( ubProfileID ) || gMercProfiles[ ubProfileID ].ubMiscFlags2 & PROFILE_MISC_FLAG2_NEEDS_TO_SAY_HOSTILE_QUOTE;
|
|
break;
|
|
/*
|
|
case FACT_SKYRIDER_CLOSE_TO_CHOPPER:
|
|
SetUpHelicopterForPlayer( 13, MAP_ROW_B );
|
|
break;
|
|
*/
|
|
case FACT_SPIKE_AT_DOOR:
|
|
gubFact[FACT_SPIKE_AT_DOOR] = CheckNPCAt( 93, 9817 );
|
|
break;
|
|
case FACT_WOUNDED_MERCS_NEARBY:
|
|
gubFact[usFact] = (NumWoundedMercsNearby( ubProfileID ) > 0);
|
|
break;
|
|
case FACT_ONE_WOUNDED_MERC_NEARBY:
|
|
gubFact[usFact] = (NumWoundedMercsNearby( ubProfileID ) == 1);
|
|
break;
|
|
case FACT_MULTIPLE_WOUNDED_MERCS_NEARBY:
|
|
gubFact[usFact] = (NumWoundedMercsNearby( ubProfileID ) > 1);
|
|
break;
|
|
case FACT_HANS_AT_SPOT:
|
|
gubFact[usFact] = CheckNPCAt( 117, 13523 );
|
|
break;
|
|
case FACT_MULTIPLE_MERCS_CLOSE:
|
|
gubFact[usFact] = ( NumMercsNear( ubProfileID, 3 ) > 1 );
|
|
break;
|
|
case FACT_SOME_MERCS_CLOSE:
|
|
gubFact[usFact] = ( NumMercsNear( ubProfileID, 3 ) > 0 );
|
|
break;
|
|
case FACT_MARIA_ESCORTED:
|
|
gubFact[usFact] = CheckNPCIsEPC( MARIA );
|
|
break;
|
|
case FACT_JOEY_ESCORTED:
|
|
gubFact[usFact] = CheckNPCIsEPC( JOEY );
|
|
break;
|
|
case FACT_ESCORTING_SKYRIDER:
|
|
gubFact[usFact] = CheckNPCIsEPC( SKYRIDER );
|
|
break;
|
|
case FACT_MARIA_ESCORTED_AT_LEATHER_SHOP:
|
|
gubFact[usFact] = ( CheckNPCIsEPC( MARIA ) && (NPCInRoom( MARIA, 2 )) );
|
|
break;
|
|
case FACT_PC_STRONG_AND_LESS_THAN_3_MALES_PRESENT:
|
|
gubFact[usFact] = ( CheckTalkerStrong() && (NumMalesPresent( ubProfileID ) < 3) );
|
|
break;
|
|
case FACT_PC_STRONG_AND_3_PLUS_MALES_PRESENT:
|
|
gubFact[usFact] = ( CheckTalkerStrong() && (NumMalesPresent( ubProfileID ) >= 3) );
|
|
break;
|
|
case FACT_FEMALE_SPEAKING_TO_NPC:
|
|
gubFact[usFact] = CheckTalkerFemale();
|
|
break;
|
|
case FACT_CARMEN_IN_C5:
|
|
gubFact[usFact] = CheckNPCSector( 78, 5, MAP_ROW_C, 0 );
|
|
break;
|
|
case FACT_JOEY_IN_C5:
|
|
gubFact[usFact] = CheckNPCSector( 90, 5, MAP_ROW_C, 0 );
|
|
break;
|
|
case FACT_JOEY_NEAR_MARTHA:
|
|
gubFact[usFact] = CheckNPCWithin( 90, 109, 5 ) && ( CheckGuyVisible( MARTHA, JOEY ) || CheckGuyVisible( JOEY, MARTHA ) );
|
|
break;
|
|
case FACT_JOEY_DEAD:
|
|
gubFact[usFact] = gMercProfiles[ JOEY ].bMercStatus == MERC_IS_DEAD;
|
|
break;
|
|
case FACT_MERC_NEAR_MARTHA:
|
|
gubFact[usFact] = ( NumMercsNear( ubProfileID, 5 ) > 0 );
|
|
break;
|
|
case FACT_REBELS_HATE_PLAYER:
|
|
gubFact[usFact] = (gTacticalStatus.fCivGroupHostile[ REBEL_CIV_GROUP ] == CIV_GROUP_HOSTILE);
|
|
break;
|
|
case FACT_CURRENT_SECTOR_G9:
|
|
gubFact[usFact] = ( gWorldSectorX == 9 && gWorldSectorY == MAP_ROW_G && gbWorldSectorZ == 0 );
|
|
break;
|
|
case FACT_CURRENT_SECTOR_C5:
|
|
gubFact[usFact] = ( gWorldSectorX == 5 && gWorldSectorY == MAP_ROW_C && gbWorldSectorZ == 0 );
|
|
break;
|
|
case FACT_CURRENT_SECTOR_C13:
|
|
gubFact[usFact] = ( gWorldSectorX == 13 && gWorldSectorY == MAP_ROW_C && gbWorldSectorZ == 0 );
|
|
break;
|
|
case FACT_CARMEN_HAS_TEN_THOUSAND:
|
|
gubFact[usFact] = ( gMercProfiles[ 78 ].uiMoney >= 10000 );
|
|
break;
|
|
case FACT_SLAY_IN_SECTOR:
|
|
gubFact[usFact] = (gMercProfiles[ SLAY ].sSectorX == gWorldSectorX && gMercProfiles[ SLAY ].sSectorY == gWorldSectorY && gMercProfiles[ SLAY ].bSectorZ == gbWorldSectorZ );
|
|
break;
|
|
case FACT_SLAY_HIRED_AND_WORKED_FOR_48_HOURS:
|
|
gubFact[usFact] = ( ( gMercProfiles[ SLAY ].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED ) && ( gMercProfiles[ SLAY ].usTotalDaysServed > 1 ) );
|
|
break;
|
|
case FACT_SHANK_IN_SQUAD_BUT_NOT_SPEAKING:
|
|
gubFact[usFact] = ( ( FindSoldierByProfileID( SHANK, TRUE ) != NULL) && ( gMercProfiles[ SHANK ].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED ) && ( gpSrcSoldier == NULL || gpSrcSoldier->ubProfile != SHANK ) );
|
|
break;
|
|
case FACT_SHANK_NOT_IN_SECTOR:
|
|
gubFact[usFact] = ( FindSoldierByProfileID( SHANK, FALSE ) == NULL );
|
|
break;
|
|
case FACT_QUEEN_DEAD:
|
|
gubFact[usFact] = (gMercProfiles[ QUEEN ].bMercStatus == MERC_IS_DEAD);
|
|
break;
|
|
case FACT_MINE_EMPTY:
|
|
gubFact[usFact] = IsHisMineEmpty( ubProfileID );
|
|
break;
|
|
case FACT_MINE_RUNNING_OUT:
|
|
gubFact[usFact] = IsHisMineRunningOut( ubProfileID );
|
|
break;
|
|
case FACT_MINE_PRODUCING_BUT_LOYALTY_LOW:
|
|
gubFact[usFact] = HasHisMineBeenProducingForPlayerForSomeTime( ubProfileID ) && IsHisMineDisloyal( ubProfileID );
|
|
break;
|
|
case FACT_CREATURES_IN_MINE:
|
|
gubFact[usFact] = IsHisMineInfested( ubProfileID );
|
|
break;
|
|
case FACT_PLAYER_LOST_MINE:
|
|
gubFact[usFact] = IsHisMineLostAndRegained( ubProfileID );
|
|
break;
|
|
case FACT_MINE_AT_FULL_PRODUCTION:
|
|
gubFact[usFact] = IsHisMineAtMaxProduction( ubProfileID );
|
|
break;
|
|
case FACT_DYNAMO_IN_J9:
|
|
gubFact[usFact] = CheckNPCSector( DYNAMO, 9, MAP_ROW_J, 0 ) && NumEnemiesInAnySector( 9, 10, 0 );
|
|
break;
|
|
case FACT_DYNAMO_ALIVE:
|
|
gubFact[usFact] = ( gMercProfiles[ DYNAMO ].bMercStatus != MERC_IS_DEAD );
|
|
break;
|
|
case FACT_DYNAMO_SPEAKING_OR_NEARBY:
|
|
gubFact[usFact] = ( gpSrcSoldier != NULL && (gpSrcSoldier->ubProfile == DYNAMO || ( CheckNPCWithin( gpSrcSoldier->ubProfile, DYNAMO, 10 ) && CheckGuyVisible( gpSrcSoldier->ubProfile, DYNAMO ) ) ) );
|
|
break;
|
|
case FACT_JOHN_EPC:
|
|
gubFact[usFact] = CheckNPCIsEPC( JOHN );
|
|
break;
|
|
case FACT_MARY_EPC:
|
|
gubFact[usFact] = CheckNPCIsEPC( MARY );
|
|
break;
|
|
case FACT_JOHN_AND_MARY_EPCS:
|
|
gubFact[usFact] = CheckNPCIsEPC( JOHN ) && CheckNPCIsEPC( MARY );
|
|
break;
|
|
case FACT_MARY_ALIVE:
|
|
gubFact[usFact] = ( gMercProfiles[ MARY ].bMercStatus != MERC_IS_DEAD );
|
|
break;
|
|
case FACT_MARY_BLEEDING:
|
|
gubFact[usFact] = CheckNPCBleeding( MARY );
|
|
break;
|
|
case FACT_JOHN_ALIVE:
|
|
gubFact[usFact] = ( gMercProfiles[ JOHN ].bMercStatus != MERC_IS_DEAD );
|
|
break;
|
|
case FACT_JOHN_BLEEDING:
|
|
gubFact[usFact] = CheckNPCBleeding( JOHN );
|
|
break;
|
|
case FACT_MARY_DEAD:
|
|
gubFact[usFact] = ( gMercProfiles[ MARY ].bMercStatus == MERC_IS_DEAD );
|
|
break;
|
|
|
|
case FACT_ANOTHER_FIGHT_POSSIBLE:
|
|
gubFact[usFact] = AnotherFightPossible();
|
|
break;
|
|
|
|
case FACT_RECEIVING_INCOME_FROM_DCAC:
|
|
gubFact[usFact] = (
|
|
( PredictDailyIncomeFromAMine( MINE_DRASSEN ) > 0 ) &&
|
|
( PredictDailyIncomeFromAMine( MINE_ALMA ) > 0 ) &&
|
|
( PredictDailyIncomeFromAMine( MINE_CAMBRIA ) > 0 ) &&
|
|
( PredictDailyIncomeFromAMine( MINE_CHITZENA ) > 0 ) );
|
|
break;
|
|
|
|
case FACT_PLAYER_BEEN_TO_K4:
|
|
{
|
|
UNDERGROUND_SECTORINFO * pUnderGroundSector;
|
|
|
|
pUnderGroundSector = FindUnderGroundSector( 4, MAP_ROW_K, 1 );
|
|
gubFact[usFact] = pUnderGroundSector->fVisited;
|
|
}
|
|
break;
|
|
case FACT_WARDEN_DEAD:
|
|
gubFact[usFact] = ( gMercProfiles[ WARDEN ].bMercStatus == MERC_IS_DEAD );
|
|
break;
|
|
|
|
case FACT_PLAYER_PAID_FOR_TWO_IN_BROTHEL:
|
|
gubFact[usFact] = (gMercProfiles[ MADAME ].bNPCData > 1);
|
|
break;
|
|
|
|
case FACT_LOYALTY_OKAY:
|
|
bTown = gMercProfiles[ ubProfileID ].bTown;
|
|
if( ( bTown != BLANK_SECTOR ) && gTownLoyalty[ bTown ].fStarted && gfTownUsesLoyalty[ bTown ])
|
|
{
|
|
gubFact[usFact] = ( (gTownLoyalty[ bTown ].ubRating >= LOYALTY_LOW_THRESHOLD ) && (gTownLoyalty[ bTown ].ubRating < LOYALTY_OK_THRESHOLD ) );
|
|
}
|
|
else
|
|
{
|
|
gubFact[usFact] = FALSE;
|
|
}
|
|
break;
|
|
|
|
case FACT_LOYALTY_LOW:
|
|
bTown = gMercProfiles[ ubProfileID ].bTown;
|
|
if( ( bTown != BLANK_SECTOR ) && gTownLoyalty[ bTown ].fStarted && gfTownUsesLoyalty[ bTown ])
|
|
{
|
|
// 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
|
|
if ( ( ubProfileID == SKYRIDER ) &&
|
|
( ( guiHelicopterSkyriderTalkState == 0 ) || ( ( GetWorldTotalMin() - guiTimeOfLastSkyriderMonologue ) < ( 24 * 60 ) ) ) )
|
|
{
|
|
gubFact[usFact] = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gubFact[usFact] = (gTownLoyalty[ bTown ].ubRating < LOYALTY_LOW_THRESHOLD );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gubFact[usFact] = FALSE;
|
|
}
|
|
break;
|
|
|
|
case FACT_LOYALTY_HIGH:
|
|
bTown = gMercProfiles[ ubProfileID ].bTown;
|
|
if( ( bTown != BLANK_SECTOR ) && gTownLoyalty[ bTown ].fStarted && gfTownUsesLoyalty[ bTown ])
|
|
{
|
|
gubFact[usFact] = (gTownLoyalty[ gMercProfiles[ ubProfileID ].bTown ].ubRating >= LOYALTY_HIGH_THRESHOLD );
|
|
}
|
|
else
|
|
{
|
|
gubFact[usFact] = FALSE;
|
|
}
|
|
break;
|
|
|
|
case FACT_ELGIN_ALIVE:
|
|
gubFact[usFact] = ( gMercProfiles[ DRUGGIST ].bMercStatus != MERC_IS_DEAD );
|
|
break;
|
|
|
|
case FACT_SPEAKER_AIM_OR_AIM_NEARBY:
|
|
gubFact[usFact] = gpDestSoldier && AIMMercWithin( gpDestSoldier->sGridNo, 10 );
|
|
break;
|
|
|
|
case FACT_MALE_SPEAKING_FEMALE_PRESENT:
|
|
gubFact[usFact] = ( !CheckTalkerFemale() && FemalePresent( ubProfileID ) );
|
|
break;
|
|
|
|
case FACT_PLAYER_OWNS_2_TOWNS_INCLUDING_OMERTA:
|
|
gubFact[usFact] = ( ( GetNumberOfWholeTownsUnderControl() == 3 ) && IsTownUnderCompleteControlByPlayer( OMERTA ) );
|
|
break;
|
|
|
|
case FACT_PLAYER_OWNS_3_TOWNS_INCLUDING_OMERTA:
|
|
gubFact[usFact] = ( ( GetNumberOfWholeTownsUnderControl() == 5 ) && IsTownUnderCompleteControlByPlayer( OMERTA ) );
|
|
break;
|
|
|
|
case FACT_PLAYER_OWNS_4_TOWNS_INCLUDING_OMERTA:
|
|
gubFact[usFact] = ( ( GetNumberOfWholeTownsUnderControl() >= 6 ) && IsTownUnderCompleteControlByPlayer( OMERTA ) );
|
|
break;
|
|
|
|
case FACT_PLAYER_FOUGHT_THREE_TIMES_TODAY:
|
|
gubFact[usFact] = !BoxerAvailable();
|
|
break;
|
|
|
|
case FACT_PLAYER_DOING_POORLY:
|
|
gubFact[usFact] = ( CurrentPlayerProgressPercentage() < 20 );
|
|
break;
|
|
|
|
case FACT_PLAYER_DOING_WELL:
|
|
gubFact[usFact] = ( CurrentPlayerProgressPercentage() > 50 );
|
|
break;
|
|
|
|
case FACT_PLAYER_DOING_VERY_WELL:
|
|
gubFact[usFact] = ( CurrentPlayerProgressPercentage() > 80 );
|
|
break;
|
|
|
|
case FACT_FATHER_DRUNK_AND_SCIFI_OPTION_ON:
|
|
gubFact[usFact] = ( ( gMercProfiles[ FATHER ].bNPCData >= 5 ) && gGameOptions.ubGameStyle == STYLE_SCIFI && gGameExternalOptions.fEnableCrepitus);
|
|
break;
|
|
|
|
case FACT_BLOODCAT_QUEST_STARTED_TWO_DAYS_AGO:
|
|
gubFact[usFact] = ( (gubQuest[ QUEST_BLOODCATS ] != QUESTNOTSTARTED) && (GetWorldTotalMin() - GetTimeQuestWasStarted( QUEST_BLOODCATS ) > 2 * NUM_SEC_IN_DAY / NUM_SEC_IN_MIN) );
|
|
break;
|
|
|
|
case FACT_NOTHING_REPAIRED_YET:
|
|
gubFact[usFact] = RepairmanIsFixingItemsButNoneAreDoneYet( ubProfileID );
|
|
break;
|
|
|
|
case FACT_NPC_COWERING:
|
|
gubFact[usFact] = CheckNPCCowering( ubProfileID );
|
|
break;
|
|
|
|
case FACT_TOP_AND_BOTTOM_LEVELS_CLEARED:
|
|
gubFact[usFact] = ( gubFact[ FACT_TOP_LEVEL_CLEARED ] && gubFact[ FACT_BOTTOM_LEVEL_CLEARED ] );
|
|
break;
|
|
|
|
case FACT_FIRST_BARTENDER:
|
|
gubFact[ usFact ] = ( gMercProfiles[ubProfileID].bNPCData == 1 || (gMercProfiles[ubProfileID].bNPCData == 0 && CountBartenders() == 0) );
|
|
break;
|
|
|
|
case FACT_SECOND_BARTENDER:
|
|
gubFact[ usFact ] = ( gMercProfiles[ubProfileID].bNPCData == 2 || (gMercProfiles[ubProfileID].bNPCData == 0 && CountBartenders() == 1) );
|
|
break;
|
|
|
|
case FACT_THIRD_BARTENDER:
|
|
gubFact[ usFact ] = ( gMercProfiles[ubProfileID].bNPCData == 3 || (gMercProfiles[ubProfileID].bNPCData == 0 && CountBartenders() == 2) );
|
|
break;
|
|
|
|
case FACT_FOURTH_BARTENDER:
|
|
gubFact[ usFact ] = ( gMercProfiles[ubProfileID].bNPCData == 4 || (gMercProfiles[ubProfileID].bNPCData == 0 && CountBartenders() == 3) );
|
|
break;
|
|
|
|
case FACT_NPC_NOT_UNDER_FIRE:
|
|
gubFact[usFact] = !CheckNPCIsUnderFire( ubProfileID );
|
|
break;
|
|
|
|
case FACT_KINGPIN_NOT_IN_OFFICE:
|
|
gubFact[usFact] = !( gWorldSectorX == 5 && gWorldSectorY == MAP_ROW_D && NPCInRoomRange( KINGPIN, 30, 39 ) );
|
|
// 30 to 39
|
|
break;
|
|
|
|
case FACT_DONT_OWE_KINGPIN_MONEY:
|
|
gubFact[usFact] = (gubQuest[ QUEST_KINGPIN_MONEY ] != QUESTINPROGRESS);
|
|
break;
|
|
|
|
case FACT_NO_CLUB_FIGHTING_ALLOWED:
|
|
gubFact[usFact] = ( gubQuest[ QUEST_KINGPIN_MONEY ] == QUESTINPROGRESS || gfBoxersResting );// plus other conditions
|
|
break;
|
|
|
|
case FACT_MADDOG_IS_SPEAKER:
|
|
gubFact[usFact] = ( gubSrcSoldierProfile == MADDOG );
|
|
break;
|
|
|
|
case FACT_PC_HAS_CONRADS_RECRUIT_OPINION:
|
|
gubFact[usFact] = ( gpDestSoldier && (CalcDesireToTalk( gpDestSoldier->ubProfile, gubSrcSoldierProfile, APPROACH_RECRUIT ) >= 50) );
|
|
break;
|
|
|
|
case FACT_NPC_HOSTILE_OR_PISSED_OFF:
|
|
gubFact[usFact] = CheckNPCIsEnemy( ubProfileID ) || (gMercProfiles[ ubProfileID ].ubMiscFlags3 & PROFILE_MISC_FLAG3_NPC_PISSED_OFF);
|
|
break;
|
|
|
|
case FACT_TONY_IN_BUILDING:
|
|
gubFact[usFact] = CheckNPCSector( TONY, 5, MAP_ROW_C, 0 ) && NPCInRoom( TONY, 50 );
|
|
break;
|
|
|
|
case FACT_SHANK_SPEAKING:
|
|
gubFact[usFact] = ( gpSrcSoldier && gpSrcSoldier->ubProfile == SHANK );
|
|
break;
|
|
|
|
case FACT_ROCKET_RIFLE_EXISTS:
|
|
gubFact[usFact] = ItemTypeExistsAtLocation( 10472, ROCKET_RIFLE, 0, NULL );
|
|
break;
|
|
|
|
case FACT_DOREEN_ALIVE:
|
|
gubFact[usFact] = gMercProfiles[ DOREEN ].bMercStatus != MERC_IS_DEAD;
|
|
break;
|
|
|
|
case FACT_WALDO_ALIVE:
|
|
gubFact[usFact] = gMercProfiles[ WALDO ].bMercStatus != MERC_IS_DEAD;
|
|
break;
|
|
|
|
case FACT_PERKO_ALIVE:
|
|
gubFact[usFact] = gMercProfiles[ PERKO ].bMercStatus != MERC_IS_DEAD;
|
|
break;
|
|
|
|
case FACT_TONY_ALIVE:
|
|
gubFact[usFact] = gMercProfiles[ TONY ].bMercStatus != MERC_IS_DEAD;
|
|
break;
|
|
|
|
case FACT_VINCE_ALIVE:
|
|
gubFact[usFact] = gMercProfiles[ VINCE ].bMercStatus != MERC_IS_DEAD;
|
|
break;
|
|
|
|
case FACT_JENNY_ALIVE:
|
|
gubFact[usFact] = gMercProfiles[ JENNY ].bMercStatus != MERC_IS_DEAD;
|
|
break;
|
|
|
|
case FACT_ARNOLD_ALIVE:
|
|
gubFact[usFact] = gMercProfiles[ ARNIE ].bMercStatus != MERC_IS_DEAD;
|
|
break;
|
|
|
|
case FACT_I16_BLOODCATS_KILLED:
|
|
gubFact[usFact] = (SectorInfo[ SEC_I16 ].bBloodCats == 0);
|
|
break;
|
|
|
|
case FACT_NPC_BANDAGED_TODAY:
|
|
gubFact[usFact] = (gMercProfiles[ ubProfileID ].ubMiscFlags2 & PROFILE_MISC_FLAG2_BANDAGED_TODAY) != 0;
|
|
break;
|
|
|
|
case FACT_PLAYER_IN_SAME_ROOM:
|
|
gubFact[usFact] = PCInSameRoom( ubProfileID );
|
|
break;
|
|
|
|
case FACT_PLAYER_SPOKE_TO_DRASSEN_MINER:
|
|
gubFact[usFact] = SpokenToHeadMiner( MINE_DRASSEN );
|
|
break;
|
|
case FACT_PLAYER_IN_CONTROLLED_DRASSEN_MINE:
|
|
gubFact[usFact] = ( GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == MINE_DRASSEN && !(StrategicMap[ gWorldSectorX + MAP_WORLD_X * gWorldSectorY ].fEnemyControlled) );
|
|
break;
|
|
case FACT_PLAYER_SPOKE_TO_CAMBRIA_MINER:
|
|
gubFact[usFact] = SpokenToHeadMiner( MINE_CAMBRIA );
|
|
break;
|
|
case FACT_PLAYER_IN_CONTROLLED_CAMBRIA_MINE:
|
|
gubFact[usFact] = ( GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == MINE_CAMBRIA && !(StrategicMap[ gWorldSectorX + MAP_WORLD_X * gWorldSectorY ].fEnemyControlled) );
|
|
break;
|
|
case FACT_PLAYER_SPOKE_TO_CHITZENA_MINER:
|
|
gubFact[usFact] = SpokenToHeadMiner( MINE_CHITZENA );
|
|
break;
|
|
case FACT_PLAYER_IN_CONTROLLED_CHITZENA_MINE:
|
|
gubFact[usFact] = ( GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == MINE_CHITZENA && !(StrategicMap[ gWorldSectorX + MAP_WORLD_X * gWorldSectorY ].fEnemyControlled) );
|
|
break;
|
|
case FACT_PLAYER_SPOKE_TO_ALMA_MINER:
|
|
gubFact[usFact] = SpokenToHeadMiner( MINE_ALMA );
|
|
break;
|
|
case FACT_PLAYER_IN_CONTROLLED_ALMA_MINE:
|
|
gubFact[usFact] = ( GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == MINE_ALMA && !(StrategicMap[ gWorldSectorX + MAP_WORLD_X * gWorldSectorY ].fEnemyControlled) );
|
|
break;
|
|
case FACT_PLAYER_SPOKE_TO_GRUMM_MINER:
|
|
gubFact[usFact] = SpokenToHeadMiner( MINE_GRUMM );
|
|
break;
|
|
case FACT_PLAYER_IN_CONTROLLED_GRUMM_MINE:
|
|
gubFact[usFact] = ( GetIdOfMineForSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ ) == MINE_GRUMM && !(StrategicMap[ gWorldSectorX + MAP_WORLD_X * gWorldSectorY ].fEnemyControlled) );
|
|
break;
|
|
|
|
case FACT_ENOUGH_LOYALTY_TO_TRAIN_MILITIA:
|
|
gubFact[usFact] = InTownSectorWithTrainingLoyalty( gWorldSectorX, gWorldSectorY );
|
|
break;
|
|
|
|
case FACT_WALKER_AT_BAR:
|
|
gubFact[usFact] = (gMercProfiles[ FATHER ].sSectorX == 13 && gMercProfiles[ FATHER ].sSectorY == MAP_ROW_C);
|
|
break;
|
|
|
|
case FACT_JOEY_ALIVE:
|
|
gubFact[usFact] = gMercProfiles[ JOEY ].bMercStatus != MERC_IS_DEAD;
|
|
break;
|
|
|
|
case FACT_UNPROPOSITIONED_FEMALE_SPEAKING_TO_NPC:
|
|
gubFact[usFact] = CheckTalkerUnpropositionedFemale();
|
|
break;
|
|
|
|
case FACT_84_AND_85_TRUE:
|
|
gubFact[usFact] = CheckFact( 84, ubProfileID ) && CheckFact( FACT_HANS_AT_SPOT, ubProfileID );
|
|
break;
|
|
|
|
case FACT_SKYRIDER_IN_B15:
|
|
gubFact[ usFact ] = CheckNPCSector( SKYRIDER, 15, MAP_ROW_B, 0 );
|
|
break;
|
|
|
|
case FACT_SKYRIDER_IN_C16:
|
|
gubFact[ usFact ] = CheckNPCSector( SKYRIDER, 16, MAP_ROW_C, 0 );
|
|
break;
|
|
case FACT_SKYRIDER_IN_E14:
|
|
gubFact[ usFact ] = CheckNPCSector( SKYRIDER, 14, MAP_ROW_E, 0 );
|
|
break;
|
|
case FACT_SKYRIDER_IN_D12:
|
|
gubFact[ usFact ] = CheckNPCSector( SKYRIDER, 12, MAP_ROW_D, 0 );
|
|
break;
|
|
|
|
case FACT_KINGPIN_IS_ENEMY:
|
|
gubFact[usFact] = (gTacticalStatus.fCivGroupHostile[ KINGPIN_CIV_GROUP ] >= CIV_GROUP_WILL_BECOME_HOSTILE);
|
|
break;
|
|
|
|
case FACT_DYNAMO_NOT_SPEAKER:
|
|
gubFact[usFact] = !( gpSrcSoldier != NULL && (gpSrcSoldier->ubProfile == DYNAMO ) );
|
|
break;
|
|
|
|
case FACT_PABLO_BRIBED:
|
|
gubFact[usFact] = !CheckFact( FACT_PABLOS_BRIBED, ubProfileID );
|
|
break;
|
|
|
|
case FACT_VEHICLE_PRESENT:
|
|
gubFact[usFact] = CheckFact( FACT_OK_USE_HUMMER, ubProfileID ) && ( (FindSoldierByProfileID( PROF_HUMMER, TRUE ) != NULL) || (FindSoldierByProfileID( PROF_ICECREAM, TRUE ) != NULL) );
|
|
break;
|
|
|
|
case FACT_PLAYER_KILLED_BOXERS:
|
|
gubFact[usFact] = !BoxerExists();
|
|
break;
|
|
|
|
case 245: // Can dimitri be recruited? should be true if already true, OR if Miguel has been recruited already
|
|
gubFact[usFact] = ( gubFact[usFact] || FindSoldierByProfileID( MIGUEL, TRUE ) );
|
|
/*
|
|
case FACT_:
|
|
gubFact[usFact] = ;
|
|
break;
|
|
*/
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return( gubFact[usFact] );
|
|
}
|
|
|
|
void StartQuest( UINT8 ubQuest, INT16 sSectorX, INT16 sSectorY )
|
|
{
|
|
InternalStartQuest( ubQuest, sSectorX, sSectorY, TRUE );
|
|
}
|
|
|
|
|
|
void InternalStartQuest( UINT8 ubQuest, INT16 sSectorX, INT16 sSectorY, BOOLEAN fUpdateHistory )
|
|
{
|
|
if ( gubQuest[ubQuest ] == QUESTNOTSTARTED )
|
|
{
|
|
gubQuest[ubQuest] = QUESTINPROGRESS;
|
|
|
|
if ( fUpdateHistory )
|
|
{
|
|
SetHistoryFact( HISTORY_QUEST_STARTED, ubQuest, GetWorldTotalMin(), sSectorX, sSectorY );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gubQuest[ubQuest] = QUESTINPROGRESS;
|
|
}
|
|
}
|
|
|
|
void EndQuest( UINT8 ubQuest, INT16 sSectorX, INT16 sSectorY )
|
|
{
|
|
InternalEndQuest( ubQuest, sSectorX, sSectorY, TRUE );
|
|
}
|
|
|
|
void InternalEndQuest( UINT8 ubQuest, INT16 sSectorX, INT16 sSectorY, BOOLEAN fUpdateHistory )
|
|
{
|
|
if ( gubQuest[ubQuest ] == QUESTINPROGRESS )
|
|
{
|
|
gubQuest[ubQuest] = QUESTDONE;
|
|
|
|
if ( fUpdateHistory )
|
|
{
|
|
ResetHistoryFact( ubQuest, sSectorX, sSectorY );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
gubQuest[ubQuest] = QUESTDONE;
|
|
}
|
|
|
|
if ( ubQuest == QUEST_RESCUE_MARIA )
|
|
{
|
|
// cheap hack to try to prevent Madame Layla from thinking that you are
|
|
// still in the brothel with Maria...
|
|
gMercProfiles[ MADAME ].bNPCData = 0;
|
|
gMercProfiles[ MADAME ].bNPCData2 = 0;
|
|
}
|
|
|
|
};
|
|
|
|
void InitQuestEngine()
|
|
{
|
|
memset(gubQuest, 0, sizeof(gubQuest));
|
|
memset(gubFact, 0, sizeof(gubFact));
|
|
|
|
// semi-hack to make the letter quest start right away
|
|
CheckForQuests( 1 );
|
|
|
|
if ( gGameOptions.ubGameStyle == STYLE_SCIFI )
|
|
{
|
|
// 3 medical boosters
|
|
gubCambriaMedicalObjects = 21;
|
|
}
|
|
else
|
|
{
|
|
gubCambriaMedicalObjects = 18;
|
|
}
|
|
|
|
gubBoxingMatchesWon = 0;
|
|
gubBoxersRests = 0;
|
|
gfBoxersResting = FALSE;
|
|
}
|
|
|
|
|
|
|
|
void CheckForQuests( UINT32 uiDay )
|
|
{
|
|
// This function gets called at 8:00 AM time of the day
|
|
|
|
#ifdef TESTQUESTS
|
|
ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"Checking For Quests, Day %d", uiDay );
|
|
#endif
|
|
|
|
// -------------------------------------------------------------------------------
|
|
// QUEST 0 : DELIVER LETTER
|
|
// -------------------------------------------------------------------------------
|
|
// The game always starts with DELIVER LETTER quest, so turn it on if it hasn't
|
|
// already started
|
|
if (gubQuest[QUEST_DELIVER_LETTER] == QUESTNOTSTARTED)
|
|
{
|
|
StartQuest( QUEST_DELIVER_LETTER, -1, -1 );
|
|
#ifdef TESTQUESTS
|
|
ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"Started DELIVER LETTER quest");
|
|
#endif
|
|
}
|
|
|
|
// This quest gets turned OFF through conversation with Miguel - when user hands
|
|
// Miguel the letter
|
|
}
|
|
|
|
|
|
BOOLEAN SaveQuestInfoToSavedGameFile( HWFILE hFile )
|
|
{
|
|
UINT32 uiNumBytesWritten;
|
|
|
|
//Save all the states if the Quests
|
|
FileWrite( hFile, gubQuest, MAX_QUESTS, &uiNumBytesWritten );
|
|
if( uiNumBytesWritten != MAX_QUESTS )
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
//Save all the states for the facts
|
|
FileWrite( hFile, gubFact, NUM_FACTS, &uiNumBytesWritten );
|
|
if( uiNumBytesWritten != NUM_FACTS )
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
BOOLEAN LoadQuestInfoFromSavedGameFile( HWFILE hFile )
|
|
{
|
|
UINT32 uiNumBytesRead;
|
|
|
|
//Save all the states if the Quests
|
|
FileRead( hFile, gubQuest, MAX_QUESTS, &uiNumBytesRead );
|
|
if( uiNumBytesRead != MAX_QUESTS )
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
//Save all the states for the facts
|
|
FileRead( hFile, gubFact, NUM_FACTS, &uiNumBytesRead );
|
|
if( uiNumBytesRead != NUM_FACTS )
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|