848 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			848 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
require("util")
 | 
						|
local compound_entities = require("prototypes.compound_entities.main_list")
 | 
						|
 | 
						|
return function (mod_name)
 | 
						|
  local common = {}
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Get mod name and path to mod
 | 
						|
  common.modName = script and script.mod_name or mod_name
 | 
						|
  common.modRoot = "__" .. common.modName .. "__"
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Greatly improved version check for mods (thanks to eradicator!)
 | 
						|
  common.Version = {}
 | 
						|
  do
 | 
						|
    local V = common.Version
 | 
						|
 | 
						|
    local function parse_version(vstr) -- string "Major.Minor.Patch"
 | 
						|
      local err = function()
 | 
						|
        error('Invalid Version String: <' .. tostring(vstr) .. '>')
 | 
						|
      end
 | 
						|
      local r = {vstr:match('^(%d+)%.(%d+)%.(%d+)$')}
 | 
						|
 | 
						|
      if #r ~= 3 then
 | 
						|
        err()
 | 
						|
      end
 | 
						|
 | 
						|
      for i=1, 3 do
 | 
						|
        r[i] = tonumber(r[i])
 | 
						|
      end
 | 
						|
 | 
						|
      return r
 | 
						|
    end
 | 
						|
 | 
						|
    V.gtr = function(verA, verB)
 | 
						|
      local a, b, c = unpack(parse_version(verA))
 | 
						|
      local x, y, z = unpack(parse_version(verB))
 | 
						|
      return (a > x) or (a == x and b > y) or (a == x and b == y and c > z)
 | 
						|
    end
 | 
						|
    local map = {
 | 
						|
      ['=' ] = function(A, B) return not (V.gtr(A, B)   or V.gtr(B, A)) end,
 | 
						|
      ['>' ] = V.gtr,
 | 
						|
      ['!='] = function(A, B) return (V.gtr(A, B)       or V.gtr(B, A)) end,
 | 
						|
      ['<='] = function(A, B) return V.gtr(B, A)        or (not V.gtr(A, B)) end,
 | 
						|
      ['>='] = function(A, B) return V.gtr(A, B)        or (not V.gtr(B, A)) end,
 | 
						|
      ['~='] = function(A, B) return (V.gtr(A, B)       or V.gtr(B, A)) end,
 | 
						|
      ['<' ] = function(A, B) return V.gtr(B, A) end,
 | 
						|
    }
 | 
						|
 | 
						|
    --~ common.Version.compare = function(mod_name, operator, need_version)
 | 
						|
    common.check_version = function(mod_name, operator, need_version)
 | 
						|
      local mod_version = (mods and mods[mod_name]) or (script and script.active_mods[mod_name])
 | 
						|
      return map[operator](mod_version, need_version)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Sane values for collision masks
 | 
						|
  -- Default: {"item-layer", "object-layer", "rail-layer", "floor-layer", "water-tile"}
 | 
						|
  --~ common.RAIL_BRIDGE_MASK = {"floor-layer", "object-layer", "consider-tile-transitions"}
 | 
						|
  common.RAIL_BRIDGE_MASK = {"object-layer", "consider-tile-transitions"}
 | 
						|
 | 
						|
  -- "Transport Drones" removes "object-layer" from rails, so if bridges have only
 | 
						|
  -- {"object-layer"}, there collision mask will be empty, and they can be built even
 | 
						|
  -- over cliffs. So we need to add another layer to bridges ("floor-layer").
 | 
						|
  -- As of Factorio 1.1.0, rails need to have "rail-layer" in their mask. This will work
 | 
						|
  -- alright, but isn't available in earlier versions of Factorio, so we will use
 | 
						|
  -- "floor-layer" there instead.
 | 
						|
  local need = common.check_version("base", ">=", "1.1.0") and "rail-layer" or "floor-layer"
 | 
						|
  table.insert(common.RAIL_BRIDGE_MASK, need)
 | 
						|
 | 
						|
  -- Rails use basically the same mask as rail bridges, ...
 | 
						|
  common.RAIL_MASK = util.table.deepcopy(common.RAIL_BRIDGE_MASK)
 | 
						|
  -- ... we just need to add some layers so our rails have the same mask as vanilla rails.
 | 
						|
  table.insert(common.RAIL_MASK, "item-layer")
 | 
						|
  table.insert(common.RAIL_MASK, "water-tile")
 | 
						|
--~ log("common.RAIL_BRIDGE_MASK: " .. serpent.block(common.RAIL_BRIDGE_MASK))
 | 
						|
--~ log("common.RAIL_MASK: " .. serpent.block(common.RAIL_MASK))
 | 
						|
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Set maximum_wire_distance of Power-to-rail connectors
 | 
						|
  common.POWER_TO_RAIL_WIRE_DISTANCE = 4
 | 
						|
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- List of compound entities
 | 
						|
  -- Key:       name of the base entity
 | 
						|
  -- tab:       name of the global table where data of these entity are stored
 | 
						|
  -- hidden:    table containing the hidden entities needed by this entity
 | 
						|
  --            (Key:   name under which the hidden entity will be stored in the table;
 | 
						|
  --             Value: name of the entity that should be placed)
 | 
						|
  common.compound_entities = compound_entities.get_HE_list()
 | 
						|
 | 
						|
  -- Map the short handles of hidden entities (e.g. "pole") to real prototype types
 | 
						|
  -- (e.g. "electric-pole")
 | 
						|
  common.HE_map = compound_entities.HE_map
 | 
						|
  -- Reverse lookup
 | 
						|
  common.HE_map_reverse = compound_entities.HE_map_reverse
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- There may be trees for which we don't want to create variations. These patterns
 | 
						|
  -- are used to build a list of trees we want to ignore.
 | 
						|
  common.ignore_name_patterns = {
 | 
						|
    -- Ignore our own trees
 | 
						|
    "bio%-tree%-.+%-%d",
 | 
						|
    -- Tree prototypes created by "Robot Tree Farm" or "Tral's Robot Tree Farm"
 | 
						|
    "rtf%-.+%-%d+",
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  -- Get list of tree prototypes that we want to ignore
 | 
						|
  common.get_tree_ignore_list = function()
 | 
						|
  --~ log("Entered function get_tree_ignore_list!")
 | 
						|
    local ignore = {}
 | 
						|
    local trees = game and
 | 
						|
                    game.get_filtered_entity_prototypes({{filter = "type", type = "tree"}}) or
 | 
						|
                    data.raw.tree
 | 
						|
    for tree_name, tree in pairs(trees) do
 | 
						|
--~ log("tree_name: " .. tree_name)
 | 
						|
      for p, pattern in ipairs(common.ignore_name_patterns) do
 | 
						|
--~ log("Check for match against pattern " .. pattern)
 | 
						|
        if tree_name:match(pattern) then
 | 
						|
--~ log("Pattern matches!")
 | 
						|
          ignore[tree_name] = true
 | 
						|
          break
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
    --~ log("Tree ignore list (" .. table_size(ignore) .. "): " .. serpent.block(ignore))
 | 
						|
    return ignore
 | 
						|
  end
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Enable writing to log file until startup options are set, so debugging output
 | 
						|
  -- from the start of a game session can be logged. This depends on a locally
 | 
						|
  -- installed dummy mod to allow debugging output during development without
 | 
						|
  -- spamming real users.
 | 
						|
  -- If the "_debug" dummy mod is active, debugging will always be on. If you don't
 | 
						|
  -- have this dummy mod but want to turn on logging anyway, set the default value
 | 
						|
  -- to "true"!
 | 
						|
  local default = false
 | 
						|
 | 
						|
  common.is_debug = ( (mods and mods["_debug"]) or
 | 
						|
                      (script and script.active_mods["_debug"])) and
 | 
						|
                    true or default
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  --                               DEBUGGING FUNCTIONS                              --
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Output debugging text
 | 
						|
  common.writeDebug = function(msg, tab, print_line)
 | 
						|
    local args = {}
 | 
						|
    -- Use serpent.line instead of serpent.block if this is true!
 | 
						|
    local line = print_line and
 | 
						|
                  (string.lower(print_line) == "line" or string.lower(print_line) == "l") and
 | 
						|
                  true or false
 | 
						|
 | 
						|
    if common.is_debug then
 | 
						|
      if type(tab) ~= "table" then
 | 
						|
        tab = { tab }
 | 
						|
      end
 | 
						|
      local v
 | 
						|
      for k = 1, #tab do
 | 
						|
        v = tab[k]
 | 
						|
        -- NIL
 | 
						|
        if v == nil then
 | 
						|
          args[#args + 1] = "NIL"
 | 
						|
        -- TABLE
 | 
						|
        elseif type(v) == "table" then
 | 
						|
          args[#args + 1] = line and serpent.line(table.deepcopy(v)) or
 | 
						|
                                      serpent.block(table.deepcopy(v))
 | 
						|
        -- OTHER VALUE
 | 
						|
        else
 | 
						|
          args[#args + 1] = v
 | 
						|
        end
 | 
						|
      end
 | 
						|
      if #args == 0 then
 | 
						|
        args[1] = "nil"
 | 
						|
      end
 | 
						|
      args.n = #args
 | 
						|
 | 
						|
      -- Print the message text to log and game
 | 
						|
      log(string.format(tostring(msg), table.unpack(args)))
 | 
						|
 | 
						|
      if game then
 | 
						|
        game.print(string.format(tostring(msg), table.unpack(args)))
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Simple helper to show a single value with descriptive text
 | 
						|
  common.show = function(desc, term)
 | 
						|
    if common.is_debug then
 | 
						|
      common.writeDebug(tostring(desc) .. ": %s", type(term) == "table" and { term } or term)
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Print "entityname (id)"
 | 
						|
  common.print_name_id = function(entity)
 | 
						|
    local id
 | 
						|
    local name = "unknown entity"
 | 
						|
 | 
						|
    if entity and entity.valid then
 | 
						|
    -- Stickers don't have an index or unit_number!
 | 
						|
      id =  (entity.type == "sticker" and entity.type) or
 | 
						|
            entity.unit_number or entity.type
 | 
						|
 | 
						|
      name = entity.name
 | 
						|
    end
 | 
						|
 | 
						|
    --~ return name .. " (" .. tostring(id) .. ")"
 | 
						|
    return string.format("%s (%s)", name, id or "nil")
 | 
						|
  end
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Print "entityname"
 | 
						|
  common.print_name = function(entity)
 | 
						|
    return entity and entity.valid and entity.name or ""
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Throw an error if a wrong argument has been passed to a function
 | 
						|
  common.arg_err = function(arg, arg_type)
 | 
						|
    error(string.format(
 | 
						|
        "Wrong argument! %s is not %s!",
 | 
						|
        (arg or "nil"), (arg_type and "a valid " .. arg_type or "valid")
 | 
						|
      )
 | 
						|
    )
 | 
						|
  end
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Rudimentary check of the arguments passed to a function
 | 
						|
  common.check_args = function(arg, arg_type, desc)
 | 
						|
    if not (arg and type(arg) == arg_type) then
 | 
						|
      common.arg_err(arg or "nil", desc or arg_type or "nil")
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  --                                  MOD SPECIFIC                                  --
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Are tiles from Alien Biomes available? (Returns true or false)
 | 
						|
  common.AB_tiles = function()
 | 
						|
 | 
						|
    local ret = false
 | 
						|
 | 
						|
    if game then
 | 
						|
      local AB = game.item_prototypes["fertilizer"].place_as_tile_result.result.name
 | 
						|
      -- In data stage, place_as_tile is only changed to Alien Biomes tiles if
 | 
						|
      -- both "vegetation-green-grass-1" and "vegetation-green-grass-3" exist. Therefore,
 | 
						|
      -- we only need to check for one tile in the control stage.
 | 
						|
      ret = (AB == "vegetation-green-grass-3") and true or false
 | 
						|
    else
 | 
						|
      ret = data.raw.tile["vegetation-green-grass-1"] and
 | 
						|
            data.raw.tile["vegetation-green-grass-3"] and true or false
 | 
						|
    end
 | 
						|
 | 
						|
    return ret
 | 
						|
  end
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Function for removing individual entities
 | 
						|
  common.remove_entity = function(entity)
 | 
						|
    if entity and entity.valid then
 | 
						|
      --~ entity.destroy()
 | 
						|
      entity.destroy{raise_destroy = true}
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Function for removing invalid prototypes from list of compound entities
 | 
						|
  common.rebuild_compound_entity_list = function()
 | 
						|
    local f_name = "rebuild_compound_entity_list"
 | 
						|
    common.writeDebug("Entered function %s()", {f_name})
 | 
						|
 | 
						|
    local ret = {}
 | 
						|
    local h_type
 | 
						|
 | 
						|
    for c_name, c_data in pairs(common.compound_entities) do
 | 
						|
common.show("base_name", c_name)
 | 
						|
common.show("data", c_data)
 | 
						|
      -- Is the base entity in the game?
 | 
						|
      if c_data.base and c_data.base.name and game.entity_prototypes[c_data.base.name] then
 | 
						|
        -- Make a copy of the compound-entity data
 | 
						|
        common.writeDebug("%s exists -- copying data", {c_name})
 | 
						|
        ret[c_name] = util.table.deepcopy(c_data)
 | 
						|
 | 
						|
        -- Check hidden entities
 | 
						|
        for h_key, h_data in pairs(ret[c_name].hidden) do
 | 
						|
          --~ h_type = common.HE_map[h_key]
 | 
						|
common.writeDebug("h_key: %s\th_data: %s", {h_key, h_data})
 | 
						|
          -- Remove hidden entity if it doesn't exist
 | 
						|
          if not game.entity_prototypes[h_data.name] then
 | 
						|
            common.writeDebug("Removing %s (%s) from list of hidden entities!", {h_data.name, h_key})
 | 
						|
            ret[c_name].hidden[h_key] = nil
 | 
						|
          end
 | 
						|
        end
 | 
						|
 | 
						|
      -- Clean table
 | 
						|
      else
 | 
						|
        local tab = c_data.tab
 | 
						|
        if tab then
 | 
						|
          -- Remove main table from global
 | 
						|
          common.writeDebug("Removing %s (%s obsolete entries)", {tab, #tab})
 | 
						|
          global[tab] = nil
 | 
						|
        end
 | 
						|
 | 
						|
        -- If this compound entity requires additional tables in global, initialize
 | 
						|
        -- them now!
 | 
						|
        local related_tables = c_data.add_global_tables
 | 
						|
        if related_tables then
 | 
						|
          for t, tab in ipairs(related_tables or {}) do
 | 
						|
            common.writeDebug("Removing global[%s] (%s values)", {tab, table_size(global[tab])})
 | 
						|
            global[tab] = nil
 | 
						|
          end
 | 
						|
        end
 | 
						|
 | 
						|
        -- If this compound entity requires additional values in global, remove them!
 | 
						|
        local related_vars = c_data.add_global_values
 | 
						|
        if related_vars then
 | 
						|
          for var_name, value in pairs(related_vars or {}) do
 | 
						|
            common.writeDebug("Removing global[%s] (was: %s)", {var_name, global[var_name]})
 | 
						|
            global[var_name] = nil
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
    common.show("ret", ret)
 | 
						|
    return ret
 | 
						|
  end
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Function to add all optional values for a compound entity to the table entry.
 | 
						|
  common.add_optional_data = function(base)
 | 
						|
    local f_name = "add_optional_data"
 | 
						|
common.writeDebug("Entered function %s(%s)", {f_name, common.print_name_id(base)})
 | 
						|
    if not (base and base.valid and global.compound_entities[base.name]) then
 | 
						|
      common.arg_err(base, "base of a compound entity")
 | 
						|
    end
 | 
						|
 | 
						|
    -- Add optional values to global table
 | 
						|
    local data = global.compound_entities[base.name]
 | 
						|
common.show("data", data)
 | 
						|
    local tab = data.tab
 | 
						|
common.show("tab", tab)
 | 
						|
common.show("global[tab]", global[tab] or "nil")
 | 
						|
 | 
						|
    local entry = global[tab][base.unit_number]
 | 
						|
 | 
						|
    for k, v in pairs(data.optional or {}) do
 | 
						|
      if entry[k] then
 | 
						|
        common.writeDebug("%s already exists: %s", {k, entry[k]})
 | 
						|
      else
 | 
						|
        entry[k] = v
 | 
						|
        common.writeDebug("Added data to %s: %s = %s", {common.print_name_id(base), k, v})
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Function for removing all parts of invalid compound entities
 | 
						|
  common.clean_global_compounds_table = function(entity_name)
 | 
						|
    local f_name = "clean_table"
 | 
						|
common.writeDebug("Entered function %s(%s)", {f_name, entity_name or "nil"})
 | 
						|
common.writeDebug("Entries in common.compound_entities[%s]: %s", {entity_name, table_size(global.compound_entities[entity_name])})
 | 
						|
 | 
						|
    --~ local entity_table = global[common.compound_entities[entity_name].tab]
 | 
						|
    --~ local hidden_entities = common.compound_entities[entity_name].hidden
 | 
						|
    local entity_table = global.compound_entities[entity_name]
 | 
						|
common.show("entity_table", entity_table and entity_table.tab)
 | 
						|
    entity_table = entity_table and entity_table.tab and global[entity_table.tab]
 | 
						|
common.writeDebug("entity_table: %s", {entity_table}, "line")
 | 
						|
    local hidden_entities = global.compound_entities[entity_name].hidden
 | 
						|
common.show("hidden_entities", hidden_entities)
 | 
						|
    local removed = 0
 | 
						|
    -- Scan the whole table
 | 
						|
    for c, compound in pairs(entity_table) do
 | 
						|
common.writeDebug ("c: %s\tcompound: %s", {c, compound})
 | 
						|
      -- No or invalid base entity!
 | 
						|
      if not (compound.base and compound.base.valid) then
 | 
						|
common.writeDebug("%s (%s) has no valid base entity -- removing entry!", {entity_name, c})
 | 
						|
 | 
						|
        for h_name, h_entity in pairs(hidden_entities) do
 | 
						|
common.writeDebug("Removing %s (%s)", {h_name, h_entity.name})
 | 
						|
          common.remove_entity(compound[h_name])
 | 
						|
        end
 | 
						|
        entity_table[c] = nil
 | 
						|
        removed = removed + 1
 | 
						|
common.writeDebug("Removed %s %s", {entity_name, c})
 | 
						|
      end
 | 
						|
    end
 | 
						|
common.show("Removed entities", removed)
 | 
						|
common.show("Pruned list size", table_size(entity_table))
 | 
						|
--~ common.show("Pruned list", entity_table)
 | 
						|
    return removed
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Function to resore missing parts of compound entities
 | 
						|
  common.restore_missing_entities = function(entity_name)
 | 
						|
    local f_name = "restore_missing_entities"
 | 
						|
common.writeDebug("Entered function %s(%s)", {f_name, entity_name or "nil"})
 | 
						|
--~ common.writeDebug("global.compound_entities[%s]: %s", {entity_name, global.compound_entities[entity_name]})
 | 
						|
common.writeDebug("global.compound_entities[%s]: %s entries", {entity_name, table_size(global.compound_entities[entity_name])})
 | 
						|
 | 
						|
    local check = global.compound_entities[entity_name]
 | 
						|
    local entity_table = check and global[check.tab] or {}
 | 
						|
    local hidden_entities = check and check.hidden or {}
 | 
						|
 | 
						|
    local checked = 0
 | 
						|
    local restored = 0
 | 
						|
    -- Scan the whole table
 | 
						|
    for c, compound in pairs(entity_table) do
 | 
						|
--~ common.writeDebug("c: %s\tcompound: %s", {c, compound})
 | 
						|
      -- Base entity is valid!
 | 
						|
      if (compound.base and compound.base.valid) then
 | 
						|
common.writeDebug("%s is valid -- checking hidden entities!", {common.print_name_id(compound.base)})
 | 
						|
        for h_name, h_entity in pairs(hidden_entities) do
 | 
						|
--~ common.writeDebug("h_name: %s\th_entity: %s", {h_name, h_entity})
 | 
						|
          -- Hidden entity is missing
 | 
						|
          if compound[h_name] and compound[h_name].valid then
 | 
						|
            common.writeDebug("%s: OK", {h_name})
 | 
						|
          else
 | 
						|
            common.writeDebug("%s: MISSING!", {h_name})
 | 
						|
            common.create_entities(entity_table, compound.base, {[h_name] = h_entity.name})
 | 
						|
            restored = restored + 1
 | 
						|
            common.writeDebug("Created %s (%s) for %s",
 | 
						|
                              {h_name, h_entity.name, common.print_name_id(compound.base)})
 | 
						|
          end
 | 
						|
        end
 | 
						|
        checked = checked + 1
 | 
						|
--~ common.writeDebug("Restored %s %s", {entity_name, c})
 | 
						|
      end
 | 
						|
    end
 | 
						|
common.writeDebug("Checked %s compound entities", {checked})
 | 
						|
common.writeDebug("Restored %s entities", {restored})
 | 
						|
--~ common.show("Fixed list", entity_table)
 | 
						|
    return {checked = checked, restored = restored}
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Function to find all unregistered compound entities of a particular type
 | 
						|
  common.register_in_compound_entity_tab = function(compound_name)
 | 
						|
  local f_name = "register_in_compound_entity_tab"
 | 
						|
    common.writeDebug("Entered function %s(%s)", {f_name, compound_name})
 | 
						|
 | 
						|
    local cnt = 0
 | 
						|
    local h_cnt = 0
 | 
						|
    local data = global.compound_entities[compound_name]
 | 
						|
    if not data then
 | 
						|
      common.arg_err(compound_name, "name of a compound entity")
 | 
						|
    end
 | 
						|
 | 
						|
    local g_tab = global[data.tab]
 | 
						|
    local found, h_found ,created
 | 
						|
 | 
						|
    -- Scan all surfaces
 | 
						|
    for s, surface in pairs(game.surfaces) do
 | 
						|
      -- Check the bases of all compound entities on the surface
 | 
						|
      found = surface.find_entities_filtered({name = compound_name})
 | 
						|
      for b, base in ipairs(found) do
 | 
						|
        -- Base entity isn't registered yet!
 | 
						|
        if not g_tab[base.unit_number] then
 | 
						|
          common.writeDebug("Found unregistered entity: %s!", {common.print_name_id(base)})
 | 
						|
          -- Create an entry in the global table
 | 
						|
          g_tab[base.unit_number] = {base = base}
 | 
						|
          -- Add optional data to the table, if there are any
 | 
						|
          common.add_optional_data(base)
 | 
						|
 | 
						|
 | 
						|
          -- Check if it already has any hidden entities
 | 
						|
          for h_name, h_data in pairs(data.hidden) do
 | 
						|
            h_found = surface.find_entities_filtered({
 | 
						|
              name = h_data.name,
 | 
						|
              type = h_data.type,
 | 
						|
              position = common.offset_position(base.position, h_data.base_offset),
 | 
						|
            })
 | 
						|
 | 
						|
            -- Check for multiple hidden entities of the same type in the same position!
 | 
						|
            if #h_found > 1 then
 | 
						|
              local cnt = 0
 | 
						|
              for duplicate = 2, #h_found do
 | 
						|
                h_found[duplicate].destroy({raise_destroy = true})
 | 
						|
                cnt = cnt + 1
 | 
						|
              end
 | 
						|
              common.writeDebug("Removed %s duplicate entities (%s)!", {cnt, h_data.name})
 | 
						|
            end
 | 
						|
 | 
						|
            -- There still is one hidden entity left. Add it to the table!
 | 
						|
            if next(h_found) then
 | 
						|
              common.writeDebug("Found %s -- adding it to the table.", {common.print_name_id(base)})
 | 
						|
              g_tab[base.unit_number][h_name] = h_found[1]
 | 
						|
 | 
						|
            -- Create hidden entity! This will automatically add it to the table.
 | 
						|
            else
 | 
						|
              created = common.create_entities(g_tab, base, {[h_name] = h_data})
 | 
						|
              common.writeDebug("Created hidden %s: %s",
 | 
						|
                                {h_name, created and common.print_name_id(created) or "nil"})
 | 
						|
              h_cnt = h_cnt + 1
 | 
						|
            end
 | 
						|
          end
 | 
						|
          cnt = cnt + 1
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
    common.writeDebug("Registered %s compound entities and created %s hidden entities", {cnt, h_cnt})
 | 
						|
    return cnt
 | 
						|
  end
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Function to find all unregistered compound entities
 | 
						|
  common.find_unregistered_entities = function()
 | 
						|
    local f_name = "find_unregistered_entities"
 | 
						|
    common.writeDebug("Entered function %s()", {f_name})
 | 
						|
 | 
						|
    local cnt = 0
 | 
						|
    for compound_entity, c in pairs(global.compound_entities) do
 | 
						|
      cnt = cnt + common.register_in_compound_entity_tab(compound_entity)
 | 
						|
    end
 | 
						|
    --~ common.writeDebug("Registered %s compound entities", {cnt})
 | 
						|
    common.writeDebug("Registered %s compound entities.", {cnt})
 | 
						|
    return cnt
 | 
						|
  end
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Function to normalize positions
 | 
						|
  common.normalize_position = function(pos)
 | 
						|
    if pos and type(pos) == "table" and table_size(pos) == 2 then
 | 
						|
      local x = pos.x or pos[1]
 | 
						|
      local y = pos.y or pos[2]
 | 
						|
      if x and y and type(x) == "number" and type(y) == "number" then
 | 
						|
        return { x = x, y = y }
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Calculate the offset position of a hidden entity
 | 
						|
  common.offset_position = function(base_pos, offset)
 | 
						|
    common.check_args(base_pos, "table", "position")
 | 
						|
    offset = offset or {x = 0, y = 0}
 | 
						|
    common.check_args(offset, "table", "position")
 | 
						|
 | 
						|
    base_pos = common.normalize_position(base_pos)
 | 
						|
    offset = common.normalize_position(offset)
 | 
						|
 | 
						|
common.show("base_pos", base_pos)
 | 
						|
common.show("offset", offset)
 | 
						|
common.show("new", {x = base_pos.x + offset.x, y = base_pos.y + offset.y})
 | 
						|
    return {x = base_pos.x + offset.x, y = base_pos.y + offset.y}
 | 
						|
  end
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Check if argument is a valid surface
 | 
						|
  common.is_surface = function(surface)
 | 
						|
    local t = type(surface)
 | 
						|
    surface = (t == "number" or t == "string" and game.surfaces[surface]) or
 | 
						|
              (t == "table" and surface.object_name and
 | 
						|
                                surface.object_name == "LuaSurface" and surface)
 | 
						|
    return surface
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Make hidden entities unminable and indestructible
 | 
						|
  local function make_unminable(entities)
 | 
						|
    for e, entity in ipairs(entities or {}) do
 | 
						|
      if entity.valid then
 | 
						|
        entity.minable = false
 | 
						|
        entity.destructible = false
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  --------------------------------------------------------------------
 | 
						|
  -- Create and register hidden entities
 | 
						|
  --~ common.create_entities = function(g_table, base_entity, hidden_entity_names, position, ...)
 | 
						|
  common.create_entities = function(g_table, base_entity, hidden_entities)
 | 
						|
    local f_name = "create_entities"
 | 
						|
    common.writeDebug("Entered function %s(%s, %s, %s)",
 | 
						|
                      {f_name, "g_table", base_entity, hidden_entities})
 | 
						|
    common.show("#g_table", g_table and table_size(g_table))
 | 
						|
    --~ common.show("hidden_entities", hidden_entities)
 | 
						|
 | 
						|
    common.check_args(g_table, "table")
 | 
						|
    common.check_args(base_entity, "table")
 | 
						|
 | 
						|
    if not base_entity.valid then
 | 
						|
      common.arg_err(base_entity, "base entity")
 | 
						|
    -- A table is required, but it may be empty! (This is needed for the
 | 
						|
    -- bio gardens, which only have a hidden pole if the "Easy Gardens"
 | 
						|
    -- setting is enabled.)
 | 
						|
    elseif not (hidden_entities and type(hidden_entities) == "table") then
 | 
						|
      common.arg_err(hidden_entities, "array of hidden-entity names")
 | 
						|
    end
 | 
						|
    local base_pos = common.normalize_position(base_entity.position) or
 | 
						|
                      common.arg_err(position or "nil", "position")
 | 
						|
 | 
						|
    local entity, offset, pos
 | 
						|
 | 
						|
    -- Initialize entry in global table
 | 
						|
    g_table[base_entity.unit_number] = g_table[base_entity.unit_number] or {}
 | 
						|
    g_table[base_entity.unit_number].base = base_entity
 | 
						|
 | 
						|
    -- Create hidden entities
 | 
						|
    local data
 | 
						|
    for key, tab in pairs(hidden_entities) do
 | 
						|
common.writeDebug("key: %s\tname: %s", {key, tab})
 | 
						|
--~ data = common.compound_entities[base_entity.name].hidden[key]
 | 
						|
      data = global.compound_entities[base_entity.name].hidden[key]
 | 
						|
--~ common.show("common.compound_entities[base_entity.name].hidden",
 | 
						|
            --~ common.compound_entities[base_entity.name].hidden)
 | 
						|
common.show("data", data)
 | 
						|
      entity = base_entity.surface.create_entity({
 | 
						|
        name = data.name,
 | 
						|
        type = data.type,
 | 
						|
        position = common.offset_position(base_pos, data.base_offset),
 | 
						|
        force = base_entity.force,
 | 
						|
      })
 | 
						|
      -- Raise the event manually, so we can pass on extra data!
 | 
						|
      script.raise_event(defines.events.script_raised_built, {
 | 
						|
        entity = entity,
 | 
						|
        base_entity = base_entity
 | 
						|
      })
 | 
						|
 | 
						|
      -- Make hidden entity unminable/undestructible
 | 
						|
      make_unminable({entity})
 | 
						|
 | 
						|
      -- Add hidden entity to global table
 | 
						|
      g_table[base_entity.unit_number][key] = entity
 | 
						|
    end
 | 
						|
 | 
						|
    -- Add optional values to global table
 | 
						|
    --~ local optional = global.compound_entities[base_entity.name].optional
 | 
						|
    --~ for k, v in pairs(optional or {}) do
 | 
						|
      --~ g_table[base_entity.unit_number][k] = v
 | 
						|
    --~ end
 | 
						|
    common.add_optional_data(base_entity)
 | 
						|
    common.writeDebug("g_table[%s]: %s", {base_entity.unit_number, g_table[base_entity.unit_number]})
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
  --------------------------------------------------------------------
 | 
						|
  -- Make a list of the pole types that Bio gardens may connect to
 | 
						|
  common.get_garden_pole_connectors = function()
 | 
						|
    --~ local ret = {}
 | 
						|
    local ret
 | 
						|
    if common.get_startup_setting("BI_Easy_Bio_Gardens") then
 | 
						|
common.writeDebug("\"Easy gardens\": Compiling list of poles they can connect to!" )
 | 
						|
      ret = {}
 | 
						|
      local poles = game.get_filtered_entity_prototypes({
 | 
						|
        {filter = "type", type = "electric-pole"},
 | 
						|
        {filter = "name", name = {
 | 
						|
            -- Poles named here will be ignored!
 | 
						|
            "bi-bio-garden-hidden-pole"
 | 
						|
            }, invert = "true", mode = "and"
 | 
						|
        }
 | 
						|
      })
 | 
						|
      for p, pole in pairs(poles) do
 | 
						|
        ret[#ret + 1] = pole.name
 | 
						|
      end
 | 
						|
    else
 | 
						|
common.writeDebug("\"Easy gardens\": Not active -- nothing to do!" )
 | 
						|
    end
 | 
						|
    return ret
 | 
						|
  end
 | 
						|
 | 
						|
  --------------------------------------------------------------------
 | 
						|
  -- Connect hidden poles of Bio gardens!
 | 
						|
  -- (This function may be called for hidden poles that have not been
 | 
						|
  -- added to the table yet if the pole has just been built. In this
 | 
						|
  -- case, we pass on the new pole explicitly!)
 | 
						|
  common.connect_garden_pole = function(base, new_pole)
 | 
						|
    local compound_entity = global.compound_entities["bi-bio-garden"]
 | 
						|
    --~ local pole_type = "electric-pole"
 | 
						|
    --~ local pole = global[compound_entity.tab][base.unit_number] and
 | 
						|
                  --~ global[compound_entity.tab][base.unit_number][pole_type] or
 | 
						|
                  --~ new_pole
 | 
						|
    local pole = global[compound_entity.tab][base.unit_number] and
 | 
						|
                  global[compound_entity.tab][base.unit_number].pole or
 | 
						|
                  new_pole
 | 
						|
 | 
						|
    --~ if pole and pole.valid then
 | 
						|
      --~ local wire_reach = game.entity_prototypes[compound_entity.hidden[pole_type]] and
 | 
						|
                          --~ game.entity_prototypes[compound_entity.hidden[pole_type]].max_wire_distance
 | 
						|
    if pole and pole.valid and  compound_entity.hidden and
 | 
						|
                                compound_entity.hidden.pole and
 | 
						|
                                compound_entity.hidden.pole.name then
 | 
						|
      local wire_reach = game.entity_prototypes[compound_entity.hidden.pole.name] and
 | 
						|
                          game.entity_prototypes[compound_entity.hidden.pole.name].max_wire_distance
 | 
						|
      if not wire_reach then
 | 
						|
        error("Prototype for hidden pole of Bio gardens doesn't exist!")
 | 
						|
      end
 | 
						|
 | 
						|
      pole.disconnect_neighbour()
 | 
						|
 | 
						|
      -- Each pole can only have 5 connections. Let's connect to other hidden
 | 
						|
      -- poles first!
 | 
						|
      local connected
 | 
						|
      local neighbours = pole.surface.find_entities_filtered({
 | 
						|
        position = pole.position,
 | 
						|
        radius = wire_reach,
 | 
						|
        type = "electric-pole",
 | 
						|
        name = compound_entity.hidden.pole.name
 | 
						|
      })
 | 
						|
common.writeDebug("Pole %g has %s neighbours", {pole.unit_number, #neighbours - 1})
 | 
						|
 | 
						|
      for n, neighbour in pairs(neighbours or{}) do
 | 
						|
        if pole ~= neighbour then
 | 
						|
          connected = pole.connect_neighbour(neighbour)
 | 
						|
common.writeDebug("Connected pole %g to %s %g: %s",
 | 
						|
                  {pole.unit_number, neighbour.name, neighbour.unit_number, connected})
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      --~ -- Connect hidden poles to other poles that may be in reach.
 | 
						|
      --~ common.garden_pole_connectors = common.garden_pole_connectors and next() or
 | 
						|
                                      --~ common.get_garden_pole_connectors()
 | 
						|
--~ common.show("Poles hidden bio-garden poles may connect to", global.mod_settings.garden_pole_connectors)
 | 
						|
 | 
						|
      -- Look for other poles around this one
 | 
						|
      neighbours = pole.surface.find_entities_filtered({
 | 
						|
        position = pole.position,
 | 
						|
        radius = wire_reach,
 | 
						|
        type = "electric-pole",
 | 
						|
        name = global.mod_settings.garden_pole_connectors,
 | 
						|
      })
 | 
						|
common.writeDebug("Pole %g has %s neighbours", {pole.unit_number, #neighbours})
 | 
						|
      for n, neighbour in pairs(neighbours or{}) do
 | 
						|
        connected = pole.connect_neighbour(neighbour)
 | 
						|
common.writeDebug("Connected pole %g to neighbour %s (%g): %s",
 | 
						|
                  {pole.unit_number, neighbour.name, neighbour.unit_number, connected})
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  --------------------------------------------------------------------
 | 
						|
  -- Connect hidden poles of powered rails -- this is also used in
 | 
						|
  -- migration scripts, so make it a function in common.lua!
 | 
						|
  -- (This function may be called for hidden poles that have not been
 | 
						|
  -- added to the table yet if the pole has just been built. In this
 | 
						|
  -- case, we pass on the new pole explicitly!)
 | 
						|
  common.connect_power_rail = function(base, new_pole)
 | 
						|
    --~ local pole_type = "electric-pole"
 | 
						|
 | 
						|
    local pole = global.bi_power_rail_table[base.unit_number].pole or new_pole
 | 
						|
    if pole and pole.valid then
 | 
						|
      -- Remove all copper wires from new pole
 | 
						|
      pole.disconnect_neighbour()
 | 
						|
common.writeDebug("Removed all wires from %s %g", {pole.name, pole.unit_number})
 | 
						|
 | 
						|
      -- Look for connecting rails at front and back of the new rail
 | 
						|
      for s, side in ipairs( {"front", "back"} ) do
 | 
						|
common.writeDebug("Looking for rails at %s", {side})
 | 
						|
        local neighbour
 | 
						|
        -- Look in all three directions
 | 
						|
        for d, direction in ipairs( {"left", "straight", "right"} ) do
 | 
						|
common.writeDebug("Looking for rails in %s direction", {direction})
 | 
						|
          neighbour = base.get_connected_rail{
 | 
						|
            rail_direction = defines.rail_direction[side],
 | 
						|
            rail_connection_direction = defines.rail_connection_direction[direction]
 | 
						|
          }
 | 
						|
common.writeDebug("Rail %s of %s (%s): %s (%s)", {direction, base.name, base.unit_number, (neighbour and neighbour.name or "nil"), (neighbour and neighbour.unit_number or "nil")})
 | 
						|
 | 
						|
          -- Only make a connection if found rail is a powered rail
 | 
						|
          -- (We'll know it's the right type if we find it in our table!)
 | 
						|
          neighbour = neighbour and neighbour.valid and global.bi_power_rail_table[neighbour.unit_number]
 | 
						|
          if neighbour and neighbour.pole and neighbour.pole.valid then
 | 
						|
            pole.connect_neighbour(neighbour.pole)
 | 
						|
            common.writeDebug("Connected poles!")
 | 
						|
          end
 | 
						|
        end
 | 
						|
      end
 | 
						|
 | 
						|
      -- Look for Power-rail connectors
 | 
						|
      local connector = base.surface.find_entities_filtered{
 | 
						|
        position = base.position,
 | 
						|
        radius = common.POWER_TO_RAIL_WIRE_DISTANCE,    -- maximum_wire_distance of Power-to-rail-connectors
 | 
						|
        name = "bi-power-to-rail-pole"
 | 
						|
      }
 | 
						|
      -- Connect to first Power-rail connector we've found
 | 
						|
      if connector and next(connector) then
 | 
						|
        pole.connect_neighbour(connector[1])
 | 
						|
        common.writeDebug("Connected " .. pole.name .. " (" .. pole.unit_number ..
 | 
						|
                          ") to " .. connector[1].name .. " (" .. connector[1].unit_number .. ")")
 | 
						|
        common.writeDebug("Connected %s (%g) to %s (%g)", {pole.name, pole.unit_number, connector[1].name, connector[1].unit_number})
 | 
						|
      end
 | 
						|
      common.writeDebug("Stored %s (%g) in global table", {base.name, base.unit_number})
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Get the value of a startup setting
 | 
						|
  common.get_startup_setting = function(setting_name)
 | 
						|
    return settings.startup[setting_name] and settings.startup[setting_name].value
 | 
						|
  end
 | 
						|
 | 
						|
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  -- Add the "icons" property based on the value of "icon"
 | 
						|
  ------------------------------------------------------------------------------------
 | 
						|
  common.BI_add_icons = function()
 | 
						|
    for tab_name, tab in pairs(data.raw) do
 | 
						|
      --~ common.writeDebug("Checking data.raw[%s]", {tab_name})
 | 
						|
      for proto_type_name, proto_type in pairs(data.raw[tab_name] or {}) do
 | 
						|
--~ common.show("proto_type.BI_add_icon", proto_type.BI_add_icon or "nil" )
 | 
						|
 | 
						|
        if proto_type.BI_add_icon then
 | 
						|
          proto_type.icons = {
 | 
						|
            {
 | 
						|
              icon = proto_type.icon,
 | 
						|
              icon_size = proto_type.icon_size,
 | 
						|
              icon_mipmaps = proto_type.icon_mipmaps
 | 
						|
            }
 | 
						|
          }
 | 
						|
          proto_type.BI_add_icon = nil
 | 
						|
          common.writeDebug("Added \"icons\" property to data.raw[\"%s\"][\"%s\"]: %s",
 | 
						|
                            {tab_name, proto_type_name, proto_type.icons}, "line")
 | 
						|
        end
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
------------------------------------------------------------------------------------
 | 
						|
--                                    END OF FILE
 | 
						|
------------------------------------------------------------------------------------
 | 
						|
return common
 | 
						|
end
 |