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 Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using Terraria;
using Terraria.GameContent;
using Terraria.ID;
using Terraria.ModLoader;

namespace SoulHarvest.Projectiles;

public class LifeStealWispProjectile : ModProjectile
{
    private const int TrailHistoryLength = 32;

    private int LifeStealLevel => (int)Projectile.ai[0];

    public override void SetStaticDefaults()
    {
        ProjectileID.Sets.TrailCacheLength[Type] = TrailHistoryLength;
        ProjectileID.Sets.TrailingMode[Type] = 2;
    }

    public override void SetDefaults()
    {
        Projectile.width = 8;
        Projectile.height = 8;
        Projectile.friendly = false;
        Projectile.hostile = false;
        Projectile.tileCollide = false;
        Projectile.ignoreWater = true;
        Projectile.timeLeft = 90;
        Projectile.penetrate = -1;
        // Modern Blood Covenant healing is resolved on the server. Keep the
        // short-lived feedback projectile important so its spawn is not skipped
        // before the owning client can render the return path.
        Projectile.netImportant = true;
        Projectile.extraUpdates = 1;
        Projectile.hide = false;
    }

    public override void AI()
    {
        Player player = Main.player[Projectile.owner];
        if (!player.active || player.dead)
        {
            Projectile.Kill();
            return;
        }

        Projectile.localAI[0]++;
        float age = Projectile.localAI[0];
        Vector2 toPlayer = player.Center - Projectile.Center;
        float approach = MathHelper.Clamp(age / 58f, 0f, 1f);
        float speed = MathHelper.Lerp(3.5f, 13f + LifeStealLevel, approach);
        Vector2 radialDirection = toPlayer.SafeNormalize(Vector2.UnitY);
        Vector2 tangent = radialDirection.RotatedBy(MathHelper.PiOver2)
            * (float)System.Math.Sin(age * 0.2f + Projectile.ai[1])
            * MathHelper.Lerp(5.5f + LifeStealLevel, 0.7f, approach);
        Vector2 desired = radialDirection * speed + tangent;
        Projectile.velocity = Vector2.Lerp(Projectile.velocity, desired, 0.085f + LifeStealLevel * 0.01f);
        Projectile.rotation = Projectile.velocity.ToRotation();

        Color color = Color.Lerp(new Color(215, 20, 45), new Color(255, 105, 195), LifeStealLevel / 5f);
        if (!Main.dedServ)
        {
            Lighting.AddLight(Projectile.Center, color.ToVector3() * (0.18f + LifeStealLevel * 0.06f));
            int particleCount = LifeStealLevel >= 4 ? 2 : 1;
            for (int index = 0; index < particleCount; index++)
            {
                if (!Main.rand.NextBool(System.Math.Max(1, 4 - LifeStealLevel / 2)))
                    continue;

                Vector2 offset = Main.rand.NextVector2Circular(2.5f + LifeStealLevel * 0.35f, 2.5f + LifeStealLevel * 0.35f);
                Dust dust = Dust.NewDustPerfect(Projectile.Center + offset, DustID.Blood,
                    -Projectile.velocity * Main.rand.NextFloat(0.025f, 0.075f), 80, color,
                    0.65f + LifeStealLevel * 0.07f + Main.rand.NextFloat(0.12f));
                dust.noGravity = true;

                if (LifeStealLevel >= 3 && Main.rand.NextBool(3))
                {
                    Dust core = Dust.NewDustPerfect(Projectile.Center - offset * 0.3f, DustID.AncientLight,
                        -Projectile.velocity * 0.018f, 110, Color.Lerp(color, Color.White, 0.45f), 0.42f + LifeStealLevel * 0.04f);
                    core.noGravity = true;
                }
            }
        }

        if (toPlayer.LengthSquared() > 16f * 16f)
            return;

        if (!Main.dedServ)
        {
            for (int index = 0; index < 3 + LifeStealLevel * 2; index++)
            {
                Dust dust = Dust.NewDustPerfect(player.Center, DustID.Blood, Main.rand.NextVector2Circular(2.5f, 2.5f), 60, color, 0.8f + Main.rand.NextFloat(0.4f));
                dust.noGravity = true;
            }
        }
        Projectile.Kill();
    }

    public override bool PreDraw(ref Color lightColor)
    {
        Texture2D texture = TextureAssets.Projectile[Type].Value;
        Vector2 origin = texture.Size() * 0.5f;
        float rotation = Projectile.velocity.ToRotation() - MathHelper.PiOver2;
        float baseScale = 0.34f + LifeStealLevel * 0.045f;
        int trailPointCount = System.Math.Min(Projectile.oldPos.Length, 14 + LifeStealLevel * 3);
        float levelRatio = MathHelper.Clamp(LifeStealLevel / 5f, 0f, 1f);
        Color outerColor = Color.Lerp(new Color(125, 0, 28), new Color(245, 38, 155), levelRatio) with { A = 0 };
        Color coreColor = Color.Lerp(new Color(235, 22, 48), new Color(255, 155, 220), levelRatio) with { A = 0 };

        for (int index = trailPointCount - 1; index >= 0; index--)
        {
            Vector2 older = Projectile.oldPos[index] + Projectile.Size * 0.5f;
            Vector2 newer = index == 0
                ? Projectile.Center
                : Projectile.oldPos[index - 1] + Projectile.Size * 0.5f;
            if (Projectile.oldPos[index] == Vector2.Zero
                || index > 0 && Projectile.oldPos[index - 1] == Vector2.Zero)
                continue;

            Vector2 segment = newer - older;
            float length = segment.Length();
            if (length is < 0.35f or > 40f)
                continue;

            float strength = 1f - index / (float)trailPointCount;
            float taper = (float)System.Math.Pow(strength, 0.72f);
            float outerWidth = (2.25f + LifeStealLevel * 0.58f) * taper;
            float coreWidth = (0.72f + LifeStealLevel * 0.2f) * taper;
            DrawTrailSegment(older, segment, outerColor * strength * 0.34f, outerWidth);
            DrawTrailSegment(older, segment, coreColor * strength * 0.82f, coreWidth);
        }

        int afterimageCount = System.Math.Min(trailPointCount, 5 + LifeStealLevel);
        for (int index = afterimageCount - 1; index >= 1; index--)
        {
            if (Projectile.oldPos[index] == Vector2.Zero)
                continue;

            float strength = 1f - index / (float)afterimageCount;
            Vector2 position = Projectile.oldPos[index] + Projectile.Size * 0.5f - Main.screenPosition;
            Main.EntitySpriteDraw(texture, position, null, coreColor * strength * 0.26f,
                rotation, origin, baseScale * (0.58f + strength * 0.24f), SpriteEffects.None);
        }

        Main.EntitySpriteDraw(texture, Projectile.Center - Main.screenPosition, null, Color.White,
            rotation, origin, baseScale, SpriteEffects.None);
        return false;
    }

    private static void DrawTrailSegment(Vector2 start, Vector2 segment, Color color, float width)
    {
        DeathDomainPrimitiveTextureSystem.DrawSmoothWorldLine(start, start + segment, color, width);
    }
}