tModLoader Why does my boss keep floating up?

JakubDuC

Terrarian
No matter what I do my boss floats up. I've removed all indications of velocity.y of just velocity that might be the issue but yet the problem persists.
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Terraria;
using Terraria.Audio;
using Terraria.DataStructures;
using Terraria.GameContent.Bestiary;
using Terraria.GameContent.ItemDropRules;
using Terraria.ID;
using Terraria.ModLoader;
using SorcerersandElementals.Content.BossBars;
using SorcerersandElementals.Content.Items.Consumables;
using SorcerersandElementals.Content.PLayerUI;
using SorcerersandElementals.Content.Elements.Radiant.Spells;
using SorcerersandElementals.Content.Projectiles.Boss.KingMagnusProj;
using SorcerersandElementals.Common;


namespace SorcerersandElementals.Content.NPCs.Bosses.KingMagnus
{
[AutoloadBossHead]
public class KingMagnus : ModNPC
{
public int patternType = 1;
public bool SecondStage
{
get => NPC.ai[0] == 1f;
set => NPC.ai[0] = value ? 1f : 0f;
}





public bool SpawnedMinions
{
get => NPC.localAI[0] == 1f;
set => NPC.localAI[0] = value ? 1f : 0f;
}

private const int FirstStageTimerMax = 90;

public ref float FirstStageTimer => ref NPC.localAI[1];

public ref float RemainingShields => ref NPC.localAI[2];

public ref float SecondStageTimer_SpawnEyes => ref NPC.localAI[3];

public static int MinionType()
{
return (NPCID.LavaSlime);

}

public int getPattern()
{
return patternType;
}

public void patternTypes()
{
if (Main.expertMode)
{
patternType = 1;
}
if (Main.masterMode) patternType = 3;

if (Main.getGoodWorld) patternType = 2;
}

public void enrage()
{
patternType = 2; // super fast projectiles + 16 projectiles every 4 loops instead of 4
}


public static int MinionCount()
{
int count = 4;

if (Main.expertMode)
{
count += 3; // Increase by 3 if expert or master mode, balanced unlike that below

}

if (Main.getGoodWorld)
{
count += 50; // Increase by 50 if using the "For The Worthy" seed, because :red: them, they will suffer
}

return count;
}

public override void SetStaticDefaults()
{
DisplayName.SetDefault("King Magnus, Lord of the Underworld");
Main.npcFrameCount[Type] = 1;

// Add this in for bosses that have a summon item, requires corresponding code in the item
NPCID.Sets.MPAllowedEnemies[Type] = true;
// Automatically group with other bosses
NPCID.Sets.BossBestiaryPriority.Add(Type);

// Specify the debuffs it is immune to
NPCDebuffImmunityData debuffData = new NPCDebuffImmunityData
{
SpecificallyImmuneTo = new int[] {
BuffID.Poisoned,
BuffID.OnFire,
BuffID.OnFire3,
BuffID.Confused // Most NPCs have this
}
};
NPCID.Sets.DebuffImmunitySets.Add(Type, debuffData);

// Influences how the NPC looks in the Bestiary
NPCID.Sets.NPCBestiaryDrawModifiers drawModifiers = new NPCID.Sets.NPCBestiaryDrawModifiers(0)
{
CustomTexturePath = "SorcerersandElementals/Assets/Textures/Bestiary/KingMagnus_Preview",
PortraitScale = 0.6f, // Portrait refers to the full picture when clicking on the icon in the bestiary
PortraitPositionYOverride = 0f,
};
NPCID.Sets.NPCBestiaryDrawOffset.Add(Type, drawModifiers);
}

public override void SetDefaults()
{
ModMain main = new ModMain();
NPC.width = 167;
NPC.height = 107;
NPC.damage = 354;
NPC.defense = 54;

NPC.lifeMax = 7000000;
NPC.HitSound = SoundID.NPCHit1;
NPC.DeathSound = SoundID.NPCDeath1;
NPC.knockBackResist = 0f;
NPC.noGravity = true;
NPC.noTileCollide = false;
NPC.SpawnWithHigherTime(30);
NPC.value = Item.buyPrice(platinum: 15, gold: 20);
NPC.boss = true;
NPC.npcSlots = 10f; // Take up open spawn slots, preventing random NPCs from spawning during the fight

// Don't set immunities like this as of 1.4:
// NPC.buffImmune[BuffID.Confused] = true;
// immunities are handled via dictionaries through NPCID.Sets.DebuffImmunitySets

// Custom AI, 0 is "bound town NPC" AI which slows the NPC down and changes sprite orientation towards the target
NPC.aiStyle = 0;

// Custom boss bar
NPC.BossBar = ModContent.GetInstance<BossBars.MinionBossBossBar>();

// The following code assigns a music track to the boss in a simple way.
if (!Main.dedServ)
{
Music = MusicLoader.GetMusicSlot(Mod, "Assets/Music/EarRapeKingMagnus"); // ear rape for music
}

}
public override void SetBestiary(BestiaryDatabase database, BestiaryEntry bestiaryEntry)
{
// Sets the description of this NPC that is listed in the bestiary
bestiaryEntry.Info.AddRange(new List<IBestiaryInfoElement> {
new MoonLordPortraitBackgroundProviderBestiaryInfoElement(), // Plain black background
new FlavorTextBestiaryInfoElement("The almighty lord and king of the Underworld. Mighty god of magma and death. King Magnus is the terryfying right hand man of the Master of Universes. He is the keeper of the Underworld, a formidible foe.")
});
}

public override void ModifyNPCLoot(NPCLoot npcLoot)
{
// Do NOT misuse the ModifyNPCLoot and OnKill hooks: the former is only used for registering drops, the latter for everything else

// Add the treasure bag using ItemDropRule.BossBag (automatically checks for expert mode)
npcLoot.Add(ItemDropRule.BossBag(ModContent.ItemType<KingMagnusBossBag>()));

npcLoot.Add(ItemDropRule.Common(ItemID.SuperHealingPotion, 1, 40, 56));

// Trophies are spawned with 1/10 chance


// ItemDropRule.MasterModeCommonDrop for the relic


// ItemDropRule.MasterModeDropOnAllPlayers for the pet


// All our drops here are based on "not expert", meaning we use .OnSuccess() to add them into the rule, which then gets added
LeadingConditionRule notExpertRule = new LeadingConditionRule(new Conditions.NotExpert());
notExpertRule.OnSuccess(ItemDropRule.Common(ModContent.ItemType<KingMagnusBossBag>()));
notExpertRule.OnSuccess(ItemDropRule.Common(ItemID.SuperHealingPotion, 1, 40, 56));

// Notice we use notExpertRule.OnSuccess instead of npcLoot.Add so it only applies in normal mode
// Boss masks are spawned with 1/7 chance


// This part is not required for a boss and is just showcasing some advanced stuff you can do with drop rules to control how items spawn
// We make 12-15 ExampleItems spawn randomly in all directions, like the lunar pillar fragments. Hereby we need the DropOneByOne rule,
// which requires these parameters to be defined




// Finally add the leading rule
npcLoot.Add(notExpertRule);
}

public override void OnKill()
{
// This sets downedMinionBoss to true, and if it was false before, it initiates a lantern night
NPC.SetEventFlagCleared(ref DownedBossSystem.downedKingMagnus, -1);

// Since this hook is only ran in singleplayer and serverside, we would have to sync it manually.
// Thankfully, vanilla sends the MessageID.WorldData packet if a BOSS was killed automatically, shortly after this hook is ran

// If your NPC is not a boss and you need to sync the world (which includes ModSystem, check DownedBossSystem), use this code:
/*
if (Main.netMode == NetmodeID.Server) {
NetMessage.SendData(MessageID.WorldData);
}
*/




Talk("You have defeated me... You shall die by the hands of my master!"); // says some random :red:
SoundEngine.PlaySound(SoundID.ForceRoarPitched); // sounds time!
SoundEngine.PlaySound(SoundID.Roar);


}
public override void BossLoot(ref string name, ref int potionType)
{
// Here you'd want to change the potion type that drops when the boss is defeated. Because this boss is early pre-hardmode, we keep it unchanged
// (Lesser Healing Potion). If you wanted to change it, simply write "potionType = ItemID.HealingPotion;" or any other potion type
}

public override bool CanHitPlayer(Player target, ref int cooldownSlot)
{
cooldownSlot = ImmunityCooldownID.Bosses; // use the boss immunity cooldown counter, to prevent ignoring boss attacks by taking damage from other sources
return true;
}

// public override void FindFrame(int frameHeight) {

// This NPC animates with a simple "go from start frame to final frame, and loop back to start frame" rule
// In this case: First stage: 0-1-2-0-1-2, Second stage: 3-4-5-3-4-5, 5 being "total frame count - 1"
// int startFrame = 0;
// int finalFrame = 6;

// if (SecondStage)
// {
// startFrame = 3;
// finalFrame = Main.npcFrameCount[NPC.type] - 1;
//
// if (NPC.frame.Y < startFrame * frameHeight)
// {
// // If we were animating the first stage frames and then switch to second stage, immediately change to the start frame of the second stage
// NPC.frame.Y = startFrame * frameHeight;
// }
// }

// int frameSpeed = 5;
// NPC.frameCounter += 0.5f;
// NPC.frameCounter += NPC.velocity.Length() / 10f; // Make the counter go faster with more movement speed
// if (NPC.frameCounter > frameSpeed)
// {
// NPC.frameCounter = 0;
// NPC.frame.Y += frameHeight;

// if (NPC.frame.Y > finalFrame * frameHeight)
// {
// NPC.frame.Y = startFrame * frameHeight;
// }
// }
// }

public override void HitEffect(int hitDirection, double damage)
{
// If the NPC dies, spawn gore and play a sound
if (Main.netMode == NetmodeID.Server)
{
// We don't want Mod.Find<ModGore> to run on servers as it will crash because gores are not loaded on servers
return;
}

if (NPC.life <= 0)
{
// These gores work by simply existing as a texture inside any folder which path contains "Gores/"
int backGoreType = Mod.Find<ModGore>("KingMagnus_Back").Type;
int frontGoreType = Mod.Find<ModGore>("KingMagnus_Front").Type;

var entitySource = NPC.GetSource_Death();

for (int i = 0; i < 2; i++)
{
Gore.NewGore(entitySource, NPC.position, new Vector2(Main.rand.Next(-6, 7), Main.rand.Next(-6, 7)), backGoreType);
Gore.NewGore(entitySource, NPC.position, new Vector2(Main.rand.Next(-6, 7), Main.rand.Next(-6, 7)), frontGoreType);
}

SoundEngine.PlaySound(SoundID.Roar, NPC.Center);


}
}

public int jumpTimer;
public bool jumping;
public int timer;
public int timer2 = 0;
public bool hasClimed = false;

public override void AI()
{
// This should almost always be the first code in AI() as it is responsible for finding the proper player target
// if (NPC.target < 0 || NPC.target == 255 || Main.player[NPC.target].dead || !Main.player[NPC.target].active)
// {
// NPC.TargetClosest();
// }


// Player player = Main.player[NPC.target];

// if (player.dead)
// {
// If the targeted player is dead, flee
// NPC.velocity.Y -= 0.04f;
// This method makes it so when the boss is in "despawn range" (outside of the screen), it despawns in 10 ticks
// NPC.EncourageDespawn(10);
// return;
// }

// SpawnMinions();

// CheckSecondStage();

// Be invulnerable during the first stage
// NPC.dontTakeDamage = !SecondStage;

// if (SecondStage)
// {
// DoSecondStage(player);
// }
// else
// {
// DoFirstStage(player);
// }
// }

float num741 = 1f;
bool flag95 = false;
bool flag103 = false;
NPC.aiAction = 0;
if (NPC.ai[3] == 0f && NPC.life > 0)
{
NPC.ai[3] = NPC.lifeMax;
}
if (NPC.localAI[3] == 0f && Main.netMode != 1)
{
NPC.ai[0] = -100f;
NPC.localAI[3] = 1f;
NPC.TargetClosest();
NPC.netUpdate = true;
}
if (Main.player[NPC.target].dead)
{
NPC.TargetClosest();
if (Main.player[NPC.target].dead)
{
NPC.timeLeft = 0;
if (Main.player[NPC.target].Center.X < NPC.Center.X)
{
NPC.direction = 1;
}
else
{
NPC.direction = -1;
}
}
}

if (!Main.player[NPC.target].dead && NPC.ai[2] >= 300f && NPC.ai[1] < 5f && NPC.velocity.Y == 0f)
{
NPC.ai[2] = 0f;
NPC.ai[0] = 0f;
NPC.ai[1] = 5f;
if (Main.netMode != 1)
{
NPC.TargetClosest(faceTarget: false);
Point point8 = NPC.Center.ToTileCoordinates();
Point point9 = Main.player[NPC.target].Center.ToTileCoordinates();
Vector2 vector165 = Main.player[NPC.target].Center - NPC.Center;
int num744 = 10;
int num745 = 0;
int num746 = 7;
int num747 = 0;
bool flag2 = false;
if (vector165.Length() > 2000f)
{
flag2 = true;
num747 = 100;
}
while (!flag2 && num747 < 100)
{
num747++;
int num748 = Main.rand.Next(point9.X - num744, point9.X + num744 + 1);
int num749 = Main.rand.Next(point9.Y - num744, point9.Y + 1);
if ((num749 >= point9.Y - num746 && num749 <= point9.Y + num746 && num748 >= point9.X - num746 && num748 <= point9.X + num746) || (num749 >= point8.Y - num745 && num749 <= point8.Y + num745 && num748 >= point8.X - num745 && num748 <= point8.X + num745) || Main.tile[num748, num749].IsHalfBlock)
{
continue;
}
int num751 = num749;
int num752 = 0;
if (Main.tile[num748, num751].IsHalfBlock && Main.tileSolid[Main.tile[num748, num751].TileType] && !Main.tileSolidTop[Main.tile[num748, num751].TileType])
{
num752 = 1;
}
else
{
for (; num752 < 150 && num751 + num752 < Main.maxTilesY; num752++)
{
int num753 = num751 + num752;
if (Main.tile[num748, num753].IsHalfBlock && Main.tileSolid[Main.tile[num748, num753].TileType] && !Main.tileSolidTop[Main.tile[num748, num753].TileType])
{
num752--;
break;
}
}
}
num749 += num752;
bool flag28 = true;
if (flag28 && Main.tile[num748, num749].TileType == TileID.Lavafall)
{
flag28 = false;
}
if (flag28 && !Collision.CanHitLine(NPC.Center, 0, 0, Main.player[NPC.target].Center, 0, 0))
{
flag28 = false;
}
if (flag28)
{
NPC.localAI[1] = num748 * 16 + 8;
NPC.localAI[2] = num749 * 16 + 16;
break;
}
}
if (num747 >= 100)
{
Vector2 bottom = Main.player[Player.FindClosest(NPC.position, NPC.width, NPC.height)].Bottom;
NPC.localAI[1] = bottom.X;
NPC.localAI[2] = bottom.Y;
}
}
}
if (!Collision.CanHitLine(NPC.Center, 0, 0, Main.player[NPC.target].Center, 0, 0))
{
NPC.ai[2] += 1f;
}
if (Math.Abs(NPC.Top.Y - Main.player[NPC.target].Bottom.Y) > 320f)
{
NPC.ai[2] += 1f;
}
Dust dust24;
if (NPC.ai[1] == 5f)
{
flag95 = true;
NPC.aiAction = 1;
NPC.ai[0] += 1f;
num741 = MathHelper.Clamp((60f - NPC.ai[0]) / 60f, 0f, 1f);
num741 = 0.5f + num741 * 0.5f;
if (NPC.ai[0] >= 60f)
{
flag103 = true;
}
if (NPC.ai[0] == 60f)
{
var entitySource = NPC.GetSource_FromAI();
Gore.NewGore(entitySource, NPC.Center + new Vector2(-40f, (0f - (float)NPC.height) / 2f), NPC.velocity, 734);
}
if (NPC.ai[0] >= 60f && Main.netMode != 1)
{
NPC.Bottom = new Vector2(NPC.localAI[1], NPC.localAI[2]);
NPC.ai[1] = 6f;
NPC.ai[0] = 0f;
NPC.netUpdate = true;
}
if (Main.netMode == 1 && NPC.ai[0] >= 120f)
{
NPC.ai[1] = 6f;
NPC.ai[0] = 0f;
}
if (!flag103)
{
for (int num755 = 0; num755 < 10; num755++)
{
int num756 = Dust.NewDust(NPC.position + Vector2.UnitX * -20f, NPC.width + 40, NPC.height, 4, NPC.velocity.X, NPC.velocity.Y, 150, new Color(78, 136, 255, 80), 2f);
Main.dust[num756].noGravity = true;
dust24 = Main.dust[num756];
dust24.velocity *= 0.5f;
}
}
}
else if (NPC.ai[1] == 6f)
{
flag95 = true;
NPC.aiAction = 0;
NPC.ai[0] += 1f;
num741 = MathHelper.Clamp(NPC.ai[0] / 30f, 0f, 1f);
num741 = 0.5f + num741 * 0.5f;
if (NPC.ai[0] >= 30f && Main.netMode != 1)
{
NPC.ai[1] = 0f;
NPC.ai[0] = 0f;
NPC.netUpdate = true;
NPC.TargetClosest();
}
if (Main.netMode == 1 && NPC.ai[0] >= 60f)
{
NPC.ai[1] = 0f;
NPC.ai[0] = 0f;
NPC.TargetClosest();
}
for (int num757 = 0; num757 < 10; num757++)
{
int num758 = Dust.NewDust(NPC.position + Vector2.UnitX * -20f, NPC.width + 40, NPC.height, 4, NPC.velocity.X, NPC.velocity.Y, 150, new Color(78, 136, 255, 80), 2f);
Main.dust[num758].noGravity = true;
dust24 = Main.dust[num758];
dust24.velocity *= 2f;
}
}
Player player = Main.player[NPC.target];
int timer3 = 0;

NPC.dontTakeDamage = (NPC.hide = flag103);

if (NPC.life == NPC.lifeMax / 2)
{

DoSecondStage(player);
NPC.immortal = true;
Immobilise();

Talk("You think you can defeat me?");
Talk("I have ascended the mortal realm!");
Talk("Behold my unlimited power!");

var sources = NPC.GetSource_FromAI();
Vector2 velocity = new Vector2(0, 0);

Projectile.NewProjectile(sources, NPC.position, velocity, ModContent.ProjectileType<MagmaGrenade>(), 500, 1f);

if (timer3 > 10)
{
NPC.immortal = false;
Projectile.NewProjectile(sources, NPC.position, velocity, ModContent.ProjectileType<MagmaGrenade>(), 500, 1f);
Projectile.NewProjectile(sources, NPC.position, velocity, ModContent.ProjectileType<MagmaGrenade>(), 500, 1f);
}
else timer3++;
}


Talk(NPC.lifeMax.ToString());

NPC.lifeMax = 700000;

if (timer < 6) timer++;
else if (timer > 6)
{
timer = 0;
JumpDrop(Main.player[NPC.target]);
}

if (jumping && jumpTimer < 6) jumpTimer++;
else if (jumpTimer < 6)
{
jumpTimer = 0;
land();
}

if (NPC.velocity.Y > player.velocity.Y + 0.25f)
{
NPC.velocity = new Vector2(0, 0);

}

if (NPC.Top.Y < player.Bottom.Y)
{
if (!hasClimed)
{
NPC.velocity.Y = player.velocity.Y + 0.25f;

if (timer2 > 2)
{
Immobilise();
}
else
timer2++;
hasClimed = true;
}


}

else if (NPC.Bottom.Y > player.Top.Y)
{
if (!hasClimed)
{
if (NPC.Bottom.Y > player.Top.Y) NPC.velocity.Y = player.velocity.Y - 0.25f;
else NPC.velocity.Y = 0;
}

}



if (NPC.velocity.Y == 0f)
{
NPC.velocity.X *= 0.8f;
if ((double)NPC.velocity.X > -0.1 && (double)NPC.velocity.X < 0.1)
{
NPC.velocity.X = 0f;
}
if (!flag95)
{

NPC.ai[0] += 2f;
if ((double)NPC.life < (double)NPC.lifeMax * 0.8)
{
NPC.ai[0] += 1f;
}
if ((double)NPC.life < (double)NPC.lifeMax * 0.6)
{
NPC.ai[0] += 1f;
}
if ((double)NPC.life < (double)NPC.lifeMax * 0.4)
{
NPC.ai[0] += 2f;
}
if ((double)NPC.life < (double)NPC.lifeMax * 0.2)
{
NPC.ai[0] += 3f;
}
if ((double)NPC.life < (double)NPC.lifeMax * 0.1)
{
NPC.ai[0] += 4f;
}
if (NPC.ai[0] >= 0f)
{
NPC.netUpdate = true;
NPC.TargetClosest();
if (NPC.ai[1] == 3f)
{
NPC.velocity.Y = -13f;
NPC.velocity.X += 3.5f * (float)NPC.direction;
NPC.ai[0] = -200f;
NPC.ai[1] = 0f;
}
else if (NPC.ai[1] == 2f)
{
NPC.velocity.Y = -6f;
NPC.velocity.X += 4.5f * (float)NPC.direction;
NPC.ai[0] = -120f;
NPC.ai[1] += 1f;
}
else
{
NPC.velocity.Y = -8f;
NPC.velocity.X += 4f * (float)NPC.direction;
NPC.ai[0] = -120f;
NPC.ai[1] += 1f;
}
}
else if (NPC.ai[0] >= -30f)
{
NPC.aiAction = 1;
}
}
}
else if (NPC.target < 255 && ((NPC.direction == 1 && NPC.velocity.X < 3f) || (NPC.direction == -1 && NPC.velocity.X > -3f)))
{
if ((NPC.direction == -1 && (double)NPC.velocity.X < 0.1) || (NPC.direction == 1 && (double)NPC.velocity.X > -0.1))
{
NPC.velocity.X += 0.2f * (float)NPC.direction;
}
else
{
NPC.velocity.X *= 0.93f;
}
}
int num759 = Dust.NewDust(NPC.position, NPC.width, NPC.height, 4, NPC.velocity.X, NPC.velocity.Y, 255, new Color(0, 80, 255, 80), NPC.scale * 1.2f);
Main.dust[num759].noGravity = true;
dust24 = Main.dust[num759];
dust24.velocity *= 0.5f;
if (NPC.life <= 0)
{
return;
}

if (Main.netMode == 1)
{
return;
}
int num763 = (int)((double)NPC.lifeMax * 0.05);
if (!((float)(NPC.life + num763) < NPC.ai[3]))
{
return;
}
NPC.ai[3] = NPC.life;
int num764 = Main.rand.Next(1, 4);
for (int num765 = 0; num765 < num764; num765++)
{
int x = (int)(NPC.position.X + (float)Main.rand.Next(NPC.width - 32));
int y = (int)(NPC.position.Y + (float)Main.rand.Next(NPC.height - 32));
int num766 = 1;
if (Main.expertMode && Main.rand.Next(4) == 0)
{
num766 = 535;
}
var source = Main.npc[num766].GetSource_FromAI();

int num768 = index;

SpawnMinions();

if (Main.netMode == 2 && num768 < 200)
{
NetMessage.SendData(23, -1, -1, null, num768);
}

}
}

public int index;
private void SpawnMinions()
{
if (SpawnedMinions)
{
// No point executing the code in this method again
return;
}

SpawnedMinions = true;

if (Main.netMode == NetmodeID.MultiplayerClient)
{
// Because we want to spawn minions, and minions are NPCs, we have to do this on the server (or singleplayer, "!= NetmodeID.MultiplayerClient" covers both)
// This means we also have to sync it after we spawned and set up the minion
return;
}

int count = MinionCount();
var entitySource = NPC.GetSource_FromAI();

Talk("My loyal minions will take care of you!");
Talk("My lava slimes! Charge him!");

for (int i = 0; i < count; i++)
{
index = NPC.NewNPC(entitySource, (int)NPC.Center.X, (int)NPC.Center.Y, NPCID.LavaSlime, NPC.whoAmI);
NPC minionNPC = Main.npc[index];

// Now that the minion is spawned, we need to prepare it with data that is necessary for it to work
// This is not required usually if you simply spawn NPCs, but because the minion is tied to the body, we need to pass this information to it



// Finally, syncing, only sync on server and if the NPC actually exists (Main.maxNPCs is the index of a dummy NPC, there is no point syncing it)
if (Main.netMode == NetmodeID.Server && index < Main.maxNPCs)
{
NetMessage.SendData(MessageID.SyncNPC, number: index);
}
}
}

private void CheckSecondStage()
{
if (SecondStage)
{
// No point checking if the NPC is already in its second stage
return;
}

float remainingShieldsSum = 0f;


// We reference this in the MinionBossBossBar
RemainingShields = remainingShieldsSum / MinionCount();


}

private void DoFirstStage(Player player)
{
// Each time the timer is 0, pick a random position a fixed distance away from the player but towards the opposite side
// The NPC moves directly towards it with fixed speed, while displaying its trajectory as a telegraph

FirstStageTimer++;
if (FirstStageTimer > FirstStageTimerMax)
{
FirstStageTimer = 0;
}

// Distance in pixels behind the player

if (FirstStageTimer == 0)
{


if (Main.netMode != NetmodeID.MultiplayerClient)
{
// Important multiplayer concideration: drastic change in behavior (that is also decided by randomness) like this requires
// to be executed on the server (or singleplayer) to keep the boss in sync

NPC.netUpdate = true;
}
}

// Move along the vector


// No damage during first phase


// Fade in based on remaining total minion life
NPC.alpha = (int)(RemainingShields * 255);


}

private int rippleCount = 3;
private int rippleSize = 5;
private int rippleSpeed = 15;
private float distortStrength = 100f;

private void DoSecondStage(Player player)
{




DoSecondStage_SpawnEyes(player);

NPC.damage = NPC.defDamage;

NPC.alpha = 0;


if (Main.netMode != NetmodeID.Server && !Terraria.Graphics.Effects.Filters.Scene["Shockwave"].IsActive())
{
Terraria.Graphics.Effects.Filters.Scene.Activate("Shockwave", NPC.Center).GetShader().UseColor(rippleCount, rippleSize, rippleSpeed).UseTargetPosition(NPC.Center);
}


if (Main.netMode != NetmodeID.Server && Terraria.Graphics.Effects.Filters.Scene["Shockwave"].IsActive())
{
float progress = (180f) / 60f;
Terraria.Graphics.Effects.Filters.Scene["Shockwave"].GetShader().UseProgress(progress).UseOpacity(distortStrength* (1 - progress / 3f));
}


}
private void DoSecondStage_SpawnEyes(Player player)
{
// At 100% health, spawn every 90 ticks
// Drops down until 33% health to spawn every 30 ticks
float timerMax = Utils.Clamp((float)NPC.life / NPC.lifeMax, 0.33f, 1f) * 90;

SecondStageTimer_SpawnEyes++;
if (SecondStageTimer_SpawnEyes > timerMax)
{
SecondStageTimer_SpawnEyes = 0;
if (Main.netMode != NetmodeID.Server && Terraria.Graphics.Effects.Filters.Scene["Shockwave"].IsActive())
{
Terraria.Graphics.Effects.Filters.Scene["Shockwave"].Deactivate();
}
}

if (NPC.HasValidTarget && SecondStageTimer_SpawnEyes == 0 && Main.netMode != NetmodeID.MultiplayerClient)
{
// Spawn projectile randomly below player, based on horizontal velocity to make kiting harder, starting velocity 1f upwards
// (The projectiles accelerate from their initial velocity)

float kitingOffsetX = Utils.Clamp(player.velocity.X * 16, -100, 100);
Vector2 position = player.Bottom + new Vector2(kitingOffsetX + Main.rand.Next(-100, 100), Main.rand.Next(50, 100));
Random random = new Random();

int damage = NPC.damage * 2;
patternTypes();

if (Main.expertMode)
{
patternType = 1;
}
if (Main.masterMode) patternType = 3;

if (Main.getGoodWorld) patternType = 2;


var entitySource = NPC.GetSource_FromAI();

MagmaBullet bullet = new MagmaBullet();

Vector2 playerpos = new Vector2(player.position.X, player.position.Y);
Vector2 velocity;
Vector2 startPos;



for (int i = 0; i < 4; i++)
{
if (patternType == 1)
{
Random rnd = new Random();
startPos.X = playerpos.X += rnd.Next(10);
startPos.Y = playerpos.X += rnd.Next(5);


velocity.X = playerpos.X += rnd.Next(2);
velocity.Y = playerpos.Y;
velocity *= 4;
velocity.Normalize();

Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);
}
if (patternType == 2)
{
Random rnd = new Random();
startPos.X = playerpos.X += rnd.Next(10);
startPos.Y = playerpos.X += rnd.Next(5);


velocity.X = playerpos.X += rnd.Next(2);
velocity.Y = playerpos.Y;
velocity *= 10;
velocity.Normalize();

Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);
Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);
Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);
Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);
}
if (patternType == 3)
{
Random rnd = new Random();
startPos.X = playerpos.X += rnd.Next(10);
startPos.Y = playerpos.X += rnd.Next(5);


velocity.X = playerpos.X += rnd.Next(2);
velocity.Y = playerpos.Y;
velocity *= 7;
velocity.Normalize();

Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);

}
if (patternType == 4)
{
Random rnd = new Random();
startPos.X = playerpos.X += rnd.Next(10);
startPos.Y = playerpos.X += rnd.Next(5);


velocity.X = playerpos.X += rnd.Next(2);
velocity.Y = playerpos.Y;
velocity *= 9;
velocity.Normalize();

Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);

}
}


}
}


public bool talked = false;

private void Talk(string message)
{
if (Main.netMode != NetmodeID.Server)
{

Main.NewText(message, 255, 111, 0);
}


}

private void JumpDrop(Player player)
{
Vector2I velocity;
velocity = new Vector2(player.position.X -= NPC.position.X * 0.25f, player.position.Y -= NPC.position.Y * 5.25f);
NPC.velocity = velocity;
jumping = true;
}

private void Immobilise()
{
NPC.velocity = new Vector2(0, 0);
}

private void land()
{
Immobilise();
for (int i = 0; i < 5; i++)
{
Dust.NewDust(NPC.position, 2, 1, DustID.Smoke, NPC.velocity.X, NPC.velocity.Y);

}
SoundEngine.PlaySound(SoundID.DD2_BetsyFireballImpact);
}
}
}
I have skimmed my code many times, however I just cannot find anything. This may be due to my coding inexperience. (Most of it is King Slime AI, some of it is handwritten) I was also the one that ported it to 1.4 so it may have been because I couldn't find the 1.4 spinoff of "Main.tile[].nactive();"
 
You have plenty of lines setting the NPC velocity still. For example:

if (!hasClimed)
{
NPC.velocity.Y = player.velocity.Y + 0.25f;

Also if you want to use a custom AI, set the aistyle to -1 instead of 0.
 
Here is my code:
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using Terraria;
using Terraria.Audio;
using Terraria.DataStructures;
using Terraria.GameContent.Bestiary;
using Terraria.GameContent.ItemDropRules;
using Terraria.ID;
using Terraria.ModLoader;
using SorcerersandElementals.Content.BossBars;
using SorcerersandElementals.Content.Items.Consumables;
using SorcerersandElementals.Content.PLayerUI;
using SorcerersandElementals.Content.Elements.Radiant.Spells;
using SorcerersandElementals.Content.Projectiles.Boss.KingMagnusProj;
using SorcerersandElementals.Common;


namespace SorcerersandElementals.Content.NPCs.Bosses.KingMagnus
{
[AutoloadBossHead]
public class KingMagnus : ModNPC
{
public int patternType = 1;
public bool SecondStage
{
get => NPC.ai[0] == 1f;
set => NPC.ai[0] = value ? 1f : 0f;
}





public bool SpawnedMinions
{
get => NPC.localAI[0] == 1f;
set => NPC.localAI[0] = value ? 1f : 0f;
}

private const int FirstStageTimerMax = 90;

public ref float FirstStageTimer => ref NPC.localAI[1];

public ref float RemainingShields => ref NPC.localAI[2];

public ref float SecondStageTimer_SpawnEyes => ref NPC.localAI[3];

public static int MinionType()
{
return (NPCID.LavaSlime);

}

public int getPattern()
{
return patternType;
}

public void patternTypes()
{
if (Main.expertMode)
{
patternType = 1;
}
if (Main.masterMode) patternType = 3;

if (Main.getGoodWorld) patternType = 2;
}

public void enrage()
{
patternType = 2; // super fast projectiles + 16 projectiles every 4 loops instead of 4
}


public static int MinionCount()
{
int count = 4;

if (Main.expertMode)
{
count += 3; // Increase by 3 if expert or master mode, balanced unlike that below

}

if (Main.getGoodWorld)
{
count += 50; // Increase by 50 if using the "For The Worthy" seed, because :red: them, they will suffer
}

return count;
}

public override void SetStaticDefaults()
{
DisplayName.SetDefault("King Magnus, Lord of the Underworld");
Main.npcFrameCount[Type] = 1;

// Add this in for bosses that have a summon item, requires corresponding code in the item
NPCID.Sets.MPAllowedEnemies[Type] = true;
// Automatically group with other bosses
NPCID.Sets.BossBestiaryPriority.Add(Type);

// Specify the debuffs it is immune to
NPCDebuffImmunityData debuffData = new NPCDebuffImmunityData
{
SpecificallyImmuneTo = new int[] {
BuffID.Poisoned,
BuffID.OnFire,
BuffID.OnFire3,
BuffID.Confused // Most NPCs have this
}
};
NPCID.Sets.DebuffImmunitySets.Add(Type, debuffData);

// Influences how the NPC looks in the Bestiary
NPCID.Sets.NPCBestiaryDrawModifiers drawModifiers = new NPCID.Sets.NPCBestiaryDrawModifiers(0)
{
CustomTexturePath = "SorcerersandElementals/Assets/Textures/Bestiary/KingMagnus_Preview",
PortraitScale = 0.6f, // Portrait refers to the full picture when clicking on the icon in the bestiary
PortraitPositionYOverride = 0f,
};
NPCID.Sets.NPCBestiaryDrawOffset.Add(Type, drawModifiers);
}

public override void SetDefaults()
{
ModMain main = new ModMain();
NPC.width = 167;
NPC.height = 107;
NPC.damage = 354;
NPC.defense = 54;

NPC.lifeMax = 7000000;
NPC.HitSound = SoundID.NPCHit1;
NPC.DeathSound = SoundID.NPCDeath1;
NPC.knockBackResist = 0f;
NPC.noGravity = true;
NPC.noTileCollide = false;
NPC.SpawnWithHigherTime(30);
NPC.value = Item.buyPrice(platinum: 15, gold: 20);
NPC.boss = true;
NPC.npcSlots = 10f; // Take up open spawn slots, preventing random NPCs from spawning during the fight

// Don't set immunities like this as of 1.4:
// NPC.buffImmune[BuffID.Confused] = true;
// immunities are handled via dictionaries through NPCID.Sets.DebuffImmunitySets

// Custom AI, 0 is "bound town NPC" AI which slows the NPC down and changes sprite orientation towards the target
NPC.aiStyle = -1;

// Custom boss bar
NPC.BossBar = ModContent.GetInstance<BossBars.MinionBossBossBar>();

// The following code assigns a music track to the boss in a simple way.
if (!Main.dedServ)
{
Music = MusicLoader.GetMusicSlot(Mod, "Assets/Music/EarRapeKingMagnus"); // ear rape for music
}

}
public override void SetBestiary(BestiaryDatabase database, BestiaryEntry bestiaryEntry)
{
// Sets the description of this NPC that is listed in the bestiary
bestiaryEntry.Info.AddRange(new List<IBestiaryInfoElement> {
new MoonLordPortraitBackgroundProviderBestiaryInfoElement(), // Plain black background
new FlavorTextBestiaryInfoElement("The almighty lord and king of the Underworld. Mighty god of magma and death. King Magnus is the terryfying right hand man of the Master of Universes. He is the keeper of the Underworld, a formidible foe.")
});
}

public override void ModifyNPCLoot(NPCLoot npcLoot)
{
// Do NOT misuse the ModifyNPCLoot and OnKill hooks: the former is only used for registering drops, the latter for everything else

// Add the treasure bag using ItemDropRule.BossBag (automatically checks for expert mode)
npcLoot.Add(ItemDropRule.BossBag(ModContent.ItemType<KingMagnusBossBag>()));

npcLoot.Add(ItemDropRule.Common(ItemID.SuperHealingPotion, 1, 40, 56));

// Trophies are spawned with 1/10 chance


// ItemDropRule.MasterModeCommonDrop for the relic


// ItemDropRule.MasterModeDropOnAllPlayers for the pet


// All our drops here are based on "not expert", meaning we use .OnSuccess() to add them into the rule, which then gets added
LeadingConditionRule notExpertRule = new LeadingConditionRule(new Conditions.NotExpert());
notExpertRule.OnSuccess(ItemDropRule.Common(ModContent.ItemType<KingMagnusBossBag>()));
notExpertRule.OnSuccess(ItemDropRule.Common(ItemID.SuperHealingPotion, 1, 40, 56));

// Notice we use notExpertRule.OnSuccess instead of npcLoot.Add so it only applies in normal mode
// Boss masks are spawned with 1/7 chance


// This part is not required for a boss and is just showcasing some advanced stuff you can do with drop rules to control how items spawn
// We make 12-15 ExampleItems spawn randomly in all directions, like the lunar pillar fragments. Hereby we need the DropOneByOne rule,
// which requires these parameters to be defined




// Finally add the leading rule
npcLoot.Add(notExpertRule);
}

public override void OnKill()
{
// This sets downedMinionBoss to true, and if it was false before, it initiates a lantern night
NPC.SetEventFlagCleared(ref DownedBossSystem.downedKingMagnus, -1);

// Since this hook is only ran in singleplayer and serverside, we would have to sync it manually.
// Thankfully, vanilla sends the MessageID.WorldData packet if a BOSS was killed automatically, shortly after this hook is ran

// If your NPC is not a boss and you need to sync the world (which includes ModSystem, check DownedBossSystem), use this code:
/*
if (Main.netMode == NetmodeID.Server) {
NetMessage.SendData(MessageID.WorldData);
}
*/




Talk("You have defeated me... You shall die by the hands of my master!"); // says some random :red:
SoundEngine.PlaySound(SoundID.ForceRoarPitched); // sounds time!
SoundEngine.PlaySound(SoundID.Roar);


}
public override void BossLoot(ref string name, ref int potionType)
{
// Here you'd want to change the potion type that drops when the boss is defeated. Because this boss is early pre-hardmode, we keep it unchanged
// (Lesser Healing Potion). If you wanted to change it, simply write "potionType = ItemID.HealingPotion;" or any other potion type
}

public override bool CanHitPlayer(Player target, ref int cooldownSlot)
{
cooldownSlot = ImmunityCooldownID.Bosses; // use the boss immunity cooldown counter, to prevent ignoring boss attacks by taking damage from other sources
return true;
}

// public override void FindFrame(int frameHeight) {

// This NPC animates with a simple "go from start frame to final frame, and loop back to start frame" rule
// In this case: First stage: 0-1-2-0-1-2, Second stage: 3-4-5-3-4-5, 5 being "total frame count - 1"
// int startFrame = 0;
// int finalFrame = 6;

// if (SecondStage)
// {
// startFrame = 3;
// finalFrame = Main.npcFrameCount[NPC.type] - 1;
//
// if (NPC.frame.Y < startFrame * frameHeight)
// {
// // If we were animating the first stage frames and then switch to second stage, immediately change to the start frame of the second stage
// NPC.frame.Y = startFrame * frameHeight;
// }
// }

// int frameSpeed = 5;
// NPC.frameCounter += 0.5f;
// NPC.frameCounter += NPC.velocity.Length() / 10f; // Make the counter go faster with more movement speed
// if (NPC.frameCounter > frameSpeed)
// {
// NPC.frameCounter = 0;
// NPC.frame.Y += frameHeight;

// if (NPC.frame.Y > finalFrame * frameHeight)
// {
// NPC.frame.Y = startFrame * frameHeight;
// }
// }
// }

public override void HitEffect(int hitDirection, double damage)
{
// If the NPC dies, spawn gore and play a sound
if (Main.netMode == NetmodeID.Server)
{
// We don't want Mod.Find<ModGore> to run on servers as it will crash because gores are not loaded on servers
return;
}

if (NPC.life <= 0)
{
// These gores work by simply existing as a texture inside any folder which path contains "Gores/"
int backGoreType = Mod.Find<ModGore>("KingMagnus_Back").Type;
int frontGoreType = Mod.Find<ModGore>("KingMagnus_Front").Type;

var entitySource = NPC.GetSource_Death();

for (int i = 0; i < 2; i++)
{
Gore.NewGore(entitySource, NPC.position, new Vector2(Main.rand.Next(-6, 7), Main.rand.Next(-6, 7)), backGoreType);
Gore.NewGore(entitySource, NPC.position, new Vector2(Main.rand.Next(-6, 7), Main.rand.Next(-6, 7)), frontGoreType);
}

SoundEngine.PlaySound(SoundID.Roar, NPC.Center);


}
}

public int jumpTimer;
public bool jumping;
public int timer;
public int timer2 = 0;
public bool hasClimed = false;

public override void AI()
{
// This should almost always be the first code in AI() as it is responsible for finding the proper player target
// if (NPC.target < 0 || NPC.target == 255 || Main.player[NPC.target].dead || !Main.player[NPC.target].active)
// {
// NPC.TargetClosest();
// }


// Player player = Main.player[NPC.target];

// if (player.dead)
// {
// If the targeted player is dead, flee
// NPC.velocity.Y -= 0.04f;
// This method makes it so when the boss is in "despawn range" (outside of the screen), it despawns in 10 ticks
// NPC.EncourageDespawn(10);
// return;
// }

// SpawnMinions();

// CheckSecondStage();

// Be invulnerable during the first stage
// NPC.dontTakeDamage = !SecondStage;

// if (SecondStage)
// {
// DoSecondStage(player);
// }
// else
// {
// DoFirstStage(player);
// }
// }

float num741 = 1f;
bool flag95 = false;
bool flag103 = false;
NPC.aiAction = 0;
if (NPC.ai[3] == 0f && NPC.life > 0)
{
NPC.ai[3] = NPC.lifeMax;
}
if (NPC.localAI[3] == 0f && Main.netMode != 1)
{
NPC.ai[0] = -100f;
NPC.localAI[3] = 1f;
NPC.TargetClosest();
NPC.netUpdate = true;
}
if (Main.player[NPC.target].dead)
{
NPC.TargetClosest();
if (Main.player[NPC.target].dead)
{
NPC.timeLeft = 0;
if (Main.player[NPC.target].Center.X < NPC.Center.X)
{
NPC.direction = 1;
}
else
{
NPC.direction = -1;
}
}
}

if (!Main.player[NPC.target].dead && NPC.ai[2] >= 300f && NPC.ai[1] < 5f && NPC.velocity.Y == 0f)
{
NPC.ai[2] = 0f;
NPC.ai[0] = 0f;
NPC.ai[1] = 5f;
if (Main.netMode != 1)
{
NPC.TargetClosest(faceTarget: false);
Point point8 = NPC.Center.ToTileCoordinates();
Point point9 = Main.player[NPC.target].Center.ToTileCoordinates();
Vector2 vector165 = Main.player[NPC.target].Center - NPC.Center;
int num744 = 10;
int num745 = 0;
int num746 = 7;
int num747 = 0;
bool flag2 = false;
if (vector165.Length() > 2000f)
{
flag2 = true;
num747 = 100;
}
while (!flag2 && num747 < 100)
{
num747++;
int num748 = Main.rand.Next(point9.X - num744, point9.X + num744 + 1);
int num749 = Main.rand.Next(point9.Y - num744, point9.Y + 1);
if ((num749 >= point9.Y - num746 && num749 <= point9.Y + num746 && num748 >= point9.X - num746 && num748 <= point9.X + num746) || (num749 >= point8.Y - num745 && num749 <= point8.Y + num745 && num748 >= point8.X - num745 && num748 <= point8.X + num745) || Main.tile[num748, num749].IsHalfBlock)
{
continue;
}
int num751 = num749;
int num752 = 0;
if (Main.tile[num748, num751].IsHalfBlock && Main.tileSolid[Main.tile[num748, num751].TileType] && !Main.tileSolidTop[Main.tile[num748, num751].TileType])
{
num752 = 1;
}
else
{
for (; num752 < 150 && num751 + num752 < Main.maxTilesY; num752++)
{
int num753 = num751 + num752;
if (Main.tile[num748, num753].IsHalfBlock && Main.tileSolid[Main.tile[num748, num753].TileType] && !Main.tileSolidTop[Main.tile[num748, num753].TileType])
{
num752--;
break;
}
}
}
num749 += num752;
bool flag28 = true;
if (flag28 && Main.tile[num748, num749].TileType == TileID.Lavafall)
{
flag28 = false;
}
if (flag28 && !Collision.CanHitLine(NPC.Center, 0, 0, Main.player[NPC.target].Center, 0, 0))
{
flag28 = false;
}
if (flag28)
{
NPC.localAI[1] = num748 * 16 + 8;
NPC.localAI[2] = num749 * 16 + 16;
break;
}
}
if (num747 >= 100)
{
Vector2 bottom = Main.player[Player.FindClosest(NPC.position, NPC.width, NPC.height)].Bottom;
NPC.localAI[1] = bottom.X;
NPC.localAI[2] = bottom.Y;
}
}
}
if (!Collision.CanHitLine(NPC.Center, 0, 0, Main.player[NPC.target].Center, 0, 0))
{
NPC.ai[2] += 1f;
}
if (Math.Abs(NPC.Top.Y - Main.player[NPC.target].Bottom.Y) > 320f)
{
NPC.ai[2] += 1f;
}
Dust dust24;
if (NPC.ai[1] == 5f)
{
flag95 = true;
NPC.aiAction = 1;
NPC.ai[0] += 1f;
num741 = MathHelper.Clamp((60f - NPC.ai[0]) / 60f, 0f, 1f);
num741 = 0.5f + num741 * 0.5f;
if (NPC.ai[0] >= 60f)
{
flag103 = true;
}
if (NPC.ai[0] == 60f)
{
var entitySource = NPC.GetSource_FromAI();
Gore.NewGore(entitySource, NPC.Center + new Vector2(-40f, (0f - (float)NPC.height) / 2f), NPC.velocity, 734);
}
if (NPC.ai[0] >= 60f && Main.netMode != 1)
{
NPC.Bottom = new Vector2(NPC.localAI[1], NPC.localAI[2]);
NPC.ai[1] = 6f;
NPC.ai[0] = 0f;
NPC.netUpdate = true;
}
if (Main.netMode == 1 && NPC.ai[0] >= 120f)
{
NPC.ai[1] = 6f;
NPC.ai[0] = 0f;
}
if (!flag103)
{
for (int num755 = 0; num755 < 10; num755++)
{
int num756 = Dust.NewDust(NPC.position + Vector2.UnitX * -20f, NPC.width + 40, NPC.height, 4, NPC.velocity.X, NPC.velocity.Y, 150, new Color(78, 136, 255, 80), 2f);
Main.dust[num756].noGravity = true;
dust24 = Main.dust[num756];
dust24.velocity *= 0.5f;
}
}
}
else if (NPC.ai[1] == 6f)
{
flag95 = true;
NPC.aiAction = 0;
NPC.ai[0] += 1f;
num741 = MathHelper.Clamp(NPC.ai[0] / 30f, 0f, 1f);
num741 = 0.5f + num741 * 0.5f;
if (NPC.ai[0] >= 30f && Main.netMode != 1)
{
NPC.ai[1] = 0f;
NPC.ai[0] = 0f;
NPC.netUpdate = true;
NPC.TargetClosest();
}
if (Main.netMode == 1 && NPC.ai[0] >= 60f)
{
NPC.ai[1] = 0f;
NPC.ai[0] = 0f;
NPC.TargetClosest();
}
for (int num757 = 0; num757 < 10; num757++)
{
int num758 = Dust.NewDust(NPC.position + Vector2.UnitX * -20f, NPC.width + 40, NPC.height, 4, NPC.velocity.X, NPC.velocity.Y, 150, new Color(78, 136, 255, 80), 2f);
Main.dust[num758].noGravity = true;
dust24 = Main.dust[num758];
dust24.velocity *= 2f;
}
}
Player player = Main.player[NPC.target];
int timer3 = 0;

NPC.dontTakeDamage = (NPC.hide = flag103);

if (NPC.life == NPC.lifeMax / 2)
{

DoSecondStage(player);
NPC.immortal = true;
Immobilise();

Talk("You think you can defeat me?");
Talk("I have ascended the mortal realm!");
Talk("Behold my unlimited power!");

var sources = NPC.GetSource_FromAI();
Vector2 velocity = new Vector2(0, 0);

Projectile.NewProjectile(sources, NPC.position, velocity, ModContent.ProjectileType<MagmaGrenade>(), 500, 1f);

if (timer3 > 10)
{
NPC.immortal = false;
Projectile.NewProjectile(sources, NPC.position, velocity, ModContent.ProjectileType<MagmaGrenade>(), 500, 1f);
Projectile.NewProjectile(sources, NPC.position, velocity, ModContent.ProjectileType<MagmaGrenade>(), 500, 1f);
}
else timer3++;
}


Talk(NPC.lifeMax.ToString());

NPC.lifeMax = 700000;





if (NPC.velocity.Y == 0f)
{
NPC.velocity.X *= 0.8f;
if ((double)NPC.velocity.X > -0.1 && (double)NPC.velocity.X < 0.1)
{
NPC.velocity.X = 0f;
}
if (!flag95)
{

NPC.ai[0] += 2f;
if ((double)NPC.life < (double)NPC.lifeMax * 0.8)
{
NPC.ai[0] += 1f;
}
if ((double)NPC.life < (double)NPC.lifeMax * 0.6)
{
NPC.ai[0] += 1f;
}
if ((double)NPC.life < (double)NPC.lifeMax * 0.4)
{
NPC.ai[0] += 2f;
}
if ((double)NPC.life < (double)NPC.lifeMax * 0.2)
{
NPC.ai[0] += 3f;
}
if ((double)NPC.life < (double)NPC.lifeMax * 0.1)
{
NPC.ai[0] += 4f;
}
if (NPC.ai[0] >= 0f)
{
NPC.netUpdate = true;
NPC.TargetClosest();
if (NPC.ai[1] == 3f)
{

NPC.velocity.X += 3.5f * (float)NPC.direction;
NPC.ai[0] = -200f;
NPC.ai[1] = 0f;
}
else if (NPC.ai[1] == 2f)
{

NPC.velocity.X += 4.5f * (float)NPC.direction;
NPC.ai[0] = -120f;
NPC.ai[1] += 1f;
}
else
{

NPC.velocity.X += 4f * (float)NPC.direction;
NPC.ai[0] = -120f;
NPC.ai[1] += 1f;
}
}
else if (NPC.ai[0] >= -30f)
{
NPC.aiAction = 1;
}
}
}
else if (NPC.target < 255 && ((NPC.direction == 1 && NPC.velocity.X < 3f) || (NPC.direction == -1 && NPC.velocity.X > -3f)))
{
if ((NPC.direction == -1 && (double)NPC.velocity.X < 0.1) || (NPC.direction == 1 && (double)NPC.velocity.X > -0.1))
{
NPC.velocity.X += 0.2f * (float)NPC.direction;
}
else
{
NPC.velocity.X *= 0.93f;
}
}
int num759 = Dust.NewDust(NPC.position, NPC.width, NPC.height, 4, NPC.velocity.X, NPC.velocity.Y, 255, new Color(0, 80, 255, 80), NPC.scale * 1.2f);
Main.dust[num759].noGravity = true;
dust24 = Main.dust[num759];
dust24.velocity *= 0.5f;
if (NPC.life <= 0)
{
return;
}

if (Main.netMode == 1)
{
return;
}
int num763 = (int)((double)NPC.lifeMax * 0.05);
if (!((float)(NPC.life + num763) < NPC.ai[3]))
{
return;
}
NPC.ai[3] = NPC.life;
int num764 = Main.rand.Next(1, 4);
for (int num765 = 0; num765 < num764; num765++)
{
int x = (int)(NPC.position.X + (float)Main.rand.Next(NPC.width - 32));
int y = (int)(NPC.position.Y + (float)Main.rand.Next(NPC.height - 32));
int num766 = 1;
if (Main.expertMode && Main.rand.Next(4) == 0)
{
num766 = 535;
}
var source = Main.npc[num766].GetSource_FromAI();

int num768 = index;

SpawnMinions();

if (Main.netMode == 2 && num768 < 200)
{
NetMessage.SendData(23, -1, -1, null, num768);
}

}
}

public int index;
private void SpawnMinions()
{
if (SpawnedMinions)
{
// No point executing the code in this method again
return;
}

SpawnedMinions = true;

if (Main.netMode == NetmodeID.MultiplayerClient)
{
// Because we want to spawn minions, and minions are NPCs, we have to do this on the server (or singleplayer, "!= NetmodeID.MultiplayerClient" covers both)
// This means we also have to sync it after we spawned and set up the minion
return;
}

int count = MinionCount();
var entitySource = NPC.GetSource_FromAI();

Talk("My loyal minions will take care of you!");
Talk("My lava slimes! Charge him!");

for (int i = 0; i < count; i++)
{
index = NPC.NewNPC(entitySource, (int)NPC.Center.X, (int)NPC.Center.Y, NPCID.LavaSlime, NPC.whoAmI);
NPC minionNPC = Main.npc[index];

// Now that the minion is spawned, we need to prepare it with data that is necessary for it to work
// This is not required usually if you simply spawn NPCs, but because the minion is tied to the body, we need to pass this information to it



// Finally, syncing, only sync on server and if the NPC actually exists (Main.maxNPCs is the index of a dummy NPC, there is no point syncing it)
if (Main.netMode == NetmodeID.Server && index < Main.maxNPCs)
{
NetMessage.SendData(MessageID.SyncNPC, number: index);
}
}
}

private void CheckSecondStage()
{
if (SecondStage)
{
// No point checking if the NPC is already in its second stage
return;
}

float remainingShieldsSum = 0f;


// We reference this in the MinionBossBossBar
RemainingShields = remainingShieldsSum / MinionCount();


}

private void DoFirstStage(Player player)
{
// Each time the timer is 0, pick a random position a fixed distance away from the player but towards the opposite side
// The NPC moves directly towards it with fixed speed, while displaying its trajectory as a telegraph

FirstStageTimer++;
if (FirstStageTimer > FirstStageTimerMax)
{
FirstStageTimer = 0;
}

// Distance in pixels behind the player

if (FirstStageTimer == 0)
{


if (Main.netMode != NetmodeID.MultiplayerClient)
{
// Important multiplayer concideration: drastic change in behavior (that is also decided by randomness) like this requires
// to be executed on the server (or singleplayer) to keep the boss in sync

NPC.netUpdate = true;
}
}

// Move along the vector


// No damage during first phase


// Fade in based on remaining total minion life
NPC.alpha = (int)(RemainingShields * 255);


}

private int rippleCount = 3;
private int rippleSize = 5;
private int rippleSpeed = 15;
private float distortStrength = 100f;

private void DoSecondStage(Player player)
{




DoSecondStage_SpawnEyes(player);

NPC.damage = NPC.defDamage;

NPC.alpha = 0;


if (Main.netMode != NetmodeID.Server && !Terraria.Graphics.Effects.Filters.Scene["Shockwave"].IsActive())
{
Terraria.Graphics.Effects.Filters.Scene.Activate("Shockwave", NPC.Center).GetShader().UseColor(rippleCount, rippleSize, rippleSpeed).UseTargetPosition(NPC.Center);
}


if (Main.netMode != NetmodeID.Server && Terraria.Graphics.Effects.Filters.Scene["Shockwave"].IsActive())
{
float progress = (180f) / 60f;
Terraria.Graphics.Effects.Filters.Scene["Shockwave"].GetShader().UseProgress(progress).UseOpacity(distortStrength* (1 - progress / 3f));
}


}
private void DoSecondStage_SpawnEyes(Player player)
{
// At 100% health, spawn every 90 ticks
// Drops down until 33% health to spawn every 30 ticks
float timerMax = Utils.Clamp((float)NPC.life / NPC.lifeMax, 0.33f, 1f) * 90;

SecondStageTimer_SpawnEyes++;
if (SecondStageTimer_SpawnEyes > timerMax)
{
SecondStageTimer_SpawnEyes = 0;
if (Main.netMode != NetmodeID.Server && Terraria.Graphics.Effects.Filters.Scene["Shockwave"].IsActive())
{
Terraria.Graphics.Effects.Filters.Scene["Shockwave"].Deactivate();
}
}

if (NPC.HasValidTarget && SecondStageTimer_SpawnEyes == 0 && Main.netMode != NetmodeID.MultiplayerClient)
{
// Spawn projectile randomly below player, based on horizontal velocity to make kiting harder, starting velocity 1f upwards
// (The projectiles accelerate from their initial velocity)

float kitingOffsetX = Utils.Clamp(player.velocity.X * 16, -100, 100);
Vector2 position = player.Bottom + new Vector2(kitingOffsetX + Main.rand.Next(-100, 100), Main.rand.Next(50, 100));
Random random = new Random();

int damage = NPC.damage * 2;
patternTypes();

if (Main.expertMode)
{
patternType = 1;
}
if (Main.masterMode) patternType = 3;

if (Main.getGoodWorld) patternType = 2;


var entitySource = NPC.GetSource_FromAI();

MagmaBullet bullet = new MagmaBullet();

Vector2 playerpos = new Vector2(player.position.X, player.position.Y);
Vector2 velocity;
Vector2 startPos;



for (int i = 0; i < 4; i++)
{
if (patternType == 1)
{
Random rnd = new Random();
startPos.X = playerpos.X += rnd.Next(10);
startPos.Y = playerpos.X += rnd.Next(5);


velocity.X = playerpos.X += rnd.Next(2);
velocity.Y = playerpos.Y;
velocity *= 4;
velocity.Normalize();

Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);
}
if (patternType == 2)
{
Random rnd = new Random();
startPos.X = playerpos.X += rnd.Next(10);
startPos.Y = playerpos.X += rnd.Next(5);


velocity.X = playerpos.X += rnd.Next(2);
velocity.Y = playerpos.Y;
velocity *= 10;
velocity.Normalize();

Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);
Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);
Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);
Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);
}
if (patternType == 3)
{
Random rnd = new Random();
startPos.X = playerpos.X += rnd.Next(10);
startPos.Y = playerpos.X += rnd.Next(5);


velocity.X = playerpos.X += rnd.Next(2);
velocity.Y = playerpos.Y;
velocity *= 7;
velocity.Normalize();

Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);

}
if (patternType == 4)
{
Random rnd = new Random();
startPos.X = playerpos.X += rnd.Next(10);
startPos.Y = playerpos.X += rnd.Next(5);


velocity.X = playerpos.X += rnd.Next(2);
velocity.Y = playerpos.Y;
velocity *= 9;
velocity.Normalize();

Projectile.NewProjectile(entitySource, startPos, velocity, ModContent.ProjectileType<MagmaBullet>(), damage, 0.25f);

}
}


}
}


public bool talked = false;

private void Talk(string message)
{
if (Main.netMode != NetmodeID.Server)
{

Main.NewText(message, 255, 111, 0);
}


}

private void JumpDrop(Player player)
{
Vector2 velocity;
velocity = new Vector2(player.position.X -= NPC.position.X * 0.25f, player.position.Y -= NPC.position.Y * 5.25f);
NPC.velocity = velocity;
jumping = true;
}

private void Immobilise()
{
NPC.velocity = new Vector2(0, 0);
}

private void land()
{
Immobilise();
for (int i = 0; i < 5; i++)
{
Dust.NewDust(NPC.position, 2, 1, DustID.Smoke, NPC.velocity.X, NPC.velocity.Y);

}
SoundEngine.PlaySound(SoundID.DD2_BetsyFireballImpact);
}

private void archive()
{
Player player = Main.player[NPC.target];

if (timer < 6) timer++;
else if (timer > 6)
{
timer = 0;
JumpDrop(Main.player[NPC.target]);
}

if (jumping && jumpTimer < 6) jumpTimer++;
else if (jumpTimer < 6)
{
jumpTimer = 0;
land();
}

if (NPC.velocity.Y > player.velocity.Y + 0.25f)
{
NPC.velocity = new Vector2(0, 0);

}

if (NPC.Top.Y < player.Bottom.Y)
{
if (!hasClimed)
{
NPC.velocity.Y = player.velocity.Y + 0.25f;

if (timer2 > 2)
{
Immobilise();
}
else
timer2++;
hasClimed = true;
}


}

else if (NPC.Bottom.Y > player.Top.Y)
{
if (!hasClimed)
{
if (NPC.Bottom.Y > player.Top.Y) NPC.velocity.Y = player.velocity.Y - 0.25f;
else NPC.velocity.Y = 0;
}

}
}
}
}
 
Last edited:
Back
Top Bottom