diff --git a/documentation/docs/event_schema.yml b/documentation/docs/event_schema.yml index a31361306..b1c60e892 100644 --- a/documentation/docs/event_schema.yml +++ b/documentation/docs/event_schema.yml @@ -589,6 +589,9 @@ paths: event: enum: - player_death + bomb: + type: boolean + description: Indicates if the player died from the bomb explosion. `Bomb` in SourceMod. headshot: type: boolean description: Indicates if the player died from a headshot. `Headshot` @@ -619,7 +622,11 @@ paths: description: Indicates if the player died from friendly fire. `FriendlyFire` in SourceMod. attacker: - "$ref": "#/components/schemas/Get5Player" + allOf: + - type: object + nullable: true + title: Get5Player + - "$ref": "#/components/schemas/Get5Player" assist: "$ref": "#/components/schemas/Get5AssisterObject" responses: { } @@ -995,6 +1002,7 @@ components: description: Describes a player. `Player` in SourceMod (or `Attacker` on `Get5PlayerDeathEvent`). Get5Weapon: type: object + nullable: true properties: name: type: string @@ -1007,7 +1015,7 @@ components: example: 27 description: The weapon ID used. See https://sm.alliedmods.net/new-api/cstrike/CSWeaponID `Id` in SourceMod. - description: Describes a weapon. `Weapon` in SourceMod. + description: Describes a weapon. `Weapon` in SourceMod. Use `HasWeapon()` to determine if `null` on `Get5PlayerDeathEvent`. Get5AssisterObject: type: object nullable: true @@ -1022,9 +1030,10 @@ components: type: boolean description: Indicates if the assist was a flash assist. `FlashAssist` in SourceMod. - description: 'Describes an assist to a kill. `null` if no assister. `Assist` - in SourceMod. **Note: Use `HasAssist()` in SourceMod to determine if the property - exists before accessing it.**' + description: | + Describes an assist to a kill. `null` if no assister. `Assist` + in SourceMod. Use `HasAssist()` in SourceMod to determine if the property + exists before accessing it. Get5GrenadeVictim: type: object properties: diff --git a/scripting/get5/stats.sp b/scripting/get5/stats.sp index 592f5de91..6c7e0f7a5 100644 --- a/scripting/get5/stats.sp +++ b/scripting/get5/stats.sp @@ -26,7 +26,7 @@ public Action HandlePlayerDamage(int victim, int &attacker, int &inflictor, floa return Plugin_Continue; } - if (attacker == victim || !IsValidClient(attacker) || !IsValidClient(victim)) { + if (!IsValidClient(attacker) || !IsValidClient(victim)) { return Plugin_Continue; } @@ -673,7 +673,7 @@ public Action Stats_PlayerDeathEvent(Event event, const char[] name, bool dontBr bool validAttacker = IsValidClient(attacker); bool validVictim = IsValidClient(victim); - bool validAssister = assister > 0 && IsValidClient(assister); + bool validAssister = IsValidClient(assister); if (!validVictim) { return Plugin_Continue; // Not sure how this would happen, but it's not something we care @@ -701,9 +701,18 @@ public Action Stats_PlayerDeathEvent(Event event, const char[] name, bool dontBr char weapon[32]; event.GetString("weapon", weapon, sizeof(weapon)); - int attackerTeam = 0; // 0 until we know attacker is valid. + CSWeaponID weaponId = CS_AliasToWeaponID(weapon); + + int attackerTeam = validAttacker ? GetClientTeam(attacker) : 0; int victimTeam = GetClientTeam(victim); - bool isSuicide = false; + + // suicide (kill console) is attacker == victim, weapon id 0, weapon "world" + // fall damage is weapon id 0, attacker 0, weapon "worldspawn" + // falling from vertigo is attacker 0, weapon id 0, weapon "trigger_hurt" + // c4 is attacker 0, weapon id 0, weapon planted_c4 + // with those in mind, we can determine suicide from this: + bool killedByBomb = StrEqual("planted_c4", weapon, true); + bool isSuicide = attacker == victim || (view_as(weaponId) == 0 && !killedByBomb); IncrementPlayerStat(victim, STAT_DEATHS); // used for calculating round KAST @@ -715,12 +724,7 @@ public Action Stats_PlayerDeathEvent(Event event, const char[] name, bool dontBr (victimTeam == CS_TEAM_CT) ? STAT_FIRSTDEATH_CT : STAT_FIRSTDEATH_T); } - CSWeaponID weaponId = CS_AliasToWeaponID(weapon); - - if (!validAttacker || attacker == victim) { - isSuicide = true; - } else { - attackerTeam = GetClientTeam(attacker); + if (!isSuicide) { if (attackerTeam != victimTeam) { if (!g_TeamFirstKillDone[attackerTeam]) { g_TeamFirstKillDone[attackerTeam] = true; @@ -755,10 +759,18 @@ public Action Stats_PlayerDeathEvent(Event event, const char[] name, bool dontBr } Get5PlayerDeathEvent playerDeathEvent = new Get5PlayerDeathEvent( - g_MatchID, g_MapNumber, g_RoundNumber, GetRoundTime(), new Get5Weapon(weapon, weaponId), + g_MatchID, g_MapNumber, g_RoundNumber, GetRoundTime(), GetPlayerObject(victim), headshot, validAttacker ? attackerTeam == victimTeam : false, - GetPlayerObject(attacker), event.GetBool("thrusmoke"), event.GetBool("noscope"), - event.GetBool("attackerblind"), isSuicide, event.GetInt("penetrated")); + event.GetBool("thrusmoke"), event.GetBool("noscope"), event.GetBool("attackerblind"), + isSuicide, event.GetInt("penetrated"), killedByBomb); + + if (validAttacker) { + playerDeathEvent.Attacker = GetPlayerObject(attacker); + } + + if (view_as(weaponId) > 0) { + playerDeathEvent.Weapon = new Get5Weapon(weapon, weaponId); + } if (validAssister) { bool assistedFlash = event.GetBool("assistedflash"); @@ -897,7 +909,7 @@ public Action Stats_PlayerBlindEvent(Event event, const char[] name, bool dontBr int victim = GetClientOfUserId(event.GetInt("userid")); int attacker = GetClientOfUserId(event.GetInt("attacker")); - if (attacker == victim || !IsValidClient(attacker) || !IsValidClient(victim)) { + if (!IsValidClient(attacker) || !IsValidClient(victim)) { return Plugin_Continue; } diff --git a/scripting/include/get5.inc b/scripting/include/get5.inc index 59f45d6a6..e95b26982 100644 --- a/scripting/include/get5.inc +++ b/scripting/include/get5.inc @@ -1196,6 +1196,16 @@ methodmap Get5PlayerWeaponEvent < Get5PlayerTimedRoundEvent { methodmap Get5PlayerDeathEvent < Get5PlayerWeaponEvent { + property bool Bomb { + public get() { + return this.GetBool("bomb"); + } + + public set(bool bomb) { + this.SetBool("bomb", bomb); + } + } + property bool Headshot { public get() { return this.GetBool("headshot"); @@ -1291,21 +1301,30 @@ methodmap Get5PlayerDeathEvent < Get5PlayerWeaponEvent { return this.GetObject("assist") != null; } + // Use before accessing "Attacker", as its getter will raise an exception if null. + public bool HasAttacker() { + return this.GetObject("attacker") != null; + } + + // Use before accessing "Weapon", as its getter will raise an exception if null. + public bool HasWeapon() { + return this.GetObject("weapon") != null; + } + public Get5PlayerDeathEvent( const char[] matchId, const int mapNumber, const int roundNumber, const int roundTime, - const Get5Weapon weapon, const Get5Player victim, const bool headshot, const bool friendlyFire, - const Get5Player attacker, const bool thruSmoke, const bool noScope, const bool attackerBlind, const bool suicide, - const int penetrated + const int penetrated, + const bool bomb ) { Get5PlayerDeathEvent self = view_as(new JSON_Object()); @@ -1314,19 +1333,20 @@ methodmap Get5PlayerDeathEvent < Get5PlayerWeaponEvent { self.MapNumber = mapNumber; self.RoundNumber = roundNumber; self.RoundTime = roundTime; - self.Weapon = weapon; self.Player = victim; self.Headshot = headshot; self.FriendlyFire = friendlyFire; - self.Attacker = attacker; self.ThruSmoke = thruSmoke; self.NoScope = noScope; self.AttackerBlind = attackerBlind; self.Suicide = suicide; self.Penetrated = penetrated; + self.Bomb = bomb; - // set assist to null initially + // set nullables to null initially self.SetObject("assist", null); + self.SetObject("attacker", null); + self.SetObject("weapon", null); return self; }