Sin Costan
Eye of Cthulhu
Thank you, it should be fixed now.U Screwed Up On The Cursed Flames Powder.cs In The Tut
Thank you, it should be fixed now.U Screwed Up On The Cursed Flames Powder.cs In The Tut
If you want it to last a couple of seconds before contacting an enemy like the dynamite, just use the bouncing code from the OP and then adjust the timeLeft to however long you want it to last.
Is This Better ?It really doesn't understand anything.. Sorry man.
Si desea que duran un par de segundos antes de ponerse en contacto con un enemigo al igual que la dinamita, sólo tiene que utilizar la combinación código de la OP y, a continuación, ajuste el timeLeft a todo el tiempo que desea que el pasado.
Is This Better ?
OP refers to the first post of the thread, which in this case, is where the whole tutorial is. Just go to Part 4 for the bouncing code.(╯•_•)╯︵ ┻━┻ @AppulDoge no me refiero a eso..
Mr. @Sin Costan what is "OP"?
PS: I use a translator to understand the English language.
OP refers to the first post of the thread, which in this case, is where the whole tutorial is. Just go to Part 4 for the bouncing code.
Tutorial: Basic Projectile Guide and Implementation
Brought to you Sin Costan - Please excuse my bad Comp Sci-ing and Thread Making Skills
Other Guides/Tutorials & Great Resources:
- Getting Started on Modding - Berberborscing
- [Tutorial] Custom Dust - BlueMagic123
- [Tutorial] Custom Boss - NPC AI and Server Syncing - BlueMagic123
- [Tutorial] Summoner Weapon - BlueMagic123
- [TUTORIAL] How to make a basic flail weapon type weapon - Gmod
- [Tutorial] Generics and Custom JSON Properties - MiraiMai
- [Tutorial] The many aspects of World Generation - TheGamingBoffin
- tAPI Community Resources - Development assistance for developers, by developers - Neojin
- http://dev.willhuxtable.com/ids/ - Uknown (Projectile AI Styles not Updated)
- Projectile AIs, Projectile AI Style IDs, Projectile IDs & Stats, MPT as an Attachment to the thread as txts and zips.
The first thing you might ask is - "Why do we need this?"
Well... The tutorial itself will answer that question for you...
Notes:
- There is a .zip file of an already completed tutorial and the complete list of AI Styles to reference when using this tutorial.
- Image files are with that .zip file, so use those when using this tutorial.
- For the Dust IDs and Buff IDs, refer to the Wikias or this handy Website, http://dev.willhuxtable.com/ids/, though the projectile aiStyles is not updated.
- Most of the example items found in this tutorial were made just for this tutorial. There will only be one item I have made for my mod "The Collection of Fun Projectiles".
- I suck at spriting.
- I wil be editing this post to fix some English-ing and include some other things.
- Time in Terraria is set in frames: 60 frames per Second
- Please read through the whole tutorial, if you have questions, post them on this thread.
- Credits to Grox (GRealm), DiverManSam (Thorium +), Zoodletec (Necro), Berberbirscing (His mod and Guide), and other Modders for some of their code!
1. Creating a Basic Projectile, Projectile Based Weapon, and Ammo
2. Using/Editing Vanilla Projectile Stats
3. Making your projectile make/do fancy effects
- Dust
- Spin
- Light Up
4. Different Methods to make the projectile unique
- DealtNPC()
- PostKill()
- AI()
- OnTileCollide()
5. Modifying your Projectile Based Weapon
- ConsumeAmmo()
- PreShoot()
6. Manipulating AI Styles1. You are modding with tAPI
2. You have a little bit of experience or have went through the “Getting Started with Modding” tutorial (by Berberborscing)
3. Have little to no experience with projectiles
4. You will follow this tutorial step by step.
Before we start with the creating the basic items for this tutorial, I want you to make a new mod folder within the Sources folder with the name of "MPT" (Modding Projectiles Tutorial), so your ModInfo.json should be like this:
Code:{ "displayName":"Modding Projectiles Tutorial", "internalName":"MPT", "author": "Your Name", "info":"These are my Projectiles!", "version":"1.0", "includePDB": true }
Make sure to save the textures in this tutorial and place them with their respective partners.
Let’s start by making the Projectile Based Weapon, so we’ll stick with a gun. We’ll put this inside the folder called “Items”. So we are going to make our JSON file with these parameters.
Code:{ "displayName": "Example Gun", "size": [52,18], "maxStack": 1, "value": [0,0,0,1], "rare": 1, "tooltip": ["Fires Example Rounds"], "useStyle": 5, "useAnimation": 15, "useTime": 15, "damage": 10, "knockback": 5, "useSound": 11, "ranged": true, "shoot": "MPT:ExampleProjectileA", "useAmmo": "MPT:ExampleRounds", "shootSpeed": 8, "noMelee": true, "recipes": [{ "items": { "Dirt Block": 1 }, "creates": 1 }] }
And now we are going to making two projectiles, which we’ll put inside a folder called “Projectiles”. The projectile aiStyles can be found as an attachment on the bottom of this post. Anyways, we will stick with aiStyle 0, which is just the projectile going straight forward and have it as a Musket Ball.
ExampleProjectileA.json
Code:{ "displayName": "Example Projectile A", "size": [12,12], "scale": 1, "aiStyle": 0, "timeLeft": 180, "friendly": true, "hostile": false, "tileCollide": false, "penetrate": 10, "ranged": true, "maxUpdates": 1 }
ExampleProjectileB.json
Code:{ "displayName": "Example Projectile B", "size": [12,12], "scale": 1, "aiStyle": 0, "timeLeft": 180, "friendly": true, "hostile": false, "tileCollide": false, "penetrate": 10, "ranged": true, "maxUpdates": 1 }
After these two items are done, we should start on our ammo, which will also be put within the “Items” folder. Since we have two types of projectiles, we will have two types of ammo to use. We will have the items have the same icon as their respective projectile.
ExampleRoundsA.json
Code:{ "displayName": "Example Rounds A", "size": [12,12], "scale": 1, "maxStack": 999, "value": [0,0,5,0], "rare": 2, "tooltip": ["Example Rounds A"], "ammo": "ExampleRounds", "shoot": "MPT:ExampleProjectileA", "damage": 10, "consumable": true, "recipes": [{ "items": { "Dirt Block": 1}, "creates": 111 }] }
ExampleRoundsB.json
Code:{ "displayName": "Example Rounds B", "size": [12,12], "scale": 1, "maxStack": 999, "value": [0,0,5,0], "rare": 2, "tooltip": ["Example Rounds B"], "ammo": "ExampleRounds", "shoot": "MPT:ExampleProjectileB", "damage": 10, "consumable": true, "recipes": [{ "items": { "Dirt Block": 1}, "creates": 111 }] }
Now we that we have all of these, compile it with tAPI Builder and test it out. This wraps up the first lesson of creating your own projectile for your future mods!
*note: Some of these items you don't need or can remove straight out in the Weapon
- "useAmmo": you can just not include it, it won't use the ammo and it's projectile
- "useAnimation" and "useTime": If "useAnimation" is greater than "useTime", it will fire multiple times per use.
- "mana": alternative to ammo if you want to make a magic weapon; it will consume mana rather than ammo
- "shoot": isn't solely restricted on projectile based weapons like magic or ranged, you can use it on a melee weapon also like Terra Blade
- "autoReuse": Allows the weapon to keep firing while holding the mouse button; If not used, will not keep firing while holding mouse button.
- "reuseDelay": Time in between uses; Like everthing else, time is in Frames.
- "noMelee": Makes item not hit anything while animated;
*note: Some of these items you don't need or can remove straight out in the Projectile
- "scale": if you don't have this, it stays at the scale of "1".
- "penetrate": if you don't have this, it only hits 1 enemy; If set to -1, infinite penetration of enemies
- "timeLeft": if you don't have this, projectile will last forever or until the game decides to despawn it;
- “maxUpdates”: not really necessary; Heat Ray has this set to 100 to keep laser effect, does not work on .png images, only with certain aiStyles.
- “tileCollide”: if you don’t have this, sets to default setting, hitting tiles
*note: ammos with the same “ammo” name are used by the weapon just the same, as seen if you have compiled and tried this out yourself.Welcome to part 2 of the Projectiles Tutorial, Using/Editing Vanilla Projectiles.
You are not editing the Vanilla Projectile's property in game, rather, making a new projectile with the Vanilla Projectile's AI and Texture. Let's say you want to make Example Projectile A into a Demon Scythe rather than a bullet.
You would need to:
- Set the AI Style to the AI Style of a Demon Scythe
- Set the projectile's type to the Demon Scythe Projectile (This automatically makes the texture default to the Demon Scythe, so you won't need to make a new texture)
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace MPT.Projectiles { public class ExampleProjectileA : ModProjectile { public override void AI() { projectile.type = 45; //45 is the Demon Scythe's ID; } } }
Remember all the properties of Example Projectile A? Well normally, Demon Scythe would only hit 5 targets and not go through walls, but instead it will hit up to 10 and go through walls!
If you want to use a vanilla projectile for your projectile, you have to match the type to it's aiStyle (Demon Scythe ID = 45 - Demon Scythe aiStyle = 18, Sharknado Minion ID = 407 - Sharknado Minion aiStyle = 62, etc. [You can find the projectile IDs as an attachment])Nice, you made it to part 3 of this tutorial! Now here are functions that are important for this.
Code:int DustID = Dust.NewDust(Vector2 Position, int Width, int Height, int Type, float SpeedX = 0f, float SpeedY = 0f, int Alpha = 0, Color newColor = default(Color), float Scale = 1f) //Spawns Dust Main.dust[DustID].noGravity = true; //Causes dust to not be affected by gravity when spawned projectile.light = float power; //Can go from 0 to a pretty high number projectile.alpha = int transparency; //Transparency (255 makes it totally invisible) projectile.rotation += (float)projectile.direction * float rotationSpeed; //Direction determines whether it goes clockwise or counterclockwise (1 = clockwise, -1 = counterclockwise); Max Rotation speed you can see well is 10.5f; Rotation does not work on some aiStyles
We will now start to make a .cs file for our other projectile, Example Projectile B in which we shall call the file "ExampleProjectileB.cs". We copy over ExampleProjectileA's .cs properties onto ExampleProjectileB, in which instead of calling the class ExampleProjectileA, we call it ExampleProjectileB. After that, you might want to add some of these effects into your AI() method. Let's say we want it to be a bit transparent, some spin, and produce some dust! We would have the .cs file sort of like this.
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace MPT.Projectiles { public class ExampleProjectileB : ModProjectile { public override void AI() { projectile.light = 0.9f; //Glow just enough for it to cover the projectile projectile.alpha = 128; //Makes it semi-transparent projectile.rotation += (float)projectile.direction * 0.8f; - Makes the projectile spin int DustID = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width + 4, projectile.height + 4, 36, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 120, default(Color), 0.75f); //Create Dust Main.dust[DustID].noGravity = true; //Makes the Dust not fall into the ground } } }
Welcome to Part 4 of the Projectile Tutorial. Here, we will make your projectile do more than just look pretty!
Here are the Methods and variables we will be using in this tutorial.
Code:public override void DealtNPC(NPC n, int hitDir, int dmgDealt, float knockback, bool crit) //Calls when you hit an enemy public override void AI() //The projectile's AI/ what the projectile does public override bool OnTileCollide(ref Vector2 velocityChange) //When the projectile hits a tile public override void PostKill() //Calls after the projectile "dies" / run out of time projectile.velocity.X = float velocity // Velocity of the projectile in the X axis projectile.position.X = int position // Position of the projectile in the X axis projectile.velocity.Y = float velocity //Velocity of the projectile in the Y axis projectile.position.Y = int position // Position of the projectile in the Y axis projectile.timeLeft = int timeInFrames //The amount of time the projectile has to live int rand = Main.rand.Next(int rightEnd); //Makes a random integer from 0 to the right end number; Helps make random chances n.AddBuff(int Type, int Time) //Adds buff/debuff to enemy and its countdown in terms of frames Player owner = Main.player[projectile.owner] //Makes a Player variable of yourself Projectile.NewProjectile(position.X, position.Y, velocity.X, velocity.Y, int type or string type, projectile.damage, projectile.knockBack, Main.myPlayer); //Creates a new Projectile MathHelper.Lerp(float leftEnd, float rightEnd, (float)Main.rand.NextDouble()) //Creates a random float number; Useful for random velocities Iten.NewItem(int X, int Y, int Width, int Height, int Type or string Type, int Stack = 1, bool noBroadcast = false, int pfix = 0, bool noGrabDelay = false); //Creates an item at a certain position
Now let's go make ExampleProjectileB intense, we'll give it these properties.
- Spawn Example Projectile A every quarter of a second
- Have it impale onto enemies (though there won't be a chance for it to stick due to excessive amounts of projectiles)
- Cause the On Fire! debuff
- Bounce off walls and half the velocity
- Get life on hit (without heal effect)
- Spawn an Item after the projectile is done
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace MPT.Projectiles { public class ExampleProjectileB : ModProjectile { //The AI() public override void AI() { Player owner = Main.player[projectile.owner]; //Makes a player variable of owner set as the player using the projectile projectile.light = 0.9f; projectile.alpha = 128; projectile.rotation += (float)projectile.direction * 0.8f; int DustID = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width + 4, projectile.height + 4, 36, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 120, default(Color), 0.75f); Main.dust[DustID].noGravity = true; if(projectile.timeLeft % 15 == 0) //If the remainder of the timeLeft divided 15 is 0, then make the projectile; Every 15 seconds spawn a projectile basically { Projectile.NewProjectile(projectile.position.X, projectile.position.Y, MathHelper.Lerp(-1f, 1f, (float)Main.rand.NextDouble()), MathHelper.Lerp(-1f, 1f, (float)Main.rand.NextDouble()), "MPT:ExampleProjectileA", 5 * (int)owner.rangedDamage, projectile.knockBack, Main.myPlayer); //owner.rangedDamage is basically the damage multiplier for ranged weapons } } //When you hit an NPC public override void DealtNPC(NPC n, int hitDir, int dmgDealt, float knockback, bool crit) { Player owner = Main.player[projectile.owner]; //Start of impalement code projectile.position.X = n.position.X; projectile.position.Y = n.position.Y; projectile.velocity.X = n.velocity.X; projectile.velocity.Y = n.velocity.Y; //End of impalement code int rand = Main.rand.Next(2); if(rand == 0) { n.AddBuff(24, 180); //On Fire! debuff for 3 seconds } else if (rand == 1) { owner.statLife += 5; //Gives 5 Health } } public override bool OnTileCollide(ref Vector2 velocityChange) //Thanks to Diverman Sam for this wonderful code for bouncing off walls! { if (projectile.velocity.X != velocityChange.X) { projectile.velocity.X = -velocityChange.X/2; } if (projectile.velocity.Y != velocityChange.Y) { projectile.velocity.Y = -velocityChange.Y/2; } return false; } //After the projectile is gone public override void PostKill() { int rand = Main.rand.Next(5); Projectile.NewProjectile(projectile.position.X, projectile.position.Y, 0, 0, 296, (int) (projectile.damage * 1.5), projectile.knockBack, Main.myPlayer); // 296 is the explosion from the Inferno Fork if(rand == 0) { Item.NewItem((int)projectile.position.X, (int)projectile.position.Y, projectile.width, projectile.height, "MPT:ExampleRoundsB", 1, false, 0, false); } } } }
Welcome to Part 5 of this tutorial! You're nearly done! Here we will be modifying your projectile based weapon.
First off, before we start with modifying the projectile based weapon.
- Change the "useAnimation" to 39 and "useTime" to 10 in "ExampleGun.json" (this makes it fire 3 times per one click)
- Add ourselves a .cs file for our ExampleGun and call it "ExampleGun.cs".
Here are the things we will be using in this part of the tutorial.
Code:public override bool ConsumeAmmo(Player p) //Calls when you fire public override bool PreShoot(Player player, Vector2 position, Vector2 velocity, int type, int damage, float knockback) //Calls before weapon shoots projectile int rand = Main.rand.Next(int rightEnd); //Random Chance! p.itemAnimation //The time left in frames p.inventory[p.selectedItem].useAnimation //total time the Animation takes for(int x = 0; x < int shotAmount; x++) //Amount of shots per one use
Now let's give it some cool properties. We'll have it:
- Each fired round in the burst shoots 3 instead of 1 (like a shotgun)
- Consume one thing of Ammo
- Have that one thing of Ammo have a chance not to be used
Code:using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using TAPI; using Terraria; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace MPT.Items { public class ExampleGun : ModItem { public override bool ConsumeAmmo(Player p) { int rand = Main.rand.Next(9); //Random chance if(p.itemAnimation < p.inventory[p.selectedItem].useAnimation - 30) //Makes it consume only one ammo { if(rand == 0 || rand == 1) //Chance for you not to use the single used ammo { return false; } else { return true; } } else { return false; } } public override bool PreShoot(Player player, Vector2 position, Vector2 velocity, int type, int damage, float knockback) { /*Code is made by berberborscing*/ int spread = 30; //The angle of random spread. float spreadMult = 0.1f; //Multiplier for bullet spread, set it higher and it will make for some outrageous spread. for (int i = 0; i < 3; i++) //Amount of shots { float vX = velocity.X+(float)Main.rand.Next(-spread,spread+1) * spreadMult; float vY = velocity.Y+(float)Main.rand.Next(-spread,spread+1) * spreadMult; Projectile.NewProjectile(position.X, position.Y, vX, vY, type, damage, knockback, Main.myPlayer); } return false; } } }
Throughout the tutorial, you have actually been manipulating AI Styles, so there isn't much to cover here. However there are some general notes that you should know.
*Notes:
- Instead of using the projectile's timeLeft to handle timed occurences, you can use other ones which include...
Some minor problems with using these is that some projectile AI styles may already use these, so the AI Style could be messed up if you use these to be your counters. Here's an example of how to use them.Code:projectile.ai[0] projectile.ai[1] projectile.localAI[0]
- As I said before inserting projectile.rotation to the overrided AI() may not work depending on the AIStyleCode://If the ai ticker (projectile.ai[0]) reaches 10, it does something and resets to 0. projectile.ai[0]++ if(projectile.ai[0] > 10) { //some code projectile.ai[0] = 0; }
- Custom AIStyle is usually -1, but you can use 0 since it doesn't do anything.
- For those who want to mess with the vanilla code, I included it under the txt file of "Projectile AI List".
If you're done with (or copied through) with the tutorial, this is what your end product should look like (It looks like an absolute mess, but it literally is a compilation of everything that the tutorial covers...)
Tutorial: Projectile Concepts
Notes:
- Make sure the spear's tip for the projectile is facing the top left corner.
- Make sure the mini spear is like an arrow texture
- "size" adjusts the hitbox, no need to adjust it so it is the size of the texture file
- The projectiles spawned by the mini spear don't need any velocity because it is just going down
- North Pole like weapons can do different things, for example, check out Gae Bolg in my mod page.
Ancient Spear Projectile JSON
Code:{ "displayName": "Ancient Spear", "size": [20, 20], "scale": 1.1, "aiStyle": 19, "friendly": true, "hostile": false, "tileCollide": false, "ignoreWater": true, "penetrate": -1, "ownerHitCheck": true, "hide": true, "melee": true }
Ancient Spear Projectile CS
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace COFP.Projectiles { public class AncientSpear : ModProjectile { public override void AI() { projectile.light = 0.9f; int DustID = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width, projectile.height, 24, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 100, default(Color), 0.5f); Main.player[projectile.owner].direction = projectile.direction; //Make's the owner face the direction the spear Main.player[projectile.owner].heldProj = projectile.whoAmI; Main.player[projectile.owner].itemTime = Main.player[projectile.owner].itemAnimation; projectile.position.X = Main.player[projectile.owner].position.X + (float)(Main.player[projectile.owner].width / 2) - (float)(projectile.width / 2); projectile.position.Y = Main.player[projectile.owner].position.Y + (float)(Main.player[projectile.owner].height / 2) - (float)(projectile.height / 2); projectile.position += projectile.velocity * projectile.ai[0]; if (projectile.ai[0] == 0f) { projectile.ai[0] = 3f; projectile.netUpdate = true; } if (Main.player[projectile.owner].itemAnimation < Main.player[projectile.owner].itemAnimationMax / 3) { projectile.ai[0] -= 1.1f; //How far back it goes if (projectile.localAI[0] == 0f && Main.myPlayer == projectile.owner) { projectile.localAI[0] = 1f; if (Collision.CanHit(Main.player[projectile.owner].position, Main.player[projectile.owner].width, Main.player[projectile.owner].height, new Vector2(projectile.center().X + projectile.velocity.X * projectile.ai[0], projectile.center().Y + projectile.velocity.Y * projectile.ai[0]), projectile.width, projectile.height)) //The if statement to make a projectile after the spear point reaaches it's max distance; you can remove the if statements and the things it contains to make it a normal spear { Projectile.NewProjectile(projectile.center().X + projectile.velocity.X , projectile.center().Y + projectile.velocity.Y , projectile.velocity.X * 1.5f, projectile.velocity.Y * 1.5f, "COFP:MiniAncientSpear", projectile.damage , projectile.knockBack * 0.85f, projectile.owner, 0f, 0f); //Spawning the projectile } } } else { projectile.ai[0] += 0.75f; //How Far It Goes } if (Main.player[projectile.owner].itemAnimation == 0) { projectile.Kill(); //Kills projectile after it is done } projectile.rotation = (float)Math.Atan2((double)projectile.velocity.Y, (double)projectile.velocity.X) + 2.355f; //Rotates the spear based on where you shoot if (projectile.spriteDirection == -1) { projectile.rotation -= 1.57f; } } } }
Ancient Spear's Mini Spear JSON
Code:{ "displayName": "Mini Ancient Spear", "size": [10, 10], "scale": 0.75, "aiStyle": 1, "friendly": true, "hostile": false, "tileCollide": true, "ignoreWater": true, "penetrate": -1, "melee": true }
Ancient Spear's Mini Spear CS
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace COFP.Projectiles { public class MiniAncientSpear : ModProjectile { public override void AI() { int DustID = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width, projectile.height, 24, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 100, default(Color), 0.4f); projectile.light = 0.9f; projectile.rotation = (float)System.Math.Atan2((double)projectile.velocity.Y, (double)projectile.velocity.X) + 1.57f; projectile.ai[0] += 1f; if (projectile.ai[0] > 30f) { projectile.ai[0] = 30f; projectile.velocity.Y = projectile.velocity.Y + 0.25f; if (projectile.velocity.Y > 16f) { projectile.velocity.Y = 16f; } projectile.velocity.X = projectile.velocity.X * 0.995f; } projectile.rotation = (float)Math.Atan2((double)projectile.velocity.Y, (double)projectile.velocity.X) + 1.57f; projectile.alpha -= 50; if (projectile.alpha < 0) { projectile.alpha = 0; } if (projectile.owner == Main.myPlayer) { projectile.localAI[0] += 1f; if (projectile.localAI[0] >= 4f) { projectile.localAI[0] = 0f; int num668 = 0; for (int num669 = 0; num669 < 1000; num669++) { if (Main.projectile[num669].active && Main.projectile[num669].owner == projectile.owner && Main.projectile[num669].type == 344) { num668++; } } float num670 = (float)projectile.damage * 0.8f; if (num668 > 100) { float num671 = (float)(num668 - 100); num671 = 1f - num671 / 100f; num670 *= num671; } if (num668 > 100) { projectile.localAI[0] -= 1f; } if (num668 > 120) { projectile.localAI[0] -= 1f; } if (num668 > 140) { projectile.localAI[0] -= 1f; } if (num668 > 150) { projectile.localAI[0] -= 1f; } if (num668 > 160) { projectile.localAI[0] -= 1f; } if (num668 > 165) { projectile.localAI[0] -= 1f; } if (num668 > 170) { projectile.localAI[0] -= 2f; } if (num668 > 175) { projectile.localAI[0] -= 3f; } if (num668 > 180) { projectile.localAI[0] -= 4f; } if (num668 > 185) { projectile.localAI[0] -= 5f; } if (num668 > 190) { projectile.localAI[0] -= 6f; } if (num668 > 195) { projectile.localAI[0] -= 7f; } if (num670 > (float)projectile.damage * 0.1f) //You really only need to worry about changing this { Projectile.NewProjectile(projectile.center().X, projectile.center().Y, 0f, 0f, "COFP:Rust", projectile.damage/4, projectile.knockBack * 0.55f, projectile.owner, 0f, (float)Main.rand.Next(3)); //This just spawns a projectile that falls down onto the ground } } } } } }
Rust JSON
Code:{ "displayName": "Rust", "size": [8, 8], "aiStyle": 1, "friendly": true, "hostile": false, "tileCollide": true, "ignoreWater": true, "melee": true }
Rust CS
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace COFP.Projectiles { public class Rust : ModProjectile { public override void AI() { int DustID = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width, projectile.height, 24, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 100, default(Color), 0.25f); projectile.light = 0.9f; } } }
Welcome to the guide on making Starfury type weapons! Here we will be using the following properties for our weapon.
Code:"holdoutOffset": [x,y], //Where you want the item to be in terms of the positions of x and y when used
Code:"code": Folder.CSFileName //.cs properties of that filename is used for the item/projectile, rather than making a .cs file with the same name as the item/projectile
Code:new Vector2(velocity.X, velocity.Y) //Allows you to make a new trajectory Vector Gore.NewGore(Vector2 Position, Vector2 Velocity, string name, float Scale = 1f) //Though usually used for NPCs, we'll be making gore for our projectile like sharks from the sharknado PlaySound(int type, int x = -1, int y = -1, int Style = 1) //Plays a sound; Type is the list ID #; x is the position in terms of x; y is the position in terms of y; Style is the ID # in the list; can be found in http://dev.willhuxtable.com/ids/.
We will be making a weapon of epic proportion... the Staff of Guide Rain! So before we start on the more complicated stuff, we'll start with our .json files. We'll need to make a file called Gores for our gore of the Guide. All the images will be included within the MPT.zip.
Code:{ "displayName": "Staff of Guide Rain", "size": [42,40], "maxStack": 1, "value": [0,0,0,1], "rare": 7, "holdoutOffset": [-8,0], "tooltip": ["It's raining Guides!", "Hallelujah!"], "useStyle": 5, "useAnimation": 30, "useTime": 5, "useSound": 32, "noMelee": true, "magic": true, "damage": 30, "shoot": "MPT:GuideProj1", "shootSpeed": 6, "mana": 1, "autoReuse": true, "recipes": [{ "items": { "Dirt Block": 1}, "creates": 1 }] }
Code:{ "code": "Projectiles.GuideProjectile", //Brings it to our upcoming .cs file "displayName": "Guide", "size": [27,46], "aiStyle": 0, "friendly": true, "hostile": false, "tileCollide": true, "magic": true }
Now that we got our .json files done, we start with our item's .cs file! Here we will make it rain Guides from the sky!
Code:using System; using Microsoft.Xna.Framework; using TAPI; using Terraria; namespace MPT.Items { public class SOGR: ModItem { public Vector2 usePos = default(Vector2); public override bool PreShoot(Player player, Vector2 position, Vector2 velocity, int projType, int damage, float knockback) { /*Grox's Code*/ usePos = Main.mouseWorld - player.position; UseStyle(player); position.X = Main.mouseWorld.X; //Makes the position equal to your mouse position.X += MathHelper.Lerp(-120f, 120f, (float)Main.rand.NextDouble()); //Allows you have the projectile spawn at random in the x direction position.Y -= 500; //Make the projectile spawn 500 pixels above you (About 32 tiles) velocity.Y = (float)(item.shootSpeed); //Makes it fall down velocity.X = 0; //Makes it not stray from direct path to ground /*End of Grox's Code*/ /*Here we get to use different Guide poses!*/ int rand = Main.rand.Next(6); if(rand == 0) Projectile.NewProjectile(position.X, position.Y, velocity.X, velocity.Y, "MPT:GuideProj1", damage, knockback, player.whoAmI); else if (rand == 1) Projectile.NewProjectile(position.X, position.Y, velocity.X, velocity.Y, "MPT:GuideProj2", damage, knockback, player.whoAmI); else if (rand == 2) Projectile.NewProjectile(position.X, position.Y, velocity.X, velocity.Y, "MPT:GuideProj3", damage, knockback, player.whoAmI); else if (rand == 3) Projectile.NewProjectile(position.X, position.Y, velocity.X, velocity.Y, "MPT:GuideProj4", damage, knockback, player.whoAmI); else if (rand == 4) Projectile.NewProjectile(position.X, position.Y, velocity.X, velocity.Y, "MPT:GuideProj5", damage, knockback, player.whoAmI); else Projectile.NewProjectile(position.X, position.Y, velocity.X, velocity.Y, "MPT:GuideProj6", damage, knockback, player.whoAmI); return false; } } }
Now after that, we make our .cs file for all our Guide projectiles!
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace MPT.Projectiles { public class GuideProjectile : ModProjectile { public override void AI() { projectile.light = 0.9f; projectile.rotation += (float)projectile.direction * 0.1f; int DustID = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width + 4, projectile.height + 4, 5, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 120, default(Color), 0.75f); Main.dust[DustID].noGravity = true; } public override void PostKill() { int rand = Main.rand.Next(4); /*GoreDef.gores["ModInternalName:GoreName"] basically just calls for the gore.*/ if (rand == 0) Gore.NewGore(projectile.position, new Vector2(MathHelper.Lerp(-4f, 4f, (float)Main.rand.NextDouble()), -2), GoreDef.gores["MPT:GuideGore1"], 1f); else if (rand == 1) Gore.NewGore(projectile.position, new Vector2(MathHelper.Lerp(-4f, 4f, (float)Main.rand.NextDouble()), -2), GoreDef.gores["MPT:GuideGore2"], 1f); else if (rand == 2) Gore.NewGore(projectile.position, new Vector2(MathHelper.Lerp(-4f, 4f, (float)Main.rand.NextDouble()), -2), GoreDef.gores["MPT:GuideGore2"], 1f); else if (rand == 3) Gore.NewGore(projectile.position, new Vector2(MathHelper.Lerp(-4f, 4f, (float)Main.rand.NextDouble()), -2), GoreDef.gores["MPT:GuideGore3"], 1f); else Gore.NewGore(projectile.position, new Vector2(MathHelper.Lerp(-4f, 4f, (float)Main.rand.NextDouble()), -2), GoreDef.gores["MPT:GuideGore3"], 1f); Main.PlaySound(4, (int)projectile.position.X, (int)projectile.position.Y, 1); //This is the splatter/general npc death sound effect } } }
After doing all this, you should have this should be what it looks like after you're done.
Here we are with Rain Cloud type weapons, though not as accurate as the original Rain Cloud weapons, it still works. There are no new properties we are going to play with.
So the weapon we'll be making is called the Evil Cloud Staff, which will produce a cloud that rains droplets of cursed flame and ichor! Let's get started on the .json files for our weapon.
Code:{ "displayName": "Evil Cloud Staff", "size": [42,40], "maxStack": 1, "value": [0,0,0,1], "rare": 7, "holdoutOffset": [-8,0], "tooltip": ["Summons a cloud from the lands of evil.", "Its rains knows no allies."], "useStyle": 5, "useAnimation": 30, "useTime": 30, "useSound": 21, "noMelee": true, "magic": true, "damage": 50, "shoot": "MPT:EvilCloud", "shootSpeed": 10, "mana": 1, "recipes": [{ "items": { "Dirt Block": 1}, "creates": 1 }] }
Code:{ "displayName": "Evil Cloud", "size": [54,34], "aiStyle": 0, "timeLeft": 30, "friendly": true, "hostile": false, "tileCollide": true, "penetrate": 10, "ranged": true, "maxUpdates": 1 }
Code:{ "displayName": "Evil Cloud", "size": [54,34], "aiStyle": 0, "timeLeft": 600, "friendly": true, "hostile": false, "tileCollide": true, "ranged": true, "maxUpdates": 1 }
Code:{ "displayName": "Evil Cloud", "size": [4,40], "aiStyle": 1, "friendly": true, "hostile": true, "tileCollide": true, "magic": true }
Now that we're done with that, let's get on with making our .cs files for our cloud to make it rain some death.
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace MPT.Projectiles { public class EvilCloud : ModProjectile { public override void AI() { projectile.damage = 0; //This makes it so that the cloud itself won't do damage and just go through enemies projectile.light = 0.9f; projectile.alpha = 128; projectile.rotation += (float)projectile.direction * 0.1f; int DustID1 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width + 4, projectile.height + 4, 75, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 120, default(Color), 0.75f); int DustID2 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width + 4, projectile.height + 4, 64, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 120, default(Color), 0.75f); Main.dust[DustID1].noGravity = true; Main.dust[DustID2].noGravity = true; } public override void PostKill() { Projectile.NewProjectile(projectile.Center.X, projectile.Center.Y, 0, 0, "MPT:EvilRainCloud", 0, projectile.knockBack, Main.myPlayer); } } }
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace MPT.Projectiles { public class EvilRainCloud : ModProjectile { public override void AI() { projectile.damage = 0; projectile.light = 0.9f; projectile.alpha = 128; int DustID1 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width + 4, projectile.height + 4, 75, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 120, default(Color), 0.75f); int DustID2 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width + 4, projectile.height + 4, 64, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 120, default(Color), 0.75f); Main.dust[DustID1].noGravity = true; Main.dust[DustID2].noGravity = true; if(projectile.timeLeft % 5 == 0) { int rand = Main.rand.Next(2); /*Apply the actual damage to the projectile by putting within the NewProjectile function, int WeaponDamage * (int) Main.player[projectile.owner].damageMultiplier (rangedDamage and such is a float so we need to convert it to an int) */ if(rand == 0) { Projectile.NewProjectile(projectile.Center.X + MathHelper.Lerp(-16f, 16f, (float)Main.rand.NextDouble()), projectile.Center.Y + 16, 0, 8, "MPT:IchorRain", 50 * (int) Main.player[projectile.owner].magicDamage, projectile.knockBack, Main.myPlayer); } else { Projectile.NewProjectile(projectile.Center.X + MathHelper.Lerp(-16f, 16f, (float)Main.rand.NextDouble()), projectile.Center.Y + 16, 0, 8, "MPT:CursedRain", 50 * (int) Main.player[projectile.owner].magicDamage, projectile.knockBack, Main.myPlayer); } } } public override void PostKill() { Main.PlaySound(2, (int)projectile.position.X, (int)projectile.position.Y, 4); //Heart Crystal use Effect } } }
IchorRain.cs
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace MPT.Projectiles { public class IchorRain : ModProjectile { public override void AI() { projectile.alpha = 128; projectile.light = 0.9f; int DustID = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width + 4, projectile.height + 4, 64, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 120, default(Color), 0.75f); Main.dust[DustID].noGravity = true; } public override void DealtNPC(NPC n, int hitDir, int dmgDealt, float knockback, bool crit) { n.AddBuff(69, 300); } public override void DealtPlayer(Player p, int hitDir, int dmgDealt, bool crit) { p.AddBuff(69, 300, false); } public override void PostKill() { Main.PlaySound(19, (int)projectile.position.X, (int)projectile.position.Y, 0); //Water splashing sound effect } } }
CursedRain.cs
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace MPT.Projectiles { public class CursedRain : ModProjectile { public override void AI() { projectile.alpha = 128; projectile.light = 0.9f; int DustID = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width + 4, projectile.height + 4, 64, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 120, default(Color), 0.75f); Main.dust[DustID].noGravity = true; } public override void DealtNPC(NPC n, int hitDir, int dmgDealt, float knockback, bool crit) { n.AddBuff(39, 300); } public override void DealtPlayer(Player p, int hitDir, int dmgDealt, bool crit) { p.AddBuff(39, 300, false); } public override void PostKill() { Main.PlaySound(19, (int)projectile.position.X, (int)projectile.position.Y, 0); } } }
After all this, it should look something like this...
Welcome to Barrier Type Weapons section. Here I will cover how to make a "Barrier" that covers the player. The item we are creating is called "Bubble Shield" in which we'll have a buff for it called "Barrier On". Here are some new properties we'll be using.
Code:Player.HasBuff(string BuffName or int Buff) //returns an integer of either 1 or -1; 1 = Has Buff; -1 = Does not have Buff.
So before we start with the complicated stuff, we should make our .json files. We're going to be making 3 json files, "BarrierOn.json", and 2 "BubbleShield.json"s (one for item and one for projectile).
Code:{ "displayName": "Bubble Shield", "size": [52,18], "maxStack": 1, "value": [0,0,0,1], "rare": 1, "tooltip": ["Bubble of protection!"], "useStyle": 1, "useAnimation": 30, "useTime": 30, "damage": 30, "knockback": 10, "useSound": 11, "ranged": true, "buff": "MPT:BarrierOn", //Produce the BarrierOn Buff! "buffTime": 18000, //5 Minutes "shoot": "MPT:BubbleShield", "shootSpeed": 0, //The speed doesn't really matter "recipes": [{ "items": { "Dirt Block": 1 }, "creates": 1 }] }
Code:{ "displayName": "Bubble Shield", "size": [64,64], "scale": 1, "aiStyle": 0, //I usually use this for custom AI, but people usually set it to -1 for custom AI "timeLeft": 18000, "friendly": true, "hostile": false, "tileCollide": false, "penetrate": -1, "melee": true }
Code:{ "displayName": "Bubble Shield", "tip": "You have a shield!", "noTimer": true //Makes it not display a timer }
Now that we have that done, we are going to be making our CS files for our three objects, the Bubble Shield item and projectile and the Barrier On buff.
Code:using System; using System.Collections.Generic; using System.Text; using System.Diagnostics; using TAPI; using Terraria; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace MPT.Items { public class BubbleShield : ModItem { //Don't think this is necessary, however this is a just in case measure public override bool PreShoot(Player player, Vector2 position, Vector2 velocity, int type, int damage, float knockback) { player.AddBuff("MPT:BarrierOn", 18000, false); Projectile.NewProjectile(position.X, position.Y, velocity.X, velocity.Y, type, damage, knockback, Main.myPlayer); return false; } } }
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace MPT.Projectiles { public class BubbleShield : ModProjectile { public override void AI() { Player owner = Main.player[projectile.owner]; if(owner.HasBuff("MPT:BarrierOn") == -1) //If the player doesn't have the buff, make the projectile despawn { projectile.Kill(); } projectile.light = 0.9f; projectile.alpha = 128; projectile.position.X = owner.Center.X - 30; //Makes it position itself at the center of the player in terms of X, need to adjust this depending on how large your projectile is. projectile.position.Y = owner.Center.Y - 24; //Makes it position itself at the center of the player in terms of Y, need to adjust this depending on how large your projectile is. } } }
Code:using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace MPT.Buffs { public class BarrierOn : TAPI.ModBuff { //Makes the buff do nothing } }
For Vanilla Dust Heat Rays.
Code:{ "displayName": "Test Ray", "size": [32, 32], "aiStyle": 0, //I usually just use this for custom AI since it doesn't do anything special "friendly": true, "hostile": false, "tileCollide": true, "ignoreWater": true, "magic": true, "penetrate": -1, "maxUpdates": 100 }
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace COFP.Projectiles { public class TestRay : ModProjectile { public override void AI() { projectile.localAI[0] += 1f; //The timer if (projectile.localAI[0] > 3f) //The amount of ticks it takes for it to load { for (int num562 = 0; num562 < 10; num562++) //Adjust the thickness of the ray; too high a number may cause lag { projectile.alpha = 255; //Dust.newDust(Vector2 position, Size.X, Size.Y, int Dust ID, Velocity.X, Velocity.Y, int alpha (transparency), Color, float scale) //Size determines where the dust will spawn (at random of course) //Dust ID's can be found in this handy page http://tconfig.wikia.com/wiki/List_of_Dusts int num563 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y - projectile.height/4), projectile.width, projectile.height, 60, 0f, 0f, 0, default(Color), 0.75f); //Vector2(projectile.position.X, projectile.position.Y - projectile.height/4) - (Vector position) makes sure it shoots from the center of where the weapon is pointing //projectile.width - Sets it to the projectile's size of X in terms of pixels (16 pixels = 1 tile block) //projectile.height - Sets it to the projectile's size of Y in terms of pixels //Dust ID - I set it to a red color, but if you want a black ray, use 54 //both of the velocity's set to zero so it won't move around // alpha - set to zero so color is vibrant for the dust // default(Color) - set to its default color (does not change color) // scale - set to 0.75 so the dust isn't humongous for the laser Main.dust[num563].scale = (float)Main.rand.Next(70, 110) * 0.013f; /*Adjust left and right values to sizes you would like to see for your dust in Main.rand.Next(int LeftEnd, int RightEnd) Main.dust[num563].velocity *= 0.2f; //sets the velocity of the dust to not move that much (not sure if you would need this or not) Main.dust[num563].noGravity = true; //Makes sure it doesn't fall on the ground //You can of course add more dusts to the code, but as I said about the for loop determining thickness, it may cause lag. } } } } }
I'm not gonna really bother with the item part of this, though if you want to see it, just go into MPT.zip.
First off, you need your projectile json file.
Code:{ "displayName": "Flame Blast", "size": [8, 42], "scale": 1, "aiStyle": 0, //Gonna use this for a custom AI (since aiStyle 0 does nothing) "timeLeft": 180, "friendly": true, "hostile": false, "tileCollide": true, "ignoreWater": false, "frameCount": 3 //How many frames are there in the image, in this case it is 3 images }
Now we need our .cs file, FlameBlast.cs.
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace MPT.Projectiles { public class FlameBlast : ModProjectile { public override void AI() { projectile.light = 5f; //Makes the projectile light projectile.rotation = (float)Math.Atan2((double)projectile.velocity.Y, (double)projectile.velocity.X) + 1.57f; //Makes sure the projectile rotates so it is pointing at where you are shooting. if(projectile.frameCounter < 20) //If the frame's counter is less than 20. { projectile.frame = 2; //Set it to the 3rd frame, since the frames go from 0 to 2. if(projectile.damage <= 100) //Increases the projectile damage if it is less than or equal to 100 projectile.damage += 1; else projectile.damage = 100; projectile.velocity.Y *= 1.01f; //increases the velocity by 1.01 times. projectile.velocity.X *= 1.01f; } else if(projectile.frameCounter >= 20 && projectile.frameCounter < 50) //If the projectile's frame counter greater than or equal to 20 and less than 50 { projectile.frame = 1; //Set the frame to the second frame if(projectile.damage <= 100) //Increase the damage again projectile.damage += 2; else projectile.damage = 100; projectile.velocity.Y *= 1.02f; //Increase velocity even further projectile.velocity.X *= 1.02f; int DustID1 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width/2 + 4, projectile.height/2 + 4, 64, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 120, default(Color), 0.75f); //Add Dust to it too Main.dust[DustID1].noGravity = true; } else //If the frame counter is something other than the two statements { projectile.frame = 0; //Set the frame to the first frame if(projectile.damage <= 100) //Increase damage even further projectile.damage += 3; else projectile.damage = 100; projectile.velocity.Y *= 1.03f; //Increase speed even further projectile.velocity.X *= 1.03f; int DustID2 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width + 4, projectile.height + 4, 64, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 120, default(Color), 0.75f); //Add dust Main.dust[DustID2].noGravity = true; } projectile.frameCounter++; //A seperate counter used for animation. } } }
So to top it all off, all you need to know from this example is that you need one property in the json file and about 2 properties in your cs file.
Code:In the .json file - "frameCount": int frameNumber, //How many frames are there in your texture in the .cs file - frame = int frameNumber; //Select the certain frame to show, goes from 0 to the frameCount minus 1 (0, frameCount - 1) - int frameCounter; //The counter to help you animate. (like a stop watch)
And here is one last example, only with the AI() hook though. Let's pretend I already did all the JSON file, with a frameCount of 4 this time and have it animate infinitely.
Code:public override void AI() [ projectile.frameCounter++; //Make the counter go up, just remember that each time it goes through the AI hook, it is 1/60 of a second, so you are counting by 1/60 of a second. if(projectile.frameCounter < 10) //If the counter is less than 10 { projectile.frame = 0; //Set it to the first frame } else if (projectile.frameCounter >= 10 && projectile.frameCounter < 20) //If the frameCounter is greater than or equal to 10 and less than 20... { projectile.frame = 1; //Set it to the second frame } else if (projectile.frameCounter >= 20 && projectile.frameCounter < 30) //If the frameCounter is greater than or equal to 20 and less than 30.... { projectile.frame = 2; //Set it to the third frame } else if (projectile.frameCounter >= 30 && projectile.frameCounter < 40) //If the frameCounter is greater than or equal to 30 and less than 40... { projectile.frame = 3; //Set it to the fourth frame } else //Let's just say this assumes that the frameCounter is greater than or equal to 40. { projectile.frame = 0; //Reset it to the first frame projectile.frameCounter = 0; //Reset the counter so it goes back to the beginning } }
So before we start with anything... Let's get our JSON files done.
Code:{ "displayName": "Cursed Flame Powder", "size": [52,18], "maxStack": 99, "value": [0,0,0,1], "rare": 1, "tooltip": ["Powder that causes Cursed Inferno on enemies."], "useStyle": 5, "useAnimation": 30, "useTime": 30, "useSound": 1, "consumable": true, //We want it to be used each time, since it is a powder like weapon "shoot": "MPT:CursedFlamePowder", "noUseGraphic": true, // We don't want it to show the item when used "shootSpeed": 2, //Adjust this for the range "recipes": [{ "items": { "Dirt Block": 1 }, "creates": 99 }] }
Code:{ "displayName": "Cursed Flame Powder", "size": [8, 8], "aiStyle": 0, "timeLeft": 120, "friendly": true, "hostile": false, "tileCollide": false, "ignoreWater": true, "penetrate": -1, //I sreiously don't think you'll be needing this, since 0 damage projectiles automatically go through enemies "maxUpdates": 5 //Shows more dust at once (As Bluemagic explained to me, this affects the speed of the projectile also) }
Now that we're done with those.... let's get with the intricacies of the AI of the projectile!
Code:using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace MPT.Projectiles { public class CursedFlamePowder : ModProjectile { public override void AI() { projectile.light = 0.9f; //Yay light! projectile.alpha = 255; //Makes sure it doesn't show a texture for(int thickness = 0; thickness < 6; thickness++) //The thickness, adjust the interger for how thick you want it { int DustID1 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y + 2f), projectile.width + 4, projectile.height + 4, 60, projectile.velocity.X * 0.2f, projectile.velocity.Y * 0.2f, 0, default(Color), 1.3f); //Creates the "powder" Main.dust[DustID1].scale = (float) Main.rand.Next(70, 110) * 0.013f; Main.dust[DustID1].noGravity = true; } //By projectile width/height, these are the width/height of the projectile's hitbox if(projectile.width > 128 && projectile.height > 128) //If the projectile's width/height exceeds 128 pixels { //Keep the projectile's width/height at 128 projectile.width = 128; projectile.height = 128; } else //Assume this means that the width/height of the projectile is less than 128 { //Increase the width/height by 1 pixel projectile.width += 1; projectile.height += 1; } //Berberborscing's Code foreach (NPC N in Main.npc) //Sets up variable for the NPC(s) in range { Rectangle MB = new Rectangle((int)projectile.position.X + (int)projectile.velocity.X, (int)projectile.position.Y + (int)projectile.velocity.Y, projectile.width, projectile.height); //Variable sets up projectile's hitbox Rectangle NB = new Rectangle((int)N.position.X, (int)N.position.Y, N.width, N.height); //Variable sets up NPC's hitbox if (MB.Intersects(NB)) //If the two hitboxes touch... { N.AddBuff(39, 300); //Then the enemy will receive a debuff! } } //End of Berberborscing's Code } } }
Coming Soon...Someone already beat me to it, but, this guide is great for those who want to make a simple flail/hook with a custom chain texture!
[TUTORIAL] How to make a basic flail type weapon - Gmod
Cool Things to Add To Your Code (Mostly Ripped from Vanilla Code)
Code:public override void PostKill() { Main.PlaySound(2, (int)projectile.position.X, (int)projectile.position.Y, 14); for (int num369 = 0; num369 < 20; num369++) { int num370 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 31, 0f, 0f, 100, default(Color), 1.5f); Main.dust[num370].velocity *= 1.4f; } for (int num371 = 0; num371 < 10; num371++) { int num372 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 6, 0f, 0f, 100, default(Color), 2.5f); Main.dust[num372].noGravity = true; Main.dust[num372].velocity *= 5f; num372 = Dust.NewDust(new Vector2(projectile.position.X, projectile.position.Y), projectile.width, projectile.height, 6, 0f, 0f, 100, default(Color), 1.5f); Main.dust[num372].velocity *= 3f; } int num373 = Gore.NewGore(new Vector2(projectile.position.X, projectile.position.Y), default(Vector2), Main.rand.Next(61, 64), 1f); Main.gore[num373].velocity *= 0.4f; Gore gore85 = Main.gore[num373]; gore85.velocity.X = gore85.velocity.X + 1f; Gore gore86 = Main.gore[num373]; gore86.velocity.Y = gore86.velocity.Y + 1f; num373 = Gore.NewGore(new Vector2(projectile.position.X, projectile.position.Y), default(Vector2), Main.rand.Next(61, 64), 1f); Main.gore[num373].velocity *= 0.4f; Gore gore87 = Main.gore[num373]; gore87.velocity.X = gore87.velocity.X - 1f; Gore gore88 = Main.gore[num373]; gore88.velocity.Y = gore88.velocity.Y + 1f; num373 = Gore.NewGore(new Vector2(projectile.position.X, projectile.position.Y), default(Vector2), Main.rand.Next(61, 64), 1f); Main.gore[num373].velocity *= 0.4f; Gore gore89 = Main.gore[num373]; gore89.velocity.X = gore89.velocity.X + 1f; Gore gore90 = Main.gore[num373]; gore90.velocity.Y = gore90.velocity.Y - 1f; num373 = Gore.NewGore(new Vector2(projectile.position.X, projectile.position.Y), default(Vector2), Main.rand.Next(61, 64), 1f); Main.gore[num373].velocity *= 0.4f; Gore gore91 = Main.gore[num373]; gore91.velocity.X = gore91.velocity.X - 1f; Gore gore92 = Main.gore[num373]; gore92.velocity.Y = gore92.velocity.Y - 1f; }[/SIZE]
[/SPOILER]Code:using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace ModInternalName { [GlobalMod] public class MProjectile : TAPI.ModProjectile { public override void PostAI() { Player player = Main.player[projectile.owner]; for (int num46 = projectile.oldPos.Length - 1; num46 > 0; num46--) { projectile.oldPos[num46] = projectile.oldPos[num46 - 1]; } projectile.oldPos[0] = projectile.position; } } }[/SIZE]
Code:public override void PostDraw(SpriteBatch sb) { Color color21 = Lighting.GetColor((int)((double)projectile.position.X + (double)projectile.width * 0.5) / 16, (int)(((double)projectile.position.Y + (double)projectile.height * 0.5) / 16.0)); SpriteEffects effects = SpriteEffects.None; int num98 = 0; int num99 = 0; float num100 = (float)(Main.projectileTexture[projectile.type].Width - projectile.width) * 0.5f + (float)projectile.width * 0.5f; for(int num126 = 0; num126 < 10; num126++) { Color alpha4 = projectile.GetAlpha(color21); float num127 = (float)(9 - num126) / 9f; alpha4.R = (byte)((float)alpha4.R * num127); alpha4.G = (byte)((float)alpha4.G * num127); alpha4.B = (byte)((float)alpha4.B * num127); alpha4.A = (byte)((float)alpha4.A * num127); float num128 = (float)(9 - num126) / 9f; Main.spriteBatch.Draw(Main.projectileTexture[projectile.type], new Vector2(projectile.oldPos[num126].X - Main.screenPosition.X + num100 + (float)num99, projectile.oldPos[num126].Y - Main.screenPosition.Y + (float)(projectile.height / 2) + projectile.gfxOffY), new Rectangle?(new Rectangle(0, 0, Main.projectileTexture[projectile.type].Width, Main.projectileTexture[projectile.type].Height)), alpha4, projectile.rotation, new Vector2(num100, (float)(projectile.height / 2 + num98)), num128 * projectile.scale, effects, 0f); } }[/SIZE]
I don't mod as much now, but I can help you with this small part. For the weapon to damage the player when you fire the gun, we should delve into the PreShoot() method for the weapon, in there you can add the code for damaging the player. So it should look something like this.I want to make a gun that hurts you when you fire it, but heals you when you kill an npc what scripts would i need to do this? PLZ help!
public override bool PreShoot(Player player, Vector2 position, Vector2 velocity, int type, int damage, float knockback)
{
player.statLife -= 2; //Subtract the player's health
CombatText.NewText(new Rectangle((int)player.position.X, (int)player.position.Y, player.width, player.height), new Color(255, 60, 70, 255), string.Concat(2), false, true);
//Shows the damage number. Change the number inside string.Concat() part of the code.
return true; //Makes the gun fire your projectile
}
I don't mod as much now, but I can help you with this small part. For the weapon to damage the player when you fire the gun, we should delve into the PreShoot() method for the weapon, in there you can add the code for damaging the player. So it should look something like this.
Code:public override bool PreShoot(Player player, Vector2 position, Vector2 velocity, int type, int damage, float knockback) { player.statLife -= 2; //Subtract the player's health CombatText.NewText(new Rectangle((int)player.position.X, (int)player.position.Y, player.width, player.height), new Color(255, 60, 70, 255), string.Concat(2), false, true); //Shows the damage number. Change the number inside string.Concat() part of the code. return true; //Makes the gun fire your projectile }
For me to give you a better idea for the second part, does this weapon have its own projectile or does it use any type of ammo?
Hello, is there an update for tApi to make 1.3 mods?I don't mod as much now, but I can help you with this small part. For the weapon to damage the player when you fire the gun, we should delve into the PreShoot() method for the weapon, in there you can add the code for damaging the player. So it should look something like this.
Code:public override bool PreShoot(Player player, Vector2 position, Vector2 velocity, int type, int damage, float knockback) { player.statLife -= 2; //Subtract the player's health CombatText.NewText(new Rectangle((int)player.position.X, (int)player.position.Y, player.width, player.height), new Color(255, 60, 70, 255), string.Concat(2), false, true); //Shows the damage number. Change the number inside string.Concat() part of the code. return true; //Makes the gun fire your projectile }
For me to give you a better idea for the second part, does this weapon have its own projectile or does it use any type of ammo?
public override void DealtNPC(NPC n, int hitDir, int dmgDealt, float knockback, bool crit)
{
if(n.life < 1) //If the npc is dead
{
int heal = 5; //Initating a heal amount variable, not really necessary though, as you can just straight up use an integer.
p.statLife += heal; //add life to the player
p.HealEffect(heal, true); //show the amount of health healed
}
}
Hello, is there an update for tApi to make 1.3 mods?
If there isn't what should I use?
I don't mod as much now, but I can help you with this small part. For the weapon to damage the player when you fire the gun, we should delve into the PreShoot() method for the weapon, in there you can add the code for damaging the player. So it should look something like this.
Code:public override bool PreShoot(Player player, Vector2 position, Vector2 velocity, int type, int damage, float knockback) { player.statLife -= 2; //Subtract the player's health CombatText.NewText(new Rectangle((int)player.position.X, (int)player.position.Y, player.width, player.height), new Color(255, 60, 70, 255), string.Concat(2), false, true); //Shows the damage number. Change the number inside string.Concat() part of the code. return true; //Makes the gun fire your projectile }
For me to give you a better idea for the second part, does this weapon have its own projectile or does it use any type of ammo?
In that case, you should go to your projectile cs file (if you already do, if not make one) and then use the DealtNpc() method. Sort of like this. Not sure if it will work though.
Code:public override void DealtNPC(NPC n, int hitDir, int dmgDealt, float knockback, bool crit) { if(n.life < 1) //If the npc is dead { int heal = 5; //Initating a heal amount variable, not really necessary though, as you can just straight up use an integer. p.statLife += heal; //add life to the player p.HealEffect(heal, true); //show the amount of health healed } }
Oh oops, you need to add the Player variable called p first, which I forgot to include within the code. All you need to do is create the Player variable anywhere before those two functions/variables(p.statLife + p.HealEffect), but I prefer putting it at the top of the method, so a modified version of the one I sent should be....Oh , im getting an error in the projectile. think its saying it doesnt know what the p in p.statLife is.. ?
public override void DealtNPC(NPC n, int hitDir, int dmgDealt, float knockback, bool crit)
{
Player p = Main.player[projectile.owner]; //This is the thing that allows the projectile to manipulate the player, you could probably add this as a global variable instead allowing you to use this in the WHOLE cs file.
if(n.life < 1) //If the npc is dead
{
int heal = 5; //Initating a heal amount variable, not really necessary though, as you can just straight up use an integer.
p.statLife += heal; //add life to the player
p.HealEffect(heal, true); //show the amount of health healed
}
}
public static Player p = Main.player[projectile.owner];
public override void DealtNPC(NPC n, int hitDir, int dmgDealt, float knockback, bool crit)
{
if(n.life < 1) //If the npc is dead
{
int heal = 5; //Initating a heal amount variable, not really necessary though, as you can just straight up use an integer.
p.statLife += heal; //add life to the player
p.HealEffect(heal, true); //show the amount of health healed
}
}
Oh oops, you need to add the Player variable called p first, which I forgot to include within the code. All you need to do is create the Player variable anywhere before those two functions/variables(p.statLife + p.HealEffect), but I prefer putting it at the top of the method, so a modified version of the one I sent should be....
Code:public override void DealtNPC(NPC n, int hitDir, int dmgDealt, float knockback, bool crit) { Player p = Main.player[projectile.owner]; //This is the thing that allows the projectile to manipulate the player, you could probably add this as a global variable instead allowing you to use this in the WHOLE cs file. if(n.life < 1) //If the npc is dead { int heal = 5; //Initating a heal amount variable, not really necessary though, as you can just straight up use an integer. p.statLife += heal; //add life to the player p.HealEffect(heal, true); //show the amount of health healed } }
You can make this a global variable by making the CS file for the projectile and placing it before your hooks like this.
Code:public static Player p = Main.player[projectile.owner]; public override void DealtNPC(NPC n, int hitDir, int dmgDealt, float knockback, bool crit) { if(n.life < 1) //If the npc is dead { int heal = 5; //Initating a heal amount variable, not really necessary though, as you can just straight up use an integer. p.statLife += heal; //add life to the player p.HealEffect(heal, true); //show the amount of health healed } }
Okay, thank you.In that case, you should go to your projectile cs file (if you already do, if not make one) and then use the DealtNpc() method. Sort of like this. Not sure if it will work though.
Code:public override void DealtNPC(NPC n, int hitDir, int dmgDealt, float knockback, bool crit) { if(n.life < 1) //If the npc is dead { int heal = 5; //Initating a heal amount variable, not really necessary though, as you can just straight up use an integer. p.statLife += heal; //add life to the player p.HealEffect(heal, true); //show the amount of health healed } }
Well from what I've read, they have discontinued tAPI, so there will never be 1.3 support lest someone already updated tAPI to do so without me knowing. There are new API, but they are still in the alpha stage. The new APIs consist of Bluemagic's tModLoader and some of tAPI's developers are making a new API called Prism. I don't have much experience with either of them, so you might want to try them out yourself. If I recall correctly, tModLoader is in near completion I think. You can find tModLoader in the "Works in Progress" and I think Prism is in the "Released" section.
That is quite weird... Player variable is dabbled in the tutorial, I believe in the "Making Projectiles Unique" secition.it's still not quite working, i think its saying in the
public static Player p = Main.player[projectile.owner];
it doesnt like the projectile.owner thing
I am currently not actively modding.Okay, thank you.
Are you using the previous version of Terraria?
Oh okThat is quite weird... Player variable is dabbled in the tutorial, I believe in the "Making Projectiles Unique" secition.
I am currently not actively modding.