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

Syntax and logic fixes #8

Merged
merged 8 commits into from
Mar 10, 2020
125 changes: 67 additions & 58 deletions lua/autorun/cfc_random_spawn.lua
Original file line number Diff line number Diff line change
@@ -1,58 +1,67 @@

cfcRandomSpawn = cfcRandomSpawn or {
config = {},

IDENTIFIER = "cfcRandomSpawn",
NICE_NAME = "cfcRandomSpawn"
}


local version = 1

if not frile or frile.VERSION < version then
frile = {
VERSION = version,

STATE_SERVER = 0,
STATE_CLIENT = 1,
STATE_SHARED = 2
}

function frile.includeFile( filename, state )
if state == frile.STATE_SHARED or filename:find( "sh_" ) then
if SERVER then AddCSLuaFile( filename ) end
include( filename )
elseif state == frile.STATE_SERVER or SERVER and filename:find( "sv_" ) then
include( filename )
elseif state == frile.STATE_CLIENT or filename:find( "cl_" ) then
if SERVER then AddCSLuaFile( filename )
else include( filename ) end
end
end

function frile.includeFolder( currentFolder, ignoreFilesInFolder, ignoreFoldersInFolder )
if file.Exists( currentFolder .. "sh_frile.lua", "LUA" ) then
frile.includeFile( currentFolder .. "sh_frile.lua" )

return
end

local files, folders = file.Find( currentFolder .. "*", "LUA" )

if not ignoreFilesInFolder then
for _, File in ipairs( files ) do
frile.includeFile( currentFolder .. File )
end
end

if not ignoreFoldersInFolder then
for _, folder in ipairs( folders ) do
frile.includeFolder( currentFolder .. folder .. "/" )
end
end
end
end

-- Do not adjust the load order. You must first load the libraries, than module and then languages.
frile.includeFolder( "cfc_random_spawn/", false, true )
frile.includeFolder( "cfc_random_spawn/module/" )

CFCRandomSpawn = CFCRandomSpawn or {
config = {},

IDENTIFIER = "cfcRandomSpawn",
NICE_NAME = "cfcRandomSpawn"
}

local version = 1

-- A valid instance already exists
if CFCRandomSpawnInit and CFCRandomSpawnInit.VERSION >= version then return end

CFCRandomSpawnInit = {
VERSION = version,

STATE_SERVER = 0,
STATE_CLIENT = 1,
STATE_SHARED = 2
}

local function isSharedFile( filename, state )
return state == CFCRandomSpawnInit.STATE_SHARED or filename:find( "sh_" )
end

local function isServerFile( filename, state )
return state == CFCRandomSpawnInit.STATE_SERVER or ( SERVER and filename:find( "sv_" ) )
end

local function isClientFile( filename, state )
return state == CFCRandomSpawnInit.STATE_CLIENT or filename:find( "cl_" )
end

function CFCRandomSpawnInit.includeFile( filename, state )
if isSharedFile( filename, state ) then
if SERVER then AddCSLuaFile( filename ) end
include( filename )
elseif isServerFile( filename, state ) then
include( filename )
elseif isClientFile( filename, state ) then
if SERVER then AddCSLuaFile( filename ) else include( filename ) end
end
end

function CFCRandomSpawnInit.includeFolder( currentFolder, ignoreFilesInFolder, ignoreFoldersInFolder )
if file.Exists( currentFolder .. "sh_CFCRandomSpawnInit.lua", "LUA" ) then
return CFCRandomSpawnInit.includeFile( currentFolder .. "sh_CFCRandomSpawnInit.lua" )
end

local files, folders = file.Find( currentFolder .. " * ", "LUA" )

if not ignoreFilesInFolder then
for _, filename in ipairs( files ) do
CFCRandomSpawnInit.includeFile( currentFolder .. filename )
end
end

if not ignoreFoldersInFolder then
for _, folder in ipairs( folders ) do
CFCRandomSpawnInit.includeFolder( currentFolder .. folder .. "/" )
end
end
end

-- Do not adjust the load order. You must first load the libraries, then module, and then languages.
CFCRandomSpawnInit.includeFolder( "cfc_random_spawn/", false, true )
CFCRandomSpawnInit.includeFolder( "cfc_random_spawn/module/" )
148 changes: 79 additions & 69 deletions lua/cfc_random_spawn/module/sv_player_spawning.lua
Original file line number Diff line number Diff line change
@@ -1,69 +1,79 @@
local customSpawnsForMap = cfcRandomSpawn.config.CUSTOM_SPAWNS[game.GetMap()]
local mapHasCustomSpawns = customSpawnsForMap ~= nil

hook.Remove( "PlayerSpawn", "CFC_PlayerSpawning" )

if mapHasCustomSpawns then
cfcRandomSpawn.spawnPointRankings = cfcRandomSpawn.spawnPointRankings or {}

local function getMeasurablePlayers()
local measurablePlayers = {}
for _, ply in pairs( player.GetHumans() ) do
if ( ply:Alive() ) then
table.insert( measurablePlayers, ply )
end
end

return measurablePlayers
end

-- This is operating on a model of spawn points and players as electrons putting a force on each other
-- The best spawn point is the spawn point under the smallest sum of forces then. This is why we use physics terms here
local function getPlayerForceFromCustomSpawn( spawn )
local totalDistanceSquared = 0
local measurablePlayers = getMeasurablePlayers()

for _, ply in pairs( measurablePlayers ) do
local plyDistanceSqr = ( ply:GetPos():DistToSqr( spawn ) )
if plyDistanceSqr < 30 * 30 then plyDistanceSqr = 1 end
totalDistanceSquared = totalDistanceSquared + 1 / plyDistanceSqr
end

return totalDistanceSquared
end

function cfcRandomSpawn.getOptimalSpawnPosition()
local randomSpawn = math.random( 1, 4 )
return cfcRandomSpawn.spawnPointRankings[randomSpawn]["spawn"]
end

function cfcRandomSpawn.updateSpawnPointRankings()
for _, spawn in pairs( customSpawnsForMap ) do
local playerNetForce = getPlayerForceFromCustomSpawn( spawn )

local spawnDistanceData = {}
spawnDistanceData["spawn"] = spawn

spawnDistanceData["inverse-distance-squared"] = playerNetForce
table.insert( PlayerIDSFromSpawn, spawnDistanceData ) -- ISD == Inverse Distance Squared
end


cfcRandomSpawn.spawnPointRankings = PlayerIDSFromSpawn
table.SortByMember( cfcRandomSpawn.spawnPointRankings, "inverse-distance-squared", true )
end

-- timer.Create( "CFC_UpdateOptimalSpawnPosition", 0.5, 0, cfcRandomSpawn.updateSpawnPointRankings )

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

cfcRandomSpawn.updateSpawnPointRankings()
local optimalSpawnPosition = cfcRandomSpawn.getOptimalSpawnPosition()

ply:SetPos( optimalSpawnPosition )
end

hook.Add( "PlayerSpawn", "CFC_PlayerSpawning", cfcRandomSpawn.handlePlayerSpawn )
end
CFCRandomSpawn = CFCRandomSpawn or {}

local customSpawnsForMap = CFCRandomSpawn.config.CUSTOM_SPAWNS[game.GetMap()]
local mapHasCustomSpawns = customSpawnsForMap ~= nil

hook.Remove( "PlayerSpawn", "CFC_PlayerSpawning" )

if not mapHasCustomSpawns then return end

CFCRandomSpawn.spawnPointRankings = CFCRandomSpawn.spawnPointRankings or {}

local function getMeasurablePlayers()
local measurablePlayers = {}
for _, ply in pairs( player.GetHumans() ) do
if ( ply:Alive() ) then
table.insert( measurablePlayers, ply )
end
end

return measurablePlayers
end

-- This is operating on a model of spawn points and players as electrons putting a force on each other
-- The best spawn point is the spawn point under the smallest sum of forces then. This is why we use physics terms here
local distSqr = 900 -- ( 30^2 )
local randMin, randMax = 1, 4
local function getPlayerForceFromCustomSpawn( spawn )
local totalDistanceSquared = 0
local measurablePlayers = getMeasurablePlayers()

for _, ply in pairs( measurablePlayers ) do
local plyDistanceSqr = ( ply:GetPos():DistToSqr( spawn ) )
if plyDistanceSqr < distSqr then plyDistanceSqr = 1 end
totalDistanceSquared = totalDistanceSquared + 1 / plyDistanceSqr
end

return totalDistanceSquared
end

function CFCRandomSpawn.getOptimalSpawnPosition()
local randomSpawn = math.random( randMin, randMax )
return CFCRandomSpawn.spawnPointRankings[randomSpawn]["spawn_pos"]
end

function CFCRandomSpawn.updateSpawnPointRankings( ply )
local playerIDSFromSpawns = {}
local playerPVPStatus = ply:GetNWBool( "CFC_PvP_Mode", false )

for _, spawn in pairs( customSpawnsForMap ) do
local isPvpSpawn = spawn["isPvpSpawn"]

if playerPVPStatus and isPvpSpawn then
local spawnPosition = spawn["spawn_pos"]
local playerNetForce = getPlayerForceFromCustomSpawn( spawnPosition )
local spawnDistanceData = {}
spawnDistanceData["spawn"] = spawnPosition
spawnDistanceData["inverse-distance-squared"] = playerNetForce

table.insert( playerIDSFromSpawn, spawnDistanceData ) -- IDS == Inverse Distance Squared
end
CFCRandomSpawn.spawnPointRankings = playerIDSFromSpawns
table.SortByMember( CFCRandomSpawn.spawnPointRankings, "inverse-distance-squared", true )
end

-- timer.Create( "CFC_UpdateOptimalSpawnPosition", 0.5, 0, CFCRandomSpawn.updateSpawnPointRankings )
end

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

CFCRandomSpawn.updateSpawnPointRankings( ply )
local optimalSpawnPosition = CFCRandomSpawn.getOptimalSpawnPosition()

ply:SetPos( optimalSpawnPosition )
end

hook.Add( "PlayerSpawn", "CFC_PlayerSpawning", CFCRandomSpawn.handlePlayerSpawn )

46 changes: 24 additions & 22 deletions lua/cfc_random_spawn/sh_config.lua
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@

cfcRandomSpawn.config.MAXIMUM_DISTANCE_FROM_PLAYERS = 500 -- The maximum distance it checks for players for when you spawn. You will automatically spawn where the least players are.

cfcRandomSpawn.config.CUSTOM_SPAWNS = {}
cfcRandomSpawn.config.CUSTOM_SPAWNS["gm_bluehills_test3"] = {
Vector( 1104.6744384766, 131.18112182617, 64.03125 ),
Vector( 385.55798339844, 157.09564208984, 64.03125 ),
Vector( 1272.7860107422, -485.29537963867, 264.03125 ),
Vector( 1117.4444580078, -1345.0144042969, 64.03125 ),
Vector( 1458.7664794922, -484.67016601563, 64.03125 ),
Vector( 971.29565429688, 401.40603637695, 264.03125 ),
Vector( 392.85052490234, 146.80117797852, 264.03125 ),
Vector( -456.48602294922, -442.73083496094, 264.03125 ),
Vector( 1148.5478515625, 90.367889404297, 456.03125 ),
Vector( -214.91020202637, -623.013671875, 64.03125 ),
Vector( -726.35272216797, -540.54400634766, 64.03125 ),
Vector( -809.68939208984, 1255.5084228516, 464.03125 ),
Vector( 425.38064575195, 1636.2911376953, 64.968605041504 ),
Vector( -974.1875, 130.53991699219, 128.03125 ),
Vector( -1357.5144042969, 1473.0662841797, 128.03125 ),
Vector( -1000.3621826172, 1044.5682373047, 464.03125 )
}

cfcRandomSpawn.config.MAXIMUM_DISTANCE_FROM_PLAYERS = 500 -- The maximum distance it checks for players for when you spawn_pos. You will automatically spawn where the least players are.

cfcRandomSpawn.config.CUSTOM_SPAWNS = {}
cfcRandomSpawn.config.CUSTOM_SPAWNS["gm_bluehills_test3"] = {

{ ["spawn_pos"] = Vector( 1104.6744384766, 131.18112182617, 64.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( 385.55798339844, 157.09564208984, 64.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( 1272.7860107422, -485.29537963867, 264.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( 1117.4444580078, -1345.0144042969, 64.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( 1458.7664794922, -484.67016601563, 64.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( 971.29565429688, 401.40603637695, 264.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( 392.85052490234, 146.80117797852, 264.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( -456.48602294922, -442.73083496094, 264.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( 1148.5478515625, 90.367889404297, 456.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( -214.91020202637, -623.013671875, 64.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( -726.35272216797, -540.54400634766, 64.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( -809.68939208984, 1255.5084228516, 464.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( 425.38064575195, 1636.2911376953, 64.968605041504 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( -974.1875, 130.53991699219, 128.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( -1357.5144042969, 1473.0662841797, 128.03125 ), ["pvp_spawn"] = true },
{ ["spawn_pos"] = Vector( -1000.3621826172, 1044.5682373047, 464.03125 ), ["pvp_spawn"] = true }

}