-
Notifications
You must be signed in to change notification settings - Fork 181
/
Map.cs
415 lines (358 loc) · 16.6 KB
/
Map.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
// -----------------------------------------------------------------------
// <copyright file="Map.cs" company="Exiled Team">
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
// </copyright>
// -----------------------------------------------------------------------
namespace Exiled.API.Features
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Decals;
using Enums;
using Exiled.API.Extensions;
using Exiled.API.Features.Hazards;
using Exiled.API.Features.Pickups;
using Exiled.API.Features.Toys;
using global::Hazards;
using InventorySystem;
using InventorySystem.Items.Firearms.BasicMessages;
using InventorySystem.Items.Pickups;
using InventorySystem.Items.ThrowableProjectiles;
using Items;
using LightContainmentZoneDecontamination;
using MapGeneration;
using MapGeneration.Distributors;
using Mirror;
using PlayerRoles;
using PlayerRoles.PlayableScps.Scp173;
using PlayerRoles.PlayableScps.Scp939;
using PlayerRoles.Ragdolls;
using RelativePositioning;
using UnityEngine;
using Utils;
using Utils.Networking;
using Object = UnityEngine.Object;
using Scp173GameRole = PlayerRoles.PlayableScps.Scp173.Scp173Role;
using Scp939GameRole = PlayerRoles.PlayableScps.Scp939.Scp939Role;
/// <summary>
/// A set of tools to easily handle the in-game map.
/// </summary>
public static class Map
{
/// <summary>
/// A list of <see cref="Locker"/>s on the map.
/// </summary>
internal static readonly List<Locker> LockersValue = new(35);
/// <summary>
/// A list of <see cref="PocketDimensionTeleport"/>s on the map.
/// </summary>
internal static readonly List<PocketDimensionTeleport> TeleportsValue = new(8);
/// <summary>
/// A list of <see cref="AdminToy"/>s on the map.
/// </summary>
internal static readonly List<AdminToy> ToysValue = new();
private static TantrumEnvironmentalHazard tantrumPrefab;
private static Scp939AmnesticCloudInstance amnesticCloudPrefab;
private static AmbientSoundPlayer ambientSoundPlayer;
/// <summary>
/// Gets the tantrum prefab.
/// </summary>
public static TantrumEnvironmentalHazard TantrumPrefab
{
get
{
if (tantrumPrefab == null)
{
Scp173GameRole scp173Role = (Scp173GameRole)RoleTypeId.Scp173.GetRoleBase();
if (scp173Role.SubroutineModule.TryGetSubroutine(out Scp173TantrumAbility scp173TantrumAbility))
tantrumPrefab = scp173TantrumAbility._tantrumPrefab;
}
return tantrumPrefab;
}
}
/// <summary>
/// Gets the amnestic cloud prefab.
/// </summary>
public static Scp939AmnesticCloudInstance AmnesticCloudPrefab
{
get
{
if (amnesticCloudPrefab == null)
{
Scp939GameRole scp939Role = (Scp939GameRole)RoleTypeId.Scp939.GetRoleBase();
if (scp939Role.SubroutineModule.TryGetSubroutine(out Scp939AmnesticCloudAbility ability))
amnesticCloudPrefab = ability._instancePrefab;
}
return amnesticCloudPrefab;
}
}
/// <summary>
/// Gets a value indicating whether decontamination has begun in the light containment zone.
/// </summary>
public static bool IsLczDecontaminated => DecontaminationController.Singleton.IsDecontaminating;
/// <summary>
/// Gets a value indicating whether decontamination phase is in the light containment zone.
/// </summary>
public static DecontaminationState DecontaminationState =>
DecontaminationController.Singleton.NetworkDecontaminationOverride is DecontaminationController.DecontaminationStatus.Disabled ?
DecontaminationState.Disabled : (DecontaminationState)DecontaminationController.Singleton._nextPhase;
/// <summary>
/// Gets all <see cref="PocketDimensionTeleport"/> objects.
/// </summary>
public static ReadOnlyCollection<PocketDimensionTeleport> PocketDimensionTeleports { get; } = TeleportsValue.AsReadOnly();
/// <summary>
/// Gets all <see cref="Locker"/> objects.
/// </summary>
public static ReadOnlyCollection<Locker> Lockers { get; } = LockersValue.AsReadOnly();
/// <summary>
/// Gets all <see cref="AdminToy"/> objects.
/// </summary>
public static ReadOnlyCollection<AdminToy> Toys { get; } = ToysValue.AsReadOnly();
/// <summary>
/// Gets or sets the current seed of the map.
/// </summary>
public static int Seed
{
get => SeedSynchronizer.Seed;
set
{
if (!SeedSynchronizer.MapGenerated)
SeedSynchronizer._singleton.Network_syncSeed = value;
}
}
/// <summary>
/// Gets the <see cref="global::AmbientSoundPlayer"/>.
/// </summary>
public static AmbientSoundPlayer AmbientSoundPlayer => ambientSoundPlayer ??= ReferenceHub.HostHub.GetComponent<AmbientSoundPlayer>();
/// <summary>
/// Broadcasts a message to all <see cref="Player">players</see>.
/// </summary>
/// <param name="broadcast">The <see cref="Features.Broadcast"/> to be broadcasted.</param>
/// <param name="shouldClearPrevious">Clears all players' broadcasts before sending the new one.</param>
public static void Broadcast(Broadcast broadcast, bool shouldClearPrevious = false)
{
if (broadcast.Show)
Broadcast(broadcast.Duration, broadcast.Content, broadcast.Type, shouldClearPrevious);
}
/// <summary>
/// Broadcasts a message to all <see cref="Player">players</see>.
/// </summary>
/// <param name="duration">The duration in seconds.</param>
/// <param name="message">The message that will be broadcast (supports Unity Rich Text formatting).</param>
/// <param name="type">The broadcast type.</param>
/// <param name="shouldClearPrevious">Clears all players' broadcasts before sending the new one.</param>
public static void Broadcast(ushort duration, string message, global::Broadcast.BroadcastFlags type = global::Broadcast.BroadcastFlags.Normal, bool shouldClearPrevious = false)
{
if (shouldClearPrevious)
ClearBroadcasts();
Server.Broadcast.RpcAddElement(message, duration, type);
}
/// <summary>
/// Shows a hint to all <see cref="Player">players</see>.
/// </summary>
/// <param name="message">The message that will be broadcasted (supports Unity Rich Text formatting).</param>
/// <param name="duration">The duration in seconds.</param>
public static void ShowHint(string message, float duration = 3f)
{
foreach (Player player in Player.List)
player.ShowHint(message, duration);
}
/// <summary>
/// Clears all <see cref="Player">players</see>' broadcasts.
/// </summary>
public static void ClearBroadcasts() => Server.Broadcast.RpcClearElements();
/// <summary>
/// Starts the light containment zone decontamination process.
/// </summary>
public static void StartDecontamination() => DecontaminationController.Singleton.ForceDecontamination();
/// <summary>
/// Turns off all lights in the facility.
/// </summary>
/// <param name="duration">The duration of the blackout.</param>
/// <param name="zoneTypes">The <see cref="ZoneType"/>s to affect.</param>
public static void TurnOffAllLights(float duration, ZoneType zoneTypes = ZoneType.Unspecified)
{
foreach (RoomLightController controller in RoomLightController.Instances)
{
Room room = controller.GetComponentInParent<Room>();
if (room == null)
continue;
if (zoneTypes == ZoneType.Unspecified || room.Zone.HasFlag(zoneTypes))
controller.ServerFlickerLights(duration);
}
}
/// <summary>
/// Turns off all lights in the facility.
/// </summary>
/// <param name="duration">The duration of the blackout.</param>
/// <param name="zoneTypes">The <see cref="ZoneType"/>s to affect.</param>
public static void TurnOffAllLights(float duration, IEnumerable<ZoneType> zoneTypes)
{
foreach (ZoneType zone in zoneTypes)
TurnOffAllLights(duration, zone);
}
/// <summary>
/// Changes the <see cref="Color"/> of all lights in the facility.
/// </summary>
/// <param name="color">The new <see cref="Color"/> of the lights.</param>
public static void ChangeLightsColor(Color color)
{
foreach (RoomLightController light in RoomLightController.Instances)
light.NetworkOverrideColor = color;
}
/// <summary>
/// Resets the <see cref="Color">color</see> of all lights in the facility.
/// </summary>
public static void ResetLightsColor()
{
foreach (RoomLightController light in RoomLightController.Instances)
light.NetworkOverrideColor = Color.clear;
}
/// <summary>
/// Gets a random <see cref="Locker"/>.
/// </summary>
/// <returns><see cref="Locker"/> object.</returns>
public static Locker GetRandomLocker() => Lockers.GetRandomValue();
/// <summary>
/// Gets a random <see cref="Pickup"/>.
/// </summary>
/// <param name="type">Filters by <see cref="ItemType"/>.</param>
/// <returns><see cref="Pickup"/> object.</returns>
public static Pickup GetRandomPickup(ItemType type = ItemType.None)
{
List<Pickup> pickups = (type != ItemType.None ? Pickup.List.Where(p => p.Type == type) : Pickup.List).ToList();
return pickups.GetRandomValue();
}
/// <summary>
/// Plays a random ambient sound.
/// </summary>
public static void PlayAmbientSound() => AmbientSoundPlayer.GenerateRandom();
/// <summary>
/// Plays an ambient sound.
/// </summary>
/// <param name="id">The id of the sound to play.</param>
public static void PlayAmbientSound(int id)
{
if (id >= AmbientSoundPlayer.clips.Length)
throw new IndexOutOfRangeException($"There are only {AmbientSoundPlayer.clips.Length} sounds available.");
AmbientSoundPlayer.RpcPlaySound(AmbientSoundPlayer.clips[id].index);
}
/// <summary>
/// Places a Tantrum (SCP-173's ability) in the indicated position.
/// </summary>
/// <param name="position">The position where you want to spawn the Tantrum.</param>
/// <param name="isActive">Whether or not the tantrum will apply the <see cref="EffectType.Stained"/> effect.</param>
/// <remarks>If <paramref name="isActive"/> is <see langword="true"/>, the tantrum is moved slightly up from its original position. Otherwise, the collision will not be detected and the slowness will not work.</remarks>
/// <returns>The <see cref="TantrumHazard"/> instance.</returns>
public static TantrumHazard PlaceTantrum(Vector3 position, bool isActive = true)
{
TantrumEnvironmentalHazard tantrum = Object.Instantiate(TantrumPrefab);
if (!isActive)
tantrum.SynchronizedPosition = new RelativePosition(position);
else
tantrum.SynchronizedPosition = new RelativePosition(position + (Vector3.up * 0.25f));
tantrum._destroyed = !isActive;
NetworkServer.Spawn(tantrum.gameObject);
return Hazard.Get(tantrum).Cast<TantrumHazard>();
}
/// <summary>
/// Destroy all <see cref="ItemPickupBase"/> objects.
/// </summary>
public static void CleanAllItems()
{
foreach (Pickup pickup in Pickup.List.ToList())
pickup.Destroy();
}
/// <summary>
/// Destroy all the <see cref="Pickup"/> objects from the specified list.
/// </summary>
/// <param name="pickups">The List of pickups to destroy.</param>
public static void CleanAllItems(IEnumerable<Pickup> pickups)
{
foreach (Pickup pickup in pickups)
pickup.Destroy();
}
/// <summary>
/// Destroy all <see cref="BasicRagdoll"/> objects.
/// </summary>
public static void CleanAllRagdolls()
{
foreach (Ragdoll ragDoll in Ragdoll.List.ToList())
ragDoll.Destroy();
}
/// <summary>
/// Destroy all <see cref="Ragdoll"/> objects from the specified list.
/// </summary>
/// <param name="ragDolls">The List of RagDolls to destroy.</param>
public static void CleanAllRagdolls(IEnumerable<Ragdoll> ragDolls)
{
foreach (Ragdoll ragDoll in ragDolls)
ragDoll.Destroy();
}
/// <summary>
/// Places a blood decal.
/// </summary>
/// <param name="position">The position of the blood decal.</param>
/// <param name="direction">The direction of the blood decal.</param>
public static void PlaceBlood(Vector3 position, Vector3 direction) => new GunDecalMessage(position, direction, DecalPoolType.Blood).SendToAuthenticated(0);
/// <summary>
/// Gets all the near cameras.
/// </summary>
/// <param name="position">The position from which starting to search cameras.</param>
/// <param name="toleration">The maximum toleration to define the radius from which get the cameras.</param>
/// <returns>A <see cref="IEnumerable{T}"/> of <see cref="Camera"/> which contains all the found cameras.</returns>
public static IEnumerable<Camera> GetNearCameras(Vector3 position, float toleration = 15f)
=> Camera.Get(cam => (position - cam.Position).sqrMagnitude <= toleration * toleration);
/// <summary>
/// Explode.
/// </summary>
/// <param name="position">The position where explosion will be created.</param>
/// <param name="projectileType">The projectile that will create the explosion.</param>
/// <param name="attacker">The player who create the explosion.</param>
public static void Explode(Vector3 position, ProjectileType projectileType, Player attacker = null)
{
ItemType item;
if ((item = projectileType.GetItemType()) is ItemType.None)
return;
attacker ??= Server.Host;
if (!InventoryItemLoader.TryGetItem(item, out ThrowableItem throwableItem))
return;
if (Object.Instantiate(throwableItem.Projectile) is not TimeGrenade timedGrenadePickup)
return;
if (timedGrenadePickup is Scp018Projectile scp018Projectile)
scp018Projectile.SetupModule();
else
ExplodeEffect(position, projectileType);
timedGrenadePickup.Position = position;
timedGrenadePickup.PreviousOwner = (attacker ?? Server.Host).Footprint;
timedGrenadePickup.ServerFuseEnd();
}
/// <summary>
/// Spawn projectile effect.
/// </summary>
/// <param name="position">The position where effect will be created.</param>
/// <param name="projectileType">The projectile that will create the effect.</param>
public static void ExplodeEffect(Vector3 position, ProjectileType projectileType)
{
ItemType item;
if ((item = projectileType.GetItemType()) is ItemType.None)
return;
ExplosionUtils.ServerSpawnEffect(position, item);
}
/// <summary>
/// Clears the lazy loading game object cache.
/// </summary>
internal static void ClearCache()
{
Item.BaseToItem.Clear();
LockersValue.RemoveAll(locker => locker == null);
Ragdoll.BasicRagdollToRagdoll.Clear();
Firearm.ItemTypeToFirearmInstance.Clear();
Firearm.BaseCodesValue.Clear();
Firearm.AvailableAttachmentsValue.Clear();
}
}
}