// 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); }