• 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.0] Only allows one damage over time spell

X X X

Newb
Joined
Jul 26, 2015
Messages
146
Reaction score
13
[SOLVED BY Andu, SEE BELOW FOR SOLUTION]
So I found a nice script here on OTland by Codinablack for an AoE spell that does instant damage AND adds a condition to the target. The script is as follows:

Code:
Code:
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_DEATHDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_BLACKSMOKE)

local area = createCombatArea(AREA_WAVE2)
combat:setArea(area)

local condition = Condition(CONDITION_CURSED)
condition:setTicks(40000)
condition:setParameter(CONDITION_PARAM_DELAYED, 1)
condition:setParameter(CONDITION_PARAM_TICKINTERVAL, 1500)

function onGetFormulaValues(cid, level, maglevel)
    min = -((level / 5) + (maglevel * 3) + 5)
    max = -((level / 5) + (maglevel * 4) + 7)
    return min, max
end

combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues")

function CastSpell(cid, var)
    local player = Player(cid)
    local level = player:getLevel()
    local maglevel = player:getMagicLevel()
    min = -((level / 5) + (maglevel * 0.5) + 5)
    max = -((level / 5) + (maglevel * 0.9) + 7)
    condition:setParameter(CONDITION_PARAM_PERIODICDAMAGE, math.random(min,max))
    combat:setCondition(condition)
    end
 
function onCastSpell(creature, var)
    CastSpell(creature:getId(), var)
    return combat:execute(creature, var)
end

Again, this script works perfect. Using it in-game creates a black smoke wave that does instant death damage and curses the target for 20 turns, doing damage every 1.5 seconds. I liked the spell, so I decided to make another one, but poison style this time. I made another script, literally copy and pasted my working one and then edited the magic effect, damage type:

Code:
Code:
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_POISONDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_INSECTS)

local area = createCombatArea(AREA_CIRCLE3X3)
combat:setArea(area)

local condition = Condition(CONDITION_POISON)
condition:setTicks(40000)
condition:setParameter(CONDITION_PARAM_DELAYED, 1)
condition:setParameter(CONDITION_PARAM_TICKINTERVAL, 1500)

function onGetFormulaValues(cid, level, maglevel)
    min = -((level / 5) + (maglevel * 3) + 5)
    max = -((level / 5) + (maglevel * 4) + 7)
    return min, max
end

combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues")

function CastSpell(cid, var)
    local player = Player(cid)
    local level = player:getLevel()
    local maglevel = player:getMagicLevel()
    min = -((level / 5) + (maglevel * 0.5) + 5)
    max = -((level / 5) + (maglevel * 0.9) + 7)
    condition:setParameter(CONDITION_PARAM_PERIODICDAMAGE, math.random(min,max))
    combat:setCondition(condition)
    end
 
function onCastSpell(creature, var)
    CastSpell(creature:getId(), var)
    return combat:execute(creature, var)
end

Once I had this script created, I made sure to add it into spells.xml, then I tested it out. When I used it in-game, the spell graphically was correct. A radius 3 AoE bomb of insects. Whatever the spell hit took instant damage, BUT WAS NOT POISONED. I thought I made a mistake in the lua script, double checked it, re-copy and pasted it, double checked spells.xml, all normal. Same issue.

Then, I tried something: I deleted the first spell, the death/curse one, the working one. Then I restarted the server, and tried the poison spell. It worked. I re-added the death spell, restarted the server, and the death spell worked, but the poison did not.

I have no idea why, but it seems as though something is not allowing me to have more than 1 of this type of spell. I know my code/script for the spells themselves are fine because when I have only one added, they both work perfectly. It's when I have both that only one fully works (by fully working I mean it adds the condition to the target).

Also, I'm not getting any errors coming up in the CMD window, and I haven't exceeded the maximum number of allowed spells. Again, as it says in the title, TFS 1.0.

Thanks
 
Last edited:
Try this:

Code:
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_POISONDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_INSECTS)
local area = createCombatArea(AREA_CIRCLE3X3)
combat:setArea(area)

local condition = Condition(CONDITION_POISON)
condition:setParameter(CONDITION_PARAM_DELAYED, 1)

function onGetFormulaValues(cid, level, maglevel)
    min = -((level / 5) + (maglevel * 4) + 7)
    max = -((level / 5) + (maglevel * 4.5) + 9)
    return min, max
end

combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues")

local ticks = 1.5 
local repeats = 10 

function onCastSpell(creature, var)
    local value = (creature:getMagicLevel() * 0.9) + (creature:getLevel()/5)
    condition:addDamage(repeats, ticks * 1000, -value)
    combat:setCondition(condition)
    return combat:execute(creature, var)
end
 
It does indeed work...partially.

Using that spell creates the AoE, does instant damage, and creates the condition (which is awesome thanks!), but no matter what level or magic level the player is, the poison damage is always 110 per turn. For example, the test character I used has level 61 and ML 42, the damage should be 12 per turn not 110.

Does anyone know how to modify the above script so that the condition damage is based on a min/max formula (similar to the instant damage?) My best attempt at modifying the above script is

Code:
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_POISONDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_INSECTS)
local area = createCombatArea(AREA_CIRCLE3X3)
combat:setArea(area)

local condition = Condition(CONDITION_POISON)
condition:setParameter(CONDITION_PARAM_DELAYED, 1)

function onGetFormulaValues(cid, level, maglevel)
    min = -((level / 5) + (maglevel * 4) + 7)
    max = -((level / 5) + (maglevel * 4.5) + 9)
    return min, max
end

combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues")


function CastSpell(cid, var)
    local player = Player(cid)
    local level = player:getLevel()
    local maglevel = player:getMagicLevel()
    min = -((level / 5) + (maglevel * 0.8) + 5)
    max = -((level / 5) + (maglevel * 0.9) + 7)
    condition:setParameter(CONDITION_PARAM_PERIODICDAMAGE, math.random(min,max))
    combat:setCondition(condition)
end
    
local ticks = 1.5
local repeats = 10
local value = ??? -- <-- I don't know what to put here so that the condition damage determined by ^^^ the above formula
  
function onCastSpell(creature, var)
    condition:addDamage(repeats, ticks * 1000, -value)
    combat:setCondition(condition)
    return combat:execute(creature, var)
end

I get stuck on how to make the local value pull its number from the min/max formula.
 
the poison damage is always 110 per turn

You miss 1 local

Code:
local function CastSpell(cid, var)

Someone who made this script made terrible mistake. This spell itself is executing CastSpell function and keeps it in memory until you restart server or reload spells becouse it haven't local before. So, it keeps values of the first player who casted it. If you use it with GM with 1000lv/1000mlv for the first time after server start, then every player (even level 1) will do as many dmg as your GM with 1000 lvl. Remember, every function except main function onCastSpell must have local before it if they are using any variables inside, like cid etc.

Frist code you posted is ok, except for missing local. It's a must fixing it, this is MAJOR bug. It may fk up many things on your server.
 
Last edited:
Andu thanks so much for responding!

Just wanted to make a couple clarifications.
the poison damage is always 110 per turn. You miss 1 local. Someone who made this script made terrible mistake.

So you are referring to the script that Dawg posted? Then the correct script should be:
Code:
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_POISONDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_INSECTS)
local area = createCombatArea(AREA_CIRCLE3X3)
combat:setArea(area)

local condition = Condition(CONDITION_POISON)
condition:setParameter(CONDITION_PARAM_DELAYED, 1)

function onGetFormulaValues(cid, level, maglevel)
    min = -((level / 5) + (maglevel * 4) + 7)
    max = -((level / 5) + (maglevel * 4.5) + 9)
    return min, max
end

combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues")

local ticks = 1.5
local repeats = 10

local function onCastSpell(creature, var)
    local value = (creature:getMagicLevel() * 0.9) + (creature:getLevel()/5)
    condition:addDamage(repeats, ticks * 1000, -value)
    combat:setCondition(condition)
    return combat:execute(creature, var)
end

Pleas let me know if that is the right place to add the "local"

Frist code you posted is ok, except for missing local. It's a must fixing it, this is MAJOR bug. It may fk up many things on your server.
Are you referring to the code in my very first post?


2nd question, do you know anything about my first question, why does it not allow me to have both spells on my server, but if I add them separately they both work fine?

3rd question, do you know how to modify the code from my 2nd post so that the condition damage is dependent on a min/max formula?

If you don't know, that's ok :) Thanks a ton for the help you already gave!!
 
Solution for 1,2,3:

change
Code:
function CastSpell(cid, var)
with
Code:
local function CastSpell(cid, var)
in top-most script in this topic and add return true before this function's end
 
Solved.
Andu, good sir, you are the man.

Just to make it easy for anyone who has the same problem, the following scripts now work:
Code:
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_POISONDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_INSECTS)

local area = createCombatArea(AREA_CIRCLE3X3)
combat:setArea(area)

local condition = Condition(CONDITION_POISON)
condition:setTicks(40000)
condition:setParameter(CONDITION_PARAM_DELAYED, 1)
condition:setParameter(CONDITION_PARAM_TICKINTERVAL, 1300)

function onGetFormulaValues(cid, level, maglevel)
    min = -((level / 5) + (maglevel * 4) + 7)
    max = -((level / 5) + (maglevel * 4.5) + 9)
    return min, max
end

combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues")

local function CastSpell(cid, var)
    local player = Player(cid)
    local level = player:getLevel()
    local maglevel = player:getMagicLevel()
    min = -((level / 5) + (maglevel * 0.8) + 5)
    max = -((level / 5) + (maglevel * 0.9) + 7)
    condition:setParameter(CONDITION_PARAM_PERIODICDAMAGE, math.random(min,max))
    combat:setCondition(condition)
    return true
    end
 
function onCastSpell(creature, var)
    CastSpell(creature:getId(), var)
    return combat:execute(creature, var)
end

and

Code:
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_DEATHDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_BLACKSMOKE)

local area = createCombatArea(AREA_WAVE2)
combat:setArea(area)

local condition = Condition(CONDITION_CURSED)
condition:setTicks(40000)
condition:setParameter(CONDITION_PARAM_DELAYED, 1)
condition:setParameter(CONDITION_PARAM_TICKINTERVAL, 1300)

function onGetFormulaValues(cid, level, maglevel)
    min = -((level / 5) + (maglevel * 4) + 7)
    max = -((level / 5) + (maglevel * 4.5) + 9)
    return min, max
end

combat:setCallback(CALLBACK_PARAM_LEVELMAGICVALUE, "onGetFormulaValues")

local function CastSpell(cid, var)
    local player = Player(cid)
    local level = player:getLevel()
    local maglevel = player:getMagicLevel()
    min = -((level / 5) + (maglevel * 0.8) + 5)
    max = -((level / 5) + (maglevel * 0.9) + 7)
    condition:setParameter(CONDITION_PARAM_PERIODICDAMAGE, math.random(min,max))
    combat:setCondition(condition)
    return true
    end
 
function onCastSpell(creature, var)
    CastSpell(creature:getId(), var)
    return combat:execute(creature, var)
end

both are fully functional, they do instant damage AND apply a condition, both of which can be independently modified with a min/max formula.
Seriously can't thank you enough, never would have remembered that about local functions!!

Ignore the other scripts/codes I posted, they are not necessary anymore.
 
Last edited:
Change
Code:
local function CastSpell(cid, var)
   local player = Player(cid)
    local level = player:getLevel()
    local maglevel = player:getMagicLevel()
    min = -((level / 5) + (maglevel * 0.8) + 5)
    max = -((level / 5) + (maglevel * 0.9) + 7)
    condition:setParameter(CONDITION_PARAM_PERIODICDAMAGE, math.random(min,max))
    combat:setCondition(condition)
    end
to, with missing return true
Code:
local function CastSpell(cid, var)
   local player = Player(cid)
    local level = player:getLevel()
    local maglevel = player:getMagicLevel()
    min = -((level / 5) + (maglevel * 0.8) + 5)
    max = -((level / 5) + (maglevel * 0.9) + 7)
    condition:setParameter(CONDITION_PARAM_PERIODICDAMAGE, math.random(min,max))
    combat:setCondition(condition)
    return true
end

And it will be 100% fixed. I'm glad I could help you.
 
Ah yes sorry I forgot to add that! Edited the code in my previous post. Still appears to work without it though...why is that?
 
Returns are signals for programs that function successfuly finished thier tasks. In OTS scripting most common are return true and return false.
For example function onCastSpell with return true will send signal that function finished his task and now OTS have to remove player's mana. But if the same function will have return false, then player will have msg "sorry not possible" and he will not lose mana for casting this spell, for example if target is too far.

Example:
Code:
function onCastSpell()
    if distance < 5 then
        return true -- if distance is smaller then 5, then player will lose mana for casting this spell
    else
        return false -- but is distance is 5 or larger, then he will not lose mana and he will get msg like "sorry not possible"
    end
    return true -- is very good idea to make a habit to write return true at end of function, you may avoid some major bugs with it
end

Other example, you are asking seller how much you have to pay for your groceries and you will get your price in return:
Code:
function priceQuestion()
     local price = bananaPrice + applePrice + tomatoPrice
     return price
end
You can write it in the other way, both codes will do exactly the same:
Code:
local price = 0
function priceQuestion()
     price = bananaPrice + applePrice + tomatoPrice
     return true
end

But if you forgot your return in function like here:
Code:
function priceQuestion()
     local price = bananaPrice + applePrice + tomatoPrice
end
Your seller will stay and look at you and he wont tell you price. To avoid such bug, you have to add return. In some situations in OTS, forgotting return true and local may cause major bugs.
 
Last edited:
That makes sense, thank you. I didn't know anything about scripting or codes before Open Tibia, I have been teaching myself ever since I got interested in Open Tibia, still have a lot to learn!
 
1)You don't need to return true or false in created functions.

2) Sometimes you can also just return the last thing you want to do such as an action script:

Code:
function onUse(player, item, fromPosition, target, toPosition, isHotkey)
          if player:getLevel() < 5 then
               return player:sendCancelMessage("You must be level 5")
          end
return true
end

If I had used return false after using the cancel message the player would only be able to see "You cannot use this" rather than my edited message of "you must be level 5")


To simplify look at it like this....

Code start

your custom function --Doesnt require a return value (but you can use them)

more code

return here

code end
 
Other example, you are asking seller how much you have to pay for your groceries and you will get your price in return:
Code:
function priceQuestion()
local price = bananaPrice + applePrice + tomatoPrice
return price
end
You can write it in the other way, both codes will do exactly the same:
Code:
local price = 0
function priceQuestion()
price = bananaPrice + applePrice + tomatoPrice
return true
end

This is wrong....

The first code ends with

return price

Which means the function will act as a number (the number that price is) and you can do something like this:
Code:
if priceQuestion() == 5 then

However if you put

return true

and tried to do the same thing there would be an error like:

Error: You are trying to compare a boolean with a number.

The code would be read as:

Code:
if true == 5 then

The best way to go about this would be:

Code:
function createPrice(x, y, z)
price = x + y + z
end

if not price then return false end

That way if price was undetermined somehow the code wont continue.
 
Last edited:
Right. Frist code:
Code:
local price = 0
function priceQuestion()
local price = bananaPrice + applePrice + tomatoPrice
return {price = price}
end
And logic if price > 5 then blabla.
I understand it. Most important for me at that time was to teach how important is return in functions.
 
My main point is you are creating a function that sets a global price thats stored outside of the function...

What I was saying is, the way you return the function matters because if you use a return the function will turn into a variable rather then just executing...

If you put return true it will be a boolean, if you put return price it will be a number, if you put return "hello" it will be a string...

If you dont use a return, it will just execute what ever you wanted.

Code:
price = 0
function priceQuestion()
price = bananaPrice + applePrice + tomatoPrice
end

Would be the most memory efficient.
 
Back
Top