about modding : new tutorials

Place to get help with not working mods / modding interface.
jerem0883
Burner Inserter
Burner Inserter
Posts: 6
Joined: Sat Dec 06, 2014 1:20 pm
Contact:

about modding : new tutorials

Post by jerem0883 »

Hi guys.
I play factorio since some months already, (I never finished a game though !).

I started to want to know more about modding, but like a lot of people saw,
there not much useful information about it.

We have:
- the overview: https://forums.factorio.com/wiki/inde ... g_overview, very (very) basic
- the only one tutorial: https://forums.factorio.com/wiki/inde ... g_Tutorial, a lot more useful, although it's not working with last versions. But explanations are valid.
- the others pages on the wiki available from https://forums.factorio.com/wiki/inde ... le=Modding. But we don't learn how to write a mod.

So, let's try to do it !
There's a lot of information to give, I don't know how to order them (and I don't know a lot yet).

First, the already existing tutorial. Here is a little summarize of what we learn about
  • mod setup: the directories and files to create
https://forums.factorio.com/wiki/inde ... _1_-_Setup
  • the items: objects we can create and store in inventory
https://forums.factorio.com/wiki/inde ... _The_Items
  • the recipes: what do we need to create a given object
https://forums.factorio.com/wiki/inde ... he_Recipes
  • the entities: what do we get once we use an item
https://forums.factorio.com/wiki/inde ... e_Entities
  • the technologies: which tech exists, which recipes it allows, ...
https://forums.factorio.com/wiki/inde ... chnologies
  • localization: how to translate your mod
https://forums.factorio.com/wiki/inde ... alizations
  • lua code: how to add a new behavior to factorio
https://forums.factorio.com/wiki/inde ... e_Lua_Code
  • the 8th chapter has to be read too, it shows the real structure of a mod (additional directories and files)
https://forums.factorio.com/wiki/inde ... the_mod.21
  • the last chapter shows some idea to expand the example mod (and a weird error if the mod is a directory rather than a zip file ???)
https://forums.factorio.com/wiki/inde ... the_mod.3F



My turn :)

First an example to show the variables initialization mechanism.
  • How to initialize your data ?
Create an empty mod, with the following code in control.lua

Code: Select all

-- import 'defines.lua' file.
-- It's located (on linux) in factorio/data/core/lualib and contains some
-- definitions. The most often used are the events.
require "defines"

-- some variables which will be modify later
glob.var = ""
var = ""

-- boolean used later to print the variables
local do_print = true

-- init is the 1st event called when you start a new game
game.oninit
(function()
    -- glob.var and var are set to the concatenation of their actual value "" and "init, "
    glob.var = glob.var .. "init, "
    var = var .. "init, "
end)

-- playercreated is an event called when you start a new game. It's called
-- after oninit
game.onevent
(defines.events.onplayercreated,
 function(event)
    glob.var = glob.var .. "playercreated, "
    var = var .. "playercreated, "
 end)

-- load is the 1st event called when you load a game
game.onload
(function()
    glob.var = glob.var .. "load, "
    var = var .. "load, "
end)

-- tick is an event called with each tick when a game is running. 1 tick is
-- 1/60 second.
-- Here we print all our variable. The first tick occurs after the other
-- events we used here (either init or playercreated or load).
game.onevent
(defines.events.ontick,
 function(event)
    -- do_print is set to true at the beginning of the script
    if do_print then
       dbg("glob.var = " .. glob.var)
       dbg("var = " .. var)
       -- do_print is set to false, so we won't print when other ticks occured
       do_print = false
    end
 end)

-- my little function to print a message on the screen.
-- WARNING: since it uses game.player, it CAN'T be called by oninit, which
-- occures before onplayercreated
function dbg(msg)
   game.player.print(msg)
end
Now create a new game, save it, load it. What can we see ?
When we start a new game, we get

Code: Select all

glob.var = init, playercreated,
var = init, playercreated,
What happened ?
The game started, so it reads our file.
The 2 variables are initialized to empty strings.

Code: Select all

glob.var = ""
var = ""
Since it's a new game, the event 'init' occurs:

Code: Select all

glob.var = glob.var .. "playercreated, "
var = var .. "playercreated, "
followed by 'playercreated':

Code: Select all

glob.var = glob.var .. "playercreated, "
var = var .. "playercreated, "
load event is not called.

Now, we save the game and load it.
This time we get

Code: Select all

glob.var = init, playercreated, load,
var = load
Why ?
Again, the variables are initialized to empty strings (in fact, glob isn't initialized, see later)

Code: Select all

glob.var = ""
var = ""
Since we loaded a game, neither 'init' or 'playercreated' occurs.

The event 'load' is called

Code: Select all

glob.var = glob.var .. "load, "
var = var .. "load, "
Why glob.var and var are different ?
'glob' is a special variable in factorio, it has 2 properties:
- it's value remains when we save a game.
- when you load a game, every affection outside a function / event is ignored,
so it's not reset and keep it's previous value. 'glob.var' was set to
"init, playercreated, ", so now it's "init, playercreated, load, ".
'var' is a normal variable, so it's reset to an empty string, then set to
"load, ".



I'm done for now. I hope it's help to understand how to set and keep your data.

I'll add some others parts, like "gui", and
"multiplayer" (I need more informations for this one)
Should I create a new topic for each part and keep this one as a table of contents?
Last edited by jerem0883 on Thu Feb 05, 2015 10:45 am, edited 3 times in total.

jerem0883
Burner Inserter
Burner Inserter
Posts: 6
Joined: Sat Dec 06, 2014 1:20 pm
Contact:

Re: about modding : new tutorials

Post by jerem0883 »

A little about GUI.
  • main anchors, create and destroy a button
followed by
  • callbacks (actions triggered by buttons)
You can anchor gui elements to 3 places
top:
top_anchor.jpeg
top_anchor.jpeg (37.43 KiB) Viewed 4474 times
left:
left_anchor.jpeg
left_anchor.jpeg (37.38 KiB) Viewed 4474 times
center:
center_anchor.jpeg
center_anchor.jpeg (37.27 KiB) Viewed 4474 times
and alltogether:
all_3_anchors.jpeg
all_3_anchors.jpeg (36.67 KiB) Viewed 4474 times
I used 2 buttons each time to show how they organise.

These screenshots are generated with the following code:

Code: Select all

require "defines"

local create_gui, destroy_gui

game.onevent
(defines.events.onplayercreated,
 function(event)
    create_gui()
 end)

game.onload
(function()
    destroy_gui()
    create_gui()
 end)

game.onsave
(function()
 end)

local create_top_button, create_left_button, create_center_button

function create_gui()
   create_top_button()
   create_left_button()
   create_center_button()
end

function destroy_gui()
   local buttons =
   {
      game.player.gui.top["top_button_1"],
      game.player.gui.top["top_button_2"],
      game.player.gui.left["left_button_1"],
      game.player.gui.left["left_button_2"],
      game.player.gui.center["center_button_1"],
      game.player.gui.center["center_button_2"],
   }
   for i = 1, #buttons do
      if buttons[i] ~= nil and buttons[i].valid
      then
         -- button ~= nil checks the 'button' variable has a value.
         -- button.valid checks the value is right (it can be wrong if you
         -- destroyed the button somewhere else).
         buttons[i].destroy()
      end
   end
end

function create_top_button()
   game.player.gui.top.add
   ({
       type = "button",
       caption = "top button 1",
       name = "top_button_1",
    })
   game.player.gui.top.add
   ({
       type = "button",
       caption = "top button 2",
       name = "top_button_2",
    })
end

function create_left_button()
   game.player.gui.left.add
   ({
       type = "button",
       caption = "left button 1",
       name = "left_button_1",
    })
   game.player.gui.left.add
   ({
       type = "button",
       caption = "left button 2",
       name = "left_button_2",
    })
end

function create_center_button()
   game.player.gui.center.add
   ({
       type = "button",
       caption = "center button 1",
       name = "center_button_1",
    })
   game.player.gui.center.add
   ({
       type = "button",
       caption = "center button 2",
       name = "center_button_2",
    })
end
About the code:

the gui is created on playercreated event and load event.

A button is created by

Code: Select all

game.player.gui.top.add
({
    type = "button",
    caption = "top button 1",
    name = "top_button_1",
 })
Here we say the button belongs to top (not left, nor center).
'type' indicates it's a button (not a frame, not anything else)
'caption' is the string we see on the button on the screen
'name' is used to access to the button, or to create a callback (to be seen later).

To access a gui element, there's 2 ways :
use a variable

Code: Select all

my_button = game.player.gui.top.add
({
    type = "button",
    caption = "top button 1",
    name = "top_button_1",
 })
my_button.destroy()
or use it's name

Code: Select all

game.player.gui.top.add
({
    type = "button",
    caption = "top button 1",
    name = "top_button_1",
 })
game.player.gui.top["top_button_1"].destroy()
I recommand to use the name, not the variable. It's too much bother when you
destroy it, and have to check every variable everywhere if it's valid or not.
Names should only be used localy inside a function, like if you create a frame
and add elements to it.

On 'load' event we have to destroy the gui before recreat it:
If you load a game you didn't just save, the gui doesn't exists;
If you save a game, 'load' event is called, so it create the gui (again).

So we first destroy it, then creat it. When we destroy it, we check if it exists.

It's done by

Code: Select all

if button ~= nil and button.valid
then
   -- button ~= nil checks the 'button' variable has a value.
   -- button.valid checks the value is right (it can be wrong if you
   -- destroyed the button somewhere else).
   button.destroy()
end

Next episod later :)

  • About callbacks (actions triggered by buttons)
Callback is the name given in software developpment to an action triggered by
gui elements (or by something else).

In factorio, we use the event 'onguiclick'. A callback can be set for a
clicked button, or for most of the other gui elements, like a frame or a table.

Here is a simple example. We build a button which caption is the number of
time we clicked on it. In Lua, numbers are converted to string automatically.
A second button is used to print to the screen the caption of the first one.

Code: Select all

require "defines"

local click_number = 0
-- first we define our 2 buttons, named 'my_button_1' and 'my_button_2'.
-- names are mandatory for using callbacks
game.onevent
(defines.events.onplayercreated,
function()
    game.player.gui.center.add
    ({
        type = "button",
        caption = click_number,
        name = "my_button_1"
     })
    game.player.gui.center.add
    ({
        type = "button",
        caption = "print caption",
        name = "my_button_2"
     })
 end)

-- then we create our callbacks

local function update_caption()
   click_number = click_number + 1
   game.player.gui.center["my_button_1"].caption = click_number
end

local function print_caption()
   game.player.print(game.player.gui.center["my_button_1"].caption)
end

game.onevent
(defines.events.onguiclick,
 function(event)
    if event.element.name == "my_button_1" then
       update_caption()
    elseif event.element.name == "my_button_2" then
       print_caption()
    end
 end)
It's done :)
Last edited by jerem0883 on Wed Feb 04, 2015 10:38 pm, edited 4 times in total.

dee-
Filter Inserter
Filter Inserter
Posts: 416
Joined: Mon Jan 19, 2015 9:21 am
Contact:

Re: about modding : new tutorials

Post by dee- »

+rep because making tutorials is a lot of work and needs dedication while giving so much back to the community. Many thanks! :)

jerem0883
Burner Inserter
Burner Inserter
Posts: 6
Joined: Sat Dec 06, 2014 1:20 pm
Contact:

Re: about modding : new tutorials

Post by jerem0883 »

You're welcome :)
thx for your support !

User avatar
FreeER
Smart Inserter
Smart Inserter
Posts: 1266
Joined: Mon Feb 18, 2013 4:26 am
Contact:

Re: about modding : new tutorials

Post by FreeER »

Nice work, you should place these on the wiki so they can be found easily later (also you can split them up more easily into relevant sections) :P
A few things I'd like to mention
  • in the first code block you initialize glob.var and var to "a" not to "" as you show later
  • the first 'instruction' is really to get the defines.lua file and assign it to a variable in the mod's Lua environment (by default to the name of the file, 'defines', but can be assigned to any variable, local or global, you like with var = require "defines"), not the initializations of glob.var and var :)
  • Since you initialize glob.var to "a" (or "") outside of any event/function that will run everytime the mod is loaded, as such it shouldn't actually retain it's value.

jerem0883
Burner Inserter
Burner Inserter
Posts: 6
Joined: Sat Dec 06, 2014 1:20 pm
Contact:

Re: about modding : new tutorials

Post by jerem0883 »

Thx for the review, it's corrected.
I'll see for placing it in the wiki.

katyal
Fast Inserter
Fast Inserter
Posts: 208
Joined: Wed Nov 12, 2014 8:42 pm
Contact:

Re: about modding : new tutorials

Post by katyal »

Thanks for these, they are greatly appreciated :D

wahming
Fast Inserter
Fast Inserter
Posts: 192
Joined: Sun May 17, 2015 8:30 pm
Contact:

Re: about modding : new tutorials

Post by wahming »

Helpful. You really should link to them in the wiki! I found them purely by chance.

Post Reply

Return to “Modding help”