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