• 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 NPC accept money on hands and bank money

gmstrikker

Well-Known Member
Joined
Jul 30, 2014
Messages
458
Solutions
1
Reaction score
50
Using 0.4 source pack, like Fir3element/3777

Should be possible to make when you buy stuff on NPC, like:
Code:
       <parameter key="shop_buyable" value="jagged sword,8602,300;steel axe,8601,300;daramanian mace,2439,300;crimson sword,7385,3000;barbarian axe,2429,3000;clerical mace,2423,3000" />

NPC accept money from your hands, and if you do not have, it accept money from your bank balance?

banknpc.lua
Code:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

local Topic, count, transfer = {}, {}, {}

function onCreatureAppear(cid)            npcHandler:onCreatureAppear(cid)        end
function onCreatureDisappear(cid)        npcHandler:onCreatureDisappear(cid)        end
function onCreatureSay(cid, type, msg)        npcHandler:onCreatureSay(cid, type, msg)    end
function onThink()                npcHandler:onThink()                end


local function getCount(s)
    local b, e = s:find('%d+')
    return b and e and math.min(4294967295, tonumber(s:sub(b, e))) or -1
end

local function findPlayer(name)
    local resultId = db.storeQuery('SELECT name FROM players WHERE name=' .. db.escapeString(name) .. ' LIMIT 1'), nil
    if resultId == false then
        return
    end
    local r = result.getDataString(resultId, "name")
    result.free(resultId)
    return r
end

local function getTown(name)
    local resultId = db.storeQuery('SELECT town_id FROM players WHERE name=' .. db.escapeString(name) .. ' LIMIT 1'), nil
    if resultId == false then
        return
    end
    local r = result.getDataInt(resultId, "town_id")
    result.free(resultId)
    return r
end

local function vocation(name)
    local resultId = db.storeQuery('SELECT vocation FROM players WHERE name=' .. db.escapeString(name) .. ' LIMIT 1'), nil
    if resultId == false then
        return
    end
    local r = result.getDataInt(resultId, "vocation")
    result.free(resultId)
    return r
end

local function updatePlayerBalance(name, value)
    db.query('UPDATE players SET balance=' .. value .. ' WHERE name=' .. db.escapeString(name) .. ' LIMIT 1')
end

function greet(cid)
    Topic[cid], count[cid], transfer[cid] = nil, nil, nil
    return true
end

function creatureSayCallback(cid, type, msg)
    if not npcHandler:isFocused(cid) then
        return false
    elseif msgcontains(msg, 'balance') then
        npcHandler:say('Your account balance is ' .. getPlayerBalance(cid) .. ' gold.', cid)
        Topic[cid] = nil
    elseif msgcontains(msg, 'deposit') and msgcontains(msg, 'all') then
        if getPlayerMoney(cid) == 0 then
            npcHandler:say('You don\'t have any gold with you.', cid)
            Topic[cid] = nil
        else
            count[cid] = getPlayerMoney(cid)
            npcHandler:say('Would you really like to deposit ' .. count[cid] .. ' gold?', cid)
            Topic[cid] = 2
        end
    elseif msgcontains(msg, 'deposit') then
        if getCount(msg) == 0 then
            npcHandler:say('You are joking, aren\'t you??', cid)
            Topic[cid] = nil
        elseif getCount(msg) ~= -1 then
            if getPlayerMoney(cid) >= getCount(msg) then
                count[cid] = getCount(msg)
                npcHandler:say('Would you really like to deposit ' .. count[cid] .. ' gold?', cid)
                Topic[cid] = 2
            else
                npcHandler:say('You do not have enough gold.', cid)
                Topic[cid] = nil
            end
        elseif getPlayerMoney(cid) == 0 then
            npcHandler:say('You don\'t have any gold with you.', cid)
            Topic[cid] = nil
        else
            npcHandler:say('Please tell me how much gold it is you would like to deposit.', cid)
            Topic[cid] = 1
        end
    elseif Topic[cid] == 1 then
        if getCount(msg) == -1 then
            npcHandler:say('Please tell me how much gold it is you would like to deposit.', cid)
            Topic[cid] = 1
        elseif getPlayerMoney(cid) >= getCount(msg) then
            count[cid] = getCount(msg)
            npcHandler:say('Would you really like to deposit ' .. count[cid] .. ' gold?', cid)
            Topic[cid] = 2
        else
            npcHandler:say('You do not have enough gold.', cid)
            Topic[cid] = nil
        end
    elseif msgcontains(msg, 'yes') and Topic[cid] == 2 then
        if doPlayerRemoveMoney(cid, count[cid]) then
            doPlayerSetBalance(cid, getPlayerBalance(cid) + count[cid])
            updatePlayerBalance(getCreatureByName(cid), getPlayerBalance(cid))
            npcHandler:say('Alright, we have added the amount of ' .. count[cid] .. ' gold to your balance. You can withdraw your money anytime you want to.', cid)
        else
            npcHandler:say('I am inconsolable, but it seems you have lost your gold. I hope you get it back.', cid)
        end
        Topic[cid] = nil
    elseif msgcontains(msg, 'no') and Topic[cid] == 2 then
        npcHandler:say('As you wish. Is there something else I can do for you?', cid)
        Topic[cid] = nil
    elseif msgcontains(msg, 'withdraw') then
        if getCount(msg) == 0 then
            npcHandler:say('Sure, you want nothing you get nothing!', cid)
            Topic[cid] = nil
        elseif getCount(msg) ~= -1 then
            if getPlayerBalance(cid) >= getCount(msg) then
                count[cid] = getCount(msg)
                npcHandler:say('Are you sure you wish to withdraw ' .. count[cid] .. ' gold from your bank account?', cid)
                Topic[cid] = 4
            else
                npcHandler:say('There is not enough gold on your account.', cid)
                Topic[cid] = nil
            end
        elseif getPlayerBalance(cid) == 0 then
            npcHandler:say('You don\'t have any money on your bank account.', cid)
            Topic[cid] = nil
        else
            npcHandler:say('Please tell me how much gold you would like to withdraw.', cid)
            Topic[cid] = 3
        end
    elseif Topic[cid] == 3 then
        if getCount(msg) == -1 then
            npcHandler:say('Please tell me how much gold you would like to withdraw.', cid)
            Topic[cid] = 3
        elseif getPlayerBalance(cid) >= getCount(msg) then
            count[cid] = getCount(msg)
            npcHandler:say('Are you sure you wish to withdraw ' .. count[cid] .. ' gold from your bank account?', cid)
            Topic[cid] = 4
        else
            npcHandler:say('There is not enough gold on your account.', cid)
            Topic[cid] = nil
        end
    elseif msgcontains(msg, 'yes') and Topic[cid] == 4 then
        if getPlayerBalance(cid) >= count[cid] then
            doPlayerAddMoney(cid, count[cid])
            doPlayerSetBalance(cid, getPlayerBalance(cid) - count[cid])
            updatePlayerBalance(getCreatureByName(cid), getPlayerBalance(cid))
            npcHandler:say('Here you are, ' .. count[cid] .. ' gold. Please let me know if there is something else I can do for you.', cid)
        else
            npcHandler:say('There is not enough gold on your account.', cid)
        end
        Topic[cid] = nil
    elseif msgcontains(msg, 'no') and Topic[cid] == 4 then
        npcHandler:say('The customer is king! Come back anytime you want to if you wish to withdraw your money.', cid)
        Topic[cid] = nil
    elseif msgcontains(msg, 'transfer') then
        if getCount(msg) == 0 then
            npcHandler:say('Please think about it. Okay?', cid)
            Topic[cid] = nil
        elseif getCount(msg) ~= -1 then
     
            count[cid] = getCount(msg)
            if getPlayerBalance(cid) >= count[cid] then
                npcHandler:say('Who would you like to transfer ' .. count[cid] .. ' gold to?', cid)
                Topic[cid] = 6
            else
                npcHandler:say('There is not enough gold on your account.', cid)
                Topic[cid] = nil
            end

        else
            npcHandler:say('Please tell me the amount of gold you would like to transfer.', cid)
            Topic[cid] = 5
        end
    elseif Topic[cid] == 5 then
        if getCount(msg) == -1 then
            npcHandler:say('Please tell me the amount of gold you would like to transfer.', cid)
            Topic[cid] = 5
        else
            count[cid] = getCount(msg)
            if getPlayerBalance(cid) >= count[cid] then
                npcHandler:say('Who would you like to transfer ' .. count[cid] .. ' gold to?', cid)
                Topic[cid] = 6
            else
                npcHandler:say('There is not enough gold on your account.', cid)
                Topic[cid] = nil
            end
        end
    elseif Topic[cid] == 6 then
        local v = getCreatureByName(msg)
        if getPlayerBalance(cid) >= count[cid] then
            local temp = (v and getCreatureName(v) or false)
            if temp then
                if getPlayerTown(v) == 7 then
                    if getPlayerTown(cid) ~= getPlayerTown(v) then
                        npcHandler:say('You can not transfer money to players from Rookgaardia if you are not from Rookgaardia.', cid)
                        Topic[cid] = nil
                        return true
                    end
                elseif getPlayerTown(v) == 9 then
                    if getPlayerTown(cid) ~= getPlayerTown(v) then
                        npcHandler:say('You can not transfer money to players from Dawn Hills if you are not from Dawn Hills.', cid)
                        Topic[cid] = nil
                        return true
                    end
                end


                transfer[cid] = msg
                npcHandler:say('Would you really like to transfer ' .. count[cid] .. ' gold to ' .. temp .. '?', cid)
                Topic[cid] = 7
            elseif getPlayerGUIDByName(msg:lower()) ~= 0 then
                if getTown(msg:lower()) == 7 then
                    if getPlayerTown(cid) ~= 7 then
                        npcHandler:say('You can not transfer money to players from Rookgaardia if you are not from Rookgaardia.', cid)
                        Topic[cid] = nil
                        return true
                    end
                elseif getTown(msg:lower()) == 9 then
                    if getPlayerTown(cid) ~= 9 then
                        npcHandler:say('You can not transfer money to players from Dawn Hills if you are not from Dawn Hills.', cid)
                        Topic[cid] = nil
                        return true
                    end
                end


                transfer[cid] = msg
                npcHandler:say('Would you really like to transfer ' .. count[cid] .. ' gold to ' .. findPlayer(msg) .. '?', cid)
                Topic[cid] = 7              
            else
                npcHandler:say('This player does not exist on this world!', cid)
                Topic[cid] = nil
            end
        else
            npcHandler:say('There is not enough gold on your account.', cid)
            Topic[cid] = nil
        end
    elseif Topic[cid] == 7 and msgcontains(msg, 'yes') then
        if getPlayerBalance(cid) >= count[cid] then
            local v = getCreatureByName(transfer[cid])
            if v then
                doPlayerSetBalance(cid, getPlayerBalance(cid) - count[cid])
                updatePlayerBalance(getCreatureName(cid), getPlayerBalance(cid))
                doPlayerSetBalance(v, getPlayerBalance(v) + count[cid])
                updatePlayerBalance(getCreatureName(v), getPlayerBalance(v))
                npcHandler:say('Very well. You have transferred ' .. count[cid] .. ' gold to ' .. getCreatureName(v) .. '.', cid)
            elseif findPlayer(transfer[cid]):lower() == transfer[cid]:lower() then
                doPlayerSetBalance(cid, getPlayerBalance(cid) - count[cid])
                updatePlayerBalance(getCreatureName(cid), getPlayerBalance(cid))
                db.query('UPDATE players SET balance=balance+' .. count[cid] .. ' WHERE name=' .. db.escapeString(transfer[cid]) .. ' LIMIT 1')
                npcHandler:say('Very well. You have transferred ' .. count[cid] .. ' gold to ' .. findPlayer(transfer[cid]) .. '.', cid)
            else
                npcHandler:say('This player does not exist.', cid)
            end
        else
            npcHandler:say('There is not enough gold on your account.', cid)
        end
        Topic[cid] = nil
    elseif Topic[cid] == 7 and msgcontains(msg, 'no') then
        npcHandler:say('Alright, is there something else I can do for you?', cid)
        Topic[cid] = nil
    elseif msgcontains(msg, 'change gold') then
        npcHandler:say('How many platinum coins would you like to get?', cid)
        Topic[cid] = 8
    elseif Topic[cid] == 8 then
        if getCount(msg) < 1 then
            npcHandler:say('Hmm, can I help you with something else?', cid)
            Topic[cid] = nil
        else
            count[cid] = math.min(500, getCount(msg))
            npcHandler:say('So you would like me to change ' .. count[cid] * 100 .. ' of your gold coins into ' .. count[cid] .. ' platinum coins?', cid)
            Topic[cid] = 9
        end
    elseif Topic[cid] == 9 then
        if msgcontains(msg, 'yes') then
            if doPlayerRemoveItem(cid, 2148, count[cid] * 100) then
                npcHandler:say('Here you are.', cid)
                doPlayerAddItem(cid, 2152, count[cid])
            else
                npcHandler:say('Sorry, you do not have enough gold coins.', cid)
            end
        else
            npcHandler:say('Well, can I help you with something else?', cid)
        end
        Topic[cid] = nil
    elseif msgcontains(msg, 'change platinum') then
        npcHandler:say('Would you like to change your platinum coins into gold or crystal?', cid)
        Topic[cid] = 10
    elseif Topic[cid] == 10 then
        if msgcontains(msg, 'gold') then
            npcHandler:say('How many platinum coins would you like to change into gold?', cid)
            Topic[cid] = 11
        elseif msgcontains(msg, 'crystal') then
            npcHandler:say('How many crystal coins would you like to get?', cid)
            Topic[cid] = 13
        else
            npcHandler:say('Well, can I help you with something else?', cid)
            Topic[cid] = nil
        end
    elseif Topic[cid] == 11 then
        if getCount(msg) < 1 then
            npcHandler:say('Hmm, can I help you with something else?', cid)
            Topic[cid] = nil
        else
            count[cid] = math.min(500, getCount(msg))
            npcHandler:say('So you would like me to change ' .. count[cid] .. ' of your platinum coins into ' .. count[cid] * 100 .. ' gold coins for you?', cid)
            Topic[cid] = 12
        end
    elseif Topic[cid] == 12 then
        if msgcontains(msg, 'yes') then
            if doPlayerRemoveItem(cid, 2152, count[cid]) then
                npcHandler:say('Here you are.', cid)
                doPlayerAddItem(cid, 2148, count[cid] * 100)
            else
                npcHandler:say('Sorry, you do not have enough platinum coins.', cid)
            end
        else
            npcHandler:say('Well, can I help you with something else?', cid)
        end
        Topic[cid] = nil
    elseif Topic[cid] == 13 then
        if getCount(msg) < 1 then
            npcHandler:say('Hmm, can I help you with something else?', cid)
            Topic[cid] = nil
        else
            count[cid] = math.min(500, getCount(msg))
            npcHandler:say('So you would like me to change ' .. count[cid] * 100 .. ' of your platinum coins into ' .. count[cid] .. ' crystal coins for you?', cid)
            Topic[cid] = 14
        end
    elseif Topic[cid] == 14 then
        if msgcontains(msg, 'yes') then
            if doPlayerRemoveItem(cid, 2152, count[cid] * 100) then
                npcHandler:say('Here you are.', cid)
                doPlayerAddItem(cid, 2160, count[cid])
            else
                npcHandler:say('Sorry, you do not have enough platinum coins.', cid)
            end
        else
            npcHandler:say('Well, can I help you with something else?', cid)
        end
        Topic[cid] = nil
    elseif msgcontains(msg, 'change crystal') then
        npcHandler:say('How many crystal coins would you like to change into platinum?', cid)
        Topic[cid] = 15
    elseif Topic[cid] == 15 then
        if getCount(msg) == -1 or getCount(msg) == 0 then
            npcHandler:say('Hmm, can I help you with something else?', cid)
            Topic[cid] = nil
        else
            count[cid] = math.min(500, getCount(msg))
            npcHandler:say('So you would like me to change ' .. count[cid] .. ' of your crystal coins into ' .. count[cid] * 100 .. ' platinum coins for you?', cid)
            Topic[cid] = 16
        end
    elseif Topic[cid] == 16 then
        if msgcontains(msg, 'yes') then
            if doPlayerRemoveItem(cid, 2160, count[cid]) then
                npcHandler:say('Here you are.', cid)
                doPlayerAddItem(cid, 2152, count[cid] * 100)
            else
                npcHandler:say('Sorry, you do not have enough crystal coins.', cid)
            end
        else
            npcHandler:say('Well, can I help you with something else?', cid)
        end
        Topic[cid] = nil
    elseif msgcontains(msg, 'change') then
        npcHandler:say('There are three different coin types in Tibia: 100 gold coins equal 1 platinum coin, 100 platinum coins equal 1 crystal coin. So if you\'d like to change 100 gold into 1 platinum, simply say \'{change gold}\' and then \'1 platinum\'.', cid)
        Topic[cid] = nil
    elseif msgcontains(msg, 'bank') then
        npcHandler:say('We can change money for you. You can also access your bank account.', cid)
        Topic[cid] = nil
    end
    return true
end

npcHandler:setCallback(CALLBACK_GREET, greet)
npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())

- ps: without make buy items like on 7x, saying buy x item, i mean, should possible do it in 'trade' square?
 
Alright, because you are annoying and can't find the response for yourself, I did the search for you...
In your server's source under npc.cpp at line 1475 you can find the code below
C++:
case ACTION_BUYITEM:
                {
                    const ItemType& iit = Item::items[npcState->itemId];
                    if(iit.id != 0)
                    {
                        uint64_t moneyCount = it->intValue;
                        if(it->strValue == "|PRICE|")
                            moneyCount = npcState->price * npcState->amount;

                        int32_t subType = -1;
                        if(iit.hasSubType())
                            subType = npcState->subType;

                        if(g_game.getMoney(player) >= moneyCount)
                        {
                            int32_t amount = npcState->amount;
                            if(iit.stackable)
                            {
                                while(amount > 0)
                                {
                                    int32_t stack = std::min(100, amount);
                                    Item* item = Item::CreateItem(iit.id, stack);
                                    if(g_game.internalPlayerAddItem(this, player, item) != RET_NOERROR)
                                    {
                                        delete item;
                                        amount = npcState->amount - amount;
                                        break;
                                    }

                                    amount -= stack;
                                }
                            }
                            else
                            {
                                for(int32_t i = 0; i < amount; ++i)
                                {
                                    Item* item = Item::CreateItem(iit.id, subType);
                                    if(g_game.internalPlayerAddItem(this, player, item) != RET_NOERROR)
                                    {
                                        delete item;
                                        amount = i + 1;
                                        break;
                                    }
                                }
                            }

                            if(it->strValue == "|PRICE|")
                                moneyCount = npcState->price * amount;

                            g_game.removeMoney(player, moneyCount);
                        }
                    }
                    break;

first you need to make sure that the line below returns true and that it consider your bank balance,
if(g_game.getMoney(player) >= moneyCount)
then
you need to make that the line below indeed removes the money in your hand, then in your bank balance
g_game.removeMoney(player, moneyCount);
 
SO I did my own research, and the code in g_game.cpp, for the function removemoney() is the following
Code:
bool Game::removeMoney(Cylinder* cylinder, int64_t money, uint32_t flags /*= 0*/)
{
    if(!cylinder)
        return false;

    if(money <= 0)
        return true;

    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() && money > 0; ++i)
    {
        if(!(thing = cylinder->__getThing(i)) || !(item = thing->getItem()))
            continue;

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

    while(listContainer.size() > 0 && money > 0)
    {
        Container* container = listContainer.front();
        listContainer.pop_front();
        for(int32_t i = 0; i < (int32_t)container->size() && money > 0; i++)
        {
            Item* item = container->getItem(i);
            if((tmpContainer = item->getContainer()))
                listContainer.push_back(tmpContainer);
            else if(item->getWorth() != 0)
            {
                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)
    {
        Item* item = mit->second;
        if(!item)
            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();
    return money == 0;
}

This is a boolean function, that returns the value false. cause it does not consider the money from your bank balance
-- >

  1. if(moneyCount < money)
  2. return false;
 
IF you want to withdraw the amount from the bank balance, instead of imidiatly returning false. when the condition -" if(moneyCount < money) " is met, you need to add another condition before returning false.
first you should get the number of money to withdraw from bank account with this formula ->
money_to_withdraw = moneycount - money;
because the cost minus your money on hand is equal to the money that will be withdraw from bank balance.

you need then to add the condition, if (player.bank balance < money to withdraw) then returns false.
 
Then before the following code that removes the coin from your inventory,
C++:
 for(MoneyMultiMap::iterator mit = moneyMap.begin(); mit != moneyMap.end() && money > 0; ++mit)
    {
        Item* item = mit->second;
        if(!item)
            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;
    }
you should remove the the money from your balance first,
do something like
player.bankbalance = player.bankbalance - money to withdraw;
moneycount = moneycount - money to withdraw; // it removes the amount of money taken from bank balance to the amount left to pay, so once the for loops enter, it removes the exact money that the players have on hand.

You do need to recompile your sources for the changes to take effect.
Saying this considering you are a total noob.
 
@danick10 did u understand what this function do?
Code:
[LIST=1]
[*] for(MoneyMultiMap::iterator mit = moneyMap.begin(); mit != moneyMap.end() && money > 0; ++mit)
[*]    {
[*]        Item* item = mit->second;
[*]        if(!item)
[*]            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;
[*]    }
[/LIST]

Little confusing for me...
To return false or not it's just change that part on game.cpp to
Code:
    // Not enough money
    if(moneyCount < money && player->balance < money)
        return false;

But there is one part left, to check in: for(MoneyMultiMap::iterator mit = moneyMap.begin(); mit != moneyMap.end() && money > 0; ++mit)
if(player_have_money_on_backpack) {
remove player coins on backpack
}
else if(player_dont_have_money_on_backpack) {
remove player bank balance
}
 
Back
Top