mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-11-25 23:17:28 +00:00
f0b6ad89d9
* SystemAddress and destructor * move respawn logic to character comp Tested that respawn pos and rot can be set as per previously by crossing a respawn point and smashing to see if I would respawn at the new place. * Move loot cheat checking * Remove GetParentUser overload Tested completing missions control behaviors collecting life crate completing a bunch of missions using macros loading into worlds brick-by-brick placing models digging the x spot in gnarled forest can still ban and mute players cheat detection is still doing its thing flags are still set (checked with flag 45) claim codes still work (created new char, checked the lego club mail was there) * Move player constructor logic Its now at the bottom of Entity constructor. Time to remove Player * Remove Player class Removes the Player class. Tested that I can still login and see another player in Venture Explorer and logging out a few times still works as well as smashing enemies * store ptr * Update SlashCommandHandler.cpp
628 lines
17 KiB
C++
628 lines
17 KiB
C++
#include "Mission.h"
|
|
|
|
#include <ctime>
|
|
|
|
#include "CDClientManager.h"
|
|
#include "Character.h"
|
|
#include "CharacterComponent.h"
|
|
#include "LevelProgressionComponent.h"
|
|
#include "DestroyableComponent.h"
|
|
#include "EntityManager.h"
|
|
#include "Game.h"
|
|
#include "GameMessages.h"
|
|
#include "Mail.h"
|
|
#include "MissionComponent.h"
|
|
#include "eRacingTaskParam.h"
|
|
#include "Logger.h"
|
|
#include "dServer.h"
|
|
#include "dZoneManager.h"
|
|
#include "InventoryComponent.h"
|
|
#include "User.h"
|
|
#include "Database.h"
|
|
#include "WorldConfig.h"
|
|
#include "eMissionState.h"
|
|
#include "eMissionTaskType.h"
|
|
#include "eMissionLockState.h"
|
|
#include "eReplicaComponentType.h"
|
|
#include "Character.h"
|
|
|
|
#include "CDMissionEmailTable.h"
|
|
|
|
Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) {
|
|
m_MissionComponent = missionComponent;
|
|
|
|
m_Completions = 0;
|
|
|
|
m_Timestamp = 0;
|
|
|
|
m_UniqueMissionID = Game::zoneManager->GetUniqueMissionIdStartingValue();
|
|
|
|
m_Reward = 0;
|
|
|
|
m_State = eMissionState::UNKNOWN;
|
|
|
|
auto* missionsTable = CDClientManager::Instance().GetTable<CDMissionsTable>();
|
|
|
|
auto* mis = missionsTable->GetPtrByMissionID(missionId);
|
|
info = *mis;
|
|
|
|
if (mis == &CDMissionsTable::Default) {
|
|
LOG("Failed to find mission (%i)!", missionId);
|
|
|
|
return;
|
|
}
|
|
|
|
auto* tasksTable = CDClientManager::Instance().GetTable<CDMissionTasksTable>();
|
|
|
|
auto tasks = tasksTable->GetByMissionID(missionId);
|
|
|
|
for (auto i = 0U; i < tasks.size(); ++i) {
|
|
auto* info = tasks[i];
|
|
|
|
auto* task = new MissionTask(this, info, i);
|
|
|
|
m_Tasks.push_back(task);
|
|
}
|
|
}
|
|
|
|
void Mission::LoadFromXml(tinyxml2::XMLElement* element) {
|
|
// Start custom XML
|
|
if (element->Attribute("state") != nullptr) {
|
|
m_State = static_cast<eMissionState>(std::stoul(element->Attribute("state")));
|
|
}
|
|
// End custom XML
|
|
|
|
if (element->Attribute("cct") != nullptr) {
|
|
m_Completions = std::stoul(element->Attribute("cct"));
|
|
|
|
m_Timestamp = std::stoul(element->Attribute("cts"));
|
|
|
|
if (IsComplete()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
auto* task = element->FirstChildElement();
|
|
|
|
auto index = 0U;
|
|
|
|
while (task != nullptr) {
|
|
if (index >= m_Tasks.size()) {
|
|
break;
|
|
}
|
|
|
|
const auto type = m_Tasks[index]->GetType();
|
|
|
|
if (type == eMissionTaskType::COLLECTION ||
|
|
type == eMissionTaskType::VISIT_PROPERTY) {
|
|
std::vector<uint32_t> uniques;
|
|
|
|
const auto value = std::stoul(task->Attribute("v"));
|
|
|
|
m_Tasks[index]->SetProgress(value, false);
|
|
|
|
task = task->NextSiblingElement();
|
|
|
|
while (task != nullptr) {
|
|
const auto unique = std::stoul(task->Attribute("v"));
|
|
|
|
uniques.push_back(unique);
|
|
|
|
if (m_MissionComponent != nullptr && type == eMissionTaskType::COLLECTION) {
|
|
m_MissionComponent->AddCollectible(unique);
|
|
}
|
|
|
|
task = task->NextSiblingElement();
|
|
}
|
|
|
|
m_Tasks[index]->SetUnique(uniques);
|
|
|
|
m_Tasks[index]->SetProgress(uniques.size(), false);
|
|
|
|
break;
|
|
} else {
|
|
const auto value = std::stoul(task->Attribute("v"));
|
|
|
|
m_Tasks[index]->SetProgress(value, false);
|
|
|
|
task = task->NextSiblingElement();
|
|
}
|
|
|
|
index++;
|
|
}
|
|
}
|
|
|
|
void Mission::UpdateXml(tinyxml2::XMLElement* element) {
|
|
// Start custom XML
|
|
element->SetAttribute("state", static_cast<unsigned int>(m_State));
|
|
// End custom XML
|
|
|
|
element->DeleteChildren();
|
|
|
|
element->SetAttribute("id", static_cast<unsigned int>(info.id));
|
|
|
|
if (m_Completions > 0) {
|
|
element->SetAttribute("cct", static_cast<unsigned int>(m_Completions));
|
|
|
|
element->SetAttribute("cts", static_cast<unsigned int>(m_Timestamp));
|
|
|
|
if (IsComplete()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (auto* task : m_Tasks) {
|
|
if (task->GetType() == eMissionTaskType::COLLECTION ||
|
|
task->GetType() == eMissionTaskType::VISIT_PROPERTY) {
|
|
|
|
auto* child = element->GetDocument()->NewElement("sv");
|
|
|
|
child->SetAttribute("v", static_cast<unsigned int>(task->GetProgress()));
|
|
|
|
element->LinkEndChild(child);
|
|
|
|
for (auto unique : task->GetUnique()) {
|
|
auto* uniqueElement = element->GetDocument()->NewElement("sv");
|
|
|
|
uniqueElement->SetAttribute("v", static_cast<unsigned int>(unique));
|
|
|
|
element->LinkEndChild(uniqueElement);
|
|
}
|
|
|
|
break;
|
|
}
|
|
auto* child = element->GetDocument()->NewElement("sv");
|
|
|
|
child->SetAttribute("v", static_cast<unsigned int>(task->GetProgress()));
|
|
|
|
element->LinkEndChild(child);
|
|
}
|
|
}
|
|
|
|
bool Mission::IsValidMission(const uint32_t missionId) {
|
|
auto* table = CDClientManager::Instance().GetTable<CDMissionsTable>();
|
|
|
|
const auto missions = table->Query([=](const CDMissions& entry) {
|
|
return entry.id == static_cast<int>(missionId);
|
|
});
|
|
|
|
return !missions.empty();
|
|
}
|
|
|
|
bool Mission::IsValidMission(const uint32_t missionId, CDMissions& info) {
|
|
auto* table = CDClientManager::Instance().GetTable<CDMissionsTable>();
|
|
|
|
const auto missions = table->Query([=](const CDMissions& entry) {
|
|
return entry.id == static_cast<int>(missionId);
|
|
});
|
|
|
|
if (missions.empty()) {
|
|
return false;
|
|
}
|
|
|
|
info = missions[0];
|
|
|
|
return true;
|
|
}
|
|
|
|
Entity* Mission::GetAssociate() const {
|
|
return m_MissionComponent->GetParent();
|
|
}
|
|
|
|
Character* Mission::GetCharacter() const {
|
|
return GetAssociate()->GetCharacter();
|
|
}
|
|
|
|
uint32_t Mission::GetMissionId() const {
|
|
return info.id;
|
|
}
|
|
|
|
const CDMissions& Mission::GetClientInfo() const {
|
|
return info;
|
|
}
|
|
|
|
uint32_t Mission::GetCompletions() const {
|
|
return m_Completions;
|
|
}
|
|
|
|
uint32_t Mission::GetTimestamp() const {
|
|
return m_Timestamp;
|
|
}
|
|
|
|
LOT Mission::GetReward() const {
|
|
return m_Reward;
|
|
}
|
|
|
|
std::vector<MissionTask*> Mission::GetTasks() const {
|
|
return m_Tasks;
|
|
}
|
|
|
|
eMissionState Mission::GetMissionState() const {
|
|
return m_State;
|
|
}
|
|
|
|
bool Mission::IsAchievement() const {
|
|
return !info.isMission;
|
|
}
|
|
|
|
bool Mission::IsMission() const {
|
|
return info.isMission;
|
|
}
|
|
|
|
bool Mission::IsRepeatable() const {
|
|
return info.repeatable;
|
|
}
|
|
|
|
bool Mission::IsComplete() const {
|
|
return m_State == eMissionState::COMPLETE;
|
|
}
|
|
|
|
bool Mission::IsActive() const {
|
|
return m_State == eMissionState::ACTIVE || m_State == eMissionState::COMPLETE_ACTIVE;
|
|
}
|
|
|
|
void Mission::MakeActive() {
|
|
SetMissionState(m_Completions == 0 ? eMissionState::ACTIVE : eMissionState::COMPLETE_ACTIVE);
|
|
}
|
|
|
|
bool Mission::IsReadyToComplete() const {
|
|
return m_State == eMissionState::READY_TO_COMPLETE || m_State == eMissionState::COMPLETE_READY_TO_COMPLETE;
|
|
}
|
|
|
|
void Mission::MakeReadyToComplete() {
|
|
SetMissionState(m_Completions == 0 ? eMissionState::READY_TO_COMPLETE : eMissionState::COMPLETE_READY_TO_COMPLETE);
|
|
}
|
|
|
|
bool Mission::IsAvalible() const {
|
|
return m_State == eMissionState::AVAILABLE || m_State == eMissionState::COMPLETE_AVAILABLE;
|
|
}
|
|
|
|
bool Mission::IsFetchMission() const {
|
|
return m_Tasks.size() == 1 && m_Tasks[0]->GetType() == eMissionTaskType::TALK_TO_NPC;
|
|
}
|
|
|
|
void Mission::MakeAvalible() {
|
|
SetMissionState(m_Completions == 0 ? eMissionState::AVAILABLE : eMissionState::COMPLETE_AVAILABLE);
|
|
}
|
|
|
|
void Mission::Accept() {
|
|
SetMissionTypeState(eMissionLockState::NEW, info.defined_type, info.defined_subtype);
|
|
|
|
SetMissionState(m_Completions > 0 ? eMissionState::COMPLETE_ACTIVE : eMissionState::ACTIVE);
|
|
|
|
Catchup();
|
|
}
|
|
|
|
void Mission::Complete(const bool yieldRewards) {
|
|
if (m_State != eMissionState::ACTIVE && m_State != eMissionState::COMPLETE_ACTIVE) {
|
|
// If we are accepting a mission here there is no point to giving it a unique ID since we just complete it immediately.
|
|
Accept();
|
|
}
|
|
|
|
for (auto* task : m_Tasks) {
|
|
task->Complete();
|
|
}
|
|
|
|
SetMissionState(eMissionState::REWARDING, true);
|
|
|
|
if (yieldRewards) {
|
|
YieldRewards();
|
|
}
|
|
|
|
SetMissionState(eMissionState::COMPLETE);
|
|
|
|
m_Completions++;
|
|
|
|
m_Timestamp = std::time(nullptr);
|
|
|
|
auto* entity = GetAssociate();
|
|
|
|
if (entity == nullptr) {
|
|
return;
|
|
}
|
|
|
|
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
|
if (characterComponent != nullptr) {
|
|
characterComponent->TrackMissionCompletion(!info.isMission);
|
|
}
|
|
|
|
auto* missionComponent = entity->GetComponent<MissionComponent>();
|
|
|
|
missionComponent->Progress(eMissionTaskType::META, info.id);
|
|
|
|
missionComponent->Progress(eMissionTaskType::RACING, info.id, static_cast<LWOOBJID>(eRacingTaskParam::COMPLETE_ANY_RACING_TASK));
|
|
|
|
missionComponent->Progress(eMissionTaskType::RACING, info.id, static_cast<LWOOBJID>(eRacingTaskParam::COMPLETE_TRACK_TASKS));
|
|
|
|
auto* missionEmailTable = CDClientManager::Instance().GetTable<CDMissionEmailTable>();
|
|
|
|
const auto missionId = GetMissionId();
|
|
|
|
const auto missionEmails = missionEmailTable->Query([missionId](const CDMissionEmail& entry) {
|
|
return entry.missionID == missionId;
|
|
});
|
|
|
|
for (const auto& email : missionEmails) {
|
|
const auto missionEmailBase = "MissionEmail_" + std::to_string(email.ID) + "_";
|
|
|
|
if (email.messageType == 1) {
|
|
const auto subject = "%[" + missionEmailBase + "subjectText]";
|
|
const auto body = "%[" + missionEmailBase + "bodyText]";
|
|
const auto sender = "%[" + missionEmailBase + "senderName]";
|
|
|
|
Mail::SendMail(LWOOBJID_EMPTY, sender, GetAssociate(), subject, body, email.attachmentLOT, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Mission::CheckCompletion() {
|
|
for (auto* task : m_Tasks) {
|
|
if (!task->IsComplete()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (IsAchievement()) {
|
|
Complete();
|
|
|
|
return;
|
|
}
|
|
|
|
MakeReadyToComplete();
|
|
}
|
|
|
|
void Mission::Catchup() {
|
|
auto* entity = GetAssociate();
|
|
|
|
auto* inventory = static_cast<InventoryComponent*>(entity->GetComponent(eReplicaComponentType::INVENTORY));
|
|
|
|
for (auto* task : m_Tasks) {
|
|
const auto type = task->GetType();
|
|
|
|
if (type == eMissionTaskType::GATHER) {
|
|
for (auto target : task->GetAllTargets()) {
|
|
const auto count = inventory->GetLotCountNonTransfer(target);
|
|
|
|
for (auto i = 0U; i < count; ++i) {
|
|
task->Progress(target);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type == eMissionTaskType::PLAYER_FLAG) {
|
|
for (int32_t target : task->GetAllTargets()) {
|
|
const auto flag = GetCharacter()->GetPlayerFlag(target);
|
|
|
|
if (!flag) {
|
|
continue;
|
|
}
|
|
|
|
task->Progress(target);
|
|
|
|
if (task->IsComplete()) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Mission::YieldRewards() {
|
|
auto* entity = GetAssociate();
|
|
|
|
if (entity == nullptr) {
|
|
return;
|
|
}
|
|
|
|
auto* character = GetCharacter();
|
|
|
|
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
|
auto* levelComponent = entity->GetComponent<LevelProgressionComponent>();
|
|
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
|
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
|
auto* missionComponent = entity->GetComponent<MissionComponent>();
|
|
|
|
// Remove mission items
|
|
for (auto* task : m_Tasks) {
|
|
if (task->GetType() != eMissionTaskType::GATHER) {
|
|
continue;
|
|
}
|
|
|
|
const auto& param = task->GetParameters();
|
|
|
|
if (param.empty() || (param[0] & 1) == 0) // Should items be removed?
|
|
{
|
|
for (const auto target : task->GetAllTargets()) {
|
|
// This is how live did it. ONLY remove item collection items from the items and hidden inventories and none of the others.
|
|
inventoryComponent->RemoveItem(target, task->GetClientInfo().targetValue, eInventoryType::ITEMS);
|
|
inventoryComponent->RemoveItem(target, task->GetClientInfo().targetValue, eInventoryType::QUEST);
|
|
|
|
missionComponent->Progress(eMissionTaskType::GATHER, target, LWOOBJID_EMPTY, "", -task->GetClientInfo().targetValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t coinsToSend = 0;
|
|
if (info.LegoScore > 0) {
|
|
eLootSourceType lootSource = info.isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT;
|
|
if (levelComponent->GetLevel() >= Game::zoneManager->GetWorldConfig()->levelCap) {
|
|
// Since the character is at the level cap we reward them with coins instead of UScore.
|
|
coinsToSend += info.LegoScore * Game::zoneManager->GetWorldConfig()->levelCapCurrencyConversion;
|
|
} else {
|
|
characterComponent->SetUScore(characterComponent->GetUScore() + info.LegoScore);
|
|
GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), info.LegoScore, lootSource);
|
|
}
|
|
}
|
|
|
|
if (m_Completions > 0) {
|
|
std::vector<std::pair<LOT, uint32_t>> items;
|
|
|
|
items.emplace_back(info.reward_item1_repeatable, info.reward_item1_repeat_count);
|
|
items.emplace_back(info.reward_item2_repeatable, info.reward_item2_repeat_count);
|
|
items.emplace_back(info.reward_item3_repeatable, info.reward_item3_repeat_count);
|
|
items.emplace_back(info.reward_item4_repeatable, info.reward_item4_repeat_count);
|
|
|
|
for (const auto& pair : items) {
|
|
// Some missions reward zero of an item and so they must be allowed through this clause,
|
|
// hence pair.second < 0 instead of pair.second <= 0.
|
|
if (pair.second < 0 || (m_Reward > 0 && pair.first != m_Reward)) {
|
|
continue;
|
|
}
|
|
|
|
// If a mission rewards zero of an item, make it reward 1.
|
|
auto count = pair.second > 0 ? pair.second : 1;
|
|
|
|
// Sanity check, 6 is the max any mission yields
|
|
if (count > 6) {
|
|
count = 0;
|
|
}
|
|
|
|
inventoryComponent->AddItem(pair.first, count, IsMission() ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT);
|
|
}
|
|
|
|
if (info.reward_currency_repeatable > 0 || coinsToSend > 0) {
|
|
eLootSourceType lootSource = info.isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT;
|
|
character->SetCoins(character->GetCoins() + info.reward_currency_repeatable + coinsToSend, lootSource);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
std::vector<std::pair<LOT, int32_t>> items;
|
|
|
|
items.emplace_back(info.reward_item1, info.reward_item1_count);
|
|
items.emplace_back(info.reward_item2, info.reward_item2_count);
|
|
items.emplace_back(info.reward_item3, info.reward_item3_count);
|
|
items.emplace_back(info.reward_item4, info.reward_item4_count);
|
|
|
|
for (const auto& pair : items) {
|
|
// Some missions reward zero of an item and so they must be allowed through this clause,
|
|
// hence pair.second < 0 instead of pair.second <= 0.
|
|
if (pair.second < 0 || (m_Reward > 0 && pair.first != m_Reward)) {
|
|
continue;
|
|
}
|
|
|
|
// If a mission rewards zero of an item, make it reward 1.
|
|
auto count = pair.second > 0 ? pair.second : 1;
|
|
|
|
// Sanity check, 6 is the max any mission yields
|
|
if (count > 6) {
|
|
count = 0;
|
|
}
|
|
|
|
inventoryComponent->AddItem(pair.first, count, IsMission() ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT);
|
|
}
|
|
|
|
if (info.reward_currency > 0 || coinsToSend > 0) {
|
|
eLootSourceType lootSource = info.isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT;
|
|
character->SetCoins(character->GetCoins() + info.reward_currency + coinsToSend, lootSource);
|
|
}
|
|
|
|
if (info.reward_maxinventory > 0) {
|
|
auto* inventory = inventoryComponent->GetInventory(ITEMS);
|
|
|
|
inventory->SetSize(inventory->GetSize() + info.reward_maxinventory);
|
|
}
|
|
|
|
if (info.reward_bankinventory > 0) {
|
|
auto* inventory = inventoryComponent->GetInventory(eInventoryType::VAULT_ITEMS);
|
|
auto modelInventory = inventoryComponent->GetInventory(eInventoryType::VAULT_MODELS);
|
|
|
|
inventory->SetSize(inventory->GetSize() + info.reward_bankinventory);
|
|
modelInventory->SetSize(modelInventory->GetSize() + info.reward_bankinventory);
|
|
}
|
|
|
|
if (info.reward_reputation > 0) {
|
|
missionComponent->Progress(eMissionTaskType::EARN_REPUTATION, 0, 0L, "", info.reward_reputation);
|
|
auto character = entity->GetComponent<CharacterComponent>();
|
|
if (character) {
|
|
character->SetReputation(character->GetReputation() + info.reward_reputation);
|
|
GameMessages::SendUpdateReputation(entity->GetObjectID(), character->GetReputation(), entity->GetSystemAddress());
|
|
}
|
|
}
|
|
|
|
if (info.reward_maxhealth > 0) {
|
|
destroyableComponent->SetMaxHealth(destroyableComponent->GetMaxHealth() + static_cast<float>(info.reward_maxhealth), true);
|
|
}
|
|
|
|
if (info.reward_maximagination > 0) {
|
|
destroyableComponent->SetMaxImagination(destroyableComponent->GetMaxImagination() + static_cast<float>(info.reward_maximagination), true);
|
|
}
|
|
|
|
Game::entityManager->SerializeEntity(entity);
|
|
|
|
if (info.reward_emote > 0) {
|
|
character->UnlockEmote(info.reward_emote);
|
|
}
|
|
|
|
if (info.reward_emote2 > 0) {
|
|
character->UnlockEmote(info.reward_emote2);
|
|
}
|
|
|
|
if (info.reward_emote3 > 0) {
|
|
character->UnlockEmote(info.reward_emote3);
|
|
}
|
|
|
|
if (info.reward_emote4 > 0) {
|
|
character->UnlockEmote(info.reward_emote4);
|
|
}
|
|
}
|
|
|
|
void Mission::Progress(eMissionTaskType type, int32_t value, LWOOBJID associate, const std::string& targets, int32_t count) {
|
|
const auto isRemoval = count < 0;
|
|
|
|
if (isRemoval && (IsComplete() || IsAchievement())) {
|
|
return;
|
|
}
|
|
|
|
for (auto* task : m_Tasks) {
|
|
if (task->IsComplete() && !isRemoval) {
|
|
continue;
|
|
}
|
|
|
|
if (task->GetType() != type) {
|
|
continue;
|
|
}
|
|
|
|
if (isRemoval && !task->InAllTargets(value)) {
|
|
continue;
|
|
}
|
|
|
|
task->Progress(value, associate, targets, count);
|
|
}
|
|
}
|
|
|
|
void Mission::SetMissionState(const eMissionState state, const bool sendingRewards) {
|
|
this->m_State = state;
|
|
|
|
auto* entity = GetAssociate();
|
|
|
|
if (entity == nullptr) {
|
|
return;
|
|
}
|
|
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
|
if (!characterComponent) return;
|
|
|
|
GameMessages::SendNotifyMission(entity, characterComponent->GetSystemAddress(), info.id, static_cast<int>(state), sendingRewards);
|
|
}
|
|
|
|
void Mission::SetMissionTypeState(eMissionLockState state, const std::string& type, const std::string& subType) {
|
|
// TODO
|
|
}
|
|
|
|
void Mission::SetCompletions(const uint32_t value) {
|
|
m_Completions = value;
|
|
}
|
|
|
|
void Mission::SetReward(const LOT lot) {
|
|
m_Reward = lot;
|
|
}
|
|
|
|
Mission::~Mission() {
|
|
for (auto* task : m_Tasks) {
|
|
delete task;
|
|
}
|
|
|
|
m_Tasks.clear();
|
|
}
|