mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
Live (the Frakjaw battle captures, LOT 16141): when its quick build completes the simple mover is serialized waiting at waypoint 0 (9) and about 0.1 s later travelling (2); it stops at its end without a serialization. That is the client's LWOPlatform, active once the quick build is complete (SendMessage 0x00bdc930), waiting the shortest wait and setting off (RunPlatform 0x00c86e10). L_CAVE_PRISON_CAGE and L_MONASTERY_BOSS_INSTANCE_SERVER never send it: they ask to be told of its PlatformAtLastWaypoint, and act then. So the simple mover sets off when it is built, the cave prison's button goes down when the counterweight arrives instead of when it is built, and Frakjaw is hit when it arrives (0.1 s and the row's 1 s after the build) instead of on a 0.5 s timer with a counterweight that never moved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
218 lines
6.1 KiB
C++
218 lines
6.1 KiB
C++
#include "CavePrisonCage.h"
|
|
#include "EntityManager.h"
|
|
#include "QuickBuildComponent.h"
|
|
#include "GameMessages.h"
|
|
#include "ObjectMessages.h"
|
|
#include "Character.h"
|
|
#include "dZoneManager.h"
|
|
#include "RenderComponent.h"
|
|
#include "MovingPlatformComponent.h"
|
|
|
|
void CavePrisonCage::OnStartup(Entity* self) {
|
|
const auto& myNum = self->GetVar<std::u16string>(u"myNumber");
|
|
|
|
if (myNum.empty()) {
|
|
return;
|
|
}
|
|
|
|
const auto spawners = Game::zoneManager->GetSpawnersByName("PrisonCounterweight_0" + GeneralUtils::UTF16ToWTF8(myNum));
|
|
if (spawners.empty()) return;
|
|
|
|
auto* spawner = spawners[0];
|
|
|
|
self->SetVar<Spawner*>(u"CWSpawner", spawner);
|
|
|
|
Setup(self, spawner);
|
|
}
|
|
|
|
void CavePrisonCage::Setup(Entity* self, Spawner* spawner) {
|
|
if (!spawner) return;
|
|
SpawnCounterweight(self, spawner);
|
|
|
|
NiPoint3 mypos = self->GetPosition();
|
|
NiQuaternion myrot = self->GetRotation();
|
|
|
|
mypos.y += 1.5;
|
|
mypos.z -= 0.5;
|
|
|
|
EntityInfo info{};
|
|
info.lot = m_Villagers[self->GetVarAs<int32_t>(u"myNumber") - 1];
|
|
info.pos = mypos;
|
|
info.rot = myrot;
|
|
info.spawnerID = self->GetObjectID();
|
|
|
|
// Spawn the villager inside the jail
|
|
auto* entity = Game::entityManager->CreateEntity(info);
|
|
|
|
// Save the villeger ID
|
|
self->SetVar<LWOOBJID>(u"villager", entity->GetObjectID());
|
|
|
|
// Construct the entity
|
|
Game::entityManager->ConstructEntity(entity);
|
|
}
|
|
|
|
void CavePrisonCage::OnQuickBuildNotifyState(Entity* self, eQuickBuildState state) {
|
|
if (state != eQuickBuildState::RESETTING) {
|
|
return;
|
|
}
|
|
|
|
auto* spawner = self->GetVar<Spawner*>(u"CWSpawner");
|
|
|
|
if (spawner == nullptr) {
|
|
return;
|
|
}
|
|
|
|
spawner->Reset();
|
|
|
|
SpawnCounterweight(self, spawner);
|
|
}
|
|
|
|
void CavePrisonCage::SpawnCounterweight(Entity* self, Spawner* spawner) {
|
|
if (!spawner) return;
|
|
|
|
spawner->Reset();
|
|
|
|
auto* counterweight = spawner->Spawn();
|
|
|
|
self->SetVar<LWOOBJID>(u"Counterweight", counterweight->GetObjectID());
|
|
|
|
auto* quickBuildComponent = counterweight->GetComponent<QuickBuildComponent>();
|
|
|
|
if (quickBuildComponent != nullptr) {
|
|
quickBuildComponent->AddQuickBuildStateCallback([this, self](eQuickBuildState state) {
|
|
OnQuickBuildNotifyState(self, state);
|
|
});
|
|
|
|
quickBuildComponent->AddQuickBuildCompleteCallback([this, self](Entity* user) {
|
|
// Save the userID as Builder
|
|
self->SetVar<LWOOBJID>(u"Builder", user->GetObjectID());
|
|
});
|
|
}
|
|
|
|
// L_CAVE_PRISON_CAGE (notifyPlatformAtLastWaypoint): the built counterweight, a simple mover (MovingPlatforms row
|
|
// 41) that sets off by itself when its quick build completes, has gone down to the end of its path
|
|
auto* const platform = counterweight->GetComponent<MovingPlatformComponent>();
|
|
if (platform) {
|
|
platform->AddPlatformAtLastWaypointCallback([this, self]() {
|
|
// Get the button and make sure it still exists
|
|
auto* button = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"Button"));
|
|
|
|
if (button == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// Play the 'down' animation on the button
|
|
RenderComponent::PlayAnimation(button, u"down");
|
|
|
|
// Setup a timer named 'buttonGoingDown' to be triggered in 5 seconds
|
|
self->AddTimer("buttonGoingDown", 5.0f);
|
|
});
|
|
}
|
|
|
|
if (self->GetVar<LWOOBJID>(u"Button")) {
|
|
return;
|
|
}
|
|
|
|
GetButton(self);
|
|
}
|
|
|
|
void CavePrisonCage::GetButton(Entity* self) {
|
|
const auto buttons = Game::entityManager->GetEntitiesInGroup("PrisonButton_0" + std::to_string(self->GetVarAs<int32_t>(u"myNumber")));
|
|
|
|
if (buttons.size() == 0) {
|
|
// Try again in 0.5 seconds
|
|
self->AddCallbackTimer(0.5, [this, self]() {
|
|
GetButton(self);
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
auto* button = buttons[0];
|
|
|
|
self->SetVar<LWOOBJID>(u"Button", button->GetObjectID());
|
|
}
|
|
|
|
void CavePrisonCage::OnTimerDone(Entity* self, std::string timerName) {
|
|
// the anim of the button down is over
|
|
if (timerName == "buttonGoingDown") {
|
|
// Play the 'up' animation
|
|
RenderComponent::PlayAnimation(self, u"up");
|
|
|
|
// Setup a timer named 'CageOpen' to be triggered in 1 second
|
|
self->AddTimer("CageOpen", 1.0f);
|
|
} else if (timerName == "CageOpen") {
|
|
// play the idle open anim
|
|
RenderComponent::PlayAnimation(self, u"idle-up");
|
|
|
|
// Get the villeger
|
|
auto* villager = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"villager"));
|
|
|
|
if (villager == nullptr) {
|
|
return;
|
|
}
|
|
|
|
GameMessages::NotifyClientObject(villager->GetObjectID(), u"TimeToChat", 0, 0, LWOOBJID_EMPTY, "").Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
|
|
// Get the builder and make sure it still exists
|
|
auto* builder = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"Builder"));
|
|
|
|
if (builder == nullptr) {
|
|
return;
|
|
}
|
|
|
|
const auto flagNum = 2020 + self->GetVarAs<int32_t>(u"myNumber");
|
|
|
|
// Set the flag on the builder character
|
|
auto* const character = builder->GetCharacter();
|
|
if (character) character->SetPlayerFlag(flagNum, true);
|
|
|
|
// Setup a timer named 'VillagerEscape' to be triggered in 5 seconds
|
|
self->AddTimer("VillagerEscape", 5.0f);
|
|
} else if (timerName == "VillagerEscape") {
|
|
// Get the villeger and make sure it still exists
|
|
auto* villager = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"villager"));
|
|
|
|
if (villager == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// Kill the villager
|
|
villager->Kill();
|
|
|
|
// Setup a timer named 'SmashCounterweight' to be triggered in 2 seconds
|
|
self->AddTimer("SmashCounterweight", 2.0f);
|
|
} else if (timerName == "SmashCounterweight") {
|
|
// Get the counterweight and make sure it still exists
|
|
auto* counterweight = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"Counterweight"));
|
|
|
|
if (counterweight == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// Smash the counterweight
|
|
counterweight->Smash();
|
|
|
|
// Get the button and make sure it still exists
|
|
auto* button = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"Button"));
|
|
|
|
if (button == nullptr) {
|
|
return;
|
|
}
|
|
|
|
// Play the 'up' animation on the button
|
|
RenderComponent::PlayAnimation(button, u"up");
|
|
|
|
// Setup a timer named 'CageClosed' to be triggered in 1 second
|
|
self->AddTimer("CageClosed", 1.0f);
|
|
} else if (timerName == "CageClosed") {
|
|
// play the idle closed anim
|
|
RenderComponent::PlayAnimation(self, u"idle");
|
|
|
|
// Setup a timer named 'ResetPrison' to be triggered in 10 seconds
|
|
self->AddTimer("ResetPrison", 10.0f);
|
|
} else if (timerName == "ResetPrison") {
|
|
Setup(self, self->GetVar<Spawner*>(u"CWSpawner"));
|
|
}
|
|
}
|