Problem with Bank Interest System

Currently I am working on creating a script that would take the amount of money deposited in the a bank and only when money is deposited and create interest after 24 hours have passed. Kind of like a daily rewards system.

Here is the scripts:
Local Script in Frame:

local timer = 24 * 60 * 60
local ATMEvent = game.ReplicatedStorage:WaitForChild("ATMEvent")
local InterestEvent = game.ReplicatedStorage:WaitForChild("InterestEvent")
local Interest = 0.015
local plr = game.Players.LocalPlayer

ATMEvent.OnClientEvent:Connect(function(deposited)
	local timeRemaining = timer
	while timeRemaining > 0 do
		task.wait(1)
		timeRemaining -= 1
		local hours = math.floor(timeRemaining / 3600)
		local minutes = math.floor((timeRemaining % 3600) / 60)
		local seconds = timeRemaining % 60
		script.Parent.Timer.Text = "Time Remaining: " .. hours .. " hr: " .. minutes .. " mins: " .. seconds .. " sec"
	end
	InterestEvent:FireServer(deposited, Interest)
end)

Remote Server Event (Interest Event):

local RemoteEvent = game.ReplicatedStorage.InterestEvent

RemoteEvent.OnServerEvent:Connect(function(deposited, Interest)
	local int = deposited*Interest
	local newvalue = int + deposited
	deposited = newvalue	
end)

Remote Sever Script (ATMEvent - Taken From Tutorial)

local ATMEvent = game.ReplicatedStorage:WaitForChild("ATMEvent")
local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("ATM")
function saveData(plr)
	local cash = plr.Values.Cash
	pcall(function()
		ds:SetAsync(plr.UserId .. "Cash", cash.Value)
	end)
end

game.Players.PlayerAdded:Connect(function(plr)
	local depositedCash
	pcall(function()
		depositedCash = ds:GetAsync(plr.UserId .. "Deposited")
	end)
	
	if not depositedCash then
		ds:SetAsync(plr.UserId .. "Deposited", 0)
	end
end)

game.Players.PlayerRemoving:Connect(saveData)
game:BindToClose(function()
	for i, plr in pairs(game.Players:GetPlayers()) do
		saveData(plr)
	end
end)

ATMEvent.OnServerEvent:Connect(function(plr, amount, option)
	if amount and option and tonumber(amount) then
		local cashStored
		pcall(function()
			cashStored = ds:GetAsync(plr.UserId .. "Deposited")
		end)
		if not cashStored then return end
		if option == "deposit" then
			if tonumber(amount) <= plr.Values.Cash.Value then
				plr.Values.Cash.Value -= amount
				cashStored += amount
			end
		elseif option == "withdraw" then
			if tonumber(amount) <= cashStored then
				plr.Values.Cash.Value += amount
				cashStored -= amount
			end
		end
		pcall(function()
			ds:SetAsync(plr.UserId .. "Deposited", cashStored)
		end)
	end
end)

local PromptService = game:GetService("ProximityPromptService")
PromptService.PromptTriggered:Connect(function(prompt, plr)
	if prompt.Name == "ATMPrompt" then
		local cashStored
		pcall(function()
			cashStored = ds:GetAsync(plr.UserId .. "Deposited")
		end)
		if cashStored then
			ATMEvent:FireClient(plr, cashStored )
		end
	end
end)




However, I am encountering a couple of errors.

  1. When I deposit money the timer starts counting down as normal. But if I deposit or withdraw any money the counter restarts but then begins to jump between the previous countdown and the current countdown.

So it looks like this: 24hr 59mins 59secs
But then next second it looks like this: 24hr 59mins 30secs
And then the cycle repeats

  1. The timer only works when you open the frame.

  2. The timer works when no money is deposited.

Please leave your suggests below. I believe that the problem lies within the first local script that I showed you because it relies on an Event to fire in order to activate it. And I think it is activating too many times in the background. However, I have no idea if this is true or how to fix it.

Thank You,
Madd

2 Likes

Are you trying to use actual hours/minutes as in real time? Or game hours(which may be faster than real time)?

Because if you are using real time, then the player would have to afk for over 24 hours to ever get interest, right? If you are not saving the ‘start time’ to a datastore, it would start over when they rejoin, no?

2 Likes

are you telling me that this localscript has to run for 24 actual hours before it triggers an event? This is… unreasonable?

Who is gonna wait that long, what if their internet crashes, or if they want to sleep?

2 Likes

Instead of a counter, why not use fromUnixTimeStamp?
You can store that variable in a player’s datastores if you want interest to be based on real world time or just in the script if it’s only based in game time when the player is actually playing.
You may only want to check it every few minutes or so to see if a ‘day’ has passed from the stored Unix date (time the money was deposited) to the current Unix date to see if interest needs to be calculated.

3 Likes

Your problem is that you fire this same function every time you deposit or withdraw money. It’s running another separate timer and sending the multiple timer values to the GUI.
You may have to run a system where it only counts the amount of money in the account once per day depending on Unix time and stores that amount as well as the time they left the game in datastores.

That way if a player has $100 game dollars into their account it would only calculate interest at a certain time (let’s say midnight). Then you wouldn’t be resetting the timer, and having to recalculate all the time difference calculations on the deposits and withdrawals in the account in relation to the timer system.
If a player had that $100 game dollars in their account and didn’t play for 5 days (5 midnights) then when they rejoined you’d check the difference of the time they left and the time they rejoined. It would be 5 days, so then calculate the interest of that $100 over 5 days and update their datastores.

2 Likes

Would you suggest to use something like this?

I agree with you that part of the problem is with the fact the time is not being saved. However, I think using a Remote Event to then string another Remote Event might be causing the system to crash.

Because if someone was to deposit 80 dollars at first then 5 minutes later deposit another 80 dollars the system won’t know how to respond causing it to bug out.

local DataStoreService = game:GetService("DataStoreService")
local playerDataStore = DataStoreService:GetDataStore("PlayerDataStore")
local InterestEvent = game.ReplicatedStorage:WaitForChild("InterestEvent")

local function calculateInterest(deposited)
    local startTime = playerDataStore:GetAsync("startTime") or os.time()
    local currentTime = os.time()
    local elapsedSeconds = currentTime - startTime
    local hours = math.floor(elapsedSeconds / 3600)
    local minutes = math.floor((elapsedSeconds % 3600) / 60)
    local seconds = elapsedSeconds % 60
    local timeString = string.format("%02d:%02d:%02d", hours, minutes, seconds)
    script.Parent.Timer.Text = "Time Remaining: " .. timeString
    playerDataStore:SetAsync("startTime", currentTime)
end

InterestEvent.OnClientEvent:Connect(calculateInterest)

Yeah, you’re overwriting startTime every time you do a transaction so it’ll mess up all the previous calculations.

I don’t even think banks would try to save the time of each transaction and calculate interest that way. They probably do it as interest on the daily balance at a certain time.

I’d just save the players os.time leave time and bank balance, then calculate it with os.time when they join back by counting the number of 24 hour days from a specific point. That way if they join 50 times in a day they wouldn’t get 50 days worth of interest. Each time they joined they’d see the exact same interest transaction since the previous day. If during those 50 joins they put $1000 in each time that would mean at the end of the current day the interest would be calculated on the previous balance + (50 deposits * $1000).

1 Like

How would you modify the current script to achieve this?

I’m not a great scripter and haven’t worked with datastores. Let’s say you want to calculate interest every time os.time gets to midnight.

Basically I’d look at firing this function on the server (not the client) by checking every minute to see if os.time has passed midnight. Don’t check for exactly midnight, but do it something like this:

Set a debounce to false at the beginning of a script
if current.time >= midnight and < midnight + 2 min then
Check if debounce is false then
Set debounce to true to keep the code from occurring more than once.
Check datastore for saved os.time when player left game last
Subtract saved os.time from current os.time and calculate how many full days have passed since the player last left. (math.floor(current time - saved time))
Multiply the players datastore saved account balance by the daily interest rate times the number of days (account balance * daily interest * # of days). This isn’t a compound interest rate, but should be close enough for your purposes.
Add this to the current balance and save it in datastores.
task.wait(a longer period than 2 minutes so the first check doesn’t fire again)
Make debounce false

Also save the players current balance and os.time each time they leave the game

1 Like

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