1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2025-01-08 00:39:30 +02:00
ComfyFactorio/maps/scrap_towny_ffa/spawn.lua
2022-10-04 17:51:22 +02:00

206 lines
7.6 KiB
Lua

local Public = {}
local table_size = table.size
local table_insert = table.insert
local math_random = math.random
local math_rad = math.rad
local math_sin = math.sin
local math_cos = math.cos
local math_floor = math.floor
local ScenarioTable = require 'maps.scrap_towny_ffa.table'
local Enemy = require 'maps.scrap_towny_ffa.enemy'
local Building = require 'maps.scrap_towny_ffa.building'
-- don't spawn if town is within this range
local spawn_point_town_buffer = 256
-- clear enemies within this distance from spawn
local spawn_point_safety = 16
-- incremental spawn distance from existing town
-- this is how much each attempt is incremented by for checking a pollution free area
local spawn_point_incremental_distance = 16
local function force_load(position, surface, radius)
--log("is_chunk_generated = " .. tostring(surface.is_chunk_generated(position)))
surface.request_to_generate_chunks(position, radius)
--log("force load position = {" .. position.x .. "," .. position.y .. "}")
surface.force_generate_chunk_requests()
end
-- gets an area (might not be even amount)
local function get_area(position, w, h)
local x1 = math_floor(w / 2)
local x2 = w - x1
local y1 = math_floor(h / 2)
local y2 = h - y1
return {{position.x - x1, position.y - y1}, {position.x + x2, position.y + y2}}
end
local function clear_spawn(position, surface, w, h)
--log("clear_spawn {" .. position.x .. "," .. position.y .. "}")
local area = get_area(position, w, h)
for _, e in pairs(surface.find_entities_filtered({area = area})) do
if e.type ~= 'character' then
e.destroy()
end
end
end
-- does the position have any pollution
local function has_pollution(position, surface)
local result = surface.get_pollution(position) > 0.0
--log("has_pollution = " .. tostring(result))
return result
end
-- is the position already used
local function in_use(position)
local this = ScenarioTable.get_table()
local result = false
for _, v in pairs(this.spawn_point) do
if v == position then
result = true
end
end
--log("in_use = " .. tostring(result))
return result
end
-- is the position empty
local function is_empty(position, surface)
local chunk_position = {}
chunk_position.x = math_floor(position.x / 32)
chunk_position.y = math_floor(position.y / 32)
if not surface.is_chunk_generated(chunk_position) then
-- force load the chunk
surface.request_to_generate_chunks(position, 0)
surface.force_generate_chunk_requests()
end
local entity_radius = 3
local tile_radius = 2
local entities = surface.find_entities_filtered({position = position, radius = entity_radius})
--log("entities = " .. #entities)
if #entities > 0 then
return false
end
local tiles = surface.count_tiles_filtered({position = position, radius = tile_radius, collision_mask = 'water-tile'})
--log("water-tiles = " .. tiles)
if tiles > 0 then
return false
end
local result = surface.can_place_entity({name = 'character', position = position})
--log("is_empty = " .. tostring(result))
return result
end
-- finds a valid spawn point that is not near a town and not in a polluted area
local function find_valid_spawn_point(force_name, surface)
local this = ScenarioTable.get_table()
-- check center of map first if valid
local position = {x = 0, y = 0}
--log("testing {" .. position.x .. "," .. position.y .. "}")
force_load(position, surface, 1)
-- is the point near any buildings
if in_use(position) == false then
if Building.near_another_town(force_name, position, surface, spawn_point_town_buffer) == false then
-- force load the position
if is_empty(position, surface) == true then
--log("found valid spawn point at {" .. position.x .. "," .. position.y .. "}")
return position
end
end
end
-- otherwise find a nearby town
local keyset = {}
for town_name, _ in pairs(this.town_centers) do
table_insert(keyset, town_name)
end
local count = table_size(keyset)
if count > 0 then
local town_name = keyset[math_random(1, count)]
local town_center = this.town_centers[town_name]
if town_center ~= nil then
position = town_center.market.position
end
--log("town center is {" .. position.x .. "," .. position.y .. "}")
end
-- and start checking around it for a suitable spawn position
local tries = 0
local radius = spawn_point_town_buffer
local angle
while (tries < 100) do
-- 8 attempts each position
for _ = 1, 8 do
-- position on the circle radius
angle = math_random(0, 360)
local t = math_rad(angle)
local x = math_floor(position.x + math_cos(t) * radius)
local y = math_floor(position.y + math_sin(t) * radius)
local target = {x = x, y = y}
--log("testing {" .. target.x .. "," .. target.y .. "}")
force_load(position, surface, 1)
if in_use(target) == false then
if has_pollution(target, surface) == false then
if Building.near_another_town(force_name, target, surface, spawn_point_town_buffer) == false then
if is_empty(target, surface) == true then
--log("found valid spawn point at {" .. target.x .. "," .. target.y .. "}")
position = target
return position
end
end
end
end
end
-- near a town, increment the radius and select another angle
radius = radius + math_random(1, spawn_point_incremental_distance)
tries = tries + 1
end
return {x = 0, y = 0}
end
function Public.get_new_spawn_point(player, surface)
local this = ScenarioTable.get_table()
-- get a new spawn point
local position = {0, 0}
if player ~= nil then
local force = player.force
if force ~= nil then
local force_name = force.name
position = find_valid_spawn_point(force_name, surface)
end
end
-- should never be invalid or blocked
this.spawn_point[player.index] = position
--log("player " .. player.name .. " assigned new spawn point at {" .. position.x .. "," .. position.y .. "}")
return position
end
-- gets a new or existing spawn point for the player
function Public.get_spawn_point(player, surface)
local this = ScenarioTable.get_table()
local position = this.spawn_point[player.index]
-- if there is a spawn point and less than three strikes
if position ~= nil and this.strikes[player.name] < 3 then
-- check that the spawn point is not blocked
if surface.can_place_entity({name = 'character', position = position}) then
--log("player " .. player.name .. "using existing spawn point at {" .. position.x .. "," .. position.y .. "}")
return position
else
position = surface.find_non_colliding_position('character', position, 0, 0.25)
return position
end
end
-- otherwise get a new spawn point
return Public.get_new_spawn_point(player, surface)
end
function Public.clear_spawn_point(position, surface)
Enemy.clear_worms(position, surface, spawn_point_safety) -- behemoth worms can attack from a range of 48, clear first time only
Enemy.clear_enemies(position, surface, spawn_point_safety) -- behemoth worms can attack from a range of 48
clear_spawn(position, surface, 7, 9)
end
return Public