From 3df37bd2f8d13f12bb1f763985b31ddffced6551 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Thu, 7 Sep 2023 07:04:00 +0700 Subject: [PATCH 1/5] Implemented rarity of kill and assist for extended user message DeathMsg --- README.md | 4 +- dist/game.cfg | 16 ++ regamedll/dlls/API/CSPlayer.cpp | 31 +++ regamedll/dlls/cbase.cpp | 6 +- regamedll/dlls/cbase.h | 4 + regamedll/dlls/client.cpp | 2 +- regamedll/dlls/combat.cpp | 24 ++- regamedll/dlls/game.cpp | 4 + regamedll/dlls/game.h | 2 + regamedll/dlls/gamerules.h | 32 +++ regamedll/dlls/multiplay_gamerules.cpp | 217 +++++++++++++++++-- regamedll/dlls/player.cpp | 8 + regamedll/dlls/weapons.cpp | 2 +- regamedll/public/regamedll/API/CSEntity.h | 28 ++- regamedll/public/regamedll/API/CSPlayer.h | 22 +- regamedll/public/utlarray.h | 248 ++++++++++++++++++++++ 16 files changed, 623 insertions(+), 27 deletions(-) create mode 100644 regamedll/public/utlarray.h diff --git a/README.md b/README.md index 5d521515e..2cbc6f3bd 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,9 @@ This means that plugins that do binary code analysis (Orpheu for example) probab | mp_give_c4_frags | 3 | - | - | How many bonuses (frags) will get the player who defused or exploded the bomb. | | mp_hostages_rescued_ratio | 1.0 | 0.0 | 1.0 | Ratio of hostages rescued to win the round. | | mp_legacy_vehicle_block | 1 | 0 | 1 | Legacy func_vehicle behavior when blocked by another entity.
`0` New behavior
`1` Legacy behavior | -| mp_dying_time | 3.0 | 0.0 | - | Time for switch to free observing after death.
`0` - disable spectating around death.
`>0.00001` - time delay to start spectate.
`NOTE`: The countdown starts when the player’s death animation is finished.| +| mp_dying_time | 3.0 | 0.0 | - | Time for switch to free observing after death.
`0` - disable spectating around death.
`>0.00001` - time delay to start spectate.
`NOTE`: The countdown starts when the player’s death animation is finished. | +| mp_deathmsg_flags | 7 | 0 | 7 | Sets a bitsum for extra information in the player's death message.
`0` disabled
`1` position where the victim died
`2` index of the assistant who helped the attacker kill the victim
`4` rarity classification bits, e.g., `blinkill`, `noscope`, `penetrated`, etc. | +| mp_assist_damage_threshold | 40 | 0 | 100 | Sets the percentage of damage needed to score an assist. | ## How to install zBot for CS 1.6? diff --git a/dist/game.cfg b/dist/game.cfg index fece0de56..ce0b3fc7f 100644 --- a/dist/game.cfg +++ b/dist/game.cfg @@ -537,3 +537,19 @@ mp_legacy_vehicle_block "1" // // Default value: "3.0" mp_dying_time "3.0" + +// Sets a bitsum for extra information in the player's death message +// +// 1 Position where the victim died +// 2 Index of the assistant who helped the attacker kill the victim +// 4 Rarity classification bits, e.g., blinkill, noscope, penetrated, etc +// +// Set to "0" to send no extra information about death +// +// Default value: "7" +mp_deathmsg_flags "7" + +// Sets the percentage of damage needed to score an assist +// +// Default value: "40" +mp_assist_damage_threshold "40" diff --git a/regamedll/dlls/API/CSPlayer.cpp b/regamedll/dlls/API/CSPlayer.cpp index 36c1d258b..4c7d22e26 100644 --- a/regamedll/dlls/API/CSPlayer.cpp +++ b/regamedll/dlls/API/CSPlayer.cpp @@ -570,6 +570,7 @@ void CCSPlayer::OnSpawn() { m_bGameForcingRespawn = false; m_flRespawnPending = 0.0f; + m_DamageList.Clear(); } void CCSPlayer::OnKilled() @@ -587,3 +588,33 @@ void CCSPlayer::OnKilled() } #endif } + +void CCSPlayer::OnConnect() +{ + ResetVars(); + m_iUserID = GETPLAYERUSERID(BasePlayer()->edict()); +} + +// Remember this amount of damage that we dealt for stats +void CCSPlayer::RecordDamage(CBasePlayer *pAttacker, float flDamage, float flFlashDurationTime) +{ + if (!pAttacker || !pAttacker->IsPlayer()) + return; + + int attackerIndex = pAttacker->entindex() - 1; + if (attackerIndex < 0 || attackerIndex >= MAX_CLIENTS) + return; + + CCSPlayer *pCSAttacker = pAttacker->CSPlayer(); + + // Accumulate damage + CDamageRecord_t &record = m_DamageList[attackerIndex]; + if (record.flDamage > 0 && record.userId != pCSAttacker->m_iUserID) + record.flDamage = 0; // reset damage if attacker became another client + + record.flDamage += flDamage; + record.userId = pCSAttacker->m_iUserID; + + if (flFlashDurationTime > 0) + record.flFlashDurationTime = gpGlobals->time + flFlashDurationTime; +} diff --git a/regamedll/dlls/cbase.cpp b/regamedll/dlls/cbase.cpp index b1cbe149e..9e14bf43e 100644 --- a/regamedll/dlls/cbase.cpp +++ b/regamedll/dlls/cbase.cpp @@ -1242,7 +1242,7 @@ bool EXT_FUNC IsPenetrableEntity_default(Vector &vecSrc, Vector &vecEnd, entvars LINK_HOOK_CLASS_CHAIN(VectorRef, CBaseEntity, FireBullets3, (VectorRef vecSrc, VectorRef vecDirShooting, float vecSpread, float flDistance, int iPenetration, int iBulletType, int iDamage, float flRangeModifier, entvars_t *pevAttacker, bool bPistol, int shared_rand), vecSrc, vecDirShooting, vecSpread, flDistance, iPenetration, iBulletType, iDamage, flRangeModifier, pevAttacker, bPistol, shared_rand) - + // Go to the trouble of combining multiple pellets into a single damage call. // This version is used by Players, uses the random seed generator to sync client and server side shots. VectorRef CBaseEntity::__API_HOOK(FireBullets3)(VectorRef vecSrc, VectorRef vecDirShooting, float vecSpread, float flDistance, int iPenetration, int iBulletType, int iDamage, float flRangeModifier, entvars_t *pevAttacker, bool bPistol, int shared_rand) @@ -1340,6 +1340,7 @@ VectorRef CBaseEntity::__API_HOOK(FireBullets3)(VectorRef vecSrc, VectorRef vecD float flDamageModifier = 0.5; + int iStartPenetration = iPenetration; while (iPenetration != 0) { ClearMultiDamage(); @@ -1400,9 +1401,11 @@ VectorRef CBaseEntity::__API_HOOK(FireBullets3)(VectorRef vecSrc, VectorRef vecD default: break; } + if (tr.flFraction != 1.0f) { CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit); + int iPenetrationCur = iPenetration; iPenetration--; flCurrentDistance = tr.flFraction * flDistance; @@ -1459,6 +1462,7 @@ VectorRef CBaseEntity::__API_HOOK(FireBullets3)(VectorRef vecSrc, VectorRef vecD flDistance = (flDistance - flCurrentDistance) * flDistanceModifier; vecEnd = vecSrc + (vecDir * flDistance); + pEntity->SetDmgPenetrationLevel(iStartPenetration - iPenetrationCur); pEntity->TraceAttack(pevAttacker, iCurrentDamage, vecDir, &tr, (DMG_BULLET | DMG_NEVERGIB)); iCurrentDamage *= flDamageModifier; } diff --git a/regamedll/dlls/cbase.h b/regamedll/dlls/cbase.h index 5199d75b0..986933f52 100644 --- a/regamedll/dlls/cbase.h +++ b/regamedll/dlls/cbase.h @@ -242,6 +242,10 @@ class CBaseEntity { void SetBlocked(void (T::*pfn)(CBaseEntity *pOther)); void SetBlocked(std::nullptr_t); + void SetDmgPenetrationLevel(int iPenetrationLevel); + void ResetDmgPenetrationLevel(); + int GetDmgPenetrationLevel() const; + #ifdef REGAMEDLL_API CCSEntity *m_pEntity; CCSEntity *CSEntity() const; diff --git a/regamedll/dlls/client.cpp b/regamedll/dlls/client.cpp index 4f3245af3..0beebf4b1 100644 --- a/regamedll/dlls/client.cpp +++ b/regamedll/dlls/client.cpp @@ -730,7 +730,7 @@ void EXT_FUNC ClientPutInServer(edict_t *pEntity) } #ifdef REGAMEDLL_API - pPlayer->CSPlayer()->ResetVars(); + pPlayer->CSPlayer()->OnConnect(); #endif UTIL_ClientPrintAll(HUD_PRINTNOTIFY, "#Game_connected", (sName[0] != '\0') ? sName : ""); diff --git a/regamedll/dlls/combat.cpp b/regamedll/dlls/combat.cpp index 5e69f213c..8dceda8d4 100644 --- a/regamedll/dlls/combat.cpp +++ b/regamedll/dlls/combat.cpp @@ -16,12 +16,29 @@ void PlayerBlind(CBasePlayer *pPlayer, entvars_t *pevInflictor, entvars_t *pevAt } } - pPlayer->Blind(fadeTime * 0.33, fadeHold, fadeTime, alpha); + float flDurationTime = fadeTime * 0.33; + pPlayer->Blind(flDurationTime, fadeHold, fadeTime, alpha); if (TheBots) { TheBots->OnEvent(EVENT_PLAYER_BLINDED_BY_FLASHBANG, pPlayer); } + +#if defined(REGAMEDLL_API) && defined(REGAMEDLL_ADD) + float flAdjustedDamage; + if (alpha > 200) + { + flAdjustedDamage = fadeTime / 3; + flAdjustedDamage = fadeHold * 1.5; + } + else + { + flAdjustedDamage = fadeTime / 1.75; + flAdjustedDamage = fadeHold * 3.5; + } + + pPlayer->CSPlayer()->RecordDamage(CBasePlayer::Instance(pevAttacker), flAdjustedDamage * 16.0f, flDurationTime); +#endif } void RadiusFlash_TraceLine_hook(CBasePlayer *pPlayer, entvars_t *pevInflictor, entvars_t *pevAttacker, Vector &vecSrc, Vector &vecSpot, TraceResult *tr) @@ -101,7 +118,7 @@ void RadiusFlash(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, if (pPlayer->pev == pevAttacker || g_pGameRules->PlayerRelationship(pPlayer, CBaseEntity::Instance(pevAttacker)) == GR_TEAMMATE) continue; break; - } + } #endif if (tr.fStartSolid) { @@ -110,7 +127,6 @@ void RadiusFlash(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, } flAdjustedDamage = flDamage - (vecSrc - tr.vecEndPos).Length() * falloff; - if (flAdjustedDamage < 0) flAdjustedDamage = 0; @@ -303,6 +319,8 @@ void RadiusDamage(Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker if (tr.flFraction != 1.0f) flAdjustedDamage = 0.0f; + else + pEntity->SetDmgPenetrationLevel(1); } #endif } diff --git a/regamedll/dlls/game.cpp b/regamedll/dlls/game.cpp index 34d9d0ad6..a9e37a8b5 100644 --- a/regamedll/dlls/game.cpp +++ b/regamedll/dlls/game.cpp @@ -166,6 +166,8 @@ cvar_t sv_autobunnyhopping = { "sv_autobunnyhopping", "0", 0, 0.0f cvar_t sv_enablebunnyhopping = { "sv_enablebunnyhopping", "0", 0, 0.0f, nullptr }; cvar_t plant_c4_anywhere = { "mp_plant_c4_anywhere", "0", 0, 0.0f, nullptr }; cvar_t give_c4_frags = { "mp_give_c4_frags", "3", 0, 3.0f, nullptr }; +cvar_t deathmsg_flags = { "mp_deathmsg_flags", "7", 0, 7.0f, nullptr }; +cvar_t assist_damage_threshold = { "mp_assist_damage_threshold", "40", 0, 40.0f, nullptr }; cvar_t hostages_rescued_ratio = { "mp_hostages_rescued_ratio", "1.0", 0, 1.0f, nullptr }; @@ -423,6 +425,8 @@ void EXT_FUNC GameDLLInit() CVAR_REGISTER(&legacy_vehicle_block); CVAR_REGISTER(&dying_time); + CVAR_REGISTER(&deathmsg_flags); + CVAR_REGISTER(&assist_damage_threshold); // print version CONSOLE_ECHO("ReGameDLL version: " APP_VERSION "\n"); diff --git a/regamedll/dlls/game.h b/regamedll/dlls/game.h index bb9d9c1ca..b9b9253b9 100644 --- a/regamedll/dlls/game.h +++ b/regamedll/dlls/game.h @@ -195,6 +195,8 @@ extern cvar_t give_c4_frags; extern cvar_t hostages_rescued_ratio; extern cvar_t legacy_vehicle_block; extern cvar_t dying_time; +extern cvar_t deathmsg_flags; +extern cvar_t assist_damage_threshold; #endif diff --git a/regamedll/dlls/gamerules.h b/regamedll/dlls/gamerules.h index 00d0116ff..59415beaa 100644 --- a/regamedll/dlls/gamerules.h +++ b/regamedll/dlls/gamerules.h @@ -220,6 +220,34 @@ enum GR_NEUTRAL, }; +enum DeathMessageFlags +{ + // float[3] + // Position where the victim died + PLAYERDEATH_POSITION = 0x001, + + // byte + // Index of the assistant who helped the attacker kill the victim + PLAYERDEATH_ASSISTANT = 0x002, + + // short + // Rarity classification bitsums + // 0x001 - Attacker was blind + // 0x002 - Attacker killed victim from sniper rifle without scope + // 0x004 - Attacker killed victim through walls + PLAYERDEATH_KILLRARITY = 0x004 +}; + +enum KillRarity +{ + KILLRARITY_HEADSHOT = 0x001, // The killer player kills the victim with a headshot + KILLRARITY_KILLER_BLIND = 0x002, // The killer player was blind + KILLRARITY_NOSCOPE = 0x004, // The killer player kills the victim with a sniper rifle with no scope + KILLRARITY_PENETRATED = 0x008, // The killer player kills the victim through walls + KILLRARITY_THROUGH_SMOKE = 0x010, // The killer player kills the victim through smoke + KILLRARITY_ASSIST_FLASH = 0x020 // The killer player kills the victim with an assistant flashbang grenade +}; + class CItem; class CGameRules @@ -698,6 +726,10 @@ class CHalfLifeMultiplay: public CGameRules VFUNC bool HasRoundTimeExpired(); VFUNC bool IsBombPlanted(); + void SendDeathMessage(CBasePlayer *pAttacker, CBasePlayer *pVictim, CBasePlayer *pAssister, const char *killerWeaponName, int iDeathMessageFlags, int iRarityOfKill); + int GetRarityOfKill(CBasePlayer *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, const char *killerWeaponName, bool bAssistWithFlashbang); + CBasePlayer *CheckAssistsToKill(CBasePlayer *pVictim, CBasePlayer *pKiller, bool &bAssistWithFlashbang); + private: void MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(int iTeam); diff --git a/regamedll/dlls/multiplay_gamerules.cpp b/regamedll/dlls/multiplay_gamerules.cpp index 0c45149cb..d1d0418a1 100644 --- a/regamedll/dlls/multiplay_gamerules.cpp +++ b/regamedll/dlls/multiplay_gamerules.cpp @@ -2035,7 +2035,7 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(RestartRound)() #endif pPlayer->RoundRespawn(); - + #ifdef REGAMEDLL_ADD FireTargets("game_entity_restart", pPlayer, nullptr, USE_TOGGLE, 0.0); #endif @@ -3922,7 +3922,7 @@ LINK_HOOK_CLASS_VOID_CUSTOM_CHAIN(CHalfLifeMultiplay, CSGameRules, PlayerKilled, void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(PlayerKilled)(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pInflictor) { DeathNotice(pVictim, pKiller, pInflictor); -#ifdef REGAMEDLL_FIXES +#ifdef REGAMEDLL_FIXES pVictim->pev->flags &= ~FL_FROZEN; #endif pVictim->m_afPhysicsFlags &= ~PFLAG_ONTRAIN; @@ -4083,7 +4083,7 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, { // by default, the player is killed by the world const char *killer_weapon_name = "world"; - int killer_index = 0; + CBasePlayer *pAttacker = nullptr; #ifndef REGAMEDLL_FIXES // Hack to fix name change @@ -4094,14 +4094,13 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, // Is the killer a client? if (pKiller->flags & FL_CLIENT) { - killer_index = ENTINDEX(ENT(pKiller)); + pAttacker = CBasePlayer::Instance(pKiller); if (pevInflictor) { if (pevInflictor == pKiller) { // If the inflictor is the killer, then it must be their current weapon doing the damage - CBasePlayer *pAttacker = CBasePlayer::Instance(pKiller); if (pAttacker && pAttacker->IsPlayer()) { if (pAttacker->m_pActiveItem) @@ -4142,12 +4141,28 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, if (!TheTutor) { - MESSAGE_BEGIN(MSG_ALL, gmsgDeathMsg); - WRITE_BYTE(killer_index); // the killer - WRITE_BYTE(ENTINDEX(pVictim->edict())); // the victim - WRITE_BYTE(pVictim->m_bHeadshotKilled); // is killed headshot - WRITE_STRING(killer_weapon_name); // what they were killed by (should this be a string?) - MESSAGE_END(); + int iAllowDeathMessageFlags = (int)deathmsg_flags.value; // allow bitsums for extra information + int iDeathMessageFlags = PLAYERDEATH_POSITION; // set bitsum default + int iRarityOfKill = 0; + + CBasePlayer *pAssister = nullptr; + CBasePlayer *pFlasher = nullptr; + + if (pAttacker && pAttacker->IsPlayer()) + { + // Sets the flag PLAYERDEATH_ASSISTANT indicating the presence of a teammate who assisted in the kill + bool bAssistWithFlashbang; + if ((pAssister = CheckAssistsToKill(pVictim, pAttacker, bAssistWithFlashbang))) + iDeathMessageFlags |= PLAYERDEATH_ASSISTANT; + + iRarityOfKill = GetRarityOfKill(pAttacker, pVictim, pAssister, killer_weapon_name, bAssistWithFlashbang); + if (iRarityOfKill != 0) + iDeathMessageFlags |= PLAYERDEATH_KILLRARITY; // Attacker killed the victim in a rare way + } + + // Apply allowed death message flags to iDeathMessageFlags + iDeathMessageFlags &= iAllowDeathMessageFlags; + SendDeathMessage(pAttacker, pVictim, pAssister, killer_weapon_name, iDeathMessageFlags, iRarityOfKill); } // This weapons from HL isn't it? @@ -4169,8 +4184,6 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, } else if (pKiller->flags & FL_CLIENT) { - CBasePlayer *pAttacker = CBasePlayer::Instance(pKiller); - const char *VictimTeam = GetTeam(pVictim->m_iTeam); const char *KillerTeam = (pAttacker && pAttacker->IsPlayer()) ? GetTeam(pAttacker->m_iTeam) : ""; @@ -4345,11 +4358,11 @@ int EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeadPlayerWeapons)(CBasePlayer *pPla { case 3: return GR_PLR_DROP_GUN_ALL; - case 2: + case 2: break; case 1: return GR_PLR_DROP_GUN_BEST; - default: + default: return GR_PLR_DROP_GUN_NO; } #endif @@ -5204,3 +5217,177 @@ bool CHalfLifeMultiplay::CanPlayerBuy(CBasePlayer *pPlayer) const return true; } + +// +// Checks for assists in a kill situation +// +// This function analyzes damage records and player actions to determine the player who contributed the most to a kill, +// considering factors such as damage dealt and the use of flashbang grenades +// +// pVictim - The victim player +// pKiller - The killer player +// bAssistWithFlashbang - A flag indicating whether a flashbang was used in the assist +// Returns - A pointer to the player who gave the most assistance, or NULL if appropriate assistant is not found +// +CBasePlayer *CHalfLifeMultiplay::CheckAssistsToKill(CBasePlayer *pVictim, CBasePlayer *pKiller, bool &bAssistWithFlashbang) +{ + CCSPlayer::DamageList_t &victimDamageTakenList = pVictim->CSPlayer()->GetDamageList(); + + float maxDamage = 0.0f; + int maxDamageIndex = -1; + CBasePlayer *maxDamagePlayer = nullptr; + bAssistWithFlashbang = false; + + // Find the best assistant + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + const CCSPlayer::CDamageRecord_t &record = victimDamageTakenList[i - 1]; + if (record.flDamage == 0) + continue; // dealt no damage + + CBasePlayer *pAttackerPlayer = UTIL_PlayerByIndex(i); + if (!pAttackerPlayer || pAttackerPlayer->IsDormant()) + continue; // ignore idle clients + + CCSPlayer *pCSAttackerPlayer = pAttackerPlayer->CSPlayer(); + if (record.userId != pCSAttackerPlayer->m_iUserID) + continue; // another client? + + if (pAttackerPlayer == pKiller || pAttackerPlayer == pVictim) + continue; // ignore involved as killer or victim + + if (record.flDamage > maxDamage) + { + // If the assistant used a flash grenade to aid in the kill, + // make sure that the victim was blinded, and that the duration of the flash effect is still preserved + if (record.flFlashDurationTime > 0 && (!pVictim->IsBlind() || record.flFlashDurationTime <= gpGlobals->time)) + continue; + + maxDamage = record.flDamage; + maxDamagePlayer = pAttackerPlayer; + maxDamageIndex = i; + } + } + + // Note: Only the highest damaging player can be an assistant + // The condition checks if the damage dealt by the player exceeds a certain percentage of the victim's max health + // Default threshold is 40%, meaning the assistant must deal at least 40% of the victim's max health as damage + if (maxDamagePlayer && maxDamage > (assist_damage_threshold.value / 100.0f) * pVictim->pev->max_health) + { + bAssistWithFlashbang = victimDamageTakenList[maxDamageIndex - 1].flFlashDurationTime > 0; // if performed the flash assist + return maxDamagePlayer; + } + + return nullptr; +} + +// +// Check the rarity estimation for a kill +// +// Estimation to represent the rarity of a kill based on various factors, including assists with flashbang grenades, +// headshot kills, kills through walls, the killer's blindness, no-scope sniper rifle kills, and kills through smoke +// +// pKiller - The player who performed the kill +// pVictim - The player who was killed +// pAssister - The assisting player (if any) +// killerWeaponName - The name of the weapon used by the killer +// bAssistWithFlashbang - A flag indicating whether an assist was made with a flashbang +// Returns an integer estimation representing the rarity of the kill +// Use with PLAYERDEATH_KILLRARITY flag to indicate a rare kill in death messages +// +int CHalfLifeMultiplay::GetRarityOfKill(CBasePlayer *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, const char *killerWeaponName, bool bAssistWithFlashbang) +{ + int iRarity = 0; + + // The killer player kills the victim with an assistant flashbang grenade + if (pAssister && bAssistWithFlashbang) + iRarity |= KILLRARITY_ASSIST_FLASH; + + // The killer player kills the victim with a headshot + if (pVictim->m_bHeadshotKilled) + iRarity |= KILLRARITY_HEADSHOT; + + // The killer player kills the victim through the walls + if (pVictim->GetDmgPenetrationLevel() > 0) + iRarity |= KILLRARITY_PENETRATED; + + // The killer player was blind + if (pKiller->IsBlind()) + iRarity |= KILLRARITY_KILLER_BLIND; + + // The killer player kills the victim with a sniper rifle with no scope + WeaponClassType weaponClass = AliasToWeaponClass(killerWeaponName); + if (weaponClass == WEAPONCLASS_SNIPERRIFLE && pKiller->m_iClientFOV == DEFAULT_FOV) + iRarity |= KILLRARITY_NOSCOPE; + + // The killer player kills the victim through smoke + const Vector inEyePos = pKiller->EyePosition(); + if (TheCSBots()->IsLineBlockedBySmoke(&inEyePos, &pVictim->pev->origin)) + iRarity |= KILLRARITY_THROUGH_SMOKE; + + return iRarity; +} + +// +// Sends death messages to all players, including info about the killer, victim, weapon used, +// extra death flags, death position, assistant, and kill rarity +// +// +// pAttacker - The player who performed the kill +// pVictim - The player who was killed +// pAssister - The assisting player (if any) +// killerWeaponName - The name of the weapon used by the killer +// iDeathMessageFlags - Flags indicating extra death message info +// iRarityOfKill - An bitsums representing the rarity classification of the kill +// +void CHalfLifeMultiplay::SendDeathMessage(CBasePlayer *pAttacker, CBasePlayer *pVictim, CBasePlayer *pAssister, const char *killerWeaponName, int iDeathMessageFlags, int iRarityOfKill) +{ + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); + if (!pPlayer || FNullEnt(pPlayer->edict())) + continue; + + if (pPlayer->IsBot() || pPlayer->IsDormant()) + continue; + + int iDeathExtraFlags = iDeathMessageFlags; + + // Send the victim's death position only + // if the attacker or victim is a teammate of the recipient player + if (pPlayer == pVictim || ( + PlayerRelationship(pVictim, pPlayer) != GR_TEAMMATE && + PlayerRelationship(pAttacker, pPlayer) != GR_TEAMMATE)) + { + iDeathExtraFlags &= ~PLAYERDEATH_POSITION; + } + + MESSAGE_BEGIN(MSG_ONE, gmsgDeathMsg, nullptr, pPlayer->pev); + WRITE_BYTE(pAttacker ? pAttacker->entindex() : 0); // the killer + WRITE_BYTE(pVictim->entindex()); // the victim + WRITE_BYTE(pVictim->m_bHeadshotKilled); // is killed headshot + WRITE_STRING(killerWeaponName); // what they were killed by (should this be a string?) + + if (iDeathExtraFlags > 0) + WRITE_LONG(iDeathExtraFlags); + + // Writes the coordinates of the place where the victim died + // The victim has just been killed, so this usefully display 'X' dead icon on the HUD radar + if (iDeathExtraFlags & PLAYERDEATH_POSITION) + { + WRITE_COORD(pVictim->pev->origin.x); + WRITE_COORD(pVictim->pev->origin.y); + WRITE_COORD(pVictim->pev->origin.z); + } + + // Writes the index of the teammate who assisted in the kill + if (iDeathExtraFlags & PLAYERDEATH_ASSISTANT) + WRITE_BYTE(pAssister->entindex()); + + // Writes the rarity classification of the kill + if (iDeathExtraFlags & PLAYERDEATH_KILLRARITY) + WRITE_LONG(iRarityOfKill); + + MESSAGE_END(); + } +} diff --git a/regamedll/dlls/player.cpp b/regamedll/dlls/player.cpp index fa26e4141..623f3dd12 100644 --- a/regamedll/dlls/player.cpp +++ b/regamedll/dlls/player.cpp @@ -973,6 +973,10 @@ BOOL EXT_FUNC CBasePlayer::__API_HOOK(TakeDamage)(entvars_t *pevInflictor, entva TheCareerTasks->HandleEnemyInjury(GetWeaponName(pevInflictor, pevAttacker), pPlayerAttacker->HasShield(), pPlayerAttacker); } } + +#ifdef REGAMEDLL_API + CSPlayer()->RecordDamage(pAttack, flDamage); +#endif } { @@ -1216,6 +1220,10 @@ BOOL EXT_FUNC CBasePlayer::__API_HOOK(TakeDamage)(entvars_t *pevInflictor, entva TheCareerTasks->HandleEnemyInjury(GetWeaponName(pevInflictor, pevAttacker), pPlayerAttacker->HasShield(), pPlayerAttacker); } } + +#ifdef REGAMEDLL_API + CSPlayer()->RecordDamage(pAttack, flDamage); +#endif } { diff --git a/regamedll/dlls/weapons.cpp b/regamedll/dlls/weapons.cpp index 097054fc7..7d2ca281e 100644 --- a/regamedll/dlls/weapons.cpp +++ b/regamedll/dlls/weapons.cpp @@ -93,7 +93,7 @@ void EXT_FUNC __API_HOOK(ApplyMultiDamage)(entvars_t *pevInflictor, entvars_t *p return; gMultiDamage.pEntity->TakeDamage(pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type); - + gMultiDamage.pEntity->ResetDmgPenetrationLevel(); } LINK_HOOK_VOID_CHAIN(AddMultiDamage, (entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType), pevInflictor, pEntity, flDamage, bitsDamageType) diff --git a/regamedll/public/regamedll/API/CSEntity.h b/regamedll/public/regamedll/API/CSEntity.h index 57e97b9eb..e2c1922c8 100644 --- a/regamedll/public/regamedll/API/CSEntity.h +++ b/regamedll/public/regamedll/API/CSEntity.h @@ -35,6 +35,7 @@ class CCSEntity CCSEntity() : m_pContainingEntity(nullptr) { + m_ucDmgPenetrationLevel = 0; } virtual ~CCSEntity() {} @@ -44,12 +45,14 @@ class CCSEntity public: CBaseEntity *m_pContainingEntity; + unsigned char m_ucDmgPenetrationLevel; // penetration level of the damage caused by the inflictor private: #if defined(_MSC_VER) #pragma region reserve_data_Region #endif - int CCSEntity_Reserve[0x1000]; + char CCSEntity_Reserve[0x3FFF]; + virtual void func_reserve1() {}; virtual void func_reserve2() {}; virtual void func_reserve3() {}; @@ -85,6 +88,29 @@ class CCSEntity #endif }; +inline void CBaseEntity::SetDmgPenetrationLevel(int iPenetrationLevel) +{ +#ifdef REGAMEDLL_API + m_pEntity->m_ucDmgPenetrationLevel = iPenetrationLevel; +#endif +} + +inline void CBaseEntity::ResetDmgPenetrationLevel() +{ +#ifdef REGAMEDLL_API + m_pEntity->m_ucDmgPenetrationLevel = 0; +#endif +} + +inline int CBaseEntity::GetDmgPenetrationLevel() const +{ +#ifdef REGAMEDLL_API + return m_pEntity->m_ucDmgPenetrationLevel; +#else + return 0; +#endif +} + class CCSDelay: public CCSEntity { public: diff --git a/regamedll/public/regamedll/API/CSPlayer.h b/regamedll/public/regamedll/API/CSPlayer.h index f56b1c513..96955884b 100644 --- a/regamedll/public/regamedll/API/CSPlayer.h +++ b/regamedll/public/regamedll/API/CSPlayer.h @@ -30,6 +30,7 @@ #include #include +#include enum WeaponInfiniteAmmoMode { @@ -54,7 +55,8 @@ class CCSPlayer: public CCSMonster { m_flJumpHeight(0), m_flLongJumpHeight(0), m_flLongJumpForce(0), - m_flDuckSpeedMultiplier(0) + m_flDuckSpeedMultiplier(0), + m_iUserID(-1) { m_szModel[0] = '\0'; } @@ -112,7 +114,7 @@ class CCSPlayer: public CCSMonster { void OnSpawn(); void OnKilled(); - + void OnConnect(); CBasePlayer *BasePlayer() const; public: @@ -140,10 +142,22 @@ class CCSPlayer: public CCSMonster { bool m_bMegaBunnyJumping; bool m_bPlantC4Anywhere; bool m_bSpawnProtectionEffects; - double m_flJumpHeight; - double m_flLongJumpHeight; + double m_flJumpHeight; + double m_flLongJumpHeight; double m_flLongJumpForce; double m_flDuckSpeedMultiplier; + + int m_iUserID; + struct CDamageRecord_t + { + float flDamage = 0.0f; + float flFlashDurationTime = 0.0f; + int userId = -1; + }; + using DamageList_t = CUtlArray; + DamageList_t m_DamageList; // A unified array of recorded damage that includes giver and taker in each entry + DamageList_t &GetDamageList() { return m_DamageList; } + void RecordDamage(CBasePlayer *pAttacker, float flDamage, float flFlashDurationTime = -1); }; // Inlines diff --git a/regamedll/public/utlarray.h b/regamedll/public/utlarray.h new file mode 100644 index 000000000..0070c19c7 --- /dev/null +++ b/regamedll/public/utlarray.h @@ -0,0 +1,248 @@ +/* +* +* Copyright (c) 1996-2002, Valve LLC. All rights reserved. +* +* This product contains software technology licensed from Id +* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. +* All Rights Reserved. +* +* Use, distribution, and modification of this source code and/or resulting +* object code is restricted to non-commercial enhancements to products from +* Valve LLC. All other use, distribution, or modification is prohibited +* without written permission from Valve LLC. +* +*/ + +#pragma once + +// A growable array class that maintains a free list and keeps elements +// in the same location +#include "tier0/platform.h" +#include "tier0/dbg.h" + +#define FOR_EACH_ARRAY(vecName, iteratorName)\ + for (int iteratorName = 0; (vecName).IsUtlArray && iteratorName < (vecName).Count(); iteratorName++) + +#define FOR_EACH_ARRAY_BACK(vecName, iteratorName)\ + for (int iteratorName = (vecName).Count() - 1; (vecName).IsUtlArray && iteratorName >= 0; iteratorName--) + +template +class CUtlArray +{ +public: + typedef T ElemType_t; + enum { IsUtlArray = true }; // Used to match this at compiletime + + CUtlArray(); + CUtlArray(T *pMemory, size_t count); + ~CUtlArray(); + + CUtlArray &operator=(const CUtlArray &other); + CUtlArray(CUtlArray const &vec); + + // element access + T &operator[](int i); + const T &operator[](int i) const; + T &Element(int i); + const T &Element(int i) const; + T &Random(); + const T &Random() const; + + T *Base(); + const T *Base() const; + + // Returns the number of elements in the array, NumAllocated() is included for consistency with UtlVector + int Count() const; + int NumAllocated() const; + + // Is element index valid? + bool IsValidIndex(int i) const; + static int InvalidIndex(); + + void CopyArray(const T *pArray, size_t count); + + void Clear(); + void RemoveAll(); + void Swap(CUtlArray< T, MAX_SIZE> &vec); + + // Finds an element (element needs operator== defined) + int Find(const T &src) const; + void FillWithValue(const T &src); + + bool HasElement(const T &src) const; + + void Sort(int(__cdecl *pfnCompare)(const T *, const T *)); + +protected: + T m_Memory[MAX_SIZE]; +}; + +// Constructor +template +inline CUtlArray::CUtlArray() +{ +} + +template +inline CUtlArray::CUtlArray(T *pMemory, size_t count) +{ + CopyArray(pMemory, count); +} + +// Destructor +template +inline CUtlArray::~CUtlArray() +{ +} + +template +inline CUtlArray &CUtlArray::operator=(const CUtlArray &other) +{ + if (this != &other) + { + for (size_t n = 0; n < MAX_SIZE; n++) + m_Memory[n] = other.m_Memory[n]; + } + + return *this; +} + +template +inline CUtlArray::CUtlArray(CUtlArray const &vec) +{ + for (size_t n = 0; n < MAX_SIZE; n++) + m_Memory[n] = vec.m_Memory[n]; +} + +template +inline T *CUtlArray::Base() +{ + return &m_Memory[0]; +} + +template +inline const T *CUtlArray::Base() const +{ + return &m_Memory[0]; +} + +// Element access +template +inline T &CUtlArray::operator[](int i) +{ + Assert(IsValidIndex(i)); + return m_Memory[i]; +} + +template +inline const T &CUtlArray::operator[](int i) const +{ + Assert(IsValidIndex(i)); + return m_Memory[i]; +} + +template +inline T &CUtlArray::Element(int i) +{ + Assert(IsValidIndex(i)); + return m_Memory[i]; +} + +template +inline const T &CUtlArray::Element(int i) const +{ + Assert(IsValidIndex(i)); + return m_Memory[i]; +} + +// Count +template +inline int CUtlArray::Count() const +{ + return (int)MAX_SIZE; +} + +template +inline int CUtlArray::NumAllocated() const +{ + return (int)MAX_SIZE; +} + +// Is element index valid? +template +inline bool CUtlArray::IsValidIndex(int i) const +{ + return (i >= 0) && (i < MAX_SIZE); +} + +// Returns in invalid index +template +inline int CUtlArray::InvalidIndex() +{ + return -1; +} + +// Sorts the vector +template +void CUtlArray::Sort(int(__cdecl *pfnCompare)(const T *, const T *)) +{ + typedef int(__cdecl *QSortCompareFunc_t)(const void *, const void *); + if (Count() <= 1) + return; + + qsort(Base(), Count(), sizeof(T), (QSortCompareFunc_t)(pfnCompare)); +} + +template +void CUtlArray::CopyArray(const T *pArray, size_t count) +{ + Assert(count < MAX_SIZE); + + for (size_t n = 0; n < count; n++) + m_Memory[n] = pArray[n]; +} + +template +void CUtlArray::Clear() +{ + Q_memset(m_Memory, 0, MAX_SIZE * sizeof(T)); +} + +template +void CUtlArray::RemoveAll() +{ + Clear(); +} + +template +void CUtlArray::Swap(CUtlArray< T, MAX_SIZE> &vec) +{ + for (size_t n = 0; n < MAX_SIZE; n++) + SWAP(m_Memory[n], vec.m_Memory[n]); +} + +// Finds an element (element needs operator== defined) +template +int CUtlArray::Find(const T &src) const +{ + for (int i = 0; i < Count(); i++) + { + if (Element(i) == src) + return i; + } + + return -1; +} + +template +void CUtlArray::FillWithValue(const T &src) +{ + for (int i = 0; i < Count(); i++) + Element(i) = src; +} + +template +bool CUtlArray::HasElement(const T &src) const +{ + return (Find(src) >= 0); +} From 95220965463c277be3c337d479558add6114629f Mon Sep 17 00:00:00 2001 From: s1lentq Date: Thu, 7 Sep 2023 07:42:41 +0700 Subject: [PATCH 2/5] Missing REGAMEDLL_ADD Fix testdemos --- regamedll/dlls/multiplay_gamerules.cpp | 11 +++++++++++ regamedll/public/utlarray.h | 13 ------------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/regamedll/dlls/multiplay_gamerules.cpp b/regamedll/dlls/multiplay_gamerules.cpp index d1d0418a1..f905c21fe 100644 --- a/regamedll/dlls/multiplay_gamerules.cpp +++ b/regamedll/dlls/multiplay_gamerules.cpp @@ -4141,6 +4141,7 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, if (!TheTutor) { +#ifdef REGAMEDLL_ADD int iAllowDeathMessageFlags = (int)deathmsg_flags.value; // allow bitsums for extra information int iDeathMessageFlags = PLAYERDEATH_POSITION; // set bitsum default int iRarityOfKill = 0; @@ -4163,6 +4164,14 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, // Apply allowed death message flags to iDeathMessageFlags iDeathMessageFlags &= iAllowDeathMessageFlags; SendDeathMessage(pAttacker, pVictim, pAssister, killer_weapon_name, iDeathMessageFlags, iRarityOfKill); +#else + MESSAGE_BEGIN(MSG_ALL, gmsgDeathMsg); + WRITE_BYTE(pAttacker ? pAttacker->entindex() : 0); // the killer + WRITE_BYTE(ENTINDEX(pVictim->edict())); // the victim + WRITE_BYTE(pVictim->m_bHeadshotKilled); // is killed headshot + WRITE_STRING(killer_weapon_name); // what they were killed by (should this be a string?) + MESSAGE_END(); +#endif } // This weapons from HL isn't it? @@ -5231,6 +5240,7 @@ bool CHalfLifeMultiplay::CanPlayerBuy(CBasePlayer *pPlayer) const // CBasePlayer *CHalfLifeMultiplay::CheckAssistsToKill(CBasePlayer *pVictim, CBasePlayer *pKiller, bool &bAssistWithFlashbang) { +#ifdef REGAMEDLL_ADD CCSPlayer::DamageList_t &victimDamageTakenList = pVictim->CSPlayer()->GetDamageList(); float maxDamage = 0.0f; @@ -5277,6 +5287,7 @@ CBasePlayer *CHalfLifeMultiplay::CheckAssistsToKill(CBasePlayer *pVictim, CBaseP bAssistWithFlashbang = victimDamageTakenList[maxDamageIndex - 1].flFlashDurationTime > 0; // if performed the flash assist return maxDamagePlayer; } +#endif return nullptr; } diff --git a/regamedll/public/utlarray.h b/regamedll/public/utlarray.h index 0070c19c7..6bdb4136c 100644 --- a/regamedll/public/utlarray.h +++ b/regamedll/public/utlarray.h @@ -71,8 +71,6 @@ class CUtlArray bool HasElement(const T &src) const; - void Sort(int(__cdecl *pfnCompare)(const T *, const T *)); - protected: T m_Memory[MAX_SIZE]; }; @@ -182,17 +180,6 @@ inline int CUtlArray::InvalidIndex() return -1; } -// Sorts the vector -template -void CUtlArray::Sort(int(__cdecl *pfnCompare)(const T *, const T *)) -{ - typedef int(__cdecl *QSortCompareFunc_t)(const void *, const void *); - if (Count() <= 1) - return; - - qsort(Base(), Count(), sizeof(T), (QSortCompareFunc_t)(pfnCompare)); -} - template void CUtlArray::CopyArray(const T *pArray, size_t count) { From 8ec281c92c9c1278b1446d6692194b1aa2246abd Mon Sep 17 00:00:00 2001 From: s1lentq Date: Fri, 8 Sep 2023 00:58:22 +0700 Subject: [PATCH 3/5] Add hookchain CGameRules::SendDeathMessage Reworked SendDeathMessage Minor cleanup --- regamedll/dlls/API/CAPI_Impl.cpp | 21 +- regamedll/dlls/API/CAPI_Impl.h | 8 +- regamedll/dlls/combat.cpp | 6 - regamedll/dlls/gamerules.h | 7 +- regamedll/dlls/multiplay_gamerules.cpp | 220 ++++++++------------- regamedll/dlls/player.cpp | 101 ++++++---- regamedll/dlls/player.h | 2 +- regamedll/msvc/ReGameDLL.vcxproj | 5 +- regamedll/msvc/ReGameDLL.vcxproj.filters | 3 + regamedll/public/regamedll/regamedll_api.h | 9 +- 10 files changed, 179 insertions(+), 203 deletions(-) diff --git a/regamedll/dlls/API/CAPI_Impl.cpp b/regamedll/dlls/API/CAPI_Impl.cpp index 792bc3ef0..3075c841d 100644 --- a/regamedll/dlls/API/CAPI_Impl.cpp +++ b/regamedll/dlls/API/CAPI_Impl.cpp @@ -61,37 +61,37 @@ void EXT_FUNC AddMultiDamage_api(entvars_t *pevInflictor, CBaseEntity *pEntity, AddMultiDamage(pevInflictor, pEntity, flDamage, bitsDamageType); } -int EXT_FUNC Cmd_Argc_api() +int EXT_FUNC Cmd_Argc_api() { return CMD_ARGC_(); } -const char *EXT_FUNC Cmd_Argv_api(int i) +const char *EXT_FUNC Cmd_Argv_api(int i) { return CMD_ARGV_(i); } -CGrenade *EXT_FUNC PlantBomb_api(entvars_t *pevOwner, Vector &vecStart, Vector &vecVelocity) +CGrenade *EXT_FUNC PlantBomb_api(entvars_t *pevOwner, Vector &vecStart, Vector &vecVelocity) { return CGrenade::ShootSatchelCharge(pevOwner, vecStart, vecVelocity); } -CGib *EXT_FUNC SpawnHeadGib_api(entvars_t *pevVictim) +CGib *EXT_FUNC SpawnHeadGib_api(entvars_t *pevVictim) { return CGib::SpawnHeadGib(pevVictim); } -void EXT_FUNC SpawnRandomGibs_api(entvars_t *pevVictim, int cGibs, int human) +void EXT_FUNC SpawnRandomGibs_api(entvars_t *pevVictim, int cGibs, int human) { CGib::SpawnRandomGibs(pevVictim, cGibs, human); } -void EXT_FUNC UTIL_RestartOther_api(const char *szClassname) +void EXT_FUNC UTIL_RestartOther_api(const char *szClassname) { UTIL_RestartOther(szClassname); } -void EXT_FUNC UTIL_ResetEntities_api() +void EXT_FUNC UTIL_ResetEntities_api() { UTIL_ResetEntities(); } @@ -130,11 +130,11 @@ CGrenade *EXT_FUNC SpawnGrenade_api(WeaponIdType weaponId, entvars_t *pevOwner, { switch (weaponId) { - case WEAPON_HEGRENADE: + case WEAPON_HEGRENADE: return CGrenade::ShootTimed2(pevOwner, vecSrc, vecThrow, time, iTeam, usEvent); - case WEAPON_FLASHBANG: + case WEAPON_FLASHBANG: return CGrenade::ShootTimed(pevOwner, vecSrc, vecThrow, time); - case WEAPON_SMOKEGRENADE: + case WEAPON_SMOKEGRENADE: return CGrenade::ShootSmokeGrenade(pevOwner, vecSrc, vecThrow, time, usEvent); case WEAPON_C4: return CGrenade::ShootSatchelCharge(pevOwner, vecSrc, vecThrow); @@ -331,6 +331,7 @@ GAMEHOOK_REGISTRY(CBasePlayer_EntSelectSpawnPoint); GAMEHOOK_REGISTRY(CBasePlayerWeapon_ItemPostFrame); GAMEHOOK_REGISTRY(CBasePlayerWeapon_KickBack); GAMEHOOK_REGISTRY(CBasePlayerWeapon_SendWeaponAnim); +GAMEHOOK_REGISTRY(CSGameRules_SendDeathMessage); int CReGameApi::GetMajorVersion() { return REGAMEDLL_API_VERSION_MAJOR; diff --git a/regamedll/dlls/API/CAPI_Impl.h b/regamedll/dlls/API/CAPI_Impl.h index b6bc45dd0..a66d9e91e 100644 --- a/regamedll/dlls/API/CAPI_Impl.h +++ b/regamedll/dlls/API/CAPI_Impl.h @@ -709,6 +709,10 @@ typedef IHookChainRegistryClassEmptyImpl CReGameHook_CSGameRules_PlayerGotWeapon; typedef IHookChainRegistryClassEmptyImpl CReGameHookRegistry_CSGameRules_PlayerGotWeapon; +// CHalfLifeMultiplay::SendDeathMessage hook +typedef IHookChainClassImpl CReGameHook_CSGameRules_SendDeathMessage; +typedef IHookChainRegistryClassEmptyImpl CReGameHookRegistry_CSGameRules_SendDeathMessage; + // CBotManager::OnEvent hook typedef IHookChainClassImpl CReGameHook_CBotManager_OnEvent; typedef IHookChainRegistryClassEmptyImpl CReGameHookRegistry_CBotManager_OnEvent; @@ -865,7 +869,7 @@ class CReGameHookchains: public IReGameHookchains { CReGameHookRegistry_CBasePlayer_Pain m_CBasePlayer_Pain; CReGameHookRegistry_CBasePlayer_DeathSound m_CBasePlayer_DeathSound; CReGameHookRegistry_CBasePlayer_JoiningThink m_CBasePlayer_JoiningThink; - + CReGameHookRegistry_FreeGameRules m_FreeGameRules; CReGameHookRegistry_PM_LadderMove m_PM_LadderMove; CReGameHookRegistry_PM_WaterJump m_PM_WaterJump; @@ -889,6 +893,7 @@ class CReGameHookchains: public IReGameHookchains { CReGameHookRegistry_CBasePlayerWeapon_ItemPostFrame m_CBasePlayerWeapon_ItemPostFrame; CReGameHookRegistry_CBasePlayerWeapon_KickBack m_CBasePlayerWeapon_KickBack; CReGameHookRegistry_CBasePlayerWeapon_SendWeaponAnim m_CBasePlayerWeapon_SendWeaponAnim; + CReGameHookRegistry_CSGameRules_SendDeathMessage m_CSGameRules_SendDeathMessage; public: virtual IReGameHookRegistry_CBasePlayer_Spawn *CBasePlayer_Spawn(); @@ -1044,6 +1049,7 @@ class CReGameHookchains: public IReGameHookchains { virtual IReGameHookRegistry_CBasePlayerWeapon_ItemPostFrame *CBasePlayerWeapon_ItemPostFrame(); virtual IReGameHookRegistry_CBasePlayerWeapon_KickBack *CBasePlayerWeapon_KickBack(); virtual IReGameHookRegistry_CBasePlayerWeapon_SendWeaponAnim *CBasePlayerWeapon_SendWeaponAnim(); + virtual IReGameHookRegistry_CSGameRules_SendDeathMessage *CSGameRules_SendDeathMessage(); }; extern CReGameHookchains g_ReGameHookchains; diff --git a/regamedll/dlls/combat.cpp b/regamedll/dlls/combat.cpp index 8dceda8d4..4cbc485dc 100644 --- a/regamedll/dlls/combat.cpp +++ b/regamedll/dlls/combat.cpp @@ -27,15 +27,9 @@ void PlayerBlind(CBasePlayer *pPlayer, entvars_t *pevInflictor, entvars_t *pevAt #if defined(REGAMEDLL_API) && defined(REGAMEDLL_ADD) float flAdjustedDamage; if (alpha > 200) - { flAdjustedDamage = fadeTime / 3; - flAdjustedDamage = fadeHold * 1.5; - } else - { flAdjustedDamage = fadeTime / 1.75; - flAdjustedDamage = fadeHold * 3.5; - } pPlayer->CSPlayer()->RecordDamage(CBasePlayer::Instance(pevAttacker), flAdjustedDamage * 16.0f, flDurationTime); #endif diff --git a/regamedll/dlls/gamerules.h b/regamedll/dlls/gamerules.h index 59415beaa..c64e18a42 100644 --- a/regamedll/dlls/gamerules.h +++ b/regamedll/dlls/gamerules.h @@ -602,6 +602,7 @@ class CHalfLifeMultiplay: public CGameRules BOOL TeamFull_OrigFunc(int team_id); BOOL TeamStacked_OrigFunc(int newTeam_id, int curTeam_id); void PlayerGotWeapon_OrigFunc(CBasePlayer *pPlayer, CBasePlayerItem *pWeapon); + void SendDeathMessage_OrigFunc(CBaseEntity *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, entvars_t *pevInflictor, const char *killerWeaponName, int iDeathMessageFlags, int iRarityOfKill); #endif public: @@ -726,9 +727,9 @@ class CHalfLifeMultiplay: public CGameRules VFUNC bool HasRoundTimeExpired(); VFUNC bool IsBombPlanted(); - void SendDeathMessage(CBasePlayer *pAttacker, CBasePlayer *pVictim, CBasePlayer *pAssister, const char *killerWeaponName, int iDeathMessageFlags, int iRarityOfKill); - int GetRarityOfKill(CBasePlayer *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, const char *killerWeaponName, bool bAssistWithFlashbang); - CBasePlayer *CheckAssistsToKill(CBasePlayer *pVictim, CBasePlayer *pKiller, bool &bAssistWithFlashbang); + void SendDeathMessage(CBaseEntity *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, entvars_t *pevInflictor, const char *killerWeaponName, int iDeathMessageFlags, int iRarityOfKill); + int GetRarityOfKill(CBaseEntity *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, const char *killerWeaponName, bool bFlashAssist); + CBasePlayer *CheckAssistsToKill(CBaseEntity *pKiller, CBasePlayer *pVictim, bool &bFlashAssist); private: void MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(int iTeam); diff --git a/regamedll/dlls/multiplay_gamerules.cpp b/regamedll/dlls/multiplay_gamerules.cpp index f905c21fe..fd0b29c3f 100644 --- a/regamedll/dlls/multiplay_gamerules.cpp +++ b/regamedll/dlls/multiplay_gamerules.cpp @@ -4079,124 +4079,59 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(PlayerKilled)(CBasePlayer *pVictim, LINK_HOOK_CLASS_VOID_CUSTOM_CHAIN(CHalfLifeMultiplay, CSGameRules, DeathNotice, (CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor), pVictim, pKiller, pevInflictor) -void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, entvars_t *pKiller, entvars_t *pevInflictor) +void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, entvars_t *pevKiller, entvars_t *pevInflictor) { // by default, the player is killed by the world - const char *killer_weapon_name = "world"; - CBasePlayer *pAttacker = nullptr; - -#ifndef REGAMEDLL_FIXES - // Hack to fix name change - char *tau = "tau_cannon"; - char *gluon = "gluon gun"; -#endif - - // Is the killer a client? - if (pKiller->flags & FL_CLIENT) - { - pAttacker = CBasePlayer::Instance(pKiller); - - if (pevInflictor) - { - if (pevInflictor == pKiller) - { - // If the inflictor is the killer, then it must be their current weapon doing the damage - if (pAttacker && pAttacker->IsPlayer()) - { - if (pAttacker->m_pActiveItem) - { - killer_weapon_name = pAttacker->m_pActiveItem->pszName(); - } - } - } - else - { - // it's just that easy - killer_weapon_name = STRING(pevInflictor->classname); - } - } - } - else -#ifdef REGAMEDLL_FIXES - if (pevInflictor) -#endif - { - killer_weapon_name = STRING(pevInflictor->classname); - } - - // strip the monster_* or weapon_* from the inflictor's classname - const char cut_weapon[] = "weapon_"; - const char cut_monster[] = "monster_"; - const char cut_func[] = "func_"; - - // replace the code names with the 'real' names - if (!Q_strncmp(killer_weapon_name, cut_weapon, sizeof(cut_weapon) - 1)) - killer_weapon_name += sizeof(cut_weapon) - 1; - - else if (!Q_strncmp(killer_weapon_name, cut_monster, sizeof(cut_monster) - 1)) - killer_weapon_name += sizeof(cut_monster) - 1; - - else if (!Q_strncmp(killer_weapon_name, cut_func, sizeof(cut_func) - 1)) - killer_weapon_name += sizeof(cut_func) - 1; + CBaseEntity *pKiller = (pevKiller->flags & FL_CLIENT) ? CBaseEntity::Instance(pevKiller) : nullptr; + const char *killer_weapon_name = pVictim->GetKillerWeaponName(pevInflictor, pevKiller); if (!TheTutor) { #ifdef REGAMEDLL_ADD - int iAllowDeathMessageFlags = (int)deathmsg_flags.value; // allow bitsums for extra information - int iDeathMessageFlags = PLAYERDEATH_POSITION; // set bitsum default int iRarityOfKill = 0; + int iDeathMessageFlags = PLAYERDEATH_POSITION; // set default bit CBasePlayer *pAssister = nullptr; - CBasePlayer *pFlasher = nullptr; - if (pAttacker && pAttacker->IsPlayer()) + bool bFlashAssist = false; + if ((pAssister = CheckAssistsToKill(pKiller, pVictim, bFlashAssist))) { - // Sets the flag PLAYERDEATH_ASSISTANT indicating the presence of a teammate who assisted in the kill - bool bAssistWithFlashbang; - if ((pAssister = CheckAssistsToKill(pVictim, pAttacker, bAssistWithFlashbang))) - iDeathMessageFlags |= PLAYERDEATH_ASSISTANT; + // Add a flag indicating the presence of an assistant who assisted in the kill + iDeathMessageFlags |= PLAYERDEATH_ASSISTANT; + } - iRarityOfKill = GetRarityOfKill(pAttacker, pVictim, pAssister, killer_weapon_name, bAssistWithFlashbang); - if (iRarityOfKill != 0) - iDeathMessageFlags |= PLAYERDEATH_KILLRARITY; // Attacker killed the victim in a rare way + iRarityOfKill = GetRarityOfKill(pKiller, pVictim, pAssister, killer_weapon_name, bFlashAssist); + if (iRarityOfKill != 0) + { + // Add a flag indicating that the attacker killed the victim in a rare way + iDeathMessageFlags |= PLAYERDEATH_KILLRARITY; } - // Apply allowed death message flags to iDeathMessageFlags - iDeathMessageFlags &= iAllowDeathMessageFlags; - SendDeathMessage(pAttacker, pVictim, pAssister, killer_weapon_name, iDeathMessageFlags, iRarityOfKill); + SendDeathMessage(pKiller, pVictim, pAssister, pevInflictor, killer_weapon_name, iDeathMessageFlags, iRarityOfKill); #else MESSAGE_BEGIN(MSG_ALL, gmsgDeathMsg); - WRITE_BYTE(pAttacker ? pAttacker->entindex() : 0); // the killer - WRITE_BYTE(ENTINDEX(pVictim->edict())); // the victim - WRITE_BYTE(pVictim->m_bHeadshotKilled); // is killed headshot - WRITE_STRING(killer_weapon_name); // what they were killed by (should this be a string?) + WRITE_BYTE(pKiller ? pKiller->entindex() : 0); // the killer + WRITE_BYTE(ENTINDEX(pVictim->edict())); // the victim + WRITE_BYTE(pVictim->m_bHeadshotKilled); // is killed headshot + WRITE_STRING(killer_weapon_name); // what they were killed by (should this be a string?) MESSAGE_END(); #endif } - // This weapons from HL isn't it? -#ifndef REGAMEDLL_FIXES - if (!Q_strcmp(killer_weapon_name, "egon")) - killer_weapon_name = gluon; - - else if (!Q_strcmp(killer_weapon_name, "gauss")) - killer_weapon_name = tau; -#endif - // Did he kill himself? - if (pVictim->pev == pKiller) + if (pVictim->pev == pevKiller) { // killed self char *team = GetTeam(pVictim->m_iTeam); UTIL_LogPrintf("\"%s<%i><%s><%s>\" committed suicide with \"%s\"\n", STRING(pVictim->pev->netname), GETPLAYERUSERID(pVictim->edict()), GETPLAYERAUTHID(pVictim->edict()), team, killer_weapon_name); } - else if (pKiller->flags & FL_CLIENT) + else if (pevKiller->flags & FL_CLIENT) { const char *VictimTeam = GetTeam(pVictim->m_iTeam); - const char *KillerTeam = (pAttacker && pAttacker->IsPlayer()) ? GetTeam(pAttacker->m_iTeam) : ""; + const char *KillerTeam = (pKiller && pKiller->IsPlayer()) ? GetTeam(((CBasePlayer *)pKiller)->m_iTeam) : ""; - UTIL_LogPrintf("\"%s<%i><%s><%s>\" killed \"%s<%i><%s><%s>\" with \"%s\"\n", STRING(pKiller->netname), GETPLAYERUSERID(ENT(pKiller)), GETPLAYERAUTHID(ENT(pKiller)), + UTIL_LogPrintf("\"%s<%i><%s><%s>\" killed \"%s<%i><%s><%s>\" with \"%s\"\n", STRING(pevKiller->netname), GETPLAYERUSERID(ENT(pevKiller)), GETPLAYERAUTHID(ENT(pevKiller)), KillerTeam, STRING(pVictim->pev->netname), GETPLAYERUSERID(pVictim->edict()), GETPLAYERAUTHID(pVictim->edict()), VictimTeam, killer_weapon_name); } else @@ -4219,7 +4154,7 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, if (pevInflictor) WRITE_SHORT(ENTINDEX(ENT(pevInflictor))); // index number of secondary entity else - WRITE_SHORT(ENTINDEX(ENT(pKiller))); // index number of secondary entity + WRITE_SHORT(ENTINDEX(ENT(pevKiller))); // index number of secondary entity if (pVictim->m_bHeadshotKilled) WRITE_LONG(9 | DRC_FLAG_DRAMATIC | DRC_FLAG_SLOWMOTION); @@ -5233,12 +5168,12 @@ bool CHalfLifeMultiplay::CanPlayerBuy(CBasePlayer *pPlayer) const // This function analyzes damage records and player actions to determine the player who contributed the most to a kill, // considering factors such as damage dealt and the use of flashbang grenades // -// pVictim - The victim player -// pKiller - The killer player -// bAssistWithFlashbang - A flag indicating whether a flashbang was used in the assist -// Returns - A pointer to the player who gave the most assistance, or NULL if appropriate assistant is not found +// pKiller - The killer entity (Note: The killer may be a non-player) +// pVictim - The victim player +// bFlashAssist - A flag indicating whether a flashbang was used in the assist +// Returns - A pointer to the player who gave the most assistance, or NULL if appropriate assistant is not found // -CBasePlayer *CHalfLifeMultiplay::CheckAssistsToKill(CBasePlayer *pVictim, CBasePlayer *pKiller, bool &bAssistWithFlashbang) +CBasePlayer *CHalfLifeMultiplay::CheckAssistsToKill(CBaseEntity *pKiller, CBasePlayer *pVictim, bool &bFlashAssist) { #ifdef REGAMEDLL_ADD CCSPlayer::DamageList_t &victimDamageTakenList = pVictim->CSPlayer()->GetDamageList(); @@ -5246,7 +5181,6 @@ CBasePlayer *CHalfLifeMultiplay::CheckAssistsToKill(CBasePlayer *pVictim, CBaseP float maxDamage = 0.0f; int maxDamageIndex = -1; CBasePlayer *maxDamagePlayer = nullptr; - bAssistWithFlashbang = false; // Find the best assistant for (int i = 1; i <= gpGlobals->maxClients; i++) @@ -5284,7 +5218,7 @@ CBasePlayer *CHalfLifeMultiplay::CheckAssistsToKill(CBasePlayer *pVictim, CBaseP // Default threshold is 40%, meaning the assistant must deal at least 40% of the victim's max health as damage if (maxDamagePlayer && maxDamage > (assist_damage_threshold.value / 100.0f) * pVictim->pev->max_health) { - bAssistWithFlashbang = victimDamageTakenList[maxDamageIndex - 1].flFlashDurationTime > 0; // if performed the flash assist + bFlashAssist = victimDamageTakenList[maxDamageIndex - 1].flFlashDurationTime > 0; // if performed the flash assist return maxDamagePlayer; } #endif @@ -5298,20 +5232,20 @@ CBasePlayer *CHalfLifeMultiplay::CheckAssistsToKill(CBasePlayer *pVictim, CBaseP // Estimation to represent the rarity of a kill based on various factors, including assists with flashbang grenades, // headshot kills, kills through walls, the killer's blindness, no-scope sniper rifle kills, and kills through smoke // -// pKiller - The player who performed the kill +// pKiller - The entity who committed the kill (Note: The killer may be a non-player) // pVictim - The player who was killed // pAssister - The assisting player (if any) // killerWeaponName - The name of the weapon used by the killer -// bAssistWithFlashbang - A flag indicating whether an assist was made with a flashbang +// bFlashAssist - A flag indicating whether an assist was made with a flashbang // Returns an integer estimation representing the rarity of the kill // Use with PLAYERDEATH_KILLRARITY flag to indicate a rare kill in death messages // -int CHalfLifeMultiplay::GetRarityOfKill(CBasePlayer *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, const char *killerWeaponName, bool bAssistWithFlashbang) +int CHalfLifeMultiplay::GetRarityOfKill(CBaseEntity *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, const char *killerWeaponName, bool bFlashAssist) { int iRarity = 0; // The killer player kills the victim with an assistant flashbang grenade - if (pAssister && bAssistWithFlashbang) + if (pAssister && bFlashAssist) iRarity |= KILLRARITY_ASSIST_FLASH; // The killer player kills the victim with a headshot @@ -5323,36 +5257,48 @@ int CHalfLifeMultiplay::GetRarityOfKill(CBasePlayer *pKiller, CBasePlayer *pVict iRarity |= KILLRARITY_PENETRATED; // The killer player was blind - if (pKiller->IsBlind()) - iRarity |= KILLRARITY_KILLER_BLIND; + if (pKiller && pKiller->IsPlayer()) + { + CBasePlayer *pKillerPlayer = static_cast(pKiller); + if (pKillerPlayer->IsBlind()) + iRarity |= KILLRARITY_KILLER_BLIND; - // The killer player kills the victim with a sniper rifle with no scope - WeaponClassType weaponClass = AliasToWeaponClass(killerWeaponName); - if (weaponClass == WEAPONCLASS_SNIPERRIFLE && pKiller->m_iClientFOV == DEFAULT_FOV) - iRarity |= KILLRARITY_NOSCOPE; + // The killer player kills the victim with a sniper rifle with no scope + WeaponClassType weaponClass = AliasToWeaponClass(killerWeaponName); + if (weaponClass == WEAPONCLASS_SNIPERRIFLE && pKillerPlayer->m_iClientFOV == DEFAULT_FOV) + iRarity |= KILLRARITY_NOSCOPE; - // The killer player kills the victim through smoke - const Vector inEyePos = pKiller->EyePosition(); - if (TheCSBots()->IsLineBlockedBySmoke(&inEyePos, &pVictim->pev->origin)) - iRarity |= KILLRARITY_THROUGH_SMOKE; + // The killer player kills the victim through smoke + const Vector inEyePos = pKillerPlayer->EyePosition(); + if (TheCSBots()->IsLineBlockedBySmoke(&inEyePos, &pVictim->pev->origin)) + iRarity |= KILLRARITY_THROUGH_SMOKE; + } return iRarity; } +LINK_HOOK_CLASS_VOID_CUSTOM_CHAIN(CHalfLifeMultiplay, CSGameRules, SendDeathMessage, (CBaseEntity *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, entvars_t *pevInflictor, const char *killerWeaponName, int iDeathMessageFlags, int iRarityOfKill), pKiller, pVictim, pAssister, pevInflictor, killerWeaponName, iDeathMessageFlags, iRarityOfKill) + // // Sends death messages to all players, including info about the killer, victim, weapon used, // extra death flags, death position, assistant, and kill rarity // // -// pAttacker - The player who performed the kill +// pKiller - The entity who performed the kill (Note: The killer may be a non-player) // pVictim - The player who was killed // pAssister - The assisting player (if any) // killerWeaponName - The name of the weapon used by the killer // iDeathMessageFlags - Flags indicating extra death message info // iRarityOfKill - An bitsums representing the rarity classification of the kill // -void CHalfLifeMultiplay::SendDeathMessage(CBasePlayer *pAttacker, CBasePlayer *pVictim, CBasePlayer *pAssister, const char *killerWeaponName, int iDeathMessageFlags, int iRarityOfKill) +void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(SendDeathMessage)(CBaseEntity *pKiller, CBasePlayer *pVictim, CBasePlayer *pAssister, entvars_t *pevInflictor, const char *killerWeaponName, int iDeathMessageFlags, int iRarityOfKill) { +#ifdef REGAMEDLL_ADD + iDeathMessageFlags &= (int)deathmsg_flags.value; // leave only allowed bitsums for extra info +#endif + + CBasePlayer *pKillerPlayer = (pKiller && pKiller->IsPlayer()) ? static_cast(pKiller) : nullptr; + for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); @@ -5362,42 +5308,44 @@ void CHalfLifeMultiplay::SendDeathMessage(CBasePlayer *pAttacker, CBasePlayer *p if (pPlayer->IsBot() || pPlayer->IsDormant()) continue; - int iDeathExtraFlags = iDeathMessageFlags; + int iSendDeathMessageFlags = iDeathMessageFlags; // Send the victim's death position only // if the attacker or victim is a teammate of the recipient player if (pPlayer == pVictim || ( - PlayerRelationship(pVictim, pPlayer) != GR_TEAMMATE && - PlayerRelationship(pAttacker, pPlayer) != GR_TEAMMATE)) + PlayerRelationship(pVictim, pPlayer) != GR_TEAMMATE && + PlayerRelationship(pKillerPlayer, pPlayer) != GR_TEAMMATE)) { - iDeathExtraFlags &= ~PLAYERDEATH_POSITION; + iSendDeathMessageFlags &= ~PLAYERDEATH_POSITION; } MESSAGE_BEGIN(MSG_ONE, gmsgDeathMsg, nullptr, pPlayer->pev); - WRITE_BYTE(pAttacker ? pAttacker->entindex() : 0); // the killer - WRITE_BYTE(pVictim->entindex()); // the victim - WRITE_BYTE(pVictim->m_bHeadshotKilled); // is killed headshot - WRITE_STRING(killerWeaponName); // what they were killed by (should this be a string?) - - if (iDeathExtraFlags > 0) - WRITE_LONG(iDeathExtraFlags); + WRITE_BYTE((pKiller && pKiller->IsPlayer()) ? pKiller->entindex() : 0); // the killer + WRITE_BYTE(pVictim->entindex()); // the victim + WRITE_BYTE(pVictim->m_bHeadshotKilled); // is killed headshot + WRITE_STRING(killerWeaponName); // what they were killed by (should this be a string?) - // Writes the coordinates of the place where the victim died - // The victim has just been killed, so this usefully display 'X' dead icon on the HUD radar - if (iDeathExtraFlags & PLAYERDEATH_POSITION) + if (iSendDeathMessageFlags > 0) { - WRITE_COORD(pVictim->pev->origin.x); - WRITE_COORD(pVictim->pev->origin.y); - WRITE_COORD(pVictim->pev->origin.z); - } + WRITE_LONG(iSendDeathMessageFlags); - // Writes the index of the teammate who assisted in the kill - if (iDeathExtraFlags & PLAYERDEATH_ASSISTANT) - WRITE_BYTE(pAssister->entindex()); + // Writes the coordinates of the place where the victim died + // The victim has just been killed, so this usefully display 'X' dead icon on the HUD radar + if (iSendDeathMessageFlags & PLAYERDEATH_POSITION) + { + WRITE_COORD(pVictim->pev->origin.x); + WRITE_COORD(pVictim->pev->origin.y); + WRITE_COORD(pVictim->pev->origin.z); + } - // Writes the rarity classification of the kill - if (iDeathExtraFlags & PLAYERDEATH_KILLRARITY) - WRITE_LONG(iRarityOfKill); + // Writes the index of the teammate who assisted in the kill + if (iSendDeathMessageFlags & PLAYERDEATH_ASSISTANT) + WRITE_BYTE(pAssister->entindex()); + + // Writes the rarity classification of the kill + if (iSendDeathMessageFlags & PLAYERDEATH_KILLRARITY) + WRITE_LONG(iRarityOfKill); + } MESSAGE_END(); } diff --git a/regamedll/dlls/player.cpp b/regamedll/dlls/player.cpp index 623f3dd12..de90f3e6b 100644 --- a/regamedll/dlls/player.cpp +++ b/regamedll/dlls/player.cpp @@ -745,24 +745,30 @@ void EXT_FUNC CBasePlayer::__API_HOOK(TraceAttack)(entvars_t *pevAttacker, float AddMultiDamage(pevAttacker, this, flDamage, bitsDamageType); } -const char *GetWeaponName(entvars_t *pevInflictor, entvars_t *pKiller) +const char *CBasePlayer::GetKillerWeaponName(entvars_t *pevInflictor, entvars_t *pevKiller) const { // by default, the player is killed by the world const char *killer_weapon_name = "world"; // Is the killer a client? - if (pKiller->flags & FL_CLIENT) + if (pevKiller->flags & FL_CLIENT) { if (pevInflictor) { - if (pevInflictor == pKiller) + if (pevInflictor == pevKiller) { - // If the inflictor is the killer, then it must be their current weapon doing the damage - CBasePlayer *pAttacker = CBasePlayer::Instance(pKiller); - if (pAttacker && pAttacker->IsPlayer()) +#ifdef REGAMEDLL_FIXES + // Ignore the inflictor's weapon if victim killed self + if (pevKiller != pev) +#endif { - if (pAttacker->m_pActiveItem) - killer_weapon_name = pAttacker->m_pActiveItem->pszName(); + // If the inflictor is the killer, then it must be their current weapon doing the damage + CBasePlayer *pAttacker = CBasePlayer::Instance(pevKiller); + if (pAttacker && pAttacker->IsPlayer()) + { + if (pAttacker->m_pActiveItem) + killer_weapon_name = pAttacker->m_pActiveItem->pszName(); + } } } else @@ -781,10 +787,11 @@ const char *GetWeaponName(entvars_t *pevInflictor, entvars_t *pKiller) } // strip the monster_* or weapon_* from the inflictor's classname - const char cut_weapon[] = "weapon_"; + const char cut_weapon[] = "weapon_"; const char cut_monster[] = "monster_"; - const char cut_func[] = "func_"; + const char cut_func[] = "func_"; + // replace the code names with the 'real' names if (!Q_strncmp(killer_weapon_name, cut_weapon, sizeof(cut_weapon) - 1)) killer_weapon_name += sizeof(cut_weapon) - 1; @@ -955,7 +962,7 @@ BOOL EXT_FUNC CBasePlayer::__API_HOOK(TakeDamage)(entvars_t *pevInflictor, entva m_bKilledByGrenade = true; } - LogAttack(pAttack, this, bTeamAttack, int(flDamage), armorHit, pev->health - flDamage, pev->armorvalue, GetWeaponName(pevInflictor, pevAttacker)); + LogAttack(pAttack, this, bTeamAttack, int(flDamage), armorHit, pev->health - flDamage, pev->armorvalue, GetKillerWeaponName(pevInflictor, pevAttacker)); bTookDamage = CBaseMonster::TakeDamage(pevInflictor, pevAttacker, int(flDamage), bitsDamageType); if (bTookDamage) @@ -970,7 +977,7 @@ BOOL EXT_FUNC CBasePlayer::__API_HOOK(TakeDamage)(entvars_t *pevInflictor, entva CBasePlayer *pPlayerAttacker = CBasePlayer::Instance(pevAttacker); if (pPlayerAttacker && !pPlayerAttacker->IsBot() && pPlayerAttacker->m_iTeam != m_iTeam) { - TheCareerTasks->HandleEnemyInjury(GetWeaponName(pevInflictor, pevAttacker), pPlayerAttacker->HasShield(), pPlayerAttacker); + TheCareerTasks->HandleEnemyInjury(GetKillerWeaponName(pevInflictor, pevAttacker), pPlayerAttacker->HasShield(), pPlayerAttacker); } } @@ -1195,11 +1202,11 @@ BOOL EXT_FUNC CBasePlayer::__API_HOOK(TakeDamage)(entvars_t *pevInflictor, entva { Pain(m_LastHitGroup, false); } - + // keep track of amount of damage last sustained m_lastDamageAmount = flDamage; - - LogAttack(pAttack, this, bTeamAttack, flDamage, armorHit, pev->health - flDamage, pev->armorvalue, GetWeaponName(pevInflictor, pevAttacker)); + + LogAttack(pAttack, this, bTeamAttack, flDamage, armorHit, pev->health - flDamage, pev->armorvalue, GetKillerWeaponName(pevInflictor, pevAttacker)); // this cast to INT is critical!!! If a player ends up with 0.5 health, the engine will get that // as an int (zero) and think the player is dead! (this will incite a clientside screentilt, etc) @@ -1217,7 +1224,7 @@ BOOL EXT_FUNC CBasePlayer::__API_HOOK(TakeDamage)(entvars_t *pevInflictor, entva CBasePlayer *pPlayerAttacker = CBasePlayer::Instance(pevAttacker); if (pPlayerAttacker && !pPlayerAttacker->IsBot() && pPlayerAttacker->m_iTeam != m_iTeam) { - TheCareerTasks->HandleEnemyInjury(GetWeaponName(pevInflictor, pevAttacker), pPlayerAttacker->HasShield(), pPlayerAttacker); + TheCareerTasks->HandleEnemyInjury(GetKillerWeaponName(pevInflictor, pevAttacker), pPlayerAttacker->HasShield(), pPlayerAttacker); } } @@ -1423,12 +1430,12 @@ void CBasePlayer::PackDeadPlayerItems() int nBestWeight = 0; CBasePlayerItem *pBestItem = nullptr; -#ifdef REGAMEDLL_ADD +#ifdef REGAMEDLL_ADD int iGunsPacked = 0; - if (iPackGun == GR_PLR_DROP_GUN_ACTIVE) + if (iPackGun == GR_PLR_DROP_GUN_ACTIVE) { - // check if we've just already dropped our active gun + // check if we've just already dropped our active gun if (!bSkipPrimSec && m_pActiveItem && m_pActiveItem->CanDrop() && m_pActiveItem->iItemSlot() < KNIFE_SLOT) { pBestItem = m_pActiveItem; @@ -1437,7 +1444,7 @@ void CBasePlayer::PackDeadPlayerItems() } // are we allowing nade drop? - if ((int)nadedrops.value >= 1) + if ((int)nadedrops.value >= 1) { // goto item loop but skip guns iPackGun = GR_PLR_DROP_GUN_ALL; @@ -1468,7 +1475,7 @@ void CBasePlayer::PackDeadPlayerItems() #endif ) { -#ifdef REGAMEDLL_ADD +#ifdef REGAMEDLL_ADD if (iPackGun == GR_PLR_DROP_GUN_ALL) { CBasePlayerItem *pNext = pPlayerItem->m_pNext; @@ -1477,10 +1484,10 @@ void CBasePlayer::PackDeadPlayerItems() if (pWeaponBox) { // just push a few units in forward to separate them - pWeaponBox->pev->velocity = pWeaponBox->pev->velocity * (1.0 + (iGunsPacked * 0.2)); + pWeaponBox->pev->velocity = pWeaponBox->pev->velocity * (1.0 + (iGunsPacked * 0.2)); iGunsPacked++; } - + pPlayerItem = pNext; continue; } @@ -2121,7 +2128,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Killed)(entvars_t *pevAttacker, int iGib) if (IsBot() && IsBlind()) // dystopm: shouldn't be !IsBot() ? wasBlind = true; - TheCareerTasks->HandleEnemyKill(wasBlind, GetWeaponName(g_pevLastInflictor, pevAttacker), m_bHeadshotKilled, killerHasShield, pAttacker, this); // last 2 param swapped to match function definition + TheCareerTasks->HandleEnemyKill(wasBlind, GetKillerWeaponName(g_pevLastInflictor, pevAttacker), m_bHeadshotKilled, killerHasShield, pAttacker, this); // last 2 param swapped to match function definition } } #endif @@ -2152,7 +2159,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Killed)(entvars_t *pevAttacker, int iGib) { if (TheCareerTasks) { - TheCareerTasks->HandleEnemyKill(wasBlind, GetWeaponName(g_pevLastInflictor, pevAttacker), m_bHeadshotKilled, killerHasShield, this, pPlayer); + TheCareerTasks->HandleEnemyKill(wasBlind, GetKillerWeaponName(g_pevLastInflictor, pevAttacker), m_bHeadshotKilled, killerHasShield, this, pPlayer); } } } @@ -2434,7 +2441,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(Killed)(entvars_t *pevAttacker, int iGib) #ifndef REGAMEDLL_FIXES // NOTE: moved to RemoveDefuser - m_bIsDefusing = false; + m_bIsDefusing = false; #endif BuyZoneIcon_Clear(this); @@ -3629,7 +3636,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(JoiningThink)() #ifndef REGAMEDLL_FIXES // NOTE: client already clears StatusIcon on join - MESSAGE_BEGIN(MSG_ONE, gmsgStatusIcon, nullptr, pev); + MESSAGE_BEGIN(MSG_ONE, gmsgStatusIcon, nullptr, pev); WRITE_BYTE(STATUSICON_HIDE); WRITE_STRING("defuser"); MESSAGE_END(); @@ -4637,7 +4644,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(PreThink)() m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = (TRAIN_NEW | TRAIN_OFF); -#ifdef REGAMEDLL_FIXES +#ifdef REGAMEDLL_FIXES if (pTrain && pTrain->Classify() == CLASS_VEHICLE) // ensure func_vehicle's m_pDriver assignation #endif { @@ -4656,7 +4663,7 @@ void EXT_FUNC CBasePlayer::__API_HOOK(PreThink)() m_afPhysicsFlags &= ~PFLAG_ONTRAIN; m_iTrain = (TRAIN_NEW | TRAIN_OFF); -#ifdef REGAMEDLL_FIXES +#ifdef REGAMEDLL_FIXES if (pTrain->Classify() == CLASS_VEHICLE) // ensure func_vehicle's m_pDriver assignation #endif { @@ -5162,7 +5169,17 @@ void EXT_FUNC CBasePlayer::__API_HOOK(PostThink)() #endif { m_LastHitGroup = HITGROUP_GENERIC; - TakeDamage(VARS(eoNullEntity), VARS(eoNullEntity), flFallDamage, DMG_FALL); + + // FIXED: The player falling to the ground, + // the damage caused by the fall is initiated by himself (and not by the world) + entvars_t *pevAttacker = +#ifdef REGAMEDLL_FIXES + pev; +#else + VARS(eoNullEntity); +#endif + TakeDamage(pevAttacker, pevAttacker, flFallDamage, DMG_FALL); + pev->punchangle.x = 0; if (TheBots) { @@ -8827,22 +8844,22 @@ void CBasePlayer::SpawnClientSideCorpse() char *pModel = GET_KEY_VALUE(infobuffer, "model"); float timeDiff = pev->animtime - gpGlobals->time; -#ifdef REGAMEDLL_ADD +#ifdef REGAMEDLL_ADD if (CGameRules::GetDyingTime() < DEATH_ANIMATION_TIME) // a short time, timeDiff estimates to be small { float animDuration = GetDyingAnimationDuration(); - // client receives a negative value - animDuration *= -1.0; + // client receives a negative value + animDuration *= -1.0; if (animDuration < timeDiff) // reasonable way to fix client side unfinished sequence bug { - // by some reason, if client receives a value less - // than "(negative current sequence time) * 100" + // by some reason, if client receives a value less + // than "(negative current sequence time) * 100" // animation will play visually awkward - // at this function call time, player death animation + // at this function call time, player death animation // has already finished so we can safely fake it - timeDiff = animDuration; + timeDiff = animDuration; } } #endif @@ -8865,7 +8882,7 @@ void CBasePlayer::SpawnClientSideCorpse() #ifndef REGAMEDLL_FIXES // already defined in StartDeathCam m_canSwitchObserverModes = true; -#endif +#endif if (TheTutor) { @@ -10132,7 +10149,7 @@ void CBasePlayer::RemoveDefuser() SetProgressBarTime(0); m_bIsDefusing = false; } -#else +#else SetProgressBarTime(0); #endif } @@ -10458,10 +10475,10 @@ bool CBasePlayer::Kill() { if (GetObserverMode() != OBS_NONE) return false; - + if (m_iJoiningState != JOINED) return false; - + m_LastHitGroup = HITGROUP_GENERIC; // have the player kill himself @@ -10470,6 +10487,6 @@ bool CBasePlayer::Kill() if (CSGameRules()->m_pVIP == this) CSGameRules()->m_iConsecutiveVIP = 10; - + return true; } diff --git a/regamedll/dlls/player.h b/regamedll/dlls/player.h index 9d9aa67c2..625e60fd6 100644 --- a/regamedll/dlls/player.h +++ b/regamedll/dlls/player.h @@ -637,6 +637,7 @@ class CBasePlayer: public CBaseMonster { bool GetIntoGame(); bool ShouldToShowAccount(CBasePlayer *pReceiver) const; bool ShouldToShowHealthInfo(CBasePlayer *pReceiver) const; + const char *GetKillerWeaponName(entvars_t *pevInflictor, entvars_t *pevKiller) const; CBasePlayerItem *GetItemByName(const char *itemName); CBasePlayerItem *GetItemById(WeaponIdType weaponID); @@ -1000,7 +1001,6 @@ void SendItemStatus(CBasePlayer *pPlayer); const char *GetCSModelName(int item_id); Vector VecVelocityForDamage(float flDamage); int TrainSpeed(int iSpeed, int iMax); -const char *GetWeaponName(entvars_t *pevInflictor, entvars_t *pKiller); void LogAttack(CBasePlayer *pAttacker, CBasePlayer *pVictim, int teamAttack, int healthHit, int armorHit, int newHealth, int newArmor, const char *killer_weapon_name); bool CanSeeUseable(CBasePlayer *me, CBaseEntity *pEntity); void FixPlayerCrouchStuck(edict_t *pPlayer); diff --git a/regamedll/msvc/ReGameDLL.vcxproj b/regamedll/msvc/ReGameDLL.vcxproj index b3227cd45..c31abc6e9 100644 --- a/regamedll/msvc/ReGameDLL.vcxproj +++ b/regamedll/msvc/ReGameDLL.vcxproj @@ -46,7 +46,7 @@ - + @@ -787,6 +787,7 @@ + @@ -1124,4 +1125,4 @@ - + \ No newline at end of file diff --git a/regamedll/msvc/ReGameDLL.vcxproj.filters b/regamedll/msvc/ReGameDLL.vcxproj.filters index 3e9c67f15..84536f321 100644 --- a/regamedll/msvc/ReGameDLL.vcxproj.filters +++ b/regamedll/msvc/ReGameDLL.vcxproj.filters @@ -1052,5 +1052,8 @@ dlls\addons + + public + \ No newline at end of file diff --git a/regamedll/public/regamedll/regamedll_api.h b/regamedll/public/regamedll/regamedll_api.h index 57da8d290..70162c259 100644 --- a/regamedll/public/regamedll/regamedll_api.h +++ b/regamedll/public/regamedll/regamedll_api.h @@ -38,7 +38,7 @@ #include #define REGAMEDLL_API_VERSION_MAJOR 5 -#define REGAMEDLL_API_VERSION_MINOR 23 +#define REGAMEDLL_API_VERSION_MINOR 24 // CBasePlayer::Spawn hook typedef IHookChainClass IReGameHook_CBasePlayer_Spawn; @@ -588,6 +588,10 @@ typedef IHookChainRegistry IReGameHookRegistry_CSGameRules_TeamS typedef IHookChain IReGameHook_CSGameRules_PlayerGotWeapon; typedef IHookChainRegistry IReGameHookRegistry_CSGameRules_PlayerGotWeapon; +// CHalfLifeMultiplay::SendDeathMessage hook +typedef IHookChain IReGameHook_CSGameRules_SendDeathMessage; +typedef IHookChainRegistry IReGameHookRegistry_CSGameRules_SendDeathMessage; + // CBotManager::OnEvent hook typedef IHookChain IReGameHook_CBotManager_OnEvent; typedef IHookChainRegistry IReGameHookRegistry_CBotManager_OnEvent; @@ -759,7 +763,7 @@ class IReGameHookchains { virtual IReGameHookRegistry_AddMultiDamage *AddMultiDamage() = 0; virtual IReGameHookRegistry_ApplyMultiDamage *ApplyMultiDamage() = 0; virtual IReGameHookRegistry_BuyItem *BuyItem() = 0; - virtual IReGameHookRegistry_CSGameRules_Think *CSGameRules_Think() = 0; + virtual IReGameHookRegistry_CSGameRules_Think *CSGameRules_Think() = 0; virtual IReGameHookRegistry_CSGameRules_TeamFull *CSGameRules_TeamFull() = 0; virtual IReGameHookRegistry_CSGameRules_TeamStacked *CSGameRules_TeamStacked() = 0; virtual IReGameHookRegistry_CSGameRules_PlayerGotWeapon *CSGameRules_PlayerGotWeapon() = 0; @@ -769,6 +773,7 @@ class IReGameHookchains { virtual IReGameHookRegistry_CBasePlayerWeapon_ItemPostFrame *CBasePlayerWeapon_ItemPostFrame() = 0; virtual IReGameHookRegistry_CBasePlayerWeapon_KickBack *CBasePlayerWeapon_KickBack() = 0; virtual IReGameHookRegistry_CBasePlayerWeapon_SendWeaponAnim *CBasePlayerWeapon_SendWeaponAnim() = 0; + virtual IReGameHookRegistry_CSGameRules_SendDeathMessage *CSGameRules_SendDeathMessage() = 0; }; struct ReGameFuncs_t { From ca493526e84011d7b0d5f9e901d396e12ce81712 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Wed, 13 Sep 2023 22:53:26 +0700 Subject: [PATCH 4/5] Add domination and revenge --- regamedll/dlls/API/CSPlayer.cpp | 11 ++++++ regamedll/dlls/gamerules.h | 8 +++- regamedll/dlls/multiplay_gamerules.cpp | 48 ++++++++++++++++++++++- regamedll/dlls/player.cpp | 2 + regamedll/public/regamedll/API/CSPlayer.h | 31 +++++++++++++++ 5 files changed, 97 insertions(+), 3 deletions(-) diff --git a/regamedll/dlls/API/CSPlayer.cpp b/regamedll/dlls/API/CSPlayer.cpp index 4c7d22e26..f0b773554 100644 --- a/regamedll/dlls/API/CSPlayer.cpp +++ b/regamedll/dlls/API/CSPlayer.cpp @@ -566,6 +566,17 @@ void CCSPlayer::ResetVars() m_bSpawnProtectionEffects = false; } +// Resets all stats +void CCSPlayer::ResetAllStats() +{ + // Resets the kill history for this player + for (int i = 0; i < MAX_CLIENTS; i++) + { + m_iNumKilledByUnanswered[i] = 0; + m_bPlayerDominated[i] = false; + } +} + void CCSPlayer::OnSpawn() { m_bGameForcingRespawn = false; diff --git a/regamedll/dlls/gamerules.h b/regamedll/dlls/gamerules.h index c64e18a42..54efa9a4d 100644 --- a/regamedll/dlls/gamerules.h +++ b/regamedll/dlls/gamerules.h @@ -220,6 +220,10 @@ enum GR_NEUTRAL, }; +// The number of times you must kill a given player to be dominating them +// Should always be more than 1 +const int CS_KILLS_FOR_DOMINATION = 4; + enum DeathMessageFlags { // float[3] @@ -245,7 +249,9 @@ enum KillRarity KILLRARITY_NOSCOPE = 0x004, // The killer player kills the victim with a sniper rifle with no scope KILLRARITY_PENETRATED = 0x008, // The killer player kills the victim through walls KILLRARITY_THROUGH_SMOKE = 0x010, // The killer player kills the victim through smoke - KILLRARITY_ASSIST_FLASH = 0x020 // The killer player kills the victim with an assistant flashbang grenade + KILLRARITY_ASSIST_FLASH = 0x020, // The killer player kills the victim with an assistant flashbang grenade + KILLRARITY_DOMINATION = 0x040, // The killer player kills is dominating victim + KILLRARITY_REVENGE = 0x080 // The killer player kills got revenge on victim }; class CItem; diff --git a/regamedll/dlls/multiplay_gamerules.cpp b/regamedll/dlls/multiplay_gamerules.cpp index fd0b29c3f..fb78706fa 100644 --- a/regamedll/dlls/multiplay_gamerules.cpp +++ b/regamedll/dlls/multiplay_gamerules.cpp @@ -4082,7 +4082,7 @@ LINK_HOOK_CLASS_VOID_CUSTOM_CHAIN(CHalfLifeMultiplay, CSGameRules, DeathNotice, void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, entvars_t *pevKiller, entvars_t *pevInflictor) { // by default, the player is killed by the world - CBaseEntity *pKiller = (pevKiller->flags & FL_CLIENT) ? CBaseEntity::Instance(pevKiller) : nullptr; + CBasePlayer *pKiller = (pevKiller->flags & FL_CLIENT) ? CBasePlayer::Instance(pevKiller) : nullptr; const char *killer_weapon_name = pVictim->GetKillerWeaponName(pevInflictor, pevKiller); if (!TheTutor) @@ -4108,6 +4108,16 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, } SendDeathMessage(pKiller, pVictim, pAssister, pevInflictor, killer_weapon_name, iDeathMessageFlags, iRarityOfKill); + + // Updates the stats of who has killed whom + if (pKiller && pKiller->IsPlayer() && PlayerRelationship(pVictim, pKiller) != GR_TEAMMATE) + { + int iPlayerIndexKiller = pKiller->entindex(); + int iPlayerIndexVictim = pVictim->entindex(); + + pKiller->CSPlayer()->m_iNumKilledByUnanswered[iPlayerIndexVictim - 1] = 0; + pVictim->CSPlayer()->m_iNumKilledByUnanswered[iPlayerIndexKiller - 1]++; + } #else MESSAGE_BEGIN(MSG_ALL, gmsgDeathMsg); WRITE_BYTE(pKiller ? pKiller->entindex() : 0); // the killer @@ -4129,7 +4139,7 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(DeathNotice)(CBasePlayer *pVictim, else if (pevKiller->flags & FL_CLIENT) { const char *VictimTeam = GetTeam(pVictim->m_iTeam); - const char *KillerTeam = (pKiller && pKiller->IsPlayer()) ? GetTeam(((CBasePlayer *)pKiller)->m_iTeam) : ""; + const char *KillerTeam = (pKiller && pKiller->IsPlayer()) ? GetTeam(pKiller->m_iTeam) : ""; UTIL_LogPrintf("\"%s<%i><%s><%s>\" killed \"%s<%i><%s><%s>\" with \"%s\"\n", STRING(pevKiller->netname), GETPLAYERUSERID(ENT(pevKiller)), GETPLAYERAUTHID(ENT(pevKiller)), KillerTeam, STRING(pVictim->pev->netname), GETPLAYERUSERID(pVictim->edict()), GETPLAYERAUTHID(pVictim->edict()), VictimTeam, killer_weapon_name); @@ -5272,6 +5282,31 @@ int CHalfLifeMultiplay::GetRarityOfKill(CBaseEntity *pKiller, CBasePlayer *pVict const Vector inEyePos = pKillerPlayer->EyePosition(); if (TheCSBots()->IsLineBlockedBySmoke(&inEyePos, &pVictim->pev->origin)) iRarity |= KILLRARITY_THROUGH_SMOKE; + + // Calculate # of unanswered kills between killer & victim + // This is plus 1 as this function gets called before the stat is updated + // That is done so that the domination and revenge will be calculated prior + // to the death message being sent to the clients + int iAttackerEntityIndex = pKillerPlayer->entindex(); + assert(iAttackerEntityIndex >= 0 && iAttackerEntityIndex < MAX_CLIENTS); + + int iKillsUnanswered = pVictim->CSPlayer()->m_iNumKilledByUnanswered[iAttackerEntityIndex - 1] + 1; + if (iKillsUnanswered == CS_KILLS_FOR_DOMINATION || pKillerPlayer->CSPlayer()->IsPlayerDominated(pVictim->entindex() - 1)) + { + // this is the Nth unanswered kill between killer and victim, killer is now dominating victim + iRarity |= KILLRARITY_DOMINATION; + + // set victim to be dominated by killer + pKillerPlayer->CSPlayer()->SetPlayerDominated(pVictim, true); + } + else if (pVictim->CSPlayer()->IsPlayerDominated(pKillerPlayer->entindex() - 1)) + { + // the killer killed someone who was dominating him, gains revenge + iRarity |= KILLRARITY_REVENGE; + + // set victim to no longer be dominating the killer + pVictim->CSPlayer()->SetPlayerDominated(pKillerPlayer, false); + } } return iRarity; @@ -5319,6 +5354,15 @@ void EXT_FUNC CHalfLifeMultiplay::__API_HOOK(SendDeathMessage)(CBaseEntity *pKil iSendDeathMessageFlags &= ~PLAYERDEATH_POSITION; } + // An recipient a client is a victim that involved in this kill + if (pPlayer == pVictim && pVictim != pKillerPlayer) + { + // Sets a domination kill for recipient of the victim once until revenge + int iKillsUnanswered = pVictim->CSPlayer()->m_iNumKilledByUnanswered[pKillerPlayer->entindex() - 1]; + if (iKillsUnanswered >= CS_KILLS_FOR_DOMINATION) + iRarityOfKill &= ~KILLRARITY_DOMINATION; + } + MESSAGE_BEGIN(MSG_ONE, gmsgDeathMsg, nullptr, pPlayer->pev); WRITE_BYTE((pKiller && pKiller->IsPlayer()) ? pKiller->entindex() : 0); // the killer WRITE_BYTE(pVictim->entindex()); // the victim diff --git a/regamedll/dlls/player.cpp b/regamedll/dlls/player.cpp index de90f3e6b..32235fab0 100644 --- a/regamedll/dlls/player.cpp +++ b/regamedll/dlls/player.cpp @@ -6008,6 +6008,8 @@ void CBasePlayer::Reset() if (CSPlayer()->GetProtectionState() == CCSPlayer::ProtectionSt_Active) { RemoveSpawnProtection(); } + + CSPlayer()->ResetAllStats(); #endif } diff --git a/regamedll/public/regamedll/API/CSPlayer.h b/regamedll/public/regamedll/API/CSPlayer.h index 96955884b..455edae18 100644 --- a/regamedll/public/regamedll/API/CSPlayer.h +++ b/regamedll/public/regamedll/API/CSPlayer.h @@ -59,6 +59,13 @@ class CCSPlayer: public CCSMonster { m_iUserID(-1) { m_szModel[0] = '\0'; + + // Resets the kill history for this player + for (int i = 0; i < MAX_CLIENTS; i++) + { + m_iNumKilledByUnanswered[i] = 0; + m_bPlayerDominated[i] = false; + } } virtual bool IsConnected() const; @@ -110,11 +117,16 @@ class CCSPlayer: public CCSMonster { virtual void OnSpawnEquip(bool addDefault = true, bool equipGame = true); virtual void SetScoreboardAttributes(CBasePlayer *destination = nullptr); + bool IsPlayerDominated(int iPlayerIndex) const; + void SetPlayerDominated(CBasePlayer *pPlayer, bool bDominated); + void ResetVars(); + void ResetAllStats(); void OnSpawn(); void OnKilled(); void OnConnect(); + CBasePlayer *BasePlayer() const; public: @@ -158,6 +170,8 @@ class CCSPlayer: public CCSMonster { DamageList_t m_DamageList; // A unified array of recorded damage that includes giver and taker in each entry DamageList_t &GetDamageList() { return m_DamageList; } void RecordDamage(CBasePlayer *pAttacker, float flDamage, float flFlashDurationTime = -1); + int m_iNumKilledByUnanswered[MAX_CLIENTS]; // [0-31] how many unanswered kills this player has been dealt by each other player + bool m_bPlayerDominated[MAX_CLIENTS]; // [0-31] array of state per other player whether player is dominating other players }; // Inlines @@ -179,3 +193,20 @@ inline CCSPlayer::EProtectionState CCSPlayer::GetProtectionState() const // has expired return ProtectionSt_Expired; } + +// Returns whether this player is dominating the specified other player +inline bool CCSPlayer::IsPlayerDominated(int iPlayerIndex) const +{ + if (iPlayerIndex < 0 || iPlayerIndex >= MAX_CLIENTS) + return false; + + return m_bPlayerDominated[iPlayerIndex]; +} + +// Sets whether this player is dominating the specified other player +inline void CCSPlayer::SetPlayerDominated(CBasePlayer *pPlayer, bool bDominated) +{ + int iPlayerIndex = pPlayer->entindex(); + assert(iPlayerIndex >= 0 && iPlayerIndex < MAX_CLIENTS); + m_bPlayerDominated[iPlayerIndex - 1] = bDominated; +} From faad5641f6caab12dead2ed7766eb1ac7f92fa8b Mon Sep 17 00:00:00 2001 From: s1lentq Date: Wed, 13 Sep 2023 22:57:53 +0700 Subject: [PATCH 5/5] Fix typo --- regamedll/dlls/gamerules.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/regamedll/dlls/gamerules.h b/regamedll/dlls/gamerules.h index 54efa9a4d..70d67c224 100644 --- a/regamedll/dlls/gamerules.h +++ b/regamedll/dlls/gamerules.h @@ -250,8 +250,8 @@ enum KillRarity KILLRARITY_PENETRATED = 0x008, // The killer player kills the victim through walls KILLRARITY_THROUGH_SMOKE = 0x010, // The killer player kills the victim through smoke KILLRARITY_ASSIST_FLASH = 0x020, // The killer player kills the victim with an assistant flashbang grenade - KILLRARITY_DOMINATION = 0x040, // The killer player kills is dominating victim - KILLRARITY_REVENGE = 0x080 // The killer player kills got revenge on victim + KILLRARITY_DOMINATION = 0x040, // The killer player dominates the victim + KILLRARITY_REVENGE = 0x080 // The killer player got revenge on the victim }; class CItem;