Jump to content

[ANSWERED] Trying to make a simple mod to help me learn modding in DST


Recommended Posts

I am new to modding and LUA for that matter, but I have quite a bit of experience with programming as a whole (java, C#, python, etc.)

I wanted to try modding DTS and just change some simple things to get started, but I am really struggling to find the resources I need. By trying to pick apart downloaded mods, not many of them are very well commented and I am not really making any progress. I am to the point where I have figured out how to create a mod, create an icon for the mod, and get it to start in game without it crashing (most of the time). Some things I've tried that I found on other posts like "TheNet:SystemMessage("Test Message", false)" straight up crashes my game.

The mod I am trying to create simply checks if the player is "Webber" and when he eats "Monster Meat", he gains health, sanity, and hunger back. For simplicity, let's just say it is 20 of each.

Can someone point out where the documentation is for modding this game? If someone wants to take a shot at making this simple mod, I tend to learn best by reading code, so that would be very helpful to me. Thanks in advance!

Edited by ItsMrEnergy
Questions were answered
Link to comment
Share on other sites

I've been at it for a few hours and I have figured out this much:

local function BetterMonsterMeatInit(prefab)
    prefab.components.edible.hungervalue = 20
    prefab.components.edible.healthvalue = 20
    prefab.components.edible.sanityvalue = 20
end
AddPrefabPostInit("monstermeat", BetterMonsterMeatInit)

print("Webber's Monster Feast loaded!") -- testing the make sure the mod is loading by checking the client logs.

This makes monster meat have +20 health, hunger, and sanity. I figured this out by going through the game's source code... which I had no idea I had access to. Still can't find anything online to help me identify what character the player is playing as. Any help would be appreciated. 

Link to comment
Share on other sites

5 hours ago, ItsMrEnergy said:

local function BetterMonsterMeatInit(prefab)
    prefab.components.edible.hungervalue = 20
    prefab.components.edible.healthvalue = 20
    prefab.components.edible.sanityvalue = 20
end
AddPrefabPostInit("monstermeat", BetterMonsterMeatInit)

A warning, some things are only supposed to be server side, most components especially, so this will crash if you're not the host and/or host with Caves. You might have noticed in the source code (which is easily accessible because Lua doesn't need compiling) that Klei often puts this somewhere in a prefab's constructor function:

if not TheWorld.ismastersim then
	return inst
end

That means everything past that point is server only. In your case you'll have to write GLOBAL.TheWorld instead of just TheWorld, because you're in the modmain environment. It's a different environment than what the game uses, but all the global variables are inside the GLOBAL table.

To address your question, though:

5 hours ago, ItsMrEnergy said:

Still can't find anything online to help me identify what character the player is playing as

Instead of identifying what character the player is playing as, think of it as the player is playing a character that is also a prefab like any other. So if you want to change something about Webber, you can use what you already learned and do:

AddPrefabPostInit("webber", function(inst)
	-- Your code here
end)

I'm a little busy right now, so I can't guide you further with what you wanted to do with Webber, but if you're still in need of help later I'll check the thread again in a bit! Hope this helped :)

  • Thanks 1
Link to comment
Share on other sites

If your making a Webber mod can you add thisC8CD3865-42D4-47BF-B7BD-18FF287D719A.thumb.webp.cdc1e77eb11e8b43cc4bb44683a566ac.webp

Built in (very important it help bridge the gap between lore(an example of this is shown in the indigestible section of the description depicted in the picture above) and game) :

Dear Klei entertainment ever sense the webber rework came out I’ve noticed that one thing is missing and that thing is a small but noticeable sanity  drain when befriended spiders die it doesn’t even need to increase by the spider like being in range of befriended spiders dying drains not spider by spider and if that’s to much sanity loss then preferred he can gain sanity around decorated dens like how regular characters lose sanity around normal dens , grain sanity around alive befriended spiders like how regular characters lose around normal spiders, or both and you send yourself that the point of the reworks are to make the characters more like themselves so please make this change it would make a world of difference I love your game and this community and thank you(depending on whether or not there just considered recruits/expendable pack cows(in the case of beefalo) or friends this could also extend to all befriended mobs)

just to clear things up the reason it’s addressed to Klei is I pulled it from my Webber skill tree tread  

Edited by Dr.Webber
Link to comment
Share on other sites

On 11/27/2023 at 2:44 AM, ClumsyPenny said:

A warning, some things are only supposed to be server side, most components especially, so this will crash if you're not the host and/or host with Caves. You might have noticed in the source code (which is easily accessible because Lua doesn't need compiling) that Klei often puts this somewhere in a prefab's constructor function:

if not TheWorld.ismastersim then
	return inst
end

Thank you so much for responding! I hope you can answer a couple other questions for me. The information you provided has been helping a lot!

Where are the constructors defined for players or items in the data file? Are they their own files or are they all looped into one larger file? All my references are from wikis and mods I have taken apart to analyze for some sort of help. Then it's a stab in the dark and many crashes later to find out what a prefab is actually named and how to change it. I also have no ideas what functions or values exist within those files or if they extend a type of constructor or not. For instance, let's look at "monstermeat", aside from knowing that you can eat it in game, how would I have found that is was a component of type edible and what values it holds. I can only assume that all edible prefabs have the same variables that have unique values to their "object", but what about other components? I feel like I am missing something major here.

I have gotten to the point that I understand that this: 

AddPrefabPostInit("monstermeat", BetterMonsterMeatInit)

is some sort of function (method) that searches for the prefab by name (I am assuming a tag name based on a couple mods that I have looked at) based on the first argument and calls the function in the second argument function. Also you can create an internal function within the same code like this

AddPrefabPostInit("mostermeat", function(inst)
	-- basically the function (method) is created here.
end)

So if I wanted to modify something about a prefab, then I should be able to do it here... assuming I know anything about it. This is where I start trying things and hoping for results. for instance, I tried prefab.components.edible.heartvalue = 20 before I tried prefab.components.edible.healthvalue = 20 because I had nothing to reference for the prefab of "monstermeat".

I found one mod that changed the initial stats of "webber" my modifying the tuning.lua file. At least I think that's what they are doing here.

-- modinfo.lua
configuration_options =
{
	 OptionTitle("WEBBER"),
{
		name = "BT_Hunger",
		label = "Webber Hunger",
		hover = "Golod Webbera",
		options =
		{
			{description = "Few", data = 75, hover = "75"},					
			{description = "Less", data = 125, hover = "125"},			
			{description = "Standard", data = 175, hover = "Standard for DST"},			
			{description = "More", data = 225, hover = "225"},		
		},
		default = 175
	},
-- bunch of other configs...
}
-- modmain.lua
TUNING.WEBBER_HEALTH = GetModConfigData("BT_Health")

This works assuming that I might want to change a constant like starting health, sanity, hunger, etc., but doesn't help much after this initial startup.

I also saw some mods that use something like this: 

inst:ListenForEvent("stopfollowing", onstopfollowing)

Is there a location I could find all the possible events too? Sorry for all the questions, I've been looking all over for the answers and feel like I am missing a very obvious thing somewhere...

On 11/27/2023 at 6:19 AM, Dr.Webber said:

If your making a Webber mod can you add this

Let me figure out the basics first then I'll see if there is anything I can do about it.

Link to comment
Share on other sites

9 hours ago, ItsMrEnergy said:

Sorry for all the questions, I've been looking all over for the answers and feel like I am missing a very obvious thing somewhere...

Don't worry, this is what this forum is for! We all have to start somewhere and you seem very eager to learn, which is a good thing!

 

 

9 hours ago, ItsMrEnergy said:

Where are the constructors defined for players or items in the data file?

All the game's code is in this directory on Windows: "Steam\steamapps\common\Don't Starve Together\data\databundles\scripts.zip\scripts", not sure about other operating systems.

For prefab files, go to the "prefabs" folder and you'll see all of them. Each file in this folder constructs some kind of prefab, sometimes even multiple! Keep in mind the name of the file won't always match up with the name of the prefab itself.

Anyway, if you open one of these files, you'll notice that most of the time at the bottom it will say something along the lines of:

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

Prefab is a function that creates the prefab, the arguments are:

  • String for the prefab name: Must be unique, this is what is used to refer to the prefab (spawning it in, AddPrefabPostInit, etc.)
  • Constructor function: This function constructs the prefab, it must return the instance of the prefab (I'll explain it below)
  • Table of assets: Technically optional, but this lets the game know what assets to use
  • Table of prefabs related to this prefab: I don't fully understand this one, I think it's for optimization and it's also optional, so ignore it for now

A prefab file can return as many prefabs as it wants, but most of the time it'll be just one. You might also find MakePlacer instead of Prefab, that's for the green/red structure that appears when you try building something. I won't cover it for now.

9 hours ago, ItsMrEnergy said:

I also have no ideas what functions or values exist within those files or if they extend a type of constructor or not.

If you refer to the constructor function, this is where all the attributes of a prefab are added. Most of the time the instance (inst) of a prefab starts as a blank slate, in fact the first line is almost always this:

local inst = CreateEntity()

You don't have to call it inst, but Klei almost always does it this way for consistency. From this point onwards, a bunch of things are added to inst. AddTransform for an entity to occupy coordinates in the world, have a rotation, etc. and AddAnimState to handle animations are very common engine functions that you'll almost always see added to a prefab, for example.

By the way, I mentioned an engine. Most of the game is in Lua, but the core engine is in C++, which isn't available to us modders.

9 hours ago, ItsMrEnergy said:

For instance, let's look at "monstermeat"

Just a quick tangent, since it will be useful in the future: "monstermeat" is created in prefabs/meats.lua, it's a file that creates all meat prefabs since they share some similarities, but it might be a bit more difficult for a beginner to read.

If in the future you want to find where a prefab is created or other stuff, I recommend using Notepad++'s "Search in Files" feature, I use it all the time when I'm modding and want to see where something is declared.

 

 

9 hours ago, ItsMrEnergy said:

aside from knowing that you can eat it in game, how would I have found that is was a component of type edible and what values it holds.

Components are almost always added to the constructor, but technically they can be added/removed whenever. They will very often be server side only, like I explained in a previous comment, so look for them after that cut-off point of "if not TheWorld.ismastersim then return inst end" in the constructor. The component themselves are defined in their respective files in the "components" folder, this time the files match the component name, bar a few exceptions you shouldn't worry about it now.

Components are a collection of variables and functions, intended to make it easier for prefabs to share the same or similar behaviors. They're also often closely linked with actions, for example the act of eating food.

Components tend to have default values that usually get changed by the prefab when it's constructed. For example, if you refer to the "edible" component, you'll see it has these default values (this isn't all of them, I'm just using them as an example):

self.healthvalue = 10
self.hungervalue = 10
self.sanityvalue = 0
self.foodtype = FOODTYPE.GENERIC

But Monster Meat overrides these default values with:

-- in the function "common()"
inst.components.edible.foodtype = FOODTYPE.MEAT

-- in the function "monster()"
inst.components.edible.healthvalue = -TUNING.HEALING_MED
inst.components.edible.hungervalue = TUNING.CALORIES_MEDSMALL
inst.components.edible.sanityvalue = -TUNING.SANITY_MED

So that it can have its own values. But there's times where the default won't be changed, because it's the default for a reason, so it's important to know how to look for it.

You might also notice the values given here are reference in a global TUNING table. This is declared in "scripts/tuning.lua", it's a collection of constants the game uses, so that they're easier to change both for developers and modders.

 

 

9 hours ago, ItsMrEnergy said:

I have gotten to the point that I understand that this: 

AddPrefabPostInit("monstermeat", BetterMonsterMeatInit)

is some sort of function (method) that searches for the prefab by name (I am assuming a tag name based on a couple mods that I have looked at) based on the first argument and calls the function in the second argument function.

AddPrefabPostInit is a function specifically for modders. It's defined in "scripts/modutil.lua", among many other functions. This one specifically lets you modify the prefab you name in the first argument after it's initialized, meaning after it's constructed with the constructor function. It's a very useful tool to mod existing prefabs.

 

 

9 hours ago, ItsMrEnergy said:

I also saw some mods that use something like this: 

inst:ListenForEvent("stopfollowing", onstopfollowing)

Is there a location I could find all the possible events too? Sorry for all the questions, I've been looking all over for the answers and feel like I am missing a very obvious thing somewhere...

This one is tricky. Events are pushed through PushEvent, letting any entity listening to that event to run a function. Thing is, events can be pushed from anywhere, so I heavily recommend referring to the "Search in Files" method I mentioned earlier to find where the relevant events are pushed.

 

 

Now, to try figuring out your problem. There isn't really cases of a character having an entirely different value for eating a certain food. There's some cases like multipliers or resistance to negative effects like: a veggie multiplier (Wurt), ignoring negative effects from raw Meat or Monster Meat (Webber), less or no stats from food (Wortox, Wormwood) and so on. But these aren't quite what you want, they're not coded to simply assign

But this game is very moddable and there's a lot of approaches we could take. Thankfully, there seems to be a relatively simple solution: the "eater" component has a "custom_stats_mod_fn" value, which is a function that takes the arguments of: the entity with the component in question, health, hunger, sanity, food and feeder and returns the new health, hunger, sanity values to use. Klei isn't using it anywhere, so they very likely made it for modders in mind.

If we were to implement it, I'd take an approach like this:

local function CustomMonsterMeatStats(inst, health, hunger, sanity, food, feeder)
	if food.prefab == "monstermeat" then -- Entities have a .prefab value that is the prefab name, so we make sure this only runs on Monster Meat
		return 20, 20, 20
    end
	
	return health, hunger, sanity -- This is the default option, if the other check doesn't pass, just return the same values that were given
end

AddPrefabPostInit("webber", function(inst) -- We're doing this only for Webber
	if not GLOBAL.TheWorld.ismastersim then -- Don't run the code after this if we're the client, since "eater" doesn't exist on clients
		return inst
	end
	
	if inst.components.eater ~= nil then -- Not necessary, but a good practice. We make sure eater exists to prevent a possible crash if mods remove it
		inst.components.eater.custom_stats_mod_fn = CustomMonsterMeatStats
	end
end)

I haven't tested it, but it should work in theory!!!

 

 

Alright, that should be everything! If you have more questions or need clarification on things I mentioned, don't be afraid to ask! I'm very happy to introduce more people to the wonders of modding DST (even if modding it can be frustrating at times...)

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

12 hours ago, ClumsyPenny said:

Don't worry, this is what this forum is for! We all have to start somewhere and you seem very eager to learn, which is a good thing!

Thank you so much! You are such a blessing to me. The frustrations are real sometimes and you majorly came through for me.

12 hours ago, ClumsyPenny said:

haven't tested it, but it should work in theory!!!

I tested it and it does work. The mod approach is interesting, because technically we are not changing the value of the prefab "monstermeat", but rather what happens when the player eats a food.prefab of this name. I have another mod that I installed to see what the values of the prefab would do when eating it. So it doesn't actually show up on this mod that the monster meat will do what it claims.

Your in code comments really helped me out with understanding this and I think I can take it a few step further towards the modding direction I am interested in.

 

12 hours ago, ClumsyPenny said:

All the game's code is in this directory on Windows: "Steam\steamapps\common\Don't Starve Together\data\databundles\scripts.zip\scripts", not sure about other operating systems.

For prefab files, go to the "prefabs" folder and you'll see all of them. Each file in this folder constructs some kind of prefab, sometimes even multiple! Keep in mind the name of the file won't always match up with the name of the prefab itself.

I have literally looked through this folder... and never saw the prefab webber.lua... I even have used the search to see if I could find it and now I just feel dumb lol... I did find it btw, and now I have pretty much everything I need to complete what I was hoping to learn.

Also, I have been using the scripts folder a lot when modding as this is where I've learned almost everything from aside from your wonderful help!

Thank you so much btw! You really helped me out and I appreciate it greatly!

13 hours ago, ClumsyPenny said:

If in the future you want to find where a prefab is created or other stuff, I recommend using Notepad++'s "Search in Files" feature, I use it all the time when I'm modding and want to see where something is declared.

I use notepad++ but I am unaware of this feature. is this an addon that I can get?

One last question. How do I print to the chat or server in game? I use the print(string) statement for debugging all the time... but I'm tired of looking through the server logs to find out where my code is or isn't executing. I saw something about using this:
 

TheNet:SystemMessage("Message", false)

but it doesn't seem to work... and it just crashed the game instead.

Link to comment
Share on other sites

9 hours ago, ItsMrEnergy said:

The mod approach is interesting, because technically we are not changing the value of the prefab "monstermeat", but rather what happens when the player eats a food.prefab of this name.

Changing the stats of Monster Meat directly would change it for everyone. You could theoretically change the stats when Webber specifically picks up the item and then revert them when he drops it, but it's unnecessarily complicated. It's a lot better to change the values when the game calculates how much you're supposed to gain, since that's how the game works in general (with spoilage, various character multipliers, etc.)

9 hours ago, ItsMrEnergy said:

I use notepad++ but I am unaware of this feature. is this an addon that I can get?

Sorry, it's called "Find in Files...", no addons necessary. You just choose the directory and search something. I recommend copying the scripts folder on your Desktop or something, since you can't search through a zipped folder. It's a bit of a pain because you'll have to replace it from time to time, when an update comes out. I also recommend deleting the "languages" folder inside "scripts", it's unnecessary for modding, makes the search slower and adds useless results to it.

9 hours ago, ItsMrEnergy said:

One last question. How do I print to the chat or server in game? I use the print(string) statement for debugging all the time... but I'm tired of looking through the server logs to find out where my code is or isn't executing. I saw something about using this:
 

TheNet:SystemMessage("Message", false)

but it doesn't seem to work... and it just crashed the game instead.

What's the crash? If you're using that line in the modmain environment, I assume it's just a matter of changing TheNet to GLOBAL.TheNet, because it's a different environment compared to the game. A lot of crashes, especially when you're just starting out, are going to be that.

Edited by ClumsyPenny
Link to comment
Share on other sites

On 11/30/2023 at 2:26 AM, ClumsyPenny said:

Sorry, it's called "Find in Files...", no addons necessary. You just choose the directory and search something. I recommend copying the scripts folder on your Desktop or something, since you can't search through a zipped folder.

Ok great! You have very good suggestions.

On 11/30/2023 at 2:26 AM, ClumsyPenny said:

I assume it's just a matter of changing TheNet to GLOBAL.TheNet,

That was it. Thank you! The crash was the game completely closing without an error message. I'm glad it was such a simple fix!

  • GL Happy 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...