C#:
using Oxide.Core;
using UnityEngine;
using Rust;
using System;
using System.Collections.Generic;
using Oxide.Core.Plugins;
namespace Oxide.Plugins
{
[Info("ReflectDamage", "Chernarust", "1.6.0")]
[Description("Reflects a portion of damage back to players in PvP, doubles headshot damage, and optionally applies a bleeding effect to the attacker. Requires specific permission.")]
public class ReflectDamage : RustPlugin
{
private const string permissionName = "reflectdamage.use";
private float reflectPercentage;
private bool applyBleeding;
private bool enableDamageToVictim;
private HashSet<BasePlayer> currentlyProcessing = new HashSet<BasePlayer>();
private HashSet<ulong> playersInAbandonedBases = new HashSet<ulong>();
private HashSet<ulong> playersInRaidableBases = new HashSet<ulong>();
[PluginReference] Plugin ZoneManager, DynamicPVP;
public static DamageConfig _config;
public class DamageConfig
{
public float bleedingIntensity = 5f;
public float ReflectPercentage = 50f;
public bool ApplyBleeding = true;
public bool EnableDamageToVictim = true;
public static DamageConfig DefaultConfig()
{
return new DamageConfig
{
bleedingIntensity = 5f,
ReflectPercentage = 50f,
ApplyBleeding = true,
EnableDamageToVictim = true
};
}
}
protected override void LoadDefaultConfig() => _config = DamageConfig.DefaultConfig();
protected override void LoadConfig()
{
base.LoadConfig();
try
{
_config = Config.ReadObject<DamageConfig>();
if (_config == null) LoadDefaultConfig();
SaveConfig();
}
catch (Exception e)
{
Debug.LogException(e);
PrintWarning("Creating new config file.");
LoadDefaultConfig();
}
}
protected override void SaveConfig() => Config.WriteObject(_config);
private void Init()
{
permission.RegisterPermission(permissionName, this);
LoadConfig();
reflectPercentage = _config.ReflectPercentage;
applyBleeding = _config.ApplyBleeding;
enableDamageToVictim = _config.EnableDamageToVictim;
}
private void OnEntityTakeDamage(BasePlayer victim, HitInfo info)
{
CanEntityTakeDamage(victim, info);
}
private object CanEntityTakeDamage(BasePlayer victim, HitInfo info)
{
if (info == null || victim == null)
return null;
var attacker = info.InitiatorPlayer;
if (attacker == null || victim == attacker || !IsRealPlayer(victim) || !IsRealPlayer(attacker))
return null;
if (!permission.UserHasPermission(attacker.UserIDString, permissionName))
return null;
// allow damage with TruePVE
if (currentlyProcessing.Contains(attacker) || currentlyProcessing.Contains(victim))
return true;
if (IsPlayerInZoneManagerZone(victim) && IsPlayerInZoneManagerZone(attacker))
return true;
if (IsPlayerInDynamicZone(victim) && IsPlayerInDynamicZone(attacker))
return true;
if (IsPlayerInRaidableZone(victim) && IsPlayerInRaidableZone(attacker))
return null;
try
{
currentlyProcessing.Add(victim);
currentlyProcessing.Add(attacker);
// Double the damage if it's a headshot
if (info.HitBone == 698017942) // 698017942 is the bone ID for the head in Rust
{
info.damageTypes.ScaleAll(2.0f);
}
// Reflect damage back to the attacker, using the percentage as a fraction
float damageToReflect = info.damageTypes.Total() * (reflectPercentage / 100f);
attacker.Hurt(damageToReflect, DamageType.Generic, victim);
// Apply bleeding effect if enabled
if (applyBleeding)
{
attacker.metabolism.bleeding.Add(_config.bleedingIntensity);
}
// Cancel the damage to the victim if configured
if (!enableDamageToVictim)
{
info.damageTypes.ScaleAll(0);
info.HitEntity = null;
info.DoHitEffects = false;
info.PointStart = Vector3.zero;
info.PointEnd = Vector3.zero;
return false; // block damage with TruePVE
}
return true; // allow damage with TruePVE
}
catch (Exception ex)
{
Puts($"Error in OnEntityTakeDamage: {ex.Message}");
}
finally
{
currentlyProcessing.Remove(victim);
currentlyProcessing.Remove(attacker);
}
return null; // can be true or false or null
}
private bool IsPlayerInZoneManagerZone(BasePlayer player)
{
string[] zoneIDs = (string[])ZoneManager?.Call("GetPlayerZoneIDs", player);
if (zoneIDs != null)
{
foreach (string zoneID in zoneIDs)
{
if ((bool)ZoneManager?.Call("IsPlayerInZone", zoneID, player))
return true;
}
}
return false;
}
private bool IsPlayerInDynamicZone(BasePlayer player)
{
string[] dynamicPVPZones = (string[])DynamicPVP?.Call("AllDynamicPVPZones");
if (dynamicPVPZones != null)
{
foreach (string zoneID in dynamicPVPZones)
{
if ((bool)DynamicPVP?.Call("IsDynamicPVPZone", zoneID) && (bool)DynamicPVP?.Call("IsPlayerInPVPDelay", player.userID))
return true;
}
}
return false;
}
private bool IsPlayerInRaidableZone(BasePlayer player)
{
// Check if player is in an abandoned base/raidable base
return playersInAbandonedBases.Contains(player.userID) || playersInRaidableBases.Contains(player.userID);
}
private bool IsRealPlayer(BasePlayer player) => player.UserIDString.IsSteamId();
private void OnPlayerEnteredAbandonedBase(BasePlayer player) => playersInAbandonedBases.Add(player.userID);
private void OnPlayerExitAbandonedBase(BasePlayer player) => playersInAbandonedBases.Remove(player.userID);
private void OnPlayerEnteredRaidableBase(BasePlayer player) => playersInRaidableBases.Add(player.userID);
private void OnPlayerExitedRaidableBase(BasePlayer player) => playersInRaidableBases.Remove(player.userID);
}
}