local BioInd = require("__" .. script.mod_name .. "__.common")(script.mod_name) local settings_changed = require("settings_changed") if BioInd.get_startup_setting("BI_Enable_gvv_support") then BioInd.writeDebug("Activating support for gvv!") require("__gvv__/gvv")() end -- We can't just check if Alien Biomes is active, because we need to know if -- the tiles we need from it exist in the game! To check this, we must call -- game.get_tile_prototypes(), but this will crash in script.on_load(). So, -- let's just declare the variable here and fill it later. local AlienBiomes --~ local Event = require('__stdlib__/stdlib/event/event').set_protected_mode(true) local Event = require('__stdlib__/stdlib/event/event').set_protected_mode(false) require ("util") require ("libs/util_ext") require ("control_tree") -- Generate a look-up table with the names of our trees local function get_bi_trees() local list = {} local trees = game.get_filtered_entity_prototypes({{filter = "type", type = "tree"}}) for tree_name, tree in pairs(trees) do if tree_name:match("^bio%-tree%-.+%-%d$") then BioInd.show("Found matching tree", tree_name) list[tree_name] = true end end return list end -- Generate a look-up table with the names of tiles that can't be changed by fertilizer local tile_patterns = { ".*concrete.*", ".*stone%-path.*", "^bi%-solar%-mat$", "^bi%-wood%-floor$", } local function get_fixed_tiles() local list = {} for tile_name, tile in pairs(game.tile_prototypes) do for p, pattern in ipairs(tile_patterns) do if tile_name:match(pattern) then BioInd.show("Found matching tile", tile_name) -- If a tile is minable and fertilizer is used on it, we must deduct the mined -- tiles from the player/robot again! list[tile_name] = tile.mineable_properties.products or true end end end BioInd.show("Forbidden tiles", list) return list end -------------------------------------------------------------------- local function init() BioInd.writeDebug("Entered init!") if BioInd.is_debug then game.check_prototype_translations() end global = global or {} -------------------------------------------------------------------- -- Settings -------------------------------------------------------------------- -- Global table for storing the last state of certain mod settings global.mod_settings = global.mod_settings or {} if BioInd.get_startup_setting("BI_Easy_Bio_Gardens") then global.mod_settings.garden_pole_connectors = BioInd.get_garden_pole_connectors() else global.mod_settings.garden_pole_connectors = nil end -- Global table for storing the data of compound entities. They may change between -- saves (e.g. Bio gardens only need hidden poles when the "Easy gardens" setting -- is active). --~ global.compound_entities = global.compound_entities or BioInd.compound_entities global.compound_entities = BioInd.rebuild_compound_entity_list() -------------------------------------------------------------------- -- Tree stuff! -------------------------------------------------------------------- global.bi = global.bi or {} global.bi.tree_growing = global.bi.tree_growing or {} for i = 1, 4 do global.bi["tree_growing_stage_" .. i] = global.bi["tree_growing_stage_" .. i] or {} end -- List of tree prototypes created by BI global.bi.trees = get_bi_trees() -- List of tile prototypes that can't be fertilized global.bi.barren_tiles = get_fixed_tiles() -------------------------------------------------------------------- -- Compound entities -------------------------------------------------------------------- -- Check what global tables we need for compound entities local compound_entity_tables = {} --~ for compound, compound_data in pairs(BioInd.compound_entities) do for compound, compound_data in pairs(global.compound_entities) do -- BioInd.compound_entities contains entries that point to the same table -- (e.g. straight/curved rails, or overlay entities), so we just overwrite -- them to remove duplicates compound_entity_tables[compound_data.tab] = compound end BioInd.show("Need to check these tables in global", compound_entity_tables) -- Prepare global tables storing data of compound entities local result for compound_tab, compound_name in pairs(compound_entity_tables) do -- Init table global[compound_tab] = global[compound_tab] or {} BioInd.writeDebug("Initialized global[%s] (%s entities stored)", {compound_name, table_size(global[compound_tab])}) -- If this compound entity requires additional tables in global, initialize -- them now! local related_tables = global.compound_entities[compound_name].add_global_tables if related_tables then for t, tab in ipairs(related_tables or {}) do global[tab] = global[tab] or {} BioInd.writeDebug("Initialized global[%s] (%s values)", {tab, table_size(global[tab])}) end end -- If this compound entity requires additional values in global, initialize -- them now! local related_vars = global.compound_entities[compound_name].add_global_values if related_vars then for var_name, value in pairs(related_vars or {}) do global[var_name] = global[var_name] or value BioInd.writeDebug("Set global[%s] to %s", {var_name, global[var_name]}) end end -- Clean up global tables (We can skip this for empty tables!) if next(global[compound_tab]) then -- Remove invalid entities result = BioInd.clean_global_compounds_table(compound_name) BioInd.writeDebug("Removed %s invalid entries from global[%s]!", {result, compound_tab}) -- Restore missing hidden entities result = BioInd.restore_missing_entities(compound_name) BioInd.writeDebug("Checked %s compound entities and restored %s missing hidden entries for global[\"%s\"]!", {result.checked, result.restored, compound_tab}) end end -- Search all surfaces for unregistered compound entities result = BioInd.find_unregistered_entities() BioInd.writeDebug("Registered %s forgotten entities!", {result}) -------------------------------------------------------------------- -- Compatibility with other mods -------------------------------------------------------------------- global.compatible = global.compatible or {} global.compatible.AlienBiomes = BioInd.AB_tiles() -- enable researched recipes for i, force in pairs(game.forces) do BioInd.writeDebug("Reset technology effects for force %s.", {force.name}) force.reset_technology_effects() end end -------------------------------------------------------------------- local function On_Load() log("Entered On_Load!") end -------------------------------------------------------------------- local function On_Config_Change(ConfigurationChangedData) BioInd.writeDebug("On Configuration changed: %s", {ConfigurationChangedData}) -- Re-initialize global tables etc. init() -- We've made a list of the tree prototypes that are currently available. Now we -- need to make sure that the lists of growing trees don't contain removed tree -- prototypes! (This fix is needed when "Alien Biomes" has been removed; it should -- work with all other mods that create trees as well.) local trees = global.bi.trees local tab -- Growing stages for i = 1, 4 do tab = global.bi["tree_growing_stage_" .. i] BioInd.writeDebug("Number of trees in growing stage %s: %s", {i, table_size(tab)}) for t, tree in pairs(tab) do if not trees[tree.tree_name] then BioInd.writeDebug("Removing invalid tree %s (%s)", {t, tree.tree_name}) tab[t] = nil end end -- Removing trees will create gaps in the table, but we need it as a continuous -- list. (Trees need to be sorted by growing time, and we always look at the -- tree with index 1 when checking if a tree has completed the growing stage, so -- lets sort the table after all invalid trees have been removed!) table.sort(tab, function(a, b) return a.time < b.time end) BioInd.show("Final tree list", tab) end end -------------------------------------------------------------------- --- Used for some compatibility with Angels Mods Event.register(defines.events.on_player_joined_game, function(event) local player = game.players[event.player_index] local force = player.force local techs = force.technologies --~ if settings.startup["angels-use-angels-barreling"] and --~ settings.startup["angels-use-angels-barreling"].value then if BioInd.get_startup_setting("angels-use-angels-barreling") then techs['fluid-handling'].researched = false techs['bi-tech-fertilizer'].reload() local _t = techs['angels-fluid-barreling'].researched techs['angels-fluid-barreling'].researched = false techs['angels-fluid-barreling'].researched = _t end end) -------------------------------------------------------------------- local function On_Built(event) local entity = event.created_entity or event.entity if not (entity and entity.valid) then BioInd.arg_err(entity or "nil", "entity") end local surface = BioInd.is_surface(entity.surface) or BioInd.arg_err(entity.surface or "nil", "surface") local position = BioInd.normalize_position(entity.position) or BioInd.arg_err(entity.position or "nil", "position") local force = entity.force BioInd.writeDebug("Entered function On_Built with these data: " .. serpent.block(event)) --~ BioInd.writeDebug("Entity name: %s", {BioInd.print_name_id(entity)}) -- We can ignore ghosts -- if ghosts are revived, there will be -- another event that triggers where actual entities are placed! if entity.name == "entity-ghost" then BioInd.writeDebug("Built ghost of %s -- return!", {entity.ghost_name}) return end BioInd.show("Built entity", BioInd.print_name_id(entity)) local base_entry = global.compound_entities[entity.name] local base = base_entry and entity -- We've found a compound entity! if base then -- Make sure we work with a copy of the original table! We don't want to -- remove anything from it for real. local hidden_entities = util.table.deepcopy(base_entry.hidden) BioInd.writeDebug("%s (%s) is a compound entity. Need to create %s", {base.name, base.unit_number, hidden_entities}) BioInd.show("hidden_entities", hidden_entities) --~ local new_base, new_base_name, optional local new_base local new_base_name = base_entry.new_base_name -- If the base entity is only an overlay, we'll replace it with the real base -- entity and raise an event. The hidden entities will be created in the second -- pass (triggered by building the final entity). BioInd.show("base_entry.new_base_name", base_entry.new_base_name) BioInd.show("base_entry.new_base_name == base.name", base_entry.new_base_name == base.name) BioInd.show("base_entry.optional", base_entry.optional) --~ if new_base_name then if new_base_name and new_base_name ~= base.name then new_base = surface.create_entity({ name = new_base_name, position = base.position, direction = base.direction, force = base.force, raise_built = true }) new_base.health = base.health BioInd.show("Created final base entity", BioInd.print_name_id(new_base)) base.destroy({raise_destroy = true}) base = new_base BioInd.writeDebug("Destroyed old base entity!") -- Second pass: We've placed the final base entity now, so we can create the -- the hidden entities! else BioInd.writeDebug("Second pass -- creating hidden entities!") BioInd.show("base_entry", base_entry) BioInd.writeDebug("global[%s]: %s", {base_entry.tab, global[base_entry.tab]}) BioInd.show("base.name", base.name) BioInd.show("base.unit_number", base.unit_number) BioInd.show("hidden_entities", hidden_entities) -- We must call create_entities even if there are no hidden entities (e.g. if -- the "Easy Gardens" setting is disabled and no hidden poles are required) -- because the compound entity gets registered there! BioInd.create_entities(global[base_entry.tab], base, hidden_entities) BioInd.writeDebug("Stored %s in table: %s", {BioInd.print_name_id(base), global[base_entry.tab][base.unit_number]}) end -- The built entity isn't one of our compound entities. else BioInd.writeDebug("%s is not a compound entity!", {BioInd.print_name_id(entity)}) -- If one of our hidden entities has been built, we'll have raised this event -- ourselves and have passed on the base entity. base = event.base_entity local entities = BioInd.compound_entities BioInd.show("Base entity", BioInd.print_name_id(base)) -- The hidden entities are listed with a common handle ("pole", "panel" etc.). We -- can get it from the reverse-lookup list via the entity type! local h_key = BioInd.HE_map_reverse[entity.type] BioInd.show("h_key", h_key or "nil") -- Electric poles -- we need to take care that they don't hook up to hidden poles! if entity.type == "electric-pole" then local pole = entity -- Make sure hidden poles of the Bio gardens are connected correctly! if pole.name == entities["bi-bio-garden"].hidden[h_key].name and base then BioInd.writeDebug("Bio garden!") BioInd.connect_garden_pole(base, pole) BioInd.writeDebug("Connected %s (%s)", {pole.name, pole.unit_number or "nil"}) end -- A seedling has been planted elseif entity.name == "seedling" then seed_planted(event) BioInd.writeDebug("Planted seedling!") -- Something else has been built else BioInd.writeDebug("Nothing to do for %s!", {entity.name}) end end BioInd.writeDebug("End of function On_Built") end local function remove_plants(entity_position, tabl) BioInd.writeDebug("Entered function remove_plants(%s, %s)", {entity_position or "nil", tabl or "nil"}) local e = BioInd.normalize_position(entity_position) if not e then BioInd.arg_err(entity_position or "nil", "position") end BioInd.check_args(tabl, "table") local pos for k, v in pairs(tabl or {}) do pos = BioInd.normalize_position(v.position) if pos and pos.x == e.x and pos.y == e.y then BioInd.writeDebug("Removing entry %s from table: %s", {k, v}) table.remove(tabl, k) break end end end -------------------------------------------------------------------- local function On_Pre_Remove(event) BioInd.writeDebug("Entered function On_Pre_Remove(%s)", {event}) local entity = event.entity if not (entity and entity.valid) then BioInd.writeDebug("No valid entity -- nothing to do!") return end --~ local compound_entity = BioInd.compound_entities[entity.name] local compound_entity = global.compound_entities[entity.name] local base_entry = compound_entity and global[compound_entity.tab][entity.unit_number] BioInd.show("entity.name", entity.name) BioInd.show("entity.unit_number", entity.unit_number) BioInd.show("compound_entity", compound_entity) BioInd.show("base_entry", base_entry) BioInd.show("compound_entity.tab", compound_entity and compound_entity.tab or "nil") BioInd.writeDebug("global[%s]: %s", {compound_entity and compound_entity.tab or "nil", compound_entity and global[compound_entity.tab] or "nil"}) -- Found a compound entity from our list! if base_entry then BioInd.writeDebug("Found compound entity %s", {base_entry.base and BioInd.print_name_id(base_entry.base)}) -- Default: Remove all hidden entities! for hidden, h_name in pairs(compound_entity.hidden or {}) do BioInd.show("hidden", hidden) --~ BioInd.writeDebug("Removing hidden entity %s %s", {base_entry[hidden] and base_entry[hidden].valid and base_entry[hidden].name or "nil", base_entry[hidden] and base_entry[hidden].valid and base_entry[hidden].unit_number or "nil"}) BioInd.writeDebug("Removing hidden entity %s", {BioInd.print_name_id(base_entry[hidden])}) BioInd.remove_entity(base_entry[hidden]) base_entry[hidden] = nil end global[compound_entity.tab][entity.unit_number] = nil -- Removed seedling elseif entity.name == "seedling" then BioInd.writeDebug("Seedling has been removed") remove_plants(entity.position, global.bi.tree_growing) -- Removed tree elseif entity.type == "tree" and global.bi.trees[entity.name] then BioInd.show("Removed tree", entity.name) local tree_stage = entity.name:match('^.+%-(%d)$') BioInd.writeDebug("Removed tree %s (grow stage: %s)", {entity.name, tree_stage or nil}) if tree_stage then remove_plants(entity.position, global.bi["tree_growing_stage_" .. tree_stage]) else error(string.format("Tree %s does not have a valid tree_stage: %s", entity.name, tree_stage or "nil")) end -- Removed something else else BioInd.writeDebug("%s has been removed -- nothing to do!", {entity.name}) end end -------------------------------------------------------------------- Event.register(Event.core_events.configuration_changed, On_Config_Change) Event.register(Event.core_events.init, init) Event.register(Event.core_events.load, On_Load) Event.build_events = { defines.events.on_built_entity, defines.events.on_robot_built_entity, defines.events.script_raised_built, defines.events.script_raised_revive } Event.pre_remove_events = { defines.events.on_pre_player_mined_item, defines.events.on_robot_pre_mined, defines.events.on_player_mined_entity, defines.events.on_robot_mined_entity, } Event.death_events = { defines.events.on_entity_died, defines.events.script_raised_destroy } Event.tile_build_events = { defines.events.on_player_built_tile, defines.events.on_robot_built_tile } Event.tile_remove_events = { defines.events.on_player_mined_tile, defines.events.on_robot_mined_tile } Event.tile_script_action = { defines.events.script_raised_set_tiles } Event.register(Event.build_events, On_Built) Event.register(Event.pre_remove_events, On_Pre_Remove) --Event.register(Event.death_events, On_Death) ------------------------------------------------------------------------------------ -- FIND LOCAL VARIABLES THAT ARE USED GLOBALLY -- -- (Thanks to eradicator!) -- ------------------------------------------------------------------------------------ setmetatable(_ENV, { __newindex = function (self, key, value) --locked_global_write error('\n\n[ER Global Lock] Forbidden global *write*:\n' .. serpent.line{key = key or '', value = value or ''} .. '\n') end, __index = function (self, key) --locked_global_read if not (key == "game" or key == "mods") then error('\n\n[ER Global Lock] Forbidden global *read*:\n' .. serpent.line{key = key or ''} .. '\n') end end })