feat(transmog): add transmog module #2

Merged
gasaichan merged 2 commits from feature/collections into master 2026-08-07 10:03:56 +03:00
4 changed files with 919 additions and 2 deletions
+13 -2
View File
@@ -145,7 +145,7 @@ import_mythicplus_sql() {
WHEN EXISTS (SELECT 1 FROM creature_template WHERE entry = 900001)
AND EXISTS (SELECT 1 FROM item_template WHERE entry = 900100)
AND EXISTS (SELECT 1 FROM gameobject_template WHERE entry = 900000)
AND EXISTS (SELECT 1 FROM creature WHERE id = 900001)
AND EXISTS (SELECT 1 FROM creature WHERE id1 = 900001)
AND EXISTS (SELECT 1 FROM gameobject WHERE id = 900000)
THEN 1
ELSE 0
@@ -156,7 +156,7 @@ import_mythicplus_sql() {
log "repairing MythicPlus world bootstrap entries"
mysql_exec "acore_world" "
DELETE FROM creature WHERE id = 900001;
DELETE FROM gameobject WHERE id = 900000;
DELETE FROM gameobject WHERE id1 = 900000;
DELETE FROM creature_template_locale WHERE entry = 900001;
DELETE FROM item_template_locale WHERE ID = 900100;
DELETE FROM creature_template_model WHERE CreatureID = 900001;
@@ -279,6 +279,16 @@ import_playerbots_ru_sql() {
import_sql_file "acore_characters" "$names_sql_file"
}
import_transmog_sql() {
log "Started importing Transmogrification SQL files"
local account_transmog_sql="$ROOT_DIR/sql/Transmogrification/auth.sql"
local character_transmog_sql="$ROOT_DIR/sql/Transmogrification/char.sql"
maybe_import_table "acore_auth" "account_transmog" "$account_transmog_sql"
maybe_import_table "acore_characters" "character_transmog" "$character_transmog_sql"
}
configure_server_motd() {
log "configuring MoonWell login message"
mysql_exec "acore_auth" "
@@ -362,6 +372,7 @@ import_aoe_loot_sql
import_playerbots_ru_sql
import_encounter_journal_sql
import_store_sql
import_transmog_sql
configure_server_motd
log "custom SQL bootstrap completed"
@@ -0,0 +1,838 @@
-- ╔════════════════════════════════════════════════════════════════════════╗
-- ║ Transmog System Eluna Script by DanielTheDeveloper ║
-- ╚════════════════════════════════════════════════════════════════════════╝
--
-- ╔══════════════════════════╗
-- ╔══════════════════════║ Transmog System Settings ║══════════════════════╗
-- ║ ╚══════════════════════════╝ ║
-- ║ Automatically adds transmog appearances to the players account-wide ║
-- ║ transmog collection when they equip an item for the first time. ║
-- ║ ║
-- ║ It is recommended to leave this option enabled. ║
-- ╟────────────────────────────────────────────────────────────────────────╢
local ADD_NEWLY_EQUIPPED_ITEMS_TO_THE_TRANSMOG_LIST = true --║
-- ╟────────────────────────────────────────────────────────────────────────╢
-- ║ ║
-- ║ Automatically adds transmog appearances to the players account-wide ║
-- ║ transmog collection when they loot an item for the first time. ║
-- ║ ║
-- ║ It is recommended to leave this option disabled as it creates the ║
-- ║ potential for a more healthy transmog economy to exist inside the ║
-- ║ auction house. ║
-- ╟────────────────────────────────────────────────────────────────────────╢
local ADD_NEWLY_LOOTED_ITEMS_TO_THE_TRANSMOG_LIST = false --║
-- ╟────────────────────────────────────────────────────────────────────────╢
-- ║ ║
-- ║ Automatically adds all applicable quest reward items as transmog ║
-- ║ appearances to the players account-wide transmog collection when ║
-- ║ completing a quest, regardless of which quest reward the player ║
-- ║ actually decided to select. ║
-- ║ ║
-- ║ It is recommended to leave this option enabled as it eliminates the ║
-- ║ dilemma of deciding between a potential transmog appearance or gear ║
-- ║ that is useful for the character. ║
-- ╟────────────────────────────────────────────────────────────────────────╢
local ADD_QUEST_REWARD_ITEMS_TO_THE_TRANSMOG_LIST = true --║
-- ╟────────────────────────────────────────────────────────────────────────╢
-- ║ ║
-- ║ Restricts armor transmog appearances to items made up of the same ║
-- ║ material. As an example, with this option enabled, cloth chest pieces ║
-- ║ can only be transmogrified to appear as other cloth chest pieces. ║
-- ║ When this option is disabled, cloth chest pieces can be ║
-- ║ transmogrified to appear as a cloth, leather, mail, or plate chest ║
-- ║ piece. ║
-- ║ ║
-- ║ It is recommended to leave this option enabled as it leaves the ║
-- ║ class fantasy intact. ║
-- ╟────────────────────────────────────────────────────────────────────────╢
local RESTRICT_ARMOR_TRANSMOG_TO_SIMILAR_MATERIALS = true --║
-- ╟────────────────────────────────────────────────────────────────────────╢
-- ║ ║
-- ║ Restricts weapon transmog appearances to the same weapon. As an ║
-- ║ example, with this option enabled, two-handed swords can only be ║
-- ║ transmogrified to appear as other two-handed swords. When this option ║
-- ║ is disabled, two-handed swords can be transmogrified to appear as a ║
-- ║ one-handed sword, staff, polearm, fishing pole, etc. ║
-- ║ ║
-- ║ It is recommended to leave this option enabled as it leaves the ║
-- ║ class fantasy intact. ║
-- ╟────────────────────────────────────────────────────────────────────────╢
local RESTRICT_WEAPON_TRANSMOG_TO_SIMILAR_WEAPONS = true --║
-- ╚════════════════════════════════════════════════════════════════════════╝
local AIO = AIO or require("AIO")
local TransmogrificationHandler = AIO.AddHandlers("TransmogrificationServer", {})
local SLOTS = 6
local CALC = 281
local PLAYER_VISIBLE_ITEM_1_ENTRYID = 283 -- Head
local PLAYER_VISIBLE_ITEM_3_ENTRYID = 287 -- Shoulder
local PLAYER_VISIBLE_ITEM_4_ENTRYID = 289 -- Shirt
local PLAYER_VISIBLE_ITEM_5_ENTRYID = 291 -- Chest
local PLAYER_VISIBLE_ITEM_6_ENTRYID = 293 -- Waist
local PLAYER_VISIBLE_ITEM_7_ENTRYID = 295 -- Legs
local PLAYER_VISIBLE_ITEM_8_ENTRYID = 297 -- Feet
local PLAYER_VISIBLE_ITEM_9_ENTRYID = 299 -- Wrist
local PLAYER_VISIBLE_ITEM_10_ENTRYID = 301 -- Hands
local PLAYER_VISIBLE_ITEM_15_ENTRYID = 311 -- Back
local PLAYER_VISIBLE_ITEM_16_ENTRYID = 313 -- Main
local PLAYER_VISIBLE_ITEM_17_ENTRYID = 315 -- Off
local PLAYER_VISIBLE_ITEM_18_ENTRYID = 317 -- Ranged
local PLAYER_VISIBLE_ITEM_19_ENTRYID = 319 -- Tabard
local UNUSABLE_INVENTORY_TYPES = {[2] = true, [11] = true, [12] = true, [18] = true, [24] = true, [27] = true, [28] = true}
-- TODO: Add further language support.
local localeMessages = {
LOOT_ITEM_LOCALE = {
[0] = " has been added to your appearance collection.", -- enUS/enGB
[3] = " wurde deiner Transmog-Sammlung hinzugefügt.", -- deDE
}
}
local function GetLocalizedMessage(messageID, locale, ...)
local message = localeMessages[messageID][locale] or localeMessages[messageID][0]
if select("#", ...) > 0 then
return string.format(message, ...)
end
return message
end
function Transmog_CalculateSlot(slot)
if (slot == 0) then
slot = 1
elseif (slot >= 2) then
slot = slot + 1
end
return CALC + (slot * 2);
end
function Transmog_CalculateSlotReverse(slot)
local reverseSlot = (slot - CALC) / 2
if (reverseSlot == 1) then
return 0;
end
return reverseSlot;
end
function Transmog_OnCharacterCreate(event, player)
local playerGUID = player:GetGUIDLow()
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_1_ENTRYID .. "', NULL, '');") -- Head
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_3_ENTRYID .. "', NULL, '');") -- Shoulder
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_4_ENTRYID .. "', NULL, '');") -- Shirt
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_5_ENTRYID .. "', NULL, '');") -- Chest
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_6_ENTRYID .. "', NULL, '');") -- Waist
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_7_ENTRYID .. "', NULL, '');") -- Legs
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_8_ENTRYID .. "', NULL, '');") -- Feet
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_9_ENTRYID .. "', NULL, '');") -- Wrist
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_10_ENTRYID .. "', NULL, '');") -- Hands
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_15_ENTRYID .. "', NULL, '');") -- Back
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_16_ENTRYID .. "', NULL, '');") -- Main
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_17_ENTRYID .. "', NULL, '');") -- Off
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_18_ENTRYID .. "', NULL, '');") -- Ranged
CharDBQuery("INSERT IGNORE INTO `character_transmog` (`player_guid`, `slot`, `item`, `real_item`) VALUES (" .. playerGUID .. ", '" .. PLAYER_VISIBLE_ITEM_19_ENTRYID .. "', NULL, '');") -- Tabard
end
function Transmog_OnCharacterDelete(event, guid)
CharDBQuery("DELETE FROM character_transmog WHERE player_guid = " .. guid .. "")
end
function Transmog_OnCharacterLogin(event, player)
local playerGUID = player:GetGUIDLow()
local playerExistsQuery = CharDBQuery("SELECT COUNT(*) FROM character_transmog WHERE player_guid = " .. playerGUID .. ";")
local playerExists = playerExistsQuery:GetUInt32(0) > 0
if not playerExists then
Transmog_OnCharacterCreate(event, player)
end
end
function TransmogrificationHandler.LootItemLocale(player, item, count, locale)
local accountGUID = player:GetAccountId()
local itemID
local itemTemplate
if type(item) == "number" then
itemID = item
itemTemplate = GetItemTemplate(itemID)
else
itemTemplate = item:GetItemTemplate()
itemID = itemTemplate:GetItemId()
end
local inventoryType = itemTemplate:GetInventoryType()
local inventorySubType = itemTemplate:GetSubClass()
local class = itemTemplate:GetClass()
if (class == 2 or class == 4) and not UNUSABLE_INVENTORY_TYPES[inventoryType] then
local countQuery = AuthDBQuery("SELECT COUNT(*) FROM account_transmog WHERE account_id = " .. accountGUID .. " AND unlocked_item_id = " .. itemID .. ";")
local count = countQuery:GetUInt32(0)
local isNewTransmog = (count == 0)
local displayID = itemTemplate:GetDisplayId()
local displayExistsQuery = AuthDBQuery("SELECT COUNT(*) FROM account_transmog WHERE account_id = " .. accountGUID .. " AND display_id = " .. displayID .. ";")
local displayExists = displayExistsQuery:GetUInt32(0) > 0
if isNewTransmog and not displayExists then
local itemName = itemTemplate:GetName()
local locItemName = itemTemplate:GetName(locale)
local itemQuality = itemTemplate:GetQuality()
itemName = itemName:gsub("'", "''")
AuthDBQuery("INSERT IGNORE INTO `account_transmog` (`account_id`, `unlocked_item_id`, `inventory_type`, `inventory_subtype`,`display_id`, `item_name`) VALUES (" .. accountGUID .. ", " .. itemID .. ", " .. inventoryType .. ", " .. inventorySubType .. ", " .. displayID .. ", '" .. itemName .. "');")
if locItemName == nil then
locItemName = itemTemplate:GetName(0)
end
local qualityColors = {
[0] = "|cff9d9d9d", -- Poor
[1] = "|cffffffff", -- Common
[2] = "|cff1eff00", -- Uncommon
[3] = "|cff0070dd", -- Rare
[4] = "|cffa335ee", -- Epic
[5] = "|cffff8000", -- Legendary
[6] = "|cffe6cc80" -- Heirloom
}
local colorCode = qualityColors[itemQuality] or "|cffffffff" -- Default to white if quality not found
local itemLink = "|Hitem:" .. itemID .. ":0:0:0:0:0:0:0:0|h" .. colorCode .. "[" .. locItemName .. "]|r|h|r"
player:SendBroadcastMessage(itemLink .. GetLocalizedMessage("LOOT_ITEM_LOCALE", locale))
end
end
end
function GetDisplaySlotForEquipmentSlot(equipSlot)
local slotMapping = {
[0] = PLAYER_VISIBLE_ITEM_1_ENTRYID, -- Head
[2] = PLAYER_VISIBLE_ITEM_3_ENTRYID, -- Shoulders
[3] = PLAYER_VISIBLE_ITEM_4_ENTRYID, -- Shirt
[4] = PLAYER_VISIBLE_ITEM_5_ENTRYID, -- Chest
[5] = PLAYER_VISIBLE_ITEM_6_ENTRYID, -- Waist
[6] = PLAYER_VISIBLE_ITEM_7_ENTRYID, -- Legs
[7] = PLAYER_VISIBLE_ITEM_8_ENTRYID, -- Feet
[8] = PLAYER_VISIBLE_ITEM_9_ENTRYID, -- Wrists
[9] = PLAYER_VISIBLE_ITEM_10_ENTRYID, -- Hands
[14] = PLAYER_VISIBLE_ITEM_15_ENTRYID, -- Back
[15] = PLAYER_VISIBLE_ITEM_16_ENTRYID, -- Main Hand
[16] = PLAYER_VISIBLE_ITEM_17_ENTRYID, -- Off Hand
[17] = PLAYER_VISIBLE_ITEM_18_ENTRYID, -- Ranged
[18] = PLAYER_VISIBLE_ITEM_19_ENTRYID -- Tabard
}
return slotMapping[equipSlot]
end
function Transmog_OnEquipItem(event, player, item, bag, slot)
local itemID = item:GetItemTemplate():GetItemId()
local locale = player:GetDbLocaleIndex()
if ADD_NEWLY_EQUIPPED_ITEMS_TO_THE_TRANSMOG_LIST then
TransmogrificationHandler.LootItemLocale(player, itemID, 1, locale)
end
local class = item:GetItemTemplate():GetClass()
local inventoryType = item:GetItemTemplate():GetInventoryType()
if (class == 2 or class == 4) and not UNUSABLE_INVENTORY_TYPES[inventoryType] then
local playerGUID = player:GetGUIDLow()
local displaySlot = GetDisplaySlotForEquipmentSlot(slot)
if displaySlot then
CharDBQuery("UPDATE character_transmog SET real_item = " .. itemID .. " WHERE player_guid = " .. playerGUID .. " AND slot = " .. displaySlot)
end
end
end
function Transmog_OnLootItem(event, player, item, count)
local locale = player:GetDbLocaleIndex()
TransmogrificationHandler.LootItemLocale(player, item, 1, locale)
end
function Transmog_OnQuestComplete(event, player, quest)
local questID = quest:GetId()
local locale = player:GetDbLocaleIndex()
local questRewardsQuery = WorldDBQuery("SELECT RewardItem1, RewardItem2, RewardItem3, RewardItem4, RewardChoiceItemID1, RewardChoiceItemID2, RewardChoiceItemID3, RewardChoiceItemID4, RewardChoiceItemID5, RewardChoiceItemID6 FROM quest_template WHERE ID = " .. questID .. ";")
if not questRewardsQuery then
return
end
for i = 0, 9 do
local itemID = questRewardsQuery:GetUInt32(i)
if itemID and itemID > 0 then
TransmogrificationHandler.LootItemLocale(player, itemID, 1, locale)
end
end
end
function GetEquipmentSlot(displaySlot)
-- Map from display/visual slots to actual equipment slots
local slotMapping = {
[PLAYER_VISIBLE_ITEM_1_ENTRYID] = 0, -- Head (0 in equipment slot system)
[PLAYER_VISIBLE_ITEM_3_ENTRYID] = 2, -- Shoulders
[PLAYER_VISIBLE_ITEM_4_ENTRYID] = 3, -- Shirt
[PLAYER_VISIBLE_ITEM_5_ENTRYID] = 4, -- Chest
[PLAYER_VISIBLE_ITEM_6_ENTRYID] = 5, -- Waist
[PLAYER_VISIBLE_ITEM_7_ENTRYID] = 6, -- Legs
[PLAYER_VISIBLE_ITEM_8_ENTRYID] = 7, -- Feet
[PLAYER_VISIBLE_ITEM_9_ENTRYID] = 8, -- Wrists
[PLAYER_VISIBLE_ITEM_10_ENTRYID] = 9, -- Hands
[PLAYER_VISIBLE_ITEM_15_ENTRYID] = 14, -- Back/Cloak
[PLAYER_VISIBLE_ITEM_16_ENTRYID] = 15, -- Main Hand
[PLAYER_VISIBLE_ITEM_17_ENTRYID] = 16, -- Off Hand
[PLAYER_VISIBLE_ITEM_18_ENTRYID] = 17, -- Ranged
[PLAYER_VISIBLE_ITEM_19_ENTRYID] = 18, -- Tabard
}
return slotMapping[displaySlot] or Transmog_CalculateSlotReverse(displaySlot)
end
-- TODO: add lua/c++ function for unequip!!
function TransmogrificationHandler.OnUnequipItem(player)
local playerGUID = player:GetGUIDLow()
-- Check all slots to see if any items were unequipped
local slots = {
PLAYER_VISIBLE_ITEM_1_ENTRYID, -- Head
PLAYER_VISIBLE_ITEM_3_ENTRYID, -- Shoulder
PLAYER_VISIBLE_ITEM_4_ENTRYID, -- Shirt
PLAYER_VISIBLE_ITEM_5_ENTRYID, -- Chest
PLAYER_VISIBLE_ITEM_6_ENTRYID, -- Waist
PLAYER_VISIBLE_ITEM_7_ENTRYID, -- Legs
PLAYER_VISIBLE_ITEM_8_ENTRYID, -- Feet
PLAYER_VISIBLE_ITEM_9_ENTRYID, -- Wrist
PLAYER_VISIBLE_ITEM_10_ENTRYID, -- Hands
PLAYER_VISIBLE_ITEM_15_ENTRYID, -- Back
PLAYER_VISIBLE_ITEM_16_ENTRYID, -- Main
PLAYER_VISIBLE_ITEM_17_ENTRYID, -- Off
PLAYER_VISIBLE_ITEM_18_ENTRYID, -- Ranged
PLAYER_VISIBLE_ITEM_19_ENTRYID -- Tabard
}
for _, slot in ipairs(slots) do
-- Get the corresponding equipment slot
local equipmentSlot = GetEquipmentSlot(slot)
-- Check if this slot has an item equipped
local currentItem = player:GetEquippedItemBySlot(equipmentSlot)
-- If the slot is empty but we have a transmog value, we need to clear it
if not currentItem then
local transmogQuery = CharDBQuery("SELECT item FROM character_transmog WHERE player_guid = "..playerGUID.." AND slot = "..slot..";")
if transmogQuery and transmogQuery:GetUInt32(0) and transmogQuery:GetUInt32(0) > 0 then
-- Clear the transmog by setting both item and real_item to NULL
CharDBQuery("UPDATE character_transmog SET item = NULL, real_item = NULL WHERE player_guid = "..playerGUID.." AND slot = "..slot..";")
player:SetUInt32Value(tonumber(slot), 0)
-- Inform the client to update the display for this slot
AIO.Handle(player, "TransmogrificationServer", "ClearSlotTransmogrification", slot)
end
end
end
end
function Transmog_Load(player)
local playerGUID = player:GetGUIDLow()
local transmogs = CharDBQuery("SELECT item, slot FROM character_transmog WHERE player_guid = "..playerGUID..";")
if (transmogs == nil) then
return;
end
for i = 1, transmogs:GetRowCount(), 1 do
local currentRow = transmogs:GetRow()
local slot = currentRow["slot"]
local item = currentRow["item"]
if (item ~= nil and item ~= '') then
player:SetUInt32Value(tonumber(slot), item)
end
transmogs:NextRow()
end
end
function Transmog_OnLogin(event, player)
-- Apply transmog on login
-- Transmog_Load(player)
--local item = player:GetEquippedItemBySlot(4)
--print(item:GetName())
end
function TransmogrificationHandler.LoadPlayer(player)
Transmog_Load(player)
player:SetUInt32Value(147, 1) -- use unit padding
end
function TransmogrificationHandler.EquipTransmogItem(player, item, slot)
local playerGUID = player:GetGUIDLow()
if item == nil and item ~= 0 then
local oldItem = CharDBQuery("SELECT real_item FROM character_transmog WHERE player_guid = "..playerGUID.." AND slot = "..slot..";")
local oldItemID = oldItem:GetUInt32(0)
if oldItemID == nil or oldItemID == 0 then
CharDBQuery("INSERT INTO character_transmog (`player_guid`, `slot`, `item`) VALUES ("..playerGUID..", '"..slot.."', NULL) ON DUPLICATE KEY UPDATE item = VALUES(item);")
player:SetUInt32Value(tonumber(slot), 0)
return
end
CharDBQuery("INSERT INTO character_transmog (`player_guid`, `slot`, `item`, `real_item`) VALUES ("..playerGUID..", '"..slot.."', NULL, "..oldItemID..") ON DUPLICATE KEY UPDATE item = VALUES(item), real_item = VALUES(real_item);")
player:SetUInt32Value(tonumber(slot), oldItemID)
return
end
local oldItem = CharDBQuery("SELECT real_item FROM character_transmog WHERE player_guid = "..playerGUID.." AND slot = "..slot..";")
local oldItemID = oldItem:GetUInt32(0)
if oldItemID == nil or oldItemID == 0 then
CharDBQuery("INSERT INTO character_transmog (`player_guid`, `slot`, `item`) VALUES ("..playerGUID..", '"..slot.."', "..item..") ON DUPLICATE KEY UPDATE item = VALUES(item);")
else
CharDBQuery("INSERT INTO character_transmog (`player_guid`, `slot`, `item`, `real_item`) VALUES ("..playerGUID..", '"..slot.."', "..item..", "..oldItemID..") ON DUPLICATE KEY UPDATE item = VALUES(item), real_item = VALUES(real_item);")
end
player:SetUInt32Value(tonumber(slot), item)
end
function TransmogrificationHandler.EquipAllTransmogItems(player, transmogPreview)
if (transmogPreview == {}) then
return;
end
local playerGUID = player:GetGUIDLow()
for slot, item in ipairs(transmogPreview) do
player:SetUInt32Value(tonumber(slot), item)
CharDBQuery("INSERT INTO character_transmog (`player_guid`, `slot`, `item`) VALUES ("..playerGUID..", '"..slot.."', "..item..") ON DUPLICATE KEY UPDATE item = VALUES(item);")
end
end
function TransmogrificationHandler.UnequipTransmogItem(player, slot)
local playerGUID = player:GetGUIDLow()
-- Get the corresponding equipment slot using our mapping function
local equipmentSlot = GetEquipmentSlot(slot)
-- Check if there's an actual item equipped in this slot
local currentItem = player:GetEquippedItemBySlot(equipmentSlot)
-- If no item is currently equipped, we should set the visual to 0
if not currentItem then
CharDBQuery("UPDATE character_transmog SET item = NULL, real_item = NULL WHERE player_guid = "..playerGUID.." AND slot = "..slot..";")
player:SetUInt32Value(tonumber(slot), 0)
return
end
-- Otherwise, handle normally but ensure we're setting to the real item or 0
local oldItem = CharDBQuery("SELECT real_item FROM character_transmog WHERE player_guid = "..playerGUID.." AND slot = "..slot..";")
-- If no record or null real_item, set to the actual item's appearance
if not oldItem or not oldItem:GetUInt32(0) or oldItem:GetUInt32(0) == 0 then
-- Here we set item to NULL (not 0) to indicate no transmog is applied
CharDBQuery("UPDATE character_transmog SET item = NULL WHERE player_guid = "..playerGUID.." AND slot = "..slot..";")
-- Get the current equipped item's ID to display
local realItemID = currentItem:GetItemTemplate():GetItemId()
player:SetUInt32Value(tonumber(slot), realItemID)
return
end
-- If we have a real_item value, restore that appearance
local oldItemID = oldItem:GetUInt32(0)
CharDBQuery("UPDATE character_transmog SET item = NULL WHERE player_guid = "..playerGUID.." AND slot = "..slot..";")
player:SetUInt32Value(tonumber(slot), oldItemID)
end
function TransmogrificationHandler.displayTransmog(player, spellid)
AIO.Handle(player, "TransmogrificationServer", "TransmogrificationFrame")
return false
end
function TransmogrificationHandler.Print(player, ...)
print(...)
end
function TransmogrificationHandler.SetTransmogItemIDs(player)
local playerGUID = player:GetGUIDLow()
local transmogs = CharDBQuery('SELECT item, real_item, slot FROM character_transmog WHERE player_guid = '..playerGUID..';') -- AND slot NOT IN ("313", "315", "317")
if (transmogs == nil) then
return;
end
for i = 1, transmogs:GetRowCount(), 1 do
local currentRow = transmogs:GetRow()
local item = currentRow["item"]
local slot = currentRow["slot"]
local real_item = currentRow["real_item"]
local validSlotItem = player:GetUInt32Value(tonumber(slot))
if (validSlotItem == 0) then
CharDBQuery("INSERT INTO character_transmog (`player_guid`, `slot`, `item`, `real_item`) VALUES ("..playerGUID..", '"..slot.."', 0, "..real_item..") ON DUPLICATE KEY UPDATE item = VALUES(item), real_item = VALUES(real_item);")
end
if (not item or item == 0 and real_item ~= nil and real_item ~= 0 and (validSlotItem ~= 0 or not validSlotItem)) then
AIO.Handle(player, "TransmogrificationServer", "SetTransmogItemIDClient", slot, 0, real_item)
else
AIO.Handle(player, "TransmogrificationServer", "SetTransmogItemIDClient", slot, item, real_item)
end
transmogs:NextRow()
end
end
function TransmogrificationHandler.SetCurrentSlotItemIDs(player, slot, page)
local accountGUID = player:GetAccountId()
-- Define inventory type mapping
local inventoryTypesMapping = {
[PLAYER_VISIBLE_ITEM_1_ENTRYID] = "= 1",
[PLAYER_VISIBLE_ITEM_3_ENTRYID] = "= 3",
[PLAYER_VISIBLE_ITEM_4_ENTRYID] = "= 4",
[PLAYER_VISIBLE_ITEM_5_ENTRYID] = "IN (5, 20)",
[PLAYER_VISIBLE_ITEM_6_ENTRYID] = "= 6",
[PLAYER_VISIBLE_ITEM_7_ENTRYID] = "= 7",
[PLAYER_VISIBLE_ITEM_8_ENTRYID] = "= 8",
[PLAYER_VISIBLE_ITEM_9_ENTRYID] = "= 9",
[PLAYER_VISIBLE_ITEM_10_ENTRYID] = "= 10",
[PLAYER_VISIBLE_ITEM_15_ENTRYID] = "= 16",
[PLAYER_VISIBLE_ITEM_16_ENTRYID] = "IN (13, 17, 21)",
[PLAYER_VISIBLE_ITEM_17_ENTRYID] = "IN (13, 17, 22, 23, 14)",
[PLAYER_VISIBLE_ITEM_18_ENTRYID] = "IN (15, 25, 26)",
[PLAYER_VISIBLE_ITEM_19_ENTRYID] = "= 19"
}
-- Get the inventory type for the given slot
local inventoryTypes = inventoryTypesMapping[slot]
if not inventoryTypes then
return -- Slot not valid, exit early
end
local equipmentSlot = nil
if slot == PLAYER_VISIBLE_ITEM_3_ENTRYID then
equipmentSlot = 2 -- Shoulder
elseif slot == PLAYER_VISIBLE_ITEM_4_ENTRYID then
equipmentSlot = 3 -- Shirt
elseif slot == PLAYER_VISIBLE_ITEM_5_ENTRYID then
equipmentSlot = 4 -- Chest
elseif slot == PLAYER_VISIBLE_ITEM_6_ENTRYID then
equipmentSlot = 5 -- Waist
elseif slot == PLAYER_VISIBLE_ITEM_10_ENTRYID then
equipmentSlot = 9 -- Hands
elseif slot == PLAYER_VISIBLE_ITEM_15_ENTRYID then
equipmentSlot = 14 -- Back
elseif slot == PLAYER_VISIBLE_ITEM_16_ENTRYID then
equipmentSlot = 15 -- Main
elseif slot == PLAYER_VISIBLE_ITEM_17_ENTRYID then
equipmentSlot = 16 -- Off
elseif slot == PLAYER_VISIBLE_ITEM_18_ENTRYID then
equipmentSlot = 17 -- Ranged
else
equipmentSlot = Transmog_CalculateSlotReverse(slot)
end
local currentItem = player:GetEquippedItemBySlot(equipmentSlot)
local equippedItemType = nil
local equippedItemSubType = nil
if currentItem then
equippedItemType = currentItem:GetClass()
equippedItemSubType = currentItem:GetSubClass()
end
-- Calculate page offset for pagination
local pageOffset = (page > 1) and (SLOTS * (page - 1)) or 0
local queryConditions = "account_id = " .. accountGUID .. " AND inventory_type " .. inventoryTypes
if equippedItemSubType then
if (RESTRICT_ARMOR_TRANSMOG_TO_SIMILAR_MATERIALS and equippedItemType == 4) then
if slot == PLAYER_VISIBLE_ITEM_5_ENTRYID then -- Special check for chestpiece/robes.
queryConditions = queryConditions .. " AND inventory_subtype = " .. equippedItemSubType
else
queryConditions = queryConditions .. " AND inventory_subtype = " .. equippedItemSubType
end
elseif (RESTRICT_WEAPON_TRANSMOG_TO_SIMILAR_WEAPONS and equippedItemType == 2) then
queryConditions = queryConditions .. " AND inventory_subtype = " .. equippedItemSubType
end
end
-- Query to count matching transmogs
local countQuery = string.format(
"SELECT COUNT(unlocked_item_id) FROM account_transmog WHERE %s",
queryConditions
)
local countResult = AuthDBQuery(countQuery)
if not countResult then
AIO.Handle(player, "TransmogrificationServer", "InitTab", {}, page, false)
return
end
-- Get the total number of transmogs
local totalTransmogs = countResult:GetUInt32(0)
local hasMorePages = (totalTransmogs > SLOTS * page)
-- Query to retrieve transmogs for the current page
local transmogQuery = string.format(
"SELECT unlocked_item_id FROM account_transmog WHERE %s LIMIT %d OFFSET %d;",
queryConditions, SLOTS, pageOffset
)
local transmogs = AuthDBQuery(transmogQuery)
if not transmogs then
AIO.Handle(player, "TransmogrificationServer", "InitTab", {}, page, false)
return
end
-- Collect the unlocked item IDs
local currentSlotItemIDs = {}
for i = 1, transmogs:GetRowCount() do
local currentRow = transmogs:GetRow()
local item = currentRow["unlocked_item_id"]
table.insert(currentSlotItemIDs, item)
transmogs:NextRow()
end
-- Return the result to the player
AIO.Handle(player, "TransmogrificationServer", "InitTab", currentSlotItemIDs, page, hasMorePages)
end
function TransmogrificationHandler.SetSearchCurrentSlotItemIDs(player, slot, page, search)
-- Ensure search is not empty or nil
if ( search == nil or serach == '' ) then
return;
end
-- Escape special characters in search string
search = search:gsub("[%'`&\"]", "%%")
-- Define slot-to-inventory type mapping
local inventoryTypesMapping = {
[PLAYER_VISIBLE_ITEM_1_ENTRYID] = "= 1",
[PLAYER_VISIBLE_ITEM_3_ENTRYID] = "= 3",
[PLAYER_VISIBLE_ITEM_4_ENTRYID] = "= 4",
[PLAYER_VISIBLE_ITEM_5_ENTRYID] = "IN (5, 20)",
[PLAYER_VISIBLE_ITEM_6_ENTRYID] = "= 6",
[PLAYER_VISIBLE_ITEM_7_ENTRYID] = "= 7",
[PLAYER_VISIBLE_ITEM_8_ENTRYID] = "= 8",
[PLAYER_VISIBLE_ITEM_9_ENTRYID] = "= 9",
[PLAYER_VISIBLE_ITEM_10_ENTRYID] = "= 10",
[PLAYER_VISIBLE_ITEM_15_ENTRYID] = "= 16",
[PLAYER_VISIBLE_ITEM_16_ENTRYID] = "IN (13, 17, 21)",
[PLAYER_VISIBLE_ITEM_17_ENTRYID] = "IN (13, 17, 22, 23, 14)",
[PLAYER_VISIBLE_ITEM_18_ENTRYID] = "IN (15, 25, 26)",
[PLAYER_VISIBLE_ITEM_19_ENTRYID] = "= 19"
}
-- Get inventory type for the given slot
local inventoryTypes = inventoryTypesMapping[slot]
if not inventoryTypes then
return -- Slot not valid
end
local equipmentSlot = nil
if slot == PLAYER_VISIBLE_ITEM_3_ENTRYID then
equipmentSlot = 2 -- Shoulder
elseif slot == PLAYER_VISIBLE_ITEM_4_ENTRYID then
equipmentSlot = 3 -- Shirt
elseif slot == PLAYER_VISIBLE_ITEM_5_ENTRYID then
equipmentSlot = 4 -- Chest
elseif slot == PLAYER_VISIBLE_ITEM_6_ENTRYID then
equipmentSlot = 5 -- Waist
elseif slot == PLAYER_VISIBLE_ITEM_10_ENTRYID then
equipmentSlot = 9 -- Hands
elseif slot == PLAYER_VISIBLE_ITEM_15_ENTRYID then
equipmentSlot = 14 -- Back
elseif slot == PLAYER_VISIBLE_ITEM_16_ENTRYID then
equipmentSlot = 15 -- Main
elseif slot == PLAYER_VISIBLE_ITEM_17_ENTRYID then
equipmentSlot = 16 -- Off
elseif slot == PLAYER_VISIBLE_ITEM_18_ENTRYID then
equipmentSlot = 17 -- Ranged
else
equipmentSlot = Transmog_CalculateSlotReverse(slot)
end
local currentItem = player:GetEquippedItemBySlot(equipmentSlot)
local equippedItemType = nil
local equippedItemSubType = nil
if currentItem then
equippedItemType = currentItem:GetClass()
equippedItemSubType = currentItem:GetSubClass()
end
-- Calculate page offset
local pageOffset = (page > 1) and (SLOTS * (page - 1)) or 0
local queryConditions = "account_id = " .. player:GetAccountId() .. " AND inventory_type " .. inventoryTypes .. " AND (display_id LIKE '%" .. search .. "%' OR item_name LIKE '%" .. search .. "%')"
if equippedItemSubType then
if (RESTRICT_ARMOR_TRANSMOG_TO_SIMILAR_MATERIALS and equippedItemType == 4) then
if slot == PLAYER_VISIBLE_ITEM_5_ENTRYID then -- Special check for chestpiece/robes.
queryConditions = queryConditions .. " AND inventory_subtype = " .. equippedItemSubType
else
queryConditions = queryConditions .. " AND inventory_subtype = " .. equippedItemSubType
end
elseif (RESTRICT_WEAPON_TRANSMOG_TO_SIMILAR_WEAPONS and equippedItemType == 2) then
queryConditions = queryConditions .. " AND inventory_subtype = " .. equippedItemSubType
end
end
-- Query to count matching transmogs
local countQuery = string.format(
"SELECT COUNT(unlocked_item_id) FROM account_transmog WHERE %s;",
queryConditions
)
local countResult = AuthDBQuery(countQuery)
if not countResult then
AIO.Handle(player, "TransmogrificationServer", "InitTab", {}, page, false)
return
end
local totalTransmogs = countResult:GetUInt32(0)
local hasMorePages = (totalTransmogs > SLOTS * page)
-- Query to get transmogs
local transmogQuery = string.format(
"SELECT unlocked_item_id FROM account_transmog WHERE %s LIMIT %d OFFSET %d;",
queryConditions, SLOTS, pageOffset
)
local transmogs = AuthDBQuery(transmogQuery)
if not transmogs then
AIO.Handle(player, "TransmogrificationServer", "InitTab", {}, page, false)
return
end
-- Collect the unlocked item IDs
local currentSlotItemIDs = {}
for i = 1, transmogs:GetRowCount() do
local currentRow = transmogs:GetRow()
local item = currentRow["unlocked_item_id"]
table.insert(currentSlotItemIDs, item)
transmogs:NextRow()
end
-- Return the result
AIO.Handle(player, "TransmogrificationServer", "InitTab", currentSlotItemIDs, page, hasMorePages)
end
function TransmogrificationHandler.SetEquipmentTransmogInfo(player, slot, currentSlotTooltip)
local playerGUID = player:GetGUIDLow()
local transmog = CharDBQuery("SELECT COUNT(item) FROM character_transmog WHERE player_guid = "..playerGUID.." AND slot = '"..slot.."';")
if (transmog == nil) then
return;
end
if (transmog:GetUInt32(0) ~= 0) then
AIO.Handle(player, "TransmogrificationServer", "SetEquipmentTransmogInfoClient", currentSlotTooltip)
end
end
function TransmogrificationHandler.GetItemsWithSameAppearance(player, itemID)
-- Query the server for the display ID of the new item
local displayIDQuery = "SELECT displayid FROM item_template WHERE entry = " .. itemID .. ";"
local displayIDResult = WorldDBQuery(displayIDQuery)
if not displayIDResult then
return
end
local displayID = displayIDResult:GetUInt32(0)
-- Find all items with the same display ID
local matchingItemsQuery = "SELECT entry FROM item_template WHERE displayid = " .. displayID .. ";"
local matchingItemsResult = WorldDBQuery(matchingItemsQuery)
if not matchingItemsResult then
return
end
-- Collect the matching item IDs into a table
local matchingItems = {}
for i = 1, matchingItemsResult:GetRowCount() do
local currentRow = matchingItemsResult:GetRow()
local matchingItemID = currentRow["entry"]
table.insert(matchingItems, matchingItemID)
matchingItemsResult:NextRow()
end
-- Send the matching transmogs to the client
AIO.Handle(player, "TransmogrificationServer", "ReceiveMatchingAppearances", itemID, matchingItems)
end
function TransmogrificationHandler.SendCollectedTransmogItemIDs(player)
local accountGUID = player:GetAccountId()
-- Query to retrieve collected transmog display IDs
local collectedDisplayIDsQuery = "SELECT DISTINCT display_id FROM account_transmog WHERE account_id = " .. accountGUID .. ";"
local displayIDsResult = AuthDBQuery(collectedDisplayIDsQuery)
if not displayIDsResult then
AIO.Handle(player, "TransmogrificationServer", "ReceiveCollectedAppearances", {}, 0)
return
end
-- Count the unique appearances.
local uniqueAppearancesCount = displayIDsResult:GetRowCount()
-- Collect the display IDs into a table
local collectedDisplayIDs = {}
for i = 1, displayIDsResult:GetRowCount() do
local currentRow = displayIDsResult:GetRow()
local displayID = currentRow["display_id"]
table.insert(collectedDisplayIDs, displayID)
displayIDsResult:NextRow()
end
local collectedAppearances = {}
-- For each display ID, find all items that share the appearance
for _, displayID in ipairs(collectedDisplayIDs) do
local itemsWithDisplayQuery = "SELECT entry FROM item_template WHERE displayid = " .. displayID .. ";"
local itemsResult = WorldDBQuery(itemsWithDisplayQuery)
if itemsResult then
for j = 1, itemsResult:GetRowCount() do
local itemRow = itemsResult:GetRow()
local itemID = itemRow["entry"]
table.insert(collectedAppearances, itemID)
itemsResult:NextRow()
end
end
end
-- Send the collected transmogs to the client
AIO.Handle(player, "TransmogrificationServer", "ReceiveCollectedAppearances", collectedAppearances, uniqueAppearancesCount)
end
RegisterPlayerEvent(1, Transmog_OnCharacterCreate)
RegisterPlayerEvent(2, Transmog_OnCharacterDelete)
RegisterPlayerEvent(3, Transmog_OnCharacterLogin)
RegisterPlayerEvent(29, Transmog_OnEquipItem)
RegisterPlayerEvent(30, function(event, player, bag, slot)
if bag == 255 then
TransmogrificationHandler.OnUnequipItem(player)
end
end)
if ADD_NEWLY_LOOTED_ITEMS_TO_THE_TRANSMOG_LIST then
RegisterPlayerEvent(32, Transmog_OnLootItem)
RegisterPlayerEvent(51, Transmog_OnLootItem)
RegisterPlayerEvent(52, Transmog_OnLootItem)
RegisterPlayerEvent(53, Transmog_OnLootItem)
RegisterPlayerEvent(56, Transmog_OnLootItem)
end
if ADD_QUEST_REWARD_ITEMS_TO_THE_TRANSMOG_LIST then
RegisterPlayerEvent(54, Transmog_OnQuestComplete)
end
RegisterPlayerEvent(42, function(event, player, command)
if command:lower() == "transmog sync" then
TransmogrificationHandler.SendCollectedTransmogItemIDs(player)
return false
end
return true
end)
print("[Eluna] Transmog System loaded successfully.")
+35
View File
@@ -0,0 +1,35 @@
-- --------------------------------------------------------
-- Host: 192.168.0.167
-- Server-Version: 8.3.0 - MySQL Community Server - GPL
-- Server-Betriebssystem: Linux
-- HeidiSQL Version: 12.8.0.6908
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-- Exportiere Struktur von Tabelle acore_auth.account_transmog
CREATE TABLE IF NOT EXISTS `account_transmog` (
`account_id` int unsigned NOT NULL,
`unlocked_item_id` int unsigned NOT NULL,
`inventory_type` int unsigned DEFAULT NULL,
`inventory_subtype` int unsigned DEFAULT NULL,
`display_id` int unsigned NOT NULL,
`item_name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
UNIQUE KEY `account_item_id` (`account_id`,`unlocked_item_id`) USING BTREE,
UNIQUE KEY `account_display_id` (`account_id`,`display_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Daten-Export vom Benutzer nicht ausgewählt
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;
+33
View File
@@ -0,0 +1,33 @@
-- --------------------------------------------------------
-- Host: 192.168.0.167
-- Server-Version: 8.3.0 - MySQL Community Server - GPL
-- Server-Betriebssystem: Linux
-- HeidiSQL Version: 12.8.0.6908
-- --------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8 */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-- Exportiere Struktur von Tabelle acore_characters.character_transmog
CREATE TABLE IF NOT EXISTS `character_transmog` (
`player_guid` int unsigned DEFAULT NULL,
`slot` varchar(3) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`item` int unsigned DEFAULT NULL,
`real_item` int unsigned DEFAULT NULL,
UNIQUE KEY `player_and_slot` (`player_guid`,`slot`) USING BTREE,
KEY `player_and_slot_key` (`player_guid`,`slot`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Daten-Export vom Benutzer nicht ausgewählt
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;