Skip to content

Commit c7369cb

Browse files
GWToolbox Botclaude
andcommitted
fix(quest): defer quest path calc until loading screen clears
Server quest messages (kServerActiveQuestChanged, kQuestAdded, kClientActiveQuestChanged, kQuestDetailsChanged) arrive while the zone-in loading screen is still up. OnPostUIMessage refreshed the path immediately, kicking the route calc off against transitional map/pathing data and leaving the route stuck until a Toolbox restart. Instead of sprinkling loading-screen guards across each call site, the message handlers and OnMapLoaded now just raise refresh_all_quest_paths_queued. QuestModule::Update consumes it in one place, already behind the existing loading-screen and pathing guards, so the calc only ever kicks off once the map is fully loaded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent b589449 commit c7369cb

1 file changed

Lines changed: 13 additions & 5 deletions

File tree

GWToolboxdll/Modules/QuestModule.cpp

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ namespace {
3737
bool QuestPathingAvailable() { return PathfindingWindow::IsPathingEnabled(); }
3838

3939
bool fetch_missing_quest_info_queued = false;
40+
// Quest messages and map loads only flag a refresh; QuestModule::Update() consumes it behind the loading-screen and
41+
// pathing guards, so we never kick a route calc off against transitional map data mid-load.
42+
bool refresh_all_quest_paths_queued = false;
4043

4144
std::vector<QuestModule::CustomMarkerChangedCallback> custom_marker_callbacks;
4245

@@ -560,10 +563,10 @@ namespace {
560563
// Quest assigned without user interaction
561564
QuestModule::SetActiveQuestId(player_chosen_quest_id, true);
562565
}
563-
RefreshQuestPath(*static_cast<GW::Constants::QuestID*>(packet));
566+
refresh_all_quest_paths_queued = true;
564567
} break;
565568
case GW::UI::UIMessage::kServerActiveQuestChanged:
566-
RefreshQuestPath(*static_cast<GW::Constants::QuestID*>(packet));
569+
refresh_all_quest_paths_queued = true;
567570
break;
568571
case GW::UI::UIMessage::kMapLoaded:
569572
BlockQuestSound();
@@ -583,11 +586,9 @@ namespace {
583586
if (custom_quest_marker_world_pos.y != 0 || custom_quest_marker_world_pos.x != 0) {
584587
QuestModule::SetCustomQuestMarker(custom_quest_marker_world_pos, quest_id_before_map_load == custom_quest_id);
585588
}
586-
RefreshAllQuestPaths();
589+
refresh_all_quest_paths_queued = true;
587590
}
588591

589-
bool refresh_all_quest_paths_queued = 0;
590-
591592
// Dangerously frees gw memory!
592593
void RemoveQuest(GW::Constants::QuestID quest_id)
593594
{
@@ -929,6 +930,13 @@ void QuestModule::Update(float)
929930
return;
930931
}
931932

933+
// Consume any refresh flagged by quest messages / map loads. We're past the loading-screen and pathing guards here,
934+
// so this is the one place a route calc kicks off — never against transitional map data mid-load.
935+
if (refresh_all_quest_paths_queued) {
936+
refresh_all_quest_paths_queued = false;
937+
RefreshAllQuestPaths();
938+
}
939+
932940
const size_t size = calculated_quest_paths.size();
933941
check_paths:
934942
for (const auto& [quest_id, calculated_quest_path] : calculated_quest_paths) {

0 commit comments

Comments
 (0)