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 System;
using System.IO;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace SoulHarvest.Projectiles;

/// <summary>
/// Target-locked release hit for the Blood special.  Its authoritative damage is
/// the total life drained during the charge; the persistent scar is a separate
/// actor worth half of that amount on every damage pulse.
/// </summary>
public sealed class ReaperBloodExecutionProjectile : ModProjectile
{
    private SickleCombatSnapshot snapshot;
    private int targetIndex = -1;
    private int phase;
    private int actionId;
    private float length;
    private float width;
    private int age;
    private bool configured;
    private bool serverAuthorized;

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

    internal static int Spawn(Terraria.DataStructures.IEntitySource source, int owner,
        in SickleCombatSnapshot snapshot, NPC target, Vector2 direction,
        float length, float width, int damage, int phase, int actionId)
    {
        if (Main.netMode == NetmodeID.MultiplayerClient || damage <= 0)
            return -1;
        direction = direction.SafeNormalize(Vector2.UnitX);
        int index = Projectile.NewProjectile(source, target.Center, direction,
            ModContent.ProjectileType<ReaperBloodExecutionProjectile>(), damage,
            snapshot.Knockback * 0.35f, owner);
        if (index < 0 || index >= Main.maxProjectiles)
            return -1;

        Projectile projectile = Main.projectile[index];
        projectile.originalDamage = damage;
        projectile.ArmorPenetration = 1000;
        projectile.CritChance = snapshot.CritChance;
        if (projectile.ModProjectile is ReaperBloodExecutionProjectile execution)
            execution.Configure(snapshot, target.whoAmI, phase, actionId, length, width);
        projectile.GetGlobalProjectile<MyGlobalProjectile>().ConfigureReaperProjectile(
            projectile, snapshot, ReaperHitKind.Special, phase,
            direction.ToRotation(), actionId);
        ReaperProjectileHelper.SyncNewProjectile(projectile);
        return index;
    }

    public override void SetDefaults()
    {
        Projectile.width = 6;
        Projectile.height = 6;
        Projectile.friendly = true;
        Projectile.hostile = false;
        Projectile.tileCollide = false;
        Projectile.ignoreWater = true;
        Projectile.penetrate = 1;
        Projectile.timeLeft = 16;
        Projectile.netImportant = true;
        Projectile.DamageType = DamageClass.Melee;
    }

    public override bool ShouldUpdatePosition() => false;
    public override bool? CanDamage() => configured && age is >= 4 and <= 6 ? null : false;
    public override bool? CanHitNPC(NPC target)
        => configured && target.whoAmI == targetIndex ? null : false;

    public override void AI()
    {
        if (Main.netMode == NetmodeID.Server && !serverAuthorized)
        {
            Projectile.Kill();
            return;
        }
        if (!configured)
            return;
        age++;
        if (targetIndex >= 0 && targetIndex < Main.maxNPCs
            && Main.npc[targetIndex].active)
        {
            Projectile.Center = Main.npc[targetIndex].Center;
        }
    }

    public override bool? Colliding(Rectangle projHitbox, Rectangle targetHitbox)
        => configured && targetIndex >= 0 && targetIndex < Main.maxNPCs
            && Main.npc[targetIndex].active
            && Main.npc[targetIndex].Hitbox.Intersects(targetHitbox);

    public override bool PreDraw(ref Color lightColor)
    {
        if (!configured || Main.dedServ)
            return false;
        float reveal = MathHelper.Clamp(age / 5f, 0f, 1f);
        float fade = 1f - MathHelper.Clamp((age - 7f) / 8f, 0f, 1f);
        DeathDomainPrimitiveTextureSystem.DrawBloodScar(Projectile.Center,
            Projectile.velocity.ToRotation(), length, width, fade, reveal);
        return false;
    }

    public override void SendExtraAI(BinaryWriter writer)
    {
        writer.Write(configured);
        if (!configured)
            return;
        snapshot.Write(writer);
        writer.Write((short)targetIndex);
        writer.Write((byte)Math.Clamp(phase, 0, byte.MaxValue));
        writer.Write(actionId);
        writer.Write(length);
        writer.Write(width);
        writer.Write((byte)Math.Clamp(age, 0, byte.MaxValue));
    }

    public override void ReceiveExtraAI(BinaryReader reader)
    {
        bool incoming = reader.ReadBoolean();
        if (!incoming)
        {
            if (Main.netMode != NetmodeID.Server)
                configured = false;
            return;
        }
        SickleCombatSnapshot incomingSnapshot = SickleCombatSnapshot.Read(reader);
        int incomingTarget = reader.ReadInt16();
        int incomingPhase = reader.ReadByte();
        int incomingAction = reader.ReadInt32();
        float incomingLength = reader.ReadSingle();
        float incomingWidth = reader.ReadSingle();
        int incomingAge = reader.ReadByte();
        if (Main.netMode == NetmodeID.Server)
            return;
        snapshot = incomingSnapshot;
        targetIndex = incomingTarget;
        phase = incomingPhase;
        actionId = incomingAction;
        length = incomingLength;
        width = incomingWidth;
        age = incomingAge;
        configured = true;
    }

    private void Configure(in SickleCombatSnapshot value, int target, int hitPhase,
        int parentActionId, float slashLength, float slashWidth)
    {
        snapshot = value;
        targetIndex = target;
        phase = hitPhase;
        actionId = parentActionId;
        length = MathHelper.Clamp(slashLength, 48f, 560f);
        width = MathHelper.Clamp(slashWidth, 7f, 96f);
        age = 0;
        configured = true;
        serverAuthorized = Main.netMode != NetmodeID.MultiplayerClient;
    }
}