Pular para o conteúdo
PTEntrar

Como fazer uma recompensa diária no Roblox?

Recompensa diária é uma data salva, não um cronômetro. Guarde os.time() no instante em que o jogador resgata e, em toda entrada, compare com agora: se passaram mais de 24 horas, o botão volta a valer. A sequência é um segundo número que você zera quando o intervalo passa de 48 horas.

Em resumo

  • Guarde a hora do resgate, nunca uma contagem. Contagem morre junto com o servidor.
  • 24 horas desde o último resgate liberam o botão; 48 horas quebram a sequência.
  • Quem decide a recompensa é o servidor. O cliente só pede.

Uma recompensa diária faz uma pergunta só: quanto tempo desde o último resgate deste jogador? Todo o resto — a sequência, o prêmio que cresce, o botão brilhando — sai desse único número. O jogador fecha o jogo, o servidor desliga, passa uma semana: uma data salva sobrevive a tudo isso e um cronômetro rodando não sobrevive a nada.

Passo a passo

1. Uma entrada de DataStore por jogador

Guarde a hora do resgate e a sequência juntas, numa tabela. Um UpdateAsync escreve as duas, então o jogador nunca termina com hora nova e sequência velha.

local DataStoreService = game:GetService("DataStoreService")
local store = DataStoreService:GetDataStore("DailyReward_v1")

local DIA = 24 * 60 * 60

local function carregar(userId: number)
	local ok, dados = pcall(function()
		return store:GetAsync("u_" .. userId)
	end)
	if not ok or type(dados) ~= "table" then
		return { lastClaim = 0, streak = 0 }
	end
	return dados
end

2. Pergunte se o botão está valendo

A resposta é uma subtração. Escreva ela uma vez, no servidor, e deixe o botão e o resgate lerem a mesma função — duas cópias dessa regra é como o jogador ganha um botão que não faz nada.

local function situacao(dados)
	local desde = os.time() - (dados.lastClaim or 0)
	return {
		canClaim = desde >= DIA,
		-- Intervalo maior que dois dias significa que ele perdeu um dia inteiro:
		-- a sequência acabou.
		streakAlive = desde < 2 * DIA,
		secondsLeft = math.max(0, DIA - desde),
	}
end

3. Resgate no servidor, nunca no cliente

O cliente aperta um botão; o servidor decide quanto aquilo vale. Recompensa calculada no cliente é recompensa que qualquer um reescreve.

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local evento = Instance.new("RemoteEvent")
evento.Name = "ClaimDaily"
evento.Parent = ReplicatedStorage

local PREMIOS = { 50, 75, 100, 150, 250, 400, 1000 }

evento.OnServerEvent:Connect(function(player: Player)
	local dados = carregar(player.UserId)
	local s = situacao(dados)
	if not s.canClaim then return end

	local sequencia = s.streakAlive and (dados.streak or 0) + 1 or 1
	local dia = math.min(sequencia, #PREMIOS)
	local valor = PREMIOS[dia]

	local ok = pcall(function()
		store:UpdateAsync("u_" .. player.UserId, function()
			return { lastClaim = os.time(), streak = sequencia }
		end)
	end)
	-- Nada é pago antes de a gravação dar certo: prêmio dado num save que falhou
	-- é prêmio que o jogador recebe de novo na próxima entrada.
	if not ok then return end

	local stats = player:FindFirstChild("leaderstats")
	local moedas = stats and stats:FindFirstChild("Coins")
	if moedas then
		moedas.Value = moedas.Value + valor
	end
	evento:FireClient(player, { claimed = true, day = dia, amount = valor })
end)

4. Diga ao cliente o que desenhar

Mande a situação na entrada e deixe a interface desenhar. O cliente nunca calcula a liberação; ele é informado.

Players.PlayerAdded:Connect(function(player: Player)
	local dados = carregar(player.UserId)
	task.defer(function()
		evento:FireClient(player, situacao(dados))
	end)
end)

Qual relógio comparar

O que você comparaSobrevive a um restartSobrevive ao fusoUse quando
Diferença de os.time()simsimQuase sempre: "24 horas desde o seu resgate"
Dia do calendário (os.date("*t"))simnão — é o dia em UTC do servidor, não o do jogadorVocê quer "um resgate por dia do calendário" e aceita UTC
tick() / os.clock()não—Nunca para isto: os dois zeram quando o servidor zera
Um while rodandonão—Nunca. O jogador fecha o jogo e o seu cronômetro morre

Perguntas

Por que a minha recompensa zera a cada restart do servidor?

Porque você está medindo com algo que também zera. tick(), os.clock() e um contador while true do task.wait(1) end começam do zero junto com o servidor. os.time() é o relógio de verdade, em segundos desde 1970, e é o mesmo número em todos os servidores da sua experiência.

Como impedir que alguém resgate duas vezes entrando de novo?

A checagem é no servidor e a data gravada é a única autoridade. Entrar de novo relê o mesmo lastClaim, então desde continua abaixo de um dia e o resgate sai na primeira linha. Nunca guarde canClaim numa variável do cliente — essa, sim, zera quando ele entra de novo.

UpdateAsync ou SetAsync?

UpdateAsync aqui. Dois servidores podem segurar o mesmo jogador por um instante num teleporte, e com SetAsync o segundo sobrescreve o primeiro. UpdateAsync lê e escreve como uma só operação, e é isso que impede a sequência de andar para trás.

Onde colocar a sequência na tela?

Do lado do botão, como o número em si — "Dia 4" — e o prêmio do dia 5 apagado ao lado. Sequência que o jogador não vê é sequência que não muda o que ele faz amanhã.

Atualizado: Testado no Studio: Roblox Studio, outubro de 2026Autor: GustavoLicença: O código deste guia pode ser usado em qualquer experiência do Roblox, inclusive paga

Pack relacionado

Ultimate Script Pack

Ultimate Script Pack

US$ 40Ver o pack

Quero ser avisado

Em breve

Deixe seu e-mail e a gente te avisa na hora em que este pack sair.