Structure Skins & Custom Cosmetics
How to Set Up Structure Skins & Custom Cosmetics
Structure Skins & Custom Cosmetics are not instantiated actors (unlike Buffs which are their own actors in the world), instead all of the following functions are called on the default object of the structure skin. This means you always need to provide a valid world context for functions you call (like PrintString or CanRunCosmeticEvents, etc) which is why all of these functions provide you with a reference to the structure that the function is being called on.
The Basics
You will require a PrimalItem & PrimalStructure file. Your item should inherit from PrimalItemStructureSkinGeneric but your structure can inherit from any structure. Since the structure is not instantiated it doesn’t matter but it can be helpful to copy the structure you want to skin.
Like a normal structure you should set StructureToBuild on your item to your structure and ConsumesPrimalItem on your structure to your item.
Additionally, you will need to set these fields on your structure:
IsStructureSkin denotes that this is a skin and StructureSkinSupportedStructures denotes which structures your skin will apply to (note: this checks inheritance so you can specify a parent class to encompass all the children).
If you need more granularity, then you can implement IsValidStructureSkinTarget:
For example, if you want your skin to apply to walls but not doorways then you would implement custom logic here since doorways are children of walls (note: you still want to call the parent here because that is what checks the StructureSkinSupportedStructures).
There are also 2 required functions that need to be implemented in your structure: StructureSkinHandleCommand & BPOverrideCantBuildReasonString
ApplySkin - This is called when a skin is first placed on a structure & also when a structure becomes relevant for a client (ie coming out of stasis). The IntParam1 will be 0 for the initial placement and 1 for a skin that is being applied for a newly relevant client. This is important because you may want certain things to happen when it initially places or vice versa (for example, its useful to play some placement effects for the initial placement but you don’t want those to play whenever somebody comes back to their base).
RemoveSkin - This is called when a skin is removed & also uses the IntParam1 to indicate whether the skin is being removed (0) or replaced (1). If a skin is being removed, then you may want to play some effects but if you play those effects when its being replaced then you may run into overlap with the new skin’s application effects.
Generally it’s best to only play additional effects for ApplySkin & RemoveSkin when the IntParam1 value is 0.
BeginPlay, DestroyedStructureWithSkin, Activated & Deactivated all fire when their respective events would have been called on a normal structure (note: BeginPlay fires before ApplySkin) but these are optional and do not need to be implemented for every skin.
This allows you to create a specific error message when your skin isn’t being applied to the correct structure (error code -555 is specific to a skin not applying to the correct target).
One important thing to note here is that components added to the blueprint will NOT be accessible from the default object, so you can add mesh components to the structure BP to help you visualize how things should look but referencing those components in your graphs will not work (for example, if your skin adds 3 meshes, you can add those as components so you can use the viewport to tweak them but you will have to have a variable that stores the actual transforms as the default objects do not contain any blueprint components).
This will not work as NewMeshComp doesn’t exist in the default object:
The final step for Custom Cosmetics is to add it to your ModDataAsset:
CosmeticID is the mod specific ID for this cosmetic (unique within your mod).
ApplyToClasses should be as shown.
ModSkinItem is your custom cosmetic item class.
ModSkinStructure is your custom cosmetic structure class.
For Structure Skins, you simply need to add your skin item to the ExtraResources container of your ModDataAsset:
Optional Functionality
If your skin only applies to a single structure type, then you can use the native MultiSoftDestructionGeoCollectionAssets variable on your structure to store your custom destroyed meshes (as shown) but if your skin applies to many structure types then you will want to expand this (e.g. if you create an entirely new tileset skin that applies to many types of structures then you will want destroyed meshes specific to the structure that is being destroyed).
Some activatable structures use different materials for their active & inactive states which likely won’t be compatible with your skin so you can use this function to override those (in this case, by providing an empty array we are forcing the structure to not use a different material when active).
Skin Multi Use
Your skin can also add or alter the structure’s multiuse entries using BPStructureSkinGetMultiUseEntries:
You will need to enable BlueprintMultiUseEntries on your structure skin:
For custom cosmetics, you need to enable ClientSideOnly on your entries:
There are 2 functions to handle responding to custom multiuse actions:
Keep in mind that BPStructureSkinTryMultiUse will only fire on Structure Skins, not Custom Cosmetics.
Generally when you implement TryMultiUse on a normal structure, you will want to multicast some data (ie changing a variable on the server or the client that initiated the multiuse isn’t useful unless we can replicate that out to other clients) and for that we have ExecSkinNetMessage:
This is similar to StructureSkinHandleCommand but is used for your custom code.
To send a net message, you call ServerStructureSkinNetMessage which then multicasts ExecSkinNetMessage to all relevant clients:
Additionally, you generally want to save some data related to your skin (e.g. whether something is visible or not) and for that, you can use the SkinPersistentData variable. To set this variable, you need to call ServerStructureSetSkinPersistentData (like ServerStructureSkinNetMessage, it will multicast the new data to all relevant clients):
Here is a simple example that shows the entire chain of multiuse function calls:
Structure Skins & Custom Cosmetics also have the ability to store local data using LocalOnlySkinCustomPersistentData:
This isn’t replicated so is useful for keeping track of things on a per client basis.
Timers
Structure skins & custom cosmetics can also set timers using SetStructureSkinTimer:
Which will call BPStructureSkinTimerTriggered:
You can have two timers per structure, one looping and 1 non-looping. Since there is no client input pin here, you can use GetActuallyTrueLocalPlayerControllers to get a reference to the client to use to send a server net message:
Custom HUD
Lastly, you can add to the structure hud by implementing BPStructureSkinBlueprintDrawHUD:
Just like a normal structure, you need to enable BlueprintDrawHUD on your skin: