Skip to content

Commit

Permalink
Zclass implementation with user prefs
Browse files Browse the repository at this point in the history
* Implement !zclass command using user preferences.
* Replace class assignment to use zclass preferences or assign default.
* Add admin flags to Z:R classes and a method to check applicability.
* Fix Health Regen value and interval for zombie classes overriding speed / gravity.
  • Loading branch information
e-n-v-i authored and xen-000 committed Jan 29, 2024
1 parent 2b0d1fa commit 3ecc172
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 24 deletions.
4 changes: 4 additions & 0 deletions configs/zr/playerclass.cfg.example
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"scale" "1.0"
"speed" "1.0"
"gravity" "1.0"
"admin_flag" "" // Allowed for all players
}
"HumanClass2"
{
Expand All @@ -26,6 +27,7 @@
"base" "HumanClass1" // optional

"model" "characters/models/ctm_sas/ctm_sas.vmdl" // overrides HumanClass1 model
"admin_flag" "b" // Only enabled in !zclass for ADMFLAG_GENERIC
}
"HumanClass3"
{
Expand All @@ -52,6 +54,7 @@
"scale" "1.05"
"speed" "1.0"
"gravity" "1.0"
"admin_flag" ""

"health_regen_count" "250"
"health_regen_interval" "5.0"
Expand All @@ -69,6 +72,7 @@
"scale" "1.15"
"speed" "1.0"
"gravity" "1.0"
"admin_flag" ""

"health_regen_count" "500"
"health_regen_interval" "5.0"
Expand Down
3 changes: 1 addition & 2 deletions src/adminsystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,10 +146,9 @@ class CAdminSystem
bool ApplyInfractions(ZEPlayer *player);
bool FindAndRemoveInfraction(ZEPlayer *player, CInfractionBase::EInfractionType type);
CAdmin *FindAdmin(uint64 iSteamID);

private:
uint64 ParseFlags(const char* pszFlags);

private:
CUtlVector<CAdmin> m_vecAdmins;
CUtlVector<CInfractionBase*> m_vecInfractions;
};
Expand Down
153 changes: 140 additions & 13 deletions src/zombiereborn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "entity/cgamerules.h"
#include "entity/services.h"
#include "entity/cteam.h"
#include "user_preferences.h"
#include <sstream>

#include "tier0/memdbgon.h"
Expand Down Expand Up @@ -89,6 +90,15 @@ void ZR_Precache(IEntityResourceManifest* pResourceManifest)
g_pZRPlayerClassManager->PrecacheModels(pResourceManifest);
}

bool ZRClass::IsApplicableTo(CCSPlayerController *pController)
{
if (!V_stricmp(szClassName.c_str(), "MotherZombie")) return false;
ZEPlayer* pPlayer = pController->GetZEPlayer();
if (!pPlayer) return false;
if (!pPlayer->IsAdminFlagSet(iAdminFlag)) return false;
return true;
}

void CZRPlayerClassManager::PrecacheModels(IEntityResourceManifest* pResourceManifest)
{
FOR_EACH_MAP_FAST(m_ZombieClassMap, i)
Expand Down Expand Up @@ -190,6 +200,11 @@ void CZRPlayerClassManager::LoadPlayerClass()
Warning("%s has unspecified keyvalue: gravity\n", pszClassName);
bMissingKey = true;
}
if (!pSubKey->FindKey("admin_flag"))
{
Warning("%s has unspecified keyvalue: admin_flag\n", pszClassName);
bMissingKey = true;
}
}
if (bMissingKey)
continue;
Expand Down Expand Up @@ -296,14 +311,28 @@ void CZRPlayerClassManager::ApplyHumanClass(ZRHumanClass *pClass, CCSPlayerPawn
CZRRegenTimer::StopRegen(pController);
}

void CZRPlayerClassManager::ApplyDefaultHumanClass(CCSPlayerPawn *pPawn)
void CZRPlayerClassManager::ApplyPreferredOrDefaultHumanClass(CCSPlayerPawn *pPawn)
{
if (m_vecHumanDefaultClass.Count() == 0)
{
Warning("Missing default human class!!!\n");
CCSPlayerController *pController = CCSPlayerController::FromPawn(pPawn);
if (!pController) return;

// Get the human class user preference, or default if no class is set
int iSlot = pController->GetPlayerSlot();
ZRHumanClass* humanClass = nullptr;
const char* sPreferredHumanClass = g_pUserPreferencesSystem->GetPreference(iSlot, HUMAN_CLASS_KEY_NAME);

// If the preferred human class exists and can be applied, override the default
uint16 index = m_HumanClassMap.Find(hash_32_fnv1a_const(sPreferredHumanClass));
if (m_HumanClassMap.IsValidIndex(index) && m_HumanClassMap[index]->IsApplicableTo(pController)) {
humanClass = m_HumanClassMap[index];
} else if (m_vecHumanDefaultClass.Count()) {
humanClass = m_vecHumanDefaultClass[rand() % m_vecHumanDefaultClass.Count()];
} else if (!humanClass) {
Warning("Missing default human class or valid preferences!\n");
return;
}
ApplyHumanClass(m_vecHumanDefaultClass[rand() % m_vecHumanDefaultClass.Count()], pPawn);

ApplyHumanClass(humanClass, pPawn);
}

ZRZombieClass* CZRPlayerClassManager::GetZombieClass(const char *pszClassName)
Expand All @@ -322,14 +351,46 @@ void CZRPlayerClassManager::ApplyZombieClass(ZRZombieClass *pClass, CCSPlayerPaw
CZRRegenTimer::StartRegen(pClass->flHealthRegenInterval, pClass->iHealthRegenCount, pController);
}

void CZRPlayerClassManager::ApplyDefaultZombieClass(CCSPlayerPawn *pPawn)
void CZRPlayerClassManager::ApplyPreferredOrDefaultZombieClass(CCSPlayerPawn *pPawn)
{
if (m_vecZombieDefaultClass.Count() == 0)
{
Warning("Missing default zombie class!!!\n");
CCSPlayerController *pController = CCSPlayerController::FromPawn(pPawn);
if (!pController) return;

// Get the zombie class user preference, or default if no class is set
int iSlot = pController->GetPlayerSlot();
ZRZombieClass* zombieClass = nullptr;
const char* sPreferredZombieClass = g_pUserPreferencesSystem->GetPreference(iSlot, ZOMBIE_CLASS_KEY_NAME);

// If the preferred zombie class exists and can be applied, override the default
uint16 index = m_ZombieClassMap.Find(hash_32_fnv1a_const(sPreferredZombieClass));
if (m_ZombieClassMap.IsValidIndex(index) && m_ZombieClassMap[index]->IsApplicableTo(pController)) {
zombieClass = m_ZombieClassMap[index];
} else if (m_vecZombieDefaultClass.Count()) {
zombieClass = m_vecZombieDefaultClass[rand() % m_vecZombieDefaultClass.Count()];
} else if (!zombieClass) {
Warning("Missing default zombie class or valid preferences!\n");
return;
}
ApplyZombieClass(m_vecZombieDefaultClass[rand() % m_vecZombieDefaultClass.Count()], pPawn);

ApplyZombieClass(zombieClass, pPawn);
}

void CZRPlayerClassManager::GetZRClassList(const char* sTeam, CUtlVector<ZRClass*> &vecClasses)
{
if (!V_stricmp(sTeam, "zombie"))
{
FOR_EACH_MAP_FAST(m_ZombieClassMap, i)
{
vecClasses.AddToTail(m_ZombieClassMap[i]);
}
}
else if (!V_stricmp(sTeam, "human"))
{
FOR_EACH_MAP_FAST(m_HumanClassMap, i)
{
vecClasses.AddToTail(m_HumanClassMap[i]);
}
}
}

float CZRRegenTimer::s_flNextExecution;
Expand Down Expand Up @@ -675,7 +736,7 @@ void ZR_Cure(CCSPlayerController *pTargetController)
if (!pTargetPawn)
return;

g_pZRPlayerClassManager->ApplyDefaultHumanClass(pTargetPawn);
g_pZRPlayerClassManager->ApplyPreferredOrDefaultHumanClass(pTargetPawn);
}

void ZR_Infect(CCSPlayerController *pAttackerController, CCSPlayerController *pVictimController, bool bDontBroadcast)
Expand All @@ -694,7 +755,7 @@ void ZR_Infect(CCSPlayerController *pAttackerController, CCSPlayerController *pV

ZR_StripAndGiveKnife(pVictimPawn);

g_pZRPlayerClassManager->ApplyDefaultZombieClass(pVictimPawn);
g_pZRPlayerClassManager->ApplyPreferredOrDefaultZombieClass(pVictimPawn);
}

void ZR_InfectMotherZombie(CCSPlayerController *pVictimController)
Expand All @@ -712,7 +773,7 @@ void ZR_InfectMotherZombie(CCSPlayerController *pVictimController)
else
{
//Warning("Missing mother zombie class!!!\n");
g_pZRPlayerClassManager->ApplyDefaultZombieClass(pVictimPawn);
g_pZRPlayerClassManager->ApplyPreferredOrDefaultZombieClass(pVictimPawn);
}
}

Expand Down Expand Up @@ -1220,4 +1281,70 @@ CON_COMMAND_CHAT(ztele, "teleport to spawn")

return -1.0f;
});
}

CON_COMMAND_CHAT(zclass, "find and select your Z:R class")
{
// Silently return so the command is completely hidden
if (!g_bEnableZR)
return;

if (!player)
{
ClientPrint(player, HUD_PRINTCONSOLE, ZR_PREFIX "You cannot use this command from the server console.");
return;
}

if (args.ArgC() < 2)
{
ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "You need to specify a team and class: %s <zombie or human> <class name>.", args[0]);
return;
}

// If no team or both team are specified, error out
bool bIsZombie = !V_strcasecmp(args[1], "zombie") || !V_strcasecmp(args[1], "zm")|| !V_strcasecmp(args[1], "z");
bool bIsHuman = !V_strcasecmp(args[1], "human") || !V_strcasecmp(args[1], "hm") || !V_strcasecmp(args[1], "h");
if (bIsZombie == bIsHuman) {
ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "You need to specify a team and class: %s <zombie|zm|z or human|hm|h> <class name>.", args[0]);
}

CUtlVector<ZRClass*> teamClasses;
const char* sPreferenceKey = bIsZombie ? ZOMBIE_CLASS_KEY_NAME : HUMAN_CLASS_KEY_NAME;
const char* sTeamName = bIsZombie ? "Zombie" : "Human";
g_pZRPlayerClassManager->GetZRClassList(sTeamName, teamClasses);
int iSlot = player->GetPlayerSlot();

// If a class is passed, find it among the list of classes and store -- otherwise print available classes
if (args.ArgC() > 2) {
FOR_EACH_VEC(teamClasses, i)
{
const char* sClassName = teamClasses[i]->szClassName.c_str();
bool bClassMatches = !V_stricmp(sClassName, args[2]);
bool bIsApplicable = teamClasses[i]->IsApplicableTo(player);
if (bClassMatches && bIsApplicable) {
ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "Your %s class is now set to '%s'.", sTeamName, sClassName);
g_pUserPreferencesSystem->SetPreference(iSlot, sPreferenceKey, sClassName);
return;
}
}
ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "No available %s classes matched '%s'.", sTeamName, args[2]);
return;
} else {
const char* sCurrentClass = g_pUserPreferencesSystem->GetPreference(iSlot, sPreferenceKey);
if (sCurrentClass[0] != '\0')
{
ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "Your current %s class is: %s. Available classes:", sTeamName, sCurrentClass);
}
else
{
ClientPrint(player, HUD_PRINTTALK, ZR_PREFIX "Available %s classes:", sTeamName);
}

FOR_EACH_VEC(teamClasses, i)
{
if (teamClasses[i]->IsApplicableTo(player)) {
ClientPrint(player, HUD_PRINTTALK, "- %s", teamClasses[i]->szClassName.c_str());
}
}
}
}
35 changes: 26 additions & 9 deletions src/zombiereborn.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@
*/

#pragma once

#include "adminsystem.h"
#include "eventlistener.h"
#include "ctimer.h"
#include "gamesystem.h"
#include "entity/ccsplayercontroller.h"
#include "entity/ccsplayerpawn.h"

#define ZR_PREFIX " \4[Zombie:Reborn]\1 "
#define HUMAN_CLASS_KEY_NAME "zr_human_class"
#define ZOMBIE_CLASS_KEY_NAME "zr_zombie_class"

enum class EZRRoundState
{
Expand All @@ -51,6 +53,7 @@ struct ZRClass
float flScale;
float flSpeed;
float flGravity;
uint64 iAdminFlag;
ZRClass(ZRClass *pClass) :
szClassName(pClass->szClassName),
iHealth(pClass->iHealth),
Expand All @@ -59,7 +62,8 @@ struct ZRClass
szColor(pClass->szColor),
flScale(pClass->flScale),
flSpeed(pClass->flSpeed),
flGravity(pClass->flGravity){};
flGravity(pClass->flGravity),
iAdminFlag(pClass->iAdminFlag){};

ZRClass(KeyValues *pKeys) :
szClassName(std::string(pKeys->GetName())),
Expand All @@ -69,7 +73,10 @@ struct ZRClass
szColor(std::string(pKeys->GetString("color", ""))),
flScale(pKeys->GetFloat("scale", 0)),
flSpeed(pKeys->GetFloat("speed", 0)),
flGravity(pKeys->GetFloat("gravity", 0)){};
flGravity(pKeys->GetFloat("gravity", 0)),
iAdminFlag(g_pAdminSystem->ParseFlags(
pKeys->GetString("admin_flag", "")
)){};
void PrintInfo()
{
Message(
Expand All @@ -80,15 +87,17 @@ struct ZRClass
"\tcolor: %s\n"
"\tscale: %f\n"
"\tspeed: %f\n"
"\tgravity: %f\n",
"\tgravity: %f\n"
"\admin flag: %llu\n",
szClassName.c_str(),
iHealth,
szModelPath.c_str(),
iSkin,
szColor.c_str(),
flScale,
flSpeed,
flGravity);
flGravity,
iAdminFlag);
};
void Override(KeyValues *pKeys)
{
Expand All @@ -107,7 +116,12 @@ struct ZRClass
flSpeed = pKeys->GetFloat("speed", 0);
if (pKeys->FindKey("gravity"))
flGravity = pKeys->GetFloat("gravity", 0);
if (pKeys->FindKey("admin_flag"))
iAdminFlag = g_pAdminSystem->ParseFlags(
pKeys->GetString("admin_flag", "")
);
};
bool IsApplicableTo(CCSPlayerController *pController);
};


Expand Down Expand Up @@ -140,6 +154,7 @@ struct ZRZombieClass : ZRClass
"\tscale: %f\n"
"\tspeed: %f\n"
"\tgravity: %f\n"
"\admin flag: %d\n"
"\thealth_regen_count: %d\n"
"\thealth_regen_interval: %f\n",
szClassName.c_str(),
Expand All @@ -150,16 +165,17 @@ struct ZRZombieClass : ZRClass
flScale,
flSpeed,
flGravity,
iAdminFlag,
iHealthRegenCount,
flHealthRegenInterval);
};
void Override(KeyValues *pKeys)
{
ZRClass::Override(pKeys);
if (pKeys->FindKey("health_regen_count"))
flSpeed = pKeys->GetInt("health_regen_count", 0);
iHealthRegenCount = pKeys->GetInt("health_regen_count", 0);
if (pKeys->FindKey("health_regen_interval"))
flGravity = pKeys->GetFloat("health_regen_interval", 0);
flHealthRegenInterval = pKeys->GetFloat("health_regen_interval", 0);
};
};

Expand All @@ -174,11 +190,12 @@ class CZRPlayerClassManager
void LoadPlayerClass();
ZRHumanClass* GetHumanClass(const char *pszClassName);
void ApplyHumanClass(ZRHumanClass *pClass, CCSPlayerPawn *pPawn);
void ApplyDefaultHumanClass(CCSPlayerPawn *pPawn);
void ApplyPreferredOrDefaultHumanClass(CCSPlayerPawn *pPawn);
ZRZombieClass* GetZombieClass(const char*pszClassName);
void ApplyZombieClass(ZRZombieClass *pClass, CCSPlayerPawn *pPawn);
void ApplyDefaultZombieClass(CCSPlayerPawn *pPawn);
void ApplyPreferredOrDefaultZombieClass(CCSPlayerPawn *pPawn);
void PrecacheModels(IEntityResourceManifest* pResourceManifest);
void GetZRClassList(const char* sTeam, CUtlVector<ZRClass*> &vecClasses);
private:
void ApplyBaseClass(ZRClass* pClass, CCSPlayerPawn *pPawn);
CUtlVector<ZRZombieClass*> m_vecZombieDefaultClass;
Expand Down

0 comments on commit 3ecc172

Please sign in to comment.