Skip to content

Commit

Permalink
added .buy auto <itemcode> to buy and autostock items until you run…
Browse files Browse the repository at this point in the history
… out of money (wip command)

added `.buy gempacks` as a shortcut to `.buy auto 6gk` because i can never remember it
  • Loading branch information
nooperation committed Feb 9, 2024
1 parent 598293a commit 607847c
Show file tree
Hide file tree
Showing 3 changed files with 324 additions and 7 deletions.
216 changes: 212 additions & 4 deletions D2Hackit/Modules/autoBuy/AutoBuy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ BOOL CALLBACK FindStuffToRefillCallback(LPCITEM item, LPARAM lParam);

AutoBuy::AutoBuy()
{
memset(&merchantNpc, 0, sizeof(GAMEUNIT));
merchantName = "";

this->isAutomaticallyRefillTP = GetPrivateProfileInt("AutoBuy", "AutomaticallyRefillTP", 1, CONFIG_PATH);
this->refillTpAtCharge = GetPrivateProfileInt("AutoBuy", "RefillTpAtCharge", 35, CONFIG_PATH);
}
Expand All @@ -26,7 +29,7 @@ struct D2GS_NPC_BUY
};
#pragma pack(pop)

bool AutoBuy::BuyItemInQuantity(DWORD dwItemID) const
bool AutoBuy::BuyItemInQuantity(DWORD dwItemID)
{
if (!me->IsUIOpened(UI_NPCSHOP))
{
Expand Down Expand Up @@ -64,10 +67,12 @@ bool AutoBuy::BuyItemInQuantity(DWORD dwItemID) const
packet.BuyType = 0x80000000;
packet.Cost = 0;


currentState = State::PurchaseWaitforitem;
return server->GameSendPacketToServer((uint8_t*)&packet, sizeof(packet));
}

bool AutoBuy::Start(int quantity, const std::string& itemCode)
bool AutoBuy::Start(int quantity, const std::string& itemCode, bool isAutomaticMode)
{
if (currentState != State::Uninitialized)
{
Expand All @@ -80,8 +85,39 @@ bool AutoBuy::Start(int quantity, const std::string& itemCode)
this->itemQuantityToBuy = max(0, quantity);
this->itemCodeToBuy = itemCode;
this->hasAlreadyRestockedTps = false;
this->isAutomaticMode = isAutomaticMode;

if (!me->IsUIOpened(UI_NPCSHOP))
{
merchantNpc.dwUnitType = UNIT_TYPE_MONSTER;
merchantNpc.dwUnitID = FindMerchant();

if (merchantNpc.dwUnitID == 0)
{
server->GamePrintString("ÿc:AutoBuyÿc0: Failed to find merchant");
currentState = State::Uninitialized;
return false;
}

currentState = State::NpcListingitems;
if (!me->StartNpcSession(&merchantNpc, NPC_TRADE))
{
server->GamePrintString("ÿc:AutoBuyÿc0: Failed to start npc session");
currentState = State::Uninitialized;
return false;
}
}
else
{
if (isAutomaticMode)
{
currentState = State::CloseMerchantUiAndRestart;
me->CloseAllUIs();
return true;
}

BuyOurStuff();
BuyOurStuff();
}

return true;
}
Expand All @@ -104,6 +140,11 @@ void AutoBuy::Stop()

void AutoBuy::BuyOurStuff()
{
if (isAutomaticMode)
{
itemQuantityToBuy = me->GetNumberOfFreeStorageSlots(STORAGE_INVENTORY);
}

if (itemQuantityToBuy > 0)
{
auto customItemIdIter = merchantItems.find(itemCodeToBuy);
Expand All @@ -116,7 +157,10 @@ void AutoBuy::BuyOurStuff()
}
}

this->Stop();
if (!isAutomaticMode)
{
this->Stop();
}
}

void AutoBuy::RestockScrolls()
Expand All @@ -140,6 +184,11 @@ void AutoBuy::RestockScrolls()
// Called when NPC is listing items up for gamble, before NPC_SESSION message
void AutoBuy::OnNpcItemList(const ITEM& merchantItem)
{
if (!me->IsUIOpened(UI_NPCSHOP))
{
return;
}

const auto itemCode = std::string(merchantItem.szItemCode);

merchantItems[itemCode] = merchantItem.dwItemID;
Expand All @@ -154,6 +203,7 @@ void AutoBuy::OnNPCShopScreenOpened()
{
merchantItems.clear();
this->hasAlreadyRestockedTps = false;

}

void AutoBuy::ProcessInventoryItem(const ITEM* item)
Expand Down Expand Up @@ -187,3 +237,161 @@ BOOL CALLBACK FindStuffToRefillCallback(LPCITEM item, LPARAM lParam)

return TRUE;
}

void AutoBuy::OnNpcSession(int success)
{
if (currentState != State::NpcListingitems)
{
return;
}

if (!success)
{
me->RedrawClient(FALSE);
me->MoveToUnit(&merchantNpc, TRUE);
server->GamePrintInfo("ÿc:AutoBuyÿc0: NPC request failed");
currentState = State::Uninitialized; // try again

if (isAutomaticMode)
{
currentState = State::Uninitialized;
Start(1, itemCodeToBuy, isAutomaticMode);
}

return;
}

if (isAutomaticMode)
{
currentState = State::PurchaseNextitem;
BuyOurStuff();
}
}

void AutoBuy::OnItemToStorageFromStore(ITEM& item)
{
if (currentState != State::PurchaseWaitforitem)
{
return;
}

if (!WillItemFit(item.dwItemID))
{
currentState = State::CloseMerchantUiAndRunAutostock;
me->CloseAllUIs();
}
}

void AutoBuy::OnUIClosed()
{
if (currentState == State::CloseMerchantUiAndRestart)
{
currentState = State::Uninitialized;
Start(1, itemCodeToBuy, isAutomaticMode);
return;
}

if (currentState == State::CloseMerchantUiAndRunAutostock)
{
currentState = State::RunAutostocker;
server->GameCommandLine("as start chat");
return;
}
}

bool AutoBuy::OnAutostockerMessage(const std::string_view& message)
{
if (currentState != State::RunAutostocker)
{
return false;
}

if (message == "Autostocker Ended")
{
me->CloseAllUIs();

if (isAutomaticMode)
{
currentState = State::Uninitialized;
Start(1, itemCodeToBuy, true);
}
}

return true;
}

bool AutoBuy::WillItemFit(DWORD dwItemId)
{
char itemCode[4];

if (!server->GetItemCode(dwItemId, itemCode, 4))
{
server->GameErrorf("WillItemFit: Failed to get item code (ID = %04X), attempting retry", dwItemId);
Sleep(1000);

if (!server->GetItemCode(dwItemId, itemCode, 4))
{
server->GameErrorf("WillItemFit: Failed to get item code (ID = %04X)", dwItemId);
return false;
}
}

return me->FindFirstStorageSpace(STORAGE_INVENTORY, server->GetItemSize(itemCode), NULL) == true;
}

DWORD AutoBuy::FindMerchant()
{
GAMEUNIT gameUnit;

if (merchantName.length() == 0)
{
const char* merchantNames[] =
{
"charsi",
"gheed",
"akara",
"elzix",
"lysander",
"fara",
"drognan",
"alkor",
"asheara",
"ormus",
"hratli",
"jamella",
"halbu",
"larzuk",
"malah",
"anya",
};

DWORD closestNpcID = 0;
DWORD closestDistance = 0;

for (int i = 0; i < sizeof(merchantNames) / sizeof(merchantNames[0]); i++)
{
if (server->FindUnitByName(merchantNames[i], UNIT_TYPE_MONSTER, &gameUnit))
{
MAPPOS npcPos = server->GetUnitPosition(&gameUnit);
DWORD distance = me->GetDistanceFrom(npcPos.x, npcPos.y);

if (closestNpcID == 0 || closestDistance > distance)
{
closestNpcID = gameUnit.dwUnitID;
closestDistance = distance;
}
}
}

return closestNpcID;
}
else
{
if (server->FindUnitByName(merchantName.c_str(), UNIT_TYPE_MONSTER, &gameUnit))
{
return gameUnit.dwUnitID;
}
}

return 0;
}
26 changes: 24 additions & 2 deletions D2Hackit/Modules/autoBuy/AutoBuy.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,17 @@
enum class State
{
Uninitialized = 0,
CloseMerchantUiAndRestart,
WaitForNPC,

OpenMerchantUi,

NpcListingitems,
NpcDonelistingitems,
PurchaseNextitem,
PurchaseWaitforitem,
CloseMerchantUiAndRunAutostock,
RunAutostocker,
};


Expand All @@ -15,26 +25,38 @@ class AutoBuy
public:
AutoBuy();

bool Start(int quantity, const std::string& itemCode);
bool Start(int quantity, const std::string& itemCode, bool isAutomaticMode);
void Stop();
void OnNPCShopScreenOpened();
void ProcessInventoryItem(const ITEM *item);
void OnNpcItemList(const ITEM &merchantItem);
void OnUIClosed();
void OnNpcSession(int success);
void OnItemToStorageFromStore(ITEM& item);

bool OnAutostockerMessage(const std::string_view& message);

private:
void BuyOurStuff();
void RestockScrolls();
bool BuyItemInQuantity(DWORD dwItemID) const;
bool BuyItemInQuantity(DWORD dwItemID);

DWORD FindMerchant();
bool WillItemFit(DWORD dwItemId);

bool isAutomaticallyRefillTP = 0;
int refillTpAtCharge = 0;

int numTPTomesToRefill = 0;
bool hasAlreadyRestockedTps = false;
bool isAutomaticMode = false;

int itemQuantityToBuy = 0;
std::string itemCodeToBuy = "";

State currentState = State::Uninitialized;
std::unordered_map<std::string, DWORD> merchantItems;

std::string merchantName;
GAMEUNIT merchantNpc;
};
Loading

0 comments on commit 607847c

Please sign in to comment.