[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
6 changed files with 280 additions and 331 deletions
+184 -231
View File
@@ -2929,18 +2929,16 @@ void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app)
void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
{
if (app->size != sizeof(AugmentItem_Struct)) {
LogError("Invalid size for AugmentItem_Struct: Expected: [{}], Got: [{}]",
sizeof(AugmentItem_Struct), app->size);
LogError("Invalid size for AugmentItem_Struct: Expected: [{}], Got: [{}]", sizeof(AugmentItem_Struct), app->size);
return;
}
AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer;
bool deleteItems = false;
if (ClientVersion() >= EQ::versions::ClientVersion::RoF)
{
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))
{
if (ClientVersion() >= EQ::versions::ClientVersion::RoF) {
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)
) {
Message(Chat::Red, "The server does not allow augmentation actions from this slot.");
auto cursor_item = m_inv[EQ::invslot::slotCursor];
auto augmented_item = m_inv[in_augment->container_slot];
@@ -2950,20 +2948,16 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
return;
}
EQ::ItemInstance *itemOneToPush = nullptr, *itemTwoToPush = 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 *item_one_to_push = nullptr, *item_two_to_push = nullptr;
EQ::ItemInstance *tobe_auged = nullptr, *old_aug = nullptr, *new_aug = nullptr, *aug = nullptr, *solvent = nullptr;
EQ::InventoryProfile& user_inv = GetInv();
uint16 item_slot = in_augment->container_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.");
return;
}
@@ -2971,272 +2965,231 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
tobe_auged = user_inv.GetItem(item_slot);
solvent = user_inv.GetItem(solvent_slot);
if (!tobe_auged)
{
if (!tobe_auged) {
Message(Chat::Red, "Error: Invalid item passed for augmenting.");
return;
}
if ((in_augment->augment_action == 1) || (in_augment->augment_action == 2))
{
// Check for valid distiller if safely removing / swapping an augmentation
if (!solvent)
{
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
old_aug = tobe_auged->GetAugment(in_augment->augment_index);
if (!old_aug || old_aug->GetItem()->AugDistiller != 0) {
LogError("Player tried to safely remove an augment without a distiller");
Message(Chat::Red, "Error: Missing an augmentation distiller for safely removing this augment.");
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);
if (!old_aug)
{
if (!old_aug) {
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);
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);
Message(Chat::Red, "Error: Wrong augmentation distiller for safely removing this augment.");
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");
Message(Chat::Red, "Error: Invalid augmentation distiller for safely removing this augment.");
return;
}
}
switch (in_augment->augment_action)
{
case 0: // Adding an augment
case 2: // Swapping augment
new_aug = user_inv.GetItem(EQ::invslot::slotCursor);
switch (in_augment->augment_action) {
case AugmentActions::Insert:
case AugmentActions::Swap:
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.
{
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.");
return;
}
else
{
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);
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");
Message(Chat::Red, "Error: No augment found on cursor for inserting.");
return;
} else {
if (!RuleB(Inventory, AllowMultipleOfSameAugment) && tobe_auged->ContainsAugmentByID(new_aug->GetID())) {
Message(Chat::Red, "Error: Cannot put multiple of the same augment in an item.");
return;
}
itemOneToPush = tobe_auged->Clone();
if (old_aug)
{
itemTwoToPush = old_aug->Clone();
}
// 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
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();
if (mat != EQ::textures::materialInvalid)
{
SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed.
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,
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
{
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 {
Message(Chat::Red, "Error: No available slot for augment in that item.");
}
}
else
{
Message(Chat::Red, "Error: No available slot for augment in that item.");
break;
case AugmentActions::Remove:
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);
}
else
{
Message(Chat::Red, "Error: Could not find augmentation to remove at index %i. Aborting.", in_augment->augment_index);
return;
}
if (item_one_to_push && item_two_to_push) {
if (solvent) {
DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true); // Consume the augment distiller
}
old_aug = tobe_auged->RemoveAugment(in_augment->augment_index);
tobe_auged->UpdateOrnamentationInfo();
DeleteItemInInventory(item_slot, 0, true); // Remove the augmented item
itemOneToPush = tobe_auged->Clone();
if (old_aug)
itemTwoToPush = old_aug->Clone();
if (!PutItemInInventory(item_slot, *item_one_to_push, true)) { // Replace it with the unaugmented item
LogError("Problem returning equipment item to player's inventory after safe augment removal");
Message(Chat::Yellow, "Error: Failed to return item after de-augmentation!");
}
if (itemOneToPush && itemTwoToPush)
{
// Consume the augment distiller
if (solvent)
DeleteItemInInventory(solvent_slot, solvent->IsStackable() ? 1 : 0, true);
CalcBonuses();
// Remove the augmented item
DeleteItemInInventory(item_slot, 0, true);
if (material != EQ::textures::materialInvalid) {
SendWearChange(material); // Visible item augged while equipped. Send WC in case ornamentation changed.
}
// Replace it with the unaugmented item
if (!PutItemInInventory(item_slot, *itemOneToPush, true))
{
LogError("Problem returning equipment item to player's inventory after safe augment removal");
Message(Chat::Yellow, "Error: Failed to return item after de-augmentation!");
// Drop the removed augment on the player's cursor
if (!PutItemInInventory(EQ::invslot::slotCursor, *item_two_to_push, true)) {
LogError("Problem returning augment to player's cursor after safe removal");
Message(Chat::Yellow, "Error: Failed to return augment after removal from item!");
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();
if (mat != EQ::textures::materialInvalid)
{
SendWearChange(mat); // Visible item augged while equipped. Send WC in case ornamentation changed.
if (material != EQ::textures::materialInvalid) {
SendWearChange(material);
}
// Drop the removed augment on the player's cursor
if (!PutItemInInventory(EQ::invslot::slotCursor, *itemTwoToPush, true))
{
LogError("Problem returning augment to player's cursor after safe removal");
Message(Chat::Yellow, "Error: Failed to return augment after removal from item!");
return;
}
}
break;
case 3: // Destroying augment (formerly done in birdbath/sealer with a solvent)
// 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;
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
{
// Delegate to tradeskill object to perform combine
Object::HandleAugmentation(this, in_augment, m_tradeskill_object);
} else {
Object::HandleAugmentation(this, in_augment, m_tradeskill_object); // Delegate to tradeskill object to perform combine
}
return;
}