Hey everyone! So I am trying to make a custom spawn system, that sends players to clan location’s or exploration when players wondering around, and I have the rest of the code working, the only thing I cannot figure out is how to make it so that players can spawn on the spawn parts I have (Not spawn locations).
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerStorage = game:GetService("ServerStorage")
local Modules = ServerStorage.Modules
local Managers = Modules.Managers
local DataManager = require(Managers.DataManager)
local PlayerData = ReplicatedStorage.PlayerData
-- Configuration
local SPAWN_BOX_SIZE = Vector3.new(30, 20, 30) -- Box dimensions (width, height, depth)
local SPAWN_FLOOR_SIZE = Vector3.new(32, 2, 32) -- Floor platform size
-- Reference to spawn points folder (must exist in workspace)
local SPAWN_POINTS_FOLDER = workspace:WaitForChild("ClanSpawn", math.huge) -- Adjust folder name as needed
-- Backup spawn position if clan spawn not found
local DEFAULT_SPAWN = Vector3.new(0, 50, 0)
-- CFrame serialization functions
local function serializeCFrame(cf)
if cf and typeof(cf) == "CFrame" then
return {cf:GetComponents()}
end
return nil
end
local function deserializeCFrame(data)
if data and type(data) == "table" and #data == 12 then
return CFrame.new(unpack(data))
end
return nil
end
-- Helper function to check if a CFrame is the default/empty CFrame
local function isDefaultCFrame(cf)
if not cf or typeof(cf) ~= "CFrame" then
return true
end
-- Check if it's the default CFrame (0, 0, 0) with no rotation
local x, y, z = cf.Position.X, cf.Position.Y, cf.Position.Z
-- Only consider it default if ALL coordinates are 0, not just X and Z
return x == 0 and y == 0 and z == 0
end
-- Function to spawn player at custom box location
function spawnAtCustomBox(player)
local character = player.Character or player.CharacterAdded:Wait()
local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
-- Position player inside the box, slightly above the floor
local spawnFloor = workspace:WaitForChild("ClanSpawn", math.huge):WaitForChild("SpawnFloor")
local spawnPosition = spawnFloor.Position + Vector3.new(0, SPAWN_BOX_SIZE.Y/2 - 5, 0)
humanoidRootPart.CFrame = CFrame.new(spawnPosition)
print(player.Name .. " spawned at custom spawn box")
end
-- Function to get clan spawn position from parts in workspace
function getClanSpawnPosition(clanName)
-- Try to find the spawn point part by clan name
local spawnPart = SPAWN_POINTS_FOLDER:FindFirstChild(clanName)
if spawnPart and spawnPart:IsA("BasePart") then
-- Return position slightly above the part to prevent clipping
return spawnPart.Position + Vector3.new(0, 5, 0)
else
-- Fallback to default spawn if clan part not found
warn("Spawn point for clan '" .. clanName .. "' not found. Using default spawn.")
return DEFAULT_SPAWN
end
end
-- Function to spawn player at clan location
function spawnAtClanLocation(player)
local character = player.Character
if not character then return end
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
if not humanoidRootPart then return end
local PlayerFolder = PlayerData:WaitForChild(player.Name, math.huge)
local SelectedSlot = PlayerFolder:WaitForChild("SelectedSlot", math.huge)
local DataSlot = PlayerFolder:WaitForChild("Slot"..SelectedSlot.Value, math.huge)
local Clan = DataSlot:WaitForChild("Clan", math.huge)
local clan = "Lee" -- Default clan
if Clan and Clan.Value ~= "" then
clan = Clan.Value
end
-- Get spawn position for clan
local spawnPosition = getClanSpawnPosition(clan)
humanoidRootPart.CFrame = CFrame.new(spawnPosition)
print(player.Name .. " spawned at " .. clan .. " clan location")
end
-- Function to check if player has a valid saved position
function hasValidSavedPosition(player)
local PlayerFolder = PlayerData:WaitForChild(player.Name, math.huge)
local SelectedSlot = PlayerFolder:WaitForChild("SelectedSlot", math.huge)
local DataSlot = PlayerFolder:WaitForChild("Slot"..SelectedSlot.Value, math.huge)
local LastCF = DataSlot:WaitForChild("LastCF", math.huge)
-- Check if LastCF has a valid non-default value
if LastCF and LastCF.Value and not isDefaultCFrame(LastCF.Value) then
return true
end
-- Also check persistent data
local profile = DataManager:RequestFullProfile(player)
if profile and profile.Data and profile.Data.LastCF then
local savedCFrame = deserializeCFrame(profile.Data.LastCF)
if savedCFrame and not isDefaultCFrame(savedCFrame) then
return true
end
end
return false
end
-- Function to handle player spawning
function onPlayerSpawned(player)
local PlayerFolder = PlayerData:WaitForChild(player.Name, math.huge)
local SelectedSlot = PlayerFolder:WaitForChild("SelectedSlot", math.huge)
local DataSlot = PlayerFolder:WaitForChild("Slot"..SelectedSlot.Value, math.huge)
local CreatedCharacter = DataSlot:WaitForChild("CreatedCharacter", math.huge)
if CreatedCharacter and CreatedCharacter.Value == false then
-- First time spawn - send to custom spawn box
spawnAtCustomBox(player)
else
-- Check if player has saved position data
if hasValidSavedPosition(player) then
-- Player has a saved position - spawn there
spawnAtLastPosition(player)
else
-- Character already created but no saved position - send to clan spawn
spawnAtClanLocation(player)
end
end
end
-- Function to spawn player at their last saved position
function spawnAtLastPosition(player)
local PlayerFolder = PlayerData:WaitForChild(player.Name, math.huge)
local SelectedSlot = PlayerFolder:WaitForChild("SelectedSlot", math.huge)
local DataSlot = PlayerFolder:WaitForChild("Slot"..SelectedSlot.Value, math.huge)
local LastCF = DataSlot:WaitForChild("LastCF", math.huge)
local character = player.Character
if not character then return end
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
if not humanoidRootPart then return end
local spawnCFrame = nil
-- First try to get from persistent data
local profile = DataManager:RequestFullProfile(player)
if profile and profile.Data and profile.Data.LastCF then
local savedCFrame = deserializeCFrame(profile.Data.LastCF)
if savedCFrame and not isDefaultCFrame(savedCFrame) then
spawnCFrame = savedCFrame
end
end
-- Fall back to LastCF value if persistent data not available
if not spawnCFrame and LastCF and LastCF.Value and not isDefaultCFrame(LastCF.Value) then
spawnCFrame = LastCF.Value
end
if spawnCFrame then
humanoidRootPart.CFrame = spawnCFrame
print(player.Name .. " spawned at last saved position")
else
-- No valid saved position, fallback to clan spawn
print(player.Name .. " has no valid saved position, spawning at clan location")
spawnAtClanLocation(player)
end
end
-- Function to save player's current position
function savePlayerPosition(player)
local profile = DataManager:RequestFullProfile(player)
local PlayerFolder = PlayerData:WaitForChild(player.Name, math.huge)
if not PlayerFolder then return end
local SelectedSlot = PlayerFolder:WaitForChild("SelectedSlot", math.huge)
if not SelectedSlot then return end
local DataSlot = PlayerFolder:WaitForChild("Slot"..SelectedSlot.Value, math.huge)
if not DataSlot then return end
local LastCF = DataSlot:WaitForChild("LastCF", math.huge)
if not LastCF then return end
local character = player.Character
if not character then return end
local humanoidRootPart = character:FindFirstChild("HumanoidRootPart")
if not humanoidRootPart then return end
-- Store the CFrame directly in the replicated storage for immediate access
LastCF.Value = humanoidRootPart.CFrame
-- Serialize the CFrame for persistent storage
local serializedCFrame = serializeCFrame(humanoidRootPart.CFrame)
if serializedCFrame then
profile.Data["LastCF" .. SelectedSlot.Value] = serializedCFrame
print("Saved exploration position for " .. player.Name .. " at " .. tostring(humanoidRootPart.Position))
else
warn("Failed to serialize CFrame for " .. player.Name)
end
end
-- Main player connection
function onPlayerAdded(player)
local PlayerFolder = PlayerData:WaitForChild(player.Name, math.huge)
local SelectedSlot = PlayerFolder:WaitForChild("SelectedSlot", math.huge)
local DataSlot = PlayerFolder:WaitForChild("Slot"..SelectedSlot.Value, math.huge)
local CreatedCharacter = DataSlot:WaitForChild("CreatedCharacter", math.huge)
CreatedCharacter.Changed:Connect(function()
if CreatedCharacter.Value == true then
task.wait(0.25)
if player.Character then
spawnAtClanLocation(player)
end
end
end)
-- Handle initial spawn
player.CharacterAdded:Connect(function()
task.wait(1) -- Small delay to ensure character is fully loaded
onPlayerSpawned(player)
end)
end
-- Connect events
Players.PlayerAdded:Connect(onPlayerAdded)
-- Handle players already in game
for _, player in pairs(Players:GetPlayers()) do
onPlayerAdded(player)
end
-- Auto-save position every 10 seconds for all players
task.spawn(function()
while true do
task.wait(10) -- Reduced from 30 to 10 seconds for more frequent saves
for _, player in pairs(Players:GetPlayers()) do
if player.Character then
savePlayerPosition(player)
end
end
end
end)
-- Save player position when they leave the game
Players.PlayerRemoving:Connect(function(player)
if player.Character then
savePlayerPosition(player)
print("Saved " .. player.Name .. "'s position on disconnect")
end
end)
-- Save positions when server is shutting down
game:BindToClose(function()
for _, player in pairs(Players:GetPlayers()) do
if player.Character then
savePlayerPosition(player)
end
end
task.wait(10) -- Give time for data to save
end)
If anyone can help fix this issue let me know because its required for my game.