-
Notifications
You must be signed in to change notification settings - Fork 79
Finding Registered UpgradeHandlers
Originally introduced in the 3.0 update and later refined in the 4.0 overhaul, MoreCyclopsUpgrades can now be used as a public API, allowing other mods to integrate their own cyclops upgrade modules and have them be fully compatible with the Auxiliary Upgrade Console.
While ideally you'd want your UpgradeHandler to be simple and self-contained, it's more likely that you have some other, more interesting component, that either gets activated or otherwise affected by that current status of your UpgradeHandler.
For example, for the Cyclops to know if it is allowed to start recharging from thermal energy, it needs to know if a Thermal Reactor Upgrade Module is currently installed.
To do this, MCUServices
exposes the Find
interface which contains methods you can use to locate anything previously registered, like your UpgradeHandlers, once they've been created.
// Remember to include these namespaces
using MoreCyclopsUpgrades.API;
using MoreCyclopsUpgrades.API.Upgrades;
/// <summary>
/// Gets the upgrade handler at the specified Cyclops sub for the specified upgrade module <see cref="TechType"/>.<para/>
/// Use this if you need to obtain a reference to your <seealso cref="UpgradeHandler"/> for something else in your mod.
/// </summary>
/// <param name="cyclops">The cyclops to search in.</param>
/// <param name="upgradeId">The upgrade module techtype ID.</param>
/// <returns>An <see cref="UpgradeHandler"/> if found by techtype; Otherwise returns null.</returns>
UpgradeHandler CyclopsUpgradeHandler(SubRoot cyclops, TechType upgradeId);
Note: To avoid possible race conditions involved in the initialization of the various Cyclops components and managers, it is strongly recommended to use Lazy Loading pattern whenever you want to store a reference to your UpgradeHandler.
Since Subnautica is limited to .NET 3.5, we do not have the Lazy wrapper.
However, you can still implement a simple Lazy Loading Pattern yourself like this:
internal class MyComponent
{
// Let's assume you already have a reference to the Cyclops sub and the upgrade TechType
private SubRoot cyclops;
private TechType myUpgradeTechType;
// This private field will hold a reference to your UpgradeHandler,
// but it starts out null when the component is first instantiated
private UpgradeHandler myUpgradeHandler = null;
// This get-only property is what you would actually use to check on your UpgradeHandler
internal UpgradeHandler MyUpgradeHandler
{
get
{
if (myUpgradeHandler == null)
{
myUpgradeHandler = MCUServices.Find.CyclopsUpgradeHandler(cyclops, myUpgradeTechType)
}
return myUpgradeHandler;
}
}
}
This example can actually be written in even less code, making use of the syntactic sugar provided by Expression Bodied Members and Null Coalescing.
internal class MyComponent
{
private SubRoot cyclops;
private TechType myCoolUpgradeTechType;
private UpgradeHandler myUpgradeHandler = null;
internal UpgradeHandler MyUpgradeHandler => myUpgradeHandler ?? (myUpgradeHandler = MCUServices.Find.CyclopsUpgradeHandler(cyclops, myUpgradeTechType));
}
By now you should be well aware that the UpgradeHandler
class, while robust in the features it offers, might not be enough to fully encapsulate what you want to do.
With this in mind, the API encourages you to create your own classes that extend UpgradeHandler
and add whatever you need.
For these cases, when you want a reference to your custom class, not the basic UpgradeHandler
.
For these cases, the API offers a generic typed method you can use to fetch your custom upgrade handler class that gives you compile the time safety to ensure you didn't miss anything.
// Remember to include these namespaces
using MoreCyclopsUpgrades.API;
using MoreCyclopsUpgrades.API.Upgrades;
/// <summary>
/// Gets the upgrade handler at the specified Cyclops sub for the specified upgrade module <see cref="TechType"/>.<para/>
/// Use this if you need to obtain a reference to your <seealso cref="UpgradeHandler"/> for something else in your mod.
/// </summary>
/// <typeparam name="T">The class created by the <seealso cref="CreateUpgradeHandler"/> you passed into <seealso cref="IMCURegistration.CyclopsUpgradeHandler(CreateUpgradeHandler)"/>.</typeparam>
/// <param name="cyclops">The cyclops to search in.</param>
/// <param name="upgradeId">The upgrade module techtype ID.</param>
/// <returns>A type casted <see cref="UpgradeHandler"/> if found by techtype; Otherwise returns null.</returns>
T CyclopsUpgradeHandler<T>(SubRoot cyclops, TechType upgradeId) where T : UpgradeHandler;
Let's update the previous example to use a custom upgrade handler class instead of the basic UpgradeHandler
.
It's really that simple.
internal class MyUpgradeHandler : UpgradeHandler
{
...
}
internal class MyComponent
{
private SubRoot cyclops;
private TechType myCoolUpgradeTechType;
// The type is now your custom upgrade handler class
private MyUpgradeHandler myUpgradeHandler = null;
internal MyUpgradeHandler MyUpgradeHandler =>
myUpgradeHandler ??
(myUpgradeHandler = MCUServices.Find.CyclopsUpgradeHandler<MyUpgradeHandler>(cyclops, myUpgradeTechType));
// Notice that all we did was add <MyUpgradeHandler> to the CyclopsUpgradeHandler method
}
Remember to check for null!
If you are callingMCUServices.Find
from a Monobehavior or some other component capable of being active on its own,
then you absolutely must check for nulls.
If the Upgrade Handlers aren't ready by the time you are calling intoMCUServices.Find
, then you will get anull
return value.
Remember, the Cyclops is loaded in parts, and it goes through a full upgrade cycle when loading in.
So treat a null UpgradeHandler as simply "not ready yet".
CbItem Patching.dll
Text Pack Patching.txt
The Cyclops Upgrade Cycle
Single UpgradeHandlers
Grouped UpgradeHandlers
Registering UpgradeHandlers
Finding UpgradeHandlers
Creating Icon Overlays
Registering Icon Overlays
The Cyclops Charge Cycle
Creating Cyclops Chargers
Registering Cyclops Chargers
Finding Cyclops Chargers
Origin Story
Cyclops Manager Promises
Creating Your Manager
Registering Your Managers
Finding Auxiliary Managers