DarkflameServer/dGame/dMission/MissionPrerequisites.cpp
David Markowitz dc29f5962d
Move CDClientManager to be a namespace (#1431)
Tested that worlds still load data as expected.  Had no use being a singleton anyways.
2024-02-08 23:40:43 -06:00

179 lines
3.7 KiB
C++

#include "MissionPrerequisites.h"
#include <sstream>
#include <ctime>
#include "CDClientManager.h"
#include "Logger.h"
PrerequisiteExpression::PrerequisiteExpression(const std::string& str) {
std::stringstream a;
std::stringstream b;
std::stringstream s;
auto bor = false;
auto sub = false;
auto done = false;
for (auto i = 0u; i < str.size(); ++i) {
if (done) {
break;
}
const auto character = str[i];
switch (character) {
case '|':
bor = true;
b << str.substr(i + 1);
done = true;
break;
case ' ':
case ')':
break;
case ',':
case '&':
case '(':
b << str.substr(i + 1);
done = true;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (sub) {
s << character;
} else {
a << character;
}
break;
case ':':
sub = true;
break;
default:
break;
}
}
this->m_or = bor;
const auto aString = a.str();
if (!aString.empty()) {
this->a = std::stoul(a.str());
} else {
this->a = 0;
}
const auto subString = s.str();
if (!subString.empty()) {
this->sub = std::stoul(s.str());
} else {
this->sub = 0;
}
const auto bString = b.str();
if (!bString.empty()) {
this->b = new PrerequisiteExpression(bString);
} else {
this->b = nullptr;
}
}
bool PrerequisiteExpression::Execute(const std::unordered_map<uint32_t, Mission*>& missions) const {
auto a = this->a == 0;
auto b = this->b == nullptr;
if (!a) {
const auto index = missions.find(this->a);
if (index != missions.end()) {
const auto* mission = index->second;
if (this->sub != 0) {
// Special case for one Wisp Lee repeatable mission.
a = mission->GetMissionState() == static_cast<eMissionState>(this->sub);
} else if (mission->IsComplete()) {
a = true;
}
}
}
if (!b) {
b = this->b->Execute(missions);
}
if (this->m_or) {
return a || b;
}
return a && b;
}
PrerequisiteExpression::~PrerequisiteExpression() {
delete b;
}
bool MissionPrerequisites::CanAccept(const uint32_t missionId, const std::unordered_map<uint32_t, Mission*>& missions) {
const auto& missionIndex = missions.find(missionId);
if (missionIndex != missions.end()) {
auto* mission = missionIndex->second;
const auto& info = mission->GetClientInfo();
if (info.repeatable) {
const auto prerequisitesMet = CheckPrerequisites(missionId, missions);
// Checked by client
const time_t time = std::time(nullptr);
const time_t lock = mission->GetTimestamp() + info.cooldownTime * 60;
// If there's no time limit, just check the prerequisites, otherwise make sure both conditions are met
return (info.cooldownTime == -1 ? prerequisitesMet : (lock - time < 0)) && prerequisitesMet;
}
// Mission is already accepted and cannot be repeatedly accepted
return false;
}
// Mission is not yet accepted, check the prerequisites
return CheckPrerequisites(missionId, missions);
}
bool MissionPrerequisites::CheckPrerequisites(uint32_t missionId, const std::unordered_map<uint32_t, Mission*>& missions) {
const auto& index = expressions.find(missionId);
if (index != expressions.end()) {
return index->second->Execute(missions);
}
auto* missionsTable = CDClientManager::GetTable<CDMissionsTable>();
const auto missionEntries = missionsTable->Query([=](const CDMissions& entry) {
return entry.id == static_cast<int>(missionId);
});
if (missionEntries.empty())
return false;
auto* expression = new PrerequisiteExpression(missionEntries[0].prereqMissionID);
expressions.insert_or_assign(missionId, expression);
return expression->Execute(missions);
}
std::unordered_map<uint32_t, PrerequisiteExpression*> MissionPrerequisites::expressions = {};