private void MakeCrystalVulnerable() { if (AttackTimer == 1) { startPos = npc.Center; while (crystalLocations.Count < 4) { BuildCrystalLocations(); } if (Main.netMode != NetmodeID.MultiplayerClient) { List <Vector2> possibleLocations = new List <Vector2>(crystalLocations); possibleLocations.ForEach(n => n += new Vector2(0, -48)); possibleLocations = Helper.RandomizeList(possibleLocations, Main.rand); 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 crystalNpc.netUpdate = true; } } } if (AttackTimer < 60) { npc.Center = Vector2.SmoothStep(startPos, homePos, AttackTimer / 60f); } if (AttackTimer == 180 && Main.netMode != NetmodeID.MultiplayerClient) { var crystal = crystals.FirstOrDefault(n => n.ai[0] == 2); if (crystal != null) { crystal.ai[0] = 0; } } if (AttackTimer > 180 && AttackTimer % 25 == 0 && Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(homePos + new Vector2(Main.rand.Next(-700, 700), -460), new Vector2(0, 18), ProjectileType <TelegraphedGlassSpike>(), 15, 0); } if (AttackTimer >= 720) { ResetAttack(); } }
private void CrystalSmash() { while (crystalLocations.Count < 4) { BuildCrystalLocations(); } //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 } //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 } int lockSpeed = 60 - BrokenCount * (Main.expertMode ? 7 : 4); //Crystals during the attack for (int k = 0; k < 4; k++) { NPC crystal = crystals[k]; VitricBossCrystal crystalModNPC = crystal.modNPC as VitricBossCrystal; if (AttackTimer == lockSpeed + k * lockSpeed && Main.netMode != NetmodeID.MultiplayerClient) //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); crystal.netUpdate = true; } if (AttackTimer >= lockSpeed + k * lockSpeed && AttackTimer <= lockSpeed + (k + 1) * lockSpeed) //move the crystal there { crystal.Center = Vector2.SmoothStep(crystalModNPC.StartPos, crystalModNPC.TargetPos, (AttackTimer - (lockSpeed + k * lockSpeed)) / lockSpeed); } if (AttackTimer == lockSpeed + (k + 1) * lockSpeed) //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 > 120 + lockSpeed * 4) { ResetAttack(); } }
private void CrystalSmashSpaced() { for (int k = 0; k < 4; k++) { NPC crystal = crystals[k]; VitricBossCrystal crystalModNPC = crystal.modNPC as VitricBossCrystal; if (AttackTimer == 60) //set motion points correctly { crystal.ai[2] = 0; //set the crystal into normal mode crystalModNPC.StartPos = crystal.Center; crystalModNPC.TargetPos = homePos + new Vector2(-500 + k * 333, -400); //endpoint is above the player } if (AttackTimer >= 60 && AttackTimer <= 120) //move the crystal there { crystal.Center = Vector2.SmoothStep(crystalModNPC.StartPos, crystalModNPC.TargetPos, (AttackTimer - 60) / 60f); } if (AttackTimer == 120) //set the crystal into falling mode after moving { crystal.ai[2] = 3; crystal.ai[1] = 0; crystalModNPC.TargetPos.Y = homePos.Y + 800; //fall through platforms } if (AttackTimer > 180) { Player player = Main.player[npc.target]; int fireRate = 60; float variance = 0; if (crystal.ai[0] == 3) { fireRate = 35; variance = 0.5f; } if (Main.expertMode) { fireRate -= 10; } if (AttackTimer % fireRate == 0 && Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(crystal.Center + new Vector2(0, -32), Vector2.Normalize(crystal.Center - player.Center).RotatedByRandom(variance) * -10, ProjectileType <NPCs.Vitric.SnakeSpit>(), 26, 0, Main.myPlayer); } if (AttackTimer % 10 == 0) { Dust.NewDustPerfect(crystal.Center, DustType <LavaSpew>()); } } } //ending the attack if (AttackTimer > 360) { ResetAttack(); } }
private void FireCage() { if (AttackTimer % 110 == 0 && AttackTimer != 0 && AttackTimer < 800) //the sand cones the boss fires { if (Main.netMode != NetmodeID.MultiplayerClient) { float rot = (npc.Center - Main.player[npc.target].Center).ToRotation() + Main.rand.NextFloat(-0.5f, 0.5f); int index = Projectile.NewProjectile(npc.Center + new Vector2(0, 30), Vector2.Zero, ProjectileType <FireCone>(), 25, 0, Main.myPlayer, 0, rot); //fire cone (Main.projectile[index].modProjectile as FireCone).extraShots = BrokenCount >= 1; lockedRotation = rot + 3.14f; RandomizeTarget(); } } if (AttackTimer % 110 == 25) { Helper.PlayPitched("VitricBoss/ceiroslidopensmall", 0.5f, Main.rand.NextFloat(0.1f, 1), npc.Center); } if (AttackTimer > 110 && AttackTimer % 110 > 10 && AttackTimer % 110 <= 90) { SetFrameY(2); int x = (int)(Math.Sin((AttackTimer % 110 - 10) / 80f * 3.14f) * 8); SetFrameX(x); } else { SetFrameY(0); } if (AttackTimer >= 110) { rotationLocked = true; } 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 = bossRand.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) { Helper.PlayPitched("VitricBoss/RingIdle", 0.075f, -0.2f, npc.Center); } if (AttackTimer >= 360 && AttackTimer < 840) //come back in { float addedRotation = BrokenCount * (Main.expertMode ? 2.3f : 1.8f); crystal.Center = npc.Center + (Vector2.SmoothStep(crystalModNPC.TargetPos, crystalModNPC.StartPos, (AttackTimer - 360) / 480) - npc.Center).RotatedBy(-(AttackTimer - 360) / 480 * (4.72f + addedRotation)); //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 > (minCageBounceDist) && dist <= crystalDist + player.width && dist >= crystalDist - player.width) && !(angleDiff > 0 && angleDiff < 1.57f)) { Vector2 maxSpeed = new Vector2(maxCageBounceSpeed); player.Hurt(Terraria.DataStructures.PlayerDeathReason.ByNPC(npc.whoAmI), Main.expertMode ? 90 : 65, 0); //do big damag player.velocity = Vector2.Clamp(player.velocity + Vector2.Normalize(player.Center - npc.Center) * -3, -maxSpeed, maxSpeed); //knock into boss Main.PlaySound(SoundID.DD2_LightningAuraZap); //bzzt! } } } }