diff --git a/src/TerraSharp/Core/Settings.Json.cs b/src/TerraSharp/Core/Settings.Json.cs index 61888a0..4a6a8a3 100644 --- a/src/TerraSharp/Core/Settings.Json.cs +++ b/src/TerraSharp/Core/Settings.Json.cs @@ -98,6 +98,15 @@ public static partial class Settings [nameof(EspWaypoints)] = "Esp.Waypoints.Enabled", [nameof(ColWaypoint)] = "Esp.Waypoints.Color", + [nameof(DamageMelee)] = "DamageNumbers.Melee.Enabled", + [nameof(ColDamageMelee)] = "DamageNumbers.Melee.Color", + [nameof(DamageRanged)] = "DamageNumbers.Ranged.Enabled", + [nameof(ColDamageRanged)] = "DamageNumbers.Ranged.Color", + [nameof(DamageMagic)] = "DamageNumbers.Magic.Enabled", + [nameof(ColDamageMagic)] = "DamageNumbers.Magic.Color", + [nameof(DamageSummon)] = "DamageNumbers.Summon.Enabled", + [nameof(ColDamageSummon)] = "DamageNumbers.Summon.Color", + [nameof(Spelunker)] = "Vision.Spelunker", [nameof(Hunter)] = "Vision.Hunter", [nameof(Shine)] = "Vision.Shine", diff --git a/src/TerraSharp/Core/Settings.cs b/src/TerraSharp/Core/Settings.cs index f8bfa7d..b002aba 100644 --- a/src/TerraSharp/Core/Settings.cs +++ b/src/TerraSharp/Core/Settings.cs @@ -46,6 +46,8 @@ public static partial class Settings public static EspBoxStyle EspStyle; public static float EspThickness = 1f, EspMaxDistance; // tiles, 0 = unlimited public static Color ColLocal = Color.White, ColPlayers = new(70, 200, 255), ColHostile = new(235, 60, 60), ColCritters = new(200, 170, 120), ColTown = new(80, 220, 110), ColBoss = new(255, 120, 40), ColItems = new(255, 210, 70), ColProjHostile = new(255, 80, 200), ColProjFriendly = new(120, 160, 255), ColWaypoint = new(220, 90, 255); + public static bool DamageMelee, DamageRanged, DamageMagic, DamageSummon; + public static Color ColDamageMelee = new(255, 110, 60), ColDamageRanged = new(120, 230, 90), ColDamageMagic = new(90, 150, 255), ColDamageSummon = new(200, 110, 255); public static bool Spelunker, Hunter, Shine, NightOwl, DangerSense, BiomeSight; public static bool FreezeTime, SpawnRateBoost; diff --git a/src/TerraSharp/Features/DamageNumbers.cs b/src/TerraSharp/Features/DamageNumbers.cs new file mode 100644 index 0000000..d09932f --- /dev/null +++ b/src/TerraSharp/Features/DamageNumbers.cs @@ -0,0 +1,76 @@ +using Microsoft.Xna.Framework; +using Terraria; +using Terraria.ID; +using TerraSharp.Core; +#if TMODLOADER +using Terraria.ModLoader; +#endif + +namespace TerraSharp.Features; + +public enum DamageKind { None, Melee, Ranged, Magic, Summon } + +// Hit paths set Current around StrikeNPC; CombatText.NewText reads it. +public static class DamageNumbers +{ + public static DamageKind Current; + + public static Color? Recolor(Color color) + { + bool crit = color == CombatText.DamagedHostileCrit; + if (!crit && color != CombatText.DamagedHostile) + return null; + + (bool enabled, Color chosen) = Current switch + { + DamageKind.Melee => (Settings.DamageMelee, Settings.ColDamageMelee), + DamageKind.Ranged => (Settings.DamageRanged, Settings.ColDamageRanged), + DamageKind.Magic => (Settings.DamageMagic, Settings.ColDamageMagic), + DamageKind.Summon => (Settings.DamageSummon, Settings.ColDamageSummon), + _ => (false, default), + }; + if (!enabled) + return null; + + var opaque = new Color(chosen.R, chosen.G, chosen.B); + return crit ? Color.Lerp(opaque, Color.White, 0.4f) : opaque; + } + +#if TMODLOADER + // Whips count as melee too, so summon is checked first. + public static DamageKind Of(DamageClass? damageClass) + { + if (damageClass is null) + return DamageKind.None; + if (damageClass.CountsAsClass(DamageClass.Summon)) + return DamageKind.Summon; + if (damageClass.CountsAsClass(DamageClass.Magic)) + return DamageKind.Magic; + if (damageClass.CountsAsClass(DamageClass.Ranged)) + return DamageKind.Ranged; + return damageClass.CountsAsClass(DamageClass.Melee) ? DamageKind.Melee : DamageKind.None; + } +#else + public static DamageKind Of(Projectile projectile) + { + if (projectile.minion || projectile.sentry || ProjectileID.Sets.IsAWhip[projectile.type] || ProjectileID.Sets.MinionShot[projectile.type] || ProjectileID.Sets.SentryShot[projectile.type]) + return DamageKind.Summon; + if (projectile.magic) + return DamageKind.Magic; + if (projectile.ranged) + return DamageKind.Ranged; + return projectile.melee ? DamageKind.Melee : DamageKind.None; + } + + public static DamageKind Of(Item item) + { + if (item.summon || item.sentry) + return DamageKind.Summon; + if (item.magic) + return DamageKind.Magic; + if (item.ranged) + return DamageKind.Ranged; + return item.melee ? DamageKind.Melee : DamageKind.None; + } +#endif +} diff --git a/src/TerraSharp/Patches/DamageNumberPatches.cs b/src/TerraSharp/Patches/DamageNumberPatches.cs new file mode 100644 index 0000000..fc28c8a --- /dev/null +++ b/src/TerraSharp/Patches/DamageNumberPatches.cs @@ -0,0 +1,103 @@ +using System; +using Microsoft.Xna.Framework; +using Terraria; +using TerraSharp.Core; +using TerraSharp.Features; +#if !TMODLOADER +using Terraria.GameContent; +using Terraria.ID; +#endif + +namespace TerraSharp.Patches; + +internal static class DamageNumberPatches +{ + delegate int NewTextOriginal(Rectangle location, Color color, string text, bool dramatic, bool dot); + + internal static void Install(HookRegistry hooks) + { + hooks.Add(HookRegistry.Method(typeof(CombatText), nameof(CombatText.NewText), [typeof(Rectangle), typeof(Color), typeof(string), typeof(bool), typeof(bool)]), (Func)NewText); +#if TMODLOADER + hooks.Add(HookRegistry.Method(typeof(NPC), nameof(NPC.StrikeNPC), [typeof(NPC.HitInfo), typeof(bool), typeof(bool)]), (Func)Strike); +#else + hooks.Add(typeof(Projectile), nameof(Projectile.Damage), (Action)ProjectileDamage); + hooks.Add(typeof(Player), "ProcessHitAgainstNPC", (Action)MeleeHit); + hooks.Add(HookRegistry.Method(typeof(Player), nameof(Player.ApplyDamageToNPC), [typeof(NPC), typeof(int), typeof(float), typeof(int), typeof(bool), typeof(PlayerNPCHitSource)]), (Action)ApplyDamage); +#endif + } + + static int NewText(NewTextOriginal original, Rectangle location, Color color, string text, bool dramatic, bool dot) + { + if (Plugin.Active && DamageNumbers.Current != DamageKind.None && DamageNumbers.Recolor(color) is { } recolored) + color = recolored; + return original(location, color, text, dramatic, dot); + } + +#if TMODLOADER + delegate int StrikeOriginal(NPC self, NPC.HitInfo hit, bool fromNet, bool noPlayerInteraction); + + static int Strike(StrikeOriginal original, NPC self, NPC.HitInfo hit, bool fromNet, bool noPlayerInteraction) + { + DamageKind previous = DamageNumbers.Current; + DamageNumbers.Current = DamageNumbers.Of(hit.DamageType); + try + { + return original(self, hit, fromNet, noPlayerInteraction); + } + finally + { + DamageNumbers.Current = previous; + } + } +#else + delegate void ProjectileDamageOriginal(Projectile self); + delegate void MeleeHitOriginal(Player self, Item item, Rectangle itemRectangle, int originalDamage, float knockBack, int npcIndex); + delegate void ApplyDamageOriginal(Player self, NPC npc, int damage, float knockback, int direction, bool crit, PlayerNPCHitSource source); + + static void ProjectileDamage(ProjectileDamageOriginal original, Projectile self) + { + DamageKind previous = DamageNumbers.Current; + DamageNumbers.Current = DamageNumbers.Of(self); + try + { + original(self); + } + finally + { + DamageNumbers.Current = previous; + } + } + + static void MeleeHit(MeleeHitOriginal original, Player self, Item item, Rectangle itemRectangle, int originalDamage, float knockBack, int npcIndex) + { + DamageKind previous = DamageNumbers.Current; + DamageNumbers.Current = DamageNumbers.Of(item); + try + { + original(self, item, itemRectangle, originalDamage, knockBack, npcIndex); + } + finally + { + DamageNumbers.Current = previous; + } + } + + // Dashes, buffs and contact damage pass the item or projectile that caused the hit. + static void ApplyDamage(ApplyDamageOriginal original, Player self, NPC npc, int damage, float knockback, int direction, bool crit, PlayerNPCHitSource source) + { + DamageKind previous = DamageNumbers.Current; + if (source.ProjType > 0 && ContentSamples.ProjectilesByType.TryGetValue(source.ProjType, out Projectile? projectile)) + DamageNumbers.Current = DamageNumbers.Of(projectile); + else if (source.ItemType > 0 && ContentSamples.ItemsByType.TryGetValue(source.ItemType, out Item? item)) + DamageNumbers.Current = DamageNumbers.Of(item); + try + { + original(self, npc, damage, knockback, direction, crit, source); + } + finally + { + DamageNumbers.Current = previous; + } + } +#endif +} diff --git a/src/TerraSharp/Patches/HookRegistry.cs b/src/TerraSharp/Patches/HookRegistry.cs index 7ca93ae..0e3c3dc 100644 --- a/src/TerraSharp/Patches/HookRegistry.cs +++ b/src/TerraSharp/Patches/HookRegistry.cs @@ -21,6 +21,7 @@ internal sealed class HookRegistry : IDisposable MainPatches.Install(hooks); PlayerPatches.Install(hooks); NpcPatches.Install(hooks); + DamageNumberPatches.Install(hooks); SpawnRatePatch.Install(hooks); ItemCheckShootPatch.Install(hooks); SandboxGuards.Install(hooks); diff --git a/src/TerraSharp/UI/MainWindow.Pages.cs b/src/TerraSharp/UI/MainWindow.Pages.cs index 89aeb28..908dff0 100644 --- a/src/TerraSharp/UI/MainWindow.Pages.cs +++ b/src/TerraSharp/UI/MainWindow.Pages.cs @@ -175,6 +175,13 @@ public sealed partial class MainWindow Toggle("Night Owl", ref Settings.NightOwl); Toggle("Shine", ref Settings.Shine); Toggle("Spelunker", ref Settings.Spelunker); + + Group("Damage Numbers"); + Note("Colors your hits by damage class. Crits are lighter."); + ToggleColor("Magic", ref Settings.DamageMagic, ref Settings.ColDamageMagic, nameof(Settings.ColDamageMagic)); + ToggleColor("Melee", ref Settings.DamageMelee, ref Settings.ColDamageMelee, nameof(Settings.ColDamageMelee)); + ToggleColor("Ranged", ref Settings.DamageRanged, ref Settings.ColDamageRanged, nameof(Settings.ColDamageRanged)); + ToggleColor("Summon", ref Settings.DamageSummon, ref Settings.ColDamageSummon, nameof(Settings.ColDamageSummon)); } void WorldPage()