From e5f04e0c0ce9d6b5ec2ec605cac63069bcb2a980 Mon Sep 17 00:00:00 2001 From: wtclaude Date: Wed, 7 Oct 2026 13:41:57 -0500 Subject: [PATCH] fix(bridge): Rust's animals and heals count toward titles again (runicnpc 9d) - Animal kills: Rust's animals are now Rust.Ai.Gen2.BaseNPC2 (stag, boar, wolf, bears, big cats, crocodile, farm animals, critters, sea life), not BaseNpc, so no animal kill counted as an animal or an NPC kill. They count as both again; ScientistNPC2, the one person among them, counts as a human NPC kill. - Heals: OnHealingItemUse is (tool, fromPlayer, toTarget). The bridge took two parameters, received the healer as the target, and dropped every heal as healing yourself. It now reads the target, and one act of help (a syringe revive raises both hooks, the heal first) counts once whichever hook comes first. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY --- overlay/oxide/plugins/RunicGateway.cs | 84 ++++++++++++++++----------- 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/overlay/oxide/plugins/RunicGateway.cs b/overlay/oxide/plugins/RunicGateway.cs index 32b679c..4217de3 100644 --- a/overlay/oxide/plugins/RunicGateway.cs +++ b/overlay/oxide/plugins/RunicGateway.cs @@ -1875,7 +1875,22 @@ namespace Oxide.Plugins // two meanings of one word would be two answers to "how many NPCs did I kill". // `animalKills` counts it AGAIN, separately (§5.2): the public NPC column keeps its // meaning mid-wipe, and the site takes the human NPCs as the difference. - if (entity is BaseNpc) + // + // Rust's own animals are now `Rust.Ai.Gen2.BaseNPC2` — stag, boar, wolf, bears, big cats, + // crocodile, farm animals, critters, sea life — and not `BaseNpc`, which is all this + // tested until the RunicNPC 9d walk found no animal kill had counted at all. `BaseNpc` + // stays for whatever still derives from it. `ScientistNPC2` is the one person among + // them, so it is a human NPC kill, not an animal. + if (entity is Rust.Ai.Gen2.ScientistNPC2) + { + Tally tally = TallyFor(attacker); + tally.NpcKills++; + tally.Killed(info, entity, false); + CreditKill(attacker, false); + return; + } + + if (entity is BaseNpc || entity is Rust.Ai.Gen2.BaseNPC2) { Tally tally = TallyFor(attacker); tally.NpcKills++; @@ -2065,55 +2080,56 @@ namespace Oxide.Plugins }); } - // A syringe on a downed player raises `OnPlayerRevive` and then `OnHealingItemUse` for the - // same use; one act of help is one heal. The revive is remembered for the frame it happened in. - private ulong _reviveHealer; - private ulong _reviveTarget; - private int _reviveFrame = -1; + // A syringe on a downed player raises `OnHealingItemUse` and then, from inside the same + // `GiveEffectsTo`, `OnPlayerRevive`; one act of help is one heal. The last one counted is + // remembered for the frame it happened in, and the other hook skips the same pair. + private ulong _helpHealer; + private ulong _helpTarget; + private int _helpFrame = -1; - /// - /// Void: a non-null return refuses the heal. is what is healed; - /// the tool's owner is who heals. Healing yourself is not a heal (D174). - /// - /// - /// The target is taken as object: the game's `GiveEffectsTo` now takes an - /// `IMedicalToolTarget`, which is not always a player, and a parameter typed - /// `BasePlayer` would simply never be called for the hook's current shape. Only a - /// player healed counts. - /// - /// - private void OnHealingItemUse(MedicalTool tool, object target) + /// Counts one act of help unless it is yourself, an NPC, or the other hook already did this frame. + private void CountHelp(BasePlayer healer, BasePlayer player) { - MarkHook("OnHealingItemUse"); - - BasePlayer player = target as BasePlayer; - BasePlayer healer = tool != null ? tool.GetOwnerPlayer() : null; - if (healer == null || healer.IsNpc || player == null || player.IsNpc || (ulong)healer.userID == (ulong)player.userID) return; - if (_reviveFrame == UnityEngine.Time.frameCount - && _reviveHealer == (ulong)healer.userID && _reviveTarget == (ulong)player.userID) + int frame = UnityEngine.Time.frameCount; + + if (_helpFrame == frame && _helpHealer == (ulong)healer.userID && _helpTarget == (ulong)player.userID) return; + _helpHealer = healer.userID; + _helpTarget = player.userID; + _helpFrame = frame; + TallyFor(healer).Healed++; } + /// + /// Void: a non-null return refuses the heal. Healing yourself is not a heal (D174). + /// + /// + /// Rust's `MedicalTool.GiveEffectsTo(fromPlayer, toTarget)` raises it as + /// (tool, fromPlayer, toTarget). Until the RunicNPC 9d walk this took two + /// parameters, so the second — the healer — arrived as the target, every heal looked like + /// healing yourself, and none counted. The target is object: `IMedicalToolTarget` + /// is not always a player, and only a player healed counts. + /// + /// + private void OnHealingItemUse(MedicalTool tool, BasePlayer fromPlayer, object target) + { + MarkHook("OnHealingItemUse"); + + CountHelp(fromPlayer ?? (tool != null ? tool.GetOwnerPlayer() : null), target as BasePlayer); + } + /// Void: a non-null return refuses the revive. Reviving yourself is not possible, and is not counted. private void OnPlayerRevive(BasePlayer reviver, BasePlayer player) { MarkHook("OnPlayerRevive"); - if (reviver == null || reviver.IsNpc || player == null || player.IsNpc - || (ulong)reviver.userID == (ulong)player.userID) - return; - - _reviveHealer = reviver.userID; - _reviveTarget = player.userID; - _reviveFrame = UnityEngine.Time.frameCount; - - TallyFor(reviver).Healed++; + CountHelp(reviver, player); } /// No return behaviour. One rocket leaving a launcher.