Files
2026-04-19 00:43:27 +02:00

349 lines
10 KiB
C#

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<RichTextLabel>("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;
}