Skip to content

Instantly share code, notes, and snippets.

@Rochet2
Last active August 19, 2022 01:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Rochet2/8693564 to your computer and use it in GitHub Desktop.
Save Rochet2/8693564 to your computer and use it in GitHub Desktop.

About

When you loot a weapon, there is a 25% chance it will have a random visual effect. The chance can be edited. If you enchant the weapon, the visual effect is lost and replaced by the enchant visual. In total there are 38 different visuals. Note that not all items can have an enchant visual.
Source: http://rochet2.github.io/Item-Enchant-Visuals.html

Installation

Download gist
Apply the the diff with git bash using command git apply ItemEnchantVisuals.diff
Supported TC version: https://github.com/TrinityCore/TrinityCore/commit/3ea97450aec21619b953753430750e0b4ec92920

Usage

Kill NPC, loot weapon, hope you get an enchant visual.

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 <boost/thread/locks.hpp>
+#include <boost/thread/shared_mutex.hpp>
+#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<boost::shared_mutex> ReadGuard;
+ typedef boost::unique_lock<boost::shared_mutex> WriteGuard;
+#else
+ typedef ACE_RW_Thread_Mutex LockType;
+ typedef ACE_Read_Guard<LockType> ReadGuard;
+ typedef ACE_Write_Guard<LockType> WriteGuard;
+#endif
+ LockType& GetLock() { return _lock; }
+ private:
+ LockType _lock;
+ };
+
+ class EnchantStore : public RWLockable
+ {
+ public:
+ typedef UNORDERED_MAP<uint32, uint32> ItemLowToEnchant; // map[itemguid] = {enchant}
+ typedef UNORDERED_MAP<uint32, ItemLowToEnchant> 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;
+}
@timothystewart6
Copy link

timothystewart6 commented Dec 20, 2016

Hi. This doesn't seem to work. I tried applying it and it first it complained about line endings. I converted the endings and now it says:

> git apply visuals.diff
error: patch failed: src/server/game/Entities/Player/Player.cpp:12486
error: src/server/game/Entities/Player/Player.cpp: patch does not apply
error: src/server/game/Scripting/ScriptLoader.cpp: No such file or directory
error: src/server/scripts/Custom/CMakeLists.txt: No such file or directory

I am trying to apply it to TrinityCore/TrinityCore@330e5b0

@BlackMetalz
Copy link

Try apply manually. It works well for me.
<3 Rochet2

@GnaXi
Copy link

GnaXi commented Jan 18, 2018

Could you elaborate on how you got it to work @BlackMetalz ?

@nyhmii
Copy link

nyhmii commented May 18, 2018

GUIDLoW? just rename to guid?

@zaicopx
Copy link

zaicopx commented Aug 19, 2022

Is it working for latest trinitycore??

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment