private void CrystalCage() { if (AttackTimer % 110 == 0 && AttackTimer != 0 && AttackTimer < 800) //the sand cones the boss fires { RandomizeTarget(); int index = Projectile.NewProjectile(npc.Center, Vector2.Zero, ProjectileType <SandCone>(), 1, 0); //spawn a sand cone attack Main.projectile[index].rotation = (npc.Center - Main.player[npc.target].Center).ToRotation() + Main.rand.NextFloat(-0.5f, 0.5f); } for (int k = 0; k < 4; k++) //each crystal { NPC crystal = crystals[k]; VitricBossCrystal crystalModNPC = crystal.modNPC as VitricBossCrystal; if (AttackTimer == 1) //set the crystal's home position to where they are { crystalModNPC.StartPos = crystal.Center; favoriteCrystal = Main.rand.Next(4); //randomize which crystal will have the opening } if (AttackTimer > 1 && AttackTimer <= 60) //suck the crystals in { crystal.Center = npc.Center + (Vector2.SmoothStep(crystalModNPC.StartPos, npc.Center, AttackTimer / 60) - npc.Center).RotatedBy(AttackTimer / 60f * 3.14f); } if (AttackTimer == 61) //Set the crystal's new endpoints. !! actual endpoints are offset by pi !! { crystalModNPC.StartPos = crystal.Center; crystalModNPC.TargetPos = npc.Center + new Vector2(0, -800).RotatedBy(1.57f * k); crystal.ai[2] = 2; //set them into this mode to get the rotational effect } if (AttackTimer >= 120 && AttackTimer < 360) //spiral outwards slowly { crystal.Center = npc.Center + (Vector2.SmoothStep(crystalModNPC.StartPos, crystalModNPC.TargetPos, (AttackTimer - 120) / 240) - npc.Center).RotatedBy((AttackTimer - 120) / 240 * 3.14f); } if (AttackTimer >= 360 && AttackTimer < 840) //come back in { crystal.Center = npc.Center + (Vector2.SmoothStep(crystalModNPC.TargetPos, crystalModNPC.StartPos, (AttackTimer - 360) / 480) - npc.Center).RotatedBy(-(AttackTimer - 360) / 480 * 4.72f); //the chosen "favorite" or master crystal is the one where our opening should be if (k != favoriteCrystal) { for (int i = 0; i < 8; i++) { Dust d = Dust.NewDustPerfect(npc.Center + (crystal.Center - npc.Center).RotatedBy(Main.rand.NextFloat(1.57f)), DustType <Dusts.BlueStamina>(), Vector2.Zero, 0, default, 2);
private void NukePlatforms() { if (AttackTimer == 1) { List <Vector2> possibleLocations = new List <Vector2>(crystalLocations); possibleLocations.ForEach(n => n += new Vector2(0, -48)); possibleLocations = Helper.RandomizeList(possibleLocations); for (int k = 0; k < crystals.Count; k++) { NPC crystalNpc = crystals[k]; VitricBossCrystal crystal = crystalNpc.modNPC as VitricBossCrystal; crystal.StartPos = crystalNpc.Center; Vector2 target = possibleLocations[k]; crystal.TargetPos = target; crystalNpc.ai[1] = 0; //reset the crystal's timers crystalNpc.ai[2] = 1; //set them into this attack's mode } Twist(60, 0); } if (AttackTimer == 180) { crystals.FirstOrDefault(n => n.ai[0] == 2).ai[0] = 0; } if (AttackTimer > 180 && AttackTimer % 25 == 0) { Projectile.NewProjectile(homePos + new Vector2(Main.rand.Next(-700, 700), -460), new Vector2(0, 12), ProjectileType <GlassSpike>(), 15, 0); } if (AttackTimer >= 720) { ResetAttack(); } }
private void NukePlatforms() { if (AttackTimer == 1) { List <Vector2> possibleLocations = new List <Vector2>(crystalLocations); possibleLocations.ForEach(n => n += new Vector2(0, -32)); for (int k = 0; k < crystals.Count; k++) { NPC crystalNpc = crystals[k]; VitricBossCrystal crystal = crystalNpc.modNPC as VitricBossCrystal; crystal.StartPos = crystalNpc.Center; Vector2 target = possibleLocations.OrderBy(n => Vector2.Distance(crystalNpc.Center, n)).ToList()[0]; //sort the remaining possible unique platforms by distance, this ensures crystals go to theirn earest unique platform crystal.TargetPos = target; possibleLocations.Remove(target); crystalNpc.ai[1] = 0; //reset the crystal's timers crystalNpc.ai[2] = 1; //set them into this attack's mode } } if (AttackTimer == 180) { crystals.FirstOrDefault(n => n.ai[0] == 2).ai[0] = 0; } if (AttackTimer > 180 && AttackTimer % 15 == 0) { Vector2 pos = crystalLocations[Main.rand.Next(crystalLocations.Count)] + new Vector2(0, -20); Projectile.NewProjectile(pos + new Vector2(Main.rand.Next(-100, 60), 64), Vector2.Zero, ProjectileType <BossSpikeSmall>(), 5, 0); } if (AttackTimer >= 720) { ResetAttack(); } }
private void CrystalCage() { if (AttackTimer % 110 == 0 && AttackTimer != 0 && AttackTimer < 800) //the sand cones the boss fires { RandomizeTarget(); int index = Projectile.NewProjectile(npc.Center + new Vector2(0, 30), Vector2.Zero, ProjectileType <SandCone>(), 1, 0); //spawn a sand cone attack Main.projectile[index].rotation = (npc.Center - Main.player[npc.target].Center).ToRotation() + Main.rand.NextFloat(-0.5f, 0.5f); Twist(40); } for (int k = 0; k < 4; k++) //each crystal { NPC crystal = crystals[k]; VitricBossCrystal crystalModNPC = crystal.modNPC as VitricBossCrystal; if (AttackTimer == 1) //set the crystal's home position to where they are { crystalModNPC.StartPos = crystal.Center; favoriteCrystal = Main.rand.Next(4); //randomize which crystal will have the opening } if (AttackTimer > 1 && AttackTimer <= 60) //suck the crystals in { crystal.Center = npc.Center + (Vector2.SmoothStep(crystalModNPC.StartPos, npc.Center, AttackTimer / 60) - npc.Center).RotatedBy(AttackTimer / 60f * 3.14f); } if (AttackTimer == 61) //Set the crystal's new endpoints. !! actual endpoints are offset by pi !! { crystalModNPC.StartPos = crystal.Center; crystalModNPC.TargetPos = npc.Center + new Vector2(0, -800).RotatedBy(1.57f * k); crystal.ai[2] = 2; //set them into this mode to get the rotational effect } if (AttackTimer >= 120 && AttackTimer < 360) //spiral outwards slowly { crystal.Center = npc.Center + (Vector2.SmoothStep(crystalModNPC.StartPos, crystalModNPC.TargetPos, (AttackTimer - 120) / 240) - npc.Center).RotatedBy((AttackTimer - 120) / 240 * 3.14f); } if (AttackTimer == 360) { Main.PlaySound(SoundID.DD2_BetsyFireballImpact, npc.Center); } if (AttackTimer >= 360 && AttackTimer < 840) //come back in { crystal.Center = npc.Center + (Vector2.SmoothStep(crystalModNPC.TargetPos, crystalModNPC.StartPos, (AttackTimer - 360) / 480) - npc.Center).RotatedBy(-(AttackTimer - 360) / 480 * 4.72f); //the chosen "favorite" or master crystal is the one where our opening should be if (k != favoriteCrystal) { crystalModNPC.shouldDrawArc = true; float alpha = 0; if (AttackTimer < 420) { alpha = (AttackTimer - 360) / 60f; } else if (AttackTimer > 760) { alpha = 1 - (AttackTimer - 760) / 80f; } else { alpha = 1; } for (int i = 0; i < 4 - (int)(AttackTimer - 360) / 100; i++) { var rot = Main.rand.NextFloat(1.57f); Dust.NewDustPerfect(npc.Center + (crystal.Center - npc.Center).RotatedBy(rot), DustType <Dusts.Glow>(), -Vector2.UnitX.RotatedBy(crystal.rotation + rot + Main.rand.NextFloat(-0.45f, 0.45f)) * Main.rand.NextFloat(1, 4) * alpha, 0, new Color(255, 160, 100) * alpha, Main.rand.NextFloat(0.4f, 0.8f) * alpha); } } } if (AttackTimer >= 840 && AttackTimer < 880) //reset to ready position { crystal.Center = Vector2.SmoothStep(npc.Center, npc.Center + new Vector2(0, -120).RotatedBy(1.57f * k), (AttackTimer - 840) / 40); } if (AttackTimer == 880) //end of the attack { crystal.ai[2] = 0; //reset our crystals ResetAttack(); //all done! } } if (AttackTimer >= 360 && AttackTimer < 840) //the collision handler for this attack. out here so its not done 4 times { foreach (Player player in Main.player.Where(n => n.active)) { float dist = Vector2.Distance(player.Center, npc.Center); //distance the player is from the boss float angleOff = (player.Center - npc.Center).ToRotation() % 6.28f; //where the player is versus the boss angularly. used to check if the player is in the opening NPC crystal = crystals[favoriteCrystal]; float crystalDist = Vector2.Distance(crystal.Center, npc.Center); //distance from the boss to the ring float crystalOff = (crystal.Center - npc.Center).ToRotation() % 6.28f; //crystal's rotation float angleDiff = Helper.CompareAngle(angleOff, crystalOff); // if the player's distance from the boss is within 2 player widths of the ring and if the player isnt in the gab where they would be safe if ((dist <= crystalDist + player.width && dist >= crystalDist - player.width) && !(angleDiff > 0 && angleDiff < 1.57f)) { player.Hurt(Terraria.DataStructures.PlayerDeathReason.ByNPC(npc.whoAmI), Main.expertMode ? 90 : 65, 0); //do big damag player.velocity += Vector2.Normalize(player.Center - npc.Center) * -3; //knock into boss Main.PlaySound(SoundID.DD2_LightningAuraZap); //bzzt! } } } }
private void CrystalSmash() { //boss during the attack if (AttackTimer == 1) { endPos = npc.Center; //set the ending point to the center of the arena so we can come back later Twist(60, 0); } //actual movement if (AttackTimer < 270) { npc.position.Y += (float)Math.Sin(AttackTimer / 90 * 6.28f) * 2.5f; float vel = ((AttackTimer % 68) / 17 - (float)Math.Pow(AttackTimer % 68, 2) / 1156) * 7; npc.position.X += (AttackTimer < 68 || AttackTimer > 68 * 3) ? vel : -vel; } if (AttackTimer == 270)//where we start our return trip { startPos = npc.Center; npc.velocity *= 0; } if (AttackTimer > 270) { npc.Center = Vector2.SmoothStep(startPos, endPos, (AttackTimer - 270) / 90); //smoothstep back to the center } //Crystals during the attack for (int k = 0; k < 4; k++) { NPC crystal = crystals[k]; VitricBossCrystal crystalModNPC = crystal.modNPC as VitricBossCrystal; if (AttackTimer == 60 + k * 60) //set motion points correctly { RandomizeTarget(); //pick a random target to smash a crystal down Player player = Main.player[npc.target]; crystal.ai[2] = 0; //set the crystal into normal mode crystalModNPC.StartPos = crystal.Center; crystalModNPC.TargetPos = new Vector2(player.Center.X + player.velocity.X * 50, player.Center.Y - 250); //endpoint is above the player crystalModNPC.TargetPos.X = MathHelper.Clamp(crystalModNPC.TargetPos.X, homePos.X - 800, homePos.X + 800); } if (AttackTimer >= 60 + k * 60 && AttackTimer <= 60 + (k + 1) * 60) //move the crystal there { crystal.Center = Vector2.SmoothStep(crystalModNPC.StartPos, crystalModNPC.TargetPos, (AttackTimer - (60 + k * 60)) / 60); } if (AttackTimer == 60 + (k + 1) * 60) //set the crystal into falling mode after moving { Player player = Main.player[npc.target]; crystal.ai[2] = 3; crystal.ai[1] = 0; crystalModNPC.TargetPos = player.Center; } } //ending the attack if (AttackTimer > 360) { ResetAttack(); } }