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;

/// <summary>
/// A non-damaging, server-authoritative Soul energy pickup. Energy is awarded only
/// after the shard reaches its owner; changing form before then destroys it.
/// </summary>
public sealed class ReaperSoulShardProjectile : ModProjectile
{
    private bool serverAuthorized;
    private Vector2 authoritativeCenter;
    private Vector2 authoritativeVelocity;
    private int timer;

    public override string Texture => "Terraria/Images/Projectile_0";

    public static bool Spawn(Terraria.DataStructures.IEntitySource source, Player owner, Vector2 origin)
    {
        if (Main.netMode == NetmodeID.MultiplayerClient || !owner.active || owner.dead)
            return false;

        int index = Projectile.NewProjectile(source, origin, Vector2.Zero,
            ModContent.ProjectileType<ReaperSoulShardProjectile>(), 0, 0f, owner.whoAmI);
        if (index < 0 || index >= Main.maxProjectiles)
            return false;

        Projectile projectile = Main.projectile[index];
        if (projectile.ModProjectile is not ReaperSoulShardProjectile shard)
        {
            projectile.Kill();
            return false;
        }

        shard.serverAuthorized = true;
        shard.authoritativeCenter = projectile.Center;
        shard.authoritativeVelocity = Vector2.Zero;
        projectile.GetGlobalProjectile<MyGlobalProjectile>().AuthorizeServerReaperProjectile(projectile);
        ReaperProjectileHelper.SyncNewProjectile(projectile);
        return true;
    }

    public override void SetDefaults()
    {
        Projectile.width = 12;
        Projectile.height = 12;
        Projectile.friendly = false;
        Projectile.hostile = false;
        Projectile.tileCollide = false;
        Projectile.ignoreWater = true;
        Projectile.penetrate = -1;
        Projectile.timeLeft = 120;
        // PreDraw supplies the orb itself; keeping the projectile hidden prevented
        // Terraria from ever placing it in a draw cache.
        Projectile.hide = false;
        Projectile.netImportant = true;
    }

    public override bool ShouldUpdatePosition() => false;

    public override bool? CanDamage() => false;

    public override void AI()
    {
        if (Main.netMode == NetmodeID.Server)
        {
            if (!serverAuthorized)
            {
                Projectile.Kill();
                return;
            }
            Projectile.Center = authoritativeCenter;
            Projectile.velocity = authoritativeVelocity;
        }

        if (Projectile.owner < 0 || Projectile.owner >= Main.maxPlayers)
        {
            Projectile.Kill();
            return;
        }

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

        if (Main.netMode != NetmodeID.MultiplayerClient
            && ReaperCombatRegistry.ResolveUsableForm(owner.GetModPlayer<MyPlayer>().ReaperProgression) != ReaperFormId.Soul)
        {
            Projectile.Kill();
            return;
        }

        timer++;
        Vector2 toOwner = owner.MountedCenter - Projectile.Center;
        if (Main.netMode != NetmodeID.MultiplayerClient && toOwner.LengthSquared() <= 24f * 24f)
        {
            Projectile.Kill();
            return;
        }

        float speed = MathHelper.Lerp(7f, 18f, MathHelper.Clamp(timer / 45f, 0f, 1f));
        Vector2 desired = toOwner.SafeNormalize(Vector2.UnitY * -1f) * speed;
        Projectile.velocity = Vector2.Lerp(Projectile.velocity, desired, 0.16f);
        Projectile.position += Projectile.velocity;
        Projectile.rotation = Projectile.velocity.ToRotation();

        if (Main.netMode == NetmodeID.Server)
        {
            authoritativeCenter = Projectile.Center;
            authoritativeVelocity = Projectile.velocity;
            if (timer % 10 == 0)
                Projectile.netUpdate = true;
        }
        else
        {
            Lighting.AddLight(Projectile.Center, new Vector3(0.12f, 0.32f, 0.58f));
            if (Main.rand.NextBool(2))
            {
                Dust dust = Dust.NewDustPerfect(Projectile.Center, DustID.AncientLight,
                    -Projectile.velocity * 0.08f, 60,
                    Main.rand.NextBool() ? new Color(86, 229, 255) : new Color(173, 91, 255), 0.8f);
                dust.noGravity = true;
            }
        }
    }

    public override bool PreDraw(ref Color lightColor)
    {
        Color outer = new Color(116, 78, 255, 0);
        Color core = new Color(130, 245, 255, 0);
        float pulse = 0.82f + (float)Math.Sin(Main.GlobalTimeWrappedHourly * 8f + Projectile.identity) * 0.15f;
        Vector2 center = Projectile.Center - Main.screenPosition;
        Vector2 axis = Projectile.rotation.ToRotationVector2();
        DeathDomainPrimitiveTextureSystem.DrawSmoothLine(Main.spriteBatch,
            center - axis * 6.5f, center + axis * 6.5f, outer * pulse, 7f);
        DeathDomainPrimitiveTextureSystem.DrawSmoothLine(Main.spriteBatch,
            center - axis * 3f, center + axis * 3f, core * pulse, 3f);
        return false;
    }
}