First Commit

This commit is contained in:
mikx
2025-03-18 19:19:03 -04:00
commit 93073b0be2
10024 changed files with 9034050 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,288 @@
#ifndef DEF_TRANSMOGRIFICATION_H
#define DEF_TRANSMOGRIFICATION_H
#include "Player.h"
#include "Config.h"
#include "ScriptMgr.h"
#include "ScriptedGossip.h"
#include "GameEventMgr.h"
#include "Item.h"
#include "ScriptMgr.h"
#include "Chat.h"
#include "ItemTemplate.h"
#include "QuestDef.h"
#include "ItemTemplate.h"
#include <unordered_map>
#include <vector>
#define PRESETS // comment this line to disable preset feature totally
#define HIDDEN_ITEM_ID 1 // used for hidden transmog - do not use a valid equipment ID
#define MAX_OPTIONS 25 // do not alter
#define MAX_SEARCH_STRING_LENGTH 50
class Item;
class Player;
class WorldSession;
struct ItemTemplate;
enum TransmogSettings
{
SETTING_HIDE_TRANSMOG = 0,
SETTING_RETROACTIVE_CHECK = 1,
SETTING_VENDOR_INTERFACE = 2,
// Subscriptions
SETTING_TRANSMOG_MEMBERSHIP_LEVEL = 0
};
enum MixedWeaponSettings
{
MIXED_WEAPONS_STRICT = 0,
MIXED_WEAPONS_MODERN = 1,
MIXED_WEAPONS_LOOSE = 2
};
enum TransmogAcoreStrings // Language.h might have same entries, appears when executing SQL, change if needed
{
LANG_ERR_TRANSMOG_OK = 11100, // change this
LANG_ERR_TRANSMOG_INVALID_SLOT,
LANG_ERR_TRANSMOG_INVALID_SRC_ENTRY,
LANG_ERR_TRANSMOG_MISSING_SRC_ITEM,
LANG_ERR_TRANSMOG_MISSING_DEST_ITEM,
LANG_ERR_TRANSMOG_INVALID_ITEMS,
LANG_ERR_TRANSMOG_NOT_ENOUGH_MONEY,
LANG_ERR_TRANSMOG_NOT_ENOUGH_TOKENS,
LANG_ERR_UNTRANSMOG_OK,
LANG_ERR_UNTRANSMOG_NO_TRANSMOGS,
#ifdef PRESETS
LANG_PRESET_ERR_INVALID_NAME,
#endif
LANG_CMD_TRANSMOG_SHOW = 11111,
LANG_CMD_TRANSMOG_HIDE = 11112,
LANG_CMD_TRANSMOG_ADD_UNSUITABLE = 11113,
LANG_CMD_TRANSMOG_ADD_FORBIDDEN = 11114,
LANG_CMD_TRANSMOG_BEGIN_SYNC = 11115,
LANG_CMD_TRANSMOG_COMPLETE_SYNC = 11116,
LANG_CMD_TRANSMOG_VENDOR_INTERFACE_ENABLE = 11117,
LANG_CMD_TRANSMOG_VENDOR_INTERFACE_DISABLE = 11118
};
enum ArmorClassSpellIDs
{
SPELL_PLATE = 750,
SPELL_MAIL = 8737,
SPELL_LEATHER = 9077,
SPELL_CLOTH = 9078
};
const uint32 AllArmorSpellIds[4] =
{
SPELL_PLATE,
SPELL_MAIL,
SPELL_LEATHER,
SPELL_CLOTH
};
const uint32 AllArmorTiers[4] =
{
ITEM_SUBCLASS_ARMOR_PLATE,
ITEM_SUBCLASS_ARMOR_MAIL,
ITEM_SUBCLASS_ARMOR_LEATHER,
ITEM_SUBCLASS_ARMOR_CLOTH
};
enum PlusFeatures
{
PLUS_FEATURE_GREY_ITEMS,
PLUS_FEATURE_LEGENDARY_ITEMS,
PLUS_FEATURE_PET,
PLUS_FEATURE_SKIP_LEVEL_REQ
};
const uint32 TMOG_VENDOR_CREATURE_ID = 190010;
class Transmogrification
{
public:
static Transmogrification* instance();
typedef std::unordered_map<ObjectGuid, ObjectGuid> transmogData;
typedef std::unordered_map<ObjectGuid, uint32> transmog2Data;
typedef std::unordered_map<ObjectGuid, transmog2Data> transmogMap;
typedef std::unordered_map<uint32, std::vector<uint32>> collectionCacheMap;
typedef std::unordered_map<uint32, std::string> searchStringMap;
typedef std::unordered_map<uint32, std::vector<uint32>> transmogPlusData;
typedef std::unordered_map<ObjectGuid, uint8> selectedSlotMap;
transmogPlusData plusDataMap;
transmogMap entryMap; // entryMap[pGUID][iGUID] = entry
transmogData dataMap; // dataMap[iGUID] = pGUID
collectionCacheMap collectionCache;
selectedSlotMap selectionCache;
#ifdef PRESETS
bool EnableSetInfo;
uint32 SetNpcText;
typedef std::map<uint8, uint32> slotMap;
typedef std::map<uint8, slotMap> presetData;
typedef std::unordered_map<ObjectGuid, presetData> presetDataMap;
presetDataMap presetById; // presetById[pGUID][presetID][slot] = entry
typedef std::map<uint8, std::string> presetIdMap;
typedef std::unordered_map<ObjectGuid, presetIdMap> presetNameMap;
presetNameMap presetByName; // presetByName[pGUID][presetID] = presetName
searchStringMap searchStringByPlayer;
void PresetTransmog(Player* player, Item* itemTransmogrified, uint32 fakeEntry, uint8 slot);
bool EnableSets;
uint8 MaxSets;
float SetCostModifier;
int32 SetCopperCost;
bool GetEnableSets() const;
uint8 GetMaxSets() const;
float GetSetCostModifier() const;
int32 GetSetCopperCost() const;
void LoadPlayerSets(ObjectGuid pGUID);
void UnloadPlayerSets(ObjectGuid pGUID);
#endif
bool EnableTransmogInfo;
uint32 TransmogNpcText;
// Use IsAllowed() and IsNotAllowed()
// these are thread unsafe, but assumed to be static data so it should be safe
std::set<uint32> Allowed;
std::set<uint32> NotAllowed;
float ScaledCostModifier;
int32 CopperCost;
bool RequireToken;
uint32 TokenEntry;
uint32 TokenAmount;
bool AllowPoor;
bool AllowCommon;
bool AllowUncommon;
bool AllowRare;
bool AllowEpic;
bool AllowLegendary;
bool AllowArtifact;
bool AllowHeirloom;
bool AllowTradeable;
bool AllowMixedArmorTypes;
bool AllowLowerTiers;
bool AllowMixedOffhandArmorTypes;
bool AllowMixedWeaponHandedness;
bool AllowFishingPoles;
uint8 AllowMixedWeaponTypes;
bool IgnoreReqRace;
bool IgnoreReqClass;
bool IgnoreReqSkill;
bool IgnoreReqSpell;
bool IgnoreReqLevel;
bool IgnoreReqEvent;
bool IgnoreReqStats;
bool UseCollectionSystem;
bool UseVendorInterface;
bool AllowHiddenTransmog;
bool HiddenTransmogIsFree;
bool TrackUnusableItems;
bool RetroActiveAppearances;
bool ResetRetroActiveAppearances;
bool IsTransmogEnabled;
bool IsPortableNPCEnabled;
bool IsAllowed(uint32 entry) const;
bool IsNotAllowed(uint32 entry) const;
bool IsAllowedQuality(uint32 quality, ObjectGuid const & playerGuid) const;
bool IsRangedWeapon(uint32 Class, uint32 SubClass) const;
bool CanNeverTransmog(ItemTemplate const* itemTemplate);
void LoadConfig(bool reload); // thread unsafe
std::string GetItemIcon(uint32 entry, uint32 width, uint32 height, int x, int y) const;
std::string GetSlotIcon(uint8 slot, uint32 width, uint32 height, int x, int y) const;
const char * GetSlotName(uint8 slot, WorldSession* session) const;
std::string GetItemLink(Item* item, WorldSession* session) const;
std::string GetItemLink(uint32 entry, WorldSession* session) const;
uint32 GetFakeEntry(ObjectGuid itemGUID) const;
void UpdateItem(Player* player, Item* item) const;
void DeleteFakeEntry(Player* player, uint8 slot, Item* itemTransmogrified, CharacterDatabaseTransaction* trans = nullptr);
void SetFakeEntry(Player* player, uint32 newEntry, uint8 slot, Item* itemTransmogrified);
bool AddCollectedAppearance(uint32 accountId, uint32 itemId);
TransmogAcoreStrings Transmogrify(Player* player, ObjectGuid itemGUID, uint8 slot, /*uint32 newEntry, */bool no_cost = false);
TransmogAcoreStrings Transmogrify(Player* player, uint32 itemEntry, uint8 slot, /*uint32 newEntry, */bool no_cost = false);
TransmogAcoreStrings Transmogrify(Player* player, Item* itemTransmogrifier, uint8 slot, /*uint32 newEntry, */bool no_cost = false, bool hidden_transmog = false);
bool CanTransmogrifyItemWithItem(Player* player, ItemTemplate const* destination, ItemTemplate const* source) const;
bool SuitableForTransmogrification(Player* player, ItemTemplate const* proto) const;
bool SuitableForTransmogrification(ObjectGuid guid, ItemTemplate const* proto) const;
bool IsItemTransmogrifiable(ItemTemplate const* proto, ObjectGuid const &playerGuid) const;
uint32 GetSpecialPrice(ItemTemplate const* proto) const;
void DeleteFakeFromDB(ObjectGuid::LowType itemLowGuid, CharacterDatabaseTransaction* trans = nullptr);
float GetScaledCostModifier() const;
int32 GetCopperCost() const;
bool GetRequireToken() const;
uint32 GetTokenEntry() const;
uint32 GetTokenAmount() const;
bool GetAllowMixedArmorTypes() const;
bool GetAllowLowerTiers() const;
bool GetAllowMixedOffhandArmorTypes() const;
uint8 GetAllowMixedWeaponTypes() const;
// Config
bool GetEnableTransmogInfo() const;
uint32 GetTransmogNpcText() const;
bool GetEnableSetInfo() const;
uint32 GetSetNpcText() const;
bool GetAllowTradeable() const;
bool GetUseCollectionSystem() const;
bool GetUseVendorInterface() const;
bool GetAllowHiddenTransmog() const;
bool GetHiddenTransmogIsFree() const;
bool GetTrackUnusableItems() const;
bool EnableRetroActiveAppearances() const;
bool EnableResetRetroActiveAppearances() const;
[[nodiscard]] bool IsEnabled() const;
bool IsValidOffhandArmor(uint32 subclass, uint32 invType) const;
bool IsTieredArmorSubclass(uint32 subclass) const;
uint32 GetHighestAvailableForPlayer(Player* player) const;
uint32 GetHighestAvailableForPlayer(int playerGuid) const;
bool TierAvailable(Player* player, int playerGuid, uint32 tierSpell) const;
bool IsInvTypeMismatchAllowed (const ItemTemplate *source, const ItemTemplate *target) const;
bool IsSubclassMismatchAllowed (Player *player, const ItemTemplate *source, const ItemTemplate *target) const;
// Transmog Plus
bool IsTransmogPlusEnabled;
[[nodiscard]] bool IsPlusFeatureEligible(ObjectGuid const& playerGuid, uint32 feature) const;
[[nodiscard]] uint32 GetPlayerMembershipLevel(Player* player) const { return player->GetPlayerSetting("acore_cms_subscriptions", SETTING_TRANSMOG_MEMBERSHIP_LEVEL).value; };
[[nodiscard]] bool IgnoreLevelRequirement(ObjectGuid const& playerGuid) const { return IgnoreReqLevel || IsPlusFeatureEligible(playerGuid, PLUS_FEATURE_SKIP_LEVEL_REQ); }
uint32 PetSpellId;
uint32 PetEntry;
[[nodiscard]] bool IsTransmogVendor(uint32 entry) const { return entry == TMOG_VENDOR_CREATURE_ID || entry == PetEntry; };
};
#define sTransmogrification Transmogrification::instance()
#endif

View File

@@ -0,0 +1,318 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Chat.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "Transmogrification.h"
#include "Tokenize.h"
#include "DatabaseEnv.h"
using namespace Acore::ChatCommands;
class transmog_commandscript : public CommandScript
{
public:
transmog_commandscript() : CommandScript("transmog_commandscript") { }
ChatCommandTable GetCommands() const override
{
static ChatCommandTable addCollectionTable =
{
{ "set", HandleAddTransmogItemSet, SEC_MODERATOR, Console::Yes },
{ "", HandleAddTransmogItem, SEC_MODERATOR, Console::Yes },
};
static ChatCommandTable transmogTable =
{
{ "add", addCollectionTable },
{ "", HandleDisableTransMogVisual, SEC_PLAYER, Console::No },
{ "sync", HandleSyncTransMogCommand, SEC_PLAYER, Console::No },
{ "portable", HandleTransmogPortableCommand, SEC_PLAYER, Console::No },
{ "interface", HandleInterfaceOption, SEC_PLAYER, Console::No }
};
static ChatCommandTable commandTable =
{
{ "transmog", transmogTable },
};
return commandTable;
}
static bool HandleSyncTransMogCommand(ChatHandler* handler)
{
Player* player = handler->GetPlayer();
uint32 accountId = player->GetSession()->GetAccountId();
handler->SendSysMessage(LANG_CMD_TRANSMOG_BEGIN_SYNC);
for (uint32 itemId : sTransmogrification->collectionCache[accountId])
handler->PSendSysMessage("TRANSMOG_SYNC:{}", itemId);
handler->SendSysMessage(LANG_CMD_TRANSMOG_COMPLETE_SYNC);
return true;
}
static bool HandleDisableTransMogVisual(ChatHandler* handler, bool hide)
{
Player* player = handler->GetPlayer();
if (hide)
{
player->UpdatePlayerSetting("mod-transmog", SETTING_HIDE_TRANSMOG, 0);
handler->SendSysMessage(LANG_CMD_TRANSMOG_SHOW);
}
else
{
player->UpdatePlayerSetting("mod-transmog", SETTING_HIDE_TRANSMOG, 1);
handler->SendSysMessage(LANG_CMD_TRANSMOG_HIDE);
}
player->UpdateObjectVisibility();
return true;
}
static bool HandleAddTransmogItem(ChatHandler* handler, Optional<PlayerIdentifier> player, ItemTemplate const* itemTemplate)
{
if (!sTransmogrification->GetUseCollectionSystem())
return true;
if (!sObjectMgr->GetItemTemplate(itemTemplate->ItemId))
{
handler->PSendSysMessage(LANG_COMMAND_ITEMIDINVALID, itemTemplate->ItemId);
handler->SetSentErrorMessage(true);
return false;
}
if (!player)
player = PlayerIdentifier::FromTargetOrSelf(handler);
if (!player)
return false;
Player* target = player->GetConnectedPlayer();
bool isNotConsole = handler->GetSession();
bool suitableForTransmog;
if (target)
suitableForTransmog = sTransmogrification->SuitableForTransmogrification(target, itemTemplate);
else
suitableForTransmog = sTransmogrification->SuitableForTransmogrification(player->GetGUID(), itemTemplate);
if (!sTransmogrification->GetTrackUnusableItems() && !suitableForTransmog)
{
handler->SendSysMessage(LANG_CMD_TRANSMOG_ADD_UNSUITABLE);
handler->SetSentErrorMessage(true);
return true;
}
if (itemTemplate->Class != ITEM_CLASS_ARMOR && itemTemplate->Class != ITEM_CLASS_WEAPON)
{
handler->SendSysMessage(LANG_CMD_TRANSMOG_ADD_FORBIDDEN);
handler->SetSentErrorMessage(true);
return true;
}
auto guid = player->GetGUID();
uint32 accountId = sCharacterCache->GetCharacterAccountIdByGuid(guid);
uint32 itemId = itemTemplate->ItemId;
std::stringstream tempStream;
tempStream << std::hex << ItemQualityColors[itemTemplate->Quality];
std::string itemQuality = tempStream.str();
std::string itemName = itemTemplate->Name1;
if (target) {
// get locale item name
int loc_idex = target->GetSession()->GetSessionDbLocaleIndex();
if (ItemLocale const* il = sObjectMgr->GetItemLocale(itemId))
ObjectMgr::GetLocaleString(il->Name, loc_idex, itemName);
}
std::string playerName = player->GetName();
std::string nameLink = handler->playerLink(playerName);
if (sTransmogrification->AddCollectedAppearance(accountId, itemId))
{
// Notify target of new item in appearance collection
if (target && !(target->GetPlayerSetting("mod-transmog", SETTING_HIDE_TRANSMOG).value) && !sTransmogrification->CanNeverTransmog(itemTemplate))
ChatHandler(target->GetSession()).PSendSysMessage(R"(|c{}|Hitem:{}:0:0:0:0:0:0:0:0|h[{}]|h|r has been added to your appearance collection.)", itemQuality.c_str(), itemId, itemName.c_str());
// Feedback of successful command execution to GM
if (isNotConsole && target != handler->GetPlayer())
handler->PSendSysMessage(R"(|c{}|Hitem:{}:0:0:0:0:0:0:0:0|h[{}]|h|r has been added to the appearance collection of Player {}.)", itemQuality.c_str(), itemId, itemName.c_str(), nameLink);
CharacterDatabase.Execute("INSERT INTO custom_unlocked_appearances (account_id, item_template_id) VALUES ({}, {})", accountId, itemId);
}
else
{
// Feedback of failed command execution to GM
if (isNotConsole)
{
handler->PSendSysMessage(R"(Player {} already has item |c{}|Hitem:{}:0:0:0:0:0:0:0:0|h[{}]|h|r in the appearance collection.)", nameLink, itemQuality.c_str(), itemId, itemName.c_str());
handler->SetSentErrorMessage(true);
}
}
return true;
}
static bool HandleAddTransmogItemSet(ChatHandler* handler, Optional<PlayerIdentifier> player, Variant<Hyperlink<itemset>, uint32> itemSetId)
{
if (!sTransmogrification->GetUseCollectionSystem())
return true;
if (!*itemSetId)
{
handler->PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND, uint32(itemSetId));
handler->SetSentErrorMessage(true);
return false;
}
if (!player)
player = PlayerIdentifier::FromTargetOrSelf(handler);
if (!player)
return false;
Player* target = player->GetConnectedPlayer();
ItemSetEntry const* set = sItemSetStore.LookupEntry(uint32(itemSetId));
bool isNotConsole = handler->GetSession();
if (!set)
{
handler->PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND, uint32(itemSetId));
handler->SetSentErrorMessage(true);
return false;
}
auto guid = player->GetGUID();
CharacterCacheEntry const* playerData = sCharacterCache->GetCharacterCacheByGuid(guid);
if (!playerData)
return false;
bool added = false;
uint32 error = 0;
uint32 itemId;
uint32 accountId = playerData->AccountId;
for (uint32 i = 0; i < MAX_ITEM_SET_ITEMS; ++i)
{
itemId = set->itemId[i];
if (itemId)
{
ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId);
if (itemTemplate)
{
if (!sTransmogrification->GetTrackUnusableItems() && (
(target && !sTransmogrification->SuitableForTransmogrification(target, itemTemplate)) ||
!sTransmogrification->SuitableForTransmogrification(guid, itemTemplate)
))
{
error = LANG_CMD_TRANSMOG_ADD_UNSUITABLE;
continue;
}
if (itemTemplate->Class != ITEM_CLASS_ARMOR && itemTemplate->Class != ITEM_CLASS_WEAPON)
{
error = LANG_CMD_TRANSMOG_ADD_FORBIDDEN;
continue;
}
if (sTransmogrification->AddCollectedAppearance(accountId, itemId))
{
CharacterDatabase.Execute("INSERT INTO custom_unlocked_appearances (account_id, item_template_id) VALUES ({}, {})", accountId, itemId);
added = true;
}
}
}
}
if (!added && error > 0)
{
handler->SendSysMessage(error);
handler->SetSentErrorMessage(true);
return true;
}
int locale = handler->GetSessionDbcLocale();
std::string setName = set->name[locale];
std::string nameLink = handler->playerLink(player->GetName());
// Feedback of command execution to GM
if (isNotConsole)
{
// Failed command execution
if (!added)
{
handler->PSendSysMessage("Player {} already has ItemSet |cffffffff|Hitemset:{}|h[{} {}]|h|r in the appearance collection.", nameLink, uint32(itemSetId), setName.c_str(), localeNames[locale]);
handler->SetSentErrorMessage(true);
return true;
}
// Successful command execution
if (target != handler->GetPlayer())
handler->PSendSysMessage("ItemSet |cffffffff|Hitemset:{}|h[{} {}]|h|r has been added to the appearance collection of Player {}.", uint32(itemSetId), setName.c_str(), localeNames[locale], nameLink);
}
// Notify target of new item in appearance collection
if (target && !(target->GetPlayerSetting("mod-transmog", SETTING_HIDE_TRANSMOG).value))
ChatHandler(target->GetSession()).PSendSysMessage("ItemSet |cffffffff|Hitemset:%d|h[{} {}]|h|r has been added to your appearance collection.", uint32(itemSetId), setName.c_str(), localeNames[locale]);
return true;
}
static bool HandleTransmogPortableCommand(ChatHandler* handler)
{
if (!sTransmogrification->IsPortableNPCEnabled)
{
handler->GetPlayer()->SendSystemMessage("The portable transmogrification NPC is disabled.");
handler->SetSentErrorMessage(true);
return true;
}
if (Player* player = PlayerIdentifier::FromSelf(handler)->GetConnectedPlayer())
{
if (sTransmogrification->IsTransmogPlusEnabled)
if (sTransmogrification->IsPlusFeatureEligible(player->GetGUID(), PLUS_FEATURE_PET))
{
player->CastSpell((Unit*)nullptr, sTransmogrification->PetSpellId, true);
return true;
}
if (player->GetSession()->GetSecurity() < SEC_MODERATOR)
return true;
player->CastSpell((Unit*)nullptr, sTransmogrification->PetSpellId, true);
}
return true;
};
static bool HandleInterfaceOption(ChatHandler* handler, bool enable)
{
handler->GetPlayer()->UpdatePlayerSetting("mod-transmog", SETTING_VENDOR_INTERFACE, enable);
handler->SendSysMessage(enable ? LANG_CMD_TRANSMOG_VENDOR_INTERFACE_ENABLE : LANG_CMD_TRANSMOG_VENDOR_INTERFACE_DISABLE);
return true;
}
};
void AddSC_transmog_commandscript()
{
new transmog_commandscript();
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
// From SC
void AddSC_Transmog();
void AddSC_transmog_commandscript();
// Add all
void Addmod_transmogScripts()
{
AddSC_Transmog();
AddSC_transmog_commandscript();
}

File diff suppressed because it is too large Load Diff