Creating a Mod

Place to get help with not working mods / modding interface.
Post Reply
CJ5Boss
Fast Inserter
Fast Inserter
Posts: 130
Joined: Thu Apr 05, 2018 11:55 pm
Contact:

Creating a Mod

Post by CJ5Boss »

Hello,
As a fairly new mod creator I want to create a mod that adds new buildings and functions to the game.I know this is very complicated, but does anyone have a good tutorial for me too look at that would help achieve this? Thanks!
CJ

User avatar
darkfrei
Smart Inserter
Smart Inserter
Posts: 2903
Joined: Thu Nov 20, 2014 11:11 pm
Contact:

Re: Creating a Mod

Post by darkfrei »

The modding has two parts: making prototypes and change the course of the game.

Now you want to make new prototypes/changing them. Now you are need to understand how it works.
We have data.raw table, where we have all items, all recipes and all buildings. If you want to know how it looks like, then you are need Notepad++ and this mod: viewtopic.php?f=135&t=45107

For first mod creating you are need to copy some code part of vanilla prototypes and change it, place this code to your mod folder and make info.json file.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Creating a Mod

Post by eradicator »

The wiki has several tutorials. An easy first step is making new prototypes.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
bobingabout
Smart Inserter
Smart Inserter
Posts: 7351
Joined: Fri May 09, 2014 1:01 pm
Contact:

Re: Creating a Mod

Post by bobingabout »

Another good option is to download a good mod, and see how they do it.

A common rookie mistake is the copy paste method. Most of my mods are build on this method. Find an entity that does something similar to what you want to do (assembling machine), copy it's code, then paste it in your mod.

This isn't such a bad thing if you're creating something entirely new, since most crafting machines in the game are actually variations on the same entity type (oil refinery, chemical plant, assembling machine, centrifuge, etc.) but if you're making an assembling machine MK4 for example, it's better to look at different methods. By far the most popular is the table.deepcopy method, where you copy the entity table to a local variable, change the parts that you need/want to change, such as name=, then finally add it to data. Personally, I'm more a fan of the util.merge function.

table.deepcopy (or util.table.deepcopy) is basically copy of table = table.deepcopy(original table), and is used in this format:

Code: Select all

local new_item = table.deepcopy(data.raw.item[item])
new_item.name = item_name
//other lines here.
data:extend({
  new_item
})
util.merge makes use of table.deepcopy internally to copy tables, but what it does is apply 1 table as changes to another as it copies it.
The method is newtable = util.merge(original table, changes table), and is used in this format:

Code: Select all

data:extend({
  util.merge(data.raw.item[item],
    {
      name = item_name
//other lines here.
    }
  )
})
A lot of my newer mods use the util.merge method for entities, but full copy/paste type(could be fully typed) code for items and recipes. I'm not going back to replace code that still works, but when new entities are added, or I need to fix broken code due to an update (or just a big change, like to my power mod's boilers and steam engines), I am actively replacing these code blocks with util.merge blocks instead. Recipes and items will likely always be full code for me because an item is usually half a dozen lines, and a recipe maybe a full dozen, and most of the lines would be unique to that item or recipe, so if I'd be changing more than half the lines after copying them, what's the point in copying in the first place?
Creator of Bob's mods. Expanding your gameplay since version 0.9.8.
I also have a Patreon.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Creating a Mod

Post by eradicator »

A notable drawback to the "merge method" is that you can not remove any keys. I.e. if you want to remove a flag or a whole property (i.e. collision_box), then with the merge method you have to remove that value after data:extend, while with deepcopy you can do it before. It's not functionally different, but i personally prefer the look of doing everything before data:extend. Oh, and merge also can't overwrite table values, which can be dangerous when you try to change..well, anything that's a table, i.e. collision_boxes, animations, fluid_boxes, flags, etcpp, because it'll just recursively merge those too. Overall i would not recommend merge until you have more modding experience.
bobingabout wrote:
Mon Oct 15, 2018 8:45 am
if I'd be changing more than half the lines after copying them, what's the point in copying in the first place?
You can still benefit from changes to the base recipes, like addition of icon_size, or new standard flags. Happens only rarely, but does happen.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
bobingabout
Smart Inserter
Smart Inserter
Posts: 7351
Joined: Fri May 09, 2014 1:01 pm
Contact:

Re: Creating a Mod

Post by bobingabout »

eradicator wrote:
Mon Oct 15, 2018 11:31 am
bobingabout wrote:
Mon Oct 15, 2018 8:45 am
if I'd be changing more than half the lines after copying them, what's the point in copying in the first place?
You can still benefit from changes to the base recipes, like addition of icon_size, or new standard flags. Happens only rarely, but does happen.
True, but I meant for readability within my mod. I guess I'm a fan of fully defining my own recipes, not just copying one with a script because I can.
I guess an alternate would be to write my own template, and update that when things change.
Last edited by bobingabout on Tue Oct 16, 2018 8:14 am, edited 2 times in total.
Creator of Bob's mods. Expanding your gameplay since version 0.9.8.
I also have a Patreon.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Creating a Mod

Post by eradicator »

bobingabout wrote:
Tue Oct 16, 2018 7:57 am
I guess an alternate would be to write my own template, and update that when things change.
Sounds like a very good idea :).
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
bobingabout
Smart Inserter
Smart Inserter
Posts: 7351
Joined: Fri May 09, 2014 1:01 pm
Contact:

Re: Creating a Mod

Post by bobingabout »

eradicator wrote:
Mon Oct 15, 2018 11:31 am
A notable drawback to the "merge method" is that you can not remove any keys. I.e. if you want to remove a flag or a whole property (i.e. collision_box), then with the merge method you have to remove that value after data:extend, while with deepcopy you can do it before. It's not functionally different, but i personally prefer the look of doing everything before data:extend. Oh, and merge also can't overwrite table values, which can be dangerous when you try to change..well, anything that's a table, i.e. collision_boxes, animations, fluid_boxes, flags, etcpp, because it'll just recursively merge those too. Overall i would not recommend merge until you have more modding experience.
Yes, it is a bit of a drawback. if you look at my Oil boiler example (it's in my 0.17 WIP power mod, so you can't actually look at it yet), First I do the util.merge method to copy Boiler MK2 (because I want stats to match that rather than the base boiler), and on one of those lines, is the following:

Code: Select all

 minable = {result = "oil-boiler"},
and in this case, it will only replace the result tag in the minable table, it won't touch harness or mining_time, which is useful for this specific example, but no so much with energy_source. This is because it copies and merges tables within tables, so similar to the table.deepcopy method I define it after the util.merge like this:

Code: Select all

data.raw.boiler["oil-boiler"].energy_source =
{
  type = "fluid",
//rest of the definition, etc.
}
and you can also blank out tags with = {} or = nil. outside of the util.merge too.
And note that I define it afterwards, not as part of the merge.
So you're right, it's not perfect, but the examples where you want to delete lines are usually the minority, not the majority.

The main difference between util.merge and table.deepcopy is that with deepcopy, you edit and delete tags in the same phase, after copying, but before adding to data.raw.
with merge, you do additions and tag replacements inside the merge, but then deletions or table replacements after it.

Also like deepcopy, you could actually save the result of util.merge to a variable then add it to data in the same way you do in deepcopy, but the difference is, because you're already changing the name in the merge, you CAN pipe the result straight into data:extend if you want to, and I usually do because most merges don't require additional editing. Boiler is one of the examples where I do change the table afterwards, and there have been a couple of cases where I set a tag to nil.
Creator of Bob's mods. Expanding your gameplay since version 0.9.8.
I also have a Patreon.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Creating a Mod

Post by eradicator »

bobingabout wrote:
Tue Oct 16, 2018 8:14 am
So you're right, [the merge method is] not perfect, but the examples where you want to delete lines are usually the minority, not the majority.
The problem with recommending it to beginning modders is that it behaves in unexpected ways. I didn't even know the thing existed before you mentioned it here, and when i looked at the source was quite astonished that it does a recursive merge instead of a first level merge as i would have expected from the name. I personally use something similar to this (heavily simplified):

Code: Select all

    local function set__union_of_keys (self,table2)
      for k,v in pairs(table2) do
        self[k] = table.deepcopy(v)
        end
      return self
      end
Which is basically merge, but without the recursiveness.

Especially if you want to remove something it becomes unintuitive. E.g:

Code: Select all

data:extend{util.merge{data.raw.container['steel-chest'],{
	name = 'my-invisible-chest',
	collision_box = nil,
	selection_box = nil
	}}
	
This will not remove any of the boxes because due to being nil, they're not even passed to merge in the first place. This is not something a beginning modders knows or should care about.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
bobingabout
Smart Inserter
Smart Inserter
Posts: 7351
Joined: Fri May 09, 2014 1:01 pm
Contact:

Re: Creating a Mod

Post by bobingabout »

eradicator wrote:
Tue Oct 16, 2018 8:38 am
bobingabout wrote:
Tue Oct 16, 2018 8:14 am
So you're right, [the merge method is] not perfect, but the examples where you want to delete lines are usually the minority, not the majority.
The problem with recommending it to beginning modders is that it behaves in unexpected ways. I didn't even know the thing existed before you mentioned it here, and when i looked at the source was quite astonished that it does a recursive merge instead of a first level merge as i would have expected from the name. I personally use something similar to this (heavily simplified):
Still, I prefer it to table.deepcopy.
note, util.table.deepcopy exists too, they're trying to put everything like that in util, the main reason it still exists outside of util is because it would break so many mods if they removed the non-util version of it. it kinda proves that merge is kind of new on the list. (also that it internally uses deepcopy proves deepcopy is older.)

Anyway... I still prefer merge over deepcopy.

I also wrote my own version of merge before merge itself was added, it's in my library, it also edits the table directly, not making a new one. I used to use it before the settings GUI was added in game to modify entire entities, since it basically just applied your edited settings table to data.
Creator of Bob's mods. Expanding your gameplay since version 0.9.8.
I also have a Patreon.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Creating a Mod

Post by eradicator »

bobingabout wrote:
Tue Oct 16, 2018 8:45 am
Anyway... I still prefer merge over deepcopy.
My argument was soley that i think deepcopy is better for inexperienced modders. I'm not trying to tell you what to do ;).

Btw, i thought of another fun example with merge.

Exercise 1) We want to make a fireproof inserter. Answer the resulting flags table of each method and explain how you came to that conclusion.

The base prototypes flags look like this:

Code: Select all

--defined by the base game
 {
 name = "inserter", 
 type = "inserter",
 flags = {"placeable-neutral", "placeable-player", "player-creation"}
 }
a) the merge method:

Code: Select all

data:extend{util.merge{data.raw.inserter.inserter,{
 name = 'my-fireproof-inserter',
 flags = {'not-flammable'},
 }
 }}
b) the deepcopy method:

Code: Select all

local myinserter = util.table.deepcopy(data.raw.inserter.inserter)
myinserter .name = 'my-fireproof-inserter'
table.insert(myinserter.flags,'not-flammable')
data:extend{myinserter}
Without testing ingame ofc ;)
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
bobingabout
Smart Inserter
Smart Inserter
Posts: 7351
Joined: Fri May 09, 2014 1:01 pm
Contact:

Re: Creating a Mod

Post by bobingabout »

eradicator wrote:
Tue Oct 16, 2018 9:56 am
a) the merge method:

Code: Select all

data:extend{util.merge{data.raw.inserter.inserter,{
 name = 'my-fireproof-inserter',
 flags = {'not-flammable'},
 }
}}
I like this one.

Also you did it wrong, util.merge should open with (, not {.

Code: Select all

data:extend{util.merge(data.raw.inserter.inserter,{
    name = 'my-fireproof-inserter',
    flags = {'not-flammable'},
    }
  )
}
Creator of Bob's mods. Expanding your gameplay since version 0.9.8.
I also have a Patreon.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Creating a Mod

Post by eradicator »

bobingabout wrote:
Tue Oct 16, 2018 1:56 pm
eradicator wrote:
Tue Oct 16, 2018 9:56 am
a) the merge method:

Code: Select all

data:extend{util.merge(data.raw.inserter.inserter,{
 name = 'my-fireproof-inserter',
 flags = {'not-flammable'},
 }
)}
I like this one.
That's the buggy one that removes the "placeable-neutral" flag :p.
Due to the recursive nature of merge it'll overwrite the first key in flags, but keep the the others because the table being merged only has one key. So you end up with

Code: Select all

 flags = {"not-flammable", "placeable-player", "player-creation"}
instead of the desired

Code: Select all

 flags = {"placeable-neutral", "placeable-player", "player-creation","not-flammable"}
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
bobingabout
Smart Inserter
Smart Inserter
Posts: 7351
Joined: Fri May 09, 2014 1:01 pm
Contact:

Re: Creating a Mod

Post by bobingabout »

eradicator wrote:
Tue Oct 16, 2018 2:04 pm
Due to the recursive nature of merge it'll overwrite the first key in flags, but keep the the others because the table being merged only has one key. So you end up with

Code: Select all

 flags = {"not-flammable", "placeable-player", "player-creation"}
instead of the desired

Code: Select all

 flags = {"placeable-neutral", "placeable-player", "player-creation","not-flammable"}
I should have known that. You can see how long it's been since I've actually sit down and modded properly. (I did actually know that, but it slipped my mind)
my last real effort in factorio was on the programming side. I've done very little in the way of modding in the past... 4 or 5 months.
Creator of Bob's mods. Expanding your gameplay since version 0.9.8.
I also have a Patreon.

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Creating a Mod

Post by eradicator »

bobingabout wrote:
Wed Oct 17, 2018 7:43 am
eradicator wrote:
Tue Oct 16, 2018 2:04 pm
Due to the recursive nature of merge it'll overwrite the first key in flags, but keep the the others because the table being merged only has one key. So you end up with

Code: Select all

 flags = {"not-flammable", "placeable-player", "player-creation"}
instead of the desired

Code: Select all

 flags = {"placeable-neutral", "placeable-player", "player-creation","not-flammable"}
I should have known that. You can see how long it's been since I've actually sit down and modded properly. (I did actually know that, but it slipped my mind)
my last real effort in factorio was on the programming side. I've done very little in the way of modding in the past... 4 or 5 months.
And now you will serve as an eternal example of how easy it is to create hidden bugs with merge(). :twisted:
Beginners should stick to deepcopy(). Q.E.D.

Oh, and btw we should probably get back on topic... *cough*.
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
darkfrei
Smart Inserter
Smart Inserter
Posts: 2903
Joined: Thu Nov 20, 2014 11:11 pm
Contact:

Re: Creating a Mod

Post by darkfrei »

bobingabout wrote:
Mon Oct 15, 2018 8:45 am
util.merge makes use of table.deepcopy internally to copy tables, but what it does is apply 1 table as changes to another as it copies it.
The method is newtable = util.merge(original table, changes table), and is used in this forma
Why util.merge does not available in control.lua?

User avatar
bobingabout
Smart Inserter
Smart Inserter
Posts: 7351
Joined: Fri May 09, 2014 1:01 pm
Contact:

Re: Creating a Mod

Post by bobingabout »

darkfrei wrote:
Sun Nov 18, 2018 2:41 pm
bobingabout wrote:
Mon Oct 15, 2018 8:45 am
util.merge makes use of table.deepcopy internally to copy tables, but what it does is apply 1 table as changes to another as it copies it.
The method is newtable = util.merge(original table, changes table), and is used in this forma
Why util.merge does not available in control.lua?
I think you have to include it before you can use it in scripting. I'm not going to give an example though, because It's something I'd have to look into first.
Creator of Bob's mods. Expanding your gameplay since version 0.9.8.
I also have a Patreon.

Post Reply

Return to “Modding help”