├── .github
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── .gitignore
├── Common Utilities.sln
├── Common Utilities.sln.DotSettings.user
├── Common Utilities
├── API.cs
├── Common Utilities.csproj
├── Config.cs
├── ConfigObjects
│ ├── ItemChance.cs
│ ├── ItemUpgradeChance.cs
│ ├── PlayerUpgradeChance.cs
│ ├── Scp914EffectChance.cs
│ ├── Scp914TeleportChance.cs
│ └── StartingAmmo.cs
├── Configs
│ └── RoleInventory.cs
├── EventHandlers
│ ├── MapHandlers.cs
│ ├── PlayerHandlers.cs
│ └── ServerHandlers.cs
├── Main.cs
└── Patches
│ └── ServerGrantLoadoutPatches.cs
├── Directory.Build.props
├── Exiled.ruleset
├── Plugin.props
├── README.md
├── Updating.md
└── stylecop.json
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: needs testing
6 | assignees: joker-119
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Server logs**
24 | 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
25 |
26 | **Copy of current CU config section**
27 |
28 |
29 | **EXILED Version ("latest" is not a version):**
30 |
31 |
32 | **Results of `show plugins` command in console:**
33 |
34 |
35 | **Additional context**
36 | Add any other context about the problem here.
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /Common Utilities/bin/*
2 | /Common Utilities/obj/*
3 | /Common Utilities.sln.DotSettings.user
4 | /packages
5 | /packages/*
6 | /.idea/*
7 | /.vs/*
8 | /bin/*
9 | /obj/*
10 |
--------------------------------------------------------------------------------
/Common Utilities.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common Utilities", "Common Utilities\Common Utilities.csproj", "{23AB2900-9C8B-4F23-A3B6-D92EAEE2F88C}"
4 | EndProject
5 | Global
6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
7 | Debug|Any CPU = Debug|Any CPU
8 | Release|Any CPU = Release|Any CPU
9 | EndGlobalSection
10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
11 | {23AB2900-9C8B-4F23-A3B6-D92EAEE2F88C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
12 | {23AB2900-9C8B-4F23-A3B6-D92EAEE2F88C}.Debug|Any CPU.Build.0 = Debug|Any CPU
13 | {23AB2900-9C8B-4F23-A3B6-D92EAEE2F88C}.Release|Any CPU.ActiveCfg = Release|Any CPU
14 | {23AB2900-9C8B-4F23-A3B6-D92EAEE2F88C}.Release|Any CPU.Build.0 = Release|Any CPU
15 | EndGlobalSection
16 | EndGlobal
17 |
--------------------------------------------------------------------------------
/Common Utilities.sln.DotSettings.user:
--------------------------------------------------------------------------------
1 |
2 | True
3 | True
4 | True
5 | True
6 | True
7 | True
8 | True
9 | True
10 | True
11 | True
12 | True
13 | True
14 | True
15 | True
16 | True
17 | True
18 | True
19 | True
20 | True
21 | True
22 | True
23 | True
24 | True
25 | True
26 | True
27 | True
28 | True
29 | True
30 | True
31 | True
32 | True
33 | True
34 | True
35 | True
36 | True
37 | True
38 | True
39 | True
40 | True
41 | True
42 | True
43 | True
44 | True
45 | True
46 | True
47 | True
48 | True
49 | True
50 | True
51 | True
52 | True
53 | True
54 | True
55 | True
56 | True
57 | True
58 | True
59 | True
60 | True
61 | True
62 | True
63 | True
64 | True
65 | True
66 | True
67 | True
68 | True
69 | True
70 | True
71 | True
72 | True
73 | True
74 | True
75 | True
76 | True
77 | True
78 | True
79 | True
80 | True
81 | True
82 | True
83 | True
84 | True
85 | True
86 | True
87 | True
88 | True
89 | True
90 | True
91 | True
92 | True
93 | True
94 | True
95 | True
96 | True
97 | True
98 | True
99 | True
100 | True
101 | True
102 | True
103 | True
104 | True
105 | True
106 | True
107 | True
108 | True
109 | True
110 | True
111 | True
112 | True
113 | True
114 | True
115 | True
116 | True
117 | True
118 | True
119 | True
120 | True
121 | True
122 | True
123 | True
124 | True
125 | True
126 | True
127 | True
128 | True
129 | True
130 | True
131 | True
132 | True
133 | True
134 | True
135 | True
136 | True
137 | True
138 | True
139 | True
140 | True
141 | True
142 | True
143 | True
144 | True
145 | True
146 | True
147 | True
148 | True
149 | True
150 | True
151 | True
152 | True
153 | True
154 | True
155 | True
156 | True
157 | True
158 | True
159 | True
160 | <AssemblyExplorer>
161 | <Assembly Path="\home\Joker\Projects\SCP Plugins\References\Assembly-CSharp-Publicized.dll" />
162 | <Assembly Path="\home\Joker\Downloads\SUPPLYDROP.dll" />
163 | <Assembly Path="\home\Joker\Downloads\UNLIMITEDRADIOBATTERY.dll" />
164 | <Assembly Path="\home\Joker\Downloads\SCP096INFO.dll" />
165 | <Assembly Path="\home\Joker\Downloads\PEANUTFUCKINGEXPLODES.dll" />
166 | <Assembly Path="\home\Joker\Downloads\012TELEPORT.dll" />
167 | <Assembly Path="C:\Users\Jacob\Downloads\Common-Utils\packages\EXILED.6.0.0-beta.1\lib\net472\Exiled.Events.dll" />
168 | <Assembly Path="C:\Users\Jacob\Downloads\Common-Utils\packages\EXILED.6.0.0-beta.1\lib\net472\Exiled.API.dll" />
169 | </AssemblyExplorer>
170 | C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe
171 | 1114112
172 |
173 |
--------------------------------------------------------------------------------
/Common Utilities/API.cs:
--------------------------------------------------------------------------------
1 | namespace Common_Utilities
2 | {
3 | using System.Collections.Generic;
4 |
5 | using Exiled.API.Features;
6 | using PlayerRoles;
7 |
8 | public static class API
9 | {
10 | public static List GetStartItems(RoleTypeId role) => Main.Instance.PlayerHandlers.StartItems(role);
11 |
12 | public static List GetStartItems(RoleTypeId role, Player player) => Main.Instance.PlayerHandlers.StartItems(role, player);
13 |
14 | public static float GetHealthOnKill(RoleTypeId role) => Main.Instance.Config.HealthOnKill?.ContainsKey(role) ?? false ? Main.Instance.Config.HealthOnKill[role] : 0f;
15 | }
16 | }
--------------------------------------------------------------------------------
/Common Utilities/Common Utilities.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Library
5 | $(ProjectName)
6 | true
7 | false
8 | 7.1.0
9 | default
10 | 7.0.6
11 | 7.0.6
12 | disable
13 | Always
14 | Exiled-Team
15 |
16 |
17 |
18 |
19 |
20 |
21 | <_Parameter1>$(ProjectName)
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Common Utilities/Config.cs:
--------------------------------------------------------------------------------
1 | namespace Common_Utilities
2 | {
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 |
6 | using ConfigObjects;
7 | using Configs;
8 | using Exiled.API.Enums;
9 | using Exiled.API.Features;
10 | using Exiled.API.Interfaces;
11 | using PlayerRoles;
12 | using Scp914;
13 | using UnityEngine;
14 |
15 | public class Config : IConfig
16 | {
17 | [Description("Whether or not debug messages should be shown.")]
18 | public bool Debug { get; set; } = false;
19 |
20 | [Description("Whether or not MTF/CI can 'escape' while disarmed to switch teams.")]
21 | public bool DisarmSwitchTeams { get; set; } = true;
22 |
23 | [Description("Whether or not disarmed people will be prevented from interacting with doors/elevators.")]
24 | public bool RestrictiveDisarming { get; set; } = true;
25 |
26 | [Description("The text displayed at the timed interval specified below.")]
27 | public string TimedBroadcast { get; set; } = "This server is running EXILED Common-Utilities, enjoy your stay!";
28 |
29 | [Description("The time each timed broadcast will be displayed.")]
30 | public ushort TimedBroadcastDuration { get; set; } = 5;
31 |
32 | [Description("The delay between each timed broadcast. To disable timed broadcasts, set this to 0")]
33 | public float TimedBroadcastDelay { get; set; } = 300f;
34 |
35 | [Description("The message displayed to the player when they first join the server. Setting this to empty will disable these broadcasts.")]
36 | public string JoinMessage { get; set; } = "Welcome %player%! Please read our rules!";
37 |
38 | [Description("The amount of time (in seconds) the join message is displayed.")]
39 | public ushort JoinMessageDuration { get; set; } = 5;
40 |
41 | [Description("The amount of time (in seconds) after the round starts, before the facilities auto-nuke will start.")]
42 | public float AutonukeTime { get; set; } = 1500f;
43 |
44 | [Description("Wether or not the nuke should be unable to be disabled during the auto-nuke countdown.")]
45 | public bool AutonukeLock { get; set; } = true;
46 |
47 | [Description("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.")]
48 | public Broadcast AutonukeBroadcast { get; set; } = new()
49 | {
50 | Content = "The auto nuke has been activated.",
51 | Duration = 10,
52 | Show = true,
53 | Type = global::Broadcast.BroadcastFlags.Normal,
54 | };
55 |
56 | [Description("Whether or not to show player's health under their name when you look at them.")]
57 | public bool PlayerHealthInfo { get; set; } = true;
58 |
59 | [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).")]
60 | public bool FriendlyFireOnRoundEnd { get; set; } = false;
61 |
62 | [Description("The multiplier applied to radio battery usage. Set to 0 to disable radio battery drain.")]
63 | public float RadioBatteryDrainMultiplier { get; set; } = 1f;
64 |
65 | [Description("The color to use for lights while the warhead is active.")]
66 | public Color WarheadColor { get; set; } = new(1f, 0.2f, 0.2f);
67 |
68 | [Description("The maximum time, in seconds, that a player can be AFK before being kicked. Set to -1 to disable AFK system.")]
69 | public int AfkLimit { get; set; } = 120;
70 |
71 | [Description("The roles that are ignored by the AFK system.")]
72 | public List AfkIgnoredRoles { get; set; } = new()
73 | {
74 | RoleTypeId.Scp079,
75 | RoleTypeId.Spectator,
76 | RoleTypeId.Tutorial,
77 | };
78 |
79 | [Description("Whether or not probabilities should be additive (50 + 50 = 100) or not (50 + 50 = 2 seperate 50% chances)")]
80 | public bool AdditiveProbabilities { get; set; } = false;
81 |
82 | [Description(
83 | "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.")]
84 | public Dictionary StartingInventories { get; set; } = new()
85 | {
86 | {
87 | RoleTypeId.ClassD, new RoleInventory
88 | {
89 | Slot1 = new List
90 | {
91 | new()
92 | {
93 | ItemName = ItemType.KeycardJanitor.ToString(),
94 | Chance = 10,
95 | Group = "none",
96 | },
97 | new()
98 | {
99 | ItemName = ItemType.Coin.ToString(),
100 | Chance = 100,
101 | Group = "none",
102 | },
103 | },
104 | Slot2 = new List
105 | {
106 | new()
107 | {
108 | ItemName = ItemType.Flashlight.ToString(),
109 | Chance = 100,
110 | Group = "none",
111 | },
112 | },
113 | Ammo = new List
114 | {
115 | new()
116 | {
117 | Type = ItemType.Ammo556x45,
118 | Amount = 200,
119 | Group = "none",
120 | },
121 | },
122 | }
123 | },
124 | };
125 |
126 | [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.")]
127 | public Dictionary> Scp914ItemChanges { get; set; } = new()
128 | {
129 | {
130 | Scp914KnobSetting.Rough, new List
131 | {
132 | {
133 | new()
134 | {
135 | Original = ItemType.KeycardO5,
136 | New = ItemType.MicroHID,
137 | Chance = 50,
138 | }
139 | },
140 | }
141 | },
142 | };
143 |
144 | [Description("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.")]
145 | public Dictionary> Scp914ClassChanges { get; set; } = new()
146 | {
147 | {
148 | Scp914KnobSetting.Rough, new List
149 | {
150 | {
151 | new()
152 | {
153 | Original = RoleTypeId.ClassD,
154 | New = RoleTypeId.Spectator.ToString(),
155 | Chance = 100,
156 | }
157 | },
158 | }
159 | },
160 | };
161 |
162 | [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.")]
163 | public Dictionary> Scp914TeleportChances { get; set; } = new()
164 | {
165 | {
166 | Scp914KnobSetting.Rough, new List
167 | {
168 | new()
169 | {
170 | Room = RoomType.LczClassDSpawn,
171 | Chance = 100,
172 | },
173 | new()
174 | {
175 | Zone = ZoneType.LightContainment,
176 | IgnoredRooms = new()
177 | {
178 | RoomType.Lcz173,
179 | },
180 | },
181 | }
182 | },
183 | };
184 |
185 | [Description("A dictionary of random effects to apply to players when going through 914 on certain settings.")]
186 | public Dictionary> Scp914EffectChances { get; set; } = new()
187 | {
188 | {
189 | Scp914KnobSetting.Rough, new List
190 | {
191 | new()
192 | {
193 | Effect = EffectType.Bleeding,
194 | Chance = 100,
195 | },
196 | }
197 | },
198 | };
199 |
200 | [Description("Determines if 914 effects are exclusive, meaning only one can be applied each time a player is processed by 914.")]
201 | public bool Scp914EffectsExclusivity { get; set; } = false;
202 |
203 | [Description("Whether or not SCPs are immune to effects gained from 914.")]
204 | public bool ScpsImmuneTo914Effects { get; set; } = false;
205 |
206 | [Description("The frequency (in seconds) between ragdoll cleanups. Set to 0 to disable.")]
207 | public float RagdollCleanupDelay { get; set; } = 0f;
208 |
209 | [Description("If ragdoll cleanup should only happen in the Pocket Dimension or not.")]
210 | public bool RagdollCleanupOnlyPocket { get; set; } = false;
211 |
212 | [Description("The frequency (in seconds) between item cleanups. Set to 0 to disable.")]
213 | public float ItemCleanupDelay { get; set; } = 0f;
214 |
215 | [Description("If item cleanup should only happen in the Pocket Dimension or not.")]
216 | public bool ItemCleanupOnlyPocket { get; set; } = false;
217 |
218 | [Description("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.")]
219 | public Dictionary RoleDamageMultipliers { get; set; } = new()
220 | {
221 | {
222 | RoleTypeId.Scp173, 1.0f
223 | },
224 | };
225 |
226 | [Description("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.")]
227 | public Dictionary DamageMultipliers { get; set; } = new()
228 | {
229 | {
230 | DamageType.E11Sr, 1.0f
231 | },
232 | };
233 |
234 | [Description("A list of roles and how much health they should be given when they kill someone.")]
235 | public Dictionary HealthOnKill { get; set; } = new()
236 | {
237 | {
238 | RoleTypeId.Scp173, 0
239 | },
240 | {
241 | RoleTypeId.Scp939, 10
242 | },
243 | };
244 |
245 | [Description("A list of roles and what their default starting health should be.")]
246 | public Dictionary HealthValues { get; set; } = new()
247 | {
248 | {
249 | RoleTypeId.Scp173, 3200
250 | },
251 | {
252 | RoleTypeId.NtfCaptain, 150
253 | },
254 | };
255 |
256 | [Description("If the plugin is enabled or not.")]
257 | public bool IsEnabled { get; set; } = true;
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/Common Utilities/ConfigObjects/ItemChance.cs:
--------------------------------------------------------------------------------
1 | namespace Common_Utilities.ConfigObjects;
2 |
3 | public class ItemChance
4 | {
5 | public string ItemName { get; set; } = ItemType.None.ToString();
6 |
7 | public double Chance { get; set; }
8 |
9 | public string Group { get; set; } = "none";
10 |
11 | public void Deconstruct(out string name, out double i, out string groupKey)
12 | {
13 | name = ItemName;
14 | i = Chance;
15 | groupKey = Group;
16 | }
17 | }
--------------------------------------------------------------------------------
/Common Utilities/ConfigObjects/ItemUpgradeChance.cs:
--------------------------------------------------------------------------------
1 | namespace Common_Utilities.ConfigObjects
2 | {
3 | public class ItemUpgradeChance
4 | {
5 | public ItemType Original { get; set; }
6 |
7 | public ItemType New { get; set; }
8 |
9 | public double Chance { get; set; }
10 |
11 | public int Count { get; set; } = 1;
12 |
13 | public void Deconstruct(out ItemType itemType, out ItemType itemType1, out double i, out int count)
14 | {
15 | itemType = Original;
16 | itemType1 = New;
17 | i = Chance;
18 | count = Count;
19 | }
20 | }
21 | }
--------------------------------------------------------------------------------
/Common Utilities/ConfigObjects/PlayerUpgradeChance.cs:
--------------------------------------------------------------------------------
1 | namespace Common_Utilities.ConfigObjects
2 | {
3 | using PlayerRoles;
4 |
5 | public class PlayerUpgradeChance
6 | {
7 | public RoleTypeId Original { get; set; }
8 |
9 | public string New { get; set; } = RoleTypeId.Spectator.ToString();
10 |
11 | public double Chance { get; set; }
12 |
13 | public bool KeepInventory { get; set; } = true;
14 |
15 | public bool KeepHealth { get; set; } = true;
16 |
17 | public void Deconstruct(out RoleTypeId old, out string newRole, out double i, out bool keepInventory, out bool keepHealth)
18 | {
19 | old = Original;
20 | newRole = New;
21 | i = Chance;
22 | keepInventory = KeepInventory;
23 | keepHealth = KeepHealth;
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/Common Utilities/ConfigObjects/Scp914EffectChance.cs:
--------------------------------------------------------------------------------
1 | namespace Common_Utilities.ConfigObjects
2 | {
3 | using Exiled.API.Enums;
4 |
5 | public class Scp914EffectChance
6 | {
7 | public EffectType Effect { get; set; }
8 |
9 | public double Chance { get; set; }
10 |
11 | public float Duration { get; set; }
12 |
13 | public void Deconstruct(out EffectType effect, out double chance, out float duration)
14 | {
15 | effect = Effect;
16 | chance = Chance;
17 | duration = Duration;
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/Common Utilities/ConfigObjects/Scp914TeleportChance.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Common_Utilities.ConfigObjects
4 | {
5 | using Exiled.API.Enums;
6 | using UnityEngine;
7 |
8 | public class Scp914TeleportChance
9 | {
10 | public ZoneType Zone { get; set; } = ZoneType.Unspecified;
11 |
12 | public List IgnoredRooms { get; set; }
13 |
14 | public RoomType Room { get; set; }
15 |
16 | public Vector3 Offset { get; set; } = Vector3.zero;
17 |
18 | public double Chance { get; set; }
19 |
20 | public float Damage { get; set; } = 0f;
21 |
22 | public void Deconstruct(out RoomType room, out List ignoredRooms, out Vector3 offset, out double chance, out float damage, out ZoneType zone)
23 | {
24 | room = Room;
25 | ignoredRooms = IgnoredRooms;
26 | offset = Offset;
27 | chance = Chance;
28 | damage = Damage;
29 | zone = Zone;
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/Common Utilities/ConfigObjects/StartingAmmo.cs:
--------------------------------------------------------------------------------
1 | namespace Common_Utilities.ConfigObjects
2 | {
3 | public class StartingAmmo
4 | {
5 | public ItemType Type { get; set; }
6 |
7 | public ushort Amount { get; set; }
8 |
9 | public string Group { get; set; } = "none";
10 |
11 | public void Deconstruct(out ItemType type, out ushort limit, out string group)
12 | {
13 | type = Type;
14 | limit = Amount;
15 | group = Group;
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Common Utilities/Configs/RoleInventory.cs:
--------------------------------------------------------------------------------
1 | namespace Common_Utilities.Configs
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | using ConfigObjects;
7 | using YamlDotNet.Serialization;
8 |
9 | public class RoleInventory
10 | {
11 | [YamlIgnore]
12 | public int UsedSlots
13 | {
14 | get
15 | {
16 | int i = 0;
17 | if (Slot1 != null && !Slot1.IsEmpty())
18 | i++;
19 | if (Slot2 != null && !Slot2.IsEmpty())
20 | i++;
21 | if (Slot3 != null && !Slot3.IsEmpty())
22 | i++;
23 | if (Slot4 != null && !Slot4.IsEmpty())
24 | i++;
25 | if (Slot5 != null && !Slot5.IsEmpty())
26 | i++;
27 | if (Slot6 != null && !Slot6.IsEmpty())
28 | i++;
29 | if (Slot7 != null && !Slot7.IsEmpty())
30 | i++;
31 | if (Slot8 != null && !Slot8.IsEmpty())
32 | i++;
33 | return i;
34 | }
35 | }
36 |
37 | public List Slot1 { get; set; } = new();
38 |
39 | public List Slot2 { get; set; } = new();
40 |
41 | public List Slot3 { get; set; } = new();
42 |
43 | public List Slot4 { get; set; } = new();
44 |
45 | public List Slot5 { get; set; } = new();
46 |
47 | public List Slot6 { get; set; } = new();
48 |
49 | public List Slot7 { get; set; } = new();
50 |
51 | public List Slot8 { get; set; } = new();
52 |
53 | public List Ammo { get; set; } = new();
54 |
55 | public IEnumerable this[int i] => i switch
56 | {
57 | 0 => Slot1,
58 | 1 => Slot2,
59 | 2 => Slot3,
60 | 3 => Slot4,
61 | 4 => Slot5,
62 | 5 => Slot6,
63 | 6 => Slot7,
64 | 7 => Slot8,
65 | _ => throw new ArgumentOutOfRangeException(),
66 | };
67 | }
68 | }
--------------------------------------------------------------------------------
/Common Utilities/EventHandlers/MapHandlers.cs:
--------------------------------------------------------------------------------
1 | using Exiled.API.Extensions;
2 | using Exiled.API.Features.Items;
3 |
4 | namespace Common_Utilities.EventHandlers
5 | {
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 |
10 | using Common_Utilities.ConfigObjects;
11 | using Exiled.API.Enums;
12 | using Exiled.API.Features;
13 | using Exiled.API.Features.Pickups;
14 | using Exiled.CustomRoles.API.Features;
15 | using Exiled.Events.EventArgs.Scp914;
16 | using MEC;
17 | using PlayerRoles;
18 | using UnityEngine;
19 |
20 | public class MapHandlers
21 | {
22 | private readonly Main plugin;
23 |
24 | public MapHandlers(Main plugin) => this.plugin = plugin;
25 |
26 | public void OnScp914UpgradingItem(UpgradingPickupEventArgs ev)
27 | {
28 | if (plugin.Config.Scp914ItemChanges.ContainsKey(ev.KnobSetting))
29 | {
30 | IEnumerable itemUpgradeChance = plugin.Config.Scp914ItemChanges[ev.KnobSetting].Where(x => x.Original == ev.Pickup.Type);
31 |
32 | foreach ((ItemType sourceItem, ItemType destinationItem, double chance, int count) in itemUpgradeChance)
33 | {
34 | double r;
35 | if (plugin.Config.AdditiveProbabilities)
36 | r = plugin.Rng.NextDouble() * itemUpgradeChance.Sum(x => x.Chance);
37 | else
38 | r = plugin.Rng.NextDouble() * 100;
39 |
40 | Log.Debug($"{nameof(OnScp914UpgradingItem)}: SCP-914 is trying to upgrade a {ev.Pickup.Type}. {sourceItem} -> {destinationItem} ({chance}). Should process: {r <= chance} ({r})");
41 | if (r <= chance)
42 | {
43 | UpgradeItem(ev.Pickup, destinationItem, ev.OutputPosition, count);
44 | ev.IsAllowed = false;
45 | break;
46 | }
47 |
48 | r -= chance;
49 | }
50 | }
51 | }
52 |
53 | public void OnScp914UpgradingInventoryItem(UpgradingInventoryItemEventArgs ev)
54 | {
55 | if (plugin.Config.Scp914ItemChanges.ContainsKey(ev.KnobSetting))
56 | {
57 | IEnumerable itemUpgradeChance = plugin.Config.Scp914ItemChanges[ev.KnobSetting].Where(x => x.Original == ev.Item.Type);
58 |
59 | foreach ((ItemType sourceItem, ItemType destinationItem, double chance, int count) in itemUpgradeChance)
60 | {
61 | double r;
62 | if (plugin.Config.AdditiveProbabilities)
63 | r = plugin.Rng.NextDouble() * itemUpgradeChance.Sum(x => x.Chance);
64 | else
65 | r = plugin.Rng.NextDouble() * 100;
66 |
67 | Log.Debug($"{nameof(OnScp914UpgradingInventoryItem)}: {ev.Player.Nickname} is attempting to upgrade hit {ev.Item.Type}. {sourceItem} -> {destinationItem} ({chance}). Should process: {r <= chance} ({r})");
68 | if (r <= chance)
69 | {
70 | ev.Player.RemoveItem(ev.Item);
71 | if (destinationItem is not ItemType.None)
72 | {
73 | for (int i = 0; i < count; i++)
74 | {
75 | if (!ev.Player.IsInventoryFull)
76 | ev.Player.AddItem(destinationItem);
77 | else
78 | Pickup.CreateAndSpawn(destinationItem, Scp914.OutputPosition, ev.Player.Rotation, ev.Player);
79 | }
80 | }
81 |
82 | break;
83 | }
84 |
85 | r -= chance;
86 | }
87 | }
88 | }
89 |
90 | public void OnScp914UpgradingPlayer(UpgradingPlayerEventArgs ev)
91 | {
92 | if (plugin.Config.Scp914ClassChanges != null && plugin.Config.Scp914ClassChanges.TryGetValue(ev.KnobSetting, out var change))
93 | {
94 | IEnumerable playerUpgradeChance = change.Where(x => x.Original == ev.Player.Role);
95 |
96 | foreach ((RoleTypeId sourceRole, string destinationRole, double chance, bool keepInventory, bool keepHealth) in playerUpgradeChance)
97 | {
98 | double r;
99 | if (plugin.Config.AdditiveProbabilities)
100 | r = plugin.Rng.NextDouble() * playerUpgradeChance.Sum(x => x.Chance);
101 | else
102 | r = plugin.Rng.NextDouble() * 100;
103 |
104 | 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})");
105 | if (r <= chance)
106 | {
107 | float originalHealth = ev.Player.Health;
108 | var originalItems = ev.Player.Items;
109 | var originalAmmo = ev.Player.Ammo;
110 |
111 | if (Enum.TryParse(destinationRole, true, out RoleTypeId roleType))
112 | {
113 | ev.Player.Role.Set(roleType, SpawnReason.Respawn, RoleSpawnFlags.None);
114 | }
115 | else if (CustomRole.TryGet(destinationRole, out CustomRole customRole))
116 | {
117 | if (customRole is not null)
118 | {
119 | customRole.AddRole(ev.Player);
120 | Timing.CallDelayed(0.5f, () => ev.Player.Teleport(ev.OutputPosition));
121 | }
122 | }
123 |
124 | if (keepHealth)
125 | {
126 | ev.Player.Health = originalHealth;
127 | }
128 |
129 | if (keepInventory)
130 | {
131 | foreach (var item in originalItems)
132 | {
133 | ev.Player.AddItem(item);
134 | }
135 |
136 | foreach (var kvp in originalAmmo)
137 | {
138 | ev.Player.SetAmmo(kvp.Key.GetAmmoType(), kvp.Value);
139 | }
140 | }
141 |
142 | ev.Player.Position = ev.OutputPosition;
143 | break;
144 | }
145 |
146 | r -= chance;
147 | }
148 | }
149 |
150 | if (plugin.Config.Scp914EffectChances != null && plugin.Config.Scp914EffectChances.ContainsKey(ev.KnobSetting) && (ev.Player.Role.Side != Side.Scp || !plugin.Config.ScpsImmuneTo914Effects))
151 | {
152 | IEnumerable scp914EffectChances = plugin.Config.Scp914EffectChances[ev.KnobSetting];
153 |
154 | foreach ((EffectType effect, double chance, float duration) in scp914EffectChances)
155 | {
156 | double r;
157 | if (plugin.Config.AdditiveProbabilities)
158 | r = plugin.Rng.NextDouble() * scp914EffectChances.Sum(x => x.Chance);
159 | else
160 | r = plugin.Rng.NextDouble() * 100;
161 |
162 | Log.Debug($"{nameof(OnScp914UpgradingPlayer)}: {ev.Player.Nickname} is trying to gain an effect. {effect} ({chance}). Should be added: {r <= chance} ({r})");
163 | if (r <= chance)
164 | {
165 | ev.Player.EnableEffect(effect, duration);
166 | if (plugin.Config.Scp914EffectsExclusivity)
167 | break;
168 | }
169 |
170 | r -= chance;
171 | }
172 | }
173 |
174 | if (plugin.Config.Scp914TeleportChances != null && plugin.Config.Scp914TeleportChances.ContainsKey(ev.KnobSetting))
175 | {
176 | IEnumerable scp914TeleportChances = plugin.Config.Scp914TeleportChances[ev.KnobSetting];
177 |
178 | foreach ((RoomType roomType, List ignoredRooms, Vector3 offset, double chance, float damage, ZoneType zone) in plugin.Config.Scp914TeleportChances[ev.KnobSetting])
179 | {
180 | double r;
181 | if (plugin.Config.AdditiveProbabilities)
182 | r = plugin.Rng.NextDouble() * scp914TeleportChances.Sum(x => x.Chance);
183 | else
184 | r = plugin.Rng.NextDouble() * 100;
185 |
186 | Log.Debug($"{nameof(OnScp914UpgradingPlayer)}: {ev.Player.Nickname} is trying to be teleported by 914. {roomType} + {offset} ({chance}). Should be teleported: {r <= chance} ({r})");
187 | if (r <= chance)
188 | {
189 | if (zone != ZoneType.Unspecified)
190 | {
191 | ev.OutputPosition = Room.List.Where(x => x.Zone == zone && !ignoredRooms.Contains(x.Type)).GetRandomValue().Position + ((Vector3.up * 1.5f) + offset);
192 | if (damage > 0f)
193 | {
194 | float amount = ev.Player.MaxHealth * damage;
195 | if (damage > 1f)
196 | amount = damage;
197 |
198 | Log.Debug($"{nameof(OnScp914UpgradingPlayer)}: {ev.Player.Nickname} is being damaged for {amount}. -- {ev.Player.Health} * {damage}");
199 | ev.Player.Hurt(amount, "SCP-914 Teleport", "SCP-914");
200 | }
201 | }
202 | else
203 | {
204 | ev.OutputPosition = Room.Get(roomType).Position + (Vector3.up * 1.5f) + offset;
205 | if (damage > 0f)
206 | {
207 | float amount = ev.Player.MaxHealth * damage;
208 | if (damage > 1f)
209 | amount = damage;
210 |
211 | Log.Debug(
212 | $"{nameof(OnScp914UpgradingPlayer)}: {ev.Player.Nickname} is being damaged for {amount}. -- {ev.Player.Health} * {damage}");
213 | ev.Player.Hurt(amount, "SCP-914 Teleport", "SCP-914");
214 | }
215 | }
216 |
217 | break;
218 | }
219 |
220 | r -= chance;
221 | }
222 | }
223 | }
224 |
225 | internal void UpgradeItem(Pickup oldItem, ItemType newItem, Vector3 pos, int count)
226 | {
227 | Quaternion quaternion = oldItem.Rotation;
228 | Player previousOwner = oldItem.PreviousOwner;
229 | oldItem.Destroy();
230 | if (newItem is not ItemType.None)
231 | {
232 | for (int i = 0; i < count; i++)
233 | {
234 | Pickup.CreateAndSpawn(newItem, pos, quaternion, previousOwner);
235 | }
236 | }
237 | }
238 | }
239 | }
--------------------------------------------------------------------------------
/Common Utilities/EventHandlers/PlayerHandlers.cs:
--------------------------------------------------------------------------------
1 | namespace Common_Utilities.EventHandlers
2 | {
3 | #pragma warning disable IDE0018
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 |
8 | using Common_Utilities.ConfigObjects;
9 | using Exiled.API.Enums;
10 | using Exiled.API.Features;
11 | using Exiled.API.Features.Roles;
12 | using Exiled.CustomItems.API.Features;
13 | using Exiled.Events.EventArgs.Interfaces;
14 | using Exiled.Events.EventArgs.Player;
15 | using PlayerRoles;
16 | using UnityEngine;
17 |
18 | using Player = Exiled.API.Features.Player;
19 |
20 | public class PlayerHandlers
21 | {
22 | private readonly Main plugin;
23 |
24 | public PlayerHandlers(Main plugin) => this.plugin = plugin;
25 |
26 | public void OnPlayerVerified(VerifiedEventArgs ev)
27 | {
28 | string message = FormatJoinMessage(ev.Player);
29 | if (!string.IsNullOrEmpty(message))
30 | ev.Player.Broadcast(plugin.Config.JoinMessageDuration, message);
31 | }
32 |
33 | public void OnChangingRole(ChangingRoleEventArgs ev)
34 | {
35 | if (ev.Player == null)
36 | {
37 | Log.Warn($"{nameof(OnChangingRole)}: Triggering player is null.");
38 | return;
39 | }
40 |
41 | if (plugin.Config.StartingInventories.ContainsKey(ev.NewRole) && !ev.ShouldPreserveInventory)
42 | {
43 | if (ev.Items == null)
44 | {
45 | Log.Warn("items is null");
46 | return;
47 | }
48 |
49 | ev.Items.Clear();
50 | ev.Items.AddRange(StartItems(ev.NewRole, ev.Player));
51 |
52 | if (plugin.Config.StartingInventories[ev.NewRole].Ammo != null && plugin.Config.StartingInventories[ev.NewRole].Ammo.Count > 0)
53 | {
54 | 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)))
55 | {
56 | ev.Ammo.Clear();
57 | foreach ((ItemType type, ushort amount, string group) in plugin.Config.StartingInventories[ev.NewRole].Ammo)
58 | {
59 | if (string.IsNullOrEmpty(group) || group == "none" || (ServerStatic.PermissionsHandler._groups.TryGetValue(group, out UserGroup userGroup) && userGroup == ev.Player.Group))
60 | {
61 | ev.Ammo.Add(type, amount);
62 | }
63 | }
64 | }
65 | }
66 | }
67 | }
68 |
69 | public void OnSpawned(SpawnedEventArgs ev)
70 | {
71 | if (ev.Player == null)
72 | {
73 | Log.Warn($"{nameof(OnSpawned)}: Triggering player is null.");
74 | return;
75 | }
76 |
77 | RoleTypeId newRole = ev.Player.Role.Type;
78 | if (plugin.Config.HealthValues != null && plugin.Config.HealthValues.TryGetValue(newRole, out int health))
79 | {
80 | ev.Player.Health = health;
81 | ev.Player.MaxHealth = health;
82 | }
83 |
84 | if (ev.Player.Role is FpcRole && plugin.Config.PlayerHealthInfo)
85 | {
86 | 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)}";
87 | }
88 |
89 | if (plugin.Config.AfkIgnoredRoles.Contains(newRole) && plugin.AfkDict.TryGetValue(ev.Player, out Tuple value))
90 | plugin.AfkDict[ev.Player] = new Tuple(newRole is RoleTypeId.Spectator ? value.Item1 : 0, ev.Player.Position);
91 | }
92 |
93 | public void OnPlayerDied(DiedEventArgs ev)
94 | {
95 | if (ev.Attacker != null && plugin.Config.HealthOnKill.ContainsKey(ev.Attacker.Role))
96 | {
97 | ev.Attacker.Heal(plugin.Config.HealthOnKill[ev.Attacker.Role]);
98 | }
99 | }
100 |
101 | public List StartItems(RoleTypeId role, Player player = null)
102 | {
103 | List items = new();
104 |
105 | for (int i = 0; i < plugin.Config.StartingInventories[role].UsedSlots; i++)
106 | {
107 | 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));
108 | double r;
109 | if (plugin.Config.AdditiveProbabilities)
110 | r = plugin.Rng.NextDouble() * itemChances.Sum(val => val.Chance);
111 | else
112 | r = plugin.Rng.NextDouble() * 100;
113 | Log.Debug($"[StartItems] ActualChance ({r})/{itemChances.Sum(val => val.Chance)}");
114 | foreach ((string item, double chance, string groupKey) in itemChances)
115 | {
116 | Log.Debug($"[StartItems] Probability ({r})/{chance}");
117 | if (r <= chance)
118 | {
119 | if (Enum.TryParse(item, true, out ItemType type))
120 | {
121 | items.Add(type);
122 | break;
123 | }
124 | else if (CustomItem.TryGet(item, out CustomItem customItem))
125 | {
126 | if (player != null)
127 | customItem!.Give(player);
128 | else
129 | Log.Warn($"{nameof(StartItems)}: Tried to give {customItem!.Name} to a null player.");
130 |
131 | break;
132 | }
133 | else
134 | 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.");
135 | }
136 |
137 | r -= chance;
138 | }
139 | }
140 |
141 | return items;
142 | }
143 |
144 | public string FormatJoinMessage(Player player) =>
145 | string.IsNullOrEmpty(plugin.Config.JoinMessage) ? string.Empty : plugin.Config.JoinMessage.Replace("%player%", player.Nickname).Replace("%server%", Server.Name).Replace("%count%", $"{Player.Dictionary.Count}");
146 |
147 | public void OnPlayerHurting(HurtingEventArgs ev)
148 | {
149 | float damageMultiplier;
150 | if (plugin.Config.RoleDamageMultipliers != null && ev.Attacker != null && plugin.Config.RoleDamageMultipliers.TryGetValue(ev.Attacker.Role, out damageMultiplier))
151 | ev.Amount *= damageMultiplier;
152 |
153 | if (plugin.Config.DamageMultipliers != null && plugin.Config.DamageMultipliers.TryGetValue(ev.DamageHandler.Type, out damageMultiplier))
154 | ev.Amount *= damageMultiplier;
155 |
156 | if (plugin.Config.PlayerHealthInfo)
157 | 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)}";
158 |
159 | if (ev.Attacker is not null && plugin.AfkDict.ContainsKey(ev.Attacker))
160 | {
161 | Log.Debug($"Resetting {ev.Attacker.Nickname} AFK timer.");
162 | plugin.AfkDict[ev.Attacker] = new Tuple(0, ev.Attacker.Position);
163 | }
164 | }
165 |
166 | public void OnInteractingDoor(InteractingDoorEventArgs ev)
167 | {
168 | if (ev.Player.IsCuffed && plugin.Config.RestrictiveDisarming)
169 | ev.IsAllowed = false;
170 |
171 | if (plugin.AfkDict.ContainsKey(ev.Player))
172 | {
173 | Log.Debug($"Resetting {ev.Player.Nickname} AFK timer.");
174 | plugin.AfkDict[ev.Player] = new Tuple(0, ev.Player.Position);
175 | }
176 | }
177 |
178 | public void OnInteractingElevator(InteractingElevatorEventArgs ev)
179 | {
180 | if (ev.Player.IsCuffed && plugin.Config.RestrictiveDisarming)
181 | ev.IsAllowed = false;
182 |
183 | if (plugin.AfkDict.ContainsKey(ev.Player))
184 | {
185 | Log.Debug($"Resetting {ev.Player.Nickname} AFK timer.");
186 | plugin.AfkDict[ev.Player] = new Tuple(0, ev.Player.Position);
187 | }
188 | }
189 |
190 | public void OnEscaping(EscapingEventArgs ev)
191 | {
192 | if (ev.EscapeScenario is EscapeScenario.CustomEscape)
193 | {
194 | ev.NewRole = ev.Player.Role.Type switch
195 | {
196 | RoleTypeId.FacilityGuard or RoleTypeId.NtfPrivate or RoleTypeId.NtfSergeant or RoleTypeId.NtfCaptain or RoleTypeId.NtfSpecialist => RoleTypeId.ChaosConscript,
197 | RoleTypeId.ChaosConscript or RoleTypeId.ChaosMarauder or RoleTypeId.ChaosRepressor or RoleTypeId.ChaosRifleman => RoleTypeId.ChaosConscript,
198 | _ => RoleTypeId.None,
199 | };
200 |
201 | ev.IsAllowed = ev.NewRole is not RoleTypeId.None;
202 | }
203 | }
204 |
205 | public void OnUsingRadioBattery(UsingRadioBatteryEventArgs ev)
206 | {
207 | ev.Drain *= plugin.Config.RadioBatteryDrainMultiplier;
208 | }
209 |
210 | public void AntiAfkEventHandler(IPlayerEvent ev)
211 | {
212 | if (ev.Player != null && plugin.AfkDict.ContainsKey(ev.Player))
213 | {
214 | Log.Debug($"Resetting {ev.Player.Nickname} AFK timer.");
215 | plugin.AfkDict[ev.Player] = new Tuple(0, ev.Player.Position);
216 | }
217 | }
218 | }
219 | }
--------------------------------------------------------------------------------
/Common Utilities/EventHandlers/ServerHandlers.cs:
--------------------------------------------------------------------------------
1 | namespace Common_Utilities.EventHandlers
2 | {
3 | #pragma warning disable SA1313 // Parameter names should begin with lower-case letter
4 |
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 |
9 | using Exiled.API.Enums;
10 | using Exiled.API.Features;
11 | using Exiled.API.Features.Pickups;
12 | using Exiled.API.Features.Roles;
13 | using Exiled.Events.EventArgs.Server;
14 | using Exiled.Events.EventArgs.Warhead;
15 | using InventorySystem.Configs;
16 | using MEC;
17 | using PlayerRoles;
18 | using UnityEngine;
19 |
20 | public class ServerHandlers
21 | {
22 | private readonly Main plugin;
23 |
24 | private bool friendlyFireDisable = false;
25 |
26 | public ServerHandlers(Main plugin) => this.plugin = plugin;
27 |
28 | public void OnRoundStarted()
29 | {
30 | if (plugin.Config.AutonukeTime > -1)
31 | plugin.Coroutines.Add(Timing.CallDelayed(plugin.Config.AutonukeTime, AutoNuke));
32 |
33 | if (plugin.Config.RagdollCleanupDelay > 0)
34 | plugin.Coroutines.Add(Timing.RunCoroutine(RagdollCleanup()));
35 |
36 | if (plugin.Config.ItemCleanupDelay > 0)
37 | plugin.Coroutines.Add(Timing.RunCoroutine(ItemCleanup()));
38 | }
39 |
40 | public void OnWaitingForPlayers()
41 | {
42 | if (plugin.Config.AfkLimit > 0)
43 | {
44 | plugin.AfkDict.Clear();
45 | plugin.Coroutines.Add(Timing.RunCoroutine(AfkCheck()));
46 | }
47 |
48 | if (friendlyFireDisable)
49 | {
50 | Log.Debug($"{nameof(OnWaitingForPlayers)}: Disabling friendly fire.");
51 | Server.FriendlyFire = false;
52 | friendlyFireDisable = false;
53 | }
54 |
55 | if (plugin.Config.TimedBroadcastDelay > 0)
56 | plugin.Coroutines.Add(Timing.RunCoroutine(ServerBroadcast()));
57 |
58 | // Fix GrandLoadout not able to give this 2 inventory
59 | StartingInventories.DefinedInventories[RoleTypeId.Tutorial] = new(Array.Empty(), new());
60 | StartingInventories.DefinedInventories[RoleTypeId.ClassD] = new(Array.Empty(), new());
61 |
62 | Warhead.IsLocked = false;
63 | }
64 |
65 | public void OnRoundEnded(RoundEndedEventArgs ev)
66 | {
67 | if (plugin.Config.FriendlyFireOnRoundEnd && !Server.FriendlyFire)
68 | {
69 | Log.Debug($"{nameof(OnRoundEnded)}: Enabling friendly fire.");
70 | Server.FriendlyFire = true;
71 | friendlyFireDisable = true;
72 | }
73 |
74 | foreach (CoroutineHandle coroutine in plugin.Coroutines)
75 | Timing.KillCoroutines(coroutine);
76 | plugin.Coroutines.Clear();
77 | }
78 |
79 | public IEnumerator ServerBroadcast()
80 | {
81 | for (; ; )
82 | {
83 | yield return Timing.WaitForSeconds(plugin.Config.TimedBroadcastDelay);
84 |
85 | Map.Broadcast(plugin.Config.TimedBroadcastDuration, plugin.Config.TimedBroadcast);
86 | }
87 | }
88 |
89 | public IEnumerator ItemCleanup()
90 | {
91 | for (; ; )
92 | {
93 | yield return Timing.WaitForSeconds(plugin.Config.ItemCleanupDelay);
94 |
95 | foreach (Pickup pickup in Pickup.List.ToList())
96 | {
97 | if (!plugin.Config.ItemCleanupOnlyPocket || pickup.Position.y < -1500f)
98 | pickup.Destroy();
99 | }
100 | }
101 | }
102 |
103 | public IEnumerator RagdollCleanup()
104 | {
105 | for (; ; )
106 | {
107 | yield return Timing.WaitForSeconds(plugin.Config.RagdollCleanupDelay);
108 |
109 | foreach (Ragdoll ragdoll in Ragdoll.List.ToList())
110 | {
111 | if (!plugin.Config.RagdollCleanupOnlyPocket || ragdoll.Position.y < -1500f)
112 | ragdoll.Destroy();
113 | }
114 | }
115 | }
116 |
117 | public void AutoNuke()
118 | {
119 | if (!Warhead.IsInProgress)
120 | {
121 | switch (plugin.Config.AutonukeBroadcast.Duration)
122 | {
123 | case 0:
124 | break;
125 | case 1:
126 | Cassie.Message(plugin.Config.AutonukeBroadcast.Content);
127 | break;
128 | default:
129 | Map.Broadcast(plugin.Config.AutonukeBroadcast);
130 | break;
131 | }
132 |
133 | Warhead.Start();
134 | }
135 |
136 | if (plugin.Config.AutonukeLock)
137 | Warhead.IsLocked = true;
138 | }
139 |
140 | public IEnumerator AfkCheck()
141 | {
142 | for (; ; )
143 | {
144 | yield return Timing.WaitForSeconds(1f);
145 |
146 | foreach (Player player in Player.List)
147 | {
148 | if (!plugin.AfkDict.ContainsKey(player))
149 | plugin.AfkDict.Add(player, new Tuple(0, player.Position));
150 |
151 | 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))
152 | {
153 | #pragma warning disable SA1013
154 | 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)}");
155 | continue;
156 | #pragma warning restore SA1013
157 | }
158 |
159 | if ((plugin.AfkDict[player].Item2 - player.Position).sqrMagnitude > 2)
160 | {
161 | Log.Debug($"Player {player.Nickname} has moved, resetting AFK timer.");
162 | plugin.AfkDict[player] = new Tuple(0, player.Position);
163 | }
164 |
165 | if (plugin.AfkDict[player].Item1 >= plugin.Config.AfkLimit)
166 | {
167 | plugin.AfkDict.Remove(player);
168 | Log.Debug($"Kicking {player.Nickname} for being AFK.");
169 | player.Kick("You were kicked by CommonUtilities for being AFK.");
170 | }
171 | else if (plugin.AfkDict[player].Item1 >= (plugin.Config.AfkLimit / 2))
172 | {
173 | 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);
174 | }
175 |
176 | plugin.AfkDict[player] = new Tuple(plugin.AfkDict[player].Item1 + 1, plugin.AfkDict[player].Item2);
177 | }
178 | }
179 | }
180 |
181 | public void OnRestartingRound()
182 | {
183 | foreach (CoroutineHandle coroutine in plugin.Coroutines)
184 | Timing.KillCoroutines(coroutine);
185 | plugin.Coroutines.Clear();
186 | }
187 |
188 | public void OnWarheadStarting(StartingEventArgs _)
189 | {
190 | foreach (Room room in Room.List)
191 | room.Color = plugin.Config.WarheadColor;
192 | }
193 |
194 | public void OnWarheadStopping(StoppingEventArgs _)
195 | {
196 | if (Warhead.IsLocked)
197 | return;
198 |
199 | foreach (Room room in Room.List)
200 | room.ResetColor();
201 | }
202 | }
203 | }
--------------------------------------------------------------------------------
/Common Utilities/Main.cs:
--------------------------------------------------------------------------------
1 | namespace Common_Utilities
2 | {
3 | #pragma warning disable SA1401 // Fields should be private
4 | using System;
5 | using System.Collections.Generic;
6 |
7 | using ConfigObjects;
8 | using Configs;
9 | using EventHandlers;
10 | using Exiled.API.Enums;
11 | using Exiled.API.Features;
12 | using HarmonyLib;
13 | using MEC;
14 | using PlayerRoles;
15 | using Scp914;
16 | using UnityEngine;
17 |
18 | using Player = Exiled.Events.Handlers.Player;
19 | using Random = System.Random;
20 | using Scp914 = Exiled.Events.Handlers.Scp914;
21 | using Server = Exiled.Events.Handlers.Server;
22 |
23 | public class Main : Plugin
24 | {
25 | public static Main Instance;
26 | public PlayerHandlers PlayerHandlers;
27 | public ServerHandlers ServerHandlers;
28 | public MapHandlers MapHandlers;
29 | public Random Rng = new();
30 | public Harmony Harmony;
31 | public string HarmonyName;
32 |
33 | public override string Name { get; } = "Common Utilities";
34 |
35 | public override string Author { get; } = "Exiled-Team";
36 |
37 | public override Version Version { get; } = new(7, 1, 1);
38 |
39 | public override Version RequiredExiledVersion { get; } = new(8, 5, 0);
40 |
41 | public override string Prefix { get; } = "CommonUtilities";
42 |
43 | public override PluginPriority Priority => PluginPriority.Higher;
44 |
45 | public List Coroutines { get; } = new();
46 |
47 | public Dictionary> AfkDict { get; } = new();
48 |
49 | public override void OnEnabled()
50 | {
51 | if (Config.Debug)
52 | DebugConfig();
53 |
54 | Instance = this;
55 |
56 | Log.Info($"Instantiating Events..");
57 | PlayerHandlers = new PlayerHandlers(this);
58 | ServerHandlers = new ServerHandlers(this);
59 | MapHandlers = new MapHandlers(this);
60 |
61 | Log.Info($"Registering EventHandlers..");
62 | if (Config.HealthOnKill != null)
63 | Player.Died += PlayerHandlers.OnPlayerDied;
64 | Player.Hurting += PlayerHandlers.OnPlayerHurting;
65 | Player.Verified += PlayerHandlers.OnPlayerVerified;
66 | if (Config.StartingInventories != null)
67 | Player.ChangingRole += PlayerHandlers.OnChangingRole;
68 | Player.Spawned += PlayerHandlers.OnSpawned;
69 | Player.InteractingDoor += PlayerHandlers.OnInteractingDoor;
70 | if (Config.RadioBatteryDrainMultiplier is not 1)
71 | Player.UsingRadioBattery += PlayerHandlers.OnUsingRadioBattery;
72 | Player.InteractingElevator += PlayerHandlers.OnInteractingElevator;
73 | if (Config.DisarmSwitchTeams)
74 | Player.Escaping += PlayerHandlers.OnEscaping;
75 | if (Config.AfkLimit > 0)
76 | {
77 | Player.Jumping += PlayerHandlers.AntiAfkEventHandler;
78 | Player.Shooting += PlayerHandlers.AntiAfkEventHandler;
79 | Player.UsingItem += PlayerHandlers.AntiAfkEventHandler;
80 | Player.MakingNoise += PlayerHandlers.AntiAfkEventHandler;
81 | Player.ReloadingWeapon += PlayerHandlers.AntiAfkEventHandler;
82 | Player.ThrownProjectile += PlayerHandlers.AntiAfkEventHandler;
83 | Player.ChangingMoveState += PlayerHandlers.AntiAfkEventHandler;
84 | }
85 |
86 | Server.RoundEnded += ServerHandlers.OnRoundEnded;
87 | Server.RoundStarted += ServerHandlers.OnRoundStarted;
88 | Server.RestartingRound += ServerHandlers.OnRestartingRound;
89 | Server.WaitingForPlayers += ServerHandlers.OnWaitingForPlayers;
90 |
91 | if (Config.Scp914ItemChanges != null)
92 | Scp914.UpgradingPickup += MapHandlers.OnScp914UpgradingItem;
93 | if (Config.Scp914ItemChanges != null)
94 | Scp914.UpgradingInventoryItem += MapHandlers.OnScp914UpgradingInventoryItem;
95 | Scp914.UpgradingPlayer += MapHandlers.OnScp914UpgradingPlayer;
96 |
97 | Exiled.Events.Handlers.Warhead.Starting += ServerHandlers.OnWarheadStarting;
98 | Exiled.Events.Handlers.Warhead.Stopping += ServerHandlers.OnWarheadStopping;
99 |
100 | HarmonyName = $"com-joker.cu-{DateTime.UtcNow.Ticks}";
101 | Harmony = new Harmony(HarmonyName);
102 | Harmony.PatchAll();
103 |
104 | base.OnEnabled();
105 | }
106 |
107 | public override void OnDisabled()
108 | {
109 | Player.Died -= PlayerHandlers.OnPlayerDied;
110 | Player.Jumping -= PlayerHandlers.AntiAfkEventHandler;
111 | Player.Shooting -= PlayerHandlers.AntiAfkEventHandler;
112 | Player.UsingItem -= PlayerHandlers.AntiAfkEventHandler;
113 | Player.Hurting -= PlayerHandlers.OnPlayerHurting;
114 | Player.Verified -= PlayerHandlers.OnPlayerVerified;
115 | Player.MakingNoise -= PlayerHandlers.AntiAfkEventHandler;
116 | Player.ReloadingWeapon -= PlayerHandlers.AntiAfkEventHandler;
117 | Player.ChangingRole -= PlayerHandlers.OnChangingRole;
118 | Player.Spawned -= PlayerHandlers.OnSpawned;
119 | Player.ThrownProjectile -= PlayerHandlers.AntiAfkEventHandler;
120 | Player.InteractingDoor -= PlayerHandlers.OnInteractingDoor;
121 | Player.UsingRadioBattery -= PlayerHandlers.OnUsingRadioBattery;
122 | Player.ChangingMoveState -= PlayerHandlers.AntiAfkEventHandler;
123 | Player.InteractingElevator -= PlayerHandlers.OnInteractingElevator;
124 | Player.Escaping -= PlayerHandlers.OnEscaping;
125 |
126 | Server.RoundEnded -= ServerHandlers.OnRoundEnded;
127 | Server.RoundStarted -= ServerHandlers.OnRoundStarted;
128 | Server.RestartingRound -= ServerHandlers.OnRestartingRound;
129 | Server.WaitingForPlayers -= ServerHandlers.OnWaitingForPlayers;
130 |
131 | Scp914.UpgradingPickup -= MapHandlers.OnScp914UpgradingItem;
132 | Scp914.UpgradingPlayer -= MapHandlers.OnScp914UpgradingPlayer;
133 | Scp914.UpgradingInventoryItem -= MapHandlers.OnScp914UpgradingInventoryItem;
134 |
135 | Exiled.Events.Handlers.Warhead.Starting -= ServerHandlers.OnWarheadStarting;
136 | Exiled.Events.Handlers.Warhead.Stopping -= ServerHandlers.OnWarheadStopping;
137 |
138 | Harmony.UnpatchAll(HarmonyName);
139 |
140 | ServerHandlers = null;
141 | PlayerHandlers = null;
142 | MapHandlers = null;
143 | base.OnDisabled();
144 | }
145 |
146 | public void DebugConfig()
147 | {
148 | if (Config.StartingInventories != null)
149 | {
150 | Log.Debug($"{Config.StartingInventories.Count}");
151 | foreach (KeyValuePair inv in Config.StartingInventories)
152 | {
153 | for (int i = 0; i < inv.Value.UsedSlots; i++)
154 | {
155 | foreach (ItemChance chance in inv.Value[i])
156 | {
157 | Log.Debug($"Inventory Config: {inv.Key} - Slot{i + 1}: {chance.ItemName} ({chance.Chance})");
158 | }
159 | }
160 |
161 | foreach ((ItemType type, ushort amount, string group) in inv.Value.Ammo)
162 | {
163 | Log.Debug($"Ammo Config: {inv.Key} - {type} {amount} ({group})");
164 | }
165 | }
166 | }
167 |
168 | if (Config.Scp914ItemChanges != null)
169 | {
170 | Log.Debug($"{Config.Scp914ItemChanges.Count}");
171 | foreach (KeyValuePair> upgrade in Config.Scp914ItemChanges)
172 | {
173 | foreach ((ItemType oldItem, ItemType newItem, double chance, int count) in upgrade.Value)
174 | Log.Debug($"914 Item Config: {upgrade.Key}: {oldItem} -> {newItem}x({count}) - {chance}");
175 | }
176 | }
177 |
178 | if (Config.Scp914ClassChanges != null)
179 | {
180 | Log.Debug($"{Config.Scp914ClassChanges.Count}");
181 | foreach (KeyValuePair> upgrade in Config.Scp914ClassChanges)
182 | {
183 | foreach ((RoleTypeId oldRole, string newRole, double chance, bool keepInventory, bool keepHealth) in upgrade.Value)
184 | Log.Debug($"914 Role Config: {upgrade.Key}: {oldRole} -> {newRole} - {chance} keepInventory: {keepInventory} keepHealth: {keepHealth}");
185 | }
186 | }
187 |
188 | if (Config.Scp914EffectChances != null)
189 | {
190 | Log.Debug($"{Config.Scp914EffectChances.Count}");
191 | foreach (KeyValuePair> upgrade in Config.Scp914EffectChances)
192 | {
193 | foreach ((EffectType effect, double chance, float duration) in upgrade.Value)
194 | Log.Debug($"914 Effect Config: {upgrade.Key}: {effect} + {duration} - {chance}");
195 | }
196 | }
197 |
198 | if (Config.Scp914TeleportChances != null)
199 | {
200 | Log.Debug($"{Config.Scp914TeleportChances.Count}");
201 | foreach (KeyValuePair> upgrade in Config.Scp914TeleportChances)
202 | {
203 | foreach ((RoomType room, List ignoredRooms, Vector3 offset, double chance, float damage, ZoneType zone) in upgrade.Value)
204 | {
205 | Log.Debug($"914 Teleport Config: {upgrade.Key}: {room}/{zone} + {offset} - {chance} [{damage}]");
206 | Log.Debug("Ignored rooms:");
207 | if (ignoredRooms != null)
208 | {
209 | foreach (RoomType roomType in ignoredRooms)
210 | {
211 | Log.Debug(roomType);
212 | }
213 | }
214 | }
215 | }
216 | }
217 | }
218 | }
219 | }
--------------------------------------------------------------------------------
/Common Utilities/Patches/ServerGrantLoadoutPatches.cs:
--------------------------------------------------------------------------------
1 | namespace Common_Utilities.Patches
2 | {
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | using Common_Utilities.ConfigObjects;
7 | using Common_Utilities.Configs;
8 | using Exiled.API.Features;
9 | using HarmonyLib;
10 | using InventorySystem;
11 | using PlayerRoles;
12 |
13 | [HarmonyPatch(typeof(InventoryItemProvider), nameof(InventoryItemProvider.ServerGrantLoadout))]
14 | public class ServerGrantLoadoutPatches
15 | {
16 | public static bool Prefix(ReferenceHub target, RoleTypeId roleTypeId, bool resetInventory = true)
17 | {
18 | if (Main.Instance.Config.StartingInventories == null || !Main.Instance.Config.StartingInventories.TryGetValue(roleTypeId, out RoleInventory startingInventories) || !Player.TryGet(target, out Player player))
19 | return true;
20 |
21 | if (resetInventory)
22 | player.ClearInventory();
23 |
24 | player.AddItem(Main.Instance.PlayerHandlers.StartItems(roleTypeId, player));
25 |
26 | if (startingInventories.Ammo != null && startingInventories.Ammo.Count > 0)
27 | {
28 | 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));
29 | if (startingAmmo.Any())
30 | {
31 | player.Ammo.Clear();
32 |
33 | foreach ((ItemType type, ushort amount, string group) in startingAmmo)
34 | {
35 | player.Ammo.Add(type, amount);
36 | }
37 | }
38 | }
39 |
40 | return false;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | $(MSBuildThisFileDirectory)\Exiled.ruleset
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Exiled.ruleset:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Plugin.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Exiled-Team
6 |
7 |
8 |
9 | net48
10 | 10.0
11 | x64
12 | false
13 | ../bin\$(Configuration)\
14 | true
15 | false
16 |
17 |
18 |
19 |
20 |
21 | 7.1.0
22 |
23 | false
24 |
25 | 2.2.2
26 | 1.1.118
27 | 1.3.0
28 | 8.0.0-beta.1
29 |
30 | Copyright © $(Authors) 2020 - $([System.DateTime]::Now.ToString("yyyy"))
31 | Git
32 | https://github.com/galaxy119/EXILED
33 | https://github.com/galaxy119/EXILED
34 | CC-BY-SA-3.0
35 |
36 | $(DefineConstants);PUBLIC_BETA
37 |
38 |
39 |
40 | False
41 | Portable
42 |
43 |
44 |
45 |
46 | $(NoWarn);SA0001
47 | $(NoWarn);SA1000
48 | $(NoWarn);SA1520
49 | $(NoWarn);SA1009
50 | $(NoWarn);SA1002
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Common-Utils
2 | Common Utils is a plugin that serves many common utilites in a day to day server life.
3 |
4 | # Main Features
5 | ## 914 Features
6 | - Ability to change 914's class upgrading (ex, DClass goes in; scientist comes out.)
7 | - Ability to add custom 914 recipies.
8 | ## Server Broadcast/Welcoming Features
9 | - Ability to completly configure a welcome message.
10 | - Ability to completly configure a broadcast message, this can appear every 'x' amount seconds.
11 | ## Custom Inventories
12 | - Ability to add custom inventories to all the main classes.
13 |
14 | (These are depricated, if you are using the Exiled 2.0 version of the plugin)
15 | # Default config:
16 | ```yaml
17 | CommonUtilities:
18 | # Whether or not debug messages should be shown.
19 | debug: false
20 | # The SCP Roles able to use V to talk to humans.
21 | scp_speech:
22 | - Scp049
23 | # Whether or not MTF/CI can 'escape' while disarmed to switch teams.
24 | disarm_switch_teams: true
25 | # Whether or not disarmed people will be prevented from interacting with doors/elevators.
26 | restrictive_disarming: true
27 | # The text displayed at the timed interval specified below.
28 | timed_broadcast: 'This server is running EXILED Common-Utilities, enjoy your stay!'
29 | # The time each timed broadcast will be displayed.
30 | timed_broadcast_duration: 5
31 | # The delay between each timed broadcast. To disable timed broadcasts, set this to 0
32 | timed_broadcast_delay: 300
33 | # The message displayed to the player when they first join the server. Setting this to empty will disable these broadcasts.
34 | join_message: 'Welcome %player%! Please read our rules!'
35 | # The amount of time (in seconds) the join message is displayed.
36 | join_message_duration: 5
37 | # The amount of time (in seconds) after the round starts, before the facilities auto-nuke will start.
38 | autonuke_time: 1500
39 | # Wether or not the nuke should be unable to be disabled during the auto-nuke countdown.
40 | autonuke_lock: true
41 | # 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.
42 | autonuke_broadcast:
43 | # The broadcast content
44 | content: 'The auto nuke has been activated.'
45 | # The broadcast duration
46 | duration: 10
47 | # The broadcast type
48 | type: Normal
49 | # Indicates whether the broadcast should be shown or not
50 | show: true
51 | # Whether or not to show player's health under their name when you look at them.
52 | player_health_info: true
53 | # Whether or not friendly fire should automatically turn on when a round ends (it will turn itself back off before the next round starts).
54 | friendly_fire_on_round_end: false
55 | # The multiplier applied to radio battery usage. Set to 0 to disable radio battery drain.
56 | radio_battery_drain_multiplier: 1
57 | # The color to use for lights while the warhead is active.
58 | warhead_color:
59 | r: 1
60 | g: 0.2
61 | b: 0.2
62 | a: 1
63 | # The maximum time, in seconds, that a player can be AFK before being kicked. Set to -1 to disable AFK system.
64 | afk_limit: 120
65 | # The roles that are ignored by the AFK system.
66 | afk_ignored_roles:
67 | - Scp079
68 | - Spectator
69 | - Tutorial
70 | # Whether or not probabilities should be additive (50 + 50 = 100) or not (50 + 50 = 2 seperate 50% chances)
71 | additive_probabilities: false
72 | # 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.
73 | starting_inventories:
74 | ClassD:
75 | slot1:
76 | - item_name: 'KeycardJanitor'
77 | chance: 10
78 | group: 'none'
79 | - item_name: 'Coin'
80 | chance: 100
81 | group: 'none'
82 | slot2:
83 | - item_name: 'Flashlight'
84 | chance: 100
85 | group: 'none'
86 | slot3: []
87 | slot4: []
88 | slot5: []
89 | slot6: []
90 | slot7: []
91 | slot8: []
92 | ammo:
93 | - type: Ammo556x45
94 | amount: 200
95 | group: 'none'
96 | # 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.
97 | scp914_item_changes:
98 | Rough:
99 | - original: KeycardO5
100 | new: MicroHID
101 | chance: 50
102 | # 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.
103 | scp914_class_changes:
104 | Rough:
105 | - original: ClassD
106 | new: Spectator
107 | chance: 100
108 | spawn_flags: None
109 | # 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.
110 | scp914_teleport_chances:
111 | Rough:
112 | - zone: Unspecified
113 | room: LczClassDSpawn
114 | offset:
115 | x: 0
116 | y: 0
117 | z: 0
118 | chance: 100
119 | damage: 0
120 | # A dictionary of random effects to apply to players when going through 914 on certain settings.
121 | scp914_effect_chances:
122 | Rough:
123 | - effect: Bleeding
124 | chance: 100
125 | duration: 0
126 | # Determines if 914 effects are exclusive, meaning only one can be applied each time a player is processed by 914.
127 | scp914_effects_exclusivity: false
128 | # Whether or not SCPs are immune to effects gained from 914.
129 | scps_immune_to914_effects: false
130 | # The frequency (in seconds) between ragdoll cleanups. Set to 0 to disable.
131 | ragdoll_cleanup_delay: 0
132 | # If ragdoll cleanup should only happen in the Pocket Dimension or not.
133 | ragdoll_cleanup_only_pocket: false
134 | # The frequency (in seconds) between item cleanups. Set to 0 to disable.
135 | item_cleanup_delay: 0
136 | # If item cleanup should only happen in the Pocket Dimension or not.
137 | item_cleanup_only_pocket: false
138 | # 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.
139 | role_damage_multipliers:
140 | Scp173: 1
141 | # 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.
142 | damage_multipliers:
143 | E11Sr: 1
144 | # A list of roles and how much health they should be given when they kill someone.
145 | health_on_kill:
146 | Scp173: 0
147 | Scp939: 10
148 | # A list of roles and what their default starting health should be.
149 | health_values:
150 | Scp173: 3200
151 | NtfCaptain: 150
152 | # If the plugin is enabled or not.
153 | is_enabled: true
154 |
155 | ```
156 |
--------------------------------------------------------------------------------
/Updating.md:
--------------------------------------------------------------------------------
1 | ### This guide should help you understand the differences between the old and new configs.
2 |
3 | ## Inventory
4 | #### All inventory configs have been rolled into a single config value `starting_inventories`. This means that you will not need to have 'empty configs' defined for roles you do not wish to change the inventories for. The new configs also allow you specify a usergroup, which will mean only that group can get that item.
5 | starting_inventories is a dictionary, and should be formatted like this:
6 | ```yaml
7 | starting_inventories:
8 | ClassD:
9 | slot1:
10 | - item_name: KeycardJanitor
11 | chance: 10
12 | group: none
13 | ```
14 | If you wish to add additional slots, or roles, you can do so.
15 | With the new inventory config, if you define a role, it's inventory will be replaced with whatever is listed. If you list a role here, but don't give them any items, it will give them an empty inventory.
16 | If you don't wish to change a particular roles inventory, simply don't include their roletype in this config.
17 |
18 | You can define configs for slots 1 through 8. Any slots that you don't define will be auto-generated as empty, but not defining them will NOT cause the config to break.
19 |
20 | ## SCP-914 Item recipe configs:
21 | #### These configs have been simplified, to allow you to not define keys you will not use, and should look like this:
22 | ```yaml
23 | scp914_item_changes:
24 | Rough:
25 | - original: KeycardO5
26 | new: MicroHID
27 | chance: 50
28 | - original: GunCOM18
29 | new: GunCOM15
30 | chance: 100
31 | ```
32 | If you wish to add additional knob settings, you can do so, but unused knob setting values will not be auto-generated anymore.
33 |
34 | ## SCP-914 Class recipe configs:
35 | #### Everything from the above Item recipe config applies, except this uses RoleTypes instead of ItemTypes, IE:
36 | ```yaml
37 | scp914_class_changes:
38 | Rough:
39 | - original: ClassD
40 | new: Spectator
41 | chance: 100
42 | OneToOne:
43 | - original: ClassD
44 | new: Scientist
45 | chance: 100
46 | - original: Scientist
47 | new: ClassD
48 | chance: 100
49 | ```
50 |
51 |
52 | ## SCP Damage Multipliers
53 | #### These have been renamed to `role_damage_multipliers` and will affect the damage of anyone who has that role.
54 |
55 | ## Weapon Damage Multipliers
56 | #### This has changed to use ItemTypes, and will affect any damage dealt by the specified item. Since only guns grenades & micro can deal damage, only those are affected.
57 |
58 | ### Both of the above damage multiplier configs are applied together, meaning if you have NtfCaptain doing double damage, and then have E11 doing double damage, an NtfCaptain will deal 4x damage with an E11
59 |
60 | ## HealthOnKill and HealthValues have both been updated to use RoleTypes directly, instead of a string that's parsed into a RoleType by the plugin.
61 |
--------------------------------------------------------------------------------
/stylecop.json:
--------------------------------------------------------------------------------
1 | {
2 | // ACTION REQUIRED: This file was automatically added to your project, but it
3 | // will not take effect until additional steps are taken to enable it. See the
4 | // following page for additional information:
5 | //
6 | // https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/EnableConfiguration.md
7 |
8 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
9 | "settings": {
10 | "documentationRules": {
11 | "companyName": "Chaos Theory",
12 | "copyrightText": "Unauthorized copying of this file, via any medium is strictly prohibited.",
13 | "headerDecoration": "-----------------------------------------------------------------------",
14 | "variables": {
15 | "licenseFile": "LICENSE",
16 | "licenseName": "Proprietary and confidential"
17 | }
18 | },
19 | "orderingRules": {
20 | "blankLinesBetweenUsingGroups": "require"
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------