CSpeakModifiers

From Valve Developer Community
Jump to navigation Jump to search
Wikipedia - Letter.png
This article has multiple issues. Please help improve it or discuss these issues on the talk page. (Learn how and when to remove these template messages)
Dead End - Icon.png
This article has no Wikipedia icon links to other VDC articles. Please help improve this article by adding links Wikipedia icon that are relevant to the context within the existing text.
January 2024

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 );
}