mirror of
https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn.git
synced 2024-12-04 09:43:00 +02:00
Refined new spawn search logic. (Using random vector search.)
This commit is contained in:
parent
e6d64fb8d8
commit
dc27eab5e7
10
devplan.txt
10
devplan.txt
@ -22,9 +22,8 @@ BACKLOG:
|
||||
- Refresh players in admin controls when dropdown is clicked
|
||||
|
||||
- Think through player flow for Space Age for creating new unique spawns on other surfaces and respawning:
|
||||
|
||||
- Create a function to create secondary uniqueSpawns for the same player
|
||||
- Move "buddy" info to unique_spawns as well.
|
||||
|
||||
|
||||
- Default to selecting SELF in admin controls player dropdown?
|
||||
- In spawn controls, add a note if spawn is full (and maybe disable the shared spawn checkbox?)
|
||||
@ -32,7 +31,8 @@ BACKLOG:
|
||||
|
||||
- If dead when resetting spawn... possibly delay the opening of the welcome GUI or block spawning until character is spawned?
|
||||
|
||||
|
||||
- Shared electricity (proper)
|
||||
- Shared items (proper)
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@ -114,4 +114,6 @@ Other Ideas, Not Committed:
|
||||
- "uniqueSpawns" should have a "primary" flag and be indexed by surface FIRST
|
||||
- Make respawn locations first be indexed by player, then surface
|
||||
- Offline protection re-implement!
|
||||
- Resolve regrowth issue with radars and confirm that when we mark chunks for removal, they can be refreshed still. (trace logic!)
|
||||
- Resolve regrowth issue with radars and confirm that when we mark chunks for removal, they can be refreshed still. (trace logic!)
|
||||
- Move "buddy" info to unique_spawns as well.
|
||||
- Fix search vector to use more variable vector'ing, always normalize vector, and then ensure the other reliant functions work still.
|
@ -172,4 +172,15 @@ function RecreateOarcGui(player)
|
||||
end
|
||||
|
||||
InitOarcGuiTabs(player)
|
||||
end
|
||||
|
||||
|
||||
function SetNauvisChunksGenerated()
|
||||
local nauvis = game.surfaces["nauvis"]
|
||||
|
||||
for x = -100, 100, 1 do
|
||||
for y = -100, 100, 1 do
|
||||
nauvis.set_chunk_generated_status({x=x, y=y}, defines.chunk_generated_status.entities)
|
||||
end
|
||||
end
|
||||
end
|
@ -614,14 +614,18 @@ function RandomNegPos()
|
||||
end
|
||||
end
|
||||
|
||||
---Create a random direction vector to look in (values are integers between -3 and 3)
|
||||
---Create a random direction vector to look in, returns normalized vector
|
||||
---@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)
|
||||
randVec.x = math.random() * 2 - 1
|
||||
randVec.y = math.random() * 2 - 1
|
||||
end
|
||||
-- Normalize the vector
|
||||
local magnitude = math.sqrt((randVec.x^2) + (randVec.y^2))
|
||||
randVec.x = randVec.x / magnitude
|
||||
randVec.y = randVec.y / magnitude
|
||||
log("direction: x=" .. randVec.x .. ", y=" .. randVec.y)
|
||||
return randVec
|
||||
end
|
||||
@ -667,94 +671,125 @@ function ClearNearbyEnemies(pos, safeDist, surface)
|
||||
end
|
||||
end
|
||||
|
||||
---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}
|
||||
-- ---Function to find coordinates of ungenerated map area in a given direction starting from the center of the map
|
||||
-- ---@param direction_vector MapPosition
|
||||
-- ---@param surface LuaSurface
|
||||
-- ---@return MapPosition
|
||||
-- function FindMapEdge(direction_vector, surface)
|
||||
-- local position = {x=0,y=0}
|
||||
-- local chunk_position = {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(chunk_position.x) > 1000) or (math.abs(chunk_position.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(chunk_position)) then
|
||||
-- chunk_position.x = chunk_position.x + direction_vector.x
|
||||
-- chunk_position.y = chunk_position.y + direction_vector.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
|
||||
-- chunk_position.x = chunk_position.x + direction_vector.x
|
||||
-- chunk_position.y = chunk_position.y + direction_vector.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(chunk_position, 10, surface) then
|
||||
-- position.x = (chunk_position.x*CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
-- position.y = (chunk_position.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
|
||||
|
||||
|
||||
---Pick a random direction, go at least the minimum distance, and start looking for ungenerated chunks
|
||||
---We try a few times (hardcoded) and then try a different random direction if we fail (up to max_tries)
|
||||
---@param surface LuaSurface
|
||||
---@param minimum_distance number
|
||||
---@param minimum_distance_chunks number Distance in chunks to start looking for ungenerated chunks
|
||||
---@param max_tries integer Maximum number of tries to find a spawn point
|
||||
---@return MapPosition
|
||||
function FindUngeneratedCoordinates(surface, minimum_distance)
|
||||
function FindUngeneratedCoordinates(surface, minimum_distance_chunks, max_tries)
|
||||
|
||||
--- Get a random vector, figure out how many times to multiply it to get the minimum distance
|
||||
local direction_vector = GetRandomVector()
|
||||
local direction_mag = math.sqrt((direction_vector.x^2) + (direction_vector.y^2))
|
||||
local direction_multiple = math.ceil(minimum_distance / direction_mag)
|
||||
local start_distance_tiles = minimum_distance_chunks * CHUNK_SIZE
|
||||
|
||||
local final_position = {x=0,y=0}
|
||||
local tries_remaining = max_tries - 1
|
||||
|
||||
-- Starting search position
|
||||
local search_pos = {
|
||||
x=direction_vector.x * direction_multiple,
|
||||
y=direction_vector.y * direction_multiple
|
||||
x=direction_vector.x * start_distance_tiles,
|
||||
y=direction_vector.y * start_distance_tiles
|
||||
}
|
||||
|
||||
local MAX_CHUNK_DISTANCE = 1000
|
||||
-- We check up to THIS many times, each jump moves out by minimum_distance_to_existing_chunks
|
||||
local jumps_count = 3
|
||||
|
||||
local minimum_distance_to_existing_chunks = global.ocfg.gameplay.minimum_distance_to_existing_chunks
|
||||
|
||||
-- Keep checking chunks in the direction of the vector, assumes this terminates...
|
||||
while(true) do
|
||||
|
||||
-- Set some absolute limits.
|
||||
if ((math.abs(search_pos.x) > MAX_CHUNK_DISTANCE) or (math.abs(search_pos.y) > MAX_CHUNK_DISTANCE)) then
|
||||
error("FindUngeneratedCoordinates - Hit max chunk distance!")
|
||||
break
|
||||
local chunk_position = GetChunkPosFromTilePos(search_pos)
|
||||
|
||||
-- If chunk is already generated, keep looking
|
||||
elseif (surface.is_chunk_generated(search_pos)) then
|
||||
search_pos.x = search_pos.x + direction_vector.x
|
||||
search_pos.y = search_pos.y + direction_vector.y
|
||||
if (jumps_count <= 0) then
|
||||
|
||||
-- Found a possible ungenerated area
|
||||
else
|
||||
|
||||
search_pos.x = search_pos.x + direction_vector.x
|
||||
search_pos.y = search_pos.y + direction_vector.y
|
||||
|
||||
-- Check there are no generated chunks in a 10x10 area.
|
||||
if IsChunkAreaUngenerated(search_pos, 10, surface) then
|
||||
final_position.x = (search_pos.x*CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
final_position.y = (search_pos.y*CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
if (tries_remaining > 0) then
|
||||
return FindUngeneratedCoordinates(surface, minimum_distance_chunks, tries_remaining)
|
||||
else
|
||||
log("WARNING - FindUngeneratedCoordinates - Hit max distance!")
|
||||
break
|
||||
end
|
||||
|
||||
-- If chunk is already generated, keep looking further out
|
||||
elseif (surface.is_chunk_generated(chunk_position)) then
|
||||
|
||||
-- For debugging, ping the map
|
||||
-- SendBroadcastMsg("GENERATED: " .. GetGPStext(surface.name, {x=chunk_position.x*32, y=chunk_position.y*32}))
|
||||
|
||||
-- Move out a bit more to give some space and then check the surrounding area
|
||||
search_pos.x = search_pos.x + (direction_vector.x * CHUNK_SIZE * minimum_distance_to_existing_chunks)
|
||||
search_pos.y = search_pos.y + (direction_vector.y * CHUNK_SIZE * minimum_distance_to_existing_chunks)
|
||||
|
||||
-- Found a possible ungenerated area
|
||||
elseif IsChunkAreaUngenerated(chunk_position, minimum_distance_to_existing_chunks, surface) then
|
||||
|
||||
-- For debugging, ping the map
|
||||
-- SendBroadcastMsg("SUCCESS: " .. GetGPStext(surface.name, {x=chunk_position.x*32, y=chunk_position.y*32}))
|
||||
|
||||
-- Place the spawn in the center of a chunk
|
||||
final_position.x = (chunk_position.x * CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
final_position.y = (chunk_position.y * CHUNK_SIZE) + (CHUNK_SIZE/2)
|
||||
break
|
||||
|
||||
-- The area around the chunk is not clear, keep looking
|
||||
else
|
||||
|
||||
-- For debugging, ping the map
|
||||
-- SendBroadcastMsg("NOT CLEAR: " .. GetGPStext(surface.name, {x=chunk_position.x*32, y=chunk_position.y*32}))
|
||||
|
||||
-- Move out a bit more to give some space and then check the surrounding area
|
||||
search_pos.x = search_pos.x + (direction_vector.x * CHUNK_SIZE * minimum_distance_to_existing_chunks)
|
||||
search_pos.y = search_pos.y + (direction_vector.y * CHUNK_SIZE * minimum_distance_to_existing_chunks)
|
||||
end
|
||||
|
||||
jumps_count = jumps_count - 1
|
||||
end
|
||||
|
||||
if (final_position.x == 0 and final_position.y == 0) then
|
||||
log("WARNING! FindUngeneratedCoordinates - Failed to find a spawn point!")
|
||||
end
|
||||
|
||||
-- log("spawn: x=" .. position.x .. ", y=" .. position.y)
|
||||
return final_position
|
||||
end
|
||||
|
||||
|
@ -1255,9 +1255,9 @@ function CreatePlayerCustomForce(player)
|
||||
player.force = newForce
|
||||
|
||||
if (newForce.name == player.name) then
|
||||
SendBroadcastMsg(player.name .. " has started their own team!") -- TODO: Localize
|
||||
SendBroadcastMsg({ "oarc-player-started-own-team", player.name })
|
||||
else
|
||||
player.print("Sorry, no new teams can be created. You were assigned to the default team instead.") -- TODO: Localize
|
||||
player.print({ "oarc-player-no-new-teams-sorry" })
|
||||
end
|
||||
|
||||
return newForce
|
||||
|
@ -858,28 +858,26 @@ function PrimarySpawnRequest(player)
|
||||
-- Cache some useful variables
|
||||
local surface = game.surfaces[spawn_choices.surface]
|
||||
|
||||
-- Find coordinates of a good place to spawn
|
||||
local spawn_position = FindUngeneratedCoordinates(surface, spawn_choices.distance, 3)
|
||||
|
||||
-- If that fails, just throw a warning and don't spawn them. They can try again.
|
||||
if ((spawn_position.x == 0) and (spawn_position.y == 0)) then
|
||||
player.print({ "oarc-no-ungenerated-land-error" })
|
||||
return
|
||||
end
|
||||
|
||||
-- Create a new force for player if they choose that radio button
|
||||
if spawn_choices.team ~= SPAWN_TEAM_CHOICE.join_main_team then
|
||||
CreatePlayerCustomForce(player)
|
||||
end
|
||||
|
||||
-- Find coordinates of a good place to spawn
|
||||
local newSpawn = { x = 0, y = 0 }
|
||||
-- TODO: Rewrite this function to make use of spawnChoices.distance!!
|
||||
newSpawn = FindUngeneratedCoordinates(surface, spawn_choices.distance)
|
||||
|
||||
-- If that fails, find a random map edge in a rand direction.
|
||||
if ((newSpawn.x == 0) and (newSpawn.y == 0)) then
|
||||
newSpawn = FindMapEdge(GetRandomVector(), surface)
|
||||
log("Resorting to find map edge! x=" .. newSpawn.x .. ",y=" .. newSpawn.y)
|
||||
end
|
||||
|
||||
-- Create that player's spawn in the global vars
|
||||
ChangePlayerRespawn(player.name, spawn_choices.surface, newSpawn)
|
||||
ChangePlayerRespawn(player.name, spawn_choices.surface, spawn_position)
|
||||
|
||||
-- Send the player there
|
||||
QueuePlayerForDelayedSpawn(player.name, spawn_choices.surface, newSpawn, spawn_choices.moat, true, nil)
|
||||
SendBroadcastMsg({ "oarc-player-is-joining-far", player.name, spawn_choices.surface })
|
||||
QueuePlayerForDelayedSpawn(player.name, spawn_choices.surface, spawn_position, spawn_choices.moat, true, nil)
|
||||
SendBroadcastMsg({ "oarc-player-is-joining", player.name, spawn_choices.surface })
|
||||
|
||||
-- Unlock spawn control gui tab
|
||||
SetOarcGuiTabEnabled(player, OARC_SPAWN_CTRL_TAB_NAME, true)
|
||||
@ -1101,16 +1099,16 @@ function AcceptBuddyRequest(player, requesting_buddy_name)
|
||||
---@type OarcSpawnChoices
|
||||
local spawn_choices = global.spawn_choices[requesting_buddy_name]
|
||||
local requesting_buddy = game.players[requesting_buddy_name]
|
||||
local surface = game.surfaces[spawn_choices.surface]
|
||||
|
||||
if (requesting_buddy.gui.screen.buddy_wait_menu ~= nil) then
|
||||
requesting_buddy.gui.screen.buddy_wait_menu.destroy()
|
||||
end
|
||||
if (player.gui.screen.buddy_request_menu ~= nil) then
|
||||
player.gui.screen.buddy_request_menu.destroy()
|
||||
end
|
||||
-- Find coordinates of a good place to spawn
|
||||
local spawn_position = FindUngeneratedCoordinates(surface, spawn_choices.distance, 3)
|
||||
|
||||
-- Create a new spawn point
|
||||
local newSpawn = { x = 0, y = 0 }
|
||||
-- If that fails, just throw a warning and don't spawn them. They can try again.
|
||||
if ((spawn_position.x == 0) and (spawn_position.y == 0)) then
|
||||
player.print({ "oarc-no-ungenerated-land-error" })
|
||||
return
|
||||
end
|
||||
|
||||
-- Create a new force for the combined players if they chose that option
|
||||
if spawn_choices.buddy_team then
|
||||
@ -1122,15 +1120,12 @@ function AcceptBuddyRequest(player, requesting_buddy_name)
|
||||
CreatePlayerCustomForce(requesting_buddy)
|
||||
end
|
||||
|
||||
local surface = game.surfaces[spawn_choices.surface]
|
||||
|
||||
-- Find coordinates of a good place to spawn
|
||||
newSpawn = FindUngeneratedCoordinates(surface, spawn_choices.distance)
|
||||
|
||||
-- If that fails, find a random map edge in a rand direction.
|
||||
if ((newSpawn.x == 0) and (newSpawn.x == 0)) then
|
||||
newSpawn = FindMapEdge(GetRandomVector(), surface)
|
||||
log("Resorting to find map edge! x=" .. newSpawn.x .. ",y=" .. newSpawn.y)
|
||||
-- Destroy GUIs
|
||||
if (requesting_buddy.gui.screen.buddy_wait_menu ~= nil) then
|
||||
requesting_buddy.gui.screen.buddy_wait_menu.destroy()
|
||||
end
|
||||
if (player.gui.screen.buddy_request_menu ~= nil) then
|
||||
player.gui.screen.buddy_request_menu.destroy()
|
||||
end
|
||||
|
||||
-- Create that spawn in the global vars
|
||||
@ -1140,12 +1135,12 @@ function AcceptBuddyRequest(player, requesting_buddy_name)
|
||||
if (spawn_choices.moat) then
|
||||
x_offset = x_offset + 10
|
||||
end
|
||||
buddySpawn = { x = newSpawn.x + x_offset, y = newSpawn.y }
|
||||
ChangePlayerRespawn(player.name, spawn_choices.surface, newSpawn)
|
||||
buddySpawn = { x = spawn_position.x + x_offset, y = spawn_position.y }
|
||||
ChangePlayerRespawn(player.name, spawn_choices.surface, spawn_position)
|
||||
ChangePlayerRespawn(requesting_buddy_name, spawn_choices.surface, buddySpawn)
|
||||
|
||||
-- Send the player there
|
||||
QueuePlayerForDelayedSpawn(player.name, spawn_choices.surface, newSpawn, spawn_choices.moat, true, requesting_buddy_name)
|
||||
QueuePlayerForDelayedSpawn(player.name, spawn_choices.surface, spawn_position, spawn_choices.moat, true, requesting_buddy_name)
|
||||
QueuePlayerForDelayedSpawn(requesting_buddy_name, spawn_choices.surface, buddySpawn, spawn_choices.moat, true, player.name)
|
||||
SendBroadcastMsg(requesting_buddy_name .. " and " .. player.name .. " are joining the game together!")
|
||||
|
||||
|
@ -61,9 +61,10 @@ oarc-buddy-spawn-info=The buddy system requires 2 players in this menu at the sa
|
||||
oarc-max-players-shared-spawn=You can allow up to __1__ other players to join you.
|
||||
oarc-spawn-dist-notes=Spawn distance is in CHUNKS from the center of the map.\nExpect a fight to reach other players.
|
||||
|
||||
oarc-player-is-joining-main-force=__1__ is joining the main force on __2__!
|
||||
oarc-player-is-joining-near=__1__ is joining the game from a distance on __2__!
|
||||
oarc-player-is-joining-far=__1__ is joining the game from a great distance on __2__!
|
||||
oarc-player-is-joining=__1__ is joining the game on __2__!
|
||||
oarc-player-started-own-team=__1__ has started their own team!
|
||||
oarc-player-no-new-teams-sorry=Sorry, no new teams can be created. You were assigned to the default team instead.
|
||||
oarc-no-ungenerated-land-error=[color=red]Failed to find ungenerated land to spawn on! Please try again. If you see this message multiple times, it could be a settings or mod conflict issue.[/color]
|
||||
|
||||
oarc-please-wait=PLEASE WAIT WHILE YOUR SPAWN POINT IS GENERATED!
|
||||
oarc-player-left-early=Player (__1__) left the game before __2__ minutes of play time. Their spawn area has been marked for cleanup.
|
||||
|
Loading…
Reference in New Issue
Block a user