diff --git a/CMakeLists.txt b/CMakeLists.txt index e085bfe7..b36bdb29 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" "NPC.xml") +set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "root.xml" "dev-tribute.xml" "atm.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 bb932991..dd15f69d 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -2197,3 +2197,9 @@ 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); +} \ No newline at end of file diff --git a/dGame/Entity.h b/dGame/Entity.h index 6546e458..7d5e24c9 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -295,6 +295,8 @@ public: void ProcessPositionUpdate(PositionUpdate& update); + void SetScale(const float scale); + protected: LWOOBJID m_ObjectID; diff --git a/dGame/dUtilities/VanityUtilities.cpp b/dGame/dUtilities/VanityUtilities.cpp index fa1a3eac..3e93f830 100644 --- a/dGame/dUtilities/VanityUtilities.cpp +++ b/dGame/dUtilities/VanityUtilities.cpp @@ -22,143 +22,13 @@ #include -std::vector VanityUtilities::m_NPCs = {}; -std::vector VanityUtilities::m_Parties = {}; -std::vector VanityUtilities::m_PartyPhrases = {}; +std::vector VanityUtilities::m_Objects = {}; +std::set VanityUtilities::m_LoadedFiles = {}; + void VanityUtilities::SpawnVanity() { - if (Game::config->GetValue("disable_vanity") == "1") { - return; - } - const uint32_t zoneID = Game::server->GetZoneID(); - for (const auto& npc : m_NPCs) { - if (npc.m_ID == LWOOBJID_EMPTY) continue; - if (npc.m_LOT == 176){ - Game::zoneManager->RemoveSpawner(npc.m_ID); - } else{ - auto* entity = Game::entityManager->GetEntity(npc.m_ID); - if (!entity) continue; - entity->Smash(LWOOBJID_EMPTY, eKillType::VIOLENT); - } - } - - m_NPCs.clear(); - m_Parties.clear(); - m_PartyPhrases.clear(); - - ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity/NPC.xml").string()); - - // Loop through all parties - for (const auto& party : m_Parties) { - const auto chance = party.m_Chance; - const auto zone = party.m_Zone; - - if (zone != Game::server->GetZoneID()) { - continue; - } - - float rate = GeneralUtils::GenerateRandomNumber(0, 1); - if (chance < rate) { - continue; - } - - // Copy m_NPCs into a new vector - std::vector npcList = m_NPCs; - std::vector taken = {}; - - LOG("Spawning party with %i locations", party.m_Locations.size()); - - // Loop through all locations - for (const auto& location : party.m_Locations) { - rate = GeneralUtils::GenerateRandomNumber(0, 1); - if (0.75f < rate) { - continue; - } - - // Get a random NPC - auto npcIndex = GeneralUtils::GenerateRandomNumber(0, npcList.size() - 1); - - while (std::find(taken.begin(), taken.end(), npcIndex) != taken.end()) { - npcIndex = GeneralUtils::GenerateRandomNumber(0, npcList.size() - 1); - } - - auto& npc = npcList[npcIndex]; - // Skip spawners - if (npc.m_LOT == 176) continue; - - taken.push_back(npcIndex); - - LOG("ldf size is %i", npc.ldf.size()); - if (npc.ldf.empty()) { - npc.ldf = { - new LDFData>(u"syncLDF", { u"custom_script_client" }), - new LDFData(u"custom_script_client", u"scripts\\ai\\SPEC\\MISSION_MINIGAME_CLIENT.lua") - }; - } - - // Spawn the NPC - if (npc.m_LOT == 176){ - npc.m_ID = SpawnSpawner(npc.m_LOT, location.m_Position, location.m_Rotation, npc.ldf); - } else { - auto* npcEntity = SpawnNPC(npc.m_LOT, npc.m_Name, location.m_Position, location.m_Rotation, npc.m_Equipment, npc.ldf); - if (!npc.m_Phrases.empty()) { - npcEntity->SetVar>(u"chats", m_PartyPhrases); - SetupNPCTalk(npcEntity); - } - } - } - return; - } - - // Loop through all NPCs - for (auto& npc : m_NPCs) { - if (npc.m_Locations.find(Game::server->GetZoneID()) == npc.m_Locations.end()) - continue; - - const std::vector& locations = npc.m_Locations.at(Game::server->GetZoneID()); - - // Pick a random location - const auto& location = locations[GeneralUtils::GenerateRandomNumber( - static_cast(0), static_cast(locations.size() - 1))]; - - float rate = GeneralUtils::GenerateRandomNumber(0, 1); - if (location.m_Chance < rate) { - continue; - } - - if (npc.ldf.empty()) { - npc.ldf = { - new LDFData>(u"syncLDF", { u"custom_script_client" }), - new LDFData(u"custom_script_client", u"scripts\\ai\\SPEC\\MISSION_MINIGAME_CLIENT.lua") - }; - } - if (npc.m_LOT == 176){ - npc.m_ID = SpawnSpawner(npc.m_LOT, location.m_Position, location.m_Rotation, npc.ldf); - } else { - // Spawn the NPC - auto* npcEntity = SpawnNPC(npc.m_LOT, npc.m_Name, location.m_Position, location.m_Rotation, npc.m_Equipment, npc.ldf); - if (!npcEntity) continue; - npc.m_ID = npcEntity->GetObjectID(); - if (!npc.m_Phrases.empty()){ - npcEntity->SetVar>(u"chats", npc.m_Phrases); - - auto* scriptComponent = npcEntity->GetComponent(); - - if (scriptComponent && !npc.m_Script.empty()) { - scriptComponent->SetScript(npc.m_Script); - scriptComponent->SetSerialized(false); - - for (const auto& npc : npc.m_Flags) { - npcEntity->SetVar(GeneralUtils::ASCIIToUTF16(npc.first), npc.second); - } - } - SetupNPCTalk(npcEntity); - } - } - } - if (zoneID == 1200) { { EntityInfo info; @@ -175,38 +45,99 @@ void VanityUtilities::SpawnVanity() { Game::entityManager->ConstructEntity(entity); } } + + if (Game::config->GetValue("disable_vanity") == "1") { + return; + } + + for (const auto& npc : m_Objects) { + if (npc.m_ID == LWOOBJID_EMPTY) continue; + if (npc.m_LOT == 176){ + Game::zoneManager->RemoveSpawner(npc.m_ID); + } else{ + auto* entity = Game::entityManager->GetEntity(npc.m_ID); + if (!entity) continue; + entity->Smash(LWOOBJID_EMPTY, eKillType::VIOLENT); + } + } + + m_Objects.clear(); + m_LoadedFiles.clear(); + + ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity/root.xml").string()); + + // Loop through all objects + for (auto& object : m_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()); + + // Pick a random location + const auto& location = locations[GeneralUtils::GenerateRandomNumber( + static_cast(0), static_cast(locations.size() - 1))]; + + 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 { + // Spawn the NPC + auto* objectEntity = SpawnObject(object, location); + if (!objectEntity) continue; + 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(LOT lot, const NiPoint3& position, const NiQuaternion& rotation, const std::vector& ldf){ +LWOOBJID VanityUtilities::SpawnSpawner(const VanityObject& object, const VanityObjectLocation& location) { SceneObject obj; - obj.lot = lot; + obj.lot = object.m_LOT; // guratantee we have no collisions do { obj.id = ObjectIDManager::GenerateObjectID(); } while(Game::zoneManager->GetSpawner(obj.id)); - obj.position = position; - obj.rotation = rotation; - obj.settings = ldf; + obj.position = location.m_Position; + obj.rotation = location.m_Rotation; + obj.settings = object.m_Config; Level::MakeSpawner(obj); return obj.id; } -Entity* VanityUtilities::SpawnNPC(LOT lot, const std::string& name, const NiPoint3& position, const NiQuaternion& rotation, const std::vector& inventory, const std::vector& ldf) { +Entity* VanityUtilities::SpawnObject(const VanityObject& object, const VanityObjectLocation& location) { EntityInfo info; - info.lot = lot; - info.pos = position; - info.rot = rotation; + info.lot = object.m_LOT; + info.pos = location.m_Position; + info.rot = location.m_Rotation; + info.scale = location.m_Scale; info.spawnerID = Game::entityManager->GetZoneControlEntity()->GetObjectID(); - info.settings = ldf; + info.settings = object.m_Config; auto* entity = Game::entityManager->CreateEntity(info); - entity->SetVar(u"npcName", name); + entity->SetVar(u"npcName", object.m_Name); if (entity->GetVar(u"noGhosting")) entity->SetIsGhostingCandidate(false); auto* inventoryComponent = entity->GetComponent(); - if (inventoryComponent && !inventory.empty()) { - inventoryComponent->SetNPCItems(inventory); + if (inventoryComponent && !object.m_Equipment.empty()) { + inventoryComponent->SetNPCItems(object.m_Equipment); } auto* destroyableComponent = entity->GetComponent(); @@ -223,6 +154,11 @@ Entity* VanityUtilities::SpawnNPC(LOT lot, const std::string& name, const NiPoin } void VanityUtilities::ParseXML(const std::string& file) { + if (m_LoadedFiles.contains(file)){ + LOG("Trying to load vanity file %s twice!!!", file.c_str()); + return; + } + m_LoadedFiles.insert(file); // Read the entire file std::ifstream xmlFile(file); std::string xml((std::istreambuf_iterator(xmlFile)), std::istreambuf_iterator()); @@ -231,210 +167,112 @@ void VanityUtilities::ParseXML(const std::string& file) { tinyxml2::XMLDocument doc; doc.Parse(xml.c_str(), xml.size()); - // Read the NPCs - auto* npcs = doc.FirstChildElement("npcs"); - - if (npcs == nullptr) { - LOG("Failed to parse NPCs"); - return; - } - - for (auto* party = npcs->FirstChildElement("party"); party != nullptr; party = party->NextSiblingElement("party")) { - // Get 'zone' as uint32_t and 'chance' as float - uint32_t zone = 0; - float chance = 0.0f; - - if (party->Attribute("zone") != nullptr) { - zone = std::stoul(party->Attribute("zone")); - } - - if (party->Attribute("chance") != nullptr) { - chance = std::stof(party->Attribute("chance")); - } - - VanityParty partyInfo; - partyInfo.m_Zone = zone; - partyInfo.m_Chance = chance; - - auto* locations = party->FirstChildElement("locations"); - - if (locations == nullptr) { - LOG("Failed to parse party locations"); - continue; - } - - for (auto* location = locations->FirstChildElement("location"); location != nullptr; - location = location->NextSiblingElement("location")) { - // Get the location data - 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"); - - if (x == nullptr || y == nullptr || z == nullptr || rw == nullptr || rx == nullptr || ry == nullptr - || rz == nullptr) { - LOG("Failed to parse party location data"); + // Read the objects + auto* files = doc.FirstChildElement("files"); + if (files) { + for (auto* file = files->FirstChildElement("file"); file != nullptr; file = file->NextSiblingElement("file")) { + std::string enabled = file->Attribute("enabled"); + std::string filename = file->Attribute("name"); + if (enabled != "1") { continue; } - - VanityNPCLocation 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; - - partyInfo.m_Locations.push_back(locationData); - } - - m_Parties.push_back(partyInfo); - } - - auto* partyPhrases = npcs->FirstChildElement("partyphrases"); - - if (partyPhrases == nullptr) { - LOG("No party phrases found"); - } else { - for (auto* phrase = partyPhrases->FirstChildElement("phrase"); phrase != nullptr; - phrase = phrase->NextSiblingElement("phrase")) { - // Get the phrase - auto* text = phrase->GetText(); - - if (text == nullptr) { - LOG("Failed to parse party phrase"); - continue; - } - - m_PartyPhrases.push_back(text); + ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity" / filename).string()); } } - for (auto* npc = npcs->FirstChildElement("npc"); npc != nullptr; npc = npc->NextSiblingElement("npc")) { - // Get the NPC name - auto* name = npc->Attribute("name"); + // Read the objects + auto* objects = doc.FirstChildElement("objects"); - if (!name) name = ""; + if (objects) { + for (auto* object = objects->FirstChildElement("object"); object != nullptr; object = object->NextSiblingElement("object")) { + // Get the NPC name + auto* name = object->Attribute("name"); - // Get the NPC lot - auto* lot = npc->Attribute("lot"); + if (!name) name = ""; - if (lot == nullptr) { - LOG("Failed to parse NPC lot"); - continue; - } + // Get the NPC lot + auto* lot = object->Attribute("lot"); - // Get the equipment - auto* equipment = npc->FirstChildElement("equipment"); - std::vector inventory; - - if (equipment) { - auto* text = equipment->GetText(); - - if (text != nullptr) { - std::string equipmentString(text); - - std::vector splitEquipment = GeneralUtils::SplitString(equipmentString, ','); - - for (auto& item : splitEquipment) { - inventory.push_back(std::stoi(item)); - } - } - } - - - // Get the phrases - auto* phrases = npc->FirstChildElement("phrases"); - - std::vector phraseList = {}; - - if (phrases) { - for (auto* phrase = phrases->FirstChildElement("phrase"); phrase != nullptr; - phrase = phrase->NextSiblingElement("phrase")) { - // Get the phrase - auto* text = phrase->GetText(); - if (text == nullptr) { - LOG("Failed to parse NPC phrase"); - continue; - } - phraseList.push_back(text); - } - } - - // Get the script - auto* scriptElement = npc->FirstChildElement("script"); - - std::string scriptName = ""; - - if (scriptElement != nullptr) { - auto* scriptNameAttribute = scriptElement->Attribute("name"); - if (scriptNameAttribute) scriptName = scriptNameAttribute; - } - - auto* ldfElement = npc->FirstChildElement("ldf"); - std::vector keys = {}; - - std::vector ldf = {}; - if(ldfElement) { - for (auto* entry = ldfElement->FirstChildElement("entry"); entry != nullptr; - entry = entry->NextSiblingElement("entry")) { - // Get the ldf data - auto* data = entry->Attribute("data"); - if (!data) continue; - - LDFBaseData* ldfData = LDFBaseData::DataFromString(data); - keys.push_back(ldfData->GetKey()); - ldf.push_back(ldfData); - } - } - if (!keys.empty()) ldf.push_back(new LDFData>(u"syncLDF", keys)); - - VanityNPC npcData; - npcData.m_Name = name; - npcData.m_LOT = std::stoi(lot); - npcData.m_Equipment = inventory; - npcData.m_Phrases = phraseList; - npcData.m_Script = scriptName; - npcData.ldf = ldf; - - // Get flags - auto* flags = npc->FirstChildElement("flags"); - - if (flags != nullptr) { - for (auto* flag = flags->FirstChildElement("flag"); flag != nullptr; - flag = flag->NextSiblingElement("flag")) { - // Get the flag name - auto* name = flag->Attribute("name"); - - if (name == nullptr) { - LOG("Failed to parse NPC flag name"); - continue; - } - - // Get the flag value - auto* value = flag->Attribute("value"); - - if (value == nullptr) { - LOG("Failed to parse NPC flag value"); - continue; - } - - npcData.m_Flags[name] = std::stoi(value); - } - } - - // Get the zones - for (auto* zone = npc->FirstChildElement("zone"); zone != nullptr; zone = zone->NextSiblingElement("zone")) { - // Get the zone ID - auto* zoneID = zone->Attribute("id"); - - if (zoneID == nullptr) { - LOG("Failed to parse NPC zone ID"); + if (lot == nullptr) { + LOG("Failed to parse object lot"); continue; } + // Get the equipment + auto* equipment = object->FirstChildElement("equipment"); + std::vector inventory; + + if (equipment) { + auto* text = equipment->GetText(); + + if (text != nullptr) { + std::string equipmentString(text); + + std::vector splitEquipment = GeneralUtils::SplitString(equipmentString, ','); + + for (auto& item : splitEquipment) { + inventory.push_back(std::stoi(item)); + } + } + } + + + // 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")) { + // Get the phrase + auto* text = phrase->GetText(); + if (text == nullptr) { + LOG("Failed to parse NPC phrase"); + continue; + } + phraseList.push_back(text); + } + } + + // 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"); + if (!data) continue; + + LDFBaseData* configData = LDFBaseData::DataFromString(data); + 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; + // Get the locations - auto* locations = zone->FirstChildElement("locations"); + auto* locations = object->FirstChildElement("locations"); if (locations == nullptr) { LOG("Failed to parse NPC locations"); @@ -443,7 +281,9 @@ void VanityUtilities::ParseXML(const std::string& file) { for (auto* location = locations->FirstChildElement("location"); location != nullptr; 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"); @@ -452,41 +292,52 @@ void VanityUtilities::ParseXML(const std::string& file) { auto* ry = location->Attribute("ry"); auto* rz = location->Attribute("rz"); - if (x == nullptr || y == nullptr || z == nullptr || rw == nullptr || rx == nullptr || ry == nullptr + if (zoneID == nullptr || x == nullptr || y == nullptr || z == nullptr || rw == nullptr || rx == nullptr || ry == nullptr || rz == nullptr) { LOG("Failed to parse NPC location data"); continue; } - VanityNPCLocation locationData; + 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 (location->Attribute("chance") != nullptr) { + if (location->Attribute("chance")) { locationData.m_Chance = std::stof(location->Attribute("chance")); } - const auto& it = npcData.m_Locations.find(std::stoi(zoneID)); + if (location->Attribute("scale")) { + locationData.m_Scale = std::stof(location->Attribute("scale")); + } - if (it != npcData.m_Locations.end()) { + + const auto& it = objectData.m_Locations.find(std::stoi(zoneID)); + + if (it != objectData.m_Locations.end()) { it->second.push_back(locationData); } else { - std::vector locations; + std::vector locations; locations.push_back(locationData); - npcData.m_Locations.insert(std::make_pair(std::stoi(zoneID), locations)); + objectData.m_Locations.insert(std::make_pair(std::stoi(zoneID), locations)); + } + + if (!(std::find(keys.begin(), keys.end(), u"teleport") != keys.end())) { + m_Objects.push_back(objectData); + objectData.m_Locations.clear(); } } + if (std::find(keys.begin(), keys.end(), u"teleport") != keys.end()) { + m_Objects.push_back(objectData); + } } - - m_NPCs.push_back(npcData); } } -VanityNPC* VanityUtilities::GetNPC(const std::string& name) { - for (size_t i = 0; i < m_NPCs.size(); i++) { - if (m_NPCs[i].m_Name == name) { - return &m_NPCs[i]; +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]; } } @@ -498,10 +349,13 @@ 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 (!t.good()) { - return ""; + output << "File "; + output << file.substr(file.rfind("/") + 1); + output << " not found!\nContact your DarkflameServer admin\nor find the server source at https://github.com/DarkflameUniverse/DarkflameServer"; + return output.str(); } std::stringstream buffer; @@ -511,7 +365,6 @@ std::string VanityUtilities::ParseMarkdown(const std::string& file) { // Loop through all lines in the file. // Replace all instances of the markdown syntax with the corresponding HTML. // Only care about headers - std::stringstream output; std::string line; std::stringstream ss; ss << fileContents; diff --git a/dGame/dUtilities/VanityUtilities.h b/dGame/dUtilities/VanityUtilities.h index cff73bce..49bd23ab 100644 --- a/dGame/dUtilities/VanityUtilities.h +++ b/dGame/dUtilities/VanityUtilities.h @@ -3,15 +3,17 @@ #include "dCommonVars.h" #include "Entity.h" #include +#include -struct VanityNPCLocation +struct VanityObjectLocation { float m_Chance = 1.0f; NiPoint3 m_Position; NiQuaternion m_Rotation; + float m_Scale = 1.0f; }; -struct VanityNPC +struct VanityObject { LWOOBJID m_ID = LWOOBJID_EMPTY; std::string m_Name; @@ -19,37 +21,24 @@ struct VanityNPC std::vector m_Equipment; std::vector m_Phrases; std::string m_Script; - std::map m_Flags; - std::map> m_Locations; - std::vector ldf; + std::map> m_Locations; + std::vector m_Config; }; -struct VanityParty -{ - uint32_t m_Zone; - float m_Chance = 1.0f; - std::vector m_Locations; -}; class VanityUtilities { public: static void SpawnVanity(); - static Entity* SpawnNPC( - LOT lot, - const std::string& name, - const NiPoint3& position, - const NiQuaternion& rotation, - const std::vector& inventory, - const std::vector& ldf + static Entity* SpawnObject( + const VanityObject& object, + const VanityObjectLocation& location ); static LWOOBJID SpawnSpawner( - LOT lot, - const NiPoint3& position, - const NiQuaternion& rotation, - const std::vector& ldf + const VanityObject& object, + const VanityObjectLocation& location ); static std::string ParseMarkdown( @@ -60,16 +49,14 @@ public: const std::string& file ); - static VanityNPC* GetNPC(const std::string& name); + static VanityObject* GetObject(const std::string& name); private: static void SetupNPCTalk(Entity* npc); static void NPCTalk(Entity* npc); - static std::vector m_NPCs; - - static std::vector m_Parties; - - static std::vector m_PartyPhrases; + static std::vector m_Objects; + + static std::set m_LoadedFiles; }; diff --git a/dScripts/02_server/DLU/CMakeLists.txt b/dScripts/02_server/DLU/CMakeLists.txt index 64d4cbbd..fb257d3e 100644 --- a/dScripts/02_server/DLU/CMakeLists.txt +++ b/dScripts/02_server/DLU/CMakeLists.txt @@ -1,3 +1,3 @@ set(DSCRIPTS_SOURCES_02_SERVER_DLU - "DLUVanityNPC.cpp" + "DLUVanityTeleportingObject.cpp" PARENT_SCOPE) diff --git a/dScripts/02_server/DLU/DLUVanityNPC.cpp b/dScripts/02_server/DLU/DLUVanityTeleportingObject.cpp similarity index 51% rename from dScripts/02_server/DLU/DLUVanityNPC.cpp rename to dScripts/02_server/DLU/DLUVanityTeleportingObject.cpp index ba2c6604..8aff1995 100644 --- a/dScripts/02_server/DLU/DLUVanityNPC.cpp +++ b/dScripts/02_server/DLU/DLUVanityTeleportingObject.cpp @@ -1,22 +1,22 @@ -#include "DLUVanityNPC.h" +#include "DLUVanityTeleportingObject.h" #include "GameMessages.h" #include "dServer.h" #include "VanityUtilities.h" #include "RenderComponent.h" -void DLUVanityNPC::OnStartup(Entity* self) { - m_NPC = VanityUtilities::GetNPC("averysumner - Destroyer of Worlds"); +void DLUVanityTeleportingObject::OnStartup(Entity* self) { + if (!self->HasVar(u"npcName") || !self->HasVar(u"teleport")) return; + m_Object = VanityUtilities::GetObject(self->GetVarAsString(u"npcName")); - if (m_NPC == nullptr) { - return; - } + if (!m_Object) return; + if (self->HasVar(u"teleportInterval")) m_TeleportInterval = self->GetVar(u"teleportInterval"); if (self->GetVar(u"teleport")) { - self->AddTimer("setupTeleport", 15.0f); + self->AddTimer("setupTeleport", m_TeleportInterval); } } -void DLUVanityNPC::OnTimerDone(Entity* self, std::string timerName) { +void DLUVanityTeleportingObject::OnTimerDone(Entity* self, std::string timerName) { if (timerName == "setupTeleport") { RenderComponent::PlayAnimation(self, u"interact"); GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportBeam", "teleportBeam"); @@ -28,20 +28,22 @@ void DLUVanityNPC::OnTimerDone(Entity* self, std::string timerName) { GameMessages::SendStopFXEffect(self, true, "teleportBeam"); GameMessages::SendStopFXEffect(self, true, "teleportRings"); } else if (timerName == "teleport") { - std::vector& locations = m_NPC->m_Locations[Game::server->GetZoneID()]; + std::vector& locations = m_Object->m_Locations[Game::server->GetZoneID()]; selectLocation: - VanityNPCLocation& newLocation = locations[GeneralUtils::GenerateRandomNumber(0, locations.size() - 1)]; + VanityObjectLocation& newLocation = locations[GeneralUtils::GenerateRandomNumber(0, locations.size() - 1)]; + // try to get not the same position, but if we get the same one twice, it's fine if (self->GetPosition() == newLocation.m_Position) { - goto selectLocation; // cry about it + VanityObjectLocation& newLocation = locations[GeneralUtils::GenerateRandomNumber(0, locations.size() - 1)]; } 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); - self->AddTimer("setupTeleport", 15.0f); + self->AddTimer("setupTeleport", m_TeleportInterval); } } diff --git a/dScripts/02_server/DLU/DLUVanityNPC.h b/dScripts/02_server/DLU/DLUVanityTeleportingObject.h similarity index 54% rename from dScripts/02_server/DLU/DLUVanityNPC.h rename to dScripts/02_server/DLU/DLUVanityTeleportingObject.h index aeb8e051..a13ba901 100644 --- a/dScripts/02_server/DLU/DLUVanityNPC.h +++ b/dScripts/02_server/DLU/DLUVanityTeleportingObject.h @@ -1,13 +1,14 @@ #pragma once #include "CppScripts.h" -class VanityNPC; -class DLUVanityNPC : public CppScripts::Script +class VanityObject; +class DLUVanityTeleportingObject : public CppScripts::Script { public: void OnStartup(Entity* self) override; void OnTimerDone(Entity* self, std::string timerName) override; private: - VanityNPC* m_NPC; + VanityObject* m_Object; + float m_TeleportInterval = 15.0f; }; diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index 071bd7a3..7cb853e6 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -216,7 +216,7 @@ #include "NtNaomiBreadcrumbServer.h" // DLU Scripts -#include "DLUVanityNPC.h" +#include "DLUVanityTeleportingObject.h" // AM Scripts #include "AmConsoleTeleportServer.h" @@ -834,8 +834,8 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr script = new NjNyaMissionitems(); //DLU: - else if (scriptName == "scripts\\02_server\\DLU\\DLUVanityNPC.lua") - script = new DLUVanityNPC(); + else if (scriptName == "scripts\\02_server\\DLU\\DLUVanityTeleportingObject.lua") + script = new DLUVanityTeleportingObject(); // Survival minigame else if (scriptName == "scripts\\02_server\\Enemy\\Survival\\L_AG_SURVIVAL_STROMBIE.lua") diff --git a/vanity/atm.xml b/vanity/atm.xml new file mode 100644 index 00000000..96ed1a2b --- /dev/null +++ b/vanity/atm.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vanity/NPC.xml b/vanity/dev-tribute.xml similarity index 65% rename from vanity/NPC.xml rename to vanity/dev-tribute.xml index 2311ab46..d20e31a6 100644 --- a/vanity/NPC.xml +++ b/vanity/dev-tribute.xml @@ -1,5 +1,5 @@ - - + + 6802, 2519, 2623, 14806 Sorry for the mess. @@ -11,39 +11,33 @@ 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; @@ -51,20 +45,18 @@ zxnpoasdfiopwemsadf'kawpfo[ekasdf;'s *teleports behind you* -