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

DeathMod

【Terraria】DeathMod (now aka Soul Harvest)

公开
关注 0 Fork 0 Star 0
UTF-8
using SoulHarvest.Items;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using Terraria;
using Terraria.GameContent;
using Terraria.ID;
using Terraria.ModLoader;

namespace SoulHarvest.Common;

/// <summary>
/// Client-only persistent presentation for a mastered form. Stage I/II weapon
/// treatment is owned by the swing projectile; Stage III adds this form aura even
/// while the player is between attacks. It never creates gameplay entities.
/// </summary>
[Autoload(Side = ModSide.Client)]
internal sealed class ReaperStageVisualSystem : ModSystem
{
    public override void PostDrawTiles()
    {
        if (Main.dedServ || Main.gameMenu || !HasVisibleAura())
            return;

        SpriteBatch spriteBatch = Main.spriteBatch;
        spriteBatch.Begin(
            SpriteSortMode.Deferred,
            BlendState.AlphaBlend,
            SamplerState.PointClamp,
            DepthStencilState.None,
            RasterizerState.CullNone,
            null,
            Main.GameViewMatrix.TransformationMatrix);

        Texture2D pixel = TextureAssets.MagicPixel.Value;
        for (int playerIndex = 0; playerIndex < Main.maxPlayers; playerIndex++)
        {
            Player player = Main.player[playerIndex];
            if (TryGetAuraForm(player, out ReaperFormId form))
                DrawAura(spriteBatch, pixel, player.MountedCenter, form, playerIndex);
        }

        spriteBatch.End();
    }

    private static bool HasVisibleAura()
    {
        for (int playerIndex = 0; playerIndex < Main.maxPlayers; playerIndex++)
        {
            if (TryGetAuraForm(Main.player[playerIndex], out _))
                return true;
        }
        return false;
    }

    private static bool TryGetAuraForm(Player player, out ReaperFormId form)
    {
        form = ReaperFormId.Base;
        if (!player.active || player.dead || player.HeldItem.ModItem is not NormalSickle)
            return false;

        MyPlayer modPlayer = player.GetModPlayer<MyPlayer>();
        ReaperStage stage;
        if (Main.netMode != NetmodeID.MultiplayerClient || player.whoAmI == Main.myPlayer)
        {
            // The owner (and single-player) has the authoritative character tree.
            // VisibleReaper* is deliberately only the compact remote-player view.
            form = modPlayer.ReaperProgression.CurrentForm;
            stage = modPlayer.ReaperProgression.GetStage(form);
        }
        else
        {
            form = modPlayer.VisibleReaperForm;
            stage = modPlayer.VisibleReaperStage;
        }

        return form == ReaperFormId.Death
            || ReaperDefinitions.IsBranchForm(form) && stage >= ReaperStage.StageIII;
    }

    private static void DrawAura(SpriteBatch spriteBatch, Texture2D pixel, Vector2 center, ReaperFormId form, int playerIndex)
    {
        float time = Main.GlobalTimeWrappedHourly;
        float phase = time * 0.62f + playerIndex * 0.73f;
        float pulse = 1f + (float)Math.Sin(time * 3.6f + playerIndex) * 0.055f;
        float radius = (form == ReaperFormId.Death ? 49f : 42f) * pulse;
        Color primary = ReaperCombatRegistry.GetPrimaryColor(form) with { A = 0 };
        Color secondary = ReaperCombatRegistry.GetSecondaryColor(form) with { A = 0 };

        DrawRing(spriteBatch, pixel, center, radius, 30, primary * 0.45f, 3.4f, phase);
        DrawRing(spriteBatch, pixel, center, radius * 0.76f, 24, secondary * 0.34f, 1.45f, -phase * 0.72f);

        int points = form == ReaperFormId.Death ? 6 : 4;
        for (int index = 0; index < points; index++)
        {
            float angle = -phase * 0.48f + index * MathHelper.TwoPi / points;
            Vector2 direction = angle.ToRotationVector2();
            Vector2 tangent = direction.RotatedBy(MathHelper.PiOver2);
            Vector2 runeCenter = center + direction * radius;
            DrawSegment(spriteBatch, pixel, runeCenter - tangent * 5f, runeCenter + tangent * 5f,
                secondary * 0.58f, 1.8f);
            DrawSegment(spriteBatch, pixel, runeCenter - direction * 3f, runeCenter + direction * 6f,
                primary * 0.50f, 2.4f);
        }
    }

    private static void DrawRing(
        SpriteBatch spriteBatch,
        Texture2D pixel,
        Vector2 center,
        float radius,
        int segments,
        Color color,
        float width,
        float rotation)
    {
        if (radius <= 0.1f || segments < 3)
            return;
        segments = Math.Max(segments,
            Math.Min(128, (int)Math.Ceiling(MathHelper.TwoPi * radius / 18f)));
        Vector2 previous = center + rotation.ToRotationVector2() * radius;
        for (int index = 1; index <= segments; index++)
        {
            Vector2 current = center + (rotation + index * MathHelper.TwoPi / segments).ToRotationVector2() * radius;
            DrawSegment(spriteBatch, pixel, previous, current, color, width);
            previous = current;
        }
    }

    private static void DrawSegment(
        SpriteBatch spriteBatch,
        Texture2D pixel,
        Vector2 start,
        Vector2 end,
        Color color,
        float width)
    {
        DeathDomainPrimitiveTextureSystem.DrawSmoothLine(spriteBatch,
            start - Main.screenPosition, end - Main.screenPosition, color, width);
    }
}