[Bug Fix] Disallow multiple augments in same item. (#1916)

- Disallows multiple augments via #augmentitem or otherwise.
- Added ItemInstance::ContainsAugmentByID(item_id) helper method for finding an augment in an item instance.
This commit is contained in:
Kinglykrab 2022-01-04 17:18:17 -05:00 committed by GitHub
parent 6bf5608cf3
commit ffa968f64f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 280 additions and 331 deletions

View File

@ -381,4 +381,11 @@ enum Invisibility : uint8 {
Special = 255 Special = 255
}; };
enum AugmentActions : int {
Insert,
Remove,
Swap,
Destroy
};
#endif /*COMMON_EMU_CONSTANTS_H*/ #endif /*COMMON_EMU_CONSTANTS_H*/

View File

@ -689,6 +689,25 @@ bool EQ::ItemInstance::IsAugmented()
return false; return false;
} }
bool EQ::ItemInstance::ContainsAugmentByID(uint32 item_id)
{
if (!m_item || !m_item->IsClassCommon()) {
return false;
}
if (!item_id) {
return false;
}
for (uint8 augment_slot = invaug::SOCKET_BEGIN; augment_slot <= invaug::SOCKET_END; ++augment_slot) {
if (GetAugmentItemID(augment_slot) == item_id) {
return true;
}
}
return false;
}
// Has attack/delay? // Has attack/delay?
bool EQ::ItemInstance::IsWeapon() const bool EQ::ItemInstance::IsWeapon() const
{ {

View File

@ -132,6 +132,7 @@ namespace EQ
void DeleteAugment(uint8 slot); void DeleteAugment(uint8 slot);
ItemInstance* RemoveAugment(uint8 index); ItemInstance* RemoveAugment(uint8 index);
bool IsAugmented(); bool IsAugmented();
bool ContainsAugmentByID(uint32 item_id);
ItemInstance* GetOrnamentationAug(int32 ornamentationAugtype) const; ItemInstance* GetOrnamentationAug(int32 ornamentationAugtype) const;
bool UpdateOrnamentationInfo(); bool UpdateOrnamentationInfo();
static bool CanTransform(const ItemData *ItemToTry, const ItemData *Container, bool AllowAll = false); static bool CanTransform(const ItemData *ItemToTry, const ItemData *Container, bool AllowAll = false);

View File

@ -744,6 +744,7 @@ RULE_BOOL(Inventory, EnforceAugmentWear, true, "Forces augment wear slot validat
RULE_BOOL(Inventory, DeleteTransformationMold, true, "False if you want mold to last forever") RULE_BOOL(Inventory, DeleteTransformationMold, true, "False if you want mold to last forever")
RULE_BOOL(Inventory, AllowAnyWeaponTransformation, false, "Weapons can use any weapon transformation") RULE_BOOL(Inventory, AllowAnyWeaponTransformation, false, "Weapons can use any weapon transformation")
RULE_BOOL(Inventory, TransformSummonedBags, false, "Transforms summoned bags into disenchanted ones instead of deleting") RULE_BOOL(Inventory, TransformSummonedBags, false, "Transforms summoned bags into disenchanted ones instead of deleting")
RULE_BOOL(Inventory, AllowMultipleOfSameAugment, false, "Allows multiple of the same augment to be placed in an item via #augmentitem or MQ2, set to true to allow")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Client) RULE_CATEGORY(Client)

View File

@ -2929,18 +2929,16 @@ void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app)
void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
{ {
if (app->size != sizeof(AugmentItem_Struct)) { if (app->size != sizeof(AugmentItem_Struct)) {
LogError("Invalid size for AugmentItem_Struct: Expected: [{}], Got: [{}]", LogError("Invalid size for AugmentItem_Struct: Expected: [{}], Got: [{}]", sizeof(AugmentItem_Struct), app->size);
sizeof(AugmentItem_Struct), app->size);
return; return;
} }
AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer; AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer;
bool deleteItems = false; if (ClientVersion() >= EQ::versions::ClientVersion::RoF) {
if (ClientVersion() >= EQ::versions::ClientVersion::RoF) if (
{ (in_augment->container_slot < EQ::invslot::EQUIPMENT_BEGIN || in_augment->container_slot > EQ::invslot::GENERAL_END) &&
if ((in_augment->container_slot < EQ::invslot::EQUIPMENT_BEGIN || in_augment->container_slot > EQ::invslot::GENERAL_END) && (in_augment->container_slot < EQ::invbag::GENERAL_BAGS_BEGIN || in_augment->container_slot > EQ::invbag::GENERAL_BAGS_END)
(in_augment->container_slot < EQ::invbag::GENERAL_BAGS_BEGIN || in_augment->container_slot > EQ::invbag::GENERAL_BAGS_END)) ) {
{
Message(Chat::Red, "The server does not allow augmentation actions from this slot."); Message(Chat::Red, "The server does not allow augmentation actions from this slot.");
auto cursor_item = m_inv[EQ::invslot::slotCursor]; auto cursor_item = m_inv[EQ::invslot::slotCursor];
auto augmented_item = m_inv[in_augment->container_slot]; auto augmented_item = m_inv[in_augment->container_slot];
@ -2950,20 +2948,16 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
return; return;
} }
EQ::ItemInstance *itemOneToPush = nullptr, *itemTwoToPush = nullptr; EQ::ItemInstance *item_one_to_push = nullptr, *item_two_to_push = nullptr;
//Log(Logs::DebugLevel::Moderate, Logs::Debug, "cslot: [{}] aslot: [{}] cidx: [{}] aidx: [{}] act: [{}] dest: [{}]",
// in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id);
EQ::ItemInstance *tobe_auged = nullptr, *old_aug = nullptr, *new_aug = nullptr, *aug = nullptr, *solvent = nullptr; EQ::ItemInstance *tobe_auged = nullptr, *old_aug = nullptr, *new_aug = nullptr, *aug = nullptr, *solvent = nullptr;
EQ::InventoryProfile& user_inv = GetInv(); EQ::InventoryProfile& user_inv = GetInv();
uint16 item_slot = in_augment->container_slot; uint16 item_slot = in_augment->container_slot;
uint16 solvent_slot = in_augment->augment_slot; uint16 solvent_slot = in_augment->augment_slot;
uint8 mat = EQ::InventoryProfile::CalcMaterialFromSlot(item_slot); // for when player is augging a piece of equipment while they're wearing it uint8 material = EQ::InventoryProfile::CalcMaterialFromSlot(item_slot); // for when player is augging a piece of equipment while they're wearing it
if (item_slot == INVALID_INDEX || solvent_slot == INVALID_INDEX) if (item_slot == INVALID_INDEX || solvent_slot == INVALID_INDEX) {
{
Message(Chat::Red, "Error: Invalid Aug Index."); Message(Chat::Red, "Error: Invalid Aug Index.");
return; return;
} }
@ -2971,272 +2965,231 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
tobe_auged = user_inv.GetItem(item_slot); tobe_auged = user_inv.GetItem(item_slot);
solvent = user_inv.GetItem(solvent_slot); solvent = user_inv.GetItem(solvent_slot);
if (!tobe_auged) if (!tobe_auged) {
{
Message(Chat::Red, "Error: Invalid item passed for augmenting."); Message(Chat::Red, "Error: Invalid item passed for augmenting.");
return; return;
} }
if ((in_augment->augment_action == 1) || (in_augment->augment_action == 2)) if (in_augment->augment_action == AugmentActions::Remove || in_augment->augment_action == AugmentActions::Swap) {
{ if (!solvent) { // Check for valid distiller if safely removing / swapping an augmentation
// Check for valid distiller if safely removing / swapping an augmentation
if (!solvent)
{
old_aug = tobe_auged->GetAugment(in_augment->augment_index); old_aug = tobe_auged->GetAugment(in_augment->augment_index);
if (!old_aug || old_aug->GetItem()->AugDistiller != 0) { if (!old_aug || old_aug->GetItem()->AugDistiller != 0) {
LogError("Player tried to safely remove an augment without a distiller"); LogError("Player tried to safely remove an augment without a distiller");
Message(Chat::Red, "Error: Missing an augmentation distiller for safely removing this augment."); Message(Chat::Red, "Error: Missing an augmentation distiller for safely removing this augment.");
return; return;
} }
} } else if (solvent->GetItem()->ItemType == EQ::item::ItemTypeAugmentationDistiller) {
else if (solvent->GetItem()->ItemType == EQ::item::ItemTypeAugmentationDistiller)
{
old_aug = tobe_auged->GetAugment(in_augment->augment_index); old_aug = tobe_auged->GetAugment(in_augment->augment_index);
if (!old_aug) if (!old_aug) {
{
LogError("Player tried to safely remove a nonexistent augment"); LogError("Player tried to safely remove a nonexistent augment");
Message(Chat::Red, "Error: No augment found in slot %i for safely removing.", in_augment->augment_index); Message(Chat::Red, "Error: No augment found in slot %i for safely removing.", in_augment->augment_index);
return; return;
} } else if (solvent->GetItem()->ID != old_aug->GetItem()->AugDistiller) {
else if (solvent->GetItem()->ID != old_aug->GetItem()->AugDistiller)
{
LogError("Player tried to safely remove an augment with the wrong distiller (item [{}] vs expected [{}])", solvent->GetItem()->ID, old_aug->GetItem()->AugDistiller); LogError("Player tried to safely remove an augment with the wrong distiller (item [{}] vs expected [{}])", solvent->GetItem()->ID, old_aug->GetItem()->AugDistiller);
Message(Chat::Red, "Error: Wrong augmentation distiller for safely removing this augment."); Message(Chat::Red, "Error: Wrong augmentation distiller for safely removing this augment.");
return; return;
} }
} } else if (solvent->GetItem()->ItemType != EQ::item::ItemTypePerfectedAugmentationDistiller) {
else if (solvent->GetItem()->ItemType != EQ::item::ItemTypePerfectedAugmentationDistiller)
{
LogError("Player tried to safely remove an augment with a non-distiller item"); LogError("Player tried to safely remove an augment with a non-distiller item");
Message(Chat::Red, "Error: Invalid augmentation distiller for safely removing this augment."); Message(Chat::Red, "Error: Invalid augmentation distiller for safely removing this augment.");
return; return;
} }
} }
switch (in_augment->augment_action) switch (in_augment->augment_action) {
{ case AugmentActions::Insert:
case 0: // Adding an augment case AugmentActions::Swap:
case 2: // Swapping augment new_aug = user_inv.GetItem(EQ::invslot::slotCursor);
new_aug = user_inv.GetItem(EQ::invslot::slotCursor);
if (!new_aug) // Shouldn't get the OP code without the augment on the user's cursor, but maybe it's h4x. if (!new_aug) { // Shouldn't get the OP code without the augment on the user's cursor, but maybe it's h4x.
{ LogError("AugmentItem OpCode with 'Insert' or 'Swap' action received, but no augment on client's cursor");
LogError("AugmentItem OpCode with 'Insert' or 'Swap' action received, but no augment on client's cursor"); Message(Chat::Red, "Error: No augment found on cursor for inserting.");
Message(Chat::Red, "Error: No augment found on cursor for inserting."); return;
return; } else {
} if (!RuleB(Inventory, AllowMultipleOfSameAugment) && tobe_auged->ContainsAugmentByID(new_aug->GetID())) {
else Message(Chat::Red, "Error: Cannot put multiple of the same augment in an item.");
{
if (((tobe_auged->IsAugmentSlotAvailable(new_aug->GetAugmentType(), in_augment->augment_index)) != -1) &&
(tobe_auged->AvailableWearSlot(new_aug->GetItem()->Slots)))
{
old_aug = tobe_auged->RemoveAugment(in_augment->augment_index);
if (old_aug)
{
// An old augment was removed in order to be replaced with the new one (augment_action 2)
CalcBonuses();
std::vector<EQ::Any> args;
args.push_back(old_aug);
parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
args.assign(1, tobe_auged);
args.push_back(false);
parse->EventItem(EVENT_AUGMENT_REMOVE, this, old_aug, nullptr, "", in_augment->augment_index, &args);
}
tobe_auged->PutAugment(in_augment->augment_index, *new_aug);
tobe_auged->UpdateOrnamentationInfo();
aug = tobe_auged->GetAugment(in_augment->augment_index);
if (aug)
{
std::vector<EQ::Any> args;
args.push_back(aug);
parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
args.assign(1, tobe_auged);
parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args);
}
else
{
Message(Chat::Red, "Error: Could not properly insert augmentation into augment slot %i. Aborting.", in_augment->augment_index);
return; return;
} }
itemOneToPush = tobe_auged->Clone(); if (
if (old_aug) ((tobe_auged->IsAugmentSlotAvailable(new_aug->GetAugmentType(), in_augment->augment_index)) != -1) &&
{ tobe_auged->AvailableWearSlot(new_aug->GetItem()->Slots)
itemTwoToPush = old_aug->Clone(); ) {
} old_aug = tobe_auged->RemoveAugment(in_augment->augment_index);
if (old_aug) { // An old augment was removed in order to be replaced with the new one (augment_action 2)
// Must push items after the items in inventory are deleted - necessary due to lore items...
if (itemOneToPush)
{
DeleteItemInInventory(item_slot, 0, true);
DeleteItemInInventory(EQ::invslot::slotCursor, new_aug->IsStackable() ? 1 : 0, true);
if (solvent)
{
// Consume the augment distiller
DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true);
}
if (itemTwoToPush)
{
// This is a swap. Return the old aug to the player's cursor.
if (!PutItemInInventory(EQ::invslot::slotCursor, *itemTwoToPush, true))
{
LogError("Problem returning old augment to player's cursor after augmentation swap");
Message(Chat::Yellow, "Error: Failed to retrieve old augment after augmentation swap!");
}
}
if (PutItemInInventory(item_slot, *itemOneToPush, true))
{
// Successfully added an augment to the item
CalcBonuses(); CalcBonuses();
if (mat != EQ::textures::materialInvalid) std::vector<EQ::Any> args;
{ args.push_back(old_aug);
SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed. parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
args.assign(1, tobe_auged);
args.push_back(false);
parse->EventItem(EVENT_AUGMENT_REMOVE, this, old_aug, nullptr, "", in_augment->augment_index, &args);
}
tobe_auged->PutAugment(in_augment->augment_index, *new_aug);
tobe_auged->UpdateOrnamentationInfo();
aug = tobe_auged->GetAugment(in_augment->augment_index);
if (aug) {
std::vector<EQ::Any> args;
args.push_back(aug);
parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
args.assign(1, tobe_auged);
parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args);
} else {
Message(
Chat::Red,
fmt::format(
"Error: Could not properly insert augmentation into augment slot {}. Aborting.",
in_augment->augment_index
).c_str()
);
return;
}
item_one_to_push = tobe_auged->Clone();
if (old_aug) {
item_two_to_push = old_aug->Clone();
}
if (item_one_to_push) { // Must push items after the items in inventory are deleted - necessary due to lore items...
DeleteItemInInventory(item_slot, 0, true);
DeleteItemInInventory(EQ::invslot::slotCursor, new_aug->IsStackable() ? 1 : 0, true);
if (solvent) { // Consume the augment distiller
DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true);
} }
if (item_two_to_push) { // This is a swap. Return the old aug to the player's cursor.
if (!PutItemInInventory(EQ::invslot::slotCursor, *item_two_to_push, true)) {
LogError("Problem returning old augment to player's cursor after augmentation swap");
Message(Chat::Yellow, "Error: Failed to retrieve old augment after augmentation swap!");
}
}
if (PutItemInInventory(item_slot, *item_one_to_push, true)) { // Successfully added an augment to the item
CalcBonuses();
if (material != EQ::textures::materialInvalid) { // Visible item augged while equipped. Send WC in case ornamentation changed.
SendWearChange(material);
}
} else {
Message(Chat::Red, "Error: No available slot for end result. Please free up the augment slot.");
}
} else {
Message(Chat::Red, "Error in cloning item for augment. Aborted.");
} }
else } else {
{ Message(Chat::Red, "Error: No available slot for augment in that item.");
Message(Chat::Red, "Error: No available slot for end result. Please free up the augment slot.");
}
}
else
{
Message(Chat::Red, "Error in cloning item for augment. Aborted.");
} }
} }
else break;
{ case AugmentActions::Remove:
Message(Chat::Red, "Error: No available slot for augment in that item."); aug = tobe_auged->GetAugment(in_augment->augment_index);
if (aug) {
std::vector<EQ::Any> args;
args.push_back(aug);
parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
args.assign(1, tobe_auged);
args.push_back(false);
parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args);
} else {
Message(Chat::Red, "Error: Could not find augmentation to remove at index %i. Aborting.", in_augment->augment_index);
return;
} }
}
break;
case 1: // Removing augment safely (distiller)
aug = tobe_auged->GetAugment(in_augment->augment_index);
if (aug)
{
std::vector<EQ::Any> args;
args.push_back(aug);
parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
args.assign(1, tobe_auged); old_aug = tobe_auged->RemoveAugment(in_augment->augment_index);
tobe_auged->UpdateOrnamentationInfo();
args.push_back(false); item_one_to_push = tobe_auged->Clone();
if (old_aug) {
item_two_to_push = old_aug->Clone();
}
parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); if (item_one_to_push && item_two_to_push) {
} if (solvent) {
else DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true); // Consume the augment distiller
{ }
Message(Chat::Red, "Error: Could not find augmentation to remove at index %i. Aborting.", in_augment->augment_index);
return;
}
old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); DeleteItemInInventory(item_slot, 0, true); // Remove the augmented item
tobe_auged->UpdateOrnamentationInfo();
itemOneToPush = tobe_auged->Clone(); if (!PutItemInInventory(item_slot, *item_one_to_push, true)) { // Replace it with the unaugmented item
if (old_aug) LogError("Problem returning equipment item to player's inventory after safe augment removal");
itemTwoToPush = old_aug->Clone(); Message(Chat::Yellow, "Error: Failed to return item after de-augmentation!");
}
if (itemOneToPush && itemTwoToPush) CalcBonuses();
{
// Consume the augment distiller
if (solvent)
DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true);
// Remove the augmented item if (material != EQ::textures::materialInvalid) {
DeleteItemInInventory(item_slot, 0, true); SendWearChange(material); // Visible item augged while equipped. Send WC in case ornamentation changed.
}
// Replace it with the unaugmented item // Drop the removed augment on the player's cursor
if (!PutItemInInventory(item_slot, *itemOneToPush, true)) if (!PutItemInInventory(EQ::invslot::slotCursor, *item_two_to_push, true)) {
{ LogError("Problem returning augment to player's cursor after safe removal");
LogError("Problem returning equipment item to player's inventory after safe augment removal"); Message(Chat::Yellow, "Error: Failed to return augment after removal from item!");
Message(Chat::Yellow, "Error: Failed to return item after de-augmentation!"); return;
}
}
break;
case AugmentActions::Destroy:
// RoF client does not require an augmentation solvent for destroying an augmentation in an item.
// Augments can be destroyed with a right click -> Destroy at any time.
aug = tobe_auged->GetAugment(in_augment->augment_index);
if (aug) {
std::vector<EQ::Any> args;
args.push_back(aug);
parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
args.assign(1, tobe_auged);
args.push_back(true);
parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args);
} else {
Message(
Chat::Red,
fmt::format(
"Error: Could not find augmentation to remove at index {}. Aborting.",
in_augment->augment_index
).c_str()
);
return;
}
tobe_auged->DeleteAugment(in_augment->augment_index);
tobe_auged->UpdateOrnamentationInfo();
item_one_to_push = tobe_auged->Clone();
if (item_one_to_push) {
DeleteItemInInventory(item_slot, 0, true);
if (!PutItemInInventory(item_slot, *item_one_to_push, true)) {
LogError("Problem returning equipment item to player's inventory after augment deletion");
Message(Chat::Yellow, "Error: Failed to return item after destroying augment!");
}
} }
CalcBonuses(); CalcBonuses();
if (mat != EQ::textures::materialInvalid) if (material != EQ::textures::materialInvalid) {
{ SendWearChange(material);
SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed.
} }
break;
// Drop the removed augment on the player's cursor default: // Unknown
if (!PutItemInInventory(EQ::invslot::slotCursor, *itemTwoToPush, true)) LogInventory(
{ "Unrecognized augmentation action - cslot: [{}] aslot: [{}] cidx: [{}] aidx: [{}] act: [{}] dest: [{}]",
LogError("Problem returning augment to player's cursor after safe removal"); in_augment->container_slot,
Message(Chat::Yellow, "Error: Failed to return augment after removal from item!"); in_augment->augment_slot,
return; in_augment->container_index,
} in_augment->augment_index,
} in_augment->augment_action,
break; in_augment->dest_inst_id
case 3: // Destroying augment (formerly done in birdbath/sealer with a solvent) );
break;
// RoF client does not require an augmentation solvent for destroying an augmentation in an item.
// Augments can be destroyed with a right click -> Destroy at any time.
aug = tobe_auged->GetAugment(in_augment->augment_index);
if (aug)
{
std::vector<EQ::Any> args;
args.push_back(aug);
parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args);
args.assign(1, tobe_auged);
args.push_back(true);
parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args);
}
else
{
Message(Chat::Red, "Error: Could not find augmentation to remove at index %i. Aborting.");
return;
}
tobe_auged->DeleteAugment(in_augment->augment_index);
tobe_auged->UpdateOrnamentationInfo();
itemOneToPush = tobe_auged->Clone();
if (itemOneToPush)
{
DeleteItemInInventory(item_slot, 0, true);
if (!PutItemInInventory(item_slot, *itemOneToPush, true))
{
LogError("Problem returning equipment item to player's inventory after augment deletion");
Message(Chat::Yellow, "Error: Failed to return item after destroying augment!");
}
}
CalcBonuses();
if (mat != EQ::textures::materialInvalid)
{
SendWearChange(mat);
}
break;
default: // Unknown
LogInventory("Unrecognized augmentation action - cslot: [{}] aslot: [{}] cidx: [{}] aidx: [{}] act: [{}] dest: [{}]",
in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id);
break;
} }
} } else {
else Object::HandleAugmentation(this, in_augment, m_tradeskill_object); // Delegate to tradeskill object to perform combine
{
// Delegate to tradeskill object to perform combine
Object::HandleAugmentation(this, in_augment, m_tradeskill_object);
} }
return; return;
} }

View File

@ -43,109 +43,93 @@ static const EQ::skills::SkillType TradeskillUnknown = EQ::skills::Skill1HBlunt;
void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augment, Object *worldo) void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augment, Object *worldo)
{ {
if (!user || !in_augment) if (!user || !in_augment) {
{
LogError("Client or AugmentItem_Struct not set in Object::HandleAugmentation"); LogError("Client or AugmentItem_Struct not set in Object::HandleAugmentation");
return; return;
} }
EQ::ItemInstance* container = nullptr; EQ::ItemInstance* container = nullptr;
if (worldo) if (worldo) {
{
container = worldo->m_inst; container = worldo->m_inst;
} } else { // Check to see if they have an inventory container type 53 that is used for this.
else
{
// Check to see if they have an inventory container type 53 that is used for this.
EQ::InventoryProfile& user_inv = user->GetInv(); EQ::InventoryProfile& user_inv = user->GetInv();
EQ::ItemInstance* inst = nullptr; EQ::ItemInstance* inst = nullptr;
inst = user_inv.GetItem(in_augment->container_slot); inst = user_inv.GetItem(in_augment->container_slot);
if (inst) if (inst) {
{
const EQ::ItemData* item = inst->GetItem(); const EQ::ItemData* item = inst->GetItem();
if (item && inst->IsType(EQ::item::ItemClassBag) && item->BagType == 53) if (item && inst->IsType(EQ::item::ItemClassBag) && item->BagType == EQ::item::BagTypeAugmentationSealer) { // We have found an appropriate inventory augmentation sealer
{
// We have found an appropriate inventory augmentation sealer
container = inst; container = inst;
// Verify that no more than two items are in container to guarantee no inadvertant wipes. // Verify that no more than two items are in container to guarantee no inadvertant wipes.
uint8 itemsFound = 0; uint8 items_found = 0;
for (uint8 i = EQ::invbag::SLOT_BEGIN; i < EQ::invtype::WORLD_SIZE; i++) for (uint8 i = EQ::invbag::SLOT_BEGIN; i < EQ::invtype::WORLD_SIZE; i++) {
{
const EQ::ItemInstance* inst = container->GetItem(i); const EQ::ItemInstance* inst = container->GetItem(i);
if (inst) if (inst) {
{ items_found++;
itemsFound++;
} }
} }
if (itemsFound != 2) if (items_found < 2) {
{ user->Message(Chat::Red, "Error: Too few items in augmentation container.");
user->Message(Chat::Red, "Error: Too many/few items in augmentation container."); return;
} else if (items_found > 2) {
user->Message(Chat::Red, "Error: Too many items in augmentation container.");
return; return;
} }
} }
} }
} }
if(!container) if(!container) {
{
LogError("Player tried to augment an item without a container set"); LogError("Player tried to augment an item without a container set");
user->Message(Chat::Red, "Error: This item is not a container!"); user->Message(Chat::Red, "Error: This item is not a container!");
return; return;
} }
EQ::ItemInstance *tobe_auged = nullptr, *auged_with = nullptr; EQ::ItemInstance *tobe_auged = nullptr, *auged_with = nullptr;
int8 slot=-1; int8 slot = -1;
// Verify 2 items in the augmentation device if (container->GetItem(0) && container->GetItem(1)) { // Verify 2 items in the augmentation device
if (container->GetItem(0) && container->GetItem(1))
{
// Verify 1 item is augmentable and the other is not // Verify 1 item is augmentable and the other is not
if (container->GetItem(0)->IsAugmentable() && !container->GetItem(1)->IsAugmentable()) if (container->GetItem(0)->IsAugmentable() && !container->GetItem(1)->IsAugmentable()) {
{
tobe_auged = container->GetItem(0); tobe_auged = container->GetItem(0);
auged_with = container->GetItem(1); auged_with = container->GetItem(1);
} } else if (!container->GetItem(0)->IsAugmentable() && container->GetItem(1)->IsAugmentable()) {
else if (!container->GetItem(0)->IsAugmentable() && container->GetItem(1)->IsAugmentable())
{
tobe_auged = container->GetItem(1); tobe_auged = container->GetItem(1);
auged_with = container->GetItem(0); auged_with = container->GetItem(0);
} } else {
else
{
// Either 2 augmentable items found or none found // Either 2 augmentable items found or none found
// This should never occur due to client restrictions, but prevent in case of a hack // This should never occur due to client restrictions, but prevent in case of a hack
user->Message(Chat::Red, "Error: Must be 1 augmentable item in the sealer"); user->Message(Chat::Red, "Error: There must be 1 augmentable item in the sealer.");
return; return;
} }
} } else { // This happens if the augment button is clicked more than once quickly while augmenting
else if (!container->GetItem(0)) {
{ user->Message(Chat::Red, "Error: No item in the first slot of sealer.");
// This happens if the augment button is clicked more than once quickly while augmenting
if (!container->GetItem(0))
{
user->Message(Chat::Red, "Error: No item in slot 0 of sealer");
} }
if (!container->GetItem(1))
{ if (!container->GetItem(1)) {
user->Message(Chat::Red, "Error: No item in slot 1 of sealer"); user->Message(Chat::Red, "Error: No item in the second slot of sealer.");
} }
return; return;
} }
bool deleteItems = false; if (!RuleB(Inventory, AllowMultipleOfSameAugment) && tobe_auged->ContainsAugmentByID(auged_with->GetID())) {
user->Message(Chat::Red, "Error: Cannot put multiple of the same augment in an item.");
return;
}
EQ::ItemInstance *itemOneToPush = nullptr, *itemTwoToPush = nullptr; bool delete_items = false;
// Adding augment EQ::ItemInstance *item_one_to_push = nullptr, *item_two_to_push = nullptr;
if (in_augment->augment_slot == -1)
{ if (in_augment->augment_slot == -1) { // Adding augment
if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) && if (
(tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) ((slot = tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType())) != -1) &&
{ tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots)
) {
tobe_auged->PutAugment(slot, *auged_with); tobe_auged->PutAugment(slot, *auged_with);
EQ::ItemInstance *aug = tobe_auged->GetAugment(slot); EQ::ItemInstance *aug = tobe_auged->GetAugment(slot);
@ -158,20 +142,15 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme
parse->EventItem(EVENT_AUGMENT_INSERT, user, aug, nullptr, "", slot, &args); parse->EventItem(EVENT_AUGMENT_INSERT, user, aug, nullptr, "", slot, &args);
} }
itemOneToPush = tobe_auged->Clone(); item_one_to_push = tobe_auged->Clone();
deleteItems = true; delete_items = true;
} else {
user->Message(Chat::Red, "Error: No available slot for augment.");
} }
else } else {
{
user->Message(Chat::Red, "Error: No available slot for augment");
}
}
else
{
EQ::ItemInstance *old_aug = nullptr; EQ::ItemInstance *old_aug = nullptr;
bool isSolvent = auged_with->GetItem()->ItemType == EQ::item::ItemTypeAugmentationSolvent; bool is_solvent = auged_with->GetItem()->ItemType == EQ::item::ItemTypeAugmentationSolvent;
if (!isSolvent && auged_with->GetItem()->ItemType != EQ::item::ItemTypeAugmentationDistiller) if (!is_solvent && auged_with->GetItem()->ItemType != EQ::item::ItemTypeAugmentationDistiller) {
{
LogError("Player tried to remove an augment without a solvent or distiller"); LogError("Player tried to remove an augment without a solvent or distiller");
user->Message(Chat::Red, "Error: Missing an augmentation solvent or distiller for removing this augment."); user->Message(Chat::Red, "Error: Missing an augmentation solvent or distiller for removing this augment.");
@ -180,8 +159,7 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme
EQ::ItemInstance *aug = tobe_auged->GetAugment(in_augment->augment_slot); EQ::ItemInstance *aug = tobe_auged->GetAugment(in_augment->augment_slot);
if (aug) { if (aug) {
if (!isSolvent && auged_with->GetItem()->ID != aug->GetItem()->AugDistiller) if (!is_solvent && auged_with->GetItem()->ID != aug->GetItem()->AugDistiller) {
{
LogError("Player tried to safely remove an augment with the wrong distiller (item [{}] vs expected [{}])", auged_with->GetItem()->ID, aug->GetItem()->AugDistiller); LogError("Player tried to safely remove an augment with the wrong distiller (item [{}] vs expected [{}])", auged_with->GetItem()->ID, aug->GetItem()->AugDistiller);
user->Message(Chat::Red, "Error: Wrong augmentation distiller for safely removing this augment."); user->Message(Chat::Red, "Error: Wrong augmentation distiller for safely removing this augment.");
return; return;
@ -191,29 +169,27 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme
parse->EventItem(EVENT_UNAUGMENT_ITEM, user, tobe_auged, nullptr, "", slot, &args); parse->EventItem(EVENT_UNAUGMENT_ITEM, user, tobe_auged, nullptr, "", slot, &args);
args.assign(1, tobe_auged); args.assign(1, tobe_auged);
args.push_back(&isSolvent); args.push_back(&is_solvent);
parse->EventItem(EVENT_AUGMENT_REMOVE, user, aug, nullptr, "", slot, &args); parse->EventItem(EVENT_AUGMENT_REMOVE, user, aug, nullptr, "", slot, &args);
} }
if (isSolvent) if (is_solvent) {
tobe_auged->DeleteAugment(in_augment->augment_slot); tobe_auged->DeleteAugment(in_augment->augment_slot);
else } else {
old_aug = tobe_auged->RemoveAugment(in_augment->augment_slot); old_aug = tobe_auged->RemoveAugment(in_augment->augment_slot);
}
itemOneToPush = tobe_auged->Clone(); item_one_to_push = tobe_auged->Clone();
if (old_aug) if (old_aug) {
itemTwoToPush = old_aug->Clone(); item_two_to_push = old_aug->Clone();
}
delete_items = true;
deleteItems = true;
} }
if (deleteItems) if (delete_items) {
{ if (worldo) {
if (worldo)
{
container->Clear(); container->Clear();
auto outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct)); auto outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct));
ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer; ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer;
@ -221,34 +197,26 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme
user->QueuePacket(outapp); user->QueuePacket(outapp);
safe_delete(outapp); safe_delete(outapp);
database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID()); database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID());
} } else { // Delete items in our inventory container...
else for (uint8 i = EQ::invbag::SLOT_BEGIN; i < EQ::invtype::WORLD_SIZE; i++) {
{
// Delete items in our inventory container...
for (uint8 i = EQ::invbag::SLOT_BEGIN; i < EQ::invtype::WORLD_SIZE; i++)
{
const EQ::ItemInstance* inst = container->GetItem(i); const EQ::ItemInstance* inst = container->GetItem(i);
if (inst) if (inst) {
{
user->DeleteItemInInventory(EQ::InventoryProfile::CalcSlotId(in_augment->container_slot, i), 0, true); user->DeleteItemInInventory(EQ::InventoryProfile::CalcSlotId(in_augment->container_slot, i), 0, true);
} }
} }
// Explicitly mark container as cleared.
container->Clear(); container->Clear(); // Explicitly mark container as cleared.
} }
} }
// Must push items after the items in inventory are deleted - necessary due to lore items... // Must push items after the items in inventory are deleted - necessary due to lore items...
if (itemOneToPush) if (item_one_to_push) {
{ user->PushItemOnCursor(*item_one_to_push, true);
user->PushItemOnCursor(*itemOneToPush, true);
} }
if (itemTwoToPush) if (item_two_to_push) {
{ user->PushItemOnCursor(*item_two_to_push, true);
user->PushItemOnCursor(*itemTwoToPush, true);
} }
} }
// Perform tradeskill combine // Perform tradeskill combine