Files
DarkflameServer/dScripts/02_server/Map/AM/AmSkullkinDrill.cpp
Aaron Kimbrell b9d5ef9963 fix: Skullkin drill credits the player who breaks it
The drill only progressed missions 972, 1305 and 1308 for the player who
started it, so another player breaking the drill (the usual way on a
busy Crux Prime) got no credit for the drill objectives. The live
script (L_SKULLKIN_DRILL.lua, notifyDie) credits the killer, then also
the activator when that is someone else, which is what it does now. It
also no longer dereferences a null attacker. Removes the "Missions"
TODO.

May account for part of #1021 (Crux Prime daily smashable objectives not
tallying); needs an in-game check with two players.

Refs #1021

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:30:46 -05:00

331 lines
8.2 KiB
C++

#include "AmSkullkinDrill.h"
#include "GameMessages.h"
#include "MovingPlatformComponent.h"
#include "DestroyableComponent.h"
#include "ProximityMonitorComponent.h"
#include "MissionComponent.h"
#include "EntityInfo.h"
#include "RenderComponent.h"
#include "eStateChangeType.h"
void AmSkullkinDrill::OnStartup(Entity* self) {
self->SetNetworkVar(u"bIsInUse", false);
self->SetVar(u"bActive", true);
GameMessages::SendPlayFXEffect(self->GetObjectID(), -1, u"spin", "active");
auto* movingPlatformComponent = self->GetComponent<MovingPlatformComponent>();
if (movingPlatformComponent == nullptr) {
return;
}
movingPlatformComponent->SetSerialized(true);
movingPlatformComponent->GotoWaypoint(0);
auto* standObj = GetStandObj(self);
if (standObj != nullptr) {
standObj->SetVar(u"bActive", true);
}
self->SetProximityRadius(5, "spin_distance");
}
Entity* AmSkullkinDrill::GetStandObj(Entity* self) {
const auto& myGroup = self->GetGroups();
if (myGroup.empty() || myGroup[0].empty()) {
return nullptr;
}
const auto& group = myGroup[0];
std::string groupName = "Drill_Stand_";
groupName.push_back(group.back());
const auto standObjs = Game::entityManager->GetEntitiesInGroup(groupName);
if (standObjs.empty()) {
return nullptr;
}
return standObjs[0];
}
void AmSkullkinDrill::OnSkillEventFired(Entity* self, Entity* caster, const std::string& message) {
if (message != "NinjagoSpinEvent" || self->GetNetworkVar<bool>(u"bIsInUse")) {
return;
}
auto* proximityMonitorComponent = self->GetComponent<ProximityMonitorComponent>();
if (proximityMonitorComponent == nullptr || !proximityMonitorComponent->IsInProximity("spin_distance", caster->GetObjectID())) {
return;
}
self->SetVar(u"activaterID", caster->GetObjectID());
self->SetNetworkVar(u"bIsInUse", true);
TriggerDrill(self);
}
void AmSkullkinDrill::TriggerDrill(Entity* self) {
RenderComponent::PlayAnimation(self, u"slowdown");
self->AddTimer("killDrill", 10.0f);
auto* standObj = GetStandObj(self);
if (standObj != nullptr) {
standObj->SetVar(u"bActive", false);
}
auto* movingPlatformComponent = self->GetComponent<MovingPlatformComponent>();
if (movingPlatformComponent == nullptr) {
return;
}
movingPlatformComponent->GotoWaypoint(1);
}
void AmSkullkinDrill::OnWaypointReached(Entity* self, uint32_t waypointIndex) {
if (waypointIndex == 1) {
auto myPos = self->GetPosition();
auto myRot = self->GetRotation();
myPos.y -= 21;
EntityInfo info = {};
info.lot = 12346;
info.pos = myPos;
info.rot = myRot;
info.scale = 3; // Needs the scale, otherwise attacks fail
info.spawnerID = self->GetObjectID();
auto* child = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(child);
self->SetVar(u"ChildSmash", child->GetObjectID());
child->AddDieCallback([this, self]() {
const auto& userID = self->GetVar<LWOOBJID>(u"activaterID");
auto* player = Game::entityManager->GetEntity(userID);
if (player == nullptr) {
return;
}
OnHitOrHealResult(self, player, 1);
});
}
OnArrived(self, waypointIndex);
}
void AmSkullkinDrill::OnUse(Entity* self, Entity* user) {
if (self->GetNetworkVar<bool>(u"bIsInUse")) {
return;
}
self->SetNetworkVar(u"bIsInUse", true);
GameMessages::SendPlayFXEffect(user->GetObjectID(), 5499, u"on-anim", "tornado");
GameMessages::SendPlayFXEffect(user->GetObjectID(), 5502, u"on-anim", "staff");
const auto userID = user->GetObjectID();
self->SetVar(u"userID", userID);
self->SetVar(u"activaterID", userID);
PlayAnim(self, user, "spinjitzu-staff-windup");
PlayCinematic(self);
FreezePlayer(self, user, true);
}
void AmSkullkinDrill::FreezePlayer(Entity* self, Entity* player, bool bFreeze) {
auto StateChangeType = eStateChangeType::POP;
if (bFreeze) {
if (player->GetIsDead()) {
return;
}
StateChangeType = eStateChangeType::PUSH;
} else {
if (player->GetIsDead()) {
//
}
}
GameMessages::SendSetStunned(player->GetObjectID(), StateChangeType, player->GetSystemAddress(), self->GetObjectID(),
true, false, true, false, true, false, true
);
}
void AmSkullkinDrill::OnArrived(Entity* self, uint32_t waypointIndex) {
auto* standObj = GetStandObj(self);
if (waypointIndex == 1) {
RenderComponent::PlayAnimation(self, u"no-spin");
GameMessages::SendStopFXEffect(self, true, "active");
GameMessages::SendPlayFXEffect(self->GetObjectID(), -1, u"indicator", "indicator");
self->SetVar(u"bActive", false);
const auto playerID = self->GetVar<LWOOBJID>(u"userID");
auto* player = Game::entityManager->GetEntity(playerID);
if (player != nullptr) {
PlayAnim(self, player, "spinjitzu-staff-end");
}
if (standObj != nullptr) {
standObj->SetVar(u"bActive", false);
}
return;
} else {
RenderComponent::PlayAnimation(self, u"idle");
GameMessages::SendPlayFXEffect(self->GetObjectID(), -1, u"spin", "active");
GameMessages::SendStopFXEffect(self, true, "indicator");
}
}
void AmSkullkinDrill::PlayCinematic(Entity* self) {
auto* player = Game::entityManager->GetEntity(self->GetVar<LWOOBJID>(u"userID"));
if (player == nullptr) {
return;
}
const auto& cine = self->GetVar<std::u16string>(u"cinematic");
if (cine.empty()) {
return;
}
GameMessages::SendPlayCinematic(player->GetObjectID(), cine, player->GetSystemAddress());
}
void AmSkullkinDrill::PlayAnim(Entity* self, Entity* player, const std::string& animName) {
const auto animTime = animName == "spinjitzu-staff-end" ? 0.5f : 1.0f;
RenderComponent::PlayAnimation(player, animName);
self->AddTimer("AnimDone_" + animName, animTime);
}
void AmSkullkinDrill::OnHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) {
auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr || attacker == nullptr || !attacker->IsPlayer()) {
return;
}
if (self->GetVar<bool>(u"bActive")) {
return;
}
const auto updateMissions = [this, self](Entity* player) {
auto* missionComponent = player->GetComponent<MissionComponent>();
if (!missionComponent) return;
for (const auto missionID : m_MissionsToUpdate) {
missionComponent->ForceProgressValue(missionID, 1, self->GetLOT());
}
};
// The player who broke the drill gets credit, and so does the player who started it if that was someone else.
updateMissions(attacker);
const auto activaterID = self->GetVar<LWOOBJID>(u"activaterID");
if (activaterID != LWOOBJID_EMPTY && activaterID != attacker->GetObjectID()) {
auto* activator = Game::entityManager->GetEntity(activaterID);
if (activator) updateMissions(activator);
}
self->Smash(attacker->GetObjectID(), eKillType::SILENT);
self->CancelAllTimers();
auto* standObj = GetStandObj(self);
if (standObj != nullptr) {
GameMessages::SendPlayFXEffect(standObj->GetObjectID(), 4946, u"explode", "explode");
}
}
void AmSkullkinDrill::OnTimerDone(Entity* self, std::string timerName) {
if (timerName == "killDrill") {
const auto childID = self->GetVar<LWOOBJID>(u"ChildSmash");
auto* child = Game::entityManager->GetEntity(childID);
if (child != nullptr) {
child->Smash(self->GetObjectID(), eKillType::SILENT);
}
self->SetNetworkVar(u"bIsInUse", false);
self->SetVar(u"bActive", true);
self->SetVar(u"activaterID", LWOOBJID_EMPTY);
auto* standObj = GetStandObj(self);
if (standObj != nullptr) {
standObj->SetVar(u"bActive", true);
}
auto* movingPlatformComponent = self->GetComponent<MovingPlatformComponent>();
if (movingPlatformComponent == nullptr) {
return;
}
movingPlatformComponent->GotoWaypoint(0);
return;
}
const auto& data = GeneralUtils::SplitString(timerName, '_');
if (data.empty()) {
return;
}
if (data[0] == "AnimDone") {
const auto& animName = data[1];
const auto playerID = self->GetVar<LWOOBJID>(u"userID");
auto* player = Game::entityManager->GetEntity(playerID);
if (player == nullptr) {
return;
}
if (animName == "spinjitzu-staff-windup") {
TriggerDrill(self);
RenderComponent::PlayAnimation(player, u"spinjitzu-staff-loop");
} else if (animName == "spinjitzu-staff-end") {
FreezePlayer(self, player, false);
self->SetVar(u"userID", LWOOBJID_EMPTY);
GameMessages::SendStopFXEffect(player, true, "tornado");
GameMessages::SendStopFXEffect(player, true, "staff");
}
} else if (data[0] == "TryUnFreezeAgain") {
}
}