Merge pull request #210 from Shichiha/patch-4

Better CustomTeleports
This commit is contained in:
Callow 2022-07-13 10:51:52 +03:00 committed by GitHub
commit a30ec4f726
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 329 additions and 254 deletions

View File

@ -22,8 +22,8 @@ namespace cheat::feature
NF(f_Next, "Teleport Next", "CustomTeleports", Hotkey(VK_OEM_6)), NF(f_Next, "Teleport Next", "CustomTeleports", Hotkey(VK_OEM_6)),
NF(f_Previous, "Teleport Previous", "CustomTeleports", Hotkey(VK_OEM_4)) NF(f_Previous, "Teleport Previous", "CustomTeleports", Hotkey(VK_OEM_4))
{ {
f_Next.value().PressedEvent += MY_METHOD_HANDLER(CustomTeleports::OnNextKeyPressed); f_Next.value().PressedEvent += MY_METHOD_HANDLER(CustomTeleports::OnNext);
f_Previous.value().PressedEvent += MY_METHOD_HANDLER(CustomTeleports::OnPreviousKeyPressed); f_Previous.value().PressedEvent += MY_METHOD_HANDLER(CustomTeleports::OnPrevious);
} }
const FeatureGUIInfo& CustomTeleports::GetGUIInfo() const const FeatureGUIInfo& CustomTeleports::GetGUIInfo() const
{ {
@ -31,244 +31,373 @@ namespace cheat::feature
return info; return info;
} }
void CustomTeleports::DrawMain() void CustomTeleports::CheckFolder()
{ {
auto& entityManager = game::EntityManager::instance();
auto& MapTeleport = MapTeleport::GetInstance();
static std::string teleportName;
static std::string search;
app::Vector3 pos = app::ActorUtils_GetAvatarPos(nullptr);
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;
// check if already added
if (std::any_of(teleports.begin(), teleports.end(), [](const auto& pair)
{ return pair.first == teleportName; }))
return;
selectedIndex = -1;
UpdateIndexName();
teleports.push_back({ teleportName, pos });
auto dir = std::filesystem::current_path();
dir /= "teleports";
if (!std::filesystem::exists(dir)) if (!std::filesystem::exists(dir))
std::filesystem::create_directory(dir); std::filesystem::create_directory(dir);
std::ofstream ofs(dir / (teleportName + ".json")); else return;
}
bool CustomTeleports::ValidateTeleport(std::string name)
{
for (auto &Teleport : Teleports)
if (Teleport.name == name)
return false;
if (name.find_first_of("\\/:*?\"<>|") != std::string::npos)
return false;
return true;
}
Teleport CustomTeleports::Teleport_(std::string name, app::Vector3 position, std::string description)
{
Teleport t(name, position, description);
return t;
}
void CustomTeleports::SerializeTeleport(Teleport t)
{
Teleports.push_back(t);
LOG_INFO("Teleport '%s' Loaded", t.name.c_str());
CheckFolder();
std::ofstream ofs(dir / (t.name + ".json"));
nlohmann::json j; nlohmann::json j;
j["name"] = teleportName; try
j["position"] = { pos.x, pos.y, pos.z }; {
j["name"] = t.name;
j["position"] = {t.position.x, t.position.y, t.position.z};
j["description"] = t.description;
ofs << j; ofs << j;
teleportName.clear(); ofs.close();
LOG_INFO("Teleport '%s' Serialized.", t.name.c_str());
} catch (std::exception e)
{
ofs.close();
LOG_ERROR("Failed to serialize teleport: %s: %s", t.name.c_str(), e.what());
}
}
Teleport CustomTeleports::SerializeFromJson(std::string json, bool fromfile)
{
nlohmann::json j;
try { j = nlohmann::json::parse(json);}
catch (nlohmann::json::parse_error &e)
{
LOG_ERROR("Invalid JSON Format");
LOG_ERROR("Failed to parse JSON: %s", e.what());
}
std::string teleportName;
teleportName = j["name"];
if (j["name"].is_null() && fromfile)
{
LOG_ERROR("No name found! Using File Name");
teleportName = std::filesystem::path(json).stem().filename().string();
}
std::string description;
if (j["description"].is_null()) description = "";
else description = j["description"];
return Teleport_(teleportName, {j["position"][0], j["position"][1], j["position"][2]}, description);
}
void CustomTeleports::ReloadTeleports()
{
auto result = std::filesystem::directory_iterator(dir);
Teleports.clear();
for (auto &file : result)
{
if (file.path().extension() == ".json")
{
std::ifstream ifs(file.path());
std::string json;
std::getline(ifs, json);
SerializeTeleport(SerializeFromJson(json, true));
}
}
}
float PositionDistance(app::Vector3 a, app::Vector3 b)
{
return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2) + pow(a.z - b.z, 2));
}
void CustomTeleports::OnTeleportKeyPressed(bool next)
{
if (!f_Enabled || selectedIndex < 0)
return;
auto &mapTeleport = MapTeleport::GetInstance();
app::Vector3 position;
if (selectedByClick)
{
position = Teleports.at(selectedIndex).position;
selectedByClick = false;
}
else
{
std::vector list(checkedIndices.begin(), checkedIndices.end());
if (selectedIndex == list.back() ? next : selectedIndex == list.front())
return;
auto index = std::distance(list.begin(), std::find(list.begin(), list.end(), selectedIndex));
position = Teleports.at(list.at(index + (next ? 1 : -1))).position;
selectedIndex = list.at(index + (next ? 1 : -1));
}
mapTeleport.TeleportTo(position);
UpdateIndexName();
}
void CustomTeleports::OnPrevious()
{
OnTeleportKeyPressed(false);
}
void CustomTeleports::OnNext()
{
OnTeleportKeyPressed(true);
}
void CustomTeleports::UpdateIndexName()
{
std::string name(selectedIndex == -1 || checkedIndices.empty() ? "" : Teleports.at(selectedIndex).name);
// 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;
}
void CustomTeleports::DrawMain()
{
// Buffers
static std::string nameBuffer_;
static std::string searchBuffer_;
static std::string JSONBuffer_;
static std::string descriptionBuffer_;
ImGui::InputText("Name", &nameBuffer_);
ImGui::InputText("Description", &descriptionBuffer_);
if (ImGui::Button("Add Teleport"))
{
selectedIndex = -1;
UpdateIndexName();
SerializeTeleport(Teleport_(nameBuffer_, app::ActorUtils_GetAvatarPos(nullptr), descriptionBuffer_));
nameBuffer_ = "";
descriptionBuffer_ = "";
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Reload")) if (ImGui::Button("Reload"))
{ {
selectedIndex = -1; selectedIndex = -1;
UpdateIndexName(); UpdateIndexName();
checkedIndices.clear(); checkedIndices.clear();
auto dir = std::filesystem::current_path(); ReloadTeleports();
dir /= "teleports";
auto result = std::filesystem::directory_iterator(dir);
teleports.clear();
for (auto& file : result)
{
if (file.path().extension() != ".json")
continue;
std::string name = file.path().stem().string();
if (file.is_directory())
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(); ImGui::SameLine();
// open directory
if (ImGui::Button("Open Folder")) if (ImGui::Button("Open Folder"))
{ {
auto dir = std::filesystem::current_path(); CheckFolder();
dir /= "teleports";
ShellExecuteA(NULL, "open", dir.string().c_str(), NULL, NULL, SW_SHOW); ShellExecuteA(NULL, "open", dir.string().c_str(), NULL, NULL, SW_SHOW);
} }
ImGui::SameLine(); ImGui::SameLine();
static std::string jsonInput;
if (ImGui::Button("Load from JSON")) if (ImGui::Button("Load from JSON"))
{ {
selectedIndex = -1; selectedIndex = -1;
UpdateIndexName(); UpdateIndexName();
auto dir = std::filesystem::current_path(); SerializeTeleport(SerializeFromJson(JSONBuffer_, false));
dir /= "teleports"; JSONBuffer_ = "";
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) ImGui::InputTextMultiline("JSON input", &JSONBuffer_, ImVec2(0, 50), ImGuiInputTextFlags_AllowTabInput);
{
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);
ConfigWidget("Teleport Next", f_Next, true, "Press to teleport next of selected"); 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("Teleport Previous", f_Previous, true, "Press to teleport previous of selected");
ConfigWidget("Enable", ConfigWidget("Enable", f_Enabled,
f_Enabled, "Enable teleport-through-list functionality\n"
"Enable teleport-through-list functionality\n" \ "Usage:\n"
"Usage:\n" \ "1. Put Checkmark to the teleports you want to teleport using hotkey\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"
"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"
"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"
"Initially it will teleport the player to the selection made\n" \
"Note: Double click or click the arrow to open teleport details"); "Note: Double click or click the arrow to open teleport details");
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Delete Checked")) if (ImGui::Button("Delete Checked"))
{ {
if (!teleports.empty()) { if (!Teleports.empty())
std::vector<std::string> teleportNames; {
// get all teleport names by index if (checkedIndices.empty())
for (auto& i : checkedIndices) { {
teleportNames.push_back(teleports.at(i).first); LOG_INFO("No teleports selected");
if (selectedIndex == i) selectedIndex = -1; return;
} }
std::vector<std::string> teleportNames;
for (auto& name : teleportNames) { for (auto &Teleport : Teleports)
auto dir = std::filesystem::current_path(); teleportNames.push_back(Teleport.name);
dir /= "teleports"; for (auto &index : checkedIndices)
// delete file {
std::filesystem::remove(dir / (name + ".json")); std::filesystem::remove(dir / (teleportNames[index] + ".json"));
// remove from list LOG_INFO("Deleted teleport %s", teleportNames[index].c_str());
teleports.erase(std::remove_if(teleports.begin(), teleports.end(), [&name](const auto& pair)
{ return pair.first == name; }), teleports.end());
} }
checkedIndices.clear(); checkedIndices.clear();
UpdateIndexName(); UpdateIndexName();
} ReloadTeleports();
} else {LOG_INFO("No teleports to delete");}
} }
ImGui::SameLine(); ImGui::SameLine();
HelpMarker("Warning: This will delete the file from the directory and\nremove the teleport from the list. It will be lost forever."); HelpMarker("Warning: This will delete the file from the directory and\n \
remove the teleport from the list. It will be lost forever.");
if (ImGui::TreeNode("Teleports")) if (ImGui::TreeNode("Teleports"))
{ {
std::sort(Teleports.begin(), Teleports.end(), [](const auto &a, const auto &b)
// using natural sort instead of ascii sort { return StrCmpLogicalW(std::wstring(a.name.begin(), a.name.end()).c_str(), std::wstring(b.name.begin(), b.name.end()).c_str()) < 0; });
std::sort(teleports.begin(), teleports.end(), [](const auto& a, const auto& b) bool allChecked = checkedIndices.size() == Teleports.size() && !Teleports.empty();
{ 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 = checkedIndices.size() == searchIndices.size() && !searchIndices.empty();
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); ImGui::Checkbox("All", &allChecked);
if (ImGui::IsItemClicked()) { if (ImGui::IsItemClicked())
if (!teleports.empty()) { {
if (allChecked) { if (!Teleports.empty())
{
if (allChecked)
{
selectedIndex = -1; selectedIndex = -1;
if (!searchIndices.empty()) { if (!searchIndices.empty())
for (const auto& i : searchIndices) { for (const auto &i : searchIndices)
checkedIndices.erase(i); checkedIndices.erase(i);
} else
}
else {
checkedIndices.clear(); checkedIndices.clear();
} }
} else if (!searchIndices.empty())
else {
if (!searchIndices.empty()) {
checkedIndices.insert(searchIndices.begin(), searchIndices.end()); checkedIndices.insert(searchIndices.begin(), searchIndices.end());
} else
else { for (int i = 0; i < Teleports.size(); i++)
for (int i = 0; i < teleports.size(); i++)
checkedIndices.insert(i); checkedIndices.insert(i);
}
}
UpdateIndexName(); UpdateIndexName();
} }
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::InputText("Search", &search); ImGui::InputText("Search", &searchBuffer_);
unsigned int index = 0; unsigned int index = 0;
searchIndices.clear(); 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; }); unsigned int maxNameLength = 0;
for (auto &Teleport : Teleports)
if (Teleport.name.length() > maxNameLength)
maxNameLength = Teleport.name.length();
ImGui::BeginTable("Teleports", 4, ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_NoSavedSettings);
ImGui::TableSetupColumn("#", ImGuiTableColumnFlags_WidthFixed, 20);
ImGui::TableSetupColumn("Commands", ImGuiTableColumnFlags_WidthFixed, 100);
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, maxNameLength * 8 + 10);
ImGui::TableSetupColumn("Position");
ImGui::TableHeadersRow();
ImGuiTreeNodeFlags nodeFlags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_SpanAvailWidth;
for (const auto &[name, position, description] : Teleports)
{
if (searchBuffer_.empty() || std::search(name.begin(), name.end(), searchBuffer_.begin(), searchBuffer_.end(), [](char a, char b)
{ return std::tolower(a) == std::tolower(b); }) != name.end())
{
if (!searchBuffer_.empty())
searchIndices.insert(index);
bool checked = std::any_of(checkedIndices.begin(), checkedIndices.end(), [&index](const auto &i)
{ return i == index; });
bool selected = index == selectedIndex; bool selected = index == selectedIndex;
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Text("%d", index);
ImGui::TableNextColumn();
ImGui::Checkbox(("##Index" + std::to_string(index)).c_str(), &checked); ImGui::Checkbox(("##Index" + std::to_string(index)).c_str(), &checked);
if (ImGui::IsItemClicked(0)) { if (ImGui::IsItemClicked(0))
if (checked) { {
if (selected) selectedIndex = -1; if (checked)
{
if (selected)
selectedIndex = -1;
checkedIndices.erase(index); checkedIndices.erase(index);
} }
else { else
checkedIndices.insert(index); checkedIndices.insert(index);
}
UpdateIndexName(); UpdateIndexName();
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button(("TP##Button" + std::to_string(index)).c_str())) if (ImGui::Button(("TP##Button" + std::to_string(index)).c_str()))
{ {
auto& mapTeleport = MapTeleport::GetInstance(); auto &manager = game::EntityManager::instance();
mapTeleport.TeleportTo(position); auto avatar = manager.avatar();
if (avatar->moveComponent() == nullptr)
{
LOG_ERROR("Avatar has no move component, Is scene loaded?");
return;
}
if (PositionDistance(position, app::ActorUtils_GetAvatarPos(nullptr)) > 60.0f)
MapTeleport::GetInstance().TeleportTo(position);
else
manager.avatar()->setAbsolutePosition(position);
}
ImGui::SameLine();
if (ImGui::Button(("Select##Button" + std::to_string(index)).c_str()))
{
selectedIndex = index;
selectedByClick = true;
UpdateIndexName();
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, selected ? IM_COL32(40, 90, 175, 255) : IM_COL32(255, 255, 255, 255)); 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; if (selected)
bool node_open = ImGui::TreeNodeEx(teleportName.data(), nodeFlags); nodeFlags |= ImGuiTreeNodeFlags_Selected;
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(); ImGui::PopStyleColor();
ImGui::TableNextColumn();
ImGui::Text("%s", name.c_str());
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::Text("%s", description.c_str());
ImGui::Text("Distance: %.2f", PositionDistance(position, app::ActorUtils_GetAvatarPos(nullptr)));
ImGui::EndTooltip();
}
ImGui::TableNextColumn();
ImGui::Text("%f, %f, %f", position.x, position.y, position.z);
} }
index++; index++;
} }
ImGui::TreePop(); ImGui::EndTable();
} }
if (selectedIndex != -1)
ImGui::Text("Selected: [%d] %s", selectedIndex, Teleports[selectedIndex].name.c_str());
} }
bool CustomTeleports::NeedStatusDraw() const bool CustomTeleports::NeedStatusDraw() const
@ -281,87 +410,6 @@ namespace cheat::feature
ImGui::Text("Custom Teleport\n[%s]", selectedIndexName); 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() CustomTeleports &CustomTeleports::GetInstance()
{ {
static CustomTeleports instance; static CustomTeleports instance;

View File

@ -8,6 +8,20 @@
namespace cheat::feature namespace cheat::feature
{ {
class Teleport
{
public:
Teleport(std::string name, app::Vector3 position, std::string description)
{
this->name = name;
this->position = position;
this->description = description;
}
std::string name;
app::Vector3 position;
std::string description;
};
class CustomTeleports : public Feature class CustomTeleports : public Feature
{ {
public: public:
@ -17,21 +31,34 @@ namespace cheat::feature
config::Field<Hotkey> f_Previous; config::Field<Hotkey> f_Previous;
static CustomTeleports& GetInstance(); static CustomTeleports& GetInstance();
const FeatureGUIInfo& GetGUIInfo() const override; const FeatureGUIInfo& GetGUIInfo() const override;
void DrawMain() override;
void CheckFolder();
bool ValidateTeleport(std::string name);
Teleport Teleport_(std::string name, app::Vector3 position, std::string description);
void SerializeTeleport(Teleport t);
void ReloadTeleports();
Teleport SerializeFromJson(std::string json, bool fromfile);
void DrawMain() override;
virtual bool NeedStatusDraw() const override; virtual bool NeedStatusDraw() const override;
void DrawStatus() override; void DrawStatus() override;
std::vector<Teleport> Teleports;
std::filesystem::path dir = std::filesystem::current_path() / "teleports";
private: private:
std::vector<std::pair<std::string, app::Vector3>> teleports;
std::set<unsigned int> checkedIndices; std::set<unsigned int> checkedIndices;
std::set<unsigned int> searchIndices; std::set<unsigned int> searchIndices;
bool selectedByClick = false; bool selectedByClick = false;
int selectedIndex = -1; int selectedIndex = -1;
std::string selectedName;
std::string selectedIndexName; std::string selectedIndexName;
CustomTeleports(); CustomTeleports();
void OnNextKeyPressed(); void OnTeleportKeyPressed(bool next);
void OnPreviousKeyPressed(); void OnPrevious();
void OnNext();
void UpdateIndexName(); void UpdateIndexName();
}; };
} }