rott/kf_sources/KFMod/Classes/KFTeamProgressVolume.uc
2026-07-14 20:27:09 +07:00

228 lines
5.6 KiB
Ucode

// Ok, what this does is it basically checks to see if
// every player on the server is in a certain area, and if
// that is satisfied, it calls the volume's event.
// This is useful for story based missions where you need to
// gather your whole team before you can progress.
// Alex
class KFTeamProgressVolume extends PhysicsVolume;
var int NumTouching;
var() bool bOff; // Whatever if the volume should start disabled.
// Configurable Variables
var() bool bDisableAfterTriggered; // default true. Changes this if you'd like to the volume to continue checking player counts.
var() bool bTimeOut; // default false. If true, the volume's event will fire even if not all players are inside.
var() int TimeOutSeconds; // Number of seconds before timeout is called
var() float PlayerThreshold; // Min percent of total players who HAVE to be in the volume before the timeout can go through.
var() bool bTeleportWhenAbsent; // Teleport all players here that are not inside the volume (to in volume playerstarts).
var() bool bRepawnDeadPlayers; // All fallen players will respawn here (to in volume playerstarts).
var() enum EVolTriggerAct
{
VTR_ToggleDisabled,
VTR_Untrigger,
VTR_TurnOff,
VTR_TurnOn,
VTR_FinishEvent
} VolumeTriggerAction;
var array<PlayerStart> PSList;
simulated function PostBeginPlay()
{
local NavigationPoint N;
local PlayerStart PS;
local int i;
Super.PostBeginPlay();
if( Level.NetMode==NM_Client )
return;
For( N=Level.NavigationPointList; N!=None; N=N.NextNavigationPoint )
{
PS = PlayerStart(N);
if( PS!=None && PS.PhysicsVolume==Self )
{
PS.bEnabled = False;
PSList.Length = i+1;
PSList[i] = PS;
i++;
}
}
}
function PlayerStart PickRandomSpawnPoint()
{
Return PSList[Rand(PSList.Length)];
}
function int CountAlivePlayers()
{
local Controller C;
local int i;
For( C=Level.ControllerList; C!=None; C=C.NextController )
{
if( PlayerController(C)!=None && C.Pawn!=None && C.Pawn.Health>0 )
i++;
}
Return i;
}
simulated event PawnEnteredVolume(Pawn Other)
{
local vector HitLocation,HitNormal;
local Actor SpawnedEntryActor;
if ( bWaterVolume && (Level.TimeSeconds - Other.SplashTime > 0.3) && (PawnEntryActor != None) && !Level.bDropDetail && (Level.DetailMode != DM_Low) && EffectIsRelevant(Other.Location,false) )
{
if ( !TraceThisActor(HitLocation, HitNormal, Other.Location - Other.CollisionHeight*vect(0,0,1), Other.Location + Other.CollisionHeight*vect(0,0,1)) )
{
SpawnedEntryActor = Spawn(PawnEntryActor,Other,,HitLocation,rot(16384,0,0));
if( SpawnedEntryActor!=None )
SpawnedEntryActor.RemoteRole = ROLE_None;
}
}
if( bOff || Level.NetMode==NM_Client || PlayerController(Other.Controller)==None )
return;
NumTouching++;
CheckActivity();
}
function CheckActivity()
{
local int i;
i = CountAlivePlayers();
if( i>=NumTouching )
{
if( IsInState('TimingOut') )
GoToState('');
FinishedEvent();
}
else if( NumTouching>0 && bTimeOut && (float(i)/float(NumTouching))>=PlayerThreshold )
{
SetTimer(2,True);
if( !IsInState('TimingOut') )
GoToState('TimingOut');
}
else
{
if( IsInState('TimingOut') )
GoToState('');
if( NumTouching<=0 )
SetTimer(0,False);
else SetTimer(2,True);
}
}
function Timer()
{
CheckActivity();
}
State TimingOut
{
Begin:
Sleep(TimeOutSeconds);
FinishedEvent();
}
function FinishedEvent()
{
local Controller C;
local PlayerStart P;
if( bOff )
Return;
if( PSList.Length>0 && KFSPGameType(Level.Game)!=None )
KFSPGameType(Level.Game).SpawnVolume = Self;
if( bTeleportWhenAbsent )
{
For( C=Level.ControllerList; C!=None; C=C.NextController )
{
if( PlayerController(C)!=None && C.Pawn!=None && C.Pawn.Health>0 && C.Pawn.PhysicsVolume!=Self )
{
P = PickRandomSpawnPoint();
if( P==None )
Continue;
C.Pawn.PlayTeleportEffect(True,False);
C.Pawn.SetLocation(P.Location);
C.ClientSetRotation(P.Rotation);
C.Pawn.PlayTeleportEffect(False,True);
}
}
}
if( bRepawnDeadPlayers )
{
For( C=Level.ControllerList; C!=None; C=C.NextController )
{
if( PlayerController(C)!=None && (C.Pawn==None || C.Pawn.Health<=0) && !C.PlayerReplicationInfo.bOnlySpectator )
{
C.GotoState('PlayerWaiting');
C.PlayerReplicationInfo.bOutOfLives = false;
C.PlayerReplicationInfo.NumLives = 0;
C.ServerReStartPlayer();
PlayerController(C).ClientSetViewTarget(C.Pawn);
PlayerController(C).SetViewTarget(C.Pawn);
}
}
}
TriggerEvent(Event,self,None);
if(bDisableAfterTriggered)
{
SetTimer(0,False);
bOff = true;
}
}
simulated event PawnLeavingVolume(Pawn P)
{
if( bOff || Level.NetMode==NM_Client || PlayerController(P.Controller)==None )
return;
NumTouching--;
CheckActivity();
}
function PlayerPawnDiedInVolume(Pawn Other)
{
if( bOff || Level.NetMode==NM_Client || PlayerController(Other.Controller)==None )
return;
NumTouching --;
CheckActivity();
}
function Trigger( actor Other, pawn EventInstigator )
{
Switch( VolumeTriggerAction )
{
Case VTR_ToggleDisabled:
bOff = !bOff;
Return;
Case VTR_Untrigger:
if( NumTouching>0 )
UntriggerEvent(Event,self, EventInstigator );
Return;
Case VTR_TurnOff:
bOff = True;
Return;
Case VTR_TurnOn:
bOff = False;
Return;
Case VTR_FinishEvent:
bOff = False;
FinishedEvent();
Return;
}
}
defaultproperties
{
bDisableAfterTriggered=true;
PlayerThreshold=0.75 // 75% players is enough to execute
bTimeOut=false
TimeOutSeconds=60
bStatic=False
bNoDelete=True
RemoteRole=ROLE_None
}