Skip to content

Commit 7d85cb3

Browse files
committed
Lots of new useful features
1 parent 03aa51c commit 7d85cb3

12 files changed

Lines changed: 362 additions & 15 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// -----------------------------------------------------------------------
2+
// <copyright file="IEnumerableExtensions.cs" company="ExMod Team">
3+
// Copyright (c) ExMod Team. All rights reserved.
4+
// Licensed under the CC BY-SA 3.0 license.
5+
// </copyright>
6+
// -----------------------------------------------------------------------
7+
8+
namespace Exiled.API.Extensions
9+
{
10+
using System;
11+
using System.Collections.Generic;
12+
13+
/// <summary>
14+
/// A set of extensions for <see cref="IEnumerable{T}"/>.
15+
/// </summary>
16+
public static class IEnumerableExtensions
17+
{
18+
/// <summary>
19+
/// Perform an action on each element of a collection.
20+
/// </summary>
21+
/// <typeparam name="T">Type of <see cref="IEnumerable{T}"/> elements.</typeparam>
22+
/// <param name="enumerable"><see cref="IEnumerable{T}"/> in this collection, the elements will perform actions.</param>
23+
/// <param name="action">Action that needs to be performed.</param>
24+
public static void ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
25+
{
26+
if (enumerable is null || action is null)
27+
return;
28+
29+
foreach (T e in enumerable)
30+
action(e);
31+
}
32+
}
33+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// -----------------------------------------------------------------------
2+
// <copyright file="PlayerPermissionsExtensions.cs" company="ExMod Team">
3+
// Copyright (c) ExMod Team. All rights reserved.
4+
// Licensed under the CC BY-SA 3.0 license.
5+
// </copyright>
6+
// -----------------------------------------------------------------------
7+
8+
namespace Exiled.API.Extensions
9+
{
10+
using System.Collections.Generic;
11+
12+
/// <summary>
13+
/// A set of extensions for <see cref="PlayerPermissions"/>.
14+
/// </summary>
15+
public static class PlayerPermissionsExtensions
16+
{
17+
/// <summary>
18+
/// Checks whether the current permissions contain any of the permissions specified in the mask.
19+
/// </summary>
20+
/// <param name="playerPermissions">The current permissions to check.</param>
21+
/// <param name="mask">The mask of permissions to test against.</param>
22+
/// <returns><see langword="true"/> if the current permissions contain at least one permission from the mask; otherwise, <see langword="false"/>.</returns>
23+
public static bool HasAnyPermission(this PlayerPermissions playerPermissions, PlayerPermissions mask)
24+
{
25+
return (playerPermissions & mask) != 0;
26+
}
27+
28+
/// <summary>
29+
/// Checks whether the current permissions contain any of the permissions specified in the collection.
30+
/// </summary>
31+
/// <param name="playerPermissions">The current permissions to check.</param>
32+
/// <param name="collectionPlayerPermissions">The collection of permissions to test against.</param>
33+
/// <returns><see langword="true"/> if the current permissions contain at least one permission from the collection; otherwise, <see langword="false"/>.</returns>
34+
public static bool HasAnyPermission(this PlayerPermissions playerPermissions, IEnumerable<PlayerPermissions> collectionPlayerPermissions)
35+
{
36+
if (collectionPlayerPermissions is null)
37+
return false;
38+
39+
foreach (PlayerPermissions perm in collectionPlayerPermissions)
40+
{
41+
if (playerPermissions.HasAnyPermission(perm))
42+
return true;
43+
}
44+
45+
return false;
46+
}
47+
}
48+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// -----------------------------------------------------------------------
2+
// <copyright file="RandomExtensions.cs" company="ExMod Team">
3+
// Copyright (c) ExMod Team. All rights reserved.
4+
// Licensed under the CC BY-SA 3.0 license.
5+
// </copyright>
6+
// -----------------------------------------------------------------------
7+
8+
namespace Exiled.API.Extensions
9+
{
10+
using System;
11+
12+
/// <summary>
13+
/// A set of extensions for <see cref="Random"/>.
14+
/// </summary>
15+
public static class RandomExtensions
16+
{
17+
/// <summary>
18+
/// Generate a random float.
19+
/// </summary>
20+
/// <param name="rnd"><see cref="Random"/> object.</param>
21+
/// <param name="min">Minimum value.</param>
22+
/// <param name="max">Maximum value.</param>
23+
/// <returns>Random value between minimum and maximum.</returns>
24+
public static float NextFloat(this Random rnd, float min, float max)
25+
{
26+
return (float)((rnd.NextDouble() * (max - min)) + min);
27+
}
28+
29+
/// <summary>
30+
/// Generate a random float.
31+
/// </summary>
32+
/// <param name="rnd"><see cref="Random"/> object.</param>
33+
/// <param name="min">Minimum value.</param>
34+
/// <param name="max">Maximum value.</param>
35+
/// <returns>Random value between minimum and maximum.</returns>
36+
public static float NextFloat(this Random rnd, double min, float max)
37+
{
38+
return (float)((rnd.NextDouble() * (max - min)) + min);
39+
}
40+
41+
/// <summary>
42+
/// Generate a random float.
43+
/// </summary>
44+
/// <param name="rnd"><see cref="Random"/> object.</param>
45+
/// <param name="min">Minimum value.</param>
46+
/// <param name="max">Maximum value.</param>
47+
/// <returns>Random value between minimum and maximum.</returns>
48+
public static float NextFloat(this Random rnd, float min, double max)
49+
{
50+
return (float)((rnd.NextDouble() * (max - min)) + min);
51+
}
52+
53+
/// <summary>
54+
/// Generate a random float.
55+
/// </summary>
56+
/// <param name="rnd"><see cref="Random"/> object.</param>
57+
/// <param name="min">Minimum value.</param>
58+
/// <param name="max">Maximum value.</param>
59+
/// <returns>Random value between minimum and maximum.</returns>
60+
public static float NextFloat(this Random rnd, double min, double max)
61+
{
62+
return (float)((rnd.NextDouble() * (max - min)) + min);
63+
}
64+
65+
/// <summary>
66+
/// Generate a random bool.
67+
/// </summary>
68+
/// <param name="rnd"><see cref="Random"/> object.</param>
69+
/// <returns>Random boolean value.</returns>
70+
public static bool NextBool(this Random rnd)
71+
{
72+
return rnd.Next(2) == 0;
73+
}
74+
}
75+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// -----------------------------------------------------------------------
2+
// <copyright file="TimeSpanExtensions.cs" company="ExMod Team">
3+
// Copyright (c) ExMod Team. All rights reserved.
4+
// Licensed under the CC BY-SA 3.0 license.
5+
// </copyright>
6+
// -----------------------------------------------------------------------
7+
8+
namespace Exiled.API.Extensions
9+
{
10+
using System;
11+
12+
/// <summary>
13+
/// A set of extensions for <see cref="TimeSpan"/>.
14+
/// </summary>
15+
public static class TimeSpanExtensions
16+
{
17+
/// <summary>
18+
/// Converts a TimeSpan object to a human-readable format.
19+
/// </summary>
20+
/// <param name="timeSpan"><see cref="TimeSpan"/> object.</param>
21+
/// <returns>A <see cref="TimeSpan"/> object in string representation.</returns>
22+
public static string ToHumanReadable(this TimeSpan timeSpan)
23+
{
24+
if (timeSpan.TotalHours < 1)
25+
return timeSpan.ToString(@"mm\:ss");
26+
27+
if (timeSpan.TotalDays < 1)
28+
return timeSpan.ToString(@"hh\:mm\:ss");
29+
30+
string daysPart = timeSpan.Days == 1 ? "1 day" : $"{timeSpan.Days} days";
31+
string timePart = timeSpan.ToString(@"hh\:mm\:ss");
32+
return $"{daysPart}, {timePart}";
33+
}
34+
}
35+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// -----------------------------------------------------------------------
2+
// <copyright file="Vector3Extensions.cs" company="ExMod Team">
3+
// Copyright (c) ExMod Team. All rights reserved.
4+
// Licensed under the CC BY-SA 3.0 license.
5+
// </copyright>
6+
// -----------------------------------------------------------------------
7+
8+
namespace Exiled.API.Extensions
9+
{
10+
using Exiled.API.Enums;
11+
using Exiled.API.Features;
12+
13+
using UnityEngine;
14+
15+
/// <summary>
16+
/// A set of extensions for <see cref="Vector3"/> that provide conversions between world space and
17+
/// room‑relative local space.
18+
/// </summary>
19+
public static class Vector3Extensions
20+
{
21+
/// <summary>
22+
/// Converts a world position to a position relative to the specified room's local coordinate system.
23+
/// </summary>
24+
/// <param name="worldPos">The world‑space position to convert.</param>
25+
/// <param name="room">The room whose local space will be used as the reference.</param>
26+
/// <returns>
27+
/// The position expressed in the room's local space.
28+
/// If the room is the <see cref="RoomType.Surface"/>, the original world position is returned unchanged.
29+
/// </returns>
30+
public static Vector3 FromWorldToRelativePos(this Vector3 worldPos, Room room)
31+
{
32+
if (room.Type == RoomType.Surface)
33+
return worldPos;
34+
return room.Transform.InverseTransformPoint(worldPos);
35+
}
36+
37+
/// <summary>
38+
/// Converts a position relative to the specified room's local space back to world space.
39+
/// </summary>
40+
/// <param name="relativePos">The local‑space position to convert.</param>
41+
/// <param name="room">The room whose local space was used as the reference.</param>
42+
/// <returns>
43+
/// The position expressed in world space.
44+
/// If the room is the <see cref="RoomType.Surface"/>, the original local position is returned unchanged
45+
/// (since surface uses world coordinates directly).
46+
/// </returns>
47+
public static Vector3 FromRelativeToWorldPos(this Vector3 relativePos, Room room)
48+
{
49+
if (room.Type == RoomType.Surface)
50+
return relativePos;
51+
return room.Transform.TransformPoint(relativePos);
52+
}
53+
}
54+
}

EXILED/Exiled.API/Features/Doors/Door.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,28 @@ public static Door Get(DoorVariant doorVariant)
337337
public static T Get<T>(DoorVariant doorVariant)
338338
where T : Door => Get(doorVariant) as T;
339339

340+
/// <summary>
341+
/// Gets the door object associated with a specific <see cref="ButtonVariant"/>, or creates a new one if there isn't one.
342+
/// </summary>
343+
/// <param name="buttonVariant">The base-game <see cref="ButtonVariant"/>.</param>
344+
/// <returns>A <see cref="Door"/> wrapper object.</returns>
345+
public static Door Get(ButtonVariant buttonVariant)
346+
{
347+
if (buttonVariant is null || buttonVariant.ParentDoor is null)
348+
return null;
349+
350+
return Get(buttonVariant.ParentDoor);
351+
}
352+
353+
/// <summary>
354+
/// Gets the <see cref="Door"/> by <see cref="ButtonVariant"/>.
355+
/// </summary>
356+
/// <param name="buttonVariant">The <see cref="ButtonVariant"/> to convert into an door.</param>
357+
/// <typeparam name="T">The specified <see cref="Door"/> type.</typeparam>
358+
/// <returns>The door wrapper for the given <see cref="ButtonVariant"/>.</returns>
359+
public static T Get<T>(ButtonVariant buttonVariant)
360+
where T : Door => Get(buttonVariant) as T;
361+
340362
/// <summary>
341363
/// Gets a <see cref="Door"/> given the specified <see cref="DoorType"/>.
342364
/// </summary>
@@ -378,7 +400,16 @@ public static T Get<T>(string name)
378400
/// </summary>
379401
/// <param name="gameObject">The base-game <see cref="UnityEngine.GameObject"/>.</param>
380402
/// <returns>The <see cref="Door"/> with the given name or <see langword="null"/> if not found.</returns>
381-
public static Door Get(GameObject gameObject) => gameObject is null ? null : Get(gameObject.GetComponentInParent<DoorVariant>());
403+
public static Door Get(GameObject gameObject)
404+
{
405+
if (gameObject != null)
406+
{
407+
// ParentDoor requires enabling "unsafe code"
408+
return Get(gameObject.GetComponentInParent<DoorVariant>() ?? gameObject.GetComponent<ButtonVariant>()?.ParentDoor);
409+
}
410+
411+
return null;
412+
}
382413

383414
/// <summary>
384415
/// Returns the closest <see cref="Door"/> to the given <paramref name="position"/>.

EXILED/Exiled.API/Features/Items/Firearm.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public static IReadOnlyDictionary<Player, Dictionary<FirearmType, AttachmentIden
143143
public new BaseFirearm Base { get; }
144144

145145
/// <summary>
146-
/// Gets a primaty magazine for current firearm.
146+
/// Gets a primary magazine for current firearm.
147147
/// </summary>
148148
public PrimaryMagazine PrimaryMagazine { get; }
149149

@@ -156,7 +156,7 @@ public static IReadOnlyDictionary<Player, Dictionary<FirearmType, AttachmentIden
156156
public BarrelMagazine BarrelMagazine { get; }
157157

158158
/// <summary>
159-
/// Gets a primaty magazine for current firearm.
159+
/// Gets a primary magazine for current firearm.
160160
/// </summary>
161161
public HitscanHitregModuleBase HitscanHitregModule { get; }
162162

@@ -189,6 +189,36 @@ public int MagazineAmmo
189189
set => PrimaryMagazine.Ammo = value;
190190
}
191191

192+
/// <summary>
193+
/// Gets or sets a value indicating whether the magazine is attached from the weapon. Setter will attach the magazine, but it will be empty.
194+
/// </summary>
195+
public bool IsMagazineAttached
196+
{
197+
get
198+
{
199+
if (PrimaryMagazine is NormalMagazine normalMag)
200+
return normalMag.MagazineInserted;
201+
202+
// Weapons that do not have a detachable magazine return false by default. For example, a revolver.
203+
return false;
204+
}
205+
206+
set
207+
{
208+
if (PrimaryMagazine is NormalMagazine normalMag)
209+
normalMag.MagazineInserted = value;
210+
}
211+
}
212+
213+
/// <summary>
214+
/// Gets or sets a value indicating whether the magazine is attached from the weapon.
215+
/// </summary>
216+
public bool IsMagazineDeattached
217+
{
218+
get => !IsMagazineAttached;
219+
set => IsMagazineAttached = !value;
220+
}
221+
192222
/// <summary>
193223
/// Gets or sets the amount of ammo in the firearm barrel.
194224
/// </summary>

EXILED/Exiled.API/Features/Log.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public static T DebugObject<T>(T @object)
7373
/// Server must have exiled_debug config enabled.
7474
/// </summary>
7575
/// <param name="message">The message to be sent.</param>
76-
public static void Debug(string message)
76+
public static void Debug(string message = "")
7777
{
7878
Assembly callingAssembly = Assembly.GetCallingAssembly();
7979
#if DEBUG
@@ -98,7 +98,7 @@ public static void Debug(string message)
9898
/// Sends a <see cref="Discord.LogLevel.Warn"/> level messages to the game console.
9999
/// </summary>
100100
/// <param name="message">The message to be sent.</param>
101-
public static void Warn(string message) => Send($"[{Assembly.GetCallingAssembly().GetName().Name}] {message}", Discord.LogLevel.Warn, ConsoleColor.Magenta);
101+
public static void Warn(string message = "") => Send($"[{Assembly.GetCallingAssembly().GetName().Name}] {message}", Discord.LogLevel.Warn, ConsoleColor.Magenta);
102102

103103
/// <summary>
104104
/// Sends a <see cref="Discord.LogLevel.Error"/> level messages to the game console.
@@ -114,7 +114,7 @@ public static void Debug(string message)
114114
/// It's recommended to send any messages in the catch block of a try/catch as errors with the exception string.
115115
/// </summary>
116116
/// <param name="message">The message to be sent.</param>
117-
public static void Error(string message) => Send($"[{Assembly.GetCallingAssembly().GetName().Name}] {message}", Discord.LogLevel.Error, ConsoleColor.DarkRed);
117+
public static void Error(string message = "") => Send($"[{Assembly.GetCallingAssembly().GetName().Name}] {message}", Discord.LogLevel.Error, ConsoleColor.DarkRed);
118118

119119
/// <summary>
120120
/// Sends a log message to the game console.

0 commit comments

Comments
 (0)