diff --git a/BH/BH.cpp b/BH/BH.cpp index 0f137718..9392f75b 100644 --- a/BH/BH.cpp +++ b/BH/BH.cpp @@ -108,7 +108,7 @@ void BH::Initialize() SetWindowLong(D2GFX_GetHwnd(), GWL_WNDPROC, (LONG)GameWindowEvent); }); - settingsUI = new Drawing::UI(BH_VERSION, 400, 247); + settingsUI = new Drawing::UI(BH_VERSION, 400, 262); Task::InitializeThreadPool(2); diff --git a/BH/Constants.h b/BH/Constants.h index d84cf7b6..372969c0 100644 --- a/BH/Constants.h +++ b/BH/Constants.h @@ -1080,6 +1080,66 @@ enum QuestFlags { #define ITEM_GROUP_GEM (ITEM_GROUP_AMETHYST|ITEM_GROUP_DIAMOND|ITEM_GROUP_EMERALD|ITEM_GROUP_RUBY|ITEM_GROUP_SAPPHIRE|ITEM_GROUP_TOPAZ|ITEM_GROUP_SKULL) +//https://d2mods.info/forum/viewtopic.php?p=487011#p487011 +//UnitAny 0xC4 +enum UNITFLAG +{ + UNITFLAG_MODE = 0x00000001, // Sends mode updates via D2GAME.6FC4C520. + UNITFLAG_SELECTABLE = 0x00000002, // Unit is selectable. + UNITFLAG_ATTACKABLE = 0x00000004, // Unit is attackable. + UNITFLAG_SEARCHABLE = 0x00000008, // Unit can be seen by target searching functions (aura, curse). + UNITFLAG_NEW = 0x00000010, // Sends entire unit to the client. + UNITFLAG_NO_SHADOW = 0x00000020, // Unit doesn't have a shadow. + UNITFLAG_FINISHED = 0x00000040, // Unit has executed the current skill's 'do' function. + UNITFLAG_PREOPERATED = 0x00000080, // Tells the game that this object has been pre-operated (that is, it should not change its mode again during initialization). + UNITFLAG_CHATMESSAGE = 0x00000100, // Sends pUnit->pChatMessage (overlay text). + UNITFLAG_HIRELING = 0x00000200, // Unit is a hireling. + UNITFLAG_SOUND = 0x00000400, // Sends pUnit->eSoundMessage. + UNITFLAG_UMOD = 0x00000800, // Sends the monster's UMods to the client if MONSTERUPDATE_UMOD is set in pMonsterData->dwUpdateMask. + UNITFLAG_FLIPPY = 0x00001000, // When adding an item to a room this tells the game to send ITEMMODE_DROP instead of ITEMMODE_ROOM. + UNITFLAG_UPDATE = 0x00002000, // Set for units that're inside the pRoom->pUnitUpdateList update queue (which is the core controller for updates). + UNITFLAG_SEQUENCE_TRANSITION = 0x00004000, // Set when a animation sequence transitions from one mode to the next. + UNITFLAG_SOFTHIT = 0x00008000, // Sends the unit's current HP percent and hitclass (pUnit->dwHitClass) to the client. + UNITFLAG_DEAD = 0x00010000, // The unit is to be considered dead even if its mode is not DD or DT. + UNITFLAG_NO_TREASURE = 0x00020000, // This unit never drops items, even if it has a treasure class. + UNITFLAG_MONSTERMODE = 0x00080000, // Set when SMONSTER_SetMode successfully changes the monster's mode. + UNITFLAG_PREDRAW = 0x00100000, // Draw this unit as if it were a ground tile. + UNITFLAG_ASYNC = 0x00200000, // This unit exists only on the client (critters). + UNITFLAG_CLIENT = 0x00400000, // This is set for all units created on the client. + UNITFLAG_DELETE = 0x00800000, // When this is set CUNIT_DoFrame will not process this unit and delete it if it is an async unit. + UNITFLAG_PRESET = 0x01000000, // Set for units that're created through GAME_SpawnPresets. Some other places also set this, usually related to quest units. + UNITFLAG_RESTORE = 0x02000000, // Unit will always be saved by SUNIT_Deactivate even if it is a corpse or opened object. + UNITFLAG_NO_EXPERIENCE = 0x04000000, // This unit doesn't give experience when slain. + UNITFLAG_SEEN = 0x10000000, // This unit was drawn. + UNITFLAG_REVEALED = 0x20000000, // This unit was added to the automap (shrines). + UNITFLAG_PETIGNORE = 0x40000000, // This unit is ignored by pets. + UNITFLAG_PET = 0x80000000 // This unit is a pet. +}; + +//UnitAny 0xC8 +enum UNITFLAGEX +{ + UNITFLAGEX_INVENTORY = 0x00000001, // Dispatches hInventory->hUpdates. Objects also check this which will lead to undefined behavior when set because they don't have an inventory. + UNITFLAGEX_2 = 0x00000002, // Set by D2SetInventoryUpdateMessage alongside UNITFLAGEX_INVENTORY if the unit is a player. + UNITFLAGEX_MERCHANT_INVENTORY_PUT = 0x00000004, // Adds the item to an NPC's trade inventory (sell to NPC). + UNITFLAGEX_DISGUISED = 0x00000008, // This unit is using another unit's graphics (shapechange). + UNITFLAGEX_MERCHANT_INVENTORY_TAKE = 0x00000010, // Removes an item from an NPC's trade inventory (buy from NPC). + UNITFLAGEX_REMOVE = 0x00000020, // If this is set alongside UNITFLAG_DELETE the client will send CMESSAGE_TELEPORT which instructs the server to set UNITFLAGEX_TELEPORTED. Must be some legacy junk. + UNITFLAGEX_CONVERTING = 0x00000040, // Set while the cursor is in conversion mode (legacy). + UNITFLAGEX_VISIBLE = 0x00000080, // Unit is within line of sight of the player. + UNITFLAGEX_DEACTIVATED = 0x00000100, // Set for units that have been deactivated by SUNIT_Deactivate. + UNITFLAGEX_REACTIVATED = 0x00000200, // Set for units that have been reactivated by SUNIT_Reactivate. + UNITFLAGEX_OWNED = 0x00000400, // This unit has an owner stored at pUnit->dwOwnerID and pUnit->eOwnerTOU (usually used by missile, but also set for pets). + UNITFLAGEX_POSITIONED = 0x00000800, // SMESSAGE_SETUNITPOS, bFadeOutScreen = FALSE. + UNITFLAGEX_MOVING = 0x00002000, // Set for critters while they're moving on the client. + UNITFLAGEX_TEMPORARY = 0x00008000, // Items with this bit set are not saved to the D2S. + UNITFLAGEX_TELEPORTED = 0x00010000, // SMESSAGE_SETUNITPOS, bFadeOutScreen = TRUE. + UNITFLAGEX_ATTACKED = 0x00020000, // The unit has been attacked and is storing details of the attacker in pUnit->dwAttackerID and pUnit->eAttackerTOU. + UNITFLAGEX_INVISIBLE = 0x00040000, // The unit is invisible and will be ignored by the drawing code completely. + UNITFLAGEX_EXPANSION = 0x02000000, // This is a unit in an expansion game. + UNITFLAGEX_SERVER = 0x04000000, // This is set for all units created on the server. +}; + #define MONSTAT_ALIGN_ENEMY 0x0 #define MONSTAT_ALIGN_ALLY 0x1 #define MONSTAT_ALIGN_NEUTRAL 0x2 diff --git a/BH/D2Helpers.cpp b/BH/D2Helpers.cpp index 0229b842..06668635 100644 --- a/BH/D2Helpers.cpp +++ b/BH/D2Helpers.cpp @@ -4,6 +4,23 @@ #include "Common.h" #include "Constants.h" +int quality_to_color[] = { + White, // none + White, // inferior + White, // normal + White, // superior + Blue, // magic + Green, // set + Yellow, // rare + Gold, // unique + Orange // craft +}; + +int ItemColorFromQuality(unsigned int quality) { + return quality_to_color[quality]; +} + + RosterUnit* FindPlayerRoster(DWORD unitId) { for (RosterUnit* roster = (*p_D2CLIENT_PlayerUnitList); roster; roster = roster->pNext) { if (roster->dwUnitId == unitId) diff --git a/BH/D2Helpers.h b/BH/D2Helpers.h index 3cdd1e76..ddbc7b3f 100644 --- a/BH/D2Helpers.h +++ b/BH/D2Helpers.h @@ -30,4 +30,5 @@ std::string GetItemName(UnitAny* item); bool IsTown(DWORD levelId); bool IsGameReady(); -DWORD GetPlayerArea(); \ No newline at end of file +DWORD GetPlayerArea(); +int ItemColorFromQuality(unsigned int quality); \ No newline at end of file diff --git a/BH/Modules/Item/Item.cpp b/BH/Modules/Item/Item.cpp index 926ac025..d67d7bda 100644 --- a/BH/Modules/Item/Item.cpp +++ b/BH/Modules/Item/Item.cpp @@ -97,6 +97,7 @@ void Item::LoadConfig() { BH::config->ReadToggle("Advanced Item Display", "None", false, Toggles["Advanced Item Display"]); BH::config->ReadToggle("Item Drop Notifications", "None", false, Toggles["Item Drop Notifications"]); BH::config->ReadToggle("Item Close Notifications", "None", false, Toggles["Item Close Notifications"]); + BH::config->ReadToggle("Item Detailed Notifications", "None", false, Toggles["Item Detailed Notifications"]); BH::config->ReadToggle("Verbose Notifications", "None", false, Toggles["Verbose Notifications"]); BH::config->ReadToggle("Allow Unknown Items", "None", false, Toggles["Allow Unknown Items"]); BH::config->ReadToggle("Suppress Invalid Stats", "None", false, Toggles["Suppress Invalid Stats"]); @@ -158,6 +159,10 @@ void Item::DrawSettings() { new Keyhook(settingsTab, keyhook_x, y+2, &Toggles["Item Close Notifications"].toggle, ""); y += 15; + new Checkhook(settingsTab, 4, y, &Toggles["Item Detailed Notifications"].state, "Item Detailed Notifications"); + new Keyhook(settingsTab, keyhook_x, y + 2, &Toggles["Item Detailed Notifications"].toggle, ""); + y += 15; + new Checkhook(settingsTab, 4, y, &Toggles["Verbose Notifications"].state, "Verbose Notifications"); new Keyhook(settingsTab, keyhook_x, y+2, &Toggles["Verbose Notifications"].toggle, ""); y += 15; diff --git a/BH/Modules/ItemMover/ItemMover.cpp b/BH/Modules/ItemMover/ItemMover.cpp index 4103ae4d..b86ad15a 100644 --- a/BH/Modules/ItemMover/ItemMover.cpp +++ b/BH/Modules/ItemMover/ItemMover.cpp @@ -2,6 +2,7 @@ #include "../../BH.h" #include "../../D2Ptrs.h" #include "../../D2Stubs.h" +#include "../../D2Helpers.h" // This module was inspired by the RedVex plugin "Item Mover", written by kaiks. // Thanks to kaiks for sharing his code. @@ -24,18 +25,6 @@ int CUBE_LEFT = 197; int CUBE_TOP = 199; int CELL_SIZE = 29; -int quality_to_color[] = { - White, // none - White, // inferior - White, // normal - White, // superior - Blue, // magic - Green, // set - Yellow, // rare - Gold, // unique - Orange // craft -}; - DWORD idBookId; DWORD unidItemId; @@ -604,11 +593,10 @@ void ItemMover::OnGamePacketRecv(BYTE* packet, bool* block) { /* break; */ } } - //PrintText(1, "Item on ground: %s, %s, %s, %X", item.name.c_str(), item.code, item.attrs->category.c_str(), item.attrs->flags); - if(showOnMap) { + if(showOnMap && !(*BH::MiscToggles2)["Item Detailed Notifications"].state) { if (color == UNDEFINED_COLOR) { - color = quality_to_color[item.quality]; + color = ItemColorFromQuality(item.quality); } if ((*BH::MiscToggles2)["Item Drop Notifications"].state && item.action == ITEM_ACTION_NEW_GROUND && diff --git a/BH/Modules/Maphack/Maphack.cpp b/BH/Modules/Maphack/Maphack.cpp index 459361c5..ebc0ae84 100644 --- a/BH/Modules/Maphack/Maphack.cpp +++ b/BH/Modules/Maphack/Maphack.cpp @@ -306,6 +306,50 @@ BYTE nChestClosedColour = 0x09; BYTE nChestLockedColour = 0x09; Act* lastAct = NULL; + +void Maphack::OnDraw() { + UnitAny* player = D2CLIENT_GetPlayerUnit(); + + if (!player || !player->pAct || player->pPath->pRoom1->pRoom2->pLevel->dwLevelNo == 0) + return; + // We're looping over all items and setting 2 flags: + // UNITFLAG_NO_EXPERIENCE - Whether the item has been checked for a drop notification (to prevent checking it again) + // UNITFLAG_REVEALED - Whether the item should be notified and drawn on the automap + // To my knowledge these flags arent typically used on items. So we can abuse them for our own use. + for (Room1* room1 = player->pAct->pRoom1; room1; room1 = room1->pRoomNext) { + for (UnitAny* unit = room1->pUnitFirst; unit; unit = unit->pListNext) { + if (unit->dwType == UNIT_ITEM && (unit->dwFlags & UNITFLAG_NO_EXPERIENCE) == 0x0) { + UnitItemInfo uInfo; + uInfo.item = unit; + uInfo.itemCode[0] = D2COMMON_GetItemText(unit->dwTxtFileNo)->szCode[0]; + uInfo.itemCode[1] = D2COMMON_GetItemText(unit->dwTxtFileNo)->szCode[1]; + uInfo.itemCode[2] = D2COMMON_GetItemText(unit->dwTxtFileNo)->szCode[2]; + uInfo.itemCode[3] = 0; + if (ItemAttributeMap.find(uInfo.itemCode) != ItemAttributeMap.end()) { + uInfo.attrs = ItemAttributeMap[uInfo.itemCode]; + for (vector::iterator it = MapRuleList.begin(); it != MapRuleList.end(); it++) { + if ((*it)->Evaluate(&uInfo, NULL)) { + if ((unit->dwFlags & UNITFLAG_REVEALED) == 0x0 + && (*BH::MiscToggles2)["Item Detailed Notifications"].state) { + std::string itemName = GetItemName(unit); + size_t start_pos = 0; + while ((start_pos = itemName.find('\n', start_pos)) != std::string::npos) { + itemName.replace(start_pos, 1, " - "); + start_pos += 3; + } + PrintText(ItemColorFromQuality(unit->pItemData->dwQuality), "%s", itemName.c_str()); + } + unit->dwFlags |= UNITFLAG_REVEALED; + break; + } + } + } + } + unit->dwFlags |= UNITFLAG_NO_EXPERIENCE; + } + } +} + void Maphack::OnAutomapDraw() { UnitAny* player = D2CLIENT_GetPlayerUnit(); @@ -449,7 +493,7 @@ void Maphack::OnAutomapDraw() { Drawing::Boxhook::Draw(automapLoc.x - 1, automapLoc.y - 1, 2, 2, color, Drawing::BTHighlight); }); } - else if (unit->dwType == UNIT_ITEM) { + else if (unit->dwType == UNIT_ITEM && (unit->dwFlags & UNITFLAG_REVEALED) == UNITFLAG_REVEALED) { UnitItemInfo uInfo; uInfo.item = unit; uInfo.itemCode[0] = D2COMMON_GetItemText(unit->dwTxtFileNo)->szCode[0]; @@ -465,7 +509,6 @@ void Maphack::OnAutomapDraw() { auto dotColor = (*it)->action.dotColor; auto pxColor = (*it)->action.pxColor; auto lineColor = (*it)->action.lineColor; - xPos = unit->pItemPath->dwPosX; yPos = unit->pItemPath->dwPosY; automapBuffer.push_top_layer( diff --git a/BH/Modules/Maphack/Maphack.h b/BH/Modules/Maphack/Maphack.h index 7d1978eb..b8d19f86 100644 --- a/BH/Modules/Maphack/Maphack.h +++ b/BH/Modules/Maphack/Maphack.h @@ -53,6 +53,7 @@ class Maphack : public Module { void LoadConfig(); void OnLoop(); + void OnDraw(); void OnAutomapDraw(); void OnGameJoin(const string& name, const string& pass, int diff); void OnGamePacketRecv(BYTE* packet, bool *block);