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
This commit is contained in:
Aaron Kimbrell 2024-02-28 17:16:47 -06:00 committed by GitHub
parent ef3fdba621
commit 43707952d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 745 additions and 582 deletions

View File

@ -179,7 +179,7 @@ file(ARCHIVE_EXTRACT INPUT ${PROJECT_BINARY_DIR}/navmeshes.zip DESTINATION ${PRO
file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip) file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip)
# Copy vanity files on first build # 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}) foreach(file ${VANITY_FILES})
configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY) configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY)

View File

@ -2191,9 +2191,3 @@ void Entity::SetRespawnRot(const NiQuaternion& rotation) {
auto* characterComponent = GetComponent<CharacterComponent>(); auto* characterComponent = GetComponent<CharacterComponent>();
if (characterComponent) characterComponent->SetRespawnRot(rotation); if (characterComponent) characterComponent->SetRespawnRot(rotation);
} }
void Entity::SetScale(const float scale) {
if (scale == m_Scale) return;
m_Scale = scale;
Game::entityManager->SerializeEntity(this);
}

View File

@ -295,7 +295,8 @@ public:
void ProcessPositionUpdate(PositionUpdate& update); 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: protected:
LWOOBJID m_ObjectID; LWOOBJID m_ObjectID;

View File

@ -40,9 +40,19 @@ void VendorComponent::RefreshInventory(bool isCreation) {
SetHasMultiCostItems(false); SetHasMultiCostItems(false);
m_Inventory.clear(); m_Inventory.clear();
// Custom code for Max vanity NPC and Mr.Ree cameras // Custom code for Vanity Vendor Invetory Override
if(isCreation && m_Parent->GetLOT() == 9749 && Game::server->GetZoneID() == 1201) { if(m_Parent->HasVar(u"vendorInvOverride")) {
SetupMaxCustomVendor(); std::vector<std::string> 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<uint32_t>(itemString);
if (!item) continue;
if (SetupItem(item.value())) {
sortPriority++;
m_Inventory.push_back(SoldItem(item.value(), sortPriority));
}
}
return; return;
} }
@ -52,24 +62,12 @@ void VendorComponent::RefreshInventory(bool isCreation) {
if (lootMatrices.empty()) return; if (lootMatrices.empty()) return;
auto* lootTableTable = CDClientManager::GetTable<CDLootTableTable>(); auto* lootTableTable = CDClientManager::GetTable<CDLootTableTable>();
auto* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
auto* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
for (const auto& lootMatrix : lootMatrices) { for (const auto& lootMatrix : lootMatrices) {
auto vendorItems = lootTableTable->GetTable(lootMatrix.LootTableIndex); auto vendorItems = lootTableTable->GetTable(lootMatrix.LootTableIndex);
if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) { if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) {
for (const auto& item : vendorItems) { for (const auto& item : vendorItems) {
if (!m_HasStandardCostItems || !m_HasMultiCostItems) { if (SetupItem(item.itemid)) m_Inventory.push_back(SoldItem(item.itemid, item.sortPriority));
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));
} }
} else { } else {
auto randomCount = GeneralUtils::GenerateRandomNumber<int32_t>(lootMatrix.minToDrop, lootMatrix.maxToDrop); auto randomCount = GeneralUtils::GenerateRandomNumber<int32_t>(lootMatrix.minToDrop, lootMatrix.maxToDrop);
@ -79,17 +77,7 @@ void VendorComponent::RefreshInventory(bool isCreation) {
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1); auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
const auto& randomItem = vendorItems.at(randomItemIndex); const auto& randomItem = vendorItems.at(randomItemIndex);
vendorItems.erase(vendorItems.begin() + randomItemIndex); vendorItems.erase(vendorItems.begin() + randomItemIndex);
if (!m_HasStandardCostItems || !m_HasMultiCostItems) { if (SetupItem(randomItem.itemid)) m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority));
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));
} }
} }
} }
@ -126,15 +114,6 @@ bool VendorComponent::SellsItem(const LOT item) const {
}) > 0; }) > 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(){ void VendorComponent::HandleMrReeCameras(){
if (m_Parent->GetLOT() == 13569) { if (m_Parent->GetLOT() == 13569) {
SetHasStandardCostItems(true); SetHasStandardCostItems(true);
@ -211,5 +190,25 @@ void VendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::VENDOR); character->SetCoins(character->GetCoins() - (coinCost), eLootSourceType::VENDOR);
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR); inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS); GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);
} }
bool VendorComponent::SetupItem(LOT item) {
auto* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
auto* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
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;
}

View File

@ -50,8 +50,8 @@ public:
void Buy(Entity* buyer, LOT lot, uint32_t count); void Buy(Entity* buyer, LOT lot, uint32_t count);
private: private:
void SetupMaxCustomVendor();
void HandleMrReeCameras(); void HandleMrReeCameras();
bool SetupItem(LOT item);
float m_BuyScalar = 0.0f; float m_BuyScalar = 0.0f;
float m_SellScalar = 0.0f; float m_SellScalar = 0.0f;
float m_RefreshTimeSeconds = 0.0f; float m_RefreshTimeSeconds = 0.0f;

View File

@ -22,9 +22,18 @@
#include <fstream> #include <fstream>
std::vector<VanityObject> VanityUtilities::m_Objects = {};
std::set<std::string> VanityUtilities::m_LoadedFiles = {};
namespace {
std::vector<VanityObject> objects;
std::set<std::string> 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() { void VanityUtilities::SpawnVanity() {
const uint32_t zoneID = Game::server->GetZoneID(); const uint32_t zoneID = Game::server->GetZoneID();
@ -36,21 +45,19 @@ void VanityUtilities::SpawnVanity() {
info.pos = { 259.5f, 246.4f, -705.2f }; info.pos = { 259.5f, 246.4f, -705.2f };
info.rot = { 0.0f, 0.0f, 1.0f, 0.0f }; info.rot = { 0.0f, 0.0f, 1.0f, 0.0f };
info.spawnerID = Game::entityManager->GetZoneControlEntity()->GetObjectID(); info.spawnerID = Game::entityManager->GetZoneControlEntity()->GetObjectID();
info.settings = {
info.settings = { new LDFData<bool>(u"hasCustomText", true), new LDFData<bool>(u"hasCustomText", true),
new LDFData<std::string>(u"customText", ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/TESTAMENT.md").string())) }; new LDFData<std::string>(u"customText", ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/TESTAMENT.md").string()))
};
auto* entity = Game::entityManager->CreateEntity(info); auto* entity = Game::entityManager->CreateEntity(info);
Game::entityManager->ConstructEntity(entity); Game::entityManager->ConstructEntity(entity);
} }
} }
if (Game::config->GetValue("disable_vanity") == "1") { if (Game::config->GetValue("disable_vanity") == "1") return;
return;
}
for (const auto& npc : m_Objects) { for (const auto& npc : objects) {
if (npc.m_ID == LWOOBJID_EMPTY) continue; if (npc.m_ID == LWOOBJID_EMPTY) continue;
if (npc.m_LOT == 176){ if (npc.m_LOT == 176){
Game::zoneManager->RemoveSpawner(npc.m_ID); Game::zoneManager->RemoveSpawner(npc.m_ID);
@ -61,13 +68,13 @@ void VanityUtilities::SpawnVanity() {
} }
} }
m_Objects.clear(); objects.clear();
m_LoadedFiles.clear(); loadedFiles.clear();
ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity/root.xml").string()); ParseXml((BinaryPathFinder::GetBinaryDir() / "vanity/root.xml").string());
// Loop through all objects // 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; if (object.m_Locations.find(Game::server->GetZoneID()) == object.m_Locations.end()) continue;
const std::vector<VanityObjectLocation>& locations = object.m_Locations.at(Game::server->GetZoneID()); const std::vector<VanityObjectLocation>& locations = object.m_Locations.at(Game::server->GetZoneID());
@ -79,12 +86,6 @@ void VanityUtilities::SpawnVanity() {
float rate = GeneralUtils::GenerateRandomNumber<float>(0, 1); float rate = GeneralUtils::GenerateRandomNumber<float>(0, 1);
if (location.m_Chance < rate) continue; if (location.m_Chance < rate) continue;
if (object.m_Config.empty()) {
object.m_Config = {
new LDFData<std::vector<std::u16string>>(u"syncLDF", { u"custom_script_client" }),
new LDFData<std::u16string>(u"custom_script_client", u"scripts\\ai\\SPEC\\MISSION_MINIGAME_CLIENT.lua")
};
}
if (object.m_LOT == 176){ if (object.m_LOT == 176){
object.m_ID = SpawnSpawner(object, location); object.m_ID = SpawnSpawner(object, location);
} else { } else {
@ -94,20 +95,13 @@ void VanityUtilities::SpawnVanity() {
object.m_ID = objectEntity->GetObjectID(); object.m_ID = objectEntity->GetObjectID();
if (!object.m_Phrases.empty()){ if (!object.m_Phrases.empty()){
objectEntity->SetVar<std::vector<std::string>>(u"chats", object.m_Phrases); objectEntity->SetVar<std::vector<std::string>>(u"chats", object.m_Phrases);
auto* scriptComponent = objectEntity->GetComponent<ScriptComponent>();
if (scriptComponent && !object.m_Script.empty()) {
scriptComponent->SetScript(object.m_Script);
scriptComponent->SetSerialized(false);
}
SetupNPCTalk(objectEntity); SetupNPCTalk(objectEntity);
} }
} }
} }
} }
LWOOBJID VanityUtilities::SpawnSpawner(const VanityObject& object, const VanityObjectLocation& location) { LWOOBJID SpawnSpawner(const VanityObject& object, const VanityObjectLocation& location) {
SceneObject obj; SceneObject obj;
obj.lot = object.m_LOT; obj.lot = object.m_LOT;
// guratantee we have no collisions // guratantee we have no collisions
@ -121,7 +115,7 @@ LWOOBJID VanityUtilities::SpawnSpawner(const VanityObject& object, const VanityO
return obj.id; return obj.id;
} }
Entity* VanityUtilities::SpawnObject(const VanityObject& object, const VanityObjectLocation& location) { Entity* SpawnObject(const VanityObject& object, const VanityObjectLocation& location) {
EntityInfo info; EntityInfo info;
info.lot = object.m_LOT; info.lot = object.m_LOT;
info.pos = location.m_Position; info.pos = location.m_Position;
@ -131,18 +125,16 @@ Entity* VanityUtilities::SpawnObject(const VanityObject& object, const VanityObj
info.settings = object.m_Config; info.settings = object.m_Config;
auto* entity = Game::entityManager->CreateEntity(info); 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<bool>(u"noGhosting")) entity->SetIsGhostingCandidate(false); if (entity->GetVar<bool>(u"noGhosting")) entity->SetIsGhostingCandidate(false);
auto* inventoryComponent = entity->GetComponent<InventoryComponent>(); auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
if (inventoryComponent && !object.m_Equipment.empty()) { if (inventoryComponent && !object.m_Equipment.empty()) {
inventoryComponent->SetNPCItems(object.m_Equipment); inventoryComponent->SetNPCItems(object.m_Equipment);
} }
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent) {
if (destroyableComponent != nullptr) {
destroyableComponent->SetIsGMImmune(true); destroyableComponent->SetIsGMImmune(true);
destroyableComponent->SetMaxHealth(0); destroyableComponent->SetMaxHealth(0);
destroyableComponent->SetHealth(0); destroyableComponent->SetHealth(0);
@ -153,12 +145,12 @@ Entity* VanityUtilities::SpawnObject(const VanityObject& object, const VanityObj
return entity; return entity;
} }
void VanityUtilities::ParseXML(const std::string& file) { void ParseXml(const std::string& file) {
if (m_LoadedFiles.contains(file)){ if (loadedFiles.contains(file)){
LOG("Trying to load vanity file %s twice!!!", file.c_str()); LOG("Trying to load vanity file %s twice!!!", file.c_str());
return; return;
} }
m_LoadedFiles.insert(file); loadedFiles.insert(file);
// Read the entire file // Read the entire file
std::ifstream xmlFile(file); std::ifstream xmlFile(file);
std::string xml((std::istreambuf_iterator<char>(xmlFile)), std::istreambuf_iterator<char>()); std::string xml((std::istreambuf_iterator<char>(xmlFile)), std::istreambuf_iterator<char>());
@ -176,24 +168,26 @@ void VanityUtilities::ParseXML(const std::string& file) {
if (enabled != "1") { if (enabled != "1") {
continue; continue;
} }
ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity" / filename).string()); ParseXml((BinaryPathFinder::GetBinaryDir() / "vanity" / filename).string());
} }
} }
// Read the objects // Read the objects
auto* objects = doc.FirstChildElement("objects"); auto* objectsElement = doc.FirstChildElement("objects");
const uint32_t currentZoneID = Game::server->GetZoneID();
if (objects) { if (objectsElement) {
for (auto* object = objects->FirstChildElement("object"); object != nullptr; object = object->NextSiblingElement("object")) { 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 // Get the NPC name
auto* name = object->Attribute("name"); auto* name = object->Attribute("name");
if (!name) name = ""; if (!name) name = "";
// Get the NPC lot // Get the NPC lot
auto* lot = object->Attribute("lot"); auto lot = GeneralUtils::TryParse<LOT>(object->Attribute("lot")).value_or(LOT_NULL);
if (lot == nullptr) { if (lot == LOT_NULL) {
LOG("Failed to parse object lot"); LOG("Failed to parse object lot");
continue; continue;
} }
@ -211,17 +205,17 @@ void VanityUtilities::ParseXML(const std::string& file) {
std::vector<std::string> splitEquipment = GeneralUtils::SplitString(equipmentString, ','); std::vector<std::string> splitEquipment = GeneralUtils::SplitString(equipmentString, ',');
for (auto& item : splitEquipment) { 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<uint32_t>(item);
if (itemInt) inventory.push_back(itemInt.value());
} }
} }
} }
// Get the phrases // Get the phrases
auto* phrases = object->FirstChildElement("phrases"); auto* phrases = object->FirstChildElement("phrases");
std::vector<std::string> phraseList = {}; std::vector<std::string> phraseList = {};
if (phrases) { if (phrases) {
for (auto* phrase = phrases->FirstChildElement("phrase"); phrase != nullptr; for (auto* phrase = phrases->FirstChildElement("phrase"); phrase != nullptr;
phrase = phrase->NextSiblingElement("phrase")) { 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"); auto* configElement = object->FirstChildElement("config");
std::vector<std::u16string> keys = {}; std::vector<std::u16string> keys = {};
std::vector<LDFBaseData*> config = {}; std::vector<LDFBaseData*> config = {};
if(configElement) { if(configElement) {
for (auto* key = configElement->FirstChildElement("key"); key != nullptr; for (auto* key = configElement->FirstChildElement("key"); key != nullptr;
key = key->NextSiblingElement("key")) { key = key->NextSiblingElement("key")) {
// Get the config data // Get the config data
auto* data = key->Attribute("data"); auto* data = key->GetText();
if (!data) continue; if (!data) continue;
LDFBaseData* configData = LDFBaseData::DataFromString(data); LDFBaseData* configData = LDFBaseData::DataFromString(data);
if (configData->GetKey() == u"useLocationsAsRandomSpawnPoint" && configData->GetValueType() == eLDFType::LDF_TYPE_BOOLEAN){
useLocationsAsRandomSpawnPoint = static_cast<bool>(configData);
continue;
}
keys.push_back(configData->GetKey()); keys.push_back(configData->GetKey());
config.push_back(configData); config.push_back(configData);
} }
} }
if (!keys.empty()) config.push_back(new LDFData<std::vector<std::u16string>>(u"syncLDF", keys)); if (!keys.empty()) config.push_back(new LDFData<std::vector<std::u16string>>(u"syncLDF", keys));
VanityObject objectData; VanityObject objectData {
objectData.m_Name = name; .m_Name = name,
objectData.m_LOT = std::stoi(lot); .m_LOT = lot,
objectData.m_Equipment = inventory; .m_Equipment = inventory,
objectData.m_Phrases = phraseList; .m_Phrases = phraseList,
objectData.m_Script = scriptName; .m_Config = config
objectData.m_Config = config; };
// Get the locations // Get the locations
auto* locations = object->FirstChildElement("locations"); auto* locations = object->FirstChildElement("locations");
@ -283,64 +270,67 @@ void VanityUtilities::ParseXML(const std::string& file) {
location = location->NextSiblingElement("location")) { location = location->NextSiblingElement("location")) {
// Get the location data // Get the location data
auto* zoneID = location->Attribute("zone"); auto zoneID = GeneralUtils::TryParse<uint32_t>(location->Attribute("zone"));
auto* x = location->Attribute("x"); auto x = GeneralUtils::TryParse<float>(location->Attribute("x"));
auto* y = location->Attribute("y"); auto y = GeneralUtils::TryParse<float>(location->Attribute("y"));
auto* z = location->Attribute("z"); auto z = GeneralUtils::TryParse<float>(location->Attribute("z"));
auto* rw = location->Attribute("rw"); auto rw = GeneralUtils::TryParse<float>(location->Attribute("rw"));
auto* rx = location->Attribute("rx"); auto rx = GeneralUtils::TryParse<float>(location->Attribute("rx"));
auto* ry = location->Attribute("ry"); auto ry = GeneralUtils::TryParse<float>(location->Attribute("ry"));
auto* rz = location->Attribute("rz"); auto rz = GeneralUtils::TryParse<float>(location->Attribute("rz"));
if (zoneID == nullptr || x == nullptr || y == nullptr || z == nullptr || rw == nullptr || rx == nullptr || ry == nullptr if (!zoneID || !x || !y || !z || !rw || !rx || !ry || !rz) {
|| rz == nullptr) {
LOG("Failed to parse NPC location data"); LOG("Failed to parse NPC location data");
continue; continue;
} }
VanityObjectLocation locationData; if (zoneID.value() != currentZoneID) {
locationData.m_Position = { std::stof(x), std::stof(y), std::stof(z) }; LOG_DEBUG("Skipping location because it is in %i and not the current zone (%i)", zoneID.value(), currentZoneID);
locationData.m_Rotation = { std::stof(rw), std::stof(rx), std::stof(ry), std::stof(rz) }; continue;
locationData.m_Chance = 1.0f; }
VanityObjectLocation locationData {
.m_Position = { x.value(), y.value(), z.value() },
.m_Rotation = { rw.value(), rx.value(), ry.value(), rz.value() },
};
if (location->Attribute("chance")) { if (location->Attribute("chance")) {
locationData.m_Chance = std::stof(location->Attribute("chance")); locationData.m_Chance = GeneralUtils::TryParse<float>(location->Attribute("chance")).value_or(1.0f);
} }
if (location->Attribute("scale")) { if (location->Attribute("scale")) {
locationData.m_Scale = std::stof(location->Attribute("scale")); locationData.m_Scale = GeneralUtils::TryParse<float>(location->Attribute("scale")).value_or(1.0f);
} }
const auto& it = objectData.m_Locations.find(zoneID.value());
const auto& it = objectData.m_Locations.find(std::stoi(zoneID));
if (it != objectData.m_Locations.end()) { if (it != objectData.m_Locations.end()) {
it->second.push_back(locationData); it->second.push_back(locationData);
} else { } else {
std::vector<VanityObjectLocation> locations; std::vector<VanityObjectLocation> locations;
locations.push_back(locationData); 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())) { if (!useLocationsAsRandomSpawnPoint) {
m_Objects.push_back(objectData); objects.push_back(objectData);
objectData.m_Locations.clear(); 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) { VanityObject* VanityUtilities::GetObject(const std::string& name) {
for (size_t i = 0; i < m_Objects.size(); i++) { for (size_t i = 0; i < objects.size(); i++) {
if (m_Objects[i].m_Name == name) { if (objects[i].m_Name == name) {
return &m_Objects[i]; return &objects[i];
} }
} }
return nullptr; return nullptr;
} }
@ -350,7 +340,7 @@ std::string VanityUtilities::ParseMarkdown(const std::string& file) {
// Read the file into a string // Read the file into a string
std::ifstream t(file); std::ifstream t(file);
std::stringstream output; 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()) { if (!t.good()) {
output << "File "; output << "File ";
output << file.substr(file.rfind("/") + 1); output << file.substr(file.rfind("/") + 1);
@ -408,13 +398,13 @@ std::string VanityUtilities::ParseMarkdown(const std::string& file) {
return output.str(); return output.str();
} }
void VanityUtilities::SetupNPCTalk(Entity* npc) { void SetupNPCTalk(Entity* npc) {
npc->AddCallbackTimer(15.0f, [npc]() { NPCTalk(npc); }); npc->AddCallbackTimer(15.0f, [npc]() { NPCTalk(npc); });
npc->SetProximityRadius(20.0f, "talk"); npc->SetProximityRadius(20.0f, "talk");
} }
void VanityUtilities::NPCTalk(Entity* npc) { void NPCTalk(Entity* npc) {
auto* proximityMonitorComponent = npc->GetComponent<ProximityMonitorComponent>(); auto* proximityMonitorComponent = npc->GetComponent<ProximityMonitorComponent>();
if (!proximityMonitorComponent->GetProximityObjects("talk").empty()) { if (!proximityMonitorComponent->GetProximityObjects("talk").empty()) {

View File

@ -5,58 +5,30 @@
#include <map> #include <map>
#include <set> #include <set>
struct VanityObjectLocation struct VanityObjectLocation {
{
float m_Chance = 1.0f; float m_Chance = 1.0f;
NiPoint3 m_Position; NiPoint3 m_Position;
NiQuaternion m_Rotation; NiQuaternion m_Rotation;
float m_Scale = 1.0f; float m_Scale = 1.0f;
}; };
struct VanityObject struct VanityObject {
{
LWOOBJID m_ID = LWOOBJID_EMPTY; LWOOBJID m_ID = LWOOBJID_EMPTY;
std::string m_Name; std::string m_Name;
LOT m_LOT; LOT m_LOT = LOT_NULL;
std::vector<LOT> m_Equipment; std::vector<LOT> m_Equipment;
std::vector<std::string> m_Phrases; std::vector<std::string> m_Phrases;
std::string m_Script;
std::map<uint32_t, std::vector<VanityObjectLocation>> m_Locations; std::map<uint32_t, std::vector<VanityObjectLocation>> m_Locations;
std::vector<LDFBaseData*> m_Config; std::vector<LDFBaseData*> m_Config;
}; };
class VanityUtilities namespace VanityUtilities {
{ void SpawnVanity();
public:
static void SpawnVanity();
static Entity* SpawnObject( VanityObject* GetObject(const std::string& name);
const VanityObject& object,
const VanityObjectLocation& location
);
static LWOOBJID SpawnSpawner( std::string ParseMarkdown(
const VanityObject& object,
const VanityObjectLocation& location
);
static std::string ParseMarkdown(
const std::string& file 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<VanityObject> m_Objects;
static std::set<std::string> m_LoadedFiles;
}; };

View File

@ -5,20 +5,17 @@
#include "RenderComponent.h" #include "RenderComponent.h"
void DLUVanityTeleportingObject::OnStartup(Entity* self) { void DLUVanityTeleportingObject::OnStartup(Entity* self) {
if (!self->HasVar(u"npcName") || !self->HasVar(u"teleport")) return; if (!self->HasVar(u"npcName")) return;
m_Object = VanityUtilities::GetObject(self->GetVarAsString(u"npcName"));
m_Object = VanityUtilities::GetObject(self->GetVarAsString(u"npcName"));
if (!m_Object) return; if (!m_Object) return;
if (self->HasVar(u"teleportInterval")) m_TeleportInterval = self->GetVar<float>(u"teleportInterval"); if (self->HasVar(u"teleportInterval")) m_TeleportInterval = self->GetVar<float>(u"teleportInterval");
if (self->GetVar<bool>(u"teleport")) {
self->AddTimer("setupTeleport", m_TeleportInterval); self->AddTimer("setupTeleport", m_TeleportInterval);
} }
}
void DLUVanityTeleportingObject::OnTimerDone(Entity* self, std::string timerName) { void DLUVanityTeleportingObject::OnTimerDone(Entity* self, std::string timerName) {
if (timerName == "setupTeleport") { if (timerName == "setupTeleport") {
RenderComponent::PlayAnimation(self, u"interact");
GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportBeam", "teleportBeam"); GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportBeam", "teleportBeam");
GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportRings", "teleportRings"); 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->SetPosition(newLocation.m_Position);
self->SetRotation(newLocation.m_Rotation); self->SetRotation(newLocation.m_Rotation);
self->SetScale(newLocation.m_Scale);
GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportBeam", "teleportBeam"); GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportBeam", "teleportBeam");
GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportRings", "teleportRings"); GameMessages::SendPlayFXEffect(self->GetObjectID(), 6478, u"teleportRings", "teleportRings");
self->AddTimer("stopFX", 2.0f); self->AddTimer("stopFX", 2.0f);

View File

@ -1,23 +1,40 @@
<objects> <objects>
<object lot="13538"> <object lot="13538">
<config> <config>
<key data="CheckPrecondition=0:168"/> <!--Precondition 168 means the player must complete mission 1203 before being able to use an ATM-->
<key>CheckPrecondition=0:168</key>
</config> </config>
<locations> <locations>
<location zone="1100" x="248.792" y="381.869" z="-181.114" rw="0.782761" rx="0.00" ry="-0.622322" rz="0.00" /> <!--AG Sentinel Base Camp-->
<location zone="1100" x="471.545" y="413.979" z="27.176" rw="0.874378" rx="0.00" ry="-0.485246" rz="0.00" /> <location zone="1100" x="248.792" y="381.781" z="-181.114" rw="0.782761" rx="0.00" ry="-0.622322" rz="0.00" />
<!--AG Picnic Area-->
<location zone="1100" x="471.545" y="413.719" z="27.176" rw="0.874378" rx="0.00" ry="-0.485246" rz="0.00" />
<!--NS Nimbus Plaza-->
<location zone="1200" x="51.663" y="291.371" z="-74.650" rw="-0.446235" rx="0.00" ry="0.894916" rz="0.00" /> <location zone="1200" x="51.663" y="291.371" z="-74.650" rw="-0.446235" rx="0.00" ry="0.894916" rz="0.00" />
<location zone="1200" x="203.348" y="259.136" z="-543.400" rw="0.481554" rx="0.00" ry="0.876416" rz="0.00" /> <!--NS Red Blocks-->
<location zone="1201" x="46.537" y="233.137" z="-311.395" rw="0.780747" rx="0.00" ry="-0.624847" rz="0.00" /> <location zone="1200" x="203.348" y="259.0" z="-543.400" rw="0.481554" rx="0.00" ry="0.876416" rz="0.00" />
<!--PC by Lighthouse-->
<location zone="1201" x="46.537" y="232.958" z="-311.395" rw="0.780747" rx="0.00" ry="-0.624847" rz="0.00" />
<!--Frostburgh-->
<location zone="1260" x="-255.991" y="535.731" z="322.299" rw="0.683777" rx="0.00" ry="-0.729691" rz="0.00" /> <location zone="1260" x="-255.991" y="535.731" z="322.299" rw="0.683777" rx="0.00" ry="-0.729691" rz="0.00" />
<location zone="1600" x="85.210" y="1526.810" z="314.816" rw="-0.159486" rx="0.00" ry="0.987200" rz="0.00" /> <!--Starbase 3001-->
<location zone="1600" x="93.572" y="1526.970" z="311.905" rw="-0.159486" rx="0.00" ry="0.987200" rz="0.00" />
<!--LEGO Club-->
<location zone="1700" x="-256.293" y="1035.092" z="109.761" rw="0.00" rx="0.00" ry="1" rz="0.00" /> <location zone="1700" x="-256.293" y="1035.092" z="109.761" rw="0.00" rx="0.00" ry="1" rz="0.00" />
<!--GF Ravine-->
<location zone="1300" x="-199.258" y="246.874" z="-101.174" rw="-0.219711" rx="0.00" ry="0.975565" rz="0.00" /> <location zone="1300" x="-199.258" y="246.874" z="-101.174" rw="-0.219711" rx="0.00" ry="0.975565" rz="0.00" />
<!--GF Race Place-->
<location zone="1300" x="51.848" y="329.0" z="561.114" rw="-0.277656" rx="0.00" ry="0.960681" rz="0.00" /> <location zone="1300" x="51.848" y="329.0" z="561.114" rw="-0.277656" rx="0.00" ry="0.960681" rz="0.00" />
<!--GF Pirate Camp-->
<location zone="1300" x="363.259" y="259.367" z="-210.834" rw="0.961918" rx="0.00" ry="-0.273340" rz="0.00" /> <location zone="1300" x="363.259" y="259.367" z="-210.834" rw="0.961918" rx="0.00" ry="-0.273340" rz="0.00" />
<!--FV Great Tree-->
<location zone="1400" x="-194.288" y="381.275" z="-93.292" rw="0.935135" rx="0.00" ry="0.354292" rz="0.00" /> <location zone="1400" x="-194.288" y="381.275" z="-93.292" rw="0.935135" rx="0.00" ry="0.354292" rz="0.00" />
<!--FV Paradox Refinery-->
<location zone="1400" x="-696.957" y="-3.206" z="-452.441" rw="0.884105" rx="0.00" ry="0.467288" rz="0.00" /> <location zone="1400" x="-696.957" y="-3.206" z="-452.441" rw="0.884105" rx="0.00" ry="0.467288" rz="0.00" />
<location zone="1800" x="-222.634" y="92.693" z="568.392" rw="-0.435024" rx="0.00" ry="0.900419" rz="0.00" /> <!--CP Sentinel Point Zeta-->
<location zone="1800" x="-222.634" y="92.373" z="568.392" rw="-0.435024" rx="0.00" ry="0.900419" rz="0.00" />
<!--NJ Monastery Courtyard-->
<location zone="2000" x="-63.487" y="208.270" z="379.195" rw="0.00" rx="0.00" ry="1" rz="0.00" />
</locations> </locations>
</object> </object>
</objects> </objects>

132
vanity/demo.xml Normal file
View File

@ -0,0 +1,132 @@
<objects>
<!--A tree spawned at two locations with different positions, rotations, and scales-->
<!--Positions and rotations are easily gotten by typing /loc or /pos, and /rot into the in-game chat-->
<object lot="3248">
<locations>
<location zone="1200" x="-15.0" y="288.8" z="-167.0" rw="0.984321" rx="0.00" ry="0.176388" rz="0.00" />
<location zone="1200" x="15.0" y="288.8" z="-158.0" rw="0.724628" rx="0.00" ry="-0.689141" rz="0.00" scale="0.30" />
</locations>
</object>
<!--A vendor who we will give GM-only items-->
<object name="Demo Fella - GM Items Vendor" lot="1867">
<equipment>7630, 1727, 7453, 7521</equipment>
<config>
<key>vendorInvOverride=0:1727,7292,16553,2243,14535,14538,14531,6730</key>
</config>
<locations>
<location zone="1200" x="35.935" y="288.896" z="-128.213" rw="0.882977" rx="0.00" ry="-0.469416" rz="0.00" />
</locations>
</object>
<!--Friendly Felix will choose one of the 3 locations, then have a 50% chance to spawn at one of them on world server startup-->
<object lot="10141">
<config>
<key>useLocationsAsRandomSpawnPoint=7:1</key>
</config>
<locations>
<location zone="1200" x="31.819" y="288.896" z="-117.095" rw="0.630659" rx="0.00" ry="-0.776060" rz="0.00" chance="0.50"/>
<location zone="1200" x="42.755" y="291.897" z="-144.385" rw="0.855306" rx="0.00" ry="-0.518124" rz="0.00" chance="0.50"/>
<location zone="1200" x="3.984" y="288.896" z="-165.947" rw="0.978508" rx="0.00" ry="-0.206210" rz="0.00" chance="0.50"/>
</locations>
</object>
<!--Spawner(s) for enemies, largely copy-pasted from Portabello in this case-->
<object lot="176">
<config>
<key>CheckPrecondition=0:</key>
<key>SmashableDoesNotCutNavmesh=7:0</key>
<key>add_to_navmesh=7:1</key>
<key>aggroRadius=3:15</key>
<key>camGradSnap=7:0</key>
<key>camPrefersToFadeObject=7:1</key>
<key>carver_only=7:0</key>
<key>create_physics=7:1</key>
<key>currency=5:0</key>
<key>custom_config_names=0:</key>
<key>explode_factor=3:1</key>
<key>fxpriority=1:0</key>
<key>gravFactor=3:1</key>
<key>grpNameQBShowBricks=0:</key>
<key>ignoreCameraCollision=7:0</key>
<key>interaction_distance=3:16</key>
<key>is_smashable=7:1</key>
<key>loadOnClientOnly=7:0</key>
<key>loadSrvrOnly=7:0</key>
<key>max_to_spawn=1:-1</key>
<key>navmesh_carver=7:0</key>
<key>no_auto_spawn=7:1</key>
<key>no_timed_spawn=7:1</key>
<key>override_faction=7:0</key>
<key>radius=3:0</key>
<key>renderAnimLODSkew=3:1</key>
<key>renderCullingGroup=5:0</key>
<key>renderOffscreenAnimEnabled=7:0</key>
<key>respawn=3:20</key>
<key>sceneIDOverride=1:255</key>
<key>sceneIDOverrideEnabled=7:0</key>
<key>sceneLayerIDOverride=5:0</key>
<key>set_faction=13:4</key>
<key>smashable_loot_matrix=1:486</key>
<key>smashable_loot_matrix_set=7:0</key>
<key>softtetherRadius=3:15</key>
<key>spawner_active_on_load=7:1</key>
<key>spawntemplate=1:12379</key>
<key>startsQBActivator=7:0</key>
<key>template=1:-1</key>
<key>tetherRadius=3:15</key>
<key>usetetherdb=7:0</key>
<key>usewanderdb=7:0</key>
<key>wanderRadius=3:15</key>
</config>
<locations>
<location zone="1200" x="-16.749" y="291.841" z="-122.349" rw="1.00" rx="0.00" ry="0.00" rz="0.00" />
<location zone="1200" x="-15.696" y="291.608" z="-136.902" rw="1.00" rx="0.00" ry="0.00" rz="0.00" />
</locations>
</object>
<!--Spawner for a crate-->
<object lot="176">
<config>
<key>CheckPrecondition=0:</key>
<key>SmashableDoesNotCutNavmesh=7:0</key>
<key>add_to_navmesh=7:1</key>
<key>bounding_radius_override=3:0</key>
<key>camGradSnap=7:0</key>
<key>camPrefersToFadeObject=7:1</key>
<key>carver_only=7:0</key>
<key>create_physics=7:1</key>
<key>custom_config_names=0:</key>
<key>explode_factor=3:1</key>
<key>friction=3:1.5</key>
<key>fxpriority=1:0</key>
<key>gravFactor=3:1</key>
<key>grpNameQBShowBricks=0:</key>
<key>ignoreCameraCollision=7:0</key>
<key>interaction_distance=3:16</key>
<key>is_smashable=7:1</key>
<key>loadOnClientOnly=7:0</key>
<key>max_to_spawn=1:-1</key>
<key>navmesh_carver=7:0</key>
<key>no_auto_spawn=7:1</key>
<key>no_timed_spawn=7:1</key>
<key>override_faction=7:0</key>
<key>radius=3:0</key>
<key>renderCullingGroup=5:0</key>
<key>respawn=5:20000</key>
<key>sceneIDOverride=1:255</key>
<key>sceneIDOverrideEnabled=7:0</key>
<key>sceneLayerIDOverride=5:0</key>
<key>set_faction=13:6 </key>
<key>smashable_loot_matrix=1:227</key>
<key>smashable_loot_matrix_set=7:0</key>
<key>spawner_active_on_load=7:1</key>
<key>spawntemplate=1:2295</key>
<key>startsQBActivator=7:0</key>
<key>template=1:-1</key>
</config>
<locations>
<location zone="1200" x="4.232" y="288.895" z="-85.846" rw="-0.205988" rx="0.00" ry="0.978555" rz="0.00" />
</locations>
</object>
</objects>

View File

@ -11,6 +11,9 @@
<phrase>Everything is awesome!</phrase> <phrase>Everything is awesome!</phrase>
<phrase>I hope my behaviors are behaving themselves.</phrase> <phrase>I hope my behaviors are behaving themselves.</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1200" x="-352.5" y="287.6" z="217.7" rw="-0.095" rx="0.0" ry="0.995" rz="0.0" /> <location zone="1200" x="-352.5" y="287.6" z="217.7" rw="-0.095" rx="0.0" ry="0.995" rz="0.0" />
</locations> </locations>
@ -22,6 +25,9 @@
<phrase>Be careful crossing the gap!</phrase> <phrase>Be careful crossing the gap!</phrase>
<phrase>Have The Maelstrom stopped going invisible?</phrase> <phrase>Have The Maelstrom stopped going invisible?</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1800" x="745.756" y="75.262" z="-207.989" rw="0.838565" rx="0.0" ry="0.544801" rz="0.0" /> <location zone="1800" x="745.756" y="75.262" z="-207.989" rw="0.838565" rx="0.0" ry="0.544801" rz="0.0" />
</locations> </locations>
@ -33,26 +39,30 @@
<phrase>Have you heard about Darkflame?</phrase> <phrase>Have you heard about Darkflame?</phrase>
<phrase>I've traveled across this entire system, but nothing beats the view here.</phrase> <phrase>I've traveled across this entire system, but nothing beats the view here.</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1200" x="163.835" y="330.756" z="-141.933" rw="0.774887" rx="0.0" ry="-0.6321" rz="0.0" /> <location zone="1200" x="163.835" y="330.756" z="-141.933" rw="0.774887" rx="0.0" ry="-0.6321" rz="0.0" />
</locations> </locations>
</object> </object>
<object name="averysumner - Destroyer of Worlds" lot="11235"> <object name="averysumner - Destroyer of Worlds" lot="11235">
<equipment></equipment>
<phrases> <phrases>
<phrase>cmerw[acowipaejio;fawjioefasdl;kfjm;</phrase> <phrase>cmerw[acowipaejio;fawjioefasdl;kfjm;</phrase>
<phrase>I, for one, welcome our new robot overlords.</phrase> <phrase>I, for one, welcome our new robot overlords.</phrase>
<phrase>zxnpoasdfiopwemsadf'kawpfo[ekasdf;'s</phrase> <phrase>zxnpoasdfiopwemsadf'kawpfo[ekasdf;'s</phrase>
<phrase>*teleports behind you*</phrase> <phrase>*teleports behind you*</phrase>
</phrases> </phrases>
<script name="scripts\02_server\DLU\DLUVanityTeleportingObject.lua" />
<config> <config>
<key data="teleport=7:1" /> <key>useLocationsAsRandomSpawnPoint=7:1</key>
<key data="teleportInterval=3:3.0" /> <key>teleportInterval=3:15.0</key>
<key>custom_script_server=0:scripts\02_server\DLU\DLUVanityTeleportingObject.lua</key>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config> </config>
<locations> <locations>
<location zone="1200" x="-361.583" y="285.541" z="64.4695" rw="0.785518" rx="0.0" ry="0.618838" rz="0.0" /> <location zone="1200" x="-361.583" y="285.541" z="64.4695" rw="0.785518" rx="0.0" ry="0.618838" rz="0.0" />
<location zone="1200" x="178.188" y="354.528" z="-173.932" rw="0.734375" rx="0.0" ry="-0.678744" rz="0.0" /> <location zone="1200" x="178.188" y="354.528" z="-173.932" rw="0.734375" rx="0.0" ry="-0.678744" rz="0.0" />
<location zone="1200" x="-318.569" y="287.637695" z="226.728" rw="-0.289502" rx="0.0" ry="0.957178" rz="0.0" />
<location zone="1200" x="389.093" y="295.119" z="-647.583" rw="0.851229" rx="0.0" ry="-0.524795" rz="0.0" /> <location zone="1200" x="389.093" y="295.119" z="-647.583" rw="0.851229" rx="0.0" ry="-0.524795" rz="0.0" />
</locations> </locations>
</object> </object>
@ -69,10 +79,11 @@
<phrase>I have been idle for 10 minutes, and will be returned to the login screen in 5 minutes.</phrase> <phrase>I have been idle for 10 minutes, and will be returned to the login screen in 5 minutes.</phrase>
<phrase>Of what has one to be proud, if dnot one's friends?</phrase> <phrase>Of what has one to be proud, if dnot one's friends?</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1100" x="287" y="451" z="-98" rw="-0.320221" rx="0" ry="0.947343" rz="0" /> <location zone="1100" x="287" y="451" z="-98" rw="-0.320221" rx="0" ry="0.947343" rz="0" />
</locations>
<locations>
<location zone="1800" x="798" y="93" z="325" rw="-0.095" rx="0.0" ry="0.995" rz="0.0" /> <location zone="1800" x="798" y="93" z="325" rw="-0.095" rx="0.0" ry="0.995" rz="0.0" />
</locations> </locations>
</object> </object>
@ -100,11 +111,13 @@
<phrase>Gather uh banana</phrase> <phrase>Gather uh banana</phrase>
<phrase>I've done nothing all day. Why am I here?</phrase> <phrase>I've done nothing all day. Why am I here?</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1100" x="444.318" y="421.154" z="54.4241" rw="0.877539" rx="0.0" ry="0.479506" rz="0.0" /> <location zone="1100" x="444.318" y="421.154" z="54.4241" rw="0.877539" rx="0.0" ry="0.479506" rz="0.0" />
</locations> </locations>
</object> </object>
<!-- Mick - Brick Market Broker -->
<object name="Mick - Brick Market Broker" lot="6876"> <object name="Mick - Brick Market Broker" lot="6876">
<equipment>2519, 4091, 5128, 7990</equipment> <equipment>2519, 4091, 5128, 7990</equipment>
<phrases> <phrases>
@ -119,11 +132,13 @@
<phrase>I know a guy that sells jetpacks, it ain't cheap but everything has a price.</phrase> <phrase>I know a guy that sells jetpacks, it ain't cheap but everything has a price.</phrase>
<phrase>You know Dr. Overbuild? He keeps ignoring my calls.</phrase> <phrase>You know Dr. Overbuild? He keeps ignoring my calls.</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1100" x="449.725" y="414.926" z="180.539" rw="0.180645" rx="0.0" ry="0.983548" rz="0.0" /> <location zone="1100" x="449.725" y="414.926" z="180.539" rw="0.180645" rx="0.0" ry="0.983548" rz="0.0" />
</locations> </locations>
</object> </object>
<!-- Knightoffaith - Sage of Wanderlust -->
<object name="Knightoffaith - Sage of Wanderlust" lot="12260"> <object name="Knightoffaith - Sage of Wanderlust" lot="12260">
<equipment>7359, 7368, 7380, 7392, 7403, 8456</equipment> <equipment>7359, 7368, 7380, 7392, 7403, 8456</equipment>
<phrases> <phrases>
@ -139,6 +154,9 @@
<phrase>Don't look at me, it was Sam's turn to look after Burno!</phrase> <phrase>Don't look at me, it was Sam's turn to look after Burno!</phrase>
<phrase>The Universe is beautiful - take some time to enjoy it</phrase> <phrase>The Universe is beautiful - take some time to enjoy it</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1100" x="-381.053" y="367.787" z="-60.1185" rw="-0.14307" rx="0" ry="0.989713" rz="0" /> <location zone="1100" x="-381.053" y="367.787" z="-60.1185" rw="-0.14307" rx="0" ry="0.989713" rz="0" />
</locations> </locations>
@ -155,6 +173,9 @@
<phrase>Hope you enjoy DLU as much as I have, explorer!</phrase> <phrase>Hope you enjoy DLU as much as I have, explorer!</phrase>
<phrase>There are many more memories still to be made.</phrase> <phrase>There are many more memories still to be made.</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1200" x="-429.296" y="291.058" z="212.918" rw="0.339487" rx="0" ry="0.940611" rz="0" /> <location zone="1200" x="-429.296" y="291.058" z="212.918" rw="0.339487" rx="0" ry="0.940611" rz="0" />
</locations> </locations>
@ -169,6 +190,9 @@
<phrase>There are many amazing properties to explore across the universe. You can visit some of them from the launchpads right here in Brick Annex.</phrase> <phrase>There are many amazing properties to explore across the universe. You can visit some of them from the launchpads right here in Brick Annex.</phrase>
<phrase>Don't stop believing! Uhh, I mean building. Don't stop building!</phrase> <phrase>Don't stop believing! Uhh, I mean building. Don't stop building!</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1200" x="-317.509" y="287.652" z="191.86" rw="0.57124" rx="0" ry="-0.820783" rz="0" /> <location zone="1200" x="-317.509" y="287.652" z="191.86" rw="0.57124" rx="0" ry="-0.820783" rz="0" />
</locations> </locations>
@ -185,6 +209,9 @@
<phrase>Look at that view!</phrase> <phrase>Look at that view!</phrase>
<phrase>Oxidize!</phrase> <phrase>Oxidize!</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1300" x="283.986" y="261.208" z="-128.466" rw="0.70207" rx="0" ry="0.712108" rz="0" /> <location zone="1300" x="283.986" y="261.208" z="-128.466" rw="0.70207" rx="0" ry="0.712108" rz="0" />
</locations> </locations>
@ -197,6 +224,9 @@
<phrase>When you do extra steps, you can always customize the steps without redoing the whole staircase</phrase> <phrase>When you do extra steps, you can always customize the steps without redoing the whole staircase</phrase>
<phrase>Who needs lamps, when you have Tiki Torches</phrase> <phrase>Who needs lamps, when you have Tiki Torches</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1300" x="204.93" y="294.784" z="471.537" rw="0.85015" rx="0" ry="-0.52654" rz="0" /> <location zone="1300" x="204.93" y="294.784" z="471.537" rw="0.85015" rx="0" ry="-0.52654" rz="0" />
</locations> </locations>
@ -213,6 +243,9 @@
<phrase>There's treasure inside of all of us. It's called Imagination.</phrase> <phrase>There's treasure inside of all of us. It's called Imagination.</phrase>
<phrase>Stromlings! Why did it have to be Stromlings?</phrase> <phrase>Stromlings! Why did it have to be Stromlings?</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1300" x="-47.6296" y="322.527" z="533.5" rw="0.145135" rx="0" ry="0.989412" rz="0" /> <location zone="1300" x="-47.6296" y="322.527" z="533.5" rw="0.145135" rx="0" ry="0.989412" rz="0" />
</locations> </locations>
@ -225,13 +258,15 @@
<phrase>Wait, did the game close yet?</phrase> <phrase>Wait, did the game close yet?</phrase>
<phrase>... What year is it?</phrase> <phrase>... What year is it?</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1400" x="-490.608" y="51.9449" z="-347.747" rw="0.594978" rx="0" ry="-0.803742" rz="0" /> <location zone="1400" x="-490.608" y="51.9449" z="-347.747" rw="0.594978" rx="0" ry="-0.803742" rz="0" />
</locations> </locations>
</object> </object>
<object name="Wicked" lot="7412"> <object name="Wicked" lot="7412">
<equipment>8287, 2957, 7523, 16613</equipment> <equipment>8287, 2957, 7523, 16613</equipment>
<phrases></phrases>
<locations> <locations>
<location zone="1400" x="264.506" y="339.931" z="54.1201" rw="0.715223" rx="0" ry="0.698896" rz="0" /> <location zone="1400" x="264.506" y="339.931" z="54.1201" rw="0.715223" rx="0" ry="0.698896" rz="0" />
</locations> </locations>
@ -248,17 +283,22 @@
<phrase>What do you mean my title is misspelled?</phrase> <phrase>What do you mean my title is misspelled?</phrase>
<phrase>When I was your age, numbers were illegal.</phrase> <phrase>When I was your age, numbers were illegal.</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1900" x="-493.724" y="1124.05" z="-76.6355" rw="0.1719" rx="0" ry="0.985114" rz="0" /> <location zone="1900" x="-493.724" y="1124.05" z="-76.6355" rw="0.1719" rx="0" ry="0.985114" rz="0" />
</locations> </locations>
</object> </object>
12651
<object name="Matthew - ?????" lot="2279"> <object name="Matthew - ?????" lot="2279">
<equipment>9856, 7793, 6928, 6927</equipment> <equipment>9856, 7793, 6928, 6927</equipment>
<phrases> <phrases>
<phrase>I think I have a migraine from staring at this for so long</phrase> <phrase>I think I have a migraine from staring at this for so long</phrase>
<phrase>Anything but Portabello</phrase> <phrase>Anything but Portabello</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1900" x="-227.621" y="1188.28" z="145.734" rw="-0.254353" rx="0" ry="0.967111" rz="0" /> <location zone="1900" x="-227.621" y="1188.28" z="145.734" rw="-0.254353" rx="0" ry="0.967111" rz="0" />
</locations> </locations>
@ -268,6 +308,9 @@
<phrases> <phrases>
<phrase>Per Aspera ad Astra</phrase> <phrase>Per Aspera ad Astra</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1900" x="-190.673" y="1218.34" z="221.6" rw="0.972371" rx="0" ry="-0.233441" rz="0" /> <location zone="1900" x="-190.673" y="1218.34" z="221.6" rw="0.972371" rx="0" ry="-0.233441" rz="0" />
</locations> </locations>
@ -280,6 +323,9 @@
<phrase>Have you tried those buttery croissants from “Farnham Spoon” they are delicious! I might just go and have another</phrase> <phrase>Have you tried those buttery croissants from “Farnham Spoon” they are delicious! I might just go and have another</phrase>
<phrase>Have fun!</phrase> <phrase>Have fun!</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1800" x="-39.2259" y="96.8605" z="-550.077" rw="0.815145" rx="0" ry="-0.579257" rz="0" /> <location zone="1800" x="-39.2259" y="96.8605" z="-550.077" rw="0.815145" rx="0" ry="-0.579257" rz="0" />
</locations> </locations>
@ -291,6 +337,9 @@
<phrase>Wu once told me, "The path we seek is never a straight line"."</phrase> <phrase>Wu once told me, "The path we seek is never a straight line"."</phrase>
<phrase>Stop! I'm from the future to warn you that there will be snakes, ninja robots! More snakes, ghosts, sky pirates, snakes that make snakes! Bikers, dragon hunters... Wait what do you mean it hasn't happened yet?</phrase> <phrase>Stop! I'm from the future to warn you that there will be snakes, ninja robots! More snakes, ghosts, sky pirates, snakes that make snakes! Bikers, dragon hunters... Wait what do you mean it hasn't happened yet?</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="2000" x="-119.269" y="230.372" z="226.63" rw="0.381416" rx="0" ry="0.924404" rz="0" /> <location zone="2000" x="-119.269" y="230.372" z="226.63" rw="0.381416" rx="0" ry="0.924404" rz="0" />
</locations> </locations>
@ -312,6 +361,9 @@
<phrase>Gonk.</phrase> <phrase>Gonk.</phrase>
<phrase>There is a theory which states that if ever anyone discovers exactly what the Universe is for and why it is here, it will instantly disappear and be replaced by something even more bizarre and inexplicable. There is another theory, which states that this has already happened.</phrase> <phrase>There is a theory which states that if ever anyone discovers exactly what the Universe is for and why it is here, it will instantly disappear and be replaced by something even more bizarre and inexplicable. There is another theory, which states that this has already happened.</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1200" x="197.549" y="259.137" z="-587.759" rw="0.873694" rx="0" ry="0.486475" rz="0" /> <location zone="1200" x="197.549" y="259.137" z="-587.759" rw="0.873694" rx="0" ry="0.486475" rz="0" />
</locations> </locations>
@ -335,13 +387,15 @@
<phrase>Excuse me, do you know the way to Frostburgh?</phrase> <phrase>Excuse me, do you know the way to Frostburgh?</phrase>
<phrase>What are those Paradox scientists up to these days?</phrase> <phrase>What are those Paradox scientists up to these days?</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1100" x="436.949" y="415.264" z="151.381" rw="0.528425" rx="0" ry="0.84898" rz="0" /> <location zone="1100" x="436.949" y="415.264" z="151.381" rw="0.528425" rx="0" ry="0.84898" rz="0" />
</locations> </locations>
</object> </object>
<object name="Ben" lot="2279"> <object name="Ben" lot="2279">
<equipment>8664, 4065, 2587</equipment> <equipment>8664, 4065, 2587</equipment>
<phrases></phrases>
<locations> <locations>
<location zone="1200" x="284.538" y="260.627" z="-506.692" rw="0.819721" rx="0" ry="0.572763" rz="0" /> <location zone="1200" x="284.538" y="260.627" z="-506.692" rw="0.819721" rx="0" ry="0.572763" rz="0" />
</locations> </locations>
@ -354,6 +408,9 @@
<phrase>I think I may be lost...</phrase> <phrase>I think I may be lost...</phrase>
<phrase>I'm feeling a bit emotionally conflicted right now</phrase> <phrase>I'm feeling a bit emotionally conflicted right now</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
</config>
<locations> <locations>
<location zone="1300" x="-171.217" y="246.482" z="-147.05" rw="-0.203118" rx="0" ry="0.979154" rz="0" /> <location zone="1300" x="-171.217" y="246.482" z="-147.05" rw="-0.203118" rx="0" ry="0.979154" rz="0" />
</locations> </locations>
@ -371,6 +428,10 @@
<phrase>I love cats, little meow meows</phrase> <phrase>I love cats, little meow meows</phrase>
<phrase>It's not perfect, but it works!</phrase> <phrase>It's not perfect, but it works!</phrase>
</phrases> </phrases>
<config>
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
<key>vendorInvOverride=0:11909,7785,12764,12241</key>
</config>
<locations> <locations>
<location zone="1201" x="197.709" y="179.335" z="-8.05284" rw="0.544424" rx="0" ry="-0.83881" rz="0" /> <location zone="1201" x="197.709" y="179.335" z="-8.05284" rw="0.544424" rx="0" ry="-0.83881" rz="0" />
</locations> </locations>

View File

@ -1,4 +1,5 @@
<files> <files>
<file name="dev-tribute.xml" enabled="1"/> <file name="dev-tribute.xml" enabled="1"/>
<file name="atm.xml" enabled="0"/> <file name="atm.xml" enabled="0"/>
<file name="demo.xml" enabled="0"/>
</files> </files>