Tutorial:Modding tutorial/Gangsir: Difference between revisions

From Official Factorio Wiki
Jump to navigation Jump to search
No edit summary
(Nearly finished with the tutorial)
Line 83: Line 83:
* A text editor, such as Emacs, Vim, Sublime text, etc
* A text editor, such as Emacs, Vim, Sublime text, etc
* An understanding of the tutorial above
* An understanding of the tutorial above
* An understanding of Lua as a programming language. Enough to know the syntax and how it works. If you have prior programming experience, it should not be difficult to pick up.


Once you have all of these things, we can begin.
Once you have all of these things, we can begin.
Line 257: Line 258:
--control.lua
--control.lua


script.on_event({defines.events.on_tick},
  function (e)
      if e.tick % 60 == 0 then --common trick to reduce how often this runs, we don't want it running every tick, just 1/second
        for index,player in pairs(game.players) do  --loop through all players on the server
            if player.get_inventory(defines.inventory.player_armor).get_item_count("fire-armor") >= 1 then --if they're wearing our armor
              game.surfaces[1].create_entity{name="fire-flame",position=player.position, force="neutral"} --create the fire where they're standing
            end
        end
      end
  end
)


</pre>
</pre>
I've used lua comments in the code above to explain each step. It's fairly easy to understand, and it shows how you would get the current armor that the player is wearing, with defines.inventory.player_armor, which is an inventory constant. You can read the list of defines [http://lua-api.factorio.com/latest/defines.html#defines.inventory here].
=== Locale ===
If you've already tried loading up Factorio and trying the mod so far, you may have noticed that the item name of the armor says "Unknown key". This means that Factorio has the internal name, but it doesn't know what it should look like to the user. So, we need to create locale for our mod.
In the mod folder, create a folder called <code>locale</code>, then create another folder inside that called <code>en</code>, then a file called <code>config.cfg</code>.
The directory structure should now look like:
* (user data directory, sometimes called .factorio)
** mods
*** FireArmor_0.1.0
**** locale
***** en
****** config.cfg
**** prototypes
***** item.lua
**** control.lua
**** data.lua
**** info.json
If you know another language, you can also translate your mod by making other language code files inside locale, such as de for german.
Inside config.cfg, paste the following:
<pre style="background-color:#AAFFAA">
[item-name]
fire-armor = Fire armor
[item-description]
fire-armor = An armor that seems to catch the ground itself on fire when you take a step. It's warm to the touch.
</pre>
Notice how this is not a lua file. Locale is handled with C config files, so the format is different.
== The finished tutorial mod ==
Well, the mod is finished. Since this mod is only a tutorial, there isn't much balance to it. Additionally, don't try submitting it to the mod portal as your own, since it's from the Wiki.
However, you're free to take this mod and modify it for your own use, changing recipes, adding technologies, whatever.
== Extended learning ==
One of the best ways to learn how to mod beyond this is to look at other mods. As all mods can be opened and looked at, looking at the mods of experienced modders can help significantly when making your own mod.
=== Keeping your mod working ===
As Factorio evolves, things will change. Previously, you probably ignored the modding part of the changelog, you now need to read it and see if any changes affect your mod(s). If so, you'll need to fix them. If there's something wrong with your mod, the game will fail to init and explain why.
== See also ==
* [[Mods]]
* [[Modding overview]]

Revision as of 19:34, 25 February 2017

This is a modding tutorial for Factorio version 0.15. In this tutorial, the author will explain how Factorio works behind the scenes, how to modify Factorio, where to find documentation, and explain concepts.

Overview

Before we start the tutorial, a few things to note:

Code tinted green like this should be included into the mod this tutorial is going to create; If the reader follows along with it. The best way to do this is to copy and paste, to ensure faithful reproduction.
Whenever code is added to the mod, a Lua comment with the file name will be at the beginning of the green box. Place the code in the box into that file. Eg:
--control.lua
Code tinted purple like this should not be included into the mod, it's just for educational/example purposes, and to boost understanding.

This tutorial was created for version 0.15, so any viewers in the future should take note that some minor changes may have been made, and should look at the changelogs up to the current version.

Terminology used in modding

Before we start the tutorial, a few terms and definitions should be laid out, to ensure the reader understands.

Mod
A script or series of scripts that allow modifications to the game through the API.
Entity
An entity in Factorio is anything in the game that is not a concept, event, or tile. Examples of entities include the character, an assembling machine, a biter, etc. This can be 'machines' or free-moving objects like the character.
Character
The actual entity that the player manipulates the world through.
Player
All the data that defines a player, such as username, position in the player table, etc. All players have characters, but no characters have players within them.
Prototype
A prototype is an internal handling of entities, a bit like a template. It defines stats, what the entity actually is, etc. A prototype is used to create an entity.
Surface
A surface is a bit like a dimention. It is composed of terrain, such as grass, sand, and water, and all the entities on the surface. By defualt, there is only one surface in Factorio, referred to internally as "nauvis", or game.surfaces[1], but mods may create additional surfaces through the API.
Event
An event is a recurring...event, that is triggered internally by the game. There are several events that mods may connect functions to, such as on_entity_died, etc. More on this in the control scripting section.
Recipe
This is a data structure a bit like a prototype that defines a recipe for the internal crafting engine. A technology follows a similar setup.

More terminology may be declared and defined later in this tutorial.

Before beginning to mod

Before we can start modding Factorio, we must understand what Factorio is. You may be tempted to answer in lieu of the about page, but that is what a player would say. Since we are trying to become a modder, we need a more detailed explanation. Factorio is a game that is coded in the language C++, with an API provided by Wube (the developers of Factorio) to mod Factorio in the programming language Lua. This API allows adding scripts to the Factorio init process, to modify it without the source code of the base game being exposed, or modifying memory. This may be different than other games that offer modding, but this is a more professional and proper way of supporting modding.

To aid in the use of this API, the devs have kindly provided fairly comprehensive documentation at their API site. Get used to using this site, as it will become a frequent visit you will make while you develop mods. It contains information on factorio's classes, information on concepts, and information on events that you can hook into. You will need to check this site often, so the author reccomends bookmarking it. In addition to this site, there is also many resources to be found created by the community, such as this tutorial.

Setup

The best way to develop a mod is to develop it in a place where it can be easily tested. When the tutorial gets to making the mod, this will be explained further. Additionally, using an editor that allows ease of typing and Lua language support is reccomended. Emacs, Vim, Sublime Text, and Notepad++ are all viable candidates. This author prefers Emacs, but it does not make a difference in the mod itself.

How Factorio loads mods

The data stage

When Factorio first initializes, it initializes part of itself, then starts looking for mods. Mods are loaded by dependency, then by alphabetical order. This is very important to understand, as it can cause you problems if you neglect it and try to add inter-mod support to your mod.

Factorio has two kinds of dependencies. There are required dependencies, and optional dependencies. Required dependencies are loaded first, always. The game will fail to initialize if one of these is not present. Optional dependencies are loaded first if present, but do not have to be present. This is useful for enabling bonus features if mods are used together. Required dependencies should be used for mod libraries, and similar infrastructure.

This is the most restricted part of the Factorio init, there's not much you can do here other than declare prototypes for technologies and entities. Stuff like manipulating files, affecting the world, etc, are blocked/unavailable. In fact, any functions or changes made will be discarded, as the lua session is terminated. You also cannot mess with the data table, it will error or be ignored. When using data:extend({}), it expects a specific format, more on this later.

When running through this stage, the game looks through all mods for a file called data.lua. This file is executed, then each mod's data-updates.lua, and finally each mod's data-final-fixes.lua. All other files to be loaded will need to be required. All the files run here should contain nothing but prototype definitions. More on requiring files later.

Migrations

Migrations are scripts that are used to "fix" a save after a mod updates. Whenever prototypes change within a mod, migrations must be setup to correct all the old instances of the prototyped entity in the world. This must be done for all updated entities, or the old entities will be removed from the world, which is an unprofessional fallback that makes users dislike you. While this tutorial will not discuss migrations, there are many resources on migrations to be found around the community, and the API site.

To avoid having to write migrations, avoid making changes to prototypes that effect prototype name, type, recipe, or technology. These things cannot be dynamically changed, and resetting techs or recipes may be necessary. Try to avoid these changes after shipping the mod out to the public. Try to come up with a finalized version of the prototype that you can base the mod around. Of course, migrations are unnecessary if the user simply starts a new world with each mod update, but do not expect the community to do this.

Control

Within most mods is a file called control.lua. This file contains scripting that makes the mod actually do things, beyond simply adding entities to the game. During this stage, each mod's control.lua is run, in it's own lua instance (this means no inter-communication without special setup) which it will own for the rest of the play session. Because this is run every time a save file is created or loaded you don't need to restart the game to see changes made to the control.lua file. Simply restarting or reloading a save will re-run this stage. There are a few other caveats to this stage, reading the data life cycle page on the API site provides the best overview.

Runtime

At this stage, the mod is setup, and the save is running. Access to all tables provided by the game can be done inside of event handlers. (More on those below.)

The major components to any Factorio mod

Whithin the average mod, there are several components that make the mod function. Every mod needs some form of "data" file, this may be called data.lua, data-updates.lua, or data-final-fixes.lua. In addition to that, mods that define new entities will need to declare these entities somewhere, the standard is in a file required by data.lua.

Some mods that need to do more than just add entities/tweak stats will also need a control.lua file, to add scripting. The mod that we'll make in this tutorial will include both prototypes, and control scripting, to give you a feel for both.

Over time, the community has settled on a specific arrangement for how a mod's directory structure should look. Following this to a T is not necessary, but can simplify things. More on directory structure below.


The tutorial mod

And now for the moment you've been waiting for. Let's start making your first mod. You'll need:

  • A recent install of Factorio
  • A text editor, such as Emacs, Vim, Sublime text, etc
  • An understanding of the tutorial above
  • An understanding of Lua as a programming language. Enough to know the syntax and how it works. If you have prior programming experience, it should not be difficult to pick up.

Once you have all of these things, we can begin.

For this mod, we're going to make a set of armor that leaves behind damaging fire behind you as you walk. It will be fully resistant to fire, but weaker towards physical damage than heavy armor, making it an armor for hit and run attacks.

Creation of the directory structure

Like this tutorial mentioned earlier, there is a somewhat community standard around for how a mod is laid out. This, combined with how the game expects mods to be laid out, limits us slightly. To start out, create a folder in your user data directory/mods folder. This folder must have a specific name, FireArmor_0.1.0. When you're finished, the mod directory should look like this:

  • (user data directory, sometimes called .factorio)
    • mods
      • FireArmor_0.1.0

Then, inside FireArmor_0.1.0, create two files, info.json and data.lua. The directory should now look like:

  • (user data directory, sometimes called .factorio)
    • mods
      • FireArmor_0.1.0
        • data.lua
        • info.json

The info.json file

Then, inside info.json, copy and paste the following into it:

{
    "name": "FireArmor",
    "version": "0.1.0",
    "title": "Fire Armor",
    "author": "You",
    "contact": "",
    "homepage": "",
    "factorio_version": "0.15",
    "dependencies": ["base >= 0.15"],
    "description": "This mod adds in fire armor that leaves behind damaging fire as you walk around."
}

To explain each field:

name
This is the internal name of your mod, it is used to identify your mod in code.
version
This is the version of your mod. This can be anything you want, provided it's a number. Most people start at some form of 0.1.0.
title
The pretty title of your mod, this will be displayed on the mods screen and when you submit it to the mod portal.
author
Your name! You can change this in the example above.
contact
Put contact info here, so someone can find you in the event of a problem.
homepage
The homepage of your mod, put a website here if you have one for the mod. Not required.
factorio_version
This tells the game what version the mod is for, this must match the version you're developing the mod for, 0.15 in this case.
dependencies
Any dependencies of your mod. Some form of "base" should always be here, so base gets loaded first.
description
A short description of your mod.

And that's all for info.json! Next, in the data.lua file:

--data.lua

require("prototypes.item")

It's a pretty simple file, all we're doing here is just telling the game to execute the file called item.lua in prototypes, which we're about to create. Create a folder in FireArmor_0.1.0 called prototypes, then inside prototypes, create a file called item.lua. The directory structure should now look like:

  • (user data directory, sometimes called .factorio)
    • mods
      • FireArmor_0.1.0
        • prototypes
          • item.lua
        • data.lua
        • info.json

Notice how our earlier require used the folder and file name in it?

Prototype creation

Now, there are two ways to create prototypes in Factorio. There's the short way, and the long way. The long way requires copying an existing definition from one of the default lua files provided with an install of Factorio, and the short way just uses a lua function to copy and modify a definition. For the sake of this tutorial, we'll do it the short way.

In item.lua, copy and paste the following:

--item.lua

local fireArmor = table.deepcopy(data.raw.armor["heavy-armor"])

fireArmor.name = "fire-armor"
fireArmor.icons= {
   {
      icon=fireArmor.icon,
      tint={r=1,g=0,b=0,a=0.3}
   },
}

fireArmor.resistances = {
   {
      type = "physical",
      decrease = 6,
      percent = 10
   },
   {
      type = "explosion",
      decrease = 10,
      percent = 30
   },
   {
      type = "acid",
      decrease = 5,
      percent = 30
   },
   {
      type = "fire",
      decrease = 0,
      percent = 100
   },
}

local recipe = table.deepcopy(data.raw.recipe["heavy-armor"])
recipe.enabled = true
recipe.ingredients = {{"copper-plate",200},{"steel-plate",50}}
recipe.result = "fire-armor"

data:extend{fireArmor,recipe}

What we've just done here is we've copied the definition of heavy armor, then changed it's properties, and injected it into the Factorio init with data:extend. The first line of code is probably the most interesting. table.deepcopy copies a table fully into another table. We do this from data.raw. The data part is a table, which will be used by game to setup the Factorio universe. In fact, it contains the function extend(self,prototypes) and a table called raw. The former is customary way to add new stuff to the latter. It is actually data.raw that holds the prototypes for the game. (You can view the implementation in the file /factorio/data/core/lualib/dataloader.lua)

In addition to defining the item prototype, we also define a recipe for it. This is necessary if you want to be able to craft the thing. We also set it to enabled so it doesn't need a technology to unlock.

More on data.raw

When Factorio initializes, all prototypes are put into a table called data.raw. This table holds all types, and within those types, individual entities. You saw earlier how we deepcopied from the definition of heavy armor, and modified some fields. In fact, let's go over each part of the deepcopy line:

local fireArmor = table.deepcopy(data.raw.armor["heavy-armor"])

We assign a variable called fireArmor that holds our copy of the heavy armor definition. Notice how in data.raw, there is a type table that holds all armors, and the specific armor we're looking for is called heavy-armor. For example, the player's prototype would be:

data.raw.player.["player"]

Because the player is the player, his type matches his name. You could define a new type of player with a mod. You can see all the prototype fields for an entity in it's long declaration in the Factorio install, at (Install)/data/base/prototypes.

You may be thinking at this point, "Can I modify factorio's existing prototypes without making new ones?" Well, the answer is yes! You would simply access the data.raw table during init, in data-final-fixes.lua, and change a property. For example, make the iron chest instead have 1000 health:

data.raw.container['iron-chest'].max_health = 1000

The reason why this code must be in data-final-fixes.lua or data-updates.lua is because that is the last file run, after all mod files have been run. This prevents (to a degree) your changes from being messed with by other mods. Of course, it is still possible to have incompatibilities. You should note any that you know of in your mod's description. Again, the dev's documentation on this should be looked at.

This can also be applied to other mods, not just factorio's base. You could mod a mod, as long as you add the modified mod to your dependencies so it gets loaded first.

The control scripting

And now, to finalize the mod, we have to make it be more than just simple armor. Let's think about what we want the armor to do. We want the armor to periodically create fire on the ground as we walk with the armor on. The event we're going to use is called on_tick, since we want the fire to be periodically created.

In our mod folder, create a file called control.lua. The directory structure should now look like:

  • (user data directory, sometimes called .factorio)
    • mods
      • FireArmor_0.1.0
        • prototypes
          • item.lua
        • control.lua
        • data.lua
        • info.json

The game will automatically execute this file, so requiring it in data.lua is not necessary.

Inside control.lua, copy and paste the following:

--control.lua

script.on_event({defines.events.on_tick},
   function (e)
      if e.tick % 60 == 0 then --common trick to reduce how often this runs, we don't want it running every tick, just 1/second
         for index,player in pairs(game.players) do  --loop through all players on the server
            if player.get_inventory(defines.inventory.player_armor).get_item_count("fire-armor") >= 1 then --if they're wearing our armor
               game.surfaces[1].create_entity{name="fire-flame",position=player.position, force="neutral"} --create the fire where they're standing
            end
         end
      end
   end
)

I've used lua comments in the code above to explain each step. It's fairly easy to understand, and it shows how you would get the current armor that the player is wearing, with defines.inventory.player_armor, which is an inventory constant. You can read the list of defines here.


Locale

If you've already tried loading up Factorio and trying the mod so far, you may have noticed that the item name of the armor says "Unknown key". This means that Factorio has the internal name, but it doesn't know what it should look like to the user. So, we need to create locale for our mod.

In the mod folder, create a folder called locale, then create another folder inside that called en, then a file called config.cfg.

The directory structure should now look like:

  • (user data directory, sometimes called .factorio)
    • mods
      • FireArmor_0.1.0
        • locale
          • en
            • config.cfg
        • prototypes
          • item.lua
        • control.lua
        • data.lua
        • info.json

If you know another language, you can also translate your mod by making other language code files inside locale, such as de for german.

Inside config.cfg, paste the following:


[item-name]
fire-armor = Fire armor

[item-description]
fire-armor = An armor that seems to catch the ground itself on fire when you take a step. It's warm to the touch.

Notice how this is not a lua file. Locale is handled with C config files, so the format is different.


The finished tutorial mod

Well, the mod is finished. Since this mod is only a tutorial, there isn't much balance to it. Additionally, don't try submitting it to the mod portal as your own, since it's from the Wiki.

However, you're free to take this mod and modify it for your own use, changing recipes, adding technologies, whatever.


Extended learning

One of the best ways to learn how to mod beyond this is to look at other mods. As all mods can be opened and looked at, looking at the mods of experienced modders can help significantly when making your own mod.

Keeping your mod working

As Factorio evolves, things will change. Previously, you probably ignored the modding part of the changelog, you now need to read it and see if any changes affect your mod(s). If so, you'll need to fix them. If there's something wrong with your mod, the game will fail to init and explain why.

See also