CSpeakModifiers
January 2024
You can help by adding links to this article from other relevant articles.
Problem
If you have had to use the Response Rules system with modifiers passed in at a response request time you will be aware that you must spend your time concatenating strings to build these modifiers to pass into the Speak calls. It is time consuming to always write and just annoying to write.
Solution
In light of this problem I decided to write a simple utility class which would handle the dirty work for me. It is very small class and allows you to just pop in your modifiers whatever order you wish and it will build the output string for the response rules for you. It also takes in parameters as floating point numbers or integer numbers and converts them to strings for you.
Here is the code for the class.
//====================== Written By PVKII Team =========================
//
// Purpose: Utility class to simplify Speak calls with modifiers
//
// $NoKeywords: $
//=======================================================================
class CSpeakModifiers
{
DECLARE_CLASS_NOBASE( CSpeakModifiers );
public:
void AddModifier( const char *keyName, const char *keyValue );
void AddModifier( const char *keyName, int keyValue );
void AddModifier( const char *keyName, float keyValue );
void GetModifiersAsString(char *buffer, size_t bufsize);
protected:
struct modifierentry
{
modifierentry()
{
Q_memset(strKeyName,0,sizeof(strKeyName));
Q_memset(strKeyValue,0,sizeof(strKeyValue));
}
char strKeyName[128];
char strKeyValue[128];
};
CUtlVector<modifierentry> m_Modifiers;
};
/***********************************************
Speak Modifiers Utility Class
***********************************************/
void CSpeakModifiers::AddModifier(const char *keyName, const char *keyValue)
{
modifierentry m;
Q_strncpy(m.strKeyName,keyName,sizeof(m.strKeyName));
Q_strncpy(m.strKeyValue,keyValue,sizeof(m.strKeyValue));
m_Modifiers.AddToTail(m);
}
void CSpeakModifiers::AddModifier(const char *keyName, float keyValue)
{
modifierentry m;
Q_strncpy(m.strKeyName,keyName,sizeof(m.strKeyName));
Q_snprintf(m.strKeyValue,sizeof(m.strKeyValue),"%f",keyValue);
m_Modifiers.AddToTail(m);
}
void CSpeakModifiers::AddModifier(const char *keyName, int keyValue)
{
modifierentry m;
Q_strncpy(m.strKeyName,keyName,sizeof(m.strKeyName));
Q_snprintf(m.strKeyValue,sizeof(m.strKeyValue),"%d",keyValue);
m_Modifiers.AddToTail(m);
}
void CSpeakModifiers::GetModifiersAsString( char *buffer, size_t bufsize )
{
Q_memset(buffer,0,bufsize);
for(int i = 0; i < m_Modifiers.Count() - 1; i++)
{
Q_strncat(buffer,UTIL_VarArgs("%s:%s,",m_Modifiers[i].strKeyName,m_Modifiers[i].strKeyValue),bufsize);
}
Q_strncat(buffer,UTIL_VarArgs("%s:%s",m_Modifiers[m_Modifiers.Count() - 1].strKeyName,m_Modifiers[m_Modifiers.Count() - 1].strKeyValue),bufsize);
}
And here would be the overridden SpeakIfAllowed function you would use in conjunction with the utility class
bool SpeakConceptIfAllowed( int iConcept, CSpeakModifiers *modifiers = NULL, bool bCanSpeakDead = false, bool bCanSpeakIfSpeaking = false, char *pszOutResponseChosen = NULL, size_t bufsize = 0, IRecipientFilter *filter = NULL );
bool CPVK2ChoreographyProperty::SpeakConceptIfAllowed( int iConcept, CSpeakModifiers *modifiers, bool bCanSpeakDead, bool bCanSpeakIfSpeaking, char *pszOutResponseChosen, size_t bufsize, IRecipientFilter *filter )
{
if(modifiers)
{
char modbuf[512];
modifiers->GetModifiersAsString(modbuf,sizeof(modbuf));
return GetOuter()->SpeakConceptIfAllowed(iConcept,modbuf,bCanSpeakDead,bCanSpeakIfSpeaking,pszOutResponseChosen,bufsize,filter);
}
else
return GetOuter()->SpeakConceptIfAllowed(iConcept,NULL,bCanSpeakDead,bCanSpeakIfSpeaking,pszOutResponseChosen,bufsize,filter);
}
And finally here is an example of the class being used
if(pPlayerAttacker)
{
CSpeakModifiers modifiers;
int nemesis_index = pPlayerAttacker->m_playerNemesis.Find(this);
if(nemesis_index != pPlayerAttacker->m_playerNemesis.InvalidIndex())
modifiers.AddModifier("revenge","true");
CBasePVK2BludgeonWeapon *pMelee = dynamic_cast<CBasePVK2BludgeonWeapon *>(subinfo.GetInflictor());
if(pMelee)
modifiers.AddModifier("attackdirection",pMelee->GetAttackDir() + 1);
if(subinfo.GetInflictor() && ( FClassnameIs(subinfo.GetInflictor(),"powderkeg") ||FClassnameIs(subinfo.GetInflictor(),"weapon_powderkeg") || FClassnameIs(subinfo.GetInflictor(),"weapon_chest") ))
modifiers.AddModifier("killweapon",subinfo.GetInflictor()->GetClassname());
modifiers.AddModifier("victimteam",GetTeamNumber());
modifiers.AddModifier("victimclass",GetPlayerClass() + 1);
pPlayerAttacker->ChoreoProp()->SpeakConceptIfAllowed( MP_CONCEPT_KILLED, &modifiers );
}