Exemple #1
0
        public static bool Intersection(Vector2 aStart, Vector2 aEnd, Vector2 bStart, Vector2 bEnd, out Vector2 intersection)
        {
            Vector2 aSize = aEnd - aStart, bSize = bEnd - bStart;

            var cross = aSize.X * bSize.Y - aSize.Y * bSize.X;

            // ReSharper disable once CompareOfFloatsByEqualityOperator
            if (cross == 0f)
            {
                intersection = default;
                return(false);
            }

            var crossReciprocal = 1.0f / cross;

            var distStart = bStart - aStart;

            var negDistStartY = -distStart.Y;

            var checkA = MathF.FusedMultiplyAdd(distStart.X, aSize.Y, negDistStartY * aSize.X) * crossReciprocal;
            var checkB = MathF.FusedMultiplyAdd(distStart.X, bSize.Y, negDistStartY * bSize.X) * crossReciprocal;

            if (checkA < 0 || checkB < 0 || checkA > 1 || checkB > 1)
            {
                intersection = default;
                return(false);
            }

            intersection = new Vector2(
                MathF.FusedMultiplyAdd(checkB, aSize.X, aStart.X),
                MathF.FusedMultiplyAdd(checkB, aSize.Y, aStart.Y)
                );
            return(true);
        }
        public override void Render(DrawingHandleWorld handle)
        {
            if (onGrid)
            {
                const int ppm          = EyeManager.PixelsPerMeter;
                var       viewportSize = (Vector2)pManager._clyde.ScreenSize;

                var position = pManager.eyeManager.ScreenToMap(Vector2.Zero);

                var gridstartx = (float)MathF.Round(position.X / snapSize, MidpointRounding.AwayFromZero) * snapSize;
                var gridstarty = (float)MathF.Round(position.Y / snapSize, MidpointRounding.AwayFromZero) * snapSize;
                var gridstart  = pManager.eyeManager.WorldToScreen(
                    new Vector2( //Find snap grid closest to screen origin and convert back to screen coords
                        gridstartx,
                        gridstarty));
                for (var a = gridstart.X;
                     a < viewportSize.X;
                     a += snapSize * ppm) //Iterate through screen creating gridlines
                {
                    var from = ScreenToWorld(new Vector2(a, 0));
                    var to   = ScreenToWorld(new Vector2(a, viewportSize.Y));
                    handle.DrawLine(from, to, new Color(0, 0, 1f));
                }

                for (var a = gridstart.Y; a < viewportSize.Y; a += snapSize * ppm)
                {
                    var from = ScreenToWorld(new Vector2(0, a));
                    var to   = ScreenToWorld(new Vector2(viewportSize.X, a));
                    handle.DrawLine(from, to, new Color(0, 0, 1f));
                }
            }

            // Draw grid BELOW the ghost.
            base.Render(handle);
        }
Exemple #3
0
 private void HandleDecompressionFloorRip(float sum)
 {
     if (sum > 20 && _robustRandom.Prob(MathF.Clamp(sum / 100, 0.005f, 0.5f)))
     {
         _gridAtmosphereComponent.PryTile(GridIndices);
     }
 }
Exemple #4
0
        public static bool ContainsPoint(this ReadOnlySpan <Vector2> poly, Vector2 point)
        {
            var result = false;
            var j      = poly.Length - 1;
            var pY     = point.Y;
            var pX     = point.X;

            for (var i = 0; i < poly.Length; i++)
            {
                var iY = poly[i].Y;
                var jY = poly[j].Y;
                var iX = poly[i].X;
                var jX = poly[j].X;

                if (iY < pY && jY >= pY ||
                    jY < pY && iY >= pY &&
                    MathF.FusedMultiplyAdd(
                        (pY - iY) / (jY - iY),
                        jX - iX,
                        iX
                        ) < pX)
                {
                    result = !result;
                }
                j = i;
            }

            return(result);
        }
Exemple #5
0
        public static float Clamp01(float value)
        {
            /*
             * if (value < 0F)
             *  return 0F;
             *
             * if (value > 1F)
             *  return 1F;
             *
             * return value;
             */

            return(MathF.Clamp(value, 0, 1));
        }
Exemple #6
0
        /// <summary>
        ///     Knocks down the mob, making it fall to the ground.
        /// </summary>
        /// <param name="seconds">How many seconds the mob will stay on the ground</param>
        public void Knockdown(float seconds)
        {
            seconds = MathF.Min(_knockdownTimer + (seconds * KnockdownTimeModifier), _knockdownCap);

            if (seconds <= 0f)
            {
                return;
            }

            StandingStateHelper.Down(Owner);

            _knockdownTimer = seconds;
            _lastStun       = _gameTiming.CurTime;

            SetStatusEffect();
            Dirty();
        }
        public override void AlignPlacementMode(ScreenCoordinates mouseScreen)
        {
            MouseCoords = ScreenToCursorGrid(mouseScreen);

            snapSize       = pManager.MapManager.GetGrid(MouseCoords.GridID).SnapSize; //Find snap size.
            GridDistancing = snapSize;
            onGrid         = true;

            var mouselocal = new Vector2( //Round local coordinates onto the snap grid
                (float)MathF.Round(MouseCoords.X / snapSize, MidpointRounding.AwayFromZero) * snapSize,
                (float)MathF.Round(MouseCoords.Y / snapSize, MidpointRounding.AwayFromZero) * snapSize);

            //Convert back to original world and screen coordinates after applying offset
            MouseCoords =
                new GridCoordinates(
                    mouselocal + new Vector2(pManager.PlacementOffset.X, pManager.PlacementOffset.Y), MouseCoords.GridID);
        }
Exemple #8
0
        /// <summary>
        ///     Stuns the entity, disallowing it from doing many interactions temporarily.
        /// </summary>
        /// <param name="seconds">How many seconds the mob will stay stunned</param>
        public void Stun(float seconds)
        {
            seconds = MathF.Min(_stunnedTimer + (seconds * StunTimeModifier), _stunCap);

            if (seconds <= 0f)
            {
                return;
            }

            StandingStateHelper.DropAllItemsInHands(Owner, false);

            _stunnedTimer = seconds;
            _lastStun     = _gameTiming.CurTime;

            SetStatusEffect();
            Dirty();
        }
Exemple #9
0
        protected override Vector2 CalculateMinimumSize()
        {
            var separation = ActualSeparation;

            var minWidth  = 0f;
            var minHeight = 0f;
            var first     = true;

            foreach (var child in Children)
            {
                if (!child.Visible)
                {
                    continue;
                }

                var(childWidth, childHeight) = child.CombinedMinimumSize;
                if (Vertical)
                {
                    minHeight += childHeight;
                    if (!first)
                    {
                        minHeight += separation;
                    }

                    first = false;

                    minWidth = MathF.Max(minWidth, childWidth);
                }
                else
                {
                    minWidth += childWidth;
                    if (!first)
                    {
                        minWidth += separation;
                    }

                    first = false;

                    minHeight = MathF.Max(minHeight, childHeight);
                }
            }

            return(new Vector2(minWidth, minHeight));
        }
Exemple #10
0
        public void ExperiencePressureDifference(int cycle, float pressureDifference, Direction direction,
                                                 float pressureResistanceProbDelta, GridCoordinates throwTarget)
        {
            if (ControlledComponent == null)
            {
                return;
            }

            // TODO ATMOS stuns?

            var transform         = ControlledComponent.Owner.Transform;
            var pressureComponent = ControlledComponent.Owner.GetComponent <MovedByPressureComponent>();
            var maxForce          = MathF.Sqrt(pressureDifference) * 2.25f;
            var moveProb          = 100f;

            if (pressureComponent.PressureResistance > 0)
            {
                moveProb = MathF.Abs((pressureDifference / pressureComponent.PressureResistance * ProbabilityBasePercent) -
                                     ProbabilityOffset);
            }

            if (moveProb > ProbabilityOffset && _robustRandom.Prob(MathF.Min(moveProb / 100f, 1f)) &&
                !float.IsPositiveInfinity(pressureComponent.MoveResist) &&
                (!ControlledComponent.Anchored &&
                 (maxForce >= (pressureComponent.MoveResist * MoveForcePushRatio))) ||
                (ControlledComponent.Anchored && (maxForce >= (pressureComponent.MoveResist * MoveForceForcePushRatio))))
            {
                if (maxForce > ThrowForce && throwTarget != GridCoordinates.InvalidGrid)
                {
                    var moveForce = MathF.Min(maxForce * MathF.Clamp(moveProb, 0, 100) / 100f, 50f);
                    var pos       = throwTarget.Position - transform.GridPosition.Position;
                    LinearVelocity = pos * moveForce;
                }
                else
                {
                    var moveForce = MathF.Min(maxForce * MathF.Clamp(moveProb, 0, 100) / 100f, 25f);
                    LinearVelocity = direction.ToVec() * moveForce;
                }

                pressureComponent.LastHighPressureMovementAirCycle = cycle;
            }
        }
Exemple #11
0
        /// <summary>
        ///     Throw an entity at the position of <paramref name="targetLoc"/> from <paramref name="sourceLoc"/>,
        ///     without overshooting.
        /// </summary>
        /// <param name="thrownEnt">The entity to throw.</param>
        /// <param name="throwForceMax">
        /// The MAXIMUM force to throw the entity with.
        /// Throw force increases with distance to target, this is the maximum force allowed.
        /// </param>
        /// <param name="targetLoc">
        /// The target location to throw at.
        /// This function will try to land at this exact spot,
        /// if <paramref name="throwForceMax"/> is large enough to allow for it to be reached.
        /// </param>
        /// <param name="sourceLoc">
        /// The position to start the throw from.
        /// </param>
        /// <param name="spread">
        /// If true, slightly spread the actual throw angle.
        /// </param>
        /// <param name="throwSourceEnt">
        /// The entity that did the throwing. An opposite impulse will be applied to this entity if passed in.
        /// </param>
        public static void ThrowTo(IEntity thrownEnt, float throwForceMax, GridCoordinates targetLoc,
                                   GridCoordinates sourceLoc, bool spread = false, IEntity throwSourceEnt = null)
        {
            var mapManager = IoCManager.Resolve <IMapManager>();
            var timing     = IoCManager.Resolve <IGameTiming>();

            // Calculate the force necessary to land a throw based on throw duration, mass and distance.
            var distance      = (targetLoc.ToMapPos(mapManager) - sourceLoc.ToMapPos(mapManager)).Length;
            var throwDuration = ThrownItemComponent.DefaultThrowTime;
            var mass          = 1f;

            if (thrownEnt.TryGetComponent(out IPhysicsComponent physicsComponent))
            {
                mass = physicsComponent.Mass;
            }

            var velocityNecessary = distance / throwDuration;
            var impulseNecessary  = velocityNecessary * mass;
            var forceNecessary    = impulseNecessary * (1f / timing.TickRate);

            // Then clamp it to the max force allowed and call Throw().
            Throw(thrownEnt, MathF.Min(forceNecessary, throwForceMax), targetLoc, sourceLoc, spread, throwSourceEnt);
        }
Exemple #12
0
        /// <summary>
        ///     Slows down the mob's walking/running speed temporarily
        /// </summary>
        /// <param name="seconds">How many seconds the mob will be slowed down</param>
        /// <param name="walkModifierOverride">Walk speed modifier. Set to 0 or negative for default value. (0.5f)</param>
        /// <param name="runModifierOverride">Run speed modifier. Set to 0 or negative for default value. (0.5f)</param>
        public void Slowdown(float seconds, float walkModifierOverride = 0f, float runModifierOverride = 0f)
        {
            seconds = MathF.Min(_slowdownTimer + (seconds * SlowdownTimeModifier), _slowdownCap);

            if (seconds <= 0f)
            {
                return;
            }

            WalkModifierOverride = walkModifierOverride;
            RunModifierOverride  = runModifierOverride;

            _slowdownTimer = seconds;
            _lastStun      = _gameTiming.CurTime;

            if (Owner.TryGetComponent(out MovementSpeedModifierComponent movement))
            {
                movement.RefreshMovementSpeedModifiers();
            }

            SetStatusEffect();
            Dirty();
        }
Exemple #13
0
 public static float Min(float a, float b, float c, float d)
 {
     return(MathF.Min(a, MathF.Min(b, MathF.Min(c, d))));
 }
Exemple #14
0
        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void HighPressureMovements()
        {
            // TODO ATMOS finish this

            if (PressureDifference > 15)
            {
                if (_soundCooldown == 0)
                {
                    EntitySystem.Get <AudioSystem>().PlayAtCoords("/Audio/Effects/space_wind.ogg",
                                                                  GridIndices.ToGridCoordinates(_mapManager, GridIndex), AudioHelpers.WithVariation(0.125f).WithVolume(MathF.Clamp(PressureDifference / 10, 10, 100)));
                }
            }


            foreach (var entity in _entityManager.GetEntitiesIntersecting(_mapManager.GetGrid(GridIndex).ParentMapId, Box2.UnitCentered.Translated(GridIndices)))
            {
                if (!entity.TryGetComponent(out ICollidableComponent physics) ||
                    !entity.TryGetComponent(out MovedByPressureComponent pressure))
                {
                    continue;
                }

                var pressureMovements = physics.EnsureController <HighPressureMovementController>();
                if (pressure.LastHighPressureMovementAirCycle < _gridAtmosphereComponent.UpdateCounter)
                {
                    pressureMovements.ExperiencePressureDifference(_gridAtmosphereComponent.UpdateCounter, PressureDifference, _pressureDirection, 0, PressureSpecificTarget?.GridIndices.ToGridCoordinates(_mapManager, GridIndex) ?? GridCoordinates.InvalidGrid);
                }
            }

            if (PressureDifference > 100)
            {
                // Do space wind graphics here!
            }

            _soundCooldown++;
            if (_soundCooldown > 75)
            {
                _soundCooldown = 0;
            }
        }
Exemple #15
0
 public static float Floor(float f) => MathF.Floor(f);
Exemple #16
0
        public bool Intersects(Box2 box, out float distance, out Vector2 hitPos)
        {
            hitPos   = Vector2.Zero;
            distance = 0;

            var         tmin    = 0.0f;           // set to -FLT_MAX to get first hit on line
            var         tmax    = float.MaxValue; // set to max distance ray can travel (for segment)
            const float epsilon = 1.0E-07f;

            // X axis slab
            {
                if (MathF.Abs(_direction.X) < epsilon)
                {
                    // ray is parallel to this slab, it will never hit unless ray is inside box
                    if (_position.X < MathF.Min(box.Left, box.Right) || _position.X > MathF.Max(box.Left, box.Right))
                    {
                        return(false);
                    }
                }

                // calculate intersection t value of ray with near and far plane of slab
                var ood = 1.0f / _direction.X;
                var t1  = (MathF.Min(box.Left, box.Right) - _position.X) * ood;
                var t2  = (MathF.Max(box.Left, box.Right) - _position.X) * ood;

                // Make t1 be the intersection with near plane, t2 with far plane
                if (t1 > t2)
                {
                    MathHelper.Swap(ref t1, ref t2);
                }

                // Compute the intersection of slab intersection intervals
                tmin = MathF.Max(t1, tmin);
                tmax = MathF.Min(t2, tmax); // Is this Min (SE) or Max(Textbook)

                // Exit with no collision as soon as slab intersection becomes empty
                if (tmin > tmax)
                {
                    return(false);
                }
            }

            // Y axis slab
            {
                if (MathF.Abs(_direction.Y) < epsilon)
                {
                    // ray is parallel to this slab, it will never hit unless ray is inside box
                    if (_position.Y < MathF.Min(box.Top, box.Bottom) || _position.Y > MathF.Max(box.Top, box.Bottom))
                    {
                        return(false);
                    }
                }

                // calculate intersection t value of ray with near and far plane of slab
                var ood = 1.0f / _direction.Y;
                var t1  = (MathF.Min(box.Top, box.Bottom) - _position.Y) * ood;
                var t2  = (MathF.Max(box.Top, box.Bottom) - _position.Y) * ood;

                // Make t1 be the intersection with near plane, t2 with far plane
                if (t1 > t2)
                {
                    MathHelper.Swap(ref t1, ref t2);
                }

                // Compute the intersection of slab intersection intervals
                tmin = MathF.Max(t1, tmin);
                tmax = MathF.Min(t2, tmax); // Is this Min (SE) or Max(Textbook)

                // Exit with no collision as soon as slab intersection becomes empty
                if (tmin > tmax)
                {
                    return(false);
                }
            }

            // Ray intersects all slabs. Return point and intersection t value
            hitPos   = _position + _direction * tmin;
            distance = tmin;
            return(true);
        }
Exemple #17
0
 public static float InterpolateCubic(float preA, float a, float b, float postB, float t)
 {
     //return a + 0.5f * t * (b - preA + t * (2.0f * preA - 5.0f * a + 4.0f * b - postB + t * (3.0f * (a - b) + postB - preA)));
     return(MathF.CubicInterpolate(preA, a, b, postB, t));
 }
Exemple #18
0
 public static float Lerp(float a, float b, float blend)
 {
     //return a + (b - a) * blend;
     return(MathF.Interpolate(a, b, blend));
 }
Exemple #19
0
        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void EqualizePressureInZone(int cycleNum)
        {
            if (Air == null || (_tileAtmosInfo.LastCycle >= cycleNum))
            {
                return;                                                        // Already done.
            }
            _tileAtmosInfo = new TileAtmosInfo();

            var startingMoles = Air.TotalMoles;
            var runAtmos      = false;

            // We need to figure if this is necessary
            foreach (var(direction, other) in _adjacentTiles)
            {
                if (other?.Air == null)
                {
                    continue;
                }
                var comparisonMoles = other.Air.TotalMoles;
                if (!(MathF.Abs(comparisonMoles - startingMoles) > Atmospherics.MinimumMolesDeltaToMove))
                {
                    continue;
                }
                runAtmos = true;
                break;
            }

            if (!runAtmos) // There's no need so we don't bother.
            {
                _tileAtmosInfo.LastCycle = cycleNum;
                return;
            }

            var queueCycle = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
            var totalMoles = 0f;
            var tiles      = new TileAtmosphere[Atmospherics.ZumosHardTileLimit];

            tiles[0] = this;
            _tileAtmosInfo.LastQueueCycle = queueCycle;
            var tileCount = 1;

            for (var i = 0; i < tileCount; i++)
            {
                if (i > Atmospherics.ZumosHardTileLimit)
                {
                    break;
                }
                var exploring = tiles[i];

                if (i < Atmospherics.ZumosTileLimit)
                {
                    var tileMoles = exploring.Air.TotalMoles;
                    exploring._tileAtmosInfo.MoleDelta = tileMoles;
                    totalMoles += tileMoles;
                }

                foreach (var(_, adj) in exploring._adjacentTiles)
                {
                    if (adj?.Air == null)
                    {
                        continue;
                    }
                    if (adj._tileAtmosInfo.LastQueueCycle == queueCycle)
                    {
                        continue;
                    }
                    adj._tileAtmosInfo = new TileAtmosInfo();

                    adj._tileAtmosInfo.LastQueueCycle = queueCycle;
                    if (tileCount < Atmospherics.ZumosHardTileLimit)
                    {
                        tiles[tileCount++] = adj;
                    }
                    if (adj.Air.Immutable)
                    {
                        // Looks like someone opened an airlock to space!
                        ExplosivelyDepressurize(cycleNum);
                        return;
                    }
                }
            }

            if (tileCount > Atmospherics.ZumosTileLimit)
            {
                for (var i = Atmospherics.ZumosTileLimit; i < tileCount; i++)
                {
                    //We unmark them. We shouldn't be pushing/pulling gases to/from them.
                    var tile = tiles[i];
                    if (tile == null)
                    {
                        continue;
                    }
                    tiles[i]._tileAtmosInfo.LastQueueCycle = 0;
                }

                tileCount = Atmospherics.ZumosTileLimit;
            }

            //tiles = tiles.AsSpan().Slice(0, tileCount).ToArray(); // According to my benchmarks, this is much slower.
            Array.Resize(ref tiles, tileCount);

            var averageMoles = totalMoles / (tiles.Length);
            var giverTiles   = new List <TileAtmosphere>();
            var takerTiles   = new List <TileAtmosphere>();

            for (var i = 0; i < tileCount; i++)
            {
                var tile = tiles[i];
                tile._tileAtmosInfo.LastCycle  = cycleNum;
                tile._tileAtmosInfo.MoleDelta -= averageMoles;
                if (tile._tileAtmosInfo.MoleDelta > 0)
                {
                    giverTiles.Add(tile);
                }
                else
                {
                    takerTiles.Add(tile);
                }
            }

            var logN = MathF.Log2(tiles.Length);

            // Optimization - try to spread gases using an O(nlogn) algorithm that has a chance of not working first to avoid O(n^2)
            if (giverTiles.Count > logN && takerTiles.Count > logN)
            {
                // Even if it fails, it will speed up the next part.
                Array.Sort(tiles, (a, b)
                           => a._tileAtmosInfo.MoleDelta.CompareTo(b._tileAtmosInfo.MoleDelta));

                foreach (var tile in tiles)
                {
                    tile._tileAtmosInfo.FastDone = true;
                    if (!(tile._tileAtmosInfo.MoleDelta > 0))
                    {
                        continue;
                    }
                    Direction eligibleAdjBits = 0;
                    var       amtEligibleAdj  = 0;
                    foreach (var direction in Cardinal)
                    {
                        if (!tile._adjacentTiles.TryGetValue(direction, out var tile2))
                        {
                            continue;
                        }

                        // skip anything that isn't part of our current processing block. Original one didn't do this unfortunately, which probably cause some massive lag.
                        if (tile2._tileAtmosInfo.FastDone || tile2._tileAtmosInfo.LastQueueCycle != queueCycle)
                        {
                            continue;
                        }

                        eligibleAdjBits |= direction;
                        amtEligibleAdj++;
                    }

                    if (amtEligibleAdj <= 0)
                    {
                        continue;                      // Oof we've painted ourselves into a corner. Bad luck. Next part will handle this.
                    }
                    var molesToMove = tile._tileAtmosInfo.MoleDelta / amtEligibleAdj;
                    foreach (var direction in Cardinal)
                    {
                        if ((eligibleAdjBits & direction) == 0 || !tile._adjacentTiles.TryGetValue(direction, out var tile2))
                        {
                            continue;
                        }
                        tile.AdjustEqMovement(direction, molesToMove);
                        tile._tileAtmosInfo.MoleDelta  -= molesToMove;
                        tile2._tileAtmosInfo.MoleDelta += molesToMove;
                    }
                }

                giverTiles.Clear();
                takerTiles.Clear();

                foreach (var tile in tiles)
                {
                    if (tile._tileAtmosInfo.MoleDelta > 0)
                    {
                        giverTiles.Add(tile);
                    }
                    else
                    {
                        takerTiles.Add(tile);
                    }
                }

                // This is the part that can become O(n^2).
                if (giverTiles.Count < takerTiles.Count)
                {
                    // as an optimization, we choose one of two methods based on which list is smaller. We really want to avoid O(n^2) if we can.
                    var queue = new List <TileAtmosphere>(takerTiles.Count);
                    foreach (var giver in giverTiles)
                    {
                        giver._tileAtmosInfo.CurrentTransferDirection = (Direction)(-1);
                        giver._tileAtmosInfo.CurrentTransferAmount    = 0;
                        var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
                        queue.Clear();
                        queue.Add(giver);
                        giver._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
                        var queueCount = queue.Count;
                        for (var i = 0; i < queueCount; i++)
                        {
                            if (giver._tileAtmosInfo.MoleDelta <= 0)
                            {
                                break; // We're done here now. Let's not do more work than needed.
                            }
                            var tile = queue[i];
                            foreach (var direction in Cardinal)
                            {
                                if (!tile._adjacentTiles.TryGetValue(direction, out var tile2))
                                {
                                    continue;
                                }
                                if (giver._tileAtmosInfo.MoleDelta <= 0)
                                {
                                    break; // We're done here now. Let's not do more work than needed.
                                }
                                if (tile2?._tileAtmosInfo == null || tile2._tileAtmosInfo.LastQueueCycle != queueCycle)
                                {
                                    continue;
                                }

                                if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow)
                                {
                                    continue;
                                }
                                queue.Add(tile2);
                                queueCount++;
                                tile2._tileAtmosInfo.LastSlowQueueCycle       = queueCycleSlow;
                                tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
                                tile2._tileAtmosInfo.CurrentTransferAmount    = 0;
                                if (tile2._tileAtmosInfo.MoleDelta < 0)
                                {
                                    // This tile needs gas. Let's give it to 'em.
                                    if (-tile2._tileAtmosInfo.MoleDelta > giver._tileAtmosInfo.MoleDelta)
                                    {
                                        // We don't have enough gas!
                                        tile2._tileAtmosInfo.CurrentTransferAmount -= giver._tileAtmosInfo.MoleDelta;
                                        tile2._tileAtmosInfo.MoleDelta             += giver._tileAtmosInfo.MoleDelta;
                                        giver._tileAtmosInfo.MoleDelta              = 0;
                                    }
                                    else
                                    {
                                        // We have enough gas.
                                        tile2._tileAtmosInfo.CurrentTransferAmount += tile2._tileAtmosInfo.MoleDelta;
                                        giver._tileAtmosInfo.MoleDelta             += tile2._tileAtmosInfo.MoleDelta;
                                        tile2._tileAtmosInfo.MoleDelta              = 0;
                                    }
                                }
                            }
                        }

                        // Putting this loop here helps make it O(n^2) over O(n^3)
                        for (var i = queue.Count - 1; i >= 0; i--)
                        {
                            var tile = queue[i];
                            if (tile._tileAtmosInfo.CurrentTransferAmount != 0 &&
                                tile._tileAtmosInfo.CurrentTransferDirection != (Direction)(-1))
                            {
                                tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);
                                if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var adjacent))
                                {
                                    adjacent._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
                                }
                                tile._tileAtmosInfo.CurrentTransferAmount = 0;
                            }
                        }
                    }
                }
                else
                {
                    var queue = new List <TileAtmosphere>(giverTiles.Count);
                    foreach (var taker in takerTiles)
                    {
                        taker._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid;
                        taker._tileAtmosInfo.CurrentTransferAmount    = 0;
                        var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
                        queue.Clear();
                        queue.Add(taker);
                        taker._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
                        var queueCount = queue.Count;
                        for (int i = 0; i < queueCount; i++)
                        {
                            if (taker._tileAtmosInfo.MoleDelta >= 0)
                            {
                                break; // We're done here now. Let's not do more work than needed.
                            }
                            var tile = queue[i];
                            foreach (var direction in Cardinal)
                            {
                                if (!tile._adjacentTiles.ContainsKey(direction))
                                {
                                    continue;
                                }
                                var tile2 = tile._adjacentTiles[direction];

                                if (taker._tileAtmosInfo.MoleDelta >= 0)
                                {
                                    break; // We're done here now. Let's not do more work than needed.
                                }
                                if (tile2?._tileAtmosInfo == null || tile2._tileAtmosInfo.LastQueueCycle != queueCycle)
                                {
                                    continue;
                                }
                                if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow)
                                {
                                    continue;
                                }
                                queue.Add(tile2);
                                queueCount++;
                                tile2._tileAtmosInfo.LastSlowQueueCycle       = queueCycleSlow;
                                tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
                                tile2._tileAtmosInfo.CurrentTransferAmount    = 0;

                                if (tile2._tileAtmosInfo.MoleDelta > 0)
                                {
                                    // This tile has gas we can suck, so let's
                                    if (tile2._tileAtmosInfo.MoleDelta > -taker._tileAtmosInfo.MoleDelta)
                                    {
                                        // They have enough gas
                                        tile2._tileAtmosInfo.CurrentTransferAmount -= taker._tileAtmosInfo.MoleDelta;
                                        tile2._tileAtmosInfo.MoleDelta             += taker._tileAtmosInfo.MoleDelta;
                                        taker._tileAtmosInfo.MoleDelta              = 0;
                                    }
                                    else
                                    {
                                        // They don't have enough gas!
                                        tile2._tileAtmosInfo.CurrentTransferAmount += tile2._tileAtmosInfo.MoleDelta;
                                        taker._tileAtmosInfo.MoleDelta             += tile2._tileAtmosInfo.MoleDelta;
                                        tile2._tileAtmosInfo.MoleDelta              = 0;
                                    }
                                }
                            }
                        }

                        for (var i = queue.Count - 1; i >= 0; i--)
                        {
                            var tile = queue[i];
                            if (tile._tileAtmosInfo.CurrentTransferAmount == 0 ||
                                tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid)
                            {
                                continue;
                            }
                            tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);

                            if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var adjacent))
                            {
                                adjacent._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
                            }
                            tile._tileAtmosInfo.CurrentTransferAmount = 0;
                        }
                    }
                }

                foreach (var tile in tiles)
                {
                    tile.FinalizeEq();
                }

                foreach (var tile in tiles)
                {
                    foreach (var direction in Cardinal)
                    {
                        if (!tile._adjacentTiles.TryGetValue(direction, out var tile2))
                        {
                            continue;
                        }
                        if (tile2?.Air?.Compare(Air) == GasMixture.GasCompareResult.NoExchange)
                        {
                            continue;
                        }
                        _gridAtmosphereComponent.AddActiveTile(tile2);
                        break;
                    }
                }
            }
        }
Exemple #20
0
 public static float Max(float a, float b, float c, float d)
 {
     return(MathF.Max(a, MathF.Max(b, MathF.Max(c, d))));
 }
Exemple #21
0
 public static float Median(float a, float b, float c)
 {
     return(MathF.Max(MathF.Min(a, b), MathF.Min(MathF.Max(a, b), c)));
 }
Exemple #22
0
 public static float Clamp(this float val, float min, float max)
 {
     return(MathF.Clamp(val, min, max));
 }