diff --git a/.gitmodules b/.gitmodules index 989ce118d..240c84b16 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "Dependencies/GWCA"] path = Dependencies/GWCA - url = https://github.com/GregLando113/GWCA.git + url = https://github.com/gwdevhub/GWCA.git diff --git a/Dependencies/GWCA b/Dependencies/GWCA index d85f7c90c..124690f9c 160000 --- a/Dependencies/GWCA +++ b/Dependencies/GWCA @@ -1 +1 @@ -Subproject commit d85f7c90c99273182e8f690e74f0f689007bff0c +Subproject commit 124690f9cfe19098329fa21fccea72bf089d46d7 diff --git a/GWToolbox/Download.cpp b/GWToolbox/Download.cpp index e2209958f..fa2e127cc 100644 --- a/GWToolbox/Download.cpp +++ b/GWToolbox/Download.cpp @@ -32,6 +32,7 @@ bool Download(std::string& content, const char* url) { RestClient client; client.SetUrl(url); + client.SetFollowLocation(true); client.SetVerifyPeer(false); client.SetTimeoutSec(5); client.SetUserAgent("curl/7.71.1"); @@ -170,7 +171,7 @@ std::string GetDllRelease(const std::filesystem::path& dllpath) bool DownloadWindow::DownloadAllFiles() { std::string content; - if (!Download(content, "https://api.github.com/repos/HasKha/GWToolboxpp/releases/latest")) { + if (!Download(content, "https://api.github.com/repos/gwdevhub/GWToolboxpp/releases/latest")) { fprintf(stderr, "Couldn't download the latest release of GWToolboxpp\n"); // @Remark: // We may not be able to grep Github api. (For instance, if we spam it) diff --git a/GWToolboxdll/CMakeLists.txt b/GWToolboxdll/CMakeLists.txt index c950c3516..28f256f39 100644 --- a/GWToolboxdll/CMakeLists.txt +++ b/GWToolboxdll/CMakeLists.txt @@ -33,11 +33,14 @@ set_target_properties(GWToolboxdll PROPERTIES VS_GLOBAL_EnableClangTidyCodeAnalysis false ) -target_link_options(GWToolboxdll PRIVATE /WX /OPT:REF /OPT:ICF /SAFESEH:NO) -target_link_options(GWToolboxdll PRIVATE $<$>:/LTCG /INCREMENTAL:NO>) -target_link_options(GWToolboxdll PRIVATE $<$:/IGNORE:4098 /OPT:NOREF /OPT:NOICF>) -target_link_options(GWToolboxdll PRIVATE $<$:/OPT:NOICF>) -target_link_options(GWToolboxdll PRIVATE /IGNORE:4099) # pdb not found for github action +target_link_options(GWToolboxdll PRIVATE + /WX /OPT:REF /OPT:ICF /SAFESEH:NO + $<$>:/LTCG /INCREMENTAL:NO> + $<$:/IGNORE:4098 /OPT:NOREF /OPT:NOICF> + $<$:/OPT:NOICF> + $<$:/NODEFAULTLIB:LIBCMT> + /IGNORE:4099 # pdb not found for github action + ) target_include_directories(GWToolboxdll PRIVATE "${PROJECT_SOURCE_DIR}/Dependencies" diff --git a/GWToolboxdll/Defines.h b/GWToolboxdll/Defines.h index ddafe5524..4bdff31ed 100644 --- a/GWToolboxdll/Defines.h +++ b/GWToolboxdll/Defines.h @@ -1,7 +1,7 @@ #pragma once #define GWTOOLBOX_WEBSITE "https://www.gwtoolbox.com" -#define RESOURCES_DOWNLOAD_URL "https://raw.githubusercontent.com/HasKha/GWToolboxpp/master/resources/" +#define RESOURCES_DOWNLOAD_URL "https://raw.githubusercontent.com/gwdevhub/GWToolboxpp/master/resources/" #define DIRECTX_REDIST_WEBSITE "https://www.microsoft.com/en-us/download/details.aspx?id=35" diff --git a/GWToolboxdll/GWToolbox.cpp b/GWToolboxdll/GWToolbox.cpp index 68c5c6bd4..1cc9ddac4 100644 --- a/GWToolboxdll/GWToolbox.cpp +++ b/GWToolboxdll/GWToolbox.cpp @@ -113,7 +113,7 @@ namespace { Resources::EnsureFileExists( Resources::GetPath(L"Font.ttf"), - "https://raw.githubusercontent.com/HasKha/GWToolboxpp/master/resources/Font.ttf", + "https://raw.githubusercontent.com/gwdevhub/GWToolboxpp/master/resources/Font.ttf", [](const bool success, const std::wstring& error) { if (success) { GuiUtils::LoadFonts(); diff --git a/GWToolboxdll/GWToolbox.rc b/GWToolboxdll/GWToolbox.rc index 901d763e0..f5f8e4d21 100644 --- a/GWToolboxdll/GWToolbox.rc +++ b/GWToolboxdll/GWToolbox.rc @@ -47,67 +47,6 @@ END #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -IDB_Missions_MissionIconIncomplete RCDATA "..\\resources\\missions\\MissionIconIncomplete.png" - -IDB_Missions_MissionIconBonus RCDATA "..\\resources\\missions\\MissionIconBonus.png" - -IDB_Missions_MissionIconPrimary RCDATA "..\\resources\\missions\\MissionIconPrimary.png" - -IDB_Missions_MissionIcon RCDATA "..\\resources\\missions\\MissionIcon.png" - -IDB_Missions_FactionsMissionIconIncomplete RCDATA "..\\resources\\missions\\FactionsMissionIconIncomplete.png" - -IDB_Missions_FactionsMissionIconExpert RCDATA "..\\resources\\missions\\FactionsMissionIconExpert.png" - -IDB_Missions_FactionsMissionIconPrimary RCDATA "..\\resources\\missions\\FactionsMissionIconPrimary.png" - -IDB_Missions_FactionsMissionIcon RCDATA "..\\resources\\missions\\FactionsMissionIcon.png" - -IDB_Missions_NightfallMissionIconIncomplete RCDATA "..\\resources\\missions\\NightfallMissionIconIncomplete.png" - -IDB_Missions_NightfallMissionIconExpert RCDATA "..\\resources\\missions\\NightfallMissionIconExpert.png" - -IDB_Missions_NightfallMissionIconPrimary RCDATA "..\\resources\\missions\\NightfallMissionIconPrimary.png" - -IDB_Missions_NightfallMissionIcon RCDATA "..\\resources\\missions\\NightfallMissionIcon.png" - -IDB_Missions_NightfallTormentMissionIconIncomplete RCDATA "..\\resources\\missions\\NightfallTormentMissionIconIncomplete.png" - -IDB_Missions_NightfallTormentMissionIconExpert RCDATA "..\\resources\\missions\\NightfallTormentMissionIconExpert.png" - -IDB_Missions_NightfallTormentMissionIconPrimary RCDATA "..\\resources\\missions\\NightfallTormentMissionIconPrimary.png" - -IDB_Missions_NightfallTormentMissionIcon RCDATA "..\\resources\\missions\\NightfallTormentMissionIcon.png" - -IDB_Missions_EOTNMissionIncomplete RCDATA "..\\resources\\missions\\EOTNMissionIncomplete.png" - -IDB_Missions_EOTNMission RCDATA "..\\resources\\missions\\EOTNMission.png" - -IDB_Missions_EOTNDungeonIncomplete RCDATA "..\\resources\\missions\\EOTNDungeonIncomplete.png" - -IDB_Missions_EOTNDungeon RCDATA "..\\resources\\missions\\EOTNDungeon.png" - -IDB_Missions_HardModeMissionIconIncomplete RCDATA "..\\resources\\missions\\HardModeMissionIconIncomplete.png" - -IDB_Missions_HardModeMissionIcon1 RCDATA "..\\resources\\missions\\HardModeMissionIcon1.png" - -IDB_Missions_HardModeMissionIcon1b RCDATA "..\\resources\\missions\\HardModeMissionIcon1b.png" - -IDB_Missions_HardModeMissionIcon2 RCDATA "..\\resources\\missions\\HardModeMissionIcon2.png" - -IDB_Missions_HardModeMissionIcon RCDATA "..\\resources\\missions\\HardModeMissionIcon.png" - -IDB_Missions_EOTNHardModeMissionIncomplete RCDATA "..\\resources\\missions\\EOTNHardModeMissionIncomplete.png" - -IDB_Missions_EOTNHardModeMission RCDATA "..\\resources\\missions\\EOTNHardModeMission.png" - -IDB_Missions_EOTNHardModeDungeonIncomplete RCDATA "..\\resources\\missions\\EOTNHardModeDungeonIncomplete.png" - -IDB_Missions_Vanquish RCDATA "..\\resources\\missions\\HardModeMissionIcon1.png" - -IDB_Missions_VanquishIncomplete RCDATA "..\\resources\\missions\\HardModeMissionIconIncomplete.png" - #endif // English (United States) resources ///////////////////////////////////////////////////////////////////////////// diff --git a/GWToolboxdll/ImGuiAddons.cpp b/GWToolboxdll/ImGuiAddons.cpp index 5806e327c..3fc97425a 100644 --- a/GWToolboxdll/ImGuiAddons.cpp +++ b/GWToolboxdll/ImGuiAddons.cpp @@ -107,8 +107,7 @@ namespace ImGui { } return *confirm_bool; } - - bool IconButton(const char* label, const ImTextureID icon, const ImVec2& size, const ImGuiButtonFlags flags, const ImVec2& icon_size) + bool CompositeIconButton(const char* label, const ImTextureID* icons, size_t icons_len, const ImVec2& size, const ImGuiButtonFlags flags, const ImVec2& icon_size, const ImVec2& uv0, ImVec2 uv1) { char button_id[128]; sprintf(button_id, "###icon_button_%s", label); @@ -118,22 +117,17 @@ namespace ImGui { const ImVec2& button_size = GetItemRectSize(); ImVec2 img_size = icon_size; - if (!icon) { - img_size = {0.f, 0.f}; + if (icon_size.x > 0.f) { + img_size.x = icon_size.x; } - else { - if (icon_size.x > 0.f) { - img_size.x = icon_size.x; - } - if (icon_size.y > 0.f) { - img_size.y = icon_size.y; - } - if (img_size.y == 0.f) { - img_size.y = button_size.y - 2.f; - } - if (img_size.x == 0.f) { - img_size.x = img_size.y; - } + if (icon_size.y > 0.f) { + img_size.y = icon_size.y; + } + if (img_size.y == 0.f) { + img_size.y = button_size.y - 2.f; + } + if (img_size.x == 0.f) { + img_size.x = img_size.y; } const ImGuiStyle& style = GetStyle(); const float content_width = img_size.x + textsize.x + style.FramePadding.x * 2.f; @@ -146,14 +140,27 @@ namespace ImGui { const float img_y = pos.y + (button_size.y - img_size.y) / 2.f; const float text_x = img_x + img_size.x + 3.f; const float text_y = pos.y + (button_size.y - textsize.y) * style.ButtonTextAlign.y; - if (img_size.x) { - AddImageCropped(icon, ImVec2(img_x, img_y), ImVec2(img_x + img_size.x, img_y + img_size.y)); + const auto top_left = ImVec2(img_x, img_y); + const auto bottom_right = ImVec2(img_x + img_size.x, img_y + img_size.y); + for (size_t i = 0; i < icons_len; i++) { + if (!icons[i]) + continue; + if (uv0.x == uv1.x && uv0.y == uv1.y) { + GetWindowDrawList()->AddImage(icons[i], top_left, bottom_right, uv0, CalculateUvCrop(icons[i], img_size)); + } + else { + GetWindowDrawList()->AddImage(icons[i], top_left, bottom_right, uv0, uv1); + } } if (label) { GetWindowDrawList()->AddText(ImVec2(text_x, text_y), ImColor(GetStyle().Colors[ImGuiCol_Text]), label); } return clicked; } + bool IconButton(const char* label, const ImTextureID icon, const ImVec2& size, const ImGuiButtonFlags flags, const ImVec2& icon_size) + { + return CompositeIconButton(label, &icon, 1, size, flags, icon_size); + } bool ColorButtonPicker(const char* label, Color* imcol, const ImGuiColorEditFlags flags) { @@ -300,6 +307,36 @@ namespace ImGui { } return uv1; } + // Given a texture, sprite size in px and the offset of the sprite we want, fill out uv0 and uv1 coords for percentage offsets. False on failure. + bool GetSpriteUvCoords(const ImTextureID user_texture_id, const ImVec2& single_sprite_size, uint32_t sprite_offset[2], ImVec2* uv0_out, ImVec2* uv1_out) { + if (!user_texture_id) + return false; + const auto texture = static_cast(user_texture_id); + D3DSURFACE_DESC desc; + const HRESULT res = texture->GetLevelDesc(0, &desc); + if (!SUCCEEDED(res)) { + return false; // Don't throw anything into the log here; this function is called every frame by modules that use it! + } + + ImVec2 img_dimensions = { static_cast(desc.Width), static_cast(desc.Height) }; + + ImVec2 start_px_offset = { single_sprite_size.x * sprite_offset[0], single_sprite_size.y * sprite_offset[1] }; + if (start_px_offset.x >= img_dimensions.x + || start_px_offset.y >= img_dimensions.y) { + return false; + } + ImVec2 end_px_offset = { start_px_offset.x + single_sprite_size.x, start_px_offset.y + single_sprite_size.y }; + if (end_px_offset.x >= img_dimensions.x + || end_px_offset.y >= img_dimensions.y) { + return false; + } + uv0_out->x = start_px_offset.x / img_dimensions.x; + uv0_out->y = end_px_offset.y / img_dimensions.y; + + uv1_out->x = end_px_offset.x / img_dimensions.x; + uv1_out->x = end_px_offset.y / img_dimensions.y; + return true; + } void ImageCropped(const ImTextureID user_texture_id, const ImVec2& size) { diff --git a/GWToolboxdll/ImGuiAddons.h b/GWToolboxdll/ImGuiAddons.h index 078d82402..424f024a6 100644 --- a/GWToolboxdll/ImGuiAddons.h +++ b/GWToolboxdll/ImGuiAddons.h @@ -26,8 +26,12 @@ namespace ImGui { IMGUI_API bool ConfirmButton(const char* label, bool* confirm_bool, const char* confirm_content = "Are you sure you want to continue?"); + // Button with single icon texture IMGUI_API bool IconButton(const char* label, ImTextureID icon, const ImVec2& size, ImGuiButtonFlags flags = ImGuiButtonFlags_None, const ImVec2& icon_size = {0.f, 0.f}); + // Button with 1 or more icon textures overlaid + IMGUI_API bool CompositeIconButton(const char* label, const ImTextureID* icons, size_t icons_len, const ImVec2& size, ImGuiButtonFlags flags = ImGuiButtonFlags_None, const ImVec2& icon_size = {0.f, 0.f}, const ImVec2& uv0 = {0.f, 0.f}, ImVec2 uv1 = {0.f, 0.f}); + IMGUI_API bool ColorButtonPicker(const char*, Color*, ImGuiColorEditFlags = 0); // Add cropped image to current window IMGUI_API void ImageCropped(ImTextureID user_texture_id, const ImVec2& size); diff --git a/GWToolboxdll/Modules/ChatCommands.cpp b/GWToolboxdll/Modules/ChatCommands.cpp index 6bf7dd6a1..f7ea92d2e 100644 --- a/GWToolboxdll/Modules/ChatCommands.cpp +++ b/GWToolboxdll/Modules/ChatCommands.cpp @@ -1462,9 +1462,7 @@ void ChatCommands::CmdChest(const wchar_t*, int, LPWSTR*) if (!IsMapReady()) { return; } - if (GW::Map::GetInstanceType() == GW::Constants::InstanceType::Outpost) { - GW::Items::OpenXunlaiWindow(); - } + GW::Items::OpenXunlaiWindow(); } void ChatCommands::CmdTB(const wchar_t* message, const int argc, LPWSTR* argv) diff --git a/GWToolboxdll/Modules/DiscordModule.cpp b/GWToolboxdll/Modules/DiscordModule.cpp index 599b134d7..34d5654f9 100644 --- a/GWToolboxdll/Modules/DiscordModule.cpp +++ b/GWToolboxdll/Modules/DiscordModule.cpp @@ -337,7 +337,7 @@ void DiscordModule::Initialize() dll_location = Resources::GetPath(L"discord_game_sdk.dll"); // NOTE: We're using the one we know matches our API version, not checking for any other discord dll on the machine. Resources::EnsureFileExists(dll_location, - "https://raw.githubusercontent.com/HasKha/GWToolboxpp/master/resources/discord_game_sdk.dll", + "https://raw.githubusercontent.com/gwdevhub/GWToolboxpp/master/resources/discord_game_sdk.dll", [&](const bool success, const std::wstring& error) { if (!success || !LoadDll()) { Log::LogW(L"Failed to load discord_game_sdk.dll. To try again, please restart GWToolbox\n%s", error.c_str()); diff --git a/GWToolboxdll/Modules/GameSettings.cpp b/GWToolboxdll/Modules/GameSettings.cpp index 5878c7b54..dc7706e30 100644 --- a/GWToolboxdll/Modules/GameSettings.cpp +++ b/GWToolboxdll/Modules/GameSettings.cpp @@ -162,7 +162,6 @@ namespace { bool drop_ua_on_cast = false; bool focus_window_on_launch = true; - bool flash_window_on_party_invite = false; bool flash_window_on_zoning = false; bool focus_window_on_zoning = false; bool flash_window_on_cinematic = true; @@ -526,7 +525,7 @@ namespace { GW::MemoryPatcher skip_map_entry_message_patch; - // Refresh agent name tags when allegiance changes ( https://github.com/HasKha/GWToolboxpp/issues/781 ) + // Refresh agent name tags when allegiance changes ( https://github.com/gwdevhub/GWToolboxpp/issues/781 ) void OnAgentAllegianceChanged(GW::HookStatus*, GW::Packet::StoC::AgentUpdateAllegiance*) { // Backup the current name tag flag state, then "flash" nametags to update. @@ -975,9 +974,6 @@ namespace { ImGui::Indent(); ImGui::StartSpacedElements(checkbox_w); ImGui::NextSpacedElement(); - ImGui::NextSpacedElement(); - ImGui::Checkbox("Receiving a party invite", &flash_window_on_party_invite); - ImGui::NextSpacedElement(); ImGui::Checkbox("Zoning in a new map", &flash_window_on_zoning); ImGui::NextSpacedElement(); ImGui::Checkbox("Cinematic start/end", &flash_window_on_cinematic); @@ -1001,7 +997,7 @@ namespace { ImGui::Checkbox("A player starts trade with you###focus_window_on_trade", &focus_window_on_trade); ImGui::Unindent(); - ImGui::Text("Show a message when a friend:"); + ImGui::Text("Show a chat message when a friend:"); ImGui::Indent(); ImGui::StartSpacedElements(checkbox_w); ImGui::NextSpacedElement(); @@ -1014,7 +1010,7 @@ namespace { ImGui::Checkbox("Leaves your outpost###notify_when_friends_leave_outpost", ¬ify_when_friends_leave_outpost); ImGui::Unindent(); - ImGui::Text("Show a message when a player:"); + ImGui::Text("Show a chat message when a player:"); ImGui::Indent(); ImGui::StartSpacedElements(checkbox_w); ImGui::NextSpacedElement(); @@ -1549,7 +1545,6 @@ void GameSettings::LoadSettings(ToolboxIni* ini) LOAD_BOOL(move_item_to_current_storage_pane); LOAD_BOOL(move_materials_to_current_storage_pane); - LOAD_BOOL(flash_window_on_party_invite); LOAD_BOOL(flash_window_on_zoning); LOAD_BOOL(flash_window_on_cinematic); LOAD_BOOL(focus_window_on_launch); @@ -1710,7 +1705,6 @@ void GameSettings::SaveSettings(ToolboxIni* ini) SAVE_BOOL(move_materials_to_current_storage_pane); SAVE_BOOL(stop_screen_shake); - SAVE_BOOL(flash_window_on_party_invite); SAVE_BOOL(flash_window_on_zoning); SAVE_BOOL(focus_window_on_launch); SAVE_BOOL(focus_window_on_zoning); @@ -2240,32 +2234,15 @@ void GameSettings::OnPartyInviteReceived(const GW::HookStatus* status, const GW: GW::PartyMgr::RespondToPartyRequest(packet->target_party_id, true); } } - if (flash_window_on_party_invite) { - FlashWindow(); - } } // Flash window on player added -void GameSettings::OnPartyPlayerJoined(const GW::HookStatus*, const GW::Packet::StoC::PartyPlayerAdd* packet) +void GameSettings::OnPartyPlayerJoined(const GW::HookStatus*, const GW::Packet::StoC::PartyPlayerAdd*) { if (GW::Map::GetInstanceType() != GW::Constants::InstanceType::Outpost) { return; } check_message_on_party_change = true; - if (flash_window_on_party_invite) { - const GW::PartyInfo* current_party = GW::PartyMgr::GetPartyInfo(); - if (!current_party) { - return; - } - const GW::AgentLiving* me = GW::Agents::GetPlayerAsAgentLiving(); - if (!me) { - return; - } - if (packet->player_id == me->login_number - || (packet->party_id == current_party->party_id && GW::PartyMgr::GetIsLeader())) { - FlashWindow(); - } - } } // Block overhead arrow marker for zaishen scout diff --git a/GWToolboxdll/Modules/GuildWarsSettingsModule.cpp b/GWToolboxdll/Modules/GuildWarsSettingsModule.cpp index 51e207121..d60654a17 100644 --- a/GWToolboxdll/Modules/GuildWarsSettingsModule.cpp +++ b/GWToolboxdll/Modules/GuildWarsSettingsModule.cpp @@ -14,6 +14,7 @@ #include "GuildWarsSettingsModule.h" #include #include +#include #include #include @@ -227,6 +228,7 @@ namespace { std::vector preference_flags{}; std::vector key_mappings{}; std::vector window_positions{}; + RECT gw_window_pos = { 0 }; }; // Read preferences from in-game memory to a PreferencesStruct @@ -252,6 +254,7 @@ namespace { for (auto i = 0u; key_mappings_array && i < key_mappings_array_length; i++) { out.key_mappings[i] = key_mappings_array[i]; } + ASSERT(GetWindowRect(GW::MemoryMgr::GetGWWindowHandle(), &out.gw_window_pos)); } // Write preferences to the game from a PreferencesStruct. Run this on the game thread. @@ -272,6 +275,15 @@ namespace { for (auto i = 0u; i < in.key_mappings.size() && i < key_mappings_array_length; i++) { key_mappings_array[i] = in.key_mappings[i]; } + + if (GW::UI::GetPreference(GW::UI::NumberPreference::ScreenBorderless) == 0) { + const auto whnd = GW::MemoryMgr::GetGWWindowHandle(); + const auto w = in.gw_window_pos.right - in.gw_window_pos.left; + const auto h = in.gw_window_pos.bottom - in.gw_window_pos.top; + if (w > 10 && h > 10) { + SetWindowPos(whnd, 0, in.gw_window_pos.left, in.gw_window_pos.top, w, h, SWP_ASYNCWINDOWPOS | SWP_NOZORDER); + } + } } // Read preferences from an ini file to a PreferencesStruct @@ -306,6 +318,10 @@ namespace { snprintf(key_buf, _countof(key_buf), "0x%02x_p2_y", window_id); window_pos.p2.y = static_cast(ini.GetDoubleValue(ini_label_windows, key_buf, window_pos.p2.y)); } + prefs.gw_window_pos.top = ini.GetLongValue(ini_label_windows, "gw_window_top", 0); + prefs.gw_window_pos.left = ini.GetLongValue(ini_label_windows, "gw_window_left", 0); + prefs.gw_window_pos.right = ini.GetLongValue(ini_label_windows, "gw_window_right", 0); + prefs.gw_window_pos.bottom = ini.GetLongValue(ini_label_windows, "gw_window_bottom", 0); } // Write preferences to an ini file from a PreferencesStruct @@ -341,6 +357,10 @@ namespace { snprintf(key_buf, _countof(key_buf), "0x%02x_p2_y", window_id); ini.SetDoubleValue(ini_label_windows, key_buf, window_pos.p2.y); } + ini.SetLongValue(ini_label_windows, "gw_window_top", prefs.gw_window_pos.top); + ini.SetLongValue(ini_label_windows, "gw_window_left", prefs.gw_window_pos.left); + ini.SetLongValue(ini_label_windows, "gw_window_right", prefs.gw_window_pos.right); + ini.SetLongValue(ini_label_windows, "gw_window_bottom", prefs.gw_window_pos.bottom); } void OnPreferencesLoadFileChosen(const char* result) diff --git a/GWToolboxdll/Modules/InventoryManager.cpp b/GWToolboxdll/Modules/InventoryManager.cpp index e369f4092..a1e524b34 100644 --- a/GWToolboxdll/Modules/InventoryManager.cpp +++ b/GWToolboxdll/Modules/InventoryManager.cpp @@ -669,10 +669,7 @@ namespace { return; } case UseItem: { - const GW::Item* item = GW::Items::GetItemById(tome_pending_item_id); - if (item) { - GW::Items::UseItem(item); - } + GW::Items::UseItem(GW::Items::GetItemById(tome_pending_item_id)); } } cancel: diff --git a/GWToolboxdll/Modules/LoginModule.cpp b/GWToolboxdll/Modules/LoginModule.cpp index ae9dfb68b..77c38eaa2 100644 --- a/GWToolboxdll/Modules/LoginModule.cpp +++ b/GWToolboxdll/Modules/LoginModule.cpp @@ -17,6 +17,17 @@ namespace { clock_t state_timestamp = 0; uint32_t char_sort_order = std::numeric_limits::max(); + void SetCharSortOrder(uint32_t order) + { + const auto current_sort_order = GetPreference(GW::UI::EnumPreference::CharSortOrder); + if (char_sort_order == std::numeric_limits::max()) { + char_sort_order = current_sort_order; + } + if (current_sort_order != order && order != std::numeric_limits::max()) { + SetPreference(GW::UI::EnumPreference::CharSortOrder, order); + } + } + enum class LoginState { Idle, PendingLogin, @@ -68,7 +79,7 @@ namespace { void OnPortalAccountLogin(const uint32_t transaction_id, uint32_t* user_id, uint32_t* session_id, wchar_t* preselect_character) { GW::Hook::EnterHook(); - // Don't pre-select the character yet; we'll do this in the Update() loop after sucessful login + // Don't pre-select the character yet; we'll do this in the Update() loop after successful login preselect_character[0] = 0; PortalAccountLogin_Ret(transaction_id, user_id, session_id, preselect_character); state_timestamp = TIMER_INIT(); @@ -136,9 +147,9 @@ void LoginModule::Update(float) state = LoginState::Idle; return; } - if (char_sort_order == std::numeric_limits::max()) { - char_sort_order = GetPreference(GW::UI::EnumPreference::CharSortOrder); - SetPreference(GW::UI::EnumPreference::CharSortOrder, static_cast(GW::Constants::Preference::CharSortOrder::Alphabetize)); + if (original_charname_parameter != nullptr && *original_charname_parameter != '\0') { + // we want to pre-select a character, set order to alphabetical so we don't get stuck on empty char slots when using arrow keys + SetCharSortOrder(static_cast(GW::Constants::Preference::CharSortOrder::Alphabetize)); } if (IsCharSelectReady()) { state = LoginState::FindCharacterIndex; @@ -158,8 +169,8 @@ void LoginModule::Update(float) state = LoginState::SelectChar; reroll_index_needed = i; reroll_index_current = 0xffff; - // Wipe out the command line parameter for GW here; its only relevent for the first login! - *original_charname_parameter = 0; + // Wipe out the command line parameter for GW here; its only relevant for the first login! + *original_charname_parameter = '\0'; return; } } @@ -181,9 +192,7 @@ void LoginModule::Update(float) if (pgc->index_1 == reroll_index_needed) { // We're on the character that was asked for state = LoginState::Idle; - if (char_sort_order != std::numeric_limits::max() && char_sort_order != GetPreference(GW::UI::EnumPreference::CharSortOrder)) { - SetPreference(GW::UI::EnumPreference::CharSortOrder, char_sort_order); - } + SetCharSortOrder(char_sort_order); return; } reroll_index_current = pgc->index_1; diff --git a/GWToolboxdll/Modules/ObserverModule.h b/GWToolboxdll/Modules/ObserverModule.h index 4511d95df..adac65dc5 100644 --- a/GWToolboxdll/Modules/ObserverModule.h +++ b/GWToolboxdll/Modules/ObserverModule.h @@ -398,8 +398,8 @@ class ObserverModule : public ToolboxModule { public: ObservableMap(const GW::AreaInfo& area_info); - uint32_t campaign; - uint32_t continent; + GW::Constants::Campaign campaign; + GW::Continent continent; GW::Region region; GW::RegionType type; uint32_t flags; diff --git a/GWToolboxdll/Modules/Resources.cpp b/GWToolboxdll/Modules/Resources.cpp index e63c27325..2a7852407 100644 --- a/GWToolboxdll/Modules/Resources.cpp +++ b/GWToolboxdll/Modules/Resources.cpp @@ -35,6 +35,8 @@ namespace { const char* d3dErrorMessage(HRESULT code) { switch (code) { + case E_INVALIDARG: + return "E_INVALIDARG One or more arguments are invalid."; case D3DERR_NOTAVAILABLE: return "D3DERR_NOTAVAILABLE"; case D3DERR_OUTOFVIDEOMEMORY: diff --git a/GWToolboxdll/Modules/ToastNotifications.cpp b/GWToolboxdll/Modules/ToastNotifications.cpp index 1d1eeee26..bb2e4d72e 100644 --- a/GWToolboxdll/Modules/ToastNotifications.cpp +++ b/GWToolboxdll/Modules/ToastNotifications.cpp @@ -37,6 +37,7 @@ namespace { bool show_notifications_on_guild_chat = false; bool show_notifications_on_ally_chat = false; bool show_notifications_on_last_to_ready = false; + bool show_notifications_on_invite = false; bool show_notifications_on_everyone_ready = false; bool show_notifications_on_self_resurrected = false; @@ -44,6 +45,7 @@ namespace { bool flash_window_on_guild_chat = false; bool flash_window_on_ally_chat = false; bool flash_window_on_last_to_ready = false; + bool flash_window_on_invite = false; bool flash_window_on_everyone_ready = false; bool flash_window_on_self_resurrected = false; @@ -277,6 +279,23 @@ namespace { } } + void OnPartyInviteReceived(const GW::HookStatus* status, GW::Packet::StoC::PacketBase*) + { + if (status->blocked) { + return; + } + if (GW::Map::GetInstanceType() != GW::Constants::InstanceType::Outpost || !GW::PartyMgr::GetIsLeader()) { + return; + } + + if (show_notifications_on_invite) { + ToastNotifications::SendToast(L"Party Invite", L"You have been invited to a party!", OnGenericToastActivated); + } + if (flash_window_on_invite) { + FlashWindow(); + } + } + struct StoC_Callback { uint32_t header = 0; GW::StoC::PacketCallback cb; @@ -290,7 +309,8 @@ namespace { {GAME_SMSG_CHAT_MESSAGE_GLOBAL, OnMessageGlobal}, {GAME_SMSG_PARTY_PLAYER_READY, OnPartyPlayerReady}, {GAME_SMSG_INSTANCE_LOADED, OnMapChange}, - {GAME_SMSG_AGENT_UPDATE_EFFECTS, OnAgentUpdateEffects} + {GAME_SMSG_AGENT_UPDATE_EFFECTS, OnAgentUpdateEffects}, + {GAME_SMSG_PARTY_JOIN_REQUEST,OnPartyInviteReceived} }; } // namespace ToastNotifications::Toast::Toast(const std::wstring& _title, const std::wstring& _message) @@ -441,6 +461,8 @@ void ToastNotifications::DrawSettingsInternal() ImGui::NextSpacedElement(); ImGui::Checkbox("Alliance Chat", &show_notifications_on_ally_chat); ImGui::NextSpacedElement(); + ImGui::Checkbox("Party Invite", &show_notifications_on_invite); + ImGui::NextSpacedElement(); ImGui::Checkbox("Last to Tick", &show_notifications_on_last_to_ready); ImGui::NextSpacedElement(); ImGui::Checkbox("Everyone Ticked", &show_notifications_on_everyone_ready); @@ -460,6 +482,8 @@ void ToastNotifications::DrawSettingsInternal() ImGui::NextSpacedElement(); ImGui::Checkbox("Alliance Chat", &flash_window_on_ally_chat); ImGui::NextSpacedElement(); + ImGui::Checkbox("Party Invite", &flash_window_on_invite); + ImGui::NextSpacedElement(); ImGui::Checkbox("Last to Tick", &flash_window_on_last_to_ready); ImGui::NextSpacedElement(); ImGui::Checkbox("Everyone Ticked", &flash_window_on_everyone_ready); @@ -497,6 +521,7 @@ void ToastNotifications::LoadSettings(ToolboxIni* ini) LOAD_BOOL(show_notifications_on_whisper); LOAD_BOOL(show_notifications_on_guild_chat); LOAD_BOOL(show_notifications_on_ally_chat); + LOAD_BOOL(show_notifications_on_invite); LOAD_BOOL(show_notifications_on_last_to_ready); LOAD_BOOL(show_notifications_on_everyone_ready); LOAD_BOOL(show_notifications_on_self_resurrected); @@ -504,6 +529,7 @@ void ToastNotifications::LoadSettings(ToolboxIni* ini) LOAD_BOOL(flash_window_on_whisper); LOAD_BOOL(flash_window_on_guild_chat); LOAD_BOOL(flash_window_on_ally_chat); + LOAD_BOOL(flash_window_on_invite); LOAD_BOOL(flash_window_on_last_to_ready); LOAD_BOOL(flash_window_on_everyone_ready); LOAD_BOOL(flash_window_on_self_resurrected); diff --git a/GWToolboxdll/Modules/Updater.cpp b/GWToolboxdll/Modules/Updater.cpp index 0f874e288..59d0469b6 100644 --- a/GWToolboxdll/Modules/Updater.cpp +++ b/GWToolboxdll/Modules/Updater.cpp @@ -50,7 +50,7 @@ namespace { // Get list of releases std::string response; unsigned int tries = 0; - const auto url = "https://api.github.com/repos/HasKha/GWToolboxpp/releases"; + const auto url = "https://api.github.com/repos/gwdevhub/GWToolboxpp/releases"; bool success = false; do { success = Resources::Instance().Download(url, response); diff --git a/GWToolboxdll/Utils/ToolboxUtils.cpp b/GWToolboxdll/Utils/ToolboxUtils.cpp index 7cd943f8c..f2ce576a4 100644 --- a/GWToolboxdll/Utils/ToolboxUtils.cpp +++ b/GWToolboxdll/Utils/ToolboxUtils.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -31,9 +32,70 @@ namespace { { return item && item->info_string && wcschr(item->info_string, 0xAC9); } + bool ArrayBoolAt(const GW::Array& array, const uint32_t index) + { + const uint32_t real_index = index / 32; + if (real_index >= array.size()) { + return false; + } + const uint32_t shift = index % 32; + const uint32_t flag = 1 << shift; + const auto res = (array[real_index] & flag); + return res != 0; + } } namespace ToolboxUtils { + uint8_t GetMissionState(GW::Constants::MapID map_id, const GW::Array& missions_completed, const GW::Array& missions_bonus) { + const auto area_info = GW::Map::GetMapInfo(map_id); + switch (area_info->type) { + case GW::RegionType::CooperativeMission: + case GW::RegionType::MissionOutpost: + case GW::RegionType::Dungeon: + break; + default: + return 0; + } + uint8_t state_out = 0; + auto complete = ArrayBoolAt(missions_completed, (uint32_t)map_id); + auto bonus = ArrayBoolAt(missions_bonus, (uint32_t)map_id); + + auto primary = complete; + auto expert = bonus; + auto master = false; + + switch (area_info->campaign) { + case GW::Constants::Campaign::Factions: + case GW::Constants::Campaign::Nightfall: + // Master = bonus, Expert = complete, Primary = any + master = bonus; + expert = complete; + primary = master || expert; + break; + } + + if(primary) + state_out |= MissionState::Primary; + if(expert) + state_out |= MissionState::Expert; + if(master) + state_out |= MissionState::Master; + return state_out; + } + uint8_t GetMissionState(GW::Constants::MapID map_id, bool is_hard_mode) { + const auto w = GW::GetWorldContext(); + const GW::Array* missions_completed = &w->missions_completed; + const GW::Array* missions_bonus = &w->missions_bonus; + if (is_hard_mode) { + missions_completed = &w->missions_completed_hm; + missions_bonus = &w->missions_bonus_hm; + } + return GetMissionState(map_id, *missions_completed, *missions_bonus); + } + uint8_t GetMissionState() { + return GetMissionState(GW::Map::GetMapID(), GW::PartyMgr::GetIsPartyInHardMode()); + } + bool IsOutpost() { return GW::Map::GetInstanceType() == GW::Constants::InstanceType::Outpost; diff --git a/GWToolboxdll/Utils/ToolboxUtils.h b/GWToolboxdll/Utils/ToolboxUtils.h index 8ebc210c9..0d649f327 100644 --- a/GWToolboxdll/Utils/ToolboxUtils.h +++ b/GWToolboxdll/Utils/ToolboxUtils.h @@ -32,6 +32,19 @@ namespace ToolboxUtils { bool IsOutpost(); bool IsExplorable(); + enum MissionState : uint8_t { + Primary = 0x1, + Expert = 0x2, + Master = 0x4 + }; + + // Returns bitwise state of MissionState enum + uint8_t GetMissionState(GW::Constants::MapID map_id, const GW::Array& missions_completed, const GW::Array& missions_bonus); + // Returns bitwise state of MissionState enum + uint8_t GetMissionState(GW::Constants::MapID map_id, bool is_hard_mode = false); + // Returns bitwise state of MissionState enum + uint8_t GetMissionState(); + // Player // Return player name without guild tag or player number diff --git a/GWToolboxdll/Widgets/EffectsMonitorWidget.cpp b/GWToolboxdll/Widgets/EffectsMonitorWidget.cpp index 99510bb5d..112928701 100644 --- a/GWToolboxdll/Widgets/EffectsMonitorWidget.cpp +++ b/GWToolboxdll/Widgets/EffectsMonitorWidget.cpp @@ -530,6 +530,7 @@ void EffectsMonitorWidget::Draw(IDirect3DDevice9*) ImGui::PopFont(); ImGui::End(); ImGui::PopStyleVar(2); + return; } } else if (effect.skill_id == GW::Constants::SkillID::Hard_mode) { diff --git a/GWToolboxdll/Widgets/Minimap/Minimap.cpp b/GWToolboxdll/Widgets/Minimap/Minimap.cpp index 165771ab4..e50e1f82e 100644 --- a/GWToolboxdll/Widgets/Minimap/Minimap.cpp +++ b/GWToolboxdll/Widgets/Minimap/Minimap.cpp @@ -614,7 +614,7 @@ void Minimap::LoadSettings(ToolboxIni* ini) ToolboxWidget::LoadSettings(ini); Resources::EnsureFileExists( Resources::GetPath(L"Markers.ini"), - "https://raw.githubusercontent.com/HasKha/GWToolboxpp/master/resources/Markers.ini", + "https://raw.githubusercontent.com/gwdevhub/GWToolboxpp/master/resources/Markers.ini", [](const bool success, const std::wstring& error) { if (success) { Instance().custom_renderer.LoadMarkers(); diff --git a/GWToolboxdll/Windows/CompletionWindow.cpp b/GWToolboxdll/Windows/CompletionWindow.cpp index b56b6dcc0..efacc1e64 100644 --- a/GWToolboxdll/Windows/CompletionWindow.cpp +++ b/GWToolboxdll/Windows/CompletionWindow.cpp @@ -25,6 +25,7 @@ #include #include +#include #include @@ -36,6 +37,8 @@ #include #include +#include + using namespace GW::Constants; using namespace Missions; using namespace CompletionWindow_Constants; @@ -93,6 +96,141 @@ namespace { wchar_t last_player_name[20]; + void GetOutpostIcons(GW::Constants::MapID map_id, IDirect3DTexture9** icons_out[4], uint8_t mission_state, bool is_hard_mode = false) { + memset(icons_out, 0, sizeof(icons_out) * sizeof(*icons_out)); + + WorldMapIcon icon_file_ids[4] = { WorldMapIcon::None }; + uint32_t icon_idx = 0; + + auto hardModeMissionIcons = [mission_state](WorldMapIcon icon_file_ids[4], bool has_master = true) { + uint32_t icon_idx = 0; + if ((mission_state & ToolboxUtils::MissionState::Primary) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::HardMode_CompletePrimary; + if((mission_state & ToolboxUtils::MissionState::Expert) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::HardMode_CompleteExpert; + + if (has_master && (mission_state & ToolboxUtils::MissionState::Master) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::HardMode_CompleteMaster; + if (!has_master + && (mission_state & ToolboxUtils::MissionState::Expert) != 0 + && (mission_state & ToolboxUtils::MissionState::Primary) != 0) { + icon_file_ids[icon_idx++] = WorldMapIcon::HardMode_CompleteAll; + } + else if (has_master && (mission_state & ToolboxUtils::MissionState::Master) != 0) { + icon_file_ids[icon_idx++] = WorldMapIcon::HardMode_CompleteAll; + } + else { + icon_file_ids[icon_idx++] = WorldMapIcon::HardMode; + } + }; + const auto area_info = GW::Map::GetMapInfo(map_id); + + if (area_info->type == GW::RegionType::ExplorableZone) { + *icon_file_ids = WorldMapIcon::HardMode; + if ((mission_state & ToolboxUtils::MissionState::Primary) != 0) + *icon_file_ids = WorldMapIcon::HardMode_CompleteAll; + } + else { + // TODO: Change these icons out for Hard Mode + switch (area_info->continent) { + case GW::Continent::Kryta: { + switch (area_info->type) { + case GW::RegionType::MissionOutpost: + case GW::RegionType::CooperativeMission: + if (is_hard_mode) { + hardModeMissionIcons(icon_file_ids, false); + } + else { + if ((mission_state & ToolboxUtils::MissionState::Primary) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::Kryta_CompletePrimary; + if((mission_state & ToolboxUtils::MissionState::Expert) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::Kryta_CompleteSecondary; + icon_file_ids[icon_idx++] = WorldMapIcon::Kryta_Mission; + } + break; + case GW::RegionType::City: + icon_file_ids[0] = WorldMapIcon::Kryta_City; + break; + case GW::RegionType::Outpost: + icon_file_ids[0] = WorldMapIcon::Kryta_Outpost; + break; + case GW::RegionType::Challenge: + icon_file_ids[0] = WorldMapIcon::Kryta_Outpost; + break; + case GW::RegionType::Dungeon: + icon_file_ids[0] = is_hard_mode ? WorldMapIcon::EyeOfTheNorth_HardMode_Dungeon : WorldMapIcon::EyeOfTheNorth_Dungeon; + break; + case GW::RegionType::EotnMission: + icon_file_ids[0] = is_hard_mode ? WorldMapIcon::EyeOfTheNorth_HardMode_Mission : WorldMapIcon::EyeOfTheNorth_Mission; + break; + } + } break; + case GW::Continent::Cantha: { + switch (area_info->type) { + case GW::RegionType::MissionOutpost: + case GW::RegionType::CooperativeMission: + if (is_hard_mode) { + hardModeMissionIcons(icon_file_ids); + } + else { + icon_file_ids[icon_idx++] = WorldMapIcon::Cantha_Mission; + if ((mission_state & ToolboxUtils::MissionState::Primary) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::Cantha_CompletePrimary; + if((mission_state & ToolboxUtils::MissionState::Expert) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::Cantha_CompleteExpert; + if((mission_state & ToolboxUtils::MissionState::Master) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::Cantha_CompleteMaster; + } + + break; + } + } break; + case GW::Continent::Elona: { + switch (area_info->type) { + case GW::RegionType::MissionOutpost: + case GW::RegionType::CooperativeMission: + if (is_hard_mode) { + hardModeMissionIcons(icon_file_ids); + } + else { + icon_file_ids[icon_idx++] = WorldMapIcon::Elona_Mission; + if ((mission_state & ToolboxUtils::MissionState::Primary) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::Elona_CompletePrimary; + if((mission_state & ToolboxUtils::MissionState::Expert) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::Elona_CompleteExpert; + if((mission_state & ToolboxUtils::MissionState::Master) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::Elona_CompleteMaster; + } + break; + } + } break; + case GW::Continent::RealmOfTorment: { + switch (area_info->type) { + case GW::RegionType::MissionOutpost: + case GW::RegionType::CooperativeMission: + if (is_hard_mode) { + hardModeMissionIcons(icon_file_ids); + } + else { + icon_file_ids[icon_idx++] = WorldMapIcon::RealmOfTorment_Mission; + if ((mission_state & ToolboxUtils::MissionState::Primary) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::Elona_CompletePrimary; + if ((mission_state & ToolboxUtils::MissionState::Expert) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::Elona_CompleteExpert; + if ((mission_state & ToolboxUtils::MissionState::Master) != 0) + icon_file_ids[icon_idx++] = WorldMapIcon::Elona_CompleteMaster; + } + break; + } + } break; + } + } + for (size_t i = 0; i < _countof(icon_file_ids) && icon_file_ids[i] != WorldMapIcon::None;i++) { + icons_out[i] = GwDatTextureModule::LoadTextureFromFileId(static_cast(icon_file_ids[i])); + } + + } + bool show_as_list = true; std::wstring chosen_player_name; @@ -268,21 +406,6 @@ namespace { } } - void LoadTextures(std::vector& mission_images) - { - Resources::EnsureFolderExists(Resources::GetPath(L"img", L"missions")); - for (auto& mission_image : mission_images) { - if (mission_image.texture) { - continue; - } - Resources::LoadTexture( - &mission_image.texture, - Resources::GetPath(L"img/missions", mission_image.file_name), - static_cast(mission_image.resource_id) - ); - } - } - // ReSharper disable once CppParameterMayBeConst // ReSharper disable once CppParameterMayBeConstPtrOrRef void OnHomLoaded(HallOfMonumentsAchievements* result) @@ -462,81 +585,8 @@ namespace { } return subject; } -} -Mission::MissionImageList PropheciesMission::normal_mode_images({ - {L"MissionIconIncomplete.png", IDB_Missions_MissionIconIncomplete}, - {L"MissionIconPrimary.png", IDB_Missions_MissionIconPrimary}, - {L"MissionIconBonus.png", IDB_Missions_MissionIconBonus}, - {L"MissionIcon.png", IDB_Missions_MissionIcon}, -}); -Mission::MissionImageList PropheciesMission::hard_mode_images({ - {L"HardModeMissionIconIncomplete.png", IDB_Missions_HardModeMissionIconIncomplete}, - {L"HardModeMissionIcon1.png", IDB_Missions_HardModeMissionIcon1}, - {L"HardModeMissionIcon1b.png", IDB_Missions_HardModeMissionIcon1b}, - {L"HardModeMissionIcon2.png", IDB_Missions_HardModeMissionIcon2}, -}); - -Mission::MissionImageList FactionsMission::normal_mode_images({ - {L"FactionsMissionIconIncomplete.png", IDB_Missions_FactionsMissionIconIncomplete}, - {L"FactionsMissionIconPrimary.png", IDB_Missions_FactionsMissionIconPrimary}, - {L"FactionsMissionIconExpert.png", IDB_Missions_FactionsMissionIconExpert}, - {L"FactionsMissionIcon.png", IDB_Missions_FactionsMissionIcon}, -}); -Mission::MissionImageList FactionsMission::hard_mode_images({ - {L"HardModeMissionIconIncomplete.png", IDB_Missions_HardModeMissionIconIncomplete}, - {L"HardModeMissionIcon1.png", IDB_Missions_HardModeMissionIcon1}, - {L"HardModeMissionIcon2.png", IDB_Missions_HardModeMissionIcon2}, - {L"HardModeMissionIcon.png", IDB_Missions_HardModeMissionIcon}, -}); - -Mission::MissionImageList NightfallMission::normal_mode_images({ - {L"NightfallMissionIconIncomplete.png", IDB_Missions_NightfallMissionIconIncomplete}, - {L"NightfallMissionIconPrimary.png", IDB_Missions_NightfallMissionIconPrimary}, - {L"NightfallMissionIconExpert.png", IDB_Missions_NightfallMissionIconExpert}, - {L"NightfallMissionIcon.png", IDB_Missions_NightfallMissionIcon}, -}); -Mission::MissionImageList NightfallMission::hard_mode_images({ - {L"HardModeMissionIconIncomplete.png", IDB_Missions_HardModeMissionIconIncomplete}, - {L"HardModeMissionIcon1.png", IDB_Missions_HardModeMissionIcon1}, - {L"HardModeMissionIcon2.png", IDB_Missions_HardModeMissionIcon2}, - {L"HardModeMissionIcon.png", IDB_Missions_HardModeMissionIcon}, -}); - -Mission::MissionImageList TormentMission::normal_mode_images({ - {L"NightfallTormentMissionIconIncomplete.png", IDB_Missions_NightfallTormentMissionIconIncomplete}, - {L"NightfallTormentMissionIconPrimary.png", IDB_Missions_NightfallTormentMissionIconPrimary}, - {L"NightfallTormentMissionIconExpert.png", IDB_Missions_NightfallTormentMissionIconExpert}, - {L"NightfallTormentMissionIcon.png", IDB_Missions_NightfallTormentMissionIcon}, -}); -Mission::MissionImageList TormentMission::hard_mode_images({ - {L"HardModeMissionIconIncomplete.png", IDB_Missions_HardModeMissionIconIncomplete}, - {L"HardModeMissionIcon1.png", IDB_Missions_HardModeMissionIcon1}, - {L"HardModeMissionIcon2.png", IDB_Missions_HardModeMissionIcon2}, - {L"HardModeMissionIcon.png", IDB_Missions_HardModeMissionIcon}, -}); - -Mission::MissionImageList EotNMission::normal_mode_images({ - {L"EOTNMissionIncomplete.png", IDB_Missions_EOTNMissionIncomplete}, - {L"EOTNMission.png", IDB_Missions_EOTNMission}, -}); -Mission::MissionImageList EotNMission::hard_mode_images({ - {L"EOTNHardModeMissionIncomplete.png", IDB_Missions_EOTNHardModeMissionIncomplete}, - {L"EOTNHardModeMission.png", IDB_Missions_EOTNHardModeMission}, -}); - -Mission::MissionImageList Dungeon::normal_mode_images({ - {L"EOTNDungeonIncomplete.png", IDB_Missions_EOTNDungeonIncomplete}, - {L"EOTNDungeon.png", IDB_Missions_EOTNDungeon}, -}); -Mission::MissionImageList Dungeon::hard_mode_images({ - {L"EOTNHardModeDungeonIncomplete.png", IDB_Missions_EOTNHardModeDungeonIncomplete}, - {L"EOTNDungeon.png", IDB_Missions_EOTNDungeon}, -}); -Mission::MissionImageList Vanquish::hard_mode_images({ - {L"VanquishIncomplete.png", IDB_Missions_VanquishIncomplete}, - {L"Vanquish.png", IDB_Missions_Vanquish}, -}); +} Color Mission::is_daily_bg_color = Colors::ARGB(102, 0, 255, 0); @@ -546,10 +596,8 @@ ImVec2 Mission::icon_size = {48.0f, 48.0f}; Mission::Mission(const MapID _outpost, - const MissionImageList& _normal_mode_images, - const MissionImageList& _hard_mode_images, const QuestID _zm_quest) - : outpost(_outpost), zm_quest(_zm_quest), normal_mode_textures(_normal_mode_images), hard_mode_textures(_hard_mode_images) + : outpost(_outpost), zm_quest(_zm_quest) { map_to = outpost; const GW::AreaInfo* map_info = GW::Map::GetMapInfo(outpost); @@ -563,9 +611,20 @@ MapID Mission::GetOutpost() const return TravelWindow::GetNearestOutpost(map_to); } +size_t Mission::GetLoadedIcons(IDirect3DTexture9* icons_out[4]) { + size_t icons_added = 0; + for (size_t i = 0; i < _countof(icons) && icons_added < 4; i++) { + if (!icons[i]) + break; + if (*icons[i]) { + icons_out[icons_added++] = *icons[i]; + } + } + return icons_added; +} + bool Mission::Draw(IDirect3DDevice9*) { - const auto texture = GetMissionImage(); const float scale = ImGui::GetIO().FontGlobalScale; @@ -578,37 +637,39 @@ bool Mission::Draw(IDirect3DDevice9*) bg = ImColor(has_quest_bg_color); } constexpr ImVec4 tint(1, 1, 1, 1); - constexpr auto uv0 = ImVec2(0, 0); - auto uv1 = ImVec2(1, 1); const ImVec2 cursor_pos = ImGui::GetCursorPos(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.f, 0.5f)); ImGui::PushID(this); + + bool clicked = false; + bool hovered = false; + + IDirect3DTexture9* icons_out[4] = { nullptr }; + size_t icons_len = GetLoadedIcons(icons_out); + + if (show_as_list) { s.y /= 2.f; if (!map_unlocked) { ImGui::PushStyleColor(ImGuiCol_Text, ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]); } - const bool clicked = ImGui::IconButton(Name(), texture, {s.x * 5.f, s.y}, 0, {s.x / 2.f, s.y}); + clicked = ImGui::CompositeIconButton(Name(), (ImTextureID*)icons_out, icons_len, {s.x * 5.f, s.y}, 0, {s.x / 2.f, s.y},icon_uv_offset[0],icon_uv_offset[1]); + hovered = ImGui::IsItemHovered(); if (!map_unlocked) { ImGui::PopStyleColor(); } - if (clicked) { - OnClick(); - } } else { - if (texture) { - uv1 = ImGui::CalculateUvCrop(texture, s); - } - if (ImGui::ImageButton(texture, s, uv0, uv1, -1, bg, tint)) { - OnClick(); - } + clicked = ImGui::CompositeIconButton("", (ImTextureID*)icons_out, icons_len, s, 0, s,icon_uv_offset[0],icon_uv_offset[1]); if (ImGui::IsItemHovered()) { ImGui::SetTooltip(Name()); } } + if (clicked) { + OnClick(); + } ImGui::PopID(); ImGui::PopStyleColor(); ImGui::PopStyleVar(); @@ -674,18 +735,18 @@ void Mission::CheckProgress(const std::wstring& player_name) map_unlocked = player_completion->maps_unlocked.empty() || ArrayBoolAt(player_completion->maps_unlocked, static_cast(outpost)); is_completed = ArrayBoolAt(*missions_complete, static_cast(outpost)); bonus = ArrayBoolAt(*missions_bonus, static_cast(outpost)); -} -IDirect3DTexture9* Mission::GetMissionImage() -{ - const auto* texture_list = &normal_mode_textures; + GW::Array complete_arr; + complete_arr.m_buffer = (uint32_t*)missions_complete->data(); + complete_arr.m_capacity = complete_arr.m_size = missions_complete->size(); - if (hard_mode) { - texture_list = &hard_mode_textures; - } - const uint8_t index = is_completed + 2 * bonus; + GW::Array bonus_arr; + bonus_arr.m_buffer = (uint32_t*)missions_bonus->data(); + bonus_arr.m_capacity = bonus_arr.m_size = missions_bonus->size(); - return texture_list->at(index).texture; + mission_state = ToolboxUtils::GetMissionState(outpost, complete_arr, bonus_arr); + + GetOutpostIcons(outpost, icons, mission_state, hard_mode); } bool Mission::IsDaily() @@ -698,12 +759,7 @@ bool Mission::HasQuest() return GW::QuestMgr::GetQuest(zm_quest) != nullptr; } -bool Dungeon::IsDaily() -{ - return false; -} - -bool Dungeon::HasQuest() +bool EotNMission::HasQuest() { for (const auto& zb : zb_quests) { if (GW::QuestMgr::GetQuest(zb)) { @@ -714,6 +770,19 @@ bool Dungeon::HasQuest() } +void EotNMission::CheckProgress(const std::wstring& player_name) { + Mission::CheckProgress(player_name); + // EotN mission icons are sprited - first sprite for incomplete, second for complete + if (is_completed) { + icon_uv_offset[0] = { .5f,0.f }; + icon_uv_offset[1] = { 1.f,1.f }; + } + else { + icon_uv_offset[0] = { .0f,0.f }; + icon_uv_offset[1] = { .5f,1.f }; + } +} + HeroUnlock::HeroUnlock(HeroID _hero_id) : PvESkill(SkillID::No_Skill) { @@ -736,20 +805,23 @@ const char* HeroUnlock::Name() return hero_names[static_cast(skill_id)]; } -IDirect3DTexture9* HeroUnlock::GetMissionImage() -{ - if (!image) { - image = new IDirect3DTexture9*; - *image = nullptr; +size_t HeroUnlock::GetLoadedIcons(IDirect3DTexture9* icons_out[4]) { + if (!icons_loaded) { + *icons = new IDirect3DTexture9 * (); const auto path = Resources::GetPath(L"img/heros"); Resources::EnsureFolderExists(path); wchar_t local_image[MAX_PATH]; swprintf(local_image, _countof(local_image), L"%s/hero_%d.jpg", path.c_str(), skill_id); char remote_image[128]; - snprintf(remote_image, _countof(remote_image), "https://github.com/HasKha/GWToolboxpp/raw/master/resources/heros/hero_%d.jpg", skill_id); - Resources::LoadTexture(image, local_image, remote_image); + snprintf(remote_image, _countof(remote_image), "https://github.com/gwdevhub/GWToolboxpp/raw/master/resources/heros/hero_%d.jpg", skill_id); + Resources::LoadTexture(*icons, local_image, remote_image); + icons_loaded = true; } - return *image; + return Mission::GetLoadedIcons(icons_out); +} +HeroUnlock::~HeroUnlock() { + if (*icons) + delete* icons; } void HeroUnlock::OnClick() @@ -774,24 +846,26 @@ const char* ItemAchievement::Name() return name.string().c_str(); } -IDirect3DTexture9* ItemAchievement::GetMissionImage() -{ - if (!name.wstring().empty()) { +size_t ItemAchievement::GetLoadedIcons(IDirect3DTexture9* icons_out[4]) { + if (!icons_loaded && !name.wstring().empty()) { if (name.wstring() == L"Brown Rabbit") { - return *Resources::GetItemImage(L"Brown Rabbit (miniature)"); + *icons = Resources::GetItemImage(L"Brown Rabbit (miniature)"); } - if (name.wstring() == L"Oppressor's Bow") { - return *Resources::GetItemImage(L"Oppressor's Longbow"); + else if (name.wstring() == L"Oppressor's Bow") { + *icons = Resources::GetItemImage(L"Oppressor's Longbow"); } - if (name.wstring() == L"Tormented Bow") { - return *Resources::GetItemImage(L"Tormented Longbow"); + else if (name.wstring() == L"Tormented Bow") { + *icons = Resources::GetItemImage(L"Tormented Longbow"); } - if (name.wstring() == L"Destroyer Bow") { - return *Resources::GetItemImage(L"Destroyer Longbow"); + else if (name.wstring() == L"Destroyer Bow") { + *icons = Resources::GetItemImage(L"Destroyer Longbow"); } - return *Resources::GetItemImage(name.wstring()); + else { + *icons = Resources::GetItemImage(name.wstring()); + } + icons_loaded = true; } - return nullptr; + return Mission::GetLoadedIcons(icons_out); } void ItemAchievement::OnClick() @@ -800,17 +874,16 @@ void ItemAchievement::OnClick() GuiUtils::OpenWiki(url); }); } - -IDirect3DTexture9* PvESkill::GetMissionImage() -{ - if (!image) { - image = Resources::GetSkillImage(skill_id); +size_t PvESkill::GetLoadedIcons(IDirect3DTexture9* icons_out[4]) { + if (!icons_loaded) { + *icons = Resources::GetSkillImage(skill_id); + icons_loaded = true; } - return *image; + return Mission::GetLoadedIcons(icons_out); } PvESkill::PvESkill(const SkillID _skill_id) - : Mission(MapID::None, dummy_var, dummy_var), skill_id(_skill_id) + : Mission(MapID::None), skill_id(_skill_id) { if (_skill_id != SkillID::No_Skill) { const auto skill = GW::SkillbarMgr::GetSkillConstantData(skill_id); @@ -901,29 +974,6 @@ bool FactionsPvESkill::Draw(IDirect3DDevice9* device) return drawn; } -void EotNMission::CheckProgress(const std::wstring& player_name) -{ - is_completed = false; - const auto& completion = character_completion; - if (!completion.contains(player_name)) { - return; - } - const std::vector* missions_bonus = &completion.at(player_name)->mission_bonus; - if (hard_mode) { - missions_bonus = &completion.at(player_name)->mission_bonus_hm; - } - is_completed = bonus = ArrayBoolAt(*missions_bonus, static_cast(outpost)); -} - -IDirect3DTexture9* EotNMission::GetMissionImage() -{ - const auto* texture_list = &normal_mode_textures; - if (hard_mode) { - texture_list = &hard_mode_textures; - } - return texture_list->at(is_completed ? 1 : 0).texture; -} - void Vanquish::CheckProgress(const std::wstring& player_name) { is_completed = false; @@ -933,13 +983,11 @@ void Vanquish::CheckProgress(const std::wstring& player_name) } const auto& unlocked = completion.at(player_name)->vanquishes; is_completed = bonus = ArrayBoolAt(unlocked, static_cast(outpost)); -} + mission_state = is_completed ? 0x7 : 0x0; -IDirect3DTexture9* Vanquish::GetMissionImage() -{ - return hard_mode_textures.at(is_completed).texture; -} + GetOutpostIcons(outpost, icons, mission_state, true); +} void CompletionWindow::Initialize() { @@ -1285,63 +1333,59 @@ void CompletionWindow::Initialize() void CompletionWindow::Initialize_Prophecies() { - LoadTextures(PropheciesMission::normal_mode_images); - LoadTextures(PropheciesMission::hard_mode_images); auto& prophecies_missions = missions.at(Campaign::Prophecies); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::The_Great_Northern_Wall, QuestID::ZaishenMission_The_Great_Northern_Wall)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Fort_Ranik, QuestID::ZaishenMission_Fort_Ranik)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Ruins_of_Surmia, QuestID::ZaishenMission_Ruins_of_Surmia)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Nolani_Academy, QuestID::ZaishenMission_Nolani_Academy)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Borlis_Pass, QuestID::ZaishenMission_Borlis_Pass)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::The_Frost_Gate, QuestID::ZaishenMission_The_Frost_Gate)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Gates_of_Kryta, QuestID::ZaishenMission_Gates_of_Kryta)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::DAlessio_Seaboard, QuestID::ZaishenMission_DAlessio_Seaboard)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Divinity_Coast, QuestID::ZaishenMission_Divinity_Coast)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::The_Wilds, QuestID::ZaishenMission_The_Wilds)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Bloodstone_Fen, QuestID::ZaishenMission_Bloodstone_Fen)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Aurora_Glade, QuestID::ZaishenMission_Aurora_Glade)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Riverside_Province, QuestID::ZaishenMission_Riverside_Province)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Sanctum_Cay, QuestID::ZaishenMission_Sanctum_Cay)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Dunes_of_Despair, QuestID::ZaishenMission_Dunes_of_Despair)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Thirsty_River, QuestID::ZaishenMission_Thirsty_River)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Elona_Reach, QuestID::ZaishenMission_Elona_Reach)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Augury_Rock_mission, QuestID::ZaishenMission_Augury_Rock)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::The_Dragons_Lair, QuestID::ZaishenMission_The_Dragons_Lair)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Ice_Caves_of_Sorrow, QuestID::ZaishenMission_Ice_Caves_of_Sorrow)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Iron_Mines_of_Moladune, QuestID::ZaishenMission_Iron_Mines_of_Moladune)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Thunderhead_Keep, QuestID::ZaishenMission_Thunderhead_Keep)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Ring_of_Fire, QuestID::ZaishenMission_Ring_of_Fire)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Abaddons_Mouth, QuestID::ZaishenMission_Abaddons_Mouth)); - prophecies_missions.push_back(new PropheciesMission( + prophecies_missions.push_back(new Mission( MapID::Hells_Precipice, QuestID::ZaishenMission_Hells_Precipice)); - LoadTextures(Vanquish::hard_mode_images); - auto& prophecies_vanquishes = vanquishes.at(Campaign::Prophecies); prophecies_vanquishes.push_back(new Vanquish(MapID::Pockmark_Flats)); prophecies_vanquishes.push_back(new Vanquish(MapID::Old_Ascalon)); @@ -1468,43 +1512,39 @@ void CompletionWindow::Initialize_Prophecies() void CompletionWindow::Initialize_Factions() { - LoadTextures(FactionsMission::normal_mode_images); - LoadTextures(FactionsMission::hard_mode_images); auto& factions_missions = missions.at(Campaign::Factions); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Minister_Chos_Estate_outpost_mission, QuestID::ZaishenMission_Minister_Chos_Estate)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Zen_Daijun_outpost_mission, QuestID::ZaishenMission_Zen_Daijun)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Vizunah_Square_Local_Quarter_outpost, QuestID::ZaishenMission_Vizunah_Square)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Vizunah_Square_Foreign_Quarter_outpost, QuestID::ZaishenMission_Vizunah_Square)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Nahpui_Quarter_outpost_mission, QuestID::ZaishenMission_Nahpui_Quarter)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Tahnnakai_Temple_outpost_mission, QuestID::ZaishenMission_Tahnnakai_Temple)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Arborstone_outpost_mission, QuestID::ZaishenMission_Arborstone)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Boreas_Seabed_outpost_mission, QuestID::ZaishenMission_Boreas_Seabed)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Sunjiang_District_outpost_mission, QuestID::ZaishenMission_Sunjiang_District)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::The_Eternal_Grove_outpost_mission, QuestID::ZaishenMission_The_Eternal_Grove)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Gyala_Hatchery_outpost_mission, QuestID::ZaishenMission_Gyala_Hatchery)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Unwaking_Waters_Kurzick_outpost, QuestID::ZaishenMission_Unwaking_Waters)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Unwaking_Waters_Luxon_outpost, QuestID::ZaishenMission_Unwaking_Waters)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Raisu_Palace_outpost_mission, QuestID::ZaishenMission_Raisu_Palace)); - factions_missions.push_back(new FactionsMission( + factions_missions.push_back(new Mission( MapID::Imperial_Sanctum_outpost_mission, QuestID::ZaishenMission_Imperial_Sanctum)); - LoadTextures(Vanquish::hard_mode_images); - auto& this_vanquishes = vanquishes.at(Campaign::Factions); this_vanquishes.push_back(new Vanquish(MapID::Haiju_Lagoon, QuestID::ZaishenVanquish_Haiju_Lagoon)); this_vanquishes.push_back(new Vanquish(MapID::Jaya_Bluffs, QuestID::ZaishenVanquish_Jaya_Bluffs)); @@ -1677,55 +1717,49 @@ void CompletionWindow::Initialize_Factions() void CompletionWindow::Initialize_Nightfall() { - LoadTextures(NightfallMission::normal_mode_images); - LoadTextures(NightfallMission::hard_mode_images); - LoadTextures(TormentMission::normal_mode_images); - LoadTextures(TormentMission::hard_mode_images); auto& nightfall_missions = missions.at(Campaign::Nightfall); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Chahbek_Village, QuestID::ZaishenMission_Chahbek_Village)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Jokanur_Diggings, QuestID::ZaishenMission_Jokanur_Diggings)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Blacktide_Den, QuestID::ZaishenMission_Blacktide_Den)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Consulate_Docks, QuestID::ZaishenMission_Consulate_Docks)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Venta_Cemetery, QuestID::ZaishenMission_Venta_Cemetery)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Kodonur_Crossroads, QuestID::ZaishenMission_Kodonur_Crossroads)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Pogahn_Passage, QuestID::ZaishenMission_Pogahn_Passage)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Rilohn_Refuge, QuestID::ZaishenMission_Rilohn_Refuge)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Moddok_Crevice, QuestID::ZaishenMission_Moddok_Crevice)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Tihark_Orchard, QuestID::ZaishenMission_Tihark_Orchard)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Dasha_Vestibule, QuestID::ZaishenMission_Dasha_Vestibule)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Dzagonur_Bastion, QuestID::ZaishenMission_Dzagonur_Bastion)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Grand_Court_of_Sebelkeh, QuestID::ZaishenMission_Grand_Court_of_Sebelkeh)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Jennurs_Horde, QuestID::ZaishenMission_Jennurs_Horde)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Nundu_Bay, QuestID::ZaishenMission_Nundu_Bay)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Gate_of_Desolation, QuestID::ZaishenMission_Gate_of_Desolation)); - nightfall_missions.push_back(new NightfallMission( + nightfall_missions.push_back(new Mission( MapID::Ruins_of_Morah, QuestID::ZaishenMission_Ruins_of_Morah)); - nightfall_missions.push_back(new TormentMission( + nightfall_missions.push_back(new Mission( MapID::Gate_of_Pain, QuestID::ZaishenMission_Gate_of_Pain)); - nightfall_missions.push_back(new TormentMission( + nightfall_missions.push_back(new Mission( MapID::Gate_of_Madness, QuestID::ZaishenMission_Gate_of_Madness)); - nightfall_missions.push_back(new TormentMission( + nightfall_missions.push_back(new Mission( MapID::Abaddons_Gate, QuestID::ZaishenMission_Abaddons_Gate)); - LoadTextures(Vanquish::hard_mode_images); - auto& this_vanquishes = vanquishes.at(Campaign::Nightfall); this_vanquishes.push_back(new Vanquish(MapID::Cliffs_of_Dohjok)); this_vanquishes.push_back(new Vanquish(MapID::Fahranur_The_First_City)); @@ -1919,8 +1953,6 @@ void CompletionWindow::Initialize_Nightfall() void CompletionWindow::Initialize_EotN() { - LoadTextures(EotNMission::normal_mode_images); - LoadTextures(EotNMission::hard_mode_images); auto& eotn_missions = missions.at(Campaign::EyeOfTheNorth); // Asura eotn_missions.push_back(new EotNMission(MapID::Finding_the_Bloodstone_mission)); @@ -1938,8 +1970,6 @@ void CompletionWindow::Initialize_EotN() eotn_missions.push_back(new EotNMission(MapID::Destructions_Depths_mission, QuestID::ZaishenMission_Destructions_Depths)); eotn_missions.push_back(new EotNMission(MapID::A_Time_for_Heroes_mission, QuestID::ZaishenMission_A_Time_for_Heroes)); - LoadTextures(Vanquish::hard_mode_images); - auto& this_vanquishes = vanquishes.at(Campaign::EyeOfTheNorth); this_vanquishes.push_back(new Vanquish(MapID::Bjora_Marches, QuestID::ZaishenVanquish_Bjora_Marches)); this_vanquishes.push_back(new Vanquish(MapID::Drakkar_Lake, QuestID::ZaishenVanquish_Drakkar_Lake)); @@ -2028,49 +2058,47 @@ void CompletionWindow::Initialize_EotN() void CompletionWindow::Initialize_Dungeons() { - LoadTextures(Dungeon::normal_mode_images); - LoadTextures(Dungeon::hard_mode_images); auto& dungeons = missions.at(Campaign::BonusMissionPack); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Catacombs_of_Kathandrax_Level_1, QuestID::ZaishenBounty_Ilsundur_Lord_of_Fire)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Rragars_Menagerie_Level_1, QuestID::ZaishenBounty_Rragar_Maneater)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Cathedral_of_Flames_Level_1, QuestID::ZaishenBounty_Murakai_Lady_of_the_Night)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Ooze_Pit_mission)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Darkrime_Delves_Level_1)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Frostmaws_Burrows_Level_1)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Sepulchre_of_Dragrimmar_Level_1, QuestID::ZaishenBounty_Remnant_of_Antiquities)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Ravens_Point_Level_1, QuestID::ZaishenBounty_Plague_of_Destruction)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Vloxen_Excavations_Level_1, QuestID::ZaishenBounty_Zoldark_the_Unholy)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Bogroot_Growths_Level_1)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Bloodstone_Caves_Level_1)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Shards_of_Orr_Level_1, QuestID::ZaishenBounty_Fendi_Nin)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Oolas_Lab_Level_1, QuestID::ZaishenBounty_TPS_Regulator_Golem)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Arachnis_Haunt_Level_1, QuestID::ZaishenBounty_Arachni)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Slavers_Exile_Level_1, { QuestID::ZaishenBounty_Forgewight, QuestID::ZaishenBounty_Selvetarm, QuestID::ZaishenBounty_Justiciar_Thommis, QuestID::ZaishenBounty_Rand_Stormweaver, QuestID::ZaishenBounty_Duncan_the_Black})); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Fronis_Irontoes_Lair_mission, {QuestID::ZaishenBounty_Fronis_Irontoe})); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Secret_Lair_of_the_Snowmen)); - dungeons.push_back(new Dungeon( + dungeons.push_back(new EotNMission( MapID::Heart_of_the_Shiverpeaks_Level_1, {QuestID::ZaishenBounty_Magmus})); } @@ -2167,7 +2195,9 @@ void CompletionWindow::Draw(IDirect3DDevice9* device) #if 1 ImGui::SameLine(); if (ImGui::Button("Change") && wcscmp(GetPlayerName(), chosen_player_name.c_str()) != 0) { - RerollWindow::Instance().Reroll(chosen_player_name.data(), false, false); + if (!RerollWindow::Instance().Reroll(chosen_player_name.data(), false, false)) { + Log::Warning("Failed to reroll to character"); + } } #endif ImGui::SameLine(); @@ -2931,12 +2961,12 @@ void WeaponAchievement::CheckProgress(const std::wstring& player_name) is_completed = bonus = unlocked[encoded_name_index] != 0; } -IDirect3DTexture9* AchieventWithWikiFile::GetMissionImage() -{ - if (!img && !wiki_file_name.empty()) { - img = Resources::GetGuildWarsWikiImage(wiki_file_name.c_str(), 64); +size_t AchieventWithWikiFile::GetLoadedIcons(IDirect3DTexture9* icons_out[4]) { + if (!icons_loaded && !wiki_file_name.empty()) { + *icons = Resources::GetGuildWarsWikiImage(wiki_file_name.c_str(), 64); + icons_loaded = true; } - return img ? *img : nullptr; + return Mission::GetLoadedIcons(icons_out); } void ArmorAchievement::CheckProgress(const std::wstring& player_name) @@ -2995,21 +3025,18 @@ void FestivalHat::CheckProgress(const std::wstring& player_name) is_completed = bonus = ArrayBoolAt(unlocked, encoded_name_index); } -IDirect3DTexture9* UnlockedPvPItemUpgrade::GetMissionImage() -{ - if (image) { - return *image; - } - const auto info = GW::Items::GetPvPItemUpgrade(encoded_name_index); - if (!info) { - return nullptr; - } - const auto found = std::ranges::find_if(item_upgrades_by_file_id, [file_id = info->file_id](auto& check) { return check.file_id == file_id; }); - if (found == item_upgrades_by_file_id.end()) { - return nullptr; +size_t UnlockedPvPItemUpgrade::GetLoadedIcons(IDirect3DTexture9* icons_out[4]) { + if (!icons_loaded) { + const auto info = GW::Items::GetPvPItemUpgrade(encoded_name_index); + if (info) { + const auto found = std::ranges::find_if(item_upgrades_by_file_id, [file_id = info->file_id](auto& check) { return check.file_id == file_id; }); + if (found != item_upgrades_by_file_id.end()) { + *icons = Resources::GetGuildWarsWikiImage(found->wiki_filename); + } + } + icons_loaded = true; } - image = Resources::GetGuildWarsWikiImage(found->wiki_filename); - return *image; + return Mission::GetLoadedIcons(icons_out); } void UnlockedPvPItemUpgrade::CheckProgress(const std::wstring&) diff --git a/GWToolboxdll/Windows/CompletionWindow.h b/GWToolboxdll/Windows/CompletionWindow.h index 64f6e4ff9..7d04b28c4 100644 --- a/GWToolboxdll/Windows/CompletionWindow.h +++ b/GWToolboxdll/Windows/CompletionWindow.h @@ -6,19 +6,11 @@ #include #include -namespace Missions { - struct MissionImage { - const wchar_t* file_name; - int resource_id; - IDirect3DTexture9* texture = nullptr; - MissionImage(const wchar_t* _file_name, const int _resource_id) - : file_name(_file_name), resource_id(_resource_id) {} - }; +namespace Missions { class Mission { protected: - using MissionImageList = std::vector; static Color is_daily_bg_color; static Color has_quest_bg_color; @@ -27,12 +19,16 @@ namespace Missions { GW::Constants::MapID outpost; GW::Constants::MapID map_to; GW::Constants::QuestID zm_quest; - MissionImageList normal_mode_textures; - MissionImageList hard_mode_textures; + + uint8_t mission_state = 0; + // Array of placeholder pointers; the actual IDirect3DTexture9* is loaded from dx9 + IDirect3DTexture9** icons[4] = { nullptr }; + bool icons_loaded = false; + ImVec2 icon_uv_offset[2] = { { .0f,.0f },{0.f,0.f} }; public: virtual ~Mission() = default; - Mission(GW::Constants::MapID, const MissionImageList&, const MissionImageList&, GW::Constants::QuestID = static_cast(0)); + Mission(GW::Constants::MapID, GW::Constants::QuestID = static_cast(0)); static ImVec2 icon_size; [[nodiscard]] GW::Constants::MapID GetOutpost() const; @@ -40,10 +36,11 @@ namespace Missions { bool bonus = false; bool map_unlocked = true; + virtual size_t GetLoadedIcons(IDirect3DTexture9* icons_out[4]); + virtual const char* Name(); virtual bool Draw(IDirect3DDevice9*); virtual void OnClick(); - virtual IDirect3DTexture9* GetMissionImage(); virtual bool IsDaily(); // True if this mission is ZM or ZB today virtual bool HasQuest(); // True if the ZM or ZB is in quest log virtual void CheckProgress(const std::wstring& player_name); @@ -52,17 +49,16 @@ namespace Missions { class PvESkill : public Mission { protected: - IDirect3DTexture9** image = nullptr; GW::Constants::SkillID skill_id; public: uint32_t profession = 0; - inline static MissionImageList dummy_var = {}; PvESkill(GW::Constants::SkillID _skill_id); - IDirect3DTexture9* GetMissionImage() override; bool IsDaily() override { return false; } bool HasQuest() override { return false; } + size_t GetLoadedIcons(IDirect3DTexture9* icons_out[4]) override; + bool Draw(IDirect3DDevice9*) override; void OnClick() override; @@ -72,8 +68,9 @@ namespace Missions { class HeroUnlock : public PvESkill { public: HeroUnlock(GW::Constants::HeroID _hero_id); - ~HeroUnlock() override { delete image; } - IDirect3DTexture9* GetMissionImage() override; + ~HeroUnlock(); + + size_t GetLoadedIcons(IDirect3DTexture9* icons_out[4]) override; void OnClick() override; @@ -89,7 +86,7 @@ namespace Missions { public: ItemAchievement(size_t _encoded_name_index, const wchar_t* encoded_name); - IDirect3DTexture9* GetMissionImage() override; + size_t GetLoadedIcons(IDirect3DTexture9* icons_out[4]) override; void OnClick() override; const char* Name() override; @@ -109,7 +106,7 @@ namespace Missions { : ItemAchievement(_encoded_name_index, nullptr) {} void CheckProgress(const std::wstring& player_name) override; - IDirect3DTexture9* GetMissionImage() override; + size_t GetLoadedIcons(IDirect3DTexture9* icons_out[4]) override; const char* Name() override; void OnClick() override; @@ -139,7 +136,6 @@ namespace Missions { class AchieventWithWikiFile : public ItemAchievement { protected: std::string wiki_file_name; - IDirect3DTexture9** img = nullptr; public: AchieventWithWikiFile(const size_t hom_achievement_index, const wchar_t* encoded_name, const char* _wiki_file_name = nullptr) @@ -150,7 +146,7 @@ namespace Missions { } } - IDirect3DTexture9* GetMissionImage() override; + size_t GetLoadedIcons(IDirect3DTexture9* icons_out[4]) override; }; class ArmorAchievement : public AchieventWithWikiFile { @@ -180,103 +176,25 @@ namespace Missions { bool Draw(IDirect3DDevice9*) override; }; - class PropheciesMission : public Mission { - public: - static MissionImageList normal_mode_images; - static MissionImageList hard_mode_images; - - PropheciesMission(const GW::Constants::MapID _outpost, const GW::Constants::QuestID _zm_quest = static_cast(0)) - : Mission(_outpost, normal_mode_images, hard_mode_images, _zm_quest) { } - }; - - - class FactionsMission : public Mission { - public: - static MissionImageList normal_mode_images; - static MissionImageList hard_mode_images; - - FactionsMission(const GW::Constants::MapID _outpost, const GW::Constants::QuestID _zm_quest = static_cast(0)) - : Mission(_outpost, normal_mode_images, hard_mode_images, _zm_quest) { } - }; - - - class NightfallMission : public Mission { - protected: - NightfallMission(const GW::Constants::MapID _outpost, - const MissionImageList& _normal_mode_images, - const MissionImageList& _hard_mode_images, - const GW::Constants::QuestID _zm_quest) - : Mission(_outpost, _normal_mode_images, _hard_mode_images, _zm_quest) { } - - public: - static MissionImageList normal_mode_images; - static MissionImageList hard_mode_images; - - NightfallMission(const GW::Constants::MapID _outpost, const GW::Constants::QuestID _zm_quest = static_cast(0)) - : Mission(_outpost, normal_mode_images, hard_mode_images, _zm_quest) { } - }; - - - class TormentMission : public NightfallMission { - public: - static MissionImageList normal_mode_images; - static MissionImageList hard_mode_images; - - TormentMission(const GW::Constants::MapID _outpost, const GW::Constants::QuestID _zm_quest = static_cast(0)) - : NightfallMission(_outpost, normal_mode_images, hard_mode_images, _zm_quest) { } - }; - class Vanquish : public Mission { public: - static MissionImageList hard_mode_images; Vanquish(const GW::Constants::MapID _outpost, const GW::Constants::QuestID _zm_quest = static_cast(0)) - : Mission(_outpost, hard_mode_images, hard_mode_images, _zm_quest) { } + : Mission(_outpost, _zm_quest) { } - - IDirect3DTexture9* GetMissionImage() override; void CheckProgress(const std::wstring& player_name) override; }; - class EotNMission : public Mission { - protected: - EotNMission(const GW::Constants::MapID _outpost, - const MissionImageList& _normal_mode_images, - const MissionImageList& _hard_mode_images, - const GW::Constants::QuestID _zm_quest = static_cast(0)) - : Mission(_outpost, _normal_mode_images, _hard_mode_images, _zm_quest) { } - public: - static MissionImageList normal_mode_images; - static MissionImageList hard_mode_images; - - EotNMission(const GW::Constants::MapID _outpost, const GW::Constants::QuestID _zm_quest = static_cast(0)) - : Mission(_outpost, normal_mode_images, hard_mode_images, _zm_quest) { } + EotNMission(const GW::Constants::MapID _outpost, const std::vector& _zb_quests) + : zb_quests(_zb_quests), Mission(_outpost) { } + EotNMission(const GW::Constants::MapID _outpost, GW::Constants::QuestID _zb_quest = static_cast(0)) + : zb_quests({_zb_quest}), Mission(_outpost) { } - IDirect3DTexture9* GetMissionImage() override; void CheckProgress(const std::wstring& player_name) override; - - private: - std::string name; - }; - - - class Dungeon : public EotNMission { - public: - static MissionImageList normal_mode_images; - static MissionImageList hard_mode_images; - - Dungeon(const GW::Constants::MapID _outpost, const std::vector& _zb_quests) - : EotNMission(_outpost, normal_mode_images, hard_mode_images), zb_quests(_zb_quests) { } - - Dungeon(const GW::Constants::MapID _outpost, GW::Constants::QuestID _zb_quest = static_cast(0)) - : EotNMission(_outpost, normal_mode_images, hard_mode_images), zb_quests({_zb_quest}) { } - - bool IsDaily() override; bool HasQuest() override; - private: std::vector zb_quests{}; }; diff --git a/GWToolboxdll/Windows/CompletionWindow_Constants.h b/GWToolboxdll/Windows/CompletionWindow_Constants.h index f6eef2bb3..d4ac46f25 100644 --- a/GWToolboxdll/Windows/CompletionWindow_Constants.h +++ b/GWToolboxdll/Windows/CompletionWindow_Constants.h @@ -10,6 +10,57 @@ namespace CompletionWindow_Constants { const char* hero_names[] = {"", "Norgu", "Goren", "Tahlkora", "Master of Whispers", "Acolyte Jin", "Koss", "Dunkoro", "Acolyte Sousuke", "Melonni", "Zhed Shadowhoof", "General Morgahn", "Margrid the Sly", "Zenmai", "Olias", "Razah", "M.O.X.", "Keiran Thackeray", "Jora", "Pyre Fierceshot", "Anton", "Livia", "Hayda", "Kahmu", "Gwen", "Xandra", "Vekk", "Ogden Stonehealer", "", "", "", "", "", "", "", "", "Miku", "Zei Ri"}; + + // GW loads these icons as an array of file hashes once, and then keeps it in memory - I can't find where these are in RDATA reliably, so have to define them here :( + enum class WorldMapIcon : uint32_t { + None = 0, + Kryta_Mission = 0x2ac23, + Kryta_CompletePrimary = 0x2ac27, + Kryta_CompleteSecondary = 0x2ac29, + Kryta_City = 0x2ac2f, + Kryta_Outpost = 0x2ac2d, + Kryta_Arena = 0x2ac2b, + + Cantha_Mission = 0x2ac35, + Cantha_CompletePrimary = 0x2ac39, + Cantha_CompleteExpert = 0x2ac3b, + Cantha_CompleteMaster = 0x2ac3d, + Cantha_City = 0x2ac43, + Cantha_Outpost = 0x2ac41, + Cantha_ChallengeMission = 0x2ac33, + Cantha_LuxonsOwned = 0x2ac31, // NB: Kurshit owned is Luxon texture with blue overlay; proof that Luxons hold the true claim to Cantha! + + Elona_Mission = 0x38048, + Elona_CompletePrimary = 0x3804c, + Elona_CompleteExpert = 0x3804e, + Elona_CompleteMaster = 0x38050, + Elona_City = 0x322ad, + Elona_Outpost = 0x322ab, + Elona_ChallengeMission = 0x38046, + + RealmOfTorment_Outpost = 0x322af, + RealmOfTorment_City = 0x322b1, + RealmOfTorment_Mission = 0x38058, + RealmOfTorment_ChallengeMission = 0x38056, + + RealmOfTorment_EliteArea = 0x43466, + + EyeOfTheNorth_Dungeon = 0x50830, + EyeOfTheNorth_DungeonComplete = 0x50830, // TODO + EyeOfTheNorth_Mission = 0x50831, + EyeOfTheNorth_MissionComplete = 0x50831, // TODO + EyeOfTheNorth_HardMode_Dungeon = 0x50832, + EyeOfTheNorth_HardMode_DungeonComplete = 0x50832, // TODO + EyeOfTheNorth_HardMode_Mission = 0x50833, + EyeOfTheNorth_HardMode_MissionComplete = 0x50833, // TODO + + HardMode = 0x45563, + HardMode_CompletePrimary = 0x45567, + HardMode_CompleteExpert = 0x45569, + HardMode_CompleteMaster = 0x4556b, + HardMode_CompleteAll = 0x4556d // Gold helm + }; + const wchar_t* encoded_festival_hat_names[] = { // Halloween L"\x8102\x5C2B\xB7F4\xC976\x5CE1", // Charr hat diff --git a/GWToolboxdll/Windows/Hotkeys.cpp b/GWToolboxdll/Windows/Hotkeys.cpp index 3915f90ef..4439b5fd3 100644 --- a/GWToolboxdll/Windows/Hotkeys.cpp +++ b/GWToolboxdll/Windows/Hotkeys.cpp @@ -883,7 +883,7 @@ int HotkeyUseItem::Description(char* buf, const size_t bufsz) bool HotkeyUseItem::Draw() { - bool hotkey_changed = ImGui::InputInt("Item ID", (int*)&item_id); + bool hotkey_changed = ImGui::InputInt("Item Model ID", (int*)&item_id); hotkey_changed |= ImGui::InputText("Item Name", name, _countof(name)); hotkey_changed |= ImGui::Checkbox("Display error message on failure", &show_error_on_failure); return hotkey_changed; @@ -1618,11 +1618,9 @@ void HotkeyAction::Execute() } switch (action) { case OpenXunlaiChest: - if (isOutpost()) { - GW::GameThread::Enqueue([] { - GW::Items::OpenXunlaiWindow(); + GW::GameThread::Enqueue([] { + GW::Items::OpenXunlaiWindow(); }); - } break; case OpenLockedChest: { if (isExplorable()) { diff --git a/GWToolboxdll/Windows/HotkeysWindow.cpp b/GWToolboxdll/Windows/HotkeysWindow.cpp index f13987701..eb590e569 100644 --- a/GWToolboxdll/Windows/HotkeysWindow.cpp +++ b/GWToolboxdll/Windows/HotkeysWindow.cpp @@ -78,16 +78,6 @@ namespace { return GW::Map::GetIsMapLoaded() && GW::Map::GetInstanceType() != GW::Constants::InstanceType::Loading && !GW::Map::GetIsObserving(); } - // @Cleanup: Find the address for this without checking map garbage - how does inv window know to show "pvp equipment" button? - bool IsPvPCharacter() - { - // This is the 3rd module that uses this function, should really be part of GWCA but leave here for now. - return !(GW::Map::GetIsMapUnlocked(GW::Constants::MapID::Ascalon_City_outpost) - || GW::Map::GetIsMapUnlocked(GW::Constants::MapID::Shing_Jea_Monastery_outpost) - || GW::Map::GetIsMapUnlocked(GW::Constants::MapID::Kamadan_Jewel_of_Istan_outpost) - || GW::Map::GetIsMapUnlocked(GW::Constants::MapID::Ascalon_City_pre_searing)); - } - // Repopulates applicable_hotkeys based on current character/map context. // Used because its not necessary to check these vars on every keystroke, only when they change bool CheckSetValidHotkeys() @@ -104,7 +94,7 @@ namespace { const GW::Constants::InstanceType instance_type = GW::Map::GetInstanceType(); const GW::Constants::MapID map_id = GW::Map::GetMapID(); const auto primary = static_cast(me->primary); - const bool is_pvp = IsPvPCharacter(); + const bool is_pvp = me->IsPvP(); valid_hotkeys.clear(); by_profession.clear(); by_map.clear(); diff --git a/GWToolboxdll/Windows/InfoWindow.cpp b/GWToolboxdll/Windows/InfoWindow.cpp index 885111b01..69444fb83 100644 --- a/GWToolboxdll/Windows/InfoWindow.cpp +++ b/GWToolboxdll/Windows/InfoWindow.cpp @@ -48,6 +48,9 @@ #include #include #include +#include +#include +#include namespace { enum class Status { @@ -610,6 +613,141 @@ namespace { } return false; } + + + typedef uint32_t*(__cdecl* CreateTexture_pt)(wchar_t* file_name, uint32_t flags); + CreateTexture_pt CreateTexture_Func = nullptr; + CreateTexture_pt CreateTexture_Ret = nullptr; + + // Why reinvent the wheel? + typedef void(__cdecl* GWCA_SendUIMessage_pt)(GW::UI::UIMessage msgid, void* wParam, void* lParam); + GWCA_SendUIMessage_pt GWCA_SendUIMessage_Func = nullptr; + GWCA_SendUIMessage_pt GWCA_SendUIMessage_Ret = nullptr; + + struct UIMessagePacket { + GW::UI::UIMessage msgid; + void* wParam; + void* lParam; + }; + + std::vector ui_message_packets_recorded; + bool record_ui_messages = false; + + void OnGWCASendUIMessage(GW::UI::UIMessage msgid, void* wParam, void* lParam) { + GW::Hook::EnterHook(); + GWCA_SendUIMessage_Ret(msgid, wParam, lParam); + if(record_ui_messages) + ui_message_packets_recorded.push_back(new UIMessagePacket({ msgid,wParam,lParam })); + GW::Hook::LeaveHook(); + } + void ClearUIMessagesRecorded() { + for (auto p : ui_message_packets_recorded) { + delete p; + } + ui_message_packets_recorded.clear(); + } + + std::map textures_created; + + bool record_textures = false; + + uint32_t FileHashToFileId(wchar_t* param_1) { + if (!param_1) + return 0; + if (((0xff < *param_1) && (0xff < param_1[1])) && + ((param_1[2] == 0 || ((0xff < param_1[2] && (param_1[3] == 0)))))) { + return (*param_1 - 0xff00ff) + (uint32_t)param_1[1] * 0xff00; + } + return 0; + } + + uint32_t* OnCreateTexture(wchar_t* file_name, uint32_t flags) { + GW::Hook::EnterHook(); + const auto out = CreateTexture_Ret(file_name, flags); + uint32_t file_id = FileHashToFileId(file_name); + if (record_textures && textures_created.find(file_id) == textures_created.end()) { + textures_created[file_id] = GwDatTextureModule::LoadTextureFromFileId(file_id); + } + GW::Hook::LeaveHook(); + return out; + } + + void DrawDebugInfo() { + if (ImGui::CollapsingHeader("Quoted Item")) { + ImGui::Text("Most recently quoted item (buy or sell) from trader"); + static GuiUtils::EncString quoted_name; + DrawItemInfo(GW::Items::GetItemById(quoted_item_id), "ed_name); + } + + record_textures = ImGui::CollapsingHeader("Loaded Textures"); + if (record_textures) { + ImGui::PushID(&textures_created); + const ImVec2 scaled_size = { 64.f,64.f }; + constexpr ImVec4 tint(1, 1, 1, 1); + const auto normal_bg = ImColor(IM_COL32(0, 0, 0, 0)); + constexpr auto uv0 = ImVec2(0, 0); + + if (ImGui::SmallButton("Reset")) { + textures_created.clear(); + } + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); + ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.f, 0.5f)); + + ImGui::StartSpacedElements(scaled_size.x); + + for (auto& it : textures_created) { + ImGui::PushID(it.first); + const auto texture = it.second; + if (!texture || !*texture) { + ImGui::PopID(); + continue; + } + + const auto uv1 = ImGui::CalculateUvCrop(*texture, scaled_size); + ImGui::NextSpacedElement(); + ImGui::ImageButton(*texture, scaled_size, uv0, uv1, -1, normal_bg, tint); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("File ID 0x%08x", it.first); + } + ImGui::PopID(); + } + + ImGui::PopStyleColor(); + ImGui::PopStyleVar(); + ImGui::PopStyleVar(); + ImGui::PopID(); + } + record_ui_messages = ImGui::CollapsingHeader("UI Message Log"); + if (record_ui_messages) { + ImGui::PushID(&ui_message_packets_recorded); + if (ImGui::SmallButton("Reset")) { + ClearUIMessagesRecorded(); + } + for (auto it : ui_message_packets_recorded) { + ImGui::PushID(it); + ImGui::Text("0x%08x 0x%08x 0x%08x", it->msgid, it->wParam, it->lParam); + ImGui::PopID(); + } + ImGui::PopID(); + } + + + // For debugging changes to flags/arrays etc + [[maybe_unused]] const GW::GameContext* g = GW::GetGameContext(); + [[maybe_unused]] const GW::GuildContext* gu = g->guild; + [[maybe_unused]] const GW::CharContext* c = g->character; + [[maybe_unused]] const GW::WorldContext* w = g->world; + [[maybe_unused]] const GW::PartyContext* p = g->party; + [[maybe_unused]] const GW::MapContext* m = g->map; + [[maybe_unused]] const GW::AccountContext* acc = g->account; + [[maybe_unused]] const GW::ItemContext* i = g->items; + [[maybe_unused]] const GW::AgentLiving* me = GW::Agents::GetPlayerAsAgentLiving(); + [[maybe_unused]] const GW::Player* me_player = me ? GW::PlayerMgr::GetPlayerByID(me->player_number) : nullptr; + [[maybe_unused]] const GW::Chat::ChatBuffer* log = GW::Chat::GetChatLog(); + [[maybe_unused]] const GW::AreaInfo* ai = GW::Map::GetMapInfo(GW::Map::GetMapID()); + } } void InfoWindow::Terminate() @@ -618,6 +756,16 @@ void InfoWindow::Terminate() delete achievement; } target_achievements.clear(); + + if (CreateTexture_Func) { + GW::HookBase::RemoveHook(CreateTexture_Func); + CreateTexture_Func = nullptr; + } + if (GWCA_SendUIMessage_Func) { + GW::HookBase::RemoveHook(GWCA_SendUIMessage_Func); + GWCA_SendUIMessage_Func = nullptr; + } + ClearUIMessagesRecorded(); } void InfoWindow::Initialize() @@ -631,16 +779,32 @@ void InfoWindow::Initialize() }); GW::StoC::RegisterPacketCallback(&InstanceLoadFile_Entry, OnInstanceLoad); GW::Chat::CreateCommand(L"resignlog", CmdResignLog); +#ifdef _DEBUG + CreateTexture_Func = (CreateTexture_pt)GW::Scanner::FindAssertion("p:\\code\\engine\\gr\\grtex2d.cpp", "!(flags & GR_TEXTURE_TRANSFER_OWNERSHIP)", -0x32); +#endif + if (CreateTexture_Func) { + GW::HookBase::CreateHook(CreateTexture_Func, OnCreateTexture, (void**)&CreateTexture_Ret); + GW::HookBase::EnableHooks(CreateTexture_Func); + } + + GWCA_SendUIMessage_Func = (GWCA_SendUIMessage_pt)GW::UI::SendUIMessage; + if (GWCA_SendUIMessage_Func) { + GW::HookBase::CreateHook(GWCA_SendUIMessage_Func, OnGWCASendUIMessage, (void**)&GWCA_SendUIMessage_Ret); + GW::HookBase::EnableHooks(GWCA_SendUIMessage_Func); + } } void InfoWindow::Draw(IDirect3DDevice9*) { if (!visible) { + record_ui_messages = false; + record_textures = false; return; } ImGui::SetNextWindowCenter(ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize(ImVec2(300, 0), ImGuiCond_FirstUseEver); if (ImGui::Begin(Name(), GetVisiblePtr(), GetWinFlags())) { + if (show_widgets) { const auto& widgets = GWToolbox::GetWidgets(); @@ -715,6 +879,7 @@ void InfoWindow::Draw(IDirect3DDevice9*) InfoField("Continent", "%d", map_info->continent); InfoField("Region", "%d", map_info->region); InfoField("Type", "%d", map_info->type); + InfoField("Mission Complete?", "%d", ToolboxUtils::GetMissionState(GW::Map::GetMapID(), GW::PartyMgr::GetIsPartyInHardMode())); InfoField("Instance Info Type", "%d", GW::Map::GetMapTypeInstanceInfo(map_info->type)->request_instance_map_type); InfoField("Flags", "0x%X", map_info->flags); InfoField("Thumbnail ID", "%d", map_info->thumbnail_id); @@ -785,13 +950,6 @@ void InfoWindow::Draw(IDirect3DDevice9*) static GuiUtils::EncString item_name; DrawItemInfo(GW::Items::GetItemBySlot(GW::Constants::Bag::Backpack, 1), &item_name); } -#ifdef _DEBUG - if (show_item && ImGui::CollapsingHeader("Quoted Item")) { - ImGui::Text("Most recently quoted item (buy or sell) from trader"); - static GuiUtils::EncString quoted_name; - DrawItemInfo(GW::Items::GetItemById(quoted_item_id), "ed_name); - } -#endif if (show_quest && ImGui::CollapsingHeader("Quest")) { const GW::Quest* q = GW::QuestMgr::GetActiveQuest(); if (q) { @@ -872,22 +1030,11 @@ void InfoWindow::Draw(IDirect3DDevice9*) DrawResignlog(); } } - ImGui::End(); #ifdef _DEBUG - // For debugging changes to flags/arrays etc - [[maybe_unused]] const GW::GameContext* g = GW::GetGameContext(); - [[maybe_unused]] const GW::GuildContext* gu = g->guild; - [[maybe_unused]] const GW::CharContext* c = g->character; - [[maybe_unused]] const GW::WorldContext* w = g->world; - [[maybe_unused]] const GW::PartyContext* p = g->party; - [[maybe_unused]] const GW::MapContext* m = g->map; - [[maybe_unused]] const GW::AccountContext* acc = g->account; - [[maybe_unused]] const GW::ItemContext* i = g->items; - [[maybe_unused]] const GW::AgentLiving* me = GW::Agents::GetPlayerAsAgentLiving(); - [[maybe_unused]] const GW::Player* me_player = me ? GW::PlayerMgr::GetPlayerByID(me->player_number) : nullptr; - [[maybe_unused]] const GW::Chat::ChatBuffer* log = GW::Chat::GetChatLog(); - [[maybe_unused]] const GW::AreaInfo* ai = GW::Map::GetMapInfo(GW::Map::GetMapID()); + DrawDebugInfo(); #endif + ImGui::End(); + } void InfoWindow::Update(const float) diff --git a/GWToolboxdll/Windows/Pcons.cpp b/GWToolboxdll/Windows/Pcons.cpp index 5cf2ce252..1f5503a9d 100644 --- a/GWToolboxdll/Windows/Pcons.cpp +++ b/GWToolboxdll/Windows/Pcons.cpp @@ -482,8 +482,7 @@ int Pcon::CheckInventory(bool* used, size_t* used_qty_ptr, const size_t from_bag if (qtyea < 1) { continue; // This is not the pcon you're looking for... } - if (used != nullptr && !*used) { - GW::Items::UseItem(item); + if (used != nullptr && !*used && GW::Items::UseItem(item)) { *used = true; used_qty = qtyea; } diff --git a/GWToolboxdll/Windows/RerollWindow.cpp b/GWToolboxdll/Windows/RerollWindow.cpp index 900664870..efeb26e4f 100644 --- a/GWToolboxdll/Windows/RerollWindow.cpp +++ b/GWToolboxdll/Windows/RerollWindow.cpp @@ -533,7 +533,10 @@ void RerollWindow::Update(float) RerollFailed(L"No scroll available for elite area"); return; } - GW::Items::UseItem(scroll); + if (!GW::Items::UseItem(scroll)) { + RerollFailed(L"Failed to use scroll item"); + return; + } reroll_stage = WaitForActiveDistrict; reroll_timeout = (reroll_stage_set = TIMER_INIT()) + 20000; } diff --git a/README.md b/README.md index 3bac98794..bfb6c8534 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ If you are here to check toolbox features and for a download link to go [https:/ 1. Open **Git Bash**. Use all the following commands in **Git Bash**. 2. Clone the repository recursively: -`git clone --recursive https://github.com/Haskha/GWToolboxpp.git` +`git clone --recursive https://github.com/gwdevhub/GWToolboxpp.git` 3. Navigate to the GWToolboxpp folder: `cd GWToolboxpp` @@ -93,7 +93,7 @@ For developers: there are a few things you should take note of: **[JetBrains](https://github.com/JetBrains)** * JetBrains Logo Providing core contributors with OSS Development Licenses -**Everyone who [proposed a PR](https://github.com/HasKha/GWToolboxpp/pulls?q=is%3Apr+is%3Amerged)** +**Everyone who [proposed a PR](https://github.com/gwdevhub/GWToolboxpp/pulls?q=is%3Apr+is%3Amerged)** **and everyone suggesting ideas!** diff --git a/plugins/Base/stl.h b/plugins/Base/stl.h index e87f0c615..732111933 100644 --- a/plugins/Base/stl.h +++ b/plugins/Base/stl.h @@ -1,6 +1,8 @@ #pragma once +#ifndef VC_EXTRALEAN #define VC_EXTRALEAN +#endif #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif diff --git a/resources/materials/Armor_of_Salvation.png b/resources/materials/Armor_of_Salvation.png deleted file mode 100644 index ae08f1316..000000000 Binary files a/resources/materials/Armor_of_Salvation.png and /dev/null differ diff --git a/resources/materials/Essence_of_Celerity.png b/resources/materials/Essence_of_Celerity.png deleted file mode 100644 index 4c1cd269b..000000000 Binary files a/resources/materials/Essence_of_Celerity.png and /dev/null differ diff --git a/resources/materials/Grail_of_Might.png b/resources/materials/Grail_of_Might.png deleted file mode 100644 index 2c2bdf4b5..000000000 Binary files a/resources/materials/Grail_of_Might.png and /dev/null differ diff --git a/resources/materials/Powerstone_of_Courage.png b/resources/materials/Powerstone_of_Courage.png deleted file mode 100644 index 94b877a51..000000000 Binary files a/resources/materials/Powerstone_of_Courage.png and /dev/null differ diff --git a/resources/materials/Scroll_of_Resurrection.png b/resources/materials/Scroll_of_Resurrection.png deleted file mode 100644 index 83d9ca910..000000000 Binary files a/resources/materials/Scroll_of_Resurrection.png and /dev/null differ diff --git a/resources/missions/EOTNDungeon.png b/resources/missions/EOTNDungeon.png deleted file mode 100644 index 035c0ad74..000000000 Binary files a/resources/missions/EOTNDungeon.png and /dev/null differ diff --git a/resources/missions/EOTNDungeonIncomplete.png b/resources/missions/EOTNDungeonIncomplete.png deleted file mode 100644 index 878982157..000000000 Binary files a/resources/missions/EOTNDungeonIncomplete.png and /dev/null differ diff --git a/resources/missions/EOTNHardModeDungeonIncomplete.png b/resources/missions/EOTNHardModeDungeonIncomplete.png deleted file mode 100644 index c29b8a092..000000000 Binary files a/resources/missions/EOTNHardModeDungeonIncomplete.png and /dev/null differ diff --git a/resources/missions/EOTNHardModeMission.png b/resources/missions/EOTNHardModeMission.png deleted file mode 100644 index f4c772365..000000000 Binary files a/resources/missions/EOTNHardModeMission.png and /dev/null differ diff --git a/resources/missions/EOTNHardModeMissionIncomplete.png b/resources/missions/EOTNHardModeMissionIncomplete.png deleted file mode 100644 index f71352be4..000000000 Binary files a/resources/missions/EOTNHardModeMissionIncomplete.png and /dev/null differ diff --git a/resources/missions/EOTNMission.png b/resources/missions/EOTNMission.png deleted file mode 100644 index 4ac5b6eee..000000000 Binary files a/resources/missions/EOTNMission.png and /dev/null differ diff --git a/resources/missions/EOTNMissionIncomplete.png b/resources/missions/EOTNMissionIncomplete.png deleted file mode 100644 index 03368822b..000000000 Binary files a/resources/missions/EOTNMissionIncomplete.png and /dev/null differ diff --git a/resources/missions/FactionsMissionIcon.png b/resources/missions/FactionsMissionIcon.png deleted file mode 100644 index 6a301991e..000000000 Binary files a/resources/missions/FactionsMissionIcon.png and /dev/null differ diff --git a/resources/missions/FactionsMissionIconExpert.png b/resources/missions/FactionsMissionIconExpert.png deleted file mode 100644 index 3c26a07d5..000000000 Binary files a/resources/missions/FactionsMissionIconExpert.png and /dev/null differ diff --git a/resources/missions/FactionsMissionIconIncomplete.png b/resources/missions/FactionsMissionIconIncomplete.png deleted file mode 100644 index 4f8eec81c..000000000 Binary files a/resources/missions/FactionsMissionIconIncomplete.png and /dev/null differ diff --git a/resources/missions/FactionsMissionIconPrimary.png b/resources/missions/FactionsMissionIconPrimary.png deleted file mode 100644 index 8d2e646c1..000000000 Binary files a/resources/missions/FactionsMissionIconPrimary.png and /dev/null differ diff --git a/resources/missions/HardModeMissionIcon.png b/resources/missions/HardModeMissionIcon.png deleted file mode 100644 index d773c8bbe..000000000 Binary files a/resources/missions/HardModeMissionIcon.png and /dev/null differ diff --git a/resources/missions/HardModeMissionIcon1.png b/resources/missions/HardModeMissionIcon1.png deleted file mode 100644 index d9de6e37d..000000000 Binary files a/resources/missions/HardModeMissionIcon1.png and /dev/null differ diff --git a/resources/missions/HardModeMissionIcon1b.png b/resources/missions/HardModeMissionIcon1b.png deleted file mode 100644 index 5ac3cd264..000000000 Binary files a/resources/missions/HardModeMissionIcon1b.png and /dev/null differ diff --git a/resources/missions/HardModeMissionIcon2.png b/resources/missions/HardModeMissionIcon2.png deleted file mode 100644 index 08a273f9f..000000000 Binary files a/resources/missions/HardModeMissionIcon2.png and /dev/null differ diff --git a/resources/missions/HardModeMissionIconIncomplete-2.png b/resources/missions/HardModeMissionIconIncomplete-2.png deleted file mode 100644 index 934ad90ae..000000000 Binary files a/resources/missions/HardModeMissionIconIncomplete-2.png and /dev/null differ diff --git a/resources/missions/HardModeMissionIconIncomplete.png b/resources/missions/HardModeMissionIconIncomplete.png deleted file mode 100644 index 934ad90ae..000000000 Binary files a/resources/missions/HardModeMissionIconIncomplete.png and /dev/null differ diff --git a/resources/missions/MissionIcon.png b/resources/missions/MissionIcon.png deleted file mode 100644 index 4b29b26f5..000000000 Binary files a/resources/missions/MissionIcon.png and /dev/null differ diff --git a/resources/missions/MissionIconBonus.png b/resources/missions/MissionIconBonus.png deleted file mode 100644 index 243ef1d86..000000000 Binary files a/resources/missions/MissionIconBonus.png and /dev/null differ diff --git a/resources/missions/MissionIconIncomplete.png b/resources/missions/MissionIconIncomplete.png deleted file mode 100644 index afa92f2df..000000000 Binary files a/resources/missions/MissionIconIncomplete.png and /dev/null differ diff --git a/resources/missions/MissionIconPrimary.png b/resources/missions/MissionIconPrimary.png deleted file mode 100644 index 5491979fd..000000000 Binary files a/resources/missions/MissionIconPrimary.png and /dev/null differ diff --git a/resources/missions/NightfallMissionIcon.png b/resources/missions/NightfallMissionIcon.png deleted file mode 100644 index 227a93a72..000000000 Binary files a/resources/missions/NightfallMissionIcon.png and /dev/null differ diff --git a/resources/missions/NightfallMissionIconExpert.png b/resources/missions/NightfallMissionIconExpert.png deleted file mode 100644 index 12b824a89..000000000 Binary files a/resources/missions/NightfallMissionIconExpert.png and /dev/null differ diff --git a/resources/missions/NightfallMissionIconIncomplete.png b/resources/missions/NightfallMissionIconIncomplete.png deleted file mode 100644 index df8a22896..000000000 Binary files a/resources/missions/NightfallMissionIconIncomplete.png and /dev/null differ diff --git a/resources/missions/NightfallMissionIconPrimary.png b/resources/missions/NightfallMissionIconPrimary.png deleted file mode 100644 index 22e860816..000000000 Binary files a/resources/missions/NightfallMissionIconPrimary.png and /dev/null differ diff --git a/resources/missions/NightfallTormentMissionIcon.png b/resources/missions/NightfallTormentMissionIcon.png deleted file mode 100644 index c9bf17faf..000000000 Binary files a/resources/missions/NightfallTormentMissionIcon.png and /dev/null differ diff --git a/resources/missions/NightfallTormentMissionIconExpert.png b/resources/missions/NightfallTormentMissionIconExpert.png deleted file mode 100644 index a2fdbc7dd..000000000 Binary files a/resources/missions/NightfallTormentMissionIconExpert.png and /dev/null differ diff --git a/resources/missions/NightfallTormentMissionIconIncomplete.png b/resources/missions/NightfallTormentMissionIconIncomplete.png deleted file mode 100644 index 530d78dd5..000000000 Binary files a/resources/missions/NightfallTormentMissionIconIncomplete.png and /dev/null differ diff --git a/resources/missions/NightfallTormentMissionIconPrimary.png b/resources/missions/NightfallTormentMissionIconPrimary.png deleted file mode 100644 index fe2b1f5b3..000000000 Binary files a/resources/missions/NightfallTormentMissionIconPrimary.png and /dev/null differ diff --git a/resources/pcons/Armor_of_Salvation.png b/resources/pcons/Armor_of_Salvation.png deleted file mode 100644 index c28443c28..000000000 Binary files a/resources/pcons/Armor_of_Salvation.png and /dev/null differ diff --git a/resources/pcons/Birthday_Cupcake.png b/resources/pcons/Birthday_Cupcake.png deleted file mode 100644 index 942394dd6..000000000 Binary files a/resources/pcons/Birthday_Cupcake.png and /dev/null differ diff --git a/resources/pcons/Blue_Rock_Candy.png b/resources/pcons/Blue_Rock_Candy.png deleted file mode 100644 index a8a608226..000000000 Binary files a/resources/pcons/Blue_Rock_Candy.png and /dev/null differ diff --git a/resources/pcons/Bottle_of_Grog.png b/resources/pcons/Bottle_of_Grog.png deleted file mode 100644 index ec51c4c5b..000000000 Binary files a/resources/pcons/Bottle_of_Grog.png and /dev/null differ diff --git a/resources/pcons/Bowl_of_Skalefin_Soup.png b/resources/pcons/Bowl_of_Skalefin_Soup.png deleted file mode 100644 index b3642d748..000000000 Binary files a/resources/pcons/Bowl_of_Skalefin_Soup.png and /dev/null differ diff --git a/resources/pcons/Candy_Apple.png b/resources/pcons/Candy_Apple.png deleted file mode 100644 index bb90f8d6b..000000000 Binary files a/resources/pcons/Candy_Apple.png and /dev/null differ diff --git a/resources/pcons/Candy_Corn.png b/resources/pcons/Candy_Corn.png deleted file mode 100644 index 06b39eaf4..000000000 Binary files a/resources/pcons/Candy_Corn.png and /dev/null differ diff --git a/resources/pcons/Drake_Kabob.png b/resources/pcons/Drake_Kabob.png deleted file mode 100644 index 1990fd8aa..000000000 Binary files a/resources/pcons/Drake_Kabob.png and /dev/null differ diff --git a/resources/pcons/Dwarven_Ale.png b/resources/pcons/Dwarven_Ale.png deleted file mode 100644 index e730ad110..000000000 Binary files a/resources/pcons/Dwarven_Ale.png and /dev/null differ diff --git a/resources/pcons/Essence_of_Celerity.png b/resources/pcons/Essence_of_Celerity.png deleted file mode 100644 index 4c1cd269b..000000000 Binary files a/resources/pcons/Essence_of_Celerity.png and /dev/null differ diff --git a/resources/pcons/Fruitcake.png b/resources/pcons/Fruitcake.png deleted file mode 100644 index c1b366189..000000000 Binary files a/resources/pcons/Fruitcake.png and /dev/null differ diff --git a/resources/pcons/Golden_Egg.png b/resources/pcons/Golden_Egg.png deleted file mode 100644 index 5ff04e274..000000000 Binary files a/resources/pcons/Golden_Egg.png and /dev/null differ diff --git a/resources/pcons/Grail_of_Might.png b/resources/pcons/Grail_of_Might.png deleted file mode 100644 index 2c2bdf4b5..000000000 Binary files a/resources/pcons/Grail_of_Might.png and /dev/null differ diff --git a/resources/pcons/Green_Rock_Candy.png b/resources/pcons/Green_Rock_Candy.png deleted file mode 100644 index 4b16e9938..000000000 Binary files a/resources/pcons/Green_Rock_Candy.png and /dev/null differ diff --git a/resources/pcons/Lunar_Fortune.png b/resources/pcons/Lunar_Fortune.png deleted file mode 100644 index a1c8129ea..000000000 Binary files a/resources/pcons/Lunar_Fortune.png and /dev/null differ diff --git a/resources/pcons/Pahnai_Salad.png b/resources/pcons/Pahnai_Salad.png deleted file mode 100644 index 44b2773de..000000000 Binary files a/resources/pcons/Pahnai_Salad.png and /dev/null differ diff --git a/resources/pcons/Red_Rock_Candy.png b/resources/pcons/Red_Rock_Candy.png deleted file mode 100644 index cec552a4a..000000000 Binary files a/resources/pcons/Red_Rock_Candy.png and /dev/null differ diff --git a/resources/pcons/Slice_of_Pumpkin_Pie.png b/resources/pcons/Slice_of_Pumpkin_Pie.png deleted file mode 100644 index 213a3f4d0..000000000 Binary files a/resources/pcons/Slice_of_Pumpkin_Pie.png and /dev/null differ diff --git a/resources/pcons/Sugary_Blue_Drink.png b/resources/pcons/Sugary_Blue_Drink.png deleted file mode 100644 index 578679efa..000000000 Binary files a/resources/pcons/Sugary_Blue_Drink.png and /dev/null differ diff --git a/resources/pcons/War_Supplies.png b/resources/pcons/War_Supplies.png deleted file mode 100644 index 777af8df9..000000000 Binary files a/resources/pcons/War_Supplies.png and /dev/null differ