refactor: mission, flag and collectible game messages as structs

Converts OfferMission, NotifyMission, NotifyMissionTask, ResetMissions,
NotifyClientFlagChange and NotifyLevelRewards (sent with SendToClient:
the old functions never broadcast) and the received RespondToMission,
MissionDialogueOK, RequestLinkedMission, SetFlag and HasBeenCollected
to NetGameMsgs in MissionMessages.{h,cpp}. Callers are switched,
OfferMission's send-it-twice behaviour moves to MissionOfferComponent,
the received messages are registered in GameMessageHandler's map and
the old functions are deleted. Handlers are copied verbatim. The
byte-equality helper can now compare SendToClient messages.

No wire change and no change in recipients. Verified byte for byte
against a frozen verbatim copy of the old functions over an input grid
(including OfferMission's two packets), received messages compared with
the old handlers' read sequences and every truncated payload rejected,
hand computed golden bytes and round trips. Layouts confirmed against
the 1.10.64 client in Ghidra.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-26 12:44:51 -05:00
parent 2681cc51ff
commit d599b788d0
16 changed files with 904 additions and 275 deletions

View File

@@ -9,6 +9,7 @@
#include "CDMissionsTable.h"
#include "CDMissionNPCComponentTable.h"
#include "GameMessages.h"
#include "MissionMessages.h"
#include "Entity.h"
#include "MissionComponent.h"
#include "Logger.h"
@@ -54,6 +55,24 @@ MissionOfferComponent::MissionOfferComponent(Entity* parent, const int32_t compo
}
}
namespace {
// "Why are we sending it twice, once to a non-player object?"
// The first one (targeting the offerer) makes the client zoom into the object.
// The second (targeting the player) actually makes the UI pop up so the mission can be offered.
// Why is it like this? Because LU isn't just a clown, it's the entire circus.
void OfferMissionToPlayer(const Entity& player, const int32_t missionID, const LWOOBJID offerer) {
GameMessages::OfferMission offerMission;
offerMission.missionID = missionID;
offerMission.offerer = offerer;
offerMission.target = offerer;
offerMission.SendToClient(player.GetSystemAddress());
offerMission.target = player.GetObjectID();
offerMission.SendToClient(player.GetSystemAddress());
}
}
void MissionOfferComponent::OnUse(Entity* originator) {
OfferMissions(originator);
}
@@ -95,7 +114,7 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
// We have the mission, if it is not complete, offer it
if (mission->IsActive() || mission->IsReadyToComplete()) {
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_Parent->GetObjectID());
OfferMissionToPlayer(*entity, missionId, m_Parent->GetObjectID());
continue;
}
@@ -132,7 +151,7 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
const auto& iter = std::find(randomMissionPool.begin(), randomMissionPool.end(), specifiedMissionId);
if (iter != randomMissionPool.end() && MissionPrerequisites::CanAccept(specifiedMissionId, missionComponent->GetMissions())) {
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), specifiedMissionId, m_Parent->GetObjectID());
OfferMissionToPlayer(*entity, specifiedMissionId, m_Parent->GetObjectID());
return;
}
@@ -152,7 +171,7 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
if (mission == nullptr || mission->IsAchievement()) continue;
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), sample, m_Parent->GetObjectID());
OfferMissionToPlayer(*entity, sample, m_Parent->GetObjectID());
canAcceptPool.clear();
@@ -169,9 +188,9 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi
const auto selected = canAcceptPool[GeneralUtils::GenerateRandomNumber<int>(0, canAcceptPool.size() - 1)];
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), selected, m_Parent->GetObjectID());
OfferMissionToPlayer(*entity, selected, m_Parent->GetObjectID());
} else if (offeredMission.GetOffersMission()) {
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_Parent->GetObjectID());
OfferMissionToPlayer(*entity, missionId, m_Parent->GetObjectID());
}
}
}