• 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 Multi-Shot (For Monsters)

Shadowsong

Game Developer & Graphic Designer
Joined
Feb 23, 2010
Messages
3,446
Solutions
21
Reaction score
2,999
Location
Bosnia & Herzegovina
YouTube
ShivaShadowsong
Hey guys.

Felt like making a small contribution, so here's a spell for monsters that I just made.

Tested and Working on: TFS 0.3.6

Spell Description
Allows a monster to fire multiple projectiles at multiple targets in a single attack.
The monster will always fire at least 1 shot, at its primary target, and several more shots will follow if you configure it so. The shots are scripted so that each shot can shoot a target only once. This means that if there are 2 players nearby, and the monster can fire 5 shots, only 2 shots will be shot at those 2 players, and no more.

lU8NQgU.gif


Spell Configuration

The script has a configuration table where you can add configs for each monster that's gonna be using this ability. You can configure the range, the delay between each shot, damage, damage type, shoot and magic effect and how many shots you want them to fire.

It can be found on line 7 of the script, under local configs:

["Lava Abomination"] = {
mindmg = -300, -- Minimum damage (don't forget that this number has to be negative)
maxdmg = -575, -- Maximum damage (don't forget that this number has to be negative)
dmgtype = COMBAT_FIREDAMAGE, -- Damage type (for available types, check your Constants library)
shooteff = 3, -- Number of distance effect that will be shot (can also be a constant)
splasheff = 187, -- Number of magic effect that will be displayed when the target is shot (can also be a constant)
delay = 200, -- Delay at which multiple shots will be fired. In miliseconds.
shots = 2, -- How many shots will the monster fire per attack?
shootrange = 6 -- Range at which the monster scans for targets and shoots the shots.
}


Spell Example
I made this spell for a monster called Lava Abomination. This monster is made out of magma and has 2 heads, so I figured - why not make him fire 1 shot from each of his heads? Other examples include .. maybe a hunter monster that has a special bow which fires several arrows at once? It's up to your imagination.

latest

Setting the spell up

Go to your monster's xml file, and under the attacks tag, add the following:

Code:
<attack name="Multishot" interval="2000" chance="100"/>

Go to spells/spells.xml and add this:
Code:
    <instant name="Multishot" words="msformonsters" lvl="999" mana="999" prem="0" range="6" casterTargetOrDirection="1" blockwalls="1" exhaustion="2000" needlearn="0" event="script" value="monster/multishot.lua">
        <vocation id="0"/>
    </instant>

In spells/monsters/ create a script file called multishot.lua and paste this code into it:
Code:
--- Multishot Monster Spell ---
--- by ShivaShadowsong -----
--- For TFS 0.3.6 ------------

function onCastSpell(cid, var)

local configs = {
    ["Lava Abomination"] = {
        mindmg = -300, -- Minimum damage (don't forget that this number has to be negative)
        maxdmg = -575, -- Maximum damage (don't forget that this number has to be negative)
        dmgtype = COMBAT_FIREDAMAGE, -- Damage type (for available types, check your Constants library)
        shooteff = 3, -- Number of distance effect that will be shot (can also be a constant)
        splasheff = 187, -- Number of magic effect that will be displayed when the target is shot (can also be a constant)
        delay = 200, -- Delay at which multiple shots will be fired. In miliseconds.
        shots = 2, -- How many shots will the monster fire per attack?
        shootrange = 6 -- Range at which the monster scans for targets and shoots the shots.
    }
}

    ---- Don't touch anything below unless you know what you're doing ----

    local victimtable, pass = {}, false
    local self = configs[getCreatureName(cid)]
    if self then
        if getCreatureTarget(cid) ~= 0 then
            local target, tpos, victims, shotsfired = getCreatureTarget(cid), getThingPos(getCreatureTarget(cid)), getSpectators(getThingPos(cid), self.shootrange, self.shootrange), 0
          
                for i = 1, self.shots do
                    if shotsfired < self.shots then
                        if shotsfired == 0 then -- First shot will always be fired at the primary target, which is a mandatory target in order to use the spell
                            table.insert(victimtable, target)
                        else
                      
                            for i=1,#victims do
                                pass = false
                              
                                if not(isInArray(victimtable,victims[i])) then
                                    if isPlayer(victims[i]) then
                                        if getPlayerGroupId(victims[i]) < 3 then
                                            pass = true
                                        end
                                    else
                                        if isPlayer(getCreatureMaster(victims[i])) then
                                            pass = true
                                        end
                                    end
                                end
                          
                                if pass == true then
                                        target = victims[i]
                                        table.insert(victimtable, target)
                                        break
                                end

                            end
                        end
                        shotsfired = shotsfired + 1
                    end
                end
              
                for v = 1, #victimtable do
                    addEvent(function()
                        if isCreature(victimtable[v]) and isCreature(victimtable[v]) then
                            if getDistanceBetween(getThingPos(cid), getThingPos(victimtable[v])) <= self.shootrange and isSightClear(getThingPos(cid), getThingPos(victimtable[v]), false) then
                                doSendDistanceShoot(getThingPos(cid), getThingPos(victimtable[v]), self.shooteff)
                                doTargetCombatHealth(cid, victimtable[v], self.dmgtype, self.mindmg, self.maxdmg, self.splasheff)
                            end
                        end
                    end, (v-1)*self.delay)
                end
              
        end
    end  
return true
end

Hope you like it. Let me know if there are any bugs with it.
I'm not the best scripter around, so i'm sure there's some area for improvement, but for now, it is what it is.

Enjoy.
 
it was towards those who wanted it for 1.2, you did good enough for 0.x :p
 
How to edit this for TFS 1.2 and for player's spell? :) Could someone do this?
 
Awesome spell, thanks man!

This worked on my TFS 1.1, so probably works on 1.2.

Code:
<instant name="Multishot" words="###50" aggressive="1" blockwalls="1" needtarget ="1" needlearn="1" script="monster/multishot.lua">

Code:
--- Multishot Monster Spell ---
--- by ShivaShadowsong -----
--- For TFS 0.3.6 ------------
function onCastSpell(cid, var)
local configs = {
    ["Lava Abomination"] = {
        mindmg = -500, -- Minimum damage (don't forget that this number has to be negative)
        maxdmg = -1000, -- Maximum damage (don't forget that this number has to be negative)
        dmgtype = COMBAT_FIREDAMAGE, -- Damage type (for available types, check your Constants library)
        shooteff = 3, -- Number of distance effect that will be shot (can also be a constant)
        splasheff = 187, -- Number of magic effect that will be displayed when the target is shot (can also be a constant)
        delay = 200, -- Delay at which multiple shots will be fired. In miliseconds.
        shots = 2, -- How many shots will the monster fire per attack?
        shootrange = 6 -- Range at which the monster scans for targets and shoots the shots.
    }
}
    ---- Don't touch anything below unless you know what you're doing ----
    local victimtable, pass = {}, false
    local self = configs[getCreatureName(cid)]
    if self then
        if getCreatureTarget(cid) ~= 0 then
            local target, tpos, victims, shotsfired = getCreatureTarget(cid), getThingPos(getCreatureTarget(cid.uid)), getSpectators(getThingPos(cid.uid), self.shootrange, self.shootrange), 0
        
                for i = 1, self.shots do
                    if shotsfired < self.shots then
                        if shotsfired == 0 then -- First shot will always be fired at the primary target, which is a mandatory target in order to use the spell
                            table.insert(victimtable, target)
                        else
                    
                            for i=1,#victims do
                                pass = false
                            
                                if not(isInArray(victimtable,victims[i])) then
                                    if isPlayer(victims[i]) then
                                        if getPlayerGroupId(victims[i]) < 3 then
                                            pass = true
                                        end
                                    else
                                        if isPlayer(getCreatureMaster(victims[i])) then
                                            pass = true
                                        end
                                    end
                                end
                        
                                if pass == true then
                                        target = victims[i]
                                        table.insert(victimtable, target)
                                        break
                                end
                            end
                        end
                        shotsfired = shotsfired + 1
                    end
                end
            
                for v = 1, #victimtable do
                    addEvent(function()
                        if isCreature(victimtable[v]) and isCreature(victimtable[v]) then
                            if getDistanceBetween(getThingPos(cid.uid), getThingPos(victimtable[v])) <= self.shootrange and isSightClear(getThingPos(cid.uid), getThingPos(victimtable[v]), false) then
                                doSendDistanceShoot(getThingPos(cid.uid), getThingPos(victimtable[v]), self.shooteff)
                                doTargetCombatHealth(cid, victimtable[v], self.dmgtype, self.mindmg, self.maxdmg, self.splasheff)
                            end
                        end
                    end, (v-1)*self.delay)
                end
            
        end
    end 
return true
end
 
Shiva has updated and re-written the code, even with new TODOs, so I wanted to share it. Thanks @Shadowsong!

Lua:
--- Multishot Monster Spell ---
--- by ShivaShadowsong -----
--- For TFS 0.3.6 ------------

local function canTargetVictim(caster, victim)
    -- Don't target the caster.
    if caster == victim then
        return false
    end

    local master = getCreatureMaster(caster)
    if master ~= caster then
        -- Don't target the creature if their master is the same as mine.
        if master == getCreatureMaster(victim) then
            return false
        end
    end

    if isPlayer(victim) then
        -- Don't target privileged players.
        if getPlayerGroupId(victim) >= 3 then
            return false
        end
        -- TODO: Don't target any players at all if my master is a player and doesn't have PVP turned on?
        -- ...
    end

    -- Don't target Npcs.
    if isNpc(victim) then
        return false
    end

    return true
end

function onCastSpell(cid, var)
    local configs = {
            ["Innate Familiar"] = {
                    mindmg = -500, -- Minimum damage (don't forget that this number has to be negative)
                    maxdmg = -1000, -- Maximum damage (don't forget that this number has to be negative)
                    dmgtype = COMBAT_FIREDAMAGE, -- Damage type (for available types, check your Constants library)
                    shooteff = 3, -- Number of distance effect that will be shot (can also be a constant)
                    splasheff = 187, -- Number of magic effect that will be displayed when the target is shot (can also be a constant)
                    delay = 200, -- Delay at which multiple shots will be fired. In miliseconds.
                    shots = 2, -- How many shots will the monster fire per attack?
                    shootrange = 6 -- Range at which the monster scans for targets and shoots the shots.
            }
    }

    ---- Don't touch anything below unless you know what you're doing ----
    local self = configs[getCreatureName(cid)]
    if ((not self) or (getCreatureTarget(cid) == 0)) then
        return true
    end

    local victims = getSpectators(getThingPos(cid.uid), self.shootrange, self.shootrange) -- List of potential victims to scan through.
    local victimtable = {} -- Will hold an array of all creatures on which we've decided to fire.

    -- First shot will always be fired at the primary target, which is a mandatory target to have in order for this ability to have been used.
    local shotsLeft = self.shots - 1
    table.insert(victimtable, getCreatureTarget(cid))

    -- Form a table of all other creatures who will be shot according to how many shots there are left to fire.
    if shotsLeft > 0 then
        for _, victim in pairs(victims) do
            if shotsLeft > 0 then
                local canTarget = canTargetVictim(cid, victim)
                if (not (isInArray(victimtable, victim)) and canTarget) then
                    table.insert(victimtable, victim)
                    shotsLeft = shotsLeft - 1
                end
            else
                break
            end
        end
    end

    -- Fire the shots.
    for _, victim in pairs(victimtable) do
        local shotsFired = 0
        addEvent(function()
            if isCreature(cid) and isCreature(victim) then
                if getDistanceBetween(getThingPos(cid.uid), getThingPos(victim)) <= self.shootrange and isSightClear(getThingPos(cid.uid), getThingPos(victim), false) then
                    doSendDistanceShoot(getThingPos(cid.uid), getThingPos(victim), self.shooteff)
                    doTargetCombatHealth(cid,    victim, self.dmgtype,    self.mindmg, self.maxdmg,    self.splasheff)
                else
                    -- TODO: Play some poof effect or display some message if the target got out of range/out of sight before the shot was fired?
                end
            end
        end, shotsFired * self.delay)
    end

    return true
end
 
Shiva has updated and re-written the code, even with new TODOs, so I wanted to share it. Thanks @Shadowsong!

Lua:
--- Multishot Monster Spell ---
--- by ShivaShadowsong -----
--- For TFS 0.3.6 ------------

local function canTargetVictim(caster, victim)
    -- Don't target the caster.
    if caster == victim then
        return false
    end

    local master = getCreatureMaster(caster)
    if master ~= caster then
        -- Don't target the creature if their master is the same as mine.
        if master == getCreatureMaster(victim) then
            return false
        end
    end

    if isPlayer(victim) then
        -- Don't target privileged players.
        if getPlayerGroupId(victim) >= 3 then
            return false
        end
        -- TODO: Don't target any players at all if my master is a player and doesn't have PVP turned on?
        -- ...
    end

    -- Don't target Npcs.
    if isNpc(victim) then
        return false
    end

    return true
end

function onCastSpell(cid, var)
    local configs = {
            ["Innate Familiar"] = {
                    mindmg = -500, -- Minimum damage (don't forget that this number has to be negative)
                    maxdmg = -1000, -- Maximum damage (don't forget that this number has to be negative)
                    dmgtype = COMBAT_FIREDAMAGE, -- Damage type (for available types, check your Constants library)
                    shooteff = 3, -- Number of distance effect that will be shot (can also be a constant)
                    splasheff = 187, -- Number of magic effect that will be displayed when the target is shot (can also be a constant)
                    delay = 200, -- Delay at which multiple shots will be fired. In miliseconds.
                    shots = 2, -- How many shots will the monster fire per attack?
                    shootrange = 6 -- Range at which the monster scans for targets and shoots the shots.
            }
    }

    ---- Don't touch anything below unless you know what you're doing ----
    local self = configs[getCreatureName(cid)]
    if ((not self) or (getCreatureTarget(cid) == 0)) then
        return true
    end

    local victims = getSpectators(getThingPos(cid.uid), self.shootrange, self.shootrange) -- List of potential victims to scan through.
    local victimtable = {} -- Will hold an array of all creatures on which we've decided to fire.

    -- First shot will always be fired at the primary target, which is a mandatory target to have in order for this ability to have been used.
    local shotsLeft = self.shots - 1
    table.insert(victimtable, getCreatureTarget(cid))

    -- Form a table of all other creatures who will be shot according to how many shots there are left to fire.
    if shotsLeft > 0 then
        for _, victim in pairs(victims) do
            if shotsLeft > 0 then
                local canTarget = canTargetVictim(cid, victim)
                if (not (isInArray(victimtable, victim)) and canTarget) then
                    table.insert(victimtable, victim)
                    shotsLeft = shotsLeft - 1
                end
            else
                break
            end
        end
    end

    -- Fire the shots.
    for _, victim in pairs(victimtable) do
        local shotsFired = 0
        addEvent(function()
            if isCreature(cid) and isCreature(victim) then
                if getDistanceBetween(getThingPos(cid.uid), getThingPos(victim)) <= self.shootrange and isSightClear(getThingPos(cid.uid), getThingPos(victim), false) then
                    doSendDistanceShoot(getThingPos(cid.uid), getThingPos(victim), self.shooteff)
                    doTargetCombatHealth(cid,    victim, self.dmgtype,    self.mindmg, self.maxdmg,    self.splasheff)
                else
                    -- TODO: Play some poof effect or display some message if the target got out of range/out of sight before the shot was fired?
                end
            end
        end, shotsFired * self.delay)
    end

    return true
end

Just noticed a small oversight, after
shotsFired * self.delay)
there should be
shotsFired = shotsFired + 1
otherwise the delay between shots will not work.
 
Back
Top