• 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 Teach me about spells.

  • Thread starter Thread starter Xikini
  • Start date Start date
X

Xikini

Guest
The TFS I use is 0.3.7_svn
However, I don't believe the TFS version I'm using is applicable to this question.

-------------------------------------------

I've always avoided spells because I don't understand them.

Everytime I sit down and try to understand them, it's like walking through a muddy river with stepping stones.
You can see the stepping stones, but if you slip off of the path, it's dark unknowing water that don't make sense.

My biggest problem is the parameter 'var'.
I've been told in the past that this is a very complex parameter that holds multitudes of data.

However this exact same parameter is cast off into space and never used when you go and use it in the weird functions above, that don't operate like a regular Lua function.

The functions above for combat have no apparent start or end, and you can paste whole lines of the same code over and over, and yet they don't collide or interact with each other.

Hell, you can even move those lines into an entirely different part of the script and they still work.

The entire spell section is a big giant mystery for me.

------------------------------------------

I'm here to ask some kind soul to do their best and break down a spell, from top to bottom, and explain it me.

My current questions;

Why is var called upon when completing a spell to calculate damages, yet var itself just disappears into thin air when you look up at the spell configuration above? (cid, combat, var)

What is var? What information does it hold?

-------------------------------------------------

I'd prefer if someone explained it like talking to an infant, but I am resourceful, and I'm not afraid to ask more direct questions once I get the initial grasp of the idea.

Thanks in advance!

Xikini

(I also have another unanswered question, if you are feeling extra generous. Lua - 0.3.7 Checking time for distance effect to reach it's intended destination)
 
So var in general is just a basic, poorly designed struct called LuaVariant and what it actually "holds" is determined by the context. it is used to pass "complex" data in between lua and the engine, mostly with spells and weapons. The implementation is:
C++:
struct LuaVariant
{
    LuaVariantType_t type;
    std::string text;
    PositionEx pos;
    uint32_t number;
};
as luascript.h is saying. Where the "type" might be:
C++:
enum LuaVariantType_t
{
    VARIANT_NONE = 0,
    VARIANT_NUMBER,
    VARIANT_POSITION,
    VARIANT_TARGETPOSITION,
    VARIANT_STRING
};

I don't quite understand the first question tho, the var there is just to pass the data about the target (if target spell) and position.
Take a look at CombatSpell::castSpell(Creature* creature, Creature* target) or CombatSpell::castSpell(Creature* creature) to peek at what exactly is going on there. It's nothing complicated really
 
So var in general is just a basic, poorly designed struct called LuaVariant and what it actually "holds" is determined by the context. it is used to pass "complex" data in between lua and the engine, mostly with spells and weapons. The implementation is:
C++:
struct LuaVariant
{
    LuaVariantType_t type;
    std::string text;
    PositionEx pos;
    uint32_t number;
};
as luascript.h is saying. Where the "type" might be:
C++:
enum LuaVariantType_t
{
    VARIANT_NONE = 0,
    VARIANT_NUMBER,
    VARIANT_POSITION,
    VARIANT_TARGETPOSITION,
    VARIANT_STRING
};

I don't quite understand the first question tho, the var there is just to pass the data about the target (if target spell) and position.
Take a look at CombatSpell::castSpell(Creature* creature, Creature* target) or CombatSpell::castSpell(Creature* creature) to peek at what exactly is going on there. It's nothing complicated really
I haven't learned anything about c / c++ /c# , so looking through the source code is an altogether strange beast for me.

Going this the above information though,
What do each of the variants hold? The values seems extremely vague, and I'm guessing that's on purpose.

_NONE -> Why would 'nothing' need to be declared?
_NUMBER -> What number is this holding? Multiple? Spell costs?
_POSITION -> Is defaulted to the position in front of the caster? Under the caster? Does it depend on the values set in spells.lua?
_TARGETPOSITION -> Does this get cid's target and find their position?
_STRING -> I can't even guess what this value holds. xP

As for the first question..
Any spell such as this random one from on this forum.
LUA:
local combat = createCombatObject()
setCombatParam(combat, COMBAT_PARAM_TYPE, COMBAT_ICEDAMAGE)
setCombatParam(combat, COMBAT_PARAM_EFFECT, CONST_ME_ICETORNADO)
setAttackFormula(combat, COMBAT_FORMULA_LEVELMAGIC, 2, 4, 3, 10)
local area = createCombatArea(AREA_CROSS5X5)
setCombatArea(combat, area)

function onCastSpell(cid, var)
    return doCombat(cid, combat, var)
end
var is being used in the combat object called "combat"
But, it's like cid and var both just disappear, and are never used, but they clearly are being used, as how else would you know where the damage is going?
Do I need to re-create them inside "combat" somewhere, if I wanted to use them in some special function? How would I even go about doing that?
Or can I just put the entire combat section inside of the onCast function, and then reuse it that way? (I've sort of tried it liked this in the past, but the script got pretty angry with me, as if I wasn't declaring something correctly.)
 
So let me be very general here.
The var structure is generic, it might be used for different purposes and hold different data.
The NONE is just "empty" var state, consider it as invalid. Number is mostly used for target id or basically cid (that stands for creature id), the position is, the position of spell (probably used differently for different type of spells), targetposition is a type where you have both number and position, and string as far as I know is used for example in param spells (exura sio "THISWILLBEINSTRING). This is mostly used internally, and as you see so far it has nothing to do with the damage or basically any combat info.
The var is used, as doCombat function parameter. And there internally it will be resolved into the above values and pasted deeper into the engine, so for example if you have an area spell, the doCombat func will extract the position from the var parameter and pass it to the actual "combat generating" function. Where is the actual damage generated is a topic for a whole other occasion, if you are intrested just study the combat.cpp file, but the logic for that is kinda confusing in TFS.
You can't create Combat object inside a function because it needs to be constant, that's why you are only able to use that func while script is loading and it is actually prohibited inside the CreateCombatObject method.
So the flow kinda is: Engine startup -> Load all the scripts -> Create those combat objects -> Use them durning events (onCastSpell).
So basically what your doCombat is doing, is that it tells the engine that this particular guy (cid) would like to use this particular Combat object (combat, that is the range, effect, damage formulas, damage type etc), with those extra variables (var, that is the position and maybe the target). Engine eats that data and calculates/produces the end effect
 
So let me be very general here.
The var structure is generic, it might be used for different purposes and hold different data.
The NONE is just "empty" var state, consider it as invalid. Number is mostly used for target id or basically cid (that stands for creature id), the position is, the position of spell (probably used differently for different type of spells), targetposition is a type where you have both number and position, and string as far as I know is used for example in param spells (exura sio "THISWILLBEINSTRING). This is mostly used internally, and as you see so far it has nothing to do with the damage or basically any combat info.
The var is used, as doCombat function parameter. And there internally it will be resolved into the above values and pasted deeper into the engine, so for example if you have an area spell, the doCombat func will extract the position from the var parameter and pass it to the actual "combat generating" function. Where is the actual damage generated is a topic for a whole other occasion, if you are intrested just study the combat.cpp file, but the logic for that is kinda confusing in TFS.
You can't create Combat object inside a function because it needs to be constant, that's why you are only able to use that func while script is loading and it is actually prohibited inside the CreateCombatObject method.
So the flow kinda is: Engine startup -> Load all the scripts -> Create those combat objects -> Use them durning events (onCastSpell).
So basically what your doCombat is doing, is that it tells the engine that this particular guy (cid) would like to use this particular Combat object (combat, that is the range, effect, damage formulas, damage type etc), with those extra variables (var, that is the position and maybe the target). Engine eats that data and calculates/produces the end effect
Okay this definitely clears things up.

let's do a follow up question to my other question.

In the example above, we are telling the combat system to attack all of the target's on those tiles.
Is there anyway to find the target's who are about to be hit with the spell, and add like a function in there?
like..
LUA:
if getCreaturehealth(target) < 300 then
    min = 100
    max = 200
end
return min, max

Or what if I wanted to have a secondary combat damage to happen 10% of the time, or only to the first 3 creatures on the tiles?
Since var is only in the onCastSpell, how could I target these players/creatures individually?

I feel like both of these things are wanting the same thing, but if the combats can only be created when we start up, does that mean it's impossible?
 
number: you are casting a spell which does not have a combatArea set and you have a target selected
position: you are using a spell which does have an area and contains the position of one of these three (in order):
1) you have a target selected, the position will be the target's position
2) your spell has the tag "needsDirection"; position will be the next position in the direction you're looking in
3) none of the above apply; position is the caster's position
string: you are using a spell such as exura sio "string here

Okay this definitely clears things up.

let's do a follow up question to my other question.

In the example above, we are telling the combat system to attack all of the target's on those tiles.
Is there anyway to find the target's who are about to be hit with the spell, and add like a function in there?
like..
LUA:
if getCreaturehealth(target) < 300 then
    min = 100
    max = 200
end
return min, max

Or what if I wanted to have a secondary combat damage to happen 10% of the time, or only to the first 3 creatures on the tiles?
Since var is only in the onCastSpell, how could I target these players/creatures individually?

I feel like both of these things are wanting the same thing, but if the combats can only be created when we start up, does that mean it's impossible?
with the current way spells are right now, i don't think there's a way to only attack x number of creatures on a specific tile (MAYBE with onTargetCreature callback parameter but i don't know how you'd use a counter without possibly interfering with other counters when multiple players are casting it)
about a secondary combat that happens 10% of the time:
LUA:
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_GROUNDSHAKER)
combat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
combat:setParameter(COMBAT_PARAM_USECHARGES, true)
combat:setArea(createCombatArea(AREA_CIRCLE3X3))

-- combat that happens 10% of the time
local secondaryCombat = Combat()
secondaryCombat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
secondaryCombat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_GROUNDSHAKER)
secondaryCombat:setParameter(COMBAT_PARAM_BLOCKARMOR, true)
secondaryCombat:setParameter(COMBAT_PARAM_USECHARGES, true)
secondaryCombat:setArea(createCombatArea(AREA_CIRCLE3X3))

function onGetFormulaValues(player, skill, attack, factor)
    local min = (player:getLevel() / 5) + (skill * attack * 0.02) + 4
    local max = (player:getLevel() / 5) + (skill * attack * 0.03) + 6
    return -min, -max
end

combat:setCallback(CALLBACK_PARAM_TARGETCREATURE, "onTargetCreature")

function onCastSpell(creature, variant)
    if math.random(100) <= 10 then
        secondaryCombat:execute(creature, variant)
    end
    return combat:execute(creature, variant)
end
lastly, if you wanted to change the damage of a spell you can't do it within that spell directly, you'd have to use an onTargetCreature callback param and add a storage to the target if its health is < 300, then check for that storage in an onHealthChange/onStatsChange script and modify the damage if the creature has that storage
 
Back
Top