using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using BSO.Net; namespace BSO { /// /// Anzeige oben links: eigene Position (LocalPlayer) und Online-Status mit allen Mitspielern (Multiplayer). /// F9 blendet ein/aus, F10 schreibt alle Entities in eine Datei. /// /// Update rechnet (wenn die Engine-Uhr weiterlaeuft), Draw gibt den fertigen Text bei jedem Durchlauf aus - die /// Engine zeigt Vision::Message-Text nur fuer einen Durchlauf, sonst flackert die Anzeige. /// public static class PositionHud { const int X = 10, Y = 10, Line = 18; static bool visible = true, rateLogged; static int frame; static string status = ""; static byte[][] lines = Array.Empty(); static readonly Stopwatch rateClock = Stopwatch.StartNew(); static int draws; public static void Update() { frame++; if (Input.Pressed(Input.F9)) visible = !visible; if (!LocalPlayer.InScene) { lines = Array.Empty(); return; } // Menue / Laden if (Input.Pressed(Input.F10)) { Entities.Dump(LocalPlayer.Camera); status = "Entities gespeichert: BSO\\entities.txt"; } // Text nur alle paar Durchlaeufe neu bauen - Draw gibt ihn dazwischen unveraendert aus if (frame % 5 != 0 && lines.Length > 0) return; PlayerState me = LocalPlayer.State; var text = new List { $"BSO {Bootstrap.ModVersion} [F8] Online [F9] Anzeige [F10] Entities [F11] Vollbild/Fenster", me.Mode switch { PlayerMode.InBus => $"Du: im Bus {LocalPlayer.Bus.Pos} {LocalPlayer.Speed * 0.036f:0} km/h", PlayerMode.OnFoot => $"Du: zu Fuss {LocalPlayer.Character.Pos}" + (LocalPlayer.Bus.Valid ? $" Bus {Meters(LocalPlayer.Bus.Pos - LocalPlayer.Character.Pos)} entfernt" : ""), _ => "Du: -" }, "", }; AddOnline(text, me); if (status.Length > 0) text.Add(status); lines = text.ConvertAll(Vision.ToAscii).ToArray(); } static void AddOnline(List text, PlayerState me) { switch (Multiplayer.Status) { case Multiplayer.NetStatus.Offline when Multiplayer.AccountProblem != null: text.Add(Multiplayer.AccountProblem); return; case Multiplayer.NetStatus.Offline: text.Add($"Offline - [F8] verbinden mit {Multiplayer.ServerAddress} als {Multiplayer.PlayerName}"); if (Multiplayer.LastError.Length > 0) text.Add(" " + Multiplayer.LastError); return; case Multiplayer.NetStatus.Connecting: text.Add($"Verbinde mit {Multiplayer.ServerAddress} ..."); return; } if (!Multiplayer.Welcomed) { text.Add($"Verbinde mit {Multiplayer.ServerAddress} ... warte auf Server"); return; } string ping = Multiplayer.PingMs >= 0 ? $"{Multiplayer.PingMs} ms" : "..."; text.Add($"Online: \"{Multiplayer.ServerName}\" ({ping}) - {Multiplayer.Players.Count + 1} Spieler [F8] trennen"); foreach (Multiplayer.Remote p in Multiplayer.Players.Values.OrderBy(p => p.HasState ? p.State.DistanceTo(me) : float.MaxValue)) { if (!p.HasState || p.State.Mode == PlayerMode.Menu) { text.Add($" {p.Name,-16} im Menue"); continue; } string mode = p.State.Mode == PlayerMode.InBus ? "Bus " : "zu Fuss "; string dist = me.Mode == PlayerMode.Menu ? "" : $"{p.State.DistanceTo(me) / 100,6:0} m"; text.Add($" {p.Name,-16} {mode} {dist} {p.State.Speed * 0.036f,3:0} km/h"); } } static string Meters(Vec3 d) => $"{d.Length / 100:0} m"; public static void Draw() { CountDraws(); if (!visible) return; byte[][] current = lines; for (int i = 0; i < current.Length; i++) Vision.Print(X, Y + i * Line, current[i]); } static void CountDraws() { draws++; if (rateLogged || rateClock.ElapsedMilliseconds < 5000) return; if (!LocalPlayer.InScene) { draws = 0; rateClock.Restart(); return; } // erst im Spiel messen rateLogged = true; Bootstrap.Log($"Anzeige: {draws / 5} Durchlaeufe pro Sekunde"); } } }