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); }
// 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); }
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)); } }
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)); }
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); } }
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); } } }
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); }
public static bool IsValidSpawnPosition(Vector2D position) { var tile = ServerWorldService.GetTile(position.ToVector2Ushort()); return(IsValidSpawnTileInternal(tile)); }
/// <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); } }
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); }
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); }