151 lines
8.4 KiB
C#
151 lines
8.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
namespace CapyLauncher
|
|
{
|
|
public class StatLine
|
|
{
|
|
public string Label;
|
|
public string Prop;
|
|
public double Base;
|
|
public double Bonus;
|
|
public bool Percent;
|
|
public string Icon;
|
|
|
|
public double Total => Base + Bonus;
|
|
|
|
public string Show(double value) => Percent ? (value * 100).ToString("0.0") + "%" : Label == "SPD" ? value.ToString("0.#") : Math.Floor(value).ToString("#,0");
|
|
}
|
|
|
|
public static class StatSheet
|
|
{
|
|
static readonly Dictionary<string, string> Names = new Dictionary<string, string>
|
|
{
|
|
{ "HPDelta", "HP" }, { "AttackDelta", "ATK" }, { "DefenceDelta", "DEF" }, { "SpeedDelta", "SPD" },
|
|
{ "HPAddedRatio", "HP" }, { "AttackAddedRatio", "ATK" }, { "DefenceAddedRatio", "DEF" }, { "SpeedAddedRatio", "SPD" },
|
|
{ "CriticalChanceBase", "CRIT Rate" }, { "CriticalDamageBase", "CRIT DMG" }, { "StatusProbabilityBase", "Effect Hit Rate" },
|
|
{ "StatusResistanceBase", "Effect RES" }, { "BreakDamageAddedRatioBase", "Break Effect" }, { "HealRatioBase", "Outgoing Healing" },
|
|
{ "SPRatioBase", "Energy Regen" }, { "PhysicalAddedRatio", "Physical DMG" }, { "FireAddedRatio", "Fire DMG" },
|
|
{ "IceAddedRatio", "Ice DMG" }, { "ThunderAddedRatio", "Lightning DMG" }, { "WindAddedRatio", "Wind DMG" },
|
|
{ "QuantumAddedRatio", "Quantum DMG" }, { "ImaginaryAddedRatio", "Imaginary DMG" }, { "ElationDamageAddedRatioBase", "Elation DMG" },
|
|
};
|
|
|
|
public static string Describe(StatBonus bonus)
|
|
{
|
|
string name = Names.TryGetValue(bonus.Prop ?? "", out string found) ? found : bonus.Prop;
|
|
bool flat = bonus.Prop == "HPDelta" || bonus.Prop == "AttackDelta" || bonus.Prop == "DefenceDelta" || bonus.Prop == "SpeedDelta";
|
|
return name + " +" + (flat ? bonus.Value.ToString("0.#") : (bonus.Value * 100).ToString("0.#") + "%");
|
|
}
|
|
|
|
static readonly Dictionary<string, string> ElementProps = new Dictionary<string, string>
|
|
{
|
|
{ "Physical", "PhysicalAddedRatio" }, { "Fire", "FireAddedRatio" }, { "Ice", "IceAddedRatio" },
|
|
{ "Lightning", "ThunderAddedRatio" }, { "Wind", "WindAddedRatio" }, { "Quantum", "QuantumAddedRatio" },
|
|
{ "Imaginary", "ImaginaryAddedRatio" },
|
|
};
|
|
|
|
static double[] Step(double[][] growth, int promotion)
|
|
{
|
|
for (int p = Math.Min(promotion, growth.Length - 1); p >= 0; p--)
|
|
{
|
|
if (growth[p] != null)
|
|
return growth[p];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public static List<StatLine> Build(Catalog catalog, BuildFile file, AvatarInfo info, Dictionary<string, object> avatar)
|
|
{
|
|
var bonus = new Dictionary<string, double>();
|
|
void Add(string prop, double value)
|
|
{
|
|
if (string.IsNullOrEmpty(prop)) return;
|
|
bonus.TryGetValue(prop, out double now);
|
|
bonus[prop] = now + value;
|
|
}
|
|
|
|
int level = BuildFile.Int(avatar, "level", 1);
|
|
int promotion = BuildFile.Int(avatar, "promotion", BuildFile.PromotionFor(level));
|
|
double[] g = Step(info.Growth, promotion) ?? new double[10];
|
|
double hp = g[0] + g[1] * (level - 1), atk = g[2] + g[3] * (level - 1), def = g[4] + g[5] * (level - 1);
|
|
double spd = g[6], critRate = g[7], critDmg = g[8];
|
|
|
|
var cone = file.LightconeOf(info.Id);
|
|
var coneInfo = cone == null ? null : catalog.Lightcone(BuildFile.Int(cone, "item_id"));
|
|
if (coneInfo != null)
|
|
{
|
|
int coneLevel = BuildFile.Int(cone, "level", 1);
|
|
double[] c = Step(coneInfo.Growth, BuildFile.Int(cone, "promotion", BuildFile.PromotionFor(coneLevel)));
|
|
if (c != null)
|
|
{
|
|
hp += c[0] + c[1] * (coneLevel - 1);
|
|
atk += c[2] + c[3] * (coneLevel - 1);
|
|
def += c[4] + c[5] * (coneLevel - 1);
|
|
}
|
|
int rank = Math.Max(1, Math.Min(5, BuildFile.Int(cone, "rank", 1)));
|
|
if (coneInfo.Path == info.Path && coneInfo.Passive[rank - 1] != null)
|
|
{
|
|
foreach (StatBonus b in coneInfo.Passive[rank - 1]) Add(b.Prop, b.Value);
|
|
}
|
|
}
|
|
|
|
foreach (Anchor anchor in info.Anchors)
|
|
{
|
|
if (anchor.Stats.Count > 0 && BuildFile.AnchorLevel(avatar, anchor) > 0)
|
|
{
|
|
foreach (StatBonus b in anchor.Stats) Add(b.Prop, b.Value);
|
|
}
|
|
}
|
|
|
|
var sets = new Dictionary<int, int>();
|
|
for (int slot = 1; slot <= 6; slot++)
|
|
{
|
|
var relic = file.RelicOf(info.Id, slot);
|
|
if (relic == null) continue;
|
|
int relicLevel = BuildFile.Int(relic, "level");
|
|
AffixInfo main = catalog.Main(slot, BuildFile.Int(relic, "main_affix_id"));
|
|
if (main != null) Add(main.Prop, main.Base + main.PerLevel * relicLevel);
|
|
foreach (SubStat sub in BuildFile.Subs(relic))
|
|
{
|
|
AffixInfo affix = catalog.Sub(sub.Id);
|
|
if (affix != null) Add(affix.Prop, affix.Base * sub.Count + affix.Step * sub.Step);
|
|
}
|
|
int setId = BuildFile.Int(relic, "relic_set_id");
|
|
sets.TryGetValue(setId, out int count);
|
|
sets[setId] = count + 1;
|
|
}
|
|
foreach (var pair in sets)
|
|
{
|
|
RelicSetInfo set = catalog.RelicSet(pair.Key);
|
|
if (set == null) continue;
|
|
foreach (var need in set.Bonus)
|
|
{
|
|
if (pair.Value >= need.Key)
|
|
{
|
|
foreach (StatBonus b in need.Value) Add(b.Prop, b.Value);
|
|
}
|
|
}
|
|
}
|
|
|
|
double Get(string prop) => bonus.TryGetValue(prop, out double v) ? v : 0;
|
|
string element = ElementProps.TryGetValue(info.Element ?? "", out string found) ? found : null;
|
|
return new List<StatLine>
|
|
{
|
|
new StatLine { Label = "HP", Prop = "HP", Icon = Icons.Property("HPDelta"), Base = hp, Bonus = hp * Get("HPAddedRatio") + Get("HPDelta") },
|
|
new StatLine { Label = "ATK", Prop = "ATK", Icon = Icons.Property("AttackDelta"), Base = atk, Bonus = atk * Get("AttackAddedRatio") + Get("AttackDelta") },
|
|
new StatLine { Label = "DEF", Prop = "DEF", Icon = Icons.Property("DefenceDelta"), Base = def, Bonus = def * Get("DefenceAddedRatio") + Get("DefenceDelta") },
|
|
new StatLine { Label = "SPD", Prop = "SPD", Icon = Icons.Property("SpeedDelta"), Base = spd, Bonus = spd * Get("SpeedAddedRatio") + Get("SpeedDelta") },
|
|
new StatLine { Label = "CRIT Rate", Prop = "CRIT Rate", Icon = Icons.Property("CriticalChanceBase"), Base = critRate, Bonus = Get("CriticalChanceBase"), Percent = true },
|
|
new StatLine { Label = "CRIT DMG", Prop = "CRIT DMG", Icon = Icons.Property("CriticalDamageBase"), Base = critDmg, Bonus = Get("CriticalDamageBase"), Percent = true },
|
|
new StatLine { Label = "Break Effect", Prop = "Break Effect", Icon = Icons.Property("BreakDamageAddedRatioBase"), Bonus = Get("BreakDamageAddedRatioBase"), Percent = true },
|
|
new StatLine { Label = "Outgoing Healing", Prop = "Outgoing Healing", Icon = Icons.Property("HealRatioBase"), Bonus = Get("HealRatioBase"), Percent = true },
|
|
new StatLine { Label = "Energy Regen Rate", Prop = "Energy Regen Rate", Icon = Icons.Property("SPRatioBase"), Base = 1, Bonus = Get("SPRatioBase"), Percent = true },
|
|
new StatLine { Label = "Effect Hit Rate", Prop = "Effect Hit Rate", Icon = Icons.Property("StatusProbabilityBase"), Bonus = Get("StatusProbabilityBase"), Percent = true },
|
|
new StatLine { Label = "Effect RES", Prop = "Effect RES", Icon = Icons.Property("StatusResistanceBase"), Bonus = Get("StatusResistanceBase"), Percent = true },
|
|
new StatLine { Label = info.Element + " DMG Boost", Prop = info.Element + " DMG", Icon = Icons.ElementDamage(info.Element), Bonus = (element == null ? 0 : Get(element)) + Get("AllDamageTypeAddedRatio"), Percent = true },
|
|
};
|
|
}
|
|
}
|
|
}
|