diff --git a/Example mod/VehicleUpgradeExample_BelowZero.cs b/Example mod/VehicleUpgradeExample_BelowZero.cs new file mode 100644 index 000000000..92386173b --- /dev/null +++ b/Example mod/VehicleUpgradeExample_BelowZero.cs @@ -0,0 +1,143 @@ +#if BELOWZERO +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using BepInEx; +using Nautilus.Assets; +using Nautilus.Assets.Gadgets; +using Nautilus.Assets.PrefabTemplates; +using Nautilus.Handlers; +using UnityEngine; +using UWE; + +namespace Nautilus.Examples; + +[BepInPlugin("com.snmodding.nautilus.vehicleupgrades", "Nautilus Vehicle Upgrades Example Mod", Nautilus.PluginInfo.PLUGIN_VERSION)] +public class VehicleUpgradeExample : BaseUnityPlugin +{ + GameObject electricalDefensePrefab; + private void Awake() + { + + var DefenseRecipe = new Crafting.RecipeData() + { + craftAmount = 1, + Ingredients = new List() + { + new Ingredient(TechType.Polyaniline, 1), + new Ingredient(TechType.Quartz, 2), + new Ingredient(TechType.Battery, 2), + new Ingredient(TechType.AluminumOxide, 1) + } + }; + Action onAdded = (Hoverbike instance, int slotID) => + { + if (electricalDefensePrefab == null) + CoroutineHost.StartCoroutine(InitElectricalPrefab()); + Subtitles.Add("Hoverbike perimeter defense upgrade module engaged."); + }; + + Action onRemoved = (Hoverbike instance, int slotID) => + { + Subtitles.Add("Hoverbike perimeter defense upgrade module disengaged. Caution advised."); + }; + + Action onUsed = (Hoverbike instance, int slotID, float charge, float chargeScalar) => + { + if (electricalDefensePrefab != null) + { + var electricalDefense = Utils.SpawnZeroedAt(electricalDefensePrefab, instance.transform, false).GetComponent(); + electricalDefense.charge = charge; + electricalDefense.chargeScalar = chargeScalar; + } + else + { + CoroutineHost.StartCoroutine(InitElectricalPrefab()); + Subtitles.Add("Hoverbike perimeter defense upgrade was not initialized. Initializing."); + } + }; + + /* + * Here we will make an Hoverbike module. + * This system is very easy to use but was complex enough to implement. + */ + var hoverbikePeriDefInfo = PrefabInfo.WithTechType( + "HoverbikePerimeterDefense", + "Hoverbike Perimeter Defense Module", + "A perimeter defense upgrade for the Hoverbike... No one knows if it will be useful.", + techTypeOwner: Assembly.GetExecutingAssembly()) + .WithIcon(SpriteManager.Get(TechType.SeaTruckUpgradePerimeterDefense)); + + CustomPrefab hoverbikePeriDefPrefab = new CustomPrefab(hoverbikePeriDefInfo); + CloneTemplate seatruckPeriDefClone = new CloneTemplate(hoverbikePeriDefInfo, TechType.SeaTruckUpgradePerimeterDefense); + + hoverbikePeriDefPrefab.SetGameObject(seatruckPeriDefClone); + + hoverbikePeriDefPrefab.SetRecipe(DefenseRecipe) + .WithFabricatorType(CraftTree.Type.Fabricator) + .WithStepsToFabricatorTab("Machines") + .WithCraftingTime(2.5f); + + hoverbikePeriDefPrefab.SetPdaGroupCategory(TechGroup.VehicleUpgrades, TechCategory.VehicleUpgrades); + hoverbikePeriDefPrefab.SetUnlock(TechType.Polyaniline); + hoverbikePeriDefPrefab.SetVehicleUpgradeModule(EquipmentType.HoverbikeModule, QuickSlotType.Chargeable) + .WithCooldown(5f) + .WithEnergyCost(5f) + .WithMaxCharge(10f) + .WithOnModuleAdded(onAdded) + .WithOnModuleRemoved(onRemoved) + .WithOnModuleUsed(onUsed); + + hoverbikePeriDefPrefab.Register(); + + + /* + * Now let's make one that can be selected and charged. + */ + var hoverbikeSelectDef = PrefabInfo.WithTechType("HoverbikeSelectDefense", + "Hoverbike Selectable Perimeter Defense Module", + "The same than the other module, but selectable.") + .WithIcon(SpriteManager.Get(TechType.SeaTruckUpgradePerimeterDefense)); + + var hoverbikeSelDefPrefab = new CustomPrefab(hoverbikeSelectDef); + + hoverbikeSelDefPrefab.SetGameObject(seatruckPeriDefClone); + hoverbikeSelDefPrefab.SetPdaGroupCategory(TechGroup.VehicleUpgrades, TechCategory.VehicleUpgrades) + .WithCompoundTechsForUnlock(new() + { + TechType.Polyaniline + }); + hoverbikeSelDefPrefab.SetRecipe(DefenseRecipe) + .WithFabricatorType(CraftTree.Type.Fabricator) + .WithStepsToFabricatorTab("Machines") + .WithCraftingTime(2.5f); + hoverbikeSelDefPrefab.SetVehicleUpgradeModule(EquipmentType.HoverbikeModule, QuickSlotType.SelectableChargeable) + .WithCooldown(5f) + .WithEnergyCost(5f) + .WithMaxCharge(10f) + .WithOnModuleAdded(onAdded) + .WithOnModuleRemoved(onRemoved) + .WithOnModuleUsed(onUsed); + + hoverbikeSelDefPrefab.Register(); + } + + internal IEnumerator InitElectricalPrefab() + { + /* + * This function is getting the ElectricalDefense prefab from the SeaTruckUpgrades. + * You can see that this monobehaviour is not stored in the upgrade prefab. + */ + var task = CraftData.GetPrefabForTechTypeAsync(TechType.SeaTruck); + yield return task; + var result = task.GetResult(); + var seatruckUpgrades = result.GetComponent(); + electricalDefensePrefab = seatruckUpgrades.electricalDefensePrefab; + Subtitles.Add("Hoverbike perimeter defense upgrade is initialized."); + } +} +#endif diff --git a/Example mod/VehicleUpgradesExample.cs b/Example mod/VehicleUpgradesExample_Subnautica.cs similarity index 79% rename from Example mod/VehicleUpgradesExample.cs rename to Example mod/VehicleUpgradesExample_Subnautica.cs index 950bc9042..bd8856ae5 100644 --- a/Example mod/VehicleUpgradesExample.cs +++ b/Example mod/VehicleUpgradesExample_Subnautica.cs @@ -29,7 +29,7 @@ private void Awake() * Here, we're assigning the hull icon for our item. * You may also use a custom image icon by calling the ImageUtils.LoadSpriteFromFile method or with AssetBundles, like mentioned higher. */ - depthUpgradeInfo.WithIcon(SpriteManager.Get(TechType.HullReinforcementModule2)); + depthUpgradeInfo.WithIcon(SpriteManager.Get(TechType.VehicleHullModule3)); Logger.LogDebug("Registerd depth upgrade icon"); /* @@ -157,45 +157,6 @@ private void Awake() defense.chargeScalar = chargeScalar; }); selfDefMK2prefab.Register(); - - /* - * And finally, a CUSTOMIZABLE-after-restart depth module! - */ - - // PLANNED FOR ANOTHER PR. -/* - PrefabInfo prefabInfo = PrefabInfo.WithTechType("SeamothCustomDepthModule", "Seamoth Variable Depth Upgrade", "Customize the depth of your upgrade from the game settings!") - .WithIcon(SpriteManager.Get(TechType.SeamothReinforcementModule)) - .WithSizeInInventory(new Vector2int(1, 1)); - - CustomPrefab prefab = new(prefabInfo); - CloneTemplate clone = new(prefabInfo, TechType.SeamothReinforcementModule); - - prefab.SetGameObject(clone); - prefab.SetPdaGroupCategory(TechGroup.VehicleUpgrades, TechCategory.VehicleUpgrades); - prefab.SetRecipe(new Crafting.RecipeData() - { - craftAmount = 1, - Ingredients = new List() - { - new CraftData.Ingredient(TechType.SeamothReinforcementModule), - new CraftData.Ingredient(TechType.Diamond, 2) - } - }) - .WithFabricatorType(CraftTree.Type.SeamothUpgrades) - .WithCraftingTime(4f) - .WithStepsToFabricatorTab("SeamothModules"); - prefab.SetVehicleUpgradeModule(EquipmentType.SeamothModule) - .WithDepthUpgrade(() => config.MaxDepth, true) - .WithOnModuleAdded((Vehicle vehicleInstance, int slotId) => { - Subtitles.Add($"New seamoth depth: {config.MaxDepth} meters.\nAdded in slot #{slotId + 1}."); - }) - .WithOnModuleRemoved((Vehicle vehicleInstance, int slotId) => - { - Subtitles.Add($"Seamoth depth module removed from slot #{slotId + 1}!"); - }); - prefab.Register(); -*/ } } diff --git a/Nautilus/Assets/Gadgets/UpgradeModuleGadget.cs b/Nautilus/Assets/Gadgets/UpgradeModuleGadget.cs index fc5962b06..cfd9698b7 100644 --- a/Nautilus/Assets/Gadgets/UpgradeModuleGadget.cs +++ b/Nautilus/Assets/Gadgets/UpgradeModuleGadget.cs @@ -2,6 +2,7 @@ using Nautilus.Handlers; using Nautilus.Utility; using Nautilus.Patchers; +using Nautilus.MonoBehaviours; namespace Nautilus.Assets.Gadgets; @@ -410,7 +411,7 @@ protected internal override void Build() VehicleUpgradesPatcher.SeatruckUpgradeModules.Add(prefab.Info.TechType, prefab); break; case EquipmentType.HoverbikeModule: - VehicleUpgradesPatcher.SnowbikeUpgradeModules.Add(prefab.Info.TechType, prefab); + HoverbikeModulesSupport.CustomModules.Add(prefab.Info.TechType, prefab); break; #elif SUBNAUTICA case EquipmentType.SeamothModule: diff --git a/Nautilus/MonoBehaviours/HoverbikeModulesSupport.cs b/Nautilus/MonoBehaviours/HoverbikeModulesSupport.cs new file mode 100644 index 000000000..7ed9909ad --- /dev/null +++ b/Nautilus/MonoBehaviours/HoverbikeModulesSupport.cs @@ -0,0 +1,635 @@ +#if BELOWZERO +using Nautilus.Assets; +using Nautilus.Assets.Gadgets; +using Nautilus.Utility; +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace Nautilus.MonoBehaviours; + +/// +/// A custom monobehaviour made to support more modules types on hoverbike. +/// +internal class HoverbikeModulesSupport : MonoBehaviour, IQuickSlots +{ + public event QuickSlots.OnBind onBind; + public event QuickSlots.OnToggle onToggle; + public event QuickSlots.OnSelect onSelect; + internal Hoverbike hoverbike; + + internal static IDictionary CustomModules = new SelfCheckingDictionary("CustomModules"); + + internal float[] quickSlotCooldown; + internal float[] quickSlotTimeUsed; + internal float[] quickSlotCharge; + internal bool[] quickSlotToggled; + internal int activeSlot; + internal Dictionary slotIndices; + internal bool ignoreInput { get; private set; } + private bool isInitialized = false; + + private void LazyInitialize() + { + if (isInitialized) + return; + hoverbike = GetComponent(); + isInitialized = true; + slotIndices = new Dictionary(); + int i = 0; + foreach(string text in hoverbike.slotIDs) + { + slotIndices.Add(hoverbike.slotIDs[i], i); + i++; + } + quickSlotCooldown = new float[hoverbike.slotIDs.Length]; + quickSlotTimeUsed = new float[hoverbike.slotIDs.Length]; + quickSlotCharge = new float[hoverbike.slotIDs.Length]; + quickSlotToggled = new bool[hoverbike.slotIDs.Length]; + hoverbike.modules.onEquip += OnEquip; + hoverbike.modules.onUnequip += OnUnequip; + hoverbike.UnlockDefaultModuleSlots(); + onToggle += OnToggle; + InternalLogger.Debug($"Initialized {nameof(HoverbikeModulesSupport)}."); + } + + private void Awake() + { + InternalLogger.Debug($"LazyInitialize {nameof(HoverbikeModulesSupport)}."); + LazyInitialize(); + } + + internal void EnterVehicle() + { + try + { + uGUI.main.quickSlots.SetTarget(this); + } + catch(Exception e) + { + InternalLogger.Error($"An error occured while entering hoverbike.\n{e}"); + } + InternalLogger.Info("Player entered an hoverbike."); + } + + internal void ExitVehicle() + { + try + { + uGUI.main.quickSlots.SetTarget(null); + } + catch (Exception e) + { + InternalLogger.Error($"An error occured while exiting hoverbike.\n{e}"); + } + InternalLogger.Info("Player exited an hoverbike."); + } + + internal void ConsumeEnergy(float energy) + { + hoverbike.energyMixin.ConsumeEnergy(energy); + } + + internal bool ConsumeEnergy(TechType techType) + { + if (!TechData.GetEnergyCost(techType, out float energyCost)) + return false; + ConsumeEnergy(energyCost); + return true; + } + + internal bool QuickSlotHasCooldown(int slotID) + { + return quickSlotTimeUsed[slotID] + quickSlotCooldown[slotID] > Time.time; + } + + protected void NotifySelectSlot(int slotID) + { + if (onSelect != null) + { + onSelect(slotID); + } + } + + internal bool CanUseUpgrade(TechType techType) + { + bool canUseModule = TechData.GetSlotType(techType) == QuickSlotType.Passive || TechData.GetSlotType(techType) == QuickSlotType.None || !hoverbike.kinematicOverride; + return canUseModule; + } + + internal void OnUpgradeModuleUse(TechType techType, int slotId) + { + quickSlotTimeUsed[slotId] = Time.time; + if (!CustomModules.TryGetValue(techType, out ICustomPrefab modulePrefab)) + return; + + if (!modulePrefab.TryGetGadget(out UpgradeModuleGadget moduleGadget)) + return; + + var charge = quickSlotCharge[slotId]; + var chargeScalar = GetSlotCharge(slotId); + moduleGadget.hoverbikeOnUsed?.Invoke(hoverbike, slotId, charge, chargeScalar); + if(moduleGadget.Cooldown > 0f) + quickSlotCooldown[slotId] = (float)moduleGadget.Cooldown; + } + + private void ToggleSlot(int slotID, bool state) + { + if (slotID < 0 || slotID >= hoverbike.slotIDs.Length) + { + return; + } + if (quickSlotToggled[slotID] == state) + { + return; + } + quickSlotToggled[slotID] = state; + NotifyToggleSlot(slotID); + } + + protected void NotifyToggleSlot(int slotID) + { + bool flag = quickSlotToggled[slotID]; + OnToggle(slotID, flag); + if (onToggle != null) + { + onToggle(slotID, flag); + } + } + + private void OnToggle(int slotID, bool state) + { + TechType techType = GetSlotBinding(slotID); + if (!CustomModules.TryGetValue(techType, out ICustomPrefab prefab)) + return; + + if (!prefab.TryGetGadget(out UpgradeModuleGadget moduleGadget)) + return; + float energyCost; + if (!TechData.GetEnergyCost(techType, out energyCost)) + energyCost = (float)moduleGadget.EnergyCost; + moduleGadget.hoverbikeOnToggled.Invoke(hoverbike, slotID, energyCost, state); + } + + private void OnEquip(string slot, InventoryItem item) + { + Pickupable item2 = item.item; + TechType techType = (item2 != null) ? item2.GetTechType() : TechType.None; + UpgradeModuleChanged(slot, techType, true); + } + + private void OnUnequip(string slot, InventoryItem item) + { + Pickupable item2 = item.item; + TechType techType = (item2 != null) ? item2.GetTechType() : TechType.None; + UpgradeModuleChanged(slot, techType, false); + } + + internal void UpgradeModuleChanged(string slot, TechType techType, bool added) + { + int slotID; + if (!slotIndices.TryGetValue(slot, out slotID)) + slotID = -1; + + OnUpgradeModuleChange(slotID, techType, added); + } + + internal void OnUpgradeModuleChange(int slotID, TechType techType, bool added) + { + if (!CustomModules.TryGetValue(techType, out ICustomPrefab prefab)) + return; + + if (!prefab.TryGetGadget(out UpgradeModuleGadget moduleGadget)) + return; + + if (moduleGadget.hoverbikeOnRemoved != null && !added) + moduleGadget.hoverbikeOnRemoved.Invoke(hoverbike, slotID); + + if (moduleGadget.hoverbikeOnAdded != null && added) + moduleGadget.hoverbikeOnAdded.Invoke(hoverbike, slotID); + } + + public void Bind(int slotID, InventoryItem item) + { + } + + public void Unbind(int slotID) + { + } + + public void DeselectSlots() + { + activeSlot = -1; + NotifySelectSlot(activeSlot); + } + + public int GetActiveSlotID() + { + return activeSlot; + } + + public TechType[] GetSlotBinding() + { + try + { + InternalLogger.Debug("GetSlotBinding now running."); + int slotsLength = hoverbike.slotIDs.Length; + InternalLogger.Debug($"GetSlotBinding slotlength: {slotsLength}"); + TechType[] array = new TechType[slotsLength]; + InternalLogger.Debug($"GetSlotBinding arraylength: {array.Length}"); + for (int i = 0; i < slotsLength; i++) + { + array[i] = hoverbike.modules.GetTechTypeInSlot(hoverbike.slotIDs[i]); + InternalLogger.Debug($"Slot {i}: {array[i]}"); + } + return array; + } + catch(Exception e) + { + InternalLogger.Error($"An error occured while getting slots.\n{e}"); + return new TechType[] { TechType.None }; + } + } + + public TechType GetSlotBinding(int slotID) + { + if (slotID < 0 || slotID >= hoverbike.slotIDs.Length) + { + return TechType.None; + } + string slot = hoverbike.slotIDs[slotID]; + return hoverbike.modules.GetTechTypeInSlot(slot); + } + + private void ResetSlotsState() + { + int i = 0; + int num = hoverbike.slotIDs.Length; + while (i < num) + { + TechType techType; + QuickSlotType quickSlotType = GetQuickSlotType(i, out techType); + if (quickSlotType == QuickSlotType.Toggleable || quickSlotType == QuickSlotType.Selectable || quickSlotType == QuickSlotType.SelectableChargeable) + { + onToggle(i, false); + } + quickSlotTimeUsed[i] = 0f; + quickSlotCooldown[i] = 0f; + quickSlotCharge[i] = 0f; + i++; + } + activeSlot = -1; + NotifySelectSlot(activeSlot); + } + + public int GetSlotByItem(InventoryItem item) + { + if (item != null) + { + int i = 0; + while (i < hoverbike.slotIDs.Length) + { + if (hoverbike.modules.GetItemInSlot(hoverbike.slotIDs[i]) == item) + { + return i; + } + i++; + } + } + return -1; + } + + public float GetSlotProgress(int slotID) + { + return GetQuickSlotCooldown(slotID); + } + + public float GetSlotCharge(int slotID) + { + try + { + if (slotID < 0 || slotID >= hoverbike.slotIDs.Length) + { + return 1f; + } + TechType techType; + QuickSlotType quickSlotType = GetQuickSlotType(slotID, out techType); + if (quickSlotType == QuickSlotType.Chargeable || quickSlotType == QuickSlotType.SelectableChargeable) + { + float quickSlotMaxCharge = TechData.GetMaxCharge(techType); + if (quickSlotMaxCharge > 0f) + { + return quickSlotCharge[slotID] / quickSlotMaxCharge; + } + } + return 1f; + } + catch(Exception e) + { + InternalLogger.Error($"An error has occured while getting the slot charge.\n{e}"); + } + return 1f; + } + + public int GetSlotCount() + { + return hoverbike.slotIDs.Length; + } + + public InventoryItem GetSlotItem(int slotID) + { + if (slotID < 0 || slotID >= hoverbike.slotIDs.Length) + { + return null; + } + string slot = hoverbike.slotIDs[slotID]; + return hoverbike.modules.GetItemInSlot(slot); + } + + protected float GetQuickSlotCooldown(int slotID) + { + float cooldown = quickSlotCooldown[slotID]; + if (cooldown <= 0f) + { + return 1f; + } + return Mathf.Clamp01((Time.time - quickSlotTimeUsed[slotID]) / cooldown); + } + + internal QuickSlotType GetQuickSlotType(int slotID, out TechType techType) + { + if (slotID >= 0 && slotID < hoverbike.slotIDs.Length) + { + techType = hoverbike.modules.GetTechTypeInSlot(hoverbike.slotIDs[slotID]); + if (techType != TechType.None) + { + return TechData.GetSlotType(techType); + } + } + techType = TechType.None; + return QuickSlotType.None; + } + + internal QuickSlotType GetQuickSlotType(int slotID) + { + if (slotID >= 0 && slotID < hoverbike.slotIDs.Length) + { + TechType techType = hoverbike.modules.GetTechTypeInSlot(hoverbike.slotIDs[slotID]); + if (techType != TechType.None) + { + return TechData.GetSlotType(techType); + } + } + return QuickSlotType.None; + } + + internal QuickSlotType GetQuickSlotType(TechType techType) + { + return TechData.GetSlotType(techType); + } + + internal float TotalCanProvide(out int sourceCount) + { + float totalenergy = 0f; + sourceCount = 0; + var energyMixin = hoverbike.energyMixin; + if(energyMixin != null && energyMixin.charge > 0f) + { + totalenergy = energyMixin.charge; + sourceCount++; + } + return totalenergy; + } + + internal void ChargeModule(TechType techType, int slotID) + { + InternalLogger.Debug($"Charging TechType {techType} in slot {slotID}"); + try + { + float slotCharge = quickSlotCharge[slotID]; + float maxCharge = TechData.GetMaxCharge(techType); + float energyCost; + TechData.GetEnergyCost(techType, out energyCost); + float energyCostOnDeltaTime = energyCost * Time.deltaTime; + float remainingCharge = maxCharge - slotCharge; + bool flag = energyCostOnDeltaTime >= remainingCharge; + float finalEnergyCost = flag ? Mathf.Max(0f, remainingCharge) : energyCostOnDeltaTime; + float addedCharge = Mathf.Min(TotalCanProvide(out int _), finalEnergyCost); + ConsumeEnergy(addedCharge); + quickSlotCharge[slotID] = quickSlotCharge[slotID] + addedCharge; + if (quickSlotCharge[slotID] > 0f && (flag || addedCharge == 0f)) + { + OnUpgradeModuleUse(techType, slotID); + quickSlotCharge[slotID] = 0f; + } + } + catch(Exception e) + { + InternalLogger.Error($"An error has occured while charging module {techType} in slot {slotID}.\n{e}"); + } + } + + public bool IsToggled(int slotID) + { + return slotID >= 0 && slotID < hoverbike.slotIDs.Length && (GetQuickSlotType(slotID) == QuickSlotType.Passive || quickSlotToggled[slotID]); + } + + public void SlotKeyDown(int slotID) + { + if (slotID < 0 || slotID >= hoverbike.slotIDs.Length) + { + return; + } + TechType techTypeInSlot = hoverbike.modules.GetTechTypeInSlot(hoverbike.slotIDs[slotID]); + QuickSlotType slotType = TechData.GetSlotType(techTypeInSlot); + if (slotType == QuickSlotType.Selectable || slotType == QuickSlotType.SelectableChargeable) + { + if (activeSlot >= 0 && activeSlot < hoverbike.slotIDs.Length) + { + quickSlotCharge[activeSlot] = 0f; + } + activeSlot = slotID; + } + if (!QuickSlotHasCooldown(slotID) && slotType == QuickSlotType.Instant && CanUseUpgrade(techTypeInSlot) && ConsumeEnergy(techTypeInSlot)) + { + OnUpgradeModuleUse(techTypeInSlot, slotID); + } + NotifySelectSlot(activeSlot); + } + + public void SlotKeyHeld(int slotID) + { + if (ignoreInput) + { + return; + } + if (slotID < 0 || slotID >= hoverbike.slotIDs.Length) + { + return; + } + if (GetQuickSlotCooldown(slotID) != 1f) + { + return; + } + TechType techType; + QuickSlotType quickSlotType = GetQuickSlotType(slotID, out techType); + if (quickSlotType == QuickSlotType.Chargeable) + { + ChargeModule(techType, slotID); + } + } + + public void SlotKeyUp(int slotID) + { + if (this.ignoreInput) + { + return; + } + if (slotID < 0 || slotID >= hoverbike.slotIDs.Length) + { + return; + } + if (GetQuickSlotCooldown(slotID) != 1f) + { + return; + } + if (GetQuickSlotType(slotID, out TechType techType) == QuickSlotType.Chargeable) + { + OnUpgradeModuleUse(techType, slotID); + quickSlotCharge[slotID] = 0f; + } + } + + public void SlotLeftDown() + { + if (ignoreInput) + { + return; + } + if (activeSlot < 0) + { + return; + } + if (GetQuickSlotCooldown(activeSlot) != 1f) + { + return; + } + if (GetQuickSlotType(activeSlot, out TechType techType) == QuickSlotType.Selectable && ConsumeEnergy(techType)) + { + OnUpgradeModuleUse(techType, activeSlot); + } + } + + public void SlotLeftHeld() + { + if (ignoreInput) + { + return; + } + if (activeSlot < 0) + { + return; + } + if (GetQuickSlotCooldown(activeSlot) != 1f) + { + return; + } + if (GetQuickSlotType(activeSlot, out TechType techType) == QuickSlotType.SelectableChargeable) + { + ChargeModule(techType, activeSlot); + } + } + + public void SlotLeftUp() + { + if (ignoreInput) + { + return; + } + if (activeSlot < 0) + { + return; + } + if (GetQuickSlotCooldown(activeSlot) != 1f) + { + return; + } + TechType techType; + if (GetQuickSlotType(activeSlot, out techType) == QuickSlotType.SelectableChargeable && quickSlotCharge[activeSlot] > 0f) + { + OnUpgradeModuleUse(techType, activeSlot); + quickSlotCharge[activeSlot] = 0f; + } + } + + public void SlotNext() + { + if (ignoreInput) + { + return; + } + int activeSlotID = GetActiveSlotID(); + int slotCount = GetSlotCount(); + int slotID = (activeSlotID < 0) ? -1 : activeSlotID; + for (int i = 0; i < slotCount; i++) + { + slotID++; + if (slotID >= slotCount) + { + slotID = 0; + } + TechType slotBinding = GetSlotBinding(slotID); + if (slotBinding != TechType.None) + { + QuickSlotType quickSlotType = GetQuickSlotType(slotBinding); + if (quickSlotType == QuickSlotType.Selectable || quickSlotType == QuickSlotType.SelectableChargeable) + { + SlotKeyDown(slotID); + return; + } + } + } + } + + public void SlotPrevious() + { + if (ignoreInput) + { + return; + } + int activeSlotID = GetActiveSlotID(); + int slotCount = GetSlotCount(); + int slotID = (activeSlotID < 0) ? slotCount : activeSlotID; + for (int i = 0; i < slotCount; i++) + { + slotID--; + if (slotID < 0) + { + slotID = slotCount - 1; + } + TechType slotBinding = GetSlotBinding(slotID); + if (slotBinding != TechType.None) + { + QuickSlotType quickSlotType = GetQuickSlotType(slotBinding); + if (quickSlotType == QuickSlotType.Selectable || quickSlotType == QuickSlotType.SelectableChargeable) + { + SlotKeyDown(slotID); + return; + } + } + } + } + + public void SlotRightDown() + { + } + + public void SlotRightHeld() + { + } + + public void SlotRightUp() + { + } +} +#endif \ No newline at end of file diff --git a/Nautilus/Patchers/VehicleUpgradesPatcher.cs b/Nautilus/Patchers/VehicleUpgradesPatcher.cs index ef796600e..13982d331 100644 --- a/Nautilus/Patchers/VehicleUpgradesPatcher.cs +++ b/Nautilus/Patchers/VehicleUpgradesPatcher.cs @@ -2,7 +2,9 @@ using HarmonyLib.Tools; using Nautilus.Assets; using Nautilus.Assets.Gadgets; +using Nautilus.MonoBehaviours; using Nautilus.Utility; +using System; using System.Collections.Generic; using UnityEngine; using OpCodes = System.Reflection.Emit.OpCodes; @@ -14,15 +16,23 @@ internal class VehicleUpgradesPatcher internal static IDictionary ExosuitUpgradeModules = new SelfCheckingDictionary("ExosuitUpgradeModules"); #if BELOWZERO internal static IDictionary SeatruckUpgradeModules = new SelfCheckingDictionary("SeatruckUpgradeModules"); - internal static IDictionary SnowbikeUpgradeModules = new SelfCheckingDictionary("SnowbikeUpgradeModules"); + // Hoverbikes upgrades are in the HoverbikeModulesSupport MonoBehaviour. #elif SUBNAUTICA internal static IDictionary SeamothUpgradeModules = new SelfCheckingDictionary("SeamothUpgradeModules"); #endif internal static void Patch(Harmony harmony) { + InternalLogger.Debug("VehicleUpgradePatcher: attempting patch..."); HarmonyFileLog.Enabled = true; - harmony.PatchAll(typeof(VehicleUpgradesPatcher)); + try + { + harmony.PatchAll(typeof(VehicleUpgradesPatcher)); + } + catch (Exception e) + { + InternalLogger.Error($"An error occured while running VehicleUpgradesPatcher.\n{e}"); + } InternalLogger.Debug("VehicleUpgradesPatcher is done."); } @@ -395,41 +405,22 @@ private static IEnumerable OnUpgradeModuleUse(IEnumerable DelegateModuleUseCallback(IEnumerable instructions) - { - var matcher = new CodeMatcher(); - - matcher - .MatchForward(true, new CodeMatch(OpCodes.Ret)) - .MatchForward(true, new CodeMatch(OpCodes.Ret)) - .Insert( - new CodeInstruction(OpCodes.Ldarg_0), - new CodeInstruction(OpCodes.Ldarg_2), - new CodeInstruction(OpCodes.Ldarg_1), - new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(VehicleUpgradesPatcher), nameof(VehicleUpgradesPatcher.DelegateUseCallback))) - ); - - return matcher.InstructionEnumeration(); - } - // // SEATRUCK // ON CHANGE + HULL UPGRADE FIX @@ -511,29 +502,45 @@ private static void LazyInitialize(SeaTruckUpgrades __instance) return; double energyCost = moduleGadget.EnergyCost; - moduleGadget.seatruckOnToggled?.Invoke(__instance, __instance.motor, slotID, (float)energyCost, state); + moduleGadget.seatruckOnToggled?.Invoke(__instance, __instance.motor, slotID, (float) energyCost, state); }; } // // HOVERBIKE - // ON CHANGE + // AWAKE / ENTER / EXIT // - [HarmonyPostfix] - [HarmonyPatch(typeof(Hoverbike), nameof(Hoverbike.OnUpgradeModuleChange))] - private static void OnUpgradeModuleDelegate(Hoverbike __instance, int slotID, TechType techType, bool added) + [HarmonyPrefix] + [HarmonyPatch(typeof(Hoverbike), nameof(Hoverbike.Awake))] + private static void HoverbikeAwake(Hoverbike __instance) { - if (!SnowbikeUpgradeModules.TryGetValue(techType, out var prefab)) - return; - - if (!prefab.TryGetGadget(out UpgradeModuleGadget moduleGadget)) - return; + InternalLogger.Info("Adding hoverbike modules support component to Hoverbike."); + __instance.gameObject.EnsureComponent(); + InternalLogger.Info("Added hoverbike modules support component to Hoverbike."); + } - if (moduleGadget.hoverbikeOnRemoved != null && !added) - moduleGadget.hoverbikeOnRemoved.Invoke(__instance, slotID); + [HarmonyPostfix] + [HarmonyPatch(typeof(Hoverbike), nameof(Hoverbike.EnterVehicle))] + private static void HoverbikeEnterVehicle(Hoverbike __instance) + { + if (__instance.gameObject.TryGetComponent(out var hoverbikeModules)) + { + InternalLogger.Debug("Running EnterVehicle of the hoverbike modules support mono."); + hoverbikeModules.EnterVehicle(); + } + else InternalLogger.Error($"Missing {nameof(HoverbikeModulesSupport)} component to hoverbike."); + } - if (moduleGadget.hoverbikeOnAdded != null && added) - moduleGadget.hoverbikeOnAdded.Invoke(__instance, slotID); + [HarmonyPostfix] + [HarmonyPatch(typeof(Hoverbike), nameof(Hoverbike.ExitVehicle))] + private static void HoverbikeExitVehicle(Hoverbike __instance) + { + if (__instance.gameObject.TryGetComponent(out var hoverbikeModules)) + { + InternalLogger.Debug("Running ExitVehicle of the hoverbike modules support mono."); + hoverbikeModules.ExitVehicle(); + } + else InternalLogger.Error($"Missing {nameof(HoverbikeModulesSupport)} component to hoverbike."); } #endif } \ No newline at end of file