KF2-Server-Extension/ServerExt/Classes/UI_Scoreboard.uc
Forrest Keller 88f2c71e54 Added perk changing without dying
Fixed scoreboard avatars not working
Added the ServerExt perk menu to the lobby
Made the perk buttons look better (Reset, Unload, Prestige)
Added a better damage popup system
Added a better pet info hud system
Made player info bars fade out with distance
Added missing traits from most of the perks
Made lobby menu support 12 players
Medic Pistol from the trait can't be dropped, sold and has infinite ammo
9mm Pistol now has infinite ammo
Pet sirens do not blow up grenades
Unlocked all cosmetics and emotes
Hide bad cosmetics that were debug
Hans pet no longer forces the camera on his death
Custom weapons in the trader now support the proper weapon names
Updated character info system to support alot of added items
2017-10-20 02:02:53 -05:00

605 lines
16 KiB
Ucode

Class UI_Scoreboard extends KFGUI_Page;
var editinline export KFGUI_RightClickMenu PlayerContext;
var KFGUI_List PlayersList;
var() float ScoreboardSpacing;
var color SBTextColor;
var Texture2D HealthIcon;
var KFGameReplicationInfo KFGRI;
var array<ExtPlayerReplicationInfo> PRIList;
var ExtPlayerReplicationInfo RightClickPlayer;
var int PlayerIndex;
var Font SBFont;
var transient float SBFontSize,CashXPos,KillsXPos,AssistXPos,PingXPos,SBFontHeight;
var transient int InitAdminSize;
var KFGUI_Tooltip ToolTipItem;
var transient bool bHasSelectedPlayer,bMeAdmin,bShowSpectatorsOnly;
function InitMenu()
{
Super.InitMenu();
HealthIcon = Texture2D(DynamicLoadObject("UI_Objective_Tex.UI_Obj_Healing_Loc",class'Texture2D'));
PlayersList = KFGUI_List(FindComponentID('PlayerList'));
}
function ShowMenu()
{
local ExtPlayerController PC;
local int i;
local bool bAdmin;
Owner.bAbsorbInput = false;
PC = ExtPlayerController(GetPlayer());
bAdmin = PC!=None && (PC.WorldInfo.NetMode!=NM_Client || (PC.PlayerReplicationInfo!=None && PC.PlayerReplicationInfo.bAdmin));
if( PC!=None && (InitAdminSize!=PC.AdminCommands.Length || !bAdmin) )
{
InitAdminSize = (bAdmin ? PC.AdminCommands.Length : 0);
PlayerContext.ItemRows.Length = 4+InitAdminSize;
for( i=0; i<InitAdminSize; ++i )
PlayerContext.ItemRows[4+i].Text = PC.AdminCommands[i].Info;
}
}
function CloseMenu()
{
Owner.bAbsorbInput = true;
KFGRI = None;
PRIList.Length = 0;
RightClickPlayer = None;
bHasSelectedPlayer = false;
bShowSpectatorsOnly = false;
}
function bool InOrder( PlayerReplicationInfo A, PlayerReplicationInfo B )
{
if( A.bOnlySpectator!=B.bOnlySpectator )
return B.bOnlySpectator;
if( A.Kills!=B.Kills )
return (A.Kills<B.Kills);
if( A.Score!=B.Score )
return (A.Score<B.Score);
return (A.PlayerName<B.PlayerName);
}
function DrawMenu()
{
local PlayerController PC;
local float XPos,YPos,XScale,YHeight,Y,FontScalar,BoxHeight,XL,YL;
local int i,j,NumSpec,NumPlayer,NumAlivePlayer;
local PlayerReplicationInfo PRI;
local ExtPlayerReplicationInfo KPRI;
local byte DefFont;
PC = GetPlayer();
if( KFGRI==None )
{
KFGRI = KFGameReplicationInfo(PC.WorldInfo.GRI);
if( KFGRI==None )
return;
}
bMeAdmin = (PC.WorldInfo.NetMode!=NM_Client || (ExtPlayerReplicationInfo(PC.PlayerReplicationInfo)!=None && ExtPlayerReplicationInfo(PC.PlayerReplicationInfo).AdminType<=1));
// Sort player list.
for( i=(KFGRI.PRIArray.Length-1); i>0; --i )
{
for( j=i-1; j>=0; --j )
if( !InOrder(KFGRI.PRIArray[i],KFGRI.PRIArray[j]) )
{
PRI = KFGRI.PRIArray[i];
KFGRI.PRIArray[i] = KFGRI.PRIArray[j];
KFGRI.PRIArray[j] = PRI;
}
}
// Check players.
PlayerIndex = -1;
NumPlayer = 0;
for( i=(KFGRI.PRIArray.Length-1); i>=0; --i )
{
KPRI = ExtPlayerReplicationInfo(KFGRI.PRIArray[i]);
if( KPRI==None || KPRI.bHiddenUser )
continue;
if( KPRI.bOnlySpectator )
{
++NumSpec;
continue;
}
if( KPRI.PlayerHealth>0 && KPRI.PlayerHealthPercent>0 && KPRI.GetTeamNum()==0 )
++NumAlivePlayer;
++NumPlayer;
}
PRIList.Length = (bShowSpectatorsOnly ? NumSpec : NumPlayer);
j = PRIList.Length;
for( i=(KFGRI.PRIArray.Length-1); i>=0; --i )
{
KPRI = ExtPlayerReplicationInfo(KFGRI.PRIArray[i]);
if( KPRI!=None && bShowSpectatorsOnly==KPRI.bOnlySpectator )
{
PRIList[--j] = KPRI;
if( KPRI==PC.PlayerReplicationInfo )
PlayerIndex = j;
}
}
// Header font info.
DefFont = Owner.CurrentStyle.DefaultFontSize;
Canvas.Font = Owner.CurrentStyle.PickFont(DefFont,FontScalar);
YL = Owner.CurrentStyle.DefaultHeight;
YHeight = YL*5.f;
// Draw header.
if( Canvas.ClipX<1000 )
{
XPos = Canvas.ClipX*0.2;
XScale = Canvas.ClipX*0.6;
}
else
{
XPos = Canvas.ClipX*0.3;
XScale = Canvas.ClipX*0.4;
}
YPos = Canvas.ClipY*0.05;
Canvas.SetDrawColor(128,32,32,FrameOpacity);
Owner.CurrentStyle.DrawRectBox(XPos,YPos,XScale*0.75,YHeight,26,1);
Canvas.DrawColor = SBTextColor;
Y = YPos+10;
Canvas.SetPos(XPos+26,Y);
Canvas.DrawText(PC.WorldInfo.Title,,FontScalar,FontScalar);
Y+=YL;
Canvas.SetPos(XPos+26,Y);
Canvas.DrawText(KFGRI.ServerName,,FontScalar,FontScalar);
if( KFGRI.GameClass!=None )
{
Y+=YL;
Canvas.SetPos(XPos+26,Y);
Canvas.DrawText(KFGRI.GameClass.Default.GameName$" - "$Class'KFCommon_LocalizedStrings'.Static.GetDifficultyString(KFGRI.GameDifficulty),,FontScalar,FontScalar);
}
Y+=YL;
Canvas.SetPos(XPos+26,Y);
Canvas.DrawText("Time: "$FormatTimeSM(KFGRI.ElapsedTime)$" | Players: "$NumPlayer$" | Alive: "$NumAlivePlayer$" | Spectators: "$NumSpec,,FontScalar,FontScalar);
XPos += XScale*0.75-1;
XScale *= 0.25;
Canvas.SetDrawColor(18,8,8,FrameOpacity);
Owner.CurrentStyle.DrawRectBox(XPos,YPos,XScale,YHeight,26);
Canvas.DrawColor = SBTextColor;
Canvas.Font = Owner.CurrentStyle.PickFont(DefFont+3,FontScalar);
Canvas.TextSize("A",XL,YL,FontScalar,FontScalar);
Y = YPos+4;
DrawCenteredText("WAVE",XPos+XScale*0.5,Y,FontScalar);
Y += YL;
DrawCenteredText(KFGRI.WaveNum$"/"$(KFGRI.WaveMax-1),XPos+XScale*0.5,Y,FontScalar*1.1);
// Scoreboard title line.
Canvas.Font = Owner.CurrentStyle.PickFont(DefFont,FontScalar);
YL = Owner.CurrentStyle.DefaultHeight;
if( Canvas.ClipX<1000 )
{
XPos = Canvas.ClipX*0.175;
XScale = Canvas.ClipX*0.65;
}
else
{
XPos = Canvas.ClipX*0.25;
XScale = Canvas.ClipX*0.5;
}
YPos += YHeight*1.05;
YHeight = YL;
if( bShowSpectatorsOnly )
Canvas.SetDrawColor(32,32,128,FrameOpacity);
else Canvas.SetDrawColor(128,32,32,FrameOpacity);
Owner.CurrentStyle.DrawRectBox(XPos,YPos,XScale,YHeight,16,2);
// Calc X offsets
CashXPos = XScale*0.5;
KillsXPos = XScale*0.7;
AssistXPos = XScale*0.8;
PingXPos = XScale*0.9;
// Header texts
Canvas.DrawColor = SBTextColor;
Y = YPos+4;
Canvas.SetPos(XPos+18,Y);
Canvas.DrawText("PLAYER",,FontScalar,FontScalar);
if( !bShowSpectatorsOnly )
{
Canvas.SetPos(XPos+CashXPos,Y);
Canvas.DrawText("DOSH",,FontScalar,FontScalar);
DrawCenteredText("KILLS",XPos+KillsXPos,Y,FontScalar);
DrawCenteredText("ASSISTS",XPos+AssistXPos,Y,FontScalar);
}
DrawCenteredText("PING",XPos+PingXPos,Y,FontScalar);
// Check how many players to draw.
YPos+=(YHeight-1);
YHeight = (Canvas.ClipY*0.95) - YPos;
i = DefFont+2;
while( i>0 )
{
Canvas.Font = Owner.CurrentStyle.PickFont(i,SBFontSize);
Canvas.TextSize("A",XL,SBFontHeight,SBFontSize,SBFontSize);
BoxHeight = SBFontHeight*2.f+ScoreboardSpacing;
if( (BoxHeight*PRIList.Length)<=YHeight )
break;
--i;
}
// Scoreboard background.
Canvas.SetDrawColor(18,8,8,FrameOpacity);
Owner.CurrentStyle.DrawRectBox(XPos,YPos,XScale,YHeight,16);
// Setup listing.
PlayersList.XPosition = (XPos+8.f) / InputPos[2];
PlayersList.YPosition = (YPos+8.f) / InputPos[3];
PlayersList.XSize = (XScale-16.f) / InputPos[2];
PlayersList.YSize = (YHeight-16.f) / InputPos[3];
PlayersList.ListItemsPerPage = YHeight/BoxHeight;
PlayersList.ChangeListSize(PRIList.Length);
SBFont = Canvas.Font;
}
final function Texture2D FindAvatar( UniqueNetId ClientID )
{
local string S;
S = KFPlayerController(GetPlayer()).GetSteamAvatar(ClientID);
if( S=="" )
return None;
return Texture2D(FindObject(S,class'Texture2D'));
}
final function DrawCenteredText( string S, float X, float Y, optional float Scale=1.f )
{
local float XL,YL;
Canvas.TextSize(S,XL,YL);
Canvas.SetPos(X-(XL*Scale*0.5),Y);
Canvas.DrawText(S,,Scale,Scale);
}
static final function string FormatTimeSM( float Sec )
{
local int Seconds,Minutes;
Sec = Abs(Sec);
Seconds = int(Sec);
Minutes = Seconds/60;
Seconds-=Minutes*60;
return Minutes$":"$(Seconds<10 ? "0"$Seconds : string(Seconds));
}
function DrawPlayerEntry( Canvas C, int Index, float YOffset, float Height, float Width, bool bFocus )
{
local ExtPlayerReplicationInfo PRI;
local float XPos,YPos,XL,YL;
local string S;
if( Index==0 )
{
// Setup font info.
C.Font = SBFont;
}
PRI = PRIList[Index];
bFocus = bFocus || (bHasSelectedPlayer && RightClickPlayer==PRI);
// Draw name entry background.
if( PRI.bOnlySpectator ) // Spectator - blue.
{
if( bFocus )
C.SetDrawColor(86,86,212,FrameOpacity);
else C.SetDrawColor(48,48,164,FrameOpacity);
}
else if( PRI.Team==None ) // Unteamed - Grey.
{
if( bFocus )
C.SetDrawColor(86,86,86,FrameOpacity);
else C.SetDrawColor(48,48,48,FrameOpacity);
}
else
{
switch( PRI.Team.TeamIndex )
{
case 0: // Humans - Red.
if( bFocus )
C.SetDrawColor(160,48,48,FrameOpacity);
else C.SetDrawColor(128,32,32,FrameOpacity);
break;
default: // Rest - Green.
if( bFocus )
C.SetDrawColor(48,160,48,FrameOpacity);
else C.SetDrawColor(32,128,32,FrameOpacity);
}
}
if( PRI.PlayerHealth<=0 || PRI.PlayerHealthPercent<=0 )
C.DrawColor = C.DrawColor*0.6;
Owner.CurrentStyle.DrawRectBox(0.f,YOffset,Width,Height-ScoreboardSpacing,10);
Height-=ScoreboardSpacing;
// Draw health bg.
if( !bShowSpectatorsOnly )
{
if( PRI.PlayerHealth<30 || PRI.PlayerHealthPercent<=0 ) // Chose color based on health.
C.SetDrawColor(220,32,32,255);
else if( PRI.PlayerHealth<70 )
C.SetDrawColor(220,220,32,255);
else C.SetDrawColor(32,225,32,255);
Owner.CurrentStyle.DrawRectBox(6.f,YOffset+6,Height-12,Height-12,5);
}
// Avatar
if( PRI.Avatar!=None )
{
C.SetDrawColor(255,255,255,255);
C.SetPos(Height+4,YOffset+4);
C.DrawTile(PRI.Avatar,Height-8,Height-8,0,0,PRI.Avatar.SizeX,PRI.Avatar.SizeY);
XPos = Height*2+8;
}
else
{
XPos = Height+4;
// Try to obtain avatar.
if( !PRI.bBot )
PRI.Avatar = FindAvatar(PRI.UniqueId);
}
// Name
C.SetPos(XPos,YOffset+2);
if( PlayerIndex==Index )
C.SetDrawColor(128,255,128,255);
else C.DrawColor = SBTextColor;
YPos = SBFontSize;
S = PRI.TaggedPlayerName;
if( PRI.ShowAdminName() )
{
S = S$" ("$PRI.GetAdminNameAbr()$")";
C.DrawColor = PRI.GetAdminColorC();
}
else if( PRI.bIsDev )
{
S = S$" (D)";
C.DrawColor = MakeColor(130,255,235,255);
}
if( bMeAdmin && PRI.FixedData>0 )
{
C.DrawColor = MakeColor(255,0,0,255);
S = S$" -"$PRI.GetDesc();
}
while( true ) // Make sure too long name doesn't overleap.
{
C.TextSize(S,XL,YL,YPos,YPos);
if( (C.CurX+XL)<CashXPos )
break;
YPos*=0.9;
}
C.DrawText(S,,YPos,YPos);
// Other info background.
C.SetDrawColor(4,4,4,255);
Owner.CurrentStyle.DrawRectBox(CashXPos-4,YOffset+4,Width-CashXPos-8,Height-8,6);
// Perk
if( !bShowSpectatorsOnly )
{
if( PRI.ECurrentPerk!=None )
{
// Icon.
C.DrawColor = PRI.HUDPerkColor;
C.SetPos(XPos,YOffset+Height*0.5);
C.DrawRect(Height*0.475,Height*0.475,PRI.ECurrentPerk.Default.PerkIcon);
// Name.
S = PRI.GetPerkLevelStr()@PRI.ECurrentPerk.Default.PerkName;
}
else if( PRI.bBot && PRI.CurrentPerkClass!=None )
{
// Icon.
C.DrawColor = SBTextColor;
C.SetPos(XPos,YOffset+Height*0.5);
C.DrawRect(Height*0.475,Height*0.475,PRI.CurrentPerkClass.Default.PerkIcon);
// Name.
S = PRI.CurrentPerkClass.Default.PerkName;
}
else
{
C.DrawColor = SBTextColor;
S = "No Perk";
}
YPos = SBFontSize*0.9;
C.SetPos(XPos+Height*0.5,YOffset+Height*0.495);
if( PRI.RespawnCounter>=0 )
{
C.DrawColor = SBTextColor;
S = "Respawn: "$FormatTimeSM(PRI.RespawnCounter);
}
while( true ) // Make sure too long name doesn't overleap.
{
C.TextSize(S,XL,YL,YPos,YPos);
if( (C.CurX+XL)<CashXPos )
break;
YPos*=0.8;
}
C.DrawText(S,,YPos,YPos);
}
// Cash
C.DrawColor = SBTextColor;
YPos = YOffset+(Height-SBFontHeight)*0.5;
if( !bShowSpectatorsOnly )
{
C.SetPos(CashXPos,YPos);
C.DrawText(string(int(PRI.Score)),,SBFontSize,SBFontSize);
// Kills
DrawCenteredText(string(PRI.Kills),KillsXPos,YPos,SBFontSize);
// Assists
DrawCenteredText(string(PRI.Assists),AssistXPos,YPos,SBFontSize);
}
// Ping
DrawCenteredText(PRI.bBot ? "BOT" : string(PRI.Ping*4),PingXPos,YPos,SBFontSize);
// Draw health.
if( !bShowSpectatorsOnly )
{
if( HealthIcon!=None )
{
C.SetPos(6+(Height-12)*0.25,YOffset+8);
C.DrawTile(HealthIcon,(Height-12)*0.5,(Height-12)*0.5,0,0,256,256);
}
if( PRI.PlayerHealth<=0 || PRI.PlayerHealthPercent<=0 )
DrawCenteredText("DEAD",6+(Height-12)*0.5,YOffset+Height*0.45,SBFontSize*0.95);
else DrawCenteredText(string(PRI.PlayerHealth),6+(Height-12)*0.5,YOffset+Height*0.45,SBFontSize*0.95);
}
}
function ClickedPlayer( int Index, bool bRight, int MouseX, int MouseY )
{
local PlayerController PC;
local int i;
if( !bRight || Index<0 )
return;
bHasSelectedPlayer = true;
RightClickPlayer = PRIList[Index];
// Check what items to disable.
PC = GetPlayer();
PlayerContext.ItemRows[0].bDisabled = (PlayerIndex==Index || !PC.IsSpectating());
PlayerContext.ItemRows[1].bDisabled = RightClickPlayer.bBot;
PlayerContext.ItemRows[2].bDisabled = (PlayerIndex==Index || RightClickPlayer.bBot);
PlayerContext.ItemRows[2].Text = (PlayerContext.ItemRows[2].bDisabled || PC.IsPlayerMuted(RightClickPlayer.UniqueId)) ? "Unmute player" : "Mute player";
if( PlayerIndex==Index ) // Selected self.
{
for( i=4; i<PlayerContext.ItemRows.Length; ++i )
PlayerContext.ItemRows[i].bDisabled = true;
}
else
{
for( i=4; i<PlayerContext.ItemRows.Length; ++i )
PlayerContext.ItemRows[i].bDisabled = false;
}
PlayerContext.OpenMenu(Self);
}
function HidRightClickMenu( KFGUI_RightClickMenu M )
{
bHasSelectedPlayer = false;
}
function SelectedRCItem( int Index )
{
local PlayerController PC;
PC = GetPlayer();
switch( Index )
{
case 0: // Spectate this player.
PC.ConsoleCommand("ViewPlayerID "$RightClickPlayer.PlayerID);
break;
case 1: // Steam profile.
OnlineSubsystemSteamworks(class'GameEngine'.static.GetOnlineSubsystem()).ShowProfileUI(0,,RightClickPlayer.UniqueId);
break;
case 2: // Mute voice.
if( !PC.IsPlayerMuted(RightClickPlayer.UniqueId) )
{
PC.ClientMessage("You've muted "$RightClickPlayer.TaggedPlayerName);
PC.ClientMutePlayer(RightClickPlayer.UniqueId);
RightClickPlayer.bIsMuted = true;
}
else
{
PC.ClientMessage("You've unmuted "$RightClickPlayer.TaggedPlayerName);
PC.ClientUnmutePlayer(RightClickPlayer.UniqueId);
RightClickPlayer.bIsMuted = false;
}
break;
default:
if( Index>=4 )
PC.ConsoleCommand("Admin "$ExtPlayerController(PC).AdminCommands[Index-4].Cmd@RightClickPlayer.PlayerID);
}
}
function ShowPlayerTooltip( int Index )
{
local ExtPlayerReplicationInfo PRI;
local string S;
PRI = PRIList[Index];
if( PRI!=None )
{
if( ToolTipItem==None )
{
ToolTipItem = New(None)Class'KFGUI_Tooltip';
ToolTipItem.Owner = Owner;
ToolTipItem.ParentComponent = Self;
ToolTipItem.InitMenu();
}
S = "Player: "$PRI.TaggedPlayerName$"|Health: "$(PRI.PlayerHealthPercent<=0 ? "0" : string(PRI.PlayerHealth));
if( PRI.ShowAdminName() )
S = S$"|"$PRI.GetAdminName();
S = S$"|(Right click for options)";
ToolTipItem.SetText(S);
ToolTipItem.ShowMenu();
ToolTipItem.CompPos[0] = Owner.MousePosition.X;
ToolTipItem.CompPos[1] = Owner.MousePosition.Y;
ToolTipItem.GetInputFocus();
}
}
function ButtonClicked( KFGUI_Button Sender )
{
switch( Sender.ID )
{
case 'Spec':
bShowSpectatorsOnly = !bShowSpectatorsOnly;
break;
}
}
defaultproperties
{
bAlwaysTop=true
SBTextColor=(R=250,G=250,B=250,A=255)
ScoreboardSpacing=4
Begin Object Class=KFGUI_List Name=PlayerList
bDrawBackground=false
OnDrawItem=DrawPlayerEntry
OnClickedItem=ClickedPlayer
ID="PlayerList"
bClickable=true
OnMouseRest=ShowPlayerTooltip
End Object
Begin Object Class=KFGUI_Button Name=B_ShowSpecs
ID="Spec"
ButtonText="Show Spectators"
Tooltip="Toggle show server spectators"
XPosition=0.67
YPosition=0.95
XSize=0.09
YSize=0.03
OnClickLeft=ButtonClicked
OnClickRight=ButtonClicked
End Object
Components.Add(PlayerList)
Components.Add(B_ShowSpecs)
Begin Object Class=KFGUI_RightClickMenu Name=PlayerContextMenu
ItemRows.Add((Text="Spectate this player"))
ItemRows.Add((Text="View player Steam profile"))
ItemRows.Add((Text="Mute"))
ItemRows.Add((bSplitter=true))
OnSelectedItem=SelectedRCItem
OnBecameHidden=HidRightClickMenu
End Object
PlayerContext=PlayerContextMenu
}