From ac5bb9098600cf5972b2ca41dc90983d7c04387b Mon Sep 17 00:00:00 2001 From: nuclear silo <58926275+nuclearsilo583@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:55:39 +0700 Subject: [PATCH] [Z:R] Implement Hitgroup Knockback config, Zombie Class specific knockback config. (#232) * update 2 decimal float for weapon knockback and hitgroup knockback config * update PackageScript, clean up checking hitgroup logic * add knockbback config for zombie class config * Simplify config, switch to new zclass storage & smart pointers --------- Co-authored-by: Vauff --- PackageScript | 1 + configs/zr/hitgroups.cfg.example | 47 ++++++++++++ configs/zr/playerclass.jsonc.example | 1 + src/cs2fixes.cpp | 4 + src/zombiereborn.cpp | 110 +++++++++++++++++++++++++-- src/zombiereborn.h | 26 ++++++- 6 files changed, 182 insertions(+), 7 deletions(-) create mode 100644 configs/zr/hitgroups.cfg.example diff --git a/PackageScript b/PackageScript index 2e084206..bd8cb922 100644 --- a/PackageScript +++ b/PackageScript @@ -64,6 +64,7 @@ builder.AddCopy(os.path.join(builder.sourcePath, 'cfg', MMSPlugin.plugin_name, ' builder.AddCopy(os.path.join(builder.sourcePath, 'cfg', MMSPlugin.plugin_name, 'maps', 'de_somemap.cfg'), mapcfg_folder) builder.AddCopy(os.path.join(builder.sourcePath, 'configs', 'zr', 'playerclass.jsonc.example'), zr_folder) builder.AddCopy(os.path.join(builder.sourcePath, 'configs', 'zr', 'weapons.cfg.example'), zr_folder) +builder.AddCopy(os.path.join(builder.sourcePath, 'configs', 'zr', 'hitgroups.cfg.example'), zr_folder) builder.AddCopy(os.path.join('gamedata', 'cs2fixes.games.txt'), gamedata_folder) # Add CS2Fixes-specific compiled asset files diff --git a/configs/zr/hitgroups.cfg.example b/configs/zr/hitgroups.cfg.example new file mode 100644 index 00000000..3a324929 --- /dev/null +++ b/configs/zr/hitgroups.cfg.example @@ -0,0 +1,47 @@ +// Attribute: Values: Description: +// ---------------------------------------------------------------------------- +// knockback decimal The knockback multiplier for this hitgroup. + +"Hitgroups" +{ + "Generic" + { + "knockback" "1.0" + } + "Head" + { + "knockback" "1.0" + } + "Chest" + { + "knockback" "1.0" + } + "Stomach" + { + "knockback" "1.0" + } + "LeftArm" + { + "knockback" "1.0" + } + "RightArm" + { + "knockback" "1.0" + } + "LeftLeg" + { + "knockback" "1.0" + } + "RightLeg" + { + "knockback" "1.0" + } + "Neck" + { + "knockback" "1.0" + } + "Gear" + { + "knockback" "1.0" + } +} diff --git a/configs/zr/playerclass.jsonc.example b/configs/zr/playerclass.jsonc.example index f0ba74d1..ee4fe1c6 100644 --- a/configs/zr/playerclass.jsonc.example +++ b/configs/zr/playerclass.jsonc.example @@ -72,6 +72,7 @@ "scale": 1.05, "speed": 1.0, "gravity": 1.0, + "knockback": 1.0, "admin_flag": "", "health_regen_count": 250, "health_regen_interval": 5.0 diff --git a/src/cs2fixes.cpp b/src/cs2fixes.cpp index 8b4310e9..4b685085 100644 --- a/src/cs2fixes.cpp +++ b/src/cs2fixes.cpp @@ -323,6 +323,7 @@ bool CS2Fixes::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool g_pUserPreferencesSystem = new CUserPreferencesSystem(); g_pUserPreferencesStorage = new CUserPreferencesREST(); g_pZRWeaponConfig = new ZRWeaponConfig(); + g_pZRHitgroupConfig = new ZRHitgroupConfig(); g_pEntityListener = new CEntityListener(); g_pIdleSystem = new CIdleSystem(); g_pPanoramaVoteHandler = new CPanoramaVoteHandler(); @@ -412,6 +413,9 @@ bool CS2Fixes::Unload(char *error, size_t maxlen) if (g_pZRWeaponConfig) delete g_pZRWeaponConfig; + + if (g_pZRHitgroupConfig) + delete g_pZRHitgroupConfig; if (g_pUserPreferencesSystem) delete g_pUserPreferencesSystem; diff --git a/src/zombiereborn.cpp b/src/zombiereborn.cpp index d22300c9..ee69af5a 100644 --- a/src/zombiereborn.cpp +++ b/src/zombiereborn.cpp @@ -69,6 +69,7 @@ static CHandle g_hTeamT; CZRPlayerClassManager* g_pZRPlayerClassManager = nullptr; ZRWeaponConfig *g_pZRWeaponConfig = nullptr; +ZRHitgroupConfig *g_pZRHitgroupConfig = nullptr; bool g_bEnableZR = false; static float g_flMaxZteleDistance = 150.0f; @@ -314,7 +315,8 @@ ZRHumanClass::ZRHumanClass(ordered_json jsonKeys, std::string szClassname) : ZRC ZRZombieClass::ZRZombieClass(ordered_json jsonKeys, std::string szClassname) : ZRClass(jsonKeys, szClassname, CS_TEAM_T), iHealthRegenCount(jsonKeys.value("health_regen_count", 0)), - flHealthRegenInterval(jsonKeys.value("health_regen_interval", 0)){}; + flHealthRegenInterval(jsonKeys.value("health_regen_interval", 0)), + flKnockback(jsonKeys.value("knockback", 1.0)){}; void ZRZombieClass::Override(ordered_json jsonKeys, std::string szClassname) { @@ -323,6 +325,8 @@ void ZRZombieClass::Override(ordered_json jsonKeys, std::string szClassname) iHealthRegenCount = jsonKeys["health_regen_count"].get(); if (jsonKeys.contains("health_regen_interval")) flHealthRegenInterval = jsonKeys["health_regen_interval"].get(); + if (jsonKeys.contains("knockback")) + flKnockback = jsonKeys["knockback"].get(); } bool ZRClass::IsApplicableTo(CCSPlayerController *pController) @@ -450,6 +454,11 @@ void CZRPlayerClassManager::LoadPlayerClass() Panic("%s has unspecified key: gravity\n", szClassName.c_str()); bMissingKey = true; } + /*if (!jsonClass.contains("knockback")) + { + Warning("%s has unspecified key: knockback\n", szClassName.c_str()); + bMissingKey = true; + }*/ if (!jsonClass.contains("admin_flag")) { Panic("%s has unspecified key: admin_flag\n", szClassName.c_str()); @@ -824,6 +833,7 @@ void ZR_OnLevelInit() }); g_pZRWeaponConfig->LoadWeaponConfig(); + g_pZRHitgroupConfig->LoadHitgroupConfig(); SetupCTeams(); } @@ -844,7 +854,7 @@ void ZRWeaponConfig::LoadWeaponConfig() { const char *pszWeaponName = pKey->GetName(); bool bEnabled = pKey->GetBool("enabled", false); - float flKnockback= pKey->GetFloat("knockback", 0.0f); + float flKnockback = pKey->GetFloat("knockback", 1.0f); Message("%s knockback: %f\n", pszWeaponName, flKnockback); ZRWeapon *weapon = new ZRWeapon; if (!bEnabled) @@ -867,6 +877,76 @@ ZRWeapon* ZRWeaponConfig::FindWeapon(const char *pszWeaponName) return nullptr; } +void ZRHitgroupConfig::LoadHitgroupConfig() +{ + m_HitgroupMap.Purge(); + KeyValues* pKV = new KeyValues("Hitgroups"); + KeyValues::AutoDelete autoDelete(pKV); + + const char *pszPath = "addons/cs2fixes/configs/zr/hitgroups.cfg"; + + if (!pKV->LoadFromFile(g_pFullFileSystem, pszPath)) + { + Warning("Failed to load %s\n", pszPath); + return; + } + for (KeyValues* pKey = pKV->GetFirstSubKey(); pKey; pKey = pKey->GetNextKey()) + { + const char *pszHitgroupName = pKey->GetName(); + float flKnockback= pKey->GetFloat("knockback", 1.0f); + int iIndex = -1; + + if (!V_strcasecmp(pszHitgroupName, "Generic")) + iIndex = 0; + else if (!V_strcasecmp(pszHitgroupName, "Head")) + iIndex = 1; + else if (!V_strcasecmp(pszHitgroupName, "Chest")) + iIndex = 2; + else if (!V_strcasecmp(pszHitgroupName, "Stomach")) + iIndex = 3; + else if (!V_strcasecmp(pszHitgroupName, "LeftArm")) + iIndex = 4; + else if (!V_strcasecmp(pszHitgroupName, "RightArm")) + iIndex = 5; + else if (!V_strcasecmp(pszHitgroupName, "LeftLeg")) + iIndex = 6; + else if (!V_strcasecmp(pszHitgroupName, "RightLeg")) + iIndex = 7; + else if (!V_strcasecmp(pszHitgroupName, "Neck")) + iIndex = 8; + else if (!V_strcasecmp(pszHitgroupName, "Gear")) + iIndex = 10; + + if (iIndex == -1) + { + Panic("Failed to load hitgroup %s, invalid name!", pszHitgroupName); + continue; + } + + std::shared_ptr hitGroup = std::make_shared(); + + hitGroup->flKnockback = flKnockback; + m_HitgroupMap.Insert(iIndex, hitGroup); + Message("Loaded hitgroup %s at index %d with %f knockback\n", pszHitgroupName, iIndex, hitGroup->flKnockback); + } + + return; +} + +std::shared_ptr ZRHitgroupConfig::FindHitgroupIndex(int iIndex) +{ + uint16 index = m_HitgroupMap.Find(iIndex); + //Message("We are finding hitgroup index with index: %d and index is: %d\n", iIndex, index); + + if (m_HitgroupMap.IsValidIndex(index)) + { + //Message("We found valid index with (m_HitgroupMap[index]): %d\n", m_HitgroupMap[index]); + return m_HitgroupMap[index]; + } + + return nullptr; +} + void ZR_RespawnAll() { for (int i = 0; i < gpGlobals->maxClients; i++) @@ -997,17 +1077,22 @@ void ZR_OnPlayerSpawn(CCSPlayerController* pController) }); } -void ZR_ApplyKnockback(CCSPlayerPawn *pHuman, CCSPlayerPawn *pVictim, int iDamage, const char *szWeapon) +void ZR_ApplyKnockback(CCSPlayerPawn *pHuman, CCSPlayerPawn *pVictim, int iDamage, const char *szWeapon, int hitgroup, float classknockback) { ZRWeapon *pWeapon = g_pZRWeaponConfig->FindWeapon(szWeapon); + std::shared_ptr pHitgroup = g_pZRHitgroupConfig->FindHitgroupIndex(hitgroup); // player shouldn't be able to pick up that weapon in the first place, but just in case if (!pWeapon) return; float flWeaponKnockbackScale = pWeapon->flKnockback; - + float flHitgroupKnockbackScale = 1.0f; + + if (pHitgroup) + flHitgroupKnockbackScale = pHitgroup->flKnockback; + Vector vecKnockback; AngleVectors(pHuman->m_angEyeAngles(), &vecKnockback); - vecKnockback *= (iDamage * g_flKnockbackScale * flWeaponKnockbackScale); + vecKnockback *= (iDamage * g_flKnockbackScale * flWeaponKnockbackScale * flHitgroupKnockbackScale * classknockback); pVictim->m_vecAbsVelocity = pVictim->m_vecAbsVelocity() + vecKnockback; } @@ -1496,13 +1581,26 @@ void ZR_OnPlayerHurt(IGameEvent* pEvent) CCSPlayerController *pVictimController = (CCSPlayerController*)pEvent->GetPlayerController("userid"); const char* szWeapon = pEvent->GetString("weapon"); int iDmgHealth = pEvent->GetInt("dmg_health"); + int iHitGroup = pEvent->GetInt("hitgroup"); // grenade and molotov knockbacks are handled by TakeDamage detours if (!pAttackerController || !pVictimController || !V_strncmp(szWeapon, "inferno", 7) || !V_strncmp(szWeapon, "hegrenade", 9)) return; if (pAttackerController->m_iTeamNum() == CS_TEAM_CT && pVictimController->m_iTeamNum() == CS_TEAM_T) - ZR_ApplyKnockback((CCSPlayerPawn*)pAttackerController->GetPawn(), (CCSPlayerPawn*)pVictimController->GetPawn(), iDmgHealth, szWeapon); + { + float flClassKnockback = 1.0f; + + if (pVictimController->GetZEPlayer()) + { + std::shared_ptr activeClass = pVictimController->GetZEPlayer()->GetActiveZRClass(); + + if (activeClass && activeClass->iTeam == CS_TEAM_T) + flClassKnockback = static_pointer_cast(activeClass)->flKnockback; + } + + ZR_ApplyKnockback((CCSPlayerPawn*)pAttackerController->GetPawn(), (CCSPlayerPawn*)pVictimController->GetPawn(), iDmgHealth, szWeapon, iHitGroup, flClassKnockback); + } } void ZR_OnPlayerDeath(IGameEvent* pEvent) diff --git a/src/zombiereborn.h b/src/zombiereborn.h index b2575d66..dcac9097 100644 --- a/src/zombiereborn.h +++ b/src/zombiereborn.h @@ -144,10 +144,12 @@ struct ZRZombieClass : ZRClass { int iHealthRegenCount; float flHealthRegenInterval; + float flKnockback; ZRZombieClass(std::shared_ptr pClass) : ZRClass(pClass, CS_TEAM_T), iHealthRegenCount(pClass->iHealthRegenCount), - flHealthRegenInterval(pClass->flHealthRegenInterval){}; + flHealthRegenInterval(pClass->flHealthRegenInterval), + flKnockback(pClass->flKnockback){}; ZRZombieClass(ordered_json jsonKeys, std::string szClassname); void PrintInfo() { @@ -173,6 +175,7 @@ struct ZRZombieClass : ZRClass "\tscale: %f\n" "\tspeed: %f\n" "\tgravity: %f\n" + "\tknockback: %f\n" "\tadmin flag: %d\n" "\thealth_regen_count: %d\n" "\thealth_regen_interval: %f\n", @@ -183,6 +186,7 @@ struct ZRZombieClass : ZRClass flScale, flSpeed, flGravity, + flKnockback, iAdminFlag, iHealthRegenCount, flHealthRegenInterval); @@ -242,6 +246,11 @@ struct ZRWeapon float flKnockback; }; +struct ZRHitgroup +{ + float flKnockback; +}; + class ZRWeaponConfig { public: @@ -255,7 +264,22 @@ class ZRWeaponConfig CUtlMap m_WeaponMap; }; + +class ZRHitgroupConfig +{ +public: + ZRHitgroupConfig() + { + m_HitgroupMap.SetLessFunc(DefLessFunc(uint32)); + }; + void LoadHitgroupConfig(); + std::shared_ptr FindHitgroupIndex(int iIndex); +private: + CUtlMap> m_HitgroupMap; +}; + extern ZRWeaponConfig *g_pZRWeaponConfig; +extern ZRHitgroupConfig *g_pZRHitgroupConfig; extern CZRPlayerClassManager* g_pZRPlayerClassManager; extern bool g_bEnableZR;