MSCO 2.10.2: passenger only while moving, drain plug sync, admin VEHICLE tab, profile layout
- Passenger: auto ride-along only when the vehicle moves (speed from its motion, also for kinematic remote cars) - no more getting stuck while working in the engine bay or re-captured - Bolts with a function (drain plug: stage 0-5 = oil leak on) are synced as stage and applied through the bolt's own stage logic; world authority sends its state once after joining. Fixes endless oil leaking when players had the drain plug in different states - Admin menu VEHICLE tab: fluids with FILL/EMPTY, all Database parts with wear/damage, REPAIR/BREAK per part, REPAIR ALL, filter - Server profile: info and players as two equal cards, no wrapping - fsmdump also lists FSM variables Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+71
-26
@@ -599,8 +599,8 @@ namespace MSCO
|
||||
GUILayout.BeginVertical();
|
||||
GUILayout.Label(s.Name, titleStyle);
|
||||
GUILayout.BeginHorizontal();
|
||||
Rect badgeRow = GUILayoutUtility.GetRect(S(10), S(20), GUILayout.ExpandWidth(true));
|
||||
float bx = badgeRow.x;
|
||||
Rect badgeRow = GUILayoutUtility.GetRect(S(10), S(22), GUILayout.ExpandWidth(true));
|
||||
float bx = badgeRow.x + titleStyle.padding.left;
|
||||
if (s.Featured) bx += Pill(bx, badgeRow, "FEATURED", pillFeaturedStyle);
|
||||
bool official = s.Tags != null && s.Tags.Contains("official"); // vergibt nur ein MSCO-Admin
|
||||
if (official) bx += Pill(bx, badgeRow, "OFFICIAL", pillStyle);
|
||||
@@ -651,39 +651,84 @@ namespace MSCO
|
||||
GUILayout.Space(S(10));
|
||||
}
|
||||
|
||||
// Infos links, Spielerliste rechts
|
||||
// Zwei Karten gleicher Höhe: Infos links, Spielerliste rechts
|
||||
bool versionOk = MSCO.Compatible(s.Version);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.BeginVertical(GUILayout.Width(S(250)));
|
||||
InfoLine("HOSTED BY", string.IsNullOrEmpty(s.Owner) ? "unknown" : s.Owner, rowNameStyle);
|
||||
InfoLine("VERSION", "v" + s.Version + (versionOk ? "" : " (you: v" + MSCO.ModVersion + ")"), versionOk ? rowNameStyle : badgeStyle);
|
||||
InfoLine("STEAM", s.RequireSteam ? "Required" : "Not required", rowNameStyle);
|
||||
InfoLine("ONLINE FOR", s.UptimeSeconds >= 0 ? FormatUptime(s.UptimeSeconds) : "-", rowNameStyle);
|
||||
InfoLine("PING", s.Ping < 0 ? "-" : s.Ping + " ms", PingStyle(s.Ping));
|
||||
InfoLine("ADDRESS", s.Address + ":" + s.Port, rowNameStyle);
|
||||
if (s.Mods.Count > 0) InfoLine("MODS", string.Join(", ", s.Mods.ToArray()), textStyle);
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.Space(S(16));
|
||||
float cardHeight = Mathf.Clamp(contentHeight - S(250), S(240), S(380));
|
||||
Rect area = GUILayoutUtility.GetRect(S(10), cardHeight, GUILayout.ExpandWidth(true));
|
||||
float gap = S(16), leftWidth = Mathf.Round(area.width * 0.44f);
|
||||
var info = new Rect(area.x, area.y, leftWidth, area.height);
|
||||
var list = new Rect(area.x + leftWidth + gap, area.y, area.width - leftWidth - gap, area.height);
|
||||
GUI.Box(info, GUIContent.none, rowBox);
|
||||
GUI.Box(list, GUIContent.none, rowBox);
|
||||
|
||||
GUILayout.BeginVertical(rowBox, GUILayout.ExpandWidth(true), GUILayout.MinHeight(S(150)));
|
||||
GUILayout.Space(S(6));
|
||||
GUILayout.Label("PLAYERS " + s.Players + " / " + s.MaxPlayers, captionStyle);
|
||||
profileScroll = GUILayout.BeginScrollView(profileScroll, GUILayout.Height(ListHeight(S(330f), S(130f))));
|
||||
EnsureProfileStyles();
|
||||
float x = info.x + S(20), w = info.width - S(40), y = info.y + S(16);
|
||||
GUI.Label(new Rect(x, y, w, S(18)), "SERVER INFO", captionStyle);
|
||||
y += S(28);
|
||||
y = ProfileRow(x, y, w, "HOSTED BY", string.IsNullOrEmpty(s.Owner) ? "unknown" : s.Owner, profileValueStyle);
|
||||
y = ProfileRow(x, y, w, "VERSION", "v" + s.Version + (versionOk ? "" : " (you: v" + MSCO.ModVersion + ")"), versionOk ? profileValueStyle : badgeStyle);
|
||||
y = ProfileRow(x, y, w, "STEAM", s.RequireSteam ? "Required" : "Not required", profileValueStyle);
|
||||
y = ProfileRow(x, y, w, "ONLINE FOR", s.UptimeSeconds >= 0 ? FormatUptime(s.UptimeSeconds) : "-", profileValueStyle);
|
||||
y = ProfileRow(x, y, w, "PING", s.Ping < 0 ? "-" : s.Ping + " ms", PingStyle(s.Ping));
|
||||
y = ProfileRow(x, y, w, "ADDRESS", s.Address + ":" + s.Port, profileValueStyle);
|
||||
if (s.Mods.Count > 0) ProfileRow(x, y, w, "MODS", string.Join(", ", s.Mods.ToArray()), profileWrapStyle);
|
||||
|
||||
// Spieler
|
||||
float lx = list.x + S(20), lw = list.width - S(40), ly = list.y + S(16);
|
||||
GUI.Label(new Rect(lx, ly, lw, S(18)), "PLAYERS " + s.Players + " / " + s.MaxPlayers, captionStyle);
|
||||
ly += S(30);
|
||||
var listArea = new Rect(lx, ly, lw, list.yMax - ly - S(14));
|
||||
if (s.PlayerNames == null || s.PlayerNames.Count == 0)
|
||||
GUILayout.Label("Nobody online right now.", textStyle);
|
||||
{
|
||||
GUI.Label(listArea, "Nobody online right now.", profileEmptyStyle);
|
||||
}
|
||||
else
|
||||
foreach (string name in s.PlayerNames)
|
||||
{
|
||||
float rowH = S(32);
|
||||
var inner = new Rect(0, 0, lw - S(14), rowH * s.PlayerNames.Count);
|
||||
profileScroll = GUI.BeginScrollView(listArea, profileScroll, inner);
|
||||
for (int i = 0; i < s.PlayerNames.Count; i++)
|
||||
{
|
||||
Rect line = GUILayoutUtility.GetRect(S(10), S(24), GUILayout.ExpandWidth(true));
|
||||
GUI.DrawTexture(new Rect(line.x + S(2), line.y + S(8), S(8), S(8)), texOnline);
|
||||
GUI.Label(new Rect(line.x + S(18), line.y, line.width - S(18), line.height), name, rowNameStyle);
|
||||
var line = new Rect(0, i * rowH, inner.width, rowH);
|
||||
if (i > 0) GUI.DrawTexture(new Rect(line.x, line.y, line.width, S(1)), texDivider);
|
||||
GUI.DrawTexture(new Rect(line.x + S(2), line.y + (rowH - S(8)) / 2f, S(8), S(8)), texOnline);
|
||||
GUI.Label(new Rect(line.x + S(20), line.y, line.width - S(20), rowH), s.PlayerNames[i], profileValueStyle);
|
||||
}
|
||||
GUILayout.EndScrollView();
|
||||
GUILayout.EndVertical();
|
||||
GUILayout.EndHorizontal();
|
||||
GUI.EndScrollView();
|
||||
}
|
||||
GUILayout.FlexibleSpace();
|
||||
}
|
||||
|
||||
GUIStyle profileValueStyle, profileWrapStyle, profileCaptionStyle, profileEmptyStyle;
|
||||
|
||||
void EnsureProfileStyles()
|
||||
{
|
||||
if (profileValueStyle != null && profileValueStyle.fontSize == P(15)) return;
|
||||
profileValueStyle = new GUIStyle(rowNameStyle) { fontSize = P(15), wordWrap = false, clipping = TextClipping.Clip, alignment = TextAnchor.MiddleLeft };
|
||||
profileWrapStyle = new GUIStyle(profileValueStyle) { wordWrap = true, fontSize = P(13), alignment = TextAnchor.UpperLeft };
|
||||
profileWrapStyle.normal.textColor = Muted;
|
||||
profileCaptionStyle = new GUIStyle(captionStyle) { alignment = TextAnchor.MiddleLeft };
|
||||
profileCaptionStyle.margin = new RectOffset();
|
||||
profileEmptyStyle = new GUIStyle(textStyle) { alignment = TextAnchor.MiddleCenter };
|
||||
}
|
||||
|
||||
/// <summary>Info-Zeile der Server-Karte: Bezeichnung links, Wert daneben, feine Linie darunter. Gibt das nächste y zurück.</summary>
|
||||
float ProfileRow(float x, float y, float w, string caption, string value, GUIStyle valueStyle)
|
||||
{
|
||||
const float captionWidth = 120f;
|
||||
float valueWidth = w - S(captionWidth);
|
||||
float h = S(36);
|
||||
if (valueStyle.wordWrap) h = Mathf.Max(h, valueStyle.CalcHeight(new GUIContent(value), valueWidth) + S(16));
|
||||
GUI.Label(new Rect(x, y, S(captionWidth), S(36)), caption, profileCaptionStyle);
|
||||
var valueRect = valueStyle.wordWrap ? new Rect(x + S(captionWidth), y + S(9), valueWidth, h - S(9)) : new Rect(x + S(captionWidth), y, valueWidth, S(36));
|
||||
GUIStyle style = valueStyle;
|
||||
if (!valueStyle.wordWrap && valueStyle.alignment != TextAnchor.MiddleLeft)
|
||||
style = new GUIStyle(valueStyle) { alignment = TextAnchor.MiddleLeft };
|
||||
GUI.Label(valueRect, value, style);
|
||||
GUI.DrawTexture(new Rect(x, y + h, w, S(1)), texDivider);
|
||||
return y + h + S(1);
|
||||
}
|
||||
|
||||
/// <summary>Eine Info-Zeile: Bezeichnung links (grau), Wert rechts.</summary>
|
||||
void InfoLine(string caption, string value, GUIStyle valueStyle)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user