コード例 #1
0
        private static bool AnyObstaclesBetween(ICharacter npc, ICharacter player)
        {
            var physicsSpace          = npc.PhysicsBody.PhysicsSpace;
            var npcCharacterCenter    = npc.Position + npc.PhysicsBody.CenterOffset;
            var playerCharacterCenter = player.Position + player.PhysicsBody.CenterOffset;

            using var obstaclesOnTheWay = physicsSpace.TestLine(
                      npcCharacterCenter,
                      playerCharacterCenter,
                      CollisionGroup.GetDefault(),
                      sendDebugEvent: false);

            foreach (var test in obstaclesOnTheWay.AsList())
            {
                var testPhysicsBody = test.PhysicsBody;
                if (testPhysicsBody.AssociatedProtoTile is null)
                {
                    continue;
                }

                var tile = ServerWorldService.GetTile(testPhysicsBody.Position.ToVector2Ushort());
                if (!tile.IsSlope)
                {
                    // cliff tile on the way
                    return(true);
                }
            }

            return(false);
        }
コード例 #2
0
        // calculate closest lava tile position and heat intensity from it
        private double ServerCalculateTileEnvironmentalIntensityAroundCharacter(ICharacter character)
        {
            if (!character.ServerIsOnline)
            {
                return(0);
            }

            var tileVolcanoSessionIndex = this.serverProtoTileVolcano.SessionIndex;
            var tileLavaSessionIndex    = this.serverProtoTileLava.SessionIndex;

            var characterCurrentTileSessionIndex = character.Tile.ProtoTileSessionIndex;

            if (characterCurrentTileSessionIndex != tileVolcanoSessionIndex &&
                characterCurrentTileSessionIndex != tileLavaSessionIndex)
            {
                // process lava heat only for players in volcano biome
                return(0);
            }

            var characterTilePosition = character.TilePosition;
            var closestDistanceSqr    = int.MaxValue;

            foreach (var tileOffset in this.serverTileOffsetsCircle)
            {
                var tilePosition = characterTilePosition.AddAndClamp(tileOffset);
                var tile         = ServerWorld.GetTile(tilePosition, logOutOfBounds: false);
                if (!tile.IsValidTile ||
                    tileLavaSessionIndex != tile.ProtoTileSessionIndex)
                {
                    continue;
                }

                var distanceSqr = tileOffset.X * tileOffset.X
                                  + tileOffset.Y * tileOffset.Y;
                if (distanceSqr < closestDistanceSqr)
                {
                    closestDistanceSqr = distanceSqr;
                }
            }

            if (closestDistanceSqr
                >= (EnvironmentalTileHeatLookupAreaDiameter
                    * EnvironmentalTileHeatLookupAreaDiameter
                    * 0.5
                    * 0.5))
            {
                // no heat - too far from any lava
                return(0);
            }

            var heatIntensity = (EnvironmentalTileHeatLookupAreaDiameter * 0.5 - Math.Sqrt(closestDistanceSqr))
                                / (EnvironmentalTileHeatLookupAreaDiameter * 0.5);

            heatIntensity *= 2;
            heatIntensity  = Math.Min(heatIntensity, 1);

            return(heatIntensity);
        }
コード例 #3
0
        private static Vector2D?FindClosestPosition(Vector2D position)
        {
            var physicsSpace   = ServerWorldService.GetPhysicsSpace();
            var collisionGroup = CollisionGroup.Default;

            if (IsValidPosition(
                    // round position
                    position.ToVector2Ushort().ToVector2D()))
            {
                // can teleport right there
                return(position);
            }

            // check nearby tiles
            var tile = ServerWorldService.GetTile(position.ToVector2Ushort());

            foreach (var neighborTile in tile.EightNeighborTiles)
            {
                if (!neighborTile.IsValidTile)
                {
                    continue;
                }

                var neighborTilePosition = neighborTile.Position.ToVector2D();
                if (IsValidPosition(neighborTilePosition))
                {
                    return(neighborTilePosition);
                }
            }

            return(null);

            // Local function for checking if the position is valid.
            bool IsValidPosition(Vector2D pos)
            {
                using var objectsNearby = physicsSpace.TestRectangle(
                          // include some padding, otherwise the check will include border-objects
                          position: pos + (0.01, 0.01),
                          size: (0.98, 0.98),
                          collisionGroup: collisionGroup,
                          sendDebugEvent: false);
                if (objectsNearby.Count > 0)
                {
                    return(false);
                }

                var posTile = Server.World.GetTile(pos.ToVector2Ushort());

                return(ServerCharacterSpawnHelper.IsValidSpawnTile(posTile,
                                                                   checkNeighborTiles: false));
            }
        }
コード例 #4
0
        protected virtual bool ServerCreateEventSearchArea(
            IWorldServerService world,
            Vector2Ushort eventPosition,
            ushort circleRadius,
            out Vector2Ushort circlePosition)
        {
            var biome = world.GetTile(eventPosition).ProtoTile;

            return(ServerSearchAreaHelper.GenerateSearchArea(eventPosition,
                                                             biome,
                                                             circleRadius,
                                                             out circlePosition,
                                                             maxAttempts: 100,
                                                             waterMaxRatio: 0.1));
        }
コード例 #5
0
        public static void DestroyAllDecals(
            Vector2Ushort tilePosition,
            StaticObjectLayoutReadOnly layout)
        {
            Api.ValidateIsServer();

            foreach (var tileOffset in layout.TileOffsets)
            {
                var tile = World.GetTile(tilePosition.X + tileOffset.X,
                                         tilePosition.Y + tileOffset.Y);
                if (!tile.IsValidTile)
                {
                    continue;
                }

                var staticObjects = tile.StaticObjects;
                if (!HasFloorDecals(staticObjects))
                {
                    continue;
                }

                // remove floor decals
                using (var tempList = Api.Shared.WrapInTempList(staticObjects))
                {
                    foreach (var staticWorldObject in tempList)
                    {
                        if (staticWorldObject.ProtoStaticWorldObject.Kind == StaticObjectKind.FloorDecal)
                        {
                            World.DestroyObject(staticWorldObject);
                        }
                    }
                }
            }

            bool HasFloorDecals(ReadOnlyListWrapper <IStaticWorldObject> staticObjects)
            {
                foreach (var staticWorldObject in staticObjects)
                {
                    if (staticWorldObject.ProtoStaticWorldObject.Kind == StaticObjectKind.FloorDecal)
                    {
                        return(true);
                    }
                }

                return(false);
            }
        }
コード例 #6
0
        private static Vector2D FindClosestPosition(Vector2D position)
        {
            var physicsSpace   = ServerWorldService.GetPhysicsSpace();
            var collisionGroup = CollisionGroup.GetDefault();

            if (IsValidPosition(
                    // round position
                    position.ToVector2Ushort().ToVector2D()))
            {
                // can teleport right there
                return(position);
            }

            // check nearby tiles
            var tile = ServerWorldService.GetTile(position.ToVector2Ushort());

            foreach (var neighborTile in tile.EightNeighborTiles)
            {
                if (!neighborTile.IsValidTile)
                {
                    continue;
                }

                var neighborTilePosition = neighborTile.Position.ToVector2D();
                if (IsValidPosition(neighborTilePosition))
                {
                    return(neighborTilePosition);
                }
            }

            throw new Exception("No empty position available nearby.");

            // Local function for checking if the position is valid.
            bool IsValidPosition(Vector2D pos)
            {
                using (var objectsNearby = physicsSpace.TestRectangle(
                           // include some padding, otherwise the check will include border-objects
                           position: pos + (0.01, 0.01),
                           size: (0.98, 0.98),
                           collisionGroup: collisionGroup,
                           sendDebugEvent: false))
                {
                    return(objectsNearby.Count == 0);
                }
            }
        }
コード例 #7
0
        public static CreateItemResult TryDropToGround(
            IReadOnlyDropItemsList dropItemsList,
            Vector2Ushort tilePosition,
            double probabilityMultiplier,
            DropItemContext context,
            [CanBeNull] out IItemsContainer groundContainer)
        {
            var character = context.HasCharacter ? context.Character : null;

            // obtain the ground container to drop the items into
            var tile = World.GetTile(tilePosition);

            groundContainer = ObjectGroundItemsContainer
                              .ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(character, tile);

            if (groundContainer is null)
            {
                // cannot drop because there are no free space available on the ground
                return(new CreateItemResult()
                {
                    IsEverythingCreated = false
                });
            }

            var result = dropItemsList.TryDropToContainer(groundContainer, context, probabilityMultiplier);

            if (groundContainer.OccupiedSlotsCount == 0)
            {
                // nothing is spawned, the ground container should be destroyed
                World.DestroyObject(groundContainer.OwnerAsStaticObject);
                groundContainer = null;
                return(result);
            }

            WorldObjectClaimSystem.ServerTryClaim(groundContainer.OwnerAsStaticObject,
                                                  character,
                                                  WorldObjectClaimDuration.GroundItems);

            return(result);
        }
コード例 #8
0
        public static bool IsValidSpawnPosition(Vector2D position)
        {
            var tile = ServerWorldService.GetTile(position.ToVector2Ushort());

            return(IsValidSpawnTileInternal(tile));
        }
コード例 #9
0
        /// <summary>
        /// Generates the search circle area position and radius for the provided position.
        /// The search area will contain at least about 33% of the provided biome tiles
        /// and will include the provided world position.
        /// </summary>
        /// <returns>False if cannot generate a search area.</returns>
        public static bool GenerateSearchArea(
            Vector2Ushort worldPosition,
            IProtoTile biome,
            ushort circleRadius,
            out Vector2Ushort circleCenter,
            int maxAttempts,
            double waterMaxRatio = 0.3)
        {
            var biomeSessionIndex = biome.SessionIndex;

            if (TryToCreateSearchArea(desiredBiomeMatchRatio: 0.75, out circleCenter) ||
                TryToCreateSearchArea(desiredBiomeMatchRatio: 0.5, out circleCenter) ||
                TryToCreateSearchArea(desiredBiomeMatchRatio: 0.33, out circleCenter))
            {
                return(true);
            }

            return(false);

            bool TryToCreateSearchArea(
                double desiredBiomeMatchRatio,
                out Vector2Ushort circleCenter)
            {
                for (var attempt = 0; attempt < maxAttempts; attempt++)
                {
                    var offset  = circleRadius * RandomHelper.NextDouble();
                    var angle   = RandomHelper.NextDouble() * MathConstants.DoublePI;
                    var resultD = new Vector2D(worldPosition.X + offset * Math.Cos(angle),
                                               worldPosition.Y + offset * Math.Sin(angle));

                    circleCenter = new Vector2Ushort((ushort)MathHelper.Clamp(resultD.X, 0, ushort.MaxValue),
                                                     (ushort)MathHelper.Clamp(resultD.Y, 0, ushort.MaxValue));

                    if (IsValidCircle(circleCenter))
                    {
                        return(true);
                    }

                    bool IsValidCircle(Vector2Ushort circleCenter)
                    {
                        uint totalChecks        = 0,
                             biomeMathes        = 0,
                             waterOrOutOfBounds = 0;

                        for (var x = -circleRadius; x < circleRadius; x += 10)
                        {
                            for (var y = -circleRadius; y < circleRadius; y += 10)
                            {
                                totalChecks++;
                                var tile = World.GetTile(circleCenter.X + x,
                                                         circleCenter.Y + y,
                                                         logOutOfBounds: false);
                                if (tile.IsOutOfBounds)
                                {
                                    waterOrOutOfBounds++;
                                    biomeMathes++; // yes, consider it a biome match
                                    continue;
                                }

                                if (tile.ProtoTileSessionIndex == biomeSessionIndex)
                                {
                                    biomeMathes++;
                                }
                                else if (tile.ProtoTile.Kind == TileKind.Water)
                                {
                                    waterOrOutOfBounds++;
                                    biomeMathes++; // yes, consider it a biome match
                                }
                            }
                        }

                        var biomeMatchRatio         = biomeMathes / (double)totalChecks;
                        var waterOrOutOfBoundsRatio = waterOrOutOfBounds / (double)totalChecks;

                        return(biomeMatchRatio >= desiredBiomeMatchRatio &&
                               waterOrOutOfBoundsRatio <= waterMaxRatio);
                    }
                }

                circleCenter = default;
                return(false);
            }
        }
コード例 #10
0
        private static Vector2Ushort?TryFindZoneSpawnPosition(
            ICharacter character,
            IServerZone spawnZone,
            Random random,
            bool isRespawn)
        {
            var characterDeathPosition = Vector2Ushort.Zero;

            if (isRespawn && character.ProtoCharacter is PlayerCharacter)
            {
                var privateState = PlayerCharacter.GetPrivateState(character);
                if (!privateState.LastDeathTime.HasValue)
                {
                    return(null);
                }

                characterDeathPosition = privateState.LastDeathPosition;
            }

            var restrictedZone = ZoneSpecialConstructionRestricted.Instance.ServerZoneInstance;

            for (var attempt = 0; attempt < SpawnInZoneAttempts; attempt++)
            {
                var randomPosition = spawnZone.GetRandomPosition(random);

                if (isRespawn)
                {
                    var sqrDistance = randomPosition.TileSqrDistanceTo(characterDeathPosition);
                    if (sqrDistance > MaxDistanceWhenRespawnSqr ||
                        sqrDistance < MinDistanceWhenRespawnSqr)
                    {
                        // too close or too far for the respawn
                        continue;
                    }
                }

                if (restrictedZone.IsContainsPosition(randomPosition))
                {
                    // the position is inside the restricted zone
                    continue;
                }

                if (worldService.GetTile(randomPosition).ProtoTile is IProtoTileCold)
                {
                    // cannot respawn in cold biome
                    continue;
                }

                if (!LandClaimSystem.SharedIsPositionInsideOwnedOrFreeArea(randomPosition,
                                                                           character,
                                                                           requireFactionPermission: false))
                {
                    // the land is claimed by another player
                    continue;
                }

                if (ServerCharacterSpawnHelper.IsPositionValidForCharacterSpawn(randomPosition.ToVector2D(),
                                                                                isPlayer: true))
                {
                    // valid position found
                    return(randomPosition);
                }
            }

            return(null);
        }
コード例 #11
0
        public static bool IsPositionValidForCharacterSpawn(
            Vector2D position,
            bool isPlayer)
        {
            var tile = ServerWorldService.GetTile(position.ToVector2Ushort());

            if (!IsValidTile(tile) ||
                tile.EightNeighborTiles.Any(t => !IsValidTile(t)))
            {
                // cliff/slope or water nearby
                return(false);
            }

            var physicsSpace = ServerWorldService.GetPhysicsSpace();

            using (var objectsNearby = physicsSpace.TestCircle(
                       position,
                       SpawnNoPlayerBuiltStructuresRadius,
                       CollisionGroups.Default,
                       sendDebugEvent: false))
            {
                if (objectsNearby.Any(
                        t => t.PhysicsBody.AssociatedWorldObject?.ProtoWorldObject is IProtoObjectStructure))
                {
                    // some structure nearby
                    return(false);
                }
            }

            if (isPlayer)
            {
                // check if mobs nearby
                using (var mobsNearby = physicsSpace.TestCircle(
                           position,
                           SpawnPlayerNoMobsRadius,
                           CollisionGroups.Default,
                           sendDebugEvent: false))
                {
                    if (mobsNearby.Any(
                            t => t.PhysicsBody.AssociatedWorldObject is ICharacter otherCharacter &&
                            otherCharacter.IsNpc))
                    {
                        // mobs nearby
                        return(false);
                    }
                }
            }
            else
            {
                // check if mobs nearby
                using (var mobsNearby = physicsSpace.TestCircle(
                           position,
                           SpawnMobNoPlayersRadius,
                           CollisionGroups.Default,
                           sendDebugEvent: false))
                {
                    if (mobsNearby.Any(
                            t => t.PhysicsBody.AssociatedWorldObject is ICharacter otherCharacter &&
                            !otherCharacter.IsNpc))
                    {
                        // players nearby
                        return(false);
                    }
                }
            }

            // check if any physics object nearby
            using (var objectsNearby = physicsSpace.TestCircle(
                       position,
                       SpawnNoPhysicsObjectsRadius,
                       CollisionGroups.Default,
                       sendDebugEvent: false))
            {
                if (objectsNearby.Count > 0)
                {
                    // some physical object nearby
                    return(false);
                }
            }

            return(true);
        }