require 'util' local remote_api = require 'script.lib' local get_factory_by_entity = remote_api.get_factory_by_entity local get_factory_by_building = remote_api.get_factory_by_building local find_factory_by_building = remote_api.find_factory_by_building local find_surrounding_factory = remote_api.find_surrounding_factory local power_middleman_surface = remote_api.power_middleman_surface local BUILDING_TYPE = BUILDING_TYPE require 'script.layout' local has_layout = Layout.has_layout require 'script.connections.connections' require 'script.updates' require 'script.blueprint' require 'script.camera' require 'script.travel' require 'script.overlay' require 'script.pollution' require 'compat.factoriomaps' local update_hidden_techs = nil -- Function stub local activate_factories = nil -- Function stub -- INITIALIZATION -- local function init_globals() Layout.init() -- List of all factories global.factories = global.factories or {} -- Map: Id from item-with-tags -> Factory global.saved_factories = global.saved_factories or {} -- Map: Player or robot -> Save name to give him on the next relevant event global.pending_saves = global.pending_saves or {} -- Map: Entity unit number -> Factory it is a part of global.factories_by_entity = global.factories_by_entity or {} -- Map: Surface name -> list of factories on it global.surface_factories = global.surface_factories or {} -- Map: Surface name -> number of used factory spots on it global.surface_factory_counters = global.surface_factory_counters or {} -- Scalar global.next_factory_surface = global.next_factory_surface or 0 -- Map: Player index -> Last teleport time global.last_player_teleport = global.last_player_teleport or {} -- Map: Player index -> Whether preview is activated global.player_preview_active = global.player_preview_active or {} -- List of all factory power pole middlemen global.middleman_power_poles = global.middleman_power_poles or {} -- Map: Surface name -> Whether radars are active global.hidden_radars = global.hidden_radars or {} -- List of all spidertrons global.spidertrons = {} for _, surface in pairs(game.surfaces) do for _, spider in pairs(surface.find_entities_filtered{type = 'spider-vehicle'}) do if spider.name ~= 'companion' then global.spidertrons[#global.spidertrons + 1] = spider script.register_on_entity_destroyed(spider) end end end if remote.interfaces['PickerDollies'] then remote.call('PickerDollies', 'add_blacklist_name', 'factory-1', true) remote.call('PickerDollies', 'add_blacklist_name', 'factory-2', true) remote.call('PickerDollies', 'add_blacklist_name', 'factory-3', true) end end script.on_init(function() init_globals() Connections.init_data_structure() Updates.init() Camera.init() power_middleman_surface() for _, force in pairs(game.forces) do update_hidden_techs(force) end Compat.handle_factoriomaps() end) script.on_load(function() Compat.handle_factoriomaps() end) script.on_configuration_changed(function(config_changed_data) init_globals() Updates.run() Camera.init() power_middleman_surface() activate_factories() if remote.interfaces['RSO'] then -- RSO compatibility for surface_name, _ in pairs(global.surface_factories or {}) do pcall(remote.call, 'RSO', 'ignoreSurface', surface_name) end end global.items_with_metadata = nil end) -- POWER MANAGEMENT -- -- disconnects a factory's power pole from the "direct connection pole" aka the pole on the outside surface local function remove_direct_connection(factory) local dc = factory.direct_connection if not dc or not dc.valid then return end for _, pole in pairs(factory.inside_power_poles) do for _, neighbour in pairs(pole.neighbours.copper) do if neighbour == dc then local old = {} for _, neighbour in ipairs(dc.neighbours.copper) do if neighbour ~= pole and neighbour.type ~= 'power-switch' then old[#old + 1] = neighbour end end dc.disconnect_neighbour() for _, neighbour in ipairs(old) do dc.connect_neighbour(neighbour) end factory.direct_connection = nil return end end end end local function delete_middleman(i) local pole = global.middleman_power_poles[i] if pole == 0 then return end global.middleman_power_poles[i] = i < #global.middleman_power_poles and 0 or nil pole.destroy() for _, factory in pairs(global.factories) do if factory.middleman_id == i then factory.middleman_id = nil end end end local function cleanup_middlemen() for i, pole in ipairs(global.middleman_power_poles) do if pole ~= 0 and #pole.neighbours.copper < 2 then delete_middleman(i) end end end local function available_pole(factory) local poles = factory.inside_power_poles for i, pole in ipairs(poles) do local next = poles[i+1] if next then next.connect_neighbour(pole) end end for i, pole in ipairs(poles) do if #pole.neighbours.copper < (i == #poles and 4 or 5) then return pole end end local layout = factory.layout local pole = factory.inside_surface.create_entity{name='factory-overflow-pole', position=poles[1].position, force=poles[1].force} pole.destructible = false pole.disconnect_neighbour() pole.connect_neighbour(poles[#poles]) table.insert(poles, pole) return pole end local function connect_power(factory, pole) if #pole.neighbours.copper == 5 then pole.surface.create_entity{name = 'flying-text', position = pole.position, text = {'electric-pole-wire-limit-reached'}} return end factory.outside_power_pole = pole if factory.inside_surface.name ~= pole.surface.name then available_pole(factory).connect_neighbour(pole) factory.direct_connection = pole return end local n for i, pole in ipairs(global.middleman_power_poles) do if pole == 0 then n = i break end end n = n or #global.middleman_power_poles + 1 local surface = power_middleman_surface() local middleman = surface.create_entity{name = 'factory-power-connection', position = {2*(n%32), 2*math.floor(n/32)}, force = 'neutral'} middleman.destructible = false global.middleman_power_poles[n] = middleman middleman.connect_neighbour(available_pole(factory)) middleman.connect_neighbour(pole) factory.middleman_id = n end function update_power_connection(factory, pole) -- pole parameter is optional local electric_network = factory.outside_energy_receiver.electric_network_id if electric_network == nil then return end local surface = factory.outside_surface local x = factory.outside_x local y = factory.outside_y if not script.active_mods['factorissimo-power-pole-addon'] and global.surface_factory_counters[surface.name] then local surrounding = find_surrounding_factory(surface, {x = x, y = y}) if surrounding then connect_power(factory, available_pole(surrounding)) return end end -- find the nearest connected power pole local D = game.max_electric_pole_supply_area_distance + factory.layout.outside_size / 2 local candidates = {} for _, entity in ipairs(surface.find_entities_filtered{type='electric-pole', area={{x-D, y-D}, {x+D,y+D}}}) do if entity.electric_network_id == electric_network and entity ~= pole then candidates[#candidates+1] = entity end end if #candidates == 0 then return end connect_power(factory, surface.get_closest({x, y}, candidates)) end local function get_factories_near_pole(pole) local D = pole.prototype.supply_area_distance if D == 0 then return {} end D = D + 1 local position = pole.position local x = position.x local y = position.y local result = {} for _, candidate in ipairs(pole.surface.find_entities_filtered{type=BUILDING_TYPE, area={{x-D, y-D}, {x+D,y+D}}}) do if has_layout(candidate.name) then result[#result + 1] = get_factory_by_building(candidate) end end return result end local function power_pole_placed(pole) for _, factory in ipairs(get_factories_near_pole(pole)) do local electric_network = factory.outside_energy_receiver.electric_network_id if electric_network == nil or electric_network ~= pole.electric_network_id then goto continue end if not factory.inside_power_poles[1].valid then goto continue end if electric_network == factory.inside_power_poles[1].electric_network_id then goto continue end connect_power(factory, pole) ::continue:: end end local function power_pole_destroyed(pole) local old_neighbours = {} for _, neighbour in pairs(pole.neighbours.copper) do if neighbour.surface == pole.surface and neighbour.type ~= 'power-switch' then old_neighbours[#old_neighbours + 1] = neighbour end end pole.disconnect_neighbour() for _, factory in ipairs(get_factories_near_pole(pole)) do update_power_connection(factory, pole) end cleanup_middlemen() for _, neighbour in pairs(old_neighbours) do pole.connect_neighbour(neighbour) end end script.on_event(defines.events.on_player_selected_area, function(event) if event.item == 'power-grid-comb' then for _, building in pairs(event.entities) do if has_layout(building.name) then local factory = get_factory_by_building(building) if factory then update_power_connection(factory) end end end end end) -- prevent SHIFT+CLICK on factory power poles script.on_event({defines.events.on_selected_entity_changed, defines.events.on_player_cursor_stack_changed}, function(event) local player = game.get_player(event.player_index) local pole = player.selected if pole and pole.type == 'electric-pole' then local permission = player.permission_group if not permission then permission = game.permissions.create_group() player.permission_group = permission end local has_cross_surface_connections = false for _, neighbour in pairs(pole.neighbours.copper) do if neighbour.surface ~= pole.surface then has_cross_surface_connections = true break end end permission.set_allows_action(defines.input_action.remove_cables, not has_cross_surface_connections) end Camera.update_camera(player) -- also update camera here end) -- FACTORY UPGRADES -- local function build_lights_upgrade(factory) if factory.upgrades.lights then return end factory.upgrades.lights = true factory.inside_surface.daytime = 1 end -- FACTORY GENERATION -- local function update_destructible(factory) if factory.built and factory.building.valid then factory.building.destructible = not settings.global['Factorissimo2-indestructible-buildings'].value end end local function create_factory_position(layout) global.next_factory_surface = global.next_factory_surface + 1 if (settings.global['Factorissimo2-same-surface'].value) then global.next_factory_surface = 1 end local surface_name = 'factory-floor-' .. global.next_factory_surface local surface = game.surfaces[layout.surface_override or surface_name] if surface == nil then surface = game.create_surface(surface_name, {width = 2, height = 2}) surface.daytime = 0.5 surface.freeze_daytime = true if remote.interfaces['RSO'] then -- RSO compatibility pcall(remote.call, 'RSO', 'ignoreSurface', surface_name) end end local n = global.surface_factory_counters[surface_name] or 0 global.surface_factory_counters[surface_name] = n+1 local cx = 16*(n % 8) local cy = 16*math.floor(n / 8) -- To make void chunks show up on the map, you need to tell them they've finished generating. surface.set_chunk_generated_status({cx-2, cy-2}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx-1, cy-2}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx+0, cy-2}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx+1, cy-2}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx-2, cy-1}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx-1, cy-1}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx+0, cy-1}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx+1, cy-1}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx-2, cy+0}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx-1, cy+0}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx+0, cy+0}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx+1, cy+0}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx-2, cy+1}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx-1, cy+1}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx+0, cy+1}, defines.chunk_generated_status.entities) surface.set_chunk_generated_status({cx+1, cy+1}, defines.chunk_generated_status.entities) surface.destroy_decoratives{area={{32*(cx-2),32*(cy-2)},{32*(cx+2),32*(cy+2)}}} local factory = {} factory.inside_surface = surface factory.inside_x = 32*cx factory.inside_y = 32*cy factory.stored_pollution = 0 factory.upgrades = {} global.surface_factories[surface_name] = global.surface_factories[surface_name] or {} global.surface_factories[surface_name][n+1] = factory local fn = #(global.factories)+1 global.factories[fn] = factory factory.id = fn return factory end local function add_tile_rect(tiles, tile_name, xmin, ymin, xmax, ymax) -- tiles is rw local i = #tiles for x = xmin, xmax-1 do for y = ymin, ymax-1 do i = i + 1 tiles[i] = {name = tile_name, position = {x, y}} end end end local function add_tile_mosaic(tiles, tile_name, xmin, ymin, xmax, ymax, pattern) -- tiles is rw local i = #tiles for x = 0, xmax-xmin-1 do for y = 0, ymax-ymin-1 do if (string.sub(pattern[y+1],x+1, x+1) == '+') then i = i + 1 tiles[i] = {name = tile_name, position = {x+xmin, y+ymin}} end end end end local function create_factory_interior(layout, force) local factory = create_factory_position(layout) factory.layout = layout factory.force = force factory.inside_door_x = layout.inside_door_x + factory.inside_x factory.inside_door_y = layout.inside_door_y + factory.inside_y local tiles = {} for _, rect in pairs(layout.rectangles) do add_tile_rect(tiles, rect.tile, rect.x1 + factory.inside_x, rect.y1 + factory.inside_y, rect.x2 + factory.inside_x, rect.y2 + factory.inside_y) end for _, mosaic in pairs(layout.mosaics) do add_tile_mosaic(tiles, mosaic.tile, mosaic.x1 + factory.inside_x, mosaic.y1 + factory.inside_y, mosaic.x2 + factory.inside_x, mosaic.y2 + factory.inside_y, mosaic.pattern) end for _, cpos in pairs(layout.connections) do table.insert(tiles, {name = layout.connection_tile, position = {factory.inside_x + cpos.inside_x, factory.inside_y + cpos.inside_y}}) end factory.inside_surface.set_tiles(tiles) local power_pole = factory.inside_surface.create_entity{ name = 'factory-power-pole', position = {factory.inside_x + layout.inside_energy_x, factory.inside_y + layout.inside_energy_y}, force = force } power_pole.destructible = false factory.inside_power_poles = {power_pole} local radar = factory.inside_surface.create_entity{ name = 'factory-hidden-radar', position = {factory.inside_x, factory.inside_y}, force = force } radar.destructible = false radar.active = false factory.radar = radar if force.technologies['factory-interior-upgrade-lights'].researched then build_lights_upgrade(factory) end factory.inside_overlay_controllers = {} if force.technologies['factory-interior-upgrade-display'].researched then Overlay.build_display_upgrade(factory) end factory.connections = {} factory.connection_settings = {} factory.connection_indicators = {} return factory end local function create_factory_exterior(factory, building) local layout = factory.layout local force = factory.force factory.outside_x = building.position.x factory.outside_y = building.position.y factory.outside_door_x = factory.outside_x + layout.outside_door_x factory.outside_door_y = factory.outside_y + layout.outside_door_y factory.outside_surface = building.surface local oer = factory.outside_surface.create_entity{name = layout.outside_energy_receiver_type, position = {factory.outside_x, factory.outside_y}, force = force} oer.destructible = false oer.operable = false oer.rotatable = false factory.outside_energy_receiver = oer factory.outside_overlay_displays = {} factory.outside_port_markers = {} global.factories_by_entity[building.unit_number] = factory factory.building = building factory.built = true Connections.recheck_factory(factory, nil, nil) update_power_connection(factory) Overlay.update_overlay(factory) update_destructible(factory) return factory end local function toggle_port_markers(factory) if not factory.built then return end if #(factory.outside_port_markers) == 0 then for id, cpos in pairs(factory.layout.connections) do local sprite_data = { sprite = 'utility/indication_arrow', orientation = cpos.direction_out/8, target = factory.building, surface = factory.building.surface, target_offset = {cpos.outside_x - 0.5 * cpos.indicator_dx, cpos.outside_y - 0.5 * cpos.indicator_dy}, only_in_alt_mode = true, render_layer = 'entity-info-icon', } table.insert(factory.outside_port_markers, rendering.draw_sprite(sprite_data)) end else for _, sprite in pairs(factory.outside_port_markers) do rendering.destroy(sprite) end factory.outside_port_markers = {} end end local function cleanup_factory_exterior(factory, building) factory.outside_energy_receiver.destroy() if factory.middleman_id then delete_middleman(factory.middleman_id) end remove_direct_connection(factory) Connections.disconnect_factory(factory) for _, render_id in pairs(factory.outside_overlay_displays) do rendering.destroy(render_id) end factory.outside_overlay_displays = {} for _, render_id in pairs(factory.outside_port_markers) do rendering.destroy(render_id) end factory.outside_port_markers = {} factory.building = nil factory.built = false end -- FACTORY SAVING AND LOADING -- commands.add_command('give-lost-factory-buildings', {'command-help-message.give-lost-factory-buildings'}, function(event) local player = game.players[event.player_index] if not (player and player.connected and player.admin) then return end local inventory = player.get_main_inventory() for id, factory in pairs(global.saved_factories) do for i = 1, #inventory do local stack = inventory[i] if stack.valid_for_read and stack.name == factory.layout.name and stack.type == 'item-with-tags' and stack.tags.id == id then goto found end end player.insert{name = factory.layout.name, count = 1, tags = {id = id}} ::found:: end end) -- FACTORY PLACEMENT AND DESTRUCTION -- local function cancel_creation(entity, player_index, message) local inserted = 0 local item_to_place = entity.prototype.items_to_place_this if not item_to_place then return end item_to_place = item_to_place[1] local surface = entity.surface local position = entity.position local force = entity.force if player_index then local player = game.get_player(player_index) if player.mine_entity(entity, false) then inserted = 1 elseif item_to_place then inserted = player.insert(item_to_place) end end entity.destroy{raise_destroy = true} if inserted == 0 and item_to_place then surface.spill_item_stack(position, item_to_place, true, force, false) end if message then surface.create_entity{ name = 'flying-text', position = position, text = message, render_player_index = player_index } end end local function can_place_factory_here(tier, surface, position) local factory = find_surrounding_factory(surface, position) if not factory then return true end local outer_tier = factory.layout.tier if outer_tier > tier and (factory.force.technologies['factory-recursion-t1'].researched or settings.global['Factorissimo2-free-recursion'].value) then return true end if (outer_tier >= tier or settings.global['Factorissimo2-better-recursion-2'].value) and (factory.force.technologies['factory-recursion-t2'].researched or settings.global['Factorissimo2-free-recursion'].value) then return true end if outer_tier > tier then surface.create_entity{name='flying-text', position=position, text={'factory-connection-text.invalid-placement-recursion-1'}, force = factory.force} elseif (outer_tier >= tier or settings.global['Factorissimo2-better-recursion-2'].value) then surface.create_entity{name='flying-text', position=position, text={'factory-connection-text.invalid-placement-recursion-2'}, force = factory.force} else surface.create_entity{name='flying-text', position=position, text={'factory-connection-text.invalid-placement'}, force = factory.force} end return false end -- When a connection piece is placed or destroyed, check if can be connected to a factory building local function recheck_nearby_connections(entity, delayed) local surface = entity.surface local pos = entity.position local sbox = table.deepcopy(entity.prototype.selection_box) local orientation = entity.orientation if orientation == 0 then -- north -- sbox is fine elseif orientation == 0.5 then -- south sbox.left_top.y, sbox.right_bottom.y = -sbox.right_bottom.y, -sbox.left_top.y elseif orientation == 0.25 then -- east sbox.left_top.y, sbox.left_top.x, sbox.right_bottom.x, sbox.right_bottom.y = -sbox.right_bottom.x, -sbox.right_bottom.y, -sbox.left_top.y, -sbox.left_top.x elseif orientation == 0.75 then -- west sbox.left_top.y, sbox.right_bottom.y = -sbox.right_bottom.y, -sbox.left_top.y sbox.left_top.y, sbox.left_top.x, sbox.right_bottom.x, sbox.right_bottom.y = -sbox.right_bottom.x, -sbox.right_bottom.y, -sbox.left_top.y, -sbox.left_top.x end -- Expand box to catch factories and also avoid illegal zero-area finds local bbox = { left_top = {x = pos.x - 0.3 + sbox.left_top.x, y = pos.y - 0.3 + sbox.left_top.y}, right_bottom = {x = pos.x + 0.3 + sbox.right_bottom.x, y = pos.y + 0.3 + sbox.right_bottom.y} } for _, candidate in pairs(surface.find_entities_filtered{area = bbox, type = BUILDING_TYPE}) do if candidate ~= entity and has_layout(candidate.name) then local factory = get_factory_by_building(candidate) if factory then if delayed then Connections.recheck_factory_delayed(factory, bbox, nil) else Connections.recheck_factory(factory, bbox, nil) end end end end local factory = find_surrounding_factory(surface, pos) if factory then if delayed then Connections.recheck_factory_delayed(factory, nil, bbox) else Connections.recheck_factory(factory, nil, bbox) end end end local function create_fresh_factory(entity) local layout = Layout.create_layout(entity.name) local factory = create_factory_interior(layout, entity.force) create_factory_exterior(factory, entity) factory.inactive = not can_place_factory_here(layout.tier, entity.surface, entity.position) return factory end local function handle_factory_placed(entity, tags) if not tags or not tags.id then create_fresh_factory(entity) elseif global.saved_factories[tags.id] then -- This is a saved factory, we need to unpack it local factory = global.saved_factories[tags.id] global.saved_factories[tags.id] = nil create_factory_exterior(factory, entity) factory.inactive = not can_place_factory_here(factory.layout.tier, entity.surface, entity.position) elseif global.factories[tags.id] then -- This factory was copied from somewhere else. Clone all contained entities local factory = create_fresh_factory(entity) Blueprint.copy_entity_ghosts(global.factories[tags.id], factory) Overlay.update_overlay(factory) else entity.surface.create_entity{name='flying-text', position=entity.position, text={'factory-connection-text.invalid-factory-data'}} entity.destroy() end end script.on_event({defines.events.on_built_entity, defines.events.on_robot_built_entity, defines.events.script_raised_built, defines.events.script_raised_revive}, function(event) local entity = event.created_entity or event.entity if has_layout(entity.name) then local stack = event.stack if stack and stack.valid_for_read and stack.type == 'item-with-tags' then handle_factory_placed(entity, stack.tags) else handle_factory_placed(entity, event.tags) end elseif Connections.is_connectable(entity) then if entity.name == 'factory-circuit-connector' then entity.operable = false else local _, _, pipe_name_input = entity.name:find('^factory%-(.*)%-input$') local _, _, pipe_name_output = entity.name:find('^factory%-(.*)%-output$') local pipe_name = pipe_name_input or pipe_name_output if pipe_name then entity = remote_api.replace_entity(entity, pipe_name) end end recheck_nearby_connections(entity) elseif entity.type == 'electric-pole' then power_pole_placed(entity) elseif entity.type == 'solar-panel' or entity.name == 'bi-solar-boiler' then if global.surface_factory_counters[entity.surface.name] then cancel_creation(entity, event.player_index, {'factory-connection-text.invalid-placement'}) else entity.force.technologies['factory-interior-upgrade-lights'].researched = true end elseif entity.type == 'entity-ghost' and Connections.indicator_names[entity.ghost_name] then Blueprint.unpack_connection_settings_from_blueprint(entity) entity.destroy() elseif entity.type == 'entity-ghost' and (entity.ghost_name == 'factory-overlay-controller' or entity.ghost_name == 'factory-blueprint-anchor') then entity.destroy() elseif entity.type == 'spider-vehicle' and entity.name ~= 'companion' then global.spidertrons[entity.unit_number] = entity script.register_on_entity_destroyed(entity) end end) local sprite_path_translation = { item = 'item', fluid = 'fluid', virtual = 'virtual-signal', } local function generate_factory_item_description(factory) local overlay = factory.inside_overlay_controller local params = {} if overlay and overlay.valid then for _, param in pairs(overlay.get_or_create_control_behavior().parameters) do if param and param.signal and param.signal.name then table.insert(params, '[' .. sprite_path_translation[param.signal.type] .. '=' .. param.signal.name .. ']') end end end params = table.concat(params, ' ') if params ~= '' then return '[font=heading-2]' .. params .. '[/font]' end end -- How players pick up factories -- Working factory buildings don't return items, so we have to manually give the player an item script.on_event({defines.events.on_player_mined_entity, defines.events.on_robot_mined_entity}, function(event) local entity = event.entity if has_layout(entity.name) then local factory = get_factory_by_building(entity) if not factory then return end cleanup_factory_exterior(factory, entity) global.saved_factories[factory.id] = factory local buffer = event.buffer buffer.clear() buffer.insert{name = factory.layout.name} buffer[1].tags = {id = factory.id} local description = generate_factory_item_description(factory) if description then buffer[1].custom_description = description end elseif Connections.is_connectable(entity) then recheck_nearby_connections(entity, true) -- Delay elseif entity.type == 'electric-pole' then power_pole_destroyed(entity) end end) local function rebuild_factory(entity) local factory = get_factory_by_building(entity) if not factory then return end global.factories_by_entity[entity.unit_number] = nil local entity = entity.surface.create_entity{ name = entity.name, position = entity.position, force = entity.force, raise_built = false, create_build_effect_smoke = false, player = entity.last_user } global.factories_by_entity[entity.unit_number] = factory factory.building = entity Overlay.update_overlay(factory) if #factory.outside_port_markers ~= 0 then factory.outside_port_markers = {} toggle_port_markers(factory) end entity.surface.create_entity{name = 'flying-text', position = entity.position, text = {'factory-cant-be-mined'}} end local fake_robots = {['repair-block-robot'] = true} -- Modded construction robots with heavy control scripting script.on_event(defines.events.on_robot_pre_mined, function(event) local entity = event.entity if has_layout(entity.name) and fake_robots[event.robot.name] then rebuild_factory(entity) entity.destroy() elseif Connections.is_connectable(entity) then recheck_nearby_connections(entity, true) -- Delay elseif entity.type == 'item-entity' and entity.stack.valid_for_read and has_layout(entity.stack.name) then event.robot.destructible = false end end) -- How biters pick up factories -- Too bad they don't have hands script.on_event(defines.events.on_entity_died, function(event) local entity = event.entity if has_layout(entity.name) then local factory = get_factory_by_building(entity) if not factory then return end global.saved_factories[factory.id] = factory cleanup_factory_exterior(factory, entity) local item = entity.surface.create_entity{ name = 'item-on-ground', position = entity.position, stack = {name = factory.layout.name, tags = {id = factory.id}} } item.order_deconstruction(entity.force) item.to_be_looted = true local description = generate_factory_item_description(factory) if description then item.stack.custom_description = description end elseif Connections.is_connectable(entity) then recheck_nearby_connections(entity, true) -- Delay elseif entity.type == 'electric-pole' then power_pole_destroyed(entity) end end) script.on_event(defines.events.on_post_entity_died, function(event) if not has_layout(event.prototype.name) or not event.ghost then return end local factory = global.factories_by_entity[event.unit_number] if not factory then return end event.ghost.tags = {id = factory.id} end) -- Just rebuild the factory in this case script.on_event(defines.events.script_raised_destroy, function(event) local entity = event.entity if has_layout(entity.name) then rebuild_factory(entity) elseif Connections.is_connectable(entity) then recheck_nearby_connections(entity, true) -- Delay elseif entity.type == 'electric-pole' then power_pole_destroyed(entity) end end) -- How to clone your factory -- This implementation will not actually clone factory buildings, but move them to where they were cloned. local clone_forbidden_prefixes = { 'factory-1-', 'factory-2-', 'factory-3-', 'factory-power-input-', 'factory-connection-indicator-', 'factory-power-pole', 'factory-overlay-controller', 'factory-overlay-display', 'factory-port-marker', 'factory-fluid-dummy-connector' } local function is_entity_clone_forbidden(name) for _, prefix in pairs(clone_forbidden_prefixes) do if name:sub(1, #prefix) == prefix then return true end end return false end script.on_event(defines.events.on_entity_cloned, function(event) local src_entity = event.source local dst_entity = event.destination if is_entity_clone_forbidden(dst_entity.name) then dst_entity.destroy() elseif has_layout(src_entity.name) then local factory = get_factory_by_building(src_entity) cleanup_factory_exterior(factory, src_entity) if src_entity.valid then src_entity.destroy() end create_factory_exterior(factory, dst_entity) end end) -- ON TICK -- CONNECTION_UPDATE_RATE = 5 script.on_nth_tick(CONNECTION_UPDATE_RATE, Connections.update) script.on_nth_tick(180, function(event) local has_players = {} for _, player in pairs(game.players) do if global.surface_factory_counters[player.surface.name] and (player.render_mode == defines.render_mode.chart or player.render_mode == defines.render_mode.chart_zoomed_in) then has_players[player.surface.name] = true end end for surface, _ in pairs(global.surface_factory_counters) do surface = game.get_surface(surface) local players = not not has_players[surface.name] if players ~= global.hidden_radars[surface.name] then for _, factory in pairs(global.factories) do if factory.radar.valid and factory.inside_surface == surface then factory.radar.active = players end end global.hidden_radars[surface.name] = players end end end) -- CONNECTION SETTINGS -- script.on_event(defines.events.on_player_rotated_entity, function(event) local entity = event.entity if Connections.indicator_names[entity.name] then entity.direction = event.previous_direction elseif Connections.is_connectable(entity) then recheck_nearby_connections(entity) if entity.valid and entity.type == 'underground-belt' then local neighbour = entity.neighbours if neighbour then recheck_nearby_connections(neighbour) end end end end) script.on_event('factory-rotate', function(event) local player = game.players[event.player_index] local entity = player.selected if not entity then return end if has_layout(entity.name) then local factory = get_factory_by_building(entity) if factory then --and player.is_cursor_empty() then toggle_port_markers(factory) end elseif Connections.indicator_names[entity.name] then local factory = find_surrounding_factory(entity.surface, entity.position) if factory then Connections.rotate(factory, entity) end end end) script.on_event('factory-increase', function(event) local entity = game.players[event.player_index].selected if not entity then return end if Connections.indicator_names[entity.name] then local factory = find_surrounding_factory(entity.surface, entity.position) if factory then Connections.adjust(factory, entity, true) end end end) script.on_event('factory-decrease', function(event) local entity = game.players[event.player_index].selected if not entity then return end if Connections.indicator_names[entity.name] then local factory = find_surrounding_factory(entity.surface, entity.position) if factory then Connections.adjust(factory, entity, false) end end end) -- MISC -- update_hidden_techs = function(force) if settings.global['Factorissimo2-hide-recursion'] and settings.global['Factorissimo2-hide-recursion'].value then force.technologies['factory-recursion-t1'].enabled = false force.technologies['factory-recursion-t2'].enabled = false elseif settings.global['Factorissimo2-hide-recursion-2'] and settings.global['Factorissimo2-hide-recursion-2'].value then force.technologies['factory-recursion-t1'].enabled = true force.technologies['factory-recursion-t2'].enabled = false else force.technologies['factory-recursion-t1'].enabled = true force.technologies['factory-recursion-t2'].enabled = true end end script.on_event(defines.events.on_runtime_mod_setting_changed, function(event) local setting = event.setting if setting == 'Factorissimo2-hide-recursion' or setting == 'Factorissimo2-hide-recursion-2' then for _, force in pairs(game.forces) do update_hidden_techs(force) end elseif setting == 'Factorissimo2-indestructible-buildings' then for _, factory in pairs(global.factories) do update_destructible(factory) end end end) script.on_event(defines.events.on_force_created, function(event) local force = event.force update_hidden_techs(force) end) script.on_event(defines.events.on_forces_merging, function(event) for _, factory in pairs(global.factories) do if not factory.force.valid then factory.force = game.forces['player'] end if factory.force.name == event.source.name then factory.force = event.destination end end end) activate_factories = function() for _, factory in pairs(global.factories) do factory.inactive = factory.outside_surface.valid and not can_place_factory_here( factory.layout.tier, factory.outside_surface, {x = factory.outside_x, y = factory.outside_y} ) end end script.on_event(defines.events.on_research_finished, function(event) if not global.factories then return end -- In case any mod or scenario script calls LuaForce.research_all_technologies() during its on_init local research = event.research local name = research.name if name == 'factory-connection-type-fluid' or name == 'factory-connection-type-chest' or name == 'factory-connection-type-circuit' then for _, factory in pairs(global.factories) do if factory.built then Connections.recheck_factory(factory, nil, nil) end end elseif name == 'factory-interior-upgrade-lights' then for _, factory in pairs(global.factories) do build_lights_upgrade(factory) end elseif name == 'factory-interior-upgrade-display' then for _, factory in pairs(global.factories) do Overlay.build_display_upgrade(factory) end elseif name == 'factory-interior-upgrade-roboport' then for _, factory in pairs(global.factories) do build_roboport_upgrade(factory) end elseif name == 'factory-recursion-t1' or name == 'factory-recursion-t2' then activate_factories() elseif name == 'factory-preview' then for _, player in pairs(game.players) do Camera.get_camera_toggle_button(player) end end end) script.on_event(defines.events.on_runtime_mod_setting_changed, function(event) if event.setting_type == 'runtime-global' then activate_factories() end end) remote.add_interface('factorissimo', remote_api)