#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 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 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(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(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(u"treasure_dug", true); // TODO: Reset other pets // Handles smashing leftovers (edge case for the AG X) auto* xObject = Game::entityManager->GetEntity(self->GetVar(u"X")); if (xObject != nullptr) { xObject->Smash(xObject->GetObjectID(), eKillType::VIOLENT); } } void PetDigServer::OnUse(Entity* self, Entity* user) { LOG_DEBUG("Treasure used! LWOOBJID: %d", self->GetObjectID()); auto* petComponent = PetComponent::GetActivePet(user->GetObjectID()); if (!petComponent) return; if(petComponent->IsReadyToInteract()) { petComponent->StartInteractTreasureDig(); } } void PetDigServer::HandleXBuildDig(const Entity* self, Entity* owner, Entity* pet) { auto playerID = self->GetVar(u"builder"); if (playerID == LWOOBJID_EMPTY || playerID != owner->GetObjectID()) return; auto* playerEntity = Game::entityManager->GetEntity(playerID); if (!playerEntity || !playerEntity->GetCharacter()) return; auto* player = playerEntity->GetCharacter(); const auto groupID = self->GetVar(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(); 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(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(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(); 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(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(); 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(u"tamer", owner->GetObjectID()), new LDFData(u"group", "pet" + std::to_string(owner->GetObjectID())), new LDFData(u"spawnAnim", "spawn-pet"), new LDFData(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; }