• 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!

VIP System [The Forgotten Server 1.0]

Summ

(\/)(;,,;)(\/) Y not?
Staff member
Global Moderator
Joined
Oct 15, 2008
Messages
4,152
Solutions
12
Reaction score
1,107
Location
Germany :O
Credits & Stuff
Credits go to @Printer for creating the original system (http://otland.net/threads/vip-system-tfs-1-1.224758/) upon which this system is based and which motivated me to work on it. Thanks for that.

This version uses a global variable to look up the current vip days of a player, so there is no need to use a mysql query each time you check if a player has vip status (which I assume is the most used function of the system).
I added support for infinite vip time and a talkaction for staff members.
If you miss any basic feature which might be helpful feel free to request it.
This version is supposed to work on The Forgotten Server 1.0 which you can download at https://github.com/otland/forgottenserver/releases.
I think I tested every case of the system if you however find any bugs / issues report them please or I might have messed up some copy & pasting.



System
MySQL queries
- execute those in your database
Code:
ALTER TABLE `accounts`
        ADD COLUMN `viplastday` int(10) NOT NULL DEFAULT 0 AFTER `lastday`,
        ADD COLUMN `vipdays` int(11) NOT NULL DEFAULT 0 AFTER `lastday`;

login.lua
- this file is located in data/creaturescripts/scripts/ folder
- add the following code after local player = Player(cid)
Code:
    player:loadVipData()
    player:updateVipTime()

global.lua
- this file is located in data/ folder
- add this code below dofile('data/compat.lua')
Code:
dofile('data/vip-system.lua')

vip-system.lua
- create this file in your data folder
- !Note: You have to adjust the following lines to your needs:
Code:
    -- true = player will be teleported to this position if Vip runs out
    -- false = player will not be teleported
    useTeleport = true,
    expirationPosition = Position(95, 114, 7),

    -- true = player will received the message you set
    -- false = player will not receive a message
    useMessage = true,
    expirationMessage = 'Your vip days ran out.',
    expirationMessageType = MESSAGE_STATUS_WARNING
- paste the following code in the newly created file
Code:
--[[

# Vip System for The Forgotten Server 1.0
# https://github.com/otland/forgottenserver/releases (1.0)

# Vip System by Summ
## Version v0.2
## Link: http://otland.net/threads/vip-system-the-forgotten-server-1-0.224910/

# Credits to Printer upon whose script this is based
## Link: http://otland.net/threads/vip-system-tfs-1-1.224758/

# MySQL query
    ALTER TABLE `accounts`
        ADD COLUMN `viplastday` int(10) NOT NULL DEFAULT 0 AFTER `lastday`,
        ADD COLUMN `vipdays` int(11) NOT NULL DEFAULT 0 AFTER `lastday`;

]]

local config = {
    -- true = player will be teleported to this position if Vip runs out
    -- false = player will not be teleported
    useTeleport = true,
    expirationPosition = Position(95, 114, 7),

    -- true = player will received the message you set
    -- false = player will not receive a message
    useMessage = true,
    expirationMessage = 'Your vip days ran out.',
    expirationMessageType = MESSAGE_STATUS_WARNING
}

if not VipData then
    VipData = { }
end

function Player.onRemoveVip(self)
    if config.useTeleport then
        self:teleportTo(config.expirationPosition)
        config.expirationPosition:sendMagicEffect(CONST_ME_TELEPORT)
    end

    if config.useMessage then
        self:sendTextMessage(config.expirationMessageType, config.expirationMessage)
    end
end

function Player.getVipDays(self)
    return VipData[self:getId()].days
end

function Player.getLastVipDay(self)
    return VipData[self:getId()].lastDay
end

function Player.isVip(self)
    return self:getVipDays() > 0
end

function Player.addInfiniteVip(self)
    local data = VipData[self:getId()]
    data.days = 0xFFFF
    data.lastDay = 0

    db.query(string.format('UPDATE `accounts` SET `vipdays` = %i, `viplastday` = %i WHERE `id` = %i;', 0xFFFF, 0, self:getAccountId()))
end

function Player.addVipDays(self, amount)
    local data = VipData[self:getId()]
    local amount = math.min(0xFFFE - data.days, amount)
    if amount > 0 then
        if data.days == 0 then
            local time = os.time()
            db.query(string.format('UPDATE `accounts` SET `vipdays` = `vipdays` + %i, `viplastday` = %i WHERE `id` = %i;', amount, time, self:getAccountId()))
            data.lastDay = time
        else
            db.query(string.format('UPDATE `accounts` SET `vipdays` = `vipdays` + %i WHERE `id` = %i;', amount, self:getAccountId()))
        end
        data.days = data.days + amount
    end

    return true
end

function Player.removeVipDays(self, amount)
    local data = VipData[self:getId()]
    if data.days == 0xFFFF then
        return false
    end

    local amount = math.min(data.days, amount)
    if amount > 0 then
        db.query(string.format('UPDATE `accounts` SET `vipdays` = `vipdays` - %i WHERE `id` = %i;', amount, self:getAccountId()))
        data.days = data.days - amount

        if data.days == 0 then
            self:onRemoveVip()
        end
    end

    return true
end

function Player.removeVip(self)
    local data = VipData[self:getId()]
    if data.days == 0 then
        return
    end

    data.days = 0
    data.lastDay = 0

    self:onRemoveVip()

    db.query(string.format('UPDATE `accounts` SET `vipdays` = 0, `viplastday` = 0 WHERE `id` = %i;', self:getAccountId()))
end

function Player.loadVipData(self)
    local resultId = db.storeQuery(string.format('SELECT `vipdays`, `viplastday` FROM `accounts` WHERE `id` = %i;', self:getAccountId()))
    if resultId then
        VipData[self:getId()] = {
            days = result.getDataInt(resultId, 'vipdays'),
            lastDay = result.getDataInt(resultId, 'viplastday')
        }

        result.free(resultId)
        return true
    end

    VipData[self:getId()] = { days = 0, lastDay = 0 }
    return false
end

function Player.updateVipTime(self)
    local save = false

    local data = VipData[self:getId()]
    local days, lastDay = data.days, data.lastDay
    local daysBefore = days
    if days == 0 or days == 0xFFFF then
        if lastDay ~= 0 then
            lastDay = 0
            save = true
        end
    elseif lastDay == 0 then
        lastDay = os.time()
        save = true
    else
        local time = os.time()
        local elapsedDays = math.floor((time - lastDay) / 86400)
        if elapsedDays > 0 then
            if elapsedDays >= days then
                days = 0
                lastDay = 0
            else
                days = days - elapsedDays
                lastDay = time - ((time - lastDay) % 86400)
            end
            save = true
        end
    end

    if save then
        if daysBefore > 0 and days == 0 then
            self:onRemoveVip()
        end

        db.query(string.format('UPDATE `accounts` SET `vipdays` = %i, `viplastday` = %i WHERE `id` = %i;', days, lastDay, self:getAccountId()))
        data.days = days
        data.lastDay = lastDay
    end
end
 
Last edited:
Extensions

Talkaction (!checkvip for all players)
Usage:
- !checkvip
--> Shows the user the amount of vip days he has left.

talkactions.xml
- the file is located in data/talkactions/
- add the following code below <talkactions>
Code:
    <talkaction words="!checkvip" separator=" " script="checkvip.lua"/>

checkvip.lua
- create this file in data/talkactions/scripts/
- paste the follow code into the newly created file
Code:
function onSay(cid, words, param)
    local player = Player(cid)

    local days = player:getVipDays()
    if days == 0 then
        player:sendCancelMessage('You do not have any vip days.')
    else
        player:sendCancelMessage(string.format('You have %s vip day%s left.', (days == 0xFFFF and 'infinite amount of' or days), (days == 1 and '' or 's')))
    end
    return false
end

Talkaction (/vip command for staff members)
Usage:
- /vip adddays, PlayerName, 5
--> Adds 5 vip days to PlayerName.
- /vip removedays, PlayerName, 5
--> Removes 5 vip days from PlayerName.
- /vip remove, PlayerName
--> Remove all vip days from PlayerName.
- /vip check, PlayerName
--> Check how many vip days PlayerName has.
- /vip addinfinite, PlayerName
--> Add infinite vip time to PlayerName.

talkactions.xml
- the file is located in data/talkactions/
- add the following code below <talkactions>
Code:
    <talkaction words="/vip" separator=" " script="vipcommand.lua" />

vipcommand.lua
- create this file in data/talkactions/scripts/
- paste the follow code into the newly created file
Code:
function onSay(cid, words, param)
    local player = Player(cid)
    if not player:getGroup():getAccess() then
        return true
    end

    local params = param:split(',')
    if not params[2] then
        player:sendTextMessage(MESSAGE_INFO_DESCR, string.format('Player is required.\nUsage:\n%s <action>, <name>, [, <value>]\n\nAvailable actions:\ncheck, adddays, addinfinite, removedays, remove', words))
        return false
    end

    local targetName = params[2]:trim()
    local target = Player(targetName)
    if not target then
        player:sendCancelMessage(string.format('Player (%s) is not online. Usage: %s <action>, <player> [, <value>]', targetName, words))
        return false
    end

    local action = params[1]:trim():lower()
    if action == 'adddays' then
        local amount = tonumber(params[3])
        if not amount then
            player:sendCancelMessage('<value> has to be a numeric value.')
            return false
        end

        target:addVipDays(amount)
        player:sendCancelMessage(string.format('%s received %s vip day(s) and now has %s vip day(s).', target:getName(), amount, target:getVipDays()))

    elseif action == 'removedays' then
        local amount = tonumber(params[3])
        if not amount then
            player:sendCancelMessage('<value> has to be a numeric value.')
            return false
        end

        target:removeVipDays(amount)
        player:sendCancelMessage(string.format('%s lost %s vip day(s) and now has %s vip day(s).', target:getName(), amount, target:getVipDays()))

    elseif action == 'addinfinite' then
        target:addInfiniteVip()
        player:sendCancelMessage(string.format('%s now has infinite vip time.', target:getName()))

    elseif action == 'remove' then
        target:removeVip()
        player:sendCancelMessage(string.format('You removed all vip days from %s.', target:getName()))

    elseif action == 'check' then
        local days = target:getVipDays()
        player:sendCancelMessage(string.format('%s has %s vip day(s).', target:getName(), (days == 0xFFFF and 'infinite' or days)))

    else
        player:sendTextMessage(MESSAGE_INFO_DESCR, string.format('Action is required.\nUsage:\n%s <action>, <name>, [, <value>]\n\nAvailable actions:\ncheck, adddays, addinfinite, removedays, remove', words))
    end
    return false
end


VIP Tiles
There are 2 versions of vip tiles.
ActionId 1500 will not let players without vip days pass.
- You can use these in a path where only vip player should be able to go through.
ActionId 1501 will not let players without vip days enter, while it will teleport vip players to a defined position.
- You can use these as the entrance to a vip-only area for example.

movements.xml
- the file is located in data/movements/
- add the following code after <movements>
Code:
    <movevent event="StepIn" actionid="1500" script="viptiles.lua"/>
    <movevent event="StepIn" actionid="1501" script="viptiles.lua"/>

viptiles.lua
- create this file in data/movements/scripts/
- add the following code to the newly created file
- Note! You must edit Position(101, 116, 7) to your vip area position if using actionid 1501
Code:
local vipPosition = Position(101, 116, 7)

function onStepIn(cid, item, position, fromPosition)
    local player = Player(cid)
    if not player then
        return true
    end

    if item.actionid == 1500 then
        if not player:isVip() then
            player:teleportTo(fromPosition)
            fromPosition:sendMagicEffect(CONST_ME_POFF)
            player:sendCancelMessage('You do not have any vip days.')
        end
    elseif item.actionid == 1501 then
        if player:isVip() then
            player:teleportTo(vipPosition)
            player:say('!* VIP *!', TALKTYPE_MONSTER_SAY)
            vipPosition:sendMagicEffect(CONST_ME_STUN)
        else
            player:teleportTo(fromPosition)
            player:sendCancelMessage('You do not have any vip days.')
            fromPosition:sendMagicEffect(CONST_ME_POFF)
        end
    end
    return true
end


VIP Doors / Actions
There are 3 versions.
ActionId 1502 should be used on these doors:
(It will teleport the player from below to above the door and vice versa)
HDBGdcK.png


ActionId 1502 should be used on these doors:
(It will teleport the player from the left to the right of the door and vice versa)
Xq39d8e.png

ActionId 1503 will teleport the player to a position you defined.
- This can be used as an entrance to your vip area for example.

actions.xml

- the file is located in data/actions/
- add the following code after <actions>
Code:
    <action actionid="1502" script="vipdoors.lua"/>
    <action actionid="1503" script="vipdoors.lua"/>
    <action actionid="1504" script="vipdoors.lua"/>

vipdoors.lua
- create this file in data/actions/scripts/
- add the following code to the newly created file
- Note! You must edit Position(101, 116, 7) to your vip area position if using actionid 1503.
Code:
local vipPosition = Position(101, 116, 7)

function onUse(cid, item, fromPosition, itemEx, toPosition, isHotkey)
    local player = Player(cid)
    if item.actionid == 1502 then
        local position = player:getPosition()
        if position.y < fromPosition.y then
            fromPosition.y = fromPosition.y + 1
        else
            fromPosition.y = fromPosition.y - 1
        end
        player:teleportTo(fromPosition)
        player:say('!* VIP *!', TALKTYPE_MONSTER_SAY)
        fromPosition:sendMagicEffect(CONST_ME_STUN)

    elseif item.actionid == 1503 then
        local position = player:getPosition()
        if position.x < fromPosition.x then
            fromPosition.x = fromPosition.x + 1
        else
            fromPosition.x = fromPosition.x - 1
        end
        player:teleportTo(fromPosition)
        player:say('!* VIP *!', TALKTYPE_MONSTER_SAY)
        fromPosition:sendMagicEffect(CONST_ME_STUN)

    elseif item.actionid == 1504 then
        if player:isVip() then
            player:teleportTo(vipPosition)
            player:say('!* VIP *!', TALKTYPE_MONSTER_SAY)
            vipPosition:sendMagicEffect(CONST_ME_STUN)
        else
            player:sendCancelMessage('You do not have any vip days.')
        end
    end
    return true
end


VIP Items
Player use these items and get vip days.
ItemId 10135 adds 10 vip days.
ItemId 10134 adds 30 vip days.
ItemId 10133 adds 90 vip days.

actions.xml

- the file is located in data/actions/
- add the following code after <actions>
Code:
    <action fromid="10133" toid="10135" script="vipitems.lua"/>

vipitems.lua
- create this file in data/actions/scripts/
- add the following code to the newly created file
- Note! You can easily edit the amount of vip days a player gains on line 2-4 of the script.
Code:
local vipItems = {
   -- [itemid] = amount of vip days
    [10135] = 10,
    [10134] = 30,
    [10133] = 90
}

function onUse(cid, item, fromPosition, itemEx, toPosition, isHotkey)
    local player = Player(cid)
    local days = vipItems[item.itemid]
    player:addVipDays(days)
    player:say('!* YAY VIP! *!', TALKTYPE_MONSTER_SAY)
    player:getPosition():sendMagicEffect(CONST_ME_STUN)
    player:sendTextMessage(MESSAGE_INFO_DESCR, string.format('You received %s vip days.', days))
    Item(item.uid):remove(1)
    return true
end
 
Last edited:
Nice work but I have little problem. This is my login.lua and where I must add your line :

Code:
-- ordered as in creaturescripts.xml
local events = {
    'TutorialCockroach',
    'ElementalSpheresOverlords',
    'BigfootBurdenVersperoth',
    'BigfootBurdenWarzone',
    'BigfootBurdenWeeper',
    'BigfootBurdenWiggler',
    'SvargrondArenaKill',
    'NewFrontierShardOfCorruption',
    'NewFrontierTirecz',
    'ServiceOfYalaharDiseasedTrio',
    'ServiceOfYalaharAzerus',
    'ServiceOfYalaharQuaraLeaders',
    'InquisitionBosses',
    'InquisitionUngreez',
    'KillingInTheNameOfKills',
    'MastersVoiceServants',
    'PharaoKillPortal',
    'SecretServiceBlackKnight',
    'ThievesGuildNomad',
    'WotELizardMagistratus',
    'WotELizardNoble',
    'WotEKeeper',
    'WotEBosses',
    'WotEZalamon',
    'PlayerDeath',
    'AdvanceSave',
}

function onLogin(player)
    local loginStr = 'Welcome to ' .. configManager.getString(configKeys.SERVER_NAME) .. '!'
    if player:getLastLoginSaved() <= 0 then
        loginStr = loginStr .. ' Please choose your outfit.'
        player:sendTutorial(1)
    else
        if loginStr ~= '' then
            player:sendTextMessage(MESSAGE_STATUS_DEFAULT, loginStr)
        end

        loginStr = string.format('Your last visit was on %s.', os.date('%a %b %d %X %Y', player:getLastLoginSaved()))
    end
    player:sendTextMessage(MESSAGE_STATUS_DEFAULT, loginStr)

    for i = 1, #events do
        player:registerEvent(events[i])
    end
    return true
end
 
Its all ok but i have 1 question. Player have 0 days in vip. no must teleport him in temple or simple location ??

Can u add some comands in player ?? !checkvip or other :) thx
 
Last edited:
You can add it after function onLogin(player).
refresh

And how i can add this show my Gesior Acc becouse him have only storage id cid but this vip system have tables :)
 
Last edited:
I will help you tomorrow evening when I get back to my PC.
 
Updated to v0.2
- !checkvip command is now available
- You can set if the player should be teleported somewhere if he loses all vip days now
- You can set if the player should receive a message if he loses all vip days now
In order to access theses changes you have to update your vip-system.lua and follow the instructions on how to add !checkvip command

@CyCu91
In login.lua add:
Code:
    player:setStorageValue(9999, player:getVipDays())
below:
Code:
   player:updateVipTime()
!! Change 9999 to the storage Gesior is using, you can then access the amount of vip days in the storage value.
 
managed to solve the error that was giving,
excellent system
 
Last edited:
Updated to v0.2
- !checkvip command is now available
- You can set if the player should be teleported somewhere if he loses all vip days now
- You can set if the player should receive a message if he loses all vip days now
In order to access theses changes you have to update your vip-system.lua and follow the instructions on how to add !checkvip command

@CyCu91
In login.lua add:
Code:
    player:setStorageValue(9999, player:getVipDays())
below:
Code:
   player:updateVipTime()
!! Change 9999 to the storage Gesior is using, you can then access the amount of vip days in the storage value.


1. Comand !checkvip is work thx
2. I tested now teleport in out days vip
3. Gesior - VIP ON THX
 
Last edited:
where do i see what gesior is using for storage value for the vip? =)
 
After adding this system I am getting this error:
Code:
Lua Script Error: [Event Interface]
data/events/scripts/player.lua:Player@onGainExperience
data/events/scripts/player.lua:162: attempt to perform arithmetic on a nil value
stack traceback:
        [C]: in function '__sub'
        data/events/scripts/player.lua:162: in function 'useStamina'
        data/events/scripts/player.lua:198: in function <data/events/scripts/player.lua:181>

Using tfs 1.1, newest rev.

I haven't done changed to player.lua, therefore it should work?
Code:
  if configManager.getBoolean(configKeys.STAMINA_SYSTEM) then
        useStamina(self)

        local staminaMinutes = self:getStamina()
        if staminaMinutes > 2400 then
            exp = exp * 1.5
        elseif staminaMinutes <= 840 then
            exp = exp * 0.5
        end
    end

Code:
local staminaTable = Game.getStorageValue("stamina")
    local timePassed = currentTime - staminaTable[playerId]
    if timePassed <= 0 then
        return
    end

    if timePassed > 60 then
        if staminaMinutes > 2 then
            staminaMinutes = staminaMinutes - 2
        else
            staminaMinutes = 0
        end
        staminaTable[playerId] = currentTime + 120
    else
        staminaMinutes = staminaMinutes - 1
        staminaTable[playerId] = currentTime + 60
    end
    player:setStamina(staminaMinutes)

"Stamina" table seems to become nil after reloading global.

Well, I haven't restarted the server, but shouldn't reload command be enough? ;o
 
Not related to this system, your stamina system is not safe against reloading.
Make sure to check if it already exists in global.lua

Code:
if not staminaTable then
    staminaTable = {}
end
 
Not related to this system, your stamina system is not safe against reloading.
Make sure to check if it already exists in global.lua

Code:
if not staminaTable then
    staminaTable = {}
end
Alright, thanks :)

Code:
Lua Script Error: [Action Interface]
data/actions/scripts/other/exclusiveClub.lua:onUse
data/lib/vip-system.lua:70: attempt to index local 'data' (a nil value)
stack traceback:
        [C]: in function '__index'
        data/lib/vip-system.lua:70: in function 'addVipDays'
        data/actions/scripts/other/exclusiveClub.lua:10: in function <data/actions/scripts/other/exclusiveClub.lua:8>

This is embarrassing, forgot to add
player:loadVipData()
player:updateVipTime()

to login.lua.
 
Back
Top