Index: src/game/Bag.cpp =================================================================== --- src/game/Bag.cpp (revision 3677) +++ src/game/Bag.cpp (working copy) @@ -219,6 +219,17 @@ if( pItem && pItem != eItem && pItem->GetEntry() == item ) count += pItem->GetCount(); } + + if(eItem && eItem->GetProto()->GemProperties) + { + for(uint32 i=0; i < ContainerSlots; i++) + { + pItem = m_bagslot[i]; + if( pItem && pItem != eItem && pItem->GetProto()->Socket[0].Color ) + count += pItem->GetGemCountWithID(item); + } + } + return count; } Index: src/game/Item.cpp =================================================================== --- src/game/Item.cpp (revision 3677) +++ src/game/Item.cpp (working copy) @@ -168,6 +168,7 @@ m_container = NULL; m_lootGenerated = false; mb_in_trade = false; + forcenextenchant = false; } bool Item::Create( uint32 guidlow, uint32 itemid, Player* owner) @@ -964,3 +965,62 @@ SetUInt32Value(ITEM_FIELD_ENCHANTMENT+slot*3+x,0); SetState(ITEM_CHANGED); } + +bool Item::GemsFitSockets() const +{ + bool fits = true; + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + { + uint8 SocketColor = GetProto()->Socket[enchant_slot-SOCK_ENCHANTMENT_SLOT].Color; + + uint32 enchant_id = GetEchantmentId(EnchantmentSlot(enchant_slot)); + if(!enchant_id) + { + if(SocketColor) fits &= false; + continue; + } + + SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!enchantEntry) + { + if(SocketColor) fits &= false; + continue; + } + + uint8 GemColor = 0; + + uint32 gemid = enchantEntry->GemID; + if(gemid) + { + ItemPrototype const* gemProto = sItemStorage.LookupEntry(gemid); + if(gemProto) + { + GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties); + if(gemProperty) + GemColor = gemProperty->color; + } + } + + fits &= (GemColor & SocketColor) ? true : false; + } + return fits; +} + +uint8 Item::GetGemCountWithID(uint32 GemID) const +{ + uint8 count = 0; + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + { + uint32 enchant_id = GetEchantmentId(EnchantmentSlot(enchant_slot)); + if(!enchant_id) + continue; + + SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!enchantEntry) + continue; + + if(GemID == enchantEntry->GemID) + count++; + } + return count; +} Index: src/game/Item.h =================================================================== --- src/game/Item.h (revision 3677) +++ src/game/Item.h (working copy) @@ -197,11 +197,13 @@ bool IsInTrade() const { return mb_in_trade; } bool IsFitToSpellRequirements(SpellEntry const* spellInfo) const; + bool GemsFitSockets() const; uint32 GetEntry() const { return GetUInt32Value(OBJECT_FIELD_ENTRY); } uint32 GetCount() const { return GetUInt32Value (ITEM_FIELD_STACK_COUNT); } void SetCount(uint32 value) { SetUInt32Value (ITEM_FIELD_STACK_COUNT, value); } uint32 GetMaxStackCount() const { return GetProto()->Stackable ? GetProto()->Stackable : 1; } + uint8 GetGemCountWithID(uint32 GemID) const; uint8 GetSlot() const {return m_slot;} Bag *GetContainer() { return m_container; } @@ -243,6 +245,8 @@ uState = state; } + bool forcenextenchant; + bool hasQuest(uint32 quest_id) const { ItemPrototype const *itemProto = GetProto(); Index: src/game/ItemHandler.cpp =================================================================== --- src/game/ItemHandler.cpp (revision 3677) +++ src/game/ItemHandler.cpp (working copy) @@ -266,7 +266,8 @@ data << uint32(0); if (pItem) { - if ((s < 3) && (pItem->GetUInt32Value(ITEM_FIELD_SOCKETS_ENCHANTMENT+s*3))) + // SOCK_ENCHANTMENT_SLOT.. SOCK_ENCHANTMENT_SLOT+2 is gem sockets + if ((s < 3) && (pItem->GetEchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+ s)))) { data << uint32(1); } @@ -907,63 +908,86 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data) { - sLog.outDebug("Received opcode CMSG_SOCKET_ITEM"); - uint64 itemguid; - uint64 gemguid[3]; - uint32 gemProperty[3]; + sLog.outDebug("WORLD: CMSG_SOCKET_ITEM"); - recv_data >> itemguid >> gemguid[0] >> gemguid[1] >> gemguid[2]; + CHECK_PACKET_SIZE(recv_data,8*4); - bool propercolor[3], full[3]; - Item *pItem = _player->GetItemByPos(_player->GetPosByGuid(itemguid)); - if(pItem) + uint64 guids[4]; + uint32 GemEnchants[3], OldEnchants[3]; + Item *Gems[3]; + bool SocketBonusActivated, SocketBonusToBeActivated; + + + for(int i = 0; i < 4; i++) + recv_data >> guids[i]; + + //cheat -> tried to socket same gem multiple times + if((guids[1] && (guids[1] == guids[2] || guids[1] == guids[3])) || (guids[2] && (guids[2] == guids[3]))) + return; + + uint16 pos = _player->GetPosByGuid(guids[0]); + Item *itemTarget = _player->GetItemByPos(pos); + if(!itemTarget) //missing item to socket + return; + + uint16 slot = pos & 255; //this slot is excepted when applying / removing meta gem bonus + + for(int i = 0; i < 3; i++) { - bool HasBonus = 1; - for(int s = 0; s < 3; s++) - { - if(gemguid[s]) - { - Item *pGem = _player->GetItemByPos(_player->GetPosByGuid(gemguid[s])); - if(pGem) - { - gemProperty[s] = pGem->GetProto()->GemProperties; - GemPropertiesEntry const *gemInfo = sGemPropertiesStore.LookupEntry(gemProperty[s]); - if(gemInfo) - { - propercolor[s] = (gemInfo->color & pItem->GetProto()->Socket[s].Color); - if ((gemInfo->color & SOCKET_COLOR_META) & !propercolor[s]) - { - // ToDo : Error message - continue; - } - pItem->SetUInt32Value(ITEM_FIELD_SOCKETS_ENCHANTMENT+s*3, gemInfo->spellitemenchantement); - _player->DestroyItem(pGem->GetBagSlot(), pGem->GetSlot(), true); - full[s] = 1; - } - } - } - else - { - gemProperty[s] = 0; - propercolor[s] = 1; - if(pItem->GetProto()->Socket[s].Color) - { - full[s]=0; - } - else - { - full[s]=1; - } - HasBonus &= full[s] & propercolor[s]; - } + pos = _player->GetPosByGuid(guids[i + 1]); + Gems[i] = _player->GetItemByPos(pos); + } - // Now if all full[s] are true and all propercolor[s] are true, also apply the bonus pItemProto->socketBonus - if(HasBonus) - { - pItem->SetUInt32Value(ITEM_FIELD_SOCKETS_ENCHANTMENT+3*3, pItem->GetProto()->socketBonus); - } + GemPropertiesEntry const *GemProps[3]; + for(int i = 0; i < 3; ++i) //get geminfo from dbc storage + { + GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetProto()->GemProperties) : NULL; + } - _player->_ApplyItemMods(pItem, pItem->GetSlot(), true); + for(int i = 0; i < 3; ++i) //check for hack maybe + { + // tried to put gem in socket where no socket exists / tried to put normal gem in meta socket + // tried to put meta gem in normal socket + if( GemProps[i] && ( (!itemTarget->GetProto()->Socket[i].Color) || (itemTarget->GetProto()->Socket[i].Color == 1 && GemProps[i]->color != 1) || (itemTarget->GetProto()->Socket[i].Color != 1 && GemProps[i]->color == 1))) + return; + } + + for(int i = 0; i < 3; ++i) //get new and old enchantments + { + GemEnchants[i] = (GemProps[i]) ? GemProps[i]->spellitemenchantement : 0; + OldEnchants[i] = itemTarget->GetEchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i)); + } + + SocketBonusActivated = itemTarget->GemsFitSockets(); //save state of socketbonus + _player->ToggleMetaGemsActive(slot, false); //turn off all metagems (except for the target item) + + //if a meta gem is being equipped, all information has to be written to the item before testing if the conditions for the gem are met + + //remove ALL enchants + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),false); + + for(int i = 0; i < 3; ++i) + { + if(GemEnchants[i]) + { + itemTarget->SetEchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i), GemEnchants[i],0,0); + pos = _player->GetPosByGuid(guids[i + 1]); + _player->DestroyItem((pos >> 8), (pos & 255), true ); } } + + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + _player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),true); + + SocketBonusToBeActivated = itemTarget->GemsFitSockets(); //current socketbonus state + if(SocketBonusActivated ^ SocketBonusToBeActivated) //if there was a change... + { + _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,false); + itemTarget->SetEchantment(BONUS_ENCHANTMENT_SLOT, (SocketBonusToBeActivated ? itemTarget->GetProto()->socketBonus : 0), 0, 0); + _player->ApplyEnchantment(itemTarget,BONUS_ENCHANTMENT_SLOT,true); + //it is not displayed, client has an inbuilt system to determine if the bonus is activated + } + + _player->ToggleMetaGemsActive(slot, true); //turn on all metagems (except for target item) } Index: src/game/Player.cpp =================================================================== --- src/game/Player.cpp (revision 3677) +++ src/game/Player.cpp (working copy) @@ -4974,6 +4974,9 @@ ApplyEnchantment(item, apply); + if(proto->Socket[0].Color) //only (un)equipping of items with sockets can influence metagems, so no need to waste time with normal items + CorrectMetaGemEnchants(slot, apply); + sLog.outDebug("_ApplyItemMods complete."); } @@ -6454,6 +6457,16 @@ count += pBag->GetItemCount(item,eItem); } + if(eItem && eItem->GetProto()->GemProperties) + { + for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++) + { + pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + if( pItem && pItem != eItem && pItem->GetProto()->Socket[0].Color ) + count += pItem->GetGemCountWithID(item); + } + } + return count; } @@ -6474,6 +6487,17 @@ if( pBag ) count += pBag->GetItemCount(item,eItem); } + + if(eItem && eItem->GetProto()->GemProperties) + { + for(int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++) + { + pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); + if( pItem && pItem != eItem && pItem->GetProto()->Socket[0].Color ) + count += pItem->GetGemCountWithID(item); + } + } + return count; } @@ -8923,6 +8947,11 @@ if(!pEnchant) return; + if(pEnchant->EnchantmentCondition && !item->forcenextenchant && !((Player*)this)->EnchantmentFitsRequirements(pEnchant->EnchantmentCondition, -1)) + return; + + item->forcenextenchant = false; + for (int s=0; s<3; s++) { uint32 enchant_display_type = pEnchant->display_type[s]; @@ -12942,6 +12971,162 @@ return m_AuraModifiers[SPELL_AURA_MOD_SHIELD_BLOCKVALUE]*(m_AuraModifiers[SPELL_AURA_MOD_SHIELD_BLOCKVALUE_PCT]+100)/100; } +bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot) //slot to be excluded while counting +{ + if(!enchantmentcondition) + return true; + + SpellItemEnchantmentConditionEntry const *Condition = sSpellItemEnchantmentConditionStore.LookupEntry(enchantmentcondition); + + if(!Condition) + return true; + + uint8 curcount[4] = {0, 0, 0, 0}; + + //counting current equipped gem colors + for(uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) + { + if(i == slot) + continue; + uint16 pos = ((INVENTORY_SLOT_BAG_0 << 8) | i ); + Item *pItem2 = GetItemByPos( pos ); + if(pItem2 && pItem2->GetProto()->Socket[0].Color) + { + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + { + uint32 enchant_id = pItem2->GetEchantmentId(EnchantmentSlot(enchant_slot)); + if(!enchant_id) + continue; + + SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!enchantEntry) + continue; + + uint32 gemid = enchantEntry->GemID; + if(!gemid) + continue; + + ItemPrototype const* gemProto = sItemStorage.LookupEntry(gemid); + if(!gemProto) + continue; + + GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties); + if(!gemProperty) + continue; + + uint8 GemColor = gemProperty->color; + + for(uint8 b = 0, tmpcolormask = 1; b < 4; b++, tmpcolormask <<= 1) + { + if(tmpcolormask & GemColor) + curcount[b]++; + } + } + } + } + + bool activate = true; + + for(int i = 0; i < 5; i++) + { + if(!Condition->Color[i]) + continue; + + uint32 _cur_gem = curcount[Condition->Color[i] - 1]; + + // if have use them as count, else use from Condition + uint32 _cmp_gem = Condition->CompareColor[i] ? curcount[Condition->CompareColor[i] - 1]: Condition->Value[i]; + + switch(Condition->Comparator[i]) { + case 2: // requires less than ( || ) gems + activate &= (_cur_gem < _cmp_gem) ? true : false; + break; + case 3: // requires more than ( || ) gems + activate &= (_cur_gem > _cmp_gem) ? true : false; + break; + case 5: // requires at least than ( || ) gems + activate &= (_cur_gem >= _cmp_gem) ? true : false; + break; + } + } + + sLog.outDebug("Checking Condition %u, there are %u Meta Gems, %u Red Gems, %u Yellow Gems and %u Blue Gems, Activate:%s", enchantmentcondition, curcount[0], curcount[1], curcount[2], curcount[3], activate ? "yes" : "no"); + + return activate; +} + + +void Player::CorrectMetaGemEnchants(uint8 exceptslot, bool apply) +{ + + for(uint32 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) //cycle all equipped items + { + //enchants for the slot being socketed are handeled by Player::ApplyItemMods + if(slot == exceptslot) + continue; + + uint16 pos = ( (INVENTORY_SLOT_BAG_0 << 8) | slot ); + Item* pItem = GetItemByPos( pos ); + + if(!pItem || !pItem->GetProto()->Socket[0].Color) + continue; + + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + { + uint32 enchant_id = pItem->GetEchantmentId(EnchantmentSlot(enchant_slot)); + if(!enchant_id) + continue; + + SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!enchantEntry) + continue; + + uint32 condition = enchantEntry->EnchantmentCondition; + if(condition) + { + bool wasactive = EnchantmentFitsRequirements(condition, apply ? exceptslot : -1); //was enchant active with/without item? + if(wasactive ^ EnchantmentFitsRequirements(condition, apply ? -1 : exceptslot)) //should it now be? + { + pItem->forcenextenchant = true; + ApplyEnchantment(pItem,EnchantmentSlot(enchant_slot),!wasactive); //if state changed, (dis)apply enchant + } + } + } + } +} + +void Player::ToggleMetaGemsActive(uint16 exceptslot, bool apply) //if false -> then toggled off if was on| if true -> toggled on if was off AND meets requirements +{ + for(int slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++) //cycle all equipped items + { + //enchants for the slot being socketed are handeled by WorldSession::HandleSocketOpcode(WorldPacket& recv_data) + if(slot == exceptslot) continue; + + uint16 pos = ( (INVENTORY_SLOT_BAG_0 << 8) | slot ); + Item *pItem = GetItemByPos( pos ); + + if(!pItem || !pItem->GetProto()->Socket[0].Color) //if item has no sockets or no item is equipped go to next item + continue; + + //cycle all (gem)enchants + for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot) + { + uint32 enchant_id = pItem->GetEchantmentId(EnchantmentSlot(enchant_slot)); + if(!enchant_id) //if no enchant go to next enchant(slot) + continue; + + SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id); + if(!enchantEntry) + continue; + + //only metagems to be (de)activated, so only enchants with condition + uint32 condition = enchantEntry->EnchantmentCondition; + if(condition) + ApplyEnchantment(pItem,EnchantmentSlot(enchant_slot), apply); + } + } +} + bool Player::DropBattleGroundFlag() { if(InBattleGround()) Index: src/game/Player.h =================================================================== --- src/game/Player.h (revision 3677) +++ src/game/Player.h (working copy) @@ -1182,6 +1182,9 @@ void _ApplyAllItemMods(); void _ApplyItemBonuses(ItemPrototype const *proto,uint8 slot,bool apply); void _ApplyAmmoBonuses(bool apply); + bool EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot); + void ToggleMetaGemsActive(uint16 exceptslot, bool apply); + void CorrectMetaGemEnchants(uint8 slot, bool apply); void InitDataForForm(); void CastItemEquipSpell(Item *item); Index: src/game/UpdateFields.h =================================================================== --- src/game/UpdateFields.h (revision 3677) +++ src/game/UpdateFields.h (working copy) @@ -46,7 +46,6 @@ ITEM_FIELD_SPELL_CHARGES_04 = OBJECT_END + 0x0E, ITEM_FIELD_FLAGS = OBJECT_END + 0x0F, // Size:1 ITEM_FIELD_ENCHANTMENT = OBJECT_END + 0x10, // Size: 33 = (temp+perm+sockets*3+bonus+temp2*2+property*3)*3 - ITEM_FIELD_SOCKETS_ENCHANTMENT = OBJECT_END + 0x16, // include (3 slots) * 3 = 9 ITEM_FIELD_PROPERTY_SEED = OBJECT_END + 0x31, // Size:1 ITEM_FIELD_RANDOM_PROPERTIES_ID = OBJECT_END + 0x32, // Size:1 ITEM_FIELD_ITEM_TEXT_ID = OBJECT_END + 0x33, // Size:1