DarkflameServer/dScripts/02_server/Map/General/PetDigServer.cpp
2023-11-24 23:56:33 -06:00

259 lines
8.5 KiB
C++

#include "dZoneManager.h"
#include "PetDigServer.h"
#include "DestroyableComponent.h"
#include "MissionComponent.h"
#include "EntityManager.h"
#include "Character.h"
#include "PetComponent.h"
#include "User.h"
#include "eMissionState.h"
std::vector<LWOOBJID> PetDigServer::treasures{};
const DigInfo PetDigServer::defaultDigInfo = DigInfo{ 3495, -1, -1, false, false, false, false };
/**
* Summary of all the special treasure behaviors, indexed by their lot
*/
const std::map<LOT, DigInfo> PetDigServer::digInfoMap{
// Regular treasures
{3495, defaultDigInfo},
// Pet cove treasure
{7612, DigInfo { 7612, -1, -1, false, false, false, false }},
// Gnarled Forest flag treasure
{7410, DigInfo { 7410, -1, -1, false, true, false, false }},
// Gnarled Forest crab treasure
{9308, DigInfo { 9308, 7694, -1, false, false, false, false }},
// Avant Gardens mission treasure
{9307, DigInfo { 9307, -1, -1, false, true, false, true }},
// Avant Gardens bouncer treasure
{7559, DigInfo { 7559, -1, -1, false, false, true, false }},
// Crux Prime dragon treasure
{13098, DigInfo { 13098, 13067, 1298, false, false, false, false }},
// Bone treasure (can only be digged using the dragon)
{12192, DigInfo { 12192, -1, -1, true, false, false, false }},
};
void PetDigServer::OnStartup(Entity* self) {
treasures.push_back(self->GetObjectID());
const auto digInfoIterator = digInfoMap.find(self->GetLOT());
const auto digInfo = digInfoIterator != digInfoMap.end() ? digInfoIterator->second : defaultDigInfo;
// Reset any bouncers that might've been created by the previous dig
if (digInfo.bouncer) {
auto bounceNumber = GeneralUtils::UTF16ToWTF8(self->GetVar<std::u16string>(u"BouncerNumber"));
auto bouncerSpawners = Game::zoneManager->GetSpawnersByName("PetBouncer" + bounceNumber);
auto switchSpawners = Game::zoneManager->GetSpawnersByName("PetBouncerSwitch" + bounceNumber);
for (auto* bouncerSpawner : bouncerSpawners) {
for (auto* bouncer : bouncerSpawner->m_Info.nodes)
bouncerSpawner->Deactivate();
bouncerSpawner->Reset();
}
for (auto* switchSpawner : switchSpawners) {
switchSpawner->Deactivate();
switchSpawner->Reset();
}
}
}
void PetDigServer::OnDie(Entity* self, Entity* killer) {
const auto iterator = std::find(treasures.begin(), treasures.end(), self->GetObjectID());
if (iterator != treasures.end()) {
treasures.erase(iterator);
}
auto* owner = killer->GetOwner();
const auto digInfoIterator = digInfoMap.find(self->GetLOT());
const auto digInfo = digInfoIterator != digInfoMap.end() ? digInfoIterator->second : defaultDigInfo;
if (digInfo.spawnLot >= 0) {
PetDigServer::SpawnPet(self, owner, digInfo);
} else if (digInfo.builderOnly) {
// Some treasures may only be retrieved by the player that built the diggable
auto builder = self->GetVar<LWOOBJID>(u"builder"); // Set by the pet dig build script
if (builder != owner->GetObjectID())
return;
} else if (digInfo.xBuild) {
PetDigServer::HandleXBuildDig(self, owner, killer);
return;
} else if (digInfo.bouncer) {
PetDigServer::HandleBouncerDig(self, owner);
}
PetDigServer::ProgressPetDigMissions(owner, self);
self->SetNetworkVar<bool>(u"treasure_dug", true);
// TODO: Reset other pets
// Handles smashing leftovers (edge case for the AG X)
auto* xObject = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"X"));
if (xObject != nullptr) {
xObject->Smash(xObject->GetObjectID(), eKillType::VIOLENT);
}
}
void PetDigServer::OnUse(Entity* self, Entity* user) {
LOG("Treasure used!");
auto* petComponent = PetComponent::GetActivePet(user->GetObjectID());
if (!petComponent) return;
if(petComponent->GetIsReadyToDig()) { // TODO: Add handling of the "first time" dig message
auto* destroyableComponent = user->GetComponent<DestroyableComponent>();
if (!destroyableComponent) return;
auto imagination = destroyableComponent->GetImagination();
if (imagination == 0) return; // TODO: Check if there was special behavior for this in the live game (PR_NEED_IMAGINATION)
petComponent->StartInteractDig();
imagination -= 1; // TODO: Get rid of this magic number
destroyableComponent->SetImagination(imagination);
Game::entityManager->SerializeEntity(user);
}
}
void PetDigServer::HandleXBuildDig(const Entity* self, Entity* owner, Entity* pet) {
auto playerID = self->GetVar<LWOOBJID>(u"builder");
if (playerID == LWOOBJID_EMPTY || playerID != owner->GetObjectID())
return;
auto* playerEntity = Game::entityManager->GetEntity(playerID);
if (!playerEntity || !playerEntity->GetParentUser() || !playerEntity->GetParentUser()->GetLastUsedChar())
return;
auto* player = playerEntity->GetCharacter();
const auto groupID = self->GetVar<std::u16string>(u"groupID");
int32_t playerFlag = 0;
// The flag that the player dug up
if (groupID == u"Flag1") {
playerFlag = 61;
} else if (groupID == u"Flag2") {
playerFlag = 62;
} else if (groupID == u"Flag3") {
playerFlag = 63;
}
// If the player doesn't have the flag yet
if (playerFlag != 0 && !player->GetPlayerFlag(playerFlag)) {
auto* petComponent = pet->GetComponent<PetComponent>();
if (petComponent != nullptr) {
// TODO: Pet state = 9 ??
}
// Shows the flag object to the player
player->SetPlayerFlag(playerFlag, true);
}
auto* xObject = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"X"));
if (xObject != nullptr) {
xObject->Smash(xObject->GetObjectID(), eKillType::VIOLENT);
}
}
void PetDigServer::HandleBouncerDig(const Entity* self, const Entity* owner) {
auto bounceNumber = GeneralUtils::UTF16ToWTF8(self->GetVar<std::u16string>(u"BouncerNumber"));
auto bouncerSpawners = Game::zoneManager->GetSpawnersByName("PetBouncer" + bounceNumber);
auto switchSpawners = Game::zoneManager->GetSpawnersByName("PetBouncerSwitch" + bounceNumber);
for (auto* bouncerSpawner : bouncerSpawners) {
bouncerSpawner->Activate();
}
for (auto* switchSpawner : switchSpawners) {
switchSpawner->Activate();
}
}
/**
* Progresses the Can You Dig It mission and the Pet Excavator Achievement if the player has never completed it yet
* \param owner the owner that just made a pet dig something up
*/
void PetDigServer::ProgressPetDigMissions(const Entity* owner, const Entity* chest) {
auto* missionComponent = owner->GetComponent<MissionComponent>();
if (missionComponent != nullptr) {
// Can You Dig It progress
const auto digMissionState = missionComponent->GetMissionState(843);
if (digMissionState == eMissionState::ACTIVE) {
missionComponent->ForceProgress(843, 1216, 1);
}
// Pet Excavator progress
const auto excavatorMissionState = missionComponent->GetMissionState(505);
if (excavatorMissionState == eMissionState::ACTIVE) {
if (chest->HasVar(u"PetDig")) {
int32_t playerFlag = 1260 + chest->GetVarAs<int32_t>(u"PetDig");
Character* player = owner->GetCharacter();
// check if player flag is set
if (!player->GetPlayerFlag(playerFlag)) {
missionComponent->ForceProgress(505, 767, 1);
player->SetPlayerFlag(playerFlag, 1);
}
}
}
}
}
/**
* Some treasures spawn special pets, this handles that case
* \param owner the owner that just made a pet dig something up
* \param digInfo information regarding the treasure, will also contain info about the pet to spawn
*/
void PetDigServer::SpawnPet(Entity* self, const Entity* owner, const DigInfo digInfo) {
// Some treasures require a mission to be active
if (digInfo.requiredMission >= 0) {
auto* missionComponent = owner->GetComponent<MissionComponent>();
if (missionComponent != nullptr && missionComponent->GetMissionState(digInfo.requiredMission) < eMissionState::ACTIVE) {
return;
}
}
EntityInfo info{};
info.lot = digInfo.spawnLot;
info.pos = self->GetPosition();
info.rot = self->GetRotation();
info.spawnerID = self->GetSpawnerID();
info.settings = {
new LDFData<LWOOBJID>(u"tamer", owner->GetObjectID()),
new LDFData<std::string>(u"group", "pet" + std::to_string(owner->GetObjectID())),
new LDFData<std::string>(u"spawnAnim", "spawn-pet"),
new LDFData<float>(u"spawnTimer", 1.0)
};
auto* spawnedPet = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(spawnedPet);
}
Entity* PetDigServer::GetClosestTresure(NiPoint3 position) {
float closestDistance = 0;
Entity* closest = nullptr;
for (const auto tresureId : treasures) {
auto* tresure = Game::entityManager->GetEntity(tresureId);
if (tresure == nullptr) continue;
float distance = Vector3::DistanceSquared(tresure->GetPosition(), position);
if (closest == nullptr || distance < closestDistance) {
closestDistance = distance;
closest = tresure;
}
}
return closest;
}