2017-10-20 02:00:49 +00:00
|
|
|
// Helper actor to find all possible spawnpoints for humans on the map.
|
|
|
|
Class ExtSpawnPointHelper extends Info
|
|
|
|
transient;
|
|
|
|
|
|
|
|
var transient array<NavigationPoint> PendingList,CheckedList;
|
|
|
|
var array<Actor> ValidSpawnSpots;
|
|
|
|
|
2020-11-28 20:04:55 +00:00
|
|
|
static final function ExtSpawnPointHelper FindHelper(WorldInfo Level)
|
2017-10-20 02:00:49 +00:00
|
|
|
{
|
|
|
|
local ExtSpawnPointHelper H;
|
|
|
|
|
|
|
|
foreach Level.DynamicActors(class'ExtSpawnPointHelper',H)
|
|
|
|
return H;
|
|
|
|
return Level.Spawn(class'ExtSpawnPointHelper');
|
|
|
|
}
|
2020-11-28 21:54:57 +00:00
|
|
|
|
2017-10-20 02:00:49 +00:00
|
|
|
final function Actor PickBestSpawn()
|
|
|
|
{
|
|
|
|
local Actor N,BestN;
|
|
|
|
local KFPawn P;
|
|
|
|
local float Score,BestScore,Dist;
|
|
|
|
local KFPawn_Human H;
|
|
|
|
|
|
|
|
BestN = None;
|
|
|
|
BestScore = 0;
|
|
|
|
foreach ValidSpawnSpots(N)
|
|
|
|
{
|
2020-11-28 20:12:58 +00:00
|
|
|
if (Rand(4)==0)
|
2017-10-20 02:00:49 +00:00
|
|
|
{
|
|
|
|
Score = FRand();
|
|
|
|
foreach WorldInfo.AllPawns(class'KFPawn',P,N.Location,2000.f)
|
|
|
|
{
|
2020-11-28 20:12:58 +00:00
|
|
|
if (!P.IsAliveAndWell())
|
2017-10-20 02:00:49 +00:00
|
|
|
continue;
|
|
|
|
Dist = VSize(N.Location-P.Location);
|
2020-11-28 20:12:58 +00:00
|
|
|
if (FastTrace(P.Location,N.Location))
|
2017-10-20 02:00:49 +00:00
|
|
|
Dist*=0.75;
|
2020-11-28 20:12:58 +00:00
|
|
|
if (P.IsA('KFPawn_Human'))
|
2017-10-20 02:00:49 +00:00
|
|
|
Score+=(3000.f-Dist)/2000.f;
|
|
|
|
else Score-=(3500.f-Dist)/2500.f;
|
|
|
|
}
|
2020-11-28 20:12:58 +00:00
|
|
|
if (BestN==None || Score>BestScore)
|
2017-10-20 02:00:49 +00:00
|
|
|
{
|
|
|
|
BestN = N;
|
|
|
|
BestScore = Score;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// See if can spawn ontop of other players.
|
|
|
|
foreach WorldInfo.AllPawns(class'KFPawn_Human',H)
|
|
|
|
{
|
2020-11-28 20:12:58 +00:00
|
|
|
if (!H.IsAliveAndWell() || H.Physics==PHYS_Falling || (ExtHumanPawn(H)!=None && ExtHumanPawn(H).bFeigningDeath))
|
2017-10-20 02:00:49 +00:00
|
|
|
continue;
|
|
|
|
Score = FRand();
|
|
|
|
foreach WorldInfo.AllPawns(class'KFPawn',P,H.Location,2000.f)
|
|
|
|
{
|
2020-11-28 20:12:58 +00:00
|
|
|
if (!P.IsAliveAndWell())
|
2017-10-20 02:00:49 +00:00
|
|
|
continue;
|
|
|
|
Dist = VSize(H.Location-P.Location);
|
2020-11-28 20:12:58 +00:00
|
|
|
if (FastTrace(P.Location,H.Location))
|
2017-10-20 02:00:49 +00:00
|
|
|
Dist*=0.75;
|
2020-11-28 20:12:58 +00:00
|
|
|
if (P.IsA('KFPawn_Human'))
|
2017-10-20 02:00:49 +00:00
|
|
|
Score+=(3000.f-Dist)/3000.f;
|
|
|
|
else Score-=(3500.f-Dist)/3500.f;
|
|
|
|
}
|
2020-11-28 20:12:58 +00:00
|
|
|
if (BestN==None || Score>BestScore)
|
2017-10-20 02:00:49 +00:00
|
|
|
{
|
|
|
|
BestN = H;
|
|
|
|
BestScore = Score;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return BestN;
|
|
|
|
}
|
|
|
|
|
|
|
|
function PreBeginPlay()
|
|
|
|
{
|
|
|
|
SetTimer(0.2,false,'InitChecker');
|
|
|
|
}
|
2020-11-28 21:54:57 +00:00
|
|
|
|
2017-10-20 02:00:49 +00:00
|
|
|
function InitChecker()
|
|
|
|
{
|
|
|
|
local PlayerStart PS,Fallback;
|
|
|
|
|
|
|
|
foreach WorldInfo.AllNavigationPoints(class'PlayerStart',PS)
|
|
|
|
{
|
|
|
|
Fallback = PS;
|
2020-11-28 20:12:58 +00:00
|
|
|
if (PS.bEnabled && PS.TeamIndex==0)
|
2017-10-20 02:00:49 +00:00
|
|
|
{
|
|
|
|
CheckSpawn(PS);
|
2020-11-28 20:12:58 +00:00
|
|
|
if (PendingList.Length!=0)
|
2017-10-20 02:00:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-11-28 20:12:58 +00:00
|
|
|
if (PendingList.Length==0 && Fallback!=None)
|
2017-10-20 02:00:49 +00:00
|
|
|
CheckSpawn(Fallback);
|
|
|
|
SetTimer(0.001,true,'NextCheck');
|
|
|
|
}
|
2020-11-28 21:54:57 +00:00
|
|
|
|
2017-10-20 02:00:49 +00:00
|
|
|
function NextCheck()
|
|
|
|
{
|
|
|
|
local NavigationPoint N;
|
|
|
|
local byte i;
|
|
|
|
|
2020-11-28 20:12:58 +00:00
|
|
|
if (PendingList.Length!=0)
|
2017-10-20 02:00:49 +00:00
|
|
|
{
|
2020-11-28 20:12:58 +00:00
|
|
|
while (++i<5 && PendingList.Length!=0)
|
2017-10-20 02:00:49 +00:00
|
|
|
{
|
|
|
|
N = PendingList[PendingList.Length-1];
|
|
|
|
PendingList.Remove(PendingList.Length-1,1);
|
|
|
|
CheckSpawn(N);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ClearTimer('NextCheck');
|
|
|
|
CheckedList.Length = 0;
|
|
|
|
}
|
|
|
|
}
|
2020-11-28 21:54:57 +00:00
|
|
|
|
2020-11-28 20:04:55 +00:00
|
|
|
final function CheckSpawn(NavigationPoint N)
|
2017-10-20 02:00:49 +00:00
|
|
|
{
|
|
|
|
local vector V;
|
|
|
|
local ReachSpec R;
|
|
|
|
local NavigationPoint E;
|
|
|
|
local KFPawnBlockingVolume P;
|
|
|
|
|
|
|
|
V = N.Location;
|
2020-11-28 20:12:58 +00:00
|
|
|
if (N.MaxPathSize.Radius>30 && N.MaxPathSize.Height>80 && FindSpot(vect(36,36,86),V) && KFDoorMarker(N)==None && PickupFactory(N)==None)
|
2017-10-20 02:00:49 +00:00
|
|
|
{
|
|
|
|
//DrawDebugLine(V,V+vect(0,0,50),255,255,255,true);
|
|
|
|
ValidSpawnSpots.AddItem(N);
|
|
|
|
}
|
|
|
|
CheckedList.AddItem(N);
|
|
|
|
|
|
|
|
foreach N.PathList(R)
|
|
|
|
{
|
|
|
|
E = R.GetEnd();
|
2020-11-28 20:12:58 +00:00
|
|
|
if (E==None || R.CollisionRadius<30 || R.CollisionHeight<80 || R.Class==Class'ProscribedReachSpec')
|
2017-10-20 02:00:49 +00:00
|
|
|
{
|
2020-11-28 20:12:58 +00:00
|
|
|
//if (E!=None)
|
2017-10-20 02:00:49 +00:00
|
|
|
// DrawDebugLine(E.Location,N.Location,255,255,0,true);
|
|
|
|
continue;
|
|
|
|
}
|
2020-11-28 20:12:58 +00:00
|
|
|
if (CheckedList.Find(E)!=INDEX_NONE)
|
2017-10-20 02:00:49 +00:00
|
|
|
continue;
|
|
|
|
// DO NOT go through any blocking volumes.
|
|
|
|
V = (N.Location+E.Location) * 0.5;
|
|
|
|
foreach OverlappingActors(class'KFPawnBlockingVolume',P,VSize(N.Location-V),V)
|
|
|
|
{
|
2020-11-28 20:12:58 +00:00
|
|
|
if (P.bBlockPlayers && TraceComponent(V,V,P.CollisionComponent,E.Location,N.Location,vect(36,36,50)))
|
2017-10-20 02:00:49 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-11-28 20:12:58 +00:00
|
|
|
if (P==None)
|
2017-10-20 02:00:49 +00:00
|
|
|
{
|
|
|
|
//DrawDebugLine(E.Location,N.Location,0,255,0,true);
|
|
|
|
PendingList.AddItem(E);
|
|
|
|
}
|
|
|
|
//else DrawDebugLine(E.Location,N.Location,255,0,0,true);
|
|
|
|
}
|
|
|
|
}
|