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