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