diff --git a/Action.cpp b/Action.cpp index c13e1ae..6f6e798 100644 --- a/Action.cpp +++ b/Action.cpp @@ -1,14 +1,58 @@ #include #include -#include #include "Action.h" #include "D2Structs.h" #include "D2Ptrs.h" #include "ItemFilter.h" -typedef std::wstring(*TokenReplaceFunction)(ItemActionResult* action, Unit* pItem); - -static std::wstring GetItemName(ItemActionResult* action, Unit* pItem) { +typedef std::wstring(*TokenReplaceFunction)(ActionResult* action, Unit* pItem); + +#define COLOR(STR, IDX) { L"{"#STR"}", ##IDX## } +static std::unordered_map COLOR_TO_STRING = { + COLOR(White, TEXT_WHITE), + COLOR(Red, TEXT_RED), + COLOR(Green, TEXT_GREEN), + COLOR(Blue, TEXT_BLUE), + COLOR(Gold, TEXT_GOLD), + COLOR(Gray, TEXT_GRAY), + COLOR(Black, TEXT_BLACK), + COLOR(Tan, TEXT_TAN), + COLOR(Orange, TEXT_ORANGE), + COLOR(Yellow, TEXT_YELLOW), + COLOR(Purple, TEXT_PURPLE), + COLOR(Dark Green, TEXT_DARK_GREEN), + //Glide Only + COLOR(Coral, TEXT_CORAL), + COLOR(Sage, TEXT_SAGE), + COLOR(Teal, TEXT_TEAL), + COLOR(Light Gray, TEXT_LIGHT_GRAY) +}; +#undef COLOR + +#define COLOR(STR, IDX) { L#STR, ##IDX## }, { L"\""#STR"\"", ##IDX## } +static std::unordered_map COLOR_TO_PALETTE_IDX = { + COLOR(White, 0x20), + COLOR(White, 0x20), + COLOR(Red, 0x0A), + COLOR(Green, 0x84), + COLOR(Blue, 0x97), + COLOR(Gold, 0x0D), + COLOR(Gray, 0xD0), + COLOR(Black, 0x00), + COLOR(Tan, 0x5A), + COLOR(Orange, 0x60), + COLOR(Yellow, 0x0C), + COLOR(Purple, 0x9B), + COLOR(Dark Green, 0x76), + COLOR(Coral, 0x66), + COLOR(Sage, 0x82), + COLOR(Teal, 0xCB), + COLOR(Light Gray, 0xD6) +}; +#undef COLOR + + +static std::wstring GetItemName(ActionResult* action, Unit* pItem) { if (action->bItemNameSet) { return action->wsItemName; } else { @@ -16,7 +60,7 @@ static std::wstring GetItemName(ItemActionResult* action, Unit* pItem) { } } -static std::wstring GetItemDesc(ItemActionResult* action, Unit* pItem) { +static std::wstring GetItemDesc(ActionResult* action, Unit* pItem) { if (action->bItemDescSet) { return action->wsItemDesc; } else { @@ -24,19 +68,19 @@ static std::wstring GetItemDesc(ItemActionResult* action, Unit* pItem) { } } -static std::wstring GetRuneNumber(ItemActionResult* action, Unit* pItem) { +static std::wstring GetRuneNumber(ActionResult* action, Unit* pItem) { if (!D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::RUNE)) { return L""; } ItemsTxt pItemTxt = D2COMMON_ItemDataTbl->pItemsTxt[pItem->dwLineId]; - return std::to_wstring(std::stoi(std::string(&pItemTxt.szCode[1], 3))); + return std::to_wstring(std::stoi(std::string(&GetItemsTxt(pItem).szCode[1], 3))); } -static std::wstring Newline(ItemActionResult* action, Unit* pItem) { +static std::wstring Newline(ActionResult* action, Unit* pItem) { return L"\n"; } -static std::wstring GetItemPrice(ItemActionResult* action, Unit* pItem) { +static std::wstring GetItemPrice(ActionResult* action, Unit* pItem) { int nPrice = 0; Unit* pPlayer = D2CLIENT_GetPlayerUnit(); if (pItem != NULL && pPlayer != NULL) { @@ -45,27 +89,48 @@ static std::wstring GetItemPrice(ItemActionResult* action, Unit* pItem) { return std::to_wstring(nPrice); } -static std::wstring GetItemSockets(ItemActionResult* action, Unit* pItem) { +static std::wstring GetItemSockets(ActionResult* action, Unit* pItem) { return std::to_wstring(D2COMMON_STATLIST_GetUnitStatUnsigned(pItem, Stat::ITEM_NUMSOCKETS, 0)); } -void HideAction::SetResult(ItemActionResult* action, Unit* pItem) { + +ColorTextAction::ColorTextAction(std::wstring value, ActionType type) : Action(value, type) { + for (auto const& color : COLOR_TO_STRING) { + replace(m_Value, color.first, color.second); + } +} + +PaletteIndexAction::PaletteIndexAction(std::wstring value, ActionType type) : Action(value, type) { + if (COLOR_TO_PALETTE_IDX.contains(value)) { + m_PaletteIndex = COLOR_TO_PALETTE_IDX[value]; + } + else { + m_PaletteIndex = static_cast(std::stoi(value, nullptr, 16)); + } +} + + +void HideAction::SetResult(ActionResult* action, Unit* pItem) { action->bHide = true; } -void ShowAction::SetResult(ItemActionResult* action, Unit* pItem) { +void ShowAction::SetResult(ActionResult* action, Unit* pItem) { action->bHide = false; } -void SetStyleAction::SetResult(ItemActionResult* action, Unit* pItem) { - if (STYLES.contains(m_Value)) { - for (auto act : STYLES[m_Value]) { +void ContinueAction::SetResult(ActionResult* action, Unit* pItem) { + action->bContinue = true; +} + +void SetStyleAction::SetResult(ActionResult* action, Unit* pItem) { + if (GlobalStyles.contains(m_Value)) { + for (auto act : GlobalStyles[m_Value]) { act->SetResult(action, pItem); } } } -void SetNameAction::SetResult(ItemActionResult* action, Unit* pItem) { +void SetNameAction::SetResult(ActionResult* action, Unit* pItem) { std::map TOKEN_REPLACEMENT_FUNCTIONS = { { L"{Name}", &GetItemName }, { L"{Sockets}", &GetItemSockets }, @@ -82,7 +147,7 @@ void SetNameAction::SetResult(ItemActionResult* action, Unit* pItem) { action->bItemNameSet = true; } -void SetDescriptionAction::SetResult(ItemActionResult* action, Unit* pItem) { +void SetDescriptionAction::SetResult(ActionResult* action, Unit* pItem) { std::map TOKEN_REPLACEMENT_FUNCTIONS = { { L"{Description}", &GetItemDesc }, { L"{Sockets}", &GetItemSockets }, @@ -99,29 +164,29 @@ void SetDescriptionAction::SetResult(ItemActionResult* action, Unit* pItem) { action->bItemDescSet = true; } -void SetBackgroundColorAction::SetResult(ItemActionResult* action, Unit* pItem) { +void SetBackgroundColorAction::SetResult(ActionResult* action, Unit* pItem) { action->bBackgroundPaletteIndexSet = true; action->nBackgroundPaletteIndex = m_PaletteIndex; } -void SetInventoryColorAction::SetResult(ItemActionResult* action, Unit* pItem) { +void SetInventoryColorAction::SetResult(ActionResult* action, Unit* pItem) { action->bInvBackgroundPaletteIndexSet = true; action->nInvBackgroundPaletteIndex = m_PaletteIndex; } -void SetBorderColorAction::SetResult(ItemActionResult* action, Unit* pItem) { +void SetBorderColorAction::SetResult(ActionResult* action, Unit* pItem) { action->bBorderPaletteIndexSet = true; action->nBorderPaletteIndex = m_PaletteIndex; } -void ChatNotifyAction::SetResult(ItemActionResult* action, Unit* pItem) { +void ChatNotifyAction::SetResult(ActionResult* action, Unit* pItem) { action->bChatAlert = true; } -void PlayAlertAction::SetResult(ItemActionResult* action, Unit* pItem) { +void PlayAlertAction::SetResult(ActionResult* action, Unit* pItem) { } -void MinimapIconAction::SetResult(ItemActionResult* action, Unit* pItem) { +void MinimapIconAction::SetResult(ActionResult* action, Unit* pItem) { action->bMinimapIcon = true; action->nMinimapIconPaletteIndex = m_PaletteIndex; -} +} \ No newline at end of file diff --git a/Action.h b/Action.h index b3f6584..79814f0 100644 --- a/Action.h +++ b/Action.h @@ -1,16 +1,16 @@ #pragma once #include -#include #include #include "Utils.h" #include "D2Structs.h" -struct ItemActionResult { +struct ActionResult { std::vector vMatchedRules; bool bHide = false; + bool bContinue = false; bool bBackgroundPaletteIndexSet = false; uint8_t nBackgroundPaletteIndex = 0; @@ -36,140 +36,104 @@ struct ItemActionResult { }; +enum class ActionType : uint8_t { + NONE, SHOW, HIDE, CONTINUE, SET_STYLE, SET_NAME, + SET_DESCRIPTION, SET_BG_COLOR, SET_INVENTORY_COLOR, + SET_BORDER_COLOR, CHAT_NOTIFY, PLAY_ALERT, MINIMAP_ICON +}; class Action { protected: + ActionType m_Type; std::wstring m_Value; public: - Action(std::wstring value) : m_Value(value) {}; - ~Action() {}; - virtual void SetResult(ItemActionResult* action, Unit* pItem) = 0; + Action(std::wstring value = L"", ActionType type = ActionType::NONE) : m_Value(value), m_Type(type) {}; + ActionType GetType() { return m_Type; } + virtual void SetResult(ActionResult* pResult, Unit* pItem) = 0; +}; + +class ShowAction : public Action { +public: + ShowAction(std::wstring value = L"") : Action(value, ActionType::SHOW) {}; + void SetResult(ActionResult* pResult, Unit* pItem) override; }; class HideAction : public Action { public: - HideAction() : Action(L"") {} - void SetResult(ItemActionResult* action, Unit* pItem); + HideAction(std::wstring value = L"") : Action(value, ActionType::HIDE) {}; + void SetResult(ActionResult* pResult, Unit* pItem) override; }; -class ShowAction : public Action { +class ContinueAction : public Action { public: - ShowAction() : Action(L"") {} - void SetResult(ItemActionResult* action, Unit* pItem); + ContinueAction(std::wstring value = L"") : Action(value, ActionType::CONTINUE) { }; + void SetResult(ActionResult* pResult, Unit* pItem) override; }; class ColorTextAction : public Action { public: - ColorTextAction(std::wstring value) : Action(value) { - std::map COLOR_TO_PALETTE_IDX = { - { L"{White}", TEXT_WHITE }, - { L"{Red}", TEXT_RED }, - { L"{Green}", TEXT_GREEN }, - { L"{Blue}", TEXT_BLUE }, - { L"{Gold}", TEXT_GOLD }, - { L"{Gray}", TEXT_GRAY }, - { L"{Black}", TEXT_BLACK }, - { L"{Tan}", TEXT_TAN }, - { L"{Orange}", TEXT_ORANGE }, - { L"{Yellow}", TEXT_YELLOW }, - { L"{Purple}", TEXT_PURPLE }, - { L"{Dark Green}", TEXT_DARK_GREEN }, - //Glide Only - { L"{Coral}", TEXT_CORAL }, - { L"{Sage}", TEXT_SAGE }, - { L"{Teal}", TEXT_TEAL }, - { L"{Light Gray}", TEXT_LIGHT_GRAY } - }; - for (auto const& color : COLOR_TO_PALETTE_IDX) { - replace(m_Value, color.first, color.second); - } - } - virtual void SetResult(ItemActionResult* action, Unit* pItem) = 0; + ColorTextAction(std::wstring value = L"", ActionType type = ActionType::NONE); + virtual void SetResult(ActionResult* pResult, Unit* pItem) = 0; }; class PaletteIndexAction : public Action { protected: uint8_t m_PaletteIndex = 0; public: - PaletteIndexAction(std::wstring value) : Action(value) { - std::map COLOR_TO_PALETTE_IDX = { - { L"White", 0x20 }, - { L"Red", 0x0A }, - { L"Green", 0x84 }, - { L"Blue", 0x97 }, - { L"Gold", 0x0D }, - { L"Gray", 0xD0 }, - { L"Black", 0x00 }, - { L"Tan", 0x5A }, - { L"Orange", 0x60 }, - { L"Yellow", 0x0C }, - { L"Purple", 0x9B }, - { L"Dark Green", 0x76 }, - { L"Coral", 0x66 }, - { L"Sage", 0x82 }, - { L"Teal", 0xCB }, - { L"Light Gray", 0xD6 } - }; - if (COLOR_TO_PALETTE_IDX.contains(value)) { - m_PaletteIndex = COLOR_TO_PALETTE_IDX[value]; - } - else { - m_PaletteIndex = static_cast(std::stoi(value, nullptr, 16)); - } - } - virtual void SetResult(ItemActionResult* action, Unit* pItem) = 0; + PaletteIndexAction(std::wstring value = L"", ActionType type = ActionType::NONE); + virtual void SetResult(ActionResult* pResult, Unit* pItem) = 0; }; class SetStyleAction : public Action { public: - SetStyleAction(std::wstring value) : Action(value) { } - void SetResult(ItemActionResult* action, Unit* pItem); + SetStyleAction(std::wstring value = L"") : Action(value, ActionType::SET_STYLE) {}; + void SetResult(ActionResult* pResult, Unit* pItem) override; }; class SetNameAction : public ColorTextAction { public: - SetNameAction(std::wstring value) : ColorTextAction(value) { } - void SetResult(ItemActionResult* action, Unit* pItem); + SetNameAction(std::wstring value = L"") : ColorTextAction(value, ActionType::SET_NAME) {}; + void SetResult(ActionResult* pResult, Unit* pItem) override; }; class SetDescriptionAction : public ColorTextAction { public: - SetDescriptionAction(std::wstring value) : ColorTextAction(value) {} - void SetResult(ItemActionResult* action, Unit* pItem); + SetDescriptionAction(std::wstring value = L"") : ColorTextAction(value, ActionType::SET_DESCRIPTION) {}; + void SetResult(ActionResult* pResult, Unit* pItem) override; }; class SetBackgroundColorAction : public PaletteIndexAction { public: - SetBackgroundColorAction(std::wstring value) : PaletteIndexAction(value) {} - void SetResult(ItemActionResult* action, Unit* pItem); + SetBackgroundColorAction(std::wstring value = L"") : PaletteIndexAction(value, ActionType::SET_BORDER_COLOR) {}; + void SetResult(ActionResult* pResult, Unit* pItem) override; }; class SetInventoryColorAction : public PaletteIndexAction { public: - SetInventoryColorAction(std::wstring value) : PaletteIndexAction(value) {} - void SetResult(ItemActionResult* action, Unit* pItem); + SetInventoryColorAction(std::wstring value = L"") : PaletteIndexAction(value, ActionType::SET_INVENTORY_COLOR) {}; + void SetResult(ActionResult* pResult, Unit* pItem) override; }; class SetBorderColorAction : public PaletteIndexAction { public: - SetBorderColorAction(std::wstring value) : PaletteIndexAction(value) {} - void SetResult(ItemActionResult* action, Unit* pItem); + SetBorderColorAction(std::wstring value = L"") : PaletteIndexAction(value, ActionType::SET_BORDER_COLOR) {}; + void SetResult(ActionResult* pResult, Unit* pItem) override; }; class ChatNotifyAction : public Action { public: - ChatNotifyAction(std::wstring value) : Action(value) {} - void SetResult(ItemActionResult* action, Unit* pItem); + ChatNotifyAction(std::wstring value = L"") : Action(value, ActionType::CHAT_NOTIFY) {}; + void SetResult(ActionResult* pResult, Unit* pItem) override; }; class PlayAlertAction : public Action { public: - PlayAlertAction(std::wstring value) : Action(value) {} - void SetResult(ItemActionResult* action, Unit* pItem); + PlayAlertAction(std::wstring value = L"") : Action(value, ActionType::PLAY_ALERT) {}; + void SetResult(ActionResult* pResult, Unit* pItem) override; }; class MinimapIconAction : public PaletteIndexAction { public: - MinimapIconAction(std::wstring value) : PaletteIndexAction(value) {} - void SetResult(ItemActionResult* action, Unit* pItem); + MinimapIconAction(std::wstring value = L"") : PaletteIndexAction(value, ActionType::MINIMAP_ICON) {}; + void SetResult(ActionResult* pResult, Unit* pItem) override; }; \ No newline at end of file diff --git a/Condition.cpp b/Condition.cpp index f0e61ec..1c2dcb4 100644 --- a/Condition.cpp +++ b/Condition.cpp @@ -1,64 +1,79 @@ -#include -#include #include "Condition.h" -#include "D2Structs.h" -#include "D2Ptrs.h" - -std::map STRICTNESS_LEVEL_TO_ENUM = { - { L"None", 0 }, - { L"Soft", 1 }, - { L"Regular", 2 }, - { L"Semi-Strict", 3 }, - { L"Strict", 4 }, - { L"Very Strict", 5 }, - { L"Uber Strict", 6 }, - { L"Uber+ Strict", 7 } -}; - -bool AndCondition::Evaluate(Unit* pItem) { - return std::all_of(m_Conditions.begin(), m_Conditions.end(), [pItem](Condition* c) { return c->Evaluate(pItem); }); -} - -bool RarityCondition::Evaluate(Unit* pItem) { - m_Lhs->SetValue(static_cast>(pItem->pItemData->dwRarity)); - return m_Expression->Evaluate(pItem); -} +#include "Utils.h" +#include bool CodeCondition::Evaluate(Unit* pItem) { - ItemsTxt pItemTxt = D2COMMON_ItemDataTbl->pItemsTxt[pItem->dwLineId]; - m_Lhs->SetValue(pItemTxt.dwCode); + m_Left->SetValue(GetItemsTxt(pItem).dwCode); return m_Expression->Evaluate(pItem); } +void CodeCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool TypeCondition::Evaluate(Unit* pItem) { - ItemsTxt pItemTxt = D2COMMON_ItemDataTbl->pItemsTxt[pItem->dwLineId]; - m_Lhs->SetValue(pItemTxt.dwCode); + m_Left->SetValue(GetItemsTxt(pItem).dwCode); return m_Expression->Evaluate(pItem); } +void TypeCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool ClassCondition::Evaluate(Unit* pItem) { return m_Expression->Evaluate(pItem); } +void ClassCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::ParseCall(Token::CLASS, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + +bool RarityCondition::Evaluate(Unit* pItem) { + m_Left->SetValue(static_cast(pItem->pItemData->dwRarity)); + return m_Expression->Evaluate(pItem); +} + +void RarityCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool EtherealCondition::Evaluate(Unit* pItem) { - uint8_t isEthereal = (pItem->pItemData->dwItemFlags & ItemFlags::ETHEREAL) == ItemFlags::ETHEREAL; - m_Lhs->SetValue(isEthereal); + m_Left->SetValue((pItem->pItemData->dwItemFlags & ItemFlags::ETHEREAL) == ItemFlags::ETHEREAL); return m_Expression->Evaluate(pItem); } +void EtherealCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool RunewordCondition::Evaluate(Unit* pItem) { - uint8_t isRuneword = (pItem->pItemData->dwItemFlags & ItemFlags::RUNEWORD) == ItemFlags::RUNEWORD; - m_Lhs->SetValue(isRuneword); + m_Left->SetValue((pItem->pItemData->dwItemFlags & ItemFlags::RUNEWORD) == ItemFlags::RUNEWORD); return m_Expression->Evaluate(pItem); } +void RunewordCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool PrefixCondition::Evaluate(Unit* pItem) { uint8_t isIdentified = (pItem->pItemData->dwItemFlags & ItemFlags::IDENTIFIED) == ItemFlags::IDENTIFIED; if (!isIdentified) { return false; } for (auto& prefix : pItem->pItemData->wMagicPrefix) { - m_Lhs->SetValue(prefix); + m_Left->SetValue(prefix); if (m_Expression->Evaluate(pItem)) { return true; } @@ -66,13 +81,19 @@ bool PrefixCondition::Evaluate(Unit* pItem) { return false; } +void PrefixCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool SuffixCondition::Evaluate(Unit* pItem) { uint8_t isIdentified = (pItem->pItemData->dwItemFlags & ItemFlags::IDENTIFIED) == ItemFlags::IDENTIFIED; if (!isIdentified) { return false; } for (auto& prefix : pItem->pItemData->wMagicSuffix) { - m_Lhs->SetValue(prefix); + m_Left->SetValue(prefix); if (m_Expression->Evaluate(pItem)) { return true; } @@ -80,102 +101,240 @@ bool SuffixCondition::Evaluate(Unit* pItem) { return false; } +void SuffixCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool ItemLevelCondition::Evaluate(Unit* pItem) { - m_Lhs->SetValue(pItem->pItemData->dwItemLevel); + m_Left->SetValue(pItem->pItemData->dwItemLevel); return m_Expression->Evaluate(pItem); } +void ItemLevelCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool QualityCondition::Evaluate(Unit* pItem) { - ItemsTxt pItemTxt = D2COMMON_ItemDataTbl->pItemsTxt[pItem->dwLineId]; - if (pItemTxt.dwCode == pItemTxt.dwUltraCode) { - m_Lhs->SetValue(static_cast>(ItemQualityLevel::ELITE)); - } else if (pItemTxt.dwCode == pItemTxt.dwUberCode) { - m_Lhs->SetValue(static_cast>(ItemQualityLevel::EXCEPTIONAL)); - } else if (pItemTxt.dwCode == pItemTxt.dwNormCode) { - m_Lhs->SetValue(static_cast>(ItemQualityLevel::NORMAL)); - } else { - m_Lhs->SetValue(static_cast>(ItemQualityLevel::UNKNOWN)); + ItemsTxt txt = GetItemsTxt(pItem); + if (txt.dwCode == txt.dwUltraCode) { + m_Left->SetValue(2); + } + else if (txt.dwCode == txt.dwUberCode) { + m_Left->SetValue(1); + } + else if (txt.dwCode == txt.dwNormCode) { + m_Left->SetValue(0); + } + else { + m_Left->SetValue(-1); } return m_Expression->Evaluate(pItem); } +void QualityCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + +bool AreaLevelCondition::Evaluate(Unit* pItem) { + return false; +} + +void AreaLevelCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool CharacterLevelCondition::Evaluate(Unit* pItem) { - Unit* pPlayer = D2CLIENT_GetPlayerUnit(); - if (!pPlayer) { - return false; - } - m_Lhs->SetValue(D2COMMON_STATLIST_GetUnitStatUnsigned(pPlayer, Stat::LEVEL, 0)); + m_Left->SetValue(D2COMMON_STATLIST_GetUnitStatUnsigned(D2CLIENT_GetPlayerUnit(), Stat::LEVEL, 0)); return m_Expression->Evaluate(pItem); } +void CharacterLevelCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool DifficultyCondition::Evaluate(Unit* pItem) { - m_Lhs->SetValue(D2CLIENT_GetDifficulty()); + m_Left->SetValue(D2CLIENT_GetDifficulty()); return m_Expression->Evaluate(pItem); } +void DifficultyCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + +bool RuneCondition::Evaluate(Unit* pItem) { + if (!D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::RUNE)) { + return false; + } + int nRuneNumber = std::stoi(std::string(&GetItemsTxt(pItem).szCode[1], 3)); + m_Left->SetValue(nRuneNumber); + return m_Expression->Evaluate(pItem); +} + +void RuneCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool IdCondition::Evaluate(Unit* pItem) { - m_Lhs->SetValue(pItem->pItemData->dwFileIndex); + m_Left->SetValue(pItem->dwLineId); return m_Expression->Evaluate(pItem); } +void IdCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool GoldCondition::Evaluate(Unit* pItem) { - return D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::GOLD) - && UnsignedStatCondition::Evaluate(pItem); + if (!D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::GOLD)) { + return false; + } + m_Left->SetValue(D2COMMON_STATLIST_GetUnitStatUnsigned(pItem, Stat::GOLD, 0)); + return m_Expression->Evaluate(pItem); } +void GoldCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool StatsCondition::Evaluate(Unit* pItem) { return m_Expression->Evaluate(pItem); } - -bool RuneCondition::Evaluate(Unit* pItem) { - if (!D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::RUNE)) { - return false; +void StatsCondition::Initialize(std::unordered_map variables) { + for (auto stat : CustomStats) { + replace(m_Value, stat.first, stat.second); } - ItemsTxt pItemTxt = D2COMMON_ItemDataTbl->pItemsTxt[pItem->dwLineId]; - int nRuneNumber = std::stoi(std::string(&pItemTxt.szCode[1], 3)); - m_Lhs->SetValue(nRuneNumber); + m_Expression = Parser::Parse(m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + +bool DefenseCondition::Evaluate(Unit* pItem) { + m_Left->SetValue(D2COMMON_STATLIST_GetUnitStatUnsigned(pItem, Stat::ARMORCLASS, 0)); return m_Expression->Evaluate(pItem); } +void DefenseCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool ArmorCondition::Evaluate(Unit* pItem) { - m_Lhs->SetValue(D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::ANY_ARMOR)); + m_Left->SetValue(D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::ANY_ARMOR)); return m_Expression->Evaluate(pItem); } +void ArmorCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool WeaponCondition::Evaluate(Unit* pItem) { - m_Lhs->SetValue(D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::WEAPON)); + m_Left->SetValue(D2COMMON_ITEMS_CheckItemTypeId(pItem, ItemType::WEAPON)); return m_Expression->Evaluate(pItem); } -bool ModeCondition::Evaluate(Unit* pItem) { - return false; -} +void WeaponCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; bool PriceCondition::Evaluate(Unit* pItem) { Unit* pPlayer = D2CLIENT_GetPlayerUnit(); if (pItem != NULL && pPlayer != NULL) { int nPrice = D2COMMON_ITEMS_GetTransactionCost(pPlayer, pItem, D2CLIENT_GetDifficulty(), D2CLIENT_GetQuestFlags(), 0x201, 1); - m_Lhs->SetValue(nPrice); + m_Left->SetValue(nPrice); return m_Expression->Evaluate(pItem); } return false; } +void PriceCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + +bool ModeCondition::Evaluate(Unit* pItem) { + return false; +} + +void ModeCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool IdentifiedCondition::Evaluate(Unit* pItem) { - uint8_t isIdentified = (pItem->pItemData->dwItemFlags & ItemFlags::IDENTIFIED) == ItemFlags::IDENTIFIED; - m_Lhs->SetValue(isIdentified); + m_Left->SetValue((pItem->pItemData->dwItemFlags & ItemFlags::IDENTIFIED) == ItemFlags::IDENTIFIED); + return m_Expression->Evaluate(pItem); +} + +void IdentifiedCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + +bool SocketsCondition::Evaluate(Unit* pItem) { + m_Left->SetValue(D2COMMON_STATLIST_GetUnitStatUnsigned(pItem, Stat::ITEM_NUMSOCKETS, 0)); return m_Expression->Evaluate(pItem); } +void SocketsCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool WidthCondition::Evaluate(Unit* pItem) { - ItemsTxt pItemTxt = D2COMMON_ItemDataTbl->pItemsTxt[pItem->dwLineId]; - m_Lhs->SetValue(pItemTxt.nInvWidth); + m_Left->SetValue(GetItemsTxt(pItem).nInvWidth); return m_Expression->Evaluate(pItem); } +void WidthCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + bool HeightCondition::Evaluate(Unit* pItem) { - ItemsTxt pItemTxt = D2COMMON_ItemDataTbl->pItemsTxt[pItem->dwLineId]; - m_Lhs->SetValue(pItemTxt.nInvHeight); + m_Left->SetValue(GetItemsTxt(pItem).nInvHeight); + return m_Expression->Evaluate(pItem); +} + +void HeightCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; + +bool RandomCondition::Evaluate(Unit* pItem) { + m_Left->SetValue(rand() % 100); //random between 0-99 return m_Expression->Evaluate(pItem); -} \ No newline at end of file +} + +void RandomCondition::Initialize(std::unordered_map variables) { + m_Left = new Variable(); + m_Expression = Parser::Parse(m_Left, m_Value.c_str()); + m_Expression->SetVariables(variables); +}; diff --git a/Condition.h b/Condition.h index a6aef39..742d6ab 100644 --- a/Condition.h +++ b/Condition.h @@ -1,310 +1,359 @@ #pragma once - -#include -#include -#include -#include -#include -#include "Utils.h" -#include "D2Ptrs.h" +#include +#include #include "D2Structs.h" -#include "Globals.h" +#include "D2Ptrs.h" #include "Expression.h" +#include + +#pragma warning( push ) +#pragma warning( disable : 26495 ) + +//to be able to access conditions by type to set variables and such +enum class ConditionType : uint8_t { + NONE, CODE, TYPE, CLASS, RARITY, ETHEREAL, RUNEWORD, PREFIX, SUFFIX, + ITEM_LEVEL, QUALITY, AREA_LEVEL, CHARACTER_LEVEL, DIFFICULTY, + RUNE, ID, GOLD, STATS, DEFENSE, ARMOR, WEAPON, PRICE, MODE, + IDENTIFIED, SOCKETS, WIDTH, HEIGHT, RANDOM +}; +static const wchar_t* CONDITIONS[] = { L"", L"Code", L"Type", L"Class", L"Rarity", L"Ethereal", L"Runeword", L"Prefix", L"Suffix", L"ItemLevel", L"Quality", L"AreaLevel", L"CharacterLevel", + L"Difficulty", L"Rune", L"Id", L"Gold", L"Stats", L"Defense", L"Armor", L"Weapon", L"Price", L"Mode", L"Identified", L"Sockets", L"Width", L"Height" }; class Condition { protected: + ConditionType m_Type; std::wstring m_Value; public: - Condition() {}; - Condition(std::wstring value) : m_Value(value) {}; - ~Condition() {}; - virtual void Initialize() {}; + Condition(std::wstring value = L"", ConditionType type = ConditionType::NONE) : m_Value(value), m_Type(type) {}; + ConditionType GetType() { return m_Type; } virtual bool Evaluate(Unit* pItem) = 0; + virtual void Initialize(std::unordered_map variables) {}; + virtual std::wstring ToString(Unit* pItem) = 0; }; -class AndCondition : public Condition { +class CodeCondition : public Condition { protected: - std::vector m_Conditions; + Variable* m_Left; + ListExpression* m_Expression; public: - AndCondition() {}; - AndCondition(std::vector condition) : m_Conditions(condition) {}; - void AddCondition(Condition* condition) { m_Conditions.push_back(condition); }; - void Initialize() { - for (auto& condition : m_Conditions) { - condition->Initialize(); - } - }; - bool Evaluate(Unit* pItem); + CodeCondition(std::wstring value = L"") : Condition(value, ConditionType::CODE) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -/* -Handles scenarios where the LHS of a condition is dynamic. i.e. -FireResist > 10 -*/ -template -class LHSCondition : public Condition { +class TypeCondition : public Condition { protected: - Var* m_Lhs; - ListExpr* m_Expression; + Variable* m_Left; + ListExpression* m_Expression; public: - LHSCondition(std::wstring variable, std::wstring value) : Condition(value) { - m_Lhs = new Var(variable); - m_Expression = Parser::Parse(m_Lhs, m_Value.c_str()); - }; - virtual bool Evaluate(Unit* pItem) = 0; + TypeCondition(std::wstring value = L"") : Condition(value, ConditionType::TYPE) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -template -class CallCondition : public Condition { +class ClassCondition : public Condition { protected: - ListExpr* m_Expression; + Variable* m_Left; + Expression* m_Expression; public: - CallCondition(std::wstring func, std::wstring value) : Condition(value) { - m_Expression = Parser::ParseCall(func.c_str(), m_Value.c_str()); - }; - virtual bool Evaluate(Unit* pItem) = 0; + ClassCondition(std::wstring value = L"") : Condition(value, ConditionType::CLASS) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class UnsignedStatCondition : public LHSCondition { +class RarityCondition : public Condition { protected: - Stat m_Stat; + Variable* m_Left; + ListExpression* m_Expression; public: - UnsignedStatCondition(std::wstring variable, Stat stat, std::wstring value) : LHSCondition(variable, value) { - m_Stat = stat; - }; - bool Evaluate(Unit* pItem) { - m_Lhs->SetValue((D2COMMON_STATLIST_GetUnitStatUnsigned(pItem, m_Stat, 0))); - return m_Expression->Evaluate(pItem); - } + RarityCondition(std::wstring value = L"") : Condition(value, ConditionType::RARITY) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class CodeCondition : public LHSCondition { +class EtherealCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - //todo.. clean this up - CodeCondition(std::wstring value) : LHSCondition(L"Code", value) {}; - void Initialize() override { - m_Expression->SetVariables(ITEM_CODE_LOOKUP_TABLE); - }; - bool Evaluate(Unit* pItem); + EtherealCondition(std::wstring value = L"") : Condition(value, ConditionType::ETHEREAL) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -//Simple name from Weapons.txt, Armor.txt, Misc.txt -class TypeCondition : public LHSCondition { +class RunewordCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - //todo.. clean this up - TypeCondition(std::wstring value) : LHSCondition(L"Type", value) {}; - void Initialize() override { - m_Expression->SetVariables(ITEM_NAME_LOOKUP_TABLE); - }; - bool Evaluate(Unit* pItem); + RunewordCondition(std::wstring value = L"") : Condition(value, ConditionType::RUNEWORD) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -//Simple name from ItemTypes.txt -class ClassCondition : public CallCondition { +class PrefixCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - ClassCondition(std::wstring value) : CallCondition(L"Class", value) {}; - void Initialize() override { - m_Expression->SetVariables(ITEM_TYPE_LOOKUP_TABLE); - }; - bool Evaluate(Unit* pItem); + PrefixCondition(std::wstring value = L"") : Condition(value, ConditionType::PREFIX) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class RarityCondition : public LHSCondition { +class SuffixCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - RarityCondition(std::wstring value) : LHSCondition(L"Rarity", value) { - std::map VARIABLES = { - { L"Inferior", 0x1 }, - { L"Normal", 0x2 }, - { L"Superior", 0x3 }, - { L"Magic", 0x4 }, - { L"Set", 0x5 }, - { L"Rare", 0x6 }, - { L"Unique", 0x7 }, - { L"Crafted", 0x8 }, - { L"Tempered", 0x9 } - }; - m_Expression->SetVariables(VARIABLES); - }; - bool Evaluate(Unit* pItem); + SuffixCondition(std::wstring value = L"") : Condition(value, ConditionType::SUFFIX) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class EtherealCondition : public LHSCondition { +class ItemLevelCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - EtherealCondition(std::wstring value) : LHSCondition(L"Ethereal", value) {}; - bool Evaluate(Unit* pItem); + ItemLevelCondition(std::wstring value = L"") : Condition(value, ConditionType::ITEM_LEVEL) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class RunewordCondition : public LHSCondition { +class QualityCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - RunewordCondition(std::wstring value) : LHSCondition(L"Runeword", value) {}; - bool Evaluate(Unit* pItem); + QualityCondition(std::wstring value = L"") : Condition(value, ConditionType::QUALITY) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class PrefixCondition : public LHSCondition { +class AreaLevelCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - PrefixCondition(std::wstring value) : LHSCondition(L"Prefix", value) {}; - bool Evaluate(Unit* pItem); + AreaLevelCondition(std::wstring value = L"") : Condition(value, ConditionType::AREA_LEVEL) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -//TODO -class SuffixCondition : public LHSCondition { +class CharacterLevelCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - SuffixCondition(std::wstring value) : LHSCondition(L"Suffix", value) {}; - bool Evaluate(Unit* pItem); + CharacterLevelCondition(std::wstring value = L"") : Condition(value, ConditionType::CHARACTER_LEVEL) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -//A, I, C Level -class ItemLevelCondition : public LHSCondition { +class DifficultyCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - ItemLevelCondition(std::wstring value) : LHSCondition(L"ItemLevel", value) {}; - bool Evaluate(Unit* pItem); + DifficultyCondition(std::wstring value = L"") : Condition(value, ConditionType::DIFFICULTY) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class QualityCondition : public LHSCondition { +class RuneCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - QualityCondition(std::wstring value) : LHSCondition(L"Quality", value) { - std::map VARIABLES = { - { L"Unknown", static_cast>(ItemQualityLevel::UNKNOWN) }, - { L"Normal", static_cast>(ItemQualityLevel::NORMAL) }, - { L"Exceptional", static_cast>(ItemQualityLevel::EXCEPTIONAL) }, - { L"Elite", static_cast>(ItemQualityLevel::ELITE) } - }; - m_Expression->SetVariables(VARIABLES); - }; - bool Evaluate(Unit* pItem); + RuneCondition(std::wstring value = L"") : Condition(value, ConditionType::RUNE) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -//todo...? kinda useless -class AreaLevelCondition : public Condition { +class IdCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - AreaLevelCondition(std::wstring value) : Condition(value) {}; - bool Evaluate(Unit* pItem) { return false; }; + IdCondition(std::wstring value = L"") : Condition(value, ConditionType::ID) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class CharacterLevelCondition : public LHSCondition { +class GoldCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - CharacterLevelCondition(std::wstring value) : LHSCondition(L"CharacterLevel", value) {}; - bool Evaluate(Unit* pItem); + GoldCondition(std::wstring value = L"") : Condition(value, ConditionType::GOLD) {}; + bool Evaluate(Unit* pItem) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + void Initialize(std::unordered_map variables) override; + }; -class DifficultyCondition : public LHSCondition { +class StatsCondition : public Condition { +protected: + Expression* m_Expression; public: - DifficultyCondition(std::wstring value) : LHSCondition(L"Difficulty", value) { - std::map VARIABLES = { - { L"Normal", 0 }, - { L"Nightmare", 1 }, - { L"Hell", 2 } - }; - m_Expression->SetVariables(VARIABLES); - }; - bool Evaluate(Unit* pItem); + StatsCondition(std::wstring value = L"") : Condition(value, ConditionType::STATS) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class RuneCondition : public LHSCondition { +class DefenseCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - RuneCondition(std::wstring value) : LHSCondition(L"Rune", value) {}; - void Initialize() override { - m_Expression->SetVariables(RUNE_NAME_LOOKUP_TABLE); - }; - bool Evaluate(Unit* pItem); + DefenseCondition(std::wstring value = L"") : Condition(value, ConditionType::DEFENSE) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class IdCondition : public LHSCondition { +class ArmorCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - IdCondition(std::wstring value) : LHSCondition(L"Id", value) {}; - bool Evaluate(Unit* pItem); + ArmorCondition(std::wstring value = L"") : Condition(value, ConditionType::ARMOR) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class GoldCondition : public UnsignedStatCondition { +class WeaponCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - GoldCondition(std::wstring value) : UnsignedStatCondition(L"Gold", Stat::GOLD, value) {}; - bool Evaluate(Unit* pItem); -}; - -//Stats - -//unsigned stat -#define USTAT(x, y)\ - class x##Condition : public UnsignedStatCondition { \ - public: \ - x##Condition(std::wstring value): UnsignedStatCondition(L#x, y, value) {}; \ + WeaponCondition(std::wstring value = L"") : Condition(value, ConditionType::WEAPON) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -//todo -class AllResistCondition : public Condition { +class PriceCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - AllResistCondition(std::wstring value) : Condition(value) {}; - bool Evaluate(Unit* pItem) { return true; }; + PriceCondition(std::wstring value = L"") : Condition(value, ConditionType::PRICE) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class StatsCondition : public Condition { +class ModeCondition : public Condition { protected: - Expr* m_Expression; + Variable* m_Left; + ListExpression* m_Expression; public: - StatsCondition(std::wstring value) : Condition(value) { - for (auto& stat : STAT_LOOKUP_TABLE) { - replace(m_Value, stat.first, stat.second); - } - m_Expression = nullptr; - }; - void Initialize() { - for (auto& stat : SKILL_LOOKUP_TABLE) { - replace(m_Value, stat.first, stat.second); - } - m_Expression = Parser::Parse(m_Value.c_str()); - }; - bool Evaluate(Unit* pItem); + ModeCondition(std::wstring value = L"") : Condition(value, ConditionType::MODE) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -USTAT(Defense, Stat::ARMORCLASS); - -//Armor - -class ArmorCondition : public LHSCondition { +class IdentifiedCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - ArmorCondition(std::wstring value) : LHSCondition(L"Armor", value) {}; - bool Evaluate(Unit* pItem); + IdentifiedCondition(std::wstring value = L"") : Condition(value, ConditionType::IDENTIFIED) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -//Weapons -class WeaponCondition : public LHSCondition { +class SocketsCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - WeaponCondition(std::wstring value) : LHSCondition(L"Weapon", value) {}; - bool Evaluate(Unit* pItem); + SocketsCondition(std::wstring value = L"") : Condition(value, ConditionType::SOCKETS) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -//Misc -class PriceCondition : public LHSCondition { +class WidthCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - PriceCondition(std::wstring value) : LHSCondition(L"Price", value) {}; - bool Evaluate(Unit* pItem); + WidthCondition(std::wstring value = L"") : Condition(value, ConditionType::WIDTH) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -//todo -class ModeCondition : public Condition { +class HeightCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - ModeCondition(std::wstring value) : Condition(value) {}; - bool Evaluate(Unit* pItem); + HeightCondition(std::wstring value = L"") : Condition(value, ConditionType::HEIGHT) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; + }; -class IdentifiedCondition : public LHSCondition { +class RandomCondition : public Condition { +protected: + Variable* m_Left; + ListExpression* m_Expression; public: - IdentifiedCondition(std::wstring value) : LHSCondition(L"Identified", value) {}; - bool Evaluate(Unit* pItem); -}; - -USTAT(Sockets, Stat::ITEM_NUMSOCKETS); + RandomCondition(std::wstring value = L"") : Condition(value, ConditionType::RANDOM) {}; + bool Evaluate(Unit* pItem) override; + void Initialize(std::unordered_map variables) override; + std::wstring ToString(Unit* pItem) override { return std::format(L"{} {}", CONDITIONS[static_cast(m_Type)], m_Expression->ToString(pItem)); }; -class WidthCondition : public LHSCondition { -public: - WidthCondition(std::wstring value) : LHSCondition(L"Width", value) {}; - bool Evaluate(Unit* pItem); }; -class HeightCondition : public LHSCondition { -public: - HeightCondition(std::wstring value) : LHSCondition(L"Height", value) {}; - bool Evaluate(Unit* pItem); -}; +#pragma warning( pop ) \ No newline at end of file diff --git a/Config.cpp b/Config.cpp deleted file mode 100644 index 252c2e7..0000000 --- a/Config.cpp +++ /dev/null @@ -1,215 +0,0 @@ -#include "Config.h" -#include -#include -#include -#include -#include -#include "Condition.h" -#include "Utils.h" - - -namespace fs = std::filesystem; - -#define CONDITION(NAME) m_ValidConditions[L#NAME " "] = &newInstance -#define ACTION(NAME) m_ValidActions[L#NAME " "] = &newInstance - -Config::Config(std::wstring path) : m_Path(path) { - SetupValidConditions(); - SetupValidActions(); -} - -void Config::SetupValidConditions() { - CONDITION(Type); - CONDITION(Code); - CONDITION(Class); //Helm, Sword, etc... (ItemTypes.txt) - //Quality - CONDITION(Ethereal); - CONDITION(Rarity); //Rare, Magic, Set, Unique - CONDITION(Runeword); - CONDITION(Prefix); - CONDITION(Suffix); - //A, I, C Level - CONDITION(ItemLevel); - CONDITION(Quality); - CONDITION(AreaLevel); - CONDITION(CharacterLevel); - CONDITION(Difficulty); - CONDITION(Rune); - CONDITION(Id); //Id from Sets.txt or Uniques.txt - CONDITION(Gold); - //CONDITION(Quantity); - //Stats - CONDITION(Stats); - CONDITION(Defense); - //Armor - CONDITION(Armor); - //Weapons - CONDITION(Weapon); - //Misc - CONDITION(Price); - CONDITION(Mode); //Expansion / Classic - CONDITION(Identified); - CONDITION(Sockets); - CONDITION(Width); - CONDITION(Height); -} - -void Config::SetupValidActions() { - ACTION(SetStyle); - ACTION(SetName); - ACTION(SetDescription); - ACTION(SetBackgroundColor); - ACTION(SetInventoryColor); - ACTION(SetBorderColor); - ACTION(ChatNotify); - ACTION(PlayAlert); //todo. - ACTION(MinimapIcon); //todo. -} - -void Config::HandleToken(ConfigToken token, std::wstring* line, uint32_t lineNum, std::vector& rules, std::map>& styles, std::vector& lines) { - switch (token) { - case ConfigToken::STYLE: { - ParseStyle(line, styles, lines); - break; - } - case ConfigToken::SHOW: { - Rule* rule = new Rule(lineNum); - rule->AddAction(new ShowAction()); - ParseRule(rule, rules, lines); - break; - } - case ConfigToken::HIDE: { - Rule* rule = new Rule(lineNum); - rule->AddAction(new HideAction()); - ParseRule(rule, rules, lines); - break; - } - default: - break; - } - lines.clear(); -} - -void Config::ParseStyle(std::wstring* line, std::map>& styles, std::vector& lines) { - std::wstring style = std::wstring(trim(line->substr(6))); - for (auto line : lines) { - std::wstring s = line.sText; - //ignore comments or blank lines - if (s.find(COMMENT) != std::string::npos) { - s = s.erase(s.find(COMMENT)); - } - s = trim(s); - if (s.length() == 0) { - continue; - } - std::wstring conditionName = L""; - std::wstring value = L""; - for (auto validAction : m_ValidActions) { - int l = validAction.first.length(); - if (s.compare(0, l, validAction.first) == 0) { - conditionName = s.substr(0, l); - value = s.substr(l); - break; - } - } - if (!conditionName.empty() && !value.empty()) { - styles[style].push_back(m_ValidActions[conditionName](value)); - } - } -} - -void Config::ParseRule(Rule* rule, std::vector &rules, std::vector& lines) { - AndCondition* andCondition = new AndCondition(); - for (auto line : lines) { - std::wstring s = line.sText; - //ignore comments or blank lines - if (s.find(COMMENT) != std::string::npos) { - s = s.erase(s.find(COMMENT)); - } - s = trim(s); - if (s.length() == 0) { - continue; - } - if (s.compare(0, 8, L"Continue") == 0) { - rule->SetIsContinue(true); - continue; - } - std::wstring conditionName = L""; - std::wstring value = L""; - for (auto validCondition : m_ValidConditions) { - int l = validCondition.first.length(); - if (s.compare(0, l, validCondition.first) == 0) { - conditionName = s.substr(0, l); - value = s.substr(l); - break; - } - } - if (!conditionName.empty() && !value.empty()) { - andCondition->AddCondition(m_ValidConditions[conditionName](value)); - } - else { - for (auto validAction : m_ValidActions) { - int l = validAction.first.length(); - if (s.compare(0, l, validAction.first) == 0) { - conditionName = s.substr(0, l); - value = s.substr(l); - break; - } - } - if (!conditionName.empty() && !value.empty()) { - rule->AddAction(m_ValidActions[conditionName](value)); - } - } - } - rule->AddCondition(andCondition); - rules.push_back(rule); - lines.clear(); -} - -void Config::ParseFilter(std::vector& rules, std::map>& styles) { - if (!fs::exists(m_Path)) { - MessageBoxA(nullptr, "No item.filter found.", "Error", MB_OK | MB_ICONSTOP); - exit(0); - } - - std::wifstream f(m_Path); - std::wstring s; - uint32_t tokenLineNum = 0; - uint32_t lineNum = 0; - ConfigToken token = ConfigToken::NONE; - Rule* r = NULL; - std::wstring* line = NULL; - std::vector lines; - - while (std::getline(f, s)) { - lineNum++; - //ignore comments or blank lines - if (s.find(COMMENT) != std::string::npos) { - s = s.erase(s.find(COMMENT)); - } - s = trim(s); - if (s.length() == 0) { - continue; - } - if (s.compare(0, 5, L"Style") == 0) { - tokenLineNum = lineNum; - HandleToken(token, line, tokenLineNum, rules, styles, lines); - token = ConfigToken::STYLE; - line = new std::wstring(s); - } - else if (s.compare(0, 4, L"Show") == 0) { - tokenLineNum = lineNum; - HandleToken(token, line, tokenLineNum, rules, styles, lines); - token = ConfigToken::SHOW; - } - else if (s.compare(0, 4, L"Hide") == 0) { - tokenLineNum = lineNum; - HandleToken(token, line, tokenLineNum, rules, styles, lines); - token = ConfigToken::HIDE; - } - else { - lines.push_back({ lineNum, s }); - } - } - HandleToken(token, line, tokenLineNum, rules, styles, lines); -} diff --git a/Config.h b/Config.h deleted file mode 100644 index 6004e90..0000000 --- a/Config.h +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once -#include -#include -#include "Rule.h" -#include "Condition.h" -#include "Action.h" - -#define COMMENT L"#" - -template Condition* newInstance(std::wstring value) { return new T(value); } -typedef std::map ConditionMap; - -template Action* newInstance(std::wstring value) { return new T(value); } -typedef std::map ActionMap; - -struct Line { - uint32_t nLineNum; - std::wstring sText; -}; - -enum class ConfigToken : uint8_t { - NONE, - STYLE, - SHOW, - HIDE -}; - -class Config { -private: - std::wstring m_Path; - ConditionMap m_ValidConditions; - ActionMap m_ValidActions; - - void SetupValidConditions(); - void SetupValidActions(); - void HandleToken(ConfigToken token, std::wstring* line, uint32_t lineNum, std::vector& rules, std::map>& styles, std::vector& lines); - void ParseStyle(std::wstring* line, std::map>& styles, std::vector& lines); - void ParseRule(Rule* rule, std::vector& rules, std::vector& lines); -public: - Config(std::wstring path); - void ParseFilter(std::vector& rules, std::map>& styles); -}; - diff --git a/Configuration.cpp b/Configuration.cpp new file mode 100644 index 0000000..67f93a5 --- /dev/null +++ b/Configuration.cpp @@ -0,0 +1,305 @@ +#include "Configuration.h" +#include +#include +#include +#include +#include +#include +#include +#include "Utils.h" + +namespace fs = std::filesystem; + +std::unordered_map> GlobalStyles; +std::map GlobalRules; + +#define COMMENT_STR L"#" +#define STYLE_STR L"Style" +#define SHOW_STR L"Show" +#define HIDE_STR L"Hide" + +Configuration::Configuration(std::wstring sPath) : m_Path(sPath) { +} + +void Configuration::Load() { + if (!IsTxtDataLoaded) { + return; + } + auto t1 = std::chrono::high_resolution_clock::now(); + + GlobalRules.clear(); + GlobalStyles.clear(); + + if (!fs::exists(m_Path)) { + std::ofstream out(m_Path); + out.close(); + } + + uint32_t tokenLineNumber = 0; + uint32_t currentLineNumber = 0; + + std::wifstream in(m_Path); + std::wstring line; + std::vector lines; + + while (std::getline(in, line)) { + currentLineNumber++; + if (line.find(COMMENT_STR) != std::wstring::npos) { + line = line.erase(line.find(COMMENT_STR)); + } + line = trim(line); + if (line.length() == 0) { + continue; + } + if (line.compare(0, 5, STYLE_STR) == 0 + || line.compare(0, 4, SHOW_STR) == 0 + || line.compare(0, 4, HIDE_STR) == 0) { + HandleToken(tokenLineNumber, lines); + tokenLineNumber = currentLineNumber; + } + lines.push_back(line); + } + HandleToken(tokenLineNumber, lines); + + InitalizeConditionVariables(); + m_Loaded = true; + + auto t2 = std::chrono::high_resolution_clock::now(); + auto ms_int = std::chrono::duration_cast(t2 - t1); + DEBUG_LOG(L"Rules parsed in %dms", ms_int.count()); +} + +void Configuration::HandleToken(uint32_t lineNumber, std::vector& lines) { + if (lines.empty()) + return; + std::wstring line = lines[0]; lines.erase(lines.begin()); + if (line.compare(0, 5, STYLE_STR) == 0) { + std::wstring style = line.substr(5); + style = trim(style); + GlobalStyles[style] = ParseActions(lines); + } + else { + Rule* rule = ParseRule(lines); + if (line.compare(0, 4, SHOW_STR) == 0) rule->AddAction(new ShowAction(), 0); + else rule->AddAction(new HideAction(), 0); + rule->SetLineNumber(lineNumber); + GlobalRules[lineNumber] = rule; + } + lines.clear(); +} + +std::vector Configuration::ParseStyle(std::vector& lines) { + return ParseActions(lines); +} + +Rule* Configuration::ParseRule(std::vector& lines) { + Rule* rule = new Rule(); + rule->AddConditions(ParseConditions(lines)); + rule->AddActions(ParseActions(lines)); + return rule; +} + +#define CONDITION(NAME) if(line.compare(0, wcslen(L#NAME), L#NAME) == 0) { int len = wcslen(L#NAME); items.push_back(new NAME##Condition(line.length() > len ? trim_copy(line.substr(len + 1)) : L"")); match = true; } +#define ACTION(NAME) if(line.compare(0, wcslen(L#NAME), L#NAME) == 0) { int len = wcslen(L#NAME); items.push_back(new NAME##Action(line.length() > len ? trim_copy(line.substr(len + 1)) : L"")); match = true; } + +std::vector Configuration::ParseConditions(std::vector& lines) { + std::vector items; + lines.erase(std::remove_if(lines.begin(), lines.end(), [&items](std::wstring const& line) { + bool match = false; + CONDITION(Code) + else CONDITION(Type) + else CONDITION(Class) + else CONDITION(Rarity) + else CONDITION(Ethereal) + else CONDITION(Runeword) + else CONDITION(Prefix) + else CONDITION(Suffix) + else CONDITION(ItemLevel) + else CONDITION(Quality) + else CONDITION(AreaLevel) + else CONDITION(CharacterLevel) + else CONDITION(Difficulty) + else CONDITION(Rune) + else CONDITION(Identified) + else CONDITION(Id) + else CONDITION(Gold) + else CONDITION(Stats) + else CONDITION(Defense) + else CONDITION(Armor) + else CONDITION(Weapon) + else CONDITION(Price) + else CONDITION(Mode) + else CONDITION(Sockets) + else CONDITION(Width) + else CONDITION(Height) + else CONDITION(Random) + return match; + }), lines.end()); + return items; +} + +std::vector Configuration::ParseActions(std::vector& lines) { + std::vector items; + lines.erase(std::remove_if(lines.begin(), lines.end(), [&items](std::wstring const& line) { + bool match = false; + ACTION(Continue) + else ACTION(SetStyle) + else ACTION(SetName) + else ACTION(SetDescription) + else ACTION(SetBackgroundColor) + else ACTION(SetInventoryColor) + else ACTION(SetBorderColor) + else ACTION(ChatNotify) + else ACTION(PlayAlert) + else ACTION(MinimapIcon) + return match; + }), lines.end()); + return items; +} + +#undef CONDITION +#undef ACTION + +//init any constants used in rules i.e. item names/skill names +void Configuration::InitalizeConditionVariables() { + //replace any variables used in the filter. i.e. item names/codes/rune numbers/etc... + InitializeTypesCodesAndRunes(); + InitializeClass(); + InitializeRaritiesAndQualities(); + InitializeOther(); + + /* + for (auto &rule : GlobalRules) { + for (auto& condition : rule->GetConditions()) { + Logger::Info("%s\n", condition->ToString().c_str()); + } + } + */ +} + +void Configuration::InitializeTypesCodesAndRunes() { + std::unordered_map names; + std::unordered_map codes; + std::unordered_map runes; + + DataTables* sgptDataTables = *D2COMMON_gpDataTables; + + for (int i = 0; i < D2COMMON_ItemDataTbl->nItemsTxtRecordCount; i++) { + auto pItemTxt = D2COMMON_ItemDataTbl->pItemsTxt[i]; + std::wstring wNameStr = D2LANG_GetStringFromTblIndex(pItemTxt.wNameStr); + names[wNameStr] = pItemTxt.dwCode; + std::wstring wCode = std::wstring(4, L' '); + mbstowcs(&wCode[0], pItemTxt.szCode, 4); + codes[wCode] = pItemTxt.dwCode; + if (pItemTxt.wType[0] == ItemType::RUNE) { + int nRuneNumber = std::stoi(std::string(&pItemTxt.szCode[1], 3)); + runes[wNameStr] = nRuneNumber; + size_t nFound = wNameStr.find(L" "); + if (nFound != std::wstring::npos) { + runes[wNameStr.substr(0, nFound)] = nRuneNumber; + } + } + } + + for (auto& rule : GlobalRules) { + for (auto& condition : rule.second->GetConditions()) { + switch (condition->GetType()) { + case ConditionType::TYPE: + condition->Initialize(names); + break; + case ConditionType::CODE: + condition->Initialize(codes); + break; + case ConditionType::RUNE: + condition->Initialize(runes); + break; + default: + break; + } + } + } +} + +void Configuration::InitializeClass() { + for (auto& rule : GlobalRules) { + for (auto& condition : rule.second->GetConditions()) { + switch (condition->GetType()) { + case ConditionType::CLASS: + condition->Initialize(ItemTypes); + break; + default: + break; + } + } + } +} + +#define KV(STR, IDX) { L#STR, ##IDX## } +void Configuration::InitializeRaritiesAndQualities() { + std::unordered_map rarities = { + KV(None, 0), KV(Inferior, 1), KV(Normal, 2), + KV(Superior, 3), KV(Magic, 4), KV(Set, 5), KV(Rare, 6), + KV(Unique, 7), KV(Crafted, 8), KV(Tempered, 9) + }; + + std::unordered_map qualities = { + KV(None, -1), KV(Normal, 0), KV(Exceptional, 1), KV(Elite, 2) + }; + for (auto& rule : GlobalRules) { + for (auto& condition : rule.second->GetConditions()) { + switch (condition->GetType()) { + case ConditionType::RARITY: + condition->Initialize(rarities); + break; + case ConditionType::QUALITY: + condition->Initialize(qualities); + break; + default: + break; + } + } + } +} +#undef KV + + +void Configuration::InitializeOther() { + std::unordered_map variables; + + for (auto& rule : GlobalRules) { + for (auto& condition : rule.second->GetConditions()) { + switch (condition->GetType()) { + case ConditionType::STATS: + case ConditionType::ETHEREAL: + case ConditionType::RUNEWORD: + case ConditionType::PREFIX: + case ConditionType::SUFFIX: + case ConditionType::ITEM_LEVEL: + case ConditionType::AREA_LEVEL: + case ConditionType::CHARACTER_LEVEL: + case ConditionType::DIFFICULTY: + case ConditionType::ID: + case ConditionType::GOLD: + case ConditionType::DEFENSE: + case ConditionType::ARMOR: + case ConditionType::WEAPON: + case ConditionType::PRICE: + case ConditionType::MODE: + case ConditionType::IDENTIFIED: + case ConditionType::SOCKETS: + case ConditionType::WIDTH: + case ConditionType::HEIGHT: + case ConditionType::RANDOM: + condition->Initialize(variables); + break; + default: + break; + } + } + } +} + +#undef COMMENT_STR +#undef STYLE_STR +#undef SHOW_STR +#undef HIDE_STR \ No newline at end of file diff --git a/Configuration.h b/Configuration.h new file mode 100644 index 0000000..f2b4b36 --- /dev/null +++ b/Configuration.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include +#include +#include "Rule.h" + +extern std::unordered_map> GlobalStyles; +extern std::map GlobalRules; + +class Configuration { +private: + std::wstring m_Path; + bool m_Loaded = false; + + void HandleToken(uint32_t lineNumber, std::vector& lines); + std::vector ParseStyle(std::vector& lines); + Rule* ParseRule(std::vector& lines); + std::vector ParseConditions(std::vector& lines); + std::vector ParseActions(std::vector& lines); + void InitalizeConditionVariables(); + void InitializeTypesCodesAndRunes(); + void InitializeClass(); + void InitializeRaritiesAndQualities(); + void InitializeOther(); +public: + Configuration(std::wstring sPath); + void Load(); + bool IsLoaded() { return m_Loaded; } +}; + diff --git a/D2Constants.h b/D2Constants.h index 5b9645d..79c80ce 100644 --- a/D2Constants.h +++ b/D2Constants.h @@ -34,7 +34,7 @@ template GameVersion::V113c ? GAME_EXE : DLLBASE_##DLL), O); /// -#define D2PTR(DLL, N, T, O) __declspec(selectany) extern T* DLL##_##N = (T*)GetDllOffset((GetGameVersion() > GameVersion::V113c ? GAME_EXE : DLLBASE_##DLL), O); +#define D2FUNC(DLL, N, R, C, A, O) typedef R (C* DLL##_##N##_t) A; __declspec(selectany) extern DLL##_##N##_t DLL##_##N = (DLL##_##N##_t)GetDllOffset((GetGameVersion() > D2Version::V113c ? GAME_EXE : DLLBASE_##DLL), O); /// +#define D2PTR(DLL, N, T, O) __declspec(selectany) extern T* DLL##_##N = (T*)GetDllOffset((GetGameVersion() > D2Version::V113c ? GAME_EXE : DLLBASE_##DLL), O); -#define F2(DLL, N, R, C, A, O113c, O114d) D2FUNC(DLL, N, R, C, A, (GetGameVersion() == GameVersion::V114d ? O114d : O113c)) -#define P2(DLL, N, T, O113c, O114d) D2PTR(DLL, N, T, (GetGameVersion() == GameVersion::V114d ? O114d : O113c)) +#define F2(DLL, N, R, C, A, O113c, O114d) D2FUNC(DLL, N, R, C, A, (GetGameVersion() == D2Version::V114d ? O114d : O113c)) +#define P2(DLL, N, T, O113c, O114d) D2PTR(DLL, N, T, (GetGameVersion() == D2Version::V114d ? O114d : O113c)) //Hooks for item filter P2(D2CLIENT, fpGetItemDescPatch, void, 0x5612C, 0xE64CE); @@ -100,7 +100,8 @@ F2(D2WIN, SetTextFont, Font, __fastcall, (Font dwSize), -10184, 0x102EF0); F2(D2WIN, D2DrawText, void, __fastcall, (const wchar_t* wStr, uint32_t nXpos, uint32_t nYpos, TextColor eColor, uint32_t dwCentered), -10150, 0x102320); //fix 1.14d -F2(D2WIN, GetTextPixelWidth, uint32_t, __fastcall, (const wchar_t* wStr), -10055, 0x1017D0); +F2(D2WIN, GetTextPixelWidthFileNo, uint32_t, __fastcall, (const wchar_t* wStr, uint32_t* dwWidth, uint32_t* dwFileNo), -10177, 0x0); +F2(D2WIN, GetTextPixelWidth, uint32_t, __fastcall, (const wchar_t* wStr), 0x0, 0x1017D0); //006C9030 / . 55 PUSH EBP //$ + 2C9030 / . 55 PUSH EBP @@ -108,4 +109,4 @@ F2(D2GFX, DrawRect, void, __stdcall, (RECT* pRect, uint8_t nPaletteIndex), -1003 //__fastcall for 114d: 0x6EFD0 F2(D2GFX, DrawSolidRectEx, void, __stdcall, (uint32_t nXStart, uint32_t nYStart, uint32_t nXEnd, uint32_t nYEnd, uint8_t nPaletteIndex, DrawMode eDrawMode), -10014, 0xF6300); -F2(D2LANG, GetStringFromTblIndex, wchar_t*, __fastcall, (uint16_t nTblIndex), -10003, 0x124A30); +F2(D2LANG, GetStringFromTblIndex, wchar_t*, __fastcall, (uint16_t nTblIndex), -10003, 0x124A30); \ No newline at end of file diff --git a/D2Structs.h b/D2Structs.h index be0b434..55c8faf 100644 --- a/D2Structs.h +++ b/D2Structs.h @@ -447,4 +447,84 @@ struct Unit Unit* pListNext; //0xE8 void* pMsgFirst; //0xEC void* pMsgLast; //0xF0 -}; \ No newline at end of file +}; + +struct LayerUnitStatId +{ + // We can not use a struct as function parameters here as it has a different effect when using the __fastcall calling convetion. + // Instead we just use D2SLayerStatIdStrc::PackedType so that we may easily change it later + using PackedType = int32_t; + + union + { + struct + { + uint16_t nLayer; //0x00 + uint16_t nStat; //0x02 + }; + PackedType nPackedValue; //0x00 + }; + + static LayerUnitStatId Make(uint16_t wLayer, uint16_t wStatId) { return { wLayer, wStatId }; } + static LayerUnitStatId MakeFromStatId(uint16_t wStatId) { return { 0, wStatId }; } + static LayerUnitStatId FromPackedType(PackedType nPackedValue) { + LayerUnitStatId ls; + ls.nPackedValue = nPackedValue; + return ls; + } +}; + +struct UnitStat : LayerUnitStatId +{ + int32_t nValue; //0x04 +}; + +struct StatsArray +{ + UnitStat* pStat; //0x00 An Array[wStatCount] + uint16_t nStatCount; //0x04 + uint16_t nCapacity; //0x06 + static const int nGrowthAmount = 4; + static const int nShrinkThreshold = 8; +}; + +struct D2ModStatsArrayStrc +{ + LayerUnitStatId* pStat; //0x00 An Array[wStatCount] + uint16_t nStatCount; //0x04 + uint16_t nCapacity; //0x06 + static const int nGrowthAmount = 4; + static const int nShrinkThreshold = 8; +}; + +struct StatList +{ + void* pMemPool; //0x00 + Unit* pUnit; //0x04 + uint32_t dwOwnerType; //0x08 + uint32_t dwOwnerId; //0x0C + uint32_t dwFlags; //0x10 D2C_StatlistFlags + uint32_t dwStateNo; //0x14 + int32_t dwExpireFrame; //0x18 + uint32_t dwSkillNo; //0x1C + uint32_t dwSLvl; //0x20 + StatsArray Stats; //0x24 + StatList* pPrevLink; //0x2C + StatList* pNextLink; //0x30 + StatList* pParent; //0x34 + void* fpStatRemove; //0x38 +}; + +struct StatListEx : public StatList +{ + StatList* pMyLastList; //0x3C + StatList* pMyStats; //0x40 + Unit* pOwner; //0x44 + StatsArray FullStats; //0x48 + D2ModStatsArrayStrc ModStats; //0x50 + uint32_t* StatFlags; //0x58 8bytes per states + void* fpCallBack; //0x5C + Game* pGame; //0x60 +}; + +#pragma pack(pop) \ No newline at end of file diff --git a/Expression.cpp b/Expression.cpp new file mode 100644 index 0000000..9ceed2d --- /dev/null +++ b/Expression.cpp @@ -0,0 +1,637 @@ +#include "Expression.h" +#include +#include +#include +#include "D2Ptrs.h" +#include "Utils.h" +#include "ItemFilter.h" + + +static int32_t GetFilterLevel(Unit* pUnit) { + return FilterLevel; +} + +static int32_t GetPingLevel(Unit* pUnit) { + return PingLevel; +} + +static int32_t GetPlayerLevel(Unit* pUnit) { + return D2COMMON_STATLIST_GetUnitStatUnsigned(D2CLIENT_GetPlayerUnit(), Stat::LEVEL, 0); +} + +static int32_t GetItemLevel(Unit* pUnit) { + return pUnit->pItemData->dwItemLevel; +} + +static int32_t GetLevel(Unit* pUnit) { + return GetItemsTxt(pUnit).nLevel; +} + +static int32_t GetMagicLevel(Unit* pUnit) { + return GetItemsTxt(pUnit).nMagicLevel; +} + +static int32_t GetAffixLevel(int32_t iLvl, int32_t qLvl, int32_t mLvl) { + if (iLvl > 99) iLvl = 99; + if (qLvl > iLvl) iLvl = qLvl; + if (mLvl > 0) return iLvl + mLvl > 99 ? 99 : iLvl + mLvl; + return ((iLvl) < (99 - ((qLvl) / 2)) ? (iLvl)-((qLvl) / 2) : (iLvl) * 2 - 99); +} + +static int32_t GetAffixLevel(Unit* pUnit) { + return GetAffixLevel( + GetItemLevel(pUnit), + GetLevel(pUnit), + GetMagicLevel(pUnit) + ); +} + +static int32_t GetCraftAffixLevel(Unit* pUnit) { + int32_t cLvl = GetPlayerLevel(pUnit); + int32_t iLvl = GetItemLevel(pUnit); + return GetAffixLevel( + iLvl / 2 + cLvl / 2, + GetLevel(pUnit), + GetMagicLevel(pUnit) + ); +} + +static std::unordered_map GlobalVariables = { + { L"Filter Level", &GetFilterLevel }, + { L"Ping Level", &GetPingLevel }, + { L"Character Level", &GetPlayerLevel }, + { L"Item Level", &GetItemLevel }, + { L"Quality Level", &GetLevel }, + { L"Magic Level", &GetMagicLevel }, + { L"Affix Level", &GetAffixLevel }, + { L"Craft Affix Level", &GetCraftAffixLevel } +}; + +int32_t Logical::Evaluate(Unit* pItem) { + int32_t left = m_Left->Evaluate(pItem); + int32_t right = m_Right->Evaluate(pItem); + int32_t ret = 0; + //Logger::Info("Doing %d%s%d\n", left, OPS[static_cast(m_Operator)], right); + switch (m_Operator) { + case Token::AND: ret = left && right; break; + case Token::OR: ret = left || right; break; + case Token::EQUALS: ret = left == right; break; + case Token::BANG_EQUALS: ret = left != right; break; + case Token::GREATER_THAN: ret = left > right; break; + case Token::GREATER_THAN_EQUALS: ret = left >= right; break; + case Token::LESS_THAN: ret = left < right; break; + case Token::LESS_THAN_EQUALS: ret = left <= right; break; + default: + break; + //error... + } + //Logger::Info("Return %d%s%d = %d\n", left, OPS[static_cast(m_Operator)], right, ret); + return ret; +} + +std::wstring Logical::ToString(Unit* pItem) { + return std::format(L"{}{}{}", m_Left->ToString(pItem), OPS[static_cast(m_Operator)], m_Right->ToString(pItem)); +} + +void Logical::SetVariables(std::unordered_map& variables) { + m_Left->SetVariables(variables); + m_Right->SetVariables(variables); +} + +int32_t Binary::Evaluate(Unit* pItem) { + int32_t left = m_Left->Evaluate(pItem); + int32_t right = m_Right->Evaluate(pItem); + int32_t ret = 0; + //Logger::Info("Doing %d%s%d\n", left, OPS[static_cast(m_Operator)], right); + switch (m_Operator) { + case Token::PLUS: ret = left + right; break; + case Token::MINUS: ret = left - right; break; + case Token::MULTIPLY: ret = left * right; break; + case Token::DIVIDE: ret = left / right; break; + default: + break; + //error.. + } + //Logger::Info("Return %d%s%d = %d\n", left, OPS[static_cast(m_Operator)], right, ret); + return ret; +} + +std::wstring Binary::ToString(Unit* pItem) { + return std::format(L"{}{}{}", m_Left->ToString(pItem), OPS[static_cast(m_Operator)], m_Right->ToString(pItem)); +} + +void Binary::SetVariables(std::unordered_map& variables) { + m_Left->SetVariables(variables); + m_Right->SetVariables(variables); +} + +int32_t In::Evaluate(Unit* pItem) { + int32_t left = m_Left->Evaluate(pItem); + int32_t min = m_Min->Evaluate(pItem); + int32_t max = m_Max->Evaluate(pItem); + return left >= min + && left <= max; +} + +std::wstring In::ToString(Unit* pItem) { + return std::format(L"{}{}{}-{}", m_Left->ToString(pItem), OPS[static_cast(m_Operator)], m_Min->ToString(pItem), m_Max->ToString(pItem)); +} + +void In::SetVariables(std::unordered_map& variables) { + m_Left->SetVariables(variables); + m_Min->SetVariables(variables); + m_Max->SetVariables(variables); +} + +int32_t Unary::Evaluate(Unit* pItem) { + int32_t right = m_Right->Evaluate(pItem); + int32_t ret = 0; + switch (m_Operator) { + case Token::BANG: ret = !right; break; + case Token::MINUS: ret = -1 * m_Right->Evaluate(pItem); break; + default: + break; + //error + } + return ret; +} + +std::wstring Unary::ToString(Unit* pItem) { + return std::format(L"{}{}", OPS[static_cast(m_Operator)], m_Right->ToString(pItem)); +} + +void Unary::SetVariables(std::unordered_map& variables) { + m_Right->SetVariables(variables); +} + +int32_t Literal::Evaluate(Unit* pItem) { + return m_Value; +} + +std::wstring Literal::ToString(Unit* pItem) { + return std::format(L"{}", m_Value); +} + +void Literal::SetVariables(std::unordered_map& variables) { +} + +int32_t Boolean::Evaluate(Unit* pItem) { + return m_Value; +} + +std::wstring Boolean::ToString(Unit* pItem) { + return std::format(L"{}", m_Value); +} + +void Boolean::SetVariables(std::unordered_map& variables) { +} + +Variable::Variable(std::wstring variable) { + m_Variable = variable; + if (GlobalVariables.contains(m_Variable)) { + m_Func = GlobalVariables[m_Variable]; + } +}; + +int32_t Variable::Evaluate(Unit* pItem) { + if (m_Func != nullptr) { + return m_Func(pItem); + } + return m_Value.value_or(0); +} + +std::wstring Variable::ToString(Unit* pItem) { + if (m_Func != nullptr) { + return std::format(L"{}({})", m_Variable, m_Func(pItem)); + } + if (!m_Value) { + return std::format(L"{}(nullptr)", m_Variable); + } + return std::format(L"{}({})", m_Variable, m_Value.value()); +} + +void Variable::SetValue(const int32_t v) { + if (m_Func != nullptr) { + return; + } + m_Value = v; +} + +void Variable::SetVariables(std::unordered_map& variables) { + if (variables.contains(m_Variable)) { + SetValue(variables[m_Variable]); + } +} + +int32_t Call::EvaluateClass(Unit* pItem, std::vector& args) { + for (auto& arg : args) { + if (D2COMMON_ITEMS_CheckItemTypeId(pItem, static_cast(arg))) { + return 1; + } + } + return 0; +} + +int32_t Call::EvaluateChargedSkill(Unit* pItem, Stat stat, std::vector& args) { + if (!(pItem->pStatListEx && (pItem->pStatListEx->dwFlags & StatlistFlags::EXTENDED))) { + return 0; + } + StatList* pStatList = pItem->pStatListEx->pMyLastList; + while (pStatList && !(0x40 & pStatList->dwFlags)) + { + pStatList = pStatList->pPrevLink; + } + if (!pStatList) { + return 0; + } + int32_t value = 0; + int32_t layer = 0; + if (args.size() > 0) layer = args[0]; + StatsArray arr = pStatList->Stats; + for (uint8_t i = 0; i < arr.nStatCount; i++) { + UnitStat stat = arr.pStat[i]; + if (stat.nStat == static_cast(Stat::ITEM_CHARGED_SKILL) + && (stat.nLayer >> 6) == layer) { + int32_t level = stat.nStat & 0x3F; + value = (level > value) ? level : value; + } + } + return value; +} + +int32_t Call::EvaluateStat(Unit* pItem, Stat stat, std::vector& args) { + int32_t layer = 0; + if (args.size() > 0) layer = args[0]; + return D2COMMON_STATLIST_GetUnitStatUnsigned(pItem, stat, layer); +} + +int32_t Call::Evaluate(Unit* pItem) { + std::vector args; + std::transform(m_Args.begin(), m_Args.end(), std::back_inserter(args), + [pItem](Expression*& expression) { return expression->Evaluate(pItem); }); + switch (m_Func) { + case Token::STAT: + { + int32_t statId = args[0]; + args.erase(args.begin()); + return EvaluateStat(pItem, static_cast(statId), args); + } + case Token::CLASS: + return EvaluateClass(pItem, args); + case Token::CHARGEDSKILL: + return EvaluateChargedSkill(pItem, Stat::ITEM_CHARGED_SKILL, args); + case Token::TABSKILL: + return EvaluateStat(pItem, Stat::ITEM_ADDSKILL_TAB, args); + case Token::SKILL: + return EvaluateStat(pItem, Stat::ITEM_SINGLESKILL, args); + case Token::CLASSSKILL: + return EvaluateStat(pItem, Stat::ITEM_ADDCLASSSKILLS, args); + //by default we exclude 0s in min and max because a stat that doesn't exist on an item + //will return 0 for the stat value. MinIn (min inclusive) will include zeros. + case Token::MAX: + return *std::ranges::max_element(args, [](int32_t o1, int32_t o2) { + return o1 != 0 && o2 != 0 ? o1 > o2 : false; + }); + case Token::MIN: + return *std::ranges::min_element(args, [](int32_t o1, int32_t o2) { + return o1 != 0 && o2 != 0 ? o1 < o2 : false; + }); + case Token::MININ: + return *std::ranges::min_element(args, [](int32_t o1, int32_t o2) { + return o1 < o2; + }); + default: + return 0; + //throw error? + } +} + +std::wstring Call::ToString(Unit* pItem) { + std::wostringstream ss; + for (auto& expression : m_Args) { + if (&expression != &m_Args.front()) { + ss << L", L"; + } + ss << expression->ToString(pItem); + } + return std::format(L"{}({})({})", OPS[static_cast(m_Func)], ss.str(), Evaluate(pItem)); +} + +void Call::SetVariables(std::unordered_map& variables) { + for (auto& expression : m_Args) { + expression->SetVariables(variables); + } +} + +int32_t ListExpression::Evaluate(Unit* pItem) { + //return true if any are true + return std::any_of(m_List.begin(), m_List.end(), [pItem](Expression* expression) { return expression->Evaluate(pItem) != 0; }); +} + +std::wstring ListExpression::ToString(Unit* pItem) { + std::wostringstream ss; + for (auto& expression : m_List) { + if (&expression != &m_List.front()) { + ss << L" or "; + } + ss << expression->ToString(pItem).c_str(); + } + return ss.str(); +} + +void ListExpression::SetVariables(std::unordered_map& variables) { + for (auto& expression : m_List) { + expression->SetVariables(variables); + } +} + +bool Tokenizer::IsNull(wchar_t c) { + return c == L'\0'; +} + +bool Tokenizer::IsOperator(const wchar_t*& expression) { + return _wcsnicmp(expression, L" and ", 5) == 0 + || _wcsnicmp(expression, L"true", 4) == 0 + || _wcsnicmp(expression, L"false", 5) == 0 + || _wcsnicmp(expression, L" or ", 4) == 0 + || _wcsnicmp(expression, L"|", 1) == 0 + || _wcsnicmp(expression, L"&", 1) == 0 + || wcsncmp(expression, L",", 1) == 0 + || wcsncmp(expression, L"=", 1) == 0 + || wcsncmp(expression, L"<", 1) == 0 + || wcsncmp(expression, L">", 1) == 0 + || wcsncmp(expression, L"!", 1) == 0 + || wcsncmp(expression, L"(", 1) == 0 + || wcsncmp(expression, L")", 1) == 0 + || wcsncmp(expression, L"+", 1) == 0 + || wcsncmp(expression, L"-", 1) == 0 + || wcsncmp(expression, L"*", 1) == 0 + || wcsncmp(expression, L"/", 1) == 0; +} + +std::wstring Tokenizer::ParseVariable(const wchar_t*& expression) { + int s = 0; + while (!IsOperator(expression) && !IsNull(*expression)) { + expression += 1; + s += 1; + } + return std::wstring(expression - s, s); +} + +std::wstring Tokenizer::ParseQuotedVariable(const wchar_t*& expression) { + int s = 1; + expression += 1; + while (*expression != '"' && !IsNull(*expression)) { + expression += 1; + s += 1; + } + if (*expression != '"') { + //throw error. unquoted string variable + } + expression += 1; + return std::wstring(expression - s, s - 1); +} + +std::wstring Tokenizer::ParseDigit(const wchar_t*& expression) { + int s = 0; + while (std::isdigit(*expression) && !IsNull(*expression)) { + expression += 1; + s += 1; + } + return std::wstring(expression - s, s); +} + +void Tokenizer::Tokenize(const wchar_t*& expression) { + while (!IsNull(*expression)) { + if (*expression == L'(') { expression++; m_Tokens.push_back(new TokenizerToken(Token::LEFT_PAREN)); } + else if (*expression == L')') { expression++; m_Tokens.push_back(new TokenizerToken(Token::RIGHT_PAREN)); } + else if (_wcsnicmp(expression, L"chargedskill(", 13) == 0) { expression += 12; m_Tokens.push_back(new TokenizerToken(Token::CHARGEDSKILL)); } + else if (_wcsnicmp(expression, L"classskill(", 11) == 0) { expression += 10; m_Tokens.push_back(new TokenizerToken(Token::CLASSSKILL)); } + else if (_wcsnicmp(expression, L"tabskill(", 9) == 0) { expression += 8; m_Tokens.push_back(new TokenizerToken(Token::TABSKILL)); } + else if (_wcsnicmp(expression, L"skill(", 6) == 0) { expression += 5; m_Tokens.push_back(new TokenizerToken(Token::SKILL)); } + else if (_wcsnicmp(expression, L"class(", 6) == 0) { expression += 5; m_Tokens.push_back(new TokenizerToken(Token::CLASS)); } + else if (_wcsnicmp(expression, L"minin(", 6) == 0) { expression += 5; m_Tokens.push_back(new TokenizerToken(Token::MININ)); } + else if (_wcsnicmp(expression, L"stat(", 5) == 0) { expression += 4; m_Tokens.push_back(new TokenizerToken(Token::STAT)); } + else if (_wcsnicmp(expression, L"false", 5) == 0) { expression += 5; m_Tokens.push_back(new TokenizerToken(Token::_FALSE)); } + else if (_wcsnicmp(expression, L"true", 4) == 0) { expression += 4; m_Tokens.push_back(new TokenizerToken(Token::_TRUE)); } + else if (_wcsnicmp(expression, L"min(", 4) == 0) { expression += 3; m_Tokens.push_back(new TokenizerToken(Token::MIN)); } + else if (_wcsnicmp(expression, L"max(", 4) == 0) { expression += 3; m_Tokens.push_back(new TokenizerToken(Token::MAX)); } + else if (_wcsnicmp(expression, L"and ", 4) == 0) { expression += 3; m_Tokens.push_back(new TokenizerToken(Token::AND)); } + else if (_wcsnicmp(expression, L"or ", 3) == 0) { expression += 2; m_Tokens.push_back(new TokenizerToken(Token::OR)); } + else if (wcsncmp(expression, L"&&", 2) == 0) { expression += 2; m_Tokens.push_back(new TokenizerToken(Token::AND)); } + else if (wcsncmp(expression, L"||", 2) == 0) { expression += 2; m_Tokens.push_back(new TokenizerToken(Token::OR)); } + else if (_wcsnicmp(expression, L"in ", 3) == 0) { expression += 2; m_Tokens.push_back(new TokenizerToken(Token::_IN)); } + else if (wcsncmp(expression, L"!=", 2) == 0) { expression += 2; m_Tokens.push_back(new TokenizerToken(Token::BANG_EQUALS)); } + else if (wcsncmp(expression, L"==", 2) == 0) { expression += 2; m_Tokens.push_back(new TokenizerToken(Token::EQUALS)); } + else if (wcsncmp(expression, L">=", 2) == 0) { expression += 2; m_Tokens.push_back(new TokenizerToken(Token::GREATER_THAN_EQUALS)); } + else if (wcsncmp(expression, L"<=", 2) == 0) { expression += 2; m_Tokens.push_back(new TokenizerToken(Token::LESS_THAN_EQUALS)); } + else if (*expression == L'>') { expression++; m_Tokens.push_back(new TokenizerToken(Token::GREATER_THAN)); } + else if (*expression == L'<') { expression++; m_Tokens.push_back(new TokenizerToken(Token::LESS_THAN)); } + else if (*expression == L'!') { expression++; m_Tokens.push_back(new TokenizerToken(Token::BANG)); } + else if (*expression == L'=') { expression++; m_Tokens.push_back(new TokenizerToken(Token::EQUALS)); } + else if (*expression == L',') { expression++; m_Tokens.push_back(new TokenizerToken(Token::COMMA)); } + else if (*expression == L'-') { expression++; m_Tokens.push_back(new TokenizerToken(Token::MINUS)); } + else if (*expression == L'+') { expression++; m_Tokens.push_back(new TokenizerToken(Token::PLUS)); } + else if (*expression == L'*') { expression++; m_Tokens.push_back(new TokenizerToken(Token::MULTIPLY)); } + else if (*expression == L'/') { expression++; m_Tokens.push_back(new TokenizerToken(Token::DIVIDE)); } + else if (iswdigit(*expression)) { m_Tokens.push_back(new TokenizerToken(Token::NUMBER, ParseDigit(expression))); } + else if (*expression == L'"') { m_Tokens.push_back(new TokenizerToken(Token::VARIABLE, ParseQuotedVariable(expression))); } + else if (iswalpha(*expression)) { m_Tokens.push_back(new TokenizerToken(Token::VARIABLE, ParseVariable(expression))); } + else if (iswspace(*expression)) { expression++; } + else { + //error... + } + } +} + +bool Tokenizer::Match(Token t) { + if (m_Current < m_Tokens.size() && m_Tokens[m_Current]->GetTokenType() == t) { + m_Current++; + return true; + } + return false; +} + +bool Tokenizer::Check(Token t) { + return Peek()->GetTokenType() == t; +} + +TokenizerToken* Tokenizer::Previous() { + if (m_Current <= 0) { + return nullptr; + } + return m_Tokens[m_Current - 1]; +} + +TokenizerToken* Tokenizer::Peek() { + return m_Tokens[m_Current]; +} + +void Tokenizer::Reset() { + m_Current = 0; +} + +void Tokenizer::Insert(TokenizerToken* t) { + m_Tokens.insert(m_Tokens.begin() + m_Current, t); +} + +Expression* Parser::_finishCall(Token call, Tokenizer* t) { + std::vector arguments; + while (!t->Match(Token::RIGHT_PAREN)) { + do { + arguments.push_back(_expression(t)); + } while (t->Match(Token::COMMA)); + } + return new Call(call, arguments); +} + +Expression* Parser::_primary(Tokenizer* t, Expression* lhs) { + if (lhs != nullptr) { return lhs; } + if (t->Match(Token::VARIABLE)) { return new Variable(t->Previous()->GetValue()); } + if (t->Match(Token::NUMBER)) { return new Literal(t->Previous()->GetValue()); } + if (t->Match(Token::_TRUE)) { return new Boolean(1); } + if (t->Match(Token::_FALSE)) { return new Boolean(0); } + if (t->Match(Token::LEFT_PAREN)) { + Expression* expr = _expression(t); + if (t->Match(Token::RIGHT_PAREN)) { + return expr; + } + } + if (t->Match(Token::STAT) + || t->Match(Token::CLASS) + || t->Match(Token::MIN) + || t->Match(Token::MININ) + || t->Match(Token::MAX) + || t->Match(Token::CHARGEDSKILL) + || t->Match(Token::CLASSSKILL) + || t->Match(Token::TABSKILL) + || t->Match(Token::SKILL) + ) { + Token call = t->Previous()->GetTokenType(); + if (t->Match(Token::LEFT_PAREN)) { + return _finishCall(call, t); + } + } + return nullptr; + //throw error. one of the above should of been matched +} + +Expression* Parser::_unary(Tokenizer* t, Expression* lhs) { + if (t->Match(Token::BANG) || t->Match(Token::MINUS)) { + TokenizerToken* op = t->Previous(); + Expression* rhs = _unary(t); + return new Unary(rhs, op->GetTokenType()); + } + Expression* expr = _primary(t, lhs); + return expr; +} + +Expression* Parser::_factor(Tokenizer* t, Expression* lhs) { + Expression* expr = _unary(t, lhs); + while (t->Match(Token::MULTIPLY) || t->Match(Token::DIVIDE)) { + TokenizerToken* op = t->Previous(); + Expression* rhs = _unary(t); + expr = new Binary(expr, rhs, op->GetTokenType()); + } + return expr; +} + +Expression* Parser::_term(Tokenizer* t, Expression* lhs) { + Expression* expr = _factor(t, lhs); + while (t->Match(Token::PLUS) || t->Match(Token::MINUS)) { + TokenizerToken* op = t->Previous(); + Expression* rhs = _factor(t); + expr = new Binary(expr, rhs, op->GetTokenType()); + } + return expr; +} + +Expression* Parser::_comparison(Tokenizer* t, Expression* lhs) { + Expression* expr = _term(t, lhs); + while (t->Match(Token::GREATER_THAN) || t->Match(Token::GREATER_THAN_EQUALS) + || t->Match(Token::LESS_THAN) || t->Match(Token::LESS_THAN_EQUALS) + || t->Match(Token::_IN)) { + TokenizerToken* op = t->Previous(); + if (op->GetTokenType() != Token::_IN) { + Expression* rhs = _term(t); + expr = new Logical(expr, rhs, op->GetTokenType()); + } + else { + Expression* min = _primary(t); + if (t->Match(Token::MINUS)) { + Expression* max = _primary(t); + expr = new In(expr, min, max, op->GetTokenType()); + } + else { + //throw error... + } + } + } + return expr; +} + +Expression* Parser::_equality(Tokenizer* t, Expression* lhs) { + Expression* expr = _comparison(t, lhs); + while (t->Match(Token::EQUALS) || t->Match(Token::BANG_EQUALS)) { + TokenizerToken* op = t->Previous(); + Expression* rhs = _comparison(t); + expr = new Logical(expr, rhs, op->GetTokenType()); + } + return expr; +} + +Expression* Parser::_and(Tokenizer* t, Expression* lhs) { + Expression* expr = _equality(t, lhs); + while (t->Match(Token::AND)) { + TokenizerToken* op = t->Previous(); + Expression* rhs = _equality(t); + expr = new Logical(expr, rhs, op->GetTokenType()); + } + return expr; +} + +Expression* Parser::_or(Tokenizer* t, Expression* lhs) { + Expression* expr = _and(t, lhs); + while (t->Match(Token::OR)) { + TokenizerToken* op = t->Previous(); + Expression* rhs = _and(t); + expr = new Logical(expr, rhs, op->GetTokenType()); + } + return expr; +} + +Expression* Parser::_expression(Tokenizer* t, Expression* lhs) { + Expression* expr = _or(t, lhs); + return expr; +} + +Expression* Parser::Parse(const wchar_t* expression) { + Tokenizer* tokenizer = new Tokenizer(expression); + Expression* expr = _expression(tokenizer); + return expr; +} + +ListExpression* Parser::Parse(Variable* lhs, const wchar_t* expression) { + ListExpression* list = new ListExpression(); + Tokenizer* tokenizer = new Tokenizer(expression); + do { + auto nextToken = tokenizer->Peek()->GetTokenType(); + if (nextToken == Token::_IN + || nextToken == Token::EQUALS || nextToken == Token::BANG_EQUALS + || nextToken == Token::LESS_THAN || nextToken == Token::LESS_THAN_EQUALS + || nextToken == Token::GREATER_THAN || nextToken == Token::GREATER_THAN_EQUALS) { + } + else { + tokenizer->Insert(new TokenizerToken(Token::EQUALS)); + } + list->Push(_expression(tokenizer, lhs)); + } while (tokenizer->Match(Token::COMMA)); + return list; +} + +Expression* Parser::ParseCall(Token func, const wchar_t* args) { + std::wstring formatted = std::format(L"({})", args); + Tokenizer* call = new Tokenizer(formatted.c_str()); + call->Insert(new TokenizerToken(func)); + return _expression(call); +} diff --git a/Expression.h b/Expression.h index 6fdd279..22e87ca 100644 --- a/Expression.h +++ b/Expression.h @@ -1,19 +1,17 @@ #pragma once - -#include #include -#include -#include -#include -#include -#include -#include -#include +#include "D2Structs.h" +#include "D2Constants.h" +#include +#include +#include +#include + /* -expression -> equality ; +expression -> or ; or -> and ( ( "or" | "||" ) and )* ; -and -> equality ( ( "or" | "||" ) equality )* ; +and -> equality ( ( "and" | "&&" ) equality )* ; equality -> comparison ( ( "!=" | "==" ) comparison )* ; comparison -> term ( ( ">" | ">=" | "<" | "<=" ) term )* ; term -> factor ( ( "-" | "+" ) factor )* ; @@ -25,14 +23,14 @@ primary -> NUMBER | VARIABLE | "true" | "false" | "nil" call -> "(" expression ")" ; */ -enum class Tokens : uint8_t { +enum class Token : uint8_t { NONE, OR, AND, LEFT_PAREN, RIGHT_PAREN, BANG, BANG_EQUALS, - EQUALS, + EQUALS, GREATER_THAN, GREATER_THAN_EQUALS, LESS_THAN, LESS_THAN_EQUALS, _IN, @@ -42,631 +40,184 @@ enum class Tokens : uint8_t { //funcs STAT, CLASS, + TABSKILL, CLASSSKILL, CHARGEDSKILL, SKILL, MIN, MININ, MAX, COMMA, - NUMBER, VARIABLE, + NUMBER, VARIABLE, _TRUE, _FALSE, END }; -//https://craftinginterpreters.com/parsing-expressions.html -//https://github.com/munificent/craftinginterpreters/blob/master/java/com/craftinginterpreters/lox/Parser.java -template -class Expr -{ -protected: - std::wstring GetOperation(Tokens op) { - switch (op) { - case Tokens::_IN: return L" in "; - case Tokens::OR: return L" or "; - case Tokens::AND: return L" and "; - case Tokens::LEFT_PAREN: return L"("; - case Tokens::RIGHT_PAREN: return L")"; - case Tokens::BANG_EQUALS: return L" != "; - case Tokens::EQUALS: return L" == "; - case Tokens::GREATER_THAN: return L" > "; - case Tokens::GREATER_THAN_EQUALS: return L" >= "; - case Tokens::LESS_THAN: return L" < "; - case Tokens::LESS_THAN_EQUALS: return L" <= "; - case Tokens::MINUS: return L" - "; - case Tokens::PLUS: return L" + "; - case Tokens::MULTIPLY: return L" * "; - case Tokens::DIVIDE: return L" / "; - case Tokens::_TRUE: return L"True"; - case Tokens::_FALSE: return L"False"; - case Tokens::STAT: return L"Stat"; - case Tokens::CLASS: return L"Class"; - case Tokens::MIN: return L"Min"; - case Tokens::MININ: return L"MinIn"; - case Tokens::MAX: return L"Max"; - default: - return L"ERROR"; - } - } +static const wchar_t* OPS[] = { L"", L" or ", L" and ", L"(", L")", L"!", L"!=", L"=", L">", L">=", L"<", L"<=", L" in ", L"-", L"+", L"*", L"/", L"Stat", L"Class", L"TabSkill", L"ClassSkill", L"ChargedSkill", L"Skill", L"Min", L"MinIn", L"Max", L",", L"", L"", L"True", L"False", L"" }; + +class Expression { public: - virtual T Evaluate(Unit* pItem) = 0; - virtual std::wstring ToString() = 0; - virtual void SetVariables(std::map variables) = 0; + virtual int32_t Evaluate(Unit* pItem) = 0; + virtual std::wstring ToString(Unit* pItem) = 0; + virtual void SetVariables(std::unordered_map& variables) = 0; }; -template -class Logical : public Expr { +class Logical : public Expression { protected: - Tokens m_Operator; - Expr* m_Left; - Expr* m_Right; + Token m_Operator; + Expression* m_Left; + Expression* m_Right; public: - Logical(Expr* left = nullptr, Expr* right = nullptr, Tokens op = Tokens::NONE) : m_Left(left), m_Right(right), m_Operator(op) {}; - std::wstring ToString() { - return L"(" + m_Left->ToString() + Expr::GetOperation(m_Operator) + m_Right->ToString() + L")"; - } - T Evaluate(Unit* pItem) { - switch (m_Operator) { - case Tokens::AND: return m_Left->Evaluate(pItem) && m_Right->Evaluate(pItem); - case Tokens::OR: return m_Left->Evaluate(pItem) || m_Right->Evaluate(pItem); - case Tokens::EQUALS: return m_Left->Evaluate(pItem) == m_Right->Evaluate(pItem); - case Tokens::BANG_EQUALS: return m_Left->Evaluate(pItem) != m_Right->Evaluate(pItem); - case Tokens::GREATER_THAN: return m_Left->Evaluate(pItem) > m_Right->Evaluate(pItem); - case Tokens::GREATER_THAN_EQUALS: return m_Left->Evaluate(pItem) >= m_Right->Evaluate(pItem); - case Tokens::LESS_THAN: return m_Left->Evaluate(pItem) < m_Right->Evaluate(pItem); - case Tokens::LESS_THAN_EQUALS: return m_Left->Evaluate(pItem) <= m_Right->Evaluate(pItem); - default: - throw L"Error"; - } - }; - void SetVariables(std::map variables) { - m_Left->SetVariables(variables); - m_Right->SetVariables(variables); - }; + Logical(Expression* left = nullptr, Expression* right = nullptr, Token op = Token::NONE) : m_Left(left), m_Right(right), m_Operator(op) {}; + int32_t Evaluate(Unit* pItem) override; + std::wstring ToString(Unit* pItem) override; + void SetVariables(std::unordered_map& variables) override; }; -template -class Binary : public Expr { +class Binary : public Expression { protected: - Tokens m_Operator; - Expr* m_Left; - Expr* m_Right; + Token m_Operator; + Expression* m_Left; + Expression* m_Right; public: - Binary(Expr* left = nullptr, Expr* right = nullptr, Tokens op = Tokens::NONE) : m_Left(left), m_Right(right), m_Operator(op) {}; - std::wstring ToString() { - return L"(" + m_Left->ToString() + Expr::GetOperation(m_Operator) + m_Right->ToString() + L")"; - } - T Evaluate(Unit* pItem) { - switch (m_Operator) { - case Tokens::PLUS: return m_Left->Evaluate(pItem) + m_Right->Evaluate(pItem); - case Tokens::MINUS: return m_Left->Evaluate(pItem) - m_Right->Evaluate(pItem); - case Tokens::MULTIPLY: return m_Left->Evaluate(pItem) * m_Right->Evaluate(pItem); - case Tokens::DIVIDE: return m_Left->Evaluate(pItem) / m_Right->Evaluate(pItem); - default: - throw L"Error"; - } - } - void SetVariables(std::map variables) { - m_Left->SetVariables(variables); - m_Right->SetVariables(variables); - }; + Binary(Expression* left = nullptr, Expression* right = nullptr, Token op = Token::NONE) : m_Left(left), m_Right(right), m_Operator(op) {}; + int32_t Evaluate(Unit* pItem) override; + std::wstring ToString(Unit* pItem) override; + void SetVariables(std::unordered_map& variables) override; }; -template -class In : public Expr { +class In : public Expression { protected: - Tokens m_Operator; - Expr* m_Left; - Expr* m_Min; - Expr* m_Max; + Token m_Operator; + Expression* m_Left; + Expression* m_Min; + Expression* m_Max; public: - In(Expr* left = nullptr, Expr* min = nullptr, Expr* max = nullptr, Tokens op = Tokens::NONE) : m_Left(left), m_Min(min), m_Max(max), m_Operator(op) {}; - std::wstring ToString() { - return m_Left->ToString() + Expr::GetOperation(m_Operator) + m_Min->ToString() + L"-" + m_Max->ToString(); - } - T Evaluate(Unit* pItem) { - T left = m_Left->Evaluate(pItem); - return left >= m_Min->Evaluate(pItem) - && left <= m_Max->Evaluate(pItem); - } - void SetVariables(std::map variables) { - m_Left->SetVariables(variables); - m_Min->SetVariables(variables); - m_Max->SetVariables(variables); - }; + In(Expression* left = nullptr, Expression* min = nullptr, Expression* max = nullptr, Token op = Token::NONE) : m_Left(left), m_Min(min), m_Max(max), m_Operator(op) {}; + int32_t Evaluate(Unit* pItem) override; + std::wstring ToString(Unit* pItem) override; + void SetVariables(std::unordered_map& variables) override; }; -template -class Unary : public Expr { +class Unary : public Expression { protected: - Tokens m_Operator; - Expr* m_Right; + Token m_Operator; + Expression* m_Right; public: - Unary(Expr* right = nullptr, Tokens op = Tokens::NONE) : m_Right(right), m_Operator(op) {}; - std::wstring ToString() { - return Expr::GetOperation(m_Operator) + m_Right->ToString(); - } - T Evaluate(Unit* pItem) { - switch (m_Operator) { - case Tokens::BANG: return !m_Right->Evaluate(pItem); - case Tokens::MINUS: return -1 * m_Right->Evaluate(pItem); - default: - throw L"Error"; - } - } - void SetVariables(std::map variables) { - m_Right->SetVariables(variables); - }; + Unary(Expression* right = nullptr, Token op = Token::NONE) : m_Right(right), m_Operator(op) {}; + int32_t Evaluate(Unit* pItem) override; + std::wstring ToString(Unit* pItem) override; + void SetVariables(std::unordered_map& variables) override; }; -template -class Literal : public Expr { +class Literal : public Expression { protected: - T m_Value; + int32_t m_Value; public: - Literal(std::wstring value) { - m_Value = stol(value, nullptr, 10); - }; - T Evaluate(Unit* pItem) { - return m_Value; - }; - std::wstring ToString() { - return std::to_wstring(m_Value); - }; - void SetVariables(std::map variables) {}; + Literal(std::wstring value) { m_Value = stol(value); }; + int32_t Evaluate(Unit* pItem) override; + std::wstring ToString(Unit* pItem) override; + void SetVariables(std::unordered_map& variables) override; }; -template -class Boolean : public Expr { +class Boolean : public Expression { protected: - T m_Value; + int32_t m_Value; public: - Boolean(T value) : m_Value(value) {}; - T Evaluate(Unit* pItem) { - return m_Value; - }; - std::wstring ToString() { - return std::to_wstring(m_Value); - }; - void SetVariables(std::map variables) {}; + Boolean(int32_t value) { m_Value = value; }; + int32_t Evaluate(Unit* pItem) override; + std::wstring ToString(Unit* pItem) override; + void SetVariables(std::unordered_map& variables) override; }; -template -class Var : public Expr { +typedef int32_t(*GlobalVariableFunction)(Unit* pUnit); + +class Variable : public Expression { protected: - std::wstring m_Var; - T* m_Value = nullptr; + std::wstring m_Variable; + std::optional m_Value; + GlobalVariableFunction m_Func; public: - Var(std::wstring value) : m_Var(value) {}; - ~Var() { - delete m_Value; - } - T Evaluate(Unit* pItem) { - if (m_Value == nullptr) { - return 0; - } - return *m_Value; - } - void SetValue(const T v) { - if (m_Value == nullptr) { - m_Value = reinterpret_cast(malloc(sizeof(T))); - } - memcpy(m_Value, &v, sizeof(T)); - } - void SetVariables(std::map variables) { - if (variables.contains(m_Var)) { - SetValue(variables[m_Var]); - } - } - std::wstring ToString() { - if (m_Value == nullptr) { - return m_Var + L"(null)"; - } - return m_Var + L"(" + std::to_wstring(*m_Value) + L")"; - } + Variable(std::wstring variable = L""); + int32_t Evaluate(Unit* pItem) override; + std::wstring ToString(Unit* pItem) override; + void SetValue(const int32_t v); + void SetVariables(std::unordered_map& variables) override; }; -template -class Call : public Expr { +class Call : public Expression { protected: - Tokens m_Func; - std::vector*> m_Args; -public: - Call(Tokens func, std::vector*> args) : m_Func(func), m_Args(args) {}; - T Evaluate(Unit* pItem) { - switch (m_Func) { - case Tokens::STAT: { - int nArgs = m_Args.size(); - if (nArgs == 0 - || nArgs > 2) { - //throw invalid # args error? - return 0; - } + Token m_Func; + std::vector m_Args; - Stat stat = static_cast(m_Args[0]->Evaluate(pItem)); - uint16_t nLayer = 0; - if (nArgs == 2) { - nLayer = m_Args[1]->Evaluate(pItem); - } - T result = D2COMMON_STATLIST_GetUnitStatUnsigned(pItem, stat, nLayer); - if (stat <= Stat::MAXSTAMINA && stat >= Stat::MAXHP) { - result >>= 8; - } - return result; - } - case Tokens::CLASS: { - if (m_Args.size() != 1) { - return 0; - } - ItemType itemType = static_cast(m_Args[0]->Evaluate(pItem)); - return D2COMMON_ITEMS_CheckItemTypeId(pItem, itemType); - } - case Tokens::MAX: - case Tokens::MININ: - case Tokens::MIN: { - std::vector values; - std::transform(m_Args.begin(), m_Args.end(), std::back_inserter(values), - [pItem](Expr* arg) -> T { return arg->Evaluate(pItem); }); - T v = values[0]; - bool includeZeros = m_Func == Tokens::MININ; - bool isMax = m_Func == Tokens::MAX; - for (auto& value : values) { - //exclude zeros. stat doesnt exist on item (i think) - if (value == 0 && !includeZeros) { - continue; - } - if (isMax && value > v) { - v = value; - } else if (value < v) { - v = value; - } - } - return v; - } - default: - throw L"Unknown function."; - //throw error? - } - } - void SetVariables(std::map variables) { - for (auto& expression : m_Args) { - expression->SetVariables(variables); - } - } - std::wstring ToString() { - std::wostringstream os; - for (auto& expression : m_Args) { - if (&expression != &m_Args.front()) { - os << ", "; - } - os << expression->ToString(); - } - return Expr::GetOperation(m_Func) + L"(" + os.str() + L")"; - } + int32_t EvaluateClass(Unit* pItem, std::vector& args); + int32_t EvaluateChargedSkill(Unit* pItem, Stat stat, std::vector& args); + int32_t EvaluateStat(Unit* pItem, Stat stat, std::vector& args); +public: + Call(Token func, std::vector args) : m_Func(func), m_Args(args) {}; + int32_t Evaluate(Unit* pItem) override; + std::wstring ToString(Unit* pItem) override; + void SetVariables(std::unordered_map& variables) override; }; -template -class ListExpr : public Expr { +class ListExpression : public Expression { protected: - std::vector*> m_List; + std::vector m_List; public: - T Evaluate(Unit* pItem) { - return std::any_of(m_List.begin(), m_List.end(), [pItem](Expr* expression) { return expression->Evaluate(pItem); }); - } - void SetVariables(std::map variables) { - for (auto& expression : m_List) { - expression->SetVariables(variables); - } - } - void Push(Expr* expression) { m_List.push_back(expression); } - std::wstring ToString() { - std::wostringstream os; - for (auto& expression : m_List) { - if (&expression != &m_List.front()) { - os << " or "; - } - os << expression->ToString(); - } - return os.str(); - } + void Push(Expression* expression) { m_List.push_back(expression); } + int32_t Evaluate(Unit* pItem) override; + std::wstring ToString(Unit* pItem) override; + void SetVariables(std::unordered_map& variables) override; }; - -class Token { +class TokenizerToken { protected: - Tokens m_TokenType; + Token m_TokenType; std::wstring m_Value; public: - Token(Tokens type = Tokens::NONE, std::wstring value = L"") : m_TokenType(type), m_Value(value) {}; - Tokens GetTokenType() { return m_TokenType; } + TokenizerToken(Token type = Token::NONE, std::wstring value = L"") : m_TokenType(type), m_Value(value) {}; + Token GetTokenType() { return m_TokenType; } std::wstring GetValue() { return m_Value; } }; - //https://github.com/munificent/craftinginterpreters/blob/master/java/com/craftinginterpreters/lox/Scanner.java class Tokenizer { protected: - std::vector m_Tokens; - const wchar_t* m_Expression; int m_Current = 0; - - bool iswnull(wint_t c) { - return c == L'\0'; - } - - static bool isoperator(const wchar_t*& expression) { - return _wcsnicmp(expression, L" and ", 5) == 0 - || _wcsnicmp(expression, L"true", 4) == 0 - || _wcsnicmp(expression, L"false", 5) == 0 - || _wcsnicmp(expression, L" or ", 4) == 0 - || _wcsnicmp(expression, L"|", 1) == 0 - || _wcsnicmp(expression, L"&", 1) == 0 - || wcsncmp(expression, L",", 1) == 0 - || wcsncmp(expression, L"=", 1) == 0 - || wcsncmp(expression, L"<", 1) == 0 - || wcsncmp(expression, L">", 1) == 0 - || wcsncmp(expression, L"!", 1) == 0 - || wcsncmp(expression, L"(", 1) == 0 - || wcsncmp(expression, L")", 1) == 0 - || wcsncmp(expression, L"+", 1) == 0 - || wcsncmp(expression, L"-", 1) == 0 - || wcsncmp(expression, L"*", 1) == 0 - || wcsncmp(expression, L"/", 1) == 0; - } - - std::wstring parsevariable(const wchar_t*& expression) { - int s = 0; - while (!isoperator(expression) && !iswnull(*expression)) { - expression += 1; - s += 1; - } - return std::wstring(const_cast(expression - s), s); - } - - std::wstring parsequotedvariable(const wchar_t*& expression) { - int s = 1; - expression += 1; - while (*expression != L'"' && !iswnull(*expression)) { - expression += 1; - s += 1; - } - if (*expression != L'"') { - //throw error. unquoted string variable - } - expression += 1; - return std::wstring(const_cast(expression - s), s - 1); - } - - std::wstring parsedigit(const wchar_t*& expression) { - int s = 0; - while (std::iswdigit(*expression) && !iswnull(*expression)) { - expression += 1; - s += 1; - } - return std::wstring(const_cast(expression - s), s); - } - - void tokenize(const wchar_t*& expression) { - while (!iswnull(*expression)) { - if (*expression == L'(') { expression++; m_Tokens.push_back(new Token(Tokens::LEFT_PAREN)); } - else if (*expression == L')') { expression++; m_Tokens.push_back(new Token(Tokens::RIGHT_PAREN)); } - else if (_wcsnicmp(expression, L"class(", 6) == 0) { expression += 5; m_Tokens.push_back(new Token(Tokens::CLASS)); } - else if (_wcsnicmp(expression, L"minin(", 6) == 0) { expression += 5; m_Tokens.push_back(new Token(Tokens::MININ)); } - else if (_wcsnicmp(expression, L"stat(", 5) == 0) { expression += 4; m_Tokens.push_back(new Token(Tokens::STAT)); } - else if (_wcsnicmp(expression, L"false", 5) == 0) { expression += 5; m_Tokens.push_back(new Token(Tokens::_FALSE)); } - else if (_wcsnicmp(expression, L"true", 4) == 0) { expression += 4; m_Tokens.push_back(new Token(Tokens::_TRUE)); } - else if (_wcsnicmp(expression, L"min(", 4) == 0) { expression += 3; m_Tokens.push_back(new Token(Tokens::MIN)); } - else if (_wcsnicmp(expression, L"max(", 4) == 0) { expression += 3; m_Tokens.push_back(new Token(Tokens::MAX)); } - else if (_wcsnicmp(expression, L"and ", 4) == 0) { expression += 3; m_Tokens.push_back(new Token(Tokens::AND)); } - else if (_wcsnicmp(expression, L"or ", 3) == 0) { expression += 2; m_Tokens.push_back(new Token(Tokens::OR)); } - else if (wcsncmp(expression, L"&&", 2) == 0) { expression += 2; m_Tokens.push_back(new Token(Tokens::AND)); } - else if (wcsncmp(expression, L"||", 2) == 0) { expression += 2; m_Tokens.push_back(new Token(Tokens::OR)); } - else if (_wcsnicmp(expression, L"in ", 3) == 0) { expression += 2; m_Tokens.push_back(new Token(Tokens::_IN)); } - else if (wcsncmp(expression, L"!=", 2) == 0) { expression += 2; m_Tokens.push_back(new Token(Tokens::BANG_EQUALS)); } - else if (wcsncmp(expression, L"==", 2) == 0) { expression += 2; m_Tokens.push_back(new Token(Tokens::EQUALS)); } - else if (wcsncmp(expression, L">=", 2) == 0) { expression += 2; m_Tokens.push_back(new Token(Tokens::GREATER_THAN_EQUALS)); } - else if (wcsncmp(expression, L"<=", 2) == 0) { expression += 2; m_Tokens.push_back(new Token(Tokens::LESS_THAN_EQUALS)); } - else if (*expression == L'>') { expression++; m_Tokens.push_back(new Token(Tokens::GREATER_THAN)); } - else if (*expression == L'<') { expression++; m_Tokens.push_back(new Token(Tokens::LESS_THAN)); } - else if (*expression == L'!') { expression++; m_Tokens.push_back(new Token(Tokens::BANG)); } - else if (*expression == L'=') { expression++; m_Tokens.push_back(new Token(Tokens::EQUALS)); } - else if (*expression == L',') { expression++; m_Tokens.push_back(new Token(Tokens::COMMA)); } - else if (*expression == L'-') { expression++; m_Tokens.push_back(new Token(Tokens::MINUS)); } - else if (*expression == L'+') { expression++; m_Tokens.push_back(new Token(Tokens::PLUS)); } - else if (*expression == L'*') { expression++; m_Tokens.push_back(new Token(Tokens::MULTIPLY)); } - else if (*expression == L'/') { expression++; m_Tokens.push_back(new Token(Tokens::DIVIDE)); } - else if (std::iswdigit(*expression)) { m_Tokens.push_back(new Token(Tokens::NUMBER, parsedigit(expression))); } - else if (*expression == L'"') { m_Tokens.push_back(new Token(Tokens::VARIABLE, parsequotedvariable(expression))); } - else if (std::iswalpha(*expression)) { m_Tokens.push_back(new Token(Tokens::VARIABLE, parsevariable(expression))); } - else if (std::iswspace(*expression)) { expression++; } - else { - //error? - } - } - m_Tokens.push_back(new Token(Tokens::END)); - }; + std::vector m_Tokens; + + bool IsNull(wchar_t c); + bool IsOperator(const wchar_t*& expression); + std::wstring ParseVariable(const wchar_t*& expression); + std::wstring ParseQuotedVariable(const wchar_t*& expression); + std::wstring ParseDigit(const wchar_t*& expression); + void Tokenize(const wchar_t*& expression); public: - Tokenizer(const wchar_t* expression) : m_Expression(expression) { - tokenize(expression); + Tokenizer(const wchar_t* expression) { + Tokenize(expression); }; - bool match(Tokens t) { - if (m_Tokens[m_Current]->GetTokenType() == t) { - m_Current++; - return true; - } - return false; - } - - bool check(Tokens t) { - return peek() == t; - } - Token* previous() { - if (m_Current <= 0) { - return nullptr; - } - return m_Tokens[m_Current - 1]; - } - - Tokens peek() { - return m_Tokens[m_Current]->GetTokenType(); - } - - void reset() { - m_Current = 0; - } - - void push_front(Token* t) { - m_Tokens.insert(m_Tokens.begin(), t); - } + std::vector GetTokens() { return m_Tokens; } + bool Match(Token t); + bool Check(Token t); + TokenizerToken* Previous(); + TokenizerToken* Peek(); + void Reset(); + void Insert(TokenizerToken* t); }; -template +//https://en.wikipedia.org/wiki/Recursive_descent_parser class Parser { protected: - - static Expr* _finishCall(Tokens call, Tokenizer* t) { - std::vector*> arguments; - while (!t->match(Tokens::RIGHT_PAREN)) { - do { - arguments.push_back(_expression(t)); - } while (t->match(Tokens::COMMA)); - } - return new Call(call, arguments); - } - - static Expr* _primary(Tokenizer* t, Expr* lhs = nullptr) { - if (lhs != nullptr) { return lhs; } - if (t->match(Tokens::VARIABLE)) return new Var(t->previous()->GetValue()); - if (t->match(Tokens::NUMBER)) return new Literal(t->previous()->GetValue()); - if (t->match(Tokens::_TRUE)) return new Boolean(1); - if (t->match(Tokens::_FALSE)) return new Boolean(0); - if (t->match(Tokens::LEFT_PAREN)) { - Expr* expr = _expression(t); - if (t->match(Tokens::RIGHT_PAREN)) { - return expr; - } - } - if(t->match(Tokens::STAT) - || t->match(Tokens::CLASS) - || t->match(Tokens::MIN) - || t->match(Tokens::MININ) - || t->match(Tokens::MAX)) { - Tokens call = t->previous()->GetTokenType(); - if (t->match(Tokens::LEFT_PAREN)) { - return _finishCall(call, t); - } - } - //error - DEBUG_LOG(L"Error"); - throw L"Error"; - } - static Expr* _unary(Tokenizer* t, Expr* lhs = nullptr) { - if (t->match(Tokens::BANG) || t->match(Tokens::MINUS)) { - Token* op = t->previous(); - Expr* rhs = _unary(t); - return new Unary(rhs, op->GetTokenType()); - } - return _primary(t, lhs); - } - static Expr* _factor(Tokenizer* t, Expr* lhs = nullptr) { - Expr* expr = _unary(t, lhs); - while (t->match(Tokens::MULTIPLY) || t->match(Tokens::DIVIDE)) { - Token* op = t->previous(); - Expr* rhs = _unary(t); - expr = new Binary(expr, rhs, op->GetTokenType()); - } - return expr; - } - static Expr* _term(Tokenizer* t, Expr* lhs = nullptr) { - Expr* expr = _factor(t, lhs); - while (t->match(Tokens::PLUS) || t->match(Tokens::MINUS)) { - Token* op = t->previous(); - Expr* rhs = _factor(t); - expr = new Binary(expr, rhs, op->GetTokenType()); - } - return expr; - } - - static Expr* _comparison(Tokenizer* t, Expr* lhs = nullptr) { - Expr* expr = _term(t, lhs); - while (t->match(Tokens::GREATER_THAN) || t->match(Tokens::GREATER_THAN_EQUALS) - || t->match(Tokens::LESS_THAN) || t->match(Tokens::LESS_THAN_EQUALS) - || t->match(Tokens::_IN)) { - Token* op = t->previous(); - if (op->GetTokenType() == Tokens::_IN) { - Expr* min = _primary(t); - if (t->match(Tokens::MINUS)) { - Expr* max = _primary(t); - expr = new In(expr, min, max, op->GetTokenType()); - } else { - throw L"Error"; - } - } else { - Expr* rhs = _term(t); - expr = new Logical(expr, rhs, op->GetTokenType()); - } - } - return expr; - } - static Expr* _equality(Tokenizer* t, Expr* lhs = nullptr) { - Expr* expr = _comparison(t, lhs); - while (t->match(Tokens::EQUALS) || t->match(Tokens::BANG_EQUALS)) { - Token* op = t->previous(); - Expr* rhs = _comparison(t); - expr = new Logical(expr, rhs, op->GetTokenType()); - } - return expr; - } - static Expr* _and(Tokenizer * t, Expr* lhs = nullptr) { - Expr* expr = _equality(t, lhs); - while (t->match(Tokens::AND)) { - Token* op = t->previous(); - Expr* rhs = _equality(t); - expr = new Logical(expr, rhs, op->GetTokenType()); - } - return expr; - } - static Expr* _or(Tokenizer* t, Expr* lhs = nullptr) { - Expr* expr = _and(t, lhs); - while (t->match(Tokens::OR)) { - Token* op = t->previous(); - Expr* rhs = _and(t); - expr = new Logical(expr, rhs, op->GetTokenType()); - } - return expr; - } - static Expr* _expression(Tokenizer* t, Expr* lhs = nullptr) { - return _or(t, lhs); - } + static Expression* _finishCall(Token call, Tokenizer* t); + static Expression* _primary(Tokenizer* t, Expression* lhs = nullptr); + static Expression* _unary(Tokenizer* t, Expression* lhs = nullptr); + static Expression* _factor(Tokenizer* t, Expression* lhs = nullptr); + static Expression* _term(Tokenizer* t, Expression* lhs = nullptr); + static Expression* _comparison(Tokenizer* t, Expression* lhs = nullptr); + static Expression* _equality(Tokenizer* t, Expression* lhs = nullptr); + static Expression* _and(Tokenizer* t, Expression* lhs = nullptr); + static Expression* _or(Tokenizer* t, Expression* lhs = nullptr); + static Expression* _expression(Tokenizer* t, Expression* lhs = nullptr); public: - //https://en.wikipedia.org/wiki/Recursive_descent_parser - static Expr* Parse(const wchar_t* expr) { - Tokenizer* tokenizer = new Tokenizer(expr); - return _expression(tokenizer); - } - - static ListExpr* Parse(Var* lhs, const wchar_t* expressions) { - ListExpr* list = new ListExpr(); - for (auto expression : split(expressions, L",")) { - Tokenizer* tokenizer = new Tokenizer(expression.c_str()); - if (tokenizer->match(Tokens::_IN) - || tokenizer->match(Tokens::EQUALS) || tokenizer->match(Tokens::BANG_EQUALS) - || tokenizer->match(Tokens::LESS_THAN) || tokenizer->match(Tokens::LESS_THAN_EQUALS) - || tokenizer->match(Tokens::GREATER_THAN) || tokenizer->match(Tokens::GREATER_THAN_EQUALS)) { - } else { - tokenizer->push_front(new Token(Tokens::EQUALS)); - } - tokenizer->reset(); - list->Push(_expression(tokenizer, lhs)); - } - return list; - }; - - static ListExpr* ParseCall(const wchar_t* func, const wchar_t* expressions) { - ListExpr* list = new ListExpr(); - for (auto expression : split(expressions, L",")) { - auto call = fmt::format(L"{}({})", func, expression); - Tokenizer* tokenizer = new Tokenizer(call.c_str()); - list->Push(_expression(tokenizer)); - } - return list; - } + static Expression* Parse(const wchar_t* expression); + static ListExpression* Parse(Variable* lhs, const wchar_t* expression); + static Expression* ParseCall(Token func, const wchar_t* args); }; diff --git a/Globals.cpp b/Globals.cpp index 452e26d..e41d964 100644 --- a/Globals.cpp +++ b/Globals.cpp @@ -1,152 +1,541 @@ #include "Globals.h" -#include -#include #include "Action.h" -GameVersion GAME_VERSION = GameVersion::NONE; +D2Version GameVersion = D2Version::NONE; +int32_t FilterLevel = 6; +int32_t PingLevel = 6; +bool IsFilterDebug = false; +bool IsTxtDataLoaded = false; -bool TXT_DATA_LOADED = false; - -std::map> STYLES; -std::map ITEM_NAME_LOOKUP_TABLE; -std::map ITEM_CODE_LOOKUP_TABLE; -std::map RUNE_NAME_LOOKUP_TABLE; -std::map SKILL_LOOKUP_TABLE; - -std::map ITEM_TYPE_LOOKUP_TABLE = { -{ L"None", static_cast>(ItemType::NONE_1) }, -{ L"None", static_cast>(ItemType::NONE_2) }, -{ L"Shield", static_cast>(ItemType::SHIELD) }, -{ L"Armor", static_cast>(ItemType::ARMOR) }, -{ L"Gold", static_cast>(ItemType::GOLD) }, -{ L"Bow Quiver", static_cast>(ItemType::BOW_QUIVER) }, -{ L"Crossbow Quiver", static_cast>(ItemType::CROSSBOW_QUIVER) }, -{ L"Player Body Part", static_cast>(ItemType::PLAYER_BODY_PART) }, -{ L"Herb", static_cast>(ItemType::HERB) }, -{ L"Potion", static_cast>(ItemType::POTION) }, -{ L"Ring", static_cast>(ItemType::RING) }, -{ L"Elixir", static_cast>(ItemType::ELIXIR) }, -{ L"Amulet", static_cast>(ItemType::AMULET) }, -{ L"Charm", static_cast>(ItemType::CHARM) }, -{ L"Not Used", static_cast>(ItemType::NONE_3) }, -{ L"Boots", static_cast>(ItemType::BOOTS) }, -{ L"Gloves", static_cast>(ItemType::GLOVES) }, -{ L"Not Used", static_cast>(ItemType::NONE_4) }, -{ L"Book", static_cast>(ItemType::BOOK) }, -{ L"Belt", static_cast>(ItemType::BELT) }, -{ L"Gem", static_cast>(ItemType::GEM) }, -{ L"Torch", static_cast>(ItemType::TORCH) }, -{ L"Scroll", static_cast>(ItemType::SCROLL) }, -{ L"Not Used", static_cast>(ItemType::NONE_5) }, -{ L"Scepter", static_cast>(ItemType::SCEPTER) }, -{ L"Wand", static_cast>(ItemType::WAND) }, -{ L"Staff", static_cast>(ItemType::STAFF) }, -{ L"Bow", static_cast>(ItemType::BOW) }, -{ L"Axe", static_cast>(ItemType::AXE) }, -{ L"Club", static_cast>(ItemType::CLUB) }, -{ L"Sword", static_cast>(ItemType::SWORD) }, -{ L"Hammer", static_cast>(ItemType::HAMMER) }, -{ L"Knife", static_cast>(ItemType::KNIFE) }, -{ L"Spear", static_cast>(ItemType::SPEAR) }, -{ L"Polearm", static_cast>(ItemType::POLEARM) }, -{ L"Crossbow", static_cast>(ItemType::CROSSBOW) }, -{ L"Mace", static_cast>(ItemType::MACE) }, -{ L"Helm", static_cast>(ItemType::HELM) }, -{ L"Missile Potion", static_cast>(ItemType::MISSILE_POTION) }, -{ L"Quest", static_cast>(ItemType::QUEST) }, -{ L"Body Part", static_cast>(ItemType::BODY_PART) }, -{ L"Key", static_cast>(ItemType::KEY) }, -{ L"Throwing Knife", static_cast>(ItemType::THROWING_KNIFE) }, -{ L"Throwing Axe", static_cast>(ItemType::THROWING_AXE) }, -{ L"Javelin", static_cast>(ItemType::JAVELIN) }, -{ L"Weapon", static_cast>(ItemType::WEAPON) }, -{ L"Melee Weapon", static_cast>(ItemType::MELEE_WEAPON) }, -{ L"Missile Weapon", static_cast>(ItemType::MISSILE_WEAPON) }, -{ L"Thrown Weapon", static_cast>(ItemType::THROWN_WEAPON) }, -{ L"Combo Weapon", static_cast>(ItemType::COMBO_WEAPON) }, -{ L"Any Armor", static_cast>(ItemType::ANY_ARMOR) }, -{ L"Any Shield", static_cast>(ItemType::ANY_SHIELD) }, -{ L"Miscellaneous", static_cast>(ItemType::MISCELLANEOUS) }, -{ L"Socket Filler", static_cast>(ItemType::SOCKET_FILLER) }, -{ L"Second Hand", static_cast>(ItemType::SECOND_HAND) }, -{ L"Staves And Rods", static_cast>(ItemType::STAVES_AND_RODS) }, -{ L"Missile", static_cast>(ItemType::MISSILE) }, -{ L"Blunt", static_cast>(ItemType::BLUNT) }, -{ L"Jewel", static_cast>(ItemType::JEWEL) }, -{ L"Class Specific", static_cast>(ItemType::CLASS_SPECIFIC) }, -{ L"Amazon Item", static_cast>(ItemType::AMAZON_ITEM) }, -{ L"Barbarian Item", static_cast>(ItemType::BARBARIAN_ITEM) }, -{ L"Necromancer Item", static_cast>(ItemType::NECROMANCER_ITEM) }, -{ L"Paladin Item", static_cast>(ItemType::PALADIN_ITEM) }, -{ L"Sorceress Item", static_cast>(ItemType::SORCERESS_ITEM) }, -{ L"Assassin Item", static_cast>(ItemType::ASSASSIN_ITEM) }, -{ L"Druid Item", static_cast>(ItemType::DRUID_ITEM) }, -{ L"Hand to Hand", static_cast>(ItemType::HAND_TO_HAND) }, -{ L"Orb", static_cast>(ItemType::ORB) }, -{ L"Voodoo Heads", static_cast>(ItemType::VOODOO_HEADS) }, -{ L"Auric Shields", static_cast>(ItemType::AURIC_SHIELDS) }, -{ L"Primal Helm", static_cast>(ItemType::PRIMAL_HELM) }, -{ L"Pelt", static_cast>(ItemType::PELT) }, -{ L"Cloak", static_cast>(ItemType::CLOAK) }, -{ L"Rune", static_cast>(ItemType::RUNE) }, -{ L"Circlet", static_cast>(ItemType::CIRCLET) }, -{ L"Healing Potion", static_cast>(ItemType::HEALING_POTION) }, -{ L"Mana Potion", static_cast>(ItemType::MANA_POTION) }, -{ L"Rejuv Potion", static_cast>(ItemType::REJUV_POTION) }, -{ L"Stamina Potion", static_cast>(ItemType::STAMINA_POTION) }, -{ L"Antidote Potion", static_cast>(ItemType::ANTIDOTE_POTION) }, -{ L"Thawing Potion", static_cast>(ItemType::THAWING_POTION) }, -{ L"Small Charm", static_cast>(ItemType::SMALL_CHARM) }, -{ L"Medium Charm", static_cast>(ItemType::MEDIUM_CHARM) }, -{ L"Large Charm", static_cast>(ItemType::LARGE_CHARM) }, -{ L"Amazon Bow", static_cast>(ItemType::AMAZON_BOW) }, -{ L"Amazon Spear", static_cast>(ItemType::AMAZON_SPEAR) }, -{ L"Amazon Javelin", static_cast>(ItemType::AMAZON_JAVELIN) }, -{ L"Hand to Hand 2", static_cast>(ItemType::HAND_TO_HAND_2) }, -{ L"Magic Bow Quiv", static_cast>(ItemType::MAGIC_BOW_QUIV) }, -{ L"Magic Xbow Quiv", static_cast>(ItemType::UNK) }, -{ L"Chipped Gem", static_cast>(ItemType::CHIPPED_GEM) }, -{ L"Flawed Gem", static_cast>(ItemType::FLAWED_GEM) }, -{ L"Standard Gem", static_cast>(ItemType::STANDARD_GEM) }, -{ L"Flawless Gem", static_cast>(ItemType::FLAWLESS_GEM) }, -{ L"Perfect Gem", static_cast>(ItemType::PERFECT_GEM) }, -{ L"Amethyst", static_cast>(ItemType::AMETHYST) }, -{ L"Diamond", static_cast>(ItemType::DIAMOND) }, -{ L"Emerald", static_cast>(ItemType::EMERALD) }, -{ L"Ruby", static_cast>(ItemType::RUBY) }, -{ L"Sapphire", static_cast>(ItemType::SAPPHIRE) }, -{ L"Topaz", static_cast>(ItemType::TOPAZ) }, -{ L"Skull", static_cast>(ItemType::SKULL) }, +//item type linker object. +#define ITEM_TYPE(STR, ENUM) { L#STR, static_cast>(ItemType::##ENUM##) } +std::unordered_map ItemTypes = { + ITEM_TYPE(None, NONE_1), + ITEM_TYPE(None, NONE_2), + ITEM_TYPE(Shield, SHIELD), + ITEM_TYPE(Armor, ARMOR), + ITEM_TYPE(Gold, GOLD), + ITEM_TYPE(Bow Quiver, BOW_QUIVER), + ITEM_TYPE(Crossbow Quiver, CROSSBOW_QUIVER), + ITEM_TYPE(Player Body Part, PLAYER_BODY_PART), + ITEM_TYPE(Herb, HERB), + ITEM_TYPE(Potion, POTION), + ITEM_TYPE(Ring, RING), + ITEM_TYPE(Elixir, ELIXIR), + ITEM_TYPE(Amulet, AMULET), + ITEM_TYPE(Charm, CHARM), + ITEM_TYPE(Not Used, NONE_3), + ITEM_TYPE(Boots, BOOTS), + ITEM_TYPE(Gloves, GLOVES), + ITEM_TYPE(Not Used, NONE_4), + ITEM_TYPE(Book, BOOK), + ITEM_TYPE(Belt, BELT), + ITEM_TYPE(Gem, GEM), + ITEM_TYPE(Torch, TORCH), + ITEM_TYPE(Scroll, SCROLL), + ITEM_TYPE(Not Used, NONE_5), + ITEM_TYPE(Scepter, SCEPTER), + ITEM_TYPE(Wand, WAND), + ITEM_TYPE(Staff, STAFF), + ITEM_TYPE(Bow, BOW), + ITEM_TYPE(Axe, AXE), + ITEM_TYPE(Club, CLUB), + ITEM_TYPE(Sword, SWORD), + ITEM_TYPE(Hammer, HAMMER), + ITEM_TYPE(Knife, KNIFE), + ITEM_TYPE(Spear, SPEAR), + ITEM_TYPE(Polearm, POLEARM), + ITEM_TYPE(Crossbow, CROSSBOW), + ITEM_TYPE(Mace, MACE), + ITEM_TYPE(Helm, HELM), + ITEM_TYPE(Missile Potion, MISSILE_POTION), + ITEM_TYPE(Quest, QUEST), + ITEM_TYPE(Body Part, BODY_PART), + ITEM_TYPE(Key, KEY), + ITEM_TYPE(Throwing Knife, THROWING_KNIFE), + ITEM_TYPE(Throwing Axe, THROWING_AXE), + ITEM_TYPE(Javelin, JAVELIN), + ITEM_TYPE(Weapon, WEAPON), + ITEM_TYPE(Melee Weapon, MELEE_WEAPON), + ITEM_TYPE(Missile Weapon, MISSILE_WEAPON), + ITEM_TYPE(Thrown Weapon, THROWN_WEAPON), + ITEM_TYPE(Combo Weapon, COMBO_WEAPON), + ITEM_TYPE(Any Armor, ANY_ARMOR), + ITEM_TYPE(Any Shield, ANY_SHIELD), + ITEM_TYPE(Miscellaneous, MISCELLANEOUS), + ITEM_TYPE(Socket Filler, SOCKET_FILLER), + ITEM_TYPE(Second Hand, SECOND_HAND), + ITEM_TYPE(Staves And Rods, STAVES_AND_RODS), + ITEM_TYPE(Missile, MISSILE), + ITEM_TYPE(Blunt, BLUNT), + ITEM_TYPE(Jewel, JEWEL), + ITEM_TYPE(Class Specific, CLASS_SPECIFIC), + ITEM_TYPE(Amazon Item, AMAZON_ITEM), + ITEM_TYPE(Barbarian Item, BARBARIAN_ITEM), + ITEM_TYPE(Necromancer Item, NECROMANCER_ITEM), + ITEM_TYPE(Paladin Item, PALADIN_ITEM), + ITEM_TYPE(Sorceress Item, SORCERESS_ITEM), + ITEM_TYPE(Assassin Item, ASSASSIN_ITEM), + ITEM_TYPE(Druid Item, DRUID_ITEM), + ITEM_TYPE(Hand to Hand, HAND_TO_HAND), + ITEM_TYPE(Orb, ORB), + ITEM_TYPE(Voodoo Heads, VOODOO_HEADS), + ITEM_TYPE(Auric Shields, AURIC_SHIELDS), + ITEM_TYPE(Primal Helm, PRIMAL_HELM), + ITEM_TYPE(Pelt, PELT), + ITEM_TYPE(Cloak, CLOAK), + ITEM_TYPE(Rune, RUNE), + ITEM_TYPE(Circlet, CIRCLET), + ITEM_TYPE(Healing Potion, HEALING_POTION), + ITEM_TYPE(Mana Potion, MANA_POTION), + ITEM_TYPE(Rejuv Potion, REJUV_POTION), + ITEM_TYPE(Stamina Potion, STAMINA_POTION), + ITEM_TYPE(Antidote Potion, ANTIDOTE_POTION), + ITEM_TYPE(Thawing Potion, THAWING_POTION), + ITEM_TYPE(Small Charm, SMALL_CHARM), + ITEM_TYPE(Medium Charm, MEDIUM_CHARM), + ITEM_TYPE(Large Charm, LARGE_CHARM), + ITEM_TYPE(Amazon Bow, AMAZON_BOW), + ITEM_TYPE(Amazon Spear, AMAZON_SPEAR), + ITEM_TYPE(Amazon Javelin, AMAZON_JAVELIN), + ITEM_TYPE(Hand to Hand 2, HAND_TO_HAND_2), + ITEM_TYPE(Magic Bow Quiv, MAGIC_BOW_QUIV), + ITEM_TYPE(Magic Xbow Quiv, UNK), + ITEM_TYPE(Chipped Gem, CHIPPED_GEM), + ITEM_TYPE(Flawed Gem, FLAWED_GEM), + ITEM_TYPE(Standard Gem, STANDARD_GEM), + ITEM_TYPE(Flawless Gem, FLAWLESS_GEM), + ITEM_TYPE(Perfect Gem, PERFECT_GEM), + ITEM_TYPE(Amethyst, AMETHYST), + ITEM_TYPE(Diamond, DIAMOND), + ITEM_TYPE(Emerald, EMERALD), + ITEM_TYPE(Ruby, RUBY), + ITEM_TYPE(Sapphire, SAPPHIRE), + ITEM_TYPE(Topaz, TOPAZ), + ITEM_TYPE(Skull, SKULL), }; +#undef ITEM_TYPE -//pretty name for stats... -std::map STAT_LOOKUP_TABLE = { - { L"\"Defense\"", fmt::format(L"Stat({})", Stat::ARMORCLASS) }, - { L"\"Enhanced Damage\"", fmt::format(L"Stat({})", Stat::ITEM_MAXDAMAGE_PERCENT) }, - { L"\"Enhanced Defense\"", fmt::format(L"Stat({})", Stat::ITEM_ARMOR_PERCENT) }, - { L"\"Durability\"", fmt::format(L"Stat({})", Stat::ITEM_MAXDURABILITY_PERCENT) }, - { L"\"Fire Resist\"", fmt::format(L"Stat({})", Stat::FIRERESIST) }, - { L"\"Cold Resist\"", fmt::format(L"Stat({})", Stat::COLDRESIST) }, - { L"\"Lightning Resist\"", fmt::format(L"Stat({})", Stat::LIGHTRESIST) }, - { L"\"Poison Resist\"", fmt::format(L"Stat({})", Stat::POISONRESIST) }, - { L"\"Increased Attack Speed\"", fmt::format(L"Stat({})", Stat::ITEM_FASTERATTACKRATE) }, - { L"\"Faster Cast Rate\"", fmt::format(L"Stat({})", Stat::ITEM_FASTERCASTRATE) }, - { L"\"Faster Hit Recovery\"", fmt::format(L"Stat({})", Stat::ITEM_FASTERGETHITRATE) }, - { L"\"Faster Run Walk\"", fmt::format(L"Stat({})", Stat::ITEM_FASTERMOVEVELOCITY) }, - { L"\"Life\"", fmt::format(L"Stat({})", Stat::MAXHP) }, - { L"\"Mana\"", fmt::format(L"Stat({})", Stat::MAXMANA) }, - { L"\"Required Level\"", fmt::format(L"Stat({})", Stat::ITEM_LEVELREQ) }, - { L"\"Magic Find\"", fmt::format(L"Stat({})", Stat::ITEM_MAGICBONUS) }, - { L"\"Gold Find\"", fmt::format(L"Stat({})", Stat::ITEM_GOLDBONUS) }, - { L"\"Strength\"", fmt::format(L"Stat({})", Stat::STRENGTH) }, - { L"\"Dexterity\"", fmt::format(L"Stat({})", Stat::DEXTERITY) }, - { L"\"Vitality\"", fmt::format(L"Stat({})", Stat::VITALITY) }, - { L"\"Energy\"", fmt::format(L"Stat({})", Stat::ENERGY) }, - { L"\"Mana After Each Kill\"", fmt::format(L"Stat({})", Stat::ITEM_MANAAFTERKILL) }, - { L"\"Attack Rating\"", fmt::format(L"Stat({})", Stat::TOHIT) }, - { L"\"Minimum Damage\"", fmt::format(L"Stat({})", Stat::MINDAMAGE) }, - { L"\"Maximum Damage\"", fmt::format(L"Stat({})", Stat::MAXDAMAGE) }, - { L"\"Repairs Durability\"", fmt::format(L"Stat({})", Stat::ITEM_REPLENISH_DURABILITY) }, - { L"\"All Resist\"", fmt::format(L"MinIn(Stat({}),Stat({}),Stat({}),Stat({}))", Stat::FIRERESIST, Stat::COLDRESIST, Stat::LIGHTRESIST, Stat::POISONRESIST) }, - { L"\"Any Resist\"", fmt::format(L"Max(Stat({}),Stat({}),Stat({}),Stat({}))", Stat::FIRERESIST, Stat::COLDRESIST, Stat::LIGHTRESIST, Stat::POISONRESIST) } -}; \ No newline at end of file +//custom strings that can be used for filtering stats +#define STAT(STR, STR2) { L#STR, L#STR2 } +std::unordered_map CustomStats = { + STAT("Defense", Stat(31)), + STAT("Sockets", Stat(194)), + STAT("Enhanced Damage", Stat(17)), + STAT("Enhanced Defense", Stat(16)), + STAT("Durability", Stat(75)), + STAT("Fire Resist", Stat(39)), + STAT("Cold Resist", Stat(43)), + STAT("Lightning Resist", Stat(41)), + STAT("Poison Resist", Stat(45)), + STAT("Increased Attack Speed", Stat(93)), + STAT("Faster Cast Rate", Stat(105)), + STAT("Faster Hit Recovery", Stat(99)), + STAT("Faster Run Walk", Stat(96)), + STAT("Faster Block Rate", Stat(102)), + STAT("Life", Stat(7)), + STAT("Mana", Stat(9)), + STAT("Required Level", Stat(92)), + STAT("Magic Find", Stat(80)), + STAT("Gold Find", Stat(79)), + STAT("Strength", Stat(0)), + STAT("Dexterity", Stat(2)), + STAT("Vitality", Stat(3)), + STAT("Energy", Stat(1)), + STAT("Mana After Each Kill", Stat(138)), + STAT("Attack Rating", Stat(19)), + STAT("Minimum Damage", Stat(21)), + STAT("Maximum Damage", Stat(22)), + STAT("Repairs Durability", Stat(252)), + STAT("All Resist", MinIn(Stat(39),Stat(43),Stat(41),Stat(45))), + STAT("Any Resist", Max(Stat(39),Stat(43),Stat(41),Stat(45))), + STAT("Amazon Skills", Stat(83,0)), + STAT("Amazon +%d to Bow and Crossbow Skills", Stat(188,0)), + STAT("Amazon +%d to Passive and Magic Skills", Stat(188,1)), + STAT("Amazon +%d to Javelin and Spear Skills", Stat(188,2)), + STAT("Sorceress Skills", Stat(83,1)), + STAT("Sorceress +%d to Fire Skills", Stat(188,8)), + STAT("Sorceress +%d to Lightning Skills", Stat(188,9)), + STAT("Sorceress +%d to Cold Skills", Stat(188,10)), + STAT("Necromancer Skills", Stat(83,2)), + STAT("Necromancer +%d to Curses", Stat(188,16)), + STAT("Necromancer +%d to Poison and Bone Skills", Stat(188,17)), + STAT("Necromancer +%d to Summoning Skills", Stat(188,18)), + STAT("Paladin Skills", Stat(83,3)), + STAT("Paladin +%d to Combat Skills", Stat(188,24)), + STAT("Paladin +%d to Offensive Auras", Stat(188,33)), + STAT("Paladin +%d to Defensive Auras", Stat(188,26)), + STAT("Barbarian Skills", Stat(83,4)), + STAT("Barbarian +%d to Combat Skills", Stat(188,32)), + STAT("Barbarian +%d to Masteries", Stat(188,33)), + STAT("Barbarian +%d to Warcries", Stat(188,34)), + STAT("Druid Skills", Stat(83,5)), + STAT("Druid +%d to Summoning Skills", Stat(188,40)), + STAT("Druid +%d to Shape Shifting Skills", Stat(188,41)), + STAT("Druid +%d to Elemental Skills", Stat(188,42)), + STAT("Assassin Skills", Stat(83,6)), + STAT("Assassin +%d to Traps", Stat(188,48)), + STAT("Assassin +%d to Shadow Disciplines", Stat(188,49)), + STAT("Assassin +%d to Martial Arts", Stat(188,50)), + STAT("Attack", Stat(107,0)), + STAT("Kick", Stat(107,1)), + STAT("Throw", Stat(107,2)), + STAT("Unsummon", Stat(107,3)), + STAT("Left Hand Throw", Stat(107,4)), + STAT("Left Hand Swing", Stat(107,5)), + STAT("Magic Arrow", Stat(107,6)), + STAT("Fire Arrow", Stat(107,7)), + STAT("Inner Sight", Stat(107,8)), + STAT("Critical Strike", Stat(107,9)), + STAT("Jab", Stat(107,10)), + STAT("Cold Arrow", Stat(107,11)), + STAT("Multiple Shot", Stat(107,12)), + STAT("Dodge", Stat(107,13)), + STAT("Power Strike", Stat(107,14)), + STAT("Poison Javelin", Stat(107,15)), + STAT("Exploding Arrow", Stat(107,16)), + STAT("Slow Missiles", Stat(107,17)), + STAT("Avoid", Stat(107,18)), + STAT("Impale", Stat(107,19)), + STAT("Lightning Bolt", Stat(107,20)), + STAT("Ice Arrow", Stat(107,21)), + STAT("Guided Arrow", Stat(107,22)), + STAT("Penetrate", Stat(107,23)), + STAT("Charged Strike", Stat(107,24)), + STAT("Plague Javelin", Stat(107,25)), + STAT("Strafe", Stat(107,26)), + STAT("Immolation Arrow", Stat(107,27)), + STAT("Dopplezon", Stat(107,28)), + STAT("Evade", Stat(107,29)), + STAT("Fend", Stat(107,30)), + STAT("Freezing Arrow", Stat(107,31)), + STAT("Valkyrie", Stat(107,32)), + STAT("Pierce", Stat(107,33)), + STAT("Lightning Strike", Stat(107,34)), + STAT("Lightning Fury", Stat(107,35)), + STAT("Fire Bolt", Stat(107,36)), + STAT("Warmth", Stat(107,37)), + STAT("Charged Bolt", Stat(107,38)), + STAT("Ice Bolt", Stat(107,39)), + STAT("Frozen Armor", Stat(107,40)), + STAT("Inferno", Stat(107,41)), + STAT("Static Field", Stat(107,42)), + STAT("Telekinesis", Stat(107,43)), + STAT("Frost Nova", Stat(107,44)), + STAT("Ice Blast", Stat(107,45)), + STAT("Blaze", Stat(107,46)), + STAT("Fire Ball", Stat(107,47)), + STAT("Nova", Stat(107,48)), + STAT("Lightning", Stat(107,49)), + STAT("Shiver Armor", Stat(107,50)), + STAT("Fire Wall", Stat(107,51)), + STAT("Enchant", Stat(107,52)), + STAT("Chain Lightning", Stat(107,53)), + STAT("Teleport", Stat(107,54)), + STAT("Glacial Spike", Stat(107,55)), + STAT("Meteor", Stat(107,56)), + STAT("Thunder Storm", Stat(107,57)), + STAT("Energy Shield", Stat(107,58)), + STAT("Blizzard", Stat(107,59)), + STAT("Chilling Armor", Stat(107,60)), + STAT("Fire Mastery", Stat(107,61)), + STAT("Hydra", Stat(107,62)), + STAT("Lightning Mastery", Stat(107,63)), + STAT("Frozen Orb", Stat(107,64)), + STAT("Cold Mastery", Stat(107,65)), + STAT("Amplify Damage", Stat(107,66)), + STAT("Teeth", Stat(107,67)), + STAT("Bone Armor", Stat(107,68)), + STAT("Skeleton Mastery", Stat(107,69)), + STAT("Raise Skeleton", Stat(107,70)), + STAT("Dim Vision", Stat(107,71)), + STAT("Weaken", Stat(107,72)), + STAT("Poison Dagger", Stat(107,73)), + STAT("Corpse Explosion", Stat(107,74)), + STAT("Clay Golem", Stat(107,75)), + STAT("Iron Maiden", Stat(107,76)), + STAT("Terror", Stat(107,77)), + STAT("Bone Wall", Stat(107,78)), + STAT("Golem Mastery", Stat(107,79)), + STAT("Raise Skeletal Mage", Stat(107,80)), + STAT("Confuse", Stat(107,81)), + STAT("Life Tap", Stat(107,82)), + STAT("Poison Explosion", Stat(107,83)), + STAT("Bone Spear", Stat(107,84)), + STAT("BloodGolem", Stat(107,85)), + STAT("Attract", Stat(107,86)), + STAT("Decrepify", Stat(107,87)), + STAT("Bone Prison", Stat(107,88)), + STAT("Summon Resist", Stat(107,89)), + STAT("IronGolem", Stat(107,90)), + STAT("Lower Resist", Stat(107,91)), + STAT("Poison Nova", Stat(107,92)), + STAT("Bone Spirit", Stat(107,93)), + STAT("FireGolem", Stat(107,94)), + STAT("Revive", Stat(107,95)), + STAT("Sacrifice", Stat(107,96)), + STAT("Smite", Stat(107,97)), + STAT("Might", Stat(107,98)), + STAT("Prayer", Stat(107,99)), + STAT("Resist Fire", Stat(107,100)), + STAT("Holy Bolt", Stat(107,101)), + STAT("Holy Fire", Stat(107,102)), + STAT("Thorns", Stat(107,103)), + STAT("Defiance", Stat(107,104)), + STAT("Resist Cold", Stat(107,105)), + STAT("Zeal", Stat(107,106)), + STAT("Charge", Stat(107,107)), + STAT("Blessed Aim", Stat(107,108)), + STAT("Cleansing", Stat(107,109)), + STAT("Resist Lightning", Stat(107,110)), + STAT("Vengeance", Stat(107,111)), + STAT("Blessed Hammer", Stat(107,112)), + STAT("Concentration", Stat(107,113)), + STAT("Holy Freeze", Stat(107,114)), + STAT("Vigor", Stat(107,115)), + STAT("Conversion", Stat(107,116)), + STAT("Holy Shield", Stat(107,117)), + STAT("Holy Shock", Stat(107,118)), + STAT("Sanctuary", Stat(107,119)), + STAT("Meditation", Stat(107,120)), + STAT("Fist of the Heavens", Stat(107,121)), + STAT("Fanaticism", Stat(107,122)), + STAT("Conviction", Stat(107,123)), + STAT("Redemption", Stat(107,124)), + STAT("Salvation", Stat(107,125)), + STAT("Bash", Stat(107,126)), + STAT("Sword Mastery", Stat(107,127)), + STAT("Axe Mastery", Stat(107,128)), + STAT("Mace Mastery", Stat(107,129)), + STAT("Howl", Stat(107,130)), + STAT("Find Potion", Stat(107,131)), + STAT("Leap", Stat(107,132)), + STAT("Double Swing", Stat(107,133)), + STAT("Pole Arm Mastery", Stat(107,134)), + STAT("Throwing Mastery", Stat(107,135)), + STAT("Spear Mastery", Stat(107,136)), + STAT("Taunt", Stat(107,137)), + STAT("Shout", Stat(107,138)), + STAT("Stun", Stat(107,139)), + STAT("Double Throw", Stat(107,140)), + STAT("Increased Stamina", Stat(107,141)), + STAT("Find Item", Stat(107,142)), + STAT("Leap Attack", Stat(107,143)), + STAT("Concentrate", Stat(107,144)), + STAT("Iron Skin", Stat(107,145)), + STAT("Battle Cry", Stat(107,146)), + STAT("Frenzy", Stat(107,147)), + STAT("Increased Speed", Stat(107,148)), + STAT("Battle Orders", Stat(107,149)), + STAT("Grim Ward", Stat(107,150)), + STAT("Whirlwind", Stat(107,151)), + STAT("Berserk", Stat(107,152)), + STAT("Natural Resistance", Stat(107,153)), + STAT("War Cry", Stat(107,154)), + STAT("Battle Command", Stat(107,155)), + STAT("Fire Hit", Stat(107,156)), + STAT("UnHolyBolt", Stat(107,157)), + STAT("SkeletonRaise", Stat(107,158)), + STAT("MaggotEgg", Stat(107,159)), + STAT("ShamanFire", Stat(107,160)), + STAT("MagottUp", Stat(107,161)), + STAT("MagottDown", Stat(107,162)), + STAT("MagottLay", Stat(107,163)), + STAT("AndrialSpray", Stat(107,164)), + STAT("Jump", Stat(107,165)), + STAT("Swarm Move", Stat(107,166)), + STAT("Nest", Stat(107,167)), + STAT("Quick Strike", Stat(107,168)), + STAT("VampireFireball", Stat(107,169)), + STAT("VampireFirewall", Stat(107,170)), + STAT("VampireMeteor", Stat(107,171)), + STAT("GargoyleTrap", Stat(107,172)), + STAT("SpiderLay", Stat(107,173)), + STAT("VampireHeal", Stat(107,174)), + STAT("VampireRaise", Stat(107,175)), + STAT("Submerge", Stat(107,176)), + STAT("FetishAura", Stat(107,177)), + STAT("FetishInferno", Stat(107,178)), + STAT("ZakarumHeal", Stat(107,179)), + STAT("Emerge", Stat(107,180)), + STAT("Resurrect", Stat(107,181)), + STAT("Bestow", Stat(107,182)), + STAT("MissileSkill1", Stat(107,183)), + STAT("MonTeleport", Stat(107,184)), + STAT("PrimeLightning", Stat(107,185)), + STAT("PrimeBolt", Stat(107,186)), + STAT("PrimeBlaze", Stat(107,187)), + STAT("PrimeFirewall", Stat(107,188)), + STAT("PrimeSpike", Stat(107,189)), + STAT("PrimeIceNova", Stat(107,190)), + STAT("PrimePoisonball", Stat(107,191)), + STAT("PrimePoisonNova", Stat(107,192)), + STAT("DiabLight", Stat(107,193)), + STAT("DiabCold", Stat(107,194)), + STAT("DiabFire", Stat(107,195)), + STAT("FingerMageSpider", Stat(107,196)), + STAT("DiabWall", Stat(107,197)), + STAT("DiabRun", Stat(107,198)), + STAT("DiabPrison", Stat(107,199)), + STAT("PoisonBallTrap", Stat(107,200)), + STAT("AndyPoisonBolt", Stat(107,201)), + STAT("HireableMissile", Stat(107,202)), + STAT("DesertTurret", Stat(107,203)), + STAT("ArcaneTower", Stat(107,204)), + STAT("MonBlizzard", Stat(107,205)), + STAT("Mosquito", Stat(107,206)), + STAT("CursedBallTrapRight", Stat(107,207)), + STAT("CursedBallTrapLeft", Stat(107,208)), + STAT("MonFrozenArmor", Stat(107,209)), + STAT("MonBoneArmor", Stat(107,210)), + STAT("MonBoneSpirit", Stat(107,211)), + STAT("MonCurseCast", Stat(107,212)), + STAT("HellMeteor", Stat(107,213)), + STAT("RegurgitatorEat", Stat(107,214)), + STAT("MonFrenzy", Stat(107,215)), + STAT("QueenDeath", Stat(107,216)), + STAT("Scroll of Identify", Stat(107,217)), + STAT("Book of Identify", Stat(107,218)), + STAT("Scroll of Townportal", Stat(107,219)), + STAT("Book of Townportal", Stat(107,220)), + STAT("Raven", Stat(107,221)), + STAT("Plague Poppy", Stat(107,222)), + STAT("Wearwolf", Stat(107,223)), + STAT("Shape Shifting", Stat(107,224)), + STAT("Firestorm", Stat(107,225)), + STAT("Oak Sage", Stat(107,226)), + STAT("Summon Spirit Wolf", Stat(107,227)), + STAT("Wearbear", Stat(107,228)), + STAT("Molten Boulder", Stat(107,229)), + STAT("Arctic Blast", Stat(107,230)), + STAT("Cycle of Life", Stat(107,231)), + STAT("Feral Rage", Stat(107,232)), + STAT("Maul", Stat(107,233)), + STAT("Eruption", Stat(107,234)), + STAT("Cyclone Armor", Stat(107,235)), + STAT("Heart of Wolverine", Stat(107,236)), + STAT("Summon Fenris", Stat(107,237)), + STAT("Rabies", Stat(107,238)), + STAT("Fire Claws", Stat(107,239)), + STAT("Twister", Stat(107,240)), + STAT("Vines", Stat(107,241)), + STAT("Hunger", Stat(107,242)), + STAT("Shock Wave", Stat(107,243)), + STAT("Volcano", Stat(107,244)), + STAT("Tornado", Stat(107,245)), + STAT("Spirit of Barbs", Stat(107,246)), + STAT("Summon Grizzly", Stat(107,247)), + STAT("Fury", Stat(107,248)), + STAT("Armageddon", Stat(107,249)), + STAT("Hurricane", Stat(107,250)), + STAT("Fire Trauma", Stat(107,251)), + STAT("Claw Mastery", Stat(107,252)), + STAT("Psychic Hammer", Stat(107,253)), + STAT("Tiger Strike", Stat(107,254)), + STAT("Dragon Talon", Stat(107,255)), + STAT("Shock Field", Stat(107,256)), + STAT("Blade Sentinel", Stat(107,257)), + STAT("Quickness", Stat(107,258)), + STAT("Fists of Fire", Stat(107,259)), + STAT("Dragon Claw", Stat(107,260)), + STAT("Charged Bolt Sentry", Stat(107,261)), + STAT("Wake of Fire Sentry", Stat(107,262)), + STAT("Weapon Block", Stat(107,263)), + STAT("Cloak of Shadows", Stat(107,264)), + STAT("Cobra Strike", Stat(107,265)), + STAT("Blade Fury", Stat(107,266)), + STAT("Fade", Stat(107,267)), + STAT("Shadow Warrior", Stat(107,268)), + STAT("Claws of Thunder", Stat(107,269)), + STAT("Dragon Tail", Stat(107,270)), + STAT("Lightning Sentry", Stat(107,271)), + STAT("Inferno Sentry", Stat(107,272)), + STAT("Mind Blast", Stat(107,273)), + STAT("Blades of Ice", Stat(107,274)), + STAT("Dragon Flight", Stat(107,275)), + STAT("Death Sentry", Stat(107,276)), + STAT("Blade Shield", Stat(107,277)), + STAT("Venom", Stat(107,278)), + STAT("Shadow Master", Stat(107,279)), + STAT("Royal Strike", Stat(107,280)), + STAT("Wake Of Destruction Sentry", Stat(107,281)), + STAT("Imp Inferno", Stat(107,282)), + STAT("Imp Fireball", Stat(107,283)), + STAT("Baal Taunt", Stat(107,284)), + STAT("Baal Corpse Explode", Stat(107,285)), + STAT("Baal Monster Spawn", Stat(107,286)), + STAT("Catapult Charged Ball", Stat(107,287)), + STAT("Catapult Spike Ball", Stat(107,288)), + STAT("Suck Blood", Stat(107,289)), + STAT("Cry Help", Stat(107,290)), + STAT("Healing Vortex", Stat(107,291)), + STAT("Teleport 2", Stat(107,292)), + STAT("Self-resurrect", Stat(107,293)), + STAT("Vine Attack", Stat(107,294)), + STAT("Overseer Whip", Stat(107,295)), + STAT("Barbs Aura", Stat(107,296)), + STAT("Wolverine Aura", Stat(107,297)), + STAT("Oak Sage Aura", Stat(107,298)), + STAT("Imp Fire Missile", Stat(107,299)), + STAT("Impregnate", Stat(107,300)), + STAT("Siege Beast Stomp", Stat(107,301)), + STAT("MinionSpawner", Stat(107,302)), + STAT("CatapultBlizzard", Stat(107,303)), + STAT("CatapultPlague", Stat(107,304)), + STAT("CatapultMeteor", Stat(107,305)), + STAT("BoltSentry", Stat(107,306)), + STAT("CorpseCycler", Stat(107,307)), + STAT("DeathMaul", Stat(107,308)), + STAT("Defense Curse", Stat(107,309)), + STAT("Blood Mana", Stat(107,310)), + STAT("mon inferno sentry", Stat(107,311)), + STAT("mon death sentry", Stat(107,312)), + STAT("sentry lightning", Stat(107,313)), + STAT("fenris rage", Stat(107,314)), + STAT("Baal Tentacle", Stat(107,315)), + STAT("Baal Nova", Stat(107,316)), + STAT("Baal Inferno", Stat(107,317)), + STAT("Baal Cold Missiles", Stat(107,318)), + STAT("MegademonInferno", Stat(107,319)), + STAT("EvilHutSpawner", Stat(107,320)), + STAT("CountessFirewall", Stat(107,321)), + STAT("ImpBolt", Stat(107,322)), + STAT("Horror Arctic Blast", Stat(107,323)), + STAT("death sentry ltng", Stat(107,324)), + STAT("VineCycler", Stat(107,325)), + STAT("BearSmite", Stat(107,326)), + STAT("Resurrect2", Stat(107,327)), + STAT("BloodLordFrenzy", Stat(107,328)), + STAT("Baal Teleport", Stat(107,329)), + STAT("Imp Teleport", Stat(107,330)), + STAT("Baal Clone Teleport", Stat(107,331)), + STAT("ZakarumLightning", Stat(107,332)), + STAT("VampireMissile", Stat(107,333)), + STAT("MephistoMissile", Stat(107,334)), + STAT("DoomKnightMissile", Stat(107,335)), + STAT("RogueMissile", Stat(107,336)), + STAT("HydraMissile", Stat(107,337)), + STAT("NecromageMissile", Stat(107,338)), + STAT("MonBow", Stat(107,339)), + STAT("MonFireArrow", Stat(107,340)), + STAT("MonColdArrow", Stat(107,341)), + STAT("MonExplodingArrow", Stat(107,342)), + STAT("MonFreezingArrow", Stat(107,343)), + STAT("MonPowerStrike", Stat(107,344)), + STAT("SuccubusBolt", Stat(107,345)), + STAT("MephFrostNova", Stat(107,346)), + STAT("MonIceSpear", Stat(107,347)), + STAT("ShamanIce", Stat(107,348)), + STAT("Diablogeddon", Stat(107,349)), + STAT("Delerium Change", Stat(107,350)), + STAT("NihlathakCorpseExplosion", Stat(107,351)), + STAT("SerpentCharge", Stat(107,352)), + STAT("Trap Nova", Stat(107,353)), + STAT("UnHolyBoltEx", Stat(107,354)), + STAT("ShamanFireEx", Stat(107,355)), + STAT("Imp Fire Missile Ex", Stat(107,356)), + STAT("Interact", Stat(107,357)), + STAT("Loot", Stat(107,358)), + STAT("TownPortal", Stat(107,359)) +}; +#undef STAT \ No newline at end of file diff --git a/Globals.h b/Globals.h index 77aa35c..210eef2 100644 --- a/Globals.h +++ b/Globals.h @@ -3,19 +3,17 @@ #include #include #include +#include #include "Action.h" #include "D2Constants.h" -extern GameVersion GAME_VERSION; +//Globals +extern D2Version GameVersion; +extern int32_t FilterLevel; +extern int32_t PingLevel; +extern bool IsFilterDebug; +extern bool IsTxtDataLoaded; -extern bool TXT_DATA_LOADED; -extern std::map> STYLES; +extern std::unordered_map ItemTypes; -extern std::map ITEM_TYPE_LOOKUP_TABLE; -extern std::map ITEM_NAME_LOOKUP_TABLE; -extern std::map ITEM_CODE_LOOKUP_TABLE; -extern std::map RUNE_NAME_LOOKUP_TABLE; -extern std::map STAT_LOOKUP_TABLE; -extern std::map CLASS_SKILL_LOOKUP_TABLE; -extern std::map TAB_SKILL_LOOKUP_TABLE; -extern std::map SKILL_LOOKUP_TABLE; \ No newline at end of file +extern std::unordered_map CustomStats; \ No newline at end of file diff --git a/ItemFilter.cpp b/ItemFilter.cpp index 79d8028..ce2861f 100644 --- a/ItemFilter.cpp +++ b/ItemFilter.cpp @@ -1,13 +1,10 @@ #include -#include -#include -#include #include "D2Ptrs.h" #include "D2Tables.h" #include "D2Structs.h" #include "ItemFilter.h" #include "Rule.h" -#include "Config.h" +#include "Configuration.h" #include "Utils.h" #include "Hooking.h" #include "Globals.h" @@ -23,21 +20,18 @@ static D2COMMON_UNITS_FreeUnit_t fpUNITS_FreeUnit; static D2COMMON_DATATBLS_LoadAllTxts_t fpDATATBLS_LoadAllTxts; static D2WIN_WndProc_t fpWndProc; -static std::vector RULES; static std::queue AUTOMAP_ITEMS; -static std::map ITEM_ACTIONS; +static std::map ITEM_ACTIONS; -bool ItemFilter::IS_DEBUG_MODE = false; -Config* ItemFilter::Configuration = NULL; + +Configuration* ItemFilter::Config = NULL; ItemFilter::ItemFilter() { - LoadFilter(); + Config = new Configuration(L"./item.filter"); - - DEBUG_LOG(L"D2COMMON_UNITS_FreeUnit: {}\n", fmt::ptr(&D2COMMON_UNITS_FreeUnit)); //alot of 114d functions need diff stubs or they changed from __stdcall to __fastcall - if (GetGameVersion() == GameVersion::V114d) { + if (GetGameVersion() == D2Version::V114d) { //Item Action Own/World Packet Hooks Hooking::TrampolineHook(D2CLIENT_ItemActionWorld, &ItemActionWorld, reinterpret_cast(&fpItemActionWorld), 9); Hooking::TrampolineHook(D2CLIENT_ItemActionOwned, &ItemActionOwned, reinterpret_cast(&fpItemActionOwned), 9); @@ -109,34 +103,36 @@ ItemFilter::ItemFilter() { Hooking::TrampolineHook(D2WIN_WndProc, &WndProc, reinterpret_cast(&fpWndProc), 5); } -void ItemFilter::LoadFilter() { - Configuration = new Config(L"./item.filter"); - RULES.empty(); - STYLES.empty(); - Configuration->ParseFilter(RULES, STYLES); - ITEM_ACTIONS.empty(); - - DEBUG_LOG(L"Filter loaded\n"); -} +void ItemFilter::ReloadFilter() { + PrintGameString(std::format(L"Filter Reloaded"), TextColor::ORANGE); + Config->Load(); + + for (uint8_t i = 0; i < 128; i++) { + Unit* pUnit = FindUnit(i, UnitType::ITEM); + while (pUnit) { + RunRules(pUnit); + DoChatAlert(pUnit); + pUnit = pUnit->pListNext; + } + } -void ItemFilter::InitializeRuleConditions() { - //populate filters w/ data from txts. - for (Rule* rule : RULES) { - rule->InitializeConditions(); + for (auto entry : ITEM_ACTIONS) { + Unit* pUnit = FindUnit(entry.first, UnitType::ITEM); + RunRules(pUnit); + DoChatAlert(pUnit); } } void ItemFilter::RunRules(Unit* pItem) { - ITEM_ACTIONS.erase(pItem->dwUnitId); - for (Rule* rule : RULES) { + ITEM_ACTIONS[pItem->dwUnitId] = new ActionResult{}; + ActionResult* result = ITEM_ACTIONS[pItem->dwUnitId]; + + for (auto entry : GlobalRules) { + Rule* rule = entry.second; if (rule->Evaluate(pItem)) { - ItemActionResult action = ITEM_ACTIONS[pItem->dwUnitId]; - if (!ITEM_ACTIONS.contains(pItem->dwUnitId)) { - ITEM_ACTIONS[pItem->dwUnitId] = {}; - } - ITEM_ACTIONS[pItem->dwUnitId].vMatchedRules.push_back(rule->GetLine()); - rule->EvaluateActionResult(&ITEM_ACTIONS[pItem->dwUnitId], pItem); - if (!rule->IsContinue()) { + result->vMatchedRules.push_back(rule->GetLineNumber()); + rule->EvaluateActionResult(result, pItem); + if (!result->bContinue) { return; } } @@ -164,114 +160,10 @@ POINT ItemFilter::ScreenToAutomap(int nX, int nY) { void ItemFilter::DATATBLS_LoadAllTxts(void* pMemPool, int a2, int a3) { fpDATATBLS_LoadAllTxts(pMemPool, a2, a3); - if(!TXT_DATA_LOADED) { - PopulateLookupTables(); - InitializeRuleConditions(); - EmptyLookupTables(); - TXT_DATA_LOADED = true; - } -} - -/* -Create item name lookup tables for more human friendly rules. i.e. -Rune >= Vex -or -ItemName Swirling Crystal -*/ -void ItemFilter::PopulateLookupTables() { - DataTables* sgptDataTables = *D2COMMON_gpDataTables; - DEBUG_LOG(L"nItemsTxtRecordCount : {}\n", D2COMMON_ItemDataTbl->nItemsTxtRecordCount); - DEBUG_LOG(L"\n|Type|Code|\n"); - DEBUG_LOG(L"|-|-|\n"); - for (int i = 0; i < D2COMMON_ItemDataTbl->nItemsTxtRecordCount; i++) { - auto pItemTxt = D2COMMON_ItemDataTbl->pItemsTxt[i]; - std::wstring wNameStr = D2LANG_GetStringFromTblIndex(pItemTxt.wNameStr); - ITEM_NAME_LOOKUP_TABLE[wNameStr] = pItemTxt.dwCode; - std::wstring wCode = std::wstring(4, L' '); - mbstowcs(&wCode[0], pItemTxt.szCode, 4); - DEBUG_LOG(L"|{}|{}|\n", wNameStr, wCode); - ITEM_CODE_LOOKUP_TABLE[wCode] = pItemTxt.dwCode; - if (pItemTxt.wType[0] == ItemType::RUNE) { - int nRuneNumber = std::stoi(std::string(&pItemTxt.szCode[1], 3)); - RUNE_NAME_LOOKUP_TABLE[wNameStr] = nRuneNumber; - size_t nFound = wNameStr.find(L" "); - if (nFound != std::wstring::npos) { - RUNE_NAME_LOOKUP_TABLE[wNameStr.substr(0, nFound)] = nRuneNumber; - } - } - } - - DEBUG_LOG(L"\n|Skill|Stat|\n"); - DEBUG_LOG(L"|-|-|\n"); - for (int i = 0; i < sgptDataTables->nCharStatsTxtRecordCount; i++) { - CharStatsTxt pCharStatsTxt = sgptDataTables->pCharStatsTxt[i]; - std::wstring k = fmt::format(L"\"{} Skills\"", pCharStatsTxt.wszClassName); - std::wstring v = fmt::format(L"Stat({},{})", Stat::ITEM_ADDCLASSSKILLS, i); - SKILL_LOOKUP_TABLE[k] = v; - for (int j = 0; j < 3; j++) { - std::wstring wStrSkillTab = D2LANG_GetStringFromTblIndex(pCharStatsTxt.wStrSkillTab[j]); - auto idx = (i * 8) + j; - k = fmt::format(L"\"{}\"", wStrSkillTab); - v = fmt::format(L"Stat({}, {})", Stat::ITEM_ADDSKILL_TAB, idx); - SKILL_LOOKUP_TABLE[k] = v; - } - } - - for (int i = 0; i < sgptDataTables->nSkillsTxtRecordCount; i++) { - SkillsTxt pSkillsTxt = sgptDataTables->pSkillsTxt[i]; - if (pSkillsTxt.wSkillDesc == UINT16_MAX) { - continue; - } - SkillDescTxt pSkillDescTxt = sgptDataTables->pSkillDescTxt[pSkillsTxt.wSkillDesc]; - std::wstring wSkillStr = D2LANG_GetStringFromTblIndex(pSkillDescTxt.wStrName); - std::wstring k = fmt::format(L"\"{}\"", wSkillStr); - std::wstring v = fmt::format(L"Stat({},{})", Stat::ITEM_SINGLESKILL, pSkillsTxt.nSkillId); - SKILL_LOOKUP_TABLE[k] = v; + IsTxtDataLoaded = true; + if (!Config->IsLoaded()) { + Config->Load(); } - - DEBUG_LOG(L"\n|Skill|Stat|\n"); - DEBUG_LOG(L"|-|-|\n"); - for (auto& e : SKILL_LOOKUP_TABLE) { - DEBUG_LOG(L"|{}|{}|\n", e.first, e.second); - } - - DEBUG_LOG(L"\n|Rune|Stat|\n"); - DEBUG_LOG(L"|-|-|\n"); - for (auto& e : RUNE_NAME_LOOKUP_TABLE) { - DEBUG_LOG(L"|{}|{}|\n", e.first, e.second); - } - - DEBUG_LOG(L"\n|Stat|Stat|\n"); - DEBUG_LOG(L"|-|-|\n"); - for (auto& e : STAT_LOOKUP_TABLE) { - DEBUG_LOG(L"|{}|{}|\n", e.first, e.second); - } - - DEBUG_LOG(L"\n|Class|Id|\n"); - DEBUG_LOG(L"|-|-|\n"); - for (auto& e : ITEM_TYPE_LOOKUP_TABLE) { - DEBUG_LOG(L"|{}|{}|\n", e.first, e.second); - } - - /* - DEBUG_LOG(L"\n|Prefix|Stat|\n"); - DEBUG_LOG(L"|-|-|\n"); - for (auto& e : STAT_LOOKUP_TABLE) { - DEBUG_LOG(L"|{}|{}|\n", e.first, e.second); - } - - DEBUG_LOG(L"\n|Suffix|Stat|\n"); - DEBUG_LOG(L"|-|-|\n"); - for (auto& e : STAT_LOOKUP_TABLE) { - DEBUG_LOG(L"|{}|{}|\n", e.first, e.second); - } - */ -} - -void ItemFilter::EmptyLookupTables() { - ITEM_NAME_LOOKUP_TABLE.empty(); - ITEM_CODE_LOOKUP_TABLE.empty(); - RUNE_NAME_LOOKUP_TABLE.empty(); } void ItemFilter::HandlePacket(uint8_t* pBitStream, D2GSServerToClientPacketHandlerFn pHandler) { @@ -290,8 +182,9 @@ void ItemFilter::HandlePacket(uint8_t* pBitStream, D2GSServerToClientPacketHandl return; } - RunRules(pItem); + //TODO: exclude NPC items? + RunRules(pItem); DoChatAlert(pItem); } @@ -318,8 +211,8 @@ BOOL __stdcall ItemFilter::GetItemName(Unit* pItem, wchar_t* pBuffer, uint32_t d void __stdcall ItemFilter::HandleItemName(Unit* pItem, wchar_t* pBuffer, uint32_t dwSize) { if (HasActions(pItem) - && ITEM_ACTIONS[pItem->dwUnitId].bItemNameSet) { - std::wstring copy = ITEM_ACTIONS[pItem->dwUnitId].wsItemName; + && ITEM_ACTIONS[pItem->dwUnitId]->bItemNameSet) { + std::wstring copy = ITEM_ACTIONS[pItem->dwUnitId]->wsItemName; replace(copy, L"{Name}", pBuffer); wcsncpy(pBuffer, copy.c_str(), 0x7d); } @@ -327,8 +220,8 @@ void __stdcall ItemFilter::HandleItemName(Unit* pItem, wchar_t* pBuffer, uint32_ void __stdcall ItemFilter::GetItemDesc(Unit* pItem, wchar_t* pBuffer) { if (HasActions(pItem) - && ITEM_ACTIONS[pItem->dwUnitId].bItemDescSet) { - std::wstring copy = ITEM_ACTIONS[pItem->dwUnitId].wsItemDesc; + && ITEM_ACTIONS[pItem->dwUnitId]->bItemDescSet) { + std::wstring copy = ITEM_ACTIONS[pItem->dwUnitId]->wsItemDesc; replace(copy, L"{Description}", pBuffer); wcsncpy(pBuffer, copy.c_str(), 0x800); } @@ -343,9 +236,9 @@ void __stdcall ItemFilter::AUTOMAP_Draw() { continue; } if (HasActions(pItem) - && ITEM_ACTIONS[pItem->dwUnitId].bMinimapIcon) { + && ITEM_ACTIONS[pItem->dwUnitId]->bMinimapIcon) { POINT p = ScreenToAutomap(pItem->pStaticPath->nXPos, pItem->pStaticPath->nYPos); - D2GFX_DrawSolidRectEx(p.x - 5, p.y - 5, p.x + 5, p.y + 5, ITEM_ACTIONS[pItem->dwUnitId].nMinimapIconPaletteIndex, DrawMode::NORMAL); + D2GFX_DrawSolidRectEx(p.x - 5, p.y - 5, p.x + 5, p.y + 5, ITEM_ACTIONS[pItem->dwUnitId]->nMinimapIconPaletteIndex, DrawMode::NORMAL); } } } @@ -353,7 +246,7 @@ void __stdcall ItemFilter::AUTOMAP_Draw() { //nXpos and nYpos don't appear to be right... BOOL __fastcall ItemFilter::UNITDRAW_DrawUnit(Unit* pUnit, uint32_t dwColorTint, int nXpos, int nYpos, BOOL bFade, BOOL bDrawOverlays) { if (HasActions(pUnit) - && ITEM_ACTIONS[pUnit->dwUnitId].bMinimapIcon) { + && ITEM_ACTIONS[pUnit->dwUnitId]->bMinimapIcon) { //this seems janky, but is used to queue up items to be drawn on automap AUTOMAP_ITEMS.push(pUnit->dwUnitId); } @@ -372,39 +265,45 @@ void __stdcall ItemFilter::DrawDebugInfo(Unit* pItem, uint32_t nXStart, uint32_t if (!HasActions(pItem)) { return; } - ItemActionResult actions = ITEM_ACTIONS[pItem->dwUnitId]; + ActionResult* actions = ITEM_ACTIONS[pItem->dwUnitId]; std::vector lines; std::wostringstream os; os << L"Matched Lines #: " << TEXT_GREEN; - for (auto& match : actions.vMatchedRules) { - if (&match != &actions.vMatchedRules.front()) { + for (auto& match : actions->vMatchedRules) { + if (&match != &actions->vMatchedRules.front()) { os << L", "; } os << match; } + lines.push_back(std::format(L"ID: {}{}", TEXT_WHITE, pItem->dwUnitId)); lines.push_back(os.str()); - lines.push_back(fmt::format(L"Shown: {}{}", TEXT_WHITE, actions.bHide ? L"False" : L"True")); - if (actions.bItemNameSet) { - lines.push_back(fmt::format(L"Name: {}{}", TEXT_WHITE, actions.wsItemName)); + lines.push_back(std::format(L"Shown: {}{}", TEXT_WHITE, actions->bHide ? L"False" : L"True")); + if (actions->bItemNameSet) { + lines.push_back(std::format(L"Name: {}{}", TEXT_WHITE, actions->wsItemName)); } - if (actions.bItemDescSet) { - lines.push_back(fmt::format(L"Description: {}{}", TEXT_WHITE, actions.wsItemDesc)); + if (actions->bItemDescSet) { + lines.push_back(std::format(L"Description: {}{}", TEXT_WHITE, actions->wsItemDesc)); } - if (actions.bChatAlert) { - lines.push_back(fmt::format(L"Chat Alert: {}{}", TEXT_WHITE, actions.bChatAlert ? L"True" : L"False")); + if (actions->bChatAlert) { + lines.push_back(std::format(L"Chat Alert: {}{}", TEXT_WHITE, actions->bChatAlert ? L"True" : L"False")); } - if (actions.bMinimapIcon) { - lines.push_back(fmt::format(L"Minimap Icon: {}{:#04x}", TEXT_WHITE, actions.nMinimapIconPaletteIndex)); + if (actions->bMinimapIcon) { + lines.push_back(std::format(L"Minimap Icon: {}{:#04x}", TEXT_WHITE, actions->nMinimapIconPaletteIndex)); } - if (actions.bBorderPaletteIndexSet) { - lines.push_back(fmt::format(L"Border Color: {}{:#04x}", TEXT_WHITE, actions.nBorderPaletteIndex)); + if (actions->bBorderPaletteIndexSet) { + lines.push_back(std::format(L"Border Color: {}{:#04x}", TEXT_WHITE, actions->nBorderPaletteIndex)); } - if (actions.bInvBackgroundPaletteIndexSet) { - lines.push_back(fmt::format(L"Inventory Color: {}{:#04x}", TEXT_WHITE, actions.nInvBackgroundPaletteIndex)); + if (actions->bInvBackgroundPaletteIndexSet) { + lines.push_back(std::format(L"Inventory Color: {}{:#04x}", TEXT_WHITE, actions->nInvBackgroundPaletteIndex)); } auto width = 0; for (auto& line : lines) { - auto w = D2WIN_GetTextPixelWidth(line.c_str()); + uint32_t w, fileNo; + if (GetGameVersion() == D2Version::V114d) { + w = D2WIN_GetTextPixelWidth(line.c_str()); + } else { + D2WIN_GetTextPixelWidthFileNo(line.c_str(), &w, &fileNo); + } if (w > width) { width = w; } @@ -444,22 +343,22 @@ void __stdcall ItemFilter::DrawGroundItemRect(DWORD retAddress, BOOL isHovered, if (pItem == nullptr) { pItem = *D2CLIENT_GetHoverItem; } - if ((isHovered || eDrawMode == DrawMode::NORMAL) && IS_DEBUG_MODE) { + if ((isHovered || eDrawMode == DrawMode::NORMAL) && IsFilterDebug) { DrawDebugInfo(pItem, nXStart, nYStart, nXEnd, nYEnd); } if (HasActions(pItem) && (pItem->eItemAnimMode == ItemAnimationMode::DROPPING || pItem->eItemAnimMode == ItemAnimationMode::GROUND)) { - nPaletteIndex = ITEM_ACTIONS[pItem->dwUnitId].bBackgroundPaletteIndexSet ? ITEM_ACTIONS[pItem->dwUnitId].nBackgroundPaletteIndex : nPaletteIndex; - if (ITEM_ACTIONS[pItem->dwUnitId].bBorderPaletteIndexSet) { + nPaletteIndex = ITEM_ACTIONS[pItem->dwUnitId]->bBackgroundPaletteIndexSet ? ITEM_ACTIONS[pItem->dwUnitId]->nBackgroundPaletteIndex : nPaletteIndex; + if (ITEM_ACTIONS[pItem->dwUnitId]->bBorderPaletteIndexSet) { auto pad = 1; RECT rect = { nXStart + pad, nYStart + pad, nXEnd - pad, nYEnd - pad }; - D2GFX_DrawRect(&rect, ITEM_ACTIONS[pItem->dwUnitId].nBorderPaletteIndex); + D2GFX_DrawRect(&rect, ITEM_ACTIONS[pItem->dwUnitId]->nBorderPaletteIndex); } if (eDrawMode == DrawMode::TRANS_50) { - eDrawMode = ITEM_ACTIONS[pItem->dwUnitId].eDrawModeAlt; + eDrawMode = ITEM_ACTIONS[pItem->dwUnitId]->eDrawModeAlt; } else { - eDrawMode = ITEM_ACTIONS[pItem->dwUnitId].eDrawModeHover; + eDrawMode = ITEM_ACTIONS[pItem->dwUnitId]->eDrawModeHover; } } //call original @@ -469,7 +368,7 @@ void __stdcall ItemFilter::DrawGroundItemRect(DWORD retAddress, BOOL isHovered, void __stdcall ItemFilter::DrawInventoryItemRect(Unit* pItem, uint32_t nXStart, uint32_t nYStart, uint32_t nXEnd, uint32_t nYEnd, uint8_t nPaletteIndex, DrawMode eDrawMode) { //0x08: red, 0xea: blue, 0x76: green. if (HasActions(pItem)) { - nPaletteIndex = ITEM_ACTIONS[pItem->dwUnitId].bInvBackgroundPaletteIndexSet ? ITEM_ACTIONS[pItem->dwUnitId].nInvBackgroundPaletteIndex : nPaletteIndex; + nPaletteIndex = ITEM_ACTIONS[pItem->dwUnitId]->bInvBackgroundPaletteIndexSet ? ITEM_ACTIONS[pItem->dwUnitId]->nInvBackgroundPaletteIndex : nPaletteIndex; } //call original D2GFX_DrawSolidRectEx(nXStart, nYStart, nXEnd, nYEnd, nPaletteIndex, eDrawMode); @@ -478,8 +377,8 @@ void __stdcall ItemFilter::DrawInventoryItemRect(Unit* pItem, uint32_t nXStart, //returns result of nodraw flag shift BOOL __fastcall ItemFilter::IsUnitNoDraw(Unit* pUnit) { if (HasActions(pUnit)) { - if (ITEM_ACTIONS[pUnit->dwUnitId].bHide - && !IS_DEBUG_MODE) { + if (ITEM_ACTIONS[pUnit->dwUnitId]->bHide + && !IsFilterDebug) { pUnit->dwFlagEx |= static_cast>(UnitFlagEx::NODRAW); } else { pUnit->dwFlagEx &= ~(static_cast>(UnitFlagEx::NODRAW)); @@ -490,11 +389,11 @@ BOOL __fastcall ItemFilter::IsUnitNoDraw(Unit* pUnit) { void ItemFilter::DoChatAlert(Unit* pUnit) { if (HasActions(pUnit) - && ITEM_ACTIONS[pUnit->dwUnitId].bChatAlert + && ITEM_ACTIONS[pUnit->dwUnitId]->bChatAlert && (pUnit->eItemAnimMode == ItemAnimationMode::DROPPING || pUnit->eItemAnimMode == ItemAnimationMode::GROUND)) { wchar_t buffer[0x100] = L""; - if (GetGameVersion() == GameVersion::V114d) { + if (GetGameVersion() == D2Version::V114d) { D2CLIENT_GetItemName_114d(pUnit, buffer, 0x100); } else { D2CLIENT_GetItemName(pUnit, buffer, 0x100); @@ -506,10 +405,10 @@ void ItemFilter::DoChatAlert(Unit* pUnit) { } void ItemFilter::ToggleDebug() { - ItemFilter::IS_DEBUG_MODE = !ItemFilter::IS_DEBUG_MODE; - DEBUG_LOG(L"Debug {}", ItemFilter::IS_DEBUG_MODE ? L"On" : L"Off"); + IsFilterDebug = !IsFilterDebug; + DEBUG_LOG(L"Debug {}", IsFilterDebug ? L"On" : L"Off"); if (D2CLIENT_GetPlayerUnit()) { - PrintGameString(fmt::format(L"Debug {}", ItemFilter::IS_DEBUG_MODE ? L"On" : L"Off"), TextColor::ORANGE); + PrintGameString(std::format(L"Debug {}", IsFilterDebug ? L"On" : L"Off"), TextColor::ORANGE); } } @@ -517,11 +416,19 @@ LRESULT ItemFilter::WndProc(HWND hWnd, int msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_SYSKEYUP: case WM_KEYUP: { + //Z debug if (wParam == 0x5A && (GetKeyState(VK_LSHIFT) & 0x80) || (GetKeyState(VK_RSHIFT) & 0x80) && (GetKeyState(VK_LCONTROL) & 0x80) || (GetKeyState(VK_RCONTROL) & 0x80)) { ItemFilter::ToggleDebug(); } + //R reload + if (wParam == 0x52 + && (GetKeyState(VK_LCONTROL) & 0x80) || (GetKeyState(VK_RCONTROL) & 0x80)) { + if (IsTxtDataLoaded) { + ItemFilter::ReloadFilter(); + } + } break; } default: { @@ -603,7 +510,7 @@ void __declspec(naked) __stdcall ItemFilter::DrawAltDownItemRect_STUB() { push[esp + 0x18]; push[esi - 0x4]; push 0; - push[esp + 0x58]; + push 0; call ItemFilter::DrawGroundItemRect; ret 0x18; } @@ -621,7 +528,7 @@ void __declspec(naked) __stdcall ItemFilter::DrawAltDownItemRect_STUB_114d() { mov eax, [esp + 0x40]; push[eax - 0x4]; push 0; - push[esp + 0x60]; + push [esp + 0x60]; call ItemFilter::DrawGroundItemRect; ret 0x18; } diff --git a/ItemFilter.h b/ItemFilter.h index cd4609d..dbd8551 100644 --- a/ItemFilter.h +++ b/ItemFilter.h @@ -3,26 +3,21 @@ #include #include "D2Structs.h" #include "Rule.h" -#include "Config.h" +#include "Configuration.h" typedef void(__fastcall* D2GSServerToClientPacketHandlerFn)(uint8_t* pBitstream); class ItemFilter { private: - static Config* Configuration; - static bool IS_DEBUG_MODE; + static Configuration* Config; public: ItemFilter(); - static void PopulateLookupTables(); - static void EmptyLookupTables(); - static void LoadFilter(); - static void InitializeRuleConditions(); + static void ReloadFilter(); static void RunRules(Unit* pItem); static void ToggleDebug(); - static bool IsDebug() { return IS_DEBUG_MODE; } static void DoChatAlert(Unit* pUnit); @@ -30,7 +25,10 @@ class ItemFilter static bool HasActions(Unit* pUnit); static POINT ScreenToAutomap(int nX, int nY); +#pragma region Hooks //Hooked Methods + static LRESULT WndProc(HWND hWnd, int msg, WPARAM wParam, LPARAM lParam); + static void __fastcall ItemActionOwned(uint8_t* pBitstream); static void __fastcall ItemActionWorld(uint8_t* pBitstream); static void HandlePacket(uint8_t* pBitstream, D2GSServerToClientPacketHandlerFn pHandler); @@ -49,7 +47,9 @@ class ItemFilter static void __stdcall AUTOMAP_Draw(); static BOOL __fastcall UNITDRAW_DrawUnit(Unit* pUnit, uint32_t dwColorTint, int nXpos, int nYpos, BOOL bFade, BOOL bDrawOverlays); static BOOL __fastcall IsUnitNoDraw(Unit* pUnit); +#pragma endregion +#pragma region Stubs //Stubs static void __stdcall DrawAltDownItemRect_STUB(); static void __stdcall DrawAltDownItemRect_STUB_114d(); @@ -65,8 +65,7 @@ class ItemFilter static void __stdcall GetItemDesc_STUB(); static void __stdcall GetItemDesc_STUB_114d(); - - static LRESULT WndProc(HWND hWnd, int msg, WPARAM wParam, LPARAM lParam); +#pragma endregion }; diff --git a/Rule.cpp b/Rule.cpp index ba7e680..125fdf3 100644 --- a/Rule.cpp +++ b/Rule.cpp @@ -1,21 +1,49 @@ #include "Rule.h" - -Rule::Rule(uint32_t lineNum) : m_LineNum(lineNum) { -} - bool Rule::Evaluate(Unit* pItem) { - return std::all_of(m_Conditions.begin(), m_Conditions.end(), [pItem](Condition* c) { return c->Evaluate(pItem); }); + for (auto& condition : m_Conditions) { + if (!condition->Evaluate(pItem)) { + return false; + } + } + return true; } -void Rule::EvaluateActionResult(ItemActionResult* actionResult, Unit* pItem) { +void Rule::EvaluateActionResult(ActionResult* pActionResult, Unit* pItem) { + pActionResult->bContinue = false; for (auto& action : m_Actions) { - action->SetResult(actionResult, pItem); + action->SetResult(pActionResult, pItem); } } -void Rule::InitializeConditions() { - for (auto& condition : m_Conditions) { - condition->Initialize(); - } +uint32_t Rule::GetLineNumber() { + return m_LineNumber; +} + +void Rule::SetLineNumber(uint32_t lineNumber) { + m_LineNumber = lineNumber; +} + +std::vector Rule::GetConditions() { + return m_Conditions; +} + +std::vector Rule::GetActions() { + return m_Actions; +} + +void Rule::AddAction(Action* pAction, int32_t idx) { + m_Actions.insert(m_Actions.begin() + idx, pAction); } + +void Rule::AddActions(std::vector actions) { + m_Actions.insert(m_Actions.end(), actions.begin(), actions.end()); +} + +void Rule::AddCondition(Condition* pCondition) { + m_Conditions.push_back(pCondition); +} + +void Rule::AddConditions(std::vector conditions) { + m_Conditions.insert(m_Conditions.end(), conditions.begin(), conditions.end()); +} \ No newline at end of file diff --git a/Rule.h b/Rule.h index 96e96d4..d268338 100644 --- a/Rule.h +++ b/Rule.h @@ -1,29 +1,26 @@ #pragma once -#include #include #include -#include +#include "D2Structs.h" #include "Condition.h" #include "Action.h" - - class Rule { private: - uint32_t m_LineNum; - bool m_IsContinue = false; + uint32_t m_LineNumber; std::vector m_Conditions; std::vector m_Actions; public: - Rule(uint32_t lineNum); - bool Evaluate(Unit* pItem); - void EvaluateActionResult(ItemActionResult* action, Unit* pItem); - void SetIsContinue(bool isContinue) { m_IsContinue = isContinue; }; - bool IsContinue() { return m_IsContinue; }; - void InitializeConditions(); - void AddCondition(Condition* condition) { m_Conditions.push_back(condition); }; - void AddAction(Action* condition) { m_Actions.push_back(condition); }; - uint32_t GetLine() { return m_LineNum; } -}; - + Rule() {} + bool Evaluate(Unit* pUnit); + void EvaluateActionResult(ActionResult* pActionResult, Unit* pItem); + uint32_t GetLineNumber(); + void SetLineNumber(uint32_t lineNumber); + std::vector GetConditions(); + std::vector GetActions(); + void AddAction(Action* pAction, int32_t idx); + void AddActions(std::vector actions); + void AddCondition(Condition* pCondition); + void AddConditions(std::vector conditions); +}; \ No newline at end of file diff --git a/Utils.cpp b/Utils.cpp index c84ec9d..44fe757 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -6,22 +6,20 @@ #include "Utils.h" #include #include -#include -#include #include "D2Tables.h" #include "D2Structs.h" #include "D2Ptrs.h" #pragma comment(lib, "Version.Lib") -GameVersion InitGameVersion(LPCVOID pVersionResource) { +D2Version InitGameVersion(LPCVOID pVersionResource) { UINT uLen; VS_FIXEDFILEINFO* ptFixedFileInfo; if (!VerQueryValue(pVersionResource, L"\\", (LPVOID*)&ptFixedFileInfo, &uLen)) - return GameVersion::NONE; + return D2Version::NONE; if (uLen == 0) - return GameVersion::ERR; + return D2Version::ERR; WORD major = HIWORD(ptFixedFileInfo->dwFileVersionMS); WORD minor = LOWORD(ptFixedFileInfo->dwFileVersionMS); @@ -29,37 +27,37 @@ GameVersion InitGameVersion(LPCVOID pVersionResource) { WORD subrevision = LOWORD(ptFixedFileInfo->dwFileVersionLS); if (major != 1) - return GameVersion::ERR; - if (minor == 0 && revision == 13 && subrevision == 60) return GameVersion::V113c; - if (minor == 14 && revision == 3 && subrevision == 71) return GameVersion::V114d; - return GameVersion::ERR; + return D2Version::ERR; + if (minor == 0 && revision == 13 && subrevision == 60) return D2Version::V113c; + if (minor == 14 && revision == 3 && subrevision == 71) return D2Version::V114d; + return D2Version::ERR; } -GameVersion InitGameVersion() { +D2Version InitGameVersion() { HMODULE hModule = GetModuleHandle(NULL); HRSRC hResInfo = FindResource(hModule, MAKEINTRESOURCE(VS_VERSION_INFO), RT_VERSION); if (!hResInfo) { - return GameVersion::ERR; + return D2Version::ERR; } HGLOBAL hResData = LoadResource(hModule, hResInfo); if (!hResData) { - return GameVersion::ERR; + return D2Version::ERR; } LPVOID pVersionResource = LockResource(hResData); - GameVersion version = InitGameVersion(pVersionResource); + D2Version version = InitGameVersion(pVersionResource); FreeResource(hResData); return version; } -GameVersion GetGameVersion() { - if (GAME_VERSION == GameVersion::NONE) { - GAME_VERSION = InitGameVersion(); +D2Version GetGameVersion() { + if (GameVersion == D2Version::NONE) { + GameVersion = InitGameVersion(); } - return GAME_VERSION; + return GameVersion; } void PrintGameString(std::wstring wStr, TextColor color) { - if (GetGameVersion() == GameVersion::V114d) { + if (GetGameVersion() == D2Version::V114d) { D2CLIENT_PrintGameStringe_114d(wStr.c_str(), color); } else { D2CLIENT_PrintGameString(wStr.c_str(), color); @@ -75,35 +73,49 @@ Unit* FindUnit(uint32_t unitId, UnitType type) { } uint32_t __fastcall GetDllOffset(uint32_t baseAddress, int offset) { - DEBUG_LOG("baseAddress: {}\n", baseAddress); - DEBUG_LOG("offset: {}\n", offset); - DEBUG_LOG("return: {}\n", baseAddress + offset); + DEBUG_LOG(L"baseAddress: {}\n", baseAddress); + DEBUG_LOG(L"offset: {}\n", offset); + DEBUG_LOG(L"return: {}\n", baseAddress + offset); if (offset < 0) return (uint32_t)GetProcAddress((HMODULE)baseAddress, (LPCSTR)(-offset)); return baseAddress + offset; } -std::wstring_view ltrim(std::wstring_view s) -{ +std::wstring_view ltrim(std::wstring_view s) { s.remove_prefix(std::distance(s.cbegin(), std::find_if(s.cbegin(), s.cend(), - [](int c) {return !std::isspace(c); }))); + [](wchar_t c) {return !iswspace(c); }))); return s; } -std::wstring_view rtrim(std::wstring_view s) -{ +std::wstring_view rtrim(std::wstring_view s) { s.remove_suffix(std::distance(s.crbegin(), std::find_if(s.crbegin(), s.crend(), - [](int c) {return !std::isspace(c); }))); + [](wchar_t c) {return !iswspace(c); }))); return s; } -std::wstring_view trim(std::wstring_view s) -{ +std::wstring_view trim(std::wstring_view s) { return ltrim(rtrim(s)); } +std::wstring ltrim_copy(std::wstring s) { + ltrim(s); + return std::wstring(s); +} + +// trim from end (copying) +std::wstring rtrim_copy(std::wstring s) { + rtrim(s); + return std::wstring(s); +} + +// trim from both ends (copying) +std::wstring trim_copy(std::wstring s) { + trim(s); + return std::wstring(s); +} + void replace(std::wstring& subject, const std::wstring& search, const std::wstring& replace) { size_t pos = 0; while ((pos = subject.find(search, pos)) != std::string::npos) { @@ -127,3 +139,7 @@ std::vector split(const std::wstring& stringToSplit, const std::ws return result; } +ItemsTxt GetItemsTxt(Unit* pUnit) { + return D2COMMON_ItemDataTbl->pItemsTxt[pUnit->dwLineId]; +} + diff --git a/Utils.h b/Utils.h index 4edf4c2..070e57d 100644 --- a/Utils.h +++ b/Utils.h @@ -5,11 +5,10 @@ #include #include #include "D2Structs.h" +#include "D2Tables.h" -#include -#include #ifdef _DEBUG -#define DEBUG_LOG(f, ...) fmt::print(f, __VA_ARGS__); +#define DEBUG_LOG(f, ...) std::wprintf(f, __VA_ARGS__); #else #define DEBUG_LOG(f, ...) ; #endif @@ -18,7 +17,7 @@ //Hooking uint32_t __fastcall GetDllOffset(uint32_t baseAddress, int offset); -GameVersion GetGameVersion(); +D2Version GetGameVersion(); void PrintGameString(std::wstring wStr, TextColor color); Unit* FindUnit(uint32_t unitId, UnitType type); @@ -26,5 +25,12 @@ Unit* FindUnit(uint32_t unitId, UnitType type); std::wstring_view ltrim(std::wstring_view s); std::wstring_view rtrim(std::wstring_view s); std::wstring_view trim(std::wstring_view s); +std::wstring ltrim_copy(std::wstring s); +std::wstring rtrim_copy(std::wstring s); +std::wstring trim_copy(std::wstring s); + void replace(std::wstring& subject, const std::wstring& search, const std::wstring& replace); std::vector split(const std::wstring& stringToSplit, const std::wstring& regexPattern); + +//Utility D2 Methods +ItemsTxt GetItemsTxt(Unit* pUnit); diff --git a/d2lootfilter.vcxproj b/d2lootfilter.vcxproj index 73103a7..8d38b36 100644 --- a/d2lootfilter.vcxproj +++ b/d2lootfilter.vcxproj @@ -58,14 +58,6 @@ true d2lootfilter - - true - true - - - true - true - Level3 @@ -132,8 +124,9 @@ if defined D2Path ( - + + @@ -143,7 +136,7 @@ if defined D2Path ( - + diff --git a/d2lootfilter.vcxproj.filters b/d2lootfilter.vcxproj.filters index d7abcbf..7beba20 100644 --- a/d2lootfilter.vcxproj.filters +++ b/d2lootfilter.vcxproj.filters @@ -30,7 +30,7 @@ Source Files - + Source Files @@ -42,6 +42,9 @@ Source Files + + Source Files + @@ -65,7 +68,7 @@ Header Files - + Header Files diff --git a/dllmain.cpp b/dllmain.cpp index d1307ab..1e94493 100644 --- a/dllmain.cpp +++ b/dllmain.cpp @@ -19,7 +19,7 @@ BOOL DllAttach() { freopen_s(reinterpret_cast(stdout), "CONOUT$", "w", stdout); freopen_s(reinterpret_cast(stderr), "CONOUT$", "w", stderr); #endif - if (GetGameVersion() == GameVersion::ERR) { + if (GetGameVersion() == D2Version::ERR) { Error(L"Could not determine the game version."); } diff --git a/doc/ENG/item.filter b/doc/ENG/item.filter index af0d05e..99255c7 100644 --- a/doc/ENG/item.filter +++ b/doc/ENG/item.filter @@ -1,10 +1,34 @@ ###### +# +# Filter Organization Notes: +# * Styles for different Tiers +# * Metadata +# * Gem Names +# * Rune Upgrade Recipies +# * Runeword Recipies +# * Upgrade Uniques Recipies +# * Upgrade Rares Recipies +# * General Metadata (colors for Magic/Rares/etc) +# * Socket Recipies +# * Runes +# * Gems +# * Shopping Tiers 1-6 +# * Uniques and Sets Tiers 1-6 +# * Rares Tiers 1-6 +# * Magic Tiers 1-6 +# * Socketables Tiers 1-6 +# * Misc Tiers 1-6 +# * TODO: Identified (Uniques and Sets) Tiers 1-6 +# * TODO: Normal Items +# +###### +###### # START Styles ###### Style T1 Rune SetName {Purple}<<< {Orange}T1 {Name} {Purple}>>> - ChatNotify True + ChatNotify "Ping Level" > 0 SetBackgroundColor Purple SetBorderColor Purple SetInventoryColor Purple @@ -12,7 +36,7 @@ Style T1 Rune Style T2 Rune SetName {Purple}<<< {Orange}T2 {Name} {Purple}>>> - ChatNotify True + ChatNotify "Ping Level" > 1 SetBackgroundColor Purple SetBorderColor Purple SetInventoryColor Purple @@ -20,52 +44,52 @@ Style T2 Rune Style T3 Rune SetName {Orange}T3 {Name} - ChatNotify True + ChatNotify "Ping Level" > 2 SetBackgroundColor Red SetBorderColor Red MinimapIcon Red Style T4 Rune SetName {Orange}T4 {Name} - ChatNotify True + ChatNotify "Ping Level" > 3 SetBorderColor Gold MinimapIcon Gold Style T1 SetName {Orange}T1 {Name} - ChatNotify True + ChatNotify "Ping Level" > 0 SetBorderColor Orange MinimapIcon Orange Style T2 SetName {Purple}T2 {Name} - ChatNotify True + ChatNotify "Ping Level" > 1 SetBorderColor Purple MinimapIcon Purple Style T3 SetName {Red}T3 {Name} - ChatNotify True + ChatNotify "Ping Level" > 2 SetBorderColor Red MinimapIcon Red Style T4 SetName {Gold}T4 {Name} - ChatNotify True + ChatNotify "Ping Level" > 3 SetBorderColor Gold MinimapIcon Gold Style T5 SetName {Dark Green}T5 {Name} -# ChatNotify True - SetBorderColor Dark Green -# MinimapIcon Dark Green + ChatNotify "Ping Level" > 4 + SetBorderColor "Dark Green" + MinimapIcon "Dark Green" Style T6 SetName {Gray}T6 {Name} -# ChatNotify True + ChatNotify "Ping Level" > 5 SetBorderColor Gray -# MinimapIcon Gray + MinimapIcon Gray ###### # END Styles @@ -116,7 +140,7 @@ Continue Show Class Gem Class Skull - SetName {Gray}0 {White}{Name} + SetName {Grey}0 {White}{Name} Continue ###### @@ -273,71 +297,6 @@ Show SetDescription {Description}{White}3x {Orange}El -> {Orange}Eld{Newline} Continue -###### -# Upgrade Uniques Metadata -###### - -Show - Weapon True - Rarity Unique - Quality Normal - SetDescription {Description}{White}Upgrade to exceptional with {Orange}Ral Sol {Green}0{White}Perfect{Newline} -Continue - -Show - Armor True - Rarity Unique - Quality Normal - SetDescription {Description}{White}Upgrade to exceptional with {Orange}Tal Sheal {White}0{White}Perfect{Newline} -Continue - -Show - Weapon True - Rarity Unique - Quality Exceptional - SetDescription {Description}{White}Upgrade to elite with {Orange}Lum Pul {Green}0{White}Perfect{Newline} -Continue - -Show - Armor True - Rarity Unique - Quality Exceptional - SetDescription {Description}{White}Upgrade to elite with {Orange}Ko Lem {White}0{White}Perfect{Newline} -Continue - -###### -# Upgrade Rares Metadata -###### - -Show - Weapon True - Rarity Rare - Quality Normal - SetDescription {Description}{White}Upgrade to exceptional with {Orange}Ort Amn {Blue}0{White}Perfect{Newline} -Continue - -Show - Armor True - Rarity Rare - Quality Normal - SetDescription {Description}{White}Upgrade to exceptional with {Orange}Ral Thul {White}0{White}Perfect{Newline} -Continue - -Show - Weapon True - Rarity Rare - Quality Exceptional - SetDescription {Description}{White}Upgrade to elite with {Orange}Fal Um {Green}0{White}Perfect{Newline} -Continue - -Show - Armor True - Rarity Rare - Quality Exceptional - SetDescription {Description}{White}Upgrade to elite with {Orange}Ko Pul {White}0{White}Perfect{Newline} -Continue - - ###### # Runewords Metadata ###### @@ -588,20 +547,85 @@ Show SetDescription {Description}{White}Breath of the Dying: {Orange}VexHelElEldZodEth{Newline} Continue + ###### -# General Metadata +# Upgrade Uniques Metadata ###### Show - Rarity < Magic - SetName {White}{Name} + Weapon True + Rarity Unique + Quality Normal + SetDescription {Description}{White}Upgrade to exceptional with {Orange}Ral Sol {Green}0{White}Perfect{Newline} Continue Show - Class Any Armor, Weapon, Ring, Amulet - Rarity <= Magic - Price 35000 - SetName {Name} {Gray}+35k + Armor True + Rarity Unique + Quality Normal + SetDescription {Description}{White}Upgrade to exceptional with {Orange}Tal Sheal {White}0{White}Perfect{Newline} +Continue + +Show + Weapon True + Rarity Unique + Quality Exceptional + SetDescription {Description}{White}Upgrade to elite with {Orange}Lum Pul {Green}0{White}Perfect{Newline} +Continue + +Show + Armor True + Rarity Unique + Quality Exceptional + SetDescription {Description}{White}Upgrade to elite with {Orange}Ko Lem {White}0{White}Perfect{Newline} +Continue + +###### +# Upgrade Rares Metadata +###### + +Show + Weapon True + Rarity Rare + Quality Normal + SetDescription {Description}{White}Upgrade to exceptional with {Orange}Ort Amn {Blue}0{White}Perfect{Newline} +Continue + +Show + Armor True + Rarity Rare + Quality Normal + SetDescription {Description}{White}Upgrade to exceptional with {Orange}Ral Thul {White}0{White}Perfect{Newline} +Continue + +Show + Weapon True + Rarity Rare + Quality Exceptional + SetDescription {Description}{White}Upgrade to elite with {Orange}Fal Um {Green}0{White}Perfect{Newline} +Continue + +Show + Armor True + Rarity Rare + Quality Exceptional + SetDescription {Description}{White}Upgrade to elite with {Orange}Ko Pul {White}0{White}Perfect{Newline} +Continue + +###### +# General Metadata +###### + +#Show +# Class Any Armor, Weapon, Ring, Amulet +# Rarity <= Magic +# Price 35000 +# SetName {Name} {Gray}+35k +#Continue + +Show + Rarity < Magic + SetName {White}{Name} Continue Show @@ -688,46 +712,401 @@ Continue ###### Show - Rune in Sur-Jah - SetStyle T1 Rune + Rune in Sur-Jah + SetStyle T1 Rune + +Show + Rune in Cham-Zod, in Vex-Lo + SetStyle T2 Rune + +Show + Rune in Mal-Gul + SetStyle T3 Rune + +Show + Rune in Lem-Um + SetStyle T4 Rune + + +Show + Rune in El-Fal + SetName {Orange}{Name} + +###### +# END Runes +###### +###### +# START Gems +###### + +#show chipped/flawed/normal gems if current char level is below 51 +Show + Class Gem + Class Chipped Gem, Flawed Gem, Standard Gem + CharacterLevel <= 50 + +#always show flawless/perf gems +Show + Class Gem + Class Flawless Gem, Perfect Gem + +###### +# END Gems +###### +###### +# START Shopping +###### +###### +# Shopping Tier 1 +###### + +# 3 trap, 3 LS claws (alvl 60+) +Show + Class Hand to Hand, Hand to Hand 2 + Rarity Magic + Stats "Assassin +%d to Traps" = 3 and "Lightning Sentry" = 3 + SetStyle T1 + +# 3 pal combat, 3 BH, 3 conc scepter (alvl 60+) +Show + Class Scepter + Rarity Magic + Stats "Paladin +%d to Combat Skills" = 3 and "Blessed Hammer" = 3 and "Concentration" = 3 + SetStyle T1 + +# 3 pal combat, 3 FoH, 3 conviction scepter (alvl 60+) +Show + Class Scepter + Rarity Magic + Stats "Paladin +%d to Combat Skills" = 3 and "Fist of the Heavens" = 3 and "Conviction" = 3 + SetStyle T1 + +###### +# Shopping Tier 2 +###### + +# 3/20 Javazon Gloves (alvl 60+) [nightmare anya] +Show + Class Gloves + Rarity Magic + Ethereal False + Stats "Amazon +%d to Javelin and Spear Skills" = 3 and "Increased Attack Speed" >= 20 + SetStyle T2 + +# 4os archon plate with life (of the whale, alvl 50) [hell Larzuk] +Show + Type Archon Plate + Rarity Magic + Ethereal False + Sockets 4 + Stats "Life" >= 80 + SetStyle T2 + +# 2 sin, 3 LS claws (alvl 50+) +Show + Class Hand to Hand, Hand to Hand 2 + Rarity Magic + Stats "Assassin Skills" >= 2 and "Lightning Sentry" = 3 + SetStyle T2 + +# 3 trap, 2+ LS claws (alvl 60+) +Show + Class Hand to Hand, Hand to Hand 2 + Rarity Magic + Stats "Assassin +%d to Traps" = 3 and "Lightning Sentry" >= 2 + SetStyle T2 + +# 3 shadow, 3 venom claws (alvl 60+) +Show + Class Hand to Hand, Hand to Hand 2 + Rarity Magic + Stats "Assassin +%d to Shadow Disciplines" = 3 and "Venom" = 3 + SetStyle T2 + +# 2 pal, 3 FoH, 3 conviction scepter (alvl 50+) +Show + Class Scepter + Rarity Magic + Stats "Paladin Skills" = 2 and "Fist of the Heavens" = 3 and "Conviction" = 3 + SetStyle T2 + +###### +# Shopping Tier 3 +###### + +# 2 trap, 3 LS claws (alvl 40+) +Show + Class Hand to Hand, Hand to Hand 2 + Rarity Magic + Stats "Assassin +%d to Traps" >= 2 and "Lightning Sentry" = 3 + SetStyle T3 + +# 2 sin, 3 venom claws (alvl 50+) +Show + Class Hand to Hand, Hand to Hand 2 + Rarity Magic + Stats "Assassin Skills" = 2 and "Venom" = 3 + SetStyle T3 + +# 3 shadow, 2+ venom claws (alvl 60+) +Show + Class Hand to Hand, Hand to Hand 2 + Rarity Magic + Stats "Assassin +%d to Shadow Disciplines" = 3 and "Venom" >= 2 + SetStyle T3 + +# 2 shadow, 3 venom claws (alvl 40+) +Show + Class Hand to Hand, Hand to Hand 2 + Rarity Magic + Stats "Assassin +%d to Shadow Disciplines" = 2 and "Venom" = 3 + SetStyle T3 + +###### +# Shopping Tier 4 +###### + +# 4os gothic plates and ancient armor with life (of the whale, alvl 50) [hell charsi] +Show + Type Gothic Plate, Ancient Armor + Rarity Magic + Sockets 4 + Ethereal False + Stats "Life" >= 80 + SetStyle T4 + +# 2/20 jav gloves (alvl 40+) [nightmare anya] +Show + Class Gloves + Rarity Magic + Ethereal False + Stats "Amazon +%d to Javelin and Spear Skills" >= 2 and "Increased Attack Speed" >= 20 + SetStyle T4 + +# 3os shields of deflecting (alvl 33+) +# tower, gothic, dragon, blade barrier, troll nest +Show + Type Tower Shield, Gothic Shield, Dragon Shield, Blade Barrier, Troll Nest + Rarity Magic + Ethereal False + Sockets 3 + Stats "Faster Block Rate" >= 30 + SetStyle T4 + +# 4os shields of deflecting (alvl 55+) +# aegis, ward +Show + Type Aegis, Ward + Rarity Magic + Ethereal False + Sockets 4 + Stats "Faster Block Rate" >= 30 + SetStyle T4 + +###### +# Shopping Tier 5 +###### + +# Lower res wand (need 1.9.8 or later) +Show + Class Wand + Rarity Magic + Stats ChargedSkill(91) + SetStyle T5 + +# Life tap wand (need 1.9.8 or later) +Show + Class Wand + Rarity Magic + Stats ChargedSkill(82) + SetStyle T5 + +# 3os helm with life (of the mammoth, no corona, spired helm, winged helm) +# Artisan's, alvl 33 +# of the Mammoth, alvl 68 +Show + Class Helm + Type != Corona + Type != Spired Helm + Type != Winged Helm + Rarity Magic + Sockets 3 + Stats "Life" >= 30 + SetStyle T5 + +# BO Sticks (+3 WC sticks) +# Echoing, alvl 60 +Show + Class Weapon + Rarity Magic + Stats "Barbarian +%d to Warcries" = 3 + SetStyle T5 + +# Javazon Gloves (+3) +# Lancer's, alvl 60 +Show + Class Gloves + Rarity Magic + Stats "Amazon +%d to Javelin and Spear Skills" = 3 + SetStyle T5 + +# Claws (+3 traps) +# Cunning, alvl 60 +Show + Class Hand to Hand, Hand to Hand 2 + Rarity Magic + Stats "Assassin +%d to Traps" = 3 + SetStyle T5 + +# 3 pal combat scepters +# Rose Branded, alvl 60 +Show + Class Scepter + Rarity Magic + Stats "Paladin +%d to Combat Skills" = 3 + SetStyle T5 + +# 2 pal scepters +# Priests, alvl 50 +Show + Class Scepter + Rarity Magic + Stats "Paladin Skills" = 2 + SetStyle T5 + +###### +# Shopping Tier 6 +###### + +# ilvl restrictions below prevent these items from showing as drops later in the game. +# Note white items cannot be bought from vendors with ilvl>24. + +# Tele staff (SK=54, lvl=1-6, formula SK*64+lvl) +# of Teleportation, alvl 24 +# Teleport staff (need 1.9.8 or later) +Show + Class Staff + Rarity Magic + Stats ChargedSkill(54) + SetStyle T6 + +# 3 warmth, 2os staff (leaf) [normal Akara at clvl<7] +# 3 fire ball, 2os staff (leaf) [normal Akara at clvl 7+, Drognan at clvl 14+] +# 3 enchant, 2os staff (leaf) [normal Drognan at clvl 14+] +Show + Class Staff + Rarity < Magic + Runeword False + Sockets 2 + Stats "Warmth" = 3 or "Fire Ball" = 3 or "Enchant" = 3 + SetStyle T6 + +Show + Type Short Staff + Rarity < Magic + Runeword False + Sockets 0, 2 + Stats "Warmth" = 3 or "Fire Ball" = 3 or "Enchant" = 3 + SetStyle T6 + +# 3 bone spear white base [normal Drognan at clvl 14+] +# 3 CE white base [normal Drognan at clvl 13] +Show + Class Wand + Rarity < Magic + Runeword False + Sockets 0, 2 + Stats "Bone Spear" = 3 or "Corpse Explosion" = 3 + SetStyle T6 + + +# alvl restrictions below prevent the items from being highlighted when higher tier +# shopping targets are available. For example, don't show mf/res gloves once +3 jav +# is available. Likely you don't care about seeing this crap at that point. + +# Prismatic, single res, or socketed shield of deflection (alvl 33 - 3 sockets available) +# of Deflecting, alvl 11 +# Mechanic's, alvl 10 +# Cobalt/Garnet/Coral, alvl 18 +# prismatic, alvl 28 +Show + Class Shield + Rarity Magic + Stats ("All Resist" > 15 or "Any Resist" > 20 or "Sockets" = 2) and "Faster Block Rate" >= 30 + SetStyle T6 +# FRW boots w/ res +# of Haste, alvl 22 +# Sapphire/Ruby/Amber, alvl 25 Show - Rune in Cham-Zod, in Vex-Lo - SetStyle T2 Rune + Class Boots + Rarity Magic + Stats "Any Resist" > 30 and "Faster Run Walk" >= 20 + SetStyle T6 + +# MF, str, or dex gloves w/ res (alvl 60 - 3 jav available) +# of Fortune, alvl 16 +# of Precision, alvl 56 +# of the Ox, alvl 45 +# Cobalt/Garnet/Coral, alvl 18 Show - Rune in Mal-Gul - SetStyle T3 Rune + Class Gloves + Rarity Magic + Stats ("Magic Find" > 15 or "Dexterity" > 9 or "Strength" > 5) and ("Any Resist" > 20) + SetStyle T6 +# Belt with life or fhr and res +# of Stability, alvl 18 +# of the Mammoth, alvl 25 +# Cobalt/Garnet/Coral, alvl 18 Show - Rune in Lem-Um - SetStyle T4 Rune - + Class Belt + Rarity Magic + Stats ("Faster Hit Recovery" = 24 or "Life" > 30) and ("Any Resist" > 20) + SetStyle T6 +# Belt with 81-100 life +# of the Whale, alvl 50 +# #ItemDisplay[MAG BELT LIFE>80]: %GRAY%T6%MAP% %PURPLE%%NAME%%TIER-6% Show - Rune in El-Fal - SetName {Orange}{Name} + Class Belt + Rarity Magic + Stats "Life" > 80 + SetStyle T6 -###### -# END Runes -###### -###### -# START Gems -###### +# FHR or life armor w/ res or sockets (alvl 55 - 4 sockets available) +# of Stability, alvl 18 +# of the Colossus, alvl 30 +# Cobalt/Garnet/Coral, alvl 18 +# Mechanic's, alvl 10 +# Artisan's, alvl 33 +Show + Class Armor + Rarity Magic + Stats ("Faster Hit Recovery" = 24 or "Life" > 40) and ("Any Resist" > 20 or "Sockets" >= 2) + SetStyle T6 -#show chipped/flawed/normal gems if current char level is below 51 +# helm with fhr and res or sockets (alvl 33 - 3 sockets available) +# of Balance, alvl 5 +# Cobalt/Garnet/Coral, alvl 18 +# Mechanic's, alvl 10 Show - Class Gem - Class Chipped Gem, Flawed Gem, Standard Gem - CharacterLevel <= 50 + Class Helm + Rarity Magic + Stats "Faster Hit Recovery" > 9 and ("Any Resist" > 20 or "Sockets" = 2) + SetStyle T6 -#always show flawless/perf gems +# 3 socket helm (alvl 68 - 31-40 life available) +# Artisan's, alvl 33 (also need ilvl 41+ base) Show - Class Gem - Class Flawless Gem, Perfect Gem + Class Helm + Rarity Magic + Sockets 3 + SetStyle T6 ###### -# END Gems +# END Shopping ###### ###### # START Uniques and Sets @@ -830,7 +1209,7 @@ Show # Eschuta's Temper, Sandstorm Trek, Azurewrath, Magefist, Silkweave # Valkyrie Wing, Razortail, Skullder's Ire Show - Type Swirling Crystal, Elder Staff, Gilded Shield, Cermonial Javelin, Serpentskin Armor, War Boots, Demonhide Sash, Vampirebone Gloves, Slayer Guard, War Belt, Mithril Coil, Vampirefang Belt, Monarch, Bone Knife, Grand Charm, Matriarchal Javelin, Thresher, Balrog Skin, Eldritch Orb, Scarabshell Boots, Phase Blade, Light Gauntlets, Silkweave, Winged Helm, Sharkskin Belt, Russet Armor + Type Swirling Crystal, Elder Staff, Gilded Shield, Ceremonial Javelin, Serpentskin Armor, War Boots, Demonhide Sash, Vampirebone Gloves, Slayer Guard, War Belt, Mithril Coil, Vampirefang Belt, Monarch, Bone Knife, Grand Charm, Matriarchal Javelin, Thresher, Balrog Skin, Eldritch Orb, Scarabshell Boots, Phase Blade, Light Gauntlets, Mesh Boots, Winged Helm, Sharkskin Belt, Russet Armor Rarity Unique SetStyle T4 @@ -895,22 +1274,260 @@ Show SetStyle T6 ###### -# Identified Uniques and Sets +# Uniques and Sets Tierless/Whitelist +###### + +Show + Rarity Unique + SetStyle T6 + +Show + Rarity Set + SetStyle T6 + +###### +# END Uniques and Sets +###### +###### +# START Rares Tiers +###### +###### +# Rares Tier 1 +###### +###### +# Rares Tier 2 +###### +###### +# Rares Tier 3 +###### +###### +# Rares Tier 4 ###### -# todo -# different styling for soj/maras/etc... +# Rings, Amulets, Jewels, & Circlets +Show + Type Amulet, Ring, Jewel, Circlet + Rarity Rare + Identified False + SetStyle T4 ###### -# Whitelist All Uniques and Sets +# Rares Tier 5 ###### +# Boots Show - Rarity Set, Unique + Class Boots + Rarity Rare + Identified False + SetStyle T4 +# Gloves +Show + Class Gloves + Rarity Rare + Identified False + SetStyle T4 ###### -# END Uniques and Sets +# Rares Tier 6 +###### +###### +# Rares Tierless/Whitelist +###### + +# rare claws capable of spawning staffmods +Show + Class Hand to Hand, Hand to Hand 2 + Rarity Rare + Identified False + Stats "Quality Level" >= 40 + +# Druid Pelts +#ItemDisplay[RARE DRU !ID]: %WHITE%+ %NAME% +Show + Class Pelt + Rarity Rare + Identified False + +# eth rare claws +Show + Class Hand to Hand, Hand to Hand 2 + Rarity Rare + Ethereal True + Identified False + +# Rare eth weapons capable of cruel + master's (alvl 55+) +Show + Class Weapon + Rarity Rare + Ethereal True + Identified False + Stats "Affix Level" >= 55 + +# Rare eth armor capable of 200 ED (Godly, alvl 50+) +#ItemDisplay[RARE ETH !ID CHEST ALVL>49]: %NAME% +Show + Class Armor + Rarity Rare + Ethereal True + Identified False + Stats "Affix Level" >= 50 + +# Sorc Orbs (for +5 orbs) +Show + Class Orb + Rarity Rare + Identified False + +# Barb Hats (for +5 BO) +Show + Class Primal Helm + Rarity Rare + Identified False + +# Amazon javs (for 4/40s) +Show + Type Maiden Javelin, Ceremonial Javelin, Matriarchal Javelin + Rarity Rare + Identified False + +# Amazon bows +Show + Type Stag Bow, Reflex Bow, Ashwood Bow, Ceremonial Bow, Matriarchal Bow, Grand Matron Bow + Rarity Rare + Identified False + +# Scepters (for +5 hammer/FoH) +Show + Class Scepter + Rarity Rare + Identified False + +# Eth rares +Show + Ethereal True + Rarity Rare + Identified False + +###### +# END Rares Tiers +###### +###### +# START Magic Items +###### +###### +# Magic Items Tier 4 +###### + +# Circlet Variants (many good things) +Show + Type Circlet + Rarity Magic + Ethereal False + Identified False + SetStyle T4 + +# Small charms and Grand charms +Show + Type Small Charm, Grand Charm + Rarity Magic + Identified False + SetStyle T4 + +# Jewels +Show + Type Jewel + Rarity Magic + Identified False + SetStyle T4 + +# JMoD? +Show + Type Monarch + Rarity Magic + Ethereal False + Identified False + SetStyle T4 + +###### +# Magic Items Tier 6 +###### +# Barb Hats (for +6 BO) +Show + Class Primal Helm + Rarity Magic + Identified False + SetStyle T6 + +# Assassin katars capable of staffmods (for +6 LS) +Show + Class Hand to Hand, Hand to Hand 2 + Rarity Magic + Identified False + SetStyle T6 + +# Rings and Amulets +Show + Type Amulet, Ring + Rarity Magic + Identified False + SetStyle T6 + +# Sorc Orbs (for +6 orbs) +Show + Class Orb + Rarity Magic + Identified False + SetStyle T6 + +# Druid Pelts (for +6 nado) +Show + Class Pelt + Rarity Magic + Ethereal False + Identified False + SetStyle T6 + +# Amazon javs (for +6 jav) +Show + Type Maiden Javelin, Ceremonial Javelin, Matriarchal Javelin + Rarity Magic + Ethereal False + Identified False + SetStyle T6 + +# Elite light armor (for 4os/life) +Show + Type Dusk Shroud, Wyrmhide, Scarab Husk, Wire Fleece, Great Hauberk, Archon Plate + Rarity Magic + Ethereal False + Identified False + SetStyle T6 + +# Large Charms +Show + Type Large Charm + Rarity Magic + Identified False + SetStyle T6 + +# Amazon javs (for +6 jav) +Show + Class Scepter + Rarity Magic + Identified False + SetStyle T6 + +# Whitelist common crafting bases except in aggressive mode +Show + Type Sharkskin Gloves, Heavy Bracers, Demonhide Boots, Sharkskin Belt, Mesh Belt, Vampirebone Gloves, Vambraces, Wyrmhide Boots, Vampirefang Belt, Mithril Coil + Rarity Magic + Identified False + SetStyle T6 + +###### +# END Magic Items ###### ###### # START Socketables @@ -1698,7 +2315,7 @@ Show # 4os phase blade (Passion) Show - Typwe Phase Blade + Type Phase Blade Rarity < Magic Runeword False Sockets 4 @@ -1906,7 +2523,7 @@ Show # 4os Eth Oath Bases (Oath) Show - Type Berserker Axe, Colossus Sword, Colossus Blade, Scourge, Ettin Axe, War Spike, Silver-edged Axe, Decapitator, Champion Axe, Glorious Axe + Type Berserker Axe, Colossus Sword, Colossus Blade, Scourge, Ettin Axe, War Spike, "Silver-edged Axe", Decapitator, Champion Axe, Glorious Axe Rarity < Magic Runeword False Ethereal True @@ -2038,28 +2655,34 @@ Show ###### # START Misc ###### +###### +# Misc Tier 1 +###### +###### +# Misc Tier 2 +###### +###### +# Misc Tier 3 +###### +###### +# Misc Tier 4 +###### + Show Type Key of Terror, Key of Hate, Key of Destruction SetStyle T4 -Show - Type Wirt's Leg - SetStyle T4 - ###### -# END Misc +# Misc Tier 5 ###### ###### -# Magic/Rares/Misc... +# Misc Tier 6 +###### +###### +# Misc Tierless/Whitelist ###### - - Show - Type Small Charm - Rarity Magic - Stats "Life" > 15 and "Any Resist" - SetInventoryColor Green -Continue + Type Wirt's Leg #Comment to hide large charms Show @@ -2077,5 +2700,8 @@ Show Show Runeword True -#Hide anything not specifically included +###### +# END Misc +###### + Hide diff --git a/vcpkg.json b/vcpkg.json deleted file mode 100644 index a0109dd..0000000 --- a/vcpkg.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "name": "d2lootfilter", - "version-string": "0.0.1", - "dependencies": [ - "minhook", - "fmt" - ] - } \ No newline at end of file