-
Notifications
You must be signed in to change notification settings - Fork 49
Home
Welcome to the Factorio Standard Library!
The Factorio Standard Library is a project to bring Factorio modders high-quality, commonly-required utilities and tools that developers have been often copying around, remixing, and rewriting poor quality copies of. Basic tools like logging, math calculations, and position or area manipulation, which involve repeated drudgery and are commonly reproduced in high-quality, documented form in the Factorio Standard Library.
- Download the latest release of the stdlib
- Unpack the archive, and copy the
stdlib/
directory into your base mod directory - In your
control.lua
or other lua classes, you can import the stdlib classes you need.
For example, using the logger:
require 'stdlib/log/logger'
require 'defines'
LOGGER = Logger.new('example-mod-name')
script.on_event(defines.events.on_tick, function(event)
if event.tick % 600 == 0 then
LOGGER.log("An example log message!")
end
end)
In factorio, mod log output will be in %appdata%\factorio\script-output\logs
(on windows), or ~/.factorio/script-output/logs
(on OSX or Linux).
Often in factorio modding, you have an array of entities and need to apply some action or data to them. For example, if you want to damage or kill biters nearby players when they log in, you might write this code:
require 'defines'
script.on_event(defines.events.on_player_created, function(event)
local player = game.players[event.player_index].character
local position = player.position
local force = player.force
local entities = player.surface.find_entities_filtered(area = {{position.x - 25, position.y - 25}, {position.x + 25, position.y + 25}, type = 'unit', force = 'enemy'})
for _, entity in pairs(entities) do
if entity.valid then
entity.damage(100, force)
end
end
end)
That code is a bit verbose. Particularly creating the area around the player and damaging the entities is very long. It's easy to miss an -
or +
and make a mistake in the math, especially on long lines.
Using the standard library, we can simplify this a great deal. I'm going to use the position and table modules only, and can short the code greatly.
require 'defines'
require 'stdlib/table'
require 'stdlib/area/position'
script.on_event(defines.events.on_player_created, function(event)
local player = game.players[event.player_index].character
local force = player.force
local valid_enemies = table.filter(player.surface.find_entities_filtered(area = Position.expand_to_area(player.position, 25), type = 'unit', force = 'enemy'}), function(e) return e.valid end)
table.each(valid_enemies, function(entity)
entity.damage(100, force)
end)
end)
The second example has a few new require statements at the top, to give access to the extra table methods (table.filter
and table.each
), and requires the Position
class to give access to Position.expand_to_area
along with all other position methods.
So, what is the second code doing? Position.expand_to_area
creates an area (square) with the player position at the center, and the edges of the area 25 blocks away from each side, just like the previous code did. table.filter
takes an array, and is filtering out any that are not true (in this case, are not valid). table.each
applies the function to each of the valid enemies, and damages them by 100 HP.
It is easier to understand and read what the code is doing in the second example, and much less error prone. There is no math to get wrong, no verbose iteration.
However, we aren't done yet. There are a few final improvements that can be done. The function we wrote for table.filter
is so common, it is also provided in the stdlib, in Game.VALID_FILTER
. We can use that instead of rewriting it ourselves.
Also, we can use the Factorio stdlib event handlers. It allows mods to register multiple handlers for a single event (encouraging smaller, easier-to-read handlers) and has built in error handling. Errors in events will trigger a message in game instead of a hard-exit-to-menu.
Here's the final code!
require 'defines'
require 'stdlib/table'
require 'stdlib/game'
require 'stdlib/event/event'
require 'stdlib/area/position'
Event.register(defines.events.on_player_created, function(event)
local player = game.players[event.player_index].character
local force = player.force
local valid_enemies = table.filter(player.surface.find_entities_filtered(area = Position.expand_to_area(player.position, 25), type = 'unit', force = 'enemy'}), Game.VALID_FILTER)
table.each(valid_enemies, function(entity)
entity.damage(100, force)
end)
end)