KF2-Server-Extension/ServerExt/Classes/UI_Scoreboard.uc

652 lines
17 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;
var KFGUI_Button SpecButton;
var localized string TimeText;
var localized string PlayersText;
var localized string PlayerText;
var localized string AliveText;
var localized string SpectatorsText;
var localized string HeaderWaveText;
var localized string HeaderPlayerText;
var localized string HeaderDoshText;
var localized string HeaderKillsText;
var localized string HeaderAssistText;
var localized string HeaderPingText;
var localized string NoPerkText;
var localized string RespawnText;
var localized string BotText;
var localized string DeadText;
var localized string UnmutePlayerText;
var localized string MutePlayerText;
var localized string YouveMutedText;
var localized string YouveUnmutedText;
var localized string HealthText;
var localized string RClickForOptsText;
var localized string SpecButtonText;
var localized string SpecButtonTooltip;
var localized string SpectateThisPlayerText;
var localized string ViewPlayerProfileText;
var localized string MuteText;
function FRowItem NewFRowItem(string Text, bool isSplitter)
{
local FRowItem newItem;
newItem.Text=Text;
newItem.bSplitter=isSplitter;
return newItem;
}
function InitMenu()
{
Super.InitMenu();
HealthIcon = Texture2D(DynamicLoadObject("UI_Objective_Tex.UI_Obj_Healing_Loc",class'Texture2D'));
PlayersList = KFGUI_List(FindComponentID('PlayerList'));
SpecButton = KFGUI_Button(FindComponentID('Spec'));
SpecButton.ButtonText=SpecButtonText;
SpecButton.Tooltip=SpecButtonTooltip;
PlayerContext.ItemRows.AddItem(NewFRowItem(SpectateThisPlayerText, false));
PlayerContext.ItemRows.AddItem(NewFRowItem(ViewPlayerProfileText, false));
PlayerContext.ItemRows.AddItem(NewFRowItem(MuteText, false));
PlayerContext.ItemRows.AddItem(NewFRowItem("", true));
}
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 <= AT_Admin));
// 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(TimeText$": "$FormatTimeSM(KFGRI.ElapsedTime)$" | "$PlayersText$": "$NumPlayer$" | "$AliveText$": "$NumAlivePlayer$" | "$SpectatorsText$": "$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(HeaderWaveText,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(HeaderPlayerText,,FontScalar,FontScalar);
if (!bShowSpectatorsOnly)
{
Canvas.SetPos(XPos+CashXPos,Y);
Canvas.DrawText(HeaderDoshText,,FontScalar,FontScalar);
DrawCenteredText(HeaderKillsText,XPos+KillsXPos,Y,FontScalar);
DrawCenteredText(HeaderAssistText,XPos+AssistXPos,Y,FontScalar);
}
DrawCenteredText(HeaderPingText,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 = NoPerkText;
}
YPos = SBFontSize*0.9;
C.SetPos(XPos+Height*0.5,YOffset+Height*0.495);
if (PRI.RespawnCounter>=0)
{
C.DrawColor = SBTextColor;
S = RespawnText$": "$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 ? BotText : 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(DeadText,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)) ? UnmutePlayerText : MutePlayerText;
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(YouveMutedText$" "$RightClickPlayer.TaggedPlayerName);
PC.ClientMutePlayer(RightClickPlayer.UniqueId);
RightClickPlayer.bIsMuted = true;
}
else
{
PC.ClientMessage(YouveUnmutedText$" "$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 = PlayerText$": "$PRI.TaggedPlayerName$"|"$HealthText$": "$(PRI.PlayerHealthPercent<=0 ? "0" : string(PRI.PlayerHealth));
if (PRI.ShowAdminName())
S = S$"|"$PRI.GetAdminName();
S = S$"|"$RClickForOptsText;
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"
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
OnSelectedItem=SelectedRCItem
OnBecameHidden=HidRightClickMenu
End Object
PlayerContext=PlayerContextMenu
}