mirror of
https://github.com/ComfyFactory/ComfyFactorio.git
synced 2025-01-08 00:39:30 +02:00
206 lines
7.6 KiB
Lua
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
|