Hungrit Scripting

Scores and rewards#

A leaderboard everybody in the world shares, a reward that comes once a day, and a round that runs on a clock. Three things nearly every game wants, built from world.store, players.store and task.

A leaderboard#

Keep one key per player under a common prefix, and let top do the ranking.

On a scoreboard object
local world = require("world")local players = require("players") -- Anything in the world can call this: a goal, a coin, a finish line.local function add_points(player_id: string, points: number)    world.store:update("score." .. player_id, function(current)        return tostring((tonumber(current) or 0) + points)    end)end self:on_touch(function(player)    add_points(player.id, 10)     print("— top 5 —")    for i, row in world.store:top("score.", 5) do        local who = players:get(row.key)        print(i, who and who.name or "someone who left", row.value)    endend)

row.key comes back without the prefix, so here it is the player's id. Somebody who has left still has a score; players:get answers nil for them, which is why the name has a fallback.

Name keys like paths

score.<id>, shop.bread, quest.<id>.step. A prefix then works as a folder: keys("quest.") lists every quest row, and a different prefix never mixes into your leaderboard.

A reward once a day#

os.time() is the real clock: seconds since 1970, the same on every server. Store when somebody last collected, in their drawer, so it follows them to whichever machine hosts the world next time.

On a chest
local players = require("players") local DAY = 24 * 60 * 60 self:on_touch(function(player)    local last, why = players.store:get(player.id, "daily.last")    if why then return end                     -- not ready, or not allowed     local now = os.time()    local since = now - (tonumber(last) or 0)    if since < DAY then        local hours = math.ceil((DAY - since) / 3600)        print(player.name .. ": come back in " .. hours .. " h")        return    end     players.store:set(player.id, "daily.last", tostring(now))    players.store:update(player.id, "gold", function(current)        return tostring((tonumber(current) or 0) + 50)    end)    print(player.name .. " collected 50 gold")end)

"Once a day" here means 24 hours after the last one. For "once per calendar day", compare dates instead: os.date("!%Y-%m-%d") is today in UTC.

A round on a clock#

A round is a sequence: open, wait, warn, wait, close, rest, again. With task it is written in that order.

On a round controller
local world = require("world") local function announce(text: string)    world.store:set("round.state", text)    print(text)end task.spawn(function()    while true do        announce("round open")        task.wait(120)        announce("30 seconds left")        task.wait(30)        announce("round over")         local best = world.store:top("score.", 1)[1]        if best then            print("winner:", best.key, best.value)        end        for _, key in world.store:keys("score.") do            world.store:set(key, nil)          -- a fresh board for the next round        end         task.wait(20)    endend)

Every object in the world can read round.state with world.store:get, so a gate or a scoreboard reacts to the round without talking to the controller.

See also#

Hungrit scripting documentation.

Updated 26 September 2026