Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Coddle newly spawned players #57

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 97 additions & 9 deletions lua/cfc_random_spawn/module/sv_player_spawning.lua
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ local CLOSENESS_LIMIT = CFCRandomSpawn.Config.CLOSENESS_LIMIT ^ 2
local CENTER_UPDATE_INTERVAL = customSpawnConfigForMap.centerUpdateInterval or CFCRandomSpawn.Config.CENTER_UPDATE_INTERVAL
local IGNORE_BUILDERS = CFCRandomSpawn.Config.IGNORE_BUILDERS

local DYNAMIC_CENTER_MINSIZE = 1750 -- getDynamicPvpCenter starts at this radius
local DYNAMIC_CENTER_MAXSIZE = 3750 -- no bigger than this radius
local DYNAMIC_CENTER_MINSIZE = 2000 -- getDynamicPvpCenter starts at this radius
local DYNAMIC_CENTER_MAXSIZE = 4000 -- no bigger than this radius
local DYNAMIC_CENTER_MINSPAWNS = 10 -- getDynamicPvpCenter needs at least this many spawns inside the radius
local DYNAMIC_CENTER_MAXSPAWNS = 30 -- max possible spawns
local DYNAMIC_CENTER_MAXSPAWNS = 35 -- max possible spawns
local DYNAMIC_CENTER_SPAWNCOUNTMATCHPVPERS = true -- pvp center gets bigger when more people are pvping
local DYNAMIC_CENTER_IMPERFECT = true -- throw a bit of randomness in, makes pvp less stiff.

local playersWhoHaveSpawned = {}

local function defaultPvpCenter()
return pvpCenters[1]
end
Expand Down Expand Up @@ -60,6 +62,9 @@ function CFCRandomSpawn.refreshMapInfo()
CENTER_UPDATE_INTERVAL = customSpawnConfigForMap.centerUpdateInterval or CFCRandomSpawn.Config.CENTER_UPDATE_INTERVAL
IGNORE_BUILDERS = CFCRandomSpawn.Config.IGNORE_BUILDERS

-- purge this since it could now be outdated
CFCRandomSpawn.spawnsUnderARoof = nil
StrawWagen marked this conversation as resolved.
Show resolved Hide resolved

loadPvpCenters()
end

Expand Down Expand Up @@ -193,6 +198,68 @@ local function findFreeSpawnPoint( spawns, plys )
return customSpawnsForMap[math.random( 1, #customSpawnsForMap )] -- If all spawnpoints are full, just return a random spawn anywhere in the map. Super rare case.
end

-- for below func
local roofUpOffset = Vector( 0, 0, 500 )

-- find spawns that are roofed
local function getRoofedSpawns( spawns )
local spawnsUnderARoof = {}
local roofTrace = {
mask = MASK_SOLID_BRUSHONLY
}
for _, spawn in ipairs( spawns ) do
local spawnsPos = spawn.spawnPos
roofTrace.start = spawnsPos
roofTrace.endpos = spawnsPos + roofUpOffset

local roofTraceResult = util.TraceLine( roofTrace )
if roofTraceResult.Hit then
table.insert( spawnsUnderARoof, spawn )
end
end
-- not enough to get good results
if #spawnsUnderARoof <= 10 then return spawns end
return spawnsUnderARoof
end

-- find spawn that's least likely to spawn-in crash a player
local function getACheapSpawn( spawns )
local alivePlayers = getLivingPlayers()
local playerPositions = {}
for _, ply in ipairs( alivePlayers ) do
table.insert( playerPositions, ply:GetPos() )
end

-- find spawns that aren't blocked by other players
local freeSpawns = {}
for _, spawn in ipairs( spawns ) do
if spawnIsFree( spawn.spawnPos, playerPositions ) then
table.insert( freeSpawns, spawn )
end
end

-- get the ent count of every spawn
local spawnsWithCounts = {}
for _, spawn in ipairs( freeSpawns ) do
local entsThatPlyWillLoad = ents.FindInPVS( spawn.spawnPos )
spawnsWithCounts[#entsThatPlyWillLoad] = spawn

if #entsThatPlyWillLoad < 250 then break end -- this one is fine, we dont need the most perfect spawn, just one that doesn't crash.
end

-- ok which one is the least expensive
local leastEntCount = math.huge
local smallestCountSpawn
for entCountInSpawnsPvs, spawn in pairs( spawnsWithCounts ) do
if entCountInSpawnsPvs < leastEntCount then
leastEntCount = entCountInSpawnsPvs
smallestCountSpawn = spawn
end
end

return smallestCountSpawn
end

local function getPlyAvg( plys, centerPos )
if not plys or not plys[1] then return centerPos or Vector() end

Expand Down Expand Up @@ -228,7 +295,7 @@ local function getDynamicPvpCenter( measurablePlayers )

-- add some randomness
if DYNAMIC_CENTER_IMPERFECT then
local offset = VectorRand() * 800
local offset = VectorRand() * 1200
offset.z = 0
playersAveragePos = playersAveragePos + offset
end
Expand Down Expand Up @@ -325,18 +392,39 @@ function CFCRandomSpawn.getOptimalSpawnPos()
return randomFreeSpawn.spawnPos, randomFreeSpawn.spawnAngle
end

function CFCRandomSpawn.getCheapSpawn()
if not CFCRandomSpawn.spawnsUnderARoof then
CFCRandomSpawn.spawnsUnderARoof = getRoofedSpawns( customSpawnsForMap )
end

-- use spawns under a roof if we can, because this function is pretty expensive,
-- finds all entities in every spawnpoint's PVS, we want to run it on as few spawns as possible.
local aCheapSpawn = getACheapSpawn( CFCRandomSpawn.spawnsUnderARoof )
return aCheapSpawn.spawnPos, aCheapSpawn.spawnAngle
end

function CFCRandomSpawn.handlePlayerSpawn( ply )
if not mapHasCustomSpawns then return end
if not ( ply and IsValid( ply ) ) then return end
if IsValid( ply.LinkedSpawnPoint ) then return end

local optimalSpawnPosition, optimalSpawnAngles = CFCRandomSpawn.getOptimalSpawnPos()
timer.Simple( 0, function()
wrefgtzweve marked this conversation as resolved.
Show resolved Hide resolved
local needsACarefulFirstSpawn = not playersWhoHaveSpawned[ply] and #player.GetAll() > 20
playersWhoHaveSpawned[ply] = true

local optimalSpawnPosition, optimalSpawnAngles = nil, nil
if needsACarefulFirstSpawn then -- need a careful spawn, player might crash out if we just put them anywhere
optimalSpawnPosition, optimalSpawnAngles = CFCRandomSpawn.getCheapSpawn()
else
optimalSpawnPosition, optimalSpawnAngles = CFCRandomSpawn.getOptimalSpawnPos()
end

ply:SetPos( optimalSpawnPosition )
ply:SetPos( optimalSpawnPosition )

if optimalSpawnAngles then
ply:SetEyeAngles( optimalSpawnAngles )
end
if optimalSpawnAngles then
ply:SetEyeAngles( optimalSpawnAngles )
end
end )
end

hook.Add( "PlayerSpawn", "CFC_RandomSpawn_ChooseOptimalSpawnpoint", CFCRandomSpawn.handlePlayerSpawn )
Expand Down
2 changes: 1 addition & 1 deletion lua/cfc_random_spawn/sv_config.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
CFCRandomSpawn.Config.DEFAULT_CENTER_CUTOFF = 3000 -- Default cutoff range from the most popular pvp center, where players further away from this will be ignored. The system tries to place you closest to everyone else.
CFCRandomSpawn.Config.CLOSENESS_LIMIT = 100 -- Will not choose spawnpoints that are within this many units of a valid player (i.e. a living pvper).
CFCRandomSpawn.Config.CLOSENESS_LIMIT = 400 -- Will not choose spawnpoints that are within this many units of a valid player (i.e. a living pvper).
CFCRandomSpawn.Config.SELECTION_SIZE = 16 -- Max number of 'ideal' spawnpoints to select from randomly.
CFCRandomSpawn.Config.CENTER_UPDATE_INTERVAL = 120 -- The gap (in seconds) between each center popularity update. If set to 0, will update on every respawn.
CFCRandomSpawn.Config.IGNORE_BUILDERS = true -- Should 'center popularity' and player position average not care about builders? Requires a PvP addon which uses a function of the form PLAYER:IsInPvp()
Expand Down