2024-08-10 19:26:52 +02:00
-- Code that handles everything regarding giving each player a separate spawn
local util = require ( " util " )
local crash_site = require ( " crash-site " )
--[[
___ _ _ ___ _____
| _ _ || \ | || _ _ || _ _ |
| | | . ` | | | | |
| ___ || _ | \ _ || ___ | | _ |
--]]
2024-09-14 19:59:28 +02:00
-- Hardcoded force names for special cases.
ABANDONED_FORCE_NAME = " _ABANDONED_ "
2024-09-23 04:18:22 +02:00
-- DESTROYED_FORCE_NAME = "_DESTROYED_"
2024-09-14 19:59:28 +02:00
2024-08-10 19:26:52 +02:00
---Initializes the globals used to track the special spawn and player status information.
---@return nil
function InitSpawnGlobalsAndForces ( )
2024-08-12 04:16:48 +02:00
-- Contains a table of entries for each surface. This tracks which surfaces allow spawning?
2024-09-14 19:59:28 +02:00
--[[@type table<string, boolean>]]
global.oarc_surfaces = { }
for _ , surface in pairs ( game.surfaces ) do
SeparateSpawnsInitSurface ( surface.name )
2024-08-12 04:16:48 +02:00
end
2024-08-11 01:29:50 +02:00
2024-09-14 19:59:28 +02:00
-- This contains each player's respawn point. Literally where they will respawn on death
-- There is a way in game to change this under one of the little menu features I added. This allows players to
-- change their respawn point to something other than their home base.
-- TODO: Space Age will potentially affect this, as I may need to allow for multiple respawn points on different surfaces.
--[[@type OarcPlayerRespawnsTable]]
global.player_respawns = { }
2024-08-10 19:26:52 +02:00
-- This is the most important table. It is a list of all the unique spawn points.
2024-09-14 19:59:28 +02:00
-- This is what chunk generation checks against, and is used for shared spawn tracking, and more.
---@type OarcUniqueSpawnsTable
global.unique_spawns = { }
2024-08-10 19:26:52 +02:00
-- Each player has an option to change their respawn which has a cooldown when used.
-- Other similar abilities/functions that require cooldowns could be added here.
2024-09-14 19:59:28 +02:00
--[[@type OarcPlayerCooldownsTable]]
global.player_cooldowns = { }
2024-08-10 19:26:52 +02:00
-- Players who have made a spawn choice get put into this list while waiting.
-- An on_tick event checks when it expires and then places down the base resources, and teleports the player.
-- Go look at DelayedSpawnOnTick() for more info.
2024-09-14 19:59:28 +02:00
--[[@type OarcDelayedSpawnsTable]]
global.delayed_spawns = { }
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
-- This stores the spawn choices that a player makes from the GUI interactions.
-- Intended to be re-used for secondary spawns! (TODO SPACE AGE)
--[[@type OarcSpawnChoicesTable]]
global.spawn_choices = { }
2024-08-28 07:36:02 +02:00
2024-08-10 19:26:52 +02:00
-- Buddy info: The only real use is to check if one of a buddy pair is online to see if we should allow enemy
2024-09-14 19:59:28 +02:00
-- attacks on the base. <br>
-- global.buddy_pairs[player.name] = requesterName <br>
-- global.buddy_pairs[requesterName] = player.name <br>
--[[@type table<string, string>]]
global.buddy_pairs = { }
2024-08-10 19:26:52 +02:00
--- Table contains all the renders that need to be faded out over time in the on_tick event. They are removed when they expire.
2024-09-14 19:59:28 +02:00
--[[@type table<integer>]]
global.oarc_renders_fadeout = { }
-- Special forces for when players with their own force want a reset.
game.create_force ( ABANDONED_FORCE_NAME )
2024-09-23 04:18:22 +02:00
-- game.create_force(DESTROYED_FORCE_NAME)
2024-08-10 19:26:52 +02:00
2024-09-17 20:19:41 +02:00
-- Special enemy forces for scaling down enemies near player bases.
CreateEnemyForces ( )
2024-08-10 19:26:52 +02:00
-- Name a new force to be the default force.
-- This is what any new player is assigned to when they join, even before they spawn.
2024-09-14 19:59:28 +02:00
local main_force = CreatePlayerForce ( global.ocfg . gameplay.main_force_name )
2024-08-16 07:40:16 +02:00
main_force.set_spawn_position ( { x = 0 , y = 0 } , global.ocfg . gameplay.default_surface )
2024-08-10 19:26:52 +02:00
2024-09-06 03:15:56 +02:00
CreateHoldingPenPermissionsGroup ( )
end
function CreateHoldingPenPermissionsGroup ( )
-- Create a permission group for the holding pen players.
if ( game.permissions . get_group ( " holding_pen " ) == nil ) then
game.permissions . create_group ( " holding_pen " )
end
local holding_pen_group = game.permissions . get_group ( " holding_pen " )
-- Disable all permissions for the holding pen group.
for _ , action in pairs ( defines.input_action ) do
holding_pen_group.set_allows_action ( action , false )
end
-- Just allow the ones we want:
holding_pen_group.set_allows_action ( defines.input_action . gui_checked_state_changed , true )
holding_pen_group.set_allows_action ( defines.input_action . gui_click , true )
holding_pen_group.set_allows_action ( defines.input_action . gui_confirmed , true )
holding_pen_group.set_allows_action ( defines.input_action . gui_elem_changed , true )
holding_pen_group.set_allows_action ( defines.input_action . gui_hover , true )
holding_pen_group.set_allows_action ( defines.input_action . gui_leave , true )
holding_pen_group.set_allows_action ( defines.input_action . gui_location_changed , true )
holding_pen_group.set_allows_action ( defines.input_action . gui_selected_tab_changed , true )
holding_pen_group.set_allows_action ( defines.input_action . gui_selection_state_changed , true )
holding_pen_group.set_allows_action ( defines.input_action . gui_switch_state_changed , true )
holding_pen_group.set_allows_action ( defines.input_action . gui_text_changed , true )
holding_pen_group.set_allows_action ( defines.input_action . gui_value_changed , true )
holding_pen_group.set_allows_action ( defines.input_action . start_walking , true )
2024-09-26 02:54:43 +02:00
holding_pen_group.set_allows_action ( defines.input_action . write_to_console , true )
2024-09-06 03:15:56 +02:00
2024-08-10 19:26:52 +02:00
end
2024-09-14 19:59:28 +02:00
---Detects when new surfaces are created and inits them. Does not trigger during on_init?
2024-08-12 04:16:48 +02:00
---@param event EventData.on_surface_created
---@return nil
function SeparateSpawnsSurfaceCreated ( event )
2024-09-14 19:59:28 +02:00
local surface_name = game.surfaces [ event.surface_index ] . name
log ( " SeparateSpawnsSurfaceCreated - " .. surface_name )
SeparateSpawnsInitSurface ( surface_name )
end
2024-08-12 04:16:48 +02:00
2024-09-14 19:59:28 +02:00
---Init globals for a new surface and set the default allow spawn value based on settings.
---@param surface_name string
---@return nil
function SeparateSpawnsInitSurface ( surface_name )
2024-08-12 04:16:48 +02:00
-- Shouldn't happen because surface created isn't triggered during on_init.
2024-09-14 19:59:28 +02:00
if ( global.oarc_surfaces == nil ) then
error ( " global.oarc_surfaces not initialized yet?! " .. surface_name )
2024-08-12 04:16:48 +02:00
end
2024-09-14 19:59:28 +02:00
if IsSurfaceBlacklisted ( surface_name ) then return end
2024-08-12 04:16:48 +02:00
-- Add the surface to the list of surfaces that allow spawns with value from config.
2024-09-12 03:20:00 +02:00
if global.ocfg . gameplay.default_allow_spawning_on_other_surfaces then
2024-09-14 19:59:28 +02:00
global.oarc_surfaces [ surface_name ] = true
-- Otherwise only allow the default surface (by default)
2024-09-12 03:20:00 +02:00
else
2024-09-14 19:59:28 +02:00
global.oarc_surfaces [ surface_name ] = ( surface_name == global.ocfg . gameplay.default_surface )
2024-09-12 03:20:00 +02:00
end
2024-09-10 17:40:19 +02:00
2024-10-06 21:28:16 +02:00
-- Make sure it has a surface configuration entry!
if ( global.ocfg . surfaces_config [ surface_name ] == nil ) then
2024-09-14 19:59:28 +02:00
log ( " Surface does NOT have a config entry, defaulting to nauvis entry for new surface: " .. surface_name )
2024-10-06 21:28:16 +02:00
global.ocfg . surfaces_config [ surface_name ] = table.deepcopy ( global.ocfg . surfaces_config [ " nauvis " ] )
2024-09-10 17:40:19 +02:00
end
2024-08-12 04:16:48 +02:00
end
---Detects when surfaces are deleted and removes them from the list of surfaces that allow spawns.
2024-08-21 20:03:48 +02:00
---@param event EventData.on_pre_surface_deleted
2024-08-12 04:16:48 +02:00
---@return nil
function SeparateSpawnsSurfaceDeleted ( event )
2024-09-14 19:59:28 +02:00
local surface_name = game.surfaces [ event.surface_index ] . name
2024-08-12 04:16:48 +02:00
2024-09-14 19:59:28 +02:00
-- Remove the surface from the list of surfaces that allow spawns
2024-09-23 04:18:22 +02:00
if global.oarc_surfaces [ surface_name ] ~= nil then
log ( " WARNING!! - Surface deleted event not validated/implemented yet! " .. surface_name )
global.oarc_surfaces [ surface_name ] = nil
-- TODO: Validate if we need to do other cleanup too, like unique spawns, etc. I can't
-- think of a reason why we would need to do that yet.
end
2024-08-12 04:16:48 +02:00
end
2024-08-10 19:26:52 +02:00
--[[
___ _ _ __ __ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___
| _ \ | | / _ \ \ \ / /| __ || _ \ / __ || _ \ | __ |/ __ || _ _ || __ || _ _ |/ __ |
| _ /| | __ / _ \ \ V / | _ | | / \ __ \ | _ /| _ || ( __ | | | _ | | || ( __
| _ | | ____ |/ _ / \ _ \ | _ | | ___ || _ | _ \ | ___ /| _ | | ___ | \ ___ || ___ || _ | | ___ | \ ___ |
--]]
2024-08-12 04:16:48 +02:00
-- When a player is newly created or just reset, present the spawn options to them.
-- If new player, assign them to the main force so they can communicate with the team without shouting (/s).
2024-08-23 20:16:11 +02:00
---@param player_index integer|string
2024-08-10 19:26:52 +02:00
---@return nil
2024-09-06 03:15:56 +02:00
function SeparateSpawnsInitPlayer ( player_index )
2024-08-10 19:26:52 +02:00
local player = game.players [ player_index ]
2024-09-06 03:15:56 +02:00
SafeTeleport ( player , game.surfaces [ HOLDING_PEN_SURFACE_NAME ] , { x = 0 , y = 0 } )
2024-09-14 19:59:28 +02:00
player.force = global.ocfg . gameplay.main_force_name -- Put them on the main force.
2024-08-10 19:26:52 +02:00
2024-09-07 03:10:29 +02:00
if ( not player.admin ) then
player.permission_group = game.permissions . get_group ( " holding_pen " )
end
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
if ( global.player_respawns [ player.name ] == nil ) then
global.player_respawns [ player.name ] = { }
end
if ( global.player_cooldowns [ player.name ] == nil ) then
global.player_cooldowns [ player.name ] = { setRespawn = game.tick }
end
-- Reset GUI and show the spawn options.
DisplayWelcomeTextGui ( player )
2024-08-12 04:16:48 +02:00
InitOarcGuiTabs ( player )
2024-08-10 19:26:52 +02:00
HideOarcGui ( player )
2024-09-14 19:59:28 +02:00
SetOarcGuiTabEnabled ( player , OARC_SPAWN_CTRL_TAB_NAME , false ) -- Make sure spawn control tab is disabled
SwitchOarcGuiTab ( player , OARC_SERVER_INFO_TAB_NAME )
2024-08-10 19:26:52 +02:00
end
-- Check if the player has a different spawn point than the default one
-- Make sure to give the default starting items
---@param event EventData.on_player_respawned
---@return nil
function SeparateSpawnsPlayerRespawned ( event )
local player = game.players [ event.player_index ]
2024-09-14 19:59:28 +02:00
local surface_name = player.surface . name
-- It's possible if player is dead, and then resets, we don't want to do anything else.
if ( player.surface . name == HOLDING_PEN_SURFACE_NAME ) then return end
2024-09-23 18:59:22 +02:00
-- If the mod isn't active on this surface, then ignore it.
if ( not global.oarc_surfaces [ surface_name ] ) then return end
2024-09-14 19:59:28 +02:00
SendPlayerToSpawn ( surface_name , player )
2024-08-12 04:16:48 +02:00
GivePlayerRespawnItems ( player )
end
---If the player leaves early, remove their base.
---@param event EventData.on_player_left_game
---@return nil
function SeparateSpawnsPlayerLeft ( event )
local player = game.players [ event.player_index ]
-- If players leave early, say goodbye.
if ( player and ( player.online_time < ( global.ocfg . gameplay.minimum_online_time * TICKS_PER_MINUTE ) ) ) then
2024-09-14 19:59:28 +02:00
SendBroadcastMsg ( { " oarc-player-left-early " , player.name , global.ocfg . gameplay.minimum_online_time } )
2024-09-13 02:58:19 +02:00
RemoveOrResetPlayer ( player , true )
2024-08-12 04:16:48 +02:00
end
2024-08-10 19:26:52 +02:00
end
2024-09-23 18:59:22 +02:00
---If the player moves surfaces, check if we need to present them with new a new spawn.
---@param event EventData.on_player_changed_surface
---@return nil
function SeparateSpawnsPlayerChangedSurface ( event )
if ( not global.ocfg . gameplay.enable_secondary_spawns ) then return end
local player = game.players [ event.player_index ]
-- Check if player has been init'd yet. If not, then ignore it.
if ( global.player_respawns [ player.name ] == nil ) then return end
-- If the mod isn't active on this surface, then ignore it.
if ( not global.oarc_surfaces [ player.surface . name ] ) then return end
-- If this is their first time on the planet, create a secondary spawn point for them.
-- TODO: Check for buddy and shared spawn hosts?
if ( global.unique_spawns [ player.surface . name ] == nil ) or ( global.unique_spawns [ player.surface . name ] [ player.name ] == nil ) then
log ( " WARNING - THIS IS NOT FULLY IMPLEMENTED YET!! " )
SecondarySpawn ( player , player.surface )
end
end
2024-08-10 19:26:52 +02:00
--[[
___ ___ _ __ __ _ _ ___ ___ _____ _ _ ___
/ __ || _ \ / _ \ \ \ / /| \ | | / __ || __ || _ _ || | | || _ \
\ __ \ | _ // _ \ \ \ / \ / / | . ` | \ __ \ | _ | | | | | _ | || _ /
| ___ /| _ | / _ / \ _ \ \ _ / \ _ / | _ | \ _ | | ___ /| ___ | | _ | \ ___ / | _ |
--]]
-- Generate the basic starter resource around a given location.
---@param surface LuaSurface
2024-09-24 04:48:08 +02:00
---@param position TilePosition --The center of the spawn area
2024-08-10 19:26:52 +02:00
---@return nil
function GenerateStartingResources ( surface , position )
2024-09-24 04:48:08 +02:00
local size_mod = global.ocfg . resource_placement.size_multiplier
local amount_mod = global.ocfg . resource_placement.amount_multiplier
2024-08-10 19:26:52 +02:00
-- Generate all resource tile patches
2024-10-08 03:48:28 +02:00
-- Generate resources in random order around the spawn point.
if global.ocfg . resource_placement.enabled then
2024-09-24 04:48:08 +02:00
if ( global.ocfg . spawn_general.shape == SPAWN_SHAPE_CHOICE_CIRCLE ) or ( global.ocfg . spawn_general.shape == SPAWN_SHAPE_CHOICE_OCTAGON ) then
2024-09-24 17:32:58 +02:00
PlaceResourcesInSemiCircle ( surface , position , size_mod , amount_mod )
2024-09-24 04:48:08 +02:00
elseif ( global.ocfg . spawn_general.shape == SPAWN_SHAPE_CHOICE_SQUARE ) then
2024-09-24 17:32:58 +02:00
PlaceResourcesInSquare ( surface , position , size_mod , amount_mod )
2024-08-10 19:26:52 +02:00
end
2024-10-08 03:48:28 +02:00
-- Generate resources using specified offsets if auto placement is disabled.
else
for r_name , r_data in pairs ( global.ocfg . surfaces_config [ surface.name ] . spawn_config.solid_resources --[[@as table<string, OarcConfigSolidResource>]] ) do
local pos = { x = position.x + r_data.x_offset , y = position.y + r_data.y_offset }
GenerateResourcePatch ( surface , r_name , r_data.size * size_mod , pos , r_data.amount * amount_mod )
end
2024-08-10 19:26:52 +02:00
end
-- Generate special fluid resource patches (oil)
2024-10-08 03:48:28 +02:00
-- Autoplace using spacing and vertical offset.
2024-09-24 04:48:08 +02:00
-- Reference position is the bottom of the spawn area.
2024-10-08 03:48:28 +02:00
if global.ocfg . resource_placement.enabled then
local y_offset = global.ocfg . resource_placement.distance_to_edge
local spacing = 4 -- HARDCODED FLUID PATCH SPACING SIZE!
local fluid_ref_pos = { x = position.x , y = position.y + global.ocfg . spawn_general.spawn_radius_tiles - y_offset }
for r_name , r_data in pairs ( global.ocfg . surfaces_config [ surface.name ] . spawn_config.fluid_resources --[[@as table<string, OarcConfigFluidResource>]] ) do
local oil_patch_x = fluid_ref_pos.x - ( ( ( r_data.num_patches - 1 ) * spacing ) / 2 )
local oil_patch_y = fluid_ref_pos.y
for i = 1 , r_data.num_patches do
surface.create_entity ( {
name = " crude-oil " ,
amount = r_data.amount ,
position = { oil_patch_x , oil_patch_y }
} )
oil_patch_x = oil_patch_x + spacing
end
fluid_ref_pos.y = fluid_ref_pos.y - spacing
end
-- This places using specified offsets if auto placement is disabled.
else
local fluid_ref_pos = { x = position.x , y = position.y + global.ocfg . spawn_general.spawn_radius_tiles }
for r_name , r_data in pairs ( global.ocfg . surfaces_config [ surface.name ] . spawn_config.fluid_resources --[[@as table<string, OarcConfigFluidResource>]] ) do
local oil_patch_x = fluid_ref_pos.x + r_data.x_offset_start
local oil_patch_y = fluid_ref_pos.y + r_data.y_offset_start
for i = 1 , r_data.num_patches do
surface.create_entity ( {
name = r_name ,
amount = r_data.amount ,
position = { oil_patch_x , oil_patch_y }
} )
oil_patch_x = oil_patch_x + r_data.x_offset_next
oil_patch_y = oil_patch_y + r_data.y_offset_next
end
2024-08-10 19:26:52 +02:00
end
end
end
2024-09-24 04:48:08 +02:00
---Places starting resource deposits in a semi-circle around the spawn point.
---@param surface LuaSurface
---@param position TilePosition --The center of the spawn area
2024-09-24 17:32:58 +02:00
---@param size_mod number
---@param amount_mod number
---@return nil
function PlaceResourcesInSemiCircle ( surface , position , size_mod , amount_mod )
2024-09-24 04:48:08 +02:00
-- Create list of resource tiles
---@type table<string>
local r_list = { }
for r_name , _ in pairs ( global.ocfg . surfaces_config [ surface.name ] . spawn_config.solid_resources --[[@as table<string, OarcConfigSolidResource>]] ) do
if ( r_name ~= " " ) then
table.insert ( r_list , r_name )
end
end
---@type table<string>
local shuffled_list = FYShuffle ( r_list )
-- This places resources in a semi-circle
local angle_offset = global.ocfg . resource_placement.angle_offset
local num_resources = table_size ( global.ocfg . surfaces_config [ surface.name ] . spawn_config.solid_resources )
2024-10-08 03:48:28 +02:00
local theta = ( ( global.ocfg . resource_placement.angle_final - global.ocfg . resource_placement.angle_offset ) / ( num_resources - 1 ) ) ;
2024-09-24 04:48:08 +02:00
local count = 0
local radius = global.ocfg . spawn_general.spawn_radius_tiles - global.ocfg . resource_placement.distance_to_edge
for _ , r_name in pairs ( shuffled_list ) do
local angle = ( theta * count ) + angle_offset ;
local tx = ( radius * math.cos ( angle ) ) + position.x
local ty = ( radius * math.sin ( angle ) ) + position.y
local pos = { x = math.floor ( tx ) , y = math.floor ( ty ) }
local resourceConfig = global.ocfg . surfaces_config [ surface.name ] . spawn_config.solid_resources [ r_name ]
GenerateResourcePatch ( surface , r_name , resourceConfig.size * size_mod , pos , resourceConfig.amount * amount_mod )
count = count + 1
end
end
---Places starting resource deposits in a line starting at the top left of the spawn point.
---@param surface LuaSurface
---@param position TilePosition --The center of the spawn area
2024-09-24 17:32:58 +02:00
---@param size_mod number
---@param amount_mod number
---@return nil
function PlaceResourcesInSquare ( surface , position , size_mod , amount_mod )
2024-09-24 04:48:08 +02:00
-- Create list of resource tiles
---@type table<string>
local r_list = { }
for r_name , _ in pairs ( global.ocfg . surfaces_config [ surface.name ] . spawn_config.solid_resources --[[@as table<string, OarcConfigSolidResource>]] ) do
if ( r_name ~= " " ) then
table.insert ( r_list , r_name )
end
end
---@type table<string>
local shuffled_list = FYShuffle ( r_list )
-- Get the top left position of the spawn area
local resource_position = { x = position.x - global.ocfg . spawn_general.spawn_radius_tiles ,
y = position.y - global.ocfg . spawn_general.spawn_radius_tiles }
-- Offset the starting position
resource_position.x = resource_position.x + global.ocfg . resource_placement.horizontal_offset
resource_position.y = resource_position.y + global.ocfg . resource_placement.vertical_offset
-- Place vertically using linear spacing
for _ , r_name in pairs ( shuffled_list ) do
local resourceConfig = global.ocfg . surfaces_config [ surface.name ] . spawn_config.solid_resources [ r_name ]
local size = resourceConfig.size * size_mod
GenerateResourcePatch ( surface , r_name , size , resource_position , resourceConfig.amount * amount_mod )
resource_position.y = resource_position.y + size + global.ocfg . resource_placement.linear_spacing
end
end
2024-08-10 19:26:52 +02:00
---Sends the player to their spawn point
2024-09-14 19:59:28 +02:00
---@param delayed_spawn OarcDelayedSpawn
2024-08-10 19:26:52 +02:00
---@return nil
2024-09-14 19:59:28 +02:00
function SendPlayerToNewSpawnAndCreateIt ( delayed_spawn )
2024-08-10 19:26:52 +02:00
local ocfg --[[@as OarcConfig]] = global.ocfg
2024-09-21 03:39:01 +02:00
local spawn_config = ocfg.surfaces_config [ delayed_spawn.surface ] . spawn_config
2024-08-10 19:26:52 +02:00
-- DOUBLE CHECK and make sure the area is super safe.
2024-10-08 04:24:08 +02:00
ClearNearbyEnemies ( delayed_spawn.position , spawn_config.safe_area . safe_radius * CHUNK_SIZE ,
2024-09-14 19:59:28 +02:00
game.surfaces [ delayed_spawn.surface ] )
2024-08-10 19:26:52 +02:00
2024-08-11 01:29:50 +02:00
-- Generate water strip only if we don't have a moat.
2024-09-14 19:59:28 +02:00
if ( not delayed_spawn.moat ) then
2024-09-21 03:39:01 +02:00
local water_data = spawn_config.water
2024-09-24 04:48:08 +02:00
-- Reference position is the top of the spawn area.
local reference_pos = {
x = delayed_spawn.position . x ,
y = delayed_spawn.position . y - global.ocfg . spawn_general.spawn_radius_tiles
}
2024-09-14 19:59:28 +02:00
CreateWaterStrip ( game.surfaces [ delayed_spawn.surface ] ,
2024-09-24 04:48:08 +02:00
{ x = reference_pos.x + water_data.x_offset , y = reference_pos.y + water_data.y_offset } ,
2024-08-11 01:29:50 +02:00
water_data.length )
2024-09-14 19:59:28 +02:00
CreateWaterStrip ( game.surfaces [ delayed_spawn.surface ] ,
2024-09-24 04:48:08 +02:00
{ x = reference_pos.x + water_data.x_offset , y = reference_pos.y + water_data.y_offset + 1 } ,
2024-08-11 01:29:50 +02:00
water_data.length )
end
-- Create the spawn resources here
2024-09-14 19:59:28 +02:00
GenerateStartingResources ( game.surfaces [ delayed_spawn.surface ] , delayed_spawn.position )
2024-08-10 19:26:52 +02:00
2024-09-24 04:48:08 +02:00
-- Reference position is RIGHT (WEST) of the spawn area.
local sharing_ref_pos = {
x = delayed_spawn.position . x + global.ocfg . spawn_general.spawn_radius_tiles ,
y = delayed_spawn.position . y
}
2024-09-21 03:39:01 +02:00
-- Create shared power poles
if ( ocfg.gameplay . enable_shared_power ) then
local power_pole_position = {
2024-09-24 04:48:08 +02:00
x = sharing_ref_pos.x + spawn_config.shared_power_pole_position . x_offset ,
y = sharing_ref_pos.y + spawn_config.shared_power_pole_position . y_offset }
2024-09-21 03:39:01 +02:00
CreateSharedPowerPolePair ( game.surfaces [ delayed_spawn.surface ] , power_pole_position )
end
-- Create shared chest
if ( ocfg.gameplay . enable_shared_chest ) then
local chest_position = {
2024-09-24 04:48:08 +02:00
x = sharing_ref_pos.x + spawn_config.shared_chest_position . x_offset ,
y = sharing_ref_pos.y + spawn_config.shared_chest_position . y_offset }
2024-09-21 03:39:01 +02:00
CreateSharedChest ( game.surfaces [ delayed_spawn.surface ] , chest_position )
end
2024-08-10 19:26:52 +02:00
-- Send the player to that position
2024-09-14 19:59:28 +02:00
local player = game.players [ delayed_spawn.playerName ]
SendPlayerToSpawn ( delayed_spawn.surface , player )
2024-08-10 19:26:52 +02:00
GivePlayerStarterItems ( player )
-- Render some welcoming text...
2024-09-14 19:59:28 +02:00
DisplayWelcomeGroundTextAtSpawn ( player , delayed_spawn.surface , delayed_spawn.position )
2024-08-10 19:26:52 +02:00
-- Chart the area.
2024-09-24 04:48:08 +02:00
ChartArea ( player.force , delayed_spawn.position , math.ceil ( global.ocfg . spawn_general.spawn_radius_tiles / CHUNK_SIZE ) ,
2024-08-10 19:26:52 +02:00
player.surface )
if ( player.gui . screen.wait_for_spawn_dialog ~= nil ) then
player.gui . screen.wait_for_spawn_dialog . destroy ( )
end
2024-09-14 19:59:28 +02:00
if ( ocfg.surfaces_config [ delayed_spawn.surface ] . starting_items.crashed_ship ) then
crash_site.create_crash_site ( game.surfaces [ delayed_spawn.surface ] ,
{ x = delayed_spawn.position . x + 15 , y = delayed_spawn.position . y - 25 } ,
ocfg.surfaces_config [ delayed_spawn.surface ] . starting_items.crashed_ship_resources ,
ocfg.surfaces_config [ delayed_spawn.surface ] . starting_items.crashed_ship_wreakage )
2024-08-10 19:26:52 +02:00
end
end
---Displays some welcoming text at the spawn point on the ground. Fades out over time.
---@param player LuaPlayer
2024-08-11 06:19:30 +02:00
---@param surface LuaSurface|string
---@param position MapPosition
2024-08-10 19:26:52 +02:00
---@return nil
2024-08-11 06:19:30 +02:00
function DisplayWelcomeGroundTextAtSpawn ( player , surface , position )
2024-08-10 19:26:52 +02:00
-- Render some welcoming text...
local tcolor = { 0.9 , 0.7 , 0.3 , 0.8 }
local ttl = 2000
local rid1 = rendering.draw_text { text = " Welcome " ,
2024-08-11 06:19:30 +02:00
surface = surface ,
target = { x = position.x - 21 , y = position.y - 15 } ,
2024-08-10 19:26:52 +02:00
color = tcolor ,
scale = 20 ,
font = " compi " ,
time_to_live = ttl ,
-- players={player},
draw_on_ground = true ,
orientation = 0 ,
-- alignment=center,
scale_with_zoom = false ,
only_in_alt_mode = false }
local rid2 = rendering.draw_text { text = " Home " ,
2024-08-11 06:19:30 +02:00
surface = surface ,
target = { x = position.x - 14 , y = position.y - 5 } ,
2024-08-10 19:26:52 +02:00
color = tcolor ,
scale = 20 ,
font = " compi " ,
time_to_live = ttl ,
-- players={player},
draw_on_ground = true ,
orientation = 0 ,
-- alignment=center,
scale_with_zoom = false ,
only_in_alt_mode = false }
table.insert ( global.oarc_renders_fadeout , rid1 )
table.insert ( global.oarc_renders_fadeout , rid2 )
end
--[[
___ _ _ _ _ _ _ _ __ ___ ___ _ _ ___ ___ _ _____ ___ ___ _ _
/ __ || || || | | || \ | || |/ / / __ || __ || \ | || __ || _ \ / _ \ | _ _ || _ _ |/ _ \ | \ | |
| ( __ | __ || | _ | || . ` || ' < | (_ || _| | .` || _| | / / _ \ | | | || (_) || .` |
\ ___ || _ || _ | \ ___ / | _ | \ _ || _ | \ _ \ \ ___ || ___ || _ | \ _ || ___ || _ | _ \ / _ / \ _ \ | _ | | ___ | \ ___ / | _ | \ _ |
--]]
---Clear the spawn areas. This should be run inside the chunk generate event and be given a list of all
---unique spawn points. This clears enemies in the immediate area, creates a slightly safe area around it,
2024-09-14 19:59:28 +02:00
---Resources are generated at a delayed time when the player is moved to the spawn point! It only works off of
---the closest spawn point!!
2024-08-10 19:26:52 +02:00
---@param surface LuaSurface
---@param chunkArea BoundingBox
---@return nil
function SetupAndClearSpawnAreas ( surface , chunkArea )
2024-09-14 19:59:28 +02:00
local closest_spawn = GetClosestUniqueSpawn ( surface.name , chunkArea.left_top )
if ( closest_spawn == nil ) then return end
2024-09-24 04:48:08 +02:00
--[[@type OarcConfigSpawnGeneral]]
local general_spawn_config = global.ocfg . spawn_general
2024-09-14 19:59:28 +02:00
local chunkAreaCenter = {
x = chunkArea.left_top . x + ( CHUNK_SIZE / 2 ) ,
y = chunkArea.left_top . y + ( CHUNK_SIZE / 2 )
}
-- If there is a buddy spawn, we need to setup both areas TOGETHER so they overlap.
local spawns = { closest_spawn }
if ( closest_spawn.buddy_name ~= nil ) then
table.insert ( spawns , global.unique_spawns [ closest_spawn.surface_name ] [ closest_spawn.buddy_name ] )
end
-- This will typically just contain the one spawn point, but if there is a buddy spawn, it will contain both.
for _ , spawn in pairs ( spawns ) do
-- 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)
2024-09-24 04:48:08 +02:00
local landArea = GetAreaAroundPos ( spawn.position , general_spawn_config.spawn_radius_tiles + CHUNK_SIZE )
2024-09-14 19:59:28 +02:00
if not CheckIfInArea ( chunkAreaCenter , landArea ) then
2024-09-05 04:21:43 +02:00
goto CONTINUE
2024-08-10 19:26:52 +02:00
end
2024-09-14 19:59:28 +02:00
-- Remove trees/resources inside the spawn area
2024-09-24 04:48:08 +02:00
if ( general_spawn_config.shape == SPAWN_SHAPE_CHOICE_CIRCLE ) or ( general_spawn_config.shape == SPAWN_SHAPE_CHOICE_OCTAGON ) then
RemoveInCircle ( surface , chunkArea , { " resource " , " cliff " , " tree " } , spawn.position , general_spawn_config.spawn_radius_tiles + 5 )
elseif ( general_spawn_config.shape == SPAWN_SHAPE_CHOICE_SQUARE ) then
RemoveInSquare ( surface , chunkArea , { " resource " , " cliff " , " tree " } , spawn.position , general_spawn_config.spawn_radius_tiles + 5 )
2024-09-19 19:19:57 +02:00
end
2024-09-07 03:10:29 +02:00
2024-09-14 19:59:28 +02:00
-- Fill in the spawn area with landfill and create a circle of trees around it.
local fill_tile = " landfill "
2024-09-24 04:48:08 +02:00
if general_spawn_config.force_grass then
2024-09-19 19:19:57 +02:00
fill_tile = " grass-1 "
2024-08-10 19:26:52 +02:00
end
2024-09-19 19:19:57 +02:00
2024-09-24 04:48:08 +02:00
if ( general_spawn_config.shape == SPAWN_SHAPE_CHOICE_CIRCLE ) then
2024-09-19 19:19:57 +02:00
CreateCropCircle (
surface ,
spawn.position ,
chunkArea ,
2024-09-24 04:48:08 +02:00
general_spawn_config.spawn_radius_tiles ,
2024-09-19 19:19:57 +02:00
fill_tile ,
spawn.moat ,
global.ocfg . gameplay.enable_moat_bridging
)
2024-09-24 04:48:08 +02:00
elseif ( general_spawn_config.shape == SPAWN_SHAPE_CHOICE_OCTAGON ) then
2024-09-19 19:19:57 +02:00
CreateCropOctagon (
surface ,
2024-09-14 19:59:28 +02:00
spawn.position ,
chunkArea ,
2024-09-24 04:48:08 +02:00
general_spawn_config.spawn_radius_tiles ,
2024-09-19 19:19:57 +02:00
fill_tile ,
spawn.moat ,
global.ocfg . gameplay.enable_moat_bridging
)
2024-09-24 04:48:08 +02:00
elseif ( general_spawn_config.shape == SPAWN_SHAPE_CHOICE_SQUARE ) then
2024-09-19 19:19:57 +02:00
CreateCropSquare (
surface ,
spawn.position ,
chunkArea ,
2024-09-24 04:48:08 +02:00
general_spawn_config.spawn_radius_tiles ,
2024-09-19 19:19:57 +02:00
fill_tile ,
spawn.moat ,
global.ocfg . gameplay.enable_moat_bridging
)
2024-08-11 01:29:50 +02:00
end
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
:: CONTINUE ::
2024-08-10 19:26:52 +02:00
end
end
2024-08-11 01:29:50 +02:00
---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
2024-08-10 19:26:52 +02:00
function SeparateSpawnsGenerateChunk ( event )
local surface = event.surface
local chunkArea = event.area
2024-09-07 03:10:29 +02:00
-- Don't block based on spawn enabled.
2024-09-14 19:59:28 +02:00
-- if (not global.oarc_surfaces[surface.name]) then return end
2024-08-12 04:16:48 +02:00
2024-08-10 19:26:52 +02:00
-- Downgrade resources near to spawns
2024-09-07 03:10:29 +02:00
-- TODO: Space Age will change this!
2024-08-11 01:29:50 +02:00
if global.ocfg . gameplay.scale_resources_around_spawns then
2024-08-10 19:26:52 +02:00
DowngradeResourcesDistanceBasedOnChunkGenerate ( surface , chunkArea )
end
-- This handles chunk generation near player spawns
2024-09-17 20:19:41 +02:00
-- If it is near a player spawn, it provide a guaranteed area of land and water tiles.
2024-08-10 19:26:52 +02:00
SetupAndClearSpawnAreas ( surface , chunkArea )
end
2024-08-11 01:29:50 +02:00
---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
2024-08-10 19:26:52 +02:00
function DowngradeResourcesDistanceBasedOnChunkGenerate ( surface , chunkArea )
2024-09-14 19:59:28 +02:00
local closestSpawn = GetClosestUniqueSpawn ( surface.name , chunkArea.left_top )
2024-08-10 19:26:52 +02:00
if ( closestSpawn == nil ) then return end
2024-08-11 01:29:50 +02:00
local distance = util.distance ( chunkArea.left_top , closestSpawn.position )
2024-08-10 19:26:52 +02:00
-- Adjust multiplier to bring it in or out
2024-10-08 04:24:08 +02:00
local modifier = ( distance / ( global.ocfg . surfaces_config [ surface.name ] . spawn_config.safe_area . danger_radius * CHUNK_SIZE * 1 ) ) ^ 3
2024-08-10 19:26:52 +02:00
if modifier < 0.1 then modifier = 0.1 end
if modifier > 1 then return end
local ore_per_tile_cap = math.floor ( 100000 * modifier )
2024-09-14 19:59:28 +02:00
for _ , entity in pairs ( surface.find_entities_filtered { area = chunkArea , type = " resource " } ) do
2024-09-23 04:18:22 +02:00
if entity.valid and entity.amount then
2024-08-10 19:26:52 +02:00
local new_amount = math.ceil ( entity.amount * modifier )
if ( new_amount < 1 ) then
entity.destroy ( )
else
if ( entity.name ~= " crude-oil " ) then
entity.amount = math.min ( new_amount , ore_per_tile_cap )
else
entity.amount = new_amount
end
end
end
end
end
--[[
___ _ ___ _ _ _ _ _ ___
/ __ || | | __ | / _ \ | \ | || | | || _ \
| ( __ | | __ | _ | / _ \ | . ` || | _ | || _ /
\ ___ || ____ || ___ |/ _ / \ _ \ | _ | \ _ | \ ___ / | _ |
--]]
2024-09-13 02:58:19 +02:00
-- ---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
2024-08-10 19:26:52 +02:00
2024-09-13 02:58:19 +02:00
-- player.force = global.ocfg.gameplay.main_force_name
2024-08-10 19:26:52 +02:00
2024-09-13 02:58:19 +02:00
-- if ((#player_old_force.players == 0) and (player_old_force.name ~= global.ocfg.gameplay.main_force_name)) then
-- SendBroadcastMsg("Team " ..
2024-09-23 04:18:22 +02:00
-- player_old_force.name .. " has been destroyed! All buildings will slowly be destroyed now.") --: localize
2024-09-13 02:58:19 +02:00
-- log("DestroyForce - FORCE DESTROYED: " .. player_old_force.name)
2024-09-14 19:59:28 +02:00
-- game.merge_forces(player_old_force, DESTROYED_FORCE_NAME)
2024-09-13 02:58:19 +02:00
-- end
2024-08-10 19:26:52 +02:00
2024-09-13 02:58:19 +02:00
-- RemoveOrResetPlayer(player, false, false, true, true)
-- SeparateSpawnsInitPlayer(player.index)
-- end
2024-08-10 19:26:52 +02:00
2024-09-13 02:58:19 +02:00
-- ---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
2024-08-10 19:26:52 +02:00
2024-09-13 02:58:19 +02:00
-- player.force = global.ocfg.gameplay.main_force_name
2024-08-10 19:26:52 +02:00
2024-09-13 02:58:19 +02:00
-- if ((#player_old_force.players == 0) and (player_old_force.name ~= global.ocfg.gameplay.main_force_name)) then
2024-09-23 04:18:22 +02:00
-- SendBroadcastMsg("Team " .. player_old_force.name .. " has been abandoned!") --: localize
2024-09-13 02:58:19 +02:00
-- log("AbandonForce - FORCE ABANDONED: " .. player_old_force.name)
2024-09-14 19:59:28 +02:00
-- game.merge_forces(player_old_force, ABANDONED_FORCE_NAME)
2024-09-13 02:58:19 +02:00
-- end
2024-08-10 19:26:52 +02:00
2024-09-13 02:58:19 +02:00
-- RemoveOrResetPlayer(player, false, false, false, false)
-- SeparateSpawnsInitPlayer(player.index)
-- end
2024-08-10 19:26:52 +02:00
2024-09-13 02:58:19 +02:00
-- ---Reset player and merge their force to neutral
-- ---@param player LuaPlayer
-- ---@return nil
-- function ResetPlayerAndMergeForceToNeutral(player)
-- RemoveOrResetPlayer(player, false, true, true, true)
-- SeparateSpawnsInitPlayer(player.index)
-- end
2024-08-10 19:26:52 +02:00
2024-08-11 01:29:50 +02:00
---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.
2024-09-13 02:58:19 +02:00
function RemoveOrResetPlayer ( player , remove_player )
2024-08-10 19:26:52 +02:00
if ( not player ) then
log ( " ERROR - CleanupPlayer on NIL Player! " )
return
end
2024-09-13 02:58:19 +02:00
-- If playtime is less than minimum online time, try to remove starter items
if ( player.online_time < ( global.ocfg . gameplay.minimum_online_time * TICKS_PER_MINUTE ) ) then
RemovePlayerStarterItems ( player )
end
2024-09-06 03:15:56 +02:00
2024-08-10 19:26:52 +02:00
-- If this player is staying in the game, lets make sure we don't delete them along with the map chunks being
-- cleared.
2024-08-21 20:03:48 +02:00
player.teleport ( { x = 0 , y = 0 } , HOLDING_PEN_SURFACE_NAME )
2024-08-10 19:26:52 +02:00
local player_old_force = player.force
2024-08-11 01:29:50 +02:00
player.force = global.ocfg . gameplay.main_force_name
2024-08-10 19:26:52 +02:00
-- Clear globals
2024-09-14 19:59:28 +02:00
CleanupPlayerGlobals ( player.name ) -- This cleans global.unique_spawns IF we are transferring ownership.
2024-08-10 19:26:52 +02:00
2024-09-13 02:58:19 +02:00
-- Safely clear the unique spawn IF it is still valid.
2024-09-14 19:59:28 +02:00
UniqueSpawnCleanupRemove ( player.name ) -- Specifically global.unique_spawns
2024-08-10 19:26:52 +02:00
-- Remove a force if this player created it and they are the only one on it
2024-09-13 02:58:19 +02:00
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 " )
2024-08-10 19:26:52 +02:00
end
-- Remove the character completely
if ( remove_player ) then
game.remove_offline_players ( { player } )
end
2024-10-13 04:07:00 +02:00
-- Refresh the shared spawn spawn gui for all players
for _ , p in pairs ( game.connected_players ) do
RefreshSharedSpawnFrameIfExist ( p )
end
2024-08-10 19:26:52 +02:00
end
2024-10-04 19:27:42 +02:00
---Searches all unique spawns for the primary one for a player. This will return null if they joined someeone else's spawn.
2024-09-14 19:59:28 +02:00
---@param player_name string
---@return OarcUniqueSpawn?
function FindPrimaryUniqueSpawn ( player_name )
for _ , spawns in pairs ( global.unique_spawns ) do
if ( spawns [ player_name ] ~= nil and spawns [ player_name ] . primary ) then
return spawns [ player_name ]
end
end
2024-10-13 04:07:00 +02:00
return nil
2024-09-14 19:59:28 +02:00
end
2024-10-04 19:27:42 +02:00
---Find the primary home spawn of a player, if one exists. It could be they joined a shared spawn.
---@param player_name string
---@return OarcUniqueSpawn?
function FindPlayerHomeSpawn ( player_name )
for _ , spawns in pairs ( global.unique_spawns ) do
for _ , spawn in pairs ( spawns ) do
if ( spawn.primary ) and ( ( spawn.host_name == player_name ) or TableContains ( spawn.joiners , player_name ) ) then
return spawn
end
end
end
end
2024-09-19 20:05:29 +02:00
---Searches all unique spawns for a list of secondary ones for a player.
---@param player_name string
---@return table<string, OarcUniqueSpawn> -- Indexed by surface name!
function FindSecondaryUniqueSpawns ( player_name )
local secondary_spawns = { }
for surface_index , spawns in pairs ( global.unique_spawns ) do
if ( spawns [ player_name ] ~= nil and not spawns [ player_name ] . primary ) then
secondary_spawns [ surface_index ] = spawns [ player_name ]
end
end
return secondary_spawns
end
2024-09-13 02:58:19 +02:00
---Cleans up a player's unique spawn point, if safe to do so.
2024-09-14 19:59:28 +02:00
---@param player_name string
2024-08-11 01:29:50 +02:00
---@return nil
2024-09-14 19:59:28 +02:00
function UniqueSpawnCleanupRemove ( player_name )
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
-- Assumes we only remove the one primary unique spawn per player.
local primary_spawn = FindPrimaryUniqueSpawn ( player_name )
if ( primary_spawn == nil ) then return end -- Safety
log ( " UniqueSpawnCleanupRemove - " .. player_name )
2024-09-24 04:48:08 +02:00
local total_spawn_width = global.ocfg . spawn_general.spawn_radius_tiles +
global.ocfg . spawn_general.moat_width_tiles
2024-08-10 19:26:52 +02:00
2024-08-28 07:36:02 +02:00
-- Check if it was near someone else's base. (Really just buddy base is possible I think?)
2024-08-10 19:26:52 +02:00
nearOtherSpawn = false
2024-09-14 19:59:28 +02:00
for player_index , spawn in pairs ( global.unique_spawns [ primary_spawn.surface_name ] ) do
if ( ( player_index ~= player_name ) and
2024-09-19 19:19:57 +02:00
( util.distance ( primary_spawn.position , spawn.position ) < ( total_spawn_width * 3 ) ) ) then
2024-09-14 19:59:28 +02:00
log ( " Won't remove base as it's close to another spawn: " .. player_index )
2024-08-10 19:26:52 +02:00
nearOtherSpawn = true
end
end
2024-09-13 02:58:19 +02:00
-- Use regrowth mod to cleanup the area.
2024-09-14 19:59:28 +02:00
local spawn_position = primary_spawn.position
2024-09-13 02:58:19 +02:00
if ( global.ocfg . regrowth.enable_abandoned_base_cleanup and ( not nearOtherSpawn ) ) then
2024-09-14 19:59:28 +02:00
log ( " Removing base: " .. spawn_position.x .. " , " .. spawn_position.y .. " on surface: " .. primary_spawn.surface_name )
2024-09-19 19:19:57 +02:00
RegrowthMarkAreaForRemoval ( primary_spawn.surface_name , spawn_position , math.ceil ( total_spawn_width / CHUNK_SIZE ) + 1 ) -- +1 to match the spawn generation requested area
2024-09-13 02:58:19 +02:00
TriggerCleanup ( )
2024-08-10 19:26:52 +02:00
end
2024-09-14 19:59:28 +02:00
global.unique_spawns [ primary_spawn.surface_name ] [ player_name ] = nil
2024-08-10 19:26:52 +02:00
end
2024-08-11 01:29:50 +02:00
---Cleans up all references to a player in the global tables.
2024-09-14 19:59:28 +02:00
---@param player_name string
2024-08-11 01:29:50 +02:00
---@return nil
2024-09-14 19:59:28 +02:00
function CleanupPlayerGlobals ( player_name )
2024-08-10 19:26:52 +02:00
-- Clear the buddy pair IF one exists
2024-09-14 19:59:28 +02:00
if ( global.buddy_pairs [ player_name ] ~= nil ) then
local buddyName = global.buddy_pairs [ player_name ]
global.buddy_pairs [ player_name ] = nil
global.buddy_pairs [ buddyName ] = nil
2024-08-10 19:26:52 +02:00
end
-- Transfer or remove a shared spawn if player is owner
2024-09-14 19:59:28 +02:00
local unique_spawn = FindPrimaryUniqueSpawn ( player_name )
if ( unique_spawn ~= nil and # unique_spawn.joiners > 0 ) then
local new_owner_name = table.remove ( unique_spawn.joiners ) -- Get 1 to use as new owner.
TransferOwnershipOfSharedSpawn ( unique_spawn , new_owner_name )
SendBroadcastMsg ( { " oarc-host-left-new-host " , player_name , new_owner_name } )
2024-08-10 19:26:52 +02:00
end
2024-09-14 19:59:28 +02:00
-- Check all other shared spawns too in case they joined one.
for surface_index , spawns in pairs ( global.unique_spawns ) do
for player_index , spawn in pairs ( spawns ) do
for index , joiner in pairs ( spawn.joiners ) do
if ( player_name == joiner ) then
global.unique_spawns [ surface_index ] [ player_index ] . joiners [ index ] = nil
goto LOOP_BREAK -- Nest loop break. Assumes only one entry per player is possible.
end
2024-08-10 19:26:52 +02:00
end
end
end
:: LOOP_BREAK ::
-- Clear their personal spawn point info
2024-09-14 19:59:28 +02:00
if ( global.player_respawns [ player_name ] ~= nil ) then
global.player_respawns [ player_name ] = nil
2024-08-10 19:26:52 +02:00
end
-- Remove them from the delayed spawn queue if they are in it
2024-09-14 19:59:28 +02:00
for index , delayedSpawn in pairs ( global.delayed_spawns --[[@as OarcDelayedSpawnsTable]] ) do
if ( player_name == delayedSpawn.playerName ) then
global.delayed_spawns [ index ] = nil
log ( " Removing player from delayed spawn queue: " .. player_name )
2024-08-10 19:26:52 +02:00
break
end
end
2024-09-06 03:15:56 +02:00
-- Remove them from any join queues they may be in:
2024-09-14 19:59:28 +02:00
RemovePlayerFromJoinQueue ( player_name )
2024-09-06 03:15:56 +02:00
2024-09-14 19:59:28 +02:00
if ( global.player_cooldowns [ player_name ] ~= nil ) then
global.player_cooldowns [ player_name ] = nil
2024-08-10 19:26:52 +02:00
end
end
2024-08-11 01:29:50 +02:00
---Transfers ownership of a shared spawn to another player.
2024-09-14 19:59:28 +02:00
---@param spawn OarcUniqueSpawn
---@param new_host_name string
2024-08-11 01:29:50 +02:00
---@return nil
2024-09-14 19:59:28 +02:00
function TransferOwnershipOfSharedSpawn ( spawn , new_host_name )
-- Create a new unique for the new owner based on the old one.
global.unique_spawns [ spawn.surface_name ] [ new_host_name ] = {
position = spawn.position ,
surface_name = spawn.surface_name ,
primary = spawn.primary ,
moat = spawn.moat ,
host_name = new_host_name ,
joiners = spawn.joiners ,
join_queue = { } ,
open_access = false ,
buddy_name = spawn.buddy_name
}
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
-- Update the matching buddy spawn if it exists.
if ( spawn.buddy_name ~= nil ) then
global.unique_spawns [ spawn.surface_name ] [ spawn.buddy_name ] . buddy_name = new_host_name
end
-- Delete the old one
global.unique_spawns [ spawn.surface_name ] [ spawn.host_name ] = nil
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
game.players [ new_host_name ] . print ( { " oarc-new-owner-msg " } )
2024-08-10 19:26:52 +02:00
end
--[[
_ _ ___ _ ___ ___ ___ ___ _____ _ _ ___ ___
| || || __ || | | _ \ | __ || _ \ / __ || _ _ || | | || __ || __ |
| __ || _ | | | __ | _ /| _ | | / \ __ \ | | | | _ | || _ | | _ |
| _ || _ || ___ || ____ || _ | | ___ || _ | _ \ | ___ / | _ | \ ___ / | _ | | _ |
--]]
2024-09-06 03:15:56 +02:00
---Finds and removes a player from a shared spawn join queue, and refreshes the host's GUI.
---@param player_name string
---@return boolean
function RemovePlayerFromJoinQueue ( player_name )
2024-09-14 19:59:28 +02:00
for surface_index , spawns in pairs ( global.unique_spawns ) do
for player_index , spawn in pairs ( spawns ) do
for index , requestor in pairs ( spawn.join_queue ) do
if ( requestor == player_name ) then
global.unique_spawns [ surface_index ] [ player_index ] . join_queue [ index ] = nil
local host_player = game.players [ player_index ]
if ( host_player ~= nil ) and ( host_player.connected ) then
OarcGuiRefreshContent ( host_player )
end
return true
2024-09-06 03:15:56 +02:00
end
end
end
end
2024-09-14 19:59:28 +02:00
2024-09-06 03:15:56 +02:00
return false
end
2024-09-14 19:59:28 +02:00
---Same as GetClosestPosFromTable but specific to global.unique_spawns
---@param surface_name string
2024-08-11 01:29:50 +02:00
---@param pos MapPosition
---@return OarcUniqueSpawn?
2024-09-14 19:59:28 +02:00
function GetClosestUniqueSpawn ( surface_name , pos )
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
local surface_spawns
for surface_index , spawns in pairs ( global.unique_spawns ) do
if ( surface_index == surface_name ) then
2024-09-23 04:18:22 +02:00
if ( table_size ( spawns ) == 0 ) then return nil end -- EXIT - No spawns on requested surface
2024-09-14 19:59:28 +02:00
surface_spawns = spawns
2024-08-11 01:29:50 +02:00
end
2024-09-14 19:59:28 +02:00
end
if ( surface_spawns == nil ) then return nil end
local closest_dist = nil
local closest_spawn = nil
2024-08-11 01:29:50 +02:00
2024-09-14 19:59:28 +02:00
for _ , spawn in pairs ( surface_spawns ) do
local new_dist = util.distance ( pos , spawn.position )
2024-08-10 19:26:52 +02:00
if ( closest_dist == nil ) then
closest_dist = new_dist
2024-09-14 19:59:28 +02:00
closest_spawn = spawn
2024-08-10 19:26:52 +02:00
elseif ( closest_dist > new_dist ) then
closest_dist = new_dist
2024-09-14 19:59:28 +02:00
closest_spawn = spawn
2024-08-10 19:26:52 +02:00
end
end
2024-09-14 19:59:28 +02:00
return closest_spawn
2024-08-10 19:26:52 +02:00
end
2024-09-14 19:59:28 +02:00
---Find all players that belong to the same PRIMARY shared spawn as this player, including buddies!
---@param player_name string
---@param include_offline boolean
---@return string[]
function GetPlayersFromSameSpawn ( player_name , include_offline )
local shared_players = { }
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
for _ , spawns in pairs ( global.unique_spawns ) do
for _ , spawn in pairs ( spawns ) do
if ( not spawn.primary ) then goto CONTINUE end
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
-- Is the player either the host OR a joiner OR a buddy?
if ( spawn.host_name == player_name ) or ( TableContains ( spawn.joiners , player_name ) or ( spawn.buddy_name == player_name ) ) then
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
if ( include_offline or game.players [ spawn.host_name ] . connected ) then
table.insert ( shared_players , spawn.host_name )
end
2024-08-11 01:29:50 +02:00
2024-09-14 19:59:28 +02:00
for _ , joiner in pairs ( spawn.joiners ) do
if ( include_offline or game.players [ joiner ] . connected ) then
table.insert ( shared_players , joiner )
end
end
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
if ( spawn.buddy_name ~= nil ) then
if ( include_offline or game.players [ spawn.buddy_name ] . connected ) then
table.insert ( shared_players , spawn.buddy_name )
end
end
2024-09-23 04:18:22 +02:00
return shared_players -- We only need to find one match.
2024-08-10 19:26:52 +02:00
end
2024-09-14 19:59:28 +02:00
:: CONTINUE ::
end
end
return shared_players
end
2024-08-11 01:29:50 +02:00
2024-09-14 19:59:28 +02:00
---Returns the number of players currently online at the shared spawn including the host.
---@param surface_name string
---@param owner_name string
---@return number
function GetOnlinePlayersAtSharedSpawn ( surface_name , owner_name )
local spawn = global.unique_spawns [ surface_name ] [ owner_name ]
if spawn == nil then return 0 end
-- Does not count base owner
local count = 0
-- For each player in the shared spawn, check if online and add to count.
for _ , joiner in pairs ( spawn.joiners ) do
if game.players [ joiner ] . connected then
count = count + 1
2024-08-10 19:26:52 +02:00
end
2024-09-14 19:59:28 +02:00
end
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
-- Add the host player to the count
if game.players [ owner_name ] . connected then
count = count + 1
2024-08-10 19:26:52 +02:00
end
2024-09-14 19:59:28 +02:00
return count
2024-08-10 19:26:52 +02:00
end
2024-09-13 02:58:19 +02:00
-- -- 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()
-- return #GetAvailableSharedSpawns()
-- end
2024-08-10 19:26:52 +02:00
2024-09-14 19:59:28 +02:00
---This is used to provide both a list of spawns and a list of hosts for easy display in the GUI.
---@alias AvailableSpawnsTable { hosts: string[], spawns : OarcUniqueSpawn[] }
2024-09-05 04:21:43 +02:00
---Get a list of available shared spawns.
2024-09-14 19:59:28 +02:00
---@return AvailableSpawnsTable
2024-09-05 04:21:43 +02:00
function GetAvailableSharedSpawns ( )
2024-09-14 19:59:28 +02:00
local available_spawns = { hosts = { } , spawns = { } }
2024-08-11 01:29:50 +02:00
2024-09-14 19:59:28 +02:00
for surface_index , spawns in pairs ( global.unique_spawns ) do
for owner_name , spawn in pairs ( spawns ) do
if IsSharedSpawnOpen ( surface_index , owner_name ) and not IsSharedSpawnFull ( surface_index , owner_name ) then
table.insert ( available_spawns.hosts , owner_name )
table.insert ( available_spawns.spawns , spawn )
end
2024-08-10 19:26:52 +02:00
end
end
2024-09-14 19:59:28 +02:00
return available_spawns
2024-08-10 19:26:52 +02:00
end
2024-09-13 02:58:19 +02:00
---Check if a specific shared spawn is valid, open and host is online (might still be full!)
2024-09-14 19:59:28 +02:00
---@param surface_name string
2024-09-11 18:49:23 +02:00
---@param owner_name string
---@return boolean
2024-09-14 19:59:28 +02:00
function IsSharedSpawnOpen ( surface_name , owner_name )
if ( global.unique_spawns [ surface_name ] == nil ) or ( global.unique_spawns [ surface_name ] [ owner_name ] == nil ) then
2024-09-11 18:49:23 +02:00
return false
end
2024-09-14 19:59:28 +02:00
local spawn = global.unique_spawns [ surface_name ] [ owner_name ]
if ( not spawn.open_access ) then
2024-09-11 18:49:23 +02:00
return false
end
if ( game.players [ owner_name ] == nil ) or not ( game.players [ owner_name ] . connected ) then
return false
end
return true
end
2024-09-13 02:58:19 +02:00
---Check if a specific shared spawn is full.
2024-09-14 19:59:28 +02:00
---@param surface_name string
2024-09-13 02:58:19 +02:00
---@param owner_name string
---@return boolean --True if the shared spawn is full or invalid.
2024-09-14 19:59:28 +02:00
function IsSharedSpawnFull ( surface_name , owner_name )
if ( global.unique_spawns [ surface_name ] [ owner_name ] == nil ) then return true end
2024-08-10 19:26:52 +02:00
2024-09-13 02:58:19 +02:00
-- Technically I only limit the players based on if they are online, so you can exceed the limit if players join
-- while others are offline. This is a feature, not a bug?
2024-09-14 19:59:28 +02:00
return ( GetOnlinePlayersAtSharedSpawn ( surface_name , owner_name ) >= global.ocfg . gameplay.number_of_players_per_shared_spawn )
2024-08-10 19:26:52 +02:00
end
2024-09-13 02:58:19 +02:00
-- ---Checks if player has a custom spawn point set.
-- ---@param player LuaPlayer
-- ---@return boolean
-- function DoesPlayerHaveCustomSpawn(player)
2024-09-14 19:59:28 +02:00
-- for name,_ in pairs(global.player_respawns --[[@as OarcPlayerRespawnsTable]]) do
2024-09-13 02:58:19 +02:00
-- if (player.name == name) then
-- return true
-- end
-- end
-- return false
-- end
-- ---Gets the custom spawn point for a player if they have one.
-- ---@param player LuaPlayer
-- ---@return OarcPlayerSpawn?
-- function GetPlayerCustomSpawn(player)
2024-09-14 19:59:28 +02:00
-- for name, player_spawn in pairs(global.player_respawns --[[@as OarcPlayerRespawnsTable]]) do
2024-09-13 02:58:19 +02:00
-- if (player.name == name) then
-- return player_spawn
-- end
-- end
-- return nil
-- end
2024-09-19 19:19:57 +02:00
---Sets the custom spawn point for a player. They can have one per surface.
2024-09-14 19:59:28 +02:00
---@param player_name string
---@param surface_name string
2024-08-11 01:29:50 +02:00
---@param position MapPosition
2024-09-19 19:19:57 +02:00
---@param reset_cooldown boolean
2024-08-11 01:29:50 +02:00
---@return nil
2024-09-19 19:19:57 +02:00
function SetPlayerRespawn ( player_name , surface_name , position , reset_cooldown )
2024-08-11 06:19:30 +02:00
---@type OarcPlayerSpawn
local updatedPlayerSpawn = { }
2024-09-14 19:59:28 +02:00
updatedPlayerSpawn.surface = surface_name
2024-08-11 06:19:30 +02:00
updatedPlayerSpawn.position = position
2024-09-14 19:59:28 +02:00
global.player_respawns [ player_name ] [ surface_name ] = updatedPlayerSpawn
2024-09-19 19:19:57 +02:00
if ( global.player_cooldowns [ player_name ] . setRespawn == nil ) or reset_cooldown then
global.player_cooldowns [ player_name ] . setRespawn = game.tick
end
2024-08-10 19:26:52 +02:00
end
2024-09-14 19:59:28 +02:00
---Creates the global.unique_spawns entries for a new spawn area.
2024-09-13 02:58:19 +02:00
---@param player_name string
---@param surface_name string
---@param spawn_position MapPosition
---@param moat_enabled boolean
2024-09-14 19:59:28 +02:00
---@param primary boolean
---@param buddy_name string?
2024-09-13 02:58:19 +02:00
---@return nil
2024-09-14 19:59:28 +02:00
function InitUniqueSpawnGlobals ( player_name , surface_name , spawn_position , moat_enabled , primary , buddy_name )
2024-09-13 02:58:19 +02:00
---@type OarcUniqueSpawn
2024-09-14 19:59:28 +02:00
local new_unique_spawn = {
surface_name = surface_name ,
position = spawn_position ,
moat = moat_enabled ,
primary = primary ,
host_name = player_name ,
joiners = { } ,
join_queue = { } ,
open_access = false ,
buddy_name = buddy_name
}
if global.unique_spawns [ surface_name ] == nil then
global.unique_spawns [ surface_name ] = { }
end
2024-09-13 02:58:19 +02:00
2024-09-14 19:59:28 +02:00
global.unique_spawns [ surface_name ] [ player_name ] = new_unique_spawn
2024-09-13 02:58:19 +02:00
end
---Queue a player for a delayed spawn. This will generate the spawn area and move the player there when ready.
2024-09-14 19:59:28 +02:00
---@param player_name string
2024-08-11 06:19:30 +02:00
---@param surface string
2024-09-14 19:59:28 +02:00
---@param spawn_position MapPosition
---@param moat_enabled boolean
---@param primary boolean
---@param buddy_name string?
2024-08-11 01:29:50 +02:00
---@return nil
2024-09-14 19:59:28 +02:00
function QueuePlayerForDelayedSpawn ( player_name , surface , spawn_position , moat_enabled , primary , buddy_name )
2024-08-10 19:26:52 +02:00
-- If we get a valid spawn point, setup the area
2024-09-14 19:59:28 +02:00
if ( ( spawn_position.x == 0 ) and ( spawn_position.y == 0 ) ) then
error ( " Invalid spawn position for player: " .. player_name .. " on surface: " .. surface )
2024-08-10 19:26:52 +02:00
end
2024-09-14 19:59:28 +02:00
InitUniqueSpawnGlobals ( player_name , surface , spawn_position , moat_enabled , primary , buddy_name )
-- Add a 1 chunk buffer to be safe
2024-09-24 04:48:08 +02:00
local total_spawn_width = global.ocfg . spawn_general.spawn_radius_tiles +
global.ocfg . spawn_general.moat_width_tiles
2024-09-19 19:19:57 +02:00
local spawn_chunk_radius = math.ceil ( total_spawn_width / CHUNK_SIZE ) + 1
2024-09-14 19:59:28 +02:00
local delay_spawn_seconds = 5 * spawn_chunk_radius
game.players [ player_name ] . print ( { " oarc-generating-spawn-please-wait " } )
game.surfaces [ surface ] . request_to_generate_chunks ( spawn_position , spawn_chunk_radius )
local final_chunk = GetChunkPosFromTilePos ( spawn_position )
final_chunk.x = final_chunk.x + spawn_chunk_radius
final_chunk.y = final_chunk.y + spawn_chunk_radius
---@type OarcDelayedSpawn
local delayedSpawn = { }
delayedSpawn.playerName = player_name
delayedSpawn.surface = surface
delayedSpawn.position = spawn_position
delayedSpawn.moat = moat_enabled
delayedSpawn.delayedTick = game.tick + delay_spawn_seconds * TICKS_PER_SECOND
delayedSpawn.final_chunk_generated = final_chunk
table.insert ( global.delayed_spawns , delayedSpawn )
HideOarcGui ( game.players [ player_name ] )
DisplayPleaseWaitForSpawnDialog ( game.players [ player_name ] , delay_spawn_seconds , game.surfaces [ surface ] , spawn_position )
RegrowthMarkAreaSafeGivenTilePos ( surface , spawn_position ,
2024-09-24 04:48:08 +02:00
math.ceil ( global.ocfg . spawn_general.spawn_radius_tiles / CHUNK_SIZE ) , true )
2024-09-14 19:59:28 +02:00
-- Chart the area to be able to display the minimap while the player waits.
ChartArea ( game.players [ player_name ] . force ,
delayedSpawn.position ,
spawn_chunk_radius ,
surface
)
2024-08-10 19:26:52 +02:00
end
2024-09-19 19:19:57 +02:00
---Creates and sends a player to a new secondary spawn, temporarily placing them in the holding pen.
---@param player LuaPlayer
---@param surface LuaSurface
---@return nil
function SecondarySpawn ( player , surface )
-- Ensure we still have their previous spawn choices
local spawn_choices = global.spawn_choices [ player.name ]
if ( spawn_choices == nil ) then
log ( " ERROR - SecondarySpawn - No spawn choices for player: " .. player.name )
return
end
-- Confirm there is no existing spawn point for this player on this surface
if ( global.unique_spawns [ surface.name ] ~= nil and global.unique_spawns [ surface.name ] [ player.name ] ~= nil ) then
log ( " ERROR - SecondarySpawn - Player already has a spawn point on this surface: " .. player.name )
return
end
-- Find a new spawn point
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
-- Add new spawn point for the new surface
SetPlayerRespawn ( player.name , surface.name , spawn_position , false ) -- Do not reset cooldown
QueuePlayerForDelayedSpawn ( player.name , surface.name , spawn_position , spawn_choices.moat , false , nil )
-- Send them to the holding pen
SafeTeleport ( player , game.surfaces [ HOLDING_PEN_SURFACE_NAME ] , { x = 0 , y = 0 } )
-- Announce
2024-09-23 18:59:22 +02:00
SendBroadcastMsg ( { " " , { " oarc-player-new-secondary " , player.name , surface.name } , " " , GetGPStext ( surface.name , spawn_position ) } )
2024-09-19 19:19:57 +02:00
end
2024-08-10 19:26:52 +02:00
-- 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.
2024-08-11 01:29:50 +02:00
---@return nil
2024-08-10 19:26:52 +02:00
function DelayedSpawnOnTick ( )
if ( ( game.tick % ( 30 ) ) == 1 ) then
2024-09-14 19:59:28 +02:00
if ( ( global.delayed_spawns ~= nil ) and ( # global.delayed_spawns > 0 ) ) then
2024-08-11 01:29:50 +02:00
-- I think this loop removes from the back of the table to the front??
2024-09-14 19:59:28 +02:00
for i = # global.delayed_spawns , 1 , - 1 do
delayedSpawn = global.delayed_spawns [ i ] --[[@as OarcDelayedSpawn]]
2024-08-10 19:26:52 +02:00
2024-08-23 20:16:11 +02:00
local surface = game.surfaces [ delayedSpawn.surface ]
if ( ( delayedSpawn.delayedTick < game.tick ) or surface.is_chunk_generated ( delayedSpawn.final_chunk_generated ) ) then
2024-08-10 19:26:52 +02:00
if ( game.players [ delayedSpawn.playerName ] ~= nil ) then
SendPlayerToNewSpawnAndCreateIt ( delayedSpawn )
end
2024-09-14 19:59:28 +02:00
table.remove ( global.delayed_spawns , i )
2024-08-10 19:26:52 +02:00
end
end
end
end
end
2024-09-13 02:58:19 +02:00
---Send player to their custom spawn point
2024-09-14 19:59:28 +02:00
---@param surface_name string
2024-08-11 01:29:50 +02:00
---@param player LuaPlayer
---@return nil
2024-09-14 19:59:28 +02:00
function SendPlayerToSpawn ( surface_name , player )
local spawn = global.player_respawns [ player.name ] [ surface_name ]
SafeTeleport ( player , game.surfaces [ surface_name ] , spawn.position )
2024-10-13 04:15:26 +02:00
player.permission_group = game.permissions . get_group ( " Default " )
2024-08-10 19:26:52 +02:00
end
2024-09-14 19:59:28 +02:00
-- ---Send player to a random spawn point.
-- ---@param player LuaPlayer
-- ---@return nil
-- function SendPlayerToRandomSpawn(player)
-- local numSpawns = #global.oc--ore.unique--Spawns
-- local rndSpawn = math.random(0, numSpawns)
-- local counter = 0
-- if (rndSpawn == 0) then
-- local gameplayConfig = global.ocfg.gameplay --[[@as OarcConfigGameplaySettings]]
-- player.teleport(
-- game.forces[gameplayConfig.main_force_name].get_spawn_position(gameplayConfig.default_surface),
-- gameplayConfig.default_surface)
-- else
-- counter = counter + 1
-- for name, spawn in pairs(global.oc--ore.unique--Spawns --[[@as OarcUnique--SpawnsTable]]) do
-- if (counter == rndSpawn) then
-- player.teleport(spawn.position)
-- break
-- end
-- counter = counter + 1
-- end
-- end
-- end
2024-08-10 19:26:52 +02:00
2024-08-23 20:16:11 +02:00
---Check if a player has a delayed spawn
---@param player_name string
---@return boolean
function PlayerHasDelayedSpawn ( player_name )
2024-09-14 19:59:28 +02:00
for _ , delayedSpawn in pairs ( global.delayed_spawns --[[@as OarcDelayedSpawnsTable]] ) do
2024-08-23 20:16:11 +02:00
if ( delayedSpawn.playerName == player_name ) then
return true
end
end
return false
end
2024-08-28 07:36:02 +02:00
---Get the list of surfaces that are allowed for spawning.
---@return string[]
function GetAllowedSurfaces ( )
---@type string[]
local surfaceList = { }
2024-09-14 19:59:28 +02:00
for surfaceName , allowed in pairs ( global.oarc_surfaces --[[@as table<string, boolean>]] ) do
2024-08-28 07:36:02 +02:00
if allowed then
table.insert ( surfaceList , surfaceName )
end
end
return surfaceList
end
2024-08-10 19:26:52 +02:00
--[[
___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___
| __ |/ _ \ | _ \ / __ || __ | / __ || _ \ | __ |/ __ || _ _ || __ || _ _ |/ __ |
| _ || ( _ ) || /| ( __ | _ | \ __ \ | _ /| _ || ( __ | | | _ | | || ( __
| _ | \ ___ / | _ | _ \ \ ___ || ___ | | ___ /| _ | | ___ | \ ___ || ___ || _ | | ___ | \ ___ |
--]]
2024-08-11 01:29:50 +02:00
2024-09-14 19:59:28 +02:00
---Create a new player force (sets ceasefire and friendly status for all teams)
2024-08-11 01:29:50 +02:00
---@param force_name string
---@return LuaForce
2024-09-14 19:59:28 +02:00
function CreatePlayerForce ( force_name )
2024-09-17 20:19:41 +02:00
local new_force = nil
2024-08-10 19:26:52 +02:00
-- Check if force already exists
if ( game.forces [ force_name ] ~= nil ) then
log ( " Force already exists! " )
2024-09-14 19:59:28 +02:00
return CreatePlayerForce ( force_name .. " _ " ) -- Append a character to make the force name unique.
2024-08-10 19:26:52 +02:00
-- Create a new force
2024-08-12 04:16:48 +02:00
elseif ( # game.forces < MAX_FORCES ) then
2024-09-17 20:19:41 +02:00
new_force = game.create_force ( force_name )
new_force.share_chart = global.ocfg . gameplay.enable_shared_team_vision
new_force.friendly_fire = global.ocfg . gameplay.enable_friendly_fire
2024-10-18 19:29:04 +02:00
new_force.research_queue_enabled = true
2024-09-17 20:19:41 +02:00
-- SetCeaseFireBetweenAllPlayerForces()
-- SetFriendlyBetweenAllPlayerForces()
ConfigurePlayerForceRelationships ( true , true )
ConfigureEnemyForceRelationshipsForNewPlayerForce ( new_force )
2024-08-10 19:26:52 +02:00
else
2024-09-14 19:59:28 +02:00
log ( " TOO MANY FORCES!!! - CreatePlayerForce() " )
2024-08-11 01:29:50 +02:00
return game.forces [ global.ocfg . gameplay.main_force_name ]
2024-08-10 19:26:52 +02:00
end
2024-09-17 20:19:41 +02:00
return new_force
2024-08-10 19:26:52 +02:00
end
2024-08-11 01:29:50 +02:00
---Create a new player force and assign the player to it.
---@param player LuaPlayer
---@return LuaForce
2024-08-10 19:26:52 +02:00
function CreatePlayerCustomForce ( player )
2024-09-14 19:59:28 +02:00
local newForce = CreatePlayerForce ( player.name )
2024-08-10 19:26:52 +02:00
player.force = newForce
if ( newForce.name == player.name ) then
2024-09-15 19:50:07 +02:00
SendBroadcastMsg ( { " oarc-player-started-own-team " , player.name } )
2024-08-10 19:26:52 +02:00
else
2024-09-15 19:50:07 +02:00
player.print ( { " oarc-player-no-new-teams-sorry " } )
2024-08-10 19:26:52 +02:00
end
return newForce
end
2024-08-12 04:16:48 +02:00
--[[
_ _ _ _ _______ _____ ___ _ _ _ _ _ ___ _____ _ _____ ___ ___ _ _ ___
| | | | | |/ _ \ | _ _ \ \ / / _ \ __ | / _ \ | \ | | \ | |/ _ \ _ _ / _ \ _ _ | _ _ / _ \ | \ | / __ |
| | _ | | _ | / _ \ | | \ V /| _ / _ | / _ \ | . ` | . ` | ( _ ) || |/ _ \ | | | | ( _ ) | . ` \ __ \
| ____ \ ___ / _ / \ _ \ | _ | | _ | | _ | | ___ | / _ / \ _ \ _ | \ _ | _ | \ _ | \ ___ / | _ / _ / \ _ \ _ | | ___ \ ___ /| _ | \ _ | ___ /
These are LUA type annotations for development and editor support .
You can ignore this unless you ' re making changes to the mod, in which case it might be helpful.
] ]
2024-08-28 07:36:02 +02:00
---@enum SpawnTeamChoice
SPAWN_TEAM_CHOICE = {
2024-08-12 04:16:48 +02:00
join_main_team = 1 ,
join_own_team = 2 ,
2024-09-05 19:18:47 +02:00
-- join_buddy_team = 3, -- Removed in favor of separate override
2024-08-12 04:16:48 +02:00
}
---Contains the respawn point for a player. Usually this is their home base but it can be changed.
---@alias OarcPlayerSpawn { surface: string, position: MapPosition }
2024-09-14 19:59:28 +02:00
---Table of [OarcSharedSpawn](lua://OarcSharedSpawn) indexed by player name and then by surface name.
---@alias OarcPlayerRespawnsTable table<string, table<string, OarcPlayerSpawn>>
---Class for unique spawn point
---@class OarcUniqueSpawn
---@field surface_name string The surface on which the spawn is located.
---@field position MapPosition The position of the spawn on that surface.
---@field primary boolean Whether this is the primary spawn point for a player, this is the first surface they spawn on. All other spawns are secondary.
---@field moat boolean Whether the spawn has a moat or not.
---@field open_access boolean Whether the spawn is open for other players to join.
---@field host_name string The player name of the host of this spawn.
---@field join_queue string[] List of players waiting to join this spawn.
---@field joiners string[] List of players who have joined this spawn NOT including the host.
---@field buddy_name string? The other buddy player name if this is a buddy spawn.
---Table of [OarcUniqueSpawnClass](lua://OarcUniqueSpawnClass) indexed first by surface name and then by player name.
---@alias OarcUniqueSpawnsTable table<string, table<string, OarcUniqueSpawn>>
2024-08-12 04:16:48 +02:00
---Contains player ability cooldowns. Right now this only tracks changing the respawn ability.
---@alias OarcPlayerCooldown { setRespawn: number }
---Table of [OarcPlayerCooldown](lua://OarcPlayerCooldown) indexed by player name.
---@alias OarcPlayerCooldownsTable table<string, OarcPlayerCooldown>
---Temporary data used when spawning a player. Player needs to wait while the area is prepared.
2024-08-23 20:16:11 +02:00
---@alias OarcDelayedSpawn { surface: string, playerName: string, position: MapPosition, moat: boolean, delayedTick: number, final_chunk_generated: ChunkPosition }
2024-08-12 04:16:48 +02:00
---Table of [OarcDelayedSpawn](lua://OarcDelayedSpawn) indexed by player name.
---@alias OarcDelayedSpawnsTable table<string, OarcDelayedSpawn>
2024-08-28 07:36:02 +02:00
---This contains the spawn choices for a player in the spawn menu.
2024-09-05 19:18:47 +02:00
---@alias OarcSpawnChoices { surface: string, team: SpawnTeamChoice, moat: boolean, buddy: string?, distance: integer, host: string?, buddy_team: boolean }
2024-08-28 07:36:02 +02:00
---Table of [OarcSpawnChoices](lua://OarcSpawnChoices) indexed by player name.
---@alias OarcSpawnChoicesTable table<string, OarcSpawnChoices>