using SoulHarvest.Common; using SoulHarvest.Tiles; using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.IO; using Terraria; using Terraria.ID; using Terraria.Localization; using Terraria.ModLoader; using Terraria.ModLoader.IO; namespace SoulHarvest.Items; public class DeathNecklace : ModItem, IDeathAltarUpgradeable { public const int MaxCoreLevel = 10; public const int MaxAbsorbAllLevel = 10; public const float RadiusGrowthPerLevel = 42f; private static readonly DeathAltarUpgradeType[] UpgradeTypes = [ DeathAltarUpgradeType.NecklaceRadius, DeathAltarUpgradeType.NecklaceAbsorbAll, DeathAltarUpgradeType.NecklaceSovereignty ]; public int RadiusLevel { get; private set; } = 1; public int AbsorbAllLevel { get; private set; } = 1; public bool SovereigntyUnlocked { get; private set; } // Serialized only so existing necklace items can migrate their combat tree // into the new character-owned DeathDomainProgression. internal int LegacyFrequencyLevel { get; private set; } = 1; internal int LegacyDamageLevel { get; private set; } = 1; internal int LegacyVolleyLevel { get; private set; } = 1; internal int LegacyLifeStealLevel { get; private set; } public float DomainRadius => 160f + (RadiusLevel - 1) * RadiusGrowthPerLevel; public float DomainAttractionRadius => DomainRadius + 320f + (AbsorbAllLevel - 1) * 32f; public float DomainAttractionStrength => 0.035f + (AbsorbAllLevel - 1) * 0.0075f; public float DomainAttractionMaximumSpeed => 0.75f + (AbsorbAllLevel - 1) * 0.09f; public float DomainRepulsionStrength => 0.055f + (AbsorbAllLevel - 1) * 0.01f; public float DomainRepulsionMaximumSpeed => 1.1f + (AbsorbAllLevel - 1) * 0.12f; // Reaching the radius cap only maximizes the finite field. Full-screen // coverage is a runtime state granted by the final Death Descends node. public bool DeathDescentUnlocked => SovereigntyUnlocked && AllCoreStatsMaxed; public bool DeathRequiemUnlocked => DeathDescentUnlocked; public int MasteryScore => RadiusLevel + AbsorbAllLevel + (SovereigntyUnlocked ? 30 : 0); public IReadOnlyList AltarUpgradeTypes => UpgradeTypes; protected override bool CloneNewInstances => true; public override void PreReforge() { DeathAltarReforgePreservation.Capture(this); } public override void PostReforge() { DeathAltarReforgePreservation.Restore(this); } public override void SetDefaults() { Item.width = 30; Item.height = 38; Item.accessory = true; Item.value = Item.sellPrice(gold: 8); Item.rare = ItemRarityID.LightRed; } public override void UpdateAccessory(Player player, bool hideVisual) { MyPlayer modPlayer = player.GetModPlayer(); modPlayer.MergeLegacyDeathNecklaceProgression(this); modPlayer.SetDeathNecklace(this); if (!Main.dedServ && SovereigntyUnlocked && Main.rand.NextBool(10)) { Dust soul = Dust.NewDustPerfect( player.Center + Main.rand.NextVector2Circular(24f, 34f), DustID.DungeonSpirit, -player.velocity * 0.04f, 70, new Color(105, 225, 245), 0.65f); soul.noGravity = true; } } public override void UpdateInventory(Player player) { player.GetModPlayer().MergeLegacyDeathNecklaceProgression(this); } public override void ModifyTooltips(List tooltips) { string key = DeathDescentUnlocked ? "Mods.SoulHarvest.UI.Enabled" : "Mods.SoulHarvest.UI.Disabled"; tooltips.Add(new TooltipLine(Mod, "DeathDomainStats", Language.GetTextValue( "Mods.SoulHarvest.UI.NecklaceStatsV2", DomainRadius / 16f, AbsorbAllLevel, Language.GetTextValue(key))) { OverrideColor = new Color(110, 220, 240) }); tooltips.Add(new TooltipLine(Mod, "DeathDomainToggle", Language.GetTextValue("Mods.SoulHarvest.UI.DeathDomainToggleHint"))); tooltips.Add(new TooltipLine(Mod, "SoulHarvestAltarHint", Language.GetTextValue("Mods.SoulHarvest.UI.AltarHint"))); } public DeathAltarPrice GetUpgradePrice(DeathAltarUpgradeType type) { return type switch { DeathAltarUpgradeType.NecklaceRadius => RadiusLevel >= MaxCoreLevel ? DeathAltarPrice.Unavailable : DeathAltarPrice.Essence((RadiusLevel + 1) / 2), DeathAltarUpgradeType.NecklaceAbsorbAll => AbsorbAllLevel >= MaxAbsorbAllLevel ? DeathAltarPrice.Unavailable : DeathAltarPrice.Essence((AbsorbAllLevel + 1) / 2), DeathAltarUpgradeType.NecklaceSovereignty => SovereigntyUnlocked || !AllCoreStatsMaxed ? DeathAltarPrice.Unavailable : DeathAltarPrice.Essence(30), _ => DeathAltarPrice.Unavailable }; } public bool ApplyAltarUpgrade(Item item, DeathAltarUpgradeType type) { if (!GetUpgradePrice(type).IsAvailable) return false; switch (type) { case DeathAltarUpgradeType.NecklaceRadius: RadiusLevel++; break; case DeathAltarUpgradeType.NecklaceAbsorbAll: AbsorbAllLevel++; break; case DeathAltarUpgradeType.NecklaceSovereignty: SovereigntyUnlocked = true; break; default: return false; } return true; } public string GetCurrentUpgradeValue(DeathAltarUpgradeType type) => type switch { DeathAltarUpgradeType.NecklaceRadius => $"{DomainRadius / 16f:0.#} tiles", DeathAltarUpgradeType.NecklaceAbsorbAll => $"Lv. {AbsorbAllLevel}", DeathAltarUpgradeType.NecklaceSovereignty => ToggleValue(SovereigntyUnlocked), _ => string.Empty }; public string GetNextUpgradeValue(DeathAltarUpgradeType type) => type switch { DeathAltarUpgradeType.NecklaceRadius => $"{(DomainRadius + RadiusGrowthPerLevel) / 16f:0.#} tiles", DeathAltarUpgradeType.NecklaceAbsorbAll => $"Lv. {AbsorbAllLevel + 1}", DeathAltarUpgradeType.NecklaceSovereignty => Language.GetTextValue("Mods.SoulHarvest.UI.DeathDescent"), _ => string.Empty }; public DeathAltarUnavailableReason GetUnavailableReason(DeathAltarUpgradeType type) { if (type == DeathAltarUpgradeType.NecklaceSovereignty && !SovereigntyUnlocked && !AllCoreStatsMaxed) return DeathAltarUnavailableReason.RequiresNecklaceMastery; return DeathAltarUnavailableReason.Maxed; } public override void SaveData(TagCompound tag) { tag[nameof(RadiusLevel)] = RadiusLevel; tag[nameof(AbsorbAllLevel)] = AbsorbAllLevel; tag["FrequencyLevel"] = LegacyFrequencyLevel; tag["DamageLevel"] = LegacyDamageLevel; tag["VolleyLevel"] = LegacyVolleyLevel; tag["LifeStealLevel"] = LegacyLifeStealLevel; tag[nameof(SovereigntyUnlocked)] = SovereigntyUnlocked; } public override void LoadData(TagCompound tag) { RadiusLevel = Math.Clamp(tag.GetInt(nameof(RadiusLevel)), 1, MaxCoreLevel); SovereigntyUnlocked = tag.GetBool(nameof(SovereigntyUnlocked)); LegacyFrequencyLevel = Math.Clamp(tag.GetInt("FrequencyLevel"), 1, DeathDomainProgression.MaxCoreLevel); LegacyDamageLevel = Math.Clamp(tag.GetInt("DamageLevel"), 1, DeathDomainProgression.MaxCoreLevel); LegacyVolleyLevel = Math.Clamp(tag.GetInt("VolleyLevel"), 1, DeathDomainProgression.MaxVolleyLevel); LegacyLifeStealLevel = Math.Clamp(tag.GetInt("LifeStealLevel"), 0, DeathDomainProgression.MaxLifeStealLevel); AbsorbAllLevel = tag.ContainsKey(nameof(AbsorbAllLevel)) ? Math.Clamp(tag.GetInt(nameof(AbsorbAllLevel)), 1, MaxAbsorbAllLevel) : SovereigntyUnlocked ? MaxAbsorbAllLevel : 1; } public override void NetSend(BinaryWriter writer) { writer.Write((byte)RadiusLevel); writer.Write((byte)AbsorbAllLevel); writer.Write((byte)LegacyFrequencyLevel); writer.Write((byte)LegacyDamageLevel); writer.Write((byte)LegacyVolleyLevel); writer.Write((byte)LegacyLifeStealLevel); writer.Write(SovereigntyUnlocked); } public override void NetReceive(BinaryReader reader) { RadiusLevel = Math.Clamp((int)reader.ReadByte(), 1, MaxCoreLevel); AbsorbAllLevel = Math.Clamp((int)reader.ReadByte(), 1, MaxAbsorbAllLevel); LegacyFrequencyLevel = Math.Clamp((int)reader.ReadByte(), 1, DeathDomainProgression.MaxCoreLevel); LegacyDamageLevel = Math.Clamp((int)reader.ReadByte(), 1, DeathDomainProgression.MaxCoreLevel); LegacyVolleyLevel = Math.Clamp((int)reader.ReadByte(), 1, DeathDomainProgression.MaxVolleyLevel); LegacyLifeStealLevel = Math.Clamp((int)reader.ReadByte(), 0, DeathDomainProgression.MaxLifeStealLevel); SovereigntyUnlocked = reader.ReadBoolean(); } public override void AddRecipes() { CreateRecipe() .AddIngredient(ItemID.PanicNecklace) .AddIngredient(ItemID.ObsidianRose) .AddIngredient(3) .AddTile(ModContent.TileType()) .Register(); } private bool AllCoreStatsMaxed => RadiusLevel >= MaxCoreLevel && AbsorbAllLevel >= MaxAbsorbAllLevel; private static string ToggleValue(bool enabled) => Language.GetTextValue( enabled ? "Mods.SoulHarvest.UI.Enabled" : "Mods.SoulHarvest.UI.Disabled"); }