Rickzzs Posted August 24, 2023 Share Posted August 24, 2023 links: Glassic API for DST skin, open source https://steamcommunity.com/sharedfiles/filedetails/?id=2521851770 Skin System for DS skin, partially open source https://steamcommunity.com/sharedfiles/filedetails/?id=2883799470 contents: 1.A Brief Introduction of Skin in DS/DST 2.Drawing Variants 3.Applying Variants 4.Drawing Skins 5.Registering Skins 6.Applying Skins 1.A Brief Introduction of Skin in DS/DST A skin in DST/DS means a different texture. Most skins only have a texture, some with different animation, some with visual fx and sound fx. Something less than skin, I would call it variant, is simply a different texture. For example, merm guard is a variant of merm, pig guard is a variant of pigman. Just because their texture is so similar that it cannot be called a skin. Skins in DST are split into some categories. The bulk of skins is items, and then the head bases and clothes. There are other kind of skins like player portraits, profile flairs, loading backgrounds, emoji, emotes, etc., but I'm not talking about these skins. Today DST has about 500+ item/head skins and ~1000 clothes. Their textures are stored in data/dynamic/*.dyn and databundles/anim_dynamic.zip/anim/dynamic/*.bin. These assets , the former encrypted and the latter not, cannot be directly used. However, you can screenshot their in-game look in the curios. To make your own skin, you can start by drawing your own texture first. And then you register your skins into the game, using either Modded Skins or Glassic API. Finally you apply your skin to an item. The existence of an API in DST is because the official system won't allow a custom skin to be owned. The existence of an API in DS in because DS never had a skin system. 1 Link to comment Share on other sites More sharing options...
Rickzzs Posted August 24, 2023 Author Share Posted August 24, 2023 (edited) 2.Drawing Variants 2.1Find Assets Here I will show how to draw the variants of Replica Relic Dish. Firstly you need to use search for the texture name of the dish. I recommend you use Everything, File Locator Pro and AnyTxt. I search in databundles/scripts.zip/languages/, and find the "Replica Relic Dish"'s prefab name is "ruinsrelic_bowl". Secondly you search the prefab. I find this prefab inside databundles/scripts.zip/prefabs/ruinsrelic.lua Lastly you search its texture. I find local assets = { Asset("ANIM", "anim/"..build..".zip"), } The build of ruinsrelic_chipbowl is local build = "ruins_"..name The name is "chipbowl", so all sums up to "ruins_chipbowl". Go to anim/, search this name and I find It has anim.bin in it! Very lucky that you will not need to find this one in another asset. Actually, you need at least 1 anim.bin to play a proper animation. However, when you are just creating texture, anim.bin is useless. 2.2Extract Assets Download pyscripts/ and html/ from the github repository, and install python3. Once you have them ready, use a terminal to convert the asset. (The different folder name in the images is because I have moved these files to z:/rr/ruinsrelic_chipbowl/, just ignore this.) python pyscripts/cli.py ruins_chipbowl.zip -json This command will give you anim.json and build.json in a sub directory. Open the animation player (html/index.html) and load them. THe screenshot you see below is my modified version, I added a spice on the dish. You are free to draw your own texture now. But don't forget to change the attributes in build.json using a text editor. build.json is something like { "type": "Build", "version": 6, "name": "ruinsrelic_chipbowl", "scale": 1, "Atlas": ["atlas-0.tex"], "Symbol": { "chipbowl": [ {"framenum": 0, "duration": 1, "x": -1.9999799728393555, "y": -17.949987411499023, "w": 205, "h": 121, "alphaidx": 96, "alphacount": 42}, {"framenum": 1, "duration": 1, "x": 5.075075149536133, "y": -0.7749959826469421, "w": 215, "h": 87, "alphaidx": 138, "alphacount": 30} ], "spice": [ {"framenum": 0, "duration": 1, "x": 6.899911880493164, "y": -29.324960708618164, "w": 232, "h": 140, "alphaidx": 6, "alphacount": 84} ], "chip": [ {"framenum": 0, "duration": 1, "x": -1.9999799728393555, "y": -17.949987411499023, "w": 205, "h": 121, "alphaidx": 90, "alphacount": 6} ], "bowl": [ {"framenum": 0, "duration": 1, "x": -1.9999799728393555, "y": -17.949987411499023, "w": 205, "h": 121, "alphaidx": 0, "alphacount": 6} ] }, You can see, there are some attributes of an image ,like x,y,w,h. w and h stands for width and height in pixel. x and y stands for the pivot of an image, as you see in the animation player there are also -x and -y as its real position. The relation between them are -x = x - w / 2 ; -y = y - h / 2 ; If you move the image to another place, change x,y in build.json as well. If you resize an image, change w,h. For example, I split the dish into two parts (chip and bowl) and draw a new texture, and change build.json, reload it, and change y. 2.3Compile Assets Firstly I want you to know that, if you are only making a texture, there is nothing to do with animation, so anim.bin is useless. Otherwise you will need anim.bin. Assuming you don't need anim.bin. You have build.json and some images. Compile build.json and image folder to a zip you can use in game. python pyscripts/cli.py ruins_chipbowl/build.json ruins_chipbowl/ 3.Applying Variants 3.1Add Assets In your modmain.lua if you have no chance to add assets in prefab, add the assets like Assets={Asset("ANIM","anim/ruinsrelic_chipbowl.zip")} If you are using dynamic anim files, write Asset("DYNAMIC_ANIM","anim/dynamic/xxx.zip"),--the build Asset("PKGREF","anim/dynamic/xxx.dyn")--the texture Or you have a xml Asset("DYNAMIC_ATLAS","images/xxx.xml") 3.2Add Inventory Icons If you want to also make a different icon for an inventory item, you need to create some 64x64 sized icons, and add these assets. Assume you have them in a folder xxx_inventoryimages. Run python pyscripts/cli.py xxx_inventoryimages/ And you will get xxx_inventoryimages.xml and xxx_inventoryimages.tex Add these assets like Asset("IMAGE","images/xxx_inventoryimages.tex"), Asset("ATLAS","images/xxx_inventoryimages.xml"), Asset("ATLAS_BUILD","images/xxx_inventoryimages.xml",256)--this number is mysterious, or maybe not required 3.3Register Inventory Icons Assume you have xxx_inventoryimages.xml, use the following code to register them. local atlas = "images/xxx_inventoryimages.xml" local function ProcessAtlas(atlas, ...) local path = resolvefilepath_soft(atlas) if not path then print("[API]: The atlas \"" .. atlas .. "\" cannot be found.") return end local success, file = pcall(io.open, path) if not success or not file then print("[API]: The atlas \"" .. atlas .. "\" cannot be opened.") return end local xml = file:read("*all") file:close() local images = xml:gmatch("<Element name=\"(.-)\"") for tex in images do RegisterInventoryItemAtlas(path, tex) RegisterInventoryItemAtlas(path, hash(tex)) end end ProcessAtlas(atlas) 3.3Use Texture The most easy way to retexture an item is the SetBuild and AddOverrideBuild function. inst.AnimState:SetBuild("xxx") If you want only some of your texture applied, like you only want to override the torso symbol of a pigman. inst.AnimState:OverrideSymbol("torso","xxx","torso")--symbol,build,override symbol Edited August 24, 2023 by Rickzzs 1 Link to comment Share on other sites More sharing options...
Rickzzs Posted August 24, 2023 Author Share Posted August 24, 2023 (edited) 4.Drawing Skins The process for skins is the same as variants, so here I am going to talk about the restriction of skins. When you create variants, you can freely modify the animation and use AnimState:SetBank(bank) to play your animation. But when you create skins, you are not encouraged to change the item's original animations, a skin is just a skin. 5.Registering Skins 5.1The process of registering skins contains full process of variants. 5.2Additionally, use CreatePrefabSkin to register it into the skin system. 5.3Finally, register it to the API. Glassic API provides GlassicAPI.SkinHandler to register skins. GlassicAPI.SkinHandler = { GetModSkins = get_mod_skins, AddModSkin = add_mod_skin, RemoveModSkin = remove_mod_skin, IsModSkin = validate_mod_skin, -- Backward compatible IsValidModSkin = validate_mod_skin, -- Backward compatible ValidateModSkin = validate_mod_skin, GetPlayerFromID = get_player_from_id, SetCharacterExclusiveSkin = set_character_exlusive_skin, DoesCharacterHaveSkin = does_character_have_skin, DoesCharacterHasSkin = does_character_have_skin, -- Backward compatible AddModSkins = add_mod_skins, -- Import skin data SetRarity = set_rarity, } GlassicAPI.SkinHandler.AddModSkin("item_skinname") GlassicAPI.SkinHandler.AddModSkins({item1={"item1_skin1","item1_skin2"},item2={}}) Additionally GlassicAPI has the following features: 1.Add Custom Rarity by SetRarity SetRarity(rarity, order, color, override_symbol, override_build) The official rarities are Spoiler SKIN_RARITY = { Common = { 0.718, 0.824, 0.851, 1 }, -- B7D2D9 - a common item (eg t-shirt, plain gloves) Classy = { 0.255, 0.314, 0.471, 1 }, -- 415078 - an uncommon item (eg dress shoes, checkered trousers) Spiffy = { 0.408, 0.271, 0.486, 1 }, -- 68457C - a rare item (eg Trenchcoat) Distinguished = { 0.729, 0.455, 0.647, 1 }, -- BA74A5 - a very rare item (eg Tuxedo) Elegant = { 0.741, 0.275, 0.275, 1 }, -- BD4646 - an extremely rare item (eg rabbit pack, GoH base skins) HeirloomElegant = { 0.933, 0.365, 0.251, 1 }, -- EE5D40 Character = { 0.718, 0.824, 0.851, 1 }, -- B7D2D9 - a character Timeless = { 0.424, 0.757, 0.482, 1 }, -- 6CC17B - not used Loyal = { 0.635, 0.769, 0.435, 1 }, -- A2C46F - a one-time giveaway (eg mini monument) ProofOfPurchase = { 0.000, 0.478, 0.302, 1 }, -- 007A4D Reward = { 0.910, 0.592, 0.118, 1 }, -- E8971E - a set bonus reward Event = { 0.957, 0.769, 0.188, 1 }, -- F4C430 - an event item Lustrous = { 1.000, 1.000, 0.298, 1 } -- FFFF4C - rarity modifier -- #40E0D0 reserved skin colour } -- Share colours SKIN_RARITY.Complimentary = SKIN_RARITY.Common SKIN_RARITY.HeirloomClassy = SKIN_RARITY.HeirloomElegant SKIN_RARITY.HeirloomSpiffy = SKIN_RARITY.HeirloomElegant SKIN_RARITY.HeirloomDistinguished = SKIN_RARITY.HeirloomElegant 2.Set Character Exclusive by SetCharacterExclusiveSkin SetCharacterExclusiveSkin(skin,character_name|{characters}) 5.3[DS]Register it to Skin System. The Skin System mod in DS provides exactly the same API as what DST does, but with fewer functions. The lack of functions are listed below: 1.Do not use init_fn. All init_fn and clear_fn are moved to a global table SKIN_FNS. If your previous code is init_fn = function(inst) xxx_init_fn(inst, "xxx_skin1") end, You need to change it to local old_xxx_init_fn=SKIN_FNS.xxx_init_fn function SKIN_FNS.xxx_init_fn(inst,buildname) if old_xxx_init_fn then old_xxx_init_fn(inst,buildname) end if buildname=="xxx_skin1" then --your own code end end Note that, if you have nothing special, you can just let the skin system do the basic things. Spoiler SKINFNS = { basic_init_fn = function(inst, build_name, def_build) inst.AnimState:SetSkin(build_name, def_build) SKINFNS.basic_inv_init_fn(inst) end, basic_clear_fn = function(inst, def_build) if def_build then inst.AnimState:SetBuild(def_build) end SKINFNS.basic_inv_clear_fn(inst) end, basic_inv_init_fn = function(inst) if inst.components.inventoryitem ~= nil then inst.components.inventoryitem:ChangeImageName(inst:GetSkinName()) end end, basic_inv_clear_fn = function(inst) if inst.components.inventoryitem ~= nil then inst.components.inventoryitem:ChangeImageName() end end } Edited August 28, 2023 by Rickzzs Link to comment Share on other sites More sharing options...
Rickzzs Posted August 24, 2023 Author Share Posted August 24, 2023 6.Applying Skins 6.1TheSim:ReskinEntity TheSim:ReskinEntity( inst.GUID, old_skinname, skinname, other.skin_id, player.userid ) --reskin inst having old skin "old_skinname" to skinname --the ownership is validated using another skin_id or a userid --the other.skin_id is used in special cases like Abigail, boat plank and lureplant. So you don't need to bother. --if there is no old skinname, just put a nil --if there is no other.skin_id, just put a nil --if neither skin_id nor userid, this will have no effect 6.2inst.AnimState:SetSkin inst.AnimState:SetSkin(skinname,default_build) --default_build is used in case this skin is not owned 6.3inst.AnimState:OverrideSkinSymbol=OverrideSymbol 6.4inst.AnimState:OverrideItemSkinSymbol inst.AnimState:OverrideItemSkinSymbol(symbol,build,overridesymbol,other.GUID,default_build) --other.GUID is the entity that this build is linked to. For example, to reskin your swap_hat, you need to provide the hat. --default_build is used when this skin is not owned Your work will be writing some code to call these functions. Like what scripts/prefabs/hats.lua does local function _base_onequip(inst, owner, symbol_override, swap_hat_override) local skin_build = inst:GetSkinBuild() if skin_build ~= nil then owner:PushEvent("equipskinneditem", inst:GetSkinName()) owner.AnimState:OverrideItemSkinSymbol(swap_hat_override or "swap_hat", skin_build, symbol_override or "swap_hat", inst.GUID, fname) else owner.AnimState:OverrideSymbol(swap_hat_override or "swap_hat", fname, symbol_override or "swap_hat") end if inst.components.fueled ~= nil then inst.components.fueled:StartConsuming() end if inst.skin_equip_sound and owner.SoundEmitter then owner.SoundEmitter:PlaySound(inst.skin_equip_sound) end end Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now