diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 161fca4..8c68d8a 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -12486,12 +12486,13 @@ void Player::QuickEquipItem(uint16 pos, Item* pItem) } } +extern uint32 GetItemEnchantVisual(Player* player, Item* item); void Player::SetVisibleItemSlot(uint8 slot, Item* pItem) { if (pItem) { SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2), pItem->GetEntry()); - SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 0, pItem->GetEnchantmentId(PERM_ENCHANTMENT_SLOT)); + SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 0, GetItemEnchantVisual(this, pItem)); SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 1, pItem->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT)); } else @@ -24930,6 +24931,7 @@ void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore cons } } +extern void SetRandomEnchantVisual(Player* player, Item* item); void Player::StoreLootItem(uint8 lootSlot, Loot* loot) { QuestItem* qitem = NULL; @@ -25005,6 +25007,7 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot) if (loot->containerID > 0) loot->DeleteLootItemFromContainerItemDB(item->itemid); + SetRandomEnchantVisual(this, newitem); } else SendEquipError(msg, NULL, NULL, item->itemid); diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index b4f1923..7531bd6 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -386,6 +386,7 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) loot->RemoveLooter(player->GetGUID()); } +extern void SetRandomEnchantVisual(Player* player, Item* item); void WorldSession::HandleLootMasterGiveOpcode(WorldPacket& recvData) { uint8 slotid; @@ -479,6 +480,7 @@ void WorldSession::HandleLootMasterGiveOpcode(WorldPacket& recvData) target->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type, item.count); target->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM, item.itemid, item.count); + SetRandomEnchantVisual(target, newitem); // mark as looted item.count = 0; item.is_looted = true; diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index 025184f..042aebc 100644 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -1416,6 +1416,7 @@ void AddBattlegroundScripts() #ifdef SCRIPTS /* This is where custom scripts' loading functions should be declared. */ +void AddSC_item_enchant_visuals(); #endif void AddCustomScripts() @@ -1423,5 +1424,6 @@ void AddCustomScripts() #ifdef SCRIPTS /* This is where custom scripts should be added. */ + AddSC_item_enchant_visuals(); #endif } diff --git a/src/server/scripts/Custom/CMakeLists.txt b/src/server/scripts/Custom/CMakeLists.txt index 5218f76..c6bda8e 100644 --- a/src/server/scripts/Custom/CMakeLists.txt +++ b/src/server/scripts/Custom/CMakeLists.txt @@ -13,6 +13,7 @@ set(scripts_STAT_SRCS ${scripts_STAT_SRCS} # ${sources_Custom} + Custom/enchant_visuals.cpp ) message(" -> Prepared: Custom") diff --git a/src/server/scripts/Custom/enchant_visuals.cpp b/src/server/scripts/Custom/enchant_visuals.cpp new file mode 100644 index 0000000..09b6d7f --- /dev/null +++ b/src/server/scripts/Custom/enchant_visuals.cpp @@ -0,0 +1,218 @@ +/* +Author: Rochet2 - https://rochet2.github.io/ +Source: https://rochet2.github.io/?page=Item_Enchant_Visuals + +About: +All weapons looted have a 25% chance to have a random enchant *visual* +This is purely visual fun and the visual will be replaced if the weapon is enchanted. + +This script is 100% automatic. Just add it to your source and DB table is automatically created + +Works with BOOST and ACE +*/ + +static const float chance = 0.25f; + +// Do not edit anything below + +#include "ScriptPCH.h" + +#ifndef UNORDERED_MAP +#define UNORDERED_MAP std::unordered_map +#endif + +#ifdef BOOST_VERSION +#define USING_BOOST +#endif +#ifdef USING_BOOST +#include +#include +#endif + +static const uint32 ItemEnchants[] = { 3789, 3854, 3273, 3225, 3870, 1899, 2674, 2675, 2671, 2672, 3365, 2673, 2343, 425, 3855, 1894, 1103, 1898, 3345, 1743, 3093, 1900, 3846, 1606, 283, 1, 3265, 2, 3, 3266, 1903, 13, 26, 7, 803, 1896, 2666, 25 }; +static const uint32 ItemEnchants_size = (sizeof(ItemEnchants) / sizeof(*ItemEnchants)) - 1; + +namespace +{ + class RWLockable + { + public: +#ifdef USING_BOOST + typedef boost::shared_mutex LockType; + typedef boost::shared_lock ReadGuard; + typedef boost::unique_lock WriteGuard; +#else + typedef ACE_RW_Thread_Mutex LockType; + typedef ACE_Read_Guard ReadGuard; + typedef ACE_Write_Guard WriteGuard; +#endif + LockType& GetLock() { return _lock; } + private: + LockType _lock; + }; + + class EnchantStore : public RWLockable + { + public: + typedef UNORDERED_MAP ItemLowToEnchant; // map[itemguid] = {enchant} + typedef UNORDERED_MAP PlayerLowToItemLowMap; // map[playerguid] = {ItemLowToEnchant} + + void LoadPlayerEnchants(uint32 playerLow) + { + QueryResult result = CharacterDatabase.PQuery("SELECT iguid, display FROM custom_item_enchant_visuals WHERE iguid IN(SELECT guid FROM item_instance WHERE owner_guid = %u)", playerLow); + if (!result) + return; + + ItemLowToEnchant temp; + do + { + uint32 iguid = result->Fetch()[0].GetUInt32(); + uint32 display = result->Fetch()[1].GetUInt32(); + temp[iguid] = display; + } while (result->NextRow()); + + WriteGuard lock(GetLock()); + hashmap[playerLow] = temp; + } + + void DeletePlayerEnchants(uint32 playerLow) + { + WriteGuard lock(GetLock()); + hashmap.erase(playerLow); + } + + void AddEnchant(uint32 playerLow, uint32 itemLow, uint32 enchant) + { + CharacterDatabase.PExecute("REPLACE INTO custom_item_enchant_visuals (iguid, display) VALUES (%u, %u)", itemLow, enchant); + + WriteGuard lock(GetLock()); + hashmap[playerLow][itemLow] = enchant; + } + + uint32 GetEnchant(uint32 playerLow, uint32 itemLow) + { + ReadGuard lock(GetLock()); + + PlayerLowToItemLowMap::iterator it = hashmap.find(playerLow); + if (it == hashmap.end()) + return 0; + + ItemLowToEnchant::iterator it2 = it->second.find(itemLow); + if (it2 == it->second.end()) + return 0; + + return it2->second; + } + + void RemoveEnchant(uint32 playerLow, uint32 itemLow) + { + { + WriteGuard lock(GetLock()); + + PlayerLowToItemLowMap::iterator it = hashmap.find(playerLow); + if (it == hashmap.end()) + return; + + it->second.erase(itemLow); + if (it->second.empty()) + hashmap.erase(playerLow); + } + + CharacterDatabase.PExecute("DELETE FROM custom_item_enchant_visuals WHERE iguid = %u", itemLow); + } + + private: + PlayerLowToItemLowMap hashmap; + }; +}; + +static EnchantStore enchantStore; + +uint32 GetItemEnchantVisual(Player* player, Item* item) +{ + if (!player || !item) + return 0; + + uint32 visual = enchantStore.GetEnchant(player->GetGUIDLow(), item->GetGUIDLow()); + if (!visual) + return 0; + + if (uint32 enchant = item->GetEnchantmentId(PERM_ENCHANTMENT_SLOT)) + { + enchantStore.RemoveEnchant(player->GetGUIDLow(), item->GetGUIDLow()); + player->SaveToDB(); + return enchant; + } + + return visual; +} + +void SetRandomEnchantVisual(Player* player, Item* item) +{ + if (!player || !item) + return; + + const ItemTemplate* temp = item->GetTemplate(); + if (temp->Class != ITEM_CLASS_WEAPON) + return; + + if (temp->SubClass == ITEM_SUBCLASS_WEAPON_BOW || + temp->SubClass == ITEM_SUBCLASS_WEAPON_GUN || + temp->SubClass == ITEM_SUBCLASS_WEAPON_obsolete || + temp->SubClass == ITEM_SUBCLASS_WEAPON_FIST || + temp->SubClass == ITEM_SUBCLASS_WEAPON_THROWN || + temp->SubClass == ITEM_SUBCLASS_WEAPON_SPEAR || + temp->SubClass == ITEM_SUBCLASS_WEAPON_CROSSBOW || + temp->SubClass == ITEM_SUBCLASS_WEAPON_WAND || + temp->SubClass == ITEM_SUBCLASS_WEAPON_FISHING_POLE) + return; + + if (rand_norm() >= chance) + return; + + uint32 enchant = ItemEnchants[urand(0, ItemEnchants_size)]; + enchantStore.AddEnchant(player->GetGUIDLow(), item->GetGUIDLow(), enchant); + + player->SaveToDB(); + player->SetVisibleItemSlot(EQUIPMENT_SLOT_MAINHAND, player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND)); + player->SetVisibleItemSlot(EQUIPMENT_SLOT_OFFHAND, player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)); +} + +class item_enchant_visuals : public PlayerScript +{ +public: + item_enchant_visuals() : PlayerScript("item_enchant_visuals") + { + // Create DB table on startup if doesnt exist + const char* sql = + "CREATE TABLE IF NOT EXISTS `custom_item_enchant_visuals` (" + " `iguid` INT(10) UNSIGNED NOT NULL COMMENT 'item DB guid'," + " `display` INT(10) UNSIGNED NOT NULL COMMENT 'enchantID'," + " PRIMARY KEY (`iguid`)" + ")" + "COMMENT='stores the enchant IDs for the visuals'" + "COLLATE='latin1_swedish_ci'" + "ENGINE=InnoDB;"; + CharacterDatabase.DirectExecute(sql); + + // Delete unused rows from DB table + CharacterDatabase.DirectExecute("DELETE FROM custom_item_enchant_visuals WHERE NOT EXISTS(SELECT 1 FROM item_instance WHERE custom_item_enchant_visuals.iguid = item_instance.guid)"); + } + + void OnLogin(Player* player, bool /*firstLogin*/) override + { + enchantStore.LoadPlayerEnchants(player->GetGUIDLow()); + player->SetVisibleItemSlot(EQUIPMENT_SLOT_MAINHAND, player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND)); + player->SetVisibleItemSlot(EQUIPMENT_SLOT_OFFHAND, player->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)); + } + + void OnLogout(Player* player) override + { + enchantStore.DeletePlayerEnchants(player->GetGUIDLow()); + } +}; + +void AddSC_item_enchant_visuals() +{ + new item_enchant_visuals; +}