Merge branch 'main' into use-npc-paths

This commit is contained in:
Aaron Kimbre
2022-12-12 13:22:36 -06:00
87 changed files with 1786 additions and 471 deletions

View File

@@ -264,14 +264,17 @@ void Character::DoQuickXMLDataParse() {
if (flags) {
auto* currentChild = flags->FirstChildElement();
while (currentChild) {
uint32_t index = 0;
uint64_t value = 0;
const auto* temp = currentChild->Attribute("v");
const auto* id = currentChild->Attribute("id");
if (temp && id) {
uint32_t index = 0;
uint64_t value = 0;
index = std::stoul(currentChild->Attribute("id"));
value = std::stoull(temp);
index = std::stoul(id);
value = std::stoull(temp);
m_PlayerFlags.insert(std::make_pair(index, value));
m_PlayerFlags.insert(std::make_pair(index, value));
}
currentChild = currentChild->NextSiblingElement();
}
}
@@ -351,6 +354,13 @@ void Character::SaveXMLToDatabase() {
flags->LinkEndChild(f);
}
// Prevents the news feed from showing up on world transfers
if (GetPlayerFlag(ePlayerFlags::IS_NEWS_SCREEN_VISIBLE)) {
auto* s = m_Doc->NewElement("s");
s->SetAttribute("si", ePlayerFlags::IS_NEWS_SCREEN_VISIBLE);
flags->LinkEndChild(s);
}
SaveXmlRespawnCheckpoints();
//Call upon the entity to update our xmlDoc:
@@ -361,6 +371,31 @@ void Character::SaveXMLToDatabase() {
m_OurEntity->UpdateXMLDoc(m_Doc);
WriteToDatabase();
//For metrics, log the time it took to save:
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed = end - start;
Game::logger->Log("Character", "Saved character to Database in: %fs", elapsed.count());
}
void Character::SetIsNewLogin() {
// If we dont have a flag element, then we cannot have a s element as a child of flag.
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag");
if (!flags) return;
auto* currentChild = flags->FirstChildElement();
while (currentChild) {
if (currentChild->Attribute("si")) {
flags->DeleteChild(currentChild);
Game::logger->Log("Character", "Removed isLoggedIn flag from character %i, saving character to database", GetID());
WriteToDatabase();
}
currentChild = currentChild->NextSiblingElement();
}
}
void Character::WriteToDatabase() {
//Dump our xml into m_XMLData:
auto* printer = new tinyxml2::XMLPrinter(0, true, 0);
m_Doc->Print(printer);
@@ -372,12 +407,6 @@ void Character::SaveXMLToDatabase() {
stmt->setUInt(2, m_ID);
stmt->execute();
delete stmt;
//For metrics, log the time it took to save:
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed = end - start;
Game::logger->Log("Character", "Saved character to Database in: %fs", elapsed.count());
delete printer;
}

View File

@@ -23,6 +23,10 @@ public:
Character(uint32_t id, User* parentUser);
~Character();
/**
* Write the current m_Doc to the database for saving.
*/
void WriteToDatabase();
void SaveXMLToDatabase();
void UpdateFromDatabase();
@@ -32,6 +36,15 @@ public:
const std::string& GetXMLData() const { return m_XMLData; }
tinyxml2::XMLDocument* GetXMLDoc() const { return m_Doc; }
/**
* Out of abundance of safety and clarity of what this saves, this is its own function.
*
* Clears the s element from the flag element and saves the xml to the database. Used to prevent the news
* feed from showing up on world transfers.
*
*/
void SetIsNewLogin();
/**
* Gets the database ID of the character
* @return the database ID of the character
@@ -427,7 +440,7 @@ public:
/**
* @brief Get the flying state
* @return value of the flying state
* @return value of the flying state
*/
bool GetIsFlying() { return m_IsFlying; }

View File

@@ -454,7 +454,7 @@ void Entity::Initialize() {
*/
CDScriptComponentTable* scriptCompTable = CDClientManager::Instance()->GetTable<CDScriptComponentTable>("ScriptComponent");
int scriptComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_SCRIPT);
int32_t scriptComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, COMPONENT_TYPE_SCRIPT, -1);
std::string scriptName = "";
bool client = false;
@@ -496,7 +496,7 @@ void Entity::Initialize() {
scriptName = customScriptServer;
}
if (!scriptName.empty() || client || m_Character) {
if (!scriptName.empty() || client || m_Character || scriptComponentID >= 0) {
m_Components.insert(std::make_pair(COMPONENT_TYPE_SCRIPT, new ScriptComponent(this, scriptName, true, client && scriptName.empty())));
}

View File

@@ -20,6 +20,7 @@
#include "Entity.h"
#include "EntityManager.h"
#include "SkillComponent.h"
#include "AssetManager.h"
UserManager* UserManager::m_Address = nullptr;
@@ -32,43 +33,59 @@ inline void StripCR(std::string& str) {
}
void UserManager::Initialize() {
std::string firstNamePath = "./res/names/minifigname_first.txt";
std::string middleNamePath = "./res/names/minifigname_middle.txt";
std::string lastNamePath = "./res/names/minifigname_last.txt";
std::string line;
std::fstream fnFile(firstNamePath, std::ios::in);
std::fstream mnFile(middleNamePath, std::ios::in);
std::fstream lnFile(lastNamePath, std::ios::in);
while (std::getline(fnFile, line, '\n')) {
AssetMemoryBuffer fnBuff = Game::assetManager->GetFileAsBuffer("names/minifigname_first.txt");
if (!fnBuff.m_Success) {
Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_first.txt").string().c_str());
throw std::runtime_error("Aborting initialization due to missing minifigure name file.");
}
std::istream fnStream = std::istream(&fnBuff);
while (std::getline(fnStream, line, '\n')) {
std::string name = line;
StripCR(name);
m_FirstNames.push_back(name);
}
fnBuff.close();
while (std::getline(mnFile, line, '\n')) {
AssetMemoryBuffer mnBuff = Game::assetManager->GetFileAsBuffer("names/minifigname_middle.txt");
if (!mnBuff.m_Success) {
Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_middle.txt").string().c_str());
throw std::runtime_error("Aborting initialization due to missing minifigure name file.");
}
std::istream mnStream = std::istream(&mnBuff);
while (std::getline(mnStream, line, '\n')) {
std::string name = line;
StripCR(name);
m_MiddleNames.push_back(name);
}
mnBuff.close();
while (std::getline(lnFile, line, '\n')) {
AssetMemoryBuffer lnBuff = Game::assetManager->GetFileAsBuffer("names/minifigname_last.txt");
if (!lnBuff.m_Success) {
Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_last.txt").string().c_str());
throw std::runtime_error("Aborting initialization due to missing minifigure name file.");
}
std::istream lnStream = std::istream(&lnBuff);
while (std::getline(lnStream, line, '\n')) {
std::string name = line;
StripCR(name);
m_LastNames.push_back(name);
}
fnFile.close();
mnFile.close();
lnFile.close();
lnBuff.close();
//Load our pre-approved names:
std::fstream chatList("./res/chatplus_en_us.txt", std::ios::in);
while (std::getline(chatList, line, '\n')) {
AssetMemoryBuffer chatListBuff = Game::assetManager->GetFileAsBuffer("chatplus_en_us.txt");
if (!chatListBuff.m_Success) {
Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "chatplus_en_us.txt").string().c_str());
throw std::runtime_error("Aborting initialization due to missing chat whitelist file.");
}
std::istream chatListStream = std::istream(&chatListBuff);
while (std::getline(chatListStream, line, '\n')) {
StripCR(line);
m_PreapprovedNames.push_back(line);
}
chatListBuff.close();
}
UserManager::~UserManager() {
@@ -210,6 +227,7 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
while (res->next()) {
LWOOBJID objID = res->getUInt64(1);
Character* character = new Character(uint32_t(objID), u);
character->SetIsNewLogin();
chars.push_back(character);
}
}

View File

@@ -42,6 +42,7 @@
#include "SkillCastFailedBehavior.h"
#include "SpawnBehavior.h"
#include "ForceMovementBehavior.h"
#include "RemoveBuffBehavior.h"
#include "ImmunityBehavior.h"
#include "InterruptBehavior.h"
#include "PlayEffectBehavior.h"
@@ -226,7 +227,9 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
break;
case BehaviorTemplates::BEHAVIOR_ALTER_CHAIN_DELAY: break;
case BehaviorTemplates::BEHAVIOR_CAMERA: break;
case BehaviorTemplates::BEHAVIOR_REMOVE_BUFF: break;
case BehaviorTemplates::BEHAVIOR_REMOVE_BUFF:
behavior = new RemoveBuffBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_GRAB: break;
case BehaviorTemplates::BEHAVIOR_MODULAR_BUILD: break;
case BehaviorTemplates::BEHAVIOR_NPC_COMBAT_SKILL:

View File

@@ -34,6 +34,7 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
"PlayEffectBehavior.cpp"
"ProjectileAttackBehavior.cpp"
"PullToPointBehavior.cpp"
"RemoveBuffBehavior.cpp"
"RepairBehavior.cpp"
"SkillCastFailedBehavior.cpp"
"SkillEventBehavior.cpp"

View File

@@ -75,5 +75,5 @@ void ForceMovementBehavior::SyncCalculation(BehaviorContext* context, RakNet::Bi
this->m_hitAction->Calculate(context, bitStream, branch);
this->m_hitEnemyAction->Calculate(context, bitStream, branch);
this->m_hitEnemyAction->Calculate(context, bitStream, branch);
this->m_hitFactionAction->Calculate(context, bitStream, branch);
}

View File

@@ -0,0 +1,21 @@
#include "RemoveBuffBehavior.h"
#include "BehaviorBranchContext.h"
#include "BehaviorContext.h"
#include "EntityManager.h"
#include "BuffComponent.h"
void RemoveBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
auto* entity = EntityManager::Instance()->GetEntity(context->caster);
if (!entity) return;
auto* buffComponent = entity->GetComponent<BuffComponent>();
if (!buffComponent) return;
buffComponent->RemoveBuff(m_BuffId, false, m_RemoveImmunity);
}
void RemoveBuffBehavior::Load() {
this->m_RemoveImmunity = GetBoolean("remove_immunity");
this->m_BuffId = GetInt("buff_id");
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "Behavior.h"
class RemoveBuffBehavior final : public Behavior
{
public:
/*
* Inherited
*/
explicit RemoveBuffBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {
}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
private:
bool m_RemoveImmunity;
uint32_t m_BuffId;
};

View File

@@ -123,13 +123,15 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
m_Buffs.emplace(id, buff);
}
void BuffComponent::RemoveBuff(int32_t id) {
void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity) {
const auto& iter = m_Buffs.find(id);
if (iter == m_Buffs.end()) {
return;
}
GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id);
m_Buffs.erase(iter);
RemoveBuffEffect(id);

View File

@@ -78,8 +78,9 @@ public:
/**
* Removes a buff from the parent entity, reversing its effects
* @param id the id of the buff to remove
* @param removeImmunity whether or not to remove immunity on removing the buff
*/
void RemoveBuff(int32_t id);
void RemoveBuff(int32_t id, bool fromUnEquip = false, bool removeImmunity = false);
/**
* Returns whether or not the entity has a buff identified by `id`

View File

@@ -209,9 +209,11 @@ void InventoryComponent::AddItem(
auto stack = static_cast<uint32_t>(info.stackSize);
bool isBrick = inventoryType == eInventoryType::BRICKS || (stack == 0 && info.itemType == 1);
// info.itemType of 1 is item type brick
if (inventoryType == eInventoryType::BRICKS || (stack == 0 && info.itemType == 1)) {
stack = 999;
if (isBrick) {
stack = UINT32_MAX;
} else if (stack == 0) {
stack = 1;
}
@@ -232,7 +234,8 @@ void InventoryComponent::AddItem(
}
}
while (left > 0) {
// If we have some leftover and we aren't bricks, make a new stack
while (left > 0 && (!isBrick || (isBrick && !existing))) {
const auto size = std::min(left, stack);
left -= size;
@@ -327,7 +330,9 @@ void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType in
const auto lot = item->GetLot();
if (item->GetConfig().empty() && !item->GetBound() || (item->GetBound() && item->GetInfo().isBOP)) {
const auto subkey = item->GetSubKey();
if (subkey == LWOOBJID_EMPTY && item->GetConfig().empty() && (!item->GetBound() || (item->GetBound() && item->GetInfo().isBOP))) {
auto left = std::min<uint32_t>(count, origin->GetLotCount(lot));
while (left > 0) {
@@ -358,7 +363,7 @@ void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType in
const auto delta = std::min<uint32_t>(item->GetCount(), count);
AddItem(lot, delta, eLootSourceType::LOOT_SOURCE_NONE, inventory, config, LWOOBJID_EMPTY, showFlyingLot, isModMoveAndEquip, LWOOBJID_EMPTY, origin->GetType(), 0, item->GetBound(), preferredSlot);
AddItem(lot, delta, eLootSourceType::LOOT_SOURCE_NONE, inventory, config, LWOOBJID_EMPTY, showFlyingLot, isModMoveAndEquip, subkey, origin->GetType(), 0, item->GetBound(), preferredSlot);
item->SetCount(item->GetCount() - delta, false, false);
}
@@ -605,16 +610,17 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
return;
}
std::vector<Inventory*> inventories;
std::vector<Inventory*> inventoriesToSave;
// Need to prevent some transfer inventories from being saved
for (const auto& pair : this->m_Inventories) {
auto* inventory = pair.second;
if (inventory->GetType() == VENDOR_BUYBACK) {
if (inventory->GetType() == VENDOR_BUYBACK || inventory->GetType() == eInventoryType::MODELS_IN_BBB) {
continue;
}
inventories.push_back(inventory);
inventoriesToSave.push_back(inventory);
}
inventoryElement->SetAttribute("csl", m_Consumable);
@@ -629,7 +635,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
bags->DeleteChildren();
for (const auto* inventory : inventories) {
for (const auto* inventory : inventoriesToSave) {
auto* bag = document->NewElement("b");
bag->SetAttribute("t", inventory->GetType());
@@ -648,7 +654,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
items->DeleteChildren();
for (auto* inventory : inventories) {
for (auto* inventory : inventoriesToSave) {
if (inventory->GetSize() == 0) {
continue;
}
@@ -985,6 +991,7 @@ void InventoryComponent::ApplyBuff(Item* item) const {
}
}
// TODO Something needs to send the remove buff GameMessage as well when it is unequipping items that would remove buffs.
void InventoryComponent::RemoveBuff(Item* item) const {
const auto buffs = FindBuffs(item, false);
@@ -1258,7 +1265,7 @@ BehaviorSlot InventoryComponent::FindBehaviorSlot(const eItemType type) {
}
bool InventoryComponent::IsTransferInventory(eInventoryType type) {
return type == VENDOR_BUYBACK || type == VAULT_ITEMS || type == VAULT_MODELS || type == TEMP_ITEMS || type == TEMP_MODELS;
return type == VENDOR_BUYBACK || type == VAULT_ITEMS || type == VAULT_MODELS || type == TEMP_ITEMS || type == TEMP_MODELS || type == MODELS_IN_BBB;
}
uint32_t InventoryComponent::FindSkill(const LOT lot) {

View File

@@ -474,7 +474,7 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet
settings.push_back(propertyObjectID);
settings.push_back(modelType);
inventoryComponent->AddItem(6662, 1, eLootSourceType::LOOT_SOURCE_DELETION, eInventoryType::HIDDEN, settings, LWOOBJID_EMPTY, false, false, spawnerId);
inventoryComponent->AddItem(6662, 1, eLootSourceType::LOOT_SOURCE_DELETION, eInventoryType::MODELS_IN_BBB, settings, LWOOBJID_EMPTY, false, false, spawnerId);
auto* item = inventoryComponent->FindItemBySubKey(spawnerId);
if (item == nullptr) {

View File

@@ -19,8 +19,9 @@
#include "DestroyableComponent.h"
ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int activityID) : Component(parent) {
m_ActivityID = activityID;
CDActivitiesTable* activitiesTable = CDClientManager::Instance()->GetTable<CDActivitiesTable>("Activities");
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == activityID); });
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
for (CDActivities activity : activities) {
m_ActivityInfo = activity;
@@ -88,6 +89,21 @@ void ScriptedActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool
}
}
void ScriptedActivityComponent::ReloadConfig() {
CDActivitiesTable* activitiesTable = CDClientManager::Instance()->GetTable<CDActivitiesTable>("Activities");
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
for (auto activity : activities) {
auto mapID = m_ActivityInfo.instanceMapID;
if ((mapID == 1203 || mapID == 1261 || mapID == 1303 || mapID == 1403) && Game::config->GetValue("solo_racing") == "1") {
m_ActivityInfo.minTeamSize = 1;
m_ActivityInfo.minTeams = 1;
} else {
m_ActivityInfo.minTeamSize = activity.minTeamSize;
m_ActivityInfo.minTeams = activity.minTeams;
}
}
}
void ScriptedActivityComponent::HandleMessageBoxResponse(Entity* player, const std::string& id) {
if (m_ActivityInfo.ActivityID == 103) {
return;

View File

@@ -276,6 +276,12 @@ public:
*/
ActivityInstance* GetInstance(const LWOOBJID playerID);
/**
* @brief Reloads the config settings for this component
*
*/
void ReloadConfig();
/**
* Removes all the instances
*/
@@ -361,6 +367,12 @@ private:
* LMIs for team sizes
*/
std::unordered_map<uint32_t, uint32_t> m_ActivityLootMatrices;
/**
* The activity id
*
*/
int32_t m_ActivityID;
};
#endif // SCRIPTEDACTIVITYCOMPONENT_H

View File

@@ -46,6 +46,10 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
switch (messageID) {
case GAME_MSG_UN_USE_BBB_MODEL: {
GameMessages::HandleUnUseModel(inStream, entity, sysAddr);
break;
}
case GAME_MSG_PLAY_EMOTE: {
GameMessages::HandlePlayEmote(inStream, entity);
break;

View File

@@ -70,6 +70,7 @@
#include "TradingManager.h"
#include "ControlBehaviors.h"
#include "AMFDeserialize.h"
#include "eBlueprintSaveResponseType.h"
void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender) {
CBITSTREAM;
@@ -2139,6 +2140,32 @@ void GameMessages::HandleSetPropertyAccess(RakNet::BitStream* inStream, Entity*
PropertyManagementComponent::Instance()->SetPrivacyOption(static_cast<PropertyPrivacyOption>(accessType));
}
void GameMessages::HandleUnUseModel(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
bool unknown{};
LWOOBJID objIdToAddToInventory{};
inStream->Read(unknown);
inStream->Read(objIdToAddToInventory);
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
if (inventoryComponent) {
auto* inventory = inventoryComponent->GetInventory(eInventoryType::MODELS_IN_BBB);
auto* item = inventory->FindItemById(objIdToAddToInventory);
if (item) {
inventoryComponent->MoveItemToInventory(item, eInventoryType::MODELS, 1);
} else {
Game::logger->Log("GameMessages", "item id %llu not found in MODELS_IN_BBB inventory, likely because it does not exist", objIdToAddToInventory);
}
}
if (unknown) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_BLUEPRINT_SAVE_RESPONSE);
bitStream.Write<LWOOBJID>(LWOOBJID_EMPTY); //always zero so that a check on the client passes
bitStream.Write(eBlueprintSaveResponseType::PlacementFailed); // Sending a non-zero error code here prevents the client from deleting its in progress build for some reason?
bitStream.Write<uint32_t>(0);
SEND_PACKET;
}
}
void GameMessages::HandleUpdatePropertyOrModelForFilterCheck(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
bool isProperty{};
LWOOBJID objectId{};
@@ -2353,16 +2380,40 @@ void GameMessages::HandleDeletePropertyModel(RakNet::BitStream* inStream, Entity
}
void GameMessages::HandleBBBLoadItemRequest(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
LWOOBJID itemID = LWOOBJID_EMPTY;
inStream->Read(itemID);
LWOOBJID previousItemID = LWOOBJID_EMPTY;
inStream->Read(previousItemID);
Game::logger->Log("BBB", "Load item request for: %lld", itemID);
Game::logger->Log("BBB", "Load item request for: %lld", previousItemID);
LWOOBJID newId = previousItemID;
auto* inventoryComponent = entity->GetComponent<InventoryComponent>();
if (inventoryComponent) {
auto* inventory = inventoryComponent->GetInventory(eInventoryType::MODELS);
auto* itemToMove = inventory->FindItemById(previousItemID);
if (itemToMove) {
LOT previousLot = itemToMove->GetLot();
inventoryComponent->MoveItemToInventory(itemToMove, eInventoryType::MODELS_IN_BBB, 1, false);
auto* destinationInventory = inventoryComponent->GetInventory(eInventoryType::MODELS_IN_BBB);
if (destinationInventory) {
auto* movedItem = destinationInventory->FindItemByLot(previousLot);
if (movedItem) newId = movedItem->GetId();
}
} else {
Game::logger->Log("GameMessages", "item id %llu not found in MODELS inventory, likely because it does not exist", previousItemID);
}
}
// Second argument always true (successful) for now
SendBlueprintLoadItemResponse(sysAddr, true, previousItemID, newId);
}
void GameMessages::SendBlueprintLoadItemResponse(const SystemAddress& sysAddr, bool success, LWOOBJID oldItemId, LWOOBJID newItemId) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, CLIENT, MSG_CLIENT_BLUEPRINT_LOAD_RESPONSE_ITEMID);
bitStream.Write(static_cast<uint8_t>(1));
bitStream.Write<LWOOBJID>(itemID);
bitStream.Write<LWOOBJID>(itemID);
bitStream.Write(static_cast<uint8_t>(success));
bitStream.Write<LWOOBJID>(oldItemId);
bitStream.Write<LWOOBJID>(newItemId);
SEND_PACKET;
}
@@ -2413,7 +2464,7 @@ void GameMessages::HandleControlBehaviors(RakNet::BitStream* inStream, Entity* e
}
auto owner = PropertyManagementComponent::Instance()->GetOwner();
if (!owner) return;
if (!owner) return;
ControlBehaviors::ProcessCommand(entity, sysAddr, static_cast<AMFArrayValue*>(amfArguments.get()), command, owner);
}
@@ -2609,8 +2660,8 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, CLIENT, CLIENT::MSG_CLIENT_BLUEPRINT_SAVE_RESPONSE);
bitStream.Write(localId);
bitStream.Write<unsigned int>(0);
bitStream.Write<unsigned int>(1);
bitStream.Write(eBlueprintSaveResponseType::EverythingWorked);
bitStream.Write<uint32_t>(1);
bitStream.Write(blueprintID);
bitStream.Write<uint32_t>(sd0Size);
@@ -2620,7 +2671,6 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent
}
SEND_PACKET;
// PacketUtils::SavePacket("MSG_CLIENT_BLUEPRINT_SAVE_RESPONSE.bin", (char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed());
//Now we have to construct this object:
@@ -3476,6 +3526,20 @@ void GameMessages::SendPlayEmote(LWOOBJID objectId, int32_t emoteID, LWOOBJID ta
SEND_PACKET;
}
void GameMessages::SendRemoveBuff(Entity* entity, bool fromUnEquip, bool removeImmunity, uint32_t buffId) {
CBITSTREAM;
CMSGHEADER;
bitStream.Write(entity->GetObjectID());
bitStream.Write(GAME_MSG::GAME_MSG_REMOVE_BUFF);
bitStream.Write(false); // bFromRemoveBehavior but setting this to true makes the GM not do anything on the client?
bitStream.Write(fromUnEquip);
bitStream.Write(removeImmunity);
bitStream.Write(buffId);
SEND_PACKET_BROADCAST;
}
void GameMessages::SendBouncerActiveStatus(LWOOBJID objectId, bool bActive, const SystemAddress& sysAddr) {
CBITSTREAM;
@@ -4398,13 +4462,13 @@ void GameMessages::SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uin
// NT
void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) {
bool bAllowPartial;
bool bAllowPartial{};
int32_t destSlot = -1;
int32_t iStackCount = 1;
eInventoryType invTypeDst = ITEMS;
eInventoryType invTypeSrc = ITEMS;
LWOOBJID itemID = LWOOBJID_EMPTY;
bool showFlyingLoot;
bool showFlyingLoot{};
LWOOBJID subkey = LWOOBJID_EMPTY;
LOT itemLOT = 0;
@@ -4428,12 +4492,12 @@ void GameMessages::HandleRequestMoveItemBetweenInventoryTypes(RakNet::BitStream*
if (itemID != LWOOBJID_EMPTY) {
auto* item = inventoryComponent->FindItemById(itemID);
if (item == nullptr) {
return;
}
if (!item) return;
if (inventoryComponent->IsPet(item->GetSubKey()) || !item->GetConfig().empty()) {
return;
// Despawn the pet if we are moving that pet to the vault.
auto* petComponent = PetComponent::GetActivePet(entity->GetObjectID());
if (petComponent && petComponent->GetDatabaseId() == item->GetSubKey()) {
inventoryComponent->DespawnPet();
}
inventoryComponent->MoveItemToInventory(item, invTypeDst, iStackCount, showFlyingLoot, false, false, destSlot);
@@ -4894,27 +4958,27 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity)
inStream->Read(emoteID);
inStream->Read(targetID);
Game::logger->Log("GameMessages", "Emote (%i) (%llu)", emoteID, targetID);
Game::logger->LogDebug("GameMessages", "Emote (%i) (%llu)", emoteID, targetID);
//TODO: If targetID != 0, and we have one of the "perform emote" missions, complete them.
if (emoteID == 0) return;
std::string sAnimationName = "deaded"; //Default name in case we fail to get the emote
MissionComponent* mission = static_cast<MissionComponent*>(entity->GetComponent(COMPONENT_TYPE_MISSION));
if (mission) {
mission->Progress(MissionTaskType::MISSION_TASK_TYPE_EMOTE, emoteID, targetID);
}
MissionComponent* missionComponent = entity->GetComponent<MissionComponent>();
if (!missionComponent) return;
if (targetID != LWOOBJID_EMPTY) {
auto* targetEntity = EntityManager::Instance()->GetEntity(targetID);
Game::logger->Log("GameMessages", "Emote target found (%d)", targetEntity != nullptr);
Game::logger->LogDebug("GameMessages", "Emote target found (%d)", targetEntity != nullptr);
if (targetEntity != nullptr) {
targetEntity->OnEmoteReceived(emoteID, entity);
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_EMOTE, emoteID, targetID);
}
} else {
Game::logger->LogDebug("GameMessages", "Target ID is empty, using backup");
const auto scriptedEntities = EntityManager::Instance()->GetEntitiesByComponent(COMPONENT_TYPE_SCRIPT);
const auto& referencePoint = entity->GetPosition();
@@ -4923,6 +4987,7 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity)
if (Vector3::DistanceSquared(scripted->GetPosition(), referencePoint) > 5.0f * 5.0f) continue;
scripted->OnEmoteReceived(emoteID, entity);
missionComponent->Progress(MissionTaskType::MISSION_TASK_TYPE_EMOTE, emoteID, scripted->GetObjectID());
}
}

View File

@@ -121,6 +121,7 @@ namespace GameMessages {
void SendMatchResponse(Entity* entity, const SystemAddress& sysAddr, int response);
void SendMatchUpdate(Entity* entity, const SystemAddress& sysAddr, std::string data, int type);
void HandleUnUseModel(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr);
void SendStartCelebrationEffect(Entity* entity, const SystemAddress& sysAddr, int celebrationID);
/**
@@ -197,6 +198,16 @@ namespace GameMessages {
void SendDownloadPropertyData(LWOOBJID objectId, const PropertyDataMessage& data, const SystemAddress& sysAddr);
/**
* @brief Send an updated item id to the client when they load a blueprint in brick build mode
*
* @param sysAddr SystemAddress to respond to
* @param oldItemId The item ID that was requested to be loaded
* @param newItemId The new item ID of the loaded item
*
*/
void SendBlueprintLoadItemResponse(const SystemAddress& sysAddr, bool success, LWOOBJID oldItemId, LWOOBJID newItemId);
void SendPropertyRentalResponse(LWOOBJID objectId, LWOCLONEID cloneId, uint32_t code, LWOOBJID propertyId, int64_t rentDue, const SystemAddress& sysAddr);
void SendLockNodeRotation(Entity* entity, std::string nodeName);
@@ -566,6 +577,8 @@ namespace GameMessages {
void HandleReportBug(RakNet::BitStream* inStream, Entity* entity);
void SendRemoveBuff(Entity* entity, bool fromUnEquip, bool removeImmunity, uint32_t buffId);
/* Message to synchronize a skill cast */
class EchoSyncSkill {
static const GAME_MSG MsgID = GAME_MSG_ECHO_SYNC_SKILL;

View File

@@ -95,7 +95,7 @@ public:
* @param lot the lot to find items for
* @param ignoreEquipped ignores equipped items
* @param ignoreBound ignores bound items
* @return item in the inventory for the provided LOT
* @return item with the lowest stack count in the inventory for the provided LOT
*/
Item* FindItemByLot(LOT lot, bool ignoreEquipped = false, bool ignoreBound = false) const;

View File

@@ -253,7 +253,7 @@ bool Item::Consume() {
}
}
Game::logger->Log("Item", "Consumed (%i) / (%llu) with (%d)", lot, id, success);
Game::logger->LogDebug("Item", "Consumed LOT (%i) itemID (%llu). Success=(%d)", lot, id, success);
GameMessages::SendUseItemResult(inventory->GetComponent()->GetParent(), lot, success);
@@ -265,14 +265,34 @@ bool Item::Consume() {
}
void Item::UseNonEquip() {
LOT thisLot = this->GetLot();
if (!GetInventory()) {
Game::logger->LogDebug("Item", "item %i has no inventory??", this->GetLot());
return;
}
auto* playerInventoryComponent = GetInventory()->GetComponent();
if (!playerInventoryComponent) {
Game::logger->LogDebug("Item", "no inventory component attached to item id %llu lot %i", this->GetId(), this->GetLot());
return;
}
auto* playerEntity = playerInventoryComponent->GetParent();
if (!playerEntity) {
Game::logger->LogDebug("Item", "no player entity attached to inventory? item id is %llu", this->GetId());
return;
}
const auto type = static_cast<eItemType>(info->itemType);
if (type == eItemType::ITEM_TYPE_MOUNT) {
GetInventory()->GetComponent()->HandlePossession(this);
playerInventoryComponent->HandlePossession(this);
// TODO Check if mounts are allowed to be spawned
} else if (type == eItemType::ITEM_TYPE_PET_INVENTORY_ITEM && subKey != LWOOBJID_EMPTY) {
const auto& databasePet = GetInventory()->GetComponent()->GetDatabasePet(subKey);
const auto& databasePet = playerInventoryComponent->GetDatabasePet(subKey);
if (databasePet.lot != LOT_NULL) {
GetInventory()->GetComponent()->SpawnPet(this);
playerInventoryComponent->SpawnPet(this);
}
// This precondition response is taken care of in SpawnPet().
} else {
auto* compRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry");
const auto packageComponentId = compRegistryTable->GetByIDAndType(lot, COMPONENT_TYPE_PACKAGE);
@@ -282,18 +302,41 @@ void Item::UseNonEquip() {
auto* packCompTable = CDClientManager::Instance()->GetTable<CDPackageComponentTable>("PackageComponent");
auto packages = packCompTable->Query([=](const CDPackageComponent entry) {return entry.id == static_cast<uint32_t>(packageComponentId); });
const auto success = !packages.empty();
auto success = !packages.empty();
if (success) {
auto* entityParent = inventory->GetComponent()->GetParent();
for (auto& pack : packages) {
std::unordered_map<LOT, int32_t> result{};
result = LootGenerator::Instance().RollLootMatrix(entityParent, pack.LootMatrixIndex);
if (!inventory->GetComponent()->HasSpaceForLoot(result)) {
if (this->GetPreconditionExpression()->Check(playerInventoryComponent->GetParent())) {
auto* entityParent = playerInventoryComponent->GetParent();
// Roll the loot for all the packages then see if it all fits. If it fits, give it to the player, otherwise don't.
std::unordered_map<LOT, int32_t> rolledLoot{};
for (auto& pack : packages) {
auto thisPackage = LootGenerator::Instance().RollLootMatrix(entityParent, pack.LootMatrixIndex);
for (auto& loot : thisPackage) {
// If we already rolled this lot, add it to the existing one, otherwise create a new entry.
auto existingLoot = rolledLoot.find(loot.first);
if (existingLoot == rolledLoot.end()) {
rolledLoot.insert(loot);
} else {
existingLoot->second += loot.second;
}
}
}
LootGenerator::Instance().GiveLoot(inventory->GetComponent()->GetParent(), result, eLootSourceType::LOOT_SOURCE_CONSUMPTION);
if (playerInventoryComponent->HasSpaceForLoot(rolledLoot)) {
LootGenerator::Instance().GiveLoot(playerInventoryComponent->GetParent(), rolledLoot, eLootSourceType::LOOT_SOURCE_CONSUMPTION);
playerInventoryComponent->RemoveItem(lot, 1);
} else {
success = false;
}
} else {
GameMessages::SendUseItemRequirementsResponse(
playerInventoryComponent->GetParent()->GetObjectID(),
playerInventoryComponent->GetParent()->GetSystemAddress(),
UseItemResponse::FailedPrecondition
);
success = false;
}
inventory->GetComponent()->RemoveItem(lot, 1);
}
Game::logger->LogDebug("Item", "Player %llu %s used item %i", playerEntity->GetObjectID(), success ? "successfully" : "unsuccessfully", thisLot);
GameMessages::SendUseItemResult(playerInventoryComponent->GetParent(), thisLot, success);
}
}

View File

@@ -1,105 +0,0 @@
#pragma once
/**
2 Engineer (Rank 1) Item Set
3 Engineer (Rank 2) Item Set
4 Engineer (Rank 3) Item Set
7 Knight (Rank 1) Item Set
8 Knight (Rank 2) Item Set
9 Knight (Rank 3) Item Set
10 Space Ranger (Rank 1) Item Set
11 Space Ranger (Rank 2) Item Set
12 Space Ranger (Rank 3) Item Set
13 Samurai (Rank 1) Item Set
14 Samurai (Rank 2) Item Set
15 Samurai (Rank 3) Item Set
16 Sorcerer (Rank 1) Item Set
17 Sorcerer (Rank 2) Item Set
18 Sorcerer (Rank 3) Item Set
19 Space Marauder (Rank 1) Item Set
20 Space Marauder (Rank 2) Item Set
21 Space Marauder (Rank 3) Item Set
22 Shinobi (Rank 1) Item Set
23 Shinobi (Rank 2) Item Set
24 Shinobi (Rank 3) Item Set
25 Inventor (Rank 1) Item Set
26 Inventor (Rank 2) Item Set
27 Inventor (Rank 3) Item Set
28 Summoner (Rank 1) Item Set
29 Summoner (Rank 2) Item Set
30 Summoner (Rank 3) Item Set
31 Adventurer (Rank 1) Item Set
32 Adventurer (Rank 2) Item Set
33 Adventurer (Rank 3) Item Set
34 Daredevil (Rank 1) Item Set
35 Daredevil (Rank 2) Item Set
36 Daredevil (Rank 3) Item Set
37 Buccaneer (Rank 1) Item Set
38 Buccaneer (Rank 2) Item Set
39 Buccaneer (Rank 3) Item Set
40 Bone Suit Item Set
41 Imagination Spinjitzu Item Set
42 Bat Lord Item Set
43 Mosaic Jester Item Set
44 Explorien Bot Item Set
45 [Unnamed] Item Set
46 [Unnamed] Item Set
47 [Unnamed] Item Set
48 Earth Spinjitzu Item Set
49 [Unnamed] Item Set
50 Fire Spinjitzu Item Set
51 Ice Spinjitzu Item Set
52 Lightning Spinjitzu Item Set
*/
enum class ItemSetPassiveAbilityID
{
EngineerRank1 = 2,
EngineerRank2 = 3,
EngineerRank3 = 4,
KnightRank1 = 7,
KnightRank2 = 8,
KnightRank3 = 9,
SpaceRangerRank1 = 10,
SpaceRangerRank2 = 11,
SpaceRangerRank3 = 12,
SamuraiRank1 = 13,
SamuraiRank2 = 14,
SamuraiRank3 = 15,
SorcererRank1 = 16,
SorcererRank2 = 17,
SorcererRank3 = 18,
SpaceMarauderRank1 = 19,
SpaceMarauderRank2 = 20,
SpaceMarauderRank3 = 21,
ShinobiRank1 = 22,
ShinobiRank2 = 23,
ShinobiRank3 = 24,
InventorRank1 = 25,
InventorRank2 = 26,
InventorRank3 = 27,
SummonerRank1 = 28,
SummonerRank2 = 29,
SummonerRank3 = 30,
AdventurerRank1 = 31,
AdventurerRank2 = 32,
AdventurerRank3 = 33,
DaredevilRank1 = 34,
DaredevilRank2 = 35,
DaredevilRank3 = 36,
BuccaneerRank1 = 37,
BuccaneerRank2 = 38,
BuccaneerRank3 = 39,
BoneSuit = 40,
ImaginationSpinjitzu = 41,
BatLord = 42,
MosaicJester = 43,
ExplorienBot = 44,
Unnamed1 = 45,
Unnamed2 = 46,
Unnamed3 = 47,
EarthSpinjitzu = 48,
Unnamed4 = 49,
FireSpinjitzu = 50,
IceSpinjitzu = 51,
LightningSpinjitzu = 52
};

View File

@@ -13,7 +13,6 @@
#include "Mail.h"
#include "MissionComponent.h"
#include "RacingTaskParam.h"
#include "dLocale.h"
#include "dLogger.h"
#include "dServer.h"
#include "dZoneManager.h"
@@ -335,13 +334,10 @@ void Mission::Complete(const bool yieldRewards) {
for (const auto& email : missionEmails) {
const auto missionEmailBase = "MissionEmail_" + std::to_string(email.ID) + "_";
const auto senderLocale = missionEmailBase + "senderName";
const auto announceLocale = missionEmailBase + "announceText";
if (email.messageType == 1 && Game::locale->HasPhrase(senderLocale)) {
const auto subject = dLocale::GetTemplate(missionEmailBase + "subjectText");
const auto body = dLocale::GetTemplate(missionEmailBase + "bodyText");
const auto sender = dLocale::GetTemplate(senderLocale);
if (email.messageType == 1) {
const auto subject = "%[" + missionEmailBase + "subjectText]";
const auto body = "%[" + missionEmailBase + "bodyText]";
const auto sender = "%[" + missionEmailBase + "senderName]";
Mail::SendMail(LWOOBJID_EMPTY, sender, GetAssociate(), subject, body, email.attachmentLOT, 1);
}

View File

@@ -1,13 +0,0 @@
#pragma once
#ifndef MISSIONLOCKSTATE_H
#define MISSIONLOCKSTATE_H
enum class MissionLockState : int
{
MISSION_LOCK_LOCKED,
MISSION_LOCK_NEW,
MISSION_LOCK_UNLOCKED,
};
#endif

View File

@@ -1,56 +0,0 @@
#pragma once
#ifndef __MISSIONSTATE__H__
#define __MISSIONSTATE__H__
/**
* Represents the possible states a mission can be in
*/
enum class MissionState : int {
/**
* The mission state is unknown
*/
MISSION_STATE_UNKNOWN = -1,
/**
* The mission is yielding rewards
*/
MISSION_STATE_REWARDING = 0,
/**
* The mission can be accepted
*/
MISSION_STATE_AVAILABLE = 1,
/**
* The mission has been accepted but not yet completed
*/
MISSION_STATE_ACTIVE = 2,
/**
* All the tasks for the mission have been completed and the entity can turn the mission in to complete it
*/
MISSION_STATE_READY_TO_COMPLETE = 4, //!< The mission is ready to complete
/**
* The mission has been completed
*/
MISSION_STATE_COMPLETE = 8,
/**
* The mission is available again and has been completed before. Used for daily missions.
*/
MISSION_STATE_COMPLETE_AVAILABLE = 9,
/**
* The mission is active and has been completed before. Used for daily missions.
*/
MISSION_STATE_COMPLETE_ACTIVE = 10,
/**
* The mission has been completed before and has now been completed again. Used for daily missions.
*/
MISSION_STATE_COMPLETE_READY_TO_COMPLETE = 12
};
#endif //!__MISSIONSTATE__H__

View File

@@ -1,31 +0,0 @@
#pragma once
#ifndef MISSIONTASKTYPE_H
#define MISSIONTASKTYPE_H
//! An enum for mission task types
enum class MissionTaskType : int {
MISSION_TASK_TYPE_UNKNOWN = -1, //!< The task type is unknown
MISSION_TASK_TYPE_SMASH = 0, //!< A task for smashing something
MISSION_TASK_TYPE_SCRIPT = 1, //!< A task handled by a server LUA script
MISSION_TASK_TYPE_ACTIVITY = 2, //!< A task for completing a quickbuild
MISSION_TASK_TYPE_ENVIRONMENT = 3, //!< A task for something in the environment
MISSION_TASK_TYPE_MISSION_INTERACTION = 4, //!< A task for interacting with a mission
MISSION_TASK_TYPE_EMOTE = 5, //!< A task for playing an emote
MISSION_TASK_TYPE_FOOD = 9, //!< A task for eating food
MISSION_TASK_TYPE_SKILL = 10, //!< A task for performing a skill
MISSION_TASK_TYPE_ITEM_COLLECTION = 11, //!< A task for collecting an item
MISSION_TASK_TYPE_LOCATION = 12, //!< A task for finding a location
MISSION_TASK_TYPE_MINIGAME = 14, //!< A task for doing something in a minigame
MISSION_TASK_TYPE_NON_MISSION_INTERACTION = 15, //!< A task for interacting with a non-mission
MISSION_TASK_TYPE_MISSION_COMPLETE = 16, //!< A task for completing a mission
MISSION_TASK_TYPE_EARN_REPUTATION = 17, //!< A task for earning reputation
MISSION_TASK_TYPE_POWERUP = 21, //!< A task for collecting a powerup
MISSION_TASK_TYPE_PET_TAMING = 22, //!< A task for taming a pet
MISSION_TASK_TYPE_RACING = 23, //!< A task for racing
MISSION_TASK_TYPE_PLAYER_FLAG = 24, //!< A task for setting a player flag
MISSION_TASK_TYPE_PLACE_MODEL = 25, //!< A task for picking up a model
MISSION_TASK_TYPE_VISIT_PROPERTY = 30 //!< A task for visiting a property
};
#endif

View File

@@ -1,20 +0,0 @@
#pragma once
#include <cstdint>
enum class RacingTaskParam : int32_t {
RACING_TASK_PARAM_FINISH_WITH_PLACEMENT = 1, //<! A task param for finishing with a specific placement.
RACING_TASK_PARAM_LAP_TIME = 2, //<! A task param for finishing with a specific lap time.
RACING_TASK_PARAM_TOTAL_TRACK_TIME = 3, //<! A task param for finishing with a specific track time.
RACING_TASK_PARAM_COMPLETE_ANY_RACING_TASK = 4, //<! A task param for completing a racing task.
RACING_TASK_PARAM_COMPLETE_TRACK_TASKS = 5, //<! A task param for completing a task for a specific track.
RACING_TASK_PARAM_MODULAR_BUILDING = 6, //<! A task param for modular building with racing builds.
RACING_TASK_PARAM_SAFE_DRIVER = 10, //<! A task param for completing a race without smashing.
RACING_TASK_PARAM_SMASHABLES = 11, //<! A task param for smashing entities during a race.
RACING_TASK_PARAM_COLLECT_IMAGINATION = 12, //<! A task param for collecting imagination during a race.
RACING_TASK_PARAM_COMPETED_IN_RACE = 13, //<! A task param for competing in a race.
RACING_TASK_PARAM_WIN_RACE_IN_WORLD = 14, //<! A task param for winning a race in a specific world.
RACING_TASK_PARAM_FIRST_PLACE_MULTIPLE_TRACKS = 15, //<! A task param for finishing in first place on multiple tracks.
RACING_TASK_PARAM_LAST_PLACE_FINISH = 16, //<! A task param for finishing in last place.
RACING_TASK_PARAM_SMASH_SPECIFIC_SMASHABLE = 17 //<! A task param for smashing dragon eggs during a race.
};

View File

@@ -0,0 +1,20 @@
#pragma once
#ifndef __BEHAVIORSTATES__H__
#define __BEHAVIORSTATES__H__
#include <string>
#include <cstdint>
#include "dCommonVars.h"
enum States : BEHAVIORSTATE {
HOME_STATE = 0, //!< The HOME behavior state
CIRCLE_STATE, //!< The CIRCLE behavior state
SQUARE_STATE, //!< The SQUARE behavior state
DIAMOND_STATE, //!< The DIAMOND behavior state
TRIANGLE_STATE, //!< The TRIANGLE behavior state
STAR_STATE //!< The STAR behavior state
};
#endif //!__BEHAVIORSTATES__H__

View File

@@ -5,50 +5,67 @@
#include "Game.h"
#include "GameMessages.h"
#include "ModelComponent.h"
#include "../../dWorldServer/ObjectIDManager.h"
#include "dLogger.h"
void ControlBehaviors::ProcessCommand(Entity* modelEntity, const SystemAddress& sysAddr, AMFArrayValue* arguments, std::string command, Entity* modelOwner) {
if (!modelEntity || !modelOwner || !arguments) return;
uint32_t GetBehaviorIDFromArgument(AMFArrayValue* arguments, const std::string& key = "BehaviorID") {
auto* behaviorIDValue = arguments->FindValue<AMFStringValue>(key);
uint32_t behaviorID = -1;
if (command == "sendBehaviorListToClient")
SendBehaviorListToClient(modelEntity, sysAddr, modelOwner);
else if (command == "modelTypeChanged")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "toggleExecutionUpdates")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "addStrip")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "removeStrip")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "mergeStrips")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "splitStrip")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "updateStripUI")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "addAction")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "migrateActions")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "rearrangeStrip")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "add")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "removeActions")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "rename")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "sendBehaviorBlocksToClient")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "moveToInventory")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else if (command == "updateAction")
Game::logger->Log("ControlBehaviors", "Got command %s but is not implemented!", command.c_str());
else
Game::logger->Log("ControlBehaviors", "Unknown behavior command (%s)\n", command.c_str());
if (behaviorIDValue) {
behaviorID = std::stoul(behaviorIDValue->GetStringValue());
} else if (arguments->FindValue<AMFUndefinedValue>(key) == nullptr){
throw std::invalid_argument("Unable to find behavior ID from argument \"" + key + "\"");
}
return behaviorID;
}
void ControlBehaviors::SendBehaviorListToClient(
BEHAVIORSTATE GetBehaviorStateFromArgument(AMFArrayValue* arguments, const std::string& key = "stateID") {
auto* stateIDValue = arguments->FindValue<AMFDoubleValue>(key);
if (!stateIDValue) throw std::invalid_argument("Unable to find behavior state from argument \"" + key + "\"");
BEHAVIORSTATE stateID = static_cast<BEHAVIORSTATE>(stateIDValue->GetDoubleValue());
return stateID;
}
STRIPID GetStripIDFromArgument(AMFArrayValue* arguments, const std::string& key = "stripID") {
auto* stripIDValue = arguments->FindValue<AMFDoubleValue>(key);
if (!stripIDValue) throw std::invalid_argument("Unable to find strip ID from argument \"" + key + "\"");
STRIPID stripID = static_cast<STRIPID>(stripIDValue->GetDoubleValue());
return stripID;
}
void RequestUpdatedID(int32_t behaviorID, ModelComponent* modelComponent, Entity* modelOwner, const SystemAddress& sysAddr) {
// auto behavior = modelComponent->FindBehavior(behaviorID);
// if (behavior->GetBehaviorID() == -1 || behavior->GetShouldSetNewID()) {
// ObjectIDManager::Instance()->RequestPersistentID(
// [behaviorID, behavior, modelComponent, modelOwner, sysAddr](uint32_t persistentId) {
// behavior->SetShouldGetNewID(false);
// behavior->SetIsTemplated(false);
// behavior->SetBehaviorID(persistentId);
// // This updates the behavior ID of the behavior should this be a new behavior
// AMFArrayValue args;
// AMFStringValue* behaviorIDString = new AMFStringValue();
// behaviorIDString->SetStringValue(std::to_string(persistentId));
// args.InsertValue("behaviorID", behaviorIDString);
// AMFStringValue* objectIDAsString = new AMFStringValue();
// objectIDAsString->SetStringValue(std::to_string(modelComponent->GetParent()->GetObjectID()));
// args.InsertValue("objectID", objectIDAsString);
// GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorID", &args);
// ControlBehaviors::SendBehaviorListToClient(modelComponent->GetParent(), sysAddr, modelOwner);
// });
// }
}
void SendBehaviorListToClient(
Entity* modelEntity,
const SystemAddress& sysAddr,
Entity* modelOwner
@@ -57,7 +74,7 @@ void ControlBehaviors::SendBehaviorListToClient(
if (!modelComponent) return;
AMFArrayValue behaviorsToSerialize;
AMFArrayValue behaviorsToSerialize;
AMFArrayValue* behaviors = new AMFArrayValue(); // Empty for now
@@ -79,3 +96,506 @@ void ControlBehaviors::SendBehaviorListToClient(
behaviorsToSerialize.InsertValue("objectID", amfStringValueForObjectID);
GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorList", &behaviorsToSerialize);
}
void ModelTypeChanged(AMFArrayValue* arguments, ModelComponent* ModelComponent) {
auto* modelTypeAmf = arguments->FindValue<AMFDoubleValue>("ModelType");
if (!modelTypeAmf) return;
uint32_t modelType = static_cast<uint32_t>(modelTypeAmf->GetDoubleValue());
//TODO Update the model type here
}
void ToggleExecutionUpdates() {
//TODO do something with this info
}
void AddStrip(AMFArrayValue* arguments) {
auto* strip = arguments->FindValue<AMFArrayValue>("strip");
if (!strip) return;
auto* actions = strip->FindValue<AMFArrayValue>("actions");
if (!actions) return;
auto* uiArray = arguments->FindValue<AMFArrayValue>("ui");
if (!uiArray) return;
auto* xPositionValue = uiArray->FindValue<AMFDoubleValue>("x");
if (!xPositionValue) return;
double xPosition = xPositionValue->GetDoubleValue();
auto* yPositionValue = uiArray->FindValue<AMFDoubleValue>("y");
if (!yPositionValue) return;
double yPosition = yPositionValue->GetDoubleValue();
STRIPID stripID = GetStripIDFromArgument(arguments);
BEHAVIORSTATE stateID = GetBehaviorStateFromArgument(arguments);
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
std::string type = "";
std::string valueParameterName = "";
std::string valueParameterString = "";
double valueParameterDouble = 0.0;
for (uint32_t position = 0; position < actions->GetDenseValueSize(); position++) {
auto* actionAsArray = actions->GetValueAt<AMFArrayValue>(position);
if (!actionAsArray) continue;
for (auto& typeValueMap : actionAsArray->GetAssociativeMap()) {
if (typeValueMap.first == "Type") {
if (typeValueMap.second->GetValueType() != AMFValueType::AMFString) continue;
type = static_cast<AMFStringValue*>(typeValueMap.second)->GetStringValue();
} else {
valueParameterName = typeValueMap.first;
// Message is the only known string parameter
if (valueParameterName == "Message") {
if (typeValueMap.second->GetValueType() != AMFValueType::AMFString) continue;
valueParameterString = static_cast<AMFStringValue*>(typeValueMap.second)->GetStringValue();
} else {
if (typeValueMap.second->GetValueType() != AMFValueType::AMFDouble) continue;
valueParameterDouble = static_cast<AMFDoubleValue*>(typeValueMap.second)->GetDoubleValue();
}
}
}
// modelComponent->AddStrip(stateID, stripID, type, behaviorID, valueParameterName, valueParameterString, valueParameterDouble, "", xPosition, yPosition);
type.clear();
valueParameterName.clear();
valueParameterString.clear();
valueParameterDouble = 0.0;
}
// RequestUpdatedID(behaviorID);
}
void RemoveStrip(AMFArrayValue* arguments) {
STRIPID stripID = GetStripIDFromArgument(arguments);
BEHAVIORSTATE stateID = GetBehaviorStateFromArgument(arguments);
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
// modelComponent->RemoveStrip(stateID, stripID, behaviorID);
// RequestUpdatedID(behaviorID);
}
void MergeStrips(AMFArrayValue* arguments) {
STRIPID srcStripID = GetStripIDFromArgument(arguments, "srcStripID");
BEHAVIORSTATE dstStateID = GetBehaviorStateFromArgument(arguments, "dstStateID");
BEHAVIORSTATE srcStateID = GetBehaviorStateFromArgument(arguments, "srcStateID");
auto* dstActionIndexValue = arguments->FindValue<AMFDoubleValue>("dstActionIndex");
if (!dstActionIndexValue) return;
uint32_t dstActionIndex = static_cast<uint32_t>(dstActionIndexValue->GetDoubleValue());
STRIPID dstStripID = GetStripIDFromArgument(arguments, "dstStripID");
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
// modelComponent->MergeStrips(srcStripID, dstStripID, srcStateID, dstStateID, behaviorID, dstActionIndex);
// RequestUpdatedID(behaviorID);
}
void SplitStrip(AMFArrayValue* arguments) {
auto* srcActionIndexValue = arguments->FindValue<AMFDoubleValue>("srcActionIndex");
if (!srcActionIndexValue) return;
uint32_t srcActionIndex = static_cast<uint32_t>(srcActionIndexValue->GetDoubleValue());
STRIPID srcStripID = GetStripIDFromArgument(arguments, "srcStripID");
BEHAVIORSTATE srcStateID = GetBehaviorStateFromArgument(arguments, "srcStateID");
STRIPID dstStripID = GetStripIDFromArgument(arguments, "dstStripID");
BEHAVIORSTATE dstStateID = GetBehaviorStateFromArgument(arguments, "dstStateID");
auto* dstStripUIArray = arguments->FindValue<AMFArrayValue>("dstStripUI");
if (!dstStripUIArray) return;
auto* xPositionValue = dstStripUIArray->FindValue<AMFDoubleValue>("x");
auto* yPositionValue = dstStripUIArray->FindValue<AMFDoubleValue>("y");
if (!xPositionValue || !yPositionValue) return;
// x and y position 15 are just where the game puts the strip by default if none is given.
double yPosition = yPositionValue->GetDoubleValue();
double xPosition = xPositionValue->GetDoubleValue();
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
// modelComponent->SplitStrip(srcActionIndex, srcStripID, srcStateID, dstStripID, dstStateID, behaviorID, yPosition, xPosition);
// RequestUpdatedID(behaviorID);
}
void UpdateStripUI(AMFArrayValue* arguments) {
auto* uiArray = arguments->FindValue<AMFArrayValue>("ui");
if (!uiArray) return;
auto* xPositionValue = uiArray->FindValue<AMFDoubleValue>("x");
auto* yPositionValue = uiArray->FindValue<AMFDoubleValue>("y");
if (!xPositionValue || !yPositionValue) return;
double yPosition = yPositionValue->GetDoubleValue();
double xPosition = xPositionValue->GetDoubleValue();
STRIPID stripID = GetStripIDFromArgument(arguments);
BEHAVIORSTATE stateID = GetBehaviorStateFromArgument(arguments);
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
// modelComponent->UpdateUIOfStrip(stateID, stripID, xPosition, yPosition, behaviorID);
// RequestUpdatedID(behaviorID);
}
void AddAction(AMFArrayValue* arguments) {
auto* actionIndexAmf = arguments->FindValue<AMFDoubleValue>("actionIndex");
if (!actionIndexAmf) return;
uint32_t actionIndex = static_cast<uint32_t>(actionIndexAmf->GetDoubleValue());
STRIPID stripID = GetStripIDFromArgument(arguments);
BEHAVIORSTATE stateID = GetBehaviorStateFromArgument(arguments);
std::string type = "";
std::string valueParameterName = "";
std::string valueParameterString = "";
double valueParameterDouble = 0.0;
auto* action = arguments->FindValue<AMFArrayValue>("action");
if (!action) return;
for (auto& typeValueMap : action->GetAssociativeMap()) {
if (typeValueMap.first == "Type") {
if (typeValueMap.second->GetValueType() != AMFValueType::AMFString) continue;
type = static_cast<AMFStringValue*>(typeValueMap.second)->GetStringValue();
} else {
valueParameterName = typeValueMap.first;
// Message is the only known string parameter
if (valueParameterName == "Message") {
if (typeValueMap.second->GetValueType() != AMFValueType::AMFString) continue;
valueParameterString = static_cast<AMFStringValue*>(typeValueMap.second)->GetStringValue();
} else {
if (typeValueMap.second->GetValueType() != AMFValueType::AMFDouble) continue;
valueParameterDouble = static_cast<AMFDoubleValue*>(typeValueMap.second)->GetDoubleValue();
}
}
}
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
// modelComponent->AddAction(stateID, stripID, type, valueParameterName, valueParameterString, valueParameterDouble, "", actionIndex, behaviorID);
// RequestUpdatedID(behaviorID);
}
void MigrateActions(AMFArrayValue* arguments) {
auto* srcActionIndexAmf = arguments->FindValue<AMFDoubleValue>("srcActionIndex");
if (!srcActionIndexAmf) return;
uint32_t srcActionIndex = static_cast<uint32_t>(srcActionIndexAmf->GetDoubleValue());
STRIPID srcStripID = GetStripIDFromArgument(arguments, "srcStripID");
BEHAVIORSTATE srcStateID = GetBehaviorStateFromArgument(arguments, "srcStateID");
auto* dstActionIndexAmf = arguments->FindValue<AMFDoubleValue>("dstActionIndex");
if (!dstActionIndexAmf) return;
uint32_t dstActionIndex = static_cast<uint32_t>(dstActionIndexAmf->GetDoubleValue());
STRIPID dstStripID = GetStripIDFromArgument(arguments, "dstStripID");
BEHAVIORSTATE dstStateID = GetBehaviorStateFromArgument(arguments, "dstStateID");
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
// modelComponent->MigrateActions(srcActionIndex, srcStripID, srcStateID, dstActionIndex, dstStripID, dstStateID, behaviorID);
// RequestUpdatedID(behaviorID);
}
void RearrangeStrip(AMFArrayValue* arguments) {
auto* srcActionIndexValue = arguments->FindValue<AMFDoubleValue>("srcActionIndex");
uint32_t srcActionIndex = static_cast<uint32_t>(srcActionIndexValue->GetDoubleValue());
uint32_t stripID = GetStripIDFromArgument(arguments);
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
auto* dstActionIndexValue = arguments->FindValue<AMFDoubleValue>("dstActionIndex");
uint32_t dstActionIndex = static_cast<uint32_t>(dstActionIndexValue->GetDoubleValue());
BEHAVIORSTATE stateID = GetBehaviorStateFromArgument(arguments);
// modelComponent->RearrangeStrip(stateID, stripID, srcActionIndex, dstActionIndex, behaviorID);
// RequestUpdatedID(behaviorID);
}
void Add(AMFArrayValue* arguments) {
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
uint32_t behaviorIndex = 0;
auto* behaviorIndexAmf = arguments->FindValue<AMFDoubleValue>("BehaviorIndex");
if (!behaviorIndexAmf) return;
behaviorIndex = static_cast<uint32_t>(behaviorIndexAmf->GetDoubleValue());
// modelComponent->AddBehavior(behaviorID, behaviorIndex, modelOwner);
// SendBehaviorListToClient();
}
void RemoveActions(AMFArrayValue* arguments) {
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
auto* actionIndexAmf = arguments->FindValue<AMFDoubleValue>("actionIndex");
if (!actionIndexAmf) return;
uint32_t actionIndex = static_cast<uint32_t>(actionIndexAmf->GetDoubleValue());
STRIPID stripID = GetStripIDFromArgument(arguments);
BEHAVIORSTATE stateID = GetBehaviorStateFromArgument(arguments);
// modelComponent->RemoveAction(stateID, stripID, actionIndex, behaviorID);
// RequestUpdatedID(behaviorID);
}
void Rename(Entity* modelEntity, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments) {
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
auto* nameAmf = arguments->FindValue<AMFStringValue>("Name");
if (!nameAmf) return;
auto name = nameAmf->GetStringValue();
// modelComponent->Rename(behaviorID, name);
SendBehaviorListToClient(modelEntity, sysAddr, modelOwner);
// RequestUpdatedID(behaviorID);
}
// TODO This is also supposed to serialize the state of the behaviors in progress but those aren't implemented yet
void SendBehaviorBlocksToClient(ModelComponent* modelComponent, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments) {
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
// auto modelBehavior = modelComponent->FindBehavior(behaviorID);
// if (!modelBehavior) return;
// modelBehavior->VerifyStates();
// auto states = modelBehavior->GetBehaviorStates();
// // Begin serialization.
// /**
// * for each state
// * strip id
// * ui info
// * x
// * y
// * actions
// * action1
// * action2
// * ...
// * behaviorID of strip
// * objectID of strip
// */
// LWOOBJID targetObjectID = LWOOBJID_EMPTY;
// behaviorID = 0;
// AMFArrayValue behaviorInfo;
// AMFArrayValue* stateSerialize = new AMFArrayValue();
// for (auto it = states.begin(); it != states.end(); it++) {
// Game::logger->Log("PropertyBehaviors", "Begin serialization of state %i!\n", it->first);
// AMFArrayValue* state = new AMFArrayValue();
// AMFDoubleValue* stateAsDouble = new AMFDoubleValue();
// stateAsDouble->SetDoubleValue(it->first);
// state->InsertValue("id", stateAsDouble);
// AMFArrayValue* strips = new AMFArrayValue();
// auto stripsInState = it->second->GetStrips();
// for (auto strip = stripsInState.begin(); strip != stripsInState.end(); strip++) {
// Game::logger->Log("PropertyBehaviors", "Begin serialization of strip %i!\n", strip->first);
// AMFArrayValue* thisStrip = new AMFArrayValue();
// AMFDoubleValue* stripID = new AMFDoubleValue();
// stripID->SetDoubleValue(strip->first);
// thisStrip->InsertValue("id", stripID);
// AMFArrayValue* uiArray = new AMFArrayValue();
// AMFDoubleValue* yPosition = new AMFDoubleValue();
// yPosition->SetDoubleValue(strip->second->GetYPosition());
// uiArray->InsertValue("y", yPosition);
// AMFDoubleValue* xPosition = new AMFDoubleValue();
// xPosition->SetDoubleValue(strip->second->GetXPosition());
// uiArray->InsertValue("x", xPosition);
// thisStrip->InsertValue("ui", uiArray);
// targetObjectID = modelComponent->GetParent()->GetObjectID();
// behaviorID = modelBehavior->GetBehaviorID();
// AMFArrayValue* stripSerialize = new AMFArrayValue();
// for (auto behaviorAction : strip->second->GetActions()) {
// Game::logger->Log("PropertyBehaviors", "Begin serialization of action %s!\n", behaviorAction->actionName.c_str());
// AMFArrayValue* thisAction = new AMFArrayValue();
// AMFStringValue* actionName = new AMFStringValue();
// actionName->SetStringValue(behaviorAction->actionName);
// thisAction->InsertValue("Type", actionName);
// if (behaviorAction->parameterValueString != "")
// {
// AMFStringValue* valueAsString = new AMFStringValue();
// valueAsString->SetStringValue(behaviorAction->parameterValueString);
// thisAction->InsertValue(behaviorAction->parameterName, valueAsString);
// }
// else if (behaviorAction->parameterValueDouble != 0.0)
// {
// AMFDoubleValue* valueAsDouble = new AMFDoubleValue();
// valueAsDouble->SetDoubleValue(behaviorAction->parameterValueDouble);
// thisAction->InsertValue(behaviorAction->parameterName, valueAsDouble);
// }
// stripSerialize->PushBackValue(thisAction);
// }
// thisStrip->InsertValue("actions", stripSerialize);
// strips->PushBackValue(thisStrip);
// }
// state->InsertValue("strips", strips);
// stateSerialize->PushBackValue(state);
// }
// behaviorInfo.InsertValue("states", stateSerialize);
// AMFStringValue* objectidAsString = new AMFStringValue();
// objectidAsString->SetStringValue(std::to_string(targetObjectID));
// behaviorInfo.InsertValue("objectID", objectidAsString);
// AMFStringValue* behaviorIDAsString = new AMFStringValue();
// behaviorIDAsString->SetStringValue(std::to_string(behaviorID));
// behaviorInfo.InsertValue("BehaviorID", behaviorIDAsString);
// GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorBlocks", &behaviorInfo);
}
void UpdateAction(AMFArrayValue* arguments) {
std::string type = "";
std::string valueParameterName = "";
std::string valueParameterString = "";
double valueParameterDouble = 0.0;
auto* actionAsArray = arguments->FindValue<AMFArrayValue>("action");
if (!actionAsArray) return;
for (auto& typeValueMap : actionAsArray->GetAssociativeMap()) {
if (typeValueMap.first == "Type") {
if (typeValueMap.second->GetValueType() != AMFValueType::AMFString) continue;
type = static_cast<AMFStringValue*>(typeValueMap.second)->GetStringValue();
} else {
valueParameterName = typeValueMap.first;
// Message is the only known string parameter
if (valueParameterName == "Message") {
if (typeValueMap.second->GetValueType() != AMFValueType::AMFString) continue;
valueParameterString = static_cast<AMFStringValue*>(typeValueMap.second)->GetStringValue();
} else {
if (typeValueMap.second->GetValueType() != AMFValueType::AMFDouble) continue;
valueParameterDouble = static_cast<AMFDoubleValue*>(typeValueMap.second)->GetDoubleValue();
}
}
}
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
auto* actionIndexValue = arguments->FindValue<AMFDoubleValue>("actionIndex");
if (!actionIndexValue) return;
uint32_t actionIndex = static_cast<uint32_t>(actionIndexValue->GetDoubleValue());
STRIPID stripID = GetStripIDFromArgument(arguments);
BEHAVIORSTATE stateID = GetBehaviorStateFromArgument(arguments);
// modelComponent->UpdateAction(stateID, stripID, type, valueParameterName, valueParameterString, valueParameterDouble, "", actionIndex, behaviorID);
// RequestUpdatedID(behaviorID);
}
void MoveToInventory(ModelComponent* modelComponent, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments) {
// This closes the UI menu should it be open while the player is removing behaviors
AMFArrayValue args;
AMFFalseValue* stateToPop = new AMFFalseValue();
args.InsertValue("visible", stateToPop);
GameMessages::SendUIMessageServerToSingleClient(modelOwner, modelOwner->GetParentUser()->GetSystemAddress(), "ToggleBehaviorEditor", &args);
uint32_t behaviorID = GetBehaviorIDFromArgument(arguments);
auto* behaviorIndexValue = arguments->FindValue<AMFDoubleValue>("BehaviorIndex");
if (!behaviorIndexValue) return;
uint32_t behaviorIndex = static_cast<uint32_t>(behaviorIndexValue->GetDoubleValue());
// modelComponent->MoveBehaviorToInventory(behaviorID, behaviorIndex, modelOwner);
SendBehaviorListToClient(modelComponent->GetParent(), sysAddr, modelOwner);
}
void ControlBehaviors::ProcessCommand(Entity* modelEntity, const SystemAddress& sysAddr, AMFArrayValue* arguments, std::string command, Entity* modelOwner) {
if (!modelEntity || !modelOwner || !arguments) return;
auto* modelComponent = modelEntity->GetComponent<ModelComponent>();
if (!modelComponent) return;
if (command == "sendBehaviorListToClient")
SendBehaviorListToClient(modelEntity, sysAddr, modelOwner);
else if (command == "modelTypeChanged")
ModelTypeChanged(arguments, modelComponent);
else if (command == "toggleExecutionUpdates")
ToggleExecutionUpdates();
else if (command == "addStrip")
AddStrip(arguments);
else if (command == "removeStrip")
RemoveStrip(arguments);
else if (command == "mergeStrips")
MergeStrips(arguments);
else if (command == "splitStrip")
SplitStrip(arguments);
else if (command == "updateStripUI")
UpdateStripUI(arguments);
else if (command == "addAction")
AddAction(arguments);
else if (command == "migrateActions")
MigrateActions(arguments);
else if (command == "rearrangeStrip")
RearrangeStrip(arguments);
else if (command == "add")
Add(arguments);
else if (command == "removeActions")
RemoveActions(arguments);
else if (command == "rename")
Rename(modelEntity, sysAddr, modelOwner, arguments);
else if (command == "sendBehaviorBlocksToClient")
SendBehaviorBlocksToClient(modelComponent, sysAddr, modelOwner, arguments);
else if (command == "moveToInventory")
MoveToInventory(modelComponent, sysAddr, modelOwner, arguments);
else if (command == "updateAction")
UpdateAction(arguments);
else
Game::logger->Log("ControlBehaviors", "Unknown behavior command (%s)\n", command.c_str());
}

View File

@@ -9,6 +9,7 @@
class Entity;
class AMFArrayValue;
class ModelComponent;
namespace ControlBehaviors {
/**
@@ -21,15 +22,6 @@ namespace ControlBehaviors {
* @param modelOwner The owner of the model which sent this command
*/
void ProcessCommand(Entity* modelEntity, const SystemAddress& sysAddr, AMFArrayValue* arguments, std::string command, Entity* modelOwner);
/**
* @brief Helper function to send the behavior list to the client
*
* @param modelEntity The model that sent this command
* @param sysAddr The SystemAddress to respond to
* @param modelOwner The owner of the model which sent this command
*/
void SendBehaviorListToClient(Entity* modelEntity, const SystemAddress& sysAddr, Entity* modelOwner);
};
#endif //!__CONTROLBEHAVIORS__H__

View File

@@ -1,5 +1,4 @@
set(DGAME_DUTILITIES_SOURCES "BrickDatabase.cpp"
"dLocale.cpp"
"GameConfig.cpp"
"GUID.cpp"
"Loot.cpp"

View File

@@ -154,7 +154,7 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat
case PreconditionType::MissionComplete:
mission = missionComponent->GetMission(value);
return mission == nullptr || mission->GetMissionState() >= MissionState::MISSION_STATE_COMPLETE;
return mission == nullptr ? false : mission->GetMissionState() >= MissionState::MISSION_STATE_COMPLETE;
case PreconditionType::PetDeployed:
return false; // TODO
case PreconditionType::HasFlag:
@@ -277,11 +277,6 @@ bool PreconditionExpression::Check(Entity* player, bool evaluateCosts) const {
return true;
}
if (player->GetGMLevel() >= 9) // Developers can skip this for testing
{
return true;
}
const auto a = Preconditions::Check(player, condition, evaluateCosts);
if (!a) {

View File

@@ -64,6 +64,8 @@
#include "ScriptedActivityComponent.h"
#include "LevelProgressionComponent.h"
#include "AssetManager.h"
#include "BinaryPathFinder.h"
#include "dConfig.h"
void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entity* entity, const SystemAddress& sysAddr) {
std::string chatCommand;
@@ -210,22 +212,6 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
return;
}
if (chatCommand == "skip-ags") {
auto* missionComponent = entity->GetComponent<MissionComponent>();
if (missionComponent != nullptr && missionComponent->HasMission(479)) {
missionComponent->CompleteMission(479);
}
}
if (chatCommand == "skip-sg") {
auto* missionComponent = entity->GetComponent<MissionComponent>();
if (missionComponent != nullptr && missionComponent->HasMission(229)) {
missionComponent->CompleteMission(229);
}
}
if (chatCommand == "fix-stats") {
// Reset skill component and buff component
auto* skillComponent = entity->GetComponent<SkillComponent>();
@@ -248,7 +234,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
}
if (chatCommand == "credits" || chatCommand == "info") {
const auto& customText = chatCommand == "credits" ? VanityUtilities::ParseMarkdown("./vanity/CREDITS.md") : VanityUtilities::ParseMarkdown("./vanity/INFO.md");
const auto& customText = chatCommand == "credits" ? VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/CREDITS.md").string()) : VanityUtilities::ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/INFO.md").string());
{
AMFArrayValue args;
@@ -1263,6 +1249,57 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
EntityManager::Instance()->ConstructEntity(newEntity);
}
if (chatCommand == "spawngroup" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER && args.size() >= 3) {
auto controllablePhysicsComponent = entity->GetComponent<ControllablePhysicsComponent>();
if (!controllablePhysicsComponent) return;
LOT lot{};
uint32_t numberToSpawn{};
float radiusToSpawnWithin{};
if (!GeneralUtils::TryParse(args[0], lot)) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid lot.");
return;
}
if (!GeneralUtils::TryParse(args[1], numberToSpawn) && numberToSpawn > 0) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid number of enemies to spawn.");
return;
}
// Must spawn within a radius of at least 0.0f
if (!GeneralUtils::TryParse(args[2], radiusToSpawnWithin) && radiusToSpawnWithin < 0.0f) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid radius to spawn within.");
return;
}
EntityInfo info;
info.lot = lot;
info.spawner = nullptr;
info.spawnerID = entity->GetObjectID();
info.spawnerNodeID = 0;
auto playerPosition = controllablePhysicsComponent->GetPosition();
while (numberToSpawn > 0) {
auto randomAngle = GeneralUtils::GenerateRandomNumber<float>(0.0f, 2 * PI);
auto randomRadius = GeneralUtils::GenerateRandomNumber<float>(0.0f, radiusToSpawnWithin);
// Set the position to the generated random position plus the player position. This will
// spawn the entity in a circle around the player. As you get further from the player, the angle chosen will get less accurate.
info.pos = playerPosition + NiPoint3(cos(randomAngle) * randomRadius, 0.0f, sin(randomAngle) * randomRadius);
info.rot = NiQuaternion();
auto newEntity = EntityManager::Instance()->CreateEntity(info);
if (newEntity == nullptr) {
ChatPackets::SendSystemMessage(sysAddr, u"Failed to spawn entity.");
return;
}
EntityManager::Instance()->ConstructEntity(newEntity);
numberToSpawn--;
}
}
if ((chatCommand == "giveuscore") && args.size() == 1 && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) {
int32_t uscore;
@@ -1714,6 +1751,21 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit
return;
}
if (chatCommand == "reloadconfig" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) {
Game::config->ReloadConfig();
VanityUtilities::SpawnVanity();
dpWorld::Instance().Reload();
auto entities = EntityManager::Instance()->GetEntitiesByComponent(COMPONENT_TYPE_SCRIPTED_ACTIVITY);
for (auto entity : entities) {
auto* scriptedActivityComponent = entity->GetComponent<ScriptedActivityComponent>();
if (!scriptedActivityComponent) continue;
scriptedActivityComponent->ReloadConfig();
}
Game::server->UpdateBandwidthLimit();
ChatPackets::SendSystemMessage(sysAddr, u"Successfully reloaded config for world!");
}
if (chatCommand == "rollloot" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_OPERATOR && args.size() >= 3) {
uint32_t lootMatrixIndex = 0;
uint32_t targetLot = 0;

View File

@@ -13,6 +13,7 @@
#include "tinyxml2.h"
#include "Game.h"
#include "dLogger.h"
#include "BinaryPathFinder.h"
#include <fstream>
@@ -27,7 +28,7 @@ void VanityUtilities::SpawnVanity() {
const uint32_t zoneID = Game::server->GetZoneID();
ParseXML("./vanity/NPC.xml");
ParseXML((BinaryPathFinder::GetBinaryDir() / "vanity/NPC.xml").string());
// Loop through all parties
for (const auto& party : m_Parties) {
@@ -131,7 +132,7 @@ void VanityUtilities::SpawnVanity() {
info.spawnerID = EntityManager::Instance()->GetZoneControlEntity()->GetObjectID();
info.settings = { new LDFData<bool>(u"hasCustomText", true),
new LDFData<std::string>(u"customText", ParseMarkdown("./vanity/TESTAMENT.md")) };
new LDFData<std::string>(u"customText", ParseMarkdown((BinaryPathFinder::GetBinaryDir() / "vanity/TESTAMENT.md").string())) };
auto* entity = EntityManager::Instance()->CreateEntity(info);

View File

@@ -1,81 +0,0 @@
#include "dLocale.h"
#include <clocale>
#include <fstream>
#include <sstream>
#include <vector>
#include <algorithm>
#include "tinyxml2.h"
#include "Game.h"
#include "dConfig.h"
dLocale::dLocale() {
if (Game::config->GetValue("locale_enabled") != "1") {
return;
}
std::ifstream file(m_LocalePath);
if (!file.good()) {
return;
}
std::stringstream data;
data << file.rdbuf();
if (data.str().empty()) {
return;
}
auto* doc = new tinyxml2::XMLDocument();
if (doc == nullptr) {
return;
}
if (doc->Parse(data.str().c_str(), data.str().size()) != 0) {
return;
}
std::hash<std::string> hash;
auto* localization = doc->FirstChildElement("localization");
auto* phrases = localization->FirstChildElement("phrases");
auto* phrase = phrases->FirstChildElement("phrase");
while (phrase != nullptr) {
// Add the phrase hash to the vector
m_Phrases.push_back(hash(phrase->Attribute("id")));
phrase = phrase->NextSiblingElement("phrase");
}
file.close();
delete doc;
}
dLocale::~dLocale() = default;
std::string dLocale::GetTemplate(const std::string& phraseID) {
return "%[" + phraseID + "]";
}
bool dLocale::HasPhrase(const std::string& phraseID) {
if (Game::config->GetValue("locale_enabled") != "1") {
return true;
}
// Compute the hash and see if it's in the vector
std::hash<std::string> hash;
std::size_t hashValue = hash(phraseID);
return std::find(m_Phrases.begin(), m_Phrases.end(), hashValue) != m_Phrases.end();
}
/*std::string dLocale::GetPhrase(const std::string& phraseID) {
if (m_Phrases.find(phraseID) == m_Phrases.end()) {
return "";
}
return m_Phrases[phraseID];
}*/

View File

@@ -1,19 +0,0 @@
#pragma once
#include <map>
#include <string>
#include <vector>
#include <cstdint>
class dLocale {
public:
dLocale();
~dLocale();
static std::string GetTemplate(const std::string& phraseID);
bool HasPhrase(const std::string& phraseID);
//std::string GetPhrase(const std::string& phraseID);
private:
std::string m_LocalePath = "./locale/locale.xml";
std::string m_Locale = "en_US"; // TODO: add to config
std::vector<std::size_t> m_Phrases;
};