From 43707952d2928796b5c0c4ddf4f20dc1c30ee285 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Wed, 28 Feb 2024 17:16:47 -0600 Subject: [PATCH] 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* - -