Issue with tycoon game

I encountered a bug in my tycoon game. The problem is that the tycoon starts working and accumulating currency before the player becomes its owner. Because of this, when a new player selects the tycoon, they immediately get the money that has already been farmed — sometimes 3-4 thousand or more. This breaks the game logic, as the player should start from scratch.

I would like to implement a smoother and fairer behavior, where:

:moneybag: The currency starts accumulating only after the owner is assigned.

:turtle: The accumulation happens slowly, by 0.1 per step, with a small delay.

:gear: The entire tycoon structure remains stable (button appearance, interaction, and collection).

I already tried making adjustments to the PartCollector script, but they broke other systems, including the appearance of buttons and purchases. Could you please help me figure out how to properly implement delayed currency accumulation with an owner check, so the tycoon works only after the player enters?

Here is a video: https://www.youtube.com/watch?v=XPNnqC6WkKw

here is my script (PurchaseHandler)

--THIS SCRIPT HAS AN ISSUE NEED FIXING

local mpService = game:GetService("MarketplaceService")
local Str = string.sub
local ID2 = game:GetService("ReplicatedStorage"):WaitForChild("GamepassIDs"):WaitForChild("X2Cash").Value
local ID1 = game:GetService("ReplicatedStorage"):WaitForChild("GamepassIDs"):WaitForChild("AutoCollect").Value

--[[
    All configurations are located in the "Settings" Module script.
    Please don't edit this script unless you know what you're doing.
--]]

-- Добавляем настройки накопления
local accumulationDelay = 0.15 -- Задержка между накоплениями
local accumulationStep = 0.1 -- Шаг накопления
local accumulationRunning = false

local folder = game.ReplicatedStorage:WaitForChild("Events")
local event = folder:WaitForChild("Build")
local Objects = {}
local TeamColor = script.Parent.TeamColor.Value
local Settings = require(script.Parent.Parent.Parent.Settings)
local ProductIds = require(script.Parent.Parent.Parent.DevProductIDS)
local Money = script.Parent.CurrencyToCollect
local Debris = game:GetService('Debris')
local Stealing = Settings.StealSettings
local CanSteal = true -- don't change or else you won't be able to steal currency

script.Parent.Essentials.Spawn.TeamColor = TeamColor
script.Parent.Essentials.Spawn.BrickColor = TeamColor

-- Функция для управления накоплением валюты
local function updateAccumulation()
	if script.Parent.Owner.Value ~= nil then
		if not accumulationRunning then
			accumulationRunning = true
			coroutine.wrap(function()
				while script.Parent.Owner.Value ~= nil and script.Parent:FindFirstChild("CurrencyToCollect") do
					script.Parent.CurrencyToCollect.Value += accumulationStep
					wait(accumulationDelay)
				end
				accumulationRunning = false
			end)()
		end
	else
		accumulationRunning = false
	end
end

-- Сбрасываем валюту при смене владельца
script.Parent.Owner.Changed:Connect(function()
	if script.Parent:FindFirstChild("CurrencyToCollect") then
		script.Parent.CurrencyToCollect.Value = 0 -- сбрасываем при заходе
	end
	updateAccumulation()
end)

function Sound(part,id)
	if part:FindFirstChild('Sound') then
		return
	else
		local Sound = Instance.new('Sound',part)
		Sound.SoundId = "rbxassetid://"..tostring(id)
		Sound:Play()
		delay(Sound.TimeLength, function()
			Sound:Destroy()
		end)
	end
end

--Parts that fall into the collector(s) get processed
for i,v in pairs(script.Parent.Essentials:GetChildren()) do
	if v.Name == "PartCollector" then
		v.Touched:connect(function(Part)
			if Part:FindFirstChild('Cash') then
				Money.Value = Money.Value + Part.Cash.Value
				Debris:AddItem(Part,0.1)
			end
		end)
	end
end

--Player Touched Collector processor
deb = false
script.Parent.Essentials.Giver.Touched:connect(function(hit)
	local player = game.Players:GetPlayerFromCharacter(hit.Parent)
	if player ~= nil then
		if script.Parent.Owner.Value == player then
			if hit.Parent:FindFirstChild("Humanoid") then
				if hit.Parent.Humanoid.Health > 0 then
					if deb == false then
						deb = true
						local Stats = game.ServerStorage.PlayerMoney:FindFirstChild(player.Name)
						if Stats ~= nil then 
							spawn(function()
								if mpService:UserOwnsGamePassAsync(player.UserId, ID2) and not mpService:UserOwnsGamePassAsync(player.UserId, ID2) then
									script.Parent.Essentials.Giver.BrickColor = BrickColor.new("Persimmon")
									Sound(script.Parent.Essentials, Settings.Sounds.Collect)
									game.ReplicatedStorage.PopUp:FireClient(player, "+"..Money.Value.." X2")
									local doubleyes = Money.Value *2
									Stats.Value = Stats.Value + doubleyes
									Money.Value = 0
									wait(1)
									script.Parent.Essentials.Giver.BrickColor = BrickColor.new("Bright green")
									deb = false
								else
									script.Parent.Essentials.Giver.BrickColor = BrickColor.new("Persimmon")
									Sound(script.Parent.Essentials, Settings.Sounds.Collect)
									game.ReplicatedStorage.PopUp:FireClient(player, "+"..Money.Value)
									Stats.Value = Stats.Value + Money.Value
									Money.Value = 0
									wait(1)
									script.Parent.Essentials.Giver.BrickColor = BrickColor.new("Bright green")
									deb = false
								end
							end)
						end
					end
				end
			end
		elseif Stealing.Stealing then -- if player isn't owner and stealing is on
			if CanSteal == true then
				CanSteal = false
				delay(Stealing.PlayerProtection, function()
					CanSteal = true
				end)
				if hit.Parent:FindFirstChild("Humanoid") then
					if hit.Parent.Humanoid.Health > 0 then
						local Stats = game.ServerStorage.PlayerMoney:FindFirstChild(player.Name)
						if Stats ~= nil then
							local Difference = math.floor(Money.Value * Stealing.StealPrecent)
							Sound(script.Parent.Essentials, Settings.Sounds.Collect)
							Stats.Value = Stats.Value + Difference
							Money.Value = Money.Value - Difference
						end
					end
				end
			else
				Sound(script.Parent.Essentials, Settings.Sounds.Error)
			end
		end
	end
end)

script.Parent:WaitForChild("Buttons")
for i,v in pairs(script.Parent.Buttons:GetChildren()) do
	spawn(function()
		if v:FindFirstChild("Head") then
			local ThingMade = script.Parent.Purchases:WaitForChild(v.Object.Value)
			if ThingMade ~= nil then
				Objects[ThingMade.Name] = ThingMade:Clone()
				ThingMade:Destroy()
			else
				--//Button doesn't have object, remove it
				error('Object missing for button: '..v.Name..', button has been removed')
				v.Head.CanCollide = false
				v.Head.Transparency = 1
				v.Extra.Transparency = 1
				v.Extra.BillboardGui.Enabled = false
			end

			if v:FindFirstChild("Dependency") then --// if button needs something unlocked before it pops up
				v.Head.CanCollide = false
				v.Head.Transparency = 1
				v.Extra.Transparency = 1
				v.Extra.BillboardGui.Enabled = false
				coroutine.resume(coroutine.create(function()
					if script.Parent.PurchasedObjects:WaitForChild(v.Dependency.Value) then
						if Settings['ButtonsFadeIn'] then
							for i=1,20 do
								wait(Settings['FadeInTime']/20)
								v.Head.Transparency = v.Head.Transparency - 0.05
								v.Extra.Transparency = v.Extra.Transparency - 0.05
							end
						end
						v.Head.CanCollide = true
						v.Head.Transparency = 0
						v.Extra.Transparency = 0
						v.Extra.BillboardGui.Enabled = true
					end
				end))
			end

			v.Head.Touched:connect(function(hit)
				local player = game.Players:GetPlayerFromCharacter(hit.Parent)
				if v.Head.CanCollide == true then
					if player ~= nil then
						if script.Parent.Owner.Value == player then
							if hit.Parent:FindFirstChild("Humanoid") then
								if hit.Parent.Humanoid.Health > 0 then
									local PlayerStats = game.ServerStorage.PlayerMoney:FindFirstChild(player.Name)
									if PlayerStats ~= nil then
										if (v:FindFirstChild('Gamepass')) and (v.Gamepass.Value >= 1) then
											if game:GetService("MarketplaceService"):PlayerOwnsAsset(player,v.Gamepass.Value) then
												Purchase({[1] = v.Price.Value,[2] = v,[3] = PlayerStats})
											else
												game:GetService('MarketplaceService'):PromptPurchase(player,v.Gamepass.Value)
											end
										elseif (v:FindFirstChild('DevProduct')) then
											game:GetService('MarketplaceService'):PromptProductPurchase(player,ProductIds[script.Parent.nameValue.Value][v.Object.Value])
										elseif PlayerStats.Value >= tonumber(v.Extra.BillboardGui.Price.Text:sub(2)) then
											Purchase({[1] = tonumber(v.Extra.BillboardGui.Price.Text:sub(2)),[2] = v,[3] = PlayerStats})
											Sound(v, Settings.Sounds.Purchase)
										else
											Sound(v, Settings.Sounds.ErrorBuy)
										end
									end
								end
							end
						end
					end
				end
			end)
		end
	end)
end

function Purchase(tbl)
	print(tbl[2])
	local cost = tbl[1]
	local item = tbl[2]
	local stats = tbl[3]
	stats.Value = stats.Value - cost
	Objects[item.Object.Value].Parent = script.Parent.PurchasedObjects
	event:FireAllClients(script.Parent.PurchasedObjects[item.Object.Value],1)
	if Settings['ButtonsFadeOut'] then
		item.Head.CanCollide = false
		coroutine.resume(coroutine.create(function()
			for i=1,20 do
				wait(Settings['FadeOutTime']/20)
				item.Head.Transparency = item.Head.Transparency + 0.05
				item.Extra.Transparency = item.Extra.Transparency + 0.05
			end
			item.Extra.BillboardGui.Enabled = false
		end))
	else
		item.Head.CanCollide = false
		item.Head.Transparency = 1
		item.Extra.Transparency = 1
		item.Extra.BillboardGui.Enabled = false
	end
end

function Create(tab)
	local x = Instance.new('Model')
	Instance.new('NumberValue',x).Value = tab[1]
	x.Value.Name = "Cost"
	Instance.new('ObjectValue',x).Value = tab[2]
	x.Value.Name = "Button"
	local Obj = Instance.new('ObjectValue',x)
	Obj.Name = "Stats"
	Obj.Value = tab[3]
	x.Parent = script.Parent.BuyObject
end

--// This was very rushed and is inefficent; if you plan on making something like this don't use a child added listener.
script.Parent:WaitForChild('BuyObject').ChildAdded:connect(function(child)
	local tab = {}
	tab[1] = child.Cost.Value
	tab[2] = child.Button.Value
	tab[3] = child.Stats.Value
	Purchase(tab)
	wait(10)
	child:Destroy()
end)

-- Инициализация накопления при запуске
updateAccumulation()
1 Like

I think you should try to disable the script and enable it when the player becomes the owner. (I’m using a translator)

I suspect that there’s a mistake here.

while script.Parent.Owner.Value ~= nil and script.Parent:FindFirstChild("CurrencyToCollect") do
					script.Parent.CurrencyToCollect.Value += accumulationStep
					wait(accumulationDelay)
				end

try this

local accumulationDelay = 0.15
local accumulationStep = 0.1

local tycoon = script.Parent
local currency = tycoon:WaitForChild("CurrencyToCollect")
local ownerValue = tycoon:WaitForChild("Owner")

local accumulationRunning = false

-- Proper coroutine accumulation loop
local function startAccumulation()
	if accumulationRunning then return end
	accumulationRunning = true

	coroutine.wrap(function()
		while accumulationRunning do
			if ownerValue.Value ~= nil then
				currency.Value += accumulationStep
			end
			wait(accumulationDelay)
		end
	end)()
end

-- Stop accumulation (called on unclaim)
local function stopAccumulation()
	accumulationRunning = false
end

-- When the owner changes (player claims or leaves)
ownerValue.Changed:Connect(function()
	local owner = ownerValue.Value
	if owner then
		currency.Value = 0 -- reset currency when claimed
		startAccumulation()
	else
		stopAccumulation()
		currency.Value = 0 -- optional: also reset on unclaim
	end
end)

-- Start monitoring
if ownerValue.Value ~= nil then
	startAccumulation()
end

Also update the PartCollector.Touched connection to check for ownership:

if v.Name == "PartCollector" then
	v.Touched:connect(function(Part)
		if Part:FindFirstChild('Cash') then
			if script.Parent.Owner.Value ~= nil then -- ✅ only add currency if owned
				Money.Value = Money.Value + Part.Cash.Value
			end
			Debris:AddItem(Part, 0.1)
		end
	end)
end

I found the issue. One dropper didn’t removed that why money counting xD

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.