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

Action [TFS 1.X] Equip hotkey system + destroy spider webs with weapon script.

Animera

* * * * *
Joined
Dec 9, 2008
Messages
2,429
Solutions
5
Reaction score
603
Location
ANIMERA.ONLINE
Equip Hotkey System
Screen Shot 03-22-20 at 02.21 AM.PNG

Like i said before i would like to share more stuff, and i only want to share stuff that is beneficial in general for everyone.
As the majority knows it is possible in the newer clients(11+ i think?) to hotkey your armor or weapon to equip it by hotkey.

A few months ago while i was investigating the source codes i saw like 6 different onequip functions and i wasn't sure which one i needed to have, so i was like hey why i am even trying if i can write this easily in lua? Then i've found out the 'moveTo' function doesn't work on equip positions.

But then i came with a workaround, The only moment you can get a item in your equip is when you 'receive' the item right? like a armor quest while having a empty slot ;)?

So then i decided i made a function that check all the item data and delete/create a new item with the same stats, this happens at the function cloneStats. You might need to make some changes as a lot of people have different kind of upgrade systems.

- So how does it work?
Just equip the item in hotkeys, if the item is possible to 'use with' like weapons make sure it is set to 'use on yourself'.

Because this script need to be registered for every item in actions.xml i merged the script with 2 other scripts of mine (spellbook script(basic tfs script), and destroy spider webs with weapon(not sure if TFS already has it implemented)). The only con is that loading action might take longer as before but it's barely noticable. and No i didn't wrote all those XML lines manually.

I am using this system for a few months now, i haven't seen any bug so far.

Code:
----------------------------------------------------------------------------------------------------------
-- Hotkey Equip System Written by @Animera from Otland.net --
-- --
-- Want to see more awesome features? Check my server: Animera.Online --
-- Want a awesome feature yourself? I also take occasionaly jobs as freelancer. PM @ Otland --
----------------------------------------------------------------------------------------------------------

function getDuration(item)[/CENTER]
    local itid = Item(item.uid)
    local tid = itid:getType():getTransformEquipId()
    
    if tid > 0 then
        local b4 = itid:getId()
        itid:transform(tid)
        local af = itid:getAttribute(ITEM_ATTRIBUTE_DURATION)                
        itid:transform(b4)
        return af
    else 
        return 0
    end
end

function cloneStats(item, duration)
    local it = Item(item)
    local itt = it:getType()
                    
    local attack = itt:getAttack()
    local armor = itt:getArmor()
    local defense = itt:getDefense()  
    local extradef = itt:getExtraDefense()
    local hitchance = itt:getHitChance()
    local shootrange = itt:getShootRange()
                
    if attack > 0 then item:setAttribute(ITEM_ATTRIBUTE_ATTACK, attack) end                
    if armor > 0 then item:setAttribute(ITEM_ATTRIBUTE_ARMOR, armor) end
    if defense > 0 then item:setAttribute(ITEM_ATTRIBUTE_DEFENSE, defense) end
    if extradef > 0 then item:setAttribute(ITEM_ATTRIBUTE_EXTRADEFENSE, extradef) end
    if hitchance > 0 then item:setAttribute(ITEM_ATTRIBUTE_HITCHANCE, hitchance) end
    if shootrange > 0 then item:setAttribute(ITEM_ATTRIBUTE_SHOOTRANGE, shootrange) end
    if duration > 0 then it:setAttribute(ITEM_ATTRIBUTE_DURATION, duration) end    
end

local spellbooks = {2175, 6120, 8900, 8901, 8902, 8903, 8904, 8918, 12647, 16112, 18401, 22422, 22423, 22424}

function onUse(player, item, fromPosition, target, toPosition, isHotkey)

    if isHotkey then
    local useOnSelf = false
    local self = player:getPosition()
    
    if toPosition.x == self.x and toPosition.y == self.y and toPosition.z == self.z then 
    useOnSelf = true -- prevent running this script, if hotkey weapon is triggered to destroy items/cowebs (hotkey target on crosshair).
    end

        if useOnSelf or toPosition.y == 0 then
        local itt = ItemType(item:getId())
        local slot = itt:getSlotPosition()
        
        if itt:getDecayId() >= 0 then
        player:sendTextMessage(MESSAGE_INFO_DESCR, "You cannot use this item to hotkey-equip.")
        player:getPosition():sendMagicEffect(CONST_ME_POFF)        
        return true
        end
        
        if player:getExhaustion(40431) > 0 then
        player:getPosition():sendMagicEffect(CONST_ME_POFF)
        player:sendTextMessage(MESSAGE_INFO_DESCR, "Hotkey-Equip Cooldown.")
        return true
        end
        
        player:setExhaustion(40431, 3)
    
        if slot == 49 then slot = CONST_SLOT_HEAD    
        elseif slot == 56 then slot = CONST_SLOT_ARMOR
        elseif slot == 112 then slot = CONST_SLOT_LEGS        
        elseif slot == 176 then    slot = CONST_SLOT_FEET
        elseif slot == 50 then slot = CONST_SLOT_NECKLACE
        elseif slot == 304 then slot = CONST_SLOT_RING
        elseif slot == 48 and (itt:getWeaponType() == 4 or not player:getSlotItem(CONST_SLOT_RIGHT)) then
        slot = CONST_SLOT_RIGHT
        elseif slot == 48 then slot = CONST_SLOT_LEFT end

        if player:getSlotItem(slot) then
            if item.uid == player:getSlotItem(slot).uid then
            player:sendTextMessage(MESSAGE_INFO_DESCR, "Already Equipped.")
            player:getPosition():sendMagicEffect(CONST_ME_POFF)                
            return true
            end
        end

        local itnam, itdesc, id, name = item:getAttribute(ITEM_ATTRIBUTE_NAME), item:getAttribute(ITEM_ATTRIBUTE_DESCRIPTION), item:getId(), item:getName()
        local duration = getDuration(item)
        
        if not duration then duration = 0 end
        
        if not player:getSlotItem(slot) then
            local newItem = player:addItem(id, 1, false, 1, slot)
            if newItem then
            item:remove()            
            newItem:setAttribute(ITEM_ATTRIBUTE_NAME, itnam)
            newItem:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, itdesc)        
            cloneStats(newItem.uid, duration)        
            player:sendTextMessage(MESSAGE_INFO_DESCR, "Equiped: "..name..".")            
            end
        else        
            local curEq = player:getSlotItem(slot)
            local itnam2, itdesc2, id2, name2 = curEq:getAttribute(ITEM_ATTRIBUTE_NAME), curEq:getAttribute(ITEM_ATTRIBUTE_DESCRIPTION), curEq:getId(), curEq:getName()                
            local prevItem = player:addItem(id2)
            if prevItem then
            curEq:remove()
            prevItem:setAttribute(ITEM_ATTRIBUTE_NAME, itnam2)
            prevItem:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, itdesc2)        
            cloneStats(prevItem.uid, duration)        
            item:remove()    
            local newItem = player:addItem(id, 1, false, 1, slot)
            newItem:setAttribute(ITEM_ATTRIBUTE_NAME, itnam)
            newItem:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, itdesc)        
            cloneStats(newItem.uid, duration)    
            player:sendTextMessage(MESSAGE_INFO_DESCR, "Switched "..name2.." with "..name..".")
            end        
        end
        else
            local spellbook = false

            for i = 1, #spellbooks do
                if item:getId() == spellbooks[i] then
                spellbook = true
                break
                end
            end

            if spellbook then -- spellbook script
                if player:getExhaustion(1000) <= 0 then
                player:setExhaustion(1000, 3)
                local text = ""
                local spells = {}
                for _, spell in ipairs(player:getInstantSpells()) do
                    if spell.level ~= 0 then
                        if spell.manapercent > 0 then
                            spell.mana = spell.manapercent .. "%"
                        end
                        spells[#spells + 1] = spell
                    end
                end

                table.sort(spells, function(a, b) return a.level < b.level end)

                local prevLevel = -1
                for i, spell in ipairs(spells) do
                    local line = ""
                    if prevLevel ~= spell.level then
                        if i ~= 1 then
                            line = "\n"
                        end
                        line = line .. "[ Level " .. spell.level .. " ]\n"
                        prevLevel = spell.level
                    end
                    text = text .. line .. "  " .. spell.name .. " × " .. spell.words .. " : " .. spell.mana .. "\n"
                end

                player:showTextDialog(2175, text)
                else
                return player:sendTextMessage(MESSAGE_STATUS_SMALL, 'You\'re exhausted for: '..player:getExhaustion(1000)..' seconds.')
                end
            end

            if item:getType():getWeaponType() > 0 and item:getType():getWeaponType() < 4 then -- destroy items with weps
                local topos = Tile (toPosition) 
                if topos then
                local target = topos:getItemById(7538)
                local target2 = topos:getItemById(7539)

                    if topos:getItemById(7538) then
                        if math.random(1, 3) == 1 then
                        topos:getItemById(7538):transform(7544)
                        toPosition:sendMagicEffect(CONST_ME_POFF)
                        else
                        toPosition:sendMagicEffect(CONST_ME_POFF)            
                        end
                        
                    elseif topos:getItemById(7539) then
                        if math.random(1, 3) == 1 then
                        topos:getItemById(7539):transform(7545)
                        toPosition:sendMagicEffect(CONST_ME_POFF)
                        else
                        toPosition:sendMagicEffect(CONST_ME_POFF)
                        end
                    end
                end
                return true
            end
        
        end
    else
        local spellbook = false

        for i = 1, #spellbooks do
            if item:getId() == spellbooks[i] then
            spellbook = true
            break
            end
        end

        if spellbook then -- spellbook script
            if player:getExhaustion(1000) <= 0 then
            player:setExhaustion(1000, 3)
            local text = ""
            local spells = {}
            for _, spell in ipairs(player:getInstantSpells()) do
                if spell.level ~= 0 then
                    if spell.manapercent > 0 then
                        spell.mana = spell.manapercent .. "%"
                    end
                    spells[#spells + 1] = spell
                end
            end

            table.sort(spells, function(a, b) return a.level < b.level end)

            local prevLevel = -1
            for i, spell in ipairs(spells) do
                local line = ""
                if prevLevel ~= spell.level then
                    if i ~= 1 then
                        line = "\n"
                    end
                    line = line .. "[ Level " .. spell.level .. " ]\n"
                    prevLevel = spell.level
                end
                text = text .. line .. "  " .. spell.name .. " × " .. spell.words .. " : " .. spell.mana .. "\n"
            end

            player:showTextDialog(2175, text)
            else
            return player:sendTextMessage(MESSAGE_STATUS_SMALL, 'You\'re exhausted for: '..player:getExhaustion(1000)..' seconds.')
            end
        end

        if item:getType():getWeaponType() > 0 and item:getType():getWeaponType() < 4 then -- destroy items with weps
            local topos = Tile (toPosition) 
            if topos then
            local target = topos:getItemById(7538)
            local target2 = topos:getItemById(7539)

                if topos:getItemById(7538) then
                    if math.random(1, 3) == 1 then
                    topos:getItemById(7538):transform(7544)
                    toPosition:sendMagicEffect(CONST_ME_POFF)
                    else
                    toPosition:sendMagicEffect(CONST_ME_POFF)            
                    end
                    
                elseif topos:getItemById(7539) then
                    if math.random(1, 3) == 1 then
                    topos:getItemById(7539):transform(7545)
                    toPosition:sendMagicEffect(CONST_ME_POFF)
                    else
                    toPosition:sendMagicEffect(CONST_ME_POFF)
                    end
                end

            end
            return true
        end
    end        
return true
end
You might want to get rid of change the message in sources like 'using one of x item..'
This can be done at actions.cpp

Code:
void Actions::showUseHotkeyMessage(Player* player, const Item* item, uint32_t count)
{
    std::ostringstream ss;

    const ItemType& it = Item::items[item->getID()];
    if (!it.showCount) {
        ss << "Using one of " << item->getName() << "...";
    } else if (count == 1) {
        ss << "Using the last " << item->getName() << "...";
    } else {
        ss << "Using one of " << count << ' ' << item->getPluralName() << "...";
    }
    player->sendCancelMessage(ss.str());
}

Animera
 

Attachments

Nice work, i will test later. But if i have custom attributes to my items, i have to include them in the cloneStats function right? Item:clone() don't work? Something like:
Lua:
local newItem = item:clone()
player:addItemEx(newItem,false, slot)
I tested clone function and really clone the item, with custom attributes and more. Without needing of worry about new custom attributes. Enjoyed your workaround!

Really thanks for the contribution!
 
Nice work, i will test later. But if i have custom attributes to my items, i have to include them in the cloneStats function right? Item:clone() don't work? Something like:
Lua:
local newItem = item:clone()
player:addItemEx(newItem,false, slot)
I tested clone function and really clone the item, with custom attributes and more. Without needing of worry about new custom attributes. Enjoyed your workaround!

Really thanks for the contribution!

LOL! i wasn't aware the clone function already existed...

You're welcome :>

Animera
 
Back
Top