diff --git a/FFTProject/Controllers/Interfaces/IListener.cs b/FFTProject/Controllers/Interfaces/IListener.cs new file mode 100644 index 0000000..eb2d74b --- /dev/null +++ b/FFTProject/Controllers/Interfaces/IListener.cs @@ -0,0 +1,18 @@ +using KSP.Messages; + +namespace FFT.Controllers.Interfaces +{ + public interface IListener + { + public interface IGameStateListener + { + event Action GameStateEntered; + event Action GameStateLeft; + } + + public interface IVesselSituationListener + { + event Action VesselSituationChanged; + } + } +} diff --git a/FFTProject/Controllers/Interfaces/ILoadModule.cs b/FFTProject/Controllers/Interfaces/ILoadModule.cs new file mode 100644 index 0000000..d251279 --- /dev/null +++ b/FFTProject/Controllers/Interfaces/ILoadModule.cs @@ -0,0 +1,9 @@ +namespace FFT.Controllers.Interfaces +{ + public interface ILoadModule + { + void Boot(); + void PreLoad(); + void Load(); + } +} \ No newline at end of file diff --git a/FFTProject/Controllers/Interfaces/IModuleController.cs b/FFTProject/Controllers/Interfaces/IModuleController.cs new file mode 100644 index 0000000..912dc04 --- /dev/null +++ b/FFTProject/Controllers/Interfaces/IModuleController.cs @@ -0,0 +1,7 @@ +namespace FFT.Controllers.Interfaces +{ + public interface IModuleController + { + void SetLoadModule(ILoadModule loadModule); + } +} \ No newline at end of file diff --git a/FFTProject/Controllers/Interfaces/IResetModule.cs b/FFTProject/Controllers/Interfaces/IResetModule.cs new file mode 100644 index 0000000..29de8db --- /dev/null +++ b/FFTProject/Controllers/Interfaces/IResetModule.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FFT.Controllers.Interfaces +{ + public interface IResetModule + { + void Reset(); + void Unload(); + } +} diff --git a/FFTProject/Controllers/Interfaces/IStartModule.cs b/FFTProject/Controllers/Interfaces/IStartModule.cs new file mode 100644 index 0000000..9dc5c81 --- /dev/null +++ b/FFTProject/Controllers/Interfaces/IStartModule.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FFT.Controllers.Interfaces +{ + public interface IStartModule + { + void StartVentValve(); + void ActivateModule(ModuleController.ModuleType moduleType); + void DeactivateModule(ModuleController.ModuleType moduleType); + } +} diff --git a/FFTProject/Controllers/LoadModule.cs b/FFTProject/Controllers/LoadModule.cs new file mode 100644 index 0000000..b5d0c48 --- /dev/null +++ b/FFTProject/Controllers/LoadModule.cs @@ -0,0 +1,93 @@ +//|=====================Summary========================|0| +//| Ensures that the correct module gets loaded |1| +//|by cvusmo===========================================|4| +//|====================================================|1| + +using BepInEx.Logging; +using FFT.Controllers.Interfaces; +using FFT.Managers; +using FFT.Modules; +using FFT.Utilities; +using Newtonsoft.Json; +using System; + +namespace FFT.Controllers +{ + [JsonObject(MemberSerialization.OptIn)] + public class LoadModule : ILoadModule + { + [JsonProperty] + public bool EnableVFX { get; private set; } = true; + + private static readonly object _lock = new object(); + private readonly ManualLogSource _logger = BepInEx.Logging.Logger.CreateLogSource("LoadModule: "); + + internal RefreshVesselData.RefreshActiveVessel RefreshActiveVessel => RefreshVesselData.Instance.RefreshActiveVesselInstance; + + private Manager _manager; + private MessageManager _messageManager; + private ConditionsManager _conditionsManager; + private ModuleController _moduleController; + private StartModule _startModule; + private ResetModule _resetModule; + private Module_VentValve _moduleVentValve; + + public event Action ModuleResetRequested; + + private static LoadModule _instance; + private static readonly Lazy _lazyInstance = new Lazy(() => new LoadModule()); + public static LoadModule Instance + { + get + { + if (_instance == null) + { + _instance = _lazyInstance.Value; + _instance.InitializeDependencies(); + } + return _instance; + } + } + private LoadModule() { } + public void InitializeDependencies() + { + _manager = Manager.Instance; + _messageManager = MessageManager.Instance; + _conditionsManager = ConditionsManager.Instance; + _moduleController = ModuleController.Instance; + _startModule = StartModule.Instance; + _resetModule = ResetModule.Instance; + _moduleVentValve = new Module_VentValve(); + } + public void Boot() + { + Utility.RefreshGameManager(); + if (RefreshActiveVessel.IsFlightActive && EnableVFX) + { + _logger.LogInfo("Booting Module_VentValve"); + _moduleController.SetModuleState(ModuleController.ModuleType.ModuleVentValve, true); + PreLoad(); + } + } + public void PreLoad() + { + Utility.RefreshGameManager(); + if (_moduleController.GetModuleState(ModuleController.ModuleType.ModuleVentValve)) + { + _logger.LogInfo("Preloading Module_VentValve"); + _moduleVentValve = new Module_VentValve(); + Load(); + } + } + public void Load() + { + Utility.RefreshGameManager(); + if (RefreshActiveVessel.IsFlightActive && _moduleController.GetModuleState(ModuleController.ModuleType.ModuleVentValve)) + { + _logger.LogInfo("Loading Module_VentValve"); + _messageManager.SubscribeToMessages(); + _moduleController.IsModuleLoaded = true; + } + } + } +} diff --git a/FFTProject/Controllers/ModuleController.cs b/FFTProject/Controllers/ModuleController.cs new file mode 100644 index 0000000..fa4b67b --- /dev/null +++ b/FFTProject/Controllers/ModuleController.cs @@ -0,0 +1,88 @@ +//|=====================Summary========================|0| +//| Dictionary for module types |1| +//|by cvusmo===========================================|4| +//|====================================================|1| +using FFT.Controllers.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace FFT.Controllers +{ + public sealed class ModuleController + { + private readonly Dictionary moduleStates = new Dictionary(); + private static ModuleController _instance; + private static readonly object _lock = new object(); + public enum ModuleType + { + Default = 0, + ModuleVentValve = 1, + ModuleOne = 2, + ModuleTwo = 3, + ModuleThree = 4, + ModuleFour = 5 + } + internal bool IsModuleLoaded { get; set; } + internal bool ShouldResetModule { get; set; } + internal static ModuleController Instance + { + get + { + if (_instance == null) + { + lock (_lock) + { + if (_instance == null) + { + _instance = new ModuleController(); + } + } + } + return _instance; + } + } + private ModuleController() + { + InitializeModuleStates(); + } + private void InitializeModuleStates() + { + foreach (ModuleType moduleType in Enum.GetValues(typeof(ModuleType))) + { + moduleStates[moduleType] = false; + } + } + internal void SetModuleState(ModuleType type, bool state) + { + if (!moduleStates.ContainsKey(type)) + { + throw new ArgumentException($"Unsupported ModuleType: {type}"); + } + + moduleStates[type] = state; + } + internal bool GetModuleState(ModuleType type) + { + if (moduleStates.TryGetValue(type, out bool state)) + { + return state; + } + throw new ArgumentException($"Unsupported ModuleType: {type}"); + } + internal void SetVentValveState(bool state) + { + SetModuleState(ModuleType.ModuleVentValve, state); + } + internal void ResetAllModuleStates() + { + foreach (var key in moduleStates.Keys.ToList()) + { + moduleStates[key] = false; + } + + IsModuleLoaded = false; + ShouldResetModule = false; + } + } +} diff --git a/FFTProject/Controllers/ResetModule.cs b/FFTProject/Controllers/ResetModule.cs new file mode 100644 index 0000000..f7723d8 --- /dev/null +++ b/FFTProject/Controllers/ResetModule.cs @@ -0,0 +1,89 @@ +//|=====================Summary========================|0| +//| Resets the ConditionsManager to its default states |1| +//|by cvusmo===========================================|4| +//|====================================================|1| +using FFT.Controllers.Interfaces; +using FFT.Managers; +using FFT.Utilities; +using Newtonsoft.Json; + +namespace FFT.Controllers +{ + [JsonObject(MemberSerialization.OptIn)] + public class ResetModule : IResetModule + { + public event Action ModuleResetRequested = delegate { }; + + private readonly ConditionsManager _conditionsManager; + private readonly Manager _manager; + private readonly ModuleController _moduleController; + private readonly RefreshVesselData _vesselData; + private bool? _isFlightActiveCache; + + private static readonly object _lock = new object(); + private static ResetModule _instance; + public ResetModule( + ConditionsManager conditionsManager, + Manager manager, + ModuleController moduleController, + RefreshVesselData vesselData) + { + _conditionsManager = conditionsManager; + _manager = manager; + _moduleController = moduleController; + _vesselData = vesselData; + } + public static ResetModule Instance + { + get + { + if (_instance == null) + { + lock (_lock) + { + if (_instance == null) + { + _instance = new ResetModule(ConditionsManager.Instance, Manager.Instance, ModuleController.Instance, new RefreshVesselData()); + } + } + } + return _instance; + } + } + public void Reset() + { + if (!_moduleController.ShouldResetModule) return; + + if (Utility.VesselSituations == KSP.Sim.impl.VesselSituations.PreLaunch || + Utility.VesselSituations == KSP.Sim.impl.VesselSituations.Landed || + Utility.VesselSituations == KSP.Sim.impl.VesselSituations.Flying) + { + ModuleResetRequested.Invoke(); + } + + _moduleController.ShouldResetModule = false; + _moduleController.IsModuleLoaded = false; + } + public void Unload() + { + bool isFlightCurrentlyActive = _vesselData.RefreshActiveVesselInstance.IsFlightActive; + + if (_isFlightActiveCache.HasValue && !_isFlightActiveCache.Value || + !_isFlightActiveCache.HasValue && !isFlightCurrentlyActive) + { + _isFlightActiveCache = false; + _manager.Logger.LogInfo("Unloading Module"); + + if (_moduleController.GetModuleState(ModuleController.ModuleType.ModuleVentValve)) + { + Reset(); + _manager.Logger.LogInfo("Reset Module_VentValve"); + } + } + else + { + _isFlightActiveCache = true; + } + } + } +} \ No newline at end of file diff --git a/FFTProject/Controllers/StartModule.cs b/FFTProject/Controllers/StartModule.cs new file mode 100644 index 0000000..80edd39 --- /dev/null +++ b/FFTProject/Controllers/StartModule.cs @@ -0,0 +1,94 @@ +//|=====================Summary========================|0| +//| Initiates the module start process |1| +//|by cvusmo===========================================|4| +//|====================================================|1| + +using FFT.Modules; +using FFT.Utilities; +using Newtonsoft.Json; +using FFT.Controllers.Interfaces; +using BepInEx.Logging; +using System; +using FFT.Managers; + +namespace FFT.Controllers +{ + [JsonObject(MemberSerialization.OptIn)] + public class StartModule : IStartModule + { + private readonly ManualLogSource _logger; + private readonly ModuleController _moduleController; + private readonly MessageManager _messageManager; + private static Module_VentValve _moduleVentValve; + public Module_VentValve ModuleVentValve { get; private set; } + + private static StartModule _instance; + private static readonly object _lock = new object(); + + private readonly TimeSpan ThrottleTimeSpan = TimeSpan.FromSeconds(2); + private DateTime _lastEventTriggerTime = DateTime.MinValue; + public static StartModule Instance + { + get + { + lock (_lock) + { + if (_instance == null) + { + _instance = new StartModule( + BepInEx.Logging.Logger.CreateLogSource("StartModule: "), + ModuleController.Instance, + _moduleVentValve = new Module_VentValve()); + } + return _instance; + } + } + } + private StartModule( + ManualLogSource logger, + ModuleController moduleController, + Module_VentValve moduleVentValve) + { + _logger = logger; + _moduleController = moduleController ?? throw new ArgumentNullException(nameof(moduleController)); + ModuleVentValve = moduleVentValve ?? throw new ArgumentNullException(nameof(moduleVentValve)); + } + internal StartModule(MessageManager messageManager, ManualLogSource logger) + { + _messageManager = messageManager; + _logger = logger; + } + public void StartVentValve() + { + if (DateTime.Now - _lastEventTriggerTime < ThrottleTimeSpan) return; // Throttling + _lastEventTriggerTime = DateTime.Now; + + Utility.RefreshActiveVesselAndCurrentManeuver(); + + if (_moduleController.GetModuleState(ModuleController.ModuleType.ModuleVentValve)) + { + ActivateModule(ModuleController.ModuleType.ModuleVentValve); + _logger.LogInfo("StartVentValve called"); + } + + if (Utility.ActiveVessel == null) + return; + } + public void ActivateModule(ModuleController.ModuleType moduleType) + { + if (moduleType == ModuleController.ModuleType.ModuleVentValve && ModuleVentValve != null) + { + ModuleVentValve.Activate(); + _logger.LogInfo("ActivateModule called"); + } + } + public void DeactivateModule(ModuleController.ModuleType moduleType) + { + if (moduleType == ModuleController.ModuleType.ModuleVentValve && ModuleVentValve != null) + { + ModuleVentValve.Deactivate(); + _logger.LogInfo("DeactivateModule called"); + } + } + } +} diff --git a/FFTProject/FFTPlugin.cs b/FFTProject/FFTPlugin.cs index d8a8c12..3ddc909 100644 --- a/FFTProject/FFTPlugin.cs +++ b/FFTProject/FFTPlugin.cs @@ -1,89 +1,136 @@ -using BepInEx; +//|=====================Summary========================|0| +//| Initializer |1| +//|by cvusmo===========================================|4| +//|====================================================|1| +using BepInEx; +using BepInEx.Configuration; using BepInEx.Logging; -using FFT.Modules; -using KSP.Game; +using FFT.Controllers; +using FFT.Controllers.Interfaces; +using FFT.Managers; +using FFT.Utilities; +using KSP.Messages; using SpaceWarp; using SpaceWarp.API.Mods; +using FFT.Modules; namespace FFT { [BepInPlugin(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)] [BepInDependency(SpaceWarpPlugin.ModGuid, SpaceWarpPlugin.ModVer)] - public class FFTPlugin : BaseSpaceWarpPlugin + internal class FFTPlugin : BaseSpaceWarpPlugin, IModuleController { - public const string ModGuid = MyPluginInfo.PLUGIN_GUID; - public const string ModName = MyPluginInfo.PLUGIN_NAME; - public const string ModVer = MyPluginInfo.PLUGIN_VERSION; - - public GameInstance gameInstance; - public GameState? _state; - public FuelTankDefinitions fuelTankDefinitions; - public Data_FuelTanks dataFuelTanks; - public VentValveDefinitions ventValveDefinitions; - public Data_ValveParts dataValveParts; - public Module_TriggerVFX Module_TriggerVFX { get; private set; } - public Module_VentValve Module_VentValve { get; private set; } - public static FFTPlugin Instance { get; set; } - public new static ManualLogSource Logger { get; set; } + public ConfigEntry FFTConfig { get; private set; } + + internal readonly ManualLogSource _logger = BepInEx.Logging.Logger.CreateLogSource("FFTPlugin"); + + private Manager _manager; + private ConditionsManager _conditionsManager; + private MessageManager _messageManager; + private LoadModule _loadModule; + private StartModule _startModule; + private ResetModule _resetModule; + private RefreshVesselData _refreshVesselData; + private ModuleController _moduleController; + private Module_VentValve _moduleVentValve; + private FuelTankDefinitions _fuelTankDefinitions; + private VentValveDefinitions _ventValveDefinitions; + private Data_ValveParts _dataValveParts; + private Data_FuelTanks _dataFuelTanks; + public static FFTPlugin Instance { get; private set; } + public FFTPlugin() + { + if (Instance != null) + { + throw new Exception("FFTPlugin is a singleton and cannot have multiple instances!"); + } + Instance = this; + } public static string Path { get; private set; } public override void OnPreInitialized() { FFTPlugin.Path = this.PluginFolderPath; + base.OnPreInitialized(); + _logger.LogInfo("OnPreInitialized FFTPlugin."); } + public override void OnInitialized() { base.OnInitialized(); + _logger.LogInfo("Initializing FFTPlugin..."); - Instance = this; - Logger = base.Logger; - Logger.LogInfo("Loaded"); - - gameInstance = GameManager.Instance.Game; - fuelTankDefinitions = new FuelTankDefinitions(); - dataFuelTanks = new Data_FuelTanks(); - ventValveDefinitions = new VentValveDefinitions(); - dataValveParts = new Data_ValveParts(); + _fuelTankDefinitions = FuelTankDefinitions.Instance; + //_fuelTankDefinitions = new FuelTankDefinitions(); + _dataFuelTanks = new Data_FuelTanks(); + _ventValveDefinitions = VentValveDefinitions.Instance; + //_ventValveDefinitions = new VentValveDefinitions(); + _dataValveParts = new Data_ValveParts(); + + Config.Bind( + "Fancy Fuel Tanks Settings", + "Enable VFX", + true, + "Fancy Fuel Tanks adds Dynamic Environmental Effects to fuel tanks" + ); + + try + { + InitializeDependencies(); + } + catch (Exception ex) + { + HandleException(ex, "FFT Initialization"); + } + + _logger.LogInfo("Initialized FFTPlugin."); + } + public void SetLoadModule(ILoadModule loadModule) + { + _loadModule = LoadModule.Instance; } - public void Update() + private void InitializeDependencies() { - _state = BaseSpaceWarpPlugin.Game?.GlobalGameState?.GetState(); + _logger.LogInfo("Subscribing to messages.... "); + _messageManager = MessageManager.Instance; + } + public override void OnPostInitialized() + { + _logger.LogInfo("Calling OnPostInitialized..."); + base.OnPostInitialized(); + + try + { + _conditionsManager = ConditionsManager.Instance; + _logger.LogInfo("ConditionsManager initialized successfully."); - if (_state == GameState.FlightView) + _moduleController = ModuleController.Instance; + _logger.LogInfo("ModuleController initialized successfully."); + + _startModule = StartModule.Instance; + _logger.LogInfo("StartModule initialized successfully."); + + Manager.InitializeInstance(); + _manager = Manager.Instance; + _logger.LogInfo("Manager initialized successfully."); + + _refreshVesselData = RefreshVesselData.Instance; + _logger.LogInfo("RefreshVesselData initialized successfully."); + + _resetModule = new ResetModule(_conditionsManager, _manager, _moduleController, _refreshVesselData); + _logger.LogInfo("ResetModule initialized successfully."); + + _moduleVentValve = new Module_VentValve(); + _logger.LogDebug("ModuleVentValve initialized successfully."); + + } + catch (Exception ex) { - if (fuelTankDefinitions == null) - { - fuelTankDefinitions = FindObjectOfType(); - } - if (ventValveDefinitions == null) - { - ventValveDefinitions = FindObjectOfType(); - } - - if (fuelTankDefinitions != null && dataFuelTanks != null) - { - fuelTankDefinitions.PopulateFuelTanks(dataFuelTanks); - } - - if (ventValveDefinitions != null && dataValveParts != null) - { - ventValveDefinitions.PopulateVentValve(dataValveParts); - } - - foreach (var module in FindObjectsOfType()) - { - module.Activate(); - } - foreach (var module in FindObjectsOfType()) - { - module.Activate(); - } + _logger.LogError($"Error during OnPostInitialized: {ex.Message}"); } } - public GameState? GetGameState() + private void HandleException(Exception ex, string context) { - Logger.LogInfo("_state" + _state); - return _state; + _logger.LogError($"Error in {context}: {ex}"); } - public override void OnPostInitialized() => base.OnPostInitialized(); } } \ No newline at end of file diff --git a/FFTProject/Managers/ConditionsManager.cs b/FFTProject/Managers/ConditionsManager.cs new file mode 100644 index 0000000..e35743e --- /dev/null +++ b/FFTProject/Managers/ConditionsManager.cs @@ -0,0 +1,146 @@ +//|=====================Summary========================|0| +//| Validates messages meet specific conditions |1| +//|by cvusmo===========================================|4| +//|====================================================|1| +using BepInEx.Logging; +using FFT.Utilities; +using KSP.Game; +using KSP.Messages; +using System; + +namespace FFT.Managers +{ + public class ConditionsManager + { + internal readonly ManualLogSource _logger; + internal readonly MessageManager _messageManager; + internal static ConditionsManager _instance; + internal static readonly object _lock = new object(); + + public delegate void ModuleConditionsMetDelegate(); + public event ModuleConditionsMetDelegate ModuleConditionsMet; + + public delegate void ModuleStartedDelegate(); + public event ModuleStartedDelegate OnModuleStarted; + public static ConditionsManager Instance + { + get + { + lock (_lock) + { + return _instance ??= new ConditionsManager(MessageManager.Instance, Logger.CreateLogSource("FFT.ConditionsManager")); + } + } + } + internal ConditionsManager(MessageManager messageManager, ManualLogSource logger) + { + _messageManager = messageManager ?? throw new ArgumentNullException(nameof(messageManager)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + _messageManager.GameStateEntered += GameStateEnteredHandler; + _messageManager.GameStateLeft += GameStateLeftHandler; + _messageManager.VesselSituationChanged += HandleVesselSituationChanged; + } + ~ConditionsManager() + { + _messageManager.GameStateEntered -= GameStateEnteredHandler; + _messageManager.GameStateLeft -= GameStateLeftHandler; + _messageManager.VesselSituationChanged -= HandleVesselSituationChanged; + } + internal void CheckConditionsAndInformManager() + { + if (ConditionsReady()) + { + ModuleConditionsMet?.Invoke(); + } + } + public void VerifyAndStartModule() + { + _logger.LogDebug("Verifying loaded module..."); + Manager.Instance.UpdateStartModule(); + } + public void OnModuleLoaded() + { + VerifyAndStartModule(); + } + public void HandleModuleLoaded() + { + ResetModuleState(); + } + public void ResetModuleState() + { + Manager.Instance.OnModuleReset(); + } + internal void GameStateEnteredHandler(MessageCenterMessage obj) + { + try + { + if (obj is GameStateEnteredMessage gameStateMessage) + { + Utility.GameState = gameStateMessage.StateBeingEntered; + _logger.LogDebug($"Entered New GameState: {Utility.GameStateToString(Utility.GameState)}."); + } + } + catch (Exception ex) + { + _logger.LogError($"Error handling GameStateEntered: {ex}"); + } + } + internal void GameStateLeftHandler(MessageCenterMessage obj) + { + try + { + if (obj is GameStateLeftMessage gameStateMessage) + { + Utility.GameState = gameStateMessage.StateBeingLeft; + _logger.LogDebug($"Left Previous GameState: {Utility.GameStateToString(Utility.GameState)}."); + } + } + catch (Exception ex) + { + _logger.LogError($"Error handling GameStateLeft: {ex}"); + } + } + internal void HandleVesselSituationChanged(VesselSituationChangedMessage msg) + { + try + { + _logger.LogDebug($"Vessel situation changed from {Utility.SituationToString(msg.OldSituation)} to {Utility.SituationToString(msg.NewSituation)}."); + } + catch (Exception ex) + { + _logger.LogError($"Error handling VesselSituationChanged: {ex}"); + } + } + internal bool ConditionsReady() + { + try + { + _logger.LogDebug("Checking conditions..."); + + bool conditionsMet = + Utility.VesselSituations == KSP.Sim.impl.VesselSituations.PreLaunch || + Utility.VesselSituations == KSP.Sim.impl.VesselSituations.Landed || + Utility.VesselSituations == KSP.Sim.impl.VesselSituations.Flying || + Utility.GameState == GameState.FlightView || + Utility.GameState == GameState.VehicleAssemblyBuilder; + + _logger.LogDebug(conditionsMet + ? $"Conditions Ready! Vessel Situation: {Utility.SituationToString(Utility.VesselSituations)}, Game State: {Utility.GameStateToString(Utility.GameState)}." + : $"Conditions not met: {Utility.SituationToString(Utility.VesselSituations)}, Game State: {Utility.GameStateToString(Utility.GameState)}."); + + if (conditionsMet) + { + ModuleConditionsMet.Invoke(); + } + + return conditionsMet; + } + catch (Exception ex) + { + _logger.LogError($"Error checking conditions: {ex}"); + return false; + } + } + } +} \ No newline at end of file diff --git a/FFTProject/Managers/Manager.cs b/FFTProject/Managers/Manager.cs new file mode 100644 index 0000000..1d5ca3d --- /dev/null +++ b/FFTProject/Managers/Manager.cs @@ -0,0 +1,128 @@ +//|=====================Summary========================|0| +//| Manager |1| +//|by cvusmo===========================================|4| +//|====================================================|1| +using BepInEx.Logging; +using FFT.Controllers; +using FFT.Utilities; +using KSP.Game; + +namespace FFT.Managers +{ + public class Manager + { + private readonly List Modules = new List(); + internal ManualLogSource Logger { get; } + + + private readonly ConditionsManager _conditionsManager; + private readonly MessageManager _messageManager; + private readonly ModuleController _moduleController; + private readonly LoadModule _loadModule; + private readonly StartModule _startModule; + private bool isModuleLoaded = false; + public static Manager Instance { get; private set; } + private Manager() + { + Logger = BepInEx.Logging.Logger.CreateLogSource("FFT.Manager: "); + _conditionsManager = ConditionsManager.Instance ?? throw new InvalidOperationException("ConditionsManager is not initialized."); + _startModule = StartModule.Instance ?? throw new InvalidOperationException("StartModule is not initialized."); + _messageManager = MessageManager.Instance ?? throw new InvalidOperationException("MessageManager is not initialized."); + _moduleController = ModuleController.Instance ?? throw new InvalidOperationException("ModuleController is not initialized."); + _loadModule = LoadModule.Instance; + if (_loadModule == null) + { + throw new InvalidOperationException("LoadModule is not initialized."); + } + _loadModule.InitializeDependencies(); + + _messageManager.ModuleReadyToLoad += HandleModuleReadyToLoad; + ConditionsManager.Instance.ModuleConditionsMet += HandleModuleConditionsMet; + + InitializeManager(); + } + public static void InitializeInstance() + { + if (Instance != null) + throw new InvalidOperationException("Manager is already initialized."); + + Instance = new Manager(); + } + private void InitializeManager() + { + Utility.RefreshGameManager(); + UpdateMessageManager(); + } + private void UpdateMessageManager() + { + _messageManager.Update(); + Logger.LogDebug("MessageManager.Update called"); + } + private void HandleModuleConditionsMet() + { + LoadModuleForFlight(); + ConditionsManager.Instance.OnModuleLoaded(); + } + internal void LoadModuleForFlight() + { + if (_loadModule == null) + { + Logger.LogError("LoadModule is null. Cannot load for flight."); + return; + } + + if (!Modules.Contains(_loadModule)) + { + Modules.Add(_loadModule); + } + isModuleLoaded = true; + } + internal List InitializeModules() + { + if (_loadModule == null) + { + Logger.LogError("LoadModule is null. Cannot initialize modules."); + return new List(); + } + + return new List { _loadModule }; + } + public void HandleModuleReadyToLoad(ModuleController.ModuleType moduleType) + { + if (IsStartingModule()) + { + LoadModuleForFlight(); + //ModuleLoaded.Invoke(moduleType); + _startModule?.StartVentValve(); + if (Utility.ActiveVessel == null) + { + Logger.LogWarning("ActiveVessel is null. HandleModuleReadyToLoad may not function as expected."); + return; + } + } + } + public void UpdateStartModule() + { + if (!isModuleLoaded) + { + Logger.LogError("Module not loaded. Cannot start."); + return; + } + + _startModule?.StartVentValve(); + Logger.LogDebug("Module started"); + + ConditionsManager.Instance.HandleModuleLoaded(); + } + internal bool IsStartingModule() + { + bool isFlightActive = Modules.FirstOrDefault()?.RefreshActiveVessel?.IsFlightActive ?? false; + return (Utility.GameState == GameState.FlightView && isFlightActive); + } + internal void OnModuleReset() + { + _moduleController?.ResetAllModuleStates(); + Logger.LogInfo("All Modules have been Reset"); + } + } +} \ No newline at end of file diff --git a/FFTProject/Managers/MessageManager.cs b/FFTProject/Managers/MessageManager.cs new file mode 100644 index 0000000..416e54d --- /dev/null +++ b/FFTProject/Managers/MessageManager.cs @@ -0,0 +1,118 @@ +//|=====================Summary========================|0| +//| listens for specific messages/events |1| +//|by cvusmo===========================================|4| +//|====================================================|1| +using BepInEx.Logging; +using FFT.Controllers; +using FFT.Controllers.Interfaces; +using FFT.Utilities; +using KSP.Game; +using KSP.Messages; +using static FFT.Controllers.ModuleController; + +namespace FFT.Managers +{ + internal class MessageManager : RefreshVesselData, IListener.IGameStateListener, IListener.IVesselSituationListener + { + private static readonly ManualLogSource _logger = Logger.CreateLogSource("FFT.MessageManager"); + private static readonly object _lock = new object(); + + private static MessageManager _instance; + public static MessageManager Instance + { + get + { + lock (_lock) + { + return _instance ??= new MessageManager(_logger); + } + } + } + + private ConditionsManager _conditionsManager; + internal ConditionsManager conditionsmanager => _conditionsManager ??= ConditionsManager.Instance; + + internal event Action HandleGameStateActions = delegate { }; + internal event Action VesselStateChangedEvent = delegate { }; + internal event Action ModuleReadyToLoad = delegate { }; + public event Action GameStateEntered; + public event Action GameStateLeft; + public event Action VesselSituationChanged; + + private readonly Dictionary ModuleListeners; + private MessageManager(ManualLogSource logger) + { + ModuleListeners = new Dictionary + { + { ModuleType.ModuleVentValve, () => _logger.LogDebug("Listening to ModuleVentValve.") } + }; + SubscribeToMessages(); + } + public void SubscribeToMessages() + { + _logger.LogDebug($"Subscribing To Messages..."); + Utility.RefreshGameManager(); + + Utility.MessageCenter.Subscribe(msg => conditionsmanager.GameStateEnteredHandler(msg)); + Utility.MessageCenter.Subscribe(msg => conditionsmanager.GameStateLeftHandler(msg)); + Utility.MessageCenter.Subscribe(msg => conditionsmanager.HandleVesselSituationChanged((VesselSituationChangedMessage)msg)); + _logger.LogDebug($"Subscribed to: {nameof(GameStateEnteredMessage)}, {nameof(GameStateLeftMessage)}, {nameof(VesselSituationChangedMessage)}"); + } + internal void OnDestroy() + { + UnsubscribeFromMessages(); + } + private void UnsubscribeFromMessages() + { + Utility.MessageCenter.Unsubscribe(conditionsmanager.GameStateEnteredHandler); + Utility.MessageCenter.Unsubscribe(conditionsmanager.GameStateLeftHandler); + Utility.MessageCenter.Unsubscribe(msg => conditionsmanager.HandleVesselSituationChanged((VesselSituationChangedMessage)msg)); + } + internal void Update() + { + try + { + Utility.RefreshGameManager(); + if (Utility.GameState == GameState.FlightView && Utility.ActiveVessel != null) + { + Utility.RefreshActiveVesselAndCurrentManeuver(); + } + } + catch (Exception ex) + { + _logger.LogError($"Error during Update: {ex}"); + } + } + internal bool StartListening(ModuleType moduleType) + { + if (ModuleListeners.TryGetValue(moduleType, out Action listenerAction)) + { + listenerAction.Invoke(); + ModuleController.Instance.SetModuleState(moduleType, true); + return true; + } + return false; + } + internal bool AreConditionsReady() + { + return conditionsmanager.ConditionsReady(); + } + internal void HandleModuleReadyToLoad(ModuleType moduleType) + { + try + { + Utility.RefreshGameManager(); + + if (StartListening(moduleType) && AreConditionsReady() && ModuleController.Instance.GetModuleState(ModuleType.ModuleVentValve)) + { + ModuleReadyToLoad.Invoke(ModuleType.ModuleVentValve); + ModuleController.Instance.SetModuleState(ModuleType.ModuleVentValve, false); + } + } + catch (Exception ex) + { + _logger.LogError($"Error handling ModuleReadyToLoad: {ex}"); + } + } + } +} \ No newline at end of file diff --git a/FFTProject/Modules/Data_FuelTanks.cs b/FFTProject/Modules/Data_FuelTanks.cs index 4fb310a..0b2f6eb 100644 --- a/FFTProject/Modules/Data_FuelTanks.cs +++ b/FFTProject/Modules/Data_FuelTanks.cs @@ -1,11 +1,14 @@ -using UnityEngine; +using KSP.Sim.Definitions; +using UnityEngine; namespace FFT.Modules { - public class Data_FuelTanks : MonoBehaviour + [Serializable] + public class Data_FuelTanks : ModuleData { + public override Type ModuleType => typeof(Module_VentValve); public Dictionary fuelTankDict; - + public void Awake() { fuelTankDict = new Dictionary(); @@ -33,6 +36,6 @@ public void Awake() public GameObject SR812A; [SerializeField] public GameObject SR813; - + } } \ No newline at end of file diff --git a/FFTProject/Modules/Data_TriggerVFX.cs b/FFTProject/Modules/Data_TriggerVFX.cs deleted file mode 100644 index c808425..0000000 --- a/FFTProject/Modules/Data_TriggerVFX.cs +++ /dev/null @@ -1,17 +0,0 @@ -using FFT.Modules; -using KSP.Sim; -using KSP.Sim.Definitions; -using System; -using UnityEngine; - -namespace FFT.Modules -{ - [Serializable] - public class Data_TriggerVFX : ModuleData - { - public override Type ModuleType => typeof(Module_TriggerVFX); - - [KSPState] - public AnimationCurve VFXOpacityCurve; - } -} \ No newline at end of file diff --git a/FFTProject/Modules/Data_ValveParts.cs b/FFTProject/Modules/Data_ValveParts.cs index ee446c8..1f11ea8 100644 --- a/FFTProject/Modules/Data_ValveParts.cs +++ b/FFTProject/Modules/Data_ValveParts.cs @@ -1,5 +1,6 @@ using KSP.Sim; using KSP.Sim.Definitions; +using System.Collections.Generic; using UnityEngine; namespace FFT.Modules diff --git a/FFTProject/Modules/Data_VentValve.cs b/FFTProject/Modules/Data_VentValve.cs index 0c51237..c698cab 100644 --- a/FFTProject/Modules/Data_VentValve.cs +++ b/FFTProject/Modules/Data_VentValve.cs @@ -1,4 +1,5 @@ using FFT.Modules; +using System.Collections.Generic; using KSP.Sim; using KSP.Sim.Definitions; using System; @@ -12,12 +13,22 @@ public class Data_VentValve : ModuleData public override Type ModuleType => typeof(Module_VentValve); [KSPState] - public AnimationCurve VFXASLCurve = new AnimationCurve(new Keyframe(0, 1), new Keyframe(1000, 0)); + public AnimationCurve VFXASLCurve = new AnimationCurve(new Keyframe(0, 0), new Keyframe(1000, 1)); [KSPState] - public AnimationCurve VFXAGLCurve = new AnimationCurve(new Keyframe(0, 1), new Keyframe(1000, 0)); + public AnimationCurve VFXAGLCurve = new AnimationCurve(new Keyframe(0, 0), new Keyframe(1000, 1)); [KSPState] - public AnimationCurve VFXVerticalSpeedCurve; + public AnimationCurve VFXVerticalVelocity = new AnimationCurve(new Keyframe(0, 0), new Keyframe(200, 1)); [KSPState] - public AnimationCurve VFXHorizontalSpeedCurve; + public AnimationCurve VFXHorizontalVelocity = new AnimationCurve(new Keyframe(0, 0), new Keyframe(200, 1)); + [KSPState] + public AnimationCurve VFXDynamicPressure = new AnimationCurve(new Keyframe(101.325f, 0), new Keyframe(0.100f, 1)); + [KSPState] + public AnimationCurve VFXStaticPressure = new AnimationCurve(new Keyframe(99.65f, 0), new Keyframe(0, 1)); + [KSPState] + public AnimationCurve VFXAtmosphericTemperature = new AnimationCurve(new Keyframe(287.24f, 0), new Keyframe(0, 1)); + [KSPState] + public AnimationCurve VFXExternalTemperature = new AnimationCurve(new Keyframe(287.24f, 0), new Keyframe(0, 1)); + [KSPState] + public AnimationCurve VFXOpacityCurve = new AnimationCurve(new Keyframe(1, 1), new Keyframe(0.95f, 0)); } } \ No newline at end of file diff --git a/FFTProject/Modules/FuelTankDefinitions.cs b/FFTProject/Modules/FuelTankDefinitions.cs index 5412422..0f0c1b0 100644 --- a/FFTProject/Modules/FuelTankDefinitions.cs +++ b/FFTProject/Modules/FuelTankDefinitions.cs @@ -1,20 +1,31 @@ using KSP.Animation; using UnityEngine; +using System.Collections.Generic; using VFX; namespace FFT.Modules { public class FuelTankDefinitions : MonoBehaviour { - [SerializeField] - public List fuelTankDefintions; - [SerializeField] - public Data_TriggerVFX _dataTriggerVFX; - [SerializeField] - public Data_FuelTanks _dataFuelTanks; + public static FuelTankDefinitions Instance { get; private set; } + + [SerializeField] public List fuelTankDefintions; + [SerializeField] public Data_FuelTanks DataFuelTanks; public Dictionary fuelTanksDict = new Dictionary(); public bool isInitialized = false; + private void Awake() + { + if (Instance != null && Instance != this) + { + Destroy(this.gameObject); + } + else + { + Instance = this; + DontDestroyOnLoad(this.gameObject); + } + } public void PopulateFuelTanks(Data_FuelTanks data) { if (isInitialized) return; @@ -38,11 +49,11 @@ public GameObject GetFuelTank(string tankName) return null; } - public Module_TriggerVFX GetTriggerVFXModule(string tankName) + public Module_VentValve GetCoolingVFX(string tankName) { if (fuelTanksDict.TryGetValue(tankName, out var tank)) { - return tank.GetComponent(); + return tank.GetComponent(); } return null; diff --git a/FFTProject/Modules/Module_TriggerVFX.cs b/FFTProject/Modules/Module_TriggerVFX.cs deleted file mode 100644 index 178f963..0000000 --- a/FFTProject/Modules/Module_TriggerVFX.cs +++ /dev/null @@ -1,116 +0,0 @@ -using KSP.Animation; -using KSP.Sim.Definitions; -using UnityEngine; -using VFX; - -namespace FFT.Modules -{ - public class Module_TriggerVFX : PartBehaviourModule - { - public override Type PartComponentModuleType => typeof(PartComponentModule_TriggerVFX); - - [SerializeField] - public Data_TriggerVFX dataTriggerVFX; - [SerializeField] - public Data_FuelTanks _dataFuelTanks; - [SerializeField] - public GameObject CoolingVFX; - - public TriggerVFXFromAnimation TriggerVFX; - public DynamicGravityForVFX GravityForVFX; - public Animator Animator; - public ParticleSystem particleSystem; - internal float _fuelLevel; - internal bool activateTriggerVFX; - public override void OnInitialize() - { - base.OnInitialize(); - - if (PartBackingMode == PartBackingModes.Flight) - { - if (CoolingVFX) - { - Awake(); - } - } - } - public void Awake() - { - if (CoolingVFX != null) - { - particleSystem = CoolingVFX.GetComponentInChildren(); - Animator = CoolingVFX.GetComponentInParent(); - FFTPlugin.Logger.LogInfo("ModuleTriggerVFX has started."); - } - } - public override void AddDataModules() - { - base.AddDataModules(); - this.dataTriggerVFX ??= new Data_TriggerVFX(); - this.DataModules.TryAddUnique(this.dataTriggerVFX, out this.dataTriggerVFX); - } - public override void OnModuleFixedUpdate(float fixedDeltaTime) - { - base.OnModuleFixedUpdate(fixedDeltaTime); - double fillRatioSum = 0; - int totalResourceCount = 0; - - foreach (var container in part.Model.Containers) - { - foreach (var resourceID in container) - { - totalResourceCount++; - fillRatioSum += container.GetResourceFillRatio(resourceID); - } - } - - _fuelLevel = (float)(fillRatioSum / totalResourceCount); - float opacity = dataTriggerVFX.VFXOpacityCurve.Evaluate(_fuelLevel); - Animator.SetFloat("FuelLevel", _fuelLevel); - - if (!FuelLevelExceedsThreshold()) - { - StopVFX(); - } - else if (!Animator.GetCurrentAnimatorStateInfo(0).IsName("CoolingVFX_LOOP")) - { - StartVFX(); - } - } - public void StartVFX() - { - particleSystem.Play(); - EnableEmission(); - GravityForVFX.enabled = true; - } - public void StopVFX() - { - particleSystem.Stop(); ; - DisableEmission(); - } - internal void EnableEmission() - { - if (particleSystem != null) - { - var emission = particleSystem.emission; - emission.enabled = true; - } - } - internal void DisableEmission() - { - if (particleSystem != null) - { - var emission = particleSystem.emission; - emission.enabled = false; - } - } - internal bool FuelLevelExceedsThreshold() - { - return _fuelLevel > 0.95f; - } - public void Activate() - { - activateTriggerVFX = true; - } - } -} \ No newline at end of file diff --git a/FFTProject/Modules/Module_VentValve.cs b/FFTProject/Modules/Module_VentValve.cs index e2ca004..aeab61e 100644 --- a/FFTProject/Modules/Module_VentValve.cs +++ b/FFTProject/Modules/Module_VentValve.cs @@ -1,4 +1,8 @@ -using KSP.Animation; +//|=====================Summary========================|0| +//| Module for Cooling/Vent VFX |1| +//|by cvusmo===========================================|4| +//|====================================================|1| +using FFT.Utilities; using KSP.Sim.Definitions; using UnityEngine; using VFX; @@ -9,121 +13,237 @@ public class Module_VentValve : PartBehaviourModule { public override Type PartComponentModuleType => typeof(PartComponentModule_VentValve); - [SerializeField] - public Data_VentValve DataVentValve; - [SerializeField] - public Data_ValveParts DataValveParts; - [SerializeField] - public GameObject VentValveVFX; + [SerializeField] public GameObject VentValveVFX; + [SerializeField] public GameObject CoolingVFX; - //unity scripts - public TriggerVFXFromAnimation TriggerVFX; - public DynamicGravityForVFX GravityForVFX; + public DynamicGravityForVFX DynamicGravityVent, DynamicGravityCooling; public Animator Animator; - public ParticleSystem ParticleSystem; + public ParticleSystem PSVentValveVFX, PSCoolingVFX; + private Data_FuelTanks _dataFuelTanks; + private Data_ValveParts _dataValveParts; + private Data_VentValve _dataVentValve; + private FuelTankDefinitions _fuelTankDefinitions; + private VentValveDefinitions _ventValveDefinitions; + + private event Action VFXConditionsMet = delegate { }; - //internal FFT scripts internal float dynamicPressure, atmosphericTemp, externalTemp, verticalSpeed, horizontalSpeed, altitudeSeaLevel, altitudeGroundLevel; internal bool activateModuleVentValve = false; - internal float ASL, AGL; - public VentValveDefinitions VentValveDefinitions { get; private set; } + internal float ASL, AGL, VV, HV, DP, SP, AT, ET, FL; + internal bool InAtmo = true; + internal bool ActivateModule; + internal float updateFrequency = 0.5f; + internal float timeSinceLastUpdate = 0.0f; public RefreshVesselData RefreshVesselData { get; private set; } public override void OnInitialize() { base.OnInitialize(); - + InitializeData(); if (PartBackingMode == PartBackingModes.Flight) { - if (VentValveVFX) - { - Awake(); - } + InitializeVFX(); } } - public void Awake() + internal void InitializeData() { - ParticleSystem = VentValveVFX.GetComponentInChildren(); - DataValveParts = new Data_ValveParts(); - DataVentValve = new Data_VentValve(); - RefreshVesselData = new RefreshVesselData(); - Animator = GetComponentInParent(); - TriggerVFX = GetComponentInParent(); - GravityForVFX = GetComponentInParent(); + if (_fuelTankDefinitions == null) + { + _fuelTankDefinitions = FindObjectOfType(); + } + if (_ventValveDefinitions == null) + { + _ventValveDefinitions = FindObjectOfType(); + } - FFTPlugin.Logger.LogInfo("Module_VentValveVFX has started."); + if (_fuelTankDefinitions != null && _dataFuelTanks != null) + { + _fuelTankDefinitions.PopulateFuelTanks(_dataFuelTanks); + } + + if (_ventValveDefinitions != null && _dataValveParts != null) + { + _ventValveDefinitions.PopulateVentValve(_dataValveParts); + } } - public override void AddDataModules() + internal void InitializeVFX() { - base.AddDataModules(); + FFTPlugin.Instance._logger.LogInfo("Module_VentValveVFX has started."); + + if (VentValveVFX != null) + { + PSVentValveVFX = VentValveVFX.GetComponentInChildren(); + DynamicGravityVent = VentValveVFX.GetComponentInChildren(); + } - if (this.DataVentValve == null) + if (CoolingVFX != null) { - this.DataVentValve = new Data_VentValve(); - this.DataModules.TryAddUnique(this.DataVentValve, out this.DataVentValve); + PSCoolingVFX = CoolingVFX.GetComponentInChildren(); + DynamicGravityCooling = CoolingVFX.GetComponentInParent(); } + + Animator = GetComponentInParent(); } public override void OnModuleFixedUpdate(float fixedDeltaTime) { - base.OnModuleFixedUpdate(fixedDeltaTime); + if (ActivateModule) + { + base.OnModuleFixedUpdate(fixedDeltaTime); - RefreshVesselData.refreshActiveVessel.RefreshData(); - var activeVessel = RefreshVesselData.refreshActiveVessel.ActiveVessel; - RefreshVesselData.RefreshAll(activeVessel); + timeSinceLastUpdate += fixedDeltaTime; + + if (timeSinceLastUpdate >= updateFrequency) + { + RefreshDataAndVFX(); + timeSinceLastUpdate = 0.0f; + } + } + } + private void RefreshDataAndVFX() + { + try + { + RefreshVesselData.Instance.RefreshActiveVesselInstance.RefreshData(); + InitializeData(); + InitializeVFX(); + UpdateVFX(); + } + catch (Exception ex) + { + FFTPlugin.Instance._logger.LogError($"Failed to refresh data and VFX: {ex.Message}"); + } + } + private float GetCurveValue(AnimationCurve curve, float inputValue) + { + if (curve == null) + { + Debug.LogWarning("Curve is null. Defaulting to 0."); + return 0f; + } - var altitudeSeaLevel = RefreshVesselData.altitudeAsl.altitudeAsl; - float ASLFromCurve = DataVentValve.VFXASLCurve.Evaluate((float)altitudeSeaLevel); - ASL = ASLFromCurve; + return curve.Evaluate(inputValue); + } + private void UpdateVFX() + { + var vesselData = RefreshVesselData.Instance; + + ASL = GetCurveValue(_dataVentValve.VFXASLCurve, (float)vesselData.AltitudeAsl); Animator.SetFloat("ASL", ASL); - var altitudeGroundLevel = RefreshVesselData.altitudeAgl.altitudeAgl; - float AGLFromCurve = DataVentValve.VFXASLCurve.Evaluate((float)altitudeGroundLevel); - AGL = AGLFromCurve; + AGL = GetCurveValue(_dataVentValve.VFXAGLCurve, (float)vesselData.AltitudeAgl); Animator.SetFloat("AGL", AGL); - if (MaxAltitudeAchieved()) + VV = GetCurveValue(_dataVentValve.VFXVerticalVelocity, (float)vesselData.VerticalVelocity); + Animator.SetFloat("VV", VV); + + HV = GetCurveValue(_dataVentValve.VFXHorizontalVelocity, (float)vesselData.HorizontalVelocity); + Animator.SetFloat("HV", HV); + + DP = GetCurveValue(_dataVentValve.VFXDynamicPressure, (float)vesselData.DynamicPressure_kPa); + Animator.SetFloat("DP", DP); + + SP = GetCurveValue(_dataVentValve.VFXStaticPressure, (float)vesselData.StaticPressure_kPa); + Animator.SetFloat("SP", SP); + + AT = GetCurveValue(_dataVentValve.VFXAtmosphericTemperature, (float)vesselData.AtmosphericTemperature); + Animator.SetFloat("AT", AT); + + ET = GetCurveValue(_dataVentValve.VFXExternalTemperature, (float)vesselData.ExternalTemperature); + Animator.SetFloat("ET", ET); + + InAtmo = vesselData.IsInAtmosphere; + + var fuelPercentage = vesselData.FuelPercentage; + double scaledFuelPercentage = vesselData.FuelPercentage / 100.0; + FL = _dataVentValve.VFXOpacityCurve.Evaluate((float)scaledFuelPercentage); + Animator.SetFloat("FL", FL); + + if (InAtmo) + { + ActivateModule = false; + } + + if (ActivateModule) { - StartVFX(); + VFXConditionsMet.Invoke(); } - else if (ASL == 0 && AGL == 0) + } + internal void OnPartModuleUpdate() + { + if (this.IsActive) { - StopVFX(); + RefreshDataAndVFX(); } + } + internal void OnPartModuleFixedUpdate(float fixedDeltaTime) + { + if (this.IsActive) + { + if (PartBackingMode == PartBackingModes.Flight) + { + timeSinceLastUpdate += fixedDeltaTime; + if (timeSinceLastUpdate >= updateFrequency) + { + RefreshDataAndVFX(); + timeSinceLastUpdate = 0.0f; + } + } + } } - public void StartVFX() + internal void StartVFX() { EnableEmission(); - ParticleSystem.Play(); + PSVentValveVFX.Play(); } - public void StopVFX() + internal void StopVFX() { DisableEmission(); - ParticleSystem.Stop(); + if (VentValveVFX != null) + { + PSVentValveVFX.Stop(); + } + if (CoolingVFX != null) + { + PSCoolingVFX.Stop(); + } } internal void EnableEmission() { - if (ParticleSystem != null) + if (VentValveVFX != null) + { + var emission = PSVentValveVFX.emission; + emission.enabled = true; + } + if (CoolingVFX != null) { - var emission = ParticleSystem.emission; + var emission = PSCoolingVFX.emission; emission.enabled = true; } } internal void DisableEmission() { - if (ParticleSystem != null) + if (VentValveVFX != null) { - var emission = ParticleSystem.emission; + var emission = PSVentValveVFX.emission; + emission.enabled = false; + } + if (CoolingVFX != null) + { + var emission = PSCoolingVFX.emission; emission.enabled = false; } } - - internal bool MaxAltitudeAchieved() + internal void Activate() { - return ASL > 0 || AGL > 0; + ActivateModule = true; + FFTPlugin.Instance._logger.LogInfo("Module_VentValve activated."); + StartVFX(); } - public void Activate() + internal void Deactivate() { - activateModuleVentValve = true; + ActivateModule = false; + FFTPlugin.Instance._logger.LogInfo("Module_VentValve deactivated."); + StopVFX(); } } } \ No newline at end of file diff --git a/FFTProject/Modules/PartComponentModule_TriggerVFX.cs b/FFTProject/Modules/PartComponentModule_TriggerVFX.cs deleted file mode 100644 index 8de97ba..0000000 --- a/FFTProject/Modules/PartComponentModule_TriggerVFX.cs +++ /dev/null @@ -1,11 +0,0 @@ -using FFT.Modules; -using KSP.Sim.impl; -using System; - -namespace FFT.Modules -{ - public class PartComponentModule_TriggerVFX : PartComponentModule - { - public override Type PartBehaviourModuleType => typeof(Module_TriggerVFX); - } -} \ No newline at end of file diff --git a/FFTProject/Modules/PartComponentModule_VentValve.cs b/FFTProject/Modules/PartComponentModule_VentValve.cs index 482a0e5..c9ea825 100644 --- a/FFTProject/Modules/PartComponentModule_VentValve.cs +++ b/FFTProject/Modules/PartComponentModule_VentValve.cs @@ -8,5 +8,4 @@ public class PartComponentModule_VentValve : PartComponentModule { public override Type PartBehaviourModuleType => typeof(Module_VentValve); } -} - +} \ No newline at end of file diff --git a/FFTProject/Modules/RefreshVesselData.cs b/FFTProject/Modules/RefreshVesselData.cs deleted file mode 100644 index 2224dc6..0000000 --- a/FFTProject/Modules/RefreshVesselData.cs +++ /dev/null @@ -1,175 +0,0 @@ -using KSP.Game; -using KSP.Messages; -using KSP.Messages.PropertyWatchers; -using KSP.Sim.DeltaV; -using KSP.Sim.impl; -using KSP.Sim.Maneuver; -using static KSP.Rendering.Planets.PQSData; - -namespace FFT.Modules -{ - public class RefreshVesselData - { - public VesselComponent VesselComponent; - public ManeuverNodeData CurrentManeuver; - public GameStateConfiguration GameState; - public MessageCenter MessageCenter; - public VesselDeltaVComponent VesselDeltaVComponentOAB; - public static double UniversalTime => GameManager.Instance.Game.UniverseModel.UniversalTime; - public VesselComponent activeVessel { get; set; } - public RefreshActiveVessel refreshActiveVessel { get; set; } - public AltitudeAgl altitudeAgl { get; private set; } - public AltitudeAsl altitudeAsl { get; private set; } - public AltitudeFromScenery altitudeFromScenery { get; private set; } - public VerticalVelocity verticalVelocity { get; private set; } - public HorizontalVelocity horizontalVelocity { get; private set; } - public DynamicPressure_kPa dynamicPressure_KPa { get; private set; } - public StaticPressure_kPa staticPressure_KPa { get; private set; } - public AtmosphericTemperature atmosphericTemperature { get; private set; } - public ExternalTemperature externalTemperature { get; private set; } - public void RefreshGameManager(VesselComponent activeVessel) - { - GameState = GameManager.Instance?.Game?.GlobalGameState?.GetGameState(); - MessageCenter = GameManager.Instance?.Game?.Messages; - } - public void RefreshStagesOAB(VesselComponent activeVessel) - { - VesselDeltaVComponentOAB = GameManager.Instance?.Game?.OAB?.Current?.Stats?.MainAssembly?.VesselDeltaV; - } - public string SituationToString(VesselSituations situation) - { - return situation switch - { - VesselSituations.PreLaunch => "Pre-Launch", - VesselSituations.Landed => "Landed", - VesselSituations.Splashed => "Splashed down", - VesselSituations.Flying => "Flying", - VesselSituations.SubOrbital => "Suborbital", - VesselSituations.Orbiting => "Orbiting", - VesselSituations.Escaping => "Escaping", - _ => "UNKNOWN", - }; - } - public string BiomeToString(BiomeSurfaceData biome) - { - string result = biome.type.ToString().ToLower().Replace('_', ' '); - return result.Substring(0, 1).ToUpper() + result.Substring(1); - } - public class RefreshActiveVessel - { - public VesselComponent ActiveVessel { get; private set; } - - public void RefreshData() - { - ActiveVessel = GameManager.Instance?.Game?.ViewController?.GetActiveVehicle(true)?.GetSimVessel(true); - } - } - public class AltitudeAgl - { - public double altitudeAgl { get; private set; } - - public void RefreshData(VesselComponent activeVessel) - { - altitudeAgl = activeVessel.AltitudeFromScenery; - } - } - public class AltitudeAsl - { - public double altitudeAsl { get; private set; } - - public void RefreshData(VesselComponent activeVessel) - { - altitudeAsl = activeVessel.AltitudeFromSeaLevel; - } - } - public class AltitudeFromScenery - { - public double altitudeFromScenery { get; private set; } - - public void RefreshData(VesselComponent activeVessel) - { - altitudeFromScenery = activeVessel.AltitudeFromTerrain; - } - } - public class VerticalVelocity - { - public double verticalVelocity { get; private set; } - - public void RefreshData(VesselComponent activeVessel) - { - verticalVelocity = activeVessel.VerticalSrfSpeed; - } - } - public class HorizontalVelocity - { - public double horizontalVelocity { get; private set; } - - public void RefreshData(VesselComponent activeVessel) - { - horizontalVelocity = activeVessel.HorizontalSrfSpeed; - } - } - public class DynamicPressure_kPa - { - public double dynamicPressure_kPa { get; private set; } - - public void RefreshData(VesselComponent activeVessel) - { - dynamicPressure_kPa = activeVessel.DynamicPressure_kPa; - } - } - public class StaticPressure_kPa - { - public double staticPressure_kPa { get; private set; } - - public void RefreshData(VesselComponent activeVessel) - { - staticPressure_kPa = activeVessel.StaticPressure_kPa; - } - } - public class AtmosphericTemperature - { - public double atmosphericTemperature { get; private set; } - - public void RefreshData(VesselComponent activeVessel) - { - atmosphericTemperature = activeVessel.AtmosphericTemperature; - } - } - public class ExternalTemperature - { - public double externalTemperature { get; private set; } - - public void RefreshData(VesselComponent activeVessel) - { - externalTemperature = activeVessel.ExternalTemperature; - } - } - public RefreshVesselData() - { - this.refreshActiveVessel = new RefreshActiveVessel(); - this.altitudeAgl = new AltitudeAgl(); - this.altitudeAsl = new AltitudeAsl(); - this.altitudeFromScenery = new AltitudeFromScenery(); - this.verticalVelocity = new VerticalVelocity(); - this.horizontalVelocity = new HorizontalVelocity(); - this.dynamicPressure_KPa = new DynamicPressure_kPa(); - this.staticPressure_KPa = new StaticPressure_kPa(); - this.atmosphericTemperature = new AtmosphericTemperature(); - this.externalTemperature = new ExternalTemperature(); - } - public void RefreshAll(VesselComponent activeVessel) - { - this.refreshActiveVessel.RefreshData(); - this.altitudeAgl.RefreshData(activeVessel); - this.altitudeAsl.RefreshData(activeVessel); - this.altitudeFromScenery.RefreshData(activeVessel); - this.verticalVelocity.RefreshData(activeVessel); - this.horizontalVelocity.RefreshData(activeVessel); - this.dynamicPressure_KPa.RefreshData(activeVessel); - this.staticPressure_KPa.RefreshData(activeVessel); - this.atmosphericTemperature.RefreshData(activeVessel); - this.externalTemperature.RefreshData(activeVessel); - } - } -} \ No newline at end of file diff --git a/FFTProject/Modules/VentValveDefinitions.cs b/FFTProject/Modules/VentValveDefinitions.cs index ace0429..982db2b 100644 --- a/FFTProject/Modules/VentValveDefinitions.cs +++ b/FFTProject/Modules/VentValveDefinitions.cs @@ -8,15 +8,26 @@ namespace FFT.Modules { public class VentValveDefinitions : MonoBehaviour { - [SerializeField] - public List ventValveDefinitions; - [SerializeField] - public Data_VentValve _dataVentValve; - [SerializeField] - public Data_ValveParts _dataValveParts; + public static VentValveDefinitions Instance { get; private set; } + + [SerializeField] public List ventValveDefinitions; + [SerializeField] public Data_VentValve _dataVentValve; + [SerializeField] public Data_ValveParts _dataValveParts; public Dictionary ventValveDict = new Dictionary(); public bool isInitialized = false; + private void Awake() + { + if (Instance != null && Instance != this) + { + Destroy(this.gameObject); + } + else + { + Instance = this; + DontDestroyOnLoad(this.gameObject); + } + } public void PopulateVentValve(Data_ValveParts data) { if (isInitialized) return; @@ -44,7 +55,5 @@ public Module_VentValve GetVentValveModule(string valveName) return null; } - } -} - +} \ No newline at end of file diff --git a/FFTProject/Utilities/RefreshVesselData.cs b/FFTProject/Utilities/RefreshVesselData.cs new file mode 100644 index 0000000..c42aa77 --- /dev/null +++ b/FFTProject/Utilities/RefreshVesselData.cs @@ -0,0 +1,60 @@ +//|=====================Summary========================|0| +//| Refreshes Data & gets values to update VFX |1| +//|by cvusmo===========================================|4| +//|====================================================|1| + +using FFT.Managers; +using KSP.Game; +using KSP.Sim.impl; +using System; +using System.Collections.Generic; + +namespace FFT.Utilities +{ + public class RefreshVesselData + { + private static readonly Lazy _instance = new Lazy(() => new RefreshVesselData()); + public static RefreshVesselData Instance => _instance.Value; + + private DateTime _lastRefreshTime; + private readonly TimeSpan _refreshInterval = TimeSpan.FromSeconds(3); + private Dictionary _cache = new Dictionary(); + public RefreshActiveVessel RefreshActiveVesselInstance { get; } = new RefreshActiveVessel(); + internal RefreshVesselData() => RefreshActiveVesselInstance.RefreshData(); + public T GetCachedValue(string key, Func fetchValue) + { + if (_cache.ContainsKey(key) && DateTime.UtcNow - _lastRefreshTime <= _refreshInterval) + { + return (T)_cache[key]; + } + else + { + var value = fetchValue(RefreshActiveVesselInstance.ActiveVessel); + _cache[key] = value; + return value; + } + } + public class RefreshActiveVessel + { + public VesselComponent ActiveVessel { get; private set; } + internal bool IsFlightActive = false; + + public void RefreshData() + { + ActiveVessel = GameManager.Instance?.Game?.ViewController?.GetActiveVehicle(true)?.GetSimVessel(true); + IsFlightActive = true; + } + } + public double AltitudeAgl => GetCachedValue("AltitudeAgl", vessel => vessel.AltitudeFromScenery); + public double AltitudeAsl => GetCachedValue("AltitudeAsl", vessel => vessel.AltitudeFromSeaLevel); + public double AltitudeFromScenery => GetCachedValue("AltitudeFromScenery", vessel => vessel.AltitudeFromTerrain); + public double VerticalVelocity => GetCachedValue("VerticalVelocity", vessel => vessel.VerticalSrfSpeed); + public double HorizontalVelocity => GetCachedValue("HorizontalVelocity", vessel => vessel.HorizontalSrfSpeed); + public double DynamicPressure_kPa => GetCachedValue("DynamicPressure_kPa", vessel => vessel.DynamicPressure_kPa); + public double StaticPressure_kPa => GetCachedValue("StaticPressure_kPa", vessel => vessel.StaticPressure_kPa); + public double AtmosphericTemperature => GetCachedValue("AtmosphericTemperature", vessel => vessel.AtmosphericTemperature); + public double ExternalTemperature => GetCachedValue("ExternalTemperature", vessel => vessel.ExternalTemperature); + public bool IsInAtmosphere => GetCachedValue("IsInAtmosphere", vessel => vessel.IsInAtmosphere); + public double FuelPercentage => GetCachedValue("FuelPercentage", vessel => vessel.FuelPercentage); + } +} diff --git a/FFTProject/Utilities/SizesUI.cs b/FFTProject/Utilities/SizesUI.cs deleted file mode 100644 index 13249b5..0000000 --- a/FFTProject/Utilities/SizesUI.cs +++ /dev/null @@ -1,96 +0,0 @@ -using UnityEngine; - -namespace FFT.Utilities -{ - public class SizesUI - { - public static readonly float[] Sizes = { 0.325f, 0.625f, 0.9375f, 1.25f, 1.875f, 2.5f, 3.125f, 3.75f, 4.375f, 5 }; - public static (int size, float visualSize) GetSize(float toGet) - { - switch (toGet) - { - case 0.325f: - return new(0, 0f); - case 0.625f: - return new(0, 0.25f); - case 0.9235f: - return new(0, 0.5f); - case 1.25f: - return new(1, 1); - case 1.875f: - return new(1, 1.5f); - case 2.5f: - return new(2, 2f); - case 3.125f: - return new(2, 2.5f); - case 3.75f: - return new(3, 3f); - case 4.375f: - return new(3, 3.5f); - case 5f: - return new(4, 4f); - default: - return default; - } - } - private int currentIndex = 4; - private int maxIndex = Sizes.Length; - public float Value => Sizes[currentIndex]; - - public SizesUI() - { - - } - - public SizesUI(float diameter) - { - int index = -1; - float difference = float.MaxValue; - - for (int i = 0; i < Sizes.Length; i++) - { - if (Math.Abs(diameter - Sizes[i]) < difference) - { - difference = Math.Abs(diameter - Sizes[i]); - index = i; - } - } - currentIndex = index; - } - - public bool DrawGUI() - { - bool changed = false; - GUILayout.BeginHorizontal(); - - if (GUILayout.Button("<")) - { - currentIndex--; - if (currentIndex < 0) - currentIndex = Sizes.Length - 1; - changed = true; - } - - GUILayout.Label(Sizes[currentIndex].ToString("0.0000m"), new GUIStyle(SpaceWarp.API.UI.Skins.ConsoleSkin.label) { alignment = TextAnchor.MiddleCenter, fontStyle = FontStyle.Bold }); - - if (GUILayout.Button(">")) - { - currentIndex++; - if (currentIndex >= maxIndex) - currentIndex = 0; - - changed = true; - } - - GUILayout.EndHorizontal(); - return changed; - } - - internal void SetLimit(int size) - { - maxIndex = size; - if (currentIndex > maxIndex) - currentIndex = maxIndex - 1; - } - } -} \ No newline at end of file diff --git a/FFTProject/Utilities/Utility.cs b/FFTProject/Utilities/Utility.cs new file mode 100644 index 0000000..56da985 --- /dev/null +++ b/FFTProject/Utilities/Utility.cs @@ -0,0 +1,78 @@ +//|=====================Summary========================|0| +//| helper methods & properties |1| +//|by cvusmo===========================================|4| +//|====================================================|1| + +using BepInEx.Logging; +using KSP.Game; +using KSP.Messages; +using KSP.Sim.impl; +using KSP.Sim.Maneuver; +using static KSP.Rendering.Planets.PQSData; + +namespace FFT.Utilities +{ + public static class Utility + { + private static ManualLogSource _logger = BepInEx.Logging.Logger.CreateLogSource("FFT.Utility: "); + public static VesselComponent ActiveVessel; + public static ManeuverNodeData CurrentManeuver; + public static GameState GameState; + public static VesselSituations VesselSituations { get; private set; } + public static MessageCenter MessageCenter { get; private set; } + public static double UniversalTime => GameManager.Instance.Game.UniverseModel.UniversalTime; + public static void Initialize() + { + RefreshGameManager(); + RefreshActiveVesselAndCurrentManeuver(); + } + public static void RefreshActiveVesselAndCurrentManeuver() + { + ActiveVessel = GameManager.Instance?.Game?.ViewController?.GetActiveVehicle(true)?.GetSimVessel(true); + CurrentManeuver = ActiveVessel != null ? GameManager.Instance?.Game?.SpaceSimulation.Maneuvers.GetNodesForVessel(ActiveVessel.GlobalId).FirstOrDefault() : null; + } + public static void RefreshGameManager() + { + var state = GameManager.Instance?.Game?.GlobalGameState?.GetGameState(); + MessageCenter = GameManager.Instance?.Game?.Messages; + } + public static string SituationToString(VesselSituations situation) + { + return situation switch + { + VesselSituations.PreLaunch => "Pre-Launch", + VesselSituations.Landed => "Landed", + VesselSituations.Splashed => "Splashed down", + VesselSituations.Flying => "Flying", + VesselSituations.SubOrbital => "Suborbital", + VesselSituations.Orbiting => "Orbiting", + VesselSituations.Escaping => "Escaping", + _ => "UNKNOWN", + }; + } + public static string GameStateToString(GameState gamestate) + { + return gamestate switch + { + GameState.KerbalSpaceCenter => "KSC", + GameState.Launchpad => "LaunchPad", + GameState.Runway => "Runway", + GameState.FlightView => "FlightView", + GameState.MainMenu => "MainMenu", + GameState.BaseAssemblyEditor => "BaseAssemblyEditor", + GameState.Map3DView => "Map3DView", + GameState.VehicleAssemblyBuilder => "VAB", + _ => "UNKNOWN GAMESTATE", + }; + } + public static string BiomeToString(BiomeSurfaceData biome) + { + string result = biome.type.ToString().ToLower().Replace('_', ' '); + return result.Substring(0, 1).ToUpper() + result.Substring(1); + } + public static double RadiansToDegrees(double radians) + { + return radians * PatchedConicsOrbit.Rad2Deg; + } + } +} \ No newline at end of file