Needing some help with control.lua.

Place to get help with not working mods / modding interface.
Post Reply
ATMunn
Inserter
Inserter
Posts: 37
Joined: Thu Jan 12, 2017 2:22 am
Contact:

Needing some help with control.lua.

Post by ATMunn »

Alright. I've been making mods for a little while now, and I have a basic idea of how things work. One thing I don't understand, however, is the control.lua. I'd like to know some basic things you can and can't do with it, as well as something basic to get me started using it, and some common errors to watch out for. Also, I was going to try and see if I could make a basic script in a scenario. This is what I made (after attempting to search around on here a bit)

Code: Select all

local player = game.players[event.player_index]
the_player.force.technologies["steel-processing"].researched=true
However, when I try to load the map this is attached to, I get this error:
...Data/Roaming/Factorio/temp/currently-playing/control.lua:1: attempt to index global 'game' (a nil value)
This confuses me a lot. If it can't even access the game object, how can it do anything?

Rseding91
Factorio Staff
Factorio Staff
Posts: 13204
Joined: Wed Jun 11, 2014 5:23 am
Contact:

Re: Needing some help with control.lua.

Post by Rseding91 »

You need to do your logic inside one of the events. What event are you using for that code?
If you want to get ahold of me I'm almost always on Discord.

User avatar
prg
Filter Inserter
Filter Inserter
Posts: 947
Joined: Mon Jan 19, 2015 12:39 am
Contact:

Re: Needing some help with control.lua.

Post by prg »

You need to do such things in an event handler. Have a look at http://lua-api.factorio.com/latest/ for a list of events and how to register handlers for them (or look at an existing mod for how it's done.)
Automatic Belt (and pipe) Planner—Automate yet another aspect of constructing your factory!

ATMunn
Inserter
Inserter
Posts: 37
Joined: Thu Jan 12, 2017 2:22 am
Contact:

Re: Needing some help with control.lua.

Post by ATMunn »

Rseding91 wrote:You need to do your logic inside one of the events. What event are you using for that code?
prg wrote:You need to do such things in an event handler. Have a look at http://lua-api.factorio.com/latest/ for a list of events and how to register handlers for them (or look at an existing mod for how it's done.)
Ah. I get it now. I changed my code to

Code: Select all

script.on_event(defines.events.on_player_created, function(event)
	local player = game.players[event.player_index]
	player.force.technologies["steel-processing"].researched=true
end)
and it now works.

So, are there some other things I should know? I imagine I will be doing more with scripts in mods than in scenarios. Also, after looking at some other control.luas in other mods/in the base game I noticed a lot of "for x, y in z do" things. How does the syntax work with these, and why is it done this way instead of a normal for loop? (I'm not too familiar with lua.)

User avatar
prg
Filter Inserter
Filter Inserter
Posts: 947
Joined: Mon Jan 19, 2015 12:39 am
Contact:

Re: Needing some help with control.lua.

Post by prg »

Code like

Code: Select all

for key, value in pairs(some_table) do ... end
is simply the easiest way of iterating over all elements of a table. This is the normal way in Lua. There's also a numerical for loop, but it doesn't make this any easier.
Automatic Belt (and pipe) Planner—Automate yet another aspect of constructing your factory!

ATMunn
Inserter
Inserter
Posts: 37
Joined: Thu Jan 12, 2017 2:22 am
Contact:

Re: Needing some help with control.lua.

Post by ATMunn »

Okay. What exactly would "key" be? I've seen some places where it's index, some where it's _, and probably others that I don't remember off the top of my head.


ATMunn
Inserter
Inserter
Posts: 37
Joined: Thu Jan 12, 2017 2:22 am
Contact:

Re: Needing some help with control.lua.

Post by ATMunn »

That will be useful. I'll keep it in mind if I have any non-Factorio-specific Lua questions.

ATMunn
Inserter
Inserter
Posts: 37
Joined: Thu Jan 12, 2017 2:22 am
Contact:

Re: Needing some help with control.lua.

Post by ATMunn »

(Don't really want to bump this thread by posting another comment, however if I just edited my last comment the likelihood that anyone would notice is low.)

So I've been messing around a bit more in a test scenario, and I've figured out quite a few things I can do. I decided to start attempting to create a simple gui. At first I started with just a label, then a button, which both were fine, but then I decided to try and make that button spawn two more buttons, and that's when I started having problems. This is my current code:

Code: Select all

script.on_event(defines.events.on_player_created, function(event)
	player = game.players[event.player_index]
	
	player.force.technologies["steel-processing"].researched=true
	player.insert{name="iron-plate", count=100}
	player.insert{name="coal", count = 50}
	player.insert{name="power-armor-mk2", count = 1}
	player.insert{name="fusion-reactor-equipment", count = 2}
	player.insert{name="exoskeleton-equipment", count = 5}
	
	player.gui.top.add{type="button", name="flamethrowers", caption = "Open flamethrower things menu"}
end)

script.on_event(defines.events.on_gui_click, function (event)
	local element = event.element
	local this_player = game.players[event.player_index]
	local is_gui_open = false
	
	if element.name == "flamethrowers" and is_gui_open == false then
		element.add{type = "frame", name = "flamethrowers_frame", caption = "Flamethrower things"}
		element.flamethrowers_frame.add{type = "button", name = "get_flamethrower", caption = "Get flamethrower"}
		element.flamethrowers_frame.add{type = "button", name = "get_ft_ammo", caption = "Get flamethrower ammo"}
	elseif element.name == "flamethrowers" and is_gui_open == true then
		element.flamethrowers_frame.destroy()
	elseif element.name == "get_flamethrower" then
		this_player.insert{name="flame-thrower", count = 1}
	elseif element.name == "get_flamethrower" then
		this_player.insert{name="flame-thrower-ammo", count = 10}
	end
end)
All the top stuff works just fine. But when I click the button, the frame element just pops up directly on top of the first button and is cut off at the edge of it. And when I click the first button again, I get an error saying:
Error while running event on_gui_click (ID 1)
Gui element with name flamethrowers_frame already present in the parent element.
stack traceback:
...Data/Roaming/Factorio/temp/currently-playing/control.lua:20: in function <...Data/Roaming/Factorio/temp/currently-playing/control.lua:14>
As with most of these errors, I have no idea what it means or why it's there. :P

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Needing some help with control.lua.

Post by Nexela »

local is_gui_open = false

You can't set a variable to false and hope it magically becomes true:)
local is_gui_open = event.element.flamethrowers_frame

Try this without the variable

Code: Select all

script.on_event(defines.events.on_gui_click, function (event)
   local element = event.element
   local this_player = game.players[event.player_index]

   --anything not nil or false is true since we don't really need the variable just check for the element directly
   
   if element.name == "flamethrowers" and not element.flamethrowers_frame then
    local frame = element.add{type = "frame", name = "flamethrowers_frame", caption = "Flamethrower things"} --adding a frame returns the frame lets store it
      frame.add{type = "button", name = "get_flamethrower", caption = "Get flamethrower"}
      frame.add{type = "button", name = "get_ft_ammo", caption = "Get flamethrower ammo"}
   elseif element.name == "flamethrowers" then
      element.flamethrowers_frame.destroy()
   elseif element.name == "get_flamethrower" then
      this_player.insert{name="flame-thrower", count = 1}
   elseif element.name == "get_flamethrower" then
      this_player.insert{name="flame-thrower-ammo", count = 10}
   end
end)
However this goes against my leave gui.top for buttons only rule :)

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Needing some help with control.lua.

Post by Nexela »

ATMunn wrote:Okay. What exactly would "key" be? I've seen some places where it's index, some where it's _, and probably others that I don't remember off the top of my head.
key, index, _ are all just variable names
key and index are typically used to say the variable "key" contains the key/index for the table in the for pairs loop. "key" can be almost any name you want however it helps to keep it consistent to the "what" the key is.
for example if your table is indexed by attributes then calling the key variable "key" or "attributes" is a lot more descriptive then calling it "this_thing"

_ is a valid variable name, however it typically denotes "I don't care about this variable/return from function"
in a common for..pairs loop you will have something like this
for author, books in pairs("library") do

If you didn't care about the author (i.e. the key/index of the table) you could write it as
for _, books in pairs(library) do

If you don't care about the books but just need the authors, then both of these are valid:
for author, _ in pairs(library) do
for author in pairs(library) do -- This is valid because the second return from pairs has nowhere to go so it is just discarded.

ATMunn
Inserter
Inserter
Posts: 37
Joined: Thu Jan 12, 2017 2:22 am
Contact:

Re: Needing some help with control.lua.

Post by ATMunn »

Nexela wrote:local is_gui_open = false

You can't set a variable to false and hope it magically becomes true:)
local is_gui_open = event.element.flamethrowers_frame

Try this without the variable

Code: Select all

script.on_event(defines.events.on_gui_click, function (event)
   local element = event.element
   local this_player = game.players[event.player_index]

   --anything not nil or false is true since we don't really need the variable just check for the element directly
   
   if element.name == "flamethrowers" and not element.flamethrowers_frame then
    local frame = element.add{type = "frame", name = "flamethrowers_frame", caption = "Flamethrower things"} --adding a frame returns the frame lets store it
      frame.add{type = "button", name = "get_flamethrower", caption = "Get flamethrower"}
      frame.add{type = "button", name = "get_ft_ammo", caption = "Get flamethrower ammo"}
   elseif element.name == "flamethrowers" then
      element.flamethrowers_frame.destroy()
   elseif element.name == "get_flamethrower" then
      this_player.insert{name="flame-thrower", count = 1}
   elseif element.name == "get_flamethrower" then
      this_player.insert{name="flame-thrower-ammo", count = 10}
   end
end)
However this goes against my leave gui.top for buttons only rule :)
I see. My intent with that variable was to have it turn true when the button was clicked, however I seem to have forgotten to do that entirely XD I also forgot about the whole "if it's not nil or false it's true" thing.
EDIT: So I went and tested this out, and it no longer gives me an error when I press the button the second time. However, it still doesn't quite work right... This happens.
Image
EDIT2: It appears that I can't make my frame a child of the button, or otherwise what I described above will happen. I changed it to being a child of gui.left and it now works.
Nexela wrote:
ATMunn wrote:Okay. What exactly would "key" be? I've seen some places where it's index, some where it's _, and probably others that I don't remember off the top of my head.
key, index, _ are all just variable names
key and index are typically used to say the variable "key" contains the key/index for the table in the for pairs loop. "key" can be almost any name you want however it helps to keep it consistent to the "what" the key is.
for example if your table is indexed by attributes then calling the key variable "key" or "attributes" is a lot more descriptive then calling it "this_thing"

_ is a valid variable name, however it typically denotes "I don't care about this variable/return from function"
in a common for..pairs loop you will have something like this
for author, books in pairs("library") do

If you didn't care about the author (i.e. the key/index of the table) you could write it as
for _, books in pairs(library) do

If you don't care about the books but just need the authors, then both of these are valid:
for author, _ in pairs(library) do
for author in pairs(library) do -- This is valid because the second return from pairs has nowhere to go so it is just discarded.
That makes sense to me now, thanks. So basically, if you did
for author, books in pairs ("library") do

the variable "author" would store the key each iteration, and the variable "books" would store the value each time (and obviously "library" is the table we're iterating through)
Right?

Also, another miscellaneous thing: I've noticed in your code here and in the base game code as well that the indentation seems a little awkward to me. I always use a single tab every time I indent, but it seems like you're using a few spaces or something. Any particular reason for this instead of using tab?

Post Reply

Return to “Modding help”