[GUIDE] To Factorio LUA 0.2.10

Place to post guides, observations, things related to modding that are not mods themselves.
Post Reply
User avatar
FreeER
Smart Inserter
Smart Inserter
Posts: 1266
Joined: Mon Feb 18, 2013 4:26 am
Contact:

[GUIDE] To Factorio LUA 0.2.10

Post by FreeER »

I wrote this in about an hour or two (but it's late and I'm tired) so there probably are mistakes Please let me know when you find them so I can edit this and not mislead people.
Also this is written for 0.2.10 and earlier. I'm not sure yet exactly how much may change with the coming 0.3 so if you are reading this after 0.3 is out and it has not been updated BEWARE OF POSSIBLE CHANGES. now that that is out of the way, here it is:

First I'll say I am in no way an expert on either lua or Factorio. Second I use Notepad++ for modding (and adobe photshop cs5 for the graphics). I also happen to be using the Windows 7 64 bit OS. As such any locations I give as to where to find files may, or may not, be accurate depending on if you are using the same OS. In relation the my first statement, Notepad++ is only for windows, it has not been ported to linux or mac (though it is open source, GPL license, written in c++).
Next I will say there are two things, once you start with lua, that are really really helpful.
first the wiki https://forums.factorio.com/wiki/inde ... _scripting (go there read it).
Second the Factorios campaign lua files. They are found under factorio\data\campaigns then you have demo and beta within both of those there are the levels folders inside of each level is a control.lua. These are the lua files that control the campagins. It can be very useful to look through these (especially when you are first learning) to see how the devs did something :)
Beyond that use the forums and experiment, not necessarily in that order.

Now, if you were to open factorio\data\lualib\freeplay.lua you would be looking at the lua code that is run whenever you start a new game. This is/will be copied into the player's saves. What this means is that if someone tries to play your mod from their save file it probably WILL NOT WORK, unless you let them know that they need to copy your freeplay.lua (or control.lua as it is called in the save files) into their save directory (which is C:\Users\<user>\AppData\Roaming\Factorio\saves on windows 7 64bit installation, if they have the zip file it is simply Factorio\saves).

One other thing before we jump into the Lua code. When you start an 'if' statement you MUST end it with 'end', you may hear/see it called an if-then statement, this is because without a 'then' there is no code to execute. the basic syntax is:
if something == something then
do this
end
Notice the double ==, lua uses a single = to SET a variable, and double == to CHECK a variable. You CAN NOT use a single = to check if x equals 10. it will give an error! (sorry about stressing this but if you are not used to coding it will cause problems, and you will use if statements alot. If you have some experience then you will understand)
Now then onto a bit of code:
When you open freeplay.lua you will see

Code: Select all

require "util"
require "story"
require "defines"
This tells the game that when freeplay.lua loads it also needs to load util.lua, story.lua, and defines.lua (don't change these unless you know for sure what you are doing)

next you will see:

Code: Select all

game.oninit = function()
end
This is a function that is run at the start of a game. Anything between function() and end will be run once at the start, when I make a mod I usually move the initial giving of items here because it makes more sense to me than having the game keep checking if it has given items already. You'll understand this more in a bit.

Then you will see

Code: Select all

game.onevent = function(event)
Most of your coding will probably go here. Mostly because alot of what you will want to do is based on Events that happen in the game, such as onplayerdied, or onbuiltentity. if you noticed the word event with in the function(), pat yourself on the back, event is a variable that is being passed on to the code that is within the onevent = function() end.
an entity if you do not know is anything, repeat anything, that is generated when the world is (like trees, coal, and creeper spawners) or that you can place 'in' the world (like furnaces, assembling machines, and inserters). The only thing that you will see in the game that are NOT entities are the tiles (the grass/water/'hills' that you walk on) and the items in your inventory (or that of a chest/furnace/etc.)

BTW: a 'block' is a section of code that works together, in an if statement the 'block' of code that is executed is the code between then and end.
The next block of code is another if statement:

Code: Select all

  if event.name == "ontick" then
    if glob.introduction == nil then
      glob.introduction = true
      game.getplayer().insert{name="iron-plate", count=8}
      game.getplayer().insert{name="pistol", count=1}
      game.getplayer().insert{name="basic-bullet-magazine", count=10}
    end
what this says is if the event.name (ie the name of the event which just occured) is ontick (notice the double =s) then give the player some iron plates, a pistol, and some ammo. ontick is used if you need something to be run constantly, like checking if the creepers should be attacking. Anything between the then and the end that closes the if statement are run when ontick is called (which as you might guess is every tick, and there are 60 ticks per second in factorio it might be different in other games but...).

To break this down:

Code: Select all

if glob.introduction == nil then
glob is a table that is loaded when the game is (this mean if you place any tables/variables you need to create inside of the glob table you do not need to worry about if they are loaded or not). introduction is a variable within the glob table and this statement checks to see if it has been defined (nil is the same as saying something does not exist. You can set a variable to nil to delete it from the games memory.) So, if the introduction variable in the table glob has not been defined (which when you first start a game it has not) then it will run the code that is within then and the end statement. You'll notice that the very first line of code is glob.introduction = true this defines the introduction variable so that it is no longer nil (nonexistent) and does not run on the next 'tick'.

The next line is

Code: Select all

game.getplayer().insert{name="iron-plate", count=8}
insert is a function that, well, 'inserts' items into an inventory. In this instance it is inserting 8 "iron-plate"s into the player's inventory. When you want to refer the game to the player you use game.getplayer(). In the campaigns you might see glob.player, this is because in the begining of the control.lua files the devs set glob.player = game.getplayer(). probably because it is simply easier to type than game.getplayer(). This is fine to use as long as you make sure that the game knows what glob.player is (also, if I am wrong on this let me know so I can edit this lol).

Notice that after insert are curly brackets { this tells the insert function that the data is in the form of a table/array. There are four types of variables, there are tables which contain the other three types (though tables can contain tables), then you have you basic numbers (which can be decimal), then you have strings which is text like "This is a string." It is possible to have numbers as a string "This string contains the number 5, but it is still a string type variable", then last you have the boolean type variable, which is very simple. A boolean variable is either true or false, note that if you set a variable this="true" it is a string variable not a boolean variable. There is one other type of variable, there are global variables which lua creates whenever you write:

Code: Select all

var=whatever
and there are local variables which only exit within the block of code where they are defined. To set a local variable you use

Code: Select all

local var=whatever
. Locals variables are useful to keep from cluttering the memory that the game is using in your (and other's) computers. It also allows you to use the same variable name for simple things like counters, that only need to exist within the block that is using them and that is not going to be used anywhere else in your code.

You should now see that the name variable is a string that tells insert to give the player the item named iron-plate (the items are defined in the json files of factorio\data\prototype-definition\item) and that count is a variable containing the number of iron-plate's to insert. Pretty simple right?
After the iron plates the game lua code also gives the player a pistol and some basic ammo.

That's the very basics of lua for factorio. To really learn how to code then you will need to watch the wiki for updated information (especially the events) and simply TRY IT OUT. Another good way is to look at the code that other people have written to see if you can A) understand it and B) see why they did it that way and if how they did it is better than you would have done it.
One last thing to be aware of, ingame you can type lua commands by pressing the ~ (the key next to the 1 on a standard us keyboard). Note that this has a limited amount of typing space, though you can use it to set variables and then use those variables to execute a longer commad.

Some common variables/commands to know (there are probably alot more, i'll edit this when people yell at me or i remember them)
game.getplayer().print("some text") will print text to the screen (very useful when debugging your code, and if you were creating a mission for someone)
game.showmessagedialog("text") Will pop message up so that the user has to hit TAB to acknowledge it.
game.getplayer().position.x and game.getplayer().position.y the x and y positions of the player.


Useful code aka "cheater chest":

Code: Select all

if event.name == "onbuiltentity" then
        if event.createdentity.name=="wooden-chest" then
        entities = game.findentities{topleft = {x = event.createdentity.position.x - .1, y = event.createdentity.position.y - .1}, bottomright = {x = event.createdentity.position.x + .1, y = event.createdentity.position.y + .1}}
        for fieldName, _ in pairs(entities) do chest=entities[fieldName].getinventory(defines.inventory.chest) end
        if chest.isvalid() and chest.caninsert({name="iron-ore", count="200"}) then
            chest.insert({name="iron-plate", count="200"})
            game.showmessagedialog("Gratis")
            game.getplayer().insert({name="rifle-gun", count="1"})
            game.getplayer().insert({name="copper-rifle-ammo", count="200"})
            game.getplayer().insert({name="coal", count="200"})
            game.getplayer().insert({name="car", count="1"})
        end --end if chest is valid
        end --end if entity=wooden-chest
      end--end onbuiltentity

The block of code that begins with for is called a for loop because it will loop through the code after 'do' and before 'end' until the table is empty. It can also be used like:

Code: Select all

for i=1, v do
count=count+1
game.getplayer().print(count) --note we are print the value of the variable count, not the text string count. This is why "" are important when you want to print something to the screen
end
You may also notice that in this example chest.insert({}) has regular curved brackets (also known as parentheses) surrounding the curly brackets {}. This is the proper way to write it since some functions could accept more than one table as inputs.


Also not sure if I mentioned it or not but if

Code: Select all

--anthing
is a comment and the game will not read the rest of the line
if you want to comment out more than one line (for debugging or you just want a long intro to your mod whatever) use

Code: Select all

--[[comment]]
Edit 1: here is a lua tutorial that I've referenced on occasion (this is about the lua language in general not just in factorio). Not sure why I didn't include this in the original post...but it's here now!
http://lua-users.org/wiki/TutorialDirectory
Last edited by FreeER on Fri Apr 12, 2013 10:19 pm, edited 2 times in total.
Reason: Desticky now that the new 0.3.x guide is out
<I'm really not active any more so these may not be up to date>
~FreeER=Factorio Modding
- Factorio Wiki
- My Factorio Modding Guide
- Wiki Modding Guide
Feel free to pm me :)
Or drop into #factorio on irc.esper.net

slpwnd
Factorio Staff
Factorio Staff
Posts: 1835
Joined: Sun Feb 03, 2013 2:51 pm
Contact:

Re: [GUIDE] To Factorio LUA 0.2.10

Post by slpwnd »

Well done ! Nice introduction. I made it sticky.

drs9999
Filter Inserter
Filter Inserter
Posts: 831
Joined: Wed Mar 06, 2013 11:16 pm
Contact:

Re: [GUIDE] To Factorio LUA 0.2.10

Post by drs9999 »

Nice work!
I have just small suggestion.
You should mention that global tables wont be saved.
After loading a savegame the tables are empty.
So if you have to store variables that should be available after loading a game you can use "a third type of variables":
glob.yourtablenamehere (exactly glob isnt a "type" but just a table)
This table will be saved and and loaded automatically.

EDIT: Oh ok just saw that you mentioned the glob-table... But anyway maybe point out the "special" function of it a bit more clearly.

Andrea88
Inserter
Inserter
Posts: 30
Joined: Mon Mar 04, 2013 6:15 pm
Contact:

Re: [GUIDE] To Factorio LUA 0.2.10

Post by Andrea88 »

just a word: awesome!!! :D

ficolas
Smart Inserter
Smart Inserter
Posts: 1068
Joined: Sun Feb 24, 2013 10:24 am
Contact:

Re: [GUIDE] To Factorio LUA 0.2.10

Post by ficolas »

you may hear/see it called an if-then statement, this is because without a 'then' there is no code to execute. 
Is not exactly the reason, in other languages like java, is also called if-then statement, but you dont need to write "then", but anyways, is ok to explain it.

Nice tutorial, im sure it will help a lot of people. :)

User avatar
rk84
Filter Inserter
Filter Inserter
Posts: 556
Joined: Wed Feb 13, 2013 9:15 am
Contact:

Re: [GUIDE] To Factorio LUA 0.2.10

Post by rk84 »

Good work.

I have some opinions. :geek: :)
FreeER wrote: ..., event is a variable that is being passed on to the code that is within the onevent = function() end.
-event is parameter of onevent function. When game calls onevent() it passes a variable to onevent's event parameter.

-I find the global part bit miss leading. I would call bools, numbers, strings and tables datatypes which can be stored either global or local variables.
Test mode
Searching Flashlight
[WIP]Fluid handling expansion
[WIP]PvP gamescript
[WIP]Rocket Express
Autofill: The torch has been pass to Nexela

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

Re: [GUIDE] To Factorio LUA 0.2.10

Post by FreeER »

Oh wow, I've gotten more reponse to this than I honestly expected (at least for overnight response, though admitedly it may not have been night for some of you).
slpwnd wrote:I made it sticky.
I read this and was like :o :shock: .... 8-) lol
ficolas wrote:Is not exactly the reason, in other languages like java, is also called if-then statement, but you dont need to write "then"
Yeah I figured that wasn't the exact reason (not that i've seen a reason except, 'because if you check for something then you want to do something based on the result'). Mostly just wanted to write something for those who might be confused by seeing it called something else somewhere else.
rk84 wrote:-event is parameter of onevent function. When game calls onevent() it passes a variable to onevent's event parameter.
-I find the global part bit miss leading. I would call bools, numbers, strings and tables datatypes which can be stored either global or local variables.
I totally agree now that I look at it, lol. I was tired and wanted to get it up so didn't look over this as well as I should have :) (I also trusted all of you to correct me for any mistake not matter how small :D )
drs9999 wrote:But anyway maybe point out the "special" function of it a bit more clearly.
I'll try to do so when I write 0.3, it is fairly important to know :)

Thanks everyone, I'm starting to think that, unless there is something major that I left out (like if I'd left out what glob was or game.getplayer() or or how to use insert or something).I might leave this like it is until I redo it for 0.3 (that's going to be fairly soon and have lots of changes after all), but I will definitely listen to what all of you have to say and try not to make the same mistakes when I do 0.3's guide :lol:

P.S. this post got long faster than I expected lol
<I'm really not active any more so these may not be up to date>
~FreeER=Factorio Modding
- Factorio Wiki
- My Factorio Modding Guide
- Wiki Modding Guide
Feel free to pm me :)
Or drop into #factorio on irc.esper.net

kwoff
Manual Inserter
Manual Inserter
Posts: 2
Joined: Sat Mar 02, 2013 11:20 am
Contact:

Re: [GUIDE] To Factorio LUA 0.2.10

Post by kwoff »

FreeER wrote: When you want to refer the game to the player you use game.getplayer(). In the campaigns you might see glob.player, this is because in the begining of the control.lua files the devs set glob.player = game.getplayer(). probably because it is simply easier to type than game.getplayer(). This is fine to use as long as you make sure that the game knows what glob.player is (also, if I am wrong on this let me know so I can edit this lol).
I'd guess that it's (also) an optimization, avoiding the overhead of calling a function every time.

Post Reply

Return to “Modding discussion”