using Microsoft.Xna.Framework; using System; using System.IO; using Terraria; using Terraria.ID; namespace SoulHarvest.Common; public static class ReaperCombatRegistry { // Death is intentionally presented at a boss-weapon scale. This value only // scales the weapon sprite; it must never be reused as an attack-range // multiplier. public const float DeathWeaponDrawScale = 2.58f; public const float DeathPrimaryMaximumTempo = 5f; public const float CrescentOuterRadiusRatio = 0.91f; public const float StandardCrescentThicknessRatio = 0.455f; public const float DeathPrimaryHalfSweep = 2.43f; // The 100x160 Death sprite reaches roughly 406 world pixels from its handle // at the presentation scale above. A 447px crescent places its 91% hot rim // on that cutting edge while remaining close to the original 430px attack // presentation instead of inheriting the weapon's +50% scale. public const float DeathPrimaryCrescentRadius = 447f; public const float DeathPrimaryBladeReach = DeathPrimaryCrescentRadius * CrescentOuterRadiusRatio; public const float DeathPrimaryCollisionReach = 430f; public static float GetDeathWeaponDrawScale(in SickleCombatSnapshot snapshot) => DeathWeaponDrawScale * snapshot.DeathRangeMultiplier; public static float GetDeathPrimaryCrescentRadius(in SickleCombatSnapshot snapshot) => DeathPrimaryCrescentRadius * snapshot.DeathRangeMultiplier; public static float GetDeathPrimaryBladeReach(in SickleCombatSnapshot snapshot) => DeathPrimaryBladeReach * snapshot.DeathRangeMultiplier; public static float GetDeathPrimaryCollisionReach(in SickleCombatSnapshot snapshot) => DeathPrimaryCollisionReach * snapshot.DeathRangeMultiplier; public static ReaperFormId ResolveUsableForm(ReaperProgressionState progression) { ReaperFormId form = progression.CurrentForm; if (form == ReaperFormId.Death) return progression.DeathFormUnlocked ? form : ReaperFormId.Base; if (ReaperDefinitions.IsBranchForm(form)) return progression.GetStage(form) != ReaperStage.Locked ? form : ReaperFormId.Base; return ReaperFormId.Base; } public static int GetBaseDamage(ReaperFormId form, ReaperStage stage) { return ReaperDefinitions.GetForm(form).GetBaseDamage(form is ReaperFormId.Base or ReaperFormId.Death ? ReaperStage.StageI : stage); } public static int GetUseTime(ReaperFormId form, ReaperStage stage, int phase = 0) { if (form == ReaperFormId.Death) // Every primary beat shares one clock. Phase-dependent use times made // auto-reuse alternately wait for the held projectile and then race it, // which was perceived as random attack frequency at high tempo. return 24; return ReaperDefinitions.GetForm(form).BaseUseTime; } public static int GetPrimaryPhaseCount(ReaperFormId form, ReaperStage stage) => form switch { ReaperFormId.Base => 2, ReaperFormId.Bone => 3, ReaperFormId.Blood => stage >= ReaperStage.StageII ? 3 : 2, ReaperFormId.Infernal => stage >= ReaperStage.StageIII ? 4 : 3, ReaperFormId.Frost => 3, ReaperFormId.Soul => 3, ReaperFormId.Void => stage >= ReaperStage.StageII ? 4 : 2, ReaperFormId.Death => 6, _ => 2 }; public static float GetPrimaryDamageMultiplier(in SickleCombatSnapshot snapshot, int phase) => snapshot.Form switch { ReaperFormId.Base => phase == 0 ? 1f : 1.15f, ReaperFormId.Bone => phase switch { 0 => 0.9f, 1 => 1f, _ => 1.25f }, ReaperFormId.Blood => phase switch { 0 => 1f, 1 => 1.1f, _ => 1.35f }, ReaperFormId.Infernal => snapshot.Stage >= ReaperStage.StageIII ? phase switch { 0 => 0.95f, 1 => 1.05f, 2 => 1.15f, _ => 1.55f } : phase switch { 0 => 0.95f, 1 => 1.05f, _ => snapshot.Stage >= ReaperStage.StageII ? 1.35f : 1.25f }, ReaperFormId.Frost => phase switch { 0 => 0.95f, 1 => 1f, _ => 0.62f }, ReaperFormId.Soul => phase switch { 0 => 0.95f, 1 => 1f, _ => 1.15f }, ReaperFormId.Void => phase switch { 0 => 1.15f, 1 => 1.2f, _ => 0.8f }, ReaperFormId.Death => phase switch { 0 => 1.1f, 1 => 1.05f, 2 => 1.15f, 3 => 0.6f, 4 => 1.05f, _ => 1.65f }, _ => 1f }; public static int GetSpecialCooldown(ReaperFormId form, ReaperStage stage) => form switch { ReaperFormId.Base => 270, ReaperFormId.Bone => stage switch { ReaperStage.StageI => 270, ReaperStage.StageII => 300, _ => 330 }, ReaperFormId.Blood => stage >= ReaperStage.StageII ? 420 : 360, ReaperFormId.Infernal => stage switch { ReaperStage.StageI => 270, ReaperStage.StageII => 330, _ => 420 }, ReaperFormId.Frost => 270, ReaperFormId.Soul => stage switch { ReaperStage.StageI => 300, ReaperStage.StageII => 330, _ => 360 }, ReaperFormId.Void => stage switch { ReaperStage.StageI => 330, ReaperStage.StageII => 360, _ => 420 }, ReaperFormId.Death => 480, _ => 300 }; public static int GetUltimateCooldown(ReaperFormId form) => form switch { ReaperFormId.Bone => 720, ReaperFormId.Blood => 900, ReaperFormId.Infernal => 840, ReaperFormId.Frost => 840, ReaperFormId.Soul => 900, ReaperFormId.Void => 960, ReaperFormId.Death => 1200, _ => 0 }; public static Color GetPrimaryColor(ReaperFormId form) => form switch { ReaperFormId.Bone => new Color(80, 235, 255), ReaperFormId.Blood => new Color(235, 45, 80), ReaperFormId.Infernal => new Color(255, 125, 25), ReaperFormId.Frost => new Color(105, 195, 255), ReaperFormId.Soul => new Color(120, 100, 255), ReaperFormId.Void => new Color(190, 55, 255), ReaperFormId.Death => new Color(235, 35, 65), _ => new Color(80, 205, 225) }; public static Color GetSecondaryColor(ReaperFormId form) => form switch { ReaperFormId.Bone => new Color(235, 245, 220), ReaperFormId.Blood => new Color(255, 145, 175), ReaperFormId.Infernal => new Color(255, 235, 85), ReaperFormId.Frost => new Color(235, 255, 255), ReaperFormId.Soul => new Color(75, 245, 255), ReaperFormId.Void => new Color(255, 85, 235), ReaperFormId.Death => new Color(255, 145, 175), _ => new Color(210, 250, 255) }; public static int GetDust(ReaperFormId form) => form switch { ReaperFormId.Bone => DustID.DungeonSpirit, ReaperFormId.Blood => DustID.Blood, ReaperFormId.Infernal => DustID.Torch, ReaperFormId.Frost => DustID.IceTorch, ReaperFormId.Soul => DustID.AncientLight, ReaperFormId.Void or ReaperFormId.Death => DustID.Shadowflame, _ => DustID.AncientLight }; /// /// Resolves the immutable weapon artwork captured by an action. Branch forms /// use one texture per progression stage; base and Death retain their fixed /// silhouettes. /// public static string GetTexturePath(ReaperFormId form, ReaperStage stage) { stage = NormalizePresentationStage(form, stage); return (form, stage) switch { (ReaperFormId.Bone, ReaperStage.StageI) => "SoulHarvest/Items/ReaperStages/BoneSickle_StageI", (ReaperFormId.Bone, ReaperStage.StageII) => "SoulHarvest/Items/ReaperStages/BoneSickle_StageII", (ReaperFormId.Bone, _) => "SoulHarvest/Items/ReaperStages/BoneSickle_StageIII", (ReaperFormId.Blood, ReaperStage.StageI) => "SoulHarvest/Items/ReaperStages/BloodSickle_StageI", (ReaperFormId.Blood, ReaperStage.StageII) => "SoulHarvest/Items/ReaperStages/BloodSickle_StageII", (ReaperFormId.Blood, _) => "SoulHarvest/Items/ReaperStages/BloodSickle_StageIII", (ReaperFormId.Infernal, ReaperStage.StageI) => "SoulHarvest/Items/ReaperStages/InfernalSickle_StageI", (ReaperFormId.Infernal, ReaperStage.StageII) => "SoulHarvest/Items/ReaperStages/InfernalSickle_StageII", (ReaperFormId.Infernal, _) => "SoulHarvest/Items/ReaperStages/InfernalSickle_StageIII", (ReaperFormId.Frost, ReaperStage.StageI) => "SoulHarvest/Items/ReaperStages/FrostSickle_StageI", (ReaperFormId.Frost, ReaperStage.StageII) => "SoulHarvest/Items/ReaperStages/FrostSickle_StageII", (ReaperFormId.Frost, _) => "SoulHarvest/Items/ReaperStages/FrostSickle_StageIII", (ReaperFormId.Soul, ReaperStage.StageI) => "SoulHarvest/Items/ReaperStages/SoulSickle_StageI", (ReaperFormId.Soul, ReaperStage.StageII) => "SoulHarvest/Items/ReaperStages/SoulSickle_StageII", (ReaperFormId.Soul, _) => "SoulHarvest/Items/ReaperStages/SoulSickle_StageIII", (ReaperFormId.Void, ReaperStage.StageI) => "SoulHarvest/Items/ReaperStages/VoidSickle_StageI", (ReaperFormId.Void, ReaperStage.StageII) => "SoulHarvest/Items/ReaperStages/VoidSickle_StageII", (ReaperFormId.Void, _) => "SoulHarvest/Items/ReaperStages/VoidSickle_StageIII", (ReaperFormId.Death, _) => "SoulHarvest/Items/DeathSickle", _ => "SoulHarvest/Items/NormalSickle" }; } public static Vector2 GetHandleAnchor(ReaperFormId form, ReaperStage stage) { _ = NormalizePresentationStage(form, stage); return form switch { ReaperFormId.Bone => new Vector2(0.25f, 0.94f), ReaperFormId.Blood => new Vector2(0.13f, 0.94f), ReaperFormId.Infernal or ReaperFormId.Void => new Vector2(0.13f, 0.93f), ReaperFormId.Frost => new Vector2(0.18f, 0.93f), ReaperFormId.Soul => new Vector2(0.20f, 0.93f), ReaperFormId.Death => new Vector2(0.08f, 0.94f), _ => new Vector2(0.06f, 0.95f) }; } public static float GetTextureRotationCorrection(ReaperFormId form, ReaperStage stage) { _ = NormalizePresentationStage(form, stage); return form switch { ReaperFormId.Bone => 1.046f, ReaperFormId.Blood => 0.824f, ReaperFormId.Infernal => 0.695f, ReaperFormId.Frost => 0.904f, ReaperFormId.Soul => 0.984f, ReaperFormId.Void => 0.846f, ReaperFormId.Death => 0.72f, _ => 0.98f }; } public static float GetBladeTipLength(ReaperFormId form, ReaperStage stage) { _ = NormalizePresentationStage(form, stage); return form switch { ReaperFormId.Bone => 129.7f, ReaperFormId.Blood => 131.6f, ReaperFormId.Infernal => 119f, ReaperFormId.Frost => 112.2f, ReaperFormId.Soul => 124.6f, ReaperFormId.Void => 137.8f, ReaperFormId.Death => 230f, _ => 157.4f }; } private static ReaperStage NormalizePresentationStage(ReaperFormId form, ReaperStage stage) { if (form == ReaperFormId.Death) return ReaperStage.StageIII; if (!ReaperDefinitions.IsBranchForm(form)) return ReaperStage.StageI; return stage switch { ReaperStage.StageII => ReaperStage.StageII, ReaperStage.StageIII => ReaperStage.StageIII, _ => ReaperStage.StageI }; } public static float GetReach(in SickleCombatSnapshot snapshot) { if (snapshot.Form == ReaperFormId.Void) return GetBladeTipLength(snapshot.Form, snapshot.Stage) * (snapshot.Skill1 switch { 2 => 1.08f, >= 3 => 1.14f, _ => 1.03f }); return snapshot.Form == ReaperFormId.Death ? 190f : 112f + snapshot.StageNumber * 10f; } public static float GetCollisionWidth(in SickleCombatSnapshot snapshot) { if (snapshot.Form == ReaperFormId.Void) return snapshot.Skill1 switch { 1 => 18f, 2 => 22f, >= 3 => 26f, _ => 18f }; return 18f + snapshot.StageNumber * 3f; } }