Lerp() public static method

public static Lerp ( double t, double a, double b ) : double
t double
a double
b double
return double
Beispiel #1
0
        /// <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);
        }
Beispiel #4
0
        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));
 }