mirror of
https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn.git
synced 2025-01-05 22:53:48 +02:00
Saving progress. Copy in old regrowth_map file. Halfway through updating the separate_spawns_guis.
This commit is contained in:
parent
8bd24d9732
commit
bd337e17e9
@ -259,6 +259,8 @@ OCFG = {
|
||||
spawn_config = {
|
||||
|
||||
general = {
|
||||
|
||||
---TODO: Rename land_area_tiles to spawn_circle_size or something more descriptive.
|
||||
-- THIS IS WHAT SETS THE SPAWN CIRCLE SIZE!
|
||||
-- Create a circle of land area for the spawn
|
||||
-- If you make this much bigger than a few chunks, good luck.
|
||||
|
@ -102,6 +102,21 @@ script.on_event(defines.events.on_chunk_generated, function(event)
|
||||
SeparateSpawnsGenerateChunk(event)
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- On Entity Spawned and On Biter Base Built
|
||||
-- This is where I modify biter spawning based on location and other factors.
|
||||
----------------------------------------
|
||||
script.on_event(defines.events.on_entity_spawned, function(event)
|
||||
if (global.ocfg.gameplay.oarc_modified_enemy_spawning) then
|
||||
ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
end
|
||||
end)
|
||||
script.on_event(defines.events.on_biter_base_built, function(event)
|
||||
if (global.ocfg.gameplay.oarc_modified_enemy_spawning) then
|
||||
ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
end
|
||||
end)
|
||||
|
||||
----------------------------------------
|
||||
-- Gui Events
|
||||
----------------------------------------
|
||||
|
@ -35,10 +35,10 @@ function ReadModSettings()
|
||||
log("Copying mod settings to OCFG table...")
|
||||
|
||||
-- Copy in the startup settings from the mod settings.
|
||||
global.ocfg.mod_overlap.enable_main_force = settings.startup["oarc-mod-enable-main-force"].value
|
||||
global.ocfg.mod_overlap.enable_separate_teams = settings.startup["oarc-mod-enable-separate-teams"].value
|
||||
global.ocfg.mod_overlap.enable_spawning_on_other_surfaces = settings.startup["oarc-mod-enable-spawning-on-other-surfaces"].value
|
||||
global.ocfg.mod_overlap.enable_buddy_spawn = settings.startup["oarc-mod-enable-buddy-spawn"].value
|
||||
global.ocfg.mod_overlap.enable_main_force = settings.startup["oarc-mod-enable-main-force"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.enable_separate_teams = settings.startup["oarc-mod-enable-separate-teams"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.enable_spawning_on_other_surfaces = settings.startup["oarc-mod-enable-spawning-on-other-surfaces"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.enable_buddy_spawn = settings.startup["oarc-mod-enable-buddy-spawn"].value --[[@as boolean]]
|
||||
|
||||
-- TODO: Vanilla spawn point are not implemented yet.
|
||||
-- settings.startup["oarc-mod-enable-vanilla-spawn-points"].value
|
||||
@ -46,21 +46,21 @@ function ReadModSettings()
|
||||
-- settings.startup["oarc-mod-vanilla-spawn-point-spacing"].value
|
||||
|
||||
-- Copy in the global settings from the mod settings.
|
||||
global.ocfg.mod_overlap.enable_regrowth = settings.global["oarc-mod-enable-regrowth"].value
|
||||
global.ocfg.mod_overlap.enable_world_eater = settings.global["oarc-mod-enable-world-eater"].value
|
||||
global.ocfg.mod_overlap.enable_offline_protection = settings.global["oarc-mod-enable-offline-protection"].value
|
||||
global.ocfg.mod_overlap.enable_shared_team_vision = settings.global["oarc-mod-enable-shared-team-vision"].value
|
||||
global.ocfg.mod_overlap.enable_shared_team_chat = settings.global["oarc-mod-enable-shared-team-chat"].value
|
||||
global.ocfg.mod_overlap.enable_shared_spawns = settings.global["oarc-mod-enable-shared-spawns"].value
|
||||
global.ocfg.mod_overlap.number_of_players_per_shared_spawn = settings.global["oarc-mod-number-of-players-per-shared-spawn"].value
|
||||
global.ocfg.mod_overlap.enable_abandoned_base_cleanup = settings.global["oarc-mod-enable-abandoned-base-cleanup"].value
|
||||
global.ocfg.mod_overlap.enable_friendly_fire = settings.global["oarc-mod-enable-friendly-fire"].value
|
||||
global.ocfg.mod_overlap.enable_allow_moats_around_spawns = settings.global["oarc-mod-enable-allow-moats-around-spawns"].value
|
||||
global.ocfg.mod_overlap.enable_regrowth = settings.global["oarc-mod-enable-regrowth"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.enable_world_eater = settings.global["oarc-mod-enable-world-eater"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.enable_offline_protection = settings.global["oarc-mod-enable-offline-protection"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.enable_shared_team_vision = settings.global["oarc-mod-enable-shared-team-vision"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.enable_shared_team_chat = settings.global["oarc-mod-enable-shared-team-chat"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.enable_shared_spawns = settings.global["oarc-mod-enable-shared-spawns"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.number_of_players_per_shared_spawn = settings.global["oarc-mod-number-of-players-per-shared-spawn"].value --[[@as integer]]
|
||||
global.ocfg.mod_overlap.enable_abandoned_base_cleanup = settings.global["oarc-mod-enable-abandoned-base-cleanup"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.enable_friendly_fire = settings.global["oarc-mod-enable-friendly-fire"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.enable_allow_moats_around_spawns = settings.global["oarc-mod-enable-allow-moats-around-spawns"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.enable_moat_bridging = settings.global["oarc-mod-enable-force-bridges-next-to-moats"].value --[[@as boolean]]
|
||||
global.ocfg.mod_overlap.minimum_distance_to_existing_chunks = settings.global["oarc-mod-minimum-distance-to-existing-chunks"].value
|
||||
global.ocfg.mod_overlap.near_spawn_min_distance = settings.global["oarc-mod-near-spawn-min-distance"].value
|
||||
global.ocfg.mod_overlap.near_spawn_max_distance = settings.global["oarc-mod-near-spawn-max-distance"].value
|
||||
global.ocfg.mod_overlap.far_spawn_min_distance = settings.global["oarc-mod-far-spawn-min-distance"].value
|
||||
global.ocfg.mod_overlap.far_spawn_max_distance = settings.global["oarc-mod-far-spawn-max-distance"].value
|
||||
global.ocfg.mod_overlap.minimum_distance_to_existing_chunks = settings.global["oarc-mod-minimum-distance-to-existing-chunks"].value --[[@as integer]]
|
||||
global.ocfg.mod_overlap.near_spawn_min_distance = settings.global["oarc-mod-near-spawn-min-distance"].value --[[@as integer]]
|
||||
global.ocfg.mod_overlap.near_spawn_max_distance = settings.global["oarc-mod-near-spawn-max-distance"].value --[[@as integer]]
|
||||
global.ocfg.mod_overlap.far_spawn_min_distance = settings.global["oarc-mod-far-spawn-min-distance"].value --[[@as integer]]
|
||||
global.ocfg.mod_overlap.far_spawn_max_distance = settings.global["oarc-mod-far-spawn-max-distance"].value --[[@as integer]]
|
||||
end
|
||||
|
||||
|
@ -68,15 +68,15 @@ MAX_INT32_NEG = -2147483648
|
||||
---@param color Color
|
||||
---@return nil
|
||||
function RenderPermanentGroundText(surface, position, scale, text, color)
|
||||
rendering.draw_text{text=text,
|
||||
surface=surface,
|
||||
target=position,
|
||||
color=color,
|
||||
scale=scale,
|
||||
--Allowed fonts: default-dialog-button default-game compilatron-message-font default-large default-large-semibold default-large-bold heading-1 compi
|
||||
font="compi",
|
||||
draw_on_ground=true}
|
||||
end
|
||||
rendering.draw_text { text = text,
|
||||
surface = surface,
|
||||
target = position,
|
||||
color = color,
|
||||
scale = scale,
|
||||
--Allowed fonts: default-dialog-button default-game compilatron-message-font default-large default-large-semibold default-large-bold heading-1 compi
|
||||
font = "compi",
|
||||
draw_on_ground = true }
|
||||
end
|
||||
|
||||
---A standardized helper text that fades out over time
|
||||
---@param text string
|
||||
@ -85,14 +85,14 @@ end
|
||||
---@param ttl number
|
||||
---@return nil
|
||||
function TemporaryHelperText(text, surface, position, ttl)
|
||||
local rid = rendering.draw_text{text=text,
|
||||
surface=surface,
|
||||
target=position,
|
||||
color={0.7,0.7,0.7,0.7},
|
||||
scale=1,
|
||||
font="compi",
|
||||
time_to_live=ttl,
|
||||
draw_on_ground=false}
|
||||
local rid = rendering.draw_text { text = text,
|
||||
surface = surface,
|
||||
target = position,
|
||||
color = { 0.7, 0.7, 0.7, 0.7 },
|
||||
scale = 1,
|
||||
font = "compi",
|
||||
time_to_live = ttl,
|
||||
draw_on_ground = false }
|
||||
table.insert(global.oarc_renders_fadeout, rid)
|
||||
end
|
||||
|
||||
@ -116,13 +116,13 @@ end
|
||||
---@return nil
|
||||
function FadeoutRenderOnTick()
|
||||
if (global.oarc_renders_fadeout and (#global.oarc_renders_fadeout > 0)) then
|
||||
for k,rid in pairs(global.oarc_renders_fadeout) do
|
||||
for k, rid in pairs(global.oarc_renders_fadeout) do
|
||||
if (rendering.is_valid(rid)) then
|
||||
local ttl = rendering.get_time_to_live(rid)
|
||||
if ((ttl > 0) and (ttl < 200)) then
|
||||
local color = rendering.get_color(rid)
|
||||
if (color.a > 0.005) then
|
||||
rendering.set_color(rid, {r=color.r, g=color.g, b=color.b, a=color.a-0.005})
|
||||
rendering.set_color(rid, { r = color.r, g = color.g, b = color.b, a = color.a - 0.005 })
|
||||
end
|
||||
end
|
||||
else
|
||||
@ -136,7 +136,7 @@ end
|
||||
---@param msg LocalisedString
|
||||
---@return nil
|
||||
function SendBroadcastMsg(msg)
|
||||
for name,player in pairs(game.connected_players) do
|
||||
for name, player in pairs(game.connected_players) do
|
||||
player.print(msg)
|
||||
end
|
||||
end
|
||||
@ -194,9 +194,9 @@ end
|
||||
---@param T table
|
||||
---@return integer
|
||||
function TableLength(T)
|
||||
local count = 0
|
||||
for _ in pairs(T) do count = count + 1 end
|
||||
return count
|
||||
local count = 0
|
||||
for _ in pairs(T) do count = count + 1 end
|
||||
return count
|
||||
end
|
||||
|
||||
-- Fisher-Yares shuffle
|
||||
@ -280,18 +280,18 @@ end
|
||||
---@param surface LuaSurface
|
||||
function ChartArea(force, position, chunkDist, surface)
|
||||
force.chart(surface,
|
||||
{{position.x-(CHUNK_SIZE*chunkDist),
|
||||
position.y-(CHUNK_SIZE*chunkDist)},
|
||||
{position.x+(CHUNK_SIZE*chunkDist),
|
||||
position.y+(CHUNK_SIZE*chunkDist)}})
|
||||
{ { position.x - (CHUNK_SIZE * chunkDist),
|
||||
position.y - (CHUNK_SIZE * chunkDist) },
|
||||
{ position.x + (CHUNK_SIZE * chunkDist),
|
||||
position.y + (CHUNK_SIZE * chunkDist) } })
|
||||
end
|
||||
|
||||
---Gives the player the respawn items if there are any
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function GivePlayerRespawnItems(player)
|
||||
for name,count in pairs(global.ocfg.starting_items.player_respawn_start_items) do
|
||||
player.insert({name=name, count=count})
|
||||
for name, count in pairs(global.ocfg.starting_items.player_respawn_start_items) do
|
||||
player.insert({ name = name, count = count })
|
||||
end
|
||||
end
|
||||
|
||||
@ -301,8 +301,8 @@ end
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function GivePlayerStarterItems(player)
|
||||
for name,count in pairs(global.ocfg.starting_items.player_spawn_start_items) do
|
||||
player.insert({name=name, count=count})
|
||||
for name, count in pairs(global.ocfg.starting_items.player_spawn_start_items) do
|
||||
player.insert({ name = name, count = count })
|
||||
end
|
||||
end
|
||||
|
||||
@ -400,31 +400,33 @@ function CheckIfInArea(point, area)
|
||||
return false
|
||||
end
|
||||
|
||||
-- -- Set all forces to ceasefire
|
||||
-- function SetCeaseFireBetweenAllForces()
|
||||
-- for name,team in pairs(game.forces) do
|
||||
-- if name ~= "neutral" and name ~= "enemy" and name ~= global.ocore.abandoned_force then
|
||||
-- for x,y in pairs(game.forces) do
|
||||
-- if x ~= "neutral" and x ~= "enemy" and name ~= global.ocore.abandoned_force then
|
||||
-- team.set_cease_fire(x,true)
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
---Set all forces to ceasefire
|
||||
---@return nil
|
||||
function SetCeaseFireBetweenAllForces()
|
||||
for name, team in pairs(game.forces) do
|
||||
if name ~= "neutral" and name ~= "enemy" and name ~= global.ocore.abandoned_force then
|
||||
for x, y in pairs(game.forces) do
|
||||
if x ~= "neutral" and x ~= "enemy" and name ~= global.ocore.abandoned_force then
|
||||
team.set_cease_fire(x, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- -- Set all forces to friendly
|
||||
-- function SetFriendlyBetweenAllForces()
|
||||
-- for name,team in pairs(game.forces) do
|
||||
-- if name ~= "neutral" and name ~= "enemy" and name ~= global.ocore.abandoned_force then
|
||||
-- for x,y in pairs(game.forces) do
|
||||
-- if x ~= "neutral" and x ~= "enemy" and name ~= global.ocore.abandoned_force then
|
||||
-- team.set_friend(x,true)
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
---Set all forces to friendly
|
||||
---@return nil
|
||||
function SetFriendlyBetweenAllForces()
|
||||
for name, team in pairs(game.forces) do
|
||||
if name ~= "neutral" and name ~= "enemy" and name ~= global.ocore.abandoned_force then
|
||||
for x, y in pairs(game.forces) do
|
||||
if x ~= "neutral" and x ~= "enemy" and name ~= global.ocore.abandoned_force then
|
||||
team.set_friend(x, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- -- For each other player force, share a chat msg.
|
||||
-- function ShareChatBetweenForces(player, msg)
|
||||
@ -484,134 +486,150 @@ end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- -- Get a random 1 or -1
|
||||
-- function RandomNegPos()
|
||||
-- if (math.random(0,1) == 1) then
|
||||
-- return 1
|
||||
-- else
|
||||
-- return -1
|
||||
-- end
|
||||
-- end
|
||||
---Get a random 1 or -1
|
||||
---@return number
|
||||
function RandomNegPos()
|
||||
if (math.random(0,1) == 1) then
|
||||
return 1
|
||||
else
|
||||
return -1
|
||||
end
|
||||
end
|
||||
|
||||
-- -- Create a random direction vector to look in
|
||||
-- function GetRandomVector()
|
||||
-- local randVec = {x=0,y=0}
|
||||
-- while ((randVec.x == 0) and (randVec.y == 0)) do
|
||||
-- randVec.x = math.random(-3,3)
|
||||
-- randVec.y = math.random(-3,3)
|
||||
-- end
|
||||
-- log("direction: x=" .. randVec.x .. ", y=" .. randVec.y)
|
||||
-- return randVec
|
||||
-- end
|
||||
---Create a random direction vector to look in
|
||||
---@return MapPosition
|
||||
function GetRandomVector()
|
||||
local randVec = {x=0,y=0}
|
||||
while ((randVec.x == 0) and (randVec.y == 0)) do
|
||||
randVec.x = math.random(-3,3)
|
||||
randVec.y = math.random(-3,3)
|
||||
end
|
||||
log("direction: x=" .. randVec.x .. ", y=" .. randVec.y)
|
||||
return randVec
|
||||
end
|
||||
|
||||
-- -- Check for ungenerated chunks around a specific chunk
|
||||
-- -- +/- chunkDist in x and y directions
|
||||
-- function IsChunkAreaUngenerated(chunkPos, chunkDist, surface)
|
||||
-- for x=-chunkDist, chunkDist do
|
||||
-- for y=-chunkDist, chunkDist do
|
||||
-- local checkPos = {x=chunkPos.x+x,
|
||||
-- y=chunkPos.y+y}
|
||||
-- if (surface.is_chunk_generated(checkPos)) then
|
||||
-- return false
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- return true
|
||||
-- end
|
||||
---Check for ungenerated chunks around a specific chunk +/- chunkDist in x and y directions
|
||||
---@param chunkPos MapPosition
|
||||
---@param chunkDist integer
|
||||
---@param surface LuaSurface
|
||||
---@return boolean
|
||||
function IsChunkAreaUngenerated(chunkPos, chunkDist, surface)
|
||||
for x=-chunkDist, chunkDist do
|
||||
for y=-chunkDist, chunkDist do
|
||||
local checkPos = {x=chunkPos.x+x,
|
||||
y=chunkPos.y+y}
|
||||
if (surface.is_chunk_generated(checkPos)) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Clear out enemies around an area with a certain distance
|
||||
---@param pos MapPosition
|
||||
---@param safeDist number
|
||||
---@param surface LuaSurface
|
||||
function ClearNearbyEnemies(pos, safeDist, surface)
|
||||
local safeArea = {left_top=
|
||||
{x=pos.x-safeDist,
|
||||
y=pos.y-safeDist},
|
||||
right_bottom=
|
||||
{x=pos.x+safeDist,
|
||||
y=pos.y+safeDist}}
|
||||
local safeArea = {
|
||||
left_top =
|
||||
{
|
||||
x = pos.x - safeDist,
|
||||
y = pos.y - safeDist
|
||||
},
|
||||
right_bottom =
|
||||
{
|
||||
x = pos.x + safeDist,
|
||||
y = pos.y + safeDist
|
||||
}
|
||||
}
|
||||
|
||||
for _, entity in pairs(surface.find_entities_filtered{area = safeArea, force = "enemy"}) do
|
||||
for _, entity in pairs(surface.find_entities_filtered { area = safeArea, force = "enemy" }) do
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
|
||||
-- -- Function to find coordinates of ungenerated map area in a given direction
|
||||
-- -- starting from the center of the map
|
||||
-- function FindMapEdge(directionVec, surface)
|
||||
-- local position = {x=0,y=0}
|
||||
-- local chunkPos = {x=0,y=0}
|
||||
---Function to find coordinates of ungenerated map area in a given direction starting from the center of the map
|
||||
---@param directionVec MapPosition
|
||||
---@param surface LuaSurface
|
||||
---@return MapPosition
|
||||
function FindMapEdge(directionVec, surface)
|
||||
local position = {x=0,y=0}
|
||||
local chunkPos = {x=0,y=0}
|
||||
|
||||
-- -- Keep checking chunks in the direction of the vector
|
||||
-- while(true) do
|
||||
-- Keep checking chunks in the direction of the vector
|
||||
while(true) do
|
||||
|
||||
-- -- Set some absolute limits.
|
||||
-- if ((math.abs(chunkPos.x) > 1000) or (math.abs(chunkPos.y) > 1000)) then
|
||||
-- break
|
||||
-- Set some absolute limits.
|
||||
if ((math.abs(chunkPos.x) > 1000) or (math.abs(chunkPos.y) > 1000)) then
|
||||
break
|
||||
|
||||
-- -- If chunk is already generated, keep looking
|
||||
-- elseif (surface.is_chunk_generated(chunkPos)) then
|
||||
-- chunkPos.x = chunkPos.x + directionVec.x
|
||||
-- chunkPos.y = chunkPos.y + directionVec.y
|
||||
-- If chunk is already generated, keep looking
|
||||
elseif (surface.is_chunk_generated(chunkPos)) then
|
||||
chunkPos.x = chunkPos.x + directionVec.x
|
||||
chunkPos.y = chunkPos.y + directionVec.y
|
||||
|
||||
-- -- Found a possible ungenerated area
|
||||
-- else
|
||||
-- Found a possible ungenerated area
|
||||
else
|
||||
|
||||
-- chunkPos.x = chunkPos.x + directionVec.x
|
||||
-- chunkPos.y = chunkPos.y + directionVec.y
|
||||
chunkPos.x = chunkPos.x + directionVec.x
|
||||
chunkPos.y = chunkPos.y + directionVec.y
|
||||
|
||||
-- -- Check there are no generated chunks in a 10x10 area.
|
||||
-- if IsChunkAreaUngenerated(chunkPos, 10, surface) then
|
||||
-- position.x = (chunkPos.x*CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
-- position.y = (chunkPos.y*CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
-- break
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- Check there are no generated chunks in a 10x10 area.
|
||||
if IsChunkAreaUngenerated(chunkPos, 10, surface) then
|
||||
position.x = (chunkPos.x*CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
position.y = (chunkPos.y*CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- -- log("spawn: x=" .. position.x .. ", y=" .. position.y)
|
||||
-- return position
|
||||
-- end
|
||||
-- log("spawn: x=" .. position.x .. ", y=" .. position.y)
|
||||
return position
|
||||
end
|
||||
|
||||
-- -- Find random coordinates within a given distance away
|
||||
-- -- maxTries is the recursion limit basically.
|
||||
-- function FindUngeneratedCoordinates(minDistChunks, maxDistChunks, surface)
|
||||
-- local position = {x=0,y=0}
|
||||
-- local chunkPos = {x=0,y=0}
|
||||
---Find random coordinates within a given distance away maxTries is the recursion limit basically.
|
||||
---@param minDistChunks integer
|
||||
---@param maxDistChunks integer
|
||||
---@param surface LuaSurface
|
||||
---@return MapPosition
|
||||
function FindUngeneratedCoordinates(minDistChunks, maxDistChunks, surface)
|
||||
local position = {x=0,y=0}
|
||||
local chunkPos = {x=0,y=0}
|
||||
|
||||
-- local maxTries = 100
|
||||
-- local tryCounter = 0
|
||||
local maxTries = 100
|
||||
local tryCounter = 0
|
||||
|
||||
-- local minDistSqr = minDistChunks^2
|
||||
-- local maxDistSqr = maxDistChunks^2
|
||||
local minDistSqr = minDistChunks^2
|
||||
local maxDistSqr = maxDistChunks^2
|
||||
|
||||
-- while(true) do
|
||||
-- chunkPos.x = math.random(0,maxDistChunks) * RandomNegPos()
|
||||
-- chunkPos.y = math.random(0,maxDistChunks) * RandomNegPos()
|
||||
while(true) do
|
||||
chunkPos.x = math.random(0,maxDistChunks) * RandomNegPos()
|
||||
chunkPos.y = math.random(0,maxDistChunks) * RandomNegPos()
|
||||
|
||||
-- local distSqrd = chunkPos.x^2 + chunkPos.y^2
|
||||
local distSqrd = chunkPos.x^2 + chunkPos.y^2
|
||||
|
||||
-- -- Enforce a max number of tries
|
||||
-- tryCounter = tryCounter + 1
|
||||
-- if (tryCounter > maxTries) then
|
||||
-- log("FindUngeneratedCoordinates - Max Tries Hit!")
|
||||
-- break
|
||||
-- Enforce a max number of tries
|
||||
tryCounter = tryCounter + 1
|
||||
if (tryCounter > maxTries) then
|
||||
log("FindUngeneratedCoordinates - Max Tries Hit!")
|
||||
break
|
||||
|
||||
-- -- Check that the distance is within the min,max specified
|
||||
-- elseif ((distSqrd < minDistSqr) or (distSqrd > maxDistSqr)) then
|
||||
-- -- Keep searching!
|
||||
-- Check that the distance is within the min,max specified
|
||||
elseif ((distSqrd < minDistSqr) or (distSqrd > maxDistSqr)) then
|
||||
-- Keep searching!
|
||||
|
||||
-- -- Check there are no generated chunks in a 10x10 area.
|
||||
-- elseif IsChunkAreaUngenerated(chunkPos, CHECK_SPAWN_UNGENERATED_CHUNKS_RADIUS, surface) then
|
||||
-- position.x = (chunkPos.x*CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
-- position.y = (chunkPos.y*CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
-- break -- SUCCESS
|
||||
-- end
|
||||
-- end
|
||||
-- Check there are no generated chunks in a 10x10 area.
|
||||
elseif IsChunkAreaUngenerated(chunkPos, global.ocfg.mod_overlap.minimum_distance_to_existing_chunks, surface) then
|
||||
position.x = (chunkPos.x*CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
position.y = (chunkPos.y*CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
break -- SUCCESS
|
||||
end
|
||||
end
|
||||
|
||||
-- log("spawn: x=" .. position.x .. ", y=" .. position.y)
|
||||
-- return position
|
||||
-- end
|
||||
log("spawn: x=" .. position.x .. ", y=" .. position.y)
|
||||
return position
|
||||
end
|
||||
|
||||
-- -- General purpose function for removing a particular recipe
|
||||
-- function RemoveRecipe(force, recipeName)
|
||||
@ -650,13 +668,18 @@ end
|
||||
---@param dist number
|
||||
---@return BoundingBox
|
||||
function GetAreaAroundPos(pos, dist)
|
||||
|
||||
return {left_top=
|
||||
{x=pos.x-dist,
|
||||
y=pos.y-dist},
|
||||
right_bottom=
|
||||
{x=pos.x+dist,
|
||||
y=pos.y+dist}}
|
||||
return {
|
||||
left_top =
|
||||
{
|
||||
x = pos.x - dist,
|
||||
y = pos.y - dist
|
||||
},
|
||||
right_bottom =
|
||||
{
|
||||
x = pos.x + dist,
|
||||
y = pos.y + dist
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
-- -- Gets chunk position of a tile.
|
||||
@ -696,9 +719,9 @@ end
|
||||
---@param dist number
|
||||
---@return nil
|
||||
function RemoveInCircle(surface, area, type, pos, dist)
|
||||
for key, entity in pairs(surface.find_entities_filtered{area=area, type= type}) do
|
||||
for key, entity in pairs(surface.find_entities_filtered { area = area, type = type }) do
|
||||
if entity.valid and entity and entity.position then
|
||||
if ((pos.x - entity.position.x)^2 + (pos.y - entity.position.y)^2 < dist^2) then
|
||||
if ((pos.x - entity.position.x) ^ 2 + (pos.y - entity.position.y) ^ 2 < dist ^ 2) then
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
@ -809,7 +832,7 @@ end
|
||||
-- table.insert(tiles, {name = "hazard-concrete-left", position = {pos.x+2, pos.y+1}})
|
||||
-- table.insert(tiles, {name = "hazard-concrete-left", position = {pos.x+3, pos.y+1}})
|
||||
-- end
|
||||
|
||||
|
||||
-- surface.set_tiles(tiles, true)
|
||||
-- end
|
||||
|
||||
@ -852,7 +875,7 @@ end
|
||||
---@param area BoundingBox
|
||||
---@return nil
|
||||
function RemoveAliensInArea(surface, area)
|
||||
for _, entity in pairs(surface.find_entities_filtered{area = area, force = "enemy"}) do
|
||||
for _, entity in pairs(surface.find_entities_filtered { area = area, force = "enemy" }) do
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
@ -863,8 +886,8 @@ end
|
||||
---@param reductionFactor integer Reduction factor divides the enemy spawns by that number. 2 = half, 3 = third, etc...
|
||||
---@return nil
|
||||
function ReduceAliensInArea(surface, area, reductionFactor)
|
||||
for _, entity in pairs(surface.find_entities_filtered{area = area, force = "enemy"}) do
|
||||
if (math.random(0,reductionFactor) > 0) then
|
||||
for _, entity in pairs(surface.find_entities_filtered { area = area, force = "enemy" }) do
|
||||
if (math.random(0, reductionFactor) > 0) then
|
||||
entity.destroy()
|
||||
end
|
||||
end
|
||||
@ -878,14 +901,12 @@ end
|
||||
---@param big_percent integer
|
||||
---@return nil
|
||||
function DowngradeWormsInArea(surface, area, small_percent, medium_percent, big_percent)
|
||||
|
||||
-- Leave out "small-worm-turret" as it's the lowest.
|
||||
local worm_types = {"medium-worm-turret", "big-worm-turret", "behemoth-worm-turret"}
|
||||
|
||||
for _, entity in pairs(surface.find_entities_filtered{area = area, name = worm_types}) do
|
||||
local worm_types = { "medium-worm-turret", "big-worm-turret", "behemoth-worm-turret" }
|
||||
|
||||
for _, entity in pairs(surface.find_entities_filtered { area = area, name = worm_types }) do
|
||||
-- Roll a number between 0-100
|
||||
local rand_percent = math.random(0,100)
|
||||
local rand_percent = math.random(0, 100)
|
||||
local worm_pos = entity.position
|
||||
local worm_name = entity.name
|
||||
|
||||
@ -893,35 +914,34 @@ function DowngradeWormsInArea(surface, area, small_percent, medium_percent, big_
|
||||
if (rand_percent <= small_percent) then
|
||||
if (not (worm_name == "small-worm-turret")) then
|
||||
entity.destroy()
|
||||
surface.create_entity{name = "small-worm-turret", position = worm_pos, force = game.forces.enemy}
|
||||
surface.create_entity { name = "small-worm-turret", position = worm_pos, force = game.forces.enemy }
|
||||
end
|
||||
|
||||
-- ELSE If number is less than medium percent, change to small
|
||||
-- ELSE If number is less than medium percent, change to small
|
||||
elseif (rand_percent <= medium_percent) then
|
||||
if (not (worm_name == "medium-worm-turret")) then
|
||||
entity.destroy()
|
||||
surface.create_entity{name = "medium-worm-turret", position = worm_pos, force = game.forces.enemy}
|
||||
surface.create_entity { name = "medium-worm-turret", position = worm_pos, force = game.forces.enemy }
|
||||
end
|
||||
|
||||
-- ELSE If number is less than big percent, change to small
|
||||
-- ELSE If number is less than big percent, change to small
|
||||
elseif (rand_percent <= big_percent) then
|
||||
if (not (worm_name == "big-worm-turret")) then
|
||||
entity.destroy()
|
||||
surface.create_entity{name = "big-worm-turret", position = worm_pos, force = game.forces.enemy}
|
||||
surface.create_entity { name = "big-worm-turret", position = worm_pos, force = game.forces.enemy }
|
||||
end
|
||||
|
||||
-- ELSE ignore it.
|
||||
-- ELSE ignore it.
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function DowngradeWormsDistanceBasedOnChunkGenerate(event)
|
||||
if (util.distance({x=0,y=0}, event.area.left_top) < (global.ocfg.near_dist_end*CHUNK_SIZE)) then
|
||||
if (util.distance({ x = 0, y = 0 }, event.area.left_top) < (global.ocfg.near_dist_end * CHUNK_SIZE)) then
|
||||
DowngradeWormsInArea(event.surface, event.area, 100, 100, 100)
|
||||
elseif (util.distance({x=0,y=0}, event.area.left_top) < (global.ocfg.far_dist_start*CHUNK_SIZE)) then
|
||||
elseif (util.distance({ x = 0, y = 0 }, event.area.left_top) < (global.ocfg.far_dist_start * CHUNK_SIZE)) then
|
||||
DowngradeWormsInArea(event.surface, event.area, 50, 90, 100)
|
||||
elseif (util.distance({x=0,y=0}, event.area.left_top) < (global.ocfg.far_dist_end*CHUNK_SIZE)) then
|
||||
elseif (util.distance({ x = 0, y = 0 }, event.area.left_top) < (global.ocfg.far_dist_end * CHUNK_SIZE)) then
|
||||
DowngradeWormsInArea(event.surface, event.area, 20, 80, 97)
|
||||
else
|
||||
DowngradeWormsInArea(event.surface, event.area, 0, 20, 90)
|
||||
@ -954,8 +974,8 @@ function RemoveWormsInArea(surface, area, small, medium, big, behemoth)
|
||||
|
||||
-- Destroy
|
||||
if (TableLength(worm_types) > 0) then
|
||||
for _, entity in pairs(surface.find_entities_filtered{area = area, name = worm_types}) do
|
||||
entity.destroy()
|
||||
for _, entity in pairs(surface.find_entities_filtered { area = area, name = worm_types }) do
|
||||
entity.destroy()
|
||||
end
|
||||
else
|
||||
log("RemoveWormsInArea had empty worm_types list!")
|
||||
@ -1214,29 +1234,27 @@ end
|
||||
---@param fillTile string
|
||||
---@return nil
|
||||
function CreateCropCircle(surface, centerPos, chunkArea, tileRadius, fillTile)
|
||||
|
||||
local tileRadSqr = tileRadius^2
|
||||
local tileRadSqr = tileRadius ^ 2
|
||||
|
||||
local dirtTiles = {}
|
||||
for i=chunkArea.left_top.x,chunkArea.right_bottom.x,1 do
|
||||
for j=chunkArea.left_top.y,chunkArea.right_bottom.y,1 do
|
||||
|
||||
for i = chunkArea.left_top.x, chunkArea.right_bottom.x, 1 do
|
||||
for j = chunkArea.left_top.y, chunkArea.right_bottom.y, 1 do
|
||||
-- This ( X^2 + Y^2 ) is used to calculate if something
|
||||
-- is inside a circle area.
|
||||
local distSqr = math.floor((centerPos.x - i)^2 + (centerPos.y - j)^2)
|
||||
local distSqr = math.floor((centerPos.x - i) ^ 2 + (centerPos.y - j) ^ 2)
|
||||
|
||||
-- Fill in all unexpected water in a circle
|
||||
if (distSqr < tileRadSqr) then
|
||||
if (surface.get_tile(i,j).collides_with("water-tile") or
|
||||
global.ocfg.spawn_config.general.force_grass) then
|
||||
table.insert(dirtTiles, {name = fillTile, position ={i,j}})
|
||||
if (surface.get_tile(i, j).collides_with("water-tile") or
|
||||
global.ocfg.spawn_config.general.force_grass) then
|
||||
table.insert(dirtTiles, { name = fillTile, position = { i, j } })
|
||||
end
|
||||
end
|
||||
|
||||
-- Create a circle of trees around the spawn point.
|
||||
if ((distSqr < tileRadSqr-100) and
|
||||
(distSqr > tileRadSqr-500)) then
|
||||
surface.create_entity({name="tree-02", amount=1, position={i, j}})
|
||||
if ((distSqr < tileRadSqr - 100) and
|
||||
(distSqr > tileRadSqr - 500)) then
|
||||
surface.create_entity({ name = "tree-02", amount = 1, position = { i, j } })
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1252,28 +1270,26 @@ end
|
||||
---@param fillTile string
|
||||
---@return nil
|
||||
function CreateCropOctagon(surface, centerPos, chunkArea, tileRadius, fillTile)
|
||||
|
||||
local dirtTiles = {}
|
||||
for i=chunkArea.left_top.x,chunkArea.right_bottom.x,1 do
|
||||
for j=chunkArea.left_top.y,chunkArea.right_bottom.y,1 do
|
||||
|
||||
for i = chunkArea.left_top.x, chunkArea.right_bottom.x, 1 do
|
||||
for j = chunkArea.left_top.y, chunkArea.right_bottom.y, 1 do
|
||||
local distVar1 = math.floor(math.max(math.abs(centerPos.x - i), math.abs(centerPos.y - j)))
|
||||
local distVar2 = math.floor(math.abs(centerPos.x - i) + math.abs(centerPos.y - j))
|
||||
local distVar = math.max(distVar1*1.1, distVar2 * 0.707*1.1);
|
||||
local distVar = math.max(distVar1 * 1.1, distVar2 * 0.707 * 1.1);
|
||||
|
||||
-- Fill in all unexpected water in a circle
|
||||
if (distVar < tileRadius+2) then
|
||||
if (surface.get_tile(i,j).collides_with("water-tile") or
|
||||
global.ocfg.spawn_config.general.force_grass or
|
||||
(game.active_mods["oarc-restricted-build"])) then
|
||||
table.insert(dirtTiles, {name = fillTile, position ={i,j}})
|
||||
if (distVar < tileRadius + 2) then
|
||||
if (surface.get_tile(i, j).collides_with("water-tile") or
|
||||
global.ocfg.spawn_config.general.force_grass or
|
||||
(game.active_mods["oarc-restricted-build"])) then
|
||||
table.insert(dirtTiles, { name = fillTile, position = { i, j } })
|
||||
end
|
||||
end
|
||||
|
||||
-- Create a tree ring
|
||||
if ((distVar < tileRadius) and
|
||||
(distVar > tileRadius-2)) then
|
||||
surface.create_entity({name="tree-01", amount=1, position={i, j}})
|
||||
(distVar > tileRadius - 2)) then
|
||||
surface.create_entity({ name = "tree-01", amount = 1, position = { i, j } })
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1289,26 +1305,23 @@ end
|
||||
---@param bridge boolean
|
||||
---@return nil
|
||||
function CreateMoat(surface, centerPos, chunkArea, tileRadius, moatTile, bridge)
|
||||
|
||||
local tileRadSqr = tileRadius^2
|
||||
local tileRadSqr = tileRadius ^ 2
|
||||
|
||||
local tiles = {}
|
||||
for i=chunkArea.left_top.x,chunkArea.right_bottom.x,1 do
|
||||
for j=chunkArea.left_top.y,chunkArea.right_bottom.y,1 do
|
||||
|
||||
if (bridge and ((j == centerPos.y-1) or (j == centerPos.y) or (j == centerPos.y+1))) then
|
||||
for i = chunkArea.left_top.x, chunkArea.right_bottom.x, 1 do
|
||||
for j = chunkArea.left_top.y, chunkArea.right_bottom.y, 1 do
|
||||
if (bridge and ((j == centerPos.y - 1) or (j == centerPos.y) or (j == centerPos.y + 1))) then
|
||||
-- This will leave the tiles "as is" on the left and right of the spawn which has the effect of creating
|
||||
-- land connections if the spawn is on or near land.
|
||||
else
|
||||
|
||||
-- This ( X^2 + Y^2 ) is used to calculate if something
|
||||
-- is inside a circle area.
|
||||
local distVar = math.floor((centerPos.x - i)^2 + (centerPos.y - j)^2)
|
||||
local distVar = math.floor((centerPos.x - i) ^ 2 + (centerPos.y - j) ^ 2)
|
||||
|
||||
-- Create a circle of water
|
||||
if ((distVar < tileRadSqr+(1500*global.ocfg.spawn_config.general.moat_size_modifier)) and
|
||||
(distVar > tileRadSqr)) then
|
||||
table.insert(tiles, {name = moatTile, position ={i,j}})
|
||||
if ((distVar < tileRadSqr + (1500 * global.ocfg.spawn_config.general.moat_size_modifier)) and
|
||||
(distVar > tileRadSqr)) then
|
||||
table.insert(tiles, { name = moatTile, position = { i, j } })
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -1323,8 +1336,8 @@ end
|
||||
---@param length integer
|
||||
function CreateWaterStrip(surface, leftPos, length)
|
||||
local waterTiles = {}
|
||||
for i=0,length,1 do
|
||||
table.insert(waterTiles, {name = "water", position={leftPos.x+i,leftPos.y}})
|
||||
for i = 0, length, 1 do
|
||||
table.insert(waterTiles, { name = "water", position = { leftPos.x + i, leftPos.y } })
|
||||
end
|
||||
surface.set_tiles(waterTiles)
|
||||
end
|
||||
@ -1336,23 +1349,23 @@ end
|
||||
---@param position TilePosition
|
||||
---@param amount integer
|
||||
function GenerateResourcePatch(surface, resourceName, diameter, position, amount)
|
||||
local midPoint = math.floor(diameter/2)
|
||||
local midPoint = math.floor(diameter / 2)
|
||||
if (diameter == 0) then
|
||||
return
|
||||
end
|
||||
for y=-midPoint, midPoint do
|
||||
for x=-midPoint, midPoint do
|
||||
if (not global.ocfg.spawn_config.general.resources_circle_shape or ((x)^2 + (y)^2 < midPoint^2)) then
|
||||
surface.create_entity({name=resourceName, amount=amount,
|
||||
position={position.x+x, position.y+y}})
|
||||
for y = -midPoint, midPoint do
|
||||
for x = -midPoint, midPoint do
|
||||
if (not global.ocfg.spawn_config.general.resources_circle_shape or ((x) ^ 2 + (y) ^ 2 < midPoint ^ 2)) then
|
||||
surface.create_entity({
|
||||
name = resourceName,
|
||||
amount = amount,
|
||||
position = { position.x + x, position.y + y }
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
-- --------------------------------------------------------------------------------
|
||||
-- -- Holding pen for new players joining the map
|
||||
-- --------------------------------------------------------------------------------
|
||||
|
385
scenarios/OARC/lib/regrowth_map.lua
Normal file
385
scenarios/OARC/lib/regrowth_map.lua
Normal file
@ -0,0 +1,385 @@
|
||||
-- regrowth_map.lua
|
||||
-- Aug 2024
|
||||
|
||||
-- Code tracks all chunks generated and allows for deleting of inactive chunks.
|
||||
--
|
||||
-- Basic rules of regrowth:
|
||||
-- 1. Area around player is safe for quite a large distance.
|
||||
-- 2. Chunks with pollution won't be deleted.
|
||||
-- 3. Chunks with any player buildings won't be deleted.
|
||||
-- 4. Anything within radar range won't be deleted, but radar MUST be active.
|
||||
-- -- This works by refreshing all chunk timers within radar range using
|
||||
-- the on_sector_scanned event.
|
||||
-- 5. Chunks timeout after 1 hour-ish, configurable
|
||||
|
||||
require("lib/oarc_utils")
|
||||
require("config")
|
||||
|
||||
-- TODO: Make this a mod startup setting?
|
||||
REGROWTH_TIMEOUT_TICKS = TICKS_PER_HOUR -- TICKS_PER_HOUR TICKS_PER_MINUTE
|
||||
|
||||
-- Init globals and set player join area to be off limits.
|
||||
function RegrowthInit()
|
||||
global.rg = {}
|
||||
global.rg.player_refresh_index = nil
|
||||
global.rg.force_removal_flag = -2000
|
||||
global.rg.map = {}
|
||||
global.rg.removal_list = {}
|
||||
global.rg.chunk_iter = nil
|
||||
global.rg.world_eater_iter = nil
|
||||
global.rg.timeout_ticks = REGROWTH_TIMEOUT_TICKS
|
||||
end
|
||||
|
||||
function TriggerCleanup()
|
||||
global.rg.force_removal_flag = game.tick
|
||||
end
|
||||
|
||||
function RegrowthForceRemoveChunksCmd(cmd_table)
|
||||
if (game.players[cmd_table.player_index].admin) then
|
||||
TriggerCleanup()
|
||||
end
|
||||
end
|
||||
|
||||
-- Get the next player index available
|
||||
function GetNextPlayerIndex(player_index)
|
||||
if (not global.rg.player_refresh_index or not game.players[global.rg.player_refresh_index]) then
|
||||
global.rg.player_refresh_index = 1
|
||||
else
|
||||
global.rg.player_refresh_index = global.rg.player_refresh_index + 1
|
||||
end
|
||||
|
||||
if (global.rg.player_refresh_index > #game.players) then
|
||||
global.rg.player_refresh_index = 1
|
||||
end
|
||||
|
||||
return global.rg.player_refresh_index
|
||||
end
|
||||
|
||||
-- Adds new chunks to the global table to track them.
|
||||
-- This should always be called first in the chunk generate sequence
|
||||
-- (Compared to other RSO & Oarc related functions...)
|
||||
function RegrowthChunkGenerate(event)
|
||||
local c_pos = GetChunkPosFromTilePos(event.area.left_top)
|
||||
|
||||
-- Surface must be "added" first.
|
||||
if (global.rg == nil) then return end
|
||||
|
||||
-- If this is the first chunk in that row:
|
||||
if (global.rg.map[c_pos.x] == nil) then
|
||||
global.rg.map[c_pos.x] = {}
|
||||
end
|
||||
|
||||
-- Only update it if it isn't already set!
|
||||
if (global.rg.map[c_pos.x][c_pos.y] == nil) then
|
||||
global.rg.map[c_pos.x][c_pos.y] = game.tick
|
||||
end
|
||||
end
|
||||
|
||||
-- Mark an area for "immediate" forced removal
|
||||
function RegrowthMarkAreaForRemoval(pos, chunk_radius)
|
||||
local c_pos = GetChunkPosFromTilePos(pos)
|
||||
for i=-chunk_radius,chunk_radius do
|
||||
local x = c_pos.x+i
|
||||
for k=-chunk_radius,chunk_radius do
|
||||
local y = c_pos.y+k
|
||||
|
||||
if (global.rg.map[x] ~= nil) then
|
||||
global.rg.map[x][y] = nil
|
||||
end
|
||||
table.insert(global.rg.removal_list, {pos={x=x,y=y},force=true})
|
||||
end
|
||||
if (table_size(global.rg.map[x]) == 0) then
|
||||
global.rg.map[x] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Downgrades permanent flag to semi-permanent.
|
||||
function RegrowthMarkAreaNotPermanentOVERWRITE(pos, chunk_radius)
|
||||
local c_pos = GetChunkPosFromTilePos(pos)
|
||||
for i=-chunk_radius,chunk_radius do
|
||||
local x = c_pos.x+i
|
||||
for k=-chunk_radius,chunk_radius do
|
||||
local y = c_pos.y+k
|
||||
|
||||
if (global.rg.map[x] and global.rg.map[x][y] and (global.rg.map[x][y] == -2)) then
|
||||
global.rg.map[x][y] = -1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Marks a chunk containing a position to be relatively permanent.
|
||||
function MarkChunkSafe(c_pos, permanent)
|
||||
if (global.rg.map[c_pos.x] == nil) then
|
||||
global.rg.map[c_pos.x] = {}
|
||||
end
|
||||
|
||||
if (permanent) then
|
||||
global.rg.map[c_pos.x][c_pos.y] = -2
|
||||
|
||||
-- Make sure we don't overwrite...
|
||||
elseif (global.rg.map[c_pos.x][c_pos.y] and (global.rg.map[c_pos.x][c_pos.y] ~= -2)) then
|
||||
global.rg.map[c_pos.x][c_pos.y] = -1
|
||||
end
|
||||
end
|
||||
|
||||
-- Marks a safe area around a CHUNK position to be relatively permanent.
|
||||
function RegrowthMarkAreaSafeGivenChunkPos(c_pos, chunk_radius, permanent)
|
||||
if (global.rg == nil) then return end
|
||||
|
||||
for i=-chunk_radius,chunk_radius do
|
||||
for j=-chunk_radius,chunk_radius do
|
||||
MarkChunkSafe({x=c_pos.x+i,y=c_pos.y+j}, permanent)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Marks a safe area around a TILE position to be relatively permanent.
|
||||
function RegrowthMarkAreaSafeGivenTilePos(pos, chunk_radius, permanent)
|
||||
if (global.rg == nil) then return end
|
||||
|
||||
local c_pos = GetChunkPosFromTilePos(pos)
|
||||
RegrowthMarkAreaSafeGivenChunkPos(c_pos, chunk_radius, permanent)
|
||||
end
|
||||
|
||||
-- Refreshes timers on a chunk containing position
|
||||
function RefreshChunkTimer(pos, bonus_time)
|
||||
local c_pos = GetChunkPosFromTilePos(pos)
|
||||
|
||||
if (global.rg.map[c_pos.x] == nil) then
|
||||
global.rg.map[c_pos.x] = {}
|
||||
end
|
||||
if (global.rg.map[c_pos.x][c_pos.y] >= 0) then
|
||||
global.rg.map[c_pos.x][c_pos.y] = game.tick + bonus_time
|
||||
end
|
||||
end
|
||||
|
||||
-- Refreshes timers on all chunks around a certain area
|
||||
function RefreshArea(pos, chunk_radius, bonus_time)
|
||||
local c_pos = GetChunkPosFromTilePos(pos)
|
||||
|
||||
for i=-chunk_radius,chunk_radius do
|
||||
local x = c_pos.x+i
|
||||
for k=-chunk_radius,chunk_radius do
|
||||
local y = c_pos.y+k
|
||||
|
||||
if (global.rg.map[x] == nil) then
|
||||
global.rg.map[x] = {}
|
||||
end
|
||||
if ((global.rg.map[x][y] == nil) or (global.rg.map[x][y] >= 0)) then
|
||||
global.rg.map[x][y] = game.tick + bonus_time
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Refreshes timers on all chunks near an ACTIVE radar
|
||||
function RegrowthSectorScan(event)
|
||||
if (event.radar.surface.name ~= GAME_SURFACE_NAME) then return end
|
||||
|
||||
RefreshArea(event.radar.position, 14, 0)
|
||||
end
|
||||
|
||||
-- Refresh all chunks near a single player. Cyles through all connected players.
|
||||
function RefreshPlayerArea()
|
||||
player_index = GetNextPlayerIndex()
|
||||
if (player_index and game.connected_players[player_index]) then
|
||||
local player = game.connected_players[player_index]
|
||||
|
||||
if (not player.character) then return end
|
||||
if (player.character.surface.name ~= GAME_SURFACE_NAME) then return end
|
||||
|
||||
RefreshArea(player.position, 4, 0)
|
||||
end
|
||||
end
|
||||
|
||||
-- Gets the next chunk the array map and checks to see if it has timed out.
|
||||
-- Adds it to the removal list if it has.
|
||||
function RegrowthSingleStepArray()
|
||||
|
||||
-- Make sure we have a valid iterator!
|
||||
if (not global.rg.chunk_iter or not global.rg.chunk_iter.valid) then
|
||||
global.rg.chunk_iter = game.surfaces[GAME_SURFACE_NAME].get_chunks()
|
||||
end
|
||||
|
||||
local next_chunk = global.rg.chunk_iter()
|
||||
|
||||
-- Check if we reached the end
|
||||
if (not next_chunk) then
|
||||
global.rg.chunk_iter = game.surfaces[GAME_SURFACE_NAME].get_chunks()
|
||||
next_chunk = global.rg.chunk_iter()
|
||||
end
|
||||
|
||||
-- Do we have it in our map?
|
||||
if (not global.rg.map[next_chunk.x] or not global.rg.map[next_chunk.x][next_chunk.y]) then
|
||||
return -- Chunk isn't in our map so we don't care?
|
||||
end
|
||||
|
||||
-- If the chunk has timed out, add it to the removal list
|
||||
local c_timer = global.rg.map[next_chunk.x][next_chunk.y]
|
||||
if ((c_timer ~= nil) and (c_timer >= 0) and ((c_timer + global.rg.timeout_ticks) < game.tick)) then
|
||||
|
||||
-- Check chunk actually exists
|
||||
if (game.surfaces[GAME_SURFACE_NAME].is_chunk_generated({x=next_chunk.x, y=next_chunk.y})) then
|
||||
table.insert(global.rg.removal_list, {pos={x=next_chunk.x, y=next_chunk.y}, force=false})
|
||||
global.rg.map[next_chunk.x][next_chunk.y] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove all chunks at same time to reduce impact to FPS/UPS
|
||||
function OarcRegrowthRemoveAllChunks()
|
||||
for key,c_remove in pairs(global.rg.removal_list) do
|
||||
local c_pos = c_remove.pos
|
||||
|
||||
-- Confirm chunk is still expired
|
||||
if (not global.rg.map[c_pos.x] or not global.rg.map[c_pos.x][c_pos.y]) then
|
||||
|
||||
-- If it is FORCE removal, then remove it regardless of pollution.
|
||||
if (c_remove.force) then
|
||||
game.surfaces[GAME_SURFACE_NAME].delete_chunk(c_pos)
|
||||
|
||||
-- If it is a normal timeout removal, don't do it if there is pollution in the chunk.
|
||||
elseif (game.surfaces[GAME_SURFACE_NAME].get_pollution({c_pos.x*32,c_pos.y*32}) > 0) then
|
||||
global.rg.map[c_pos.x][c_pos.y] = game.tick
|
||||
|
||||
-- Else delete the chunk
|
||||
else
|
||||
game.surfaces[GAME_SURFACE_NAME].delete_chunk(c_pos)
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove entry
|
||||
global.rg.removal_list[key] = nil
|
||||
end
|
||||
|
||||
-- MUST GET A NEW CHUNK ITERATOR ON DELETE CHUNK!
|
||||
global.rg.chunk_iter = nil
|
||||
global.rg.world_eater_iter = nil
|
||||
end
|
||||
|
||||
-- This is the main work function, it checks a single chunk in the list
|
||||
-- per tick. It works according to the rules listed in the header of this
|
||||
-- file.
|
||||
function RegrowthOnTick()
|
||||
|
||||
-- Every half a second, refresh all chunks near a single player
|
||||
-- Cyles through all players. Tick is offset by 2
|
||||
if ((game.tick % (30)) == 2) then
|
||||
RefreshPlayerArea()
|
||||
end
|
||||
|
||||
-- Every tick, check a few points in the 2d array of the only active surface According to /measured-command this
|
||||
-- shouldn't take more than 0.1ms on average
|
||||
for i=1,20 do
|
||||
RegrowthSingleStepArray()
|
||||
end
|
||||
|
||||
if (not global.world_eater_disable) then
|
||||
WorldEaterSingleStep()
|
||||
end
|
||||
|
||||
-- Allow enable/disable of auto cleanup, can change during runtime.
|
||||
local interval_ticks = global.rg.timeout_ticks
|
||||
-- Send a broadcast warning before it happens.
|
||||
if ((game.tick % interval_ticks) == interval_ticks-(60*30 + 1)) then
|
||||
if (#global.rg.removal_list > 100) then
|
||||
SendBroadcastMsg("Map cleanup in 30 seconds... Unused and old map chunks will be deleted!")
|
||||
end
|
||||
end
|
||||
|
||||
-- Delete all listed chunks across all active surfaces
|
||||
if ((game.tick % interval_ticks) == interval_ticks-1) then
|
||||
if (#global.rg.removal_list > 100) then
|
||||
OarcRegrowthRemoveAllChunks()
|
||||
SendBroadcastMsg("Map cleanup done, sorry for your loss.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- This function removes any chunks flagged but on demand.
|
||||
-- Controlled by the global.rg.force_removal_flag
|
||||
-- This function may be used outside of the normal regrowth modse.
|
||||
function RegrowthForceRemovalOnTick()
|
||||
-- Catch force remove flag
|
||||
if (game.tick == global.rg.force_removal_flag+60) then
|
||||
SendBroadcastMsg("Map cleanup (forced) in 30 seconds... Unused and old map chunks will be deleted!")
|
||||
end
|
||||
|
||||
if (game.tick == global.rg.force_removal_flag+(60*30 + 60)) then
|
||||
OarcRegrowthRemoveAllChunks()
|
||||
SendBroadcastMsg("Map cleanup done, sorry for your loss.")
|
||||
end
|
||||
end
|
||||
|
||||
function WorldEaterSingleStep()
|
||||
|
||||
-- Make sure we have a valid iterator!
|
||||
if (not global.rg.world_eater_iter or not global.rg.world_eater_iter.valid) then
|
||||
global.rg.world_eater_iter = game.surfaces[GAME_SURFACE_NAME].get_chunks()
|
||||
end
|
||||
|
||||
local next_chunk = global.rg.world_eater_iter()
|
||||
|
||||
-- Check if we reached the end
|
||||
if (not next_chunk) then
|
||||
global.rg.world_eater_iter = game.surfaces[GAME_SURFACE_NAME].get_chunks()
|
||||
next_chunk = global.rg.world_eater_iter()
|
||||
end
|
||||
|
||||
-- Do we have it in our map?
|
||||
if (not global.rg.map[next_chunk.x] or not global.rg.map[next_chunk.x][next_chunk.y]) then
|
||||
return -- Chunk isn't in our map so we don't care?
|
||||
end
|
||||
|
||||
-- Search for any abandoned radars and destroy them?
|
||||
local entities = game.surfaces[GAME_SURFACE_NAME].find_entities_filtered{area=next_chunk.area,
|
||||
force={global.ocore.abandoned_force},
|
||||
name="radar"}
|
||||
for k,v in pairs(entities) do
|
||||
v.die(nil)
|
||||
end
|
||||
|
||||
-- Search for any entities with _DESTROYED_ force and kill them.
|
||||
entities = game.surfaces[GAME_SURFACE_NAME].find_entities_filtered{area=next_chunk.area,
|
||||
force={global.ocore.destroyed_force}}
|
||||
for k,v in pairs(entities) do
|
||||
v.die(nil)
|
||||
end
|
||||
|
||||
-- If the chunk isn't marked permament, then check if we can remove it
|
||||
local c_timer = global.rg.map[next_chunk.x][next_chunk.y]
|
||||
if (c_timer == -1) then
|
||||
|
||||
local area = {left_top = {next_chunk.area.left_top.x-8, next_chunk.area.left_top.y-8},
|
||||
right_bottom = {next_chunk.area.right_bottom.x+8, next_chunk.area.right_bottom.y+8}}
|
||||
|
||||
local entities = game.surfaces[GAME_SURFACE_NAME].find_entities_filtered{area=area, force={"enemy", "neutral"}, invert=true}
|
||||
local total_count = #entities
|
||||
local has_last_user_set = false
|
||||
|
||||
if (total_count > 0) then
|
||||
for k,v in pairs(entities) do
|
||||
if (v.last_user or (v.type == "character") or string.contains(v.type, "robot")) then
|
||||
has_last_user_set = true
|
||||
return -- This means we're done checking this chunk.
|
||||
end
|
||||
end
|
||||
|
||||
-- If all entities found have no last user, then KILL all entities!
|
||||
if (not has_last_user_set) then
|
||||
for k,v in pairs(entities) do
|
||||
if (v and v.valid) then
|
||||
v.die(nil)
|
||||
end
|
||||
end
|
||||
-- SendBroadcastMsg(next_chunk.x .. "," .. next_chunk.y .. " WorldEaterSingleStep - ENTITIES FOUND")
|
||||
global.rg.map[next_chunk.x][next_chunk.y] = game.tick -- Set the timer on it.
|
||||
end
|
||||
else
|
||||
-- SendBroadcastMsg(next_chunk.x .. "," .. next_chunk.y .. " WorldEaterSingleStep - NO ENTITIES FOUND")
|
||||
global.rg.map[next_chunk.x][next_chunk.y] = game.tick -- Set the timer on it.
|
||||
end
|
||||
end
|
||||
end
|
@ -27,7 +27,7 @@ BUDDY_SPAWN_CHOICE = {
|
||||
---Contains the respawn point for a player. Usually this is their home base but it can be changed.
|
||||
---@alias OarcPlayerSpawn { surface: LuaSurface, position: MapPosition }
|
||||
---Table of [OarcSharedSpawn](lua://OarcSharedSpawn) indexed by player name.
|
||||
---@alias OarcPlayerSpawnTable table<string, OarcPlayerSpawn>
|
||||
---@alias OarcPlayerSpawnsTable table<string, OarcPlayerSpawn>
|
||||
|
||||
---A unique spawn point. This is what chunk generation checks against.
|
||||
---@alias OarcUniqueSpawn { surface: LuaSurface, position: MapPosition, moat: boolean }
|
||||
@ -35,7 +35,7 @@ BUDDY_SPAWN_CHOICE = {
|
||||
---@alias OarcUniqueSpawnsTable table<string, OarcUniqueSpawn>
|
||||
|
||||
---A shared spawn point. This is a spawn point that multiple players can share.
|
||||
---@alias OarcSharedSpawn { surface: LuaSurface, position: MapPosition, openAccess: boolean, players: string[] }
|
||||
---@alias OarcSharedSpawn { surface: LuaSurface, position: MapPosition, openAccess: boolean, players: string[], joinQueue: string[] }
|
||||
---Table of [OarcSharedSpawn](lua://OarcSharedSpawn) indexed by a unique name.
|
||||
---@alias OarcSharedSpawnsTable table<string, OarcSharedSpawn>
|
||||
|
||||
@ -70,10 +70,13 @@ function InitSpawnGlobalsAndForces()
|
||||
global.ocore = {}
|
||||
end
|
||||
|
||||
-- TODO: Add a global for which surfaces allow spawns.
|
||||
|
||||
-- This contains each player's spawn point. Literally where they will respawn.
|
||||
-- There is a way in game to change this under one of the little menu features I added.
|
||||
---TODO: Add support for multiple surfaces!
|
||||
if (global.ocore.playerSpawns == nil) then
|
||||
global.ocore.playerSpawns --[[@as OarcPlayerSpawnTable]] = {}
|
||||
global.ocore.playerSpawns --[[@as OarcPlayerSpawnsTable]] = {}
|
||||
end
|
||||
|
||||
-- This is the most important table. It is a list of all the unique spawn points.
|
||||
@ -208,7 +211,8 @@ function CreateNewSharedSpawn(player)
|
||||
surface = global.ocore.playerSpawns[player.name].surface,
|
||||
position = global.ocore.playerSpawns[player.name].position,
|
||||
openAccess = true,
|
||||
players = {}
|
||||
players = {},
|
||||
joinQueue = {}
|
||||
}
|
||||
end
|
||||
|
||||
@ -277,7 +281,6 @@ end
|
||||
---@param delayedSpawn OarcDelayedSpawn
|
||||
---@return nil
|
||||
function SendPlayerToNewSpawnAndCreateIt(delayedSpawn)
|
||||
|
||||
local ocfg --[[@as OarcConfig]] = global.ocfg
|
||||
|
||||
-- DOUBLE CHECK and make sure the area is super safe.
|
||||
@ -285,20 +288,20 @@ function SendPlayerToNewSpawnAndCreateIt(delayedSpawn)
|
||||
|
||||
-- TODO: Vanilla spawn point are not implemented yet.
|
||||
-- if (not delayedSpawn.vanilla) then
|
||||
|
||||
-- Generate water strip only if we don't have a moat.
|
||||
if (not delayedSpawn.moat) then
|
||||
local water_data = ocfg.spawn_config.water
|
||||
CreateWaterStrip(delayedSpawn.surface,
|
||||
{ x = delayedSpawn.position.x + water_data.x_offset, y = delayedSpawn.position.y + water_data.y_offset },
|
||||
water_data.length)
|
||||
CreateWaterStrip(delayedSpawn.surface,
|
||||
{ x = delayedSpawn.position.x + water_data.x_offset, y = delayedSpawn.position.y + water_data.y_offset + 1 },
|
||||
water_data.length)
|
||||
end
|
||||
|
||||
-- Create the spawn resources here
|
||||
GenerateStartingResources(delayedSpawn.surface, delayedSpawn.position)
|
||||
-- Generate water strip only if we don't have a moat.
|
||||
if (not delayedSpawn.moat) then
|
||||
local water_data = ocfg.spawn_config.water
|
||||
CreateWaterStrip(delayedSpawn.surface,
|
||||
{ x = delayedSpawn.position.x + water_data.x_offset, y = delayedSpawn.position.y + water_data.y_offset },
|
||||
water_data.length)
|
||||
CreateWaterStrip(delayedSpawn.surface,
|
||||
{ x = delayedSpawn.position.x + water_data.x_offset, y = delayedSpawn.position.y + water_data.y_offset + 1 },
|
||||
water_data.length)
|
||||
end
|
||||
|
||||
-- Create the spawn resources here
|
||||
GenerateStartingResources(delayedSpawn.surface, delayedSpawn.position)
|
||||
|
||||
-- end -- Vanilla spawn point are not implemented yet.
|
||||
|
||||
@ -380,11 +383,9 @@ end
|
||||
---@param chunkArea BoundingBox
|
||||
---@return nil
|
||||
function SetupAndClearSpawnAreas(surface, chunkArea)
|
||||
|
||||
local spawn_config --[[@as OarcConfigSpawn]] = global.ocfg.spawn_config
|
||||
|
||||
for name,spawn in pairs(global.ocore.uniqueSpawns --[[@as OarcUniqueSpawnsTable]] ) do
|
||||
|
||||
for name, spawn in pairs(global.ocore.uniqueSpawns --[[@as OarcUniqueSpawnsTable]]) do
|
||||
if (spawn.surface ~= surface) then
|
||||
return
|
||||
end
|
||||
@ -407,13 +408,13 @@ function SetupAndClearSpawnAreas(surface, chunkArea)
|
||||
if (util.distance(spawn.position, chunkAreaCenter) < spawn_config.safe_area.safe_radius) then
|
||||
RemoveAliensInArea(surface, chunkArea)
|
||||
|
||||
-- Create a warning area with heavily reduced enemies
|
||||
-- Create a warning area with heavily reduced enemies
|
||||
elseif (util.distance(spawn.position, chunkAreaCenter) < spawn_config.safe_area.warn_radius) then
|
||||
ReduceAliensInArea(surface, chunkArea, spawn_config.safe_area.warn_reduction)
|
||||
-- DowngradeWormsInArea(surface, chunkArea, 100, 100, 100)
|
||||
RemoveWormsInArea(surface, chunkArea, false, true, true, true) -- remove all non-small worms.
|
||||
|
||||
-- Create a third area with moderatly reduced enemies
|
||||
-- Create a third area with moderatly reduced enemies
|
||||
elseif (util.distance(spawn.position, chunkAreaCenter) < spawn_config.safe_area.danger_radius) then
|
||||
ReduceAliensInArea(surface, chunkArea, spawn_config.safe_area.danger_reduction)
|
||||
-- DowngradeWormsInArea(surface, chunkArea, 50, 100, 100)
|
||||
@ -422,52 +423,53 @@ function SetupAndClearSpawnAreas(surface, chunkArea)
|
||||
|
||||
-- TODO: Vanilla spawn point are not implemented yet.
|
||||
-- if (not spawn.vanilla) then
|
||||
|
||||
|
||||
local enable_test = global.ocfg.mod_overlap.enable_moat_bridging
|
||||
|
||||
-- If the chunk is within the main land area, then clear trees/resources
|
||||
-- and create the land spawn areas (guaranteed land with a circle of trees)
|
||||
if CheckIfInArea(chunkAreaCenter, landArea) then
|
||||
-- Remove trees/resources inside the spawn area
|
||||
RemoveInCircle(surface, chunkArea, "tree", spawn.position, spawn_config.general.land_area_tiles)
|
||||
RemoveInCircle(surface, chunkArea, "resource", spawn.position, spawn_config.general.land_area_tiles + 5)
|
||||
RemoveInCircle(surface, chunkArea, "cliff", spawn.position, spawn_config.general.land_area_tiles + 5)
|
||||
-- If the chunk is within the main land area, then clear trees/resources
|
||||
-- and create the land spawn areas (guaranteed land with a circle of trees)
|
||||
if CheckIfInArea(chunkAreaCenter, landArea) then
|
||||
-- Remove trees/resources inside the spawn area
|
||||
RemoveInCircle(surface, chunkArea, "tree", spawn.position, spawn_config.general.land_area_tiles)
|
||||
RemoveInCircle(surface, chunkArea, "resource", spawn.position, spawn_config.general.land_area_tiles + 5)
|
||||
RemoveInCircle(surface, chunkArea, "cliff", spawn.position, spawn_config.general.land_area_tiles + 5)
|
||||
|
||||
local fill_tile = "landfill"
|
||||
if (spawn_config.general.tree_circle) then
|
||||
CreateCropCircle(surface, spawn.position, chunkArea, spawn_config.general.land_area_tiles, fill_tile)
|
||||
end
|
||||
if (spawn_config.general.tree_octagon) then
|
||||
CreateCropOctagon(surface, spawn.position, chunkArea, spawn_config.general.land_area_tiles, fill_tile)
|
||||
end
|
||||
-- TODO: Confirm removal of global setting check of enable_allow_moats_around_spawns is okay?
|
||||
if (spawn.moat) then
|
||||
CreateMoat(surface,
|
||||
spawn.position,
|
||||
chunkArea,
|
||||
spawn_config.general.land_area_tiles,
|
||||
"water",
|
||||
global.ocfg.mod_overlap.enable_moat_bridging)
|
||||
end
|
||||
local fill_tile = "landfill"
|
||||
if (spawn_config.general.tree_circle) then
|
||||
CreateCropCircle(surface, spawn.position, chunkArea, spawn_config.general.land_area_tiles, fill_tile)
|
||||
end
|
||||
if (spawn_config.general.tree_octagon) then
|
||||
CreateCropOctagon(surface, spawn.position, chunkArea, spawn_config.general.land_area_tiles, fill_tile)
|
||||
end
|
||||
-- TODO: Confirm removal of global setting check of enable_allow_moats_around_spawns is okay?
|
||||
if (spawn.moat) then
|
||||
CreateMoat(surface,
|
||||
spawn.position,
|
||||
chunkArea,
|
||||
spawn_config.general.land_area_tiles,
|
||||
"water",
|
||||
global.ocfg.mod_overlap.enable_moat_bridging)
|
||||
end
|
||||
end
|
||||
|
||||
-- end -- Vanilla spawn point are not implemented yet.
|
||||
end
|
||||
end
|
||||
|
||||
-- This is the main function that creates the spawn area
|
||||
-- Provides resources, land and a safe zone
|
||||
---This is the main function that creates the spawn area. Provides resources, land and a safe zone.
|
||||
---@param event EventData.on_chunk_generated
|
||||
---@return nil
|
||||
function SeparateSpawnsGenerateChunk(event)
|
||||
local surface = event.surface
|
||||
local chunkArea = event.area
|
||||
|
||||
-- Modify enemies first.
|
||||
if global.ocfg.modified_enemy_spawning then
|
||||
if global.ocfg.gameplay.oarc_modified_enemy_spawning then
|
||||
DowngradeWormsDistanceBasedOnChunkGenerate(event)
|
||||
end
|
||||
|
||||
-- Downgrade resources near to spawns
|
||||
if global.ocfg.scale_resources_around_spawns then
|
||||
if global.ocfg.gameplay.scale_resources_around_spawns then
|
||||
DowngradeResourcesDistanceBasedOnChunkGenerate(surface, chunkArea)
|
||||
end
|
||||
|
||||
@ -477,13 +479,16 @@ function SeparateSpawnsGenerateChunk(event)
|
||||
SetupAndClearSpawnAreas(surface, chunkArea)
|
||||
end
|
||||
|
||||
-- Based on the danger distance, you get full resources, and it is exponential from the spawn point to that distance.
|
||||
---Based on the danger distance, you get full resources, and it is exponential from the spawn point to that distance.
|
||||
---@param surface LuaSurface
|
||||
---@param chunkArea BoundingBox
|
||||
---@return nil
|
||||
function DowngradeResourcesDistanceBasedOnChunkGenerate(surface, chunkArea)
|
||||
local closestSpawn = GetClosestUniqueSpawn(chunkArea.left_top)
|
||||
local closestSpawn = GetClosestUniqueSpawn(surface, chunkArea.left_top)
|
||||
|
||||
if (closestSpawn == nil) then return end
|
||||
|
||||
local distance = util.distance(chunkArea.left_top, closestSpawn.pos)
|
||||
local distance = util.distance(chunkArea.left_top, closestSpawn.position)
|
||||
-- Adjust multiplier to bring it in or out
|
||||
local modifier = (distance / (global.ocfg.spawn_config.safe_area.danger_radius * 1)) ^ 3
|
||||
if modifier < 0.1 then modifier = 0.1 end
|
||||
@ -510,6 +515,8 @@ end
|
||||
-- I wrote this to ensure everyone gets safer spawns regardless of evolution level.
|
||||
-- This is intended to downgrade any biters/spitters spawning near player bases.
|
||||
-- I'm not sure the performance impact of this but I'm hoping it's not bad.
|
||||
---@param event EventData.on_entity_spawned|EventData.on_biter_base_built
|
||||
---@return nil
|
||||
function ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
if (not event.entity or not (event.entity.force.name == "enemy") or not event.entity.position) then
|
||||
log("ModifyBiterSpawns - Unexpected use.")
|
||||
@ -520,7 +527,7 @@ function ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
local surface = event.entity.surface
|
||||
local enemy_name = event.entity.name
|
||||
|
||||
local closest_spawn = GetClosestUniqueSpawn(enemy_pos)
|
||||
local closest_spawn = GetClosestUniqueSpawn(surface, enemy_pos)
|
||||
|
||||
if (closest_spawn == nil) then
|
||||
-- log("GetClosestUniqueSpawn ERROR - None found?")
|
||||
@ -528,11 +535,11 @@ function ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
end
|
||||
|
||||
-- No enemies inside safe radius!
|
||||
if (util.distance(enemy_pos, closest_spawn.pos) < global.ocfg.spawn_config.safe_area.safe_radius) then
|
||||
if (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.spawn_config.safe_area.safe_radius) then
|
||||
event.entity.destroy()
|
||||
|
||||
-- Warn distance is all SMALL only.
|
||||
elseif (util.distance(enemy_pos, closest_spawn.pos) < global.ocfg.spawn_config.safe_area.warn_radius) then
|
||||
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.spawn_config.safe_area.warn_radius) then
|
||||
if ((enemy_name == "big-biter") or (enemy_name == "behemoth-biter") or (enemy_name == "medium-biter")) then
|
||||
event.entity.destroy()
|
||||
surface.create_entity { name = "small-biter", position = enemy_pos, force = game.forces.enemy }
|
||||
@ -548,7 +555,7 @@ function ModifyEnemySpawnsNearPlayerStartingAreas(event)
|
||||
end
|
||||
|
||||
-- Danger distance is MEDIUM max.
|
||||
elseif (util.distance(enemy_pos, closest_spawn.pos) < global.ocfg.spawn_config.safe_area.danger_radius) then
|
||||
elseif (util.distance(enemy_pos, closest_spawn.position) < global.ocfg.spawn_config.safe_area.danger_radius) then
|
||||
if ((enemy_name == "big-biter") or (enemy_name == "behemoth-biter")) then
|
||||
event.entity.destroy()
|
||||
surface.create_entity { name = "medium-biter", position = enemy_pos, force = game.forces.enemy }
|
||||
@ -573,15 +580,17 @@ end
|
||||
|
||||
--]]
|
||||
|
||||
|
||||
---Resets the player and destroys their force if they are not on the main one.
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function ResetPlayerAndDestroyForce(player)
|
||||
local player_old_force = player.force
|
||||
|
||||
player.force = global.ocfg.main_force
|
||||
player.force = global.ocfg.gameplay.main_force_name
|
||||
|
||||
if ((#player_old_force.players == 0) and (player_old_force.name ~= global.ocfg.main_force)) then
|
||||
if ((#player_old_force.players == 0) and (player_old_force.name ~= global.ocfg.gameplay.main_force_name)) then
|
||||
SendBroadcastMsg("Team " ..
|
||||
player_old_force.name .. " has been destroyed! All buildings will slowly be destroyed now.")
|
||||
player_old_force.name .. " has been destroyed! All buildings will slowly be destroyed now.")
|
||||
log("DestroyForce - FORCE DESTROYED: " .. player_old_force.name)
|
||||
game.merge_forces(player_old_force, global.ocore.destroyed_force)
|
||||
end
|
||||
@ -590,12 +599,15 @@ function ResetPlayerAndDestroyForce(player)
|
||||
SeparateSpawnsPlayerCreated(player.index, false)
|
||||
end
|
||||
|
||||
---Resets the player and merges their force into the abandoned_force.
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function ResetPlayerAndAbandonForce(player)
|
||||
local player_old_force = player.force
|
||||
|
||||
player.force = global.ocfg.main_force
|
||||
player.force = global.ocfg.gameplay.main_force_name
|
||||
|
||||
if ((#player_old_force.players == 0) and (player_old_force.name ~= global.ocfg.main_force)) then
|
||||
if ((#player_old_force.players == 0) and (player_old_force.name ~= global.ocfg.gameplay.main_force_name)) then
|
||||
SendBroadcastMsg("Team " .. player_old_force.name .. " has been abandoned!")
|
||||
log("AbandonForce - FORCE ABANDONED: " .. player_old_force.name)
|
||||
game.merge_forces(player_old_force, global.ocore.abandoned_force)
|
||||
@ -605,11 +617,17 @@ function ResetPlayerAndAbandonForce(player)
|
||||
SeparateSpawnsPlayerCreated(player.index, false)
|
||||
end
|
||||
|
||||
---Reset player and merge their force to neutral
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function ResetPlayerAndMergeForceToNeutral(player)
|
||||
RemoveOrResetPlayer(player, false, true, true, true)
|
||||
SeparateSpawnsPlayerCreated(player.index, true)
|
||||
end
|
||||
|
||||
---Kicks player from game and marks player for removal from globals.
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function KickAndMarkPlayerForRemoval(player)
|
||||
game.kick_player(player, "KickAndMarkPlayerForRemoval")
|
||||
if (not global.ocore.player_removal_list) then
|
||||
@ -618,7 +636,12 @@ function KickAndMarkPlayerForRemoval(player)
|
||||
table.insert(global.ocore.player_removal_list, player)
|
||||
end
|
||||
|
||||
-- Call this if a player leaves the game early (or a player wants an early game reset)
|
||||
---Call this if a player leaves the game early (or a player wants an early game reset)
|
||||
---@param player LuaPlayer
|
||||
---@param remove_player boolean Deletes player from the game assuming they are offline.
|
||||
---@param remove_force boolean
|
||||
---@param remove_base boolean
|
||||
---@param immediate boolean
|
||||
function RemoveOrResetPlayer(player, remove_player, remove_force, remove_base, immediate)
|
||||
if (not player) then
|
||||
log("ERROR - CleanupPlayer on NIL Player!")
|
||||
@ -627,9 +650,9 @@ function RemoveOrResetPlayer(player, remove_player, remove_force, remove_base, i
|
||||
|
||||
-- If this player is staying in the game, lets make sure we don't delete them along with the map chunks being
|
||||
-- cleared.
|
||||
player.teleport({ x = 0, y = 0 }, GAME_SURFACE_NAME)
|
||||
player.teleport({ x = 0, y = 0 }, global.ocfg.gameplay.main_force_surface)
|
||||
local player_old_force = player.force
|
||||
player.force = global.ocfg.main_force
|
||||
player.force = global.ocfg.gameplay.main_force_name
|
||||
|
||||
-- Clear globals
|
||||
CleanupPlayerGlobals(player.name) -- Except global.ocore.uniqueSpawns
|
||||
@ -639,7 +662,7 @@ function RemoveOrResetPlayer(player, remove_player, remove_force, remove_base, i
|
||||
|
||||
-- Remove a force if this player created it and they are the only one on it
|
||||
if (remove_force) then
|
||||
if ((#player_old_force.players == 0) and (player_old_force.name ~= global.ocfg.main_force)) then
|
||||
if ((#player_old_force.players == 0) and (player_old_force.name ~= global.ocfg.gameplay.main_force_name)) then
|
||||
log("RemoveOrResetPlayer - FORCE REMOVED: " .. player_old_force.name)
|
||||
game.merge_forces(player_old_force, "neutral")
|
||||
end
|
||||
@ -651,42 +674,55 @@ function RemoveOrResetPlayer(player, remove_player, remove_force, remove_base, i
|
||||
end
|
||||
end
|
||||
|
||||
---Cleans up a player's unique spawn point.
|
||||
---TODO: Move relevant stuff to regrowth?
|
||||
---@param playerName string
|
||||
---@param cleanup boolean
|
||||
---@param immediate boolean Trrigger cleanup immediately.
|
||||
---@return nil
|
||||
function UniqueSpawnCleanupRemove(playerName, cleanup, immediate)
|
||||
if (global.ocore.uniqueSpawns[playerName] == nil) then return end -- Safety
|
||||
log("UniqueSpawnCleanupRemove - " .. playerName)
|
||||
|
||||
local spawnPos = global.ocore.uniqueSpawns[playerName].pos
|
||||
local land_area_tiles = global.ocfg.spawn_config.general.land_area_tiles
|
||||
|
||||
-- Check if it was near someone else's base. (Really just buddy base is possible I think.)
|
||||
nearOtherSpawn = false
|
||||
for spawnPlayerName, otherSpawnPos in pairs(global.ocore.uniqueSpawns) do
|
||||
if ((spawnPlayerName ~= playerName) and (util.distance(spawnPos, otherSpawnPos.pos) < (global.ocfg.spawn_config.land_area_tiles * 3))) then
|
||||
if ((spawnPlayerName ~= playerName) and
|
||||
(util.distance(spawnPos, otherSpawnPos.pos) < (land_area_tiles * 3))) then
|
||||
log("Won't remove base as it's close to another spawn: " .. spawnPlayerName)
|
||||
nearOtherSpawn = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Unused Chunk Removal mod (aka regrowth)
|
||||
if (cleanup and global.ocfg.enable_abandoned_base_removal and (not nearOtherSpawn) and global.ocfg.enable_regrowth) then
|
||||
if (global.ocore.uniqueSpawns[playerName].vanilla) then
|
||||
log("Returning a vanilla spawn back to available.")
|
||||
table.insert(global.vanillaSpawns, { x = spawnPos.x, y = spawnPos.y })
|
||||
end
|
||||
if (cleanup and global.ocfg.mod_overlap.enable_abandoned_base_cleanup and
|
||||
(not nearOtherSpawn) and global.ocfg.mod_overlap.enable_regrowth) then
|
||||
-- TODO: Vanilla spawn point are not implemented yet.
|
||||
-- if (global.ocore.uniqueSpawns[playerName].vanilla) then
|
||||
-- log("Returning a vanilla spawn back to available.")
|
||||
-- table.insert(global.vanillaSpawns, { x = spawnPos.x, y = spawnPos.y })
|
||||
-- end
|
||||
|
||||
if (immediate) then
|
||||
log("IMMEDIATE Removing base: " .. spawnPos.x .. "," .. spawnPos.y)
|
||||
RegrowthMarkAreaForRemoval(spawnPos, math.ceil(global.ocfg.spawn_config.land_area_tiles / CHUNK_SIZE))
|
||||
RegrowthMarkAreaForRemoval(spawnPos, math.ceil(land_area_tiles / CHUNK_SIZE))
|
||||
TriggerCleanup()
|
||||
else
|
||||
log("Removing permanent flags on base: " .. spawnPos.x .. "," .. spawnPos.y)
|
||||
RegrowthMarkAreaNotPermanentOVERWRITE(spawnPos,
|
||||
math.ceil(global.ocfg.spawn_config.land_area_tiles / CHUNK_SIZE))
|
||||
math.ceil(land_area_tiles / CHUNK_SIZE))
|
||||
end
|
||||
end
|
||||
|
||||
global.ocore.uniqueSpawns[playerName] = nil
|
||||
end
|
||||
|
||||
---Cleans up all references to a player in the global tables.
|
||||
---@param playerName string
|
||||
---@return nil
|
||||
function CleanupPlayerGlobals(playerName)
|
||||
-- Clear the buddy pair IF one exists
|
||||
if (global.ocore.buddyPairs[playerName] ~= nil) then
|
||||
@ -710,7 +746,8 @@ function CleanupPlayerGlobals(playerName)
|
||||
|
||||
-- Transfer or remove a shared spawn if player is owner
|
||||
if (global.ocore.sharedSpawns[playerName] ~= nil) then
|
||||
local teamMates = global.ocore.sharedSpawns[playerName].players
|
||||
local sharedSpawn = global.ocore.sharedSpawns[playerName] --[[@as OarcSharedSpawn]]
|
||||
local teamMates = sharedSpawn.players
|
||||
|
||||
if (#teamMates >= 1) then
|
||||
local newOwnerName = table.remove(teamMates) -- Remove 1 to use as new owner.
|
||||
@ -722,7 +759,7 @@ function CleanupPlayerGlobals(playerName)
|
||||
end
|
||||
|
||||
-- Remove from other shared spawns (need to search all)
|
||||
for _, sharedSpawn in pairs(global.ocore.sharedSpawns) do
|
||||
for _, sharedSpawn in pairs(global.ocore.sharedSpawns --[[@as OarcSharedSpawnsTable]]) do
|
||||
for key, name in pairs(sharedSpawn.players) do
|
||||
if (playerName == name) then
|
||||
sharedSpawn.players[key] = nil;
|
||||
@ -738,14 +775,15 @@ function CleanupPlayerGlobals(playerName)
|
||||
end
|
||||
|
||||
-- Remove them from the delayed spawn queue if they are in it
|
||||
for idx, delayedSpawn in pairs(global.ocore.delayedSpawns) do
|
||||
for index, delayedSpawn in pairs(global.ocore.delayedSpawns --[[@as OarcDelayedSpawnsTable]]) do
|
||||
if (playerName == delayedSpawn.playerName) then
|
||||
if (delayedSpawn.vanilla) then
|
||||
log("Returning a vanilla spawn back to available.")
|
||||
table.insert(global.vanillaSpawns, { x = delayedSpawn.pos.x, y = delayedSpawn.pos.y })
|
||||
end
|
||||
---TODO: Vanilla spawn point are not implemented yet.
|
||||
-- if (delayedSpawn.vanilla) then
|
||||
-- log("Returning a vanilla spawn back to available.")
|
||||
-- table.insert(global.vanillaSpawns, { x = delayedSpawn.pos.x, y = delayedSpawn.pos.y })
|
||||
-- end
|
||||
|
||||
table.remove(global.ocore.delayedSpawns, idx)
|
||||
global.ocore.delayedSpawns[index] = nil
|
||||
log("Removing player from delayed spawn queue: " .. playerName)
|
||||
break
|
||||
end
|
||||
@ -754,18 +792,20 @@ function CleanupPlayerGlobals(playerName)
|
||||
if (global.ocore.playerCooldowns[playerName] ~= nil) then
|
||||
global.ocore.playerCooldowns[playerName] = nil
|
||||
end
|
||||
|
||||
global.oarc_store.pmf_counts[playerName] = {}
|
||||
end
|
||||
|
||||
---Transfers ownership of a shared spawn to another player.
|
||||
---@param prevOwnerName string
|
||||
---@param newOwnerName string
|
||||
---@return nil
|
||||
function TransferOwnershipOfSharedSpawn(prevOwnerName, newOwnerName)
|
||||
-- Transfer the shared spawn global
|
||||
global.ocore.sharedSpawns[newOwnerName] = global.ocore.sharedSpawns[prevOwnerName]
|
||||
global.ocore.sharedSpawns[newOwnerName] = global.ocore.sharedSpawns[prevOwnerName] --[[@as OarcSharedSpawn]]
|
||||
global.ocore.sharedSpawns[newOwnerName].openAccess = false
|
||||
global.ocore.sharedSpawns[prevOwnerName] = nil
|
||||
|
||||
-- Transfer the unique spawn global
|
||||
global.ocore.uniqueSpawns[newOwnerName] = global.ocore.uniqueSpawns[prevOwnerName]
|
||||
global.ocore.uniqueSpawns[newOwnerName] = global.ocore.uniqueSpawns[prevOwnerName] --[[@as OarcUniqueSpawn]]
|
||||
global.ocore.uniqueSpawns[prevOwnerName] = nil
|
||||
|
||||
game.players[newOwnerName].print("You have been given ownership of this base!")
|
||||
@ -779,12 +819,19 @@ end
|
||||
|
||||
--]]
|
||||
|
||||
-- Same as GetClosestPosFromTable but specific to global.ocore.uniqueSpawns
|
||||
function GetClosestUniqueSpawn(pos)
|
||||
---Same as GetClosestPosFromTable but specific to global.ocore.uniqueSpawns
|
||||
---@param surface LuaSurface
|
||||
---@param pos MapPosition
|
||||
---@return OarcUniqueSpawn?
|
||||
function GetClosestUniqueSpawn(surface, pos)
|
||||
local closest_dist = nil
|
||||
local closest_key = nil
|
||||
|
||||
for k, s in pairs(global.ocore.uniqueSpawns) do
|
||||
if (s.surface ~= surface) then
|
||||
goto CONTINUE
|
||||
end
|
||||
|
||||
local new_dist = util.distance(pos, s.pos)
|
||||
if (closest_dist == nil) then
|
||||
closest_dist = new_dist
|
||||
@ -793,6 +840,8 @@ function GetClosestUniqueSpawn(pos)
|
||||
closest_dist = new_dist
|
||||
closest_key = k
|
||||
end
|
||||
|
||||
::CONTINUE:: -- Continue loop label
|
||||
end
|
||||
|
||||
if (closest_key == nil) then
|
||||
@ -803,8 +852,9 @@ function GetClosestUniqueSpawn(pos)
|
||||
return global.ocore.uniqueSpawns[closest_key]
|
||||
end
|
||||
|
||||
-- Return the owner of the shared spawn for this player.
|
||||
-- May return nil if player has not spawned yet.
|
||||
---Return the owner of the shared spawn for this player. May return nil if player has not spawned yet.
|
||||
---@param playerName string
|
||||
---@return string?
|
||||
function FindPlayerSharedSpawn(playerName)
|
||||
-- If the player IS an owner, he can't be in any other shared base.
|
||||
if (global.ocore.sharedSpawns[playerName] ~= nil) then
|
||||
@ -812,7 +862,7 @@ function FindPlayerSharedSpawn(playerName)
|
||||
end
|
||||
|
||||
-- Otherwise, search all shared spawns for this player and return the owner.
|
||||
for ownerName, sharedSpawn in pairs(global.ocore.sharedSpawns) do
|
||||
for ownerName, sharedSpawn in pairs(global.ocore.sharedSpawns --[[@as OarcSharedSpawnsTable]]) do
|
||||
for _, sharingPlayerName in pairs(sharedSpawn.players) do
|
||||
if (playerName == sharingPlayerName) then
|
||||
return ownerName
|
||||
@ -824,9 +874,13 @@ function FindPlayerSharedSpawn(playerName)
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Returns the number of players currently online at the shared spawn
|
||||
---Returns the number of players currently online at the shared spawn
|
||||
---@param ownerName string
|
||||
---@return number
|
||||
function GetOnlinePlayersAtSharedSpawn(ownerName)
|
||||
if (global.ocore.sharedSpawns[ownerName] ~= nil) then
|
||||
local sharedSpawn = global.ocore.sharedSpawns[ownerName] --[[@as OarcSharedSpawn]]
|
||||
|
||||
if (sharedSpawn ~= nil) then
|
||||
-- Does not count base owner
|
||||
local count = 0
|
||||
|
||||
@ -836,7 +890,8 @@ function GetOnlinePlayersAtSharedSpawn(ownerName)
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
for _, playerName in pairs(global.ocore.sharedSpawns[ownerName].players) do
|
||||
|
||||
for _, playerName in pairs(sharedSpawn.players) do
|
||||
if (playerName == player.name) then
|
||||
count = count + 1
|
||||
end
|
||||
@ -849,18 +904,21 @@ function GetOnlinePlayersAtSharedSpawn(ownerName)
|
||||
end
|
||||
end
|
||||
|
||||
-- Get the number of currently available shared spawns
|
||||
-- Get the number of currently available shared spawns.
|
||||
-- This means the base owner has enabled access AND the number of online players
|
||||
-- is below the threshold.
|
||||
---@return number
|
||||
function GetNumberOfAvailableSharedSpawns()
|
||||
local count = 0
|
||||
|
||||
for ownerName, sharedSpawn in pairs(global.ocore.sharedSpawns) do
|
||||
local number_of_players_per_shared_spawn = global.ocfg.mod_overlap.number_of_players_per_shared_spawn
|
||||
|
||||
for ownerName, sharedSpawn in pairs(global.ocore.sharedSpawns --[[@as OarcSharedSpawnsTable]]) do
|
||||
if (sharedSpawn.openAccess and
|
||||
(game.players[ownerName] ~= nil) and
|
||||
game.players[ownerName].connected) then
|
||||
if ((global.ocfg.max_players_shared_spawn == 0) or
|
||||
(#global.ocore.sharedSpawns[ownerName].players < global.ocfg.max_players_shared_spawn)) then
|
||||
if ((number_of_players_per_shared_spawn == 0) or
|
||||
(#global.ocore.sharedSpawns[ownerName].players < number_of_players_per_shared_spawn)) then
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
@ -869,8 +927,11 @@ function GetNumberOfAvailableSharedSpawns()
|
||||
return count
|
||||
end
|
||||
|
||||
---Checks if player has a custom spawn point set.
|
||||
---@param player LuaPlayer
|
||||
---@return boolean
|
||||
function DoesPlayerHaveCustomSpawn(player)
|
||||
for name, spawnPos in pairs(global.ocore.playerSpawns) do
|
||||
for name, spawnPos in pairs(global.ocore.playerSpawns --[[@as OarcPlayerSpawnsTable]]) do
|
||||
if (player.name == name) then
|
||||
return true
|
||||
end
|
||||
@ -882,7 +943,7 @@ end
|
||||
---@param player LuaPlayer
|
||||
---@return OarcPlayerSpawn?
|
||||
function GetPlayerCustomSpawn(player)
|
||||
for name, playerSpawn in pairs(global.ocore.playerSpawns) do
|
||||
for name, playerSpawn in pairs(global.ocore.playerSpawns --[[@as OarcPlayerSpawnsTable]]) do
|
||||
if (player.name == name) then
|
||||
return playerSpawn
|
||||
end
|
||||
@ -890,30 +951,59 @@ function GetPlayerCustomSpawn(player)
|
||||
return nil
|
||||
end
|
||||
|
||||
function ChangePlayerSpawn(player, pos)
|
||||
global.ocore.playerSpawns[player.name] = pos
|
||||
--TODO: Add support for multiple surfaces
|
||||
---Sets the custom spawn point for a player.
|
||||
---@param player LuaPlayer
|
||||
---@param position MapPosition
|
||||
---@return nil
|
||||
function ChangePlayerSpawn(player, position)
|
||||
global.ocore.playerSpawns[player.name] = position
|
||||
global.ocore.playerCooldowns[player.name] = { setRespawn = game.tick }
|
||||
end
|
||||
|
||||
function QueuePlayerForDelayedSpawn(playerName, spawn, moatEnabled, vanillaSpawn)
|
||||
---Queue a player for a delayed spawn.
|
||||
---@param playerName string
|
||||
---@param surface LuaSurface
|
||||
---@param spawnPosition MapPosition
|
||||
---@param moatEnabled boolean
|
||||
---@param vanillaSpawn boolean
|
||||
---@return nil
|
||||
function QueuePlayerForDelayedSpawn(playerName, surface, spawnPosition, moatEnabled, vanillaSpawn)
|
||||
-- If we get a valid spawn point, setup the area
|
||||
if ((spawn.x ~= 0) or (spawn.y ~= 0)) then
|
||||
global.ocore.uniqueSpawns[playerName] = { pos = spawn, moat = moatEnabled, vanilla = vanillaSpawn }
|
||||
if ((spawnPosition.x ~= 0) or (spawnPosition.y ~= 0)) then
|
||||
---@type OarcUniqueSpawn
|
||||
local newUniqueSpawn = {}
|
||||
newUniqueSpawn.surface = surface
|
||||
newUniqueSpawn.position = spawnPosition
|
||||
newUniqueSpawn.moat = moatEnabled
|
||||
|
||||
local delay_spawn_seconds = 5 * (math.ceil(global.ocfg.spawn_config.land_area_tiles / CHUNK_SIZE))
|
||||
---TODO: Vanilla spawn point are not implemented yet.
|
||||
-- newUniqueSpawn.vanilla = vanillaSpawn
|
||||
global.ocore.uniqueSpawns[playerName] = newUniqueSpawn
|
||||
|
||||
local delay_spawn_seconds = 5 * (math.ceil(global.ocfg.spawn_config.general.land_area_tiles / CHUNK_SIZE))
|
||||
|
||||
---TODO: Move text to locale.
|
||||
game.players[playerName].print("Generating your spawn now, please wait for at least " ..
|
||||
delay_spawn_seconds .. " seconds...")
|
||||
game.players[playerName].surface.request_to_generate_chunks(spawn, 4)
|
||||
delayedTick = game.tick + delay_spawn_seconds * TICKS_PER_SECOND
|
||||
table.insert(global.ocore.delayedSpawns,
|
||||
{ playerName = playerName, pos = spawn, moat = moatEnabled, vanilla = vanillaSpawn, delayedTick = delayedTick })
|
||||
delay_spawn_seconds .. " seconds...")
|
||||
game.surfaces[surface].request_to_generate_chunks(spawnPosition, 4)
|
||||
|
||||
|
||||
---@type OarcDelayedSpawn
|
||||
local delayedSpawn = {}
|
||||
delayedSpawn.playerName = playerName
|
||||
delayedSpawn.surface = surface
|
||||
delayedSpawn.position = spawnPosition
|
||||
delayedSpawn.moat = moatEnabled
|
||||
delayedSpawn.delayedTick = game.tick + delay_spawn_seconds * TICKS_PER_SECOND
|
||||
|
||||
table.insert(global.ocore.delayedSpawns, delayedSpawn)
|
||||
|
||||
HideOarcGui(game.players[playerName])
|
||||
HideOarcStore(game.players[playerName])
|
||||
DisplayPleaseWaitForSpawnDialog(game.players[playerName], delay_spawn_seconds)
|
||||
|
||||
RegrowthMarkAreaSafeGivenTilePos(spawn, math.ceil(global.ocfg.spawn_config.land_area_tiles / CHUNK_SIZE), true)
|
||||
RegrowthMarkAreaSafeGivenTilePos(spawnPosition,
|
||||
math.ceil(global.ocfg.spawn_config.general.land_area_tiles / CHUNK_SIZE), true)
|
||||
else
|
||||
log("THIS SHOULD NOT EVER HAPPEN! Spawn failed!")
|
||||
SendBroadcastMsg("ERROR!! Failed to create spawn point for: " .. playerName)
|
||||
@ -923,14 +1013,18 @@ end
|
||||
-- Check a table to see if there are any players waiting to spawn
|
||||
-- Check if we are past the delayed tick count
|
||||
-- Spawn the players and remove them from the table.
|
||||
---@return nil
|
||||
function DelayedSpawnOnTick()
|
||||
if ((game.tick % (30)) == 1) then
|
||||
if ((global.ocore.delayedSpawns ~= nil) and (#global.ocore.delayedSpawns > 0)) then
|
||||
|
||||
--TODO: Investigate this magic indexing with ints and keys?
|
||||
-- I think this loop removes from the back of the table to the front??
|
||||
for i = #global.ocore.delayedSpawns, 1, -1 do
|
||||
delayedSpawn = global.ocore.delayedSpawns[i]
|
||||
delayedSpawn = global.ocore.delayedSpawns[i] --[[@as OarcDelayedSpawn]]
|
||||
|
||||
if (delayedSpawn.delayedTick < game.tick) then
|
||||
-- TODO, add check here for if chunks around spawn are generated surface.is_chunk_generated(chunkPos)
|
||||
-- TODO: add check here for if chunks around spawn are generated surface.is_chunk_generated(chunkPos)
|
||||
if (game.players[delayedSpawn.playerName] ~= nil) then
|
||||
SendPlayerToNewSpawnAndCreateIt(delayedSpawn)
|
||||
end
|
||||
@ -941,6 +1035,9 @@ function DelayedSpawnOnTick()
|
||||
end
|
||||
end
|
||||
|
||||
---Send player to their custom spawn point if one exists, otherwise to the force's spawn point.
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function SendPlayerToSpawn(player)
|
||||
local playerSpawn = GetPlayerCustomSpawn(player)
|
||||
if (playerSpawn ~= nil) then
|
||||
@ -948,19 +1045,24 @@ function SendPlayerToSpawn(player)
|
||||
playerSpawn.surface,
|
||||
playerSpawn.position)
|
||||
else
|
||||
local gameplayConfig = global.ocfg.gameplay --[[@as OarcConfigGameplaySettings]]
|
||||
SafeTeleport(player,
|
||||
global.ocfg.gameplay.main_force_surface,
|
||||
game.forces[global.ocfg.gameplay.main_force_name].get_spawn_position(GAME_SURFACE_NAME))
|
||||
gameplayConfig.main_force_surface,
|
||||
game.forces[gameplayConfig.main_force_name].get_spawn_position(gameplayConfig.main_force_surface))
|
||||
end
|
||||
end
|
||||
|
||||
---Send player to a random spawn point.
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function SendPlayerToRandomSpawn(player)
|
||||
local numSpawns = TableLength(global.ocore.uniqueSpawns)
|
||||
local rndSpawn = math.random(0, numSpawns)
|
||||
local counter = 0
|
||||
|
||||
if (rndSpawn == 0) then
|
||||
player.teleport(game.forces[global.ocfg.main_force].get_spawn_position(GAME_SURFACE_NAME), GAME_SURFACE_NAME)
|
||||
local gameplayConfig = global.ocfg.gameplay --[[@as OarcConfigGameplaySettings]]
|
||||
player.teleport(game.forces[gameplayConfig.main_force_name].get_spawn_position(gameplayConfig.main_force_surface), gameplayConfig.main_force_surface)
|
||||
else
|
||||
counter = counter + 1
|
||||
for name, spawn in pairs(global.ocore.uniqueSpawns) do
|
||||
@ -981,6 +1083,10 @@ end
|
||||
|
||||
--]]
|
||||
|
||||
|
||||
---Create a new force
|
||||
---@param force_name string
|
||||
---@return LuaForce
|
||||
function CreateForce(force_name)
|
||||
local newForce = nil
|
||||
|
||||
@ -992,47 +1098,47 @@ function CreateForce(force_name)
|
||||
-- Create a new force
|
||||
elseif (TableLength(game.forces) < MAX_FORCES) then
|
||||
newForce = game.create_force(force_name)
|
||||
if global.ocfg.enable_shared_team_vision then
|
||||
if global.ocfg.mod_overlap.enable_shared_team_vision then
|
||||
newForce.share_chart = true
|
||||
end
|
||||
if global.ocfg.enable_research_queue then
|
||||
newForce.research_queue_enabled = true
|
||||
end
|
||||
-- Chart silo areas if necessary
|
||||
if global.ocfg.frontier_rocket_silo and global.ocfg.frontier_silo_vision then
|
||||
ChartRocketSiloAreas(game.surfaces[GAME_SURFACE_NAME], newForce)
|
||||
end
|
||||
|
||||
-- This now defaults to true?
|
||||
-- if global.ocfg.enable_research_queue then
|
||||
-- newForce.research_queue_enabled = true
|
||||
-- end
|
||||
|
||||
|
||||
SetCeaseFireBetweenAllForces()
|
||||
SetFriendlyBetweenAllForces()
|
||||
newForce.friendly_fire = global.ocfg.enable_friendly_fire
|
||||
if (global.ocfg.enable_anti_grief) then
|
||||
AntiGriefing(newForce)
|
||||
end
|
||||
|
||||
if global.ocfg.lock_goodies_rocket_launch and not global.ocore.satellite_sent then
|
||||
for _, v in ipairs(LOCKED_TECHNOLOGIES) do
|
||||
DisableTech(newForce, v.t)
|
||||
end
|
||||
end
|
||||
newForce.friendly_fire = global.ocfg.mod_overlap.enable_friendly_fire
|
||||
|
||||
-- if (global.ocfg.enable_anti_grief) then
|
||||
-- AntiGriefing(newForce)
|
||||
-- end
|
||||
|
||||
else
|
||||
log("TOO MANY FORCES!!! - CreateForce()")
|
||||
return game.forces[global.ocfg.main_force]
|
||||
return game.forces[global.ocfg.gameplay.main_force_name]
|
||||
end
|
||||
|
||||
-- Add productivity bonus for solo teams.
|
||||
if (ENABLE_FORCE_LAB_PROD_BONUS) then
|
||||
local tech_mult = game.difficulty_settings.technology_price_multiplier
|
||||
if (tech_mult > 1) and (force_name ~= global.ocfg.main_force) then
|
||||
newForce.laboratory_productivity_bonus = (tech_mult - 1)
|
||||
end
|
||||
end
|
||||
-- if (ENABLE_FORCE_LAB_PROD_BONUS) then
|
||||
-- local tech_mult = game.difficulty_settings.technology_price_multiplier
|
||||
-- if (tech_mult > 1) and (force_name ~= global.ocfg.main_force) then
|
||||
-- newForce.laboratory_productivity_bonus = (tech_mult - 1)
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- Loot distance buff
|
||||
newForce.character_loot_pickup_distance_bonus = 16
|
||||
-- newForce.character_loot_pickup_distance_bonus = 16
|
||||
|
||||
return newForce
|
||||
end
|
||||
|
||||
---Create a new player force and assign the player to it.
|
||||
---@param player LuaPlayer
|
||||
---@return LuaForce
|
||||
function CreatePlayerCustomForce(player)
|
||||
local newForce = CreateForce(player.name)
|
||||
player.force = newForce
|
||||
@ -1056,107 +1162,107 @@ end
|
||||
|
||||
-- Function to generate some map_gen_settings.starting_points
|
||||
-- You should only use this at the start of the game really.
|
||||
function CreateVanillaSpawns(count, spacing)
|
||||
local points = {}
|
||||
-- function CreateVanillaSpawns(count, spacing)
|
||||
-- local points = {}
|
||||
|
||||
-- Get an ODD number from the square of the input count.
|
||||
-- Always rounding up so we don't end up with less points that requested.
|
||||
local sqrt_count = math.ceil(math.sqrt(count))
|
||||
if (sqrt_count % 2 == 0) then
|
||||
sqrt_count = sqrt_count + 1
|
||||
end
|
||||
-- -- Get an ODD number from the square of the input count.
|
||||
-- -- Always rounding up so we don't end up with less points that requested.
|
||||
-- local sqrt_count = math.ceil(math.sqrt(count))
|
||||
-- if (sqrt_count % 2 == 0) then
|
||||
-- sqrt_count = sqrt_count + 1
|
||||
-- end
|
||||
|
||||
-- Need to know how much to offset the grid.
|
||||
local sqrt_half = math.floor((sqrt_count - 1) / 2)
|
||||
-- -- Need to know how much to offset the grid.
|
||||
-- local sqrt_half = math.floor((sqrt_count - 1) / 2)
|
||||
|
||||
if (sqrt_count < 1) then
|
||||
log("CreateVanillaSpawns less than 1!!")
|
||||
return
|
||||
end
|
||||
-- if (sqrt_count < 1) then
|
||||
-- log("CreateVanillaSpawns less than 1!!")
|
||||
-- return
|
||||
-- end
|
||||
|
||||
if (global.vanillaSpawns == nil) then
|
||||
global.vanillaSpawns = {}
|
||||
end
|
||||
-- if (global.vanillaSpawns == nil) then
|
||||
-- global.vanillaSpawns = {}
|
||||
-- end
|
||||
|
||||
-- This should give me points centered around 0,0 I think.
|
||||
for i = -sqrt_half, sqrt_half, 1 do
|
||||
for j = -sqrt_half, sqrt_half, 1 do
|
||||
if (i ~= 0 or j ~= 0) then -- EXCEPT don't put 0,0
|
||||
local x_pos = (i * spacing)
|
||||
x_pos = x_pos - (x_pos % CHUNK_SIZE) + (CHUNK_SIZE / 2)
|
||||
local y_pos = (j * spacing)
|
||||
y_pos = y_pos - (y_pos % CHUNK_SIZE) + (CHUNK_SIZE / 2)
|
||||
-- -- This should give me points centered around 0,0 I think.
|
||||
-- for i = -sqrt_half, sqrt_half, 1 do
|
||||
-- for j = -sqrt_half, sqrt_half, 1 do
|
||||
-- if (i ~= 0 or j ~= 0) then -- EXCEPT don't put 0,0
|
||||
-- local x_pos = (i * spacing)
|
||||
-- x_pos = x_pos - (x_pos % CHUNK_SIZE) + (CHUNK_SIZE / 2)
|
||||
-- local y_pos = (j * spacing)
|
||||
-- y_pos = y_pos - (y_pos % CHUNK_SIZE) + (CHUNK_SIZE / 2)
|
||||
|
||||
table.insert(points, { x = x_pos, y = y_pos })
|
||||
table.insert(global.vanillaSpawns, { x = x_pos, y = y_pos })
|
||||
end
|
||||
end
|
||||
end
|
||||
-- table.insert(points, { x = x_pos, y = y_pos })
|
||||
-- table.insert(global.vanillaSpawns, { x = x_pos, y = y_pos })
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- Do something with the return value.
|
||||
return points
|
||||
end
|
||||
-- -- Do something with the return value.
|
||||
-- return points
|
||||
-- end
|
||||
|
||||
-- Useful when combined with something like CreateVanillaSpawns
|
||||
-- Where it helps ensure ALL chunks generated use new map_gen_settings.
|
||||
function DeleteAllChunksExceptCenter(surface)
|
||||
-- Delete the starting chunks that make it into the game before settings are changed.
|
||||
for chunk in surface.get_chunks() do
|
||||
-- Don't delete the chunk that might contain players lol.
|
||||
-- This is really only a problem for launching AS the host. Not headless
|
||||
if ((chunk.x ~= 0) and (chunk.y ~= 0)) then
|
||||
surface.delete_chunk({ chunk.x, chunk.y })
|
||||
end
|
||||
end
|
||||
end
|
||||
-- -- Useful when combined with something like CreateVanillaSpawns
|
||||
-- -- Where it helps ensure ALL chunks generated use new map_gen_settings.
|
||||
-- function DeleteAllChunksExceptCenter(surface)
|
||||
-- -- Delete the starting chunks that make it into the game before settings are changed.
|
||||
-- for chunk in surface.get_chunks() do
|
||||
-- -- Don't delete the chunk that might contain players lol.
|
||||
-- -- This is really only a problem for launching AS the host. Not headless
|
||||
-- if ((chunk.x ~= 0) and (chunk.y ~= 0)) then
|
||||
-- surface.delete_chunk({ chunk.x, chunk.y })
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- Find a vanilla spawn as close as possible to the given target_distance
|
||||
function FindUnusedVanillaSpawn(surface, target_distance)
|
||||
local best_key = nil
|
||||
local best_distance = nil
|
||||
-- -- Find a vanilla spawn as close as possible to the given target_distance
|
||||
-- function FindUnusedVanillaSpawn(surface, target_distance)
|
||||
-- local best_key = nil
|
||||
-- local best_distance = nil
|
||||
|
||||
for k, v in pairs(global.vanillaSpawns) do
|
||||
-- Check if chunks nearby are not generated.
|
||||
local chunk_pos = GetChunkPosFromTilePos(v)
|
||||
if IsChunkAreaUngenerated(chunk_pos, CHECK_SPAWN_UNGENERATED_CHUNKS_RADIUS, surface) then
|
||||
-- Is this our first valid find?
|
||||
if ((best_key == nil) or (best_distance == nil)) then
|
||||
best_key = k
|
||||
best_distance = math.abs(math.sqrt((v.x ^ 2) + (v.y ^ 2)) - target_distance)
|
||||
-- for k, v in pairs(global.vanillaSpawns) do
|
||||
-- -- Check if chunks nearby are not generated.
|
||||
-- local chunk_pos = GetChunkPosFromTilePos(v)
|
||||
-- if IsChunkAreaUngenerated(chunk_pos, CHECK_SPAWN_UNGENERATED_CHUNKS_RADIUS, surface) then
|
||||
-- -- Is this our first valid find?
|
||||
-- if ((best_key == nil) or (best_distance == nil)) then
|
||||
-- best_key = k
|
||||
-- best_distance = math.abs(math.sqrt((v.x ^ 2) + (v.y ^ 2)) - target_distance)
|
||||
|
||||
-- Check if it is closer to target_distance than previous option.
|
||||
else
|
||||
local new_distance = math.abs(math.sqrt((v.x ^ 2) + (v.y ^ 2)) - target_distance)
|
||||
if (new_distance < best_distance) then
|
||||
best_key = k
|
||||
best_distance = new_distance
|
||||
end
|
||||
end
|
||||
-- -- Check if it is closer to target_distance than previous option.
|
||||
-- else
|
||||
-- local new_distance = math.abs(math.sqrt((v.x ^ 2) + (v.y ^ 2)) - target_distance)
|
||||
-- if (new_distance < best_distance) then
|
||||
-- best_key = k
|
||||
-- best_distance = new_distance
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- If it's not a valid spawn anymore, let's remove it.
|
||||
else
|
||||
log("Removing vanilla spawn due to chunks generated: x=" .. v.x .. ",y=" .. v.y)
|
||||
table.remove(global.vanillaSpawns, k)
|
||||
end
|
||||
end
|
||||
-- -- If it's not a valid spawn anymore, let's remove it.
|
||||
-- else
|
||||
-- log("Removing vanilla spawn due to chunks generated: x=" .. v.x .. ",y=" .. v.y)
|
||||
-- table.remove(global.vanillaSpawns, k)
|
||||
-- end
|
||||
-- end
|
||||
|
||||
local spawn_pos = { x = 0, y = 0 }
|
||||
if ((best_key ~= nil) and (global.vanillaSpawns[best_key] ~= nil)) then
|
||||
spawn_pos.x = global.vanillaSpawns[best_key].x
|
||||
spawn_pos.y = global.vanillaSpawns[best_key].y
|
||||
table.remove(global.vanillaSpawns, best_key)
|
||||
end
|
||||
log("Found unused vanilla spawn: x=" .. spawn_pos.x .. ",y=" .. spawn_pos.y)
|
||||
return spawn_pos
|
||||
end
|
||||
-- local spawn_pos = { x = 0, y = 0 }
|
||||
-- if ((best_key ~= nil) and (global.vanillaSpawns[best_key] ~= nil)) then
|
||||
-- spawn_pos.x = global.vanillaSpawns[best_key].x
|
||||
-- spawn_pos.y = global.vanillaSpawns[best_key].y
|
||||
-- table.remove(global.vanillaSpawns, best_key)
|
||||
-- end
|
||||
-- log("Found unused vanilla spawn: x=" .. spawn_pos.x .. ",y=" .. spawn_pos.y)
|
||||
-- return spawn_pos
|
||||
-- end
|
||||
|
||||
function ValidateVanillaSpawns(surface)
|
||||
for k, v in pairs(global.vanillaSpawns) do
|
||||
-- Check if chunks nearby are not generated.
|
||||
local chunk_pos = GetChunkPosFromTilePos(v)
|
||||
if not IsChunkAreaUngenerated(chunk_pos, CHECK_SPAWN_UNGENERATED_CHUNKS_RADIUS + 15, surface) then
|
||||
log("Removing vanilla spawn due to chunks generated: x=" .. v.x .. ",y=" .. v.y)
|
||||
table.remove(global.vanillaSpawns, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- function ValidateVanillaSpawns(surface)
|
||||
-- for k, v in pairs(global.vanillaSpawns) do
|
||||
-- -- Check if chunks nearby are not generated.
|
||||
-- local chunk_pos = GetChunkPosFromTilePos(v)
|
||||
-- if not IsChunkAreaUngenerated(chunk_pos, CHECK_SPAWN_UNGENERATED_CHUNKS_RADIUS + 15, surface) then
|
||||
-- log("Removing vanilla spawn due to chunks generated: x=" .. v.x .. ",y=" .. v.y)
|
||||
-- table.remove(global.vanillaSpawns, k)
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
|
@ -1,7 +1,7 @@
|
||||
-- separate_spawns_guis.lua
|
||||
-- Nov 2016
|
||||
|
||||
-- I made a separate file for all the GUI related functions
|
||||
-- I made a separate file for all the GUI related functions. Yay me.
|
||||
|
||||
require("lib/separate_spawns")
|
||||
|
||||
@ -23,8 +23,9 @@ local SPAWN_GUI_MAX_HEIGHT = 1000
|
||||
-- Oarc=sharedSpawnExample3}
|
||||
|
||||
|
||||
-- A display gui message
|
||||
-- Meant to be display the first time a player joins.
|
||||
---A display gui message. Meant to be display the first time a player joins.
|
||||
---@param player LuaPlayer
|
||||
---@return boolean
|
||||
function DisplayWelcomeTextGui(player)
|
||||
if ((player.gui.screen["welcome_msg"] ~= nil) or
|
||||
(player.gui.screen["spawn_opts"] ~= nil) or
|
||||
@ -73,7 +74,9 @@ function DisplayWelcomeTextGui(player)
|
||||
end
|
||||
|
||||
|
||||
-- Handle the gui click of the welcome msg
|
||||
---Handle the gui click of the welcome msg
|
||||
---@param event EventData.on_gui_click
|
||||
---@return nil
|
||||
function WelcomeTextGuiClick(event)
|
||||
if not (event and event.element and event.element.valid) then return end
|
||||
local player = game.players[event.player_index]
|
||||
@ -93,7 +96,9 @@ function WelcomeTextGuiClick(event)
|
||||
end
|
||||
|
||||
|
||||
-- Display the spawn options and explanation
|
||||
---Display the spawn options and explanation
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function DisplaySpawnOptions(player)
|
||||
if (player == nil) then
|
||||
log("DisplaySpawnOptions with no valid player...")
|
||||
@ -104,6 +109,9 @@ function DisplaySpawnOptions(player)
|
||||
log("Tried to display spawn options when it was already displayed!")
|
||||
return
|
||||
end
|
||||
|
||||
local mod_overlap = global.ocfg.mod_overlap
|
||||
|
||||
player.gui.screen.add{name = "spawn_opts",
|
||||
type = "frame",
|
||||
direction = "vertical",
|
||||
@ -116,7 +124,9 @@ function DisplaySpawnOptions(player)
|
||||
-- Warnings and explanations...
|
||||
local warn_msg = {"oarc-click-info-btn-help"}
|
||||
AddLabel(sGui, "warning_lbl1", warn_msg, my_warning_style)
|
||||
AddLabel(sGui, "spawn_msg_lbl1", SPAWN_MSG1, my_label_style)
|
||||
|
||||
-- TODO: Not sure what this is for...? SPAWN_MSG1 is not defined anywhere.
|
||||
-- AddLabel(sGui, "spawn_msg_lbl1", SPAWN_MSG1, my_label_style)
|
||||
|
||||
-- Button and message about the regular vanilla spawn
|
||||
-- if ENABLE_DEFAULT_SPAWN then
|
||||
@ -136,7 +146,7 @@ function DisplaySpawnOptions(player)
|
||||
style = "bordered_frame"}
|
||||
|
||||
-- Radio buttons to pick your team.
|
||||
if (global.ocfg.enable_separate_teams) then
|
||||
if (mod_overlap.enable_separate_teams) then
|
||||
soloSpawnFlow.add{name = "isolated_spawn_main_team_radio",
|
||||
type = "radiobutton",
|
||||
caption={"oarc-join-main-team-radio"},
|
||||
@ -152,7 +162,9 @@ function DisplaySpawnOptions(player)
|
||||
-- "Additional spawn options can be selected here. Not all are compatible with each other.", my_label_style)
|
||||
|
||||
-- Allow players to spawn with a moat around their area.
|
||||
if (global.ocfg.spawn_config.gen_settings.moat_choice_enabled and not global.ocfg.enable_vanilla_spawns) then
|
||||
--TODO: Vanilla spawn points are not implemented yet.
|
||||
-- and not global.ocfg.enable_vanilla_spawns
|
||||
if (mod_overlap.enable_allow_moats_around_spawns) then
|
||||
soloSpawnFlow.add{name = "isolated_spawn_moat_option_checkbox",
|
||||
type = "checkbox",
|
||||
caption={"oarc-moat-option"},
|
||||
@ -180,22 +192,22 @@ function DisplaySpawnOptions(player)
|
||||
caption={"oarc-solo-spawn-far"},
|
||||
style = "confirm_button"}
|
||||
|
||||
if (global.ocfg.enable_vanilla_spawns) then
|
||||
AddLabel(soloSpawnFlow, "isolated_spawn_lbl1",
|
||||
{"oarc-starting-area-vanilla"}, my_label_style)
|
||||
AddLabel(soloSpawnFlow, "vanilla_spawn_lbl2",
|
||||
{"oarc-vanilla-spawns-available", #global.vanillaSpawns}, my_label_style)
|
||||
else
|
||||
-- if (global.ocfg.enable_vanilla_spawns) then
|
||||
-- AddLabel(soloSpawnFlow, "isolated_spawn_lbl1",
|
||||
-- {"oarc-starting-area-vanilla"}, my_label_style)
|
||||
-- AddLabel(soloSpawnFlow, "vanilla_spawn_lbl2",
|
||||
-- {"oarc-vanilla-spawns-available", #global.vanillaSpawns}, my_label_style)
|
||||
-- else
|
||||
AddLabel(soloSpawnFlow, "isolated_spawn_lbl1",
|
||||
{"oarc-starting-area-normal"}, my_label_style)
|
||||
end
|
||||
-- end
|
||||
|
||||
-- Spawn options to join another player's base.
|
||||
local sharedSpawnFrame = sGui.add{name = "spawn_shared_flow",
|
||||
type = "frame",
|
||||
direction="vertical",
|
||||
style = "bordered_frame"}
|
||||
if global.ocfg.enable_shared_spawns then
|
||||
if mod_overlap.enable_shared_spawns then
|
||||
local numAvailSpawns = GetNumberOfAvailableSharedSpawns()
|
||||
if (numAvailSpawns > 0) then
|
||||
sharedSpawnFrame.add{name = "join_other_spawn",
|
||||
@ -215,8 +227,9 @@ function DisplaySpawnOptions(player)
|
||||
end
|
||||
|
||||
-- Awesome buddy spawning system
|
||||
if (not global.ocfg.enable_vanilla_spawns) then
|
||||
if global.ocfg.enable_shared_spawns and global.ocfg.enable_buddy_spawn then
|
||||
---TODO: Vanilla spawn points are not implemented yet.
|
||||
-- if (not global.ocfg.enable_vanilla_spawns) then
|
||||
if mod_overlap.enable_shared_spawns and mod_overlap.enable_buddy_spawn then
|
||||
local buddySpawnFrame = sGui.add{name = "spawn_buddy_flow",
|
||||
type = "frame",
|
||||
direction="vertical",
|
||||
@ -229,21 +242,27 @@ function DisplaySpawnOptions(player)
|
||||
AddLabel(buddySpawnFrame, "buddy_spawn_lbl1",
|
||||
{"oarc-buddy-spawn-info"} , my_label_style)
|
||||
end
|
||||
end
|
||||
-- end
|
||||
|
||||
-- Some final notes
|
||||
if (global.ocfg.max_players_shared_spawn > 0) then
|
||||
if (mod_overlap.number_of_players_per_shared_spawn > 0) then
|
||||
AddLabel(sGui, "max_players_lbl2",
|
||||
{"oarc-max-players-shared-spawn", global.ocfg.max_players_shared_spawn-1},
|
||||
{"oarc-max-players-shared-spawn", mod_overlap.number_of_players_per_shared_spawn-1},
|
||||
my_note_style)
|
||||
end
|
||||
local spawn_distance_notes={"oarc-spawn-dist-notes", global.ocfg.near_dist_start, global.ocfg.near_dist_end, global.ocfg.far_dist_start, global.ocfg.far_dist_end}
|
||||
|
||||
local spawn_distance_notes={"oarc-spawn-dist-notes",
|
||||
mod_overlap.near_spawn_min_distance,
|
||||
mod_overlap.near_spawn_max_distance,
|
||||
mod_overlap.far_spawn_min_distance,
|
||||
mod_overlap.far_spawn_max_distance}
|
||||
AddLabel(sGui, "note_lbl1", spawn_distance_notes, my_note_style)
|
||||
end
|
||||
|
||||
|
||||
---This just updates the radio buttons/checkboxes when players click them.
|
||||
---@param event EventData.on_gui_checked_state_changed
|
||||
---@return nil
|
||||
function SpawnOptsRadioSelect(event)
|
||||
if not (event and event.element and event.element.valid) then return end
|
||||
local elemName = event.element.name
|
||||
@ -267,7 +286,9 @@ function SpawnOptsRadioSelect(event)
|
||||
end
|
||||
|
||||
|
||||
-- Handle the gui click of the spawn options
|
||||
---Handle the gui click of the spawn options
|
||||
---@param event EventData.on_gui_click
|
||||
---@return nil
|
||||
function SpawnOptsGuiClick(event)
|
||||
if not (event and event.element and event.element.valid) then return end
|
||||
local player = game.players[event.player_index]
|
||||
@ -284,7 +305,7 @@ function SpawnOptsGuiClick(event)
|
||||
|
||||
local pgcs = player.gui.screen.spawn_opts
|
||||
|
||||
local joinMainTeamRadio, joinOwnTeamRadio, moatChoice, vanillaChoice = false
|
||||
local joinMainTeamRadio, joinOwnTeamRadio, moatChoice, vanillaChoice = false, false, false, false
|
||||
|
||||
-- Check if a valid button on the gui was pressed
|
||||
-- and delete the GUI
|
||||
@ -295,7 +316,7 @@ function SpawnOptsGuiClick(event)
|
||||
(elemName == "buddy_spawn") or
|
||||
(elemName == "join_other_spawn_check")) then
|
||||
|
||||
if (global.ocfg.enable_separate_teams) then
|
||||
if (global.ocfg.mod_overlap.enable_separate_teams) then
|
||||
joinMainTeamRadio =
|
||||
pgcs.spawn_solo_flow.isolated_spawn_main_team_radio.state
|
||||
joinOwnTeamRadio =
|
||||
@ -304,7 +325,8 @@ function SpawnOptsGuiClick(event)
|
||||
joinMainTeamRadio = true
|
||||
joinOwnTeamRadio = false
|
||||
end
|
||||
if (global.ocfg.spawn_config.gen_settings.moat_choice_enabled and not global.ocfg.enable_vanilla_spawns and
|
||||
---TODO: Vanilla spawn points are not implemented yet. and not global.ocfg.enable_vanilla_spawns
|
||||
if (global.ocfg.mod_overlap.enable_allow_moats_around_spawns and
|
||||
(pgcs.spawn_solo_flow.isolated_spawn_moat_option_checkbox ~= nil)) then
|
||||
moatChoice = pgcs.spawn_solo_flow.isolated_spawn_moat_option_checkbox.state
|
||||
end
|
||||
@ -319,9 +341,9 @@ function SpawnOptsGuiClick(event)
|
||||
|
||||
if (elemName == "default_spawn_btn") then
|
||||
GivePlayerStarterItems(player)
|
||||
ChangePlayerSpawn(player, player.force.get_spawn_position(GAME_SURFACE_NAME))
|
||||
ChangePlayerSpawn(player, player.force.get_spawn_position(global.ocfg.gameplay.main_force_surface))
|
||||
SendBroadcastMsg({"oarc-player-is-joining-main-force", player.name})
|
||||
ChartArea(player.force, player.position, math.ceil(global.ocfg.spawn_config.gen_settings.land_area_tiles/CHUNK_SIZE), player.surface)
|
||||
ChartArea(player.force, player.position, math.ceil(global.ocfg.spawn_config.general.land_area_tiles/CHUNK_SIZE), player.surface)
|
||||
-- Unlock spawn control gui tab
|
||||
SetOarcGuiTabEnabled(player, OARC_SPAWN_CTRL_GUI_NAME, true)
|
||||
|
||||
@ -330,35 +352,40 @@ function SpawnOptsGuiClick(event)
|
||||
-- Create a new spawn point
|
||||
local newSpawn = {x=0,y=0}
|
||||
|
||||
local mod_overlap = global.ocfg.mod_overlap
|
||||
|
||||
-- Create a new force for player if they choose that radio button
|
||||
if global.ocfg.enable_separate_teams and joinOwnTeamRadio then
|
||||
if mod_overlap.enable_separate_teams and joinOwnTeamRadio then
|
||||
local newForce = CreatePlayerCustomForce(player)
|
||||
end
|
||||
|
||||
-- Find an unused vanilla spawn
|
||||
-- if (vanillaChoice) then
|
||||
if (global.ocfg.enable_vanilla_spawns) then
|
||||
if (elemName == "isolated_spawn_far") then
|
||||
newSpawn = FindUnusedVanillaSpawn(game.surfaces[GAME_SURFACE_NAME],
|
||||
global.ocfg.far_dist_end*CHUNK_SIZE)
|
||||
elseif (elemName == "isolated_spawn_near") then
|
||||
newSpawn = FindUnusedVanillaSpawn(game.surfaces[GAME_SURFACE_NAME],
|
||||
global.ocfg.near_dist_start*CHUNK_SIZE)
|
||||
end
|
||||
---TODO: Vanilla spawn points are not implemented yet.
|
||||
-- -- Find an unused vanilla spawn
|
||||
-- -- if (vanillaChoice) then
|
||||
-- if (global.ocfg.enable_vanilla_spawns) then
|
||||
-- if (elemName == "isolated_spawn_far") then
|
||||
-- newSpawn = FindUnusedVanillaSpawn(game.surfaces[GAME_SURFACE_NAME],
|
||||
-- global.ocfg.far_dist_end*CHUNK_SIZE)
|
||||
-- elseif (elemName == "isolated_spawn_near") then
|
||||
-- newSpawn = FindUnusedVanillaSpawn(game.surfaces[GAME_SURFACE_NAME],
|
||||
-- global.ocfg.near_dist_start*CHUNK_SIZE)
|
||||
-- end
|
||||
|
||||
-- Default OARC-type pre-set layout spawn.
|
||||
else
|
||||
|
||||
-- -- Default OARC-type pre-set layout spawn.
|
||||
-- else
|
||||
-- Find coordinates of a good place to spawn
|
||||
if (elemName == "isolated_spawn_far") then
|
||||
newSpawn = FindUngeneratedCoordinates(global.ocfg.far_dist_start,global.ocfg.far_dist_end, player.surface)
|
||||
newSpawn = FindUngeneratedCoordinates(mod_overlap.far_spawn_min_distance, mod_overlap.far_spawn_max_distance, player.surface)
|
||||
elseif (elemName == "isolated_spawn_near") then
|
||||
newSpawn = FindUngeneratedCoordinates(global.ocfg.near_dist_start,global.ocfg.near_dist_end, player.surface)
|
||||
newSpawn = FindUngeneratedCoordinates(mod_overlap.near_spawn_min_distance, mod_overlap.near_spawn_max_distance, player.surface)
|
||||
end
|
||||
end
|
||||
-- end
|
||||
|
||||
-- If that fails, find a random map edge in a rand direction.
|
||||
---TODO: Add support for multiple surfaces.
|
||||
if ((newSpawn.x == 0) and (newSpawn.y == 0)) then
|
||||
newSpawn = FindMapEdge(GetRandomVector(), player.surface)
|
||||
newSpawn = FindMapEdge(GetRandomVector(), game.surfaces[global.ocfg.gameplay.main_force_surface]) ---TODO: Add support for multiple surfaces.
|
||||
log("Resorting to find map edge! x=" .. newSpawn.x .. ",y=" .. newSpawn.y)
|
||||
end
|
||||
|
||||
@ -366,7 +393,12 @@ function SpawnOptsGuiClick(event)
|
||||
ChangePlayerSpawn(player, newSpawn)
|
||||
|
||||
-- Send the player there
|
||||
QueuePlayerForDelayedSpawn(player.name, newSpawn, moatChoice, global.ocfg.enable_vanilla_spawns)
|
||||
---TODO: Add support for multiple surfaces.
|
||||
QueuePlayerForDelayedSpawn(player.name,
|
||||
game.surfaces[global.ocfg.gameplay.main_force_surface],
|
||||
newSpawn,
|
||||
moatChoice,
|
||||
false) -- global.ocfg.enable_vanilla_spawns --TODO: Vanilla spawn points are not implemented yet.
|
||||
if (elemName == "isolated_spawn_near") then
|
||||
SendBroadcastMsg({"oarc-player-is-joining-near", player.name})
|
||||
elseif (elemName == "isolated_spawn_far") then
|
||||
@ -398,7 +430,9 @@ function SpawnOptsGuiClick(event)
|
||||
end
|
||||
|
||||
|
||||
-- Display the spawn options and explanation
|
||||
---Display the spawn options and explanation
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function DisplaySharedSpawnOptions(player)
|
||||
player.gui.screen.add{name = "shared_spawn_opts",
|
||||
type = "frame",
|
||||
@ -418,8 +452,8 @@ function DisplaySharedSpawnOptions(player)
|
||||
if (sharedSpawn.openAccess and
|
||||
(game.players[spawnName] ~= nil) and
|
||||
game.players[spawnName].connected) then
|
||||
local spotsRemaining = global.ocfg.max_players_shared_spawn - #global.ocore.sharedSpawns[spawnName].players
|
||||
if (global.ocfg.max_players_shared_spawn == 0) then
|
||||
local spotsRemaining = global.ocfg.mod_overlap.number_of_players_per_shared_spawn - #global.ocore.sharedSpawns[spawnName].players
|
||||
if (global.ocfg.mod_overlap.number_of_players_per_shared_spawn == 0) then
|
||||
shGui.add{type="button", caption=spawnName, name=spawnName}
|
||||
elseif (spotsRemaining > 0) then
|
||||
shGui.add{type="button", caption={"oarc-spawn-spots-remaining", spawnName, spotsRemaining}, name=spawnName}
|
||||
@ -438,7 +472,9 @@ function DisplaySharedSpawnOptions(player)
|
||||
style = "back_button"}
|
||||
end
|
||||
|
||||
-- Handle the gui click of the shared spawn options
|
||||
---Handle the gui click of the shared spawn options
|
||||
---@param event EventData.on_gui_click
|
||||
---@return nil
|
||||
function SharedSpwnOptsGuiClick(event)
|
||||
if not (event and event.element and event.element.valid) then return end
|
||||
local player = game.players[event.player_index]
|
||||
@ -465,15 +501,12 @@ function SharedSpwnOptsGuiClick(event)
|
||||
-- Else check for which spawn was selected
|
||||
-- If a spawn is removed during this time, the button will not do anything
|
||||
else
|
||||
for spawnName,sharedSpawn in pairs(global.ocore.sharedSpawns) do
|
||||
for spawnName,sharedSpawn in pairs(global.ocore.sharedSpawns --[[@as OarcSharedSpawnsTable]]) do
|
||||
if ((buttonClicked == spawnName) and
|
||||
(game.players[spawnName] ~= nil) and
|
||||
(game.players[spawnName].connected)) then
|
||||
|
||||
-- Add the player to that shared spawns join queue.
|
||||
if (global.ocore.sharedSpawns[spawnName].joinQueue == nil) then
|
||||
global.ocore.sharedSpawns[spawnName].joinQueue = {}
|
||||
end
|
||||
table.insert(global.ocore.sharedSpawns[spawnName].joinQueue, player.name)
|
||||
|
||||
-- Clear the shared spawn options gui.
|
||||
@ -492,6 +525,9 @@ function SharedSpwnOptsGuiClick(event)
|
||||
end
|
||||
end
|
||||
|
||||
---Display shared spawn join wait menu
|
||||
---@param player LuaPlayer
|
||||
---@return nil
|
||||
function DisplaySharedSpawnJoinWaitMenu(player)
|
||||
|
||||
local sGui = player.gui.screen.add{name = "join_shared_spawn_wait_menu",
|
||||
@ -511,7 +547,9 @@ function DisplaySharedSpawnJoinWaitMenu(player)
|
||||
style = "back_button"}
|
||||
end
|
||||
|
||||
-- Handle the gui click of the buddy wait menu
|
||||
---Handle the gui click of the shared spawn join wait menu
|
||||
---@param event EventData.on_gui_click
|
||||
---@return nil
|
||||
function SharedSpawnJoinWaitMenuClick(event)
|
||||
if not (event and event.element and event.element.valid) then return end
|
||||
local player = game.players[event.player_index]
|
||||
@ -532,16 +570,14 @@ function SharedSpawnJoinWaitMenuClick(event)
|
||||
DisplaySpawnOptions(player)
|
||||
|
||||
-- Find and remove the player from the joinQueue they were in.
|
||||
for spawnName,sharedSpawn in pairs(global.ocore.sharedSpawns) do
|
||||
if (sharedSpawn.joinQueue ~= nil) then
|
||||
for spawnName,sharedSpawn in pairs(global.ocore.sharedSpawns --[[@as OarcSharedSpawnsTable]]) do
|
||||
for index,requestingPlayer in pairs(sharedSpawn.joinQueue) do
|
||||
if (requestingPlayer == player.name) then
|
||||
global.ocore.sharedSpawns[spawnName].joinQueue[index] = false
|
||||
global.ocore.sharedSpawns[spawnName].joinQueue[index] = nil
|
||||
game.players[spawnName].print({"oarc-player-cancel-join-request", player.name})
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
log("ERROR! Failed to remove player from joinQueue!")
|
||||
@ -615,8 +651,7 @@ function CreateSpawnCtrlGuiTab(tab_container, player)
|
||||
|
||||
-- Display a list of people in the join queue for your base.
|
||||
if (global.ocfg.enable_shared_spawns and IsSharedSpawnActive(player)) then
|
||||
if ((global.ocore.sharedSpawns[player.name].joinQueue ~= nil) and
|
||||
(#global.ocore.sharedSpawns[player.name].joinQueue > 0)) then
|
||||
if (#global.ocore.sharedSpawns[player.name].joinQueue > 0) then
|
||||
|
||||
|
||||
AddLabel(spwnCtrls, "drop_down_msg_lbl1", {"oarc-select-player-join-queue"}, my_label_style)
|
||||
|
Loading…
Reference in New Issue
Block a user