示例#1
0
 /// <summary>
 /// Returns the first hit of a given ray tested against the physics world, does filtering based on the given CollisionGroup only solid collisions will be reported
 /// </summary>
 /// <param name="ray">Ray to test against</param>
 /// <param name="length">Maximum distance to test</param>
 /// <param name="group">Collision Group to use for the ray, anything above "Normal" is ignored</param>
 /// <param name="outHitResult">The hit that was found if any</param>
 /// <returns>True if any object was hit</returns>
 public bool Raycast(ref Ray ray, float length, CollisionGroup group, ref SRaycastResult outHitResult)
 {
     return(Raycast(ref ray, length, GetSolidFilter(group), ref outHitResult));
 }
示例#2
0
 static Player()
 {
     CollisionGroup.DefineCollisionRule(noCollisionGroup, GameModel.GhostGroup, CollisionRule.NoBroadPhase);
     CollisionGroup.DefineCollisionRule(noCollisionGroup, GameModel.NormalGroup, CollisionRule.NoBroadPhase);
 }
		public TilemapCollisionComponent()
		{
			Group = CollisionGroup.None;
		}
示例#4
0
        private CollisionRule GetCollisionRuleWithGroup(ICollisionRulesOwner rulesOwner, CollisionGroup group)
        {
            if (rulesOwner.CollisionRules.Personal != CollisionRule.Defer)
            {
                return(rulesOwner.CollisionRules.Personal);
            }

            CollisionGroupPair pair = new CollisionGroupPair(group, rulesOwner.CollisionRules.Group);

            CollisionRules.CollisionGroupRules.TryGetValue(pair, out CollisionRule rule);
            return(rule);
        }
示例#5
0
        /// <summary>
        /// Check if the character's interaction area collides with the world object click area.
        /// The character also should not be dead.
        /// </summary>
        public virtual bool SharedIsInsideCharacterInteractionArea(
            ICharacter character,
            TWorldObject worldObject,
            bool writeToLog,
            CollisionGroup requiredCollisionGroup = null)
        {
            if (worldObject.IsDestroyed)
            {
                return(false);
            }

            try
            {
                this.VerifyGameObject(worldObject);
            }
            catch (Exception ex)
            {
                if (writeToLog)
                {
                    Logger.Exception(ex);
                }
                else
                {
                    Logger.Warning(ex.Message + " during " + nameof(SharedIsInsideCharacterInteractionArea));
                }

                return(false);
            }

            if (character.GetPublicState <ICharacterPublicState>().IsDead ||
                IsServer && !character.ServerIsOnline ||
                ((IsServer || character.IsCurrentClientCharacter) &&
                 PlayerCharacter.GetPrivateState(character).IsDespawned))
            {
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {worldObject} - character is offline/despawned/dead.",
                        character);
                }

                return(false);
            }

            bool isInsideInteractionArea;

            if (worldObject.PhysicsBody.HasShapes &&
                worldObject.PhysicsBody.HasAnyShapeCollidingWithGroup(CollisionGroups.ClickArea))
            {
                // check that the world object is inside the interaction area of the character
                using var objectsInCharacterInteractionArea
                          = InteractionCheckerSystem.SharedGetTempObjectsInCharacterInteractionArea(
                                character,
                                writeToLog,
                                requiredCollisionGroup);

                isInsideInteractionArea = false;
                if (objectsInCharacterInteractionArea is not null)
                {
                    foreach (var t in objectsInCharacterInteractionArea.AsList())
                    {
                        if (!ReferenceEquals(worldObject, t.PhysicsBody.AssociatedWorldObject))
                        {
                            continue;
                        }

                        isInsideInteractionArea = true;
                        break;
                    }
                }
            }
            else if (worldObject.ProtoWorldObject is IProtoStaticWorldObject protoStaticWorldObject)
            {
                // the world object doesn't have click area collision shapes
                // check this object tile by tile
                // ensure at least one tile of this object is inside the character interaction area
                // ensure there is direct line of sight between player character and this tile

                var characterInteractionAreaShape = character.PhysicsBody.Shapes.FirstOrDefault(
                    s => s.CollisionGroup
                    == CollisionGroups.CharacterInteractionArea);
                isInsideInteractionArea = false;
                foreach (var tileOffset in protoStaticWorldObject.Layout.TileOffsets)
                {
                    var penetration = character.PhysicsBody.PhysicsSpace.TestShapeCollidesWithShape(
                        sourceShape: characterInteractionAreaShape,
                        targetShape: new RectangleShape(
                            position: (worldObject.TilePosition + tileOffset).ToVector2D()
                            + (0.01, 0.01),
                            size: (0.98, 0.98),
                            collisionGroup: CollisionGroups.ClickArea),
                        sourceShapeOffset: character.PhysicsBody.Position);

                    if (!penetration.HasValue)
                    {
                        // this tile is not inside the character interaction area
                        continue;
                    }

                    // the tile is inside the character interaction area
                    // check that there is a direct line between the character and the tile
                    isInsideInteractionArea = true;
                    break;
                }
            }
            else
            {
                isInsideInteractionArea = false;
            }

            if (!isInsideInteractionArea)
            {
                // the world object is outside the character interaction area
                if (writeToLog)
                {
                    Logger.Warning(
                        $"Character cannot interact with {worldObject} - outside the interaction area.",
                        character);

                    if (IsClient)
                    {
                        ClientOnCannotInteract(worldObject, CoreStrings.Notification_TooFar, isOutOfRange: true);
                    }
                }

                return(false);
            }

            if (character.ProtoCharacter is PlayerCharacterSpectator)
            {
                // don't test for obstacles for spectator character
                return(true);
            }

            // check that there are no other objects on the way between them (defined by default layer)
            var physicsSpace    = character.PhysicsBody.PhysicsSpace;
            var characterCenter = character.Position + character.PhysicsBody.CenterOffset;

            if (!ObstacleTestHelper.SharedHasObstaclesInTheWay(characterCenter,
                                                               physicsSpace,
                                                               worldObject,
                                                               sendDebugEvents: writeToLog))
            {
                return(true);
            }

            if (writeToLog)
            {
                Logger.Warning(
                    $"Character cannot interact with {worldObject} - there are other objects on the way.",
                    character);

                if (IsClient)
                {
                    ClientOnCannotInteract(worldObject,
                                           CoreStrings.Notification_ObstaclesOnTheWay,
                                           isOutOfRange: true);
                }
            }

            return(false);
        }
示例#6
0
        /// <summary>
        /// Sweeps a sphere against the physics world reporting the closest hit to the start location, beware convex sweeps can be very costly especially if they are long
        /// </summary>
        /// <param name="radius">Radius of the sphere</param>
        /// <param name="startPos">Location to start the sweep from</param>
        /// <param name="sweep">Direction in which to sweep, the length of the vector is the length of the sweep</param>
        /// <param name="group">Collision group to filter the sweep hits, anything above "Normal" is ignored</param>
        /// <param name="outSweepResult">Closest hit to the start location</param>
        /// <returns>True if any object was hit</returns>
        public bool SphereSweep(float radius, ref SharpDX.Vector3 startPos, ref SharpDX.Vector3 sweep, CollisionGroup group, ref SRaycastResult outSweepResult)
        {
            SphereShape sphereShape = new SphereShape(radius);
            Quaternion  rotation    = Quaternion.Identity;

            return(ConvexSweep(sphereShape, ref startPos, ref rotation, ref sweep, group, ref outSweepResult));
        }
示例#7
0
        /// <summary>
        /// Sweeps a capsule against the physics world reporting the closest hit to the start location, beware convex sweeps can be very costly especially if they are long
        /// </summary>
        /// <param name="length">Length of the capsule</param>
        /// <param name="radius">Radius of the capsule</param>
        /// <param name="startPos">Location to start the sweep from</param>
        /// <param name="rotation">Rotation of the shape during the sweep</param>
        /// <param name="sweep">Direction in which to sweep, the length of the vector is the length of the sweep</param>
        /// <param name="group">Collision group to filter the sweep hits, anything above "Normal" is ignored</param>
        /// <param name="outSweepResult">Closest hit to the start location</param>
        /// <returns>True if any object was hit</returns>
        public bool CapsuleSweep(float length, float radius, ref SharpDX.Vector3 startPos, ref SharpDX.Quaternion rotation, ref SharpDX.Vector3 sweep, CollisionGroup group, ref SRaycastResult outSweepResult)
        {
            CapsuleShape capsuleShape = new CapsuleShape(length, radius);

            return(ConvexSweep(capsuleShape, ref startPos, ref rotation, ref sweep, group, ref outSweepResult));
        }
示例#8
0
        public bool SharedValidatePlacement(ICharacter character, Vector2Ushort targetPosition, bool logErrors)
        {
            // check if there is a direct line of sight
            // check that there are no other objects on the way between them (defined by default layer)
            var physicsSpace      = character.PhysicsBody.PhysicsSpace;
            var characterCenter   = character.Position + character.PhysicsBody.CenterOffset;
            var worldObjectCenter = targetPosition.ToVector2D() + (0.5, 0.5);

            // local method for testing if there is an obstacle from current to the specified position
            bool TestHasObstacle(Vector2D toPosition)
            {
                using (var obstaclesOnTheWay = physicsSpace.TestLine(
                           characterCenter,
                           toPosition,
                           CollisionGroup.GetDefault(),
                           sendDebugEvent: false))
                {
                    foreach (var test in obstaclesOnTheWay)
                    {
                        var testPhysicsBody = test.PhysicsBody;
                        if (testPhysicsBody.AssociatedProtoTile != null)
                        {
                            // obstacle tile on the way
                            return(true);
                        }

                        var testWorldObject = testPhysicsBody.AssociatedWorldObject;
                        if (testWorldObject == character)
                        {
                            // not an obstacle - it's the character or world object itself
                            continue;
                        }

                        // obstacle object on the way
                        return(true);
                    }

                    // no obstacles
                    return(false);
                }
            }

            // let's test by casting rays from character center to the center of the planted bomb
            if (TestHasObstacle(worldObjectCenter))
            {
                // has obstacle
                if (logErrors)
                {
                    if (IsClient)
                    {
                        this.ClientShowCannotPlaceObstaclesOnTheWayNotification();
                    }
                    else
                    {
                        Logger.Warning($"{character} cannot place {this} - obstacles on the way");
                        this.CallClient(character, _ => _.ClientRemote_CannotPlaceObstacles());
                    }
                }

                return(false);
            }

            // validate distance to the character
            if (targetPosition.TileDistanceTo(character.TilePosition)
                > this.DeployDistanceMax)
            {
                // distance exceeded - too far
                if (logErrors)
                {
                    if (IsClient)
                    {
                        this.ClientShowCannotPlaceTooFarNotification();
                    }
                    else
                    {
                        Logger.Warning($"{character} cannot place {this} - too far");
                        this.CallClient(character, _ => _.ClientRemote_CannotPlaceTooFar());
                    }
                }

                return(false);
            }

            if (!this.ObjectExplosiveProto.CheckTileRequirements(targetPosition,
                                                                 character,
                                                                 logErrors))
            {
                // explosive static object placement requirements failed
                return(false);
            }

            return(true);
        }
示例#9
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="body"></param>
 /// <param name="collisionGroup"></param>
 /// <param name="onCollision">If you return true, the collision will be resolved.</param>
 public Collider(Body body, CollisionGroup collisionGroup, Func <CollisionData, bool> onCollision)
 {
     this.body           = body;
     this.collisionGroup = collisionGroup;
     this.onCollision    = onCollision;
 }
示例#10
0
文件: Body.cs 项目: siquel/BeatEmUp
 public void CollidesWithGroup(CollisionGroup mask)
 {
     BroadphaseProxy.CollisionFilterGroup = mask;
 }
示例#11
0
文件: Body.cs 项目: siquel/BeatEmUp
 public bool BelongsToGroup(CollisionGroup group)
 {
     return((BroadphaseProxy.CollisionFilterGroup & group) != CollisionGroup.None);
 }
示例#12
0
文件: Body.cs 项目: siquel/BeatEmUp
 public void SetCollisionGroup(CollisionGroup group)
 {
     BroadphaseProxy.CollisionGroup = group;
 }
示例#13
0
        private static void SharedShotWeaponHitscan(
            ICharacter character,
            IProtoItemWeapon protoWeapon,
            Vector2D fromPosition,
            WeaponFinalCache weaponCache,
            Vector2D?customTargetPosition,
            IProtoCharacterCore characterProtoCharacter,
            double fireSpreadAngleOffsetDeg,
            CollisionGroup collisionGroup,
            bool isMeleeWeapon,
            IDynamicWorldObject characterCurrentVehicle,
            ProtoSkillWeapons protoWeaponSkill,
            PlayerCharacterSkills playerCharacterSkills,
            ITempList <IWorldObject> allHitObjects)
        {
            Vector2D toPosition;
            var      rangeMax = weaponCache.RangeMax;

            if (customTargetPosition.HasValue)
            {
                var direction = customTargetPosition.Value - fromPosition;
                // ensure the max range is not exceeded
                direction  = direction.ClampMagnitude(rangeMax);
                toPosition = fromPosition + direction;
            }
            else
            {
                toPosition = fromPosition
                             + new Vector2D(rangeMax, 0)
                             .RotateRad(characterProtoCharacter.SharedGetRotationAngleRad(character)
                                        + fireSpreadAngleOffsetDeg * Math.PI / 180.0);
            }

            using var lineTestResults = character.PhysicsBody.PhysicsSpace.TestLine(
                      fromPosition: fromPosition,
                      toPosition: toPosition,
                      collisionGroup: collisionGroup);
            var damageMultiplier    = 1d;
            var hitObjects          = new List <WeaponHitData>(isMeleeWeapon ? 1 : lineTestResults.Count);
            var characterTileHeight = character.Tile.Height;

            if (IsClient ||
                Api.IsEditor)
            {
                SharedEditorPhysicsDebugger.SharedVisualizeTestResults(lineTestResults, collisionGroup);
            }

            var isDamageRayStopped = false;

            foreach (var testResult in lineTestResults.AsList())
            {
                var testResultPhysicsBody = testResult.PhysicsBody;
                var attackedProtoTile     = testResultPhysicsBody.AssociatedProtoTile;
                if (attackedProtoTile != null)
                {
                    if (attackedProtoTile.Kind != TileKind.Solid)
                    {
                        // non-solid obstacle - skip
                        continue;
                    }

                    var attackedTile = IsServer
                                           ? Server.World.GetTile((Vector2Ushort)testResultPhysicsBody.Position)
                                           : Client.World.GetTile((Vector2Ushort)testResultPhysicsBody.Position);

                    if (attackedTile.Height < characterTileHeight)
                    {
                        // attacked tile is below - ignore it
                        continue;
                    }

                    // tile on the way - blocking damage ray
                    isDamageRayStopped = true;
                    var hitData = new WeaponHitData(testResult.PhysicsBody.Position
                                                    + SharedOffsetHitWorldPositionCloserToTileHitboxCenter(
                                                        testResultPhysicsBody,
                                                        testResult.Penetration,
                                                        isRangedWeapon: !isMeleeWeapon));
                    hitObjects.Add(hitData);

                    weaponCache.ProtoWeapon
                    .SharedOnHit(weaponCache,
                                 null,
                                 0,
                                 hitData,
                                 out _);
                    break;
                }

                var damagedObject = testResultPhysicsBody.AssociatedWorldObject;
                if (ReferenceEquals(damagedObject, character) ||
                    ReferenceEquals(damagedObject, characterCurrentVehicle))
                {
                    // ignore collision with self
                    continue;
                }

                if (!(damagedObject.ProtoGameObject is IDamageableProtoWorldObject damageableProto))
                {
                    // shoot through this object
                    continue;
                }

                // don't allow damage is there is no direct line of sight on physical colliders layer between the two objects
                if (SharedHasTileObstacle(character.Position,
                                          characterTileHeight,
                                          damagedObject,
                                          targetPosition: testResult.PhysicsBody.Position
                                          + testResult.PhysicsBody.CenterOffset))
                {
                    continue;
                }

                using (CharacterDamageContext.Create(attackerCharacter: character,
                                                     damagedObject as ICharacter,
                                                     protoWeaponSkill))
                {
                    if (!damageableProto.SharedOnDamage(
                            weaponCache,
                            damagedObject,
                            damageMultiplier,
                            damagePostMultiplier: 1.0,
                            out var obstacleBlockDamageCoef,
                            out var damageApplied))
                    {
                        // not hit
                        continue;
                    }

                    var hitData = new WeaponHitData(damagedObject,
                                                    testResult.Penetration.ToVector2F());
                    weaponCache.ProtoWeapon
                    .SharedOnHit(weaponCache,
                                 damagedObject,
                                 damageApplied,
                                 hitData,
                                 out var isDamageStop);

                    if (isDamageStop)
                    {
                        obstacleBlockDamageCoef = 1;
                    }

                    if (IsServer)
                    {
                        if (damageApplied > 0 &&
                            damagedObject is ICharacter damagedCharacter)
                        {
                            CharacterUnstuckSystem.ServerTryCancelUnstuckRequest(damagedCharacter);
                        }

                        if (damageApplied > 0)
                        {
                            // give experience for damage
                            protoWeaponSkill?.ServerOnDamageApplied(playerCharacterSkills,
                                                                    damagedObject,
                                                                    damageApplied);
                        }
                    }

                    if (obstacleBlockDamageCoef < 0 ||
                        obstacleBlockDamageCoef > 1)
                    {
                        Logger.Error(
                            "Obstacle block damage coefficient should be >= 0 and <= 1 - wrong calculation by "
                            + damageableProto);
                        break;
                    }

                    hitObjects.Add(hitData);

                    if (isMeleeWeapon)
                    {
                        // currently melee weapon could attack only one object on the ray
                        isDamageRayStopped = true;
                        break;
                    }

                    damageMultiplier *= 1.0 - obstacleBlockDamageCoef;
                    if (damageMultiplier <= 0)
                    {
                        // target blocked the damage ray
                        isDamageRayStopped = true;
                        break;
                    }
                }
            }

            var shotEndPosition = GetShotEndPosition(isDamageRayStopped,
                                                     hitObjects,
                                                     toPosition,
                                                     isRangedWeapon: !isMeleeWeapon);

            if (hitObjects.Count == 0)
            {
                protoWeapon.SharedOnMiss(weaponCache,
                                         shotEndPosition);
            }

            SharedCallOnWeaponHitOrTrace(character,
                                         protoWeapon,
                                         weaponCache.ProtoAmmo,
                                         shotEndPosition,
                                         hitObjects,
                                         endsWithHit: isDamageRayStopped);

            foreach (var entry in hitObjects)
            {
                if (!entry.IsCliffsHit &&
                    !allHitObjects.Contains(entry.WorldObject))
                {
                    allHitObjects.Add(entry.WorldObject);
                }
            }
        }
 public void Register(CollisionGroup a, CollisionGroup b, Action <ICollider, ICollider> action)
 {
     lookup[Tuple.Create(a, b)] = action;
 }
示例#15
0
 /// <summary>
 /// Sweeps a convex shape against the physics world reporting the closest hit to the start location, beware convex sweeps can be very costly especially if they are long
 /// </summary>
 /// <param name="shape">Shape to sweep</param>
 /// <param name="startPos">Location to start the sweep from</param>
 /// <param name="rotation">Rotation of the shape during the sweep</param>
 /// <param name="sweep">Direction in which to sweep, the length of the vector is the length of the sweep</param>
 /// <param name="group">Collision group to filter the sweep hits, anything above "Normal" is ignored</param>
 /// <param name="outSweepResult">Closest hit to the start location</param>
 /// <returns>True if any object was hit</returns>
 public bool ConvexSweep(ConvexShape shape, ref SharpDX.Vector3 startPos, ref SharpDX.Quaternion rotation, ref SharpDX.Vector3 sweep, CollisionGroup group, ref SRaycastResult outSweepResult)
 {
     return(ConvexSweep(shape, ref startPos, ref rotation, ref sweep, GetSolidFilter(group), ref outSweepResult));
 }
示例#16
0
 /// <summary>
 ///
 /// </summary>
 /// <param name="body"></param>
 /// <param name="pos">Position relitive to body Pos.</param>
 /// <param name="radius"></param>
 /// <param name="OnCollision"></param>
 public CircleCollider(Body body, CollisionGroup collisionGroup, Vector2 pos, float radius, Func <CollisionData, bool> onCollision) : base(body, collisionGroup, onCollision)
 {
     this.pos    = pos;
     this.radius = radius;
 }
示例#17
0
        /// <summary>
        /// Sweeps a convex shape against the physics world reporting all hits on the path that are not ignored by the filter, beware convex sweeps can be very costly especially if they are long
        /// </summary>
        /// <param name="shape">Shape to sweep</param>
        /// <param name="startPos">Location to start the sweep from</param>
        /// <param name="rotation">Rotation of the shape during the sweep</param>
        /// <param name="sweep">Direction in which to sweep, the length of the vector is the length of the sweep</param>
        /// <param name="group">Collision group to filter the sweep hits, anything above "NoSolver" is ignored</param>
        /// <param name="outSweepResults">All overlaps on the sweep path</param>
        /// <returns>True if any object was hit</returns>
        public bool MultiSweep(ConvexShape shape, ref SharpDX.Vector3 startPos, ref SharpDX.Quaternion rotation, ref SharpDX.Vector3 sweep, CollisionGroup group, List <SRaycastResult> outSweepResults)
        {
            RigidTransform       startTransform = new RigidTransform(startPos.ToBepu(), rotation.ToBepu());
            Vector3              sweepVector    = sweep.ToBepu();
            List <RayCastResult> physicsResults = new List <RayCastResult>();

            if (m_physicSpace.ConvexCast(shape, ref startTransform, ref sweepVector, GetOverlapFilter(group), physicsResults))
            {
                foreach (RayCastResult physicsResult in physicsResults)
                {
                    if (physicsResult.HitObject.Tag is CEntity gameEntity)
                    {
                        CollisionRule  rule       = GetCollisionRuleWithGroup(physicsResult.HitObject, group);
                        SRaycastResult gameResult = new SRaycastResult()
                        {
                            HitEntity   = gameEntity,
                            Location    = physicsResult.HitData.Location.ToSharp(),
                            Normal      = -physicsResult.HitData.Normal.ToSharp(),
                            Distance    = physicsResult.HitData.T,
                            bIsSolidHit = rule <= CollisionRule.Normal
                        };
                        outSweepResults.Add(gameResult);
                    }
                }
                return(outSweepResults.Count > 0);
            }

            return(false);
        }
示例#18
0
        public static void ServerProcessExplosionCircle(
            Vector2D positionEpicenter,
            IPhysicsSpace physicsSpace,
            double damageDistanceMax,
            WeaponFinalCache weaponFinalCache,
            bool damageOnlyDynamicObjects,
            bool isDamageThroughObstacles,
            Func <double, double> callbackCalculateDamageCoefByDistanceForStaticObjects,
            Func <double, double> callbackCalculateDamageCoefByDistanceForDynamicObjects,
            [CanBeNull] CollisionGroup collisionGroup = null)
        {
            var playerCharacterSkills = weaponFinalCache.Character?.SharedGetSkills();
            var protoWeaponSkill      = playerCharacterSkills is not null
                                       ? weaponFinalCache.ProtoWeapon?.WeaponSkillProto
                                       : null;

            // collect all damaged objects via physics space
            var damageCandidates = new HashSet <IWorldObject>();

            collisionGroup ??= CollisionGroups.Default;

            var defaultCollisionGroup = collisionGroup;

            CollectDamagedPhysicalObjects(defaultCollisionGroup);
            CollectDamagedPhysicalObjects(CollisionGroups.HitboxMelee);
            CollectDamagedPhysicalObjects(CollisionGroups.HitboxRanged);

            void CollectDamagedPhysicalObjects(CollisionGroup collisionGroup)
            {
                using var testResults = physicsSpace.TestCircle(positionEpicenter,
                                                                radius: damageDistanceMax,
                                                                collisionGroup: collisionGroup);
                foreach (var testResult in testResults.AsList())
                {
                    var testResultPhysicsBody = testResult.PhysicsBody;
                    var damagedObject         = testResultPhysicsBody.AssociatedWorldObject;

                    if (damageOnlyDynamicObjects &&
                        damagedObject is IStaticWorldObject)
                    {
                        continue;
                    }

                    if (!(damagedObject?.ProtoWorldObject is IDamageableProtoWorldObject))
                    {
                        // non-damageable world object
                        continue;
                    }

                    damageCandidates.Add(damagedObject);
                }
            }

            if (!damageOnlyDynamicObjects)
            {
                // Collect all the damageable static objects in the explosion radius
                // which don't have a collider colliding with the collision group.
                var startTilePosition        = positionEpicenter.ToVector2Ushort();
                var damageDistanceMaxRounded = (int)damageDistanceMax;
                var damageDistanceMaxSqr     = damageDistanceMax * damageDistanceMax;
                var minTileX = startTilePosition.X - damageDistanceMaxRounded;
                var minTileY = startTilePosition.Y - damageDistanceMaxRounded;
                var maxTileX = startTilePosition.X + damageDistanceMaxRounded;
                var maxTileY = startTilePosition.Y + damageDistanceMaxRounded;

                for (var x = minTileX; x <= maxTileX; x++)
                {
                    for (var y = minTileY; y <= maxTileY; y++)
                    {
                        if (x < 0 ||
                            x > ushort.MaxValue ||
                            y < 0 ||
                            y > ushort.MaxValue)
                        {
                            continue;
                        }

                        if (new Vector2Ushort((ushort)x, (ushort)y)
                            .TileSqrDistanceTo(startTilePosition)
                            > damageDistanceMaxSqr)
                        {
                            // too far
                            continue;
                        }

                        var tileObjects = Api.Server.World.GetStaticObjects(new Vector2Ushort((ushort)x, (ushort)y));
                        if (tileObjects.Count == 0)
                        {
                            continue;
                        }

                        foreach (var tileObject in tileObjects)
                        {
                            if (!(tileObject.ProtoStaticWorldObject is IDamageableProtoWorldObject))
                            {
                                // non-damageable
                                continue;
                            }

                            if (tileObject.PhysicsBody.HasAnyShapeCollidingWithGroup(defaultCollisionGroup))
                            {
                                // has a collider colliding with the collision group so we ignore this
                                continue;
                            }

                            damageCandidates.Add(tileObject);
                        }
                    }
                }
            }

            // order by distance to explosion center
            var orderedDamageCandidates = damageCandidates.OrderBy(
                ServerExplosionGetDistanceToEpicenter(positionEpicenter, collisionGroup));

            var hitCharacters = new List <WeaponHitData>();

            // process all damage candidates
            foreach (var damagedObject in orderedDamageCandidates)
            {
                if (!isDamageThroughObstacles &&
                    ServerHasObstacleForExplosion(physicsSpace,
                                                  positionEpicenter,
                                                  damagedObject,
                                                  defaultCollisionGroup))
                {
                    continue;
                }

                var distanceToDamagedObject = ServerCalculateDistanceToDamagedObject(positionEpicenter,
                                                                                     damagedObject);
                var damagePreMultiplier =
                    damagedObject is IDynamicWorldObject
                        ? callbackCalculateDamageCoefByDistanceForDynamicObjects(distanceToDamagedObject)
                        : callbackCalculateDamageCoefByDistanceForStaticObjects(distanceToDamagedObject);

                damagePreMultiplier = MathHelper.Clamp(damagePreMultiplier, 0, 1);

                var damageableProto = (IDamageableProtoWorldObject)damagedObject.ProtoGameObject;
                damageableProto.SharedOnDamage(
                    weaponFinalCache,
                    damagedObject,
                    damagePreMultiplier,
                    damagePostMultiplier: 1.0,
                    out _,
                    out var damageApplied);

                if (damageApplied > 0 &&
                    damagedObject is ICharacter damagedCharacter)
                {
                    hitCharacters.Add(new WeaponHitData(damagedCharacter,
                                                        (0,
                                                         damagedCharacter
                                                         .ProtoCharacter.CharacterWorldWeaponOffsetRanged)));
                }

                if (damageApplied > 0)
                {
                    // give experience for damage
                    protoWeaponSkill?.ServerOnDamageApplied(playerCharacterSkills,
                                                            damagedObject,
                                                            damageApplied);
                }

                weaponFinalCache.ProtoExplosive?.ServerOnObjectHitByExplosion(damagedObject,
                                                                              damageApplied,
                                                                              weaponFinalCache);

                (weaponFinalCache.ProtoWeapon as ProtoItemMobWeaponNova)?
                .ServerOnObjectHitByNova(damagedObject,
                                         damageApplied,
                                         weaponFinalCache);
            }

            if (hitCharacters.Count == 0)
            {
                return;
            }

            // display damages on clients in scope of every damaged object
            var observers = new HashSet <ICharacter>();

            using var tempList = Api.Shared.GetTempList <ICharacter>();

            foreach (var hitObject in hitCharacters)
            {
                if (hitObject.WorldObject is ICharacter damagedCharacter &&
                    !damagedCharacter.IsNpc)
                {
                    // notify the damaged character
                    observers.Add(damagedCharacter);
                }

                Server.World.GetScopedByPlayers(hitObject.WorldObject, tempList);
                tempList.Clear();
                observers.AddRange(tempList.AsList());
            }

            // add all observers within the sound radius
            var eventNetworkRadius = (byte)Math.Max(
                20,
                Math.Ceiling(1.5 * damageDistanceMax));

            tempList.Clear();
            Server.World.GetCharactersInRadius(positionEpicenter.ToVector2Ushort(),
                                               tempList,
                                               radius: eventNetworkRadius,
                                               onlyPlayers: true);
            observers.AddRange(tempList.AsList());

            if (observers.Count > 0)
            {
                Instance.CallClient(observers,
                                    _ => _.ClientRemote_OnCharactersHitByExplosion(hitCharacters));
            }
        }
示例#19
0
        /// <summary>
        /// Sweeps a sphere against the physics world reporting all hits on the path that are not ignored by the filter, beware convex sweeps can be very costly especially if they are long
        /// </summary>
        /// <param name="radius">Radius of the sphere</param>
        /// <param name="startPos">Location to start the sweep from</param>
        /// <param name="sweep">Direction in which to sweep, the length of the vector is the length of the sweep</param>
        /// <param name="group">Collision group to filter the sweep hits, anything above "NoSolver" is ignored</param>
        /// <param name="outSweepResults">All overlaps on the sweep path</param>
        /// <returns>True if any object was hit</returns>
        public bool SphereMultiSweep(float radius, ref SharpDX.Vector3 startPos, ref SharpDX.Vector3 sweep, CollisionGroup group, List <SRaycastResult> outSweepResults)
        {
            SphereShape sphereShape = new SphereShape(radius);
            Quaternion  rotation    = Quaternion.Identity;

            return(MultiSweep(sphereShape, ref startPos, ref rotation, ref sweep, group, outSweepResults));
        }
示例#20
0
        public void LoadLevel(String fileName)
        {
            // Load level data
            GameLevelContent levelData = Game.Content.Load <GameLevelContent>(fileName);
            int blocksCount            = levelData.BlocksCount();

            // List of blocks chapes for compound body
            List <CompoundShapeEntry> shapes = new List <CompoundShapeEntry>();

            // Create block groups and block physics
            foreach (BlockGroupData blockGroup in levelData.BlockGroups)
            {
                // Create block group
                LoadBlockType(blockGroup.BlockTypeName, blocksCount);

                // Create physical representation of blocks in this group
                Vector3    scale;
                Quaternion rotation;
                Vector3    translation;
                foreach (BlockData blockData in blockGroup.Blocks)
                {
                    // Extract size, position and orientation values from block data transform
                    blockData.Transform.Decompose(out scale, out rotation, out translation);
                    blockData.Scale = scale;

                    // Create physical shape of the block to be part of compound body of blocks
                    BoxShape blockShape = new BoxShape(scale.X, scale.Y, scale.Z);

                    // Create compound shape entry for compund body of blocks
                    CompoundShapeEntry entry = new CompoundShapeEntry(blockShape, new RigidTransform(translation, rotation));
                    shapes.Add(entry);
                }
            }

            // Create compound body
            compoundBody = new CompoundBody(shapes, COMPOUND_BODY_MASS);

            compoundBody.PositionUpdateMode = BEPUphysics.PositionUpdating.PositionUpdateMode.Continuous;
            compoundBody.AngularDamping     = COMPOUND_BODY_ANGULAR_DAMPING;

            // Compound body has Position and LocalPosition (in Collision information)
            // Position property is position of mass center in global space - it is calculated automatically.
            // LocalPosition property is position of geometry of the body in its local space.
            // So in order to create compound body which is rotated around desired position ((0,0,0) for now)
            // We should switch Position and LocalPosition properties of our compound body.
            compoundBody.CollisionInformation.LocalPosition = compoundBody.Position;
            compoundBody.Position = Vector3.Zero;

            // Add constraint prevents compound body from moving
            constraint = new MaximumLinearSpeedConstraint(compoundBody, 0.0f);

            // Create collision group for removed blocks
            removedBlocksGroup = new CollisionGroup();

            // Create blocks
            int childCollidableIndex = 0;

            foreach (BlockGroupData blockGroup in levelData.BlockGroups)
            {
                Matrix localPosTransform = Matrix.CreateTranslation(compoundBody.CollisionInformation.LocalPosition);

                foreach (BlockData blockData in blockGroup.Blocks)
                {
                    // Obtain block type and instanced mesh for the block
                    BlockType blockType = blockTypes[blockGroup.BlockTypeName];
                    InstancedMesh <VertexData> instancedMesh = blockInstancedMeshes[blockGroup.BlockTypeName];

                    // Obtain physics body (a part of compound body) for the block
                    CompoundChild child = compoundBody.CollisionInformation.Children[childCollidableIndex];

                    // Create instance of the block in instanced mesh
                    InstancedMesh <VertexData> .Instance instance = instancedMesh.AppendInstance(Matrix.CreateScale(blockData.Scale) * child.Entry.LocalTransform.Matrix * localPosTransform);

                    // Store new block instance to the list
                    Block block = new Block(blockType, instance, child);
                    blocks.Add(block);

                    block.Scale = blockData.Scale;

                    childCollidableIndex++;
                }
            }

            // Add compound body and its constraints to physics space
            space.Add(compoundBody);
            space.Add(constraint);
        }
示例#21
0
        /// <summary>
        /// Sweeps a capsule against the physics world reporting all hits on the path that are not ignored by the filter, beware convex sweeps can be very costly especially if they are long
        /// </summary>
        /// <param name="length">Length of the capsule</param>
        /// <param name="radius">Radius of the capsule</param>
        /// <param name="startPos">Location to start the sweep from</param>
        /// <param name="rotation">Rotation of the shape during the sweep</param>
        /// <param name="sweep">Direction in which to sweep, the length of the vector is the length of the sweep</param>
        /// <param name="group">Collision group to filter the sweep hits, anything above "NoSolver" is ignored</param>
        /// <param name="outSweepResults">All overlaps on the sweep path</param>
        /// <returns>True if any object was hit</returns>
        public bool CapsuleMultiSweep(float length, float radius, ref SharpDX.Vector3 startPos, ref SharpDX.Quaternion rotation, ref SharpDX.Vector3 sweep, CollisionGroup group, List <SRaycastResult> outSweepResults)
        {
            CapsuleShape capsuleShape = new CapsuleShape(length, radius);

            return(MultiSweep(capsuleShape, ref startPos, ref rotation, ref sweep, group, outSweepResults));
        }
示例#22
0
        public static bool SharedHasObstaclesOnTheWay(
            Vector2D fromPosition,
            IPhysicsSpace physicsSpace,
            [CanBeNull] IWorldObject worldObject,
            Vector2D worldObjectCenter,
            Vector2D worldObjectPointClosestToCharacter,
            bool sendDebugEvents,
            CollisionGroup collisionGroup)
        {
            // let's test by casting rays from "fromPosition" (usually it's the character's center) to:
            // 0) world object center
            // 1) world object point closest to the character
            // 2) combined - take X from center, take Y from closest
            // 3) combined - take X from closest, take Y from center
            if (TestHasObstacle(worldObjectCenter) &&
                TestHasObstacle(worldObjectPointClosestToCharacter) &&
                TestHasObstacle((worldObjectCenter.X,
                                 worldObjectPointClosestToCharacter.Y)) &&
                TestHasObstacle((worldObjectPointClosestToCharacter.X, worldObjectCenter.Y)))
            {
                // has obstacle
                return(true);
            }

            return(false);

            // local method for testing if there is an obstacle from current to the specified position
            bool TestHasObstacle(Vector2D toPosition)
            {
                using var obstaclesOnTheWay = physicsSpace.TestLine(
                          fromPosition,
                          toPosition,
                          collisionGroup,
                          sendDebugEvent: sendDebugEvents);
                foreach (var test in obstaclesOnTheWay.AsList())
                {
                    var testPhysicsBody = test.PhysicsBody;
                    if (testPhysicsBody.AssociatedProtoTile is not null)
                    {
                        // obstacle tile on the way
                        return(true);
                    }

                    var testWorldObject = testPhysicsBody.AssociatedWorldObject;
                    if (ReferenceEquals(testWorldObject, worldObject))
                    {
                        // not an obstacle - it's the target object itself
                        // stop checking collisions as we've reached the target object
                        return(false);
                    }

                    if (testWorldObject is IDynamicWorldObject)
                    {
                        // dynamic world objects are not assumed as an obstacle
                        continue;
                    }

                    // no need for this check anymore as we're checking for general "is ICharacter" above
                    //if (ReferenceEquals(testWorldObject, character))
                    //{
                    //    // not an obstacle - it's the player's character itself
                    //    continue;
                    //}

                    if (!testWorldObject.ProtoWorldObject
                        .SharedIsAllowedObjectToInteractThrough(testWorldObject))
                    {
                        // obstacle object on the way
                        return(true);
                    }
                }

                // no obstacles
                return(false);
            }
        }
示例#23
0
 public void ApplyPhysicsData(BsonDocument doc)
 {
     if (doc.ContainsKey("ph_pos"))
     {
         SetPosition(Location.FromDoubleBytes(doc["ph_pos"].AsBinary, 0));
     }
     if (doc.ContainsKey("ph_vel"))
     {
         SetVelocity(Location.FromDoubleBytes(doc["ph_vel"].AsBinary, 0));
     }
     if (doc.ContainsKey("ph_avel"))
     {
         SetAngularVelocity(Location.FromDoubleBytes(doc["ph_avel"].AsBinary, 0));
     }
     if (doc.ContainsKey("ph_ang_x") && doc.ContainsKey("ph_ang_y") && doc.ContainsKey("ph_ang_z") && doc.ContainsKey("ph_ang_w"))
     {
         double ax = (double)doc["ph_ang_x"].AsDouble;
         double ay = (double)doc["ph_ang_y"].AsDouble;
         double az = (double)doc["ph_ang_z"].AsDouble;
         double aw = (double)doc["ph_ang_w"].AsDouble;
         SetOrientation(new BEPUutilities.Quaternion(ax, ay, az, aw));
     }
     if (doc.ContainsKey("ph_grav"))
     {
         SetGravity(Location.FromDoubleBytes(doc["ph_grav"].AsBinary, 0));
     }
     if (doc.ContainsKey("ph_bounce"))
     {
         SetBounciness((double)doc["ph_bounce"].AsDouble);
     }
     if (doc.ContainsKey("ph_frict"))
     {
         SetFriction((double)doc["ph_frict"].AsDouble);
     }
     if (doc.ContainsKey("ph_mass"))
     {
         SetMass((double)doc["ph_mass"].AsDouble);
     }
     if (doc.ContainsKey("ph_cg"))
     {
         int cg = doc["ph_cg"].AsInt32;
         if (cg == 0)
         {
             CGroup = CollisionUtil.NonSolid;
         }
         else if (cg == 1)
         {
             CGroup = CollisionUtil.Solid;
         }
         else if (cg == 2)
         {
             CGroup = CollisionUtil.Player;
         }
         else if (cg == 3)
         {
             CGroup = CollisionUtil.Item;
         }
         else // cg == 4
         {
             CGroup = CollisionUtil.Water;
         }
     }
     if (doc.ContainsKey("ph_flag"))
     {
         int flags = doc["ph_flag"].AsInt32;
         Visible        = (flags & 1) == 1;
         GenBlockShadow = (flags & 2) == 2;
         TransmitMe     = (flags & 128) == 128;
     }
 }
示例#24
0
文件: Actor.cs 项目: snoozbuster/ld28
 static Actor()
 {
     CollisionGroup.DefineCollisionRule(staticObjects, staticObjects, CollisionRule.NoBroadPhase);
     CollisionGroup.DefineCollisionRule(staticObjects, dynamicObjects, CollisionRule.Normal);
 }
示例#25
0
 public static IEntity?SpawnIfUnobstructed(
     this IEntityManager entityManager,
     string?prototypeName,
     EntityCoordinates coordinates,
     CollisionGroup collisionLayer,
     in Box2?box = null,
示例#26
0
 public RectangleCollider(Body body, CollisionGroup collisionGroup, Vector2 pos, Vector2 dimensions, Func <CollisionData, bool> OnCollision) : base(body, collisionGroup, OnCollision)
 {
     this.dimensions = dimensions;
     this.pos        = pos;
 }