Jump to content

[Modded Skins API Tutorial] Creating Custom Skins for Mods


Recommended Posts

Posted (edited)

Hi everyone, a few years back I had written a tutorial on creating custom skins for modded characters. Today that tutorial becomes deprecated as the Revamped Modded Skins API created by me and some others makes its release. This API will make it easy to add both skins for characters and items/structures. I’m very happy to present this new API to you all and I hope you enjoy it too, as it comes with custom art for new rarities, frames, and even a custom skin unlockable system for modded skins. This will be a multi-tutorial post, so be sure to check out the table of contents and find what you’re looking for!

Thank you @Fidooop, @Cunning fox, and @Dudedude for helping to develop the API and having me hop on board.

Here is the API related to the topic!
Modded Skins [API]

preview.thumb.jpg.25324dfbe27bffb52a23ea671a70803e.jpg

Table of Contents:

  • Creating Custom Skins for Modded Characters
  • Creating Custom Skins for Modded Items
  • Miscellaneous
    • Adding an event icon to your custom skin
    • Making a Modded Skin Lockable
    • Setting Custom Offset/Scale for Clean Sweeper effects
    • Compatibility with Mods in Menu
  • Resources

Some quick important notes:

All of the skin id’s(aka the names of the skins in the code) you choose for your skin should start with “ms_”. For example, an appropriately named skin would be “ms_whimsy_victorian”. (The default ‘_none’ skin is excluded from this rule, however)

This tutorial will assume you have your skin assets ready to go if you’ve created a modded item/character before. If not and you are completely unaware on how to create items and character builds in the first place, it is likely you’re not ready for this tutorial yet.

I’ll be using my friends character mod as an example throughout the tutorial :), @-Variant, love u bb.

 

Creating Custom Skins For Modded Characters

Spoiler

If you’ve created a character mod(which you likely have if you’re reading through this tutorial), you’ve most likely already created a skin! The “_none” prefab file you usually have in character mods is actually a skin, the default skin! I mentioned this because we’re going to do something very similar so this should hopefully give you a sense of familiarity.

Let’s take a quick look at all the parameters used for the ‘CreatePrefabSkin’ function, which is the function necessary for creating our custom skins. I’ll be using the name ‘esctemplate’ in place of the prefab name as that should help to correlate it with the Extended Character Template and make things feel familiar.


CreatePrefabSkin("esctemplate_none", {--The ID of your skin, e.g. “whimsy_none”, “ms_whimsy_victorian”
	assets = { --All the assets correlated to your skin
		Asset("ANIM", "anim/esctemplate.zip"),
	},
	skins = { --The skin modes, generally there are usually two modes your character is in. Either the normal mode or ghost form(And most character skins don’t have unique ghost sprites!) However if you’re developing a character similar to Woodie or Wanda with different forms you can create skins for those custom forms! I may expand this tutorial to include how to do that.
		normal_skin = "esctemplate",
		ghost_skin = "ghost_esctemplate",
	},

	base_prefab = "esctemplate", --The prefab you are going to skin
	build_name_override = "esctemplate", --The build name we’re using to skin

	type = "base", --The type of skin this is, for characters only “base” really matters, but there are of course other types of skins such as item/structure skins that we will cover later in this tutorial.
	rarity = "Character", --The rarity of your skin. For the ‘_none’ skins it is the ‘Character’ rarity. Actual skins however will have you use either the ‘ModMade’ or ‘ModLocked’ rarities.

	--Misc parameters, these are not at all necessary 
	torso_untuck_builds = { "esctemplate", }, --All builds in this table will have their ‘torso’ symbols ‘tucked’ into their skirt!

	skin_tags = { "BASE", "ESCTEMPLATE"},
})

Now we’ll go through an example of how it was used. What my friend has done is renaming the ‘_none’ file to ‘_skins’ and then initializing all their custom character’s skins in there! So let’s go through how they did it.


--A prefabs table is created in which all of our custom skins will be poured into and then this file returns the unpacked contents.
--”whimsy” in this case is the code name of my friends custom character
local prefabs = {}

table.insert(prefabs, CreatePrefabSkin("whimsy_none", {
	assets = {
		Asset("ANIM", "anim/whimsy.zip"),
	},
	skins = {
		normal_skin = "whimsy",
		ghost_skin = "ghost_whimsy",
	},

	base_prefab = "whimsy",
	build_name_override = "whimsy",

	type = "base",
	rarity = "Character",

	skin_tags = { "BASE", "WHIMSY"},
}))

table.insert(prefabs, CreatePrefabSkin("ms_whimsy_victorian", {
	assets = {
        Asset("ANIM", "anim/ms_whimsy_victorian.zip"),
        Asset("ANIM", "anim/ghost_whimsy.zip"),
    },
	skins = {
		normal_skin = "ms_whimsy_victorian",
		ghost_skin = "ghost_whimsy",
	},

	base_prefab = "whimsy",
	build_name_override = "ms_whimsy_victorian",

	torso_untuck_builds = { "ms_whimsy_victorian", },
	type = "base",
	rarity = "ModMade",

	skin_tags = { "BASE", "WHIMSY", "VICTORIAN"},
}))


return unpack(prefabs)

If everything went right, you should now be able to see your custom character! If not, be sure to let me know in the comments!

ThBCfhbk1qrkVkWlOJ9YEiCRfhhYkVnDM2jYxgRSW6Way7PmGs090LTUtkLrZ5RanXlWfwL-It5qnolU-8Fgjpw_WOk48_N84KVzfqVONrDe9mnrpHeUtrzol1YaHsmAdSN_FcDKYq5eIpRo3A

There’s just two more things to take care of.

Setting Custom Strings
Your custom character skin should also come with a custom name, custom description, and a custom quote. Here’s a template for that! Place this in your modmain.lua(or wherever you desire, really) and write your strings.
 


--Replace skinid with the id of your custom skin
STRINGS.SKIN_NAMES.skinid = "The Template"
STRINGS.SKIN_DESCRIPTIONS.skinid = "It is but a humble template! Does it’s existence even matter? It’s just a nobody looking to be replaced by a somebody!"
STRINGS.SKIN_QUOTES.skinid = "I am a template."

 

u0Ny8c7o99vVtLYH7pA2mG9KxjLMA8dBYxrIhy9UGFsxL6XjGQ8ZP2iFjKrRZSs8G4-RuDCPeFUJftMFmIL8yAWdsbnK-5C3P7cck-iKF9m-MdIWYoPRp-JhrhXLAEtL7IgFep3qk0ze9shHMw

And there are your wonderful custom strings!

Creating a portrait for your custom skin
Creating your portrait for your custom skin will be super simple! It’s nearly exactly the same as creating a portrait for a regular character. Instead of using the character name for the portrait, you’ll use the ID of the skin, like so!

yOvNOcPEYE9XaJxFr1OkdwelgBL7WRdHON7X1_EuKj3fDhL_-qU15mfI8xhvMSGxJ5p7Ka3TwfdzuI5cSUC82JrVEs03Kx5-kSTR5tC1-8_bA3G_TCjEfCVzU3RQawah3as1LEn3DrQyCOrG-Q

The ID of the skin in question is “ms_whimsy_victorian” so we’ll be using that for the portrait name. Simple enough!

There is one last thing we need to do however to make sure it works. Open up the xml file associated with your skins portrait, and make sure the string tied to the name variable in the xml file is the skin id with an ‘_oval’ at the end of it like-so.drPqCl2ijnIuqGbcy5sY33adTSw2ottiDbYVSwKZ2S8A1FjfzCLtS6z_9LmjRv4p8UBLkX8VlLqr5e-lJjst2tzttqp9IFLyD7Um6wU68l5o5kNXHcRFgJ_w2z27H3nc9YfeFap92eJcpNtegQ

The skin ID here is “ms_whimsy_victorian”, so it should be “ms_whimsy_victorian_oval”. The filename should stay the same, it is only the name you should be changing.

yuXO8RBanyhTgl96LEfWJvJymY2zTG9f7E5HfnDOIj8_6CvRot5JLVGt6nSu5KHsZa9AeHQIU_jX1kCE6_6Xjo3O1gpBQ8IGLC2EdN7xIJpKaee-yUX_Q9luv4TNlJFlt2-EosTEfxFTSsAqDQ

And now, your custom portrait should be working!

 

That concludes this section of how to create a custom skin for your modded character.

 

Creating Custom Skins For Modded Items/Structures

Spoiler

If you've read the part on how to create a custom skin for a modded character, we're going to be doing something very similar again for modded items/structures, returning to the CreatePrefabSkin function.


table.insert(prefabs, CreatePrefabSkin("ms_nightmarecollar_victorian", { --The ID of our skin
    assets = { --Our assets
        Asset( "ANIM", "anim/ms_nightmarecollar_victorian.zip"),
    },
    base_prefab = "nightmarecollar", --The prefab of the item/structure we're adding a skin for
    build_name_override = "ms_nightmarecollar_victorian",

    type = "item", --We are now creating a modded item/structure! Thus our skin's type is "item" (Note: there aren't different types for modded "structures", to the game there is no difference between skinning an item, a structure, or even a mob! (Yes you could create mob skins if you wanted!)
    rarity = "ModMade",

    skin_tags = {"NIGHTMARECOLLAR"}, --Skin tags, you should add a tag matching the original prefab of the item/structure we're adding a skin for in full capitalization
}))

In my friend's mod, they ended up making their "whimsy_skins.lua" file also include item skins which is where they put their custom skin for one of their character's modded items. You can do the same, or make a seperate prefab file to make your skin. The organization is up to you!

Now this is where things get different from making a skin for a modded character. You'll need to initalize two functions in the global environment, named "prefabname_init_fn", and "prefabname_clear_fn". These are the functions that will be responsible for actually skinning your prefab when crafting it from the UI or using a clean sweeper. Here is an example of how these two functions are defined, these are best placed in modmain.lua.


--We want these to be in the GLOBAL environment
--prefabname should be switched with the prefab of the item you are skinning
--"defaultbuild" should be switched with the usual build your item takes on while unskinned

GLOBAL.prefabname_init_fn = function(inst, build_name)
    GLOBAL.basic_init_fn(inst, build_name, "defaultbuild" )
end

GLOBAL.prefabname_clear_fn = function(inst)
    GLOBAL.basic_clear_fn(inst, "defaultbuild" )
end

 

When you craft a skinned item, or use the sweeper on an item. The game will call the _init_fn and pass the build name you set(or skin id by default if you did not set a build name for the skin) and those passed parameters can then be used to skin the item.

You most likely noticed the calls to "basic_init_fn" and "basic_clear_fn". Since many items are same-y in how they functionally work when skinned, Klei made basic/common functions to use instead of just copying and pasting code for all of their items. To see what the basic_init_fn and basic_clear_fn functions do, look in the spoiler below.

Spoiler


function basic_init_fn( inst, build_name, def_build )
    if inst.components.placer == nil and not TheWorld.ismastersim then
        return
    end

    inst.AnimState:SetSkin(build_name, def_build)
    if inst.components.inventoryitem ~= nil then
        inst.components.inventoryitem:ChangeImageName(inst:GetSkinName())
    end

    if inst.components.floater ~= nil then
        if inst.components.floater:IsFloating() then
            inst.components.floater:SwitchToDefaultAnim(true)
            inst.components.floater:SwitchToFloatAnim()
        end
    end
end
function basic_clear_fn(inst, def_build)
    inst.AnimState:SetBuild(def_build)
    if inst.components.inventoryitem ~= nil then
        inst.components.inventoryitem:ChangeImageName()
    end

    if inst.components.floater ~= nil then
        if inst.components.floater:IsFloating() then
            inst.components.floater:SwitchToDefaultAnim(true)
            inst.components.floater:SwitchToFloatAnim()
        end
    end
end

The whole gist is the AnimState get's its skin set, and the inventoryitem gets its skin icon set.

You can get very creative with what you do in the init_fn and clear_fn functions. For example one of the vanilla Rabbit Hutch skins has a very special effect where they spawn neat glowing lights! The init function for the rabbit hutch spawns the glowing lights if its the evergreen skin, and then clears them in the clear function!

Spoiler

Hutch.gif



rabbithouse_init_fn = function(inst, build_name)
    basic_init_fn( inst, build_name, "rabbit_house" )

    if inst.components.placer == nil and not inst:HasTag("burnt") then
        local skin_fx = SKIN_FX_PREFAB[build_name]
        if skin_fx ~= nil then
            if skin_fx[1] ~= nil then
                inst.glow_fx = SpawnPrefab(skin_fx[1])
                inst.glow_fx.entity:SetParent(inst.entity)
                inst.glow_fx.AnimState:OverrideItemSkinSymbol("glow", build_name, "glow", inst.GUID, "rabbit_house")
            end
        end
    end
end
rabbithouse_clear_fn = function(inst)
    basic_clear_fn(inst, "rabbit_house" )

    if inst.glow_fx ~= nil then
        inst.glow_fx:Remove()
        inst.glow_fx = nil
    end
end

 

So let's see how my friend defined their init and clear functions, and it's quite simple really! Their item isn't anything complex, just your average equippable item so the basic_init and basic_clear functions provide all they need to skin their item.


GLOBAL.nightmarecollar_init_fn = function(inst, build_name)
    GLOBAL.basic_init_fn( inst, build_name, "nightmarecollar" )
end

GLOBAL.nightmarecollar_clear_fn = function(inst)
    GLOBAL.basic_clear_fn(inst, "nightmarecollar" )
end

If you did everything right, your item should now have sprites while on the ground! We'll look at inventory icons soon enough.

Now, if you're making an equippable, there's a little more work to do, which'll be shown what to do to make your item actually skinned while it's equipped. otherwise you can move on.
Making the skinned item equippable

Spoiler

If you've looked around in the vanilla game scripts, you may have seen something like this before in the onequip callback of an equippable skinnable item!:



local skin_build = inst:GetSkinBuild()
if skin_build ~= nil then
	owner.AnimState:OverrideItemSkinSymbol("swap_object", skin_build, "swap_spear", inst.GUID, "swap_spear")
else
	owner.AnimState:OverrideSymbol("swap_object", "swap_spear", "swap_spear")
end

Essentially what's happening is, if the item has a skin when being equipped, instead of using the usual AnimState:OverrideSymbol for a skinned item, you have to use a special AnimState:OverrideItemSkinSymbol for skinned swap symbols.



--The first three parameters aren't anything new and should look similar, but i'll talk about them anyways just to remind you
--First paramter: "swap_object" is the symbol we're going to be replacing in the entity's animations
--Second parameter: skin_build is the build of the skin from which we're going to pull the new symbol we're going to use to replace the first parameter
--Third parameter: "swap_spear" is now the symbol that resides in the skin_build and is going to replace "swap_object" in the entity's animations

--These are the new parameters different from OverrideSymbol!
--Fourth paramter: inst.GUID. The GUID of an entity is it's unique identifier. This is important, and should generally always be the same variable
--Fifth parameter: Is the default/unskinned build of the entity
AnimState:OverrideItemSkinSymbol("swap_object", skin_build, "swap_spear", inst.GUID, "swap_spear")

Here's an example of how my friend was able to make it work for their equippable skinned item.



local function OnEquip(inst, owner)
	local skin_build = inst:GetSkinBuild()
	if skin_build ~= nil then
		owner.AnimState:OverrideItemSkinSymbol("swap_body", skin_build, "swap_body", inst.GUID, "nightmarecollar")
	else
		owner.AnimState:OverrideSymbol("swap_body", "nightmarecollar", "swap_body")
	end
end

And just like that, their swap symbol for their skin works fantastically!

image.png.14ebd82bf0d8ed86b8ebd721264a73fd.png


Skin Strings
Place these lines in your modmain.lua and replace "ms_customitem" with the ID of your skin and then you can create your custom strings!

Something to note is the description will almost never be seen as you can't view item belongings in-game. However, the Mods In Menu mod which allows you to load in server mods in the main menu can be compatible with your mod and allow you to view your skins in the item collection screen! There'll be a part of this tutorial to cover on how to do that.


STRINGS.SKIN_NAMES.ms_customitem = "The Modded"
STRINGS.SKIN_DESCRIPTIONS.ms_customitem = "A descriptor"

Inventory Icon
To create your inventory icon, create your xml and tex files and make sure both have the name of your skin id, and then place this line in modmain and change accordingly.


RegisterInventoryItemAtlas(GLOBAL.resolvefilepath("images/inventoryimages/ms_exampleskin.xml"), "ms_exampleskin.tex")

The first parameter should be the path to the atlas(xml), and the second parameter should be the name of your skin + .tex attached at the end.

And that's it! That easy to implement your custom skin icon :)

That concludes this part of the tutorial on how to make a custom skin for a Modded Item.

 

Miscellaneous

Spoiler

Adding Event Icons to your Modded Skins

Spoiler

You’ve likely noticed now how throughout the tutorial the custom skin I’ve been using as a template has the Gorge icon show up. How did I do that? It’s quite simple really!

Each event icon has a skin tag associated with it. If you add the skin tag to the list of skin tags for your skin prefab, the correlated icon will appear.

Here is a list of all the event icons and their associated tags below.



"COSTUME" --Hallowed nights
"HALLOWED"--Also hallowed nights
"VICTORIAN" -- Gorge
"LAVA" --Gladiator and Magmatic
"ICE" --Snowfallen
"ROSE" --Roseate
"SHADOW" --Triumphant
"SURVIVOR" -- Survivor
"YULE" --Merry maker

 

Making your Modded Skin Lockable

Spoiler

Before we talk about how to make your modded skin lockable, let’s talk about how our unlockable system actually works!

iNIhQIHiT-7lBhN3uTyZh21a9Pn7TN_qRCpLC-JLVkKgpaQ5wdZO4IBRH4ETU-mQFausbt_SdwBXzv01N7Xw6buOCm_VKpxe9n77owEcTLTFpUoVuxsGbqZ4TrlArAFKFhFVHsO_V1C1YloiZw

This unlockable system is of course not tied to anything monetary like spools. Instead, YOU will be able to unlock a skin to any client at any point of the game. Essentially you can do achievements and the character will unlock modded gifts! For example, you could make an “achievement” where the character needs to kill 5 spiders to unlock one of your custom skins, and if you’re able to program that mechanic yourself and then push the necessary RPC to the client, a neat little gift will pop up for them that can be opened at a magic station!

va_6exO8y_mu1mU4RoXAGm-TqK86CEouURyBZtXFVMOTu2olEfD8G11BlTztZabtpspF0t7cnVOT9M7qPbrk2zaoShCC3v4LY3AtPl2aubxCXZRTWJvCKUE_-loDXx3YvHMBQ-NhKXMG5RJAFg

So let’s get right into it!

To make your skin lockable, let’s return to the prefab skin for it, you’ll be changing the rarity of your skin to ‘ModLocked’, and adding a new ‘condition’ table.

For right now, there is only one configuration you can set for your skin, whether or not you’d like it to require a gift to be opened when unlocking it. For instances where you might want a skin to not require a gift, the Forge and Gorge events have no way of opening gifts normally and thus it’d be practically impossible to open any gifts there! Simply uncomment the ‘no_gift’ line if you’d like your skin to not require a gift.



rarity = "ModLocked", --Changing rarity to ‘ModLocked’
	
condition = { --Adding conditions for our locked skins(Only one condition exists so far!)
	--no_gift = true
},

Now as for actually unlocking the skin, we’ll be using a RPC that the API adds for you that’ll allow you to pass the id of your skin to be unlocked.



if CLIENT_MOD_RPC[“ModdedSkins”] then --This is necessary to make sure the RPC actually exists as depending on your mod a user may not always have the API enabled.
	SendModRPCToClient(GetClientModRPC("ModdedSkins", "UnlockModdedSkin"), sender_list, "skinid") --sender_list can either be nil to send the rpc to all clients, or be the string for a single clients user id, OR an array that includes multiple user id’s to send the rpc to all the clients bearing those user id’s. Skinid should be filled out with the id of the skin you’d like to be unlocked.
end

If you’re not too familiar with RPC’s, I would absolutely recommend checking out Zarklord’s documentation on the subject:

He does super awesome stuff and explains things really well!

Moving on, with that wonderful snippet of code you can now let your custom skins be unlocked! One last thing to do now..

auA3TYNTHHxa9wVqCngX5bOP4twylfOkFm86YV7FknQBdk1w7-emYOigEVOp-nsDAXKSbGjC7SLwR3TUPMRSnv7fhbrdLvQW6lrbj8X4aGfX4olD8FmgZadhlzU0geJ5VINgfW36-9yu4hK-pA

There is a feature with the API that’ll come with a nifty little button on locked skins to let the player know how to unlock it! You’ll need to make this string yourself, other-wise it defaults to a missing string. The snippet of code below will help you to set your custom unlock requirements
 



--Replace “skinid” with the ID of your skin
--Set your unlock requirements to whatever you wish!
if STRINGS.MODSKINUNLOCK_REQUIREMENT~= nil then --Again making sure the API is actually enabled
	STRINGS.MODSKINUNLOCK_REQUIREMENT[“skinid”] = To unlock this skin you must…. *dies* 
end

And that concludes this part on how to make a lockable modded skin!

Setting Custom Offset/Scale for Clean Sweeper effects

Spoiler

If you've ever paid close attention to the Clean Sweeper, you'll notice the unique effect it spawns when sweeping an entity has unique height offsets and scaling depending on the entity it's used on!

1346346581_GIF9-15-20225-25-03PM.gif.012cf6d91c7b2686a6f1349be90780a8.gif

(See the change in size of the shadow effect on the Alchemy Engine and on the Spear)

If you have a custom item that you'd like to have a different offset and scaling for when sweeped by the Clean Sweeper, it's very simple to do! Here is a template, you may place this in your modmain.lua



if RESKIN_FX_INFO ~= nil then
	RESKIN_FX_INFO["prefabname"] = { offset = 0, scale = 1 }
end

Fill in prefabname, and adjust offset and scale as needed.

 

Compatibility with Mods in Menu

Spoiler

Mods In Menu

?imw=5000&imh=5000&ima=fit&impolicy=Letterbox&imcolor=%23000000&letterbox=false

 

If you're not aware what Mods in Menu is. It's a neat mod made by Fidooop that allows you to load server-side mods on the client. The purpose of this mod is to allow things like seeing modded characters in the main menu, allowing you to see their Compendium and Item Collection screens, and plus their icons will appear for save files that have that character.

If you've done skins with the Modded Skins API, you can also make your mod compatible with mods in menu! If you do so correctly you'll be able to see your mods custom skins in the item collection screen if the Modded Skins API and your mod is enabled with MiM

I would absolutely reccommend checking Fidooop's small tutorial to how to make a mod compatible with MiM, but essentially you need to create a new lua file where your modmain is named "modclientmain.lua". This does exactly what it sounds like and is a modmain that MiM loads on the client. If you load all the data related to your skins/character then you should be able to view your custom skins in the main menu.

 

 

Resources

Spoiler

Skinset Portraits

Spoiler

Here are all of the skinset portraits that I have gathered! Some are unfortunately missing however. If someone can make convincing copies of the missing ones then I'm happy enough to include them.

empty_portraits.zip

Modded Skins Compatibility Icon

Spoiler

An icon to let people know your mod is compatible with Modded Skins, feel free to use and paste in screenshot previews of your mod or in the description.

ms-compatible.png

 

 

 

And also! Let me know when you've got a mod compatible with the API! So that I may add it to the collection of API compatible mods :>

Edited by Hornete
Added resources section
  • Like 11
  • Thanks 1
  • Ninja 1
  • Health 1
  • Spool 2
  • Wavey 1
  • Big Ups 1
Link to comment
Share on other sites

10 hours ago, TheSkylarr said:

Here we go! Can't wait for the item part of the tutorial, I may dive into the scripts and try to do it without the tutorial though if I get bored!

Item part should be finished now ^-^

Link to comment
Share on other sites

I have a question, assuming this is the place to ask it. When one unlocks a skin, does it stay unlocked? Like, say I start a world and unlock a character's skin, then start a new world with the same character, does it stay unlocked in the new world? Because it'd be neat to have unlock conditions that involve ReForged and Re-Gorge-itated.

Link to comment
Share on other sites

5 hours ago, icantevenname said:

I have a question, assuming this is the place to ask it. When one unlocks a skin, does it stay unlocked? Like, say I start a world and unlock a character's skin, then start a new world with the same character, does it stay unlocked in the new world? Because it'd be neat to have unlock conditions that involve ReForged and Re-Gorge-itated.

Yep! Skin Unlocks are tied to the client just like how the Cookbook and the Plant Registry in the vanilla game are.

Link to comment
Share on other sites

Posted (edited)

When some of you get to making your character mods/item mods compatible with Modded Skins, it'd be great if you could use the image below in the description of your mod to show it's compatible with the API and let users know that, :D

Could even slap it on some screenshots showing usage of the API in the gallery of your mod's workshop page too!

Spoiler

ms-compatible.png

 

Edited by Hornete
Link to comment
Share on other sites

22 minutes ago, Cagealicous said:

The big question I've got on my mind is do we replace the current code for said character if it is using your old Skin API code? Or is it adaptable with this old code?

Luckily there should be no crashes if the two clash. I would absolutely reccommend you get rid of the code I told you to place in modmain.lua in the old tutorial as all of that stuff is now handled innately in CreatePrefabSkin with the Skins API.

The CreatePrefabSkin part of the tutorial remains pretty much exactly the same, the only differences I can think of is how you should be starting your skin id's with "ms_" now, and the rarities.(Only available rarities are 'ModMade', and "ModLocked', we automatically set your skin to 'ModMade' if you're not using one of the two)

  • Like 1
Link to comment
Share on other sites

Posted (edited)

So how do I deal with items that have two anim files, one for the item's drop animation and the other that's used for the swap symbol?

For ex.

My item's default build and bank is called "itemname" which is mostly used for the drop animation, but my item uses a different build for equipping the item called "swap_itemname"

With that being said, my item I'm mentioning shows the drop animation correctly, but for some reason when I equip the item, it looks like I have the mime skin for that item equipped (meaning that the item literally doesn't show up even when I specified the files in my assets table)

Also another problem is that my item's inventory image and name for the skin doesn't show up except for the crafting tab for some reason.

Edited by Stormish
  • Thanks 1
Link to comment
Share on other sites

Posted (edited)
1 hour ago, Stormish said:

So how do I deal with items that have two anim files, one for the item's drop animation and the other that's used for the swap symbol?

For ex.

My item's default build and bank is called "itemname" which is mostly used for the drop animation, but my item uses a different build for equipping the item called "swap_itemname"

With that being said, my item I'm mentioning shows the drop animation correctly, but for some reason when I equip the item, it looks like I have the mime skin for that item equipped (meaning that the item literally doesn't show up even when I specified the files in my assets table)

Klei's system has only ever really supported having one build for both the items drop animation and swap symbol, and we use these same systems. Super easy to fix still though!
I'm thinking you could use a table where you set the key of the skin build and set it's value to be the actual swap build and reference that table for your OnEquip.

For example, 

local swapbuilds = {
	["skinname"] = "swap_skinname",
}
local function OnEquip(inst, owner)
	local skin_build = inst:GetSkinBuild()
	if skin_build ~= nil then
		owner.AnimState:OverrideItemSkinSymbol("swap_body", swapbuilds[skin_build], "swap_body", inst.GUID, "defaultbuild")
	else
		owner.AnimState:OverrideSymbol("swap_body", "defaultbuild", "swap_body")
	end
end

Let's say for example the ID our skin(or the build override) is "skinname", and your swap is in a different build named "swap_skinname". We can instead use a table to use "swap_skinname" if the skin build is "skinname".

1 hour ago, Stormish said:

Also another problem is that my item's inventory image and name for the skin doesn't show up except for the crafting tab for some reason.

Could you do some prints with your skinned item in the world and tell me what prints out in the console?

local invitem = c_findnext("prefabnameofyouritem") 
    print(invitem.replica.inventoryitem:GetAtlas(), invitem.replica.inventoryitem:GetImage())

(I would also appreciate seeing how you're registering your inventory icon, I assume you're using RegisterInventoryItemAtlas? And I also assume your icon name is the same as the ID of the skin?)

Edited by Hornete
Link to comment
Share on other sites

10 minutes ago, Hornete said:

Klei's system has only ever really supported having one build for both the items drop animation and swap symbol, and we use these same systems. Super easy to fix still though!
I'm thinking you could use a table where you set the key of the skin build and set it's value to be the actual swap build and reference that table for your OnEquip.

For example, 


local swapbuilds = {
	["skinname"] = "swap_skinname",
}
local function OnEquip(inst, owner)
	local skin_build = inst:GetSkinBuild()
	if skin_build ~= nil then
		owner.AnimState:OverrideItemSkinSymbol("swap_body", swapbuilds[skin_build], "swap_body", inst.GUID, "defaultbuild")
	else
		owner.AnimState:OverrideSymbol("swap_body", "defaultbuild", "swap_body")
	end
end
 

Let's say for example the ID our skin(or the build override) is "skinname", and your swap is in a different build named "swap_skinname". We can instead use a table to use "swap_skinname" if the skin build is "skinname".

Thanks, that worked!

12 minutes ago, Hornete said:

Could you do some prints and tell me what prints out in the console?


local invitem = c_findnext("prefabnameofyouritem") 
    print(invitem.replica.inventoryitem:GetAtlas(), invitem.replica.inventoryitem:GetImage())
 

(I would also appreciate seeing how you're registering your inventory icon, I assume you're using RegisterInventoryItemAtlas? And I also assume your icon name is the same as the ID of the skin?)

I printed that and this is what I got

[00:05:01]: 1847978818	ms_jangtonknife_bloody.tex

I did however get some weird errors and it's attempting to get the inventory image from inventoryimages2.xml which is not where I put my inventory image for the skin

[00:05:01]: WARNING! Could not find region 'ms_jangtonknife_bloody.tex' from atlas 'FROMNUM'. Is the region specified in the atlas?
[00:05:01]: Looking for default texture 'default.tex' from atlas 'FROMNUM'.
[00:05:01]: WARNING! Could not find region 'default.tex' from atlas 'FROMNUM'. Is the region specified in the atlas?
[00:05:01]: Error Looking for default texture in from atlas 'FROMNUM'.
[00:05:01]: WARNING! Could not find region 'jangtonknife.tex' from atlas 'images/inventoryimages2.xml'. Is the region specified in the atlas?
[00:05:01]: Looking for default texture '' from atlas 'images/inventoryimages2.xml'.
[00:05:01]: Error Looking for default texture in from atlas 'images/inventoryimages2.xml'.
[00:05:02]: WARNING! Could not find region 'jangtonknife.tex' from atlas 'images/inventoryimages2.xml'. Is the region specified in the atlas?
[00:05:02]: Looking for default texture '' from atlas 'images/inventoryimages2.xml'.
[00:05:02]: Error Looking for default texture in from atlas 'images/inventoryimages2.xml'.

Also yes, I did use RegisterInventoryItemAtlas as seen here

RegisterInventoryItemAtlas(GLOBAL.resolvefilepath("images/inventoryimages/ms_jangtonknife_bloody.xml"), "ms_jangtonknife_bloody.tex")

 

Link to comment
Share on other sites

7 minutes ago, Stormish said:

[snip]

Are you setting your atlas for your item manually? When testing with my friend they had been setting the inventory atlas/image for their image manually like so, which had made them run into issues.

inst.components.inventoryitem.atlasname = "images/atlasname.tex"

This may be interfering with your skin icon if that is the case.

Link to comment
Share on other sites

8 minutes ago, Hornete said:

Are you setting your atlas for your item manually? When testing with my friend they had been setting the inventory atlas/image for their image manually like so, which had made them run into issues.


inst.components.inventoryitem.atlasname = "images/atlasname.tex"

This may be interfering with your skin icon if that is the case.

I did do this for the default prefab for the item itself
 

	inst:AddComponent("inventoryitem")
	inst.components.inventoryitem.atlasname = "images/inventoryimages/"..inventoryimage_atlas..".xml"
	inst.components.inventoryitem.imagename = inventoryimage_image

I assume this has no use anymore?

Link to comment
Share on other sites

1 minute ago, Stormish said:

I did do this for the default prefab for the item itself
 


	inst:AddComponent("inventoryitem")
	inst.components.inventoryitem.atlasname = "images/inventoryimages/"..inventoryimage_atlas..".xml"
	inst.components.inventoryitem.imagename = inventoryimage_image

I assume this has no use anymore?

Yep! The atlas name being set here is overriding the actual one used for your skin item.

I would recommend using "RegisterInventoryItemAtlas" for all your inventory icons. Not just for the API, but if you don't use RegisterInventoryItemAtlas then currently you have to manually put the data for your icon in every spot of the game where it's needed(Crafting Recipes, Inventory, The "Enters the Constant With:" section of the character select, minisign images, etc etc).

Using RegisterInventoryItemAtlas covers ALL of these things in just one easy line

Link to comment
Share on other sites

6 minutes ago, Hornete said:

Yep! The atlas name being set here is overriding the actual one used for your skin item.

I would recommend using "RegisterInventoryItemAtlas" for all your inventory icons. Not just for the API, but if you don't use RegisterInventoryItemAtlas then currently you have to manually put the data for your icon in every spot of the game where it's needed(Crafting Recipes, Inventory, The "Enters the Constant With:" section of the character select, minisign images, etc etc).

Using RegisterInventoryItemAtlas covers ALL of these things in just one easy line

That actually makes life a lot easier when it comes to inventory images.

Thank you so much!

  • Big Ups 1
Link to comment
Share on other sites

Also, one last thing

So I did mention the drop animation worked right? Sike, it didn't

Well it worked, but it made another issue where the default prefab of the item's dropping animation doesn't work, I did change their spriter projects banks by making the skin's bank to its own name and it made the default prefab's dropping animation work but not the skinned item.

Is there a solution to this?

Link to comment
Share on other sites

Posted (edited)
10 minutes ago, Stormish said:

Well it worked, but it made another issue where the default prefab of the item's dropping animation doesn't work, I did change their spriter projects banks by making the skin's bank to its own name and it made the default prefab's dropping animation work but not the skinned item.

Is there a specific reason you're giving the skin it's own bank? The idea behind skinning is that, well, you're taking existing animations and replacing new assets on top, so you shouldn't be needing to make a seperate bank for each skin, only builds.

If you absolutely do need to keep the skin's unique bank, you can add onto the init_fn and clear_fn you have to set a new bank/return to the old bank when skinning. Here is an example:

GLOBAL.nightmarecollar_init_fn = function(inst, build_name)
    GLOBAL.basic_init_fn( inst, build_name, "nightmarecollar" )

	inst.AnimState:SetBank(build_name) --This assumes the build name is the same as your bank, adjust accordingly if your build name is not equal to your bank.
end

GLOBAL.nightmarecollar_clear_fn = function(inst)
    GLOBAL.basic_clear_fn(inst, "nightmarecollar" )

	inst.AnimState:SetBank("nightmarecollar") --Returning to the original bank when skin is cleared.
end

 

Edited by Hornete
Link to comment
Share on other sites

2 minutes ago, Hornete said:

Is there a specific reason you're giving the skin it's own bank? The idea behind skinning is that, well, you're taking existing animations and replacing new assets on top, so you shouldn't be needing to make a seperate bank for each skin, only builds.

If you absolutely do need to keep the skin's unique bank, you can add onto the init_fn and clear_fn you have to set a new bank/return to the old bank when skinning. Here is an example:


GLOBAL.nightmarecollar_init_fn = function(inst, build_name)
    GLOBAL.basic_init_fn( inst, build_name, "nightmarecollar" )

	inst.AnimState:SetBank(build_name) --This assumes the build name is the same as your bank, adjust accordingly if your build name is not equal to your bank.
end

GLOBAL.nightmarecollar_clear_fn = function(inst)
    GLOBAL.basic_clear_fn(inst, "nightmarecollar" )

	inst.AnimState:SetBank("nightmarecollar") --Returning to the original bank when skin is cleared.
end

 

Well, as I mentioned above, when I rename the skin's bank as my default prefab's bank, it makes the dropping animation of the default prefab not work, but when I rename the skin's bank to have its own name from spriter, the skin's dropping animation just doesn't work but the default prefab does.

Anyways, the code helped me out, thanks again.

  • Like 1
Link to comment
Share on other sites

Sorry if I missed something simple somewhere, but I don't know what do here... ^^;

Do I take the files from your new API mod and insert them into my mod, and if I do, where do I put the skin prefab code you provided here in the thread? Should that go in its own file or should I make a new skins_api file in my mod? I just found that part of the process to be a bit unclear. :P

  • Like 1
Link to comment
Share on other sites

19 minutes ago, Garamonde said:

Sorry if I missed something simple somewhere, but I don't know what do here... ^^;

Do I take the files from your new API mod and insert them into my mod, and if I do, where do I put the skin prefab code you provided here in the thread? Should that go in its own file or should I make a new skins_api file in my mod? I just found that part of the process to be a bit unclear. :P

Sorry for the confusion, I'll take this as feedback and try to improve the tutorial when I'm free.

If you're making your mod have skins then the user will also need to subscribe to the API mod and enable that along-side your mod, if they don't, then they simply lose out on the skins from your mod. The CreatePrefabSkin calls can go into their own file, I would personally reccommend re-using the "character_none" prefab file that you likely already have to add the rest of your skins.

I know it sounds like it might stink that people have to enable another mod in order to use the skins added from your mod, instead of just getting a file and integrating it directly into your mod, I've gotten a bit of backlash from others for it. I used to not like the idea of seperate API mods all that much for the mentioned reason too! But it is for the better as it allows every mod to share a unified code base, instead of every modder having their own API skins file that they need to manually update by re-downloading from their respective tutorial posts. That's a lot to ask from each individual modder. Plus the way this mod was created makes it essentially (hopefully) immune to future updates, My old skins api file and Cunning Fox's file unfortunately won't stand the test of time like this revamped API mod will.

  • Like 1
Link to comment
Share on other sites

Posted (edited)

Thank you! I understand, you gotta do what you gotta do for the better! But uh, having said that, I now have an actual problem with loading my mod:

Dedicated server failed to start error, and according to the log here, it says I'm trying to "skin something official", and to "contact Hornet". Weird.

master_server_log.txt

EDIT: Also, when I try to launch a server without Modded Skins API, I get this error:

"[00:00:02]: [string "scripts/mainfunctions.lua"]:1485: variable 'global_error_widget' is not declared", not sure if that's related or not, though. Also, forgot to add my actual "_none" file, heh.

kk_none.lua

Edited by Garamonde
Added an extra error.
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...