using System; using System.Text; using Godot; using RobotAndDonkey.Game.Board; using RobotAndDonkey.Game.Cards; using RobotAndDonkey.Game.Data; using RobotAndDonkey.Game.GameState; using RobotAndDonkey.Game.Modifiers; using RobotAndDonkey.Game.Pois; public partial class Tooltip : Control { public override void _Ready() { Instance = this; m_Label = GetNode("RichText"); m_Label.BbcodeEnabled = true; m_Label.AutowrapMode = TextServer.AutowrapMode.Word; m_Label.ScrollActive = true; } public override void _Process(double delta) { UpdateGamepadScroll((float)delta); } private void UpdateGamepadScroll(float delta) { if (m_Label == null || !Visible) return; var axis = Input.GetActionStrength("tooltip_scroll_down") - Input.GetActionStrength("tooltip_scroll_up"); if (Mathf.Abs(axis) < 0.1f) return; var vbar = m_Label.GetVScrollBar(); if (vbar == null) return; vbar.Value += axis * GamepadScrollSpeed * delta; } public void Describe(Cell cell) { Begin(); AppendCell(cell); Commit(); } public void Describe(Card card) { Begin(); AppendCard(card); Commit(); } private void Begin() { m_Builder.Clear(); } private void Commit() { if (m_Label == null) return; m_Label.Text = m_Builder.ToString(); m_Label.ScrollToLine(0); } private static string WrapColor(string text, string colorHex) { return $"[color={colorHex}]{text}[/color]"; } private void AppendTitle(string title, string subtitle = null) { m_Builder.Append(WrapColor($"[b]{title}[/b]", ColorTitle)); if (!string.IsNullOrEmpty(subtitle)) { m_Builder.Append(' '); m_Builder.Append(WrapColor(subtitle, ColorSubtitle)); } m_Builder.Append('\n'); } private void AppendSectionHeader(string title) { m_Builder.Append('\n'); m_Builder.Append(WrapColor($"[b]{title}[/b]", ColorHeader)); m_Builder.Append('\n'); } private void AppendKeyValue(string key, string value) { m_Builder.Append(WrapColor($"{key}: ", ColorLabel)).Append(WrapColor(value, ColorText)).Append('\n'); } private void AppendBlankLine() { m_Builder.Append('\n'); } private void AppendCell(Cell cell) { var subtitle = cell.Hex.ToString(); AppendTitle("Cell", subtitle); var surfaceColor = GetCellColor(cell.Type); AppendKeyValue("Surface", WrapColor(cell.Type.ToString(), surfaceColor)); m_Builder.Append(WrapColor(GetCellDescription(cell.Type), ColorText)); m_Builder.Append('\n'); if (cell.Poi != null) { Main.Instance.Music.Play(cell.Poi); AppendPoi(cell.Poi); } else { Main.Instance.Music.Play(cell.Type); } AppendModifiers("Cell", cell); } public static string GetCellDescription(ECellType cellType) { return cellType switch { ECellType.Grass => "Plain and simple terrain, suitable for most tasks.", ECellType.Dry => $"Resting replenishes {WrapColor($"{Balancing.Instance.DryRestEnergyMalus} less energy", ColorBad)} and interacting costs {WrapColor($"{Balancing.Instance.DryInteractEnergyMalus} more energy", ColorBad)}.", ECellType.Fertile => $"Replenish {WrapColor($"{Balancing.Instance.FertileRestEnergyReplenish} more energy", ColorGood)} while resting here.", ECellType.Mud => $"Movement is very difficult and costs {WrapColor($"{Balancing.Instance.MudMoveEnergyCost} more energy", ColorBad)}.", ECellType.Blocked => "Can't walk here, try jumping or flying.", ECellType.Rocky => "When entering, programs have a high risk of race conditions.", _ => throw new ArgumentOutOfRangeException(nameof(cellType), cellType, null) }; } private string GetCellColor(ECellType type) { return type switch { ECellType.Grass => "#9ccc65", ECellType.Dry => "#ffcc80", ECellType.Fertile => "#a5d6a7", ECellType.Mud => "#8d6e63", ECellType.Blocked => "#b0bec5", ECellType.Rocky => "#b39ddb", _ => ColorText }; } private void AppendPoi(Poi poi) { switch (poi) { case Avatar: AppendRobot(Main.Instance.CoreLoop.Robot); break; case Shed shed: { var summary = $"Requested {shed.Requested}\n" + $"Received {shed.Received}\n" + $"Remaining {shed.Remaining}"; AppendPoiBlock("Shed", summary, poi); break; } case Crate crate: AppendPoiBlock("Crate", $"Contains {crate.Amount} goods", poi); break; case Tower tower: AppendPoiBlock("Tower", tower.Active ? WrapColor("Active", ColorBad) : WrapColor("Disabled", ColorGood), poi); break; case Donkey: AppendPoiBlock("Donkey", string.Empty, poi); break; default: AppendPoiBlock(poi.GetType().Name, string.Empty, poi); break; } } private void AppendPoiBlock(string title, string body, Poi poi) { AppendSectionHeader(title); m_Builder.Append("[indent]\n"); if (!string.IsNullOrWhiteSpace(body)) { m_Builder.Append(WrapColor(body, ColorText)); m_Builder.Append("\n\n"); } if (!string.IsNullOrWhiteSpace(poi.Tooltip)) { m_Builder.Append(WrapColor(poi.Tooltip, ColorText)); m_Builder.Append('\n'); var tooltipModifiers = poi.TooltipModifiers; if (tooltipModifiers.Length > 0) { AppendSectionHeader("Related:"); foreach (var modifier in tooltipModifiers) AppendModifier(modifier); } } m_Builder.Append("[/indent]\n"); } private void AppendRobot(RobotAndDonkey.Game.Robots.Robot robot) { AppendSectionHeader(Main.Instance.CoreLoop.Robot.Type.ToString()); m_Builder.Append("[indent]\n"); AppendKeyValue("Programs", robot.ProgramCount.ToString()); AppendCurrency(robot.Currency); AppendModifiers("Robot", robot); m_Builder.Append("[/indent]\n"); } private void AppendCurrency(Currency currency) { AppendKeyValue("Remaining energy", currency.Energy.ToString()); AppendKeyValue("Good carried", $"{currency.Carry}/{currency.MaxCarry}"); AppendKeyValue("Completed deliveries", currency.Delivery.ToString()); AppendKeyValue("Tape length", currency.TapeLength.ToString()); AppendKeyValue("Hand size", currency.HandSize.ToString()); } private void AppendCard(Card card) { var rarityColor = GetRarityColor(card.Rarity); var rarityText = WrapColor(card.Rarity.ToString(), rarityColor); var subtitle = $"\n{rarityText} {card.CardType}"; AppendTitle(card.Name, subtitle); AppendSectionHeader("Costs"); m_Builder.Append("[indent]\n"); AppendKeyValue("Shop", card.ShopCost.ToString()); AppendKeyValue("Play", card.PlayCost.ToString()); m_Builder.Append("[/indent]\n"); if (!string.IsNullOrWhiteSpace(card.ToolTip)) { AppendSectionHeader("Effect"); m_Builder.Append("[indent]\n"); m_Builder.Append(WrapColor(card.ToolTip, ColorText)); m_Builder.Append('\n'); var tooltipModifiers = card.TooltipModifiers; if (tooltipModifiers.Length > 0) { AppendSectionHeader("Related:"); foreach (var modifier in tooltipModifiers) AppendModifier(modifier); } m_Builder.Append("[/indent]\n"); } AppendModifiers("Card", card); } private string GetRarityColor(ERarity rarity) { return rarity switch { ERarity.Common => "#FFFFFF", ERarity.Magic => "#4CFF4C", ERarity.Uncommon => "#4C99FF", ERarity.Rare => "#CC4CFF", ERarity.Legendary => "#FF8033", _ => ColorText }; } private void AppendModifiers(string entityName, Entity entity) { if (entity.Modifiers.Count == 0) return; AppendSectionHeader($"{entityName} modifiers"); m_Builder.Append("[indent]\n"); foreach (var modifier in entity.Modifiers) AppendModifier(modifier); m_Builder.Append("[/indent]\n"); } private void AppendModifier(Modifier modifier) { var debuffed = modifier.DebuffSources.Count > 0; var header = $"{modifier.Id} [{modifier.Duration}]"; if (debuffed) { m_Builder.Append(WrapColor($"[s]{header}[/s]", ColorMuted)); } else { m_Builder.Append(WrapColor(header, ColorModifier)); } m_Builder.Append('\n'); if (!string.IsNullOrWhiteSpace(modifier.ToolTip)) { m_Builder.Append(WrapColor(modifier.ToolTip, ColorText)); m_Builder.Append('\n'); } if (debuffed) { m_Builder.Append(WrapColor(" (Debuffed)", ColorMuted)); m_Builder.Append('\n'); } } public static Tooltip Instance { get; private set; } private const string ColorTitle = "#ffd65c"; private const string ColorSubtitle = "#aaaaaa"; private const string ColorHeader = "#a5d6ff"; private const string ColorLabel = "#cccccc"; private const string ColorText = "#ffffff"; private const string ColorGood = "#a5d6a7"; private const string ColorBad = "#ef9a9a"; private const string ColorMuted = "#9e9e9e"; private const string ColorModifier = "#ffcc80"; private readonly StringBuilder m_Builder = new(); [Export] public float GamepadScrollSpeed = 800f; private RichTextLabel m_Label; }