public override bool PreAI(NPC npc) { bool result = base.PreAI(npc); NPC.golemBoss = npc.whoAmI; if (FargoSoulsWorld.SwarmActive) { return(result); } /*if (npc.ai[0] == 0f && npc.velocity.Y == 0f) //manipulating golem jump ai * { * if (npc.ai[1] > 0f) * { * npc.ai[1] += 5f; //count up to initiate jump faster * } * else * { * float threshold = -2f - (float)Math.Round(18f * npc.life / npc.lifeMax); * * if (npc.ai[1] < threshold) //jump activates at npc.ai[1] == -1 * npc.ai[1] = threshold; * } * }*/ foreach (Player p in Main.player) { if (p.active && p.Distance(npc.Center) < 2000) { p.AddBuff(ModContent.BuffType <LowGround>(), 2); } } HealPerSecond = FargoSoulsWorld.MasochistModeReal ? 240 : 180; if (!IsInTemple) //temple enrage, more horiz move and fast jumps { HealPerSecond *= 2; npc.position.X += npc.velocity.X / 2f; if (npc.velocity.Y < 0) { npc.position.Y += npc.velocity.Y * 0.5f; if (npc.velocity.Y > -2) { npc.velocity.Y = 20; } } } if (npc.velocity.Y < 0) //jumping up { if (!HaveBoostedJumpHeight) { HaveBoostedJumpHeight = true; npc.velocity.Y *= 1.25f; if (!IsInTemple) //temple enrage { if (Main.player[npc.target].Center.Y < npc.Center.Y - 16 * 30) { npc.velocity.Y *= 1.5f; } } } } else { HaveBoostedJumpHeight = false; } if (DoStompBehaviour) { if (npc.velocity.Y == 0f) //landing attacks { DoStompBehaviour = false; IsInTemple = Framing.GetTileSafely(npc.Center).WallType == WallID.LihzahrdBrickUnsafe; if (IsInTemple) //in temple { StompAttackCounter++; if (StompAttackCounter == 1) //plant geysers { if (FargoSoulsWorld.MasochistModeReal) { StompAttackCounter++; } Vector2 spawnPos = new Vector2(npc.position.X, npc.Center.Y); //floor geysers spawnPos.X -= npc.width * 7; for (int i = 0; i < 6; i++) { int tilePosX = (int)spawnPos.X / 16 + npc.width * i * 3 / 16; int tilePosY = (int)spawnPos.Y / 16;// + 1; if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), tilePosX * 16 + 8, tilePosY * 16 + 8, 0f, 0f, ModContent.ProjectileType <GolemGeyser2>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer, npc.whoAmI); } } spawnPos = npc.Center; for (int i = -3; i <= 3; i++) //ceiling geysers { int tilePosX = (int)spawnPos.X / 16 + npc.width * i * 3 / 16; int tilePosY = (int)spawnPos.Y / 16;// + 1; if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), tilePosX * 16 + 8, tilePosY * 16 + 8, 0f, 0f, ModContent.ProjectileType <GolemGeyser>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer, npc.whoAmI); } } } else if (StompAttackCounter == 2) //empty jump { } else if (StompAttackCounter == 3) //rocks fall { if (FargoSoulsWorld.MasochistModeReal) { StompAttackCounter = 0; } if (npc.HasPlayerTarget) { if (!Main.dedServ) { Main.LocalPlayer.GetModPlayer <FargoSoulsPlayer>().Screenshake = 20; } if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, Vector2.Zero, ProjectileID.DD2OgreSmash, 0, 0, Main.myPlayer); } for (int i = -2; i <= 2; i++) { int tilePosX = (int)Main.player[npc.target].Center.X / 16; int tilePosY = (int)Main.player[npc.target].Center.Y / 16;// + 1; tilePosX += 4 * i; //first move up through solid tiles while (Main.tile[tilePosX, tilePosY].HasUnactuatedTile && Main.tileSolid[Main.tile[tilePosX, tilePosY].TileType]) { tilePosY--; } //then move up through air until next ceiling reached while (!(Main.tile[tilePosX, tilePosY].HasUnactuatedTile && Main.tileSolid[Main.tile[tilePosX, tilePosY].TileType])) { tilePosY--; } Vector2 spawn = new Vector2(tilePosX * 16 + 8, tilePosY * 16 + 8); if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), spawn, Vector2.Zero, ModContent.ProjectileType <GolemBoulder>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer); } } } } else //empty jump { StompAttackCounter = 0; } } else //outside temple { Vector2 spawnPos = new Vector2(npc.position.X, npc.Center.Y); spawnPos.X -= npc.width * 7; for (int i = 0; i < 6; i++) { int tilePosX = (int)spawnPos.X / 16 + npc.width * i * 3 / 16; int tilePosY = (int)spawnPos.Y / 16;// + 1; while (!(Main.tile[tilePosX, tilePosY].HasUnactuatedTile && Main.tileSolid[(int)Main.tile[tilePosX, tilePosY].TileType])) { tilePosY++; } if (Main.netMode != NetmodeID.MultiplayerClient) { if (npc.HasPlayerTarget && Main.player[npc.target].position.Y > tilePosY * 16) { Projectile.NewProjectile(npc.GetSource_FromThis(), tilePosX * 16 + 8, tilePosY * 16 + 8, 6.3f, 6.3f, ProjectileID.FlamesTrap, FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer); Projectile.NewProjectile(npc.GetSource_FromThis(), tilePosX * 16 + 8, tilePosY * 16 + 8, -6.3f, 6.3f, ProjectileID.FlamesTrap, FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer); } Projectile.NewProjectile(npc.GetSource_FromThis(), tilePosX * 16 + 8, tilePosY * 16 + 8, 0f, -8f, ProjectileID.GeyserTrap, FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer); Projectile.NewProjectile(npc.GetSource_FromThis(), tilePosX * 16 + 8, tilePosY * 16 + 8 - 640, 0f, -8f, ProjectileID.GeyserTrap, FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer); Projectile.NewProjectile(npc.GetSource_FromThis(), tilePosX * 16 + 8, tilePosY * 16 + 8 - 640, 0f, 8f, ProjectileID.GeyserTrap, FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer); } } if (npc.HasPlayerTarget) { for (int i = -3; i <= 3; i++) { int tilePosX = (int)Main.player[npc.target].Center.X / 16; int tilePosY = (int)Main.player[npc.target].Center.Y / 16;// + 1; tilePosX += 10 * i; for (int j = 0; j < 30; j++) { if (Main.tile[tilePosX, tilePosY].HasUnactuatedTile && Main.tileSolid[Main.tile[tilePosX, tilePosY].TileType]) { break; } tilePosY--; } Vector2 spawn = new Vector2(tilePosX * 16 + 8, tilePosY * 16 + 8); if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), spawn, Vector2.Zero, ModContent.ProjectileType <GolemBoulder>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer); } } } } } } else if (npc.velocity.Y > 0) { DoStompBehaviour = true; } //spray spiky balls if (FargoSoulsWorld.MasochistModeReal && ++SpikyBallTimer >= 900) { if (Framing.GetTileSafely(npc.Center).WallType == WallID.LihzahrdBrickUnsafe) { if (npc.velocity.Y > 0) //only when falling, implicitly assume at peak of a jump { SpikyBallTimer = FargoSoulsWorld.MasochistModeReal ? 600 : 0; for (int i = 0; i < 8; i++) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.position.X + Main.rand.Next(npc.width), npc.position.Y + Main.rand.Next(npc.height), Main.rand.NextFloat(-0.3f, 0.3f), Main.rand.NextFloat(-10, -6), ModContent.ProjectileType <GolemSpikyBall>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer); } } } else //outside temple { SpikyBallTimer = 600; //do it more often for (int i = 0; i < 16; i++) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.position.X + Main.rand.Next(npc.width), npc.position.Y + Main.rand.Next(npc.height), Main.rand.NextFloat(-1f, 1f), Main.rand.Next(-20, -9), ModContent.ProjectileType <GolemSpikyBall>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer); } } } //golem's anti-air fireball spray (when player is above) if (FargoSoulsWorld.MasochistModeReal && ++AntiAirTimer > 240 && npc.velocity.Y == 0) { AntiAirTimer = 0; if (npc.HasPlayerTarget && Main.player[npc.target].Center.Y < npc.Bottom.Y && Main.netMode != NetmodeID.MultiplayerClient) //shoutouts to arterius { bool inTemple = Framing.GetTileSafely(npc.Center).WallType == WallID.LihzahrdBrickUnsafe; float gravity = -0.2f; //normally floats up if (Main.player[npc.target].Center.Y > npc.Bottom.Y) { gravity *= -1f; //aim down if player below golem } const float time = 60f; Vector2 distance = Main.player[npc.target].Center - npc.Center; distance += Main.player[npc.target].velocity * 45f; distance.X = distance.X / time; distance.Y = distance.Y / time - 0.5f * gravity * time; if (Math.Sign(distance.Y) != Math.Sign(gravity)) { distance.Y = 0f; //cannot arc shots to hit someone on the same elevation } int max = inTemple ? 2 : 4; for (int i = -max; i <= max; i++) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center.X, npc.Center.Y, distance.X + i, distance.Y, ModContent.ProjectileType <GolemFireball>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage, 0.8f), 0f, Main.myPlayer, gravity, 0); } } } EModeUtils.DropSummon(npc, "LihzahrdPowerCell2", NPC.downedGolemBoss, ref DroppedSummon, NPC.downedPlantBoss); return(result); }
public override bool PreAI(NPC npc) { bool result = base.PreAI(npc); EModeGlobalNPC.beeBoss = npc.whoAmI; if (FargoSoulsWorld.SwarmActive) { return(result); } if (!SpawnedRoyalSubjectWave1 && npc.life < npc.lifeMax / 3 * 2 && npc.HasPlayerTarget) { SpawnedRoyalSubjectWave1 = true; Vector2 vector72 = new Vector2(npc.position.X + npc.width / 2 + Main.rand.Next(20) * npc.direction, npc.position.Y + npc.height * 0.8f); int n = FargoSoulsUtil.NewNPCEasy(npc.GetSource_FromAI(), vector72, ModContent.NPCType <RoyalSubject>(), velocity: new Vector2(Main.rand.Next(-200, 201) * 0.1f, Main.rand.Next(-200, 201) * 0.1f)); if (n != Main.maxNPCs) { Main.npc[n].localAI[0] = 60f; } string name = Language.GetTextValue($"Mods.{mod.Name}.NPCName.RoyalSubject"); string hasAwoken = Language.GetTextValue($"Mods.{mod.Name}.Message.HasAwoken"); FargoSoulsUtil.PrintText($"{name} {hasAwoken}", new Color(175, 75, 255)); npc.netUpdate = true; NetSync(npc); } if (!SpawnedRoyalSubjectWave2 && npc.life < npc.lifeMax / 3 && npc.HasPlayerTarget) { SpawnedRoyalSubjectWave2 = true; if (FargoSoulsWorld.MasochistModeReal) { SpawnedRoyalSubjectWave1 = false; //do this again } Vector2 vector72 = new Vector2(npc.position.X + npc.width / 2 + Main.rand.Next(20) * npc.direction, npc.position.Y + npc.height * 0.8f); int n = FargoSoulsUtil.NewNPCEasy(npc.GetSource_FromAI(), vector72, ModContent.NPCType <RoyalSubject>(), velocity: new Vector2(Main.rand.Next(-200, 201) * 0.1f, Main.rand.Next(-200, 201) * 0.1f)); if (n != Main.maxNPCs) { Main.npc[n].localAI[0] = 60f; } string name = Language.GetTextValue($"Mods.{mod.Name}.NPCName.RoyalSubject"); string hasAwoken = Language.GetTextValue($"Mods.{mod.Name}.Message.HasAwoken"); FargoSoulsUtil.PrintText($"{name} {hasAwoken}", new Color(175, 75, 255)); NPC.SpawnOnPlayer(npc.target, ModContent.NPCType <RoyalSubject>()); //so that both dont stack for being spawned from qb npc.netUpdate = true; NetSync(npc); } if (!InPhase2 && npc.life < npc.lifeMax / 2) //enable new attack and roar below 50% { InPhase2 = true; SoundEngine.PlaySound(SoundID.Roar, npc.Center); if (FargoSoulsWorld.MasochistModeReal) { SpawnedRoyalSubjectWave1 = false; //do this again } npc.netUpdate = true; NetSync(npc); } if (NPC.AnyNPCs(ModContent.NPCType <RoyalSubject>())) { npc.HitSound = SoundID.NPCHit4; npc.color = new Color(127, 127, 127); int dustId = Dust.NewDust(npc.position, npc.width, npc.height, 1, 0f, 0f, 100, default(Color), 2f); Main.dust[dustId].noGravity = true; int dustId3 = Dust.NewDust(npc.position, npc.width, npc.height, 1, 0f, 0f, 100, default(Color), 2f); Main.dust[dustId3].noGravity = true; npc.ai[0] = 3; //always shoot stingers mode if (npc.ai[1] > 1) { npc.ai[1] -= 0.5f; //slower stingers } } else { npc.HitSound = SoundID.NPCHit1; npc.color = default; if (InPhase2 && HiveThrowTimer % 2 == 0) { HiveThrowTimer++; //throw hives faster when no royal subjects alive } } if (FargoSoulsWorld.MasochistModeReal) { HiveThrowTimer++; if (ForgorDeathrayTimer > 0 && --ForgorDeathrayTimer % 10 == 0 && npc.HasValidTarget && Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), Main.player[npc.target].Center - 2000 * Vector2.UnitY, Vector2.UnitY, ModContent.ProjectileType <WillDeathraySmall>(), (int)(npc.damage * .75), 0f, Main.myPlayer, Main.player[npc.target].Center.X, npc.whoAmI); } } if (!InPhase2 || FargoSoulsWorld.MasochistModeReal) { if (npc.ai[0] == 3f || npc.ai[0] == 1f) //only when in stationary modes { if (++StingerRingTimer > 90 * 3) { StingerRingTimer = 0; } if (StingerRingTimer % 90 == 0) { if (Main.netMode != NetmodeID.MultiplayerClient) { FargoSoulsUtil.XWay(StingerRingTimer == 90 * 3 ? 16 : 8, npc.GetSource_FromThis(), npc.Center, ProjectileID.QueenBeeStinger, 6, 11, 1); } } } } if (InPhase2) { if (++HiveThrowTimer > 570 && BeeSwarmTimer <= 600 && (npc.ai[0] == 3f || npc.ai[0] == 1f)) //lobs hives below 50%, not dashing { HiveThrowTimer = 0; npc.netUpdate = true; NetSync(npc); const float gravity = 0.25f; float time = 75f; Vector2 distance = Main.player[npc.target].Center - Vector2.UnitY * 16 - npc.Center + Main.player[npc.target].velocity * 30f; distance.X = distance.X / time; distance.Y = distance.Y / time - 0.5f * gravity * time; if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, distance, ModContent.ProjectileType <Beehive>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer, time - 5); } } if (npc.ai[0] == 0 && npc.ai[1] == 1f) //if qb tries to start doing dashes of her own volition { npc.ai[0] = 3f; npc.ai[1] = 0f; //don't npc.netUpdate = true; } } //only while stationary mode if (npc.ai[0] == 3f || npc.ai[0] == 1f) { if (InPhase2 && ++BeeSwarmTimer > 600) { if (BeeSwarmTimer < 720) //slow down { if (BeeSwarmTimer == 601) { npc.netUpdate = true; NetSync(npc); if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, Vector2.Zero, ModContent.ProjectileType <GlowRing>(), 0, 0f, Main.myPlayer, npc.whoAmI, npc.type); } if (npc.HasValidTarget) { SoundEngine.PlaySound(SoundID.ForceRoarPitched, Main.player[npc.target].Center); //eoc roar } if (FargoSoulsWorld.MasochistModeReal) { BeeSwarmTimer += 30; } } if (Collision.CanHitLine(npc.Center, 0, 0, Main.player[npc.target].Center, 0, 0)) { npc.velocity *= 0.975f; } else if (BeeSwarmTimer > 630) { BeeSwarmTimer--; //stall this section until has line of sight return(true); } } else if (BeeSwarmTimer < 840) //spray bees { npc.velocity = Vector2.Zero; if (BeeSwarmTimer % 2 == 0 && Main.netMode != NetmodeID.MultiplayerClient) { const float rotation = 0.025f; for (int i = -1; i <= 1; i += 2) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center + new Vector2(3 * npc.direction, 15), i * Main.rand.NextFloat(9f, 18f) * Vector2.UnitX.RotatedBy(MathHelper.ToRadians(Main.rand.NextFloat(-45, 45))), ModContent.ProjectileType <Bee>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage, FargoSoulsWorld.MasochistModeReal ? 4f / 3 : 1), 0f, Main.myPlayer, npc.target, Main.rand.NextBool() ? -rotation : rotation); } } } else if (BeeSwarmTimer > 870) //return to normal AI { BeeSwarmTimer = 0; HiveThrowTimer -= 60; npc.netUpdate = true; NetSync(npc); npc.ai[0] = 0f; npc.ai[1] = 4f; //trigger dashes, but skip the first one npc.ai[2] = -44f; npc.ai[3] = 0f; } if (npc.netUpdate) { npc.netUpdate = false; if (Main.netMode == NetmodeID.Server) { NetMessage.SendData(MessageID.SyncNPC, -1, -1, null, npc.whoAmI); } } return(false); } } if (npc.ai[0] == 0 && npc.ai[1] == 4) //when about to do dashes triggered by royal subjects/bee swarm, telegraph and stall { if (npc.ai[2] < 0) { if (npc.ai[2] == -44) //telegraph { SoundEngine.PlaySound(SoundID.Item21, npc.Center); for (int i = 0; i < 44; i++) { int d = Dust.NewDust(npc.position, npc.width, npc.height, Main.rand.NextBool() ? 152 : 153, npc.velocity.X * 0.2f, npc.velocity.Y * 0.2f); Main.dust[d].scale = Main.rand.NextFloat(1f, 3f); Main.dust[d].velocity *= Main.rand.NextFloat(4.4f); Main.dust[d].noGravity = Main.rand.NextBool(); if (Main.dust[d].noGravity) { Main.dust[d].scale *= 2.2f; Main.dust[d].velocity *= 4.4f; } } if (FargoSoulsWorld.MasochistModeReal) { npc.ai[2] = 0; } ForgorDeathrayTimer = 95; } npc.velocity *= 0.95f; npc.ai[2]++; return(false); } } if (!npc.HasValidTarget || (npc.HasPlayerTarget && npc.Distance(Main.player[npc.target].Center) > 3000)) { if (npc.timeLeft > 30) { npc.timeLeft = 30; } } EModeUtils.DropSummon(npc, "Abeemination2", NPC.downedQueenBee, ref DroppedSummon); return(result); }
public override void AI(NPC npc) { base.AI(npc); EModeGlobalNPC.wallBoss = npc.whoAmI; if (FargoSoulsWorld.SwarmActive) { return; } if (npc.ai[3] == 0f) //when spawned in, make one eye invul { for (int i = 0; i < Main.maxNPCs; i++) { if (Main.npc[i].active && Main.npc[i].type == NPCID.WallofFleshEye && Main.npc[i].realLife == npc.whoAmI) { Main.npc[i].ai[2] = -1f; Main.npc[i].netUpdate = true; npc.ai[3] = 1f; npc.netUpdate = true; break; } } } if (InPhase2) //phase 2 { if (++WorldEvilAttackCycleTimer > 600) { WorldEvilAttackCycleTimer = 0; UseCorruptAttack = !UseCorruptAttack; npc.netUpdate = true; NetSync(npc); } else if (WorldEvilAttackCycleTimer > 600 - 120) //telegraph for special attacks { int type = !UseCorruptAttack ? 75 : 170; //corruption dust, then crimson dust int speed = !UseCorruptAttack ? 10 : 8; float scale = !UseCorruptAttack ? 6f : 4f; float speedModifier = !UseCorruptAttack ? 12f : 5f; Vector2 direction = npc.DirectionTo(Main.player[npc.target].Center); Vector2 vel = speed * direction; int d = Dust.NewDust(npc.Center + 32f * direction, 0, 0, type, vel.X, vel.Y, 100, Color.White, scale); Main.dust[d].velocity *= speedModifier; Main.dust[d].noGravity = true; } else if (WorldEvilAttackCycleTimer < 240) //special attacks { if (UseCorruptAttack) //cursed inferno attack { if (WorldEvilAttackCycleTimer == 10 && Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.Center, Vector2.UnitY, ModContent.ProjectileType <CursedDeathrayWOFS>(), 0, 0f, Main.myPlayer, npc.direction, npc.whoAmI); } if (WorldEvilAttackCycleTimer % 4 == 0) { float xDistance = (2500f - 1800f * WorldEvilAttackCycleTimer / 240f) * Math.Sign(npc.velocity.X); Vector2 spawnPos = new Vector2(npc.Center.X + xDistance, npc.Center.Y); Main.PlaySound(SoundID.Item34, spawnPos); const int offsetY = 800; const int speed = 14; if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(spawnPos + Vector2.UnitY * offsetY, Vector2.UnitY * -speed, ModContent.ProjectileType <CursedFlamethrower>(), npc.damage / 4, 0f, Main.myPlayer); Projectile.NewProjectile(spawnPos + Vector2.UnitY * offsetY / 2, Vector2.UnitY * speed, ModContent.ProjectileType <CursedFlamethrower>(), npc.damage / 4, 0f, Main.myPlayer); Projectile.NewProjectile(spawnPos + Vector2.UnitY * -offsetY / 2, Vector2.UnitY * -speed, ModContent.ProjectileType <CursedFlamethrower>(), npc.damage / 4, 0f, Main.myPlayer); Projectile.NewProjectile(spawnPos + Vector2.UnitY * -offsetY, Vector2.UnitY * speed, ModContent.ProjectileType <CursedFlamethrower>(), npc.damage / 4, 0f, Main.myPlayer); } } } else //ichor attack { if (WorldEvilAttackCycleTimer % 8 == 0) { if (Main.netMode != NetmodeID.MultiplayerClient) { for (int i = 0; i < 8; i++) { Vector2 target = npc.Center; target.X += Math.Sign(npc.velocity.X) * 1000f * WorldEvilAttackCycleTimer / 240f; //gradually targets further and further target.X += Main.rand.NextFloat(-100, 100); target.Y += Main.rand.NextFloat(-450, 450); const float gravity = 0.5f; float time = 60f; Vector2 distance = target - npc.Center; distance.X = distance.X / time; distance.Y = distance.Y / time - 0.5f * gravity * time; Projectile.NewProjectile(npc.Center + Vector2.UnitX * Math.Sign(npc.velocity.X) * 32f, distance, ModContent.ProjectileType <GoldenShowerWOF>(), npc.damage / 4, 0f, Main.myPlayer, time); } } } } } } else if (npc.life < npc.lifeMax * (FargoSoulsWorld.MasochistModeReal ? 0.9 : .75)) //enter phase 2 { InPhase2 = true; npc.netUpdate = true; NetSync(npc); if (!Main.dedServ) { Main.PlaySound(mod.GetLegacySoundSlot(SoundType.Custom, "Sounds/Monster94"), npc.HasValidTarget && Main.player[npc.target].ZoneUnderworldHeight ? Main.player[npc.target].Center : npc.Center); if (Main.LocalPlayer.active) { Main.LocalPlayer.GetModPlayer <FargoPlayer>().Screenshake = 90; } } } if (npc.ai[3] == 2) //phase 3 { if (InDesperationPhase) { //ChainBarrageTimer -= 0.5f; //increment faster if (WorldEvilAttackCycleTimer % 2 == 1) //always make sure its even in here { WorldEvilAttackCycleTimer--; } } int floor = 240 - (InDesperationPhase ? 120 : 0); int ceiling = 600 - 180 - (InDesperationPhase ? 120 : 0); if (WorldEvilAttackCycleTimer >= floor && WorldEvilAttackCycleTimer <= ceiling) { if (--ChainBarrageTimer < 0) { ChainBarrageTimer = 80; if (npc.HasValidTarget && Main.player[npc.target].ZoneUnderworldHeight) { if (Main.netMode != NetmodeID.MultiplayerClient) //spawn reticles for chain barrages { Vector2 spawnPos = Main.player[npc.target].Center; float offset = 1000f * (ceiling - WorldEvilAttackCycleTimer) / (ceiling - floor); //progress further as attack continues spawnPos.X += Math.Sign(npc.velocity.X) * offset; spawnPos.Y += Main.rand.NextFloat(-100, 100); if (spawnPos.Y / 16 < Main.maxTilesY - 200) //clamp so it stays in hell { spawnPos.Y = (Main.maxTilesY - 200) * 16; } if (spawnPos.Y / 16 >= Main.maxTilesY) { spawnPos.Y = Main.maxTilesY * 16 - 16; } Projectile.NewProjectile(spawnPos, Vector2.Zero, ModContent.ProjectileType <WOFReticle>(), npc.damage / 6, 0f, Main.myPlayer); } } } } else { ChainBarrageTimer = 0; } } else if (npc.ai[3] == 1 && npc.life < npc.lifeMax * (FargoSoulsWorld.MasochistModeReal ? .8 : .5)) //enter phase 3 { npc.ai[3] = 2; npc.netUpdate = true; NetSync(npc); if (!Main.dedServ) { Main.PlaySound(mod.GetLegacySoundSlot(SoundType.Custom, "Sounds/Monster94"), npc.HasValidTarget && Main.player[npc.target].ZoneUnderworldHeight ? Main.player[npc.target].Center : npc.Center); if (Main.LocalPlayer.active) { Main.LocalPlayer.GetModPlayer <FargoPlayer>().Screenshake = 90; } } } if (npc.life < npc.lifeMax / (FargoSoulsWorld.MasochistModeReal ? 4 : 10)) //final phase { WorldEvilAttackCycleTimer++; if (!InDesperationPhase) { InDesperationPhase = true; //temporarily stop eyes from attacking during the transition to avoid accidental insta-lasers for (int i = 0; i < Main.maxNPCs; i++) { if (Main.npc[i].active && Main.npc[i].type == NPCID.WallofFleshEye && Main.npc[i].realLife == npc.whoAmI) { Main.npc[i].GetEModeNPCMod <WallofFleshEye>().PreventAttacks = 60; } } npc.netUpdate = true; NetSync(npc); if (!Main.dedServ) { Main.PlaySound(mod.GetLegacySoundSlot(SoundType.Custom, "Sounds/Monster5").WithVolume(1.5f), npc.HasValidTarget && Main.player[npc.target].ZoneUnderworldHeight ? Main.player[npc.target].Center : npc.Center); if (Main.LocalPlayer.active) { Main.LocalPlayer.GetModPlayer <FargoPlayer>().Screenshake = 180; } } } } float maxSpeed = FargoSoulsWorld.MasochistModeReal ? 4.5f : 3.5f; //don't let wof move faster than this normally if (npc.HasPlayerTarget && (Main.player[npc.target].dead || Vector2.Distance(npc.Center, Main.player[npc.target].Center) > 3000)) { npc.TargetClosest(true); if (Main.player[npc.target].dead || Vector2.Distance(npc.Center, Main.player[npc.target].Center) > 3000) { npc.position.X += 60 * Math.Sign(npc.velocity.X); //move faster to despawn } else if (Math.Abs(npc.velocity.X) > maxSpeed) { npc.position.X -= (Math.Abs(npc.velocity.X) - maxSpeed) * Math.Sign(npc.velocity.X); } } else if (Math.Abs(npc.velocity.X) > maxSpeed) { npc.position.X -= (Math.Abs(npc.velocity.X) - maxSpeed) * Math.Sign(npc.velocity.X); } if (Main.LocalPlayer.active & !Main.LocalPlayer.dead && !Main.LocalPlayer.ghost && Main.LocalPlayer.ZoneUnderworldHeight) { float velX = npc.velocity.X; if (velX > maxSpeed) { velX = maxSpeed; } else if (velX < -maxSpeed) { velX = -maxSpeed; } for (int i = 0; i < 10; i++) //dust { Vector2 dustPos = new Vector2(2000 * npc.direction, 0f).RotatedBy(Math.PI / 3 * (-0.5 + Main.rand.NextDouble())); int d = Dust.NewDust(npc.Center + dustPos, 0, 0, DustID.Fire); Main.dust[d].scale += 1f; Main.dust[d].velocity.X = velX; Main.dust[d].velocity.Y = npc.velocity.Y; Main.dust[d].noGravity = true; Main.dust[d].noLight = true; } if (++npc.localAI[1] > 15f) { npc.localAI[1] = 0f; //tongue the player if they're 2000-2800 units away if (Math.Abs(2400 - npc.Distance(Main.LocalPlayer.Center)) < 400) { if (!Main.LocalPlayer.tongued) { Main.PlaySound(SoundID.ForceRoar, Main.LocalPlayer.Center, -1); //eoc roar } Main.LocalPlayer.AddBuff(BuffID.TheTongue, 10); } } } EModeUtils.DropSummon(npc, ModContent.ItemType <FleshyDoll>(), Main.hardMode, ref DroppedSummon); }
public override void AI(NPC npc) { base.AI(npc); EModeGlobalNPC.cultBoss = npc.whoAmI; if (FargoSoulsWorld.SwarmActive) { return; } if (npc.ai[3] == -1f) { if (Fargowiltas.Instance.MasomodeEXLoaded && npc.ai[1] >= 120f && npc.ai[1] < 419f) //skip summoning ritual LMAO { npc.ai[1] = 419f; npc.netUpdate = true; } if (npc.ai[0] == 5) { if (npc.ai[1] == 1f) { RitualRotation = Main.rand.Next(360); npc.netUpdate = true; NetSync(npc); } if (npc.ai[1] > 30f && npc.ai[1] < 330f) { RitualRotation += 2f - Math.Min(1f, 2f * npc.life / npc.lifeMax); //always at least 1, begins scaling below 50% life npc.Center = Main.player[npc.target].Center + 180f * Vector2.UnitX.RotatedBy(MathHelper.ToRadians(RitualRotation)); } /*if (npc.ai[1] > 275f && npc.ai[1] < 330f) //dust that reveals the real cultist at last second * { * float modifier = 0; * int repeats = (int)npc.ai[1] - 275; * for (int i = 0; i < repeats; i++) * modifier = MathHelper.Lerp(modifier, 1f, 0.08f); * float distance = npc.height * 2 * modifier; * float rotation = MathHelper.TwoPi * modifier; * for (int i = 0; i < 4; i++) * { * int d = Dust.NewDust(npc.Center + distance * Vector2.UnitX.RotatedBy(rotation + MathHelper.TwoPi / 4 * i), 0, 0, 88, newColor: Color.White); * Main.dust[d].noGravity = true; * Main.dust[d].velocity *= npc.ai[1] > 315 ? 18f : 0.5f; * Main.dust[d].scale = 0.5f + 2.5f * modifier; * } * }*/ } } else { //if (npc.ai[3] == 0) npc.damage = 0; //about to begin moving for ritual: 0 39 0 12 //begin transit for ritual: 1 34 0 12 //pause just before ritual: 0 0 0 13 //ritual: 5 0 0 -1 if (!EnteredPhase2 && npc.life < npc.lifeMax / 2) //p2 transition, force a ritual immediately { EnteredPhase2 = true; npc.ai[0] = 5; npc.ai[1] = 0; npc.ai[2] = 0; npc.ai[3] = -1; Main.PlaySound(SoundID.Roar, npc.Center, 0); if (Main.netMode != NetmodeID.MultiplayerClient) { npc.netUpdate = true; NetSync(npc); } } int damage = Math.Max(npc.damage, 75); //necessary because calameme switch ((int)npc.ai[0]) { case -1: if (npc.ai[1] == 419f) //always start fight with a ritual { npc.ai[0] = 0f; npc.ai[1] = 0f; npc.ai[3] = 11f; npc.netUpdate = true; } break; case 2: //ice mist, frost wave support if (EnteredPhase2) { if (npc.ai[1] < 60 && npc.ai[1] % 4 == 3) { int spacing = 14 - (int)(npc.ai[1] - 3) / 4; //start far and get closer for (int j = -1; j <= 1; j += 2) //from above and below { for (int i = -1; i <= 1; i += 2) //waves beside you { if (i == 0) { continue; } Vector2 spawnPos = Main.player[npc.target].Center; spawnPos.X += Math.Sign(i) * 150 * 2 + i * 120 * spacing; spawnPos.Y -= (700 + Math.Abs(i) * 50) * j; float speed = 8 + spacing * 0.8f; if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(spawnPos, Vector2.UnitY * speed * j, ProjectileID.FrostWave, damage / 3, 0f, Main.myPlayer); } } } } } else { if (npc.ai[1] == (FargoSoulsWorld.MasochistModeReal ? 5f : 60f) && Main.netMode != NetmodeID.MultiplayerClient) //single wave { for (int i = 0; i < Main.maxNPCs; i++) { if (Main.npc[i].active && Main.npc[i].type == NPCID.CultistBossClone) { Vector2 distance = Main.player[npc.target].Center - Main.npc[i].Center; distance.Normalize(); distance *= Main.rand.NextFloat(8f, 9f); distance = distance.RotatedByRandom(Math.PI / 24); Projectile.NewProjectile(Main.npc[i].Center, distance, ProjectileID.FrostWave, damage / 3, 0f, Main.myPlayer); } } } } break; case 3: //fireballs if (Main.netMode != NetmodeID.MultiplayerClient) { if (EnteredPhase2) //fireball ring { if (npc.ai[1] == 3f) { int max = NPC.CountNPCS(NPCID.CultistBossClone) * 2 + 6; Vector2 baseOffset = npc.DirectionTo(Main.player[npc.target].Center); const float spawnOffset = 1200f; const float speed = 7f; const float ai0 = spawnOffset / speed; for (int i = 0; i < max; i++) { Projectile.NewProjectile(Main.player[npc.target].Center + spawnOffset * baseOffset.RotatedBy(2 * Math.PI / max * i), -speed * baseOffset.RotatedBy(2 * Math.PI / max * i), ModContent.ProjectileType <CultistFireball>(), damage / 3, 0f, Main.myPlayer, ai0); } Projectile.NewProjectile(npc.Center, Vector2.Zero, ModContent.ProjectileType <GlowRing>(), 0, 0f, Main.myPlayer, npc.whoAmI, npc.type); } } if (npc.ai[1] % 20 == 6) //homing flare support { for (int i = 0; i < Main.maxNPCs; i++) { if (Main.npc[i].active && Main.npc[i].type == NPCID.CultistBossClone) { int n = NPC.NewNPC((int)Main.npc[i].Center.X, (int)Main.npc[i].Center.Y, NPCID.SolarFlare, Target: npc.target); if (n != Main.maxNPCs && Main.netMode == NetmodeID.Server) { NetMessage.SendData(MessageID.SyncNPC, number: n); } } } } } break; case 4: //lightning if (npc.ai[1] == 19f && npc.HasPlayerTarget && Main.netMode != NetmodeID.MultiplayerClient) { int cultistCount = 1; for (int i = 0; i < Main.maxNPCs; i++) { if (Main.npc[i].active && Main.npc[i].type == NPCID.CultistBossClone) { if (EnteredPhase2) //vortex lightning { Projectile.NewProjectile(Main.npc[i].Center, Main.rand.NextVector2Square(-15, 15), ModContent.ProjectileType <CultistVortex>(), damage / 15 * 6, 0, Main.myPlayer, 0f, cultistCount); cultistCount++; } else //aimed lightning { if (FargoSoulsWorld.MasochistModeReal) { Vector2 dir = Main.player[npc.target].Center - Main.npc[i].Center; float ai1New = Main.rand.Next(100); Vector2 vel = Vector2.Normalize(dir.RotatedByRandom(Math.PI / 4)) * 6f; Projectile.NewProjectile(Main.npc[i].Center, vel, ModContent.ProjectileType <HostileLightning>(), damage / 15 * 6, 0, Main.myPlayer, dir.ToRotation(), ai1New); } else { Vector2 vel = Main.npc[i].DirectionTo(Main.player[npc.target].Center).RotatedByRandom(MathHelper.ToRadians(5)); vel *= Main.rand.NextFloat(4f, 6f); Projectile.NewProjectile(Main.npc[i].Center, vel, ModContent.ProjectileType <LightningVortexHostile>(), damage / 15 * 6, 0, Main.myPlayer); } } } } } break; case 7: if (npc.ai[1] == 3f && Main.netMode != NetmodeID.MultiplayerClient) //ancient light, jellyfish support { for (int i = 0; i < Main.maxProjectiles; i++) { if (Main.projectile[i].active && Main.projectile[i].type == ModContent.ProjectileType <CultistRitual>()) { Projectile.NewProjectile(new Vector2(Main.projectile[i].Center.X, Main.player[npc.target].Center.Y - 700), Vector2.Zero, ModContent.ProjectileType <StardustRain>(), damage / 3, 0f, Main.myPlayer); } } } break; case 8: if (npc.ai[1] == 3f) //ancient doom, nebula sphere support { int t = npc.HasPlayerTarget ? npc.target : npc.FindClosestPlayer(); if (t != -1 && Main.player[t].active) { for (int i = 0; i < Main.maxNPCs; i++) { if (Main.npc[i].active && Main.npc[i].type == NPCID.CultistBossClone) { Projectile.NewProjectile(Main.npc[i].Center, Vector2.Zero, ProjectileID.NebulaSphere, damage / 15 * 6, 0f, Main.myPlayer); } } } } break; default: break; } } npc.defense = npc.defDefense; //prevent vanilla p2 from lowering defense! Lighting.AddLight(npc.Center, 1f, 1f, 1f); EModeUtils.DropSummon(npc, ModContent.ItemType <CultistSummon>(), NPC.downedAncientCultist, ref DroppedSummon, NPC.downedGolemBoss); }
public override void AI(NPC npc) { EModeGlobalNPC.moonBoss = npc.whoAmI; if (FargoSoulsWorld.SwarmActive) { return; } if (!SpawnedRituals) { SpawnedRituals = true; VulnerabilityState = 0; if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.Center, Vector2.Zero, ModContent.ProjectileType <LunarRitual>(), 25, 0f, Main.myPlayer, 0f, npc.whoAmI); Projectile.NewProjectile(npc.Center, Vector2.Zero, ModContent.ProjectileType <FragmentRitual>(), 0, 0f, Main.myPlayer, 0f, npc.whoAmI); } } if (Main.LocalPlayer.active && !Main.LocalPlayer.dead && !Main.LocalPlayer.ghost && VulnerabilityState >= 0 && VulnerabilityState <= 3) { Main.LocalPlayer.AddBuff(ModContent.BuffType <NullificationCurse>(), 2); } npc.position -= npc.velocity * 2f / 3f; //SLOW DOWN if (npc.dontTakeDamage) { if (AttackTimer == 370 && Main.netMode != NetmodeID.MultiplayerClient) { for (int i = 0; i < 3; i++) { NPC bodyPart = Main.npc[(int)npc.localAI[i]]; if (bodyPart.active) { Projectile.NewProjectile(bodyPart.Center, Vector2.Zero, ModContent.ProjectileType <GlowRing>(), 0, 0f, Main.myPlayer, bodyPart.whoAmI, bodyPart.type); } } } if (AttackTimer > 400) { AttackTimer = 0; npc.netUpdate = true; NetSync(npc); if (Main.netMode != NetmodeID.MultiplayerClient) { switch (VulnerabilityState) { case 0: //melee for (int i = 0; i < 3; i++) { NPC bodyPart = Main.npc[(int)npc.localAI[i]]; if (bodyPart.active) { int damage = 30; for (int j = -2; j <= 2; j++) { Projectile.NewProjectile(bodyPart.Center, 6f * bodyPart.DirectionFrom(Main.player[npc.target].Center).RotatedBy(Math.PI / 2 / 4 * j), ModContent.ProjectileType <MoonLordFireball>(), damage, 0f, Main.myPlayer, 20, 20 + 60); } } } break; case 1: //ranged for (int j = 0; j < 6; j++) { Vector2 spawn = Main.player[npc.target].Center + 500 * npc.DirectionFrom(Main.player[npc.target].Center).RotatedBy(MathHelper.TwoPi / 6 * (j + 0.5f)); Projectile.NewProjectile(spawn, Vector2.Zero, ModContent.ProjectileType <LightningVortexHostile>(), 30, 0f, Main.myPlayer, 1, Main.player[npc.target].DirectionFrom(spawn).ToRotation()); } break; case 2: //magic for (int i = 0; i < 3; i++) { NPC bodyPart = Main.npc[(int)npc.localAI[i]]; if (bodyPart.active && ((i == 2 && bodyPart.type == NPCID.MoonLordHead) || bodyPart.type == NPCID.MoonLordHand)) { int damage = 35; const int max = 6; for (int j = 0; j < max; j++) { int p = Projectile.NewProjectile(bodyPart.Center, 2.5f * bodyPart.DirectionFrom(Main.player[npc.target].Center).RotatedBy(Math.PI * 2 / max * (j + 0.5)), ModContent.ProjectileType <MoonLordNebulaBlaze>(), damage, 0f, Main.myPlayer); if (p != Main.maxProjectiles) { Main.projectile[p].timeLeft = 1200; } } } } break; case 3: //summoner for (int i = 0; i < 3; i++) { NPC bodyPart = Main.npc[(int)npc.localAI[i]]; if (bodyPart.active && ((i == 2 && bodyPart.type == NPCID.MoonLordHead) || bodyPart.type == NPCID.MoonLordHand)) { Vector2 speed = Main.player[npc.target].Center - bodyPart.Center; speed.Normalize(); speed *= 5f; for (int j = -1; j <= 1; j++) { Vector2 vel = speed.RotatedBy(MathHelper.ToRadians(15) * j); int n = NPC.NewNPC((int)bodyPart.Center.X, (int)bodyPart.Center.Y, NPCID.AncientLight, 0, 0f, (Main.rand.NextFloat() - 0.5f) * 0.3f * 6.28318548202515f / 60f, vel.X, vel.Y); if (n != Main.maxNPCs) { Main.npc[n].velocity = vel; Main.npc[n].netUpdate = true; if (Main.netMode == NetmodeID.Server) { NetMessage.SendData(MessageID.SyncNPC, -1, -1, null, n); } } } } } break; default: //phantasmal eye rings if (Main.netMode != NetmodeID.MultiplayerClient) { const int max = 4; const int speed = 8; const float rotationModifier = 0.5f; int damage = 40; float rotation = 2f * (float)Math.PI / max; Vector2 vel = Vector2.UnitY * speed; int type = ModContent.ProjectileType <Projectiles.MutantBoss.MutantSphereRing>(); for (int i = 0; i < max; i++) { vel = vel.RotatedBy(rotation); int p = Projectile.NewProjectile(npc.Center, vel, type, damage, 0f, Main.myPlayer, rotationModifier, speed); if (p != Main.maxProjectiles) { Main.projectile[p].timeLeft = 1800 - VulnerabilityTimer; } p = Projectile.NewProjectile(npc.Center, vel, type, damage, 0f, Main.myPlayer, -rotationModifier, speed); if (p != Main.maxProjectiles) { Main.projectile[p].timeLeft = 1800 - VulnerabilityTimer; } } Main.PlaySound(SoundID.Item84, npc.Center); } break; } } } } else //only when vulnerable { if (!EnteredPhase2) { EnteredPhase2 = true; AttackTimer = 0; Main.PlaySound(SoundID.Roar, Main.LocalPlayer.Center, 0); npc.netUpdate = true; NetSync(npc); } Player player = Main.player[npc.target]; switch (VulnerabilityState) { case 0: //melee { if (AttackTimer > 30) { AttackTimer -= 300; AttackMemory = AttackMemory == 0 ? 1 : 0; float handToAttackWith = npc.localAI[AttackMemory]; if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(Main.npc[(int)handToAttackWith].Center, Vector2.Zero, ModContent.ProjectileType <MoonLordSun>(), 60, 0f, Main.myPlayer, npc.whoAmI, handToAttackWith); } } } break; case 1: //vortex { if (AttackMemory == 0) //spawn the vortex { AttackMemory = 1; for (int i = -1; i <= 1; i += 2) { if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.Center, Vector2.Zero, ModContent.ProjectileType <MoonLordVortex>(), 40, 0f, Main.myPlayer, i, npc.whoAmI); } } } } break; case 2: //nebula { if (AttackTimer > 30) { AttackTimer -= 360; for (int i = 0; i < 3; i++) { NPC bodyPart = Main.npc[(int)npc.localAI[i]]; int damage = 35; for (int j = -2; j <= 2; j++) { if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(bodyPart.Center, 2.5f * bodyPart.DirectionFrom(Main.player[npc.target].Center).RotatedBy(Math.PI / 2 / 2 * (j + Main.rand.NextFloat(-0.25f, 0.25f))), ModContent.ProjectileType <MoonLordNebulaBlaze2>(), damage, 0f, Main.myPlayer, npc.whoAmI); } } } } } break; case 3: //stardust { if (AttackTimer > 360) { AttackTimer -= 360; AttackMemory = 0; } float baseRotation = MathHelper.ToRadians(50); if (++AttackMemory == 10) { if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(Main.npc[(int)npc.localAI[0]].Center, Main.npc[(int)npc.localAI[0]].DirectionTo(player.Center), ModContent.ProjectileType <PhantasmalDeathrayMLSmall>(), 60, 0f, Main.myPlayer, baseRotation * Main.rand.NextFloat(0.9f, 1.1f), npc.localAI[0]); } } else if (AttackMemory == 20) { if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(Main.npc[(int)npc.localAI[1]].Center, Main.npc[(int)npc.localAI[2]].DirectionTo(player.Center), ModContent.ProjectileType <PhantasmalDeathrayMLSmall>(), 60, 0f, Main.myPlayer, -baseRotation * Main.rand.NextFloat(0.9f, 1.1f), npc.localAI[1]); } } else if (AttackMemory == 30) { if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(Main.npc[(int)npc.localAI[2]].Center, Main.npc[(int)npc.localAI[1]].DirectionTo(player.Center), ModContent.ProjectileType <PhantasmalDeathrayMLSmall>(), 60, 0f, Main.myPlayer, baseRotation * Main.rand.NextFloat(0.9f, 1.1f), npc.localAI[2]); } } else if (AttackMemory == 40) { if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.Center, npc.DirectionTo(player.Center), ModContent.ProjectileType <PhantasmalDeathrayMLSmall>(), 60, 0f, Main.myPlayer, -baseRotation * Main.rand.NextFloat(0.9f, 1.1f), npc.whoAmI); } } } break; default: //any { if (AttackMemory == 0) //spawn the moons { AttackMemory = 1; foreach (Projectile p in Main.projectile.Where(p => p.active && p.hostile)) { if (p.type == ModContent.ProjectileType <LunarRitual>() && p.ai[1] == npc.whoAmI) //find my arena { if (Main.netMode != NetmodeID.MultiplayerClient) { for (int i = 0; i < 4; i++) { Projectile.NewProjectile(npc.Center, p.DirectionTo(Main.player[npc.target].Center).RotatedBy(MathHelper.TwoPi / 4 * i), ModContent.ProjectileType <MoonLordMoon>(), 60, 0f, Main.myPlayer, p.identity, 1450); } for (int i = 0; i < 4; i++) { Projectile.NewProjectile(npc.Center, p.DirectionTo(Main.player[npc.target].Center).RotatedBy(MathHelper.TwoPi / 4 * (i + 0.5f)), ModContent.ProjectileType <MoonLordMoon>(), 60, 0f, Main.myPlayer, p.identity, -950); } } break; } } } } break; } } if (npc.ai[0] == 2f) //moon lord is dead { VulnerabilityState = 4; VulnerabilityTimer = 0; AttackTimer = 0; } else //moon lord isn't dead { int increment = (int)Math.Max(1, (1f - (float)npc.life / npc.lifeMax) * 4); if (FargoSoulsWorld.MasochistModeReal) { increment++; } VulnerabilityTimer += increment; AttackTimer += increment; if (VulnerabilityTimer > 1800) { VulnerabilityState = ++VulnerabilityState % 5; VulnerabilityTimer = 0; AttackTimer = 0; AttackMemory = 0; npc.netUpdate = true; NetSync(npc); } } switch (VulnerabilityState) { case 0: Main.monolithType = 3; break; case 1: Main.monolithType = 0; break; case 2: Main.monolithType = 1; break; case 3: Main.monolithType = 2; if (VulnerabilityTimer < 120) //so that player isn't punished for using weapons during prior phase { Main.LocalPlayer.GetModPlayer <FargoPlayer>().MasomodeMinionNerfTimer = 0; } break; default: break; } EModeUtils.DropSummon(npc, ModContent.ItemType <CelestialSigil2>(), NPC.downedMoonlord, ref DroppedSummon, NPC.downedAncientCultist); }
public override bool PreAI(NPC npc) { EModeGlobalNPC.slimeBoss = npc.whoAmI; npc.color = Main.DiscoColor * 0.3f; // Rainbow colour if (FargoSoulsWorld.SwarmActive) { return(true); } if (FargoSoulsWorld.MasochistModeReal) { npc.position.X += npc.velocity.X * 0.2f; } // Attack that happens when landing if (LandingAttackReady) { if (npc.velocity.Y == 0f) { LandingAttackReady = false; if (Main.netMode != NetmodeID.MultiplayerClient) { if (FargoSoulsWorld.MasochistModeReal) { for (int i = 0; i < 30; i++) //spike spray { Projectile.NewProjectile(npc.GetSource_FromThis(), new Vector2(npc.Center.X + Main.rand.Next(-5, 5), npc.Center.Y - 15), new Vector2(Main.rand.NextFloat(-6, 6), Main.rand.NextFloat(-8, -5)), ProjectileID.SpikedSlimeSpike, FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer); } } if (npc.HasValidTarget) { SoundEngine.PlaySound(SoundID.Item21, Main.player[npc.target].Center); if (Main.netMode != NetmodeID.MultiplayerClient) { for (int i = 0; i < 6; i++) { Vector2 spawn = Main.player[npc.target].Center; spawn.X += Main.rand.Next(-150, 151); spawn.Y -= Main.rand.Next(600, 901); Vector2 speed = Main.player[npc.target].Center - spawn; speed.Normalize(); speed *= IsBerserk ? 10f : 5f; speed = speed.RotatedByRandom(MathHelper.ToRadians(4)); Projectile.NewProjectile(npc.GetSource_FromThis(), spawn, speed, ModContent.ProjectileType <SlimeBallHostile>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage, 4f / 6), 0f, Main.myPlayer); } } } } } } else if (npc.velocity.Y > 0) { // If they're in the air, flag that the landing attack should be used next time they land LandingAttackReady = true; } if (npc.velocity.Y < 0) // Jumping up { if (!CurrentlyJumping) // Once per jump... { CurrentlyJumping = true; bool shootSpikes = false; if (FargoSoulsWorld.MasochistModeReal) { shootSpikes = true; } // If player is well above me, jump higher and spray spikes if (npc.HasValidTarget && Main.player[npc.target].Center.Y < npc.position.Y + npc.height - 240) { npc.velocity.Y *= 2f; shootSpikes = true; } if (shootSpikes && Main.netMode != NetmodeID.MultiplayerClient) { const float gravity = 0.15f; float time = 90f; Vector2 distance = Main.player[npc.target].Center - npc.Center + Main.player[npc.target].velocity * 30f; distance.X = distance.X / time; distance.Y = distance.Y / time - 0.5f * gravity * time; for (int i = 0; i < 15; i++) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, distance + Main.rand.NextVector2Square(-1f, 1f), ModContent.ProjectileType <SlimeSpike>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer); } } } } else { CurrentlyJumping = false; } if ((IsBerserk || npc.life < npc.lifeMax * .5f) && npc.HasValidTarget) { if (--SpikeRainCounter < 0) // Spike rain { SpikeRainCounter = 240; if (Main.netMode != NetmodeID.MultiplayerClient) { for (int i = -12; i <= 12; i++) { Vector2 spawnPos = Main.player[npc.target].Center; spawnPos.X += 110 * i; spawnPos.Y -= 500; Projectile.NewProjectile(npc.GetSource_FromThis(), spawnPos, (IsBerserk ? 6f : 0f) * Vector2.UnitY, ModContent.ProjectileType <SlimeSpike2>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage, 4f / 6), 0f, Main.myPlayer); } } } } /*if (!masoBool[0]) //is not berserk * { * SharkCount = 0; * * if (npc.HasPlayerTarget) * { * Player player = Main.player[npc.target]; * if (player.active && !player.dead && player.Center.Y < npc.position.Y && npc.Distance(player.Center) < 1000f) * { * Counter[1]++; //timer runs if player is above me and nearby * if (Counter[1] >= 600 && Main.netMode != NetmodeID.MultiplayerClient) //go berserk * { * masoBool[0] = true; * npc.netUpdate = true; * NetUpdateMaso(npc.whoAmI); * if (Main.netMode == NetmodeID.Server) * ChatHelper.BroadcastChatMessage(NetworkText.FromLiteral("King Slime has enraged!"), new Color(175, 75, 255)); * else * Main.NewText("King Slime has enraged!", 175, 75, 255); * } * } * else * { * Counter[1] = 0; * } * } * } * else //is berserk * { * SharkCount = 1; * * if (!masoBool[2]) * { * masoBool[2] = true; * SoundEngine.PlaySound(SoundID.Roar, npc.Center); * } * * if (Counter[0] > 45) //faster slime spike rain * Counter[0] = 45; * * if (++Counter[2] > 30) //aimed spikes * { * Counter[2] = 0; * const float gravity = 0.15f; * float time = 45f; * Vector2 distance = Main.player[npc.target].Center - npc.Center + Main.player[npc.target].velocity * 30f; * distance.X = distance.X / time; * distance.Y = distance.Y / time - 0.5f * gravity * time; * for (int i = 0; i < 15; i++) * { * Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, distance + Main.rand.NextVector2Square(-1f, 1f) * 2f, * ModContent.ProjectileType<SlimeSpike>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer); * } * } * * if (npc.HasValidTarget && Main.netMode != NetmodeID.MultiplayerClient && Main.player[npc.target].position.Y > npc.position.Y) //player went back down * { * masoBool[0] = false; * masoBool[2] = false; * NetUpdateMaso(npc.whoAmI); * } * }*/ if (npc.life < npc.lifeMax / 2) { if (npc.ai[1] == 5) //when teleporting { if (npc.ai[0] == 1 && !DidP2SpecialTeleport) { SoundEngine.PlaySound(SoundID.Roar, npc.Center); } if (npc.HasPlayerTarget) //live update tp position { if (DidP2SpecialTeleport) { if (npc.ai[0] == 1) //only update y pos once { npc.localAI[2] = Main.player[npc.target].Center.Y; } } else { Vector2 desiredTeleport = Main.player[npc.target].Center; desiredTeleport.X += 800 * System.Math.Sign(Main.player[npc.target].Center.X - npc.Center.X); //tp ahead of player if (Collision.CanHitLine(desiredTeleport, 0, 0, Main.player[npc.target].position, Main.player[npc.target].width, Main.player[npc.target].height)) { npc.localAI[1] = desiredTeleport.X; npc.localAI[2] = desiredTeleport.Y; } } } } else if (npc.ai[1] == 6) //actually did the teleport and now regrowing { DidP2SpecialTeleport = true; } else { if (!DidP2SpecialTeleport) { npc.ai[2] += 60; } npc.ai[2] += 1f / 3f; //always increment the teleport timer } } // Drop summon EModeUtils.DropSummon(npc, "SlimyCrown", NPC.downedSlimeKing, ref DroppedSummon); return(base.PreAI(npc)); }
public override bool PreAI(NPC npc) { bool result = base.PreAI(npc); IsVenomEnraged = false; if (FargoSoulsWorld.SwarmActive) { return(result); } if (!npc.HasValidTarget) { npc.velocity.Y++; } const float innerRingDistance = 130f; const int delayForRingToss = 360 + 120; if (--RingTossTimer < 0) { RingTossTimer = delayForRingToss; if (Main.netMode != NetmodeID.MultiplayerClient && !Main.npc.Any(n => n.active && n.type == ModContent.NPCType <CrystalLeaf>() && n.ai[0] == npc.whoAmI && n.ai[1] == innerRingDistance)) { const int max = 5; float rotation = 2f * (float)Math.PI / max; for (int i = 0; i < max; i++) { Vector2 spawnPos = npc.Center + new Vector2(innerRingDistance, 0f).RotatedBy(rotation * i); FargoSoulsUtil.NewNPCEasy(npc.GetSource_FromAI(), spawnPos, ModContent.NPCType <CrystalLeaf>(), 0, npc.whoAmI, innerRingDistance, 0, rotation * i); } } } else if (RingTossTimer == 120) { if (FargoSoulsWorld.MasochistModeReal) { RingTossTimer = 0; //instantly spawn next set of crystals } npc.netUpdate = true; NetSync(npc); if (Main.netMode != NetmodeID.MultiplayerClient) { float speed = 8f; int p = Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, speed * npc.DirectionTo(Main.player[npc.target].Center), ModContent.ProjectileType <MutantMark2>(), npc.defDamage / 4, 0f, Main.myPlayer); if (p != Main.maxProjectiles) { Main.projectile[p].timeLeft -= 300; foreach (NPC n in Main.npc.Where(n => n.active && n.type == ModContent.NPCType <CrystalLeaf>() && n.ai[0] == npc.whoAmI && n.ai[1] == innerRingDistance)) //my crystal leaves { SoundEngine.PlaySound(SoundID.Grass, n.Center); Projectile.NewProjectile(npc.GetSource_FromThis(), n.Center, Vector2.Zero, ModContent.ProjectileType <PlanteraCrystalLeafRing>(), npc.defDamage / 4, 0f, Main.myPlayer, Main.projectile[p].identity, n.ai[3]); n.life = 0; n.HitEffect(); n.checkDead(); n.active = false; if (Main.netMode == NetmodeID.Server) { NetMessage.SendData(MessageID.SyncNPC, -1, -1, null, n.whoAmI); } } } } } if (npc.life > npc.lifeMax / 2) { if (--DicerTimer < 0) { DicerTimer = 150 * 4 + 25; if (FargoSoulsWorld.MasochistModeReal && npc.HasValidTarget && Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), Main.player[npc.target].Center, Vector2.Zero, ModContent.ProjectileType <DicerPlantera>(), npc.defDamage / 4, 0f, Main.myPlayer, 0, 0); for (int i = 0; i < 3; i++) { Projectile.NewProjectile(npc.GetSource_FromThis(), Main.player[npc.target].Center, 30f * npc.DirectionTo(Main.player[npc.target].Center).RotatedBy(2 * (float)Math.PI / 3 * i), ModContent.ProjectileType <DicerPlantera>(), npc.defDamage / 4, 0f, Main.myPlayer, 1, 1); } } } } else { if (!InPhase2) { InPhase2 = true; DicerTimer = 0; } void SpawnOuterLeafRing() { const int max = 12; const float distance = 250; float rotation = 2f * (float)Math.PI / max; for (int i = 0; i < max; i++) { Vector2 spawnPos = npc.Center + new Vector2(distance, 0f).RotatedBy(rotation * i); FargoSoulsUtil.NewNPCEasy(npc.GetSource_FromAI(), spawnPos, ModContent.NPCType <CrystalLeaf>(), 0, npc.whoAmI, distance, 0, rotation * i); } } if (!EnteredPhase2) { EnteredPhase2 = true; if (Main.netMode != NetmodeID.MultiplayerClient) { if (!Main.npc.Any(n => n.active && n.type == ModContent.NPCType <CrystalLeaf>() && n.ai[0] == npc.whoAmI && n.ai[1] == innerRingDistance)) { const int innerMax = 5; float innerRotation = 2f * (float)Math.PI / innerMax; for (int i = 0; i < innerMax; i++) { Vector2 spawnPos = npc.Center + new Vector2(innerRingDistance, 0f).RotatedBy(innerRotation * i); FargoSoulsUtil.NewNPCEasy(npc.GetSource_FromAI(), spawnPos, ModContent.NPCType <CrystalLeaf>(), 0, npc.whoAmI, innerRingDistance, 0, innerRotation * i); } } SpawnOuterLeafRing(); for (int i = 0; i < Main.maxProjectiles; i++) { if (Main.projectile[i].active && Main.projectile[i].hostile && (Main.projectile[i].type == ProjectileID.ThornBall || Main.projectile[i].type == ModContent.ProjectileType <DicerPlantera>() || Main.projectile[i].type == ModContent.ProjectileType <PlanteraCrystalLeafRing>() || Main.projectile[i].type == ModContent.ProjectileType <CrystalLeafShot>())) { Main.projectile[i].Kill(); } } } } //explode time * explode repetitions + spread delay * propagations const int delayForDicers = 150 * 4 + 25 * 8; if (--DicerTimer < -120) { DicerTimer = delayForDicers + delayForRingToss + 240; //Counter3 = delayForDicers + 120; //extra compensation for the toss offset npc.netUpdate = true; NetSync(npc); if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, Vector2.Zero, ModContent.ProjectileType <DicerPlantera>(), npc.defDamage / 4, 0f, Main.myPlayer); for (int i = 0; i < 3; i++) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, 25f * npc.DirectionTo(Main.player[npc.target].Center).RotatedBy(2 * (float)Math.PI / 3 * i), ModContent.ProjectileType <DicerPlantera>(), npc.defDamage / 4, 0f, Main.myPlayer, 1, 8); } } } if (DicerTimer > delayForDicers || DicerTimer < 0) { if (RingTossTimer > 120) //to still respawn the leaf ring if it's missing but disable throwing it { RingTossTimer = 120; } } else if (DicerTimer < delayForDicers) { RingTossTimer -= 1; if (RingTossTimer % 2 == 0) //make sure plantera can get the timing for its check { RingTossTimer--; } } else if (DicerTimer == delayForDicers) { RingTossTimer = 121; //activate it immediately as the mines fade } IsVenomEnraged = npc.HasPlayerTarget && Main.player[npc.target].venom; if (--TentacleTimer <= 0) { float slowdown = Math.Min(0.9f, -TentacleTimer / 60f); if (FargoSoulsWorld.MasochistModeReal && slowdown > 0.75f) { slowdown = 0.75f; } npc.position -= npc.velocity * slowdown; if (TentacleTimer == 0) { TentacleAttackAngleOffset = Main.rand.NextFloat(MathHelper.TwoPi); SoundEngine.PlaySound(SoundID.Roar, npc.Center); npc.netUpdate = true; NetSync(npc); foreach (NPC n in Main.npc.Where(n => n.active && n.type == ModContent.NPCType <CrystalLeaf>() && n.ai[0] == npc.whoAmI && n.ai[1] > innerRingDistance)) //my crystal leaves { SoundEngine.PlaySound(SoundID.Grass, n.Center); n.life = 0; n.HitEffect(); n.checkDead(); n.active = false; if (Main.netMode == NetmodeID.Server) { NetMessage.SendData(MessageID.SyncNPC, -1, -1, null, n.whoAmI); } } } const int maxTime = 30; const int interval = 3; float maxDegreeCoverage = 45f; //on either side of the middle, the full coverage of one side is x2 this if (TentacleTimer >= -maxTime && TentacleTimer % interval == 0) { int tentacleSpawnOffset = Math.Abs(TentacleTimer) / interval; for (int i = -tentacleSpawnOffset; i <= tentacleSpawnOffset; i += tentacleSpawnOffset * 2) { float attackAngle = MathHelper.WrapAngle( TentacleAttackAngleOffset + MathHelper.ToRadians(maxDegreeCoverage / (maxTime / interval)) * (i + Main.rand.NextFloat(-0.5f, 0.5f)) ); if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, Main.rand.NextVector2CircularEdge(24, 24), ModContent.ProjectileType <PlanteraTentacle>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer, npc.whoAmI, attackAngle); Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, Main.rand.NextVector2CircularEdge(24, 24), ModContent.ProjectileType <PlanteraTentacle>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer, npc.whoAmI, attackAngle + MathHelper.Pi); } if (i == 0) { break; } } } if (TentacleTimer < -390) { TentacleTimer = 600 + Main.rand.Next(120); if (!FargoSoulsWorld.MasochistModeReal) { npc.velocity = Vector2.Zero; } npc.netUpdate = true; NetSync(npc); SpawnOuterLeafRing(); } } else { npc.position -= npc.velocity * (IsVenomEnraged ? 0.1f : 0.2f); } if (FargoSoulsWorld.MasochistModeReal && --TentacleTimerMaso < 0) { TentacleTimerMaso = 420; if (Main.netMode != NetmodeID.MultiplayerClient) { float angle = npc.DirectionTo(Main.player[npc.target].Center).ToRotation(); for (int i = -1; i <= 1; i++) { float offset = MathHelper.ToRadians(6) * i; Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, Main.rand.NextVector2CircularEdge(24, 24), ModContent.ProjectileType <PlanteraTentacle>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer, npc.whoAmI, angle + offset); Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, Main.rand.NextVector2CircularEdge(24, 24), ModContent.ProjectileType <PlanteraTentacle>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0f, Main.myPlayer, npc.whoAmI, -angle + offset); } } } } EModeUtils.DropSummon(npc, "PlanterasFruit", NPC.downedPlantBoss, ref DroppedSummon, NPC.downedMechBoss1 && NPC.downedMechBoss2 && NPC.downedMechBoss3); return(result); }
public override bool PreAI(NPC npc) { bool result = base.PreAI(npc); EModeGlobalNPC.deerBoss = npc.whoAmI; if (FargoSoulsWorld.SwarmActive) { return(result); } const int MaxBerserkTime = 600; BerserkSpeedupTimer -= 1; if (npc.localAI[3] > 0 || EnteredPhase3) { npc.localAI[2]++; //cry about it } const int TeleportThreshold = 780; if (npc.ai[0] != 0) { npc.alpha -= 10; if (npc.alpha < 0) { npc.alpha = 0; } if (EnteredPhase3) { npc.localAI[2]++; } } TeleportTimer++; if (EnteredPhase3) { TeleportTimer++; } if (Main.LocalPlayer.active && !Main.LocalPlayer.ghost && !Main.LocalPlayer.dead && npc.Distance(Main.LocalPlayer.Center) < 1200) { Main.LocalPlayer.AddBuff(ModContent.BuffType <LowGround>(), 2); } switch ((int)npc.ai[0]) { case 0: //walking at player if (TeleportTimer < TeleportThreshold) { if (EnteredPhase3) { npc.position.X += npc.velocity.X; } if (npc.velocity.Y == 0) { if (EnteredPhase2) { npc.position.X += npc.velocity.X; } if (BerserkSpeedupTimer > 0) { npc.position.X += npc.velocity.X * 4f * BerserkSpeedupTimer / MaxBerserkTime; } } } if (EnteredPhase2) { if (!EnteredPhase3 && npc.life < npc.lifeMax * .33) { npc.ai[0] = 3; npc.ai[1] = 0; npc.netUpdate = true; break; } if (TeleportTimer > TeleportThreshold) { npc.velocity.X *= 0.9f; npc.dontTakeDamage = true; npc.localAI[1] = 0; //reset walls attack counter if (EnteredPhase2 && Main.LocalPlayer.active && !Main.LocalPlayer.ghost && !Main.LocalPlayer.dead) { FargoSoulsUtil.AddDebuffFixedDuration(Main.LocalPlayer, BuffID.Darkness, 2); FargoSoulsUtil.AddDebuffFixedDuration(Main.LocalPlayer, BuffID.Blackout, 2); } if (npc.alpha == 0) { SoundEngine.PlaySound(SoundID.Roar, npc.Center); if (Main.netMode != NetmodeID.MultiplayerClient) { const int max = 12; for (int i = 0; i < 12; i++) { Vector2 spawnPos = Main.player[npc.target].Center + 16 * Main.rand.NextFloat(6, 36) * Vector2.UnitX.RotatedBy(MathHelper.TwoPi / max * (i + Main.rand.NextFloat())); Projectile.NewProjectile(npc.GetSource_FromThis(), spawnPos, Vector2.Zero, ModContent.ProjectileType <DeerclopsHand>(), 0, 0f, Main.myPlayer, npc.target); } } } npc.alpha += 5; if (npc.alpha > 255) { npc.alpha = 255; npc.localAI[3] = 30; if (npc.HasPlayerTarget) //teleport { float distance = 16 * 14 * Math.Sign(npc.Center.X - Main.player[npc.target].Center.X); distance *= -1f; //alternate back and forth if (TeleportTimer == TeleportThreshold + 10) //introduce randomness { if (Main.rand.NextBool()) { distance *= -1f; } if (Main.netMode == NetmodeID.Server) { NetMessage.SendData(MessageID.SyncNPC, number: npc.whoAmI); } DoLaserAttack = !DoLaserAttack; //guarantee he alternates wall attacks at some point in the fight NetSync(npc); } npc.Bottom = Main.player[npc.target].Bottom + distance * Vector2.UnitX; npc.direction = Math.Sign(Main.player[npc.target].Center.X - npc.Center.X); npc.velocity.X = 3.4f * npc.direction; npc.velocity.Y = 0; int addedThreshold = 180; if (EnteredPhase3) { addedThreshold -= 30; } if (FargoSoulsWorld.MasochistModeReal) { addedThreshold -= 30; } if (TeleportTimer > TeleportThreshold + addedThreshold) { TeleportTimer = 0; npc.velocity.X = 0; npc.ai[0] = 4; npc.ai[1] = 0; NetSync(npc); if (Main.netMode == NetmodeID.Server) { NetMessage.SendData(MessageID.SyncNPC, number: npc.whoAmI); } } } } else { TeleportTimer = TeleportThreshold; if (npc.localAI[3] > 0) { npc.localAI[3] -= 3; //remove visual effect } } return(false); } } else if (npc.life < npc.lifeMax * .66) { npc.ai[0] = 3; npc.ai[1] = 0; npc.netUpdate = true; } break; case 1: //ice wave, npc.localai[1] counts them, attacks at ai1=30, last spike 52, ends at ai1=80 if (npc.ai[1] < 30) { if (FargoSoulsWorld.MasochistModeReal) { npc.ai[1] += 0.5f; npc.frameCounter += 0.5; } } break; case 2: //debris attack break; case 3: //roar at 30, ends at ai1=60 if (!FargoSoulsWorld.MasochistModeReal && npc.ai[1] < 30) { npc.ai[1] -= 0.5f; npc.frameCounter -= 0.5; } if (EnteredPhase2) { npc.localAI[1] = 0; //ensure this is always the same npc.localAI[3] = 30; //go invul if (npc.ai[1] > 30) { Main.dayTime = false; Main.time = 16200; //midnight, to help teleport visual } } else if (npc.life < npc.lifeMax * .66) { EnteredPhase2 = true; NetSync(npc); } if (EnteredPhase3) { if (!Main.dedServ) { Main.LocalPlayer.GetModPlayer <FargoSoulsPlayer>().Screenshake = 2; } if (npc.ai[1] > 30) //roaring { if (npc.HasValidTarget) //fly over player { npc.position = Vector2.Lerp(npc.position, Main.player[npc.target].Center - 450 * Vector2.UnitY, 0.2f); } } } else if (npc.life < npc.lifeMax * .33) { EnteredPhase3 = true; NetSync(npc); } if (EnteredPhase3 || FargoSoulsWorld.MasochistModeReal) { BerserkSpeedupTimer = MaxBerserkTime; } break; case 4: //both sides ice wave, attacks at ai1=50, last spike 70, ends at ai1=90 { int cooldown = 100; //stops deerclops from teleporting while old ice walls are still there if (EnteredPhase3) { cooldown *= 2; } if (TeleportTimer > TeleportThreshold - cooldown) { TeleportTimer = TeleportThreshold - cooldown; } if (EnteredPhase2 && npc.ai[1] == 0) { if (npc.alpha == 0) //i.e. dont randomize when coming out of tp { DoLaserAttack = Main.rand.NextBool(); } NetSync(npc); if (DoLaserAttack && Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, Vector2.Zero, ModContent.ProjectileType <GlowRing>(), 0, 0f, Main.myPlayer, npc.whoAmI, npc.type); } } Vector2 eye = npc.Center + new Vector2(64 * npc.direction, -24f) * npc.scale; if (FargoSoulsWorld.MasochistModeReal) { const int desiredStartup = 30; //effectively changes startup from 50 to this value const int threshold = 50 - desiredStartup / 2; if (npc.ai[1] < threshold) { npc.ai[1]++; } } if (DoLaserAttack && npc.ai[1] >= 70) { if (EnteredPhase3) { const float baseIncrement = 0.33f; float increment = baseIncrement; //if (FargoSoulsWorld.MasochistModeReal) increment *= 2; if (npc.ai[1] == 70) //shoot laser { float time = (90 - 70) / baseIncrement - 5; time *= 5; //account for the ray having extra updates float rotation = MathHelper.Pi * (FargoSoulsWorld.MasochistModeReal ? 1f : 0.8f) / time * -npc.direction; if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), eye, Vector2.UnitY, ModContent.ProjectileType <DeerclopsDeathray>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage, 2f), 0f, Main.myPlayer, rotation, time); } } npc.ai[1] += increment; //more endlag than normal if (npc.ai[1] < 90) { return(false); //stop deerclops from turning around } } else { npc.ai[1] += 0.33f; //more endlag than normal if (npc.ai[1] >= 89) { npc.ai[0] = 2; //force debris attack instead npc.ai[1] = 0; npc.frameCounter = 0; npc.netUpdate = true; break; } } if (npc.ai[1] < 90) { return(false); //stop deerclops from turning around } } } break; case 6: //trying to return home npc.TargetClosest(); if (npc.ai[1] > 120 && (!npc.HasValidTarget || npc.Distance(Main.player[npc.target].Center) > 1600)) { if (Main.netMode != NetmodeID.MultiplayerClient) //force despawn { npc.ai[0] = 8f; npc.ai[1] = 0.0f; npc.localAI[1] = 0.0f; npc.netUpdate = true; } } break; default: break; } if (EnteredPhase3 && !(npc.ai[0] == 0 && npc.alpha > 0)) { npc.localAI[3] += 3; if (npc.localAI[3] > 30) { npc.localAI[3] = 30; } } //FargoSoulsUtil.PrintAI(npc); EModeUtils.DropSummon(npc, "DeerThing2", NPC.downedDeerclops, ref DroppedSummon); return(result); }
public override bool PreAI(NPC npc) { EModeGlobalNPC.eyeBoss = npc.whoAmI; if (FargoSoulsWorld.SwarmActive) { return(true); } void SpawnServants() { if (npc.life <= npc.lifeMax * 0.65 && NPC.CountNPCS(NPCID.ServantofCthulhu) < 9 && Main.netMode != NetmodeID.MultiplayerClient) { Vector2 vel = new Vector2(3, 3); for (int i = 0; i < 4; i++) { int n = NPC.NewNPC(npc.GetSource_FromAI(), (int)npc.Center.X, (int)npc.Center.Y, NPCID.ServantofCthulhu); if (n != Main.maxNPCs) { Main.npc[n].velocity = vel.RotatedBy(Math.PI / 2 * i); if (Main.netMode == NetmodeID.Server) { NetMessage.SendData(MessageID.SyncNPC, -1, -1, null, n); } } } } } npc.dontTakeDamage = npc.alpha > 50; if (npc.dontTakeDamage) { Lighting.AddLight(npc.Center, 0.75f, 1.35f, 1.5f); } if (ScytheSpawnTimer > 0) { if (ScytheSpawnTimer % (IsInFinalPhase ? 2 : 6) == 0 && Main.netMode != NetmodeID.MultiplayerClient) { if (IsInFinalPhase && !FargoSoulsWorld.MasochistModeReal) { int p = Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, Vector2.Zero, ModContent.ProjectileType <BloodScythe>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 1f, Main.myPlayer); if (p != Main.maxProjectiles) { Main.projectile[p].timeLeft = 75; } } else { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, Vector2.Normalize(npc.velocity), ModContent.ProjectileType <BloodScythe>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 1f, Main.myPlayer); } } ScytheSpawnTimer--; } if (npc.ai[0] == 0f && npc.ai[1] == 2f && npc.ai[2] == 0f) { ScytheSpawnTimer = 30; } if (npc.ai[1] == 3f && !IsInFinalPhase) //during dashes in phase 2 { if (FargoSoulsWorld.MasochistModeReal) { ScytheSpawnTimer = 30; SpawnServants(); } if (!ScytheRingIsOnCD) { ScytheRingIsOnCD = true; if (Main.netMode != NetmodeID.MultiplayerClient) { FargoSoulsUtil.XWay(8, npc.GetSource_FromThis(), npc.Center, ModContent.ProjectileType <BloodScythe>(), 1.5f, FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0); } } } else { ScytheRingIsOnCD = false; //hacky fix for scythe spam during p2 transition } if (npc.life < npc.lifeMax / 2) { if (IsInFinalPhase) //final phase { const float speedModifier = 0.3f; if (npc.HasValidTarget && !Main.dayTime) { if (npc.timeLeft < 300) { npc.timeLeft = 300; } } else //despawn and retarget { npc.TargetClosest(false); npc.velocity.X *= 0.98f; npc.velocity.Y -= npc.velocity.Y > 0 ? 1f : 0.25f; if (npc.timeLeft > 30) { npc.timeLeft = 30; } AITimer = 90; FinalPhaseDashCD = 0; FinalPhaseBerserkDashesComplete = true; FinalPhaseDashHorizSpeedSet = false; FinalPhaseAttackCounter = 0; npc.alpha = 0; const float PI = (float)Math.PI; if (npc.rotation > PI) { npc.rotation -= 2 * PI; } if (npc.rotation < -PI) { npc.rotation += 2 * PI; } float targetRotation = npc.DirectionTo(Main.player[npc.target].Center).ToRotation() - PI / 2; if (targetRotation > PI) { targetRotation -= 2 * PI; } if (targetRotation < -PI) { targetRotation += 2 * PI; } npc.rotation = MathHelper.Lerp(npc.rotation, targetRotation, 0.07f); } if (++AITimer == 1) //teleport to random position { if (Main.netMode != NetmodeID.MultiplayerClient) { npc.Center = Main.player[npc.target].Center; npc.position.X += Main.rand.NextBool() ? -600 : 600; npc.position.Y += Main.rand.NextBool() ? -400 : 400; npc.TargetClosest(false); npc.netUpdate = true; NetSync(npc); } } else if (AITimer < 90) //fade in, moving into position { npc.alpha -= FargoSoulsWorld.MasochistModeReal ? 5 : 4; if (npc.alpha < 0) { npc.alpha = 0; if (FargoSoulsWorld.MasochistModeReal && AITimer < 90) { AITimer = 90; } } const float PI = (float)Math.PI; if (npc.rotation > PI) { npc.rotation -= 2 * PI; } if (npc.rotation < -PI) { npc.rotation += 2 * PI; } float targetRotation = npc.DirectionTo(Main.player[npc.target].Center).ToRotation() - PI / 2; if (targetRotation > PI) { targetRotation -= 2 * PI; } if (targetRotation < -PI) { targetRotation += 2 * PI; } npc.rotation = MathHelper.Lerp(npc.rotation, targetRotation, 0.07f); for (int i = 0; i < 3; i++) { int d = Dust.NewDust(npc.position, npc.width, npc.height, 229, 0f, 0f, 0, default(Color), 1.5f); Main.dust[d].noGravity = true; Main.dust[d].noLight = true; Main.dust[d].velocity *= 4f; } Vector2 target = Main.player[npc.target].Center; target.X += npc.Center.X < target.X ? -600 : 600; target.Y += npc.Center.Y < target.Y ? -400 : 400; if (npc.Center.X < target.X) { npc.velocity.X += speedModifier; if (npc.velocity.X < 0) { npc.velocity.X += speedModifier * 2; } } else { npc.velocity.X -= speedModifier; if (npc.velocity.X > 0) { npc.velocity.X -= speedModifier * 2; } } if (npc.Center.Y < target.Y) { npc.velocity.Y += speedModifier; if (npc.velocity.Y < 0) { npc.velocity.Y += speedModifier * 2; } } else { npc.velocity.Y -= speedModifier; if (npc.velocity.Y > 0) { npc.velocity.Y -= speedModifier * 2; } } if (Math.Abs(npc.velocity.X) > 24) { npc.velocity.X = 24 * Math.Sign(npc.velocity.X); } if (Math.Abs(npc.velocity.Y) > 24) { npc.velocity.Y = 24 * Math.Sign(npc.velocity.Y); } } else if (!FinalPhaseBerserkDashesComplete) //berserk dashing phase { AITimer = 90; const float xSpeed = 18f; const float ySpeed = 40f; if (++FinalPhaseDashCD == 1) { SoundEngine.PlaySound(SoundID.ForceRoarPitched, Main.player[npc.target].Center); if (!FinalPhaseDashHorizSpeedSet) //only set this on the first dash of each set { FinalPhaseDashHorizSpeedSet = true; npc.velocity.X = npc.Center.X < Main.player[npc.target].Center.X ? xSpeed : -xSpeed; } npc.velocity.Y = npc.Center.Y < Main.player[npc.target].Center.Y ? ySpeed : -ySpeed; //alternate this every dash ScytheSpawnTimer = 30; if (FargoSoulsWorld.MasochistModeReal) { SpawnServants(); } if (Main.netMode != NetmodeID.MultiplayerClient) { FargoSoulsUtil.XWay(8, npc.GetSource_FromThis(), npc.Center, ModContent.ProjectileType <BloodScythe>(), 1f, FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 0); } npc.netUpdate = true; } else if (FinalPhaseDashCD > 20) { FinalPhaseDashCD = 0; } if (++FinalPhaseDashStageDuration > 600 * 3 / xSpeed + 5) //proceed { ScytheSpawnTimer = 0; FinalPhaseDashStageDuration = 0; FinalPhaseBerserkDashesComplete = true; if (!FargoSoulsWorld.MasochistModeReal) { FinalPhaseAttackCounter++; } npc.velocity *= 0.75f; npc.netUpdate = true; } const float PI = (float)Math.PI; npc.rotation = npc.velocity.ToRotation() - PI / 2; if (npc.rotation > PI) { npc.rotation -= 2 * PI; } if (npc.rotation < -PI) { npc.rotation += 2 * PI; } } else { bool mustRest = FinalPhaseAttackCounter >= 5; const int restingTime = 240; int threshold = 180; if (mustRest) { threshold += restingTime; } if (mustRest && AITimer < restingTime + 90) { if (AITimer == 91) { npc.velocity = npc.DirectionTo(Main.player[npc.target].Center) * npc.velocity.Length() * 0.75f; } npc.velocity.X *= 0.98f; if (Math.Abs(npc.Center.X - Main.player[npc.target].Center.X) < 300) { npc.velocity.X *= 0.9f; } bool floatUp = Collision.SolidCollision(npc.position, npc.width, npc.height); if (!floatUp && npc.Bottom.X > 0 && npc.Bottom.X < Main.maxTilesX * 16 && npc.Bottom.Y > 0 && npc.Bottom.Y < Main.maxTilesY * 16) { Tile tile = Framing.GetTileSafely(npc.Bottom); if (tile != null && tile.HasUnactuatedTile) { floatUp = Main.tileSolid[tile.TileType]; } } if (floatUp) { npc.velocity.X *= 0.95f; npc.velocity.Y -= speedModifier; if (npc.velocity.Y > 0) { npc.velocity.Y = 0; } if (Math.Abs(npc.velocity.Y) > 24) { npc.velocity.Y = 24 * Math.Sign(npc.velocity.Y); } } else { npc.velocity.Y += speedModifier; if (npc.velocity.Y < 0) { npc.velocity.Y += speedModifier * 2; } if (npc.velocity.Y > 15) { npc.velocity.Y = 15; } } } else { npc.alpha += FargoSoulsWorld.MasochistModeReal ? 16 : 4; if (npc.alpha > 255) { npc.alpha = 255; if (FargoSoulsWorld.MasochistModeReal && AITimer < threshold) { AITimer = threshold; } } if (mustRest) { npc.velocity.Y -= speedModifier * 0.5f; if (npc.velocity.Y > 0) { npc.velocity.Y = 0; } if (Math.Abs(npc.velocity.Y) > 24) { npc.velocity.Y = 24 * Math.Sign(npc.velocity.Y); } } else { npc.velocity *= 0.98f; } } const float PI = (float)Math.PI; float targetRotation = MathHelper.WrapAngle(npc.DirectionTo(Main.player[npc.target].Center).ToRotation() - PI / 2); npc.rotation = MathHelper.WrapAngle(MathHelper.Lerp(npc.rotation, targetRotation, 0.07f)); if (npc.alpha > 0) { for (int i = 0; i < 3; i++) { int d = Dust.NewDust(npc.position, npc.width, npc.height, 229, 0f, 0f, 0, default(Color), 1.5f); Main.dust[d].noGravity = true; Main.dust[d].noLight = true; Main.dust[d].velocity *= 4f; } } if (AITimer > threshold) //reset { AITimer = 0; FinalPhaseDashCD = 0; FinalPhaseBerserkDashesComplete = false; FinalPhaseDashHorizSpeedSet = false; if (mustRest) { FinalPhaseAttackCounter = 0; } npc.velocity = Vector2.Zero; npc.netUpdate = true; } } if (npc.netUpdate) { if (Main.netMode == NetmodeID.Server) { NetMessage.SendData(MessageID.SyncNPC, -1, -1, null, npc.whoAmI); NetSync(npc); } npc.netUpdate = false; } return(false); } else if (!IsInFinalPhase && npc.life <= npc.lifeMax * 0.1) //go into final phase { npc.velocity *= 0.98f; npc.alpha += 4; for (int i = 0; i < 3; i++) { int d = Dust.NewDust(npc.position, npc.width, npc.height, 229, 0f, 0f, 0, default(Color), 1.5f); Main.dust[d].noGravity = true; Main.dust[d].noLight = true; Main.dust[d].velocity *= 4f; } if (npc.alpha > 255) { npc.alpha = 255; IsInFinalPhase = true; SoundEngine.PlaySound(SoundID.Roar, npc.HasValidTarget ? Main.player[npc.target].Center : npc.Center); if (Main.netMode != NetmodeID.MultiplayerClient) { Projectile.NewProjectile(npc.GetSource_FromThis(), npc.Center, Vector2.Zero, ModContent.ProjectileType <GlowRing>(), 0, 0f, Main.myPlayer, npc.whoAmI, npc.type); } } return(false); } else if (npc.ai[0] == 3 && (npc.ai[1] == 0 || npc.ai[1] == 5)) { if (npc.ai[2] < 2) { npc.ai[2]--; npc.alpha += 4; for (int i = 0; i < 3; i++) { int d = Dust.NewDust(npc.position, npc.width, npc.height, 229, 0f, 0f, 0, default(Color), 1.5f); Main.dust[d].noGravity = true; Main.dust[d].noLight = true; Main.dust[d].velocity *= 4f; } if (npc.alpha > 255) { npc.alpha = 255; if (Main.netMode != NetmodeID.MultiplayerClient && npc.HasPlayerTarget) { npc.ai[2] = 60; npc.ai[1] = 5f; Vector2 distance = Main.player[npc.target].Center - npc.Center; if (Math.Abs(distance.X) > 1200) { distance.X = 1200 * Math.Sign(distance.X); } else if (Math.Abs(distance.X) < 600) { distance.X = 600 * Math.Sign(distance.X); } if (distance.Y > 0) //always ensure eoc teleports above player { distance.Y *= -1; } if (Math.Abs(distance.Y) > 450) { distance.Y = 450 * Math.Sign(distance.Y); } if (Math.Abs(distance.Y) < 150) { distance.Y = 150 * Math.Sign(distance.Y); } npc.Center = Main.player[npc.target].Center + distance; npc.netUpdate = true; } } } else { npc.alpha -= 4; if (npc.alpha < 0) { npc.alpha = 0; } else { npc.ai[2]--; npc.position -= npc.velocity / 2; for (int i = 0; i < 3; i++) { int d = Dust.NewDust(npc.position, npc.width, npc.height, 229, 0f, 0f, 0, default(Color), 1.5f); Main.dust[d].noGravity = true; Main.dust[d].noLight = true; Main.dust[d].velocity *= 4f; } } } } /*if (++Timer > 600) * { * Timer = 0; * if (npc.HasValidTarget) * { * Player player = Main.player[npc.target]; * SoundEngine.PlaySound(SoundID.Item9104, player.Center); * if (Main.netMode != NetmodeID.MultiplayerClient) * { * Vector2 spawnPos = player.Center; * int direction; * if (player.velocity.X == 0f) * direction = player.direction; * else * direction = Math.Sign(player.velocity.X); * spawnPos.X += 600 * direction; * spawnPos.Y -= 600; * Vector2 speed = Vector2.UnitY; * for (int i = 0; i < 30; i++) * { * Projectile.NewProjectile(npc.GetSource_FromThis(), spawnPos, speed, ModContent.ProjectileType<BloodScythe>(), FargoSoulsUtil.ScaledProjectileDamage(npc.damage), 1f, Main.myPlayer); * spawnPos.X += 72 * direction; * speed.Y += 0.15f; * } * } * } * }*/ } else { npc.alpha = 0; npc.dontTakeDamage = false; } // Drop summon EModeUtils.DropSummon(npc, "SuspiciousEye", NPC.downedBoss1, ref DroppedSummon); return(true); }