diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index ebf8a25..affc0b5 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ diff --git a/Source/ArachnaeSwarm/Hediffs/ARA_GestaltNode/GestaltBandwidthGizmo.cs b/Source/ArachnaeSwarm/Hediffs/ARA_GestaltNode/GestaltBandwidthGizmo.cs index 1a7a792..8c3f8ee 100644 --- a/Source/ArachnaeSwarm/Hediffs/ARA_GestaltNode/GestaltBandwidthGizmo.cs +++ b/Source/ArachnaeSwarm/Hediffs/ARA_GestaltNode/GestaltBandwidthGizmo.cs @@ -1,100 +1,121 @@ -using RimWorld; +using RimWorld; using UnityEngine; using Verse; -using Verse.Sound; namespace ArachnaeSwarm { + [StaticConstructorOnStartup] public class GestaltBandwidthGizmo : Gizmo { - private Pawn_GestaltTracker tracker; - + private static readonly Color EmptyBlockColor = new Color(0.3f, 0.3f, 0.3f, 1f); + private static readonly Color FilledBlockColor = ColorLibrary.Yellow; + private static readonly Color ExcessBlockColor = ColorLibrary.Red; + + private readonly Pawn_GestaltTracker tracker; + public GestaltBandwidthGizmo(Pawn_GestaltTracker tracker) { this.tracker = tracker; - this.Order = -90f; // 放在最前面 + Order = -90f; } - - public override bool Visible => tracker != null && tracker.Pawn != null && tracker.Pawn.IsGestaltNode(GestaltNodeType.OverlordNode); - - public override float GetWidth(float maxWidth) - { - return 160f; // 稍微加宽以显示更多信息 - } - + + public override bool Visible => + tracker != null && + tracker.Pawn != null && + tracker.Pawn.IsGestaltNode(GestaltNodeType.OverlordNode) && + Find.Selector.SelectedPawns.Count == 1; + public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms) { - Rect rect = new Rect(topLeft.x, topLeft.y, GetWidth(maxWidth), 75f); // 增加高度以容纳超载信息 - Rect rect2 = rect.ContractedBy(6f); - - // 背景 - GUI.color = parms.lowLight ? Command.LowLightBgColor : Color.white; - GenUI.DrawTextureWithMaterial(rect, Command.BGTex, null); - GUI.color = Color.white; - - // 标题 - Text.Font = GameFont.Tiny; - Text.Anchor = TextAnchor.UpperCenter; - Widgets.Label(new Rect(rect2.x, rect2.y, rect2.width, 18f), "ARA_GestaltBandwidth".Translate()); - - // 带宽条 - Rect barRect = new Rect(rect2.x, rect2.y + 20f, rect2.width, 20f); - float fillPercent = tracker.TotalBandwidth > 0 ? - Mathf.Clamp01((float)tracker.UsedBandwidth / tracker.TotalBandwidth) : - 1f; - - // 超载时使用红色,否则使用蓝色 - Color barColor = tracker.IsNetworkOverloaded() ? - new Color(0.8f, 0.2f, 0.2f) : // 红色 - new Color(0.2f, 0.6f, 0.8f); // 蓝色 - - Widgets.FillableBar(barRect, fillPercent, SolidColorMaterials.NewSolidColorTexture(barColor)); - - // 带宽文本 - Text.Font = GameFont.Small; - Text.Anchor = TextAnchor.MiddleCenter; - string bandwidthText = tracker.IsNetworkOverloaded() ? - $"{tracker.UsedBandwidth}/{tracker.TotalBandwidth} (+{tracker.NetworkOverload})" : - $"{tracker.UsedBandwidth}/{tracker.TotalBandwidth}"; - - if (tracker.TotalBandwidth <= 0) - { - bandwidthText = $"{tracker.UsedBandwidth} (∞)"; - } - - Widgets.Label(barRect, bandwidthText); - - // 控制组数量 - Rect groupRect = new Rect(rect2.x, rect2.y + 45f, rect2.width, 20f); - Text.Font = GameFont.Tiny; - Text.Anchor = TextAnchor.MiddleLeft; - string groupText = "ARA_GestaltGroup".Translate() + $": {tracker.ControlGroups.Count}/{tracker.TotalAvailableControlGroups}"; - Widgets.Label(groupRect, groupText); - - Text.Anchor = TextAnchor.UpperLeft; - Text.Font = GameFont.Small; - - // 鼠标悬停提示 - if (Mouse.IsOver(rect)) - { - TooltipHandler.TipRegion(rect, () => - { - string tip = "ARA_GestaltBandwidthTip".Translate(tracker.UsedBandwidth, tracker.TotalBandwidth); - int availableBandwidth = Mathf.Max(0, tracker.TotalBandwidth - tracker.UsedBandwidth); - tip += $"\n\n{"ARA_AvailableBandwidth".Translate(availableBandwidth)}"; + Rect rect = new Rect(topLeft.x, topLeft.y, GetWidth(maxWidth), 75f); + Rect innerRect = rect.ContractedBy(6f); - if (tracker.IsNetworkOverloaded()) - { - tip += $"\n\n{"ARA_GestaltBandwidthExceeded".Translate()}".Colorize(Color.red); - } - - return tip; - }, 7234342); - - Widgets.DrawHighlight(rect); + Widgets.DrawWindowBackground(rect); + + int totalBandwidth = tracker.TotalBandwidth; + int usedBandwidth = tracker.UsedBandwidth; + string countText = usedBandwidth.ToString("F0") + " / " + totalBandwidth.ToString("F0"); + + string tip = "ARA_GestaltBandwidth".Translate().Colorize(ColoredText.TipSectionTitleColor) + ": " + countText + "\n\n" + + "ARA_GestaltBandwidthTip".Translate(usedBandwidth, totalBandwidth); + tip += "\n\n" + "ARA_AvailableBandwidth".Translate(Mathf.Max(0, totalBandwidth - usedBandwidth)); + tip += "\n" + "ARA_GestaltGroup".Translate() + ": " + tracker.ControlGroups.Count + "/" + tracker.TotalAvailableControlGroups; + if (tracker.IsNetworkOverloaded()) + { + tip += "\n\n" + "ARA_GestaltBandwidthExceeded".Translate().Colorize(ColorLibrary.RedReadable); } - + TooltipHandler.TipRegion(rect, tip); + + Text.Font = GameFont.Small; + Text.Anchor = TextAnchor.UpperLeft; + Rect headerRect = new Rect(innerRect.x, innerRect.y, innerRect.width, 20f); + Widgets.Label(headerRect, "ARA_GestaltBandwidth".Translate()); + Text.Anchor = TextAnchor.UpperRight; + Widgets.Label(headerRect, countText); + Text.Anchor = TextAnchor.UpperLeft; + + int cellCount = Mathf.Max(usedBandwidth, totalBandwidth); + Rect gridRect = new Rect(innerRect.x, headerRect.yMax + 6f, innerRect.width, innerRect.height - headerRect.height - 6f); + + int rows = 2; + int cellSize = Mathf.FloorToInt(gridRect.height / rows); + int cols = cellSize > 0 ? Mathf.FloorToInt(gridRect.width / cellSize) : 0; + + int failSafe = 0; + while (rows * cols < cellCount) + { + rows++; + cellSize = Mathf.FloorToInt(gridRect.height / rows); + cols = cellSize > 0 ? Mathf.FloorToInt(gridRect.width / cellSize) : 0; + failSafe++; + + if (failSafe >= 1000) + { + Log.Error("Failed to fit gestalt bandwidth cells into gizmo rect."); + return new GizmoResult(GizmoState.Clear); + } + } + + if (cellSize <= 0 || cols <= 0) + { + return new GizmoResult(GizmoState.Clear); + } + + float xOffset = (gridRect.width - cols * cellSize) / 2f; + int current = 0; + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + current++; + if (current > cellCount) + { + continue; + } + + Rect cellRect = new Rect( + gridRect.x + c * cellSize + xOffset, + gridRect.y + r * cellSize, + cellSize, + cellSize).ContractedBy(2f); + + if (current <= usedBandwidth) + { + Widgets.DrawRectFast(cellRect, current <= totalBandwidth ? FilledBlockColor : ExcessBlockColor); + } + else + { + Widgets.DrawRectFast(cellRect, EmptyBlockColor); + } + } + } + return new GizmoResult(GizmoState.Clear); } + + public override float GetWidth(float maxWidth) + { + return 136f; + } } } diff --git a/Source/ArachnaeSwarm/Hediffs/ARA_GestaltNode/GestaltControlGroupGizmo.cs b/Source/ArachnaeSwarm/Hediffs/ARA_GestaltNode/GestaltControlGroupGizmo.cs index d02b73f..46a2558 100644 --- a/Source/ArachnaeSwarm/Hediffs/ARA_GestaltNode/GestaltControlGroupGizmo.cs +++ b/Source/ArachnaeSwarm/Hediffs/ARA_GestaltNode/GestaltControlGroupGizmo.cs @@ -1,82 +1,204 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using RimWorld; using UnityEngine; using Verse; +using Verse.Sound; namespace ArachnaeSwarm { public class GestaltControlGroupGizmo : Gizmo { - private GestaltControlGroup controlGroup; + private static readonly Color UncontrolledPawnBackgroundColor = new Color32(byte.MaxValue, 25, 25, 55); - public override bool Visible => controlGroup.AssignedPawns.Count > 0; - public override float Order => -89f; + private readonly GestaltControlGroup controlGroup; + + public override bool Visible + { + get + { + if (controlGroup?.Tracker?.Pawn == null) + { + return false; + } + + if (controlGroup.AssignedPawns.Count <= 0) + { + return Find.Selector.SelectedPawns.Count == 1; + } + + return true; + } + } + + public override float Order => controlGroup.AssignedPawns.Count > 0 ? -89f : -88f; public GestaltControlGroupGizmo(GestaltControlGroup controlGroup) { this.controlGroup = controlGroup; + Order = -89f; } public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms) { + if (controlGroup?.Tracker == null) + { + return new GizmoResult(GizmoState.Clear); + } + + AcceptanceReport canControlPawns = controlGroup.Tracker.CanControlPawns; + disabled = !canControlPawns; + disabledReason = canControlPawns.Reason; + Rect rect = new Rect(topLeft.x, topLeft.y, GetWidth(maxWidth), 75f); Rect innerRect = rect.ContractedBy(6f); + bool mouseOver = Mouse.IsOver(innerRect); + List assignedPawns = controlGroup.AssignedPawns; - // 背景 + Material material = (disabled || parms.lowLight || assignedPawns.Count <= 0) ? TexUI.GrayscaleGUI : null; GUI.color = parms.lowLight ? Command.LowLightBgColor : Color.white; - GenUI.DrawTextureWithMaterial(rect, Command.BGTex, null); + GenUI.DrawTextureWithMaterial(rect, parms.shrunk ? Command.BGTexShrunk : Command.BGTex, material); GUI.color = Color.white; - // 控制组编号 Text.Font = GameFont.Small; Text.Anchor = TextAnchor.UpperLeft; - string label = "ARA_GestaltGroup".Translate(); - Widgets.Label(innerRect, label); - // 显示被控制的pawn - Rect pawnRect = new Rect(innerRect.x, innerRect.y + 20f, innerRect.width, innerRect.height - 20f); + TaggedString title = ("ARA_GestaltGroup".Translate() + " " + controlGroup.Index).Truncate(innerRect.width); + Vector2 titleSize = Text.CalcSize(title); + Rect titleRect = new Rect(innerRect.x, innerRect.y, titleSize.x, titleSize.y); + Widgets.Label(titleRect, title); + + if (Mouse.IsOver(titleRect)) + { + Widgets.DrawHighlight(titleRect); + if (Widgets.ButtonInvisible(titleRect)) + { + Find.Selector.ClearSelection(); + for (int i = 0; i < assignedPawns.Count; i++) + { + Find.Selector.Select(assignedPawns[i]); + } + } + } - List assignedPawns = controlGroup.AssignedPawns; if (assignedPawns.Count == 0) { GUI.color = ColoredText.SubtleGrayColor; Text.Anchor = TextAnchor.MiddleCenter; - Widgets.Label(pawnRect, "(" + "ARA_NoGestaltPawns".Translate() + ")"); + Widgets.Label(innerRect, "(" + "ARA_NoGestaltPawns".Translate() + ")"); Text.Anchor = TextAnchor.UpperLeft; GUI.color = Color.white; return new GizmoResult(GizmoState.Clear); } - // 计算pawn图标布局 - float iconSize = Mathf.Min(pawnRect.width / assignedPawns.Count, pawnRect.height); - float spacing = (pawnRect.width - (iconSize * assignedPawns.Count)) / (assignedPawns.Count + 1); + Rect pawnsRect = new Rect(innerRect.x, innerRect.y + 26f + 4f, innerRect.width, innerRect.height - 26f - 4f); + float cellSize = pawnsRect.height; + int cols = 0; + int rows = 0; - for (int i = 0; i < assignedPawns.Count; i++) + for (float candidate = cellSize; candidate >= 1f; candidate -= 1f) { - Rect iconRect = new Rect( - pawnRect.x + spacing + (iconSize + spacing) * i, - pawnRect.y + (pawnRect.height - iconSize) / 2, - iconSize, - iconSize - ); - - Pawn pawn = assignedPawns[i]; - Widgets.ThingIcon(iconRect, pawn); - - if (Mouse.IsOver(iconRect)) + cols = Mathf.FloorToInt(pawnsRect.width / candidate); + rows = Mathf.FloorToInt(pawnsRect.height / candidate); + if (cols * rows >= assignedPawns.Count) { - Widgets.DrawHighlight(iconRect); - TooltipHandler.TipRegion(iconRect, pawn.LabelCap); - - if (Widgets.ButtonInvisible(iconRect)) - { - CameraJumper.TryJumpAndSelect(pawn); - } + cellSize = candidate; + break; } } - return new GizmoResult(GizmoState.Clear); + if (cols <= 0 || rows <= 0) + { + return new GizmoResult(mouseOver ? GizmoState.Mouseover : GizmoState.Clear); + } + + float xOffset = (pawnsRect.width - cols * cellSize) / 2f; + float yOffset = (pawnsRect.height - rows * cellSize) / 2f; + + int index = 0; + for (int r = 0; r < rows; r++) + { + for (int c = 0; c < cols; c++) + { + if (index >= assignedPawns.Count) + { + break; + } + + Rect pawnRect = new Rect( + pawnsRect.x + c * cellSize + xOffset, + pawnsRect.y + r * cellSize + yOffset, + cellSize, + cellSize); + + Pawn pawn = assignedPawns[index]; + RenderTexture portrait = PortraitsCache.Get( + pawn, + pawnRect.size, + Rot4.East, + default(Vector3), + pawn.kindDef.controlGroupPortraitZoom); + + if (!controlGroup.Tracker.ControlledPawns.Contains(pawn)) + { + Widgets.DrawRectFast(pawnRect, UncontrolledPawnBackgroundColor); + } + + GUI.DrawTexture(pawnRect, portrait); + + if (Mouse.IsOver(pawnRect)) + { + Widgets.DrawHighlight(pawnRect); + MouseoverSounds.DoRegion(pawnRect, SoundDefOf.Mouseover_Command); + + if (Event.current.type == EventType.MouseDown) + { + if (Event.current.shift) + { + Find.Selector.Select(pawn); + } + else + { + CameraJumper.TryJumpAndSelect(pawn); + } + } + + TargetHighlighter.Highlight(pawn, arrow: true, colonistBar: false); + } + + if (Find.Selector.IsSelected(pawn)) + { + SelectionDrawerUtility.DrawSelectionOverlayOnGUI(pawn, pawnRect, 0.8f / cols, 20f); + } + + index++; + } + + if (index >= assignedPawns.Count) + { + break; + } + } + + if (Find.WindowStack.FloatMenu == null) + { + TooltipHandler.TipRegion(rect, () => + { + string tip = ("ARA_GestaltGroup".Translate() + " #" + controlGroup.Index).Colorize(ColoredText.TipSectionTitleColor) + "\n\n"; + IEnumerable entries = assignedPawns.Select(p => + p.LabelCap + " (+" + p.GetStatValue(ARA_StatDefOf.ARA_GestaltBandwidthCost).ToString("0.#") + ")"); + tip += "AssignedPawns".Translate().Colorize(ColoredText.TipSectionTitleColor) + "\n" + entries.ToLineList(" - "); + + if (disabled && !disabledReason.NullOrEmpty()) + { + tip += "\n\n" + ("DisabledCommand".Translate() + ": " + disabledReason).Colorize(ColorLibrary.RedReadable); + } + + return tip; + }, controlGroup.Index * 137 + 548233); + } + + return new GizmoResult(mouseOver ? GizmoState.Mouseover : GizmoState.Clear); } public override float GetWidth(float maxWidth) @@ -84,4 +206,4 @@ namespace ArachnaeSwarm return 130f; } } -} \ No newline at end of file +}