Jump to content

Guidance for the next steps of making a modded character


Recommended Posts

Hello, I’m making a new character for DST, based on equippable hats to gain any kind of buffs (and debuffs). I’ve read most of pertinent tutorials I could find, and so far, so good : I’ve managed to implement my ideas for all my hats in their initial version and they work well. However, I need some guidance for the next steps, and reassurance on specific points, since I lack deep knowledge in Lua and deep knowledge on how the game works on some parts.

About making a post in the modding forums, Ultroman said is his guidelines :

 

Quote

Try to keep your post on topic and don't ask too many things at once. It's better to start two, three or four topics, if you want help with two, three or four different things, but you should generally try to focus on fixing only a few things at a time.

I know I should make several posts, but I have regrouped all my questionings during my development so I could make a unique post, instead of spamming several. I apologize in advance for the long post (There's a TL; DR in the end just in case) and for any English mistake .

 

General

1) The game had two events : the Forge and the Gorge, which I sadly, and entirely, missed them. I’ve noticed those obviously exists in the game’s code. However, it seems that some characters scripts have special cases concerning them.

 

  • Since the events are gone, do I need to take them in consideration when coding my character ?

 

2) I made until now 10 hats that have effects on the wearer. However, for some, I would like to disable the possibility for the player to unequip them (like a cursed item). I have found nothing that matches this behavior so far in the game, even in the equippable component. I naturally thought about the slurper, but the latter is a “fake hat”, and forces to unequip your current one.

 

  • How can I do this ? Is anything already exists for that purpose, or do I need to remove any listeniner for any “unequip” events ?

 

3) I’ve noticed that torches and objects with fueled lights have an event called “torchranout”. I guess this is an event to trigger the reaction of the character saying “My torch ran out!”. I’ve copied the code from the miner hat, but I don’t know if I used it properly, since my character doesn’t say anything when the hat’s fuel is depleted.

 

  • Do I need to worry about this specific point ? Is that event needed for the good behavior of the game ?

 

Core idea : Hats

4) All my hats do what I wanted to implement but truth is, it’s half the core concept of my character. I would like to implement a mechanic that would fill or empty a new gauge, like the wetness for example, and the gauge percentage would have effect on wearing hats.

 

  • How can I implement this ? Is implementing this mechanic as a component the best way to do it ?

 

5) I’ve made a hat that allows cooking with it, but only when equipped. To disable the fact that you could cook with objects put on the floor, I remove the tag in the cooker component when unequipping the hat, and adding it again when equipped.

 

  •  Is this a good way to do it ? Since no method exists in cooker in that purpose, it feels like a hack.

 

6) I’ve made a hat with specific properties, and I would like it to spawn it more shadow creatures when insane, like the nightmare amulet, as described in the wiki. However, after checking amulet.lua, I failed to see where the “spawn more shadow creatures” is managed. Even in sanity:SetInducedInsanity, there’s nothing about spawning them.

 

  •  Is the spawn rate is really altered or is the wiki lying ? If the spawn rate is truly changed, where can I find the code responsible for this ?

 

7-8) I’ve made a “lucky hat”, that is extremely fragile and alters the chances objects can be looted. To do that, I made it an “armor” and I literally edit the loot table of the monster that is attacked by the wearer. I have two questions on this :

 

  • Can I make a hat breakable without the armor component ? I would really like that the absorption reduction, and the attacked creature’s power, would be out of the equation for the hat’s health.

 

  • Is altering directly the loot tables chances the only way to implement this idea ? It works well for all mob instances, but again, this feels like a huge hack, and above else, a pretty unsafe one.

 

9-10) I’ve made a “stealth hat” that would avoid detection from creatures and preys alike, with the “notarget” and “scarytoprey” tags. However, this is way too powerful, since I don’t want all mobs to be fooled by it. I want it to be working on every mob except bosses, tentacles and etheral creatures (shadow/gesalts). I was wondering how the shadow creatures would not attack the player with the ancient fuelweaver items and I noticed a special tag with “shadowdominance”. Therefore, I have two questions :

 

  • How can I manage specific cases for the mentioned monsters without checking what is around the player at X frames ?

 

  • If this is manageable with tags, how do I implement it ? Where can I put a new behavior on an existing monster based on a new tag ?

 

11) I’ve made a hat that give the wearer an “aura”, and I’ll call that a “self-aura”. I’ve seen Ultroman’s tutorial but I used in the end the existing component instead, because I don’t like the idea to run a loop each X frames, performance wise. Of course, if the existing component is not enough for my idea, I would have to resort to that solution, hence why I need to test it. But here’s the catch : my mod is under development and I’m making something that has effect on others. Hence my question.

 

  • Is there a way you can test self-auras when playing in singleplayer, or do you need to test it when playing with others in a sort of “beta test” ?

 

Code related

Things below gets a bit technical, so it’s mainly for people with experience in Object-Oriented Programming.

12-14) I’ve made 10 hats. In scripts terms, I have 10, for each one, that are fully functional but not cleaned yet. However, as you can suspect, I have lots of duplicating code, which is not to my liking. But I also did this because I want to avoid making a megafile like hats.lua.

In my usual environment (C++ or C#), I would make an abstract class with children that inherit from it, but I struggle with Lua, since it’s more lenient than other languages, and especially on how it is integrated in the game. I know classes are possible in Lua ; most, if not all, components in the game are classes. But I fail to see how it works, since everything is “returned” at the end of file, and I could not find any “new” instructions in the rest of the code for those objects. Therefore, I have several questions on that one.

 

  • Where can I find “crucial scripts”, that constitute the core of the game ? I tried to find where the AddComponent was managed but I failed to see a lua file that does it.

 

  • When applied for DST, is making a abstract class a good idea for refactoring and making the common grounds for my hats or is it too much a hassle ?

 

  • If the refactoring is possible, how do I instantiate it, in the context on how DST was made ? Would it be better to implement it as a component instead ?

 

Networking

I have few, to none, experience in networking programming, so I'll take all advice on that matter.

15) I saw in scripts that DST use « replicas ». I assume this is for networking, since a client needs to replicate things sent by a server to synchronize the game’ state. For my character, the buffs applied when wearing a hat are only done when wearing them.

 

  • Do I need to worry about replicas for my character ?

 

Art

16) I used the Extended Sample Character Template to start working on my mod. For my first mod, this was the best way to get introduced in modding for DST, since it contains lots of stuff to analyze and understand. However, I noticed that esc's proportions might be an issue (it's a cat with huge ears), since I want to make a human character. Ideally, I would like to base it on Wendy, since I’m no artist and since she already has lots of facial features that would only require editing. However, no exported folder exists for Wendy in the game (obviously), only her atlas. So here is my question :

 

  • Do we (as players/users) have access to the « exported » elements of an existing character somehow, or do we need to extract all of them with some dedicated tools (like Krane) ?

 

Misc.

17) This one is about asking more question if needed on this forum. Since I already made a long and dedicated post to it...

 

  • Will it be best to post one single question by thread, or continuing on this one ?

 

18) I like to print logs when testing if everything work correctly. However, prints are sent into the logs, which is kinda inconvenient sometimes. I’ve found TheNet:SystemMessage in that purpose, but you are limited with around 6 or 7 lines. So my question is the following.

 

  • How do I write inside the in-game console, the same console you see when entering commands like c_spawn() ?

 

19-20) This question is more for someone from Klei, but if anyone can answer, I’ll be happy too.

 

  • How can I link Visual Studio to a mod project I make for DST ? Not having an API is tough, and auto-completion would greatly help me to know what can I use when I’m handling any object when coding. I’ve read somewhere that some people at Klei “still works” on Visual Studio, and I’m interested to know how you manage that.

 

  • If you cannot answer that question for corporate issues (that I would understand of course), how can I attach Visual Studio’s debugger to DST process, if possible ? The idea behind this is again to see what I’m manipulating, and help me debug faster when something goes wrong. It’s been a while since I’ve coded without an IDE so things can be really rough sometimes when something went awry.

 

 

TL; DR

 

Spoiler

General

1) Since the Forge/Gorge events are gone, do I need to take them in consideration when coding my character ?

2) How can prevent player from unequipping a hat ? Is anything already exists for that purpose, or do I need to remove any listeniner for any “unequip” events ?

3) Do I need to worry about the “torchranout” event for my “miner-hat” like or can I get rid of it ? Is that event needed for the good behavior of the game ?

 

Core idea : Hats

4) How can I implement a new gauge mechanic ? Is implementing this as a component the best way to do it ?

5) Is removing the “cooker” tag out of the cooker component a good way to do disable it without losing its purpose ? Since no method exists in cooker in that purpose, it feels like a hack.

6) How can I spawn more shadow creatures, “like the nightmare amulet” as said in the wiki, despite the fact there’s nothing in code that indicates so ? If the spawn rate is truly changed, where can I find the code responsible for this ?

7) Can I make a hat breakable without the armor component ? I want a finite number of hits taken for hp, instead of damage reduction/power’s hit.

8) Is altering directly the loot tables chances the only way to make loot chance higher ? It works fine but feels like a huge hack, and above else, a pretty unsafe one.

9) How can I manage a “no target” behavior only for specific monsters, without checking what is around the player at X frames ?

10) If the above is manageable with tags, how do I implement it ? Where can I put a new behavior on an existing monster based on a new tag ?

11) Is there a way you can test self-auras when playing in singleplayer, or do you need to test it when playing with others in a sort of “beta test” ?

 

Code related

12) Where can I find “crucial scripts”, that constitute the core of the game ? I tried to find where the AddComponent was managed but I failed to see a lua file that does it.

13) When applied for DST, is making a abstract class a good idea for refactoring and making the common grounds for my hats or is it too much a hassle ?

14) If the refactoring is possible, how do I instantiate my new hats, in the context on how DST was made ? Would it be better to implement it as a component instead ?

 

Networking

15) In my character context, getting buffs/debuff with hats, do I need to worry about replicas ?

 

Art

16) Do we have access to the « exported » elements of an existing character somehow, or do we need to extract all of them with some dedicated tools (like Krane) ?

 

Misc

17) In the future, if I have further questions, will it be best to post one single question by thread, or continuing on this one ?

18) How do I write inside the in-game console, the same console you use to enter commands like c_spawn() ?

19) How can I link Visual Studio to a mod project I make for DST ?

20) If you cannot answer the question above, how can I attach Visual Studio’s debugger to DST process, if possible ?

 

Thanks in advance for any answer I'll get !

Edited by Nelzanger
Fixed typos
  • Like 2
Link to comment
Share on other sites

Wow, that's a lengthy one.

1. The Forge and The Gorge sure are gone officially but there are still mods that implement these modes back into the game. Generally if you don't intend for your character to work with these modes it's not necessary.

2. An easy way of doing that would probably be by adding 'onunequip' event listener and reequipping the item whenever a player tries to unequip it. If you want it to look a bit more fancy you'd have to edit actions (actions.lua), specifically 'ACTIONS.UNEQUIP.fn' and 'ACTIONS.UNEQUIP.strfn'

3. Using Notepad++ I searched in the games code to find any instance of ListenForEvent("torchranout") and didn't find anything so can't really say what it's used for but if it is used to say "My torch ran out!" it's shouldn't be a big deal.

4. Adding a new meter should definitely be done with a component. Other than that you'll probably need a component replica and a widget.

5. Instead of just removing the tag and adding it back you could remove the entire component and then add it back. This way it would probably be more clean.

6. The rate of shadow creatures spawning is handled in shadowcreaturespawner.lua at line 184.

7. If you don't want your hat being an 'armor' use 'fueled' instead.

8. With how Don't Starves loot tables work I don't think it's possible to increase drop rates without a lot of coding and many adjustments to how the loot is generated. You could theoretically AddComponentPostInit and override GenerateLoot() from the lootdropper component to increase drop chances when a player has the luck hat but as I said it would require some work.

9. Making a stealth hat working for every mob might be impossible without some heavy modifications to the games code. Tags probably won't cut it. The best you can do is add the 'hiding' state tag which should give the best, but not a perfect, result.

10. ^^^

11. You can spawn player prefabs using the console. Since actual players are just prefabs that can perform actions by a human the 'empty' one should give the same result.

12. I don't know what text editor you're using but in Notepad++ there is a function to search for a term/word through entire folders. That way you can find where the function comes from. AddComponentPostInit all well as AddPrefabPostInit, AddClassPostConstruct are in modutil.lua. However some functions used in the lua part of the game come from the games engine, which is written in C++. These cannot be accessed but there is a list of them here.

13. I'm not too experienced with Object-Oriented coding languages but usually when making a lot of prefabs that share the same 'ground' you can create a common_fn with all the code that would be used in EVERY hat and then create specific fn's, combine them and return the prefabs.
Example:

local function common_fn(anim, bank, build, tags, cookable)
    local inst = CreateEntity()

    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    inst.entity:AddNetwork()
	
    inst.AnimState:SetBank(bank)
    inst.AnimState:SetBuild(build)
    inst.AnimState:PlayAnimation(anim)
	
	MakeInventoryPhysics(inst)
	
	if tags then -- You might need specific tags
		for i, v in ipairs(tags) do
            inst:AddTag(v)
        end
	end
	
	inst.entity:SetPristine()
	
	if not TheWorld.ismastersim then
		return inst
	end
	inst:AddComponent("inventoryitem")-- Add different components and such

	if cookable then -- You might need a specific component
		inst:AddComponent("cookable")
	end
    return inst
end

local function special_fn1()
    local inst = common_fn(specialAnim1, specialBank1, specialBuild1, tags1, iscookable)

	if not TheWorld.ismastersim then
		return inst
	end
	
	-- Unique code for special_1
	
    return inst
end

local function special_fn2()
    local inst = common_fn(specialAnim2, specialBank2, specialBuild2, tags2, iscookable)

	if not TheWorld.ismastersim then
		return inst
	end
	
	-- Unique code for special_2
	
    return inst
end

return Prefab("common/inventory/item1", special_fn1, assets1, prefabs1),
	Prefab("common/inventory/item2", special_fn2, assets2, prefabs2)

14. ^^^

15. Haven't looked into replicas too much yet but I think nothing 'onequip' needs replicas.

16. Creating assets for mods, especially in Spriter, is a nightmare I'll tell you that. From what I remember you have to seek all of the anim.bin files that contain animations for player actions (there's a lot of them), or just pick one, preferably idle, and combine them with the characters build.bin and atlas.tex. Put them in one folder and then use ktools.

17. I think creating a new thread for a new problem is better since it also allows other people with similar problems find a specific thread and follow it. So unless you're creating a thread containing a guide to modding with multiple categories it's best to ask for help with a new thread.

18. A simple print() prints text in the console, sometimes... It might not always work whether the print is used in a specific place. I'm not sure, never looked into it. I usually use TheNet:Announce() since it sends a message to the server that is shown at the top on the screen, kinda like the death announcement.

19. -

20. -

 

Here you go. Hope this helps you in your modding adventure.

  • Like 1
Link to comment
Share on other sites

Thanks for the answers. It definitely nudges me in a good direction. Obviously, anyone else can give their input on the subject and I'll gladly take any advice. I'll just react on specific points.

 

10 hours ago, -t- said:

2. An easy way of doing that would probably be by adding 'onunequip' event listener and reequipping the item whenever a player tries to unequip it. If you want it to look a bit more fancy you'd have to edit actions (actions.lua), specifically 'ACTIONS.UNEQUIP.fn' and 'ACTIONS.UNEQUIP.strfn'

I'll check about this solution, since I tend to apply effects when equipping hats, and clean effects when unequipping. Forcing to reequip them may lead to weird behavior, so I'll take an extra look to the file you mentioned.

 

10 hours ago, -t- said:

6. The rate of shadow creatures spawning is handled in shadowcreaturespawner.lua at line 184.

I've quickly checked the file and it seems I cannot alter the spawn rate without inducing insanity like the amulet : I wanted to increase the spawn rate only if the player goes insane. I'll think about reworking that aspect, and test if inducing insanity like the amulet would be a good workaround working well with the gauge mechanic.

 

10 hours ago, -t- said:

11. You can spawn player prefabs using the console. Since actual players are just prefabs that can perform actions by a human the 'empty' one should give the same result.

I didn't know it was possible ! I thought they needed to be player controlled. How do you perform that ? Is c_spawn("wortox") is for example enough ?

 

10 hours ago, -t- said:

12. I don't know what text editor you're using but in Notepad++ there is a function to search for a term/word through entire folders. That way you can find where the function comes from. AddComponentPostInit all well as AddPrefabPostInit, AddClassPostConstruct are in modutil.lua. However some functions used in the lua part of the game come from the games engine, which is written in C++. These cannot be accessed but there is a list of them here.

I'm using Notepad++, but as I stated later on this topic, my coding tool is Visual Studio, and sadly, I'm too accustomed to its powerful features, hence why going back on Notepad++ is a bit tough. I didn't know there was a function to search for a term/word though entire folders so I'll check that feature with great interest. Also thanks to point out modutil.lua, I hadn't look it since my character does not use those functions yet, but now would be the right time to do that, especially for the AddComponent method. I guess we cannot access C++ code without decompiling the generated binaries, so I'll gladly take the link you gave to me : it's exactly what I wanted to get my hands on.

 

10 hours ago, -t- said:

13. I'm not too experienced with Object-Oriented coding languages but usually when making a lot of prefabs that share the same 'ground' you can create a common_fn with all the code that would be used in EVERY hat and then create specific fn's, combine them and return the prefabs.

I knew this would be a tricky one : I thought about coding it this way, since it's used in hats.lua, but the biggest issue I have with that is that it will create a megafile, and breaks some SOLID principles, which is an issue to me, for maintainability and scalability reasons. Hence why I was referencing to abstract classes (virtual pure in C++), inheritance, and interface segregation if applicable.

OOP brings lots of design patterns and problems to solve about code architecture, which I tend to value more than a code that "just works". To be frank, I'm not forced to do it that way but it would be really nice if I could code it in a clean and maintainable way, especially if I consider adding more hats in the future.

In the end, the main issue for me on that matter is how everything is "returned" at the end of files : I don't understand how prefabs created that way are registered and kept into memory to be used later by the game, which prevents me to truly understand on how the whole game works, especially when it needs to communicate with the C++ engine. I think I should delve deeper into Lua's documentation on that subject but I'm scared of this specific issue might be an exception due to how the game was made.

 

11 hours ago, -t- said:

16. Creating assets for mods, especially in Spriter, is a nightmare I'll tell you that. From what I remember you have to seek all of the anim.bin files that contain animations for player actions (there's a lot of them), or just pick one, preferably idle, and combine them with the characters build.bin and atlas.tex. Put them in one folder and then use ktools.

I didn't used Spriter yet, except for renaming animations for the new hats but it sure sounds dreadful... I guess I asked for it. I'll make sure to carefully follow tutorials you can find in the modding forums.

 

11 hours ago, -t- said:

18. A simple print() prints text in the console, sometimes... It might not always work whether the print is used in a specific place. I'm not sure, never looked into it. I usually use TheNet:Announce() since it sends a message to the server that is shown at the top on the screen, kinda like the death announcement.

In my case, prints are not displayed in the in-game console, only in the server-logs, which is annoying when I want to track debug logs in real time. As I said, I use TheNet:SystemMessage in that purpose, which is fine, but limited. I'll keep in mind the TheNet:Announce() though : it can gives me an extra "real-time" debug line, and might come in handy for specific cases I thought about.

  • Like 1
Link to comment
Share on other sites

Quote

I didn't know it was possible ! I thought they needed to be player controlled. How do you perform that ? Is c_spawn("wortox") is for example enough ?

Yes, c_spawn(prefab) works with player prefabs.

 

Quote

I knew this would be a tricky one : I thought about coding it this way, since it's used in hats.lua, but the biggest issue I have with that is that it will create a megafile, and breaks some SOLID principles, which is an issue to me, for maintainability and scalability reasons. Hence why I was referencing to abstract classes (virtual pure in C++), inheritance, and interface segregation if applicable.

OOP brings lots of design patterns and problems to solve about code architecture, which I tend to value more than a code that "just works". To be frank, I'm not forced to do it that way but it would be really nice if I could code it in a clean and maintainable way, especially if I consider adding more hats in the future.

In the end, the main issue for me on that matter is how everything is "returned" at the end of files : I don't understand how prefabs created that way are registered and kept into memory to be used later by the game, which prevents me to truly understand on how the whole game works, especially when it needs to communicate with the C++ engine. I think I should delve deeper into Lua's documentation on that subject but I'm scared of this specific issue might be an exception due to how the game was made.

It is possible to break down the 'megafile' if you really want. Create one file for the main code used in all of the hats as a function and return it:

local function MakeCustomHat(name)
	local fname = "hat_"..name
	local symname = name.."hat"
	local prefabname = symname

	local inst = CreateEntity()

	-- code

	local function onEquip(inst, owner)
		-- code
	end

	local function onUnequip(inst, owner)
		-- code
	end

	inst:AddComponent("inventoryitem")

	inst:AddComponent("inspectable")

	inst:AddComponent("tradable")

	inst:AddComponent("equippable")
	inst.components.equippable.equipslot = EQUIPSLOTS.HEAD
	inst.components.equippable:SetOnEquip(onEquip)
	inst.components.equippable:SetOnUnequip(onUnequip)

	-- code

	return inst
end

return MakeCustomHat

Then create a separate file for a specific hat and use 'require' to get access to the main function:

local MakeCustomHat = require "scripts/custom_hat" -- or whatever your MakeCustomHat file is called

local function fn()
	local inst = MakeCustomHat("lucky_hat")

	-- specific code for lucky hat

	return inst
end

return Prefab("lucky_hat", fn, assets, prefabs)

 

  • Like 1
Link to comment
Share on other sites

On 8/28/2021 at 5:40 PM, -t- said:

It is possible to break down the 'megafile' if you really want. Create one file for the main code used in all of the hats as a function and return it

Thanks a lot for sharing this. Even if it's not the optimum way I would like to implement it, it definitely something that I was looking for.

  • Like 1
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...