mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
The live script (L_EXPLODING_ASSET.lua) does everything inside "if not bIsHit": smash the attacker if they stand within 10 units, cast the explosion skill, smash itself and progress missions. The server only guarded the attacker smash, so another hit before the asset despawned cast the skill and progressed missions again. The TODO asking to kill everything in a radius is obsolete: live smashes only the attacker directly, and everyone else in range takes the explosion skill's damage, which the server already casts. Removed it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
107 lines
4.0 KiB
C++
107 lines
4.0 KiB
C++
#include "ExplodingAsset.h"
|
|
#include "DestroyableComponent.h"
|
|
#include "GameMessages.h"
|
|
#include "MissionComponent.h"
|
|
#include "SkillComponent.h"
|
|
#include "eMissionTaskType.h"
|
|
#include "CDClientManager.h"
|
|
#include "CDObjectSkillsTable.h"
|
|
#include "RenderComponent.h"
|
|
#include "TeamManager.h"
|
|
#include "ProximityMonitorComponent.h"
|
|
|
|
void ExplodingAsset::OnStartup(Entity* self) {
|
|
self->SetProximityRadius(20.0f, "outRadius");
|
|
self->SetVar<int32_t>(u"playersNearChest", 0);
|
|
self->SetProximityRadius(10.0f, "crateHitters");
|
|
}
|
|
|
|
void ExplodingAsset::ProgressPlayerMissions(Entity& self, Entity& player) {
|
|
const auto missionID = self.GetVar<int32_t>(u"missionID");
|
|
auto achievementIDs = self.GetVarAsString(u"achieveID");
|
|
auto* const missionComponent = player.GetComponent<MissionComponent>();
|
|
if (missionComponent) {
|
|
if (missionID != 0) {
|
|
missionComponent->ForceProgressValue(missionID,
|
|
static_cast<uint32_t>(eMissionTaskType::SCRIPT),
|
|
self.GetLOT(), false);
|
|
}
|
|
|
|
if (!achievementIDs.empty()) {
|
|
for (const auto& achievementID : GeneralUtils::SplitString(achievementIDs, u'_')) {
|
|
const auto achievementIDInt = GeneralUtils::TryParse<int32_t>(achievementID);
|
|
if (!achievementIDInt) continue;
|
|
missionComponent->ForceProgressValue(achievementIDInt.value(),
|
|
static_cast<uint32_t>(eMissionTaskType::SCRIPT),
|
|
self.GetLOT());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ExplodingAsset::OnHit(Entity* self, Entity* attacker) {
|
|
const auto* const proximityComponent = self->GetComponent<ProximityMonitorComponent>();
|
|
if (!proximityComponent) return;
|
|
|
|
// Like the live script, the asset only explodes once. Only the attacker is smashed directly, and only when
|
|
// standing close to the asset; everyone else in range is hit by the explosion skill.
|
|
if (!attacker || self->GetBoolean(u"bIsHit")) return;
|
|
|
|
for (const auto objID : proximityComponent->GetProximityObjects("crateHitters")) {
|
|
auto* const entity = Game::entityManager->GetEntity(objID);
|
|
if (!entity || entity->GetObjectID() != attacker->GetObjectID()) continue;
|
|
|
|
auto* const destroyable = entity->GetComponent<DestroyableComponent>();
|
|
if (destroyable) destroyable->Smash(attacker->GetObjectID());
|
|
}
|
|
|
|
attacker = attacker->GetOwner();
|
|
self->SetBoolean(u"bIsHit", true);
|
|
self->SetOwnerOverride(attacker->GetObjectID());
|
|
|
|
GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(self, u"camshake", self->GetObjectID(), 16);
|
|
self->Smash(attacker->GetObjectID());
|
|
|
|
auto* skillComponent = self->GetComponent<SkillComponent>();
|
|
if (skillComponent != nullptr) {
|
|
// Technically supposed to get first skill in the skill component but only 1 object in the live game used this.
|
|
skillComponent->CalculateBehavior(147, 4721, LWOOBJID_EMPTY, true);
|
|
}
|
|
const auto* const team = TeamManager::Instance()->GetTeam(attacker->GetObjectID());
|
|
// Progress all scripted missions related to this asset
|
|
if (team) {
|
|
for (const auto& member : team->members) {
|
|
auto* const memberEntity = Game::entityManager->GetEntity(member);
|
|
if (memberEntity) {
|
|
ProgressPlayerMissions(*self, *memberEntity);
|
|
}
|
|
}
|
|
} else {
|
|
ProgressPlayerMissions(*self, *attacker);
|
|
}
|
|
}
|
|
|
|
void ExplodingAsset::OnProximityUpdate(Entity* self, Entity* entering, std::string name, std::string status) {
|
|
auto* destuctableComponent = entering->GetComponent<DestroyableComponent>();
|
|
|
|
if (destuctableComponent == nullptr) return;
|
|
|
|
const auto& factions = destuctableComponent->GetFactionIDs();
|
|
|
|
if (!std::count(factions.begin(), factions.end(), 1)) return;
|
|
|
|
if (status == "ENTER") {
|
|
RenderComponent::PlayAnimation(self, u"bounce");
|
|
GameMessages::SendPlayFXEffect(self, -1, u"anim", "bouncin", LWOOBJID_EMPTY, 1, 1, true);
|
|
self->SetVar(u"playersNearChest", self->GetVar<int32_t>(u"playersNearChest") + 1);
|
|
} else if (status == "LEAVE") {
|
|
self->SetVar(u"playersNearChest", self->GetVar<int32_t>(u"playersNearChest") - 1);
|
|
|
|
if (self->GetVar<int32_t>(u"playersNearChest") < 1) {
|
|
RenderComponent::PlayAnimation(self, u"idle");
|
|
GameMessages::SendStopFXEffect(self, true, "bouncin");
|
|
self->SetVar<int32_t>(u"playersNearChest", 0);
|
|
}
|
|
}
|
|
}
|