mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-11-24 22:47:25 +00:00
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:
parent
ef3fdba621
commit
43707952d2
@ -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)
|
||||
|
@ -2191,9 +2191,3 @@ void Entity::SetRespawnRot(const NiQuaternion& rotation) {
|
||||
auto* characterComponent = GetComponent<CharacterComponent>();
|
||||
if (characterComponent) characterComponent->SetRespawnRot(rotation);
|
||||
}
|
||||
|
||||
void Entity::SetScale(const float scale) {
|
||||
if (scale == m_Scale) return;
|
||||
m_Scale = scale;
|
||||
Game::entityManager->SerializeEntity(this);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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<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;
|
||||
}
|
||||
|
||||
@ -52,24 +62,12 @@ void VendorComponent::RefreshInventory(bool isCreation) {
|
||||
if (lootMatrices.empty()) return;
|
||||
|
||||
auto* lootTableTable = CDClientManager::GetTable<CDLootTableTable>();
|
||||
auto* itemComponentTable = CDClientManager::GetTable<CDItemComponentTable>();
|
||||
auto* compRegistryTable = CDClientManager::GetTable<CDComponentsRegistryTable>();
|
||||
|
||||
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<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);
|
||||
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<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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -22,9 +22,18 @@
|
||||
|
||||
#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() {
|
||||
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<bool>(u"hasCustomText", true),
|
||||
new LDFData<std::string>(u"customText", ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/TESTAMENT.md").string())) };
|
||||
info.settings = {
|
||||
new LDFData<bool>(u"hasCustomText", true),
|
||||
new LDFData<std::string>(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<VanityObjectLocation>& locations = object.m_Locations.at(Game::server->GetZoneID());
|
||||
@ -79,12 +86,6 @@ void VanityUtilities::SpawnVanity() {
|
||||
float rate = GeneralUtils::GenerateRandomNumber<float>(0, 1);
|
||||
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){
|
||||
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<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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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<bool>(u"noGhosting")) entity->SetIsGhostingCandidate(false);
|
||||
|
||||
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventoryComponent && !object.m_Equipment.empty()) {
|
||||
inventoryComponent->SetNPCItems(object.m_Equipment);
|
||||
}
|
||||
|
||||
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
|
||||
|
||||
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<char>(xmlFile)), std::istreambuf_iterator<char>());
|
||||
@ -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<LOT>(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<std::string> 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<uint32_t>(item);
|
||||
if (itemInt) inventory.push_back(itemInt.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get the phrases
|
||||
auto* phrases = object->FirstChildElement("phrases");
|
||||
|
||||
std::vector<std::string> 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<std::u16string> keys = {};
|
||||
|
||||
std::vector<LDFBaseData*> 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<bool>(configData);
|
||||
continue;
|
||||
}
|
||||
keys.push_back(configData->GetKey());
|
||||
config.push_back(configData);
|
||||
}
|
||||
}
|
||||
if (!keys.empty()) config.push_back(new LDFData<std::vector<std::u16string>>(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<uint32_t>(location->Attribute("zone"));
|
||||
auto x = GeneralUtils::TryParse<float>(location->Attribute("x"));
|
||||
auto y = GeneralUtils::TryParse<float>(location->Attribute("y"));
|
||||
auto z = GeneralUtils::TryParse<float>(location->Attribute("z"));
|
||||
auto rw = GeneralUtils::TryParse<float>(location->Attribute("rw"));
|
||||
auto rx = GeneralUtils::TryParse<float>(location->Attribute("rx"));
|
||||
auto ry = GeneralUtils::TryParse<float>(location->Attribute("ry"));
|
||||
auto rz = GeneralUtils::TryParse<float>(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<float>(location->Attribute("chance")).value_or(1.0f);
|
||||
}
|
||||
|
||||
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(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<VanityObjectLocation> 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<ProximityMonitorComponent>();
|
||||
|
||||
if (!proximityMonitorComponent->GetProximityObjects("talk").empty()) {
|
||||
|
@ -5,58 +5,30 @@
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
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<LOT> m_Equipment;
|
||||
std::vector<std::string> m_Phrases;
|
||||
std::string m_Script;
|
||||
std::map<uint32_t, std::vector<VanityObjectLocation>> m_Locations;
|
||||
std::vector<LDFBaseData*> 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<VanityObject> m_Objects;
|
||||
|
||||
static std::set<std::string> m_LoadedFiles;
|
||||
};
|
||||
|
@ -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<float>(u"teleportInterval");
|
||||
|
||||
if (self->GetVar<bool>(u"teleport")) {
|
||||
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);
|
||||
|
@ -1,23 +1,40 @@
|
||||
<objects>
|
||||
<object lot="13538">
|
||||
<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>
|
||||
<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" />
|
||||
<location zone="1100" x="471.545" y="413.979" z="27.176" rw="0.874378" rx="0.00" ry="-0.485246" rz="0.00" />
|
||||
<!--AG Sentinel Base Camp-->
|
||||
<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="203.348" y="259.136" z="-543.400" rw="0.481554" rx="0.00" ry="0.876416" rz="0.00" />
|
||||
<location zone="1201" x="46.537" y="233.137" z="-311.395" rw="0.780747" rx="0.00" ry="-0.624847" rz="0.00" />
|
||||
<!--NS Red Blocks-->
|
||||
<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="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" />
|
||||
<!--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" />
|
||||
<!--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" />
|
||||
<!--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" />
|
||||
<!--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" />
|
||||
<!--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="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>
|
||||
</object>
|
||||
</objects>
|
||||
|
132
vanity/demo.xml
Normal file
132
vanity/demo.xml
Normal 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>
|
@ -11,6 +11,9 @@
|
||||
<phrase>Everything is awesome!</phrase>
|
||||
<phrase>I hope my behaviors are behaving themselves.</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<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" />
|
||||
</locations>
|
||||
@ -22,6 +25,9 @@
|
||||
<phrase>Be careful crossing the gap!</phrase>
|
||||
<phrase>Have The Maelstrom stopped going invisible?</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<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" />
|
||||
</locations>
|
||||
@ -33,26 +39,30 @@
|
||||
<phrase>Have you heard about Darkflame?</phrase>
|
||||
<phrase>I've traveled across this entire system, but nothing beats the view here.</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<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" />
|
||||
</locations>
|
||||
</object>
|
||||
<object name="averysumner - Destroyer of Worlds" lot="11235">
|
||||
<equipment></equipment>
|
||||
<phrases>
|
||||
<phrase>cmerw[acowipaejio;fawjioefasdl;kfjm;</phrase>
|
||||
<phrase>I, for one, welcome our new robot overlords.</phrase>
|
||||
<phrase>zxnpoasdfiopwemsadf'kawpfo[ekasdf;'s</phrase>
|
||||
<phrase>*teleports behind you*</phrase>
|
||||
</phrases>
|
||||
<script name="scripts\02_server\DLU\DLUVanityTeleportingObject.lua" />
|
||||
<config>
|
||||
<key data="teleport=7:1" />
|
||||
<key data="teleportInterval=3:3.0" />
|
||||
<key>useLocationsAsRandomSpawnPoint=7:1</key>
|
||||
<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>
|
||||
<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="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" />
|
||||
</locations>
|
||||
</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>Of what has one to be proud, if dnot one's friends?</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<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" />
|
||||
</locations>
|
||||
</object>
|
||||
@ -100,11 +111,13 @@
|
||||
<phrase>Gather uh banana</phrase>
|
||||
<phrase>I've done nothing all day. Why am I here?</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<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" />
|
||||
</locations>
|
||||
</object>
|
||||
<!-- Mick - Brick Market Broker -->
|
||||
<object name="Mick - Brick Market Broker" lot="6876">
|
||||
<equipment>2519, 4091, 5128, 7990</equipment>
|
||||
<phrases>
|
||||
@ -119,11 +132,13 @@
|
||||
<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>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<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" />
|
||||
</locations>
|
||||
</object>
|
||||
<!-- Knightoffaith - Sage of Wanderlust -->
|
||||
<object name="Knightoffaith - Sage of Wanderlust" lot="12260">
|
||||
<equipment>7359, 7368, 7380, 7392, 7403, 8456</equipment>
|
||||
<phrases>
|
||||
@ -139,6 +154,9 @@
|
||||
<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>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1100" x="-381.053" y="367.787" z="-60.1185" rw="-0.14307" rx="0" ry="0.989713" rz="0" />
|
||||
</locations>
|
||||
@ -155,6 +173,9 @@
|
||||
<phrase>Hope you enjoy DLU as much as I have, explorer!</phrase>
|
||||
<phrase>There are many more memories still to be made.</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1200" x="-429.296" y="291.058" z="212.918" rw="0.339487" rx="0" ry="0.940611" rz="0" />
|
||||
</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>Don't stop believing! Uhh, I mean building. Don't stop building!</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1200" x="-317.509" y="287.652" z="191.86" rw="0.57124" rx="0" ry="-0.820783" rz="0" />
|
||||
</locations>
|
||||
@ -185,6 +209,9 @@
|
||||
<phrase>Look at that view!</phrase>
|
||||
<phrase>Oxidize!</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1300" x="283.986" y="261.208" z="-128.466" rw="0.70207" rx="0" ry="0.712108" rz="0" />
|
||||
</locations>
|
||||
@ -197,6 +224,9 @@
|
||||
<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>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1300" x="204.93" y="294.784" z="471.537" rw="0.85015" rx="0" ry="-0.52654" rz="0" />
|
||||
</locations>
|
||||
@ -213,6 +243,9 @@
|
||||
<phrase>There's treasure inside of all of us. It's called Imagination.</phrase>
|
||||
<phrase>Stromlings! Why did it have to be Stromlings?</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1300" x="-47.6296" y="322.527" z="533.5" rw="0.145135" rx="0" ry="0.989412" rz="0" />
|
||||
</locations>
|
||||
@ -225,13 +258,15 @@
|
||||
<phrase>Wait, did the game close yet?</phrase>
|
||||
<phrase>... What year is it?</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1400" x="-490.608" y="51.9449" z="-347.747" rw="0.594978" rx="0" ry="-0.803742" rz="0" />
|
||||
</locations>
|
||||
</object>
|
||||
<object name="Wicked" lot="7412">
|
||||
<equipment>8287, 2957, 7523, 16613</equipment>
|
||||
<phrases></phrases>
|
||||
<locations>
|
||||
<location zone="1400" x="264.506" y="339.931" z="54.1201" rw="0.715223" rx="0" ry="0.698896" rz="0" />
|
||||
</locations>
|
||||
@ -248,17 +283,22 @@
|
||||
<phrase>What do you mean my title is misspelled?</phrase>
|
||||
<phrase>When I was your age, numbers were illegal.</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1900" x="-493.724" y="1124.05" z="-76.6355" rw="0.1719" rx="0" ry="0.985114" rz="0" />
|
||||
</locations>
|
||||
</object>
|
||||
12651
|
||||
<object name="Matthew - ?????" lot="2279">
|
||||
<equipment>9856, 7793, 6928, 6927</equipment>
|
||||
<phrases>
|
||||
<phrase>I think I have a migraine from staring at this for so long</phrase>
|
||||
<phrase>Anything but Portabello</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1900" x="-227.621" y="1188.28" z="145.734" rw="-0.254353" rx="0" ry="0.967111" rz="0" />
|
||||
</locations>
|
||||
@ -268,6 +308,9 @@
|
||||
<phrases>
|
||||
<phrase>Per Aspera ad Astra</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1900" x="-190.673" y="1218.34" z="221.6" rw="0.972371" rx="0" ry="-0.233441" rz="0" />
|
||||
</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 fun!</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1800" x="-39.2259" y="96.8605" z="-550.077" rw="0.815145" rx="0" ry="-0.579257" rz="0" />
|
||||
</locations>
|
||||
@ -291,6 +337,9 @@
|
||||
<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>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="2000" x="-119.269" y="230.372" z="226.63" rw="0.381416" rx="0" ry="0.924404" rz="0" />
|
||||
</locations>
|
||||
@ -312,6 +361,9 @@
|
||||
<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>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1200" x="197.549" y="259.137" z="-587.759" rw="0.873694" rx="0" ry="0.486475" rz="0" />
|
||||
</locations>
|
||||
@ -335,13 +387,15 @@
|
||||
<phrase>Excuse me, do you know the way to Frostburgh?</phrase>
|
||||
<phrase>What are those Paradox scientists up to these days?</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1100" x="436.949" y="415.264" z="151.381" rw="0.528425" rx="0" ry="0.84898" rz="0" />
|
||||
</locations>
|
||||
</object>
|
||||
<object name="Ben" lot="2279">
|
||||
<equipment>8664, 4065, 2587</equipment>
|
||||
<phrases></phrases>
|
||||
<locations>
|
||||
<location zone="1200" x="284.538" y="260.627" z="-506.692" rw="0.819721" rx="0" ry="0.572763" rz="0" />
|
||||
</locations>
|
||||
@ -354,6 +408,9 @@
|
||||
<phrase>I think I may be lost...</phrase>
|
||||
<phrase>I'm feeling a bit emotionally conflicted right now</phrase>
|
||||
</phrases>
|
||||
<config>
|
||||
<key>custom_script_client=0:scripts\ai\SPEC\MISSION_MINIGAME_CLIENT.lua</key>
|
||||
</config>
|
||||
<locations>
|
||||
<location zone="1300" x="-171.217" y="246.482" z="-147.05" rw="-0.203118" rx="0" ry="0.979154" rz="0" />
|
||||
</locations>
|
||||
@ -371,6 +428,10 @@
|
||||
<phrase>I love cats, little meow meows</phrase>
|
||||
<phrase>It's not perfect, but it works!</phrase>
|
||||
</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>
|
||||
<location zone="1201" x="197.709" y="179.335" z="-8.05284" rw="0.544424" rx="0" ry="-0.83881" rz="0" />
|
||||
</locations>
|
||||
|
@ -1,4 +1,5 @@
|
||||
<files>
|
||||
<file name="dev-tribute.xml" enabled="1"/>
|
||||
<file name="atm.xml" enabled="0"/>
|
||||
<file name="demo.xml" enabled="0"/>
|
||||
</files>
|
||||
|
Loading…
Reference in New Issue
Block a user