using System; using System.Collections.Generic; using EscapeRoomEngine.Engine.Runtime.Modules; using EscapeRoomEngine.Engine.Runtime.Utilities; using NaughtyAttributes; using UnityEngine; namespace EscapeRoomEngine.Portal.Runtime { /// /// The portal is a specific type of for seamless transitions between spaces. /// public class Portal : DoorState { public static readonly Matrix4x4 HalfRotation = Matrix4x4.Rotate(Quaternion.Euler(0, 180, 0)); /// /// The portal that is connected with this one. /// public Portal linkedPortal; /// /// The camera that will draw the view for this portal. /// [BoxGroup("Internal")] public PortalCamera portalCamera; /// /// The transform marking the edge of the portal plane. /// [BoxGroup("Internal")] public Transform portalTransform; /// /// Whether this portal is connected is determined by whether the reference to its linked portal is set. /// internal bool Connected => linkedPortal != null; internal readonly List closePortalDrivers = new(); protected virtual void Awake() { DoorEvent += (_, type) => { // ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault switch (type) { case DoorEventType.Connected: linkedPortal = FromDoorState(Module.ConnectedDoorState); portalCamera.screen.gameObject.SetActive(true); portalCamera.enabled = true; break; case DoorEventType.Locked: portalCamera.enabled = false; portalCamera.screen.gameObject.SetActive(false); linkedPortal = null; break; } }; } private void FixedUpdate() { if (Connected) { for (var i = 0; i < closePortalDrivers.Count; i++) { var portalDriver = closePortalDrivers[i]; if (portalDriver.entrySide < 0 && CalculateSide(portalDriver.transform) >= 0) // must have entered from the front and exited the back { StopTrackingDriver(portalDriver); linkedPortal.StartTrackingDriver(portalDriver, -1); portalDriver.Teleport(this, linkedPortal); if (portalDriver.player) { linkedPortal.ExitFrom(); } i--; // decrease the loop counter because the list is one element smaller now } } } } /// /// Begin tracking a portal driver that came close to this portal and might need to be teleported. /// internal void StartTrackingDriver(PortalDriver portalDriver, int entrySide) { closePortalDrivers.Add(portalDriver); portalDriver.EnableClone(linkedPortal); portalDriver.entrySide = entrySide; } /// /// End tracking a portal driver that distanced itself from this portal or was teleported to the linked portal. /// internal void StopTrackingDriver(PortalDriver portalDriver) { closePortalDrivers.Remove(portalDriver); portalDriver.DisableClone(linkedPortal); } /// /// Calculate which side of the portal plane a transform is. /// internal int CalculateSide(Transform portalDriverTransform) { return Math.Sign(Vector3.Dot(portalTransform.forward, portalDriverTransform.position - portalTransform.position)); } private static Portal FromDoorState(DoorState state) { if (state is Portal portal) { return portal; } throw new WrongTypeException(typeof(Portal), state.GetType(), typeof(DoorState)); } } }