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

443 lines
11 KiB
Ucode

//=============================================================================
// PlayerReplicationInfo.
//=============================================================================
class PlayerReplicationInfo extends ReplicationInfo
native nativereplication;
var float Score; // Player's current score.
var float Deaths; // Number of player's deaths.
var Decoration HasFlag;
var byte Ping;
var Volume PlayerVolume;
var ZoneInfo PlayerZone;
var int NumLives;
var string PlayerName; // Player name, or blank if none.
var string CharacterName, OldCharacterName;
var string OldName, PreviousName; // Temporary value.
var int PlayerID; // Unique id number.
var TeamInfo Team; // Player Team
var int TeamID; // Player position in team.
var class<VoicePack> VoiceType;
var string VoiceTypeName;
var bool bAdmin; // Player logged in as Administrator
//if _RO_
var bool bSilentAdmin;
//end _RO_
var bool bIsFemale;
var bool bIsSpectator;
var bool bOnlySpectator;
var bool bWaitingPlayer;
var bool bReadyToPlay;
var bool bOutOfLives;
var bool bBot;
var bool bWelcomed; // set after welcome message broadcast (not replicated)
var bool bReceivedPing;
var bool bNoTeam; // true if no teaminfo for this PRI
var bool bTeamNotified;
var byte PacketLoss;
// Time elapsed.
var int StartTime;
var localized String StringDead;
var localized String StringSpectating;
var localized String StringUnknown;
var int GoalsScored; // not replicated - used on server side only
var int Kills; // not replicated
//if _RO_
var float FFKills; // Moved up here to allow Web Admin to display friendly fire kills
var SteamStatsAndAchievementsBase SteamStatsAndAchievements; // Reference to the ROSteamStatsAndAchievements Actor created in PlayerController
//end _RO_
var vehicle CurrentVehicle; // for performance on clients
var LinkedReplicationInfo CustomReplicationInfo; // for use by mod authors
// ========================================
// ========================================
// Voice chat
// ========================================
// ========================================
var VoiceChatReplicationInfo VoiceInfo;
var bool bRegisteredChatRoom;
var VoiceChatRoom PrivateChatRoom; // not replicated - simulated spawn
var int ActiveChannel; // this player's currently active channel
var int VoiceMemberMask; // members of this player's private chatroom
var byte VoiceID; // contains the player's unique ID used by voice channels
replication
{
// Things the server should send to the client.
reliable if ( bNetDirty && (Role == Role_Authority) )
Score, Kills, Deaths, PlayerVolume, PlayerZone,
PlayerName, Team, TeamID, bIsFemale, bAdmin,
bIsSpectator, bOnlySpectator, bWaitingPlayer, bReadyToPlay,
StartTime, bOutOfLives, CharacterName,
VoiceID, VoiceMemberMask, ActiveChannel;
reliable if ( bNetDirty && (Role == ROLE_Authority) && bNetOwner && HasFlag == None )
HasFlag;
reliable if ( bNetDirty && (Role == Role_Authority) && (!bNetOwner || bDemoRecording) )
PacketLoss, Ping;
reliable if ( bNetDirty && (Role == Role_Authority) && bNetInitial )
PlayerID, bBot, VoiceTypeName, bNoTeam, CustomReplicationInfo;
}
event PostBeginPlay()
{
if ( Role < ROLE_Authority )
return;
if (AIController(Owner) != None)
bBot = true;
StartTime = Level.Game.GameReplicationInfo.ElapsedTime;
Timer();
SetTimer(1.5 + FRand(), true);
}
simulated event PostNetBeginPlay()
{
local GameReplicationInfo GRI;
local VoiceChatReplicationInfo VRI;
if ( Level.GRI != None )
Level.GRI.AddPRI(self);
else
{
ForEach DynamicActors(class'GameReplicationInfo',GRI)
{
GRI.AddPRI(self);
break;
}
}
// VoiceInfo will only have a value if our PlayerID was replicated prior to our PostNetBeginPlay() & VoiceReplicationInfo had been initialized.
foreach DynamicActors(class'VoiceChatReplicationInfo', VRI)
{
VoiceInfo = VRI;
break;
}
if ( Team != None )
bTeamNotified = true;
}
simulated function bool NeedNetNotify()
{
return ( !bRegisteredChatRoom || (!bNoTeam && (Team == None)) );
}
simulated event PostNetReceive()
{
local Actor A;
local PlayerController PC;
if ( !bTeamNotified && (Team != None) )
{
bTeamNotified = true;
PC = Level.GetLocalPlayerController();
ForEach DynamicActors(class'Actor', A)
{
// find my pawn and tell it
if ( Pawn(A) != None && Pawn(A).PlayerReplicationInfo == self )
{
Pawn(A).NotifyTeamChanged();
if ( PC.PlayerReplicationInfo != self )
break;
}
else if ( A.bNotifyLocalPlayerTeamReceived && PC.PlayerReplicationInfo == self )
A.NotifyLocalPlayerTeamReceived(); //if this is the local player's PRI, tell actors that want to know about it
}
}
if ( !bRegisteredChatRoom && VoiceInfo != None && PlayerID != default.PlayerID && VoiceID != default.VoiceID )
{
bRegisteredChatRoom = True;
VoiceInfo.AddVoiceChatter(Self);
}
bNetNotify = NeedNetNotify();
}
simulated function Destroyed()
{
local GameReplicationInfo GRI;
if ( Level.GRI != None )
Level.GRI.RemovePRI(self);
else
{
ForEach DynamicActors(class'GameReplicationInfo',GRI)
{
GRI.RemovePRI(self);
break;
}
}
if ( VoiceInfo == None )
foreach DynamicActors( class'VoiceChatReplicationInfo', VoiceInfo )
break;
if ( VoiceInfo != None )
VoiceInfo.RemoveVoiceChatter(Self);
Super.Destroyed();
}
function SetCharacterVoice(string S)
{
local class<VoicePack> NewVoiceType;
if ( (Left(S,1) == ".") || (Right(S,1) == ".") || (Left(S,5) ~= "none.") )
{
return;
}
if ( (Level.NetMode == NM_DedicatedServer) && (VoiceType != None) )
{
VoiceTypeName = S;
return;
}
if ( S == "" )
{
VoiceTypeName = "";
return;
}
NewVoiceType = class<VoicePack>(DynamicLoadObject(S,class'Class'));
if ( NewVoiceType != None )
{
VoiceType = NewVoiceType;
VoiceTypeName = S;
}
}
function SetCharacterName(string S)
{
CharacterName = S;
}
/* Reset()
reset actor to initial state - used when restarting level without reloading.
*/
function Reset()
{
Super.Reset();
Score = 0;
Deaths = 0;
HasFlag = None;
bReadyToPlay = false;
NumLives = 0;
bOutOfLives = false;
}
simulated function string GetHumanReadableName()
{
return PlayerName;
}
simulated function string GetLocationName()
{
local string VehicleString;
local Vehicle V;
if( ( PlayerVolume == None ) && ( PlayerZone == None ) )
{
if ( (Owner != None) && Controller(Owner).IsInState('Dead') )
return StringDead;
else
return StringSpectating;
}
if ( Owner != None )
{
if ( Vehicle(Controller(Owner).Pawn) != None )
VehicleString = Vehicle(Controller(Owner).Pawn).GetVehiclePositionString();
}
else if ( Level.NetMode == NM_Client )
{
if ( (CurrentVehicle != None) && (CurrentVehicle.PlayerReplicationInfo == self) )
VehicleString = CurrentVehicle.GetVehiclePositionString();
else if ( Level.TimeSeconds - Level.LastVehicleCheck > 1 )
{
// vehicles are often bAlwaysRelevant, so may still be relevant
ForEach DynamicActors(class'Vehicle', V)
if ( V.PlayerReplicationInfo == self )
{
VehicleString = V.GetVehiclePositionString();
CurrentVehicle = V;
break;
}
if ( V == None )
Level.LastVehicleCheck = Level.TimeSeconds;
}
}
if( ( PlayerVolume != None ) && ( PlayerVolume.LocationName != class'Volume'.Default.LocationName ) )
{
if ( len(PlayerVolume.LocationName@VehicleString) > 32 )
return PlayerVolume.LocationName;
return PlayerVolume.LocationName@VehicleString;
}
else if( PlayerZone != None && ( PlayerZone.LocationName != "" ) )
{
if ( len(PlayerZone.LocationName@VehicleString) > 32 )
return PlayerZone.LocationName;
return PlayerZone.LocationName@VehicleString;
}
else if ( VehicleString != "" )
return VehicleString;
else if ( Level.Title != Level.Default.Title )
return Level.Title;
else
return StringUnknown;
}
simulated function material GetPortrait();
event UpdateCharacter();
function UpdatePlayerLocation()
{
local Volume V, Best;
local Pawn P;
local Controller C;
C = Controller(Owner);
if( C != None )
P = C.Pawn;
if( P == None )
{
PlayerVolume = None;
PlayerZone = None;
return;
}
if ( PlayerZone != P.Region.Zone )
PlayerZone = P.Region.Zone;
foreach P.TouchingActors( class'Volume', V )
{
if( V.LocationName == "")
continue;
if( (Best != None) && (V.LocationPriority <= Best.LocationPriority) )
continue;
if( V.Encompasses(P) )
Best = V;
}
if ( PlayerVolume != Best )
PlayerVolume = Best;
}
/* DisplayDebug()
list important controller attributes on canvas
*/
simulated function DisplayDebug(Canvas Canvas, out float YL, out float YPos)
{
if ( Team != None )
Canvas.DrawText(" PlayerName "$PlayerName$" Team "$Team.GetHumanReadableName() $"("$Team.TeamIndex$") has flag "$HasFlag);
else
Canvas.DrawText(" PlayerName "$PlayerName$" NO Team");
if ( !bBot )
{
YPos += YL;
Canvas.SetPos(4, YL);
Canvas.DrawText(" bIsSpec:"$bIsSpectator@"OnlySpec:"$bOnlySpectator@"Waiting:"$bWaitingPlayer@"Ready:"$bReadyToPlay@"OutOfLives:"$bOutOfLives);
}
}
event ClientNameChange()
{
local PlayerController PC;
ForEach DynamicActors(class'PlayerController', PC)
PC.ReceiveLocalizedMessage( class'GameMessage', 2, self );
}
function Timer()
{
local Controller C;
UpdatePlayerLocation();
SetTimer(1.5 + FRand(), true);
if( FRand() < 0.65 )
return;
if( !bBot )
{
C = Controller(Owner);
if ( !bReceivedPing )
Ping = Min(int(0.25 * float(C.ConsoleCommand("GETPING"))),255);
}
}
function SetPlayerName(string S)
{
OldName = PlayerName;
PlayerName = S;
if ( PlayerController(Owner) != None )
PlayerController(Owner).PlayerOwnerName = S;
}
function SetWaitingPlayer(bool B)
{
bIsSpectator = B;
bWaitingPlayer = B;
}
function SetChatPassword( string InPassword )
{
if ( PrivateChatRoom != None )
PrivateChatRoom.SetChannelPassword(InPassword);
}
function SetVoiceMemberMask( int NewMask )
{
VoiceMemberMask = NewMask;
}
simulated function string GetCallSign()
{
if ( TeamID > 14 )
return "";
return class'GameInfo'.default.CallSigns[TeamID];
}
simulated event string GetNameCallSign()
{
if ( TeamID > 14 )
return PlayerName;
return PlayerName$" ["$class'GameInfo'.default.CallSigns[TeamID]$"]";
}
// if _RO_
// implemented in ROPlayerReplicationInfo
simulated function Material getRolePortrait()
{
return none;
}
// end if _RO_
defaultproperties
{
RemoteRole=ROLE_SimulatedProxy
bAlwaysRelevant=True
StringSpectating="Spectating"
StringUnknown="Unknown"
StringDead="Dead"
NetUpdateFrequency=1
VoiceID=255
bNetNotify=True
}