Decompiled source of BetterTooltips v0.1.6
plugins/BetterTooltips/BetterTooltips.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Logging; using Burst2Flame; using Burst2Flame.Observable; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = "")] [assembly: AssemblyCompany("BetterTooltips")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Melhora textos, tooltips e descrições do Stolen Realm.")] [assembly: AssemblyFileVersion("0.1.6.0")] [assembly: AssemblyInformationalVersion("0.1.6")] [assembly: AssemblyProduct("BetterTooltips")] [assembly: AssemblyTitle("BetterTooltips")] [assembly: AssemblyVersion("0.1.6.0")] namespace BetterTooltips { [BepInPlugin("com.gumatos.bettertooltips", "Better Tooltips", "0.1.6")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Log { get; private set; } private void Awake() { Log = ((BaseUnityPlugin)this).Logger; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Better Tooltips carregado."); AplicarPatches(); } private static void AplicarPatches() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown Harmony val = new Harmony("com.gumatos.bettertooltips"); List<string> list = new List<string>(); int num = 0; int num2 = 0; int num3 = 0; Type[] types; try { types = typeof(Plugin).Assembly.GetTypes(); } catch (Exception ex) { Log.LogError((object)("BetterTooltips: nao deu para listar os tipos do mod — nenhum gancho aplicado: " + ex)); return; } foreach (Type type in types) { if (EhClasseDeGancho(type)) { num++; try { int num4 = val.CreateClassProcessor(type).Patch()?.Count ?? 0; num3 += num4; num2++; Log.LogInfo((object)("BetterTooltips: gancho aplicado — " + type.Name + " (" + num4 + " metodo(s) de patch).")); } catch (Exception ex2) { list.Add(type.Name); Log.LogError((object)("BetterTooltips: FALHA ao aplicar o gancho " + type.Name + " — " + ex2.Message)); } } } string text = "BetterTooltips: patches Harmony aplicados (" + num2 + "/" + num + " ganchos, " + num3 + " metodos de patch)."; if (list.Count == 0) { Log.LogInfo((object)text); } else { Log.LogError((object)(text + " GANCHOS QUE FALHARAM: " + string.Join(", ", list.ToArray()) + ". O mod continua de pe, mas o recurso que dependia deles nao existe nesta sessao.")); } } private static bool EhClasseDeGancho(Type tipo) { try { if (tipo == null || !tipo.IsClass) { return false; } return tipo.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0; } catch (Exception) { return false; } } } } namespace BetterTooltips.Patches { internal static class AtributoPercentualPatch { private const string CorDoValorDoMotor = "CBB396"; private static Dictionary<string, SkillInfo> _porDescricao; private static bool _indicePronto; private static readonly HashSet<string> _marcas = new HashSet<string>(); public static bool TentaMontar(string original, string linhaDoJogo, out string linhaComValor, out string nota) { linhaComValor = null; nota = null; try { if (string.IsNullOrEmpty(original) || original.IndexOf('%') < 0) { return false; } SkillInfo val = SkillDoTexto(original); if ((Object)(object)val == (Object)null) { return false; } Character val2 = ShrineAuraPatch.ReceptorDaTooltip(); if (val2 == null) { Marca("sem numero: nenhum personagem em foco"); return false; } List<string> list = MontaPartes(val, val2, original); if (list.Count == 0) { Marca("sem numero: '" + val.SkillName + "' nao rendeu valor em nenhum atributo"); return false; } string text = string.Join(", ", list.ToArray()); linhaComValor = linhaDoJogo.TrimEnd() + " (<color=#CBB396>" + text + "</color>)"; nota = NotaDoCalculo(); Marca("'" + val2.CharacterName + "' '" + val.SkillName + "' -> " + text); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Atributo % BT-13] valor dinamico falhou (linha intacta): " + ex.GetType().Name + ": " + ex.Message)); linhaComValor = null; nota = null; return false; } } private static List<string> MontaPartes(SkillInfo skill, Character personagem, string descricao) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 List<string> list = new List<string>(); CharacterAttribute[] array = AtributosPrimarios(); if (array == null || skill.AttributeEffects == null) { return list; } CharacterEffectInfo[] attributeEffects = skill.AttributeEffects; float num = default(float); foreach (CharacterEffectInfo val in attributeEffects) { if (val == null || (int)val.CharacterEffectMethod != 1 || val.CalculateOnSecondPass) { continue; } CharacterAttribute characterAttribute = val.CharacterAttribute; if (!EhAtributoPrimario(characterAttribute, array)) { continue; } if (!val.TryGetConstantAmount(ref num)) { Marca("atributo '" + ((Object)characterAttribute).name + "': a % do asset nao e constante ('" + val.Amount + "') — fora da conta"); continue; } float num2 = personagem[((Object)characterAttribute).name]; if (num2 <= 0f) { Marca("atributo '" + ((Object)characterAttribute).name + "': valor atual " + num2.ToString("0.#", CultureInfo.InvariantCulture) + " — sem o que reportar"); continue; } float attributeValueByMethod = personagem.GetAttributeValueByMethod(characterAttribute, (CharacterEffectMethod)1, false, false); float num3 = 100f + attributeValueByMethod; if (num3 < 1f) { Marca("atributo '" + ((Object)characterAttribute).name + "': soma das % (" + attributeValueByMethod.ToString("0.#", CultureInfo.InvariantCulture) + ") anula o denominador"); continue; } float num4 = num2 * num / num3; list.Add(((num4 >= 0f) ? "+" : "") + num4.ToString("0.#", CultureInfo.InvariantCulture) + " " + RotuloDoAtributo(descricao, characterAttribute)); Marca("atributo '" + ((Object)characterAttribute).name + "': final=" + num2.ToString("0.#", CultureInfo.InvariantCulture) + " soma%=" + attributeValueByMethod.ToString("0.#", CultureInfo.InvariantCulture) + " pct=" + num.ToString("0.#", CultureInfo.InvariantCulture) + " -> " + num4.ToString("0.#", CultureInfo.InvariantCulture)); } return list; } private static CharacterAttribute[] AtributosPrimarios() { try { return ((Object)(object)Game.Instance != (Object)null) ? Game.Instance.LevelableCharacterAttributes : null; } catch { return null; } } private static bool EhAtributoPrimario(CharacterAttribute atributo, CharacterAttribute[] primarios) { if ((Object)(object)atributo == (Object)null || primarios == null) { return false; } foreach (CharacterAttribute val in primarios) { if (!((Object)(object)val == (Object)null) && (val == atributo || val.Guid == atributo.Guid)) { return true; } } return false; } private static string RotuloDoAtributo(string descricao, CharacterAttribute atributo) { int startIndex = 0; while (true) { int num = descricao.IndexOf('@', startIndex); if (num < 0) { break; } int num2 = descricao.IndexOf('@', num + 1); if (num2 < 0) { break; } string text = descricao.Substring(num + 1, num2 - num - 1); if (text.Length > 0 && ((Object)atributo).name.StartsWith(text, StringComparison.OrdinalIgnoreCase)) { return Tooltip.ToTitleCase(text); } startIndex = num2 + 1; } return ((Object)atributo).name; } internal static string NotaDoCalculo() { return "Counted on the attribute value you have before this bonus, so the amount grows with your gear, powerups and skills."; } private static SkillInfo SkillDoTexto(string original) { GaranteIndice(); Dictionary<string, SkillInfo> porDescricao = _porDescricao; if (porDescricao == null) { return null; } if (!porDescricao.TryGetValue(Normaliza(original), out var value)) { return null; } return value; } private static void GaranteIndice() { if (_indicePronto) { return; } try { if ((Object)(object)Game.Instance == (Object)null) { return; } CharacterAttribute[] levelableCharacterAttributes = Game.Instance.LevelableCharacterAttributes; List<SkillInfo> skills = Game.Instance.Skills; if (levelableCharacterAttributes == null || skills == null) { return; } Dictionary<string, SkillInfo> dictionary = new Dictionary<string, SkillInfo>(StringComparer.Ordinal); int num = 0; foreach (SkillInfo item in skills) { if ((Object)(object)item == (Object)null || string.IsNullOrEmpty(item.Description) || !TemPercentualEmPrimario(item, levelableCharacterAttributes)) { continue; } string text = Normaliza(item.Description); if (text.Length == 0) { continue; } if (dictionary.TryGetValue(text, out var value)) { if ((Object)(object)value != (Object)null) { dictionary[text] = null; num++; } } else { dictionary[text] = item; } } _porDescricao = dictionary; _indicePronto = true; Plugin.Log.LogInfo((object)("BetterTooltips: BT-13 — familia de skills de atributo em %: " + ListaDaFamilia(dictionary) + " (" + Conta(dictionary) + " skill(s); " + num + " descricao(oes) ambigua(s))")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("BetterTooltips: BT-13 nao montou o indice da familia — " + ex.GetType().Name + ": " + ex.Message)); } } private static bool TemPercentualEmPrimario(SkillInfo skill, CharacterAttribute[] primarios) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 if (skill.AttributeEffects == null) { return false; } CharacterEffectInfo[] attributeEffects = skill.AttributeEffects; foreach (CharacterEffectInfo val in attributeEffects) { if (val != null && (int)val.CharacterEffectMethod == 1 && EhAtributoPrimario(val.CharacterAttribute, primarios)) { return true; } } return false; } private static string ListaDaFamilia(Dictionary<string, SkillInfo> indice) { List<string> list = new List<string>(); foreach (KeyValuePair<string, SkillInfo> indie in indice) { if ((Object)(object)indie.Value != (Object)null) { list.Add(indie.Value.SkillName); } } list.Sort(StringComparer.Ordinal); if (list.Count != 0) { return string.Join("; ", list.ToArray()); } return "(nenhuma)"; } private static int Conta(Dictionary<string, SkillInfo> indice) { int num = 0; foreach (KeyValuePair<string, SkillInfo> indie in indice) { if ((Object)(object)indie.Value != (Object)null) { num++; } } return num; } private static string Normaliza(string texto) { if (string.IsNullOrEmpty(texto)) { return texto; } string text = texto.Trim(); if (text.IndexOf(" ", StringComparison.Ordinal) < 0) { return text; } while (text.Contains(" ")) { text = text.Replace(" ", " "); } return text; } private static void Marca(string linha) { try { if (_marcas.Count < 200 && _marcas.Add(linha)) { Plugin.Log.LogInfo((object)("[Atributo % BT-13] " + linha)); } } catch { } } } internal static class BerserkersBloodPatch { internal const string NomeDaSkill = "Beserker's Blood"; private const string TermoDaFormula = "HealthRatioInverse"; private const string CorDoValorDoMotor = "CBB396"; private static SkillInfo _skill; private static bool _fatorLido; private static float _fator; private static readonly HashSet<string> _marcas = new HashSet<string>(); public static bool TentaMontar(string original, string linhaDoJogo, out string linhaComValor, out string nota) { linhaComValor = null; nota = null; try { SkillInfo val = SkillDoBerserkersBlood(); if ((Object)(object)val == (Object)null || !MesmoTexto(val.Description, original)) { return false; } float num = FatorDaFormula(val); if (num <= 0f) { Marca("sem numero: a formula de 'Beserker's Blood' nao trouxe um fator legivel"); return false; } Character val2 = ShrineAuraPatch.ReceptorDaTooltip(); if (val2 == null) { Marca("sem numero: nenhum personagem em foco"); return false; } float maxHealth = val2.MaxHealth; if (maxHealth <= 0f) { Marca("sem numero: personagem em foco sem MaxHealth"); return false; } float healthRatioInverse = val2.HealthRatioInverse; string text = (healthRatioInverse * num).ToString("0.#", CultureInfo.InvariantCulture); linhaComValor = InsereValorNaLinha(linhaDoJogo, text); nota = NotaDoCalculo(); float num2 = maxHealth * healthRatioInverse; Marca("'" + val2.CharacterName + "' MaxHealth=" + maxHealth.ToString("0.#") + " perdida=" + num2.ToString("0.#") + " (x " + num.ToString("0.#") + ") -> '" + text + "% de dano'"); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Beserker's Blood BT-12] valor dinamico falhou (linha intacta): " + ex.GetType().Name + ": " + ex.Message)); linhaComValor = null; nota = null; return false; } } private static string InsereValorNaLinha(string linhaDoJogo, string valor) { string text = (linhaDoJogo ?? string.Empty).TrimEnd(); string text2 = " (<color=#CBB396>" + valor + "</color>% increased damage right now)"; if (text.EndsWith(".", StringComparison.Ordinal)) { return text.Substring(0, text.Length - 1) + text2 + "."; } return text + text2 + "."; } private static SkillInfo SkillDoBerserkersBlood() { if ((Object)(object)_skill != (Object)null) { return _skill; } if ((Object)(object)Game.Instance == (Object)null) { return null; } List<SkillInfo> skills = Game.Instance.Skills; if (skills == null) { return null; } foreach (SkillInfo item in skills) { if ((Object)(object)item != (Object)null && string.Equals(item.SkillName, "Beserker's Blood", StringComparison.Ordinal)) { _skill = item; return _skill; } } return null; } private static float FatorDaFormula(SkillInfo skill) { if (_fatorLido) { return _fator; } if (skill.AttributeEffects == null) { return 0f; } CharacterEffectInfo[] attributeEffects = skill.AttributeEffects; foreach (CharacterEffectInfo val in attributeEffects) { if (val != null && !string.IsNullOrEmpty(val.Amount) && LeFatorDaFormula(val.Amount, out _fator) && _fator > 0f) { _fatorLido = true; return _fator; } } return 0f; } private static bool LeFatorDaFormula(string formula, out float fator) { fator = 0f; if (string.IsNullOrEmpty(formula)) { return false; } if (formula.IndexOf("HealthRatioInverse", StringComparison.Ordinal) < 0) { return false; } string text = TiraParentesesDeFora(formula.Trim()); int num = -1; int num2 = 0; int num3 = 0; for (int i = 0; i < text.Length; i++) { switch (text[i]) { case '(': num3++; break; case ')': num3--; if (num3 < 0) { return false; } break; case '*': if (num3 == 0) { num2++; num = i; } break; } } if (num3 != 0 || num2 != 1) { return false; } string text2 = text.Substring(0, num).Trim(); string text3 = text.Substring(num + 1).Trim(); bool flag = text2.IndexOf("HealthRatioInverse", StringComparison.Ordinal) >= 0; bool flag2 = text3.IndexOf("HealthRatioInverse", StringComparison.Ordinal) >= 0; if (flag == flag2) { return false; } string lado = (flag ? text2 : text3); string lado2 = (flag ? text3 : text2); if (EhReferenciaDaRazao(lado)) { return TentaNumero(lado2, out fator); } return false; } private static string TiraParentesesDeFora(string texto) { texto = texto.Trim(); while (texto.Length >= 2 && texto[0] == '(' && texto[texto.Length - 1] == ')') { int num = 0; bool flag = true; for (int i = 0; i < texto.Length; i++) { if (texto[i] == '(') { num++; } else if (texto[i] == ')') { num--; if (num == 0 && i != texto.Length - 1) { flag = false; break; } } } if (!flag || num != 0) { break; } texto = texto.Substring(1, texto.Length - 2).Trim(); } return texto; } private static string SemEspacos(string texto) { return string.Concat(texto.Split(new char[4] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)); } private static bool EhReferenciaDaRazao(string lado) { string text = SemEspacos(TiraParentesesDeFora(lado.Trim())); if (text.StartsWith("Mathf.Round(", StringComparison.Ordinal) && text.EndsWith(")", StringComparison.Ordinal)) { int num = text.Length - 1; int num2 = UltimaVirgulaDeTopo(text, "Mathf.Round(".Length, num); if (num2 < 0) { return false; } string texto = text.Substring("Mathf.Round(".Length, num2 - "Mathf.Round(".Length); string texto2 = text.Substring(num2 + 1, num - num2 - 1); if (EhReferenciaSimples(texto)) { return EhInteiroDeDigitos(texto2); } return false; } return EhReferenciaSimples(text); } private static bool EhReferenciaSimples(string texto) { if (!texto.EndsWith("HealthRatioInverse", StringComparison.Ordinal)) { return false; } int num = texto.Length - "HealthRatioInverse".Length; if (num == 0) { return true; } if (texto[num - 1] != '.') { return false; } for (int i = 0; i < num - 1; i++) { char c = texto[i]; if (!char.IsLetterOrDigit(c) && c != '_' && c != '.') { return false; } } return true; } private static int UltimaVirgulaDeTopo(string texto, int inicio, int fim) { int num = 0; int result = -1; for (int i = inicio; i < fim; i++) { switch (texto[i]) { case '(': num++; break; case ')': num--; break; case ',': if (num == 0) { result = i; } break; } } return result; } private static bool TentaNumero(string lado, out float numero) { numero = 0f; string text = SemEspacos(TiraParentesesDeFora(lado.Trim())); if (text.Length > 0 && (text[text.Length - 1] == 'f' || text[text.Length - 1] == 'F')) { text = text.Substring(0, text.Length - 1); } bool flag = false; string text2 = text; foreach (char c in text2) { if (char.IsDigit(c)) { flag = true; } else if (c != '.' && c != ',') { return false; } } if (!flag) { return false; } return float.TryParse(text.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out numero); } private static bool EhInteiroDeDigitos(string texto) { bool result = false; for (int i = 0; i < texto.Length; i++) { if (!char.IsDigit(texto[i])) { return false; } result = true; } return result; } private static bool MesmoTexto(string a, string b) { if (!string.IsNullOrEmpty(a) && !string.IsNullOrEmpty(b)) { return string.Equals(a.Trim(), b.Trim(), StringComparison.Ordinal); } return false; } internal static string NotaDoCalculo() { return "Counts the health you are missing right now as a share of your maximum health, so the bonus rises as you take damage and falls as you heal."; } private static void Marca(string linha) { try { if (_marcas.Count < 200 && _marcas.Add(linha)) { Plugin.Log.LogInfo((object)("[Beserker's Blood BT-12] " + linha)); } } catch { } } } internal static class ConvercaoAtributoPatch { private const string CorDoValorDoMotor = "CBB396"; private static Dictionary<string, SkillInfo> _porDescricao; private static bool _indicePronto; private static readonly HashSet<string> _marcas = new HashSet<string>(); public static bool TentaMontar(string original, string linhaDoJogo, out string linhaComValor, out string nota) { linhaComValor = null; nota = null; try { if (string.IsNullOrEmpty(original) || original.IndexOf("armor", StringComparison.OrdinalIgnoreCase) < 0) { return false; } SkillInfo val = SkillDoTexto(original); if ((Object)(object)val == (Object)null) { return false; } if (JaRenderizadoPeloMotor(val)) { Marca("'" + val.SkillName + "' ja rende o valor por DescriptionExpressions — linha do jogo intacta (sem repeticao)"); return false; } Character val2 = ShrineAuraPatch.ReceptorDaTooltip(); if (val2 == null) { Marca("sem numero: nenhum personagem em foco"); return false; } List<string> list = MontaPartes(val, val2); if (list.Count == 0) { Marca("sem numero: '" + val.SkillName + "' nao rendeu valor em Armor/Magic Armor"); return false; } string text = string.Join(", ", list.ToArray()); linhaComValor = linhaDoJogo.TrimEnd() + " (<color=#CBB396>" + text + "</color>)"; nota = NotaDoCalculo(); Marca("'" + val2.CharacterName + "' '" + val.SkillName + "' -> " + text); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Convercao BT-20] valor dinamico falhou (linha intacta): " + ex.GetType().Name + ": " + ex.Message)); linhaComValor = null; nota = null; return false; } } private static List<string> MontaPartes(SkillInfo skill, Character personagem) { List<string> list = new List<string>(); if (skill.AttributeEffects == null) { return list; } CharacterEffectInfo[] attributeEffects = skill.AttributeEffects; foreach (CharacterEffectInfo val in attributeEffects) { if (EhConvercao(val)) { if (!ShrineAuraPatch.ValorDaExpressao(val.Amount, personagem, personagem, out var valor)) { Marca("atributo '" + ((Object)val.CharacterAttribute).name + "': a Amount '" + val.Amount + "' nao avaliou neste build — fora da conta"); } else { list.Add(((valor >= 0f) ? "+" : "") + valor.ToString("0.#", CultureInfo.InvariantCulture) + " " + RotuloDoAtributo(val.CharacterAttribute)); Marca("atributo '" + ((Object)val.CharacterAttribute).name + "': expressao '" + val.Amount + "' -> " + valor.ToString("0.#", CultureInfo.InvariantCulture)); } } } return list; } private static bool EhConvercao(CharacterEffectInfo efeito) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) if (efeito == null || (int)efeito.CharacterEffectMethod != 0 || efeito.CalculateOnSecondPass) { return false; } if (!EhArmaduraOuMagicArmor(efeito.CharacterAttribute)) { return false; } if (!string.IsNullOrEmpty(efeito.Amount)) { return efeito.Amount.IndexOf("Source[", StringComparison.Ordinal) >= 0; } return false; } private static bool EhArmaduraOuMagicArmor(CharacterAttribute atributo) { if ((Object)(object)atributo == (Object)null) { return false; } if (!string.Equals(((Object)atributo).name, "Armor", StringComparison.Ordinal)) { return string.Equals(((Object)atributo).name, "MagicArmor", StringComparison.Ordinal); } return true; } private static string RotuloDoAtributo(CharacterAttribute atributo) { if ((Object)(object)atributo == (Object)null) { return string.Empty; } string tooltipDisplayName = atributo.GetTooltipDisplayName(); if (!string.IsNullOrEmpty(tooltipDisplayName)) { return tooltipDisplayName; } return ((Object)atributo).name; } internal static string NotaDoCalculo() { return "Counted on your current attributes, so the amount follows your gear, powerups and skills."; } private static bool JaRenderizadoPeloMotor(SkillInfo skill) { string[] descriptionExpressions = skill.DescriptionExpressions; if (descriptionExpressions == null || descriptionExpressions.Length == 0) { return false; } return TemTokenNumerico(skill.Description); } private static bool TemTokenNumerico(string descricao) { if (string.IsNullOrEmpty(descricao)) { return false; } for (int i = 0; i + 1 < descricao.Length; i++) { if (descricao[i] == '[' && descricao[i + 1] >= '0' && descricao[i + 1] <= '9') { return true; } } return false; } private static SkillInfo SkillDoTexto(string original) { GaranteIndice(); Dictionary<string, SkillInfo> porDescricao = _porDescricao; if (porDescricao == null) { return null; } if (!porDescricao.TryGetValue(Normaliza(original), out var value)) { return null; } return value; } private static void GaranteIndice() { if (_indicePronto) { return; } try { if ((Object)(object)Game.Instance == (Object)null) { return; } List<SkillInfo> skills = Game.Instance.Skills; if (skills == null) { return; } Dictionary<string, SkillInfo> dictionary = new Dictionary<string, SkillInfo>(StringComparer.Ordinal); int num = 0; foreach (SkillInfo item in skills) { if ((Object)(object)item == (Object)null || string.IsNullOrEmpty(item.Description) || !TemConvercao(item)) { continue; } string text = Normaliza(item.Description); if (text.Length == 0) { continue; } if (dictionary.TryGetValue(text, out var value)) { if ((Object)(object)value != (Object)null) { dictionary[text] = null; num++; } } else { dictionary[text] = item; } } _porDescricao = dictionary; _indicePronto = true; Plugin.Log.LogInfo((object)("BetterTooltips: BT-20 — familia de skills de conversao de atributo em Armor/Magic Armor: " + ListaDaFamilia(dictionary) + " (" + Conta(dictionary) + " skill(s); " + num + " descricao(oes) ambigua(s))")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("BetterTooltips: BT-20 nao montou o indice da familia — " + ex.GetType().Name + ": " + ex.Message)); } } private static bool TemConvercao(SkillInfo skill) { if (skill.AttributeEffects == null) { return false; } CharacterEffectInfo[] attributeEffects = skill.AttributeEffects; for (int i = 0; i < attributeEffects.Length; i++) { if (EhConvercao(attributeEffects[i])) { return true; } } return false; } private static string ListaDaFamilia(Dictionary<string, SkillInfo> indice) { List<string> list = new List<string>(); foreach (KeyValuePair<string, SkillInfo> indie in indice) { if ((Object)(object)indie.Value != (Object)null) { list.Add(indie.Value.SkillName); } } list.Sort(StringComparer.Ordinal); if (list.Count != 0) { return string.Join("; ", list.ToArray()); } return "(nenhuma)"; } private static int Conta(Dictionary<string, SkillInfo> indice) { int num = 0; foreach (KeyValuePair<string, SkillInfo> indie in indice) { if ((Object)(object)indie.Value != (Object)null) { num++; } } return num; } private static string Normaliza(string texto) { if (string.IsNullOrEmpty(texto)) { return texto; } string text = texto.Trim(); if (text.IndexOf(" ", StringComparison.Ordinal) < 0) { return text; } while (text.Contains(" ")) { text = text.Replace(" ", " "); } return text; } private static void Marca(string linha) { try { if (_marcas.Count < 200 && _marcas.Add(linha)) { Plugin.Log.LogInfo((object)("[Convercao BT-20] " + linha)); } } catch { } } } public static class GlobulePatch { private static readonly HashSet<string> GlobuleKeys = new HashSet<string> { "Heals for 50% of Max Health", "Restores 50% of Max Mana", "Cleanses all debuffs", "Grants an additional Action Point", "Lowers Cooldowns by 1", "Increases all damage by 10%", "Increases damage and summon damage by 10%" }; private static readonly HashSet<string> PickupKeys = new HashSet<string> { "Restores 10% health", "Restores 15% health", "Restores 20% health", "Restores 10% mana", "Restores 15% mana", "Restores 20% mana" }; private const string PrefixoSustenance = "Consuming any Globule heals you for "; private static Character _cacheChar; private static float _cachePct; private static List<string> _cacheTiers; private static float _cacheQuando = -999f; private const float TtlCache = 1f; private static readonly HashSet<string> _marcas = new HashSet<string>(); public static bool EhGlobule(string texto) { if (texto != null) { if (!GlobuleKeys.Contains(texto)) { return PickupKeys.Contains(texto); } return true; } return false; } private static bool AtributoExiste(Character personagem, string nome) { Game instance = Game.Instance; if ((Object)(object)instance == (Object)null) { return false; } if ((Object)(object)instance.GetAttribute(nome) != (Object)null) { return true; } if ((Object)(object)instance.GetVariableAttribute(nome) != (Object)null && personagem.Values != null) { return personagem.Values.ContainsKey(nome); } return false; } public static string FraseSustenance(string chaveDaTooltip) { try { if (!EhGlobule(chaveDaTooltip)) { return ""; } Character val = ShrineAuraPatch.ReceptorDaTooltip(); if (val == null) { Marca("sem numero: nenhum personagem em foco"); return ""; } if (!AtributoExiste(val, "MaxHealth") || !AtributoExiste(val, "MaxMana")) { Marca("sem numero: MaxHealth/MaxMana nao existem neste contexto (target nulo ou tooltip fora de jogador)"); return ""; } PctDasTiersAtivas(val, out var pct, out var tiers); if (pct <= 0f || tiers.Count == 0) { Marca("'" + chaveDaTooltip + "': " + val.CharacterName + " nao tem Sustenance ativa -> sem numero"); return ""; } float maxHealth = val.MaxHealth; float maxMana = val.MaxMana; if (maxHealth <= 0f && maxMana <= 0f) { Marca("sem numero: personagem sem MaxHealth/MaxMana"); return ""; } string text = ((tiers.Count == 1) ? tiers[0] : string.Join(" and ", tiers.ToArray())); string text2 = (PickupKeys.Contains(chaveDaTooltip) ? "Picking this up also heals you for " : "Consuming a Globule also heals you for "); string text3 = text2 + pct.ToString("0.#") + "% of your Max Health and Max Mana (" + text + "): " + (maxHealth * pct / 100f).ToString("0.#") + " health and " + (maxMana * pct / 100f).ToString("0.#") + " mana with your current pools."; Marca("'" + chaveDaTooltip + "': " + val.CharacterName + " " + text + " = " + pct.ToString("0.#") + "% -> " + text3); return text3; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Globule RV-28] frase do Sustenance falhou (tooltip intacto): " + ex.GetType().Name + ": " + ex.Message)); return ""; } } private static void PctDasTiersAtivas(Character personagem, out float pct, out List<string> tiers) { if (_cacheChar == personagem && _cacheTiers != null && Time.realtimeSinceStartup - _cacheQuando < 1f) { pct = _cachePct; tiers = _cacheTiers; return; } pct = 0f; tiers = new List<string>(); List<SkillInfo> skills = personagem.Skills; if (skills != null) { foreach (SkillInfo item in skills) { if (!((Object)(object)item == (Object)null) && !string.IsNullOrEmpty(item.Description) && TentaLerPct(item.Description, out var pct2) && !(pct2 <= 0f)) { pct += pct2; tiers.Add(string.IsNullOrEmpty(item.SkillName) ? "(sem nome)" : item.SkillName); } } } _cacheChar = personagem; _cachePct = pct; _cacheTiers = tiers; _cacheQuando = Time.realtimeSinceStartup; } private static bool TentaLerPct(string descricao, out float pct) { pct = 0f; int num = descricao.IndexOf("Consuming any Globule heals you for ", StringComparison.Ordinal); if (num < 0) { return false; } num += "Consuming any Globule heals you for ".Length; int num2 = descricao.IndexOf('%', num); if (num2 < 0) { return false; } string text = descricao.Substring(num, num2 - num).Trim(); if (text.Length > 0) { return float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out pct); } return false; } private static void Marca(string linha) { try { if (_marcas.Count < 200 && _marcas.Add(linha)) { Plugin.Log.LogInfo((object)("[Globule RV-28] " + linha)); } } catch { } } } internal static class HungerPatch { internal const string NomeDaSkill = "Hunger"; private const string CorDoValorDoMotor = "CBB396"; private static SkillInfo _skill; private static bool _pctLido; private static float _pct; private static readonly HashSet<string> _marcas = new HashSet<string>(); public static bool TentaMontar(string original, string linhaDoJogo, out string linhaComValor, out string nota) { linhaComValor = null; nota = null; try { SkillInfo val = SkillDoHunger(); if ((Object)(object)val == (Object)null || !MesmoTexto(val.Description, original)) { return false; } float num = PercentualDaSkill(val); if (num <= 0f) { Marca("sem numero: o asset de 'Hunger' nao trouxe a % do sacrificio"); return false; } Character val2 = ShrineAuraPatch.ReceptorDaTooltip(); if (val2 == null) { Marca("sem numero: nenhum personagem em foco"); return false; } float maxHealth = val2.MaxHealth; if (maxHealth <= 0f) { Marca("sem numero: personagem em foco sem MaxHealth"); return false; } string text = (maxHealth * num / 100f).ToString("0.#", CultureInfo.InvariantCulture); linhaComValor = InsereValorNaLinha(linhaDoJogo, text); nota = NotaDoCalculo(); Marca("'" + val2.CharacterName + "' MaxHealth=" + maxHealth.ToString("0.#") + " x " + num.ToString("0.#") + "% -> '" + text + "' por turno"); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Hunger BT-11] valor dinamico falhou (linha intacta): " + ex.GetType().Name + ": " + ex.Message)); linhaComValor = null; nota = null; return false; } } private static string InsereValorNaLinha(string linhaDoJogo, string valor) { string text = (linhaDoJogo ?? string.Empty).TrimEnd(); string text2 = " (<color=#CBB396>" + valor + "</color> health per turn for you)"; if (text.EndsWith(".", StringComparison.Ordinal)) { return text.Substring(0, text.Length - 1) + text2 + "."; } return text + text2 + "."; } private static SkillInfo SkillDoHunger() { if ((Object)(object)_skill != (Object)null) { return _skill; } if ((Object)(object)Game.Instance == (Object)null) { return null; } List<SkillInfo> skills = Game.Instance.Skills; if (skills == null) { return null; } foreach (SkillInfo item in skills) { if ((Object)(object)item != (Object)null && string.Equals(item.SkillName, "Hunger", StringComparison.Ordinal)) { _skill = item; return _skill; } } return null; } private static float PercentualDaSkill(SkillInfo skill) { if (_pctLido) { return _pct; } if (LePercentual(skill.Description, out _pct) && _pct > 0f) { _pctLido = true; } return _pct; } private static bool MesmoTexto(string a, string b) { if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) { return false; } return string.Equals(NormalizaEspacos(a), NormalizaEspacos(b), StringComparison.Ordinal); } private static string NormalizaEspacos(string texto) { string text = texto.Trim(); while (text.Contains(" ")) { text = text.Replace(" ", " "); } return text; } private static bool LePercentual(string descricao, out float pct) { pct = 0f; if (string.IsNullOrEmpty(descricao)) { return false; } int num = descricao.IndexOf("health", StringComparison.OrdinalIgnoreCase); if (num < 0) { return false; } int num2 = descricao.LastIndexOf('%', num); if (num2 < 0) { return false; } int num3; for (num3 = num2; num3 > 0; num3--) { char c = descricao[num3 - 1]; if (!char.IsDigit(c) && c != '.' && c != ',') { break; } } if (num3 >= num2) { return false; } return float.TryParse(descricao.Substring(num3, num2 - num3).Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out pct); } internal static string NotaDoCalculo() { return "Based on your own Max Health, so the amount sacrificed follows your gear, powerups and skills."; } private static void Marca(string linha) { try { if (_marcas.Count < 200 && _marcas.Add(linha)) { Plugin.Log.LogInfo((object)("[Hunger BT-11] " + linha)); } } catch { } } } [HarmonyPatch(typeof(OptionsManager), "Localize")] public static class LocalizePatch { [HarmonyPatch] internal static class CorEOrdemDoTooltip { private static bool _diagnosticadoCor; private static readonly Type[] AssinaturaDoShowTooltip = new Type[13] { typeof(string), typeof(string), typeof(Sprite), typeof(string), typeof(TooltipOffsetInfo), typeof(Color), typeof(Vector3?), typeof(bool), typeof(bool), typeof(float), typeof(ControlGlyphInfo?), typeof(string), typeof(bool) }; private static MethodBase TargetMethod() { try { MethodInfo methodInfo = AccessTools.Method(typeof(Tooltip), "ShowTooltip", AssinaturaDoShowTooltip, (Type[])null); if (methodInfo != null) { return methodInfo; } Plugin.Log.LogError((object)"BetterTooltips: Tooltip.ShowTooltip com a assinatura de 13 parametros NAO foi encontrado — o gancho de cor/ordem do tooltip NAO sera aplicado nesta sessao (nunca um metodo escolhido por engano)."); return null; } catch (Exception ex) { Plugin.Log.LogError((object)("BetterTooltips: falha ao resolver Tooltip.ShowTooltip pela lista de tipos — gancho de cor/ordem NAO aplicado: " + ex.Message)); return null; } } private static void Prefix(ref string description) { string text = description; try { if (string.IsNullOrEmpty(description)) { return; } string text2 = (string.IsNullOrEmpty(_corEspecialDoJogo) ? "C8B090" : _corEspecialDoJogo); string text3 = CorDaLinhaDeAuras(); bool ultimo = string.Equals(text2, text3, StringComparison.OrdinalIgnoreCase); string bloco; bool flag = TirarBloco(ref description, text3, ultimo, "Your skills on this status:", out bloco); string bloco2; bool flag2 = TirarBloco(ref description, text3, ultimo, "Your active shrine auras:", out bloco2); List<string> list = new List<string>(); string nota; bool flag3 = TirarNotaDoMod(ref description, out nota); while (flag3) { list.Add(nota); flag3 = TirarNotaDoMod(ref description, out nota); } if (!flag2 && !flag && list.Count == 0) { if (!_diagnosticadoCor) { _diagnosticadoCor = true; Plugin.Log.LogInfo((object)("BetterTooltips: ShowTooltip interceptado SEM o bloco (procurando <color=#" + text2 + "> e <color=#" + text3 + ">)")); } return; } if (!_diagnosticadoCor) { _diagnosticadoCor = true; Plugin.Log.LogInfo((object)("BetterTooltips: ShowTooltip interceptado, blocos movidos para o fim (nota #" + text2 + ", auras ativas #" + text3 + ")")); } if (flag) { bloco2 = (string.IsNullOrEmpty(bloco2) ? bloco : (bloco + "\n\n" + bloco2)); } string text4 = description.TrimEnd(); foreach (string item in list) { text4 = text4 + "\n\n" + item; } if (!string.IsNullOrEmpty(bloco2)) { text4 = text4 + "\n\n" + bloco2; } description = text4; } catch (Exception e) { description = text; RegistrarFalhaIgnorada("prefix de cor/ordem do Tooltip.ShowTooltip", e); } } private static bool TirarBloco(ref string corpo, string hex, bool ultimo, string exigir, out string bloco) { bloco = null; if (string.IsNullOrEmpty(corpo) || string.IsNullOrEmpty(hex)) { return false; } string value = "<color=#" + hex + ">"; int num = (ultimo ? corpo.LastIndexOf(value, StringComparison.Ordinal) : corpo.IndexOf(value, StringComparison.Ordinal)); while (num >= 0) { int num2 = corpo.IndexOf("</color>", num, StringComparison.Ordinal); if (num2 < 0) { return false; } int num3 = InicioDoSeparador(corpo, num); int num4 = num2 + "</color>".Length; string text = corpo.Substring(num3, num4 - num3).TrimStart('\n').TrimEnd(); if (string.IsNullOrEmpty(exigir) || text.IndexOf(exigir, StringComparison.Ordinal) >= 0) { bloco = text; corpo = corpo.Remove(num3, num4 - num3); return true; } if (ultimo) { if (num == 0) { return false; } num = corpo.LastIndexOf(value, num - 1, StringComparison.Ordinal); } else { num = corpo.IndexOf(value, num + 1, StringComparison.Ordinal); } } return false; } private static bool TirarNotaDoMod(ref string corpo, out string nota) { nota = null; if (string.IsNullOrEmpty(corpo)) { return false; } int num = 0; while (num < corpo.Length) { int num2 = corpo.IndexOf("<color=#", num, StringComparison.Ordinal); if (num2 < 0) { return false; } int num3 = corpo.IndexOf('>', num2); if (num3 < 0) { return false; } int num4 = corpo.IndexOf("</color>", num3, StringComparison.Ordinal); if (num4 < 0) { return false; } if (_conteudosDeNota.Contains(corpo.Substring(num3 + 1, num4 - num3 - 1))) { int num5 = InicioDoSeparador(corpo, num2); int num6 = num4 + "</color>".Length; nota = corpo.Substring(num5, num6 - num5).TrimStart('\n').TrimEnd(); corpo = corpo.Remove(num5, num6 - num5); return true; } num = num4 + 1; } return false; } private static int InicioDoSeparador(string corpo, int posicao) { int num = posicao; if (num > 0 && corpo[num - 1] == '\n') { num--; if (num > 0 && corpo[num - 1] == '\n') { num--; } } return num; } } [HarmonyPatch(typeof(Root), "SendMessageWindowMessage")] internal static class SemNotaNoFeedDeTexto { private static void Prefix() { try { EntrarTextoDeFeed(); } catch (Exception ex) { Plugin.Log.LogError((object)("BetterTooltips: falha ao entrar no contexto do feed de texto (BUG-34): " + ex.Message)); } } private static void Finalizer() { try { SairTextoDeFeed(); } catch (Exception ex) { Plugin.Log.LogError((object)("BetterTooltips: falha ao sair do contexto do feed de texto (BUG-34): " + ex.Message)); } } } [HarmonyPatch(typeof(OverheadMessageDisplay), "SpawnOverheadMessage")] internal static class SemNotaNoTextoFlutuanteDeCombate { private static void Prefix() { try { EntrarTextoDeFeed(); } catch (Exception ex) { Plugin.Log.LogError((object)("BetterTooltips: falha ao entrar no contexto do texto flutuante (BUG-34): " + ex.Message)); } } private static void Finalizer() { try { SairTextoDeFeed(); } catch (Exception ex) { Plugin.Log.LogError((object)("BetterTooltips: falha ao sair do contexto do texto flutuante (BUG-34): " + ex.Message)); } } } internal static string _corEspecialDoJogo; internal const string MarcadorCorDeExplicacao = "C8B090"; private static readonly HashSet<string> _conteudosDeNota = new HashSet<string>(StringComparer.Ordinal); internal static string _corAuraDoJogo; private static readonly Dictionary<string, string> TextAppends = new Dictionary<string, string> { { "+ 20% Damage Reduction", "\n<color=#C8B090>Reduces all damage you take. Applied before Resistances and Armor.</color>" }, { "+ 16% Damage Reduction", "\n<color=#C8B090>Reduces all damage you take. Applied before Resistances and Armor.</color>" }, { "+ 12% Damage Reduction", "\n<color=#C8B090>Reduces all damage you take. Applied before Resistances and Armor.</color>" }, { "+ 8% Damage Reduction", "\n<color=#C8B090>Reduces all damage you take. Applied before Resistances and Armor.</color>" }, { "+ 4% Damage Reduction", "\n<color=#C8B090>Reduces all damage you take. Applied before Resistances and Armor.</color>" }, { "Summons a cloud of chaos that strikes 3 times. At every strike each damage type has a @50%@ chance to deal *0 damage.", "\n<color=#C8B090>The 50% roll only decides whether each element lands; the damage can still be dodged, and those are two separate rolls.</color>" }, { "Hurls a bolt of chaos dealing damage to a single target. Every damage type has a 50% chance to deal *0 damage.", "\n<color=#C8B090>The 50% roll only decides whether each element lands; the damage can still be dodged, and those are two separate rolls.</color>" }, { "Curses the target to take damage. Each element has a @50%@ chance to deal *0 damage every turn.", "\n<color=#C8B090>The 50% roll only decides whether each element lands; the damage can still be dodged, and those are two separate rolls.</color>" }, { "Summons a whirling blade of chaos in a line. Every damage type has a 50% chance to deal *0 damage.", "\n<color=#C8B090>The 50% roll only decides whether each element lands; the damage can still be dodged, and those are two separate rolls.</color>" }, { "Creates a clone of yourself that can cast your abilities. The Replicate Clone explodes for damage on death, each element has a @50%@ chance to deal *0 damage.", "\n<color=#C8B090>The 50% roll only decides whether each element lands; the damage can still be dodged, and those are two separate rolls.</color>" }, { "Increases damage dealt by 15%.", "\n<color=#C8B090>Stacks up to 10 times, one stack per ally killed.</color>" }, { "@Life steal@ increased.", "\n<color=#C8B090>Increases life steal by 50%. The aura reaches 3 hexes from its source and applies to allies inside it.</color>" }, { "@Life steal@ increased by 5%.", "\n<color=#C8B090>The aura reaches 3 hexes from its source and applies to allies inside it.</color>" }, { "@Life steal@ increased by 20%.", "\n<color=#C8B090>The aura reaches 3 hexes from its source and applies to allies inside it.</color>" }, { "@Life Steal@ increased by 5%.", "\n<color=#C8B090>The aura reaches 3 hexes from its source and applies to allies inside it.</color>" }, { "@Life steal@ increased by 50%.", "\n<color=#C8B090>The aura reaches 3 hexes from its source and applies to allies inside it.</color>" }, { "Grants 50% increased Armor, Magic Armor, and Max Health.", "\n<color=#C8B090>Lasts 2 turns.</color>" }, { "Increases Max Health and Damage by [0]%. Stacks.", "\n<color=#C8B090>Stacks up to 5 times.</color>" }, { "The next damage you deal causes a Thunder Bolt to strike your target. Stacks.", "\n<color=#C8B090>Stacks up to 8 times.</color>" }, { "Will leave when finished playing with you.", "\n<color=#C8B090>Invincible: it cannot be damaged.</color>" }, { "Decreases damage taken by 10%.", "\n<color=#C8B090>It reaches allies within 3 hexes of the seal's bearer.</color>" }, { "Cannot control your character. Increases damage by [0]%. Grants enrage.", "\n<color=#C8B090>Also increases the healing this character does by the same percentage.</color>" }, { "Damage increased by 10%, Range increased by 1 per stack.", "\n<color=#C8B090>Gains a stack at the end of your turn, and moving removes the status.\nAlso increases the healing this character does by the same percentage.</color>" }, { "Increases Damage Dealt and Max Health by 25%.", "\n<color=#C8B090>Current health is increased by the same percentage and goes back down when the status ends.\nAlso increases the healing this character does by the same percentage.</color>" }, { "Increases @Mana Costs@ by {4,20}%, power of all @Mana Abilites@ by {8,40}% and @Intelligence@ by {3,15}. <i><color=#808080>The energies of a Mana Spring swell within you!</i></color> ", "\n<color=#C8B090>Power of Mana increases the damage and healing of abilities that cost Mana.\nIt does not change how much Mana they cost, it does nothing for abilities that cost no Mana,\nand it does not change the duration or the bonuses of the statuses those abilities apply.</color>" }, { "Adds *0 lightning damage to your next basic attack. Can stack up to 10 times.", "\n<color=#C8B090>Lasts 2 turns.</color>" }, { "The Dead One's tether to this world wanes. Lasts [0] more round(s).", "\n<color=#C8B090>While the tether lasts the Dead One is Invincible: it takes no damage and harmful statuses cannot be applied to it.</color>" }, { "<i><color=#808080>You carry the Necronomicon!</i></color> Changes your @Skeletal Summons@ into something... stronger. Increases Summon Damage and Health by {5,25}%", "\n<color=#C8B090>Your skeletal summons are raised as Undead instead: Undead Berserker, Undead Ranger and Undead Wizard.</color>" }, { "Mana costs reduced.", "\n<color=#C8B090>Allies within 4 hexes of the caster: Muse I reduces Mana Costs by 10%, Muse II by a further 15%.</color>" }, { "<i><color=#808080>\"Look at me, I'm the alpha now!</i></color> Changes your @Nature Summons@ into @Pack Summons@. Increases Summon Damage and Health by {5,25}%", "\n<color=#C8B090>Pack summons are fixed instead of random: Timber Wolf, Tundra Wolf and Dire Wolf.</color>" }, { "Gain the ability to shapeshift into a White Dragonkin. <i><color=#808080>\"Imbued with a gentle radiance, it serves as a testament to the enduring strength of the forces of light\"</i></color> ", "\n<color=#C8B090>While transformed you gain: Dragonkin Holy Slash, Dragonkin Holy Breath and Dragonkin Holy Blast.</color>" }, { "Invincible.", "\n<color=#C8B090>Cannot take damage.</color>" }, { "Damage increased by 60%.", "\n<color=#C8B090>Also increases the healing this character does by the same percentage.</color>" }, { "Can only be harmed by destroying Soul Fetishes. Immune to movement imparing effects.", "\n<color=#C8B090>Also grants immunity to effects that Disable the character (they cannot perform actions).</color>" }, { "Targets in holy ground receive healing.", "\n<color=#C8B090>Heals each turn while the target stays in the holy ground.</color>" }, { "Healed.", "\n<color=#C8B090>Restores health. The amount depends on the effect that applied it.</color>" }, { "Immune to damage.", "\n<color=#C8B090>Also cannot be targeted by harmful skills, and harmful statuses do not apply.</color>" }, { "Damage and Health increased by 25% Size increased by 25%", "\n<color=#C8B090>Also increases the healing you do by the same percentage.</color>" }, { "Damage increased by [0]%. Damage taken increased by [0]%. ", "\n<color=#C8B090>Base 25% damage and +25% damage taken; the value shown already includes the Shrine Effect Bonus. Also increases the healing you do by the same percentage.</color>" }, { "Damage increased by 5% per stack. Can stack up to 10 times.", "\n<color=#C8B090>Also increases the healing you do by the same percentage.</color>" }, { "Immune to all movement impairing effects and knockback.", "\n<color=#C8B090>Also immune to being Chilled, Frozen, Stunned or Disabled.</color>" }, { "Dodging incoming attacks.", "\n<color=#C8B090>Guarantees a dodge (100% chance) against incoming attacks; each dodge spends one charge.</color>" }, { "Dodge chance increased by 8%.", "\n<color=#C8B090>Each stack adds another 8%; you gain one stack per strike.</color>" }, { "Damage increased by 50% ", "\n<color=#C8B090>Lasts 3 turns.\nAlso increases the healing you do by the same percentage.</color>" }, { "Increases your @Action Points@ by @1@. If you are using a One-Handed Sword, Axe, Hammer, Gun, or Wand. <i><color=#808080>You carry the Speed of the Ymir!</color></i>", "\n<color=#C8B090>Also works with Fist weapons, and it does not apply while wielding a two-handed weapon.</color>" }, { "Has a [0]% chance to resist any control effects.", "\n<color=#C8B090>Also halves the effect of Chilled.</color>" }, { "Damage and healing from non basic skills increased by 10%.", "\n<color=#C8B090>Each stack is consumed by your next damaging or healing skill.</color>" }, { "Increases @Damage@ by {6,30}%. Increases @Damage Taken@ by 10%. <i><color=#808080>\"Judge not a life until its final breath, for the Reaper's touch brings truth to death.\"</i></color> ", "\n<color=#C8B090>Also increases the healing you do by the same percentage.</color>" }, { "Your first attack in battle deals 100% additional damage.", "\n<color=#C8B090>It also boosts your first healing, and the bonus is consumed by your first damaging or healing cast.</color>" }, { "Damage increased by 50%. Damage taken increased by 25%.", "\n<color=#C8B090>Also increases the healing you do by the same percentage.</color>" }, { "Damage increased by 30%. Damage taken increased by 15%.", "\n<color=#C8B090>Also increases the healing you do by the same percentage.</color>" }, { "Damage increased by 10%. Damage taken increased by 5%.", "\n<color=#C8B090>Also increases the healing you do by the same percentage.</color>" }, { "Damage increased by 40%. Damage taken increased by 20%.", "\n<color=#C8B090>Also increases the healing you do by the same percentage.</color>" }, { "Damage increased by 20%. Damage taken increased by 10%.", "\n<color=#C8B090>Also increases the healing you do by the same percentage.</color>" }, { "Increases damage dealt and life steal by 25%.", "\n<color=#C8B090>Lasts 2 turns.\nAlso increases the healing you do by the same percentage.</color>" }, { "Increases damage dealt by 30%.", "\n<color=#C8B090>Lasts 3 turns.\nAlso increases the healing you do by the same percentage.</color>" }, { "Immune to poisoned.", "\n<color=#C8B090>Lasts 2 turns.</color>" }, { "Reduces @Range@ by @3@ per stack.", "\n<color=#C8B090>Only ranged attacks and skills lose range; melee range is unchanged.</color>" }, { "Enemies gain 2 stacks of poison.", "\n<color=#C8B090>Reapplied each turn to enemies in range of the flower.</color>" }, { "Critical Failure", "\n<color=#C8B090>All attributes reduced by 5%.</color>" }, { "Forced to attack the taunter. Damage reduced by 20%.", "\n<color=#C8B090>You can only target the taunter, and you are forced to attack them.</color>" }, { "Taunted.", "\n<color=#C8B090>You can only target the taunter, and you are forced to attack them.</color>" }, { "Stunned.", "\n<color=#C8B090>Cannot move or perform actions.</color>" }, { "@Cooldowns@ increased by @1@ turn.", "\n<color=#C8B090>Only abilities whose base cooldown is 2 or more are affected: 1-turn cooldowns stay at 1, and basic attacks are unaffected.</color>" }, { "Lowers movement and range by 1 and lowers Damage by 10% per stack.", "\n<color=#C8B090>Only ranged attacks and skills lose range; melee range is unchanged.</color>" }, { "Lowers movement and range by 1 and lowers Damage by 5% per stack.", "\n<color=#C8B090>Only ranged attacks and skills lose range; melee range is unchanged.</color>" }, { "Asleep.", "\n<color=#C8B090>Cannot move or perform actions; damage taken is increased by 50%.</color>" }, { "Shapeshifted into a box. Damage taken increased by 100%.", "\n<color=#C8B090>While in the box you cannot move or use abilities.</color>" }, { "Range is reduced by 6 Hexes.", "\n<color=#C8B090>Only ranged abilities are affected.</color>" }, { "Range is reduced by 4 Hexes.", "\n<color=#C8B090>Only ranged abilities are affected.</color>" }, { "Range is reduced by 2 Hexes.", "\n<color=#C8B090>Only ranged abilities are affected.</color>" }, { "Max Health reduced by 25%.", "\n<color=#C8B090>Current health is reduced by the same percentage and comes back when the status ends. This cannot kill.</color>" }, { "Reduces @Damage@ by @20%@ per stack.", "\n<color=#C8B090>Also reduces the healing this character does by the same percentage.</color>" }, { "Damage reduced by 50%.", "\n<color=#C8B090>Also reduces the healing this character does by the same percentage.</color>" }, { "Damage reduced by 20%.", "\n<color=#C8B090>Also reduces the healing this character does by the same percentage.</color>" }, { "Damage reduced by 5%.", "\n<color=#C8B090>Also reduces the healing this character does by the same percentage.</color>" }, { "Damage reduced by 25%.", "\n<color=#C8B090>Also reduces the healing this character does by the same percentage.</color>" }, { "Reduces Physical resistance by 25%.", "\n<color=#C8B090>Resistances reduce their damage type by the listed %. If a resistance goes negative, that damage type is amplified instead.</color>" }, { "Attackers lifesteal for [0]%.", "\n<color=#C8B090>Lasts 2 turns.</color>" }, { "Blind.", "\n<color=#C8B090>Skill range is reduced to 1 hex.</color>" }, { "*0 lightning damage dealt to enemies within the aura each time they perform an action.", "\n<color=#C8B090>The aura reaches 3 hexes from its source.</color>" }, { "Deals *0 cold damage per turn while in the aura.", "\n<color=#C8B090>The aura reaches 3 hexes from its source, and it damages enemies that start their turns inside it.</color>" }, { "[0] fire damage dealt to enemies within the aura.", "\n<color=#C8B090>The aura reaches 3 hexes from its source.</color>" }, { "*0 fire damage dealt to enemies within the aura.", "\n<color=#C8B090>The aura reaches 3 hexes from its source.</color>" }, { "Shapeshift into a @Werewolf@. Empowers your basic attack and gain new abilities. Increases @Max Health@ by 10%.", "\n<color=#C8B090>While transformed you gain: a stronger basic attack, Blood Howl and Cursed Bite.</color>" }, { "Shapeshift into a @Dire Werewolf@. Basic attack and abilities are further empowered. Increases @Max Health@ by 20%.", "\n<color=#C8B090>While transformed you gain: a further empowered basic attack, Greater Blood Howl and Greater Cursed Bite.</color>" }, { "Shapeshift into a @Vampire Bat@. Gain new abilities. @Movement@ increased. Immune to @attacks of opportunity@.", "\n<color=#C8B090>While transformed you gain: Energy Drain.</color>" }, { "The first time you deal damage or healing in battle, it's effectiveness is increased by 100%.", "\n<color=#C8B090>That bonus is consumed by your first damaging cast.</color>" }, { "Deals *0 physical damage. Pulls enemies within 4 hexes towards you. Targets are crippled for 1 turn. ", "\n<color=#C8B090>The pull drags them the whole way: they land on a hex adjacent to you.</color>" }, { "Breath fire dealing *0 fire damage to all enemies in a cone. ", "\n<color=#C8B090>The cone reaches 4 hexes from you.</color>" }, { "Deals *0 cold damage each turn.", "\n<color=#C8B090>Hits everything in a cone reaching 4 hexes from you.</color>" }, { "Deals *0 weapon damage and applies {STA=Stunned} to all enemies within a line for 1 turn.", "\n<color=#C8B090>The line reaches 5 hexes from you.</color>" }, { "Deals *0 weapon damage to all enemies within a line.", "\n<color=#C8B090>The line reaches 5 hexes from you.</color>" }, { "The caster hurls a razor-like shard of ice piercing foes in a line dealing *0 cold damage. ", "\n<color=#C8B090>The line reaches 10 hexes from you.</color>" }, { "Conjures the breath of a frost dragon dealing *0 cold damage to all enemies in a cone. ", "\n<color=#C8B090>The cone reaches 4 hexes from you.</color>" }, { "Breath lightning dealing *0 lightning damage to all enemies in a cone. ", "\n<color=#C8B090>The cone reaches 4 hexes from you.</color>" }, { "Charges to the target and deals *0 weapon damage to all enemies in your path.", "\n<color=#C8B090>The path is a line of up to 7 hexes.</color>" }, { "Conjures a lightning bolt that chains up to 5 enemies near the target dealing *0 lightning damage to each target. ", "\n<color=#C8B090>The 5 counts the chain's HOPS, not different enemies: the bolt can hit the same enemy again, so with only two enemies in range it bounces between them.</color>" }, { "Increase the mana cost of all skills by 20% and increase the power of mana using skills by 40%.", "\n<color=#C8B090>Power of Mana increases the damage and healing of abilities that cost Mana.\nIt does not change how much Mana they cost, it does nothing for abilities that cost no Mana,\nand it does not change the duration or the bonuses of the statuses those abilities apply.</color>" }, { "Increase the mana cost of all skills by an additional 30% and increase the power of mana using skills by an additional 60%.", "\n<color=#C8B090>Power of Mana increases the damage and healing of abilities that cost Mana.\nIt does not change how much Mana they cost, it does nothing for abilities that cost no Mana,\nand it does not change the duration or the bonuses of the statuses those abilities apply.</color>" }, { "Calls down a magical rain that increases potency of Mana using abilities by 50% for allies within the area. Lasts 2 turns.", "\n<color=#C8B090>Power of Mana increases the damage and healing of abilities that cost Mana.\nIt does not change how much Mana they cost, it does nothing for abilities that cost no Mana,\nand it does not change the duration or the bonuses of the statuses those abilities apply.</color>" }, { "Grants [0] Return Physical Damage.", "\n<color=#C8B090>Scales with your character level.</color>" }, { "Grants an additional [0] Return Physical Damage.", "\n<color=#C8B090>Scales with your character level.</color>" }, { "Grants [0] Return Shadow Damage.", "\n<color=#C8B090>Scales with your character level.</color>" }, { "Physical damage increased by [0].", "\n<color=#C8B090>Scales with your character level.</color>" }, { "@Intelligence@ grants [0] @Armor@ and @Magic Armor@ per point. Current Bonus: [1]", "\n<color=#C8B090>Scales with your character level.</color>" }, { "Damage caused steals [0] points of @Dexterity@. Lasts the entire battle. Stacks up to 10 times.", "\n<color=#C8B090>Scales with your character level.</color>" }, { "Damage caused steals [0] points of @Intelligence@. Lasts the entire battle. Stacks up to 10 times.", "\n<color=#C8B090>Scales with your character level.</color>" }, { "Damage caused steals [0] points of @Might@. Lasts the entire battle. Stacks up to 10 times.", "\n<color=#C8B090>Scales with your character level.</color>" }, { "Envelopes the target in living vines that increase @armor@ and @magic armor@ by [0] and causes the target to regenerate *0 @health@ each turn.", "\n<color=#C8B090>Scales with your character level.</color>" }, { "Increases all attributes by [0]. In addition, 10% of your highest attribute value is added to all other attributes. Current Bonus: [1]", "\n<color=#C8B090>Scales with your character level.</color>" }, { "Encases the friendly target in a dome of ice granting [0] Armor and Magic Armor for the duration. Immobilizes the target.", "\n<color=#C8B090>Scales with your character level.</color>" }, { "Summons a Raven, Coyote, or Raccoon to fight for you.", "\n<color=#C8B090>One is summoned at random:\n- Raven: Melee Attack, Evasion\n- Raccoon: Melee Attack, Steal Action\n- Coyote: Melee Attack, Cripple\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Summons a Stag, Wolf, or Boar to fight for you.", "\n<color=#C8B090>One is summoned at random:\n- Stag: Melee Attack, Stunning Kick\n- Wolf: Melee Attack, Howl\n- Boar: Melee Attack, Fracture\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Summons a Bear, Moose, or Panther to fight for you.", "\n<color=#C8B090>One is summoned at random:\n- Bear (a Grizzly): Stunning Slam, Wild Cleave\n- Moose: Melee Attack, Ground Slam\n- Panther: Melee Attack, Shadow Walk\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Summons a Tundra Wolf to fight for you.", "\n<color=#C8B090>Tundra Wolf: Melee Attack, Howl\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Summons a Dire Wolf to fight for you.", "\n<color=#C8B090>Dire Wolf: Melee Attack, Blood Howl\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Summon a grizzly to fight your enemies.", "\n<color=#C8B090>Grizzly: Stunning Slam, Wild Cleave\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Summon a wolf to fight your enemies.", "\n<color=#C8B090>Wolf: Melee Attack, Howl\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Summon a Raven to fight your enemies. ", "\n<color=#C8B090>Raven: Melee Attack, Evasion\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Raise an Undead Ranger to fight by your side.", "\n<color=#C8B090>Undead Ranger: Ranged Attack, Hide In Shadows\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Raise an Undead Berserker to fight by your side.", "\n<color=#C8B090>Undead Berserker: Bleeding Cleave\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Summon brambles to block 4 hexes. Attackers will take physical damage when striking the brambles. Lasts 4 turns.", "\n<color=#C8B090>Benefits from your Might and Intelligence, but does not count as a summon.</color>" }, { "Summon pillars of ice to block 4 hexes blocking enemy's line of sight. Attackers take cold damage. Lasts 4 turns.", "\n<color=#C8B090>Benefits from your Might and Intelligence, but does not count as a summon.</color>" }, { "Increases the target allies' stats by 20%.", "\n<color=#C8B090>Lasts 5 turns.</color>" }, { "Restores target's health by *0.", "\n<color=#C8B090>Healing scales with your Attack Power and Holy Power.\nReduced by effects that lower the target's healing received.</color>" }, { "Restores *0 health to all allies within 2 hexes of target.", "\n<color=#C8B090>Healing scales with your Attack Power and Holy Power.\nReduced by effects that lower the target's healing received.\nThe 2-hex area is centred on the chosen TARGET, not on the caster.</color>" }, { "Target restores *0 health per turn. ", "\n<color=#C8B090>Healing scales with your Attack Power and Holy Power.\nReduced by effects that lower the target's healing received.</color>" }, { "All allies within 3 hexes of you heal for *0 per turn. ", "\n<color=#C8B090>Healing scales with your Attack Power and Holy Power.\nReduced by effects that lower the target's healing received.</color>" }, { "Restores the target to full health.", "\n<color=#C8B090>Healing scales with your Holy Power.\nReduced by effects that lower the target's healing received.</color>" }, { "Summons a radiant light applying {STA=Blind} to the target dealing *0 Holy damage.", "\n<color=#C8B090>Damage scales with your Attack Power and Holy Power.</color>" }, { "Breathe out a stream of holy light dealing *0 holy damage to all enemies and healing *0 to all allies in range.", "\n<color=#C8B090>Damage and healing scale with your Attack Power and Holy Power.\nHealing is reduced by effects that lower the target's healing received.</color>" }, { "Expel a powerful celestial light blinding enemies and dealing *0 holy damage to all enemies and healing *1 to all allies in range.", "\n<color=#C8B090>Damage and healing scale with your Attack Power and Holy Power.\nHealing is reduced by effects that lower the target's healing received.</color>" }, { "Consecrate an area of ground to heal allies that stand upon it. Heals *0 per turn for 3 turns. ", "\n<color=#C8B090>Healing scales with your Attack Power and Holy Power.</color>" }, { "Any target you heal with Cure, Regenerate, Healing Hand, Mass Cure, or Divine Intervention also receives an additional healing over time effect restoring *0 health for 3 turns.", "\n<color=#C8B090>Healing scales with your Attack Power and Holy Power.</color>" }, { "Calls down a shield that protects the target absorbing *0 damage. ", "\n<color=#C8B090>Absorption scales with your Attack Power and Holy Power.</color>" }, { "Conjures a shield imbued with holy flame absorbing *0 damage. Upon shield depletion, it explodes and deals *0 holy damage to all foes in a 3 hex radius.", "\n<color=#C8B090>Damage and absorption scale with your Attack Power and Holy Power.</color>" }, { "+ 20% increased Treasure Find", "\n<color=#C8B090>Increases the chance for enemies to drop items. Does not affect item rarity. Stacks with your party.</color>" }, { "+ 40% increased Treasure Find", "\n<color=#C8B090>Increases the chance for enemies to drop items. Does not affect item rarity. Stacks with your party.</color>" }, { "+ 60% increased Treasure Find", "\n<color=#C8B090>Increases the chance for enemies to drop items. Does not affect item rarity. Stacks with your party.</color>" }, { "+ 80% increased Treasure Find", "\n<color=#C8B090>Increases the chance for enemies to drop items. Does not affect item rarity. Stacks with your party.</color>" }, { "+ 100% increased Treasure Find", "\n<color=#C8B090>Increases the chance for enemies to drop items. Does not affect item rarity. Stacks with your party.</color>" }, { "+ 10% increased Gold Find", "\n<color=#C8B090>Increases the gold you earn. Stacks with your party.</color>" }, { "+ 20% increased Gold Find", "\n<color=#C8B090>Increases the gold you earn. Stacks with your party.</color>" }, { "+ 30% increased Gold Find", "\n<color=#C8B090>Increases the gold you earn. Stacks with your party.</color>" }, { "+ 40% increased Gold Find", "\n<color=#C8B090>Increases the gold you earn. Stacks with your party.</color>" }, { "+ 50% increased Gold Find", "\n<color=#C8B090>Increases the gold you earn. Stacks with your party.</color>" }, { "+ 5% to Damage and Healing", "\n<color=#C8B090>Increases all damage you deal and all healing you do.</color>" }, { "+ 10% to Damage and Healing", "\n<color=#C8B090>Increases all damage you deal and all healing you do.</color>" }, { "+ 15% to Damage and Healing", "\n<color=#C8B090>Increases all damage you deal and all healing you do.</color>" }, { "+ 20% to Damage and Healing", "\n<color=#C8B090>Increases all damage you deal and all healing you do.</color>" }, { "+ 25% to Damage and Healing", "\n<color=#C8B090>Increases all damage you deal and all healing you do.</color>" }, { "Gain 1 additional movement", "\n<color=#C8B090>Each movement point lets you move one extra hex per turn.</color>" }, { "Gain 2 additional movement", "\n<color=#C8B090>Each movement point lets you move one extra hex per turn.</color>" }, { "Gain 1 additional range", "\n<color=#C8B090>Increases the range of your ranged attacks and skills. Does not apply to certain skills.</color>" }, { "Gain 2 additional range", "\n<color=#C8B090>Increases the range of your ranged attacks and skills. Does not apply to certain skills.</color>" }, { "Skill Options +1", "\n<color=#C8B090>Increases the number of skill choices offered when leveling up.</color>" }, { "Skill Options +2", "\n<color=#C8B090>Increases the number of skill choices offered when leveling up.</color>" }, { "Attribute Points Per Level +1", "\n<color=#C8B090>Grants additional attribute points each time you level up.</color>" }, { "Attribute Points Per Level +2", "\n<color=#C8B090>Grants additional attribute points each time you level up.</color>" }, { "Attribute Points Per Level +3", "\n<color=#C8B090>Grants additional attribute points each time you level up.</color>" }, { "[[ResistCold]] +10%", "\n<color=#C8B090>Reduces Cold damage you take.</color>" }, { "[[ResistCold]] +15%", "\n<color=#C8B090>Reduces Cold damage you take.</color>" }, { "[[ResistCold]] +20%", "\n<color=#C8B090>Reduces Cold damage you take.</color>" }, { "[[ResistFire]] +10%", "\n<color=#C8B090>Reduces Fire damage you take.</color>" }, { "[[ResistFire]] +15%", "\n<color=#C8B090>Reduces Fire damage you take.</color>" }, { "[[ResistFire]] +20%", "\n<color=#C8B090>Reduces Fire damage you take.</color>" }, { "[[ResistLightning]] +10%", "\n<color=#C8B090>Reduces Lightning damage you take.</color>" }, { "[[ResistLightning]] +15%", "\n<color=#C8B090>Reduces Lightning damage you take.</color>" }, { "[[ResistLightning]] +20%", "\n<color=#C8B090>Reduces Lightning damage you take.</color>" }, { "[[ResistPhysical]] +10%", "\n<color=#C8B090>Reduces Physical damage you take.</color>" }, { "[[ResistPhysical]] +15%", "\n<color=#C8B090>Reduces Physical damage you take.</color>" }, { "[[ResistPhysical]] +20%", "\n<color=#C8B090>Reduces Physical damage you take.</color>" }, { "+ 5% to Summon Damage and Health", "\n<color=#C8B090>Increases the damage and health of your summoned creatures.</color>" }, { "+ 10% to Summon Damage and Health", "\n<color=#C8B090>Increases the damage and health of your summoned creatures.</color>" }, { "+ 15% to Summon Damage and Health", "\n<color=#C8B090>Increases the damage and health of your summoned creatures.</color>" }, { "+ 20% to Summon Damage and Health", "\n<color=#C8B090>Increases the damage and health of your summoned creatures.</color>" }, { "+ 25% to Summon Damage and Health", "\n<color=#C8B090>Increases the damage and health of your summoned creatures.</color>" }, { "Skill Tree Removals +1", "\n<color=#C8B090>Lets you remove skill trees from your pool of skill choices (at least 4 trees remain). Can only be chosen at select party screen.</color>" }, { "Skill Tree Removals +2", "\n<color=#C8B090>Lets you remove skill trees from your pool of skill choices (at least 4 trees remain). Can only be chosen at select party screen.</color>" }, { "Skill Tree Removals +3", "\n<color=#C8B090>Lets you remove skill trees from your pool of skill choices (at least 4 trees remain). Can only be chosen at select party screen.</color>" }, { "Skill Tree Removals +4", "\n<color=#C8B090>Lets you remove skill trees from your pool of skill choices (at least 4 trees remain). Can only be chosen at select party screen.</color>" }, { "Skill Tree Removals +5", "\n<color=#C8B090>Lets you remove skill trees from your pool of skill choices (at least 4 trees remain). Can only be chosen at select party screen.</color>" }, { "Skill Tree Removals +6", "\n<color=#C8B090>Lets you remove skill trees from your pool of skill choices (at least 4 trees remain). Can only be chosen at select party screen.</color>" }, { "Skill Tree Removals +7", "\n<color=#C8B090>Lets you remove skill trees from your pool of skill choices (at least 4 trees remain). Can only be chosen at select party screen.</color>" }, { "Skill Tree Removals +8", "\n<color=#C8B090>Lets you remove skill trees from your pool of skill choices (at least 4 trees remain). Can only be chosen at select party screen.</color>" }, { "Attackers take Fire Damage.", "\n<color=#C8B090>The attacker takes this damage in return, based on its own Max Health and not on the health of the one it attacked, before damage reduction.</color>" }, { "Take [0]% of your Max Health in Shadow Damage per turn.", "\n<color=#C8B090>Raw damage, before damage reduction: your own Max Health multiplied by the percentage shown; the damage can be 0.</color>" }, { "Damage increased by [0]%. ", "\n<color=#C8B090>Base 20%; the value shown already includes the Shrine Effect Bonus.</color>" }, { "Reduces Damage taken by [0]%. ", "\n<color=#C8B090>Base 20%; the value shown already includes the Shrine Effect Bonus.</color>" }, { "Critical hit chance increased by [0]%.", "\n<color=#C8B090>Base 20%; the value shown already includes the Shrine Effect Bonus.</color>" }, { "Increases dodge chance by [0]%.", "\n<color=#C8B090>Base 20%; the value shown already includes the Shrine Effect Bonus.</color>" }, { "Lifesteal increased by [0]%.", "\n<color=#C8B090>Base 8%; the value shown already includes the Shrine Effect Bonus.</color>" }, { "Decreases the cost of mana using abilities by [0]%.", "\n<color=#C8B090>Base 50%; the value shown already includes the Shrine Effect Bonus.</color>" }, { "Your attacks have a [0]% chance to stun the target.", "\n<color=#C8B090>Base 20%; the value shown already includes the Shrine Effect Bonus.</color>" }, { "While a two handed weapon is equipped all damage is increased by 20%. ", "\n<color=#C8B090>Counts 2H Axe, 2H Sword, 2H Mace, Polearm, Bow, 2H Gun, and Staff.</color>" }, { "While dual-wielding, @dodge chance@ increased by 5%, @critical hit chance@ increased by 5%, and @critical hit damage@ is increased by 20%.", "\n<color=#C8B090>Counts two one-handed weapons: 1H Sword, 1H Axe, 1H Mace, 1H Gun, Wand, Fist weapons, or Unarmed.</color>" } }; private static readonly Dictionary<string, string> TextFixes = new Dictionary<string, string> { { "10% chance to cast Level [level value] {SKL=Meteor} when Striking.", "10% chance to cast Level [level value] {SKL=Meteor} when striking." }, { "Striking with this weapon applies 2 stack of Poison.", "Striking with this weapon applies 2 stacks of Poison." }, { "Vitality increased by 10 for 5 turns.", "Vitality increased by 5 for 5 turns." }, { "Applies Hunger Ksvaldir: Sacrifices 10% of your max health per turn.", "Applies Hunger of Ksvaldir: Sacrifices 10% of your max health per turn." }, { "Weapon attacks apply 1 stacks of bleeding.", "Weapon attacks apply 1 stack of bleeding." }, { "10% chance on hit to cast Level [level value] {SKL=Consumption}", "10% chance on hit to cast Level [level value] {SKL=Consumption}." }, { "5% chance to cast Level [level value] {SKL=Thunder Bolt} when hitting an enemy with any ability. 5% chance to cast Level [level value] {SKL=Frost Nova} when hitting an enemy with any ability. 5% chance to cast Level [level value] {SKL=Fireball} when hitting an enemy with any ability.", "5% chance to cast Level [level value] {SKL=Thunder Bolt} when hitting an enemy with any ability. 8% chance to cast Level [level value] {SKL=Frost Nova} when hitting an enemy with any ability. 5% chance to cast Level [level value] {SKL=Fireball} when hitting an enemy with any ability." }, { "Grants the passive Grants the passive skill {SKL=Lich Lord}.", "Grants the passive skill {SKL=Lich Lord}." }, { "Increases the power and cost of all @Mana Abilites@ by 5% of your Max Mana.", "Increases the power and cost of all @Mana Abilities@ by 5% of your Max Mana." }, { "Holy power increased by [15]%", "Holy Power increased by [15]%" }, { "Holy power increased by [6]%", "Holy Power increased by [6]%" }, { "Holy power increased by [3]%", "Holy Power increased by [3]%" }, { "Holy power increased by [9]%", "Holy Power increased by [9]%" }, { "Holy power increased by [12]%", "Holy Power increased by [12]%" }, { "5% of your max life regenerated per turn.", "5% of your max health regenerated per turn." }, { "Recover [0]% of maximum mana each turn. ", "Recover [0]% of max mana each turn. \n<color=#C8B090>Base 10%; the value shown already includes the Shrine Effect Bonus.</color>" }, { "Increases @Life Steal@ by 6%. Reduces @Max Life@ by 15%. <i><color=#808080>\"A thirst so deep, it blurs the lines, Til we're but marionettes of our own designs.\"</i></color>", "Increases @Life Steal@ by 6%. Reduces @Max Health@ by 15%. <i><color=#808080>\"A thirst so deep, it blurs the lines, Til we're but marionettes of our own designs.\"</i></color>" }, { "Increases Damage and Damage Taken by 25%. Increases max life by 15%.", "Increases Damage and Damage Taken by 25%. Increases max health by 15%." }, { "Consuming any Globule heals you for 20% of max life and mana.", "Consuming any Globule heals you for 20% of max health and mana." }, { "Consuming any Globule heals you for 8% of max life and mana.", "Consuming any Globule heals you for 8% of max health and mana." }, { "Increases @maximum mana@ by 20%.", "Increases @max mana@ by 20%." }, { "Every summon active increases the @Max Life@, @Damage@ and @Healing@ of you and your summons by 5%.", "Every summon active increases the @Max Health@, @Damage@ and @Healing@ of you and your summons by 5%." }, { "@Maximum health@ and @maximum mana@ lowered by 25%.", "@Max health@ and @max mana@ lowered by 25%." }, { "Recover [0]% of maximum health each turn. ", "Recover [0]% of max health each turn. \n<color=#C8B090>Base 10%; the value shown already includes the Shrine Effect Bonus.</color>" }, { "@Maximum health@ increased by 10% per stack.", "@Max health@ increased by 10% per stack." }, { "@Maximum health@ and @maximum mana@ lowered by 25% per stack.", "@Max health@ and @max mana@ lowered by 25% per stack." }, { "Increases damage received by 20%.", "Increases damage taken by 20%." }, { "Heals 25% of your maximum health and removes all negative statuses. Can be used when Disabled.", "Heals 25% of your max health and removes all negative statuses. Can be used when Disabled." }, { "Every enemy that dies heals you for 10% of your maximum health.", "Every enemy that dies heals you for 10% of your max health." }, { "Increases @Damage dealt@ and @Max Life@ by 15%. Increases @damage received@ by 15%.", "Increases @Damage dealt@ and @Max Health@ by 15%. Increases @damage taken@ by 15%." }, { "For every enemy within 3 hexes of your character gain 3% @increased Damage@ and 3% @Maximum Health@.", "For every enemy within 3 hexes of your character gain 3% @increased Damage@ and 3% @Max Health@." }, { "Grants 10% @mana steal@. Sacrifices 10% maximum health per turn.", "Grants 10% @mana steal@. Sacrifices 10% max health per turn." }, { "Gain 2% of your maximum health as @Additional Shadow Damage@. Current Shadow Damage Increase: [0].", "Gain 2% of your max health as @Additional Shadow Damage@. Current Shadow Damage Increase: [0]." }, { "Increase the effectiveness of your damage and healing skills by 30%. Maximum health reduced by 20%.", "Increase the effectiveness of your damage and healing skills by 30%. Max health reduced by 20%." }, { "Devour the life force of all enemies within 2 hexes dealing *0 Shadow Damage and giving you 10% @Maximum Health@ for each enemy effected.", "Devour the life force of all enemies within 2 hexes dealing *0 Shadow Damage and giving you 10% @Max Health@ for each enemy effected.\n\n<color=#C8B090>Each stack grants another 10% Max Health; you gain one stack per enemy hit.</color>" }, { "Every enemy slain grants 6% @maximum health@ and @Increased Damage@. Lasts the duration of the battle. Stacks up to 5 times.", "Every enemy slain grants 6% @max health@ and @Increased Damage@. Lasts the duration of the battle. Stacks up to 5 times." }, { "Bless all allies within 5 hexes. Increases @damage@, @maximum health@, and @maximum Mana@ by 10%.", "Bless all allies within 5 hexes. Increases @damage@, @max health@, and @max mana@ by 10%." }, { "Increases @Damage dealt@ and @damage received@ by 25%. Increases @Max Life@ by 15%.", "Increases @Damage dealt@ and @damage taken@ by 25%. Increases @Max Health@ by 15%." }, { "Divine Resistance increased by 5%", "Holy Resistance increased by 5%" }, { "Divine Resistance increased by 15%", "Holy Resistance increased by 15%" }, { "Divine Resistance increased by 10%", "Holy Resistance increased by 10%" }, { "Applies @2@ @poison@ when striking Applies @2@ @poison@ when struck", "Applies @2@ @poison@ when striking. Applies @2@ @poison@ when struck." }, { "Increased damage by 50% All resistances lowered by 25% Control Resistance", "Increased damage by 50%. All resistances except Holy lowered by 25%. Control Resistance" }, { "Increases damage and summon damage by 10%", "Increases damage and summon damage by 10%.\n<color=#C8B090>Also increases the healing this character does by the same percentage.</color>" }, { "Ranged spells deal damage the closer you are. Calculates how much ranged you have and the closer you are from your max range the more damage you deal.", "Increases damage by 50%.\n<color=#C8B090>Also increases the healing this character does by the same percentage.</color>" }, { "Movement increased by 3 Action Points increased by 1 Max Health reduced by 25% Damage reduced by 25%", "Movement increased by 3. Action Points increased by 1. Max Health reduced by 25%. Damage reduced by 25%.\n<color=#C8B090>Also reduces the healing this character does by the same percentage.</color>\n<color=#C8B090>Current health is reduced by the same percentage and comes back when the status ends. This cannot kill.</color>" }, { "Grants 1 additional Action Points. Lowers movement points by 6.", "Grants 1 additional Action Point. Lowers movement points by 6." }, { "Grants 1 additional Action Point. Increases Damage Taken by 25%.", "Grants 1 additional Action Point." }, { "Immune to Bleeding.", "Immune to Blind." }, { "All Resistances reduced by 30%", "All Resistances except Holy reduced by 30%" }, { "All Resistances reduced by 20%", "All Resistances except Holy reduced by 20%" }, { "All Resistances reduced by 10%", "All Resistances except Holy reduced by 10%" }, { "Deals *0 damage. Lifesteals for 8%.", "Deals *0 damage. Lifesteals for 2.5%." }, { "All stats reduced by 15%", "Might, Dexterity, Intelligence, Vitality and Recovery reduced by 15%" }, { "Cannot receive additional actions points. Caused by receiving additional action points this turn.", "Cannot receive additional action points. Caused by receiving additional action points this turn." }, { "Your damage dealt is reduced by 20%", "Your damage dealt is reduced by 20%." }, { "Dash to an enemy dealing *0 weapon damage then quickly dash to an enemy within 2 hexes to strike again. Strikes up to 4 times. Each time you strike a target it increases your dodge chance by 8%. Lasts 2 turns. ", "Dash to an enemy dealing *0 weapon damage then quickly dash to an enemy within 3 hexes to strike again. Strikes up to 4 times. Each time you strike a target it increases your dodge chance by 8%. Lasts 2 turns. " }, { "Dash to an enemy dealing *0 weapon damage then quickly dash to an enemy within 2 hexes to strike again. Strikes up to 4 times. Each time you hit increases the damage of Power Strikes by 50%.", "Dash to an enemy dealing *0 weapon damage then quickly dash to an enemy within 3 hexes to strike again. Strikes up to 4 times. Each time you hit increases the damage of Power Strikes by 50%." }, { "Dash to an enemy dealing *0 weapon damage then quickly dash to an enemy within 2 hexes to strike again. Strikes up to 4 times.", "Dash to an enemy dealing *0 weapon damage then quickly dash to an enemy within 3 hexes to strike again. Strikes up to 3 times." }, { "Resist Divine", "Resist Holy" }, { "Resistance Divine", "Resistance Holy" }, { "Shops are refreshed every time your party completes a quest. Check back often for new loot!", "Shops are refreshed every time your party completes a quest. Check back often for new loot!" }, { "A well timed healing or mana potion can turn the tide of battle. ", "A well timed healing or mana potion can turn the tide of battle." }, { "Deals *0 shadow damage to the target every turn for 3 turns. @Life Steals@ for 6%. @Life Steal@ heals you for a percentage of your @max health@ each time you hit. Reduced for area of effect and no ap cost abilities.", "Deals *0 shadow damage to the target every turn for 3 turns. @Life Steals@ for 2.5%. @Life Steal@ heals you for a percentage of your @max health@ each time you hit. Reduced for area of effect and no ap cost abilities." }, { "The caster creates an poison gas cloud applies @4@ stacks of {STA=Poisoned} within 2 hexes of the selected target.", "The caster creates a poison gas cloud that applies @4@ stacks of {STA=Poisoned} within 2 hexes of the selected target." }, { "Raise a Undead Wizard to fight by your side.", "Raise an Undead Wizard to fight by your side.\n<color=#C8B090>Undead Wizard: Bone Explosion, Consumption, Ghost Armor\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Completely negates the next attack. Lasts until hit. ", "Completely negates the next attack. Lasts until hit." }, { "Crush the target's soul dealing *0 @Shadow Damage@ increased by your Max Health. ", "Crush the target's soul dealing *0 @Shadow Damage@ increased by your Max Health." }, { "Summon a skeletal archer to fight by your side.", "Raise a skeletal archer to fight by your side.\n<color=#C8B090>Skeletal Archer: Ranged Attack\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Summon a skeletal mage to fight by your side.", "Raise a skeletal mage to fight by your side.\n<color=#C8B090>Skeletal Mage: Frost Nova, Fireball, Twister, Ghost Armor\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Summon a skeletal warrior to fight by your side.", "Raise a skeletal warrior to fight by your side.\n<color=#C8B090>Skeletal Warrior: Melee Attack, Cleave\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Raise a Mighty Iron Golem to fight for you.", "Raise a Mighty Iron Golem to fight by your side.\n<color=#C8B090>Iron Golem: Ground Slam\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Gain the ability to shapeshift into a powerful elemental @Dragonkin@. Empowers basic attack, grants new abilities, and resistance based on the color you choose. Increases @Armor@ by [0].", "Shapeshift into a powerful elemental @Dragonkin@. Empowers basic attack, grants new abilities, and resistance based on the color you choose. Increases @Armor@ by [0].\n<color=#C8B090>Scales with your character level.</color>\n<color=#C8B090>The Dragonkin grants a Slash, a Breath and an Orb of its element: Shadow, Frost, Lightning, Fire or Holy.</color>" }, { "Sacrifice 15% of your maximum health in exchange for an additonal action this turn. Applies {STA=Exhaustion}.", "Sacrifice 15% of your max health in exchange for an additional action this turn. Applies {STA=Exhaustion}." }, { "Increase the range of all non-melee abilites by 1 hex. ", "Increase the range of all non-melee abilities by 1 hex. " }, { "Deals *0 weapon damage. Every hex between you and your target increases the damage of by 10%. Called Shot cannot be dodged or blocked.", "Deals *0 weapon damage. Every hex between you and your target increases the damage by 10%. Called Shot cannot be dodged or blocked." }, { "Deals *0 weapon damage, removes 1 random benefical status from the target and lowers the target's @Resistance@ by 20% for 2 turns.", "Deals *0 weapon damage, removes 1 random beneficial status from the target and lowers the target's @Resistance@ by 20% for 2 turns." }, { "Deals *0 weapon damage. Removes 1 random benefical status from the target.", "Deals *0 weapon damage. Removes 1 random beneficial status from the target." }, { "Every enemy slain on your turn grants 1 additonal AP. Can only grant up to 1 additional AP per turn.", "Every enemy slain on your turn grants 1 additional AP. Can only grant up to 1 additional AP per turn." }, { "Deals *0 shadow damage and removes a random benefical status from the target. If a status is removed, the target suffers an additional *0 shadow damage.", "Deals *0 shadow damage and removes a random beneficial status from the target. If a status is removed, the target suffers an additional *0 shadow damage." }, { "Deals *0 weapon damage. Every hex between you and your target increases the damage of by 10%. ", "Deals *0 weapon damage. Every hex between you and your target increases the damage by 10%. " }, { "Deals *0 weapon damage. Every hex between you and your target increases the damage of by 15%. ", "Deals *0 weapon damage. Every hex between you and your target increases the damage by 15%. " }, { "Infects enemies with Stunning Spores applying {STA=Stun} for 1 turns.", "Infects enemies with Stunning Spores applying {STA=Stun} for 1 turn." }, { "Hurls a ball of Ice dealing *0 cold damage to all targets in range. Applies 5 stacks of {STA=Chilled}", "Hurls a ball of Ice dealing *0 cold damage to all targets in range. Applies 5 stacks of {STA=Chilled}." }, { "Hurls a ball of flame dealing *0 fire damage to all targets in range. Applies 10 stacks of {STA=Heat}", "Hurls a ball of flame dealing *0 fire damage to all targets in range. Applies 10 stacks of {STA=Heat}." }, { "Summons a Timber Wolf to fight for you", "Summons a Timber Wolf to fight for you.\n<color=#C8B090>Timber Wolf: Melee Attack, Cripple\nDoes not copy your attributes.\nYour Might raises its damage; your Intelligence, its health.</color>" }, { "Increases damage dealt and max life by 15%. Increases damage received by 15%.", "Increases damage dealt and max health by 15%. Increases damage taken by 15%.\n\n<color=#C8B090>Current health is increased by the same percentage and goes back down when the status ends.\nAlso increases the healing this character does by the same percentage.</color>" }, { "@Maximum health@ and @Damage Dealt@ increased by 6% per stack.", "@Max health@ and @Damage Dealt@ increased by 6% per stack.\n\n<color=#C8B090>Stacks up to 5 times.\nAlso increases the healing you do by the same percentage.</color>" }, { "@Maximum health@ and @maximum mana@ increased by 10%. @Damage dealt@ increased by 10%.", "@Max health@ and @max mana@ increased by 10%. @Damage dealt@ increased by 10%.\n\n<color=#C8B090>Also increases the healing you do by the same percentage.</color>" }, { "Maximum health increased by 25% Deals more damage at lower health", "Max health increased by 25% Deals more damage at lower health\n\n<color=#C8B090>Lasts 3 turns.\nThe bonus is your missing health as a percentage: up to +100% damage at 1 health.</color>" }, { "Increases damage dealt by 25%. Increases max life and damage taken by 15%.", "Increases damage dealt by 25%. Increases max health and damage taken by 15%.\n\n<color=#C8B090>Lasts 3 turns.\nAlso increases the healing you do by the same percentage.</color>" }, { "Maximum health reduced by 15%.", "Max health reduced by 15%.\n\n<color=#C8B090>Current health is reduced by the same percentage and comes back when the status ends. This cannot kill.</color>" }, { "@Maximum health@ reduced by 10%.", "@Max health@ reduced by 10%.\n\n<color=#C8B090>Each stack grants another 10% Max Health; you gain one stack per enemy hit.</color>" }, { "@Maximum health@ reduced by 20%.", "@Max health@ reduced by 20%.\n\n<color=#C8B090>Each stack grants another 10% Max Health; you gain one stack per enemy hit.</color>" }, { "Removes all negative statuses from friendly target but inflicts 10% of target's maximum health as fire damage. Can be used when Disabled.", "Removes all negative statuses from friendly target but inflicts 10% of target's max health as fire damage. Can be used when Disabled.\n\n<color=#C8B090>This cannot reduce the target below 1 health.</color>" }, { "The caster reaches out in aid healing 30% of target's maximum health.", "The caster reaches out in aid healing 30% of target's max health.\n\n<color=#C8B090>Healing scales with your Holy Power.\nReduced by effects that lower the target's healing received.</color>" }, { "Max Health, Damage, Armor, and Dodge Chance reduced by 50%.", "Max Health, Damage, Armor, Magic Armor, and Dodge Chance reduced by 50%." }, { "Damage increased 50%.", "Damage increased by 50%." }, { "Enchants the target's weapon to add *0 fire damage to attacks. ", "Enchants the target's weapon to add *0 fire damage to hits, including spells. " }, { "Enchants the target's weapon to add *0 cold damage to attacks. ", "Enchants the target's weapon to add *0 cold damage to hits, including spells. " }, { "Enchants the target's weapon to add *0 lightning damage to attacks. ", "Enchants the target's weapon to add *0 lightning damage to hits, including spells. " } }; private static readonly HashSet<string> _appliedFixes = new HashSet<string>(); private static readonly HashSet<string> _appliedAppends = new HashSet<string>(); private static readonly HashSet<string> _rotulosDeStatusAvisados = new HashSet<string>(); private static bool _loggedAlive; private static readonly Regex AttributePowerupRegex = new Regex("^\\+\\s*\\d+\\s+to\\s+(Might|Dexterity|Intelligence|Vitality|Reflex)$", RegexOptions.Compiled); private static readonly Regex DamageReductionRegex = new Regex("^\\+\\s*\\d+%\\s+Damage Reduction$", RegexOptions.Compiled); private static readonly (Regex Pattern, string Attribute)[] StatusAttributeRules = new(Regex, string)[5] { (new Regex("^Might increased by (\\[\\d+\\]|\\d+)\\.$", RegexOptions.Compiled), "Might"), (new Regex("^Dexterity increased by (\\[\\d+\\]|\\d+)\\.$", RegexOptions.Compiled), "Dexterity"), (new Regex("^Intelligence increased\\.$|^Intelligence increased by (\\[\\d+\\]|\\d+)\\.$", RegexOptions.Compiled), "Intelligence"), (new Regex("^Vitality increased by (\\[\\d+\\]|\\d+)\\.$", RegexOptions.Compiled), "Vitality"), (new Regex("^Reflex increased by (\\[\\d+\\]|\\d+)\\.$", RegexOptions.Compiled), "Reflex") }; private static readonly (Regex Pattern, string Attribute)[] FortuneAttributeRules = new(Regex, string)[5] { (new Regex("^@Might@ increased by \\{\\d+,\\d+\\}\\.?", RegexOptions.Compiled), "Might"), (new Regex("^@Dexterity@ increased by \\{\\d+,\\d+\\}\\.?", RegexOptions.Compiled), "Dexterity"), (new Regex("^@Intelligence@ increased by \\{\\d+,\\d+\\}\\.?", RegexOptions.Compiled), "Intelligence"), (new Regex("^@Vitality@ increased by \\{\\d+,\\d+\\}\\.?", RegexOptions.Compiled), "Vitality"), (new Regex("^@Reflex@ increased by \\{\\d+,\\d+\\}\\.?", RegexOptions.Compiled), "Reflex") }; private const string ResistanceExplainSuffix = "\nResistances reduce their damage type by the listed %. If a resistance goes negative, that damage type is amplified instead."; private static readonly string[] ResistanceReductionPrefixes = new string[7] { "Elemental resistance reduced by 5% per stack", "Reduces @Cold Resistance@ by @25%@ per stack", "Reduces @Fire Resistance@ by @25%@ per stack", "Reduces @Physical Resistance@ by @25%@ per stack", "Reduces Elemental resistances by 25%", "@Resistance@ lowered by 20%", "@Resistances@ lowered by 20%. Reduces healing received by 50%" }; private static readonly HashSet<string> GlossaryLabelExclusions = new HashSet<string> { "Life Steal", "Lifesteal", "% Life Steal", "% Lifesteal", "Stealth", "Enrage", "Marked Prey" }; private static readonly (Regex Match, string Append, string ExcludeIfContains)[] SkillGlossaryRules = new(Regex, string, string)[4] { (new Regex("\\bStealth\\b", RegexOptions.Compiled), "\nStealth: attacking from stealth has 100% critical hit chance.", "critical hit chance"), (new Regex("Marked Prey", RegexOptions.Compiled), "\nMarked Prey: increases damage taken by 10% per stack.", "Damage taken"), (new Regex("\\bEnrage\\b", RegexOptions.Compiled), "\nEnraged characters are immune to movement impairing effects and knockback.", "immune to"), (new Regex("Life Steal|Lifesteal", RegexOptions.Compiled), "\nLife Steal: heals you for a percentage of your Max Health each time you hit. Reduced for area of effect abilities.", "heals you for") }; private static readonly Regex ArmorAffixRegex = new Regex("\\bArmor\\b", RegexOptions.Compiled); private static readonly HashSet<string> ArmorQueTambemDaMagicArmor = new HashSet<string> { "Armor increased by 5%", "Armor increased by 35%", "Armor increased by 15%", "Armor increased by [50]% Movement points lowered 1", "Armor increased by 25% Movement points lowered", "Armor increased by [30]% Movement points lowered 1", "Armor increased by 45% Movement points lowered", "Armor increased by 20%", "Armor increased by 25%", "Armor increased by [60]% Movement points lowered 1", "Armor increased by [20]% Movement points lowered 1", "Armor increased by 35% Movement points lowered", "Armor increased by 45%", "Armor increased by [40]% Movement points lowered 1", "Armo