Skip to content
3 changes: 2 additions & 1 deletion Features/Enums/ToolGunObjectType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ public enum ToolGunObjectType
Capybara = 5,
Schematic = 6,
Scp079Camera = 7,
ShootingTarget = 8
ShootingTarget = 8,
Teleporter = 9,
}
140 changes: 140 additions & 0 deletions Features/Objects/TeleporterObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using GameCore;
using LabApi.Features.Wrappers;
using Mirror;
using ProjectMER.Features.Serializable;
using UnityEngine;
using MEC;

namespace ProjectMER.Features.Objects;

public class TeleporterObject : MonoBehaviour
{
// TODO:
// Implement OnTeleporting event
// Implement conditional teleport
// Implement pickup teleport
// Implement chance-based target select.

/// <summary>
/// Gets a <see cref="DateTime"/> indicating when this teleporter will next be usable.
/// </summary>
public DateTime WhenWillBeUsable { get; private set; }

/// <summary>
/// Gets a value indicating whether this teleporter is currently usable.
/// </summary>
public bool IsUsable => DateTime.Now > WhenWillBeUsable;

/// <summary>
/// Gets or sets the base <see cref="SerializableTeleporter"/> for this object.
/// </summary>
public SerializableTeleporter Base { get; set; }

/// <summary>
/// Gets or sets the global position of the object.
/// </summary>
public Vector3 Position
{
get => transform.position;
set
{
transform.position = value;
}
}

/// <summary>
/// Gets or sets the global rotation of the object.
/// </summary>
public Quaternion Rotation
{
get => transform.rotation;
set
{
transform.rotation = value;
}
}

/// <summary>
/// Gets or sets the global euler angles of the object.
/// </summary>
public Vector3 EulerAngles
{
get => Rotation.eulerAngles;
set => Rotation = Quaternion.Euler(value);
}

/// <summary>
/// Gets or sets the scale of the object.
/// </summary>
public Vector3 Scale
{
get => transform.localScale;
set
{
transform.localScale = value;
}
}

/// <summary>
/// Gets a Dictionary to access teleporters by their ID.
/// </summary>
internal static List<TeleporterObject> Teleporters { get; private set; } = new ();

private TeleporterObject GetTarget()
{
return Teleporters.FirstOrDefault(t => t.Base.Id == Base.Targets.RandomItem());
}

private bool TryGetTarget(out TeleporterObject teleporterObject)
{
teleporterObject = GetTarget();

return teleporterObject != null;
}

private void OnTriggerEnter(Collider other)
{
if (!IsUsable || !CanBeTeleported(other) || !TryGetTarget(out TeleporterObject target))
{
return;
}

Player? player = Player.Get(other.gameObject);
if (player is null)
{
return;
}

WhenWillBeUsable = DateTime.Now.AddSeconds(Base.Cooldown);
target.WhenWillBeUsable = DateTime.Now.AddSeconds(target.Base.Cooldown);


Timing.CallDelayed(0.05f, () =>
{
player.Position = target.Position;
try
{
player.LookRotation = target.EulerAngles;
}
catch (Exception e)
{
Logger.Error(e);
}
});
}

private void Start()
{
Teleporters.Add(this);
}

private void OnDestroy()
{
Teleporters.Remove(this);
}

private bool CanBeTeleported(Collider collider)
{
return true;
}
}
14 changes: 12 additions & 2 deletions Features/Serializable/MapSchematic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public MapSchematic(string mapName)
public Dictionary<string, SerializableShootingTarget> ShootingTargets { get; set; } = [];

public Dictionary<string, SerializableSchematic> Schematics { get; set; } = [];

public Dictionary<string, SerializableTeleporter> Teleporters { get; set; } = [];

public List<MapEditorObject> SpawnedObjects = [];

Expand All @@ -51,6 +53,7 @@ public MapSchematic Merge(MapSchematic other)
Schematics.AddRange(other.Schematics);
Scp079Cameras.AddRange(other.Scp079Cameras);
ShootingTargets.AddRange(other.ShootingTargets);
Teleporters.AddRange(other.Teleporters);

return this;
}
Expand Down Expand Up @@ -81,6 +84,7 @@ public void Reload()
Schematics.ForEach(kVP => SpawnObject(kVP.Key, kVP.Value));
Scp079Cameras.ForEach(kVP => SpawnObject(kVP.Key, kVP.Value));
ShootingTargets.ForEach(kVP => SpawnObject(kVP.Key, kVP.Value));
Teleporters.ForEach(kVP => SpawnObject(kVP.Key, kVP.Value));
}

public void SpawnObject<T>(string id, T serializableObject) where T : SerializableObject
Expand Down Expand Up @@ -136,13 +140,16 @@ public bool TryAddElement<T>(string id, T serializableObject) where T : Serializ

if (Schematics.TryAdd(id, serializableObject))
return true;
if (Scp079Cameras.TryAdd(id, serializableObject))
return true;

if (ShootingTargets.TryAdd(id, serializableObject))
return true;


if (Teleporters.TryAdd(id, serializableObject))
return true;

return false;
}

Expand All @@ -168,6 +175,9 @@ public bool TryRemoveElement(string id)

if (Schematics.Remove(id))
return true;

if (Teleporters.Remove(id))
return true;

if (Scp079Cameras.Remove(id))
return true;
Expand Down
140 changes: 140 additions & 0 deletions Features/Serializable/SerializableTeleporter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using AdminToys;
using LabApi.Features.Wrappers;
using Mirror;
using PlayerRoles;
using ProjectMER.Features.Extensions;
using ProjectMER.Features.Interfaces;
using ProjectMER.Features.Objects;
using UnityEngine;
using PrimitiveObjectToy = AdminToys.PrimitiveObjectToy;

namespace ProjectMER.Features.Serializable;

public class SerializableTeleporter : SerializableObject, IIndicatorDefinition
{
// TODO:
// Add proper ID management
private int teleporterId;

/// <summary>
/// Gets or sets the teleporter ID for this teleporter.
/// </summary>
public int Id
{
get => teleporterId;
set
{
teleporterId = value;
}
}

public List<int> Targets { get; set; } = new List<int>();

public float Cooldown { get; set; } = 10f;

public override GameObject SpawnOrUpdateObject(Room? room = null, GameObject? instance = null)
{
GameObject primitive = instance == null ? GameObject.CreatePrimitive(PrimitiveType.Cube) : instance;
Vector3 position = room.GetAbsolutePosition(Position);
Quaternion rotation = room.GetAbsoluteRotation(Rotation);
_prevIndex = Index;

primitive.transform.SetPositionAndRotation(position, rotation);
primitive.transform.localScale = Scale;

if (!primitive.TryGetComponent(out TeleporterObject teleporter))
{
teleporter = primitive.AddComponent<TeleporterObject>();
}

teleporter.Base = this;

if (primitive.TryGetComponent(out BoxCollider collider))
{
collider.isTrigger = true;
}

return primitive;
}

public GameObject SpawnOrUpdateIndicator(Room room, GameObject? instance = null)
{
PrimitiveObjectToy root;
PrimitiveObjectToy trigger;
PrimitiveObjectToy cylinder;
PrimitiveObjectToy arrowY;
PrimitiveObjectToy arrowX;
PrimitiveObjectToy arrow;

Vector3 position = room.GetAbsolutePosition(Position - new Vector3(0, 0.5f, 0));
Quaternion rotation = room.GetAbsoluteRotation(Rotation);

if (instance == null)
{
root = UnityEngine.Object.Instantiate(PrefabManager.PrimitiveObjectPrefab);
root.NetworkPrimitiveFlags = PrimitiveFlags.None;
root.name = "Indicator";
root.transform.position = position;

trigger = UnityEngine.Object.Instantiate(PrefabManager.PrimitiveObjectPrefab);
trigger.NetworkPrimitiveFlags = PrimitiveFlags.Visible;
trigger.name = "Trigger";
trigger.NetworkPrimitiveType = PrimitiveType.Cube;
trigger.transform.localScale = Scale;
trigger.transform.position = position + new Vector3(0, 0.5f, 0);
trigger.transform.parent = root.transform;

cylinder = GameObject.Instantiate(PrefabManager.PrimitiveObjectPrefab, root.transform);
cylinder.transform.localPosition = Vector3.zero;
cylinder.NetworkPrimitiveType = PrimitiveType.Cylinder;
cylinder.NetworkPrimitiveFlags = PrimitiveFlags.Visible;
cylinder.transform.localScale = new Vector3(1f, 0.001f, 1f);

arrowY = UnityEngine.Object.Instantiate(PrefabManager.PrimitiveObjectPrefab);
arrowY.NetworkPrimitiveFlags = PrimitiveFlags.None;
arrowY.name = "Arrow Y Axis";
arrowY.transform.parent = root.transform;

arrowX = UnityEngine.Object.Instantiate(PrefabManager.PrimitiveObjectPrefab);
arrowX.NetworkPrimitiveFlags = PrimitiveFlags.None;
arrowX.name = "Arrow X Axis";
arrowX.transform.parent = arrowY.transform;

arrow = GameObject.Instantiate(PrefabManager.PrimitiveObjectPrefab, arrowX.transform);
arrow.transform.localPosition = root.transform.forward;
arrow.NetworkPrimitiveType = PrimitiveType.Cube;
arrow.NetworkPrimitiveFlags = PrimitiveFlags.Visible;
arrow.transform.localScale = new Vector3(0.1f, 0.1f, 1f);
}
else
{
root = instance.GetComponent<PrimitiveObjectToy>();

trigger = root.transform.Find("Trigger").GetComponent<PrimitiveObjectToy>();
arrowY = root.transform.Find("Arrow Y Axis").GetComponent<PrimitiveObjectToy>();
arrowX = arrowY.transform.Find("Arrow X Axis").GetComponent<PrimitiveObjectToy>();

trigger.transform.localScale = Scale;
}

root.transform.position = position;
arrowY.transform.localPosition = Vector3.up * 1.6f;
arrowY.transform.localEulerAngles = new Vector3(0f, rotation.eulerAngles.y, 0f);
arrowX.transform.localPosition = Vector3.zero;
arrowX.transform.localEulerAngles = new Vector3(-rotation.eulerAngles.x, 0f, 0f);

foreach (PrimitiveObjectToy primitive in root.GetComponentsInChildren<PrimitiveObjectToy>())
{
if (Targets.Count > 0)
{
primitive.NetworkMaterialColor = new Color(0.11f, 0.98f, 0.92f, 0.5f);
}
else
{
primitive.NetworkMaterialColor = new Color(1f, 1f, 1f, 0.25f);
}
}

return root.gameObject;
}
}
6 changes: 6 additions & 0 deletions Features/ToolGun/ToolGunHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ public static void CreateObject(Vector3 position, ToolGunObjectType objectType,
break;
}

case SerializableTeleporter _:
{
serializableObject.Position = position + Vector3.up * 0.5f;
break;
}

case SerializableSchematic serializableSchematic:
{
serializableObject.Position = position;
Expand Down
1 change: 1 addition & 0 deletions Features/ToolGun/ToolGunItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class ToolGunItem
{ ToolGunObjectType.Schematic, typeof(SerializableSchematic) },
{ ToolGunObjectType.Scp079Camera, typeof(SerializableScp079Camera) },
{ ToolGunObjectType.ShootingTarget, typeof(SerializableShootingTarget) },
{ ToolGunObjectType.Teleporter, typeof(SerializableTeleporter) },
};

private ToolGunObjectType _selectedObjectToSpawn;
Expand Down