Files
DarkflameServer/dGame/dMission/MissionPrerequisites.h
Aaron Kimbrell c1079fc20c fix(missions): prerequisites read as the client reads them
The client checks Missions.prereqMissionID in
LWOMissionComponent::getMissionState (0x00cd7400): text without any of
!&^;,|(): is one mission ID (met when 0 or ever completed); anything else
goes through its expression reader (sub_00a32980), left to right with no
precedence, & , ; and, | or, ^ xor, ! ~ not, ( ) groups, and/or skipping
the rest once the result is known. Each operand (sub_00c90a80): "id" is
met once the mission was ever completed; "id:state" when the client's
state mask of that mission equals state.

DLU read the text right to left ignoring groups, so
"(509|229)&(812|813|814|815)" (mission 781) was met by 509 alone; a plain
ID needed the mission's state to be 8, so a repeatable being repeated
blocked the missions after it (1870 after 1869); and id:state compared the
mission's own state. GetClientMissionState moves to MissionPrerequisites
so operands can ask for the mask.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 15:48:44 -05:00

61 lines
2.8 KiB
C++

#pragma once
#include <cstdint>
#include <functional>
#include <string>
#include <string_view>
#include <unordered_map>
#include "Mission.h"
/**
* Utility class for checking whether or not a mission can be accepted using its prerequisites
*/
class MissionPrerequisites final
{
public:
/**
* Checks whether or not the mission identified by the specified ID can be accepted based on the mission inventory passed.
* Also performs checks for daily missions (e.g. if the time out is valid).
* @param missionId the mission ID to check prerequisites for
* @param missions the mission inventory to check the prerequisites against
* @return whether or not the mission identified by the specified ID can be accepted
*/
static bool CanAccept(uint32_t missionId, const std::unordered_map<uint32_t, Mission*>& missions);
/**
* Checks the prerequisites (Missions.prereqMissionID) of a mission, as the client
* (LWOMissionComponent::getMissionState 0x00cd7400): an expression is evaluated by EvaluateExpression with
* IsOperandMet for each operand; text without any of !&^;,|(): is one mission ID, met when 0 or once ever completed.
* @param missionId the mission ID to check prerequisites for
* @param missions the mission inventory to check the prerequisites against
* @return whether or not the prerequisites of the mission identified by the specified ID are met
*/
static bool CheckPrerequisites(uint32_t missionId, const std::unordered_map<uint32_t, Mission*>& missions);
/**
* The client's state bit mask for a mission (LWOMissionComponent::getMissionState 0x00cd7400): 8 once ever
* completed; in progress, its state (with 8 if completed before); else 1 when never completed (or repeatable) and
* the prerequisites are met; 0 for no mission.
*/
static int32_t GetClientMissionState(uint32_t missionId, const std::unordered_map<uint32_t, Mission*>& missions);
/**
* One operand of a prerequisite expression (sub_00c90a80): "id" is met once the mission was ever completed;
* "id:state" when the client's state mask of that mission is state (a mission naming itself counts as state 0).
*/
static bool IsOperandMet(std::string_view operand, uint32_t missionId, const std::unordered_map<uint32_t, Mission*>& missions);
/**
* The client's boolean expression evaluator (sub_00a32980): read left to right, no precedence; & , ; and, | or,
* ^ xor, ! ~ negate the next operand, ( ) group. And/or skip their right side once the result is known; an operand
* or group with no operator before it (other than the first) is ignored.
*/
static bool EvaluateExpression(std::string_view expression, const std::function<bool(std::string_view)>& isMet);
private:
/**
* Cache of the prerequisite text of each mission
*/
static std::unordered_map<uint32_t, std::string> expressions;
};