mirror of
https://github.com/mapbase-source/source-sdk-2013.git
synced 2024-12-25 06:15:30 +03:00
Merge pull request #275 from Blixibon/mapbase/feature/response-followup-criteria-expansion
Better circumstantial criteria for followup responses
This commit is contained in:
commit
02f81094e5
@ -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))
|
||||
@ -99,6 +108,7 @@ static void DispatchComeback( CAI_ExpresserWithFollowup *pExpress, CBaseEntity *
|
||||
{
|
||||
pActor->Speak( followup.followup_concept, &criteria );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -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 )
|
||||
|
@ -403,6 +403,9 @@ public:
|
||||
|
||||
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 );
|
||||
|
@ -126,6 +126,13 @@ public:
|
||||
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
|
||||
@ -244,9 +251,15 @@ public:
|
||||
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
|
||||
@ -311,11 +324,15 @@ private:
|
||||
//
|
||||
|
||||
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 );
|
||||
|
@ -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>
|
||||
|
||||
@ -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();
|
||||
}
|
||||
@ -197,6 +210,7 @@ static CAI_Expresser *InferExpresserFromBaseEntity(CBaseEntity * RESTRICT pEnt)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void CResponseQueue::CDeferredResponse::Quash()
|
||||
@ -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.
|
||||
@ -272,9 +303,15 @@ 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);
|
||||
@ -282,6 +319,11 @@ bool CResponseQueue::DispatchOneResponse(CDeferredResponse &response)
|
||||
{
|
||||
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 ) )
|
||||
{
|
||||
@ -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;
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
@ -116,6 +116,11 @@ public:
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user