using System.Collections.Generic;
using Luneraria.Core;
using UnityEngine;
namespace Luneraria.UI
{
/// Tooltip content (Godot: the dictionary with title, subtitle, stats, text, note, warn).
public sealed class LuneTip
{
public string Title = "";
public string Subtitle = "";
public string Glyph = "";
public Color? GlyphColor;
/// Rows [glyph, name, value].
public readonly List Stats = new List();
public string Text = "";
public string Note = "";
public string Warn = "";
public bool IsEmpty { get { return Title.Length == 0 && Subtitle.Length == 0 && Stats.Count == 0 && Text.Length == 0 && Note.Length == 0 && Warn.Length == 0; } }
public LuneTip Stat(string glyph, string name, string value)
{
Stats.Add(new[] { glyph, name, value });
return this;
}
/// Everything the tooltip shows, as one string (tests and change detection).
public override string ToString()
{
var parts = new List { Title, Subtitle };
foreach (string[] row in Stats)
parts.Add(row[1] + ": " + row[2]);
parts.Add(Text);
parts.Add(Note);
parts.Add(Warn);
return string.Join("\n", parts.FindAll(p => p.Length > 0));
}
}
///
/// Display figures from the real unit definitions and combat rules (port of
/// scripts/luneraria/ui/lune_stats.gd): damage per hit, one attack per second times the attack
/// tempo bonus, range, armour and damage type with the damage matrix (LuneUnit.DamageMatrix).
/// Nothing is invented; values are only formatted.
///
public static class LuneStats
{
static readonly Dictionary RoleNames = new Dictionary
{ { "front", "Tank" }, { "melee", "Nahkampf" }, { "ranged", "Fernkampf" }, { "splash", "Flächenschaden" }, { "support", "Support" } };
static readonly Dictionary RoleGlyphs = new Dictionary
{ { "front", "tank" }, { "melee", "melee" }, { "ranged", "ranged" }, { "splash", "splash" }, { "support", "support" } };
public static string RoleName(string role)
{
return RoleNames.TryGetValue(role ?? "", out string name) ? name : role;
}
public static string RoleGlyph(string role)
{
return RoleGlyphs.TryGetValue(role ?? "", out string glyph) ? glyph : "melee";
}
static int Find(int attackType, double factor)
{
int row = GdMath.Clamp(attackType, 0, 2);
for (int armor = 0; armor < 3; armor++)
{
if (LuneUnit.DamageMatrix[row, armor] == factor)
return armor;
}
return -1;
}
/// Armour class the damage type hits for 130 % (DamageMatrix).
public static string StrongAgainst(int attackType)
{
return ContentData.ArmorNames[Find(attackType, 1.30)];
}
/// Armour class the damage type hits for 75 % (DamageMatrix).
public static string WeakAgainst(int attackType)
{
return ContentData.ArmorNames[Find(attackType, 0.75)];
}
public static double HitDamage(UnitDef definition)
{
return definition.Damage * (1.0 + definition.DamageBonus);
}
public static double AttacksPerSecond(UnitDef definition)
{
return 1.0 + GdMath.Clamp(definition.AttackSpeedBonus, -.25, .5);
}
public static string ArmorName(int armorType)
{
return ContentData.ArmorNames[GdMath.Clamp(armorType, 0, 2)];
}
public static string DamageName(int attackType)
{
return ContentData.DamageNames[GdMath.Clamp(attackType, 0, 2)];
}
/// Stat rows [glyph, name, value] for tooltips and detail panels.
public static List Rows(UnitDef definition, bool withMove = false)
{
int attackType = definition.AttackType;
double reduction = definition.ReductionBonus;
var result = new List
{
new[] { "heart", "Leben", LuneUiTheme.Number(definition.MaxHp) },
new[] { "sword", "Schaden/Treffer", LuneUiTheme.Decimal(HitDamage(definition)) },
new[] { "tempo", "Angriffstempo", LuneUiTheme.Decimal(AttacksPerSecond(definition), 2) + " /s" },
new[] { "range", "Reichweite", LuneUiTheme.Decimal(definition.AttackRange) + " m" },
new[] { "armor", "Rüstung", ArmorName(definition.ArmorType) + (reduction > 0.0 ? " · −" + LuneUiTheme.Round(reduction * 100.0) + " % Schaden" : "") },
new[] { "splash", "Angriffsart", DamageName(attackType) + " · stark gegen " + StrongAgainst(attackType) },
};
if (withMove)
result.Add(new[] { "move", "Lauftempo", LuneUiTheme.Decimal(definition.Speed) + " m/s" });
if (definition.Regen > 0.0)
result.Add(new[] { "heart", "Regeneration", LuneUiTheme.Decimal(definition.Regen * 100.0) + " % Leben/s" });
if (definition.ItemSplash > 0.0)
result.Add(new[] { "splash", "Splitter", LuneUiTheme.Round(definition.ItemSplash * 100.0) + " % an bis zu 2 Gegner" });
return result;
}
/// Compact pairs [glyph, "Name Wert"] for narrow panels (two per row).
public static List ShortRows(UnitDef definition)
{
int attackType = GdMath.Clamp(definition.AttackType, 0, 2);
return new List
{
new[] { "heart", "Leben " + LuneUiTheme.Number(definition.MaxHp) },
new[] { "sword", "Schaden " + LuneUiTheme.Decimal(HitDamage(definition)) },
new[] { "tempo", "Tempo " + LuneUiTheme.Decimal(AttacksPerSecond(definition), 2) + "/s" },
new[] { "range", "Reichweite " + LuneUiTheme.Decimal(definition.AttackRange) + " m" },
new[] { "armor", "Rüstung " + ArmorName(definition.ArmorType) },
new[] { "splash", DamageName(attackType) + " › " + StrongAgainst(attackType) },
};
}
/// "Schaden pro Sekunde" from hit damage and tempo, without abilities or type modifiers.
public static double Dps(UnitDef definition)
{
return HitDamage(definition) * AttacksPerSecond(definition);
}
/// Stage of a troop id: even numbers are the upgraded stage (g02, w04 …).
public static int StageOf(string typeId)
{
int number = int.Parse(typeId.Substring(1));
return number % 2 == 0 ? 2 : 1;
}
/// Tooltip content for a troop type (shop cards, army chips).
public static LuneTip TroopTip(string typeId, string role, string extra = "")
{
UnitDef data = DataCatalog.GetUnit(typeId);
string upgrade = data.UpgradeId ?? "";
string note;
if (upgrade.Length > 0)
{
UnitDef next = DataCatalog.GetUnit(upgrade);
note = string.Format("Aufwertung: {0} für {1} Gold (Leben {2} → {3}, Schaden {4} → {5})", next.DisplayName, next.Cost,
LuneUiTheme.Number(data.MaxHp), LuneUiTheme.Number(next.MaxHp), LuneUiTheme.Decimal(data.Damage), LuneUiTheme.Decimal(next.Damage));
}
else
{
note = "Höchste Stufe.";
}
var tip = new LuneTip { Title = data.DisplayName, Glyph = RoleGlyph(role),
Subtitle = string.Format("{0} · Stufe {1} · {2} Gold", RoleName(role), StageOf(typeId) == 2 ? "II" : "I", data.Cost),
Text = data.AbilityDescription ?? "", Note = note, Warn = extra ?? "" };
tip.Stats.AddRange(Rows(data));
return tip;
}
}
}