Metin2 [C++] Auto Pot Filling System [Auto Potion Refill]

Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!

Blaze

Elite
Elite
VIP
Joined
Jan 28, 2019
Messages
507
Credits
635
If the system is actually used correctly it contains a perfect idea.
The logic is very simple, if you approach the NPC you have set, your auto pots will automatically start to fill up.
As in the video below. When the character approaches the gun dealer, look at the fullness of the auto pot. This happens in the arena areas, where gasoline can be marketed very nicely to your players by providing the possibility of automatic filling of pots with an NPC such as NPC.

READ CAREFULLY!
If the GET NPC LOCATION BY VNUM system is not attached to you, you need to add it first!!!

Service.h / CommonDefines.h:
Expand Collapse Copy
//add:
#define ENABLE_POTION_REFILL
game/sectree_manager.h:
Expand Collapse Copy
//find:
        bool        SaveAttributeToImage(int lMapIndex, const char * c_pszFileName, LPSECTREE_MAP pMapSrc = NULL);
//Add:
#ifdef ENABLE_POTION_REFILL
        bool        GetNpcLocationByVnum(long lMapIndex, DWORD npcVnum, std::vector<std::pair<long, long>>& positions);
#endif
game/sectree_manager.cpp:
Expand Collapse Copy
//Finding the function:
bool SECTREE_MANAGER::SaveAttributeToImage(int lMapIndex, const char * c_pszFileName, LPSECTREE_MAP pMapSrc)
//Follow the end of the function and append:
#ifdef ENABLE_POTION_REFILL
bool SECTREE_MANAGER::GetNpcLocationByVnum(long lMapIndex, DWORD npcVnum, std::vector<std::pair<long, long>>& positions)
{
    LPSECTREE_MAP sectree = SECTREE_MANAGER::instance().GetMap(lMapIndex);
    if (sectree != nullptr)
    {
        struct FFindNPCByVnum
        {
            DWORD specifiedVnum;
            std::vector<std::pair<long, long>>& npcPositions;
            FFindNPCByVnum(DWORD vnum, std::vector<std::pair<long, long>>& positions)
                : specifiedVnum(vnum), npcPositions(positions) {}
            void operator()(LPENTITY ent)
            {
                if (ent->IsType(ENTITY_CHARACTER))
                {
                    LPCHARACTER pChar = static_cast<LPCHARACTER>(ent);
                    if (pChar->IsNPC() && pChar->GetMobTable().dwVnum == specifiedVnum)
                    {
                        npcPositions.emplace_back(pChar->GetX(), pChar->GetY());
                    }
                }
            }
        };
        std::vector<std::pair<long, long>> npcPositions;
        FFindNPCByVnum finder(npcVnum, npcPositions);
        sectree->for_each(finder);
        positions.insert(positions.end(), npcPositions.begin(), npcPositions.end());
        return !positions.empty();
    }
    return false;
}
#endif

game/unique_item.h:
Expand Collapse Copy
/!!!IMPORTANT!!!
//Adjust the enum to your needs, e.g. if you are using different auto potion vnums.
//AUTO_POTION_MAX_NUM should be equal to the number of auto potions you are using.
//!!!IMPORTANT!!!
//Add at the end of the file:
#ifdef ENABLE_POTION_REFILL
enum EAutoPotion
{
    ITEM_AUTO_HP_POTION_1 = ITEM_AUTO_HP_RECOVERY_S,
    ITEM_AUTO_HP_POTION_2 = ITEM_AUTO_HP_RECOVERY_M,
    ITEM_AUTO_HP_POTION_3 = ITEM_AUTO_HP_RECOVERY_L,
    ITEM_AUTO_HP_POTION_4 = ITEM_AUTO_HP_RECOVERY_X,
    ITEM_AUTO_SP_POTION_1 = ITEM_AUTO_SP_RECOVERY_S,
    ITEM_AUTO_SP_POTION_2 = ITEM_AUTO_SP_RECOVERY_M,
    ITEM_AUTO_SP_POTION_3 = ITEM_AUTO_SP_RECOVERY_L,
    ITEM_AUTO_SP_POTION_4 = ITEM_AUTO_SP_RECOVERY_X,
    AUTO_POTION_MAX_NUM = 8,
};
#endif

game/char.h:
Expand Collapse Copy
//find:
        BYTE            m_bChatCounter;
//add:
#ifdef ENABLE_POTION_REFILL
        bool            m_bIsNearRefillNPC;
        LPEVENT            m_refillPotionEvent;
        void            PotionRefillMgt();
#endif
//find:
        DWORD            GetLastAttackTime() const    { return m_dwLastAttackTime; }
//add:
#ifdef ENABLE_POTION_REFILL
        void            PotionRefillMgt();
        void            SetNearRefillNPC(bool bIsNear);
        bool            GetNearRefillNPC() const        { return m_bIsNearRefillNPC; }
#endif

game/char.cpp:
Expand Collapse Copy
/bool CHARACTER::Move(long x, long y): find function in
    OnMove();
    return Sync(x, y);
    
//Add to the function you found:
#ifdef ENABLE_POTION_REFILL
    PotionRefillMgt();
#endif


//Inside the function void CHARACTER::Disconnect(const char * c_pszReason): find the following code

marriage::CManager::instance().Logout(this);

//add on

#ifdef ENABLE_POTION_REFILL
    if (GetNearRefillNPC())
    {
        SetNearRefillNPC(false);
    }
#endif

//find function:
void CHARACTER::SendMovePacket(BYTE bFunc, BYTE bArg, DWORD x, DWORD y, DWORD dwDuration, DWORD dwTime, int iRot)

//After the function is finished, add it as a new function below:
#ifdef ENABLE_POTION_REFILL


void CHARACTER::PotionRefillMgt()
{
    if (IsPC())
    {
        DWORD dwVnum = 9001; //Change NPC vnum here
        float fArea = 1500.0f; //Change area around NPC here
        std::vector<std::pair<long, long>> positions;
        if (SECTREE_MANAGER::Instance().GetNpcLocationByVnum(GetMapIndex(), dwVnum, positions))
        {
            if (!positions.empty())
            {
                if (!GetNearRefillNPC())
                {
                    for (const auto& position : positions)
                    {
                        if (DISTANCE_APPROX(position.first - GetX(), position.second - GetY()) <= fArea)
                        {
                            ChatPacket(CHAT_TYPE_INFO, "Potions are refilling...");
                            SetNearRefillNPC(true);
                            break;
                        }
                    }
                }
                else
                {
                    for (const auto& position : positions)
                    {
                        if (DISTANCE_APPROX(position.first - GetX(), position.second - GetY()) > fArea)
                        {
                            ChatPacket(CHAT_TYPE_INFO, "Refilling of potions stopped");
                            SetNearRefillNPC(false);
                            break;
                        }
                    }
                }
            }
        }
    }
}



void CHARACTER::PotionRefillMgt()
{
    if (IsPC())
    {
        DWORD dwVnum = 9001; //Change NPC vnum here
        float fArea = 1500.0f; //Change area around NPC here
        std::vector<std::pair<long, long>> positions;
        if (SECTREE_MANAGER::Instance().GetNpcLocationByVnum(GetMapIndex(), dwVnum, positions))
        {
            if (!positions.empty())
            {
                if (!GetNearRefillNPC())
                {
                    for (const auto& position : positions)
                    {
                        if (DISTANCE_APPROX(position.first - GetX(), position.second - GetY()) <= fArea)
                        {
                            ChatPacket(CHAT_TYPE_INFO, "Potions are refilling...");
                            SetNearRefillNPC(true);
                            break;
                        }
                    }
                }
                else
                {
                    for (const auto& position : positions)
                    {
                        if (DISTANCE_APPROX(position.first - GetX(), position.second - GetY()) > fArea)
                        {
                            ChatPacket(CHAT_TYPE_INFO, "Refilling of potions stopped");
                            SetNearRefillNPC(false);
                            break;
                        }
                    }
                }
            }
        }
    }
}
void CHARACTER::SetNearRefillNPC(bool bIsNear)
{
    m_bIsNearRefillNPC = bIsNear;
    std::vector<LPITEM> ownedPotions;
    LPITEM item = nullptr;
    for (int i = 0; i < AUTO_POTION_MAX_NUM; i++)
    {
        switch (i)
        {
            case 0:
                item = FindSpecifyItem(ITEM_AUTO_HP_POTION_1);
                break;
            case 1:
                item = FindSpecifyItem(ITEM_AUTO_HP_POTION_2);
                break;
            case 2:
                item = FindSpecifyItem(ITEM_AUTO_HP_POTION_3);
                break;
            case 3:
                item = FindSpecifyItem(ITEM_AUTO_HP_POTION_4);
                break;
            case 4:
                item = FindSpecifyItem(ITEM_AUTO_SP_POTION_1);
                break;
            case 5:
                item = FindSpecifyItem(ITEM_AUTO_SP_POTION_2);
                break;
            case 6:
                item = FindSpecifyItem(ITEM_AUTO_SP_POTION_3);
                break;
            case 7:
                item = FindSpecifyItem(ITEM_AUTO_SP_POTION_4);
                break;
            default:
                break;
        }
        if (item)
            ownedPotions.push_back(item);
    }
    for (auto &potion : ownedPotions)
    {
        if (bIsNear)
            potion->StartPotionRecoveryEvent();
        else
            potion->StopPotionRecoveryEvent();
    }
}
#endif

// void CHARACTER::Disconnect(const char * c_pszReason): find function in
    marriage::CManager::instance().Logout(this);
    
//Add on top:

#ifdef ENABLE_POTION_REFILL
    if (GetNearRefillNPC())
    {
        SetNearRefillNPC(false);
    }
#endif

game/item.h:
Expand Collapse Copy
//find:

        void        StartDestroyEvent(int iSec=300);
//add:

#ifdef ENABLE_POTION_REFILL
        void        SetPotionRecoveryEvent(LPEVENT pkEvent);
        void        StartPotionRecoveryEvent();
        void        StopPotionRecoveryEvent();
#endif

//find:

        LPEVENT        m_pkOwnershipEvent;
//add:

#ifdef ENABLE_POTION_REFILL
        LPEVENT        m_pkPotionRecoveryEvent;
#endif

game/item.cpp:
Expand Collapse Copy
//find:

    m_pkRealTimeExpireEvent = NULL;
//add:

#ifdef ENABLE_POTION_REFILL
    m_pkPotionRecoveryEvent = NULL;
#endif

// void CItem::Destroy()): find function in
CEntity::Destroy();

//add below:

#ifdef ENABLE_POTION_REFILL
    event_cancel(&m_pkPotionRecoveryEvent);
#endif

//find the following function:
void CItem::StartDestroyEvent(int iSec)

//When the function is finished add it below

#ifdef ENABLE_POTION_REFILL
EVENTFUNC(potion_recovery_event)
{
    item_event_info* info = dynamic_cast<item_event_info*>(event->info);
    if (info == NULL)
    {
        sys_err("potion_recovery_event> <Factor> Null pointer");
        return 0;
    }
    LPITEM pkItem = info->item;
    //Check if potion is fully restored
    //return and cancel event if so
    const TItemTable* itemTable = pkItem->GetProto();
    if (pkItem->GetSocket(1) == 0)
    {
        pkItem->SetPotionRecoveryEvent(NULL);
        return 0;
    }
    else
    {
        int potionValueToRestore = itemTable->alValues[0] / 100 * 10;
        int missingPotionValue = pkItem->GetSocket(1);
        int newPotionValue = 0;
        if (missingPotionValue < potionValueToRestore)
        {
            pkItem->SetSocket(1, 0, false);
            pkItem->SetPotionRecoveryEvent(NULL);
            return 0;
        }
        else
        {
            newPotionValue = pkItem->GetSocket(1) - potionValueToRestore;
            pkItem->SetSocket(1, newPotionValue, false);
            return PASSES_PER_SEC(3);
        }
    }
}
void CItem::SetPotionRecoveryEvent(LPEVENT pkEvent)
{
    m_pkPotionRecoveryEvent = pkEvent;
}
void CItem::StartPotionRecoveryEvent()
{
    if (m_pkPotionRecoveryEvent)
        return;
    item_event_info* info = AllocEventInfo<item_event_info>();
    info->item = this;
    SetPotionRecoveryEvent(event_create(potion_recovery_event, info, PASSES_PER_SEC(3)));
}
void CItem::StopPotionRecoveryEvent()
{
    if (m_pkPotionRecoveryEvent)
    {
        event_cancel(&m_pkPotionRecoveryEvent);
    }
}
#endif


 
Back
Top