tModLoader [Tutorial] Projectile Guide and Implementation: tModLoader Edition

Code:
public override void ModifyHitNPC(NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection)
        {
            if (target.type = NPCID.Zombie)
            {
                target.life = 0f;
            }
        }

Yet another error.

c:\Users\tehpootisman\Documents\My Games\Terraria\ModLoader\Mod Sources\NerfMod\Projectiles\ZombieDartProj.cs(32,17) : error CS0029: Cannot implicitly convert type 'int' to 'bool'

I had been getting this error before I requested help and using OnHitNPC, so I might've been on the right track before I messed it up.
 
Code:
public override void ModifyHitNPC(NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection)
        {
            if (target.type = NPCID.Zombie)
            {
                target.life = 0f;
            }
        }

Yet another error.



I had been getting this error before I requested help and using OnHitNPC, so I might've been on the right track before I messed it up.
Code:
public override void ModifyHitNPC(NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection)
        {
            if (target.type = NPCID.Zombie)
            {
                target.life = 0f;
            }
        }

Yet another error.



I had been getting this error before I requested help and using OnHitNPC, so I might've been on the right track before I messed it up.
You need to have "==", not "=" in your if statement. Here are some of what you can use for comparisons in conditionals.

Code:
== | Equals
>   | Greater
>= | Greater or Equals
<   | Less
<= | Less or Equals
 
I got a projectile (Named "CataclysmicLife") to spawn the "SpiritHeal" projectile upon hitting an NPC, but the SpiritHeal projectile does not heal me, even with the Spectre Hood equipped. Is there something I'm missing?
The mod loads fine, though. Here's the code:
Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Projectiles
{

    public class CataclysmicLife : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.width = 60;
            projectile.height = 140;
            projectile.friendly = true;
            projectile.melee = true;
            projectile.tileCollide = false;
            projectile.penetrate = -1;
            projectile.timeLeft = 900;
            projectile.light = 0.75f;
            projectile.extraUpdates = 1;
            projectile.tileCollide = false;
            projectile.ignoreWater = true;
        }
      
        public override void AI()
        {
            projectile.rotation = (float)Math.Atan2((double)projectile.velocity.Y, (double)projectile.velocity.X) + 1.75f; 
        }
      
        public override void OnHitNPC(NPC target, int damage, float knockback, bool crit)
        {
                int ShotAmt = 1;
                int spread = 1;
                float spreadMult = 0.9f;
                for (int i = 0; i < ShotAmt; i++)
                {
                    float vX = projectile.velocity.X + (float)Main.rand.Next(-spread, spread + 1) * spreadMult;
                    float vY = projectile.velocity.Y + (float)Main.rand.Next(-spread, spread + 1) * spreadMult;
                    Projectile.NewProjectile(projectile.position.X, projectile.position.Y, vX / 2, vY / 2, ProjectileID.SpiritHeal, projectile.damage * 5, -10f, 0);
                }
          
            target.AddBuff(BuffID.DryadsWardDebuff, 5 * 60);
        }
    }
}
 
I got a projectile (Named "CataclysmicLife") to spawn the "SpiritHeal" projectile upon hitting an NPC, but the SpiritHeal projectile does not heal me, even with the Spectre Hood equipped. Is there something I'm missing?
The mod loads fine, though. Here's the code:
Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Projectiles
{

    public class CataclysmicLife : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.width = 60;
            projectile.height = 140;
            projectile.friendly = true;
            projectile.melee = true;
            projectile.tileCollide = false;
            projectile.penetrate = -1;
            projectile.timeLeft = 900;
            projectile.light = 0.75f;
            projectile.extraUpdates = 1;
            projectile.tileCollide = false;
            projectile.ignoreWater = true;
        }
     
        public override void AI()
        {
            projectile.rotation = (float)Math.Atan2((double)projectile.velocity.Y, (double)projectile.velocity.X) + 1.75f;
        }
     
        public override void OnHitNPC(NPC target, int damage, float knockback, bool crit)
        {
                int ShotAmt = 1;
                int spread = 1;
                float spreadMult = 0.9f;
                for (int i = 0; i < ShotAmt; i++)
                {
                    float vX = projectile.velocity.X + (float)Main.rand.Next(-spread, spread + 1) * spreadMult;
                    float vY = projectile.velocity.Y + (float)Main.rand.Next(-spread, spread + 1) * spreadMult;
                    Projectile.NewProjectile(projectile.position.X, projectile.position.Y, vX / 2, vY / 2, ProjectileID.SpiritHeal, projectile.damage * 5, -10f, 0);
                }
         
            target.AddBuff(BuffID.DryadsWardDebuff, 5 * 60);
        }
    }
}
You'll need to set owner, ai0, and ai1 correctly. owner should be projectile.owner, ai0 should be closest player's whoami, and ai1 should be the lifesteal.

But, if you really want the spectre heal thing, just call projectile.ghostHeal(int dmg, Vector2 Position). I'm not sure how it works, but I do know that projectile.vampireHeal(int dmg, Vector2 Position) works for general purpose life steal. (dmg is how much to heal, position is where to spawn it from.)

In any case, you should probably study the source code, I don't know what exactly you are trying to do.


public static int NewProjectile(Vector2 position, Vector2 velocity, int Type, int Damage, float KnockBack, int Owner = 255, float ai0 = 0f, float ai1 = 0f)
 
You'll need to set owner, ai0, and ai1 correctly. owner should be projectile.owner, ai0 should be closest player's whoami, and ai1 should be the lifesteal.

But, if you really want the spectre heal thing, just call projectile.ghostHeal(int dmg, Vector2 Position). I'm not sure how it works, but I do know that projectile.vampireHeal(int dmg, Vector2 Position) works for general purpose life steal. (dmg is how much to heal, position is where to spawn it from.)

In any case, you should probably study the source code, I don't know what exactly you are trying to do.


public static int NewProjectile(Vector2 position, Vector2 velocity, int Type, int Damage, float KnockBack, int Owner = 255, float ai0 = 0f, float ai1 = 0f)
I was trying to make the projectile spawn the spectre heal orb when it hits an NPC, the orb just wasn't healing, unfortunately.
 
I was trying to make the projectile spawn the spectre heal orb when it hits an NPC, the orb just wasn't healing, unfortunately.
Like Jopo said, you would need to use the last two parameters of "Projectile.NewProjectile()" for it to function. However, just as Jopo said, you can use Projectile.ghostHeal(int dmg, Vector2 Position) to do the same thing with less work.

So in an "OnHitNPC" hook, you would just set it up like this...

Code:
public override void OnHitNPC(NPC target, int damage, float knockback, bool crit)
{
   Projectile.ghostHeal(damage, projectile.position);
}

So here is what ghostHeal looks like in code form...

Code:
// Terraria.Projectile
public void ghostHeal(int dmg, Vector2 Position)
{
    float num = 0.2f;
    num -= (float)this.numHits * 0.05f;
    if (num <= 0f)
    {
        return;
    }
    float num2 = (float)dmg * num;
    if ((int)num2 <= 0)
    {
        return;
    }
    if (Main.player[Main.myPlayer].lifeSteal <= 0f)
    {
        return;
    }
    Main.player[Main.myPlayer].lifeSteal -= num2;
    if (!this.magic)
    {
        return;
    }
    float num3 = 0f;
    int num4 = this.owner;
    for (int i = 0; i < 255; i++)
    {
        if (Main.player[i].active && !Main.player[i].dead && ((!Main.player[this.owner].hostile && !Main.player[i].hostile) || Main.player[this.owner].team == Main.player[i].team))
        {
            float num5 = Math.Abs(Main.player[i].position.X + (float)(Main.player[i].width / 2) - this.position.X + (float)(this.width / 2)) + Math.Abs(Main.player[i].position.Y + (float)(Main.player[i].height / 2) - this.position.Y + (float)(this.height / 2));
            if (num5 < 1200f && (float)(Main.player[i].statLifeMax2 - Main.player[i].statLife) > num3)
            {
                num3 = (float)(Main.player[i].statLifeMax2 - Main.player[i].statLife);
                num4 = i;
            }
        }
    }
    Projectile.NewProjectile(Position.X, Position.Y, 0f, 0f, 298, 0, 0f, this.owner, (float)num4, num2);
}

From here, it will get the damage multiplied by a multiplier "num" which is in regards to number of hits. It does a number of checks to see if lifesteal is less than 0, and if it is magic. These checks if less than zero or is not magic will just make it not work. After that, it just checks all players if they're distance is less than 1200 pixels and assigns that player as the person to heal. At the end, it spawns the spirit heal with num4 being the player it identified as the one needing healing and num2 as the value of health that player will receive.
 
Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Projectiles     //We need this to basically indicate the folder where it is to be read from, so you the texture will load correctly.
{
    public class ValkyrieBlade : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.width = 80;
            projectile.height = 80;
            projectile.aiStyle = -1;
            projectile.friendly = true;
            projectile.melee = true;
            projectile.penetrate = 3;
            projectile.tileCollide = false;
            projectile.ignoreWater = true;
        }

        // NewTileCollideStyle is the replacement for TileCollideStyle, but we will support the old TileCollideStyle until a new vanilla release, at which time NewTileCollideStyle will become TileCollideStyle
        public override bool TileCollideStyle(ref int width, ref int height, ref bool fallThrough)
        {
            // For going through platforms and such, javelins use a tad smaller size
            width = 50;
            height = 50;
            return true;
        }

        public override bool? Colliding(Rectangle projHitbox, Rectangle targetHitbox)
        {
            // Inflate some target hitboxes if they are beyond 8,8 size
            if (targetHitbox.Width > 8 && targetHitbox.Height > 8)
            {
                targetHitbox.Inflate(-targetHitbox.Width / 8, -targetHitbox.Height / 8);
            }
            // Return if the hitboxes intersects, which means the javelin collides or not
            return projHitbox.Intersects(targetHitbox);
        }

        public override void Kill(int timeLeft)
        {
            Main.PlaySound(0, (int)projectile.position.X, (int)projectile.position.Y, 1, 1f, 0f); // Play a death sound
        }

        // Here's an example on how you could make your AI even more readable, by giving AI fields more descriptive names
        // These are not used in AI, but it is good practice to apply some form like this to keep things organized
        public float isStickingToTarget
        {
            get { return projectile.ai[0]; }
            set { projectile.ai[0] = value; }
        }

        public float targetWhoAmI
        {
            get { return projectile.ai[1]; }
            set { projectile.ai[1] = value; }
        }

        public override void ModifyHitNPC(NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection)
        {
            // If you'd use the example above, you'd do: isStickingToTarget = 1f;
            // and: targetWhoAmI = (float)target.whoAmI;
            projectile.ai[0] = 1f; // Set ai0 state to 1f
            projectile.ai[1] = (float)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
            target.AddBuff(CursedInferno, 10 * 60, false); // Unsure what this does, but it was in the vanilla code

            projectile.damage = 0; // Makes sure the sticking javelins do not deal damage anymore
            // The following code handles the javelin sticking to the enemy hit.

            int maxStickingJavelins = 6; // This seems to be the maximum amount of javelins being able to attach
            Point[] stickingJavelins = new Point[maxStickingJavelins]; // The point array holding for sticking javelins
            int javelinIndex = 0; // The javelin index
            for (int i = 0; i < Main.maxProjectiles; i++) // Loop all projectiles
            {
                Projectile currentProjectile = Main.projectile[i];
                if (i != projectile.whoAmI  // Make sure the looped projectile is not the current javelin
                    && currentProjectile.active // Make sure the projectile is active
                    && currentProjectile.owner == Main.myPlayer // Make sure the projectile's owner is the client's player
                    && currentProjectile.type == projectile.type // Make sure the projectile is of the same type as this javelin
                    && currentProjectile.ai[0] == 1f // Make sure ai0 state is set to 1f (set earlier in ModifyHitNPC)
                    && currentProjectile.ai[1] == (float)target.whoAmI) // Make sure ai1 is set to the target whoAmI (set earlier in ModifyHitNPC)
                {
                    stickingJavelins[javelinIndex++] = new Point(i, currentProjectile.timeLeft); // Add the current projectile's index and timeleft to the point array
                    if (javelinIndex >= stickingJavelins.Length) // If the javelin's index is bigger than or equal to the point array's length, break
                    {
                        break;
                    }
                }
            }
            // Here we loop the other javelins if new javelin needs to take an older javelin's place.
            if (javelinIndex >= stickingJavelins.Length)
            {
                int oldJavelinIndex = 0;
                // Loop our point array
                for (int i = 1; i < stickingJavelins.Length; i++)
                {
                    // Remove the already existing javelin if it's timeLeft value (which is the Y value in our point array) is smaller than the new javelin's timeLeft
                    if (stickingJavelins[i].Y < stickingJavelins[oldJavelinIndex].Y)
                    {
                        oldJavelinIndex = i; // Remember the index of the removed javelin
                    }
                }
                // Remember that the X value in our point array was equal to the index of that javelin, so it's used here to kill it.
                Main.projectile[stickingJavelins[oldJavelinIndex].X].Kill();
            }
        }
      
        // Added these 2 constant to showcase how you could make AI code cleaner by doing this
        // Change this number if you want to alter how long the javelin can travel at a constant speed
        private const float maxTicks = 56.25f;
        // Change this number if you want to alter how the alpha changes
        private const int alphaReduction = 25;

        public override void AI()
        {
            // Slowly remove alpha as it is present
            if (projectile.alpha > 0)
            {
                projectile.alpha -= alphaReduction;
            }
            // If alpha gets lower than 0, set it to 0
            if (projectile.alpha < 0)
            {
                projectile.alpha = 0;
            }
            // If ai0 is 0f, run this code. This is the 'movement' code for the javelin as long as it isn't sticking to a target
            if (projectile.ai[0] == 0f)
            {
                projectile.ai[1] += 1f;
                // For a little while, the javelin will travel with the same speed, but after this, the javelin drops velocity very quickly.
                if (projectile.ai[1] >= maxTicks)
                {
                    // Change these multiplication factors to alter the javelin's movement change after reaching maxTicks
                    float velXmult = 0.98f; // x velocity factor, every AI update the x velocity will be 98% of the original speed
                    float velYmult = 0.35f; // y velocity factor, every AI update the y velocity will be be 0.35f bigger of the original speed, causing the javelin to drop to the ground
                    projectile.ai[1] = maxTicks; // set ai1 to maxTicks continuously
                    projectile.velocity.X = projectile.velocity.X * velXmult;
                    projectile.velocity.Y = projectile.velocity.Y + velYmult;
                }
                // Make sure to set the rotation accordingly to the velocity, and add some to work around the sprite's rotation
                projectile.rotation = projectile.velocity.ToRotation() + MathHelper.ToRadians(90f); // Please notice the MathHelper usage, offset the rotation by 90 degrees (to radians because rotation uses radians) because the sprite's rotation is not aligned!
            }
            // This code is ran when the javelin is sticking to a target
            if (projectile.ai[0] == 1f)
            {
                // These 2 could probably be moved to the ModifyNPCHit hook, but in vanilla they are present in the AI
                projectile.ignoreWater = true; // Make sure the projectile ignores water
                projectile.tileCollide = false; // Make sure the projectile doesn't collide with tiles anymore
                int aiFactor = 10; // Change this factor to change the 'lifetime' of this sticking javelin
                bool killProj = false; // if true, kill projectile at the end
                bool hitEffect = true; // if true, perform a hit effect
                projectile.localAI[0] += 1f;
                // Every 30 ticks, the javelin will perform a hit effect
                hitEffect = projectile.localAI[0] % 30f == 0f;
                int projTargetIndex = (int)projectile.ai[1];
                if (projectile.localAI[0] >= (float)(60 * aiFactor)) // If it's time for this javelin to die, kill it
                {
                    killProj = true;
                }
                else if (projTargetIndex < 0 || projTargetIndex >= 200) // If the index is past its limits, kill the javelin
                {
                    killProj = true;
                }
                else if (Main.npc[projTargetIndex].active && !Main.npc[projTargetIndex].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[projTargetIndex].Center - projectile.velocity * 2f;
                    projectile.gfxOffY = Main.npc[projTargetIndex].gfxOffY;
                    if (hitEffect) // Perform a hit effect here
                    {
                        Main.npc[projTargetIndex].HitEffect(0, 1.0);
                    }
                }
                else // Otherwise, kill the projectile
                {
                    killProj = true;
                }

                if (killProj) // Kill the projectile
                {
                    projectile.Kill();
                }
            }
        }
    }
}
A few noob-ish questions regarding this:
1) How do I make the projectile's speed higher?
2) Why isn't the projectile doing additional damage to the NPC when it hits said enemy? It only just hits once, and sticks there.
3) The sprite when the projectile spawns is facing a diagonal angle, so do I have to change the sprite to make it face upwards, so it faces the correct way?
 
Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Projectiles     //We need this to basically indicate the folder where it is to be read from, so you the texture will load correctly.
{
    public class ValkyrieBlade : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.width = 80;
            projectile.height = 80;
            projectile.aiStyle = -1;
            projectile.friendly = true;
            projectile.melee = true;
            projectile.penetrate = 3;
            projectile.tileCollide = false;
            projectile.ignoreWater = true;
        }

        // NewTileCollideStyle is the replacement for TileCollideStyle, but we will support the old TileCollideStyle until a new vanilla release, at which time NewTileCollideStyle will become TileCollideStyle
        public override bool TileCollideStyle(ref int width, ref int height, ref bool fallThrough)
        {
            // For going through platforms and such, javelins use a tad smaller size
            width = 50;
            height = 50;
            return true;
        }

        public override bool? Colliding(Rectangle projHitbox, Rectangle targetHitbox)
        {
            // Inflate some target hitboxes if they are beyond 8,8 size
            if (targetHitbox.Width > 8 && targetHitbox.Height > 8)
            {
                targetHitbox.Inflate(-targetHitbox.Width / 8, -targetHitbox.Height / 8);
            }
            // Return if the hitboxes intersects, which means the javelin collides or not
            return projHitbox.Intersects(targetHitbox);
        }

        public override void Kill(int timeLeft)
        {
            Main.PlaySound(0, (int)projectile.position.X, (int)projectile.position.Y, 1, 1f, 0f); // Play a death sound
        }

        // Here's an example on how you could make your AI even more readable, by giving AI fields more descriptive names
        // These are not used in AI, but it is good practice to apply some form like this to keep things organized
        public float isStickingToTarget
        {
            get { return projectile.ai[0]; }
            set { projectile.ai[0] = value; }
        }

        public float targetWhoAmI
        {
            get { return projectile.ai[1]; }
            set { projectile.ai[1] = value; }
        }

        public override void ModifyHitNPC(NPC target, ref int damage, ref float knockback, ref bool crit, ref int hitDirection)
        {
            // If you'd use the example above, you'd do: isStickingToTarget = 1f;
            // and: targetWhoAmI = (float)target.whoAmI;
            projectile.ai[0] = 1f; // Set ai0 state to 1f
            projectile.ai[1] = (float)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
            target.AddBuff(CursedInferno, 10 * 60, false); // Unsure what this does, but it was in the vanilla code

            projectile.damage = 0; // Makes sure the sticking javelins do not deal damage anymore
            // The following code handles the javelin sticking to the enemy hit.

            int maxStickingJavelins = 6; // This seems to be the maximum amount of javelins being able to attach
            Point[] stickingJavelins = new Point[maxStickingJavelins]; // The point array holding for sticking javelins
            int javelinIndex = 0; // The javelin index
            for (int i = 0; i < Main.maxProjectiles; i++) // Loop all projectiles
            {
                Projectile currentProjectile = Main.projectile[i];
                if (i != projectile.whoAmI  // Make sure the looped projectile is not the current javelin
                    && currentProjectile.active // Make sure the projectile is active
                    && currentProjectile.owner == Main.myPlayer // Make sure the projectile's owner is the client's player
                    && currentProjectile.type == projectile.type // Make sure the projectile is of the same type as this javelin
                    && currentProjectile.ai[0] == 1f // Make sure ai0 state is set to 1f (set earlier in ModifyHitNPC)
                    && currentProjectile.ai[1] == (float)target.whoAmI) // Make sure ai1 is set to the target whoAmI (set earlier in ModifyHitNPC)
                {
                    stickingJavelins[javelinIndex++] = new Point(i, currentProjectile.timeLeft); // Add the current projectile's index and timeleft to the point array
                    if (javelinIndex >= stickingJavelins.Length) // If the javelin's index is bigger than or equal to the point array's length, break
                    {
                        break;
                    }
                }
            }
            // Here we loop the other javelins if new javelin needs to take an older javelin's place.
            if (javelinIndex >= stickingJavelins.Length)
            {
                int oldJavelinIndex = 0;
                // Loop our point array
                for (int i = 1; i < stickingJavelins.Length; i++)
                {
                    // Remove the already existing javelin if it's timeLeft value (which is the Y value in our point array) is smaller than the new javelin's timeLeft
                    if (stickingJavelins[i].Y < stickingJavelins[oldJavelinIndex].Y)
                    {
                        oldJavelinIndex = i; // Remember the index of the removed javelin
                    }
                }
                // Remember that the X value in our point array was equal to the index of that javelin, so it's used here to kill it.
                Main.projectile[stickingJavelins[oldJavelinIndex].X].Kill();
            }
        }
     
        // Added these 2 constant to showcase how you could make AI code cleaner by doing this
        // Change this number if you want to alter how long the javelin can travel at a constant speed
        private const float maxTicks = 56.25f;
        // Change this number if you want to alter how the alpha changes
        private const int alphaReduction = 25;

        public override void AI()
        {
            // Slowly remove alpha as it is present
            if (projectile.alpha > 0)
            {
                projectile.alpha -= alphaReduction;
            }
            // If alpha gets lower than 0, set it to 0
            if (projectile.alpha < 0)
            {
                projectile.alpha = 0;
            }
            // If ai0 is 0f, run this code. This is the 'movement' code for the javelin as long as it isn't sticking to a target
            if (projectile.ai[0] == 0f)
            {
                projectile.ai[1] += 1f;
                // For a little while, the javelin will travel with the same speed, but after this, the javelin drops velocity very quickly.
                if (projectile.ai[1] >= maxTicks)
                {
                    // Change these multiplication factors to alter the javelin's movement change after reaching maxTicks
                    float velXmult = 0.98f; // x velocity factor, every AI update the x velocity will be 98% of the original speed
                    float velYmult = 0.35f; // y velocity factor, every AI update the y velocity will be be 0.35f bigger of the original speed, causing the javelin to drop to the ground
                    projectile.ai[1] = maxTicks; // set ai1 to maxTicks continuously
                    projectile.velocity.X = projectile.velocity.X * velXmult;
                    projectile.velocity.Y = projectile.velocity.Y + velYmult;
                }
                // Make sure to set the rotation accordingly to the velocity, and add some to work around the sprite's rotation
                projectile.rotation = projectile.velocity.ToRotation() + MathHelper.ToRadians(90f); // Please notice the MathHelper usage, offset the rotation by 90 degrees (to radians because rotation uses radians) because the sprite's rotation is not aligned!
            }
            // This code is ran when the javelin is sticking to a target
            if (projectile.ai[0] == 1f)
            {
                // These 2 could probably be moved to the ModifyNPCHit hook, but in vanilla they are present in the AI
                projectile.ignoreWater = true; // Make sure the projectile ignores water
                projectile.tileCollide = false; // Make sure the projectile doesn't collide with tiles anymore
                int aiFactor = 10; // Change this factor to change the 'lifetime' of this sticking javelin
                bool killProj = false; // if true, kill projectile at the end
                bool hitEffect = true; // if true, perform a hit effect
                projectile.localAI[0] += 1f;
                // Every 30 ticks, the javelin will perform a hit effect
                hitEffect = projectile.localAI[0] % 30f == 0f;
                int projTargetIndex = (int)projectile.ai[1];
                if (projectile.localAI[0] >= (float)(60 * aiFactor)) // If it's time for this javelin to die, kill it
                {
                    killProj = true;
                }
                else if (projTargetIndex < 0 || projTargetIndex >= 200) // If the index is past its limits, kill the javelin
                {
                    killProj = true;
                }
                else if (Main.npc[projTargetIndex].active && !Main.npc[projTargetIndex].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[projTargetIndex].Center - projectile.velocity * 2f;
                    projectile.gfxOffY = Main.npc[projTargetIndex].gfxOffY;
                    if (hitEffect) // Perform a hit effect here
                    {
                        Main.npc[projTargetIndex].HitEffect(0, 1.0);
                    }
                }
                else // Otherwise, kill the projectile
                {
                    killProj = true;
                }

                if (killProj) // Kill the projectile
                {
                    projectile.Kill();
                }
            }
        }
    }
}
A few noob-ish questions regarding this:
1) How do I make the projectile's speed higher?
2) Why isn't the projectile doing additional damage to the NPC when it hits said enemy? It only just hits once, and sticks there.
3) The sprite when the projectile spawns is facing a diagonal angle, so do I have to change the sprite to make it face upwards, so it faces the correct way?

1. Depends on your shoot speed in your item if you are talking about initial take off speed. If ongoing speed, there are two multipliers in this code...

Code:
velXMult
velYMult

To increase speed in the x direction, make the velXMult go higher than 1, if slower in the x direction, set lower than 1 but greater than 0.

To increase speed in the y direction (going down), make the velYMult go higher than 1, and if slower fall speed, set lower than 1 but greater than 0.

2. There's no code indicating it should damage an npc while sticking to the NPC. However, the "addBuff" is the damage I guess.

Code:
target.AddBuff(CursedInferno, 10 * 60, false);

This SHOULD be adding "CursedInferno" buff, however there is no BuffID to it so it shouldn't be working.

The 10 * 60 just indicates 10 * 60 ticks (60 ticks being 1 second), so this is the buff time

false indicates it doesn't bother sending the client a message

3. The code that works the rotation is

Code:
projectile.rotation = projectile.velocity.ToRotation() + MathHelper.ToRadians(90f);

If your sprite is pointing to the top right corner, then change "90f" to "45f", since things pointing to the top right corner tend to be 45 degrees.
 
1. Depends on your shoot speed in your item if you are talking about initial take off speed. If ongoing speed, there are two multipliers in this code...

Code:
velXMult
velYMult

To increase speed in the x direction, make the velXMult go higher than 1, if slower in the x direction, set lower than 1 but greater than 0.

To increase speed in the y direction (going down), make the velYMult go higher than 1, and if slower fall speed, set lower than 1 but greater than 0.

2. There's no code indicating it should damage an npc while sticking to the NPC. However, the "addBuff" is the damage I guess.

Code:
target.AddBuff(CursedInferno, 10 * 60, false);

This SHOULD be adding "CursedInferno" buff, however there is no BuffID to it so it shouldn't be working.

The 10 * 60 just indicates 10 * 60 ticks (60 ticks being 1 second), so this is the buff time

false indicates it doesn't bother sending the client a message

3. The code that works the rotation is

Code:
projectile.rotation = projectile.velocity.ToRotation() + MathHelper.ToRadians(90f);

If your sprite is pointing to the top right corner, then change "90f" to "45f", since things pointing to the top right corner tend to be 45 degrees.
Thanks :) Except for one thing, the projectile's velocity during the "private const float maxTicks = 56.25f;" line is a little too slow, how do I adjust it's speed to make it fast during the "maxTicks" line? I want it to be about the same speed as the Daybreak.


P.S While I'm at it, is there guides about Custom Debuffs?
 
Last edited:
Are these all the sprites that I need for a Whip-Like Weapon? And does the size matter for these sprites?
DirigonHead.png
Dirigon Head
Width: 46
Height: 46

DirigonBlade.png
Dirigon Blade
Width: 46
Height: 26

DirigonHandle.png
Dirigon Handle
Width: 46
Height: 40

Solar Eruption Sprite.png
Solar Eruption Sprite
Dirigon Sprite.png
Dirigon Sprite

Edit: Almost forgot about coding, whoops ^-^; Is the coding right?
Code:
 Note this is just the item, not the Head of the Weapon :3
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Items.Weapons.Dirigon
{
    public class Dirigon : ModItem
    {
        public override void SetDefaults()
        {
            DisplayName.SetDefault("Dirigon");
            item.useStyle = 5;
            item.useAnimation = 20;
            item.useTime = 20;
            item.shootSpeed = 24f;
            item.knockBack = 2f;
            item.width = 16;
            item.height = 16;
            item.UseSound = SoundID.Item116;
            item.shoot = 611;
            item.rare = 10;
            item.value = Item.sellPrice(1, 0, 0, 0);
            item.noMelee = true;
            item.noUseGraphic = true;
            item.channel = true;
            item.autoReuse = true;
            item.melee = true;
            item.damage = 1000;
            item.shoot = mod.ProjectileType("DirigonHandle");
            return;
        }
    }
}

Code:
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Projectiles.Dirigon
{
    public class DirigonBlade : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.CloneDefaults(ProjectileID.SolarWhipSword);
            DisplayName.SetDefault("Dirigon Blade");
            aiType = ProjectileID.SolarWhipSword;
        }
        public override void AI()
        {
            {
                Player player = Main.player[projectile.owner];
                float num = 1.57079637f;
                Vector2 vector = player.RotatedRelativePoint(player.MountedCenter, true);
                projectile.ai[0] += 1f;
                int num2 = 0;
                if (projectile.ai[0] >= 40f)
                {
                    num2++;
                }
                if (projectile.ai[0] >= 80f)
                {
                    num2++;
                }
                if (projectile.ai[0] >= 120f)
                {
                    num2++;
                }
                int num3 = 24;
                int num4 = 6;
                projectile.ai[1] += 1f;
                bool flag = false;
                if (projectile.ai[1] >= (float)(num3 - num4 * num2))
                {
                    projectile.ai[1] = 0f;
                    flag = true;
                }
                projectile.frameCounter += 1 + num2;
                if (projectile.frameCounter >= 4)
                {
                    projectile.frameCounter = 0;
                    projectile.frame++;
                    if (projectile.frame >= 6)
                    {
                        projectile.frame = 0;
                    }
                }
                if (projectile.soundDelay <= 0)
                {
                    projectile.soundDelay = num3 - num4 * num2;
                    if (projectile.ai[0] != 1f)
                    {
                        Main.PlaySound(SoundID.Item91, projectile.position);
                    }
                }
                if (projectile.ai[1] == 1f && projectile.ai[0] != 1f)
                {
                    Vector2 vector2 = Vector2.UnitX * 24f;
                    vector2 = vector2.RotatedBy((double)(projectile.rotation - 1.57079637f), default(Vector2));
                    Vector2 value = projectile.Center + vector2;
                    for (int i = 0; i < 2; i++)
                    {
                        int num5 = Dust.NewDust(value - Vector2.One * 8f, 16, 16, 135, projectile.velocity.X / 2f, projectile.velocity.Y / 2f, 100, default(Color), 1f);
                        Main.dust[num5].velocity *= 0.66f;
                        Main.dust[num5].noGravity = true;
                        Main.dust[num5].scale = 1.4f;
                    }
                }
                if (flag && Main.myPlayer == projectile.owner)
                {
                    bool flag2 = player.channel && player.CheckMana(player.inventory[player.selectedItem].mana, true, false) && !player.noItems && !player.CCed;
                    if (flag2)
                    {
                        float scaleFactor = player.inventory[player.selectedItem].shootSpeed * projectile.scale;
                        Vector2 value2 = vector;
                        Vector2 value3 = Main.screenPosition + new Vector2((float)Main.mouseX, (float)Main.mouseY) - value2;
                        if (player.gravDir == -1f)
                        {
                            value3.Y = (float)(Main.screenHeight - Main.mouseY) + Main.screenPosition.Y - value2.Y;
                        }
                        Vector2 vector3 = Vector2.Normalize(value3);
                        if (float.IsNaN(vector3.X) || float.IsNaN(vector3.Y))
                        {
                            vector3 = -Vector2.UnitY;
                        }
                        vector3 *= scaleFactor;
                        if (vector3.X != projectile.velocity.X || vector3.Y != projectile.velocity.Y)
                        {
                            projectile.netUpdate = true;
                        }
                        projectile.velocity = vector3;
                        int num6 = 440;
                        float scaleFactor2 = 14f;
                        int num7 = 7;
                        for (int j = 0; j < 2; j++)
                        {
                            value2 = projectile.Center + new Vector2((float)Main.rand.Next(-num7, num7 + 1), (float)Main.rand.Next(-num7, num7 + 1));
                            Vector2 spinningpoint = Vector2.Normalize(projectile.velocity) * scaleFactor2;
                            spinningpoint = spinningpoint.RotatedBy(Main.rand.NextDouble() * 0.19634954631328583 - 0.098174773156642914, default(Vector2));
                            if (float.IsNaN(spinningpoint.X) || float.IsNaN(spinningpoint.Y))
                            {
                                spinningpoint = -Vector2.UnitY;
                            }
                            Projectile.NewProjectile(value2.X, value2.Y, spinningpoint.X, spinningpoint.Y, num6, projectile.damage, projectile.knockBack, projectile.owner, 0f, 0f);
                        }
                    }
                    else
                    {
                        projectile.Kill();
                    }
                }
            }
        }
    }
}

Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Projectiles.Dirigon
{
    public class DirigonHandle : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.CloneDefaults(ProjectileID.SolarCounter);
            DisplayName.SetDefault("Dirigon Handle");
        }
        public override void AI()
        {
            projectile.ai[1] += 0.01f;
            projectile.scale = projectile.ai[1];
            projectile.ai[0] += 1f;
            if (projectile.ai[0] >= (float)(3 * Main.projFrames[projectile.type]))
            {
                projectile.Kill();
                return;
            }
            if (++projectile.frameCounter >= 3)
            {
                projectile.frameCounter = 0;
                if (++projectile.frame >= Main.projFrames[projectile.type])
                {
                    projectile.hide = true;
                }
            }
            projectile.alpha -= 63;
            if (projectile.alpha < 0)
            {
                projectile.alpha = 0;
            }
            bool flag56 = projectile.type == 612;
            bool flag57 = projectile.type == 624;
            if (flag56)
            {
                Lighting.AddLight(projectile.Center, 0.9f, 0.8f, 0.6f);
            }
            if (projectile.ai[0] == 1f)
            {
                projectile.position = projectile.Center;
                projectile.width = (projectile.height = (int)(52f * projectile.scale));
                projectile.Center = projectile.position;
                projectile.Damage();
                if (flag56)
                {
                    Main.PlaySound(SoundID.Item14, projectile.position);
                    for (int num980 = 0; num980 < 4; num980++)
                    {
                        int num981 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 31, 0f, 0f, 100, default(Color), 1.5f);
                        Main.dust[num981].position = projectile.Center + Vector2.UnitY.RotatedByRandom(3.1415927410125732) * (float)Main.rand.NextDouble() * (float)projectile.width / 2f;
                    }
                    for (int num982 = 0; num982 < 10; num982++)
                    {
                        int num983 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 6, 0f, 0f, 200, default(Color), 2.7f);
                        Main.dust[num983].position = projectile.Center + Vector2.UnitY.RotatedByRandom(3.1415927410125732) * (float)Main.rand.NextDouble() * (float)projectile.width / 2f;
                        Main.dust[num983].noGravity = true;
                        Main.dust[num983].velocity *= 3f;
                        num983 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 6, 0f, 0f, 100, default(Color), 1.5f);
                        Main.dust[num983].position = projectile.Center + Vector2.UnitY.RotatedByRandom(3.1415927410125732) * (float)Main.rand.NextDouble() * (float)projectile.width / 2f;
                        Main.dust[num983].velocity *= 2f;
                        Main.dust[num983].noGravity = true;
                        Main.dust[num983].fadeIn = 2.5f;
                    }
                    for (int num984 = 0; num984 < 5; num984++)
                    {
                        int num985 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 6, 0f, 0f, 0, default(Color), 2.7f);
                        Main.dust[num985].position = projectile.Center + Vector2.UnitX.RotatedByRandom(3.1415927410125732).RotatedBy((double)projectile.velocity.ToRotation(), default(Vector2)) * (float)projectile.width / 2f;
                        Main.dust[num985].noGravity = true;
                        Main.dust[num985].velocity *= 3f;
                    }
                    for (int num986 = 0; num986 < 10; num986++)
                    {
                        int num987 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 31, 0f, 0f, 0, default(Color), 1.5f);
                        Main.dust[num987].position = projectile.Center + Vector2.UnitX.RotatedByRandom(3.1415927410125732).RotatedBy((double)projectile.velocity.ToRotation(), default(Vector2)) * (float)projectile.width / 2f;
                        Main.dust[num987].noGravity = true;
                        Main.dust[num987].velocity *= 3f;
                    }
                }
                if (flag57)
                {
                    Main.PlaySound(SoundID.Item14, projectile.position);
                    for (int num988 = 0; num988 < 20; num988++)
                    {
                        int num989 = Dust.NewDust(projectile.position, projectile.width, projectile.height, 135, 0f, 0f, 100, default(Color), 1.5f);
                        Main.dust[num989].position = projectile.Center + Vector2.UnitY.RotatedByRandom(3.1415927410125732) * (float)Main.rand.NextDouble() * (float)projectile.width / 2f;
                        Main.dust[num989].velocity *= 2f;
                        Main.dust[num989].noGravity = true;
                        Main.dust[num989].fadeIn = 2.5f;
                    }
                    for (int num990 = 0; num990 < 15; num990++)
                    {
                        int num991 = Dust.NewDust(projectile.position, projectile.width, projectile.height, 135, 0f, 0f, 0, default(Color), 2.7f);
                        Main.dust[num991].position = projectile.Center + Vector2.UnitX.RotatedByRandom(3.1415927410125732).RotatedBy((double)projectile.velocity.ToRotation(), default(Vector2)) * (float)projectile.width / 2f;
                        Main.dust[num991].noGravity = true;
                        Main.dust[num991].velocity *= 3f;
                    }
                    float num992 = (float)Main.rand.NextDouble() * 6.28318548f;
                    float num993 = (float)Main.rand.NextDouble() * 6.28318548f;
                    float num994 = (float)Main.rand.NextDouble() * 6.28318548f;
                    float num995 = 7f + (float)Main.rand.NextDouble() * 7f;
                    float num996 = 7f + (float)Main.rand.NextDouble() * 7f;
                    float num997 = 7f + (float)Main.rand.NextDouble() * 7f;
                    float num998 = num995;
                    if (num996 > num998)
                    {
                        num998 = num996;
                    }
                    if (num997 > num998)
                    {
                        num998 = num997;
                    }
                    for (int num999 = 0; num999 < 200; num999++)
                    {
                        int num1000 = 135;
                        float scaleFactor14 = num998;
                        if (num999 > 50)
                        {
                            scaleFactor14 = num996;
                        }
                        if (num999 > 100)
                        {
                            scaleFactor14 = num995;
                        }
                        if (num999 > 150)
                        {
                            scaleFactor14 = num997;
                        }
                        int num1001 = Dust.NewDust(projectile.position, 6, 6, num1000, 0f, 0f, 100, default(Color), 1f);
                        Vector2 vector123 = Main.dust[num1001].velocity;
                        Main.dust[num1001].position = projectile.Center;
                        vector123.Normalize();
                        vector123 *= scaleFactor14;
                        if (num999 > 150)
                        {
                            vector123.Y *= 0.5f;
                            vector123 = vector123.RotatedBy((double)num994, default(Vector2));
                        }
                        else if (num999 > 100)
                        {
                            vector123.X *= 0.5f;
                            vector123 = vector123.RotatedBy((double)num992, default(Vector2));
                        }
                        else if (num999 > 50)
                        {
                            vector123.Y *= 0.5f;
                            vector123 = vector123.RotatedBy((double)num993, default(Vector2));
                        }
                        Main.dust[num1001].velocity *= 0.2f;
                        Main.dust[num1001].velocity += vector123;
                        if (num999 <= 200)
                        {
                            Main.dust[num1001].scale = 2f;
                            Main.dust[num1001].noGravity = true;
                            Main.dust[num1001].fadeIn = Main.rand.NextFloat() * 2f;
                            if (Main.rand.Next(4) == 0)
                            {
                                Main.dust[num1001].fadeIn = 2.5f;
                            }
                            Main.dust[num1001].noLight = true;
                            if (num999 < 100)
                            {
                                Main.dust[num1001].position += Main.dust[num1001].velocity * 20f;
                                Main.dust[num1001].velocity *= -1f;
                            }
                        }
                    }
                }
            }
        }
    }
}
 
Last edited:
Are these all the sprites that I need for a Whip-Like Weapon? And does the size matter for these sprites?
View attachment 175391
Dirigon Head
Width: 46
Height: 46

View attachment 175394
Dirigon Blade
Width: 46
Height: 26

View attachment 175395
Dirigon Handle
Width: 46
Height: 40

View attachment 175396
Solar Eruption Sprite
View attachment 175397
Dirigon Sprite

Edit: Almost forgot about coding, whoops ^-^; Is the coding right?
Code:
 Note this is just the item, not the Head of the Weapon :3
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Items.Weapons.Dirigon
{
    public class Dirigon : ModItem
    {
        public override void SetDefaults()
        {
            DisplayName.SetDefault("Dirigon");
            item.useStyle = 5;
            item.useAnimation = 20;
            item.useTime = 20;
            item.shootSpeed = 24f;
            item.knockBack = 2f;
            item.width = 16;
            item.height = 16;
            item.UseSound = SoundID.Item116;
            item.shoot = 611;
            item.rare = 10;
            item.value = Item.sellPrice(1, 0, 0, 0);
            item.noMelee = true;
            item.noUseGraphic = true;
            item.channel = true;
            item.autoReuse = true;
            item.melee = true;
            item.damage = 1000;
            item.shoot = mod.ProjectileType("DirigonHandle");
            return;
        }
    }
}

Code:
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Projectiles.Dirigon
{
    public class DirigonBlade : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.CloneDefaults(ProjectileID.SolarWhipSword);
            DisplayName.SetDefault("Dirigon Blade");
            aiType = ProjectileID.SolarWhipSword;
        }
        public override void AI()
        {
            {
                Player player = Main.player[projectile.owner];
                float num = 1.57079637f;
                Vector2 vector = player.RotatedRelativePoint(player.MountedCenter, true);
                projectile.ai[0] += 1f;
                int num2 = 0;
                if (projectile.ai[0] >= 40f)
                {
                    num2++;
                }
                if (projectile.ai[0] >= 80f)
                {
                    num2++;
                }
                if (projectile.ai[0] >= 120f)
                {
                    num2++;
                }
                int num3 = 24;
                int num4 = 6;
                projectile.ai[1] += 1f;
                bool flag = false;
                if (projectile.ai[1] >= (float)(num3 - num4 * num2))
                {
                    projectile.ai[1] = 0f;
                    flag = true;
                }
                projectile.frameCounter += 1 + num2;
                if (projectile.frameCounter >= 4)
                {
                    projectile.frameCounter = 0;
                    projectile.frame++;
                    if (projectile.frame >= 6)
                    {
                        projectile.frame = 0;
                    }
                }
                if (projectile.soundDelay <= 0)
                {
                    projectile.soundDelay = num3 - num4 * num2;
                    if (projectile.ai[0] != 1f)
                    {
                        Main.PlaySound(SoundID.Item91, projectile.position);
                    }
                }
                if (projectile.ai[1] == 1f && projectile.ai[0] != 1f)
                {
                    Vector2 vector2 = Vector2.UnitX * 24f;
                    vector2 = vector2.RotatedBy((double)(projectile.rotation - 1.57079637f), default(Vector2));
                    Vector2 value = projectile.Center + vector2;
                    for (int i = 0; i < 2; i++)
                    {
                        int num5 = Dust.NewDust(value - Vector2.One * 8f, 16, 16, 135, projectile.velocity.X / 2f, projectile.velocity.Y / 2f, 100, default(Color), 1f);
                        Main.dust[num5].velocity *= 0.66f;
                        Main.dust[num5].noGravity = true;
                        Main.dust[num5].scale = 1.4f;
                    }
                }
                if (flag && Main.myPlayer == projectile.owner)
                {
                    bool flag2 = player.channel && player.CheckMana(player.inventory[player.selectedItem].mana, true, false) && !player.noItems && !player.CCed;
                    if (flag2)
                    {
                        float scaleFactor = player.inventory[player.selectedItem].shootSpeed * projectile.scale;
                        Vector2 value2 = vector;
                        Vector2 value3 = Main.screenPosition + new Vector2((float)Main.mouseX, (float)Main.mouseY) - value2;
                        if (player.gravDir == -1f)
                        {
                            value3.Y = (float)(Main.screenHeight - Main.mouseY) + Main.screenPosition.Y - value2.Y;
                        }
                        Vector2 vector3 = Vector2.Normalize(value3);
                        if (float.IsNaN(vector3.X) || float.IsNaN(vector3.Y))
                        {
                            vector3 = -Vector2.UnitY;
                        }
                        vector3 *= scaleFactor;
                        if (vector3.X != projectile.velocity.X || vector3.Y != projectile.velocity.Y)
                        {
                            projectile.netUpdate = true;
                        }
                        projectile.velocity = vector3;
                        int num6 = 440;
                        float scaleFactor2 = 14f;
                        int num7 = 7;
                        for (int j = 0; j < 2; j++)
                        {
                            value2 = projectile.Center + new Vector2((float)Main.rand.Next(-num7, num7 + 1), (float)Main.rand.Next(-num7, num7 + 1));
                            Vector2 spinningpoint = Vector2.Normalize(projectile.velocity) * scaleFactor2;
                            spinningpoint = spinningpoint.RotatedBy(Main.rand.NextDouble() * 0.19634954631328583 - 0.098174773156642914, default(Vector2));
                            if (float.IsNaN(spinningpoint.X) || float.IsNaN(spinningpoint.Y))
                            {
                                spinningpoint = -Vector2.UnitY;
                            }
                            Projectile.NewProjectile(value2.X, value2.Y, spinningpoint.X, spinningpoint.Y, num6, projectile.damage, projectile.knockBack, projectile.owner, 0f, 0f);
                        }
                    }
                    else
                    {
                        projectile.Kill();
                    }
                }
            }
        }
    }
}

Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Projectiles.Dirigon
{
    public class DirigonHandle : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.CloneDefaults(ProjectileID.SolarCounter);
            DisplayName.SetDefault("Dirigon Handle");
        }
        public override void AI()
        {
            projectile.ai[1] += 0.01f;
            projectile.scale = projectile.ai[1];
            projectile.ai[0] += 1f;
            if (projectile.ai[0] >= (float)(3 * Main.projFrames[projectile.type]))
            {
                projectile.Kill();
                return;
            }
            if (++projectile.frameCounter >= 3)
            {
                projectile.frameCounter = 0;
                if (++projectile.frame >= Main.projFrames[projectile.type])
                {
                    projectile.hide = true;
                }
            }
            projectile.alpha -= 63;
            if (projectile.alpha < 0)
            {
                projectile.alpha = 0;
            }
            bool flag56 = projectile.type == 612;
            bool flag57 = projectile.type == 624;
            if (flag56)
            {
                Lighting.AddLight(projectile.Center, 0.9f, 0.8f, 0.6f);
            }
            if (projectile.ai[0] == 1f)
            {
                projectile.position = projectile.Center;
                projectile.width = (projectile.height = (int)(52f * projectile.scale));
                projectile.Center = projectile.position;
                projectile.Damage();
                if (flag56)
                {
                    Main.PlaySound(SoundID.Item14, projectile.position);
                    for (int num980 = 0; num980 < 4; num980++)
                    {
                        int num981 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 31, 0f, 0f, 100, default(Color), 1.5f);
                        Main.dust[num981].position = projectile.Center + Vector2.UnitY.RotatedByRandom(3.1415927410125732) * (float)Main.rand.NextDouble() * (float)projectile.width / 2f;
                    }
                    for (int num982 = 0; num982 < 10; num982++)
                    {
                        int num983 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 6, 0f, 0f, 200, default(Color), 2.7f);
                        Main.dust[num983].position = projectile.Center + Vector2.UnitY.RotatedByRandom(3.1415927410125732) * (float)Main.rand.NextDouble() * (float)projectile.width / 2f;
                        Main.dust[num983].noGravity = true;
                        Main.dust[num983].velocity *= 3f;
                        num983 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 6, 0f, 0f, 100, default(Color), 1.5f);
                        Main.dust[num983].position = projectile.Center + Vector2.UnitY.RotatedByRandom(3.1415927410125732) * (float)Main.rand.NextDouble() * (float)projectile.width / 2f;
                        Main.dust[num983].velocity *= 2f;
                        Main.dust[num983].noGravity = true;
                        Main.dust[num983].fadeIn = 2.5f;
                    }
                    for (int num984 = 0; num984 < 5; num984++)
                    {
                        int num985 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 6, 0f, 0f, 0, default(Color), 2.7f);
                        Main.dust[num985].position = projectile.Center + Vector2.UnitX.RotatedByRandom(3.1415927410125732).RotatedBy((double)projectile.velocity.ToRotation(), default(Vector2)) * (float)projectile.width / 2f;
                        Main.dust[num985].noGravity = true;
                        Main.dust[num985].velocity *= 3f;
                    }
                    for (int num986 = 0; num986 < 10; num986++)
                    {
                        int num987 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 31, 0f, 0f, 0, default(Color), 1.5f);
                        Main.dust[num987].position = projectile.Center + Vector2.UnitX.RotatedByRandom(3.1415927410125732).RotatedBy((double)projectile.velocity.ToRotation(), default(Vector2)) * (float)projectile.width / 2f;
                        Main.dust[num987].noGravity = true;
                        Main.dust[num987].velocity *= 3f;
                    }
                }
                if (flag57)
                {
                    Main.PlaySound(SoundID.Item14, projectile.position);
                    for (int num988 = 0; num988 < 20; num988++)
                    {
                        int num989 = Dust.NewDust(projectile.position, projectile.width, projectile.height, 135, 0f, 0f, 100, default(Color), 1.5f);
                        Main.dust[num989].position = projectile.Center + Vector2.UnitY.RotatedByRandom(3.1415927410125732) * (float)Main.rand.NextDouble() * (float)projectile.width / 2f;
                        Main.dust[num989].velocity *= 2f;
                        Main.dust[num989].noGravity = true;
                        Main.dust[num989].fadeIn = 2.5f;
                    }
                    for (int num990 = 0; num990 < 15; num990++)
                    {
                        int num991 = Dust.NewDust(projectile.position, projectile.width, projectile.height, 135, 0f, 0f, 0, default(Color), 2.7f);
                        Main.dust[num991].position = projectile.Center + Vector2.UnitX.RotatedByRandom(3.1415927410125732).RotatedBy((double)projectile.velocity.ToRotation(), default(Vector2)) * (float)projectile.width / 2f;
                        Main.dust[num991].noGravity = true;
                        Main.dust[num991].velocity *= 3f;
                    }
                    float num992 = (float)Main.rand.NextDouble() * 6.28318548f;
                    float num993 = (float)Main.rand.NextDouble() * 6.28318548f;
                    float num994 = (float)Main.rand.NextDouble() * 6.28318548f;
                    float num995 = 7f + (float)Main.rand.NextDouble() * 7f;
                    float num996 = 7f + (float)Main.rand.NextDouble() * 7f;
                    float num997 = 7f + (float)Main.rand.NextDouble() * 7f;
                    float num998 = num995;
                    if (num996 > num998)
                    {
                        num998 = num996;
                    }
                    if (num997 > num998)
                    {
                        num998 = num997;
                    }
                    for (int num999 = 0; num999 < 200; num999++)
                    {
                        int num1000 = 135;
                        float scaleFactor14 = num998;
                        if (num999 > 50)
                        {
                            scaleFactor14 = num996;
                        }
                        if (num999 > 100)
                        {
                            scaleFactor14 = num995;
                        }
                        if (num999 > 150)
                        {
                            scaleFactor14 = num997;
                        }
                        int num1001 = Dust.NewDust(projectile.position, 6, 6, num1000, 0f, 0f, 100, default(Color), 1f);
                        Vector2 vector123 = Main.dust[num1001].velocity;
                        Main.dust[num1001].position = projectile.Center;
                        vector123.Normalize();
                        vector123 *= scaleFactor14;
                        if (num999 > 150)
                        {
                            vector123.Y *= 0.5f;
                            vector123 = vector123.RotatedBy((double)num994, default(Vector2));
                        }
                        else if (num999 > 100)
                        {
                            vector123.X *= 0.5f;
                            vector123 = vector123.RotatedBy((double)num992, default(Vector2));
                        }
                        else if (num999 > 50)
                        {
                            vector123.Y *= 0.5f;
                            vector123 = vector123.RotatedBy((double)num993, default(Vector2));
                        }
                        Main.dust[num1001].velocity *= 0.2f;
                        Main.dust[num1001].velocity += vector123;
                        if (num999 <= 200)
                        {
                            Main.dust[num1001].scale = 2f;
                            Main.dust[num1001].noGravity = true;
                            Main.dust[num1001].fadeIn = Main.rand.NextFloat() * 2f;
                            if (Main.rand.Next(4) == 0)
                            {
                                Main.dust[num1001].fadeIn = 2.5f;
                            }
                            Main.dust[num1001].noLight = true;
                            if (num999 < 100)
                            {
                                Main.dust[num1001].position += Main.dust[num1001].velocity * 20f;
                                Main.dust[num1001].velocity *= -1f;
                            }
                        }
                    }
                }
            }
        }
    }
}
To be honest, I have no idea, you're just going to have to run it.
 
To be honest, I have no idea, you're just going to have to run it.
Ah well. Guess I'll stick with a normal flail then. A few questions about those:
1) Is there any way for a Modded Flail to automatically retract back to the player after reaching max length?
2) Is there a way to increase the Flail's max length?
3) Is it possible for the "Chain" of the Flail to do damage?
4) Is there any way for the "Head" of the Flail to not be affected by gravity and fall?
5) Is it possible to make the flail come back to the player after a set amount of time/frames?
 
Code:
using System;
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Items
{
    public class Tyrhung : ModItem
    {
        public override void SetDefaults()
        {
            item.width = 30;
            item.height = 10;
            item.value = Item.sellPrice(1, 0, 0, 0);
            item.expert = true;
            item.noMelee = true; // Makes sure that the animation when using the item doesn't hurt NPCs.
            item.useStyle = 5; // Set the correct useStyle.
            item.useAnimation = 16; // Determines how long the animation lasts. 
            item.useTime = 16; // Determines how fast you can use this weapon (A lower value results in a faster use time).
            item.knockBack = 6F;
            item.damage = 1000;
            item.scale = 1.1F;
            item.UseSound = SoundID.Item116;
            item.noUseGraphic = true; // Do not use the item graphic when using the item (We just want the ball to spawn).
            item.shoot = mod.ProjectileType("TyrhungBlade");
            item.shootSpeed = 10F;
            item.melee = false; // Deals melee damage.
            item.autoReuse = true; // We can keep the left mouse button down when trying to keep using this weapon.
        }
       
        public override bool CanUseItem(Player player)       //this make that you can shoot only 1 boomerang at once
        {
            for (int i = 0; i < 1000; ++i)
            {
                if (Main.projectile[i].active && Main.projectile[i].owner == Main.myPlayer && Main.projectile[i].type == item.shoot)
                {
                    return true;
                }
            }
            return true;
        }
    }
}
Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Projectiles
{
    public class TyrhungBlade : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.width = 34;
            projectile.height = 34;
            projectile.aiStyle = 3;
            projectile.friendly = true;
            projectile.penetrate = -1; //Number of NPCs it can penetrate. "-1" is infinite.
            projectile.tileCollide = false;
            projectile.ignoreWater = true;
            projectile.melee = true; // Deals melee damage.
            projectile.extraUpdates = 1;
            projectile.timeLeft = 900;
            projectile.light = 0.75f;
        }

        //The "Chain" for the Flail Weapon.
        public override bool PreDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch, Color lightColor)
        {
            Texture2D texture = ModLoader.GetTexture("JoshuasMod/Projectiles/TyrhungChain");

            Vector2 position = projectile.Center;
            Vector2 mountedCenter = Main.player[projectile.owner].MountedCenter;
            Microsoft.Xna.Framework.Rectangle? sourceRectangle = new Microsoft.Xna.Framework.Rectangle?();
            Vector2 origin = new Vector2((float)texture.Width * 0.5f, (float)texture.Height * 0.5f);
            float num1 = (float)texture.Height;
            Vector2 vector2_4 = mountedCenter - position;
            float rotation = (float)Math.Atan2((double)vector2_4.Y, (double)vector2_4.X) - 1.57f;
            bool flag = true;
            if (float.IsNaN(position.X) && float.IsNaN(position.Y))
                flag = false;
            if (float.IsNaN(vector2_4.X) && float.IsNaN(vector2_4.Y))
                flag = false;
            while (flag)
            {
                if ((double)vector2_4.Length() < (double)num1 + 1.0)
                {
                    flag = false;
                }
                else
                {
                    Vector2 vector2_1 = vector2_4;
                    vector2_1.Normalize();
                    position += vector2_1 * num1;
                    vector2_4 = mountedCenter - position;
                    Microsoft.Xna.Framework.Color color2 = Lighting.GetColor((int)position.X / 16, (int)((double)position.Y / 16.0));
                    color2 = projectile.GetAlpha(color2);
                    Main.spriteBatch.Draw(texture, position - Main.screenPosition, sourceRectangle, color2, rotation, origin, 1.35f, SpriteEffects.None, 0.0f);
                }
            }
            return true;
        }
       
        public override void AI()           //This make that the projectile will face the correct way.
        {
            projectile.rotation = (float)Math.Atan2((double)projectile.velocity.Y, (double)projectile.velocity.X) + 1.75f;  
        }
    }
}

1) Why doesn't it pierce enemies? It only pierces them on it's way back to the player. Is it because it's a boomerang-type weapon?
2) How do I make the projectile face the way it was shot, without making the sprite look back at the player upon returning?
3) Not really related, but I couldn't find a "boomerang" weapon in ExampleMod. Unless I assume it's not called "ExampleBoomerang" or something. -_-
 
Ah well. Guess I'll stick with a normal flail then. A few questions about those:
1) Is there any way for a Modded Flail to automatically retract back to the player after reaching max length?
2) Is there a way to increase the Flail's max length?
3) Is it possible for the "Chain" of the Flail to do damage?
4) Is there any way for the "Head" of the Flail to not be affected by gravity and fall?
5) Is it possible to make the flail come back to the player after a set amount of time/frames?
https://forums.terraria.org/index.php?threads/tutorial-how-to-make-a-basic-flail-type-weapon.16065/

Try that

Code:
using System;
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Items
{
    public class Tyrhung : ModItem
    {
        public override void SetDefaults()
        {
            item.width = 30;
            item.height = 10;
            item.value = Item.sellPrice(1, 0, 0, 0);
            item.expert = true;
            item.noMelee = true; // Makes sure that the animation when using the item doesn't hurt NPCs.
            item.useStyle = 5; // Set the correct useStyle.
            item.useAnimation = 16; // Determines how long the animation lasts.
            item.useTime = 16; // Determines how fast you can use this weapon (A lower value results in a faster use time).
            item.knockBack = 6F;
            item.damage = 1000;
            item.scale = 1.1F;
            item.UseSound = SoundID.Item116;
            item.noUseGraphic = true; // Do not use the item graphic when using the item (We just want the ball to spawn).
            item.shoot = mod.ProjectileType("TyrhungBlade");
            item.shootSpeed = 10F;
            item.melee = false; // Deals melee damage.
            item.autoReuse = true; // We can keep the left mouse button down when trying to keep using this weapon.
        }
      
        public override bool CanUseItem(Player player)       //this make that you can shoot only 1 boomerang at once
        {
            for (int i = 0; i < 1000; ++i)
            {
                if (Main.projectile[i].active && Main.projectile[i].owner == Main.myPlayer && Main.projectile[i].type == item.shoot)
                {
                    return true;
                }
            }
            return true;
        }
    }
}
Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Projectiles
{
    public class TyrhungBlade : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.width = 34;
            projectile.height = 34;
            projectile.aiStyle = 3;
            projectile.friendly = true;
            projectile.penetrate = -1; //Number of NPCs it can penetrate. "-1" is infinite.
            projectile.tileCollide = false;
            projectile.ignoreWater = true;
            projectile.melee = true; // Deals melee damage.
            projectile.extraUpdates = 1;
            projectile.timeLeft = 900;
            projectile.light = 0.75f;
        }

        //The "Chain" for the Flail Weapon.
        public override bool PreDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch, Color lightColor)
        {
            Texture2D texture = ModLoader.GetTexture("JoshuasMod/Projectiles/TyrhungChain");

            Vector2 position = projectile.Center;
            Vector2 mountedCenter = Main.player[projectile.owner].MountedCenter;
            Microsoft.Xna.Framework.Rectangle? sourceRectangle = new Microsoft.Xna.Framework.Rectangle?();
            Vector2 origin = new Vector2((float)texture.Width * 0.5f, (float)texture.Height * 0.5f);
            float num1 = (float)texture.Height;
            Vector2 vector2_4 = mountedCenter - position;
            float rotation = (float)Math.Atan2((double)vector2_4.Y, (double)vector2_4.X) - 1.57f;
            bool flag = true;
            if (float.IsNaN(position.X) && float.IsNaN(position.Y))
                flag = false;
            if (float.IsNaN(vector2_4.X) && float.IsNaN(vector2_4.Y))
                flag = false;
            while (flag)
            {
                if ((double)vector2_4.Length() < (double)num1 + 1.0)
                {
                    flag = false;
                }
                else
                {
                    Vector2 vector2_1 = vector2_4;
                    vector2_1.Normalize();
                    position += vector2_1 * num1;
                    vector2_4 = mountedCenter - position;
                    Microsoft.Xna.Framework.Color color2 = Lighting.GetColor((int)position.X / 16, (int)((double)position.Y / 16.0));
                    color2 = projectile.GetAlpha(color2);
                    Main.spriteBatch.Draw(texture, position - Main.screenPosition, sourceRectangle, color2, rotation, origin, 1.35f, SpriteEffects.None, 0.0f);
                }
            }
            return true;
        }
      
        public override void AI()           //This make that the projectile will face the correct way.
        {
            projectile.rotation = (float)Math.Atan2((double)projectile.velocity.Y, (double)projectile.velocity.X) + 1.75f; 
        }
    }
}

1) Why doesn't it pierce enemies? It only pierces them on it's way back to the player. Is it because it's a boomerang-type weapon?
2) How do I make the projectile face the way it was shot, without making the sprite look back at the player upon returning?
3) Not really related, but I couldn't find a "boomerang" weapon in ExampleMod. Unless I assume it's not called "ExampleBoomerang" or something. -_-
1. I worded the thing wrong, "projectile.penetrate" is the amount of NPCs it can hit, so if you want it to be able to pierce through 1, then you set it to 2 instead
2. Try "projectile.spriteDirection" and set it to 1 or -1 depending on velocity
3. Check for boomerang AI style and use that, if that doesn't work you're gonna have to figure it out with the vanilla code.
 
1. I worded the thing wrong, "projectile.penetrate" is the amount of NPCs it can hit, so if you want it to be able to pierce through 1, then you set it to 2 instead
2. Try "projectile.spriteDirection" and set it to 1 or -1 depending on velocity
3. Check for boomerang AI style and use that, if that doesn't work you're gonna have to figure it out with the vanilla code.

Behold, the Wall of Text has awoken!
TL;DR at bottom if too lazy to read (It's not that much, to be honest).

Code:
using System;
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Items
{
    public class Tyrhung : ModItem
    {
        public override void SetDefaults()
        {
            item.width = 30;
            item.height = 10;
            item.value = Item.sellPrice(1, 0, 0, 0);
            item.expert = true;
            item.noMelee = true; // Makes sure that the animation when using the item doesn't hurt NPCs.
            item.useStyle = 1; // Set the correct useStyle.
            item.useAnimation = 16; // Determines how long the animation lasts.
            item.useTime = 16; // Determines how fast you can use this weapon (A lower value results in a faster use time).
            item.knockBack = 5F;
            item.damage = 1000;
            item.scale = 1.1F;
            item.UseSound = SoundID.Item71;
            item.noUseGraphic = true; // Do not use the item graphic when using the item (We just want the ball to spawn).
            item.shoot = mod.ProjectileType("TyrhungBlade");
            item.shootSpeed = 12F;
            item.melee = false; // Deals melee damage.
            item.autoReuse = true; // We can keep the left mouse button down when trying to keep using this weapon.
        }
 
        public override bool CanUseItem(Player player)       // This makes it that the weapon shoots 1 boomerang at once.
        {
            for (int i = 0; i < 1000; ++i)
            {
                if (Main.projectile[i].active && Main.projectile[i].owner == Main.myPlayer && Main.projectile[i].type == item.shoot)
                {
                    return false;
                }
            }
            return true;
        }
    }
}
Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Projectiles
{
    public class TyrhungBlade : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.width = 34;
            projectile.height = 34;
            projectile.aiStyle = 3;
            projectile.friendly = true;
            projectile.melee = true;
            projectile.tileCollide = false;
            projectile.penetrate = -1;
            projectile.timeLeft = 900;
            projectile.light = 0.75f;
            projectile.extraUpdates = 1;
            projectile.tileCollide = false;
            projectile.ignoreWater = true;
        }

        //The "Chain" for the Flail Weapon.
        public override bool PreDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch, Color lightColor)
        {
            Texture2D texture = ModLoader.GetTexture("JoshuasMod/Projectiles/TyrhungChain");

            Vector2 position = projectile.Center;
            Vector2 mountedCenter = Main.player[projectile.owner].MountedCenter;
            Microsoft.Xna.Framework.Rectangle? sourceRectangle = new Microsoft.Xna.Framework.Rectangle?();
            Vector2 origin = new Vector2((float)texture.Width * 0.5f, (float)texture.Height * 0.5f);
            float num1 = (float)texture.Height;
            Vector2 vector2_4 = mountedCenter - position;
            float rotation = (float)Math.Atan2((double)vector2_4.Y, (double)vector2_4.X) - 1.57f;
            bool flag = true;
            if (float.IsNaN(position.X) && float.IsNaN(position.Y))
                flag = false;
            if (float.IsNaN(vector2_4.X) && float.IsNaN(vector2_4.Y))
                flag = false;
            while (flag)
            {
                if ((double)vector2_4.Length() < (double)num1 + 1.0)
                {
                    flag = false;
                }
                else
                {
                    Vector2 vector2_1 = vector2_4;
                    vector2_1.Normalize();
                    position += vector2_1 * num1;
                    vector2_4 = mountedCenter - position;
                    Microsoft.Xna.Framework.Color color2 = Lighting.GetColor((int)position.X / 16, (int)((double)position.Y / 16.0));
                    color2 = projectile.GetAlpha(color2);
                    Main.spriteBatch.Draw(texture, position - Main.screenPosition, sourceRectangle, color2, rotation, origin, 1.35f, SpriteEffects.None, 0.0f);
                }
            }
            return true;
        }
 
        public override void AI()           //This make that the projectile will face the correct way.
        {
            projectile.rotation = (float)Math.Atan2((double)projectile.velocity.Y, (double)projectile.velocity.X) + 1.75f;
        }
    }
}
I tried "projectile.penetrate = 2;", which pierces through 2 enemies and disappears, so I did "projectile.penetrate = -1;", which should pierce infinitely. The problem is, It only pierces enemies when the projectile is on its way back to the player. When hitting enemies before the projectile turns back, it quickly returns to the player. I want it to always pierce my enemies, regardless of the direction.
I did "projectile.spriteDirection = 1;", but it didn't work, so I changed "1;" to "-1;", and that still didn't work. Was it supposed to be in a different place?
Also, I want the player to hold a TyrhungHandle to the weapon that shoots the TyrhungBlade projectile (Like the Solar Eruption's Handle), but without changing the TyrhungBlade projectile's AI. The TyrhungHandle would look like this:
TyrhungHandle.png
Do I just edit the Item's Code from this:
Code:
item.shoot = mod.ProjectileType("TyrhungBlade");
To:
Code:
item.shoot = mod.ProjectileType("TyrhungHandle");
And make the TyrhungHandle's AI shoot the TyrhungBlade Projectile? Problem is, I don't really know how to make the TyrhungHandle shoot the TyrhungBlade's projectile at the same time and the where the TyrhungHandle projectile was.

TL;DR
1) Projectile not piercing enemies.
2) Sprite Direction doesn't work.
3) Want to add a handle which shoots out the Blade projectile, without changing the Blade projectile's AI.
 

Behold, the Wall of Text has awoken!
TL;DR at bottom if too lazy to read (It's not that much, to be honest).

Code:
using System;
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Items
{
    public class Tyrhung : ModItem
    {
        public override void SetDefaults()
        {
            item.width = 30;
            item.height = 10;
            item.value = Item.sellPrice(1, 0, 0, 0);
            item.expert = true;
            item.noMelee = true; // Makes sure that the animation when using the item doesn't hurt NPCs.
            item.useStyle = 1; // Set the correct useStyle.
            item.useAnimation = 16; // Determines how long the animation lasts.
            item.useTime = 16; // Determines how fast you can use this weapon (A lower value results in a faster use time).
            item.knockBack = 5F;
            item.damage = 1000;
            item.scale = 1.1F;
            item.UseSound = SoundID.Item71;
            item.noUseGraphic = true; // Do not use the item graphic when using the item (We just want the ball to spawn).
            item.shoot = mod.ProjectileType("TyrhungBlade");
            item.shootSpeed = 12F;
            item.melee = false; // Deals melee damage.
            item.autoReuse = true; // We can keep the left mouse button down when trying to keep using this weapon.
        }

        public override bool CanUseItem(Player player)       // This makes it that the weapon shoots 1 boomerang at once.
        {
            for (int i = 0; i < 1000; ++i)
            {
                if (Main.projectile[i].active && Main.projectile[i].owner == Main.myPlayer && Main.projectile[i].type == item.shoot)
                {
                    return false;
                }
            }
            return true;
        }
    }
}
Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Projectiles
{
    public class TyrhungBlade : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.width = 34;
            projectile.height = 34;
            projectile.aiStyle = 3;
            projectile.friendly = true;
            projectile.melee = true;
            projectile.tileCollide = false;
            projectile.penetrate = -1;
            projectile.timeLeft = 900;
            projectile.light = 0.75f;
            projectile.extraUpdates = 1;
            projectile.tileCollide = false;
            projectile.ignoreWater = true;
        }

        //The "Chain" for the Flail Weapon.
        public override bool PreDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch, Color lightColor)
        {
            Texture2D texture = ModLoader.GetTexture("JoshuasMod/Projectiles/TyrhungChain");

            Vector2 position = projectile.Center;
            Vector2 mountedCenter = Main.player[projectile.owner].MountedCenter;
            Microsoft.Xna.Framework.Rectangle? sourceRectangle = new Microsoft.Xna.Framework.Rectangle?();
            Vector2 origin = new Vector2((float)texture.Width * 0.5f, (float)texture.Height * 0.5f);
            float num1 = (float)texture.Height;
            Vector2 vector2_4 = mountedCenter - position;
            float rotation = (float)Math.Atan2((double)vector2_4.Y, (double)vector2_4.X) - 1.57f;
            bool flag = true;
            if (float.IsNaN(position.X) && float.IsNaN(position.Y))
                flag = false;
            if (float.IsNaN(vector2_4.X) && float.IsNaN(vector2_4.Y))
                flag = false;
            while (flag)
            {
                if ((double)vector2_4.Length() < (double)num1 + 1.0)
                {
                    flag = false;
                }
                else
                {
                    Vector2 vector2_1 = vector2_4;
                    vector2_1.Normalize();
                    position += vector2_1 * num1;
                    vector2_4 = mountedCenter - position;
                    Microsoft.Xna.Framework.Color color2 = Lighting.GetColor((int)position.X / 16, (int)((double)position.Y / 16.0));
                    color2 = projectile.GetAlpha(color2);
                    Main.spriteBatch.Draw(texture, position - Main.screenPosition, sourceRectangle, color2, rotation, origin, 1.35f, SpriteEffects.None, 0.0f);
                }
            }
            return true;
        }

        public override void AI()           //This make that the projectile will face the correct way.
        {
            projectile.rotation = (float)Math.Atan2((double)projectile.velocity.Y, (double)projectile.velocity.X) + 1.75f;
        }
    }
}
I tried "projectile.penetrate = 2;", which pierces through 2 enemies and disappears, so I did "projectile.penetrate = -1;", which should pierce infinitely. The problem is, It only pierces enemies when the projectile is on its way back to the player. When hitting enemies before the projectile turns back, it quickly returns to the player. I want it to always pierce my enemies, regardless of the direction.
I did "projectile.spriteDirection = 1;", but it didn't work, so I changed "1;" to "-1;", and that still didn't work. Was it supposed to be in a different place?
Also, I want the player to hold a TyrhungHandle to the weapon that shoots the TyrhungBlade projectile (Like the Solar Eruption's Handle), but without changing the TyrhungBlade projectile's AI. The TyrhungHandle would look like this:
View attachment 175759
Do I just edit the Item's Code from this:
Code:
item.shoot = mod.ProjectileType("TyrhungBlade");
To:
Code:
item.shoot = mod.ProjectileType("TyrhungHandle");
And make the TyrhungHandle's AI shoot the TyrhungBlade Projectile? Problem is, I don't really know how to make the TyrhungHandle shoot the TyrhungBlade's projectile at the same time and the where the TyrhungHandle projectile was.

TL;DR
1) Projectile not piercing enemies.
2) Sprite Direction doesn't work.
3) Want to add a handle which shoots out the Blade projectile, without changing the Blade projectile's AI.
1. Probably is built into the AI style, meaning you'll have to get the code from the game and modify from there
2. Generally should be in AI style, an example of how spriteDirection works is that you have a player sprite usually facing right, so... spriteDirection 1 being default will have the player looking to the right, while with -1, to the left, you can see an example of doing this with this https://github.com/Harrison-Jue/COFP/blob/master/Projectiles/Flying/FleeingFairy.cs
3. Just make the item spawn two projectiles using Shoot hook
 
c:\Users\Joshua\Documents\My Games\Terraria\ModLoader\Mod Sources\JoshuasMod\Items\Tyrhung.cs(40,30) : error CS0103: The name 'vector2_1' does not exist in the current context

c:\Users\Joshua\Documents\My Games\Terraria\ModLoader\Mod Sources\JoshuasMod\Items\Tyrhung.cs(40,43) : error CS0103: The name 'vector2_1' does not exist in the current context

c:\Users\Joshua\Documents\My Games\Terraria\ModLoader\Mod Sources\JoshuasMod\Items\Tyrhung.cs(40,56) : error CS0103: The name 'SpeedX' does not exist in the current context

c:\Users\Joshua\Documents\My Games\Terraria\ModLoader\Mod Sources\JoshuasMod\Items\Tyrhung.cs(40,72) : error CS0103: The name 'SpeedY' does not exist in the current context
Code:
using System;
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Items
{
    public class Tyrhung : ModItem
    {
        public override void SetDefaults()
        {
            item.width = 30;
            item.height = 10;
            item.value = Item.sellPrice(1, 0, 0, 0);
            item.expert = true;
            item.noMelee = true; // Makes sure that the animation when using the item doesn't hurt NPCs.
            item.useStyle = 5; // Set the correct useStyle.
            item.useAnimation = 16; // Determines how long the animation lasts. 
            item.useTime = 16; // Determines how fast you can use this weapon (A lower value results in a faster use time).
            item.knockBack = 5F;
            item.damage = 1000;
            item.scale = 1.1F;
            item.UseSound = SoundID.Item71;
            item.noUseGraphic = true; // Do not use the item graphic when using the item (We just want the ball to spawn).
            item.shoot = mod.ProjectileType("TyrhungHandle");
            item.shootSpeed = 12F;
            item.melee = false; // Deals melee damage.
            item.autoReuse = true; // We can keep the left mouse button down when trying to keep using this weapon.
        }
       
        // Shotgun-Style: Multiple Projectiles, Random Spread.
        public override bool Shoot(Player player, ref Vector2 position, ref float speedX, ref float speedY, ref int type, ref int damage, ref float knockBack)
        {
            int numberProjectiles = 1 + Main.rand.Next(2); //This defines how many projectiles to shot. 1 + Main.rand.Next(2)= 1 or 2 shots
            for (int i = 0; i < numberProjectiles; i++)
            {
                Vector2 perturbedSpeed = new Vector2(speedX, speedY).RotatedByRandom(MathHelper.ToRadians(15)); // This defines the projectile's random spread.
                Projectile.NewProjectile(position.X, position.Y, perturbedSpeed.X, perturbedSpeed.Y, type, damage, knockBack, player.whoAmI);
                Projectile.NewProjectile(vector2_1.X, vector2_1.Y, SpeedX * 1.00f, SpeedY * 1.00f, mod.ProjectileType("TyrhungBlade"), damage * 1, knockBack, Main.myPlayer, 0.0f, (float)Main.rand.Next(5));
            }
            return false;
        }
    }
}
I tried to make it shoot the projectile 'randomly' near the cursor, which is why I have the "Shotgun Style" AI.
Also, did I do the second "Projectile.NewProjectile" line incorrectly?
 
Hello. Could I get code for making weapon shoot two or three different projectiles at once? I wanted to create sword that shoots Meowmere, Star Wrath and Horseman's Blade projectiles at once.
 
c:\Users\Joshua\Documents\My Games\Terraria\ModLoader\Mod Sources\JoshuasMod\Items\Tyrhung.cs(40,30) : error CS0103: The name 'vector2_1' does not exist in the current context

c:\Users\Joshua\Documents\My Games\Terraria\ModLoader\Mod Sources\JoshuasMod\Items\Tyrhung.cs(40,43) : error CS0103: The name 'vector2_1' does not exist in the current context

c:\Users\Joshua\Documents\My Games\Terraria\ModLoader\Mod Sources\JoshuasMod\Items\Tyrhung.cs(40,56) : error CS0103: The name 'SpeedX' does not exist in the current context

c:\Users\Joshua\Documents\My Games\Terraria\ModLoader\Mod Sources\JoshuasMod\Items\Tyrhung.cs(40,72) : error CS0103: The name 'SpeedY' does not exist in the current context
Code:
using System;
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace JoshuasMod.Items
{
    public class Tyrhung : ModItem
    {
        public override void SetDefaults()
        {
            item.width = 30;
            item.height = 10;
            item.value = Item.sellPrice(1, 0, 0, 0);
            item.expert = true;
            item.noMelee = true; // Makes sure that the animation when using the item doesn't hurt NPCs.
            item.useStyle = 5; // Set the correct useStyle.
            item.useAnimation = 16; // Determines how long the animation lasts.
            item.useTime = 16; // Determines how fast you can use this weapon (A lower value results in a faster use time).
            item.knockBack = 5F;
            item.damage = 1000;
            item.scale = 1.1F;
            item.UseSound = SoundID.Item71;
            item.noUseGraphic = true; // Do not use the item graphic when using the item (We just want the ball to spawn).
            item.shoot = mod.ProjectileType("TyrhungHandle");
            item.shootSpeed = 12F;
            item.melee = false; // Deals melee damage.
            item.autoReuse = true; // We can keep the left mouse button down when trying to keep using this weapon.
        }
      
        // Shotgun-Style: Multiple Projectiles, Random Spread.
        public override bool Shoot(Player player, ref Vector2 position, ref float speedX, ref float speedY, ref int type, ref int damage, ref float knockBack)
        {
            int numberProjectiles = 1 + Main.rand.Next(2); //This defines how many projectiles to shot. 1 + Main.rand.Next(2)= 1 or 2 shots
            for (int i = 0; i < numberProjectiles; i++)
            {
                Vector2 perturbedSpeed = new Vector2(speedX, speedY).RotatedByRandom(MathHelper.ToRadians(15)); // This defines the projectile's random spread.
                Projectile.NewProjectile(position.X, position.Y, perturbedSpeed.X, perturbedSpeed.Y, type, damage, knockBack, player.whoAmI);
                Projectile.NewProjectile(vector2_1.X, vector2_1.Y, SpeedX * 1.00f, SpeedY * 1.00f, mod.ProjectileType("TyrhungBlade"), damage * 1, knockBack, Main.myPlayer, 0.0f, (float)Main.rand.Next(5));
            }
            return false;
        }
    }
}
I tried to make it shoot the projectile 'randomly' near the cursor, which is why I have the "Shotgun Style" AI.
Also, did I do the second "Projectile.NewProjectile" line incorrectly?
No?

You're using some random variable that doesn't exist in your code at all, I'm guessing that "vectr2_1" you copied from somewhere else? Anyways, you probably just want to set the the first two params using "position" instead.

Also, it's speedX and speedY, not SpeedX and SpeedY. No idea what is with the random multipliers, you don't need them if you just have it at 1

That random "(float)Main.rand.Next(5))" is totally unecessary, as I don't see you using projectile.ai[1] at all in your code for the blade.

Why are you trying to make it a shotgun exactly? If anything you don't need the for loop at all since you probably only want one blade and one handle.

Hello. Could I get code for making weapon shoot two or three different projectiles at once? I wanted to create sword that shoots Meowmere, Star Wrath and Horseman's Blade projectiles at once.
Use the Shoot() hook, add multiple lines of "Projectile.NewProjectile(<Fill in the blanks here>)", return false or return true if you set the "item.shoot" to one of the 3, and then 2 "Projectile.NewProjectile()" for the other 2.
 
Hello, how would I make a weapon "charge" and shoot a different projectile depending on how long I charged?
I.E Like the Charged Blaster Cannon:
Less than 1 full second: Small, non-piercing orb.
After 1 second: Larger, faster, multiple-piercing orb.
After 3 seconds: Large, solid beam.
Edit: Also, how do I add dust particles to the hand when the weapon is being used? Similar to the Nebula Blaze, but only when the weapon is being used.
 
Last edited:
Back
Top Bottom