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

more runes same time!

vena

New Member
Joined
Jun 21, 2017
Messages
65
Reaction score
2
Hello, I need help, I want to do it on the server so that you can use it at the same time np manarune and sd or SD and healtig rune.

i have tfs 1.2 , 8.6

spells and sd works well but I want it to work so manarune and sd even

PLEAS HELP ME!
 
Solution
Please Here and this
-- Item Usage
timeBetweenActions = 0
timeBetweenExActions = 0
leave for 0 these two?

You can change it back to it's original value, default on ExActions I think it is 2000.

The solution I found is setting ExActions = 0 only when using potions, so it shouldn't affects others items like runes, etc.
C++:
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2016  Mark Samman <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is...
You can't use these 2 items rightaway because of the ExAction exaustion, lower it on config.lua.

Lua:
timeBetweenExActions = 1000
 
did so on and I still do not use mana potion + SD

its my:

-- Item Usage
timeBetweenActions = 0
timeBetweenExActions = 1000
 
Last edited:
I'm going to thank you for your help.
I will give you a good ++++rep as you help

C++:
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2016  Mark Samman <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "otpch.h"

#include "actions.h"
#include "bed.h"
#include "configmanager.h"
#include "container.h"
#include "game.h"
#include "pugicast.h"
#include "spells.h"

extern Game g_game;
extern Spells* g_spells;
extern Actions* g_actions;
extern ConfigManager g_config;

Actions::Actions() :
    scriptInterface("Action Interface")
{
    scriptInterface.initState();
}

Actions::~Actions()
{
    clear();
}

inline void Actions::clearMap(ActionUseMap& map)
{
    // Filter out duplicates to avoid double-free
    std::unordered_set<Action*> set;
    for (const auto& it : map) {
        set.insert(it.second);
    }
    map.clear();

    for (Action* action : set) {
        delete action;
    }
}

void Actions::clear()
{
    clearMap(useItemMap);
    clearMap(uniqueItemMap);
    clearMap(actionItemMap);

    scriptInterface.reInitState();
}

LuaScriptInterface& Actions::getScriptInterface()
{
    return scriptInterface;
}

std::string Actions::getScriptBaseName() const
{
    return "actions";
}

Event* Actions::getEvent(const std::string& nodeName)
{
    if (strcasecmp(nodeName.c_str(), "action") != 0) {
        return nullptr;
    }
    return new Action(&scriptInterface);
}

bool Actions::registerEvent(Event* event, const pugi::xml_node& node)
{
    Action* action = static_cast<Action*>(event); //event is guaranteed to be an Action

    pugi::xml_attribute attr;
    if ((attr = node.attribute("itemid"))) {
        uint16_t id = pugi::cast<uint16_t>(attr.value());

        auto result = useItemMap.emplace(id, action);
        if (!result.second) {
            std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << id << std::endl;
        }
        return result.second;
    } else if ((attr = node.attribute("fromid"))) {
        pugi::xml_attribute toIdAttribute = node.attribute("toid");
        if (!toIdAttribute) {
            std::cout << "[Warning - Actions::registerEvent] Missing toid in fromid: " << attr.as_string() << std::endl;
            return false;
        }

        uint16_t fromId = pugi::cast<uint16_t>(attr.value());
        uint16_t iterId = fromId;
        uint16_t toId = pugi::cast<uint16_t>(toIdAttribute.value());

        auto result = useItemMap.emplace(iterId, action);
        if (!result.second) {
            std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << iterId << " in fromid: " << fromId << ", toid: " << toId << std::endl;
        }

        bool success = result.second;
        while (++iterId <= toId) {
            result = useItemMap.emplace(iterId, action);
            if (!result.second) {
                std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << iterId << " in fromid: " << fromId << ", toid: " << toId << std::endl;
                continue;
            }
            success = true;
        }
        return success;
    } else if ((attr = node.attribute("uniqueid"))) {
        uint16_t uid = pugi::cast<uint16_t>(attr.value());

        auto result = uniqueItemMap.emplace(uid, action);
        if (!result.second) {
            std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with uniqueid: " << uid << std::endl;
        }
        return result.second;
    } else if ((attr = node.attribute("fromuid"))) {
        pugi::xml_attribute toUidAttribute = node.attribute("touid");
        if (!toUidAttribute) {
            std::cout << "[Warning - Actions::registerEvent] Missing touid in fromuid: " << attr.as_string() << std::endl;
            return false;
        }

        uint16_t fromUid = pugi::cast<uint16_t>(attr.value());
        uint16_t iterUid = fromUid;
        uint16_t toUid = pugi::cast<uint16_t>(toUidAttribute.value());

        auto result = uniqueItemMap.emplace(iterUid, action);
        if (!result.second) {
            std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with unique id: " << iterUid << " in fromuid: " << fromUid << ", touid: " << toUid << std::endl;
        }

        bool success = result.second;
        while (++iterUid <= toUid) {
            result = uniqueItemMap.emplace(iterUid, action);
            if (!result.second) {
                std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with unique id: " << iterUid << " in fromuid: " << fromUid << ", touid: " << toUid << std::endl;
                continue;
            }
            success = true;
        }
        return success;
    } else if ((attr = node.attribute("actionid"))) {
        uint16_t aid = pugi::cast<uint16_t>(attr.value());

        auto result = actionItemMap.emplace(aid, action);
        if (!result.second) {
            std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with actionid: " << aid << std::endl;
        }
        return result.second;
    } else if ((attr = node.attribute("fromaid"))) {
        pugi::xml_attribute toAidAttribute = node.attribute("toaid");
        if (!toAidAttribute) {
            std::cout << "[Warning - Actions::registerEvent] Missing toaid in fromaid: " << attr.as_string() << std::endl;
            return false;
        }

        uint16_t fromAid = pugi::cast<uint16_t>(attr.value());
        uint16_t iterAid = fromAid;
        uint16_t toAid = pugi::cast<uint16_t>(toAidAttribute.value());

        auto result = actionItemMap.emplace(iterAid, action);
        if (!result.second) {
            std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with action id: " << iterAid << " in fromaid: " << fromAid << ", toaid: " << toAid << std::endl;
        }

        bool success = result.second;
        while (++iterAid <= toAid) {
            result = actionItemMap.emplace(iterAid, action);
            if (!result.second) {
                std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with action id: " << iterAid << " in fromaid: " << fromAid << ", toaid: " << toAid << std::endl;
                continue;
            }
            success = true;
        }
        return success;
    }
    return false;
}

ReturnValue Actions::canUse(const Player* player, const Position& pos)
{
    if (pos.x != 0xFFFF) {
        const Position& playerPos = player->getPosition();
        if (playerPos.z != pos.z) {
            return playerPos.z > pos.z ? RETURNVALUE_FIRSTGOUPSTAIRS : RETURNVALUE_FIRSTGODOWNSTAIRS;
        }

        if (!Position::areInRange<1, 1>(playerPos, pos)) {
            return RETURNVALUE_TOOFARAWAY;
        }
    }
    return RETURNVALUE_NOERROR;
}

ReturnValue Actions::canUse(const Player* player, const Position& pos, const Item* item)
{
    Action* action = getAction(item);
    if (action) {
        return action->canExecuteAction(player, pos);
    }
    return RETURNVALUE_NOERROR;
}

ReturnValue Actions::canUseFar(const Creature* creature, const Position& toPos, bool checkLineOfSight, bool checkFloor)
{
    if (toPos.x == 0xFFFF) {
        return RETURNVALUE_NOERROR;
    }

    const Position& creaturePos = creature->getPosition();
    if (checkFloor && creaturePos.z != toPos.z) {
        return creaturePos.z > toPos.z ? RETURNVALUE_FIRSTGOUPSTAIRS : RETURNVALUE_FIRSTGODOWNSTAIRS;
    }

    if (!Position::areInRange<7, 5>(toPos, creaturePos)) {
        return RETURNVALUE_TOOFARAWAY;
    }

    if (checkLineOfSight && !g_game.canThrowObjectTo(creaturePos, toPos)) {
        return RETURNVALUE_CANNOTTHROW;
    }

    return RETURNVALUE_NOERROR;
}

Action* Actions::getAction(const Item* item)
{
    if (item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
        auto it = uniqueItemMap.find(item->getUniqueId());
        if (it != uniqueItemMap.end()) {
            return it->second;
        }
    }

    if (item->hasAttribute(ITEM_ATTRIBUTE_ACTIONID)) {
        auto it = actionItemMap.find(item->getActionId());
        if (it != actionItemMap.end()) {
            return it->second;
        }
    }

    auto it = useItemMap.find(item->getID());
    if (it != useItemMap.end()) {
        return it->second;
    }

    //rune items
    return g_spells->getRuneSpell(item->getID());
}

ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey)
{
    if (Door* door = item->getDoor()) {
        if (!door->canUse(player)) {
            return RETURNVALUE_CANNOTUSETHISOBJECT;
        }
    }

    Action* action = getAction(item);
    if (action) {
        if (action->isScripted()) {
            if (action->executeUse(player, item, pos, nullptr, pos, isHotkey)) {
                return RETURNVALUE_NOERROR;
            }
        } else if (action->function) {
            if (action->function(player, item, pos, nullptr, pos, isHotkey)) {
                return RETURNVALUE_NOERROR;
            }
        }
    }

    if (BedItem* bed = item->getBed()) {
        if (!bed->canUse(player)) {
            return RETURNVALUE_CANNOTUSETHISOBJECT;
        }

        bed->sleep(player);
        return RETURNVALUE_NOERROR;
    }

    if (Container* container = item->getContainer()) {
        Container* openContainer;

        //depot container
        if (DepotLocker* depot = container->getDepotLocker()) {
            DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId());
            myDepotLocker->setParent(depot->getParent());
            openContainer = myDepotLocker;
            player->setLastDepotId(depot->getDepotId());
        } else {
            openContainer = container;
        }

        uint32_t corpseOwner = container->getCorpseOwner();
        if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) {
            return RETURNVALUE_YOUARENOTTHEOWNER;
        }

        //open/close container
        int32_t oldContainerId = player->getContainerID(openContainer);
        if (oldContainerId != -1) {
            player->onCloseContainer(openContainer);
            player->closeContainer(oldContainerId);
        } else {
            player->addContainer(index, openContainer);
            player->onSendContainer(openContainer);
        }

        return RETURNVALUE_NOERROR;
    }

    const ItemType& it = Item::items[item->getID()];
    if (it.canReadText) {
        if (it.canWriteText) {
            player->setWriteItem(item, it.maxTextLen);
            player->sendTextWindow(item, it.maxTextLen, true);
        } else {
            player->setWriteItem(nullptr);
            player->sendTextWindow(item, 0, false);
        }

        return RETURNVALUE_NOERROR;
    }

    return RETURNVALUE_CANNOTUSETHISOBJECT;
}

bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey)
{
    player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL));
    player->stopWalk();

    if (isHotkey) {
        showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), -1));
    }

    ReturnValue ret = internalUseItem(player, pos, index, item, isHotkey);
    if (ret != RETURNVALUE_NOERROR) {
        player->sendCancelMessage(ret);
        return false;
    }
    return true;
}

bool Actions::useItemEx(Player* player, const Position& fromPos, const Position& toPos,
                        uint8_t toStackPos, Item* item, bool isHotkey, Creature* creature/* = nullptr*/)
{
    player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::EX_ACTIONS_DELAY_INTERVAL));
    player->stopWalk();

    Action* action = getAction(item);
    if (!action) {
        player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
        return false;
    }

    ReturnValue ret = action->canExecuteAction(player, toPos);
    if (ret != RETURNVALUE_NOERROR) {
        player->sendCancelMessage(ret);
        return false;
    }

    if (isHotkey) {
        showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), -1));
    }

    if (!action->executeUse(player, item, fromPos, action->getTarget(player, creature, toPos, toStackPos), toPos, isHotkey)) {
        if (!action->hasOwnErrorHandler()) {
            player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
        }
        return false;
    }
    return true;
}

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->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
}

Action::Action(LuaScriptInterface* _interface) :
    Event(_interface)
{
    allowFarUse = false;
    checkFloor = true;
    checkLineOfSight = true;
    function = nullptr;
}

Action::Action(const Action* copy) :
    Event(copy)
{
    allowFarUse = copy->allowFarUse;
    checkFloor = copy->checkFloor;
    checkLineOfSight = copy->checkLineOfSight;
    function = copy->function;
}

bool Action::configureEvent(const pugi::xml_node& node)
{
    pugi::xml_attribute allowFarUseAttr = node.attribute("allowfaruse");
    if (allowFarUseAttr) {
        setAllowFarUse(allowFarUseAttr.as_bool());
    }

    pugi::xml_attribute blockWallsAttr = node.attribute("blockwalls");
    if (blockWallsAttr) {
        setCheckLineOfSight(blockWallsAttr.as_bool());
    }

    pugi::xml_attribute checkFloorAttr = node.attribute("checkfloor");
    if (checkFloorAttr) {
        setCheckFloor(checkFloorAttr.as_bool());
    }

    return true;
}

bool Action::loadFunction(const pugi::xml_attribute& attr)
{
    const char* functionName = attr.as_string();
    if (strcasecmp(functionName, "increaseitemid") == 0) {
        function = increaseItemId;
    } else if (strcasecmp(functionName, "decreaseitemid") == 0) {
        function = decreaseItemId;
    } else {
        std::cout << "[Warning - Action::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl;
        return false;
    }

    scripted = false;
    return true;
}

bool Action::increaseItemId(Player*, Item* item, const Position&, Thing*, const Position&, bool)
{
    g_game.startDecay(g_game.transformItem(item, item->getID() + 1));
    return true;
}

bool Action::decreaseItemId(Player*, Item* item, const Position&, Thing*, const Position&, bool)
{
    g_game.startDecay(g_game.transformItem(item, item->getID() - 1));
    return true;
}

std::string Action::getScriptEventName() const
{
    return "onUse";
}

ReturnValue Action::canExecuteAction(const Player* player, const Position& toPos)
{
    if (!getAllowFarUse()) {
        return g_actions->canUse(player, toPos);
    } else {
        return g_actions->canUseFar(player, toPos, getCheckLineOfSight(), getCheckFloor());
    }
}

Thing* Action::getTarget(Player* player, Creature* targetCreature, const Position& toPosition, uint8_t toStackPos) const
{
    if (targetCreature) {
        return targetCreature;
    }
    return g_game.internalGetThing(player, toPosition, toStackPos, 0, STACKPOS_USETARGET);
}

bool Action::executeUse(Player* player, Item* item, const Position& fromPos, Thing* target, const Position& toPos, bool isHotkey)
{
    //onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if (!scriptInterface->reserveScriptEnv()) {
        std::cout << "[Error - Action::executeUse] Call stack overflow" << std::endl;
        return false;
    }

    ScriptEnvironment* env = scriptInterface->getScriptEnv();
    env->setScriptId(scriptId, scriptInterface);

    lua_State* L = scriptInterface->getLuaState();

    scriptInterface->pushFunction(scriptId);

    LuaScriptInterface::pushUserdata<Player>(L, player);
    LuaScriptInterface::setMetatable(L, -1, "Player");

    LuaScriptInterface::pushThing(L, item);
    LuaScriptInterface::pushPosition(L, fromPos);

    LuaScriptInterface::pushThing(L, target);
    LuaScriptInterface::pushPosition(L, toPos);

    LuaScriptInterface::pushBoolean(L, isHotkey);
    return scriptInterface->callFunction(6);
}
 
yes ,that's just mana potiona
I am waiting impatiently for your help. You will get a big + rep
 
Last edited:
Please Here and this
-- Item Usage
timeBetweenActions = 0
timeBetweenExActions = 0
leave for 0 these two?


Lua:
local antidote = Combat()
antidote:setParameter(COMBAT_PARAM_TYPE, COMBAT_HEALING)
antidote:setParameter(COMBAT_PARAM_EFFECT, CONST_ME_MAGIC_BLUE)
antidote:setParameter(COMBAT_PARAM_TARGETCASTERORTOPMOST, true)
antidote:setParameter(COMBAT_PARAM_AGGRESSIVE, false)
antidote:setParameter(COMBAT_PARAM_DISPEL, CONDITION_POISON)

local exhaust = Condition(CONDITION_EXHAUST_HEAL)
exhaust:setParameter(CONDITION_PARAM_TICKS, (configManager.getNumber(configKeys.EX_ACTIONS_DELAY_INTERVAL)))
-- 1000 - 100 due to exact condition timing. -100 doesn't hurt us, and players don't have reminding ~50ms exhaustion.

local t = {
    -- Supreme health potion
    [26031] = {empty = 7635, vocArray = {4, 8}, level = 200, str = "knights", func = function(p) return doTargetCombatHealth(0, p, COMBAT_HEALING, 875, 1125, CONST_ME_MAGIC_BLUE) end},
    -- Ultimate spirit potion
    [26030] = {empty = 7635, vocArray = {3, 7}, level = 130, str = "paladins", func = function(p) return doTargetCombatHealth(0, p, COMBAT_HEALING, 420, 680, CONST_ME_MAGIC_BLUE), doTargetCombatMana(0, p, 150, 250, CONST_ME_MAGIC_BLUE) end},
    -- Ultimate health potion
    [8473]  = {empty = 7635, vocArray = {4, 8}, level = 130, str = "knights", func = function(p) return doTargetCombatHealth(0, p, COMBAT_HEALING, 650, 860, CONST_ME_MAGIC_BLUE) end},
    -- Great health potion
    [7591]  = {empty = 7635, vocArray = {3, 4, 7, 8}, level = 80, str = "knights", func = function(p) return doTargetCombatHealth(0, p, COMBAT_HEALING, 425, 575, CONST_ME_MAGIC_BLUE) end},
    -- Great spirit potion
    [8472]  = {empty = 7635, vocArray = {3, 7}, level = 80, str = "paladins", func = function(p) return doTargetCombatHealth(0, p, COMBAT_HEALING, 250, 350, CONST_ME_MAGIC_BLUE), doTargetCombatMana(0, p, 100, 200, CONST_ME_MAGIC_BLUE) end},
    -- Strong health potion
    [7588]  = {empty = 7634, vocArray = {3, 4, 7, 8}, level = 80, str = "paladins and knights", func = function(p) return doTargetCombatHealth(0, p, COMBAT_HEALING, 250, 350, CONST_ME_MAGIC_BLUE) end},
    -- Ultimate mana potion
    [26029] = {empty = 7635, vocArray = {1, 2, 5, 6}, level = 130, str = "sorcerers and druids", func = function(p) return doTargetCombatMana(0, p, 425, 575, CONST_ME_MAGIC_BLUE) end},
    -- Great mana potion
    [7590]  = {empty = 7635, vocArray = {1, 2, 5, 6}, level = 80, str = "sorcerers and druids", func = function(p) return doTargetCombatMana(0, p, 150, 250, CONST_ME_MAGIC_BLUE) end},
    -- Strong mana potion
    [7589]  = {empty = 7634, vocArray = {1, 2, 3, 5, 6, 7}, level = 50, str = "sorcerers and druids", func = function(p) return doTargetCombatMana(0, p, 115, 185, CONST_ME_MAGIC_BLUE) end},
    -- Mana potion
    [7620]  = {empty = 7636, func = function(p) return doTargetCombatMana(0, p, 75, 125, CONST_ME_MAGIC_BLUE) end},
    -- Health potion
    [7618]  = {empty = 7636, func = function(p) return doTargetCombatHealth(0, p, COMBAT_HEALING, 125, 175, CONST_ME_MAGIC_BLUE) end},
    -- Small health potion
    [8704]  = {empty = 7636, func = function(p) return doTargetCombatHealth(0, p, COMBAT_HEALING, 60, 90, CONST_ME_MAGIC_BLUE) end},
    -- Antidote potion
    [8474]  = {empty = 7636, func = function(p) return antidote:execute(p, numberToVariant(p:getId())) end}
}

function onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if not target:getPlayer() then
        return true
    end
    if player:getCondition(CONDITION_EXHAUST_HEAL)  then
        player:sendTextMessage(MESSAGE_STATUS_SMALL, Game.getReturnMessage(RETURNVALUE_YOUAREEXHAUSTED))
        return true
    end

   

    local tmp = t[item.itemid]
    if tmp then
        local level = tmp.level or 0
        if tmp.vocArray and (not isInArray(tmp.vocArray, target:getVocation():getId()) or target:getLevel() < level) and not getPlayerFlagValue(player, PlayerFlag_IgnoreSpellCheck) then
            player:say(string.format("This potion can only be consumed by %s of level %d or higher.", tmp.str, level), TALKTYPE_MONSTER_SAY)
            return true
        end
       
        -- local exhausted_storagevalue = 7000 -- Storage Value to store exhaust. It MUST be unused! --
        -- local exhausted_seconds = 1
        -- if(os.time() > player:getStorageValue(exhausted_storagevalue)) then
       
        if not tmp.func(target) then
            return false
        end
       
        -- player:setStorageValue(exhausted_storagevalue, os.time() + exhausted_seconds)
       
        target:say("Aaaah...", TALKTYPE_MONSTER_SAY)
        item:remove(1)
        --else
        player:addCondition(exhaust)
        --end
        player:addItem(tmp.empty, 1)
       
       
    end
    return true
end
 
Please Here and this
-- Item Usage
timeBetweenActions = 0
timeBetweenExActions = 0
leave for 0 these two?

You can change it back to it's original value, default on ExActions I think it is 2000.

The solution I found is setting ExActions = 0 only when using potions, so it shouldn't affects others items like runes, etc.
C++:
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2016  Mark Samman <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include "otpch.h"

#include "actions.h"
#include "bed.h"
#include "configmanager.h"
#include "container.h"
#include "game.h"
#include "pugicast.h"
#include "spells.h"

extern Game g_game;
extern Spells* g_spells;
extern Actions* g_actions;
extern ConfigManager g_config;

Actions::Actions() :
    scriptInterface("Action Interface")
{
    scriptInterface.initState();
}

Actions::~Actions()
{
    clear();
}

inline void Actions::clearMap(ActionUseMap& map)
{
    // Filter out duplicates to avoid double-free
    std::unordered_set<Action*> set;
    for (const auto& it : map) {
        set.insert(it.second);
    }
    map.clear();

    for (Action* action : set) {
        delete action;
    }
}

void Actions::clear()
{
    clearMap(useItemMap);
    clearMap(uniqueItemMap);
    clearMap(actionItemMap);

    scriptInterface.reInitState();
}

LuaScriptInterface& Actions::getScriptInterface()
{
    return scriptInterface;
}

std::string Actions::getScriptBaseName() const
{
    return "actions";
}

Event* Actions::getEvent(const std::string& nodeName)
{
    if (strcasecmp(nodeName.c_str(), "action") != 0) {
        return nullptr;
    }
    return new Action(&scriptInterface);
}

bool Actions::registerEvent(Event* event, const pugi::xml_node& node)
{
    Action* action = static_cast<Action*>(event); //event is guaranteed to be an Action

    pugi::xml_attribute attr;
    if ((attr = node.attribute("itemid"))) {
        uint16_t id = pugi::cast<uint16_t>(attr.value());

        auto result = useItemMap.emplace(id, action);
        if (!result.second) {
            std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << id << std::endl;
        }
        return result.second;
    } else if ((attr = node.attribute("fromid"))) {
        pugi::xml_attribute toIdAttribute = node.attribute("toid");
        if (!toIdAttribute) {
            std::cout << "[Warning - Actions::registerEvent] Missing toid in fromid: " << attr.as_string() << std::endl;
            return false;
        }

        uint16_t fromId = pugi::cast<uint16_t>(attr.value());
        uint16_t iterId = fromId;
        uint16_t toId = pugi::cast<uint16_t>(toIdAttribute.value());

        auto result = useItemMap.emplace(iterId, action);
        if (!result.second) {
            std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << iterId << " in fromid: " << fromId << ", toid: " << toId << std::endl;
        }

        bool success = result.second;
        while (++iterId <= toId) {
            result = useItemMap.emplace(iterId, action);
            if (!result.second) {
                std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << iterId << " in fromid: " << fromId << ", toid: " << toId << std::endl;
                continue;
            }
            success = true;
        }
        return success;
    } else if ((attr = node.attribute("uniqueid"))) {
        uint16_t uid = pugi::cast<uint16_t>(attr.value());

        auto result = uniqueItemMap.emplace(uid, action);
        if (!result.second) {
            std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with uniqueid: " << uid << std::endl;
        }
        return result.second;
    } else if ((attr = node.attribute("fromuid"))) {
        pugi::xml_attribute toUidAttribute = node.attribute("touid");
        if (!toUidAttribute) {
            std::cout << "[Warning - Actions::registerEvent] Missing touid in fromuid: " << attr.as_string() << std::endl;
            return false;
        }

        uint16_t fromUid = pugi::cast<uint16_t>(attr.value());
        uint16_t iterUid = fromUid;
        uint16_t toUid = pugi::cast<uint16_t>(toUidAttribute.value());

        auto result = uniqueItemMap.emplace(iterUid, action);
        if (!result.second) {
            std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with unique id: " << iterUid << " in fromuid: " << fromUid << ", touid: " << toUid << std::endl;
        }

        bool success = result.second;
        while (++iterUid <= toUid) {
            result = uniqueItemMap.emplace(iterUid, action);
            if (!result.second) {
                std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with unique id: " << iterUid << " in fromuid: " << fromUid << ", touid: " << toUid << std::endl;
                continue;
            }
            success = true;
        }
        return success;
    } else if ((attr = node.attribute("actionid"))) {
        uint16_t aid = pugi::cast<uint16_t>(attr.value());

        auto result = actionItemMap.emplace(aid, action);
        if (!result.second) {
            std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with actionid: " << aid << std::endl;
        }
        return result.second;
    } else if ((attr = node.attribute("fromaid"))) {
        pugi::xml_attribute toAidAttribute = node.attribute("toaid");
        if (!toAidAttribute) {
            std::cout << "[Warning - Actions::registerEvent] Missing toaid in fromaid: " << attr.as_string() << std::endl;
            return false;
        }

        uint16_t fromAid = pugi::cast<uint16_t>(attr.value());
        uint16_t iterAid = fromAid;
        uint16_t toAid = pugi::cast<uint16_t>(toAidAttribute.value());

        auto result = actionItemMap.emplace(iterAid, action);
        if (!result.second) {
            std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with action id: " << iterAid << " in fromaid: " << fromAid << ", toaid: " << toAid << std::endl;
        }

        bool success = result.second;
        while (++iterAid <= toAid) {
            result = actionItemMap.emplace(iterAid, action);
            if (!result.second) {
                std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with action id: " << iterAid << " in fromaid: " << fromAid << ", toaid: " << toAid << std::endl;
                continue;
            }
            success = true;
        }
        return success;
    }
    return false;
}

ReturnValue Actions::canUse(const Player* player, const Position& pos)
{
    if (pos.x != 0xFFFF) {
        const Position& playerPos = player->getPosition();
        if (playerPos.z != pos.z) {
            return playerPos.z > pos.z ? RETURNVALUE_FIRSTGOUPSTAIRS : RETURNVALUE_FIRSTGODOWNSTAIRS;
        }

        if (!Position::areInRange<1, 1>(playerPos, pos)) {
            return RETURNVALUE_TOOFARAWAY;
        }
    }
    return RETURNVALUE_NOERROR;
}

ReturnValue Actions::canUse(const Player* player, const Position& pos, const Item* item)
{
    Action* action = getAction(item);
    if (action) {
        return action->canExecuteAction(player, pos);
    }
    return RETURNVALUE_NOERROR;
}

ReturnValue Actions::canUseFar(const Creature* creature, const Position& toPos, bool checkLineOfSight, bool checkFloor)
{
    if (toPos.x == 0xFFFF) {
        return RETURNVALUE_NOERROR;
    }

    const Position& creaturePos = creature->getPosition();
    if (checkFloor && creaturePos.z != toPos.z) {
        return creaturePos.z > toPos.z ? RETURNVALUE_FIRSTGOUPSTAIRS : RETURNVALUE_FIRSTGODOWNSTAIRS;
    }

    if (!Position::areInRange<7, 5>(toPos, creaturePos)) {
        return RETURNVALUE_TOOFARAWAY;
    }

    if (checkLineOfSight && !g_game.canThrowObjectTo(creaturePos, toPos)) {
        return RETURNVALUE_CANNOTTHROW;
    }

    return RETURNVALUE_NOERROR;
}

Action* Actions::getAction(const Item* item)
{
    if (item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
        auto it = uniqueItemMap.find(item->getUniqueId());
        if (it != uniqueItemMap.end()) {
            return it->second;
        }
    }

    if (item->hasAttribute(ITEM_ATTRIBUTE_ACTIONID)) {
        auto it = actionItemMap.find(item->getActionId());
        if (it != actionItemMap.end()) {
            return it->second;
        }
    }

    auto it = useItemMap.find(item->getID());
    if (it != useItemMap.end()) {
        return it->second;
    }

    //rune items
    return g_spells->getRuneSpell(item->getID());
}

ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey)
{
    if (Door* door = item->getDoor()) {
        if (!door->canUse(player)) {
            return RETURNVALUE_CANNOTUSETHISOBJECT;
        }
    }

    Action* action = getAction(item);
    if (action) {
        if (action->isScripted()) {
            if (action->executeUse(player, item, pos, nullptr, pos, isHotkey)) {
                return RETURNVALUE_NOERROR;
            }
        } else if (action->function) {
            if (action->function(player, item, pos, nullptr, pos, isHotkey)) {
                return RETURNVALUE_NOERROR;
            }
        }
    }

    if (BedItem* bed = item->getBed()) {
        if (!bed->canUse(player)) {
            return RETURNVALUE_CANNOTUSETHISOBJECT;
        }

        bed->sleep(player);
        return RETURNVALUE_NOERROR;
    }

    if (Container* container = item->getContainer()) {
        Container* openContainer;

        //depot container
        if (DepotLocker* depot = container->getDepotLocker()) {
            DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId());
            myDepotLocker->setParent(depot->getParent());
            openContainer = myDepotLocker;
            player->setLastDepotId(depot->getDepotId());
        } else {
            openContainer = container;
        }

        uint32_t corpseOwner = container->getCorpseOwner();
        if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) {
            return RETURNVALUE_YOUARENOTTHEOWNER;
        }

        //open/close container
        int32_t oldContainerId = player->getContainerID(openContainer);
        if (oldContainerId != -1) {
            player->onCloseContainer(openContainer);
            player->closeContainer(oldContainerId);
        } else {
            player->addContainer(index, openContainer);
            player->onSendContainer(openContainer);
        }

        return RETURNVALUE_NOERROR;
    }

    const ItemType& it = Item::items[item->getID()];
    if (it.canReadText) {
        if (it.canWriteText) {
            player->setWriteItem(item, it.maxTextLen);
            player->sendTextWindow(item, it.maxTextLen, true);
        } else {
            player->setWriteItem(nullptr);
            player->sendTextWindow(item, 0, false);
        }

        return RETURNVALUE_NOERROR;
    }

    return RETURNVALUE_CANNOTUSETHISOBJECT;
}

bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey)
{
    player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::ACTIONS_DELAY_INTERVAL));
    player->stopWalk();

    if (isHotkey) {
        showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), -1));
    }

    ReturnValue ret = internalUseItem(player, pos, index, item, isHotkey);
    if (ret != RETURNVALUE_NOERROR) {
        player->sendCancelMessage(ret);
        return false;
    }
    return true;
}

bool Actions::useItemEx(Player* player, const Position& fromPos, const Position& toPos,
                        uint8_t toStackPos, Item* item, bool isHotkey, Creature* creature/* = nullptr*/)
{
    int32_t potions[] = {26031, 26030, 8473, 7591, 8472, 7588, 26029, 7590, 7589, 7620, 7618, 8704, 8474};
    int16_t potionID = item->getID();
    if(std::find(std::begin(potions), std::end(potions), potionID) == std::end(potions)) {
        player->setNextAction(OTSYS_TIME() + g_config.getNumber(ConfigManager::EX_ACTIONS_DELAY_INTERVAL));
    }
    player->stopWalk();

    Action* action = getAction(item);
    if (!action) {
        player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
        return false;
    }

    ReturnValue ret = action->canExecuteAction(player, toPos);
    if (ret != RETURNVALUE_NOERROR) {
        player->sendCancelMessage(ret);
        return false;
    }

    if (isHotkey) {
        showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), -1));
    }

    if (!action->executeUse(player, item, fromPos, action->getTarget(player, creature, toPos, toStackPos), toPos, isHotkey)) {
        if (!action->hasOwnErrorHandler()) {
            player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
        }
        return false;
    }
    return true;
}

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->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
}

Action::Action(LuaScriptInterface* _interface) :
    Event(_interface)
{
    allowFarUse = false;
    checkFloor = true;
    checkLineOfSight = true;
    function = nullptr;
}

Action::Action(const Action* copy) :
    Event(copy)
{
    allowFarUse = copy->allowFarUse;
    checkFloor = copy->checkFloor;
    checkLineOfSight = copy->checkLineOfSight;
    function = copy->function;
}

bool Action::configureEvent(const pugi::xml_node& node)
{
    pugi::xml_attribute allowFarUseAttr = node.attribute("allowfaruse");
    if (allowFarUseAttr) {
        setAllowFarUse(allowFarUseAttr.as_bool());
    }

    pugi::xml_attribute blockWallsAttr = node.attribute("blockwalls");
    if (blockWallsAttr) {
        setCheckLineOfSight(blockWallsAttr.as_bool());
    }

    pugi::xml_attribute checkFloorAttr = node.attribute("checkfloor");
    if (checkFloorAttr) {
        setCheckFloor(checkFloorAttr.as_bool());
    }

    return true;
}

bool Action::loadFunction(const pugi::xml_attribute& attr)
{
    const char* functionName = attr.as_string();
    if (strcasecmp(functionName, "increaseitemid") == 0) {
        function = increaseItemId;
    } else if (strcasecmp(functionName, "decreaseitemid") == 0) {
        function = decreaseItemId;
    } else {
        std::cout << "[Warning - Action::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl;
        return false;
    }

    scripted = false;
    return true;
}

bool Action::increaseItemId(Player*, Item* item, const Position&, Thing*, const Position&, bool)
{
    g_game.startDecay(g_game.transformItem(item, item->getID() + 1));
    return true;
}

bool Action::decreaseItemId(Player*, Item* item, const Position&, Thing*, const Position&, bool)
{
    g_game.startDecay(g_game.transformItem(item, item->getID() - 1));
    return true;
}

std::string Action::getScriptEventName() const
{
    return "onUse";
}

ReturnValue Action::canExecuteAction(const Player* player, const Position& toPos)
{
    if (!getAllowFarUse()) {
        return g_actions->canUse(player, toPos);
    } else {
        return g_actions->canUseFar(player, toPos, getCheckLineOfSight(), getCheckFloor());
    }
}

Thing* Action::getTarget(Player* player, Creature* targetCreature, const Position& toPosition, uint8_t toStackPos) const
{
    if (targetCreature) {
        return targetCreature;
    }
    return g_game.internalGetThing(player, toPosition, toStackPos, 0, STACKPOS_USETARGET);
}

bool Action::executeUse(Player* player, Item* item, const Position& fromPos, Thing* target, const Position& toPos, bool isHotkey)
{
    //onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if (!scriptInterface->reserveScriptEnv()) {
        std::cout << "[Error - Action::executeUse] Call stack overflow" << std::endl;
        return false;
    }

    ScriptEnvironment* env = scriptInterface->getScriptEnv();
    env->setScriptId(scriptId, scriptInterface);

    lua_State* L = scriptInterface->getLuaState();

    scriptInterface->pushFunction(scriptId);

    LuaScriptInterface::pushUserdata<Player>(L, player);
    LuaScriptInterface::setMetatable(L, -1, "Player");

    LuaScriptInterface::pushThing(L, item);
    LuaScriptInterface::pushPosition(L, fromPos);

    LuaScriptInterface::pushThing(L, target);
    LuaScriptInterface::pushPosition(L, toPos);

    LuaScriptInterface::pushBoolean(L, isHotkey);
    return scriptInterface->callFunction(6);
}
 
Solution
Back
Top