-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pathfinding window, quest module, not prod rdy yet
- Loading branch information
Showing
7 changed files
with
402 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.