

A client-side mod that makes kill indicators consistent when you are not host.
Requires Bepinex, but is not dependent on GTFO-API plugin, so you can remove GTFO-API.dll from plugins folder to keep vanilla rundown and progression.
The way this fix works is by keeping track of enemies you are shooting at:
onkill event from host is received for an enemy you are shooting at then a kill marker is shown.To prevent two kill markers from showing, the plugin keeps note of what enemies you are shooting at and what enemies it has already shown the kill marker for:
onkill event from host.markerLifeTime in config)To prevent a kill marker showing on an enemy you shot 30 minutes ago, the plugin keeps track of enemies you have shot at recently within a 1 second period (dictated by tagBufferPeriod in config)
using KillIndicatorFix;
[BepInPlugin(Module.GUID, Module.Name, Module.Version)]
[BepInDependency(KillIndicatorFix.BepInEx.Module.GUID, BepInDependency.DependencyFlags.HardDependency)]
public class Plugin : BasePlugin {
public override void Load() {
Kill.RegisterAll();
Kill.OnKillIndicator += (Action<EnemyAgent, ItemEquippable, long>)OnKillIndicator;
}
[OnKillIndicator]
/// <summary>
/// Is called whenever a kill indicator is shown for a given enemy.
/// </summary>
/// <param name="enemy">Enemy that kill indicator was shown for.</param>
/// <param name="item">Item used.</param>
/// <param name="delay">On client, if the local player made the kill, provides the delay in milliseconds from player shot to recieving death packet from host.</param>
private static void OnKillIndicator(EnemyAgent enemy, ItemEquippable item, long delay) {
}
}
If you have a mod that applies damage and you want KillIndicatorFix to work with it, you can alter the enemies HP value directly for clients and tag the enemy using:
/// <summary>
/// Tag an enemy for Kill Indicator Fix to handle kills.
/// </summary>
/// <param name="enemy">Enemy to tag.</param>
/// <param name="item">Item that applied the damage for this tag. If not provided, uses currently equipped weapon.</param>
/// <param name="localHitPosition">Where the indicator should appear. Typically this is set to the hit position for bullet hit. If not provided, uses last tag's hit position when available, otherwise uses eye position. NOTE: The position is local to the enemy, not world-space.</param>
Kill.TagEnemy(EnemyAgent enemy, ItemEquippable? item = null, Vector3? localHitPosition = null);
Example use:
Dam_EnemyDamageLimb limb = ...;
Dam_EnemyDamageBase enemy = ...
enemy.Health -= damage;
Kill.TagEnemy(enemy); // Tag the enemy, so when it dies KIF will show kill indicator as approapriate.
if (enemy.Health <= 0) {
limb.ShowHitIndicator(...); // Important to trigger local hit marker for KillIndicatorFix (accounts for delay due to ping)
}
This plugin alters the internal hp values stored by the game (when ran as client):
[HarmonyPatch(typeof(Dam_EnemyDamageLimb), nameof(Dam_EnemyDamageLimb.BulletDamage))]
[HarmonyPrefix]
public static void BulletDamage(Dam_EnemyDamageLimb __instance, float dam, ...)
{
...
// Apply damage modifiers (head, occiput etc...)
float num = __instance.ApplyWeakspotAndArmorModifiers(dam, precisionMulti);
num = __instance.ApplyDamageFromBehindBonus(num, position, direction);
__instance.m_base.Health -= num; // Alter the internal health value
...
}
The decision to do so was done out of laziness and to avoid overcomplicating the system. As a result, if your plugin relies on these values (either through modification or by reading it) then the plugin will be incompatible with this one.
If you wish to use the health values whilst still maintaining compatability, you will need to store and manage your own health values seperately. This can be done using a dictionary of InstanceIDs to corresponding health values and manually subtracting health from damage sources:
// Dictionary storing hp values for enemies
// make sure to clear this dictionary OnRundownStart() or something to prevent leaks.
private static Dictionary<int, float> hpTracker = new Dictionary<int, float>();
[HarmonyPatch(typeof(Dam_EnemyDamageLimb), nameof(Dam_EnemyDamageLimb.BulletDamage))]
[HarmonyPrefix]
public static void BulletDamage(Dam_EnemyDamageLimb __instance, float dam, ...)
{
...
Dam_EnemyDamageBase m_base = __instance.m_base;
EnemyAgent owner = m_base.Owner;
int instanceID = owner.GetInstanceID();
// Apply damage modifiers (head, occiput etc...)
float num = __instance.ApplyWeakspotAndArmorModifiers(dam, precisionMulti);
num = __instance.ApplyDamageFromBehindBonus(num, position, direction);
// Manage your own health tracker - maybe remove trackers when their hp falls below 0
if (hpTracker.ContainsKey(instanceID))
{
hpTracker[instanceID] = hpTracker[instanceID] - num;
}
else hpTracker.Add(instanceID, m_base.HealthMax - num);
...
}
It should be noted that the above example only tracks changes to hp from bullet damage, you will need to extend this example to include melee damage etc...
v0.1.6
v0.1.5
[HarmonyPriority(Priority.Last)] for improved compatabilityv0.1.4
v0.1.3
v0.1.2
v0.1.1
v0.1.0
v0.0.9
v0.0.8
v0.0.7
v0.0.6
v0.0.5
v0.0.4
v0.0.3
v0.0.2
EyePosition to make seeing the marker for giants clearerv0.0.1