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

How does the npc see ml?

chucky91

Advanced OT User
Joined
Apr 8, 2010
Messages
280
Solutions
9
Reaction score
153
How do I get npc to see magiclevel?
Because when I try to put it as it is in spells.xml and spell.cpp.
gives npc relogging error.
Code:
Topic=1,"yes",Level<SpellLevel(String) -> Amount=SpellLevel(String), "Don't be in high spirits. Advance to magic level %A, and then come again."

although another error occurs with spellbook.
Lua Script Error: [Action Interface]
data/actions/scripts/misc/spellbook.lua:eek:nUse
data/actions/scripts/misc/spellbook.lua:7: attempt to compare number with nil
stack traceback:
[C]: in function '__le'
data/actions/scripts/misc/spellbook.lua:7: in function <data/actions/scripts/misc/spellbook.lua:1>
Lua:
function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local count = getPlayerInstantSpellCount(player)
    local text = ""
    local spells = {}
    for i = 0, count - 1 do
        local spell = getPlayerInstantSpellInfo(player, i)
        if spell.maglv >= 0 then
            if spell.manapercent > 0 then
                spell.mana = spell.manapercent .. "%"
            end
            spells[#spells + 1] = spell
        end
    end

    table.sort(spells, function(a, b) return a.maglv < b.maglv end)

    local prevmaglv = -1
    for i, spell in ipairs(spells) do
        local line = ""
        if prevmaglv ~= spell.maglv then
            if i ~= 1 then
                line = "\n"
            end
            line = line .. "Spells for magic maglv " .. spell.maglv .. "\n"
            prevmaglv = spell.maglv
        end
        text = text .. line .. "  " .. spell.words .. " - " .. spell.name .. " : " .. spell.mana .. "\n"
    end

    player:showTextDialog(item:getId(), text)
    return true
end
 
Last edited:
I decided to blog my thought process during this support challenge. :)

getPlayerInstantSpellCount returns the amount of spells a player has, (etc 7 spells) which you add to the variable count.
It then loops from 0 to that count-1, so etc 0,1,2,3,4,5 and finally 6. This is what will be stored in the variable i for each iteration of the for loop.

But then you proceed to insert this decreasing (remaining spells count to loop over) into the function getPlayerInstantSpellInfo. However, this function is interested in spellid, not some random recursive iterator in the code that just logically tells how many spells remains to be looped over.

To figure out how this works, I open the project folder in notepad++ or sublime text or any editor that lets you recursively search all files inside a folder (TFS server) for a text. I searched for getPlayerInstantSpellInfo to figure out how it works, and found it located in the data/lib/compat/compat.lua:

Lua:
function getPlayerInstantSpellCount(cid) local p = Player(cid) return p and #p:getInstantSpells() end
function getPlayerInstantSpellInfo(cid, spellId) -- second param here is spellid
	local player = Player(cid)
	if not player then
		return false
	end

	local spell = Spell(spellId) -- <-- spell metatable? Perhaps we have something in our wiki
	if not spell or not player:canCast(spell) then
		return false -- <-- hmm, if it fails to find the spell, it returns false. 
	end

	return spell -- the object it returns upon success, appears to be the spell metatable itself
end
You see the second parameter is spellid. Your third spell might be under your i=3 iteration value, but the spellid might be 53. If it fails to find a spell, it will return false instead of the spell metatable. In line 6 you load this variable, and in line 7 you compare it:
Lua:
        local spell = getPlayerInstantSpellInfo(player, i)
        if spell.maglv >= 0 then
But in this instance, spell could be false, so your trying to compare false.maglv >= 0. false is a boolean value, and has no attributes, so when you try to grab "false" .maglv property, it returns null, which then is compared to see if its greater or equal to 0. This is probably why you get the error message spellbook.lua:7: attempt to compare number with nil.

But do we know that you need to call spell.maglv in order to retrieve the magic level requirement for the spell? Could it be another name, like maglvl or magicLevel? I don't remember, so figured I wanted to find out.

I also figured looking at the default code for spellbook in the TFS repo is a good starting point to see how its made:
Lua:
local spellbook = Action()

function spellbook.onUse(player, item, fromPosition, target, toPosition, isHotkey)
	local text = {}
	local spells = {}
	for _, spell in ipairs(player:getInstantSpells()) do -- interesting way to loop through spells, declares spell variable for us
		if spell.level ~= 0 then
			if spell.manapercent > 0 then
				spell.mana = spell.manapercent .. "%"
			end
			spells[#spells + 1] = spell
		end
	end

	table.sort(spells, function(a, b) return a.level < b.level end)

	local prevLevel = -1
	for i, spell in ipairs(spells) do
		if prevLevel ~= spell.level then
			if i == 1 then
				text[#text == nil and 1 or #text+1] = "Spells for Level "
			else
				text[#text+1] = "\nSpells for Level "
			end
			text[#text+1] = spell.level .. "\n"
			prevLevel = spell.level
		end
		text[#text+1] = spell.words .. " - " .. spell.name .. " : " .. spell.mana .. "\n"
	end

	player:showTextDialog(item:getId(), table.concat(text))
	return true
end

spellbook:id(2175, 6120, 8900, 8901, 8902, 8903, 8904, 8918, 16112, 18401, 22422, 22423, 22424, 23771)
spellbook:register()

Looking at this code, I see they used a different way to loop through spells, instead of
Lua:
local count = getPlayerInstantSpellCount(player)
for i = 0, count - 1 do
  local spell = getPlayerInstantSpellInfo(player, i)
  -- ...
They iterate over the spells using ipairs:
Lua:
for _, spell in ipairs(player:getInstantSpells()) do

Which seems a bit easier to work with than using compat functions. :p
Another benefit of already getting spell objects, is that we probably don't need to figure out the spellid for each spell, and if we do, we can probably find it using spell.id.

I was hoping to see a sample on how magic level is used here. But found none.

But since spell metatable is used (I think?), so lets look into the docs:

But its unfortunately a bit lacking, I tried to patch it up a year ago but turns out I didnt finish it. But you might be able to do spell.magicLevel, but perhaps try spell.maglvl as well.

To see everything that is available in the spell metatable, you can try to dump it to console:

Lua:
tdump("Iterated spell object", spell)
To have a look at the data inside the spell variable. It should print it out in console when you execute the code, nice for debugging. There you can see what you need to call to retrieve etc magiclevel, name and other spell things.

So lets use these findings, and try to build some code, I start with the presumption that I can get magic level from spell.maglv (and hope the spell object was just initialized wrong in your code sample).

Lua:
function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    --local count = getPlayerInstantSpellCount(player)
    local text = ""
    local spells = {}
    for _, spell in ipairs(player:getInstantSpells()) do --for i = 0, count - 1 do
        tdump("Iterated spell object", spell)--local spell = getPlayerInstantSpellInfo(player, i)
        if spell.maglv >= 0 then
            if spell.manapercent > 0 then
                spell.mana = spell.manapercent .. "%"
            end
            spells[#spells + 1] = spell
        end
    end

    table.sort(spells, function(a, b) return a.maglv < b.maglv end)

    local prevmaglv = -1
    for i, spell in ipairs(spells) do
        local line = ""
        if prevmaglv ~= spell.maglv then
            if i ~= 1 then
                line = "\n"
            end
            line = line .. "Spells for magic maglv " .. spell.maglv .. "\n"
            prevmaglv = spell.maglv
        end
        text = text .. line .. "  " .. spell.words .. " - " .. spell.name .. " : " .. spell.mana .. "\n"
    end

    player:showTextDialog(item:getId(), text)
    return true
end
Notice I only did 3 changes, commented out line 2 (dont need count anymore)
replaced line 5 with this better loop method that also loads in our spell objects for each iteration.
So since we already have spell (from the for loop), we can replace line 6 with a tdump function.
This should spam your console when you execute this action, but it will show the key value pairs inside the spell object, so if we are wrong about maglv, just have a look at what the actual name is there. When everything works, remove line 6 to make the console silent again.

Let me know how this works, I have invested enough time into this now that I am curious. :)

Edit:
As for your actual NPC problem, why don't you post the full code? I would probably do some magic before the communication dialogue lines.
 
Last edited:
Wow... This is the best answer from all support threads I've ever seen. You taught him how to understand what's going on and where to find the answers! Very good job Znote! I guess I haven't seen answers like this even on stackoverflow xd
 
Thank you very much for the explanation, i have been studying c++ code for a few days.
but i take medicine and it’s difficult for me to reason.
npc is this.
C#:
# GIMUD - Graphical Interface Multi User Dungeon

# zoltan.npc Datenbank fuer den Zauberlehrer Zoltan



Name = "Zoltan"

Outfit = (130,95-94-95-57)

Home = [33268,31849,4]

Radius = 3



Behaviour = {

ADDRESS,"hello$",! -> "Welcome %N, student of the arcane arts."

ADDRESS,"hi$",!    -> *

ADDRESS,!          -> Idle

BUSY,"hello$",!    -> "Wait %N. Your time will come.", Queue

BUSY,"hi$",!       -> *

BUSY,!             -> NOP

VANISH,!           -> "Use your knowledge wisely."



"bye"       -> "Use your knowledge wisely", Idle

"job"       -> "I am a teacher of the most powerful spells in Tibia."

"name"      -> "I am known in this world as Zoltan."

"time"      -> "It's %T."

"king"      -> "King Tibianus III was the founder of our academy."

"tibianus"  -> *

"army"      -> "They rely too much on their brawn instead of their brain."

"ferumbras" -> "A fallen sorcerer, indeed. What a shame."

"excalibug" -> "You will need no weapon if you manipulate the essence of magic."

"thais"     -> "Thais is a place of barbary."

"tibia"     -> "There is still much left to be explored in this world."

"carlin"    -> "Carlin's druids waste the influence they have in enviromentalism."

"edron"     -> "Sciences are thriving on this isle."

"news"      -> "I have no time for chit chat."

"rumors"    -> *

"eremo"     -> "He is an old and wise man that has seen a lot of Tibia. He is also one of the best magicians. Visit him on his little island."

"visit"     -> "You should visit Eremo on his little island. Just ask Pemaret on Cormaya for passage."



"yenny","gentle"  -> "Ah, Yenny the Gentle was one of the founders of the druid order called Crunor's Caress, that has been originated in her hometown Carlin."

"yenny"           -> "Yenny? Which Yenny? That is a common name."

"crunor","caress",QuestValue(211)=1 -> "A quite undruidic order of druids they were, as far as we know. I have no more enlightening knowledge about them though.",SetQuestValue(211,2)

"crunor","caress",QuestValue(211)>1 -> *

"crunor","caress",QuestValue(211)=0 -> "I am quite busy, ask another time!"





"spellbook" -> "Don't bother me with that. Ask in the shops for it."

"spell"     -> "I have some very powerful spells: 'Energy Bomb', 'Mass Healing', 'Poison Storm', 'Paralyze', and 'Ultimate Explosion'."



"energy","bomb",Sorcerer        -> String="Energybomb", Price=2300, "Are you prepared to learn the spell 'Energy Bomb' for %P gold?", Topic=1

"energy","bomb"                 -> "No, no, no. This dangerous spell is only for sorcerers."

"mass","healing",Druid          -> String="Mass Healing", Price=2200, "Are you prepared to learn the spell 'Mass Healing' for %P gold?", Topic=1

"mass","healing"                -> "No, no, no. This elemental spell is only for druids."

"poison","storm",Druid          -> String="Poison Storm", Price=3400, "Are you prepared to learn the spell 'Poison Storm' for %P gold?", Topic=1

"poison","storm"                -> "No, no, no. This elemental spell is only for druids."

"paralyze",Druid                -> String="Paralyze", Price=1900, "Are you prepared to learn the spell 'Paralyze' for %P gold?", Topic=1

"paralyze"                      -> "No, no, no. This elemental spell is only for druids."

"ultimate","explosion",Sorcerer -> String="Ultimate Explosion", Price=8000, "Are you prepared to learn the spell 'Ultimate Explosion' for %P gold?", Topic=1

"ultimate","explosion"          -> "No, no, no. This dangerous spell is only for sorcerers."



Topic=1,"yes",SpellKnown(String)=1        -> "Want to fool me? You already know this spell."

Topic=1,"yes",Level<SpellLevel(String) -> Amount=SpellLevel(String), "Don't be in high spirits. Advance to magic level %A, and then come again."

Topic=1,"yes",CountMoney<Price            -> "Want to learn one of the most powerful spells, and don't even know how much money you have?"

Topic=1,"yes"                             -> "Congratulations. You now know one of the most powerful spells. Use it wisely.", DeleteMoney, EffectOpp(13), TeachSpell(String)

Topic=1                                   -> "Lost your heart?"

}
while i'm at home, i'm at the computer.
 
Ohh, I have no idea how that works.
That looks like a file from the Nostalrius server, its not standard TFS npc format, so not many people are familiar with it and can help you.
Maybe @Ezzz can help you, he is the main dev of this server.
 
Yes, i found it very original and does not consume much memory, i wanted to edit the compilation, but have a problem with a library that does not install on my windows 10.
 
Back
Top