So, in my game I have NPCs. These NPCs work in literally all other devices, but the 2 people we’ve had play on a PS5 have reported the exact same issue, the NPCs just get stuck.
Here are the 2 scripts I believe to be culprits but also not because they work on literally all other devices.
This is the pathfinding movement: (I’m still a noob with pathfinding)
local function followPath(path: Path, humanoid: Humanoid, root: BasePart): boolean
if not path or path.Status == Enum.PathStatus.NoPath then return false end
local blocked = false
local conn, conn2
conn = path.Blocked:Connect(function() blocked = true end)
conn2 = humanoid.MoveToFinished:Connect(function(reached)
if not reached then
blocked = true
end
end)
local npcMove, npcGoal = 0, #path:GetWaypoints()
for index, waypoint in ipairs(path:GetWaypoints()) do
humanoid:MoveTo(waypoint.Position)
local reached = false
local startTime = os.clock()
while os.clock() - startTime < 2 do
if blocked then break end
if humanoid.MoveToFinished.Wait then
reached = humanoid.MoveToFinished:Wait()
break
end
task.wait()
end
if not reached or blocked then
if conn then
conn:Disconnect()
conn2:Disconnect()
end
return false
end
npcMove = index
end
if conn then
conn:Disconnect()
conn2:Disconnect()
end
return true
end
And this is my NPCs “shopping loop”
while spent < spendingLimit and maxTries > 0 do
task.wait()
local furnitureList = getFurnitureWithStock(storeModel, npc:GetAttribute("playerSpendingLimit") - spent)
local validFurniture = {}
task.desynchronize()
for _, furniture in ipairs(furnitureList) do
local item = furniture:GetAttribute("Item")
local itemId = sharedF.getItemId(item, sharedF.stockList)
local price = (sharedF.stockPrices[itemId] and sharedF.stockPrices[itemId].sell) or 0
if not VISITED_ITEMS[furniture] and price <= spendingLimit - spent then
table.insert(validFurniture, furniture)
end
end
task.synchronize()
local numFurniture = #validFurniture
local totalFurniture = #furnitureList
table.clear(furnitureList)
if numFurniture == 0 and totalFurniture == numFurniture and not visitedAny then
game.ReplicatedStorage:WaitForChild("events"):WaitForChild("sendServerDebug"):FireServer(`No furniture was found`)
say(NPC_DIALOGUE.NothingToBuy[math.random(1, #NPC_DIALOGUE.NothingToBuy)], lastSpoke, root)
lastSpoke = time()
table.clear(VISITED_ITEMS)
VISITED_ITEMS = nil
break
elseif numFurniture == 0 and totalFurniture ~= numFurniture and not visitedAny then
game.ReplicatedStorage:WaitForChild("events"):WaitForChild("sendServerDebug"):FireServer(`NPC cannot afford items`)
say(NPC_DIALOGUE.LowTrustDialogue[math.random(1, #NPC_DIALOGUE.LowTrustDialogue)], lastSpoke, root)
lastSpoke = time()
table.clear(VISITED_ITEMS)
VISITED_ITEMS = nil
break
elseif numFurniture == 0 then
game.ReplicatedStorage:WaitForChild("events"):WaitForChild("sendServerDebug"):FireServer(`No furiture remains to be visited`)
table.clear(VISITED_ITEMS)
VISITED_ITEMS = nil
break
end
local chosen = pickFurniture(validFurniture, npc)
if not chosen then
game.ReplicatedStorage:WaitForChild("events"):WaitForChild("sendServerDebug"):FireServer(`Couldn't choose furniture- rng fallback`)
chosen = validFurniture[math.random(1, numFurniture)] --// Fallback to old RNG system
end
VISITED_ITEMS[chosen] = true
visitedAny = true
local target = chosen:FindFirstChild("npcTarget")
if not target then continue end
path = computePath(path, root.Position, target.Position)
if not path or not followPath(path, humanoid, root) then
game.ReplicatedStorage:WaitForChild("events"):WaitForChild("sendServerDebug"):FireServer(`Path couldn't be calculated`)
if #validFurniture == 1 then
table.clear(validFurniture)
say(NPC_DIALOGUE.CannotReach[math.random(1, #NPC_DIALOGUE.CannotReach)], lastSpoke, root)
lastSpoke = time()
warn("invalid furniture")
break
else
table.clear(validFurniture)
maxTries -= 1
continue
end
end
table.clear(validFurniture)
local itemName = chosen:GetAttribute("Item")
local itemId = sharedF.getItemId(itemName, sharedF.stockList)
if not itemId then
maxTries -= 1
continue
end
local available = math.max(0, (chosen:GetAttribute("Amount") or 0) - (chosen:GetAttribute("npcReserved") or 0))
if available <= 0 then
maxTries -= 1
continue
end
local price = (sharedF.stockPrices[itemId] and sharedF.stockPrices[itemId].sell) or 0
if price <= 0 then
maxTries -= 1
continue
end
local maxAffordable = math.floor((spendingLimit - spent) / price)
if maxAffordable <= 0 then
say(NPC_DIALOGUE.cantAffordResponses[math.random(1, #NPC_DIALOGUE.cantAffordResponses)], lastSpoke, root)
lastSpoke = time()
maxTries -= 1
continue
end
local desired = math.min(maxAffordable, math.random(1, 3 ^ (Players.LocalPlayer.leaderstats.Rating.Value / 1.6)), available)
if desired <= 0 then
maxTries -= 1
continue
end
if npc.PrimaryPart and chosen.PrimaryPart then
local lookAt = Vector3.new(chosen.PrimaryPart.Position.X, npc.PrimaryPart.Position.Y, chosen.PrimaryPart.Position.Z)
npc:PivotTo(CFrame.lookAt(npc.PrimaryPart.Position, lookAt))
end
local boughtAmount = 0
npc:SetAttribute("npcRngThreshold", rngThreshold)
for i = 1, desired do
grabAnim:Play()
task.wait(math.random() * 0.4 + 0.2)
boughtAmount += 1
thoughtItemsBought += 1
spent += price
npc:SetAttribute("spentMoney", spent)
if npc:FindFirstChild("BillboardGui") and npc.BillboardGui:FindFirstChild("TextLabel") then
local totalSpendingLimit = npc:GetAttribute("playerSpendingLimit") or spendingLimit
npc.BillboardGui.TextLabel.Text = `${math.max(0, totalSpendingLimit - spent)}`
end
local roll = math.random()
if roll < rngThreshold then
break
end
npc:SetAttribute("npcRngThreshold", (rngThreshold or 0) + (0.02/Players.LocalPlayer.leaderstats.Rating.Value))
task.wait(math.random() * 0.3 + 0.1)
end
if boughtAmount > 0 then
local success = game.ReplicatedStorage.functions.npcReserve:InvokeServer(npc, chosen, boughtAmount, itemId)
if not success then
thoughtItemsBought -= boughtAmount
spent -= price
npc:SetAttribute("spentMoney", spent)
if npc:FindFirstChild("BillboardGui") and npc.BillboardGui:FindFirstChild("TextLabel") then
local totalSpendingLimit = npc:GetAttribute("playerSpendingLimit") or spendingLimit
npc.BillboardGui.TextLabel.Text = `${math.max(0, totalSpendingLimit - spent)}`
end
else
if thoughtItemsBought > 0 then
local hasAnyStock = false
for _, v in storeModel.playerFurniture:GetChildren() do
if v:GetAttribute("Item") and v:GetAttribute("Amount") > 0 then
hasAnyStock = true
break
end
end
if not hasAnyStock then
sharedF.Notification(`<font color="#ff2323">Your store has run out of stock!</font>`)
end
end
end
end
task.desynchronize()
boughtAmount = nil
desired = nil
chosen = nil
maxAffordable = nil
price = nil
available = nil
target = nil
numFurniture = nil
task.synchronize()
maxTries -= 1
end
I’m almost positive that either of these scripts are the problem because the code runs up to here and then gets stuck, as to why its only a problem on console, I have no idea.
I’m fully out of ideas, I’ve tried debugging it for 2 days already and I feel like I’m going insane.
And while we’re here, feel free to bash my code and tell me how I could write it better.