Ir para conteúdo

Featured Replies

Postado

Sorry I don't speak spanish so you will have to bare with me.

 

This is a new way for people to create npc's which use different types of currency, rather than a coming up with different items to trade with the npc or trying to edit the npc modules this method simplifies everything by providing the npc with a npc currency id.

 

All this npc currency id is, is a storage value.. pretty simple eh?

If the npc doesn't have a currency id then it will use the normal currency e.g. gold, plat, cc etc..

 

I originally posted this on otland, but fuck them xD

 

Using Lailene here you can see she has a currency attribute with id of 123456

<?xml version="1.0" encoding="UTF-8"?>
<npc name="Lailene" currency="123456" script="lailene.lua" walkinterval="2000" floorchange="0" speechbubble="2">
<health now="100" max="100"/>
<look type="279" head="114" body="94" legs="113" feet="114" addons="0"/>
</npc>

 

Now any player who has a storage value of 123456 can purchase things from her shop provided they have enough value stored within the storage, similar to having money in the bank.

The money or in this case the storage value is added and removed from the player in real time.

 

Lets get to the code

 

game.cpp

Find this

bool Game::removeMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)

Replace the whole function with this.

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

    if (money == 0) {
        return true;
    }
    uint32_t currencyId = 0;
    Player* player;
    if (Creature* creature = cylinder->getCreature()) {
        if (Player* p = creature->getPlayer()) {
            currencyId = p->getNpcCurrencyId();
            player = p;
        }
    }
    if (!currencyId) {
        std::vector<Container*> containers;

        std::multimap<uint32_t, Item*> moneyMap;
        uint64_t moneyCount = 0;

        for (size_t i = cylinder->getFirstIndex(), j = cylinder->getLastIndex(); i < j; ++i) {
            Thing* thing = cylinder->getThing(i);
            if (!thing) {
                continue;
            }

            Item* item = thing->getItem();
            if (!item) {
                continue;
            }

            Container* container = item->getContainer();
            if (container) {
                containers.push_back(container);
            }
            else {
                const uint32_t worth = item->getWorth();
                if (worth != 0) {
                    moneyCount += worth;
                    moneyMap.emplace(worth, item);
                }
            }
        }

        size_t i = 0;
        while (i < containers.size()) {
            Container* container = containers[i++];
            for (Item* item : container->getItemList()) {
                Container* tmpContainer = item->getContainer();
                if (tmpContainer) {
                    containers.push_back(tmpContainer);
                }
                else {
                    const uint32_t worth = item->getWorth();
                    if (worth != 0) {
                        moneyCount += worth;
                        moneyMap.emplace(worth, item);
                    }
                }
            }
        }

        if (moneyCount < money) {
            return false;
        }

        for (const auto& moneyEntry : moneyMap) {
            Item* item = moneyEntry.second;
            if (moneyEntry.first < money) {
                internalRemoveItem(item);
                money -= moneyEntry.first;
            }
            else if (moneyEntry.first > money) {
                const uint32_t worth = moneyEntry.first / item->getItemCount();
                const uint32_t removeCount = (money / worth) + 1;
                addMoney(cylinder, (worth * removeCount) - money, flags);
                internalRemoveItem(item, removeCount);
                break;
            }
            else {
                internalRemoveItem(item);
                break;
            }
        }
    }
    else {
        int32_t value;
        player->getStorageValue(currencyId, value);
        if (value < money) {
            return false;
        }
        player->addStorageValue(currencyId, value - money);
    }
    return true;
}

Next find this

void Game::addMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)

Replace the whole function with this

void Game::addMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
{
    if (money == 0) {
        return;
    }

    if (Creature* creature = cylinder->getCreature()) {
        if (Player* player = creature->getPlayer()) {
            if(uint32_t currencyId = player->getNpcCurrencyId()){
                int32_t value;
                player->getStorageValue(currencyId, value);
                player->addStorageValue(currencyId, value + money);
                return;
            }
        }
    }

    uint32_t crystalCoins = money / 10000;
    money -= crystalCoins * 10000;
    while (crystalCoins > 0) {
        const uint16_t count = std::min<uint32_t>(100, crystalCoins);

        Item* remaindItem = Item::CreateItem(ITEM_CRYSTAL_COIN, count);

        ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
        if (ret != RETURNVALUE_NOERROR) {
            internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
        }

        crystalCoins -= count;
    }

    uint16_t platinumCoins = money / 100;
    if (platinumCoins != 0) {
        Item* remaindItem = Item::CreateItem(ITEM_PLATINUM_COIN, platinumCoins);

        ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
        if (ret != RETURNVALUE_NOERROR) {
            internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
        }

        money -= platinumCoins * 100;
    }

    if (money != 0) {
        Item* remaindItem = Item::CreateItem(ITEM_GOLD_COIN, money);

        ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
        if (ret != RETURNVALUE_NOERROR) {
            internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
        }
    }

}

 

npc.cpp
Look for this

    pugi::xml_attribute attr;
    if ((attr = npcNode.attribute("speed"))) {
        baseSpeed = pugi::cast<uint32_t>(attr.value());
    } else {
        baseSpeed = 100;
    }

Right underneath that you are going to place this.

    if ((attr = npcNode.attribute("currency"))) {
        currency = pugi::cast<uint32_t>(attr.value());
    }

 

npc.h
Look for this

        bool isPushable() const final {
            return walkTicks > 0;
        }

Place this right underneath

        uint32_t getCurrencyId() const {
            return currency;
        }

Look for this

uint32_t walkTicks;

Place this right underneath

uint32_t currency;

 

player.cpp
Find this

void Player::openShopWindow(Npc* npc, const std::list<ShopInfo>& shop)

Replace that function with this

void Player::openShopWindow(Npc* npc, const std::list<ShopInfo>& shop)
{
    shopItemList = shop;
    sendShop(npc);
    sendSaleItemList(npc);
}

Next find this

bool Player::updateSaleShopList(const Item* item)

Replace that function with this

bool Player::updateSaleShopList(const Item* item)
{
    uint16_t itemId = item->getID();
    if (itemId != ITEM_GOLD_COIN && itemId != ITEM_PLATINUM_COIN && itemId != ITEM_CRYSTAL_COIN) {
        auto it = std::find_if(shopItemList.begin(), shopItemList.end(), [itemId](const ShopInfo& shopInfo) { return shopInfo.itemId == itemId && shopInfo.sellPrice != 0; });
        if (it == shopItemList.end()) {
            const Container* container = item->getContainer();
            if (!container) {
                return false;
            }

            const auto& items = container->getItemList();
            return std::any_of(items.begin(), items.end(), [this](const Item* containerItem) {
                return updateSaleShopList(containerItem);
            });
        }
    }

    if (client) {
        client->sendSaleItemList(shopOwner, shopItemList);
    }
    return true;
}

Next you are going to look for

uint64_t Player::getMoney() const

Now right underneath that function you are going to place these.

uint64_t Player::getMoney(Npc* npc) const
{
    uint64_t cash;
    setNpcCurrencyId(npc);
    uint32_t currencyId = getNpcCurrencyId();
    if (currencyId) {
        int32_t value;
        getStorageValue(currencyId, value);
        cash = (uint64_t)value;
    }
    else {
        cash = getMoney();
    }
    return cash;
}


void Player::setNpcCurrencyId(Npc* npc) const{
    currencyId = npc->getCurrencyId();
}

uint32_t Player::getNpcCurrencyId() const {
    return currencyId;
}

 

player.h
Look for this

        uint64_t getMoney() const;

Place this right underneath

        uint64_t getMoney(Npc*) const;

        void setNpcCurrencyId(Npc*) const;

        uint32_t getNpcCurrencyId() const;

Find this

        void sendShop(Npc* npc) const {
            if (client) {
                client->sendShop(npc, shopItemList);
            }
        }

Place this right underneath

        void sendSaleItemList(Npc* npc) const {
            if (client) {
                client->sendSaleItemList(npc, shopItemList);
            }
        }

Find this

        uint32_t manaMax;

Place this right underneath

         mutable uint32_t currencyId;

 

protocolgame.cpp

Now find this function

void ProtocolGame::sendSaleItemList(const std::list<ShopInfo>& shop)

Place this right underneath

void ProtocolGame::sendSaleItemList(Npc* npc, const std::list<ShopInfo>& shop)
{
    NetworkMessage msg;
    msg.addByte(0x7B);
    msg.add<uint64_t>(player->getMoney(npc));

    std::map<uint16_t, uint32_t> saleMap;

    if (shop.size() <= 5) {
        // For very small shops it's not worth it to create the complete map
        for (const ShopInfo& shopInfo : shop) {
            if (shopInfo.sellPrice == 0) {
                continue;
            }

            int8_t subtype = -1;

            const ItemType& itemType = Item::items[shopInfo.itemId];
            if (itemType.hasSubType() && !itemType.stackable) {
                subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType);
            }

            uint32_t count = player->getItemTypeCount(shopInfo.itemId, subtype);
            if (count > 0) {
                saleMap[shopInfo.itemId] = count;
            }
        }
    } else {
        // Large shop, it's better to get a cached map of all item counts and use it
        // We need a temporary map since the finished map should only contain items
        // available in the shop
        std::map<uint32_t, uint32_t> tempSaleMap;
        player->getAllItemTypeCount(tempSaleMap);

        // We must still check manually for the special items that require subtype matches
        // (That is, fluids such as potions etc., actually these items are very few since
        // health potions now use their own ID)
        for (const ShopInfo& shopInfo : shop) {
            if (shopInfo.sellPrice == 0) {
                continue;
            }

            int8_t subtype = -1;

            const ItemType& itemType = Item::items[shopInfo.itemId];
            if (itemType.hasSubType() && !itemType.stackable) {
                subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType);
            }

            if (subtype != -1) {
                uint32_t count;
                if (!itemType.isFluidContainer() && !itemType.isSplash()) {
                    count = player->getItemTypeCount(shopInfo.itemId, subtype); // This shop item requires extra checks
                } else {
                    count = subtype;
                }

                if (count > 0) {
                    saleMap[shopInfo.itemId] = count;
                }
            } else {
                std::map<uint32_t, uint32_t>::const_iterator findIt = tempSaleMap.find(shopInfo.itemId);
                if (findIt != tempSaleMap.end() && findIt->second > 0) {
                    saleMap[shopInfo.itemId] = findIt->second;
                }
            }
        }
    }

    uint8_t itemsToSend = std::min<size_t>(saleMap.size(), std::numeric_limits<uint8_t>::max());
    msg.addByte(itemsToSend);

    uint8_t i = 0;
    for (std::map<uint16_t, uint32_t>::const_iterator it = saleMap.begin(); i < itemsToSend; ++it, ++i) {
        msg.addItemId(it->first);
        msg.addByte(std::min<uint32_t>(it->second, std::numeric_limits<uint8_t>::max()));
    }

    writeToOutputBuffer(msg);
}

 

protocolgame.h

Find this

void sendSaleItemList(const std::list<ShopInfo>& shop);

Place this right underneath

void sendSaleItemList(Npc* npc, const std::list<ShopInfo>& shop);

 

luascript.cpp

Find

int LuaScriptInterface::luaPlayerAddMoney(lua_State* L)

Replace that whole function with this

int LuaScriptInterface::luaPlayerAddMoney(lua_State* L)
{
    // player:addMoney(money[, currencyId])
    uint64_t money = getNumber<uint64_t>(L, 2);
    uint32_t currencyId = getNumber<uint32_t>(L, 3);
    Player* player = getUserdata<Player>(L, 1);
    if (player) {
        if (currencyId) {
            int32_t value;
            player->getStorageValue(currencyId, value);
            player->addStorageValue(currencyId, value + money);
        }
        else {
            g_game.addMoney(player, money);
        }
        pushBoolean(L, true);
    } else {
        lua_pushnil(L);
    }
    return 1;
}

Next find this function which should be right below it.

int LuaScriptInterface::luaPlayerRemoveMoney(lua_State* L)

Replace that whole function with this

int LuaScriptInterface::luaPlayerRemoveMoney(lua_State* L)
{
    // player:removeMoney(money[, currencyId])
    Player* player = getUserdata<Player>(L, 1);
    if (player) {
        uint64_t money = getNumber<uint64_t>(L, 2);
        uint32_t currencyId = getNumber<uint32_t>(L, 3);
        if (currencyId) {
            int32_t value;
            player->getStorageValue(currencyId, value);
            if (value < money) {
                pushBoolean(L, false);
                return 1;
            }
            player->addStorageValue(currencyId, value - money);
            pushBoolean(L, true);
        }
        else {
            pushBoolean(L, g_game.removeMoney(player, money));
        }

    } else {
        lua_pushnil(L);
    }
    return 1;
}

 

Editado por Codex NG (veja o histórico de edições)

  • 2 weeks later...
  • Respostas 5
  • Visualizações 2.8k
  • Created
  • Última resposta

Top Posters In This Topic

Postado

Parabéns, seu tópico de conteúdo foi aprovado!
Muito obrigado pela sua contribuição, nós do Tibia King agradecemos.
Seu conteúdo com certeza ajudará à muitos outros, você recebeu +1 REP.

Spoiler

Congratulations, your content has been approved!
Thank you for your contribution, we of Tibia King we are grateful.
Your content will help many other users, you received +1 REP.

 

  • 2 weeks later...
  • 4 weeks later...
Postado
  • Autor
On 10/18/2016 at 1:43 AM, Pastabase said:

  @Codex NG  Hiho men if im using otx 2.x and i edit these sources files i will upgrade my otx 2 to 3 npc system?wait for u reply nice tuto

 

If it works on otx2 then yes it will work on otx3, it also works on TFS 1.2/1.3

 

Some aspects of the sources haven't changed since 0.3, so you might even be able to use this code as far back as then of course you will need to make code adjustments. But i don't see why it can't be done.

Editado por Codex NG (veja o histórico de edições)

Participe da conversa

Você pode postar agora e se cadastrar mais tarde. Se você tem uma conta, faça o login para postar com sua conta.

Visitante
Responder

Quem Está Navegando 0

  • Nenhum usuário registrado visualizando esta página.

Estatísticas dos Fóruns

  • Tópicos 96.9k
  • Posts 519.7k

Informação Importante

Confirmação de Termo