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>A persistent black wound with a smooth red rim.</summary>
public sealed class ReaperBloodScarProjectile : ModProjectile
{
    private SickleCombatSnapshot snapshot;
    private float scarLength;
    private float scarWidth;
    private int lifetime;
    private int seed;
    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, Vector2 center, Vector2 direction,
        float length, float width, int lifetime, float damageMultiplier,
        int phase, int actionId, int exactDamage = 0)
    {
        if (Main.netMode == NetmodeID.MultiplayerClient)
            return -1;
        direction = direction.SafeNormalize(Vector2.UnitX);
        int damage = exactDamage > 0
            ? exactDamage
            : Math.Max(1, (int)Math.Round(snapshot.Damage * damageMultiplier));
        int index = Projectile.NewProjectile(source, center, direction,
            ModContent.ProjectileType<ReaperBloodScarProjectile>(),
            damage,
            snapshot.Knockback * 0.12f, owner);
        if (index < 0 || index >= Main.maxProjectiles)
            return -1;

        Projectile projectile = Main.projectile[index];
        projectile.originalDamage = snapshot.Damage;
        if (exactDamage > 0)
            projectile.ArmorPenetration = 1000;
        int stableSeed = unchecked(actionId * 16777619 + phase * 486187739 + owner * 97);
        if (projectile.ModProjectile is ReaperBloodScarProjectile scar)
            scar.Configure(snapshot, length, width, lifetime, stableSeed);
        projectile.GetGlobalProjectile<MyGlobalProjectile>().ConfigureReaperProjectile(
            projectile, snapshot, ReaperHitKind.DamageOverTime, phase,
            direction.ToRotation(), actionId);
        projectile.localNPCHitCooldown = 18;
        ReaperProjectileHelper.SyncNewProjectile(projectile);
        return index;
    }

    public override void SetDefaults()
    {
        Projectile.width = 4;
        Projectile.height = 4;
        Projectile.friendly = true;
        Projectile.hostile = false;
        Projectile.tileCollide = false;
        Projectile.ignoreWater = true;
        Projectile.penetrate = -1;
        Projectile.timeLeft = 150;
        Projectile.netImportant = true;
        Projectile.DamageType = DamageClass.Melee;
        Projectile.usesLocalNPCImmunity = true;
        Projectile.localNPCHitCooldown = 18;
    }

    public override bool ShouldUpdatePosition() => false;
    public override bool? CanDamage()
        => configured && age >= 4 && Projectile.timeLeft > 15 ? null : false;

    public override void AI()
    {
        if (Main.netMode == NetmodeID.Server && !serverAuthorized)
        {
            Projectile.Kill();
            return;
        }
        if (!configured)
            return;
        age++;
        Projectile.rotation = Projectile.velocity.ToRotation();
    }

    public override bool? Colliding(Rectangle projHitbox, Rectangle targetHitbox)
    {
        Vector2 axis = Projectile.velocity.SafeNormalize(Vector2.UnitX);
        Vector2 start = Projectile.Center - axis * scarLength * 0.5f;
        Vector2 end = Projectile.Center + axis * scarLength * 0.5f;
        float collisionPoint = 0f;
        return Collision.CheckAABBvLineCollision(targetHitbox.TopLeft(), targetHitbox.Size(),
            start, end, Math.Max(3f, scarWidth * 0.64f), ref collisionPoint);
    }

    public override bool PreDraw(ref Color lightColor)
    {
        if (!configured || Main.dedServ)
            return false;

        float reveal = Smooth01(age / 9f);
        float fade = Smooth01(Projectile.timeLeft / 34f);
        DeathDomainPrimitiveTextureSystem.DrawBloodScar(Projectile.Center,
            Projectile.rotation, scarLength, scarWidth, fade, reveal);
        return false;
    }

    public override void SendExtraAI(BinaryWriter writer)
    {
        writer.Write(configured);
        if (!configured)
            return;
        snapshot.Write(writer);
        writer.Write(scarLength);
        writer.Write(scarWidth);
        writer.Write((short)Math.Clamp(lifetime, 1, short.MaxValue));
        writer.Write(seed);
        writer.Write((short)Math.Clamp(age, 0, short.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);
        float incomingLength = reader.ReadSingle();
        float incomingWidth = reader.ReadSingle();
        int incomingLifetime = reader.ReadInt16();
        int incomingSeed = reader.ReadInt32();
        int incomingAge = reader.ReadInt16();
        if (Main.netMode == NetmodeID.Server)
            return;
        snapshot = incomingSnapshot;
        scarLength = incomingLength;
        scarWidth = incomingWidth;
        lifetime = incomingLifetime;
        seed = incomingSeed;
        age = incomingAge;
        configured = true;
    }

    private void Configure(in SickleCombatSnapshot value, float length, float width,
        int activeLifetime, int shapeSeed)
    {
        snapshot = value;
        scarLength = MathHelper.Clamp(length, 32f, 2400f);
        scarWidth = MathHelper.Clamp(width, 5f, 180f);
        lifetime = Math.Clamp(activeLifetime, 30, 600);
        seed = shapeSeed;
        age = 0;
        configured = true;
        serverAuthorized = Main.netMode != NetmodeID.MultiplayerClient;
        Projectile.timeLeft = lifetime;
    }

    private static float Smooth01(float value)
    {
        value = MathHelper.Clamp(value, 0f, 1f);
        return value * value * (3f - 2f * value);
    }
}