Jump to content

Total Newb Looking for where to start


Recommended Posts

Hello everyone,

I am totally new to the modding scene of DST and have been looking for a general place to start and have been very unsuccessful.  I have a pretty strong background in C# and am no stranger to doing my due diligence on research.  However, I can't seem to find a solid API reference or file structure etc.. One of my biggest issues is that I am not great at looking at existing mods and dissecting them to find what I am looking for.  

Enough about my sob story and get to the point.  My children and I play the game casually and feel there are a few things that need to be changed.  The first being that we think you should be able to make the blowdart (damage ones) with any feather not just the winter birds feather.  I have tried adding the recipe as some other mods do but it then takes away the original recipe and I can't seem to ad more then just one for the same item.  

I can think of a million other things that I would like to do but this would be a great start.  I would greatly appreciate if someone would be willing to show me where to start so I can start getting my feet wet.  

 

 

Link to comment
Share on other sites

15 hours ago, Silisiban said:

Hello everyone,

I am totally new to the modding scene of DST and have been looking for a general place to start and have been very unsuccessful.  I have a pretty strong background in C# and am no stranger to doing my due diligence on research.  However, I can't seem to find a solid API reference or file structure etc.. One of my biggest issues is that I am not great at looking at existing mods and dissecting them to find what I am looking for.

My newcomer post is a good place to start :) It has links to some major tutorial and starter-threads, tips for debugging, a few simple example mods, and tips on how to search the forum effectively, as well.

There's no API reference, as such. There's just us... There is a sadly very lacking unofficial API documentation, but other than that it's just getting your hands dirty with LUA. You simply MUST take at least a beginner's course, because even though it has similar features to many programming languages, its syntax and its concept of "everything is a table" can be very jarring, especially when coming from a type-safe language.

Once you get going, it's not too hard. We have at least 5 beginners coming in here each week, and I think 3-4 of them end up finishing their mod, and while some are never heard from again, some stay and start helping others.

15 hours ago, Silisiban said:

My children and I play the game casually and feel there are a few things that need to be changed.  The first being that we think you should be able to make the blowdart (damage ones) with any feather not just the winter birds feather.  I have tried adding the recipe as some other mods do but it then takes away the original recipe and I can't seem to ad more then just one for the same item.

For duplicating recipes, this guy actually had it running with all the bells and whistles. I'll write a more generic version of the code and post it below. I went ahead and wrote a mod that lets you toggle for each type of blow dart whether it should add duplicate recipes using each of the feathers, and put it on the workshop. I can't not help when there are kids involved :)

Link to the mod on Steam Workshop

Here's the code:

For anyone finding this thread wanting to duplicate recipes, this should work for just about any recipe.
All disclaimers apply :p

If anyone has any constructive criticism, I'd be more than happy to hear it, so I can mend the code.

Spoiler

local function GenerateDuplicateRecipe(original_prefab, unique_suffix, ingredients)
	-- Fetch the original recipe, and make sure it is actually there.
	local orig_recipe = GLOBAL.AllRecipes[original_prefab]
	if not orig_recipe then
		print("A recipe for "..original_prefab.." does not exist!")
		return
	end
	
	-- Create our new unique prefab name, using the unique_suffix.
	local new_prefab_name = original_prefab.."_"..unique_suffix
	
	-- The parameter list for constructing a recipe, which is mirrored by the AddRecipe function:
	-- AddRecipe(name, ingredients, tab, level, placer, min_spacing, nounlock,
	-- numtogive, builder_tag, atlas, image, testfn, product)

	-- Add a new recipe, reusing everything from the original recipe, except the ingredients and name.
	local new_prefab_recipe = AddRecipe(
		new_prefab_name,
		ingredients,
		orig_recipe.tab,
		orig_recipe.level,
		orig_recipe.placer,
		orig_recipe.min_spacing,
		orig_recipe.nounlock,
		orig_recipe.numtogive,
		orig_recipe.builder_tag,
		orig_recipe.atlas,
		orig_recipe.imagefn or orig_recipe.image,
		orig_recipe.testfn,
		original_prefab)
	
	-- To make our recipe not end up at the bottom of the crafting tab, we inherit the sortkey,
	-- but add a little so it appears underneath the original recipe.
	new_prefab_recipe.sortkey = orig_recipe.sortkey + 0.5
	
	-- Duplicate the name- and description-strings of the original recipe.
	local upper_new_prefab = string.upper(new_prefab_name)
	local upper_prefab = string.upper(original_prefab)

	-- These three lines do not really have a function, but I'll leave them in for good measure.
	-- Their original purpose from the other author, was to fill out the NAMES and RECIPE_DESC
	-- using the data from the original recipe, but the crafting tab pop-up actually reads the name
	-- and description using the "product" parameter, which is the name of the original prefab,
	-- so the duplicate recipes will always have the name and description of the original prefab.
	-- You cannot rename or change the description of the duplicate recipe in this way, unless
	-- you do more intrusive code.
	local STRINGS = GLOBAL.STRINGS
	STRINGS.NAMES[upper_new_prefab] = STRINGS.NAMES[upper_prefab]
	STRINGS.RECIPE_DESC[upper_new_prefab] = STRINGS.RECIPE_DESC[upper_prefab]
end

-- Example usage. You can find the original recipes in recipes.lua.
if GetModConfigData("blowdart_pipe") then
	-- Here we generate the duplicates for the blowdart_pipe prefab, using the other three types of feathers.
	GenerateDuplicateRecipe("blowdart_pipe", "feather_canary", {Ingredient("cutreeds", 2),Ingredient("houndstooth", 1),Ingredient("feather_canary", 1) })
	GenerateDuplicateRecipe("blowdart_pipe", "feather_robin", {Ingredient("cutreeds", 2),Ingredient("houndstooth", 1),Ingredient("feather_robin", 1) })
	GenerateDuplicateRecipe("blowdart_pipe", "feather_crow", {Ingredient("cutreeds", 2),Ingredient("houndstooth", 1),Ingredient("feather_crow", 1) })
end

 

 

Edited by Ultroman
Link to comment
Share on other sites

@Ultroman I first just want to say thank you so very much for such a detailed explanation and going out of your way to create something for me

I have already learned quite a bit just at looking at what you have done.  I do indeed don't understand lua very well but it doesn't seem like its to hard to pick up and grasp the general concept of it.  So I have taken the work you did and tried making a little practice thing (HUGE THANKS ON LEARNING DEBUG STATEMENTS) and I'm hoping you can explain something to me here.  I didn't write any functions or do anything crazy yet just more of a testing like scenario.

		AddRecipe("Black_Dart",
		{Ingredient("cutreeds", 2),Ingredient("twigs", 1),Ingredient("feather_crow", 1)},
		orig_recipe.tab,
		orig_recipe.level,
		orig_recipe.placer,
		orig_recipe.min_spacing,
		orig_recipe.nounlock,
		orig_recipe.numtogive,
		orig_recipe.builder_tag,
		orig_recipe.atlas,
		orig_recipe.image,
		orig_recipe.testfn,
		original_prefab)
		
		local STRINGS = GLOBAL.STRINGS
		STRINGS.NAMES["BLACK_DART"] = "Black Dart"

This does indeed get the recipe in the tab and working just as I would expect.  However, the name doesn't change.  If I remove the last line in the AddRecipe function then it shows up with the correct name but I can't craft it.

Link to comment
Share on other sites

You're welcome :) I'm happy you can use it.

I'm not entirely sure what you're doing. Is this an alteration to my code? Or did you cut out the AddRecipe part to a separate call? If the latter, do you have orig_recipe declared somewhere? Which prefab is it?

In order to learn more, you'll need to look at two things...mostly one thing, but the other thing is important to know. You need to know what AddRecipe does, and what it does with all its many parameters. The answer is...not too much we should care about, but it's important engine stuff. You can find it in the modutils.lua.

env.AddRecipe = function(arg1, ...)
	initprint("AddRecipe", arg1)
	require("recipe")
	mod_protect_Recipe = false
	local rec = Recipe(arg1, ...)
	mod_protect_Recipe = true
	rec:SetModRPCID()
	return rec
end

Basically, it does engine-magic, but for our purposes, it's important to see that it calls Recipe() internally, and that it passes along all the parameters given to the AddRecipe function using ... The only reason why arg1 is written out, is because that's the prefab name, and they wanted to print it. The rest of the parameters are summed up into ...

Now, the Recipe "constructor" in recipe.lua in the game files looks like this:

Recipe = Class(function(self, name, ingredients, tab, level, placer, min_spacing, nounlock, numtogive, builder_tag, atlas, image, testfn, product)
    if mod_protect_Recipe then
        print("Warning: Calling Recipe from a mod is now deprecated. Please call AddRecipe from your modmain.lua file.")
    end

    self.name          = name

    self.ingredients   = {}
    self.character_ingredients = {}
    self.tech_ingredients = {}

    for k,v in pairs(ingredients) do
        table.insert(
            (IsCharacterIngredient(v.type) and self.character_ingredients) or
            (IsTechIngredient(v.type) and self.tech_ingredients) or
            self.ingredients,
            v
        )
    end

    self.product       = product or name
    self.tab           = tab
    
    self.imagefn       = type(image) == "function" and image or nil
self.image         = self.imagefn == nil and image or (self.product .. ".tex")
    self.atlas         = (atlas and resolvefilepath(atlas))-- or resolvefilepath(GetInventoryItemAtlas(self.image))

    --self.lockedatlas   = (lockedatlas and resolvefilepath(lockedatlas)) or (atlas == nil and resolvefilepath("images/inventoryimages_inverse.xml")) or nil
    --self.lockedimage   = lockedimage or (self.product ..".tex")

    self.sortkey       = num
    self.rpc_id        = num --mods will set the rpc_id in SetModRPCID when called by AddRecipe()
    self.level         = TechTree.Create(level)
    self.placer        = placer
    self.min_spacing   = min_spacing or 3.2

    --V2C: custom test function if default test isn't enough
    self.testfn        = testfn

    self.nounlock      = nounlock or false

    self.numtogive     = numtogive or 1

    self.builder_tag   = builder_tag or nil

    num                = num + 1
    AllRecipes[name]   = self
end)

That is a LOT of stuff, but most is just setting the variables on the recipe using the parameter values. The ingredients are sorted into categories, and there's some funkiness about the image (which you seem to have cut from your copy/paste of my code :o ), but you need to look at what it does using the "name" and "product" parameters.

If you give it a "product", then the product of the recipe is set to be "product". If you leave out product, then it will default to using the "name" you've provided as the product. In your case of not giving it a product, you are providing a "name" that does not correspond to an existing prefab. Indeed, that is also what I'm doing in my code, BUT I provide an existing prefab to be the "product".

Then the product is used to find out which image to use. You might see the crafting image is missing (it should be) if you do not supply a product. After this, "name" (your fake prefab name) is only used as the key when placing the new recipe in the AllRecipes dictionary. This "name" parameter IS NOT used to set which name is displayed on the recipe. It is merely an identifier. What's shown in the crafting tab isn't even the variable you set here (I actually don't know why it's there...I just copied that part from the other guy).

The code in the recipepopup.lua uses the recipe like this when filling out the name and description for the crafting recipe (in its Refresh() function):

self.name:SetTruncatedString(STRINGS.NAMES[string.upper(self.recipe.product)], TEXT_WIDTH, self.smallfonts and 51 or 41, true)
self.desc:SetMultilineTruncatedString(STRINGS.RECIPE_DESC[string.upper(self.recipe.product)], 2, TEXT_WIDTH, self.smallfonts and 40 or 33, true)

As you can see, it uses the product, which is the real prefab. You can change it so it uses self.recipe.name instead. But it's probably not worth it. Who knows which other recipes may change names, as well. Also, no matter what, you would have to extend some functions in builder, if you want to change the name and description of the item you actually gain from crafting, since the "product" is still the original prefab, which is not linked to the strings we set. Again, there's no function to those strings. There may have been when the guy wrote his code, though.

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...