[GUIDE] To Modding Factorio 0.3.x-0.7.x

Place to post guides, observations, things related to modding that are not mods themselves.
Post Reply

Did this help you?

yes
20
61%
a little
7
21%
no
1
3%
Too Long;Didn't Read
5
15%
 
Total votes: 33

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

[GUIDE] To Modding Factorio 0.3.x-0.7.x

Post by FreeER » Fri Apr 12, 2013 10:18 pm

Welcome to the Factorio Modding Guide version 0.7.5

Once again, because I still am not, I'll say right now that I am in no way an expert on either lua or Factorio so there may be (read: very likely are) mistakes, Please let me know if/when you find them so I can edit this and not mislead people.

Also the current Factorio version is 0.7.5 (this guide was originally for 0.3). If you are reading this after a new update has been released and this has not been updated BEWARE OF POSSIBLE CHANGES. Also for those of you that need just a bit of help a very good way to learn lua is to look at the Programming in Lua book (first edition (lua 5.0) can be read online here. It is possible to get the current (3rd edition lua 5.2) here). These wiki pages are also very very useful Lua/Objects - for properties and methods of entities and Lua/Events - for the names of events and the variables they have.

For those who need something a bit more hands on than simply looking at existing code, there also is a step by step mod on the wiki, located here that you can see how a mod is made and easily download (or recreate) and play with it. Now that that is out of the way, here it is:

First, I'd like to say again that I am not an expert on this :). Second I use Notepad++ for modding (and Adobe Photoshop CS6 for the graphics). I also happen to be using the Windows 7 64 bit OS (on a laptop, not that it matters much). As such any locations I give as to where to find files may, or may not, be accurate. In relation to my second 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++) do a Google search and I'm sure you will find alternatives however.

Next I will say there are two things, once you start with lua, that are really really helpful.
First the wiki (go there, read it, especially the Modding and Scripting section lol): https://forums.factorio.com/wiki/inde ... =Main_Page
Second Factorio uses lua files to add content (as well as images). The game files are found (on my computer) in C:\Program Files\Factorio\data (On mac I BELIEVE it is ~/Library/Application\ Support/factorio/data, on linux systems I BELIEVE it is ~/.factorio/data). Within the data folder you have the base and core folders.
Base contains the data that come with Factorio (inserters, furnaces, the player entity, etc.)
Core contains the rest of the data that comes with the game that is not mod specific (ie. will typically be the same regardless of what mods or campaign you play, fonts, lua code (such as freeplay.lua for new games, and the story code the devs use with their campaigns), sounds, and graphics

The campaign files are found under data\base\campaigns, there you have demo and beta, within both of those there are the levels folders (the individual missions) inside of each level is a control.lua. These are the lua files that control the campaigns. It can be very useful to look through these (especially when you are first learning) to see how the devs did something :) The blueprint.dat file is the actual map that is loaded when someone plays the campaign, it contains the location of everything from the tiles (ie grass/hills/water) to creepers/spawners to the player and everything else. the locale folder contains the translation from code variables to what is seen in-game (so you don't see 'Unknown Key:"entity.biter-spawner-mk2"' but instead "Killer Biter Spawner") as well as story messages and languages (to change the name of the basic-inserter to (de) Knickarmroboter).

Mods go inside the mods folder (on Windows either %appdata%\Factorio\mods or the install directory if you are using the zip. If it's in %appdata% there is a shortcut in the install directory instead of a folder). In the mods folder there is a modlist.json file, this is ONLY to disable the mods without removing them completely. Factorio will automatically find mods placed in the mods folder and add them to the modlist.json file on its own (though that can be disabled). However, you can do this ingame as well (usually :))

Beyond that info, use the forums (read what people have said and look at how they wrote their mods) and experiment, and not necessarily in that order (you will practically ALWAYS learn more from figuring things out on your own, but it will almost always take more time to do so :)). If you get completely stuck (which can happen from a simple change that wasn't documented, or weird errors, lol) go ahead and make a post on the forums, I'd 99% guarantee that someone will help you :)

On to making a mod!
--First off, (straight from wiki) "Every mod is required to provide certain meta information. Let's say the mod name is foo-mod. Then the mod meta information are stored in the mods/foo-mod/info.json file. The file is supposed to contain a map with following fields: name*, author*, version*, title*, homepage, description. The fields with an asterisk are required. The name field is required to have a same value as the name of the directory for now. The version field is in the format "x.y.z" where x is the major version y is the middle version and z is the minor version" (z added in 0.4). Mod dependencies were added with update 0.4. The basic dependency (though if missing entirely this will be default) is "dependencies": ["base"],

An example would be:

Code: Select all

{
  "name": "RemoteInterfacesTest",
  "version": "0.1",
  "title": "Testing Mod for Remote Interfaces",
  "author": "FreeER",
  "description": "Simple mod to test remote interfaces between mods."
   "dependencies": ["base >= 0.4.1", "scenario-pack", "? bar = 0.3"],
}
The dependencies line states: the Mod base must be active in the version greater or equal to version 0.4.1, the mod scenario-pack must be active in any version, and if the bar mod is active it is required to have exactly version number 0.3.

This info file is the one I used for testing with remote interfaces between mods :) It's not an actual mod that does anything so no you won't find it in the forums mod section lol. Now the next file that will (almost always) be included with mods is data.lua. Small mods can easily get away with just these two files and a control.lua if they want in-game code (which is explained below). Only the largest mods really need to layout files in the manner that the devs have done with the base 'mod', Though I like the layout because A) it is easy to tell exactly where everything is if B) if someone want to look through it C) I'm used to it from pre 0.3.x and D) It's simply more orderly in my opinion.

Now to start with the prototypes guide, I'll first say that an 'entity' is anything that you see in the world, except for the ground itself and the items that you drop on the ground. Anything that you see in your inventory is an 'item'. The ground itself (just for reference) is called a 'tile'. I'll show you a few examples of 'prototypes' which is the data behind every object in the game (including the player). We'll start with my standard (the stone furnace), because once you understand it you can create a faster furnace and who doesn't want that :)

Code: Select all

  {
    type = "item",
    name = "stone-furnace",
    icon = "__base__/graphics/icons/stone-furnace.png",
    flags = {"goes-to-quickbar"},
    group = "production",
    order = "h-b-a",
    place_result = "stone-furnace",
    stack_size = 64
  },
--First there is the type, item says that it is an item that goes into the player's inventory (and can be crafted, assuming there is a recipe for it, which I'll show next). Next you have the name of the item (doesn't HAVE to be the same as the entity as far as I know but not sure why you would want it to be different lol), while you could probably use spaces here it would make it more difficult to reference it later so use a - or _. next you have the path to the icon image, the picture that you see when the stone-furnace is in your inventory (and on the crafting menu), this is where it gets 'slightly' tricky. Notice the __base__, you must place the name of your mod between underscores with two on each side. This is because it actually uses the name here as a variable (I'm 99% certain) that is stored in the modlist.json file for the full path. This means that regardless of where the factorio folder actually is all you have to do is place __yourmodname__ (or that of another mod, say base for example) and then reference your graphics (and other resources) from your mods folder (if this was not the case you'd have to have type the full path C:\Program Files\Factorio... but there are several different install paths, depending on OS, thus this would be difficult) be grateful for this :) Next you have flags, this will either be "goes-to-quickbar" or "goes-to-main-inventory" depending on if you want it to go into a free slot on the players quickbar when they craft it or simply their inventory. After flags, you have the order, to be honest I rarely even look at it (I copy stuff *face of shame*) but it determines the order items are sorted in the player's inventory and goes in alphabetical order, thus a is before b and a-e is after a-d.

Now the really important part (especially if you copy and paste and then change the relevant info lol) place_result = "entity_name". THIS is where you put the name of the entity that we'll go over in a minute, if you are creating an item (like iron plates or alien artifacts) that players will not be placing on the ground you actually skip this line. and last is the stack_size, fairly obvious this is the maximum size that one slot in an inventory can hold.

Now onto the recipe code :) :

Code: Select all

  {
    type = "recipe",
    name = "stone-furnace",
    ingredients = {{"stone", 5}},
    result = "stone-furnace"
  },
Again we see type, and as we might expect it says recipe, then we have the name of the recipe (it does not have be the same as the item since it is possible to have duplicate recipes for items, and you can not have two recipes with the same name. Note, do not create cyclic recipes (ie, iron requires iron-plate, iron-plate requires iron. Factorio will complain...). Anyways, the next is fairly obvious, ingredients = stone, 5. Now you'll notice all the {}. This is so you can specify multiple ingredients. Each ingredient goes within it's own 'block' and seperated by commas (','). and then the IMPORTANT bit, result, just like place_result in the item description above, only this is where you specify the name of the 'item' that you want the player to receive when they use this recipe :) Don't copy this change the name and ingredients then forget to change the result, you'll get laughed at I'm sure :)

And finally the part some of you have been waiting for! The entity description!

Code: Select all

 
{
    type = "furnace",
    name = "stone-furnace",
    icon = "__base__/graphics/icons/stone-furnace.png",
    flags = {"placeable-neutral", "placeable-player", "player-creation"},
    minable = {mining_time = 1, result = "stone-furnace"},
    max_health = 150,
    corpse = "medium-remnants",
    resistances = 
    {
      {
        type = "fire",
        percent = 80
      },
      {
        type = "explosion",
        percent = 30
      }
    },
    collision_box = {{-0.7, -0.7}, {0.7, 0.7}},
    selection_box = {{-0.8, -1}, {0.8, 1}},
    smelting_categories = {"smelting"},
    result_inventory_size = 1,
    smelting_energy_consumption = 3,
    smelting_speed = 0.5,
    source_inventory_size = 1,
    energy_source =
    {
      type = "burner",
      effectivity = 1,
      fuel_inventory_size = 1,
      emissions = 0.01,
      smoke =
      {
        {
          name = "smoke",
          deviation = {0.1, 0.1},
          frequency = 0.5,
          position = {0, -2.3}
        }
      }
    },
    drawing_scale = 1,
    on_animation =
    {
      filename = "__base__/graphics/entity/stone-furnace/stone-furnace.png",
      priority = "extra-high",
      frame_width = 81,
      frame_height = 64,
      frame_count = 1,
      shift = {0.5, 0.05 }
    },
    off_animation =
    {
      filename = "__base__/graphics/entity/stone-furnace/stone-furnace.png",
      priority = "extra-high",
      frame_width = 81,
      frame_height = 64,
      frame_count = 1,
      shift = {0.5, 0.05 }
    },
    fire_animation =
    {
      filename = "__base__/graphics/entity/stone-furnace/stone-furnace-fire.png",
      priority = "extra-high",
      frame_width = 23,
      frame_height = 27,
      frame_count = 12,
      shift = { 0.078125, 0.5234375}
    },
    fast_replaceable_group = "furnace"
  },
Ok lets break this down. You see type yet again, only this time while you might have been expecting it to say 'entity' it says furnace. This is because entities are based off the types that the devs have already made (until they come up with a way for us to make our own). To find the types just look through the entity files or look on the wiki here. If none of them match what you want to do you'll have to get creative with the ingame lua (just like most of the mod authors had to do). I'm going to skip the name and icon since they are the same as above, but flags are slightly different this time.

It should be fairly obvious what they mean, but the possible flags are: "pushable", "placeable-player", "placeable-off-grid", "placeable-neutral", and "placeable-enemy". Pushable means it can be moved pushed by the car, and placeable-player/enemy/neutral means it can placed (belong to) either 'force'. placeable-off-grid refers to the fact that most entities are placed on an invisible 'grid' within the world, entities with the placeable-off-grid flag do not have to line up with this grid (like trees, players, biters, and land-mines).

minable is of course the time it takes to mine (I believe with a iron-pickaxe) and the item you get when you mine it. Of course if it is not minable (ie, player, biter, biter corpse, etc.) you skip the minable line. max_health is the health of the entity when placed down or healing.

The corpse is the 'corpse' type entity that gets spawned when the entity dies. I do not really understand the resistances (I have not played with them) but I assume that 80% means it resists 80% of any fire damage (or only takes 20% of fire damage, however you'd like to look at it)

The collision box and selection box are important because the selection is the blue outline that shows up when you mouse over an entity and the collision box is what actually prevents you from running through something. Of course if you want players to be able to walk through an entity, leave the collision box off, however unless you do not want players to be able to pick an entity up you need a selection box (in that case don't use minable lol) :) I believe the numbers correspond in some manner to the size of the graphic (I belive 32 pixels are 1 tile) but honestly I just play with these numbers until it looks right :)

smelting_categories is semi-important :) If you simply want it to work as a regular furnace leave it as smelting, but you can create your own categories with

Code: Select all

type = "recipe-category",
    name = "smelting"
and make it so only your special furnace can smelt say, uranium into refined uranium or something :). Assembly machines have the same line for the same reason. Now up until the energy source block it's fairly self explanatory by the names :) so let's move on the the energy sources.

The energy source type can be burner, meaning it uses coal and has the values you see above (again fairly self-explanatory but play with the numbers if you like), or type = "electric", with input_priority = (secondary or primary or tertiary) and buffer_capacity and input_flow_limit and resting_consumption_ratio

The smoke is fairly easy to understand, play with it if you do not understand just by looking at it :) Drawing scale is, I'm fairly certain, the scale at which the entity graphic is drawn (say you created a large image so you could work with it easier, rather than resaving it smaller you can use the drawing_scale).

Then you give it the path/location to the pictures and or animations, in the case of the furnace there are three animations on/off_animation and fire_animation.

fast_replaceable_froup is the entity 'type' that can be replaced by the furnace (or can replace the furnace) without having to mine it first. And that's it, it may seem like alot if you are brand new, but once you have played with it it is really pretty easy to understand. The rest of the prototypes are really similar and any differences are obvious simply from the name, like attack_cooldown from unit entities, and spawning_radius for the unit-spawner.

Now, if you were to open factorio\data\base\scenarios\freeplay\freeplay.lua you would be looking at the lua code that is run whenever you start a new game. Two things before we jump into the Lua code. In lua 'if' statements (sometimes called if-then statements) MUST include 'then' and MUST be ended with 'end' . Second, if you see '--' anything on the same line after '--' is a comment and is ignored by lua, this '--[[multiline-comment--]]' is also a comment but everything in between the '--[[' and the '--]]' is ignored, regardless of how many lines are in between. The basic syntax of an if statement is:

Code: Select all

if something == somethingElse then --[[if something is equal to somethingElse then (note: you can also use ~= as not equal, > as greater than, < as less than, >= as greater than or equal to, and <= as less than or equal to)--]]
  --place the code you want executed when something is equal to somethingElse here
end
Notice the double '==', lua uses a single '=' to SET a variable, and a double == to COMPARE a variable to something else. 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) Note that you can also do 'equations' in the if statement (if x%2 == 0 or if the remainder of x / 2 is 0, this is x is 2,4,6,8,10,12, etc.)

Now then onto a bit of code:
When you open freeplay.lua you will see

Code: Select all

    require "defines"
Then you will see

Code: Select all

normalattacksentevent = game.generateeventname()
landingattacksentevent = game.generateeventname()
These are examples of setting variables to equal something. In this case the devs are setting normalattacksentevent to equal what the game.generateeventname() function returns to it and the same for landingattacksentevent (generateeventname lets modders create their own events).

This is left over from how the devs let other modders find out if the player is currently being attacked by creepers, either during a normal creeper wave or from the 'landing wave' (sent when the player places the rocket defense down and must then defend it). The rest of this is part of the next bit of code that starts with remote.addinterface, but I'll actually be skipping because it is slightly more complicated than it looks and it is intended for your mod to interact with other mods (this bit of code specifically for interacting with the freeplay.lua code). We WILL come back to this later, but for now let us move on to some code we can use to actually create our own mod before we worry too much about adding interfaces with other mods.

We'll be skipping down to line 39 (if you are using an editor that allows you to see line number it will be easy to find, other wise here is the code, you'll just have to scroll down a bit until you see it)

Code: Select all

gt = game.gettext
game.oninit(function()
  local character = game.player.character
  character.insert{name="iron-plate", count=8}
  character.insert{name="pistol", count=1}
  character.insert{name="basic-bullet-magazine", count=10}
  initattackdata()
  game.showmessagedialog(gt("msg-intro"))
end)
This is the code that is ran whenever a new world is generated. The most important part is the game.onit(function() line. This is what tells the game that on the initialization of the world execute the following (but never again, this is why it is often used to give players items when they first enter the world, as well as to setup the variables that you will use throughout the rest of your mod): In the code you again see a variable being assigned. gt = game.gettext is simply making a faster/easier way to call game.gettext. The next variable assignment is different though, you see local as the first word in the line. To explain local I must explain that there are two 'scopes' that variables can reside within, Global variables and Local variables. Global variables can be accessed from anywhere in your code but local variables can only be accessed within their 'scope' which is within the block of code where they were assigned/created. As such the local character variable only exists within the game.onit(function() block of code, after the block is ended with 'end)' the local character variable no longer exists.

To continue the variable discussion there are 4 (maybe 5) 'datatypes' in lua. You have your typical numbers (which can be integers with a -/+ sign and can be floating like 1.246578, or just your standard number 5), then you have strings (which are any bit of text "this is a string", "as is this"), what can be useful and confusing about lua is that it can sometimes change numbers into strings and strings into numbers depending on how a variable is being used. If you have a string of "513" and want to add 32 to it lua would have no problem doing so because it can recognize that the string is made entirely of numbers. Now I said there were 4 types of data, the other two are booleans (either 'true' or 'false') and tables or arrays (just like you would expect a table to do, it can contain other datatypes within itself, including other tables). Tables are created using tableName = {key=value} or tableName = {value1, value2, etc.} (where key is usually a short recognizable name for what the value is of, if you do not specify a key then it is automatically assigned a number in consecutive order 1,2,3,etc.). Tables are referenced by their name and their data like so: tableName.secondTable["key"] (which would give you the value of key from secondTable, that is within tableName).

All other variables are assigned using variableName=value/data. There is one other 'type' which is 'nil', any variable that has not been defined (ie. does not exist) has a value of nil. To delete a variable all you have to do is set it equal to nil like: variableName = nil (more info on variables can be found here: http://www.troubleshooters.com/codecorn ... iables.htm)

Now If you are still following along after that long (and probably somewhat rambling) paragraph about variables datatypes and scopes, you can understand that

Code: Select all

local character = game.player.character
is a variable named 'character' that only exists within the game.oninit(function() block (and any blocks that are created within it, such as blocks of if statements) and is equal to the data contained in the table named character which is within the player table which (as you may guess) is within the table named game. Now why define the character variable (well the only reason I can honestly see) is to shorten the amount of typing (and thus potential mistyping/errors) so instead of having the next three lines start with game.player.character... all you need to type is character...

Now what does game.player.character.insert{name="iron-plate", count=8} do? Well insert is a function that (like it's name says) inserts what ever you tell it to into an inventory. In this case it is associated with game.player.character (which is the player you see running around on the screen) and thus it inserts the count (8) of the item name ("iron-plate") into the player's inventory (note that the name of the item MUST be within the "" because the function expects a string, without the quotes it assumes that what you gave it was a variable name). Since it is within the onit block this code is ran (as I said at the beginning of that long paragraph above) when the new world first loads. Thus what happens is that when you start a new world you (almost immediately) are given 8 iron plates, a pistol, and 10 magazines of basic ammo. The next thing that happens is that the game runs the function initattackdata (which if you read the function above, sets the first attack wave to be five enemies and to start in 216000 ticks or 3600 seconds or 60 minutes) and then the game pops up a message telling you to 'prepare the planet for colonizing forces' and 'protect [yourself] from the natives'.

The next bit of coding may help explain if statements a bit more so lets go over it:

Code: Select all

-- for backwards compatibility
game.onload(function()
  if glob.attackdata == nil then
    initattackdata()
    if glob.attackcount ~= nil then glob.attackdata.attackcount = glob.attackcount end
    if glob.untilnextattacknormal ~= nil then glob.attackdata.untilnextattack = glob.untilnextattacknormal end
  end
end)
Now first I want to point out that the glob table is a [/u]VERY[/u] special and useful table. I'll explain now why. Any table/variable that you create that is NOT contained within the glob table is NOT SAVED with the game. Meaning that if you create a variable and need to use it even after the player saves, closes Factorio, and then loads their game again, you MUST place it within the glob table for it to be saved/loaded (and I'd suggest creating your own table within glob and then create your variables within your table just to avoid any possible complications)
slpwnd wrote:The saving/loading mechanism is meant for plain lua structures and factorio data. Serializing functions (though possible) can cause issues. Well plain functions should be fine, however closures will most probably cause issues. Just a sidenote the library we use for serialization is called serpent. It is actually required by default for every script run in factorio. It can also be used for pretty printing the variables. Something like:

Code: Select all

game.player.print(serpent.block(variabletoprint))
For details have a look at: https://github.com/pkulchenko/serpent

Now to the code, this time we are using onload, which as you might guess from the name occurs whenever the data is loaded (ie from a previous save). What the code does is as soon as it is loaded it checks to see if attackdata exists (if a variable is equal to nil then it has not been defined and thus does not exist) if it does not then it runs initattackdata() and then checks to see if attackcount is NOT equal to nil (glob.attackdat.attackcount I THINK is the pre 0.3.x location of the number of enemies that attack in the next wave. Thus if the old location is being used we need to change it to what the new value is) the same occurs for untilnextattacknormal. After that both the original if block and the onload event function is closed/ended. This is an important piece of code (especially for when you update your mod) because IF/WHEN you change the location of one/several of your variables you must expect that someone will update and expect to continue from their previous save (unless you explicitly say on your page that the new version is incompatible with previous versions of your mod and you will probably still[\b] get a couple who didn't read your page lol).

Finally we get down to the code block starting with

Code: Select all

game.onevent(defines.events.ontick, 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 (you can get a list here. If you noticed the word event within the function(), pat yourself on the back, event is a parameter of the onevent function. When the game calls onevent() it passes a variable to onevent and that variable exists within the onevent code block for you to use in anyway necessary (infact when you call game.onevent you are passing two variables, the id of an event, and a function for it to run). Now I could go through all of this line by line, but for the most part it is pretty much self explanatory. One thing I will note is that math.floor is a lua function that rounds a value down, also look in data/core/lualib/defines.lua. But other than that look at the name of the variables/tables and the most obvious thing that comes to you is probably correct.

That is the very basics of lua coding 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. Also watch the forums for people who ask for help and see if you can figure out how why their code does not work or if there is a better way to do it (just playing around with code will help you get better at using it)

One last thing to be aware of, in-game you can type lua commands by pressing the ~ (the key next to the 1 on a standard us keyboard). You can use it to set variables and then use those variables to execute a longer command several times. HOWEVER, you will need to set up a mod interface before you can use any of your mod functions, because the console uses the code described in freeplay.lua (like creating a simple function to give you all the items in your mod for testing purposes).

I'll explain mod interfaces shortly but first:
Some common variables/commands to know (there are probably more, I'll edit this when people yell at me or i remember them)
game.getplayer().print("some text") or game.player.print("test")--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 a message up so that the user has to hit TAB to acknowledge it, use this if the info you are telling the player is something you do not what them to ignore/miss
game.player.character.position.x and game.player.character.position.y --the x and y positions of the player. You probably want to first check if game.player.character then --code end, since you can now play as a 'ghost'/'god'.
game.findentities{topLeftX, topLeftY, bottomRightX, bottomRightY} --gives a table named entities containing all the data from all of the entities found, small example below, to destroy an entity find it and then do entities[key].destroy() Note that you may find the older version where it literally states topleft = {x , y}, bottomright = {x, y}. This has been changed in 0.3.x and it now causes random issues, swapping of the variables, use the NEW version shown on this line.

Useful code aka "cheater chest":

Code: Select all

    game.onevent(defines.events.onbuiltentity, function(event)
        if event.createdentity.name == "wooden-chest" then --if the created entity is a wooden chest
            entities = game.findentities{event.createdentity.position.x - .1, event.createdentity.position.y - .1, event.createdentity.position.x + .1, event.createdentity.position.y + .1}
            for fieldName, _ in pairs(entities) do
            chest=entities[fieldName].getinventory(defines.inventory.chest) end --[[a better method would be chest = event.createdentity.getinventory(defines.inventory.chest) BUT I left the findentities and the for loop as examples for when you need to find the position of something near the player but you don't have it's exact position, simply change the event.createdentity.position.x to game.player.position.x and game.player.position.y--]]
            if chest.isvalid and chest.caninsert(name="iron-plate", count="200"} then --if the chest exists (to prevent accessing after it has been mined/destoryed) and if there is room for 200 iron-plates
                chest.insert({name="iron-plate", count="200"}) --insert 200 iron-plates
                game.showmessagedialog("Gratis") --tell the player it's free
                game.player.character.getinventory(defines.inventory.playerguns)[1].clear() --clear the player's first gun slot
                game.player.character.getinventory(defines.inventory.playerguns)[1].insert({name="rifle-gun", count="1"}) --give submachine gun
                game.player.character.getinventory(defines.inventory.playerammo)[1].clear()
                game.player.character.getinventory(defines.inventory.playerammo)[1].insert({name="piercing-bullet-magazine", count="100"}) --give good ammo
                game.player.character.insert({name="piercing-bullet-magazine", count="200"}) -- give more good ammo
                game.player.character.insert({name="coal", count="200"}) --give coal
                game.player.character.insert({name="car", count="1"})--give a car
            end --end if chest is valid
        end --end if entity=wooden-chest
    end--end onbuiltentity event
You may have noticed 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, however for this it would work just as well using insert{name="iron-plate",count=200}. The block of code that begins with for is a for loop, it will loop through the code after 'do' and before 'end' until the table (entities in this case, generated from the findentities function) is empty. It can also be used like so:

Code: Select all

    for i=1, v do --i is a variable local to the for loop. the syntax of a for loop is for i to v count by x (notice that there is no x in the code, if you do not add the count by (x in this example) variable it is assumed to be 1)
    count=count+1 --increase count by one
    game.getplayer().print(count) --note we are printing 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
Note that v is not defined in this code, unless it was defined before the code block you would need to give the for loop an actual number (say 50) or you would get an error.

Did you notice how the chest had several properties and functions, like valid and position and insert() and perhaps player.print() and player.character? How do you know what entities have what properties and methods/functions? Well you go to the wiki here

Now, remember that code WAY up at the top? The one that began with

Code: Select all

remote.addinterface("freeplay",
Now I try to tell you what it does :lol:

Code: Select all

remote.addinterface("freeplay",
{
  setattackdata = function(data)
    glob.attackdata = data
  end,

  getattackdata = function()
    return glob.attackdata
  end,

  getnormalattacksentevent = function()
    return normalattacksentevent
  end,

  getlandingattacksentevent = function()
    return landingattacksentevent
  end
})
You should now be able to understand that the setattackdata is a variable containing a function (or a function called by the variable name setattackdata) that sets the attackdata in the glob table to the variable data, which is passed to the setattackdata function when it is called like so, setattackdata(newdata). BUT if we wanted to use that function from within the mod the freeplay.lua it could have easily been added (and actually I do not think you CAN call a function defined in an interface from the same mod) outside of the interface but! what we really want to do is let another mod access that function so that IT can change the attackdata. To do this the remote interface is created just like you see remote.addinterface("distinctiveName_UsuallyYourModName", {functionName = function, other functions}). The other mods can then use the command remote.call{"freeplay", "setattackdata", {enabled=true, changevalues=true, distraction=defines.distraction.byenemy, attackcount=2000, untilnextattack=1}} to set the attack data (either higher or lower), probably after checking what it is with remote.call{"freeplay", "getattackdata"} (and probably set to a variable like freeplayAttackData=remote...). Notice that the return, that is needed to return the data however you can only have a return right before an end. You can test if an interface exists simply by using

Code: Select all

if remote.interfaces.freeplay and remote.interfaces.freeplay.getattackdata then your code
But what if you want to add a cheat/test function that you can call from the console? You'd try the obvious right?

Code: Select all

remote.addinterface("yourMod",
{
gimme = function(name, count) --remote.call("yourMod", "gimme", "gun-turret", 10)
        game.player.character.insert{name=name, count=count}
      end,
 }
then go into the console and run remote.call{"yourMod", "gimme"}, what happens? Does it work? GOOD. Oh what do you mean that's not how you would have done it :) Oh, you have done this:

Code: Select all

    cheat = function() --remote.call("RemoteInterfacesTest", "cheat")
            game.player.character.insert{name="car", count=10}
            game.player.character.insert{name="iron-ore", count=10}
	end,
Well that should work too :) But the first will give you more options lol

version 0.3 edited on 5/21 (thanks to the developers for the great game (and especially to slpwnd and kovarex for helping me understand modding, and thus, hopefully, helping you))
version 0.4 edited to 0.7.5 on 11/11
Last edited by FreeER on Tue Nov 12, 2013 3:33 am, edited 2 times in total.
Reason: update for slpwnd's comments and 0.4.x and added poll
<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

FrenchDeath
Inserter
Inserter
Posts: 25
Joined: Sun Mar 17, 2013 4:05 am
Contact:

Re: [GUIDE] To Modding Factorio 0.3.x

Post by FrenchDeath » Fri Apr 12, 2013 10:56 pm

i get a problem with a mod
try to transfer from 0.2.10 to .3 and the game dont use the new item

Here the mode :
http://www.mediafire.com/?li421pcq2ci2abs

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

Re: [GUIDE] To Modding Factorio 0.3.x

Post by slpwnd » Tue May 21, 2013 6:53 pm

Couple of observations / corrections:
FreeER wrote:NOW, you do not (from 0.3.x and on) place your mods in the data folder! When Factorio is launched for the first time it creates a mods folder.
That is correct. The data folder is reserved for the core and official mods (base, scenario pack, etc.).
FreeER wrote:The version field is in the format "x.y" where x is the major version and y is the minor version.
As of Factorio 0.4.x the mod version is actually three numbers. The reason for this is that the base mod and scenario pack mod could correspond to the version of the Factorio binary itself.
FreeER wrote: At the moment the mod can't express it's dependencies on other mods.
The dependencies are there as of Factorio 0.4.x. For details see
https://forums.factorio.com/wiki/inde ... d_ordering
FreeER wrote:This means that regardless of where the factorio folder actually is all you have to do is place __yourmodname__ and then reference your graphics from your mods folder (if this was not the case you'd have to have type the full path C:\Program Files\Factorio... but there are several different install paths, depending on OS, thus this would be difficult) be grateful for this
This can also be used to pull the graphics / sounds from other mods.
FreeER wrote:After flags, you have the order =, to be honest I haven't played with this part (actually I usually don't even look at it when I copy stuff *face of shame*) but I'm assuming that it goes in alphabetical order, thus a is before b and e is after d.
Yes, this is the ordering string. It is used to order items for display in the inventory.
FreeER wrote: And it's fairly obvious what they mean, except for placeable-neutral, honestly I'm not certain what this means lol
Placeable-neutral is any entity that can be placed on the map in the map editor and has a neutral force. Things like trees, resources, regular (non-military) buildings - belts, furnaces, assembling machines, etc. Entities like smoke, arrows, etc. are not placeable and entities like enemy, spawner, turret are placeable_player or placeable_enemy.
FreeER wrote:Due to the changes in 0.3 I believe that the code will be loaded as long as the mod-list.json file has the mod set to 'enabled' (which the game does by default), BUT I'm not certain so you may wish to do your own testing on this
As of 0.4.x the mod has to be both enabled and valid. Enabled is player's setting, while valid is whether its dependencies are allright. For more details have a look at: https://forums.factorio.com/wiki/inde ... d_ordering
FreeER wrote:Two things before we jump into the Lua code.
There is a great book about lua called The PIL ("Programming In Lua"). AFAIK it is the best source of information about Lua out there. I think you can get it for the older versions of Lua (5.1) for free online.
FreeER wrote:Honestly as of right now (and due to the code directly below it) I'm half guessing that it is a way to let other modders find out if the player is currently being attacked by creepers, either during a normal creeper wave or from the 'landing wave' (sent when the player places the rocket defense down and must then defend it)
That is correct. This is part of the concept called script interfaces which allows different mod scripts to communicate with each other. You can read more about script interfaces on the wiki: https://forums.factorio.com/wiki/inde ... interfaces
FreeER wrote:Meaning that if you create a variable and need to use it even after the player saves and then loads their game you MUST place it within the glob table for it to be saved/loaded
The saving/loading mechanism is meant for plain lua structures and factorio data. Serializing functions (though possible) can cause issues. Well plain functions should be fine, however closures will most probably cause issues. Just a sidenote the library we use for serialization is called serpent. It is actually required by default for every script run in factorio. It can also be used for pretty printing the variables. Something like:

Code: Select all

print(serpent.block(variabletoprint))
For details have a look at: https://github.com/pkulchenko/serpent
FreeER wrote: CODE: SELECT ALL
if remote.interfaces.freeplay and remote.interfaces.freeplay.getattackdata then your code
(note: This is currently broken as of 0.3.2. When you use this from the script that defines interfaces on its own the game will crash.)
This is already fixed as of 0.4.



Apart from the small corrections mentioned above I find the tutorial really helpful. The only drawback is probably the length of it. It is quite scary:) I suppose that breaking it down to smaller sections (lua introduction, scripting introduction, general modding overview, example mod walkthrough, etc.) would be way more friendly for the readers.

Post Reply

Return to “Modding discussion”

Who is online

Users browsing this forum: No registered users and 1 guest