mirror of
https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn.git
synced 2024-12-14 10:23:17 +02:00
407 lines
15 KiB
Lua
407 lines
15 KiB
Lua
-- separate_spawns.lua
|
|
-- Nov 2016
|
|
--
|
|
-- Code that handles everything regarding giving each player a separate spawn
|
|
-- Includes the GUI stuff
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- EVENT RELATED FUNCTIONS
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- When a new player is created, present the spawn options
|
|
-- Assign them to the main force so they can communicate with the team
|
|
-- without shouting.
|
|
function SeparateSpawnsPlayerCreated(event)
|
|
local player = game.players[event.player_index]
|
|
player.force = MAIN_FORCE
|
|
DisplayWelcomeTextGui(player)
|
|
end
|
|
|
|
|
|
-- Check if the player has a different spawn point than the default one
|
|
-- Make sure to give the default starting items
|
|
function SeparateSpawnsPlayerRespawned(event)
|
|
local player = game.players[event.player_index]
|
|
SendPlayerToSpawn(player)
|
|
end
|
|
|
|
|
|
-- This is the main function that creates the spawn area
|
|
-- Provides resources, land and a safe zone
|
|
function SeparateSpawnsGenerateChunk(event)
|
|
local surface = event.surface
|
|
local chunkArea = event.area
|
|
|
|
-- This handles chunk generation near player spawns
|
|
-- If it is near a player spawn, it does a few things like make the area
|
|
-- safe and provide a guaranteed area of land and water tiles.
|
|
SetupAndClearSpawnAreas(surface, chunkArea, global.uniqueSpawns)
|
|
end
|
|
|
|
|
|
-- Call this if a player leaves the game
|
|
-- Still seems to have a bug.
|
|
function FindUnusedSpawns(event)
|
|
local player = game.players[event.player_index]
|
|
if (player.online_time < MIN_ONLINE_TIME) then
|
|
|
|
DropGravestoneChests(player)
|
|
|
|
-- Clear out global variables for that player
|
|
if (global.playerSpawns[player.name] ~= nil) then
|
|
global.playerSpawns[player.name] = nil
|
|
end
|
|
|
|
-- Transfer or remove a shared spawn if player is owner
|
|
if (global.sharedSpawns[player.name] ~= nil) then
|
|
|
|
local teamMates = global.sharedSpawns[player.name].players
|
|
|
|
if (#teamMates >= 1) then
|
|
local newOwnerName = table.remove(teamMates)
|
|
TransferOwnershipOfSharedSpawn(player.name, newOwnerName)
|
|
else
|
|
global.sharedSpawns[player.name] = nil
|
|
end
|
|
end
|
|
|
|
-- If a uniqueSpawn was created for the player, mark it as unused.
|
|
if (global.uniqueSpawns[player.name] ~= nil) then
|
|
|
|
local spawnPos = global.uniqueSpawns[player.name].pos
|
|
|
|
-- Check if it was near someone else's base.
|
|
nearOtherSpawn = false
|
|
for _,otherSpawnPos in pairs(global.uniqueSpawns) do
|
|
if (getDistance(spawnPos, otherSpawnPos) < (CHUNK_SIZE*10)) then
|
|
nearOtherSpawn = true
|
|
end
|
|
end
|
|
|
|
if (ENABLE_ABANDONED_BASE_REMOVAL and not nearOtherSpawn) then
|
|
global.uniqueSpawns[player.name] = nil
|
|
|
|
SendBroadcastMsg(player.name .. "'s base was marked for immediate clean up because they left within "..MIN_ONLINE_TIME_IN_MINUTES.." minutes of joining.")
|
|
OarcRegrowthMarkForRemoval(spawnPos, 10)
|
|
global.chunk_regrow.force_removal_flag = game.tick
|
|
else
|
|
table.insert(global.unusedSpawns, global.uniqueSpawns[player.name])
|
|
global.uniqueSpawns[player.name] = nil
|
|
SendBroadcastMsg(player.name .. " base was freed up because they left within "..MIN_ONLINE_TIME_IN_MINUTES.." minutes of joining.")
|
|
end
|
|
end
|
|
|
|
-- remove that player's cooldown setting
|
|
if (global.playerCooldowns[player.name] ~= nil) then
|
|
global.playerCooldowns[player.name] = nil
|
|
end
|
|
|
|
-- Remove from shared spawn player slots (need to search all)
|
|
for _,sharedSpawn in pairs(global.sharedSpawns) do
|
|
for key,playerName in pairs(sharedSpawn.players) do
|
|
if (player.name == playerName) then
|
|
sharedSpawn.players[key] = nil;
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Remove a force if this player created it and they are the only one on it
|
|
if ((#player.force.players <= 1) and (player.force.name ~= MAIN_FORCE)) then
|
|
game.merge_forces(player.force, MAIN_FORCE)
|
|
end
|
|
|
|
-- Remove the character completely
|
|
game.remove_offline_players({player})
|
|
end
|
|
end
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- NON-EVENT RELATED FUNCTIONS
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- Add a spawn to the shared spawn global
|
|
-- Used for tracking which players are assigned to it, where it is and if
|
|
-- it is open for new players to join
|
|
function CreateNewSharedSpawn(player)
|
|
global.sharedSpawns[player.name] = {openAccess=true,
|
|
position=global.playerSpawns[player.name],
|
|
players={}}
|
|
end
|
|
|
|
function TransferOwnershipOfSharedSpawn(prevOwnerName, newOwnerName)
|
|
-- Transfer the shared spawn global
|
|
global.sharedSpawns[newOwnerName] = global.sharedSpawns[prevOwnerName]
|
|
global.sharedSpawns[newOwnerName].openAccess = false
|
|
global.sharedSpawns[prevOwnerName] = nil
|
|
|
|
-- Transfer the unique spawn global
|
|
global.uniqueSpawns[newOwnerName] = global.uniqueSpawns[prevOwnerName]
|
|
global.uniqueSpawns[prevOwnerName] = nil
|
|
|
|
game.players[newOwnerName].print("You have been given ownership of this base!")
|
|
end
|
|
|
|
-- Returns the number of players currently online at the shared spawn
|
|
function GetOnlinePlayersAtSharedSpawn(ownerName)
|
|
if (global.sharedSpawns[ownerName] ~= nil) then
|
|
|
|
-- Does not count base owner
|
|
local count = 0
|
|
|
|
-- For each player in the shared spawn, check if online and add to count.
|
|
for _,player in pairs(game.connected_players) do
|
|
if (ownerName == player.name) then
|
|
count = count + 1
|
|
end
|
|
|
|
for _,playerName in pairs(global.sharedSpawns[ownerName].players) do
|
|
|
|
if (playerName == player.name) then
|
|
count = count + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
return count
|
|
else
|
|
return 0
|
|
end
|
|
end
|
|
|
|
-- 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.
|
|
function GetNumberOfAvailableSharedSpawns()
|
|
local count = 0
|
|
|
|
for ownerName,sharedSpawn in pairs(global.sharedSpawns) do
|
|
if (sharedSpawn.openAccess) then
|
|
if (GetOnlinePlayersAtSharedSpawn(ownerName) < MAX_ONLINE_PLAYERS_AT_SHARED_SPAWN) then
|
|
count = count+1
|
|
end
|
|
end
|
|
end
|
|
|
|
return count
|
|
end
|
|
|
|
|
|
-- Initializes the globals used to track the special spawn and player
|
|
-- status information
|
|
function InitSpawnGlobalsAndForces()
|
|
-- Containes an array of all player spawns
|
|
-- A secondary array tracks whether the character will respawn there.
|
|
if (global.playerSpawns == nil) then
|
|
global.playerSpawns = {}
|
|
end
|
|
if (global.uniqueSpawns == nil) then
|
|
global.uniqueSpawns = {}
|
|
end
|
|
if (global.sharedSpawns == nil) then
|
|
global.sharedSpawns = {}
|
|
end
|
|
if (global.unusedSpawns == nil) then
|
|
global.unusedSpawns = {}
|
|
end
|
|
if (global.playerCooldowns == nil) then
|
|
global.playerCooldowns = {}
|
|
end
|
|
if (global.waitingBuddies == nil) then
|
|
global.waitingBuddies = {}
|
|
end
|
|
if (global.delayedSpawns == nil) then
|
|
global.delayedSpawns = {}
|
|
end
|
|
if (global.buddySpawnOptions == nil) then
|
|
global.buddySpawnOptions = {}
|
|
end
|
|
|
|
game.create_force(MAIN_FORCE)
|
|
game.forces[MAIN_FORCE].set_spawn_position(game.forces["player"].get_spawn_position(GAME_SURFACE_NAME), GAME_SURFACE_NAME)
|
|
|
|
if ENABLE_SHARED_TEAM_VISION then
|
|
game.forces[MAIN_FORCE].share_chart = true
|
|
end
|
|
|
|
SetCeaseFireBetweenAllForces()
|
|
SetFriendlyBetweenAllForces()
|
|
-- AntiGriefing(game.forces[MAIN_FORCE])
|
|
end
|
|
|
|
|
|
function DoesPlayerHaveCustomSpawn(player)
|
|
for name,spawnPos in pairs(global.playerSpawns) do
|
|
if (player.name == name) then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function ChangePlayerSpawn(player, pos)
|
|
global.playerSpawns[player.name] = pos
|
|
global.playerCooldowns[player.name] = {setRespawn=game.tick}
|
|
end
|
|
|
|
function QueuePlayerForDelayedSpawn(player, spawn, moatEnabled)
|
|
|
|
-- If we get a valid spawn point, setup the area
|
|
if ((spawn.x ~= 0) and (spawn.y ~= 0)) then
|
|
global.uniqueSpawns[player.name] = {pos=spawn,moat=moatEnabled}
|
|
|
|
player.print("Generating your spawn now, please wait 10 seconds...")
|
|
player.surface.request_to_generate_chunks(spawn, 4)
|
|
delayedTick = game.tick + 10*TICKS_PER_SECOND
|
|
table.insert(global.delayedSpawns, {player=player, spawn=spawn, moatEnabled=moatEnabled, delayedTick=delayedTick})
|
|
|
|
else
|
|
DebugPrint("THIS SHOULD NOT EVER HAPPEN! Spawn failed!")
|
|
SendBroadcastMsg("ERROR!! Failed to create spawn point for: " .. player.name)
|
|
end
|
|
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.
|
|
function DelayedSpawnOnTick()
|
|
if ((game.tick % (30)) == 1) then
|
|
if ((global.delayedSpawns ~= nil) and (#global.delayedSpawns > 0)) then
|
|
for i=#global.delayedSpawns,1,-1 do
|
|
delayedSpawn = global.delayedSpawns[i]
|
|
|
|
if (delayedSpawn.delayedTick < game.tick) then
|
|
SendPlayerToNewSpawnAndCreateIt(delayedSpawn.player, delayedSpawn.spawn, delayedSpawn.moatEnabled)
|
|
table.remove(global.delayedSpawns, i)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function SendPlayerToNewSpawnAndCreateIt(player, spawn, moatEnabled)
|
|
|
|
-- Make sure the area is super safe.
|
|
ClearNearbyEnemies(spawn, SAFE_AREA_TILE_DIST, game.surfaces[GAME_SURFACE_NAME])
|
|
|
|
-- Create the spawn resources here
|
|
CreateWaterStrip(game.surfaces[GAME_SURFACE_NAME],
|
|
{x=spawn.x+WATER_SPAWN_OFFSET_X, y=spawn.y+WATER_SPAWN_OFFSET_Y},
|
|
WATER_SPAWN_LENGTH)
|
|
CreateWaterStrip(game.surfaces[GAME_SURFACE_NAME],
|
|
{x=spawn.x+WATER_SPAWN_OFFSET_X, y=spawn.y+WATER_SPAWN_OFFSET_Y+1},
|
|
WATER_SPAWN_LENGTH)
|
|
GenerateStartingResources(surface, spawn)
|
|
|
|
-- Send the player to that position
|
|
player.teleport(spawn, GAME_SURFACE_NAME)
|
|
GivePlayerStarterItems(player)
|
|
end
|
|
|
|
function SendPlayerToSpawn(player)
|
|
if (DoesPlayerHaveCustomSpawn(player)) then
|
|
player.teleport(global.playerSpawns[player.name], GAME_SURFACE_NAME)
|
|
else
|
|
player.teleport(game.forces[MAIN_FORCE].get_spawn_position(GAME_SURFACE_NAME), GAME_SURFACE_NAME)
|
|
end
|
|
end
|
|
|
|
function SendPlayerToRandomSpawn(player)
|
|
local numSpawns = TableLength(global.uniqueSpawns)
|
|
local rndSpawn = math.random(0,numSpawns)
|
|
local counter = 0
|
|
|
|
if (rndSpawn == 0) then
|
|
player.teleport(game.forces[MAIN_FORCE].get_spawn_position(GAME_SURFACE_NAME), GAME_SURFACE_NAME)
|
|
else
|
|
counter = counter + 1
|
|
for name,spawn in pairs(global.uniqueSpawns) do
|
|
if (counter == rndSpawn) then
|
|
player.teleport(spawn.pos)
|
|
break
|
|
end
|
|
counter = counter + 1
|
|
end
|
|
end
|
|
end
|
|
|
|
function CreatePlayerCustomForce(player)
|
|
local newForce = nil
|
|
|
|
-- Check if force already exists
|
|
if (game.forces[player.name] ~= nil) then
|
|
DebugPrint("Force already exists!")
|
|
player.force = game.forces[player.name]
|
|
return game.forces[player.name]
|
|
|
|
-- Create a new force using the player's name
|
|
elseif (TableLength(game.forces) < MAX_FORCES) then
|
|
newForce = game.create_force(player.name)
|
|
if ENABLE_SHARED_TEAM_VISION then
|
|
newForce.share_chart = true
|
|
end
|
|
player.force = newForce
|
|
SetCeaseFireBetweenAllForces()
|
|
SetFriendlyBetweenAllForces()
|
|
SendBroadcastMsg(player.name.." has started their own team!")
|
|
else
|
|
player.force = MAIN_FORCE
|
|
player.print("Sorry, no new teams can be created. You were assigned to the default team instead.")
|
|
end
|
|
|
|
return newForce
|
|
end
|
|
|
|
-- For each force, if it's a valid force, chart the chunk that all active players
|
|
-- are in.
|
|
-- I have no idea how compute intensive this function is. If it starts to lag the game
|
|
-- we'll have to figure out how to change it.
|
|
-- function ShareVisionBetweenPlayers()
|
|
|
|
-- if ((game.tick % 10) == 0) then
|
|
|
|
-- for _,force in pairs(game.forces) do
|
|
-- if (force ~= nil) then
|
|
-- if ((force.name ~= enemy) and
|
|
-- (force.name ~= neutral) and
|
|
-- (force.name ~= player)) then
|
|
|
|
-- for _,player in pairs(game.connected_players) do
|
|
-- force.chart(GAME_SURFACE_NAME,
|
|
-- {{player.position.x-(2*CHUNK_SIZE),
|
|
-- player.position.y-(2*CHUNK_SIZE)},
|
|
-- {player.position.x+(2*CHUNK_SIZE),
|
|
-- player.position.y+(2*CHUNK_SIZE)}})
|
|
-- end
|
|
-- end
|
|
-- end
|
|
-- end
|
|
-- end
|
|
-- end
|
|
|
|
|
|
-- For each force, if it's a valid force, chart the chunk that was just scanned
|
|
-- for all forces.
|
|
-- I have no idea how compute intensive this function is. If it starts to lag the game
|
|
-- we'll have to figure out how to change it.
|
|
-- function ShareRadarBetweenForces(event)
|
|
|
|
-- for _,force in pairs(game.forces) do
|
|
-- if (force ~= nil) then
|
|
-- if ((force.name ~= enemy) and
|
|
-- (force.name ~= neutral) and
|
|
-- (force.name ~= player)) then
|
|
|
|
-- for _,player in pairs(game.connected_players) do
|
|
-- force.chart(GAME_SURFACE_NAME,
|
|
-- {{event.chunk_position.x*CHUNK_SIZE,
|
|
-- event.chunk_position.y*CHUNK_SIZE},
|
|
-- {event.chunk_position.x*CHUNK_SIZE,
|
|
-- event.chunk_position.y*CHUNK_SIZE}})
|
|
-- end
|
|
-- end
|
|
-- end
|
|
-- end
|
|
-- end |