From e30fbfeaad7c070bf7f083708dfc046b1cb41600 Mon Sep 17 00:00:00 2001 From: TheSAguy Date: Sat, 30 Aug 2025 19:39:23 -0700 Subject: [PATCH] Fix arboretum --- Bio_Industries_2/contro.lua | 1037 ------------------------ Bio_Industries_2/control.lua | 53 +- Bio_Industries_2/control_arboretum.lua | 50 +- 3 files changed, 83 insertions(+), 1057 deletions(-) delete mode 100644 Bio_Industries_2/contro.lua diff --git a/Bio_Industries_2/contro.lua b/Bio_Industries_2/contro.lua deleted file mode 100644 index 054ed17..0000000 --- a/Bio_Industries_2/contro.lua +++ /dev/null @@ -1,1037 +0,0 @@ -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('__kry_stdlib__/stdlib/event/event').set_protected_mode(false) -require("util") -require("libs/util_ext") -require("control_tree") -require("control_arboretum") - - ----************** Used for Testing ----- ---require ("Test_Spawn") ----************* - - -local function Create_dummy_force() - -- Create dummy force for musk floor if electric grid overlay should NOT be shown in map view - local f = game.create_force(BioInd.MuskForceName) - -- Set new force as neutral to every other force - for name, force in pairs(game.forces) do - if name ~= BioInd.MuskForceName then - f.set_friend(force, false) - f.set_cease_fire(force, true) - end - end - -- New force won't share chart data with any other force - f.share_chart = false - - BioInd.writeDebug("Created force: %s", { game.forces[BioInd.MuskForceName].name }) -end - - --- Generate a look-up table with the names of our trees -local function get_bi_trees() - local list = {} - - local trees = prototypes.get_entity_filtered({ { 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(prototypes.tile) 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 - - --- Generate a look-up table with recipe ingredients, as other mods may have changed them -local function get_arboretum_recipes() - local list = {} - - local recipes = prototypes.recipe - local name - - for i = 1, 5 do - name = "bi-arboretum-r" .. i - list[name] = {} - list[name].items = {} - list[name].fluids = {} - - for i, ingredient in pairs(recipes[name].ingredients) do - if ingredient.type == "item" then - list[name].items[ingredient.name] = ingredient.amount - else - list[name].fluids[ingredient.name] = ingredient.amount - end - end - end - - BioInd.show("Terraformer recipes", list) - return list -end - - --------------------------------------------------------------------- -local function init() - BioInd.writeDebug("Entered init!") - if BioInd.is_debug then - game.check_prototype_translations() - end - - storage = storage or {} - - -------------------------------------------------------------------- - -- Settings - -------------------------------------------------------------------- - -- Global table for storing the last state of certain mod settings - storage.mod_settings = storage.mod_settings or {} - if BioInd.get_startup_setting("BI_Easy_Bio_Gardens") then - storage.mod_settings.garden_pole_connectors = BioInd.get_garden_pole_connectors() - else - storage.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). - storage.compound_entities = BioInd.rebuild_compound_entity_list() - - - -------------------------------------------------------------------- - -- Tree stuff! - -------------------------------------------------------------------- - storage.bi = storage.bi or {} - storage.bi.tree_growing = storage.bi.tree_growing or {} - for i = 1, 4 do - storage.bi["tree_growing_stage_" .. i] = storage.bi["tree_growing_stage_" .. i] or {} - end - - -- List of tree prototypes created by BI - storage.bi.trees = get_bi_trees() - - -- List of tile prototypes that can't be fertilized - storage.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(storage.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 - storage[compound_tab] = storage[compound_tab] or {} - BioInd.writeDebug("Initialized storage[%s] (%s entities stored)", - { compound_name, table_size(storage[compound_tab]) }) - -- If this compound entity requires additional tables in global, initialize - -- them now! - local related_tables = storage.compound_entities[compound_name].add_global_tables - if related_tables then - for t, tab in ipairs(related_tables or {}) do - storage[tab] = storage[tab] or {} - BioInd.writeDebug("Initialized storage[%s] (%s values)", { tab, table_size(storage[tab]) }) - end - end - -- If this compound entity requires additional values in global, initialize - -- them now! - local related_vars = storage.compound_entities[compound_name].add_global_values - if related_vars then - for var_name, value in pairs(related_vars or {}) do - storage[var_name] = storage[var_name] or value - BioInd.writeDebug("Set storage[%s] to %s", { var_name, storage[var_name] }) - end - end - - -- Clean up global tables (We can skip this for empty tables!) - if next(storage[compound_tab]) then - -- Remove invalid entities - result = BioInd.clean_global_compounds_table(compound_name) - BioInd.writeDebug("Removed %s invalid entries from storage[%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 storage[\"%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 }) - - - - -------------------------------------------------------------------- - -- Musk floor - -------------------------------------------------------------------- - storage.bi_musk_floor_table = storage.bi_musk_floor_table or {} - storage.bi_musk_floor_table.tiles = storage.bi_musk_floor_table.tiles or {} - storage.bi_musk_floor_table.forces = storage.bi_musk_floor_table.forces or {} - - - - -------------------------------------------------------------------- - -- Arboretum - -------------------------------------------------------------------- - -- Global table for arboretum radars - storage.bi_arboretum_radar_table = storage.bi_arboretum_radar_table or {} - storage.bi_arboretum_table = storage.bi_arboretum_table or {} - - -- Global table for arboretum bases / entries (used throughout the control scripts) - storage.bi_arboretum_table = storage.bi_arboretum_table or {} - - -- Global table of ingredients for terraformer recipes - storage.bi_arboretum_recipe_table = get_arboretum_recipes() - - -------------------------------------------------------------------- - -- Compatibility with other mods - -------------------------------------------------------------------- - storage.compatible = storage.compatible or {} - storage.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 - - -- Create dummy force for musk floor if electric grid overlay should NOT be shown in map view - if BioInd.UseMuskForce and not game.forces[BioInd.MuskForceName] then - Create_dummy_force() - 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() - - -- Has setting BI_Show_musk_floor_in_mapview changed? - if ConfigurationChangedData.mod_startup_settings_changed then - settings_changed.musk_floor() - -- Has this been obsoleted by the new init process? Turn it off for now! - end - - -- 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 = storage.bi.trees - local tab - -- Growing stages - for i = 1, 4 do - tab = storage.bi["tree_growing_stage_" .. i] - BioInd.writeDebug("Number of trees in growing stage %s: %s", { i, table_size(tab) }) - for t = #tab, 1, -1 do - if not trees[tab[t].tree_name] then - BioInd.writeDebug("Removing invalid tree %s (%s)", { t, tab[t].tree_name }) - table.remove(tab, t) - 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("Number of trees in final 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 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) - - ---------------------------------------------- -Event.register(defines.events.on_trigger_created_entity, function(event) - --- Used for Seed-bomb - local ent = event.entity - local surface = ent.surface - local position = ent.position - - -- 'AlienBiomes' is a bool value -- we don't want to read it again if it's false, - -- but only if it hasn't been set yet! - AlienBiomes = AlienBiomes ~= nil and AlienBiomes or BioInd.AB_tiles() - - -- Basic - if ent.name == "seedling" then - BioInd.writeDebug("Seed Bomb Activated - Basic") - seed_planted_trigger(event) - - -- Standard - elseif ent.name == "seedling-2" then - BioInd.writeDebug("Seed Bomb Activated - Standard") - local currTile = surface.get_tile(position).name - if storage.bi.barren_tiles[currTile] then - BioInd.writeDebug("Can't fertilize %s!", { currTile }) - else - BioInd.writeDebug("Using fertilizer!") - local terrain_name_s = AlienBiomes and "vegetation-green-grass-3" or "grass-3" - surface.set_tiles { { name = terrain_name_s, position = position } } - end - seed_planted_trigger(event) - - -- Advanced - elseif ent.name == "seedling-3" then - BioInd.writeDebug("Seed Bomb Activated - Advanced") - local currTile = surface.get_tile(position).name - if storage.bi.barren_tiles[currTile] then - BioInd.writeDebug("Can't fertilize %s!", { currTile }) - else - BioInd.writeDebug("Using fertilizer!") - local terrain_name_a = AlienBiomes and "vegetation-green-grass-1" or "grass-1" - surface.set_tiles { { name = terrain_name_a, position = position } } - end - seed_planted_trigger(event) - end -end) - --------------------------------------------------------------------- -local function On_Built(event) - BioInd.writeDebug("Entered function On_Built with these data: " .. serpent.block(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 - - - -- 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 = storage.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 - 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 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("storage[%s]: %s", { base_entry.tab, storage[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(storage[base_entry.tab], base, hidden_entities) - BioInd.writeDebug("Stored %s in table: %s", - { BioInd.print_name_id(base), storage[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") - - -- Arboretum radar -- we need to add it to the table! - if entity.type == "radar" and - --entity.name == entities["bi-arboretum-area"].hidden[h_key].name and base then - entity.name == entities["bi-arboretum"].hidden[h_key].name and base then - storage.bi_arboretum_radar_table[entity.unit_number] = base.unit_number - entity.backer_name = "" - BioInd.writeDebug("Added %s to storage.bi_arboretum_radar_table", { BioInd.print_name_id(entity) }) - - -- Electric poles -- we need to take care that they don't hook up to hidden poles! - elseif 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 = storage.compound_entities[entity.name] - local base_entry = compound_entity and storage[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("storage[%s]: %s", - { compound_entity and compound_entity.tab or "nil", compound_entity and storage[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) }) - - -- Arboretum: Need to separately remove the entry from the radar table - if entity.name == "bi-arboretum" and base_entry.radar and base_entry.radar.valid then - storage.bi_arboretum_radar_table[base_entry.radar.unit_number] = nil - BioInd.show("Removed arboretum radar! Table", storage.bi_arboretum_radar_table) - end - - -- Power rails: Connections must be explicitely removed, otherwise the poles - -- from the remaining rails will automatically connect and bridge the gap in - -- the power supply! - if entity.name:match("bi%-%a+%-rail%-power") and base_entry.pole and base_entry.pole.valid then - BioInd.writeDebug("Before") - BioInd.writeDebug("Disconnecting %s!", { BioInd.print_name_id(base_entry.pole) }) - base_entry.pole.disconnect_neighbour() - BioInd.writeDebug("After") - end - - -- 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", { BioInd.print_name_id(base_entry[hidden]) }) - BioInd.remove_entity(base_entry[hidden]) - base_entry[hidden] = nil - end - storage[compound_entity.tab][entity.unit_number] = nil - - -- Rail-to-power: Connections must be explicitely removed, otherwise the poles - -- from the different rail tracks hooked up to this connector will automatically - -- keep the separate power networks connected! - elseif entity.name == "bi-power-to-rail-pole" then - BioInd.writeDebug("Rail-to-power connector has been removed") - entity.disconnect_neighbour() - BioInd.writeDebug("Removed copper wires from %s (%g)", { entity.name, entity.unit_number }) - - -- Removed seedling - elseif entity.name == "seedling" then - BioInd.writeDebug("Seedling has been removed") - remove_plants(entity.position, storage.bi.tree_growing) - - -- Removed tree - elseif entity.type == "tree" and storage.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, storage.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 - - --------------------------------------------------------------------- -local function On_Damage(event) - local f_name = "On_Damage" - BioInd.writeDebug("Entered function %s(%s)", { f_name, event }) - local entity = event.entity - local final_health = event.final_health - - local arb = "bi-arboretum" - local associated - - -- Base was damaged: Find the radar associated with it! - if entity.name == arb then - associated = storage.bi_arboretum_table[entity.unit_number].radar - -- Radar was damaged: Find the base entity! - elseif entity.name == storage.compound_entities[arb].hidden.radar.name then - local base_id = storage.bi_arboretum_radar_table[entity.unit_number] - associated = storage.bi_arboretum_table[base_id].base - end - - if associated and associated.valid then - associated.health = final_health - BioInd.writeDebug("%s was damaged (%s). Reducing health of %s to %s!", { - BioInd.print_name_id(entity), - event.final_damage_amount, - entity.name == arb and "associated radar" or "base", - associated.health - }) - end -end - --------------------------------------------------------------------- -local function On_Death(event) - local f_name = "On_Death" - BioInd.writeDebug("Entered function %s(%s)", { f_name, event }) - - local entity = event.entity - if not entity then - error("Something went wrong -- no entity data!") - end - - if - -- Table checks - storage.compound_entities[entity.name] or - storage.bi.trees[entity.name] or - -- Entity checks - entity.name == storage.compound_entities["bi-arboretum"].hidden.radar.name or - entity.name == "bi-power-to-rail-pole" or - entity.name == "seedling" then - BioInd.writeDebug("Divert to On_Pre_Remove!") - On_Pre_Remove(event) - else - BioInd.writeDebug("Nothing to do!") - end -end - - --------------------------------------------------------------------- --- Radar stuff --------------------------------------------------------------------- - --- Radar completed a sector scan -local function On_Sector_Scanned(event) - local f_name = "On_Sector_Scanned" - BioInd.writeDebug("Entered function %s(%s)", { f_name, event }) - - ---- Each time a Arboretum-Radar scans a sector ---- - --[[ - local arboretum = storage.bi_arboretum_radar_table[event.radar.unit_number] - if arboretum then - Get_Arboretum_Recipe(storage.bi_arboretum_table[arboretum], event) - end - ]] - local radar_unit = event and event.radar and event.radar.unit_number - local arb_base_id = radar_unit and storage.bi_arboretum_radar_table and storage.bi_arboretum_radar_table[radar_unit] - if not arb_base_id then - BioInd.writeDebug("Sector scanned: radar_unit %s not registered in bi_arboretum_radar_table", { tostring(radar_unit) }) - return - end - local arb_table = storage.bi_arboretum_table and storage.bi_arboretum_table[arb_base_id] - if not arb_table then - BioInd.writeDebug("Sector scanned: storage.bi_arboretum_table[%s] missing", { tostring(arb_base_id) }) - return - end - Get_Arboretum_Recipe(arb_table, event) - -end - - --------------------------------------------------------------------- --- Solar Mat stuff --------------------------------------------------------------------- - --------------------------------------------------------------------- --- Solar mat was removed -local function solar_mat_removed(event) - BioInd.writeDebug("Entered solar_mat_removed (\"%s\")", { event }) - - local surface = game.surfaces[event.surface_index] - local tiles = event.tiles - - local pos, x, y - -- tiles contains an array of the old tiles and their position - for t, tile in pairs(tiles) do - if tile.old_tile and tile.old_tile.name == "bi-solar-mat" then - pos = BioInd.normalize_position(tile.position) - x, y = pos.x, pos.y - - BioInd.writeDebug("Looking for hidden entities to remove") - for _, o in pairs(surface.find_entities_filtered { - name = { 'bi-musk-mat-hidden-pole', 'bi-musk-mat-hidden-panel' }, - position = { x + 0.5, y + 0.5 } - } or {}) do - BioInd.show("Removing", o.name) - o.destroy() - end - - -- Remove tile from global tables - local force_name = storage.bi_musk_floor_table.tiles and - storage.bi_musk_floor_table.tiles[x] and - storage.bi_musk_floor_table.tiles[x][y] - if force_name then - BioInd.writeDebug("Removing Musk floor tile from tables!") - storage.bi_musk_floor_table.tiles[x][y] = nil - if not next(storage.bi_musk_floor_table.tiles[x]) then - storage.bi_musk_floor_table.tiles[x] = nil - end - - if storage.bi_musk_floor_table.forces[force_name] and - storage.bi_musk_floor_table.forces[force_name][x] then - storage.bi_musk_floor_table.forces[force_name][x][y] = nil - if not next(storage.bi_musk_floor_table.forces[force_name][x]) then - storage.bi_musk_floor_table.forces[force_name][x] = nil - end - end - end - end - end - - BioInd.writeDebug("bi-solar-mat: removed %g tiles", { table_size(tiles) }) -end - - --------------------------------------------------------------------- --- A solar mat must be placed -local function place_musk_floor(force, position, surface) - BioInd.check_args(force, "string") - position = BioInd.normalize_position(position) or BioInd.arg_err(position, "position") - surface = BioInd.is_surface(surface) or BioInd.arg_err(surface, "surface") - - local x, y = position.x, position.y - local created - for n, name in ipairs({ "bi-musk-mat-hidden-pole", "bi-musk-mat-hidden-panel" }) do - created = surface.create_entity({ name = name, position = { x + 0.5, y + 0.5 }, force = force }) - created.minable = false - created.destructible = false - BioInd.writeDebug("Created %s: %s", { name, created.unit_number }) - end - - -- Add to global tables! - storage.bi_musk_floor_table.tiles[x] = storage.bi_musk_floor_table.tiles[x] or {} - storage.bi_musk_floor_table.tiles[x][y] = force - - storage.bi_musk_floor_table.forces[force] = storage.bi_musk_floor_table.forces[force] or {} - storage.bi_musk_floor_table.forces[force][x] = storage.bi_musk_floor_table.forces[force][x] or {} - storage.bi_musk_floor_table.forces[force][x][y] = true -end - --------------------------------------------------------------------- --- Solar mat was built -local function solar_mat_built(event) - BioInd.show("Entered function \"solar_mat_built\"", event) - -- Called from player, bot and script-raised events, so event may - -- contain "robot" or "player_index" - - local tile = event.tile - local surface = game.surfaces[event.surface_index] - local player = event.player_index and game.players[event.player_index] - local robot = event.robot - local force = (BioInd.UseMuskForce and BioInd.MuskForceName) or - (event.player_index and game.players[event.player_index].force.name) or - (event.robot and event.robot.force.name) or - event.force.name - BioInd.show("Force.name", force) - - -- Item that was used to place the tile - local item = event.item - local old_tiles = event.tiles - - - local position --, x, y - - - -- Musk floor has been built -- create hidden entities! - if tile.name == "bi-solar-mat" then - BioInd.writeDebug("Solar Mat has been built -- must create hidden entities!") - BioInd.show("Tile data", tile) - - for index, t in pairs(old_tiles or { tile }) do - BioInd.show("Read old_tile inside loop", t) - -- event.tiles will also contain landscape tiles like "grass-1", and it will always - -- contain at least one tile - position = BioInd.normalize_position(t.position) - -- If we got here by a call from script_raised_built, force may be stored - -- with the tile - force = force or t.force - BioInd.show("Got force from tile data", t.force or "false") - BioInd.writeDebug("Building solar mat for force %s at position %s", - { tostring(type(force) == "table" and force.name or force), position }) - - place_musk_floor(force, position, surface) - end - - -- Fertilizer/Advanced fertilizer has been used. Check if the tile was valid - -- (no Musk floor, no wooden floor, no concrete etc.) - elseif item and (item.name == "fertilizer" or item.name == "bi-adv-fertilizer") then - local restore_tiles = {} - local products, remove_this - - for index, t in pairs(old_tiles or { tile }) do - BioInd.show("index", index) - BioInd.show("t.old_tile.name", t.old_tile.name) - - -- We want to restore removed tiles if nothing is supposed to grow on them! - if storage.bi.barren_tiles[t.old_tile.name] then - BioInd.writeDebug("%s was used on forbidden ground (%s)!", { item.name, t.old_tile.name }) - restore_tiles[#restore_tiles + 1] = { name = t.old_tile.name, position = t.position } - - -- Is that tile minable? - products = storage.bi.barren_tiles[t.old_tile.name] - if type(products) == "table" then - for p, product in ipairs(products) do - remove_this = { name = product.name, count = product.amount } - if player then - BioInd.writeDebug("Removing %s (%s) from player %s", - { product.name, product.amount, player.name }) - player.remove_item(remove_this) - elseif robot then - BioInd.writeDebug("Removing %s (%s) from robot %s", - { product.name, product.amount, robot.unit_number }) - robot.remove_item(remove_this) - end - end - end - end - end - BioInd.show("restore_tiles", restore_tiles) - if restore_tiles then - surface.set_tiles( - restore_tiles, - true, -- correct_tiles - true, -- remove_colliding_entities - true, -- remove_colliding_decoratives - true -- raise_event - ) - end - - -- Some other tile has been built -- check if it replaced musk floor! - else - local test - local removed_tiles = {} - for index, t in pairs(old_tiles or { tile }) do - position = BioInd.normalize_position(t.position) - test = storage.bi_musk_floor_table and - storage.bi_musk_floor_table.tiles and - storage.bi_musk_floor_table.tiles[position.x] and - storage.bi_musk_floor_table.tiles[position.x][position.y] - if test then - removed_tiles[#removed_tiles + 1] = { - old_tile = { name = "bi-solar-mat" }, - position = position - } - end - end - if next(removed_tiles) then - solar_mat_removed({ surface_index = event.surface_index, tiles = removed_tiles }) - else - BioInd.writeDebug("%s has been built -- nothing to do!", { tile.name }) - end - end -end - - --------------------------------------------------------------------- --- A tille has been changed -local function Tile_Changed(event) - local f_name = "Tile_Changed" - BioInd.writeDebug("Entered function %s(%s)", { f_name, event }) - - -- The event gives us only a list of the new tiles that have been placed. - -- So let's check if any Musk floor has been built! - local new_musk_floor_tiles = {} - local old_musk_floor_tiles = {} - local remove_musk_floor_tiles = {} - local pos, old_tile, force - - local tile_force - - for t, tile in ipairs(event.tiles) do - BioInd.show("t", t) - pos = BioInd.normalize_position(tile.position) - tile_force = storage.bi_musk_floor_table.tiles[pos.x] and - storage.bi_musk_floor_table.tiles[pos.x][pos.y] - BioInd.show("Placed tile", tile.name) - - -- Musk floor was placed - if tile.name == "bi-solar-mat" then - BioInd.writeDebug("Musk floor tile was placed!") - new_musk_floor_tiles[#new_musk_floor_tiles + 1] = { - old_tile = { name = tile.name }, - position = pos, - force = tile_force or - BioInd.UseMuskForce and BioInd.MuskForceName or - "neutral" - } - -- Other tile was placed -- by one of our fertilizers? - elseif tile.name:match("^vegetation%-green%-grass%-[13]$") or - tile.name:match("^green%-grass%-[13]$") then - BioInd.writeDebug("Fertilizer was used!") - - -- Fertilizer was used on a Musk floor tile -- restore the tile! - BioInd.show("Musk floor tile in position", tile_force) - if tile_force then - old_musk_floor_tiles[#old_musk_floor_tiles + 1] = { - old_tile = { name = "bi-solar-mat" }, - position = pos, - force = tile_force - } - end - -- Other tile was placed on a Musk floor tile -- remove Musk floor from lists! - elseif tile_force then - remove_musk_floor_tiles[#remove_musk_floor_tiles + 1] = { - old_tile = { name = "bi-solar-mat" }, - position = pos, - } - end - end - BioInd.show("new_musk_floor_tiles", new_musk_floor_tiles) - BioInd.show("old_musk_floor_tiles", old_musk_floor_tiles) - BioInd.show("remove_musk_floor_tiles", remove_musk_floor_tiles) - - if next(new_musk_floor_tiles) then - solar_mat_built({ - surface_index = event.surface_index, - tile = { name = "bi-solar-mat" }, - force = BioInd.MuskForceName, - tiles = new_musk_floor_tiles - }) - end - if next(old_musk_floor_tiles) then - solar_mat_built({ - surface_index = event.surface_index, - tile = { name = "bi-solar-mat" }, - tiles = old_musk_floor_tiles - }) - end - if next(remove_musk_floor_tiles) then - solar_mat_removed({ surface_index = event.surface_index, tiles = remove_musk_floor_tiles }) - end - BioInd.show("End of function", f_name) -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) -Event.register(Event.tile_build_events, solar_mat_built) -Event.register(Event.tile_remove_events, solar_mat_removed) - - -Event.register(defines.events.on_entity_damaged, On_Damage, function(event) - -- A function is needed for event filtering with stdlib! - local entity = event.entity - - -- Ignore damage without effect (invulnerable/resistant entities) - if event.final_damage_amount ~= 0 and - -- Terraformer/Terraformer radar was damaged - (storage.bi_arboretum_table[entity.unit_number] or - storage.bi_arboretum_radar_table[entity.unit_number]) then - return true - end -end) - --- Radar scan -Event.register(defines.events.on_sector_scanned, On_Sector_Scanned, function(event) - -- A function is needed for event filtering with stdlib! - if event.radar.name == BioInd.compound_entities["bi-arboretum"].hidden.radar.name then - return true - end -end) - --- Tile changed -Event.register(Event.tile_script_action, Tile_Changed) - - ------------------------------------------------------------------------------------- --- 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" or key == "storage") then - error('\n\n[ER Global Lock] Forbidden global *read*:\n' - .. serpent.line { key = key or '' } .. '\n') - end - end -}) diff --git a/Bio_Industries_2/control.lua b/Bio_Industries_2/control.lua index 8f3d8f6..fb3669f 100644 --- a/Bio_Industries_2/control.lua +++ b/Bio_Industries_2/control.lua @@ -460,15 +460,15 @@ local function On_Built(event) elseif entity.type == "electric-pole" then local pole = entity -- Make sure hidden poles of the Bio gardens are connected correctly! - local garden_names = { "bi-bio-garden", "bi-bio-garden-larger", "bi-bio-garden-huge" } - for _, gname in ipairs(garden_names) do - if entities[gname] and pole.name == entities[gname].hidden[h_key].name and base then - BioInd.writeDebug("Bio garden (" .. gname .. ")!") - BioInd.connect_garden_pole(base, pole) - BioInd.writeDebug("Connected %s (%s)", { pole.name, pole.unit_number or "nil" }) - break - end - end + local garden_names = { "bi-bio-garden", "bi-bio-garden-larger", "bi-bio-garden-huge" } + for _, gname in ipairs(garden_names) do + if entities[gname] and pole.name == entities[gname].hidden[h_key].name and base then + BioInd.writeDebug("Bio garden (" .. gname .. ")!") + BioInd.connect_garden_pole(base, pole) + BioInd.writeDebug("Connected %s (%s)", { pole.name, pole.unit_number or "nil" }) + break + end + end -- A seedling has been planted elseif entity.name == "seedling" then @@ -649,16 +649,37 @@ end -- Radar stuff -------------------------------------------------------------------- --- Radar completed a sector scan +-- Robust sector scanned handler for Arboretum radar local function On_Sector_Scanned(event) - local f_name = "On_Sector_Scanned" - BioInd.writeDebug("Entered function %s(%s)", { f_name, event }) + -- defensive checks + BioInd.writeDebug("On_Sector_Scanned fired") + --game.print("On_Sector_Scanned fired") + if not (event and event.radar) then return end + local radar = event.radar + if not (radar.valid and radar.unit_number) then return end - ---- Each time a Arboretum-Radar scans a sector ---- - local arboretum = storage.bi_arboretum_radar_table[event.radar.unit_number] - if arboretum then - Get_Arboretum_Recipe(storage.bi_arboretum_table[arboretum], event) + -- Make sure compound-entity data is available before accessing it + local arb_proto = BioInd.compound_entities and BioInd.compound_entities["bi-arboretum"] + if not (arb_proto and arb_proto.hidden and arb_proto.hidden.radar and arb_proto.hidden.radar.name) then + -- not ready yet (init not finished) — bail out safely + return end + + -- Only handle scans from our arboretum radar type + if radar.name ~= arb_proto.hidden.radar.name then return end + + -- Look up the base arboretum unit_number (stored when the hidden radar was created) + local base_unit_number = storage.bi_arboretum_radar_table and storage.bi_arboretum_radar_table[radar.unit_number] + if not base_unit_number then + -- no mapping found -> nothing to do + return + end + + local arb_table = storage.bi_arboretum_table and storage.bi_arboretum_table[base_unit_number] + if not arb_table then return end + + -- All good: call the arboretum recipe handler + Get_Arboretum_Recipe(arb_table, event) end diff --git a/Bio_Industries_2/control_arboretum.lua b/Bio_Industries_2/control_arboretum.lua index db79727..570d71d 100644 --- a/Bio_Industries_2/control_arboretum.lua +++ b/Bio_Industries_2/control_arboretum.lua @@ -42,12 +42,21 @@ end -- Check that all ingredients are available! local function check_ingredients(arboretum) local recipe = arboretum.get_recipe() - local need = recipe and storage.bi_arboretum_recipe_table[recipe.name] + if not recipe then + --game.print("No recipe set on arboretum") + return nil + end + --game.print("Recipe name: " .. recipe.name) + local need = storage.bi_arboretum_recipe_table[recipe.name] + if not need then + --game.print("No recipe data found for " .. recipe.name) + return nil + end local function check(need, have) for name, amount in pairs(need or {}) do if not (have and have[name]) or (have[name] < amount) then - BioInd.writeDebug("Missing ingredient %s (have %s of %s)", { name, have[name] or 0, amount }) + --game.print("Missing ingredient " .. name .. " (have " .. (have[name] or 0) .. " of " .. amount .. ")") return false end end @@ -55,9 +64,38 @@ local function check_ingredients(arboretum) end local inventory = arboretum.get_inventory(defines.inventory.assembling_machine_input) + local inv_contents_raw = inventory and inventory.get_contents() or {} + + -- Check if inv_contents_raw is a map or list, convert if needed + local function is_map(t) + if type(t) ~= "table" then return false end + for k, v in pairs(t) do + if type(k) ~= "string" or type(v) ~= "number" then + return false + end + end + return true + end + + local inv_contents + if is_map(inv_contents_raw) then + inv_contents = inv_contents_raw + else + -- Convert list of item stacks to map + inv_contents = {} + for _, item in pairs(inv_contents_raw) do + inv_contents[item.name] = (inv_contents[item.name] or 0) + item.count + end + end + + local fluid_contents = arboretum.get_fluid_contents() or {} + + --game.print("Inventory contents (map): " .. serpent.line(inv_contents)) + --game.print("Fluid contents: " .. serpent.line(fluid_contents)) + return need and - check(need.items, inventory and inventory.get_contents()) and - check(need.fluids, arboretum.get_fluid_contents()) and + check(need.items, inv_contents) and + check(need.fluids, fluid_contents) and { ingredients = need, name = recipe.name } or nil end @@ -111,10 +149,14 @@ function Get_Arboretum_Recipe(ArboretumTable, event) local check = check_ingredients(arboretum) local ingredients, recipe_name if check then + --game.print("There are ingredients") ingredients, recipe_name = check.ingredients, check.name + else + --game.print("No ingredients") end if ingredients then + local create_seedling, new_plant pos = BioInd.normalize_position(arboretum.position) or BioInd.arg_err("nil", "position")