first commit, even though I've been "done" with the mod for like ever
This commit is contained in:
212
Projectiles/SIVANanite.cs
Normal file
212
Projectiles/SIVANanite.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Siva.Buffs;
|
||||
using Terraria;
|
||||
using Terraria.DataStructures;
|
||||
using Terraria.ID;
|
||||
using Terraria.ModLoader;
|
||||
using System;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
|
||||
namespace Siva.Projectiles
|
||||
{
|
||||
// This Example show how to implement simple homing projectile
|
||||
public class SIVANanite : ModProjectile
|
||||
{
|
||||
public static Texture2D glowMaskTexture;
|
||||
public static readonly int ProjectileType = ModContent.ProjectileType<SIVANanite>();
|
||||
|
||||
// These properties wrap the usual ai arrays for cleaner and easier to understand code.
|
||||
// Are we sticking to a target?
|
||||
public bool IsStickingToTarget {
|
||||
get => Projectile.ai[0] == 1f;
|
||||
set => Projectile.ai[0] = value ? 1f : 0f;
|
||||
}
|
||||
|
||||
// Index of the current target
|
||||
public int TargetWhoAmI {
|
||||
get => (int)Projectile.ai[1];
|
||||
set => Projectile.ai[1] = value;
|
||||
}
|
||||
|
||||
public float TimeAttached {
|
||||
get => Projectile.localAI[0];
|
||||
set => Projectile.localAI[0] = value;
|
||||
}
|
||||
|
||||
public float Damage
|
||||
{
|
||||
get => Projectile.localAI[1];
|
||||
set => Projectile.localAI[1] = value;
|
||||
}
|
||||
|
||||
public override void SetStaticDefaults() {
|
||||
ProjectileID.Sets.CultistIsResistantTo[Projectile.type] = true; // Make the cultist resistant to this projectile, as it's resistant to all homing projectiles.
|
||||
Main.projFrames[Projectile.type] = 4;
|
||||
}
|
||||
|
||||
// Setting the default parameters of the projectile
|
||||
// You can check most of Fields and Properties here https://github.com/tModLoader/tModLoader/wiki/Projectile-Class-Documentation
|
||||
public override void SetDefaults() {
|
||||
|
||||
Projectile.width = 16; // The width of projectile hitbox
|
||||
Projectile.height = 16; // The height of projectile hitbox
|
||||
Projectile.scale = .8f;
|
||||
Projectile.aiStyle = 0; // The ai style of the projectile (0 means custom AI). For more please reference the source code of Terraria
|
||||
Projectile.DamageType = DamageClass.Ranged; // What type of damage does this projectile affect?
|
||||
Projectile.friendly = true; // Can the projectile deal damage to enemies?
|
||||
Projectile.hostile = false; // Can the projectile deal damage to the player?
|
||||
Projectile.ignoreWater = true; // Does the projectile's speed be influenced by water?
|
||||
Projectile.tileCollide = false; // Can the projectile collide with tiles?
|
||||
Projectile.timeLeft = 180; // The live time for the projectile (60 = 1 second, so 420 is 7 seconds)
|
||||
Projectile.penetrate = -1;
|
||||
Projectile.CritChance = 0;
|
||||
}
|
||||
|
||||
public override void PostDraw(Color lightColor)
|
||||
{
|
||||
Texture2D NaniteGlow = new Texture2D()
|
||||
Main.EntitySpriteDraw()
|
||||
base.PostDraw(lightColor);
|
||||
}
|
||||
public override void AI()
|
||||
{
|
||||
Projectile.ai[2] += 1f;
|
||||
if (++Projectile.frameCounter >= 5) {
|
||||
Projectile.frameCounter = 0;
|
||||
// Or more compactly Projectile.frame = ++Projectile.frame % Main.projFrames[Projectile.type];
|
||||
if (++Projectile.frame >= Main.projFrames[Projectile.type])
|
||||
Projectile.frame = 0;
|
||||
}
|
||||
Lighting.AddLight(Projectile.position, Color.Red.ToVector3() * 0.7f);
|
||||
// Run either the Sticky AI or Normal AI
|
||||
// Separating into different methods helps keeps your AI clean
|
||||
// Main.NewText($"IsStickingToTarget = {IsStickingToTarget}"); //
|
||||
if (IsStickingToTarget)
|
||||
{
|
||||
// Main.NewText("Calling StickyAI()"); // debug text
|
||||
StickyAI();
|
||||
}
|
||||
else {
|
||||
NormalAI();
|
||||
}
|
||||
}
|
||||
|
||||
public void NormalAI()
|
||||
{
|
||||
float maxDetectRadius = 400f; // The maximum radius at which a projectile can detect a target
|
||||
float projSpeed = 7f; // The speed at which the projectile moves towards the target
|
||||
Projectile.damage = (int)Damage; // Sets the damage of the projectile to the damage it was created with.
|
||||
// Trying to find NPC closest to the projectile
|
||||
NPC closestNPC = FindClosestNPC(maxDetectRadius);
|
||||
if (closestNPC != null)
|
||||
{
|
||||
// If a target is found, change the velocity of the projectile and rotate it in the direction of the target
|
||||
Projectile.velocity = (closestNPC.Center - Projectile.Center).SafeNormalize(Vector2.Zero) * projSpeed;
|
||||
Projectile.rotation = Projectile.velocity.ToRotation();
|
||||
}
|
||||
else
|
||||
{
|
||||
Projectile.velocity = Vector2.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
private const int MaxTimeAttached = 540;
|
||||
public void StickyAI()
|
||||
{
|
||||
TimeAttached += 1f;
|
||||
// Main.NewText($"TimeAttached: {TimeAttached}"); // debug text
|
||||
int npcTarget = TargetWhoAmI;
|
||||
if (TimeAttached >= MaxTimeAttached) { // If the nanite has stuck for 9 seconds (or more)
|
||||
Projectile.Kill();
|
||||
}
|
||||
else if (Main.npc[npcTarget].active && !Main.npc[npcTarget].dontTakeDamage) {
|
||||
// If the target is active and can take damage
|
||||
// Set the projectile's position relative to the target's center
|
||||
Projectile.Center = Main.npc[npcTarget].Center - Projectile.velocity * 2f;
|
||||
Projectile.gfxOffY = Main.npc[npcTarget].gfxOffY;
|
||||
}
|
||||
else
|
||||
{
|
||||
Projectile.Kill();
|
||||
}
|
||||
/* Very fun but a bit OP
|
||||
else { // Otherwise, reset the timer to 3 seconds and unstick it
|
||||
Projectile.timeLeft = 180;
|
||||
IsStickingToTarget = false;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
public NPC FindClosestNPC(float maxDetectDistance)
|
||||
{
|
||||
NPC closestNPC = null;
|
||||
float sqrMaxDetectDistance = maxDetectDistance * maxDetectDistance;
|
||||
|
||||
foreach (NPC target in Main.npc)
|
||||
{
|
||||
if (target.CanBeChasedBy())
|
||||
{
|
||||
float sqrDistanceToTarget = Vector2.DistanceSquared(target.Center, Projectile.Center);
|
||||
if (sqrDistanceToTarget < sqrMaxDetectDistance)
|
||||
{
|
||||
sqrMaxDetectDistance = sqrDistanceToTarget;
|
||||
closestNPC = target;
|
||||
}
|
||||
}
|
||||
}
|
||||
return closestNPC;
|
||||
}
|
||||
|
||||
public override void OnSpawn(IEntitySource source)
|
||||
{
|
||||
Damage = Projectile.damage;
|
||||
}
|
||||
|
||||
public const int maxNanites = 100;
|
||||
private readonly Point[] attachedNanites = new Point[maxNanites];
|
||||
public override void OnHitNPC(NPC target, NPC.HitInfo hit, int damageDone)
|
||||
{
|
||||
// Main.NewText($"{damageDone}"); // debug text
|
||||
IsStickingToTarget = true; // we are sticking to a target
|
||||
Projectile.damage = 0; // Makes sure it can't deal damage, this is reset when NormalAI() is called again.
|
||||
TargetWhoAmI = target.whoAmI; // Set the target whoAmI
|
||||
Projectile.velocity = (target.Center - Projectile.Center) * 0.75f; // Change velocity based on delta center of targets (difference between entity centers)
|
||||
Projectile.netUpdate = true; // netUpdate this javelin
|
||||
int na = 0;
|
||||
foreach (var p in Main.ActiveProjectiles) // Refreses the duration of all of the nanites on the npc.
|
||||
{
|
||||
if (p.type == ProjectileType && p.ai[0] == 1f && p.ai[1] == TargetWhoAmI)
|
||||
{
|
||||
// Main.NewText("Refreshing duration of nanite"); // debug text
|
||||
p.timeLeft = 540;
|
||||
na++;
|
||||
}
|
||||
}
|
||||
// Main.NewText(na); // debug text
|
||||
// ExampleJavelinBuff handles the damage over time (DoT)
|
||||
target.AddBuff(Parasitism.BuffType, 540);
|
||||
}
|
||||
|
||||
public static void Create(int naniteCount, NPC target, int projDamage)
|
||||
{
|
||||
// Main.NewText($"naniteCount = {naniteCount}"); //debug text
|
||||
int i = 0;
|
||||
while(i < naniteCount) // I'm proud of this basic function, draws the amount of nanites in a parabola above the target
|
||||
{
|
||||
float k = naniteCount/2;
|
||||
// Main.NewText($"k = {k}"); //debug text
|
||||
int x = (i - ((int)Math.Floor(k)))*4;
|
||||
float y = 1.0f / 6.0f * x * x;
|
||||
y -= target.height;
|
||||
x *= 4;
|
||||
y *= 2;
|
||||
Projectile.NewProjectile(target.GetSource_FromThis(), Vector2.Add(target.Center, new Vector2(x, y)), Vector2.Zero, ProjectileType, projDamage, 0f, Main.myPlayer); // Might want to use Target.Top and make it not as high above them, big bosses have nanites spawn on them I think
|
||||
// Main.NewText($"({x},{y})"); //debug text
|
||||
i += 1;
|
||||
}
|
||||
// Main.NewText($"made {i} nanites off of {naniteCount}"); //debug text
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user