• Sign up for TFS Icon Competition! The prize includes Otland Premium time and a medal, as well as obviously taking part in TFS history.
  • 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 0.X [C++] Show spells when MLVL up deppeding on vocation

gmstrikker

Active Member
Joined
Jul 30, 2014
Messages
441
Solutions
1
Reaction score
40
I was helped on this topic TFS 0.X - ML up show new spell from spells.xml (https://otland.net/threads/ml-up-show-new-spell-from-spells-xml.270306/) to edit my source so a script like this work:
It shows all spells you from your magic level when u get magic level up (from spells.xml)
But there is a problem...
It show all spells, it not verify your vocation, so for example when paladins get mlvls it shows mages spells

Is anyone know how to put a vocation condition to when players get mlvl up it shows only your vocation spells?

creaturescript script:
Code:
--[[------------------------------------------------<|]
    |* * * * * * * * * * * * * * * * * * * * * * * * * * *|
    |* * * * * * *  [SpellUp! Script] * * * * * * * * * * |
    |* * * * * * * * By: Cybermaster * * * * * * * * * * *|    
    |* * *  Tested on: The Forgotten Server 0.3.6pl1 * * *|
    |* * * * * * * * * * * * * * * * * * * * * * * * * * *|
    |>---------------------------------------------------]]

local s = { --SETUP
    repeatAfterDeath = true, -- true -> player will always get the msg at lvlup | false -> player will only get the 1st time the gets the new level
-- storage below is where the newlevel will be stored ONLY IF YOU USE repeatAfterDeath
    Storage = 10000,
    messageType = 'popUp', -- options: 'popUp' or 'channel'
--this one below only used if messageType = channel
    channelClass = MESSAGE_EVENT_ORANGE
}
  
function onAdvance(cid, skill, oldlevel, newlevel)
    if skill ~= 7 or not s.repeatAfterDeath and getCreatureStorage(cid, s.Storage) >= newlevel then
        return true
    end

    local t = {}
    for i = 0, getPlayerInstantSpellCount(cid) - 1 do
        local spell = getPlayerInstantSpellInfo(cid, i)
        if(spell.mlevel ~= 0) and spell.mlevel == newlevel then
            if(spell.manapercent > 0) then
                spell.mana = spell.manapercent .. '%'
            end
            table.insert(t, spell)
        end
    end

    table.sort(t, function(a, b) return a.level < b.level end)
    local tmpStr = ''
    local Str = ''
    for i, spell in ipairs(t) do
        tmpStr = 'NEW SPELL:\n['..spell.name..'] "'..spell.words..'"\n\n'
        Str = Str .. tmpStr
    end

    if Str == '' then
        return true
    end

    doCreatureSetStorage(cid, s.Storage, newlevel)
    if s.messageType == 'popUp' then
        doShowTextDialog(cid, 2175, Str)
    elseif s.messageType == 'channel' then
        doPlayerSendTextMessage(cid, s.channelClass, Str)
    end
    return true
end

sources changes:
spells.cpp
added:
Code:
InstantSpell* Spells::getInstantSpellByIndexCustom(const Player* player, uint32_t index)
{
    uint32_t count = 0;
    for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it)
    {
        InstantSpell* instantSpell = it->second;
        if(count == index)
            return instantSpell;

        ++count;
    }

    return NULL;
}

spells.h
Code:
InstantSpell* getInstantSpellByIndexCustom(const Player* player, uint32_t index);

obs1: i need to show spells with needlearn="1" (because i put spells to buy in my server)
obs2: i have a lot of custom spells and i want to do so much more, so
obs3: source Fir3element/3777 (https://github.com/Fir3element/3777/tree/master/src)
 
Solution
Probably getPlayerInstantSpellCount is broken and provides too big value (it does not support vocation restriction I suppose).
It makes sense when you take a look into getPlayerInstantSpellInfo function implementation:
It returns false (boolean value) if spell with provided index does not exist and also prints an error to the console. Which perfectly fits your error:
Code:
[...]
[15:20:36.607] (luaGetPlayerInstantSpellInfo) Spell not found
[...]
[15:20:36.607] data/creaturescripts/scripts/spellup.lua:26: attempt to index local 'spell' (a boolean value)
[...]


If you'd like to keep code the way you did...

nadjsyrb

Member
Joined
Jun 9, 2020
Messages
24
Reaction score
16
i had the same problem, i would like to use a script like yours, but for level
i know how to change your script to get lvl instead mlvl

but idk and i need some help to
show spells even with needlearn="1"

if anybody know how pls post here
i gonna use it as base
 

nadjsyrb

Member
Joined
Jun 9, 2020
Messages
24
Reaction score
16
i had the same problem, i would like to use a script like yours, but for level
i know how to change your script to get lvl instead mlvl

but idk and i need some help to
show spells even with needlearn="1"

if anybody know how pls post here
i gonna use it as base

upppp
 

mackerel

Well-Known Member
Joined
Apr 26, 2017
Messages
399
Solutions
18
Reaction score
71
The change I made was in the wrong place. I realised that too late haha. There was another function that you had to modify, I can't remember which one but I might help you in 1-2 months time when I'm back on my pc :p
 
OP
gmstrikker

gmstrikker

Active Member
Joined
Jul 30, 2014
Messages
441
Solutions
1
Reaction score
40
The change I made was in the wrong place. I realised that too late haha. There was another function that you had to modify, I can't remember which one but I might help you in 1-2 months time when I'm back on my pc :p

Any spooilers where i should look for, so i try to help you to help me, i want this so much :)
 

Nihilist

Member
Joined
Jan 10, 2018
Messages
18
Solutions
4
Reaction score
9
@gmstrikker, for other vocation's spells not to show, you should change your custom function as follows:
C++:
InstantSpell* Spells::getInstantSpellByIndexCustom(const Player* player, uint32_t index)
{
    uint32_t count = 0;
    for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it)
    {
        InstantSpell* instantSpell = it->second;

        if (!vocSpellMap.empty() && vocSpellMap.find(player->getVocationId()) == vocSpellMap.end())
            continue;

        if(count == index)
            return instantSpell;

        ++count;
    }

    return NULL;
}

I did not test it, but I'm pretty sure it's gonna work.
 
OP
gmstrikker

gmstrikker

Active Member
Joined
Jul 30, 2014
Messages
441
Solutions
1
Reaction score
40
@gmstrikker, for other vocation's spells not to show, you should change your custom function as follows:
C++:
InstantSpell* Spells::getInstantSpellByIndexCustom(const Player* player, uint32_t index)
{
    uint32_t count = 0;
    for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it)
    {
        InstantSpell* instantSpell = it->second;

        if (!vocSpellMap.empty() && vocSpellMap.find(player->getVocationId()) == vocSpellMap.end())
            continue;

        if(count == index)
            return instantSpell;

        ++count;
    }

    return NULL;
}

I did not test it, but I'm pretty sure it's gonna work.


Its showing an error:
Code:
spells.cpp: In member function ‘InstantSpell* Spells::getInstantSpellByIndexCustom(const Player*, uint32_t)’:
spells.cpp:265:14: error: ‘vocSpellMap’ was not declared in this scope
         if (!vocSpellMap.empty() && vocSpellMap.find(player->getVocationId()) == vocSpellMap.end())
              ^~~~~~~~~~~
Makefile:33: recipe for target 'spells.o' failed

strange this vocSpellMap
is used in others functions in spells.cpp
 

Nihilist

Member
Joined
Jan 10, 2018
Messages
18
Solutions
4
Reaction score
9
@gmstrikker My bad, vocSpellsMap is defined only in the InstantSpell class, we are trying to use it in Spells. You can try creating a new function.

Below
C++:
bool canCast(const Player* player) const;
in spells.h:
C++:
bool hasVocation(int32_t vocId) const;


Then, spells.cpp:
C++:
bool InstantSpell::hasVocation(int32_t vocId) const
{
    if ((vocSpellMap.empty() || vocSpellMap.find(vocId) != vocSpellMap.end()))
        return true;

    return false;
}

At last, change the function again:

C++:
InstantSpell* Spells::getInstantSpellByIndexCustom(const Player* player, uint32_t index)
{
    uint32_t count = 0;
    for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it)
    {
        InstantSpell* instantSpell = it->second;

        if (!instantSpell->hasVocation(player->getVocationId()))
            continue;

        if(count == index)
            return instantSpell;

        ++count;
    }

    return NULL;
}

Hope it works.
 
OP
gmstrikker

gmstrikker

Active Member
Joined
Jul 30, 2014
Messages
441
Solutions
1
Reaction score
40
@gmstrikker My bad, vocSpellsMap is defined only in the InstantSpell class, we are trying to use it in Spells. You can try creating a new function.

Below
C++:
bool canCast(const Player* player) const;
in spells.h:
C++:
bool hasVocation(int32_t vocId) const;


Then, spells.cpp:
C++:
bool InstantSpell::hasVocation(int32_t vocId) const
{
    if ((vocSpellMap.empty() || vocSpellMap.find(vocId) != vocSpellMap.end()))
        return true;

    return false;
}

At last, change the function again:

C++:
InstantSpell* Spells::getInstantSpellByIndexCustom(const Player* player, uint32_t index)
{
    uint32_t count = 0;
    for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it)
    {
        InstantSpell* instantSpell = it->second;

        if (!instantSpell->hasVocation(player->getVocationId()))
            continue;

        if(count == index)
            return instantSpell;

        ++count;
    }

    return NULL;
}

Hope it works.

Code:
[15:20:36.607] [Error - CreatureScript Interface] 
[15:20:36.607] data/creaturescripts/scripts/spellup.lua:onAdvance
[15:20:36.607] Description: 
[15:20:36.607] (luaGetPlayerInstantSpellInfo) Spell not found

[15:20:36.607] [Error - CreatureScript Interface] 
[15:20:36.607] data/creaturescripts/scripts/spellup.lua:onAdvance
[15:20:36.607] Description: 
[15:20:36.607] data/creaturescripts/scripts/spellup.lua:26: attempt to index local 'spell' (a boolean value)
[15:20:36.607] stack traceback:
[15:20:36.607]     data/creaturescripts/scripts/spellup.lua:26: in function <data/creaturescripts/scripts/spellup.lua:18>

[15:20:42.975] [Error - CreatureScript Interface] 
[15:20:42.975] data/creaturescripts/scripts/spellup.lua:onAdvance
[15:20:42.975] Description: 
[15:20:42.975] (luaGetPlayerInstantSpellInfo) Spell not found

[15:20:42.975] [Error - CreatureScript Interface] 
[15:20:42.975] data/creaturescripts/scripts/spellup.lua:onAdvance
[15:20:42.975] Description: 
[15:20:42.975] data/creaturescripts/scripts/spellup.lua:26: attempt to index local 'spell' (a boolean value)
[15:20:42.975] stack traceback:
[15:20:42.975]     data/creaturescripts/scripts/spellup.lua:26: in function <data/creaturescripts/scripts/spellup.lua:18>

should i change something in creaturesripts/scripts/spellsup.lua?

Code:
--[[------------------------------------------------<|]
    |* * * * * * * * * * * * * * * * * * * * * * * * * * *|
    |* * * * * * *  [SpellUp! Script] * * * * * * * * * * |
    |* * * * * * * * By: Cybermaster * * * * * * * * * * *|     
    |* * *  Tested on: The Forgotten Server 0.3.6pl1 * * *|
    |* * * * * * * * * * * * * * * * * * * * * * * * * * *|
    |>---------------------------------------------------]]

local s = { --SETUP
    repeatAfterDeath = true, -- true -> player will always get the msg at lvlup | false -> player will only get the 1st time the gets the new level
-- storage below is where the newlevel will be stored ONLY IF YOU USE repeatAfterDeath
    Storage = 10000,
    messageType = 'popUp', -- options: 'popUp' or 'channel'
--this one below only used if messageType = channel
    channelClass = MESSAGE_EVENT_ORANGE
}
   
function onAdvance(cid, skill, oldlevel, newlevel)
    if skill ~= 7 or not s.repeatAfterDeath and getCreatureStorage(cid, s.Storage) >= newlevel then
        return true
    end
 
    local t = {}
    for i = 0, getPlayerInstantSpellCount(cid) - 1 do
        local spell = getPlayerInstantSpellInfo(cid, i)
        if(spell.mlevel ~= 0) and spell.mlevel == newlevel then
            if(spell.manapercent > 0) then
                spell.mana = spell.manapercent .. '%'
            end
            table.insert(t, spell)
        end
    end

    table.sort(t, function(a, b) return a.level < b.level end)
    local tmpStr = ''
    local Str = ''
    for i, spell in ipairs(t) do
        tmpStr = 'NEW SPELL:\n['..spell.name..'] "'..spell.words..'"\n\n'
        Str = Str .. tmpStr
    end
 
    if Str == '' then
        return true
    end
 
    doCreatureSetStorage(cid, s.Storage, newlevel)
    if s.messageType == 'popUp' then
        doShowTextDialog(cid, 2175, Str)
    elseif s.messageType == 'channel' then
        doPlayerSendTextMessage(cid, s.channelClass, Str)
    end
    return true
end
 

esigma94

Well-Known Member
Joined
Nov 1, 2009
Messages
101
Solutions
16
Reaction score
79
Probably getPlayerInstantSpellCount is broken and provides too big value (it does not support vocation restriction I suppose).
It makes sense when you take a look into getPlayerInstantSpellInfo function implementation:
It returns false (boolean value) if spell with provided index does not exist and also prints an error to the console. Which perfectly fits your error:
Code:
[...]
[15:20:36.607] (luaGetPlayerInstantSpellInfo) Spell not found
[...]
[15:20:36.607] data/creaturescripts/scripts/spellup.lua:26: attempt to index local 'spell' (a boolean value)
[...]


If you'd like to keep code the way you did before, you should update getPlayerInstantSpellCount lua function definition in luascript.cpp:
It should be using some custom function to get spells count like getPlayerInstantSpellInfo does.
For example change in this line:
lua_pushnumber(L, g_spells->getInstantSpellCount(player));
to
lua_pushnumber(L, g_spells->getInstantSpellCountCustom(player));


Create custom function getInstantSpellCountCustom to get spells amount, similiar to this one:
Its implementation might look like this (similiar to getInstantSpellByIndexCustom but with different output):
C++:
uint32_t Spells::getInstantSpellCountCustom(const Player* player)
{
    uint32_t count = 0;
    for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it)
    {
        InstantSpell* instantSpell = it->second;
        if (!instantSpell->hasVocation(player->getVocationId()))
            continue;

            ++count;
    }

    return count;
}


Dont forget to add its declaration to spells.h. It might be right below this line:
and looks like this:
uint32_t getInstantSpellCountCustom(const Player* player);
 
Solution
OP
gmstrikker

gmstrikker

Active Member
Joined
Jul 30, 2014
Messages
441
Solutions
1
Reaction score
40
Probably getPlayerInstantSpellCount is broken and provides too big value (it does not support vocation restriction I suppose).
It makes sense when you take a look into getPlayerInstantSpellInfo function implementation:
It returns false (boolean value) if spell with provided index does not exist and also prints an error to the console. Which perfectly fits your error:
Code:
[...]
[15:20:36.607] (luaGetPlayerInstantSpellInfo) Spell not found
[...]
[15:20:36.607] data/creaturescripts/scripts/spellup.lua:26: attempt to index local 'spell' (a boolean value)
[...]


If you'd like to keep code the way you did before, you should update getPlayerInstantSpellCount lua function definition in luascript.cpp:
It should be using some custom function to get spells count like getPlayerInstantSpellInfo does.
For example change in this line:
lua_pushnumber(L, g_spells->getInstantSpellCount(player));
to
lua_pushnumber(L, g_spells->getInstantSpellCountCustom(player));


Create custom function getInstantSpellCountCustom to get spells amount, similiar to this one:
Its implementation might look like this (similiar to getInstantSpellByIndexCustom but with different output):
C++:
uint32_t Spells::getInstantSpellCountCustom(const Player* player)
{
    uint32_t count = 0;
    for(InstantsMap::iterator it = instants.begin(); it != instants.end(); ++it)
    {
        InstantSpell* instantSpell = it->second;
        if (!instantSpell->hasVocation(player->getVocationId()))
            continue;

            ++count;
    }

    return count;
}


Dont forget to add its declaration to spells.h. It might be right below this line:
and looks like this:
uint32_t getInstantSpellCountCustom(const Player* player);

I think u solve our problem
Thank you so much!
 
Top