FACTORIO 2.0 WARNING
The factorio 2.0 version is a very quick patch of the most obvious breakage, there are most likely things that don't work correctly. I only did this as an aid for updating dependent mods, though in the future you may be able to use flib instead, or you may have to wait a while for a proper update, we'll see what happens.
Reporting bugs
Please report bugs on GitHub.
Why does this exist?
Because understanding where exactly do recipes take their locale from took me several days of trial and error (and harassing Bilka on discord). I certainly don't wish anyone to go through that again, so I packaged all of the pain into this convenient little library.
For real though, I need to generate virtual signals to represent recipes in Crafting Combinator and for a long time I used a half-assed approach of figuring out the locale that completely ignored ~90% of the mechanism. This (obviously) didn't work for many recipes, so I decided to bite the bullet and do it properly. Since several people seemed interested in my work, I decided to polish it a bit and make it into this lib.
There are also several things that are prone to require updates with changes to the base game. This way only the library needs to be updated, rather than each mod individually.
What can it do?
The main purpose of the library is to find the correct localised_name
and localised_description
of prototypes. This of course includes recipes (which are by far the most complicated), but it works for any other prototype too.
It can also give you proper icons
for any prototype. This was added because recipes can inherit icons
from their products (though differently than locale), but is also useful to parse the various formats an icon can be defined in.
Apart from that, there's a bunch of random utilities that the lib uses itself, but I thought I'd expose them, since they could be useful on their own as well. Notably, there's a prototype type inheritance tree and a couple utilities around that.
How to
First of all you need to add the lib to your dependencies in info.json
- something like:
{
"name": "moddy-mc-modface",
"version": "4.2.0",
...
"dependencies": [..., "rusty-locale"]
}
should do the trick.
Next, import the modules you want to use with something like:
local rusty_locale = require '__rusty-locale__.locale'
and call functions on them like:
local locale = rusty_locale.of(some_recipe_prototype)
This is for the locale module, for icons you'd replace locale
with icons
, and so on... You can find the available modules and the functions in them in the reference bellow.
Reference
locale
All the functions in this module return a special object that has two properties: name
and description
, which return the localised name and description, respectively. The properties are resolved lazily, so don't worry if you only need to use one of them.
of(prototype)
Get a locale object for the given prototype. The type of the prototype is determined automatically and the proper algorithm is used to resolve the locale. This is the preferred way to use the module as it is future-proof in case some prototype is changed to require a special algorithm.
As this is the "smart" function, it does more work than the specific ones, so it can be slower. This shouldn't be much of a concern, but if you really care about data stage performance for whatever reason and you know the type of the prototype in advance, you can save some time by calling the specific function for that type directly.
of(name, type)
Shorthand for locale.of(prototypes.find(name, type))
.
of_recipe(prototype)
Get a locale object for the given prototype, assuming it's a recipe.
of_item(prototype)
Get a locale object for the given prototype, assuming it's an item.
of_generic(prototype)
Get a locale object for the given prototype, assuming it doesn't use any of the special formats.
localised_types
This is a table mapping the prototype types that support localisation to true
for easy lookup.
icons
All the icons in this module are returned in the icons
-only format. This means that it is safe to use the results in a prototype just by setting them to icons
.
Note that the spidertron remote also requires icon_color_indicator_mask
and icon_size
to be specified in the root of the prototype. You won't get those from the functions here at all, which shouldn't be an issue in most cases, but make sure to handle them yourself if you do need to (for example if you're creating your own spidertron remote)
of(prototype, silent)
Get the icons for the given prototype. The type of the prototype is determined automatically and the proper algorithm is used to resolve the icons. This is the preferred way to use the module as it is future-proof in case some prototype is changed to require a special algorithm.
If silent is true
, nil
will be returned when no valid icons definition could be found, instead of throwing an error. This also applies to prototype
being nil
.
The same disclaimer applies as for locale.of()
.
of(name, type, silent)
Shorthand for icons.of(prototypes.find(name, type), silent)
.
Note that the silent
parameter is not passed through to prototypes.find()
, so a non-existent prototype will still throw an error. If that's a problem, use the full form instead: icons.of(prototypes.find(name, type, silent), silent)
. This way it's clear which errors you want to ignore, but be aware that it doesn't make sense to call this like icons.of(prototypes.find(name, type, true), false)
, as the potential nil
coming from prototypes.find()
will always result in an error, so you may as well just let the more descriptive error be thrown right away instead.
of_recipe(prototype, silent)
Get the icons for the given prototype, assuming it's a recipe.
If silent is true
, nil
will be returned when no valid icons definition could be found, instead of throwing an error.
of_generic(prototype, silent)
Get the icons for the given prototype, assuming it doesn't use any special format.
If silent is true
, nil
will be returned when no valid icons definition could be found, instead of throwing an error.
prototypes
These are utilties to work with the prototype inheritance tree.
find(name, type, silent)
Find a prototype with the given name, whose type inherits from the given type. If silent is true
, nil
will be returned if no prototype is found, otherwise an error is thrown (default is false
). Note that this also applies to name
being nil
, but type
being nil
will always result in an error - use prototypes.find_by_name()
to search by name only instead.
This is particularly useful for finding prototypes of recipe products where you may only know the name and that it's an item/fluid, not the exact type. For example:
prototypes.find('firearm-magazine', 'item')
would give you the prototype for firearm-magazine
, eventhough the actual type is ammo
, not item
.
find_by_name(name)
Find all the prototypes with the given name. Returns a table of {type: prototype}
.
inherits(type, base)
Check if the given type is a descendant of the given base. If it does, the name of the base is returned, or nil
if it doesn't.
You can also pass a table to base
that maps base type names to true
. The function will then check if the type inherits from any of the bases, not all of them. The returned base is the one that matched first.
descendants(type)
Get the tree of descendants, rooted at the given type, or nil
if the type doesn't exist. The tree is represented as a nested table mapping type names to their respective descendant subtrees.
recipes
The partial
functions only operate on the given table as if it was the full recipe spec. This means normal/expensive specs are ignored when passing the whole recipe, but can be passed to the function themselves instead.
parse_product(product)
Get the given product in the {name = ..., type = ..., ...}
format.
get_main_product(recipe)
Get the main product of the given recipe, correctly handling normal+expensive definitions. This means that the main product is only returned if it's the same for both normal and expensive.
The main product is returned in the {name = ..., type =..., ...}
format. nil
will be returned if there is no main product.
partial.get_main_product(recipe)
Get the main product of the given recipe part.
partial.find_product(recipe, name)
Get the full product definition for a product with the given name from the recipe part. This is mainly useful for main products.