DarkflameServer/dGame/dInventory/Item.cpp
David Markowitz 455f9470a5
Move EntityManager to Game namespace (#1140)
* Move EntityManager to Game namespace

* move initialization to later

Need to wait for dZoneManager to be initialized.

* Fix bugs

- Cannot delete from a RandomAccessIterator while in a range based for loop.

Touchup zone manager initialize

replace magic numbers with better named constants
replace magic zonecontrol id with a more readable hex alternative
condense stack variables
move initializers closer to their use
initialize entity manager with zone control

change initialize timings

If zone is not zero we expect to initialize the entity manager during zone manager initialization

Add constexpr for zone control LOT

* Add proper error handling

* revert vanity changes

* Update WorldServer.cpp

* Update dZoneManager.cpp
2023-07-15 13:56:33 -07:00

504 lines
13 KiB
C++

#include "Item.h"
#include <sstream>
#include "../dWorldServer/ObjectIDManager.h"
#include "GeneralUtils.h"
#include "GameMessages.h"
#include "Entity.h"
#include "Game.h"
#include "dLogger.h"
#include "EntityManager.h"
#include "RenderComponent.h"
#include "PossessableComponent.h"
#include "CharacterComponent.h"
#include "eItemType.h"
#include "AssetManager.h"
#include "InventoryComponent.h"
#include "Loot.h"
#include "eObjectBits.h"
#include "eReplicaComponentType.h"
#include "eUseItemResponse.h"
#include "CDBrickIDTableTable.h"
#include "CDObjectSkillsTable.h"
#include "CDComponentsRegistryTable.h"
#include "CDPackageComponentTable.h"
Item::Item(const LWOOBJID id, const LOT lot, Inventory* inventory, const uint32_t slot, const uint32_t count, const bool bound, const std::vector<LDFBaseData*>& config, const LWOOBJID parent, LWOOBJID subKey, eLootSourceType lootSourceType) {
if (!Inventory::IsValidItem(lot)) {
return;
}
this->id = id;
this->lot = lot;
this->inventory = inventory;
this->slot = slot;
this->count = count;
this->bound = bound;
this->config = config;
this->parent = parent;
this->info = &Inventory::FindItemComponent(lot);
this->preconditions = new PreconditionExpression(this->info->reqPrecondition);
this->subKey = subKey;
inventory->AddManagedItem(this);
}
Item::Item(
const LOT lot,
Inventory* inventory,
const uint32_t slot,
const uint32_t count,
const std::vector<LDFBaseData*>& config,
const LWOOBJID parent,
bool showFlyingLoot,
bool isModMoveAndEquip,
LWOOBJID subKey,
bool bound,
eLootSourceType lootSourceType) {
if (!Inventory::IsValidItem(lot)) {
return;
}
if (isModMoveAndEquip) {
showFlyingLoot = false;
}
this->lot = lot;
this->inventory = inventory;
this->slot = slot;
this->count = count;
this->config = config;
this->parent = parent;
this->id = LWOOBJID_EMPTY;
this->info = &Inventory::FindItemComponent(lot);
this->bound = info->isBOP || bound;
this->preconditions = new PreconditionExpression(this->info->reqPrecondition);
this->subKey = subKey;
LWOOBJID id = ObjectIDManager::GenerateRandomObjectID();
GeneralUtils::SetBit(id, eObjectBits::CHARACTER);
GeneralUtils::SetBit(id, eObjectBits::PERSISTENT);
const auto type = static_cast<eItemType>(info->itemType);
if (type == eItemType::MOUNT) {
GeneralUtils::SetBit(id, eObjectBits::CLIENT);
}
this->id = id;
inventory->AddManagedItem(this);
auto* entity = inventory->GetComponent()->GetParent();
GameMessages::SendAddItemToInventoryClientSync(entity, entity->GetSystemAddress(), this, id, showFlyingLoot, static_cast<int>(this->count), subKey, lootSourceType);
if (isModMoveAndEquip) {
Equip();
Game::logger->Log("Item", "Move and equipped (%i) from (%i)", this->lot, this->inventory->GetType());
Game::entityManager->SerializeEntity(inventory->GetComponent()->GetParent());
}
}
LWOOBJID Item::GetId() const {
return id;
}
LOT Item::GetLot() const {
return lot;
}
uint32_t Item::GetCount() const {
return count;
}
uint32_t Item::GetSlot() const {
return slot;
}
std::vector<LDFBaseData*>& Item::GetConfig() {
return config;
}
const CDItemComponent& Item::GetInfo() const {
return *info;
}
bool Item::GetBound() const {
return bound;
}
Inventory* Item::GetInventory() const {
return inventory;
}
LWOOBJID Item::GetParent() const {
return parent;
}
LWOOBJID Item::GetSubKey() const {
return subKey;
}
PreconditionExpression* Item::GetPreconditionExpression() const {
return preconditions;
}
void Item::SetCount(const uint32_t value, const bool silent, const bool disassemble, const bool showFlyingLoot, eLootSourceType lootSourceType) {
if (value == count) {
return;
}
const auto delta = std::abs(static_cast<int32_t>(value) - static_cast<int32_t>(count));
const auto type = static_cast<eItemType>(info->itemType);
if (disassemble) {
if (value < count) {
for (auto i = 0; i < delta; ++i) {
Disassemble();
}
}
}
if (!silent) {
auto* entity = inventory->GetComponent()->GetParent();
if (value > count) {
GameMessages::SendAddItemToInventoryClientSync(entity, entity->GetSystemAddress(), this, id, showFlyingLoot, delta, LWOOBJID_EMPTY, lootSourceType);
} else {
GameMessages::SendRemoveItemFromInventory(entity, entity->GetSystemAddress(), id, lot, inventory->GetType(), delta, value);
}
}
count = value;
if (count == 0) {
RemoveFromInventory();
}
}
void Item::SetSlot(const uint32_t value) {
if (slot == value) {
return;
}
for (const auto& pair : inventory->GetItems()) {
auto* item = pair.second;
if (item->slot == value) {
item->slot = slot;
}
}
slot = value;
}
void Item::SetBound(const bool value) {
bound = value;
}
void Item::SetSubKey(LWOOBJID value) {
subKey = value;
}
void Item::SetInventory(Inventory* value) {
inventory->RemoveManagedItem(this);
inventory = value;
inventory->AddManagedItem(this);
}
void Item::Equip(const bool skipChecks) {
if (IsEquipped()) {
return;
}
inventory->GetComponent()->EquipItem(this, skipChecks);
}
void Item::UnEquip() {
if (!IsEquipped()) {
return;
}
inventory->GetComponent()->UnEquipItem(this);
}
bool Item::IsEquipped() const {
auto* component = inventory->GetComponent();
for (const auto& pair : component->GetEquippedItems()) {
const auto item = pair.second;
if (item.id == id) {
return true;
}
}
return false;
}
bool Item::Consume() {
auto* skillsTable = CDClientManager::Instance().GetTable<CDObjectSkillsTable>();
auto skills = skillsTable->Query([=](const CDObjectSkills entry) {
return entry.objectTemplate == static_cast<uint32_t>(lot);
});
auto success = false;
for (auto& skill : skills) {
if (skill.castOnType == 3) // Consumable type
{
success = true;
}
}
Game::logger->LogDebug("Item", "Consumed LOT (%i) itemID (%llu). Success=(%d)", lot, id, success);
GameMessages::SendUseItemResult(inventory->GetComponent()->GetParent(), lot, success);
if (success) {
inventory->GetComponent()->RemoveItem(lot, 1);
}
return success;
}
void Item::UseNonEquip(Item* item) {
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::MOUNT) {
playerInventoryComponent->HandlePossession(this);
// TODO Check if mounts are allowed to be spawned
} else if (type == eItemType::PET_INVENTORY_ITEM && subKey != LWOOBJID_EMPTY) {
const auto& databasePet = playerInventoryComponent->GetDatabasePet(subKey);
if (databasePet.lot != LOT_NULL) {
playerInventoryComponent->SpawnPet(this);
}
// This precondition response is taken care of in SpawnPet().
} else {
bool success = false;
auto inventory = item->GetInventory();
if (inventory && inventory->GetType() == eInventoryType::ITEMS) {
auto* compRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
const auto packageComponentId = compRegistryTable->GetByIDAndType(lot, eReplicaComponentType::PACKAGE);
if (packageComponentId == 0) return;
auto* packCompTable = CDClientManager::Instance().GetTable<CDPackageComponentTable>();
auto packages = packCompTable->Query([=](const CDPackageComponent entry) {return entry.id == static_cast<uint32_t>(packageComponentId); });
auto success = !packages.empty();
if (success) {
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;
}
}
}
if (playerInventoryComponent->HasSpaceForLoot(rolledLoot)) {
LootGenerator::Instance().GiveLoot(playerInventoryComponent->GetParent(), rolledLoot, eLootSourceType::CONSUMPTION);
item->SetCount(item->GetCount() - 1);
} else {
success = false;
}
} else {
GameMessages::SendUseItemRequirementsResponse(
playerInventoryComponent->GetParent()->GetObjectID(),
playerInventoryComponent->GetParent()->GetSystemAddress(),
eUseItemResponse::FailedPrecondition
);
success = false;
}
}
}
Game::logger->LogDebug("Item", "Player %llu %s used item %i", playerEntity->GetObjectID(), success ? "successfully" : "unsuccessfully", thisLot);
GameMessages::SendUseItemResult(playerInventoryComponent->GetParent(), thisLot, success);
}
}
void Item::Disassemble(const eInventoryType inventoryType) {
for (auto* data : config) {
if (data->GetKey() == u"assemblyPartLOTs") {
auto modStr = data->GetValueAsString();
// This shouldn't be null but always check your pointers.
if (GetInventory()) {
auto inventoryComponent = GetInventory()->GetComponent();
if (inventoryComponent) {
auto entity = inventoryComponent->GetParent();
if (entity) entity->SetVar<std::string>(u"currentModifiedBuild", modStr);
}
}
std::vector<LOT> modArray;
std::stringstream ssData(modStr);
std::string token;
const auto deliminator = '+';
while (std::getline(ssData, token, deliminator)) {
const auto modLot = std::stoi(token.substr(2, token.size() - 1));
modArray.push_back(modLot);
}
for (const auto mod : modArray) {
inventory->GetComponent()->AddItem(mod, 1, eLootSourceType::DELETION, inventoryType);
}
}
}
}
void Item::DisassembleModel() {
auto* table = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
const auto componentId = table->GetByIDAndType(GetLot(), eReplicaComponentType::RENDER);
auto query = CDClientDatabase::CreatePreppedStmt(
"SELECT render_asset, LXFMLFolder FROM RenderComponent WHERE id = ?;");
query.bind(1, (int)componentId);
auto result = query.execQuery();
if (result.eof() || result.fieldIsNull(0)) {
return;
}
std::string renderAsset = std::string(result.getStringField(0));
std::string lxfmlFolderName = std::string(result.getStringField(1));
std::vector<std::string> renderAssetSplit = GeneralUtils::SplitString(renderAsset, '\\');
if (renderAssetSplit.size() == 0) return;
std::string lxfmlPath = "BrickModels/" + lxfmlFolderName + "/" + GeneralUtils::SplitString(renderAssetSplit.back(), '.').at(0) + ".lxfml";
auto buffer = Game::assetManager->GetFileAsBuffer(lxfmlPath.c_str());
if (!buffer.m_Success) {
Game::logger->Log("Item", "Failed to load %s to disassemble model into bricks, check that this file exists", lxfmlPath.c_str());
return;
}
std::istream file(&buffer);
result.finalize();
if (!file.good()) {
buffer.close();
return;
}
std::stringstream data;
data << file.rdbuf();
buffer.close();
if (data.str().empty()) {
return;
}
auto* doc = new tinyxml2::XMLDocument();
if (!doc) {
return;
}
if (doc->Parse(data.str().c_str(), data.str().size()) != 0) {
return;
}
std::vector<int> parts;
auto* lxfml = doc->FirstChildElement("LXFML");
auto* bricks = lxfml->FirstChildElement("Bricks");
std::string searchTerm = "Brick";
if (!bricks) {
searchTerm = "Part";
bricks = lxfml->FirstChildElement("Scene")->FirstChildElement("Model")->FirstChildElement("Group");
if (!bricks) {
return;
}
}
auto* currentBrick = bricks->FirstChildElement(searchTerm.c_str());
while (currentBrick) {
if (currentBrick->Attribute("designID") != nullptr) {
parts.push_back(std::stoi(currentBrick->Attribute("designID")));
}
currentBrick = currentBrick->NextSiblingElement(searchTerm.c_str());
}
auto* brickIDTable = CDClientManager::Instance().GetTable<CDBrickIDTableTable>();
for (unsigned int part : parts) {
const auto brickID = brickIDTable->Query([=](const CDBrickIDTable& entry) {
return entry.LEGOBrickID == part;
});
if (brickID.empty()) {
continue;
}
GetInventory()->GetComponent()->AddItem(brickID[0].NDObjectID, 1, eLootSourceType::DELETION);
}
}
void Item::RemoveFromInventory() {
UnEquip();
count = 0;
inventory->RemoveManagedItem(this);
delete this;
}
Item::~Item() {
delete preconditions;
for (auto* value : config) {
delete value;
}
config.clear();
}