using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; using Oxide.Core.Configuration; using Oxide.Core.Plugins; using UnityEngine; namespace Oxide.Plugins { [Info("FacePunch", "Raul-Sorin Sorban", "2.4.3")] [Description("Punch players in the face (consensually).")] public class FacePunch : RustPlugin { private static readonly System.Random Random = new System.Random(); public const string DefaultHitEffect = "assets/bundled/prefabs/fx/impacts/stab/wood/wood1.prefab"; public const string DefaultBleedEffect = "assets/bundled/prefabs/fx/impacts/blunt/flesh/fleshbloodimpact.prefab"; public const string DefaultWaterSplashEffect = "assets/bundled/prefabs/fx/impacts/slash/water/water.prefab"; private Dictionary ItemHoldCache { get; } = new Dictionary(); [PluginReference] Plugin StaticLootables; #region Overrides #if !CARBON public DynamicConfigFile Config { get; } = new("oxide/config/FacePunch.json"); #else public DynamicConfigFile Config { get; } = new("FacePunch.json"); #endif private void Init() { InstallPermissions(); } private void Loaded() { if (!Config.Exists()) { ConfigInstance = new RootConfig(); OnServerSave(); } else { try { ConfigInstance = Config.ReadObject(); } catch (Exception exception) { Puts($"Broken configuration: {exception.Message}"); ConfigInstance = new RootConfig(); OnServerSave(); } } } private void OnServerSave() { Config.WriteObject(ConfigInstance); } private void OnPlayerInput(BasePlayer initiatorPlayer, InputState input) { if (initiatorPlayer.IsDead() || initiatorPlayer.IsWounded() || initiatorPlayer.IsSleeping() || !HasPermission(initiatorPlayer, UsePerm)) return; var holdsAnyItem = initiatorPlayer.IsHoldingEntity(); if (!ItemHoldCache.ContainsKey(initiatorPlayer)) ItemHoldCache[initiatorPlayer] = initiatorPlayer.GetHeldEntity(); var previouslyHeldEntity = ItemHoldCache[initiatorPlayer]; var newlyHeldEntity = initiatorPlayer.GetHeldEntity(); if (newlyHeldEntity != previouslyHeldEntity) { if (newlyHeldEntity is Keycard) { if (!TriggerPunchCooldown(initiatorPlayer, 0.8f)) return; } else if (newlyHeldEntity is Hammer) if (!TriggerPunchCooldown(initiatorPlayer, 0.8f)) return; initiatorPlayer.Invoke(() => { ItemHoldCache[initiatorPlayer] = initiatorPlayer.GetHeldEntity(); }, 0.8f); return; } if (((newlyHeldEntity is Keycard) || (newlyHeldEntity is Hammer)) && HasPermission(initiatorPlayer, UseSlicePerm)) { if ((!input.IsDown(BUTTON.FIRE_PRIMARY))) return; } else { if ((ConfigInstance.HeldClickPunching ? !input.IsDown(BUTTON.FIRE_PRIMARY) : !input.WasJustPressed(BUTTON.FIRE_PRIMARY))) return; } if (!GamePhysics.Trace(initiatorPlayer.eyes.HeadRay(), 0.0f, out var hitInfo, 1.5f, ~0)) { return; } if (holdsAnyItem) { if (initiatorPlayer.GetHeldEntity() is Keycard && HasPermission(initiatorPlayer, UseSlicePerm) && ConfigInstance.EnableSlice) Slice(initiatorPlayer, hitInfo.collider?.gameObject, hitInfo.point); else if (initiatorPlayer.GetHeldEntity() is Hammer && HasPermission(initiatorPlayer, UseHammerPerm) && ConfigInstance.EnableHammer) HammerHit(initiatorPlayer, hitInfo.collider?.gameObject, hitInfo.point); return; } Punch(initiatorPlayer, hitInfo); } #endregion #region Cooldown private Dictionary PunchCooldownPool { get; } = new Dictionary(); private Dictionary GutshotScreamCooldownPool { get; } = new Dictionary(); private bool TriggerPunchCooldown(BasePlayer player, float? cooldown = null) { if (cooldown == null) cooldown = ConfigInstance.PunchCooldown; if (PunchCooldownPool.ContainsKey(player.userID)) { var previousDate = new DateTime(PunchCooldownPool[player.userID]); if (DateTime.Now.Subtract(previousDate).TotalSeconds < cooldown) { return true; } PunchCooldownPool[player.userID] = DateTime.Now.Ticks; } else { PunchCooldownPool.Add(player.userID, DateTime.Now.Ticks); } return false; } private bool TriggerGutshotScreamCooldown(BasePlayer player) { if (GutshotScreamCooldownPool.ContainsKey(player.userID)) { var previousDate = new DateTime(GutshotScreamCooldownPool[player.userID]); if (DateTime.Now.Subtract(previousDate).TotalSeconds < ConfigInstance.GruntCooldown) { return true; } GutshotScreamCooldownPool[player.userID] = DateTime.Now.Ticks; } else { GutshotScreamCooldownPool.Add(player.userID, DateTime.Now.Ticks); } return false; } #endregion #region Methods public void Punch(BasePlayer initiatorPlayer, RaycastHit info) { var entity = info.GetEntity(); if (entity != null) { var targetPlayer = entity as BasePlayer; var damageableEntity = targetPlayer != null ? null : entity as BaseCombatEntity; var damageRule = entity as NPCPlayer != null ? ConfigInstance.NpcDamage : ConfigInstance.PlayerDamage; var resourceEntity = damageableEntity != null ? null : entity as ResourceEntity; var terrainObject = resourceEntity == null ? entity.transform.root : null; if (terrainObject == null) terrainObject = entity.transform; if (targetPlayer != null && targetPlayer != initiatorPlayer) { var impactEffect = targetPlayer.impactEffect.isValid ? targetPlayer.impactEffect.resourcePath : null; if (string.IsNullOrEmpty(impactEffect)) impactEffect = "assets/bundled/prefabs/fx/player/fall-damage.prefab"; if (TriggerPunchCooldown(initiatorPlayer)) return; if (!(initiatorPlayer.Distance(targetPlayer) <= damageRule.Distance)) return; Effect.server.Run("assets/bundled/prefabs/fx/player/swing_weapon.prefab", initiatorPlayer.transform.position, Vector3.zero); if (ConfigInstance.AnimateAttacker) initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, "hurry"); ServerMgr.Instance.Invoke(() => { Effect.server.Run(impactEffect, info.point, Vector3.zero); if (!ConfigInstance.OnePunchManMode) { damageRule.MakeBleed(initiatorPlayer); damageRule.MakeTargetBleed(targetPlayer, initiatorPlayer); } damageRule.MakeTargetHurt(targetPlayer, initiatorPlayer, ConfigInstance.OnePunchManMode); if (ConfigInstance.AnimatePlayerTargets) targetPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, "whoa"); }, 0.2f); return; } else { if (damageableEntity != null) { var impactEffect = damageableEntity.impactEffect.isValid ? damageableEntity.impactEffect.resourcePath : null; damageRule = null; if (damageableEntity is LootContainer) damageRule = ConfigInstance.LootContainerDamage; else if (damageableEntity is BradleyAPC) damageRule = ConfigInstance.BradleyDamage; else if (damageableEntity is BaseAnimalNPC) damageRule = ConfigInstance.AnimalDamage; else if (damageableEntity is RidableHorse) damageRule = ConfigInstance.HorseDamage; else if (damageableEntity is BuildingBlock) damageRule = ConfigInstance.BuildingBlocksDamage; if (damageRule == null) return; if (string.IsNullOrEmpty(impactEffect)) impactEffect = damageRule.FistBleedAmount > 0 && !damageRule.HasHandsCovered(initiatorPlayer) ? DefaultBleedEffect : DefaultHitEffect; if (TriggerPunchCooldown(initiatorPlayer)) return; if (initiatorPlayer.Distance(info.point) > damageRule.Distance) return; Effect.server.Run("assets/bundled/prefabs/fx/player/swing_weapon.prefab", initiatorPlayer.transform.position, Vector3.zero); if (ConfigInstance.AnimateAttacker) initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, "hurry"); ServerMgr.Instance.Invoke(() => { Effect.server.Run(impactEffect, info.point, Vector3.zero); if (!ConfigInstance.OnePunchManMode) { if (damageRule.MakeBleed(initiatorPlayer)) { if (!TriggerGutshotScreamCooldown(initiatorPlayer)) { ServerMgr.Instance.Invoke( () => { Effect.server.Run( "assets/bundled/prefabs/fx/player/gutshot_scream.prefab", initiatorPlayer.transform.position, Vector3.zero); }, 0.2f); } } } damageRule.MakeTargetHurt(damageableEntity, initiatorPlayer, ConfigInstance.OnePunchManMode, damageableEntity is BradleyAPC ? Rust.DamageType.Explosion : Rust.DamageType.Slash); }, 0.2f); return; } if (resourceEntity != null) { var impactEffect = resourceEntity.impactEffect.isValid ? resourceEntity.impactEffect.resourcePath : null; if (string.IsNullOrEmpty(impactEffect)) impactEffect = ConfigInstance.ResourceDamage.FistBleedAmount > 0 && !ConfigInstance.ResourceDamage.HasHandsCovered(initiatorPlayer) ? DefaultBleedEffect : DefaultHitEffect; if (TriggerPunchCooldown(initiatorPlayer)) return; if (initiatorPlayer.Distance(info.point) > ConfigInstance.ResourceDamage.Distance) return; Effect.server.Run("assets/bundled/prefabs/fx/player/swing_weapon.prefab", initiatorPlayer.transform.position, Vector3.zero); if (ConfigInstance.AnimateAttacker) initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, "hurry"); ServerMgr.Instance.Invoke(() => { initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, null); Effect.server.Run(impactEffect, info.point, Vector3.zero); if (!ConfigInstance.OnePunchManMode) { if (ConfigInstance.ResourceDamage.MakeBleed(initiatorPlayer)) { if (!TriggerGutshotScreamCooldown(initiatorPlayer)) { ServerMgr.Instance.Invoke( () => { Effect.server.Run( "assets/bundled/prefabs/fx/player/gutshot_scream.prefab", initiatorPlayer.transform.position, Vector3.zero); }, 0.2f); } } } }, 0.2f); return; } else if (terrainObject != null && initiatorPlayer.serverInput.IsDown(BUTTON.DUCK)) { if (terrainObject.GetComponent() != null) return; var impactEffect = ConfigInstance.TerrainDamage.FistBleedAmount > 0 && !ConfigInstance.TerrainDamage.HasHandsCovered(initiatorPlayer) ? DefaultBleedEffect : DefaultHitEffect; if (TriggerPunchCooldown(initiatorPlayer)) return; if (initiatorPlayer.Distance(info.point) > ConfigInstance.TerrainDamage.Distance) return; Effect.server.Run("assets/bundled/prefabs/fx/player/swing_weapon.prefab", initiatorPlayer.transform.position, Vector3.zero); if (ConfigInstance.AnimateAttacker) initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, "hurry"); ServerMgr.Instance.Invoke(() => { initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, null); Effect.server.Run(impactEffect, info.point, Vector3.zero); if (!ConfigInstance.OnePunchManMode) { if (ConfigInstance.TerrainDamage.MakeBleed(initiatorPlayer)) { if (!TriggerGutshotScreamCooldown(initiatorPlayer)) { ServerMgr.Instance.Invoke( () => { Effect.server.Run( "assets/bundled/prefabs/fx/player/gutshot_scream.prefab", initiatorPlayer.transform.position, Vector3.zero); }, 0.2f); } } } }, 0.2f); return; } } } var water = GetWaterBody(info); if (water != null) { if (TriggerPunchCooldown(initiatorPlayer)) return; if (initiatorPlayer.Distance(info.point) > ConfigInstance.WaterDamage.Distance) return; Effect.server.Run("assets/bundled/prefabs/fx/player/swing_weapon.prefab", initiatorPlayer.transform.position, Vector3.zero); if (ConfigInstance.AnimateAttacker) initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, "hurry"); ServerMgr.Instance.Invoke(() => { initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, null); Effect.server.Run(DefaultWaterSplashEffect, info.point, Vector3.zero); if (!ConfigInstance.OnePunchManMode) { var isSaltWater = water.Type == WaterBodyType.Ocean; ConfigInstance.WaterDamage.MakeHeal(initiatorPlayer, isSaltWater); if (ConfigInstance.WaterDamage.GiveTrout(initiatorPlayer, isSaltWater)) { if (!TriggerGutshotScreamCooldown(initiatorPlayer)) { ServerMgr.Instance.Invoke( () => { Effect.server.Run("assets/bundled/prefabs/fx/player/gutshot_scream.prefab", initiatorPlayer.transform.position, Vector3.zero); }, 0.2f); } } } }, 0.2f); } if (TriggerPunchCooldown(initiatorPlayer)) return; Effect.server.Run("assets/bundled/prefabs/fx/player/swing_weapon.prefab", initiatorPlayer.transform.position, Vector3.zero); if (ConfigInstance.AnimateAttacker) initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, "hurry"); ServerMgr.Instance.Invoke(() => { if (!TriggerGutshotScreamCooldown(initiatorPlayer)) { ServerMgr.Instance.Invoke( () => { Effect.server.Run( "assets/bundled/prefabs/fx/player/gutshot_scream.prefab", initiatorPlayer.transform.position, Vector3.zero); }, 0.2f); } ConfigInstance.StaticLootableDamage.MakeBleed(initiatorPlayer); initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, null); Effect.server.Run(DefaultBleedEffect, info.point, Vector3.zero); StaticLootables?.CallHook("EmulateAttack", initiatorPlayer, info.collider.gameObject, ConfigInstance.StaticLootableDamage.DamageMin, ConfigInstance.StaticLootableDamage.DamageMax); }, 0.2f); } public void Slice(BasePlayer initiatorPlayer, GameObject hitObject, Vector3 hitpoint) { var entity = hitObject?.transform?.GetComponentInParent(); if (hitObject != null) { var targetPlayer = entity as BasePlayer; var damageableEntity = targetPlayer != null ? null : entity as BaseCombatEntity; var damageRule = entity as NPCPlayer != null ? ConfigInstance.NpcDamage : ConfigInstance.PlayerDamage; var resourceEntity = damageableEntity != null ? null : entity as ResourceEntity; var terrainObject = resourceEntity == null ? hitObject?.transform.root : null; if (terrainObject == null) terrainObject = hitObject?.transform; if (targetPlayer != null && targetPlayer != initiatorPlayer) { var impactEffect = targetPlayer.impactEffect.isValid ? targetPlayer.impactEffect.resourcePath : null; if (string.IsNullOrEmpty(impactEffect)) impactEffect = DefaultBleedEffect; if (TriggerPunchCooldown(initiatorPlayer, ConfigInstance.SliceCooldown)) return; if (!(initiatorPlayer.Distance(targetPlayer) <= damageRule.Distance)) return; ServerMgr.Instance.Invoke(() => { Effect.server.Run(impactEffect, hitpoint, Vector3.zero); if (!ConfigInstance.OnePunchManMode) { damageRule.MakeBleed(initiatorPlayer); damageRule.MakeTargetBleed(targetPlayer, initiatorPlayer); } damageRule.MakeTargetHurt(targetPlayer, initiatorPlayer, ConfigInstance.OnePunchManMode); if (ConfigInstance.AnimatePlayerTargets) targetPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, "whoa"); }, 0.2f); return; } else if (damageableEntity != null) { var impactEffect = damageableEntity.impactEffect.isValid ? damageableEntity.impactEffect.resourcePath : null; damageRule = null; if (damageableEntity is LootContainer) damageRule = ConfigInstance.LootContainerDamage; else if (damageableEntity is BradleyAPC) damageRule = ConfigInstance.BradleyDamage; else if (damageableEntity is BaseAnimalNPC) { damageRule = ConfigInstance.AnimalDamage; impactEffect = DefaultBleedEffect; } else if (damageableEntity is RidableHorse) { damageRule = ConfigInstance.HorseDamage; impactEffect = DefaultBleedEffect; } else if (damageableEntity is BuildingBlock) damageRule = ConfigInstance.BuildingBlocksDamage; if (damageRule == null) return; if (string.IsNullOrEmpty(impactEffect)) impactEffect = DefaultHitEffect; if (TriggerPunchCooldown(initiatorPlayer, ConfigInstance.SliceCooldown)) return; if (initiatorPlayer.Distance(hitpoint) > damageRule.Distance) return; ServerMgr.Instance.Invoke(() => { Effect.server.Run(impactEffect, hitpoint, Vector3.zero); damageRule.MakeTargetHurt(damageableEntity, initiatorPlayer, ConfigInstance.OnePunchManMode, damageableEntity is BradleyAPC ? Rust.DamageType.Explosion : Rust.DamageType.Slash); }, 0.2f); return; } else if (resourceEntity != null) { var impactEffect = resourceEntity.impactEffect.isValid ? resourceEntity.impactEffect.resourcePath : null; if (string.IsNullOrEmpty(impactEffect)) impactEffect = DefaultHitEffect; if (TriggerPunchCooldown(initiatorPlayer, ConfigInstance.SliceCooldown)) return; if (initiatorPlayer.Distance(hitpoint) > ConfigInstance.ResourceDamage.Distance) return; ServerMgr.Instance.Invoke(() => { Effect.server.Run(impactEffect, hitpoint, Vector3.zero); }, 0.2f); return; } else if (terrainObject != null && initiatorPlayer.serverInput.IsDown(BUTTON.DUCK)) { if (terrainObject.GetComponent() != null) return; var impactEffect = DefaultHitEffect; if (TriggerPunchCooldown(initiatorPlayer, ConfigInstance.SliceCooldown)) return; if (initiatorPlayer.Distance(hitpoint) > ConfigInstance.TerrainDamage.Distance) return; ServerMgr.Instance.Invoke(() => { Effect.server.Run(impactEffect, hitpoint, Vector3.zero); }, 0.2f); return; } } var water = hitObject?.transform?.root?.GetComponent(); if (water != null) { if (TriggerPunchCooldown(initiatorPlayer)) return; if (initiatorPlayer.Distance(hitpoint) > ConfigInstance.WaterDamage.Distance) return; Effect.server.Run("assets/bundled/prefabs/fx/player/swing_weapon.prefab", initiatorPlayer.transform.position, Vector3.zero); ServerMgr.Instance.Invoke(() => { initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, null); Effect.server.Run(DefaultWaterSplashEffect, hitpoint, Vector3.zero); if (!ConfigInstance.OnePunchManMode) { var isSaltWater = water.Type == WaterBodyType.Ocean; ConfigInstance.WaterDamage.MakeHeal(initiatorPlayer, isSaltWater); if (ConfigInstance.WaterDamage.GiveTrout(initiatorPlayer, isSaltWater)) { if (!TriggerGutshotScreamCooldown(initiatorPlayer)) { ServerMgr.Instance.Invoke(() => { Effect.server.Run("assets/bundled/prefabs/fx/player/gutshot_scream.prefab", initiatorPlayer.transform.position, Vector3.zero); }, 0.2f); } } } }, 0.2f); } } public void HammerHit(BasePlayer initiatorPlayer, GameObject hitObject, Vector3 hitpoint) { var entity = hitObject?.transform?.GetComponentInParent(); if (hitObject != null) { var targetPlayer = entity as BasePlayer; var damageableEntity = targetPlayer != null ? null : entity as BaseCombatEntity; var damageRule = entity?.GetComponentInParent() != null ? ConfigInstance.NpcDamage : ConfigInstance.PlayerDamage; var resourceEntity = damageableEntity != null ? null : entity as ResourceEntity; var terrainObject = resourceEntity == null ? hitObject?.transform.root : null; if (terrainObject == null) terrainObject = hitObject?.transform; if (targetPlayer != null && targetPlayer != initiatorPlayer) { var impactEffect = targetPlayer.impactEffect.isValid ? targetPlayer.impactEffect.resourcePath : null; if (string.IsNullOrEmpty(impactEffect)) impactEffect = "assets/bundled/prefabs/fx/player/fall-damage.prefab"; if (TriggerPunchCooldown(initiatorPlayer, ConfigInstance.HammerCooldown)) return; if (!(initiatorPlayer.Distance(targetPlayer) <= damageRule.Distance)) return; ServerMgr.Instance.Invoke(() => { Effect.server.Run(impactEffect, hitpoint, Vector3.zero); if (!ConfigInstance.OnePunchManMode) { damageRule.MakeBleed(initiatorPlayer); damageRule.MakeTargetBleed(targetPlayer, initiatorPlayer); } if (ConfigInstance.AnimatePlayerTargets) targetPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, "whoa"); }, 0.2f); return; } else if (damageableEntity != null) { var impactEffect = damageableEntity.impactEffect.isValid ? damageableEntity.impactEffect.resourcePath : null; damageRule = null; if (damageableEntity is LootContainer) damageRule = ConfigInstance.LootContainerDamage; else if (damageableEntity is BradleyAPC) damageRule = ConfigInstance.BradleyDamage; else if (damageableEntity is BaseAnimalNPC) { damageRule = ConfigInstance.AnimalDamage; impactEffect = DefaultBleedEffect; } else if (damageableEntity is RidableHorse) { damageRule = ConfigInstance.HorseDamage; impactEffect = DefaultBleedEffect; } else if (damageableEntity is BuildingBlock) damageRule = ConfigInstance.BuildingBlocksDamage; if (damageRule == null) return; if (string.IsNullOrEmpty(impactEffect)) impactEffect = DefaultHitEffect; if (TriggerPunchCooldown(initiatorPlayer, ConfigInstance.HammerCooldown)) return; if (initiatorPlayer.Distance(hitpoint) > damageRule.Distance) return; ServerMgr.Instance.Invoke(() => { if (!(damageableEntity is LootContainer)) Effect.server.Run(impactEffect, hitpoint, Vector3.zero); damageRule.MakeTargetHurt(damageableEntity, initiatorPlayer, ConfigInstance.OnePunchManMode, damageableEntity is BradleyAPC ? Rust.DamageType.Explosion : Rust.DamageType.Slash); }, 0.2f); return; } else if (resourceEntity != null) { var impactEffect = resourceEntity.impactEffect.isValid ? resourceEntity.impactEffect.resourcePath : null; if (string.IsNullOrEmpty(impactEffect)) impactEffect = DefaultHitEffect; if (TriggerPunchCooldown(initiatorPlayer, ConfigInstance.HammerCooldown)) return; if (initiatorPlayer.Distance(hitpoint) > ConfigInstance.ResourceDamage.Distance) return; ServerMgr.Instance.Invoke(() => { Effect.server.Run(impactEffect, hitpoint, Vector3.zero); }, 0.2f); return; } else if (terrainObject != null && initiatorPlayer.serverInput.IsDown(BUTTON.DUCK)) { if (terrainObject.GetComponent() != null) return; var impactEffect = DefaultHitEffect; if (TriggerPunchCooldown(initiatorPlayer, ConfigInstance.HammerCooldown)) return; if (initiatorPlayer.Distance(hitpoint) > ConfigInstance.TerrainDamage.Distance) return; ServerMgr.Instance.Invoke(() => { Effect.server.Run(impactEffect, hitpoint, Vector3.zero); }, 0.2f); return; } } var water = hitObject?.transform?.root?.GetComponent(); if (water != null) { if (TriggerPunchCooldown(initiatorPlayer, ConfigInstance.HammerCooldown)) return; if (initiatorPlayer.Distance(hitpoint) > ConfigInstance.WaterDamage.Distance) return; Effect.server.Run("assets/bundled/prefabs/fx/player/swing_weapon.prefab", initiatorPlayer.transform.position, Vector3.zero); if (ConfigInstance.AnimateAttacker) initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, "hurry"); ServerMgr.Instance.Invoke(() => { initiatorPlayer.SignalBroadcast(BaseEntity.Signal.Gesture, null); Effect.server.Run(DefaultWaterSplashEffect, hitpoint, Vector3.zero); if (!ConfigInstance.OnePunchManMode) { var isSaltWater = water.Type == WaterBodyType.Ocean; ConfigInstance.WaterDamage.MakeHeal(initiatorPlayer, isSaltWater); if (ConfigInstance.WaterDamage.GiveTrout(initiatorPlayer, isSaltWater)) { if (!TriggerGutshotScreamCooldown(initiatorPlayer)) { ServerMgr.Instance.Invoke(() => { Effect.server.Run("assets/bundled/prefabs/fx/player/gutshot_scream.prefab", initiatorPlayer.transform.position, Vector3.zero); }, 0.2f); } } } }, 0.2f); } } public static WaterBody GetWaterBody(RaycastHit hit) { if (hit.collider == null) return WaterSystem.Ocean; Transform transform = hit.collider.transform; WaterBody component; return transform.TryGetComponent(out component) ? component : transform.parent != null ? transform.parent.GetComponentInChildren() : null; } #endregion #region Permission public const string UsePerm = "facepunch.use"; public const string UseSlicePerm = "facepunch.useslice"; public const string UseHammerPerm = "facepunch.usehammer"; private void InstallPermissions() { permission.RegisterPermission(UsePerm, this); permission.RegisterPermission(UseSlicePerm, this); permission.RegisterPermission(UseHammerPerm, this); } private bool HasPermission(BasePlayer player, string perm) { if (!permission.UserHasPermission(player.UserIDString, perm)) { return false; } return true; } #endregion #region Input private RaycastHit[] buffer = new RaycastHit[5]; private static bool TryGetPlayerView(BasePlayer player, out Quaternion viewAngle) { viewAngle = new Quaternion(0f, 0f, 0f, 0f); var input = player.serverInput; if (input == null) return false; if (input.current == null) return false; viewAngle = Quaternion.Euler(input.current.aimAngles); return true; } #endregion #region Extensions private static float Scale(float oldValue, float oldMin, float oldMax, float newMin, float newMax) { var oldRange = (oldMax - oldMin); var newRange = (newMax - newMin); var newValue = (((oldValue - oldMin) * newRange) / oldRange) + newMin; return newValue; } #endregion #region Config public new RootConfig ConfigInstance { get; set; } = new RootConfig(); public class RootConfig { public bool OnePunchManMode { get; set; } = false; public bool EnableSlice { get; set; } = true; public bool EnableHammer { get; set; } = true; public float PunchCooldown { get; set; } = 0.75f; public float SliceCooldown { get; set; } = 0.86f; public float HammerCooldown { get; set; } = 0.4725f; public float GruntCooldown { get; set; } = 3f; public bool HeldClickPunching { get; set; } = true; public bool AnimateAttacker { get; set; } = true; public bool AnimatePlayerTargets { get; set; } = true; public DamageRule PlayerDamage { get; set; } = new DamageRule(0.4f, 1.5f, 5.0f); public DamageRule NpcDamage { get; set; } = new DamageRule(0.4f, 7.5f, 10.0f); public DamageRule AnimalDamage { get; set; } = new DamageRule(0.55f, 2.5f); public DamageRule HorseDamage { get; set; } = new DamageRule(0.55f, 2.5f, fistBleedAmount: 0.25f); public DamageRule LootContainerDamage { get; set; } = new DamageRule(0.25f, 2.5f, fistBleedAmount: 0.5f); public DamageRule BradleyDamage { get; set; } = new DamageRule(0.55f, 2.5f); public DamageRule BuildingBlocksDamage { get; set; } = new DamageRule(0.25f, 1.5f, 2.5f, fistBleedAmount: 0.6f); public DamageRule TerrainDamage { get; set; } = new DamageRule(0.15f, 0f, fistBleedAmount: 0.75f); public DamageRule ResourceDamage { get; set; } = new DamageRule(0.25f, 0f, fistBleedAmount: 0.75f); public DamageRule StaticLootableDamage { get; set; } = new DamageRule(1, 2.5f, 4.5f, fistBleedAmount: 0.4f); public WaterRule WaterDamage { get; set; } = new WaterRule(0.35f); public class DamageRule { public float Distance { get; set; } public float DamageMin { get; set; } public float DamageMax { get; set; } public float FistBleedAmount { get; set; } public DamageRule() { } public DamageRule(float distance, float damageMin, float damageMax = 0f, float fistBleedAmount = 0f) { Distance = distance; DamageMin = damageMin; DamageMax = damageMax; FistBleedAmount = fistBleedAmount; } public float GetTargetDamage() { if (DamageMax <= 0) return DamageMin; return Random.Next((int)(DamageMin * 100f), (int)(DamageMax * 100)) / 100; } public void MakeTargetHurt(BaseCombatEntity target, BasePlayer initiator, bool onePunchManMode, Rust.DamageType damageType = Rust.DamageType.Bleeding) { var amount = GetTargetDamage(); target.Hurt(amount * (onePunchManMode ? 10000000 : 1), damageType, initiator); } public void MakeTargetBleed(BasePlayer target, BasePlayer initiator) { var amount = GetTargetDamage() * 2; target.metabolism.bleeding.Add(HasHandsCovered(initiator) ? amount / 3 : amount); } public bool MakeBleed(BasePlayer initiator) { if (FistBleedAmount <= 0f) return false; if (!HasHandsCovered(initiator)) { initiator.metabolism.bleeding.Add(FistBleedAmount); return true; } return false; } public bool HasHandsCovered(BasePlayer player) { return player.inventory.containerWear.itemList.Any(x => x.info.shortname == "burlap.gloves.new" || x.info.shortname == "burlap.gloves" || x.info.shortname == "roadsign.gloves" || x.info.shortname == "tactical.gloves" || x.info.shortname == "scientistsuit_heavy" || x.info.shortname == "hazmatsuit_scientist" || x.info.shortname == "hazmatsuit_scientist_peacekeeper" || x.info.shortname == "barrelcostume" || x.info.shortname == "cratecostume" || x.info.shortname == "ghostsheet" || x.info.shortname == "hazmatsuit"); } } public class WaterRule { public float Distance { get; set; } public WaterType Salt { get; set; } = new WaterType(healingAmount: 0.25f, fistBleedHealingAmount: 0.5f, smallTroutGatherChanceAmount: 20, smallTroutGatherChanceTimes: 1); public WaterType Pure { get; set; } = new WaterType(); public WaterRule() { } public WaterRule(float distance) { Distance = distance; } public bool MakeHeal(BasePlayer initiator, bool isSaltWater) { var fistBleedHealingAmount = isSaltWater ? Salt.FistBleedHealingAmount : Pure.FistBleedHealingAmount; var healingAmount = isSaltWater ? Salt.HealingAmount : Pure.HealingAmount; if (fistBleedHealingAmount <= 0f || initiator.metabolism.bleeding.value <= 0) return false; if (!HasHandsCovered(initiator)) { initiator.Heal(healingAmount); initiator.metabolism.bleeding.Subtract(fistBleedHealingAmount); return true; } return false; } public bool GiveTrout(BasePlayer initiator, bool isSaltWater) { var water = isSaltWater ? Salt : Pure; if (Random.Next(0, water.SmallTroutGatherChanceAmount) > water.SmallTroutGatherChanceTimes - 1) return false; var troutItem = ItemManager.CreateByName("fish.troutsmall", 1); initiator.GiveItem(troutItem, BaseEntity.GiveItemReason.ResourceHarvested); return true; } public bool HasHandsCovered(BasePlayer player) { return player.inventory.containerWear.itemList.Any(x => x.info.shortname == "burlap.gloves.new" || x.info.shortname == "burlap.gloves" || x.info.shortname == "roadsign.gloves" || x.info.shortname == "tactical.gloves" || x.info.shortname == "scientistsuit_heavy" || x.info.shortname == "hazmatsuit_scientist" || x.info.shortname == "hazmatsuit_scientist_peacekeeper" || x.info.shortname == "barrelcostume" || x.info.shortname == "cratecostume" || x.info.shortname == "ghostsheet" || x.info.shortname == "hazmatsuit"); } public class WaterType { [JsonProperty("HealingAmount (Heals player only if bleeding)")] public float HealingAmount { get; set; } public float FistBleedHealingAmount { get; set; } public int SmallTroutGatherChanceAmount { get; set; } = 10; public int SmallTroutGatherChanceTimes { get; set; } = -1; public WaterType() { } public WaterType( float healingAmount, float fistBleedHealingAmount, int smallTroutGatherChanceAmount, int smallTroutGatherChanceTimes) { HealingAmount = healingAmount; FistBleedHealingAmount = fistBleedHealingAmount; SmallTroutGatherChanceAmount = smallTroutGatherChanceAmount; SmallTroutGatherChanceTimes = smallTroutGatherChanceTimes; } } } } #endregion } }