Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of CombatLog v1.0.0
CombatLog.dll
Decompiled 15 hours agousing System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace BubCombatLog; internal class Step { public string Label; public string Detail; public float Before; public float After; public Step(string label, string detail, float before, float after) { Label = label; Detail = detail; Before = before; After = after; } } internal class Half { public bool IsAttack; public ZDOID Attacker; public ZDOID Victim; public float Key; public string AttackerName = "?"; public string VictimName = "?"; public List<Step> Steps = new List<Step>(); public float HpBefore; public float HpAfter; public float HpMax; public bool Blocked; public bool Parry; public bool Timed; public bool GuardBroken; public bool Dodged; public bool Killed; public bool Staggered; public string BlockKind = ""; public float Held; public float Stamina; public float StaggerBefore; public float StaggerAfter; public float StaggerMax; public float Instant; public float Dot; public string DamageText = ""; public void Write(ZPackage p) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) p.Write(IsAttack); p.Write(Attacker); p.Write(Victim); p.Write(Key); p.Write(AttackerName); p.Write(VictimName); p.Write(Steps.Count); foreach (Step step in Steps) { p.Write(step.Label); p.Write(step.Detail ?? ""); p.Write(step.Before); p.Write(step.After); } p.Write(HpBefore); p.Write(HpAfter); p.Write(HpMax); int num = (Blocked ? 1 : 0) | (Parry ? 2 : 0) | (Timed ? 4 : 0) | (GuardBroken ? 8 : 0) | (Dodged ? 16 : 0) | (Killed ? 32 : 0) | (Staggered ? 64 : 0); p.Write(num); p.Write(BlockKind); p.Write(Held); p.Write(Stamina); p.Write(StaggerBefore); p.Write(StaggerAfter); p.Write(StaggerMax); p.Write(Instant); p.Write(Dot); p.Write(DamageText); } public static Half Read(ZPackage p) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) Half half = new Half(); half.IsAttack = p.ReadBool(); half.Attacker = p.ReadZDOID(); half.Victim = p.ReadZDOID(); half.Key = p.ReadSingle(); half.AttackerName = p.ReadString(); half.VictimName = p.ReadString(); int num = p.ReadInt(); for (int i = 0; i < num; i++) { half.Steps.Add(new Step(p.ReadString(), p.ReadString(), p.ReadSingle(), p.ReadSingle())); } half.HpBefore = p.ReadSingle(); half.HpAfter = p.ReadSingle(); half.HpMax = p.ReadSingle(); int num2 = p.ReadInt(); half.Blocked = (num2 & 1) != 0; half.Parry = (num2 & 2) != 0; half.Timed = (num2 & 4) != 0; half.GuardBroken = (num2 & 8) != 0; half.Dodged = (num2 & 0x10) != 0; half.Killed = (num2 & 0x20) != 0; half.Staggered = (num2 & 0x40) != 0; half.BlockKind = p.ReadString(); half.Held = p.ReadSingle(); half.Stamina = p.ReadSingle(); half.StaggerBefore = p.ReadSingle(); half.StaggerAfter = p.ReadSingle(); half.StaggerMax = p.ReadSingle(); half.Instant = p.ReadSingle(); half.Dot = p.ReadSingle(); half.DamageText = p.ReadString(); return half; } } internal static class Combat { private class Ctx { public Ctx Prev; public Character Victim; public HitData Hit; public Half Half; public float StaminaBefore; public float BlockBefore; public float AfterArmor = -1f; } public static FieldInfo StaggerDamage; public static FieldInfo BlockTimer; public static FieldInfo AttackCharacter; public static FieldInfo AttackWeapon; public static FieldInfo SemanCharacter; public static Func<Humanoid, ItemData> GetCurrentBlocker; public static Func<Character, float> GetStaggerTreshold; private static readonly ConditionalWeakTable<HitData, Half> AttackByHit = new ConditionalWeakTable<HitData, Half>(); private static readonly ConditionalWeakTable<object, Half> AttackBySource = new ConditionalWeakTable<object, Half>(); private static object currentSource; private static readonly List<string> SiteLabels = new List<string>(); private static Ctx current; public static void Patch(Harmony h) { StaggerDamage = AccessTools.Field(typeof(Character), "m_staggerDamage"); BlockTimer = AccessTools.Field(typeof(Humanoid), "m_blockTimer"); AttackCharacter = AccessTools.Field(typeof(Attack), "m_character"); AttackWeapon = AccessTools.Field(typeof(Attack), "m_weapon"); SemanCharacter = AccessTools.Field(typeof(SEMan), "m_character"); GetCurrentBlocker = AccessTools.MethodDelegate<Func<Humanoid, ItemData>>(AccessTools.Method(typeof(Humanoid), "GetCurrentBlocker", (Type[])null, (Type[])null), (object)null, true); GetStaggerTreshold = AccessTools.MethodDelegate<Func<Character, float>>(AccessTools.Method(typeof(Character), "GetStaggerTreshold", (Type[])null, (Type[])null), (object)null, true); h.Patch((MethodBase)AccessTools.Method(typeof(Attack), "ModifyDamage", (Type[])null, (Type[])null), Hm("ModifyDamagePrefix"), Hm("ModifyDamagePostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(SEMan), "ModifyAttack", (Type[])null, (Type[])null), Hm("ModifyAttackPrefix"), Hm("ModifyAttackPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(Projectile), "Setup", (Type[])null, (Type[])null), (HarmonyMethod)null, Hm("SourceSetup"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(Aoe), "Setup", (Type[])null, (Type[])null), (HarmonyMethod)null, Hm("SourceSetup"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(Projectile), "OnHit", (Type[])null, (Type[])null), Hm("SourceHitPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, Hm("SourceHitFinalizer"), (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(Aoe), "OnHit", (Type[])null, (Type[])null), Hm("SourceHitPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, Hm("SourceHitFinalizer"), (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(Character), "Damage", (Type[])null, (Type[])null), Hm("DeliverPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); HarmonyMethod val = Hm("DefenseTranspiler"); MethodInfo methodInfo = AccessTools.Method(typeof(Character), "RPC_Damage", (Type[])null, (Type[])null); HarmonyMethod obj = Hm("DefensePrefix"); HarmonyMethod obj2 = Hm("DefensePostfix"); HarmonyMethod val2 = Hm("DefenseFinalizer"); h.Patch((MethodBase)methodInfo, obj, obj2, val, val2, (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(Character), "ApplyDamage", (Type[])null, (Type[])null), Hm("ApplyDamagePrefix"), Hm("ApplyDamagePostfix"), val, (HarmonyMethod)null, (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(Humanoid), "BlockAttack", (Type[])null, (Type[])null), Hm("BlockPrefix"), Hm("BlockPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(Character), "Stagger", (Type[])null, (Type[])null), (HarmonyMethod)null, Hm("StaggerPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(Character), "AddStaggerDamage", (Type[])null, (Type[])null), Hm("AddStaggerPrefix"), Hm("AddStaggerPostfix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); h.Patch((MethodBase)AccessTools.Method(typeof(Player), "OnDeath", (Type[])null, (Type[])null), Hm("DeathPrefix"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static HarmonyMethod Hm(string name) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return new HarmonyMethod(typeof(Combat).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic)); } public static string Name(Character c) { if ((Object)(object)c == (Object)null) { return "ambiente"; } if (c is Player) { return c.GetHoverName(); } string text = ((Localization.instance != null) ? Localization.instance.Localize(c.m_name) : c.m_name); int num = c.GetLevel() - 1; if (num <= 0) { return text; } return text + " " + new string('★', num); } public static float Total(HitData hit) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) DamageTypes damage = hit.m_damage; return damage.m_damage + damage.m_blunt + damage.m_slash + damage.m_pierce + damage.m_fire + damage.m_frost + damage.m_lightning + damage.m_poison + damage.m_spirit; } public static string Source(Character attacker, HitData hit) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected I4, but got Unknown if ((Object)(object)attacker != (Object)null) { return Name(attacker); } HitType hitType = hit.m_hitType; return (hitType - 3) switch { 0 => "queda", 1 => "afogamento", 2 => "queimadura", 3 => "congelamento", 4 => "veneno", 5 => "água", 6 => "fumaça", 7 => "borda do mundo", 8 => "impacto", 9 => "carroça", 10 => "árvore caindo", 11 => "você mesmo", 12 => "estrutura caindo", 13 => "torreta", 14 => "barco", 15 => "estalactite", 16 => "catapulta", 17 => "fogo de brasa", 18 => "mar das Ashlands", 19 => "lava", 20 => "incinerador", 21 => "ponte levadiça", _ => "ambiente", }; } public static float Bar(Character c) { return (float)StaggerDamage.GetValue(c); } public static ZDOID Id(Character c) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)c != (Object)null)) { return ZDOID.None; } return c.GetZDOID(); } public static string F(float v) { return v.ToString("0.00", CultureInfo.InvariantCulture).Replace('.', ','); } public static string N(float v) { return Mathf.RoundToInt(v).ToString(); } public static string DamageText(DamageTypes d) { List<string> list = new List<string>(); Part(list, d.m_damage, "puro"); Part(list, d.m_blunt, "contundente"); Part(list, d.m_slash, "corte"); Part(list, d.m_pierce, "perfurante"); Part(list, d.m_fire, "fogo"); Part(list, d.m_frost, "gelo"); Part(list, d.m_lightning, "raio"); Part(list, d.m_poison, "veneno"); Part(list, d.m_spirit, "espírito"); return string.Join(", ", list.ToArray()); } private static void Part(List<string> parts, float v, string name) { if (v >= 0.5f) { parts.Add(N(v) + " " + name); } } private static bool IsLocal(Character c) { if ((Object)(object)c != (Object)null) { return (Object)(object)c == (Object)(object)Player.m_localPlayer; } return false; } private static void ModifyDamagePrefix(Attack __instance, HitData __0, float __1) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_00b7: Unknown result type (might be due to invalid IL or missing references) Character val = (Character)AttackCharacter.GetValue(__instance); ItemData val2 = (ItemData)AttackWeapon.GetValue(__instance); Half half = new Half(); half.IsAttack = true; half.AttackerName = Name(val); float num = Total(__0); string text = ((val2 != null && Localization.instance != null) ? Localization.instance.Localize(val2.m_shared.m_name) : "arma"); string text2 = ((val2 != null && val2.m_quality > 1) ? (" (nível " + val2.m_quality + ")") : ""); half.Steps.Add(new Step("Dano base: " + text + text2, DamageText(__0.m_damage), num, num)); num = Mult(half, "Multiplicador do ataque", __instance.m_damageMultiplier, num); num = Mult(half, (val is Player) ? "Skill (com variação aleatória)" : "Variação aleatória", __1, num); if ((Object)(object)val != (Object)null) { int num2 = val.GetLevel() - 1; num = Mult(half, "Estrelas do atacante (" + Mathf.Max(0, num2) + "★)", 1f + (float)Mathf.Max(0, num2) * 0.5f, num); if (__instance.m_damageMultiplierPerMissingHP > 0f) { num = Mult(half, "Bônus por vida faltando", 1f + (val.GetMaxHealth() - val.GetHealth()) * __instance.m_damageMultiplierPerMissingHP, num); } if (__instance.m_damageMultiplierByTotalHealthMissing > 0f) { num = Mult(half, "Bônus por % de vida faltando", 1f + (1f - val.GetHealthPercentage()) * __instance.m_damageMultiplierByTotalHealthMissing, num); } } AttackByHit.Remove(__0); AttackByHit.Add(__0, half); } private static float Mult(Half t, string label, float factor, float before) { if (Mathf.Abs(factor - 1f) < 0.001f) { return before; } float num = before * factor; t.Steps.Add(new Step(label, "×" + F(factor), before, num)); return num; } private static void ModifyDamagePostfix(HitData __0) { if (AttackByHit.TryGetValue(__0, out var value)) { Reconcile(value, Total(__0), "Ajuste do jogo/mods"); } } private static void Reconcile(Half t, float actual, string label) { float num = ((t.Steps.Count > 0) ? t.Steps[t.Steps.Count - 1].After : actual); if (!(Mathf.Abs(actual - num) < 0.05f)) { string detail = ((num > 0.01f) ? ("×" + F(actual / num)) : ""); t.Steps.Add(new Step(label, detail, num, actual)); } } private static void ModifyAttackPrefix(ref HitData __1) { if (__1 != null && AttackByHit.TryGetValue(__1, out var value)) { Reconcile(value, Total(__1), "Bônus do golpe (fim de combo)"); } } private static void ModifyAttackPostfix(SEMan __instance, ref HitData __1) { if (__1 == null || !AttackByHit.TryGetValue(__1, out var value)) { return; } float after = value.Steps[value.Steps.Count - 1].After; float num = Total(__1); if (Mathf.Abs(num - after) < 0.05f) { return; } List<string> list = new List<string>(); foreach (StatusEffect statusEffect in __instance.GetStatusEffects()) { if ((Object)(object)statusEffect != (Object)null && !string.IsNullOrEmpty(statusEffect.m_name)) { list.Add((Localization.instance != null) ? Localization.instance.Localize(statusEffect.m_name) : statusEffect.m_name); } } value.Steps.Add(new Step("Efeitos no atacante", "×" + F(num / Mathf.Max(0.01f, after)) + ((list.Count > 0) ? (" (" + string.Join(", ", list.ToArray()) + ")") : ""), after, num)); } private static void SourceSetup(object __instance, HitData __3) { if (__3 != null && AttackByHit.TryGetValue(__3, out var value)) { AttackBySource.Remove(__instance); AttackBySource.Add(__instance, value); } } private static string SourceName(object source) { Component val = (Component)((source is Component) ? source : null); if (!((Object)(object)val != (Object)null)) { return "?"; } return ((Object)val.gameObject).name.Replace("(Clone)", "").Trim(); } private static void SourceHitPrefix(object __instance, out object __state) { __state = currentSource; currentSource = __instance; } private static Exception SourceHitFinalizer(Exception __exception, object __state) { currentSource = __state; return __exception; } private static void DeliverPrefix(Character __instance, HitData __0) { //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) if (__0 == null) { return; } Character attacker = __0.GetAttacker(); if (IsLocal(attacker) || IsLocal(__instance) || __instance is Player) { if (!AttackByHit.TryGetValue(__0, out var value) && (currentSource == null || !AttackBySource.TryGetValue(currentSource, out value))) { value = new Half(); value.AttackerName = Source(attacker, __0); string label = ((currentSource is Aoe) ? ("Efeito de área: " + SourceName(currentSource)) : ((currentSource is Projectile) ? ("Projétil: " + SourceName(currentSource)) : (((Object)(object)attacker == (Object)null) ? ("Causa: " + Source(null, __0)) : "Golpe sem detalhamento (origem não rastreada)"))); float num = Total(__0); value.Steps.Add(new Step(label, DamageText(__0.m_damage) + ((currentSource != null) ? " (dano próprio do efeito)" : ""), num, num)); } Half half = new Half(); half.IsAttack = true; half.Steps.AddRange(value.Steps); half.AttackerName = Source(attacker, __0); half.VictimName = Name(__instance); half.Attacker = __0.m_attacker; half.Victim = Id(__instance); half.Key = Total(__0); Reconcile(half, half.Key, (currentSource is Projectile) ? "Ajuste do projétil" : ((currentSource is Aoe) ? "Ajuste da área" : "Ajuste do jogo/mods")); if (IsLocal(attacker) || IsLocal(__instance)) { Log.Merge(half); } if (__instance is Player && !IsLocal(__instance)) { Net.Send(__instance, half); } } } private static void DefensePrefix(Character __instance, HitData __1, out Ctx __state) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) __state = null; if (__1 != null && !((Object)(object)Player.m_localPlayer == (Object)null)) { Character attacker = __1.GetAttacker(); if ((IsLocal(__instance) || attacker is Player) && !__instance.IsDead() && !__instance.IsTeleporting() && !__instance.InCutscene()) { Ctx ctx = new Ctx(); ctx.Prev = current; ctx.Victim = __instance; ctx.Hit = __1; Half half = new Half(); half.Attacker = __1.m_attacker; half.Victim = Id(__instance); half.Key = Total(__1); half.AttackerName = Source(attacker, __1); half.VictimName = Name(__instance); half.DamageText = DamageText(__1.m_damage); half.HpBefore = __instance.GetHealth(); half.HpMax = __instance.GetMaxHealth(); half.StaggerBefore = Bar(__instance); half.StaggerMax = GetStaggerTreshold(__instance); ctx.Half = half; ctx.StaminaBefore = (IsLocal(__instance) ? Player.m_localPlayer.GetStamina() : 0f); current = ctx; __state = ctx; } } } private static Exception DefenseFinalizer(Exception __exception, Ctx __state) { if (__state != null) { current = __state.Prev; } return __exception; } private static void DefensePostfix(Character __instance, Ctx __state) { if (__state != null) { Half half = __state.Half; half.HpAfter = __instance.GetHealth(); half.StaggerAfter = Bar(__instance); half.Killed = __instance.IsDead() || half.HpAfter <= 0f; if (IsLocal(__instance)) { half.Stamina = __state.StaminaBefore - Player.m_localPlayer.GetStamina(); } if (half.Steps.Count == 0 && half.HpAfter >= half.HpBefore) { half.Dodged = true; } Character attacker = __state.Hit.GetAttacker(); if (IsLocal(attacker) || IsLocal(__instance)) { Log.Merge(half); } if (attacker is Player && !IsLocal(attacker)) { Net.Send(attacker, half); } } } private static Ctx Active(HitData hit) { Ctx ctx = current; if (ctx == null || ctx.Hit != hit) { return null; } return ctx; } private static IEnumerable<CodeInstruction> DefenseTranspiler(IEnumerable<CodeInstruction> instructions) { //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(instructions); MethodInfo methodInfo = AccessTools.Method(typeof(HitData), "ApplyModifier", new Type[1] { typeof(float) }, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(HitData), "ApplyArmor", (Type[])null, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(typeof(HitData), "ApplyResistance", (Type[])null, (Type[])null); MethodInfo methodInfo4 = AccessTools.Method(typeof(SEMan), "OnDamaged", (Type[])null, (Type[])null); for (int i = 0; i < list.Count; i++) { CodeInstruction val = list[i]; if (CodeInstructionExtensions.Calls(val, methodInfo) || CodeInstructionExtensions.Calls(val, methodInfo2)) { bool flag = CodeInstructionExtensions.Calls(val, methodInfo2); int count = SiteLabels.Count; SiteLabels.Add(flag ? ArmorLabel(list, i) : ModifierLabel(list, i)); val.opcode = OpCodes.Call; val.operand = AccessTools.Method(typeof(Combat), flag ? "WArmor" : "WModifier", (Type[])null, (Type[])null); CodeInstruction val2 = new CodeInstruction(OpCodes.Ldc_I4, (object)count); val2.labels.AddRange(val.labels); val.labels.Clear(); list.Insert(i, val2); i++; } else if (CodeInstructionExtensions.Calls(val, methodInfo3)) { val.opcode = OpCodes.Call; val.operand = AccessTools.Method(typeof(Combat), "WResistance", (Type[])null, (Type[])null); } else if (CodeInstructionExtensions.Calls(val, methodInfo4)) { val.opcode = OpCodes.Call; val.operand = AccessTools.Method(typeof(Combat), "WOnDamaged", (Type[])null, (Type[])null); } } return list; } private static bool Back(List<CodeInstruction> code, int i, int range, Func<CodeInstruction, bool> test) { int num = i - 1; while (num >= 0 && num >= i - range) { if (test(code[num])) { return true; } num--; } return false; } private static bool LoadsField(CodeInstruction ci, string name) { FieldInfo fieldInfo = ci.operand as FieldInfo; if (fieldInfo != null) { return fieldInfo.Name == name; } return false; } private static bool CallsNamed(CodeInstruction ci, string name) { MethodInfo methodInfo = ci.operand as MethodInfo; if (methodInfo != null) { return methodInfo.Name == name; } return false; } private static string ModifierLabel(List<CodeInstruction> code, int i) { if (Back(code, i, 2, (CodeInstruction c) => LoadsField(c, "m_enemyDamageRate"))) { return "Mundo: dano dos inimigos"; } if (Back(code, i, 2, (CodeInstruction c) => LoadsField(c, "m_playerDamageRate"))) { return "Mundo: dano dos jogadores"; } if (Back(code, i, 2, (CodeInstruction c) => LoadsField(c, "m_localDamgeTakenRate"))) { return "Dano recebido (dificuldade)"; } if (Back(code, i, 2, (CodeInstruction c) => LoadsField(c, "m_backstabBonus"))) { return "Ataque furtivo (backstab)"; } if (Back(code, i, 3, (CodeInstruction c) => CallsNamed(c, "GetDifficultyDamageScalePlayer"))) { return "Escala por nº de jogadores (contra jogador)"; } if (Back(code, i, 6, (CodeInstruction c) => CallsNamed(c, "GetDifficultyDamageScaleEnemy"))) { return "Escala por nº de jogadores (contra inimigo)"; } if (Back(code, i, 2, (CodeInstruction c) => c.opcode == OpCodes.Ldc_R4 && c.operand is float && (float)c.operand == 2f)) { return "Alvo em stagger"; } return "Modificador"; } private static string ArmorLabel(List<CodeInstruction> code, int i) { if (Back(code, i, 3, (CodeInstruction c) => CallsNamed(c, "GetBodyArmor"))) { return "Armadura"; } if (Back(code, i, 4, (CodeInstruction c) => LoadsField(c, "m_worldLevelEnemyBaseAC"))) { return "Armadura base (nível do mundo)"; } return "Armadura"; } private static void WModifier(HitData hit, float factor, int site) { float before = Total(hit); hit.ApplyModifier(factor); Ctx ctx = Active(hit); if (ctx != null && Mathf.Abs(factor - 1f) > 0.001f) { ctx.Half.Steps.Add(new Step(SiteLabels[site], "×" + F(factor), before, Total(hit))); } } private static void WArmor(HitData hit, float armor, int site) { float num = Total(hit); hit.ApplyArmor(armor); Ctx ctx = Active(hit); if (ctx != null) { if (armor > 0f || num - Total(hit) > 0.05f) { ctx.Half.Steps.Add(new Step(SiteLabels[site] + " " + N(armor), "", num, Total(hit))); } ctx.AfterArmor = Total(hit); } } private static void WResistance(HitData hit, DamageModifiers mods, out DamageModifier significant) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) float num = Total(hit); DamageTypes damage = hit.m_damage; hit.ApplyResistance(mods, ref significant); Ctx ctx = Active(hit); if (ctx != null) { List<string> list = new List<string>(); Res(list, damage.m_blunt, mods.m_blunt, "contundente"); Res(list, damage.m_slash, mods.m_slash, "corte"); Res(list, damage.m_pierce, mods.m_pierce, "perfurante"); Res(list, damage.m_fire, mods.m_fire, "fogo"); Res(list, damage.m_frost, mods.m_frost, "gelo"); Res(list, damage.m_lightning, mods.m_lightning, "raio"); Res(list, damage.m_poison, mods.m_poison, "veneno"); Res(list, damage.m_spirit, mods.m_spirit, "espírito"); string label = ((hit.m_weakSpot >= 0) ? "Resistências (ponto fraco)" : "Resistências"); if (list.Count > 0 || Mathf.Abs(num - Total(hit)) > 0.05f) { ctx.Half.Steps.Add(new Step(label, string.Join(", ", list.ToArray()), num, Total(hit))); } } } private static void Res(List<string> parts, float amount, DamageModifier mod, string type) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected I4, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (!(amount < 0.5f) && (int)mod != 0) { parts.Add(type + " " + (mod - 1) switch { 0 => "resistente", 4 => "muito resistente", 6 => "pouco resistente", 1 => "fraco", 5 => "muito fraco", 7 => "pouco fraco", 2 => "imune", 3 => "ignora", _ => ((object)mod).ToString(), }); } } private static void WOnDamaged(SEMan seman, HitData hit, Character attacker) { float num = Total(hit); seman.OnDamaged(hit, attacker); Ctx ctx = Active(hit); if (ctx != null && Mathf.Abs(num - Total(hit)) > 0.05f) { ctx.Half.Steps.Add(new Step("Efeitos no alvo", "", num, Total(hit))); } } private static void BlockPrefix(Humanoid __instance, HitData __0) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Invalid comparison between Unknown and I4 Ctx ctx = Active(__0); if (ctx != null && !((Object)(object)ctx.Victim != (Object)(object)__instance)) { ItemData val = GetCurrentBlocker(__instance); if (val != null) { float timedBlockBonus = val.m_shared.m_timedBlockBonus; float num = (float)BlockTimer.GetValue(__instance); Half half = ctx.Half; half.Timed = num >= 0f && num < 0.25f; half.Parry = half.Timed && timedBlockBonus > 1f; half.BlockKind = (((int)val.m_shared.m_itemType != 5) ? "com arma" : ((timedBlockBonus <= 1f) ? "tower" : ((timedBlockBonus >= 2f) ? "buckler" : "redondo"))); ctx.BlockBefore = Total(__0); } } } private static void BlockPostfix(Humanoid __instance, HitData __0, bool __result) { Ctx ctx = Active(__0); if (ctx != null && !((Object)(object)ctx.Victim != (Object)(object)__instance) && __result && ctx.Half.BlockKind.Length != 0) { Half half = ctx.Half; half.Blocked = true; half.Held = ctx.BlockBefore - Total(__0); string label = (half.Parry ? "Parry " : "Bloqueio ") + half.BlockKind + ((half.Timed && !half.Parry) ? " no tempo" : ""); half.Steps.Add(new Step(label, half.GuardBroken ? "GUARDA QUEBRADA" : "", ctx.BlockBefore, Total(__0))); } } private static void ApplyDamagePrefix(Character __instance, HitData __0, out float __state) { __state = __instance.GetHealth(); Ctx ctx = Active(__0); if (ctx != null) { float num = Total(__0); if (ctx.AfterArmor >= 0f && ctx.AfterArmor - num > 0.05f) { ctx.Half.Dot = ctx.AfterArmor - num; ctx.Half.Steps.Add(new Step("Vira dano contínuo (fogo/veneno/espírito)", "", ctx.AfterArmor, num)); } } } private static void ApplyDamagePostfix(Character __instance, HitData __0, float __state) { float num = __state - __instance.GetHealth(); Ctx ctx = Active(__0); if (ctx != null) { ctx.Half.Instant = num; float num2 = ((ctx.Half.Steps.Count > 0) ? ctx.Half.Steps[ctx.Half.Steps.Count - 1].After : ctx.Half.Key); if (Mathf.Abs(num2 - num) > 0.5f && num >= 0f) { ctx.Half.Steps.Add(new Step("Limite de vida/outros", "", num2, num)); } } else if (num > 0f && (IsLocal(__instance) || IsLocal(__0.GetAttacker()))) { Log.DotTick(__instance, __0, num); } } private static void StaggerPostfix(Character __instance) { Ctx ctx = current; if (ctx != null && (Object)(object)ctx.Victim == (Object)(object)__instance) { ctx.Half.Staggered = true; if (ctx.Half.BlockKind.Length > 0) { ctx.Half.GuardBroken = true; } } else if (Log.Near(__instance)) { Log.Event(Log.Color(Name(__instance) + " CAMBALEOU", "ffcc40")); } } private static void AddStaggerPrefix(Character __instance, out float __state) { __state = Bar(__instance); } private static void AddStaggerPostfix(Character __instance, float __state, float __0) { Ctx ctx = current; if ((ctx == null || !((Object)(object)ctx.Victim == (Object)(object)__instance)) && !(__instance is Player) && Log.Near(__instance) && !(__0 <= 0f)) { Log.Event(Log.Color(Name(__instance) + " +" + N(__0) + " stagger", "d0a0ff") + " (stagger " + N(__state) + "→" + N(Bar(__instance)) + "/" + N(GetStaggerTreshold(__instance)) + ")"); } } private static void DeathPrefix(Player __instance) { if (IsLocal((Character)(object)__instance)) { Log.Death(); } } } internal static class Net { private const string Rpc = "bub_CombatLog_Half"; public static void Register() { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register<ZPackage>("bub_CombatLog_Half", (Action<long, ZPackage>)OnHalf); } } public static void Send(Character to, Half half) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown if (ZRoutedRpc.instance != null && !((Object)(object)to == (Object)null)) { ZNetView component = ((Component)to).GetComponent<ZNetView>(); ZDO val = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (val != null) { ZPackage val2 = new ZPackage(); half.Write(val2); ZRoutedRpc.instance.InvokeRoutedRPC(val.GetOwner(), "bub_CombatLog_Half", new object[1] { val2 }); } } } private static void OnHalf(long sender, ZPackage p) { try { Log.Merge(Half.Read(p)); } catch (Exception ex) { Debug.LogWarning((object)("[Combat Log] pacote de outro jogador inválido: " + ex.Message)); } } } [BepInPlugin("bub.combatlog", "Combat Log", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "bub.combatlog"; public const string Version = "1.0.0"; internal static Plugin Instance; internal ConfigEntry<KeyboardShortcut> toggleKey; internal ConfigEntry<bool> writeFile; internal ConfigEntry<float> nearbyRange; internal ConfigEntry<float> recapSeconds; internal Widget widget; private void Awake() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Expected O, but got Unknown Instance = this; toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "Toggle key", new KeyboardShortcut((KeyCode)289, (KeyCode[])(object)new KeyCode[0]), "Shows/hides the widget (and closes the death recap)."); writeFile = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Write file", true, "Write every hit, detailed, to BepInEx/CombatLog/<date>.log."); nearbyRange = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Nearby range", 40f, "Staggers of creatures within this distance are logged."); recapSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Death recap", "Seconds", 10f, new ConfigDescription("How far back the death recap looks.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 60f), new object[0])); widget = new Widget(((BaseUnityPlugin)this).Config); Harmony val = new Harmony("bub.combatlog"); Combat.Patch(val); val.Patch((MethodBase)AccessTools.Method(typeof(ZNetScene), "Awake", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin).GetMethod("RegisterRpc", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } private static void RegisterRpc() { Net.Register(); } private void Update() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = toggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { if (Log.RecapVisible) { Log.HideRecap(); } else { widget.ToggleVisible(); } } Log.Tick(); } private void OnDestroy() { Log.Close(); } private void OnGUI() { widget.Draw(); } } internal class Entry { public Half Attack; public Half Defense; public float Created; public DateTime Time; public bool Final; public string Compact; public string Detailed; public Half Any => Defense ?? Attack; public bool Incoming { get { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { return Any.Victim == ((Character)localPlayer).GetZDOID(); } return false; } } } internal static class Log { public class Item { public string Compact; public string Detail; public bool Expanded; } private class Dot { public string Victim; public string Type; public string Source; public float Amount; public float HpStart; public float HpEnd; public float Started; public bool Incoming; } private class DotRecord { public float Time; public string Source; public float Amount; } private static readonly List<Entry> pending = new List<Entry>(); private static readonly List<Entry> finished = new List<Entry>(); public static readonly List<Item> Items = new List<Item>(); public static int Added; private static readonly Dictionary<string, Dot> dots = new Dictionary<string, Dot>(); private static readonly List<DotRecord> dotHistory = new List<DotRecord>(); private static string recap; private static float recapShownAt = -1f; private static StreamWriter file; public static bool RecapVisible { get { if (recap != null) { return Time.time - recapShownAt < 45f; } return false; } } public static void HideRecap() { recap = null; } public static string RecapText() { return recap; } public static string Color(string text, string hex) { return "<color=#" + hex + ">" + text + "</color>"; } public static bool Near(Character c) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && (Object)(object)c != (Object)null) { return Vector3.Distance(((Component)localPlayer).transform.position, ((Component)c).transform.position) <= Plugin.Instance.nearbyRange.Value; } return false; } private static string Stamp(DateTime t) { return t.ToString("HH:mm:ss"); } public static void Merge(Half h) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) Entry entry = null; float num = float.MaxValue; foreach (Entry item in pending) { Half half = (h.IsAttack ? item.Defense : item.Attack); Half half2 = (h.IsAttack ? item.Attack : item.Defense); if (half2 == null && half != null && !(half.Attacker != h.Attacker) && !(half.Victim != h.Victim)) { float num2 = Mathf.Abs(half.Key - h.Key); if (num2 < num) { num = num2; entry = item; } } } if (entry == null) { entry = new Entry(); entry.Created = Time.time; entry.Time = DateTime.Now; pending.Add(entry); } if (h.IsAttack) { entry.Attack = h; } else { entry.Defense = h; } if (entry.Attack != null && entry.Defense != null) { Finish(entry); } } public static void Tick() { for (int num = pending.Count - 1; num >= 0; num--) { if (Time.time - pending[num].Created > 1.5f) { Finish(pending[num]); } } List<string> list = null; foreach (KeyValuePair<string, Dot> dot in dots) { if (Time.time - dot.Value.Started > 3f) { if (list == null) { list = new List<string>(); } list.Add(dot.Key); } } if (list == null) { return; } foreach (string item in list) { FlushDot(item); } } private static void Finish(Entry e) { pending.Remove(e); e.Final = true; e.Compact = CompactLine(e); e.Detailed = DetailedBlock(e); finished.Add(e); if (finished.Count > 300) { finished.RemoveAt(0); } AddItem(e.Compact, e.Detailed); WriteFile(e.Detailed); } public static void Event(string text) { string text2 = Stamp(DateTime.Now) + " " + text; AddItem(text2, null); WriteFile(text2); } private static void AddItem(string compact, string detail) { Item item = new Item(); item.Compact = compact; item.Detail = detail; Items.Add(item); Added++; if (Items.Count > 300) { Items.RemoveAt(0); } } private static string CompactLine(Entry e) { Half attack = e.Attack; Half defense = e.Defense; Half any = e.Any; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(Stamp(e.Time)).Append(" "); stringBuilder.Append(Color(any.AttackerName + " → " + Who(any.VictimName, e), e.Incoming ? "ff9a7a" : "9ad0ff")).Append(": "); if (defense == null) { stringBuilder.Append("golpe ").Append(Combat.N(attack.Key)).Append(Color(" (defesa calculada em outro PC sem o mod)", "999999")); return stringBuilder.ToString(); } if (defense.Dodged) { return stringBuilder.Append(Color("IGNORADO (esquiva/invulnerável)", "8fe08f")).ToString(); } float v = defense.HpBefore - defense.HpAfter; stringBuilder.Append(Color(Combat.N(v) + " de dano", "ff7070")); if (defense.Dot > 0.5f) { stringBuilder.Append(" + ").Append(Combat.N(defense.Dot)).Append(" contínuo"); } if (defense.Blocked) { stringBuilder.Append(" | ").Append(Color((defense.Parry ? "parry " : "bloqueou ") + Combat.N(defense.Held) + " (" + defense.BlockKind + ((defense.Timed && !defense.Parry) ? ", no tempo" : "") + ")", "8fe08f")); } if (defense.GuardBroken) { stringBuilder.Append(" | ").Append(Color("GUARDA QUEBRADA", "ff5050")); } else if (defense.Staggered) { stringBuilder.Append(" | ").Append(Color("CAMBALEOU", "ffcc40")); } stringBuilder.Append(" | vida ").Append(Combat.N(defense.HpBefore)).Append("→") .Append(Combat.N(defense.HpAfter)); if (Mathf.Abs(defense.Stamina) >= 0.5f) { stringBuilder.Append(" | stamina ").Append((defense.Stamina > 0f) ? "−" : "+").Append(Combat.N(Mathf.Abs(defense.Stamina))); } stringBuilder.Append(" | stagger ").Append(Combat.N(defense.StaggerBefore)).Append("→") .Append(Combat.N(defense.StaggerAfter)) .Append("/") .Append(Combat.N(defense.StaggerMax)); if (defense.Killed) { stringBuilder.Append(" | ").Append(Color("MORREU", "ff5050")); } if (attack == null) { stringBuilder.Append(Color(" (ataque calculado em outro PC sem o mod)", "999999")); } return stringBuilder.ToString(); } private static string Who(string name, Entry e) { if (!e.Incoming) { return name; } return "você"; } private static string DetailedBlock(Entry e) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) Half attack = e.Attack; Half defense = e.Defense; Half any = e.Any; StringBuilder stringBuilder = new StringBuilder(); string text = any.AttackerName; Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && any.Attacker == ((Character)localPlayer).GetZDOID()) { text = "você"; } stringBuilder.Append(Stamp(e.Time)).Append(" ").Append(Color(text + " → " + Who(any.VictimName, e), e.Incoming ? "ff9a7a" : "9ad0ff")); if (defense != null && !defense.Dodged) { stringBuilder.Append(" — ").Append(Color(Combat.N(defense.HpBefore - defense.HpAfter) + " de dano", "ff7070")).Append(" (vida ") .Append(Combat.N(defense.HpBefore)) .Append(" → ") .Append(Combat.N(defense.HpAfter)) .Append(")"); } if (defense != null && defense.Dodged) { stringBuilder.Append(" — ").Append(Color("IGNORADO (esquiva/invulnerável)", "8fe08f")); } stringBuilder.Append("\n ").Append(Color("ATAQUE", "e0c070")); if (attack == null) { stringBuilder.Append("\n ").Append(Color("indisponível (calculado em outro PC sem o mod) — golpe chegou com " + Combat.N(any.Key), "999999")); } else { foreach (Step step in attack.Steps) { stringBuilder.Append("\n ").Append(StepLine(step)); } } stringBuilder.Append("\n ").Append(Color("DEFESA", "e0c070")); if (defense == null) { stringBuilder.Append("\n ").Append(Color("indisponível (calculado em outro PC sem o mod)", "999999")); } else { if (defense.DamageText.Length > 0) { stringBuilder.Append("\n Golpe recebido ").Append(defense.DamageText).Append(" = ") .Append(Combat.N(defense.Key)); } foreach (Step step2 in defense.Steps) { stringBuilder.Append("\n ").Append(StepLine(step2)); } if (!defense.Dodged) { stringBuilder.Append("\n = ").Append(Combat.N(defense.Instant)).Append(" na hora"); if (defense.Dot > 0.5f) { stringBuilder.Append(" · ").Append(Combat.N(defense.Dot)).Append(" contínuo"); } if (defense.Blocked && Mathf.Abs(defense.Stamina) >= 0.5f) { stringBuilder.Append(" · stamina −").Append(Combat.N(defense.Stamina)); } stringBuilder.Append(" · stagger ").Append(Combat.N(defense.StaggerBefore)).Append("→") .Append(Combat.N(defense.StaggerAfter)) .Append("/") .Append(Combat.N(defense.StaggerMax)); if (defense.GuardBroken) { stringBuilder.Append(" · ").Append(Color("GUARDA QUEBRADA", "ff5050")); } else if (defense.Staggered) { stringBuilder.Append(" · ").Append(Color("CAMBALEOU", "ffcc40")); } if (defense.Killed) { stringBuilder.Append(" · ").Append(Color("MORREU", "ff5050")); } } } return stringBuilder.ToString(); } private static string StepLine(Step s) { string text = (string.IsNullOrEmpty(s.Detail) ? "" : (" " + Color(s.Detail, "b8b8b8"))); if (Mathf.Abs(s.Before - s.After) < 0.05f) { return s.Label + text + " " + Combat.N(s.After); } string text2 = ((s.After < s.Before) ? Color(" (−" + Combat.N(s.Before - s.After) + ")", "8fe08f") : Color(" (+" + Combat.N(s.After - s.Before) + ")", "ff9a7a")); return s.Label + text + " " + Combat.N(s.Before) + " → " + Combat.N(s.After) + text2; } public static void DotTick(Character victim, HitData hit, float lost) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) string text = ((hit.m_damage.m_poison > 0f) ? "veneno" : ((hit.m_damage.m_fire > 0f) ? "fogo" : ((hit.m_damage.m_spirit > 0f) ? "espírito" : ((hit.m_damage.m_frost > 0f) ? "gelo" : "dano contínuo")))); Character attacker = hit.GetAttacker(); string key = ((object)victim.GetZDOID()/*cast due to .constrained prefix*/).ToString() + "|" + text; if (!dots.TryGetValue(key, out var value)) { value = new Dot(); value.Victim = (((Object)(object)victim == (Object)(object)Player.m_localPlayer) ? "você" : Combat.Name(victim)); value.Type = text; value.Source = (((Object)(object)attacker != (Object)null) ? Combat.Name(attacker) : text); value.HpStart = victim.GetHealth() + lost; value.Started = Time.time; value.Incoming = (Object)(object)victim == (Object)(object)Player.m_localPlayer; dots[key] = value; } value.Amount += lost; value.HpEnd = victim.GetHealth(); if (value.Incoming) { DotRecord dotRecord = new DotRecord(); dotRecord.Time = Time.time; dotRecord.Source = value.Source + " (" + text + ")"; dotRecord.Amount = lost; dotHistory.Add(dotRecord); if (dotHistory.Count > 400) { dotHistory.RemoveAt(0); } } } private static void FlushDot(string key) { Dot dot = dots[key]; dots.Remove(key); Event(Color(dot.Victim + ": " + dot.Type, dot.Incoming ? "ff9a7a" : "9ad0ff") + " " + Combat.N(dot.Amount) + " em " + Combat.N(Mathf.Max(1f, Time.time - dot.Started)) + " s (vida " + Combat.N(dot.HpStart) + "→" + Combat.N(dot.HpEnd) + ")" + ((dot.Source != dot.Type) ? (" — de " + dot.Source) : "")); } public static void Death() { for (int num = pending.Count - 1; num >= 0; num--) { Finish(pending[num]); } float value = Plugin.Instance.recapSeconds.Value; DateTime dateTime = DateTime.Now.AddSeconds(0f - value); Dictionary<string, float[]> dictionary = new Dictionary<string, float[]>(); Entry entry = null; float num2 = 0f; foreach (Entry item in finished) { if (item.Incoming && !(item.Time < dateTime) && item.Defense != null) { float num3 = Mathf.Max(0f, item.Defense.HpBefore - item.Defense.HpAfter); Add(dictionary, item.Any.AttackerName, num3, 1f); num2 += num3; entry = item; } } foreach (DotRecord item2 in dotHistory) { if (!(Time.time - item2.Time > value)) { Add(dictionary, item2.Source, item2.Amount, 0f); num2 += item2.Amount; } } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(Color("VOCÊ MORREU", "ff5050")).Append(" — ").Append(Combat.N(num2)) .Append(" de dano nos últimos ") .Append(Combat.N(value)) .Append(" s"); List<KeyValuePair<string, float[]>> list = new List<KeyValuePair<string, float[]>>(dictionary); list.Sort((KeyValuePair<string, float[]> x, KeyValuePair<string, float[]> y) => y.Value[0].CompareTo(x.Value[0])); foreach (KeyValuePair<string, float[]> item3 in list) { float v = ((num2 > 0f) ? (item3.Value[0] / num2 * 100f) : 0f); stringBuilder.Append("\n ").Append(item3.Key).Append(": ") .Append(Combat.N(item3.Value[0])) .Append(" (") .Append(Combat.N(v)) .Append("%)"); if (item3.Value[1] > 0f) { stringBuilder.Append(", ").Append(Combat.N(item3.Value[1])).Append((item3.Value[1] > 1f) ? " golpes" : " golpe"); } } if (entry != null) { stringBuilder.Append("\n\n").Append(Color("Último golpe", "e0c070")).Append("\n") .Append(entry.Detailed); } stringBuilder.Append("\n\n").Append(Color("F8 fecha", "999999")); recap = stringBuilder.ToString(); recapShownAt = Time.time; WriteFile("===== RESUMO DE MORTE =====\n" + recap + "\n==========================="); } private static void Add(Dictionary<string, float[]> d, string key, float dmg, float hits) { if (!d.TryGetValue(key, out var value)) { value = (d[key] = new float[2]); } value[0] += dmg; value[1] += hits; } private static void WriteFile(string text) { if (!Plugin.Instance.writeFile.Value) { return; } try { if (file == null) { string text2 = Path.Combine(Paths.BepInExRootPath, "CombatLog"); Directory.CreateDirectory(text2); file = new StreamWriter(Path.Combine(text2, DateTime.Now.ToString("yyyy-MM-dd") + ".log"), append: true, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); file.AutoFlush = true; } file.WriteLine(StripTags(text)); } catch (Exception ex) { Debug.LogWarning((object)("[Combat Log] não consegui gravar o arquivo: " + ex.Message)); Plugin.Instance.writeFile.Value = false; } } public static void Close() { if (file != null) { file.Close(); } } public static string Plain(string s) { return StripTags(s); } private static string StripTags(string s) { StringBuilder stringBuilder = new StringBuilder(s.Length); bool flag = false; foreach (char c in s) { switch (c) { case '<': flag = true; continue; case '>': flag = false; continue; } if (!flag) { stringBuilder.Append(c); } } return stringBuilder.ToString(); } } internal class Widget { private const float Margin = 12f; private const float BarHeight = 22f; private const float Grip = 16f; private const int MaxShown = 120; private readonly ConfigEntry<bool> visible; private readonly ConfigEntry<bool> minimized; private readonly ConfigEntry<bool> detailAll; private readonly ConfigEntry<int> width; private readonly ConfigEntry<int> height; private readonly ConfigEntry<int> fontSize; private readonly ConfigEntry<float> opacity; private float curW; private float curH; private Vector2 scroll; private bool stick = true; private int seenAdded = -1; private float contentHeight; private bool resizing; private bool textMode; private string plain; private int plainAdded = -1; private bool plainDetail; private string toast; private float toastUntil; private Vector2 resizeMouse; private Vector2 resizeSize; private GUIStyle text; private GUIStyle entry; private GUIStyle title; private GUIStyle button; private GUIStyle area; private Texture2D bg; private Texture2D bar; private Texture2D hover; private int styledFont = -1; public Widget(ConfigFile config) { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Expected O, but got Unknown visible = config.Bind<bool>("Widget", "Visible", true, "Widget shown (F8 toggles)."); minimized = config.Bind<bool>("Widget", "Minimized", false, "Only the title bar with the last line."); detailAll = config.Bind<bool>("Widget", "Detailed", false, "Show every hit detailed (otherwise click a hit to expand it)."); width = config.Bind<int>("Widget", "Width", 560, "Width in pixels (drag the top-left corner to resize)."); height = config.Bind<int>("Widget", "Height", 260, "Height in pixels."); fontSize = config.Bind<int>("Widget", "Font size", 13, new ConfigDescription("Text size.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(9, 24), new object[0])); opacity = config.Bind<float>("Widget", "Background opacity", 0.6f, new ConfigDescription("0 = transparent, 1 = solid.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0])); curW = width.Value; curH = height.Value; } public void ToggleVisible() { visible.Value = !visible.Value; } private void Init() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Expected O, but got Unknown //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Expected O, but got Unknown //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Expected O, but got Unknown if (text == null || styledFont != fontSize.Value) { styledFont = fontSize.Value; text = new GUIStyle(GUI.skin.label); text.richText = true; text.wordWrap = true; text.fontSize = fontSize.Value; text.margin = new RectOffset(2, 2, 1, 1); text.padding = new RectOffset(4, 4, 1, 1); text.normal.textColor = new Color(0.92f, 0.9f, 0.85f); hover = Solid(new Color(1f, 1f, 1f, 0.08f)); entry = new GUIStyle(text); entry.hover.background = hover; entry.hover.textColor = Color.white; entry.active.background = hover; entry.active.textColor = Color.white; title = new GUIStyle(text); title.wordWrap = false; title.clipping = (TextClipping)1; title.fontStyle = (FontStyle)1; title.alignment = (TextAnchor)3; area = new GUIStyle(GUI.skin.textArea); area.richText = false; area.wordWrap = true; area.fontSize = fontSize.Value; area.normal.background = null; area.focused.background = null; area.hover.background = null; area.normal.textColor = new Color(0.92f, 0.9f, 0.85f); area.focused.textColor = area.normal.textColor; area.hover.textColor = area.normal.textColor; button = new GUIStyle(GUI.skin.button); button.fontSize = Mathf.Max(10, fontSize.Value - 2); button.padding = new RectOffset(4, 4, 1, 1); } } private static Texture2D Solid(Color c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_000b: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, c); val.Apply(); return val; } private void Backgrounds() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)bg == (Object)null || !Mathf.Approximately(bg.GetPixel(0, 0).a, opacity.Value)) { bg = Solid(new Color(0.03f, 0.03f, 0.03f, opacity.Value)); bar = Solid(new Color(0.12f, 0.1f, 0.07f, Mathf.Min(1f, opacity.Value + 0.2f))); } } public void Draw() { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_039a: Unknown result type (might be due to invalid IL or missing references) //IL_0438: Unknown result type (might be due to invalid IL or missing references) //IL_03e0: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Game.instance == (Object)null) { return; } Init(); Backgrounds(); if (Log.RecapVisible) { DrawRecap(); } if (!visible.Value) { return; } bool flag = Cursor.visible; float num = Mathf.Clamp(curW, 280f, (float)Screen.width - 24f); float num2 = (minimized.Value ? 22f : Mathf.Clamp(curH, 82f, (float)Screen.height - 24f)); Rect val = default(Rect); ((Rect)(ref val))..ctor((float)Screen.width - num - 12f, (float)Screen.height - num2 - 12f, num, num2); if (flag && !minimized.Value) { HandleResize(val); } GUI.DrawTexture(val, (Texture)(object)bg); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).x, ((Rect)(ref val)).y, num, 22f); GUI.DrawTexture(val2, (Texture)(object)bar); float num3 = ((Rect)(ref val)).xMax - 4f; if (flag) { num3 -= 24f; if (GUI.Button(new Rect(num3, ((Rect)(ref val)).y + 2f, 22f, 18f), minimized.Value ? "▴" : "▾", button)) { minimized.Value = !minimized.Value; } if (!minimized.Value) { num3 -= 82f; if (GUI.Button(new Rect(num3, ((Rect)(ref val)).y + 2f, 80f, 18f), detailAll.Value ? "Detalhado" : "Lista", button)) { detailAll.Value = !detailAll.Value; } num3 -= 58f; if (GUI.Button(new Rect(num3, ((Rect)(ref val)).y + 2f, 56f, 18f), textMode ? "Normal" : "Texto", button)) { textMode = !textMode; } num3 -= 62f; if (GUI.Button(new Rect(num3, ((Rect)(ref val)).y + 2f, 60f, 18f), "Copiar", button)) { Copy(PlainText(), "log copiado"); } } } string text = "Combat Log"; if (toast != null && Time.unscaledTime < toastUntil) { text = text + " <color=#8fe08f>" + toast + "</color>"; } else if (minimized.Value && Log.Items.Count > 0) { text = text + " " + Log.Items[Log.Items.Count - 1].Compact; } else if (!flag) { object obj = text; text = string.Concat(obj, " <color=#8a8a8a><size=", Mathf.Max(9, fontSize.Value - 2), ">Tab libera o mouse</size></color>"); } float num4 = ((Rect)(ref val)).x + ((flag && !minimized.Value) ? 20f : 6f); GUI.Label(new Rect(num4, ((Rect)(ref val)).y, num3 - num4 - 4f, 22f), text, title); if (flag && !minimized.Value) { GUI.Label(new Rect(((Rect)(ref val)).x + 1f, ((Rect)(ref val)).y + 1f, 16f, 16f), "<color=#c8a860>◤</color>", this.text); } if (!minimized.Value) { DrawBody(new Rect(((Rect)(ref val)).x + 2f, ((Rect)(ref val)).y + 22f + 2f, num - 4f, num2 - 22f - 4f), flag); } } private void HandleResize(Rect r) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Invalid comparison between Unknown and I4 //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Invalid comparison between Unknown and I4 //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) Event current = Event.current; Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref r)).x, ((Rect)(ref r)).y, 20f, 20f); if ((int)current.type == 0 && current.button == 0 && ((Rect)(ref val)).Contains(current.mousePosition)) { resizing = true; resizeMouse = current.mousePosition; resizeSize = new Vector2(((Rect)(ref r)).width, ((Rect)(ref r)).height); current.Use(); } else if (resizing && (int)current.type == 3) { curW = Mathf.Clamp(resizeSize.x + (resizeMouse.x - current.mousePosition.x), 280f, (float)Screen.width - 24f); curH = Mathf.Clamp(resizeSize.y + (resizeMouse.y - current.mousePosition.y), 82f, (float)Screen.height - 24f); current.Use(); } else if (resizing && (int)current.rawType == 1) { resizing = false; width.Value = Mathf.RoundToInt(curW); height.Value = Mathf.RoundToInt(curH); current.Use(); } } private void DrawBody(Rect body, bool interactive) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Invalid comparison between Unknown and I4 //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Invalid comparison between Unknown and I4 //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) if (Log.Added != seenAdded) { seenAdded = Log.Added; if (stick) { scroll.y = float.MaxValue; } } GUILayout.BeginArea(body); scroll = GUILayout.BeginScrollView(scroll, false, false, GUIStyle.none, GUI.skin.verticalScrollbar, (GUILayoutOption[])(object)new GUILayoutOption[0]); if (textMode) { GUILayout.TextArea(PlainText(), area, (GUILayoutOption[])(object)new GUILayoutOption[0]); } else if (Log.Items.Count == 0) { GUILayout.Label("<color=#8a8a8a>Nenhum golpe ainda.</color>", this.text, (GUILayoutOption[])(object)new GUILayoutOption[0]); } int num = Mathf.Max(0, Log.Items.Count - 120); int num2 = num; while (!textMode && num2 < Log.Items.Count) { Log.Item item = Log.Items[num2]; bool flag = item.Detail != null && (detailAll.Value || item.Expanded); string text = (flag ? item.Detail : item.Compact); if (interactive && item.Detail != null && !detailAll.Value) { if (GUILayout.Button(text, entry, (GUILayoutOption[])(object)new GUILayoutOption[0])) { if (Event.current.button == 1) { Copy(Log.Plain(item.Detail), "golpe copiado"); } else { item.Expanded = !item.Expanded; } } } else { GUILayout.Label(text, this.text, (GUILayoutOption[])(object)new GUILayoutOption[0]); } if (flag) { GUILayout.Space(4f); } num2++; } GUILayout.Label("", GUIStyle.none, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(0f) }); if ((int)Event.current.type == 7) { Rect lastRect = GUILayoutUtility.GetLastRect(); contentHeight = ((Rect)(ref lastRect)).yMax; } GUILayout.EndScrollView(); GUILayout.EndArea(); if ((int)Event.current.type == 7) { stick = scroll.y >= contentHeight - ((Rect)(ref body)).height - 8f; } } private string PlainText() { if (plain != null && plainAdded == Log.Added && plainDetail == detailAll.Value) { return plain; } StringBuilder stringBuilder = new StringBuilder(); int num = Mathf.Max(0, Log.Items.Count - 120); for (int i = num; i < Log.Items.Count; i++) { Log.Item item = Log.Items[i]; bool flag = item.Detail != null && (detailAll.Value || item.Expanded); stringBuilder.Append(Log.Plain(flag ? item.Detail : item.Compact)).AppendLine(); if (flag) { stringBuilder.AppendLine(); } } plain = stringBuilder.ToString(); plainAdded = Log.Added; plainDetail = detailAll.Value; return plain; } private void Copy(string s, string what) { GUIUtility.systemCopyBuffer = s; toast = what; toastUntil = Time.unscaledTime + 2f; } private void DrawRecap() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) string text = Log.RecapText(); float num = Mathf.Min(680f, (float)Screen.width - 24f); GUIContent val = new GUIContent(text); float num2 = this.text.CalcHeight(val, num - 16f) + 12f; Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((float)Screen.width - num) * 0.5f, (float)Screen.height * 0.1f, num, num2); GUI.DrawTexture(val2, (Texture)(object)bar); GUI.Label(new Rect(((Rect)(ref val2)).x + 8f, ((Rect)(ref val2)).y + 6f, num - 16f, num2), val, this.text); } }