Jump to content

[Tutorial] Making your own skins for modded items


Cunning fox
 Share

Recommended Posts

 

DISCLAIMER: This guide is for modded items only. Klei prohibited creating custom skins for official items, and this library won't be able to add them.

Hey everyone! Since I haven't found up-to-date and easy to use library for creating custom skins for items, I decided to make my own one and share it with you :D

I used @Kzisor / Ysovuka's library as a base for mine.
1. Installation
First of all, you'll need to download the library from here:

Then, you'll need to put it in your mod's folder. I'll be using path scripts/libs/skins_api.lua.
After that, you'll need to include this library for your mod. Add this line into your modmain:
 

modimport("scripts/libs/skins_api.lua")

All set! The library is installed, and we can actually add our skins.
2. Making custom skin prefab
Unlike Klei's skins, modded skins use different prefabs for every skin. So, you can different properties for every skin! They even can have different functionality.

So, you have 2 options:

  1. Make a separate prefabs file for skins (or each skin)
  2. Make prefab's skins directly in the base prefab's file.

To register your skin you'll need to use CreateModPrefabSkin(name, properties) function, where name is a string with the name of your skin, and properties is a table. For example:
 

CreateModPrefabSkin("dummy_formal",
	{
		assets = {
			Asset("ANIM", "anim/dummy_formal.zip"),
		},
		base_prefab = "dummy",
		fn = formal,
		rarity = "Timeless",
		reskinable = true,
		
		build_name_override = "dummy_formal",
		
		type = "item",
		skin_tags = { },
		release_group = 0,
	})
  • Assets: a table of assets for your skin;
  • base_prefab: Prefab of the items you're making a skin for.
  • fn: Prefab's constructor. Acts like a regular prefab constructor.
  • rarity: Your item's rarity.
  • reskinable: Will your item be reskinable using the Clean Sweeper.
  • build_name_override: The build of the skin.

The rest of the properties is only used by Klei, so it's better to just keep them unchanged.
If you're confused about the Fn, here's an example:

Let's say that you want to make a skin for something similar to a carrot. You have a constructor function:

local function fn()
    local inst = CreateEntity()

    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    inst.entity:AddNetwork()

    inst.AnimState:SetBank("carrot")
    inst.AnimState:SetBuild("carrot")
    inst.AnimState:PlayAnimation("planted")

    inst.entity:SetPristine()

    if not TheWorld.ismastersim then
        return inst
    end

    inst:AddComponent("inspectable")

    inst:AddComponent("pickable")
    inst.components.pickable:SetUp("carrot", 10)
    inst.components.pickable.onpickedfn = onpicked

    return inst
end

To make a skin you'll need to create a new constructor like this:
 

local function carrot_skin()
	local inst = fn()

	inst.AnimState:SetBuild("skinned_carrot_build")

	return inst
end

Here we create a default carrot (using fn()), and then apply some changes to it (change the carrot's build).
After that, we'll need to return our skin, just like a regular prefab. At the end of the file you need to put:

return Prefab("carrot", fn, assets),
	CreateModPrefabSkin("dummy_formal",
	{
		assets = {
			Asset("ANIM", "anim/skinned_carrot.zip"),
		},
		base_prefab = "carrot",
		fn = carrot_skin, -- This is our constructor!
		rarity = "Timeless",
		reskinable = true,
		
		build_name_override = "skinned_carrot",
		
		type = "item",
		skin_tags = { },
		release_group = 0,
	})


3. Updating the recipe and adding strings

After creating your skins' prefab, you'll need to update your recipe too. After you've added your item's recipe with AddRecipe, you'll need to call MadeRecipeSkinnable(recipe_name, atlas_data), where recipe_name is the name of your recipe, and atlas_data is a table, containing info about your skin's inventory image.

MadeRecipeSkinnable("dummy", {
	dummy_formal = {
		atlas = "images/inventoryimages/dummy.xml",
		image = "dummy_formal.tex",
	},
})

The second argument is the table. Here you'll add atlas and image of the skin. Here, dummy_formal is the name of the skin, the atlas is a path to .xml file, and the image is the name of the texture.

After that, you'll need to name your skin, using STRINGS.SKIN_NAMES table. Is works just like adding names to regular prefabs, but the name of the prefab has to be lowercased.

STRINGS.SKIN_NAMES.dummy_formal = "Formal dummy"

All set! Your modded item now should have its own skin.
4. Applying skins on placers.
If your item is a structure and has a placer, you'll want to apply a skin to its placer too. To do it, you'll need to modify your MakePlacer, to make it look like this:

MakePlacer("dummy_placer", "dummy", "dummy", "anim", nil, nil, nil, nil, nil, nil, placer)

placer here is a custom function that'll be applying the skin itself. If should look like this:

local function placer(inst)
	inst.ApplySkin = function(inst, skin)
		if skin == "dummy_formal" then
			inst.AnimState:SetBuild("dummy_formal")
		end
	end
end

Here placer function adds ApplySkin function. The skin argument is the name of the skin for the item. In this example, we check if the skin is dummy_formal, and apply custom build for a placer if it's true.

That's all you need to add a skin for your item! If you have any questions or feature requests, feel free to ask them in the comments! :D

Mods that use this library (You can use them as an example)
The Dummy Mod

Edited by Cunning fox
  • Like 4
  • Thanks 4
Link to comment
Share on other sites

My game froze and crashed with the API when someone spawned a clean sweeper.

This is the error I got:
 

Spoiler

[00:30:47]: error calling PrefabPostInit: reskin_tool in mod workshop-1422448457 (Thomas, The Skeleton Chef): 
[string "../mods/workshop-1422448457/scripts/tools/i..."]:222: attempt to index field 'spellcaster' (a nil value)
LUA ERROR stack traceback:
        ../mods/workshop-1422448457/scripts/tools/itemskins_api.lua(222,1)
        =(tail call) ?
        =[C] in function 'xpcall'
        scripts/mods.lua(162,1) in function 'mod'
        scripts/mainfunctions.lua(267,1)	
[00:30:47]: Disabling workshop-1422448457 (Thomas, The Skeleton Chef) because it had an error.	
[00:30:47]: [string "../mods/workshop-1422448457/scripts/tools/i..."]:222: attempt to index field 'spellcaster' (a nil value)
LUA ERROR stack traceback:
        ../mods/workshop-1422448457/scripts/tools/itemskins_api.lua(222,1)
        =(tail call) ?
        =[C] in function 'xpcall'
        scripts/mods.lua(162,1) in function 'mod'
        scripts/mainfunctions.lua(267,1)
[00:30:48]: [string "../mods/workshop-1422448457/scripts/tools/i..."]:222: attempt to index field 'spellcaster' (a nil value)
LUA ERROR stack traceback:
        ../mods/workshop-1422448457/scripts/tools/itemskins_api.lua(222,1)
        =(tail call) ?
        =[C] in function 'xpcall'
        scripts/mods.lua(162,1) in function 'mod'
        scripts/mainfunctions.lua(267,1)	
[00:30:48]: error calling PrefabPostInit: reskin_tool in mod workshop-1341057026 (Stormish): 
[string "../mods/workshop-1341057026/scripts/tools/i..."]:222: attempt to index field 'spellcaster' (a nil value)
LUA ERROR stack traceback:
        ../mods/workshop-1341057026/scripts/tools/itemskins_api.lua(222,1)
        =(tail call) ?
        =[C] in function 'xpcall'
        scripts/mods.lua(162,1) in function 'mod'
        scripts/mainfunctions.lua(267,1)	
[00:30:48]: Disabling workshop-1341057026 (Stormish) because it had an error.	

 

 

Link to comment
Share on other sites

I found out the reason why the game crashes when the Clean Sweeper is spawned.

The reason why it crashed is because you didn't rmake it check if the spellcaster component existed or not and since it's doing this on client side, it wasn't able to find the spellcaster component so the game crashed for that very reason, hence why it says spellcaster is a "nil" value.

This is what the PrefabPostInit function for "reskin_tool" should look like now in the API code:

Spoiler

env.AddPrefabPostInit("reskin_tool", function(inst)
	local spellcaster = inst.components.spellcaster
	if spellcaster ~= nil then
		local _can_cast_fn = spellcaster.can_cast_fn
		spellcaster.can_cast_fn = function(doer, target, ...)
			if HasSkins(target) then
				return true
			end
			
			return _can_cast_fn(doer, target, ...)
		end
		
		-- Our custom reskinner!
		local _spell = spellcaster.spell
		spellcaster.spell = function(inst, target, ...)
			target = target or inst.components.inventoryitem.owner --if no target, then get the owner of the tool. Self target for beards
			if not HasSkins(target) then
				return _spell(inst, target, ...)
			end
			
			SpawnAt("explode_reskin", target)

			local prefab = target.base_prefab or target.prefab
			local skin = target.skinname or target.prefab
			
			if not inst._cached_reskinname[prefab] then
				inst._cached_reskinname[prefab] = skin
			end
			
			while inst._cached_reskinname[prefab] == skin do
				for item_type, _ in pairs(MODDED_SKINS[prefab]) do
					if item_type ~= skin then
						inst._cached_reskinname[prefab] = item_type
						break
					end
				end
			end
			
			ReskinModEntity(target, inst._cached_reskinname[prefab])
		end
	end
end)

 

 

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

On 6/2/2020 at 4:42 PM, Stormish said:

I found out the reason why the game crashes when the Clean Sweeper is spawned.

The reason why it crashed is because you didn't rmake it check if the spellcaster component existed or not and since it's doing this on client side, it wasn't able to find the spellcaster component so the game crashed for that very reason, hence why it says spellcaster is a "nil" value.

This is what the PrefabPostInit function for "reskin_tool" should look like now in the API code:

  Reveal hidden contents


env.AddPrefabPostInit("reskin_tool", function(inst)
	local spellcaster = inst.components.spellcaster
	if spellcaster ~= nil then
		local _can_cast_fn = spellcaster.can_cast_fn
		spellcaster.can_cast_fn = function(doer, target, ...)
			if HasSkins(target) then
				return true
			end
			
			return _can_cast_fn(doer, target, ...)
		end
		
		-- Our custom reskinner!
		local _spell = spellcaster.spell
		spellcaster.spell = function(inst, target, ...)
			target = target or inst.components.inventoryitem.owner --if no target, then get the owner of the tool. Self target for beards
			if not HasSkins(target) then
				return _spell(inst, target, ...)
			end
			
			SpawnAt("explode_reskin", target)

			local prefab = target.base_prefab or target.prefab
			local skin = target.skinname or target.prefab
			
			if not inst._cached_reskinname[prefab] then
				inst._cached_reskinname[prefab] = skin
			end
			
			while inst._cached_reskinname[prefab] == skin do
				for item_type, _ in pairs(MODDED_SKINS[prefab]) do
					if item_type ~= skin then
						inst._cached_reskinname[prefab] = item_type
						break
					end
				end
			end
			
			ReskinModEntity(target, inst._cached_reskinname[prefab])
		end
	end
end)

 

 

Oh, right, sorry. It's just exams season in my university rn, I don't have that much time on mods, but I'll update it asap! :)

  • Thanks 1
Link to comment
Share on other sites

I have multiple skins on one item in my implementation, but the Clean Sweeper only toggles between my item's default skin and one other skin, despite there being 3 working and craftable skins other than the default, not just one. Anyone seem to know why?

Example: Starts with 3 different skins plus default, end up with only default and one other.

wrongskin.gif.b3ece440c36b35b1db0b2d615c3f5b54.gif

Link to comment
Share on other sites

On 09.01.2022 at 18:12, TheSkylarr said:

У меня есть несколько скинов для одного предмета в моей реализации, но Clean Sweeper переключается только между скином моего предмета по умолчанию и одним другим скином, несмотря на то, что есть 3 рабочих и создаваемых скина, отличных от стандартного, а не только один. Кажется, кто-нибудь знает, почему?

Пример: начинается с 3 разных скинов плюс по умолчанию, заканчивается только по умолчанию и еще одним.

неправильной кожи.gif.b3ece440c36b35b1db0b2d615c3f5b54.gif

Could you post the source?

Link to comment
Share on other sites

On 25.05.2020 at 20:39, Cunning fox said:

 

DISCLAIMER: This guide is for modded items only. Klei prohibited creating custom skins for official items, and this library won't be able to add them.

Hey everyone! Since I haven't found up-to-date and easy to use library for creating custom skins for items, I decided to make my own one and share it with you :D

I used @Kzisor / Ysovuka's library as a base for mine.
1. Installation
First of all, you'll need to download the library from here:

Then, you'll need to put it in your mod's folder. I'll be using path scripts/libs/skins_api.lua.
After that, you'll need to include this library for your mod. Add this line into your modmain:
 


modimport("scripts/libs/skins_api.lua")

All set! The library is installed, and we can actually add our skins.
2. Making custom skin prefab
Unlike Klei's skins, modded skins use different prefabs for every skin. So, you can different properties for every skin! They even can have different functionality.

So, you have 2 options:

  1. Make a separate prefabs file for skins (or each skin)
  2. Make prefab's skins directly in the base prefab's file.

To register your skin you'll need to use CreateModPrefabSkin(name, properties) function, where name is a string with the name of your skin, and properties is a table. For example:
 


CreateModPrefabSkin("dummy_formal",
	{
		assets = {
			Asset("ANIM", "anim/dummy_formal.zip"),
		},
		base_prefab = "dummy",
		fn = formal,
		rarity = "Timeless",
		reskinable = true,
		
		build_name_override = "dummy_formal",
		
		type = "item",
		skin_tags = { },
		release_group = 0,
	})
  • Assets: a table of assets for your skin;
  • base_prefab: Prefab of the items you're making a skin for.
  • fn: Prefab's constructor. Acts like a regular prefab constructor.
  • rarity: Your item's rarity.
  • reskinable: Will your item be reskinable using the Clean Sweeper.
  • build_name_override: The build of the skin.

The rest of the properties is only used by Klei, so it's better to just keep them unchanged.
If you're confused about the Fn, here's an example:

Let's say that you want to make a skin for something similar to a carrot. You have a constructor function:


local function fn()
    local inst = CreateEntity()

    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    inst.entity:AddNetwork()

    inst.AnimState:SetBank("carrot")
    inst.AnimState:SetBuild("carrot")
    inst.AnimState:PlayAnimation("planted")

    inst.entity:SetPristine()

    if not TheWorld.ismastersim then
        return inst
    end

    inst:AddComponent("inspectable")

    inst:AddComponent("pickable")
    inst.components.pickable:SetUp("carrot", 10)
    inst.components.pickable.onpickedfn = onpicked

    return inst
end

To make a skin you'll need to create a new constructor like this:
 


local function carrot_skin()
	local inst = fn()

	inst.AnimState:SetBuild("skinned_carrot_build")

	return inst
end

Here we create a default carrot (using fn()), and then apply some changes to it (change the carrot's build).
After that, we'll need to return our skin, just like a regular prefab. At the end of the file you need to put:


return Prefab("carrot", fn, assets),
	CreateModPrefabSkin("dummy_formal",
	{
		assets = {
			Asset("ANIM", "anim/skinned_carrot.zip"),
		},
		base_prefab = "carrot",
		fn = carrot_skin, -- This is our constructor!
		rarity = "Timeless",
		reskinable = true,
		
		build_name_override = "skinned_carrot"
		    
	


3. Обновление рецепта и добавление строк

После создания префаба ваших скинов вам также нужно будет обновить свой рецепт. После того, как вы добавили рецепт вашего предмета с помощью AddRecipe , вам нужно будет вызвать  MadeRecipeSkinnable(recipe_name, atlas_data) , где recipe_name — это название вашего рецепта, а atlas_data — это таблица, содержащая информацию об изображении инвентаря вашего скина.


    
	

Второй аргумент — это таблица. Здесь вы добавите атлас и изображение кожи. Здесь dummy_formal — название скина, атлас — путь к XML-файлу, а изображение — имя текстуры.

После этого вам нужно будет назвать свой скин, используя  таблицу STRINGS.SKIN_NAMES  . Это работает так же, как добавление имен к обычным префабам, но имя префаба должно быть в нижнем регистре.


Все готово! Ваш модифицированный предмет теперь должен иметь собственный скин.
4. Нанесение шкур на россыпи.
Если ваш предмет представляет собой структуру и имеет россыпь, вы также захотите применить скин к его россыпи. Для этого вам нужно изменить MakePlacer, чтобы он выглядел так:


         

россыпь здесь — это пользовательская функция, которая будет применять сам скин. Если должно выглядеть так:


  
		  
		
	

Здесь функция россыпи добавляет функцию ApplySkin. Аргумент skin — это имя скина для предмета. В этом примере мы проверяем, является ли скин  dummy_formal , и применяем кастомную сборку для россыпи, если это правда.

Это все, что вам нужно, чтобы добавить скин для вашего предмета! Если у вас есть какие-либо вопросы или пожелания, не стесняйтесь задавать их в комментариях! :D

Моды, которые используют эту библиотеку  (Вы можете использовать их в качестве примера)
The Dummy Mod

It doesn't work anymore.

44-1.jpg

Link to comment
Share on other sites

Seems like this is broken with the update to the crafting UI. Dang.

From a few minutes of glancing at the code, it may be able to be mostly reused, just will need some changes to replace the new UI rather than the old one. Unfortunately I have no idea how to do this on my own.

Edited by TheSkylarr
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...