Example #1
0
        public static MotionResult PhysicsStepVertical(ref CharacterPhysicsInfo cpi,
                                                       ref Position position, ref ThreeDVelocity velocity,
                                                       ref bool onGround,
                                                       int coefficientOfRestitution256,
                                                       int gravityUp256, int gravityDown256, bool lockVertical,
                                                       WorldPhysics physics, bool groundIsAPit)
        {
            if (lockVertical)
            {
                velocity.Y.Reset();
            }
            else
            {
                if (velocity.Y.Velocity256 > 0) // Moving upwards
                {
                    onGround = false;

                    int startY = position.Y + cpi.height;
                    position.Y = velocity.Y.Update(position.Y, gravityUp256);
                    int endY = position.Y + cpi.height;

                    if (endY > startY)
                    {
                        int ceiling = physics.GetCeilingHeightInXRange(position.X + cpi.startX, position.X + cpi.endX, position.Z, startY, endY, cpi.owner);
                        if (ceiling < endY) // Hit ceiling
                        {
                            position.Y             = ceiling - cpi.height;
                            velocity.Y.Velocity256 = -((velocity.Y.Velocity256 * coefficientOfRestitution256) >> 8); // <- Bounce (fixed-point multiply)
                            return(MotionResult.HitCeiling);
                        }
                    }
                }
                else // Moving downwards
                {
                    int groundHeight = CharacterPhysics.GroundHeight(ref cpi, physics, position);

                    if (position.Y > groundHeight)
                    {
                        onGround = false;               // we came off the ground
                    }
                    else if (position.Y < groundHeight) // TODO: This needs some rethinking (added to let separation do its thing)
                    {
                        onGround = true;                // we are embedded in the ground (we may start physics in this state, if we are put here by teleport/animation)
                    }
                    else if (position.Y == groundHeight && velocity.Y.Velocity256 == 0)
                    {
                        onGround = true; // we were gently placed on the ground (don't trigger OnHitGround)
                    }
                    if (!onGround)
                    {
                        position.Y = velocity.Y.Update(position.Y, gravityDown256);

                        if (position.Y <= groundHeight) // Hit the ground
                        {
                            position.Y = groundHeight;

                            if (groundHeight == 0 && groundIsAPit)
                            {
                                velocity = new ThreeDVelocity();
                                return(MotionResult.HitBottomOfPit);
                            }
                            else if (velocity.Y.Velocity256 > -128) // <- Kill velocity of we're moving too slowly downwards (start rolling)
                            {
                                onGround = true;
                                velocity.Y.Reset();
                            }
                            else
                            {
                                onGround = false;
                                velocity.Y.Scale256(-coefficientOfRestitution256);
                                velocity.X.Scale256(coefficientOfRestitution256);
                                velocity.Z.Scale256(coefficientOfRestitution256);
                            }

                            return(MotionResult.HitGround);
                        }
                    }
                }
            }

            return(MotionResult.None);
        }
Example #2
0
        /// <summary>Get the Y position of the ground for this actor.</summary>
        public int GroundHeight(WorldPhysics physics)
        {
            var cpi = new CharacterPhysicsInfo(animationSet, this);

            return(CharacterPhysics.GroundHeight(ref cpi, physics, position));
        }
Example #3
0
        public void DoSeparation(UpdateContext updateContext)
        {
            //
            // Process anyone requesting to be updated before separation
            //

            foreach (var i in collidersToUpdateBeforeSeparation)
            {
                Actor         actor         = (Actor)colliderEntries[i].owner; // <- will always succeed, because we registered it that way
                HeightmapView heightmapView = new HeightmapView(actor.animationSet.Heightmap, actor.position, actor.facingLeft);
                ChangeCollider(i, heightmapView);
            }



            //
            // Remove everyone from static geometry
            //

            for (int i = 0; i < moverCount; i++)
            {
                if (moverWeDoNotMove[i])
                {
                    continue;
                }

                if (moverActors[i].IncomingConnection != null)
                {
                    continue; // Don't separate if we're attached to someone
                }
                var cpi = moverCharacterPhysics[i];
                int simpleGroundHeight = CharacterPhysics.GroundHeight(ref cpi, this, moverActors[i].position, true);
                if (simpleGroundHeight <= moverActors[i].position.Y)
                {
                    continue; // We are in open space
                }
                //
                // Oh no! We are stuck in the level!
                //


                // Attempt 1: Move back to start point, and then try moving to new position:
                //            (Doing this first, to try to prevent tunneling behaviour - also probably faster than TryToFitNearestInZSlice)
                Position moveDelta = moverActors[i].position - moverInitialPositions[i];
                if (moveDelta != Position.Zero && // <- Can't undo non-moves.
                    moveDelta.ManhattenLength <= 8)        // <- Don't try to undo huge moves, as they may be teleports/animations, and we could get snagged on the way there.
                {
                    var groundHeightAtInitial = CharacterPhysics.GroundHeight(ref cpi, this, moverInitialPositions[i], true);
                    if (groundHeightAtInitial <= moverInitialPositions[i].Y) // <- OK at initial position
                    {
                        // Attempt to move towards target:
                        Position target = moverActors[i].position;
                        moverActors[i].position = moverInitialPositions[i];

                        CharacterPhysics.TryMove(ref moverCharacterPhysics[i], this, moveDelta, false, ref moverActors[i].position, true);

                        continue; // Success!
                    }
                }

                // Attempt 2: Attempt to extricate ourselves from the situation directly
                #region // a whole bunch of code
                {
                    int  statsZSliceCount = 0;
                    long statsVoxelCount  = 0;

                    Position desiredPosition = moverActors[i].position;

                    // Ensure we didn't come out of the level entirely
                    if (desiredPosition.X < StartX)
                    {
                        desiredPosition.X = StartX;
                    }
                    if (desiredPosition.X >= EndX)
                    {
                        desiredPosition.X = EndX - 1;
                    }
                    if (desiredPosition.Y < 0)
                    {
                        desiredPosition.Y = 0;
                    }
                    if (desiredPosition.Z < StartZ)
                    {
                        desiredPosition.Z = StartZ;
                    }
                    if (desiredPosition.Z >= EndZ)
                    {
                        desiredPosition.Z = EndZ - 1;
                    }

                    // Grab the nearest point on our current plane:
                    const int maxRadiusXY = 128;
                    Point     nearest     = TryToFitNearestInZSlice(moverCharacterPhysics[i].startX, moverCharacterPhysics[i].endX, moverCharacterPhysics[i].height,
                                                                    desiredPosition.X, desiredPosition.Y, moverActors[i].position.Y,
                                                                    desiredPosition.X - maxRadiusXY, desiredPosition.X + maxRadiusXY, desiredPosition.Z, desiredPosition.Y - maxRadiusXY, desiredPosition.Y + maxRadiusXY,
                                                                    moverActors[i], true);
                    statsZSliceCount++;
                    statsVoxelCount += (maxRadiusXY * maxRadiusXY * 4);

                    Position bestPosition;
                    int      bestDistance;
                    if (nearest != NoFitFound)
                    {
                        bestDistance = Math.Abs(desiredPosition.X - nearest.X) + Math.Abs(desiredPosition.Y - nearest.Y);
                        bestPosition = new Position(nearest.X, nearest.Y, desiredPosition.Z);

                        // Debugging: If check that we're not ending up back in the same place (why did we try to separate, if we fit?)
                        //            If this assert fires, there is a bug in the separation algorithm.
                        // NOTE: checks vs non-clipped position!
                        Debug.Assert(Position.ManhattenDistance(moverActors[i].position, bestPosition) > 0,
                                     "SEPARATION DID NOTHING. Actor is: " + moverActors[i].ToString()
                                     + "\n\nIf you can reproduce this, please report to Andrew. Otherwise hit \"Ignore\" and carry on.");
                    }
                    else
                    {
                        bestPosition = default(Position);
                        bestDistance = int.MaxValue;
                    }


                    // Search forwards and backwards for better positions on nearby planes:
                    const int maxRadiusZ        = 10;
                    int       searchBoundStartZ = Math.Max(StartZ, desiredPosition.Z - maxRadiusZ);
                    int       searchBoundEndZ   = Math.Min(EndZ, desiredPosition.Z + maxRadiusZ);

                    // Searching forwards:  (NOTE: Copy-paste with searching backwards)
                    int searchZ = desiredPosition.Z - 1;
                    // NOTE: Checking vs bestDistance each time, so we might be able to early-out
                    while (searchZ >= searchBoundStartZ && (desiredPosition.Z - searchZ) < bestDistance) // <- IMPORTANT: not doing maths on bestDistance, as it can be int.MaxValue!
                    {
                        Debug.Assert(bestDistance > 0);
                        int radiusXY = Math.Min(bestDistance, maxRadiusXY); // <- hopefully reduces search range

                        nearest = TryToFitNearestInZSlice(moverCharacterPhysics[i].startX, moverCharacterPhysics[i].endX, moverCharacterPhysics[i].height,
                                                          desiredPosition.X, desiredPosition.Y, moverActors[i].position.Y,
                                                          desiredPosition.X - radiusXY, desiredPosition.X + radiusXY, searchZ, desiredPosition.Y - radiusXY, desiredPosition.Y + radiusXY,
                                                          moverActors[i], true);
                        statsZSliceCount++;
                        statsVoxelCount += (radiusXY * radiusXY * 4);

                        if (nearest != NoFitFound)
                        {
                            int distance = Math.Abs(desiredPosition.X - nearest.X) + Math.Abs(desiredPosition.Y - nearest.Y) + Math.Abs(desiredPosition.Z - searchZ);
                            if (distance < bestDistance)
                            {
                                bestDistance = distance;
                                bestPosition = new Position(nearest.X, nearest.Y, searchZ);
                            }
                        }

                        searchZ--;
                    }

                    // Searching backwards:  (NOTE: Copy-paste with searching forwards)
                    searchZ = desiredPosition.Z + 1;
                    // NOTE: Checking vs bestDistance each time, so we might be able to early-out
                    while (searchZ < searchBoundEndZ && (searchZ - desiredPosition.Z) < bestDistance) // <- IMPORTANT: not doing maths on bestDistance, as it can be int.MaxValue!
                    {
                        Debug.Assert(bestDistance > 0);
                        int radiusXY = Math.Min(bestDistance, maxRadiusXY); // <- hopefully reduces search range

                        nearest = TryToFitNearestInZSlice(moverCharacterPhysics[i].startX, moverCharacterPhysics[i].endX, moverCharacterPhysics[i].height,
                                                          desiredPosition.X, desiredPosition.Y, moverActors[i].position.Y,
                                                          desiredPosition.X - radiusXY, desiredPosition.X + radiusXY, searchZ, desiredPosition.Y - radiusXY, desiredPosition.Y + radiusXY,
                                                          moverActors[i], true);
                        statsZSliceCount++;
                        statsVoxelCount += (radiusXY * radiusXY * 4);

                        if (nearest != NoFitFound)
                        {
                            int distance = Math.Abs(desiredPosition.X - nearest.X) + Math.Abs(desiredPosition.Y - nearest.Y) + Math.Abs(desiredPosition.Z - searchZ);
                            if (distance < bestDistance)
                            {
                                bestDistance = distance;
                                bestPosition = new Position(nearest.X, nearest.Y, searchZ);
                            }
                        }

                        searchZ++;
                    }

#if DEBUG                                                                                                      // Separation stats
                    {
                        bool isActuallyTheMainSimulation = (updateContext.DebugNetworkUnsafeHasLocalSettings); // <- Somewhat hacky mechanisim so we don't get debug writes for "alternate universes" when debugging
                        if (isActuallyTheMainSimulation)
                        {
                            Debug.WriteLine("Separation of \"" + moverActors[i] + "\" searched " + statsZSliceCount + " Z slices, checking "
                                            + statsVoxelCount + " voxels; moved from " + moverActors[i].position + " to " + bestPosition + ".");
                        }
                    }
#endif

                    if (bestDistance != int.MaxValue) // <- Did we find a suitable position?
                    {
                        moverActors[i].position = bestPosition;
                        continue;
                    }
                }
                #endregion


                //
                // At this point... holy crap... how did you manage to get an object here??
                //

                stuckObjects.Add(moverActors[i]); // <- game state can deal with it.
            }
        }