2021-12-05 17:54:36 +00:00
|
|
|
/*
|
|
|
|
* Darkflame Universe
|
|
|
|
* Copyright 2019
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sstream>
|
|
|
|
#include "MissionOfferComponent.h"
|
|
|
|
#include "CDClientManager.h"
|
|
|
|
#include "CDMissionsTable.h"
|
|
|
|
#include "CDMissionNPCComponentTable.h"
|
|
|
|
#include "GameMessages.h"
|
|
|
|
#include "Entity.h"
|
|
|
|
#include "MissionComponent.h"
|
|
|
|
#include "dLogger.h"
|
|
|
|
#include "Game.h"
|
|
|
|
#include "MissionPrerequisites.h"
|
|
|
|
|
|
|
|
OfferedMission::OfferedMission(const uint32_t missionId, const bool offersMission, const bool acceptsMission) {
|
|
|
|
this->missionId = missionId;
|
|
|
|
this->offersMission = offersMission;
|
|
|
|
this->acceptsMission = acceptsMission;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t OfferedMission::GetMissionId() const {
|
|
|
|
return this->missionId;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OfferedMission::GetOfferMission() const {
|
|
|
|
return this->offersMission;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool OfferedMission::GetAcceptMission() const {
|
|
|
|
return this->acceptsMission;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------ MissionOfferComponent below ------------------------
|
|
|
|
|
|
|
|
MissionOfferComponent::MissionOfferComponent(Entity* parent, const LOT parentLot) : Component(parent) {
|
|
|
|
auto* compRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry");
|
|
|
|
|
|
|
|
auto value = compRegistryTable->GetByIDAndType(parentLot, COMPONENT_TYPE_MISSION_OFFER, -1);
|
|
|
|
|
|
|
|
if (value != -1) {
|
|
|
|
const uint32_t componentId = value;
|
|
|
|
|
|
|
|
// Now lookup the missions in the MissionNPCComponent table
|
|
|
|
auto* missionNpcComponentTable = CDClientManager::Instance()->GetTable<CDMissionNPCComponentTable>("MissionNPCComponent");
|
2022-07-25 02:26:51 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
auto missions = missionNpcComponentTable->Query([=](const CDMissionNPCComponent& entry) {
|
|
|
|
return entry.id == static_cast<unsigned>(componentId);
|
|
|
|
});
|
|
|
|
|
|
|
|
for (auto& mission : missions) {
|
|
|
|
auto* offeredMission = new OfferedMission(mission.missionID, mission.offersMission, mission.acceptsMission);
|
|
|
|
this->offeredMissions.push_back(offeredMission);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MissionOfferComponent::~MissionOfferComponent() {
|
|
|
|
for (auto* mission : this->offeredMissions) {
|
|
|
|
if (mission) {
|
|
|
|
delete mission;
|
|
|
|
mission = nullptr;
|
|
|
|
}
|
|
|
|
}
|
2022-07-25 02:26:51 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
offeredMissions.clear();
|
|
|
|
}
|
|
|
|
|
2022-07-25 02:26:51 +00:00
|
|
|
void MissionOfferComponent::OnUse(Entity* originator) {
|
2021-12-05 17:54:36 +00:00
|
|
|
OfferMissions(originator);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifiedMissionId) {
|
|
|
|
// First, get the entity's MissionComponent. If there is not one, then we cannot offer missions to this entity.
|
|
|
|
auto* missionComponent = static_cast<MissionComponent*>(entity->GetComponent(COMPONENT_TYPE_MISSION));
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
if (!missionComponent) {
|
2022-07-25 02:26:51 +00:00
|
|
|
Game::logger->Log("MissionOfferComponent", "Unable to get mission component for Entity %llu", entity->GetObjectID());
|
2021-12-05 17:54:36 +00:00
|
|
|
return;
|
|
|
|
}
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
std::vector<uint32_t> offered{};
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
CDMissions info{};
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
if (specifiedMissionId > 0 && !Mission::IsValidMission(specifiedMissionId, info)) {
|
|
|
|
return;
|
|
|
|
}
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
for (auto* offeredMission : this->offeredMissions) {
|
|
|
|
if (specifiedMissionId > 0) {
|
|
|
|
if (offeredMission->GetMissionId() != specifiedMissionId && !info.isRandom) {
|
|
|
|
continue;
|
2022-07-28 13:39:57 +00:00
|
|
|
}
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
// First, check if we already have the mission
|
|
|
|
const auto missionId = offeredMission->GetMissionId();
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
auto* mission = missionComponent->GetMission(missionId);
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
if (mission != nullptr) {
|
|
|
|
if (specifiedMissionId <= 0) {
|
|
|
|
// Handles the odd case where the offer object should not display the mission again
|
|
|
|
if (!mission->IsComplete() && mission->GetClientInfo().offer_objectID == m_Parent->GetLOT() && mission->GetClientInfo().target_objectID != m_Parent->GetLOT() && mission->IsFetchMission()) {
|
|
|
|
continue;
|
2022-07-28 13:39:57 +00:00
|
|
|
}
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
// 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());
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
offered.push_back(missionId);
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
continue;
|
2022-07-28 13:39:57 +00:00
|
|
|
}
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
const auto canAccept = MissionPrerequisites::CanAccept(missionId, missionComponent->GetMissions());
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
// Mission has not yet been accepted - check the prereqs
|
|
|
|
if (!canAccept)
|
|
|
|
continue;
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
if (!Mission::IsValidMission(missionId, info)) {
|
|
|
|
continue;
|
|
|
|
}
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
const auto& randomPool = info.randomPool;
|
|
|
|
const auto isRandom = info.isRandom;
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
if (isRandom && randomPool.empty()) // This means the mission is part of a random pool of missions.
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
if (isRandom && !randomPool.empty()) {
|
|
|
|
std::istringstream stream(randomPool);
|
|
|
|
std::string token;
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
std::vector<uint32_t> randomMissionPool;
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
while (std::getline(stream, token, ',')) {
|
|
|
|
try {
|
|
|
|
const auto value = std::stoul(token);
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
randomMissionPool.push_back(value);
|
|
|
|
} catch (std::invalid_argument& exception) {
|
2022-07-25 02:26:51 +00:00
|
|
|
Game::logger->Log("MissionOfferComponent", "Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
|
2022-07-28 13:39:57 +00:00
|
|
|
}
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
if (specifiedMissionId > 0) {
|
|
|
|
const auto& iter = std::find(randomMissionPool.begin(), randomMissionPool.end(), specifiedMissionId);
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
if (iter != randomMissionPool.end() && MissionPrerequisites::CanAccept(specifiedMissionId, missionComponent->GetMissions())) {
|
|
|
|
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), specifiedMissionId, m_Parent->GetObjectID());
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
return;
|
2022-07-28 13:39:57 +00:00
|
|
|
}
|
2021-12-05 17:54:36 +00:00
|
|
|
}
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
std::vector<uint32_t> canAcceptPool;
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
for (const auto sample : randomMissionPool) {
|
|
|
|
const auto state = missionComponent->GetMissionState(sample);
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
if (state == MissionState::MISSION_STATE_ACTIVE ||
|
|
|
|
state == MissionState::MISSION_STATE_COMPLETE_ACTIVE ||
|
|
|
|
state == MissionState::MISSION_STATE_READY_TO_COMPLETE ||
|
|
|
|
state == MissionState::MISSION_STATE_COMPLETE_READY_TO_COMPLETE ||
|
|
|
|
sample == specifiedMissionId) {
|
|
|
|
mission = missionComponent->GetMission(sample);
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
if (mission == nullptr || mission->IsAchievement()) {
|
|
|
|
continue;
|
|
|
|
}
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), sample, m_Parent->GetObjectID());
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
canAcceptPool.clear();
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
break;
|
|
|
|
}
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
if (std::find(offered.begin(), offered.end(), sample) == offered.end() && MissionPrerequisites::CanAccept(sample, missionComponent->GetMissions())) {
|
|
|
|
canAcceptPool.push_back(sample);
|
|
|
|
}
|
|
|
|
}
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
// If the mission is already active or we already completed one of them today
|
|
|
|
if (canAcceptPool.empty())
|
|
|
|
continue;
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
const auto selected = canAcceptPool[GeneralUtils::GenerateRandomNumber<int>(0, canAcceptPool.size() - 1)];
|
2022-07-28 13:39:57 +00:00
|
|
|
|
2021-12-05 17:54:36 +00:00
|
|
|
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), selected, m_Parent->GetObjectID());
|
|
|
|
} else if (std::find(offered.begin(), offered.end(), missionId) == offered.end() && offeredMission->GetOfferMission()) {
|
|
|
|
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_Parent->GetObjectID());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|