mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-15 18:45:23 -04:00
Combine NormalPlayerListWidget and ExtPlayerListWidget into one class
This commit is contained in:
parent
9066bf55ae
commit
d1c3f85518
@ -1,6 +1,6 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Microsoft Visual Studio Solution File, Format Version 9.00
|
||||
# Visual Studio 2005
|
||||
# SharpDevelop 4.4
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassicalSharp", "ClassicalSharp\ClassicalSharp.csproj", "{BEB1C785-5CAD-48FF-A886-876BF0A318D4}"
|
||||
EndProject
|
||||
|
@ -86,14 +86,10 @@ namespace ClassicalSharp.Gui.Screens {
|
||||
hotbar.Dispose();
|
||||
hotbar.Init();
|
||||
|
||||
if (!hadPlayerList) return;
|
||||
|
||||
if (game.Server.UsingExtPlayerList && !game.UseClassicTabList) {
|
||||
playerList = new ExtPlayerListWidget(game, playerFont);
|
||||
} else {
|
||||
playerList = new NormalPlayerListWidget(game, playerFont);
|
||||
}
|
||||
|
||||
if (!hadPlayerList) return;
|
||||
bool extended = game.Server.UsingExtPlayerList && !game.UseClassicTabList;
|
||||
playerList = new PlayerListWidget(game, playerFont, !extended);
|
||||
|
||||
playerList.Init();
|
||||
playerList.RecalcYOffset();
|
||||
playerList.Reposition();
|
||||
|
@ -138,12 +138,10 @@ namespace ClassicalSharp.Gui.Widgets {
|
||||
List<string> matches = new List<string>();
|
||||
game.Chat.Add(null, MessageType.ClientStatus3);
|
||||
|
||||
TabListEntry[] entries = game.TabList.Entries;
|
||||
TabListEntry[] entries = TabList.Entries;
|
||||
for (int i = 0; i < EntityList.MaxCount; i++) {
|
||||
if (entries[i] == null) continue;
|
||||
|
||||
string rawName = entries[i].PlayerName;
|
||||
string name = Utils.StripColours(rawName);
|
||||
if (entries[i] == null) continue;
|
||||
string name = entries[i].PlayerName;
|
||||
if (Utils.CaselessStarts(name, part)) matches.Add(name);
|
||||
}
|
||||
|
||||
|
@ -1,190 +0,0 @@
|
||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using ClassicalSharp.Entities;
|
||||
using ClassicalSharp.Events;
|
||||
|
||||
namespace ClassicalSharp.Gui.Widgets {
|
||||
public class ExtPlayerListWidget : PlayerListWidget {
|
||||
|
||||
public ExtPlayerListWidget(Game game, Font font) : base(game, font) {
|
||||
textures = new Texture[512];
|
||||
elementOffset = 10;
|
||||
}
|
||||
|
||||
PlayerInfo[] info = new PlayerInfo[512];
|
||||
class PlayerInfo {
|
||||
|
||||
public string ListName;
|
||||
public string PlayerName;
|
||||
public string GroupName;
|
||||
public byte GroupRank;
|
||||
public byte NameId;
|
||||
public bool IsGroup = false;
|
||||
|
||||
public PlayerInfo(TabListEntry p, byte id) {
|
||||
ListName = Utils.StripColours(p.ListName);
|
||||
PlayerName = Utils.StripColours(p.PlayerName);
|
||||
NameId = id;
|
||||
GroupName = p.GroupName;
|
||||
GroupRank = p.GroupRank;
|
||||
}
|
||||
|
||||
public PlayerInfo(string groupName) {
|
||||
GroupName = groupName;
|
||||
IsGroup = true;
|
||||
}
|
||||
}
|
||||
|
||||
PlayerInfoComparer comparer = new PlayerInfoComparer();
|
||||
class PlayerInfoComparer : IComparer<PlayerInfo> {
|
||||
|
||||
public bool JustComparingGroups = true;
|
||||
|
||||
public int Compare(PlayerInfo x, PlayerInfo y) {
|
||||
if (JustComparingGroups)
|
||||
return x.GroupName.CompareTo(y.GroupName);
|
||||
|
||||
int rankOrder = x.GroupRank.CompareTo(y.GroupRank);
|
||||
return rankOrder != 0 ? rankOrder :
|
||||
x.ListName.CompareTo(y.ListName);
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ShouldOffset(int i) {
|
||||
return info[i] == null || !info[i].IsGroup;
|
||||
}
|
||||
|
||||
public override void Init() {
|
||||
base.Init();
|
||||
game.EntityEvents.TabListEntryAdded += TabEntryAdded;
|
||||
game.EntityEvents.TabListEntryRemoved += TabEntryRemoved;
|
||||
game.EntityEvents.TabListEntryChanged += TabEntryChanged;
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
base.Dispose();
|
||||
game.EntityEvents.TabListEntryAdded -= TabEntryAdded;
|
||||
game.EntityEvents.TabListEntryChanged -= TabEntryChanged;
|
||||
game.EntityEvents.TabListEntryRemoved -= TabEntryRemoved;
|
||||
}
|
||||
|
||||
void TabEntryChanged(object sender, IdEventArgs e) {
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
PlayerInfo pInfo = info[i];
|
||||
if (!pInfo.IsGroup && pInfo.NameId == e.Id) {
|
||||
Texture tex = textures[i];
|
||||
gfx.DeleteTexture(ref tex);
|
||||
AddPlayerInfo(game.TabList.Entries[e.Id], e.Id, i);
|
||||
SortPlayerInfo();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TabEntryRemoved(object sender, IdEventArgs e) {
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
PlayerInfo pInfo = info[i];
|
||||
if (!pInfo.IsGroup && pInfo.NameId == e.Id) {
|
||||
RemoveItemAt(info, i);
|
||||
RemoveTextureAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TabEntryAdded(object sender, IdEventArgs e) {
|
||||
AddPlayerInfo(game.TabList.Entries[e.Id], e.Id, -1);
|
||||
SortPlayerInfo();
|
||||
}
|
||||
|
||||
protected override void CreateInitialPlayerInfo() {
|
||||
TabListEntry[] entries = game.TabList.Entries;
|
||||
for (int i = 0; i < entries.Length; i++) {
|
||||
TabListEntry e = entries[i];
|
||||
if (e != null) AddPlayerInfo(e, (byte)i, -1);
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetNameUnder(int mouseX, int mouseY) {
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
Texture tex = textures[i];
|
||||
if (tex.IsValid && tex.Bounds.Contains(mouseX, mouseY) && !info[i].IsGroup) {
|
||||
return Utils.StripColours(info[i].PlayerName);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void AddPlayerInfo(TabListEntry player, byte id, int index) {
|
||||
DrawTextArgs args = new DrawTextArgs(player.ListName, font, true);
|
||||
Texture tex = game.Drawer2D.MakeTextTexture(ref args, 0, 0);
|
||||
game.Drawer2D.ReducePadding(ref tex, Utils.Floor(font.Size), 3);
|
||||
|
||||
if (index < 0) {
|
||||
info[namesCount] = new PlayerInfo(player, id);
|
||||
textures[namesCount] = tex;
|
||||
namesCount++;
|
||||
} else {
|
||||
info[index] = new PlayerInfo(player, id);
|
||||
textures[index] = tex;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SortInfoList() {
|
||||
if (namesCount == 0) return;
|
||||
|
||||
// Sort the list into groups
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
if (info[i].IsGroup) DeleteGroup(ref i);
|
||||
}
|
||||
comparer.JustComparingGroups = true;
|
||||
Array.Sort(info, textures, 0, namesCount, comparer);
|
||||
|
||||
// Sort the entries in each group
|
||||
comparer.JustComparingGroups = false;
|
||||
int index = 0;
|
||||
while (index < namesCount) {
|
||||
AddGroup(info[index].GroupName, ref index);
|
||||
int count = GetGroupCount(index);
|
||||
Array.Sort(info, textures, index, count, comparer);
|
||||
index += count;
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteGroup(ref int i) {
|
||||
gfx.DeleteTexture(ref textures[i]);
|
||||
RemoveItemAt(info, i);
|
||||
RemoveItemAt(textures, i);
|
||||
|
||||
namesCount--;
|
||||
i--;
|
||||
}
|
||||
|
||||
void AddGroup(string group, ref int index) {
|
||||
DrawTextArgs args = new DrawTextArgs(group, font, true);
|
||||
Texture tex = game.Drawer2D.MakeTextTexture(ref args, 0, 0);
|
||||
game.Drawer2D.ReducePadding(ref tex, Utils.Floor(font.Size), 3);
|
||||
|
||||
for (int i = info.Length - 1; i > index; i--) {
|
||||
info[i] = info[i - 1];
|
||||
textures[i] = textures[i - 1];
|
||||
}
|
||||
info[index] = new PlayerInfo(group);
|
||||
textures[index] = tex;
|
||||
|
||||
index++;
|
||||
namesCount++;
|
||||
}
|
||||
|
||||
int GetGroupCount(int startIndex) {
|
||||
string group = info[startIndex].GroupName;
|
||||
int count = 0;
|
||||
while (startIndex < namesCount && info[startIndex++].GroupName == group) {
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,123 +0,0 @@
|
||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using ClassicalSharp.Entities;
|
||||
using ClassicalSharp.Events;
|
||||
|
||||
namespace ClassicalSharp.Gui.Widgets {
|
||||
public class NormalPlayerListWidget : PlayerListWidget {
|
||||
|
||||
public NormalPlayerListWidget(Game game, Font font) : base(game, font) {
|
||||
textures = new Texture[256];
|
||||
}
|
||||
|
||||
PlayerInfo[] info = new PlayerInfo[256];
|
||||
protected class PlayerInfo {
|
||||
|
||||
public string Name, ColouredName;
|
||||
public byte Id;
|
||||
|
||||
public PlayerInfo(TabListEntry p, byte id) {
|
||||
ColouredName = p.PlayerName;
|
||||
Name = Utils.StripColours(p.PlayerName);
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetNameUnder(int mouseX, int mouseY) {
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
Texture texture = textures[i];
|
||||
if (texture.IsValid && texture.Bounds.Contains(mouseX, mouseY))
|
||||
return Utils.StripColours(info[i].Name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Init() {
|
||||
base.Init();
|
||||
game.EntityEvents.TabListEntryAdded += TabEntryAdded;
|
||||
game.EntityEvents.TabListEntryRemoved += TabEntryRemoved;
|
||||
game.EntityEvents.TabListEntryChanged += TabEntryChanged;
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
base.Dispose();
|
||||
game.EntityEvents.TabListEntryAdded -= TabEntryAdded;
|
||||
game.EntityEvents.TabListEntryChanged -= TabEntryChanged;
|
||||
game.EntityEvents.TabListEntryRemoved -= TabEntryRemoved;
|
||||
}
|
||||
|
||||
protected override void CreateInitialPlayerInfo() {
|
||||
TabListEntry[] entries = game.TabList.Entries;
|
||||
for (int i = 0; i < entries.Length; i++) {
|
||||
TabListEntry e = entries[i];
|
||||
if (e != null)
|
||||
AddPlayerInfo(new PlayerInfo(e, (byte)i), -1);
|
||||
}
|
||||
}
|
||||
|
||||
void AddPlayerInfo(PlayerInfo pInfo, int index) {
|
||||
Texture tex = DrawName(pInfo);
|
||||
if (index < 0) {
|
||||
info[namesCount] = pInfo;
|
||||
textures[namesCount] = tex;
|
||||
namesCount++;
|
||||
} else {
|
||||
info[index] = pInfo;
|
||||
textures[index] = tex;
|
||||
}
|
||||
}
|
||||
|
||||
Texture DrawName(PlayerInfo pInfo) {
|
||||
string name = pInfo.ColouredName;
|
||||
if (game.PureClassic) name = Utils.StripColours(name);
|
||||
|
||||
DrawTextArgs args = new DrawTextArgs(name, font, false);
|
||||
Texture tex = game.Drawer2D.MakeTextTexture(ref args, 0, 0);
|
||||
game.Drawer2D.ReducePadding(ref tex, Utils.Floor(font.Size), 3);
|
||||
return tex;
|
||||
}
|
||||
|
||||
void TabEntryAdded(object sender, IdEventArgs e) {
|
||||
AddPlayerInfo(new PlayerInfo(game.TabList.Entries[e.Id], e.Id), -1);
|
||||
SortPlayerInfo();
|
||||
}
|
||||
|
||||
void TabEntryChanged(object sender, IdEventArgs e) {
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
PlayerInfo pInfo = info[i];
|
||||
if (pInfo.Id != e.Id) continue;
|
||||
|
||||
Texture tex = textures[i];
|
||||
gfx.DeleteTexture(ref tex);
|
||||
AddPlayerInfo(new PlayerInfo(game.TabList.Entries[e.Id], e.Id), i);
|
||||
SortPlayerInfo();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TabEntryRemoved(object sender, IdEventArgs e) {
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
PlayerInfo pInfo = info[i];
|
||||
if (pInfo.Id == e.Id) {
|
||||
RemoveItemAt(info, i);
|
||||
RemoveTextureAt(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PlayerInfoComparer comparer = new PlayerInfoComparer();
|
||||
class PlayerInfoComparer : IComparer<PlayerInfo> {
|
||||
|
||||
public int Compare(PlayerInfo x, PlayerInfo y) {
|
||||
return x.Name.CompareTo(y.Name);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void SortInfoList() {
|
||||
Array.Sort(info, textures, 0, namesCount, comparer);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
using System;
|
||||
using System.Drawing;
|
||||
|
||||
namespace ClassicalSharp.Gui.Widgets {
|
||||
public abstract class PlayerListWidget : Widget {
|
||||
|
||||
protected readonly Font font;
|
||||
public PlayerListWidget(Game game, Font font) : base(game) {
|
||||
HorizontalAnchor = Anchor.Centre;
|
||||
VerticalAnchor = Anchor.Centre;
|
||||
this.font = font;
|
||||
}
|
||||
|
||||
protected const int columnPadding = 5;
|
||||
protected const int boundsSize = 10;
|
||||
protected const int namesPerColumn = 20;
|
||||
|
||||
protected int elementOffset = 0;
|
||||
protected int namesCount = 0;
|
||||
protected Texture[] textures;
|
||||
protected int columns;
|
||||
protected int xMin, xMax, yHeight;
|
||||
|
||||
static FastColour topCol = new FastColour(0, 0, 0, 180);
|
||||
static FastColour bottomCol = new FastColour(50, 50, 50, 205);
|
||||
TextWidget overview;
|
||||
|
||||
public override void Init() {
|
||||
overview = TextWidget.Create(game, "Connected players:", font)
|
||||
.SetLocation(Anchor.Centre, Anchor.LeftOrTop, 0, 0);
|
||||
|
||||
CreateInitialPlayerInfo();
|
||||
SortPlayerInfo();
|
||||
}
|
||||
|
||||
public abstract string GetNameUnder(int mouseX, int mouseY);
|
||||
|
||||
public override void Render(double delta) {
|
||||
gfx.Texturing = false;
|
||||
int offset = overview.Height + 10;
|
||||
int height = Math.Max(300, Height + overview.Height);
|
||||
gfx.Draw2DQuad(X, Y - offset, Width, height, topCol, bottomCol);
|
||||
|
||||
gfx.Texturing = true;
|
||||
overview.YOffset = Y - offset + 5;
|
||||
overview.Reposition();
|
||||
overview.Render(delta);
|
||||
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
Texture tex = textures[i];
|
||||
int texY = tex.Y;
|
||||
tex.Y1 -= 10;
|
||||
if (tex.IsValid) tex.Render(gfx);
|
||||
tex.Y1 = texY;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
Texture tex = textures[i];
|
||||
gfx.DeleteTexture(ref tex);
|
||||
textures[i] = tex;
|
||||
}
|
||||
overview.Dispose();
|
||||
}
|
||||
|
||||
protected void UpdateTableDimensions() {
|
||||
int width = xMax - xMin;
|
||||
X = xMin - boundsSize;
|
||||
Y = game.Height / 2 - yHeight / 2 - boundsSize;
|
||||
Width = width + boundsSize * 2;
|
||||
Height = yHeight + boundsSize * 2;
|
||||
}
|
||||
|
||||
protected void CalcMaxColumnHeight() {
|
||||
yHeight = 0;
|
||||
for (int col = 0; col < columns; col++) {
|
||||
yHeight = Math.Max(GetColumnHeight(col), yHeight);
|
||||
}
|
||||
}
|
||||
|
||||
protected int GetColumnWidth(int column) {
|
||||
int i = column * namesPerColumn;
|
||||
int maxWidth = 0;
|
||||
int maxIndex = Math.Min(namesCount, i + namesPerColumn);
|
||||
|
||||
for (; i < maxIndex; i++)
|
||||
maxWidth = Math.Max(maxWidth, textures[i].Width);
|
||||
return maxWidth + columnPadding + elementOffset;
|
||||
}
|
||||
|
||||
protected int GetColumnHeight(int column) {
|
||||
int i = column * namesPerColumn;
|
||||
int total = 0;
|
||||
int maxIndex = Math.Min(namesCount, i + namesPerColumn);
|
||||
|
||||
for (; i < maxIndex; i++)
|
||||
total += textures[i].Height + 1;
|
||||
return total;
|
||||
}
|
||||
|
||||
protected void SetColumnPos(int column, int x, int y) {
|
||||
int i = column * namesPerColumn;
|
||||
int maxIndex = Math.Min(namesCount, i + namesPerColumn);
|
||||
|
||||
for (; i < maxIndex; i++) {
|
||||
Texture tex = textures[i];
|
||||
tex.X1 = x; tex.Y1 = y;
|
||||
|
||||
y += tex.Height + 1;
|
||||
if (ShouldOffset(i))
|
||||
tex.X1 += elementOffset;
|
||||
textures[i] = tex;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual bool ShouldOffset(int i) { return true; }
|
||||
|
||||
public void RecalcYOffset() {
|
||||
YOffset = -Math.Max(0, game.Height / 4 - Height / 2);
|
||||
}
|
||||
|
||||
public override void Reposition() {
|
||||
int oldX = X, oldY = Y;
|
||||
base.Reposition();
|
||||
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
textures[i].X1 += X - oldX;
|
||||
textures[i].Y1 += Y - oldY;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void CreateInitialPlayerInfo();
|
||||
|
||||
protected abstract void SortInfoList();
|
||||
|
||||
protected void RemoveTextureAt(int i) {
|
||||
Texture tex = textures[i];
|
||||
gfx.DeleteTexture(ref tex);
|
||||
RemoveItemAt(textures, i);
|
||||
namesCount--;
|
||||
SortPlayerInfo();
|
||||
}
|
||||
|
||||
protected void RemoveItemAt<T>(T[] array, int index) {
|
||||
for (int i = index; i < namesCount - 1; i++) {
|
||||
array[i] = array[i + 1];
|
||||
}
|
||||
array[namesCount - 1] = default(T);
|
||||
}
|
||||
|
||||
protected void SortPlayerInfo() {
|
||||
columns = Utils.CeilDiv(namesCount, namesPerColumn);
|
||||
SortInfoList();
|
||||
columns = Utils.CeilDiv(namesCount, namesPerColumn);
|
||||
CalcMaxColumnHeight();
|
||||
int y = game.Height / 2 - yHeight / 2;
|
||||
int midCol = columns / 2;
|
||||
|
||||
int centreX = game.Width / 2;
|
||||
int offset = 0;
|
||||
if (columns % 2 != 0) {
|
||||
// For an odd number of columns, the middle column is centred.
|
||||
offset = Utils.CeilDiv(GetColumnWidth(midCol), 2);
|
||||
}
|
||||
|
||||
xMin = centreX - offset;
|
||||
for (int col = midCol - 1; col >= 0; col--) {
|
||||
xMin -= GetColumnWidth(col);
|
||||
SetColumnPos(col, xMin, y);
|
||||
}
|
||||
xMax = centreX - offset;
|
||||
for (int col = midCol; col < columns; col++) {
|
||||
SetColumnPos(col, xMax, y);
|
||||
xMax += GetColumnWidth(col);
|
||||
}
|
||||
|
||||
OnSort();
|
||||
UpdateTableDimensions();
|
||||
RecalcYOffset();
|
||||
Reposition();
|
||||
}
|
||||
|
||||
protected virtual void OnSort() {
|
||||
int width = 0, centreX = game.Width / 2;
|
||||
for (int col = 0; col < columns; col++)
|
||||
width += GetColumnWidth(col);
|
||||
if (width < 480) width = 480;
|
||||
|
||||
xMin = centreX - width / 2;
|
||||
xMax = centreX + width / 2;
|
||||
|
||||
int x = xMin, y = game.Height / 2 - yHeight / 2;
|
||||
for (int col = 0; col < columns; col++) {
|
||||
SetColumnPos(col, x, y);
|
||||
x += GetColumnWidth(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
313
ClassicalSharp/2D/Widgets/PlayerListWidget.cs
Normal file
313
ClassicalSharp/2D/Widgets/PlayerListWidget.cs
Normal file
@ -0,0 +1,313 @@
|
||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using ClassicalSharp.Entities;
|
||||
using ClassicalSharp.Events;
|
||||
|
||||
namespace ClassicalSharp.Gui.Widgets {
|
||||
public class PlayerListWidget : Widget {
|
||||
|
||||
readonly Font font;
|
||||
bool classic;
|
||||
public PlayerListWidget(Game game, Font font, bool classic) : base(game) {
|
||||
HorizontalAnchor = Anchor.Centre;
|
||||
VerticalAnchor = Anchor.Centre;
|
||||
this.font = font;
|
||||
this.classic = classic;
|
||||
elementOffset = classic ? 0 : 10;
|
||||
}
|
||||
|
||||
const int columnPadding = 5;
|
||||
const int boundsSize = 10;
|
||||
const int namesPerColumn = 20;
|
||||
|
||||
int elementOffset, namesCount = 0;
|
||||
Texture[] textures = new Texture[512];
|
||||
short[] IDs = new short[512];
|
||||
int xMin, xMax, yHeight;
|
||||
|
||||
static FastColour topCol = new FastColour(0, 0, 0, 180);
|
||||
static FastColour bottomCol = new FastColour(50, 50, 50, 205);
|
||||
TextWidget overview;
|
||||
|
||||
public override void Init() {
|
||||
TabListEntry[] entries = TabList.Entries;
|
||||
for (int id = 0; id < entries.Length; id++) {
|
||||
TabListEntry e = entries[id];
|
||||
if (e != null) AddPlayerInfo((byte)id, -1);
|
||||
}
|
||||
SortAndReposition();
|
||||
|
||||
overview = TextWidget.Create(game, "Connected players:", font)
|
||||
.SetLocation(Anchor.Centre, Anchor.LeftOrTop, 0, 0);
|
||||
game.EntityEvents.TabListEntryAdded += TabEntryAdded;
|
||||
game.EntityEvents.TabListEntryRemoved += TabEntryRemoved;
|
||||
game.EntityEvents.TabListEntryChanged += TabEntryChanged;
|
||||
}
|
||||
|
||||
public override void Render(double delta) {
|
||||
gfx.Texturing = false;
|
||||
int offset = overview.Height + 10;
|
||||
int height = Math.Max(300, Height + overview.Height);
|
||||
gfx.Draw2DQuad(X, Y - offset, Width, height, topCol, bottomCol);
|
||||
|
||||
gfx.Texturing = true;
|
||||
overview.YOffset = Y - offset + 5;
|
||||
overview.Reposition();
|
||||
overview.Render(delta);
|
||||
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
Texture tex = textures[i];
|
||||
int texY = tex.Y;
|
||||
tex.Y1 -= 10;
|
||||
if (tex.IsValid) tex.Render(gfx);
|
||||
tex.Y1 = texY;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
Texture tex = textures[i];
|
||||
gfx.DeleteTexture(ref tex);
|
||||
textures[i] = tex;
|
||||
}
|
||||
|
||||
overview.Dispose();
|
||||
game.EntityEvents.TabListEntryAdded -= TabEntryAdded;
|
||||
game.EntityEvents.TabListEntryChanged -= TabEntryChanged;
|
||||
game.EntityEvents.TabListEntryRemoved -= TabEntryRemoved;
|
||||
}
|
||||
|
||||
public string GetNameUnder(int mouseX, int mouseY) {
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
Texture tex = textures[i];
|
||||
if (tex.IsValid && tex.Bounds.Contains(mouseX, mouseY) && IDs[i] >= 0) {
|
||||
return TabList.Entries[IDs[i]].PlayerName;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
void RepositionColumns() {
|
||||
int width = 0, centreX = game.Width / 2;
|
||||
yHeight = 0;
|
||||
|
||||
int columns = Utils.CeilDiv(namesCount, namesPerColumn);
|
||||
for (int col = 0; col < columns; col++) {
|
||||
width += GetColumnWidth(col);
|
||||
yHeight = Math.Max(GetColumnHeight(col), yHeight);
|
||||
}
|
||||
|
||||
if (width < 480) width = 480;
|
||||
xMin = centreX - width / 2;
|
||||
xMax = centreX + width / 2;
|
||||
|
||||
int x = xMin, y = game.Height / 2 - yHeight / 2;
|
||||
for (int col = 0; col < columns; col++) {
|
||||
SetColumnPos(col, x, y);
|
||||
x += GetColumnWidth(col);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateTableDimensions() {
|
||||
int width = xMax - xMin;
|
||||
X = xMin - boundsSize;
|
||||
Y = game.Height / 2 - yHeight / 2 - boundsSize;
|
||||
Width = width + boundsSize * 2;
|
||||
Height = yHeight + boundsSize * 2;
|
||||
}
|
||||
|
||||
int GetColumnWidth(int column) {
|
||||
int i = column * namesPerColumn;
|
||||
int maxWidth = 0;
|
||||
int maxIndex = Math.Min(namesCount, i + namesPerColumn);
|
||||
|
||||
for (; i < maxIndex; i++)
|
||||
maxWidth = Math.Max(maxWidth, textures[i].Width);
|
||||
return maxWidth + columnPadding + elementOffset;
|
||||
}
|
||||
|
||||
int GetColumnHeight(int column) {
|
||||
int i = column * namesPerColumn;
|
||||
int total = 0;
|
||||
int maxIndex = Math.Min(namesCount, i + namesPerColumn);
|
||||
|
||||
for (; i < maxIndex; i++)
|
||||
total += textures[i].Height + 1;
|
||||
return total;
|
||||
}
|
||||
|
||||
void SetColumnPos(int column, int x, int y) {
|
||||
int i = column * namesPerColumn;
|
||||
int maxIndex = Math.Min(namesCount, i + namesPerColumn);
|
||||
|
||||
for (; i < maxIndex; i++) {
|
||||
Texture tex = textures[i];
|
||||
tex.X1 = x; tex.Y1 = y;
|
||||
|
||||
y += tex.Height + 1;
|
||||
// offset player names a bit, compared to group name
|
||||
if (!classic && IDs[i] >= 0) {
|
||||
tex.X1 += elementOffset;
|
||||
}
|
||||
textures[i] = tex;
|
||||
}
|
||||
}
|
||||
|
||||
public void RecalcYOffset() {
|
||||
YOffset = -Math.Max(0, game.Height / 4 - Height / 2);
|
||||
}
|
||||
|
||||
public override void Reposition() {
|
||||
int oldX = X, oldY = Y;
|
||||
base.Reposition();
|
||||
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
textures[i].X1 += X - oldX;
|
||||
textures[i].Y1 += Y - oldY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AddPlayerInfo(byte id, int index) {
|
||||
string name = TabList.Entries[id].ListName;
|
||||
Texture tex = DrawName(name);
|
||||
|
||||
// insert at end of list
|
||||
if (index == -1) { index = namesCount; namesCount++; }
|
||||
IDs[index] = id;
|
||||
textures[index] = tex;
|
||||
}
|
||||
|
||||
void TabEntryAdded(object sender, IdEventArgs e) {
|
||||
AddPlayerInfo(e.Id, -1);
|
||||
SortAndReposition();
|
||||
}
|
||||
|
||||
void TabEntryChanged(object sender, IdEventArgs e) {
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
if (IDs[i] != e.Id) continue;
|
||||
|
||||
Texture tex = textures[i];
|
||||
gfx.DeleteTexture(ref tex);
|
||||
AddPlayerInfo(e.Id, i);
|
||||
SortAndReposition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TabEntryRemoved(object sender, IdEventArgs e) {
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
if (IDs[i] != e.Id) continue;
|
||||
RemoveAt(i);
|
||||
SortAndReposition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveAt(int i) {
|
||||
Texture tex = textures[i];
|
||||
gfx.DeleteTexture(ref tex);
|
||||
|
||||
for (; i < namesCount - 1; i++) {
|
||||
IDs[i] = IDs[i + 1];
|
||||
textures[i] = textures[i + 1];
|
||||
}
|
||||
|
||||
IDs[namesCount] = 0;
|
||||
textures[namesCount] = default(Texture);
|
||||
namesCount--;
|
||||
}
|
||||
|
||||
|
||||
void SortAndReposition() {
|
||||
SortEntries();
|
||||
RepositionColumns();
|
||||
UpdateTableDimensions();
|
||||
RecalcYOffset();
|
||||
Reposition();
|
||||
}
|
||||
|
||||
Texture DrawName(string name) {
|
||||
if (game.PureClassic) name = Utils.StripColours(name);
|
||||
|
||||
DrawTextArgs args = new DrawTextArgs(name, font, false);
|
||||
Texture tex = game.Drawer2D.MakeTextTexture(ref args, 0, 0);
|
||||
game.Drawer2D.ReducePadding(ref tex, Utils.Floor(font.Size), 3);
|
||||
return tex;
|
||||
}
|
||||
|
||||
IComparer<short> comparer = new PlayerComparer();
|
||||
class PlayerComparer : IComparer<short> {
|
||||
public int Compare(short x, short y) {
|
||||
byte xRank = TabList.Entries[x].GroupRank;
|
||||
byte yRank = TabList.Entries[y].GroupRank;
|
||||
int rankOrder = xRank.CompareTo(yRank);
|
||||
if (rankOrder != 0) return rankOrder;
|
||||
|
||||
string xName = TabList.Entries[x].ListNameColourless;
|
||||
string yName = TabList.Entries[y].ListNameColourless;
|
||||
return xName.CompareTo(yName);
|
||||
}
|
||||
}
|
||||
|
||||
IComparer<short> grpComparer = new GroupComparer();
|
||||
class GroupComparer : IComparer<short> {
|
||||
public int Compare(short x, short y) {
|
||||
string xGroup = TabList.Entries[x].Group;
|
||||
string yGroup = TabList.Entries[y].Group;
|
||||
return xGroup.CompareTo(yGroup);
|
||||
}
|
||||
}
|
||||
|
||||
void SortEntries() {
|
||||
if (namesCount == 0) return;
|
||||
if (classic) {
|
||||
Array.Sort(IDs, textures, 0, namesCount, grpComparer);
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort the list into groups
|
||||
for (int i = 0; i < namesCount; i++) {
|
||||
if (IDs[i] < 0) DeleteGroup(ref i);
|
||||
}
|
||||
Array.Sort(IDs, textures, 0, namesCount, grpComparer);
|
||||
|
||||
// Sort the entries in each group
|
||||
int index = 0;
|
||||
while (index < namesCount) {
|
||||
short id = IDs[index];
|
||||
AddGroup(id, ref index);
|
||||
int count = GetGroupCount(id, index);
|
||||
Array.Sort(IDs, textures, index, count, comparer);
|
||||
index += count;
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteGroup(ref int i) { RemoveAt(i); i--; }
|
||||
void AddGroup(short id, ref int index) {
|
||||
for (int i = IDs.Length - 1; i > index; i--) {
|
||||
IDs[i] = IDs[i - 1];
|
||||
textures[i] = textures[i - 1];
|
||||
}
|
||||
|
||||
IDs[index] = (short)-id;
|
||||
string group = TabList.Entries[id].Group;
|
||||
textures[index] = DrawName(group);
|
||||
|
||||
index++;
|
||||
namesCount++;
|
||||
}
|
||||
|
||||
int GetGroupCount(short id, int idx) {
|
||||
string group = TabList.Entries[id].Group;
|
||||
int count = 0;
|
||||
while (idx < namesCount && TabList.Entries[IDs[idx]].Group == group) {
|
||||
idx++; count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
@ -127,9 +127,7 @@
|
||||
<Compile Include="2D\Widgets\Menu\MenuInputValidator.cs" />
|
||||
<Compile Include="2D\Widgets\Menu\MenuInputWidget.cs" />
|
||||
<Compile Include="2D\Widgets\ButtonWidget.cs" />
|
||||
<Compile Include="2D\Widgets\PlayerList\ExtPlayerListWidget.cs" />
|
||||
<Compile Include="2D\Widgets\PlayerList\NormalPlayerListWidget.cs" />
|
||||
<Compile Include="2D\Widgets\PlayerList\PlayerListWidget.cs" />
|
||||
<Compile Include="2D\Widgets\PlayerListWidget.cs" />
|
||||
<Compile Include="2D\Widgets\ScrollbarWidget.cs" />
|
||||
<Compile Include="2D\Widgets\SurvivalHotbarWidget.cs" />
|
||||
<Compile Include="2D\Widgets\TableWidget.cs" />
|
||||
@ -330,7 +328,6 @@
|
||||
<Folder Include="2D\Widgets" />
|
||||
<Folder Include="2D\Widgets\Menu" />
|
||||
<Folder Include="2D\Widgets\Chat" />
|
||||
<Folder Include="2D\Widgets\PlayerList" />
|
||||
<Folder Include="Blocks" />
|
||||
<Folder Include="Entities\Components" />
|
||||
<Folder Include="Entities\Mobs" />
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace ClassicalSharp.Entities {
|
||||
|
||||
public sealed class TabList : IGameComponent {
|
||||
public TabListEntry[] Entries = new TabListEntry[256];
|
||||
public static TabListEntry[] Entries = new TabListEntry[256];
|
||||
|
||||
public void Init(Game game) { }
|
||||
public void Ready(Game game) { }
|
||||
@ -17,30 +17,24 @@ namespace ClassicalSharp.Entities {
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class TabListEntry {
|
||||
|
||||
/// <summary> Unformatted name of the player for autocompletion, etc. </summary>
|
||||
/// <remarks> Colour codes are always removed from this. </remarks>
|
||||
public string PlayerName;
|
||||
|
||||
public sealed class TabListEntry {
|
||||
/// <summary> Plain name of the player for autocompletion, etc. </summary>
|
||||
public string PlayerName;
|
||||
/// <summary> Formatted name for display in the player list. </summary>
|
||||
/// <remarks> Can include colour codes. </remarks>
|
||||
public string ListName;
|
||||
|
||||
public string ListNameColourless;
|
||||
/// <summary> Name of the group this player is in. </summary>
|
||||
/// <remarks> Can include colour codes. </remarks>
|
||||
public string GroupName;
|
||||
|
||||
public string Group;
|
||||
/// <summary> Player's rank within the group. (0 is highest) </summary>
|
||||
/// <remarks> Multiple group members can share the same rank,
|
||||
/// so a player's group rank is not a unique identifier. </remarks>
|
||||
/// <remarks> Multiple players can share the same rank, so this is not a unique identifier. </remarks>
|
||||
public byte GroupRank;
|
||||
|
||||
public TabListEntry(string playerName, string listName,
|
||||
string groupName, byte groupRank) {
|
||||
PlayerName = playerName;
|
||||
PlayerName = Utils.StripColours(playerName);
|
||||
ListName = listName;
|
||||
GroupName = groupName;
|
||||
ListNameColourless = Utils.StripColours(listName);
|
||||
Group = groupName;
|
||||
GroupRank = groupRank;
|
||||
}
|
||||
}
|
||||
|
@ -98,14 +98,14 @@ namespace ClassicalSharp.Network {
|
||||
|
||||
internal void AddTablistEntry(byte id, string playerName, string listName,
|
||||
string groupName, byte groupRank) {
|
||||
TabListEntry oldInfo = game.TabList.Entries[id];
|
||||
TabListEntry oldInfo = TabList.Entries[id];
|
||||
TabListEntry info = new TabListEntry(playerName, listName, groupName, groupRank);
|
||||
game.TabList.Entries[id] = info;
|
||||
TabList.Entries[id] = info;
|
||||
|
||||
if (oldInfo != null) {
|
||||
// Only redraw the tab list if something changed.
|
||||
if (info.PlayerName != oldInfo.PlayerName || info.ListName != oldInfo.ListName ||
|
||||
info.GroupName != oldInfo.GroupName || info.GroupRank != oldInfo.GroupRank) {
|
||||
info.Group != oldInfo.Group || info.GroupRank != oldInfo.GroupRank) {
|
||||
game.EntityEvents.RaiseTabListEntryChanged(id);
|
||||
}
|
||||
} else {
|
||||
@ -115,7 +115,7 @@ namespace ClassicalSharp.Network {
|
||||
|
||||
internal void RemoveTablistEntry(byte id) {
|
||||
game.EntityEvents.RaiseTabEntryRemoved(id);
|
||||
game.TabList.Entries[id] = null;
|
||||
TabList.Entries[id] = null;
|
||||
}
|
||||
|
||||
internal void DisableAddEntityHack() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user