public static ICharacter GetClosestPlayer(ICharacter characterNpc)
        {
            byte?tileHeight = null;

            try
            {
                var playersInView = TempListPlayersInView;
                ServerWorldService.GetCharactersInView(characterNpc,
                                                       playersInView,
                                                       onlyPlayerCharacters: true);
                if (playersInView.Count == 0)
                {
                    return(null);
                }

                ICharacter enemy = null;
                foreach (var player in playersInView)
                {
                    if (player.GetPublicState <ICharacterPublicState>().IsDead)
                    {
                        // do not pay attention to dead characters
                        continue;
                    }

                    if (player.ProtoCharacter.GetType() != typeof(PlayerCharacter))
                    {
                        // don't react on non-player prototype (spectator?)
                        continue;
                    }

                    var playerCharacterTile = player.Tile;
                    if (!tileHeight.HasValue)
                    {
                        var npcTile = characterNpc.Tile;
                        tileHeight = npcTile.Height;
                    }

                    if (playerCharacterTile.Height != tileHeight.Value)
                    {
                        // attack only on the same height characters
                        // unless there is a direct line of sight between the NPC and the target
                        if (AnyObstaclesBetween(characterNpc, player))
                        {
                            continue;
                        }
                    }

                    enemy = player;
                    break;
                }

                return(enemy);
            }
            finally
            {
                TempListPlayersInView.Clear();
            }
        }
        private void ServerUpdateMobDespawn(ICharacter characterMob, TPrivateState privateState, double deltaTime)
        {
            if (!this.IsAutoDespawn)
            {
                return;
            }

            var distanceToSpawnSqr = privateState.SpawnPosition
                                     .TileSqrDistanceTo(characterMob.TilePosition);

            if (distanceToSpawnSqr < DespawnTileDistanceThreshold * DespawnTileDistanceThreshold)
            {
                // close to spawn area, no need to despawn
                privateState.TimerDespawn = 0;
                return;
            }

            // too far from spawn area, should despawn after delay
            if (privateState.TimerDespawn < DespawnTimeThreshold)
            {
                // delay is not finished yet
                privateState.TimerDespawn += deltaTime;
                return;
            }

            // should despawn
            // check that nobody is observing the mob
            var playersInView = TempListPlayersInView;

            playersInView.Clear();
            ServerWorld.GetCharactersInView(characterMob,
                                            playersInView,
                                            onlyPlayerCharacters: true);

            foreach (var playerCharacter in playersInView)
            {
                if (playerCharacter.ServerIsOnline)
                {
                    // cannot despawn - scoped by a player
                    return;
                }
            }

            // nobody is observing, can despawn
            Logger.Important("Mob despawned as it went too far from the spawn position for too long: " + characterMob);
            ServerWorld.DestroyObject(characterMob);
        }