Added ping feature to sensors, pulsing a wave of revealing light into the level Added IEventBehavior interface, used by sensors and weapons, for behaviors containing event subscriptions which need to be reset when EntityInstance is disposed Implemented sensor impact for locked weapons (lock speed varies with sensor acquisition) Added ping cooldown bar to HUD Added spline curve implementations to AetheriaMath, applied better mapping to wormhole transition animation Tweaked loadout generator to prefer larger items to a controllable degree (e.g. very important for cargo bays, less for shop inventory), shop inventory bans non-ship hulls and manages internal space more effectively
847 lines
No EOL
32 KiB
C#
847 lines
No EOL
32 KiB
C#
/* 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 System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using Cinemachine;
|
|
using MessagePack;
|
|
using TMPro;
|
|
using UniRx;
|
|
using UnityEngine;
|
|
using UnityEngine.InputSystem;
|
|
using UnityEngine.Rendering.PostProcessing;
|
|
using UnityEngine.Serialization;
|
|
using UnityEngine.UI;
|
|
using Unity.Mathematics;
|
|
using static Unity.Mathematics.math;
|
|
using float2 = Unity.Mathematics.float2;
|
|
using float3 = Unity.Mathematics.float3;
|
|
using quaternion = Unity.Mathematics.quaternion;
|
|
using Random = UnityEngine.Random;
|
|
|
|
public class ActionGameManager : MonoBehaviour
|
|
{
|
|
private static DirectoryInfo _gameDataDirectory;
|
|
public static DirectoryInfo GameDataDirectory
|
|
{
|
|
get => _gameDataDirectory ??= new DirectoryInfo(Application.dataPath).Parent.CreateSubdirectory("GameData");
|
|
}
|
|
|
|
private static DatabaseCache _database;
|
|
|
|
public static DatabaseCache Database
|
|
{
|
|
get
|
|
{
|
|
if (_database != null) return _database;
|
|
|
|
_database = new DatabaseCache(Path.Combine(GameDataDirectory.FullName, "AetherDB.msgpack"));
|
|
_database.Load();
|
|
|
|
return _database;
|
|
}
|
|
}
|
|
|
|
private static PlayerSettings _playerSettings;
|
|
public static PlayerSettings PlayerSettings
|
|
{
|
|
get => _playerSettings ??= File.Exists(_playerSettingsFilePath)
|
|
? MessagePackSerializer.Deserialize<PlayerSettings>(File.ReadAllBytes(_playerSettingsFilePath))
|
|
: new PlayerSettings {Name = Environment.UserName};
|
|
}
|
|
private static string _playerSettingsFilePath => Path.Combine(GameDataDirectory.FullName, "PlayerSettings.msgpack");
|
|
public static void SavePlayerSettings()
|
|
{
|
|
File.WriteAllBytes(_playerSettingsFilePath, MessagePackSerializer.Serialize(_playerSettings));
|
|
}
|
|
|
|
public static Sector CurrentSector;
|
|
|
|
public GameSettings Settings;
|
|
//public string StarterShipTemplate = "Longinus";
|
|
public float2 Sensitivity;
|
|
public int Credits = 15000000;
|
|
public float TargetSpottedBlinkFrequency = 20;
|
|
public float TargetSpottedBlinkOffset = -.25f;
|
|
|
|
[Header("Name Generation")]
|
|
public TextAsset NameFile;
|
|
public int NameGeneratorMinLength = 5;
|
|
public int NameGeneratorMaxLength = 10;
|
|
public int NameGeneratorOrder = 4;
|
|
|
|
[Header("Postprocessing")]
|
|
public float DeathPPTransitionTime;
|
|
public PostProcessVolume DeathPP;
|
|
public PostProcessVolume HeatstrokePP;
|
|
public PostProcessVolume SevereHeatstrokePP;
|
|
|
|
[Header("Scene Links")]
|
|
public TradeMenu TradeMenu;
|
|
public Prototype HostileTargetIndicator;
|
|
public PlaceUIElementWorldspace ViewDot;
|
|
public PlaceUIElementWorldspace TargetIndicator;
|
|
public Prototype LockIndicator;
|
|
public PlaceUIElementWorldspace[] Crosshairs;
|
|
public EventLog EventLog;
|
|
[FormerlySerializedAs("SectorRenderer")] public ZoneRenderer ZoneRenderer;
|
|
public CinemachineVirtualCamera DockCamera;
|
|
public CinemachineVirtualCamera FollowCamera;
|
|
public GameObject GameplayUI;
|
|
public MenuPanel Menu;
|
|
public MapRenderer MenuMap;
|
|
//public SectorRenderer SectorRenderer;
|
|
public SectorMap SectorMap;
|
|
public SchematicDisplay SchematicDisplay;
|
|
public SchematicDisplay TargetSchematicDisplay;
|
|
public InventoryMenu Inventory;
|
|
public InventoryPanel ShipPanel;
|
|
public InventoryPanel TargetShipPanel;
|
|
[FormerlySerializedAs("ConfirmationDialog")] public ConfirmationDialog Dialog;
|
|
|
|
//public PlayerInput Input;
|
|
|
|
// private CinemachineFramingTransposer _transposer;
|
|
// private CinemachineComposer _composer;
|
|
|
|
private DirectoryInfo _loadoutPath;
|
|
private bool _editMode;
|
|
private float _time;
|
|
private AetheriaInput _input;
|
|
private int _zoomLevelIndex;
|
|
private Entity _currentEntity;
|
|
|
|
// private ShipInput _shipInput;
|
|
private float2 _entityYawPitch;
|
|
private float3 _viewDirection;
|
|
private (HardpointData[] hardpoints, Transform[] barrels, PlaceUIElementWorldspace crosshair)[] _articulationGroups;
|
|
private (LockWeapon targetLock, PlaceUIElementWorldspace indicator, Rotate spin)[] _lockingIndicators;
|
|
private Dictionary<Entity, VisibleHostileIndicator> _visibleHostileIndicators = new Dictionary<Entity, VisibleHostileIndicator>();
|
|
private List<IDisposable> _shipSubscriptions = new List<IDisposable>();
|
|
private float _severeHeatstrokePhase;
|
|
|
|
public EquippedDockingBay DockingBay { get; private set; }
|
|
public Entity DockedEntity { get; private set; }
|
|
|
|
public Entity CurrentEntity
|
|
{
|
|
get => _currentEntity;
|
|
set => _currentEntity = value;
|
|
}
|
|
|
|
public ItemManager ItemManager { get; private set; }
|
|
public Zone Zone { get; private set; }
|
|
public List<EntityPack> Loadouts { get; } = new List<EntityPack>();
|
|
|
|
private readonly (float2 direction, string name)[] _directions = {
|
|
(float2(0, 1), "Front"),
|
|
(float2(1, 0), "Right"),
|
|
(float2(-1, 0), "Left"),
|
|
(float2(0, -1), "Rear")
|
|
};
|
|
|
|
public EntitySettings NewEntitySettings
|
|
{
|
|
get => MessagePackSerializer.Deserialize<EntitySettings>(MessagePackSerializer.Serialize(Settings.GameplaySettings.DefaultEntitySettings));
|
|
}
|
|
|
|
public void SaveLoadout(EntityPack pack)
|
|
{
|
|
File.WriteAllBytes(Path.Combine(_loadoutPath.FullName, $"{pack.Name}.loadout"), MessagePackSerializer.Serialize(pack));
|
|
}
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
if(CurrentSector!=null)
|
|
{
|
|
SaveState();
|
|
}
|
|
}
|
|
|
|
public void SaveState()
|
|
{
|
|
PlayerSettings.CurrentRun = new SavedGame(CurrentSector, Zone, CurrentEntity);
|
|
SavePlayerSettings();
|
|
}
|
|
|
|
void Start()
|
|
{
|
|
AkSoundEngine.RegisterGameObj(gameObject);
|
|
ConsoleController.MessageReceiver = this;
|
|
|
|
ItemManager = new ItemManager(Database, Settings.GameplaySettings, Debug.Log);
|
|
ZoneRenderer.ItemManager = ItemManager;
|
|
|
|
// _loadoutPath = GameDataDirectory.CreateSubdirectory("Loadouts");
|
|
// Loadouts.AddRange(_loadoutPath.EnumerateFiles("*.loadout")
|
|
// .Select(fi => MessagePackSerializer.Deserialize<EntityPack>(File.ReadAllBytes(fi.FullName))));
|
|
|
|
#region Input Handling
|
|
|
|
_input = new AetheriaInput();
|
|
_input.Global.Enable();
|
|
_input.UI.Enable();
|
|
|
|
_zoomLevelIndex = Settings.DefaultMinimapZoom;
|
|
_input.Player.MinimapZoom.performed += context =>
|
|
{
|
|
_zoomLevelIndex = (_zoomLevelIndex + 1) % Settings.MinimapZoomLevels.Length;
|
|
ZoneRenderer.MinimapDistance = Settings.MinimapZoomLevels[_zoomLevelIndex];
|
|
};
|
|
|
|
_input.Global.MapToggle.performed += context =>
|
|
{
|
|
if (Menu.gameObject.activeSelf && Menu.CurrentTab == MenuTab.Map)
|
|
{
|
|
Cursor.lockState = CursorLockMode.Locked;
|
|
Menu.gameObject.SetActive(false);
|
|
if (CurrentEntity != null && CurrentEntity.Parent == null)
|
|
{
|
|
_input.Player.Enable();
|
|
GameplayUI.SetActive(true);
|
|
|
|
SchematicDisplay.ShowShip(CurrentEntity);
|
|
ShipPanel.Display(CurrentEntity, true);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
Cursor.lockState = CursorLockMode.None;
|
|
_input.Player.Disable();
|
|
GameplayUI.SetActive(false);
|
|
Menu.ShowTab(MenuTab.Map);
|
|
MenuMap.Position = CurrentEntity.Position.xz;
|
|
};
|
|
|
|
_input.Global.Inventory.performed += context =>
|
|
{
|
|
if (Menu.gameObject.activeSelf && Menu.CurrentTab == MenuTab.Inventory)
|
|
{
|
|
Cursor.lockState = CursorLockMode.Locked;
|
|
Menu.gameObject.SetActive(false);
|
|
if (CurrentEntity != null && CurrentEntity.Parent == null)
|
|
{
|
|
_input.Player.Enable();
|
|
GameplayUI.SetActive(true);
|
|
|
|
SchematicDisplay.ShowShip(CurrentEntity);
|
|
ShipPanel.Display(CurrentEntity, true);
|
|
}
|
|
return;
|
|
}
|
|
|
|
Cursor.lockState = CursorLockMode.None;
|
|
_input.Player.Disable();
|
|
Menu.ShowTab(MenuTab.Inventory);
|
|
GameplayUI.SetActive(false);
|
|
};
|
|
|
|
_input.Global.Dock.performed += context =>
|
|
{
|
|
if (CurrentEntity == null)
|
|
{
|
|
AkSoundEngine.PostEvent("UI_Fail", gameObject);
|
|
Dialog.Clear();
|
|
Dialog.Title.text = "Can't undock. You dont have a ship!";
|
|
Dialog.Show();
|
|
Dialog.MoveToCursor();
|
|
}
|
|
else if (CurrentEntity.Parent == null) Dock();
|
|
else Undock();
|
|
};
|
|
|
|
_input.Player.EnterWormhole.performed += context =>
|
|
{
|
|
if(CurrentEntity is Ship ship)
|
|
{
|
|
foreach (var wormhole in ZoneRenderer.WormholeInstances.Keys)
|
|
{
|
|
if (length(wormhole.Position - CurrentEntity.Position.xz) < Settings.GameplaySettings.WormholeExitRadius)
|
|
{
|
|
var oldZone = Zone;
|
|
ship.EnterWormhole(wormhole.Position);
|
|
ship.OnEnteredWormhole += () =>
|
|
{
|
|
GenerateLevel(wormhole.Target);
|
|
ship.ExitWormhole(ZoneRenderer.WormholeInstances.Keys.First(w=>w.Target==oldZone.SectorZone).Position,
|
|
Settings.GameplaySettings.WormholeExitVelocity * ItemManager.Random.NextFloat2Direction());
|
|
CurrentEntity.Zone = Zone;
|
|
SaveState();
|
|
};
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
_input.Player.OverrideShutdown.performed += context =>
|
|
{
|
|
CurrentEntity.OverrideShutdown = !CurrentEntity.OverrideShutdown;
|
|
};
|
|
|
|
_input.Player.Ping.performed += context =>
|
|
{
|
|
CurrentEntity.Sensor?.Ping();
|
|
};
|
|
|
|
_input.Player.ToggleHeatsinks.performed += context =>
|
|
{
|
|
CurrentEntity.HeatsinksEnabled = !CurrentEntity.HeatsinksEnabled;
|
|
AkSoundEngine.PostEvent(CurrentEntity.HeatsinksEnabled ? "UI_Success" : "UI_Fail", gameObject);
|
|
};
|
|
|
|
_input.Player.ToggleShield.performed += context =>
|
|
{
|
|
if (CurrentEntity.Shield != null)
|
|
{
|
|
CurrentEntity.Shield.Item.Enabled.Value = !CurrentEntity.Shield.Item.Enabled.Value;
|
|
AkSoundEngine.PostEvent(CurrentEntity.Shield.Item.Enabled.Value ? "UI_Success" : "UI_Fail", gameObject);
|
|
}
|
|
};
|
|
|
|
#region Targeting
|
|
|
|
_input.Player.TargetReticle.performed += context =>
|
|
{
|
|
var underReticle = Zone.Entities.Where(x => x != CurrentEntity)
|
|
.MaxBy(x => dot(normalize(x.Position - CurrentEntity.Position), CurrentEntity.LookDirection));
|
|
CurrentEntity.Target.Value = CurrentEntity.Target.Value == underReticle ? null : underReticle;
|
|
};
|
|
|
|
_input.Player.TargetNearest.performed += context =>
|
|
{
|
|
CurrentEntity.Target.Value = Zone.Entities.Where(x=>x!=CurrentEntity)
|
|
.MaxBy(x => length(x.Position - CurrentEntity.Position));
|
|
};
|
|
|
|
_input.Player.TargetNext.performed += context =>
|
|
{
|
|
var targets = Zone.Entities.Where(x => x != CurrentEntity).OrderBy(x => length(x.Position - CurrentEntity.Position)).ToArray();
|
|
var currentTargetIndex = Array.IndexOf(targets, CurrentEntity.Target.Value);
|
|
CurrentEntity.Target.Value = targets[(currentTargetIndex + 1) % targets.Length];
|
|
};
|
|
|
|
_input.Player.TargetPrevious.performed += context =>
|
|
{
|
|
var targets = Zone.Entities.Where(x => x != CurrentEntity).OrderBy(x => length(x.Position - CurrentEntity.Position)).ToArray();
|
|
var currentTargetIndex = Array.IndexOf(targets, CurrentEntity.Target.Value);
|
|
CurrentEntity.Target.Value = targets[(currentTargetIndex + targets.Length - 1) % targets.Length];
|
|
};
|
|
|
|
#endregion
|
|
|
|
|
|
#region Trigger Groups
|
|
|
|
_input.Player.PreviousWeaponGroup.performed += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
SchematicDisplay.SelectedGroupIndex--;
|
|
};
|
|
|
|
_input.Player.NextWeaponGroup.performed += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
SchematicDisplay.SelectedGroupIndex++;
|
|
};
|
|
|
|
_input.Player.PreviousWeapon.performed += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
SchematicDisplay.SelectedItemIndex--;
|
|
};
|
|
|
|
_input.Player.NextWeapon.performed += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
SchematicDisplay.SelectedItemIndex++;
|
|
};
|
|
|
|
_input.Player.ToggleWeaponGroup.performed += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
var item = SchematicDisplay.SchematicItems[SchematicDisplay.SelectedItemIndex];
|
|
if (CurrentEntity.TriggerGroups[SchematicDisplay.SelectedGroupIndex].items.Contains(item.Item))
|
|
CurrentEntity.TriggerGroups[SchematicDisplay.SelectedGroupIndex].items.Remove(item.Item);
|
|
else CurrentEntity.TriggerGroups[SchematicDisplay.SelectedGroupIndex].items.Add(item.Item);
|
|
foreach (var group in item.Item.BehaviorGroups.Values)
|
|
{
|
|
var weapon = group.GetBehavior<Weapon>();
|
|
if(weapon != null)
|
|
{
|
|
if (CurrentEntity.TriggerGroups[SchematicDisplay.SelectedGroupIndex].weapons.Contains(weapon))
|
|
CurrentEntity.TriggerGroups[SchematicDisplay.SelectedGroupIndex].weapons.Remove(weapon);
|
|
else CurrentEntity.TriggerGroups[SchematicDisplay.SelectedGroupIndex].weapons.Add(weapon);
|
|
}
|
|
}
|
|
SchematicDisplay.UpdateTriggerGroups();
|
|
};
|
|
|
|
_input.Player.FireGroup1.started += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
foreach (var s in CurrentEntity.TriggerGroups[0].weapons) s.Activate();
|
|
};
|
|
|
|
_input.Player.FireGroup1.canceled += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
foreach (var s in CurrentEntity.TriggerGroups[0].weapons) s.Deactivate();
|
|
};
|
|
|
|
_input.Player.FireGroup2.started += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
foreach (var s in CurrentEntity.TriggerGroups[1].weapons) s.Activate();
|
|
};
|
|
|
|
_input.Player.FireGroup2.canceled += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
foreach (var s in CurrentEntity.TriggerGroups[1].weapons) s.Deactivate();
|
|
};
|
|
|
|
_input.Player.FireGroup3.started += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
foreach (var s in CurrentEntity.TriggerGroups[2].weapons) s.Activate();
|
|
};
|
|
|
|
_input.Player.FireGroup3.canceled += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
foreach (var s in CurrentEntity.TriggerGroups[2].weapons) s.Deactivate();
|
|
};
|
|
|
|
_input.Player.FireGroup4.started += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
foreach (var s in CurrentEntity.TriggerGroups[3].weapons) s.Activate();
|
|
};
|
|
|
|
_input.Player.FireGroup4.canceled += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
foreach (var s in CurrentEntity.TriggerGroups[3].weapons) s.Deactivate();
|
|
};
|
|
|
|
_input.Player.FireGroup5.started += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
foreach (var s in CurrentEntity.TriggerGroups[4].weapons) s.Activate();
|
|
};
|
|
|
|
_input.Player.FireGroup5.canceled += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
foreach (var s in CurrentEntity.TriggerGroups[4].weapons) s.Deactivate();
|
|
};
|
|
|
|
_input.Player.FireGroup6.started += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
foreach (var s in CurrentEntity.TriggerGroups[5].weapons) s.Activate();
|
|
};
|
|
|
|
_input.Player.FireGroup6.canceled += context =>
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
foreach (var s in CurrentEntity.TriggerGroups[5].weapons) s.Deactivate();
|
|
};
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
StartGame();
|
|
}
|
|
|
|
public void GenerateLevel(SectorZone sectorZone)
|
|
{
|
|
if (sectorZone == null) throw new ArgumentNullException(nameof(sectorZone));
|
|
|
|
if (sectorZone.Contents == null)
|
|
{
|
|
sectorZone.PackedContents ??= ZoneGenerator.GenerateZone(
|
|
ItemManager,
|
|
Settings.ZoneSettings,
|
|
CurrentSector,
|
|
sectorZone
|
|
);
|
|
sectorZone.Contents = new Zone(ItemManager, Settings.PlanetSettings, sectorZone.PackedContents, sectorZone);
|
|
}
|
|
Zone = sectorZone.Contents;
|
|
|
|
Zone.Log = s => Debug.Log($"Zone: {s}");
|
|
|
|
if (CurrentEntity != null)
|
|
{
|
|
CurrentEntity.Deactivate();
|
|
CurrentEntity.Zone.Entities.Remove(CurrentEntity);
|
|
CurrentEntity.Zone = Zone;
|
|
Zone.Entities.Add(CurrentEntity);
|
|
CurrentEntity.Activate();
|
|
}
|
|
|
|
ZoneRenderer.LoadZone(Zone);
|
|
|
|
if (CurrentEntity != null)
|
|
{
|
|
UnbindEntity();
|
|
BindToEntity(CurrentEntity);
|
|
}
|
|
}
|
|
|
|
private void StartGame()
|
|
{
|
|
if (CurrentSector != null)
|
|
{
|
|
if (PlayerSettings.CurrentRun == null)
|
|
{
|
|
GenerateLevel(CurrentSector.Entrance);
|
|
var loadoutGenerator = new LoadoutGenerator(ref ItemManager.Random, ItemManager, CurrentSector, Zone.SectorZone, null, 2);
|
|
var ship = EntitySerializer.Unpack(ItemManager, Zone, loadoutGenerator.GenerateShipLoadout(), true);
|
|
// EntitySerializer.Unpack(ItemManager, Zone, Loadouts.First(x => x.Name == StarterShipTemplate), true);
|
|
((Ship) ship).IsPlayerShip = true;
|
|
ship.Position = float3.zero;
|
|
ship.Zone = Zone;
|
|
Zone.Entities.Add(ship);
|
|
ship.Activate();
|
|
BindToEntity(ship);
|
|
}
|
|
else
|
|
{
|
|
GenerateLevel(CurrentSector.Zones[PlayerSettings.CurrentRun.CurrentZone]);
|
|
var targetEntity = Zone.Entities[PlayerSettings.CurrentRun.CurrentZoneEntity];
|
|
if (targetEntity is OrbitalEntity orbitalEntity)
|
|
DoDock(orbitalEntity, orbitalEntity.DockingBays.First());
|
|
else
|
|
BindToEntity(targetEntity);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dock()
|
|
{
|
|
if (CurrentEntity.Parent != null) return;
|
|
if (CurrentEntity is Ship ship)
|
|
{
|
|
foreach (var entity in Zone.Entities.ToArray())
|
|
{
|
|
if (lengthsq(entity.Position.xz - CurrentEntity.Position.xz) <
|
|
Settings.GameplaySettings.DockingDistance * Settings.GameplaySettings.DockingDistance)
|
|
{
|
|
var bay = entity.TryDock(ship);
|
|
if (bay != null)
|
|
{
|
|
UnbindEntity();
|
|
DoDock(entity, bay);
|
|
TradeMenu.Inventory = entity.CargoBays.First();
|
|
AkSoundEngine.PostEvent("Dock", gameObject);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
AkSoundEngine.PostEvent("Dock_Fail", gameObject);
|
|
}
|
|
|
|
private void DoDock(Entity entity, EquippedDockingBay dockingBay)
|
|
{
|
|
DockedEntity = entity;
|
|
ZoneRenderer.PerspectiveEntity = DockedEntity;
|
|
DockingBay = dockingBay;
|
|
DockCamera.enabled = true;
|
|
FollowCamera.enabled = false;
|
|
var orbital = (OrbitalEntity) entity;
|
|
DockCamera.Follow = ZoneRenderer.EntityInstances[orbital].transform;
|
|
var parentOrbit = Zone.Orbits[orbital.OrbitData].Data.Parent;
|
|
var parentPlanet = ZoneRenderer.Planets[Zone.Planets.FirstOrDefault(p => p.Value.Orbit == parentOrbit).Key];
|
|
DockCamera.LookAt = parentPlanet.Body.transform;
|
|
Menu.ShowTab(MenuTab.Inventory);
|
|
}
|
|
|
|
public void Undock()
|
|
{
|
|
if (CurrentEntity.Parent == null) return;
|
|
if (CurrentEntity is Ship ship)
|
|
{
|
|
if (CurrentEntity.GetBehavior<Cockpit>() == null)
|
|
{
|
|
Dialog.Clear();
|
|
Dialog.Title.text = "Can't undock. Missing cockpit component!";
|
|
Dialog.Show();
|
|
Dialog.MoveToCursor();
|
|
AkSoundEngine.PostEvent("UI_Fail", gameObject);
|
|
}
|
|
else if (CurrentEntity.GetBehavior<Thruster>() == null)
|
|
{
|
|
Dialog.Clear();
|
|
Dialog.Title.text = "Can't undock. Missing thruster component!";
|
|
Dialog.Show();
|
|
Dialog.MoveToCursor();
|
|
AkSoundEngine.PostEvent("UI_Fail", gameObject);
|
|
}
|
|
else if (CurrentEntity.GetBehavior<Reactor>() == null)
|
|
{
|
|
Dialog.Clear();
|
|
Dialog.Title.text = "Can't undock. Missing reactor component!";
|
|
Dialog.Show();
|
|
Dialog.MoveToCursor();
|
|
AkSoundEngine.PostEvent("UI_Fail", gameObject);
|
|
}
|
|
else if (CurrentEntity.Parent.TryUndock(ship))
|
|
{
|
|
BindToEntity(ship);
|
|
AkSoundEngine.PostEvent("Undock", gameObject);
|
|
}
|
|
else
|
|
{
|
|
Dialog.Title.text = "Can't undock. Must empty docking bay!";
|
|
Dialog.Show();
|
|
Dialog.MoveToCursor();
|
|
AkSoundEngine.PostEvent("UI_Fail", gameObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UnbindEntity()
|
|
{
|
|
foreach (var indicator in _visibleHostileIndicators)
|
|
{
|
|
Destroy(indicator.Value.gameObject);
|
|
}
|
|
|
|
_visibleHostileIndicators.Clear();
|
|
if(_lockingIndicators!=null) foreach(var (_, indicator, _) in _lockingIndicators)
|
|
indicator.GetComponent<Prototype>().ReturnToPool();
|
|
_input.Player.Disable();
|
|
Cursor.lockState = CursorLockMode.None;
|
|
GameplayUI.SetActive(false);
|
|
|
|
foreach(var subscription in _shipSubscriptions) subscription.Dispose();
|
|
_shipSubscriptions.Clear();
|
|
}
|
|
|
|
private void BindToEntity(Entity entity)
|
|
{
|
|
if (!ZoneRenderer.EntityInstances.ContainsKey(entity))
|
|
{
|
|
Debug.LogError($"Attempted to bind to entity {entity.Name}, but SectorRenderer has no such instance!");
|
|
return;
|
|
}
|
|
|
|
CurrentEntity = entity;
|
|
DeathPP.weight = 0;
|
|
ZoneRenderer.PerspectiveEntity = CurrentEntity;
|
|
|
|
Menu.gameObject.SetActive(false);
|
|
DockedEntity = null;
|
|
DockingBay = null;
|
|
DockCamera.enabled = false;
|
|
FollowCamera.enabled = true;
|
|
|
|
if (length(CurrentEntity.Direction) > .1f)
|
|
_viewDirection = float3(CurrentEntity.Direction.x,0,CurrentEntity.Direction.y);
|
|
|
|
Cursor.lockState = CursorLockMode.Locked;
|
|
GameplayUI.SetActive(true);
|
|
_input.Player.Enable();
|
|
ShipPanel.Display(CurrentEntity, true);
|
|
SchematicDisplay.ShowShip(CurrentEntity);
|
|
|
|
FollowCamera.LookAt = ZoneRenderer.EntityInstances[CurrentEntity].LookAtPoint;
|
|
FollowCamera.Follow = ZoneRenderer.EntityInstances[CurrentEntity].transform;
|
|
_articulationGroups = CurrentEntity.Equipment
|
|
.Where(item => item.Behaviors.Any(x => x.Data is WeaponData && !(x.Data is LauncherData)))
|
|
.GroupBy(item => ZoneRenderer.EntityInstances[CurrentEntity]
|
|
.GetBarrel(CurrentEntity.Hardpoints[item.Position.x, item.Position.y])
|
|
.GetComponentInParent<ArticulationPoint>()?.Group ?? -1)
|
|
.Select((group, index) => {
|
|
return (
|
|
group.Select(item => CurrentEntity.Hardpoints[item.Position.x, item.Position.y]).ToArray(),
|
|
group.Select(item => ZoneRenderer.EntityInstances[CurrentEntity].GetBarrel(CurrentEntity.Hardpoints[item.Position.x, item.Position.y])).ToArray(),
|
|
Crosshairs[index]
|
|
);
|
|
}).ToArray();
|
|
|
|
foreach (var crosshair in Crosshairs)
|
|
crosshair.gameObject.SetActive(false);
|
|
foreach (var group in _articulationGroups)
|
|
group.crosshair.gameObject.SetActive(true);
|
|
|
|
_shipSubscriptions.Add(CurrentEntity.Target.Subscribe(target =>
|
|
{
|
|
TargetIndicator.gameObject.SetActive(CurrentEntity.Target.Value != null);
|
|
TargetShipPanel.gameObject.SetActive(target != null);
|
|
if (target != null)
|
|
{
|
|
TargetShipPanel.Display(target, true);
|
|
TargetSchematicDisplay.ShowShip(target, CurrentEntity);
|
|
}
|
|
}));
|
|
|
|
foreach (var hostile in CurrentEntity.VisibleHostiles)
|
|
{
|
|
var indicator = HostileTargetIndicator.Instantiate<VisibleHostileIndicator>();
|
|
_visibleHostileIndicators.Add(hostile, indicator);
|
|
}
|
|
_shipSubscriptions.Add(CurrentEntity.VisibleHostiles.ObserveAdd().Subscribe(addEvent =>
|
|
{
|
|
var indicator = HostileTargetIndicator.Instantiate<VisibleHostileIndicator>();
|
|
_visibleHostileIndicators.Add(addEvent.Value, indicator);
|
|
}));
|
|
_shipSubscriptions.Add(CurrentEntity.VisibleHostiles.ObserveRemove().Subscribe(removeEvent =>
|
|
{
|
|
_visibleHostileIndicators[removeEvent.Value].GetComponent<Prototype>().ReturnToPool();
|
|
_visibleHostileIndicators.Remove(removeEvent.Value);
|
|
}));
|
|
|
|
_shipSubscriptions.Add(CurrentEntity.Death.Subscribe(_ =>
|
|
{
|
|
var deathTime = Time.time;
|
|
UnbindEntity();
|
|
CurrentEntity = null;
|
|
Dialog.Clear();
|
|
Dialog.Title.text = "You have died!";
|
|
Dialog.Show(() =>
|
|
{
|
|
PlayerSettings.CurrentRun = null;
|
|
StartGame();
|
|
}, null, "Try again!");
|
|
Dialog.transform.position = new Vector3(Screen.width / 2, Screen.height / 2);
|
|
//Dialog.MoveToCursor();
|
|
Observable.EveryUpdate()
|
|
.Select(_ => (Time.time - deathTime) / DeathPPTransitionTime)
|
|
.Where(t => t < 1)
|
|
.Subscribe(t =>
|
|
{
|
|
HeatstrokePP.weight = 1 - t;
|
|
SevereHeatstrokePP.weight = 1 - t;
|
|
DeathPP.weight = t;
|
|
}, () =>
|
|
{
|
|
HeatstrokePP.weight = 0;
|
|
SevereHeatstrokePP.weight = 0;
|
|
DeathPP.weight = 1;
|
|
});
|
|
}));
|
|
|
|
_lockingIndicators = CurrentEntity.GetBehaviors<LockWeapon>()
|
|
.Select(x =>
|
|
{
|
|
var i = LockIndicator.Instantiate<PlaceUIElementWorldspace>();
|
|
return (x, i, i.GetComponent<Rotate>());
|
|
}).ToArray();
|
|
}
|
|
|
|
public void SaveZone(string name) => File.WriteAllBytes(
|
|
Path.Combine(_gameDataDirectory.FullName, $"{name}.zone"), MessagePackSerializer.Serialize(Zone.PackZone()));
|
|
|
|
// public void ToggleEditMode()
|
|
// {
|
|
// _editMode = !_editMode;
|
|
// FollowCamera.gameObject.SetActive(!_editMode);
|
|
// TopDownCamera.gameObject.SetActive(_editMode);
|
|
// }
|
|
|
|
public IEnumerable<EquippedCargoBay> AvailableCargoBays()
|
|
{
|
|
if (CurrentEntity.Parent != null)
|
|
{
|
|
foreach (var bay in CurrentEntity.Parent.DockingBays)
|
|
{
|
|
if (bay.DockedShip.IsPlayerShip) yield return bay;
|
|
}
|
|
}
|
|
}
|
|
|
|
public IEnumerable<Entity> AvailableEntities()
|
|
{
|
|
if(DockedEntity != null)
|
|
foreach (var entity in DockedEntity.Children)
|
|
{
|
|
if (entity is Ship { IsPlayerShip: true }) yield return entity;
|
|
}
|
|
else if (CurrentEntity != null)
|
|
yield return CurrentEntity;
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
if(!_editMode)
|
|
{
|
|
_time += Time.deltaTime;
|
|
ItemManager.Time = Time.time;
|
|
if(CurrentEntity !=null && CurrentEntity.Parent==null)
|
|
{
|
|
foreach (var indicator in _visibleHostileIndicators)
|
|
{
|
|
indicator.Value.gameObject.SetActive(indicator.Key!=CurrentEntity.Target.Value);
|
|
indicator.Value.Place.Target = indicator.Key.Position;
|
|
indicator.Value.Fill.fillAmount =
|
|
saturate(indicator.Key.EntityInfoGathered[CurrentEntity] / Settings.GameplaySettings.TargetDetectionInfoThreshold);
|
|
indicator.Value.Fill.enabled =
|
|
!(indicator.Key.EntityInfoGathered[CurrentEntity] > Settings.GameplaySettings.TargetDetectionInfoThreshold) ||
|
|
sin(TargetSpottedBlinkFrequency * Time.time) + TargetSpottedBlinkOffset > 0;
|
|
}
|
|
var look = _input.Player.Look.ReadValue<Vector2>();
|
|
_entityYawPitch = float2(_entityYawPitch.x + look.x * Sensitivity.x, clamp(_entityYawPitch.y + look.y * Sensitivity.y, -.45f * PI, .45f * PI));
|
|
_viewDirection = mul(float3(0, 0, 1), Unity.Mathematics.float3x3.Euler(float3(_entityYawPitch.yx, 0), RotationOrder.YXZ));
|
|
CurrentEntity.LookDirection = _viewDirection;
|
|
HeatstrokePP.weight = saturate(unlerp(0, Settings.GameplaySettings.SevereHeatstrokeRiskThreshold, CurrentEntity.Heatstroke));
|
|
var severeHeatstrokeLerp = saturate(unlerp(Settings.GameplaySettings.SevereHeatstrokeRiskThreshold, 1, CurrentEntity.Heatstroke));
|
|
SevereHeatstrokePP.weight =
|
|
severeHeatstrokeLerp + severeHeatstrokeLerp * (1 - severeHeatstrokeLerp) *
|
|
max(Settings.HeatstrokePhasingFloor, sin(Time.time * Settings.HeatstrokePhasingFrequency));
|
|
|
|
if(CurrentEntity is Ship ship)
|
|
{
|
|
ship.MovementDirection = _input.Player.Move.ReadValue<Vector2>();
|
|
}
|
|
}
|
|
}
|
|
Zone.Update(_time, Time.deltaTime);
|
|
ZoneRenderer.GameTime = _time;
|
|
}
|
|
|
|
private void LateUpdate()
|
|
{
|
|
UpdateTargetIndicators();
|
|
}
|
|
|
|
private void UpdateTargetIndicators()
|
|
{
|
|
if (CurrentEntity == null || CurrentEntity.Parent != null) return;
|
|
|
|
ViewDot.Target = ZoneRenderer.EntityInstances[CurrentEntity].LookAtPoint.position;
|
|
if (CurrentEntity.Target.Value != null)
|
|
TargetIndicator.Target = CurrentEntity.Target.Value.Position;
|
|
var distance = length((float3)ViewDot.Target - CurrentEntity.Position);
|
|
foreach (var (_, barrels, crosshair) in _articulationGroups)
|
|
{
|
|
var averagePosition = Vector3.zero;
|
|
foreach (var barrel in barrels)
|
|
averagePosition += barrel.position + barrel.forward * distance;
|
|
averagePosition /= barrels.Length;
|
|
crosshair.Target = averagePosition;
|
|
}
|
|
|
|
foreach (var (targetLock, indicator, spin) in _lockingIndicators)
|
|
{
|
|
var showLockingIndicator = targetLock.Lock > .01f && CurrentEntity.Target.Value != null && CurrentEntity.Target.Value.IsHostileTo(CurrentEntity);
|
|
indicator.gameObject.SetActive(showLockingIndicator);
|
|
if(showLockingIndicator)
|
|
{
|
|
indicator.Target = CurrentEntity.Target.Value.Position;
|
|
indicator.NoiseAmplitude = Settings.GameplaySettings.LockIndicatorNoiseAmplitude * (1 - targetLock.Lock);
|
|
indicator.NoiseFrequency = Settings.GameplaySettings.LockIndicatorFrequency.Evaluate(targetLock.Lock);
|
|
spin.Speed = Settings.GameplaySettings.LockSpinSpeed.Evaluate(targetLock.Lock);
|
|
}
|
|
}
|
|
}
|
|
} |