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