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

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

公开
关注 0 Fork 0 Star 0
UTF-8
using SoulHarvest.Common;
using SoulHarvest.Items;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ReLogic.Content;
using System;
using System.Collections.Generic;
using System.Text;
using Terraria;
using Terraria.Audio;
using Terraria.GameContent;
using Terraria.GameContent.UI.Elements;
using Terraria.GameInput;
using Terraria.ID;
using Terraria.Localization;
using Terraria.ModLoader;
using Terraria.UI;

namespace SoulHarvest.UI;

internal sealed class ReaperTreeNodeModel
{
    public ReaperTreeNodeKind Kind { get; }
    public ReaperFormId Form { get; }
    public int Index { get; }
    public ReaperCommonNode CommonNode { get; }
    public Vector2 Position { get; }
    public float Radius { get; }

    public int CurrentLevel { get; private set; }
    public int MaximumLevel { get; private set; } = 1;
    public int ExpectedTarget { get; private set; }
    public bool Completed { get; private set; }
    public bool CanRequest { get; private set; }
    public bool Affordable { get; private set; }
    public bool Lit => Kind == ReaperTreeNodeKind.Stage ? Completed : CurrentLevel > 0;
    public string Title { get; private set; } = string.Empty;
    public string Category { get; private set; } = string.Empty;
    public string Description { get; private set; } = string.Empty;
    public string Status { get; private set; } = string.Empty;
    public string Cost { get; private set; } = string.Empty;

    private ReaperTreeNodeModel(ReaperTreeNodeKind kind, ReaperFormId form, int index, ReaperCommonNode commonNode, Vector2 position, float radius)
    {
        Kind = kind;
        Form = form;
        Index = index;
        CommonNode = commonNode;
        Position = position;
        Radius = radius;
    }

    public static ReaperTreeNodeModel Base(Vector2 position) => new(ReaperTreeNodeKind.Base, ReaperFormId.Base, 0, default, position, 46f);
    public static ReaperTreeNodeModel Death(Vector2 position) => new(ReaperTreeNodeKind.Death, ReaperFormId.Death, 0, default, position, 50f);
    public static ReaperTreeNodeModel Stage(ReaperFormId form, int stage, Vector2 position) => new(ReaperTreeNodeKind.Stage, form, stage, default, position, 31f);
    public static ReaperTreeNodeModel Skill(ReaperFormId form, int skill, Vector2 position) => new(ReaperTreeNodeKind.Skill, form, skill, default, position, 24f);
    public static ReaperTreeNodeModel Common(ReaperCommonNode node, Vector2 position) => new(ReaperTreeNodeKind.Common, ReaperFormId.Base, 0, node, position, 23f);
    public static ReaperTreeNodeModel DeathUpgrade(ReaperCommonNode node,
        Vector2 position) => new(ReaperTreeNodeKind.DeathUpgrade,
        ReaperFormId.Death, 0, node, position, 25f);

    public void Refresh(Player player, ReaperProgressionState progression, int essenceCount, int souls)
    {
        CanRequest = false;
        Affordable = false;
        Cost = string.Empty;
        switch (Kind)
        {
            case ReaperTreeNodeKind.Base:
                CurrentLevel = MaximumLevel = 1;
                Completed = true;
                Title = ReaperUIData.GetFormName(ReaperFormId.Base);
                Category = Text("CategoryBase");
                Description = ReaperUIData.GetFormSummary(ReaperFormId.Base);
                Status = Text("BaseAlwaysAvailable");
                break;
            case ReaperTreeNodeKind.Death:
                RefreshDeath(progression);
                break;
            case ReaperTreeNodeKind.Stage:
                RefreshStage(player, progression, essenceCount);
                break;
            case ReaperTreeNodeKind.Skill:
                RefreshSkill(progression, souls);
                break;
            case ReaperTreeNodeKind.Common:
            case ReaperTreeNodeKind.DeathUpgrade:
                RefreshCommon(progression, essenceCount, souls);
                break;
        }
    }

    public void RequestUpgrade()
    {
        if (!CanRequest || !Affordable)
            return;
        switch (Kind)
        {
            case ReaperTreeNodeKind.Stage:
                SoulHarvest.RequestReaperStageUpgrade(Form, (ReaperStage)ExpectedTarget);
                break;
            case ReaperTreeNodeKind.Skill:
                SoulHarvest.RequestReaperSkillUpgrade(Form, Index, ExpectedTarget);
                break;
            case ReaperTreeNodeKind.Common:
            case ReaperTreeNodeKind.DeathUpgrade:
                SoulHarvest.RequestReaperCommonUpgrade(CommonNode, ExpectedTarget);
                break;
        }
    }

    private void RefreshDeath(ReaperProgressionState progression)
    {
        CurrentLevel = progression.DeathFormUnlocked ? 1 : 0;
        MaximumLevel = 1;
        Completed = progression.DeathFormUnlocked;
        Title = ReaperUIData.GetFormName(ReaperFormId.Death);
        Category = Text("CategoryFinal");
        Description = $"{ReaperUIData.GetFormSummary(ReaperFormId.Death)}\n{Text("UltimateLabel", ReaperUIData.GetUltimateName(ReaperFormId.Death))}";
        Status = Text(progression.DeathFormUnlocked ? "DeathUnlocked" : "DeathLocked");
        int completedBranches = 0;
        int masteredSkills = 0;
        foreach (ReaperFormId form in ReaperDefinitions.BranchForms)
        {
            if (progression.GetStage(form) >= ReaperStage.StageIII)
                completedBranches++;
            for (int skill = 0; skill < ReaperDefinitions.SkillsPerBranch; skill++)
                if (progression.GetSkillLevel(form, skill) >= ReaperDefinitions.MaximumSkillLevel)
                    masteredSkills++;
        }
        Cost = Text("DeathMasteryProgress", completedBranches, masteredSkills);
    }

    private void RefreshStage(Player player, ReaperProgressionState progression, int essenceCount)
    {
        int current = (int)progression.GetStage(Form);
        // Each stage node is a binary unlock, not one shared three-rank node.
        CurrentLevel = current >= Index ? 1 : 0;
        MaximumLevel = 1;
        ExpectedTarget = Index;
        Completed = current >= Index;
        CanRequest = current == Index - 1;
        Title = $"{ReaperUIData.GetFormName(Form)} · {ReaperUIData.GetStageName(Index)}";
        Category = Text("CategoryStage");
        ReaperFormDefinition definition = ReaperDefinitions.GetForm(Form);
        Description = Text("StageCombatStats", ReaperUIData.GetFormSummary(Form), definition.GetBaseDamage((ReaperStage)Index), definition.BaseUseTime);
        if (Index == 3)
            Description += $"\n{Text("UltimateLabel", ReaperUIData.GetUltimateName(Form))}";

        int seals = progression.GetSoulSealAvailable((ReaperStage)Index);
        int receiptCount = CountInventoryItem(player, ReaperSoulSealCatalog.GetItemType((ReaperStage)Index));
        int essenceCost = ReaperDefinitions.GetStageEssenceCost((ReaperStage)Index);
        bool worldReady = ReaperDefinitions.MeetsWorldGate((ReaperStage)Index);
        Affordable = Completed || worldReady && seals > 0 && receiptCount > 0 && essenceCount >= essenceCost;
        if (Completed)
        {
            Status = Text("StatusComplete");
            Cost = Text("Completed");
        }
        else if (!CanRequest)
        {
            Status = Text("StatusLocked");
            Cost = Text("StagePrerequisite", Index - 1);
        }
        else if (!worldReady)
        {
            Status = Text("StatusLocked");
            Cost = Language.GetTextValue($"Mods.SoulHarvest.UI.ReaperTree.StageGates.Stage{Index}");
        }
        else if (seals <= 0)
        {
            Status = Text("StatusInsufficient");
            Cost = Text("NoStageSeal");
        }
        else if (receiptCount <= 0)
        {
            Status = Text("StatusInsufficient");
            Cost = Text("NoStageSealItem");
        }
        else
        {
            Status = Affordable ? Text("StatusReady") : Text("StatusInsufficient");
            Cost = Text("StageCost", essenceCost, seals, receiptCount);
        }
    }

    private void RefreshSkill(ReaperProgressionState progression, int souls)
    {
        int stage = (int)progression.GetStage(Form);
        int level = progression.GetSkillLevel(Form, Index);
        int requiredStage = Index + 1;
        CurrentLevel = level;
        MaximumLevel = ReaperDefinitions.MaximumSkillLevel;
        ExpectedTarget = level + 1;
        Completed = level >= MaximumLevel;
        CanRequest = stage >= requiredStage && !Completed;
        int soulCost = ReaperDefinitions.GetSkillSoulCost(level + 1);
        Affordable = Completed || CanRequest && soulCost >= 0 && souls >= soulCost;
        Title = ReaperUIData.GetSkillName(Form, Index);
        Category = Text("CategorySkill", ReaperUIData.GetFormName(Form));
        Description = ReaperUIData.GetSkillDescription(Form, Index);
        if (stage < requiredStage)
        {
            Status = Text("StatusLocked");
            Cost = Text("SkillStageLocked", requiredStage);
        }
        else if (Completed)
        {
            Status = Text("StatusComplete");
            Cost = Text("CompletedLevel", level, MaximumLevel);
        }
        else
        {
            Status = Affordable ? Text("StatusReady") : Text("StatusInsufficient");
            Cost = Text("SkillLevel", level, MaximumLevel, soulCost);
        }
    }

    private void RefreshCommon(ReaperProgressionState progression, int essenceCount, int souls)
    {
        int level = progression.GetCommonNodeLevel(CommonNode);
        int maximum = ReaperDefinitions.GetCommonNodeMaxLevel(CommonNode);
        bool unlimited = ReaperDefinitions.IsUnlimitedCommonNode(CommonNode);
        int targetLevel = level + 1;
        ReaperStage requiredStage = ReaperDefinitions.GetCommonNodeRequiredStage(CommonNode, targetLevel);
        ReaperProgressionCost? price = ReaperDefinitions.GetCommonNodeCost(CommonNode, targetLevel);
        CurrentLevel = level;
        MaximumLevel = maximum;
        ExpectedTarget = targetLevel;
        Completed = !unlimited && level >= maximum;
        CanRequest = !Completed && progression.HasAnyBranchAtLeast(requiredStage);
        if (ReaperDefinitions.IsDeathNode(CommonNode))
        {
            CanRequest &= progression.DeathFormUnlocked;
            if (CommonNode == ReaperCommonNode.DeathRiftWidth)
            {
                CanRequest &= progression.GetCommonNodeLevel(
                    ReaperCommonNode.DeathSpaceBreak) > 0;
            }
        }
        Affordable = Completed || CanRequest && price is ReaperProgressionCost cost
            && (cost.Currency == ReaperProgressionCurrency.Souls ? souls >= cost.Amount : essenceCount >= cost.Amount);
        Title = ReaperUIData.GetCommonNodeName(CommonNode);
        Category = Text(ReaperDefinitions.IsDeathNode(CommonNode)
            ? "CategoryDeathUpgrade" : "CategoryCommon");
        Description = ReaperUIData.GetCommonNodeDescription(CommonNode);
        if (Completed)
        {
            Status = Text("StatusComplete");
            Cost = Text("CompletedLevel", level, maximum);
        }
        else if (!CanRequest)
        {
            Status = Text("StatusLocked");
            Cost = ReaperDefinitions.IsDeathNode(CommonNode)
                ? Text("DeathUpgradeLocked")
                : Text("CommonStageLocked", (int)requiredStage);
        }
        else if (price is ReaperProgressionCost commonCost)
        {
            Status = Affordable ? Text("StatusReady") : Text("StatusInsufficient");
            Cost = unlimited
                ? Text("CommonUnlimitedLevel", level, FormatCost(commonCost))
                : Text("CommonLevel", level, maximum, FormatCost(commonCost));
        }
        else
        {
            Status = Text("StatusLocked");
            Cost = Text("Unavailable");
        }
    }

    private static int CountInventoryItem(Player player, int itemType)
    {
        if (itemType <= ItemID.None)
            return 0;
        int count = 0;
        int slots = Math.Min(58, player.inventory.Length);
        for (int slot = 0; slot < slots; slot++)
        {
            Item item = player.inventory[slot];
            if (item.type == itemType && item.stack > 0)
                count += item.stack;
        }
        return count;
    }

    private static string FormatCost(ReaperProgressionCost cost) => Text(cost.Currency == ReaperProgressionCurrency.Souls ? "CostSouls" : "CostEssence", cost.Amount);
    private static string Text(string key, params object[] args) => Language.GetTextValue($"Mods.SoulHarvest.UI.ReaperTree.{key}", args);
}