#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 "soldier macros.h" #include "dialogue control.h" #include "Strategic Town Loyalty.h" #include "message.h" #include "Timer Control.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" #include "Soldier Profile.h" #include "BriefingRoom_Data.h" #include "Map Screen Helicopter.h" #include "Drugs And Alcohol.h" // added by Flugente for DoesMercHavePersonality( ... ) #include "SaveLoadGame.h" #ifdef JA2UB #include "Ja25_Tactical.h" #include "Ja25_Tactical.h" #include "ub_config.h" #endif //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 ) { sprintf( zFileName, "NPCData\\%03d.npc", ubNPC ); if ( !FileExists( zFileName ) ) { // use a copy of Herve's data file instead! sprintf( zFileName, "NPCData\\%03d.npc", HERVE ); } } #ifdef JA2UB if ( ubNPC == MANUEL_UB|| ubNPC == BIGGENS_UB || ubNPC == JOHN_K_UB || ubNPC == TEX_UB || ubNPC == GASTON_UB || ubNPC == STOGIE_UB || ubNPC == JERRY_MILO_UB || ubNPC == PGMALE4_UB || ubNPC == BETTY_UB || ubNPC == RAUL_UB || ubNPC == MORRIS_UB || ubNPC == RUDY_UB ) { sprintf( zFileName, "NPCData\\%03d.npc", ubNPC ); } #endif else if ( gMercProfiles[ubNPC].Type == PROFILETYPE_AIM || gMercProfiles[ubNPC].Type == PROFILETYPE_MERC || (gMercProfiles[ubNPC].Type == PROFILETYPE_RPC && gMercProfiles[ubNPC].ubMiscFlags & PROFILE_MISC_FLAG_RECRUITED ) || gMercProfiles[ubNPC].Type == PROFILETYPE_IMP ) { sprintf( zFileName, "NPCData\\000.npc" ); } 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.... else 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! // 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 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 ( gMercProfiles[ubNPC].Type == PROFILETYPE_RPC ) { 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 { if ( gMercProfiles[ubLoop].Type == PROFILETYPE_AIM || gMercProfiles[ubLoop].Type == PROFILETYPE_MERC || gMercProfiles[ubLoop].Type == PROFILETYPE_IMP ) RefreshNPCScriptRecord( ubLoop, ubRecord ); } //new profiles by Jazz //for ( ubLoop = FIRST_RPC; ubLoop < FIRST_NPC; ubLoop++ ) for ( ubLoop = 0; ubLoop < NUM_PROFILES; ++ubLoop ) { if ( gMercProfiles[ubLoop].Type == PROFILETYPE_RPC ) { 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 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 ) { for ( UINT8 ubProfile = 0; ubProfile < NUM_PROFILES; ubProfile++ ) { if ( gMercProfiles[ubProfile].Type == PROFILETYPE_RPC || gMercProfiles[ubProfile].Type == PROFILETYPE_NPC ) { // zap backup if any if ( gpBackupNPCQuoteInfoArray[ ubProfile ] != NULL ) { MemFree( gpBackupNPCQuoteInfoArray[ ubProfile ] ); gpBackupNPCQuoteInfoArray[ ubProfile ] = NULL; } ReloadQuoteFileIfLoaded( ubProfile ); } } // reload all civ quote files for ( UINT8 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, TRUE ); 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 && DoesMercHavePersonality( pSoldier, CHAR_TRAIT_ASSERTIVE ) ) { iDeadliness += 50; } return( (EffectiveLeadership( pSoldier ) + iStrength + iDeadliness) / 3 ); //Trail minor bug fix. } INT32 GetEffectiveApproachValue( UINT8 usProfile, UINT8 usApproach, CHAR16* apStr ) { if ( usApproach > APPROACH_RECRUIT ) return 100; FLOAT val = 0; CHAR16 atStr[500]; swprintf( atStr, L"" ); if ( usApproach == APPROACH_THREATEN ) { INT32 threateneffectiveness = CalcThreateningEffectiveness( usProfile ); if ( apStr ) { swprintf( atStr, szLaptopStatText[LAPTOP_STAT_TEXT_THREATEN_EFFECTIVENESS], threateneffectiveness ); wcscat( apStr, atStr ); } val = threateneffectiveness; } else { if ( apStr ) { swprintf( atStr, szLaptopStatText[LAPTOP_STAT_TEXT_LEADERSHIP], gMercProfiles[usProfile].bLeadership ); wcscat( apStr, atStr ); } val = ((INT32)gMercProfiles[usProfile].bLeadership); } FLOAT approachfactor = (FLOAT)(gMercProfiles[usProfile].usApproachFactor[usApproach - 1]) / 100.0f; if ( apStr ) { swprintf( atStr, szLaptopStatText[LAPTOP_STAT_TEXT_APPROACH_MODIFIER], approachfactor ); wcscat( apStr, atStr ); } val *= approachfactor; // Flugente: backgrounds SOLDIERTYPE* pSoldier = FindSoldierByProfileID( usProfile, TRUE ); if ( pSoldier ) { UINT8 bgprperty = 0; if ( usApproach == APPROACH_FRIENDLY ) bgprperty = BG_PERC_APPROACH_FRIENDLY; else if ( usApproach == APPROACH_DIRECT ) bgprperty = BG_PERC_APPROACH_DIRECT; else if ( usApproach == APPROACH_THREATEN ) bgprperty = BG_PERC_APPROACH_THREATEN; else if ( usApproach == APPROACH_RECRUIT ) bgprperty = BG_PERC_APPROACH_RECRUIT; FLOAT bgmodifier = (FLOAT)((100.0f + pSoldier->GetBackgroundValue( bgprperty ))) / 100.0f; if ( apStr ) { swprintf( atStr, szLaptopStatText[LAPTOP_STAT_TEXT_BACKGROUND_MODIFIER], bgmodifier ); wcscat( apStr, atStr ); } val = val * bgmodifier; if ( apStr && gGameOptions.fNewTraitSystem && usApproach != APPROACH_THREATEN ) { if ( DoesMercHavePersonality( pSoldier, CHAR_TRAIT_ASSERTIVE ) ) { swprintf( atStr, L" \n" ); wcscat( apStr, atStr ); swprintf( atStr, szLaptopStatText[LAPTOP_STAT_TEXT_ASSERTIVE] ); wcscat( apStr, atStr ); } else if ( DoesMercHavePersonality( pSoldier, CHAR_TRAIT_MALICIOUS ) ) { swprintf( atStr, L" \n" ); wcscat( apStr, atStr ); swprintf( atStr, szLaptopStatText[LAPTOP_STAT_TEXT_MALICIOUS] ); wcscat( apStr, atStr ); } } } return (INT32)val; } 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) { INT16 id = GetSoldierIDFromMercID( ubMerc ); if ( id > -1 ) { if ( DoesMercHavePersonality( MercPtrs[id], CHAR_TRAIT_ASSERTIVE ) ) iPersonalVal += 50; else if ( DoesMercHavePersonality( MercPtrs[id], 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 { iEffectiveLeadership = GetEffectiveApproachValue( ubMerc, bApproach, NULL ); 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 && !ItemIsRocketLauncher(usItemToConsider) ) { 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 ); } // anv: Waldo! if ((*pObj)[0]->data.money.uiMoneyAmount >= CalculateHelicopterRepairCost( FALSE ) ) { SetFactTrue( FACT_GIVEN_ENOUGH_TO_REPAIR_HELI ); } if ((*pObj)[0]->data.money.uiMoneyAmount >= CalculateHelicopterRepairCost( TRUE ) ) { SetFactTrue( FACT_GIVEN_ENOUGH_TO_SERIOUSLY_REPAIR_HELI ); } 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; //DBrot: More Rooms //UINT8 ubKingpinRoom; UINT16 usKingpinRoom; pKingpin = FindSoldierByProfileID( KINGPIN, FALSE ); if ( pKingpin && InARoom( pKingpin->sGridNo, &usKingpinRoom ) ) { if ( IN_KINGPIN_HOUSE( usKingpinRoom ) ) { // 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 || gMercProfiles[ubNPC].Type == PROFILETYPE_RPC ) ) { 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 ] ); DebugQuestInfo(String("reset QUOTE_FLAG_SAID for profile %d", ubNPC)); } void ResetOncePerConvoRecordsForAllNPCsInLoadedSector( void ) { if ( gWorldSectorX == 0 || gWorldSectorY == 0 ) { return; } for ( UINT8 IDnpc = 0; IDnpc < NUM_PROFILES; ++IDnpc ) { if ( gMercProfiles[IDnpc].Type == PROFILETYPE_RPC || gMercProfiles[IDnpc].Type == PROFILETYPE_NPC ) { if ( gMercProfiles[IDnpc].sSectorX == gWorldSectorX && gMercProfiles[IDnpc].sSectorY == gWorldSectorY && gMercProfiles[IDnpc].bSectorZ == gbWorldSectorZ && gpNPCQuoteInfoArray[IDnpc] != NULL ) { ResetOncePerConvoRecordsForNPC( IDnpc ); } } } } 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; DebugQuestInfo(String("Converse: merc <%d> to NPC <%d> approach %d data %d", ubMerc, ubNPC, bApproach, uiApproachData)); // 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 DebugQuestInfo(String("ResetOncePerConvoRecords for <%d>", ubNPC)); 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 ); DebugQuestInfo(String("reset QUOTE_FLAG_SAID for <%d> 'DARREN'", ubNPC)); } // 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: // Flugente: additional dialogue if ( bApproach == APPROACH_FRIENDLY ) AdditionalTacticalCharacterDialogue_CallsLua( FindSoldierByProfileID( ubMerc, FALSE ), ADE_DIALOGUE_NPC_FRIENDLY, ubNPC ); else if ( bApproach == APPROACH_DIRECT ) AdditionalTacticalCharacterDialogue_CallsLua( FindSoldierByProfileID( ubMerc, FALSE ), ADE_DIALOGUE_NPC_DIRECT, ubNPC ); else if ( bApproach == APPROACH_THREATEN ) AdditionalTacticalCharacterDialogue_CallsLua( FindSoldierByProfileID( ubMerc, FALSE ), ADE_DIALOGUE_NPC_THREATEN, ubNPC ); else if ( bApproach == APPROACH_RECRUIT ) AdditionalTacticalCharacterDialogue_CallsLua( FindSoldierByProfileID( ubMerc, FALSE ), ADE_DIALOGUE_NPC_RECRUIT, ubNPC ); else if ( bApproach == APPROACH_REPEAT ) AdditionalTacticalCharacterDialogue_CallsLua( FindSoldierByProfileID( ubMerc, FALSE ), ADE_DIALOGUE_NPC_REPEAT, ubNPC ); 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 DebugAI(AI_MSG_INFO, pNPC, String("CancelAIAction: NPC coverse")); 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 ); if (pSoldier) { // stupid hack CC if (ubNPC == KYLE) { // make sure he has keys pSoldier->flags.bHasKeys = TRUE; } if (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) // FIXME: Dereferencing null pointer { //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; DebugQuestInfo(String("TriggerNPCRecord: <%d> record %d", ubTriggerNPC, ubTriggerNPCRec)); 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; DebugQuestInfo(String("TriggerNPCRecordImmediately: <%d> record %d", ubTriggerNPC, ubTriggerNPCRec)); 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= 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= 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 ); } // anv: if ( uiSaveGameVersion < WALDO_CAN_REPAIR_HELICOPTER ) { RefreshNPCScriptRecord( WALDO, 5 ); RefreshNPCScriptRecord( WALDO, 6 ); RefreshNPCScriptRecord( WALDO, 7 ); RefreshNPCScriptRecord( WALDO, 13 ); RefreshNPCScriptRecord( WALDO, 14 ); RefreshNPCScriptRecord( WALDO, 15 ); RefreshNPCScriptRecord( WALDO, 16 ); RefreshNPCScriptRecord( WALDO, 17 ); RefreshNPCScriptRecord( WALDO, 18 ); } 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= STOMP12_SAVEGAME_DATATYPE_CHANGE ) uiNumberOfProfilesToLoad = NUM_PROFILES; else uiNumberOfProfilesToLoad = NUM_PROFILES_v111; //Loop through all the NPC quotes for( cnt=0; cnt= 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 // silversurfer: Only if Darren and Daryl are dead and we didn't just fight other opponents in their sector! if ( gMercProfiles[DARREL].bMercStatus == MERC_IS_DEAD && gMercProfiles[DARYL].bMercStatus == MERC_IS_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