local Public = {} local Table = require 'modules.scrap_towny_ffa.table' local town_radius = 27 local connection_radius = 7 local neutral_whitelist = { ["wooden-chest"] = true, ["iron-chest"] = true, ["steel-chest"] = true, ["raw-fish"] = true, } local entity_type_whitelist = { ["accumulator"] = true, ["ammo-turret"] = true, ["arithmetic-combinator"] = true, ["artillery-turret"] = true, ["assembling-machine"] = true, ["boiler"] = true, ["constant-combinator"] = true, ["container"] = true, ["curved-rail"] = true, ["decider-combinator"] = true, ["electric-pole"] = true, ["electric-turret"] = true, ["fluid-turret"] = true, ["furnace"] = true, ["gate"] = true, ["generator"] = true, ["heat-interface"] = true, ["heat-pipe"] = true, ["infinity-container"] = true, ["infinity-pipe"] = true, ["inserter"] = true, ["lab"] = true, ["lamp"] = true, ["land-mine"] = true, ["loader"] = true, ["logistic-container"] = true, ["market"] = true, ["mining-drill"] = true, ["offshore-pump"] = true, ["pipe"] = true, ["pipe-to-ground"] = true, ["programmable-speaker"] = true, ["pump"] = true, ["radar"] = true, ["rail-chain-signal"] = true, ["rail-signal"] = true, ["reactor"] = true, ["roboport"] = true, ["rocket-silo"] = true, ["solar-panel"] = true, ["splitter"] = true, ["storage-tank"] = true, ["straight-rail"] = true, ["train-stop"] = true, ["transport-belt"] = true, ["underground-belt"] = true, ["wall"] = true, } local function isolated(surface, force, position) local position_x = position.x local position_y = position.y local area = { { position_x - connection_radius, position_y - connection_radius }, { position_x + connection_radius, position_y + connection_radius } } local count = 0 for _, e in pairs(surface.find_entities_filtered({ area = area, force = force.name })) do if entity_type_whitelist[e.type] then count = count + 1 if count > 1 then return false end -- are there more than one team entities in the area? end end return true end local function refund_item(event, item_name) if item_name == "blueprint" then return end if event.player_index then game.players[event.player_index].insert({ name = item_name, count = 1 }) return end if event.robot then local inventory = event.robot.get_inventory(defines.inventory.robot_cargo) inventory.insert({ name = item_name, count = 1 }) return end end local function error_floaty(surface, position, msg) surface.create_entity({ name = "flying-text", position = position, text = msg, color = { r = 0.77, g = 0.0, b = 0.0 } }) end local function in_range(pos1, pos2, radius) if pos1 == nil then return false end if pos2 == nil then return false end if radius < 1 then return true end local dx = pos1.x - pos2.x local dy = pos1.y - pos2.y if dx ^ 2 + dy ^ 2 < radius ^ 2 then return true end return false end -- is the position near a town? function Public.near_town(position, surface, radius) local ffatable = Table.get_table() for _, town_center in pairs(ffatable.town_centers) do if town_center ~= nil then local market = town_center.market if in_range(position, market.position, radius) and market.surface == surface then return true end end end return false end local function in_town(force, position) local ffatable = Table.get_table() local town_center = ffatable.town_centers[force.name] if town_center ~= nil then local center = town_center.market.position if position.x >= center.x - town_radius and position.x <= center.x + town_radius then if position.y >= center.y - town_radius and position.y <= center.y + town_radius then return true end end end return false end local function prevent_isolation_entity(event, player) local p = player or nil local entity = event.created_entity local position = entity.position if not entity.valid then return end local entity_name = entity.name local item = event.item if item == nil then return end local item_name = item.name local force = entity.force if force == game.forces.player then return end if force == game.forces["rogue"] then return end local surface = entity.surface local error = false if not in_town(force, position) and isolated(surface, force, position) then error = true entity.destroy() if entity_name ~= "entity-ghost" and entity_name ~= "tile-ghost" then refund_item(event, item_name) end --return true end if error == true then if p ~= nil then p.play_sound({ path = "utility/cannot_build", position = p.position, volume_modifier = 0.75 }) end error_floaty(surface, position, "Building is not connected to town!") end end local function prevent_isolation_tile(event, player) local p = player or nil local tile = event.tile if not tile.valid then return end local tile_name = tile.name local surface = game.surfaces[event.surface_index] local tiles = event.tiles local force if event.player_index then force = game.players[event.player_index].force else force = event.robot.force end local error = false local position for _, t in pairs(tiles) do local old_tile = t.old_tile position = t.position if not in_town(force, position) and isolated(surface, force, position) then error = true surface.set_tiles({ { name = old_tile.name, position = position } }, true) if tile_name ~= "tile-ghost" then if tile_name == "stone-path" then tile_name = "stone-brick" end refund_item(event, tile_name) end end end if error == true then if p ~= nil then p.play_sound({ path = "utility/cannot_build", position = p.position, volume_modifier = 0.75 }) end error_floaty(surface, position, "Tile is not connected to town!") end end local function restrictions(event, player) local p = player or nil local entity = event.created_entity if not entity.valid then return end local entity_name = entity.name local surface = entity.surface local position = entity.position local error = false if entity.force == game.forces["player"] or entity.force == game.forces["rogue"] then if Public.near_town(position, surface, 32) then error = true entity.destroy() if entity_name ~= "entity-ghost" then refund_item(event, event.stack.name) end else entity.force = game.forces["neutral"] end return end if error == true then if p ~= nil then p.play_sound({ path = "utility/cannot_build", position = p.position, volume_modifier = 0.75 }) end error_floaty(surface, position, "Can't build near town!") end if not neutral_whitelist[entity.type] then return end entity.force = game.forces["neutral"] end -- called when a player places an item, or a ghost local function on_built_entity(event) local player = game.players[event.player_index] if prevent_isolation_entity(event, player) then return end restrictions(event, player) end local function on_robot_built_entity(event) if prevent_isolation_entity(event) then return end restrictions(event) end -- called when a player places landfill local function on_player_built_tile(event) local player = game.players[event.player_index] prevent_isolation_tile(event, player) end local function on_robot_built_tile(event) prevent_isolation_tile(event) end local Event = require 'utils.event' Event.add(defines.events.on_built_entity, on_built_entity) Event.add(defines.events.on_player_built_tile, on_player_built_tile) Event.add(defines.events.on_robot_built_entity, on_robot_built_entity) Event.add(defines.events.on_robot_built_tile, on_robot_built_tile) return Public