Exemple #1
0
        private static bool IsAreaLocalDensityExceeded(
            SpawnRequest spawnRequest,
            ObjectSpawnPreset preset,
            SpawnZoneArea area,
            out double countToSpawnRemains)
        {
            var spawnedObjectsCount = area.WorldObjectsByPreset.Find(preset)?.Count ?? 0;
            var desiredObjectsCount = (int)Math.Round(
                area.ZoneTilesCount * spawnRequest.Density,
                MidpointRounding.AwayFromZero);

            countToSpawnRemains = desiredObjectsCount - spawnedObjectsCount;
            return(countToSpawnRemains <= 0);
        }
Exemple #2
0
        private static bool ServerIsAnyPlayerNearby(SpawnZoneArea area, List <Vector2Ushort> playersPositions)
        {
            // check 5 locations of the area - 4 corners and center
            var       start       = area.StartPosition;
            const int size        = SpawnZoneAreaSize;
            var       topLeft     = start;
            var       topRight    = new Vector2Ushort((ushort)(start.X + size), start.Y);
            var       bottomLeft  = new Vector2Ushort(start.X, (ushort)(start.Y + size));
            var       bottomRight = new Vector2Ushort((ushort)(start.X + size), (ushort)(start.Y + size));
            var       center      = new Vector2Ushort((ushort)(start.X + size / 2), (ushort)(start.Y + size / 2));

            return(ServerIsAnyPlayerNearby(center, playersPositions) ||
                   ServerIsAnyPlayerNearby(topLeft, playersPositions) ||
                   ServerIsAnyPlayerNearby(topRight, playersPositions) ||
                   ServerIsAnyPlayerNearby(bottomLeft, playersPositions) ||
                   ServerIsAnyPlayerNearby(bottomRight, playersPositions));
        }
        private static bool IsAreaLocalDensityExceeded(
            SpawnRequest spawnRequest,
            ObjectSpawnPreset preset,
            SpawnZoneArea area,
            out double countToSpawnRemains)
        {
            var spawnedObjectsCount      = area.WorldObjectsByPreset.Find(preset)?.Count ?? 0;
            var desiredObjectsCountFloat = area.ZoneTilesCount * spawnRequest.Density;
            var desiredObjectsCount      = (int)Math.Round(desiredObjectsCountFloat, MidpointRounding.AwayFromZero);

            if (desiredObjectsCount == 0 &&
                preset.SpawnAtLeastOnePerSector &&
                preset.PresetUseSectorDensity &&
                desiredObjectsCountFloat > 0)
            {
                // spawn at least a single object
                desiredObjectsCount = 1;
            }

            countToSpawnRemains = desiredObjectsCount - spawnedObjectsCount;
            return(countToSpawnRemains <= 0);
        }
        /// <summary>
        /// Get all (already populated) areas with their objects inside the zone
        /// </summary>
        private async Task ServerFillSpawnAreasInZoneAsync(
            IServerZone zone,
            IReadOnlyDictionary <Vector2Ushort, SpawnZoneArea> areas,
            Func <Task> callbackYieldIfOutOfTime)
        {
            // this is a heavy method so we will try to yield every 100 objects to reduce the load
            const int defaultCounterToYieldValue = 100;
            var       counterToYield             = defaultCounterToYieldValue;

            // this check has a problem - it returns only objects strictly inside the zone,
            // but we also need to consider objects nearby the zone for restriction presets
            //await zone.PopulateStaticObjectsInZone(tempList, callbackYieldIfOutOfTime);

            TempListAllStaticWorldObjects.Clear();
            await Api.Server.World.GetStaticWorldObjectsAsync(TempListAllStaticWorldObjects);

            foreach (var staticObject in TempListAllStaticWorldObjects)
            {
                await YieldIfOutOfTime();

                if (staticObject.IsDestroyed)
                {
                    continue;
                }

                var position = staticObject.TilePosition;
                var area     = GetArea(position, isMobTrackingEnumeration: false);
                if (area is null)
                {
                    continue;
                }

                var protoStaticWorldObject = staticObject.ProtoStaticWorldObject;
                if (!(protoStaticWorldObject is ObjectGroundItemsContainer))
                {
                    if (protoStaticWorldObject.IsIgnoredBySpawnScripts)
                    {
                        // we don't consider padding to certain objects such as ground decals
                        // (though they still might affect spawn during the tiles check)
                        continue;
                    }

                    // create entry for regular static object
                    var preset = this.FindPreset(protoStaticWorldObject);
                    if (preset != null &&
                        preset.Density > 0 &&
                        !zone.IsContainsPosition(staticObject.TilePosition))
                    {
                        // this object is a part of the preset spawn list but it's not present in the zone
                        // don't consider this object
                        // TODO: this might cause a problem if there is a padding to this object check
                        continue;
                    }

                    area.Add(preset, position);
                    continue;
                }

                // ground container object
                var itemsContainer = ObjectGroundItemsContainer.GetPublicState(staticObject).ItemsContainer;
                foreach (var item in itemsContainer.Items)
                {
                    // create entry for each item in the ground container
                    var preset = this.FindPreset(item.ProtoItem);
                    if (preset != null &&
                        preset.Density > 0 &&
                        !zone.IsContainsPosition(staticObject.TilePosition))
                    {
                        // this object is a part of the preset spawn list but it's not present in the zone
                        // don't consider this object
                        continue;
                    }

                    area.Add(preset, position);
                }
            }

            var mobsTrackingManager = SpawnedMobsTrackingManagersStore.Get(this, zone);

            foreach (var mob in mobsTrackingManager.EnumerateAll())
            {
                await YieldIfOutOfTime();

                var position = mob.TilePosition;
                var area     = GetArea(position, isMobTrackingEnumeration: true);
                area?.Add(this.FindPreset(mob.ProtoCharacter), position);
            }

            return;

            SpawnZoneArea GetArea(Vector2Ushort tilePosition, bool isMobTrackingEnumeration)
            {
                var zoneChunkStartPosition = SpawnZoneArea.CalculateStartPosition(tilePosition);

                if (areas.TryGetValue(zoneChunkStartPosition, out var area))
                {
                    return(area);
                }

                if (isMobTrackingEnumeration)
                {
                    return(null);
                }

                return(null);
//                throw new Exception("No zone area found for " + tilePosition);

                //var newArea = new SpawnZoneArea(zoneChunkStartPosition, zoneChunk);
                //areas.Add(newArea);
                //return newArea;
            }

            Task YieldIfOutOfTime()
            {
                if (--counterToYield > 0)
                {
                    return(Task.CompletedTask);
                }

                counterToYield = defaultCounterToYieldValue;
                return(callbackYieldIfOutOfTime());
            }
        }
        private static bool ServerIsCanSpawn(
            SpawnRequest spawnRequest,
            ObjectSpawnPreset preset,
            IReadOnlyDictionary <Vector2Ushort, SpawnZoneArea> spawnZoneAreas,
            Vector2Ushort spawnPosition,
            IPhysicsSpace physicsSpace,
            out SpawnZoneArea resultSpawnArea,
            out bool isSectorDensityExceeded)
        {
            if (ServerWorldService.GetTile(spawnPosition)
                .IsCliffOrSlope)
            {
                // quick discard - don't spawn on cliff or slope
                resultSpawnArea         = null;
                isSectorDensityExceeded = false;
                return(false);
            }

            var presetPadding             = preset.Padding;
            var presetCustomObjectPadding = preset.CustomObjectPadding;

            resultSpawnArea = null;
            var resultSpawnAreaStartPosition = SpawnZoneArea.CalculateStartPosition(spawnPosition);

            foreach (var area in EnumerateAdjacentZoneAreas(resultSpawnAreaStartPosition, spawnZoneAreas))
            {
                foreach (var nearbyPreset in area.WorldObjectsByPreset)
                {
                    var    nearbyObjectPreset = nearbyPreset.Key;
                    double padding;

                    if (nearbyObjectPreset != null)
                    {
                        // preset found
                        if (presetCustomObjectPadding.TryGetValue(nearbyObjectPreset, out padding))
                        {
                            // use custom padding value
                        }
                        else
                        {
                            // don't have custom padding
                            padding = Math.Max(presetPadding, nearbyObjectPreset.Padding);
                        }
                    }
                    else
                    {
                        // preset of another object not defined in this spawn list - use default object spawn padding
                        padding = DefaultObjectSpawnPadding;
                    }

                    foreach (var nearbyObjectTilePosition in nearbyPreset.Value)
                    {
                        var distance = spawnPosition.TileSqrDistanceTo(nearbyObjectTilePosition);

                        // Actually using < will be more correct, but it will produce not so nice-looking result
                        // (objects could touch each other on each side).
                        // So we insist objects must don't even touch each other on their
                        // left/up/right/down edges (but the diagonal corners touch is ok).
                        if (distance <= padding * padding)
                        {
                            // too close
                            isSectorDensityExceeded = false;
                            return(false);
                        }
                    }
                }

                if (resultSpawnAreaStartPosition == area.StartPosition)
                {
                    resultSpawnArea = area;
                }
            }

            var needToCheckLandClaimPresence = true;

            if (preset.IsContainsOnlyStaticObjects)
            {
                needToCheckLandClaimPresence = !ServerWorldService.GetTile(spawnPosition)
                                               .ProtoTile
                                               .IsRestrictingConstruction;
            }

            if (needToCheckLandClaimPresence &&
                ServerCheckLandClaimAreaPresence(spawnPosition, preset.PaddingToLandClaimAreas))
            {
                // the land is claimed by players
                resultSpawnArea         = null;
                isSectorDensityExceeded = false;
                return(false);
            }

            if (preset.CustomCanSpawnCheckCallback != null &&
                !preset.CustomCanSpawnCheckCallback(physicsSpace, spawnPosition))
            {
                // custom spawn check failed
                resultSpawnArea         = null;
                isSectorDensityExceeded = false;
                return(false);
            }

            if (resultSpawnArea == null)
            {
                // no area exist (will be created)
                isSectorDensityExceeded = false;
                return(true);
            }

            if (!spawnRequest.UseSectorDensity)
            {
                isSectorDensityExceeded = false;
                return(true);
            }

            // ensure that the area/sector density is not exceeded for this spawn preset
            isSectorDensityExceeded = IsAreaLocalDensityExceeded(spawnRequest,
                                                                 preset,
                                                                 resultSpawnArea,
                                                                 out var countToSpawnRemains);
            if (isSectorDensityExceeded)
            {
                // already spawned too many objects of the required type in the area
                return(false);
            }

            if (countToSpawnRemains < 1)
            {
                // density allows to spawn an extra object of this type with some small probability
                if (!RandomHelper.RollWithProbability(countToSpawnRemains))
                {
                    return(false);
                }
            }

            return(true);
        }
Exemple #6
0
        /// <summary>
        /// Get all (already populated) areas with their objects inside the zone
        /// </summary>
        private async Task ServerFillSpawnAreasInZoneAsync(
            IServerZone zone,
            IReadOnlyDictionary <Vector2Ushort, SpawnZoneArea> areas,
            Func <Task> callbackYieldIfOutOfTime)
        {
            // this is a heavy method so we will try to yield every 100 objects to reduce the load
            const int defaultCounterToYieldValue = 100;
            var       counterToYield             = defaultCounterToYieldValue;

            using (var tempList = Api.Shared.GetTempList <IStaticWorldObject>())
            {
                await zone.PopulateStaticObjectsInZone(tempList, callbackYieldIfOutOfTime);

                foreach (var staticObject in tempList)
                {
                    await YieldIfOutOfTime();

                    if (staticObject.IsDestroyed)
                    {
                        continue;
                    }

                    var position = staticObject.TilePosition;
                    var area     = GetArea(position, isMobTrackingEnumeration: false);
                    var protoStaticWorldObject = staticObject.ProtoStaticWorldObject;
                    if (!(protoStaticWorldObject is ObjectGroundItemsContainer))
                    {
                        if (protoStaticWorldObject.Kind == StaticObjectKind.FloorDecal)
                        {
                            // we don't consider padding to decal objects
                            // (though they still might affect spawn during tile check)
                            continue;
                        }

                        // create entry for regular static object
                        area.Add(this.FindPreset(protoStaticWorldObject), position);
                        continue;
                    }

                    // ground container object
                    var itemsContainer = ObjectGroundItemsContainer.GetPublicState(staticObject).ItemsContainer;
                    foreach (var item in itemsContainer.Items)
                    {
                        // create entry for each item in the ground container
                        area.Add(this.FindPreset(item.ProtoItem), position);
                    }
                }
            }

            var mobsTrackingManager = SpawnedMobsTrackingManagersStore.Get(this, zone);

            foreach (var mob in mobsTrackingManager.EnumerateAll())
            {
                await YieldIfOutOfTime();

                var position = mob.TilePosition;
                var area     = GetArea(position, isMobTrackingEnumeration: true);
                area?.Add(this.FindPreset(mob.ProtoCharacter), position);
            }

            return;

            SpawnZoneArea GetArea(Vector2Ushort tilePosition, bool isMobTrackingEnumeration)
            {
                var zoneChunkStartPosition = SpawnZoneArea.CalculateStartPosition(tilePosition);

                if (areas.TryGetValue(zoneChunkStartPosition, out var area))
                {
                    return(area);
                }

                if (isMobTrackingEnumeration)
                {
                    return(null);
                }

                throw new Exception("No zone area found for " + tilePosition);

                //var newArea = new SpawnZoneArea(zoneChunkStartPosition, zoneChunk);
                //areas.Add(newArea);
                //return newArea;
            }

            Task YieldIfOutOfTime()
            {
                if (--counterToYield > 0)
                {
                    return(Task.CompletedTask);
                }

                counterToYield = defaultCounterToYieldValue;
                return(callbackYieldIfOutOfTime());
            }
        }