mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
fix: check the mission state before completing it from a dialogue
MissionDialogueOK accepted or completed the mission for whichever object the message named as the responder. The responder now has to be the player that sent the message, the object the dialogue belongs to has to give or take the mission (on its own or through a random mission's pool), a mission is only accepted when the player can accept it and only completed when it is ready to complete. The object's script runs after these checks. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
@@ -136,6 +136,21 @@ std::vector<uint32_t> MissionOfferComponent::ParseRandomPool(const std::string&
|
||||
return pool;
|
||||
}
|
||||
|
||||
bool MissionOfferComponent::HasMission(const uint32_t missionId) const {
|
||||
for (const auto& offeredMission : this->offeredMissions) {
|
||||
if (offeredMission.GetMissionId() == missionId) return true;
|
||||
|
||||
// A random mission stands for the missions in its pool
|
||||
CDMissions info{};
|
||||
if (!Mission::IsValidMission(offeredMission.GetMissionId(), info) || !info.isRandom) continue;
|
||||
|
||||
const auto randomMissionPool = ParseRandomPool(info.randomPool);
|
||||
if (std::ranges::find(randomMissionPool, missionId) != randomMissionPool.end()) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const OfferedMission* MissionOfferComponent::GetOfferedMission(const uint32_t missionID) const {
|
||||
const auto offered = std::ranges::find_if(offeredMissions, [missionID](const OfferedMission& other) { return other.GetMissionId() == missionID; });
|
||||
return offered == offeredMissions.end() ? nullptr : &*offered;
|
||||
|
||||
@@ -96,6 +96,12 @@ public:
|
||||
*/
|
||||
void OfferMissions(Entity* entity, uint32_t specifiedMissionId = 0);
|
||||
|
||||
/**
|
||||
* Returns whether this entity offers or accepts a mission, on its own or as one of a random mission's pool
|
||||
* @param missionId the mission to look for
|
||||
*/
|
||||
bool HasMission(uint32_t missionId) const;
|
||||
|
||||
// RequestLinkedMission after a mission window: offers the next mission of this giver, if there is a new one
|
||||
void OfferLinkedMission(Entity* entity, uint32_t missionID);
|
||||
|
||||
|
||||
@@ -143,13 +143,12 @@ namespace GameMessages {
|
||||
void MissionDialogueOK::Handle(Entity& entityRef, const SystemAddress& sysAddr) {
|
||||
Entity* entity = &entityRef;
|
||||
Entity* player = Game::entityManager->GetEntity(responder);
|
||||
if (!player) {
|
||||
LOG("MissionDialogueOK for mission %i from unknown responder %llu", missionID, responder);
|
||||
// A player answers a mission dialogue for themselves
|
||||
if (!player || !player->IsPlayer() || player->GetSystemAddress() != sysAddr) {
|
||||
LOG("Responder %llu of MissionDialogueOK for mission %i is not the player that sent it", responder, missionID);
|
||||
return;
|
||||
}
|
||||
|
||||
entity->GetScript()->OnMissionDialogueOK(entity, player, missionID, iMissionState);
|
||||
|
||||
// Get the player's mission component
|
||||
MissionComponent* missionComponent = static_cast<MissionComponent*>(player->GetComponent(eReplicaComponentType::MISSION));
|
||||
if (!missionComponent) {
|
||||
@@ -157,9 +156,35 @@ namespace GameMessages {
|
||||
return;
|
||||
}
|
||||
|
||||
if (iMissionState == eMissionState::AVAILABLE || iMissionState == eMissionState::COMPLETE_AVAILABLE) {
|
||||
// The mission has to be one the object the dialogue belongs to gives or takes
|
||||
const auto* const missionOfferComponent = entity->GetComponent<MissionOfferComponent>();
|
||||
if (!missionOfferComponent || !missionOfferComponent->HasMission(missionID)) {
|
||||
LOG("Object %llu (LOT %i) has no mission %i for player %llu to answer", entity->GetObjectID(), entity->GetLOT(), missionID, player->GetObjectID());
|
||||
return;
|
||||
}
|
||||
|
||||
const bool isAccepting = iMissionState == eMissionState::AVAILABLE || iMissionState == eMissionState::COMPLETE_AVAILABLE;
|
||||
const bool isCompleting = iMissionState == eMissionState::READY_TO_COMPLETE || iMissionState == eMissionState::COMPLETE_READY_TO_COMPLETE;
|
||||
|
||||
if (isAccepting && !missionComponent->CanAccept(missionID)) {
|
||||
LOG("Player %llu tried to accept mission %i, which they can not accept", player->GetObjectID(), missionID);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isCompleting) {
|
||||
// Only a mission with all of its tasks done can be handed in
|
||||
const auto* const readyMission = missionComponent->GetMission(missionID);
|
||||
if (!readyMission || !readyMission->IsReadyToComplete()) {
|
||||
LOG("Player %llu tried to hand in mission %i, which is not ready to complete", player->GetObjectID(), missionID);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
entity->GetScript()->OnMissionDialogueOK(entity, player, missionID, iMissionState);
|
||||
|
||||
if (isAccepting) {
|
||||
missionComponent->AcceptMission(missionID);
|
||||
} else if (iMissionState == eMissionState::READY_TO_COMPLETE || iMissionState == eMissionState::COMPLETE_READY_TO_COMPLETE) {
|
||||
} else if (isCompleting) {
|
||||
// LWOMissionComponent::MissionWindowOK (0x00d501f0) only turns a mission in when every reward fits
|
||||
// (CanReceiveAllRewards, with the reward the player picked)
|
||||
const auto* const mission = missionComponent->GetMission(missionID);
|
||||
|
||||
Reference in New Issue
Block a user