diff --git a/GameVersion.cpp b/GameVersion.cpp index 0018daf1..a14a9bf6 100644 --- a/GameVersion.cpp +++ b/GameVersion.cpp @@ -15,9 +15,9 @@ #ifdef JA2EDITOR #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8507 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Unfinished Business - Map Editor v1.13.8516 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8507 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Map Editor v1.13.8516 (Development Build)" }; #endif // ------------------------------ @@ -27,11 +27,11 @@ //DEBUG BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8507 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: Unfinished Business - v1.13.8516 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8507 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: JA2 Demo - v1.13.8516 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8507 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Debug: v1.13.8516 (Development Build)" }; #endif #elif defined CRIPPLED_VERSION @@ -46,16 +46,16 @@ //RELEASE BUILD VERSION #ifdef JA2UB - CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8507 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release Unfinished Business - v1.13.8516 (Development Build)" }; #elif defined (JA113DEMO) - CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8507 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release JA2 Demo - v1.13.8516 (Development Build)" }; #else - CHAR16 zVersionLabel[256] = { L"Release v1.13.8507 (Development Build)" }; + CHAR16 zVersionLabel[256] = { L"Release v1.13.8516 (Development Build)" }; #endif #endif -CHAR8 czVersionNumber[16] = { "Build 17.11.08" }; //YY.MM.DD +CHAR8 czVersionNumber[16] = { "Build 18.01.10" }; //YY.MM.DD CHAR16 zTrackingNumber[16] = { L"Z" }; // SAVE_GAME_VERSION is defined in header, change it there diff --git a/Strategic/LuaInitNPCs.cpp b/Strategic/LuaInitNPCs.cpp index 610ee07a..8b481739 100644 --- a/Strategic/LuaInitNPCs.cpp +++ b/Strategic/LuaInitNPCs.cpp @@ -869,6 +869,9 @@ static int l_GetSamSiteHackStatus( lua_State *L ); static int l_SetMiniGameType( lua_State *L ); static int l_SoldierSpendMoney( lua_State *L ); +static int l_SetAdditionalDialogue( lua_State *L ); +static int l_PlaySound( lua_State *L ); + using namespace std; UINT16 idProfil; @@ -1744,6 +1747,9 @@ void IniFunction(lua_State *L, BOOLEAN bQuests ) lua_register( L, "SetMiniGameType", l_SetMiniGameType ); lua_register( L, "SoldierSpendMoney", l_SoldierSpendMoney ); + + lua_register( L, "SetAdditionalDialogue", l_SetAdditionalDialogue ); + lua_register( L, "PlaySound", l_PlaySound ); } #ifdef NEWMUSIC BOOLEAN LetLuaMusicControl(UINT8 Init) @@ -7208,6 +7214,9 @@ static int l_ACTION_ITEM_SEX (lua_State *L) DoorCloser[0]->data.misc.bActionValue = ACTION_ITEM_CLOSE_DOOR; PerformItemAction( sDoorSpot, &DoorCloser ); + // Flugente: additional dialogue + AdditionalTacticalCharacterDialogue_CallsLua( MercPtrs[ubID], ADE_SEX ); + // have sex HandleNPCDoAction( 0, NPC_ACTION_SEX, 0 ); @@ -10574,7 +10583,6 @@ static int l_CheckCharacterSectorX (lua_State *L) return 1; } -//SetPendingNewScreen( SEX_SCREEN ); static int l_SetPendingNewScreenSEXSCREEN (lua_State *L) { UINT8 n = lua_gettop(L); @@ -13360,3 +13368,67 @@ static int l_SoldierSpendMoney( lua_State *L ) return 1; } + +extern BOOLEAN LuaCallsToDoDialogueStuff( UINT8 ubProfile, INT32 iFaceIndex, const char* azSoundString ); + +extern void SetQuoteStr( STR16 aStr ); + +static int l_SetAdditionalDialogue( lua_State *L ) +{ + if ( lua_gettop( L ) >= 4 ) + { + UINT8 profile = lua_tointeger( L, 1 ); + INT32 faceindex = lua_tointeger( L, 2 ); + + size_t len = 0; + const char* str = lua_tolstring( L, 3, &len ); + + { + size_t len = 0; + const char* str = lua_tolstring( L, 4, &len ); + + CHAR16 quote_str[1000]; + + MultiByteToWideChar( CP_UTF8, 0, str, -1, quote_str, sizeof( quote_str ) / sizeof( quote_str[0] ) ); + quote_str[sizeof( quote_str ) / sizeof( quote_str[0] ) - 1] = '\0'; + + SetQuoteStr( quote_str ); + } + + LuaCallsToDoDialogueStuff( profile, faceindex, str ); + } + + return 0; +} + +static int l_PlaySound( lua_State *L ) +{ + if ( lua_gettop( L ) >= 1 ) + { + size_t len = 0; + const char* str = lua_tolstring( L, 1, &len ); + + UINT32 volume = min( lua_tointeger( L, 2 ), HIGHVOLUME ); + if ( !volume ) + volume = MIDVOLUME; + + PlayJA2SampleFromFile( (char*)str, RATE_11025, volume, 1, MIDDLEPAN ); + } + + return 0; +} + +void LuaHandleAdditionalDialogue( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, UINT8 ubProfile, INT32 iFaceIndex, UINT16 usEventNr, UINT32 aData1, UINT32 aData2, UINT32 aData3 ) +{ + const char* filename = "scripts\\Overhead.lua"; + + LuaScopeState _LS( true ); + + lua_register( _LS.L(), "CheckFact", l_CheckFact ); + IniFunction( _LS.L(), TRUE ); + IniGlobalGameSetting( _LS.L() ); + + SGP_THROW_IFFALSE( _LS.L.EvalFile( filename ), _BS( "Cannot open file: " ) << filename << _BS::cget ); + + LuaFunction( _LS.L, "HandleAdditionalDialogue" ).Param( sSectorX ).Param( sSectorY ).Param( bSectorZ ).Param( ubProfile ).Param( iFaceIndex ).Param( usEventNr ).Param( aData1 ).Param( aData2 ).Param( aData3 ).Call( 9 ); +} diff --git a/Strategic/LuaInitNPCs.h b/Strategic/LuaInitNPCs.h index 617d76c2..b7fa1851 100644 --- a/Strategic/LuaInitNPCs.h +++ b/Strategic/LuaInitNPCs.h @@ -91,4 +91,6 @@ extern BOOLEAN LuaIntro(UINT8 Init, UINT32 uiCurrentVideo, INT8 bIntroType, UINT extern BOOLEAN LuaIDScripts(UINT8 Init, UINT8 ubTargetNPC, UINT16 usActionCode, UINT8 ubQuoteNum); extern BOOLEAN LetLuaMusicControl(UINT8 Init); + +void LuaHandleAdditionalDialogue( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, UINT8 ubProfile, INT32 iFaceIndex, UINT16 usEventNr, UINT32 aData1, UINT32 aData2, UINT32 aData3 ); #endif diff --git a/Strategic/strategicmap.cpp b/Strategic/strategicmap.cpp index a9deb7de..51d7aa83 100644 --- a/Strategic/strategicmap.cpp +++ b/Strategic/strategicmap.cpp @@ -2106,43 +2106,57 @@ void HandleRPCDescriptionOfSector( INT16 sSectorX, INT16 sSectorY, INT16 sSector // Default to false gTacticalStatus.fCountingDownForGuideDescription = FALSE; - - + // OK, if the first time in... if ( GetSectorFlagStatus( sSectorX, sSectorY, (UINT8)sSectorZ, SF_HAVE_USED_GUIDE_QUOTE ) != TRUE ) { - if ( sSectorZ != 0 ) + // guide quotes only happen on the surface + if ( sSectorZ == 0 ) { - return; + // OK, check if we are in a good sector.... + for ( cnt = 0; cnt < 33; ++cnt ) + { + if ( sSectorX == ubSectorDescription[cnt][1] && sSectorY == ubSectorDescription[cnt][0] ) + { + if ( cnt == 3 && ( gGameOptions.ubGameStyle == STYLE_REALISTIC || !gGameExternalOptions.fEnableCrepitus ) ) + continue; + + /* + // If we're not scifi, skip some + if ( !(gGameOptions.ubGameStyle == STYLE_SCIFI && gGameExternalOptions.fEnableCrepitus) && cnt == 3 ) + { + continue; + } + */ + + gTacticalStatus.fCountingDownForGuideDescription = TRUE; + gTacticalStatus.bGuideDescriptionCountDown = (INT8)( 4 + Random( 5 ) ); // 4 to 8 tactical turns... + gTacticalStatus.ubGuideDescriptionToUse = ubSectorDescription[cnt][2]; + gTacticalStatus.bGuideDescriptionSectorX = (INT8)sSectorX; + gTacticalStatus.bGuideDescriptionSectorY = (INT8)sSectorY; + + break; + } + } } - // OK, check if we are in a good sector.... - for ( cnt = 0; cnt < 33; cnt++ ) + // Flugente: We abuse this mechanic to also set up lua-based comments. We only play those if there are no normal comments + if ( !gTacticalStatus.fCountingDownForGuideDescription ) { - if ( sSectorX == ubSectorDescription[cnt][1] && sSectorY == ubSectorDescription[cnt][0] ) - { - if ( (gGameOptions.ubGameStyle == STYLE_REALISTIC || !gGameExternalOptions.fEnableCrepitus) && cnt == 3 ) - continue; + gTacticalStatus.fCountingDownForGuideDescription = TRUE; + gTacticalStatus.bGuideDescriptionCountDown = (INT8)( 2 + Random( 3 ) ); - /* - // If we're not scifi, skip some - if ( !(gGameOptions.ubGameStyle == STYLE_SCIFI && gGameExternalOptions.fEnableCrepitus) && cnt == 3 ) - { - continue; - } - */ - - SetSectorFlag( sSectorX, sSectorY, (UINT8)sSectorZ, SF_HAVE_USED_GUIDE_QUOTE ); - - gTacticalStatus.fCountingDownForGuideDescription = TRUE; - gTacticalStatus.bGuideDescriptionCountDown = (INT8)(4 + Random( 5 )); // 4 to 8 tactical turns... - gTacticalStatus.ubGuideDescriptionToUse = ubSectorDescription[cnt][2]; - gTacticalStatus.bGuideDescriptionSectorX = (INT8)sSectorX; - gTacticalStatus.bGuideDescriptionSectorY = (INT8)sSectorY; - } + // this value isn't used for the normal guide quotes - this way we know this is meant for lua only + gTacticalStatus.ubGuideDescriptionToUse = 100; + + gTacticalStatus.bGuideDescriptionSectorX = (INT8)sSectorX; + gTacticalStatus.bGuideDescriptionSectorY = (INT8)sSectorY; } } + // Flugente: set this flag in any case + SetSectorFlag( sSectorX, sSectorY, (UINT8)sSectorZ, SF_HAVE_USED_GUIDE_QUOTE ); + // Handle guide description ( will be needed if a SAM one ) #ifdef JA2UB //UB diff --git a/Tactical/Dialogue Control.cpp b/Tactical/Dialogue Control.cpp index d8042ab1..717587b8 100644 --- a/Tactical/Dialogue Control.cpp +++ b/Tactical/Dialogue Control.cpp @@ -1374,7 +1374,23 @@ void HandleDialogue( ) //ExecuteCharacterDialogue( QItem->ubCharacterNum, QItem->usQuoteNum, QItem->iFaceIndex, DIALOGUE_TACTICAL_UI, TRUE ); ExecuteSnitchCharacterDialogue( QItem->ubCharacterNum, QItem->usQuoteNum, QItem->iFaceIndex, QItem->bUIHandlerID, QItem->uiSpecialEventData2, QItem->uiSpecialEventData3, QItem->uiSpecialEventData4 ); } - + // Flugente: additional dialogue + else if ( QItem->uiSpecialEventData & MULTIPURPOSE_SPECIAL_EVENT_ADDITIONAL_DIALOGUE ) + { + // Flugente: only set up face if we can access correctly + if ( QItem->iFaceIndex >= 0 ) + { + // Setup face pointer + gpCurrentTalkingFace = &gFacesData[QItem->iFaceIndex]; + } + + gubCurrentTalkingID = QItem->iFaceIndex; + + extern BOOLEAN ExecuteAdditionalCharacterDialogue( UINT8 ubProfile, INT32 iFaceIndex, UINT16 usEventNr, UINT32 aData1, UINT32 aData2, UINT32 aData3 ); + + ExecuteAdditionalCharacterDialogue( QItem->ubCharacterNum, QItem->iFaceIndex, QItem->usQuoteNum, QItem->uiSpecialEventData2, QItem->uiSpecialEventData3, QItem->uiSpecialEventData4 ); + } + #ifdef JA2UB //JA25 UB if ( QItem->uiSpecialEventData & MULTIPURPOSE_SPECIAL_EVENT_TEAM_MEMBERS_DONE_TALKING ) @@ -1671,6 +1687,9 @@ BOOLEAN SnitchTacticalCharacterDialogue( SOLDIERTYPE *pSoldier, UINT16 usQuoteNu if (pSoldier->stats.bLife < CONSCIOUSNESS ) return( FALSE ); + if ( pSoldier->usSkillCooldown[SOLDIER_COOLDOWN_CRYO] ) + return FALSE; + if (pSoldier->stats.bLife < OKLIFE && usQuoteNum != QUOTE_SERIOUSLY_WOUNDED ) return( FALSE ); @@ -1703,6 +1722,81 @@ BOOLEAN SnitchTacticalCharacterDialogue( SOLDIERTYPE *pSoldier, UINT16 usQuoteNu DIALOGUE_TACTICAL_UI, TRUE, FALSE ) ); } + + + +// Flugente: additional dialogue +// We call this function from several places. It uses the dialogue functions, but calls a Lua script to know whether something, and what, should be said +// This allows us to call a lot of very specific dialogue for individual mercs without the need to define what exactly should be said in the code - +// a modder can simply control in Lua what should happen +BOOLEAN AdditionalTacticalCharacterDialogue_CallsLua( SOLDIERTYPE *pSoldier, UINT16 usEventNr, UINT32 aData1, UINT32 aData2, UINT32 aData3 ) +{ + // Haydent + if ( is_client ) + return( FALSE ); //somewhere amongst all this it causes a puase of merc movement while making the quote which throws out the movement sync between clients... : hayden. + + if ( pSoldier->ubProfile == NO_PROFILE ) + return( FALSE ); + +#if (defined JA2UB) + //Ja25 no meanwhiles +#else + if ( AreInMeanwhile() ) + return( FALSE ); +#endif + + if ( pSoldier->usSkillCooldown[SOLDIER_COOLDOWN_CRYO] ) + return FALSE; + + if ( pSoldier->stats.bLife < OKLIFE ) + return( FALSE ); + + if ( pSoldier->flags.uiStatusFlags & SOLDIER_GASSED ) + return( FALSE ); + + if ( ( AM_A_ROBOT( pSoldier ) ) ) + return( FALSE ); + + if ( pSoldier->bAssignment == ASSIGNMENT_POW ) + return( FALSE ); + + if ( AM_AN_EPC( pSoldier ) && !( gMercProfiles[pSoldier->ubProfile].ubMiscFlags & PROFILE_MISC_FLAG_FORCENPCQUOTE ) ) + return( FALSE ); + + // we actually use the snitch function here, and simply deviate later on due to marking this with MULTIPURPOSE_SPECIAL_EVENT_ADDITIONAL_DIALOGUE + return( SnitchCharacterDialogue( pSoldier->ubProfile, usEventNr, pSoldier->iFaceIndex, + DIALOGUE_SPECIAL_EVENT_MULTIPURPOSE, MULTIPURPOSE_SPECIAL_EVENT_ADDITIONAL_DIALOGUE, + aData1, aData2, aData3, + DIALOGUE_TACTICAL_UI, TRUE, FALSE ) ); +} + +// call event for all mercs in a sector +void AdditionalTacticalCharacterDialogue_AllInSector(INT16 aSectorX, INT16 aSectorY, INT8 aSectorZ, UINT8 ausIgnoreProfile, + UINT16 usEventNr, UINT32 aData1, UINT32 aData2, UINT32 aData3, INT32 aAroundGridno, INT32 aRadius ) +{ + SOLDIERTYPE* pSoldier; + int cnt = gTacticalStatus.Team[gbPlayerNum].bFirstID; + for ( pSoldier = MercPtrs[cnt]; cnt <= gTacticalStatus.Team[gbPlayerNum].bLastID; ++cnt, pSoldier++ ) + { + if ( pSoldier->stats.bLife >= OKLIFE && pSoldier->bActive && + pSoldier->ubProfile != ausIgnoreProfile && + pSoldier->sSectorX == aSectorX && pSoldier->sSectorY == aSectorY && pSoldier->bSectorZ == aSectorZ && + pSoldier->bAssignment != ASSIGNMENT_POW && pSoldier->bAssignment != IN_TRANSIT && + (aAroundGridno == NOWHERE || PythSpacesAway( pSoldier->sGridNo, aAroundGridno ) <= aRadius ) && + !pSoldier->flags.fBetweenSectors ) + { + AdditionalTacticalCharacterDialogue_CallsLua( pSoldier, usEventNr, aData1, aData2, aData3 ); + } + } +} + +void AdditionalTacticalCharacterDialogue_AllInSectorRadiusCall( UINT8 ausIgnoreProfile, + UINT16 usEventNr, UINT32 aData1, UINT32 aData2, UINT32 aData3, INT32 aAroundGridno, INT32 aRadius ) +{ + return AdditionalTacticalCharacterDialogue_AllInSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, ausIgnoreProfile, + usEventNr, aData1, aData2, aData3, aAroundGridno, aRadius ); +} + // This function takes a profile num, quote num, faceindex and a UI hander ID. // What it does is queues up the dialog to be ultimately loaded/displayed // FACEINDEX @@ -1785,7 +1879,6 @@ BOOLEAN CharacterDialogueWithSpecialEventEx( UINT8 ubCharacterNum, UINT16 usQuot return( TRUE ); } - BOOLEAN CharacterDialogue( UINT8 ubCharacterNum, UINT16 usQuoteNum, INT32 iFaceIndex, UINT8 bUIHandlerID, BOOLEAN fFromSoldier, BOOLEAN fDelayed ) { DIALOGUE_Q_STRUCT *QItem; @@ -1813,6 +1906,22 @@ BOOLEAN CharacterDialogue( UINT8 ubCharacterNum, UINT16 usQuoteNum, INT32 iFaceI // Add to queue ghDialogueQ = AddtoQueue( ghDialogueQ, &QItem ); + // Flugente: additional dialogue + // in the dialogue screen, only the merc doing the talking may answer + if ( bUIHandlerID == DIALOGUE_NPC_UI ) + { + if ( gusSelectedSoldier != NOBODY ) + { + AdditionalTacticalCharacterDialogue_CallsLua( MercPtrs[gusSelectedSoldier], ADE_DIALOGUE_REACTION, ubCharacterNum, usQuoteNum ); + } + } + // if teammembers talk, anyone may answer + else + { + // other teammembers might react to this line + AdditionalTacticalCharacterDialogue_AllInSector( gWorldSectorX, gWorldSectorY, gbWorldSectorZ, ubCharacterNum, ADE_DIALOGUE_REACTION, ubCharacterNum, usQuoteNum ); + } + return( TRUE ); } @@ -2075,15 +2184,57 @@ BOOLEAN ExecuteSnitchCharacterDialogue( UINT8 ubCharacterNum, UINT16 usQuoteNum, gbUIHandlerID = DIALOGUE_TACTICAL_UI; guiScreenIDUsedWhenUICreated = guiCurrentScreen; - - return( TRUE ); } + return( TRUE ); +} + +// Flugente: additional dialogue +extern void LuaHandleAdditionalDialogue( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ, UINT8 ubProfile, INT32 iFaceIndex, UINT16 usEventNr, UINT32 aData1, UINT32 aData2, UINT32 aData3 ); + +BOOLEAN ExecuteAdditionalCharacterDialogue( UINT8 ubProfile, INT32 iFaceIndex, UINT16 usEventNr, UINT32 aData1, UINT32 aData2, UINT32 aData3 ) +{ + SOLDIERTYPE *pSoldier = FindSoldierByProfileID( ubProfile, TRUE ); + + if ( !pSoldier ) + return FALSE; + + // call Lua script on whether we can play something here, and get text and sound file + LuaHandleAdditionalDialogue( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ, ubProfile, iFaceIndex, usEventNr, aData1, aData2, aData3 ); + + return( TRUE ); +} + +void SetQuoteStr( STR16 aStr ) +{ + //Copy the original string over to the temp string + wcscpy( gzQuoteStr, aStr ); +} + +BOOLEAN LuaCallsToDoDialogueStuff( UINT8 ubProfile, INT32 iFaceIndex, const char* azSoundString ) +{ + SOLDIERTYPE *pSoldier = FindSoldierByProfileID( ubProfile, TRUE ); + + if ( !pSoldier ) + return FALSE; + + // in order to stop modders from crashing the game by calling this function a million times, we can only say one thing at a time + if ( IsFaceTalking( iFaceIndex ) ) + return FALSE; + + SetFaceTalking( iFaceIndex, (char*)azSoundString, gzQuoteStr, RATE_11025, 30, 1, MIDDLEPAN ); + CreateTalkingUI( DIALOGUE_TACTICAL_UI, iFaceIndex, ubProfile, pSoldier, gzQuoteStr ); + + // Set global handleer ID value, used when face desides it's done... + gbUIHandlerID = DIALOGUE_TACTICAL_UI; + + guiScreenIDUsedWhenUICreated = guiCurrentScreen; + + return TRUE; } void CreateTalkingUI( INT8 bUIHandlerID, INT32 iFaceIndex, UINT8 ubCharacterNum, SOLDIERTYPE *pSoldier, STR16 zQuoteStr ) { - // Show text, if on if ( gGameSettings.fOptions[ TOPTION_SUBTITLES ] || !gFacesData[ iFaceIndex ].fValidSpeech ) { diff --git a/Tactical/Dialogue Control.h b/Tactical/Dialogue Control.h index 93eb17a5..392ce451 100644 --- a/Tactical/Dialogue Control.h +++ b/Tactical/Dialogue Control.h @@ -276,6 +276,7 @@ enum DialogQuoteIDs #endif #define MULTIPURPOSE_SPECIAL_EVENT_SNITCH_DIALOGUE 0x00000004 +#define MULTIPURPOSE_SPECIAL_EVENT_ADDITIONAL_DIALOGUE 0x00000008 #ifdef JA2UB enum{ @@ -341,6 +342,38 @@ BOOLEAN TacticalCharacterDialogue( SOLDIERTYPE *pSoldier, UINT16 usQuoteNum ); BOOLEAN SnitchTacticalCharacterDialogue( SOLDIERTYPE *pSoldier, UINT16 usQuoteNum, UINT8 ubEventType, UINT8 ubTargetProfile, UINT8 ubSecondaryTargetProfile ); +// Flugente: additional dialogue +enum AdditionalDialogEvents +{ + ADE_DISCOVER_ROOM = 0, // merc is the 1st one to explore this room + ADE_DIALOGUE_REACTION, // someone else used a line, and we might answer to that + ADE_OMERTA_ENTRY, // similar to an IMP, we can comment upon the initial Omerta landing + ADE_SECTOR_COMMENTARY, // similar to the rebel's quotes, this is called upon visiting a sector for the first time + ADE_MERCHANT_CHAT, // we greet a merchant (used with non-profile merchants, in order to be a bit more lively) + ADE_DIALOGUE_NPC_FRIENDLY, // in a conversation, we can say something when clicking on a button + ADE_DIALOGUE_NPC_DIRECT, + ADE_DIALOGUE_NPC_THREATEN, + ADE_DIALOGUE_NPC_RECRUIT, + ADE_DIALOGUE_NPC_REPEAT, + ADE_DIALOGUE_RPC_RECRUIT_SUCCESS, // we've successfully recruited an RPC, and might comment on that + ADE_SEX, // does exactly what you think it does + ADE_WEATHERCHANGE, // the weather has changed, and we can warn the player about that + ADE_SKILL_RESULT, // we used a skill, and comment on the result + ADE_GRENADEWARNING, // a delayed enemy grenade was dropped nearby +}; + +// We call this function from several places. It uses the dialogue functions, but calls a Lua script to know whether something, and what, should be said +// This allows us to call a lot of very specific dialogue for individual mercs without the need to define what exactly should be said in the code - +// a modder can simply control in Lua what should happen +BOOLEAN AdditionalTacticalCharacterDialogue_CallsLua( SOLDIERTYPE *pSoldier, UINT16 usEventNr, UINT32 aData1 = 0, UINT32 aData2 = 0, UINT32 aData3 = 0 ); + +// call event for all mercs in a sector +void AdditionalTacticalCharacterDialogue_AllInSector( INT16 aSectorX, INT16 aSectorY, INT8 aSectorZ, UINT8 ausIgnoreProfile, + UINT16 usEventNr, UINT32 aData1 = 0, UINT32 aData2 = 0, UINT32 aData3 = 0, INT32 aAroundGridno = -1, INT32 aRadius = 0 ); + +void AdditionalTacticalCharacterDialogue_AllInSectorRadiusCall( UINT8 ausIgnoreProfile, UINT16 usEventNr, + UINT32 aData1 = 0, UINT32 aData2 = 0, UINT32 aData3 = 0, INT32 aAroundGridno = -1, INT32 aRadius = 0 ); + // Flugente: replace text with other text BOOLEAN ReplaceTextWithOtherText( CHAR16 *pFinishedString, CHAR16 compare[32], CHAR16 replace[32] ); diff --git a/Tactical/Faces.cpp b/Tactical/Faces.cpp index 55f9dbdd..1b2339a6 100644 --- a/Tactical/Faces.cpp +++ b/Tactical/Faces.cpp @@ -3148,7 +3148,10 @@ BOOLEAN SetFaceTalking( INT32 iFaceIndex, CHAR8 *zSoundFile, STR16 zTextString, return( TRUE ); } - +BOOLEAN IsFaceTalking( INT32 iFaceIndex ) +{ + return gFacesData[iFaceIndex].fTalking; +} BOOLEAN SetFaceTalkingMultipleSounds( INT32 iFaceIndex, CHAR8 zSoundFiles[][64], UINT8 ubMaxSoundsCount, STR16 zTextString, UINT32 usRate, UINT32 ubVolume, UINT32 ubLoops, UINT32 uiPan ) diff --git a/Tactical/Merc Hiring.cpp b/Tactical/Merc Hiring.cpp index 54401181..ee23a96a 100644 --- a/Tactical/Merc Hiring.cpp +++ b/Tactical/Merc Hiring.cpp @@ -52,6 +52,7 @@ #include "Quests.h" #include "GameSettings.h" #include "DynamicDialogue.h"// added by Flugente + #include "Dialogue Control.h" // added by Flugente #endif #include "connect.h" @@ -689,13 +690,19 @@ void HandleMercArrivesQuotes( SOLDIERTYPE *pSoldier ) // If we are approaching with helicopter, don't say any ( yet ) if ( pSoldier->ubStrategicInsertionCode != INSERTION_CODE_CHOPPER ) { - // Player-generated characters issue a comment about arriving in Omerta. - if ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__PLAYER_CHARACTER) + // if we haven't met the rebels yet, characters issue some comments + if ( gubQuest[QUEST_DELIVER_LETTER] == QUESTINPROGRESS ) { - if ( gubQuest[ QUEST_DELIVER_LETTER ] == QUESTINPROGRESS ) + // Player-generated characters issue a comment about arriving in Omerta. + if ( pSoldier->ubWhatKindOfMercAmI == MERC_TYPE__PLAYER_CHARACTER ) { TacticalCharacterDialogue( pSoldier, QUOTE_PC_DROPPED_OMERTA ); } + // Flugente: additional dialogue + else + { + AdditionalTacticalCharacterDialogue_CallsLua( pSoldier, ADE_OMERTA_ENTRY ); + } } // Check to see if anyone hates this merc and will now complain diff --git a/Tactical/ShopKeeper Interface.cpp b/Tactical/ShopKeeper Interface.cpp index 2332c1ab..c2c23969 100644 --- a/Tactical/ShopKeeper Interface.cpp +++ b/Tactical/ShopKeeper Interface.cpp @@ -2477,6 +2477,10 @@ void EnterShopKeeperInterfaceScreen_NonNPC( INT8 ubArmsDealerID, UINT8 aMercID ) gfSKIScreenExit = TRUE; } + // Flugente: additional dialogue + if ( gusSelectedSoldier != NOBODY ) + AdditionalTacticalCharacterDialogue_CallsLua( MercPtrs[gusSelectedSoldier], ADE_MERCHANT_CHAT ); + LeaveTacticalScreen( SHOPKEEPER_SCREEN ); } diff --git a/Tactical/SkillMenu.cpp b/Tactical/SkillMenu.cpp index ec525445..d1377a38 100644 --- a/Tactical/SkillMenu.cpp +++ b/Tactical/SkillMenu.cpp @@ -14,7 +14,7 @@ #include "strategicmap.h" #include "Map Screen Interface.h" #include "Rotting Corpses.h" - +#include "Dialogue Control.h" extern void GetEquipmentTemplates( ); @@ -319,7 +319,10 @@ SkillSelection::Functions( UINT32 aVal ) UINT8 ubID = WhoIsThere2(sTraitsMenuTargetGridNo, 0 ); - pSoldier->UseSkill(aVal, sTraitsMenuTargetGridNo, ubID); + BOOLEAN result = pSoldier->UseSkill(aVal, sTraitsMenuTargetGridNo, ubID); + + // additional dialogue + AdditionalTacticalCharacterDialogue_CallsLua( pSoldier, ADE_SKILL_RESULT, aVal, result ); Cancel(); gTraitSelection.Cancel(); @@ -472,7 +475,10 @@ ArtilleryTeam::Functions( UINT32 aVal ) if ( pSoldier == NULL || !pSoldier->CanUseSkill(SKILLS_RADIO_ARTILLERY) ) return; - pSoldier->OrderArtilleryStrike(usSector, sTraitsMenuTargetGridNo, (UINT8)(aVal)); + BOOLEAN result = pSoldier->OrderArtilleryStrike(usSector, sTraitsMenuTargetGridNo, (UINT8)(aVal)); + + // additional dialogue + AdditionalTacticalCharacterDialogue_CallsLua( pSoldier, ADE_SKILL_RESULT, SKILLS_RADIO_ARTILLERY, result ); Cancel(); gArtillerySector.Cancel(); @@ -663,7 +669,10 @@ ReinforcementNumber::Functions( UINT32 aVal ) if ( pSoldier == NULL || !pSoldier->CanUseSkill(SKILLS_RADIO_CALLREINFORCEMENTS) ) return; - pSoldier->RadioCallReinforcements(usSector, aVal); + BOOLEAN result = pSoldier->RadioCallReinforcements(usSector, aVal); + + // additional dialogue + AdditionalTacticalCharacterDialogue_CallsLua( pSoldier, ADE_SKILL_RESULT, SKILLS_RADIO_CALLREINFORCEMENTS, result ); Cancel(); gReinforcementSector.Cancel(); @@ -742,7 +751,10 @@ SoldierSelection::Functions( UINT32 aVal ) if ( pSoldier == NULL ) return; - pSoldier->UseSkill(usSkill, sTraitsMenuTargetGridNo, aVal); + BOOLEAN result = pSoldier->UseSkill(usSkill, sTraitsMenuTargetGridNo, aVal); + + // additional dialogue + AdditionalTacticalCharacterDialogue_CallsLua( pSoldier, ADE_SKILL_RESULT, usSkill, result ); Cancel(); gSkillSelection.Cancel(); @@ -821,7 +833,10 @@ DragSelection::Functions( UINT32 aVal ) if ( pSoldier ) { - pSoldier->UseSkill( usSkill, sTraitsMenuTargetGridNo, aVal ); + BOOLEAN result = pSoldier->UseSkill( usSkill, sTraitsMenuTargetGridNo, aVal ); + + // additional dialogue + AdditionalTacticalCharacterDialogue_CallsLua( pSoldier, ADE_SKILL_RESULT, usSkill, result ); } Cancel( ); diff --git a/Tactical/Soldier Profile.cpp b/Tactical/Soldier Profile.cpp index 8a0b3ea7..cb3ac251 100644 --- a/Tactical/Soldier Profile.cpp +++ b/Tactical/Soldier Profile.cpp @@ -2002,6 +2002,9 @@ BOOLEAN RecruitRPC( UINT8 ubCharNum ) HandlePlayingQuoteWhenHiringNpc( pNewSoldier->ubProfile ); #endif + // Flugente: additional dialogue + AdditionalTacticalCharacterDialogue_AllInSectorRadiusCall( ubCharNum, ADE_DIALOGUE_RPC_RECRUIT_SUCCESS, ubCharNum ); + // Flugente: external scripts might have extra functionality on recruiting someone LuaRecruitRPCAdditionalHandling( ubCharNum ); diff --git a/Tactical/Tactical Turns.cpp b/Tactical/Tactical Turns.cpp index e31cfc25..56c07535 100644 --- a/Tactical/Tactical Turns.cpp +++ b/Tactical/Tactical Turns.cpp @@ -27,6 +27,7 @@ #include "strategicmap.h" #include "Random.h" #include "Explosion Control.h" + #include "Dialogue Control.h" // added by Flugente #endif #include "Reinforcement.h" @@ -47,7 +48,7 @@ void HandleRPCDescription( ) std::vector ubMercsInSector (CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS, 0); // UINT8 ubMercsInSector[ CODE_MAXIMUM_NUMBER_OF_PLAYER_SLOTS ] = { 0 }; UINT8 ubNumMercs = 0; - UINT8 ubChosenMerc; + UINT8 ubChosenMerc = 0; SOLDIERTYPE *pTeamSoldier; INT32 cnt2; BOOLEAN fSAMSite = FALSE; @@ -91,8 +92,7 @@ void HandleRPCDescription( ) { return; } - - + gTacticalStatus.bGuideDescriptionCountDown--; if ( gTacticalStatus.bGuideDescriptionCountDown == 0 ) @@ -102,36 +102,55 @@ void HandleRPCDescription( ) // OK, count how many rpc guys we have.... // set up soldier ptr as first element in mercptrs list cnt2 = gTacticalStatus.Team[ gbPlayerNum ].bFirstID; - - // run through list - for ( pTeamSoldier = MercPtrs[ cnt2 ]; cnt2 <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt2++,pTeamSoldier++ ) + { - // Add guy if he's a candidate... - if ( RPC_RECRUITED( pTeamSoldier ) ) + // run through list + for ( pTeamSoldier = MercPtrs[cnt2]; cnt2 <= gTacticalStatus.Team[gbPlayerNum].bLastID; ++cnt2, pTeamSoldier++ ) { - if ( pTeamSoldier->stats.bLife >= OKLIFE && pTeamSoldier->bActive && + // Add guy if he's a candidate... + if ( RPC_RECRUITED( pTeamSoldier ) ) + { + if ( pTeamSoldier->stats.bLife >= OKLIFE && pTeamSoldier->bActive && pTeamSoldier->sSectorX == gTacticalStatus.bGuideDescriptionSectorX && pTeamSoldier->sSectorY == gTacticalStatus.bGuideDescriptionSectorY && pTeamSoldier->bSectorZ == gbWorldSectorZ && - !pTeamSoldier->flags.fBetweenSectors ) - { - if ( pTeamSoldier->ubProfile == IRA || + !pTeamSoldier->flags.fBetweenSectors ) + { + if ( pTeamSoldier->ubProfile == IRA || pTeamSoldier->ubProfile == MIGUEL || pTeamSoldier->ubProfile == CARLOS || pTeamSoldier->ubProfile == DIMITRI ) - { - ubMercsInSector[ ubNumMercs ] = (UINT8)cnt2; - ubNumMercs++; + { + ubMercsInSector[ubNumMercs] = (UINT8)cnt2; + ++ubNumMercs; + } } } } + + // If we are > 0 + if ( ubNumMercs > 0 ) + { + ubChosenMerc = (UINT8)Random( ubNumMercs ); + + TacticalCharacterDialogueWithSpecialEvent( MercPtrs[ubMercsInSector[ubChosenMerc]], gTacticalStatus.ubGuideDescriptionToUse, DIALOGUE_SPECIAL_EVENT_USE_ALTERNATE_FILES, 0, 0 ); + } } - // If we are > 0 - if ( ubNumMercs > 0 ) + // Flugente: special value signifies lua-based quotes + if ( !ubNumMercs || gTacticalStatus.ubGuideDescriptionToUse == 100 ) { - ubChosenMerc = (UINT8)Random( ubNumMercs ); - - TacticalCharacterDialogueWithSpecialEvent( MercPtrs[ ubMercsInSector[ ubChosenMerc ] ], gTacticalStatus.ubGuideDescriptionToUse, DIALOGUE_SPECIAL_EVENT_USE_ALTERNATE_FILES, 0, 0 ); + // run through list + cnt2 = gTacticalStatus.Team[gbPlayerNum].bFirstID; + for ( pTeamSoldier = MercPtrs[cnt2]; cnt2 <= gTacticalStatus.Team[gbPlayerNum].bLastID; ++cnt2, pTeamSoldier++ ) + { + if ( pTeamSoldier->stats.bLife >= OKLIFE && pTeamSoldier->bActive && + pTeamSoldier->sSectorX == gTacticalStatus.bGuideDescriptionSectorX && pTeamSoldier->sSectorY == gTacticalStatus.bGuideDescriptionSectorY && + pTeamSoldier->bSectorZ == gbWorldSectorZ && + !pTeamSoldier->flags.fBetweenSectors ) + { + AdditionalTacticalCharacterDialogue_CallsLua( pTeamSoldier, ADE_SECTOR_COMMENTARY ); + } + } } } } diff --git a/Tactical/faces.h b/Tactical/faces.h index 755eb375..3e374974 100644 --- a/Tactical/faces.h +++ b/Tactical/faces.h @@ -236,6 +236,8 @@ BOOLEAN SetFaceTalking( INT32 iFaceIndex, CHAR8 *zSoundFile, STR16 zTextString, // This function will setup appropriate face data and begin the speech process. It can fail if the sound //cannot be played for any reason. +BOOLEAN IsFaceTalking( INT32 iFaceIndex ); + BOOLEAN SetFaceTalkingMultipleSounds( INT32 iFaceIndex, CHAR8 zSoundFiles[][64], UINT8 ubMaxSoundsCount, STR16 zTextString, UINT32 usRate, UINT32 ubVolume, UINT32 ubLoops, UINT32 uiPan ); // Set some face talking flags without need to play sound diff --git a/TacticalAI/AIMain.cpp b/TacticalAI/AIMain.cpp index 477fa2f3..b1f1ea18 100644 --- a/TacticalAI/AIMain.cpp +++ b/TacticalAI/AIMain.cpp @@ -2294,7 +2294,11 @@ INT8 ExecuteAction(SOLDIERTYPE *pSoldier) { UINT8 ubID = WhoIsThere2( pSoldier->aiData.usActionData, 0 ); - pSoldier->UseSkill(pSoldier->usAISkillUse, pSoldier->aiData.usActionData, ubID); + BOOLEAN result = pSoldier->UseSkill(pSoldier->usAISkillUse, pSoldier->aiData.usActionData, ubID); + + // additional dialogue + AdditionalTacticalCharacterDialogue_CallsLua( pSoldier, ADE_SKILL_RESULT, pSoldier->usAISkillUse, result ); + ActionDone( pSoldier ); } break; diff --git a/TacticalAI/NPC.cpp b/TacticalAI/NPC.cpp index 0dbb0367..1348031f 100644 --- a/TacticalAI/NPC.cpp +++ b/TacticalAI/NPC.cpp @@ -2217,6 +2217,14 @@ void Converse( UINT8 ubNPC, UINT8 ubMerc, INT8 bApproach, UINT32 uiApproachData 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; } diff --git a/TileEngine/Render Fun.cpp b/TileEngine/Render Fun.cpp index 7781c486..c77c78bf 100644 --- a/TileEngine/Render Fun.cpp +++ b/TileEngine/Render Fun.cpp @@ -20,6 +20,7 @@ #include "dialogue control.h" #include "Random.h" #include "gamesettings.h" + #include "Dialogue Control.h" // added by Flugente #endif // Room Information @@ -278,11 +279,10 @@ void RemoveRoomRoof( INT32 sGridNo, UINT16 usRoomNum, SOLDIERTYPE *pSoldier ) // STRUCTURE *pStructure;//, *pBase; // LOOP THORUGH WORLD AND CHECK ROOM INFO - for ( cnt = 0; cnt < WORLD_MAX; cnt++ ) + for ( cnt = 0; cnt < WORLD_MAX; ++cnt ) { if ( gusWorldRoomInfo[ cnt ] == usRoomNum ) { - SetGridNoRevealedFlag( cnt );//dnl ch56 141009 //RemoveRoofIndexFlagsFromTypeRange( cnt, FIRSTROOF, SECONDSLANTROOF, LEVELNODE_REVEAL ); @@ -319,7 +319,6 @@ void RemoveRoomRoof( INT32 sGridNo, UINT16 usRoomNum, SOLDIERTYPE *pSoldier ) // Get XY ConvertGridNoToXY( cnt, &sX, &sY ); SetRecalculateWireFrameFlagRadius( sX, sY, 2 ); - } } @@ -331,17 +330,18 @@ void RemoveRoomRoof( INT32 sGridNo, UINT16 usRoomNum, SOLDIERTYPE *pSoldier ) // } //} + // Flugente: additional dialogue + if ( pSoldier ) + AdditionalTacticalCharacterDialogue_CallsLua( pSoldier, ADE_DISCOVER_ROOM, usRoomNum ); + // DIRTY THE WORLD! InvalidateWorldRedundency(); SetRenderFlags(RENDER_FLAG_FULL ); - - + CalculateWorldWireFrameTiles( FALSE ); - } - BOOLEAN AddSpecialTileRange( SGPRect *pSelectRegion ) { INT32 cnt1, cnt2; diff --git a/TileEngine/environment.cpp b/TileEngine/environment.cpp index 0d282dd9..bb592c43 100644 --- a/TileEngine/environment.cpp +++ b/TileEngine/environment.cpp @@ -28,6 +28,7 @@ #include "Strategic Movement.h" // added by Flugente #include "tiledef.h" // added by Flugente #include "worldman.h" // added by Flugente + #include "Dialogue Control.h" // added by Flugente #endif #include "Text.h" @@ -906,7 +907,10 @@ void ChangeWeather( UINT8 aSector, UINT8 aType ) ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s: Changed weather from %s to %s", pStrSectorName, szWeatherTypeText[SectorInfo[aSector].usWeather], szWeatherTypeText[aType] ); } - // First turn off whatever rain it is, then turn on the requested rain + // Flugente: additional dialogue + AdditionalTacticalCharacterDialogue_AllInSector( SECTORX( aSector ), SECTORY( aSector ), 0, NO_PROFILE, ADE_WEATHERCHANGE, aType ); + + // First turn off whatever weather it is, then turn on the requested weather SectorInfo[aSector].usWeather = aType; } } diff --git a/TileEngine/physics.cpp b/TileEngine/physics.cpp index cba16167..31c1609d 100644 --- a/TileEngine/physics.cpp +++ b/TileEngine/physics.cpp @@ -32,6 +32,7 @@ #include "environment.h" #include "GameSettings.h" #include "Buildings.h" +#include "Dialogue Control.h" // added by Flugente #endif #include "Campaign.h" @@ -2710,6 +2711,13 @@ void HandleArmedObjectImpact( REAL_OBJECT *pObject ) fDoImpact = TRUE; } + // Flugente: additional dialogue + // any merc nearby that can see this can warn us + if ( ( *pObj )[0]->data.misc.bDelay > 0 && pObject->ubOwner != NOBODY )//&& MercPtrs[pObject->ubOwner]->bTeam != gbPlayerNum ) + { + AdditionalTacticalCharacterDialogue_AllInSectorRadiusCall( NO_PROFILE, ADE_GRENADEWARNING, 0, 0, 0, pObject->sGridNo, 5 ); + } + if ( fDoImpact ) { if ( Item[pObject->Obj.usItem].flare )