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)); } }
private static bool ServerCheckCanSpawn(IProtoWorldObject protoObjectToSpawn, Vector2Ushort spawnPosition) { switch (protoObjectToSpawn) { case IProtoCharacterMob _: return(ServerCharacterSpawnHelper.IsPositionValidForCharacterSpawn(spawnPosition.ToVector2D(), isPlayer: false)); case IProtoStaticWorldObject protoStaticWorldObject: return(protoStaticWorldObject.CheckTileRequirements(spawnPosition, character: null, logErrors: false)); default: throw new Exception("Unknown object type to spawn: " + protoObjectToSpawn); } }
protected virtual bool ServerIsValidSpawnPosition(Vector2D worldPosition) { const double noObstaclesCheckRadius = 1.5; var physicsSpace = Server.World.GetPhysicsSpace(); foreach (var _ in physicsSpace.TestCircle(worldPosition, radius: noObstaclesCheckRadius, CollisionGroups.Default, sendDebugEvent: false).EnumerateAndDispose()) { // position is not valid for spawning return(false); } return(ServerCharacterSpawnHelper.IsValidSpawnTile( Api.Server.World.GetTile(worldPosition.ToVector2Ushort()), checkNeighborTiles: true)); }
/// <summary> /// Server spawn callback for mob. /// </summary> /// <param name="trigger">Trigger leading to this spawn.</param> /// <param name="zone">Server zone instance.</param> /// <param name="protoMob">Prototype of character mob object to spawn.</param> /// <param name="tilePosition">Position to try spawn at.</param> protected virtual IGameObjectWithProto ServerSpawnMob( IProtoTrigger trigger, IServerZone zone, IProtoCharacterMob protoMob, Vector2Ushort tilePosition) { var worldPosition = tilePosition.ToVector2D(); if (!ServerCharacterSpawnHelper.IsPositionValidForCharacterSpawn(worldPosition, isPlayer: false)) { // position is not valid for spawning return(null); } return(Server.Characters.SpawnCharacter( protoMob, worldPosition)); }
private static bool ServerCheckCanSpawn(IProtoWorldObject protoObjectToSpawn, Vector2Ushort spawnPosition) { return(protoObjectToSpawn switch { IProtoCharacterMob => ServerCharacterSpawnHelper.IsPositionValidForCharacterSpawn( spawnPosition.ToVector2D(), isPlayer: false) && !LandClaimSystem.SharedIsLandClaimedByAnyone(spawnPosition), IProtoStaticWorldObject protoStaticWorldObject // Please note: land claim check must be integrated in the object tile requirements => protoStaticWorldObject.CheckTileRequirements( spawnPosition, character: null, logErrors: false), _ => throw new ArgumentOutOfRangeException("Unknown object type to spawn: " + protoObjectToSpawn) });
public static void ServerTrySpawnMobsCustom( IProtoCharacter protoMob, ICollection <ICharacter> spawnedCollection, int countToSpawn, RectangleInt excludeBounds, int maxSpawnDistanceFromExcludeBounds, double noObstaclesCheckRadius, int maxAttempts) { if (countToSpawn <= 0) { return; } var spawnBounds = excludeBounds.Inflate(maxSpawnDistanceFromExcludeBounds, maxSpawnDistanceFromExcludeBounds); var physicsSpace = Api.Server.World.GetPhysicsSpace(); while (maxAttempts-- > 0) { var position = new Vector2D(spawnBounds.Left + RandomHelper.NextDouble() * spawnBounds.Width, spawnBounds.Bottom + RandomHelper.NextDouble() * spawnBounds.Height); if (IsTooClose(position)) { continue; } var character = ServerTrySpawnMob(position); if (character is null) { // cannot spawn there continue; } spawnedCollection.Add(character); countToSpawn--; if (countToSpawn == 0) { return; } } bool IsTooClose(in Vector2D position) => position.X >= excludeBounds.X && position.Y >= excludeBounds.Y && position.X < excludeBounds.X + excludeBounds.Width && position.Y < excludeBounds.Y + excludeBounds.Height; ICharacter ServerTrySpawnMob(Vector2D worldPosition) { foreach (var _ in physicsSpace.TestCircle(worldPosition, radius: noObstaclesCheckRadius, CollisionGroups.Default, sendDebugEvent: false).EnumerateAndDispose()) { // position is not valid for spawning return(null); } if (!ServerCharacterSpawnHelper.IsValidSpawnTile( Api.Server.World.GetTile(worldPosition.ToVector2Ushort()), checkNeighborTiles: true)) { return(null); } return(Api.Server.Characters.SpawnCharacter(protoMob, worldPosition)); } }
public static void ServerSpawnBossMinionsOnDeath( Vector2Ushort epicenterPosition, double bossDifficultyCoef, IProtoCharacter minionProto, int minionsDefaultCount, double minionsRadius) { var countToSpawnRemains = minionsDefaultCount; // apply difficulty coefficient countToSpawnRemains = (int)Math.Ceiling(countToSpawnRemains * bossDifficultyCoef); var attemptsRemains = 3000; while (countToSpawnRemains > 0) { attemptsRemains--; if (attemptsRemains <= 0) { // attempts exceeded return; } // calculate random distance from the explosion epicenter var distance = RandomHelper.Range(2, minionsRadius); // ensure we spawn more objects closer to the epicenter var spawnProbability = 1 - (distance / minionsRadius); spawnProbability = Math.Pow(spawnProbability, 1.25); if (!RandomHelper.RollWithProbability(spawnProbability)) { // random skip continue; } var angle = RandomHelper.NextDouble() * MathConstants.DoublePI; var spawnPosition = new Vector2Ushort( (ushort)(epicenterPosition.X + distance * Math.Cos(angle)), (ushort)(epicenterPosition.Y + distance * Math.Sin(angle))); if (ServerTrySpawnMinion(spawnPosition)) { // spawned successfully! countToSpawnRemains--; } } bool ServerTrySpawnMinion(Vector2Ushort spawnPosition) { var worldPosition = spawnPosition.ToVector2D(); if (!ServerCharacterSpawnHelper.IsPositionValidForCharacterSpawn(worldPosition, isPlayer: false)) { // position is not valid for spawning return(false); } var spawnedCharacter = ServerCharacters.SpawnCharacter(minionProto, worldPosition); return(spawnedCharacter is not null); } }