Door Path System Help

Helo Everyone.
Right now i am coding on a Simulator Door system to unlock Doors but with a Path system so you cannot unlock a Door from another Path when you haven’t unlocked all the doors of your current Path, for example:

I open a Door from the Path “Nature” and there are currently 3 doors with the Path-attribute “Nature” but i can still open a door from the Path “Ice” even tho i havent opend every door from nature. I already asked Ai but it was not helpful. Can someone Help me with this Problem, down below is the LocalScript is use.

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")

local player = Players.LocalPlayer

if not player then
	return
end

local doorsFolder = Workspace:WaitForChild("Doors")
local doorEvents = ReplicatedStorage:WaitForChild("DoorEvents")

-- Store which paths the player has unlocked
local unlockedPaths = {}

-- Format large numbers
local function formatNumber(n)
	local suffixes = { "", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No" }
	local i = 1
	while n >= 1000 and i < #suffixes do
		n = n / 1000
		i += 1
	end

	local formatted = string.format("%.2f", n)
	formatted = formatted:gsub("%.0+$", ""):gsub("(%.%d-)0+$", "%1")

	return formatted .. suffixes[i]
end

-- Smoothly hide a door with animation
local TweenService = game:GetService("TweenService")

-- Fade out part to simulate fog and play sound for the player
local function fadePartToFog(part, player)
	if part:IsA("BasePart") then
		-- Tween to make it fade like fog (increasing transparency)
		local tweenInfo = TweenInfo.new(2, Enum.EasingStyle.Linear, Enum.EasingDirection.Out)
		local goal = { Transparency = 1 }
		local tween = TweenService:Create(part, tweenInfo, goal)
		tween:Play()

		-- Find the UnlockSound part and play the sound for the player
		local soundPart = part:FindFirstChild("UnlockSound")
		if soundPart and soundPart:IsA("Sound") then
			-- Clone the sound to avoid multiple players hearing it at once
			local soundClone = soundPart:Clone()
			soundClone.Parent = part
			soundClone:Play()
			-- Optionally clean up the sound after it finishes playing
			game:GetService("Debris"):AddItem(soundClone, soundClone.TimeLength)
		end

		-- Optional: Add fog-like particle effect
		local particle = Instance.new("ParticleEmitter")
		particle.Texture = "rbxassetid://243098098" -- Example fog texture
		particle.Rate = 50
		particle.Lifetime = NumberRange.new(2)
		particle.Size = NumberSequence.new(0.5, 1)
		particle.Parent = part

		-- Clean up the particle effect after it fades
		game:GetService("Debris"):AddItem(particle, 2)
	end
end

-- Fade out the door and parts in the Fog Folder, and play sound for the unlocking player
local function fadeOutDoor(door, player)
	if not door or not door:IsDescendantOf(game) then return end

	-- Safely remove children
	local infoUI = door:FindFirstChild("DoorInfomations")
	if infoUI then infoUI:Destroy() end

	local attachment = door:FindFirstChild("Attachment")
	if attachment then attachment:Destroy() end

	local fogFolder = door:FindFirstChild("Fog Folder")

	-- Fade the door itself
	fadePartToFog(door, player)

	-- Fade all parts inside the Fog Folder
	if fogFolder then
		for _, part in ipairs(fogFolder:GetChildren()) do
			fadePartToFog(part, player)
		end
	end

	-- Schedule cleanup AFTER fade is done (3 seconds)
	delay(3, function()
		if door and door:IsDescendantOf(game) then
			door:Destroy()
		end
	end)
end

local function destroyDoor(door)
	door:Destroy()
end

-- Track which paths the player has unlocked
local unlockedPaths = {}

-- Check if the player can unlock a door from a given path
local function canUnlockPath(path)
	-- If the path has no doors, allow unlocking it
	if not path then
		return true
	end

	-- If the player hasn't unlocked any paths, they can choose any path
	if #unlockedPaths == 0 then
		return true
	end

	-- Check if all doors in the current path have been unlocked
	if unlockedPaths[path] == nil then
		-- Check if all doors in the current path are unlocked
		for _, door in pairs(doorsFolder:GetChildren()) do
			if door:GetAttribute("Path") == path then
				local ownedVal = player:FindFirstChild("Doors"):FindFirstChild(door.Name)
				if ownedVal and not ownedVal.Value then
					return false
				end
			end
		end
		-- If all doors in the path are unlocked, mark the path as unlocked
		unlockedPaths[path] = true
		return true
	end

	-- Return whether the path is unlocked
	return unlockedPaths[path] == true
end

-- Function to handle unlocking a door
local function unlockDoor(door)
	local path = door:GetAttribute("Path")
	if path and not canUnlockPath(path) then
		-- Show message or handle logic for not being able to unlock this path
		return
	end

	-- Handle unlocking the door as normal
	doorEvents:WaitForChild("Buy"):FireServer(door.Name)
end

-- Update the UI for doors with path information
local function updateDoorUI(door)
	if not door or not door:IsDescendantOf(game) then return end

	local cost = door:GetAttribute("Cost")
	local currencyName = door:GetAttribute("Currency")
	local ownedVal = player:FindFirstChild("Doors"):FindFirstChild(door.Name)
	if not ownedVal then return end
	local owned = ownedVal.Value
	local currencyStat = player:FindFirstChild("leaderstats"):FindFirstChild(currencyName)
	if not currencyStat then return end
	local currencyVal = currencyStat.Value

	local gui = door:FindFirstChild("DoorInformations")
	if not gui then return end

	gui:FindFirstChild("Doorname").Text = door.Name

	if cost > 100000 then
		gui.Cost.Text = formatNumber(cost)
	else
		gui.Cost.Text = tostring(cost):reverse():gsub("(%d%d%d)", "%1."):reverse():gsub("^%.", "")
	end

	gui:FindFirstChild("ImageBackground").Visible = (currencyName == "Coins")

	gui.Cost.Visible = not owned
	gui.Close.Visible = not owned
	gui.Doorname.Visible = not owned

	if owned then
		fadeOutDoor(door)
	else
		local textColor = currencyVal >= cost and Color3.fromRGB(0, 255, 0) or Color3.fromRGB(255, 0, 0)
		gui.Cost.TextColor3 = textColor
		gui.Close.TextColor3 = textColor
	end
end

-- Initial door setup
for _, door in pairs(doorsFolder:GetChildren()) do
	local ownedVal = player:WaitForChild("Doors"):WaitForChild(door.Name)
	if ownedVal.Value == true then
		fadeOutDoor(door)
	else
		updateDoorUI(door)
	end
end

-- Update UI when currency changes
local trackedCurrencies = {}

for _, door in pairs(doorsFolder:GetChildren()) do
	local currencyName = door:GetAttribute("Currency")

	if not trackedCurrencies[currencyName] then
		trackedCurrencies[currencyName] = true

		local currencyStat = player.leaderstats:FindFirstChild(currencyName)
		if currencyStat then
			currencyStat.Changed:Connect(function()
				for _, d in pairs(doorsFolder:GetChildren()) do
					if d:GetAttribute("Currency") == currencyName then
						updateDoorUI(d)
					end
				end
			end)
		end
	end
end

-- Handle buying a door from prompt
doorEvents:WaitForChild("Door").OnClientEvent:Connect(function(doorName)
	local door = doorsFolder:FindFirstChild(doorName)
	if not door then return end

	local cost = door:GetAttribute("Cost")
	local currency = door:GetAttribute("Currency")
	local currencyVal = player.leaderstats:FindFirstChild(currency).Value
	local owned = player:WaitForChild("Doors"):WaitForChild(doorName).Value

	-- Check if the player can unlock the door based on the path
	local path = door:GetAttribute("Path")
	if path and not canUnlockPath(path) then
		-- Optionally show a message about the path not being unlocked
		return
	end

	if not owned and currencyVal >= cost then
		doorEvents:WaitForChild("Buy"):FireServer(doorName)
	end
end)

-- Smoothly fade out a door when bought
-- Handle the door fade and removal when the ChangeDoor event is triggered
doorEvents:WaitForChild("ChangeDoor").OnClientEvent:Connect(function(doorName)
	local door = doorsFolder:FindFirstChild(doorName)
	if door then
		-- Add path to unlockedPaths if it's the first door unlocked in a path
		local path = door:GetAttribute("Path")
		if path and not unlockedPaths[path] then
			unlockedPaths[path] = true
		end

		-- Hide GUI elements associated with the door
		local gui = door:FindFirstChild("DoorInformations")
		if gui then
			gui.Cost.Visible = false
			gui.Close.Visible = false
			gui.Doorname.Visible = false
		end

		-- Call the fade-out and destroy function to visually remove the door
		fadeOutDoor(door)
	end
end)

-- Update UI for doors when currencies change
for _, door in pairs(doorsFolder:GetChildren()) do
	updateDoorUI(door)
end

doorsFolder.ChildAdded:Connect(function(newDoor)
	print("🆕 New door added:", newDoor.Name)
	task.wait(0.5)
	updateDoorUI(newDoor)
end)

for _, door in pairs(doorsFolder:GetChildren()) do
	print("🔍 Found door:", door.Name)
	updateDoorUI(door)
end

Please do not ask people to write entire scripts or design entire systems for you. If you can’t answer the three questions above, you should probably pick a different category.

2 Likes