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;
///
/// A non-damaging, server-authoritative Soul energy pickup. Energy is awarded only
/// after the shard reaches its owner; changing form before then destroys it.
///
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(), 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().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().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;
}
}