• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[TFS 1.5] Rewards by online time (๐’ช๐“ƒ๐“๐’พ๐“ƒ๐‘’ ๐’ซ๐‘œ๐’พ๐“ƒ๐“‰๐“ˆ ๐’ฎ๐“Ž๐“ˆ๐“‰๐‘’๐“‚)

Sarah Wesker

ฦฦ–ั”gฮฑฮทั‚ Sัƒฮทั‚ฮฑx โค
Staff member
TFS Developer
Support Team
Joined
Mar 16, 2017
Messages
1,421
Solutions
155
Reaction score
1,985
Location
London
GitHub
MillhioreBT
Twitch
millhiorebt
This is a very common script, I even posted a similar one a long time ago, but I still want to post the version for TFS 1.5.
for tfs 1.4 click here

Rewards by online time.
data/scripts/rewardsbyonlinetime.lua
Lua:
--๐“œ๐“ฒ๐“ต๐“ต๐“ฑ๐“ฒ๐“ธ๐“ป๐“ฎ ๐“‘๐“ฃ
local config = {
    seconds = 3600, -- 1 hour
    eventInterval = 1000, -- 1 second
    checkIP = true,
    checkAccount = true,
    allowMCs = 3,
    storageKey = 73106,
    rewards = {
        { itemId = 2160, count = 100 },
        { name = "premium points", itemDb = 'accounts', value = 'premium_points', count = 100 }
    }
}

local onlineTimeRewards = GlobalEvent("onlineTimeRewards")

function onlineTimeRewards.onThink(interval)
    local duplicateIps = {}
    local duplicateAccounts = {}
    for _, player in pairs(Game.getPlayers()) do repeat
        local ip = player:getIp()
        if config.checkIP and ip == 0 or (duplicateIps[ip] or 0) >= config.allowMCs then
            break
        end
        duplicateIps[ip] = duplicateIps[ip] and duplicateIps[ip] + 1 or 1
        local accountId = player:getAccountId()
        if config.checkAccount and (duplicateAccounts[accountId] or 0) >= config.allowMCs then
            break
        end
        duplicateAccounts[accountId] = duplicateAccounts[accountId] and duplicateAccounts[accountId] + 1 or 1
        local seconds = math.max(player.storage[storageKey], 0) + math.ceil(interval/1000)
        if seconds >= config.seconds then
            player.storage[storageKey] = 0
            local rewards = {}
            for _, reward in pairs(config.rewards) do
                if reward.itemDb then
                    if db.query(string.format("UPDATE `%s` SET `%s` = `%s` + %d WHERE `id` = %d", reward.itemDb, reward.value, reward.value, reward.count, accountId)) then
                        rewards[#rewards + 1] = string.format('%s x%d', reward.name, reward.count)
                    else
                        print(string.format("[onlineTimeRewards] Error while rewarding player %s.", player:getName()))
                    end
                else
                    local item = player:addItem(reward.itemId, reward.count)
                    if item then
                        rewards[#rewards + 1] = string.format('%s x%d', item:getName(), reward.count)
                    end
                end
            end
            player:sendTextMessage(MESSAGE_INFO_DESCR, "You have received the following reward(s): " .. table.concat(rewards, ', '))
            break
        end
        player.storage[storageKey] = seconds
    until true end
    return true
end

onlineTimeRewards:interval(config.eventInterval)
onlineTimeRewards:register()

This is a normal reward:
{ itemId = 2160, count = 100 } -- 100 crystal coins.

This is a reward for adding points to a database variable:
{ name = "premium points", itemDb = 'accounts', value = 'premium_points', count = 100 } -- 100 premium points.

This is the interval of the event:
- If your server is affected by this script, perhaps you can try increasing the interval to 2000, 3000, 10000, although if you set very large intervals the script may lose a bit of efficiency and not grant very much. accuracy the rewards in the estimated time.
eventInterval = 1000, -- 1 second

Check if player has IP:
- If set to false, then players without IP can still count points online.
checkIP = true

Check if any player on the same account is already earning points online:
- If set to false, then all players regardless of the same account will still count points.
checkAccount = true

Number of players with the same IP or with the same account allowed:
- This variable works if any of the options checkIP, checkAccount is true.
allowMCs = 3

Storage key, this will be used to keep track of the seconds that have elapsed during the player's online time:
storageKey = 73106

1660808404852.png
 
Last edited:
This is a very common script, I even posted a similar one a long time ago, but I still want to post the version for TFS 1.5.

Rewards by online time.
data/scripts/rewardsbyonlinetime.lua
Lua:
--๐“œ๐“ฒ๐“ต๐“ต๐“ฑ๐“ฒ๐“ธ๐“ป๐“ฎ ๐“‘๐“ฃ
local config = {
    seconds = 3600, -- 1 hour
    eventInterval = 1000, -- 1 second
    checkIP = true,
    checkAccount = true,
    allowMCs = 3,
    storageKey = 73106,
    rewards = {
        { itemId = 2160, count = 100 },
        { name = "premium points", itemDb = 'accounts', value = 'premium_points', count = 100 }
    }
}

local onlineTimeRewards = GlobalEvent("onlineTimeRewards")

function onlineTimeRewards.onThink(interval)
    local duplicateIps = {}
    local duplicateAccounts = {}
    for _, player in pairs(Game.getPlayers()) do repeat
        local ip = player:getIp()
        if config.checkIP and ip == 0 or (duplicateIps[ip] or 0) >= config.allowMCs then
            break
        end
        duplicateIps[ip] = duplicateIps[ip] and duplicateIps[ip] + 1 or 1
        local accountId = player:getAccountId()
        if config.checkAccount and (duplicateAccounts[accountId] or 0) >= config.allowMCs then
            break
        end
        duplicateAccounts[accountId] = duplicateAccounts[accountId] and duplicateAccounts[accountId] + 1 or 1
        local seconds = math.max(player.storage[storageKey], 0) + math.ceil(interval/1000)
        if seconds >= config.seconds then
            player.storage[storageKey] = 0
            local rewards = {}
            for _, reward in pairs(config.rewards) do
                if reward.itemDb then
                    if db.query(string.format("UPDATE `%s` SET `%s` = `%s` + %d WHERE `id` = %d", reward.itemDb, reward.value, reward.value, reward.count, accountId)) then
                        rewards[#rewards + 1] = string.format('%s x%d', reward.name, reward.count)
                    else
                        print(string.format("[onlineTimeRewards] Error while rewarding player %s.", player:getName()))
                    end
                else
                    local item = player:addItem(reward.itemId, reward.count)
                    if item then
                        rewards[#rewards + 1] = string.format('%s x%d', item:getName(), reward.count)
                    end
                end
            end
            player:sendTextMessage(MESSAGE_INFO_DESCR, "You have received the following reward(s): " .. table.concat(rewards, ', '))
            break
        end
        player.storage[storageKey] = seconds
    until true end
    return true
end

onlineTimeRewards:interval(config.eventInterval)
onlineTimeRewards:register()

This is a normal reward:
{ itemId = 2160, count = 100 } -- 100 crystal coins.

This is a reward for adding points to a database variable:
{ name = "premium points", itemDb = 'accounts', value = 'premium_points', count = 100 } -- 100 premium points.

This is the interval of the event:
- If your server is affected by this script, perhaps you can try increasing the interval to 2000, 3000, 10000, although if you set very large intervals the script may lose a bit of efficiency and not grant very much. accuracy the rewards in the estimated time.
eventInterval = 1000, -- 1 second

Check if player has IP:
- If set to false, then players without IP can still count points online.
checkIP = true

Check if any player on the same account is already earning points online:
- If set to false, then all players regardless of the same account will still count points.
checkAccount = true

Number of players with the same IP or with the same account allowed:
- This variable works if any of the options checkIP, checkAccount is true.
allowMCs = 3

Storage key, this will be used to keep track of the seconds that have elapsed during the player's online time:
storageKey = 73106

View attachment 69944
THIS ERROR TFS 1.3
 

Attachments

small improvement:
if someone want to have a CHANCE of getting that reward:
on reward items add "chance":
Lua:
-- if chance is not set, then chance = 100
{ itemId = 2160, count = 1, chance=10 }, -- crystal coin, chance 10%

search for:
Lua:
local item = player:addItem(reward.itemId, reward.count)
if item then
    rewards[#rewards + 1] = string.format('%s x%d', item:getName(), reward.count)
end

change to:
Lua:
local random = math.random(1,100)
if not reward.chance or random <= reward.chance then
    local item = player:addItem(reward.itemId, reward.count)
    if item then
        rewards[#rewards + 1] = string.format('%s x%d', item:getName(), reward.count)
    end
end

If you gonna implement this system with a chance of getting no reward, you can do this, too:
on
Lua:
player:sendTextMessage(MESSAGE_INFO_DESCR, "You have received the following reward(s): " .. table.concat(rewards, ', '))
change to:
Lua:
if rewards[1] then
    player:sendTextMessage(MESSAGE_INFO_DESCR, "You have received the following reward(s): " .. table.concat(rewards, ', '))
else
    player:sendTextMessage(MESSAGE_INFO_DESCR, "You haven't received any reward...\nBetter luck next time.")
end

and thanks @Sarah Wesker for all the nice content you bring here.
 
this work in tfs 1.4.2 ?
Yes, you simply need to change everything related to player.storage to player:getStorageValue and setStorageValue respectively.
Lua:
--๐“œ๐“ฒ๐“ต๐“ต๐“ฑ๐“ฒ๐“ธ๐“ป๐“ฎ ๐“‘๐“ฃ
local config = {
    seconds = 3600, -- 1 hour
    eventInterval = 1000, -- 1 second
    checkIP = true,
    checkAccount = true,
    allowMCs = 3,
    storageKey = 73106,
    rewards = {
        { itemId = 2160, count = 100 },
        { name = "premium points", itemDb = 'accounts', value = 'premium_points', count = 100 }
    }
}

local onlineTimeRewards = GlobalEvent("onlineTimeRewards")

function onlineTimeRewards.onThink(interval)
    local duplicateIps = {}
    local duplicateAccounts = {}
    for _, player in pairs(Game.getPlayers()) do repeat
        local ip = player:getIp()
        if config.checkIP and ip == 0 or (duplicateIps[ip] or 0) >= config.allowMCs then
            break
        end
        duplicateIps[ip] = duplicateIps[ip] and duplicateIps[ip] + 1 or 1
        local accountId = player:getAccountId()
        if config.checkAccount and (duplicateAccounts[accountId] or 0) >= config.allowMCs then
            break
        end
        duplicateAccounts[accountId] = duplicateAccounts[accountId] and duplicateAccounts[accountId] + 1 or 1
        local seconds = math.max(player:getStorageValue(config.storageKey), 0) + math.ceil(interval/1000)
        if seconds >= config.seconds then
            player:setStorageValue(config.storageKey, 0)
            local rewards = {}
            for _, reward in pairs(config.rewards) do
                if reward.itemDb then
                    if db.query(string.format("UPDATE `%s` SET `%s` = `%s` + %d WHERE `id` = %d", reward.itemDb, reward.value, reward.value, reward.count, accountId)) then
                        rewards[#rewards + 1] = string.format('%s x%d', reward.name, reward.count)
                    else
                        print(string.format("[onlineTimeRewards] Error while rewarding player %s.", player:getName()))
                    end
                else
                    local item = player:addItem(reward.itemId, reward.count)
                    if item then
                        rewards[#rewards + 1] = string.format('%s x%d', item:getName(), reward.count)
                    end
                end
            end
            player:sendTextMessage(MESSAGE_INFO_DESCR, "You have received the following reward(s): " .. table.concat(rewards, ', '))
            break
        end
        player:setStorageValue(config.storageKey, seconds)
    until true end
    return true
end

onlineTimeRewards:interval(config.eventInterval)
onlineTimeRewards:register()
 
Anyone has this converted into 0.4 by any chance?
 
Anyone has this converted into 0.4 by any chance?
Lua:
local config = {
    seconds = 3600, -- 1 hour
    eventInterval = 1000, -- 1 second
    checkIP = true,
    checkAccount = true,
    allowMCs = 3,
    storageKey = 73106,
    rewards = {
        { itemId = 2160, count = 100 },
        { name = "premium points", itemDb = 'accounts', value = 'premium_points', count = 100 }
    }
}

function onThink(interval)

    -- # no rewards found
    if #config.rewards == 0 then
        return true
    end
  
    local duplicateIps = {}
    local duplicateAccounts = {}

    local playersOnline = getPlayersOnline()
    for i = 1, #playersOnline do

        local pid = playersOnline[i]
        local ipAddress = getPlayerIp(pid)
        local accountId = getPlayerAccountId(pid)

        repeat

            if config.checkIP then
                -- # ip address <0> = exit client / lost connection
                if ipAddress == 0 or (duplicateIps[ipAddress] or 0) >= config.allowMCs then
                    break
                end
                duplicateIps[ipAddress] = (duplicateIps[ipAddress] or 0) + 1
            end

            if config.checkAccount then
                if (duplicateAccounts[accountId] or 0) >= config.allowMCs then
                    break
                end
                duplicateAccounts[accountId] = (duplicateAccounts[accountId] or 0) or 1
            end

            local newSeconds = math.max(0, tonumber(getPlayerStorageValue(pid, config.storageKey))) + math.ceil(interval / 1000)
            setPlayerStorageValue(pid, config.storageKey, newSeconds)

            if newSeconds < config.seconds then
                break
            end

            local rewards = {}
            for _, reward in ipairs(config.rewards) do
                if reward.itemDb then
                    if db.executeQuery(string.format("UPDATE `%s` SET `%s` = `%s` + %d WHERE `id` = %d", reward.itemDb, reward.value, reward.value, reward.count, accountId)) then
                        rewards[#rewards + 1] = string.format("x%d %s", reward.name, reward.count)
                    else
                        print(string.format("[onlineTimeRewards - %s] Error while rewarding player %s.", reward.name, getCreatureName(pid)))
                    end
                else
                    doPlayerAddItem(pid, reward.itemId, reward.count) -- # it will drop on map if player has no slots/capacity available
                    rewards[#rewards + 1] = string.format("x%d %s", reward.count, getItemNameById(reward.itemId))
                end       
            end

            doPlayerSendTextMessage(pid, MESSAGE_INFO_DESCR, "You have received the following reward(s): " .. table.concat(rewards, ', '):gsub("(.*),", "%1 and"))
            setPlayerStorageValue(pid, config.storageKey, 0)

        until true
    end

    return true
end
 
Last edited:
I've been learning about Revscript for the past few days, but I haven't had a server to test it on until now. I've used Nekiro 1.5 downgrade to test some things out, and I'm wondering if Revscript is a good way to write modular code. What do you think?

Lua:
local config = {
    seconds = 3600, -- 1 hour
    eventInterval = 1000, -- 1 second
    checkIP = true,
    checkAccount = true,
    allowMCs = 3,
    storageKey = 73106,
    rewards = {
        { itemId = 2160, count = 100 },
        { name = "premium points", itemDb = 'accounts', value = 'premium_points', count = 100 }
    }
}

local function rewardPlayer(player)
    local accountId = player:getAccountId()
    local rewards, rewardsCount = {}, 0
    for j = 1, #config.rewards do
        local reward = config.rewards[j]
        if reward.itemDb then
            local query = ("UPDATE `%s` SET `%s` = `%s` + %d WHERE `id` = %d"):format(reward.itemDb, reward.value, reward.value, reward.count, accountId)
            if not db.query(query) then
                print("[onlineTimeRewards] Error while rewarding player " .. player:getName() .. ".")
                return
            end
            rewardsCount = rewardsCount + 1
            rewards[rewardsCount] = reward.name .. " x" .. reward.count
        else
            local item = player:addItem(reward.itemId, reward.count)
            if item then
                rewardsCount = rewardsCount + 1
                rewards[rewardsCount] = item:getName() .. " x" .. reward.count
            end
        end
    end
    player:sendTextMessage(MESSAGE_INFO_DESCR, "You have received the following reward(s): " .. table.concat(rewards, ', '))
end

local function checkDuplicates(players)
    local duplicateIps, duplicateAccounts = {}, {}
    for i = 1, #players do
        local player = players[i]
        local ip = player:getIp()
        if config.checkIP and (ip == 0 or (duplicateIps[ip] or 0) >= config.allowMCs) then
            return false
        end
        duplicateIps[ip] = (duplicateIps[ip] or 0) + 1
        local accountId = player:getAccountId()
        if config.checkAccount and (duplicateAccounts[accountId] or 0) >= config.allowMCs then
            return false
        end
        duplicateAccounts[accountId] = (duplicateAccounts[accountId] or 0) + 1
    end
    return true
end

local function rewardPlayers(interval)
    local players = Game.getPlayers()
    if not checkDuplicates(players) then
        return true
    end
    for i = 1, #players do
        local player = players[i]
        local seconds = math.max(player:getStorageValue(config.storageKey), 0) + math.ceil(interval/1000)
        if seconds >= config.seconds then
            player:setStorageValue(config.storageKey, 0)
            rewardPlayer(player)
        else
            player:setStorageValue(config.storageKey, seconds)
        end
    end
    return true
end

local onlineTimeRewards = GlobalEvent("onlineTimeRewards")

onlineTimeRewards.onThink = rewardPlayers
onlineTimeRewards:interval(config.eventInterval)
onlineTimeRewards:register()
 
Last edited:
I've been learning about Revscript for the past few days, but I haven't had a server to test it on until now. I've used Nekiro 1.5 downgrade to test some things out, and I'm wondering if Revscript is a good way to write modular code. What do you think?

Lua:
local config = {
    seconds = 3600, -- 1 hour
    eventInterval = 1000, -- 1 second
    checkIP = true,
    checkAccount = true,
    allowMCs = 3,
    storageKey = 73106,
    rewards = {
        { itemId = 2160, count = 100 },
        { name = "premium points", itemDb = 'accounts', value = 'premium_points', count = 100 }
    }
}

local function rewardPlayer(player)
    local accountId = player:getAccountId()
    local rewards, rewardsCount = {}, 0
    for j = 1, #config.rewards do
        local reward = config.rewards[j]
        if reward.itemDb then
            local query = ("UPDATE `%s` SET `%s` = `%s` + %d WHERE `id` = %d"):format(reward.itemDb, reward.value, reward.value, reward.count, accountId)
            if not db.query(query) then
                print("[onlineTimeRewards] Error while rewarding player " .. player:getName() .. ".")
                return
            end
            rewardsCount = rewardsCount + 1
            rewards[rewardsCount] = reward.name .. " x" .. reward.count
        else
            local item = player:addItem(reward.itemId, reward.count)
            if item then
                rewardsCount = rewardsCount + 1
                rewards[rewardsCount] = item:getName() .. " x" .. reward.count
            end
        end
    end
    player:sendTextMessage(MESSAGE_INFO_DESCR, "You have received the following reward(s): " .. table.concat(rewards, ', '))
end

local function checkDuplicates(players)
    local duplicateIps, duplicateAccounts = {}, {}
    for i = 1, #players do
        local player = players[i]
        local ip = player:getIp()
        if config.checkIP and (ip == 0 or (duplicateIps[ip] or 0) >= config.allowMCs) then
            return false
        end
        duplicateIps[ip] = (duplicateIps[ip] or 0) + 1
        local accountId = player:getAccountId()
        if config.checkAccount and (duplicateAccounts[accountId] or 0) >= config.allowMCs then
            return false
        end
        duplicateAccounts[accountId] = (duplicateAccounts[accountId] or 0) + 1
    end
    return true
end

local function rewardPlayers(interval)
    local players = Game.getPlayers()
    if not checkDuplicates(players) then
        return true
    end
    for i = 1, #players do
        local player = players[i]
        local seconds = math.max(player:getStorageValue(config.storageKey), 0) + math.ceil(interval/1000)
        if seconds >= config.seconds then
            player:setStorageValue(config.storageKey, 0)
            rewardPlayer(player)
        else
            player:setStorageValue(config.storageKey, seconds)
        end
    end
    return true
end

local onlineTimeRewards = GlobalEvent("onlineTimeRewards")

onlineTimeRewards.onThink = rewardPlayers
onlineTimeRewards:interval(config.eventInterval)
onlineTimeRewards:register()
Yes, revscript is exactly for that, so you can write modular code, and use any trick possible with Lua.
 
I'm wondering if Revscript is a good way to write modular code.
Yes it is but in an optimal way

Quick example; you are iterating over players table twice (unlike before)
What I would have done on that matter without overthinking, would be this:

Lua:
local function checkPlayerDuplicates(player, duplicates)
  -- # it is safe to store accountsIds and ip addresses on the same object
  -- # they'll never be on the same range

  local ip = player:getIp()
  local accountId = player:getAccountId()

  if config.checkIP and (ip == 0 or (duplicates[ip] or 0) >= config.allowMCs) then
      return false
  end
  duplicates[ip] = (duplicates[ip] or 0) + 1

  if config.checkAccount and (duplicates[accountId] or 0) >= config.allowMCs then
      return false
  end
  duplicates[accountId] = (duplicates[accountId] or 0) + 1

  return true
end

local function rewardPlayers(interval)
  local players = Game.getPlayers()
  local duplicates = {}
  for i = 1, #players do
      local player = players[i]
      if checkPlayerDuplicates(player, duplicates) then -- # table passed as reference
        local seconds = math.max(player:getStorageValue(config.storageKey), 0) + math.ceil(interval/1000)
        if seconds >= config.seconds then
            player:setStorageValue(config.storageKey, 0)
            rewardPlayer(player)
        else
            player:setStorageValue(config.storageKey, seconds)
        end
      end
  end
  return true
end
 
Yes, you simply need to change everything related to player.storage to player:getStorageValue and setStorageValue respectively.
Lua:
--๐“œ๐“ฒ๐“ต๐“ต๐“ฑ๐“ฒ๐“ธ๐“ป๐“ฎ ๐“‘๐“ฃ
local config = {
    seconds = 3600, -- 1 hour
    eventInterval = 1000, -- 1 second
    checkIP = true,
    checkAccount = true,
    allowMCs = 3,
    storageKey = 73106,
    rewards = {
        { itemId = 2160, count = 100 },
        { name = "premium points", itemDb = 'accounts', value = 'premium_points', count = 100 }
    }
}

local onlineTimeRewards = GlobalEvent("onlineTimeRewards")

function onlineTimeRewards.onThink(interval)
    local duplicateIps = {}
    local duplicateAccounts = {}
    for _, player in pairs(Game.getPlayers()) do repeat
        local ip = player:getIp()
        if config.checkIP and ip == 0 or (duplicateIps[ip] or 0) >= config.allowMCs then
            break
        end
        duplicateIps[ip] = duplicateIps[ip] and duplicateIps[ip] + 1 or 1
        local accountId = player:getAccountId()
        if config.checkAccount and (duplicateAccounts[accountId] or 0) >= config.allowMCs then
            break
        end
        duplicateAccounts[accountId] = duplicateAccounts[accountId] and duplicateAccounts[accountId] + 1 or 1
        local seconds = math.max(player:getStorageValue(config.storageKey), 0) + math.ceil(interval/1000)
        if seconds >= config.seconds then
            player:setStorageValue(config.storageKey, 0)
            local rewards = {}
            for _, reward in pairs(config.rewards) do
                if reward.itemDb then
                    if db.query(string.format("UPDATE `%s` SET `%s` = `%s` + %d WHERE `id` = %d", reward.itemDb, reward.value, reward.value, reward.count, accountId)) then
                        rewards[#rewards + 1] = string.format('%s x%d', reward.name, reward.count)
                    else
                        print(string.format("[onlineTimeRewards] Error while rewarding player %s.", player:getName()))
                    end
                else
                    local item = player:addItem(reward.itemId, reward.count)
                    if item then
                        rewards[#rewards + 1] = string.format('%s x%d', item:getName(), reward.count)
                    end
                end
            end
            player:sendTextMessage(MESSAGE_INFO_DESCR, "You have received the following reward(s): " .. table.concat(rewards, ', '))
            break
        end
        player:setStorageValue(config.storageKey, seconds)
    until true end
    return true
end

onlineTimeRewards:interval(config.eventInterval)
onlineTimeRewards:register()
Is it difficult to make sure that if there are more than the allowed limit of mc connected, none of them receive the reward? and you get a message that you will not receive a prize because you have more than 3 connected
 
Back
Top