--luacheck: ignore local Event = require 'utils.event' local HDT = require 'modules.hidden_dimension.table' local Public = {} Public.events = { reset_game = Event.generate_event_name('reset_game'), init_surfaces = Event.generate_event_name('init_surfaces') } --- If true then surface will be picked to nauvis. Public.enable_auto_init = true local deepcopy = table.deepcopy local transport_table = HDT.transport_table local levels_table = HDT.levels_table local math_max = math.max local table_insert = table.insert local function get_table(t) local key local value for k, v in pairs(t) do key = k value = v end return key, value end local function teleport(entity, pos, surface) local sane_pos = surface.find_non_colliding_position(entity.name, pos, 0, 1, 1) if entity.type == 'character' then for k, v in pairs(game.players) do if v.character == entity then v.teleport(sane_pos, surface) end end end end local function clear_surroundings(surface, pos) local entity = surface.find_entities(pos) for i, _ in ipairs(entity) do if entity[i].type ~= 'character' then entity[i].destroy() else teleport(entity[i], {0, 0}, entity[i].surface) end end end local function transport_resources(container1, container2, transport_type) if container1 == nil or container2 == nil then return end if not container1.valid or not container2.valid then return end local function average(c1c, c2c) local average_content = (c1c + c2c) / 2 c1c = average_content c2c = average_content return c1c, c2c end local function get_steam_temperature(container) local temperature = 0 local function test_for(temp) local count = container.remove_fluid({name = 'steam', amount = 1, temperature = temp}) if count ~= 0 then temperature = temp else return end container.insert_fluid({name = 'steam', amount = count, temperature = temp}) end test_for(15) if temperature == 0 then test_for(165) if temperature == 0 then test_for(500) if temperature == 0 then temperature = 165 end end end return temperature end local function divide_fluids() local af = container1.get_fluid_contents() local bf = container2.get_fluid_contents() local name1, amount1 = get_table(af) local name2, amount2 = get_table(bf) amount1 = amount1 or 0 amount2 = amount2 or 0 if ((not name1 and not name2) or (name1 and name2 and name1 ~= name2) or (amount1 < 1 and amount2 < 1) or (amount1 == amount2)) then return end if (not name1) then name1 = name2 elseif (not name2) then name2 = name1 end local v = (amount1 + amount2) / 2 if (name1 == 'steam') then local temp local at = get_steam_temperature(container1) local bt = get_steam_temperature(container2) temp = math_max(at, bt) container1.clear_fluid_inside() container2.clear_fluid_inside() container1.insert_fluid({name = name1, amount = v, temperature = temp}) container2.insert_fluid({name = name2, amount = v, temperature = temp}) else container1.clear_fluid_inside() container2.clear_fluid_inside() container1.insert_fluid({name = name1, amount = v}) container2.insert_fluid({name = name2, amount = v}) end end local function divide_contents() local chest1 = container1.get_inventory(defines.inventory.chest) local chest2 = container2.get_inventory(defines.inventory.chest) for k, v in pairs(chest1.get_contents()) do local t = {name = k, count = v} local c = chest2.insert(t) if (c > 0) then chest1.remove({name = k, count = c}) end end end if container1.type == 'container' and container2.type == 'container' then if transport_type == 'in-out' then divide_contents() end elseif container1.type == 'pipe-to-ground' and container2.type == 'pipe-to-ground' then divide_fluids() else if transport_type == 'average' then if container1.temperature and container2.temperature then container1.temperature, container2.temperature = average(container1.temperature, container2.temperature) end end end end local function create_underground_floor(surface, size, going_down) local function tile_generation(floor_type, offset_pos, floor_pos, floor_area) local area = {} if type(floor_area) == 'table' then area.x = floor_area.x area.y = floor_area.y else area.x = floor_area area.y = floor_area end local pos = {} pos.x = floor_pos.x + offset_pos.x pos.y = floor_pos.y + offset_pos.y local tiles = {} for i = 0, area.x - 1 do for j = 0, area.y - 1 do table_insert(tiles, {name = floor_type, position = {i + pos.x, j + pos.y}}) end end surface.set_tiles(tiles) end tile_generation('tutorial-grid', {x = 0, y = 0}, {x = -size / 2, y = -size / 2}, {x = size - 1, y = size}) tile_generation('black-refined-concrete', {x = 0, y = 0}, {x = -size / 2, y = -size / 2}, {x = size - 1, y = size}) tile_generation('hazard-concrete-left', {x = 0, y = 0}, {x = -3, y = -7}, {x = 6, y = 3}) if going_down then tile_generation('hazard-concrete-left', {x = 0, y = 0}, {x = -3, y = 3}, {x = 6, y = 3}) tile_generation('hazard-concrete-left', {x = 0, y = 0}, {x = -2, y = -2}, {x = 4, y = 3}) else tile_generation('black-refined-concrete', {x = 0, y = 0}, {x = -size / 2, y = -size / 2}, {x = size - 1, y = size}) end end local function create_main_surface(rebuild) local hidden_dimension = HDT.get('hidden_dimension') local name = hidden_dimension.hd_surface local position = hidden_dimension.position if not game.surfaces[name] then return end if not game.surfaces[name].valid then return end local surface = game.surfaces[name] if rebuild then hidden_dimension.main_surface.reference = surface.create_entity { name = 'car', position = position, force = game.forces.enemy, create_build_effect_smoke = false } hidden_dimension.main_surface.reference.minable = false hidden_dimension.main_surface.reference.destructible = false hidden_dimension.main_surface.reference.operable = false hidden_dimension.main_surface.reference.get_inventory(defines.inventory.fuel).insert({name = 'coal', count = 100}) return end if not hidden_dimension.main_surface.reference or not hidden_dimension.main_surface.reference.valid then hidden_dimension.main_surface.reference = surface.create_entity { name = 'car', position = {position.x, position.y - 23}, force = game.forces.enemy, create_build_effect_smoke = false } hidden_dimension.main_surface.reference.minable = false hidden_dimension.main_surface.reference.destructible = false hidden_dimension.main_surface.reference.operable = false hidden_dimension.main_surface.reference.get_inventory(defines.inventory.fuel).insert({name = 'coal', count = 100}) if hidden_dimension.logistic_research_level == 0 then return end Public.create_chests(hidden_dimension.main_surface, hidden_dimension.logistic_research_level, 'in_and_out') end end local function create_underground_surfaces() local function create_underground(floor_table, name, going_down) --local underground_level floor_table.surface = game.create_surface(name, {width = 14, height = 16}) floor_table.surface.always_day = true floor_table.surface.daytime = 0.5 floor_table.surface.request_to_generate_chunks({0, 0}, 10) floor_table.surface.force_generate_chunk_requests() local clear_ent = floor_table.surface.find_entities() for i, _ in ipairs(clear_ent) do clear_ent[i].destroy() end floor_table.name = name floor_table.size = 16 floor_table.surface.destroy_decoratives({area = {{-floor_table.size, -floor_table.size}, {floor_table.size, floor_table.size}}}) create_underground_floor(floor_table.surface, floor_table.size, going_down) floor_table.going_up.reference = floor_table.surface.create_entity { name = 'car', position = {0, -6}, force = game.forces.enemy, create_build_effect_smoke = false } rendering.draw_light( { sprite = 'utility/light_medium', scale = 5.5, intensity = 1, minimum_darkness = 0, oriented = true, color = {255, 255, 255}, target = floor_table.going_up.reference, surface = floor_table.surface, visible = true, only_in_alt_mode = false } ) floor_table.going_up.reference.minable = false floor_table.going_up.reference.destructible = false floor_table.going_up.reference.operable = false floor_table.going_up.reference.get_inventory(defines.inventory.fuel).insert({name = 'coal', count = 100}) if going_down then floor_table.going_down.reference = floor_table.surface.create_entity { name = 'car', position = {0, 4}, force = game.forces.enemy, create_build_effect_smoke = false } floor_table.going_down.reference.minable = false floor_table.going_down.reference.destructible = false floor_table.going_down.reference.operable = false floor_table.going_down.reference.get_inventory(defines.inventory.fuel).insert({name = 'coal', count = 100}) rendering.draw_light( { sprite = 'utility/light_medium', scale = 5.5, intensity = 1, minimum_darkness = 0, oriented = true, color = {255, 255, 255}, target = floor_table.going_down.reference, surface = floor_table.surface, visible = true, only_in_alt_mode = false } ) end end local hidden_dimension = HDT.get('hidden_dimension') if not hidden_dimension.reset_counter then hidden_dimension.reset_counter = 1 else hidden_dimension.reset_counter = hidden_dimension.reset_counter + 1 end create_underground(hidden_dimension.level_1, 'level_1_' .. tostring(hidden_dimension.reset_counter), true) create_underground(hidden_dimension.level_2, 'level_2_' .. tostring(hidden_dimension.reset_counter), false) end local function reset_surface() HDT.reset_table() local hidden_dimension = HDT.get('hidden_dimension') hidden_dimension.logistic_research_level = 0 hidden_dimension.level_1.going_down.entities = {} hidden_dimension.level_1.going_up.entities = {} hidden_dimension.level_2.going_down.entities = {} hidden_dimension.level_2.going_up.entities = {} hidden_dimension.main_surface.entities = {} game.delete_surface('level_1_' .. tostring(hidden_dimension.reset_counter)) game.delete_surface('level_2_' .. tostring(hidden_dimension.reset_counter)) create_underground_surfaces() end local function logistic_update() local hidden_dimension = HDT.get('hidden_dimension') local name = hidden_dimension.hd_surface if not game.surfaces[name] then return end if not game.surfaces[name].valid then return end local function energy_update(t) local g = 0 local c = 0 for k, v in pairs(t) do if (v.valid) then g = g + v.energy c = c + v.electric_buffer_size end end for k, v in pairs(t) do if (v.valid) then local r = (v.electric_buffer_size / c) v.energy = g * r end end end local function entrance_transport_resources(transport1, transport2) transport_resources(transport1.reference, transport2.reference, 'average') if transport1.transport_type == 'out_and_out' then transport_resources(transport2.entities.chest_1, transport1.entities.chest_1, 'in-out') transport_resources(transport2.entities.chest_2, transport1.entities.chest_2, 'in-out') elseif transport1.transport_type == 'in_and_out' then transport_resources(transport1.entities.chest_1, transport2.entities.chest_1, 'in-out') transport_resources(transport2.entities.chest_2, transport1.entities.chest_2, 'in-out') end transport_resources(transport1.entities.pipe_1, transport2.entities.pipe_1, 'in-out') transport_resources(transport2.entities.pipe_2, transport1.entities.pipe_2, 'in-out') if hidden_dimension.logistic_research_level > 1 then transport_resources(transport1.entities.pipe_3, transport2.entities.pipe_3, 'in-out') transport_resources(transport2.entities.pipe_4, transport1.entities.pipe_4, 'in-out') if hidden_dimension.logistic_research_level > 2 then transport_resources(transport1.entities.pipe_5, transport2.entities.pipe_5, 'in-out') transport_resources(transport2.entities.pipe_6, transport1.entities.pipe_6, 'in-out') end end end entrance_transport_resources(hidden_dimension.main_surface, hidden_dimension.level_1.going_up) entrance_transport_resources(hidden_dimension.level_1.going_down, hidden_dimension.level_2.going_up) transport_resources(hidden_dimension.level_1.going_down.reference, hidden_dimension.level_1.going_up.reference, 'average') energy_update(hidden_dimension.energy) end local function on_research_finished(event) local hidden_dimension = HDT.get('hidden_dimension') if event.research.name == 'automation' or event.research.name == 'automation-2' or event.research.name == 'automation-3' then if event.research.name == 'automation' then hidden_dimension.level_1.size = 32 hidden_dimension.level_2.size = 32 create_underground_floor(hidden_dimension.level_1.surface, hidden_dimension.level_1.size, true) create_underground_floor(hidden_dimension.level_2.surface, hidden_dimension.level_1.size, false) elseif event.research.name == 'automation-2' then hidden_dimension.level_1.size = 64 create_underground_floor(hidden_dimension.level_1.surface, hidden_dimension.level_1.size, true) elseif event.research.name == 'automation-3' then hidden_dimension.level_2.size = 64 create_underground_floor(hidden_dimension.level_2.surface, hidden_dimension.level_1.size, false) end elseif event.research.name == 'logistics' then hidden_dimension.logistic_research_level = 1 Public.create_chests_logistics(1) elseif event.research.name == 'logistics-2' then hidden_dimension.logistic_research_level = 2 Public.upgrade_transport_buildings(2) elseif event.research.name == 'logistics-3' then hidden_dimension.logistic_research_level = 3 Public.upgrade_transport_buildings(3) elseif event.research.name == 'electric-energy-accumulators' then -- fix power end end local function through_teleporter_update() local hidden_dimension = HDT.get('hidden_dimension') local function teleport_players_around(source, destination) if source == nil or source.valid ~= true then return end if destination == nil or (not destination.valid) then return end local function surface_play_sound(sound_path, surface, pos) for k, v in pairs(game.connected_players) do if v.surface.name == surface then v.play_sound {path = sound_path, position = pos} end end end local to_teleport_out_entity_list = source.surface.find_entities_filtered { area = { {source.position.x - 1.1, source.position.y - 1.1}, {source.position.x + 1.1, source.position.y + 1.1} }, type = 'character' } for i, v in ipairs(to_teleport_out_entity_list) do if v.type == 'character' then local pos = {x = destination.position.x, y = destination.position.y} if v.position.y < source.position.y then pos.y = pos.y + 2 else pos.y = pos.y - 2 end teleport(v, pos, destination.surface) local sound = 'utility/wire_connect_pole' surface_play_sound(sound, source.surface.name, source.position) surface_play_sound(sound, destination.surface.name, destination.position) end end end if hidden_dimension.main_surface and hidden_dimension.main_surface.reference then teleport_players_around(hidden_dimension.main_surface.reference, hidden_dimension.level_1.going_up.reference) end if hidden_dimension.level_1 and hidden_dimension.level_1.going_up and hidden_dimension.level_1.going_up.reference then teleport_players_around(hidden_dimension.level_1.going_up.reference, hidden_dimension.main_surface.reference) end if hidden_dimension.level_1 and hidden_dimension.level_1.going_down and hidden_dimension.level_1.going_down.reference then teleport_players_around(hidden_dimension.level_1.going_down.reference, hidden_dimension.level_2.going_up.reference) end if hidden_dimension.level_2 and hidden_dimension.level_2.going_up and hidden_dimension.level_2.going_up.reference then teleport_players_around(hidden_dimension.level_2.going_up.reference, hidden_dimension.level_1.going_down.reference) end end local function on_entity_cloned(event) local hidden_dimension = HDT.get('hidden_dimension') for k, v in pairs(hidden_dimension.main_surface.entities) do if event.source == v then event.destination.destroy() end end hidden_dimension.main_surface.reference.destroy() end local function on_player_joined_game(event) if event.player_index == 1 then create_underground_surfaces() create_main_surface(true) end end local function on_tick(e) if e.tick % 5 then logistic_update() if e.tick % 30 then create_main_surface() through_teleporter_update() end end end local function on_init() local hidden_dimension = HDT.get('hidden_dimension') hidden_dimension.level_1 = deepcopy(levels_table) hidden_dimension.level_2 = deepcopy(levels_table) hidden_dimension.main_surface = deepcopy(transport_table) end --- If a different surface is wanted then this should be called -- with: -- local HD = require 'modules.hidden_dimension.main' -- HD.init({hd_surface = some_surface}) function Public.init(args) local hidden_dimension = HDT.get('hidden_dimension') if args then hidden_dimension.position = args.position or {x = 0, y = 3} hidden_dimension.hd_surface = args.hd_surface or 'nauvis' else hidden_dimension.hd_surface = 'nauvis' hidden_dimension.position = {x = 0, y = 3} end end function Public.create_chests(surface, level, build_type) surface.transport_type = build_type local direction1 local direction2 local rotation1 local rotation2 if build_type == 'in_and_out' then direction1 = defines.direction.south direction2 = defines.direction.north rotation1 = 'input' rotation2 = 'output' elseif build_type == 'out_and_in' then direction1 = defines.direction.north direction2 = defines.direction.south rotation1 = 'output' rotation2 = 'input' elseif build_type == 'in_and_in' then direction1 = defines.direction.south direction2 = defines.direction.south rotation1 = 'input' rotation2 = 'input' else direction1 = defines.direction.north direction2 = defines.direction.north rotation1 = 'output' rotation2 = 'output' end local logistic_building = surface.reference local energy = 'electric-energy-interface' local loader local chest = 'blue-chest' if level == 1 then loader = 'loader' elseif level == 2 then loader = 'fast-loader' else loader = 'express-loader' end local function add_container(name, pos, direction, type) local container_entity container_entity = logistic_building.surface.find_entity(name, {logistic_building.position.x + pos.x, logistic_building.position.y + pos.y}) if container_entity == nil then local pos2 = {logistic_building.position.x + pos.x, logistic_building.position.y + pos.y} if name == 'loader' or name == 'fast-loader' or name == 'express-loader' then container_entity = logistic_building.surface.create_entity { name = name, position = pos2, force = game.forces.player, type = type } container_entity.direction = direction elseif name == 'pipe-to-ground' then container_entity = logistic_building.surface.create_entity {name = name, position = pos2, force = game.forces.player} container_entity.direction = direction elseif name == energy then container_entity = logistic_building.surface.create_entity {name = name, position = pos2, force = game.forces.player} container_entity.minable = false container_entity.destructible = false container_entity.operable = false container_entity.power_production = 0 container_entity.electric_buffer_size = 10000000 else container_entity = logistic_building.surface.create_entity {name = name, position = pos2, force = game.forces.player} end end container_entity.minable = false container_entity.destructible = false return container_entity end surface.entities.loader_1 = add_container(loader, {x = -2, y = 0}, direction1, rotation1) surface.entities.loader_2 = add_container(loader, {x = 1, y = 0}, direction2, rotation2) surface.entities.chest_1 = add_container(chest, {x = -2, y = 1}) surface.entities.chest_2 = add_container(chest, {x = 1, y = 1}) surface.entities.pipe_1 = add_container('pipe-to-ground', {x = -3, y = 1}, defines.direction.west) surface.entities.pipe_2 = add_container('pipe-to-ground', {x = 2, y = 1}, defines.direction.east) if level > 1 then surface.entities.pipe_3 = add_container('pipe-to-ground', {x = -3, y = 0}, defines.direction.west) surface.entities.pipe_4 = add_container('pipe-to-ground', {x = 2, y = 0}, defines.direction.east) if level > 2 then local hidden_dimension = HDT.get('hidden_dimension') hidden_dimension.energy[#hidden_dimension.energy + 1] = add_container(energy, {x = 0, y = 0}) surface.entities.pipe_5 = add_container('pipe-to-ground', {x = -3, y = -1}, defines.direction.west) surface.entities.pipe_6 = add_container('pipe-to-ground', {x = 2, y = -1}, defines.direction.east) end end end function Public.upgrade_transport_buildings(level) local function upgrade(transport, building_type) local function copy_chest_content(content, chest) for k, v in pairs(content) do chest.insert({name = k, count = v}) end end if transport.reference == nil or transport.reference.valid ~= true then return end if transport.entities.chest_1 == nil or transport.entities.chest_1.valid ~= true then return end local chest_1_inventory = transport.entities.chest_1.get_inventory(defines.inventory.chest).get_contents() local chest_2_inventory = transport.entities.chest_2.get_inventory(defines.inventory.chest).get_contents() local pos = transport.reference.position local logistic_bb = {{pos.x - 5, pos.y - 5}, {pos.x + 5, pos.y + 5}} local surface = transport.reference.surface clear_surroundings(transport.reference.surface, logistic_bb) transport.reference = surface.create_entity {name = building_type, position = pos, force = game.forces.enemy} transport.reference.minable = false transport.reference.destructible = false transport.reference.operable = false transport.reference.get_inventory(defines.inventory.fuel).insert({name = 'coal', count = 100}) Public.create_chests(transport, level, transport.transport_type) copy_chest_content(chest_1_inventory, transport.entities.chest_1) copy_chest_content(chest_2_inventory, transport.entities.chest_2) end local hidden_dimension = HDT.get('hidden_dimension') upgrade(hidden_dimension.level_1.going_up, 'car') upgrade(hidden_dimension.level_1.going_down, 'car') upgrade(hidden_dimension.level_2.going_up, 'car') upgrade(hidden_dimension.main_surface, 'car') end function Public.create_chests_logistics(level) local hidden_dimension = HDT.get('hidden_dimension') Public.create_chests(hidden_dimension.main_surface, level, 'in_and_out') Public.create_chests(hidden_dimension.level_1.going_up, level, 'out_and_in') Public.create_chests(hidden_dimension.level_1.going_down, level, 'in_and_out') Public.create_chests(hidden_dimension.level_2.going_up, level, 'out_and_in') end if Public.enable_auto_init then Public.init() end Event.on_init(on_init) Event.add(defines.events.on_player_joined_game, on_player_joined_game) Event.add(defines.events.on_tick, on_tick) Event.add(defines.events.on_research_finished, on_research_finished) Event.add(defines.events.on_entity_cloned, on_entity_cloned) Event.add(Public.events.reset_game, reset_surface) Event.add(Public.events.init_surfaces, create_underground_surfaces) return Public