Highly Derivative

by mrudat

Support library for mods that derive items from other mods.

3 years ago
0.18
5
Owner:
mrudat
Source:
https://github.com/mrudat/Factorio-Hi...
Homepage:
N/A
License:
MIT
Created:
3 years ago
Latest Version:
0.0.1 (3 years ago)
Factorio version:
0.18
Downloaded by:
5 users

Highly Derivative

A library to help write mods that derive things from existing things, most definitely still a work in progress.

-- load the Highly Derivative library.
local HighlyDerivative = require('__HighlyDerivative__.library')

Public Functions

derive()

Runs through every prototype currently in data.raw and calls all registered callbacks.

Will call each (non-index) callback exactly once for each new prototype encountered.

This should be called in data-updates.lua and data-final-fixes.lua.

-- data-updates and data-final-fixes
require('__HighlyDerivative__.library').derive()

index(prototype)

Called with no arguments, will cause the library to re-index all prototypes in data.raw, in case they may have been changed.

Called with a prototype, causes that prototype to be (re)-indexed.

HighlyDerivative.index(thing)

mark_final(prototype)

Marks prototype as 'final' which will prevent prototype from being fed to any derivation callbacks.

Only needs name and type, so can be called ahead of time for a prototype that may not end up being created at all.

HighlyDerivative.mark_final({
  type = 'item',
  name = 'fred',
})

register_index(prototype_type, callback, and_descendants)

Registers callback to be called for each instance of prototype_type in data.raw.

If and_descendants is true, the callback will be registered against every descendant of prototype_type.

callback can be called again with is_reindex set to true if it is known that the prototype has changed since the last call.

local items_by_stack_size = {}

HighlyDerivative.register_index('item', function(item, item_name, item_type, is_reindex)
  if is_reindex then
    for _, item_list in pairs(items_by_stack_size) do
      item_list[item_name] = nil
    end
  end
  local stack_size = item.stack_size
  local item_list = items_by_stack_size[stack_size]
  if not item_list then
    item_list = {}
    items_by_stack_size[stack_size] = item_list
  end
  item_list[item_name] = true
end, true)

register_filter(prototype_type, callback, and_descendants)

Registers callback to be called for each instance of prototype_type in data.raw, with the intent of modifying the prototype in some way.

If and_descendants is true, the callback will be registered against every descendant of prototype_type.

callback is expected to return true if it has modified the prototype, to prompt the library to call the index callback on the prototype again.

HighlyDerivative.register_filter('item', function(item, item_name, item_type)
  if item_name:sub(1,PREFIX:len()) == PREFIX then return end
  if item_type == 'item-with-entity-data' then return end
  item.stack_size = item.stack_size * 2
  return true
end, true)

register_derivation(prototype_type, callback, and_descendants)

Registers callback to be called for each instance of prototype_type in data.raw, with the intent of creating new things based on the prototype.

If and_descendants is true, the callback will be registered against every descendant of prototype_type.

callback is expected to add newly created items to new_things, to allow the library to index, filter, and further derive more new things from new_things. All prototypes in new_things are fed to data:extend by the library.

callback will be called exactly once for each prototype in data.raw; if you create a new item of the same type that the callback is registered for, the callback will be called for the new item as well.

HighlyDerivative.register_derivation('item', function(new_things, item, item_name, item_type)
  if item_name:sub(1,PREFIX:len()) == PREFIX then return end
  if item_type == 'item-with-entity-data' then return end
  local new_item = table.deepcopy(item)
  new_item.stack_size = new_item.stack_size * 2
  new_item.name = PREFIX .. new_item.name .. '-2'
  table.insert(new_things, new_item)
end, true)

register_final_derivation(prototype_type, callback, and_descendants)

Registers callback to be called for each instance of prototype_type in data.raw, with the intent of creating new things based on the prototype.

If and_descendants is true, the callback will be registered against every descendant of prototype_type.

callback is expected to add newly created items to new_things, to allow the library to index and filter the new_things. All prototypes in new_things are fed to data:extend and mark_final by the library.

callback will be called exactly once for each prototype in data.raw.

HighlyDerivative.register_final_derivation('item', function(new_things, item, item_name, item_type)
  if item_name:sub(1,PREFIX:len()) == PREFIX then return end
  local new_item = table.deepcopy(item)
  new_item.stack_size = new_item.stack_size * 4
  new_item.name = PREFIX .. new_item.name .. '-4'
  table.insert(new_things, new_item)
end, true)

Utility functions

Not certain if these should remain here, or be moved to a distinct mod, possibly named 'A Certain Overcomplicated Index'.

find_items_that_place(entity, include_hidden)

Inspects both entity.placeable_by, and an index of item.place_result and returns a list of { item_prototype, count } for every item that can place entity.

If include hidden is true, also includes any items with the hidden flag.

local recipes = HighlyDerivative.find_items_that_place(data.raw.accumulator.accumulator)

for _, ingredient in pairs(recipes) do
  local item = ingredient[1]
  local count = ingredient[2]
  log('you could place an accumulator using ' .. count .. ' x ' .. item.name)
end

find_recipes_that_make(item)

Inspects an index of item.result/item.results and returns a table of recipes that can make the item.

local recipes = HighlyDerivative.find_recipes_that_make({
  type = 'fluid',
  name = 'light-oil'
})

for recipe_name, difficulty_flags in pairs(recipes) do
  log(recipe_name .. ' creates light-oil in:')
  if bit32.band(difficulty_flags, 3) == 3 then
    log('  both difficulties')
  else
    if bit32.band(difficulty_flags, 1) == 1 then log('  normal') end
    if bit32.band(difficulty_flags, 2) == 2 then log('  expensive') end
  end
end

can_be_made(item)

Inspects an index of item.result/item.results and returns true if at least one recipe in normal difficulty and at least one recipe in expensive difficulty is capable of crafting item.

if HighlyDerivative.can_be_made({ type = 'fluid', name = 'light-oil' }) then
  log('light-oil can be made')
end