Skip to content

Commit

Permalink
Merge pull request entropy-zero#275 from Blixibon/mapbase/feature/res…
Browse files Browse the repository at this point in the history
…ponse-followup-criteria-expansion

Better circumstantial criteria for followup responses
  • Loading branch information
Blixibon authored Feb 4, 2024
2 parents 11533fa + 4adab15 commit 02f8109
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 6 deletions.
10 changes: 10 additions & 0 deletions sp/src/game/server/ai_expresserfollowup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ static void DispatchComeback( CAI_ExpresserWithFollowup *pExpress, CBaseEntity *
// add in any provided contexts from the parameters onto the ones stored in the followup
criteria.Merge( followup.followup_contexts );

#ifdef MAPBASE
if (CAI_ExpresserSink *pSink = dynamic_cast<CAI_ExpresserSink *>(pRespondent))
{
criteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", (pRespondent->GetAbsOrigin() - pSpeaker->GetAbsOrigin()).Length() ) );
g_ResponseQueueManager.GetQueue()->AppendFollowupCriteria( followup.followup_concept, criteria, pSink->GetSinkExpresser(), pSink, pRespondent, pSpeaker, kDRT_SPECIFIC );

pSink->Speak( followup.followup_concept, &criteria );
}
#else
// This is kludgy and needs to be fixed in class hierarchy, but for now, try to guess at the most likely
// kinds of targets and dispatch to them.
if (CBaseMultiplayerPlayer *pPlayer = dynamic_cast<CBaseMultiplayerPlayer *>(pRespondent))
Expand All @@ -99,6 +108,7 @@ static void DispatchComeback( CAI_ExpresserWithFollowup *pExpress, CBaseEntity *
{
pActor->Speak( followup.followup_concept, &criteria );
}
#endif
}

#if 0
Expand Down
48 changes: 48 additions & 0 deletions sp/src/game/server/ai_playerally.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1800,6 +1800,54 @@ bool CAI_PlayerAlly::IsAllowedToSpeak( AIConcept_t concept, bool bRespondingToPl
return true;
}

#ifdef MAPBASE
//-----------------------------------------------------------------------------
// Purpose: Specifically for player allies handling followup responses.
// Better-accounts for unknown concepts so that users are free in what they use.
//-----------------------------------------------------------------------------
bool CAI_PlayerAlly::IsAllowedToSpeakFollowup( AIConcept_t concept, CBaseEntity *pIssuer, bool bSpecific )
{
CAI_AllySpeechManager * pSpeechManager = GetAllySpeechManager();
ConceptInfo_t * pInfo = pSpeechManager->GetConceptInfo( concept );
ConceptCategory_t category = SPEECH_PRIORITY; // Must be SPEECH_PRIORITY to get around semaphore

if ( !IsOkToSpeak( category, true ) )
return false;

// If this followup is specifically targeted towards us, speak if we're not already speaking
// If it's meant to be spoken by anyone, respect speech delay and semaphore
if ( bSpecific )
{
if ( !GetExpresser()->CanSpeakAfterMyself() )
return false;
}
else
{
if ( !GetExpresser()->CanSpeak() )
return false;

CAI_TimedSemaphore *pSemaphore = GetExpresser()->GetMySpeechSemaphore( this );
if ( pSemaphore && !pSemaphore->IsAvailable( this ) )
{
// Only if the semaphore holder isn't the one dispatching the followup
if ( pSemaphore->GetOwner() != pIssuer )
return false;
}
}

if ( !pSpeechManager->ConceptDelayExpired( concept ) )
return false;

if ( ( pInfo && pInfo->flags & AICF_SPEAK_ONCE ) && GetExpresser()->SpokeConcept( concept ) )
return false;

if ( !GetExpresser()->CanSpeakConcept( concept ) )
return false;

return true;
}
#endif

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CAI_PlayerAlly::SpeakIfAllowed( AIConcept_t concept, const char *modifiers, bool bRespondingToPlayer, char *pszOutResponseChosen, size_t bufsize )
Expand Down
3 changes: 3 additions & 0 deletions sp/src/game/server/ai_playerally.h
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,9 @@ class CAI_PlayerAlly : public CAI_BaseActor

bool ShouldSpeakRandom( AIConcept_t concept, int iChance );
bool IsAllowedToSpeak( AIConcept_t concept, bool bRespondingToPlayer = false );
#ifdef MAPBASE
bool IsAllowedToSpeakFollowup( AIConcept_t concept, CBaseEntity *pIssuer, bool bSpecific );
#endif
virtual bool SpeakIfAllowed( AIConcept_t concept, const char *modifiers = NULL, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 );
#ifdef MAPBASE
virtual bool SpeakIfAllowed( AIConcept_t concept, AI_CriteriaSet& modifiers, bool bRespondingToPlayer = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0 );
Expand Down
19 changes: 18 additions & 1 deletion sp/src/game/server/ai_speech_new.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ class CAI_ExpresserSink
virtual void OnSpokeConcept( AIConcept_t concept, AI_Response *response ) {};
virtual void OnStartSpeaking() {}
virtual bool UseSemaphore() { return true; }

#ifdef MAPBASE
// Works around issues with CAI_ExpresserHost<> class hierarchy
virtual CAI_Expresser *GetSinkExpresser() { return NULL; }
virtual bool IsAllowedToSpeakFollowup( AIConcept_t concept, CBaseEntity *pIssuer, bool bSpecific ) { return true; }
virtual bool Speak( AIConcept_t concept, AI_CriteriaSet *pCriteria, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL ) { return false; }
#endif
};

struct ConceptHistory_t
Expand Down Expand Up @@ -244,9 +251,15 @@ class CAI_Expresser : public ResponseRules::IResponseFilter
static bool RunScriptResponse( CBaseEntity *pTarget, const char *response, AI_CriteriaSet *criteria, bool file );
#endif

#ifdef MAPBASE
public:
#else
protected:
#endif
CAI_TimedSemaphore *GetMySpeechSemaphore( CBaseEntity *pNpc );

protected:

bool SpeakRawScene( const char *pszScene, float delay, AI_Response *response, IRecipientFilter *filter = NULL );
// This will create a fake .vcd/CChoreoScene to wrap the sound to be played
#ifdef MAPBASE
Expand Down Expand Up @@ -311,11 +324,15 @@ class CAI_Expresser : public ResponseRules::IResponseFilter
//

template <class BASE_NPC>
class CAI_ExpresserHost : public BASE_NPC, protected CAI_ExpresserSink
class CAI_ExpresserHost : public BASE_NPC, public CAI_ExpresserSink
{
DECLARE_CLASS_NOFRIEND( CAI_ExpresserHost, BASE_NPC );

public:
#ifdef MAPBASE
CAI_Expresser *GetSinkExpresser() { return this->GetExpresser(); }
#endif

virtual void NoteSpeaking( float duration, float delay );

virtual bool Speak( AIConcept_t concept, const char *modifiers = NULL, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );
Expand Down
74 changes: 69 additions & 5 deletions sp/src/game/server/ai_speechqueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
#include "ai_baseactor.h"
#include "ai_speech.h"
//#include "flex_expresser.h"
#ifdef MAPBASE
#include "sceneentity.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>

Expand Down Expand Up @@ -170,15 +173,25 @@ void CResponseQueue::RemoveExpresserHost(CBaseEntity *host)
}
}

#ifdef MAPBASE
/// Get the expresser for a base entity.
static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt, CAI_ExpresserSink **ppSink = NULL)
{
if ( CAI_ExpresserSink *pSink = dynamic_cast<CAI_ExpresserSink *>(pEnt) )
{
if (ppSink)
*ppSink = pSink;
return pSink->GetSinkExpresser();
}

return NULL;
}
#else
/// Get the expresser for a base entity.
/// TODO: Kind of an ugly hack until I get the class hierarchy straightened out.
static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt)
{
#ifdef MAPBASE
if ( CBasePlayer *pPlayer = ToBasePlayer(pEnt) )
#else
if ( CBaseMultiplayerPlayer *pPlayer = dynamic_cast<CBaseMultiplayerPlayer *>(pEnt) )
#endif
{
return pPlayer->GetExpresser();
}
Expand All @@ -197,6 +210,7 @@ static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt)
return NULL;
}
}
#endif


void CResponseQueue::CDeferredResponse::Quash()
Expand All @@ -205,6 +219,23 @@ void CResponseQueue::CDeferredResponse::Quash()
m_fDispatchTime = 0;
}

#ifdef MAPBASE
void CResponseQueue::AppendFollowupCriteria( AIConcept_t concept, AI_CriteriaSet &set, CAI_Expresser *pEx,
CAI_ExpresserSink *pSink, CBaseEntity *pTarget, CBaseEntity *pIssuer, DeferredResponseTarget_t nTargetType )
{
// Allows control over which followups interrupt speech routines
set.AppendCriteria( "followup_allowed_to_speak", (pSink->IsAllowedToSpeakFollowup( concept, pIssuer, nTargetType == kDRT_SPECIFIC )) ? "1" : "0" );

set.AppendCriteria( "followup_target_type", UTIL_VarArgs( "%i", (int)nTargetType ) );

// NOTE: This assumes any expresser entity derived from CBaseFlex is also derived from CBaseCombatCharacter
if (pTarget->IsCombatCharacter())
set.AppendCriteria( "is_speaking", (pEx->IsSpeaking() || IsRunningScriptedSceneWithSpeechAndNotPaused( assert_cast<CBaseFlex*>(pTarget) )) ? "1" : "0" );
else
set.AppendCriteria( "is_speaking", "0" );
}
#endif

bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
{
// find the target.
Expand Down Expand Up @@ -272,16 +303,27 @@ bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
continue; // too far
}

#ifdef MAPBASE
CAI_ExpresserSink *pSink = NULL;
pEx = InferExpresserFromBaseEntity( pTarget, &pSink );
#else
pEx = InferExpresserFromBaseEntity(pTarget);
#endif
if ( !pEx || pTarget == pIssuer )
continue;

AI_CriteriaSet characterCriteria;
pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
characterCriteria.Merge(&deferredCriteria);
if ( pIssuer )
{
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) );
}

#ifdef MAPBASE
AppendFollowupCriteria( response.m_concept, characterCriteria, pEx, pSink, pTarget, pIssuer, kDRT_ALL );
#endif

AI_Response prospectiveResponse;
if ( pEx->FindResponse( prospectiveResponse, response.m_concept, &characterCriteria ) )
{
Expand All @@ -304,14 +346,26 @@ bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
return false; // we're done right here.

// Get the expresser for the target.
#ifdef MAPBASE
CAI_ExpresserSink *pSink = NULL;
pEx = InferExpresserFromBaseEntity( pTarget, &pSink );
#else
pEx = InferExpresserFromBaseEntity(pTarget);
#endif
if (!pEx)
return false;


AI_CriteriaSet characterCriteria;
pEx->GatherCriteria(&characterCriteria, response.m_concept, NULL);
characterCriteria.Merge(&deferredCriteria);
#ifdef MAPBASE
if ( pIssuer )
{
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", (pTarget->GetAbsOrigin() - pIssuer->GetAbsOrigin()).Length() ) );
}

AppendFollowupCriteria( response.m_concept, characterCriteria, pEx, pSink, pTarget, pIssuer, kDRT_SPECIFIC );
#endif
pEx->Speak( response.m_concept, &characterCriteria );

return true;
Expand Down Expand Up @@ -364,7 +418,12 @@ bool CResponseQueue::DispatchOneResponse_ThenANY( CDeferredResponse &response, A
continue; // too far
}

#ifdef MAPBASE
CAI_ExpresserSink *pSink = NULL;
pEx = InferExpresserFromBaseEntity( pTarget, &pSink );
#else
pEx = InferExpresserFromBaseEntity(pTarget);
#endif
if ( !pEx )
continue;

Expand All @@ -376,6 +435,11 @@ bool CResponseQueue::DispatchOneResponse_ThenANY( CDeferredResponse &response, A
{
characterCriteria.AppendCriteria( "dist_from_issuer", UTIL_VarArgs( "%f", sqrt(distIssuerToTargetSq) ) );
}

#ifdef MAPBASE
AppendFollowupCriteria( response.m_concept, characterCriteria, pEx, pSink, pTarget, pIssuer, kDRT_ANY );
#endif

AI_Response prospectiveResponse;

#ifdef MAPBASE
Expand Down
5 changes: 5 additions & 0 deletions sp/src/game/server/ai_speechqueue.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ class CResponseQueue
inline int GetNumExpresserTargets() const;
inline CBaseEntity *GetExpresserHost(int which) const;

#ifdef MAPBASE
void AppendFollowupCriteria( AIConcept_t concept, AI_CriteriaSet &set, CAI_Expresser *pEx,
CAI_ExpresserSink *pSink, CBaseEntity *pTarget, CBaseEntity *pIssuer, DeferredResponseTarget_t nTargetType );
#endif

protected:
/// Actually send off one response to a consumer
/// Return true if dispatch succeeded
Expand Down

0 comments on commit 02f8109

Please sign in to comment.