Phil25/RTD

Refactor perk definition system

Opened this issue · 0 comments

Overview

Perks define the call method and now the init method which correspond to the function names in code. With the upcoming programmatic preconditions (#71), there will be a third such function, and probably even more upcoming. This can be restructured into a single very generic function which accepts a enum of an action call, to which perk implementation will respond accordingly. This should also be exposed via API.

I will skip over a lot of thoughts I had about this refactor, mostly because I don't want to bother formalizing them, but also because this is a huge undertaking and many things will need to be investigated during implementation. For now, I needed a place to store the PoC below, so, if anyone interested happens to read it, hopefully it will clear some things up at least.

Proof of Concept

Include file

enum RTDPerkCall
{
	RTDPerkCall_Init,
	RTDPerkCall_Apply,
	RTDPerkCall_Remove,
	RTDPerkCall_IsValidForClient,
	RTDPerkCall_GetSettingType,
	RTDPerkCall_VerifySettingValue,
}

enum RTDSettingType
{
	RTDSettingType_Unknown,
	RTDSettingType_Any,
	RTDSettingType_Integer,
	RTDSettingType_Float,
	RTDSettingType_String
}

enum RTDSettingVerification
{
	RTDSettingVerification_OK,
	RTDSettingVerification_Warning,
	RTDSettingVerification_Fatal
}

methodmap RTDPerkBase
{
	public void Apply(const int client)
	{
		char sCaller[32];
		GetCallerName(sCaller, sizeof(sCaller));
		LogStackTrace("Perk \"%s\" has no Apply method defined", sCaller);
	}

	public void Remove(const int client)
	{
		char sCaller[32];
		GetCallerName(sCaller, sizeof(sCaller));
		LogStackTrace("Perk \"%s\" has no Remove method defined", sCaller);
	}

	public bool IsValidForClient(const int client, const TFClassType eClass)
	{
		return true;
	}

	public RTDSettingType GetSettingType(const char[] sSetting)
	{
		return RTDSettingType_Any;
	}

	public bool VerifySettingValue(const char[] sSetting, const char[] sValue, char[] sError, const int iErrorLen)
	{
		return true;
	}
}

#define RTD2_DECLARE_PERK_TYPE(%1) \
	int %1(RTDPerkCall ePerkCall, const int client, const TFClassType eClass, char[] sFeedback, const int iFeedbackLen, const char[] sString1="", const char[] sString2="") \
	{ \
		switch (ePerkCall) \
		{ \
		case RTDPerkCall_Apply: view_as<%1_>(0).Apply(client); \
		case RTDPerkCall_Remove: view_as<%1_>(0).Remove(client); \
		case RTDPerkCall_IsValidForClient: return view_as<%1_>(0).IsValidForClient(client, eClass); \
		case RTDPerkCall_GetSettingType: return view_as<int>(view_as<%1_>(0).GetSettingType(sString1)); \
		case RTDPerkCall_VerifySettingValue: return view_as<%1_>(0).VerifySettingValue(sString1, sString2, sFeedback, iFeedbackLen); \
		} \
		return 0; \
	} \
	methodmap %1_ < RTDPerkBase

Perk implementation

RTD2_DECLARE_PERK_TYPE(MyCustomPerk)
{
	public void Apply(const int client)
	{
		PrintToServer("Applying MyCustomPerk on %d", client);
	}

	public void Remove(const int client)
	{
		PrintToServer("Removing MyCustomPerk from %d", client);
	}

	public bool IsValidForClient(const int client, const TFClassType eClass)
	{
		return eClass == TFClass_Scout;
	}

	public RTDSettingType GetSettingType(const char[] sSetting)
	{
		if (StrEqual(sSetting, "amount"))
		{
			return RTDSettingType_Integer;
		}

		return RTDSettingType_Unknown;
	}

	public bool VerifySettingValue(const char[] sSetting, const char[] sValue, char[] sError, const int iErrorLen)
	{
		if (StrEqual(sSetting, "amount") && !(1 <= StringToInt(sValue) <= 3))
		{
			Format(sError, iErrorLen, "amount should be either 1, 2 or 3 (is: %s)", sValue);
			return false;
		}

		return true;
	}
}

Calls from RTD source

char sFeedback[128];
MyCustomPerk(RTDPerkCall_Apply, 4, TFClass_DemoMan, sFeedback, sizeof(sFeedback));
MyCustomPerk(RTDPerkCall_Remove, 5, TFClass_DemoMan, sFeedback, sizeof(sFeedback));
MyCustomPerk(RTDPerkCall_IsValidForClient, 5, TFClass_DemoMan, sFeedback, sizeof(sFeedback));
MyCustomPerk(RTDPerkCall_GetSettingType, 5, TFClass_DemoMan, sFeedback, sizeof(sFeedback), "amount");
MyCustomPerk(RTDPerkCall_VerifySettingValue, 5, TFClass_DemoMan, sFeedback, sizeof(sFeedback), "amount", "3");
PrintToServer("Feedback: %s", sFeedback);