diff --git a/dScripts/AgQbWall.cpp b/dScripts/AgQbWall.cpp new file mode 100644 index 00000000..d6222419 --- /dev/null +++ b/dScripts/AgQbWall.cpp @@ -0,0 +1,15 @@ +#include "AgQbWall.h" + +void AgQbWall::OnRebuildComplete(Entity* self, Entity* player) { + self->SetVar(u"player", player->GetObjectID()); + auto targetWallSpawners = GeneralUtils::UTF16ToWTF8(self->GetVar(u"spawner")); + if (targetWallSpawners != "") { + auto groupObjs = EntityManager::Instance()->GetEntitiesInGroup(targetWallSpawners); + for (auto* obj : groupObjs) { + if (obj) { + obj->SetVar(u"player", player->GetObjectID()); + obj->OnFireEventServerSide(self, "spawnMobs"); + } + } + } +} diff --git a/dScripts/AgQbWall.h b/dScripts/AgQbWall.h new file mode 100644 index 00000000..93187e0e --- /dev/null +++ b/dScripts/AgQbWall.h @@ -0,0 +1,7 @@ +#pragma once +#include "CppScripts.h" + +class AgQbWall : public CppScripts::Script { +public: + void OnRebuildComplete(Entity* self, Entity* player) override; +}; diff --git a/dScripts/CMakeLists.txt b/dScripts/CMakeLists.txt index ac35b76d..66e3b65b 100644 --- a/dScripts/CMakeLists.txt +++ b/dScripts/CMakeLists.txt @@ -21,6 +21,7 @@ set(DSCRIPT_SOURCES "ActivityManager.cpp" "AgPropGuard.cpp" "AgPropguards.cpp" "AgQbElevator.cpp" + "AgQbWall.cpp" "AgSalutingNpcs.cpp" "AgShipPlayerDeathTrigger.cpp" "AgShipPlayerShockServer.cpp" @@ -206,6 +207,7 @@ set(DSCRIPT_SOURCES "ActivityManager.cpp" "PrSeagullFly.cpp" "PrWhistle.cpp" "QbEnemyStunner.cpp" + "QbSpawner.cpp" "RaceImagineCrateServer.cpp" "RaceImaginePowerup.cpp" "RaceMaelstromGeiser.cpp" diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index 73c50d62..7d114cd3 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -61,6 +61,8 @@ #include "VeMissionConsole.h" #include "VeEpsilonServer.h" #include "AgSurvivalBuffStation.h" +#include "QbSpawner.h" +#include "AgQbWall.h" // NS Scripts #include "NsModularBuild.h" @@ -466,6 +468,10 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = new WildAmbients(); else if (scriptName == "scripts\\ai\\NS\\NS_PP_01\\L_NS_PP_01_TELEPORT.lua") script = new PropertyDeathPlane(); + else if (scriptName == "scripts\\02_server\\Map\\General\\L_QB_SPAWNER.lua") + script = new QbSpawner(); + else if (scriptName == "scripts\\ai\\AG\\L_AG_QB_Wall.lua") + script = new AgQbWall(); //GF: else if (scriptName == "scripts\\02_server\\Map\\GF\\L_GF_TORCH.lua") diff --git a/dScripts/QbSpawner.cpp b/dScripts/QbSpawner.cpp new file mode 100644 index 00000000..edee6148 --- /dev/null +++ b/dScripts/QbSpawner.cpp @@ -0,0 +1,136 @@ +#include "QbSpawner.h" +#include "BaseCombatAIComponent.h" +#include "MovementAIComponent.h" + +void QbSpawner::OnStartup(Entity* self) { + auto mobNum = self->GetVar(u"mobNum"); + auto spawnDist = self->GetVar(u"spawnDist"); + auto mobTemplate = self->GetVar(u"mobTemplate"); + auto spawnTime = self->GetVar(u"spawnTime"); + + if (!mobNum) self->SetVar(u"mobNum", m_DefaultMobNum); + if (!spawnDist) self->SetVar(u"spawnDist", m_DefaultSpawnDist); + if (!mobTemplate) self->SetVar(u"mobTemplate", m_DefaultMobTemplate); + if (!spawnTime) self->SetVar(u"spawnTime", m_DefaultSpawnTime); + + // go ahead and setup the mob table here + std::vector mobTable; + mobTable.assign(self->GetVar(u"mobNum"), LWOOBJID_EMPTY); + + self->SetVar>(u"mobTable", mobTable); +} + +void QbSpawner::OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) { + auto gateObjID = sender->GetObjectID(); + if (!gateObjID) return; + if (args == "spawnMobs") { + self->SetVar(u"gateObj", gateObjID); + auto spawnTime = self->GetVar(u"spawnTime"); + self->AddTimer("SpawnMobEnemies", spawnTime); + } +} + +void QbSpawner::OnTimerDone(Entity* self, std::string timerName) { + if (timerName == "SpawnMobEnemies") { + auto mobTable = self->GetVar>(u"mobTable"); + + auto spawnDist = self->GetVar(u"spawnDist"); + auto mobTemplate = self->GetVar(u"mobTemplate"); + + auto gateObjID = self->GetVar(u"gateObj"); + if (!gateObjID) return; + + auto* gate = EntityManager::Instance()->GetEntity(gateObjID); + if (!gate) return; + + auto oPos = gate->GetPosition(); + auto oDir = gate->GetRotation().GetForwardVector(); + NiPoint3 newPos( + oPos.x + (oDir.x * spawnDist), + oPos.y, + oPos.z + (oDir.z * spawnDist) + ); + auto newRot = NiQuaternion::LookAt(newPos, oPos); + + for (int i = 0; i < mobTable.size(); i++) { + int posOffset = -10; + if (mobTable[i] == LWOOBJID_EMPTY) { + posOffset = posOffset + 5 * i; + auto newOffset = newPos; + newOffset.z = newOffset.z + posOffset; + + EntityInfo info{}; + info.lot = mobTemplate; + info.pos = newOffset; + info.rot = newRot; + info.spawnerID = self->GetObjectID(); + info.spawnerNodeID = 0; + info.settings = { + new LDFData(u"no_timed_spawn", true), + new LDFData(u"aggroRadius", 70), + new LDFData(u"softtetherRadius", 80), + new LDFData(u"tetherRadius", 90), + new LDFData(u"wanderRadius", 5), + new LDFData(u"mobTableLoc", i) + }; + + auto* child = EntityManager::Instance()->CreateEntity(info, nullptr, self); + EntityManager::Instance()->ConstructEntity(child); + + OnChildLoaded(self, child); + } else { + auto* mob = EntityManager::Instance()->GetEntity(mobTable[i]); + AggroTargetObject(self, mob); + } + } + + } +} + +void QbSpawner::OnChildLoaded(Entity* self, Entity* child) { + auto mobTable = self->GetVar>(u"mobTable"); + auto tableLoc = child->GetVar(u"mobTableLoc"); + + mobTable[tableLoc] = child->GetObjectID(); + self->SetVar>(u"mobTable", mobTable); + + AggroTargetObject(self, child); + + const auto selfID = self->GetObjectID(); + + child->AddDieCallback([this, selfID, child]() { + auto* self = EntityManager::Instance()->GetEntity(selfID); + OnChildRemoved(self, child); + } + ); +} + +void QbSpawner::OnChildRemoved(Entity* self, Entity* child) { + auto mobTable = self->GetVar>(u"mobTable"); + auto tableLoc = child->GetVar(u"mobTableLoc"); + + mobTable[tableLoc] = LWOOBJID_EMPTY; + self->SetVar>(u"mobTable", mobTable); +} + +void QbSpawner::AggroTargetObject(Entity* self, Entity* enemy) { + auto* baseCombatAIComponent = enemy->GetComponent(); + if (!baseCombatAIComponent) return; + + auto gateObjID = self->GetVar(u"gateObj"); + if (gateObjID) { + auto* gate = EntityManager::Instance()->GetEntity(gateObjID); + if (gate) { + auto* movementAIComponent = enemy->GetComponent(); + if (movementAIComponent) movementAIComponent->SetDestination(gate->GetPosition()); + baseCombatAIComponent->Taunt(gateObjID, 1000); + } + } + + auto playerObjID = self->GetVar(u"player"); + if (playerObjID) { + baseCombatAIComponent->Taunt(playerObjID, 100); + } + +} + diff --git a/dScripts/QbSpawner.h b/dScripts/QbSpawner.h new file mode 100644 index 00000000..105d0835 --- /dev/null +++ b/dScripts/QbSpawner.h @@ -0,0 +1,17 @@ +#pragma once +#include "CppScripts.h" + +class QbSpawner : public CppScripts::Script { +public: + void OnStartup(Entity* self) override; + void OnFireEventServerSide(Entity* self, Entity* sender, std::string args, int32_t param1, int32_t param2, int32_t param3) override; + void OnTimerDone(Entity* self, std::string timerName) override; + void OnChildLoaded(Entity* self, Entity* child); + void OnChildRemoved(Entity* self, Entity* child); + void AggroTargetObject(Entity* self, Entity* enemy); +private: + const int m_DefaultMobNum = 3; + const float m_DefaultSpawnDist = 25.0; + const LWOOBJID m_DefaultMobTemplate = 4712; + const float m_DefaultSpawnTime = 2.0; +};