using Terraria; using Terraria.ID; using Terraria.ModLoader; using Microsoft.Xna.Framework; using Terraria.Chat; using Terraria.Localization; namespace ForgottenWawiTech.Content.NPCs.OriginalNiko { [AutoloadBossHead] public class Niko : ModNPC { public override void SetStaticDefaults() { Main.npcFrameCount[NPC.type] = 1; } public override void SetDefaults() { NPC.width = 40; NPC.height = 40; NPC.damage = 2000; NPC.defense = 300; NPC.lifeMax = 1000000; NPC.HitSound = SoundID.NPCHit1; NPC.DeathSound = SoundID.NPCDeath1; NPC.knockBackResist = 0f; NPC.boss = true; NPC.aiStyle = -1; NPC.value = Item.buyPrice(0, 1, 0, 0); } public override void AI() { // Phased behaviour for Niko: // Phase 0: initial/dummy AI until 50% HP // Phase 1: invulnerable for ~1 minute (dialogue opportunity) // Phase 2: dummy AI until 30% HP // Phase 3: spawn other bosses (Lyyka, Neptune, Lexy) if they are undefeated, then move to Phase 4 NPC.TargetClosest(false); Player target = Main.player[NPC.target]; if (!target.active || target.dead) { NPC.timeLeft = 300; return; } int phase = (int)NPC.ai[0]; int flags = (int)NPC.ai[1]; // bit0=Lyyka spawned, bit1=Neptune spawned, bit2=Lexy spawned int timer = (int)NPC.ai[2]; // Simple dummy movement helper void DoDummyMovement(float speed) { Vector2 dir = target.Center - NPC.Center; if (dir.Length() > 8f) { dir.Normalize(); NPC.velocity = Vector2.Lerp(NPC.velocity, dir * speed, 0.02f); } } // Phase 0: initial until 50% HP if (phase == 0) { DoDummyMovement(3f); if (NPC.life <= NPC.lifeMax / 2) { // Enter invulnerable phase NPC.ai[0] = 1; NPC.ai[2] = 0; // reset timer NPC.dontTakeDamage = true; NPC.netUpdate = true; string announce = "Niko envelopes themself in a strange silence..."; if (Main.netMode != NetmodeID.MultiplayerClient) Main.NewText(announce, 200, 100, 255); else ChatHelper.BroadcastChatMessage(NetworkText.FromLiteral(announce), new Color(200, 100, 255)); } } // Phase 1: invulnerable waiting (~1 minute) for dialogue else if (phase == 1) { NPC.ai[2]++; // Idle drift NPC.velocity *= 0.98f; // Periodic short dialogue during invulnerable phase int t = (int)NPC.ai[2]; if (t == 120) { string line = "...silence stretches, an uneasy calm."; if (Main.netMode != NetmodeID.MultiplayerClient) Main.NewText(line, 200, 120, 255); else ChatHelper.BroadcastChatMessage(NetworkText.FromLiteral(line), new Color(200, 120, 255)); } else if (t == 900) { string line = "Impressive..."; if (Main.netMode != NetmodeID.MultiplayerClient) Main.NewText(line, 220, 160, 255); else ChatHelper.BroadcastChatMessage(NetworkText.FromLiteral(line), new Color(220, 160, 255)); } else if (t == 1000) { string line = "But this has already gone on long enough."; if (Main.netMode != NetmodeID.MultiplayerClient) Main.NewText(line, 180, 130, 255); else ChatHelper.BroadcastChatMessage(NetworkText.FromLiteral(line), new Color(180, 130, 255)); } // TODO: Audio and VO lines here else if (t == 1500) { string line = "Sky has rated the following film 12 - suitable for ages 12 and above."; if (Main.netMode != NetmodeID.MultiplayerClient) Main.NewText(line, 210, 150, 255); else ChatHelper.BroadcastChatMessage(NetworkText.FromLiteral(line), new Color(210, 150, 255)); } else if (t == 1800) { string line = "This film contains violence, explicit language, and flashing images."; // this is such a random reference that ill be surprised if anyone gets it especially outside gb if (Main.netMode != NetmodeID.MultiplayerClient) Main.NewText(line, 230, 180, 255); else ChatHelper.BroadcastChatMessage(NetworkText.FromLiteral(line), new Color(230, 180, 255)); } // TODO: When audio is finished, switch to Noctis Boss 2 theme (remix of In The End? Or something new? TBA) // After approx 42 seconds (2500 ticks), end invulnerability if (NPC.ai[2] >= 2500) { NPC.dontTakeDamage = false; NPC.ai[0] = 2; NPC.netUpdate = true; string announce = "Now get out of my sight."; if (Main.netMode != NetmodeID.MultiplayerClient) Main.NewText(announce, 255, 150, 100); else ChatHelper.BroadcastChatMessage(NetworkText.FromLiteral(announce), new Color(255, 150, 100)); } } // Phase 2: dummy AI after invul until 30% HP else if (phase == 2) { DoDummyMovement(4f); if (NPC.life <= (int)(NPC.lifeMax * 0.3f)) { NPC.ai[0] = 3; // go to spawn phase NPC.netUpdate = true; } } // Phase 3: spawn other bosses if undefeated else if (phase == 3) { // Spawn Lyyka if undefeated and not already spawned if (!WorldDefeatedBosses.Lyyka && (flags & 1) == 0) { flags |= 1; string announceL = "Lyyka is called to the fray!"; } // Spawn Neptune if undefeated and not already spawned if (!WorldDefeatedBosses.Neptune && (flags & 2) == 0) { flags |= 2; string announceN = "The waves answer Niko's call: Neptune arrives!"; } // Spawn Lexy if undefeated and not already spawned if (!WorldDefeatedBosses.Lexy && (flags & 4) == 0) { flags |= 4; string announceX = "Lexy emerges to twist the battlefield!"; } // Save flags and move to final phase NPC.ai[1] = flags; NPC.ai[0] = 4; NPC.netUpdate = true; } // Phase 4+: final behaviour (dummy) else { DoDummyMovement(5f); } } public override void OnKill() { // Ending monologue before playing the final track // Only if Niko was actually defeated (not fled) and hasn't been defeated before if (!WorldDefeatedBosses.Niko) { string line1 = "With a final breath, Niko whispers..."; string line2 = "\"gg, no re\""; string line3 = "The battlefield falls silent as Niko vanishes."; if (Main.netMode != NetmodeID.MultiplayerClient) { Main.NewText(line1, 255, 100, 100); Main.NewText(line2, 200, 50, 50); Main.NewText(line3, 255, 150, 150); } else { ChatHelper.BroadcastChatMessage(NetworkText.FromLiteral(line1), new Color(255, 100, 100)); ChatHelper.BroadcastChatMessage(NetworkText.FromLiteral(line2), new Color(200, 50, 50)); ChatHelper.BroadcastChatMessage(NetworkText.FromLiteral(line3), new Color(255, 150, 150)); } // Set a world flag or similar to indicate Niko has been defeated WorldDefeatedBosses.Niko = true; // Immediate sync of world flags WorldDefeatedBosses.Sync(ModContent.GetInstance()); } } } }