tModLoader Is it possible to fire a laser beam weapon as a new projectile?

Synthetic Pigeon

Terrarian
I want to make a weapon that can fire a laser, shotgun and meteors at the same time. Using a shoot hook with Projectile.NewProjectile disables item.shoot = modProjectileType("laser"). So now I'm trying to figure out how to make a laser using the shoot hook, Projectile.NewProjectile thing. So far no luck, but the meteors and shotgun are working as intended. Still pretty new to modding so keeping it simple would be appreciated. :D
Here's my code, though I doubt it'll help.

Weapon Code

using Terraria.Enums;
using Microsoft.Xna.Framework;
using System;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;

namespace ExampleMOD8.Items.Weapons
{
public class SuperLaser : ModItem
{
public override void SetStaticDefaults()
{

DisplayName.SetDefault("SuperLaser");
Tooltip.SetDefault("nioce");
}
public override void SetDefaults()
{

item.noMelee = true;
item.damage = 8000;
item.magic = true;
item.width = 40;
item.height = 40;
item.useTime = 20;
item.useAnimation = 20;
item.useStyle = 5;
item.knockBack = 30;
item.value = 10000;
item.rare = 11;
item.UseSound = SoundID.Item1;
item.autoReuse = true;
item.channel = true;
item.mana = 1;
item.UseSound = SoundID.Item13;
item.shootSpeed = 18f;
item.shoot = mod.ProjectileType("LaserBeam");
}
public override bool PreDrawInInventory(SpriteBatch spriteBatch, Vector2 position, Rectangle frame, Color drawColor, Color itemColor, Vector2 origin, float scale)
{
//New item inventory sprite is Items/someItem.png
spriteBatch.Draw(mod.GetTexture("Items/Super Laser"),
new Rectangle((int)position.X, (int)position.Y, (int)(frame.Width * scale), (int)(frame.Height * scale)),
drawColor);
return false; //Stops default drawing
}
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 = 6 + Main.rand.Next(0); //This defines how many projectiles to shot. 4 + Main.rand.Next(2)= 4 or 5 shots
for (int i = 0; i < numberProjectiles; i++)
{
Vector2 perturbedSpeed = new Vector2(speedX, speedY).RotatedByRandom(MathHelper.ToRadians(15)); // This defines the projectiles random spread . 30 degree spread.
Projectile.NewProjectile(position.X, position.Y, perturbedSpeed.X, perturbedSpeed.Y, mod.ProjectileType("Shotgun2"), damage, knockBack, player.whoAmI);
}
for (int index = 0; index < numberProjectiles; ++index)
{
Vector2 vector2_1 = new Vector2((float)((double)player.position.X + (double)player.width * 0.5 + (double)(Main.rand.Next(201) * -player.direction) + ((double)Main.mouseX + (double)Main.screenPosition.X - (double)player.position.X)), (float)((double)player.position.Y + (double)player.height * 0.5 - 600.0)); //this defines the projectile width, direction and position
vector2_1.X = (float)(((double)vector2_1.X + (double)player.Center.X) / 2.0) + (float)Main.rand.Next(-200, 201);
vector2_1.Y -= (float)(100 * index);
float num12 = (float)Main.mouseX + Main.screenPosition.X - vector2_1.X;
float num13 = (float)Main.mouseY + Main.screenPosition.Y - vector2_1.Y;
if ((double)num13 < 0.0) num13 *= -1f;
if ((double)num13 < 20.0) num13 = 20f;
float num14 = (float)Math.Sqrt((double)num12 * (double)num12 + (double)num13 * (double)num13);
float num15 = item.shootSpeed / num14;
float num16 = num12 * num15;
float num17 = num13 * num15;
float SpeedX = num16 + (float)Main.rand.Next(-40, 41) * 0.02f; //this defines the projectile X position speed and randomnes
float SpeedY = num17 + ((float)Main.rand.Next(-40, 41) * 0.02f); //this defines the projectile Y position speed and randomnes
Projectile.NewProjectile(vector2_1.X, vector2_1.Y, SpeedX, SpeedY, mod.ProjectileType("Meteor2"), damage, knockBack, Main.myPlayer, 0.0f, (float)Main.rand.Next(5));
}

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

The Laser Code

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ModLoader;
using Terraria.ID;
using Terraria.Enums;

namespace ExampleMOD8.Projectiles
{
public class LaserBeam: ModProjectile
{
private Vector2 _targetPos; //Ending position of the laser beam
private int _charge; //The charge level of the weapon
private float _moveDist = 45f; //The distance charge particle from the player center

public override void SetDefaults()
{
projectile.Name = "LaserBeam"; //this is the projectile name
projectile.width = 20;
projectile.height = 10;
projectile.friendly = true; //this defines if the projectile is frendly
projectile.penetrate = -1; //this defines the projectile penetration, -1 = infinity
projectile.tileCollide = false; //this defines if the tile can colide with walls
projectile.magic = true;
projectile.hide = true;

}
public override bool PreDraw(SpriteBatch spriteBatch, Color lightColor)
{
if (_charge == 50)
{
Vector2 unit = _targetPos - Main.player[projectile.owner].Center;
unit.Normalize();
DrawLaser(spriteBatch, Main.projectileTexture[projectile.type], Main.player[projectile.owner].Center, unit, 5, projectile.damage, -1.57f, 1f, 1000f, Color.White, 45);// this is the projectile sprite draw, 45 = the distance of where the projectile starts from the player
}
return false;

}

/// <summary>
/// The core function of drawing a laser
/// </summary>
public void DrawLaser(SpriteBatch spriteBatch, Texture2D texture, Vector2 start, Vector2 unit, float step, int damage, float rotation = 0f, float scale = 1f, float maxDist = 2000f, Color color = default(Color), int transDist = 50)
{
Vector2 origin = start;
float r = unit.ToRotation() + rotation;


#region Draw laser body
for (float i = transDist; i <= _moveDist; i += step)
{
Color c = Color.White;
origin = start + i * unit;
spriteBatch.Draw(texture, origin - Main.screenPosition,
new Rectangle(0, 26, 28, 26), i < transDist ? Color.Transparent : c, r,
new Vector2(28 / 2, 26 / 2), scale, 0, 0);
}
#endregion

#region Draw laser tail
spriteBatch.Draw(texture, start + unit * (transDist - step) - Main.screenPosition,
new Rectangle(0, 0, 28, 26), Color.White, r, new Vector2(28 / 2, 26 / 2), scale, 0, 0);
#endregion

#region Draw laser head
spriteBatch.Draw(texture, start + (_moveDist + step) * unit - Main.screenPosition,
new Rectangle(0, 52, 28, 26), Color.White, r, new Vector2(28 / 2, 26 / 2), scale, 0, 0);
#endregion
}

/// <summary>
/// Change the way of collision check of the projectile
/// </summary>
public override bool? Colliding(Rectangle projHitbox, Rectangle targetHitbox)
{
if (_charge == 50)
{
Player p = Main.player[projectile.owner];
Vector2 unit = (Main.player[projectile.owner].Center - _targetPos);
unit.Normalize();
float point = 0f;
if (Collision.CheckAABBvLineCollision(targetHitbox.TopLeft(), targetHitbox.Size(), p.Center - 95f * unit, p.Center - unit * _moveDist, 22, ref point))
{
return true;
}
}
return false;
}

/// <summary>
/// Change the behavior after hit a NPC
/// </summary>
public override void OnHitNPC(NPC target, int damage, float knockback, bool crit)
{
target.immune[projectile.owner] = 5;

{
target.AddBuff(144, 480, false); //The debuff inflicted is the modded debuff Ethereal Flames. 180 is the duration in frames: Terraria runs at 60 FPS, so that's 3 seconds (180/60=3). To change the modded debuff, change EtherealFlames to whatever the buff is called; to add a vanilla debuff, change mod.BuffType("EtherealFlames") to a number based on the terraria buff IDs. Some useful ones are 20 for poison, 24 for On Fire!, 39 for Cursed Flames, 69 for Ichor, and 70 for Venom.
}
}

/// <summary>
/// The AI of the projectile
/// </summary>
public override void AI()
{

Vector2 mousePos = Main.MouseWorld;
Player player = Main.player[projectile.owner];

#region Set projectile position
if (projectile.owner == Main.myPlayer) // Multiplayer support
{
Vector2 diff = mousePos - player.Center;
diff.Normalize();
projectile.position = player.Center + diff * _moveDist;
projectile.timeLeft = 2;
int dir = projectile.position.X > player.position.X ? 1 : -1;
player.ChangeDir(dir);
player.heldProj = projectile.whoAmI;
player.itemTime = 2;
player.itemAnimation = 2;
player.itemRotation = (float)Math.Atan2(diff.Y * dir, diff.X * dir);
projectile.soundDelay--;
#endregion
}



#region Charging process
// Kill the projectile if the player stops channeling
if (!player.channel)
{
projectile.Kill();
}
else
{
if (Main.time % 10 < 1 && !player.CheckMana(player.inventory[player.selectedItem].mana, true))
{
projectile.Kill();
}
Vector2 offset = mousePos - player.Center;
offset.Normalize();
offset *= _moveDist - 20;
Vector2 dustPos = player.Center + offset - new Vector2(10, 10);
if (_charge < 100)
{
_charge++;
}
int chargeFact = _charge / 20;
Vector2 dustVelocity = Vector2.UnitX * 18f;
dustVelocity = dustVelocity.RotatedBy(projectile.rotation - 1.57f, default(Vector2));
Vector2 spawnPos = projectile.Center + dustVelocity;
for (int k = 0; k < chargeFact + 1; k++)
{
Vector2 spawn = spawnPos + ((float)Main.rand.NextDouble() * 6.28f).ToRotationVector2() * (12f - (chargeFact * 2));
Dust dust = Main.dust[Dust.NewDust(dustPos, 30, 30, 235, projectile.velocity.X / 2f, //this 30, 30 is the dust weight and height 235 is the tail dust
projectile.velocity.Y / 2f, 0, default(Color), 1f)];
dust.velocity = Vector2.Normalize(spawnPos - spawn) * 1.5f * (10f - chargeFact * 2f) / 10f;
dust.noGravity = true;
dust.scale = Main.rand.Next(10, 20) * 0.05f;
}
}
#endregion


#region Set laser tail position and dusts
if (_charge < 50) return;
Vector2 start = player.Center;
Vector2 unit = (player.Center - mousePos);
unit.Normalize();
unit *= -1;
for (_moveDist = 95f; _moveDist <= 1600; _moveDist += 5) //this 1600 is the dsitance of the beam
{
start = player.Center + unit * _moveDist;
if (!Collision.CanHit(player.Center, 1, 1, start, 1, 1))
{
_moveDist -= 5f;
break;
}

if (projectile.soundDelay <= 0)//this is the proper sound delay for this type of weapon
{
Main.PlaySound(2, (int)projectile.Center.X, (int)projectile.Center.Y, 15); //this is the sound when the weapon is used cheange 15 for diferent sound
projectile.soundDelay = 40; //this is the proper sound delay for this type of weapon
}

}
_targetPos = player.Center + unit * _moveDist;

//dust
for (int i = 0; i < 2; ++i)
{
float num1 = projectile.velocity.ToRotation() + (Main.rand.Next(2) == 1 ? -1.0f : 1.0f) * 1.57f;
float num2 = (float)(Main.rand.NextDouble() * 0.8f + 1.0f);
Vector2 dustVel = new Vector2((float)Math.Cos(num1) * num2, (float)Math.Sin(num1) * num2);
Dust dust = Main.dust[Dust.NewDust(_targetPos, 0, 0, 235, dustVel.X, dustVel.Y, 0, new Color(231, 172, 255), 1f)]; //this is the head dust
Dust dust2 = Main.dust[Dust.NewDust(_targetPos, 0, 0, 235, dustVel.X, dustVel.Y, 0, new Color(283, 33, 100), 1f)]; //this is the head dust 2
dust.noGravity = true;
dust.scale = 1.2f;
}

#endregion
}

public override bool ShouldUpdatePosition()
{
return false;

}
}
}


Thank you in advance!
 
I am not sure, but can't you just use NewProjectile, like you do with all of your other projectiles? After all, that's what happens to item.Shoot as well. If that doesn't work, you can just return true from your override Shoot. That way the vanilla shooting stuff doesn't get disabled. I am guessing you are returning false right now, but that part of your code seems to be missing from your post.
Also, next time could you please put your code between a [ CODE ][ /CODE ] (without the spaces of course) block and indent it properly? It's kinda hard to read this way...
 
I don't know how to make a Laser using Projectile.NewProjectile D:

Setting return true makes the laser charge up then shoot for a second, after that second it disappears. Aiming at terrain while holding down left click while the sprite is gone makes the dust spawn, it also does no damage while its gone.
It also turns off autoreuse for the shotgun and meteor. If I click left click they spawn but if i hold only 1 of them spawn and I have to click again. Spaming left click they spawn extremely fast.

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

namespace ExampleMOD8.Items.Weapons
{
    public class SuperLaser : ModItem
    {
        public override void SetStaticDefaults()
        {

            DisplayName.SetDefault("SuperLaser");
            Tooltip.SetDefault("nioce");
        }
        public override void SetDefaults()
        {

            item.noMelee = true;
            item.damage = 8000;
            item.magic = true;
            item.width = 40;
            item.height = 40;
            item.useTime = 20;
            item.useAnimation = 20;
            item.useStyle = 5;
            item.knockBack = 30;
            item.value = 10000;
            item.rare = 11;
            item.UseSound = SoundID.Item1;
            item.autoReuse = true;
            item.channel = true;
            item.mana = 1;
            item.UseSound = SoundID.Item13;
            item.shootSpeed = 18f;
            item.shoot = mod.ProjectileType("LaserBeam");
        }
        public override bool PreDrawInInventory(SpriteBatch spriteBatch, Vector2 position, Rectangle frame, Color drawColor, Color itemColor, Vector2 origin, float scale)
        {
            //New item inventory sprite is Items/someItem.png
            spriteBatch.Draw(mod.GetTexture("Items/Super Laser"),
                  new Rectangle((int)position.X, (int)position.Y, (int)(frame.Width * scale), (int)(frame.Height * scale)),
                  drawColor);
            return false; //Stops default drawing
        }
        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 = 6 + Main.rand.Next(0); //This defines how many projectiles to shot. 4 + Main.rand.Next(2)= 4 or 5 shots
            for (int i = 0; i < numberProjectiles; i++)
            {
                Vector2 perturbedSpeed = new Vector2(speedX, speedY).RotatedByRandom(MathHelper.ToRadians(13)); // This defines the projectiles random spread . 30 degree spread.
                Projectile.NewProjectile(position.X, position.Y, perturbedSpeed.X, perturbedSpeed.Y, mod.ProjectileType("Shotgun2"), damage, knockBack, player.whoAmI);
            }
            for (int index = 0; index < numberProjectiles; ++index)
            {
                Vector2 vector2_1 = new Vector2((float)((double)player.position.X + (double)player.width * 0.5 + (double)(Main.rand.Next(201) * -player.direction) + ((double)Main.mouseX + (double)Main.screenPosition.X - (double)player.position.X)), (float)((double)player.position.Y + (double)player.height * 0.5 - 600.0));   //this defines the projectile width, direction and position
                vector2_1.X = (float)(((double)vector2_1.X + (double)player.Center.X) / 2.0) + (float)Main.rand.Next(-200, 201);
                vector2_1.Y -= (float)(100 * index);
                float num12 = (float)Main.mouseX + Main.screenPosition.X - vector2_1.X;
                float num13 = (float)Main.mouseY + Main.screenPosition.Y - vector2_1.Y;
                if ((double)num13 < 0.0) num13 *= -1f;
                if ((double)num13 < 20.0) num13 = 20f;
                float num14 = (float)Math.Sqrt((double)num12 * (double)num12 + (double)num13 * (double)num13);
                float num15 = item.shootSpeed / num14;
                float num16 = num12 * num15;
                float num17 = num13 * num15;
                float SpeedX = num16 + (float)Main.rand.Next(-40, 41) * 0.02f;  //this defines the projectile X position speed and randomnes
                float SpeedY = num17 + ((float)Main.rand.Next(-40, 41) * 0.02f);  //this defines the projectile Y position speed and randomnes
                Projectile.NewProjectile(vector2_1.X, vector2_1.Y, SpeedX, SpeedY, mod.ProjectileType("Meteor2"), damage, knockBack, Main.myPlayer, 0.0f, (float)Main.rand.Next(5));

            }

         
                return false;
         
        }

     
    }

}
 
Last edited:
Sorry, I am kind of ill right now, so I don't really have the mental capacity to untangle decompiled code... But I do have some guesses, if that helps.
Making a laser should be as simple as this:
Code:
public override bool Shoot (Player player, ref Vector2 position, ref float speedX, ref float speedY, ref int type, ref int damage, ref float knockBack)
{
      Projectile.NewProjectile(position.X, position.Y, 0, 0, mod.ProjectileType("laserNameHere“), damage, knockBack, player.whoAmI);
      return false;
}
(Wrote this from memory, I probably got some syntax wrong.)

I thing the use issue might be because you have both AutoReuse and Channeling set to true. Maybe try disabling AutoReuse, and see what happens.
If this is the case, the easiest solution would be to leave the channeling on, and deal with periodically firing the non-laser stuff. You could do this in a ModPlayer in PostUpdate. (Or maybe with async/await, though I never tried that in TModLoader)
 
Last edited:
So I found a new different laser beam projectile code and decided to paste that one over my old one. That seems to have fixed the problem. Now the beam doesn't immediately disappear after firing the weapon for like a second, and actually fires the beam without a problem. But the projectiles still only shoot out once when I hold left click (they dont use autoreuse) and they're still spammable.

Code:
using System;
using System.Collections.Generic;
using ExampleMod.Dusts;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ModLoader;
using Terraria.ID;
using Terraria.Enums;

namespace ExampleMod.Projectiles
{
    // The following laser shows a channeled ability, after charging up the laser will be fired
    // Using custom drawing, dust effects, and custom collision checks for tiles
    public class ExampleLaser : ModProjectile
    {
        // The maximum charge value
        private const float MaxChargeValue = 50f;
        //The distance charge particle from the player center
        private const float MoveDistance = 60f;

        // The actual distance is stored in the ai0 field
        // By making a property to handle this it makes our life easier, and the accessibility more readable
        public float Distance
        {
            get { return projectile.ai[0]; }
            set { projectile.ai[0] = value; }
        }

        // The actual charge value is stored in the localAI0 field
        public float Charge
        {
            get { return projectile.localAI[0]; }
            set { projectile.localAI[0] = value; }
        }

        // Are we at max charge? With c#6 you can simply use => which indicates this is a get only property
        public bool AtMaxCharge { get { return Charge == MaxChargeValue; } }

        public override void SetDefaults()
        {
            projectile.width = 10;
            projectile.height = 10;
            projectile.friendly = true;
            projectile.penetrate = -1;
            projectile.tileCollide = false;
            projectile.magic = true;
            projectile.hide = true;
        }

        public override bool PreDraw(SpriteBatch spriteBatch, Color lightColor)
        {
            // We start drawing the laser if we have charged up
            if (AtMaxCharge)
            {
                DrawLaser(spriteBatch, Main.projectileTexture[projectile.type], Main.player[projectile.owner].Center,
                    projectile.velocity, 10, projectile.damage, -1.57f, 1f, 1000f, Color.White, (int)MoveDistance);
            }
            return false;
        }

        // The core function of drawing a laser
        public void DrawLaser(SpriteBatch spriteBatch, Texture2D texture, Vector2 start, Vector2 unit, float step, int damage, float rotation = 0f, float scale = 1f, float maxDist = 2000f, Color color = default(Color), int transDist = 50)
        {
            Vector2 origin = start;
            float r = unit.ToRotation() + rotation;

            #region Draw laser body
            for (float i = transDist; i <= Distance; i += step)
            {
                Color c = Color.White;
                origin = start + i * unit;
                spriteBatch.Draw(texture, origin - Main.screenPosition,
                    new Rectangle(0, 26, 28, 26), i < transDist ? Color.Transparent : c, r,
                    new Vector2(28 * .5f, 26 * .5f), scale, 0, 0);
            }
            #endregion

            #region Draw laser tail
            spriteBatch.Draw(texture, start + unit * (transDist - step) - Main.screenPosition,
                new Rectangle(0, 0, 28, 26), Color.White, r, new Vector2(28 * .5f, 26 * .5f), scale, 0, 0);
            #endregion

            #region Draw laser head
            spriteBatch.Draw(texture, start + (Distance + step) * unit - Main.screenPosition,
                new Rectangle(0, 52, 28, 26), Color.White, r, new Vector2(28 * .5f, 26 * .5f), scale, 0, 0);
            #endregion
        }

        // Change the way of collision check of the projectile
        public override bool? Colliding(Rectangle projHitbox, Rectangle targetHitbox)
        {
            // We can only collide if we are at max charge, which is when the laser is actually fired
            if (AtMaxCharge)
            {
                Player player = Main.player[projectile.owner];
                Vector2 unit = projectile.velocity;
                float point = 0f;
                // Run an AABB versus Line check to look for collisions, look up AABB collision first to see how it works
                // It will look for collisions on the given line using AABB
                return Collision.CheckAABBvLineCollision(targetHitbox.TopLeft(), targetHitbox.Size(), player.Center,
                    player.Center + unit * Distance, 22, ref point);
            }
            return false;
        }

        // Set custom immunity time on hitting an NPC
        public override void OnHitNPC(NPC target, int damage, float knockback, bool crit)
        {
            target.immune[projectile.owner] = 5;
        }

        // The AI of the projectile
        public override void AI()
        {
            Vector2 mousePos = Main.MouseWorld;
            Player player = Main.player[projectile.owner];

            #region Set projectile position
            // Multiplayer support here, only run this code if the client running it is the owner of the projectile
            if (projectile.owner == Main.myPlayer)
            {
                Vector2 diff = mousePos - player.Center;
                diff.Normalize();
                projectile.velocity = diff;
                projectile.direction = Main.MouseWorld.X > player.position.X ? 1 : -1;
                projectile.netUpdate = true;
            }
            projectile.position = player.Center + projectile.velocity * MoveDistance;
            projectile.timeLeft = 2;
            int dir = projectile.direction;
            player.ChangeDir(dir);
            player.heldProj = projectile.whoAmI;
            player.itemTime = 2;
            player.itemAnimation = 2;
            player.itemRotation = (float)Math.Atan2(projectile.velocity.Y * dir, projectile.velocity.X * dir);
            #endregion

            #region Charging process
            // Kill the projectile if the player stops channeling
            if (!player.channel)
            {
                projectile.Kill();
            }
            else
            {
                // Do we still have enough mana? If not, we kill the projectile because we cannot use it anymore
                if (Main.time % 10 < 1 && !player.CheckMana(player.inventory[player.selectedItem].mana, true))
                {
                    projectile.Kill();
                }
                Vector2 offset = projectile.velocity;
                offset *= MoveDistance - 20;
                Vector2 pos = player.Center + offset - new Vector2(10, 10);
                if (Charge < MaxChargeValue)
                {
                    Charge++;
                }
                int chargeFact = (int)(Charge / 20f);
                Vector2 dustVelocity = Vector2.UnitX * 18f;
                dustVelocity = dustVelocity.RotatedBy(projectile.rotation - 1.57f, default(Vector2));
                Vector2 spawnPos = projectile.Center + dustVelocity;
                for (int k = 0; k < chargeFact + 1; k++)
                {
                    Vector2 spawn = spawnPos + ((float)Main.rand.NextDouble() * 6.28f).ToRotationVector2() * (12f - (chargeFact * 2));
                    Dust dust = Main.dust[Dust.NewDust(pos, 20, 20, 226, projectile.velocity.X / 2f,
                        projectile.velocity.Y / 2f, 0, default(Color), 1f)];
                    dust.velocity = Vector2.Normalize(spawnPos - spawn) * 1.5f * (10f - chargeFact * 2f) / 10f;
                    dust.noGravity = true;
                    dust.scale = Main.rand.Next(10, 20) * 0.05f;
                }
            }
            #endregion

            #region Set laser tail position and dusts
            if (Charge < MaxChargeValue) return;
            Vector2 start = player.Center;
            Vector2 unit = projectile.velocity;
            unit *= -1;
            for (Distance = MoveDistance; Distance <= 2200f; Distance += 5f)
            {
                start = player.Center + projectile.velocity * Distance;
                if (!Collision.CanHit(player.Center, 1, 1, start, 1, 1))
                {
                    Distance -= 5f;
                    break;
                }
            }

            Vector2 dustPos = player.Center + projectile.velocity * Distance;
            //Imported dust code from source because I'm lazy
            for (int i = 0; i < 2; ++i)
            {
                float num1 = projectile.velocity.ToRotation() + (Main.rand.Next(2) == 1 ? -1.0f : 1.0f) * 1.57f;
                float num2 = (float)(Main.rand.NextDouble() * 0.8f + 1.0f);
                Vector2 dustVel = new Vector2((float)Math.Cos(num1) * num2, (float)Math.Sin(num1) * num2);
                Dust dust = Main.dust[Dust.NewDust(dustPos, 0, 0, 226, dustVel.X, dustVel.Y, 0, new Color(), 1f)];
                dust.noGravity = true;
                dust.scale = 1.2f;
                dust = Dust.NewDustDirect(Main.player[projectile.owner].Center, 0, 0, 31,
                    -unit.X * Distance, -unit.Y * Distance);
                dust.fadeIn = 0f;
                dust.noGravity = true;
                dust.scale = 0.88f;
                dust.color = Color.Cyan;
            }
            if (Main.rand.Next(5) == 0)
            {
                Vector2 offset = projectile.velocity.RotatedBy(1.57f, new Vector2()) * ((float)Main.rand.NextDouble() - 0.5f) *
                                 projectile.width;
                Dust dust = Main.dust[
                    Dust.NewDust(dustPos + offset - Vector2.One * 4f, 8, 8, 31, 0.0f, 0.0f, 100, new Color(), 1.5f)];
                dust.velocity = dust.velocity * 0.5f;
                dust.velocity.Y = -Math.Abs(dust.velocity.Y);

                unit = dustPos - Main.player[projectile.owner].Center;
                unit.Normalize();
                dust = Main.dust[
                    Dust.NewDust(Main.player[projectile.owner].Center + 55 * unit, 8, 8, 31, 0.0f, 0.0f, 100, new Color(), 1.5f)];
                dust.velocity = dust.velocity * 0.5f;
                dust.velocity.Y = -Math.Abs(dust.velocity.Y);
            }
            #endregion

            //Add lights
            DelegateMethods.v3_1 = new Vector3(0.8f, 0.8f, 1f);
            Utils.PlotTileLine(projectile.Center, projectile.Center + projectile.velocity * (Distance - MoveDistance), 26,
                DelegateMethods.CastLight);
        }

        public override bool ShouldUpdatePosition()
        {
            return false;
        }

        public override void CutTiles()
        {
            DelegateMethods.tilecut_0 = TileCuttingContext.AttackProjectile;
            Vector2 unit = projectile.velocity;
            Utils.PlotTileLine(projectile.Center, projectile.Center + unit * Distance, (projectile.width + 16) * projectile.scale, DelegateMethods.CutTiles);
        }
    }
}

or here's the link if you prefer that -> https://github.com/blushiemagic/tModLoader/blob/master/ExampleMod/Projectiles/ExampleLaser.cs

using your code does exactly the same thing as having return set to true, holding down left click shoots the laser which is working perfectly fine but the other projectiles only shoot out once, they're spammable. Setting AutoReuse to false has the same effect, Setting AutoReuse to true has the same effect, setting Channel to false makes the AutoReuse work but the fire rate is insanely fast (also prevents the laser from shooting obviously).

I hope your illness gets better, Thank you again for the help :D
 
Yeah, that seems to line line up with my theory. It looks like you'll have to deal with the reuse loop and the cool down manually somewhere else.
I'd love to write that for you, but I can't right now... So, good luck. :)
 
I'll keep experimenting, good luck to you too! :)
 
Back
Top Bottom