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

7.1 quest system? Item spawn only once

al222

Member
Joined
Sep 18, 2019
Messages
81
Reaction score
18
Hello,

I would like to implement an old school quest system like it was in 7.1, e.g. No quest chests. For example I would like to replace Bright sword quest with horned helmet, but make it so that it can only be obtained once. Not once per player. Not once per server save. Once period. Any ideas on how to accomplish this?

Thanks!
 
Use refreshable tiles, like Cip did. Tiles that had "refresh" flag were always refreshed during SS plus a few times a day, while tiles without that flag were never refreshed, not even during SS. It could only be refreshed when Cip loaded a new map.
You can read my article about that matter here: Tibiantis (https://tibiantis.online/forum/viewtopic.php?id=83)
 
Use refreshable tiles, like Cip did. Tiles that had "refresh" flag were always refreshed during SS plus a few times a day, while tiles without that flag were never refreshed, not even during SS. It could only be refreshed when Cip loaded a new map.
You can read my article about that matter here: Tibiantis (https://tibiantis.online/forum/viewtopic.php?id=83)

Thanks! Doesn't seem to work in Nostalrius however. Both refresh and non-refresh tiles seem to get cleaned at server save or by /closeserver shutdown-command. Any suggestions?
 
Thanks! Doesn't seem to work in Nostalrius however. Both refresh and non-refresh tiles seem to get cleaned at server save or by /closeserver shutdown-command. Any suggestions?

As far as I remember, Nostalrius map does have refresh tiles (special tiles?) converted from real tibia 7.7 map, so that's good to start with. But ofc it doesn't have the system I described and I don't think any other distro has. You'd have to write it yourself.
 
As far as I remember, Nostalrius map does have refresh tiles (special tiles?) converted from real tibia 7.7 map, so that's good to start with. But ofc it doesn't have the system I described and I don't think any other distro has. You'd have to write it yourself.
Yeah, there are refresh tiles in the Nostalrius map and you can place them in the RME for Nostalrius. I just assumed they would work without adding any code, silly me :p

Writing something like that seems a bit daunting. I'm having a lot of difficulty understanding the source code since there are hardly any comments at all. So I was hoping for more of a shortcut way to add the functionality. Is there any way of just saving a boolean somewhere when an item is removed and then reading that value when the map has loaded and having a condition on whether or not the item should be there?
Post automatically merged:

...or could you use house tiles somehow? Since those seem to save items just fine.
 
Yea maybe you could try with a house-tile trick. Just make sure that square is non-walkable, so players don't get confused with "you are not invited" message or you don't mess up with PZ/monsters (not sure how it is coded there, but perhaps monsters are unable to enter houses even if the tile is not PZ).
You could make a chest that's immoble and impossible to destroy, then put rewards inside and set that tile as house one. @al222
 
IF gonna make a server like that, please maintain vanilla 7.1 style as possible, no mc, no tasks, and no new nothing, just a little fresh vanilla style where ppl can take their time to do things and not rush rush rush ruhs rush rush rush and more content content content content
 
IF gonna make a server like that, please maintain vanilla 7.1 style as possible, no mc, no tasks, and no new nothing, just a little fresh vanilla style where ppl can take their time to do things and not rush rush rush ruhs rush rush rush and more content content content content
That's my goal. Although I have no plans or desire to host a public server, I'm just making it because I want to play the game I remember. Will probably share it though if someone has any interest in it.
 
Hello,

I would like to implement an old school quest system like it was in 7.1, e.g. No quest chests. For example I would like to replace Bright sword quest with horned helmet, but make it so that it can only be obtained once. Not once per player. Not once per server save. Once period. Any ideas on how to accomplish this?

Thanks!
I would use a globalstorage.

chests
Lua:
function onUse(cid, item, fromPosition, itemEx, toPosition)
    if getGlobalStorageValue(45001) < 1 then
        -- do stuff
        
        -- successful
        setGlobalStorageValue(45001, 1)
    else
        -- sorry only 1 of this item.
    end
    return true
end

items on ground.
Lua:
local items_on_ground = {
    {45001, 3878, {1000, 1000, 7}},
    {45002, 3878, {1000, 1000, 7}},
    {45003, 3878, {1000, 1000, 7}}
}

function onThink(cid, interval)
    for i = 1, #items_on_ground do
        if getGlobalStorageValue(items_on_ground[i][1]) < 1 then
            if getTileItemById(items_on_ground[i][3], items_on_ground[i][2]).uid > 0 then
                setGlobalStorageValue(items_on_ground[i][1], 1)
            end
        end
    end
    return true
end
 
I would use a globalstorage.

Thanks!

chests
Lua:
function onUse(cid, item, fromPosition, itemEx, toPosition)
    if getGlobalStorageValue(45001) < 1 then
        -- do stuff
       
        -- successful
        setGlobalStorageValue(45001, 1)
    else
        -- sorry only 1 of this item.
    end
    return true
end

This would work like a "normal" quest chest right? At -- do stuff I would put code to place an item in the player's inventory?

What I would like is a normal dumb chest that is a container so that the player can open it and find an item. Only I don't want the I place there to reappear at next server launch if someone has removed it. So I guess I would need some sort of onMove function for the item in a normal chest? Any ideas?

items on ground.
Lua:
local items_on_ground = {
    {45001, 3878, {1000, 1000, 7}},
    {45002, 3878, {1000, 1000, 7}},
    {45003, 3878, {1000, 1000, 7}}
}

function onThink(cid, interval)
    for i = 1, #items_on_ground do
        if getGlobalStorageValue(items_on_ground[i][1]) < 1 then
            if getTileItemById(items_on_ground[i][3], items_on_ground[i][2]).uid > 0 then
                setGlobalStorageValue(items_on_ground[i][1], 1)
            end
        end
    end
    return true
end

Where would I put this code?
 
Thanks!
This would work like a "normal" quest chest right? At -- do stuff I would put code to place an item in the player's inventory?

Yep! Just treat it like a normal chest, but instead of using a playerstorage, you use the globalstorage.

What I would like is a normal dumb chest that is a container so that the player can open it and find an item. Only I don't want the I place there to reappear at next server launch if someone has removed it. So I guess I would need some sort of onMove function for the item in a normal chest? Any ideas?

Well, inside a chest is a bit different, since you'll need to check the container to see if it contains the specific item..
But otherwise it's the same principle as before, where you check the globalstorage to see if it should be there or not.

For creating the items we do that with an onStartUp script using the same table as the onThink script, but instead of checking whether the item exists, you check if the item should be spawned in.

like this..
XML:
<globalevent name="single_use_items" type="startup" event="script" value="single_use_items.lua"/>
Lua:
local items_on_ground = {
    {45001, 3878, {x = 1000, y = 1000, z = 7}},
    {45002, 3878, {x = 1000, y = 1000, z = 7}},
    {45003, 3878, {x = 1000, y = 1000, z = 7}}
}

function onStartup()
    for i = 1, #items_on_ground do
        if getGlobalStorageValue(items_on_ground[i][1]) < 1 then
            doCreateItem(items_on_ground[i][2], 1, items_on_ground[i][3])
        end
    end
    return true
end

function onThink(cid, interval)
    for i = 1, #items_on_ground do
        if getGlobalStorageValue(items_on_ground[i][1]) < 1 then
            if getTileItemById(items_on_ground[i][3], items_on_ground[i][2]).uid > 0 then
                setGlobalStorageValue(items_on_ground[i][1], 1)
            end
        end
    end
    return true
end

Where would I put this code?
If you're using 0.4, this would go into data/globalevents/scripts
and inside of data/globalevents/globalevents.xml you'd choose an interval to check with

for example, every 15 minutes...
XML:
<globalevent name="save" interval="900000" event="script" value="save.lua"/>

Realistically if we are spawning the items in with onStartup() we could create an addEvent to lessen the server load and ditch the onThink altogether.
But meh. 🤷‍♂️
 
Yep! Just treat it like a normal chest, but instead of using a playerstorage, you use the globalstorage.



Well, inside a chest is a bit different, since you'll need to check the container to see if it contains the specific item..
But otherwise it's the same principle as before, where you check the globalstorage to see if it should be there or not.

For creating the items we do that with an onStartUp script using the same table as the onThink script, but instead of checking whether the item exists, you check if the item should be spawned in.

like this..
XML:
<globalevent name="single_use_items" type="startup" event="script" value="single_use_items.lua"/>
Lua:
local items_on_ground = {
    {45001, 3878, {x = 1000, y = 1000, z = 7}},
    {45002, 3878, {x = 1000, y = 1000, z = 7}},
    {45003, 3878, {x = 1000, y = 1000, z = 7}}
}

function onStartup()
    for i = 1, #items_on_ground do
        if getGlobalStorageValue(items_on_ground[i][1]) < 1 then
            doCreateItem(items_on_ground[i][2], 1, items_on_ground[i][3])
        end
    end
    return true
end

function onThink(cid, interval)
    for i = 1, #items_on_ground do
        if getGlobalStorageValue(items_on_ground[i][1]) < 1 then
            if getTileItemById(items_on_ground[i][3], items_on_ground[i][2]).uid > 0 then
                setGlobalStorageValue(items_on_ground[i][1], 1)
            end
        end
    end
    return true
end


If you're using 0.4, this would go into data/globalevents/scripts
and inside of data/globalevents/globalevents.xml you'd choose an interval to check with

for example, every 15 minutes...
XML:
<globalevent name="save" interval="900000" event="script" value="save.lua"/>

Realistically if we are spawning the items in with onStartup() we could create an addEvent to lessen the server load and ditch the onThink altogether.
But meh. 🤷‍♂️

Thanks! Starting to get the hang of it now.. :) I'm doing a slightly different approach: I've marked each quest chest location on the map with a unique id, then on startup I'll add a chest empty or with items depending on the global storage. Then I use a movement script onRemoveItem to set the global storage.

Everything seems to be working, except that I can't seem to set global storage(!)
I call it like this:
Lua:
setGlobalStorageValue(questItems[i][1],1)
where
Lua:
questItems = {
    -- unique storage id, item id(s), position
  {24001, {3065, 3061}, {x = 32357, y = 31763, z = 7}},
}
I can print from where setGlobalStorageValue is called, so I know it gets there at the right time. And the questItems value reads correctly to 24001. But when I read the value with my startup script, getGlobalStorageValue returns -1 for 24001.

I noticed that the get/setGlobalStorageValue only calls Game.getStorageValue (this returns nil for me, if I call it directly) and Game.setStorageValue, which are defined in game.lua, where they use globalStorageTable. When I search my project for globalStorageTable, these are the only references I find (all in game.lua):

Lua:
if not globalStorageTable then
    globalStorageTable = {}
end

function Game.getStorageValue(key)
    return globalStorageTable[key]
end

function Game.setStorageValue(key, value)
    globalStorageTable[key] = value
end

That doesn't seem like it will work, right?
 
If I understand everything correct, I think you are putting what we have 2 systems of, 1 is just a "1 time a server" chest, and the other is a "percent to respawn type of prize. I am not sure if this will work for you, but this is what we use for 1 time per server chest:

Lua:
function onUse(cid, item, fromPosition, itemEx, toPosition)  
   
        local player = Player(cid)      
        if getEternalStorageValue(77889) <= 0 then
            doPlayerAddItem(cid,itemnumberofitem,1)
            setEternalStorageValue(77889, 1)
            broadcastMessage("The Rare Item has been found! Congratulations!", MESSAGE_STATUS_CONSOLE_ORANGE)  
            setPlayerStorageValue(cid, 152, 1)
         else
            doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"Sorry, someone already completed the quest and got the reward.")
        end
   
    return true
end

The other normal storage is for to show in the players quest log that he is the one who got it.
Of course that can be changed to be just a thing, and activate by movement, etc.

The other one is where the item will respawn each day until taken, then after that, there has a chance every server save to respawn again, and you could set the percent to 0, but also if you decide you do want a "second" of the item for players to take, you could set the percent to 100 for a server save. In the lib, we add:

Code:
EPICQUESTS = {
   
    {30096,1534,1412,5,2361,2.5,1548}, -- storage,pos.x,pos.y,pos.z,item,% chance of respaw, blocking item

of all your quests, then in start up:

Lua:
for i, quest in ipairs(EPICQUESTS) do
        if getEternalStorageValue(quest[1]) ~= 1 then
            Game.createItem(quest[5], 1, Position(quest[2], quest[3], quest[4]))
            print('>> - Epic Quest: ' .. getItemNameById(quest[5]) .. ' respawned.')
        else
           
            randomValue = math.random(100)
            if quest[6] >= randomValue then
                setEternalStorageValue(quest[1], 0)
                Game.createItem(quest[5], 1, Position(quest[2], quest[3], quest[4]))
                print('>> - Epic Quest: ' .. getItemNameById(quest[5]) .. ' respawned. Chance to spawn: ' .. quest[6] .. '%. RNG ' .. randomValue .. '/100.')
            else
                Game.createItem(quest[7], 1, Position(quest[2], quest[3], quest[4]))
                print('>> - Epic Quest: ' .. getItemNameById(quest[5]) .. ' won\'t respaw yet. Chance to spawn: ' .. quest[6] .. '%. RNG ' .. randomValue .. '/100.')
            end
        end
    end

Then in the movements of the item:

Lua:
function onRemoveItem(item, tile, position)
   
    gatepos = {x=612, y=367, z=8, stackpos=1}
    local lock1pos = {x=573, y=366, z=4}
    local lock2pos = {x=572, y=370, z=4}
    local lock3pos = {x=581, y=374, z=5}
   
    doSummonCreature("Behemoth",lock1pos)
    doSummonCreature("Behemoth",lock2pos)
    doSummonCreature("Behemoth",lock3pos)
    doCreateItem(1548,1,gatepos)
   
        if getEternalStorageValue(30096) ~= 1 then
                setEternalStorageValue(30096, 1)
               
       
    end
    broadcastMessage("The Rare Item has been found! Congratulations!", MESSAGE_STATUS_CONSOLE_ORANGE)

    return true
end

For this one, it also summons monsters when you get the prize, and the blocking item (1548) is needed because if not, you can put an object on the spot and move it, which will re trigger it all.

I might not fully understand what you are trying to do and what we use might not fully work, but I hope it can help to create some ideas to get success. Peonso and Kungen did the heavy thinking of this creation, so please thank them if this helps. Good luck!
 
If I understand everything correct, I think you are putting what we have 2 systems of, 1 is just a "1 time a server" chest, and the other is a "percent to respawn type of prize. I am not sure if this will work for you, but this is what we use for 1 time per server chest:

Lua:
function onUse(cid, item, fromPosition, itemEx, toPosition)
 
        local player = Player(cid)    
        if getEternalStorageValue(77889) <= 0 then
            doPlayerAddItem(cid,itemnumberofitem,1)
            setEternalStorageValue(77889, 1)
            broadcastMessage("The Rare Item has been found! Congratulations!", MESSAGE_STATUS_CONSOLE_ORANGE)
            setPlayerStorageValue(cid, 152, 1)
         else
            doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"Sorry, someone already completed the quest and got the reward.")
        end
 
    return true
end

The other normal storage is for to show in the players quest log that he is the one who got it.
Of course that can be changed to be just a thing, and activate by movement, etc.

The other one is where the item will respawn each day until taken, then after that, there has a chance every server save to respawn again, and you could set the percent to 0, but also if you decide you do want a "second" of the item for players to take, you could set the percent to 100 for a server save. In the lib, we add:

Code:
EPICQUESTS = {
 
    {30096,1534,1412,5,2361,2.5,1548}, -- storage,pos.x,pos.y,pos.z,item,% chance of respaw, blocking item

of all your quests, then in start up:

Lua:
for i, quest in ipairs(EPICQUESTS) do
        if getEternalStorageValue(quest[1]) ~= 1 then
            Game.createItem(quest[5], 1, Position(quest[2], quest[3], quest[4]))
            print('>> - Epic Quest: ' .. getItemNameById(quest[5]) .. ' respawned.')
        else
         
            randomValue = math.random(100)
            if quest[6] >= randomValue then
                setEternalStorageValue(quest[1], 0)
                Game.createItem(quest[5], 1, Position(quest[2], quest[3], quest[4]))
                print('>> - Epic Quest: ' .. getItemNameById(quest[5]) .. ' respawned. Chance to spawn: ' .. quest[6] .. '%. RNG ' .. randomValue .. '/100.')
            else
                Game.createItem(quest[7], 1, Position(quest[2], quest[3], quest[4]))
                print('>> - Epic Quest: ' .. getItemNameById(quest[5]) .. ' won\'t respaw yet. Chance to spawn: ' .. quest[6] .. '%. RNG ' .. randomValue .. '/100.')
            end
        end
    end

Then in the movements of the item:

Lua:
function onRemoveItem(item, tile, position)
 
    gatepos = {x=612, y=367, z=8, stackpos=1}
    local lock1pos = {x=573, y=366, z=4}
    local lock2pos = {x=572, y=370, z=4}
    local lock3pos = {x=581, y=374, z=5}
 
    doSummonCreature("Behemoth",lock1pos)
    doSummonCreature("Behemoth",lock2pos)
    doSummonCreature("Behemoth",lock3pos)
    doCreateItem(1548,1,gatepos)
 
        if getEternalStorageValue(30096) ~= 1 then
                setEternalStorageValue(30096, 1)
             
     
    end
    broadcastMessage("The Rare Item has been found! Congratulations!", MESSAGE_STATUS_CONSOLE_ORANGE)

    return true
end

For this one, it also summons monsters when you get the prize, and the blocking item (1548) is needed because if not, you can put an object on the spot and move it, which will re trigger it all.

I might not fully understand what you are trying to do and what we use might not fully work, but I hope it can help to create some ideas to get success. Peonso and Kungen did the heavy thinking of this creation, so please thank them if this helps. Good luck!

Thanks, but it's not quite what I'm looking for. I want it to work exactly like in 7.1 where you can actually open the chest and remove the items, and once you've done that, they don't respawn again (used to be until map reset).

I ended up making a table in the database and adding the desired spawns there to solve the storage problem. Here's my code if anyone else is interested. Global event:
Lua:
function onStartup()
    for i = 1, #questItems, 1 do

        local chest = nil

        if questItems[i][2] == 1 then
            -- Create a chest for each location that should have one
            chest = Game.createItem(2472, 1, questItems[i][4])
            end

        local resultId = db.storeQuery("SELECT completed FROM one_time_quests WHERE name = " .. "'".. questItems[i][1] .. "'")

        if resultId ~= false then
            local completed = result.getDataInt(resultId, "completed")

            if completed == 0 then

                if chest ~= nil then
                    -- Add items in chest
                    for j = #questItems[i][3], 1, -1 do
                        chest:addItem(questItems[i][3][j])
                        end

                else
                    -- Add item on ground
                    Game.createItem(questItems[i][3][1], 1, questItems[i][4])
                end
            end
        end
    end
end

Movement:
Lua:
questItems = {
    -- quest name, chest?, item id(s), position
  {"test_quest", 1, {3065, 3061}, {x = 32357, y = 31763, z = 7}},
    {"test_quest_2", 0, {3065}, {x = 32358, y = 31766, z = 7}},
}

function onRemoveItem(item, tileitem, position)

    for i = 1, #questItems, 1 do

        -- Find correct chest by position
        if tileitem:getPosition().x == questItems[i][4].x and
        tileitem:getPosition().y == questItems[i][4].y and
        tileitem:getPosition().z == questItems[i][4].z then

            db.query("UPDATE one_time_quests SET completed = 1 WHERE name = " .. "'".. questItems[i][1] .. "'")

            end
        end
end

That solves my problem. Thanks everyone who responded for your help and suggestions!
 
Back
Top