mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-11-14 07:47:13 +00:00
fix(shooting gallery): a target at the end of its path respawns and despawns, without a score
SG_TARGET.lua, the targets' server script: at the last waypoint of its path (onArrived with isLastPoint, which the client's PushToLua 0x00b1a910 sets for a moving platform's Arrived as for a waypoint follower's) a target notifies the cannon, FinishedPath, and sg_cannon.lua spawns the next one as after a hit (checkSpawn). A target set to despawn there then dies silently after 1 s, a moving platform after 2 s. Here ships and subs sat at the end of their path until the game ended, and nothing came after them. The script cannot load (the targets have a script row of their own), so the cannon does it: - the respawn is split out of RegisterHit (CheckSpawn), and the die callback only registers a hit when the death is not the silent one; - a moving platform target tells through PlatformAtLastWaypoint, a waypoint follower through a new MovementAIComponent waypoint callback. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
@@ -541,6 +541,9 @@ void MovementAIComponent::SetMaxSpeed(const float value) {
|
||||
|
||||
void MovementAIComponent::RunWaypointCommands(uint32_t waypointNum) {
|
||||
m_Parent->GetScript()->OnWaypointReached(m_Parent, waypointNum);
|
||||
// A callback may add another
|
||||
const auto callbacks = m_WaypointReachedCallbacks;
|
||||
for (const auto& callback : callbacks) callback(waypointNum);
|
||||
|
||||
if (!m_Path || waypointNum >= m_Path->pathWaypoints.size()) return;
|
||||
const auto& commands = m_Path->pathWaypoints[waypointNum].commands;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "eReplicaComponentType.h"
|
||||
#include "Zone.h"
|
||||
#include "dpKnockback.h"
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
@@ -188,6 +189,12 @@ public:
|
||||
*/
|
||||
bool AtFinalWaypoint() const { return m_AtFinalWaypoint; }
|
||||
|
||||
/**
|
||||
* Calls back at each waypoint of the path the entity follows, with the waypoint's number (what the Arrived game
|
||||
* message tells an object's script)
|
||||
*/
|
||||
void AddWaypointReachedCallback(const std::function<void(uint32_t)>& callback) { m_WaypointReachedCallbacks.push_back(callback); }
|
||||
|
||||
/**
|
||||
* Renders the entity stationary
|
||||
*/
|
||||
@@ -380,6 +387,8 @@ private:
|
||||
// The number of waypoints that were on the path in the call to SetPath
|
||||
uint32_t m_CurrentPathWaypointCount{ 0 };
|
||||
|
||||
std::vector<std::function<void(uint32_t)>> m_WaypointReachedCallbacks;
|
||||
|
||||
LWOOBJID m_FollowedTarget{ LWOOBJID_EMPTY };
|
||||
|
||||
/**
|
||||
|
||||
@@ -287,7 +287,7 @@ void SGCannon::DoSpawnTimerFunc(Entity* self, const std::string& name) {
|
||||
info.pos = path->pathWaypoints[0].position;
|
||||
|
||||
info.settings.Insert<SGEnemy>(u"SpawnData", toSpawn);
|
||||
info.settings.Insert<std::string>(u"custom_script_server", "scripts/ai/ACT/SG_TARGET.lua"); // this script is never loaded;
|
||||
info.settings.Insert<std::string>(u"custom_script_server", "scripts/ai/ACT/SG_TARGET.lua"); // never loaded (the targets have a script row): TargetFinishedPath does its part
|
||||
info.settings.Insert<std::string>(u"custom_script_client", "scripts/client/ai/SG_TARGET_CLIENT.lua");
|
||||
info.settings.Insert<std::string>(u"attached_path", path->pathName);
|
||||
info.settings.Insert<uint32_t>(u"attached_path_start", 0);
|
||||
@@ -322,10 +322,27 @@ void SGCannon::DoSpawnTimerFunc(Entity* self, const std::string& name) {
|
||||
movementAI->SetPath(path->pathWaypoints);
|
||||
}
|
||||
|
||||
enemy->AddDieCallback([this, self, enemy, name]() {
|
||||
RegisterHit(self, enemy, name);
|
||||
// A hit target scores (live's cannon ball tells the cannon of its kills). One that got to the end of its path
|
||||
// tells the cannon and dies silently a moment later (SG_TARGET.lua), which is no hit.
|
||||
const auto silentDeath = std::make_shared<bool>(false);
|
||||
enemy->AddDieCallback([this, self, enemy, name, silentDeath]() {
|
||||
if (!*silentDeath) RegisterHit(self, enemy, name);
|
||||
});
|
||||
|
||||
// SG_TARGET.lua (onArrived): the Arrived message says whether the waypoint is the path's last one, for a moving
|
||||
// platform as for an object following waypoints (the script reads isLastPoint, which the client's PushToLua
|
||||
// 0x00b1a910 sets from the path and the waypoint)
|
||||
const auto lastWaypoint = static_cast<uint32_t>(path->pathWaypoints.size() - 1);
|
||||
if (movingPlatform) {
|
||||
movingPlatform->AddPlatformAtLastWaypointCallback([this, self, enemy, name, silentDeath]() {
|
||||
TargetFinishedPath(self, enemy, name, silentDeath);
|
||||
});
|
||||
} else if (movementAI) {
|
||||
movementAI->AddWaypointReachedCallback([this, self, enemy, name, silentDeath, lastWaypoint](const uint32_t waypoint) {
|
||||
if (waypoint == lastWaypoint) TargetFinishedPath(self, enemy, name, silentDeath);
|
||||
});
|
||||
}
|
||||
|
||||
// Save the enemy
|
||||
if (enemy != nullptr) {
|
||||
const_cast<std::vector<LWOOBJID>&>(self->GetVar<std::vector<LWOOBJID>>(SpawnedObjects)).push_back(enemy->GetObjectID());
|
||||
@@ -684,6 +701,37 @@ void SGCannon::OnActivityNotify(Entity* self, GameMessages::ActivityNotify& noti
|
||||
self->SetVar<std::vector<LWOOBJID>>(u"CannonBallKills", {});
|
||||
}
|
||||
|
||||
void SGCannon::CheckSpawn(Entity* self, Entity* target, const std::string& timerName) {
|
||||
if (!self->GetVar<bool>(GameStartedVariable)) return;
|
||||
const auto& spawnInfo = target->GetVar<SGEnemy>(u"SpawnData");
|
||||
|
||||
if (spawnInfo.respawns && target->GetVar<uint32_t>(u"wave") == self->GetVar<uint32_t>(ThisWaveVariable)) {
|
||||
const auto respawnTime = GeneralUtils::GenerateRandomNumber<float_t>(spawnInfo.minRespawnTime, spawnInfo.maxRespawnTime);
|
||||
|
||||
ActivityTimerStart(self, timerName, respawnTime, respawnTime);
|
||||
}
|
||||
}
|
||||
|
||||
// SG_TARGET.lua: at the last waypoint of its path a target notifies the cannon (FinishedPath: sg_cannon.lua spawns the
|
||||
// next one as after a hit, without a score) and, when it is to despawn there, dies silently 1 s later, a moving
|
||||
// platform after 2 s (so the clients' platforms get to the end of the path first).
|
||||
void SGCannon::TargetFinishedPath(Entity* self, Entity* target, const std::string& timerName, const std::shared_ptr<bool>& silentDeath) {
|
||||
// Once: a platform sent on would tell again
|
||||
if (target->GetVar<bool>(u"FinishedPath")) return;
|
||||
target->SetVar<bool>(u"FinishedPath", true);
|
||||
|
||||
CheckSpawn(self, target, timerName);
|
||||
|
||||
const auto& spawnInfo = target->GetVar<SGEnemy>(u"SpawnData");
|
||||
if (!spawnInfo.despawnOnLastWaypoint) return;
|
||||
target->AddCallbackTimer(spawnInfo.isMovingPlatform ? 2.0f : 1.0f, [target, silentDeath]() {
|
||||
// Hit in the meantime
|
||||
if (target->GetIsDead()) return;
|
||||
*silentDeath = true;
|
||||
target->Smash(target->GetObjectID(), eKillType::SILENT);
|
||||
});
|
||||
}
|
||||
|
||||
void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& timerName) {
|
||||
if (!self->GetVar<bool>(GameStartedVariable)) return;
|
||||
|
||||
@@ -692,11 +740,7 @@ void SGCannon::RegisterHit(Entity* self, Entity* target, const std::string& time
|
||||
self->SetVar<std::vector<LWOOBJID>>(u"CannonBallKills", cannonBallKills);
|
||||
const auto& spawnInfo = target->GetVar<SGEnemy>(u"SpawnData");
|
||||
|
||||
if (spawnInfo.respawns && target->GetVar<uint32_t>(u"wave") == self->GetVar<uint32_t>(ThisWaveVariable)) {
|
||||
const auto respawnTime = GeneralUtils::GenerateRandomNumber<float_t>(spawnInfo.minRespawnTime, spawnInfo.maxRespawnTime);
|
||||
|
||||
ActivityTimerStart(self, timerName, respawnTime, respawnTime);
|
||||
}
|
||||
CheckSpawn(self, target, timerName);
|
||||
|
||||
int32_t score = spawnInfo.score;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include "ActivityManager.h"
|
||||
|
||||
@@ -89,6 +90,10 @@ private:
|
||||
void PauseChargeCannon(Entity* self);
|
||||
void StopGame(Entity* self, bool cancel = false);
|
||||
void RegisterHit(Entity* self, Entity* target, const std::string& timerName);
|
||||
// sg_cannon.lua (checkSpawn): a target that respawns gets its next spawn, while its wave runs
|
||||
void CheckSpawn(Entity* self, Entity* target, const std::string& timerName);
|
||||
// SG_TARGET.lua (onArrived at the last waypoint): the cannon spawns the next one, and the target is gone a moment later
|
||||
void TargetFinishedPath(Entity* self, Entity* target, const std::string& timerName, const std::shared_ptr<bool>& silentDeath);
|
||||
void UpdateStreak(Entity* self);
|
||||
float_t GetCurrentBonus(Entity* self);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user