From d3df5e1ea88688b14e7405edf51e5613836a4359 Mon Sep 17 00:00:00 2001 From: HarrySilan Date: Sun, 19 Jun 2022 16:57:14 +0800 Subject: [PATCH 1/6] add teleport through list in Custom Teleport --- .../cheat-base/events/helpers/alphanum.hpp | 274 ++++++++++ .../user/cheat/teleport/CustomTeleports.cpp | 467 ++++++++++++------ .../src/user/cheat/teleport/CustomTeleports.h | 16 + 3 files changed, 610 insertions(+), 147 deletions(-) create mode 100644 cheat-base/src/cheat-base/events/helpers/alphanum.hpp diff --git a/cheat-base/src/cheat-base/events/helpers/alphanum.hpp b/cheat-base/src/cheat-base/events/helpers/alphanum.hpp new file mode 100644 index 0000000..6dac03c --- /dev/null +++ b/cheat-base/src/cheat-base/events/helpers/alphanum.hpp @@ -0,0 +1,274 @@ +#ifndef ALPHANUM__HPP +#define ALPHANUM__HPP + +/* +The Alphanum Algorithm is an improved sorting algorithm for strings +containing numbers. Instead of sorting numbers in ASCII order like a +standard sort, this algorithm sorts numbers in numeric order. + +The Alphanum Algorithm is discussed at http://www.DaveKoelle.com + +This implementation is Copyright (c) 2008 Dirk Jagdmann . +It is a cleanroom implementation of the algorithm and not derived by +other's works. In contrast to the versions written by Dave Koelle this +source code is distributed with the libpng/zlib license. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you + must not claim that you wrote the original software. If you use + this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and + must not be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. */ + + /* $Header: /code/doj/alphanum.hpp,v 1.3 2008/01/28 23:06:47 doj Exp $ */ + +#include +#include +#include +#include + +#ifdef ALPHANUM_LOCALE +#include +#endif + +#ifdef DOJDEBUG +#include +#include +#endif + +// TODO: make comparison with hexadecimal numbers. Extend the alphanum_comp() function by traits to choose between decimal and hexadecimal. + +namespace doj +{ + + // anonymous namespace for functions we use internally. But if you + // are coding in C, you can use alphanum_impl() directly, since it + // uses not C++ features. + namespace { + + // if you want to honour the locale settings for detecting digit + // characters, you should define ALPHANUM_LOCALE +#ifdef ALPHANUM_LOCALE + + bool alphanum_isdigit(int c) + { + return isdigit(c); + } +#else + + bool alphanum_isdigit(const char c) + { + return c >= '0' && c <= '9'; + } +#endif + + int alphanum_impl(const char* l, const char* r) + { + enum mode_t { STRING, NUMBER } mode = STRING; + + while (*l && *r) + { + if (mode == STRING) + { + char l_char, r_char; + while ((l_char = *l) && (r_char = *r)) + { + // check if this are digit characters + const bool l_digit = alphanum_isdigit(l_char), r_digit = alphanum_isdigit(r_char); + // if both characters are digits, we continue in NUMBER mode + if (l_digit && r_digit) + { + mode = NUMBER; + break; + } + // if only the left character is a digit, we have a result + if (l_digit) return -1; + // if only the right character is a digit, we have a result + if (r_digit) return +1; + // compute the difference of both characters + const int diff = l_char - r_char; + // if they differ we have a result + if (diff != 0) return diff; + // otherwise process the next characters + ++l; + ++r; + } + } + else // mode==NUMBER + { +#ifdef ALPHANUM_LOCALE + // get the left number + char* end; + unsigned long l_int = strtoul(l, &end, 0); + l = end; + + // get the right number + unsigned long r_int = strtoul(r, &end, 0); + r = end; +#else + // get the left number + unsigned long l_int = 0; + while (*l && alphanum_isdigit(*l)) + { + // TODO: this can overflow + l_int = l_int * 10 + *l - '0'; + ++l; + } + + // get the right number + unsigned long r_int = 0; + while (*r && alphanum_isdigit(*r)) + { + // TODO: this can overflow + r_int = r_int * 10 + *r - '0'; + ++r; + } +#endif + + // if the difference is not equal to zero, we have a comparison result + const long diff = l_int - r_int; + if (diff != 0) + return diff; + + // otherwise we process the next substring in STRING mode + mode = STRING; + } + } + + if (*r) return -1; + if (*l) return +1; + return 0; + } + + } + + template + int alphanum_comp(const lT& left, const rT& right) + { +#ifdef DOJDEBUG + std::clog << "alphanum_comp<" << typeid(left).name() << "," << typeid(right).name() << "> " << left << "," << right << std::endl; +#endif + std::ostringstream l; l << left; + std::ostringstream r; r << right; + return alphanum_impl(l.str().c_str(), r.str().c_str()); + } + + template <> inline + int alphanum_comp(const std::string& l, const std::string& r) + { +#ifdef DOJDEBUG + std::clog << "alphanum_comp " << l << "," << r << std::endl; +#endif + return alphanum_impl(l.c_str(), r.c_str()); + } + + + // now follow a lot of overloaded alphanum_comp() functions to get a + // direct call to alphanum_impl() upon the various combinations of c + // and c++ strings. + + inline int alphanum_comp(char* l, char* r) + { + assert(l); + assert(r); +#ifdef DOJDEBUG + std::clog << "alphanum_comp " << l << "," << r << std::endl; +#endif + return alphanum_impl(l, r); + } + + inline int alphanum_comp(const char* l, const char* r) + { + assert(l); + assert(r); +#ifdef DOJDEBUG + std::clog << "alphanum_comp " << l << "," << r << std::endl; +#endif + return alphanum_impl(l, r); + } + + inline int alphanum_comp(char* l, const char* r) + { + assert(l); + assert(r); +#ifdef DOJDEBUG + std::clog << "alphanum_comp " << l << "," << r << std::endl; +#endif + return alphanum_impl(l, r); + } + + inline int alphanum_comp(const char* l, char* r) + { + assert(l); + assert(r); +#ifdef DOJDEBUG + std::clog << "alphanum_comp " << l << "," << r << std::endl; +#endif + return alphanum_impl(l, r); + } + + inline int alphanum_comp(const std::string& l, char* r) + { + assert(r); +#ifdef DOJDEBUG + std::clog << "alphanum_comp " << l << "," << r << std::endl; +#endif + return alphanum_impl(l.c_str(), r); + } + + inline int alphanum_comp(char* l, const std::string& r) + { + assert(l); +#ifdef DOJDEBUG + std::clog << "alphanum_comp " << l << "," << r << std::endl; +#endif + return alphanum_impl(l, r.c_str()); + } + + inline int alphanum_comp(const std::string& l, const char* r) + { + assert(r); +#ifdef DOJDEBUG + std::clog << "alphanum_comp " << l << "," << r << std::endl; +#endif + return alphanum_impl(l.c_str(), r); + } + + inline int alphanum_comp(const char* l, const std::string& r) + { + assert(l); +#ifdef DOJDEBUG + std::clog << "alphanum_comp " << l << "," << r << std::endl; +#endif + return alphanum_impl(l, r.c_str()); + } + + + template + struct alphanum_less + { public: + typedef Ty first_arg_type; + typedef Ty second_arg_type; + typedef bool result_type; + bool operator()(const Ty& left, const Ty& right) const + { + return alphanum_comp(left, right) < 0; + } + }; + +} + + +#endif \ No newline at end of file diff --git a/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp b/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp index a0aa564..ef344e6 100644 --- a/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp +++ b/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp @@ -9,165 +9,338 @@ #include #include #include -#include +#include #include +#include namespace cheat::feature { - CustomTeleports::CustomTeleports() : Feature(), - NF(f_DebugMode, "Debug Mode", "CustomTeleports", false) // Soon to be added - { } - const FeatureGUIInfo &CustomTeleports::GetGUIInfo() const - { - static const FeatureGUIInfo info{"Custom Teleports", "Teleport", true}; - return info; - } + CustomTeleports::CustomTeleports() : Feature(), + NF(f_DebugMode, "Debug Mode", "CustomTeleports", false), // Soon to be added + NF(f_Enabled, "Custom Teleport", "CustomTeleports", false), + NF(f_Next, "Teleport Next", "CustomTeleports", Hotkey(VK_OEM_6)), + NF(f_Previous, "Teleport Previous", "CustomTeleports", Hotkey(VK_OEM_4)) + { + f_Next.value().PressedEvent += MY_METHOD_HANDLER(CustomTeleports::OnNextKeyPressed); + f_Previous.value().PressedEvent += MY_METHOD_HANDLER(CustomTeleports::OnPreviousKeyPressed); + } + const FeatureGUIInfo& CustomTeleports::GetGUIInfo() const + { + static const FeatureGUIInfo info{ "Custom Teleports", "Teleport", true }; + return info; + } - void CustomTeleports::DrawMain() - { - auto &entityManager = game::EntityManager::instance(); - auto &MapTeleport = MapTeleport::GetInstance(); - static std::string teleportName; - ImGui::InputText("Teleport name", &teleportName); - static std::vector> teleports; - app::Vector3 pos = app::ActorUtils_GetAvatarPos(nullptr); - if (ImGui::Button("Add teleport")) - { - // check if already added - bool found = false; - for (const auto &[name, pos] : teleports) - { - if (name == teleportName) - { - found = true; - break; - } - } - // check if name is valid and doesnt contain special characters - if (teleportName.find_first_of("\\/:*?\"<>|") != std::string::npos) - { - return; - } + void CustomTeleports::DrawMain() + { + auto& entityManager = game::EntityManager::instance(); + auto& MapTeleport = MapTeleport::GetInstance(); + static std::string teleportName; + static std::string search; + app::Vector3 pos = app::ActorUtils_GetAvatarPos(nullptr); - teleports.push_back({teleportName, pos}); + ImGui::InputText("Teleport name", &teleportName); + if (ImGui::Button("Add Teleport")) + { + // check if name is valid and doesnt contain special characters + if (teleportName.find_first_of("\\/:*?\"<>|") != std::string::npos) + return; - auto dir = std::filesystem::current_path(); - dir /= "teleports"; - if (!std::filesystem::exists(dir)) - std::filesystem::create_directory(dir); - std::ofstream ofs(dir / (teleportName + ".json")); - nlohmann::json j; - j["name"] = teleportName; - j["position"] = {pos.x, pos.y, pos.z}; - ofs << j; - teleportName.clear(); - } - ImGui::SameLine(); - if (ImGui::Button("Reload")) - { - auto dir = std::filesystem::current_path(); - dir /= "teleports"; - auto result = std::filesystem::directory_iterator(dir); - teleports.clear(); - for (auto &file : result) - { + // check if already added + if (std::any_of(teleports.begin(), teleports.end(), [](const auto& pair) + { return pair.first == teleportName; })) + return; - if (file.path().extension() != ".json") - continue; + selectedIndex = -1; + teleports.push_back({ teleportName, pos }); - std::string name = file.path().stem().string(); - if (file.is_directory()) - continue; + auto dir = std::filesystem::current_path(); + dir /= "teleports"; + if (!std::filesystem::exists(dir)) + std::filesystem::create_directory(dir); + std::ofstream ofs(dir / (teleportName + ".json")); + nlohmann::json j; + j["name"] = teleportName; + j["position"] = { pos.x, pos.y, pos.z }; + ofs << j; + teleportName.clear(); + } + ImGui::SameLine(); + if (ImGui::Button("Reload")) + { + selectedIndex = -1; + auto dir = std::filesystem::current_path(); + dir /= "teleports"; + auto result = std::filesystem::directory_iterator(dir); + teleports.clear(); + for (auto& file : result) + { + if (file.path().extension() != ".json") + continue; - std::ifstream ifs(file.path()); - nlohmann::json j; - ifs >> j; - teleports.push_back({j["name"], {j["position"][0], j["position"][1], j["position"][2]}}); - LOG_INFO("Loaded teleport %s", name.c_str()); - } - } - ImGui::SameLine(); - // open directory - if (ImGui::Button("Open Folder")) - { - auto dir = std::filesystem::current_path(); - dir /= "teleports"; - ShellExecuteA(NULL, "open", dir.string().c_str(), NULL, NULL, SW_SHOW); - } + std::string name = file.path().stem().string(); + if (file.is_directory()) + continue; - static std::string jsonInput; - ImGui::InputTextMultiline("JSON input", &jsonInput, ImVec2(0, 50), ImGuiInputTextFlags_AllowTabInput); + std::ifstream ifs(file.path()); + nlohmann::json j; + ifs >> j; + teleports.push_back({ j["name"], {j["position"][0], j["position"][1], j["position"][2]} }); + LOG_INFO("Loaded teleport %s", name.c_str()); + } + } + ImGui::SameLine(); + // open directory + if (ImGui::Button("Open Folder")) + { + auto dir = std::filesystem::current_path(); + dir /= "teleports"; + ShellExecuteA(NULL, "open", dir.string().c_str(), NULL, NULL, SW_SHOW); + } + ImGui::SameLine(); + static std::string jsonInput; + if (ImGui::Button("Load from JSON")) + { + selectedIndex = -1; + auto dir = std::filesystem::current_path(); + dir /= "teleports"; + LOG_INFO("Defined dir"); + if (!std::filesystem::exists(dir)) + std::filesystem::create_directory(dir); + nlohmann::json j; + try + { + j = nlohmann::json::parse(jsonInput); + } + catch (nlohmann::json::parse_error& e) + { + LOG_ERROR("Failed to parse JSON: %s", e.what()); + return; + } + LOG_INFO("Parsed JSON"); + std::string teleportName = j["name"]; + app::Vector3 pos = { j["position"][0], j["position"][1], j["position"][2] }; + teleports.push_back({ teleportName, pos }); + LOG_INFO("Loaded teleport %s", teleportName.c_str()); + std::ofstream ofs(dir / (teleportName + ".json")); + ofs << jsonInput; + jsonInput.clear(); + } + ImGui::InputTextMultiline("JSON input", &jsonInput, ImVec2(0, 50), ImGuiInputTextFlags_AllowTabInput); - if (ImGui::Button("Load from JSON")) - { - auto dir = std::filesystem::current_path(); - dir /= "teleports"; - LOG_INFO("Defined dir"); - if (!std::filesystem::exists(dir)) - std::filesystem::create_directory(dir); - nlohmann::json j; - try - { - j = nlohmann::json::parse(jsonInput); - } - catch (nlohmann::json::parse_error &e) - { - LOG_ERROR("Failed to parse JSON: %s", e.what()); - return; - } - LOG_INFO("Parsed JSON"); - std::string teleportName = j["name"]; - app::Vector3 pos = {j["position"][0], j["position"][1], j["position"][2]}; - teleports.push_back({teleportName, pos}); - LOG_INFO("Loaded teleport %s", teleportName.c_str()); - std::ofstream ofs(dir / (teleportName + ".json")); - ofs << jsonInput; - jsonInput.clear(); - } + ConfigWidget("Teleport Next", f_Next, true, "Press to teleport next of selected"); + ConfigWidget("Teleport Previous", f_Previous, true, "Press to teleport previous of selected"); + ConfigWidget("Enable", + f_Enabled, + "Enable teleport-through-list functionality\n" \ + "Usage:\n" \ + "1. Put Checkmark to the teleports you want to teleport using hotkey\n" \ + "2. Single click the teleport (with checkmark) to select where you want to start\n" \ + "3. You can now press Next or Previous Hotkey to Teleport through the Checklist\n" \ + "Initially it will teleport the player to the selection made\n" \ + "Note: Double click or click the arrow to open teleport details"); + ImGui::SameLine(); + if (ImGui::Button("Delete Checked")) + { + if (!teleports.empty()) { + std::vector teleportNames; + // get all teleport names by index + for (auto& i : checkedIndices) { + teleportNames.push_back(teleports.at(i).first); + if (selectedIndex == i) selectedIndex = -1; + } - if (ImGui::TreeNode("Teleports")) - { - std::string search; - ImGui::InputText("Search", &search); - for (const auto &[teleportName, position] : teleports) - { - // find without case sensitivity - if (search.empty() || std::search(teleportName.begin(), teleportName.end(), search.begin(), search.end(), [](char a, char b) - { return std::tolower(a) == std::tolower(b); }) != teleportName.end()) - { - if (ImGui::TreeNode(teleportName.data())) - { - ImGui::Text("Position: %.3f, %.3f, %.3f", position.x, position.y, position.z); - if (ImGui::Button("Teleport")) - { - auto &mapTeleport = MapTeleport::GetInstance(); - mapTeleport.TeleportTo(position); - } - ImGui::SameLine(); - if (ImGui::Button("Remove")) - { - auto dir = std::filesystem::current_path(); - dir /= "teleports"; - // delete file - std::filesystem::remove(dir / (teleportName + ".json")); - auto it = std::find_if(teleports.begin(), teleports.end(), [&teleportName](const auto &pair) - { return pair.first == teleportName; }); - if (it != teleports.end()) - teleports.erase(it); - } - ImGui::SameLine(); - HelpMarker("Warning: Removing a teleport will remove the file from the directory. It will be lost forever."); - ImGui::TreePop(); - } - } - } - ImGui::TreePop(); - } - } + for (auto& name : teleportNames) { + auto dir = std::filesystem::current_path(); + dir /= "teleports"; + // delete file + std::filesystem::remove(dir / (name + ".json")); + // remove from list + teleports.erase(std::remove_if(teleports.begin(), teleports.end(), [&name](const auto& pair) + { return pair.first == name; }), teleports.end()); + } + checkedIndices.clear(); + UpdateIndexName(); + } - CustomTeleports& CustomTeleports::GetInstance() - { - static CustomTeleports instance; - return instance; - } + } + ImGui::SameLine(); + HelpMarker("Warning: This will delete the file from the directory and\nremove the teleport from the list. It will be lost forever."); + + if (ImGui::TreeNode("Teleports")) + { + // using natural sort instead of ascii sort + std::sort(teleports.begin(), teleports.end(), [](const auto& a, const auto& b) + {return doj::alphanum_less()(a.first, b.first); }); + + bool allChecked = checkedIndices.size() == teleports.size() && !teleports.empty(); + ImGui::Checkbox("Check All", &allChecked); + if (ImGui::IsItemClicked()) { + if (!teleports.empty()) { + if (allChecked) { + selectedIndex = -1; + checkedIndices.clear(); + } + else { + for (int i = 0; i < teleports.size(); i++) { + checkedIndices.insert(i); + } + } + UpdateIndexName(); + } + } + ImGui::SameLine(); + ImGui::InputText("Search", &search); + unsigned int index = 0; + for (const auto& [teleportName, position] : teleports) + { + // find without case sensitivity + if (search.empty() || std::search(teleportName.begin(), teleportName.end(), search.begin(), search.end(), [](char a, char b) + { return std::tolower(a) == std::tolower(b); }) != teleportName.end()) + { + bool checked = std::any_of(checkedIndices.begin(), checkedIndices.end(), [&index](const auto& i) { return i == index; }); + bool selected = index == selectedIndex; + + const char* hashId = ("##Index" + std::to_string(index)).c_str(); + ImGui::Checkbox(hashId, &checked); + if (ImGui::IsItemClicked(0)) { + if (checked) { + if (selected) selectedIndex = -1; + checkedIndices.erase(index); + } + else { + // sets are sorted by default + checkedIndices.insert(index); + } + } + ImGui::SameLine(); + if (ImGui::Button("TP")) + { + auto& mapTeleport = MapTeleport::GetInstance(); + mapTeleport.TeleportTo(position); + } + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Text, selected ? IM_COL32(40, 90, 175, 255) : IM_COL32(255, 255, 255, 255)); + ImGuiTreeNodeFlags nodeFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth; + if (selected) nodeFlags |= ImGuiTreeNodeFlags_Selected; + bool node_open = ImGui::TreeNodeEx(teleportName.data(), nodeFlags); + if (ImGui::IsItemClicked() && checked) { + if (!selected) { + selectedIndex = index; + selectedByClick = true; + } + else { + selectedIndex = -1; + selectedByClick = false; + } + UpdateIndexName(); + } + if (node_open) + { + ImGui::Text("Position: %.3f, %.3f, %.3f", position.x, position.y, position.z); + ImGui::TreePop(); + } + ImGui::PopStyleColor(); + } + index++; + } + ImGui::TreePop(); + } + } + + bool CustomTeleports::NeedStatusDraw() const + { + return f_Enabled; + } + + void CustomTeleports::DrawStatus() + { + ImGui::Text("Custom Teleport\n[%s]", selectedIndexName); + } + + void CustomTeleports::OnNextKeyPressed() + { + if (!f_Enabled || selectedIndex < 0) + return; + + auto& mapTeleport = MapTeleport::GetInstance(); + app::Vector3 position; + + if (selectedByClick) { + position = teleports.at(selectedIndex).second; + selectedByClick = false; + } + else { + std::vector list(checkedIndices.begin(), checkedIndices.end()); + if (selectedIndex == list.back()) + return; + + auto index = std::distance(list.begin(), std::find(list.begin(), list.end(), selectedIndex)); + position = teleports.at(list.at(index + 1)).second; + selectedIndex = list.at(index + 1); + } + mapTeleport.TeleportTo(position); + UpdateIndexName(); + } + + void CustomTeleports::OnPreviousKeyPressed() + { + if (!f_Enabled || selectedIndex < 0) + return; + + auto& mapTeleport = MapTeleport::GetInstance(); + app::Vector3 position; + + if (selectedByClick) { + position = teleports.at(selectedIndex).second; + selectedByClick = false; + } + else { + std::vector list(checkedIndices.begin(), checkedIndices.end()); + if (selectedIndex == list.front()) + return; + + auto index = std::distance(list.begin(), std::find(list.begin(), list.end(), selectedIndex)); + position = teleports.at(list.at(index - 1)).second; + selectedIndex = list.at(index - 1); + } + mapTeleport.TeleportTo(position); + UpdateIndexName(); + } + + void CustomTeleports::UpdateIndexName() { + std::string name(selectedIndex == -1 || checkedIndices.empty() ? "" : teleports.at(selectedIndex).first); + + // abbreviate teleport names that are too long + if (name.length() > 15) { + std::string shortened; + std::regex numsExp("[\\d]+"); + std::regex firstCharsExp("\\b[A-Za-z]"); + + std::sregex_iterator wordItr(name.begin(), name.end(), firstCharsExp); + while (wordItr != std::sregex_iterator()) { + for (unsigned i = 0; i < wordItr->size(); i++) { + shortened.append((*wordItr)[i]); + } + wordItr++; + } + + std::sregex_iterator numItr(name.begin(), name.end(), numsExp); + while (numItr != std::sregex_iterator()) { + for (unsigned i = 0; i < numItr->size(); i++) { + shortened.append(" "); + shortened.append((*numItr)[i]); + } + numItr++; + } + name = shortened; + } + selectedIndexName = name; + + } + + CustomTeleports& CustomTeleports::GetInstance() + { + static CustomTeleports instance; + return instance; + } } diff --git a/cheat-library/src/user/cheat/teleport/CustomTeleports.h b/cheat-library/src/user/cheat/teleport/CustomTeleports.h index 9fa2d09..e0f6db5 100644 --- a/cheat-library/src/user/cheat/teleport/CustomTeleports.h +++ b/cheat-library/src/user/cheat/teleport/CustomTeleports.h @@ -4,6 +4,7 @@ #include #include +#include namespace cheat::feature { @@ -11,10 +12,25 @@ namespace cheat::feature { public: config::Field> f_DebugMode; + config::Field> f_Enabled; + config::Field f_Next; + config::Field f_Previous; static CustomTeleports& GetInstance(); const FeatureGUIInfo& GetGUIInfo() const override; void DrawMain() override; + + virtual bool NeedStatusDraw() const override; + void DrawStatus() override; + private: + std::vector> teleports; + std::set checkedIndices; + bool selectedByClick = false; + int selectedIndex = -1; + std::string selectedIndexName; CustomTeleports(); + void OnNextKeyPressed(); + void OnPreviousKeyPressed(); + void UpdateIndexName(); }; } \ No newline at end of file From a843ce87bd3264ae67d09ae763d618d62d6a736a Mon Sep 17 00:00:00 2001 From: HarrySilan Date: Sun, 19 Jun 2022 21:13:45 +0800 Subject: [PATCH 2/6] fix TP buttons --- cheat-library/src/user/cheat/teleport/CustomTeleports.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp b/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp index ef344e6..8156165 100644 --- a/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp +++ b/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp @@ -200,8 +200,7 @@ namespace cheat::feature bool checked = std::any_of(checkedIndices.begin(), checkedIndices.end(), [&index](const auto& i) { return i == index; }); bool selected = index == selectedIndex; - const char* hashId = ("##Index" + std::to_string(index)).c_str(); - ImGui::Checkbox(hashId, &checked); + ImGui::Checkbox(("##Index" + std::to_string(index)).c_str(), &checked); if (ImGui::IsItemClicked(0)) { if (checked) { if (selected) selectedIndex = -1; @@ -213,7 +212,7 @@ namespace cheat::feature } } ImGui::SameLine(); - if (ImGui::Button("TP")) + if (ImGui::Button(("TP##Button" + std::to_string(index)).c_str())) { auto& mapTeleport = MapTeleport::GetInstance(); mapTeleport.TeleportTo(position); From fce6ed4a444590df5d981af1c1e5bd3430ed3b75 Mon Sep 17 00:00:00 2001 From: HarrySilan Date: Mon, 20 Jun 2022 13:38:47 +0800 Subject: [PATCH 3/6] update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3e06217..e8d3907 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ As well as setting up **`cheat-library`** as startup project. #### Teleport - Chest/Oculi Teleport (Teleports to nearest) - Map Teleport (Teleport to mark on map) +- Custom Teleport (Teleport through list) #### Visuals - ESP From a2f656302cf75a4c91a335f2fed648b9c8bd1ea9 Mon Sep 17 00:00:00 2001 From: HarrySilan Date: Mon, 20 Jun 2022 23:04:49 +0800 Subject: [PATCH 4/6] Check All now leave checkmarks only on Search if used together --- .../user/cheat/teleport/CustomTeleports.cpp | 28 +++++++++++++++---- .../src/user/cheat/teleport/CustomTeleports.h | 1 + 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp b/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp index 8156165..1c86527 100644 --- a/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp +++ b/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp @@ -172,17 +172,27 @@ namespace cheat::feature std::sort(teleports.begin(), teleports.end(), [](const auto& a, const auto& b) {return doj::alphanum_less()(a.first, b.first); }); - bool allChecked = checkedIndices.size() == teleports.size() && !teleports.empty(); - ImGui::Checkbox("Check All", &allChecked); + bool allSearchChecked = std::includes(checkedIndices.begin(), checkedIndices.end() ,searchIndices.begin(), searchIndices.end()) && !searchIndices.empty(); + bool allChecked = (checkedIndices.size() == teleports.size() && !teleports.empty()) || allSearchChecked; + ImGui::Checkbox("All", &allChecked); if (ImGui::IsItemClicked()) { if (!teleports.empty()) { if (allChecked) { selectedIndex = -1; - checkedIndices.clear(); + if (!searchIndices.empty()) { + checkedIndices.erase(searchIndices.begin(), searchIndices.end()); + } + else { + checkedIndices.clear(); + } } else { - for (int i = 0; i < teleports.size(); i++) { - checkedIndices.insert(i); + if (!searchIndices.empty()) { + checkedIndices.insert(searchIndices.begin(), searchIndices.end()); + } + else { + for (int i = 0; i < teleports.size(); i++) + checkedIndices.insert(i); } } UpdateIndexName(); @@ -191,12 +201,19 @@ namespace cheat::feature ImGui::SameLine(); ImGui::InputText("Search", &search); unsigned int index = 0; + searchIndices.clear(); for (const auto& [teleportName, position] : teleports) { // find without case sensitivity if (search.empty() || std::search(teleportName.begin(), teleportName.end(), search.begin(), search.end(), [](char a, char b) { return std::tolower(a) == std::tolower(b); }) != teleportName.end()) { + // sets are sorted by default and does not allow duplicates + // which works in favor here. + if (!search.empty()) { + searchIndices.insert(index); + } + bool checked = std::any_of(checkedIndices.begin(), checkedIndices.end(), [&index](const auto& i) { return i == index; }); bool selected = index == selectedIndex; @@ -207,7 +224,6 @@ namespace cheat::feature checkedIndices.erase(index); } else { - // sets are sorted by default checkedIndices.insert(index); } } diff --git a/cheat-library/src/user/cheat/teleport/CustomTeleports.h b/cheat-library/src/user/cheat/teleport/CustomTeleports.h index e0f6db5..aba3865 100644 --- a/cheat-library/src/user/cheat/teleport/CustomTeleports.h +++ b/cheat-library/src/user/cheat/teleport/CustomTeleports.h @@ -25,6 +25,7 @@ namespace cheat::feature private: std::vector> teleports; std::set checkedIndices; + std::set searchIndices; bool selectedByClick = false; int selectedIndex = -1; std::string selectedIndexName; From 48f75d168f210bdda80b9edf5d2c473bdcb51822 Mon Sep 17 00:00:00 2001 From: HarrySilan Date: Tue, 21 Jun 2022 00:45:26 +0800 Subject: [PATCH 5/6] replace Alphanum with StrCmpLogicalW --- .../cheat-base/events/helpers/alphanum.hpp | 274 ------------------ cheat-library/cheat-library.vcxproj | 2 +- .../user/cheat/teleport/CustomTeleports.cpp | 5 +- 3 files changed, 4 insertions(+), 277 deletions(-) delete mode 100644 cheat-base/src/cheat-base/events/helpers/alphanum.hpp diff --git a/cheat-base/src/cheat-base/events/helpers/alphanum.hpp b/cheat-base/src/cheat-base/events/helpers/alphanum.hpp deleted file mode 100644 index 6dac03c..0000000 --- a/cheat-base/src/cheat-base/events/helpers/alphanum.hpp +++ /dev/null @@ -1,274 +0,0 @@ -#ifndef ALPHANUM__HPP -#define ALPHANUM__HPP - -/* -The Alphanum Algorithm is an improved sorting algorithm for strings -containing numbers. Instead of sorting numbers in ASCII order like a -standard sort, this algorithm sorts numbers in numeric order. - -The Alphanum Algorithm is discussed at http://www.DaveKoelle.com - -This implementation is Copyright (c) 2008 Dirk Jagdmann . -It is a cleanroom implementation of the algorithm and not derived by -other's works. In contrast to the versions written by Dave Koelle this -source code is distributed with the libpng/zlib license. - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you - must not claim that you wrote the original software. If you use - this software in a product, an acknowledgment in the product - documentation would be appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and - must not be misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. */ - - /* $Header: /code/doj/alphanum.hpp,v 1.3 2008/01/28 23:06:47 doj Exp $ */ - -#include -#include -#include -#include - -#ifdef ALPHANUM_LOCALE -#include -#endif - -#ifdef DOJDEBUG -#include -#include -#endif - -// TODO: make comparison with hexadecimal numbers. Extend the alphanum_comp() function by traits to choose between decimal and hexadecimal. - -namespace doj -{ - - // anonymous namespace for functions we use internally. But if you - // are coding in C, you can use alphanum_impl() directly, since it - // uses not C++ features. - namespace { - - // if you want to honour the locale settings for detecting digit - // characters, you should define ALPHANUM_LOCALE -#ifdef ALPHANUM_LOCALE - - bool alphanum_isdigit(int c) - { - return isdigit(c); - } -#else - - bool alphanum_isdigit(const char c) - { - return c >= '0' && c <= '9'; - } -#endif - - int alphanum_impl(const char* l, const char* r) - { - enum mode_t { STRING, NUMBER } mode = STRING; - - while (*l && *r) - { - if (mode == STRING) - { - char l_char, r_char; - while ((l_char = *l) && (r_char = *r)) - { - // check if this are digit characters - const bool l_digit = alphanum_isdigit(l_char), r_digit = alphanum_isdigit(r_char); - // if both characters are digits, we continue in NUMBER mode - if (l_digit && r_digit) - { - mode = NUMBER; - break; - } - // if only the left character is a digit, we have a result - if (l_digit) return -1; - // if only the right character is a digit, we have a result - if (r_digit) return +1; - // compute the difference of both characters - const int diff = l_char - r_char; - // if they differ we have a result - if (diff != 0) return diff; - // otherwise process the next characters - ++l; - ++r; - } - } - else // mode==NUMBER - { -#ifdef ALPHANUM_LOCALE - // get the left number - char* end; - unsigned long l_int = strtoul(l, &end, 0); - l = end; - - // get the right number - unsigned long r_int = strtoul(r, &end, 0); - r = end; -#else - // get the left number - unsigned long l_int = 0; - while (*l && alphanum_isdigit(*l)) - { - // TODO: this can overflow - l_int = l_int * 10 + *l - '0'; - ++l; - } - - // get the right number - unsigned long r_int = 0; - while (*r && alphanum_isdigit(*r)) - { - // TODO: this can overflow - r_int = r_int * 10 + *r - '0'; - ++r; - } -#endif - - // if the difference is not equal to zero, we have a comparison result - const long diff = l_int - r_int; - if (diff != 0) - return diff; - - // otherwise we process the next substring in STRING mode - mode = STRING; - } - } - - if (*r) return -1; - if (*l) return +1; - return 0; - } - - } - - template - int alphanum_comp(const lT& left, const rT& right) - { -#ifdef DOJDEBUG - std::clog << "alphanum_comp<" << typeid(left).name() << "," << typeid(right).name() << "> " << left << "," << right << std::endl; -#endif - std::ostringstream l; l << left; - std::ostringstream r; r << right; - return alphanum_impl(l.str().c_str(), r.str().c_str()); - } - - template <> inline - int alphanum_comp(const std::string& l, const std::string& r) - { -#ifdef DOJDEBUG - std::clog << "alphanum_comp " << l << "," << r << std::endl; -#endif - return alphanum_impl(l.c_str(), r.c_str()); - } - - - // now follow a lot of overloaded alphanum_comp() functions to get a - // direct call to alphanum_impl() upon the various combinations of c - // and c++ strings. - - inline int alphanum_comp(char* l, char* r) - { - assert(l); - assert(r); -#ifdef DOJDEBUG - std::clog << "alphanum_comp " << l << "," << r << std::endl; -#endif - return alphanum_impl(l, r); - } - - inline int alphanum_comp(const char* l, const char* r) - { - assert(l); - assert(r); -#ifdef DOJDEBUG - std::clog << "alphanum_comp " << l << "," << r << std::endl; -#endif - return alphanum_impl(l, r); - } - - inline int alphanum_comp(char* l, const char* r) - { - assert(l); - assert(r); -#ifdef DOJDEBUG - std::clog << "alphanum_comp " << l << "," << r << std::endl; -#endif - return alphanum_impl(l, r); - } - - inline int alphanum_comp(const char* l, char* r) - { - assert(l); - assert(r); -#ifdef DOJDEBUG - std::clog << "alphanum_comp " << l << "," << r << std::endl; -#endif - return alphanum_impl(l, r); - } - - inline int alphanum_comp(const std::string& l, char* r) - { - assert(r); -#ifdef DOJDEBUG - std::clog << "alphanum_comp " << l << "," << r << std::endl; -#endif - return alphanum_impl(l.c_str(), r); - } - - inline int alphanum_comp(char* l, const std::string& r) - { - assert(l); -#ifdef DOJDEBUG - std::clog << "alphanum_comp " << l << "," << r << std::endl; -#endif - return alphanum_impl(l, r.c_str()); - } - - inline int alphanum_comp(const std::string& l, const char* r) - { - assert(r); -#ifdef DOJDEBUG - std::clog << "alphanum_comp " << l << "," << r << std::endl; -#endif - return alphanum_impl(l.c_str(), r); - } - - inline int alphanum_comp(const char* l, const std::string& r) - { - assert(l); -#ifdef DOJDEBUG - std::clog << "alphanum_comp " << l << "," << r << std::endl; -#endif - return alphanum_impl(l, r.c_str()); - } - - - template - struct alphanum_less - { public: - typedef Ty first_arg_type; - typedef Ty second_arg_type; - typedef bool result_type; - bool operator()(const Ty& left, const Ty& right) const - { - return alphanum_comp(left, right) < 0; - } - }; - -} - - -#endif \ No newline at end of file diff --git a/cheat-library/cheat-library.vcxproj b/cheat-library/cheat-library.vcxproj index f4dabce..994a6f9 100644 --- a/cheat-library/cheat-library.vcxproj +++ b/cheat-library/cheat-library.vcxproj @@ -962,7 +962,7 @@ false false $(OutDir);$(ProjectDir)vendor\lib\ - cheat-base.lib;ntdll.lib;%(AdditionalDependencies) + cheat-base.lib;ntdll.lib;shlwapi.lib;%(AdditionalDependencies) RequireAdministrator diff --git a/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp b/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp index 1c86527..6d6ee78 100644 --- a/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp +++ b/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include "shlwapi.h" namespace cheat::feature { @@ -168,9 +168,10 @@ namespace cheat::feature if (ImGui::TreeNode("Teleports")) { + // using natural sort instead of ascii sort std::sort(teleports.begin(), teleports.end(), [](const auto& a, const auto& b) - {return doj::alphanum_less()(a.first, b.first); }); + { return StrCmpLogicalW(std::wstring(a.first.begin(), a.first.end()).c_str(), std::wstring(b.first.begin(), b.first.end()).c_str()) < 0; }); bool allSearchChecked = std::includes(checkedIndices.begin(), checkedIndices.end() ,searchIndices.begin(), searchIndices.end()) && !searchIndices.empty(); bool allChecked = (checkedIndices.size() == teleports.size() && !teleports.empty()) || allSearchChecked; From b8ffcb6c9d9cbb4433d6e8c48ed5a5a272908d1d Mon Sep 17 00:00:00 2001 From: HarrySilan Date: Tue, 21 Jun 2022 13:18:56 +0800 Subject: [PATCH 6/6] fix Active Feature status not updating when uncheking selected teleport. --- cheat-library/src/user/cheat/teleport/CustomTeleports.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp b/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp index 6d6ee78..1751938 100644 --- a/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp +++ b/cheat-library/src/user/cheat/teleport/CustomTeleports.cpp @@ -227,6 +227,7 @@ namespace cheat::feature else { checkedIndices.insert(index); } + UpdateIndexName(); } ImGui::SameLine(); if (ImGui::Button(("TP##Button" + std::to_string(index)).c_str()))