diff --git a/src/Client/WarFare/CMakeLists.txt b/src/Client/WarFare/CMakeLists.txt index 2b177188d..1bbcfeb12 100644 --- a/src/Client/WarFare/CMakeLists.txt +++ b/src/Client/WarFare/CMakeLists.txt @@ -89,6 +89,8 @@ set(WARFARE_SCENE_INGAME_UI_SOURCES UIInn.h UIInventory.cpp UIInventory.h + UIItemUpgrade.cpp + UIItemUpgrade.h UIKnightsOperation.cpp UIKnightsOperation.h UILevelGuide.cpp diff --git a/src/Client/WarFare/GameDef.h b/src/Client/WarFare/GameDef.h index cdd50d922..146f3dd90 100644 --- a/src/Client/WarFare/GameDef.h +++ b/src/Client/WarFare/GameDef.h @@ -382,15 +382,20 @@ enum e_PlugPosition : int8_t enum e_ItemAttrib : int8_t { - ITEM_ATTRIB_GENERAL = 0, - ITEM_ATTRIB_MAGIC = 1, - ITEM_ATTRIB_LAIR = 2, - ITEM_ATTRIB_CRAFT = 3, - ITEM_ATTRIB_UNIQUE = 4, - ITEM_ATTRIB_UPGRADE = 5, - ITEM_ATTRIB_UNIQUE_REVERSE = 11, - ITEM_ATTRIB_UPGRADE_REVERSE = 12, - ITEM_ATTRIB_UNKNOWN = -1 + ITEM_ATTRIB_GENERAL = 0, + ITEM_ATTRIB_MAGIC = 1, + ITEM_ATTRIB_LAIR = 2, + ITEM_ATTRIB_CRAFT = 3, + ITEM_ATTRIB_UNIQUE = 4, + ITEM_ATTRIB_UPGRADE = 5, + ITEM_ATTRIB_UNKNOWN = -1 +}; + +enum e_ItemEffect2 : uint8_t +{ + ITEM_EFFECT2_RING_UPGRADE_REQ = 253, + ITEM_EFFECT2_RENTAL_SCROLL = 254, + ITEM_EFFECT2_ITEM_UPGRADE_REQ = 255 }; enum e_ItemGrade : uint8_t diff --git a/src/Client/WarFare/GameProcMain.cpp b/src/Client/WarFare/GameProcMain.cpp index 784d9bbfe..777fc29b3 100644 --- a/src/Client/WarFare/GameProcMain.cpp +++ b/src/Client/WarFare/GameProcMain.cpp @@ -52,6 +52,7 @@ #include "UIQuestTalk.h" #include "UIDead.h" #include "UIUpgradeSelect.h" +#include "UIItemUpgrade.h" #include "UILevelGuide.h" #include "UIMsgBoxOkCancel.h" @@ -139,6 +140,7 @@ CGameProcMain::CGameProcMain() // r기본 생성자.. 각 변수의 역활 m_pUIQuestTalk = new CUIQuestTalk(); m_pUIDead = new CUIDead(); m_pUIUpgradeSelect = new CUIUpgradeSelect(); + m_pUIItemUpgrade = new CUIItemUpgrade(); m_pUILevelGuide = new CUILevelGuide(); m_pSubProcPerTrade = new CSubProcPerTrade(); @@ -189,6 +191,7 @@ CGameProcMain::~CGameProcMain() delete m_pUIQuestTalk; delete m_pUIDead; delete m_pUIUpgradeSelect; + delete m_pUIItemUpgrade; delete m_pUILevelGuide; delete m_pSubProcPerTrade; @@ -242,6 +245,7 @@ void CGameProcMain::ReleaseUIs() m_pUIInn->Release(); m_pUICreateClanName->Release(); m_pUIUpgradeSelect->Release(); + m_pUIItemUpgrade->Release(); m_pUILevelGuide->Release(); CN3UIBase::DestroyTooltip(); @@ -4221,6 +4225,14 @@ void CGameProcMain::InitUI() m_pUIUpgradeSelect->SetState(UI_STATE_COMMON_NONE); m_pUIUpgradeSelect->SetStyle(m_pUIUpgradeSelect->GetStyle() | UISTYLE_USER_MOVE_HIDE | UISTYLE_SHOW_ME_ALONE); + m_pUIItemUpgrade->Init(s_pUIMgr); + m_pUIItemUpgrade->LoadFromFile(pTbl->szItemUpgrade); + m_pUIItemUpgrade->SetVisibleWithNoSound(false); + rc = m_pUIItemUpgrade->GetRegion(); + m_pUIItemUpgrade->SetPos(iW - (rc.right - rc.left), 10); + m_pUIItemUpgrade->SetState(UI_STATE_COMMON_NONE); + m_pUIItemUpgrade->SetStyle(UISTYLE_USER_MOVE_HIDE | UISTYLE_SHOW_ME_ALONE); + //ui level guide m_pUILevelGuide->Init(s_pUIMgr); m_pUILevelGuide->LoadFromFile(pTbl->szLvlGuide); @@ -5778,6 +5790,15 @@ void CGameProcMain::MsgRecv_ObjectEvent(Packet& pkt) } break; + case OBJECT_TYPE_ANVIL: + { + /* + if (iResult == 0) // anvil object failed animation + if (iResult == 1) // anvil object succeeded animation + */ + } + break; + default: __ASSERT(0, "Unknown Object Event"); } @@ -8001,14 +8022,12 @@ void CGameProcMain::MsgRecv_ItemUpgrade(Packet& pkt) } break; - // NOLINTNEXTLINE(bugprone-branch-clone) case ITEM_UPGRADE_PROCESS: -#if 0 // TODO if (m_pUIItemUpgrade != nullptr) m_pUIItemUpgrade->MsgRecv_ItemUpgrade(pkt); -#endif break; + // NOLINTNEXTLINE(bugprone-branch-clone) case ITEM_UPGRADE_ACCESSORIES: #if 0 // TODO if (m_pUIRingUpgrade != nullptr) diff --git a/src/Client/WarFare/GameProcMain.h b/src/Client/WarFare/GameProcMain.h index 6d6f0dffd..7d566f06c 100644 --- a/src/Client/WarFare/GameProcMain.h +++ b/src/Client/WarFare/GameProcMain.h @@ -64,6 +64,7 @@ class CGameProcMain : public CGameProcedure class CUITradeBBSEditDlg* m_pUITradeBBSEdit; // 상거래 게시물 설명 class CUIUpgradeSelect* m_pUIUpgradeSelect; + class CUIItemUpgrade* m_pUIItemUpgrade; class CUILevelGuide* m_pUILevelGuide; class CN3Shape* m_pTargetSymbol; // 플레이어가 타겟으로 잡은 캐릭터의 위치위에 그리면 된다.. diff --git a/src/Client/WarFare/IconItemSkill.cpp b/src/Client/WarFare/IconItemSkill.cpp index 37b9e098b..2911c28a3 100644 --- a/src/Client/WarFare/IconItemSkill.cpp +++ b/src/Client/WarFare/IconItemSkill.cpp @@ -1,15 +1,17 @@ #include "StdAfx.h" #include "IconItemSkill.h" #include "GameDef.h" +#include "N3UIIcon.h" +#include "N3UIWndBase.h" __IconItemSkill::__IconItemSkill() { + pUIIcon = nullptr; pItemBasic = nullptr; pItemExt = nullptr; iCount = 0; iDurability = 0; pSkill = nullptr; - pUIIcon = nullptr; } int __IconItemSkill::GetItemID() const @@ -55,3 +57,49 @@ int __IconItemSkill::GetSellPrice(bool bHasPremium /*= false*/) const return iSellPrice; } + +bool __IconItemSkill::IsStackable() const +{ + if (pItemBasic == nullptr) + return false; + + return pItemBasic->byContable == UIITEM_TYPE_COUNTABLE || pItemBasic->byContable == UIITEM_TYPE_COUNTABLE_SMALL; +} + +void __IconItemSkill::CreateIcon(const std::string& szFN, CN3UIBase* pParent, uint32_t dwStyle, float fUVAspect) +{ + szIconFN = szFN; + + delete pUIIcon; + pUIIcon = new CN3UIIcon(); + pUIIcon->Init(pParent); + + pUIIcon->SetTex(szFN); + pUIIcon->SetUVRect(0, 0, fUVAspect, fUVAspect); + pUIIcon->SetStyle(dwStyle); + pUIIcon->SetVisible(true); +} + +__IconItemSkill* __IconItemSkill::Clone(CN3UIBase* pParent) +{ + __IconItemSkill* spItemNew = new __IconItemSkill(); + + spItemNew->szIconFN = szIconFN; + spItemNew->pItemBasic = pItemBasic; + spItemNew->pItemExt = pItemExt; + spItemNew->pSkill = pSkill; + spItemNew->iCount = iCount; + spItemNew->iDurability = iDurability; + + if (pUIIcon != nullptr) + { + spItemNew->pUIIcon = new CN3UIIcon(); + spItemNew->pUIIcon->Init(pParent); + spItemNew->pUIIcon->SetTex(szIconFN); + spItemNew->pUIIcon->SetUVRect(*pUIIcon->GetUVRect()); + spItemNew->pUIIcon->SetStyle(pUIIcon->GetStyle()); + spItemNew->pUIIcon->SetVisible(pUIIcon->IsVisible()); + } + + return spItemNew; +} diff --git a/src/Client/WarFare/IconItemSkill.h b/src/Client/WarFare/IconItemSkill.h index 02e7f3756..ac799a9f1 100644 --- a/src/Client/WarFare/IconItemSkill.h +++ b/src/Client/WarFare/IconItemSkill.h @@ -3,9 +3,12 @@ #pragma once +#include + #include #include +class CN3UIBase; class CN3UIIcon; struct __TABLE_ITEM_BASIC; struct __TABLE_ITEM_EXT; @@ -29,9 +32,14 @@ struct __IconItemSkill }; __IconItemSkill(); + __IconItemSkill(const __IconItemSkill& src) = delete; int GetItemID() const; int GetBuyPrice() const; int GetSellPrice(bool bHasPremium = false) const; + bool IsStackable() const; + void CreateIcon(const std::string& szFN, CN3UIBase* pParent, uint32_t dwStyle = UISTYLE_ICON_ITEM | UISTYLE_ICON_CERTIFICATION_NEED, + float fUVAspect = 45.0f / 64.0f); + __IconItemSkill* Clone(CN3UIBase* pParent); }; #endif // CLIENT_WARFARE_ICONITEMSKILL_H diff --git a/src/Client/WarFare/N3UIWndBase.h b/src/Client/WarFare/N3UIWndBase.h index 090c04a2a..6faaa3607 100644 --- a/src/Client/WarFare/N3UIWndBase.h +++ b/src/Client/WarFare/N3UIWndBase.h @@ -34,17 +34,20 @@ enum e_UIWND : uint8_t // District Info.. enum e_UIWND_DISTRICT : uint8_t { - UIWND_DISTRICT_INVENTORY_SLOT = 0, // Slot district of Inventory Wnd.. - UIWND_DISTRICT_INVENTORY_INV, // Inv district of Inventory Wnd.. - UIWND_DISTRICT_TRADE_NPC, // Transaction district of Transaction Wnd of Npc.. - UIWND_DISTRICT_PER_TRADE_MY, // My Transaction district of Per Transaction Wnd.. - UIWND_DISTRICT_PER_TRADE_OTHER, // Other Transaction district of Per Transaction Wnd.. - UIWND_DISTRICT_DROPITEM, // Dropitem district of Drop item wnd.. - UIWND_DISTRICT_SKILL_TREE, // Skillicon district of Skill icon wnd.. - UIWND_DISTRICT_SKILL_HOTKEY, // Skillicon district of Hotkey icon wnd.. - UIWND_DISTRICT_TRADE_MY, // Npc 와의 거래에서 내 영역.. - UIWND_DISTRICT_PER_TRADE_INV, // Inv District of Per Trade Wnd .. - UIWND_DISTRICT_UNKNOWN, // District Unknown.. + UIWND_DISTRICT_INVENTORY_SLOT = 0, // Slot district of Inventory Wnd.. + UIWND_DISTRICT_INVENTORY_INV, // Inv district of Inventory Wnd.. + UIWND_DISTRICT_TRADE_NPC, // Transaction district of Transaction Wnd of Npc.. + UIWND_DISTRICT_PER_TRADE_MY, // My Transaction district of Per Transaction Wnd.. + UIWND_DISTRICT_PER_TRADE_OTHER, // Other Transaction district of Per Transaction Wnd.. + UIWND_DISTRICT_DROPITEM, // Dropitem district of Drop item wnd.. + UIWND_DISTRICT_SKILL_TREE, // Skillicon district of Skill icon wnd.. + UIWND_DISTRICT_SKILL_HOTKEY, // Skillicon district of Hotkey icon wnd.. + UIWND_DISTRICT_TRADE_MY, // Npc 와의 거래에서 내 영역.. + UIWND_DISTRICT_PER_TRADE_INV, // Inv District of Per Trade Wnd .. + UIWND_DISTRICT_UPGRADE_INV, // Upgrade Inv + UIWND_DISTRICT_UPGRADE_SLOT, // Upgrade material slot + UIWND_DISTRICT_UPGRADE_RESULT_SLOT, // Upgrade result slot + UIWND_DISTRICT_UNKNOWN, // District Unknown.. }; enum e_UIIconState : uint8_t diff --git a/src/Client/WarFare/UIImageTooltipDlg.cpp b/src/Client/WarFare/UIImageTooltipDlg.cpp index 38b714f36..cd4b89984 100644 --- a/src/Client/WarFare/UIImageTooltipDlg.cpp +++ b/src/Client/WarFare/UIImageTooltipDlg.cpp @@ -916,9 +916,7 @@ void CUIImageTooltipDlg::CalcTooltipStringNumAndWriteImpl(__IconItemSkill* spIte std::string szReduce; if (spItem->pItemExt->siNeedStrength < 0) { - if (spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE - || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE_REVERSE - || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE_REVERSE) + if (spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE) szReduce = fmt::format_text_resource(IDS_TOOLTIP_REDUCE); } @@ -943,9 +941,7 @@ void CUIImageTooltipDlg::CalcTooltipStringNumAndWriteImpl(__IconItemSkill* spIte std::string szReduce; if (spItem->pItemExt->siNeedStamina < 0) { - if (spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE - || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE_REVERSE - || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE_REVERSE) + if (spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE) szReduce = fmt::format_text_resource(IDS_TOOLTIP_REDUCE); } @@ -970,9 +966,7 @@ void CUIImageTooltipDlg::CalcTooltipStringNumAndWriteImpl(__IconItemSkill* spIte std::string szReduce; if (spItem->pItemExt->siNeedDexterity < 0) { - if (spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE - || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE_REVERSE - || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE_REVERSE) + if (spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE) szReduce = fmt::format_text_resource(IDS_TOOLTIP_REDUCE); } @@ -997,9 +991,7 @@ void CUIImageTooltipDlg::CalcTooltipStringNumAndWriteImpl(__IconItemSkill* spIte std::string szReduce; if (spItem->pItemExt->siNeedInteli < 0) { - if (spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE - || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE_REVERSE - || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE_REVERSE) + if (spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE) szReduce = fmt::format_text_resource(IDS_TOOLTIP_REDUCE); } @@ -1024,9 +1016,7 @@ void CUIImageTooltipDlg::CalcTooltipStringNumAndWriteImpl(__IconItemSkill* spIte std::string szReduce; if (spItem->pItemExt->siNeedMagicAttack < 0) { - if (spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE - || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE_REVERSE - || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE_REVERSE) + if (spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UNIQUE || spItem->pItemExt->byMagicOrRare == ITEM_ATTRIB_UPGRADE) szReduce = fmt::format_text_resource(IDS_TOOLTIP_REDUCE); } diff --git a/src/Client/WarFare/UIItemUpgrade.cpp b/src/Client/WarFare/UIItemUpgrade.cpp new file mode 100644 index 000000000..e843398dd --- /dev/null +++ b/src/Client/WarFare/UIItemUpgrade.cpp @@ -0,0 +1,1351 @@ +#include "StdAfx.h" +#include "UIItemUpgrade.h" +#include "APISocket.h" +#include "GameProcMain.h" +#include "IconItemSkill.h" +#include "LocalInput.h" +#include "N3UIIcon.h" +#include "N3UIWndBase.h" +#include "PlayerMySelf.h" +#include "UIImageTooltipDlg.h" +#include "UIInventory.h" +#include "UIManager.h" +#include "UIMsgBoxOkCancel.h" + +#include "text_resources.h" + +#include +#include +#include +#include + +#include +#include + +CUIItemUpgrade::CUIItemUpgrade() +{ + for (int i = 0; i < ANVIL_REQ_MAX; i++) + m_iRequirementSlotInvPos[i] = -1; +} + +CUIItemUpgrade::~CUIItemUpgrade() +{ + for (int i = 0; i < MAX_ITEM_INVENTORY; i++) + { + if (m_pMyUpgradeInv[i] == nullptr) + continue; + + // NOTE: As a child, this is freed by CN3UIBase + m_pMyUpgradeInv[i]->pUIIcon = nullptr; + delete m_pMyUpgradeInv[i]; + m_pMyUpgradeInv[i] = nullptr; + } +} + +void CUIItemUpgrade::Release() +{ + m_eAnimationState = AnimationState::None; + m_fAnimationTimer = 0.0f; + m_iCurrentFrame = 0; + m_bUpgradeSucceeded = false; + m_bUpgradeInProgress = false; + m_iNpcID = 0; + + m_rcCover1Original = {}; + m_rcCover2Original = {}; + + m_pBtnClose = nullptr; + m_pBtnOk = nullptr; + m_pBtnCancel = nullptr; + m_pStrMyGold = nullptr; + m_pAreaUpgrade = nullptr; + m_pAreaResult = nullptr; + + for (int i = 0; i < MAX_ITEM_INVENTORY; i++) + { + m_pAreaInv[i] = nullptr; + m_pStrInv[i] = nullptr; + + if (m_pMyUpgradeInv[i] != nullptr) + { + m_pMyUpgradeInv[i]->pUIIcon = nullptr; + delete m_pMyUpgradeInv[i]->pUIIcon; + m_pMyUpgradeInv[i] = nullptr; + } + } + + for (int i = 0; i < ANVIL_REQ_MAX; i++) + { + m_pSlotArea[i] = nullptr; + m_iRequirementSlotInvPos[i] = -1; + } + + for (int i = 0; i < FLIPFLOP_MAX_FRAMES; i++) + { + m_pImgFail[i] = nullptr; + m_pImgSuccess[i] = nullptr; + } + + m_pImageCover1 = nullptr; + m_pImageCover2 = nullptr; + + m_pUITooltipDlg = nullptr; + m_pUIMsgBoxOkCancel = nullptr; + + if (m_pItemBeingDragged != nullptr) + { + delete m_pItemBeingDragged->pUIIcon; + m_pItemBeingDragged->pUIIcon = nullptr; + delete m_pItemBeingDragged; + m_pItemBeingDragged = nullptr; + } + + m_iItemBeingDraggedSourcePos = -1; + m_iUpgradeItemSlotInvPos = -1; + + CN3UIBase::Release(); +} + +void CUIItemUpgrade::Tick() +{ + if (m_pImageCover1 != nullptr && m_pImageCover2 != nullptr) + { + switch (m_eAnimationState) + { + case AnimationState::Start: + StartUpgradeAnim(); + break; + + case AnimationState::FlipFlop: + UpdateFlipFlopAnimation(); + break; + + case AnimationState::Result: + if (m_bUpgradeSucceeded && m_iUpgradeItemSlotInvPos >= 0 && m_iUpgradeItemSlotInvPos < MAX_ITEM_INVENTORY) + { + __IconItemSkill* spItem = m_pMyUpgradeInv[m_iUpgradeItemSlotInvPos]; + if (spItem != nullptr && m_pAreaResult != nullptr) + { + spItem->pUIIcon->SetRegion(m_pAreaResult->GetRegion()); + spItem->pUIIcon->SetMoveRect(m_pAreaResult->GetRegion()); + } + } + + m_eAnimationState = AnimationState::CoverOpening; + break; + + case AnimationState::CoverOpening: + UpdateCoverAnimation(); + break; + + case AnimationState::None: + m_fAnimationTimer = 0.0f; + m_iCurrentFrame = 0; + break; + + case AnimationState::Done: + break; + } + } + + CN3UIBase::Tick(); +} + +void CUIItemUpgrade::Render() +{ + if (!m_bVisible) + return; + + POINT ptCur = CGameProcedure::s_pLocalInput->MouseGetPos(); + + if (m_pUITooltipDlg != nullptr) + m_pUITooltipDlg->DisplayTooltipsDisable(); + + __IconItemSkill* spItemTooltip = nullptr; + + for (auto itor = m_Children.rbegin(); m_Children.rend() != itor; ++itor) + { + CN3UIBase* pChild = *itor; + if (GetState() == UI_STATE_ICON_MOVING && pChild->UIType() == UI_TYPE_ICON && m_pItemBeingDragged != nullptr + && pChild == m_pItemBeingDragged->pUIIcon) + continue; + + pChild->Render(); + + if (GetState() == UI_STATE_COMMON_NONE && pChild->UIType() == UI_TYPE_ICON && (pChild->GetStyle() & UISTYLE_ICON_HIGHLIGHT)) + spItemTooltip = GetHighlightIconItem(pChild); + } + + if (GetState() == UI_STATE_ICON_MOVING && m_pItemBeingDragged != nullptr && m_pItemBeingDragged->pUIIcon != nullptr) + m_pItemBeingDragged->pUIIcon->Render(); + + for (int i = 0; i < MAX_ITEM_INVENTORY; i++) + { + CN3UIString* pStr = m_pStrInv[i]; + if (pStr == nullptr) + continue; + + if (m_pMyUpgradeInv[i] == nullptr || m_pMyUpgradeInv[i]->iCount <= 0 || !m_pMyUpgradeInv[i]->IsStackable()) + continue; + + pStr->SetVisibleWithNoSound(true); + pStr->SetStringAsInt(m_pMyUpgradeInv[i]->iCount); + pStr->Render(); + pStr->SetVisibleWithNoSound(false); + } + + if (spItemTooltip != nullptr) + m_pUITooltipDlg->DisplayTooltipsEnable(ptCur.x, ptCur.y, spItemTooltip, false, false); +} + +__IconItemSkill* CUIItemUpgrade::GetHighlightIconItem(CN3UIBase* pUIIcon) const +{ + for (int i = 0; i < ANVIL_REQ_MAX; i++) + { + if (m_pRequirementSlot[i] != nullptr && m_pRequirementSlot[i]->pUIIcon == pUIIcon) + return m_pRequirementSlot[i]; + } + + for (int i = 0; i < MAX_ITEM_INVENTORY; i++) + { + if (m_pMyUpgradeInv[i] != nullptr && m_pMyUpgradeInv[i]->pUIIcon == pUIIcon) + return m_pMyUpgradeInv[i]; + } + + return nullptr; +} + +bool CUIItemUpgrade::GetSelectedIconInfo(CN3UIBase* pUIIcon, int* piOrder, e_UIWND_DISTRICT* peDistrict, __IconItemSkill** pspItem) const +{ + if (pUIIcon == nullptr || m_pAreaResult == nullptr || m_pAreaUpgrade == nullptr) + return false; + + for (int i = 0; i < ANVIL_REQ_MAX; i++) + { + __IconItemSkill* spItem = m_pRequirementSlot[i]; + if (spItem == nullptr || spItem->pUIIcon != pUIIcon) + continue; + + if (piOrder != nullptr) + *piOrder = i; + + if (peDistrict != nullptr) + *peDistrict = UIWND_DISTRICT_UPGRADE_SLOT; + + if (pspItem != nullptr) + *pspItem = spItem; + + return true; + } + + for (int i = 0; i < MAX_ITEM_INVENTORY; i++) + { + __IconItemSkill* spItem = m_pMyUpgradeInv[i]; + if (spItem == nullptr || spItem->pUIIcon != pUIIcon) + continue; + + if (piOrder != nullptr) + *piOrder = i; + + if (peDistrict != nullptr) + { + // Hacky result slot check; this should be placed, so it should match. + // Ideally this would just be assigned to an icon slot and not need to do any of this. + const RECT rcIcon = spItem->pUIIcon->GetRegion(); + const RECT rcResult = m_pAreaResult->GetRegion(); + const RECT rcUpgrade = m_pAreaUpgrade->GetRegion(); + if (rcIcon.left == rcResult.left && rcIcon.top == rcResult.top && rcIcon.right == rcResult.right + && rcIcon.bottom == rcResult.bottom) + *peDistrict = UIWND_DISTRICT_UPGRADE_RESULT_SLOT; + else if (rcIcon.left == rcUpgrade.left && rcIcon.top == rcUpgrade.top && rcIcon.right == rcUpgrade.right + && rcIcon.bottom == rcUpgrade.bottom) + *peDistrict = UIWND_DISTRICT_UPGRADE_SLOT; + else + *peDistrict = UIWND_DISTRICT_UPGRADE_INV; + } + + if (pspItem != nullptr) + *pspItem = spItem; + + return true; + } + + if (piOrder != nullptr) + *piOrder = -1; + + if (peDistrict != nullptr) + *peDistrict = UIWND_DISTRICT_UNKNOWN; + + if (pspItem != nullptr) + *pspItem = nullptr; + + return false; +} + +void CUIItemUpgrade::SetNpcID(int iNpcID) +{ + m_iNpcID = iNpcID; +} + +void CUIItemUpgrade::GoldUpdate() +{ + if (m_pStrMyGold == nullptr) + return; + + std::string formattedGold = CGameBase::FormatNumber(CGameBase::s_pPlayer->m_InfoExt.iGold); + m_pStrMyGold->SetString(formattedGold); +} + +void CUIItemUpgrade::CopyInventoryItems() +{ + CUIInventory* pInven = CGameProcedure::s_pProcMain->m_pUIInventory; + if (pInven == nullptr) + return; + + m_iUpgradeItemSlotInvPos = -1; + + for (int i = 0; i < ANVIL_REQ_MAX; i++) + m_iRequirementSlotInvPos[i] = -1; + + for (int i = 0; i < MAX_ITEM_INVENTORY; i++) + { + if (m_pMyUpgradeInv[i] != nullptr) + { + delete m_pMyUpgradeInv[i]->pUIIcon; + m_pMyUpgradeInv[i]->pUIIcon = nullptr; + delete m_pMyUpgradeInv[i]; + m_pMyUpgradeInv[i] = nullptr; + } + + __IconItemSkill* spItemInv = pInven->m_pMyInvWnd[i]; + if (spItemInv != nullptr) + { + __IconItemSkill* spItem = spItemInv->Clone(this); + + CN3UIArea* pArea = m_pAreaInv[i]; + if (pArea != nullptr && spItem->pUIIcon != nullptr) + { + spItem->pUIIcon->SetRegion(pArea->GetRegion()); + spItem->pUIIcon->SetMoveRect(pArea->GetRegion()); + } + + m_pMyUpgradeInv[i] = spItem; + } + } +} + +void CUIItemUpgrade::Close() +{ + bool bWork = IsVisible(); + SetVisibleWithNoSound(false, bWork, false); +} + +bool CUIItemUpgrade::ReceiveIconDrop() +{ + if (m_pItemBeingDragged == nullptr) + return false; + + POINT ptCur = CGameProcedure::s_pLocalInput->MouseGetPos(); + if (IsAllowedUpgradeItem(m_pItemBeingDragged)) + { + if (m_iUpgradeItemSlotInvPos == -1 && m_pAreaUpgrade->IsIn(ptCur.x, ptCur.y)) + { + if (m_pItemBeingDragged->pUIIcon != nullptr && m_pAreaUpgrade != nullptr) + { + m_pItemBeingDragged->pUIIcon->SetRegion(m_pAreaUpgrade->GetRegion()); + m_pItemBeingDragged->pUIIcon->SetMoveRect(m_pAreaUpgrade->GetRegion()); + } + + m_iUpgradeItemSlotInvPos = m_iItemBeingDraggedSourcePos; + return true; + } + } + else if (IsValidRequirementItem(m_pItemBeingDragged)) + { + for (int i = 0; i < ANVIL_REQ_MAX; i++) + { + CN3UIArea* pArea = m_pSlotArea[i]; + if (pArea == nullptr || !pArea->IsIn(ptCur.x, ptCur.y)) + continue; + + SetRequirementItemSlot(m_pItemBeingDragged, m_iItemBeingDraggedSourcePos, i); + return true; + } + } + + return false; +} + +void CUIItemUpgrade::CancelIconDrop() +{ + if (m_pItemBeingDragged == nullptr) + return; + + // If stackable, restore stack size in inventory + // Note that we assume this is from the inventory because it's the only draggable source. + RestoreInvItemStackSize(m_pMyUpgradeInv[m_iItemBeingDraggedSourcePos]); + + delete m_pItemBeingDragged->pUIIcon; + m_pItemBeingDragged->pUIIcon = nullptr; + delete m_pItemBeingDragged; + m_pItemBeingDragged = nullptr; + m_iItemBeingDraggedSourcePos = -1; +} + +uint32_t CUIItemUpgrade::MouseProc(uint32_t dwFlags, const POINT& ptCur, const POINT& ptOld) +{ + uint32_t dwRet = UI_MOUSEPROC_NONE; + + if (!m_bVisible) + return dwRet; + + if (GetState() == UI_STATE_ICON_MOVING && m_pItemBeingDragged != nullptr && m_pItemBeingDragged->pUIIcon != nullptr) + { + RECT region = GetSampleRect(); + m_pItemBeingDragged->pUIIcon->SetRegion(region); + m_pItemBeingDragged->pUIIcon->SetMoveRect(region); + + dwRet |= UI_MOUSEPROC_DONESOMETHING; + } + + return CN3UIBase::MouseProc(dwFlags, ptCur, ptOld) | dwRet; +} + +// Returns a rectangle centered at the mouse position, used for moving icons. +RECT CUIItemUpgrade::GetSampleRect() +{ + __ASSERT(m_pAreaResult, "m_pAreaResult not loaded"); + + if (m_pAreaResult == nullptr) + return {}; + + POINT ptCur = CGameProcedure::s_pLocalInput->MouseGetPos(); + RECT rect = m_pAreaResult->GetRegion(); + + float fWidth = (float) (rect.right - rect.left) * 0.5f; + float fHeight = (float) (rect.bottom - rect.top) * 0.5f; + rect.left = ptCur.x - (int) fWidth; + rect.right = ptCur.x + (int) fWidth; + rect.top = ptCur.y - (int) fHeight; + rect.bottom = ptCur.y + (int) fHeight; + return rect; +} + +e_UIWND_DISTRICT CUIItemUpgrade::GetWndDistrict(const POINT ptCur) const +{ + if (m_pAreaUpgrade != nullptr && m_pAreaUpgrade->IsIn(ptCur.x, ptCur.y)) + return UIWND_DISTRICT_UPGRADE_SLOT; + + if (m_pAreaResult != nullptr && m_pAreaResult->IsIn(ptCur.x, ptCur.y)) + return UIWND_DISTRICT_UPGRADE_RESULT_SLOT; + + for (int i = 0; i < ANVIL_REQ_MAX; i++) + { + if (m_pSlotArea[i] != nullptr && m_pSlotArea[i]->IsIn(ptCur.x, ptCur.y)) + return UIWND_DISTRICT_UPGRADE_SLOT; + } + + for (int i = 0; i < MAX_ITEM_INVENTORY; i++) + { + if (m_pAreaInv[i] != nullptr && m_pAreaInv[i]->IsIn(ptCur.x, ptCur.y)) + return UIWND_DISTRICT_UPGRADE_INV; + } + + return UIWND_DISTRICT_UNKNOWN; +} + +// Handles UI messages such as button clicks and icon events. +bool CUIItemUpgrade::ReceiveMessage(CN3UIBase* pSender, uint32_t dwMsg) +{ + if (pSender == nullptr) + return false; + + if (dwMsg == UIMSG_BUTTON_CLICK) + { + if (pSender == m_pBtnClose) + { + Close(); + } + else if (pSender == m_pBtnCancel) + { + if (!m_bUpgradeInProgress) + ResetUpgradeInventory(); + } + else if (pSender == m_pBtnOk) + { + if (!m_bUpgradeInProgress && m_pUIMsgBoxOkCancel != nullptr) + m_pUIMsgBoxOkCancel->ShowWindow(CHILD_UI_MSGBOX_OKCANCEL, this); + } + + return true; + } + + switch (dwMsg) + { + case UIMSG_ICON_DOWN_FIRST: + { + if (m_bUpgradeInProgress) + { + SetState(UI_STATE_COMMON_NONE); + return false; + } + + if (m_pItemBeingDragged != nullptr) + CancelIconDrop(); + + e_UIWND_DISTRICT eDistrict = UIWND_DISTRICT_UNKNOWN; + int iOrder = -1; + __IconItemSkill* spItem = nullptr; + + if (!GetSelectedIconInfo(pSender, &iOrder, &eDistrict, &spItem) || iOrder < 0) + { + SetState(UI_STATE_COMMON_NONE); + return false; + } + + if (eDistrict == UIWND_DISTRICT_UPGRADE_RESULT_SLOT) + ResetUpgradeInventory(); + + if (eDistrict == UIWND_DISTRICT_UPGRADE_INV) + { + if (m_bUpgradeSucceeded) + ResetUpgradeInventory(); + + m_iItemBeingDraggedSourcePos = iOrder; + m_pItemBeingDragged = spItem->Clone(this); + + ReduceInvItemStackSize(m_pItemBeingDragged); + + // Set icon region for moving. + RECT region = GetSampleRect(); + if (m_pItemBeingDragged->pUIIcon != nullptr) + { + m_pItemBeingDragged->pUIIcon->SetRegion(region); + m_pItemBeingDragged->pUIIcon->SetMoveRect(region); + } + } + else // if (eDistrict != UIWND_DISTRICT_UPGRADE_INV) + { + SetState(UI_STATE_COMMON_NONE); + return false; + } + } + break; + + case UIMSG_ICON_UP: + if (m_pItemBeingDragged == nullptr) + break; + + if (!ReceiveIconDrop()) + { + // Restore the icon position to its original place if drop failed. + CancelIconDrop(); + } + + m_pItemBeingDragged = nullptr; + m_iItemBeingDraggedSourcePos = -1; + + SetState(UI_STATE_COMMON_NONE); + break; + + case UIMSG_ICON_RDOWN_FIRST: + if (!m_bUpgradeInProgress && !HandleInventoryIconRightClick(pSender)) + return false; + break; + + default: + break; + } + + return true; +} + +void CUIItemUpgrade::SetVisible(bool bVisible) +{ + CN3UIBase::SetVisible(bVisible); + + if (bVisible) + { + CancelIconDrop(); + CopyInventoryItems(); + GoldUpdate(); + CGameProcedure::s_pUIMgr->SetVisibleFocusedUI(this); + } + else + { + CGameProcedure::s_pUIMgr->ReFocusUI(); + } +} + +void CUIItemUpgrade::SetVisibleWithNoSound(bool bVisible, bool bWork, bool bReFocus) +{ + CN3UIBase::SetVisibleWithNoSound(bVisible, bWork, bReFocus); + + if (bVisible) + { + SetVisible(true); + } + else + { + if (m_pUITooltipDlg != nullptr) + m_pUITooltipDlg->DisplayTooltipsDisable(); + + if (GetState() == UI_STATE_ICON_MOVING) + CancelIconDrop(); + + // Reset the item's inventory area. + ResetUpgradeInventory(); + AnimClose(); + } + + if (bReFocus) + { + if (bVisible) + CGameProcedure::s_pUIMgr->SetVisibleFocusedUI(this); + else + CGameProcedure::s_pUIMgr->ReFocusUI(); + } +} + +// Loads the UI from file and initializes all required UI components. +bool CUIItemUpgrade::Load(File& file) +{ + if (!CN3UIBase::Load(file)) + return false; + + std::string szID; + + N3_VERIFY_UI_COMPONENT(m_pBtnClose, GetChildByID("btn_close")); + N3_VERIFY_UI_COMPONENT(m_pBtnOk, GetChildByID("btn_ok")); + N3_VERIFY_UI_COMPONENT(m_pBtnCancel, GetChildByID("btn_cancel")); + N3_VERIFY_UI_COMPONENT(m_pAreaUpgrade, GetChildByID("a_upgrade")); + N3_VERIFY_UI_COMPONENT(m_pAreaResult, GetChildByID("a_result")); + N3_VERIFY_UI_COMPONENT(m_pImageCover1, GetChildByID("img_cover_01")); + N3_VERIFY_UI_COMPONENT(m_pImageCover2, GetChildByID("img_cover_02")); + N3_VERIFY_UI_COMPONENT(m_pStrMyGold, GetChildByID("text_gold")); + // TODO: Implement this UI later + //N3_VERIFY_UI_COMPONENT(m_pBtnConversation, GetChildByID("btn_conversation")); + + if (m_pStrMyGold != nullptr) + m_pStrMyGold->SetString("0"); + + if (m_pImageCover1 != nullptr) + m_pImageCover1->SetVisible(false); + + if (m_pImageCover2 != nullptr) + m_pImageCover2->SetVisible(false); + + for (int i = 0; i < ANVIL_REQ_MAX; i++) + { + szID = fmt::format("a_m_{}", i); + N3_VERIFY_UI_COMPONENT(m_pSlotArea[i], GetChildByID(szID)); + } + + for (int i = 0; i < MAX_ITEM_INVENTORY; i++) + { + szID = fmt::format("a_slot_{}", i); + N3_VERIFY_UI_COMPONENT(m_pAreaInv[i], GetChildByID(szID)); + + szID = fmt::format("s_count_{}", i); + N3_VERIFY_UI_COMPONENT(m_pStrInv[i], GetChildByID(szID)); + + if (m_pStrInv[i] != nullptr) + m_pStrInv[i]->SetVisible(false); + } + + for (int i = 0; i < FLIPFLOP_MAX_FRAMES; i++) + { + szID = fmt::format("img_s_load_{}", i); + N3_VERIFY_UI_COMPONENT(m_pImgSuccess[i], GetChildByID(szID)); + + szID = fmt::format("img_f_load_{}", i); + N3_VERIFY_UI_COMPONENT(m_pImgFail[i], GetChildByID(szID)); + + if (m_pImgFail[i] != nullptr) + m_pImgFail[i]->SetVisible(false); + + if (m_pImgSuccess[i] != nullptr) + m_pImgSuccess[i]->SetVisible(false); + } + + __TABLE_UI_RESRC* pTbl = CGameBase::s_pTbl_UI.Find(CGameBase::s_pPlayer->Nation()); + __ASSERT(pTbl != nullptr, "__TABLE_UI_RESRC: missing nation"); + if (pTbl != nullptr) + { + if (m_pUITooltipDlg == nullptr) + m_pUITooltipDlg = new CUIImageTooltipDlg(); + + m_pUITooltipDlg->Init(this); + m_pUITooltipDlg->LoadFromFile(pTbl->szItemInfo); + m_pUITooltipDlg->InitPos(); + m_pUITooltipDlg->SetVisible(false); + + if (m_pUIMsgBoxOkCancel == nullptr) + m_pUIMsgBoxOkCancel = new CUIMsgBoxOkCancel(); + m_pUIMsgBoxOkCancel->Init(this); + m_pUIMsgBoxOkCancel->LoadFromFile(pTbl->szMsgBoxOkCancel); + m_pUIMsgBoxOkCancel->SetText(fmt::format_text_resource(IDS_ITEM_UPGRADE_CONFIRM)); + int iX = (m_rcRegion.right + m_rcRegion.left) / 2; + int iY = (m_rcRegion.bottom + m_rcRegion.top) / 2; + m_pUIMsgBoxOkCancel->SetPos(iX - (m_pUIMsgBoxOkCancel->GetWidth() / 2), iY - (m_pUIMsgBoxOkCancel->GetHeight() / 2) - 80); + m_pUIMsgBoxOkCancel->SetVisible(false); + } + + return true; +} + +// Handles key press events, such as closing the UI with ESC. +bool CUIItemUpgrade::OnKeyPress(int iKey) +{ + if (iKey == DIK_ESCAPE) + { + Close(); + return true; + } + + return CN3UIBase::OnKeyPress(iKey); +} + +// Restores the inventory and requirement slots. +void CUIItemUpgrade::ResetUpgradeInventory() +{ + if (m_iUpgradeItemSlotInvPos != -1) + { + __IconItemSkill* spItem = m_pMyUpgradeInv[m_iUpgradeItemSlotInvPos]; + CN3UIArea* pArea = m_pAreaInv[m_iUpgradeItemSlotInvPos]; + if (spItem != nullptr && spItem->pUIIcon != nullptr && pArea != nullptr) + { + spItem->pUIIcon->SetRegion(pArea->GetRegion()); + spItem->pUIIcon->SetMoveRect(pArea->GetRegion()); + } + + m_iUpgradeItemSlotInvPos = -1; + } + + for (int i = 0; i < ANVIL_REQ_MAX; i++) + { + int8_t iOrder = m_iRequirementSlotInvPos[i]; + if (iOrder == -1) + continue; + + __IconItemSkill* spItem = m_pMyUpgradeInv[iOrder]; + if (spItem != nullptr) + { + CN3UIArea* pArea = m_pAreaInv[iOrder]; + if (spItem->pUIIcon != nullptr && pArea != nullptr) + { + spItem->pUIIcon->SetRegion(pArea->GetRegion()); + spItem->pUIIcon->SetMoveRect(pArea->GetRegion()); + } + + RestoreInvItemStackSize(spItem); + + delete m_pRequirementSlot[i]->pUIIcon; + m_pRequirementSlot[i]->pUIIcon = nullptr; + delete m_pRequirementSlot[i]; + m_pRequirementSlot[i] = nullptr; + } + + m_iRequirementSlotInvPos[i] = -1; + } + + m_bUpgradeSucceeded = false; + m_bUpgradeInProgress = false; +} + +// Checks if the given item is allowed to be upgraded (unique or upgrade type). +bool CUIItemUpgrade::IsAllowedUpgradeItem(const __IconItemSkill* spItem) const +{ + if (m_iUpgradeItemSlotInvPos != -1) + return false; + + if (spItem == nullptr || spItem->pItemBasic == nullptr) + return false; + + switch (spItem->pItemBasic->byAttachPoint) + { + case ITEM_POS_DUAL: + case ITEM_POS_RIGHTHAND: + case ITEM_POS_LEFTHAND: + case ITEM_POS_TWOHANDRIGHT: + case ITEM_POS_TWOHANDLEFT: + case ITEM_POS_SHOES: + case ITEM_POS_GLOVES: + case ITEM_POS_HEAD: + case ITEM_POS_LOWER: + case ITEM_POS_UPPER: + break; + + default: + return false; + } + + e_ItemAttrib eItemAttribute = static_cast(spItem->pItemExt->byMagicOrRare); + return eItemAttribute == ITEM_ATTRIB_UNIQUE || eItemAttribute == ITEM_ATTRIB_UPGRADE; +} + +void CUIItemUpgrade::SendToServerUpgradeMsg() +{ + if (m_iUpgradeItemSlotInvPos < 0 || m_iUpgradeItemSlotInvPos >= MAX_ITEM_INVENTORY + || m_pMyUpgradeInv[m_iUpgradeItemSlotInvPos] == nullptr) + return; + + struct ItemPair + { + int ID = 0; + int8_t Pos = -1; + }; + + uint8_t byBuff[512]; + std::array reqItems {}; + int iOffset = 0, iTotalSent = 0; + + m_bUpgradeInProgress = true; + + CAPISocket::MP_AddByte(byBuff, iOffset, WIZ_ITEM_UPGRADE); + CAPISocket::MP_AddByte(byBuff, iOffset, ITEM_UPGRADE_PROCESS); + CAPISocket::MP_AddWord(byBuff, iOffset, m_iNpcID); + + // Add item to upgrade + CAPISocket::MP_AddDword(byBuff, iOffset, m_pMyUpgradeInv[m_iUpgradeItemSlotInvPos]->GetItemID()); + CAPISocket::MP_AddByte(byBuff, iOffset, m_iUpgradeItemSlotInvPos); + + // Add requirement items + for (int i = 0; i < ANVIL_REQ_MAX; i++) + { + int8_t iOrder = m_iRequirementSlotInvPos[i]; + if (iOrder < 0 || iOrder >= MAX_ITEM_INVENTORY) + continue; + + reqItems[iTotalSent++] = { .ID = m_pMyUpgradeInv[iOrder]->GetItemID(), .Pos = iOrder }; + } + + std::sort(reqItems.begin(), reqItems.end(), // + [](const ItemPair& lhs, const ItemPair& rhs) { return lhs.ID > rhs.ID; }); + + for (const ItemPair& reqItem : reqItems) + { + CAPISocket::MP_AddDword(byBuff, iOffset, reqItem.ID); + CAPISocket::MP_AddByte(byBuff, iOffset, reqItem.Pos); + } + + CGameProcedure::s_pSocket->Send(byBuff, iOffset); +} + +void CUIItemUpgrade::MsgRecv_ItemUpgrade(Packet& pkt) +{ + CUIInventory* pInven = CGameProcedure::s_pProcMain->m_pUIInventory; + if (pInven == nullptr) + return; + + struct ItemPair + { + int ID = 0; + int8_t Pos = -1; + }; + + ItemPair upgradeItem {}; + std::array reqItems {}; + + int8_t result = pkt.read(); + + upgradeItem.ID = pkt.read(); + upgradeItem.Pos = pkt.read(); + + for (int i = 0; i < ANVIL_REQ_MAX; i++) + { + reqItems[i].ID = pkt.read(); + reqItems[i].Pos = pkt.read(); + } + + std::string szMsg; + + CancelIconDrop(); + + // When failing (burning) or succeeding, all requirement items are consumed and should be removed. + if (result == ITEM_UPGRADE_RESULT_FAILED || result == ITEM_UPGRADE_RESULT_SUCCEEDED) + { + for (int i = 0; i < ANVIL_REQ_MAX; i++) + { + // Remove requirement items first. + // These are all copies, not references to existing data. + if (m_pRequirementSlot[i] != nullptr) + { + delete m_pRequirementSlot[i]->pUIIcon; + m_pRequirementSlot[i]->pUIIcon = nullptr; + delete m_pRequirementSlot[i]; + m_pRequirementSlot[i] = nullptr; + } + + m_iRequirementSlotInvPos[i] = -1; + + const ItemPair& reqItem = reqItems[i]; + + // Remove a stack from the requirement items in the base inventory. + int8_t byInvOrder = reqItem.Pos; + if (byInvOrder < 0 || byInvOrder >= MAX_ITEM_INVENTORY) + continue; + + __IconItemSkill* spItemInv = pInven->m_pMyInvWnd[byInvOrder]; + if (spItemInv == nullptr) + continue; + + if (spItemInv->GetItemID() != reqItem.ID) + { + assert(!"Unexpected item"); + continue; + } + + if (--spItemInv->iCount <= 0) + { + delete spItemInv->pUIIcon; + spItemInv->pUIIcon = nullptr; + delete spItemInv; + pInven->m_pMyInvWnd[byInvOrder] = nullptr; + } + } + } + + if (result == ITEM_UPGRADE_RESULT_FAILED) + { + if (upgradeItem.Pos < 0 || upgradeItem.Pos >= MAX_ITEM_INVENTORY) + return; + + // The item burned. We should remove it from the inventory. + __IconItemSkill* spItemInv = pInven->m_pMyInvWnd[upgradeItem.Pos]; + if (spItemInv != nullptr) + { + delete spItemInv->pUIIcon; + spItemInv->pUIIcon = nullptr; + delete spItemInv; + pInven->m_pMyInvWnd[upgradeItem.Pos] = nullptr; + } + + // Reset the inventory, copying it back from the original. + // Note that in this case, we don't need the upgrade item to be displayed. + // This will be reset at this point. + if (IsVisible()) + { + CopyInventoryItems(); + + m_bUpgradeSucceeded = false; + m_eAnimationState = AnimationState::Start; + } + + szMsg = fmt::format_text_resource(IDS_ITEM_UPGRADE_FAILED); + CGameProcedure::s_pProcMain->MsgOutput(szMsg, D3DCOLOR_XRGB(255, 0, 255)); + } + else if (result == ITEM_UPGRADE_RESULT_SUCCEEDED) + { + if (upgradeItem.Pos < 0 || upgradeItem.Pos >= MAX_ITEM_INVENTORY) + return; + + if (IsVisible()) + { + m_bUpgradeSucceeded = true; + + if (m_bUpgradeInProgress) + m_eAnimationState = AnimationState::Start; + } + + szMsg = fmt::format_text_resource(IDS_ITEM_UPGRADE_SUCCEEDED); + CGameProcedure::s_pProcMain->MsgOutput(szMsg, D3DCOLOR_XRGB(128, 128, 255)); + + e_PartPosition ePart = PART_POS_UNKNOWN; + e_PlugPosition ePlug = PLUG_POS_UNKNOWN; + e_ItemType eItemType = ITEM_TYPE_UNKNOWN; + std::string szIconFN; + + __TABLE_ITEM_BASIC* pItemBasic = CGameBase::s_pTbl_Items_Basic.Find(upgradeItem.ID / 1000 * 1000); + __TABLE_ITEM_EXT* pItemExt = nullptr; + + if (pItemBasic != nullptr && pItemBasic->byExtIndex >= 0 && pItemBasic->byExtIndex < MAX_ITEM_EXTENSION) + pItemExt = CGameBase::s_pTbl_Items_Exts[pItemBasic->byExtIndex].Find(upgradeItem.ID % 1000); + + eItemType = CGameBase::MakeResrcFileNameForUPC( + pItemBasic, pItemExt, nullptr, &szIconFN, ePart, ePlug, CGameBase::s_pPlayer->Race()); + if (eItemType == ITEM_TYPE_UNKNOWN) + return; + + // Upgrade the item in the inventory. + __IconItemSkill* spItemInv = pInven->m_pMyInvWnd[upgradeItem.Pos]; + if (spItemInv != nullptr) + { + spItemInv->pItemBasic = pItemBasic; + spItemInv->pItemExt = pItemExt; + spItemInv->iDurability = pItemBasic->siMaxDurability + pItemExt->siMaxDurability; + + if (spItemInv->pUIIcon != nullptr) + spItemInv->pUIIcon->SetTex(szIconFN); + + CN3UIArea* pArea = pInven->GetChildAreaByiOrder(UI_AREA_TYPE_INV, upgradeItem.Pos); + if (pArea != nullptr && spItemInv->pUIIcon != nullptr) + { + spItemInv->pUIIcon->SetRegion(pArea->GetRegion()); + spItemInv->pUIIcon->SetMoveRect(pArea->GetRegion()); + } + } + + if (IsVisible()) + { + // Reset the inventory, copying it back from the original. + CopyInventoryItems(); + + m_iUpgradeItemSlotInvPos = upgradeItem.Pos; + + // Note that in this case, we need the upgrade item to be displayed in the result slot. + // We should restore it. + spItemInv = m_pMyUpgradeInv[upgradeItem.Pos]; + if (spItemInv != nullptr && spItemInv->pUIIcon && m_pAreaResult != nullptr) + { + spItemInv->pUIIcon->SetRegion(m_pAreaResult->GetRegion()); + spItemInv->pUIIcon->SetMoveRect(m_pAreaResult->GetRegion()); + } + } + } + else if (result == ITEM_UPGRADE_RESULT_TRADING) + { + if (IsVisible()) + { + m_bUpgradeInProgress = false; + ResetUpgradeInventory(); + } + + szMsg = fmt::format_text_resource(IDS_ITEM_UPGRADE_CANNOT_PERFORM); + CGameProcedure::s_pProcMain->MsgOutput(szMsg, D3DCOLOR_XRGB(255, 0, 255)); + } + else if (result == ITEM_UPGRADE_RESULT_NEED_COINS) + { + if (IsVisible()) + { + m_bUpgradeInProgress = false; + ResetUpgradeInventory(); + } + + szMsg = fmt::format_text_resource(IDS_ITEM_UPGRADE_NEED_COINS); + CGameProcedure::s_pProcMain->MsgOutput(szMsg, D3DCOLOR_XRGB(255, 0, 255)); + } + else if (result == ITEM_UPGRADE_RESULT_NO_MATCH) + { + if (IsVisible()) + { + m_bUpgradeInProgress = false; + ResetUpgradeInventory(); + } + + szMsg = fmt::format_text_resource(IDS_ITEM_UPGRADE_NO_MATCH); + CGameProcedure::s_pProcMain->MsgOutput(szMsg, D3DCOLOR_XRGB(255, 0, 255)); + } + + GoldUpdate(); +} + +void CUIItemUpgrade::UpdateCoverAnimation() +{ + constexpr float COVER_ANIMATION_DURATION = 0.8f; + + if (m_pImageCover1 == nullptr || m_pImageCover2 == nullptr) + return; + + m_fAnimationTimer += CN3Base::s_fSecPerFrm; + + float t = m_fAnimationTimer / COVER_ANIMATION_DURATION; + + // Only handle opening (covers move outward and hide) + float ease = 1.0f - ((1.0f - t) * (1.0f - t)); + int coverShift = m_rcCover1Original.bottom - m_rcCover1Original.top; + + // Calculate new Y positions for opening + int y1 = m_rcCover1Original.top + (int) ((m_rcCover1Original.top - coverShift - m_rcCover1Original.top) * ease); + int y2 = m_rcCover2Original.top + (int) ((m_rcCover2Original.top + coverShift - m_rcCover2Original.top) * ease); + + // Animation completed - hide covers and reset positions + if (t >= 1.0f) + { + m_eAnimationState = AnimationState::None; + + m_pImageCover1->SetVisible(false); + m_pImageCover2->SetVisible(false); + m_pImageCover1->SetRegion(m_rcCover1Original); + m_pImageCover2->SetRegion(m_rcCover2Original); + + m_bUpgradeInProgress = false; + return; + } + + // Update cover regions + RECT rc1 = m_rcCover1Original; + RECT rc2 = m_rcCover2Original; + int height1 = rc1.bottom - rc1.top; + int height2 = rc2.bottom - rc2.top; + rc1.top = y1; + rc1.bottom = y1 + height1; + rc2.top = y2; + rc2.bottom = y2 + height2; + + m_pImageCover1->SetRegion(rc1); + m_pImageCover2->SetRegion(rc2); +} + +void CUIItemUpgrade::UpdateFlipFlopAnimation() +{ + constexpr float FLIPFLOP_FRAME_DELAY = 0.1f; + + m_fAnimationTimer += CN3Base::s_fSecPerFrm; + + if (m_fAnimationTimer >= FLIPFLOP_FRAME_DELAY) + { + m_fAnimationTimer -= FLIPFLOP_FRAME_DELAY; + ++m_iCurrentFrame; + + if (m_iCurrentFrame >= FLIPFLOP_MAX_FRAMES) + { + HideAllAnimationFrames(); + + m_eAnimationState = AnimationState::Result; + m_fAnimationTimer = 0.0f; + } + else + { + FlipFlopAnim(); + } + } +} + +void CUIItemUpgrade::HideAllAnimationFrames() +{ + for (int i = 0; i < FLIPFLOP_MAX_FRAMES; i++) + { + // Hide all img_s_load_X frames + if (m_pImgSuccess[i] != nullptr) + m_pImgSuccess[i]->SetVisible(false); + + // Hide all img_f_load_X frames + if (m_pImgFail[i] != nullptr) + m_pImgFail[i]->SetVisible(false); + } +} + +bool CUIItemUpgrade::IsValidRequirementItem(const __IconItemSkill* pSrc) const +{ + if (m_bUpgradeInProgress) + return false; + + if (pSrc->pItemBasic->dwEffectID2 != ITEM_EFFECT2_ITEM_UPGRADE_REQ) + return false; + + bool bHasConsumable = false; + bool bHasScroll = false; + + for (int i = 0; i < ANVIL_REQ_MAX; i++) + { + int8_t iOrder = m_iRequirementSlotInvPos[i]; + if (iOrder < 0) + continue; + + if (m_pMyUpgradeInv[iOrder]->pItemBasic->byClass == ITEM_CLASS_CONSUMABLE) + bHasConsumable = true; + else + bHasScroll = true; + } + + if (bHasConsumable && pSrc->pItemBasic->byClass == ITEM_CLASS_CONSUMABLE) + return false; + + if (bHasScroll && pSrc->pItemBasic->byClass != ITEM_CLASS_CONSUMABLE) + return false; + + if (bHasConsumable && bHasScroll) + return false; + + return true; +} + +void CUIItemUpgrade::FlipFlopAnim() +{ + if (m_bUpgradeSucceeded) + { + // Hide previous frame + if (m_iCurrentFrame > 0 && m_pImgSuccess[m_iCurrentFrame - 1] != nullptr) + m_pImgSuccess[m_iCurrentFrame - 1]->SetVisible(false); + + // Show current frame + if (m_pImgSuccess[m_iCurrentFrame] != nullptr) + { + m_pImgSuccess[m_iCurrentFrame]->SetVisible(true); + m_pImgSuccess[m_iCurrentFrame]->SetParent(this); // Re-order for rendering + } + } + else + { + // Hide previous frame + if (m_iCurrentFrame > 0 && m_pImgFail[m_iCurrentFrame - 1] != nullptr) + m_pImgFail[m_iCurrentFrame - 1]->SetVisible(false); + + // Show current frame + if (m_pImgFail[m_iCurrentFrame] != nullptr) + { + m_pImgFail[m_iCurrentFrame]->SetVisible(true); + m_pImgFail[m_iCurrentFrame]->SetParent(this); // Re-order for rendering + } + } +} + +void CUIItemUpgrade::AnimClose() +{ + if (m_eAnimationState != AnimationState::None) + { + m_eAnimationState = AnimationState::None; + m_fAnimationTimer = 0.0f; + m_iCurrentFrame = 0; + + if (m_pImageCover1 != nullptr) + m_pImageCover1->SetVisible(false); + + if (m_pImageCover2 != nullptr) + m_pImageCover2->SetVisible(false); + } + + m_bUpgradeInProgress = false; + HideAllAnimationFrames(); +} + +void CUIItemUpgrade::StartUpgradeAnim() +{ + if (!m_bUpgradeInProgress) + return; + + if (m_pImageCover1 == nullptr || m_pImageCover2 == nullptr) + return; + + m_fAnimationTimer = 0.0f; + m_iCurrentFrame = 0; + + // Make covers visible during animation + m_pImageCover1->SetVisible(true); + m_pImageCover2->SetVisible(true); + m_pImageCover1->SetParent(this); // Re-order for rendering + m_pImageCover2->SetParent(this); + + // save original positions + m_rcCover1Original = m_pImageCover1->GetRegion(); + m_rcCover2Original = m_pImageCover2->GetRegion(); + m_eAnimationState = AnimationState::FlipFlop; +} + +bool CUIItemUpgrade::HandleInventoryIconRightClick(CN3UIBase* pUIIcon) +{ + if (pUIIcon == nullptr || !pUIIcon->IsVisible()) + return false; + + e_UIWND_DISTRICT eDistrict = UIWND_DISTRICT_UNKNOWN; + int iSrcOrder = -1; + __IconItemSkill* spItem = nullptr; + if (!GetSelectedIconInfo(pUIIcon, &iSrcOrder, &eDistrict, &spItem) || iSrcOrder < 0) + return false; + + if (spItem == nullptr || spItem->pUIIcon == nullptr) + return false; + + if (eDistrict == UIWND_DISTRICT_UPGRADE_INV) + { + if (m_bUpgradeSucceeded) + ResetUpgradeInventory(); + + if (m_iUpgradeItemSlotInvPos == -1 && IsAllowedUpgradeItem(spItem)) + { + if (m_pAreaUpgrade != nullptr) + { + spItem->pUIIcon->SetRegion(m_pAreaUpgrade->GetRegion()); + spItem->pUIIcon->SetMoveRect(m_pAreaUpgrade->GetRegion()); + } + + m_iUpgradeItemSlotInvPos = iSrcOrder; + return true; + } + + if (IsValidRequirementItem(spItem)) + { + __IconItemSkill* spItemNew = spItem->Clone(this); + + for (int i = 0; i < ANVIL_REQ_MAX; i++) + { + if (m_iRequirementSlotInvPos[i] != -1) + continue; + + // If stackable, reduce stack size in inventory + ReduceInvItemStackSize(m_pMyUpgradeInv[iSrcOrder]); + SetRequirementItemSlot(spItemNew, iSrcOrder, i); + return true; + } + + if (spItemNew != nullptr) + { + delete spItemNew->pUIIcon; + spItemNew->pUIIcon = nullptr; + delete spItemNew; + } + } + } + else if (eDistrict == UIWND_DISTRICT_UPGRADE_RESULT_SLOT) + { + // Move upgrade result to inv + ResetUpgradeInventory(); + return true; + } + + return false; +} + +void CUIItemUpgrade::SetRequirementItemSlot(__IconItemSkill* spItem, int iSrcOrder, int iDstOrder) +{ + if (spItem == nullptr) + return; + + CN3UIArea* pArea = m_pSlotArea[iDstOrder]; + + if (spItem->pUIIcon != nullptr && pArea != nullptr) + { + spItem->pUIIcon->SetRegion(pArea->GetRegion()); + spItem->pUIIcon->SetMoveRect(pArea->GetRegion()); + } + + m_iRequirementSlotInvPos[iDstOrder] = iSrcOrder; + m_pRequirementSlot[iDstOrder] = spItem; +} + +void CUIItemUpgrade::CallBackProc(int iID, uint32_t dwFlag) +{ + if (iID == CHILD_UI_MSGBOX_OKCANCEL) + { + if (dwFlag == CUIMsgBoxOkCancel::CALLBACK_OK) + { + SendToServerUpgradeMsg(); + } + else if (dwFlag == CUIMsgBoxOkCancel::CALLBACK_CANCEL) + { + if (m_pUIMsgBoxOkCancel != nullptr) + m_pUIMsgBoxOkCancel->SetVisible(false); + } + } +} + +void CUIItemUpgrade::ReduceInvItemStackSize(__IconItemSkill* spItem) +{ + // If stackable, reduce stack size in inventory + if (!spItem->IsStackable()) + return; + + if (--spItem->iCount == 0 && spItem->pUIIcon != nullptr) + spItem->pUIIcon->SetVisibleWithNoSound(false); +} + +void CUIItemUpgrade::RestoreInvItemStackSize(__IconItemSkill* spItem) +{ + // If stackable, restore stack size in inventory + if (!spItem->IsStackable()) + return; + + if (++spItem->iCount == 1 && spItem->pUIIcon != nullptr) + spItem->pUIIcon->SetVisibleWithNoSound(true); +} diff --git a/src/Client/WarFare/UIItemUpgrade.h b/src/Client/WarFare/UIItemUpgrade.h new file mode 100644 index 000000000..b21c09b9d --- /dev/null +++ b/src/Client/WarFare/UIItemUpgrade.h @@ -0,0 +1,108 @@ +#pragma once + +#include + +#include "GameDef.h" + +enum e_UIWND_DISTRICT : uint8_t; +struct __IconItemSkill; +class CN3UIIcon; +class CUIImageTooltipDlg; +class CUIMsgBoxOkCancel; +class CUIItemUpgrade : public CN3UIBase +{ +private: + static constexpr int FLIPFLOP_MAX_FRAMES = 19; + static constexpr int CHILD_UI_MSGBOX_OKCANCEL = 1; + + enum class AnimationState : uint8_t + { + None, + Start, + FlipFlop, + Result, + CoverOpening, + Done + }; + + AnimationState m_eAnimationState = AnimationState::None; + + float m_fAnimationTimer = 0.0f; + int m_iCurrentFrame = 0; + bool m_bUpgradeSucceeded = false; + bool m_bUpgradeInProgress = false; + int m_iNpcID = 0; + + RECT m_rcCover1Original = {}; + RECT m_rcCover2Original = {}; + + CN3UIButton* m_pBtnClose = nullptr; + CN3UIButton* m_pBtnOk = nullptr; + CN3UIButton* m_pBtnCancel = nullptr; + CN3UIString* m_pStrMyGold = nullptr; + CN3UIArea* m_pAreaUpgrade = nullptr; + CN3UIArea* m_pAreaResult = nullptr; + CN3UIArea* m_pAreaInv[MAX_ITEM_INVENTORY] = {}; + CN3UIString* m_pStrInv[MAX_ITEM_INVENTORY] = {}; + CN3UIArea* m_pSlotArea[ANVIL_REQ_MAX] = {}; + CN3UIImage* m_pImgFail[FLIPFLOP_MAX_FRAMES] = {}; + CN3UIImage* m_pImgSuccess[FLIPFLOP_MAX_FRAMES] = {}; + CN3UIImage* m_pImageCover1 = nullptr; + CN3UIImage* m_pImageCover2 = nullptr; + + CUIImageTooltipDlg* m_pUITooltipDlg = nullptr; + CUIMsgBoxOkCancel* m_pUIMsgBoxOkCancel = nullptr; + + __IconItemSkill* m_pItemBeingDragged = nullptr; + int m_iItemBeingDraggedSourcePos = -1; + + __IconItemSkill* m_pMyUpgradeInv[MAX_ITEM_INVENTORY] = {}; + __IconItemSkill* m_pRequirementSlot[ANVIL_REQ_MAX] = {}; + int8_t m_iRequirementSlotInvPos[ANVIL_REQ_MAX] = {}; + int8_t m_iUpgradeItemSlotInvPos = -1; + +public: + CUIItemUpgrade(); + ~CUIItemUpgrade() override; + void Release() override; + void Close(); + void SetVisibleWithNoSound(bool bVisible, bool bWork = false, bool bReFocus = false) override; + void MsgRecv_ItemUpgrade(Packet& pkt); + void SetNpcID(int iNpcID); + void SetVisible(bool bVisible) override; + +private: + RECT GetSampleRect(); + e_UIWND_DISTRICT GetWndDistrict(const POINT ptCur) const; + bool HandleInventoryIconRightClick(CN3UIBase* pUIIcon); + bool OnKeyPress(int iKey) override; + + uint32_t MouseProc(uint32_t dwFlags, const POINT& ptCur, const POINT& ptOld) override; + bool Load(File& file) override; + bool ReceiveMessage(CN3UIBase* pSender, uint32_t dwMsg) override; + void Render() override; + + __IconItemSkill* GetHighlightIconItem(CN3UIBase* pUIIcon) const; + bool GetSelectedIconInfo( + CN3UIBase* pUIIcon, int* piOrder = nullptr, e_UIWND_DISTRICT* peDistrict = nullptr, __IconItemSkill** pspItem = nullptr) const; + void CancelIconDrop(); + bool ReceiveIconDrop(); + void CopyInventoryItems(); + void ResetUpgradeInventory(); + void GoldUpdate(); + bool IsAllowedUpgradeItem(const __IconItemSkill* spItem) const; + void SendToServerUpgradeMsg(); + void CallBackProc(int iID, uint32_t dwFlag) override; + + void FlipFlopAnim(); + void AnimClose(); + void StartUpgradeAnim(); + void UpdateCoverAnimation(); + void UpdateFlipFlopAnimation(); + void HideAllAnimationFrames(); + void SetRequirementItemSlot(__IconItemSkill* spItem, int iSrcOrder, int iDstOrder); + bool IsValidRequirementItem(const __IconItemSkill* pSrc) const; + void Tick() override; + void ReduceInvItemStackSize(__IconItemSkill* spItem); + void RestoreInvItemStackSize(__IconItemSkill* spItem); +}; diff --git a/src/Client/WarFare/UIUpgradeSelect.cpp b/src/Client/WarFare/UIUpgradeSelect.cpp index 31cfcfaf0..f23e413f3 100644 --- a/src/Client/WarFare/UIUpgradeSelect.cpp +++ b/src/Client/WarFare/UIUpgradeSelect.cpp @@ -1,7 +1,7 @@ #include "StdAfx.h" #include "UIUpgradeSelect.h" #include "GameProcMain.h" -// #include "UIItemUpgrade.h" +#include "UIItemUpgrade.h" // #include "UIRingUpgrade.h" #include "UIManager.h" @@ -40,16 +40,12 @@ bool CUIUpgradeSelect::ReceiveMessage(CN3UIBase* pSender, uint32_t dwMsg) { if (pSender == m_pBtn_Upgrade_1) { -#if 1 - CGameProcedure::MessageBoxPost("CUIItemUpgrade needs to be implemented.", "Not implemented", MB_OK); -#else CUIItemUpgrade* pUIItemUpgrade = CGameProcedure::s_pProcMain->m_pUIItemUpgrade; if (pUIItemUpgrade != nullptr) { pUIItemUpgrade->SetVisible(true); pUIItemUpgrade->SetNpcID(m_iNpcID); } -#endif SetVisible(false); } diff --git a/src/Client/WarFare/WarFare.Core.vcxproj b/src/Client/WarFare/WarFare.Core.vcxproj index dfc12b703..77c3ad81f 100644 --- a/src/Client/WarFare/WarFare.Core.vcxproj +++ b/src/Client/WarFare/WarFare.Core.vcxproj @@ -214,6 +214,7 @@ + @@ -324,6 +325,7 @@ + diff --git a/src/Client/WarFare/WarFare.Core.vcxproj.filters b/src/Client/WarFare/WarFare.Core.vcxproj.filters index 4b6534084..1611402fc 100644 --- a/src/Client/WarFare/WarFare.Core.vcxproj.filters +++ b/src/Client/WarFare/WarFare.Core.vcxproj.filters @@ -350,6 +350,9 @@ Scene\Ingame\UI + + Scene\Ingame\UI + Scene\Ingame\UI @@ -442,6 +445,9 @@ Scene\Ingame\UI + + Scene\Ingame\UI + Scene\Ingame\UI diff --git a/src/Client/WarFare/text_resources.h b/src/Client/WarFare/text_resources.h index 07a774e62..925c1d82f 100644 --- a/src/Client/WarFare/text_resources.h +++ b/src/Client/WarFare/text_resources.h @@ -512,7 +512,14 @@ enum e_TextResourceID : uint16_t IDS_WARP_NOT_DURING_CSW = 6612, // You cannot enter during the Castle Siege War. IDS_WARP_NEED_LOYALTY = 6613, // You cannot enter when you have 0 national points. - IDS_TELEPORT_TO_X_NEED_Y_COINS = 7612, // To teleport to %s, you need %d coins + IDS_ITEM_UPGRADE_SUCCEEDED = 6700, // Item upgrade succeeded. + IDS_ITEM_UPGRADE_FAILED = 6701, // Item upgrade failed. + IDS_ITEM_UPGRADE_CANNOT_PERFORM = 6702, // Cannot perform item upgrade. + IDS_ITEM_UPGRADE_NEED_COINS = 6703, // You don't have enough Coins. + IDS_ITEM_UPGRADE_NO_MATCH = 6704, // The items required for upgrade does not match. + IDS_ITEM_UPGRADE_CONFIRM = 6705, // The item might be destroyed while performing the upgrade. Will you continue? + + IDS_TELEPORT_TO_X_NEED_Y_COINS = 7612, // To teleport to %s, you need %d coins IDS_EXITING_GAME_IN_X_SECONDS = 7632, // Exiting game in %d seconds. IDS_EXITING_GAME_CANCELED = 7633, // Exiting game canceled. diff --git a/src/N3Base/N3UIImage.cpp b/src/N3Base/N3UIImage.cpp index 1bc1e978e..36c08438b 100644 --- a/src/N3Base/N3UIImage.cpp +++ b/src/N3Base/N3UIImage.cpp @@ -137,11 +137,13 @@ void CN3UIImage::SetRegion(const RECT& Rect) void CN3UIImage::SetUVRect(float left, float top, float right, float bottom) { - m_frcUVRect.left = left; - m_frcUVRect.top = top; - m_frcUVRect.right = right; - m_frcUVRect.bottom = bottom; + __FLOAT_RECT frc = { .left = left, .top = top, .right = right, .bottom = bottom }; + SetUVRect(frc); +} +void CN3UIImage::SetUVRect(const __FLOAT_RECT& frc) +{ + m_frcUVRect = frc; SetVB(); } diff --git a/src/N3Base/N3UIImage.h b/src/N3Base/N3UIImage.h index b2b891547..0d1d36a78 100644 --- a/src/N3Base/N3UIImage.h +++ b/src/N3Base/N3UIImage.h @@ -57,6 +57,7 @@ class CN3UIImage : public CN3UIBase void SetTex(const std::string& szFN); // Texture 지정 void SetUVRect(float left, float top, float right, float bottom); // image의 uv좌표 지정 + void SetUVRect(const __FLOAT_RECT& frc); // image의 uv좌표 지정 void SetColor(D3DCOLOR color); // 칼라 지정 void SetRegion(const RECT& Rect) override; // 영역 지정 diff --git a/src/Server/Ebenezer/EbenezerApp.cpp b/src/Server/Ebenezer/EbenezerApp.cpp index d717fe52b..ca9578c75 100644 --- a/src/Server/Ebenezer/EbenezerApp.cpp +++ b/src/Server/Ebenezer/EbenezerApp.cpp @@ -992,12 +992,12 @@ bool EbenezerApp::LoadItemUpgradeTable() return false; } - // Ensure requirement items are sorted in ascending order. + // Ensure requirement items are sorted in descending order. for (ModelType* model : localVec) { std::sort(std::begin(model->RequiredItem), std::end(model->RequiredItem), // - [](int lhs, int rhs) { return lhs < rhs; }); + [](int lhs, int rhs) { return lhs > rhs; }); } // Now that we're done, swap it over to the destination container. diff --git a/src/Server/Ebenezer/User.cpp b/src/Server/Ebenezer/User.cpp index 39490e58f..47e5952ce 100644 --- a/src/Server/Ebenezer/User.cpp +++ b/src/Server/Ebenezer/User.cpp @@ -12767,14 +12767,14 @@ void CUser::ItemUpgrade(char* pBuf) uint8_t originPos = -1; bool upgradeSuccess = false; - uint8_t reqItemPos[9] {}; - int32_t reqItemId[9] {}; + uint8_t reqItemPos[ANVIL_REQ_MAX] {}; + int32_t reqItemId[ANVIL_REQ_MAX] {}; npcId = GetShort(pBuf, index); originItemId = GetDWORD(pBuf, index); originPos = GetByte(pBuf, index); - for (int i = 0; i < 9; i++) + for (int i = 0; i < ANVIL_REQ_MAX; i++) { reqItemId[i] = GetDWORD(pBuf, index); reqItemPos[i] = GetByte(pBuf, index); @@ -12783,7 +12783,7 @@ void CUser::ItemUpgrade(char* pBuf) // Cannot upgrade while in the middle of trading. if (m_sExchangeUser != -1) { - SendItemUpgradeFailed(ITEM_UPGRADE_ERROR_TRADING); + SendItemUpgradeFailed(ITEM_UPGRADE_RESULT_TRADING); return; } @@ -12839,7 +12839,7 @@ void CUser::ItemUpgrade(char* pBuf) std::unordered_set usedItemPositions; usedItemPositions.insert(originPos); - for (int i = 0; i < ANVIL_MAX; i++) + for (int i = 0; i < ANVIL_REQ_MAX; i++) { // This implies the slot is unused, so we should ignore it. if (reqItemPos[i] == 255) @@ -12923,7 +12923,7 @@ void CUser::ItemUpgrade(char* pBuf) // supports rental items. if (originItem.byFlag != ITEM_FLAG_NONE) { - SendItemUpgradeFailed(ITEM_UPGRADE_ERROR_ITEM_RENTED); + SendItemUpgradeFailed(ITEM_UPGRADE_RESULT_ITEM_RENTED); return; } @@ -13032,12 +13032,12 @@ void CUser::ItemUpgrade(char* pBuf) if ((itemUpgradeElementClass == 1 || itemUpgradeElementClass == 2) && itemClass != itemUpgradeElementClass) { - SendItemUpgradeFailed(ITEM_UPGRADE_ERROR_NO_MATCH); + SendItemUpgradeFailed(ITEM_UPGRADE_RESULT_NO_MATCH); return; } bool matchedRequiredItems = true; - for (int j = 0; j < 8; j++) + for (int j = 0; j < ANVIL_REQ_MAX - 1; j++) { if (itemUpgradeModel->RequiredItem[j] == 0) break; @@ -13055,7 +13055,7 @@ void CUser::ItemUpgrade(char* pBuf) if (itemUpgradeModel->RequiredCoins > m_pUserData->m_iGold) { - SendItemUpgradeFailed(ITEM_UPGRADE_ERROR_NEED_COINS); + SendItemUpgradeFailed(ITEM_UPGRADE_RESULT_NEED_COINS); return; } @@ -13065,7 +13065,7 @@ void CUser::ItemUpgrade(char* pBuf) if (matchedItemUpgradeModel == nullptr) { - SendItemUpgradeFailed(ITEM_UPGRADE_ERROR_NO_MATCH); + SendItemUpgradeFailed(ITEM_UPGRADE_RESULT_NO_MATCH); return; } @@ -13114,7 +13114,7 @@ void CUser::ItemUpgrade(char* pBuf) originItem.sTimeRemaining = 0; } - for (int i = 0; i < 9; i++) + for (int i = 0; i < ANVIL_REQ_MAX; i++) { if (reqItemPos[i] >= HAVE_MAX) continue; @@ -13144,18 +13144,18 @@ void CUser::ItemUpgrade(char* pBuf) if (upgradeSuccess) { - SetByte(sendBuffer, ITEM_UPGRADE_ERROR_SUCCEEDED, sendIndex); + SetByte(sendBuffer, ITEM_UPGRADE_RESULT_SUCCEEDED, sendIndex); SetDWORD(sendBuffer, newItemId, sendIndex); } else { - SetByte(sendBuffer, ITEM_UPGRADE_ERROR_FAILED, sendIndex); + SetByte(sendBuffer, ITEM_UPGRADE_RESULT_FAILED, sendIndex); SetDWORD(sendBuffer, originItemId, sendIndex); } SetByte(sendBuffer, originPos, sendIndex); - for (int i = 0; i < 9; i++) + for (int i = 0; i < ANVIL_REQ_MAX; i++) { SetDWORD(sendBuffer, reqItemId[i], sendIndex); SetByte(sendBuffer, reqItemPos[i], sendIndex); diff --git a/src/shared/globals.h b/src/shared/globals.h index b6085d556..ba67164e9 100644 --- a/src/shared/globals.h +++ b/src/shared/globals.h @@ -436,7 +436,7 @@ inline constexpr uint8_t RESERVED = 14; inline constexpr uint8_t SLOT_MAX = 14; // 14 equipped item slots inline constexpr uint8_t HAVE_MAX = 28; // 28 inventory slots inline constexpr uint8_t WAREHOUSE_MAX = 192; // 창고 아이템 MAX -inline constexpr uint8_t ANVIL_MAX = 9; +inline constexpr uint8_t ANVIL_REQ_MAX = 9; // Start of inventory area inline constexpr int INVENTORY_INVENT = SLOT_MAX; diff --git a/src/shared/packets.h b/src/shared/packets.h index a07149b41..83b8aa364 100644 --- a/src/shared/packets.h +++ b/src/shared/packets.h @@ -545,12 +545,12 @@ enum e_ItemUpgradeOpcode : uint8_t enum e_ItemUpgradeResult : uint8_t { - ITEM_UPGRADE_ERROR_FAILED = 0, - ITEM_UPGRADE_ERROR_SUCCEEDED = 1, - ITEM_UPGRADE_ERROR_TRADING = 2, - ITEM_UPGRADE_ERROR_NEED_COINS = 3, - ITEM_UPGRADE_ERROR_NO_MATCH = 4, - ITEM_UPGRADE_ERROR_ITEM_RENTED = 5, + ITEM_UPGRADE_RESULT_FAILED = 0, + ITEM_UPGRADE_RESULT_SUCCEEDED = 1, + ITEM_UPGRADE_RESULT_TRADING = 2, + ITEM_UPGRADE_RESULT_NEED_COINS = 3, + ITEM_UPGRADE_RESULT_NO_MATCH = 4, + ITEM_UPGRADE_RESULT_ITEM_RENTED = 5, }; //////////////////////////////////////////////////////////////// diff --git a/tests/Server/Ebenezer/ItemUpgrade_test.cpp b/tests/Server/Ebenezer/ItemUpgrade_test.cpp index 748eea03f..8c11fc205 100644 --- a/tests/Server/Ebenezer/ItemUpgrade_test.cpp +++ b/tests/Server/Ebenezer/ItemUpgrade_test.cpp @@ -151,7 +151,7 @@ TEST_P(ItemUpgradeTest, BasicUpgradeSucceeds) EXPECT_EQ(packet->Opcode, WIZ_ITEM_UPGRADE); EXPECT_EQ(packet->SubOpcode, ITEM_UPGRADE_PROCESS); - EXPECT_EQ(packet->Result, ITEM_UPGRADE_ERROR_SUCCEEDED); + EXPECT_EQ(packet->Result, ITEM_UPGRADE_RESULT_SUCCEEDED); EXPECT_EQ(packet->Item[0].ID, NEW_ITEM_ID); EXPECT_EQ(packet->Item[0].Pos, 0); EXPECT_EQ(packet->Item[1].ID, REQ_ITEM1_ID); @@ -250,7 +250,7 @@ TEST_F(ItemUpgradeTest, BasicUpgradeBurns) EXPECT_EQ(packet->Opcode, WIZ_ITEM_UPGRADE); EXPECT_EQ(packet->SubOpcode, ITEM_UPGRADE_PROCESS); - EXPECT_EQ(packet->Result, ITEM_UPGRADE_ERROR_FAILED); + EXPECT_EQ(packet->Result, ITEM_UPGRADE_RESULT_FAILED); EXPECT_EQ(packet->Item[0].ID, OLD_ITEM_ID); EXPECT_EQ(packet->Item[0].Pos, 0); EXPECT_EQ(packet->Item[1].ID, REQ_ITEM1_ID); @@ -386,7 +386,7 @@ TEST_F(ItemUpgradeTest, InsufficientGoldRejected) auto packet = reinterpret_cast(pBuf); EXPECT_EQ(packet->Opcode, WIZ_ITEM_UPGRADE); EXPECT_EQ(packet->SubOpcode, ITEM_UPGRADE_PROCESS); - EXPECT_EQ(packet->Result, ITEM_UPGRADE_ERROR_NEED_COINS); + EXPECT_EQ(packet->Result, ITEM_UPGRADE_RESULT_NEED_COINS); }); // Copy it into the larger buffer in case it were to ever read beyond the struct's size. @@ -573,7 +573,7 @@ TEST_F(ItemUpgradeTest, UserTradingRejected) auto packet = reinterpret_cast(pBuf); EXPECT_EQ(packet->Opcode, WIZ_ITEM_UPGRADE); EXPECT_EQ(packet->SubOpcode, ITEM_UPGRADE_PROCESS); - EXPECT_EQ(packet->Result, ITEM_UPGRADE_ERROR_TRADING); + EXPECT_EQ(packet->Result, ITEM_UPGRADE_RESULT_TRADING); }); // Copy it into the larger buffer in case it were to ever read beyond the struct's size. @@ -615,7 +615,7 @@ TEST_F(ItemUpgradeTest, RentalItemRejected) auto packet = reinterpret_cast(pBuf); EXPECT_EQ(packet->Opcode, WIZ_ITEM_UPGRADE); EXPECT_EQ(packet->SubOpcode, ITEM_UPGRADE_PROCESS); - EXPECT_EQ(packet->Result, ITEM_UPGRADE_ERROR_ITEM_RENTED); + EXPECT_EQ(packet->Result, ITEM_UPGRADE_RESULT_ITEM_RENTED); }); // Copy it into the larger buffer in case it were to ever read beyond the struct's size.