File size: 5,169 Bytes
0712d5f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
-- 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)