• Vote in November Mapping Competition to select a winner!
  • 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]] Brutal Swing

Leo32

Getting back into it...
Joined
Sep 21, 2007
Messages
970
Solutions
14
Reaction score
461
Edit: Fked the title again šŸ¤”

This is a replacement for exori ico on my server.
It's basically the brutal strike spell, except;
  • you can lock on to targets when you're in range
  • it changes the hit area if the targeted monster is diagonal to you.
One-handed weapons have a smaller hit area compared to two-handed weapons.
Thrusting/spear/lance-type weapons use a different area altogether and do 150% damage on their two-handed types due to the smaller area.




CONST_ME_BLOCKHIT on walls for cool points

spells.xml
XML:
<instant group="attack" name="Brutal Swing" words="brutal swing" lvl="15" mana="30" prem="1" needweapon="1" casterTargetOrDirection="1" exhaustion="6000" groupcooldown="2000" script="custom/brutal swing.lua">
    <vocation name="..." />
</instant>

custom/brutal swing.lua
Lua:
local checkWeaponSlots = {
    CONST_SLOT_LEFT,
    CONST_SLOT_RIGHT,
}

-- These weapons have a different hit area
local lanceWeapons = { -- LANCE (3)
    2414, -- Dragon Lance
    2964, -- Ripper Lance
    11323,-- Zaoan Halberd
    13838,-- Heavy Trident
    2425, -- Obsidian Lance
    2426, -- Naginata
    2438, -- Epee
    2396, -- Ice Rapier
    20093 -- Life Preserver
}

local hitArea = {
    { -- ONE HAND     (1)
        { -- NESW
            {0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0},
            {0, 1, 3, 1, 0},
            {0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0}
        },
        { -- DIAGONAL
            {0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0},
            {0, 0, 3, 1, 0},
            {0, 0, 1, 0, 0},
            {0, 0, 0, 0, 0}
        }
    },
    { -- TWO HAND     (2)
        { -- NESW
            {0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0},
            {0, 1, 3, 1, 0},
            {0, 1, 0, 1, 0},
            {0, 0, 0, 0, 0}
        },
        { -- DIAGONAL
            {0, 0, 0, 0, 0},
            {0, 0, 0, 0, 0},
            {0, 0, 3, 1, 1},
            {0, 0, 1, 0, 0},
            {0, 0, 1, 0, 0}
        }
    },
    { -- LANCE         (3)
        { -- NESW
            {0, 1, 0},
            {1, 1, 1},
            {1, 3, 1}
        },
        { -- DIAGONAL
            {0, 0, 0, 0},
            {0, 1, 1, 0},
            {0, 1, 3, 1},
            {0, 0, 1, 0}
        }
    }
}

local combats = {}

function onGetFormulaValues(player, skill, attack, factor)
    local skillTotal = skill * attack
    local levelTotal = player:getLevel() / 5
    return -(((skillTotal * 0.03) + 7) + (levelTotal)), -(((skillTotal * 0.05) + 11) + (levelTotal))
end

for i = 1, #hitArea do
    combats[i] = Combat()
    combats[i]:setParameter(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE)
    combats[i]:setParameter(COMBAT_PARAM_BLOCKARMOR, 1)
    combats[i]:setParameter(COMBAT_PARAM_USECHARGES, 1)
    combats[i]:setArea(createCombatArea(hitArea[i][1], hitArea[i][2]))
    --combats[i]:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_POFF)
end

local function animation(pos, playerpos)
    if not Tile(Position(pos)):hasProperty(CONST_PROP_BLOCKPROJECTILE) then
        --if Position(pos):isSightClear(playerpos) then
            Position(pos):sendMagicEffect(CONST_ME_HITAREA)
        --end
    else
        Position(pos):sendMagicEffect(CONST_ME_BLOCKHIT)
    end
    return true
end

function onCastSpell(creature, var)
    
    local animationArea = { -- The start of each frame needs to have a 2 or 3 in it to indicate the player/target position
        { -- NOT DIAGONAL        DIRECTION_NORTH / DIRECTION_EAST / DIRECTION_SOUTH / DIRECTION_WEST
            { -- ONE HAND
                { -- SWING (R to L)
                    {
                        {0, 2, 1},
                    },
                    {
                        {0, 1, 0},
                    },
                    {
                        {1, 0, 0},

                    }
                },
                { -- SWING (L to R)
                    {
                        {1, 2, 0},

                    },
                    {
                        {0, 1, 0},

                    },
                    {
                        {0, 0, 1},
                    }
                }
            },
            { -- TWO HAND
                { -- SWING (R to L)
                    {
                        {0, 2, 0},
                        {0, 0, 1}
                    },
                    {
                        {0, 0, 1},
                        {0, 0, 0}
                    },
                    {
                        {0, 1, 0},
                        {0, 0, 0}
                    },
                    {
                        {1, 0, 0},
                        {0, 0, 0}
                    },
                    {
                        {0, 0, 0},
                        {1, 0, 0}
                    }
                },
                { -- SWING (L to R)
                    {
                        {0, 2, 0},
                        {1, 0, 0}
                    },
                    {
                        {1, 0, 0},
                        {0, 0, 0}
                    },
                    {
                        {0, 1, 0},
                        {0, 0, 0}
                    },
                    {
                        {0, 0, 1},
                        {0, 0, 0}
                    },
                    {
                        {0, 0, 0},
                        {0, 0, 1}
                    }
                }
            },
            { -- LANCE
                { -- SWING (Only 1 direction)
                    {
                        {0, 0, 0},
                        {0, 0, 0},
                        {1, 2, 1}
                    },
                    {
                        {0, 0, 0},
                        {0, 0, 0},
                        {0, 1, 0}
                    },
                    {
                        {0, 0, 0},
                        {0, 1, 0},
                        {0, 0, 0}
                    },
                    {
                        {0, 1, 0},
                        {1, 0, 1},
                        {0, 0, 0}
                    }
                }
            }
        },
        { -- DIAGONAL            DIRECTION_NORTHWEST / DIRECTION_NORTHEAST / DIRECTION_SOUTHEST / DIRECTION_SOUTHWEST
            { -- ONE HAND
                { -- SWING (R to L)
                    {
                        {0, 0, 0},
                        {0, 2, 1},
                        {0, 0, 0}
                    },
                    {
                        {0, 0, 0},
                        {0, 1, 0},
                        {0, 0, 0}
                    },
                    {
                        {0, 0, 0},
                        {0, 0, 0},
                        {0, 1, 0}
                    }
                },
                { -- SWING (L to R)
                    {
                        {0, 0, 0},
                        {0, 2, 0},
                        {0, 1, 0}
                    },
                    {
                        {0, 0, 0},
                        {0, 1, 0},
                        {0, 0, 0}
                    },
                    {
                        {0, 0, 0},
                        {0, 0, 1},
                        {0, 0, 0}
                    }
                }
            },
            { -- TWO HAND
                { -- SWING (R to L)
                    {
                        {0, 0, 2, 0, 1},
                        {0, 0, 0, 0, 0},
                        {0, 0, 0, 0, 0}
                    },
                    {
                        {0, 0, 0, 1, 0},
                        {0, 0, 0, 0, 0},
                        {0, 0, 0, 0, 0}
                    },
                    {
                        {0, 0, 1, 0, 0},
                        {0, 0, 0, 0, 0},
                        {0, 0, 0, 0, 0}
                    },
                    {
                        {0, 0, 0, 0, 0},
                        {0, 0, 1, 0, 0},
                        {0, 0, 0, 0, 0}
                    },
                    {
                        {0, 0, 0, 0, 0},
                        {0, 0, 0, 0, 0},
                        {0, 0, 1, 0, 0}
                    }
                },
                { -- SWING (L to R)
                    {
                        {0, 0, 2, 0, 0},
                        {0, 0, 0, 0, 0},
                        {0, 0, 1, 0, 0}
                    },
                    {
                        {0, 0, 0, 0, 0},
                        {0, 0, 1, 0, 0},
                        {0, 0, 0, 0, 0}
                    },
                    {
                        {0, 0, 1, 0, 0},
                        {0, 0, 0, 0, 0},
                        {0, 0, 0, 0, 0}
                    },
                    {
                        {0, 0, 0, 1, 0},
                        {0, 0, 0, 0, 0},
                        {0, 0, 0, 0, 0}
                    },
                    {
                        {0, 0, 0, 0, 1},
                        {0, 0, 0, 0, 0},
                        {0, 0, 0, 0, 0}
                    }
                }
            },
            { -- LANCE
                { -- SWING (Only 1 direction)
                    {
                        {0, 0, 0, 0},
                        {0, 0, 0, 0},
                        {0, 0, 2, 1},
                        {0, 0, 1, 0}
                    },
                    {
                        {0, 0, 0, 0},
                        {0, 0, 0, 0},
                        {0, 0, 1, 0},
                        {0, 0, 0, 0}
                    },
                    {
                        {0, 0, 0, 0},
                        {0, 0, 1, 0},
                        {0, 1, 0, 0},
                        {0, 0, 0, 0}
                    },
                    {
                        {0, 0, 0, 0},
                        {0, 1, 0, 0},
                        {0, 0, 0, 0},
                        {0, 0, 0, 0}
                    }
                }
            }
        }
    }
    -- Defaults
    local animationType = 1 -- Default (straight or diagonal)
    local player = Player(creature)
    local target = player:getTarget()
    local creaturePos = creature:getPosition()
    local direction = creature:getDirection()
    local damageModifer = 1 -- Default (varies for special animations)
    local weapon = 0
    local attack = 0
    local skill = 0
    local swing = 1 -- Default (animation variants)
    local directionalX = 0
    local directionalY = 0
    local range = 1
    local distance = 0
    local delay = 60 -- Time between animations in ms
    
    -- Get config spell based on weapon
    for i = 1,#checkWeaponSlots do -- Check what weapon is being used
        if player:getSlotItem(checkWeaponSlots[i]) ~= nil then
            local weaponLiteral = player:getSlotItem(checkWeaponSlots[i]) -- weapon object
            local itemType = ItemType(weaponLiteral:getId()) -- itemtype object
            local weaponType = itemType:getWeaponType()
            if weaponType > 0 then
                if isInArray({WEAPON_SWORD, WEAPON_CLUB, WEAPON_AXE}, weaponType) then
                    -- Get weapon info
                    attack = itemType:getAttack()
                    if weaponLiteral:getAttribute(ITEM_ATTRIBUTE_ATTACK) > attack then -- If custom item with added attack
                        attack = weaponLiteral:getAttribute(ITEM_ATTRIBUTE_ATTACK)
                    end
                    
                    -- Get skill to check
                    if weaponType == WEAPON_SWORD then
                        skill = player:getSkillLevel(SKILL_SWORD)
                    elseif weaponType == WEAPON_AXE then
                        skill = player:getSkillLevel(SKILL_AXE)
                    elseif weaponType == WEAPON_CLUB then
                        skill = player:getSkillLevel(SKILL_CLUB)
                    end
                    
                    -- Determine animation based on weapontype
                    local positionType = itemType:getSlotPosition() -- How many hands
                    if positionType == 48 then -- ONE
                        weapon = 1 -- animation(1)
                    elseif positionType == 2096 then -- TWO
                        weapon = 2 -- animation(2)
                    end
                    if isInArray(lanceWeapons, itemType:getId()) then
                        weapon = 3 -- animation(3)
                        if positionType == 2096 then -- Two handed lances get extra damage because of smaller area
                            damageModifer = 1.5
                        end
                    end
                    
                    -- If normal ONE or TWO hander, roll a swing direction
                    if weapon < 3 then
                        swing = math.random(1,2)
                    end
                    
                    -- Creature targeted
                    if target then -- Make sure its in range before doing targeted changes
                        distance = creaturePos:getDistance(target:getPosition())
                        if distance <= range then
                            creaturePos = target:getPosition() -- Change position to monster
                            local playerPos = player:getPosition() -- Get player position
                            local diagonal = false
                            -- Adjust DIRECTION based on target location rather than player direction
                            if creaturePos.x > playerPos.x then
                                if creaturePos.y > playerPos.y then
                                    direction = DIRECTION_SOUTHEAST
                                    diagonal = true
                                elseif creaturePos.y < playerPos.y then
                                    direction = DIRECTION_NORTHEAST
                                    diagonal = true
                                elseif creaturePos.y == playerPos.y then
                                    direction = DIRECTION_EAST
                                end
                            elseif creaturePos.x < playerPos.x then
                                if creaturePos.y > playerPos.y then
                                    direction = DIRECTION_SOUTHWEST
                                    diagonal = true
                                elseif creaturePos.y < playerPos.y then
                                    direction = DIRECTION_NORTHWEST
                                    diagonal = true
                                elseif creaturePos.y == playerPos.y then
                                    direction = DIRECTION_WEST
                                end
                            elseif creaturePos.x == playerPos.x then
                                if creaturePos.y > playerPos.y then
                                    direction = DIRECTION_SOUTH
                                else
                                    direction = DIRECTION_NORTH
                                end
                            end
                            if diagonal then
                                animationType = 2
                            end
                        else -- Targeted, but not in range
                            target = false
                            local nextPos = player:getPosition():getNextPosition(player:getDirection(), 1)
                            if Tile(nextPos):hasProperty(CONST_PROP_BLOCKPROJECTILE) then -- If directional tile is not valid
                                creature:sendCancelMessage(RETURNVALUE_NOTENOUGHROOM)
                                creaturePos:sendMagicEffect(CONST_ME_POFF)
                                return false
                            else
                                var = Variant(nextPos) -- Change spell cast position back to directional pos and continue with spell
                            end
                        end
                    end
                    -- Rotate area based on DIRECTION
                    for i = 1, #animationArea[animationType][weapon][swing] do
                        if direction == DIRECTION_EAST or direction == DIRECTION_NORTHEAST then
                            animationArea[animationType][weapon][swing][i] = rotate_CW_90(animationArea[animationType][weapon][swing][i])
                            if not target then
                                directionalX = 1
                            end
                        elseif direction == DIRECTION_SOUTH or direction == DIRECTION_SOUTHEAST then
                            animationArea[animationType][weapon][swing][i] = rotate_180(animationArea[animationType][weapon][swing][i])
                            if not target then
                                directionalY = 1
                            end
                        elseif direction == DIRECTION_WEST or direction == DIRECTION_SOUTHWEST then
                            animationArea[animationType][weapon][swing][i] = rotate_CCW_90(animationArea[animationType][weapon][swing][i])
                            if not target then
                                directionalX = -1
                            end
                        else
                            if not target then
                                directionalY = -1
                            end
                        end
                    end
                end
            end
        end
    end
    
    -- No weapon
    if weapon == 0 then
        creature:sendCancelMessage("You need to have a weapon equipped to use this spell.")
        creaturePos:sendMagicEffect(CONST_ME_POFF)
        return false
    end
    
    -- Damage formula
    local skillTotal = skill * attack
    local levelTotal = player:getLevel() / 5
    local min, max = -(((skillTotal * 0.02) + 4) + (levelTotal) * damageModifer), -(((skillTotal * 0.04) + 9) + (levelTotal) * damageModifer)
    combats[weapon]:setFormula(COMBAT_FORMULA_SKILL, 0, min, 0, max)
    
    -- Collate animation
    local centre = {}
    local damagearea = {}
    for j = 1, #animationArea[animationType][weapon][swing] do
        for k,v in ipairs(animationArea[animationType][weapon][swing][j]) do
            for i = 1, #v do
                if v[i] == 3 or v[i] == 2 then
                    centre.Row = k
                    centre.Column = i
                    if v[i] == 3 then
                        table.insert(damagearea, centre)
                    end
                elseif v[i] == 1 then
                    local darea = {}
                    darea.Row = k
                    darea.Column = i
                    darea.Delay = j * delay
                    table.insert(damagearea, darea)
                end
            end
        end --print(centre.Column .. "," .. centre.Row)
    end
    
    -- Do Animation
    for i = 1,#damagearea do
        local damagepos = Position(creaturePos)
        
        -- Compare with centrepos
        local modifierx = damagearea[i].Column - centre.Column
        local modifiery = damagearea[i].Row - centre.Row -- print(damagearea[i].Column .. "," .. damagearea[i].Row)
        
        -- Adjust pos, account for direction
        damagepos.x = damagepos.x + modifierx + directionalX -- print("x " .. modifierx .. " " .. "y " .. modifiery)
        damagepos.y = damagepos.y + modifiery + directionalY -- print("Damage: " .. damagepos.x .. "," .. damagepos.y .. "," .. damagepos.z)
        
        -- Animate
        local animationDelay = damagearea[i].Delay or 0
        addEvent(animation, animationDelay, damagepos) -- player:getPosition()
    end
    
    -- Execute Damage
    combats[weapon]:execute(creature, var)
    return true
end

You will also needs to add the spell matrix rotate functions to the spells lib, or at the top of the brutal swing script, or in global.lua etc
spells/lib/spells.lua
add to the end:
Lua:
function rotate_CCW_90(m)
   local rotated = {}
   for c, m_1_c in ipairs(m[1]) do
      local col = {m_1_c}
      for r = 2, #m do
         col[r] = m[r][c]
      end
      table.insert(rotated, 1, col)
   end
   return rotated
end

function rotate_180(m)
   return rotate_CCW_90(rotate_CCW_90(m))
end

function rotate_CW_90(m)
   return rotate_CCW_90(rotate_CCW_90(rotate_CCW_90(m)))
end


āš  Don't do šŸ‘‡ if you're using this to make a ranged spell as it will go through walls.

Optional:
Because of how casterTargetOrDirection="1" works, you can't cast the spell if you have a target - and the the target isn't in sight: (RETURNVALUE_CREATUREISNOTREACHABLE).

This is fine in most scenarios, but I just didn't like it when it happened (creature targeted, behind a wall).
So I forced the spell to execute - by creating a new spell parameter/attribute alwaysUse.

spells.h
under:
C++:
        bool internalCastSpell(Creature* creature, const LuaVariant& var);

        bool needDirection = false;
        bool hasParam = false;
        bool hasPlayerNameParam = false;
        bool checkLineOfSight = true;
        bool casterTargetOrDirection = false;
add:
C++:
        bool alwaysUse = false;

spells.cpp
under:
C++:
if ((attr = node.attribute("direction"))) {
        needDirection = attr.as_bool();
    } else if ((attr = node.attribute("casterTargetOrDirection"))) {
        casterTargetOrDirection = attr.as_bool();
    }
add:
C++:
if ((attr = node.attribute("alwaysUse"))) {
        alwaysUse = attr.as_bool();
    }
replace:
C++:
            if (!canThrowSpell(player, target)) {
                    player->sendCancelMessage(RETURNVALUE_CREATUREISNOTREACHABLE);
                    g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF);
                    return false;
            }
with:
C++:
            if (!canThrowSpell(player, target)) {
                if (!alwaysUse) {
                    player->sendCancelMessage(RETURNVALUE_CREATUREISNOTREACHABLE);
                    g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF);
                    return false;
                }
            }


add alwaysUse="1" to your Brutal Swing Spell.
*Don't do this if you've customized this into a ranged spell.

spells.xml
XML:
<instant group="attack" name="Brutal Swing" words="brutal swing" ... ... ... alwaysUse="1" ... ... ... script="custom/brutal swing.lua">
 
Last edited:

Znote

<?php echo $title; ?>
Staff member
Global Moderator
Premium User
Joined
Feb 14, 2008
Messages
6,831
Solutions
240
Reaction score
1,610
Location
Norway
GitHub
Znote
Whoah! Thats so cool! šŸ˜Ž
 
OP
Leo32

Leo32

Getting back into it...
Joined
Sep 21, 2007
Messages
970
Solutions
14
Reaction score
461
Bug:
If you targeted a creature but killed it with a normal attack while casting the spell, the animation would trigger on the target;
but the combat and damage would apply to the squares in-front of the player (because invalid target, its dead).

The animation logic needed to be fixed:

replace:
Lua:
if distance <= range then
with:
Lua:
if distance <= range and target:getHealth() > 0 then
 

Otfan125

Active Member
Joined
Mar 1, 2008
Messages
159
Solutions
1
Reaction score
43
Location
Thais

miguelshta

Member
Joined
Mar 21, 2009
Messages
275
Solutions
1
Reaction score
6
Location
Toronto, Canada
yeah thats my post but no solved yet :c
 

xFlamek

New Member
Joined
May 1, 2020
Messages
22
Reaction score
0
@Leo32
hello there, tried to add this spell as it is to for testing, but getting error when using regular sword with it, is it something I f**ed with data files?
whole script is copy>paste except for directory.

1605952891058.png


also when trying to use spell when not in range

1605953069305.png
 

Xikini

I whore myself out for likes
Joined
Nov 17, 2010
Messages
4,605
Solutions
246
Reaction score
2,698
@Leo32
hello there, tried to add this spell as it is to for testing, but getting error when using regular sword with it, is it something I f**ed with data files?
whole script is copy>paste except for directory.

View attachment 51779


also when trying to use spell when not in range

View attachment 51780
No, not really your fault.

It looks like he forgot to verify that the tiles exist before attempting to pull information from them.

So for example, line 420 has
Lua:
if Tile(nextPos):hasProperty(CONST_PROP_BLOCKPROJECTILE) then
You'd want to change it to confirm that the tile exists first, like this
Lua:
if Tile(nextPos) and Tile(nextPos):hasProperty(CONST_PROP_BLOCKPROJECTILE) then

--
The above is assuming there's some sort of empty space you're firing it into though.

idk, that's just my assumption based on a quick read through. xD
 
Top