mirror of
https://github.com/SunsetMkt/Akebi-GC.git
synced 2025-01-09 02:53:43 +08:00
add teleport through list in Custom Teleport
This commit is contained in:
parent
e21ad2096a
commit
d3df5e1ea8
274
cheat-base/src/cheat-base/events/helpers/alphanum.hpp
Normal file
274
cheat-base/src/cheat-base/events/helpers/alphanum.hpp
Normal file
@ -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 <doj@cubic.org>.
|
||||
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 <cassert>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef ALPHANUM_LOCALE
|
||||
#include <cctype>
|
||||
#endif
|
||||
|
||||
#ifdef DOJDEBUG
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
#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 <typename lT, typename rT>
|
||||
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<std::string>(const std::string& l, const std::string& r)
|
||||
{
|
||||
#ifdef DOJDEBUG
|
||||
std::clog << "alphanum_comp<std::string,std::string> " << 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<char*,char*> " << 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<const char*,const char*> " << 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<char*,const char*> " << 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<const char*,char*> " << 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<std::string,char*> " << 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<char*,std::string> " << 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<std::string,const char*> " << 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<const char*,std::string> " << l << "," << r << std::endl;
|
||||
#endif
|
||||
return alphanum_impl(l, r.c_str());
|
||||
}
|
||||
|
||||
|
||||
template<class Ty>
|
||||
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
|
@ -9,165 +9,338 @@
|
||||
#include <misc/cpp/imgui_stdlib.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <helpers.h>
|
||||
#include <regex>
|
||||
#include <imgui_internal.h>
|
||||
#include <cheat-base/events/helpers/alphanum.hpp>
|
||||
|
||||
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<std::pair<std::string, app::Vector3>> 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<std::string> 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<std::string>()(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;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <cheat-base/cheat/Feature.h>
|
||||
#include <cheat-base/config/Config.h>
|
||||
#include <set>
|
||||
|
||||
namespace cheat::feature
|
||||
{
|
||||
@ -11,10 +12,25 @@ namespace cheat::feature
|
||||
{
|
||||
public:
|
||||
config::Field<config::Toggle<Hotkey>> f_DebugMode;
|
||||
config::Field<config::Toggle<Hotkey>> f_Enabled;
|
||||
config::Field<Hotkey> f_Next;
|
||||
config::Field<Hotkey> f_Previous;
|
||||
static CustomTeleports& GetInstance();
|
||||
const FeatureGUIInfo& GetGUIInfo() const override;
|
||||
void DrawMain() override;
|
||||
|
||||
virtual bool NeedStatusDraw() const override;
|
||||
void DrawStatus() override;
|
||||
|
||||
private:
|
||||
std::vector<std::pair<std::string, app::Vector3>> teleports;
|
||||
std::set<unsigned int> checkedIndices;
|
||||
bool selectedByClick = false;
|
||||
int selectedIndex = -1;
|
||||
std::string selectedIndexName;
|
||||
CustomTeleports();
|
||||
void OnNextKeyPressed();
|
||||
void OnPreviousKeyPressed();
|
||||
void UpdateIndexName();
|
||||
};
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user