Esempio n. 1
0
        // Noise selector is experimental feature to make spawn distribution always the same.
        // For example, if one area was cleaned of objects, the random selector will help us to ensure that objects
        // will be respawned in this area and not in other areas.
        private NoiseSelector CreateTileRandomSelector(double density, ObjectSpawnPreset preset, int desiredCount)
        {
            var seed = (this.Id.GetHashCode() * 397) ^ preset.GetHashCode();

            // we need to use a little bit higher density in our noise selector,
            // otherwise it will be always impossible to spawn the full amount of objects
            density *= 1.065;

            var minDensity = 0.0;

            if (desiredCount < 10)
            {
                // ensure that min density is high enough to allow spawning of these objects
                minDensity = 0.1;
            }

            // clamp density
            density = MathHelper.Clamp(density, minDensity, max: 1);

            return(new NoiseSelector(
                       from: 0,
                       to: density,
                       noise: new WhiteNoise(seed: seed)));
        }
        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);
        }
 /// <summary>
 /// Set custom padding (minimal distance between objects) with another preset. Applied in both directions, so no need to do
 /// the same for another preset.
 /// </summary>
 /// <param name="preset">Another spawn preset.</param>
 /// <param name="padding">Defines the minimal distance to the objects of this and another spawn presets.</param>
 /// <returns>This instance (chainable).</returns>
 public ObjectSpawnPreset SetCustomPaddingWith(ObjectSpawnPreset preset, double padding)
 {
     this.InternalSetCustomMinSpawnDistance(preset, padding, recursive: true);
     return(this);
 }