diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 52a67da..4878d44 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,7 +3,7 @@ name: Bug report about: Create a report to help us improve title: '' labels: needs testing -assignees: joker-119 +assignees: Mikihero --- @@ -20,17 +20,17 @@ Steps to reproduce the behavior: **Expected behavior** A clear and concise description of what you expected to happen. -**Server logs** -Please include a pastebin of your localadmin log file (or both MA_log and SCP_log files if you use MultiAdmin) from the time in which the bug occured - -**Copy of current CU config section** +**Actual behavior** +A clear and concise description of what actually happens. +**Copy of the relevant CU config section** +If you're experiencing an issue with the Class Max Health module, then post that entire part. Skip this if config is not relevant. **EXILED Version ("latest" is not a version):** +For example 8.9.2 - -**Results of `show plugins` command in console:** +**Screenshot of the results of the `plym show` command in server console:** **Additional context** -Add any other context about the problem here. +Add any other relevant context about the problem here. \ No newline at end of file diff --git a/Common Utilities/API.cs b/Common Utilities/API.cs index 7ff51f0..4d8a0b9 100644 --- a/Common Utilities/API.cs +++ b/Common Utilities/API.cs @@ -1,16 +1,13 @@ -namespace Common_Utilities -{ - using System.Collections.Generic; - - using Exiled.API.Features; - using PlayerRoles; - - public static class API - { - public static List GetStartItems(RoleTypeId role) => Main.Instance.PlayerHandlers.StartItems(role); - - public static List GetStartItems(RoleTypeId role, Player player) => Main.Instance.PlayerHandlers.StartItems(role, player); - - public static float GetHealthOnKill(RoleTypeId role) => Main.Instance.Config.HealthOnKill?.ContainsKey(role) ?? false ? Main.Instance.Config.HealthOnKill[role] : 0f; - } +namespace Common_Utilities.API; + +using System.Collections.Generic; +using Exiled.API.Features; +using PlayerRoles; + +public static class API +{ + // API methods for potential use by other plugins, not sure if anyone actually uses this + public static float GetHealthOnKill(RoleTypeId role) => Plugin.Instance.Config.HealthOnKill?.ContainsKey(role) ?? false ? Plugin.Instance.Config.HealthOnKill[role] : 0f; + + public static List GetStartItems(RoleTypeId role, Player player = null) => Plugin.Instance.playerHandlers.GetStartingInventory(role, player); } \ No newline at end of file diff --git a/Common Utilities/Common Utilities.csproj b/Common Utilities/Common Utilities.csproj index 209b54c..8a733f9 100644 --- a/Common Utilities/Common Utilities.csproj +++ b/Common Utilities/Common Utilities.csproj @@ -23,9 +23,8 @@ - + - diff --git a/Common Utilities/Config.cs b/Common Utilities/Config.cs index eb379fc..957fd8f 100644 --- a/Common Utilities/Config.cs +++ b/Common Utilities/Config.cs @@ -14,32 +14,41 @@ namespace Common_Utilities public class Config : IConfig { + [Description("If the plugin is enabled or not.")] + public bool IsEnabled { get; set; } = true; + [Description("Whether or not debug messages should be shown.")] public bool Debug { get; set; } = false; - [Description("Whether or not MTF/CI can 'escape' while disarmed to switch teams.")] - public bool DisarmSwitchTeams { get; set; } = true; - - [Description("Whether or not disarmed people will be prevented from interacting with doors/elevators.")] - public bool RestrictiveDisarming { get; set; } = true; + [Description("Roles that when cuffed in the escape area will change into the target one.")] + public Dictionary DisarmedEscapeSwitchRole { get; set; } = + new() + { + { + RoleTypeId.NtfCaptain, RoleTypeId.ChaosMarauder + }, + { + RoleTypeId.ChaosMarauder, RoleTypeId.NtfCaptain + }, + }; [Description("The text displayed at the timed interval specified below.")] - public string TimedBroadcast { get; set; } = "This server is running EXILED Common-Utilities, enjoy your stay!"; + public string TimedBroadcast { get; set; } = "This server is running EXILED Common-Utilities, enjoy your stay!"; [Description("The time each timed broadcast will be displayed.")] public ushort TimedBroadcastDuration { get; set; } = 5; [Description("The delay between each timed broadcast. To disable timed broadcasts, set this to 0")] - public float TimedBroadcastDelay { get; set; } = 300f; + public float TimedBroadcastDelay { get; set; } = 0; [Description("The message displayed to the player when they first join the server. Setting this to empty will disable these broadcasts.")] - public string JoinMessage { get; set; } = "Welcome %player%! Please read our rules!"; + public string JoinMessage { get; set; } = string.Empty; [Description("The amount of time (in seconds) the join message is displayed.")] public ushort JoinMessageDuration { get; set; } = 5; - [Description("The amount of time (in seconds) after the round starts, before the facilities auto-nuke will start.")] - public float AutonukeTime { get; set; } = 1500f; + [Description("The amount of time (in seconds) after the round starts, before the facilities auto-nuke will start. Set to -1 to disable.")] + public float AutonukeTime { get; set; } = -1; [Description("Wether or not the nuke should be unable to be disabled during the auto-nuke countdown.")] public bool AutonukeLock { get; set; } = true; @@ -54,7 +63,7 @@ public class Config : IConfig }; [Description("Whether or not to show player's health under their name when you look at them.")] - public bool PlayerHealthInfo { get; set; } = true; + public bool PlayerHealthInfo { get; set; } = false; [Description("Whether or not friendly fire should automatically turn on when a round ends (it will turn itself back off before the next round starts).")] public bool FriendlyFireOnRoundEnd { get; set; } = false; @@ -62,11 +71,14 @@ public class Config : IConfig [Description("The multiplier applied to radio battery usage. Set to 0 to disable radio battery drain.")] public float RadioBatteryDrainMultiplier { get; set; } = 1f; - [Description("The color to use for lights while the warhead is active.")] - public Color WarheadColor { get; set; } = new(1f, 0.2f, 0.2f); + [Description("Whether to change the color of lights while warhead is active.")] + public bool ChangeWarheadColor { get; set; } = false; + + [Description("The color to use for lights while the warhead is active. In the RGBA format using values between 0 and 1. Ignored if ChangeWarheadColor is set to false.")] + public Color WarheadColor { get; set; } = new(1f, 0.2f, 0.2f, 1); [Description("The maximum time, in seconds, that a player can be AFK before being kicked. Set to -1 to disable AFK system.")] - public int AfkLimit { get; set; } = 120; + public int AfkLimit { get; set; } = -1; [Description("The roles that are ignored by the AFK system.")] public List AfkIgnoredRoles { get; set; } = new() @@ -74,6 +86,8 @@ public class Config : IConfig RoleTypeId.Scp079, RoleTypeId.Spectator, RoleTypeId.Tutorial, + RoleTypeId.Filmmaker, + RoleTypeId.Overwatch, }; [Description("Whether or not probabilities should be additive (50 + 50 = 100) or not (50 + 50 = 2 seperate 50% chances)")] @@ -114,7 +128,7 @@ public class Config : IConfig { new() { - Type = ItemType.Ammo556x45, + AmmoType = ItemType.Ammo556x45, Amount = 200, Group = "none", }, @@ -123,19 +137,17 @@ public class Config : IConfig }, }; - [Description("The list of custom 914 recipies. Original is the item being upgraded, New is the item to upgrade to, and Chance is the percent chance of the upgrade happening. You can specify multiple upgrade choices for the same item.")] - public Dictionary> Scp914ItemChanges { get; set; } = new() + [Description("The list of custom 914 recipies. OriginalItem is the item being upgraded, NewItem is the item to upgrade to, and Chance is the percent chance of the upgrade happening. You can specify multiple upgrade choices for the same item.")] + public Dictionary> Scp914ItemChances { get; set; } = new() { { Scp914KnobSetting.Rough, new List { + new() { - new() - { - Original = ItemType.KeycardO5, - New = ItemType.MicroHID, - Chance = 50, - } + Original = ItemType.KeycardO5.ToString(), + New = ItemType.MicroHID.ToString(), + Chance = 50, }, } }, @@ -150,7 +162,7 @@ public class Config : IConfig { new() { - Original = RoleTypeId.ClassD, + Original = RoleTypeId.ClassD.ToString(), New = RoleTypeId.Spectator.ToString(), Chance = 100, } @@ -159,7 +171,7 @@ public class Config : IConfig }, }; - [Description("The list of 914 teleport settings. Note that if you set \"zone\" to anything other than Unspecified, it will always select a random room from that zone that isn't in the ignoredRooms list, instead of the room type defined.")] + [Description("The list of 914 teleport settings. Note that if you set \"zone\" to anything other than Unspecified, it will always select a random room from that zone that isn't in the ignoredRooms list, instead of the provided room type.")] public Dictionary> Scp914TeleportChances { get; set; } = new() { { @@ -168,7 +180,7 @@ public class Config : IConfig new() { Room = RoomType.LczClassDSpawn, - Chance = 100, + Chance = 50, }, new() { @@ -177,6 +189,7 @@ public class Config : IConfig { RoomType.Lcz173, }, + Chance = 100, }, } }, @@ -237,9 +250,6 @@ public class Config : IConfig { RoleTypeId.Scp173, 0 }, - { - RoleTypeId.Scp939, 10 - }, }; [Description("A list of roles and what their default starting health should be.")] @@ -248,12 +258,6 @@ public class Config : IConfig { RoleTypeId.Scp173, 3200 }, - { - RoleTypeId.NtfCaptain, 150 - }, }; - - [Description("If the plugin is enabled or not.")] - public bool IsEnabled { get; set; } = true; } } diff --git a/Common Utilities/ConfigObjects/ItemChance.cs b/Common Utilities/ConfigObjects/ItemChance.cs index 0aaf0c4..5316097 100644 --- a/Common Utilities/ConfigObjects/ItemChance.cs +++ b/Common Utilities/ConfigObjects/ItemChance.cs @@ -1,6 +1,6 @@ namespace Common_Utilities.ConfigObjects; -public class ItemChance +public class ItemChance : IChanceObject { public string ItemName { get; set; } = ItemType.None.ToString(); @@ -8,10 +8,9 @@ public class ItemChance public string Group { get; set; } = "none"; - public void Deconstruct(out string name, out double i, out string groupKey) + public void Deconstruct(out string name, out double i) { name = ItemName; i = Chance; - groupKey = Group; } } \ No newline at end of file diff --git a/Common Utilities/ConfigObjects/ItemUpgradeChance.cs b/Common Utilities/ConfigObjects/ItemUpgradeChance.cs index afa921f..d0c5d98 100644 --- a/Common Utilities/ConfigObjects/ItemUpgradeChance.cs +++ b/Common Utilities/ConfigObjects/ItemUpgradeChance.cs @@ -1,20 +1,20 @@ namespace Common_Utilities.ConfigObjects { - public class ItemUpgradeChance + public class ItemUpgradeChance : IChanceObject { - public ItemType Original { get; set; } + public string Original { get; set; } - public ItemType New { get; set; } + public string New { get; set; } public double Chance { get; set; } public int Count { get; set; } = 1; - public void Deconstruct(out ItemType itemType, out ItemType itemType1, out double i, out int count) + public void Deconstruct(out string originalItem, out string destinationItem, out double chance, out int count) { - itemType = Original; - itemType1 = New; - i = Chance; + originalItem = Original; + destinationItem = New; + chance = Chance; count = Count; } } diff --git a/Common Utilities/ConfigObjects/PlayerUpgradeChance.cs b/Common Utilities/ConfigObjects/PlayerUpgradeChance.cs index 72fb0e4..2d8ddeb 100644 --- a/Common Utilities/ConfigObjects/PlayerUpgradeChance.cs +++ b/Common Utilities/ConfigObjects/PlayerUpgradeChance.cs @@ -2,9 +2,9 @@ namespace Common_Utilities.ConfigObjects { using PlayerRoles; - public class PlayerUpgradeChance + public class PlayerUpgradeChance : IChanceObject { - public RoleTypeId Original { get; set; } + public string Original { get; set; } public string New { get; set; } = RoleTypeId.Spectator.ToString(); @@ -14,11 +14,11 @@ public class PlayerUpgradeChance public bool KeepHealth { get; set; } = true; - public void Deconstruct(out RoleTypeId old, out string newRole, out double i, out bool keepInventory, out bool keepHealth) + public void Deconstruct(out string oldRole, out string newRole, out double chance, out bool keepInventory, out bool keepHealth) { - old = Original; + oldRole = Original; newRole = New; - i = Chance; + chance = Chance; keepInventory = KeepInventory; keepHealth = KeepHealth; } diff --git a/Common Utilities/ConfigObjects/Scp914EffectChance.cs b/Common Utilities/ConfigObjects/Scp914EffectChance.cs index f0a0918..2868b95 100644 --- a/Common Utilities/ConfigObjects/Scp914EffectChance.cs +++ b/Common Utilities/ConfigObjects/Scp914EffectChance.cs @@ -2,7 +2,7 @@ namespace Common_Utilities.ConfigObjects { using Exiled.API.Enums; - public class Scp914EffectChance + public class Scp914EffectChance : IChanceObject { public EffectType Effect { get; set; } diff --git a/Common Utilities/ConfigObjects/Scp914TeleportChance.cs b/Common Utilities/ConfigObjects/Scp914TeleportChance.cs index ba8a1e4..dc22dc1 100644 --- a/Common Utilities/ConfigObjects/Scp914TeleportChance.cs +++ b/Common Utilities/ConfigObjects/Scp914TeleportChance.cs @@ -1,32 +1,30 @@ +namespace Common_Utilities.ConfigObjects; + using System.Collections.Generic; +using Exiled.API.Enums; +using UnityEngine; -namespace Common_Utilities.ConfigObjects +public class Scp914TeleportChance : IChanceObject { - using Exiled.API.Enums; - using UnityEngine; + public ZoneType Zone { get; set; } = ZoneType.Unspecified; - public class Scp914TeleportChance - { - public ZoneType Zone { get; set; } = ZoneType.Unspecified; - - public List IgnoredRooms { get; set; } + public List IgnoredRooms { get; set; } = new(); - public RoomType Room { get; set; } + public RoomType Room { get; set; } = RoomType.Unknown; - public Vector3 Offset { get; set; } = Vector3.zero; + public Vector3 Offset { get; set; } = Vector3.zero; - public double Chance { get; set; } + public double Chance { get; set; } - public float Damage { get; set; } = 0f; + public float Damage { get; set; } = 0f; - public void Deconstruct(out RoomType room, out List ignoredRooms, out Vector3 offset, out double chance, out float damage, out ZoneType zone) - { - room = Room; - ignoredRooms = IgnoredRooms; - offset = Offset; - chance = Chance; - damage = Damage; - zone = Zone; - } + public void Deconstruct(out RoomType room, out List ignoredRooms, out Vector3 offset, out double chance, out float damage, out ZoneType zone) + { + room = Room; + ignoredRooms = IgnoredRooms; + offset = Offset; + chance = Chance; + damage = Damage; + zone = Zone; } } \ No newline at end of file diff --git a/Common Utilities/ConfigObjects/StartingAmmo.cs b/Common Utilities/ConfigObjects/StartingAmmo.cs index 40dd0a8..7ce52c3 100644 --- a/Common Utilities/ConfigObjects/StartingAmmo.cs +++ b/Common Utilities/ConfigObjects/StartingAmmo.cs @@ -2,16 +2,16 @@ namespace Common_Utilities.ConfigObjects { public class StartingAmmo { - public ItemType Type { get; set; } + public ItemType AmmoType { get; set; } public ushort Amount { get; set; } public string Group { get; set; } = "none"; - public void Deconstruct(out ItemType type, out ushort limit, out string group) + public void Deconstruct(out ItemType ammoType, out ushort amount, out string group) { - type = Type; - limit = Amount; + ammoType = AmmoType; + amount = Amount; group = Group; } } diff --git a/Common Utilities/EventHandlers/MapHandlers.cs b/Common Utilities/EventHandlers/MapHandlers.cs index 36ca19f..f93c13b 100644 --- a/Common Utilities/EventHandlers/MapHandlers.cs +++ b/Common Utilities/EventHandlers/MapHandlers.cs @@ -1,238 +1,284 @@ +namespace Common_Utilities.EventHandlers; + +using Exiled.CustomItems.API.Features; using Exiled.API.Extensions; -using Exiled.API.Features.Items; +using System; +using System.Collections.Generic; +using System.Linq; +using ConfigObjects; +using Exiled.API.Enums; +using Exiled.API.Features; +using Exiled.API.Features.Pickups; +using Exiled.CustomRoles.API.Features; +using Exiled.Events.EventArgs.Scp914; +using MEC; +using PlayerRoles; +using UnityEngine; -namespace Common_Utilities.EventHandlers +public class MapHandlers { - using System; - using System.Collections.Generic; - using System.Linq; - - using Common_Utilities.ConfigObjects; - using Exiled.API.Enums; - using Exiled.API.Features; - using Exiled.API.Features.Pickups; - using Exiled.CustomRoles.API.Features; - using Exiled.Events.EventArgs.Scp914; - using MEC; - using PlayerRoles; - using UnityEngine; - - public class MapHandlers - { - private readonly Main plugin; - - public MapHandlers(Main plugin) => this.plugin = plugin; + private Config config => Plugin.Instance.Config; - public void OnScp914UpgradingItem(UpgradingPickupEventArgs ev) + public void OnUpgradingPickup(UpgradingPickupEventArgs ev) + { + if (config.Scp914ItemChances.TryGetValue(ev.KnobSetting, out List outItemUpgradeChances)) { - if (plugin.Config.Scp914ItemChanges.ContainsKey(ev.KnobSetting)) + List itemUpgradeChances = outItemUpgradeChances + .Where(x => + x.Original == ev.Pickup.Type.ToString() + || (CustomItem.TryGet(ev.Pickup, out CustomItem item) && item!.Name == x.Original)) + .ToList(); + + double rolledChance = Utils.RollChance(itemUpgradeChances); + + foreach ((string sourceItem, string destinationItem, double chance, int count) in itemUpgradeChances) { - IEnumerable itemUpgradeChance = plugin.Config.Scp914ItemChanges[ev.KnobSetting].Where(x => x.Original == ev.Pickup.Type); + Log.Debug($"{nameof(OnUpgradingPickup)}: SCP-914 is trying to upgrade a {ev.Pickup.Type}. {sourceItem} -> {destinationItem} ({chance}). Should process: {rolledChance <= chance} ({rolledChance})"); - foreach ((ItemType sourceItem, ItemType destinationItem, double chance, int count) in itemUpgradeChance) + if (rolledChance <= chance) { - double r; - if (plugin.Config.AdditiveProbabilities) - r = plugin.Rng.NextDouble() * itemUpgradeChance.Sum(x => x.Chance); - else - r = plugin.Rng.NextDouble() * 100; - - Log.Debug($"{nameof(OnScp914UpgradingItem)}: SCP-914 is trying to upgrade a {ev.Pickup.Type}. {sourceItem} -> {destinationItem} ({chance}). Should process: {r <= chance} ({r})"); - if (r <= chance) + if (Enum.TryParse(destinationItem, out ItemType itemType)) { - UpgradeItem(ev.Pickup, destinationItem, ev.OutputPosition, count); - ev.IsAllowed = false; - break; + if (itemType is not ItemType.None) + { + UpgradePickup(ev.Pickup, ev.OutputPosition, count, false, itemType: itemType); + } } - - r -= chance; + else if (CustomItem.TryGet(destinationItem, out CustomItem customItem)) + { + if (customItem is not null) + { + UpgradePickup(ev.Pickup, ev.OutputPosition, count, true, customItem: customItem); + } + } + + ev.Pickup.Destroy(); + ev.IsAllowed = false; + break; } + + if (config.AdditiveProbabilities) + rolledChance -= chance; } } + } - public void OnScp914UpgradingInventoryItem(UpgradingInventoryItemEventArgs ev) + public void OnUpgradingInventoryItem(UpgradingInventoryItemEventArgs ev) + { + if (config.Scp914ItemChances.TryGetValue(ev.KnobSetting, out List outItemUpgradeChances)) { - if (plugin.Config.Scp914ItemChanges.ContainsKey(ev.KnobSetting)) - { - IEnumerable itemUpgradeChance = plugin.Config.Scp914ItemChanges[ev.KnobSetting].Where(x => x.Original == ev.Item.Type); + List itemUpgradeChances = outItemUpgradeChances + .Where(x => + x.Original == ev.Item.Type.ToString() + || (CustomItem.TryGet(ev.Item, out CustomItem item) && item!.Name == x.Original)) + .ToList(); + + double rolledChance = Utils.RollChance(itemUpgradeChances); - foreach ((ItemType sourceItem, ItemType destinationItem, double chance, int count) in itemUpgradeChance) + foreach ((string sourceItem, string destinationItem, double chance, int count) in itemUpgradeChances) + { + Log.Debug($"{nameof(OnUpgradingInventoryItem)}: {ev.Player.Nickname} is attempting to upgrade hit {ev.Item.Type}. {sourceItem} -> {destinationItem} ({chance}). Should process: {rolledChance <= chance} ({rolledChance})"); + + if (rolledChance <= chance) { - double r; - if (plugin.Config.AdditiveProbabilities) - r = plugin.Rng.NextDouble() * itemUpgradeChance.Sum(x => x.Chance); - else - r = plugin.Rng.NextDouble() * 100; - - Log.Debug($"{nameof(OnScp914UpgradingInventoryItem)}: {ev.Player.Nickname} is attempting to upgrade hit {ev.Item.Type}. {sourceItem} -> {destinationItem} ({chance}). Should process: {r <= chance} ({r})"); - if (r <= chance) + if (Enum.TryParse(destinationItem, out ItemType itemType)) { - ev.Player.RemoveItem(ev.Item); - if (destinationItem is not ItemType.None) + if (itemType is not ItemType.None) { - for (int i = 0; i < count; i++) - { - if (!ev.Player.IsInventoryFull) - ev.Player.AddItem(destinationItem); - else - Pickup.CreateAndSpawn(destinationItem, Scp914.OutputPosition, ev.Player.Rotation, ev.Player); - } + UpgradeInventoryItem(ev, count, false, itemType: itemType); + } + } + else if (CustomItem.TryGet(destinationItem, out CustomItem customItem)) + { + if (customItem is not null) + { + UpgradeInventoryItem(ev, count, true, customItem: customItem); } - - break; } - r -= chance; + break; } + + if (config.AdditiveProbabilities) + rolledChance -= chance; } } + } - public void OnScp914UpgradingPlayer(UpgradingPlayerEventArgs ev) + public void OnUpgradingPlayer(UpgradingPlayerEventArgs ev) + { + if (config.Scp914ClassChanges != null && config.Scp914ClassChanges.TryGetValue(ev.KnobSetting, out var outPlayerUpgradeChances)) { - if (plugin.Config.Scp914ClassChanges != null && plugin.Config.Scp914ClassChanges.TryGetValue(ev.KnobSetting, out var change)) - { - IEnumerable playerUpgradeChance = change.Where(x => x.Original == ev.Player.Role); + List playerUpgradeChances = outPlayerUpgradeChances + .Where(x => + x.Original == ev.Player.Role.ToString() + || (CustomRole.TryGet(ev.Player, out IReadOnlyCollection customRoles) && customRoles.Select(r => r.Name).Contains(x.Original))) + .ToList(); + + double rolledChance = Utils.RollChance(playerUpgradeChances); - foreach ((RoleTypeId sourceRole, string destinationRole, double chance, bool keepInventory, bool keepHealth) in playerUpgradeChance) + foreach ((string sourceRole, string destinationRole, double chance, bool keepInventory, bool keepHealth) in playerUpgradeChances) + { + Log.Debug($"{nameof(OnUpgradingPlayer)}: {ev.Player.Nickname} ({ev.Player.Role}) is trying to upgrade his class. {sourceRole} -> {destinationRole} ({chance}). Should be processed: {rolledChance <= chance} ({rolledChance})"); + if (rolledChance <= chance) { - double r; - if (plugin.Config.AdditiveProbabilities) - r = plugin.Rng.NextDouble() * playerUpgradeChance.Sum(x => x.Chance); - else - r = plugin.Rng.NextDouble() * 100; - - Log.Debug($"{nameof(OnScp914UpgradingPlayer)}: {ev.Player.Nickname} ({ev.Player.Role})is trying to upgrade his class. {sourceRole} -> {destinationRole} ({chance}). Should be processed: {r <= chance} ({r})"); - if (r <= chance) - { - float originalHealth = ev.Player.Health; - var originalItems = ev.Player.Items; - var originalAmmo = ev.Player.Ammo; + float originalHealth = ev.Player.Health; + var originalItems = ev.Player.Items; + var originalAmmo = ev.Player.Ammo; - if (Enum.TryParse(destinationRole, true, out RoleTypeId roleType)) - { - ev.Player.Role.Set(roleType, SpawnReason.Respawn, RoleSpawnFlags.None); - } - else if (CustomRole.TryGet(destinationRole, out CustomRole customRole)) + if (Enum.TryParse(destinationRole, out RoleTypeId roleType)) + { + ev.Player.Role.Set(roleType, SpawnReason.Respawn, RoleSpawnFlags.None); + } + else if (CustomRole.TryGet(destinationRole, out CustomRole customRole)) + { + if (customRole is not null) { - if (customRole is not null) - { - customRole.AddRole(ev.Player); - Timing.CallDelayed(0.5f, () => ev.Player.Teleport(ev.OutputPosition)); - } + customRole.AddRole(ev.Player); + Timing.CallDelayed(0.5f, () => ev.Player.Teleport(ev.OutputPosition)); } + } + + if (keepHealth) + { + ev.Player.Health = originalHealth; + } - if (keepHealth) + if (keepInventory) + { + foreach (var item in originalItems) { - ev.Player.Health = originalHealth; + ev.Player.AddItem(item); } - if (keepInventory) + foreach (var kvp in originalAmmo) { - foreach (var item in originalItems) - { - ev.Player.AddItem(item); - } - - foreach (var kvp in originalAmmo) - { - ev.Player.SetAmmo(kvp.Key.GetAmmoType(), kvp.Value); - } + ev.Player.SetAmmo(kvp.Key.GetAmmoType(), kvp.Value); } - - ev.Player.Position = ev.OutputPosition; - break; } - - r -= chance; + + ev.Player.Position = ev.OutputPosition; + break; } + + if (config.AdditiveProbabilities) + rolledChance -= chance; } + } - if (plugin.Config.Scp914EffectChances != null && plugin.Config.Scp914EffectChances.ContainsKey(ev.KnobSetting) && (ev.Player.Role.Side != Side.Scp || !plugin.Config.ScpsImmuneTo914Effects)) - { - IEnumerable scp914EffectChances = plugin.Config.Scp914EffectChances[ev.KnobSetting]; + if (config.Scp914EffectChances != null && config.Scp914EffectChances.ContainsKey(ev.KnobSetting) && (ev.Player.Role.Side != Side.Scp || !config.ScpsImmuneTo914Effects)) + { + IEnumerable scp914EffectChances = config.Scp914EffectChances[ev.KnobSetting]; + + double rolledChance = Utils.RollChance(scp914EffectChances); - foreach ((EffectType effect, double chance, float duration) in scp914EffectChances) + foreach ((EffectType effect, double chance, float duration) in scp914EffectChances) + { + Log.Debug($"{nameof(OnUpgradingPlayer)}: {ev.Player.Nickname} is trying to gain an effect through SCP-914. {effect} ({chance}). Should be added: {rolledChance <= chance} ({rolledChance})"); + + if (rolledChance <= chance) { - double r; - if (plugin.Config.AdditiveProbabilities) - r = plugin.Rng.NextDouble() * scp914EffectChances.Sum(x => x.Chance); - else - r = plugin.Rng.NextDouble() * 100; - - Log.Debug($"{nameof(OnScp914UpgradingPlayer)}: {ev.Player.Nickname} is trying to gain an effect. {effect} ({chance}). Should be added: {r <= chance} ({r})"); - if (r <= chance) - { - ev.Player.EnableEffect(effect, duration); - if (plugin.Config.Scp914EffectsExclusivity) - break; - } - - r -= chance; + ev.Player.EnableEffect(effect, duration); + if (config.Scp914EffectsExclusivity) + break; } + + if (config.AdditiveProbabilities) + rolledChance -= chance; } + } + + if (config.Scp914TeleportChances != null && config.Scp914TeleportChances.ContainsKey(ev.KnobSetting)) + { + IEnumerable scp914TeleportChances = config.Scp914TeleportChances[ev.KnobSetting]; + + double rolledChance = Utils.RollChance(scp914TeleportChances); - if (plugin.Config.Scp914TeleportChances != null && plugin.Config.Scp914TeleportChances.ContainsKey(ev.KnobSetting)) + foreach ((RoomType roomType, List ignoredRooms, Vector3 offset, double chance, float damage, ZoneType zone) in config.Scp914TeleportChances[ev.KnobSetting]) { - IEnumerable scp914TeleportChances = plugin.Config.Scp914TeleportChances[ev.KnobSetting]; + Log.Debug($"{nameof(OnUpgradingPlayer)}: {ev.Player.Nickname} is trying to be teleported by 914. {roomType} + {offset} ({chance}). Should be teleported: {rolledChance <= chance} ({rolledChance})"); - foreach ((RoomType roomType, List ignoredRooms, Vector3 offset, double chance, float damage, ZoneType zone) in plugin.Config.Scp914TeleportChances[ev.KnobSetting]) + if (rolledChance <= chance) { - double r; - if (plugin.Config.AdditiveProbabilities) - r = plugin.Rng.NextDouble() * scp914TeleportChances.Sum(x => x.Chance); - else - r = plugin.Rng.NextDouble() * 100; - - Log.Debug($"{nameof(OnScp914UpgradingPlayer)}: {ev.Player.Nickname} is trying to be teleported by 914. {roomType} + {offset} ({chance}). Should be teleported: {r <= chance} ({r})"); - if (r <= chance) - { - if (zone != ZoneType.Unspecified) - { - ev.OutputPosition = Room.List.Where(x => x.Zone == zone && !ignoredRooms.Contains(x.Type)).GetRandomValue().Position + ((Vector3.up * 1.5f) + offset); - if (damage > 0f) - { - float amount = ev.Player.MaxHealth * damage; - if (damage > 1f) - amount = damage; - - Log.Debug($"{nameof(OnScp914UpgradingPlayer)}: {ev.Player.Nickname} is being damaged for {amount}. -- {ev.Player.Health} * {damage}"); - ev.Player.Hurt(amount, "SCP-914 Teleport", "SCP-914"); - } - } - else - { - ev.OutputPosition = Room.Get(roomType).Position + (Vector3.up * 1.5f) + offset; - if (damage > 0f) - { - float amount = ev.Player.MaxHealth * damage; - if (damage > 1f) - amount = damage; - - Log.Debug( - $"{nameof(OnScp914UpgradingPlayer)}: {ev.Player.Nickname} is being damaged for {amount}. -- {ev.Player.Health} * {damage}"); - ev.Player.Hurt(amount, "SCP-914 Teleport", "SCP-914"); - } - } - - break; - } + ev.OutputPosition = ChoosePosition(zone, ignoredRooms, offset, roomType); + + DealDamage(ev.Player, damage); - r -= chance; + break; } + + if (config.AdditiveProbabilities) + rolledChance -= chance; } } + } + + private Vector3 ChoosePosition(ZoneType zone, List ignoredRooms, Vector3 offset, RoomType roomType) + { + Room room1 = Room.List.Where(x => x.Zone == zone && !ignoredRooms.Contains(x.Type)).GetRandomValue(); + Vector3 pos1 = (room1?.Position ?? Vector3.zero) + (Vector3.up * 1.5f); + + Room room2 = Room.Get(roomType); + Vector3 pos2 = room2?.Position ?? Vector3.zero + (Vector3.up * 1.5f) + offset; + + return zone != ZoneType.Unspecified ? pos1 : pos2; + } + + private void DealDamage(Player player, float damage) + { + if (damage > 0f) + { + float amount = player.MaxHealth * damage; + if (damage > 1f) + amount = damage; + + Log.Debug( + $"{nameof(OnUpgradingPlayer)}: {player.Nickname} is being damaged for {amount}. -- {player.Health} * {damage}"); + player.Hurt(amount, "SCP-914 Teleport", "SCP-914"); + } + } - internal void UpgradeItem(Pickup oldItem, ItemType newItem, Vector3 pos, int count) + private void UpgradePickup(Pickup oldItem, Vector3 outputPos, int count, bool isCustomItem, ItemType itemType = ItemType.None, CustomItem customItem = null) + { + if (!isCustomItem) { Quaternion quaternion = oldItem.Rotation; Player previousOwner = oldItem.PreviousOwner; - oldItem.Destroy(); - if (newItem is not ItemType.None) + for (int i = 0; i < count; i++) { - for (int i = 0; i < count; i++) - { - Pickup.CreateAndSpawn(newItem, pos, quaternion, previousOwner); - } + Pickup.CreateAndSpawn(itemType, outputPos, quaternion, previousOwner); + } + } + else + { + for (int i = 0; i < count; i++) + { + customItem!.Spawn(outputPos, oldItem.PreviousOwner); + } + } + } + + private void UpgradeInventoryItem(UpgradingInventoryItemEventArgs ev, int count, bool isCustomItem, ItemType itemType = ItemType.None, CustomItem customItem = null) + { + ev.Player.RemoveItem(ev.Item); + if (!isCustomItem) + { + for (int i = 0; i < count; i++) + { + if (!ev.Player.IsInventoryFull) + ev.Player.AddItem(itemType); + else + Pickup.CreateAndSpawn(itemType, Scp914.OutputPosition, ev.Player.Rotation, ev.Player); + } + } + else + { + for (int i = 0; i < count; i++) + { + if (!ev.Player.IsInventoryFull) + customItem!.Give(ev.Player); + else + customItem!.Spawn(Scp914.OutputPosition, ev.Player); } } } diff --git a/Common Utilities/EventHandlers/PlayerHandlers.cs b/Common Utilities/EventHandlers/PlayerHandlers.cs index 6fddbb6..49043a2 100644 --- a/Common Utilities/EventHandlers/PlayerHandlers.cs +++ b/Common Utilities/EventHandlers/PlayerHandlers.cs @@ -1,219 +1,208 @@ -namespace Common_Utilities.EventHandlers -{ +namespace Common_Utilities.EventHandlers; + #pragma warning disable IDE0018 - using System; - using System.Collections.Generic; - using System.Linq; - - using Common_Utilities.ConfigObjects; - using Exiled.API.Enums; - using Exiled.API.Features; - using Exiled.API.Features.Roles; - using Exiled.CustomItems.API.Features; - using Exiled.Events.EventArgs.Interfaces; - using Exiled.Events.EventArgs.Player; - using PlayerRoles; - using UnityEngine; - - using Player = Exiled.API.Features.Player; - - public class PlayerHandlers +using System; +using System.Collections.Generic; +using System.Linq; +using ConfigObjects; +using Exiled.API.Features; +using Exiled.API.Features.Roles; +using Exiled.CustomItems.API.Features; +using Exiled.Events.EventArgs.Interfaces; +using Exiled.Events.EventArgs.Player; +using PlayerRoles; +using UnityEngine; + +using Player = Exiled.API.Features.Player; + +public class PlayerHandlers +{ + private Config config => Plugin.Instance.Config; + + public void OnPlayerVerified(VerifiedEventArgs ev) { - private readonly Main plugin; - - public PlayerHandlers(Main plugin) => this.plugin = plugin; + string message = FormatJoinMessage(ev.Player); + + if (!string.IsNullOrEmpty(message)) + ev.Player.Broadcast(config.JoinMessageDuration, message); + } - public void OnPlayerVerified(VerifiedEventArgs ev) + public void OnChangingRole(ChangingRoleEventArgs ev) + { + if (ev.Player == null) { - string message = FormatJoinMessage(ev.Player); - if (!string.IsNullOrEmpty(message)) - ev.Player.Broadcast(plugin.Config.JoinMessageDuration, message); + Log.Warn($"{nameof(OnChangingRole)}: Triggering player is null."); + return; } - - public void OnChangingRole(ChangingRoleEventArgs ev) + + // no clue why this works while in ChangingRole instead of spawned but if it ain't broke don't fix it + if (config.StartingInventories.ContainsKey(ev.NewRole) && !ev.ShouldPreserveInventory) { - if (ev.Player == null) + if (ev.Items == null) { - Log.Warn($"{nameof(OnChangingRole)}: Triggering player is null."); + Log.Warn("items is null"); return; } + + ev.Items.Clear(); + ev.Items.AddRange(GetStartingInventory(ev.NewRole, ev.Player)); - if (plugin.Config.StartingInventories.ContainsKey(ev.NewRole) && !ev.ShouldPreserveInventory) + if (config.StartingInventories[ev.NewRole].Ammo == null || config.StartingInventories[ev.NewRole].Ammo.Count <= 0) + return; + + if (config.StartingInventories[ev.NewRole].Ammo.Any(s => string.IsNullOrEmpty(s.Group) || s.Group == "none" || (ServerStatic.PermissionsHandler._groups.TryGetValue(s.Group, out UserGroup userGroup) && userGroup == ev.Player.Group))) { - if (ev.Items == null) + ev.Ammo.Clear(); + foreach ((ItemType type, ushort amount, string group) in config.StartingInventories[ev.NewRole].Ammo) { - Log.Warn("items is null"); - return; - } - - ev.Items.Clear(); - ev.Items.AddRange(StartItems(ev.NewRole, ev.Player)); - - if (plugin.Config.StartingInventories[ev.NewRole].Ammo != null && plugin.Config.StartingInventories[ev.NewRole].Ammo.Count > 0) - { - if (plugin.Config.StartingInventories[ev.NewRole].Ammo.Any(s => string.IsNullOrEmpty(s.Group) || s.Group == "none" || (ServerStatic.PermissionsHandler._groups.TryGetValue(s.Group, out UserGroup userGroup) && userGroup == ev.Player.Group))) + if (string.IsNullOrEmpty(group) || group == "none" || (ServerStatic.PermissionsHandler._groups.TryGetValue(group, out UserGroup userGroup) && userGroup == ev.Player.Group)) { - ev.Ammo.Clear(); - foreach ((ItemType type, ushort amount, string group) in plugin.Config.StartingInventories[ev.NewRole].Ammo) - { - if (string.IsNullOrEmpty(group) || group == "none" || (ServerStatic.PermissionsHandler._groups.TryGetValue(group, out UserGroup userGroup) && userGroup == ev.Player.Group)) - { - ev.Ammo.Add(type, amount); - } - } + ev.Ammo.Add(type, amount); } } } } + } - public void OnSpawned(SpawnedEventArgs ev) + public void OnSpawned(SpawnedEventArgs ev) + { + if (ev.Player == null) { - if (ev.Player == null) - { - Log.Warn($"{nameof(OnSpawned)}: Triggering player is null."); - return; - } - - RoleTypeId newRole = ev.Player.Role.Type; - if (plugin.Config.HealthValues != null && plugin.Config.HealthValues.TryGetValue(newRole, out int health)) - { - ev.Player.Health = health; - ev.Player.MaxHealth = health; - } - - if (ev.Player.Role is FpcRole && plugin.Config.PlayerHealthInfo) - { - ev.Player.CustomInfo = $"({ev.Player.Health}/{ev.Player.MaxHealth}) {(!string.IsNullOrEmpty(ev.Player.CustomInfo) ? ev.Player.CustomInfo.Substring(ev.Player.CustomInfo.LastIndexOf(')') + 1) : string.Empty)}"; - } - - if (plugin.Config.AfkIgnoredRoles.Contains(newRole) && plugin.AfkDict.TryGetValue(ev.Player, out Tuple value)) - plugin.AfkDict[ev.Player] = new Tuple(newRole is RoleTypeId.Spectator ? value.Item1 : 0, ev.Player.Position); + Log.Warn($"{nameof(OnSpawned)}: Triggering player is null."); + return; } - public void OnPlayerDied(DiedEventArgs ev) + RoleTypeId newRole = ev.Player.Role.Type; + if (config.HealthValues != null && config.HealthValues.TryGetValue(newRole, out int health)) { - if (ev.Attacker != null && plugin.Config.HealthOnKill.ContainsKey(ev.Attacker.Role)) - { - ev.Attacker.Heal(plugin.Config.HealthOnKill[ev.Attacker.Role]); - } + ev.Player.MaxHealth = health; + ev.Player.Health = health; } - public List StartItems(RoleTypeId role, Player player = null) + if (ev.Player.Role is FpcRole && config.PlayerHealthInfo) { - List items = new(); - - for (int i = 0; i < plugin.Config.StartingInventories[role].UsedSlots; i++) - { - IEnumerable itemChances = plugin.Config.StartingInventories[role][i].Where(x => player == null || string.IsNullOrEmpty(x.Group) || x.Group == "none" || (ServerStatic.PermissionsHandler._groups.TryGetValue(x.Group, out var group) && group == player.Group)); - double r; - if (plugin.Config.AdditiveProbabilities) - r = plugin.Rng.NextDouble() * itemChances.Sum(val => val.Chance); - else - r = plugin.Rng.NextDouble() * 100; - Log.Debug($"[StartItems] ActualChance ({r})/{itemChances.Sum(val => val.Chance)}"); - foreach ((string item, double chance, string groupKey) in itemChances) - { - Log.Debug($"[StartItems] Probability ({r})/{chance}"); - if (r <= chance) - { - if (Enum.TryParse(item, true, out ItemType type)) - { - items.Add(type); - break; - } - else if (CustomItem.TryGet(item, out CustomItem customItem)) - { - if (player != null) - customItem!.Give(player); - else - Log.Warn($"{nameof(StartItems)}: Tried to give {customItem!.Name} to a null player."); - - break; - } - else - Log.Warn($"{nameof(StartItems)}: {item} is not a valid ItemType or it is a CustomItem that is not installed! It is being skipper in inventory decisions."); - } - - r -= chance; - } - } + ev.Player.CustomInfo = $"({ev.Player.Health}/{ev.Player.MaxHealth}) {(!string.IsNullOrEmpty(ev.Player.CustomInfo) ? ev.Player.CustomInfo.Substring(ev.Player.CustomInfo.LastIndexOf(')') + 1) : string.Empty)}"; + } - return items; + if (config.AfkIgnoredRoles.Contains(newRole) && Plugin.AfkDict.TryGetValue(ev.Player, out Tuple value)) + { + Plugin.AfkDict[ev.Player] = + new Tuple(newRole is RoleTypeId.Spectator ? value.Item1 : 0, ev.Player.Position); } + } - public string FormatJoinMessage(Player player) => - string.IsNullOrEmpty(plugin.Config.JoinMessage) ? string.Empty : plugin.Config.JoinMessage.Replace("%player%", player.Nickname).Replace("%server%", Server.Name).Replace("%count%", $"{Player.Dictionary.Count}"); - - public void OnPlayerHurting(HurtingEventArgs ev) + public void OnPlayerDied(DiedEventArgs ev) + { + if (ev.Attacker != null && config.HealthOnKill.ContainsKey(ev.Attacker.Role)) { - float damageMultiplier; - if (plugin.Config.RoleDamageMultipliers != null && ev.Attacker != null && plugin.Config.RoleDamageMultipliers.TryGetValue(ev.Attacker.Role, out damageMultiplier)) - ev.Amount *= damageMultiplier; + ev.Attacker.Heal(config.HealthOnKill[ev.Attacker.Role]); + } + } + + public void OnPlayerHurting(HurtingEventArgs ev) + { + if (config.RoleDamageMultipliers != null && ev.Attacker != null && config.RoleDamageMultipliers.TryGetValue(ev.Attacker.Role, out var damageMultiplier)) + ev.Amount *= damageMultiplier; - if (plugin.Config.DamageMultipliers != null && plugin.Config.DamageMultipliers.TryGetValue(ev.DamageHandler.Type, out damageMultiplier)) - ev.Amount *= damageMultiplier; + if (config.DamageMultipliers != null && config.DamageMultipliers.TryGetValue(ev.DamageHandler.Type, out damageMultiplier)) + ev.Amount *= damageMultiplier; - if (plugin.Config.PlayerHealthInfo) - ev.Player.CustomInfo = $"({ev.Player.Health}/{ev.Player.MaxHealth}) {(!string.IsNullOrEmpty(ev.Player.CustomInfo) ? ev.Player.CustomInfo.Substring(ev.Player.CustomInfo.LastIndexOf(')') + 1) : string.Empty)}"; + if (config.PlayerHealthInfo) + ev.Player.CustomInfo = $"({ev.Player.Health}/{ev.Player.MaxHealth}) {(!string.IsNullOrEmpty(ev.Player.CustomInfo) ? ev.Player.CustomInfo.Substring(ev.Player.CustomInfo.LastIndexOf(')') + 1) : string.Empty)}"; - if (ev.Attacker is not null && plugin.AfkDict.ContainsKey(ev.Attacker)) - { - Log.Debug($"Resetting {ev.Attacker.Nickname} AFK timer."); - plugin.AfkDict[ev.Attacker] = new Tuple(0, ev.Attacker.Position); - } + if (ev.Attacker is not null && Plugin.AfkDict.ContainsKey(ev.Attacker)) + { + Log.Debug($"Resetting {ev.Attacker.Nickname} AFK timer."); + Plugin.AfkDict[ev.Attacker] = new Tuple(0, ev.Attacker.Position); } + } - public void OnInteractingDoor(InteractingDoorEventArgs ev) + public void OnEscaping(EscapingEventArgs ev) + { + if (ev.Player.IsCuffed && config.DisarmedEscapeSwitchRole.TryGetValue(ev.Player.Role, out RoleTypeId newRole)) { - if (ev.Player.IsCuffed && plugin.Config.RestrictiveDisarming) - ev.IsAllowed = false; - - if (plugin.AfkDict.ContainsKey(ev.Player)) - { - Log.Debug($"Resetting {ev.Player.Nickname} AFK timer."); - plugin.AfkDict[ev.Player] = new Tuple(0, ev.Player.Position); - } + ev.NewRole = newRole; + ev.IsAllowed = newRole != RoleTypeId.None; } + } - public void OnInteractingElevator(InteractingElevatorEventArgs ev) - { - if (ev.Player.IsCuffed && plugin.Config.RestrictiveDisarming) - ev.IsAllowed = false; + public void OnUsingRadioBattery(UsingRadioBatteryEventArgs ev) + { + ev.Drain *= config.RadioBatteryDrainMultiplier; + } - if (plugin.AfkDict.ContainsKey(ev.Player)) - { - Log.Debug($"Resetting {ev.Player.Nickname} AFK timer."); - plugin.AfkDict[ev.Player] = new Tuple(0, ev.Player.Position); - } + public void AntiAfkEventHandler(IPlayerEvent ev) + { + if (ev.Player != null && Plugin.AfkDict.ContainsKey(ev.Player)) + { + Log.Debug($"Resetting {ev.Player.Nickname} AFK timer."); + Plugin.AfkDict[ev.Player] = new Tuple(0, ev.Player.Position); } - - public void OnEscaping(EscapingEventArgs ev) + } + + public List GetStartingInventory(RoleTypeId role, Player player = null) + { + List items = new(); + + // iterate through slots + for (int i = 0; i < config.StartingInventories[role].UsedSlots; i++) { - if (ev.EscapeScenario is EscapeScenario.CustomEscape) +#pragma warning disable SA1119 + // item chances for that slot + List itemChances = config.StartingInventories[role][i] + .Where(x => + player == null + || string.IsNullOrEmpty(x.Group) + || x.Group == "none" + || (ServerStatic.PermissionsHandler._groups.TryGetValue(x.Group, out var group) && group == player.Group)) + .ToList(); +#pragma warning restore SA1119 + + double rolledChance = Utils.RollChance(itemChances); + + Log.Debug($"[StartItems] RolledChance ({rolledChance})/{itemChances.Sum(val => val.Chance)}"); + + foreach ((string item, double chance) in itemChances) { - ev.NewRole = ev.Player.Role.Type switch + Log.Debug($"[StartItems] Probability ({rolledChance})/{chance}"); + + if (rolledChance <= chance) { - RoleTypeId.FacilityGuard or RoleTypeId.NtfPrivate or RoleTypeId.NtfSergeant or RoleTypeId.NtfCaptain or RoleTypeId.NtfSpecialist => RoleTypeId.ChaosConscript, - RoleTypeId.ChaosConscript or RoleTypeId.ChaosMarauder or RoleTypeId.ChaosRepressor or RoleTypeId.ChaosRifleman => RoleTypeId.ChaosConscript, - _ => RoleTypeId.None, - }; + if (Enum.TryParse(item, true, out ItemType type)) + { + items.Add(type); + break; + } - ev.IsAllowed = ev.NewRole is not RoleTypeId.None; - } - } + if (CustomItem.TryGet(item, out CustomItem customItem)) + { + if (player != null) + customItem!.Give(player); + else + Log.Warn($"{nameof(GetStartingInventory)}: Tried to give {customItem!.Name} to a null player."); + + break; + } - public void OnUsingRadioBattery(UsingRadioBatteryEventArgs ev) - { - ev.Drain *= plugin.Config.RadioBatteryDrainMultiplier; - } + Log.Warn($"{nameof(GetStartingInventory)}: {item} is not a valid ItemType or CustomItem! It is being skipped in inventory decisions."); + } - public void AntiAfkEventHandler(IPlayerEvent ev) - { - if (ev.Player != null && plugin.AfkDict.ContainsKey(ev.Player)) - { - Log.Debug($"Resetting {ev.Player.Nickname} AFK timer."); - plugin.AfkDict[ev.Player] = new Tuple(0, ev.Player.Position); + if (config.AdditiveProbabilities) + rolledChance -= chance; } } + + return items; + } + + private string FormatJoinMessage(Player player) + { + return + string.IsNullOrEmpty(config.JoinMessage) + ? string.Empty + : config.JoinMessage + .Replace("%player%", player.Nickname) + .Replace("%server%", Server.Name) + .Replace("%count%", $"{Player.Dictionary.Count}"); } } \ No newline at end of file diff --git a/Common Utilities/EventHandlers/ServerHandlers.cs b/Common Utilities/EventHandlers/ServerHandlers.cs index aed321d..f580c43 100644 --- a/Common Utilities/EventHandlers/ServerHandlers.cs +++ b/Common Utilities/EventHandlers/ServerHandlers.cs @@ -1,48 +1,43 @@ +// ReSharper disable IteratorNeverReturns namespace Common_Utilities.EventHandlers { #pragma warning disable SA1313 // Parameter names should begin with lower-case letter using System; using System.Collections.Generic; - using System.Linq; - - using Exiled.API.Enums; + using Exiled.API.Features; using Exiled.API.Features.Pickups; using Exiled.API.Features.Roles; using Exiled.Events.EventArgs.Server; using Exiled.Events.EventArgs.Warhead; - using InventorySystem.Configs; using MEC; - using PlayerRoles; using UnityEngine; public class ServerHandlers { - private readonly Main plugin; - - private bool friendlyFireDisable = false; - - public ServerHandlers(Main plugin) => this.plugin = plugin; + private Config config => Plugin.Instance.Config; + + private bool friendlyFireDisable; public void OnRoundStarted() { - if (plugin.Config.AutonukeTime > -1) - plugin.Coroutines.Add(Timing.CallDelayed(plugin.Config.AutonukeTime, AutoNuke)); + if (config.AutonukeTime > -1) + Plugin.Coroutines.Add(Timing.CallDelayed(config.AutonukeTime, AutoNuke)); - if (plugin.Config.RagdollCleanupDelay > 0) - plugin.Coroutines.Add(Timing.RunCoroutine(RagdollCleanup())); + if (config.RagdollCleanupDelay > 0) + Plugin.Coroutines.Add(Timing.RunCoroutine(RagdollCleanup())); - if (plugin.Config.ItemCleanupDelay > 0) - plugin.Coroutines.Add(Timing.RunCoroutine(ItemCleanup())); + if (config.ItemCleanupDelay > 0) + Plugin.Coroutines.Add(Timing.RunCoroutine(ItemCleanup())); } public void OnWaitingForPlayers() { - if (plugin.Config.AfkLimit > 0) + if (config.AfkLimit > 0) { - plugin.AfkDict.Clear(); - plugin.Coroutines.Add(Timing.RunCoroutine(AfkCheck())); + Plugin.AfkDict.Clear(); + Plugin.Coroutines.Add(Timing.RunCoroutine(AfkCheck())); } if (friendlyFireDisable) @@ -52,103 +47,129 @@ public void OnWaitingForPlayers() friendlyFireDisable = false; } - if (plugin.Config.TimedBroadcastDelay > 0) - plugin.Coroutines.Add(Timing.RunCoroutine(ServerBroadcast())); - - // Fix GrandLoadout not able to give this 2 inventory - StartingInventories.DefinedInventories[RoleTypeId.Tutorial] = new(Array.Empty(), new()); - StartingInventories.DefinedInventories[RoleTypeId.ClassD] = new(Array.Empty(), new()); + if (config.TimedBroadcastDelay > 0) + Plugin.Coroutines.Add(Timing.RunCoroutine(ServerBroadcast())); Warhead.IsLocked = false; } public void OnRoundEnded(RoundEndedEventArgs ev) { - if (plugin.Config.FriendlyFireOnRoundEnd && !Server.FriendlyFire) + if (config.FriendlyFireOnRoundEnd && !Server.FriendlyFire) { Log.Debug($"{nameof(OnRoundEnded)}: Enabling friendly fire."); Server.FriendlyFire = true; friendlyFireDisable = true; } - foreach (CoroutineHandle coroutine in plugin.Coroutines) + Timing.KillCoroutines(Plugin.Coroutines.ToArray()); + + Plugin.Coroutines.Clear(); + } + + public void OnRestartingRound() + { + foreach (CoroutineHandle coroutine in Plugin.Coroutines) Timing.KillCoroutines(coroutine); - plugin.Coroutines.Clear(); + Plugin.Coroutines.Clear(); + } + + public void OnWarheadStarting(StartingEventArgs _) + { + if (!config.ChangeWarheadColor) + return; + + foreach (Room room in Room.List) + room.Color = config.WarheadColor; } - public IEnumerator ServerBroadcast() + public void OnWarheadStopping(StoppingEventArgs _) + { + if (!config.ChangeWarheadColor || Warhead.IsLocked) + return; + + foreach (Room room in Room.List) + room.ResetColor(); + } + + private IEnumerator ServerBroadcast() { - for (; ; ) + while(true) { - yield return Timing.WaitForSeconds(plugin.Config.TimedBroadcastDelay); + yield return Timing.WaitForSeconds(config.TimedBroadcastDelay); - Map.Broadcast(plugin.Config.TimedBroadcastDuration, plugin.Config.TimedBroadcast); + Map.Broadcast(config.TimedBroadcastDuration, config.TimedBroadcast); } } - public IEnumerator ItemCleanup() + private IEnumerator ItemCleanup() { - for (; ; ) + while(true) { - yield return Timing.WaitForSeconds(plugin.Config.ItemCleanupDelay); + yield return Timing.WaitForSeconds(config.ItemCleanupDelay); - foreach (Pickup pickup in Pickup.List.ToList()) + foreach (Pickup pickup in Pickup.List) { - if (!plugin.Config.ItemCleanupOnlyPocket || pickup.Position.y < -1500f) + if (!config.ItemCleanupOnlyPocket || pickup.Position.y < -1500f) pickup.Destroy(); } } } - public IEnumerator RagdollCleanup() + private IEnumerator RagdollCleanup() { - for (; ; ) + while(true) { - yield return Timing.WaitForSeconds(plugin.Config.RagdollCleanupDelay); + yield return Timing.WaitForSeconds(config.RagdollCleanupDelay); - foreach (Ragdoll ragdoll in Ragdoll.List.ToList()) + foreach (Ragdoll ragdoll in Ragdoll.List) { - if (!plugin.Config.RagdollCleanupOnlyPocket || ragdoll.Position.y < -1500f) + if (!config.RagdollCleanupOnlyPocket || ragdoll.Position.y < -1500f) ragdoll.Destroy(); } } } - public void AutoNuke() + private void AutoNuke() { if (!Warhead.IsInProgress) { - switch (plugin.Config.AutonukeBroadcast.Duration) + switch (config.AutonukeBroadcast.Duration) { case 0: break; case 1: - Cassie.Message(plugin.Config.AutonukeBroadcast.Content); + Cassie.Message(config.AutonukeBroadcast.Content); break; default: - Map.Broadcast(plugin.Config.AutonukeBroadcast); + Map.Broadcast(config.AutonukeBroadcast); break; } Warhead.Start(); } - if (plugin.Config.AutonukeLock) + if (config.AutonukeLock) Warhead.IsLocked = true; } - public IEnumerator AfkCheck() + private IEnumerator AfkCheck() { - for (; ; ) + while(true) { yield return Timing.WaitForSeconds(1f); foreach (Player player in Player.List) { - if (!plugin.AfkDict.ContainsKey(player)) - plugin.AfkDict.Add(player, new Tuple(0, player.Position)); - - if (player.Role.IsDead || player.IsGodModeEnabled || player.IsNoclipPermitted || player.Role is FpcRole { IsGrounded: false } || player.RemoteAdminPermissions.HasFlag(PlayerPermissions.AFKImmunity) || plugin.Config.AfkIgnoredRoles.Contains(player.Role.Type)) + if (!Plugin.AfkDict.ContainsKey(player)) + Plugin.AfkDict.Add(player, new Tuple(0, player.Position)); + + if (player.Role.IsDead + || player.IsGodModeEnabled + || player.IsNoclipPermitted + || player.Role is FpcRole { IsGrounded: false } + || player.RemoteAdminPermissions.HasFlag(PlayerPermissions.AFKImmunity) + || config.AfkIgnoredRoles.Contains(player.Role.Type)) { #pragma warning disable SA1013 Log.Debug($"Player {player.Nickname} ({player.Role.Type}) is not a checkable player. NoClip: {player.IsNoclipPermitted} GodMode: {player.IsGodModeEnabled} IsNotGrounded: {player.Role is FpcRole { IsGrounded: false }} AFKImunity: {player.RemoteAdminPermissions.HasFlag(PlayerPermissions.AFKImmunity)}"); @@ -156,48 +177,26 @@ public IEnumerator AfkCheck() #pragma warning restore SA1013 } - if ((plugin.AfkDict[player].Item2 - player.Position).sqrMagnitude > 2) + if ((Plugin.AfkDict[player].Item2 - player.Position).sqrMagnitude > 2) { Log.Debug($"Player {player.Nickname} has moved, resetting AFK timer."); - plugin.AfkDict[player] = new Tuple(0, player.Position); + Plugin.AfkDict[player] = new Tuple(0, player.Position); } - if (plugin.AfkDict[player].Item1 >= plugin.Config.AfkLimit) + if (Plugin.AfkDict[player].Item1 >= config.AfkLimit) { - plugin.AfkDict.Remove(player); + Plugin.AfkDict.Remove(player); Log.Debug($"Kicking {player.Nickname} for being AFK."); player.Kick("You were kicked by CommonUtilities for being AFK."); } - else if (plugin.AfkDict[player].Item1 >= (plugin.Config.AfkLimit / 2)) + else if (Plugin.AfkDict[player].Item1 >= (config.AfkLimit / 2)) { - player.Broadcast(2, $"You have been AFK for {plugin.AfkDict[player].Item1} seconds. You will be automatically kicked if you remain AFK for a total of {plugin.Config.AfkLimit} seconds.", shouldClearPrevious: true); + player.Broadcast(2, $"You have been AFK for {Plugin.AfkDict[player].Item1} seconds. You will be automatically kicked if you remain AFK for a total of {config.AfkLimit} seconds.", shouldClearPrevious: true); } - plugin.AfkDict[player] = new Tuple(plugin.AfkDict[player].Item1 + 1, plugin.AfkDict[player].Item2); + Plugin.AfkDict[player] = new Tuple(Plugin.AfkDict[player].Item1 + 1, Plugin.AfkDict[player].Item2); } } } - - public void OnRestartingRound() - { - foreach (CoroutineHandle coroutine in plugin.Coroutines) - Timing.KillCoroutines(coroutine); - plugin.Coroutines.Clear(); - } - - public void OnWarheadStarting(StartingEventArgs _) - { - foreach (Room room in Room.List) - room.Color = plugin.Config.WarheadColor; - } - - public void OnWarheadStopping(StoppingEventArgs _) - { - if (Warhead.IsLocked) - return; - - foreach (Room room in Room.List) - room.ResetColor(); - } } -} \ No newline at end of file +} diff --git a/Common Utilities/Interfaces/IChanceObject.cs b/Common Utilities/Interfaces/IChanceObject.cs new file mode 100644 index 0000000..96c5d34 --- /dev/null +++ b/Common Utilities/Interfaces/IChanceObject.cs @@ -0,0 +1,6 @@ +namespace Common_Utilities.ConfigObjects; + +public interface IChanceObject +{ + public double Chance { get; set; } +} \ No newline at end of file diff --git a/Common Utilities/Main.cs b/Common Utilities/Main.cs deleted file mode 100644 index 6c73de9..0000000 --- a/Common Utilities/Main.cs +++ /dev/null @@ -1,219 +0,0 @@ -namespace Common_Utilities -{ -#pragma warning disable SA1401 // Fields should be private - using System; - using System.Collections.Generic; - - using ConfigObjects; - using Configs; - using EventHandlers; - using Exiled.API.Enums; - using Exiled.API.Features; - using HarmonyLib; - using MEC; - using PlayerRoles; - using Scp914; - using UnityEngine; - - using Player = Exiled.Events.Handlers.Player; - using Random = System.Random; - using Scp914 = Exiled.Events.Handlers.Scp914; - using Server = Exiled.Events.Handlers.Server; - - public class Main : Plugin - { - public static Main Instance; - public PlayerHandlers PlayerHandlers; - public ServerHandlers ServerHandlers; - public MapHandlers MapHandlers; - public Random Rng = new(); - public Harmony Harmony; - public string HarmonyName; - - public override string Name { get; } = "Common Utilities"; - - public override string Author { get; } = "Exiled-Team"; - - public override Version Version { get; } = new(7, 1, 1); - - public override Version RequiredExiledVersion { get; } = new(8, 5, 0); - - public override string Prefix { get; } = "CommonUtilities"; - - public override PluginPriority Priority => PluginPriority.Higher; - - public List Coroutines { get; } = new(); - - public Dictionary> AfkDict { get; } = new(); - - public override void OnEnabled() - { - if (Config.Debug) - DebugConfig(); - - Instance = this; - - Log.Info($"Instantiating Events.."); - PlayerHandlers = new PlayerHandlers(this); - ServerHandlers = new ServerHandlers(this); - MapHandlers = new MapHandlers(this); - - Log.Info($"Registering EventHandlers.."); - if (Config.HealthOnKill != null) - Player.Died += PlayerHandlers.OnPlayerDied; - Player.Hurting += PlayerHandlers.OnPlayerHurting; - Player.Verified += PlayerHandlers.OnPlayerVerified; - if (Config.StartingInventories != null) - Player.ChangingRole += PlayerHandlers.OnChangingRole; - Player.Spawned += PlayerHandlers.OnSpawned; - Player.InteractingDoor += PlayerHandlers.OnInteractingDoor; - if (Config.RadioBatteryDrainMultiplier is not 1) - Player.UsingRadioBattery += PlayerHandlers.OnUsingRadioBattery; - Player.InteractingElevator += PlayerHandlers.OnInteractingElevator; - if (Config.DisarmSwitchTeams) - Player.Escaping += PlayerHandlers.OnEscaping; - if (Config.AfkLimit > 0) - { - Player.Jumping += PlayerHandlers.AntiAfkEventHandler; - Player.Shooting += PlayerHandlers.AntiAfkEventHandler; - Player.UsingItem += PlayerHandlers.AntiAfkEventHandler; - Player.MakingNoise += PlayerHandlers.AntiAfkEventHandler; - Player.ReloadingWeapon += PlayerHandlers.AntiAfkEventHandler; - Player.ThrownProjectile += PlayerHandlers.AntiAfkEventHandler; - Player.ChangingMoveState += PlayerHandlers.AntiAfkEventHandler; - } - - Server.RoundEnded += ServerHandlers.OnRoundEnded; - Server.RoundStarted += ServerHandlers.OnRoundStarted; - Server.RestartingRound += ServerHandlers.OnRestartingRound; - Server.WaitingForPlayers += ServerHandlers.OnWaitingForPlayers; - - if (Config.Scp914ItemChanges != null) - Scp914.UpgradingPickup += MapHandlers.OnScp914UpgradingItem; - if (Config.Scp914ItemChanges != null) - Scp914.UpgradingInventoryItem += MapHandlers.OnScp914UpgradingInventoryItem; - Scp914.UpgradingPlayer += MapHandlers.OnScp914UpgradingPlayer; - - Exiled.Events.Handlers.Warhead.Starting += ServerHandlers.OnWarheadStarting; - Exiled.Events.Handlers.Warhead.Stopping += ServerHandlers.OnWarheadStopping; - - HarmonyName = $"com-joker.cu-{DateTime.UtcNow.Ticks}"; - Harmony = new Harmony(HarmonyName); - Harmony.PatchAll(); - - base.OnEnabled(); - } - - public override void OnDisabled() - { - Player.Died -= PlayerHandlers.OnPlayerDied; - Player.Jumping -= PlayerHandlers.AntiAfkEventHandler; - Player.Shooting -= PlayerHandlers.AntiAfkEventHandler; - Player.UsingItem -= PlayerHandlers.AntiAfkEventHandler; - Player.Hurting -= PlayerHandlers.OnPlayerHurting; - Player.Verified -= PlayerHandlers.OnPlayerVerified; - Player.MakingNoise -= PlayerHandlers.AntiAfkEventHandler; - Player.ReloadingWeapon -= PlayerHandlers.AntiAfkEventHandler; - Player.ChangingRole -= PlayerHandlers.OnChangingRole; - Player.Spawned -= PlayerHandlers.OnSpawned; - Player.ThrownProjectile -= PlayerHandlers.AntiAfkEventHandler; - Player.InteractingDoor -= PlayerHandlers.OnInteractingDoor; - Player.UsingRadioBattery -= PlayerHandlers.OnUsingRadioBattery; - Player.ChangingMoveState -= PlayerHandlers.AntiAfkEventHandler; - Player.InteractingElevator -= PlayerHandlers.OnInteractingElevator; - Player.Escaping -= PlayerHandlers.OnEscaping; - - Server.RoundEnded -= ServerHandlers.OnRoundEnded; - Server.RoundStarted -= ServerHandlers.OnRoundStarted; - Server.RestartingRound -= ServerHandlers.OnRestartingRound; - Server.WaitingForPlayers -= ServerHandlers.OnWaitingForPlayers; - - Scp914.UpgradingPickup -= MapHandlers.OnScp914UpgradingItem; - Scp914.UpgradingPlayer -= MapHandlers.OnScp914UpgradingPlayer; - Scp914.UpgradingInventoryItem -= MapHandlers.OnScp914UpgradingInventoryItem; - - Exiled.Events.Handlers.Warhead.Starting -= ServerHandlers.OnWarheadStarting; - Exiled.Events.Handlers.Warhead.Stopping -= ServerHandlers.OnWarheadStopping; - - Harmony.UnpatchAll(HarmonyName); - - ServerHandlers = null; - PlayerHandlers = null; - MapHandlers = null; - base.OnDisabled(); - } - - public void DebugConfig() - { - if (Config.StartingInventories != null) - { - Log.Debug($"{Config.StartingInventories.Count}"); - foreach (KeyValuePair inv in Config.StartingInventories) - { - for (int i = 0; i < inv.Value.UsedSlots; i++) - { - foreach (ItemChance chance in inv.Value[i]) - { - Log.Debug($"Inventory Config: {inv.Key} - Slot{i + 1}: {chance.ItemName} ({chance.Chance})"); - } - } - - foreach ((ItemType type, ushort amount, string group) in inv.Value.Ammo) - { - Log.Debug($"Ammo Config: {inv.Key} - {type} {amount} ({group})"); - } - } - } - - if (Config.Scp914ItemChanges != null) - { - Log.Debug($"{Config.Scp914ItemChanges.Count}"); - foreach (KeyValuePair> upgrade in Config.Scp914ItemChanges) - { - foreach ((ItemType oldItem, ItemType newItem, double chance, int count) in upgrade.Value) - Log.Debug($"914 Item Config: {upgrade.Key}: {oldItem} -> {newItem}x({count}) - {chance}"); - } - } - - if (Config.Scp914ClassChanges != null) - { - Log.Debug($"{Config.Scp914ClassChanges.Count}"); - foreach (KeyValuePair> upgrade in Config.Scp914ClassChanges) - { - foreach ((RoleTypeId oldRole, string newRole, double chance, bool keepInventory, bool keepHealth) in upgrade.Value) - Log.Debug($"914 Role Config: {upgrade.Key}: {oldRole} -> {newRole} - {chance} keepInventory: {keepInventory} keepHealth: {keepHealth}"); - } - } - - if (Config.Scp914EffectChances != null) - { - Log.Debug($"{Config.Scp914EffectChances.Count}"); - foreach (KeyValuePair> upgrade in Config.Scp914EffectChances) - { - foreach ((EffectType effect, double chance, float duration) in upgrade.Value) - Log.Debug($"914 Effect Config: {upgrade.Key}: {effect} + {duration} - {chance}"); - } - } - - if (Config.Scp914TeleportChances != null) - { - Log.Debug($"{Config.Scp914TeleportChances.Count}"); - foreach (KeyValuePair> upgrade in Config.Scp914TeleportChances) - { - foreach ((RoomType room, List ignoredRooms, Vector3 offset, double chance, float damage, ZoneType zone) in upgrade.Value) - { - Log.Debug($"914 Teleport Config: {upgrade.Key}: {room}/{zone} + {offset} - {chance} [{damage}]"); - Log.Debug("Ignored rooms:"); - if (ignoredRooms != null) - { - foreach (RoomType roomType in ignoredRooms) - { - Log.Debug(roomType); - } - } - } - } - } - } - } -} \ No newline at end of file diff --git a/Common Utilities/Patches/ServerGrantLoadoutPatches.cs b/Common Utilities/Patches/ServerGrantLoadoutPatches.cs deleted file mode 100644 index c5824af..0000000 --- a/Common Utilities/Patches/ServerGrantLoadoutPatches.cs +++ /dev/null @@ -1,43 +0,0 @@ -namespace Common_Utilities.Patches -{ - using System.Collections.Generic; - using System.Linq; - - using Common_Utilities.ConfigObjects; - using Common_Utilities.Configs; - using Exiled.API.Features; - using HarmonyLib; - using InventorySystem; - using PlayerRoles; - - [HarmonyPatch(typeof(InventoryItemProvider), nameof(InventoryItemProvider.ServerGrantLoadout))] - public class ServerGrantLoadoutPatches - { - public static bool Prefix(ReferenceHub target, RoleTypeId roleTypeId, bool resetInventory = true) - { - if (Main.Instance.Config.StartingInventories == null || !Main.Instance.Config.StartingInventories.TryGetValue(roleTypeId, out RoleInventory startingInventories) || !Player.TryGet(target, out Player player)) - return true; - - if (resetInventory) - player.ClearInventory(); - - player.AddItem(Main.Instance.PlayerHandlers.StartItems(roleTypeId, player)); - - if (startingInventories.Ammo != null && startingInventories.Ammo.Count > 0) - { - IEnumerable startingAmmo = startingInventories.Ammo.Where(s => string.IsNullOrEmpty(s.Group) || s.Group == "none" || (ServerStatic.PermissionsHandler._groups.TryGetValue(s.Group, out UserGroup userGroup) && userGroup == player.Group)); - if (startingAmmo.Any()) - { - player.Ammo.Clear(); - - foreach ((ItemType type, ushort amount, string group) in startingAmmo) - { - player.Ammo.Add(type, amount); - } - } - } - - return false; - } - } -} diff --git a/Common Utilities/Plugin.cs b/Common Utilities/Plugin.cs new file mode 100644 index 0000000..debac66 --- /dev/null +++ b/Common Utilities/Plugin.cs @@ -0,0 +1,226 @@ +// ReSharper disable InconsistentNaming +namespace Common_Utilities; + +#pragma warning disable SA1401 // Fields should be private +using System; +using System.Collections.Generic; + +using ConfigObjects; +using Configs; +using EventHandlers; +using Exiled.API.Enums; +using Exiled.API.Features; +using MEC; +using PlayerRoles; +using Scp914; +using UnityEngine; + +#pragma warning disable SA1211 +using player = Exiled.Events.Handlers.Player; +using scp914 = Exiled.Events.Handlers.Scp914; +using server = Exiled.Events.Handlers.Server; +using warhead = Exiled.Events.Handlers.Warhead; +using Random = System.Random; +#pragma warning restore SA1211 + +public class Plugin : Plugin +{ +#pragma warning disable SA1307 + public static Plugin Instance; + public static Random Random; + public PlayerHandlers playerHandlers; + private ServerHandlers serverHandlers; + private MapHandlers mapHandlers; +#pragma warning restore SA1307 + + public static List Coroutines { get; } = new(); + + public static Dictionary> AfkDict { get; } = new(); + + public override string Name { get; } = "Common Utilities"; + + public override string Author { get; } = "ExMod-Team"; + + public override Version Version { get; } = new(7, 2, 0); + + public override Version RequiredExiledVersion { get; } = new(8, 11, 0); + + public override string Prefix { get; } = "CommonUtilities"; + + public override PluginPriority Priority => PluginPriority.Higher; + + public override void OnEnabled() + { + if (Config.Debug) + DebugConfig(); + + Instance = this; + + Random = new Random(); + + Log.Debug("Instantiating Events.."); + + playerHandlers = new PlayerHandlers(); + serverHandlers = new ServerHandlers(); + mapHandlers = new MapHandlers(); + + Log.Debug("Registering EventHandlers.."); + + player.Hurting += playerHandlers.OnPlayerHurting; + player.Verified += playerHandlers.OnPlayerVerified; + player.Spawned += playerHandlers.OnSpawned; + player.Escaping += playerHandlers.OnEscaping; + + if (Config.HealthOnKill != null) + player.Died += playerHandlers.OnPlayerDied; + + if (Config.StartingInventories != null) + player.ChangingRole += playerHandlers.OnChangingRole; + + if (Config.RadioBatteryDrainMultiplier is not 1) + player.UsingRadioBattery += playerHandlers.OnUsingRadioBattery; + + if (Config.AfkLimit > 0) + { + player.Jumping += playerHandlers.AntiAfkEventHandler; + player.Shooting += playerHandlers.AntiAfkEventHandler; + player.UsingItem += playerHandlers.AntiAfkEventHandler; + player.MakingNoise += playerHandlers.AntiAfkEventHandler; + player.ReloadingWeapon += playerHandlers.AntiAfkEventHandler; + player.ThrownProjectile += playerHandlers.AntiAfkEventHandler; + player.ChangingMoveState += playerHandlers.AntiAfkEventHandler; + player.InteractingDoor += playerHandlers.AntiAfkEventHandler; + player.InteractingElevator += playerHandlers.AntiAfkEventHandler; + } + + server.RoundEnded += serverHandlers.OnRoundEnded; + server.RoundStarted += serverHandlers.OnRoundStarted; + server.RestartingRound += serverHandlers.OnRestartingRound; + server.WaitingForPlayers += serverHandlers.OnWaitingForPlayers; + + scp914.UpgradingPlayer += mapHandlers.OnUpgradingPlayer; + + if (Config.Scp914ItemChances != null) + scp914.UpgradingPickup += mapHandlers.OnUpgradingPickup; + if (Config.Scp914ItemChances != null) + scp914.UpgradingInventoryItem += mapHandlers.OnUpgradingInventoryItem; + + warhead.Starting += serverHandlers.OnWarheadStarting; + warhead.Stopping += serverHandlers.OnWarheadStopping; + + Log.Debug("Registered EventHandlers"); + + base.OnEnabled(); + } + + public override void OnDisabled() + { + player.Hurting -= playerHandlers.OnPlayerHurting; + player.Verified -= playerHandlers.OnPlayerVerified; + player.Spawned -= playerHandlers.OnSpawned; + player.Escaping -= playerHandlers.OnEscaping; + player.Died -= playerHandlers.OnPlayerDied; + player.ChangingRole -= playerHandlers.OnChangingRole; + player.UsingRadioBattery -= playerHandlers.OnUsingRadioBattery; + player.Jumping -= playerHandlers.AntiAfkEventHandler; + player.Shooting -= playerHandlers.AntiAfkEventHandler; + player.UsingItem -= playerHandlers.AntiAfkEventHandler; + player.MakingNoise -= playerHandlers.AntiAfkEventHandler; + player.ReloadingWeapon -= playerHandlers.AntiAfkEventHandler; + player.ThrownProjectile -= playerHandlers.AntiAfkEventHandler; + player.ChangingMoveState -= playerHandlers.AntiAfkEventHandler; + player.InteractingDoor -= playerHandlers.AntiAfkEventHandler; + player.InteractingElevator -= playerHandlers.AntiAfkEventHandler; + + server.RoundEnded -= serverHandlers.OnRoundEnded; + server.RoundStarted -= serverHandlers.OnRoundStarted; + server.RestartingRound -= serverHandlers.OnRestartingRound; + server.WaitingForPlayers -= serverHandlers.OnWaitingForPlayers; + + scp914.UpgradingPlayer -= mapHandlers.OnUpgradingPlayer; + scp914.UpgradingPickup -= mapHandlers.OnUpgradingPickup; + scp914.UpgradingInventoryItem -= mapHandlers.OnUpgradingInventoryItem; + + warhead.Starting -= serverHandlers.OnWarheadStarting; + warhead.Stopping -= serverHandlers.OnWarheadStopping; + + serverHandlers = null; + playerHandlers = null; + mapHandlers = null; + + base.OnDisabled(); + } + + private void DebugConfig() + { + if (Config.StartingInventories != null) + { + Log.Debug($"Starting Inventories: {Config.StartingInventories.Count}"); + foreach (KeyValuePair kvp in Config.StartingInventories) + { + for (int i = 0; i < kvp.Value.UsedSlots; i++) + { + foreach (ItemChance chance in kvp.Value[i]) + { + Log.Debug($"Inventory Config: {kvp.Key} - Slot{i + 1}: {chance.ItemName} ({chance.Chance})"); + } + } + + foreach ((ItemType type, ushort amount, string group) in kvp.Value.Ammo) + { + Log.Debug($"Ammo Config: {kvp.Key} - {type} {amount} ({group})"); + } + } + } + + if (Config.Scp914ItemChances != null) + { + Log.Debug($"{Config.Scp914ItemChances.Count}"); + foreach (KeyValuePair> upgrade in Config.Scp914ItemChances) + { + foreach ((string oldItem, string newItem, double chance, int count) in upgrade.Value) + Log.Debug($"914 Item Config: {upgrade.Key}: {oldItem} -> {newItem}x({count}) - {chance}"); + } + } + + if (Config.Scp914ClassChanges != null) + { + Log.Debug($"{Config.Scp914ClassChanges.Count}"); + foreach (KeyValuePair> upgrade in Config.Scp914ClassChanges) + { + foreach ((string oldRole, string newRole, double chance, bool keepInventory, bool keepHealth) in upgrade.Value) + Log.Debug($"914 Role Config: {upgrade.Key}: {oldRole} -> {newRole} - {chance} keepInventory: {keepInventory} keepHealth: {keepHealth}"); + } + } + + if (Config.Scp914EffectChances != null) + { + Log.Debug($"{Config.Scp914EffectChances.Count}"); + foreach (KeyValuePair> upgrade in Config.Scp914EffectChances) + { + foreach ((EffectType effect, double chance, float duration) in upgrade.Value) + Log.Debug($"914 Effect Config: {upgrade.Key}: {effect} + {duration} - {chance}"); + } + } + + if (Config.Scp914TeleportChances != null) + { + Log.Debug($"{Config.Scp914TeleportChances.Count}"); + foreach (KeyValuePair> upgrade in Config.Scp914TeleportChances) + { + foreach ((RoomType room, List ignoredRooms, Vector3 offset, double chance, float damage, ZoneType zone) in upgrade.Value) + { + Log.Debug($"914 Teleport Config: {upgrade.Key}: {room}/{zone} + {offset} - {chance} [{damage}]"); + Log.Debug("Ignored rooms:"); + if (ignoredRooms != null) + { + foreach (RoomType roomType in ignoredRooms) + { + Log.Debug(roomType); + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Common Utilities/Utils.cs b/Common Utilities/Utils.cs new file mode 100644 index 0000000..410ec3e --- /dev/null +++ b/Common Utilities/Utils.cs @@ -0,0 +1,20 @@ +namespace Common_Utilities; + +using System.Collections.Generic; +using System.Linq; +using ConfigObjects; + +public static class Utils +{ + public static double RollChance(IEnumerable scp914EffectChances) + { + double rolledChance; + + if (Plugin.Instance.Config.AdditiveProbabilities) + rolledChance = Plugin.Random.NextDouble() * scp914EffectChances.Sum(x => x.Chance); + else + rolledChance = Plugin.Random.NextDouble() * 100; + + return rolledChance; + } +} \ No newline at end of file diff --git a/README.md b/README.md index 696161c..71dcc25 100644 --- a/README.md +++ b/README.md @@ -3,153 +3,160 @@ Common Utils is a plugin that serves many common utilites in a day to day server # Main Features ## 914 Features -- Ability to change 914's class upgrading (ex, DClass goes in; scientist comes out.) -- Ability to add custom 914 recipies. +- Ability to change 914's class upgrading (ex, DClass goes in; scientist comes out., supports Custom Roles) +- Ability to add custom 914 recipes (support Custom Items) ## Server Broadcast/Welcoming Features - Ability to completly configure a welcome message. - Ability to completly configure a broadcast message, this can appear every 'x' amount seconds. ## Custom Inventories -- Ability to add custom inventories to all the main classes. - -(These are depricated, if you are using the Exiled 2.0 version of the plugin) +- Ability to add custom inventories to all the main classes # Default config: ```yaml CommonUtilities: -# Whether or not debug messages should be shown. -debug: false -# The SCP Roles able to use V to talk to humans. -scp_speech: -- Scp049 -# Whether or not MTF/CI can 'escape' while disarmed to switch teams. -disarm_switch_teams: true -# Whether or not disarmed people will be prevented from interacting with doors/elevators. -restrictive_disarming: true -# The text displayed at the timed interval specified below. -timed_broadcast: 'This server is running EXILED Common-Utilities, enjoy your stay!' -# The time each timed broadcast will be displayed. -timed_broadcast_duration: 5 -# The delay between each timed broadcast. To disable timed broadcasts, set this to 0 -timed_broadcast_delay: 300 -# The message displayed to the player when they first join the server. Setting this to empty will disable these broadcasts. -join_message: 'Welcome %player%! Please read our rules!' -# The amount of time (in seconds) the join message is displayed. -join_message_duration: 5 -# The amount of time (in seconds) after the round starts, before the facilities auto-nuke will start. -autonuke_time: 1500 -# Wether or not the nuke should be unable to be disabled during the auto-nuke countdown. -autonuke_lock: true -# The message given to all players when the auto-nuke is triggered. A duration of 2 or more will be a text message on-screen. A duration of 1 makes it a cassie announcement. A duration of 0 disables it. -autonuke_broadcast: -# The broadcast content - content: 'The auto nuke has been activated.' - # The broadcast duration - duration: 10 - # The broadcast type - type: Normal - # Indicates whether the broadcast should be shown or not - show: true -# Whether or not to show player's health under their name when you look at them. -player_health_info: true -# Whether or not friendly fire should automatically turn on when a round ends (it will turn itself back off before the next round starts). -friendly_fire_on_round_end: false -# The multiplier applied to radio battery usage. Set to 0 to disable radio battery drain. -radio_battery_drain_multiplier: 1 -# The color to use for lights while the warhead is active. -warhead_color: - r: 1 - g: 0.2 - b: 0.2 - a: 1 -# The maximum time, in seconds, that a player can be AFK before being kicked. Set to -1 to disable AFK system. -afk_limit: 120 -# The roles that are ignored by the AFK system. -afk_ignored_roles: -- Scp079 -- Spectator -- Tutorial -# Whether or not probabilities should be additive (50 + 50 = 100) or not (50 + 50 = 2 seperate 50% chances) -additive_probabilities: false -# The list of starting items for roles. ItemName is the item to give them, and Chance is the percent chance of them spawning with it, and Group allows you to restrict the item to only players with certain RA groups (Leave this as 'none' to allow all players to get the item). You can specify the same item multiple times. -starting_inventories: - ClassD: - slot1: - - item_name: 'KeycardJanitor' - chance: 10 - group: 'none' - - item_name: 'Coin' - chance: 100 - group: 'none' - slot2: - - item_name: 'Flashlight' - chance: 100 - group: 'none' - slot3: [] - slot4: [] - slot5: [] - slot6: [] - slot7: [] - slot8: [] - ammo: - - type: Ammo556x45 - amount: 200 - group: 'none' -# The list of custom 914 recipies. Original is the item being upgraded, New is the item to upgrade to, and Chance is the percent chance of the upgrade happening. You can specify multiple upgrade choices for the same item. -scp914_item_changes: - Rough: - - original: KeycardO5 - new: MicroHID - chance: 50 -# The list of custom 914 recipies for roles. Original is the role to be changed, New is the new role to assign, Chance is the % chance of the upgrade occuring. -scp914_class_changes: - Rough: - - original: ClassD - new: Spectator - chance: 100 - spawn_flags: None -# The list of 914 teleport settings. Note that if you set "zone" to anything other than Unspecified, it will always select a random room from that zone, instead of the room type defined. -scp914_teleport_chances: - Rough: - - zone: Unspecified - room: LczClassDSpawn - offset: - x: 0 - y: 0 - z: 0 - chance: 100 - damage: 0 -# A dictionary of random effects to apply to players when going through 914 on certain settings. -scp914_effect_chances: - Rough: - - effect: Bleeding - chance: 100 - duration: 0 -# Determines if 914 effects are exclusive, meaning only one can be applied each time a player is processed by 914. -scp914_effects_exclusivity: false -# Whether or not SCPs are immune to effects gained from 914. -scps_immune_to914_effects: false -# The frequency (in seconds) between ragdoll cleanups. Set to 0 to disable. -ragdoll_cleanup_delay: 0 -# If ragdoll cleanup should only happen in the Pocket Dimension or not. -ragdoll_cleanup_only_pocket: false -# The frequency (in seconds) between item cleanups. Set to 0 to disable. -item_cleanup_delay: 0 -# If item cleanup should only happen in the Pocket Dimension or not. -item_cleanup_only_pocket: false -# A list of all roles and their damage modifiers. The number here is a multiplier, not a raw damage amount. Thus, setting it to 1 = normal damage, 1.5 = 50% more damage, and 0.5 = 50% less damage. -role_damage_multipliers: - Scp173: 1 -# A list of all Weapons and their damage modifiers. The number here is a multiplier, not a raw damage amount. Thus, setting it to 1 = normal damage, 1.5 = 50% more damage, and 0.5 = 50% less damage. -damage_multipliers: - E11Sr: 1 -# A list of roles and how much health they should be given when they kill someone. -health_on_kill: - Scp173: 0 - Scp939: 10 -# A list of roles and what their default starting health should be. -health_values: - Scp173: 3200 - NtfCaptain: 150 -# If the plugin is enabled or not. -is_enabled: true - + # If the plugin is enabled or not. + is_enabled: true + # Whether or not debug messages should be shown. + debug: false + # Roles that when cuffed in the escape area will change into the target one. + disarmed_escape_switch_role: + NtfCaptain: ChaosMarauder + ChaosMarauder: NtfCaptain + # The text displayed at the timed interval specified below. + timed_broadcast: 'This server is running EXILED Common-Utilities, enjoy your stay!' + # The time each timed broadcast will be displayed. + timed_broadcast_duration: 5 + # The delay between each timed broadcast. To disable timed broadcasts, set this to 0 + timed_broadcast_delay: 300 + # The message displayed to the player when they first join the server. Setting this to empty will disable these broadcasts. + join_message: 'Welcome %player%! Please read our rules!' + # The amount of time (in seconds) the join message is displayed. + join_message_duration: 5 + # The amount of time (in seconds) after the round starts, before the facilities auto-nuke will start. + autonuke_time: 1500 + # Wether or not the nuke should be unable to be disabled during the auto-nuke countdown. + autonuke_lock: true + # The message given to all players when the auto-nuke is triggered. A duration of 2 or more will be a text message on-screen. A duration of 1 makes it a cassie announcement. A duration of 0 disables it. + autonuke_broadcast: + # The broadcast content + content: 'The auto nuke has been activated.' + # The broadcast duration + duration: 10 + # The broadcast type + type: Normal + # Indicates whether the broadcast should be shown or not + show: true + # Whether or not to show player's health under their name when you look at them. + player_health_info: true + # Whether or not friendly fire should automatically turn on when a round ends (it will turn itself back off before the next round starts). + friendly_fire_on_round_end: false + # The multiplier applied to radio battery usage. Set to 0 to disable radio battery drain. + radio_battery_drain_multiplier: 1 + # The color to use for lights while the warhead is active. In the RGBA format using values between 0 and 1. + warhead_color: + r: 1 + g: 0.2 + b: 0.2 + a: 1 + # The maximum time, in seconds, that a player can be AFK before being kicked. Set to -1 to disable AFK system. + afk_limit: 120 + # The roles that are ignored by the AFK system. + afk_ignored_roles: + - Scp079 + - Spectator + - Tutorial + # Whether or not probabilities should be additive (50 + 50 = 100) or not (50 + 50 = 2 seperate 50% chances) + additive_probabilities: false + # The list of starting items for roles. ItemName is the item to give them, and Chance is the percent chance of them spawning with it, and Group allows you to restrict the item to only players with certain RA groups (Leave this as 'none' to allow all players to get the item). You can specify the same item multiple times. + starting_inventories: + ClassD: + slot1: + - item_name: 'KeycardJanitor' + chance: 10 + group: 'none' + - item_name: 'Coin' + chance: 100 + group: 'none' + slot2: + - item_name: 'Flashlight' + chance: 100 + group: 'none' + slot3: [] + slot4: [] + slot5: [] + slot6: [] + slot7: [] + slot8: [] + ammo: + - ammo_type: Ammo556x45 + amount: 200 + group: 'none' + # The list of custom 914 recipies. OriginalItem is the item being upgraded, NewItem is the item to upgrade to, and Chance is the percent chance of the upgrade happening. You can specify multiple upgrade choices for the same item. + scp914_item_chances: + Rough: + - original_item: 'KeycardO5' + new_item: 'MicroHID' + chance: 50 + count: 1 + # The list of custom 914 recipies for roles. Original is the role to be changed, New is the new role to assign, Chance is the % chance of the upgrade occuring. + scp914_class_changes: + Rough: + - original_role: 'ClassD' + new_role: 'Spectator' + chance: 100 + keep_inventory: true + keep_health: true + # The list of 914 teleport settings. Note that if you set "zone" to anything other than Unspecified, it will always select a random room from that zone that isn't in the ignoredRooms list, instead of the room type defined. + scp914_teleport_chances: + Rough: + - zone: Unspecified + ignored_rooms: + room: LczClassDSpawn + offset: + x: 0 + y: 0 + z: 0 + chance: 100 + damage: 0 + - zone: LightContainment + ignored_rooms: + - Lcz173 + room: Unknown + offset: + x: 0 + y: 0 + z: 0 + chance: 0 + damage: 0 + # A dictionary of random effects to apply to players when going through 914 on certain settings. + scp914_effect_chances: + Rough: + - effect: Bleeding + chance: 100 + duration: 0 + # Determines if 914 effects are exclusive, meaning only one can be applied each time a player is processed by 914. + scp914_effects_exclusivity: false + # Whether or not SCPs are immune to effects gained from 914. + scps_immune_to914_effects: false + # The frequency (in seconds) between ragdoll cleanups. Set to 0 to disable. + ragdoll_cleanup_delay: 0 + # If ragdoll cleanup should only happen in the Pocket Dimension or not. + ragdoll_cleanup_only_pocket: false + # The frequency (in seconds) between item cleanups. Set to 0 to disable. + item_cleanup_delay: 0 + # If item cleanup should only happen in the Pocket Dimension or not. + item_cleanup_only_pocket: false + # A list of all roles and their damage modifiers. The number here is a multiplier, not a raw damage amount. Thus, setting it to 1 = normal damage, 1.5 = 50% more damage, and 0.5 = 50% less damage. + role_damage_multipliers: + Scp173: 1 + # A list of all Weapons and their damage modifiers. The number here is a multiplier, not a raw damage amount. Thus, setting it to 1 = normal damage, 1.5 = 50% more damage, and 0.5 = 50% less damage. + damage_multipliers: + E11Sr: 1 + # A list of roles and how much health they should be given when they kill someone. + health_on_kill: + Scp173: 0 + Scp939: 10 + # A list of roles and what their default starting health should be. + health_values: + Scp173: 3200 + NtfCaptain: 150 ```