Files
source/TacticalAI/NPC.cpp
T
Wanne b150bd172c - Temporary disabled the Encyclopedia, because it seems it takes up a huge amount of virtual/committed memory (~ 2 GB) (by tazpn)
o A define ENABLE_ENCYCLOPEDIA defines, if the encyclopedia code should be compiled
o What is happening here is encyclopedia has a lot of really big structures (ENCYCLOPEDIA_LOCATION) and then a lot of arrays of size NUM_SECTOR, NUM_PROFILES, NUMITEMS which are fairly sizable themselves.
o What the problem with doing this is when you need to allocate memory it typically needs to go to virtual memory to get large blocks of memory (malloc and related routines all do this) but you only have 2GB worth of virtual space as a pointer is 32 bits and user mode gets half of that 00000000-7FFFFFFF for addressing. This statically allocated space is taking up 7/8ths of the virtual address space. When animations or maps need to be loaded there might not be enough virtual space left due to fragmentation.
o To see the amount of committed memory, open TaskManager and add the "Committed Memory" column to the view

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5212 3b4a5df2-a311-0410-b5c6-a8a6f20db521
2012-04-15 23:45:41 +00:00

3852 lines
120 KiB
C++

#ifdef PRECOMPILEDHEADERS
#include "AI All.h"
#include "interface items.h"
#else
#include "types.h"
#include "wcheck.h"
#include "overhead.h"
#include "ai.h"
#include "Soldier Profile.h"
#include "NPC.h"
#include "Isometric Utils.h"
#include "Quests.h"
#include "interface dialogue.h"
#include "Game Clock.h"
#include "FileMan.h"
#include "Random.h"
#include "items.h"
#include "interface.h"
#include "Assignments.h"
#include "QuestDebug.h"
#include "soldier macros.h"
#include "dialogue control.h"
#include "Strategic Town Loyalty.h"
#include "message.h"
#include "Timer Control.h"
#include "WorldMan.h"
#include "Soldier Add.h"
#include "Soldier Tile.h"
#include "Weapons.h"
#include "meanwhile.h"
#ifdef JA2TESTVERSION
#include "Quest Debug System.h"
#include "QuestText.h"
#endif
#include "SkillCheck.h"
#include "Render Fun.h"
#include "Text.h"
#include "finances.h"
#include "Arms Dealer Init.h"
#include "Interface Items.h"
#include "opplist.h"
#include "Animation Control.h"
#include "Scheduling.h"
#include "Tactical Save.h"
#include "Campaign Types.h"
#include "GameSettings.h" // added by SANDRO
#include "Soldier Profile.h"
#include "GameVersion.h"
#endif
#include "Soldier Profile.h"
#include "Encyclopedia.h"
//forward declarations of common classes to eliminate includes
class OBJECTTYPE;
class SOLDIERTYPE;
// WANNE - BMP: DONE!
//SB: new .npc format
#define _MAKEFOURCC(ch0, ch1, ch2, ch3) \
((UINT32)(UINT8)(ch0) | ((UINT32)(UINT8)(ch1) << 8) | \
((UINT32)(UINT8)(ch2) << 16) | ((UINT32)(UINT8)(ch3) << 24 ))
#define NPCEX_SIGNATURE _MAKEFOURCC('B','1','1','3')
#define NUM_CIVQUOTE_SECTORS 20
#define MINERS_CIV_QUOTE_INDEX 16
INT16 gsCivQuoteSector[NUM_CIVQUOTE_SECTORS][2] =
{
2, MAP_ROW_A,
2, MAP_ROW_B,
13, MAP_ROW_B,
13, MAP_ROW_C,
13, MAP_ROW_D,
8, MAP_ROW_F,
9, MAP_ROW_F,
8, MAP_ROW_G,
9, MAP_ROW_G,
1, MAP_ROW_H,
2, MAP_ROW_H,
3, MAP_ROW_H,
8, MAP_ROW_H,
13, MAP_ROW_H,
14, MAP_ROW_I,
11, MAP_ROW_L,
12, MAP_ROW_L,
0, 0, // THIS ONE USED NOW - FOR bSectorZ > 0.....
0, 0,
0, 0,
};
#define NO_FACT (MAX_FACTS - 1)
#define NO_QUEST 255
#define QUEST_NOT_STARTED_NUM 100
#define QUEST_DONE_NUM 200
#define NO_QUOTE 255
#define IRRELEVANT 255
#define NO_MOVE NOWHERE//dnl ch46 031009
NPCQuoteInfo * gpNPCQuoteInfoArray[NUM_PROFILES] = { NULL };
NPCQuoteInfo * gpBackupNPCQuoteInfoArray[NUM_PROFILES] = { NULL };
NPCQuoteInfo * gpCivQuoteInfoArray[NUM_CIVQUOTE_SECTORS] = { NULL };
#ifdef JA2TESTVERSION
// Warning: cheap hack approaching
BOOLEAN gfTriedToLoadQuoteInfoArray[NUM_PROFILES] = { FALSE };
#endif
UINT8 gubTeamPenalty;
BOOLEAN EnsureQuoteFileLoaded( UINT8 ubNPC );
NPCQuoteInfo * LoadQuoteFile( UINT8 ubNPC );
UINT8 NPCConsiderQuote( UINT8 ubNPC, UINT8 ubMerc, UINT8 ubApproach, UINT8 ubQuoteNum, UINT8 ubTalkDesire, NPCQuoteInfo * pNPCQuoteInfoArray );
UINT8 NPCConsiderReceivingItemFromMerc( UINT8 ubNPC, UINT8 ubMerc, OBJECTTYPE * pObj, NPCQuoteInfo * pNPCQuoteInfoArray, NPCQuoteInfo ** ppResultQuoteInfo, UINT8 *pubQuoteNum );
void PCsNearNPC( UINT8 ubNPC );
void TriggerClosestMercWhoCanSeeNPC( UINT8 ubNPC, NPCQuoteInfo *pQuotePtr );
BOOLEAN NPCHasUnusedRecordWithGivenApproach( UINT8 ubNPC, UINT8 ubApproach );
INT8 gbFirstApproachFlags[4] = { 0x01, 0x02, 0x04, 0x08 };
UINT8 gubAlternateNPCFileNumsForQueenMeanwhiles[] = { 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176 };
UINT8 gubAlternateNPCFileNumsForElliotMeanwhiles[] = { 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196 };
#ifdef JA2BETAVERSION
BOOLEAN gfDisplayScreenMsgOnRecordUsage = FALSE;
#endif
extern void PauseAITemporarily( void );
extern void PayOffSkyriderDebtIfAny( );
//
// NPC QUOTE LOW LEVEL ROUTINES
//
// WANNE - BMP: DONE!
NPCQuoteInfo * LoadQuoteFile( UINT8 ubNPC )
{
CHAR8 zFileName[255];
HWFILE hFile;
NPCQuoteInfo * pFileData;
UINT32 uiBytesRead;
UINT32 uiFileSize;
DWORD uiSignature = 0; // SB // WANNE - BMP: DONE!
if ( ubNPC == PETER || ubNPC == ALBERTO || ubNPC == CARLO )
{
// use a copy of Herve's data file instead!
sprintf( zFileName, "NPCData\\%03d.npc", HERVE );
}
//else if ( ubNPC < FIRST_RPC || ubNPC >= GASTON || (ubNPC < FIRST_NPC && gMercProfiles[ ubNPC ].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED ) )
//new profiles by Jazz
else if ( gProfilesIMP[ubNPC].ProfilId == ubNPC || gProfilesAIM[ubNPC].ProfilId == ubNPC || gProfilesMERC[ubNPC].ProfilId == ubNPC || ( gProfilesRPC[ubNPC].ProfilId == ubNPC && gMercProfiles[ ubNPC ].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED ) )
{
sprintf( zFileName, "NPCData\\000.npc", ubNPC );
}
else
{
sprintf( zFileName, "NPCData\\%03d.npc", ubNPC );
}
#ifdef JA2UB
//Ja25: No meanwhiles
#else
// ATE: Put some stuff i here to use a different NPC file if we are in a meanwhile.....
if ( AreInMeanwhile( ) )
{
// If we are the queen....
if ( ubNPC == QUEEN )
{
sprintf( zFileName, "NPCData\\%03d.npc", gubAlternateNPCFileNumsForQueenMeanwhiles[ GetMeanwhileID( ) ] );
}
// If we are elliot....
if ( ubNPC == ELLIOT )
{
sprintf( zFileName, "NPCData\\%03d.npc", gubAlternateNPCFileNumsForElliotMeanwhiles[ GetMeanwhileID( ) ] );
}
}
#endif
CHECKN( FileExists( zFileName ) );
hFile = FileOpen( zFileName, FILE_ACCESS_READ, FALSE );
CHECKN( hFile );
// WANNE - BMP: DONE!
//<SB> new script format
if (!FileRead( hFile, &uiSignature, sizeof(uiSignature), &uiBytesRead ) || uiBytesRead != sizeof(uiSignature) )
return NULL;
if(uiSignature == NPCEX_SIGNATURE)//new fmt
{
uiFileSize = sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS;
pFileData = (NPCQuoteInfo *)MemAlloc( uiFileSize );
if (pFileData)
{
if (!FileRead( hFile, pFileData, uiFileSize, &uiBytesRead ) || uiBytesRead != uiFileSize )
{
MemFree( pFileData );
pFileData = NULL;
}
}
FileClose( hFile );
}
else //old fmt - make conversion
{
int iRecord;
_old_NPCQuoteInfo * pFileData_old_;
uiFileSize = sizeof( _old_NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS;
pFileData_old_ = ( _old_NPCQuoteInfo * )MemAlloc( uiFileSize );
FileSeek(hFile, 0, FILE_SEEK_FROM_START);
if (pFileData_old_)
{
if (!FileRead( hFile, pFileData_old_, uiFileSize, &uiBytesRead ) || uiBytesRead != uiFileSize )
{
MemFree( pFileData_old_ );
pFileData_old_ = NULL;
}
}
FileClose( hFile );
//check for Russian script & make a runtime conversion of it to International
if( *(DWORD*)pFileData_old_ == 0x00350039 )
{
//just offset records 4 bytes backward
_old_NPCQuoteInfo * pEnglishScript = ( _old_NPCQuoteInfo * )MemAlloc( uiFileSize );
memcpy( pEnglishScript, ((char*)pFileData_old_)+4, uiFileSize-4 );
MemFree( pFileData_old_ );
pFileData_old_ = pEnglishScript;
}
// Now it's time for conversion
uiFileSize = sizeof(NPCQuoteInfo) * NUM_NPC_QUOTE_RECORDS;
pFileData = (NPCQuoteInfo*)MemAlloc(uiFileSize);
for(iRecord=0; iRecord<NUM_NPC_QUOTE_RECORDS; iRecord++)
pFileData[iRecord] = pFileData_old_[iRecord];//dnl ch46 021009
MemFree(pFileData_old_);
}
//
//</SB> new script format
return( pFileData );
}
//dnl ch46 021009
NPCQuoteInfo& NPCQuoteInfo::operator=(const _old_NPCQuoteInfo& src)
{
if((void*)this != (void*)&src)
{
fFlags = src.fFlags;
if(src.sRequiredGridno < 0)
{
sRequiredItem = NOTHING;
sRequiredGridNo = -src.sRequiredGridno;
}
else
{
sRequiredItem = src.sRequiredItem;
sRequiredGridNo = NOWHERE;
}
usFactMustBeTrue = src.usFactMustBeTrue;
usFactMustBeFalse = src.usFactMustBeFalse;
ubQuest = src.ubQuest;
ubFirstDay = src.ubFirstDay;
ubLastDay = src.ubLastDay;
ubApproachRequired = src.ubApproachRequired;
ubOpinionRequired = src.ubOpinionRequired;
usUnused = 0;
ubQuoteNum = src.ubQuoteNum;
ubNumQuotes = src.ubNumQuotes;
ubStartQuest = src.ubStartQuest;
ubEndQuest = src.ubEndQuest;
ubTriggerNPC = src.ubTriggerNPC;
ubTriggerNPCRec = src.ubTriggerNPCRec;
usSetFactTrue = src.usSetFactTrue;
usGiftItem = src.usGiftItem;
sActionData = src.sActionData;
if(src.usGoToGridno >= OLD_WORLD_MAX)
usGoToGridNo = NOWHERE;
else
usGoToGridNo = src.usGoToGridno;
}
return(*this);
}
void RevertToOriginalQuoteFile( UINT8 ubNPC )
{
if ( gpBackupNPCQuoteInfoArray[ ubNPC ] && gpNPCQuoteInfoArray[ubNPC] )
{
MemFree( gpNPCQuoteInfoArray[ubNPC] );
gpNPCQuoteInfoArray[ubNPC] = gpBackupNPCQuoteInfoArray[ubNPC];
gpBackupNPCQuoteInfoArray[ubNPC] = NULL;
}
}
void BackupOriginalQuoteFile( UINT8 ubNPC )
{
gpBackupNPCQuoteInfoArray[ ubNPC ] = gpNPCQuoteInfoArray[ ubNPC ];
gpNPCQuoteInfoArray[ubNPC] = NULL;
}
extern UINT32 guiCurrentSaveGameVersion;
BOOLEAN EnsureQuoteFileLoaded( UINT8 ubNPC )
{
BOOLEAN fLoadFile = FALSE;
if ( ubNPC == ROBOT )
{
return( FALSE );
}
if(guiCurrentSaveGameVersion < FIXED_NPC_QUOTE_BUG)
{
if (gpNPCQuoteInfoArray[ubNPC] == NULL)
{
fLoadFile = TRUE;
}
else
{
// WANNE:Reload the NPC quote files if only ONE quote exists. This seems to be a bug anywhere in the quote system.
// This is a hack, but works :)
// This should fix the bug, that special quests are not triggerd when talking to NPCs!
if (gpNPCQuoteInfoArray[ubNPC][1].ubQuoteNum == 0 && gpNPCQuoteInfoArray[ubNPC][1].ubNumQuotes == 0)
{
fLoadFile = TRUE;
}
}
}
else
{
if (gpNPCQuoteInfoArray[ubNPC] == NULL)
{
fLoadFile = TRUE;
}
else if (gpNPCQuoteInfoArray[ubNPC][0].ubQuoteNum != 0 && gpNPCQuoteInfoArray[ubNPC][1].ubNumQuotes == 0)
{ // CHRISL: This will reset the quote file if, for some reason, the first quote exists but the 2nd doesn't
fLoadFile = TRUE;
}
}
// if ( ubNPC >= FIRST_RPC && ubNPC < FIRST_NPC )
//new profiles by Jazz
if ( ( gProfilesRPC[ubNPC].ProfilId == ubNPC ) )
{
if (gMercProfiles[ ubNPC ].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED)
{
// recruited
if ( gpBackupNPCQuoteInfoArray[ ubNPC ] == NULL )
{
// no backup stored of current script, so need to backup
fLoadFile = TRUE;
// set pointer to back up script!
BackupOriginalQuoteFile( ubNPC );
}
// else have backup, are recruited, nothing special
}
else
{
// not recruited
if ( gpBackupNPCQuoteInfoArray[ ubNPC ] != NULL )
{
// backup stored, restore backup
RevertToOriginalQuoteFile( ubNPC );
}
// else are no backup, nothing special
}
}
if ( fLoadFile )
{
gpNPCQuoteInfoArray[ubNPC] = LoadQuoteFile( ubNPC );
if (gpNPCQuoteInfoArray[ubNPC] == NULL)
{
#ifdef CRIPPLED_VERSION
// make sure we're not trying to load NOPROFILE for some stupid reason
if ( ubNPC != NO_PROFILE )
{
SOLDIERTYPE * pNull = NULL;
pNull->stats.bLife = 0; // crash!
}
#else
#ifdef JA2TESTVERSION
if (!gfTriedToLoadQuoteInfoArray[ubNPC]) // don't report the error a second time
{
ScreenMsg( MSG_FONT_RED, MSG_DEBUG, L"ERROR: NPC.C - NPC needs NPC file: %d.", ubNPC );
gfTriedToLoadQuoteInfoArray[ubNPC] = TRUE;
}
#endif
#endif
// error message at this point!
return( FALSE );
}
#ifdef CRIPPLED_VERSION
// check a random record to make sure the bytes are set right in the header
if ( gpNPCQuoteInfoArray[ubNPC][ 0 ].ubIdentifier[0] != '9')
{
if ( gpNPCQuoteInfoArray[ubNPC][ 0 ].ubIdentifier[2] != '5')
{
// crash!
SOLDIERTYPE * pNull = NULL;
pNull->stats.bLife = 0;
}
}
#endif
}
return( TRUE );
}
BOOLEAN ReloadQuoteFile( UINT8 ubNPC )
{
if (gpNPCQuoteInfoArray[ubNPC] != NULL)
{
MemFree( gpNPCQuoteInfoArray[ubNPC] );
gpNPCQuoteInfoArray[ubNPC] = NULL;
}
// zap backup if any
if ( gpBackupNPCQuoteInfoArray[ ubNPC ] != NULL )
{
MemFree( gpBackupNPCQuoteInfoArray[ ubNPC ] );
gpBackupNPCQuoteInfoArray[ ubNPC ] = NULL;
}
return( EnsureQuoteFileLoaded( ubNPC ) );
}
BOOLEAN ReloadQuoteFileIfLoaded( UINT8 ubNPC )
{
if (gpNPCQuoteInfoArray[ubNPC] != NULL)
{
MemFree( gpNPCQuoteInfoArray[ubNPC] );
gpNPCQuoteInfoArray[ubNPC] = NULL;
return( EnsureQuoteFileLoaded( ubNPC ) );
}
else
{
return( TRUE );
}
}
BOOLEAN RefreshNPCScriptRecord( UINT8 ubNPC, UINT8 ubRecord )
{
UINT8 ubLoop;
NPCQuoteInfo * pNewArray;
if ( ubNPC == NO_PROFILE )
{
// we have some work to do...
// loop through all PCs, and refresh their copy of this record
//for ( ubLoop = 0; ubLoop < FIRST_RPC; ubLoop++ ) // need more finesse here
//new profiles by Jazz
for ( ubLoop = 0; ubLoop < NUM_PROFILES; ubLoop++ ) // need more finesse here
{
//new profiles by Jazz
if ( gProfilesIMP[ubLoop].ProfilId == ubLoop || gProfilesAIM[ubLoop].ProfilId == ubLoop || gProfilesMERC[ubLoop].ProfilId == ubLoop )
RefreshNPCScriptRecord( ubLoop, ubRecord );
}
#ifdef JA2UB
//no UB
#else
//for ( ubLoop = GASTON; ubLoop < NUM_PROFILES; ubLoop++ ) // need more finesse here
//{
// RefreshNPCScriptRecord( ubLoop, ubRecord );
//}
#endif
//new profiles by Jazz
//for ( ubLoop = FIRST_RPC; ubLoop < FIRST_NPC; ubLoop++ )
for ( ubLoop = 0; ubLoop < NUM_PROFILES; ubLoop++ )
{
if ( gProfilesRPC[ubLoop].ProfilId == ubLoop )
{
if ( gMercProfiles[ ubNPC ].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED && gpBackupNPCQuoteInfoArray[ ubNPC ] != NULL )
{
RefreshNPCScriptRecord( ubLoop, ubRecord );
}
}
}
return( TRUE );
}
if ( gpNPCQuoteInfoArray[ ubNPC ] )
{
if ( CHECK_FLAG( gpNPCQuoteInfoArray[ ubNPC ][ ubRecord ].fFlags, QUOTE_FLAG_SAID ) )
{
// already used so we don't have to refresh!
return( TRUE );
}
pNewArray = LoadQuoteFile( ubNPC );
if ( pNewArray )
{
memcpy( &(gpNPCQuoteInfoArray[ ubNPC ][ ubRecord ]), &(pNewArray[ ubRecord ]), sizeof( NPCQuoteInfo ) );
MemFree( pNewArray );
}
}
return( TRUE );
}
//
// CIV QUOTE LOW LEVEL ROUTINES
//
NPCQuoteInfo * LoadCivQuoteFile( UINT8 ubIndex )
{
CHAR8 zFileName[255];
HWFILE hFile;
NPCQuoteInfo * pFileData;
UINT32 uiBytesRead;
UINT32 uiFileSize;
DWORD uiSignature = 0; // SB // WANNE - BMP: DONE!
if ( ubIndex == MINERS_CIV_QUOTE_INDEX )
{
sprintf( zFileName, "NPCData\\miners.npc" );
}
else
{
sprintf( zFileName, "NPCData\\%c%d.npc", 'A' + ( gsCivQuoteSector[ ubIndex ][ 1 ] - 1 ), gsCivQuoteSector[ ubIndex ][ 0 ] );
}
CHECKN( FileExists( zFileName ) );
hFile = FileOpen( zFileName, FILE_ACCESS_READ, FALSE );
CHECKN( hFile );
// -----------------------------------------------------------
// WANNE: This fixes the bug, that NPCs do not give hints
// Problem was, the missing conversion from old to new NPC structure!
if (!FileRead( hFile, &uiSignature, sizeof(uiSignature), &uiBytesRead ) || uiBytesRead != sizeof(uiSignature) )
return NULL;
if(uiSignature == NPCEX_SIGNATURE)//new fmt
{
uiFileSize = sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS;
pFileData = (NPCQuoteInfo *)MemAlloc( uiFileSize );
if (pFileData)
{
if (!FileRead( hFile, pFileData, uiFileSize, &uiBytesRead ) || uiBytesRead != uiFileSize )
{
MemFree( pFileData );
pFileData = NULL;
}
}
FileClose( hFile );
}
else //old fmt - make conversion
{
int iRecord;
_old_NPCQuoteInfo * pFileData_old_;
uiFileSize = sizeof( _old_NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS;
pFileData_old_ = ( _old_NPCQuoteInfo * )MemAlloc( uiFileSize );
FileSeek(hFile, 0, FILE_SEEK_FROM_START);
if (pFileData_old_)
{
if (!FileRead( hFile, pFileData_old_, uiFileSize, &uiBytesRead ) || uiBytesRead != uiFileSize )
{
MemFree( pFileData_old_ );
pFileData_old_ = NULL;
}
}
FileClose( hFile );
//check for Russian script & make a runtime conversion of it to International
if( *(DWORD*)pFileData_old_ == 0x00350039 )
{
//just offset records 4 bytes backward
_old_NPCQuoteInfo * pEnglishScript = ( _old_NPCQuoteInfo * )MemAlloc( uiFileSize );
memcpy( pEnglishScript, ((char*)pFileData_old_)+4, uiFileSize-4 );
MemFree( pFileData_old_ );
pFileData_old_ = pEnglishScript;
}
// Now it's time for conversion
uiFileSize = sizeof(NPCQuoteInfo) * NUM_NPC_QUOTE_RECORDS;
pFileData = (NPCQuoteInfo*)MemAlloc(uiFileSize);
for(iRecord=0; iRecord<NUM_NPC_QUOTE_RECORDS; iRecord++)
pFileData[iRecord] = pFileData_old_[iRecord];//dnl ch46 021009
MemFree(pFileData_old_);
}
// -----------------------------------------------------------
//
//</SB> new script format
return( pFileData );
// -----------------------------------------------------------
// WANNE: This is old vanilla JA2 code without conversion of NPC data to new format used for Big Maps project
/*
uiFileSize = sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS;
pFileData = (NPCQuoteInfo *)MemAlloc( uiFileSize );
if (pFileData)
{
if (!FileRead( hFile, pFileData, uiFileSize, &uiBytesRead ) || uiBytesRead != uiFileSize )
{
MemFree( pFileData );
pFileData = NULL;
}
}
FileClose( hFile );
return( pFileData );
// -----------------------------------------------------------
*/
}
BOOLEAN EnsureCivQuoteFileLoaded( UINT8 ubIndex )
{
BOOLEAN fLoadFile = FALSE;
if (gpCivQuoteInfoArray[ubIndex] == NULL)
{
fLoadFile = TRUE;
}
if ( fLoadFile )
{
gpCivQuoteInfoArray[ubIndex] = LoadCivQuoteFile( ubIndex );
if (gpCivQuoteInfoArray[ubIndex] == NULL)
{
return( FALSE );
}
}
return( TRUE );
}
BOOLEAN ReloadCivQuoteFile( UINT8 ubIndex )
{
if (gpCivQuoteInfoArray[ubIndex] != NULL)
{
MemFree( gpCivQuoteInfoArray[ubIndex] );
gpCivQuoteInfoArray[ubIndex] = NULL;
}
return( EnsureCivQuoteFileLoaded( ubIndex ) );
}
BOOLEAN ReloadCivQuoteFileIfLoaded( UINT8 ubIndex )
{
if (gpCivQuoteInfoArray[ubIndex] != NULL)
{
MemFree( gpCivQuoteInfoArray[ubIndex] );
gpCivQuoteInfoArray[ubIndex] = NULL;
return( EnsureCivQuoteFileLoaded( ubIndex ) );
}
else
{
return( TRUE );
}
}
void ShutdownNPCQuotes( void )
{
UINT8 ubLoop;
for ( ubLoop = 0; ubLoop < NUM_PROFILES; ubLoop++ )
{
if ( gpNPCQuoteInfoArray[ ubLoop ] )
{
MemFree( gpNPCQuoteInfoArray[ ubLoop ] );
gpNPCQuoteInfoArray[ ubLoop ] = NULL;
}
if ( gpBackupNPCQuoteInfoArray[ ubLoop ] != NULL )
{
MemFree( gpBackupNPCQuoteInfoArray[ ubLoop ] );
gpBackupNPCQuoteInfoArray[ ubLoop ] = NULL;
}
}
for ( ubLoop = 0; ubLoop < NUM_CIVQUOTE_SECTORS; ubLoop++ )
{
if ( gpCivQuoteInfoArray[ ubLoop ] )
{
MemFree( gpCivQuoteInfoArray[ ubLoop ] );
gpCivQuoteInfoArray[ ubLoop ] = NULL;
}
}
}
//
// GENERAL LOW LEVEL ROUTINES
//
BOOLEAN ReloadAllQuoteFiles( void )
{
UINT8 ubProfile, ubLoop;
//for ( ubProfile = FIRST_RPC; ubProfile < GASTON; ubProfile++ )
//new profiles by Jazz
for ( ubProfile = 0; ubProfile < NUM_PROFILES; ubProfile++ )
{
if ( gProfilesRPC[ubProfile].ProfilId == ubProfile || gProfilesNPC[ubProfile].ProfilId == ubProfile )
{
// zap backup if any
if ( gpBackupNPCQuoteInfoArray[ ubProfile ] != NULL )
{
MemFree( gpBackupNPCQuoteInfoArray[ ubProfile ] );
gpBackupNPCQuoteInfoArray[ ubProfile ] = NULL;
}
ReloadQuoteFileIfLoaded( ubProfile );
}
}
// reload all civ quote files
for ( ubLoop = 0; ubLoop < NUM_CIVQUOTE_SECTORS; ubLoop++ )
{
ReloadCivQuoteFileIfLoaded( ubLoop );
}
return( TRUE );
}
//
// THE REST
//
void SetQuoteRecordAsUsed( UINT8 ubNPC, UINT8 ubRecord )
{
if ( EnsureQuoteFileLoaded( ubNPC ) )
{
gpNPCQuoteInfoArray[ ubNPC ][ ubRecord ].fFlags |= QUOTE_FLAG_SAID;
}
}
INT32 CalcThreateningEffectiveness( UINT8 ubMerc )
{
SOLDIERTYPE * pSoldier;
INT32 iStrength, iDeadliness;
// effective threat is 1/3 strength, 1/3 weapon deadliness, 1/3 leadership
pSoldier = FindSoldierByProfileID( ubMerc, TRUE );
if ( !pSoldier )
{
return( 0 );
}
iStrength = EffectiveStrength( pSoldier );
if ( Item[ pSoldier->inv[HANDPOS].usItem ].usItemClass & IC_WEAPON )
{
iDeadliness = Weapon[ pSoldier->inv[HANDPOS].usItem ].ubDeadliness;
}
else
{
iDeadliness = 0;
}
if ( iDeadliness == 0 )
{
// penalize!
iDeadliness = -30;
}
// SANDRO - bonus for threatening for assertive people
if ( gGameOptions.fNewTraitSystem && gMercProfiles[pSoldier->ubProfile].bCharacterTrait == CHAR_TRAIT_ASSERTIVE )
{
iDeadliness += 50;
}
return( (EffectiveLeadership( pSoldier ) + iStrength + iDeadliness) / 3 ); //Trail minor bug fix.
}
UINT8 CalcDesireToTalk( UINT8 ubNPC, UINT8 ubMerc, INT8 bApproach )
{
INT32 iWillingness;
INT32 iPersonalVal, iTownVal, iApproachVal;
INT32 iEffectiveLeadership;
MERCPROFILESTRUCT * pNPCProfile;
MERCPROFILESTRUCT * pMercProfile;
pNPCProfile = &(gMercProfiles[ubNPC]);
pMercProfile = &(gMercProfiles[ubMerc]);
iPersonalVal = 50; /* + pNPCProfile->bMercTownReputation[ pNPCProfile->bTown ] */;
if (OKToCheckOpinion(ubMerc)) {
iPersonalVal += pNPCProfile->bMercOpinion[ubMerc];
}
// SANDRO - bonus for communication with people for assertive people
if ( gGameOptions.fNewTraitSystem && bApproach != APPROACH_THREATEN)
{
if ( pMercProfile->bCharacterTrait == CHAR_TRAIT_ASSERTIVE )
iPersonalVal += 50;
else if ( pMercProfile->bCharacterTrait == CHAR_TRAIT_MALICIOUS )
iPersonalVal -= 50;
}
// ARM: NOTE - for towns which don't use loyalty (San Mona, Estoni, Tixa, Orta )
// loyalty will always remain 0 (this was OKed by Ian)
iTownVal = gTownLoyalty[ pNPCProfile->bTown ].ubRating;
iTownVal = iTownVal * pNPCProfile->bTownAttachment / 100;
if (bApproach == NPC_INITIATING_CONV || bApproach == APPROACH_GIVINGITEM )
{
iApproachVal = 100;
}
else if ( bApproach == APPROACH_THREATEN )
{
iEffectiveLeadership = CalcThreateningEffectiveness( ubMerc ) * pMercProfile->usApproachFactor[bApproach - 1] / 100;
iApproachVal = pNPCProfile->ubApproachVal[bApproach - 1] * iEffectiveLeadership / 50;
}
else
{
iEffectiveLeadership = ((INT32) pMercProfile->bLeadership) * pMercProfile->usApproachFactor[bApproach - 1] / 100;
iApproachVal = pNPCProfile->ubApproachVal[bApproach - 1] * iEffectiveLeadership / 50;
}
// NB if town attachment is less than 100% then we should make personal value proportionately more important!
if ( pNPCProfile->bTownAttachment < 100 )
{
iPersonalVal = iPersonalVal * (100 + (100 - pNPCProfile->bTownAttachment) ) / 100;
}
iWillingness = (iPersonalVal / 2 + iTownVal / 2) * iApproachVal / 100 - gubTeamPenalty;
if (bApproach == NPC_INITIATING_CONV)
{
iWillingness -= INITIATING_FACTOR;
}
if (iWillingness < 0)
{
iWillingness = 0;
}
return( (UINT8) iWillingness );
}
void ApproachedForFirstTime( MERCPROFILESTRUCT * pNPCProfile, INT8 bApproach )
{
UINT8 ubLoop;
UINT32 uiTemp;
pNPCProfile->bApproached |= gbFirstApproachFlags[bApproach - 1];
for (ubLoop = 1; ubLoop <= NUM_REAL_APPROACHES; ubLoop++)
{
uiTemp = (UINT32) pNPCProfile->ubApproachVal[ubLoop - 1] * (UINT32) pNPCProfile->ubApproachMod[bApproach - 1][ubLoop - 1] / 100;
if (uiTemp > 255)
{
uiTemp = 255;
}
pNPCProfile->ubApproachVal[ubLoop-1] = (UINT8) uiTemp;
}
}
UINT8 NPCConsiderTalking( UINT8 ubNPC, UINT8 ubMerc, INT8 bApproach, UINT8 ubRecord, NPCQuoteInfo * pNPCQuoteInfoArray, NPCQuoteInfo ** ppResultQuoteInfo, UINT8 *pubQuoteNum )
{
// This function returns the opinion level required of the "most difficult" quote
// that the NPC is willing to say to the merc. It can also provide the quote #.
MERCPROFILESTRUCT * pNPCProfile=NULL;
NPCQuoteInfo * pNPCQuoteInfo=NULL;
UINT8 ubTalkDesire, ubLoop, ubQuote, ubHighestOpinionRequired = 0;
BOOLEAN fQuoteFound = FALSE;
UINT32 uiDay;
UINT8 ubFirstQuoteRecord, ubLastQuoteRecord;
SOLDIERTYPE *pSoldier=NULL;
ubTalkDesire = ubQuote = 0;
pSoldier = FindSoldierByProfileID( ubNPC, FALSE );
if (pSoldier == NULL)
{
return( 0 );
}
if (ppResultQuoteInfo)
{
(*ppResultQuoteInfo) = NULL;
}
if ( pubQuoteNum )
{
(*pubQuoteNum) = 0;
}
if (bApproach <= NUM_REAL_APPROACHES)
{
pNPCProfile = &(gMercProfiles[ubNPC]);
// What's our willingness to divulge?
ubTalkDesire = CalcDesireToTalk( ubNPC, ubMerc, bApproach );
if ( bApproach < NUM_REAL_APPROACHES && !(pNPCProfile->bApproached & gbFirstApproachFlags[bApproach - 1]) )
{
ApproachedForFirstTime( pNPCProfile, bApproach );
}
}
else if ( ubNPC == PABLO && bApproach == APPROACH_SECTOR_NOT_SAFE ) // for Pablo, consider as threaten
{
pNPCProfile = &(gMercProfiles[ubNPC]);
// What's our willingness to divulge?
ubTalkDesire = CalcDesireToTalk( ubNPC, ubMerc, APPROACH_THREATEN );
if ( pNPCProfile->bApproached & gbFirstApproachFlags[APPROACH_THREATEN - 1] )
{
ApproachedForFirstTime( pNPCProfile, APPROACH_THREATEN );
}
}
switch( bApproach )
{
/*
case APPROACH_RECRUIT:
ubFirstQuoteRecord = 0;
ubLastQuoteRecord = 0;
break;
*/
case TRIGGER_NPC:
ubFirstQuoteRecord = ubRecord;
ubLastQuoteRecord = ubRecord;
break;
default:
ubFirstQuoteRecord = 0;
ubLastQuoteRecord = NUM_NPC_QUOTE_RECORDS - 1;
break;
}
uiDay = GetWorldDay();
for (ubLoop = ubFirstQuoteRecord; ubLoop <= ubLastQuoteRecord; ubLoop++)
{
pNPCQuoteInfo = &(pNPCQuoteInfoArray[ ubLoop ]);
// Check if we have the item / are in right spot
if ( pNPCQuoteInfo->sRequiredItem > 0 )
{
if ( !ObjectExistsInSoldierProfile( ubNPC, pNPCQuoteInfo->sRequiredItem ) )
{
continue;
}
}
else if(!TileIsOutOfBounds(pNPCQuoteInfo->sRequiredGridNo))//dnl ch46 031009
{
if(pSoldier->sGridNo != pNPCQuoteInfo->sRequiredGridNo)
continue;
}
if ( NPCConsiderQuote( ubNPC, ubMerc, bApproach, ubLoop, ubTalkDesire, pNPCQuoteInfoArray ) )
{
if (bApproach == NPC_INITIATING_CONV)
{
// want to find the quote with the highest required opinion rating that we're willing
// to say
if ( pNPCQuoteInfo->ubOpinionRequired > ubHighestOpinionRequired )
{
fQuoteFound = TRUE;
ubHighestOpinionRequired = pNPCQuoteInfo->ubOpinionRequired;
ubQuote = pNPCQuoteInfo->ubQuoteNum;
}
}
else
{
// we do have a quote to say, and we want to say this one right away!
if (ppResultQuoteInfo)
{
(*ppResultQuoteInfo) = pNPCQuoteInfo;
}
if ( pubQuoteNum )
{
(*pubQuoteNum) = ubLoop;
}
return( pNPCQuoteInfo->ubOpinionRequired );
}
}
}
// Whew, checked them all. If we found a quote, return the appropriate values.
if (fQuoteFound)
{
if (ppResultQuoteInfo)
{
(*ppResultQuoteInfo) = pNPCQuoteInfo;
}
if ( pubQuoteNum )
{
(*pubQuoteNum) = ubQuote;
}
return( ubHighestOpinionRequired );
}
else
{
if (ppResultQuoteInfo)
{
(*ppResultQuoteInfo) = NULL;
}
if ( pubQuoteNum )
{
(*pubQuoteNum) = 0;
}
return( 0 );
}
}
UINT8 NPCConsiderReceivingItemFromMerc( UINT8 ubNPC, UINT8 ubMerc, OBJECTTYPE * pObj, NPCQuoteInfo * pNPCQuoteInfoArray, NPCQuoteInfo ** ppResultQuoteInfo, UINT8 *pubQuoteNum )
{
// This function returns the opinion level required of the "most difficult" quote
// that the NPC is willing to say to the merc. It can also provide the quote #.
MERCPROFILESTRUCT * pNPCProfile;
NPCQuoteInfo * pNPCQuoteInfo;
UINT8 ubTalkDesire, ubLoop, ubHighestOpinionRequired = 0;
BOOLEAN fQuoteFound = FALSE;
UINT8 ubFirstQuoteRecord, ubLastQuoteRecord;
UINT16 usItemToConsider;
(*ppResultQuoteInfo) = NULL;
(*pubQuoteNum) = 0;
if ( CheckFact( FACT_NPC_IS_ENEMY, ubNPC ) && ubNPC != JOE )
{
// don't accept any items when we are the player's enemy
return( 0 );
}
pNPCProfile = &(gMercProfiles[ubNPC]);
// How much do we want to talk with this merc?
ubTalkDesire = CalcDesireToTalk( ubNPC, ubMerc, APPROACH_GIVINGITEM );
ubFirstQuoteRecord = 0;
ubLastQuoteRecord = NUM_NPC_QUOTE_RECORDS - 1;
usItemToConsider = pObj->usItem;
if ( Item[ usItemToConsider ].usItemClass == IC_GUN && !Item[usItemToConsider].rocketlauncher )
{
UINT8 ubWeaponClass;
ubWeaponClass = Weapon[ usItemToConsider ].ubWeaponClass;
if ( ubWeaponClass == RIFLECLASS || ubWeaponClass == MGCLASS )
{
usItemToConsider = ANY_RIFLE; // treat all rifles the same
}
}
switch( usItemToConsider )
{
case HEAD_2:
case HEAD_3:
//case HEAD_4: // NOT Slay's head; it's different
case HEAD_5:
case HEAD_6:
case HEAD_7:
// all treated the same in the NPC code
usItemToConsider = HEAD_2;
break;
case MONEY:
case SILVER:
case GOLD:
if ((*pObj)[0]->data.money.uiMoneyAmount < LARGE_AMOUNT_MONEY)
{
SetFactTrue( FACT_SMALL_AMOUNT_OF_MONEY );
}
else
{
SetFactTrue( FACT_LARGE_AMOUNT_OF_MONEY );
}
usItemToConsider = MONEY;
break;
case WINE:
case BEER:
usItemToConsider = ALCOHOL;
break;
default:
break;
}
if ((*pObj)[0]->data.objectStatus < 50)
{
SetFactTrue( FACT_ITEM_POOR_CONDITION );
}
else
{
SetFactFalse( FACT_ITEM_POOR_CONDITION );
}
for (ubLoop = ubFirstQuoteRecord; ubLoop <= ubLastQuoteRecord; ubLoop++)
{
pNPCQuoteInfo = &(pNPCQuoteInfoArray[ ubLoop ]);
// First see if we want that item....
if ( pNPCQuoteInfo->sRequiredItem > 0 && ( pNPCQuoteInfo->sRequiredItem == usItemToConsider || pNPCQuoteInfo->sRequiredItem == ACCEPT_ANY_ITEM ) )
{
// Now see if everyhting else is OK
if ( NPCConsiderQuote( ubNPC, ubMerc, APPROACH_GIVINGITEM, ubLoop, ubTalkDesire, pNPCQuoteInfoArray ) )
{
switch( ubNPC )
{
case DARREN:
if (usItemToConsider == MONEY && pNPCQuoteInfo->sActionData == NPC_ACTION_DARREN_GIVEN_CASH)
{
if ((*pObj)[0]->data.money.uiMoneyAmount < 1000)
{
// refuse, bet too low - record 15
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[15];
(*pubQuoteNum) = 15;
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
else if ((*pObj)[0]->data.money.uiMoneyAmount > 5000)
{
// refuse, bet too high - record 16
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[16];
(*pubQuoteNum) = 16;
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
else
{
// accept - record 17
/*
{
SOLDIERTYPE * pSoldier;
INT8 bMoney;
INT8 bEmptySlot;
pSoldier = FindSoldierByProfileID( DARREN, FALSE );
bMoney = FindObjWithin( pSoldier, MONEY, BIGPOCK1POS, SMALLPOCK8POS );
bEmptySlot = FindObjWithin( pSoldier, NOTHING, BIGPOCK1POS, SMALLPOCK8POS );
}
*/
// record amount of bet
gMercProfiles[ DARREN ].iBalance = (*pObj)[0]->data.money.uiMoneyAmount;
SetFactFalse( FACT_DARREN_EXPECTING_MONEY );
// if never fought before, use record 17
// if fought before, today, use record 31
// else use record 18
if ( ! ( gpNPCQuoteInfoArray[ DARREN ][ 17 ].fFlags & QUOTE_FLAG_SAID ) ) // record 17 not used
{
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[17];
(*pubQuoteNum) = 17;
}
else
{
// find Kingpin, if he's in his house, invoke the script to move him to the bar
SOLDIERTYPE * pKingpin;
UINT8 ubKingpinRoom;
pKingpin = FindSoldierByProfileID( KINGPIN, FALSE );
if ( pKingpin && InARoom( pKingpin->sGridNo, &ubKingpinRoom ) )
{
if ( IN_KINGPIN_HOUSE( ubKingpinRoom ) )
{
// first boxer, bring kingpin over
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[17];
(*pubQuoteNum) = 17;
}
else
{
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[31];
(*pubQuoteNum) = 31;
}
}
else
{
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[31];
(*pubQuoteNum) = 31;
}
}
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
}
break;
case ANGEL:
if (usItemToConsider == MONEY && pNPCQuoteInfo->sActionData == NPC_ACTION_ANGEL_GIVEN_CASH)
{
if ((*pObj)[0]->data.money.uiMoneyAmount < Item[LEATHER_JACKET_W_KEVLAR].usPrice)
{
// refuse, bet too low - record 8
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[8];
(*pubQuoteNum) = 8;
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
else if ((*pObj)[0]->data.money.uiMoneyAmount > Item[LEATHER_JACKET_W_KEVLAR].usPrice)
{
// refuse, bet too high - record 9
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[9];
(*pubQuoteNum) = 9;
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
else
{
// accept - record 10
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[10];
(*pubQuoteNum) = 10;
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
}
break;
case MADAME:
if ( usItemToConsider == MONEY )
{
if ( gMercProfiles[ ubMerc ].bSex == FEMALE )
{
// say quote about not catering to women!
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[5];
(*pubQuoteNum) = 5;
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
switch( (*pObj)[0]->data.money.uiMoneyAmount )
{
case 100:
case 200: // Carla
if ( CheckFact( FACT_CARLA_AVAILABLE, 0 ) )
{
gMercProfiles[ MADAME ].bNPCData += (INT8) ((*pObj)[0]->data.money.uiMoneyAmount / 100);
TriggerNPCRecord( MADAME, 16 );
}
else
{
// see default case
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[25];
(*pubQuoteNum) = 25;
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
break;
case 500:
case 1000: // Cindy
if ( CheckFact( FACT_CINDY_AVAILABLE, 0 ) )
{
gMercProfiles[ MADAME ].bNPCData += (INT8) ((*pObj)[0]->data.money.uiMoneyAmount / 500);
TriggerNPCRecord( MADAME, 17 );
}
else
{
// see default case
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[25];
(*pubQuoteNum) = 25;
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
break;
case 300:
case 600: // Bambi
if ( CheckFact( FACT_BAMBI_AVAILABLE, 0 ) )
{
gMercProfiles[ MADAME ].bNPCData += (INT8) ((*pObj)[0]->data.money.uiMoneyAmount / 300);
TriggerNPCRecord( MADAME, 18 );
}
else
{
// see default case
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[25];
(*pubQuoteNum) = 25;
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
break;
case 400:
case 800: // Maria?
if ( gubQuest[ QUEST_RESCUE_MARIA ] == QUESTINPROGRESS )
{
gMercProfiles[ MADAME ].bNPCData += (INT8) ((*pObj)[0]->data.money.uiMoneyAmount / 400);
TriggerNPCRecord( MADAME, 19 );
break;
}
else
{
// see default case
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[25];
(*pubQuoteNum) = 25;
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
break;
default:
// play quotes 39-42 (plus 44 if quest 22 on) plus 43 if >1 PC
// and return money
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[25];
(*pubQuoteNum) = 25;
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
}
break;
case JOE:
if ( ubNPC == JOE && usItemToConsider == MONEY && pNPCQuoteInfo->sActionData != NPC_ACTION_JOE_GIVEN_CASH )
{
break;
}
// else fall through
case GERARD:
if ( ubNPC == GERARD && usItemToConsider == MONEY && pNPCQuoteInfo->sActionData != NPC_ACTION_GERARD_GIVEN_CASH )
{
break;
}
// else fall through
case STEVE:
case VINCE:
case WALTER:
case FRANK:
if (usItemToConsider == MONEY)
{
if ( ubNPC == VINCE || ubNPC == STEVE )
{
if ( CheckFact( FACT_VINCE_EXPECTING_MONEY, ubNPC ) == FALSE && gMercProfiles[ ubNPC ].iBalance < 0 && pNPCQuoteInfo->sActionData != NPC_ACTION_DONT_ACCEPT_ITEM )
{
// increment balance
gMercProfiles[ ubNPC ].iBalance += (INT32) (*pObj)[0]->data.money.uiMoneyAmount;
gMercProfiles[ ubNPC ].uiTotalCostToDate += (*pObj)[0]->data.money.uiMoneyAmount;
if ( gMercProfiles[ ubNPC ].iBalance > 0 )
{
gMercProfiles[ ubNPC ].iBalance = 0;
}
ScreenMsg( FONT_YELLOW, MSG_INTERFACE, TacticalStr[ BALANCE_OWED_STR ], gMercProfiles[ubNPC].zNickname, -gMercProfiles[ubNPC].iBalance );
}
else if ( CheckFact( FACT_VINCE_EXPECTING_MONEY, ubNPC ) == FALSE && pNPCQuoteInfo->sActionData != NPC_ACTION_DONT_ACCEPT_ITEM )
{
// just accept cash!
if ( ubNPC == VINCE )
{
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[ 8 ];
}
else
{
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[ 7 ];
}
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
else
{
// handle the player giving NPC some money
HandleNPCBeingGivenMoneyByPlayer( ubNPC, (*pObj)[0]->data.money.uiMoneyAmount, pubQuoteNum );
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[ *pubQuoteNum ];
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
}
else
{
// handle the player giving NPC some money
HandleNPCBeingGivenMoneyByPlayer( ubNPC, (*pObj)[0]->data.money.uiMoneyAmount, pubQuoteNum );
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[ *pubQuoteNum ];
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
}
break;
case KINGPIN:
if ( usItemToConsider == MONEY && gubQuest[ QUEST_KINGPIN_MONEY ] == QUESTINPROGRESS )
{
HandleNPCBeingGivenMoneyByPlayer( ubNPC, (*pObj)[0]->data.money.uiMoneyAmount, pubQuoteNum );
(*ppResultQuoteInfo) = &pNPCQuoteInfoArray[ *pubQuoteNum ];
return( (*ppResultQuoteInfo)->ubOpinionRequired );
}
break;
default:
//if ( usItemToConsider == MONEY && (ubNPC == SKYRIDER || (ubNPC >= FIRST_RPC && ubNPC < FIRST_NPC) ) )
//new profiles by Jazz
if ( usItemToConsider == MONEY && (ubNPC == SKYRIDER || ( gProfilesRPC[ubNPC].ProfilId == ubNPC ) ) )
{
if ( gMercProfiles[ ubNPC ].iBalance < 0 && pNPCQuoteInfo->sActionData != NPC_ACTION_DONT_ACCEPT_ITEM )
{
// increment balance
gMercProfiles[ ubNPC ].iBalance += (INT32) (*pObj)[0]->data.money.uiMoneyAmount;
gMercProfiles[ ubNPC ].uiTotalCostToDate += (*pObj)[0]->data.money.uiMoneyAmount;
if ( gMercProfiles[ ubNPC ].iBalance > 0 )
{
gMercProfiles[ ubNPC ].iBalance = 0;
}
ScreenMsg( FONT_YELLOW, MSG_INTERFACE, TacticalStr[ BALANCE_OWED_STR ], gMercProfiles[ubNPC].zNickname, -gMercProfiles[ubNPC].iBalance );
}
}
break;
}
// This is great!
// Return desire value
(*ppResultQuoteInfo) = pNPCQuoteInfo;
(*pubQuoteNum) = ubLoop;
return( pNPCQuoteInfo->ubOpinionRequired );
}
}
}
return( 0 );
}
BOOLEAN HandleNPCBeingGivenMoneyByPlayer( UINT8 ubNPC, UINT32 uiMoneyAmount, UINT8 *pQuoteValue )
{
switch( ubNPC )
{
// handle for STEVE and VINCE
case STEVE:
case VINCE:
{
INT32 iCost;
iCost = (INT32) CalcMedicalCost( ubNPC );
// check amount of money
if ( (INT32)uiMoneyAmount + giHospitalTempBalance + giHospitalRefund >= iCost )
{
// enough cash, check how much help is needed
if( CheckFact( FACT_WOUNDED_MERCS_NEARBY , ubNPC) )
{
*pQuoteValue = 26;
}
else if( CheckFact( FACT_ONE_WOUNDED_MERC_NEARBY, ubNPC ) )
{
*pQuoteValue = 25;
}
if ( giHospitalRefund > 0 )
{
giHospitalRefund = __max( 0, giHospitalRefund - iCost + uiMoneyAmount );
}
giHospitalTempBalance = 0;
}
else
{
CHAR16 sTempString[ 100 ];
swprintf( sTempString, L"%ld", iCost - uiMoneyAmount - giHospitalTempBalance );
InsertDollarSignInToString( sTempString );
// not enough cash
ScreenMsg( FONT_MCOLOR_LTYELLOW,
MSG_INTERFACE,
Message[ STR_NEED_TO_GIVE_MONEY ],
gMercProfiles[ ubNPC ].zNickname,
sTempString );
*pQuoteValue = 27;
giHospitalTempBalance += uiMoneyAmount;
}
}
break;
case KINGPIN:
if ( (INT32) uiMoneyAmount < -gMercProfiles[ KINGPIN ].iBalance )
{
*pQuoteValue = 9;
}
else
{
*pQuoteValue = 10;
}
gMercProfiles[ KINGPIN ].iBalance += (INT32) uiMoneyAmount;
break;
case WALTER:
if ( gMercProfiles[ WALTER ].iBalance == 0 )
{
*pQuoteValue = 12;
}
else
{
*pQuoteValue = 13;
}
gMercProfiles[ WALTER ].iBalance += uiMoneyAmount;
break;
case FRANK:
gArmsDealerStatus[ ARMS_DEALER_FRANK ].uiArmsDealersCash += uiMoneyAmount;
break;
case GERARD:
gMercProfiles[ GERARD ].iBalance += uiMoneyAmount;
if ( (gMercProfiles[ GERARD ].iBalance) >= 10000 )
{
*pQuoteValue = 12;
}
else
{
*pQuoteValue = 11;
}
break;
case JOE:
gMercProfiles[ JOE ].iBalance += uiMoneyAmount;
if ( (gMercProfiles[ JOE ].iBalance) >= 10000 )
{
*pQuoteValue = 7;
}
else
{
*pQuoteValue = 6;
}
break;
}
return( TRUE );
}
UINT8 NPCConsiderQuote( UINT8 ubNPC, UINT8 ubMerc, UINT8 ubApproach, UINT8 ubQuoteNum, UINT8 ubTalkDesire, NPCQuoteInfo * pNPCQuoteInfoArray )
{
//This function looks at a quote and determines if conditions for it have been met.
// Returns 0 if none , 1 if one is found
MERCPROFILESTRUCT * pNPCProfile;
NPCQuoteInfo * pNPCQuoteInfo;
UINT32 uiDay;
BOOLEAN fTrue;
if ( ubNPC == NO_PROFILE )
{
pNPCProfile = NULL;
}
else
{
pNPCProfile = &(gMercProfiles[ubNPC]);
}
// How much do we want to talk with this merc?
uiDay = GetWorldDay();
pNPCQuoteInfo = &(pNPCQuoteInfoArray[ubQuoteNum]);
#ifdef JA2TESTVERSION
if ( ubNPC != NO_PROFILE && ubMerc != NO_PROFILE )
{
NpcRecordLoggingInit( ubNPC, ubMerc, ubQuoteNum, ubApproach );
}
#endif
if (CHECK_FLAG( pNPCQuoteInfo->fFlags, QUOTE_FLAG_SAID ))
{
#ifdef JA2TESTVERSION
//Add entry to the quest debug file
NpcRecordLogging( ubApproach, "Quote Already Said, leaving");
#endif
// skip quotes already said
return( FALSE );
}
// if the quote is quest-specific, is the player on that quest?
if (pNPCQuoteInfo->ubQuest != NO_QUEST)
{
#ifdef JA2TESTVERSION
//Add entry to the quest debug file
NpcRecordLogging( ubApproach, "Quest(%d:'%S') Must be in Progress, status is %d. %s", pNPCQuoteInfo->ubQuest, QuestDescText[ pNPCQuoteInfo->ubQuest ], gubQuest[pNPCQuoteInfo->ubQuest], (gubQuest[pNPCQuoteInfo->ubQuest] != QUESTINPROGRESS) ? "False, return" : "True" );
#endif
if (pNPCQuoteInfo->ubQuest > QUEST_DONE_NUM)
{
if (gubQuest[pNPCQuoteInfo->ubQuest - QUEST_DONE_NUM] != QUESTDONE)
{
return( FALSE );
}
}
else if (pNPCQuoteInfo->ubQuest > QUEST_NOT_STARTED_NUM)
{
if (gubQuest[pNPCQuoteInfo->ubQuest - QUEST_NOT_STARTED_NUM] != QUESTNOTSTARTED)
{
return( FALSE );
}
}
else
{
if (gubQuest[pNPCQuoteInfo->ubQuest] != QUESTINPROGRESS)
{
return( FALSE );
}
}
}
// if there are facts to be checked, check them
if (pNPCQuoteInfo->usFactMustBeTrue != NO_FACT)
{
fTrue = CheckFact( pNPCQuoteInfo->usFactMustBeTrue, ubNPC );
#ifdef JA2TESTVERSION
//Add entry to the quest debug file
NpcRecordLogging( ubApproach, "Fact (%d:'%S') Must be True, status is %s", pNPCQuoteInfo->usFactMustBeTrue, FactDescText[pNPCQuoteInfo->usFactMustBeTrue], (fTrue == FALSE) ? "False, returning" : "True" );
#endif
if (fTrue == FALSE)
{
return( FALSE );
}
}
if (pNPCQuoteInfo->usFactMustBeFalse != NO_FACT)
{
fTrue = CheckFact( pNPCQuoteInfo->usFactMustBeFalse, ubNPC );
#ifdef JA2TESTVERSION
//Add entry to the quest debug file
NpcRecordLogging( ubApproach, "Fact(%d:'%S') Must be False status is %s", pNPCQuoteInfo->usFactMustBeFalse, FactDescText[pNPCQuoteInfo->usFactMustBeFalse], (fTrue == TRUE) ? "True, return" : "FALSE" );
#endif
if (fTrue == TRUE)
{
return( FALSE );
}
}
// check for required approach
// since the "I hate you" code triggers the record, triggering has to work properly
// with the other value that is stored!
if ( pNPCQuoteInfo->ubApproachRequired || !(ubApproach == APPROACH_FRIENDLY || ubApproach == APPROACH_DIRECT || ubApproach == TRIGGER_NPC ) )
{
#ifdef JA2TESTVERSION
//Add entry to the quest debug file
NpcRecordLogging( ubApproach, "Approach Taken(%d) must equal required Approach(%d) = %s", ubApproach, pNPCQuoteInfo->ubApproachRequired, (ubApproach != pNPCQuoteInfo->ubApproachRequired) ? "TRUE, return" : "FALSE" );
#endif
if ( pNPCQuoteInfo->ubApproachRequired == APPROACH_ONE_OF_FOUR_STANDARD )
{
// friendly to recruit will match
if ( ubApproach < APPROACH_FRIENDLY || ubApproach > APPROACH_RECRUIT )
{
return( FALSE );
}
}
else if ( pNPCQuoteInfo->ubApproachRequired == APPROACH_FRIENDLY_DIRECT_OR_RECRUIT )
{
if ( ubApproach != APPROACH_FRIENDLY && ubApproach != APPROACH_DIRECT && ubApproach != APPROACH_RECRUIT )
{
return( FALSE );
}
}
else if (ubApproach != pNPCQuoteInfo->ubApproachRequired)
{
return( FALSE );
}
}
// check time constraints on the quotes
if (pNPCProfile != NULL && pNPCQuoteInfo->ubFirstDay == MUST_BE_NEW_DAY)
{
#ifdef JA2TESTVERSION
//Add entry to the quest debug file
NpcRecordLogging( ubApproach, "Time constraints. Current Day(%d) must <= Day last spoken too (%d) : %s", uiDay, pNPCProfile->ubLastDateSpokenTo, (uiDay <= pNPCProfile->ubLastDateSpokenTo) ? "TRUE, return" : "FALSE" );
#endif
if (uiDay <= pNPCProfile->ubLastDateSpokenTo)
{
// too early!
return( FALSE );
}
}
else if (uiDay < pNPCQuoteInfo->ubFirstDay)
{
#ifdef JA2TESTVERSION
//Add entry to the quest debug file
NpcRecordLogging( ubApproach, "Current Day(%d) is before Required first day(%d) = %s", uiDay, pNPCQuoteInfo->ubFirstDay, (uiDay < pNPCQuoteInfo->ubFirstDay) ? "False, returning" : "True" );
#endif
// too early!
return( FALSE );
}
if (uiDay > pNPCQuoteInfo->ubLastDay && uiDay < 255 )
{
#ifdef JA2TESTVERSION
//Add entry to the quest debug file
NpcRecordLogging( ubApproach, "Current Day(%d) is after Required first day(%d) = %s", uiDay, pNPCQuoteInfo->ubFirstDay, (uiDay > pNPCQuoteInfo->ubLastDay) ? "TRUE, returning" : "FALSE" );
#endif
// too late!
return( FALSE );
}
// check opinion required
if ((pNPCQuoteInfo->ubOpinionRequired != IRRELEVANT) && (ubApproach != TRIGGER_NPC))
{
#ifdef JA2TESTVERSION
//Add entry to the quest debug file
NpcRecordLogging( ubApproach, "Opinion Required. Talk Desire (%d), Opinion Required(%d) : %s", ubTalkDesire, pNPCQuoteInfo->ubOpinionRequired, (ubTalkDesire < pNPCQuoteInfo->ubOpinionRequired) ? "False, return" : "False, continue" );
#endif
if (ubTalkDesire < pNPCQuoteInfo->ubOpinionRequired )
{
return( FALSE );
}
}
#ifdef JA2TESTVERSION
//Add entry to the quest debug file
NpcRecordLogging( ubApproach, "Return the quote opinion value! = TRUE");
#endif
// Return the quote opinion value!
return( TRUE );
}
void ReplaceLocationInNPCData( NPCQuoteInfo * pNPCQuoteInfoArray, INT32 sOldGridNo, INT32 sNewGridNo )
{
UINT8 ubFirstQuoteRecord, ubLastQuoteRecord, ubLoop;
NPCQuoteInfo * pNPCQuoteInfo;
ubFirstQuoteRecord = 0;
ubLastQuoteRecord = NUM_NPC_QUOTE_RECORDS - 1;
for (ubLoop = ubFirstQuoteRecord; ubLoop <= ubLastQuoteRecord; ubLoop++)
{
pNPCQuoteInfo = &(pNPCQuoteInfoArray[ ubLoop ]);
if(sOldGridNo == pNPCQuoteInfo->sRequiredGridNo)//dnl ch46 031009
pNPCQuoteInfo->sRequiredGridNo = sNewGridNo;
if (sOldGridNo == pNPCQuoteInfo->usGoToGridNo)
{
pNPCQuoteInfo->usGoToGridNo = sNewGridNo;
}
}
}
void ReplaceLocationInNPCDataFromProfileID( UINT8 ubNPC, INT32 sOldGridNo, INT32 sNewGridNo )
{
NPCQuoteInfo * pNPCQuoteInfoArray;
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
return;
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubNPC];
ReplaceLocationInNPCData( pNPCQuoteInfoArray, sOldGridNo, sNewGridNo );
}
void ResetOncePerConvoRecords( NPCQuoteInfo * pNPCQuoteInfoArray )
{
UINT8 ubLoop;
for ( ubLoop = 0; ubLoop < NUM_NPC_QUOTE_RECORDS; ubLoop++ )
{
if ( CHECK_FLAG(pNPCQuoteInfoArray[ubLoop].fFlags, QUOTE_FLAG_SAY_ONCE_PER_CONVO) )
{
TURN_FLAG_OFF( pNPCQuoteInfoArray[ubLoop].fFlags, QUOTE_FLAG_SAID );
}
}
}
void ResetOncePerConvoRecordsForNPC( UINT8 ubNPC )
{
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
return;
}
ResetOncePerConvoRecords( gpNPCQuoteInfoArray[ ubNPC ] );
}
void ResetOncePerConvoRecordsForAllNPCsInLoadedSector( void )
{
UINT8 ubLoop;
UINT8 IDnpc;
if ( gWorldSectorX == 0 || gWorldSectorY == 0 )
{
return;
}
//for ( ubLoop = FIRST_RPC; ubLoop < GASTON; ubLoop++ )
//new profiles by Jazz
for ( IDnpc = 0; IDnpc < NUM_PROFILES; IDnpc++ )
{
if ( gProfilesRPC[IDnpc].ProfilId == IDnpc || gProfilesNPC[IDnpc].ProfilId == IDnpc)
{
ubLoop = IDnpc;
if ( gMercProfiles[ ubLoop ].sSectorX == gWorldSectorX &&
gMercProfiles[ ubLoop ].sSectorY == gWorldSectorY &&
gMercProfiles[ ubLoop ].bSectorZ == gbWorldSectorZ &&
gpNPCQuoteInfoArray[ ubLoop ] != NULL )
{
ResetOncePerConvoRecordsForNPC( ubLoop );
}
}
}
}
void ReturnItemToPlayerIfNecessary( UINT8 ubMerc, INT8 bApproach, UINT32 uiApproachData, NPCQuoteInfo * pQuotePtr )
{
OBJECTTYPE * pObj;
SOLDIERTYPE * pSoldier;
// if the approach was changed, always return the item
// otherwise check to see if the record in question specified refusal
if ( bApproach != APPROACH_GIVINGITEM || (pQuotePtr == NULL ) || (pQuotePtr->sActionData == NPC_ACTION_DONT_ACCEPT_ITEM ) )
{
pObj = (OBJECTTYPE *) uiApproachData;
// Find the merc
pSoldier = FindSoldierByProfileID( ubMerc, FALSE );
// Try to auto place object and then if it fails, put into cursor
if ( !AutoPlaceObject( pSoldier, pObj, FALSE ) )
{
InternalBeginItemPointer( pSoldier, pObj, NO_SLOT );
}
DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
}
}
void Converse( UINT8 ubNPC, UINT8 ubMerc, INT8 bApproach, UINT32 uiApproachData )
{
NPCQuoteInfo QuoteInfo;
NPCQuoteInfo * pQuotePtr = &(QuoteInfo);
NPCQuoteInfo * pNPCQuoteInfoArray=NULL;
MERCPROFILESTRUCT * pProfile=NULL;
UINT8 ubLoop, ubQuoteNum, ubRecordNum;
SOLDIERTYPE * pSoldier=NULL;
UINT32 uiDay;
OBJECTTYPE * pObj=NULL;
SOLDIERTYPE * pNPC;
BOOLEAN fAttemptingToGiveItem;
// we have to record whether an item is being given in order to determine whether,
// in the case where the approach is overridden, we need to return the item to the
// player
fAttemptingToGiveItem = (bApproach == APPROACH_GIVINGITEM);
pNPC = FindSoldierByProfileID( ubNPC, FALSE );
if ( pNPC )
{
// set delay for civ AI movement
pNPC->uiTimeSinceLastSpoke = GetJA2Clock();
if ( CheckFact( FACT_CURRENT_SECTOR_IS_SAFE, ubNPC ) == FALSE )
{
if ( bApproach != TRIGGER_NPC && bApproach != APPROACH_GIVEFIRSTAID && bApproach != APPROACH_DECLARATION_OF_HOSTILITY && bApproach != APPROACH_ENEMY_NPC_QUOTE )
{
if ( NPCHasUnusedRecordWithGivenApproach( ubNPC, APPROACH_SECTOR_NOT_SAFE ) )
{
// override with sector-not-safe approach
bApproach = APPROACH_SECTOR_NOT_SAFE;
}
}
}
// make sure civ is awake now
pNPC->aiData.fAIFlags &= (~AI_ASLEEP);
}
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
if ( fAttemptingToGiveItem )
{
ReturnItemToPlayerIfNecessary( ubMerc, bApproach, uiApproachData, NULL );
}
return;
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubNPC];
//Legion jazz
if (zHiddenNames[ubNPC].Hidden == TRUE)
{
zHiddenNames[ubNPC].Hidden = FALSE;
ShowNPCEncyclopediaEntry(ubNPC, TRUE);
}
pProfile = &(gMercProfiles[ubNPC]);
switch( bApproach )
{
case NPC_INITIAL_QUOTE:
// reset stuff
ResetOncePerConvoRecords( pNPCQuoteInfoArray );
// CHEAP HACK
// Since we don't have CONDITIONAL once-per-convo refreshes, do this in code
// NB fact 281 is 'Darren has explained boxing rules'
if ( ubNPC == DARREN && CheckFact( 281, DARREN ) == FALSE )
{
TURN_FLAG_OFF( pNPCQuoteInfoArray[11].fFlags, QUOTE_FLAG_SAID );
}
// turn the NPC to face us
// this '1' value is a dummy....
NPCDoAction( ubNPC, NPC_ACTION_TURN_TO_FACE_NEAREST_MERC, 1 );
if (pProfile->ubLastDateSpokenTo > 0)
{
uiDay = GetWorldDay();
if (uiDay > pProfile->ubLastDateSpokenTo)
{
NPCConsiderTalking( ubNPC, ubMerc, APPROACH_SPECIAL_INITIAL_QUOTE, 0, pNPCQuoteInfoArray, &pQuotePtr, &ubRecordNum );
if (pQuotePtr != NULL)
{
// converse using this approach instead!
if ( fAttemptingToGiveItem )
{
ReturnItemToPlayerIfNecessary( ubMerc, bApproach, uiApproachData, NULL );
}
Converse( ubNPC, ubMerc, APPROACH_SPECIAL_INITIAL_QUOTE, 0 );
return;
}
// subsequent times approached intro
ubQuoteNum = QUOTE_SUBS_INTRO;
}
else
{
// say nothing!
if ( fAttemptingToGiveItem )
{
ReturnItemToPlayerIfNecessary( ubMerc, bApproach, uiApproachData, NULL );
}
return;
}
}
else
{
// try special initial quote first
NPCConsiderTalking( ubNPC, ubMerc, APPROACH_SPECIAL_INITIAL_QUOTE, 0, pNPCQuoteInfoArray, &pQuotePtr, &ubRecordNum );
if (pQuotePtr != NULL)
{
// converse using this approach instead!
if ( fAttemptingToGiveItem )
{
ReturnItemToPlayerIfNecessary( ubMerc, bApproach, uiApproachData, NULL );
}
Converse( ubNPC, ubMerc, APPROACH_SPECIAL_INITIAL_QUOTE, 0 );
return;
}
NPCConsiderTalking( ubNPC, ubMerc, APPROACH_INITIAL_QUOTE, 0, pNPCQuoteInfoArray, &pQuotePtr, &ubRecordNum );
if (pQuotePtr != NULL)
{
// converse using this approach instead!
if ( fAttemptingToGiveItem )
{
ReturnItemToPlayerIfNecessary( ubMerc, bApproach, uiApproachData, NULL );
}
Converse( ubNPC, ubMerc, APPROACH_INITIAL_QUOTE, 0 );
return;
}
// first time approached intro
ubQuoteNum = QUOTE_INTRO;
}
TalkingMenuDialogue( ubQuoteNum );
pProfile->ubLastQuoteSaid = ubQuoteNum;
pProfile->bLastQuoteSaidWasSpecial = FALSE;
break;
case NPC_WHOAREYOU:
ubQuoteNum = QUOTE_INTRO;
TalkingMenuDialogue( ubQuoteNum );
// For now, DO NOT remember for 'Come again?'
break;
case APPROACH_REPEAT:
if (pProfile->ubLastQuoteSaid == NO_QUOTE)
{
// this should never occur now!
TalkingMenuDialogue( QUOTE_INTRO );
}
else
{
if (pProfile->bLastQuoteSaidWasSpecial)
{
pQuotePtr = &(pNPCQuoteInfoArray[pProfile->ubLastQuoteSaid]);
// say quote and following consecutive quotes
for (ubLoop = 0; ubLoop < pQuotePtr->ubNumQuotes; ubLoop++)
{
// say quote #(pQuotePtr->ubQuoteNum + ubLoop)
TalkingMenuDialogue( (UINT8)( pQuotePtr->ubQuoteNum + ubLoop ) );
}
}
else
{
TalkingMenuDialogue( pProfile->ubLastQuoteSaid );
}
}
break;
default:
switch( bApproach )
{
case APPROACH_GIVINGITEM:
// first start by triggering any introduction quote if there is one...
if ( pProfile->ubLastDateSpokenTo > 0)
{
uiDay = GetWorldDay();
if (uiDay > pProfile->ubLastDateSpokenTo)
{
NPCConsiderTalking( ubNPC, ubMerc, APPROACH_SPECIAL_INITIAL_QUOTE, 0, pNPCQuoteInfoArray, &pQuotePtr, &ubRecordNum );
if (pQuotePtr != NULL)
{
// converse using this approach instead!
Converse( ubNPC, ubMerc, APPROACH_SPECIAL_INITIAL_QUOTE, 0 );
if ( ubNPC == DARREN )
{
// then we have to make this give attempt fail
ReturnItemToPlayerIfNecessary( ubMerc, bApproach, uiApproachData, NULL );
return;
}
}
}
}
else
{
NPCConsiderTalking( ubNPC, ubMerc, APPROACH_INITIAL_QUOTE, 0, pNPCQuoteInfoArray, &pQuotePtr, &ubRecordNum );
if (pQuotePtr != NULL)
{
// converse using this approach instead!
Converse( ubNPC, ubMerc, APPROACH_INITIAL_QUOTE, 0 );
}
}
// If we are approaching because we want to give an item, do something different
pObj = (OBJECTTYPE *) uiApproachData;
NPCConsiderReceivingItemFromMerc( ubNPC, ubMerc, pObj, pNPCQuoteInfoArray, &pQuotePtr, &ubRecordNum );
break;
case TRIGGER_NPC:
// if triggering, pass in the approach data as the record to consider
DebugMsg( TOPIC_JA2, DBG_LEVEL_0, String( "Handling trigger %S/%d at %lu", gMercProfiles[ ubNPC ].zNickname, (UINT8)uiApproachData, GetJA2Clock() ) );
NPCConsiderTalking( ubNPC, ubMerc, bApproach, (UINT8)uiApproachData, pNPCQuoteInfoArray, &pQuotePtr, &ubRecordNum );
break;
default:
NPCConsiderTalking( ubNPC, ubMerc, bApproach, 0, pNPCQuoteInfoArray, &pQuotePtr, &ubRecordNum );
break;
}
if (pQuotePtr == NULL)
{
// say random everyday quote
// do NOT set last quote said!
switch( bApproach )
{
case APPROACH_FRIENDLY:
if (pProfile->bFriendlyOrDirectDefaultResponseUsedRecently)
{
ubQuoteNum = QUOTE_GETLOST;
}
else
{
ubQuoteNum = QUOTE_FRIENDLY_DEFAULT1 + (UINT8) Random( 2 );
pProfile->bFriendlyOrDirectDefaultResponseUsedRecently = TRUE;
}
break;
case APPROACH_DIRECT:
if (pProfile->bFriendlyOrDirectDefaultResponseUsedRecently)
{
ubQuoteNum = QUOTE_GETLOST;
}
else
{
ubQuoteNum = QUOTE_DIRECT_DEFAULT;
pProfile->bFriendlyOrDirectDefaultResponseUsedRecently = TRUE;
}
break;
case APPROACH_THREATEN:
if (pProfile->bThreatenDefaultResponseUsedRecently)
{
ubQuoteNum = QUOTE_GETLOST;
}
else
{
ubQuoteNum = QUOTE_THREATEN_DEFAULT;
pProfile->bThreatenDefaultResponseUsedRecently = TRUE;
}
break;
case APPROACH_RECRUIT:
if (pProfile->bRecruitDefaultResponseUsedRecently)
{
ubQuoteNum = QUOTE_GETLOST;
}
else
{
ubQuoteNum = QUOTE_RECRUIT_NO;
pProfile->bRecruitDefaultResponseUsedRecently = TRUE;
}
break;
case APPROACH_GIVINGITEM:
ubQuoteNum = QUOTE_GIVEITEM_NO;
/*
CC - now handled below
*/
break;
case TRIGGER_NPC:
// trigger did not succeed - abort!!
return;
default:
ubQuoteNum = QUOTE_INTRO;
break;
}
TalkingMenuDialogue( ubQuoteNum );
pProfile->ubLastQuoteSaid = ubQuoteNum;
pProfile->bLastQuoteSaidWasSpecial = FALSE;
if (ubQuoteNum == QUOTE_GETLOST)
{
if (ubNPC == 70 || ubNPC == 120)
{
// becomes an enemy
NPCDoAction( ubNPC, NPC_ACTION_BECOME_ENEMY, 0 );
}
// close panel at end of speech
NPCClosePanel();
}
else if ( ubQuoteNum == QUOTE_GIVEITEM_NO )
{
// close panel at end of speech
NPCClosePanel();
if ( pNPC )
{
switch( ubNPC )
{
case JIM:
case JACK:
case OLAF:
case RAY:
case OLGA:
case TYRONE:
// Start combat etc
CancelAIAction( pNPC, TRUE );
AddToShouldBecomeHostileOrSayQuoteList( pNPC->ubID );
default:
break;
}
}
}
}
else
{
#ifdef JA2BETAVERSION
if ( gfDisplayScreenMsgOnRecordUsage )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, L"Using record %d for %s", ubRecordNum, gMercProfiles[ ubNPC ].zNickname );
}
#endif
// turn before speech?
if ( pQuotePtr->sActionData <= -NPC_ACTION_TURN_TO_FACE_NEAREST_MERC )
{
pSoldier = FindSoldierByProfileID( ubNPC, FALSE );
ZEROTIMECOUNTER( pSoldier->timeCounters.AICounter );
if (pSoldier->aiData.bNextAction == AI_ACTION_WAIT)
{
pSoldier->aiData.bNextAction = AI_ACTION_NONE;
pSoldier->aiData.usNextActionData = 0;
}
NPCDoAction( ubNPC, (UINT16) -(pQuotePtr->sActionData), ubRecordNum );
}
if (pQuotePtr->ubQuoteNum != NO_QUOTE)
{
// say quote and following consecutive quotes
for (ubLoop = 0; ubLoop < pQuotePtr->ubNumQuotes; ubLoop++)
{
TalkingMenuDialogue( (UINT8)( pQuotePtr->ubQuoteNum + ubLoop ) );
}
pProfile->ubLastQuoteSaid = ubRecordNum;
pProfile->bLastQuoteSaidWasSpecial = TRUE;
}
// set to "said" if we should do so
if (pQuotePtr->fFlags & QUOTE_FLAG_ERASE_ONCE_SAID || pQuotePtr->fFlags & QUOTE_FLAG_SAY_ONCE_PER_CONVO)
{
TURN_FLAG_ON( pQuotePtr->fFlags, QUOTE_FLAG_SAID );
}
// Carry out implications (actions) of this record
// Give NPC item if appropriate
if (bApproach == APPROACH_GIVINGITEM )
{
if ( pQuotePtr->sActionData != NPC_ACTION_DONT_ACCEPT_ITEM )
{
PlaceObjectInSoldierProfile( ubNPC, pObj );
// Find the GIVER....
pSoldier = FindSoldierByProfileID( ubMerc, FALSE );
// Is this one of us?
if ( pSoldier->bTeam == gbPlayerNum )
{
for (int x = INV_START_POS; x < NUM_INV_SLOTS; ++x) {
if (&(pSoldier->inv[x]) == pObj) {
DeleteObj(&pSoldier->inv[x]);
DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
break;
}
}
}
else
{
RemoveObjectFromSoldierProfile( ubMerc, pObj->usItem );
}
}
// CC: now handled below
/*
else
{
// ATE: Here, put back into inventory or place on ground....
{
// Find the merc
pSoldier = FindSoldierByProfileID( ubMerc, FALSE );
// Try to auto place object and then if it fails, put into cursor
if ( !AutoPlaceObject( pSoldier, pObj, FALSE ) )
{
InternalBeginItemPointer( pSoldier, pObj, NO_SLOT );
}
DirtyMercPanelInterface( pSoldier, DIRTYLEVEL2 );
}
}
*/
}
else if (bApproach == APPROACH_RECRUIT)
{
// the guy just joined our party
}
// Set things
if (pQuotePtr->usSetFactTrue != NO_FACT)
{
SetFactTrue( pQuotePtr->usSetFactTrue );
}
if (pQuotePtr->ubEndQuest != NO_QUEST)
{
EndQuest( pQuotePtr->ubEndQuest, gWorldSectorX, gWorldSectorY );
}
if (pQuotePtr->ubStartQuest != NO_QUEST)
{
StartQuest( pQuotePtr->ubStartQuest, gWorldSectorX, gWorldSectorY );
}
// Give item to merc?
if ( pQuotePtr->usGiftItem >= TURN_UI_OFF )
{
switch ( pQuotePtr->usGiftItem )
{
case TURN_UI_OFF:
if ( !(gTacticalStatus.uiFlags & INCOMBAT) )
{
gTacticalStatus.uiFlags |= ENGAGED_IN_CONV;
// Increment refrence count...
giNPCReferenceCount = 1;
}
break;
case TURN_UI_ON:
// while the special ref count is set, ignore standard off
if ( giNPCSpecialReferenceCount == 0 )
{
gTacticalStatus.uiFlags &= ~ENGAGED_IN_CONV;
// Decrement refrence count...
giNPCReferenceCount = 0;
}
break;
case SPECIAL_TURN_UI_OFF:
if ( !(gTacticalStatus.uiFlags & INCOMBAT) )
{
gTacticalStatus.uiFlags |= ENGAGED_IN_CONV;
// Increment refrence count...
giNPCReferenceCount = 1;
if ( giNPCSpecialReferenceCount < 0 )
{
// ???
giNPCSpecialReferenceCount = 0;
}
// increment SPECIAL reference count
giNPCSpecialReferenceCount += 1;
}
break;
case SPECIAL_TURN_UI_ON:
// Decrement SPECIAL reference count
giNPCSpecialReferenceCount -= 1;
// if special count is now 0, turn reactivate UI
if ( giNPCSpecialReferenceCount == 0 )
{
gTacticalStatus.uiFlags &= ~ENGAGED_IN_CONV;
giNPCReferenceCount = 0;
}
else if ( giNPCSpecialReferenceCount < 0 )
{
// ???
giNPCSpecialReferenceCount = 0;
}
break;
}
}
else if ( pQuotePtr->usGiftItem != 0 )
{
{
INT8 bInvPos;
// Get soldier
pSoldier = FindSoldierByProfileID( ubNPC, FALSE );
// Look for item....
bInvPos = FindObj( pSoldier, pQuotePtr->usGiftItem );
AssertMsg( bInvPos != NO_SLOT, "NPC.C: Gift item does not exist in NPC." );
TalkingMenuGiveItem( ubNPC, &(pSoldier->inv[ bInvPos ] ), bInvPos );
}
}
// Action before movement?
if ( pQuotePtr->sActionData < 0 && pQuotePtr->sActionData > -NPC_ACTION_TURN_TO_FACE_NEAREST_MERC )
{
pSoldier = FindSoldierByProfileID( ubNPC, FALSE );
ZEROTIMECOUNTER( pSoldier->timeCounters.AICounter );
if (pSoldier->aiData.bNextAction == AI_ACTION_WAIT)
{
pSoldier->aiData.bNextAction = AI_ACTION_NONE;
pSoldier->aiData.usNextActionData = 0;
}
NPCDoAction( ubNPC, (UINT16) -(pQuotePtr->sActionData), ubRecordNum );
}
else if ( pQuotePtr->usGoToGridNo == NO_MOVE && pQuotePtr->sActionData > 0 )
{
pSoldier = FindSoldierByProfileID( ubNPC, FALSE );
ZEROTIMECOUNTER( pSoldier->timeCounters.AICounter );
if (pSoldier->aiData.bNextAction == AI_ACTION_WAIT)
{
pSoldier->aiData.bNextAction = AI_ACTION_NONE;
pSoldier->aiData.usNextActionData = 0;
}
NPCDoAction( ubNPC, (UINT16) (pQuotePtr->sActionData), ubRecordNum );
}
// Movement?
if ( pQuotePtr->usGoToGridNo != NO_MOVE )
{
pSoldier = FindSoldierByProfileID( ubNPC, FALSE );
// stupid hack CC
if (pSoldier && ubNPC == KYLE)
{
// make sure he has keys
pSoldier->flags.bHasKeys = TRUE;
}
if (pSoldier && pSoldier->sGridNo == pQuotePtr->usGoToGridNo )
{
// search for quotes to trigger immediately!
pSoldier->ubQuoteRecord = ubRecordNum + 1; // add 1 so that the value is guaranteed nonzero
NPCReachedDestination( pSoldier, TRUE );
}
else
{
// turn off cowering
if ( pNPC->flags.uiStatusFlags & SOLDIER_COWERING)
{
//pNPC->flags.uiStatusFlags &= ~SOLDIER_COWERING;
pNPC->EVENT_InitNewSoldierAnim( STANDING, 0 , FALSE );
}
pSoldier->ubQuoteRecord = ubRecordNum + 1; // add 1 so that the value is guaranteed nonzero
if (pQuotePtr->sActionData == NPC_ACTION_TELEPORT_NPC)
{
BumpAnyExistingMerc( pQuotePtr->usGoToGridNo );
TeleportSoldier( pSoldier, pQuotePtr->usGoToGridNo, FALSE );
// search for quotes to trigger immediately!
NPCReachedDestination( pSoldier, FALSE );
}
else
{
NPCGotoGridNo( ubNPC, pQuotePtr->usGoToGridNo, ubRecordNum );
}
}
}
// Trigger other NPC?
// ATE: Do all triggers last!
if ( pQuotePtr->ubTriggerNPC != IRRELEVANT )
{
// Check for special NPC trigger codes
if ( pQuotePtr->ubTriggerNPC == 0 )
{
TriggerClosestMercWhoCanSeeNPC( ubNPC, pQuotePtr );
}
else if ( pQuotePtr->ubTriggerNPC == 1 )
{
// trigger self
TriggerNPCRecord( ubNPC, pQuotePtr->ubTriggerNPCRec );
}
else
{
TriggerNPCRecord( pQuotePtr->ubTriggerNPC, pQuotePtr->ubTriggerNPCRec );
}
}
// Ian says it is okay to take this out!
/*
if (bApproach == APPROACH_ENEMY_NPC_QUOTE)
{
NPCClosePanel();
}
*/
}
break;
}
// Set last day spoken!
switch( bApproach )
{
case APPROACH_FRIENDLY:
case APPROACH_DIRECT:
case APPROACH_THREATEN:
case APPROACH_RECRUIT:
case NPC_INITIATING_CONV:
case NPC_INITIAL_QUOTE:
case APPROACH_SPECIAL_INITIAL_QUOTE:
case APPROACH_DECLARATION_OF_HOSTILITY:
case APPROACH_INITIAL_QUOTE:
case APPROACH_GIVINGITEM:
// SANDRO - new records - NPCs discovered
if ( pProfile->ubLastDateSpokenTo == 0 && FindSoldierByProfileID( ubMerc, TRUE ) != NULL )
{
gMercProfiles[ubMerc].records.usNPCsDiscovered++;
}
pProfile->ubLastDateSpokenTo = (UINT8) GetWorldDay();
break;
default:
break;
}
// return item?
if ( fAttemptingToGiveItem )
{
ReturnItemToPlayerIfNecessary( ubMerc, bApproach, uiApproachData, pQuotePtr );
}
}
INT32 NPCConsiderInitiatingConv( SOLDIERTYPE * pNPC, UINT8 * pubDesiredMerc )
{
INT32 sMyGridNo, sDist, sDesiredMercDist = 100;
UINT8 ubNPC, ubMerc, ubDesiredMerc = NOBODY;
UINT8 ubTalkDesire, ubHighestTalkDesire = 0;
SOLDIERTYPE * pMerc;
SOLDIERTYPE * pDesiredMerc = NULL;
NPCQuoteInfo * pNPCQuoteInfoArray;
CHECKF( pubDesiredMerc );
sMyGridNo = pNPC->sGridNo;
ubNPC = pNPC->ubProfile;
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
return( NOWHERE );
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubNPC];
// loop through all mercs
for ( ubMerc = 0; ubMerc < guiNumMercSlots; ubMerc++ )
{
pMerc = MercSlots[ ubMerc ];
if (pMerc != NULL)
{
// only look for mercs on the side of the player
if (pMerc->bSide != gbPlayerNum)
{
continue;
}
// only look for active mercs
if (pMerc->bAssignment >= ON_DUTY )
{
continue;
}
// if they're not visible, don't think about it
if (pNPC->aiData.bOppList[ubMerc] != SEEN_CURRENTLY)
{
continue;
}
// what's the opinion required for the highest-opinion quote that we would
// say to this merc
ubTalkDesire = NPCConsiderTalking( pNPC->ubProfile, pMerc->ubProfile, NPC_INITIATING_CONV, 0, pNPCQuoteInfoArray, NULL, NULL );
if (ubTalkDesire > 0)
{
if (ubTalkDesire > ubHighestTalkDesire)
{
ubHighestTalkDesire = ubTalkDesire;
ubDesiredMerc = ubMerc;
pDesiredMerc = MercSlots[ubMerc];
sDesiredMercDist = PythSpacesAway( sMyGridNo, pDesiredMerc->sGridNo );
}
else if (ubTalkDesire == ubHighestTalkDesire)
{
sDist = PythSpacesAway( sMyGridNo, MercSlots[ubMerc]->sGridNo );
if (sDist < sDesiredMercDist)
{
// we can say the same thing to this merc, and they're closer!
ubDesiredMerc = ubMerc;
pDesiredMerc = MercSlots[ubMerc];
sDesiredMercDist = sDist;
}
}
}
}
}
if (ubDesiredMerc == NOBODY)
{
return( NOWHERE );
}
else
{
*pubDesiredMerc = ubDesiredMerc;
return ( pDesiredMerc->sGridNo );
}
}
UINT8 NPCTryToInitiateConv( SOLDIERTYPE * pNPC )
{ // assumes current action is ACTION_APPROACH_MERC
if (pNPC->aiData.bAction != AI_ACTION_APPROACH_MERC)
{
return( AI_ACTION_NONE );
}
if (PythSpacesAway( pNPC->sGridNo, MercPtrs[pNPC->aiData.usActionData]->sGridNo ) < CONVO_DIST)
{
// initiate conversation!
Converse( pNPC->ubProfile, MercPtrs[pNPC->aiData.usActionData]->ubProfile, NPC_INITIATING_CONV, 0 );
// after talking, wait a while before moving anywhere else
return( AI_ACTION_WAIT );
}
else
{
// try to move towards that merc
return( AI_ACTION_APPROACH_MERC );
}
}
/*
BOOLEAN NPCOkToGiveItem( UINT8 ubNPC, UINT8 ubMerc, UINT16 usItem )
{
// This function seems to be unused...
NPCQuoteInfo QuoteInfo;
NPCQuoteInfo * pQuotePtr = &(QuoteInfo);
NPCQuoteInfo * pNPCQuoteInfoArray;
UINT8 ubOpinionVal;
UINT8 ubQuoteNum;
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
return( FALSE );
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubNPC];
ubOpinionVal = NPCConsiderReceivingItemFromMerc( ubNPC, ubMerc, usItem, pNPCQuoteInfoArray, &pQuotePtr, &ubQuoteNum );
if ( ubOpinionVal )
{
return( TRUE );
}
else
{
return( FALSE );
}
}
*/
void NPCReachedDestination( SOLDIERTYPE * pNPC, BOOLEAN fAlreadyThere )
{
// perform action or whatever after reaching our destination
UINT8 ubNPC;
NPCQuoteInfo * pQuotePtr;
NPCQuoteInfo * pNPCQuoteInfoArray;
UINT8 ubLoop;
UINT8 ubQuoteRecord;
if ( pNPC->ubQuoteRecord == 0 )
{
ubQuoteRecord = 0;
}
else
{
ubQuoteRecord = (UINT8) (pNPC->ubQuoteRecord - 1);
}
// Clear values!
pNPC->ubQuoteRecord = 0;
if (pNPC->bTeam == gbPlayerNum)
{
// the "under ai control" flag was set temporarily; better turn it off now
pNPC->flags.uiStatusFlags &= (~SOLDIER_PCUNDERAICONTROL);
// make damn sure the AI_HANDLE_EVERY_FRAME flag is turned off
pNPC->aiData.fAIFlags &= (AI_HANDLE_EVERY_FRAME);
}
ubNPC = pNPC->ubProfile;
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
return;
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubNPC];
pQuotePtr = &(pNPCQuoteInfoArray[ubQuoteRecord]);
// either we are supposed to consider a new quote record
// (indicated by a negative gridno in the has-item field)
// or an action to perform once we reached this gridno
if ( pNPC->sGridNo == pQuotePtr->usGoToGridNo )
{
// check for an after-move action
if ( pQuotePtr->sActionData > 0)
{
NPCDoAction( ubNPC, (UINT16) pQuotePtr->sActionData, ubQuoteRecord );
}
}
for ( ubLoop = 0; ubLoop < NUM_NPC_QUOTE_RECORDS; ubLoop++ )
{
pQuotePtr = &(pNPCQuoteInfoArray[ubLoop]);
if ( pNPC->sGridNo == pQuotePtr->sRequiredGridNo )//dnl ch46 031009
{
if ( NPCConsiderQuote( ubNPC, 0, TRIGGER_NPC, ubLoop, 0, pNPCQuoteInfoArray ) )
{
if (fAlreadyThere)
{
TriggerNPCRecord( ubNPC, ubLoop );
}
else
{
// trigger this quote
TriggerNPCRecordImmediately( ubNPC, ubLoop );
}
return;
}
}
}
}
void TriggerNPCRecord( UINT8 ubTriggerNPC, UINT8 ubTriggerNPCRec )
{
// Check if we have a quote to trigger...
NPCQuoteInfo *pQuotePtr;
BOOLEAN fDisplayDialogue = TRUE;
if (EnsureQuoteFileLoaded( ubTriggerNPC ) == FALSE)
{
// error!!!
return;
}
pQuotePtr = &(gpNPCQuoteInfoArray[ ubTriggerNPC ][ ubTriggerNPCRec ]);
if ( pQuotePtr->ubQuoteNum == IRRELEVANT )
{
fDisplayDialogue = FALSE;
}
if ( NPCConsiderQuote( ubTriggerNPC, 0, TRIGGER_NPC, ubTriggerNPCRec, 0, gpNPCQuoteInfoArray[ ubTriggerNPC ] ) )
{
NPCTriggerNPC( ubTriggerNPC, ubTriggerNPCRec, TRIGGER_NPC, fDisplayDialogue );
}
else
{
// don't do anything
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "WARNING: trigger of %d, record %d cannot proceed, possible error", ubTriggerNPC, ubTriggerNPCRec ) );
}
}
void TriggerNPCRecordImmediately( UINT8 ubTriggerNPC, UINT8 ubTriggerNPCRec )
{
// Check if we have a quote to trigger...
NPCQuoteInfo *pQuotePtr;
BOOLEAN fDisplayDialogue = TRUE;
if (EnsureQuoteFileLoaded( ubTriggerNPC ) == FALSE)
{
// error!!!
return;
}
pQuotePtr = &(gpNPCQuoteInfoArray[ ubTriggerNPC ][ ubTriggerNPCRec ]);
if ( pQuotePtr->ubQuoteNum == IRRELEVANT )
{
fDisplayDialogue = FALSE;
}
if ( NPCConsiderQuote( ubTriggerNPC, 0, TRIGGER_NPC, ubTriggerNPCRec, 0, gpNPCQuoteInfoArray[ ubTriggerNPC ] ) )
{
// trigger IMMEDIATELY
HandleNPCTriggerNPC( ubTriggerNPC, ubTriggerNPCRec, fDisplayDialogue, TRIGGER_NPC );
}
else
{
// don't do anything
DebugMsg( TOPIC_JA2, DBG_LEVEL_3, String( "WARNING: trigger of %d, record %d cannot proceed, possible error", ubTriggerNPC, ubTriggerNPCRec ) );
}
}
void PCsNearNPC( UINT8 ubNPC )
{
UINT8 ubLoop;
NPCQuoteInfo * pNPCQuoteInfoArray;
SOLDIERTYPE *pSoldier;
NPCQuoteInfo * pQuotePtr;
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
return;
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubNPC];
// see what this triggers...
SetFactTrue( FACT_PC_NEAR );
// Clear values!
// Get value for NPC
pSoldier = FindSoldierByProfileID( ubNPC, FALSE );
pSoldier->ubQuoteRecord = 0;
for ( ubLoop = 0; ubLoop < NUM_NPC_QUOTE_RECORDS; ubLoop++ )
{
pQuotePtr = &(pNPCQuoteInfoArray[ubLoop]);
if ( pSoldier->sGridNo == pQuotePtr->sRequiredGridNo )//dnl ch46 031009
{
if ( NPCConsiderQuote( ubNPC, 0, TRIGGER_NPC, ubLoop, 0, pNPCQuoteInfoArray ) )
{
// trigger this quote IMMEDIATELY!
TriggerNPCRecordImmediately( ubNPC, ubLoop );
break;
}
}
}
// reset fact
SetFactFalse( FACT_PC_NEAR );
}
BOOLEAN PCDoesFirstAidOnNPC( UINT8 ubNPC )
{
UINT8 ubLoop;
NPCQuoteInfo * pNPCQuoteInfoArray;
SOLDIERTYPE *pSoldier;
NPCQuoteInfo * pQuotePtr;
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
return( FALSE );
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubNPC];
// Get ptr to NPC
pSoldier = FindSoldierByProfileID( ubNPC, FALSE );
// Clear values!
pSoldier->ubQuoteRecord = 0;
// Set flag...
gMercProfiles[ ubNPC ].ubMiscFlags2 |= PROFILE_MISC_FLAG2_BANDAGED_TODAY;
for ( ubLoop = 0; ubLoop < NUM_NPC_QUOTE_RECORDS; ubLoop++ )
{
pQuotePtr = &(pNPCQuoteInfoArray[ubLoop]);
if ( pQuotePtr->ubApproachRequired == APPROACH_GIVEFIRSTAID )
{
if ( NPCConsiderQuote( ubNPC, 0, TRIGGER_NPC, ubLoop, 0, pNPCQuoteInfoArray ) )
{
// trigger this quote IMMEDIATELY!
TriggerNPCRecordImmediately( ubNPC, ubLoop );
return( TRUE );
}
}
}
return( FALSE );
}
void TriggerClosestMercWhoCanSeeNPC( UINT8 ubNPC, NPCQuoteInfo *pQuotePtr )
{
// Loop through all mercs, gather closest mercs who can see and trigger one!
UINT8 ubMercsInSector[ 40 ] = { 0 };
UINT8 ubNumMercs = 0;
UINT8 ubChosenMerc;
SOLDIERTYPE *pTeamSoldier, *pSoldier;
INT32 cnt;
// First get pointer to NPC
pSoldier = FindSoldierByProfileID( ubNPC, FALSE );
// Loop through all our guys and randomly say one from someone in our sector
// set up soldier ptr as first element in mercptrs list
cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
// run through list
for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++,pTeamSoldier++ )
{
// Add guy if he's a candidate...
if ( OK_INSECTOR_MERC( pTeamSoldier ) && pTeamSoldier->aiData.bOppList[ pSoldier->ubID ] == SEEN_CURRENTLY )
{
ubMercsInSector[ ubNumMercs ] = (UINT8)cnt;
ubNumMercs++;
}
}
// If we are > 0
if ( ubNumMercs > 0 )
{
ubChosenMerc = (UINT8)Random( ubNumMercs );
// Post action to close panel
NPCClosePanel( );
// If 64, do something special
if ( pQuotePtr->ubTriggerNPCRec == QUOTE_RESPONSE_TO_MIGUEL_SLASH_QUOTE_MERC_OR_RPC_LETGO )
{
TacticalCharacterDialogueWithSpecialEvent( MercPtrs[ ubMercsInSector[ ubChosenMerc ] ], pQuotePtr->ubTriggerNPCRec, DIALOGUE_SPECIAL_EVENT_PCTRIGGERNPC, 57, 6 );
}
else
{
TacticalCharacterDialogue( MercPtrs[ ubMercsInSector[ ubChosenMerc ] ], pQuotePtr->ubTriggerNPCRec );
}
}
}
BOOLEAN TriggerNPCWithIHateYouQuote( UINT8 ubTriggerNPC )
{
// Check if we have a quote to trigger...
NPCQuoteInfo * pNPCQuoteInfoArray;
NPCQuoteInfo *pQuotePtr;
BOOLEAN fDisplayDialogue = TRUE;
UINT8 ubLoop;
if (EnsureQuoteFileLoaded( ubTriggerNPC ) == FALSE)
{
// error!!!
return( FALSE );
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubTriggerNPC];
for ( ubLoop = 0; ubLoop < NUM_NPC_QUOTE_RECORDS; ubLoop++ )
{
pQuotePtr = &(pNPCQuoteInfoArray[ubLoop]);
if ( NPCConsiderQuote( ubTriggerNPC, 0, APPROACH_DECLARATION_OF_HOSTILITY, ubLoop, 0, pNPCQuoteInfoArray ) )
{
// trigger this quote!
// reset approach required value so that we can trigger it
//pQuotePtr->ubApproachRequired = TRIGGER_NPC;
NPCTriggerNPC( ubTriggerNPC, ubLoop, APPROACH_DECLARATION_OF_HOSTILITY, TRUE );
gMercProfiles[ ubTriggerNPC ].ubMiscFlags |= PROFILE_MISC_FLAG_SAID_HOSTILE_QUOTE;
return( TRUE );
}
}
return( FALSE );
}
BOOLEAN NPCHasUnusedRecordWithGivenApproach( UINT8 ubNPC, UINT8 ubApproach )
{
// Check if we have a quote that could be used
NPCQuoteInfo * pNPCQuoteInfoArray;
NPCQuoteInfo *pQuotePtr;
UINT8 ubLoop;
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
return( FALSE );
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubNPC];
for ( ubLoop = 0; ubLoop < NUM_NPC_QUOTE_RECORDS; ubLoop++ )
{
pQuotePtr = &(pNPCQuoteInfoArray[ubLoop]);
if ( NPCConsiderQuote( ubNPC, 0, ubApproach, ubLoop, 0, pNPCQuoteInfoArray ) )
{
return( TRUE );
}
}
return( FALSE );
}
BOOLEAN NPCHasUnusedHostileRecord( UINT8 ubNPC, UINT8 ubApproach )
{
// this is just like the standard check BUT we must skip any
// records using fact 289 and print debug msg for any records which can't be marked as used
// Check if we have a quote that could be used
NPCQuoteInfo * pNPCQuoteInfoArray;
NPCQuoteInfo *pQuotePtr;
UINT8 ubLoop;
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
return( FALSE );
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubNPC];
for ( ubLoop = 0; ubLoop < NUM_NPC_QUOTE_RECORDS; ubLoop++ )
{
pQuotePtr = &(pNPCQuoteInfoArray[ubLoop]);
if ( NPCConsiderQuote( ubNPC, 0, ubApproach, ubLoop, 0, pNPCQuoteInfoArray ) )
{
if ( pQuotePtr->usFactMustBeTrue == FACT_NPC_HOSTILE_OR_PISSED_OFF )
{
continue;
}
#ifdef JA2BETAVERSION
if ( !(pQuotePtr->fFlags & QUOTE_FLAG_ERASE_ONCE_SAID) )
{
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, L"Warning: possible infinite quote loop to follow." );
}
#endif
return( TRUE );
}
}
return( FALSE );
}
BOOLEAN NPCWillingToAcceptItem( UINT8 ubNPC, UINT8 ubMerc, OBJECTTYPE * pObj )
{
// Check if we have a quote that could be used, that applies to this item
NPCQuoteInfo * pNPCQuoteInfoArray;
NPCQuoteInfo * pQuotePtr;
UINT8 ubOpinion, ubQuoteNum;
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
return( FALSE );
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubNPC];
ubOpinion = NPCConsiderReceivingItemFromMerc( ubNPC, ubMerc, pObj, pNPCQuoteInfoArray, &pQuotePtr, &ubQuoteNum );
if ( pQuotePtr )
{
return( TRUE );
}
return( FALSE );
}
BOOLEAN GetInfoForAbandoningEPC( UINT8 ubNPC, UINT16 * pusQuoteNum, UINT16 * pusFactToSetTrue )
{
// Check if we have a quote that could be used
NPCQuoteInfo * pNPCQuoteInfoArray;
NPCQuoteInfo *pQuotePtr;
UINT8 ubLoop;
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
return( FALSE );
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubNPC];
for ( ubLoop = 0; ubLoop < NUM_NPC_QUOTE_RECORDS; ubLoop++ )
{
pQuotePtr = &(pNPCQuoteInfoArray[ubLoop]);
if ( NPCConsiderQuote( ubNPC, 0, APPROACH_EPC_IN_WRONG_SECTOR, ubLoop, 0, pNPCQuoteInfoArray ) )
{
*pusQuoteNum = pNPCQuoteInfoArray[ubLoop].ubQuoteNum;
*pusFactToSetTrue = pNPCQuoteInfoArray[ubLoop].usSetFactTrue;
return( TRUE );
}
}
return( FALSE );
}
BOOLEAN TriggerNPCWithGivenApproach( UINT8 ubTriggerNPC, UINT8 ubApproach, BOOLEAN fShowPanel )
{
// Check if we have a quote to trigger...
NPCQuoteInfo * pNPCQuoteInfoArray;
NPCQuoteInfo *pQuotePtr;
BOOLEAN fDisplayDialogue = TRUE;
UINT8 ubLoop;
if (EnsureQuoteFileLoaded( ubTriggerNPC ) == FALSE)
{
// error!!!
return( FALSE );
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ubTriggerNPC];
for ( ubLoop = 0; ubLoop < NUM_NPC_QUOTE_RECORDS; ubLoop++ )
{
pQuotePtr = &(pNPCQuoteInfoArray[ubLoop]);
if ( NPCConsiderQuote( ubTriggerNPC, 0, ubApproach, ubLoop, 0, pNPCQuoteInfoArray ) )
{
if ( pQuotePtr->ubQuoteNum == IRRELEVANT )
{
fShowPanel = FALSE;
}
else
{
fShowPanel = TRUE;
}
// trigger this quote!
// reset approach required value so that we can trigger it
//pQuotePtr->ubApproachRequired = TRIGGER_NPC;
NPCTriggerNPC( ubTriggerNPC, ubLoop, ubApproach, fShowPanel );
return( TRUE );
}
}
return( FALSE );
}
BOOLEAN SaveNPCInfoToSaveGameFile( HWFILE hFile )
{
UINT32 uiNumBytesWritten=0;
UINT32 cnt;
UINT8 ubOne = 1;
UINT8 ubZero = 0;
//Loop through all the NPC quotes
for( cnt=0; cnt<NUM_PROFILES; cnt++)
{
//if there is a npc qutoe
if( gpNPCQuoteInfoArray[ cnt ] )
{
//save a byte specify that there is an npc quote saved
FileWrite( hFile, &ubOne, sizeof( UINT8 ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( UINT8 ) )
{
return( FALSE );
}
//Save the NPC quote entry
FileWrite( hFile, gpNPCQuoteInfoArray[ cnt ], sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS, &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS )
{
return( FALSE );
}
}
else
{
//save a byte specify that there is an npc quote saved
FileWrite( hFile, &ubZero, sizeof( UINT8 ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( UINT8 ) )
{
return( FALSE );
}
}
}
for( cnt = 0; cnt < NUM_CIVQUOTE_SECTORS; cnt++)
{
//if there is a civ quote
if( gpCivQuoteInfoArray[ cnt ] )
{
//save a byte specify that there is an npc quote saved
FileWrite( hFile, &ubOne, sizeof( UINT8 ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( UINT8 ) )
{
return( FALSE );
}
//Save the NPC quote entry
FileWrite( hFile, gpCivQuoteInfoArray[ cnt ], sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS, &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS )
{
return( FALSE );
}
}
else
{
//save a byte specify that there is an npc quote saved
FileWrite( hFile, &ubZero, sizeof( UINT8 ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( UINT8 ) )
{
return( FALSE );
}
}
}
return( TRUE );
}
extern INT32 ReadFieldByField(HWFILE hFile, PTR pDest, UINT32 uiFieldSize, UINT32 uiElementSize, UINT32 uiCurByteCount);
BOOLEAN LoadNPCInfoFromSavedGameFile( HWFILE hFile, UINT32 uiSaveGameVersion )
{
UINT32 uiNumBytesRead=0;
UINT32 cnt;
UINT8 ubLoadQuote=0;
UINT32 uiNumberToLoad=0;
INT32 numBytesRead = 0;
INT8 padding;
// If we are trying to restore a saved game prior to version 44, use the
// MAX_NUM_SOLDIERS, else use NUM_PROFILES. Dave used the wrong define!
// Verion 111 increased NUM_PROFILES from 170 to 255
if( uiSaveGameVersion >= 111 )
uiNumberToLoad = NUM_PROFILES;
else if( uiSaveGameVersion >= 44 )
uiNumberToLoad = NUM_PROFILES_v111;
else
uiNumberToLoad = MAX_NUM_SOLDIERS;
//Loop through all the NPC quotes
for( cnt=0; cnt<uiNumberToLoad; cnt++ )
{
//Load a byte specify that there is an npc quote Loadd
FileRead( hFile, &ubLoadQuote, sizeof( UINT8 ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( UINT8 ) )
{
return( FALSE );
}
//if there is an existing quote
if( gpNPCQuoteInfoArray[ cnt ] )
{
//delete it
MemFree( gpNPCQuoteInfoArray[ cnt ] );
gpNPCQuoteInfoArray[ cnt ] = NULL;
}
//if there is a npc quote
if( ubLoadQuote )
{
//if there is no memory allocated
if( gpNPCQuoteInfoArray[ cnt ] == NULL )
{
//allocate memory for the quote
gpNPCQuoteInfoArray[ cnt ] = (NPCQuoteInfo *)MemAlloc( sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS );
if( gpNPCQuoteInfoArray[ cnt ] == NULL )
return( FALSE );
memset( gpNPCQuoteInfoArray[ cnt ], 0, sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS );
}
//Load the NPC quote entry
numBytesRead = 0;
for(UINT32 cnt2 = 0; cnt2 < NUM_NPC_QUOTE_RECORDS; cnt2++)
{
if(guiCurrentSaveGameVersion >= STOMP12_SAVEGAME_DATATYPE_CHANGE)
{
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].fFlags, sizeof(gpNPCQuoteInfoArray[ cnt ]->fFlags), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].sRequiredItem, sizeof(gpNPCQuoteInfoArray[ cnt ]->sRequiredItem), sizeof(INT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usFactMustBeTrue, sizeof(gpNPCQuoteInfoArray[ cnt ]->usFactMustBeTrue), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usFactMustBeFalse, sizeof(gpNPCQuoteInfoArray[ cnt ]->usFactMustBeFalse), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubQuest, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubQuest), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubFirstDay, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubFirstDay), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubLastDay, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubLastDay), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubApproachRequired, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubApproachRequired), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubOpinionRequired, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubOpinionRequired), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubQuoteNum, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubQuoteNum), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubNumQuotes, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubNumQuotes), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubStartQuest, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubStartQuest), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubEndQuest, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubEndQuest), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubTriggerNPC, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubTriggerNPC), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubTriggerNPCRec, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubTriggerNPCRec), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubFiller, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubFiller), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usSetFactTrue, sizeof(gpNPCQuoteInfoArray[ cnt ]->usSetFactTrue), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usGiftItem, sizeof(gpNPCQuoteInfoArray[ cnt ]->usGiftItem), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usUnused, sizeof(gpNPCQuoteInfoArray[ cnt ]->usUnused), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].sActionData, sizeof(gpNPCQuoteInfoArray[ cnt ]->sActionData), sizeof(INT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].sRequiredGridNo, sizeof(gpNPCQuoteInfoArray[ cnt ]->sRequiredGridNo), sizeof(INT32), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usGoToGridNo, sizeof(gpNPCQuoteInfoArray[ cnt ]->usGoToGridNo), sizeof(INT32), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usSourceDialogueLevel, sizeof(gpNPCQuoteInfoArray[ cnt ]->usSourceDialogueLevel), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usDestDialogueLevel, sizeof(gpNPCQuoteInfoArray[ cnt ]->usDestDialogueLevel), sizeof(UINT16), numBytesRead);
}
else
{
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].fFlags, sizeof(gpNPCQuoteInfoArray[ cnt ]->fFlags), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].sRequiredItem, sizeof(gpNPCQuoteInfoArray[ cnt ]->sRequiredItem), sizeof(INT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].sRequiredGridNo, sizeof(gpNPCQuoteInfoArray[ cnt ]->sRequiredGridNo), sizeof(INT32), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usFactMustBeTrue, sizeof(gpNPCQuoteInfoArray[ cnt ]->usFactMustBeTrue), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usFactMustBeFalse, sizeof(gpNPCQuoteInfoArray[ cnt ]->usFactMustBeFalse), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubQuest, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubQuest), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubFirstDay, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubFirstDay), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubLastDay, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubLastDay), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubApproachRequired, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubApproachRequired), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubOpinionRequired, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubOpinionRequired), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &padding, sizeof(padding), sizeof(UINT8), numBytesRead); //previous ubUnused no longer exists
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubQuoteNum, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubQuoteNum), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubNumQuotes, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubNumQuotes), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubStartQuest, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubStartQuest), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubEndQuest, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubEndQuest), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubTriggerNPC, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubTriggerNPC), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].ubTriggerNPCRec, sizeof(gpNPCQuoteInfoArray[ cnt ]->ubTriggerNPCRec), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usSetFactTrue, sizeof(gpNPCQuoteInfoArray[ cnt ]->usSetFactTrue), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usGiftItem, sizeof(gpNPCQuoteInfoArray[ cnt ]->usGiftItem), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].sActionData, sizeof(gpNPCQuoteInfoArray[ cnt ]->sActionData), sizeof(INT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpNPCQuoteInfoArray[ cnt ][cnt2].usGoToGridNo, sizeof(gpNPCQuoteInfoArray[ cnt ]->usGoToGridNo), sizeof(INT32), numBytesRead);
}
}
if(guiCurrentSaveGameVersion >= STOMP12_SAVEGAME_DATATYPE_CHANGE)
{
while(numBytesRead != (sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS) )
numBytesRead = ReadFieldByField(hFile, &padding, sizeof(padding), sizeof(INT8), numBytesRead);
}
else
{
while(numBytesRead != 1800 )
numBytesRead = ReadFieldByField(hFile, &padding, sizeof(padding), sizeof(INT8), numBytesRead);
numBytesRead += 200;
}
//FileRead( hFile, gpNPCQuoteInfoArray[ cnt ], sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS, &uiNumBytesRead );
//if( uiNumBytesRead != sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS )
if( numBytesRead != sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS )
{
return( FALSE );
}
}
else
{
}
}
if ( uiSaveGameVersion >= 56 )
{
for( cnt = 0; cnt < NUM_CIVQUOTE_SECTORS; cnt++)
{
FileRead( hFile, &ubLoadQuote, sizeof( UINT8 ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( UINT8 ) )
{
return( FALSE );
}
//if there is an existing quote
if( gpCivQuoteInfoArray[ cnt ] )
{
//delete it
MemFree( gpCivQuoteInfoArray[ cnt ] );
gpCivQuoteInfoArray[ cnt ] = NULL;
}
// if there is a civ quote file
if( ubLoadQuote )
{
//if there is no memory allocated
if( gpCivQuoteInfoArray[ cnt ] == NULL )
{
//allocate memory for the quote
gpCivQuoteInfoArray[ cnt ] = (NPCQuoteInfo *)MemAlloc( sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS );
if( gpCivQuoteInfoArray[ cnt ] == NULL )
return( FALSE );
memset( gpCivQuoteInfoArray[ cnt ], 0, sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS );
}
//Load the civ quote entry
FileRead( hFile, gpCivQuoteInfoArray[ cnt ], sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS, &uiNumBytesRead );
if( uiNumBytesRead != sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS )
{
return( FALSE );
}
}
}
}
if ( uiSaveGameVersion < 88 )
{
RefreshNPCScriptRecord( NO_PROFILE, 5 ); // special pass-in value for "replace PC scripts"
RefreshNPCScriptRecord( DARYL, 11 );
RefreshNPCScriptRecord( DARYL, 14 );
RefreshNPCScriptRecord( DARYL, 15 );
}
if ( uiSaveGameVersion < 89 )
{
RefreshNPCScriptRecord( KINGPIN, 23 );
RefreshNPCScriptRecord( KINGPIN, 27 );
}
if ( uiSaveGameVersion < 90 )
{
RefreshNPCScriptRecord( KINGPIN, 25 );
RefreshNPCScriptRecord( KINGPIN, 26 );
}
if ( uiSaveGameVersion < 92 )
{
RefreshNPCScriptRecord( MATT, 14 );
#ifdef JA2UB
//no Ub
#else
RefreshNPCScriptRecord( AUNTIE, 8 );
#endif
}
if ( uiSaveGameVersion < 93 )
{
RefreshNPCScriptRecord( JENNY, 7 );
RefreshNPCScriptRecord( JENNY, 8 );
RefreshNPCScriptRecord( FRANK, 7 );
RefreshNPCScriptRecord( FRANK, 8 );
RefreshNPCScriptRecord( FATHER, 12 );
RefreshNPCScriptRecord( FATHER, 13 );
}
if ( uiSaveGameVersion < 94 )
{
RefreshNPCScriptRecord( CONRAD, 0 );
RefreshNPCScriptRecord( CONRAD, 2 );
RefreshNPCScriptRecord( CONRAD, 9 );
}
if ( uiSaveGameVersion < 95 )
{
RefreshNPCScriptRecord( WALDO, 6 );
RefreshNPCScriptRecord( WALDO, 7 );
RefreshNPCScriptRecord( WALDO, 10 );
RefreshNPCScriptRecord( WALDO, 11 );
RefreshNPCScriptRecord( WALDO, 12 );
}
if ( uiSaveGameVersion < 96 )
{
RefreshNPCScriptRecord( HANS, 18 );
RefreshNPCScriptRecord( ARMAND, 13 );
RefreshNPCScriptRecord( DARREN, 4 );
RefreshNPCScriptRecord( DARREN, 5 );
}
if ( uiSaveGameVersion < 97 )
{
RefreshNPCScriptRecord( JOHN, 22 );
RefreshNPCScriptRecord( JOHN, 23 );
RefreshNPCScriptRecord( SKYRIDER, 19 );
RefreshNPCScriptRecord( SKYRIDER, 21 );
RefreshNPCScriptRecord( SKYRIDER, 22 );
}
if ( uiSaveGameVersion < 98 )
{
RefreshNPCScriptRecord( SKYRIDER, 19 );
RefreshNPCScriptRecord( SKYRIDER, 21 );
RefreshNPCScriptRecord( SKYRIDER, 22 );
}
return( TRUE );
}
BOOLEAN SaveBackupNPCInfoToSaveGameFile( HWFILE hFile )
{
UINT32 uiNumBytesWritten=0;
UINT32 cnt;
UINT8 ubOne = 1;
UINT8 ubZero = 0;
//Loop through all the NPC quotes
for( cnt=0; cnt<NUM_PROFILES; cnt++)
{
//if there is a npc qutoe
if( gpBackupNPCQuoteInfoArray[ cnt ] )
{
//save a byte specify that there is an npc quote saved
FileWrite( hFile, &ubOne, sizeof( UINT8 ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( UINT8 ) )
{
return( FALSE );
}
//Save the NPC quote entry
FileWrite( hFile, gpBackupNPCQuoteInfoArray[ cnt ], sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS, &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS )
{
return( FALSE );
}
}
else
{
//save a byte specify that there is an npc quote saved
FileWrite( hFile, &ubZero, sizeof( UINT8 ), &uiNumBytesWritten );
if( uiNumBytesWritten != sizeof( UINT8 ) )
{
return( FALSE );
}
}
}
return( TRUE );
}
BOOLEAN LoadBackupNPCInfoFromSavedGameFile( HWFILE hFile, UINT32 uiSaveGameVersion )
{
UINT32 uiNumBytesRead=0;
UINT32 cnt;
UINT8 ubLoadQuote=0;
UINT32 uiNumberOfProfilesToLoad=0;
INT32 numBytesRead = 0;
INT8 padding;
if( guiCurrentSaveGameVersion >= STOMP12_SAVEGAME_DATATYPE_CHANGE )
uiNumberOfProfilesToLoad = NUM_PROFILES;
else
uiNumberOfProfilesToLoad = NUM_PROFILES_v111;
//Loop through all the NPC quotes
for( cnt=0; cnt<uiNumberOfProfilesToLoad; cnt++ )
{
//Load a byte specify that there is an npc quote Loadd
FileRead( hFile, &ubLoadQuote, sizeof( UINT8 ), &uiNumBytesRead );
if( uiNumBytesRead != sizeof( UINT8 ) )
{
return( FALSE );
}
//if there is an existing quote
if( gpBackupNPCQuoteInfoArray[ cnt ] )
{
//delete it
MemFree( gpBackupNPCQuoteInfoArray[ cnt ] );
gpBackupNPCQuoteInfoArray[ cnt ] = NULL;
}
//if there is a npc quote
if( ubLoadQuote )
{
//if there is no memory allocated
if( gpBackupNPCQuoteInfoArray[ cnt ] == NULL )
{
//allocate memory for the quote
gpBackupNPCQuoteInfoArray[ cnt ] = (NPCQuoteInfo *)MemAlloc( sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS );
if( gpBackupNPCQuoteInfoArray[ cnt ] == NULL )
return( FALSE );
memset( gpBackupNPCQuoteInfoArray[ cnt ], 0, sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS );
}
//Load the NPC quote entry
numBytesRead = 0;
if(guiCurrentSaveGameVersion >= STOMP12_SAVEGAME_DATATYPE_CHANGE)
{
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->fFlags, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->fFlags), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->sRequiredItem, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->sRequiredItem), sizeof(INT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usFactMustBeTrue, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usFactMustBeTrue), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usFactMustBeFalse, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usFactMustBeFalse), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubQuest, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubQuest), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubFirstDay, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubFirstDay), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubLastDay, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubLastDay), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubApproachRequired, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubApproachRequired), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubOpinionRequired, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubOpinionRequired), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubQuoteNum, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubQuoteNum), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubNumQuotes, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubNumQuotes), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubStartQuest, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubStartQuest), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubEndQuest, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubEndQuest), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubTriggerNPC, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubTriggerNPC), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubTriggerNPCRec, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubTriggerNPCRec), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubFiller, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubFiller), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usSetFactTrue, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usSetFactTrue), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usGiftItem, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usGiftItem), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usUnused, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usUnused), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->sActionData, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->sActionData), sizeof(INT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->sRequiredGridNo, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->sRequiredGridNo), sizeof(INT32), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usGoToGridNo, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usGoToGridNo), sizeof(INT32), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usSourceDialogueLevel, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usSourceDialogueLevel), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usDestDialogueLevel, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usDestDialogueLevel), sizeof(UINT16), numBytesRead);
while(numBytesRead != (sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS) )
numBytesRead = ReadFieldByField(hFile, &padding, sizeof(padding), sizeof(INT8), numBytesRead);
}
else
{
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->fFlags, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->fFlags), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->sRequiredItem, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->sRequiredItem), sizeof(INT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->sRequiredGridNo, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->sRequiredGridNo), sizeof(INT32), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usFactMustBeTrue, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usFactMustBeTrue), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usFactMustBeFalse, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usFactMustBeFalse), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubQuest, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubQuest), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubFirstDay, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubFirstDay), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubLastDay, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubLastDay), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubApproachRequired, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubApproachRequired), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubOpinionRequired, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubOpinionRequired), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &padding, sizeof(padding), sizeof(UINT8), numBytesRead); //previous ubUnused no longer exists
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubQuoteNum, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubQuoteNum), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubNumQuotes, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubNumQuotes), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubStartQuest, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubStartQuest), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubEndQuest, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubEndQuest), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubTriggerNPC, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubTriggerNPC), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->ubTriggerNPCRec, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->ubTriggerNPCRec), sizeof(UINT8), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usSetFactTrue, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usSetFactTrue), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usGiftItem, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usGiftItem), sizeof(UINT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->sActionData, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->sActionData), sizeof(INT16), numBytesRead);
numBytesRead = ReadFieldByField(hFile, &gpBackupNPCQuoteInfoArray[ cnt ]->usGoToGridNo, sizeof(gpBackupNPCQuoteInfoArray[ cnt ]->usGoToGridNo), sizeof(INT32), numBytesRead);
while(numBytesRead != 1800 )
numBytesRead = ReadFieldByField(hFile, &padding, sizeof(padding), sizeof(INT8), numBytesRead);
numBytesRead += 200;
}
//FileRead( hFile, gpBackupNPCQuoteInfoArray[ cnt ], sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS, &uiNumBytesRead );
//if( uiNumBytesRead != sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS )
if( numBytesRead != sizeof( NPCQuoteInfo ) * NUM_NPC_QUOTE_RECORDS )
{
return( FALSE );
}
}
else
{
}
}
return( TRUE );
}
void TriggerFriendWithHostileQuote( UINT8 ubNPC )
{
UINT8 ubMercsAvailable[ 40 ] = { 0 };
UINT8 ubNumMercsAvailable = 0, ubChosenMerc;
SOLDIERTYPE * pTeamSoldier;
SOLDIERTYPE * pSoldier;
INT32 cnt;
INT8 bTeam;
// First get pointer to NPC
pSoldier = FindSoldierByProfileID( ubNPC, FALSE );
if (!pSoldier)
{
return;
}
bTeam = pSoldier->bTeam;
// Loop through all our guys and find one to yell
// set up soldier ptr as first element in mercptrs list
cnt = gTacticalStatus.Team[ bTeam ].bFirstID;
// run through list
for ( pTeamSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ bTeam ].bLastID; cnt++,pTeamSoldier++ )
{
// Add guy if he's a candidate...
if ( pTeamSoldier->bActive && pSoldier->bInSector && pTeamSoldier->stats.bLife >= OKLIFE && pTeamSoldier->bBreath >= OKBREATH && pTeamSoldier->aiData.bOppCnt > 0 && pTeamSoldier->ubProfile != NO_PROFILE )
{
if ( bTeam == CIV_TEAM && pSoldier->ubCivilianGroup != NON_CIV_GROUP && pTeamSoldier->ubCivilianGroup != pSoldier->ubCivilianGroup )
{
continue;
}
if ( ! ( gMercProfiles[ pTeamSoldier->ubProfile ].ubMiscFlags & PROFILE_MISC_FLAG_SAID_HOSTILE_QUOTE ) )
{
ubMercsAvailable[ ubNumMercsAvailable ] = pTeamSoldier->ubProfile;
ubNumMercsAvailable++;
}
}
}
if ( bTeam == CIV_TEAM && pSoldier->ubCivilianGroup != NON_CIV_GROUP && gTacticalStatus.fCivGroupHostile[ pSoldier->ubCivilianGroup ] == CIV_GROUP_NEUTRAL )
{
CivilianGroupMemberChangesSides( pSoldier );
}
if (ubNumMercsAvailable > 0)
{
PauseAITemporarily();
ubChosenMerc = (UINT8) Random( ubNumMercsAvailable );
TriggerNPCWithIHateYouQuote( ubMercsAvailable[ ubChosenMerc ] );
}
else
{
// done... we should enter combat mode with this soldier's team starting,
// after all the dialogue is completed
NPCDoAction( ubNPC, NPC_ACTION_ENTER_COMBAT, 0 );
}
}
UINT8 ActionIDForMovementRecord( UINT8 ubNPC, UINT8 ubRecord )
{
// Check if we have a quote to trigger...
NPCQuoteInfo * pNPCQuoteInfoArray;
NPCQuoteInfo *pQuotePtr;
BOOLEAN fDisplayDialogue = TRUE;
if ( EnsureQuoteFileLoaded( ubNPC ) == FALSE )
{
// error!!!
return( FALSE );
}
pNPCQuoteInfoArray = gpNPCQuoteInfoArray[ ubNPC ];
pQuotePtr = &( pNPCQuoteInfoArray[ ubRecord ] );
switch( pQuotePtr->sActionData )
{
case NPC_ACTION_TRAVERSE_MAP_EAST:
return( QUOTE_ACTION_ID_TRAVERSE_EAST );
case NPC_ACTION_TRAVERSE_MAP_SOUTH:
return( QUOTE_ACTION_ID_TRAVERSE_SOUTH );
case NPC_ACTION_TRAVERSE_MAP_WEST:
return( QUOTE_ACTION_ID_TRAVERSE_WEST );
case NPC_ACTION_TRAVERSE_MAP_NORTH:
return( QUOTE_ACTION_ID_TRAVERSE_NORTH );
default:
return( QUOTE_ACTION_ID_CHECKFORDEST );
}
}
void HandleNPCChangesForTacticalTraversal( SOLDIERTYPE * pSoldier )
{
if ( !pSoldier || pSoldier->ubProfile == NO_PROFILE || (pSoldier->aiData.fAIFlags & AI_CHECK_SCHEDULE) )
{
return;
}
switch( pSoldier->ubQuoteActionID )
{
case QUOTE_ACTION_ID_TRAVERSE_EAST:
gMercProfiles[pSoldier->ubProfile].sSectorX++;
// Call to change the NPC's Sector Location
ChangeNpcToDifferentSector( pSoldier->ubProfile,
gMercProfiles[pSoldier->ubProfile].sSectorX,
gMercProfiles[pSoldier->ubProfile].sSectorY,
gMercProfiles[pSoldier->ubProfile].bSectorZ );
break;
case QUOTE_ACTION_ID_TRAVERSE_SOUTH:
gMercProfiles[pSoldier->ubProfile].sSectorY++;
// Call to change the NPC's Sector Location
ChangeNpcToDifferentSector( pSoldier->ubProfile,
gMercProfiles[pSoldier->ubProfile].sSectorX,
gMercProfiles[pSoldier->ubProfile].sSectorY,
gMercProfiles[pSoldier->ubProfile].bSectorZ );
break;
case QUOTE_ACTION_ID_TRAVERSE_WEST:
gMercProfiles[pSoldier->ubProfile].sSectorX--;
// Call to change the NPC's Sector Location
ChangeNpcToDifferentSector( pSoldier->ubProfile,
gMercProfiles[pSoldier->ubProfile].sSectorX,
gMercProfiles[pSoldier->ubProfile].sSectorY,
gMercProfiles[pSoldier->ubProfile].bSectorZ );
break;
case QUOTE_ACTION_ID_TRAVERSE_NORTH:
gMercProfiles[pSoldier->ubProfile].sSectorY--;
// Call to change the NPC's Sector Location
ChangeNpcToDifferentSector( pSoldier->ubProfile,
gMercProfiles[pSoldier->ubProfile].sSectorX,
gMercProfiles[pSoldier->ubProfile].sSectorY,
gMercProfiles[pSoldier->ubProfile].bSectorZ );
break;
default:
break;
}
}
void HandleVictoryInNPCSector( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ )
{
// handle special cases of victory in certain sector
INT16 sSector = 0;
// not the surface?..leave
if( sSectorZ != 0 )
{
return;
}
// grab sector value
sSector = SECTOR( sSectorX, sSectorY );
switch( sSector )
{
case( SEC_F10 ):
{
// we won over the hillbillies
// set fact they are dead
if( CheckFact( FACT_HILLBILLIES_KILLED, KEITH ) == FALSE )
{
SetFactTrue( FACT_HILLBILLIES_KILLED );
}
// check if keith is out of business
if( CheckFact( FACT_KEITH_OUT_OF_BUSINESS, KEITH ) == TRUE )
{
SetFactFalse( FACT_KEITH_OUT_OF_BUSINESS );
}
}
}
return;
}
BOOLEAN HandleShopKeepHasBeenShutDown( UINT8 ubCharNum )
{
// check if shopkeep has been shutdown, if so handle
switch( ubCharNum )
{
case( KEITH ):
{
// if keith out of business, do action and leave
if( CheckFact( FACT_KEITH_OUT_OF_BUSINESS, KEITH ) == TRUE )
{
TriggerNPCRecord( KEITH, 11 );
return( TRUE );
}
else if( CheckFact( FACT_LOYALTY_LOW, KEITH ) == TRUE )
{
// loyalty is too low
TriggerNPCRecord( KEITH, 7 );
return( TRUE );
}
}
}
return( FALSE );
}
#ifdef JA2BETAVERSION
void ToggleNPCRecordDisplay( void )
{
if ( gfDisplayScreenMsgOnRecordUsage )
{
gfDisplayScreenMsgOnRecordUsage = FALSE;
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, L"Turning record reporting OFF" );
}
else
{
gfDisplayScreenMsgOnRecordUsage = TRUE;
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_UI_FEEDBACK, L"Turning record reporting ON" );
}
}
#endif
void UpdateDarrelScriptToGoTo( SOLDIERTYPE * pSoldier )
{
// change destination in Darrel record 10 to go to a gridno adjacent to the
// soldier's gridno, and destination in record 11
INT32 sAdjustedGridNo;
UINT8 ubDummyDirection;
SOLDIERTYPE * pDarrel;
pDarrel = FindSoldierByProfileID( DARREL, FALSE );
if ( !pDarrel )
{
return;
}
// find a spot to an alternate location nearby
sAdjustedGridNo = FindGridNoFromSweetSpotExcludingSweetSpot( pDarrel, pSoldier->sGridNo, 5, &ubDummyDirection );
if (TileIsOutOfBounds(sAdjustedGridNo))
{
// yikes! try again with a bigger radius!
sAdjustedGridNo = FindGridNoFromSweetSpotExcludingSweetSpot( pDarrel, pSoldier->sGridNo, 10, &ubDummyDirection );
if (TileIsOutOfBounds(sAdjustedGridNo))
{
// ok, now we're completely foobar
return;
}
}
EnsureQuoteFileLoaded( DARREL );
gpNPCQuoteInfoArray[ DARREL ][ 10 ].usGoToGridNo = sAdjustedGridNo;
gpNPCQuoteInfoArray[ DARREL ][ 11 ].sRequiredGridNo = sAdjustedGridNo;//dnl ch46 031009
gpNPCQuoteInfoArray[ DARREL ][ 11 ].ubTriggerNPC = pSoldier->ubProfile;
}
BOOLEAN RecordHasDialogue( UINT8 ubNPC, UINT8 ubRecord )
{
if (EnsureQuoteFileLoaded( ubNPC ) == FALSE)
{
// error!!!
return( FALSE );
}
if ( gpNPCQuoteInfoArray[ ubNPC ][ ubRecord ].ubQuoteNum != NO_QUOTE && gpNPCQuoteInfoArray[ ubNPC ][ ubRecord ].ubQuoteNum != 0 )
{
return( TRUE );
}
else
{
return( FALSE );
}
}
INT8 FindCivQuoteFileIndex( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ )
{
UINT8 ubLoop;
if ( sSectorZ > 0 )
{
return( MINERS_CIV_QUOTE_INDEX );
}
else
{
for ( ubLoop = 0; ubLoop < NUM_CIVQUOTE_SECTORS; ubLoop++ )
{
if ( gsCivQuoteSector[ ubLoop ][ 0 ] == sSectorX && gsCivQuoteSector[ ubLoop ][ 1 ] == sSectorY )
{
return( ubLoop );
}
}
}
return( -1 );
}
INT8 ConsiderCivilianQuotes( INT16 sSectorX, INT16 sSectorY, INT16 sSectorZ, BOOLEAN fSetAsUsed )
{
INT8 bLoop, bCivQuoteSectorIndex;
NPCQuoteInfo * pCivQuoteInfoArray;
bCivQuoteSectorIndex = FindCivQuoteFileIndex( sSectorX, sSectorY, sSectorZ );
if ( bCivQuoteSectorIndex == -1 )
{
// no hints for this sector!
return( -1 );
}
if ( EnsureCivQuoteFileLoaded( bCivQuoteSectorIndex ) == FALSE )
{
// error!!!
return( -1 );
}
pCivQuoteInfoArray = gpCivQuoteInfoArray[ bCivQuoteSectorIndex ];
for ( bLoop = 0; bLoop < NUM_NPC_QUOTE_RECORDS; bLoop++ )
{
if ( NPCConsiderQuote( NO_PROFILE, NO_PROFILE, 0, bLoop, 0, pCivQuoteInfoArray ) )
{
if ( fSetAsUsed )
{
TURN_FLAG_ON( pCivQuoteInfoArray[ bLoop ].fFlags, QUOTE_FLAG_SAID );
}
if (pCivQuoteInfoArray[ bLoop ].ubStartQuest != NO_QUEST)
{
StartQuest( pCivQuoteInfoArray[ bLoop ].ubStartQuest, gWorldSectorX, gWorldSectorY );
}
// return quote #
return( pCivQuoteInfoArray[ bLoop ].ubQuoteNum );
}
}
return( -1 );
}
#ifdef JA2UB
//UB
BOOLEAN HasNpcSaidQuoteBefore( UINT8 ubNPC, UINT8 ubRecord )
{
if( CHECK_FLAG( gpNPCQuoteInfoArray[ ubNPC ][ ubRecord ].fFlags, QUOTE_FLAG_SAID ) )
return( TRUE );
else
return( FALSE );
}
#endif