3 using System.Collections.Generic;
4 using System.Collections.Immutable;
7 using Microsoft.Xna.Framework;
11 internal partial class ReadyCheck
13 private static LocalizedString ReadyCheckBody(
string name) =>
string.IsNullOrWhiteSpace(name) ? TextManager.Get(
"readycheck.serverbody") : TextManager.GetWithVariable(
"readycheck.body",
"[player]", name);
15 private static LocalizedString ReadyCheckStatus(
int ready,
int total) => TextManager.GetWithVariables(
"readycheck.readycount",
16 (
"[ready]", ready.ToString()),
17 (
"[total]", total.ToString()));
18 private static LocalizedString ReadyCheckPleaseWait(
int seconds) => TextManager.GetWithVariable(
"readycheck.pleasewait",
"[seconds]", seconds.ToString());
20 private static readonly LocalizedString readyCheckHeader = TextManager.Get(
"ReadyCheck.Title");
22 private static readonly LocalizedString noButton = TextManager.Get(
"No"),
23 yesButton = TextManager.Get(
"Yes"),
24 closeButton = TextManager.Get(
"Close");
26 private const string TimerData =
"Timer",
27 PromptData =
"ReadyCheck",
28 ResultData =
"ReadyCheckResults",
29 UserListData =
"ReadyUserList",
30 ReadySpriteData =
"ReadySprite";
32 private int lastSecond = 1;
34 private GUIMessageBox? msgBox;
35 private GUIMessageBox? resultsBox;
37 public static DateTime ReadyCheckCooldown = DateTime.MinValue;
39 public static bool IsReadyCheck(GUIComponent? msgBox) => msgBox?.UserData as
string == PromptData || msgBox?.UserData as
string == ResultData;
41 private void CreateMessageBox(
string author)
43 Vector2 relativeSize =
new Vector2(0.2f / GUI.AspectRatioAdjustment, 0.15f);
44 Point minSize =
new Point(300, 200);
45 msgBox =
new GUIMessageBox(readyCheckHeader, ReadyCheckBody(author),
new[] { yesButton, noButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = PromptData, Draggable =
true };
47 GUILayoutGroup contentLayout =
new GUILayoutGroup(
new RectTransform(
new Vector2(1f, 0.125f), msgBox.Content.RectTransform), childAnchor:
Anchor.Center);
48 new GUIProgressBar(
new RectTransform(
new Vector2(0.8f, 1f), contentLayout.RectTransform), 0.0f, GUIStyle.Orange) { UserData = TimerData };
51 msgBox.Buttons[0].OnClicked = delegate
54 if (GameMain.Client ==
null) {
return true; }
55 SendState(ReadyStatus.Yes);
56 CreateResultsMessage();
61 msgBox.Buttons[1].OnClicked = delegate
64 if (GameMain.Client ==
null) {
return true; }
65 SendState(ReadyStatus.No);
66 CreateResultsMessage();
71 private void CreateResultsMessage()
73 if (GameMain.Client ==
null) {
return; }
75 Vector2 relativeSize =
new Vector2(0.2f, 0.3f);
76 Point minSize =
new Point(300, 400);
77 resultsBox =
new GUIMessageBox(readyCheckHeader,
string.Empty,
new[] { closeButton }, relativeSize, minSize, type: GUIMessageBox.Type.Vote) { UserData = ResultData, Draggable =
true };
80 resultsBox.RectTransform.ScreenSpaceOffset = msgBox.RectTransform.ScreenSpaceOffset;
83 GUIListBox listBox =
new GUIListBox(
new RectTransform(
new Vector2(1f, 0.8f), resultsBox.Content.RectTransform)) { UserData = UserListData };
85 foreach (var (
id, status) in Clients)
87 Client? client = GameMain.
Client.ConnectedClients.FirstOrDefault(c => c.SessionId ==
id);
90 string list = GameMain.Client.ConnectedClients.Aggregate(
"Available clients:\n", (current, c) => current + $
"{c.SessionId}: {c.Name}\n");
91 DebugConsole.AddWarning($
"Client ID {id} was reported in ready check but was not found.\n" + list.TrimEnd(
'\n'));
94 GUIFrame container =
new GUIFrame(
new RectTransform(
new Vector2(1f, 0.15f), listBox.Content.RectTransform), style:
"ListBoxElement") { UserData =
id };
95 GUILayoutGroup frame =
new GUILayoutGroup(
new RectTransform(Vector2.One, container.RectTransform), isHorizontal:
true) { Stretch =
true };
97 int height = frame.Rect.Height;
102 if (jobPrefab?.Icon !=
null)
105 new GUIImage(
new RectTransform(
new Point(height, height), frame.RectTransform), jobPrefab.Icon, scaleToFit:
true) { Color = jobPrefab.
UIColor };
108 new GUITextBlock(
new RectTransform(
new Vector2(0.75f, 1), frame.RectTransform), client?.Name ?? $
"Unknown ID {id}", jobPrefab?.UIColor ?? Color.White, textAlignment: Alignment.Center) { AutoScaleHorizontal =
true };
109 var statusIcon =
new GUIImage(
new RectTransform(
new Point(height, height), frame.RectTransform),
null, scaleToFit:
true) { UserData = ReadySpriteData };
110 UpdateStatusIcon(statusIcon, status);
113 resultsBox.Buttons[0].OnClicked = delegate
120 private void UpdateBar()
122 double elapsedTime = (DateTime.Now - startTime).TotalSeconds;
123 if (msgBox is { Closed:
false } && GUIMessageBox.MessageBoxes.Contains(msgBox))
125 if (msgBox.FindChild(TimerData,
true) is GUIProgressBar bar)
127 bar.BarSize = (float)(elapsedTime / (endTime - startTime).TotalSeconds);
132 int second = (int)Math.Ceiling(elapsedTime);
133 if (second > lastSecond)
135 if (msgBox is { Closed:
false })
143 private static void CloseLingeringPopups()
145 foreach (GUIComponent box
in GUIMessageBox.MessageBoxes.ToImmutableArray())
147 if (box is not GUIMessageBox msgBox) {
continue; }
149 if (msgBox.UserData is PromptData or ResultData)
158 ReadyCheckState state = (ReadyCheckState)inc.
ReadByte();
159 CrewManager? crewManager = GameMain.GameSession?.CrewManager;
160 var otherClients = GameMain.Client.ConnectedClients;
161 if (crewManager ==
null || otherClients ==
null)
163 if (state == ReadyCheckState.Start)
165 SendState(ReadyStatus.No);
172 case ReadyCheckState.Start:
173 CloseLingeringPopups();
186 isOwn = authorId == GameMain.Client.SessionId;
190 List<byte> clients =
new List<byte>();
191 for (
int i = 0; i < clientCount; i++)
196 ReadyCheck rCheck =
new ReadyCheck(clients,
197 DateTimeOffset.FromUnixTimeSeconds(startTime).LocalDateTime,
198 DateTimeOffset.FromUnixTimeSeconds(endTime).LocalDateTime);
199 crewManager.ActiveReadyCheck = rCheck;
203 SendState(ReadyStatus.Yes);
204 rCheck.CreateResultsMessage();
208 rCheck.CreateMessageBox(author);
211 if (hasAuthor && rCheck.Clients.ContainsKey(authorId))
213 rCheck.Clients[authorId] = ReadyStatus.Yes;
216 case ReadyCheckState.Update:
217 ReadyStatus newState = (ReadyStatus)inc.
ReadByte();
219 crewManager.ActiveReadyCheck?.UpdateState(targetId, newState);
221 case ReadyCheckState.End:
223 for (
int i = 0; i < count; i++)
226 ReadyStatus status = (ReadyStatus)inc.
ReadByte();
227 crewManager.ActiveReadyCheck?.UpdateState(
id, status);
230 crewManager.ActiveReadyCheck?.EndReadyCheck();
231 crewManager.ActiveReadyCheck?.msgBox?.Close();
232 crewManager.ActiveReadyCheck =
null;
237 partial
void EndReadyCheck()
239 if (IsFinished) {
return; }
242 int readyCount = Clients.Count(
static pair => pair.Value == ReadyStatus.Yes);
243 int totalCount = Clients.Count;
247 private void UpdateState(
byte id, ReadyStatus status)
249 if (Clients.ContainsKey(
id))
251 Clients[id] = status;
254 if (resultsBox ==
null || resultsBox.Closed || !GUIMessageBox.MessageBoxes.Contains(resultsBox)) {
return; }
255 if (resultsBox.Content.FindChild(UserListData) is not GUIListBox userList) {
return; }
257 var child = userList.Content.FindChild(
id);
258 if (child?.GetChild<GUILayoutGroup>().FindChild(ReadySpriteData) is not GUIImage image) {
return; }
259 UpdateStatusIcon(image, status);
262 private static void UpdateStatusIcon(GUIImage image, ReadyStatus status)
267 case ReadyStatus.Yes:
268 style =
"MissionCompletedIcon";
271 style =
"MissionFailedIcon";
276 image.ApplyStyle(GUIStyle.GetComponentStyle(style));
279 private static void SendState(ReadyStatus status)
283 msg.
WriteByte((
byte)ReadyCheckState.Update);
285 GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
288 public static void CreateReadyCheck()
290 if (ReadyCheckCooldown < DateTime.Now)
293 ReadyCheckCooldown = DateTime.Now.AddMinutes(1);
297 msg.
WriteByte((
byte)ReadyCheckState.Start);
298 GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
302 GUIMessageBox msgBox =
new GUIMessageBox(readyCheckHeader, ReadyCheckPleaseWait((ReadyCheckCooldown - DateTime.Now).Seconds),
new[] { closeButton });
303 msgBox.Buttons[0].OnClicked = delegate
static ChatMessage Create(string senderName, string text, ChatMessageType type, Entity sender, Client client=null, PlayerConnectionChangeType changeType=PlayerConnectionChangeType.None, Color? textColor=null)
Client(string name, byte sessionId)