DarkflameServer/dGame/dComponents/MissionOfferComponent.cpp
David Markowitz c1e8546d48
fix: add Nexus Tower missing scripts (#1349)
add final missing scripts for nt

also fix the turnin for the breadcrumb missions not showing the completion window.

Fix another missing script

Add another script

fix include guards

Fix dirt clouds not appearing on mission accept
2023-12-22 23:53:21 -08:00

214 lines
6.5 KiB
C++

/*
* 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 "Logger.h"
#include "Game.h"
#include "MissionPrerequisites.h"
#include "eMissionState.h"
#include "CDComponentsRegistryTable.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::GetOffersMission() const {
return this->offersMission;
}
bool OfferedMission::GetAcceptsMission() const {
return this->acceptsMission;
}
//------------------------ MissionOfferComponent below ------------------------
MissionOfferComponent::MissionOfferComponent(Entity* parent, const LOT parentLot) : Component(parent) {
auto* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
auto value = compRegistryTable->GetByIDAndType(parentLot, eReplicaComponentType::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>();
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;
}
}
offeredMissions.clear();
}
void MissionOfferComponent::OnUse(Entity* originator) {
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(eReplicaComponentType::MISSION));
if (!missionComponent) {
LOG("Unable to get mission component for Entity %llu", entity->GetObjectID());
return;
}
std::vector<uint32_t> offered{};
CDMissions info{};
if (specifiedMissionId > 0 && !Mission::IsValidMission(specifiedMissionId, info)) {
return;
}
for (auto* offeredMission : this->offeredMissions) {
if (specifiedMissionId > 0) {
if (offeredMission->GetMissionId() != specifiedMissionId && !info.isRandom) {
continue;
}
}
// First, check if we already have the mission
const auto missionId = offeredMission->GetMissionId();
auto* mission = missionComponent->GetMission(missionId);
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;
}
}
// 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());
offered.push_back(missionId);
continue;
}
}
const auto canAccept = MissionPrerequisites::CanAccept(missionId, missionComponent->GetMissions());
// Mission has not yet been accepted - check the prereqs
if (!canAccept)
continue;
if (!Mission::IsValidMission(missionId, info)) {
continue;
}
const auto& randomPool = info.randomPool;
const auto isRandom = info.isRandom;
if (isRandom && randomPool.empty()) // This means the mission is part of a random pool of missions.
{
continue;
}
if (isRandom && !randomPool.empty()) {
std::istringstream stream(randomPool);
std::string token;
std::vector<uint32_t> randomMissionPool;
while (std::getline(stream, token, ',')) {
try {
const auto value = std::stoul(token);
randomMissionPool.push_back(value);
} catch (std::invalid_argument& exception) {
LOG("Failed to parse value (%s): (%s)!", token.c_str(), exception.what());
}
}
if (specifiedMissionId > 0) {
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());
return;
}
}
std::vector<uint32_t> canAcceptPool;
for (const auto sample : randomMissionPool) {
const auto state = missionComponent->GetMissionState(sample);
if (state == eMissionState::ACTIVE ||
state == eMissionState::COMPLETE_ACTIVE ||
state == eMissionState::READY_TO_COMPLETE ||
state == eMissionState::COMPLETE_READY_TO_COMPLETE ||
sample == specifiedMissionId) {
mission = missionComponent->GetMission(sample);
if (mission == nullptr || mission->IsAchievement()) {
continue;
}
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), sample, m_Parent->GetObjectID());
canAcceptPool.clear();
break;
}
if (std::find(offered.begin(), offered.end(), sample) == offered.end() && MissionPrerequisites::CanAccept(sample, missionComponent->GetMissions())) {
canAcceptPool.push_back(sample);
}
}
// If the mission is already active or we already completed one of them today
if (canAcceptPool.empty())
continue;
const auto selected = canAcceptPool[GeneralUtils::GenerateRandomNumber<int>(0, canAcceptPool.size() - 1)];
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), selected, m_Parent->GetObjectID());
} else if (
std::find(offered.begin(), offered.end(), missionId) == offered.end()
&&
(offeredMission->GetOffersMission() || offeredMission->GetAcceptsMission())) {
GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_Parent->GetObjectID());
}
}
}