• 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!
  • 2026 staff recruitment is open! Check it out and consider applying!

NPC store conversation context

Paulix

Active Member
Joined
Sep 13, 2012
Messages
151
Solutions
8
Reaction score
36
I'm trying to build and npc that you'll be able to try and buy outfits from, the problem is that the talk sequence is a bit complex. At the moment, my behavior is working as espected, but I'm facing a major problem that I've no idea on how to solve...


LUA:
    message_table = msg:explode(" ")
    msg_action = message_table[1]
    msg_outfit = (message_table[2].." "..(message_table[3] or "").." "..(message_table[4] or "")):trim()

    if msgcontains(message_table[1], "buy") then
        if outfits[msg_outfit:lower()] ~= nil then
            npcHandler:say("Do you want to buy the " .. msg_outfit .. " outfit?\n{Base} skins without outfits is 10 coins\n{first} and {second} addon is 5 coins each\n{complete} skin + addons is 20 coins.", cid)
            npcHandler.topic[cid] = 1
        else
            npcHandler:say("I could not find that outfit, sorry.", cid)
        end
    end

The msg_outfit is working when the player says for the first time, the problem is that the npc should remember this info to use it later on the same conversation. I need to store topic to control the stage, but I also need to save the selected outfit.

How can I store this kind of information into a npc dialog, that it should keep it save in between messages, that is individual by players?
 
Solution
I'm trying to build and npc that you'll be able to try and buy outfits from, the problem is that the talk sequence is a bit complex. At the moment, my behavior is working as espected, but I'm facing a major problem that I've no idea on how to solve...


LUA:
    message_table = msg:explode(" ")
    msg_action = message_table[1]
    msg_outfit = (message_table[2].." "..(message_table[3] or "").." "..(message_table[4] or "")):trim()

    if msgcontains(message_table[1], "buy") then
        if outfits[msg_outfit:lower()] ~= nil then
            npcHandler:say("Do you want to buy the " .. msg_outfit .. " outfit?\n{Base} skins without outfits is 10 coins\n{first} and {second} addon is 5 coins each\n{complete} skin + addons is 20 coins."...
So i got downvoted because i dident provide you with a readable answer?

What “storage” is in TFS (conceptually)​


A storage is basically:




Think of it like a tiny database row attached to each character.


  • Key = a number (storage ID)
  • Value = a number (usually -1, 0, 1, 2, etc.)

Example:



<span><span>Storage 5000 = 1<br></span></span>

Means: this player has progressed something




Default behavior (IMPORTANT)​


If a storage was never set:




<span><span>player:getStorageValue(</span><span><span>5000</span></span><span>) == </span><span><span>-1</span></span><span><br></span></span>

So:


  • -1 = not started / never touched
  • 0+ = something has been set

This is why you’ll see == -1 checks everywhere.




Creating a storage (there is no “registration”)​


You do not create storages anywhere.


There is:


  • ❌ no database table
  • ❌ no XML
  • ❌ no config file

You just pick a number and use it.


Best practice: reserve ranges​


Common convention:




<span><span>1000–1999 = small quests<br>2000–2999 = main quests<br>3000–3999 = NPC states<br>50000+ = systems / global logic<br></span></span>

Example:




<span><span><span>local</span></span><span> STORAGE_MAIN_QUEST = </span><span><span>2000</span></span><span><br></span></span>



Reading a storage​




<span><span><span>local</span></span><span> value = player:getStorageValue(</span><span><span>2000</span></span><span>)<br></span></span>

Typical checks:




<span><span><span>if</span></span><span> player:getStorageValue(</span><span><span>2000</span></span><span>) == </span><span><span>-1</span></span><span> </span><span><span>then</span></span><span><br> </span><span><span>-- player has NOT started</span></span><span><br></span><span><span>end</span></span><span><br><br></span><span><span>if</span></span><span> player:getStorageValue(</span><span><span>2000</span></span><span>) &gt;= </span><span><span>1</span></span><span> </span><span><span>then</span></span><span><br> </span><span><span>-- player is in progress or finished</span></span><span><br></span><span><span>end</span></span><span><br></span></span>



Saving / setting a storage​


This is where it’s “saved”.




<span><span>player:setStorageValue(</span><span><span>2000</span></span><span>, </span><span><span>1</span></span><span>)<br></span></span>

That’s it.


TFS automatically:


  • stores it in the database
  • loads it when the player logs in
  • keeps it per character

You do not manually save anything.




Typical quest progression example​




<span><span><span>local</span></span><span> STORAGE_QUEST = </span><span><span>2000</span></span><span><br><br></span><span><span>if</span></span><span> player:getStorageValue(STORAGE_QUEST) == </span><span><span>-1</span></span><span> </span><span><span>then</span></span><span><br> player:sendTextMessage(MESSAGE_INFO_DESCR, </span><span><span>"Quest started!"</span></span><span>)<br> player:setStorageValue(STORAGE_QUEST, </span><span><span>1</span></span><span>)<br></span><span><span>elseif</span></span><span> player:getStorageValue(STORAGE_QUEST) == </span><span><span>1</span></span><span> </span><span><span>then</span></span><span><br> player:sendTextMessage(MESSAGE_INFO_DESCR, </span><span><span>"Quest completed!"</span></span><span>)<br> player:setStorageValue(STORAGE_QUEST, </span><span><span>2</span></span><span>)<br></span><span><span>end</span></span><span><br></span></span>

Progression:




<span><span>-1 = not started<br> 1 = started<br> 2 = completed<br></span></span>



Using storages with NPCs​


Very common pattern:




<span><span><span>if</span></span><span> player:getStorageValue(</span><span><span>3001</span></span><span>) &lt; </span><span><span>1</span></span><span> </span><span><span>then</span></span><span><br> npcHandler:say(</span><span><span>"Hello stranger."</span></span><span>, cid)<br></span><span><span>else</span></span><span><br> npcHandler:say(</span><span><span>"Welcome back, hero."</span></span><span>, cid)<br></span><span><span>end</span></span><span><br></span></span>

And when the player accepts something:




<span><span>player:setStorageValue(</span><span><span>3001</span></span><span>, </span><span><span>1</span></span><span>)<br></span></span>



Using storages with actions / movement scripts​


Action example (lever, chest, etc.)​




<span><span><span><span>function</span></span></span><span> </span><span><span>onUse</span></span><span><span>(player, item, fromPosition, target, toPosition, isHotkey)</span></span><span><br> </span><span><span>if</span></span><span> player:getStorageValue(</span><span><span>4000</span></span><span>) == </span><span><span>-1</span></span><span> </span><span><span>then</span></span><span><br> player:addItem(</span><span><span>2160</span></span><span>, </span><span><span>10</span></span><span>)<br> player:setStorageValue(</span><span><span>4000</span></span><span>, </span><span><span>1</span></span><span>)<br> player:sendTextMessage(MESSAGE_INFO_DESCR, </span><span><span>"You found a reward."</span></span><span>)<br> </span><span><span>else</span></span><span><br> player:sendTextMessage(MESSAGE_INFO_DESCR, </span><span><span>"The chest is empty."</span></span><span>)<br> </span><span><span>end</span></span><span><br> </span><span><span>return</span></span><span> </span><span><span>true</span></span><span><br></span><span><span>end</span></span><span><br></span></span>



Common mistakes (VERY important)​


❌ Using storage​


Storage 0 is risky and sometimes reserved.
Avoid it.


❌ Reusing the same storage for multiple things​




<span><span><span>-- BAD</span></span><span><br>STORAGE = </span><span><span>2000</span></span><span><br></span></span>

Use separate IDs:




<span><span>STORAGE_QUEST = </span><span><span>2000</span></span><span><br>STORAGE_BOSS = </span><span><span>2001</span></span><span><br>STORAGE_DOOR = </span><span><span>2002</span></span><span><br></span></span>

❌ Forgetting that default is​


Checking:




<span><span><span>if</span></span><span> player:getStorageValue(id) == </span><span><span>0</span></span><span> </span><span><span>then</span></span><span><br></span></span>

…will fail if it was never set.




GlobalStorage (difference)​


  • Storage → per player
  • GlobalStorage → shared by the entire server



<span><span>Game.getStorageValue(</span><span><span>9000</span></span><span>)<br>Game.setStorageValue(</span><span><span>9000</span></span><span>, </span><span><span>1</span></span><span>)<br></span></span>

Use GlobalStorage for:


  • world events
  • raid states
  • server-wide quest phases



Debugging storages​


Quick debug:




<span><span>player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE,<br> </span><span><span>"Storage 2000 = "</span></span><span> .. player:getStorageValue(</span><span><span>2000</span></span><span>))<br></span></span>



TL;DR​


  • Storage = per-player key/value
  • Default value = -1
  • No registration needed
  • Saved automatically
  • Use ranges + constants
  • setStorageValue() = saving



If you want next, I can:


  • show real Tibia quest patterns
  • design a storage map for a big questline
  • explain storage vs globalstorage pitfalls
  • show how TFS 1.4+ differs

Just tell me 😄
You know that that's not i was looking for, I would not have come to ask for something if i wanted to use storage values, clearly storage values are not the best approach for my case, I wanted to store it temporarly just while chatting, and was looking if someone else had any solution for this specific problem.

If you are really insterested to help, don't come with this disgusting chat response that you didn't even read before posting, just stay quiet like everyone else did.
 
That you didn’t like the answer you got, even though it’s my way of helping you, shows how ungrateful you are as a person, and it disgusts me. Go away.
 
Last edited:
I'm trying to build and npc that you'll be able to try and buy outfits from, the problem is that the talk sequence is a bit complex. At the moment, my behavior is working as espected, but I'm facing a major problem that I've no idea on how to solve...


LUA:
    message_table = msg:explode(" ")
    msg_action = message_table[1]
    msg_outfit = (message_table[2].." "..(message_table[3] or "").." "..(message_table[4] or "")):trim()

    if msgcontains(message_table[1], "buy") then
        if outfits[msg_outfit:lower()] ~= nil then
            npcHandler:say("Do you want to buy the " .. msg_outfit .. " outfit?\n{Base} skins without outfits is 10 coins\n{first} and {second} addon is 5 coins each\n{complete} skin + addons is 20 coins.", cid)
            npcHandler.topic[cid] = 1
        else
            npcHandler:say("I could not find that outfit, sorry.", cid)
        end
    end

The msg_outfit is working when the player says for the first time, the problem is that the npc should remember this info to use it later on the same conversation. I need to store topic to control the stage, but I also need to save the selected outfit.

How can I store this kind of information into a npc dialog, that it should keep it save in between messages, that is individual by players?
There are multiple ways you could do this (assuming there is no npcsystem default for this). For me, I would simply create an empty table(local) near the top of your script (not inside any function), this will stay global for your NPC script only. You can then simply store any data you want using the player's GUID(cid) as the key.

LUA:
-- top of script
local data = {}

--

data[cid] = "test" -- to set...
local val = data[cid] -- to get...
(It's not vital but.... make sure you clear any data for a player that loses focuses, it will keep the table clean)
 
Last edited:
Solution
There are multiple ways you could do this (assuming there is no npcsystem default for this). For me, I would simply create an empty table(local) near the top of your script (not inside any function), this will stay global for your NPC script only. You can then simply store any data you want using the player's GUID(cid) as the key.

LUA:
-- top of script
local data = {}

--

data[cid] = "test" -- to set...
local val = data[cid] -- to get...
(It's not vital but.... make sure you clear any data for a player that loses focuses, it will keep the table clean)
Thats probably the best way to do it, i'll mark as solved since I manage to get it working my just using tables inside topic.
I didn't know you could set any info to topic, and thats kinda the purpose of it, I'm currently using it to control the general flow and store the informations needed per player talking to the npc at the same time.
 
Thats probably the best way to do it, i'll mark as solved since I manage to get it working my just using tables inside topic.
I didn't know you could set any info to topic, and thats kinda the purpose of it, I'm currently using it to control the general flow and store the informations needed per player talking to the npc at the same time.
Yep, topic is just a global Lua table inside the npcsystem, and acts just the way like I described (using cid as key), but it's usually used to store the talkstate. Either way, good luck!
 
Back
Top