From c9a8be4fb9f977cb55b53096082bdcc5b7a65e9d Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Tue, 27 Feb 2024 06:40:49 -0800 Subject: [PATCH 01/10] Remove extra speed class (#1480) Speed is used in more waypoints than not so we may as well reduce repeated references. tested that the data is still loaded as normal in avant gardens Update Zone.cpp --- dGame/dComponents/MovingPlatformComponent.cpp | 4 ++-- dZoneManager/Zone.cpp | 4 ++-- dZoneManager/Zone.h | 7 +------ 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/dGame/dComponents/MovingPlatformComponent.cpp b/dGame/dComponents/MovingPlatformComponent.cpp index 4bcf78c0..77e90983 100644 --- a/dGame/dComponents/MovingPlatformComponent.cpp +++ b/dGame/dComponents/MovingPlatformComponent.cpp @@ -162,7 +162,7 @@ void MovingPlatformComponent::StartPathing() { const auto& nextWaypoint = m_Path->pathWaypoints[subComponent->mNextWaypointIndex]; subComponent->mPosition = currentWaypoint.position; - subComponent->mSpeed = currentWaypoint.movingPlatform.speed; + subComponent->mSpeed = currentWaypoint.speed; subComponent->mWaitTime = currentWaypoint.movingPlatform.wait; targetPosition = nextWaypoint.position; @@ -213,7 +213,7 @@ void MovingPlatformComponent::ContinuePathing() { const auto& nextWaypoint = m_Path->pathWaypoints[subComponent->mNextWaypointIndex]; subComponent->mPosition = currentWaypoint.position; - subComponent->mSpeed = currentWaypoint.movingPlatform.speed; + subComponent->mSpeed = currentWaypoint.speed; subComponent->mWaitTime = currentWaypoint.movingPlatform.wait; // + 2; pathSize = m_Path->pathWaypoints.size() - 1; diff --git a/dZoneManager/Zone.cpp b/dZoneManager/Zone.cpp index 42ce1215..38c3a1ee 100644 --- a/dZoneManager/Zone.cpp +++ b/dZoneManager/Zone.cpp @@ -419,7 +419,7 @@ void Zone::LoadPath(std::istream& file) { if (path.pathType == PathType::MovingPlatform) { BinaryIO::BinaryRead(file, waypoint.movingPlatform.lockPlayer); - BinaryIO::BinaryRead(file, waypoint.movingPlatform.speed); + BinaryIO::BinaryRead(file, waypoint.speed); BinaryIO::BinaryRead(file, waypoint.movingPlatform.wait); if (path.pathVersion >= 13) { BinaryIO::ReadString(file, waypoint.movingPlatform.departSound, BinaryIO::ReadType::WideString); @@ -438,7 +438,7 @@ void Zone::LoadPath(std::istream& file) { BinaryIO::BinaryRead(file, waypoint.racing.planeHeight); BinaryIO::BinaryRead(file, waypoint.racing.shortestDistanceToEnd); } else if (path.pathType == PathType::Rail) { - if (path.pathVersion > 16) BinaryIO::BinaryRead(file, waypoint.rail.speed); + if (path.pathVersion > 16) BinaryIO::BinaryRead(file, waypoint.speed); } // object LDF configs diff --git a/dZoneManager/Zone.h b/dZoneManager/Zone.h index a62a81ed..2f394510 100644 --- a/dZoneManager/Zone.h +++ b/dZoneManager/Zone.h @@ -40,7 +40,6 @@ struct SceneTransition { struct MovingPlatformPathWaypoint { uint8_t lockPlayer; - float speed; float wait; std::string departSound; std::string arriveSound; @@ -62,17 +61,13 @@ struct RacingPathWaypoint { float shortestDistanceToEnd; }; -struct RailPathWaypoint { - float speed; -}; - struct PathWaypoint { NiPoint3 position; NiQuaternion rotation; // not included in all, but it's more convenient here MovingPlatformPathWaypoint movingPlatform; CameraPathWaypoint camera; RacingPathWaypoint racing; - RailPathWaypoint rail; + float speed; std::vector config; }; From 366a80ffd236209b39f558df2ec887268428d7ce Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Tue, 27 Feb 2024 08:07:14 -0800 Subject: [PATCH 02/10] comments from movementAI branch (#1483) tests tested that red green and yellow bots waved when interacted with tested that construction robot races when interacted with wandering vendor does nothing before and after, but script is ready for use when npcs are implemented. add scripts for robot city --- dGame/dGameMessages/GameMessages.cpp | 10 +++--- dScripts/02_server/Map/AM/CMakeLists.txt | 4 ++- dScripts/02_server/Map/AM/WanderingVendor.cpp | 33 +++++++++++++++++++ dScripts/02_server/Map/AM/WanderingVendor.h | 13 ++++++++ dScripts/CppScripts.cpp | 15 +++++++-- dScripts/ai/WILD/CMakeLists.txt | 1 + dScripts/ai/WILD/LupGenericInteract.cpp | 6 ++++ dScripts/ai/WILD/LupGenericInteract.h | 12 +++++++ dScripts/zone/CMakeLists.txt | 1 + dScripts/zone/LUPs/CMakeLists.txt | 12 +++++-- .../zone/LUPs/RobotCity_Intro/CMakeLists.txt | 3 ++ .../LUPs/RobotCity_Intro/WblRobotCitizen.cpp | 24 ++++++++++++++ .../LUPs/RobotCity_Intro/WblRobotCitizen.h | 13 ++++++++ 13 files changed, 136 insertions(+), 11 deletions(-) create mode 100644 dScripts/02_server/Map/AM/WanderingVendor.cpp create mode 100644 dScripts/02_server/Map/AM/WanderingVendor.h create mode 100644 dScripts/ai/WILD/LupGenericInteract.cpp create mode 100644 dScripts/ai/WILD/LupGenericInteract.h create mode 100644 dScripts/zone/LUPs/RobotCity_Intro/CMakeLists.txt create mode 100644 dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.cpp create mode 100644 dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.h diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 77f4e908..55b6907e 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -322,8 +322,8 @@ void GameMessages::SendPlayNDAudioEmitter(Entity* entity, const SystemAddress& s bitStream.Write(entity->GetObjectID()); bitStream.Write(eGameMessageType::PLAY_ND_AUDIO_EMITTER); - bitStream.Write0(); - bitStream.Write0(); + bitStream.Write0(); // callback message data {lwoobjid} + bitStream.Write0(); // audio emitterid {uint32_t} uint32_t length = audioGUID.size(); bitStream.Write(length); @@ -331,9 +331,9 @@ void GameMessages::SendPlayNDAudioEmitter(Entity* entity, const SystemAddress& s bitStream.Write(audioGUID[k]); } - bitStream.Write(0); - bitStream.Write0(); - bitStream.Write0(); + bitStream.Write(0); // size of NDAudioMetaEventName (then print the string like the guid) + bitStream.Write0(); // result {bool} + bitStream.Write0(); // m_TargetObjectIDForNDAudioCallbackMessages {lwoobjid} SEND_PACKET_BROADCAST; } diff --git a/dScripts/02_server/Map/AM/CMakeLists.txt b/dScripts/02_server/Map/AM/CMakeLists.txt index 177b3c45..538879db 100644 --- a/dScripts/02_server/Map/AM/CMakeLists.txt +++ b/dScripts/02_server/Map/AM/CMakeLists.txt @@ -15,7 +15,9 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_AM "AmSkullkinDrillStand.cpp" "AmSkullkinTower.cpp" "AmBlueX.cpp" - "AmTeapotServer.cpp") + "AmTeapotServer.cpp" + "WanderingVendor.cpp" + ) add_library(dScriptsServerMapAM ${DSCRIPTS_SOURCES_02_SERVER_MAP_AM}) target_include_directories(dScriptsServerMapAM PUBLIC ".") diff --git a/dScripts/02_server/Map/AM/WanderingVendor.cpp b/dScripts/02_server/Map/AM/WanderingVendor.cpp new file mode 100644 index 00000000..ae49aa94 --- /dev/null +++ b/dScripts/02_server/Map/AM/WanderingVendor.cpp @@ -0,0 +1,33 @@ +#include "WanderingVendor.h" +#include "MovementAIComponent.h" +#include "ProximityMonitorComponent.h" + +void WanderingVendor::OnStartup(Entity* self) { + auto movementAIComponent = self->GetComponent(); + if (!movementAIComponent) return; + // movementAIComponent->Resume(); + self->SetProximityRadius(10, "playermonitor"); +} + +void WanderingVendor::OnProximityUpdate(Entity* self, Entity* entering, std::string name, std::string status) { + if (status == "ENTER" && entering->IsPlayer()) { + auto movementAIComponent = self->GetComponent(); + if (!movementAIComponent) return; + // movementAIComponent->Pause(); + self->CancelTimer("startWalking"); + } else if (status == "LEAVE") { + auto* proximityMonitorComponent = self->GetComponent(); + if (!proximityMonitorComponent) self->AddComponent(); + + const auto proxObjs = proximityMonitorComponent->GetProximityObjects("playermonitor"); + if (proxObjs.empty()) self->AddTimer("startWalking", 1.5); + } +} + +void WanderingVendor::OnTimerDone(Entity* self, std::string timerName) { + if (timerName == "startWalking") { + auto movementAIComponent = self->GetComponent(); + if (!movementAIComponent) return; + // movementAIComponent->Resume(); + } +} diff --git a/dScripts/02_server/Map/AM/WanderingVendor.h b/dScripts/02_server/Map/AM/WanderingVendor.h new file mode 100644 index 00000000..e0cb1645 --- /dev/null +++ b/dScripts/02_server/Map/AM/WanderingVendor.h @@ -0,0 +1,13 @@ +#ifndef __WANDERINGVENDOR__H__ +#define __WANDERINGVENDOR__H__ + +#include "CppScripts.h" + +class WanderingVendor : public CppScripts::Script { +public: + void OnStartup(Entity* self) override; + void OnProximityUpdate(Entity* self, Entity* entering, std::string name, std::string status) override; + void OnTimerDone(Entity* self, std::string timerName) override; +}; + +#endif //!__WANDERINGVENDOR__H__ diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index 7cb853e6..1b66b447 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -240,6 +240,7 @@ #include "AmDarklingDragon.h" #include "AmBlueX.h" #include "AmTeapotServer.h" +#include "WanderingVendor.h" // NJ Scripts #include "NjGarmadonCelebration.h" @@ -317,6 +318,8 @@ #include "WildNinjaSensei.h" #include "WildNinjaBricks.h" #include "VisToggleNotifierServer.h" +#include "LupGenericInteract.h" +#include "WblRobotCitizen.h" namespace { InvalidScript* invalidToReturn = new InvalidScript(); @@ -547,7 +550,7 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr //PR: else if (scriptName == "scripts\\client\\ai\\PR\\L_PR_WHISTLE.lua") script = new PrWhistle(); - else if (scriptName == "scripts\\02_server\\Map\\PR\\L_PR_SEAGULL_FLY.lua") + if (scriptName == "scripts\\02_server\\Map\\PR\\L_PR_SEAGULL_FLY.lua") script = new PrSeagullFly(); else if (scriptName == "scripts\\ai\\PETS\\L_HYDRANT_SMASHABLE.lua") script = new HydrantSmashable(); @@ -642,6 +645,8 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = new MailBoxServer(); else if (scriptName == "scripts\\ai\\ACT\\L_ACT_MINE.lua") script = new ActMine(); + else if (scriptName == "scripts\\02_server\\Map\\AM\\L_WANDERING_VENDOR.lua") + script = new WanderingVendor(); //Racing: else if (scriptName == "scripts\\ai\\RACING\\OBJECTS\\RACE_IMAGINE_CRATE_SERVER.lua") @@ -726,7 +731,7 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = new NTNaomiDirtServer(); //AM: - else if (scriptName == "scripts\\02_server\\Map\\AM\\L_AM_CONSOLE_TELEPORT_SERVER.lua") + if (scriptName == "scripts\\02_server\\Map\\AM\\L_AM_CONSOLE_TELEPORT_SERVER.lua") script = new AmConsoleTeleportServer(); else if (scriptName == "scripts\\02_server\\Map\\AM\\L_RANDOM_SPAWNER_FIN.lua") script = new RandomSpawnerFin(); @@ -806,7 +811,7 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = new Lieutenant(); else if (scriptName == "scripts\\02_server\\Map\\njhub\\L_RAIN_OF_ARROWS.lua") script = new RainOfArrows(); - else if (scriptName == "scripts\\02_server\\Map\\njhub\\L_CAVE_PRISON_CAGE.lua") + if (scriptName == "scripts\\02_server\\Map\\njhub\\L_CAVE_PRISON_CAGE.lua") script = new CavePrisonCage(); else if (scriptName == "scripts\\02_server\\Map\\njhub\\boss_instance\\L_MONASTERY_BOSS_INSTANCE_SERVER.lua") script = new NjMonastryBossInstance(); @@ -938,6 +943,10 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = new WildNinjaStudent(); else if (scriptName == "scripts\\ai\\WILD\\L_WILD_NINJA_SENSEI.lua") script = new WildNinjaSensei(); + else if (scriptName == "scripts\\ai\\WILD\\L_LUP_generic_interact.lua") + script = new LupGenericInteract(); + else if (scriptName.rfind("scripts\\zone\\LUPs\\RobotCity Intro\\WBL_RCIntro_RobotCitizen", 0) == 0) + script = new WblRobotCitizen(); // handle invalid script reporting if the path is greater than zero and it's not an ignored script // information not really needed for sys admins but is for developers diff --git a/dScripts/ai/WILD/CMakeLists.txt b/dScripts/ai/WILD/CMakeLists.txt index 418ffb5f..d9dc2126 100644 --- a/dScripts/ai/WILD/CMakeLists.txt +++ b/dScripts/ai/WILD/CMakeLists.txt @@ -1,5 +1,6 @@ set(DSCRIPTS_SOURCES_AI_WILD "AllCrateChicken.cpp" + "LupGenericInteract.cpp" "WildAmbients.cpp" "WildAmbientCrab.cpp" "WildAndScared.cpp" diff --git a/dScripts/ai/WILD/LupGenericInteract.cpp b/dScripts/ai/WILD/LupGenericInteract.cpp new file mode 100644 index 00000000..95eb7a96 --- /dev/null +++ b/dScripts/ai/WILD/LupGenericInteract.cpp @@ -0,0 +1,6 @@ +#include "LupGenericInteract.h" +#include "GameMessages.h" + +void LupGenericInteract::OnUse(Entity* self, Entity* user) { + GameMessages::SendPlayAnimation(self, u"interact"); +} diff --git a/dScripts/ai/WILD/LupGenericInteract.h b/dScripts/ai/WILD/LupGenericInteract.h new file mode 100644 index 00000000..68949bbf --- /dev/null +++ b/dScripts/ai/WILD/LupGenericInteract.h @@ -0,0 +1,12 @@ +#ifndef __LUCGENERICINTERACT__H__ +#define __LUCGENERICINTERACT__H__ + +#include "CppScripts.h" + +class LupGenericInteract : public CppScripts::Script { +public: + void OnUse(Entity* self, Entity* user) override; +}; + +#endif //!__LUCGENERICINTERACT__H__ + diff --git a/dScripts/zone/CMakeLists.txt b/dScripts/zone/CMakeLists.txt index 93ea70ca..cfd1f83d 100644 --- a/dScripts/zone/CMakeLists.txt +++ b/dScripts/zone/CMakeLists.txt @@ -22,6 +22,7 @@ add_library(dScriptsZone STATIC ${DSCRIPTS_SOURCES_ZONE}) target_include_directories(dScriptsZone PUBLIC "." "AG" "LUPs" + "LUPs/RobotCity_Intro" "PROPERTY" "PROPERTY/FV" "PROPERTY/GF" diff --git a/dScripts/zone/LUPs/CMakeLists.txt b/dScripts/zone/LUPs/CMakeLists.txt index b3b55ad6..7ce84fcc 100644 --- a/dScripts/zone/LUPs/CMakeLists.txt +++ b/dScripts/zone/LUPs/CMakeLists.txt @@ -1,3 +1,11 @@ -set(DSCRIPTS_SOURCES_ZONE_LUPS +set(DSCRIPTS_SOURCES_ZONE_LUPS "WblGenericZone.cpp" - PARENT_SCOPE) +) + +add_subdirectory(RobotCity_Intro) + +foreach(file ${DSCRIPTS_SOURCES_ZONE_LUPS_ROBOTCITYINTRO}) + set(DSCRIPTS_SOURCES_ZONE_LUPS ${DSCRIPTS_SOURCES_ZONE_LUPS} "RobotCity_Intro/${file}") +endforeach() + +set(DSCRIPTS_SOURCES_ZONE_LUPS ${DSCRIPTS_SOURCES_ZONE_LUPS} PARENT_SCOPE) diff --git a/dScripts/zone/LUPs/RobotCity_Intro/CMakeLists.txt b/dScripts/zone/LUPs/RobotCity_Intro/CMakeLists.txt new file mode 100644 index 00000000..fae8793b --- /dev/null +++ b/dScripts/zone/LUPs/RobotCity_Intro/CMakeLists.txt @@ -0,0 +1,3 @@ +set(DSCRIPTS_SOURCES_ZONE_LUPS_ROBOTCITYINTRO + "WblRobotCitizen.cpp" + PARENT_SCOPE) diff --git a/dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.cpp b/dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.cpp new file mode 100644 index 00000000..93bf2576 --- /dev/null +++ b/dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.cpp @@ -0,0 +1,24 @@ +#include "WblRobotCitizen.h" +#include "MovementAIComponent.h" +#include "RenderComponent.h" + +void WblRobotCitizen::OnStartup(Entity* self) { + auto movementAIComponent = self->GetComponent(); + if (!movementAIComponent) return; + // movementAIComponent->Resume(); +} + +void WblRobotCitizen::OnUse(Entity* self, Entity* user) { + // auto movementAIComponent = self->GetComponent(); + // if (!movementAIComponent) movementAIComponent->Pause(); + auto face = NiQuaternion::LookAt(self->GetPosition(), user->GetPosition()); + self->SetRotation(face); + auto timer = RenderComponent::PlayAnimation(self, "wave"); + self->AddTimer("animation time", timer); +} + +void WblRobotCitizen::OnTimerDone(Entity* self, std::string timerName) { + auto movementAIComponent = self->GetComponent(); + if (!movementAIComponent) return; + // movementAIComponent->Resume(); +} diff --git a/dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.h b/dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.h new file mode 100644 index 00000000..2f720764 --- /dev/null +++ b/dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.h @@ -0,0 +1,13 @@ +#ifndef __WBLROBOTCITIZEN__H__ +#define __WBLROBOTCITIZEN__H__ + +#include "CppScripts.h" + +class WblRobotCitizen : public CppScripts::Script { +public: + void OnStartup(Entity* self) override; + void OnUse(Entity* self, Entity* user) override; + void OnTimerDone(Entity* self, std::string timerName) override; +}; + +#endif //!__WBLROBOTCITIZEN__H__ From 398426545c56ebb0defb7a824c1b26d8d008c8e3 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 27 Feb 2024 15:56:58 -0600 Subject: [PATCH 03/10] fix: default chat to the correct port when no option is given (#1484) --- dChatServer/ChatServer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index 9ba70026..84104726 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -101,7 +101,7 @@ int main(int argc, char** argv) { //It's safe to pass 'localhost' here, as the IP is only used as the external IP. std::string ourIP = "localhost"; const uint32_t maxClients = GeneralUtils::TryParse(Game::config->GetValue("max_clients")).value_or(999); - const uint32_t ourPort = GeneralUtils::TryParse(Game::config->GetValue("chat_server_port")).value_or(1501); + const uint32_t ourPort = GeneralUtils::TryParse(Game::config->GetValue("chat_server_port")).value_or(2005); const auto externalIPString = Game::config->GetValue("external_ip"); if (!externalIPString.empty()) ourIP = externalIPString; From ef3fdba6211fd0279f9360b954e7509a108df2ff Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Tue, 27 Feb 2024 21:40:26 -0800 Subject: [PATCH 04/10] fix: FrameStats serialization (#1481) * fix serialization Was incorrect before. The only flags are if any data in the FrameStats has changed, you write them again. Velocities also do not use dirty flags for their values, they use a flag to determine if their velocity if zero or non-zero. if any velocity changes, re-write FrameStats. Tested that 2 players can see each other move as before, enemies move as before and players racing is identical as before. * Update HavokVehiclePhysicsComponent.cpp --- dCommon/PositionUpdate.h | 22 ++------ dGame/Entity.cpp | 8 +-- .../ControllablePhysicsComponent.cpp | 51 +++++++----------- .../ControllablePhysicsComponent.h | 22 -------- .../HavokVehiclePhysicsComponent.cpp | 53 ++++++------------- .../HavokVehiclePhysicsComponent.h | 10 +--- 6 files changed, 41 insertions(+), 125 deletions(-) diff --git a/dCommon/PositionUpdate.h b/dCommon/PositionUpdate.h index 4d591a97..f28c682d 100644 --- a/dCommon/PositionUpdate.h +++ b/dCommon/PositionUpdate.h @@ -6,28 +6,14 @@ struct RemoteInputInfo { - RemoteInputInfo() { - m_RemoteInputX = 0; - m_RemoteInputY = 0; - m_IsPowersliding = false; - m_IsModified = false; - } - - void operator=(const RemoteInputInfo& other) { - m_RemoteInputX = other.m_RemoteInputX; - m_RemoteInputY = other.m_RemoteInputY; - m_IsPowersliding = other.m_IsPowersliding; - m_IsModified = other.m_IsModified; - } - bool operator==(const RemoteInputInfo& other) { return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified; } - float m_RemoteInputX; - float m_RemoteInputY; - bool m_IsPowersliding; - bool m_IsModified; + float m_RemoteInputX = 0; + float m_RemoteInputY = 0; + bool m_IsPowersliding = false; + bool m_IsModified = false; }; struct LocalSpaceInfo { diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 55b2b85f..be8d8b10 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -2126,9 +2126,7 @@ void Entity::ProcessPositionUpdate(PositionUpdate& update) { havokVehiclePhysicsComponent->SetIsOnGround(update.onGround); havokVehiclePhysicsComponent->SetIsOnRail(update.onRail); havokVehiclePhysicsComponent->SetVelocity(update.velocity); - havokVehiclePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3Constant::ZERO); havokVehiclePhysicsComponent->SetAngularVelocity(update.angularVelocity); - havokVehiclePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3Constant::ZERO); havokVehiclePhysicsComponent->SetRemoteInputInfo(update.remoteInputInfo); } else { // Need to get the mount's controllable physics @@ -2139,9 +2137,7 @@ void Entity::ProcessPositionUpdate(PositionUpdate& update) { possessedControllablePhysicsComponent->SetIsOnGround(update.onGround); possessedControllablePhysicsComponent->SetIsOnRail(update.onRail); possessedControllablePhysicsComponent->SetVelocity(update.velocity); - possessedControllablePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3Constant::ZERO); possessedControllablePhysicsComponent->SetAngularVelocity(update.angularVelocity); - possessedControllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3Constant::ZERO); } Game::entityManager->SerializeEntity(possassableEntity); } @@ -2163,9 +2159,7 @@ void Entity::ProcessPositionUpdate(PositionUpdate& update) { controllablePhysicsComponent->SetIsOnGround(update.onGround); controllablePhysicsComponent->SetIsOnRail(update.onRail); controllablePhysicsComponent->SetVelocity(update.velocity); - controllablePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3Constant::ZERO); controllablePhysicsComponent->SetAngularVelocity(update.angularVelocity); - controllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3Constant::ZERO); auto* ghostComponent = GetComponent(); if (ghostComponent) ghostComponent->SetGhostReferencePoint(update.position); @@ -2202,4 +2196,4 @@ void Entity::SetScale(const float scale) { if (scale == m_Scale) return; m_Scale = scale; Game::entityManager->SerializeEntity(this); -} \ No newline at end of file +} diff --git a/dGame/dComponents/ControllablePhysicsComponent.cpp b/dGame/dComponents/ControllablePhysicsComponent.cpp index daa14ac3..99b51af8 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.cpp +++ b/dGame/dComponents/ControllablePhysicsComponent.cpp @@ -21,8 +21,6 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Phy m_InJetpackMode = false; m_IsOnGround = true; m_IsOnRail = false; - m_DirtyVelocity = true; - m_DirtyAngularVelocity = true; m_dpEntity = nullptr; m_Static = false; m_SpeedMultiplier = 1; @@ -135,26 +133,28 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo outBitStream.Write(m_IsOnGround); outBitStream.Write(m_IsOnRail); - outBitStream.Write(m_DirtyVelocity); - if (m_DirtyVelocity) { + bool isNotZero = m_Velocity != NiPoint3Constant::ZERO; + outBitStream.Write(isNotZero); + if (isNotZero) { outBitStream.Write(m_Velocity.x); outBitStream.Write(m_Velocity.y); outBitStream.Write(m_Velocity.z); } - outBitStream.Write(m_DirtyAngularVelocity); - if (m_DirtyAngularVelocity) { + isNotZero = m_AngularVelocity != NiPoint3Constant::ZERO; + outBitStream.Write(isNotZero); + if (isNotZero) { outBitStream.Write(m_AngularVelocity.x); outBitStream.Write(m_AngularVelocity.y); outBitStream.Write(m_AngularVelocity.z); } outBitStream.Write0(); - } - - if (!bIsInitialUpdate) { - outBitStream.Write(m_IsTeleporting); - m_IsTeleporting = false; + if (!bIsInitialUpdate) { + m_DirtyPosition = false; + outBitStream.Write(m_IsTeleporting); + m_IsTeleporting = false; + } } } @@ -211,33 +211,29 @@ void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) { } void ControllablePhysicsComponent::SetVelocity(const NiPoint3& vel) { - if (m_Static) { - return; - } + if (m_Static || m_Velocity == vel) return; m_Velocity = vel; m_DirtyPosition = true; - m_DirtyVelocity = true; if (m_dpEntity) m_dpEntity->SetVelocity(vel); } void ControllablePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) { - if (m_Static) { - return; - } + if (m_Static || m_AngularVelocity == vel) return; m_AngularVelocity = vel; m_DirtyPosition = true; - m_DirtyAngularVelocity = true; } void ControllablePhysicsComponent::SetIsOnGround(bool val) { + if (m_IsOnGround == val) return; m_DirtyPosition = true; m_IsOnGround = val; } void ControllablePhysicsComponent::SetIsOnRail(bool val) { + if (m_IsOnRail == val) return; m_DirtyPosition = true; m_IsOnRail = val; } @@ -245,15 +241,6 @@ void ControllablePhysicsComponent::SetIsOnRail(bool val) { void ControllablePhysicsComponent::SetDirtyPosition(bool val) { m_DirtyPosition = val; } - -void ControllablePhysicsComponent::SetDirtyVelocity(bool val) { - m_DirtyVelocity = val; -} - -void ControllablePhysicsComponent::SetDirtyAngularVelocity(bool val) { - m_DirtyAngularVelocity = val; -} - void ControllablePhysicsComponent::AddPickupRadiusScale(float value) { m_ActivePickupRadiusScales.push_back(value); if (value > m_PickupRadius) { @@ -309,7 +296,7 @@ void ControllablePhysicsComponent::RemoveSpeedboost(float value) { Game::entityManager->SerializeEntity(m_Parent); } -void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bool specialAnims){ +void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bool specialAnims) { if (m_IsInBubble) { LOG("Already in bubble"); return; @@ -321,7 +308,7 @@ void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bo Game::entityManager->SerializeEntity(m_Parent); } -void ControllablePhysicsComponent::DeactivateBubbleBuff(){ +void ControllablePhysicsComponent::DeactivateBubbleBuff() { m_DirtyBubble = true; m_IsInBubble = false; Game::entityManager->SerializeEntity(m_Parent); @@ -336,9 +323,9 @@ void ControllablePhysicsComponent::SetStunImmunity( const bool bImmuneToStunJump, const bool bImmuneToStunMove, const bool bImmuneToStunTurn, - const bool bImmuneToStunUseItem){ + const bool bImmuneToStunUseItem) { - if (state == eStateChangeType::POP){ + if (state == eStateChangeType::POP) { if (bImmuneToStunAttack && m_ImmuneToStunAttackCount > 0) m_ImmuneToStunAttackCount -= 1; if (bImmuneToStunEquip && m_ImmuneToStunEquipCount > 0) m_ImmuneToStunEquipCount -= 1; if (bImmuneToStunInteract && m_ImmuneToStunInteractCount > 0) m_ImmuneToStunInteractCount -= 1; diff --git a/dGame/dComponents/ControllablePhysicsComponent.h b/dGame/dComponents/ControllablePhysicsComponent.h index 313f80de..a0cc6aef 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.h +++ b/dGame/dComponents/ControllablePhysicsComponent.h @@ -104,18 +104,6 @@ public: */ void SetDirtyPosition(bool val); - /** - * Mark the velocity as dirty, forcing a serializtion update next tick - * @param val whether or not the velocity is dirty - */ - void SetDirtyVelocity(bool val); - - /** - * Mark the angular velocity as dirty, forcing a serialization update next tick - * @param val whether or not the angular velocity is dirty - */ - void SetDirtyAngularVelocity(bool val); - /** * Sets whether or not the entity is currently wearing a jetpack * @param val whether or not the entity is currently wearing a jetpack @@ -310,21 +298,11 @@ private: */ dpEntity* m_dpEntity; - /** - * Whether or not the velocity is dirty, forcing a serialization of the velocity - */ - bool m_DirtyVelocity; - /** * The current velocity of the entity */ NiPoint3 m_Velocity; - /** - * Whether or not the angular velocity is dirty, forcing a serialization - */ - bool m_DirtyAngularVelocity; - /** * The current angular velocity of the entity */ diff --git a/dGame/dComponents/HavokVehiclePhysicsComponent.cpp b/dGame/dComponents/HavokVehiclePhysicsComponent.cpp index 464cb357..635830cc 100644 --- a/dGame/dComponents/HavokVehiclePhysicsComponent.cpp +++ b/dGame/dComponents/HavokVehiclePhysicsComponent.cpp @@ -7,8 +7,6 @@ HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent) : Phy m_IsOnGround = true; m_IsOnRail = false; m_DirtyPosition = true; - m_DirtyVelocity = true; - m_DirtyAngularVelocity = true; m_EndBehavior = GeneralUtils::GenerateRandomNumber(0, 7); } @@ -37,17 +35,9 @@ void HavokVehiclePhysicsComponent::SetIsOnRail(bool val) { } void HavokVehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) { - if (m_RemoteInputInfo == remoteInputInfo) return; + if (remoteInputInfo == m_RemoteInputInfo) return; this->m_RemoteInputInfo = remoteInputInfo; - m_DirtyRemoteInput = true; -} - -void HavokVehiclePhysicsComponent::SetDirtyVelocity(bool val) { - m_DirtyVelocity = val; -} - -void HavokVehiclePhysicsComponent::SetDirtyAngularVelocity(bool val) { - m_DirtyAngularVelocity = val; + m_DirtyPosition = true; } void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) { @@ -67,34 +57,32 @@ void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo outBitStream.Write(m_IsOnGround); outBitStream.Write(m_IsOnRail); - outBitStream.Write(bIsInitialUpdate || m_DirtyVelocity); - - if (bIsInitialUpdate || m_DirtyVelocity) { + bool isNotZero = m_Velocity != NiPoint3Constant::ZERO; + outBitStream.Write(isNotZero); + if (isNotZero) { outBitStream.Write(m_Velocity.x); outBitStream.Write(m_Velocity.y); outBitStream.Write(m_Velocity.z); - m_DirtyVelocity = false; } - outBitStream.Write(bIsInitialUpdate || m_DirtyAngularVelocity); - - if (bIsInitialUpdate || m_DirtyAngularVelocity) { + isNotZero = m_AngularVelocity != NiPoint3Constant::ZERO; + outBitStream.Write(isNotZero); + if (isNotZero) { outBitStream.Write(m_AngularVelocity.x); outBitStream.Write(m_AngularVelocity.y); outBitStream.Write(m_AngularVelocity.z); - m_DirtyAngularVelocity = false; } outBitStream.Write0(); // local_space_info. TODO: Implement this - outBitStream.Write(m_DirtyRemoteInput || bIsInitialUpdate); // remote_input_info - if (m_DirtyRemoteInput || bIsInitialUpdate) { - outBitStream.Write(m_RemoteInputInfo.m_RemoteInputX); - outBitStream.Write(m_RemoteInputInfo.m_RemoteInputY); - outBitStream.Write(m_RemoteInputInfo.m_IsPowersliding); - outBitStream.Write(m_RemoteInputInfo.m_IsModified); - m_DirtyRemoteInput = false; - } + // This structure only has this bool flag set to false if a ptr to the peVehicle is null, which we don't have + // therefore, this will always be 1, even if all the values in the structure are 0. + outBitStream.Write1(); // has remote_input_info + outBitStream.Write(m_RemoteInputInfo.m_RemoteInputX); + outBitStream.Write(m_RemoteInputInfo.m_RemoteInputY); + outBitStream.Write(m_RemoteInputInfo.m_IsPowersliding); + outBitStream.Write(m_RemoteInputInfo.m_IsModified); + outBitStream.Write(125.0f); // remote_input_ping TODO: Figure out how this should be calculated as it seems to be constant through the whole race. @@ -110,12 +98,3 @@ void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo outBitStream.Write0(); } - -void HavokVehiclePhysicsComponent::Update(float deltaTime) { - if (m_SoftUpdate > 5) { - Game::entityManager->SerializeEntity(m_Parent); - m_SoftUpdate = 0; - } else { - m_SoftUpdate += deltaTime; - } -} diff --git a/dGame/dComponents/HavokVehiclePhysicsComponent.h b/dGame/dComponents/HavokVehiclePhysicsComponent.h index 94c14ec6..83eb82fe 100644 --- a/dGame/dComponents/HavokVehiclePhysicsComponent.h +++ b/dGame/dComponents/HavokVehiclePhysicsComponent.h @@ -17,8 +17,6 @@ public: void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; - void Update(float deltaTime) override; - /** * Sets the velocity * @param vel the new velocity @@ -67,22 +65,16 @@ public: */ const bool GetIsOnRail() const { return m_IsOnRail; } - void SetDirtyPosition(bool val); - void SetDirtyVelocity(bool val); - void SetDirtyAngularVelocity(bool val); void SetRemoteInputInfo(const RemoteInputInfo&); private: - bool m_DirtyVelocity; NiPoint3 m_Velocity; - - bool m_DirtyAngularVelocity; NiPoint3 m_AngularVelocity; + bool m_IsOnGround; bool m_IsOnRail; float m_SoftUpdate = 0; uint32_t m_EndBehavior; RemoteInputInfo m_RemoteInputInfo; - bool m_DirtyRemoteInput; }; From 43707952d2928796b5c0c4ddf4f20dc1c30ee285 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Wed, 28 Feb 2024 17:16:47 -0600 Subject: [PATCH 05/10] feat: move all ldf config to be in xml (#1482) * feat: move all ldf config to be in xml cleanup dev-tribute.xml add comments to atm.xml remove custom script tag in favor of ldfconfig for it * replace sto* calls with tryParse's * remove unesessary .has_value() calls and check for null_lot * remove member variable naming that on on-member vars * move max's vendor inventory to be configurable via vanity * Consolidate triplecated vendor code * don't write name if one is not given * Updates to vanity xml's and demo for later docs * rename vars --- CMakeLists.txt | 2 +- dGame/Entity.cpp | 6 - dGame/Entity.h | 3 +- dGame/dComponents/VendorComponent.cpp | 73 +- dGame/dComponents/VendorComponent.h | 2 +- dGame/dUtilities/VanityUtilities.cpp | 184 ++-- dGame/dUtilities/VanityUtilities.h | 42 +- .../DLU/DLUVanityTeleportingObject.cpp | 10 +- vanity/atm.xml | 59 +- vanity/demo.xml | 132 +++ vanity/dev-tribute.xml | 809 ++++++++++-------- vanity/root.xml | 5 +- 12 files changed, 745 insertions(+), 582 deletions(-) create mode 100644 vanity/demo.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index b36bdb29..3ab0a524 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -179,7 +179,7 @@ file(ARCHIVE_EXTRACT INPUT ${PROJECT_BINARY_DIR}/navmeshes.zip DESTINATION ${PRO file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip) # Copy vanity files on first build -set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "root.xml" "dev-tribute.xml" "atm.xml") +set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "root.xml" "dev-tribute.xml" "atm.xml" "demo.xml") foreach(file ${VANITY_FILES}) configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY) diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index be8d8b10..2611f1dd 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -2191,9 +2191,3 @@ void Entity::SetRespawnRot(const NiQuaternion& rotation) { auto* characterComponent = GetComponent(); if (characterComponent) characterComponent->SetRespawnRot(rotation); } - -void Entity::SetScale(const float scale) { - if (scale == m_Scale) return; - m_Scale = scale; - Game::entityManager->SerializeEntity(this); -} diff --git a/dGame/Entity.h b/dGame/Entity.h index 711e0b63..a2d4d132 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -295,7 +295,8 @@ public: void ProcessPositionUpdate(PositionUpdate& update); - void SetScale(const float scale); + // Scale will only be communicated to the client when the construction packet is sent + void SetScale(const float scale) { m_Scale = scale; }; protected: LWOOBJID m_ObjectID; diff --git a/dGame/dComponents/VendorComponent.cpp b/dGame/dComponents/VendorComponent.cpp index 8a26399c..abe11ea5 100644 --- a/dGame/dComponents/VendorComponent.cpp +++ b/dGame/dComponents/VendorComponent.cpp @@ -40,9 +40,19 @@ void VendorComponent::RefreshInventory(bool isCreation) { SetHasMultiCostItems(false); m_Inventory.clear(); - // Custom code for Max vanity NPC and Mr.Ree cameras - if(isCreation && m_Parent->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) { - SetupMaxCustomVendor(); + // Custom code for Vanity Vendor Invetory Override + if(m_Parent->HasVar(u"vendorInvOverride")) { + std::vector items = GeneralUtils::SplitString(m_Parent->GetVarAsString(u"vendorInvOverride"), ','); + uint32_t sortPriority = -1; + for (auto& itemString : items) { + itemString.erase(remove_if(itemString.begin(), itemString.end(), isspace), itemString.end()); + auto item = GeneralUtils::TryParse(itemString); + if (!item) continue; + if (SetupItem(item.value())) { + sortPriority++; + m_Inventory.push_back(SoldItem(item.value(), sortPriority)); + } + } return; } @@ -52,24 +62,12 @@ void VendorComponent::RefreshInventory(bool isCreation) { if (lootMatrices.empty()) return; auto* lootTableTable = CDClientManager::GetTable(); - auto* itemComponentTable = CDClientManager::GetTable(); - auto* compRegistryTable = CDClientManager::GetTable(); for (const auto& lootMatrix : lootMatrices) { auto vendorItems = lootTableTable->GetTable(lootMatrix.LootTableIndex); if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) { for (const auto& item : vendorItems) { - if (!m_HasStandardCostItems || !m_HasMultiCostItems) { - auto itemComponentID = compRegistryTable->GetByIDAndType(item.itemid, eReplicaComponentType::ITEM, -1); - if (itemComponentID == -1) { - LOG("Attempted to add item %i with ItemComponent ID -1 to vendor %i inventory. Not adding item!", itemComponentID, m_Parent->GetLOT()); - continue; - } - auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID); - if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true); - if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true); - } - m_Inventory.push_back(SoldItem(item.itemid, item.sortPriority)); + if (SetupItem(item.itemid)) m_Inventory.push_back(SoldItem(item.itemid, item.sortPriority)); } } else { auto randomCount = GeneralUtils::GenerateRandomNumber(lootMatrix.minToDrop, lootMatrix.maxToDrop); @@ -79,17 +77,7 @@ void VendorComponent::RefreshInventory(bool isCreation) { auto randomItemIndex = GeneralUtils::GenerateRandomNumber(0, vendorItems.size() - 1); const auto& randomItem = vendorItems.at(randomItemIndex); vendorItems.erase(vendorItems.begin() + randomItemIndex); - if (!m_HasStandardCostItems || !m_HasMultiCostItems) { - auto itemComponentID = compRegistryTable->GetByIDAndType(randomItem.itemid, eReplicaComponentType::ITEM, -1); - if (itemComponentID == -1) { - LOG("Attempted to add item %i with ItemComponent ID -1 to vendor %i inventory. Not adding item!", itemComponentID, m_Parent->GetLOT()); - continue; - } - auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID); - if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true); - if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true); - } - m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority)); + if (SetupItem(randomItem.itemid)) m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority)); } } } @@ -126,15 +114,6 @@ bool VendorComponent::SellsItem(const LOT item) const { }) > 0; } - -void VendorComponent::SetupMaxCustomVendor(){ - SetHasStandardCostItems(true); - m_Inventory.push_back(SoldItem(11909, 0)); // Top hat w frog - m_Inventory.push_back(SoldItem(7785, 0)); // Flash bulb - m_Inventory.push_back(SoldItem(12764, 0)); // Big fountain soda - m_Inventory.push_back(SoldItem(12241, 0)); // Hot cocoa (from fb) -} - void VendorComponent::HandleMrReeCameras(){ if (m_Parent->GetLOT() == 13569) { SetHasStandardCostItems(true); @@ -211,5 +190,25 @@ void VendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) { character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::VENDOR); inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR); GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS); +} + +bool VendorComponent::SetupItem(LOT item) { + + auto* itemComponentTable = CDClientManager::GetTable(); + auto* compRegistryTable = CDClientManager::GetTable(); + + auto itemComponentID = compRegistryTable->GetByIDAndType(item, eReplicaComponentType::ITEM, -1); + if (itemComponentID == -1) { + LOG("Attempted to add item %i with ItemComponent ID -1 to vendor %i inventory. Not adding item!", itemComponentID, m_Parent->GetLOT()); + return false; + } + + if (!m_HasStandardCostItems || !m_HasMultiCostItems) { + auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID); + if (!m_HasStandardCostItems && itemComponent.baseValue != -1) SetHasStandardCostItems(true); + if (!m_HasMultiCostItems && !itemComponent.currencyCosts.empty()) SetHasMultiCostItems(true); + } + + return true; +} -} \ No newline at end of file diff --git a/dGame/dComponents/VendorComponent.h b/dGame/dComponents/VendorComponent.h index 2abd3536..b5e9f5d0 100644 --- a/dGame/dComponents/VendorComponent.h +++ b/dGame/dComponents/VendorComponent.h @@ -50,8 +50,8 @@ public: void Buy(Entity* buyer, LOT lot, uint32_t count); private: - void SetupMaxCustomVendor(); void HandleMrReeCameras(); + bool SetupItem(LOT item); float m_BuyScalar = 0.0f; float m_SellScalar = 0.0f; float m_RefreshTimeSeconds = 0.0f; diff --git a/dGame/dUtilities/VanityUtilities.cpp b/dGame/dUtilities/VanityUtilities.cpp index 3e93f830..0324ee1b 100644 --- a/dGame/dUtilities/VanityUtilities.cpp +++ b/dGame/dUtilities/VanityUtilities.cpp @@ -22,9 +22,18 @@ #include -std::vector VanityUtilities::m_Objects = {}; -std::set VanityUtilities::m_LoadedFiles = {}; +namespace { + std::vector objects; + std::set loadedFiles; +} + +void SetupNPCTalk(Entity* npc); +void NPCTalk(Entity* npc); +void ParseXml(const std::string& file); +LWOOBJID SpawnSpawner(const VanityObject& object, const VanityObjectLocation& location); +Entity* SpawnObject(const VanityObject& object, const VanityObjectLocation& location); +VanityObject* GetObject(const std::string& name); void VanityUtilities::SpawnVanity() { const uint32_t zoneID = Game::server->GetZoneID(); @@ -36,21 +45,19 @@ void VanityUtilities::SpawnVanity() { info.pos = { 259.5f, 246.4f, -705.2f }; info.rot = { 0.0f, 0.0f, 1.0f, 0.0f }; info.spawnerID = Game::entityManager->GetZoneControlEntity()->GetObjectID(); - - info.settings = { new LDFData(u"hasCustomText", true), - new LDFData(u"customText", ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/TESTAMENT.md").string())) }; + info.settings = { + new LDFData(u"hasCustomText", true), + new LDFData(u"customText", ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/TESTAMENT.md").string())) + }; auto* entity = Game::entityManager->CreateEntity(info); - Game::entityManager->ConstructEntity(entity); } } - if (Game::config->GetValue("disable_vanity") == "1") { - return; - } + if (Game::config->GetValue("disable_vanity") == "1") return; - for (const auto& npc : m_Objects) { + for (const auto& npc : objects) { if (npc.m_ID == LWOOBJID_EMPTY) continue; if (npc.m_LOT == 176){ Game::zoneManager->RemoveSpawner(npc.m_ID); @@ -61,13 +68,13 @@ void VanityUtilities::SpawnVanity() { } } - m_Objects.clear(); - m_LoadedFiles.clear(); + objects.clear(); + loadedFiles.clear(); - ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity/root.xml").string()); + ParseXml((BinaryPathFinder::GetBinaryDir() / "vanity/root.xml").string()); // Loop through all objects - for (auto& object : m_Objects) { + for (auto& object : objects) { if (object.m_Locations.find(Game::server->GetZoneID()) == object.m_Locations.end()) continue; const std::vector& locations = object.m_Locations.at(Game::server->GetZoneID()); @@ -79,12 +86,6 @@ void VanityUtilities::SpawnVanity() { float rate = GeneralUtils::GenerateRandomNumber(0, 1); if (location.m_Chance < rate) continue; - if (object.m_Config.empty()) { - object.m_Config = { - new LDFData>(u"syncLDF", { u"custom_script_client" }), - new LDFData(u"custom_script_client", u"scripts\\ai\\SPEC\\MISSION_MINIGAME_CLIENT.lua") - }; - } if (object.m_LOT == 176){ object.m_ID = SpawnSpawner(object, location); } else { @@ -94,20 +95,13 @@ void VanityUtilities::SpawnVanity() { object.m_ID = objectEntity->GetObjectID(); if (!object.m_Phrases.empty()){ objectEntity->SetVar>(u"chats", object.m_Phrases); - - auto* scriptComponent = objectEntity->GetComponent(); - - if (scriptComponent && !object.m_Script.empty()) { - scriptComponent->SetScript(object.m_Script); - scriptComponent->SetSerialized(false); - } SetupNPCTalk(objectEntity); } } } } -LWOOBJID VanityUtilities::SpawnSpawner(const VanityObject& object, const VanityObjectLocation& location) { +LWOOBJID SpawnSpawner(const VanityObject& object, const VanityObjectLocation& location) { SceneObject obj; obj.lot = object.m_LOT; // guratantee we have no collisions @@ -121,7 +115,7 @@ LWOOBJID VanityUtilities::SpawnSpawner(const VanityObject& object, const VanityO return obj.id; } -Entity* VanityUtilities::SpawnObject(const VanityObject& object, const VanityObjectLocation& location) { +Entity* SpawnObject(const VanityObject& object, const VanityObjectLocation& location) { EntityInfo info; info.lot = object.m_LOT; info.pos = location.m_Position; @@ -131,18 +125,16 @@ Entity* VanityUtilities::SpawnObject(const VanityObject& object, const VanityObj info.settings = object.m_Config; auto* entity = Game::entityManager->CreateEntity(info); - entity->SetVar(u"npcName", object.m_Name); + if (!object.m_Name.empty()) entity->SetVar(u"npcName", object.m_Name); if (entity->GetVar(u"noGhosting")) entity->SetIsGhostingCandidate(false); auto* inventoryComponent = entity->GetComponent(); - if (inventoryComponent && !object.m_Equipment.empty()) { inventoryComponent->SetNPCItems(object.m_Equipment); } auto* destroyableComponent = entity->GetComponent(); - - if (destroyableComponent != nullptr) { + if (destroyableComponent) { destroyableComponent->SetIsGMImmune(true); destroyableComponent->SetMaxHealth(0); destroyableComponent->SetHealth(0); @@ -153,12 +145,12 @@ Entity* VanityUtilities::SpawnObject(const VanityObject& object, const VanityObj return entity; } -void VanityUtilities::ParseXML(const std::string& file) { - if (m_LoadedFiles.contains(file)){ +void ParseXml(const std::string& file) { + if (loadedFiles.contains(file)){ LOG("Trying to load vanity file %s twice!!!", file.c_str()); return; } - m_LoadedFiles.insert(file); + loadedFiles.insert(file); // Read the entire file std::ifstream xmlFile(file); std::string xml((std::istreambuf_iterator(xmlFile)), std::istreambuf_iterator()); @@ -176,24 +168,26 @@ void VanityUtilities::ParseXML(const std::string& file) { if (enabled != "1") { continue; } - ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity" / filename).string()); + ParseXml((BinaryPathFinder::GetBinaryDir() / "vanity" / filename).string()); } } // Read the objects - auto* objects = doc.FirstChildElement("objects"); - - if (objects) { - for (auto* object = objects->FirstChildElement("object"); object != nullptr; object = object->NextSiblingElement("object")) { + auto* objectsElement = doc.FirstChildElement("objects"); + const uint32_t currentZoneID = Game::server->GetZoneID(); + if (objectsElement) { + for (auto* object = objectsElement->FirstChildElement("object"); object != nullptr; object = object->NextSiblingElement("object")) { + // for use later when adding to the vector of VanityObjects + bool useLocationsAsRandomSpawnPoint = false; // Get the NPC name auto* name = object->Attribute("name"); if (!name) name = ""; // Get the NPC lot - auto* lot = object->Attribute("lot"); + auto lot = GeneralUtils::TryParse(object->Attribute("lot")).value_or(LOT_NULL); - if (lot == nullptr) { + if (lot == LOT_NULL) { LOG("Failed to parse object lot"); continue; } @@ -211,17 +205,17 @@ void VanityUtilities::ParseXML(const std::string& file) { std::vector splitEquipment = GeneralUtils::SplitString(equipmentString, ','); for (auto& item : splitEquipment) { - inventory.push_back(std::stoi(item)); + // remove spaces for tryParse to work + item.erase(remove_if(item.begin(), item.end(), isspace), item.end()); + auto itemInt = GeneralUtils::TryParse(item); + if (itemInt) inventory.push_back(itemInt.value()); } } } - // Get the phrases auto* phrases = object->FirstChildElement("phrases"); - std::vector phraseList = {}; - if (phrases) { for (auto* phrase = phrases->FirstChildElement("phrase"); phrase != nullptr; phrase = phrase->NextSiblingElement("phrase")) { @@ -235,41 +229,34 @@ void VanityUtilities::ParseXML(const std::string& file) { } } - // Get the script - auto* scriptElement = object->FirstChildElement("script"); - - std::string scriptName = ""; - - if (scriptElement != nullptr) { - auto* scriptNameAttribute = scriptElement->Attribute("name"); - if (scriptNameAttribute) scriptName = scriptNameAttribute; - } - auto* configElement = object->FirstChildElement("config"); std::vector keys = {}; - std::vector config = {}; if(configElement) { for (auto* key = configElement->FirstChildElement("key"); key != nullptr; key = key->NextSiblingElement("key")) { // Get the config data - auto* data = key->Attribute("data"); + auto* data = key->GetText(); if (!data) continue; LDFBaseData* configData = LDFBaseData::DataFromString(data); + if (configData->GetKey() == u"useLocationsAsRandomSpawnPoint" && configData->GetValueType() == eLDFType::LDF_TYPE_BOOLEAN){ + useLocationsAsRandomSpawnPoint = static_cast(configData); + continue; + } keys.push_back(configData->GetKey()); config.push_back(configData); } } if (!keys.empty()) config.push_back(new LDFData>(u"syncLDF", keys)); - VanityObject objectData; - objectData.m_Name = name; - objectData.m_LOT = std::stoi(lot); - objectData.m_Equipment = inventory; - objectData.m_Phrases = phraseList; - objectData.m_Script = scriptName; - objectData.m_Config = config; + VanityObject objectData { + .m_Name = name, + .m_LOT = lot, + .m_Equipment = inventory, + .m_Phrases = phraseList, + .m_Config = config + }; // Get the locations auto* locations = object->FirstChildElement("locations"); @@ -283,64 +270,67 @@ void VanityUtilities::ParseXML(const std::string& file) { location = location->NextSiblingElement("location")) { // Get the location data - auto* zoneID = location->Attribute("zone"); - auto* x = location->Attribute("x"); - auto* y = location->Attribute("y"); - auto* z = location->Attribute("z"); - auto* rw = location->Attribute("rw"); - auto* rx = location->Attribute("rx"); - auto* ry = location->Attribute("ry"); - auto* rz = location->Attribute("rz"); + auto zoneID = GeneralUtils::TryParse(location->Attribute("zone")); + auto x = GeneralUtils::TryParse(location->Attribute("x")); + auto y = GeneralUtils::TryParse(location->Attribute("y")); + auto z = GeneralUtils::TryParse(location->Attribute("z")); + auto rw = GeneralUtils::TryParse(location->Attribute("rw")); + auto rx = GeneralUtils::TryParse(location->Attribute("rx")); + auto ry = GeneralUtils::TryParse(location->Attribute("ry")); + auto rz = GeneralUtils::TryParse(location->Attribute("rz")); - if (zoneID == nullptr || x == nullptr || y == nullptr || z == nullptr || rw == nullptr || rx == nullptr || ry == nullptr - || rz == nullptr) { + if (!zoneID || !x || !y || !z || !rw || !rx || !ry || !rz) { LOG("Failed to parse NPC location data"); continue; } - VanityObjectLocation locationData; - locationData.m_Position = { std::stof(x), std::stof(y), std::stof(z) }; - locationData.m_Rotation = { std::stof(rw), std::stof(rx), std::stof(ry), std::stof(rz) }; - locationData.m_Chance = 1.0f; + if (zoneID.value() != currentZoneID) { + LOG_DEBUG("Skipping location because it is in %i and not the current zone (%i)", zoneID.value(), currentZoneID); + continue; + } + + VanityObjectLocation locationData { + .m_Position = { x.value(), y.value(), z.value() }, + .m_Rotation = { rw.value(), rx.value(), ry.value(), rz.value() }, + }; if (location->Attribute("chance")) { - locationData.m_Chance = std::stof(location->Attribute("chance")); + locationData.m_Chance = GeneralUtils::TryParse(location->Attribute("chance")).value_or(1.0f); } if (location->Attribute("scale")) { - locationData.m_Scale = std::stof(location->Attribute("scale")); + locationData.m_Scale = GeneralUtils::TryParse(location->Attribute("scale")).value_or(1.0f); } - - const auto& it = objectData.m_Locations.find(std::stoi(zoneID)); + const auto& it = objectData.m_Locations.find(zoneID.value()); if (it != objectData.m_Locations.end()) { it->second.push_back(locationData); } else { std::vector locations; locations.push_back(locationData); - objectData.m_Locations.insert(std::make_pair(std::stoi(zoneID), locations)); + objectData.m_Locations.insert(std::make_pair(zoneID.value(), locations)); } - if (!(std::find(keys.begin(), keys.end(), u"teleport") != keys.end())) { - m_Objects.push_back(objectData); + if (!useLocationsAsRandomSpawnPoint) { + objects.push_back(objectData); objectData.m_Locations.clear(); } } - if (std::find(keys.begin(), keys.end(), u"teleport") != keys.end()) { - m_Objects.push_back(objectData); - } + + if (useLocationsAsRandomSpawnPoint && !objectData.m_Locations.empty()) { + objects.push_back(objectData); + } } } } VanityObject* VanityUtilities::GetObject(const std::string& name) { - for (size_t i = 0; i < m_Objects.size(); i++) { - if (m_Objects[i].m_Name == name) { - return &m_Objects[i]; + for (size_t i = 0; i < objects.size(); i++) { + if (objects[i].m_Name == name) { + return &objects[i]; } } - return nullptr; } @@ -350,7 +340,7 @@ std::string VanityUtilities::ParseMarkdown(const std::string& file) { // Read the file into a string std::ifstream t(file); std::stringstream output; - // If the file does not exist, return an empty string. + // If the file does not exist, return a useful error. if (!t.good()) { output << "File "; output << file.substr(file.rfind("/") + 1); @@ -408,13 +398,13 @@ std::string VanityUtilities::ParseMarkdown(const std::string& file) { return output.str(); } -void VanityUtilities::SetupNPCTalk(Entity* npc) { +void SetupNPCTalk(Entity* npc) { npc->AddCallbackTimer(15.0f, [npc]() { NPCTalk(npc); }); npc->SetProximityRadius(20.0f, "talk"); } -void VanityUtilities::NPCTalk(Entity* npc) { +void NPCTalk(Entity* npc) { auto* proximityMonitorComponent = npc->GetComponent(); if (!proximityMonitorComponent->GetProximityObjects("talk").empty()) { diff --git a/dGame/dUtilities/VanityUtilities.h b/dGame/dUtilities/VanityUtilities.h index 49bd23ab..a1d00501 100644 --- a/dGame/dUtilities/VanityUtilities.h +++ b/dGame/dUtilities/VanityUtilities.h @@ -5,58 +5,30 @@ #include #include -struct VanityObjectLocation -{ +struct VanityObjectLocation { float m_Chance = 1.0f; NiPoint3 m_Position; NiQuaternion m_Rotation; float m_Scale = 1.0f; }; -struct VanityObject -{ +struct VanityObject { LWOOBJID m_ID = LWOOBJID_EMPTY; std::string m_Name; - LOT m_LOT; + LOT m_LOT = LOT_NULL; std::vector m_Equipment; std::vector m_Phrases; - std::string m_Script; std::map> m_Locations; std::vector m_Config; }; -class VanityUtilities -{ -public: - static void SpawnVanity(); +namespace VanityUtilities { + void SpawnVanity(); - static Entity* SpawnObject( - const VanityObject& object, - const VanityObjectLocation& location - ); + VanityObject* GetObject(const std::string& name); - static LWOOBJID SpawnSpawner( - const VanityObject& object, - const VanityObjectLocation& location - ); - - static std::string ParseMarkdown( + std::string ParseMarkdown( const std::string& file ); - - static void ParseXML( - const std::string& file - ); - - static VanityObject* GetObject(const std::string& name); - -private: - static void SetupNPCTalk(Entity* npc); - - static void NPCTalk(Entity* npc); - - static std::vector m_Objects; - - static std::set m_LoadedFiles; }; diff --git a/dScripts/02_server/DLU/DLUVanityTeleportingObject.cpp b/dScripts/02_server/DLU/DLUVanityTeleportingObject.cpp index 8aff1995..60d2d715 100644 --- a/dScripts/02_server/DLU/DLUVanityTeleportingObject.cpp +++ b/dScripts/02_server/DLU/DLUVanityTeleportingObject.cpp @@ -5,20 +5,17 @@ #include "RenderComponent.h" void DLUVanityTeleportingObject::OnStartup(Entity* self) { - if (!self->HasVar(u"npcName") || !self->HasVar(u"teleport")) return; - m_Object = VanityUtilities::GetObject(self->GetVarAsString(u"npcName")); + if (!self->HasVar(u"npcName")) return; + m_Object = VanityUtilities::GetObject(self->GetVarAsString(u"npcName")); if (!m_Object) return; if (self->HasVar(u"teleportInterval")) m_TeleportInterval = self->GetVar(u"teleportInterval"); - if (self->GetVar(u"teleport")) { - self->AddTimer("setupTeleport", m_TeleportInterval); - } + self->AddTimer("setupTeleport", m_TeleportInterval); } void DLUVanityTeleportingObject::OnTimerDone(Entity* self, std::string timerName) { if (timerName == "setupTeleport") { - RenderComponent::PlayAnimation(self, u"interact"); GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportBeam", "teleportBeam"); GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportRings", "teleportRings"); @@ -40,7 +37,6 @@ void DLUVanityTeleportingObject::OnTimerDone(Entity* self, std::string timerName self->SetPosition(newLocation.m_Position); self->SetRotation(newLocation.m_Rotation); - self->SetScale(newLocation.m_Scale); GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportBeam", "teleportBeam"); GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportRings", "teleportRings"); self->AddTimer("stopFX", 2.0f); diff --git a/vanity/atm.xml b/vanity/atm.xml index 96ed1a2b..863498ed 100644 --- a/vanity/atm.xml +++ b/vanity/atm.xml @@ -1,23 +1,40 @@ - - - - - - - - - - - - - - - - - - - - - + + + + CheckPrecondition=0:168 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vanity/demo.xml b/vanity/demo.xml new file mode 100644 index 00000000..9e891dbe --- /dev/null +++ b/vanity/demo.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + 7630, 1727, 7453, 7521 + + vendorInvOverride=0:1727,7292,16553,2243,14535,14538,14531,6730 + + + + + + + + + + useLocationsAsRandomSpawnPoint=7:1 + + + + + + + + + + + + CheckPrecondition=0: + SmashableDoesNotCutNavmesh=7:0 + add_to_navmesh=7:1 + aggroRadius=3:15 + camGradSnap=7:0 + camPrefersToFadeObject=7:1 + carver_only=7:0 + create_physics=7:1 + currency=5:0 + custom_config_names=0: + explode_factor=3:1 + fxpriority=1:0 + gravFactor=3:1 + grpNameQBShowBricks=0: + ignoreCameraCollision=7:0 + interaction_distance=3:16 + is_smashable=7:1 + loadOnClientOnly=7:0 + loadSrvrOnly=7:0 + max_to_spawn=1:-1 + navmesh_carver=7:0 + no_auto_spawn=7:1 + no_timed_spawn=7:1 + override_faction=7:0 + radius=3:0 + renderAnimLODSkew=3:1 + renderCullingGroup=5:0 + renderOffscreenAnimEnabled=7:0 + respawn=3:20 + sceneIDOverride=1:255 + sceneIDOverrideEnabled=7:0 + sceneLayerIDOverride=5:0 + set_faction=13:4 + smashable_loot_matrix=1:486 + smashable_loot_matrix_set=7:0 + softtetherRadius=3:15 + spawner_active_on_load=7:1 + spawntemplate=1:12379 + startsQBActivator=7:0 + template=1:-1 + tetherRadius=3:15 + usetetherdb=7:0 + usewanderdb=7:0 + wanderRadius=3:15 + + + + + + + + + + + CheckPrecondition=0: + SmashableDoesNotCutNavmesh=7:0 + add_to_navmesh=7:1 + bounding_radius_override=3:0 + camGradSnap=7:0 + camPrefersToFadeObject=7:1 + carver_only=7:0 + create_physics=7:1 + custom_config_names=0: + explode_factor=3:1 + friction=3:1.5 + fxpriority=1:0 + gravFactor=3:1 + grpNameQBShowBricks=0: + ignoreCameraCollision=7:0 + interaction_distance=3:16 + is_smashable=7:1 + loadOnClientOnly=7:0 + max_to_spawn=1:-1 + navmesh_carver=7:0 + no_auto_spawn=7:1 + no_timed_spawn=7:1 + override_faction=7:0 + radius=3:0 + renderCullingGroup=5:0 + respawn=5:20000 + sceneIDOverride=1:255 + sceneIDOverrideEnabled=7:0 + sceneLayerIDOverride=5:0 + set_faction=13:6 + smashable_loot_matrix=1:227 + smashable_loot_matrix_set=7:0 + spawner_active_on_load=7:1 + spawntemplate=1:2295 + startsQBActivator=7:0 + template=1:-1 + + + + + + diff --git a/vanity/dev-tribute.xml b/vanity/dev-tribute.xml index d20e31a6..a3d4e7eb 100644 --- a/vanity/dev-tribute.xml +++ b/vanity/dev-tribute.xml @@ -1,378 +1,439 @@ - - 6802, 2519, 2623, 14806 - - Sorry for the mess. - To future endeavours! - What could imagination bring to light? - Vroom vroom... - Take care to preserve the universe. - Builders of the world, unite! - Everything is awesome! - I hope my behaviors are behaving themselves. - - - - - - - 12947, 12949, 12962, 12963 - - I hope quickbulds are still working! - Be careful crossing the gap! - Have The Maelstrom stopped going invisible? - - - - - - - 9950, 9944, 14102, 14092 - - Hello Explorer! It's great to see you made it! - Have you heard about Darkflame? - I've traveled across this entire system, but nothing beats the view here. - - - - - - - - - cmerw[acowipaejio;fawjioefasdl;kfjm; - I, for one, welcome our new robot overlords. - zxnpoasdfiopwemsadf'kawpfo[ekasdf;'s - *teleports behind you* - -