Загрузка данных



-- Подключаем сервис игроков: через него получаем список всех игроков в игре
local Players = game:GetService("Players")

-- Ждём, пока в Workspace появится папка SpawnPoints (там лежат Part с точками спавна)
local spawnFolder = workspace:WaitForChild("SpawnPoints")

-- Число: минимальная дистанция между центром нового спавна и другими игроками (в студиях)
local minDistance = 8

--[[
  Локальная функция getSpawns:
  - нужна, чтобы один раз собрать все Part из папки в таблицу (массив точек)
  - возвращает таблицу parts — по ним потом выбираем случайную позицию
]]
local function getSpawns()
	local points = {} -- пустая таблица, сюда будем добавлять части

	-- ipairs обходит детей папки по порядку с 1,2,3...
	for _, obj in ipairs(spawnFolder:GetChildren()) do
		-- BasePart — это Part, SpawnLocation, MeshPart и т.д. (у всех есть Position)
		if obj:IsA("BasePart") then
			table.insert(points, obj) -- добавляем часть в конец таблицы
		end
	end

	return points -- отдаём список всех точек спавна
end

--[[
  Локальная функция isFarFromOthers(pos):
  - аргумент pos — Vector3, куда хотим поставить игрока
  - возвращает true, если от всех других игроков далеко, иначе false
]]
local function isFarFromOthers(pos)
	-- GetPlayers() — метод сервиса Players, даёт таблицу всех игроков сейчас в игре
	for _, plr in ipairs(Players:GetPlayers()) do
		-- У игрока может ещё не быть персонажа — проверяем Character
		if plr.Character and plr.Character:FindFirstChild("HumanoidRootPart") then
			local otherPos = plr.Character.HumanoidRootPart.Position -- позиция другого игрока

			-- (otherPos - pos) — разница векторов; .Magnitude — длина вектора = расстояние
			if (otherPos - pos).Magnitude < minDistance then
				return false -- слишком близко — эту точку не берём
			end
		end
	end

	return true -- ни один живой игрок не слишком близко
end

--[[
  Локальная функция spawnCharacter(character):
  - character — модель персонажа, который только что заспавнился
  - переносим HumanoidRootPart на выбранную позицию
]]
local function spawnCharacter(character)
	-- WaitForChild ждёт появления дочернего объекта (корень персонажа для перемещения)
	local root = character:WaitForChild("HumanoidRootPart")

	local points = getSpawns() -- получаем все Part-точки
	if #points == 0 then return end -- если точек нет — выходим, нечего делать

	-- Цикл: пробуем до стольки раз, сколько у нас точек (чтобы не крутиться бесконечно)
	for i = 1, #points do
		-- math.random(1, #points) — случайное целое от 1 до количества точек
		local point = points[math.random(1, #points)]

		-- Position — центр Part; + Vector3.new(0, 3, 0) чуть поднимаем, чтобы не застрять в полу
		local pos = point.Position + Vector3.new(0, 3, 0)

		if isFarFromOthers(pos) then
			-- CFrame.new(pos) — новая ориентация и позиция; ставим персонажа сюда
			root.CFrame = CFrame.new(pos)
			return -- нашли место — выходим из функции
		end
	end

	-- Если все точки "заняты" по правилу дистанции — всё равно ставим в случайную
	local fallback = points[math.random(1, #points)]
	root.CFrame = CFrame.new(fallback.Position + Vector3.new(0, 3, 0))
end

--[[
  Событие PlayerAdded:
  - срабатывает, когда новый игрок заходит в игру
  - внутри подписываемся на CharacterAdded этого игрока (каждый новый персонаж / респавн)
]]
Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function(character)
		task.wait(0.1) -- маленькая пауза: даём движку "успокоить" персонажа после спавна
		spawnCharacter(character) -- вызываем нашу функцию переноса
	end)
end)