Projectiles
Last update: 9th of June, 2016
Table of contents:
1. Prerequisites
2. Introduction
3. The first projectile
4. Shooting in angles
Prerequisites
Make sure you have....
Introduction
We're going to look at projectiles in general in this tutorial. How can we make a projectile? How can we use our custom projectiles?
This tutorial does not cover projectile AI, that's for a later tutorial
The first projectile
Unlike our previous tutorials, we will have to create some files now. First, create a 'Projectiles' folder.
Inside this folder, create a file named WoodenArrowOfDeath.cs
If you're not using MVS, take the following skeleton. Make sure to change the namespace.
As you can see, we have two classes here. One is an item, the other is a projectile. The reason we need an item is because the item will function actual ammo.
Since we've covered items before, I don't need to explain how we need to tackle this, we can make use of CloneDefaults()
Even though I've included item.shoot and item.ammo, I wouldn't have to as they were already cloned. I did this, because those are the 2 most important settings for now.
Before we can actually fire things, we must finish our ModProjectile. We can apply the same technique using CloneDefaults()
As you can see, I've made use of OnHitNPC() in this projectile, as we've learned about this hook in a previous tutorial.
These arrows will strike the target an additional time for their maximum life, this ensures any create hit by these arrows should instantly die.
Go ahaid, go in-game and craft some arrows. Take a bow and start shooting things. Everything should die.
I hope you can see the similarities between projectiles and items, their code is incredibly similar in this case. If you've followed the previous tutorial, this shouldn't all be too difficult.
Let's continue with a new projectile, an improved Terrabeam for our Better Terra Blade.
Better Terrabeam
To save you time, I'm going to give you the code of this projectile right away. You will most likely understand the full code, as it is extremely similar to what we've looked at before.
Go ahaid and paste the following code in a new file named 'BetterTerrabeam.cs' inside 'Projectiles'. Again, remember to change the namespace.
As you can see, I've made this projectile apply an OnFire debuff for 5 seconds when it hits something.
Let's go back to our BetterTerraBlade ModItem in MyFirstItem.cs , we need to change the projectile the weapon shoots to our new ModProjectile.
We can do it as follows:
That's it! Go in-game and test it out! Our new Terrabeams should now apply the OnFire debuff!
Shooting in angles
You can fire projectiles in certain angles, if you want. We can use the following snippet to do so.
These projectiles will fire at a certain angle. Let's implement this in our Better Terra Blade. We should use the Shoot() hook for this.
Add this hook to your BetterTerraBlade class.
The reason we return false is because we don't want it to shoot the one original projectile. You can change the number of projectiles and the angle in the two variables.
We can also shoot projectiles in an even arc at an angle using the following snippet:
Let's say we'd want to fire 4 projectiles between the ones we are shooting in our previous code, we could do the following: (improved code slightly)
This results in the following:
As you can see, all projectiles neatly come from the exact middle which is where I clicked.
THIS TUTORIAL IS HIGHLY WIP, MORE COMING.
Full codes
Go to next tutorial: Not yet available
Last update: 9th of June, 2016
Table of contents:
1. Prerequisites
2. Introduction
3. The first projectile
4. Shooting in angles
Prerequisites
Make sure you have....
- ...the latest tModLoader installed (offical thread)
- ...the latest Microsoft .NET Framework installed (offical .net site)
- ...the Microsoft XNA Framework installed (you have this if you are able to play Terraria)
- ...Microsoft Visual Studio (community = free) installed with the C# workspace (official MVS site)
- .. actually, MVS isn't required but it's a very nice IDE to work with. You can also use notepad or notepadd++ or alike, but MVS will be super useful for noobs that makes simple mistakes because the IDE will help you program
- ...C# knowledge, all tML mods currently are programmed with the C# language
- ...followed the tutorial prior to this one http://forums.terraria.org/index.php?threads/tutorial-3-items.44842/
Introduction
We're going to look at projectiles in general in this tutorial. How can we make a projectile? How can we use our custom projectiles?
This tutorial does not cover projectile AI, that's for a later tutorial
The first projectile
Unlike our previous tutorials, we will have to create some files now. First, create a 'Projectiles' folder.
Inside this folder, create a file named WoodenArrowOfDeath.cs
If you're not using MVS, take the following skeleton. Make sure to change the namespace.
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Terraria;
using Terraria.ModLoader;
using Terraria.ID;
namespace MyFirstMod.Projectiles
{
public class WoodenArrowOfDeathItem : ModItem
{
}
public class WoodenArrowOfDeath : ModProjectile
{
}
}
Since we've covered items before, I don't need to explain how we need to tackle this, we can make use of CloneDefaults()
Even though I've included item.shoot and item.ammo, I wouldn't have to as they were already cloned. I did this, because those are the 2 most important settings for now.
Code:
public class WoodenArrowOfDeathItem : ModItem
{
public override void SetDefaults()
{
item.CloneDefaults(ItemID.WoodenArrow);
item.name = "Wooden Arrow of Death";
item.shoot = mod.ProjectileType("WoodenArrowOfDeath");
item.ammo = ProjectileID.WoodenArrowFriendly;
}
public override bool Autoload(ref string name, ref string texture, IList<EquipType> equips)
{
texture = "Terraria/Item_" + ItemID.WoodenArrow;
return true;
}
public override void AddRecipes()
{
ModRecipe recipe = new ModRecipe(mod);
recipe.SetResult(this);
recipe.AddRecipe();
}
}
Before we can actually fire things, we must finish our ModProjectile. We can apply the same technique using CloneDefaults()
Code:
public class WoodenArrowOfDeath : ModProjectile
{
public override void SetDefaults()
{
projectile.CloneDefaults(ProjectileID.WoodenArrowFriendly);
projectile.name = "Wooden Arrow of Death";
aiType = ProjectileID.WoodenArrowFriendly;
}
public override bool Autoload(ref string name, ref string texture)
{
texture = "Terraria/Projectile_" + ProjectileID.WoodenArrowFriendly;
return true;
}
public override void OnHitNPC(NPC target, int damage, float knockback, bool crit)
{
target.StrikeNPCNoInteraction(target.lifeMax, 0f, -target.direction);
}
}
These arrows will strike the target an additional time for their maximum life, this ensures any create hit by these arrows should instantly die.
Go ahaid, go in-game and craft some arrows. Take a bow and start shooting things. Everything should die.
I hope you can see the similarities between projectiles and items, their code is incredibly similar in this case. If you've followed the previous tutorial, this shouldn't all be too difficult.
Let's continue with a new projectile, an improved Terrabeam for our Better Terra Blade.
Better Terrabeam
To save you time, I'm going to give you the code of this projectile right away. You will most likely understand the full code, as it is extremely similar to what we've looked at before.
Go ahaid and paste the following code in a new file named 'BetterTerrabeam.cs' inside 'Projectiles'. Again, remember to change the namespace.
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Terraria;
using Terraria.ModLoader;
using Terraria.ID;
namespace MyFirstMod.Projectiles
{
class BetterTerraBeam : ModProjectile
{
public override void SetDefaults()
{
projectile.CloneDefaults(ProjectileID.TerraBeam);
projectile.name = "Terrabeam of Death";
aiType = ProjectileID.TerraBeam;
}
public override bool Autoload(ref string name, ref string texture)
{
texture = "Terraria/Projectile_" + ProjectileID.TerraBeam;
return true;
}
public override void OnHitNPC(NPC target, int damage, float knockback, bool crit)
{
target.AddBuff(BuffID.OnFire, 5 * 60);
}
}
}
Let's go back to our BetterTerraBlade ModItem in MyFirstItem.cs , we need to change the projectile the weapon shoots to our new ModProjectile.
We can do it as follows:
Code:
public override void SetDefaults()
{
item.CloneDefaults(ItemID.TerraBlade);
item.name = "Better Terra Blade";
item.damage = 500;
item.shoot = mod.ProjectileType("BetterTerrabeam");
}
Shooting in angles
You can fire projectiles in certain angles, if you want. We can use the following snippet to do so.
Code:
float rotation = MathHelper.ToRadians(20);
for (int i = 0; i < numberProjectiles; i++)
{
Vector2 perturbedSpeed = new Vector2(speedX, speedY).RotatedBy(MathHelper.Lerp(-rotation, rotation, i / (numberProjectiles - 1)));
Projectile.NewProjectile(position.X, position.Y, perturbedSpeed.X, perturbedSpeed.Y, type, damage, knockBack, player.whoAmI);
}
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)
{
int numberProjectiles = 2;
float rotation = MathHelper.ToRadians(20);
for (int i = 0; i < numberProjectiles; i++)
{
Vector2 perturbedSpeed = new Vector2(speedX, speedY).RotatedBy(MathHelper.Lerp(-rotation, rotation, i / (numberProjectiles - 1)));
Projectile.NewProjectile(position.X, position.Y, perturbedSpeed.X, perturbedSpeed.Y, type, damage, knockBack, player.whoAmI);
}
return false;
}
The reason we return false is because we don't want it to shoot the one original projectile. You can change the number of projectiles and the angle in the two variables.
We can also shoot projectiles in an even arc at an angle using the following snippet:
Code:
public override bool Shoot(Player player, ref Microsoft.Xna.Framework.Vector2 position, ref float speedX, ref float speedY, ref int type, ref int damage, ref float knockBack)
{
float spread = 15f * 0.0174f;
float baseSpeed = (float)Math.Sqrt(speedX * speedX + speedY * speedY);
double startAngle = Math.Atan2(speedX, speedY)- spread/2;
double deltaAngle = spread/8f;
double offsetAngle;
int i;
for (i = 0; i < 8;i++ )
{
offsetAngle = startAngle + deltaAngle * i;
Terraria.Projectile.NewProjectile(position.X, position.Y, baseSpeed*(float)Math.Sin(offsetAngle), baseSpeed*(float)Math.Cos(offsetAngle), item.shoot, damage, knockBack, item.owner);
}
return false;
}
Let's say we'd want to fire 4 projectiles between the ones we are shooting in our previous code, we could do the following: (improved code slightly)
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)
{
int numberProjectiles = 2;
float rotation = MathHelper.ToRadians(20);
for (int i = 0; i < numberProjectiles + 1; i++)
{
Vector2 perturbedSpeed = new Vector2(speedX, speedY).RotatedBy(MathHelper.Lerp(-rotation, rotation, i / (numberProjectiles - 1)));
Projectile.NewProjectile(position.X, position.Y, perturbedSpeed.X, perturbedSpeed.Y, type, damage, knockBack, player.whoAmI);
}
int numProjectiles2 = 4;
float spread = MathHelper.ToRadians(10);
float baseSpeed = (float)Math.Sqrt(speedX * speedX + speedY * speedY);
double startAngle = Math.Atan2(speedX, speedY) - spread / 2;
double deltaAngle = spread / (float)numProjectiles2;
double offsetAngle;
for (int j = 0; j < numProjectiles2; j++)
{
offsetAngle = startAngle + deltaAngle * j;
Projectile.NewProjectile(position.X, position.Y, baseSpeed * (float)Math.Sin(offsetAngle), baseSpeed * (float)Math.Cos(offsetAngle), type, damage, knockBack, player.whoAmI);
}
return false;
}
As you can see, all projectiles neatly come from the exact middle which is where I clicked.
THIS TUTORIAL IS HIGHLY WIP, MORE COMING.
Full codes
Code:
public class BetterTerraBlade : ModItem
{
public override void SetDefaults()
{
item.CloneDefaults(ItemID.TerraBlade);
item.name = "Better Terra Blade";
item.damage = 500;
item.shoot = mod.ProjectileType("BetterTerrabeam");
}
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 = 2;
float rotation = MathHelper.ToRadians(20);
for (int i = 0; i < numberProjectiles + 1; i++)
{
Vector2 perturbedSpeed = new Vector2(speedX, speedY).RotatedBy(MathHelper.Lerp(-rotation, rotation, i / (numberProjectiles - 1)));
Projectile.NewProjectile(position.X, position.Y, perturbedSpeed.X, perturbedSpeed.Y, type, damage, knockBack, player.whoAmI);
}
int numProjectiles2 = 4;
float spread = MathHelper.ToRadians(10);
float baseSpeed = (float)Math.Sqrt(speedX * speedX + speedY * speedY);
double startAngle = Math.Atan2(speedX, speedY) - spread / 2;
double deltaAngle = spread / (float)numProjectiles2;
double offsetAngle;
for (int j = 0; j < numProjectiles2; j++)
{
offsetAngle = startAngle + deltaAngle * j;
Projectile.NewProjectile(position.X, position.Y, baseSpeed * (float)Math.Sin(offsetAngle), baseSpeed * (float)Math.Cos(offsetAngle), type, damage, knockBack, player.whoAmI);
}
return false;
}
public override void OnHitNPC(Player player, NPC target, int damage, float knockBack, bool crit)
{
target.AddBuff(BuffID.OnFire, 5 * 60);
if (target.townNPC)
{
player.Hurt(5, -player.direction, false, false, " is an evil :red:...");
}
}
public override bool Autoload(ref string name, ref string texture, IList<EquipType> equips)
{
texture = "Terraria/Item_" + ItemID.TerraBlade;
return mod.Properties.Autoload;
}
public override void AddRecipes()
{
ModRecipe recipe = new ModRecipe(mod);
recipe.SetResult(this);
recipe.AddRecipe();
}
}
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Terraria;
using Terraria.ModLoader;
using Terraria.ID;
namespace MyFirstMod.Projectiles
{
public class WoodenArrowOfDeathItem : ModItem
{
public override void SetDefaults()
{
item.CloneDefaults(ItemID.WoodenArrow);
item.name = "Wooden Arrow of Death";
item.shoot = mod.ProjectileType("WoodenArrowOfDeath");
item.ammo = ProjectileID.WoodenArrowFriendly;
}
public override bool Autoload(ref string name, ref string texture, IList<EquipType> equips)
{
texture = "Terraria/Item_" + ItemID.WoodenArrow;
return mod.Properties.Autoload;
}
public override void AddRecipes()
{
ModRecipe recipe = new ModRecipe(mod);
recipe.SetResult(this);
recipe.AddRecipe();
}
}
public class WoodenArrowOfDeath : ModProjectile
{
public override void SetDefaults()
{
projectile.CloneDefaults(ProjectileID.WoodenArrowFriendly);
projectile.name = "Wooden Arrow of Death";
aiType = ProjectileID.WoodenArrowFriendly;
}
public override bool Autoload(ref string name, ref string texture)
{
texture = "Terraria/Projectile_" + ProjectileID.WoodenArrowFriendly;
return mod.Properties.Autoload;
}
public override void OnHitNPC(NPC target, int damage, float knockback, bool crit)
{
target.StrikeNPCNoInteraction(target.lifeMax, 0f, -target.direction);
}
}
}
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Terraria;
using Terraria.ModLoader;
using Terraria.ID;
namespace MyFirstMod.Projectiles
{
class BetterTerrabeam : ModProjectile
{
public override void SetDefaults()
{
projectile.CloneDefaults(ProjectileID.TerraBeam);
projectile.name = "Terrabeam of Death";
aiType = ProjectileID.TerraBeam;
}
public override bool Autoload(ref string name, ref string texture)
{
texture = "Terraria/Projectile_" + ProjectileID.TerraBeam;
return mod.Properties.Autoload;
}
public override void OnHitNPC(NPC target, int damage, float knockback, bool crit)
{
target.AddBuff(BuffID.OnFire, 5 * 60);
}
}
}
Go to next tutorial: Not yet available