Understanding Lua

Place to get help with not working mods / modding interface.
Aethyrium
Burner Inserter
Burner Inserter
Posts: 6
Joined: Mon Feb 09, 2026 8:55 pm
Contact:

Understanding Lua

Post by Aethyrium »

Hello friends!

-- Needless exposition
I am a long (long... long...) time Factorio player, but very new to modding. And by very new, I mean I like just started this week (and this is my first real foray into programming). And that journey started with learning how Lua works. So I ran through the Lua crash course on Codecademy, after which I felt like I had a fair rudimentary understanding of the language - enough that when I looked at other code, I felt confident I roughly (emphasis: "rough" - based on complexity of code) understood what was happening. So today I jumped right in to execute what I felt like a was a fairly basic idea for a mod (modifying a base recipe) just to put skills to the test. Many problems later, and a couple of hours later, my mod functions and does what I want it to do. Yay!

While I'd like to do some expanding to my mod, now that I have a portion of it working, I'd like to understand some things, and improve my coding here so I can establish good habits early. So...

-- My Mod
At the most basic, I wanted to add a teeny bit of complexity while preserving default ratio to the transport belt cost - also I wanted to increase general use of iron sticks. My answer was to add 2 sticks to the transport belt recipe, increase gears to 2, and produce 4 belts instead of two. This is where I landed, and it works - it's tested in game and functions (let's ignore that sticks require Electric Energy Distribution 1... I'll figure out how to fix that later / if I keep working on this particular project).

Code: Select all

data.raw.recipe["transport-belt"].ingredients =
	{
		{type = "item", name = "iron-plate", amount = 1},
		{type = "item", name = "iron-gear-wheel", amount = 2},
		{type = "item", name = "iron-stick", amount = 2},
	}
data.raw.recipe["transport-belt"].results = {{type="item", name="transport-belt", amount=4}}
-- The Questions
Of which, I have three... point five(ish) half a dozen. Specific questions are bolded.

First, is that while trying to figure out how to do this, and troubleshooting the various errors I had, I came across this thread/solution to the problem: viewtopic.php?t=40209

In this example mod, the code they use is very small:

Code: Select all

data.raw.recipe["transport-belt"].ingredients =
{
	{"copper-plate", 1},
	{"iron-gear-wheel", 2}
}
And boy did I try to make that work. I kept getting errors (something something dictionary ROOT.data.raw... I broke quality (actually recycling according to the error) at one point. It was wild). Now, once I found a mod on the portal that did something similar to mine (basic recipe tweaks... which I am thinking can be done multiple ways depending at what stage you do it at, but this seems beyond me at the moment), I saw that they were using the full breakout like I am using in my 'finalized' mod. Which is what I thought to do initially, and had I done, would have saved me like at least an hour - but as this solution said solved, I really tried to make it work for me.

Now my best guess here is that this is just old language that doesn't work anymore, which clicked once I got mine working. So my guess here is that the code from the linked thread doesn't work, because the way the code works in 2.0 is just not compatible with that formatting anymore. Can I get confirmation that this is the case, or otherwise, an explanation as to why that exact coding does not seem to work anymore (I couldn't make it work, even with direct copy)?

Question two is a little more in the weeds. In the code that functions there is this line:

Code: Select all

data.raw.recipe["transport-belt"].results = {{type="item", name="transport-belt", amount=4}}
If I were to break this down with my rudimentary understanding of Lua (please forgive any verbiage errors).
  • data.raw is calling the "master table" of information compiled by the game.
  • .recipe is designating a subsection of that.
  • ["transport-belt"] is selecting the object from that subsection that I want to mess with.
  • '.results =' is changing the variable that is the results of that object.
  • {{type="item", name="transport-belt", amount="4""}} are defining what you get.
Assuming my understanding is correct (and please correct me if I'm wrong!) my question (and bonus questions) here is: What is the purpose of the double {{}} around the last part, defining what you get? I (think I) understand why the first set of brackets is needed, we're creating a table to be referenced. But what is the second set of brackets actually doing, it isn't (as far as I can tell) attached to anything else?

Bonus questions about this here are that on the data.raw page of the wiki, it suggests that the '.recipe' potion of this code could be replaced with ["recipe"] in the same way we call the transport belt. So it'd look like this:

Code: Select all

data.raw.["recipe"]["transport-belt"].results = ...
Is this accurate? And if it is accurate, is there a preferred choice between which method is used, or if both, when is "generally considered" appropriate to use one over the other? Additionally, does that mean we couple replace '.results' with ["results"]? If not, can you explain why? And if that's the case, could you replace ["transport-belt"] with .transport-belt, and if not, why not?

Actually, on that note, why does '.results' work at all? Based on my understanding 'results =' is a variable we've defined <somewhere>. In this case in the recipe. I am guessing based on context that the 'thing.thing.thing.thing' format is a way for the code to read a table (which was present in my little Lua course, I have just since done some extra reading and am vaguely familiar) and that the way that data.raw works (basically) is that it compiles things into a table that can be read by the code in a file-like structure so it's basically the same thing as data/raw/recipes/transport-belt/results and the reason we're able to call it is because of tabling much earlier on... If this is way off base and too much to explain to a novice, I understand.

And the final question which isn't really about the mod or Lua specifically, is just about formatting conventions - which the internet seems to have mixed opinions on. How many spaces before a new line of code 2-3-4 (vs. tab in note++ which is what I have been using, and appears equal to 4). Nesting vs. not nesting? Are there objective formatting conventions I should be aware of out of the gate that aren't just "your" personal preference, or is this mostly a personal preference sort of thing?

-- Outro
That... Ended up being a lot. Sorry about that! Just a quick preemptive thank you to anyone who reads through this. And an extra thanks to anyone who can shed light on one or more of these questions for me, I really appreciate your time and assistance!
eugenekay
Smart Inserter
Smart Inserter
Posts: 1009
Joined: Tue May 15, 2018 2:14 am
Contact:

Re: Understanding Lua

Post by eugenekay »

Aethyrium wrote: Mon Feb 09, 2026 10:19 pmCan I get confirmation that this is the case, or otherwise, an explanation as to why that exact coding does not seem to work anymore (I couldn't make it work, even with direct copy)?
Yes, version 2.0 changed how recipe ingredients are specified. There used to be a shorthand format for compatibility. The Quality mod chokes if a Recipe does not have a valid Ingredients because it auto-generates recipes for each quality level… it is generally easiest/faster to disable the DLC mods when experimenting with recipe changes unless they require a Space Age item - or use Logic to detect if they are enabled and conditionally add support.
Question two is a little more in the weeds. In the code that functions there is this line:

Code: Select all

data.raw.recipe["transport-belt"].results = {{type="item", name="transport-belt", amount=4}}
If I were to break this down with my rudimentary understanding of Lua (please forgive any verbiage errors).
  • data.raw is calling the "master table" of information compiled by the game.
  • .recipe is designating a subsection of that.
  • ["transport-belt"] is selecting the object from that subsection that I want to mess with.
  • '.results =' is changing the variable that is the results of that object.
  • {{type="item", name="transport-belt", amount="4""}} are defining what you get.
Assuming my understanding is correct (and please correct me if I'm wrong!) my question (and bonus questions) here is: What is the purpose of the double {{}} around the last part, defining what you get? I (think I) understand why the first set of brackets is needed, we're creating a table to be referenced. But what is the second set of brackets actually doing, it isn't (as far as I can tell) attached to anything else?
The outer brackets are defining a ProductPrototype; the inner brackets define a (or multiple) Item/FluidProductPrototype within that wrapper.
Bonus questions about this here are that on the data.raw page of the wiki, it suggests that the '.recipe' potion of this code could be replaced with ["recipe"] in the same way we call the transport belt. So it'd look like this:

Code: Select all

data.raw.["recipe"]["transport-belt"].results = ...
Is this accurate? And if it is accurate, is there a preferred choice between which method is used, or if both, when is "generally considered" appropriate to use one over the other? Additionally, does that mean we couple replace '.results' with ["results"]? If not, can you explain why? And if that's the case, could you replace ["transport-belt"] with .transport-belt, and if not, why not?

Actually, on that note, why does '.results' work at all? Based on my understanding 'results =' is a variable we've defined <somewhere>. In this case in the recipe. I am guessing based on context that the 'thing.thing.thing.thing' format is a way for the code to read a table (which was present in my little Lua course, I have just since done some extra reading and am vaguely familiar) and that the way that data.raw works (basically) is that it compiles things into a table that can be read by the code in a file-like structure so it's basically the same thing as data/raw/recipes/transport-belt/results and the reason we're able to call it is because of tabling much earlier on... If this is way off base and too much to explain to a novice, I understand.
Named properties (like “results”) can be accessed as either a .method; or as a a ["property"]. This is just Lua syntax being weird because it’s an interpreted language. I think Members of an array can only be accessed by name, eg ["transport-belt"]
And the final question which isn't really about the mod or Lua specifically, is just about formatting conventions - which the internet seems to have mixed opinions on. How many spaces before a new line of code 2-3-4 (vs. tab in note++ which is what I have been using, and appears equal to 4). Nesting vs. not nesting? Are there objective formatting conventions I should be aware of out of the gate that aren't just "your" personal preference, or is this mostly a personal preference sort of thing?
There is no single authority on spaces-per-indent-level in Lua. I like to use Tab characters, which are normally 8 characters wide.

Good Luck!
Aethyrium
Burner Inserter
Burner Inserter
Posts: 6
Joined: Mon Feb 09, 2026 8:55 pm
Contact:

Re: Understanding Lua

Post by Aethyrium »

Yes, version 2.0 changed how recipe ingredients are specified. There used to be a shorthand format for compatibility. The Quality mod chokes if a Recipe does not have a valid Ingredients because it auto-generates recipes for each quality level… it is generally easiest/faster to disable the DLC mods when experimenting with recipe changes unless they require a Space Age item - or use Logic to detect if they are enabled and conditionally add support.
Oh perfect, I'm glad I was right about this. Good to know disabling mods and what have you, I will keep that in mind. Thank you!
The outer brackets are defining a ProductPrototype; the inner brackets define a (or multiple) Item/FluidProductPrototype within that wrapper.
I think I'm following this. As I look at the code again, I'm recognizing that this (seems) to functionally be happening on the ingredient line as well.

Code: Select all

	{
		{type = "item", name = "iron-plate", amount = 1},
		{type = "item", name = "iron-gear-wheel", amount = 2}, -- input increased from 1 to 2
		{type = "item", name = "iron-stick", amount = 2}, -- input added
	}
Could also be written

Code: Select all

	{{type = "item", name = "iron-plate", amount = 1}, {type = "item", name = "iron-gear-wheel", amount = 2}, {type = "item", name = "iron-stick", amount = 2}}
...Of course this second writing also asks that you not have eyes, and I suspect is the sort of thing that makes anyone who programs regularly want to lynch you. But a little testing seems to suggest that it works just as well in practice. It could be written then also as something like:

Code: Select all

	{{type = "item", name = "iron-plate", amount = 1},
	{type = "item", name = "iron-gear-wheel", amount = 2},
	{type = "item", name = "iron-stick", amount = 2}}
As some kind of middle ground? My point being that these are all "stylistic" choices, rather than "functional" ones, right?

And as I was mucking around in the code, I was just wondering the same thing about some of the code written in the game:

Code: Select all

data.raw.recipe["transport-belt"].results = {{type="item", name="transport-belt", amount=2}}
Has no spaces between type = "item", etc, but it could be written that way to maintain the same formatting as the ingredients above - or vice versa, the ingredients could be written without the spaces. For example, the above can seamlessly be written as:

Code: Select all

data.raw.recipe["transport-belt"].results = {{type = "item", name = "transport-belt", amount = 2}}
Right?
Named properties (like “results”) can be accessed as either a .method; or as a a ["property"]. This is just Lua syntax being weird because it’s an interpreted language. I think Members of an array can only be accessed by name, eg ["transport-belt"]

There is no single authority on spaces-per-indent-level in Lua. I like to use Tab characters, which are normally 8 characters wide.
This is very informative, thank you!
eugenekay
Smart Inserter
Smart Inserter
Posts: 1009
Joined: Tue May 15, 2018 2:14 am
Contact:

Re: Understanding Lua

Post by eugenekay »

Aethyrium wrote: Tue Feb 10, 2026 12:18 amAs some kind of middle ground? My point being that these are all "stylistic" choices, rather than "functional" ones, right?
Correct, whitespace is stripped out by the Lua interpreter at runtime outside of "quoted strings". The first format is probably the sanest for readability.
Aethyrium
Burner Inserter
Burner Inserter
Posts: 6
Joined: Mon Feb 09, 2026 8:55 pm
Contact:

Re: Understanding Lua

Post by Aethyrium »

Correct, whitespace is stripped out by the Lua interpreter at runtime outside of "quoted strings". The first format is probably the sanest for readability.
You're the best. Thank you!

Okay so, I return with new things to understand. I have made my mod, and I'm feeling pretty comfortable with what I did. But I hunger for more. I don't think the mod I made was particularly revolutionary (which was the point!) but kicked open the door. I'd like to try my hands at some things a bit more advanced which I think I will almost assuredly eventually have questions about. But in the meantime, can someone explain the data lifecycle to me like I'm 5?

I have read: https://lua-api.factorio.com/latest/aux ... cycle.html

And I think I understand the gist of it. But not... How or why to make use of that information? I've seen some general information that can be summarized as "create stuff as early in the lifecycle as possible", which makes me wonder... Why ever use anything other than 'data'? While making my mod, one of the things I haven't gotten to work is adding the sounds for new items, which caused crashes. I am not entirely sure why yet, but I have assumed that this has to do with load order and the mod trying to call sounds which aren't loaded yet... So that's a thing to figure out. So my question isn't really about that specifically it's about the three stages more broadly.

What goes where? Why use 'data.updates'? Or maybe not why but when should you? Same with 'data.final.fixes'. What should you edit in 'data' vs. 'data-updates' vs 'data.final.fixes'?

I think it's a three stage structure that's throwing me for a loop. Based on the naming and how distinct the stages are, I can see 'data.final.fixes' as some sort of "execute these these functions once everything else is sorted out and polish it off". This seems like if you wanted to "hard" override something, you'd do it here. But then why is there the middle stage at all?

Bonus question, when is it correct (or maybe when is it NOT correct) to use 'data:extend'? At first I didn't even realize this was ever necessary, as I don't have it in my recipe adjustments for the mod that is definitely working. But I do have it in the added intermediates code, and I seem to recall (though I've processed so much information in two days that maybe I'm misremembering) that when I initially didn't have it, I ran into issues. So what defines when this is or isn't needed? It seems like, omnipresent when I look at other mods, but... The difference there (as I type it) might be that one thing is modifying a thing that already exists, wherein the other is adding a thing that doesn't exist. Is that key?
eugenekay
Smart Inserter
Smart Inserter
Posts: 1009
Joined: Tue May 15, 2018 2:14 am
Contact:

Re: Understanding Lua

Post by eugenekay »

Aethyrium wrote: Wed Feb 11, 2026 10:54 pmWhat goes where? Why use 'data.updates'? Or maybe not why but when should you? Same with 'data.final.fixes'. What should you edit in 'data' vs. 'data-updates' vs 'data.final.fixes'?

I think it's a three stage structure that's throwing me for a loop. Based on the naming and how distinct the stages are, I can see 'data.final.fixes' as some sort of "execute these these functions once everything else is sorted out and polish it off". This seems like if you wanted to "hard" override something, you'd do it here. But then why is there the middle stage at all?
Data is for standalone definition, like those provided by the Base Game. This is where you should define “new” prototype data which doesn’t have any dependencies.

Data-Updates allows you to modify the Data loaded by other Mods, such as changing recipes or modifying item stats.

Data-final-fixes is a hackjob that should be avoided if at all possible. Sometimes it is necessary for adding cross-mod compatibility or overriding Data-updates that you don’t want.
Nidan
Filter Inserter
Filter Inserter
Posts: 353
Joined: Sat Nov 21, 2015 1:40 am
Contact:

Re: Understanding Lua

Post by Nidan »

Aethyrium wrote: Mon Feb 09, 2026 10:19 pm Are there objective formatting conventions I should be aware of out of the gate that aren't just "your" personal preference
Even this is a personal recommendation, but if there's one thing: be consistent. Coding style is a topic of endless debate that always boils down to taste. Do whatever feels most reasonable/readable to you and stick to that. When editing other people's code, use the style they've been using.
eugenekay wrote: Mon Feb 09, 2026 11:12 pm Named properties (like “results”) can be accessed as either a .method; or as a a ["property"]. This is just Lua syntax being weird because it’s an interpreted language. I think Members of an array can only be accessed by name, eg ["transport-belt"]
.foo and ["foo"] are mostly equivalent and interchangeable in Lua. ["foo"] always works, the .foo variant is a shorthand (syntactic sugar) to make Lua look more similar to other languages, but only works for names/identifiers (only consisting of letters and digits). E.g. in .transport-belt the - will be treated as minus/subtraction operator. The beginning of chapter 3 of the Lua Reference Manual has more details.

Speaking of the reference manual, if you don't mind some technical reading, read chapter 3. That and chapter 6 is basically all you need when writing Lua. (Of course, add facorios Lua API doc when writing factorio mods.) Ignore chapters 4 and 5, those are the parts the factorio devs need for providing their Lua API.
Aethyrium wrote: Mon Feb 09, 2026 10:19 pm Actually, on that note, why does […] work at all?
In factorios prototype stage, we're just putting tables within table within tables. That a construct like data.raw.recipes["transport-belt"].results = {{type = "item", name = "transport-belt", amount = 4}} happens to have a particular meaning is because the devs decided to expose access to the games data that way. Nothing is stopping us from assigning nonsense like data.raw["the greatest programmer in the world"] = "me", but the game will either ignore it or complain.
Aethyrium wrote: Wed Feb 11, 2026 10:54 pm When is it correct (or maybe when is it NOT correct) to use 'data:extend'?
The official documentation (https://lua-api.factorio.com/latest/typ ... tml#extend , https://lua-api.factorio.com/latest/typ ... ethod.html) is a bit sparse: "It's the primary way to add prototypes to the data table." So, if you're adding new prototypes (items, recipes, technologies, etc.), use data:extend, it probably does some magic aside from sorting things into the correct data.raw.x entry; if you're interacting with an already existing prototype, access it via data.raw.
Aethyrium wrote: Wed Feb 11, 2026 10:54 pm What goes where? Why use 'data.updates'? Or maybe not why but when should you? Same with 'data.final.fixes'. What should you edit in 'data' vs. 'data-updates' vs 'data.final.fixes'?
Try to be done as early as possible (says the one whose mod only has data-final-fixes…); later stages are for compatibility between mods.
Aethyrium
Burner Inserter
Burner Inserter
Posts: 6
Joined: Mon Feb 09, 2026 8:55 pm
Contact:

Re: Understanding Lua

Post by Aethyrium »

Even this is a personal recommendation, but if there's one thing: be consistent. Coding style is a topic of endless debate that always boils down to taste. Do whatever feels most reasonable/readable to you and stick to that. When editing other people's code, use the style they've been using.
Haha. Consistency I can do! I actually spent a bit of time just starring at my code, and comparing it to other bits, internalizing what I wanted to do. This was great advice.
Data is for standalone definition, like those provided by the Base Game. This is where you should define “new” prototype data which doesn’t have any dependencies.

Data-Updates allows you to modify the Data loaded by other Mods, such as changing recipes or modifying item stats.

Data-final-fixes is a hackjob that should be avoided if at all possible. Sometimes it is necessary for adding cross-mod compatibility or overriding Data-updates that you don’t want.
The official documentation (https://lua-api.factorio.com/latest/typ ... tml#extend , https://lua-api.factorio.com/latest/typ ... ethod.html) is a bit sparse: "It's the primary way to add prototypes to the data table." So, if you're adding new prototypes (items, recipes, technologies, etc.), use data:extend, it probably does some magic aside from sorting things into the correct data.raw.x entry; if you're interacting with an already existing prototype, access it via data.raw.

Try to be done as early as possible (says the one whose mod only has data-final-fixes…); later stages are for compatibility between mods.
Between these, I think I pretty well understand. Thank you both, this helped immensely (and let me understand some of what I was seeing in other mods as I was exploring!).
.foo and ["foo"] are mostly equivalent and interchangeable in Lua. ["foo"] always works, the .foo variant is a shorthand (syntactic sugar) to make Lua look more similar to other languages, but only works for names/identifiers (only consisting of letters and digits). E.g. in .transport-belt the - will be treated as minus/subtraction operator. The beginning of chapter 3 of the Lua Reference Manual has more details.

Speaking of the reference manual, if you don't mind some technical reading, read chapter 3. That and chapter 6 is basically all you need when writing Lua. (Of course, add facorios Lua API doc when writing factorio mods.) Ignore chapters 4 and 5, those are the parts the factorio devs need for providing their Lua API.
Oh the first part of this, about why '.transport-belt' doesn't work is really helpful, thank you. I totally see that. And chapter suggestions do help! I was looking at that reference manual. I shall consume that this week.
In factorios prototype stage, we're just putting tables within table within tables. That a construct like data.raw.recipes["transport-belt"].results = {{type = "item", name = "transport-belt", amount = 4}} happens to have a particular meaning is because the devs decided to expose access to the games data that way. Nothing is stopping us from assigning nonsense like data.raw["the greatest programmer in the world"] = "me", but the game will either ignore it or complain.
If I am understanding you correctly (and I think I am!) this aligns with my guess/understanding of why and how this was working. I appreciate that, thank you!

Okay so today we have some new stuff... And a new question or two.

After finishing(ish, gotta go fix that sound issue) my belts mod and learning the changelog format (which is probably unneeded, but I wanted to know) I started thinking I wanted to play with something else. So I started tackling mining and looking at modding the mining processing just for giggles. First up was changing mining time to fit with the grander vision I have. It took me a minute to figure out that I needed '.minable' ahead of '.mining_time =', which didn't make a ton of sense to me until I went back and realized the ores are actually functions and the 'minable' thing is in the function itself, not in the definition of each ore. But this was made more confusing by the fact that it is in uranium ore and crude oil. Which I can't figure out why they're written differently... Yet.

But actually the real problem came when I was trying to modify the results. In the base code, results aren't defined inside of the ores (iron, copper, stone), but are to differing degrees in uranium and crude oil. And I this really threw me. I looked, initially, at the ores sort of like items and expected there to be a 'result(s) =' somewhere, but couldn't find it. Cue forty-five minutes of me searching everywhere for this being defined somewhere else. Giving them a:

Code: Select all

data.raw.resource["iron-ore"].minable.results = x
Did not produce the expected outcome. Nor did changing results to result. And nothing seemed to work...

Eventually this led me to the API Docs (which I find terribly intimidating, but I think mostly on account of I just don't really understand how to use it / read it... Yet!) and my first real attempt to understand them. So I went and tried to wrap my brain around that, found the MinableProperties (why do we we use '.minable' and not '.minableproperties', and is there a place this defined somewhere/where all of... Types(?) and how they get typed in the code are?) and because I had accidentally found my way to the 'mining_time' solution I was able to intuit (I hope...) that each of the properties listed in the miningproperty type were things that one could add after a dot. 'minable.fluid_amount =' or 'minable.mining_trigger' (even if this is an example and I don't yet understand what you'd do with this specific one).

So excitedly trying to figure that out. I try to define results. That does nothing. Hrm! Read a little further. Try to define result, which apparently doesn't load if results isn't defined (and based on that I couldn't make it work, I thought for some reason this was just some weirdness I didn't understnad about why results wasn't used in mining). Still no working. Now I'd only seen up to this point results be used to define what gets used when... Get to count finally and have a big AH HA! moment. Redefine count, works exactly the way I want it too. Yay! But also confused...

So the questions are...

Why doesn't '.minable.results =' return an adjusted amount of resources?

...As I was typing this I had an epiphany. I was going to compare to the results of a recipe (like say, iron plate) and realized that the code is actually different. Defining results has that double wrapper I asked about above, and those results are calling a specific thing of everything being spelled out, not just the amount. And a moment of testing later, I realize this is correct and that:

Code: Select all

data.raw.resource["iron-ore"].minable.results = 
	{
		{type = "item", name = "iron-ore", amount = 25}
	}
Does, in fact, produce the outcome that I expected initially. Turns out, you just have to do things the right way. Funny how that works.

Okay, so, other questions that still matter...

What is the difference between: 'results', 'result', and 'count'? (My guess is that these are just relics, used in differing places throughout the code, and so any of them are feasible, but they could be condensed into a single thing if someone was to go through and "clean up" the various entries)

Is there a reason that the ores use count, rather than having a defined result(s?)? (I'm guessing this is just because having a default number that can be applied to each ore is easier coding than rewriting unique result(s) lines for every individual entry, but just in case I'm wrong about that)

In places where count is used, is there a reason to use that over individually defined results? Or I guess the other way makes more sense, is there a reason to use individually defined results over using the count that exists and modifying that? I think I'm trying to 'optimize the code' here in that, I want it to be as seamless as is reasonable (for the game sake, and my own good habit sake). So maybe the actual question here is, is there a specific reason to each/any of these, or are they more or less freely interchangeable where applicable?
Pi-C
Smart Inserter
Smart Inserter
Posts: 1792
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Understanding Lua

Post by Pi-C »

Aethyrium wrote: Sat Feb 14, 2026 7:16 pm Eventually this led me to the API Docs (which I find terribly intimidating, but I think mostly on account of I just don't really understand how to use it / read it... Yet!) and my first real attempt to understand them. So I went and tried to wrap my brain around that, found the MinableProperties (why do we we use '.minable' and not '.minableproperties', and is there a place this defined somewhere/where all of... Types(?) and how they get typed in the code are?)
.minable is shorter than .minableproperties, while MinableProperties is more descriptive. So the first is easier to use in code, and the second is better for use in the documentation to describe a certain type of properties. For example, the game knows not just MinableProperties, but also LightProperties – if you check out the list of types, you'll see that all types have rather telling names that hint at in which context they are used.
and because I had accidentally found my way to the 'mining_time' solution I was able to intuit (I hope...) that each of the properties listed in the miningproperty type were things that one could add after a dot. 'minable.fluid_amount =' or 'minable.mining_trigger'
Have a look at Lua's lexical conventions – if you have table keys with names that contain illegal characters, you must use ["brackets"], otherwise you can use dots for brevity. By the way, you can use the serpent library to better visualize the structure of a table. For example, if you run this

Code: Select all

log(serpent.block(data.raw.item["stone-furnace"]))
you will see that the nested tables are indented for each level of subtables.
What is the difference between: 'results', 'result', and 'count'? (My guess is that these are just relics, used in differing places throughout the code, and so any of them are feasible, but they could be condensed into a single thing if someone was to go through and "clean up" the various entries)
You should distinguish between results on the one hand, and result + count on the other. Using results is more versatile. As you can see in the description of MinableProperties, results is an array of ProductPrototypes. Arrays can have >1 elements, so you could give players several different things when they are mining something (this makes more sense for entities than resources, e.g. you could return copper wire + wood when a small powerpole is mined). Moreover, ProductPrototype comprises both ItemProductPrototype and FluidProductPrototype, so you can also return fluids!

In contrast, the combination of result and count is a shorthand that you can use when you want to return just one type of item (result is an ItemID, i.e. the name of the item prototype, while count is an integer specifying the amount of items you want to return).
Is there a reason that the ores use count, rather than having a defined result(s?)?
If you "mined" (rather: deconstructed) a building, car, transport belt etc., it would make sense to return different items or even fluids – but if you went out to mine coal, you'd be interested in coal only, so it wouldn't be necessary to define results: name and amount of the item returned are sufficient in this case.

There is one catch, though: if both results and result/count are defined, only the things defined in results will be used, while whatever is specified in result/count will be ignored.

Also, it is possible to define neither results nor result: In this case, the object owning the MinableProperties can be mined but the player will receive nothing. While that doesn't really make sense for a ResourceEntityPrototype, it can be useful for an EntityPrototype.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
Pi-C
Smart Inserter
Smart Inserter
Posts: 1792
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Understanding Lua

Post by Pi-C »

Pi-C wrote: Sun Feb 15, 2026 6:37 am Moreover, ProductPrototype comprises both ItemProductPrototype and FluidProductPrototype, so you can also return fluids!
You may also want to use results for shenanigans like a probability that mining will yield anything, a random yield amount within a given range, spoiled products, etc. Using result/count is rather basic in contrast as it allows only for yielding a fixed amount of exactly one item.
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!
Aethyrium
Burner Inserter
Burner Inserter
Posts: 6
Joined: Mon Feb 09, 2026 8:55 pm
Contact:

Re: Understanding Lua

Post by Aethyrium »

.minable is shorter than .minableproperties, while MinableProperties is more descriptive. So the first is easier to use in code, and the second is better for use in the documentation to describe a certain type of properties. For example, the game knows not just MinableProperties, but also LightProperties – if you check out the list of types, you'll see that all types have rather telling names that hint at in which context they are used.
Yeah, totally get ease. I think my question wasn't phrased well - your logic makes total sense and I agree. What I actually meant was why, functionally, do we use .minable instead of .minableproperties - where is that defined? Or maybe put another way, how would I know that I can access the MinableProperties type via .minable without "just knowing". I figured that specific case out by digging around and slowly working backward from results in the base code and made an educated guess once I found it, that later got confirmed by noticing it in the API docs example code. But say, with that knowledge in hand, my instinct would be that LightProperties is accessible via .light - which it may very well be, I haven't touched it at all, but there is no confirmation in the API docs the same way the MinableProperies has one... Unless I'm blind, which has been known to be true once or twice in the past.

Obviously I can just sift through the code and find it, especially now that I have an idea of what I'm looking for, it just seems inefficient to learn that way / need to find it that way.
Have a look at Lua's lexical conventions – if you have table keys with names that contain illegal characters, you must use ["brackets"], otherwise you can use dots for brevity. By the way, you can use the serpent library to better visualize the structure of a table. For example, if you run this

you will see that the nested tables are indented for each level of subtables.
I think I am following about the ["brackets"] and dots. At least, the idea makes sense to me. I'm struggling to think of a reason you'd ever name something with an illegal character to begin with, but I'm going to assume that's just inexperience talking.

I had a chance to run the serpent library code, and figure out where that went. This seems like a way to find information buried in the code that you might have trouble finding otherwise? Is there another practical application to this that isn't clear to me?
You should distinguish between results on the one hand, and result + count on the other...

If you "mined" (rather: deconstructed) a building, car, transport belt etc.,...

You may also want to use results for shenanigans like a probability that mining will yield anything, a random yield amount within a given range, spoiled products, etc. Using result/count is rather basic in contrast as it allows only for yielding a fixed amount of exactly one item.
Genuinely so informative. This makes perfect sense and really anchored what I was seeing, and makes intuitive practical application sense. Thank you so much.

Speaking of, Pi-C! Hi! You wrote the big changelog guide that I just read the other day while trying to make the changelog of my mod work (god they're finicky!), and ultimately it got me to where I wanted to be. Thanks for that too, also incredibly informative and useful.

Gosh, I feel like every day I have more stuff to add. I recognize that the mod I've made is not revolutionary and I'm not exactly doing any code work that's particular complex, so I really appreciate everyone who has read and added input. You've all helped tremendously! Hopefully I can keep picking everyone's brains.

Let's see, what have I done since last post. Oh! I got sound working on the new intermediates, which fixed using local. Which was my first time playing with that particular piece of code. Later I realized I probably could have solved it just by manually adding the direct address to each item, but I'm glad I did it this way at least for now since it gave me a reason to learn about local - which, if I'm understanding, is code that is only being run (...locally) within the file that its present in. So a thing in foo.lua won't impact anything in bar.lua. But while reading up on this function, I think I learned that you can use it even more deeply than that - you can have a thing local within another bit of code (I think the term I'm looking for is block). Which is interesting! I think. I can't think of a reason to do that presently, but it seems too useful to be a thing that won't come up.

With sound done I thought I was officially done with this project. But then someone made a message requesting a feature (big hype for me, tbh) and I started thinking about how to execute it. I figure if it fits the mod theme and expands my knowledge, I couldn't ask for better practice. While thinking about that, I wanted to decouple the mod from requiring Space Age and instead be optional with it. Took me a minute to figure out how to do that which led me to learn about conditional loading - if mods["space-age"] then - and playing around with that to see if it worked the way I anticipated and I think it did. I think the way I solved this is a matter of taste/preference, rather than something with objective truth. But I have been wrong before...

I am using data (new definitions) and data-updates (updates to existing definitions) to load various parts of the mod. Which I don't think is strictly needed (originally I was just inputting everything into data but I realized somewhere that if I was ever doing a bigger project than like 12 recipes and a half dozen other things, all of it being crammed into one file would be hugely difficult to parse as a person). Originally this only called for a couple files, broken out just for organization purposes. So the structure is now:

Code: Select all

- Prototypes
	- item
		- intermediates
		- space-age-intermediates
	- recipe
		- intermediates
		- space-age-intermediates
	- updates
		- base-recipe
		- base-technology
		- space-age-recipe
		- space-age-technology
Now originally, all of the space age definitions were also inside of the base and new intermediates. I separated them out and in data and data-updates I am require() these files only if Space Age is loaded. And this all works and seems to have broken dependency on space age, but rather includes it as an optional thing that adds other stuff when its present. If I understand the code, another way I could have done this, is to wrap the actual space age required definitions in the if mods["space-age"] then within the original files and accomplished the same thing, right? Which brings me to this is preference, not functional, organization, right? There is no practical difference between these two methods of execution?

Today the plan is to try and figure out how to make a toggle setting that players can opt in or out of upon loading the mod the first time - like, if yes, add these recipes/items, if no, don't. Which also may require calling (or constructing?) a gui? And the selection needs to be saved and only selected the one time. If I can figure this out, I think that'll be the largest hurdle in being able to add the requested features to the mod. I think this is done in settings.lua but I haven't had to mess with that before. So we'll see how that goes.
Aethyrium
Burner Inserter
Burner Inserter
Posts: 6
Joined: Mon Feb 09, 2026 8:55 pm
Contact:

Re: Understanding Lua

Post by Aethyrium »

Maybe I am blind.

Alright. Unrelated to questions and thoughts above, I started messing with item groups and trying to understand how they're organized. And I'm pretty sure I get it, but I can't find where it's doing it, and so I can't confirm which makes me a bit lost.

The player crafting gui and the factoriopedia display information in groups and within those groups are subgroups, and then within those ordering. I (am pretty sure) get that. And I can see this in application. If I look at item-groups I can see each major group and find what I would expect to find within those.

I would then expect that these groups are a way to call specific things to, say entities that can build stuff. Which I can also see. If I look at the assembling machine entity, it can craft 'crafting', 'basic-crafting', and 'advanced-crafting'. And if I look at the items I know to find inside of the assembling machine, I can find the recipes that have one or more of these categories attached to them. All of this makes sense.

So I made a new group. Got it to show up in the crafting panel and the factoriopedia as expected, got to play with ordering to understand how that functioned. But then I realized some oddity and this is where I don't understand what's happening - or, I think I understand what's happening (based on above), but I don't understand where its happening.

The player crafting groups are: logistics, production, intermediate products, and combat.
The factoriopedia has expanded groups: fluids, signals, enemies, tiles, environment, and unsorted.

These groups are all defined in item-groups, and becuase there is a difference I would expect there to be some kind of flag. Except its not there, as far as I can tell.

Why can you not see 'fluids' in the character crafting panel, but can in the factoriopedia? Where/how is that distinction being made?

If it is not immediately obvious by the answer to above, if you were making your own group, how would you make it so it was seen in one but not the other?

Smaller bonus questions:

In item-groups, each heading is separated by a comment of many, many dashes and then the header for that category. I assume that's just a formatting thing since it's a single line comment, but when recently working with the changelog and the need for a very specific number of dashes, I'm just wondering if there's an actual meaning in this particular use or if its just a comment formatted for readability.

Also, in the same file, intermediate products has a line of code that is not present in any of the other groups:

Code: Select all

    order_in_recipe = "0",
Why? What is this doing?
Post Reply

Return to “Modding help”