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

TFS 0.X Buy things on NPC with Bank Balance

potinho

Advanced OT User
Joined
Oct 11, 2009
Messages
1,403
Solutions
17
Reaction score
151
Location
Brazil
Hello guys,

Im trying to merge the solution from this post (TFS 0.X - Buy items with money backpack or bank balance 0.4 (https://otland.net/threads/buy-items-with-money-backpack-or-bank-balance-0-4.260563/post-2520072)) into my sources, but im failing. None errors when compiling, but in game it not work, i cant buy anything without money in backpack, even with a lot of money in bank balance. Can u guys point me what im doing wrong and help me to fix?
Here follow my code:

C++:
uint64_t Game::getMoney(const Cylinder* cylinder)
{
    if (!cylinder)
        return 0;

    std::list<Container*> listContainer;
    Container* tmpContainer = NULL;

    Thing* thing = NULL;
    Item* item = NULL;

    uint64_t moneyCount = 0;

    for (int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex(); ++i)
    {
        if (!(thing = cylinder->__getThing(i)) || !(item = thing->getItem()))
            continue;

        if ((tmpContainer = item->getContainer()))
            listContainer.push_back(tmpContainer);
        else if (item->getWorth())
            moneyCount += item->getWorth();
    }

    Container* container = NULL;
    while (listContainer.size() > 0)
    {
        container = listContainer.front();
        listContainer.pop_front();
        for (ItemList::const_iterator it = container->getItems(); it != container->getEnd(); ++it)
        {
            item = *it;
            if ((tmpContainer = item->getContainer()))
                listContainer.push_back(tmpContainer);
            else if (item->getWorth())
                moneyCount += item->getWorth();
        }
    }

    if (const Player* p = dynamic_cast<const Player*>(cylinder))
    {
        moneyCount += p->balance;
    }

    return moneyCount;
}

bool Game::removeMoney(Cylinder* cylinder, int64_t money, uint32_t flags /*= 0*/)
{
    if (!cylinder)
        return false;

    if (money <= 0)
        return true;

    Player* p = dynamic_cast<Player*>(cylinder);
    if (p)
    {
        money += p->balance;

        // Not enough money
        if (money < 0)
            return false;
    }

    typedef std::multimap<int32_t, Item*, std::less<int32_t> > MoneyMultiMap;
    MoneyMultiMap moneyMap;

    std::list<Container*> listContainer;
    Container* tmpContainer = NULL;

    Thing* thing = NULL;
    Item* item = NULL;

    int64_t moneyCount = 0;
    for (int32_t i = cylinder->__getFirstIndex(); i < cylinder->__getLastIndex(); ++i)
    {
        if (!(thing = cylinder->__getThing(i)) || !(item = thing->getItem()))
            continue;

        if ((tmpContainer = item->getContainer()))
            listContainer.push_back(tmpContainer);
        else if (item->getWorth())
        {
            moneyCount += item->getWorth();
            moneyMap.insert(std::make_pair(item->getWorth(), item));
        }
    }

    while (listContainer.size() > 0)
    {
        Container* container = listContainer.front();
        listContainer.pop_front();
        for (int32_t i = 0; i < (int32_t)container->size(); ++i)
        {
            Item* item = container->getItem(i);
            if ((tmpContainer = item->getContainer()))
                listContainer.push_back(tmpContainer);
            else if (item->getWorth())
            {
                moneyCount += item->getWorth();
                moneyMap.insert(std::make_pair(item->getWorth(), item));
            }
        }
    }

    // Not enough money
    if (moneyCount < money)
        return false;

    for (MoneyMultiMap::iterator mit = moneyMap.begin(); mit != moneyMap.end() && money > 0; ++mit)
    {
        if (!(item = mit->second))
            continue;

        internalRemoveItem(NULL, item);
        if (mit->first > money)
        {
            // Remove a monetary value from an item
            addMoney(cylinder, (int64_t)(item->getWorth() - money), flags);
            money = 0;
        }
        else
            money -= mit->first;

        mit->second = NULL;
    }

    moneyMap.clear();
    if (p)
    {
        p->balance -= money;
        std::stringstream ss;
        ss << "Paid " << money << " gold from bank account. Your account balance is now " << p->balance << " gold.";
        p->sendTextMessage(MSG_INFO_DESCR, ss.str());
    }

    return true;
}

Here's my bank system who work by talkaction:

Lua:
    <config name="command-bank-config"><![CDATA[
        transferDisabledVocations = {0} -- disable non vocation characters
    ]]></config>

    <talkaction words="!bank;/bank" event="script"><![CDATA[
        domodlib('command-bank-config')
        local config = {
            transferDisabledVocations = transferDisabledVocations
        }

        local function validAmount(amount)
            return (isNumber(amount) and amount > 0 and amount < 4294967296)
        end
        local function getAmount(amount, cid, f)
            return (amount == 'all' and f(cid) or tonumber(amount))
        end
        local function getPlayerVocationByName(name)
            local result = db.getResult("SELECT `vocation` FROM `players` WHERE `name` = " .. db.escapeString(name))
            if(result:getID() == -1) then
                return false
            end

            local value = result:getDataString("vocation")
            result:free()
            return value
        end

        function onSay(cid, words, param, channel)
            if(param == '') then
                doPlayerPopupFYI(cid,
                    "Bank management manual.\n\n" ..
                    "!bank or /bank balance - show your account balance\n" ..
                    "!bank or /bank deposit 100 - deposit 100 gold\n" ..
                    "!bank or /bank withdraw 50 - withdraw 50 gold\n" ..
                    "!bank or /bank transfer 30 God - transfer 30 gold to player God\n\n" ..
                    "Tip: you can also use 'all' as amount.\n" ..
                    "!bank or /bank deposit all - deposit all gold you have\n" ..
                    "!bank or /bank withdraw all - withdraw all gold from your bank account"
                )
                return true
            end

            local t = string.explode(param, " ", 2)
            local command = t[1]:lower()
            if(command == 'balance') then
                doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE, "Your account balance is " .. getPlayerBalance(cid) .. " gold.")
            elseif(command == 'deposit') then
                if(not t[2]) then
                    doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"Amount is required.")
                    return true
                end

                local amount = getAmount(t[2], cid, getPlayerMoney)
                if(validAmount(amount) and getPlayerMoney(cid) >= amount and doPlayerDepositMoney(cid, amount)) then
                    doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,amount .. " gold has been deposited.")
                else
                    doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"Not enough money to deposit.")
                end
            elseif(command == 'withdraw') then
                if(not t[2]) then
                    doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"Amount is required.")
                    return true
                end

                local amount = getAmount(t[2], cid, getPlayerBalance)
                if(validAmount(amount) and getPlayerBalance(cid) >= amount and doPlayerWithdrawMoney(cid, amount)) then
                    doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,amount .. " gold has been withdrawn.")
                else
                    doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"Not enough money to withdraw.")
                end
            elseif(command == 'transfer') then
                if(not t[2]) then
                    doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"Amount is required.")
                    return true
                end

                if(not t[3]) then
                    doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"Player name to transfer is required.")
                    return true
                end

                local amount, target = tonumber(t[2]), t[3]
                if(getPlayerGUID(cid) == getPlayerGUIDByName(target)) then
                    doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"You cannot transfer money to yourself.")
                elseif(isInArray(config.transferDisabledVocations, getPlayerVocation(cid))) then
                    doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"Your vocation cannot transfer money.")
                elseif(not validAmount(amount) or getPlayerBalance(cid) < amount) then
                    doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"Not enough money to transfer.")
                else
                    local targetVocation = getPlayerVocationByName(target)
                    if(not playerExists(target) or not targetVocation or isInArray(config.transferDisabledVocations, targetVocation) or not doPlayerTransferMoneyTo(cid, target, amount)) then
                        doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"This player does not exist on this world or have no vocation.")
                    else
                        doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"You have transferred " .. amount .. " gold to \"" .. target .."\".")
                    end
                end
            else
                doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE,"Invalid command usage. Use '!bank' to view manual.")
            end

            return true
        end
    ]]></talkaction>
 
I did not analyse whole C++ code - there can be other bugs -, but for sure first bug is here:
C++:
bool Game::removeMoney(Cylinder* cylinder, int64_t money, uint32_t flags /*= 0*/)
{
    if (!cylinder)
        return false;

    if (money <= 0)
        return true;

    Player* p = dynamic_cast<Player*>(cylinder);
    if (p)
    {
        money += p->balance;

        // Not enough money
        if (money < 0)
            return false;
    }
It should be:
C++:
bool Game::removeMoney(Cylinder* cylinder, int64_t money, uint32_t flags /*= 0*/)
{
    if (!cylinder)
        return false;

    if (money <= 0)
        return true;
as right now you are adding bank balance to 'money to remove' amount, not player money (defined below in moneyCount).

Anyway, you should not modify these C++ functions at all. Your doPlayerDepositMoney probably use doPlayerRemoveMoney(cid, amount) to remove money that player deposits. With these changes players will be able to deposit money from bank account to bank account, which will result in bugs (clone money or make money disappear).

If you want it to work only in NPCs, edit NPCs code (find+replace on 'npc' folder) to use custom functions for money ex. getPlayerMoneyNpc, doPlayerRemoveMoneyNpc and doPlayerAddMoneyNpc. Then define these functions in some global library - to make every NPC use it - as:
Lua:
function getPlayerMoneyNpc(cid)
    return getPlayerMoney(cid) + getPlayerBalance(cid)
end

function doPlayerRemoveMoneyNpc(cid, amount)
    local playerMoney = getPlayerMoney(cid)
    local playerBankBalance = getPlayerBalance(cid)
    if playerMoney + playerBankBalance < amount then
        return false
    end
   
    local toRemoveFromMoney = math.min(playerMoney, amount)
    doPlayerRemoveMoney(cid, toRemoveFromMoney)

    local toRemoveFromBankBalance = amount - toRemoveFromMoney
    if toRemoveFromBankBalance > 0 then
-- here you can add message about removing money from bank balance
        doPlayerSetBalance(cid, playerBankBalance - toRemoveLeft)
    end

    return true
end

function doPlayerAddMoneyNpc(cid, amount)
-- here you can decide to add money from NPC (ex. for sold items) to bank balance, not BP
-- return doPlayerSetBalance(cid, getPlayerBalance(cid) + amount)

-- OR leave original code that removes money from BP:
   return doPlayerAddMoney(cid, amount)
end
CODE NOT TESTED

After moving this part of NPC logic to Lua, you are able to let players decide with some Storage Value, if they prefer to pay with money or bank balance (remove money from BP or bank balance first).
 
I did not analyse whole C++ code - there can be other bugs -, but for sure first bug is here:
C++:
bool Game::removeMoney(Cylinder* cylinder, int64_t money, uint32_t flags /*= 0*/)
{
    if (!cylinder)
        return false;

    if (money <= 0)
        return true;

    Player* p = dynamic_cast<Player*>(cylinder);
    if (p)
    {
        money += p->balance;

        // Not enough money
        if (money < 0)
            return false;
    }
It should be:
C++:
bool Game::removeMoney(Cylinder* cylinder, int64_t money, uint32_t flags /*= 0*/)
{
    if (!cylinder)
        return false;

    if (money <= 0)
        return true;
as right now you are adding bank balance to 'money to remove' amount, not player money (defined below in moneyCount).

Anyway, you should not modify these C++ functions at all. Your doPlayerDepositMoney probably use doPlayerRemoveMoney(cid, amount) to remove money that player deposits. With these changes players will be able to deposit money from bank account to bank account, which will result in bugs (clone money or make money disappear).

If you want it to work only in NPCs, edit NPCs code (find+replace on 'npc' folder) to use custom functions for money ex. getPlayerMoneyNpc, doPlayerRemoveMoneyNpc and doPlayerAddMoneyNpc. Then define these functions in some global library - to make every NPC use it - as:
Lua:
function getPlayerMoneyNpc(cid)
    return getPlayerMoney(cid) + getPlayerBalance(cid)
end

function doPlayerRemoveMoneyNpc(cid, amount)
    local playerMoney = getPlayerMoney(cid)
    local playerBankBalance = getPlayerBalance(cid)
    if playerMoney + playerBankBalance < amount then
        return false
    end
  
    local toRemoveFromMoney = math.min(playerMoney, amount)
    doPlayerRemoveMoney(cid, toRemoveFromMoney)

    local toRemoveFromBankBalance = amount - toRemoveFromMoney
    if toRemoveFromBankBalance > 0 then
-- here you can add message about removing money from bank balance
        doPlayerSetBalance(cid, playerBankBalance - toRemoveLeft)
    end

    return true
end

function doPlayerAddMoneyNpc(cid, amount)
-- here you can decide to add money from NPC (ex. for sold items) to bank balance, not BP
-- return doPlayerSetBalance(cid, getPlayerBalance(cid) + amount)

-- OR leave original code that removes money from BP:
   return doPlayerAddMoney(cid, amount)
end
CODE NOT TESTED

After moving this part of NPC logic to Lua, you are able to let players decide with some Storage Value, if they prefer to pay with money or bank balance (remove money from BP or bank balance first).
Thanks for the tips Gesior.
 
Back
Top