/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ using System; using Cinemachine; using GameCult.Aetheria.State.Verse; using GameCult.Mesh; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Serialization; using UnityEngine.UI; using float2 = Unity.Mathematics.float2; using float3 = Unity.Mathematics.float3; public class ActionGameManager : MonoBehaviour { private AetheriaDaemonObserver _daemonObserver; private AetheriaUnityPilotCommandSender _pilotCommands; private AetheriaUnityObservedEntityRestorer _observedEntityRestorer; private AetheriaUnityCurrentEntityBinder _currentEntityBinder; private AetheriaUnityObservedZoneContextFactory _observedZoneContextFactory; private AetheriaUnityPilotFrameController _pilotFrameController; private AetheriaUnityPilotOperationController _pilotOperationController; private AetheriaUnityObservedFrameApplier _observedFrameApplier; private AetheriaUnityEntityBlueprintMaterializer _entityBlueprintMaterializer; private AetheriaUnityMenuShell _menuShell; private AetheriaUnityGameplayInputShell _gameplayInputShell; private AetheriaUnityCockpitHudShell _cockpitHudShell; private AetheriaUnityGameplayLoopShell _gameplayLoopShell; private AetheriaUnityGameplayBootShell _gameplayBootShell; private AetheriaUnityGameplaySceneWiring _sceneWiring; private AetheriaClientState _gameplayState; private AetheriaClientState GameplayState => _gameplayState ??= AetheriaUnityRuntimeClientProvider.RuntimeState("unity-action-game-manager"); private AetheriaUnityPilotCommandSender PilotCommands => _pilotCommands ??= new AetheriaUnityPilotCommandSender( () => AetheriaUnityRuntimeClientProvider.Control("unity-pilot-commands"), () => Time.unscaledTime); private AetheriaUnityObservedEntityRestorer ObservedEntityRestorer => _observedEntityRestorer ??= new AetheriaUnityObservedEntityRestorer( _presentationEntityIndex, ItemManager, EntityBlueprintMaterializer.MaterializeObservedEntity, _loadoutItemFactory.CreateLoadoutItem, Debug.LogWarning); private AetheriaUnityObservedZoneContextFactory ObservedZoneContextFactory => _observedZoneContextFactory ??= new AetheriaUnityObservedZoneContextFactory( ItemManager, Settings.PlanetSettings, () => ObservedGalaxy, Debug.LogWarning, PlayMusic); private AetheriaUnityCurrentEntityBinder CurrentEntityBinder => _currentEntityBinder ??= new AetheriaUnityCurrentEntityBinder { ZoneRenderer = ZoneRenderer, DeathPost = DeathPost, GameplayUI = GameplayUI, CurrentEntityPresentation = _currentEntityPresentation, TargetPresentation = _targetPresentation, GetCurrentEntity = () => CurrentEntity, ResolveDockParent = ResolveDockParentFromCurrentDocking, SetCurrentEntity = entity => CurrentEntity = entity, GetViewDirection = () => _viewDirection, SetViewDirection = direction => _viewDirection = direction, ResolveZoneRender = () => ResolveDaemonObserver()?.LastRenderView?.ZoneRender, ApplyActionBarBindings = _ => _actionBarPresentation?.ApplyLocalBindings(), EnablePlayerInput = EnablePlayerInput, DisablePlayerInput = DisablePlayerInput, PlayMusic = PlayMusic, UpdateTargetPanel = target => CockpitHudShell.UpdateTarget(target, CurrentEntity) }; private AetheriaUnityPilotFrameController PilotFrameController => _pilotFrameController ??= new AetheriaUnityPilotFrameController(direction => _viewDirection = direction) { ZoneRenderer = ZoneRenderer, Input = Input, TargetPresentation = _targetPresentation, PilotCommands = PilotCommands, HeatstrokePost = HeatstrokePost, SevereHeatstrokePost = SevereHeatstrokePost, Sensitivity = Sensitivity }; private AetheriaUnityPilotOperationController PilotOperationController => _pilotOperationController ??= new AetheriaUnityPilotOperationController( () => PilotCommands, _presentationEntityIndex, () => _viewDirection, () => CurrentEntity); private AetheriaUnityObservedFrameApplier ObservedFrameApplier => _observedFrameApplier ??= new AetheriaUnityObservedFrameApplier( ResolveDaemonObserver, ObservedZoneContextFactory.ResolveGalaxyZone, () => Zone, zone => Zone = zone, _presentationEntityIndex, ObservedEntityRestorer, ObservedZoneContextFactory, () => ZoneRenderer, () => CurrentEntity, entity => CurrentEntityBinder.RestoreBinding(entity), Debug.LogWarning); private AetheriaUnityEntityBlueprintMaterializer EntityBlueprintMaterializer => _entityBlueprintMaterializer ??= new AetheriaUnityEntityBlueprintMaterializer(_loadoutItemFactory); private AetheriaUnityMenuShell MenuShell => _menuShell ??= new AetheriaUnityMenuShell { MainMenu = MainMenu, HelpScreen = HelpScreen, InputDisplayLayout = InputDisplayLayout, UiRoot = UiRoot, Menu = Menu, GameplayUI = GameplayUI, ResolveCurrentEntity = () => CurrentEntity, IsCurrentEntityUndocked = IsCurrentEntityObservedUndocked, ResolveObservedTarget = entity => _targetPresentation.GetObservedTarget(entity), SetPaused = paused => GameplayLoopShell.Paused = paused, EnablePlayerInput = EnablePlayerInput, DisablePlayerInput = DisablePlayerInput, UpdatePlayerPanel = () => CockpitHudShell.UpdatePlayer(CurrentEntity), UpdateTargetPanel = target => CockpitHudShell.UpdateTarget(target, CurrentEntity) }; private AetheriaUnityGameplayInputShell GameplayInputShell => _gameplayInputShell ??= new AetheriaUnityGameplayInputShell { RuntimePlayerSettings = RuntimePlayerSettings, InputDisplayLayout = InputDisplayLayout, Dialog = Dialog, MainMenu = MainMenu, Menu = Menu, MenuMap = MenuMap, GameplayUI = GameplayUI, ActionBar = ActionBar, ActionBarSlot = ActionBarSlot, ZoneRenderer = ZoneRenderer, MenuShell = MenuShell, DragSession = _dragSession, ActionBarPresentation = _actionBarPresentation, PilotOperationController = PilotOperationController, ResolveCurrentEntity = () => CurrentEntity, SetInput = input => Input = input }; private AetheriaUnityCockpitHudShell CockpitHudShell => _cockpitHudShell ??= new AetheriaUnityCockpitHudShell { ShipPanel = ShipPanel, TargetShipPanel = TargetShipPanel, SchematicDisplay = SchematicDisplay, TargetSchematicDisplay = TargetSchematicDisplay, TargetIndicator = TargetIndicator }; private AetheriaUnityGameplayLoopShell GameplayLoopShell => _gameplayLoopShell ??= new AetheriaUnityGameplayLoopShell { ResolveCurrentEntity = () => CurrentEntity, IsCurrentEntityUndocked = IsCurrentEntityObservedUndocked, ApplyLatestZoneRender = () => ObservedFrameApplier.ApplyLatestZoneRender(), CurrentEntityPresentation = _currentEntityPresentation, PilotFrameController = PilotFrameController, TargetPresentation = _targetPresentation }; private AetheriaUnityGameplayBootShell GameplayBootShell => _gameplayBootShell ??= new AetheriaUnityGameplayBootShell { Settings = Settings, ZoneRenderer = ZoneRenderer, CockpitHudShell = CockpitHudShell, TargetSpottedBlinkFrequency = TargetSpottedBlinkFrequency, TargetSpottedBlinkOffset = TargetSpottedBlinkOffset, Log = Debug.Log }; private AetheriaUnityGameplaySceneWiring SceneWiring => _sceneWiring ??= new AetheriaUnityGameplaySceneWiring { ZoneRenderer = ZoneRenderer, DockCamera = DockCamera, FollowCamera = FollowCamera, Menu = Menu, TradeMenu = TradeMenu, GameplayUI = GameplayUI, Inventory = Inventory, ShipPanel = ShipPanel, TargetShipPanel = TargetShipPanel, SchematicDisplay = SchematicDisplay, TargetSchematicDisplay = TargetSchematicDisplay, Crosshairs = Crosshairs, LockIndicator = LockIndicator, HitMarker = HitMarker, HitMarkerDuration = HitMarkerDuration, TargetShieldsBackground = TargetShieldsBackground, TargetShieldsIcon = TargetShieldsIcon, ShieldColor = ShieldColor, NoShieldColor = NoShieldColor, ShieldIcon = ShieldIcon, NoShieldIcon = NoShieldIcon, HostileTargetIndicator = HostileTargetIndicator, FriendlyTargetIndicator = FriendlyTargetIndicator, ViewDot = ViewDot, TargetIndicator = TargetIndicator, TargetHitpointsFill = TargetHitpointsFill, TargetVisibilityFill = TargetVisibilityFill, VisibilityToTargetFill = VisibilityToTargetFill, TargetShieldsFill = TargetShieldsFill, MainMenu = MainMenu }; private readonly AetheriaUnityPresentationEntityIndex _presentationEntityIndex = new AetheriaUnityPresentationEntityIndex(); private static RuntimePlayerSettings RuntimePlayerSettings => AetheriaUnityRuntimeClientProvider.PlayerSettings; public GameSettings Settings; //public string StarterShipTemplate = "Longinus"; public float2 Sensitivity; public float TargetSpottedBlinkFrequency = 20; public float TargetSpottedBlinkOffset = -.25f; [Header("Postprocessing")] public Volume DeathPost; public Volume HeatstrokePost; public Volume SevereHeatstrokePost; [Header("Scene Links")] public GameObject UiRoot; public GameObject HelpScreen; public InputDisplayLayout InputDisplayLayout; public Transform ActionBar; public ActionBarSlot ActionBarSlot; public ZoneRenderer ZoneRenderer; public CinemachineVirtualCamera DockCamera; public CinemachineVirtualCamera FollowCamera; [Header("Menu UI")] public MainMenu MainMenu; public MenuPanel Menu; public MapRenderer MenuMap; public TradeMenu TradeMenu; public InventoryMenu Inventory; public InventoryPanel ShipPanel; public InventoryPanel TargetShipPanel; public ConfirmationDialog Dialog; [Header("Gameplay UI")] public CanvasGroup GameplayUI; public Prototype HostileTargetIndicator; public Prototype FriendlyTargetIndicator; public PlaceUIElementWorldspace ViewDot; public Prototype LockIndicator; public PlaceUIElementWorldspace[] Crosshairs; public GameObject HitMarker; public float HitMarkerDuration; public SchematicDisplay SchematicDisplay; public SchematicDisplay TargetSchematicDisplay; [Header("Target Indicator")] public PlaceUIElementWorldspace TargetIndicator; public Image TargetHitpointsFill; public Image TargetVisibilityFill; public Image VisibilityToTargetFill; public Image TargetShieldsBackground; public Image TargetShieldsFill; public Image TargetShieldsIcon; public Color ShieldColor; public Color NoShieldColor; public Sprite ShieldIcon; public Sprite NoShieldIcon; //public PlayerInput Input; // private CinemachineFramingTransposer _transposer; // private CinemachineComposer _composer; private Entity _currentEntity; // private ShipInput _shipInput; private float3 _viewDirection; private readonly AetheriaUnityCurrentEntityPresentation _currentEntityPresentation = new AetheriaUnityCurrentEntityPresentation(); private readonly AetheriaUnityTargetPresentation _targetPresentation = new AetheriaUnityTargetPresentation(); public AetheriaInput Input { get; private set; } private Entity CurrentEntity { get => _currentEntity; set => _currentEntity = value; } private ItemManager ItemManager { get; set; } private AetheriaUnityLoadoutItemFactory _loadoutItemFactory; private Galaxy ObservedGalaxy { get; set; } private Zone Zone { get; set; } private readonly AetheriaUnityDragSession _dragSession = new AetheriaUnityDragSession(); private readonly AetheriaUnityActionBarPresentation _actionBarPresentation = new AetheriaUnityActionBarPresentation(); private void OnDisable() { _gameplayInputShell?.Dispose(); AetheriaUnityRuntimeClientProvider.Dispose(); } void Start() { var boot = GameplayBootShell.Boot(); ItemManager = boot.ItemManager; _loadoutItemFactory = boot.LoadoutItemFactory; ObservedGalaxy = boot.ObservedGalaxy; SceneWiring.ConfigureCurrentEntityPresentation(_currentEntityPresentation, boot.RuntimeCatalog); SceneWiring.ConfigureTargetPresentation( _targetPresentation, boot.RuntimeCatalog, _presentationEntityIndex); SceneWiring.ConfigureInventoryDragSession(_dragSession); SceneWiring.ConfigureActionBarPresentation( _actionBarPresentation, GameplayInputShell, boot.RuntimeCatalog, Settings, () => CurrentEntity, () => AetheriaUnityRuntimeClientProvider.Control("unity-action-bar")); SceneWiring.ConfigureRuntimeInputScreenShell(MenuShell); SceneWiring.ConfigurePresentationEntityIndex(_presentationEntityIndex); // TODO: Process Stories GameplayInputShell.Bootstrap(); StartGame(); } private void EnablePlayerInput() { GameplayInputShell.EnablePlayerInput(); } private void DisablePlayerInput() { GameplayInputShell.DisablePlayerInput(); } private void StartGame() { ObservedFrameApplier.ApplyLatestZoneRender(); } private bool IsCurrentEntityObservedUndocked() { var docking = CurrentDockingSnapshot(); if (CurrentEntity == null || docking == null || docking.CurrentEntityIndex != CurrentEntity.DaemonEntityIndex) { return false; } if (_presentationEntityIndex.TryGetRecordKeyForPresentationEntity(CurrentEntity, out var currentEntityKey) && !string.IsNullOrWhiteSpace(docking.CurrentEntityKey) && !string.Equals(docking.CurrentEntityKey, currentEntityKey, StringComparison.Ordinal)) { return false; } return !docking.IsDocked; } private Entity ResolveDockParentFromCurrentDocking(Entity currentEntity) { var docking = CurrentDockingSnapshot(); if (currentEntity == null || docking == null || !docking.IsDocked || docking.CurrentEntityIndex != currentEntity.DaemonEntityIndex || string.IsNullOrWhiteSpace(docking.DockParentEntityKey)) { return null; } if (_presentationEntityIndex.TryGetRecordKeyForPresentationEntity(currentEntity, out var currentEntityKey) && !string.IsNullOrWhiteSpace(docking.CurrentEntityKey) && !string.Equals(docking.CurrentEntityKey, currentEntityKey, StringComparison.Ordinal)) { return null; } return _presentationEntityIndex.TryGetPresentationEntityByRecordKey(docking.DockParentEntityKey, out var dockParent) ? dockParent : null; } private AetheriaRuntimeCurrentDockingDocument CurrentDockingSnapshot() { try { return GameplayState.CurrentDocking.Latest(); } catch (Exception ex) { Debug.LogWarning($"Failed to read Aetheria current docking for gameplay manager: {ex.Message}"); return null; } } // public void ToggleEditMode() // { // _editMode = !_editMode; // FollowCamera.gameObject.SetActive(!_editMode); // TopDownCamera.gameObject.SetActive(_editMode); // } public void PlayMusic(MusicType type) { // TODO: SFX: Music } void Update() { GameplayLoopShell.Tick(Time.deltaTime, Time.time); } private AetheriaDaemonObserver ResolveDaemonObserver() { if (_daemonObserver != null) { return _daemonObserver; } _daemonObserver = FindAnyObjectByType(); return _daemonObserver; } private void LateUpdate() { GameplayLoopShell.LateTick(); } }