/// <summary> /// Returns the vector force that a Magnet should exert on a Particle /// </summary> /// <param name="cMagnet">The Magnet affecting the Particle</param> /// <param name="cParticle">The Particle being affected by the Magnet</param> /// <returns>Returns the vector force that a Magnet should exert on a Particle</returns> protected Vector3 CalculateForceMagnetShouldExertOnParticle(DefaultParticleSystemMagnet cMagnet, DPSFDefaultBaseParticle cParticle) { // Variable to store the Force to Exert on the Particle Vector3 sForceToExertOnParticle = Vector3.Zero; // Calculate which Direction to push the Particle Vector3 sDirectionToPushParticle; // If this is a Point Magnet if (cMagnet.MagnetType == DefaultParticleSystemMagnet.MagnetTypes.PointMagnet) { // Cast the Magnet to the proper type MagnetPoint cPointMagnet = (MagnetPoint)cMagnet; // Calculate the direction to attract the Particle to the point in space where the Magnet is sDirectionToPushParticle = cPointMagnet.PositionData.Position - cParticle.Position; } // Else If this is a Line Magnet else if (cMagnet.MagnetType == DefaultParticleSystemMagnet.MagnetTypes.LineMagnet) { // Cast the Magnet to the proper type MagnetLine cLineMagnet = (MagnetLine)cMagnet; // Calculate the closest point on the Line to the Particle. // Equation taken from http://ozviz.wasp.uwa.edu.au/~pbourke/geometry/pointline/ // Also explained at http://www.allegro.cc/forums/thread/589720 // Calculate 2 points on the Line Vector3 sPosition1 = cLineMagnet.PositionOnLine; Vector3 sPosition2 = cLineMagnet.PositionOnLine + cLineMagnet.Direction; // Put calculations into temp variables for speed and easy readability float fA = cParticle.Position.X - sPosition1.X; float fB = cParticle.Position.Y - sPosition1.Y; float fC = cParticle.Position.Z - sPosition1.Z; float fD = sPosition2.X - sPosition1.X; float fE = sPosition2.Y - sPosition1.Y; float fF = sPosition2.Z - sPosition1.Z; // Next calculate the value of U. // NOTE: The Direction is normalized, so the distance between Position1 and Position2 is one, so we // don't need to bother squaring and dividing by the length here. float fU = (fA * fD) + (fB * fE) + (fC * fF); // Calculate the closest point on the Line to the Particle Vector3 sClosestPointOnLine = new Vector3(); sClosestPointOnLine.X = sPosition1.X + (fU * fD); sClosestPointOnLine.Y = sPosition1.Y + (fU * fE); sClosestPointOnLine.Z = sPosition1.Z + (fU * fF); // Calculate the direction to attract the Particle to the closest point on the Line sDirectionToPushParticle = sClosestPointOnLine - cParticle.Position; } // Else if the is a Line Segment Magnet else if (cMagnet.MagnetType == DefaultParticleSystemMagnet.MagnetTypes.LineSegmentMagnet) { // Cast the Magnet to the proper type MagnetLineSegment cLineSegmentMagnet = (MagnetLineSegment)cMagnet; // Calculate the closest point on the Line to the Particle. // Equation taken from http://ozviz.wasp.uwa.edu.au/~pbourke/geometry/pointline/ // Also explained at http://www.allegro.cc/forums/thread/589720 // Calculate 2 points on the Line Vector3 sPosition1 = cLineSegmentMagnet.EndPoint1; Vector3 sPosition2 = cLineSegmentMagnet.EndPoint2; // Put calculations into temp variables for speed and easy readability float fA = cParticle.Position.X - sPosition1.X; float fB = cParticle.Position.Y - sPosition1.Y; float fC = cParticle.Position.Z - sPosition1.Z; float fD = sPosition2.X - sPosition1.X; float fE = sPosition2.Y - sPosition1.Y; float fF = sPosition2.Z - sPosition1.Z; // Next calculate the value of U float fDot = (fA * fD) + (fB * fE) + (fC * fF); float fLengthSquared = (fD * fD) + (fE * fE) + (fF * fF); float fU = fDot / fLengthSquared; // Calculate the closest point on the Line to the Particle Vector3 sClosestPointOnLine = new Vector3(); // If the Particle is closest to the first End Point if (fU < 0.0f) { sClosestPointOnLine = sPosition1; } // Else If the Particle is closest to the second End Point else if (fU > 1.0f) { sClosestPointOnLine = sPosition2; } // Else the Particle is closest to the Line Segment somewhere between the End Points else { // Calculate where in between the End Points the Particle is closest to sClosestPointOnLine.X = sPosition1.X + (fU * (sPosition2.X - sPosition1.X)); sClosestPointOnLine.Y = sPosition1.Y + (fU * (sPosition2.Y - sPosition1.Y)); sClosestPointOnLine.Z = sPosition1.Z + (fU * (sPosition2.Z - sPosition1.Z)); } // Calculate the direction to attract the Particle to the closest point on the Line sDirectionToPushParticle = sClosestPointOnLine - cParticle.Position; } // Else If this is a Plane Magnet else if (cMagnet.MagnetType == DefaultParticleSystemMagnet.MagnetTypes.PlaneMagnet) { // Cast the Magnet to the proper type MagnetPlane cPlaneMagnet = (MagnetPlane)cMagnet; // Calculate the closest point on the Plane to the Particle. // Equation taken from http://ozviz.wasp.uwa.edu.au/~pbourke/geometry/pointline/ // Calculate how far from the Plane the Particle is float fDistanceFromPlane = Vector3.Dot(cParticle.Position - cPlaneMagnet.PositionOnPlane, cPlaneMagnet.Normal); // Calculate the closest point on the Plane to the Particle Vector3 sClosestPointOnPlane = cParticle.Position + (-cPlaneMagnet.Normal * fDistanceFromPlane); // Calculate the direction to attract the Particle to the closest point on the Plane sDirectionToPushParticle = sClosestPointOnPlane - cParticle.Position; } // Else we don't know what kind of Magnet this is else { // So exit returning no force return(Vector3.Zero); } // If the Particle should be Repelled away from the Magnet (instead of attracted to it) if (cMagnet.Mode == DefaultParticleSystemMagnet.MagnetModes.Repel) { // Reverse the direction we are going to push the Particle sDirectionToPushParticle *= -1; } // If the Direction To Push the Particle is not valid and we should be Repelling the Particle if (sDirectionToPushParticle == Vector3.Zero && cMagnet.Mode == DefaultParticleSystemMagnet.MagnetModes.Repel) { // Pick a random Direction vector with a very short length to repel the Particle with sDirectionToPushParticle = DPSFHelper.RandomNormalizedVector() * 0.00001f; } // Get how far away the Particle is from the Magnet float fDistanceFromMagnet = sDirectionToPushParticle.Length(); // If the Particle is within range to be affected by the Magnet if (fDistanceFromMagnet >= cMagnet.MinDistance && fDistanceFromMagnet <= cMagnet.MaxDistance) { // If the Direction To Push the Particle is valid if (sDirectionToPushParticle != Vector3.Zero) { // Normalize the Direction To Push the Particle sDirectionToPushParticle.Normalize(); } // Calculate the normalized distance from the Magnet that the Particle is float fLerpAmount = 0.0f; if (cMagnet.MaxDistance != cMagnet.MinDistance) { fLerpAmount = (fDistanceFromMagnet - cMagnet.MinDistance) / (cMagnet.MaxDistance - cMagnet.MinDistance); } // Else the Max Distance equals the Min Distance else { // So to avoid a divide by zero we just assume a full Lerp amount fLerpAmount = 1.0f; } // Calculate how much of the Max Force to apply to the Particle float fNormalizedForce = 0.0f; switch (cMagnet.DistanceFunction) { default: case DefaultParticleSystemMagnet.DistanceFunctions.Constant: fNormalizedForce = cMagnet.MaxForce; break; case DefaultParticleSystemMagnet.DistanceFunctions.Linear: fNormalizedForce = MathHelper.Lerp(0, cMagnet.MaxForce, fLerpAmount); break; case DefaultParticleSystemMagnet.DistanceFunctions.Squared: fNormalizedForce = MathHelper.Lerp(0, cMagnet.MaxForce, fLerpAmount * fLerpAmount); break; case DefaultParticleSystemMagnet.DistanceFunctions.Cubed: fNormalizedForce = MathHelper.Lerp(0, cMagnet.MaxForce, fLerpAmount * fLerpAmount * fLerpAmount); break; case DefaultParticleSystemMagnet.DistanceFunctions.LinearInverse: fNormalizedForce = MathHelper.Lerp(cMagnet.MaxForce, 0, fLerpAmount); break; case DefaultParticleSystemMagnet.DistanceFunctions.SquaredInverse: fNormalizedForce = MathHelper.Lerp(cMagnet.MaxForce, 0, fLerpAmount * fLerpAmount); break; case DefaultParticleSystemMagnet.DistanceFunctions.CubedInverse: fNormalizedForce = MathHelper.Lerp(cMagnet.MaxForce, 0, fLerpAmount * fLerpAmount * fLerpAmount); break; } // Calculate how much Force should be Exerted on the Particle sForceToExertOnParticle = sDirectionToPushParticle * (fNormalizedForce * cMagnet.MaxForce); } // Return how much Force to Exert on the Particle return(sForceToExertOnParticle); }
public override bool Shoot(Player player, ref Vector2 position, ref float speedX, ref float speedY, ref int type, ref int damage, ref float knockBack) { if (type == ProjectileID.Bullet) { int degrees = Main.rand.Next(10); float numberProjectiles = 3; // 3 shots float rotation = MathHelper.ToRadians(30); //30 degrees spread position += Vector2.Normalize(new Vector2(speedX, speedY)) * 40f; for (int i = 0; i < numberProjectiles; i++) { Vector2 perturbedSpeed = new Vector2(speedX, speedY).RotatedByRandom(MathHelper.Lerp(-rotation, rotation, i / (numberProjectiles - 1))) * .75f; // Watch out for dividing by 0 if there is only 1 projectile. Projectile.NewProjectile(position.X, position.Y, perturbedSpeed.X, perturbedSpeed.Y, type, damage, knockBack, player.whoAmI); } return(false); } else { return(true); } }
public override bool PreDraw(SpriteBatch spriteBatch, Color lightColor) { SpriteEffects effects1 = SpriteEffects.None; if (projectile.direction == 1) { effects1 = SpriteEffects.FlipHorizontally; } Microsoft.Xna.Framework.Color color3 = Lighting.GetColor((int)((double)projectile.position.X + (double)projectile.width * 0.5) / 16, (int)(((double)projectile.position.Y + (double)projectile.height * 0.5) / 16.0)); { Texture2D texture = Main.projectileTexture[projectile.type]; Texture2D glow = Main.projectileTexture[projectile.type]; int height = Main.projectileTexture[projectile.type].Height / Main.projFrames[projectile.type]; Microsoft.Xna.Framework.Rectangle r = new Microsoft.Xna.Framework.Rectangle(0, height * projectile.frame, texture.Width, height); Vector2 origin = r.Size() / 2f; int num2 = 5; int num3 = 1; int num4 = 1; float num5 = 1f; float num6 = 0.0f; num3 = 1; num5 = 3f; int index1 = num4; while (num3 > 0 && index1 < num2 || num3 < 0 && index1 > num2) { Microsoft.Xna.Framework.Color newColor = color3; newColor = Microsoft.Xna.Framework.Color.Lerp(newColor, Microsoft.Xna.Framework.Color.White, 2.5f); Microsoft.Xna.Framework.Color color1 = projectile.GetAlpha(newColor); float num7 = (float)(num2 - index1); if (num3 < 0) { num7 = (float)(num4 - index1); } Microsoft.Xna.Framework.Color color2 = color1 * (num7 / ((float)ProjectileID.Sets.TrailCacheLength[projectile.type] * 1.5f)); Vector2 oldPo = projectile.oldPos[index1]; float rotation = projectile.rotation; SpriteEffects effects2 = effects1; if (ProjectileID.Sets.TrailingMode[projectile.type] == 2) { rotation = projectile.oldRot[index1]; effects2 = projectile.oldSpriteDirection[index1] == -1 ? SpriteEffects.FlipHorizontally : SpriteEffects.None; } Main.spriteBatch.Draw(glow, oldPo + projectile.Size / 2f - Main.screenPosition + new Vector2(0.0f, projectile.gfxOffY), new Microsoft.Xna.Framework.Rectangle?(r), color2, rotation + projectile.rotation * num6 * (float)(index1 - 1) * (float)-effects1.HasFlag((Enum)SpriteEffects.FlipHorizontally).ToDirectionInt(), origin, MathHelper.Lerp(projectile.scale, num5, (float)index1 / 15f), effects2, 0.0f); label_709: index1 += num3; } Microsoft.Xna.Framework.Color color4 = projectile.GetAlpha(color3); Main.spriteBatch.Draw(texture, projectile.Center - Main.screenPosition + new Vector2(0.0f, projectile.gfxOffY), new Microsoft.Xna.Framework.Rectangle?(r), new Color(255 - projectile.alpha, 255 - projectile.alpha, 255 - projectile.alpha, 175), projectile.rotation, origin, projectile.scale, effects1, 0.0f); } for (int a = 0; a < 1; a++) { SpriteEffects spriteEffects = SpriteEffects.None; if (projectile.spriteDirection == 1) { spriteEffects = SpriteEffects.FlipHorizontally; } Texture2D texture = Main.projectileTexture[projectile.type]; Vector2 vector2_3 = new Vector2((float)(Main.projectileTexture[projectile.type].Width / 2), (float)(Main.projectileTexture[projectile.type].Height / 1 / 2)); int height = Main.projectileTexture[projectile.type].Height / Main.projFrames[projectile.type]; Microsoft.Xna.Framework.Rectangle r = new Microsoft.Xna.Framework.Rectangle(0, height * projectile.frame, texture.Width, height); float addY = 0f; float addHeight = 0f; int num7 = 5; float num9 = (float)(Math.Cos((double)Main.GlobalTime % 2.40000009536743 / 2.40000009536743 * 6.28318548202515) / 2.0 + 0.5); float num99 = (float)(Math.Cos((double)Main.GlobalTime % 2.40000009536743 / 2.40000009536743 * 6.28318548202515) / 4.0 + 0.5); float num8 = 0f; Microsoft.Xna.Framework.Color secondColor = Microsoft.Xna.Framework.Color.White; float num10 = 0.0f; Vector2 bb = projectile.Center - Main.screenPosition - new Vector2((float)texture.Width, (float)(texture.Height / 1)) * projectile.scale / 2f + vector2_3 * projectile.scale + new Vector2(0.0f, addY + addHeight + projectile.gfxOffY); Microsoft.Xna.Framework.Color color2 = new Microsoft.Xna.Framework.Color((int)sbyte.MaxValue - projectile.alpha, (int)sbyte.MaxValue - projectile.alpha, (int)sbyte.MaxValue - projectile.alpha, 0).MultiplyRGBA(Microsoft.Xna.Framework.Color.White); for (int index2 = 0; index2 < 4; ++index2) { Microsoft.Xna.Framework.Color newColor2 = color2; Microsoft.Xna.Framework.Color faa = projectile.GetAlpha(newColor2) * (1f - num99); Vector2 position2 = projectile.Center + ((float)((double)index2 / (double)4 * 6.28318548202515) + projectile.rotation + num10).ToRotationVector2() * (float)(8.0 * (double)num99 + 2.0) - Main.screenPosition - new Vector2((float)texture.Width, (float)(texture.Height / 1)) * projectile.scale / 2f + vector2_3 * projectile.scale + new Vector2(0.0f, addY + addHeight + projectile.gfxOffY); Main.spriteBatch.Draw(Main.projectileTexture[projectile.type], position2, new Microsoft.Xna.Framework.Rectangle?(r), faa, projectile.rotation, vector2_3, projectile.scale, spriteEffects, 0.0f); } } Lighting.AddLight(projectile.Center, Color.Red.ToVector3() / 2f); return(false); }
private void UpdateAv() { foreach (var p in ActiveProjetiles) { if (!p.Miss || (int)p.State > 3) { continue; } if (p.Info.MuzzleId == -1) { p.CreateFakeBeams(true); continue; } if (!p.EnableAv) { continue; } if (p.SmartsOn) { if (p.EnableAv && Vector3D.Dot(p.Info.VisualDir, p.AccelDir) < Session.VisDirToleranceCosine) { p.VisualStep += 0.0025; if (p.VisualStep > 1) { p.VisualStep = 1; } Vector3D lerpDir; Vector3D.Lerp(ref p.Info.VisualDir, ref p.AccelDir, p.VisualStep, out lerpDir); Vector3D.Normalize(ref lerpDir, out p.Info.VisualDir); } else if (p.EnableAv && Vector3D.Dot(p.Info.VisualDir, p.AccelDir) >= Session.VisDirToleranceCosine) { p.Info.VisualDir = p.AccelDir; p.VisualStep = 0; } } else if (p.FeelsGravity) { p.Info.VisualDir = p.Info.Direction; } if (p.LineOrNotModel) { if (p.State == ProjectileState.OneAndDone) { DeferedAvDraw.Add(new DeferedAv { Info = p.Info, StepSize = 0, VisualLength = p.MaxTrajectory, TracerFront = p.Position }); } else if (p.ModelState == EntityState.None && p.Info.AmmoDef.Const.AmmoParticle && !p.Info.AmmoDef.Const.DrawLine) { if (p.AtMaxRange) { p.ShortStepAvUpdate(true, false); } else { DeferedAvDraw.Add(new DeferedAv { Info = p.Info, StepSize = p.Info.DistanceTraveled - p.Info.PrevDistanceTraveled, VisualLength = p.Info.AmmoDef.Const.CollisionSize, TracerFront = p.Position }); } } else { p.Info.ProjectileDisplacement += Math.Abs(Vector3D.Dot(p.Info.Direction, (p.Velocity - p.StartSpeed) * StepConst)); var displaceDiff = p.Info.ProjectileDisplacement - p.TracerLength; if (p.Info.ProjectileDisplacement < p.TracerLength && Math.Abs(displaceDiff) > 0.0001) { if (p.AtMaxRange) { p.ShortStepAvUpdate(false, false); } else { DeferedAvDraw.Add(new DeferedAv { Info = p.Info, StepSize = p.Info.DistanceTraveled - p.Info.PrevDistanceTraveled, VisualLength = p.Info.ProjectileDisplacement, TracerFront = p.Position }); } } else { if (p.AtMaxRange) { p.ShortStepAvUpdate(false, false); } else { DeferedAvDraw.Add(new DeferedAv { Info = p.Info, StepSize = p.Info.DistanceTraveled - p.Info.PrevDistanceTraveled, VisualLength = p.TracerLength, TracerFront = p.Position }); } } } } if (p.Info.ModelOnly) { DeferedAvDraw.Add(new DeferedAv { Info = p.Info, StepSize = p.Info.DistanceTraveled - p.Info.PrevDistanceTraveled, VisualLength = p.TracerLength, TracerFront = p.Position }); } if (p.Info.AmmoDef.Const.AmmoParticle) { p.TestSphere.Center = p.Position; if (p.Info.AvShot.OnScreen != Screen.None || Session.Camera.IsInFrustum(ref p.TestSphere)) { if (!p.Info.AmmoDef.Const.IsBeamWeapon && !p.ParticleStopped && p.AmmoEffect != null && p.Info.AmmoDef.Const.AmmoParticleShrinks) { p.AmmoEffect.UserScale = MathHelper.Clamp(MathHelper.Lerp(p.BaseAmmoParticleScale, 0, p.Info.AvShot.DistanceToLine / p.Info.AmmoDef.AmmoGraphics.Particles.Hit.Extras.MaxDistance), 0.05f, p.BaseAmmoParticleScale); } if ((p.ParticleStopped || p.ParticleLateStart)) { p.PlayAmmoParticle(); } } else if (!p.ParticleStopped && p.AmmoEffect != null) { p.DisposeAmmoEffect(false, true); } } } }
public override void AI() { npc.defense = npc.defDefense; if (!spawned) { //Laugh after a second if (AI_Timer == 0) { targetZAxisRotation = 0f; zAxisRotation = 0f; targetAlpha = 255f; ignoreRetargetPlayer = true; AI_Timer = 60 + 100; AI_Attack = FadeIn; npc.TargetClosest(); npc.netUpdate = true; } else if (AI_Timer <= 100) { spawned = true; targetAlpha = 0f; Main.PlaySound(SoundID.Zombie, npc.Center, 105); //Cultist laugh sound npc.netUpdate = true; } else { targetAlpha -= 255f / 60f; } } //Player is dead/not connected? Target a new one //That player is also dead/not connected? Begin the "fade away" animation and despawn Player player = npc.target >= 0 && npc.target < Main.maxPlayers ? Main.player[npc.target] : null; if (!ignoreRetargetPlayer && (npc.target < 0 || npc.target >= Main.maxPlayers || player.dead || !player.active)) { npc.TargetClosest(); player = npc.target >= 0 && npc.target < Main.maxPlayers ? Main.player[npc.target] : null; if (npc.target < 0 || npc.target >= Main.maxPlayers || player.dead || !player.active) { //Go away AI_Attack = FadeAway; AI_Timer = -1; AI_AttackProgress = -1; hadNoPlayerTargetForLongEnough = true; npc.netUpdate = true; } else if (hadNoPlayerTargetForLongEnough) { hadNoPlayerTargetForLongEnough = false; //Start back in the idle phase SetAttack(Attack_DoNothing); npc.netUpdate = true; } } npc.defense = npc.defDefense; switch ((int)AI_Attack) { case FadeIn: //Do the laughing animation if (AI_Timer > 0 && AI_Timer <= 100) { npc.velocity *= 1f - 3f / 60f; } else if (AI_Timer <= 0) { //Transition to the next subphase SetAttack(Attack_DoNothing); npc.dontTakeDamage = false; ignoreRetargetPlayer = false; } else { npc.velocity = new Vector2(0, 1.5f); SpawnDusts(); } break; case FadeAway: npc.velocity *= 1f - 3f / 60f; //Spawn dusts as the boss fades away, then despawn it once fully invisible SpawnDusts(); targetAlpha += 255f / 180f; if (targetAlpha >= 255) { npc.active = false; return; } break; case Attack_DoNothing: inertia = DefaultInertia; zAxisLerpStrength = DefaultZAxisLerpStrength; FloatTowardsTarget(player); if (AI_Timer <= 0) { //Prevent the same attack from being rolled twice in a row int attack; do { attack = Main.rand.Next(Attack_SummonMeteors, Attack_ChargeAtPlayer + 1); }while(attack == oldAttack); SetAttack(attack); npc.netUpdate = true; oldAttack = (int)AI_Attack; } break; case Attack_SummonMeteors: npc.defense = npc.defDefense + 40; //Face forwards targetZAxisRotation = 0f; npc.velocity *= 1f - 4.67f / 60f; if (Math.Abs(npc.velocity.X) < 0.02f) { npc.velocity.X = 0f; } if (Math.Abs(npc.velocity.Y) < 0.02f) { npc.velocity.Y = 0f; } int max = Main.expertMode ? PortalTimerMax : (int)(PortalTimerMax * 1.5f); if ((int)AI_Timer == max - 1) { Main.PlaySound(SoundID.Zombie, npc.Center, 99); } else if ((int)AI_Timer == max - 24) { //Wait until the animation frame changes to the one facing forwards if (GetAnimationSetFrame() == Animation_LookFront_JawOpen) { Main.PlaySound(SoundID.Zombie, npc.Center, 93); AI_AttackProgress++; } else { AI_Timer++; } } if (AI_AttackProgress == 1) { //Spawn portals near and above the player Vector2 orig = player.Center - new Vector2(0, 15 * 16); int count = Main.expertMode ? 4 : 2; for (int i = 0; i < count; i++) { Vector2 spawn = orig + new Vector2(Main.rand.NextFloat(-1, 1) * 40 * 16, Main.rand.NextFloat(-1, 1) * 6 * 16); Projectile.NewProjectile(spawn, Vector2.Zero, ModContent.ProjectileType <Portal>(), MiscUtils.TrueDamage(Main.expertMode ? 140 : 90), 0f, Main.myPlayer); // TODO: netcode } AI_AttackProgress++; } else if (AI_AttackProgress == 2 && AI_Timer == 0) { SetAttack(Attack_DoNothing); } break; case Attack_SummonLesserDemons: FloatTowardsTarget(player); if (AI_AttackProgress < 3) { if (AI_Timer % 75 == 0) { Vector2 spawn = npc.Center + new Vector2(Main.rand.NextFloat(-1, 1) * 22 * 16, Main.rand.NextFloat(-1, 1) * 10 * 16); NPC.NewNPC((int)spawn.X, (int)spawn.Y, ModContent.NPCType <MiniCraterDemon>(), ai3: npc.whoAmI); //Exhale sound Main.PlaySound(SoundID.Zombie, npc.Center, 93); // TODO: netcode AI_AttackProgress++; } } else if (AI_Timer <= 0) { //Go to next attack immediately SetAttack(Attack_DoNothing); } else if (CountAliveLesserDemons() == 0) { //All the minions have been killed. Transition to the next subphase immediately AI_Timer = 1; } break; case Attack_ChargeAtPlayer: inertia = DefaultInertia * 0.1f; float chargeVelocity = Main.expertMode ? 26f : 17f; int repeatRelative = AI_AttackProgress >= Attack_ChargeAtPlayer_RepeatStart ? ((int)AI_AttackProgress - Attack_ChargeAtPlayer_RepeatStart) % Attack_ChargeAtPlayer_RepeatSubphaseCount : -1; int repeatCount = AI_AttackProgress >= Attack_ChargeAtPlayer_RepeatStart ? ((int)AI_AttackProgress - Attack_ChargeAtPlayer_RepeatStart) / Attack_ChargeAtPlayer_RepeatSubphaseCount : 0; if (AI_AttackProgress == 0) { if (bigPortal.visible) { bigPortal = new BigPortalInfo(); } if (bigPortal2.visible) { bigPortal2 = new BigPortalInfo(); } //Wait until initial wait is done if (AI_Timer <= 0) { AI_AttackProgress++; //Spawn portal -- must be close to boss and player Vector2 spawn; const float playerDistMax = 40 * 16; int tries = 0; bool success = false; do { spawn = npc.Center + Main.rand.NextVector2Unit() * 40 * 16; tries++; }while(tries < 1000 && (success = player.DistanceSQ(spawn) >= playerDistMax * playerDistMax)); if (!success && tries == 1000) { //Failsafe: put the portal directly on the target player spawn = player.Center; } SpawnBigPortal(spawn, ref bigPortal); } } else if (AI_AttackProgress == 1) { float dist = npc.DistanceSQ(bigPortal.center); const float portalDist = 5; bool tooFar = dist > portalDist * portalDist; //Update the portal if (bigPortal.scale > 0.98f) { bigPortal.scale = 1f; bigPortal.alpha = 1f; } else { bigPortal.scale = MiscUtils.ScaleLogarithmic(bigPortal.scale, 1f, 2.7219f, 1f / 60f); bigPortal.alpha = bigPortal.scale; } if (tooFar) { //Float toward first portal movementTarget = bigPortal.center; FloatTowardsTarget(player, minimumDistanceThreshold: 0); } else { npc.Center = bigPortal.center; npc.velocity = Vector2.Zero; movementTarget = null; //If the portal hasn't gotten to the full size yet, wait for it to do so if (bigPortal.scale >= 1f) { AI_AttackProgress++; UpdateScale(1f); AI_Timer = 45; } } } else if (repeatRelative == 0) { //Shrink (factor of 4.3851 makes it reach 0.01 in around 60 ticks, 12.2753 ~= 20 ticks) const float epsilon = 0.01f; if (npc.scale > epsilon) { UpdateScale(MiscUtils.ScaleLogarithmic(npc.scale, 0f, repeatCount == 0 ? 4.3851f : 12.2753f, 1f / 60f)); } targetAlpha = 255f * (1f - npc.scale); if (AI_Timer <= 0) { //Shrink the portal, but a bit slower bigPortal.scale = MiscUtils.ScaleLogarithmic(bigPortal.scale, 0f, repeatCount == 0 ? 2.7219f : 9.2153f, 1f / 60f); bigPortal.alpha = bigPortal.scale; } if (npc.scale < 0.4f) { hideMapIcon = true; npc.dontTakeDamage = true; } if (npc.scale < epsilon) { UpdateScale(epsilon); targetAlpha = 255f; //Sanity check targetZAxisRotation = 0; zAxisRotation = 0; } if (bigPortal.scale < epsilon) { bigPortal.scale = 0f; bigPortal.visible = false; AI_AttackProgress++; if (repeatCount == 0) { Main.PlaySound(SoundID.Zombie, npc.Center, 105); //Cultist laugh sound } //Wait for a random amount of time AI_Timer = Main.expertMode ? Main.rand.Next(40, 90 + 1) : Main.rand.Next(100, 220 + 1); npc.netUpdate = true; movementTarget = null; } } else if (repeatRelative == 1) { //Wait, then spawn a portal if (AI_Timer <= 0) { Vector2 offset = Main.rand.NextVector2Unit() * 30 * 16; //Second portal is where the boss will end up SpawnBigPortal(player.Center + offset, ref bigPortal, fast: true); SpawnBigPortal(player.Center - offset, ref bigPortal2, fast: true); bigPortal2.visible = false; npc.Center = bigPortal.center; npc.velocity = Vector2.Zero; AI_AttackProgress++; } } else if (repeatRelative == 2) { //Make the portal grow FAST (9.9583 results in around 26 ticks) bigPortal.scale = MiscUtils.ScaleLogarithmic(bigPortal.scale, 1f, 9.9583f, 1f / 60f); bigPortal.alpha = bigPortal.scale; if (bigPortal.scale >= 0.99f) { bigPortal.scale = 1f; AI_AttackProgress++; AI_Timer = 40; } bigPortal.alpha = bigPortal.scale; } else if (repeatRelative == 3) { //Make the boss charge at the player after fading in zAxisLerpStrength = DefaultZAxisLerpStrength * 2.7f; //15.7025 ~= 16 ticks to go from 0.01 to 1 if (npc.scale < 0.99f) { UpdateScale(MiscUtils.ScaleLogarithmic(npc.scale, 1, 15.7025f, 1f / 60f)); } else { UpdateScale(1f); } if (npc.scale > 0.4f) { npc.dontTakeDamage = false; hideMapIcon = false; } targetAlpha = 255f * (1f - npc.scale); SetTargetZAxisRotation(player, out _); if (AI_Timer <= 0 && npc.scale == 1f) { AI_AttackProgress++; AI_Timer = Main.expertMode ? 30 : 60; npc.Center = bigPortal.center; npc.velocity = npc.DirectionTo(player.Center) * chargeVelocity; Main.PlaySound(SoundID.ForceRoar, (int)npc.Center.X, (int)npc.Center.Y, -1); if (repeatCount >= (Main.expertMode ? Main.rand.Next(5, 8) : Main.rand.Next(2, 5))) { //Stop the repetition bigPortal2.visible = false; bigPortal2.scale = 8f / 240f; SetAttack(Attack_PostCharge); } } } else if (repeatRelative == 4) { //Second portal appears once the boss is within 22 update ticks of its center float activeDist = chargeVelocity * 22; if (AI_Timer < 0 && npc.DistanceSQ(bigPortal2.center) <= activeDist * activeDist) { bigPortal2.visible = true; } //First portal disappears once the boss leaves within 22 update ticks of its center if (npc.DistanceSQ(bigPortal.center) > activeDist * activeDist) { bigPortal.scale = MiscUtils.ScaleLogarithmic(bigPortal.scale, 0f, 15.2753f, 1f / 60f); bigPortal.alpha = bigPortal.scale; if (bigPortal.scale <= 0.01f) { bigPortal.scale = 0f; bigPortal.alpha = 0f; bigPortal.visible = false; } } if (bigPortal2.visible) { bigPortal2.scale = MiscUtils.ScaleLogarithmic(bigPortal2.scale, 1f, 15.2753f, 1f / 60f); bigPortal2.alpha = bigPortal2.scale; if (bigPortal2.scale >= 0.99f) { bigPortal2.scale = 1f; bigPortal2.alpha = 1f; } } const float portalEnterDist = 5, portalTargetDist = 4 * 16; if (AI_Timer >= 0) { if (AI_Timer == 0) { bigPortal2.center = npc.Center + Vector2.Normalize(npc.velocity) * (activeDist + 3 * 16); } } else { //Make sure the boss snaps to the center of the portal before repeating the logic float dist = npc.DistanceSQ(bigPortal2.center); if (dist < portalEnterDist * portalEnterDist) { UpdateScale(1f); npc.Center = bigPortal2.center; npc.velocity = Vector2.Zero; targetAlpha = 0; movementTarget = null; if (bigPortal2.scale >= 1f) { AI_AttackProgress++; Utils.Swap(ref bigPortal, ref bigPortal2); } } else if (dist < portalTargetDist * portalTargetDist) { //Float to the center movementTarget = bigPortal2.center; inertia = DefaultInertia * 0.1f; float oldZ = targetZAxisRotation; FloatTowardsTarget(player, minimumDistanceThreshold: 0); targetZAxisRotation = oldZ; } } } break; case Attack_PostCharge: if (npc.velocity == Vector2.Zero && !bigPortal.visible) { SetAttack(Attack_DoNothing); } else if (AI_Timer <= 0) { //Charge has ended. Make the portal fade away and slow the boss down npc.velocity *= 1f - 8.5f / 60f; //5.9192 ~= 45 ticks to reach 0.01 scale bigPortal.scale = MiscUtils.ScaleLogarithmic(bigPortal.scale, 0f, 5.9192f, 1f / 60f); if (Math.Abs(npc.velocity.X) < 0.05f && Math.Abs(npc.velocity.Y) <= 0.05f) { npc.velocity = Vector2.Zero; } if (bigPortal.scale <= 0.01f) { bigPortal = new BigPortalInfo(); } } break; } AI_Timer--; AI_AnimationCounter++; if (AI_Attack != FadeAway && targetAlpha > 0) { targetAlpha -= 255f / 60f; if (targetAlpha < 0) { targetAlpha = 0; } } npc.alpha = (int)targetAlpha; if (Math.Abs(zAxisRotation - targetZAxisRotation) < 0.02f) { zAxisRotation = targetZAxisRotation; } else { zAxisRotation = MathHelper.Lerp(zAxisRotation, targetZAxisRotation, zAxisLerpStrength / 60f); } //We don't want sprite flipping npc.spriteDirection = -1; bigPortal.Update(); bigPortal2.Update(); }
public double GetPower(bool a, double b) { if (!a) { return(NeedsAtmosphereForInfluence?0:Force *EffectivenessAtMinInfluence); } if (EffectivenessAtMinInfluence == EffectivenessAtMaxInfluence) { return(Force * EffectivenessAtMaxInfluence); } var x = MathHelper.Clamp((b - MinPlanetaryInfluence) / (MaxPlanetaryInfluence - MinPlanetaryInfluence), 0f, 1f); return(Force * MathHelper.Lerp(EffectivenessAtMinInfluence, EffectivenessAtMaxInfluence, x)); }