Standalone [1.3] tModLoader - A Modding API

What is happening is the sound is restarting every frame, so you never hear it until it dies (since it doesn't restart the next frame)
You forgot the code that prevents it from triggering more than once:
Code:
        public override void AI()
        {
            Lighting.AddLight(projectile.Center, 0.1f, 0.4f, 1f);
            if (projectile.ai[1] == 0f)
            {
                projectile.ai[1] = 0f;
                Main.PlaySound(2, (int)projectile.position.X, (int)projectile.position.Y, 28);
            }

            Vector2 vector13;
            float num138;
            float num139;
            float num140;
            float num141;
            float num142;
            bool flag4;
            int num143;
            int num144;
            float num145;
            float num146;
            float num147;
            int num148;
            float num149;
            float num150;
            float num151;
            float num152;
            float num153;
            float num154;
            float num155;
            int num156;

            {
                for (int num136 = 0; num136 < 1; num136++)
                {
                    float x2 = projectile.position.X - projectile.velocity.X / 10f * (float)num136;
                    float y2 = projectile.position.Y - projectile.velocity.Y / 10f * (float)num136;
                    int dust1 = Dust.NewDust(new Vector2(x2, y2), 20, 20, 15, 0f, 0f, 200, default(Color), 2f);
                    Main.dust[dust1].velocity *= 0f;
                    Main.dust[dust1].noGravity = true;
                    int dust2= Dust.NewDust(new Vector2(x2, y2), 20, 20, 15, 0f, 0f, 200, default(Color), 1f);
                    Main.dust[dust2].velocity *= 0f;
                    Main.dust[dust2].noGravity = true;
                }
            }

            num138 = (float)Math.Sqrt((double)(projectile.velocity.X * projectile.velocity.X + projectile.velocity.Y * projectile.velocity.Y));
            num139 = projectile.localAI[0];
            if (num139 == 0f)
            {
                projectile.localAI[0] = num138;
                num139 = num138;
            }

            num140 = projectile.position.X;
            num141 = projectile.position.Y;
            num142 = 300f;
            flag4 = false;
            num143 = 0;
            if (projectile.ai[1] == 0f)
            {
                for (num144 = 0; num144 < 200; num144++)
                {
                    if (Main.npc[num144].CanBeChasedBy(projectile, false) && (projectile.ai[1] == 0f || projectile.ai[1] == (float)(num144 + 1)))
                    {
                        num145 = Main.npc[num144].position.X + (float)(Main.npc[num144].width / 2);
                        num146 = Main.npc[num144].position.Y + (float)(Main.npc[num144].height / 2);
                        num147 = Math.Abs(projectile.position.X + (float)(projectile.width / 2) - num145) + Math.Abs(projectile.position.Y + (float)(projectile.height / 2) - num146);
                        if (num147 < num142 && Collision.CanHit(new Vector2(projectile.position.X + (float)(projectile.width / 2), projectile.position.Y + (float)(projectile.height / 2)), 1, 1, Main.npc[num144].position, Main.npc[num144].width, Main.npc[num144].height))
                        {
                            num142 = num147;
                            num140 = num145;
                            num141 = num146;
                            flag4 = true;
                            num143 = num144;
                        }
                    }
                }
                if (flag4)
                {
                    projectile.ai[1] = (float)(num143 + 1);
                }
                flag4 = false;
            }
            if (projectile.ai[1] > 0f)
            {
                num148 = (int)(projectile.ai[1] - 1f);
                if (Main.npc[num148].active && Main.npc[num148].CanBeChasedBy(projectile, true) && !Main.npc[num148].dontTakeDamage)
                {
                    num149 = Main.npc[num148].position.X + (float)(Main.npc[num148].width / 2);
                    num150 = Main.npc[num148].position.Y + (float)(Main.npc[num148].height / 2);
                    num151 = Math.Abs(projectile.position.X + (float)(projectile.width / 2) - num149) + Math.Abs(projectile.position.Y + (float)(projectile.height / 2) - num150);
                    if (num151 < 1000f)
                    {
                        flag4 = true;
                        num140 = Main.npc[num148].position.X + (float)(Main.npc[num148].width / 2);
                        num141 = Main.npc[num148].position.Y + (float)(Main.npc[num148].height / 2);
                    }
                }
                else
                {
                    projectile.ai[1] = 0f;
                }
            }
            if (!projectile.friendly)
            {
                flag4 = false;
            }
            if (flag4)
            {
                num152 = num139;
                vector13 = new Vector2(projectile.position.X + (float)projectile.width * 0.5f, projectile.position.Y + (float)projectile.height * 0.5f);
                num153 = num140 - vector13.X;
                num154 = num141 - vector13.Y;
                num155 = (float)Math.Sqrt((double)(num153 * num153 + num154 * num154));
                num155 = num152 / num155;
                num153 *= num155;
                num154 *= num155;
                num156 = 8;
                projectile.velocity.X = (projectile.velocity.X * (float)(num156 - 1) + num153) / (float)num156;
                projectile.velocity.Y = (projectile.velocity.Y * (float)(num156 - 1) + num154) / (float)num156;
            }
        }
Okay, now I got really stuck.
Code:
            if (projectile.ai[1] == 0f)
            {
                projectile.ai[1] = 0f;
                Main.PlaySound(2, (int)projectile.position.X, (int)projectile.position.Y, 28);
            }
When I put these codes in a totally new and totally simple test laser's AI hook, it works just find.
Once I throw them in this chlorophyte bullet hooming projectile, it went wrong. By "wrong", I mean the sound will only be played when the projectile hits the tile. Based on this, I guess there may be some kinds of conflictions. But... what's wrong anyways?
 
(so actually, if you want to add in that AI code, you'll want to switch the 1 to 0 in the ai code or the code I posted earlier.)

Code:
Okay, now I got really stuck.
[CODE]            if (projectile.ai[1] == 0f)
            {
                projectile.ai[1] = 0f;
                Main.PlaySound(2, (int)projectile.position.X, (int)projectile.position.Y, 28);
            }
When I put these codes in a totally new and totally simple test laser's AI hook, it works just find.
Once I throw them in this chlorophyte bullet hooming projectile, it went wrong. By "wrong", I mean the sound will only be played when the projectile hits the tile. Based on this, I guess there may be some kinds of conflictions. But... what's wrong anyways?

Ok, I seem to have confused you, my bad. What I meant was swap ai[1] with ai[0], not 1f with 0f. Basically, the other code is using ai[1], so your bit of code needs to use the float in the ai array that isn't being used.
Code:
            if (projectile.ai[0] == 0f)
            {
                projectile.ai[0] = 1f;
                Main.PlaySound(2, (int)projectile.position.X, (int)projectile.position.Y, 28);
            }
 
Ok, I seem to have confused you, my bad. What I meant was swap ai[1] with ai[0], not 1f with 0f. Basically, the other code is using ai[1], so your bit of code needs to use the float in the ai array that isn't being used.
Code:
            if (projectile.ai[0] == 0f)
            {
                projectile.ai[0] = 1f;
                Main.PlaySound(2, (int)projectile.position.X, (int)projectile.position.Y, 28);
            }
In this case, I guess every value refers to a single and different situation. Is that right? I mean ai = 0f has nothing to do with ai = 1f. They two cases. But why is this? Why don't put them together? Or is there bound to be conflictions if they have been put together?

EDIT: :confused:Yeah, after I posted, I gave it a quick test. And now it's silent. No sound was played whatever hit or not hit. Anything missed?
 
In this case, I guess every value refers to a single and different situation. Is that right? I mean ai = 0f has nothing to do with ai = 1f. They two cases. But why is this? Why don't put them together? Or is there bound to be conflictions if they have been put together?

EDIT: :confused:Yeah, after I posted, I gave it a quick test. And now it's silent. No sound was played whatever hit or not hit. Anything missed?
All we are doing with this code is faking a boolean value. The numbers mean whatever you want them to mean. Only your code will ever use the ai[0] and ai[1] floats. Before I posted I ran it in game and the sound played every time I shot it, so the code works. Are you saying that the code didn't work for you? You didn't switch anything else around, right? Post what you got if it isn't working.
 
All we are doing with this code is faking a boolean value. The numbers mean whatever you want them to mean. Only your code will ever use the ai[0] and ai[1] floats. Before I posted I ran it in game and the sound played every time I shot it, so the code works. Are you saying that the code didn't work for you? You didn't switch anything else around, right? Post what you got if it isn't working.
OK, this is all the codes of the porjectile.
Code:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;

namespace wch.Projectiles
{
    public class UnicornPonyAttack : ModProjectile
    {
        public override void SetDefaults()
        {
            projectile.name = "Unicorn Pony Attack";
            projectile.width = 6;
            projectile.height = 6;
            projectile.aiStyle = 1;
            projectile.friendly = true;
            projectile.penetrate = 1;
            projectile.alpha = 100;
            projectile.extraUpdates = 2;
            projectile.scale = 1.4f;
            projectile.timeLeft = 600;
            projectile.magic = true;
            aiType = -1;
        }

        public override void AI()
        {
            Lighting.AddLight(projectile.Center, 0.1f, 0.4f, 1f);
            if (projectile.ai[0] == 0f)
            {
                projectile.ai[0] = 1f;
                Main.PlaySound(2, (int)projectile.position.X, (int)projectile.position.Y, 28);
            }

            Vector2 vector13;
            float num138;
            float num139;
            float num140;
            float num141;
            float num142;
            bool flag4;
            int num143;
            int num144;
            float num145;
            float num146;
            float num147;
            int num148;
            float num149;
            float num150;
            float num151;
            float num152;
            float num153;
            float num154;
            float num155;
            int num156;

            {
                for (int num136 = 0; num136 < 1; num136++)
                {
                    float x2 = projectile.position.X - projectile.velocity.X / 10f * (float)num136;
                    float y2 = projectile.position.Y - projectile.velocity.Y / 10f * (float)num136;
                    int dust1 = Dust.NewDust(new Vector2(x2, y2), 20, 20, 15, 0f, 0f, 200, default(Color), 2f);
                    Main.dust[dust1].velocity *= 0f;
                    Main.dust[dust1].noGravity = true;
                    int dust2= Dust.NewDust(new Vector2(x2, y2), 20, 20, 15, 0f, 0f, 200, default(Color), 1f);
                    Main.dust[dust2].velocity *= 0f;
                    Main.dust[dust2].noGravity = true;
                }
            }

            num138 = (float)Math.Sqrt((double)(projectile.velocity.X * projectile.velocity.X + projectile.velocity.Y * projectile.velocity.Y));
            num139 = projectile.localAI[0];
            if (num139 == 0f)
            {
                projectile.localAI[0] = num138;
                num139 = num138;
            }

            num140 = projectile.position.X;
            num141 = projectile.position.Y;
            num142 = 300f;
            flag4 = false;
            num143 = 0;
            if (projectile.ai[1] == 0f)
            {
                for (num144 = 0; num144 < 200; num144++)
                {
                    if (Main.npc[num144].CanBeChasedBy(projectile, false) && (projectile.ai[1] == 0f || projectile.ai[1] == (float)(num144 + 1)))
                    {
                        num145 = Main.npc[num144].position.X + (float)(Main.npc[num144].width / 2);
                        num146 = Main.npc[num144].position.Y + (float)(Main.npc[num144].height / 2);
                        num147 = Math.Abs(projectile.position.X + (float)(projectile.width / 2) - num145) + Math.Abs(projectile.position.Y + (float)(projectile.height / 2) - num146);
                        if (num147 < num142 && Collision.CanHit(new Vector2(projectile.position.X + (float)(projectile.width / 2), projectile.position.Y + (float)(projectile.height / 2)), 1, 1, Main.npc[num144].position, Main.npc[num144].width, Main.npc[num144].height))
                        {
                            num142 = num147;
                            num140 = num145;
                            num141 = num146;
                            flag4 = true;
                            num143 = num144;
                        }
                    }
                }
                if (flag4)
                {
                    projectile.ai[1] = (float)(num143 + 1);
                }
                flag4 = false;
            }
            if (projectile.ai[1] > 0f)
            {
                num148 = (int)(projectile.ai[1] - 1f);
                if (Main.npc[num148].active && Main.npc[num148].CanBeChasedBy(projectile, true) && !Main.npc[num148].dontTakeDamage)
                {
                    num149 = Main.npc[num148].position.X + (float)(Main.npc[num148].width / 2);
                    num150 = Main.npc[num148].position.Y + (float)(Main.npc[num148].height / 2);
                    num151 = Math.Abs(projectile.position.X + (float)(projectile.width / 2) - num149) + Math.Abs(projectile.position.Y + (float)(projectile.height / 2) - num150);
                    if (num151 < 1000f)
                    {
                        flag4 = true;
                        num140 = Main.npc[num148].position.X + (float)(Main.npc[num148].width / 2);
                        num141 = Main.npc[num148].position.Y + (float)(Main.npc[num148].height / 2);
                    }
                }
                else
                {
                    projectile.ai[1] = 0f;
                }
            }
            if (!projectile.friendly)
            {
                flag4 = false;
            }
            if (flag4)
            {
                num152 = num139;
                vector13 = new Vector2(projectile.position.X + (float)projectile.width * 0.5f, projectile.position.Y + (float)projectile.height * 0.5f);
                num153 = num140 - vector13.X;
                num154 = num141 - vector13.Y;
                num155 = (float)Math.Sqrt((double)(num153 * num153 + num154 * num154));
                num155 = num152 / num155;
                num153 *= num155;
                num154 *= num155;
                num156 = 8;
                projectile.velocity.X = (projectile.velocity.X * (float)(num156 - 1) + num153) / (float)num156;
                projectile.velocity.Y = (projectile.velocity.Y * (float)(num156 - 1) + num154) / (float)num156;
            }
        }

        public override bool OnTileCollide(Vector2 oldVelocity)
        {
            Collision.HitTiles(projectile.position, projectile.velocity, projectile.width, projectile.height);
            Main.PlaySound(2, (int)projectile.position.X, (int)projectile.position.Y, 10);
            return true;
        }
    }
}
And, why would there be so many cases of a projectile AI? Why is this neccessary?
 
OK, this is all the codes of the porjectile.
Code:
            projectile.aiStyle = 1;
            aiType = -1;

            Main.PlaySound(2, (int)projectile.position.X, (int)projectile.position.Y, 10);
And, why would there be so many cases of a projectile AI? Why is this neccessary?

Delete projectile.aiStyle, you wrote your own ai code, but with aiStyle, the vanilla ai code is also being called. (and that is conflicting with your code.)

Delete aiType since you aren't using it.

Also, don't you want the ice crash sound? It's number 27 if you do.

It's not that there are "so many cases" but rather the most versatile option is to use floats for data bound to the projectile. In your code, we see ai[0] acting as a bool because it starts at 0 and we set it to 1 once we play the sound. In this case, the programmer decided that ai[0] would represent "soundPlayed". Also in the code you posted we see ai[1] acting as an int indexing into the NPC array that Terraria maintains to keep track of all the NPCs. In this case, ai[1] can be thought of as "npcTargetIndex". I also mentioned using it as a timer, that is another way to use them.

The point I'm getting at is it is up to the programmer to decide how to use these two float values that are available. ai[0] and ai[1] are ONLY used in the AI method, how they are used and what they represent are up to you, the programmer.
 
Delete projectile.aiStyle, you wrote your own ai code, but with aiStyle, the vanilla ai code is also being called. (and that is conflicting with your code.)

Delete aiType since you aren't using it.

Also, don't you want the ice crash sound? It's number 27 if you do.

It's not that there are "so many cases" but rather the most versatile option is to use floats for data bound to the projectile. In your code, we see ai[0] acting as a bool because it starts at 0 and we set it to 1 once we play the sound. In this case, the programmer decided that ai[0] would represent "soundPlayed". Also in the code you posted we see ai[1] acting as an int indexing into the NPC array that Terraria maintains to keep track of all the NPCs. In this case, ai[1] can be thought of as "npcTargetIndex". I also mentioned using it as a timer, that is another way to use them.

The point I'm getting at is it is up to the programmer to decide how to use these two float values that are available. ai[0] and ai[1] are ONLY used in the AI method, how they are used and what they represent are up to you, the programmer.
OK, well, now I understand why you called it "a faking boolean value".
Though still, why they're seperated almost all the time in vanilla codes? Won't it be good just to put them together? Since seperating means another setting, and for me, that's a trouble. In another word, why will it act that wired, if I don't seperate them?
 
OK, well, now I understand why you called it "a faking boolean value".
Though still, why they're seperated almost all the time in vanilla codes? Won't it be good just to put them together? Since seperating means another setting, and for me, that's a trouble. In another word, why will it act that wired, if I don't seperate them?
There are two floats in projectile.ai[] so that you can have your projectile do cool things, if there were only 1 float available, several of the cool projectiles you see in Terraria wouldn't be possible.

The reason it won't work if you combine them is because you are overwriting the info you needed to keep! (Each is literally just one number!) If we set ai[0] to 1 to make our code only play the sound once, but later on in the same method ai[0] is set back to 0 because there are no NPC around to target, the "Main.PlaySound" method will be called again on the next frame (the next time AI is called.)!

AI is called every frame.

Note that NPC have an ai[] array as well. They have 4 floats in the ai array available to them. This allows NPC to store more state information. Bosses can use that info to cycle through attack patterns and other things.
 
Code:
dust.scale -= 0.05f; // you can change this to fade faster
if (dust.scale < 0.15f) // and this too.
{
dust.active = false;
}
return false;
No that didnt work its still like tiny lines that are randomly rotated
[DOUBLEPOST=1451384020,1451383726][/DOUBLEPOST]It looks like this :
-//-\||--//-\|¨-|--//-\-||--/-/-\||--//--\||-
(dont know how to add image :))
 
No that didnt work its still like tiny lines that are randomly rotated
[DOUBLEPOST=1451384020,1451383726][/DOUBLEPOST]It looks like this :
-//-\||--//-\|¨-|--//-\-||--/-/-\||--//--\||-
(dont know how to add image :))
Oh, I didn't have a suitable image so I didn't notice. Alright, so just like scale, rotation is random by default as well.

You can do this while spawning the dust:
projectile.rotation = (float)Math.Atan2((double)projectile.velocity.Y, (double)projectile.velocity.X);
int dust = Dust.NewDust(projectile.position,projectile.width, projectile.height,mod.DustType("Laser"), 1f, 1f, 0);
Main.dust[dust].scale = 1f;
Main.dust[dust].rotation = projectile.rotation; // important part.
 
I tryed that and it wrote this error :
cannot use local variable 'dust' before it is declared
shoud i add something to "modname".cs ?
 
using System;
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ModLoader;

namespace :red:.Dusts
{
public class Laser : ModDust
{
public override void OnSpawn(Dust dust)
{
dust.noGravity = true;
dust.noLight = true;
}

public override bool Update(Dust dust)
{
dust.velocity *= 0.2f;
dust.rotation = (float)Math.Atan2((double)dust.velocity.Y, (double)dust.velocity.X);
if (dust.velocity.Length() < 0.001)
{
dust.active = false;
}
dust.scale -= 0.05f;
if (dust.scale < 0.15f)
{
dust.active = false;
}
return false;
Main.dust[dust].rotation = projectile.rotation;
Main.dust[dust].scale = 1f;
int dust = Dust.NewDust(projectile.position,projectile.width,projectile.height,mod.DustType("Laser"),1f,1f,0);
}
}
}
 
using System;
using Microsoft.Xna.Framework;
using Terraria;
using Terraria.ModLoader;

namespace :red:.Dusts
{
public class Laser : ModDust
{
public override void OnSpawn(Dust dust)
{
dust.noGravity = true;
dust.noLight = true;
}

public override bool Update(Dust dust)
{
dust.velocity *= 0.2f;
dust.rotation = (float)Math.Atan2((double)dust.velocity.Y, (double)dust.velocity.X);
if (dust.velocity.Length() < 0.001)
{
dust.active = false;
}
dust.scale -= 0.05f;
if (dust.scale < 0.15f)
{
dust.active = false;
}
return false;
Main.dust[dust].rotation = projectile.rotation;
Main.dust[dust].scale = 1f;
int dust = Dust.NewDust(projectile.position,projectile.width,projectile.height,mod.DustType("Laser"),1f,1f,0);
}
}
}
Whoa, what? you are mixing the Dust update code with the Projectile.AI code I gave you.

This is your new ModDust.Update:
Code:
        public override bool Update(Dust dust)
        {
            dust.scale -= 0.05f;
            if (dust.scale < 0.15f)
            {
                dust.active = false;
            }
            return false;
        }

and this is your new ModProjectile.AI:
Code:
        public override void AI()
        {
            projectile.velocity.Y += projectile.ai[0];
            projectile.rotation = (float)Math.Atan2((double)projectile.velocity.Y, (double)projectile.velocity.X);
            int dust = Dust.NewDust(projectile.position, projectile.width, projectile.height, mod.DustType("Laser"), 1f, 1f, 0);
            Main.dust[dust].scale = 1f;
            Main.dust[dust].rotation = projectile.rotation; // important part.
        }
 
There are two floats in projectile.ai[] so that you can have your projectile do cool things, if there were only 1 float available, several of the cool projectiles you see in Terraria wouldn't be possible.

The reason it won't work if you combine them is because you are overwriting the info you needed to keep! (Each is literally just one number!) If we set ai[0] to 1 to make our code only play the sound once, but later on in the same method ai[0] is set back to 0 because there are no NPC around to target, the "Main.PlaySound" method will be called again on the next frame (the next time AI is called.)!

AI is called every frame.

Note that NPC have an ai[] array as well. They have 4 floats in the ai array available to them. This allows NPC to store more state information. Bosses can use that info to cycle through attack patterns and other things.
Okay, now I understand how it works a little bit more, but still a lot of questions available. If the 1f and 0f are two different, independent cases of ai of a projectile. And I can use them to save infos as my wish(although I don't even know what my wish is!). But as you said, AI hook is called every frame once a projectile is created. In this case, why would the sound will only be called once, while the hooming codes will be called consitently?
Since I had noticed that if the chlorophyte bullet's AI codes were not added, this problem wouldn't occrur. I guess it's the chlorophyte bullet's AI codes that has forced the projectile to act in every single frame? Or this is precisely the default of AI hook?
Besides, what's the difference between PreAI and AI?

Edit: I'm really confused at this point, and I think understanding this AI hook will be a huge help to my making Chain Lighting. Please give a quick reply if you got time!:)jopojelly... I don't know how to @ ya. Hope you can see this.
 
Last edited:
Back
Top Bottom