Where do i put this piece of code?float moveToX = projectile.position.X;
float moveToY = projectile.position.Y;
float moveDist = 100000f; //This is basically the view range of your minion; you might want to make it slightly larger than this.
int attacking = -1;
for(int k = 0; k < 200; k++)
{
if(Main.npc[k].active && !Main.npc[k].dontTakeDamage && !Main.npc[k].friendly && Main.npc[k].lifeMax > 5)
{
float monsterX = Main.npc[k].position.X + (float)(Main.npc[k].width / 2);
float monsterY = Main.npc[k].position.Y + (float)(Main.npc[k].height / 2);
float monsterDist = System.Math.Abs(projectile.position.X + (float)(projectile.width / 2) - monsterX) + System.Math.Abs(projectile.position.Y + (float)(projectile.height / 2) - monsterY);
if(monsterDist < moveDist)
{
if(monsterDist < moveDist && Collision.CanHit(projectile.position, projectile.width, projectile.height, Main.npc[k].position, Main.npc[k].width, Main.npc[k].height))
{
moveDist = monsterDist;
moveToX = monsterX;
moveToY = monsterY;
attacking = k;
}
}
}
}
moveDist is the distance from the distance from the nearest monster your projectile can see; moveToX and moveToY specify the location of this monster; and attacking is the index location of the monster in Main.npc.
You'd put that in the AI method, where the comment says to do what the minion what normally do (attacking enemies or following the player).Where do i put this piece of code?
also, you haven't really said where any of these should be so it's kind of hard to figure out
Now that r15 for tAPI has finally been released, I've updated my tutorial. Basically, a bug was fixed so that we don't need to manually despawn minions anymore. This means that we don't need a ModItem class anymore, so that's less source code we need to copy!
Now to wait for the same bug to be fixed with Damage...
Also, I recognized that the AI part (ModProjectile) of the tutorial was a bit lacking, so I included a link to my NPC AI tutorial, since NPCs and projectiles are very similar.
Also I finally get to use the thread changelog I had at the end of the post, lol
To be honest, the code doesn't really become any easier; we just don't need to copy over a bunch of source code anymore to free minion slots.Less hard code?
To be honest, the code doesn't really become any easier; we just don't need to copy over a bunch of source code anymore to free minion slots.
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using TAPI;
using Terraria;
namespace Terra.Items
{
public class MonadoShield : ModItem
{
public override void DamageNPC(Player p, NPC n, int hitDir, ref int damage, ref float knockback, ref bool crit, ref float critMult)
{
if (crit)
{
if (Main.rand.Next(5) == 0)
{
p.AddBuff("Terra:MonadoJump",420,true)
}
else
{
p.AddBuff("Terra:MonadoSpeed",420,true)
}
else
{
p.AddBuff("Terra:MonadoShield",420,true)
}
else
{
p.AddBuff("Terra:MonadoBuster",420,true)
}
else
{
p.AddBuff("Terra:MonadoSmash",420,true)
}
}
}
}
}
It looks like you have the right idea; it's just the syntax that's incorrect. Specifically, this part of the code:Question: If I want it to be that when you crit, you get 1 of 5 buffs, will this code work?
Code:using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using TAPI; using Terraria; namespace Terra.Items { public class MonadoShield : ModItem { public override void DamageNPC(Player p, NPC n, int hitDir, ref int damage, ref float knockback, ref bool crit, ref float critMult) { if (crit) { if (Main.rand.Next(5) == 0) { p.AddBuff("Terra:MonadoJump",420,true) } else { p.AddBuff("Terra:MonadoSpeed",420,true) } else { p.AddBuff("Terra:MonadoShield",420,true) } else { p.AddBuff("Terra:MonadoBuster",420,true) } else { p.AddBuff("Terra:MonadoSmash",420,true) } } } } }
if (Main.rand.Next(5) == 0)
{
p.AddBuff("Terra:MonadoJump",420,true)
}
else
{
p.AddBuff("Terra:MonadoSpeed",420,true)
}
else
{
p.AddBuff("Terra:MonadoShield",420,true)
}
else
{
p.AddBuff("Terra:MonadoBuster",420,true)
}
else
{
p.AddBuff("Terra:MonadoSmash",420,true)
}
int choice = Main.rand.Next(5);
if(choice == 0)
{
//add your buff
}
else if(choice == 1)
{
//add a different buff
}
else if(choice == 2)
{
//etc.
}
int choice = Main.rand.Next(5);
switch(choice)
{
case 0:
//add your buff
break;
case 1:
//add a different buff
break;
case 2:
//etc.
break;
case 3:
//etc.
break;
default:
//this is the last choice
break;
}
It looks like you have the right idea; it's just the syntax that's incorrect. Specifically, this part of the code:
What you want to do is assign the random number to a variable, then use that variable either in an if/else chain or a switch statement, like this:Code:if (Main.rand.Next(5) == 0) { p.AddBuff("Terra:MonadoJump",420,true) } else { p.AddBuff("Terra:MonadoSpeed",420,true) } else { p.AddBuff("Terra:MonadoShield",420,true) } else { p.AddBuff("Terra:MonadoBuster",420,true) } else { p.AddBuff("Terra:MonadoSmash",420,true) }
All the way to if(choice == 4). If you want to use a switch statement instead, then it would work something like this:Code:int choice = Main.rand.Next(5); if(choice == 0) { //add your buff } else if(choice == 1) { //add a different buff } else if(choice == 2) { //etc. }
Code:int choice = Main.rand.Next(5); switch(choice) { case 0: //add your buff break; case 1: //add a different buff break; case 2: //etc. break; case 3: //etc. break; default: //this is the last choice break; }
{
"displayName": "Piranha Minion",
"tip": "This is a Piranha Minion. Yay, a Summoner Weapon.",
"noTimer": true
}
using System;
using Terraria;
using TAPI;
namespace Terra.Buffs {
public class Piranhaminion : ModBuff
{
public override void Start(Player player, int index)
{
player.buffTime[index] = 3600;
}
public override void MidUpdate(Player player)
{
bool flag = false;
for(int k = 0; k < 1000; k++)
{
if(Main.projectile[k].active && Main.projectile[k].owner == player.whoAmI && Main.projectile[k].type == ProjDef.byName["Terra:Piranhasummon"].type)
{
flag = true;
break;
}
}
MPlayer modPlayer = player.GetSubClass<MPlayer>();
if(flag)
{
modPlayer.ModMinion = true;
}
int type = BuffDef.byName["Terra:Piranhaminion"];
int index = -1;
for(int k = 0; k < player.maxBuffs; k++)
{
if(player.buffType[k] == type)
{
index = k;
break;
}
}
if(index < 0)
{
return;
}
if(!modPlayer.ModMinion)
{
player.DelBuff(index);
}
else
{
player.buffTime[index] = 18000;
}
}
}}
{
"displayName": "Piranha Summon",
"size": [28, 22],
"aiStyle": 54, //This is the Raven AI. List of different summon AIs below.
"timeLeft": 99999999999999999999999999, //Just to make sure the projectile doesn't despawn.
"friendly": true,
"hostile": false,
"tileCollide": false, //So it can return to you with no trouble.
"damage": 13,//Damage projectiles from this summon do.
"minion": true, //Receives summoner bonuses
"minionSlots": 1, //Takes up 1 minion slot
"penetrate": -1 //Lasts forever
}
using System;
using Microsoft.Xna.Framework;
using Terraria;
using TAPI;
namespace Terra.Projectiles {
public class Piranhasummon : ModProjectile
{
//If you want to store information, place fields here.
public override void AI()
{
Player owner = Main.player[projectile.owner];
if (owner == null || !owner.active || owner.dead)
{
projectile.Kill();
return;
}
if (Main.myPlayer == owner.whoAmI)
{
int id = owner.HasBuff("Terra:Piranhaminion");
if(id == -1)
{
projectile.Kill();
return;
}
}
}
private void UpdateFrame()
{
//Set projectile.frame here based on your projectile's state. I'll explain what this is later. To keep track of animation time, you can use projectile.frameCounter.
//projectile.frameCounter++; //Only if you are using projectile.frameCounter.
//projectile.frameCounter %= 10; //Or however long you want one cycle of animation to last.
}
}}
{
"displayName": "Piranha Staff",
"size": [84, 84],
"maxStack": 1,
"value": [0, 3, 0, 0],
"rare": 2,
"useStyle": 1,
"useAnimation": 18,
"tooltip": "Spawns a Piranha that hopefully isn't buggy",
"useTime": 18,
"useSound": 44,
"damage": 8,
"knockback": 6,
"autoReuse": false,
"noMelee": true,
"buff": "Terra:Piranhaminion",
"buffTime": 99999999999999999999999,
"shoot": "Terra:Piranhasummon",
"shootSpeed": 3,
"summon": true,
"mana": 10
}
Hm, it's hard to tell. Did you try copying over the Damage() source code and removing the first two if statements? Also, did you try making sure projectile.friendly is true when it is attacking?FOR THE LIFE OF ME, WHY ISN'T MY PROJECTILE DOING DAMAGE (MINION)?
Code:{ "displayName": "Piranha Minion", "tip": "This is a Piranha Minion. Yay, a Summoner Weapon.", "noTimer": true }
Code:using System; using Terraria; using TAPI; namespace Terra.Buffs { public class Piranhaminion : ModBuff { public override void Start(Player player, int index) { player.buffTime[index] = 3600; } public override void MidUpdate(Player player) { bool flag = false; for(int k = 0; k < 1000; k++) { if(Main.projectile[k].active && Main.projectile[k].owner == player.whoAmI && Main.projectile[k].type == ProjDef.byName["Terra:Piranhasummon"].type) { flag = true; break; } } MPlayer modPlayer = player.GetSubClass<MPlayer>(); if(flag) { modPlayer.ModMinion = true; } int type = BuffDef.byName["Terra:Piranhaminion"]; int index = -1; for(int k = 0; k < player.maxBuffs; k++) { if(player.buffType[k] == type) { index = k; break; } } if(index < 0) { return; } if(!modPlayer.ModMinion) { player.DelBuff(index); } else { player.buffTime[index] = 18000; } } }}
Code:{ "displayName": "Piranha Summon", "size": [28, 22], "aiStyle": 54, //This is the Raven AI. List of different summon AIs below. "timeLeft": 99999999999999999999999999, //Just to make sure the projectile doesn't despawn. "friendly": true, "hostile": false, "tileCollide": false, //So it can return to you with no trouble. "damage": 13,//Damage projectiles from this summon do. "minion": true, //Receives summoner bonuses "minionSlots": 1, //Takes up 1 minion slot "penetrate": -1 //Lasts forever }
Code:using System; using Microsoft.Xna.Framework; using Terraria; using TAPI; namespace Terra.Projectiles { public class Piranhasummon : ModProjectile { //If you want to store information, place fields here. public override void AI() { Player owner = Main.player[projectile.owner]; if (owner == null || !owner.active || owner.dead) { projectile.Kill(); return; } if (Main.myPlayer == owner.whoAmI) { int id = owner.HasBuff("Terra:Piranhaminion"); if(id == -1) { projectile.Kill(); return; } } } private void UpdateFrame() { //Set projectile.frame here based on your projectile's state. I'll explain what this is later. To keep track of animation time, you can use projectile.frameCounter. //projectile.frameCounter++; //Only if you are using projectile.frameCounter. //projectile.frameCounter %= 10; //Or however long you want one cycle of animation to last. } }}
It builds fine, but it won't do any damage.Code:{ "displayName": "Piranha Staff", "size": [84, 84], "maxStack": 1, "value": [0, 3, 0, 0], "rare": 2, "useStyle": 1, "useAnimation": 18, "tooltip": "Spawns a Piranha that hopefully isn't buggy", "useTime": 18, "useSound": 44, "damage": 8, "knockback": 6, "autoReuse": false, "noMelee": true, "buff": "Terra:Piranhaminion", "buffTime": 99999999999999999999999, "shoot": "Terra:Piranhasummon", "shootSpeed": 3, "summon": true, "mana": 10 }
Hm, it's hard to tell. Did you try copying over the Damage() source code and removing the first two if statements? Also, did you try making sure projectile.friendly is true when it is attacking?
Have you tried setting the projectile's damage directly in the AI? For example...Friendly is set to true.
public override void AI()
{
projectile.damage = 10;
}
Just whatever name you want. For example, I use BluemagicPlayer since it's easy for me to remember.I have a question. When you write "ExamplePlayer", what should we replace that with?
using System;
using Microsoft.Xna.Framework;
using Terraria;
namespace Hypaxi.Projectiles.Minions
{
public abstract class Mancer : Minion
{
protected float idleAccel = 0.05f;
protected float spacingMult = 1f;
protected float viewDist = 400f;
protected float chaseDist = 200f;
protected float chaseAccel = 6f;
protected float inertia = 40f;
protected float shootCool = 90f;
protected float shootSpeed;
protected int shoot;
public virtual void CreateDust()
{
}
public virtual void SelectFrame()
{
}
public override void Behavior()
{
Player player = Main.player[projectile.owner];
float spacing = (float)projectile.width * spacingMult;
for (int k = 0; k < 1000; k++)
{
Projectile otherProj = Main.projectile[k];
if (k != projectile.whoAmI && otherProj.active && otherProj.owner == projectile.owner && otherProj.type == projectile.type && System.Math.Abs(projectile.position.X - otherProj.position.X) + System.Math.Abs(projectile.position.Y - otherProj.position.Y) < spacing)
{
if (projectile.position.X < Main.projectile[k].position.X)
{
projectile.velocity.X -= idleAccel;
}
else
{
projectile.velocity.X += idleAccel;
}
if (projectile.position.Y < Main.projectile[k].position.Y)
{
projectile.velocity.Y -= idleAccel;
}
else
{
projectile.velocity.Y += idleAccel;
}
}
}
Vector2 targetPos = projectile.position;
float targetDist = viewDist;
bool target = false;
projectile.tileCollide = true;
for (int k = 0; k < 200; k++)
{
NPC npc = Main.npc[k];
if (npc.CanBeChasedBy(this, false))
{
float distance = Vector2.Distance(npc.Center, projectile.Center);
if ((distance < targetDist || !target) && Collision.CanHitLine(projectile.position, projectile.width, projectile.height, npc.position, npc.width, npc.height))
{
targetDist = distance;
targetPos = npc.Center;
target = true;
}
}
}
if (Vector2.Distance(player.Center, projectile.Center) > (target ? 1000f : 500f))
{
projectile.ai[0] = 1f;
projectile.netUpdate = true;
}
if (projectile.ai[0] == 1f)
{
projectile.tileCollide = false;
}
if (target && projectile.ai[0] == 0f)
{
Vector2 direction = targetPos - projectile.Center;
if (direction.Length() > chaseDist)
{
direction.Normalize();
projectile.velocity = (projectile.velocity * inertia + direction * chaseAccel) / (inertia + 1);
}
else
{
projectile.velocity *= (float)Math.Pow(0.97, 40.0 / inertia);
}
}
else
{
if (!Collision.CanHitLine(projectile.Center, 1, 1, player.Center, 1, 1))
{
projectile.ai[0] = 1f;
}
float speed = 6f;
if (projectile.ai[0] == 1f)
{
speed = 15f;
}
Vector2 center = projectile.Center;
Vector2 direction = player.Center - center;
projectile.ai[1] = 3600f;
projectile.netUpdate = true;
int num = 1;
for (int k = 0; k < projectile.whoAmI; k++)
{
if (Main.projectile[k].active && Main.projectile[k].owner == projectile.owner && Main.projectile[k].type == projectile.type)
{
num++;
}
}
direction.X -= (float)((10 + num * 40) * player.direction);
direction.Y -= 70f;
float distanceTo = direction.Length();
if (distanceTo > 200f && speed < 9f)
{
speed = 9f;
}
if (distanceTo < 100f && projectile.ai[0] == 1f && !Collision.SolidCollision(projectile.position, projectile.width, projectile.height))
{
projectile.ai[0] = 0f;
projectile.netUpdate = true;
}
if (distanceTo > 2000f)
{
projectile.Center = player.Center;
}
if (distanceTo > 48f)
{
direction.Normalize();
direction *= speed;
float temp = inertia / 2f;
projectile.velocity = (projectile.velocity * temp + direction) / (temp + 1);
}
else
{
projectile.direction = Main.player[projectile.owner].direction;
projectile.velocity *= (float)Math.Pow(0.9, 40.0 / inertia);
}
}
projectile.rotation = projectile.velocity.X * 0.05f;
SelectFrame();
CreateDust();
if (projectile.velocity.X > 0f)
{
projectile.spriteDirection = (projectile.direction = -1);
}
else if (projectile.velocity.X < 0f)
{
projectile.spriteDirection = (projectile.direction = 1);
}
if (projectile.ai[1] > 0f)
{
projectile.ai[1] += 1f;
if (Main.rand.Next(3) == 0)
{
projectile.ai[1] += 1f;
}
}
if (projectile.ai[1] > shootCool)
{
projectile.ai[1] = 0f;
projectile.netUpdate = true;
}
if (projectile.ai[0] == 0f)
{
if (target)
{
if ((targetPos - projectile.Center).X > 0f)
{
projectile.spriteDirection = (projectile.direction = -1);
}
else if ((targetPos - projectile.Center).X < 0f)
{
projectile.spriteDirection = (projectile.direction = 1);
}
if (projectile.ai[1] == 0f)
{
projectile.ai[1] = 1f;
if (Main.myPlayer == projectile.owner)
{
Vector2 shootVel = targetPos - projectile.Center;
if (shootVel == Vector2.Zero)
{
shootVel = new Vector2(0f, 1f);
}
shootVel.Normalize();
shootVel *= shootSpeed;
int proj = Projectile.NewProjectile(projectile.Center.X, projectile.Center.Y, shootVel.X, shootVel.Y, shoot, projectile.damage, projectile.knockBack, Main.myPlayer, 0f, 0f);
Main.projectile[proj].timeLeft = 300;
Main.projectile[proj].netUpdate = true;
projectile.netUpdate = true;
}
}
}
}
}
public override void TileCollideStyle(ref int width, ref int height, ref bool fallThrough)
{
fallThrough = true;
}
}
}
I had someone help me out with this and I'm a little bad at C so can someone explain to me why my minions sprite is flapping like a bird.