mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 19:03:43 +00:00
Converts the combat messages to NetGameMsgs in CombatMessages.{h,cpp}:
Die, Resurrect, SetResurrectRestoreValues, SetPlayerAllowedRespawn
(SendToClient, as before), Knockback, SetStunned, SetStunImmunity,
SetStatusImmunity, AddBuff, RemoveBuff, AddRunSpeedModifier,
RemoveRunSpeedModifier and DeactivateBubbleBuffFromServer, and the
received RequestDie, RequestSmashPlayer, RequestResurrect, Resurrect,
ActivateBubbleBuff and DeactivateBubbleBuff. Smash and UnSmash move
here from GameMessages.h (Smash's ghostCapacity is ghostOpacity, the
client's name) and gain a Deserialize.
The callers (DestroyableComponent, BuffComponent,
ControllablePhysicsComponent, QuickBuildComponent, RacingControl,
RailActivator and the scripts) build the structs. SendResurrect's
respawn timer moves to DestroyableComponent::Resurrect. The received
messages are registered in GameMessageHandler's map and their switch
cases are deleted, along with the unused HandleRequestDie overload and
the Send* functions nothing called (SendSmash, SendUnSmash, the run
speed modifiers, SendActivateBubbleBuffFromServer). Handler logic is
unchanged.
No wire change and no change in recipients. Verified byte for byte
against a frozen verbatim copy of the old functions over an input grid
(every optional field, bool patterns, empty and long strings), received
messages compared with the old handlers' read sequences and truncated
payloads rejected, hand computed golden bytes, round trips, and a
deliberate width mutation made the tests fail.
Known difference from the client, not changed here: DLU writes
SetStatusImmunity's nine flags in its own order; the client reads them
alphabetically (GameMessage::SetStatusImmunity::Serialize @ 00d8f140).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
342 lines
8.8 KiB
C++
342 lines
8.8 KiB
C++
#include "AmSkullkinDrill.h"
|
|
#include "GameMessages.h"
|
|
#include "EffectsMessages.h"
|
|
#include "CombatMessages.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::PlayFXEffect(self->GetObjectID(), -1, u"spin", "active").Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
|
|
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::PlayFXEffect(user->GetObjectID(), 5499, u"on-anim", "tornado").Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
GameMessages::PlayFXEffect(user->GetObjectID(), 5502, u"on-anim", "staff").Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
|
|
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::SetStunned stun;
|
|
stun.target = player->GetObjectID();
|
|
stun.StateChangeType = StateChangeType;
|
|
stun.Originator = self->GetObjectID();
|
|
stun.bCantAttack = true;
|
|
stun.bCantInteract = true;
|
|
stun.bCantMove = true;
|
|
stun.bCantUseItem = true;
|
|
stun.Send(player->GetSystemAddress());
|
|
}
|
|
|
|
void AmSkullkinDrill::OnArrived(Entity* self, uint32_t waypointIndex) {
|
|
auto* standObj = GetStandObj(self);
|
|
|
|
if (waypointIndex == 1) {
|
|
RenderComponent::PlayAnimation(self, u"no-spin");
|
|
GameMessages::StopFXEffect(self->GetObjectID(), true, "active").Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
GameMessages::PlayFXEffect(self->GetObjectID(), -1, u"indicator", "indicator").Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
|
|
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::PlayFXEffect(self->GetObjectID(), -1, u"spin", "active").Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
GameMessages::StopFXEffect(self->GetObjectID(), true, "indicator").Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
}
|
|
}
|
|
|
|
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::PlayCinematic cinematic;
|
|
cinematic.target = player->GetObjectID();
|
|
cinematic.pathName = cine;
|
|
cinematic.Send(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::PlayFXEffect(standObj->GetObjectID(), 4946, u"explode", "explode").Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
}
|
|
}
|
|
|
|
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::StopFXEffect(player->GetObjectID(), true, "tornado").Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
GameMessages::StopFXEffect(player->GetObjectID(), true, "staff").Send(UNASSIGNED_SYSTEM_ADDRESS);
|
|
}
|
|
|
|
} else if (data[0] == "TryUnFreezeAgain") {
|
|
|
|
}
|
|
}
|