TimberWoods / src /ServerScriptService /ShopManager.server.lua
algorembrant's picture
Upload 88 files
0712d5f verified
-- src/ServerScriptService/ShopManager.server.lua
local CollectionService = game:GetService("CollectionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")
local ShopConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("ShopConfig"))
local PurchaseEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("PurchaseEvent")
local ShopEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("ShopEvent")
local NotificationEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("NotificationEvent")
local SoundEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("SoundEvent")
local counters = {}
local function getPlayerCash(player)
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats then
local cash = leaderstats:FindFirstChild("Cash")
if cash then
return cash.Value, cash
end
end
return 0, nil
end
-- Direct purchase handler (from GUI)
local function handleDirectPurchase(player, itemId)
if not itemId or type(itemId) ~= "string" then return end
if not ShopConfig.Items[itemId] then return end
-- Anti-cheat
if _G.IsRateLimited and _G.IsRateLimited(player, "Purchase") then return end
local item = ShopConfig.Items[itemId]
local currentCash, cashObj = getPlayerCash(player)
if currentCash < item.Price then
NotificationEvent:FireClient(player, "Error", "Not enough cash! Need $" .. tostring(item.Price))
SoundEvent:FireClient(player, "PurchaseFail", nil)
return
end
-- Check if already owned (for tools)
if item.Type == "Tool" and _G.HasInInventory and _G.HasInInventory(player, "Tools", itemId) then
NotificationEvent:FireClient(player, "Info", "You already own this tool!")
return
end
-- Deduct cash
cashObj.Value = cashObj.Value - item.Price
-- Add to inventory
local categoryMap = {
Tool = "Tools",
Machine = "Machines",
Vehicle = "Vehicles",
Automation = "Automation",
Structure = "Machines",
Utility = "Machines",
Component = "Machines",
}
local category = categoryMap[item.Type] or "Machines"
if _G.AddToInventory then
_G.AddToInventory(player, category, itemId)
end
-- Progress quests
if _G.ProgressQuest then
_G.ProgressQuest(player, "Purchase", 1)
end
NotificationEvent:FireClient(player, "Purchase", "Purchased " .. item.Name .. " for $" .. tostring(item.Price))
SoundEvent:FireClient(player, "PurchaseSuccess", nil)
-- Send updated inventory to client
local data = _G.GetPlayerData and _G.GetPlayerData(player)
if data then
ShopEvent:FireClient(player, "InventoryUpdate", data.Inventory)
end
end
-- Counter-based purchase (legacy physical shop)
local function handleCounterPurchase(player, counterZone)
if not counters[counterZone] then return end
local totalCost = 0
local itemsToBuy = {}
for boxedItem, _ in pairs(counters[counterZone]) do
if boxedItem.Parent then
local itemId = boxedItem:GetAttribute("ItemId")
if itemId and ShopConfig.Items[itemId] then
totalCost = totalCost + ShopConfig.Items[itemId].Price
table.insert(itemsToBuy, {item = boxedItem, id = itemId})
end
else
counters[counterZone][boxedItem] = nil
end
end
if #itemsToBuy == 0 then return end
local currentCash, cashObj = getPlayerCash(player)
if currentCash >= totalCost then
cashObj.Value = cashObj.Value - totalCost
for _, buyData in ipairs(itemsToBuy) do
buyData.item:SetAttribute("Purchased", true)
buyData.item.BrickColor = BrickColor.new("Bright green")
counters[counterZone][buyData.item] = nil
if _G.AddToInventory then
_G.AddToInventory(player, "Machines", buyData.id)
end
end
NotificationEvent:FireClient(player, "Purchase", "Transaction complete! Total: $" .. tostring(totalCost))
SoundEvent:FireClient(player, "PurchaseSuccess", nil)
if _G.ProgressQuest then
_G.ProgressQuest(player, "Purchase", #itemsToBuy)
end
else
NotificationEvent:FireClient(player, "Error", "Not enough cash! Need $" .. tostring(totalCost))
SoundEvent:FireClient(player, "PurchaseFail", nil)
end
end
local function bindCounterZone(zonePart)
counters[zonePart] = {}
zonePart.Touched:Connect(function(hit)
if CollectionService:HasTag(hit, "BoxedItem") and not hit:GetAttribute("Purchased") then
counters[zonePart][hit] = true
end
end)
zonePart.TouchEnded:Connect(function(hit)
if counters[zonePart][hit] then
counters[zonePart][hit] = nil
end
end)
end
CollectionService:GetInstanceAddedSignal("ShopCounter"):Connect(bindCounterZone)
for _, zone in pairs(CollectionService:GetTagged("ShopCounter")) do
bindCounterZone(zone)
end
-- Handle both types of purchase events
PurchaseEvent.OnServerEvent:Connect(function(player, target)
if type(target) == "string" then
-- Direct purchase by item ID (from GUI)
handleDirectPurchase(player, target)
elseif typeof(target) == "Instance" and CollectionService:HasTag(target, "ShopCounter") then
-- Counter-based purchase
handleCounterPurchase(player, target)
end
end)