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

Lua [1.2] Animated Spell Conversion.

Caduceus

Unknown Member
Joined
May 10, 2010
Messages
321
Solutions
2
Reaction score
24
Sorry to spam Support with another post about animated spells, but I have also been looking to fix old scripts from an old 8.6 server That I ran a long time ago. I am trying to convert to 1.2 & getting the error below:

Code:
Lua Script Error: [Test Interface]
data/spells/scripts/custom/static_wave.lua
LuaScriptInterface::luaCreateCombatArea(). Invalid area table.
stack traceback:
        [C]: in function 'createCombatArea'
        data/spells/scripts/custom/static_wave.lua:4: in main chunk

Lua Script Error: [Test Interface]
data/spells/scripts/custom/static_wave.lua
data/spells/scripts/custom/static_wave.lua:5: attempt to call method 'setParameter' (a nil value)
stack traceback:
        [C]: in function 'setParameter'
        data/spells/scripts/custom/static_wave.lua:5: in main chunk
[Warning - Event::checkScript] Can not load script: scripts/custom/static_wave.lua

http://pastebin.com/s71s29Pn
 
Rather than just giving you the solution, lets take the code apart so you understand why it doesn't work.
We will also not concern ourselves with if you are using the correct functions for whatever distro.
Code:
local combat = {} -- create an empty table labeled combat
    for i = 1, 6 do
        -- create a new combat area object and store it in the combat table at the index of i
        combat[i] = createCombatArea()
        -- this is where you are screwing up the code
        -- your telling your server to access the entire table of objects which is why it doesn't work
        -- when you should be telling it to access the objects stored in the table
        combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
        -- and here aswell
        combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ENERGYAREA)
     
-- as well as here
combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues")
        end

This is why it important to learn lua so you understand the structure, logic & execution of a script.
 
Last edited:
I tried to use:

http://pastebin.com/z6NbnNYJ

I am getting no errors on server restart, nor on cast.
This is because again you are not applying the same logic you did with the initial problem, I am glad you thought about how to resolve the issue and did.. Congratulations! This is what development is all about :)

Now that we have our combat table created, and our objects populating the combat table, and the area table generated.. the next step is to make sure the interface onCastSpell resembles a 1.2 spell script.

It is important that we go through these steps to ensure the next time around we have a better understanding of what we are looking at
Currently in your script it looks like this
Code:
    function onCastSpell(player, var)
        local time_ = {1, 200, 400, 800, 1000, 1200}
        for t in ipairs(time_) do
            addEvent(executeCombat, time_[t], cid, combat[t], var)
        end
        return TRUE
    end

Now player and var are just placeholders for information which is retrieved from the server and passed to the interface so that the script can manipulate that data and send it back which allows you to cast your spell.. and defeat your enemies...

Lets not worry about all of the contents of the onCastSpell, right now we need to concern ourselves with its return value.

This return TRUE is like saying return an empty value or nil because in tfs 1.x TRUE and true are not the same thing, all interfaces require our scripts to return a boolean value, 1 of 2 values in order to define whether our script should execute or not, a value of either true or false.

When sending a value of nil back it is like sending false.. essentially.

So do we want our script to execute? Yes, of course we do, so we change that nasty TRUE to true and now although it doesn't work as expected it will execute upto the point where it doesn't work :p and give us some errors.
 
Ok, I understand a nil value, basically saying there is nothing there.. blank.. in this case false. I wasn't aware that TRUE and true were different. time_ is also a place holder? not sure there. I am actually trying to understand the what & wheres of everything. A bit slow, I know. As for the Functions, that is my weak point. As stated in the message earlier, If there was a reference to help define:
Example:
Code:
player:getSex() - Obviously, defines male or female of player

say you go to Compat, search "getSex()" obviously you come to this. Which is confusing as all hell to me.
Code:
function getPlayerSex(cid) local p = Player(cid) return p ~= nil and p:getSex() or false end

Back on topic: Only thing I can see is maybe, "for t in ipairs(time_) do", again, I'm unsure. Just a guess.
through trial and error, I find that, well, no this didn't work. So, again.. I'm at no errors, & no resolution to my issue.

the spell is registering ingame. I know this because the words do not transfer over to pm or world chat. So I know that the spell.xml is working.
 
Last edited:
So lets take apart whats confusing, and we will structure it so it is legible.
Code:
function getPlayerSex(cid)
    local p = Player(cid)
    return p ~= nil and p:getSex() or false
end
getPlayerSex(cid), any time you see the word function before the name of the function and open and closing parentheses, this is known as the function definition, now obviously when we do decide to use or call getPlayerSex(cid) we won't be using function in front of it, i am just clarifying for those that don't know.

cid in this case is userdata that is retrieved from the server, cid doesn't need to be called cid since it is a user defined function.. what do I mean by that.. user defined? Well if we can see, not touch at all, edit or add to a function's definition then it is considered user defined.

Code:
local p = Player(cid)
As we are now aware that cid is userdata, it is passed to Player as an argument, Player takes this data and manipulates it while returning whatever information (in this case an instance of player) and stores it in p, now p because it is defined inside of getPlayerSex does not exist outside of the getPlayerSex, so when we are done using getPlayerSex the information that we retrieved will no longer exist.

Code:
return p ~= nil and p:getSex() or false end
The above is telling you hey if p does not equal (~=) nil then (and) return p:getSex() if it does (or) return false, the end is there to tell the interpreter in this case the server this is the end of function.
This is just short hand for
Code:
if p ~= nil then
    return p:getSex()
else
    return false
end
Of course it can be simplified even further...

@topic
I guess tfs 1.x is terrible with giving errors :(
Code:
-- this is a user defined table... look above if you are not sure what user defined means
local time_ = {1, 200, 400, 800, 1000, 1200}
for t in ipairs(time_) do
    addEvent(executeCombat, time_[t], cid, combat[t], var)
end

The for is a iterator or loop, it allows us to traverse or cycle through data by either incrementing or decrementing depending on the type of for loop used, there are 2 types 1 which handles numerical and the other which handles table values.

Unfortunately programming is not read from right to left it is read from many different angles, with that said lets look at ipairs, since time_ is a table it is passed to ipairs as an argument, every iteration or loop ipairs will traverse or cycle through the table starting at index 1 and store this information in t which is a temporary variable and will only exist within the for loop.

Most of us are use to seeing a for loop like this
Code:
for k, v in ipairs(time_)

There isn't anything special about this method of using the for loop, all i did was ommit the 2nd variable.
It could have just as easily as been written like this.
Code:
for t = 1, #time_ do
end
or
for t, v in ipairs(time_)
end
Now the reason we don't use pairs to traverse the table is because ipairs although slower will cycle through the table in order of how it is defined where as pairs might not, also note that ipairs will only traverse through a table that has numerical indexes but those indexes must be within the bounds of its size... where as pairs doesn't care what value the index is..
 
The spell is casting, the player loses mana, the exhaust works. I'm getting no combat. No Console errors. So what can cause the combat to fail in this statement...


Code:
function onCastSpell(player, var)
        local time_ = {1, 200, 400, 800, 1000, 1200}
        for t, _ in ipairs(time_) do
            addEvent(executeCombat, time_[t], combat[t], var)
        end
        return true
    end
 
Last edited:
Errors are always a good sign :)

Now we encounter this line of code within the for loop
Code:
addEvent(executeCombat, time_[t], cid, combat[t], var)

Since cid isn't defined within the scope of onCastSpell, we will ommit this as an argument of addEvent, lets come back to a bit later.
For now lets look at the user defined function called executeCombat.
Code:
    function executeCombat(cid, combat, var)
        if isCreature(cid) == TRUE then
            doCombat(cid, combat, var)
        end
    end

Since this code was not made for 1.2 server we need to update it to 1.2, changing its parameters and how it executes with the data passed to it.
Code:
function executeCombat(player, var)

Since player and var are defined in onCastSpell, we need to pass the same data to executeCombat as arguments.

Player in this case of 1.1/1.2 is the actual instance the object of the player where as in previous we needed to create an instance or player from cid, so we need to pass player, safely to another function.

So we need to go back to onCastSpell and create a table that can safely send this information to the function executeCombat without crashing the server if the player logs out. (this is why you are getting those warnings : LuaScriptInterface::luaAddEvent(). Argument #3 is unsafe )
Code:
function onCastSpell(player, var)
     local p = {player = player, var = var, combat = combat}
     local time_ = {1, 200, 400, 800, 1000, 1200}
     for t, _ in ipairs(time_) do
         addEvent(executeCombat, time_[t], p, t)
     end
     return true
end

Then we will go back to the function executeCombat and change its parmeters to
Code:
function executeCombat(p, i)

Now we can safely check to make sure the player still exists even after the spell was casted
Code:
function executeCombat(p, i)
   -- this will execute 1st before anything else in this function
    if not p.player then
        return false
    end
    -- we could have combined the 2 but hey there is no harm in being careful :D
    if not p.player:isPlayer() then
         return false
    end
    -- now that we have determined that the player still exists
    -- we can execute the spell
    p.combat[i]:execute(p.player, p.var)
end

I know I forgot the combat area :p but I love seeing the errors it is important to see them as your code won't always work the 1st time :)
 
Last edited:
well, this is great & all, but I have used

Code:
function onCastSpell(player, var)
     local p = {player = player, var = var, combat = combat}
     local time_ = {1, 200, 400, 800, 1000, 1200}
     for t, _ in ipairs(time_) do
         addEvent(executeCombat, time_[t], p, t)
     end
     return true
end

still, I receive no errors, spell casts, no combat.
 
Now lets take a look at this for loop
Code:
    for x, _ in ipairs(area) do
        Combat(combat[x], Combat(area[x]))
    end
We'll need to update this code as well
Code:
    for x, _ in ipairs(area) do
        combat[x]:setArea(createCombatArea(area[x]))
    end
 
In the end your code should resemble something like this
Code:
    local combat = {}

    for i = 1, 6 do
        combat[i] = Combat()
        combat[i]:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
        combat[i]:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_ENERGYAREA)
        combat[i]:setFormula(COMBAT_FORMULA_LEVELMAGIC, -2.0, -150, -1.6, -150)
    end

    local area = {
        {
            {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
            {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
            {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
            {0, 0, 0, 1, 1, 2, 1, 1, 0, 0, 0},
            {0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
            {0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0},
            {0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0},
            {0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
        },
        {
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
            {0, 0, 0, 0, 1, 2, 1, 0, 0, 0, 0},
            {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
        },
        {
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
            {0, 0, 0, 1, 0, 2, 0, 1, 0, 0, 0},
            {0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
            {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
        },
        {
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
            {0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0},
            {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
            {0, 0, 1, 0, 0, 2, 0, 0, 1, 0, 0},
            {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
            {0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0},
            {0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
        },
        {
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
            {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
            {0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0},
            {0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0},
            {0, 1, 0, 0, 0, 2, 0, 0, 0, 1, 0},
            {0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0},
            {0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0},
            {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
            {0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
        },
        {
            {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}
        }
    }
    for x, _ in ipairs(area) do
        combat[x]:setArea(createCombatArea(area[x]))
    end

    function executeCombat(p, i)
        if not p.player then
            return false
        end
        if not p.player:isPlayer() then
             return false
        end
        p.combat[i]:execute(p.player, p.var)
    end

    function onCastSpell(player, var)
         local p = {player = player, var = var, combat = combat}
         local time_ = {1, 200, 400, 800, 1000, 1200}
         for t, _ in ipairs(time_) do
             addEvent(executeCombat, time_[t], p, t)
         end
         return true
    end
 
I didn't have this section done, but thank you for your extensive explanation of the entire script. Hopefully this will help me as well as others learn what the hell we're doing.

Code:
function executeCombat(p, i)
        if not p.player then
            return false
        end
        if not p.player:isPlayer() then
             return false
        end
        p.combat[i]:execute(p.player, p.var)
    end
 
Back
Top