tModLoader [Tutorial] Projectile Guide and Implementation: tModLoader Edition

What do you mean by spawning a specific projectile on a target? If you mean changing what the projectile is firing, you just modify 5th parameter in Projectile.NewProjectile. Here's a layout of the parameters of the Projectile.NewProjectile.

Code:
Projectile.NewProjectile(
int positionX,
int positionY,
float velocityX,
float velocityY,
int type,
int damage,
float knockback,
int ownerId,
float ai1,
float ai2
)

If this is not the case, could you give me an example of what you mean by your question?

I got this figured out now but when I throw the projectile(it's a boomerang) it just immediately despawns.

Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Terraria;
using Terraria.ModLoader;

namespace GuidosMod.projectiles
{   
    public class NightBoomerang : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.name = "NightBoomerang";
            projectile.width = 35;
            projectile.height = 42;
            projectile.timeLeft = 2400;
            projectile.penetrate = -1;
            projectile.friendly = true;
            projectile.hostile = false;
            projectile.tileCollide = true;
            projectile.ignoreWater = true;
            projectile.melee = true;
            projectile.aiStyle = 3;
        }
    public override void AI()
{
    for(int i = 0; i < 200; i++)
    {
       //Enemy NPC variable being set
       NPC target = Main.npc[i];

       //Getting the shooting trajectory
       float shootToX = target.position.X + (float)target.width * 0.5f - projectile.Center.X;
       float shootToY = target.position.Y - projectile.Center.Y;
       float distance = (float)System.Math.Sqrt((double)(shootToX * shootToX + shootToY * shootToY));

       //If the distance between the projectile and the live target is active
       if(distance < 480f && !target.friendly && target.active)
       {
           if(projectile.ai[0] > 4f) //Assuming you are already incrementing this in AI outside of for loop
           {
               //Dividing the factor of 3f which is the desired velocity by distance
               distance = 3f / distance;
  
               //Multiplying the shoot trajectory with distance times a multiplier if you so choose to
               shootToX *= distance * 5;
               shootToY *= distance * 5;
  
               //Shoot projectile and set ai back to 0
               Projectile.NewProjectile(projectile.Center.X, projectile.Center.Y, shootToX, shootToY, mod.ProjectileType("UnholyStar")
, 30, 0, Main.myPlayer, 0f, 0f); //Spawning a projectile
               Main.PlaySound(33, (int)projectile.position.X, (int)projectile.position.Y, 88); 
               projectile.ai[0] = 0f;
           }
       }
    }
    projectile.ai[0] += 1f;
}
    }
}
 
I got this figured out now but when I throw the projectile(it's a boomerang) it just immediately despawns.

Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Terraria;
using Terraria.ModLoader;

namespace GuidosMod.projectiles
{ 
    public class NightBoomerang : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.name = "NightBoomerang";
            projectile.width = 35;
            projectile.height = 42;
            projectile.timeLeft = 2400;
            projectile.penetrate = -1;
            projectile.friendly = true;
            projectile.hostile = false;
            projectile.tileCollide = true;
            projectile.ignoreWater = true;
            projectile.melee = true;
            projectile.aiStyle = 3;
        }
    public override void AI()
{
    for(int i = 0; i < 200; i++)
    {
       //Enemy NPC variable being set
       NPC target = Main.npc[i];

       //Getting the shooting trajectory
       float shootToX = target.position.X + (float)target.width * 0.5f - projectile.Center.X;
       float shootToY = target.position.Y - projectile.Center.Y;
       float distance = (float)System.Math.Sqrt((double)(shootToX * shootToX + shootToY * shootToY));

       //If the distance between the projectile and the live target is active
       if(distance < 480f && !target.friendly && target.active)
       {
           if(projectile.ai[0] > 4f) //Assuming you are already incrementing this in AI outside of for loop
           {
               //Dividing the factor of 3f which is the desired velocity by distance
               distance = 3f / distance;

               //Multiplying the shoot trajectory with distance times a multiplier if you so choose to
               shootToX *= distance * 5;
               shootToY *= distance * 5;

               //Shoot projectile and set ai back to 0
               Projectile.NewProjectile(projectile.Center.X, projectile.Center.Y, shootToX, shootToY, mod.ProjectileType("UnholyStar")
, 30, 0, Main.myPlayer, 0f, 0f); //Spawning a projectile
               Main.PlaySound(33, (int)projectile.position.X, (int)projectile.position.Y, 88);
               projectile.ai[0] = 0f;
           }
       }
    }
    projectile.ai[0] += 1f;
}
    }
}
So, the thing about this is that sometimes when combining vanilla AI with custom AI, a problem occurs when using the projectile.ai as a counter or switch for a projectile, as vanilla ones tend to use this thing a lot to determine what the projectile does. Unfortunately this is one of those cases where screwing with the projectile.ai screws with the vanilla AI. Best solution? Not to use projectile.ai as a counter, but instead we can use the alternative ai counter, localAI since it is not used in the vanilla boomerang code. We can just replace "projectile.ai[0]" with "projectile.localAI[0]" to hopefully, mitigate this situation.
 
Ok question, what is the usestyle for the staff because mine is 5
here is the picture:
 

Attachments

  • Staff.png
    Staff.png
    411.5 KB · Views: 848
Ok question, what is the usestyle for the staff because mine is 5
here is the picture:
Can't really tell with what you mean by using the picture, but I can probably assume that you are having a problem with your staff not having the right rotation? If that is the case, add this to your set defaults.

Code:
/*From ExampleMod in tModLoader github*/
Item.staff[item.type] = true; //this makes the useStyle animate as a staff instead of as a gun

If you are having problems with a floaty staff, use the HoldoutOffset hook.
 
Ok I just realized I made a big mistake. The other day I asked a question about using a custom sound for projectiles in the tAPI thread. I meant to post it here in the tModLoader thread.

Does this change anything? Am I still unable to change the sound used by my projectile due to the AI type?

EDIT: nvm I just realized that you noticed I meant Modloader instead and answered according to that.

Just curious, why can't you use a custom sound with an AI type? I don't really know much about C# or Terreria modding but if you can use CloneDefaults to get all the data on a projectile then modify all these parts of it such as the name or whether or not it can collide with tiles, why can't you also modify the sound it uses?
 
Last edited:
Ok I just realized I made a big mistake. The other day I asked a question about using a custom sound for projectiles in the tAPI thread. I meant to post it here in the tModLoader thread.

Does this change anything? Am I still unable to change the sound used by my projectile due to the AI type?

EDIT: nvm I just realized that you noticed I meant Modloader instead and answered according to that.

Just curious, why can't you use a custom sound with an AI type? I don't really know much about C# or Terreria modding but if you can use CloneDefaults to get all the data on a projectile then modify all these parts of it such as the name or whether or not it can collide with tiles, why can't you also modify the sound it uses?
Because it calls the sound inside the style, there isn't a property in a projectile for the projectile to make a sound.
 
You can use the code "Having a projectile shoot at something" in the next post after the opening post and replace all the "projectile" to "npc" and the "npc" to "player". After that, change the projectile ID parameter in Projectile.NewProjectile to the projectile you want. The only thing that is a problem is that if you have it shoot a vanilla projectile, it will be considered friendly to the player, so it will damage the enemy npc instead, so it's best you use a custom projectile for this.
OK, thanks, but where is that? Bold it:
Npc.NewProjectile(projectile.Center.X, projectile.Center.Y, shootToX, shootToY, 1, 0, 0, Main.myPlayer, 0f, 0f);
Also, what sound is the bow fire sound?
 
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
namespace ApocalypseMOD.Projectiles
{
public class Boss : ModProjectile
{
public override void SetDefaults()
{
projectile.name = "ProjectileName"; //projectile name
projectile.width = 16; //projectile width
projectile.height = 16; //projectile height
projectile.friendly = false; //make that the projectile will not damage you
projectile.melee = true; //
projectile.tileCollide = true; //make that the projectile will be destroed if it hits the terrain
projectile.penetrate = 2; //how many npc will penetrate
projectile.timeLeft = 200; //how many time this projectile has before disepire
projectile.light = 0.75f; // projectile light
projectile.extraUpdates = 1;
projectile.ignoreWater = true;
}
public override void AI()
{
int minDist = 1000;
int target = 0;
float dX = 0f;
float dY = 0f;
float distance = 0;
float speed = 15f;
if(projectile.ai[0] % 60 == 0)
{
for(int i = 0; i < Main.npc.Length - 1; i++)
{
NPC enemy = Main.npc;
if(!enemy.friendly)
{
dX = enemy.Center.X - projectile.Center.X;
dY = enemy.Center.Y - projectile.Center.Y;
distance = (float) Math.Sqrt((double)(dX * dX + dY * dY));
if(distance < minDist)
{
minDist = distance;
enemy = i;
}
}

this is the homing projectile code.
this is the error:
error CS0266: Cannot implicitly convert type 'float' to 'int'. An explicit conversion exists (are you missing a cast?)
error CS0029: Cannot implicitly convert type 'int' to 'Terraria.NPC'
error CS0103: The name 'enemy' does not exist in the current context

[doublepost=1495732220,1495732202][/doublepost]help would be appreciated thanks!
 
I've given up on that, but my boss still needs spicing up in the ranged department. how would I get him to hoot multiple projectiles at once?
 
I've given up on that, but my boss still needs spicing up in the ranged department. how would I get him to hoot multiple projectiles at once?
Use the "Shoot()" hook on your item.

using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
namespace ApocalypseMOD.Projectiles
{
public class Boss : ModProjectile
{
public override void SetDefaults()
{
projectile.name = "ProjectileName"; //projectile name
projectile.width = 16; //projectile width
projectile.height = 16; //projectile height
projectile.friendly = false; //make that the projectile will not damage you
projectile.melee = true; //
projectile.tileCollide = true; //make that the projectile will be destroed if it hits the terrain
projectile.penetrate = 2; //how many npc will penetrate
projectile.timeLeft = 200; //how many time this projectile has before disepire
projectile.light = 0.75f; // projectile light
projectile.extraUpdates = 1;
projectile.ignoreWater = true;
}
public override void AI()
{
int minDist = 1000;
int target = 0;
float dX = 0f;
float dY = 0f;
float distance = 0;
float speed = 15f;
if(projectile.ai[0] % 60 == 0)
{
for(int i = 0; i < Main.npc.Length - 1; i++)
{
NPC enemy = Main.npc;
if(!enemy.friendly)
{
dX = enemy.Center.X - projectile.Center.X;
dY = enemy.Center.Y - projectile.Center.Y;
distance = (float) Math.Sqrt((double)(dX * dX + dY * dY));
if(distance < minDist)
{
minDist = distance;
enemy = i;
}
}

this is the homing projectile code.
this is the error:
error CS0266: Cannot implicitly convert type 'float' to 'int'. An explicit conversion exists (are you missing a cast?)
error CS0029: Cannot implicitly convert type 'int' to 'Terraria.NPC'
error CS0103: The name 'enemy' does not exist in the current context

[doublepost=1495732220,1495732202][/doublepost]help would be appreciated thanks!
Main.npc is an array, use i to index through it, also this doesn't do anything.
 
hey, I know its not related but how would I get one of three ores to spawn on destroying a block?
(I need it for my custom hardmode)
 
hey, I know its not related but how would I get one of three ores to spawn on destroying a block?
(I need it for my custom hardmode)
Here's the hook

Code:
public override void Drop(int i, int j) 
{

}

Use Main.rand.next(num) for rand num

Item.NewItem to create new items
 
here is current code:

where and how would I add item.newitem? and how would I define the three tiles? would I write another line of code or connect it to one of the pieces you've shown me?
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ModLoader;

namespace ApocalypseMOD.Tiles
{
public class HallowedAltar : ModTile
{
public override void SetDefaults()
{
Main.tileSolid[Type] = true;
Main.tileMergeDirt[Type] = false;
Main.tileBlockLight[Type] = true; //true for block to emit light
Main.tileLighted[Type] = true;
AddMapEntry(new Color(200, 200, 200));
}

public override void ModifyLight(int i, int j, ref float r, ref float g, ref float b) //light colors
{
r = 0.5f;
g = 0.5f;
b = 0.5f;
public override void Drop(int i, int j)
{
}
Use Main.rand.next(3)
}
}
}
 
anyone?
[doublepost=1495769774,1495769443][/doublepost]do I have to move this for you to reply?
 
anyone?
[doublepost=1495769774,1495769443][/doublepost]do I have to move this for you to reply?
Just a serious question, but do you know what patience is? Please, stop spamming until you get a reply, just post and wait for an answer. This is really inappropriate behavior, and is a cause for not getting help.

A hook is a thing that allows you to add resources to an object in tModLoader. An example of this is "public override void Drop(int i, int j)". This works like how you set up SetDefaults and Recipes. So this should be under the "class" bracket since this is a function within a "class". "Main.rand.next(int i)" is a function that returns a random integer value from 0 up to that integer value, so it should be stored within an "int" variable. Item.NewItem() is another function returning an integer value that is an index to "Main.item[]" array. But you do not care about this anyways so you do not need to store the integer value. So your code should look something like this, I'm just going to give you psuedocode for you to figure out.

Code:
public override void Drop(int i, int j) //i and j pertain to the tile x and y coordinates so you will need to multiply i and j by 16 to get their actual coordinates
   if Main.rand.next(2) = 0 //this is 50% drop rate because it will either choose 0 or 1
       Item.NewItem(positionX, positionY, width, height, itemId, numberOfDrops) //You should be able to figure out what to switch these values for

Next time you start spamming, I won't be helping you. Also, please stay within the topic of the thread, this question needs to go to The Official tModLoader Help Thread. Now, please, be patient next time, as some are not as tolerant.
 
Back
Top Bottom