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

Spell [TFS 1.X] Thunder Storm

Leo32

Getting back into it...
Joined
Sep 21, 2007
Messages
987
Solutions
14
Reaction score
532
This spell is inspired by Thunder Storm from Diablo 2.
It's kind of an aura, where you cast the spell and it lasts 60 seconds (by default).
Every 5 seconds (by default), it strikes a monster on-screen.

Starts off at 1 target, gaining an extra target every 20 magic levels.

XhR7U0q.gif


spells.xml
XML:
<instant group="support" name="Thunder Storm" words="thunder storm" lvl="30" mana="250" prem="1" aggressive="0" script="support/thunderstorm.lua">
    <vocation name="..." />
</instant>

thunderstorm.lua
Lua:
local minTargets = 1
local thunderstorm = 28001 -- Storage used for thunderstorm cooldown
local duration = 60000 -- 60 Lasts seconds
local ticks = 5000 -- Hits every 5 seconds

function shuffle(t)
    local rand = math.random
    assert(t, "table.shuffle() expected a table, got nil")
    local iterations = #t
    local j
  
    for i = iterations, 2, -1 do
        j = rand(i)
        t[i], t[j] = t[j], t[i]
    end
end
                  
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_YELLOWENERGY)

-- For exhaust on the first cast
local exhaust = Condition(CONDITION_EXHAUST)
exhaust:setParameter(CONDITION_PARAM_TICKS, 2000)

function thunderStormDuration(playeruid, count)
    -- Convert back to Player object
    local player = Player(playeruid)
    if player then -- If player still logged in
        local playerPos = player:getPosition()
        playerPos:sendMagicEffect(CONST_ME_YELLOWENERGY)
        local playerTile = Tile(Position(playerPos))
        if playerTile:hasFlag(TILESTATE_PROTECTIONZONE) == false and playerTile:hasFlag(TILESTATE_HOUSE) == false then -- if player not in PZ
            local targets = Game.getSpectators(playerPos, false, false, 7, 7, 5, 5)
            -- Shuffle targets table
            if targets then
                if #targets > 1 then
                    shuffle(targets)
                end
            end
            -- Strike targets
            local validTargets = {}
            local level = player:getLevel()
            local maglevel = player:getMagicLevel()
            local targetCount = minTargets + math.floor(maglevel / 20) -- Get extra target every 20 magic levels
            for k,v in pairs(targets) do
                -- Only target monsters
                if v:isMonster() then
                    local monsterPos = v:getPosition()
                    if monsterPos:isSightClear(playerPos) then
                        local currentTargets = #validTargets or 0
                        if currentTargets < targetCount then
                            table.insert(validTargets, v)
                        end
                    end
                end
            end
            if validTargets then
                for i = 1,#validTargets do
                    -- Animation
                    local monsterPos = validTargets[i]:getPosition()
                    monsterPos:sendMagicEffect(CONST_ME_BIGCLOUDS)
                    monsterPos.y = monsterPos.y - 1
                    monsterPos:sendMagicEffect(CONST_ME_BIGCLOUDS)
                    monsterPos.y = monsterPos.y + 2
                    monsterPos:sendMagicEffect(CONST_ME_BIGCLOUDS)
                    monsterPos.x = monsterPos.x - 1
                    monsterPos.y = monsterPos.y - 1
                    monsterPos:sendMagicEffect(CONST_ME_BIGCLOUDS)
                    monsterPos.x = monsterPos.x + 2
                    monsterPos:sendMagicEffect(CONST_ME_BIGCLOUDS)
                    -- Damage Formula
                    local min = (level / 5) + (maglevel * 1.4) + 15
                    local max = (level / 5) + (maglevel * 2.0) + 35
                    combat:setFormula(COMBAT_FORMULA_LEVELMAGIC, 0, -min, 0, -max)  
                    combat:execute(player, Variant(validTargets[i].uid)) -- Strike Monster
                end
            end
        end
        -- Repeat until count reaches 0
        local counter = count - 1
        if counter > 0 then
            addEvent(thunderStormDuration, ticks, playeruid, counter)
        else
            player:setStorageValue(thunderstorm, -1)
        end
    else
        return false
    end
end

function onCastSpell(creature, var, isHotkey)
    local thunderstormExhaust = creature:getStorageValue(thunderstorm)
    if creature:getCondition(CONDITION_EXHAUST) then
        creature:sendTextMessage(MESSAGE_STATUS_SMALL, Game.getReturnMessage(RETURNVALUE_YOUAREEXHAUSTED))
        creature:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end
    if thunderstormExhaust ~= -1 then
        if thunderstormExhaust > os.time() then
            creature:sendTextMessage(MESSAGE_STATUS_SMALL, Game.getReturnMessage(RETURNVALUE_YOUAREEXHAUSTED))
            creature:getPosition():sendMagicEffect(CONST_ME_POFF)
            return false
        end
    end
    creature:setStorageValue(thunderstorm, os.time() + duration)
    creature:addCondition(exhaust) -- Only exhaust on original cast
    addEvent(thunderStormDuration, 0, creature.uid, math.floor(duration / ticks))
    return true
end

The only iffy part about this spell is the CONST_ME_BIGCLOUDS animation.
It only goes off in a checkerboard pattern (idk why cip made it that way).
Because of this, the animation area is a CROSS(+)

So some targets get a single cloud, whereas others are surrounded by four.
This could also be improved further by ignoring your own summoned creatures, but I don't really care for that at the moment.
 
Last edited:
Wow Leo those spells are sick!

I always wanted to make a spell like this but instead of damage to periodically set condition (for example bleeding or poison) for monsters but for it to scale with magic level. It's hard to find on this forum, and official spells all have set parameters. Would you have an idea how to make a condition with a normal spell formula instead of set damage?

Normal conditions are all coded something like this
Code:
local condition = Condition(CONDITION_BLEEDING)
condition:setParameter(CONDITION_PARAM_DELAYED, 10)
condition:addDamage(30, 2000, -25)
combat:addCondition(condition)

This would be sick, not only as a spell but even as a script for a weapon (for example poison arrows' poison scale off mlvl)

Cheers
 
Wow Leo those spells are sick!

I always wanted to make a spell like this but instead of damage to periodically set condition (for example bleeding or poison) for monsters but for it to scale with magic level. It's hard to find on this forum, and official spells all have set parameters. Would you have an idea how to make a condition with a normal spell formula instead of set damage?

Normal conditions are all coded something like this
Code:
local condition = Condition(CONDITION_BLEEDING)
condition:setParameter(CONDITION_PARAM_DELAYED, 10)
condition:addDamage(30, 2000, -25)
combat:addCondition(condition)

This would be sick, not only as a spell but even as a script for a weapon (for example poison arrows' poison scale off mlvl)

Cheers
Not a place for that, but how about casting combat x times with loop and addEvent?

@TOPIC
Cool, I like the scaling target amount the most :p
 
Not a place for that, but how about casting combat x times with loop and addEvent?

@TOPIC
Cool, I like the scaling target amount the most :p

Then I think it couldn't be healed by player by any condition healer (like exana pox)
I'm not really well versed in coding but perhaps you could
Code:
condition:addDamage(30, 2000, -25)
Change the "30" value to some local modifier where the formula is located
 
Then I think it couldn't be healed by player by any condition healer (like exana pox)
I'm not really well versed in coding but perhaps you could
Code:
condition:addDamage(30, 2000, -25)
Change the "30" value to some local modifier where the formula is located
On using spell set storage=1
on casting combat each time check if storage == 1
on curing spell set storage=0
boom
 
I always wanted to make a spell like this but instead of damage to periodically set condition (for example bleeding or poison) for monsters but for it to scale with magic level.

I mean sure.

Lua:
local combat = Combat()
combat:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
combat:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_DRAWBLOOD)

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

function onCastSpell(creature, var)

     -- Damage Formula
     local level = creature:getLevel()
     local maglevel = creature:getMagicLevel()
     local min = (level / 5) + (maglevel * 0.7) + 10
     local max = (level / 5) + (maglevel * 0.9) + 25
     local damage = math.random(min, max)
   
     -- Damage ticks
     local damageTable = {
        {2, -(damage)}, -- 2 ticks at full damage
        {4, -(damage / 2)}, -- 4 ticks at half damage
        {5, -(damage / 4)} -- five ticks at quarter damage
    }
    for i = 1, #damageTable do
        local t = damageTable[i]
        condition:addDamage(t[1], 4000, t[2])
    end
  
    -- Apply damage
    combat:addCondition(condition)
  
    -- Execute combat
    return combat:execute(creature, var)
end

You'd probably want to edit the damageTable ticks though.
Idk how long bleed lasts or how the damage reduces over time.

Should be posting these questions in the support board though.
 
Sorry brother you are right but I just felt inspired by this spell alone! Other animation and I will have myself a thorn aura of some sort.
Cheers, looking forwad for more of your wizardry
 
If player die or logout while spell then exhast is infinity. I put player:storageValue() -1 for my login.lua
 
Back
Top