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!!!
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:
//add:
#define ENABLE_POTION_REFILL
game/sectree_manager.h:
//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:
//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:
/!!!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:
//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:
/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:
//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:
//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


