mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
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
3852 lines
120 KiB
C++
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
|