Skip to content

Commit

Permalink
Pathfinding window, quest module, not prod rdy yet
Browse files Browse the repository at this point in the history
  • Loading branch information
3vcloud committed Dec 7, 2023
1 parent 116b780 commit 1b5d3d5
Show file tree
Hide file tree
Showing 7 changed files with 402 additions and 55 deletions.
230 changes: 219 additions & 11 deletions GWToolboxdll/Modules/QuestModule.cpp
Original file line number Diff line number Diff line change
@@ -1,30 +1,238 @@
#include <GWCA/Utilities/Hook.h>

#include <GWCA/GameEntities/Quest.h>
#include <GWCA/GameEntities/Agent.h>

#include <GWCA/Managers/UIMgr.h>
#include <GWCA/Managers/QuestMgr.h>
#include <GWCA/Managers/AgentMgr.h>

#include "QuestModule.h"

#include <Windows/PathfindingWindow.h>
#include <Widgets/Minimap/CustomRenderer.h>
#include <Widgets/Minimap/Minimap.h>

namespace {
// Settings
GW::HookEntry ui_message_entry;

void OnQuestPathRecalculated(const std::vector<GW::GamePos>& waypoints, void* args);

void ClearCalculatedPath(GW::Constants::QuestID quest_id);

void RedirectQuestMarker(const GW::GamePos& new_marker);

void OnPreUIMessage(GW::HookStatus*, const GW::UI::UIMessage message_id, void*, void*)
{
switch (message_id) {
case GW::UI::UIMessage::kMapLoaded:
break;
struct CalculatedQuestPath {
CalculatedQuestPath(GW::Constants::QuestID _quest_id) : quest_id(_quest_id) {};
~CalculatedQuestPath() {
ClearMinimapLines();
}
std::vector<GW::GamePos> waypoints;
std::vector<CustomRenderer::CustomLine*> minimap_lines;
GW::GamePos original_quest_marker;
GW::GamePos calculated_from;
uint32_t current_waypoint = 0;
GW::Constants::QuestID quest_id;
bool calculating = false;
void ClearMinimapLines() {
for (auto l : minimap_lines) {
Minimap::Instance().custom_renderer.RemoveCustomLine(l);
}
minimap_lines.clear();
}

void DrawMinimapLines() {
ClearMinimapLines();
for (size_t i = current_waypoint; i < waypoints.size() - 1; i++) {
const auto l = Minimap::Instance().custom_renderer.AddCustomLine(waypoints[i],waypoints[i + 1]);
minimap_lines.push_back(l);
}
}

const GW::Quest* GetQuest() {
return GW::QuestMgr::GetQuest(static_cast<GW::Constants::QuestID>(quest_id));
}
bool IsActive() {
const auto a = GW::QuestMgr::GetActiveQuest();
return a && a == GetQuest();
}
const GW::GamePos* CurrentWaypoint() {
return &waypoints[current_waypoint];
}
const GW::GamePos* NextWaypoint() {
return current_waypoint < waypoints.size() - 1 ? &waypoints[current_waypoint + 1] : nullptr;
}
void Recalculate(const GW::GamePos& from) {
if (from == calculated_from)
return;
if (original_quest_marker.x == INFINITY)
return;
calculated_from = from;
calculating = true;
PathfindingWindow::CalculatePath(calculated_from, original_quest_marker, OnQuestPathRecalculated, this);
}
bool Update(const GW::GamePos& from) {
const auto quest = GetQuest();
if (!quest) {
ClearCalculatedPath(quest_id);
return true;
}
if (calculating) {
return false;
}
constexpr float dist_check = 1500.f * 1500.f;
if (GetSquareDistance(from, calculated_from) > 1500.f * 1500.f) {
Recalculate(from);
return false;
}
const auto wp = CurrentWaypoint();
if (wp && current_waypoint < waypoints.size() - 1 && GetSquareDistance(from, *wp) < dist_check) {
current_waypoint++;
calculated_from = from;
UpdateUI();
}
return false;
}
void UpdateUI() {
if (!waypoints.size())
return;
DrawMinimapLines();
if(IsActive())
RedirectQuestMarker(waypoints[current_waypoint]);
}
};

std::unordered_map<GW::Constants::QuestID, CalculatedQuestPath*> calculated_quest_paths;
void ClearCalculatedPaths() {
for (auto& it : calculated_quest_paths) {
delete it.second;
}
calculated_quest_paths.clear();
}
void ClearCalculatedPath(GW::Constants::QuestID quest_id) {
const auto found = calculated_quest_paths.find(quest_id);
if (found == calculated_quest_paths.end())
return;
ASSERT(!found->second->calculating);
delete found->second;
calculated_quest_paths.erase(found);
}
CalculatedQuestPath* GetCalculatedQuestPath(GW::Constants::QuestID quest_id) {
const auto found = calculated_quest_paths.find(quest_id);
if (found != calculated_quest_paths.end()) return found->second;
auto cqp = new CalculatedQuestPath(quest_id);
calculated_quest_paths[quest_id] = cqp;
return cqp;
}

GW::UI::UIMessage ui_messages[] = {
GW::UI::UIMessage::kQuestDetailsChanged,
GW::UI::UIMessage::kQuestAdded,
GW::UI::UIMessage::kClientActiveQuestChanged
};

bool is_spoofing_quest_update = false;

// Settings
GW::GamePos* GetPlayerPos() {
const auto p = GW::Agents::GetPlayer();
return p ? &p->pos : nullptr;
}

// Replace marker for current quest, broadcast update to the game
void RedirectQuestMarker(const GW::GamePos& new_marker) {
const auto quest = GW::QuestMgr::GetActiveQuest();
if (!quest)
return;
Log::Info("Overriding quest marker from %.2f, %.2f to %.2f, %.2f", quest->marker.x, quest->marker.y, new_marker.x, new_marker.y);
if (quest->marker == new_marker)
return; // No change

quest->marker = new_marker;
struct QuestUIMsg {
GW::Constants::QuestID quest_id{};
GW::GamePos marker{};
uint32_t h0024{};
GW::Constants::MapID map_to{};
uint32_t log_state{};
} msg;
msg.quest_id = quest->quest_id;
msg.marker = quest->marker;
msg.h0024 = quest->h0024;
msg.map_to = quest->map_to;
msg.log_state = quest->log_state;

is_spoofing_quest_update = true;
SendUIMessage(GW::UI::UIMessage::kClientActiveQuestChanged, &msg);
is_spoofing_quest_update = false;
}
// Cast helper
float GetSquareDistance(const GW::GamePos& a, const GW::GamePos& b) {
return GetSquareDistance(static_cast<GW::Vec2f>(a), static_cast<GW::Vec2f>(b));
}

void OnPostUIMessage(GW::HookStatus*, const GW::UI::UIMessage message_id, void*, void*)
{
switch (message_id) {
case GW::UI::UIMessage::kMapLoaded:
break;
// Called by PathfindingWindow when a path has been calculated. Should be on the main loop.
void OnQuestPathRecalculated(const std::vector<GW::GamePos>& waypoints, void* args) {
CalculatedQuestPath* cqp = (CalculatedQuestPath*)args;
ASSERT(cqp->calculating);

cqp->current_waypoint = 0;
cqp->waypoints = waypoints;

if (GetSquareDistance(cqp->waypoints.back(), cqp->calculated_from) < GetSquareDistance(cqp->waypoints.front(), cqp->calculated_from)) {
// Waypoint array is in descending distance, flip it
std::reverse(cqp->waypoints.begin(), cqp->waypoints.end());
}

cqp->calculating = false;
cqp->UpdateUI();
}
// Callback invoked by quest related ui messages. All messages sent should have the quest id as first wparam variable
void OnGWQuestMarkerUpdated(GW::HookStatus*,GW::UI::UIMessage, void* packet, void*) {
if (is_spoofing_quest_update)
return; // Recursive
GW::Constants::QuestID affected_quest_id = *(GW::Constants::QuestID*)packet;

const auto quest = GW::QuestMgr::GetQuest(affected_quest_id);
auto cqp = GetCalculatedQuestPath(affected_quest_id);

cqp->original_quest_marker = quest->marker;
const auto pos = GetPlayerPos();
if (!pos)
return;
cqp->Recalculate(*pos);
}


} // namespace

void QuestModule::Initialize()
{
ToolboxModule::Initialize();

for (auto ui_message : ui_messages) {
// Post callbacks, non blocking
(ui_message);
//GW::UI::RegisterUIMessageCallback(&ui_message_entry, ui_message, OnGWQuestMarkerUpdated, 0x4000);
}
}
void QuestModule::SignalTerminate() {
ToolboxModule::SignalTerminate();
GW::UI::RemoveUIMessageCallback(&ui_message_entry);
ClearCalculatedPaths();
}
void QuestModule::Update(float) {
const auto pos = GetPlayerPos();
if (!pos)
return;

for (const auto& it : calculated_quest_paths) {
if (it.second->Update(*pos))
break; // Deleted, skip frame
}
}
void QuestModule::Terminate()
{
ToolboxModule::Terminate();

}
5 changes: 2 additions & 3 deletions GWToolboxdll/Modules/QuestModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,10 @@ class QuestModule : public ToolboxModule {

[[nodiscard]] const char* Name() const override { return "Quest Module"; }
[[nodiscard]] const char* Icon() const override { return ICON_FA_COMPASS; }
[[nodiscard]] const char* Description() const override { return "A set of QoL improvements to the quest log and related behavior"; }

void Initialize() override;
void Terminate() override;
void SignalTerminate() override;
void Update(float) override;
void DrawSettingsInternal() override;
void LoadSettings(ToolboxIni*) override;
void SaveSettings(ToolboxIni*) override;
};
10 changes: 7 additions & 3 deletions GWToolboxdll/Modules/ToolboxSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <Modules/ChatLog.h>
#include <Modules/HintsModule.h>
#include <Modules/PluginModule.h>
#include <Modules/QuestModule.h>
#include <Modules/KeyboardLanguageFix.h>
#if 0
#include <Modules/GWFileRequester.h>
Expand Down Expand Up @@ -55,8 +56,9 @@
#include <Windows/DoorMonitorWindow.h>
#include <Windows/StringDecoderWindow.h>
#include <Windows/SkillListingWindow.h>
#include <Windows/PathfindingWindow.h>

#endif
#include <Windows/PathfindingWindow.h>
#include <Windows/RerollWindow.h>
#include <Windows/ArmoryWindow.h>

Expand Down Expand Up @@ -135,7 +137,8 @@ namespace {
MouseFix::Instance(),
KeyboardLanguageFix::Instance(),
ZrawDeepModule::Instance(),
GuildWarsSettingsModule::Instance()
GuildWarsSettingsModule::Instance(),
QuestModule::Instance()
};

std::vector<WidgetToggle> optional_widgets = {
Expand Down Expand Up @@ -209,8 +212,9 @@ void ToolboxSettings::LoadModules(ToolboxIni* ini)
GWToolbox::ToggleModule(StringDecoderWindow::Instance());
GWToolbox::ToggleModule(DoorMonitorWindow::Instance());
GWToolbox::ToggleModule(SkillListingWindow::Instance());
GWToolbox::ToggleModule(PathfindingWindow::Instance());

#endif
GWToolbox::ToggleModule(PathfindingWindow::Instance());
for (const auto& m : optional_modules) {
GWToolbox::ToggleModule(*m.toolbox_module, m.enabled);
}
Expand Down
Loading

0 comments on commit 1b5d3d5

Please sign in to comment.