tModLoader [Tutorial] Projectile Guide and Implementation: tModLoader Edition

Sin Costan

Eye of Cthulhu
[Tutorial] Projectile Guide and Implementation
tModLoader Edition
by: Sin Costan

Introduction:
Hello TCF community, by a request, I have imported the original tAPI guide to tModLoader, with a few changes of course.


This page looks pretty empty boss, where's the tutorial?
By the power of Google Docs, I have made it in that, though reading code will be a pain, so you might want to look at MPT.zip instead if you want to read code. The Google Doc for the guide is VERY detailed, reaching nearly 20 PAGES! Of course, you can just skip through the reading portions, but I highly recommend reading through them if you have little to no experience with modding or coding. The advanced concepts will be on this thread. If you want to learn something from my original guide or something from Vanilla Terraria, then comment on the thread and if multiple people like it, I will add it. Also, if you need any help, go ahead and post a comment and either me or experts will try and help, just remember this is for Projectiles and their Implements (though you can implement projectiles on nearly everything...)


The Google Docs Link:
Projectile Guide and Implementation

Resources:
Dust and Sound Catalogue
Sin Costan's Collection of Fun Projectiles: Once Again

 

Attachments

  • MPT.zip
    8.1 KB · Views: 3,608
Last edited:
Advanced Concepts:

For those who want to make a homing projectile without the use of an aiStyle try this. This is a modified version of my PDT Bullet in COFP. This should go inside the AI hook.
Code:
//For all of the NPC slots in Main.npc
//Note, you can replace NPC with other entities such as Projectiles and Players
public override void AI()
{
    for(int i = 0; i < 200; i++)
    {
       NPC target = Main.npc[i];
       //If the npc is hostile
       if(target.hostile)
       {
           //Get the shoot trajectory from the projectile and target
           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 live targeted npc and the projectile is less than 480 pixels
           if(distance < 480f && !target.friendly && target.active)
           {
               //Divide the factor, 3f, which is the desired velocity
               distance = 3f / distance;
   
               //Multiply the distance by a multiplier if you wish the projectile to have go faster
               shootToX *= distance * 5;
               shootToY *= distance * 5;

               //Set the velocities to the shoot values
               projectile.velocity.X = shootToX;
               projectile.velocity.Y = shootToY;
           }
       }
    }
}

I see this a lot on the forums, asking how to make things shoot at a target so, here you guys go. This is a modified version of my PDT from COFP. This should be inside the AI hook.
Code:
//Note, you can use this with an NPC to shoot at a Player also
//For every npc slot in Main.npc
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, 1, 0, 0, Main.myPlayer, 0f, 0f); //Spawning a projectile
               Main.PlaySound(2, (int)projectile.position.X, (int)projectile.position.Y, 11); //Bullet noise
               projectile.ai[0] = 0f;
           }
       }
    }
    projectile.ai[0] += 1f;
}

I just figured this out, and I felt really dumb because it was really simple... with the power of Math: Trigonometry Edition, here's how you can make a projectile orbit around something. This example involves orbiting the player.
Code:
public override void AI()
{
    //Making player variable "p" set as the projectile's owner
    Player p = Main.player[projectile.owner];

    //Factors for calculations
    double deg = (double) projectile.ai[1]; //The degrees, you can multiply projectile.ai[1] to make it orbit faster, may be choppy depending on the value
    double rad = deg * (Math.PI / 180); //Convert degrees to radians
    double dist = 64; //Distance away from the player
 
    /*Position the player based on where the player is, the Sin/Cos of the angle times the /
    /distance for the desired distance away from the player minus the projectile's width   /
    /and height divided by two so the center of the projectile is at the right place.     */
    projectile.position.X = player.Center.X - (int)(Math.Cos(rad) * dist) - projectile.width/2;
    projectile.position.Y = player.Center.Y - (int)(Math.Sin(rad) * dist) - projectile.height/2;
 
    //Increase the counter/angle in degrees by 1 point, you can change the rate here too, but the orbit may look choppy depending on the value
    projectile.ai[1] += 1f;
}
 
Last edited:
Once again I have added more stuff to the "Advanced Concepts" section, this time having a projectile orbit around a player. Have fun with this! I have also made a few changes, here are some of the changes.

Code:
Advanced Concepts:

Added
   - Having a projectile orbit around something with an example with the projectile orbiting the player.
Fixed some mistakes
   - 3f is actually the desired velocity for the homing projectile and the projectile shooting something. Fixed comments to reflect this.
   - Some of the comments were off, so I fixed some of those too.
   - Decided not to be lazy and added the AI hook
 
em how do you make a sword shoot a projectile that's already in the game?
Just have the shoot value of the item as the ID number or use TerrariaID like this...

Code:
item.shoot = 405; //Literal integer ID of Flairon Bubble
item.shoot = ProjectileID.FlaironBubble; //Must have "using Terraria.ID;" at the top
 
IT WORKS but not the way it shoud you know horesmans balde that the pumpkin things go after an enmies for some reason it gos down
down
[doublepost=1458667021,1458666918][/doublepost]no sorry it gos in one spot and stays there spinning none stop
The reason it does that is because the pumpkin head most likely uses on of it's pojectile.ai[] slots as a way to save the enemy ID to track, which for the FlamingJack projectile, uses projectile.ai[0] to save the npc ID. So you either would need to set a target for the pumpkin depending on a certain factor, for example you can do distance. You would need to use the Shoot() override to do 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) 
{
    //Target ID
    int target = 0;
   
    //Min distance
    float min = 3000;
   
    for(int i = 0; i < 200; i++)
    {
        //Get npc at slot i
        NPC n = Main.npc[i];
       
        //If not friendly and is active
        if(n.active && !n.friendly)
        {
            //Get the distance
            float dX = n.Center.X * 0.5f - player.Center.X; 
            float dY = n.position.Y - player.Center.Y; 
            float distance = (float)System.Math.Sqrt((double)(dX * dX + dY * dY));
           
            //If the distance is less than the min distance set min to that distance and target to that target ID
            if(distance < min)
            {
                min = distance;
                target = n.whoAmI;
            }
        }
    }
   
    //Spawn projectile
    Projectile.NewProjectile(position.X, position.Y, speedX, speedY, type, damage, knockBack, player.whoAmI, target, 0f);
   
    //Don't fire original projectile
    return false;
}
 
where would this go
[doublepost=1458668787,1458668772][/doublepost]the whole code
[doublepost=1458669008][/doublepost]em how could i make it so will attack anything in range expect NPCs
 
where would this go
[doublepost=1458668787,1458668772][/doublepost]the whole code
[doublepost=1458669008][/doublepost]em how could i make it so will attack anything in range expect NPCs
1. You would put that in the item cs file, usually I put that under, not in, SetDefaults().
2. Just delete the min within the if statement so that it will only check if the npc is within the min. This only works once after the projectile is fired, the AI handles the rest.
 
it didn't work so my dad helped me with it and here is the working code
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)
{
    //Target ID
    int target = 0;
   
  
    //Min distance
    float min = 3000;
       
    for(int i = 0; i < 200; i++)
    {
            //Get npc at slot i
            NPC n = Main.npc[i];
          
            //If not friendly and is active
            if(n.active && !n.friendly)
            {
                //Get the distance
                //float dX = n.Center.X * 0.5f - player.Center.X;
                float dX = n.Center.X - player.Center.X;
                //float dY = n.position.Y - player.Center.Y;
                float dY = n.Center.Y - player.Center.Y;
                float distance = (float)System.Math.Sqrt((double)(dX * dX + dY * dY));
              
                //If the distance is less than the min distance set min to that distance and target to that target ID
                if(distance < min)
                {
                    min = distance;
                    target = n.whoAmI;
                }
            }
    }
   
    //Spawn projectile
    if (target > 0)
    {
        Projectile.NewProjectile(position.X, position.Y, speedX, speedY, type, damage, knockBack, player.whoAmI, target, 0f);
    }
   
  
    //Don't fire original projectile
    return false;
}
 
it didn't work so my dad helped me with it and here is the working code
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)
{
    //Target ID
    int target = 0;
  
 
    //Min distance
    float min = 3000;
      
    for(int i = 0; i < 200; i++)
    {
            //Get npc at slot i
            NPC n = Main.npc[i];
         
            //If not friendly and is active
            if(n.active && !n.friendly)
            {
                //Get the distance
                //float dX = n.Center.X * 0.5f - player.Center.X;
                float dX = n.Center.X - player.Center.X;
                //float dY = n.position.Y - player.Center.Y;
                float dY = n.Center.Y - player.Center.Y;
                float distance = (float)System.Math.Sqrt((double)(dX * dX + dY * dY));
             
                //If the distance is less than the min distance set min to that distance and target to that target ID
                if(distance < min)
                {
                    min = distance;
                    target = n.whoAmI;
                }
            }
    }
  
    //Spawn projectile
    if (target > 0)
    {
        Projectile.NewProjectile(position.X, position.Y, speedX, speedY, type, damage, knockBack, player.whoAmI, target, 0f);
    }
  
 
    //Don't fire original projectile
    return false;
}
Oh wow, I did dX and dY the stupid way... Forgot I changed my style to instead use centers... Thanks for your feedback!
 
I need help with ore coding
Ore coding isn't my sort of thing and this place isn't really the place to ask about ore coding, as this place is only for projectiles and their implements, which ore is not an implement of projectiles. You should post these sorts of questions in the Official tModLoader Help Thread or the tModLoader page. Anyways, here's a code snippet from the ExampleMod that should help you with ore generation.

Here I have the code from the ExampleMod's ExampleWorld. I'm just going to use jopojelly's explanations to explain this code. In the ExampleMod, this was called in the "ModifyWorldGenTasks(List<GenPass> tasks, ref float totalWeigh)" hook.
Code:
//The 6E-05 is the number of patchess
for (int k = 0; k < (int)((double)(Main.maxTilesX * Main.maxTilesY) * 6E-05); k++)
{
    //The below comment are the arguments for TileRunner
    //TileRunner(int i, int j, double strength, int steps, int type, bool addTile = false, float speedX = 0f, float speedY = 0f, bool noYChange = false, bool overRide = true)
    //Adjusting the strength and step will adjust the patch size
    WorldGen.TileRunner(WorldGen.genRand.Next(0, Main.maxTilesX), WorldGen.genRand.Next((int)WorldGen.worldSurfaceLow, Main.maxTilesY), (double)WorldGen.genRand.Next(3, 6), WorldGen.genRand.Next(2, 6), mod.TileType("ExampleBlock"), false, 0f, 0f, false, true);
}

For future reference, please only ask about projectile stuff in this thread. Thank you.
 
how do you make a unfriendly projectile to friendly for example some projectile are used from enemies wich damages the player so if i use a weapons with that projectile and some how hit me (not to hard) it damages me and not the mobs
 
how do you make a unfriendly projectile to friendly for example some projectile are used from enemies wich damages the player so if i use a weapons with that projectile and some how hit me (not to hard) it damages me and not the mobs

You can just set projectile.friendly to true in the SetDefaults function of your projectile.
 
Back
Top Bottom