Skip to content

Commit 3095f1c

Browse files
Camera toy wrapper (northwood-studios#229)
* Isolated room creation from base game. * Added camera toy wrapper * Added camera property to camera toy wapper --------- Co-authored-by: ced777ric <44529860+ced777ric@users.noreply.github.com>
1 parent 5e4095c commit 3095f1c

5 files changed

Lines changed: 242 additions & 12 deletions

File tree

LabApi/Features/Wrappers/AdminToys/AdminToy.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ internal static void Initialize()
2727
Register<ShootingTarget>(x => new ShootingTargetToy(x));
2828
Register<AdminToys.SpeakerToy>(x => new SpeakerToy(x));
2929
Register<InvisibleInteractableToy>(x => new InteractableToy(x));
30+
Register<Scp079CameraToy>(x => new CameraToy(x));
3031
}
3132

3233
/// <summary>
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
using AdminToys;
2+
using System.Collections.Generic;
3+
using System.Diagnostics.CodeAnalysis;
4+
using UnityEngine;
5+
6+
namespace LabApi.Features.Wrappers;
7+
8+
/// <summary>
9+
/// The wrapper for the <see cref="Scp079CameraToy"/> class.
10+
/// </summary>
11+
public class CameraToy : AdminToy
12+
{
13+
/// <summary>
14+
/// Contains all the camera toys, accessible through their <see cref="Base"/>.
15+
/// </summary>
16+
public new static Dictionary<Scp079CameraToy, CameraToy> Dictionary { get; } = [];
17+
18+
/// <summary>
19+
/// A reference to all instances of <see cref="CameraToy"/>.
20+
/// </summary>
21+
public new static IReadOnlyCollection<CameraToy> List => Dictionary.Values;
22+
23+
/// <summary>
24+
/// An internal constructor to prevent external instantiation.
25+
/// </summary>
26+
/// <param name="baseCameraToy">The base <see cref="Scp079CameraToy"/> object.</param>
27+
internal CameraToy(Scp079CameraToy baseCameraToy)
28+
: base(baseCameraToy)
29+
{
30+
Dictionary.Add(baseCameraToy, this);
31+
Base = baseCameraToy;
32+
}
33+
34+
/// <summary>
35+
/// An internal method to remove itself from the cache when the base object is destroyed.
36+
/// </summary>
37+
internal override void OnRemove()
38+
{
39+
base.OnRemove();
40+
Dictionary.Remove(Base);
41+
}
42+
43+
/// <summary>
44+
/// The <see cref="Scp079CameraToy"/> object.
45+
/// </summary>
46+
public new Scp079CameraToy Base { get; }
47+
48+
/// <summary>
49+
/// The camera instance associated with this toy.
50+
/// </summary>
51+
public Camera Camera => Camera.Get(Base.Camera);
52+
53+
/// <summary>
54+
/// Gets or sets the label of the camera displayed to SCP-079 on HUD.
55+
/// </summary>
56+
public string Label
57+
{
58+
get => Base.Label;
59+
set => Base.NetworkLabel = value;
60+
}
61+
62+
/// <summary>
63+
/// Gets or sets the room associated with this camera.
64+
/// </summary>
65+
/// <remarks>
66+
/// Room will never be <see langword="null"/>.
67+
/// This determines what cameras are visible to SCP-079 for what room.
68+
/// </remarks>
69+
public Room Room
70+
{
71+
get => Room.Get(Base.Room);
72+
set => Base.NetworkRoom = value.Base;
73+
}
74+
75+
/// <summary>
76+
/// Gets or sets how high and low the camera can move from its initial rotation in degrees.
77+
/// </summary>
78+
/// <remarks>
79+
/// X should be less than or equal to y. e.g. <c>Vector2(-10, 30)</c> means you can look up 10 degrees and down 30.
80+
/// </remarks>
81+
public Vector2 VerticalConstraints
82+
{
83+
get => Base.VerticalConstraint;
84+
set => Base.NetworkVerticalConstraint = value;
85+
}
86+
87+
/// <summary>
88+
/// Gets or sets how left and right the camera can move from its initial rotation in degrees.
89+
/// </summary>
90+
/// <remarks>
91+
/// X should be less than or equal to y. e.g. <c>Vector2(-10, 30)</c> means you can look left 10 degrees and right 30.
92+
/// </remarks>
93+
public Vector2 HorizontalConstraint
94+
{
95+
get => Base.HorizontalConstraint;
96+
set => Base.NetworkHorizontalConstraint = value;
97+
}
98+
99+
/// <summary>
100+
/// Gets or set the min and max zoom level of the camera.
101+
/// </summary>
102+
/// <remarks>
103+
/// Values range from 0.0 to 1.0, with zero being the minimum zoom, and 1 being the maximum zoom.
104+
/// X should be less than or equal to y.
105+
/// </remarks>
106+
public Vector2 ZoomConstraints
107+
{
108+
get => Base.ZoomConstraint;
109+
set => Base.NetworkZoomConstraint = value;
110+
}
111+
112+
/// <summary>
113+
/// Creates a new camera toy.
114+
/// </summary>
115+
/// <param name="parent">The parent transform.</param>
116+
/// <param name="networkSpawn">Whether to spawn the toy on the client.</param>
117+
/// <returns>The created camera toy.</returns>
118+
public static CameraToy Create(Transform? parent = null, bool networkSpawn = true)
119+
=> Create(Vector3.zero, parent, networkSpawn);
120+
121+
/// <summary>
122+
/// Creates a new camera toy.
123+
/// </summary>
124+
/// <param name="position">The initial local position.</param>
125+
/// <param name="parent">The parent transform.</param>
126+
/// <param name="networkSpawn">Whether to spawn the toy on the client.</param>
127+
/// <returns>The created camera toy.</returns>
128+
public static CameraToy Create(Vector3 position, Transform? parent = null, bool networkSpawn = true)
129+
=> Create(position, Quaternion.identity, parent, networkSpawn);
130+
131+
/// <summary>
132+
/// Creates a new camera toy.
133+
/// </summary>
134+
/// <param name="position">The initial local position.</param>
135+
/// <param name="rotation">The initial local rotation.</param>
136+
/// <param name="parent">The parent transform.</param>
137+
/// <param name="networkSpawn">Whether to spawn the toy on the client.</param>
138+
/// <returns>The created camera toy.</returns>
139+
public static CameraToy Create(Vector3 position, Quaternion rotation, Transform? parent = null, bool networkSpawn = true)
140+
=> Create(position, rotation, Vector3.one, parent, networkSpawn);
141+
142+
/// <summary>
143+
/// Creates a new camera toy.
144+
/// </summary>
145+
/// <param name="position">The initial local position.</param>
146+
/// <param name="rotation">The initial local rotation.</param>
147+
/// <param name="scale">The initial local scale.</param>
148+
/// <param name="parent">The parent transform.</param>
149+
/// <param name="networkSpawn">Whether to spawn the toy on the client.</param>
150+
/// <returns>The created camera toy.</returns>
151+
public static CameraToy Create(Vector3 position, Quaternion rotation, Vector3 scale, Transform? parent = null, bool networkSpawn = true)
152+
{
153+
CameraToy toy = Get(Create<Scp079CameraToy>(position, rotation, scale, parent));
154+
155+
if (networkSpawn)
156+
toy.Spawn();
157+
158+
return toy;
159+
}
160+
161+
/// <summary>
162+
/// Gets the camera toy wrapper from the <see cref="Dictionary"/> or creates a new one if it doesn't exist and the provided <see cref="Scp079CameraToy"/> was not <see langword="null"/>.
163+
/// </summary>
164+
/// <param name="baseCameraToy">The <see cref="Base"/> of the camera toy.</param>
165+
/// <returns>The requested camera toy or <see langword="null"/>.</returns>
166+
[return: NotNullIfNotNull(nameof(baseCameraToy))]
167+
public static CameraToy? Get(Scp079CameraToy? baseCameraToy)
168+
{
169+
if (baseCameraToy == null)
170+
return null;
171+
172+
return Dictionary.TryGetValue(baseCameraToy, out CameraToy item) ? item : (CameraToy)CreateAdminToyWrapper(baseCameraToy);
173+
}
174+
175+
/// <summary>
176+
/// Tries to get the camera toy wrapper from the <see cref="Dictionary"/>.
177+
/// </summary>
178+
/// <param name="baseCameraToy">The <see cref="Base"/> of the camera toy.</param>
179+
/// <param name="cameraToy">The requested camera toy.</param>
180+
/// <returns>True if the camera toy exists, otherwise false.</returns>
181+
public static bool TryGet(Scp079CameraToy? baseCameraToy, [NotNullWhen(true)] out CameraToy? cameraToy)
182+
{
183+
cameraToy = Get(baseCameraToy);
184+
return cameraToy != null;
185+
}
186+
}

LabApi/Features/Wrappers/Facility/Rooms/PocketDimension/PocketDimension.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ public static float MaxPocketItemTriggerDelay
7171
internal PocketDimension(RoomIdentifier room)
7272
: base(room)
7373
{
74-
Instance = this;
74+
if (CanCache)
75+
Instance = this;
7576
}
7677

7778
/// <summary>

LabApi/Features/Wrappers/Facility/Rooms/Room.cs

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ internal static void Initialize()
3939
/// <param name="roomIdentifier">The identifier of the room.</param>
4040
internal Room(RoomIdentifier roomIdentifier)
4141
{
42-
Dictionary.Add(roomIdentifier, this);
4342
Base = roomIdentifier;
43+
44+
if (CanCache)
45+
Dictionary.Add(roomIdentifier, this);
4446
}
4547

4648
/// <summary>
@@ -56,6 +58,11 @@ internal virtual void OnRemoved()
5658
/// </summary>
5759
public RoomIdentifier Base { get; }
5860

61+
/// <summary>
62+
/// Gets whether the base room instance was destroyed.
63+
/// </summary>
64+
public bool IsDestroyed => Base == null || GameObject == null;
65+
5966
/// <summary>
6067
/// The room's shape.
6168
/// </summary>
@@ -124,20 +131,26 @@ public IEnumerable<Door> Doors
124131
/// </summary>
125132
public IEnumerable<Player> Players => Player.List.Where(p => p.Room == this);
126133

134+
/// <summary>
135+
/// Gets whether the room wrapper is allowed to be cached.
136+
/// </summary>
137+
protected bool CanCache => !IsDestroyed;
138+
127139
/// <summary>
128140
/// Gets the room wrapper from the <see cref="Dictionary"/>, or creates a new one if it doesn't exist.
129141
/// </summary>
130142
/// <param name="roomIdentifier">The identifier of the room.</param>
131143
/// <returns>The requested room.</returns>
132-
public static Room? Get(RoomIdentifier roomIdentifier)
144+
[return: NotNullIfNotNull(nameof(roomIdentifier))]
145+
public static Room? Get(RoomIdentifier? roomIdentifier)
133146
{
134147
if (roomIdentifier == null)
135148
return null;
136149

137-
if (!Dictionary.TryGetValue(roomIdentifier, out Room room))
138-
return null;
150+
if (Dictionary.TryGetValue(roomIdentifier, out Room room))
151+
return room;
139152

140-
return room;
153+
return CreateRoomWrapper(roomIdentifier);
141154
}
142155

143156
/// <summary>
@@ -208,14 +221,36 @@ public static bool TryGetRoomAtPosition(Vector3 position, [NotNullWhen(true)] ou
208221
/// <returns>The room at the specified position or <see langword="null"/> if no room was found.</returns>
209222
public static Room? GetRoomAtPosition(Vector3 position) => TryGetRoomAtPosition(position, out Room? room) ? room : null;
210223

224+
/// <summary>
225+
/// Creates a new wrapper from the base room object.
226+
/// </summary>
227+
/// <param name="roomIdentifier">The base <see cref="RoomIdentifier"/> object.</param>
228+
/// <returns>The newly created wrapper.</returns>
229+
protected static Room CreateRoomWrapper(RoomIdentifier roomIdentifier)
230+
{
231+
if (roomIdentifier.Name == RoomName.Pocket)
232+
return new PocketDimension(roomIdentifier);
233+
else if (roomIdentifier.Name == RoomName.Lcz914)
234+
return new Scp914(roomIdentifier);
235+
else
236+
return new Room(roomIdentifier);
237+
}
238+
211239
/// <summary>
212240
/// Handles the creation of a room in the server.
213241
/// </summary>
214242
/// <param name="roomIdentifier">The <see cref="RoomIdentifier"/> of the room.</param>
215-
// TODO: use factory instead.
216243
private static void AddRoom(RoomIdentifier roomIdentifier)
217244
{
218-
_ = roomIdentifier.Name == RoomName.Pocket ? new PocketDimension(roomIdentifier) : new Room(roomIdentifier);
245+
try
246+
{
247+
if (!Dictionary.ContainsKey(roomIdentifier))
248+
_ = CreateRoomWrapper(roomIdentifier);
249+
}
250+
catch (System.Exception e)
251+
{
252+
Console.Logger.InternalError($"Failed to handle room creation with error: {e}");
253+
}
219254
}
220255

221256
/// <summary>
@@ -224,7 +259,14 @@ private static void AddRoom(RoomIdentifier roomIdentifier)
224259
/// <param name="roomIdentifier">The <see cref="RoomIdentifier"/> of the room.</param>
225260
private static void RemoveRoom(RoomIdentifier roomIdentifier)
226261
{
227-
if (Dictionary.TryGetValue(roomIdentifier, out Room room))
228-
room.OnRemoved();
262+
try
263+
{
264+
if (Dictionary.TryGetValue(roomIdentifier, out Room room))
265+
room.OnRemoved();
266+
}
267+
catch (System.Exception e)
268+
{
269+
Console.Logger.InternalError($"Failed to handle item destruction with error: {e}");
270+
}
229271
}
230272
}

LabApi/Features/Wrappers/Facility/Rooms/Scp914/Scp914.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using InventorySystem;
22
using InventorySystem.Items;
3-
using InventorySystem.Items.Pickups;
43
using LabApi.Features.Interfaces;
54
using MapGeneration;
65
using Scp914;
@@ -37,7 +36,8 @@ public class Scp914 : Room
3736
internal Scp914(RoomIdentifier roomIdentifier)
3837
: base(roomIdentifier)
3938
{
40-
Instance = this;
39+
if (CanCache)
40+
Instance = this;
4141
}
4242

4343
/// <summary>

0 commit comments

Comments
 (0)