• 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.X+ 1.3 Learn and teach spell

William Hughes

New Member
Joined
Oct 12, 2018
Messages
28
Reaction score
1
I have a vocation that can teach other vocations spells but I need a talkaction that can handle an array of spell names I can fill out, compare them against them command given and an existing entity as well as take an item as cost to use the talkaction. Sample command would be !Teachspell playername, spellname / !teachspell newguy, light

Ive tried a few ways to get this working but lack the understanding of dual param in a talkaction and multiple escapes(else) because of failures such as the player does not exist and so on.

I think this will require fail on not having the req item/fail on player does not exist/fail on spell does not exist/fail on no command or improper use.

Anything towards this would help and I will be active all day today looking at this, thank you to anyone posting.

function onSay(player, words, param)
local vocation = player:getVocation():getId()
if isInArray({2,21,22}, vocation) and player:getMana() > 499 then -- vocations that can teach spells and mana cost
if words == "!teachspell" then
local name = param:split(",")
local targetPlayer = Player(name[1])
if not targetPlayer then
player:sendCancelMessage("Player not found.")
return false
end


still trying diff things but just get a storm of various errors :p

bad is bad lololol :D Lots of people looking no one knows? or just laughing at me lol :D
 
Last edited:
Solution
Code:
local spells = {
    ["light"] = { -- Make sure the spell name is exact as it is in spells.xml
        levelReqCaster = 1, -- Level requirement for the caster to teach someone this spell
        levelReqTarget = 1, -- level requirement the person needs to be to learn the spell from the caster
        itemsReq = { -- Items required to teach someone the spell
            [1] = {itemid = 18422, count = 10}
        }
    },
    ["haste"] = { -- Make sure the spell name is exact as it is in spells.xml
        levelReqCaster = 1, -- Level requirement for the caster to teach someone this spell
        levelReqTarget = 1, -- level requirement the person needs to be to learn the spell from the caster
        itemsReq = { -- Items required...
You were close man I promise... Take a look at this and hopefully you can learn it.

Code:
local vocations = {2, 21, 22}

-- You only need to use words == "blank" if there is more than one talkaction linked to the code. --
-- In this case there is only !teachspell linked to this code so no need for it. --

local spells = {
    ["SpellName 1"] = { -- Make sure the spell name is exact as it is in spells.xml
        levelReqCaster = 10, -- Level requirement for the caster to teach someone this spell
        levelReqTarget = 10, -- level requirement the person needs to be to learn the spell from the caster
        itemsReq = { -- Items required to teach someone the spell
            [1] = {itemid = 1111, count = 1}
        }
     
     
    }
}

function onSay(player, words, param)
    if not isInArray(vocations, player:getVocation():getId()) then
        player:sendCancelMessage("Your vocation cannot use this spell.")
        return true
    end
 
    if player:getMana() < 500 then
        player:sendCancelMessage("You do not have enough mana to cast this spell.")
        return true
    end
 
        --Param is the text we are splitting--
        --We split the text by using , --
        --We set each split to the table t --
    local t = param:split(",")
         
        --words      --t[1]  --t[2]
        --!teachspell player, spellName
 
 
 
    -- So t[1] is the first text before a ,
    local target = Player(t[1])
 
    -- We set the target to be the first bit of text before a , using t[1]
    -- The next text after the , is accessed with t[2]
    local spellName = t[2]
 
    --If we wanted to access the next text we would use t[3] but we dont need to in this code.. --
 
    --Now we see if the player is online
    if not target then
        player:sendCancelMessage("Player is not online or doesn't exist.")
        return true
    end
 
    -- Make sure the player has given a spell name to teach
    if not spellName then
        player:sendCancelMessage("You must type a spell name to teach the player.")
        return true
    end
 
    --Next we see if the player is close enough to the person casting the spell so the cant do it from across the map
    if player:getPosition():getDistanceTo(target:getPosition()) > 3 then
        player:sendCancelMessage("Target is too far away.")
        return true
    end
 
    local SPELL = spells[spellName]
 
    if not SPELL then
        player:sendCancelMessage("Could not find a spell with that name. Make sure you capatalize correctly.")
        return true
    end
 
    if target:hasLearnedSpell(spellName) then
        player:sendCancelMessage(target:getName().." has already learned that spell.")
        return true
    end

    if player:getLevel() < SPELL.levelReqCaster then
        player:sendCancelMessage("You are not high enough level to each someone that spell.")
        return true
    end
 
    if target:getLevel() < SPELL.levelReqTarget then
        player:sendCancelMessage(target:getName().." is not high enough level to learn that spell.")
        return true
    end
 
    local hasItems = true
    for i = 1, #SPELL.items do
        if player:getItemCount(SPELL.items[i].itemid) < SPELL.items[i].count then
            hasItems = false
            break
        end
    end
 
    if hasItems then
        for i = 1, #SPELL.items do
            player:removeItem(SPELL.items[i].itemid, SPELL.items[i].count)
        end
        target:learnSpell(spellName)
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have taught "..target:getName().." the spell "..spellName.."!")
    else
        local text = "You do not have the items required to teach this spell. You need: "
        for i = 1, #SPELL.items do
            text = text..""..SPELL.items[i].count.." "..Item(SPELL.items[i].itemid):getName()..", "
        end
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, text)
        return true
    end
return true
end
 
Last edited:
The distance to the other player is having an issue?

line 40 and 61 are pretty clear! wonder why it is all mad about that.
 

Attachments

Last edited:
I didnt adjust the first spell data part on the original error I got, but the second error I updated the image on it is about the distance to a target :p
 
Replace

Code:
if player:getPosition():getDistanceTo(target:getPosition()) > 3 then

for

Code:
if Tile(player:getPosition()):getDistanceTo(target:getPosition()) > 3 then
 
Tired... Here..

Code:
local vocations = {2, 21, 22}

-- You only need to use words == "blank" if there is more than one talkaction linked to the code. --
-- In this case there is only !teachspell linked to this code so no need for it. --

local spells = {
    ["SpellName 1"] = { -- Make sure the spell name is exact as it is in spells.xml
        levelReqCaster = 10, -- Level requirement for the caster to teach someone this spell
        levelReqTarget = 10, -- level requirement the person needs to be to learn the spell from the caster
        itemsReq = { -- Items required to teach someone the spell
            [1] = {itemid = 1111, count = 1}
        }
       
       
    }
}

function onSay(player, words, param)
    if not isInArray(vocations, player:getVocation():getId()) then
        player:sendCancelMessage("You vocation cannot use this spell.")
        return true
    end
   
    if player:getMana() < 500 then
        player:sendCancelMessage("You do not have enough mana to cast this spell.")
        return true
    end
   
        --Param is the text we are splitting--
        --We split the text by using , --
        --We set each split to the table t --
    local t = param:split(",")
           
        --words      --t[1]  --t[2]
        --!teachspell player, spellName
   
   
   
    -- So t[1] is the first text before a ,
    local target = Player(t[1])
   
    -- We set the target to be the first bit of text before a , using t[1]
    -- The next text after the , is accessed with t[2]
    local spellName = t[2]
   
    --If we wanted to access the next text we would use t[3] but we dont need to in this code.. --
   
    --Now we see if the player is online
    if not target then
        player:sendCancelMessage("Player is not online or doesn't exist.")
        return true
    end
   
    -- Make sure the player has given a spell name to teach
    if not spellName then
        player:sendCancelMessage("You must type a spell name to teach the player.")
        return true
    end
   
    --Next we see if the player is close enough to the person casting the spell so the cant do it from across the map
    if player:getPosition():getDistance(target:getPosition()) > 3 then
        player:sendCancelMessage("Target is too far away.")
        return true
    end
   
    local SPELL = spells[spellName]
   
    if not SPELL then
        player:sendCancelMessage("Could not find a spell with that name. Make sure you capatalize correctly.")
        return true
    end
   
    if target:hasLearnedSpell(spellName) then
        player:sendCancelMessage(target:getName().." has already learned that spell.")
        return true
    end

    if player:getLevel() < SPELL.levelReqCaster then
        player:sendCancelMessage("You are not high enough level to each someone that spell.")
        return true
    end
   
    if target:getLevel() < SPELL.levelReqTarget then
        player:sendCancelMessage(target:getName().." is not high enough level to learn that spell.")
        return true
    end
   
    local hasItems = true
    for i = 1, #SPELL.itemsReq do
        if player:getItemCount(SPELL.itemsReq[i].itemid) < SPELL.itemsReq[i].count then
            hasItems = false
            break
        end
    end
   
    if hasItems then
        for i = 1, #SPELL.itemsReq do
            player:removeItem(SPELL.itemsReq[i].itemid, SPELL.itemsReq[i].count)
        end
        target:learnSpell(spellName)
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, "You have taught "..target:getName().." the spell "..spellName.."!")
    else
        local text = "You do not have the items required to teach this spell. You need: "
        for i = 1, #SPELL.itemsReq do
            text = text..""..SPELL.itemsReq[i].count.." "..Item(SPELL.itemsReq[i].itemid):getName()..", "
        end
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_ORANGE, text)
        return true
    end
return true
end
 
if player:getPosition():getDistance(target:getPosition()) > 3 then
was not in last posted code fyi. I used it then replaced that then ended up with a index nil value error off line 105

text = text..""..SPELL.itemsReq.count.." "..Item(SPELL.itemsReq.itemid):getName()..", "

The problem was caused by not enough item...when the count was not 6 but 10 as it should be by my required setting no error. so an else out for the error and it should be good
 
if player:getPosition():getDistance(target:getPosition()) > 3 then
was not in last posted code fyi. I used it then replaced that then ended up with a index nil value error off line 105

text = text..""..SPELL.itemsReq.count.." "..Item(SPELL.itemsReq.itemid):getName()..", "

The problem was caused by not enough item...when the count was not 6 but 10 as it should be by my required setting no error. so an else out for the error and it should be good
change line 105 to
Code:
text = text..""..SPELL.itemsReq[i].count.." "..ItemType(SPELL.itemsReq[i].itemid):getName()..", "
 
Yes I have an error here on adding to the array, I am closing as many as I open to add spells but I am not in the right layer, however when i adjust it down it is still not right.

local spells = {
["light"] = { -- Make sure the spell name is exact as it is in spells.xml
levelReqCaster = 1, -- Level requirement for the caster to teach someone this spell
levelReqTarget = 1, -- level requirement the person needs to be to learn the spell from the caster
itemsReq = { -- Items required to teach someone the spell
[1] = {itemid = 18422, count = 10}
}

}

["haste"] = { -- Make sure the spell name is exact as it is in spells.xml
levelReqCaster = 1, -- Level requirement for the caster to teach someone this spell
levelReqTarget = 1, -- level requirement the person needs to be to learn the spell from the caster
itemsReq = { -- Items required to teach someone the spell
[1] = {itemid = 18422, count = 10}
}

}





}
 
Code:
local spells = {
    ["light"] = { -- Make sure the spell name is exact as it is in spells.xml
        levelReqCaster = 1, -- Level requirement for the caster to teach someone this spell
        levelReqTarget = 1, -- level requirement the person needs to be to learn the spell from the caster
        itemsReq = { -- Items required to teach someone the spell
            [1] = {itemid = 18422, count = 10}
        }
    },
    ["haste"] = { -- Make sure the spell name is exact as it is in spells.xml
        levelReqCaster = 1, -- Level requirement for the caster to teach someone this spell
        levelReqTarget = 1, -- level requirement the person needs to be to learn the spell from the caster
        itemsReq = { -- Items required to teach someone the spell
            [1] = {itemid = 18422, count = 10}
        }
    } --Add next after this make sure you add ,
    
    
}

Mark as solved please.
 
Solution
Back
Top