XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

公开
关注 0 Fork 0 Star 0
UTF-8
using System;
using System.Collections.Generic;
using System.IO;
using Terraria;
using Terraria.Localization;
using Terraria.ModLoader.IO;

namespace SoulHarvest.Common;

/// <summary>
/// Character-owned combat progression shared by every weapon, wing and
/// accessory that cuts open the Death Domain. It intentionally contains no
/// domain toggle: opening the complete field remains a Death Necklace ability.
/// </summary>
public sealed class DeathDomainProgression : IDeathAltarUpgradeable
{
    private const string SaveKey = "DeathDomainProgression";

    public const int MaxCoreLevel = 10;
    public const int MaxVolleyLevel = 5;
    public const int MaxLifeStealLevel = 5;
    public const int MaxSoulRefinementLevel = 5;
    public const float SoulRefinementPerLevel = 0.10f;

    private static readonly DeathAltarUpgradeType[] UpgradeTypes =
    [
        DeathAltarUpgradeType.DomainFrequency,
        DeathAltarUpgradeType.DomainDamage,
        DeathAltarUpgradeType.DomainVolley,
        DeathAltarUpgradeType.DomainLifeSteal,
        DeathAltarUpgradeType.DomainSoulRefinement
    ];

    public int FrequencyLevel { get; private set; } = 1;
    public int DamageLevel { get; private set; } = 1;
    public int VolleyLevel { get; private set; } = 1;
    public int LifeStealLevel { get; private set; }
    public int SoulRefinementLevel { get; private set; }

    public int SpawnInterval => Math.Max(34, 100 - (FrequencyLevel - 1) * 7);
    public float NormalSlashLifeRatio => DamageLevel * 0.01f;
    public float BossSlashLifeRatio => DamageLevel * 0.001f;
    public int SlashCount => VolleyLevel;
    public float SoulRefinementBonus => SoulRefinementLevel * SoulRefinementPerLevel;
    public float VisualMastery => Math.Clamp(
        (FrequencyLevel + DamageLevel + VolleyLevel + LifeStealLevel + SoulRefinementLevel - 3f) / 32f,
        0f,
        1f);

    public IReadOnlyList<DeathAltarUpgradeType> AltarUpgradeTypes => UpgradeTypes;

    public void Reset()
    {
        FrequencyLevel = 1;
        DamageLevel = 1;
        VolleyLevel = 1;
        LifeStealLevel = 0;
        SoulRefinementLevel = 0;
    }

    public int ApplySoulRefinement(int baseAmount)
    {
        if (baseAmount <= 0 || SoulRefinementLevel <= 0)
            return Math.Max(0, baseAmount);

        int bonus = (int)Math.Floor(baseAmount * SoulRefinementBonus);
        return Math.Max(baseAmount, baseAmount + bonus);
    }

    public bool MergeLegacyNecklaceLevels(int frequency, int damage, int volley, int lifeSteal)
    {
        int oldFrequency = FrequencyLevel;
        int oldDamage = DamageLevel;
        int oldVolley = VolleyLevel;
        int oldLifeSteal = LifeStealLevel;
        FrequencyLevel = Math.Max(FrequencyLevel, Math.Clamp(frequency, 1, MaxCoreLevel));
        DamageLevel = Math.Max(DamageLevel, Math.Clamp(damage, 1, MaxCoreLevel));
        VolleyLevel = Math.Max(VolleyLevel, Math.Clamp(volley, 1, MaxVolleyLevel));
        LifeStealLevel = Math.Max(LifeStealLevel, Math.Clamp(lifeSteal, 0, MaxLifeStealLevel));
        return oldFrequency != FrequencyLevel
            || oldDamage != DamageLevel
            || oldVolley != VolleyLevel
            || oldLifeSteal != LifeStealLevel;
    }

    public DeathAltarPrice GetUpgradePrice(DeathAltarUpgradeType type) => type switch
    {
        DeathAltarUpgradeType.DomainFrequency => FrequencyLevel >= MaxCoreLevel
            ? DeathAltarPrice.Unavailable
            : DeathAltarPrice.Essence((FrequencyLevel + 1) / 2),
        DeathAltarUpgradeType.DomainDamage => DamageLevel >= MaxCoreLevel
            ? DeathAltarPrice.Unavailable
            : DeathAltarPrice.Essence(DamageLevel),
        DeathAltarUpgradeType.DomainVolley => VolleyLevel >= MaxVolleyLevel
            ? DeathAltarPrice.Unavailable
            : DeathAltarPrice.Essence(VolleyLevel * 3),
        DeathAltarUpgradeType.DomainLifeSteal => LifeStealLevel >= MaxLifeStealLevel
            ? DeathAltarPrice.Unavailable
            : DeathAltarPrice.Essence(2 + LifeStealLevel * 2),
        DeathAltarUpgradeType.DomainSoulRefinement => SoulRefinementLevel >= MaxSoulRefinementLevel
            ? DeathAltarPrice.Unavailable
            : DeathAltarPrice.Essence(4 + SoulRefinementLevel * 3),
        _ => DeathAltarPrice.Unavailable
    };

    public bool ApplyAltarUpgrade(Item item, DeathAltarUpgradeType type)
    {
        if (!GetUpgradePrice(type).IsAvailable)
            return false;

        switch (type)
        {
            case DeathAltarUpgradeType.DomainFrequency:
                FrequencyLevel++;
                break;
            case DeathAltarUpgradeType.DomainDamage:
                DamageLevel++;
                break;
            case DeathAltarUpgradeType.DomainVolley:
                VolleyLevel++;
                break;
            case DeathAltarUpgradeType.DomainLifeSteal:
                LifeStealLevel++;
                break;
            case DeathAltarUpgradeType.DomainSoulRefinement:
                SoulRefinementLevel++;
                break;
            default:
                return false;
        }

        return true;
    }

    public string GetCurrentUpgradeValue(DeathAltarUpgradeType type) => type switch
    {
        DeathAltarUpgradeType.DomainFrequency => $"{SpawnInterval / 60f:0.##}s",
        DeathAltarUpgradeType.DomainDamage => FormatSlashDamage(NormalSlashLifeRatio, BossSlashLifeRatio),
        DeathAltarUpgradeType.DomainVolley => $"×{SlashCount}",
        DeathAltarUpgradeType.DomainLifeSteal => $"{LifeStealLevel}%",
        DeathAltarUpgradeType.DomainSoulRefinement => $"+{SoulRefinementBonus * 100f:0}%",
        _ => string.Empty
    };

    public string GetNextUpgradeValue(DeathAltarUpgradeType type) => type switch
    {
        DeathAltarUpgradeType.DomainFrequency => $"{Math.Max(34, SpawnInterval - 7) / 60f:0.##}s",
        DeathAltarUpgradeType.DomainDamage => FormatSlashDamage(NormalSlashLifeRatio + 0.01f, BossSlashLifeRatio + 0.001f),
        DeathAltarUpgradeType.DomainVolley => $"×{SlashCount + 1}",
        DeathAltarUpgradeType.DomainLifeSteal => $"{LifeStealLevel + 1}%",
        DeathAltarUpgradeType.DomainSoulRefinement => $"+{(SoulRefinementLevel + 1) * SoulRefinementPerLevel * 100f:0}%",
        _ => string.Empty
    };

    public DeathAltarUnavailableReason GetUnavailableReason(DeathAltarUpgradeType type)
        => DeathAltarUnavailableReason.Maxed;

    public void SaveData(TagCompound rootTag)
    {
        rootTag[SaveKey] = new TagCompound
        {
            [nameof(FrequencyLevel)] = FrequencyLevel,
            [nameof(DamageLevel)] = DamageLevel,
            [nameof(VolleyLevel)] = VolleyLevel,
            [nameof(LifeStealLevel)] = LifeStealLevel,
            [nameof(SoulRefinementLevel)] = SoulRefinementLevel
        };
    }

    public void LoadData(TagCompound rootTag)
    {
        Reset();
        if (!rootTag.ContainsKey(SaveKey))
            return;

        TagCompound tag = rootTag.GetCompound(SaveKey);
        FrequencyLevel = Math.Clamp(tag.GetInt(nameof(FrequencyLevel)), 1, MaxCoreLevel);
        DamageLevel = Math.Clamp(tag.GetInt(nameof(DamageLevel)), 1, MaxCoreLevel);
        VolleyLevel = Math.Clamp(tag.GetInt(nameof(VolleyLevel)), 1, MaxVolleyLevel);
        LifeStealLevel = Math.Clamp(tag.GetInt(nameof(LifeStealLevel)), 0, MaxLifeStealLevel);
        SoulRefinementLevel = Math.Clamp(tag.GetInt(nameof(SoulRefinementLevel)), 0, MaxSoulRefinementLevel);
    }

    public void NetSend(BinaryWriter writer)
    {
        writer.Write((byte)FrequencyLevel);
        writer.Write((byte)DamageLevel);
        writer.Write((byte)VolleyLevel);
        writer.Write((byte)LifeStealLevel);
        writer.Write((byte)SoulRefinementLevel);
    }

    public void NetReceive(BinaryReader reader)
    {
        FrequencyLevel = Math.Clamp((int)reader.ReadByte(), 1, MaxCoreLevel);
        DamageLevel = Math.Clamp((int)reader.ReadByte(), 1, MaxCoreLevel);
        VolleyLevel = Math.Clamp((int)reader.ReadByte(), 1, MaxVolleyLevel);
        LifeStealLevel = Math.Clamp((int)reader.ReadByte(), 0, MaxLifeStealLevel);
        SoulRefinementLevel = Math.Clamp((int)reader.ReadByte(), 0, MaxSoulRefinementLevel);
    }

    private static string FormatSlashDamage(float normalRatio, float bossRatio)
        => Language.GetTextValue("Mods.SoulHarvest.UI.NecklaceSlashDamage", normalRatio * 100f, bossRatio * 100f);
}