using Yuanshen.Protocol; namespace Yuanshen.Gameserver.Game; internal sealed class WorldSpawner { private const float SpawnRadiusSq = 120f * 120f; private const float DespawnRadiusSq = 150f * 150f; private const float StreamStepSq = 20f * 20f; private const int ConcurrentCap = 25; private const int WildlifeCap = 6; private readonly Player _player; private readonly SceneManager _scene; private readonly MonsterSpawnTable _table; private Vector? _lastStream; public WorldSpawner(Player player, SceneManager scene, MonsterSpawnTable table) { _player = player; _scene = scene; _table = table; } public async ValueTask EnterSceneAsync(Vector pos) { _player.Monsters.Clear(); _lastStream = null; await StreamAsync(pos, force: true); } public async ValueTask StreamAsync(Vector pos) { await StreamAsync(pos, force: false); } public async ValueTask RespawnAsync(Vector pos) { _player.KilledNodeIds.Clear(); await EnterSceneAsync(pos); } private async ValueTask StreamAsync(Vector pos, bool force) { if (!force && _lastStream is { } last && HorizontalSq(pos, last) < StreamStepSq) return; _lastStream = pos; HashSet active = new(); foreach (GameMonster monster in _player.Monsters) if (monster.NodeId != 0) active.Add(monster.NodeId); foreach (GameMonster monster in _player.Monsters .Where(monster => monster.NodeId != 0 && HorizontalSq(pos, monster.Pos) > DespawnRadiusSq) .ToList()) { active.Remove(monster.NodeId); await _scene.DespawnMonsterAsync(monster); } int wildlife = _player.Monsters.Count(monster => monster.IsWildlife); foreach (SpawnNode node in _table.Nodes.OrderBy(node => node.IsWildlife)) { if (active.Count >= ConcurrentCap) break; if (active.Contains(node.NodeId) || _player.KilledNodeIds.Contains(node.NodeId)) continue; if (HorizontalSq(pos, node.Pos) > SpawnRadiusSq) continue; if (node.IsWildlife && wildlife >= WildlifeCap) continue; if (node.IsWildlife) wildlife++; GameMonster monster = _player.SpawnMonster(node.MonsterId, node.Pos, node.Level, node.Hp, node.Attack, node.Defense, node.NodeId, node.ConfigId, node.GroupId); active.Add(node.NodeId); await _scene.AppearMonsterAsync(monster); } } private static float HorizontalSq(Vector a, Vector b) { float dx = a.X - b.X; float dz = a.Z - b.Z; return dx * dx + dz * dz; } }