8 [
Attribute(
"20",
params:
"1 inf",
desc:
"Target cached spawnpoint amount for quicker simultaneous spawn")]
9 protected int m_iCachedSpawnPointCount;
12 protected float m_fSafeDistance;
14 [
Attribute(
"2500",
params:
"1 inf",
desc:
"Target amount of cells to split the terrain into")]
15 protected int m_iTargetCellDensity;
17 [
Attribute(
"10",
params:
"1 inf",
desc:
"Number of spawn points to generate per batch frame")]
18 protected int m_iGenerationBatchSize;
21 protected int m_iEmergencyAttempts;
24 protected ref array<ref SCR_SpawnPointBlacklistZone> m_aBlacklistZones;
26 protected const float MAX_SURFACE_DELTA = 0.8;
27 protected const float PLAYER_CYLINDER_HEIGHT = 2;
28 protected const float PLAYER_CYLINDER_RADIUS = 0.5;
29 protected const float WATER_LEVEL_THRESHOLD = 1;
30 protected const float RAYCAST_HEIGHT_OFFSET = 10;
31 protected const float MIN_CELL_SIZE = 10;
32 protected const float JUMP_GOLDEN_RATIO = 0.6180339887;
34 protected ref array<vector> m_aPrecalculatedSpawns = {};
61 float worldWidth, worldLength, totalArea, cellArea;
62 float halfCell, safeDistLimit, safeDistSq, effectiveRadius;
63 int blCount, basesCount;
64 bool bNeedsRandomSpawns =
false;
67 array<SCR_SpawnPoint> initialSpawns;
68 array<vector> staticBaseOrigins = {};
69 array<vector> blacklistCenters = {};
70 array<float> blacklistSqRadii = {};
87 if (spawnPoint.IsSpawnPointRandom())
89 bNeedsRandomSpawns =
true;
101 staticBaseOrigins.Insert(spawnPoint.GetOrigin());
106 if (!bNeedsRandomSpawns)
112 GetGame().GetWorldEntity().GetTerrain(0, 0).GetTerrainBoundBox(mins, maxs);
113 worldWidth = maxs[0] - mins[0];
114 worldLength = maxs[2] - mins[2];
115 totalArea = worldWidth * worldLength;
117 cellArea = totalArea / m_iTargetCellDensity;
128 if (m_aBlacklistZones)
135 blacklistCenters.Insert(
zone.m_vCenter);
136 effectiveRadius =
zone.m_fRadius + halfCell;
137 blacklistSqRadii.Insert(effectiveRadius * effectiveRadius);
141 safeDistLimit = m_fSafeDistance + halfCell;
142 safeDistSq = safeDistLimit * safeDistLimit;
143 blCount = blacklistCenters.Count();
144 basesCount = staticBaseOrigins.Count();
146 for (
float x = mins[0], maxX = maxs[0]; x < maxX; x +=
m_fCellSize)
148 for (
float z = mins[2], maxY = maxs[2]; z < maxY; z +=
m_fCellSize)
150 cellCenter[0] = x + halfCell;
151 cellCenter[2] = z + halfCell;
153 if (m_World.GetSurfaceY(cellCenter[0], cellCenter[2]) <= WATER_LEVEL_THRESHOLD)
157 for (
int i; i < blCount; i++)
159 if (
vector.DistanceSqXZ(cellCenter, blacklistCenters[i]) <= blacklistSqRadii[i])
166 if (cellIsSafe && basesCount > 0)
168 for (
int i; i < basesCount; i++)
170 if (
vector.DistanceSqXZ(cellCenter, staticBaseOrigins[i]) < safeDistSq)
198 for (
int i = m_aPrecalculatedSpawns.Count() - 1; i >= 0; i--)
200 candidatePos = m_aPrecalculatedSpawns[i];
201 m_aPrecalculatedSpawns.Remove(i);
205 outPos = candidatePos;
212 int emergencyAttempts = 0;
213 while (emergencyAttempts < m_iEmergencyAttempts && !
m_aSafeCells.IsEmpty())
217 outPos = emergencyPos;
268 if (availableCells == 0)
272 int jumpAmount = availableCells * JUMP_GOLDEN_RATIO;
279 float surfaceY, traceCoef, hitY;
281 for (
int attempt = 0; attempt < 20; attempt++)
283 testPos = cellCenter;
287 surfaceY = m_World.GetSurfaceY(testPos[0], testPos[2]);
288 testPos[1] = surfaceY;
290 if (testPos[1] <= WATER_LEVEL_THRESHOLD)
307 if (hitY - surfaceY > MAX_SURFACE_DELTA)
310 testPos[1] =
Math.Max(hitY + 0.01, surfaceY);
319 int lastIndex = availableCells - 1;
332 if (!currentWorldSpawns)
338 if (!spawnPoint || !spawnPoint.IsSpawnPointEnabled())
341 spFaction = spawnPoint.GetFactionKey();
342 if (spFaction != playerFaction && spFaction !=
string.Empty)