diff --git a/Features/Enums/ToolGunObjectType.cs b/Features/Enums/ToolGunObjectType.cs index b1b1b9d..e414ff9 100644 --- a/Features/Enums/ToolGunObjectType.cs +++ b/Features/Enums/ToolGunObjectType.cs @@ -10,5 +10,6 @@ public enum ToolGunObjectType Capybara = 5, Schematic = 6, Scp079Camera = 7, - ShootingTarget = 8 + ShootingTarget = 8, + Teleporter = 9, } diff --git a/Features/Objects/TeleporterObject.cs b/Features/Objects/TeleporterObject.cs new file mode 100644 index 0000000..33636a0 --- /dev/null +++ b/Features/Objects/TeleporterObject.cs @@ -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. + + /// + /// Gets a indicating when this teleporter will next be usable. + /// + public DateTime WhenWillBeUsable { get; private set; } + + /// + /// Gets a value indicating whether this teleporter is currently usable. + /// + public bool IsUsable => DateTime.Now > WhenWillBeUsable; + + /// + /// Gets or sets the base for this object. + /// + public SerializableTeleporter Base { get; set; } + + /// + /// Gets or sets the global position of the object. + /// + public Vector3 Position + { + get => transform.position; + set + { + transform.position = value; + } + } + + /// + /// Gets or sets the global rotation of the object. + /// + public Quaternion Rotation + { + get => transform.rotation; + set + { + transform.rotation = value; + } + } + + /// + /// Gets or sets the global euler angles of the object. + /// + public Vector3 EulerAngles + { + get => Rotation.eulerAngles; + set => Rotation = Quaternion.Euler(value); + } + + /// + /// Gets or sets the scale of the object. + /// + public Vector3 Scale + { + get => transform.localScale; + set + { + transform.localScale = value; + } + } + + /// + /// Gets a Dictionary to access teleporters by their ID. + /// + internal static List 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; + } +} diff --git a/Features/Serializable/MapSchematic.cs b/Features/Serializable/MapSchematic.cs index 5973ef6..8cbd231 100644 --- a/Features/Serializable/MapSchematic.cs +++ b/Features/Serializable/MapSchematic.cs @@ -37,6 +37,8 @@ public MapSchematic(string mapName) public Dictionary ShootingTargets { get; set; } = []; public Dictionary Schematics { get; set; } = []; + + public Dictionary Teleporters { get; set; } = []; public List SpawnedObjects = []; @@ -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; } @@ -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(string id, T serializableObject) where T : SerializableObject @@ -136,13 +140,16 @@ public bool TryAddElement(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; } @@ -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; diff --git a/Features/Serializable/SerializableTeleporter.cs b/Features/Serializable/SerializableTeleporter.cs new file mode 100644 index 0000000..7269bb5 --- /dev/null +++ b/Features/Serializable/SerializableTeleporter.cs @@ -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; + + /// + /// Gets or sets the teleporter ID for this teleporter. + /// + public int Id + { + get => teleporterId; + set + { + teleporterId = value; + } + } + + public List Targets { get; set; } = new List(); + + 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(); + } + + 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(); + + trigger = root.transform.Find("Trigger").GetComponent(); + arrowY = root.transform.Find("Arrow Y Axis").GetComponent(); + arrowX = arrowY.transform.Find("Arrow X Axis").GetComponent(); + + 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()) + { + 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; + } +} \ No newline at end of file diff --git a/Features/ToolGun/ToolGunHandler.cs b/Features/ToolGun/ToolGunHandler.cs index 0181426..89da760 100644 --- a/Features/ToolGun/ToolGunHandler.cs +++ b/Features/ToolGun/ToolGunHandler.cs @@ -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; diff --git a/Features/ToolGun/ToolGunItem.cs b/Features/ToolGun/ToolGunItem.cs index 1ee812c..4af0899 100644 --- a/Features/ToolGun/ToolGunItem.cs +++ b/Features/ToolGun/ToolGunItem.cs @@ -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;