diff --git a/dScripts/02_server/Map/AM/AmSkeletonSpawnerVolume.cpp b/dScripts/02_server/Map/AM/AmSkeletonSpawnerVolume.cpp new file mode 100644 index 000000000..fae3b4414 --- /dev/null +++ b/dScripts/02_server/Map/AM/AmSkeletonSpawnerVolume.cpp @@ -0,0 +1,75 @@ +#include "AmSkeletonSpawnerVolume.h" + +#include "Entity.h" +#include "dZoneManager.h" +#include "Spawner.h" + +namespace { + const std::u16string TotalPlayers = u"TotalPlayers"; + const std::u16string SpawnStateMax = u"SpawnStateMax"; + const std::u16string JustChanged = u"JustChanged"; + + void SetNumAndReset(const std::string& spawnerName, const int32_t num) { + for (auto* const spawner : Game::zoneManager->GetSpawnersByName(spawnerName)) { + spawner->SetNumToMaintain(num); + spawner->Reset(); + } + } +} + +void AmSkeletonSpawnerVolume::OnStartup(Entity* self) { + self->SetVar(TotalPlayers, 0); + self->SetVar(SpawnStateMax, false); + self->SetVar(JustChanged, false); +} + +void AmSkeletonSpawnerVolume::OnCollisionPhantom(Entity* self, Entity* target) { + if (!target || !target->IsPlayer()) return; + + const auto totalPlayers = self->GetVar(TotalPlayers) + 1; + self->SetVar(TotalPlayers, totalPlayers); + + if (totalPlayers >= ChangeNum && !self->GetVar(JustChanged) && !self->GetVar(SpawnStateMax)) { + SetSpawnCounts(self, true); + } +} + +void AmSkeletonSpawnerVolume::OnOffCollisionPhantom(Entity* self, Entity* target) { + if (!target || !target->IsPlayer()) return; + + const auto totalPlayers = std::max(self->GetVar(TotalPlayers) - 1, 0); + self->SetVar(TotalPlayers, totalPlayers); + + if (totalPlayers < ChangeNum && !self->GetVar(JustChanged) && self->GetVar(SpawnStateMax)) { + SetSpawnCounts(self, false); + } +} + +void AmSkeletonSpawnerVolume::OnTimerDone(Entity* self, std::string timerName) { + if (timerName != "StateChange") return; + + self->SetVar(JustChanged, false); + + // Catch up on any change that was held back while the counts were cooling down. + const auto totalPlayers = self->GetVar(TotalPlayers); + const auto isMax = self->GetVar(SpawnStateMax); + if (!isMax && totalPlayers >= ChangeNum) { + SetSpawnCounts(self, true); + } else if (isMax && totalPlayers < ChangeNum) { + SetSpawnCounts(self, false); + } +} + +void AmSkeletonSpawnerVolume::SetSpawnCounts(Entity* self, const bool max) { + self->SetVar(SpawnStateMax, max); + self->SetVar(JustChanged, true); + self->AddTimer("StateChange", ChangeCooldown); + + // There are 6 engineer and 6 miner spawner networks. + for (int32_t i = 1; i <= 6; i++) { + SetNumAndReset("Skullkin_Engineer_" + std::to_string(i), max ? 2 : 1); + SetNumAndReset("Skullkin_Miner_" + std::to_string(i), max ? 6 : 4); + } + + SetNumAndReset("Skullkin_Patrollers", max ? 6 : 4); +} diff --git a/dScripts/02_server/Map/AM/AmSkeletonSpawnerVolume.h b/dScripts/02_server/Map/AM/AmSkeletonSpawnerVolume.h new file mode 100644 index 000000000..921c2ec7e --- /dev/null +++ b/dScripts/02_server/Map/AM/AmSkeletonSpawnerVolume.h @@ -0,0 +1,21 @@ +#pragma once +#include "CppScripts.h" + +// A volume over the Skullkin area of Crux Prime that spawns more Skullkin while many players are in it. +class AmSkeletonSpawnerVolume : public CppScripts::Script { +public: + void OnStartup(Entity* self) override; + void OnCollisionPhantom(Entity* self, Entity* target) override; + void OnOffCollisionPhantom(Entity* self, Entity* target) override; + void OnTimerDone(Entity* self, std::string timerName) override; + +private: + // Sets the spawner networks to the higher or lower counts. + static void SetSpawnCounts(Entity* self, bool max); + + // Players needed in the volume for the higher counts. + static constexpr int32_t ChangeNum = 10; + + // Seconds after a change before the counts may change again. + static constexpr float ChangeCooldown = 60.0f; +}; diff --git a/dScripts/02_server/Map/AM/CMakeLists.txt b/dScripts/02_server/Map/AM/CMakeLists.txt index 81a37548d..bbf96c600 100644 --- a/dScripts/02_server/Map/AM/CMakeLists.txt +++ b/dScripts/02_server/Map/AM/CMakeLists.txt @@ -1,5 +1,6 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_AM "AmConsoleTeleportServer.cpp" + "AmSkeletonSpawnerVolume.cpp" "RandomSpawnerFin.cpp" "RandomSpawnerPit.cpp" "RandomSpawnerStr.cpp" diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index 0e03c9836..b4bb79da7 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -136,6 +136,8 @@ #include "FvHorsemenTrigger.h" #include "FvFlyingCreviceDragon.h" #include "FvDragonInstanceServer.h" +#include "ForceFieldEffect.h" +#include "NpcNpJetpackGuy.h" #include "FvMaelstromDragon.h" #include "DragonRonin.h" #include "FvDragonSmashingGolemQb.h" @@ -251,6 +253,7 @@ #include "AmDarklingDragon.h" #include "AmBlueX.h" #include "AmTeapotServer.h" +#include "AmSkeletonSpawnerVolume.h" #include "WanderingVendor.h" // NJ Scripts @@ -608,6 +611,7 @@ namespace { {"scripts\\02_server\\Enemy\\AM\\L_AM_DARKLING_APE.lua", []() {return new BaseEnemyApe();}}, {"scripts\\02_server\\Map\\AM\\L_BLUE_X.lua", []() {return new AmBlueX();}}, {"scripts\\02_server\\Map\\AM\\L_TEAPOT_SERVER.lua", []() {return new AmTeapotServer();}}, + {"scripts\\02_server\\Map\\AM\\L_SKELETON_SPAWNER_VOLUME.lua", []() {return new AmSkeletonSpawnerVolume();}}, //Ninjago {"scripts\\02_server\\Map\\njhub\\L_GARMADON_CELEBRATION_SERVER.lua", []() {return new NjGarmadonCelebration();}}, @@ -723,6 +727,8 @@ namespace { {"scripts\\equipmenttriggers\\ImaginationBackPack.lua", []() {return new ImaginationBackPack();}}, {"scripts\\ai\\MINIGAME\\SG_GF\\SERVER\\SG_CANNON_INSTANCE_ACTOR.lua", [](){return new RegisterWithZoneControl();}}, {"scripts\\ai\\MINIGAME\\SG_GF\\SERVER\\SG_CANNON_INSTANCE_EFFECT.lua", [](){return new RegisterWithZoneControl();}}, + {"scripts\\ai\\LS\\ForceFieldEffect.lua", []() {return new ForceFieldEffect();}}, + {"scripts\\ai\\NP\\L_NPC_NP_JETPACK_GUY.lua", []() {return new NpcNpJetpackGuy();}}, }; std::set g_ExcludedScripts = { @@ -749,6 +755,7 @@ namespace { "scripts\\ai\\GENERAL\\L_NPC_GENERIC_MOVEMENT.lua", // Really old alpha script "scripts\\zone\\LUPs\\DeepFreeze Intro\\WBL_Enemy_Beaver.lua", // Really old alpha script "scripts\\ai\\GENERAL\\L_NPC_GENERIC_WANDER_SMALL.lua", // Really old alpha script + "scripts\\zone\\LUPs\\Moonbase Intro\\WBL_Enemy_Grabbler.lua", // Really old alpha script, only sets wander variables "scripts\\ai\\NP\\L_NPC_NP_OLD_MAN_SHERLAND.lua", // This NPC doesn't even exist in modern crux, the only place this is used... "scripts\\02_server\\Map\\General\\L_SIMPLE_MOVER_SWITCH.lua", // This platform does not exist even when moved manually on a client }; diff --git a/dScripts/ai/GENERAL/CMakeLists.txt b/dScripts/ai/GENERAL/CMakeLists.txt index 77441b4ac..49a9c6093 100644 --- a/dScripts/ai/GENERAL/CMakeLists.txt +++ b/dScripts/ai/GENERAL/CMakeLists.txt @@ -1,4 +1,5 @@ set(DSCRIPTS_SOURCES_AI_GENERAL + "ForceFieldEffect.cpp" "InstanceExitTransferPlayerToLastNonInstance.cpp" "LegoDieRoll.cpp") diff --git a/dScripts/ai/GENERAL/ForceFieldEffect.cpp b/dScripts/ai/GENERAL/ForceFieldEffect.cpp new file mode 100644 index 000000000..62d0eeb82 --- /dev/null +++ b/dScripts/ai/GENERAL/ForceFieldEffect.cpp @@ -0,0 +1,10 @@ +#include "ForceFieldEffect.h" + +#include "Entity.h" +#include "GameMessages.h" + +void ForceFieldEffect::OnCollisionPhantom(Entity* self, Entity* target) { + if (!target) return; + + GameMessages::SendPlayFXEffect(target, 3671, u"cast", "", LWOOBJID_EMPTY); +} diff --git a/dScripts/ai/GENERAL/ForceFieldEffect.h b/dScripts/ai/GENERAL/ForceFieldEffect.h new file mode 100644 index 000000000..2db6c26d2 --- /dev/null +++ b/dScripts/ai/GENERAL/ForceFieldEffect.h @@ -0,0 +1,8 @@ +#pragma once +#include "CppScripts.h" + +// Plays a sparkle effect on anything that touches this phantom physics object. +class ForceFieldEffect : public CppScripts::Script { +public: + void OnCollisionPhantom(Entity* self, Entity* target) override; +}; diff --git a/dScripts/ai/NP/CMakeLists.txt b/dScripts/ai/NP/CMakeLists.txt index b71bd1f8b..55b4b6ea3 100644 --- a/dScripts/ai/NP/CMakeLists.txt +++ b/dScripts/ai/NP/CMakeLists.txt @@ -1,4 +1,5 @@ set(DSCRIPTS_SOURCES_AI_NP + "NpcNpJetpackGuy.cpp" "NpcNpSpacemanBob.cpp") add_library(dScriptsAiNP OBJECT ${DSCRIPTS_SOURCES_AI_NP}) diff --git a/dScripts/ai/NP/NpcNpJetpackGuy.cpp b/dScripts/ai/NP/NpcNpJetpackGuy.cpp new file mode 100644 index 000000000..0e772ad1b --- /dev/null +++ b/dScripts/ai/NP/NpcNpJetpackGuy.cpp @@ -0,0 +1,9 @@ +#include "NpcNpJetpackGuy.h" + +#include "Entity.h" +#include "GameMessages.h" + +void NpcNpJetpackGuy::OnUse(Entity* self, Entity* user) { + // No effect ID, the client plays the object's own effect of this type. + GameMessages::SendPlayFXEffect(self, -1, u"launch", "", LWOOBJID_EMPTY); +} diff --git a/dScripts/ai/NP/NpcNpJetpackGuy.h b/dScripts/ai/NP/NpcNpJetpackGuy.h new file mode 100644 index 000000000..bff925104 --- /dev/null +++ b/dScripts/ai/NP/NpcNpJetpackGuy.h @@ -0,0 +1,8 @@ +#pragma once +#include "CppScripts.h" + +// Plays the launch effect when a player talks to the jetpack NPC. +class NpcNpJetpackGuy : public CppScripts::Script { +public: + void OnUse(Entity* self, Entity* user) override; +};