#include "Client/Mode/CMode.h" #include "Client/Mode/ModeMgr.h" #include "Client/Mode/ModeDragState.h" #include "Client/Mode/ModeEvents.h" #include "Client/Session.h" #include "Contents/Skills/ClientSkills.h" #include "UI/Shortcuts/UIQuickSlotWnd.h" #include "UI/Shortcuts/UIShortCutWnd.h" #include "UI/Window/UINewSelectCharWnd.h" #include "UI/Window/UIWindow.h" #include #include #include #include namespace { // INLINED __forceinline void Refresh(UIWindow& window) { window.SendMsg(nullptr, 23, 0, 0, 0, 0); window.Invalidate(); } // INLINED void RemoveFirst(std::list& cooldowns, int slot) { for (ShortcutCooldownRecord& record : cooldowns) { const auto value = std::find_if(record.main.begin(), record.main.end(), [&](const CooldownState& state) { return state.slot == slot; }); if (value != record.main.end()) { record.main.erase(value); return; } } } // INLINED __forceinline std::list::iterator FindCooldownRecord(std::list& records, const std::uint32_t& id) { if (!g_session.IsHomunculusSkill(id) && !g_session.IsMercenarySkill(id)) { return std::find_if(records.begin(), records.end(), [&](const ShortcutCooldownRecord& entry) { return entry.id == id; }); } const std::uint32_t group = 9999; return std::find_if(records.begin(), records.end(), [&](const ShortcutCooldownRecord& entry) { return entry.id == group; }); } // INLINED __forceinline std::list::iterator FindCooldownState(std::list& states, const int& slot) { return std::find_if(states.begin(), states.end(), [&](const CooldownState& state) { return state.slot == slot; }); } } // OFFSET: 0x00917640 // FUNCTION: fn_12589 void UIShortCutWnd::Slot10(int x, int y, std::intptr_t argument) { UIWindow& window = *this; ShortcutGrid& grid = this->m_grid; const ModeDragState& value = *reinterpret_cast(argument); const int bank = static_cast(g_session.m_options.Get(10)); if (value.skill != 0) { const std::string copiedName = value.skillName; const std::uint32_t id = value.id; const int column = grid.HitTest(x, y, g_session.ItemSlotActive(), g_session.m_hotkeyPage); if (column != -1) { if (9 * g_session.m_hotkeyPage + column == value.slot) { g_modeMgr.gameMode->SendMsg(18, 0, 0, 0, 0); return; } CSkillInfo skill = g_session.LookupSkill(id); const int index = 9 * g_session.m_hotkeyPage + column; if (skill.level == 0 && skill.available == 0) { g_modeMgr.gameMode->SendMsg(18, 0, 0, 0, 0); return; } if (grid.rows <= g_session.m_hotkeyPage) { if (g_modeMgr.gameMode) { g_modeMgr.gameMode->SendMsg(18, 0, 0, 0, 0); } return; } g_session.AssignShortcut(1, skill.id, value.skillLevel, index, bank); if (value.slot != index) { RemoveFirst(g_session.m_requestTimers, index); } const auto record = FindCooldownRecord(g_session.m_requestTimers, skill.id); if (record != g_session.m_requestTimers.end() && record->end > timeGetTime()) { const auto previous = FindCooldownState(record->main, value.slot); const int targetX = 29 * column + 16; const int targetY = 33 * g_session.m_hotkeyPage + 4; if (previous != record->main.end()) { if (previous->y == targetY) { previous->slot = index; previous->x = targetX; } else { record->main.push_back(CooldownState(index, targetX, targetY, record->duration, record->end - record->duration, 0, 0)); } } else { record->main.push_back(CooldownState(index, targetX, targetY, record->duration, record->end - record->duration, 0, 0)); } } Refresh(window); } else if (value.slot != -1) { CSkillInfo skill = g_session.LookupSkill(id); const auto record = FindCooldownRecord(g_session.m_requestTimers, skill.id); if (record != g_session.m_requestTimers.end()) { const auto previous = FindCooldownState(record->main, value.slot); if (previous != record->main.end()) { record->main.erase(previous); } } g_session.AssignShortcut(1, 0, 0, value.slot, bank); Refresh(window); } g_modeMgr.gameMode->SendMsg(18, 0, 0, 0, 0); return; } if (value.source == 6 || value.source == 9) { g_modeMgr.gameMode->SendMsg(18, 0, 0, 0, 0); return; } ITEM_INFO item = LookupClientItemByIdentity(g_session.m_inventoryItems, value.id); if (item.unknown_0000[1] == 0 || item.identified == 0) { g_modeMgr.gameMode->SendMsg(18, 0, 0, 0, 0); return; } switch (item.unknown_0000[0]) { case 3: case 10: case 16: case 17: case 19: { const int column = grid.HitTest(x, y, g_session.ItemSlotActive(), g_session.m_hotkeyPage); if (column != -1 && 9 * grid.rows <= 9 * g_session.m_hotkeyPage + column) { if (g_session.ItemSlotActive()) { g_session.AssignItemShortcut(static_cast(item.GetItemId()), column); Refresh(window); } if (g_modeMgr.gameMode) { g_modeMgr.gameMode->SendMsg(18, 0, 0, 0, 0); } return; } } } switch (item.unknown_0000[0]) { case 4: case 5: case 8: case 9: case 11: case 12: case 13: case 14: case 15: if (item.unknown_0000[2] == 0) { break; } case 0: case 1: case 2: case 10: case 16: case 17: case 19: { const int column = grid.HitTest(x, y, g_session.ItemSlotActive(), g_session.m_hotkeyPage); if (column != -1) { const int index = 9 * g_session.m_hotkeyPage + column; if (grid.rows <= g_session.m_hotkeyPage) { if (g_modeMgr.gameMode) { g_modeMgr.gameMode->SendMsg(18, 0, 0, 0, 0); } return; } g_session.AssignShortcut(0, static_cast(item.GetItemId()), 0, index, bank); if (value.slot != index) { RemoveFirst(g_session.m_requestTimers, index); } Refresh(window); } break; } } g_modeMgr.gameMode->SendMsg(18, 0, 0, 0, 0); } namespace { // INLINED void RemoveAlternate(std::list& cooldowns, int source, int slot) { for (ShortcutCooldownRecord& record : cooldowns) { if (source == slot) { continue; } for (std::list::iterator state = record.alternate.begin(); state != record.alternate.end(); ++state) { if (state->slot == slot) { record.alternate.erase(state); return; } } } } } // OFFSET: 0x00917100 // FUNCTION: fn_12588 void UIQuickSlotWnd::Slot10(int x, int y, std::intptr_t argument) { const ModeDragState& value = *reinterpret_cast(argument); std::list& records = g_session.m_requestTimers; if (value.skill != 0) { const std::string copiedName(value.skillName.c_str(), value.skillName.size()); const int slot = this->SlotAt(x, y); if (slot != -1) { CSkillInfo skill = g_session.LookupSkill(value.id); if (skill.level != 0 && skill.available != 0) { g_session.SetHotkeyEntry(1, skill.id, value.skillLevel, slot); RemoveAlternate(g_session.m_requestTimers, value.slot, slot); const std::list::iterator record = FindCooldownRecord(records, skill.id); if (record != records.end() && timeGetTime() < record->end) { std::list& states = record->alternate; const std::list::iterator previous = FindCooldownState(states, value.slot); if (previous != states.end()) { previous->slot = slot; previous->x = this->SlotX(slot); } else { for (std::list::iterator state = states.begin(); state != states.end(); ++state) { if (state->x == this->SlotX(slot)) { states.erase(state); break; } } } } this->SendMsg(nullptr, 23, 0, 0, 0, 0); this->Invalidate(); } } else if (value.slot != -1) { CSkillInfo skill = g_session.LookupSkill(value.id); const std::list::iterator record = FindCooldownRecord(records, skill.id); if (record != records.end()) { std::list& states = record->alternate; const std::list::iterator previous = FindCooldownState(states, value.slot); if (previous != states.end()) { states.erase(previous); } } g_session.SetHotkeyEntry(1, 0, 0, value.slot); this->SendMsg(nullptr, 23, 0, 0, 0, 0); this->Invalidate(); } } else if (value.source != 6 && value.source != 9) { ITEM_INFO item = LookupClientItemByIdentity(g_session.m_inventoryItems, value.id); if (item.unknown_0000[1] != 0 && item.identified != 0) { switch (item.unknown_0000[0]) { case 4: case 5: case 8: case 9: case 11: case 12: case 13: case 14: case 15: if (item.unknown_0000[2] == 0) { break; } case 0: case 1: case 2: case 10: case 16: case 17: case 19: { const int slot = this->SlotAt(x, y); if (slot != -1) { g_session.SetHotkeyEntry(0, item.GetItemId(), 0, slot); RemoveAlternate(g_session.m_requestTimers, value.slot, slot); this->SendMsg(nullptr, 23, 0, 0, 0, 0); this->Invalidate(); } break; } } } } g_modeMgr.gameMode->SendMsg(18, 0, 0, 0, 0); }