From 1d46f6826e139341aa37d487ef3abc5306ffe8db Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sat, 29 Oct 2016 16:22:37 +0300 Subject: [PATCH 01/35] Initial commit - copy from https://github.com/Rochet2/TrinityCore/commit/71eb6320539b8cfb8f67361b8a6fcec3b21cfbd9 --- .../game/AI/SmartScripts/SmartScript.cpp | 12 +- .../game/Entities/Creature/Creature.cpp | 58 +++++--- src/server/game/Entities/Creature/Creature.h | 17 ++- src/server/game/Globals/ObjectMgr.cpp | 140 +++++++++++++++--- src/server/game/Globals/ObjectMgr.h | 25 +++- src/server/game/Handlers/QueryHandler.cpp | 8 +- src/server/game/Handlers/SpellHandler.cpp | 38 +++++ src/server/game/Server/WorldSession.cpp | 1 + .../game/Spells/Auras/SpellAuraEffects.cpp | 6 +- src/server/game/World/World.cpp | 3 + src/server/scripts/Commands/cs_reload.cpp | 10 ++ .../scripts/Custom/DressNPCs/Example.sql | 7 + src/server/scripts/Custom/DressNPCs/README.md | 47 ++++++ 13 files changed, 310 insertions(+), 62 deletions(-) create mode 100644 src/server/scripts/Custom/DressNPCs/Example.sql create mode 100644 src/server/scripts/Custom/DressNPCs/README.md diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 33a426be2d1f1..65b9e7218443c 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -272,7 +272,15 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(e.action.morphOrMount.creature)) { - uint32 displayId = ObjectMgr::ChooseDisplayId(ci); + Creature* crea = (*itr)->ToCreature(); + ASSERT(crea); + crea->SetOutfit(ObjectMgr::ChooseDisplayId(ci)); + uint32 displayId = sObjectMgr->GetCreatureDisplay(crea->GetOutfit()); + if (crea->GetOutfit() < 0 && displayId) + (*itr)->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); + else + (*itr)->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); + (*itr)->ToCreature()->SetDisplayId(displayId); TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL: Creature entry %u, %s set displayid to %u", (*itr)->GetEntry(), (*itr)->GetGUID().ToString().c_str(), displayId); @@ -1113,7 +1121,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (e.action.morphOrMount.creature > 0) { if (CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(e.action.morphOrMount.creature)) - (*itr)->ToUnit()->Mount(ObjectMgr::ChooseDisplayId(cInfo)); + (*itr)->ToUnit()->Mount(sObjectMgr->GetCreatureDisplay(ObjectMgr::ChooseDisplayId(cInfo))); } else (*itr)->ToUnit()->Mount(e.action.morphOrMount.model); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 9c1089392d98e..9c3ce04eed5df 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -83,10 +83,10 @@ VendorItem const* VendorItemData::FindItemCostPair(uint32 item_id, uint32 extend return nullptr; } -uint32 CreatureTemplate::GetRandomValidModelId() const +int32 CreatureTemplate::GetRandomValidModelId() const { uint8 c = 0; - uint32 modelIDs[4]; + int32 modelIDs[4]; if (Modelid1) modelIDs[c++] = Modelid1; if (Modelid2) modelIDs[c++] = Modelid2; @@ -96,7 +96,7 @@ uint32 CreatureTemplate::GetRandomValidModelId() const return ((c>0) ? modelIDs[urand(0, c-1)] : 0); } -uint32 CreatureTemplate::GetFirstValidModelId() const +int32 CreatureTemplate::GetFirstValidModelId() const { if (Modelid1) return Modelid1; if (Modelid2) return Modelid2; @@ -107,46 +107,47 @@ uint32 CreatureTemplate::GetFirstValidModelId() const uint32 CreatureTemplate::GetFirstInvisibleModel() const { - CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid1); + CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid1)); if (modelInfo && modelInfo->is_trigger) - return Modelid1; + return sObjectMgr->GetCreatureDisplay(Modelid1); - modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid2); + modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid2)); if (modelInfo && modelInfo->is_trigger) - return Modelid2; + return sObjectMgr->GetCreatureDisplay(Modelid2); - modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid3); + modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid3)); if (modelInfo && modelInfo->is_trigger) - return Modelid3; + return sObjectMgr->GetCreatureDisplay(Modelid3); - modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid4); + modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid4)); if (modelInfo && modelInfo->is_trigger) - return Modelid4; + return sObjectMgr->GetCreatureDisplay(Modelid4); return 11686; } uint32 CreatureTemplate::GetFirstVisibleModel() const { - CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid1); + CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid1)); if (modelInfo && !modelInfo->is_trigger) - return Modelid1; + return sObjectMgr->GetCreatureDisplay(Modelid1); - modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid2); + modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid2)); if (modelInfo && !modelInfo->is_trigger) - return Modelid2; + return sObjectMgr->GetCreatureDisplay(Modelid2); - modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid3); + modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid3)); if (modelInfo && !modelInfo->is_trigger) - return Modelid3; + return sObjectMgr->GetCreatureDisplay(Modelid3); - modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid4); + modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid4)); if (modelInfo && !modelInfo->is_trigger) - return Modelid4; + return sObjectMgr->GetCreatureDisplay(Modelid4); return 17519; } + bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { if (Unit* victim = ObjectAccessor::GetUnit(m_owner, m_victim)) @@ -185,7 +186,7 @@ _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(UI64LIT(0)), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), -m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_focusSpell(nullptr), m_focusDelay(0) +m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_focusSpell(nullptr), m_focusDelay(0), outfitId(0) { m_regenTimer = CREATURE_REGEN_INTERVAL; m_valuesCount = UNIT_END; @@ -355,7 +356,13 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) return false; } - uint32 displayID = ObjectMgr::ChooseDisplayId(GetCreatureTemplate(), data); + SetOutfit(ObjectMgr::ChooseDisplayId(GetCreatureTemplate(), data)); + uint32 displayID = sObjectMgr->GetCreatureDisplay(GetOutfit()); + if (GetOutfit() < 0 && displayID) + SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); + else + RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); + CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&displayID); if (!minfo) // Cancel load if no model defined { @@ -431,7 +438,10 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/) SetUInt64Value(UNIT_NPC_FLAGS, npcflag); SetUInt32Value(UNIT_FIELD_FLAGS, unit_flags); - SetUInt32Value(UNIT_FIELD_FLAGS_2, cInfo->unit_flags2); + if (GetOutfit() < 0 && GetDisplayId()) + SetUInt32Value(UNIT_FIELD_FLAGS_2, cInfo->unit_flags2 | UNIT_FLAG2_MIRROR_IMAGE); + else + SetUInt32Value(UNIT_FIELD_FLAGS_2, cInfo->unit_flags2); SetUInt32Value(OBJECT_DYNAMIC_FLAGS, dynamicflags); @@ -1079,8 +1089,8 @@ void Creature::SaveToDB(uint32 mapid, uint32 spawnMask, uint32 phaseMask) CreatureTemplate const* cinfo = GetCreatureTemplate(); if (cinfo) { - if (displayId == cinfo->Modelid1 || displayId == cinfo->Modelid2 || - displayId == cinfo->Modelid3 || displayId == cinfo->Modelid4) + if (displayId == sObjectMgr->GetCreatureDisplay(cinfo->Modelid1) || displayId == sObjectMgr->GetCreatureDisplay(cinfo->Modelid2) || + displayId == sObjectMgr->GetCreatureDisplay(cinfo->Modelid3) || displayId == sObjectMgr->GetCreatureDisplay(cinfo->Modelid4)) displayId = 0; if (npcflag == cinfo->npcflag) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 47b9931353c10..112bd49f88fb8 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -304,10 +304,10 @@ struct TC_GAME_API CreatureTemplate uint32 Entry; uint32 DifficultyEntry[MAX_CREATURE_DIFFICULTIES]; uint32 KillCredit[MAX_KILL_CREDIT]; - uint32 Modelid1; - uint32 Modelid2; - uint32 Modelid3; - uint32 Modelid4; + int32 Modelid1; + int32 Modelid2; + int32 Modelid3; + int32 Modelid4; std::string Name; std::string FemaleName; std::string SubName; @@ -365,8 +365,8 @@ struct TC_GAME_API CreatureTemplate uint32 MechanicImmuneMask; uint32 flags_extra; uint32 ScriptID; - uint32 GetRandomValidModelId() const; - uint32 GetFirstValidModelId() const; + int32 GetRandomValidModelId() const; + int32 GetFirstValidModelId() const; uint32 GetFirstInvisibleModel() const; uint32 GetFirstVisibleModel() const; @@ -704,6 +704,9 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void SetObjectScale(float scale) override; void SetDisplayId(uint32 modelId) override; + void SetOutfit(int32 outfit) { outfitId = outfit; }; + int32 GetOutfit() const { return outfitId; }; + void DisappearAndDie(); bool Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, uint32 entry, float x, float y, float z, float ang, CreatureData const* data = nullptr, uint32 vehId = 0); @@ -1012,6 +1015,8 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma Spell const* m_focusSpell; ///> Locks the target during spell cast for proper facing uint32 m_focusDelay; + int32 outfitId; + CreatureTextRepeatGroup m_textRepeat; }; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 005985b4d1ddd..f3d92c534cd4e 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -467,10 +467,10 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i) creatureTemplate.KillCredit[i] = fields[4 + i].GetUInt32(); - creatureTemplate.Modelid1 = fields[6].GetUInt32(); - creatureTemplate.Modelid2 = fields[7].GetUInt32(); - creatureTemplate.Modelid3 = fields[8].GetUInt32(); - creatureTemplate.Modelid4 = fields[9].GetUInt32(); + creatureTemplate.Modelid1 = fields[6].GetInt32(); + creatureTemplate.Modelid2 = fields[7].GetInt32(); + creatureTemplate.Modelid3 = fields[8].GetInt32(); + creatureTemplate.Modelid4 = fields[9].GetInt32(); creatureTemplate.Name = fields[10].GetString(); creatureTemplate.FemaleName = fields[11].GetString(); creatureTemplate.SubName = fields[12].GetString(); @@ -795,66 +795,66 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) if (cInfo->Modelid1) { - CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid1); + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(sObjectMgr->GetCreatureDisplay(cInfo->Modelid1)); if (!displayEntry) { - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid1 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid1); + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid1 id (%i), this can crash the client.", cInfo->Entry, cInfo->Modelid1); const_cast(cInfo)->Modelid1 = 0; } else if (!displayScaleEntry) displayScaleEntry = displayEntry; - CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid1); + CreatureModelInfo const* modelInfo = GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(cInfo->Modelid1)); if (!modelInfo) - TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid1` = %u listed by creature (Entry: %u).", cInfo->Modelid1, cInfo->Entry); + TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid1` = %i listed by creature (Entry: %u).", cInfo->Modelid1, cInfo->Entry); } if (cInfo->Modelid2) { - CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid2); + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(sObjectMgr->GetCreatureDisplay(cInfo->Modelid2)); if (!displayEntry) { - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid2 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid2); + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid2 id (%i), this can crash the client.", cInfo->Entry, cInfo->Modelid2); const_cast(cInfo)->Modelid2 = 0; } else if (!displayScaleEntry) displayScaleEntry = displayEntry; - CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid2); + CreatureModelInfo const* modelInfo = GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(cInfo->Modelid2)); if (!modelInfo) - TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid2` = %u listed by creature (Entry: %u).", cInfo->Modelid2, cInfo->Entry); + TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid2` = %i listed by creature (Entry: %u).", cInfo->Modelid2, cInfo->Entry); } if (cInfo->Modelid3) { - CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid3); + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(sObjectMgr->GetCreatureDisplay(cInfo->Modelid3)); if (!displayEntry) { - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid3 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid3); + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid3 id (%i), this can crash the client.", cInfo->Entry, cInfo->Modelid3); const_cast(cInfo)->Modelid3 = 0; } else if (!displayScaleEntry) displayScaleEntry = displayEntry; - CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid3); + CreatureModelInfo const* modelInfo = GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(cInfo->Modelid3)); if (!modelInfo) - TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid3` = %u listed by creature (Entry: %u).", cInfo->Modelid3, cInfo->Entry); + TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid3` = %i listed by creature (Entry: %u).", cInfo->Modelid3, cInfo->Entry); } if (cInfo->Modelid4) { - CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid4); + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(sObjectMgr->GetCreatureDisplay(cInfo->Modelid4)); if (!displayEntry) { - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid4 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid4); + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid4 id (%i), this can crash the client.", cInfo->Entry, cInfo->Modelid4); const_cast(cInfo)->Modelid4 = 0; } else if (!displayScaleEntry) displayScaleEntry = displayEntry; - CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid4); + CreatureModelInfo const* modelInfo = GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(cInfo->Modelid4)); if (!modelInfo) - TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid4` = %u listed by creature (Entry: %u).", cInfo->Modelid4, cInfo->Entry); + TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid4` = %i listed by creature (Entry: %u).", cInfo->Modelid4, cInfo->Entry); } if (!displayScaleEntry) @@ -1306,7 +1306,7 @@ CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelId) const return nullptr; } -uint32 ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data /*= nullptr*/) +int32 ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data /*= nullptr*/) { // Load creature model (display id) if (data && data->displayid) @@ -5941,7 +5941,7 @@ uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt CreatureTemplate const* mount_info = GetCreatureTemplate(mount_entry); if (mount_info) { - mount_id = mount_info->GetRandomValidModelId(); + mount_id = sObjectMgr->GetCreatureDisplay(mount_info->GetRandomValidModelId()); if (!mount_id) { TC_LOG_ERROR("sql.sql", "No displayid found for the taxi mount with the entry %u! Can't load it!", mount_entry); @@ -8054,6 +8054,89 @@ SkillRangeType GetSkillRangeType(SkillRaceClassInfoEntry const* rcEntry) return SKILL_RANGE_LEVEL; } +void ObjectMgr::LoadCreatureOutfits() +{ + uint32 oldMSTime = getMSTime(); + + _creatureOutfitStore.clear(); // for reload case (test only) + + QueryResult result = WorldDatabase.Query("SELECT entry, race, class, gender, skin, face, hair, haircolor, facialhair, " + "head, shoulders, body, chest, waist, legs, feet, wrists, hands, tabard, back FROM creature_template_outfits"); + + if (!result) + { + TC_LOG_ERROR("server.loading", ">> Loaded 0 creature outfits. DB table `creature_template_outfits` is empty!"); + return; + } + + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + + uint32 i = 0; + uint32 entry = fields[i++].GetUInt32(); + + CreatureOutfit co; + + co.race = fields[i++].GetUInt8(); + const ChrRacesEntry* rEntry = sChrRacesStore.LookupEntry(co.race); + if (!rEntry) + { + TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has incorrect race (%u).", entry, uint32(co.race)); + continue; + } + + co.Class = fields[i++].GetUInt8(); + const ChrClassesEntry* cEntry = sChrClassesStore.LookupEntry(co.Class); + if (!cEntry) + { + TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has incorrect class (%u).", entry, uint32(co.Class)); + continue; + } + + co.gender = fields[i++].GetUInt8(); + switch (co.gender) + { + case GENDER_FEMALE: co.displayId = rEntry->FemaleDisplayID; break; + case GENDER_MALE: co.displayId = rEntry->MaleDisplayID; break; + default: + TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has invalid gender %u", entry, uint32(co.gender)); + continue; + } + + co.skin = fields[i++].GetUInt8(); + co.face = fields[i++].GetUInt8(); + co.hair = fields[i++].GetUInt8(); + co.haircolor = fields[i++].GetUInt8(); + co.facialhair = fields[i++].GetUInt8(); + for (uint32 j = 0; j < MAX_CREATURE_OUTFIT_DISPLAYS; ++j) + { + int32 displayInfo = fields[i + j].GetInt32(); + if (displayInfo > 0) // entry + { + if (uint32 display = sDB2Manager.GetItemDisplayId(static_cast(displayInfo), 0)) + co.outfit[j] = display; + else + { + TC_LOG_ERROR("server.loading", ">> Creature entry %u in `creature_template_outfits` has invalid item entry %i", entry, displayInfo); + co.outfit[j] = 0; + } + } + else // display + co.outfit[j] = uint32(-displayInfo); + } + + _creatureOutfitStore[entry] = co; + + ++count; + } + while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u creature outfits in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + void ObjectMgr::LoadGameTele() { uint32 oldMSTime = getMSTime(); @@ -9309,6 +9392,19 @@ PlayerInfo const* ObjectMgr::GetPlayerInfo(uint32 race, uint32 class_) const return info; } +uint32 ObjectMgr::GetCreatureDisplay(int32 modelid) const +{ + if (modelid >= 0) + return modelid; + + const CreatureOutfitContainer& outfits = GetCreatureOutfitMap(); + CreatureOutfitContainer::const_iterator it = outfits.find(-modelid); + if (it != outfits.end()) + return it->second.displayId; + + return 0; +} + void ObjectMgr::LoadRaceAndClassExpansionRequirements() { uint32 oldMSTime = getMSTime(); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 383ee43833544..fe3538b7cdf29 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -137,6 +137,23 @@ struct GameTele typedef std::unordered_map GameTeleContainer; +#define MAX_CREATURE_OUTFIT_DISPLAYS 11 +struct CreatureOutfit +{ + uint8 race; + uint8 Class; + uint8 gender; + uint8 face; + uint8 skin; + uint8 hair; + uint8 facialhair; + uint8 haircolor; + uint32 displayId; + uint32 outfit[MAX_CREATURE_OUTFIT_DISPLAYS]; +}; + +typedef std::unordered_map CreatureOutfitContainer; + enum ScriptsType { SCRIPTS_FIRST = 1, @@ -761,7 +778,7 @@ class TC_GAME_API ObjectMgr CreatureTemplateContainer const* GetCreatureTemplates() const { return &_creatureTemplateStore; } CreatureModelInfo const* GetCreatureModelInfo(uint32 modelId) const; CreatureModelInfo const* GetCreatureModelRandomGender(uint32* displayID) const; - static uint32 ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data = nullptr); + static int32 ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data = nullptr); static void ChooseCreatureFlags(CreatureTemplate const* cinfo, uint64& npcflag, uint32& unit_flags, uint32& dynamicflags, CreatureData const* data = nullptr); EquipmentInfo const* GetEquipmentInfo(uint32 entry, int8& id) const; CreatureAddon const* GetCreatureAddon(ObjectGuid::LowType lowguid) const; @@ -770,6 +787,7 @@ class TC_GAME_API ObjectMgr CreatureAddon const* GetCreatureTemplateAddon(uint32 entry) const; ItemTemplate const* GetItemTemplate(uint32 entry) const; ItemTemplateContainer const* GetItemTemplateStore() const { return &_itemTemplateStore; } + uint32 GetCreatureDisplay(int32 modelid) const; InstanceTemplate const* GetInstanceTemplate(uint32 mapId) const; @@ -1065,6 +1083,7 @@ class TC_GAME_API ObjectMgr void LoadNPCSpellClickSpells(); + void LoadCreatureOutfits(); void LoadGameTele(); void LoadGossipMenu(); @@ -1273,6 +1292,8 @@ class TC_GAME_API ObjectMgr bool AddGameTele(GameTele& data); bool DeleteGameTele(std::string const& name); + const CreatureOutfitContainer& GetCreatureOutfitMap() const { return _creatureOutfitStore; } + TrainerSpellData const* GetNpcTrainerSpells(uint32 entry) const { CacheTrainerSpellContainer::const_iterator iter = _cacheTrainerSpellStore.find(entry); @@ -1502,6 +1523,8 @@ class TC_GAME_API ObjectMgr PageTextContainer _pageTextStore; InstanceTemplateContainer _instanceTemplateStore; + CreatureOutfitContainer _creatureOutfitStore; + TerrainPhaseInfo _terrainPhaseInfoStore; TerrainPhaseInfo _terrainMapDefaultStore; TerrainUIPhaseInfo _terrainWorldMapStore; diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index a380d272f16fd..ac64514cdfefb 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -90,10 +90,10 @@ void WorldSession::HandleCreatureQuery(WorldPackets::Query::QueryCreature& packe for (uint32 i = 0; i < MAX_KILL_CREDIT; ++i) stats.ProxyCreatureID[i] = creatureInfo->KillCredit[i]; - stats.CreatureDisplayID[0] = creatureInfo->Modelid1; - stats.CreatureDisplayID[1] = creatureInfo->Modelid2; - stats.CreatureDisplayID[2] = creatureInfo->Modelid3; - stats.CreatureDisplayID[3] = creatureInfo->Modelid4; + stats.CreatureDisplayID[0] = sObjectMgr->GetCreatureDisplay(creatureInfo->Modelid1); + stats.CreatureDisplayID[1] = sObjectMgr->GetCreatureDisplay(creatureInfo->Modelid2); + stats.CreatureDisplayID[2] = sObjectMgr->GetCreatureDisplay(creatureInfo->Modelid3); + stats.CreatureDisplayID[3] = sObjectMgr->GetCreatureDisplay(creatureInfo->Modelid4); stats.HpMulti = creatureInfo->ModHealth; stats.EnergyMulti = creatureInfo->ModMana; diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index f45a6053685ba..8b7de47a4ac80 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -492,6 +492,44 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPackets::Spells::GetMirrorI if (!unit) return; + if (Creature* creature = unit->ToCreature()) + { + int32 outfitId = creature->GetOutfit(); + if (outfitId < 0) + { + const CreatureOutfitContainer& outfits = sObjectMgr->GetCreatureOutfitMap(); + CreatureOutfitContainer::const_iterator it = outfits.find(-outfitId); + if (it != outfits.end()) + { + CreatureOutfit const& outfit = it->second; + if (creature->GetDisplayId() == outfit.displayId) + { + WorldPackets::Spells::MirrorImageComponentedData packet; + packet.UnitGUID = guid; + packet.DisplayID = outfit.displayId; + packet.RaceID = outfit.race; + packet.Gender = outfit.gender; + packet.ClassID = outfit.Class; + packet.SkinColor = outfit.skin; + packet.FaceVariation = outfit.face; + packet.HairVariation = outfit.hair; + packet.HairColor = outfit.haircolor; + packet.BeardVariation = outfit.facialhair; + packet.GuildGUID = ObjectGuid::Empty; + + packet.ItemDisplayID.reserve(11); + + // item displays + for (auto const& display : it->second.outfit) + packet.ItemDisplayID.push_back(display); + + SendPacket(packet.Write()); + return; + } + } + } + } + if (!unit->HasAuraType(SPELL_AURA_CLONE_CASTER)) return; diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 72750f97ade9b..37035d723bf87 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -1356,6 +1356,7 @@ uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) co case CMSG_CHAT_MESSAGE_YELL: // 0 3.5 case CMSG_INSPECT: // 0 3.5 case CMSG_AREA_SPIRIT_HEALER_QUERY: // not profiled + case CMSG_GET_MIRROR_IMAGE_DATA: // not profiled case CMSG_STAND_STATE_CHANGE: // not profiled case CMSG_RANDOM_ROLL: // not profiled case CMSG_TIME_SYNC_RESPONSE: // not profiled diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 7ce328e1ab032..b036ad4077002 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -2125,7 +2125,7 @@ void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode, { uint32 model_id = 0; - if (uint32 modelid = ci->GetRandomValidModelId()) + if (uint32 modelid = sObjectMgr->GetCreatureDisplay(ci->GetRandomValidModelId())) model_id = modelid; // Will use the default model here target->SetDisplayId(model_id); @@ -2166,7 +2166,7 @@ void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode, uint32 cr_id = target->GetAuraEffectsByType(SPELL_AURA_MOUNTED).front()->GetMiscValue(); if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(cr_id)) { - uint32 displayID = ObjectMgr::ChooseDisplayId(ci); + uint32 displayID = sObjectMgr->GetCreatureDisplay(ObjectMgr::ChooseDisplayId(ci)); sObjectMgr->GetCreatureModelRandomGender(&displayID); target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, displayID); @@ -2648,7 +2648,7 @@ void AuraEffect::HandleAuraMounted(AuraApplication const* aurApp, uint8 mode, bo if (!displayId || vehicleId) { - displayId = ObjectMgr::ChooseDisplayId(creatureInfo); + displayId = sObjectMgr->GetCreatureDisplay(ObjectMgr::ChooseDisplayId(creatureInfo)); sObjectMgr->GetCreatureModelRandomGender(&displayId); } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 677b40da5244b..db5dc4f5f224c 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1655,6 +1655,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Creature Model Based Info Data..."); sObjectMgr->LoadCreatureModelInfo(); + TC_LOG_INFO("server.loading", "Loading Creature template outfits..."); // must be before LoadCreatureTemplates + sObjectMgr->LoadCreatureOutfits(); + TC_LOG_INFO("server.loading", "Loading Creature templates..."); sObjectMgr->LoadCreatureTemplates(); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index ee59c6117e5e0..f9e36a9bf9e11 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -86,6 +86,7 @@ class reload_commandscript : public CommandScript { "creature_queststarter", rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_QUESTSTARTER, true, &HandleReloadCreatureQuestStarterCommand, "" }, { "creature_summon_groups", rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_SUMMON_GROUPS, true, &HandleReloadCreatureSummonGroupsCommand, "" }, { "creature_template", rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_TEMPLATE, true, &HandleReloadCreatureTemplateCommand, "" }, + { "creature_template_outfits", rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_TEMPLATE, true, &HandleReloadCreatureTemplateOutfitsCommand, "" }, { "criteria_data", rbac::RBAC_PERM_COMMAND_RELOAD_CRITERIA_DATA, true, &HandleReloadCriteriaDataCommand, "" }, { "disables", rbac::RBAC_PERM_COMMAND_RELOAD_DISABLES, true, &HandleReloadDisablesCommand, "" }, { "disenchant_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_DISENCHANT_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesDisenchantCommand, "" }, @@ -194,6 +195,7 @@ class reload_commandscript : public CommandScript HandleReloadGameTeleCommand(handler, ""); HandleReloadCreatureSummonGroupsCommand(handler, ""); + HandleReloadCreatureTemplateOutfitsCommand(handler, ""); HandleReloadVehicleAccessoryCommand(handler, ""); HandleReloadVehicleTemplateAccessoryCommand(handler, ""); @@ -452,6 +454,14 @@ class reload_commandscript : public CommandScript return true; } + static bool HandleReloadCreatureTemplateOutfitsCommand(ChatHandler* handler, const char* /*args*/) + { + TC_LOG_INFO("misc", "Loading Creature Outfits... (`creature_template_outfits`)"); + sObjectMgr->LoadCreatureOutfits(); + handler->SendGlobalGMSysMessage("DB table `creature_template_outfits` reloaded."); + return true; + } + static bool HandleReloadCreatureQuestStarterCommand(ChatHandler* handler, const char* /*args*/) { TC_LOG_INFO("misc", "Loading Quests Relations... (`creature_queststarter`)"); diff --git a/src/server/scripts/Custom/DressNPCs/Example.sql b/src/server/scripts/Custom/DressNPCs/Example.sql new file mode 100644 index 0000000000000..8f8d3524f8483 --- /dev/null +++ b/src/server/scripts/Custom/DressNPCs/Example.sql @@ -0,0 +1,7 @@ +SET @NPCENTRY := 6; + +INSERT INTO `creature_template_outfits` (`entry`, `race`, `gender`, `skin`, `face`, `hair`, `haircolor`, `facialhair`, `head`, `shoulders`, `body`, `chest`, `waist`, `legs`, `feet`, `wrists`, `hands`, `back`, `tabard`) +VALUES (123, 11, 1, 14, 4, 10, 3, 5, -31286, -43617, 0, -26267, -26270, -26272, 0, 0, -43698, 0, 0); +INSERT INTO `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) +VALUES (@NPCENTRY, 2, 32946, 32945, 0); +UPDATE `creature_template` SET `modelid2` = -123 WHERE `entry` = @NPCENTRY; diff --git a/src/server/scripts/Custom/DressNPCs/README.md b/src/server/scripts/Custom/DressNPCs/README.md new file mode 100644 index 0000000000000..4f25bdc480762 --- /dev/null +++ b/src/server/scripts/Custom/DressNPCs/README.md @@ -0,0 +1,47 @@ +#DressNPCs [![Build Status](https://travis-ci.org/Rochet2/TrinityCore.svg?branch=dressnpcs_6.x)](https://travis-ci.org/Rochet2/TrinityCore) + +####About +You can make an NPC, set the displays or items you want him to have and his race and gender (defines displayID) as well as facial features and skin color. +All this is done in the database. No client edits needed. Completely server side. +Source: http://rochet2.github.io/Dress-NPCs.html + +Known bugs: +- Portraits of the NPCs may not work properly at times - a client side visual bug. +- NPCs have no sound replies when you talk to them. This is because of the models used and there is no fix without editing client. + +####Installation + +Available as: +- Direct merge: https://github.com/Rochet2/TrinityCore/tree/dressnpcs_6.x +- Diff: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:6.x...dressnpcs_6.x.diff +- Diff in github view: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:6.x...dressnpcs_6.x + +Using direct merge: +- open git bash to source location +- do `git remote add rochet2 https://github.com/Rochet2/TrinityCore.git` +- do `git pull rochet2 dressnpcs_6.x` +- use cmake and compile + +Using diff: +- DO NOT COPY THE DIFF DIRECTLY! It causes apply to fail. +- download the diff by __right clicking__ the link and select __Save link as__ +- place the downloaded `dressnpcs_6.x.diff` to the source root folder +- open git bash to source location +- do `git apply dressnpcs_6.x.diff` +- use cmake and compile + +After compiling: +- TrinityCore auto updater should run needed SQLs automatically. +- If you do not use the auto updater then run files named `*_dressnpcs.sql` from `\sql\custom` to your databases. + +####Usage +Create a row to `creature_template_outfits` table with your desired race, class, gender and equipped items. +***Note*** The items can use positive value as item entry and negative for displayid. +Create an NPC. Set the `creature_template_outfits` entry to the modelID column in `creature_template`, but __make it negative__. +Clear wow cache folder, restart server and spawn the NPC. +The patch also adds `.reload creature_template_outfit` command. You can use it to reload the creature outfit table again for testing. +You should be able to reload the table with new entries of ingame creatures. Relog to update the visual look of creatures with the reloaded data. +__Using reload is not recommended.__ (not outfit nor template) Use them only for testing and debugging purposes. Not for live servers. + +####Bugs and Contact +Report issues and similar to http://rochet2.github.io/ From 88ee8cac6ea8db7ea7933ea4c935169f137abeb9 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 7 Nov 2016 00:28:27 +0200 Subject: [PATCH 02/35] Add gitignored files --- .../world/2016_07_24_00_world_dressnpcs.sql | 31 +++++++++++++++++++ .../world/2016_07_24_01_world_dressnpcs.sql | 2 ++ 2 files changed, 33 insertions(+) create mode 100644 sql/custom/world/2016_07_24_00_world_dressnpcs.sql create mode 100644 sql/custom/world/2016_07_24_01_world_dressnpcs.sql diff --git a/sql/custom/world/2016_07_24_00_world_dressnpcs.sql b/sql/custom/world/2016_07_24_00_world_dressnpcs.sql new file mode 100644 index 0000000000000..387cf774ae92f --- /dev/null +++ b/sql/custom/world/2016_07_24_00_world_dressnpcs.sql @@ -0,0 +1,31 @@ +CREATE TABLE IF NOT EXISTS `creature_template_outfits` ( + `entry` INT(10) UNSIGNED NOT NULL, + `race` TINYINT(3) UNSIGNED NOT NULL DEFAULT '1', + `gender` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' COMMENT '0 for male, 1 for female', + `skin` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `face` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `hair` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `haircolor` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `facialhair` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `head` INT(10) NOT NULL DEFAULT '0', + `shoulders` INT(10) NOT NULL DEFAULT '0', + `body` INT(10) NOT NULL DEFAULT '0', + `chest` INT(10) NOT NULL DEFAULT '0', + `waist` INT(10) NOT NULL DEFAULT '0', + `legs` INT(10) NOT NULL DEFAULT '0', + `feet` INT(10) NOT NULL DEFAULT '0', + `wrists` INT(10) NOT NULL DEFAULT '0', + `hands` INT(10) NOT NULL DEFAULT '0', + `back` INT(10) NOT NULL DEFAULT '0', + `tabard` INT(10) NOT NULL DEFAULT '0', + PRIMARY KEY (`entry`) +) +COMMENT='Use positive values for item entries and negative to use item displayid for head, shoulders etc.' +COLLATE='utf8_general_ci' +ENGINE=InnoDB; + +ALTER TABLE `creature_template` + CHANGE COLUMN `modelid1` `modelid1` INT NOT NULL DEFAULT '0' AFTER `KillCredit2`, + CHANGE COLUMN `modelid2` `modelid2` INT NOT NULL DEFAULT '0' AFTER `modelid1`, + CHANGE COLUMN `modelid3` `modelid3` INT NOT NULL DEFAULT '0' AFTER `modelid2`, + CHANGE COLUMN `modelid4` `modelid4` INT NOT NULL DEFAULT '0' AFTER `modelid3`; diff --git a/sql/custom/world/2016_07_24_01_world_dressnpcs.sql b/sql/custom/world/2016_07_24_01_world_dressnpcs.sql new file mode 100644 index 0000000000000..cd6e5b9623383 --- /dev/null +++ b/sql/custom/world/2016_07_24_01_world_dressnpcs.sql @@ -0,0 +1,2 @@ +ALTER TABLE `creature_template_outfits` + ADD COLUMN `class` TINYINT(3) UNSIGNED NOT NULL DEFAULT '1' AFTER `race`; From 3fef50bea90a99a752c17d8f78a5d3a31bc5be6f Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 7 Nov 2016 00:30:17 +0200 Subject: [PATCH 03/35] Update for 7.x support. Closes https://github.com/Rochet2/TrinityCore/issues/17 --- .../world/2016_11_06_00_world_dressnpcs.sql | 18 ++++++ src/server/game/Globals/ObjectMgr.cpp | 14 +++-- src/server/game/Globals/ObjectMgr.h | 6 +- src/server/game/Handlers/SpellHandler.cpp | 56 +++++++++++++------ .../scripts/Custom/DressNPCs/Example.sql | 2 - src/server/scripts/Custom/DressNPCs/README.md | 15 ++--- 6 files changed, 77 insertions(+), 34 deletions(-) create mode 100644 sql/custom/world/2016_11_06_00_world_dressnpcs.sql diff --git a/sql/custom/world/2016_11_06_00_world_dressnpcs.sql b/sql/custom/world/2016_11_06_00_world_dressnpcs.sql new file mode 100644 index 0000000000000..255748a439332 --- /dev/null +++ b/sql/custom/world/2016_11_06_00_world_dressnpcs.sql @@ -0,0 +1,18 @@ +ALTER TABLE `creature_template_outfits` + ADD COLUMN `feature1` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `facialhair`, + ADD COLUMN `feature2` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `feature1`, + ADD COLUMN `feature3` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `feature2`; + +ALTER TABLE `creature_template_outfits` + COMMENT='Use positive values for item entries and negative to use item displayid for head, shoulders etc.\r\nTo use special appearances either find the displayid for it or use ((AppearanceModID << 32) | item_entry). The appearance IDs are usually from 0 to 10.', + CHANGE COLUMN `head` `head` BIGINT NOT NULL DEFAULT '0' AFTER `feature3`, + CHANGE COLUMN `shoulders` `shoulders` BIGINT NOT NULL DEFAULT '0' AFTER `head`, + CHANGE COLUMN `body` `body` BIGINT NOT NULL DEFAULT '0' AFTER `shoulders`, + CHANGE COLUMN `chest` `chest` BIGINT NOT NULL DEFAULT '0' AFTER `body`, + CHANGE COLUMN `waist` `waist` BIGINT NOT NULL DEFAULT '0' AFTER `chest`, + CHANGE COLUMN `legs` `legs` BIGINT NOT NULL DEFAULT '0' AFTER `waist`, + CHANGE COLUMN `feet` `feet` BIGINT NOT NULL DEFAULT '0' AFTER `legs`, + CHANGE COLUMN `wrists` `wrists` BIGINT NOT NULL DEFAULT '0' AFTER `feet`, + CHANGE COLUMN `hands` `hands` BIGINT NOT NULL DEFAULT '0' AFTER `wrists`, + CHANGE COLUMN `back` `back` BIGINT NOT NULL DEFAULT '0' AFTER `hands`, + CHANGE COLUMN `tabard` `tabard` BIGINT NOT NULL DEFAULT '0' AFTER `back`; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index f3d92c534cd4e..98b77607a5a6c 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8060,7 +8060,7 @@ void ObjectMgr::LoadCreatureOutfits() _creatureOutfitStore.clear(); // for reload case (test only) - QueryResult result = WorldDatabase.Query("SELECT entry, race, class, gender, skin, face, hair, haircolor, facialhair, " + QueryResult result = WorldDatabase.Query("SELECT entry, race, class, gender, skin, face, hair, haircolor, facialhair, feature1, feature2, feature3, " "head, shoulders, body, chest, waist, legs, feet, wrists, hands, tabard, back FROM creature_template_outfits"); if (!result) @@ -8111,12 +8111,16 @@ void ObjectMgr::LoadCreatureOutfits() co.hair = fields[i++].GetUInt8(); co.haircolor = fields[i++].GetUInt8(); co.facialhair = fields[i++].GetUInt8(); - for (uint32 j = 0; j < MAX_CREATURE_OUTFIT_DISPLAYS; ++j) + for (uint32 j = 0; j < CreatureOutfit::max_custom_displays; ++j) + co.customdisplay[j] = fields[i++].GetUInt8(); + for (uint32 j = 0; j < CreatureOutfit::max_outfit_displays; ++j) { - int32 displayInfo = fields[i + j].GetInt32(); + int64 displayInfo = fields[i + j].GetInt64(); if (displayInfo > 0) // entry { - if (uint32 display = sDB2Manager.GetItemDisplayId(static_cast(displayInfo), 0)) + uint32 entry = static_cast(displayInfo & 0xFFFFFFFF); + uint32 appearancemodid = static_cast(displayInfo >> 32); + if (uint32 display = sDB2Manager.GetItemDisplayId(entry, appearancemodid)) co.outfit[j] = display; else { @@ -8125,7 +8129,7 @@ void ObjectMgr::LoadCreatureOutfits() } } else // display - co.outfit[j] = uint32(-displayInfo); + co.outfit[j] = static_cast(-displayInfo); } _creatureOutfitStore[entry] = co; diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index fe3538b7cdf29..600e7ad697e18 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -137,9 +137,10 @@ struct GameTele typedef std::unordered_map GameTeleContainer; -#define MAX_CREATURE_OUTFIT_DISPLAYS 11 struct CreatureOutfit { + static constexpr uint32 max_outfit_displays = 11; + static constexpr uint32 max_custom_displays = 3; uint8 race; uint8 Class; uint8 gender; @@ -148,8 +149,9 @@ struct CreatureOutfit uint8 hair; uint8 facialhair; uint8 haircolor; + uint8 customdisplay[3]; uint32 displayId; - uint32 outfit[MAX_CREATURE_OUTFIT_DISPLAYS]; + uint32 outfit[max_outfit_displays]; }; typedef std::unordered_map CreatureOutfitContainer; diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 8b7de47a4ac80..1bc76e8f999d0 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -504,26 +504,46 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPackets::Spells::GetMirrorI CreatureOutfit const& outfit = it->second; if (creature->GetDisplayId() == outfit.displayId) { - WorldPackets::Spells::MirrorImageComponentedData packet; - packet.UnitGUID = guid; - packet.DisplayID = outfit.displayId; - packet.RaceID = outfit.race; - packet.Gender = outfit.gender; - packet.ClassID = outfit.Class; - packet.SkinColor = outfit.skin; - packet.FaceVariation = outfit.face; - packet.HairVariation = outfit.hair; - packet.HairColor = outfit.haircolor; - packet.BeardVariation = outfit.facialhair; - packet.GuildGUID = ObjectGuid::Empty; - - packet.ItemDisplayID.reserve(11); - - // item displays + WorldPackets::Spells::MirrorImageComponentedData mirrorImageComponentedData; + mirrorImageComponentedData.UnitGUID = guid; + mirrorImageComponentedData.DisplayID = outfit.displayId; + mirrorImageComponentedData.RaceID = outfit.race; + mirrorImageComponentedData.Gender = outfit.gender; + mirrorImageComponentedData.ClassID = outfit.Class; + + mirrorImageComponentedData.SkinColor = outfit.skin; + mirrorImageComponentedData.FaceVariation = outfit.face; + mirrorImageComponentedData.HairVariation = outfit.hair; + mirrorImageComponentedData.HairColor = outfit.haircolor; + mirrorImageComponentedData.BeardVariation = outfit.facialhair; + + static_assert(CreatureOutfit::max_custom_displays == PLAYER_CUSTOM_DISPLAY_SIZE, "Amount of custom displays for player has changed - change it for dressnpcs as well"); + for (uint32 i = 0; i < PLAYER_CUSTOM_DISPLAY_SIZE; ++i) + mirrorImageComponentedData.CustomDisplay[i] = outfit.customdisplay[i]; + mirrorImageComponentedData.GuildGUID = ObjectGuid::Empty; + + mirrorImageComponentedData.ItemDisplayID.reserve(11); + + static EquipmentSlots const itemSlots[] = + { + EQUIPMENT_SLOT_HEAD, + EQUIPMENT_SLOT_SHOULDERS, + EQUIPMENT_SLOT_BODY, + EQUIPMENT_SLOT_CHEST, + EQUIPMENT_SLOT_WAIST, + EQUIPMENT_SLOT_LEGS, + EQUIPMENT_SLOT_FEET, + EQUIPMENT_SLOT_WRISTS, + EQUIPMENT_SLOT_HANDS, + EQUIPMENT_SLOT_TABARD, + EQUIPMENT_SLOT_BACK, + EQUIPMENT_SLOT_END + }; + for (auto const& display : it->second.outfit) - packet.ItemDisplayID.push_back(display); + mirrorImageComponentedData.ItemDisplayID.push_back(display); - SendPacket(packet.Write()); + SendPacket(mirrorImageComponentedData.Write()); return; } } diff --git a/src/server/scripts/Custom/DressNPCs/Example.sql b/src/server/scripts/Custom/DressNPCs/Example.sql index 8f8d3524f8483..ac739a6239a3f 100644 --- a/src/server/scripts/Custom/DressNPCs/Example.sql +++ b/src/server/scripts/Custom/DressNPCs/Example.sql @@ -2,6 +2,4 @@ SET @NPCENTRY := 6; INSERT INTO `creature_template_outfits` (`entry`, `race`, `gender`, `skin`, `face`, `hair`, `haircolor`, `facialhair`, `head`, `shoulders`, `body`, `chest`, `waist`, `legs`, `feet`, `wrists`, `hands`, `back`, `tabard`) VALUES (123, 11, 1, 14, 4, 10, 3, 5, -31286, -43617, 0, -26267, -26270, -26272, 0, 0, -43698, 0, 0); -INSERT INTO `creature_equip_template` (`entry`, `id`, `itemEntry1`, `itemEntry2`, `itemEntry3`) -VALUES (@NPCENTRY, 2, 32946, 32945, 0); UPDATE `creature_template` SET `modelid2` = -123 WHERE `entry` = @NPCENTRY; diff --git a/src/server/scripts/Custom/DressNPCs/README.md b/src/server/scripts/Custom/DressNPCs/README.md index 4f25bdc480762..48ee9a89ffb7f 100644 --- a/src/server/scripts/Custom/DressNPCs/README.md +++ b/src/server/scripts/Custom/DressNPCs/README.md @@ -1,4 +1,4 @@ -#DressNPCs [![Build Status](https://travis-ci.org/Rochet2/TrinityCore.svg?branch=dressnpcs_6.x)](https://travis-ci.org/Rochet2/TrinityCore) +#DressNPCs [![Build Status](https://travis-ci.org/Rochet2/TrinityCore.svg?branch=dressnpcs_7.x)](https://travis-ci.org/Rochet2/TrinityCore) ####About You can make an NPC, set the displays or items you want him to have and his race and gender (defines displayID) as well as facial features and skin color. @@ -12,22 +12,22 @@ Known bugs: ####Installation Available as: -- Direct merge: https://github.com/Rochet2/TrinityCore/tree/dressnpcs_6.x -- Diff: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:6.x...dressnpcs_6.x.diff -- Diff in github view: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:6.x...dressnpcs_6.x +- Direct merge: https://github.com/Rochet2/TrinityCore/tree/dressnpcs_7.x +- Diff: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:master...dressnpcs_7.x.diff +- Diff in github view: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:master...dressnpcs_7.x Using direct merge: - open git bash to source location - do `git remote add rochet2 https://github.com/Rochet2/TrinityCore.git` -- do `git pull rochet2 dressnpcs_6.x` +- do `git pull rochet2 dressnpcs_7.x` - use cmake and compile Using diff: - DO NOT COPY THE DIFF DIRECTLY! It causes apply to fail. - download the diff by __right clicking__ the link and select __Save link as__ -- place the downloaded `dressnpcs_6.x.diff` to the source root folder +- place the downloaded `dressnpcs_7.x.diff` to the source root folder - open git bash to source location -- do `git apply dressnpcs_6.x.diff` +- do `git apply dressnpcs_7.x.diff` - use cmake and compile After compiling: @@ -37,6 +37,7 @@ After compiling: ####Usage Create a row to `creature_template_outfits` table with your desired race, class, gender and equipped items. ***Note*** The items can use positive value as item entry and negative for displayid. +***Note*** After wotlk the display system has changed slightly. If you want special appearance on items, find the correct displayid and use it or use an entry which has the appearance id added to it in the following way `((AppearanceModID << 32) | item_entry)`. Appearances are usually between 0 and 10. Create an NPC. Set the `creature_template_outfits` entry to the modelID column in `creature_template`, but __make it negative__. Clear wow cache folder, restart server and spawn the NPC. The patch also adds `.reload creature_template_outfit` command. You can use it to reload the creature outfit table again for testing. From 6c70a7b55bfca1798c41cb05270faae5241053cf Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 7 Nov 2016 00:53:21 +0200 Subject: [PATCH 04/35] Fix linux errors --- src/server/game/Globals/ObjectMgr.cpp | 6 +++--- src/server/game/Handlers/SpellHandler.cpp | 17 ----------------- 2 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 98b77607a5a6c..f5f22cd395b9f 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8118,13 +8118,13 @@ void ObjectMgr::LoadCreatureOutfits() int64 displayInfo = fields[i + j].GetInt64(); if (displayInfo > 0) // entry { - uint32 entry = static_cast(displayInfo & 0xFFFFFFFF); + uint32 item_entry = static_cast(displayInfo & 0xFFFFFFFF); uint32 appearancemodid = static_cast(displayInfo >> 32); - if (uint32 display = sDB2Manager.GetItemDisplayId(entry, appearancemodid)) + if (uint32 display = sDB2Manager.GetItemDisplayId(item_entry, appearancemodid)) co.outfit[j] = display; else { - TC_LOG_ERROR("server.loading", ">> Creature entry %u in `creature_template_outfits` has invalid item entry %i", entry, displayInfo); + TC_LOG_ERROR("server.loading", ">> Creature entry %u in `creature_template_outfits` has invalid (item entry, appearance) combination: %u, %u. Value in DB: %s", entry, item_entry, appearancemodid, std::to_string(displayInfo)); co.outfit[j] = 0; } } diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 1bc76e8f999d0..545ca9bb7b50d 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -523,23 +523,6 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPackets::Spells::GetMirrorI mirrorImageComponentedData.GuildGUID = ObjectGuid::Empty; mirrorImageComponentedData.ItemDisplayID.reserve(11); - - static EquipmentSlots const itemSlots[] = - { - EQUIPMENT_SLOT_HEAD, - EQUIPMENT_SLOT_SHOULDERS, - EQUIPMENT_SLOT_BODY, - EQUIPMENT_SLOT_CHEST, - EQUIPMENT_SLOT_WAIST, - EQUIPMENT_SLOT_LEGS, - EQUIPMENT_SLOT_FEET, - EQUIPMENT_SLOT_WRISTS, - EQUIPMENT_SLOT_HANDS, - EQUIPMENT_SLOT_TABARD, - EQUIPMENT_SLOT_BACK, - EQUIPMENT_SLOT_END - }; - for (auto const& display : it->second.outfit) mirrorImageComponentedData.ItemDisplayID.push_back(display); From e449a7e39ed51d31586c5a1b2614605713ac2e09 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 7 Nov 2016 01:19:16 +0200 Subject: [PATCH 05/35] Fix linux --- src/server/game/Globals/ObjectMgr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index f5f22cd395b9f..325d5146a634a 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8124,7 +8124,7 @@ void ObjectMgr::LoadCreatureOutfits() co.outfit[j] = display; else { - TC_LOG_ERROR("server.loading", ">> Creature entry %u in `creature_template_outfits` has invalid (item entry, appearance) combination: %u, %u. Value in DB: %s", entry, item_entry, appearancemodid, std::to_string(displayInfo)); + TC_LOG_ERROR("server.loading", ">> Creature entry %u in `creature_template_outfits` has invalid (item entry, appearance) combination: %u, %u. Value in DB: %s", entry, item_entry, appearancemodid, std::to_string(displayInfo).c_str()); co.outfit[j] = 0; } } From 61c2f8cf4210a8fdb8b8d261933bef9479fb31e8 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sun, 2 Apr 2017 00:39:26 +0300 Subject: [PATCH 06/35] Update README.md --- src/server/scripts/Custom/DressNPCs/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/server/scripts/Custom/DressNPCs/README.md b/src/server/scripts/Custom/DressNPCs/README.md index 48ee9a89ffb7f..68b320c9ada4d 100644 --- a/src/server/scripts/Custom/DressNPCs/README.md +++ b/src/server/scripts/Custom/DressNPCs/README.md @@ -1,6 +1,6 @@ -#DressNPCs [![Build Status](https://travis-ci.org/Rochet2/TrinityCore.svg?branch=dressnpcs_7.x)](https://travis-ci.org/Rochet2/TrinityCore) +# DressNPCs [![Build Status](https://travis-ci.org/Rochet2/TrinityCore.svg?branch=dressnpcs_7.x)](https://travis-ci.org/Rochet2/TrinityCore) -####About +#### About You can make an NPC, set the displays or items you want him to have and his race and gender (defines displayID) as well as facial features and skin color. All this is done in the database. No client edits needed. Completely server side. Source: http://rochet2.github.io/Dress-NPCs.html @@ -9,7 +9,7 @@ Known bugs: - Portraits of the NPCs may not work properly at times - a client side visual bug. - NPCs have no sound replies when you talk to them. This is because of the models used and there is no fix without editing client. -####Installation +#### Installation Available as: - Direct merge: https://github.com/Rochet2/TrinityCore/tree/dressnpcs_7.x @@ -34,7 +34,7 @@ After compiling: - TrinityCore auto updater should run needed SQLs automatically. - If you do not use the auto updater then run files named `*_dressnpcs.sql` from `\sql\custom` to your databases. -####Usage +#### Usage Create a row to `creature_template_outfits` table with your desired race, class, gender and equipped items. ***Note*** The items can use positive value as item entry and negative for displayid. ***Note*** After wotlk the display system has changed slightly. If you want special appearance on items, find the correct displayid and use it or use an entry which has the appearance id added to it in the following way `((AppearanceModID << 32) | item_entry)`. Appearances are usually between 0 and 10. @@ -44,5 +44,5 @@ The patch also adds `.reload creature_template_outfit` command. You can use it t You should be able to reload the table with new entries of ingame creatures. Relog to update the visual look of creatures with the reloaded data. __Using reload is not recommended.__ (not outfit nor template) Use them only for testing and debugging purposes. Not for live servers. -####Bugs and Contact +#### Bugs and Contact Report issues and similar to http://rochet2.github.io/ From 61f7c22e1a16077e5c3e3f6fda7d09ade64e5926 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Wed, 26 Apr 2017 19:55:33 +0300 Subject: [PATCH 07/35] Recode applying of mirror image flag and display. Closes https://github.com/Rochet2/TrinityCore/issues/52 --- .../game/AI/SmartScripts/SmartScript.cpp | 16 +++--- .../game/Entities/Creature/Creature.cpp | 47 +++++++++++++---- src/server/game/Entities/Creature/Creature.h | 8 +++ src/server/game/Globals/ObjectMgr.cpp | 2 +- src/server/game/Handlers/SpellHandler.cpp | 51 +++++++++---------- src/server/scripts/Commands/cs_modify.cpp | 3 ++ src/server/scripts/Commands/cs_npc.cpp | 2 + 7 files changed, 86 insertions(+), 43 deletions(-) diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index a8390c62732a8..951f7043d16a4 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -276,15 +276,19 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u Creature* crea = (*itr)->ToCreature(); ASSERT(crea); crea->SetOutfit(ObjectMgr::ChooseDisplayId(ci)); - uint32 displayId = sObjectMgr->GetCreatureDisplay(crea->GetOutfit()); - if (crea->GetOutfit() < 0 && displayId) - (*itr)->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); + if (crea->IsMirrorImage()) + { + new MirrorImageUpdate(crea); + } else + { (*itr)->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); - (*itr)->ToCreature()->SetDisplayId(displayId); - TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL: Creature entry %u, %s set displayid to %u", - (*itr)->GetEntry(), (*itr)->GetGUID().ToString().c_str(), displayId); + uint32 displayId = sObjectMgr->GetCreatureDisplay(crea->GetOutfit()); + (*itr)->ToCreature()->SetDisplayId(displayId); + TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL: Creature entry %u, %s set displayid to %u", + (*itr)->GetEntry(), (*itr)->GetGUID().ToString().c_str(), displayId); + } } } //if no param1, then use value from param2 (modelId) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 87633474a51af..90a19270aed4c 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -148,7 +148,6 @@ uint32 CreatureTemplate::GetFirstVisibleModel() const return 17519; } - bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) { if (Unit* victim = ObjectAccessor::GetUnit(m_owner, m_victim)) @@ -359,10 +358,9 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) SetOutfit(ObjectMgr::ChooseDisplayId(GetCreatureTemplate(), data)); uint32 displayID = sObjectMgr->GetCreatureDisplay(GetOutfit()); - if (GetOutfit() < 0 && displayID) - SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); - else - RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); + if (IsMirrorImage()) + displayID = 11686; // invisible in beginning if a mirror image + RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&displayID); if (!minfo) // Cancel load if no model defined @@ -439,10 +437,9 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, SetUInt64Value(UNIT_NPC_FLAGS, npcflag); SetUInt32Value(UNIT_FIELD_FLAGS, unit_flags); - if (GetOutfit() < 0 && GetDisplayId()) - SetUInt32Value(UNIT_FIELD_FLAGS_2, cInfo->unit_flags2 | UNIT_FLAG2_MIRROR_IMAGE); - else - SetUInt32Value(UNIT_FIELD_FLAGS_2, cInfo->unit_flags2); + SetUInt32Value(UNIT_FIELD_FLAGS_2, cInfo->unit_flags2); + if (IsMirrorImage()) + new MirrorImageUpdate(this); SetUInt32Value(OBJECT_DYNAMIC_FLAGS, dynamicflags); @@ -1099,6 +1096,8 @@ void Creature::SaveToDB(uint32 mapid, uint32 spawnMask, uint32 phaseMask) CreatureData& data = sObjectMgr->NewOrExistCreatureData(m_spawnId); uint32 displayId = GetNativeDisplayId(); + if (IsMirrorImage()) + displayId = 0; // For mirror images dont save displayid, it comes from outfit uint64 npcflag = GetUInt64Value(UNIT_NPC_FLAGS); uint32 unit_flags = GetUInt32Value(UNIT_FIELD_FLAGS); uint32 dynamicflags = GetUInt32Value(OBJECT_DYNAMIC_FLAGS); @@ -1797,6 +1796,8 @@ void Creature::Respawn(bool force) { SetDisplayId(displayID); SetNativeDisplayId(displayID); + if (IsMirrorImage()) + new MirrorImageUpdate(this); SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender); } @@ -2915,3 +2916,31 @@ void Creature::ClearTextRepeatGroup(uint8 textGroup) if (groupItr != m_textRepeat.end()) groupItr->second.clear(); } + +MirrorImageUpdate::MirrorImageUpdate(Creature* creature) : BasicEvent(), creature(creature) +{ + static uint32 delay = 1; + creature->m_Events.AddEvent(this, creature->m_Events.CalculateTime(delay)); + creature->SetDisplayId(11686); // invisible in beginning if a mirror image + creature->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); +} + +bool MirrorImageUpdate::Execute(uint64 /*e_time*/, uint32 /*p_time*/) +{ + // From AuraEffect::HandleAuraCloneCaster + int32 outfitId = creature->GetOutfit(); + if (outfitId < 0) + { + const CreatureOutfitContainer& outfits = sObjectMgr->GetCreatureOutfitMap(); + auto it = outfits.find(-outfitId); + if (it != outfits.end()) + { + creature->SetDisplayId(it->second.displayId); + creature->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); + return true; + } + } + creature->SetDisplayId(creature->GetNativeDisplayId()); + creature->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); + return true; +} diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 6cb791bc0dc84..f20b571caa828 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -717,6 +717,7 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void SetOutfit(int32 outfit) { outfitId = outfit; }; int32 GetOutfit() const { return outfitId; }; + bool IsMirrorImage() const { return outfitId < 0; }; void DisappearAndDie(); @@ -1071,4 +1072,11 @@ class TC_GAME_API ForcedDespawnDelayEvent : public BasicEvent Seconds const m_respawnTimer; }; +struct MirrorImageUpdate : BasicEvent +{ + MirrorImageUpdate(Creature* creature); + bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override; + Creature* creature; +}; + #endif diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index b86cb36896641..f339454b2adc9 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -9605,7 +9605,7 @@ uint32 ObjectMgr::GetCreatureDisplay(int32 modelid) const const CreatureOutfitContainer& outfits = GetCreatureOutfitMap(); CreatureOutfitContainer::const_iterator it = outfits.find(-modelid); if (it != outfits.end()) - return it->second.displayId; + return 11686; // invisible for mirror image return 0; } diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 35f3ebf9137e6..4c85e000d847c 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -506,33 +506,30 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPackets::Spells::GetMirrorI if (it != outfits.end()) { CreatureOutfit const& outfit = it->second; - if (creature->GetDisplayId() == outfit.displayId) - { - WorldPackets::Spells::MirrorImageComponentedData mirrorImageComponentedData; - mirrorImageComponentedData.UnitGUID = guid; - mirrorImageComponentedData.DisplayID = outfit.displayId; - mirrorImageComponentedData.RaceID = outfit.race; - mirrorImageComponentedData.Gender = outfit.gender; - mirrorImageComponentedData.ClassID = outfit.Class; - - mirrorImageComponentedData.SkinColor = outfit.skin; - mirrorImageComponentedData.FaceVariation = outfit.face; - mirrorImageComponentedData.HairVariation = outfit.hair; - mirrorImageComponentedData.HairColor = outfit.haircolor; - mirrorImageComponentedData.BeardVariation = outfit.facialhair; - - static_assert(CreatureOutfit::max_custom_displays == PLAYER_CUSTOM_DISPLAY_SIZE, "Amount of custom displays for player has changed - change it for dressnpcs as well"); - for (uint32 i = 0; i < PLAYER_CUSTOM_DISPLAY_SIZE; ++i) - mirrorImageComponentedData.CustomDisplay[i] = outfit.customdisplay[i]; - mirrorImageComponentedData.GuildGUID = ObjectGuid::Empty; - - mirrorImageComponentedData.ItemDisplayID.reserve(11); - for (auto const& display : it->second.outfit) - mirrorImageComponentedData.ItemDisplayID.push_back(display); - - SendPacket(mirrorImageComponentedData.Write()); - return; - } + WorldPackets::Spells::MirrorImageComponentedData mirrorImageComponentedData; + mirrorImageComponentedData.UnitGUID = guid; + mirrorImageComponentedData.DisplayID = outfit.displayId; + mirrorImageComponentedData.RaceID = outfit.race; + mirrorImageComponentedData.Gender = outfit.gender; + mirrorImageComponentedData.ClassID = outfit.Class; + + mirrorImageComponentedData.SkinColor = outfit.skin; + mirrorImageComponentedData.FaceVariation = outfit.face; + mirrorImageComponentedData.HairVariation = outfit.hair; + mirrorImageComponentedData.HairColor = outfit.haircolor; + mirrorImageComponentedData.BeardVariation = outfit.facialhair; + + static_assert(CreatureOutfit::max_custom_displays == PLAYER_CUSTOM_DISPLAY_SIZE, "Amount of custom displays for player has changed - change it for dressnpcs as well"); + for (uint32 i = 0; i < PLAYER_CUSTOM_DISPLAY_SIZE; ++i) + mirrorImageComponentedData.CustomDisplay[i] = outfit.customdisplay[i]; + mirrorImageComponentedData.GuildGUID = ObjectGuid::Empty; + + mirrorImageComponentedData.ItemDisplayID.reserve(11); + for (auto const& display : it->second.outfit) + mirrorImageComponentedData.ItemDisplayID.push_back(display); + + SendPacket(mirrorImageComponentedData.Write()); + return; } } } diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp index 4071a8f4ed290..def4c534f48a7 100644 --- a/src/server/scripts/Commands/cs_modify.cpp +++ b/src/server/scripts/Commands/cs_modify.cpp @@ -1037,6 +1037,9 @@ class modify_commandscript : public CommandScript return false; target->SetDisplayId(display_id); + target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); + if (Creature* crea = target->ToCreature()) + crea->SetOutfit(display_id); return true; } diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index ec7835b5654b7..6c99766b20084 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -904,6 +904,8 @@ class npc_commandscript : public CommandScript creature->SetDisplayId(displayId); creature->SetNativeDisplayId(displayId); + creature->SetOutfit(displayId); + creature->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); creature->SaveToDB(); From 1aada978352e587c5f48774c08f1efe923a05da4 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sun, 25 Jun 2017 01:43:29 +0300 Subject: [PATCH 08/35] Auto merge --- .travis.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 40994a7a122ed..d40b8f667c40c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ compiler: - clang git: - depth: 1 + depth: 100 addons: apt: @@ -28,11 +28,12 @@ services: - mysql before_install: - - git config user.email "travis@build.bot" && git config user.name "Travis CI" + - git config user.email "rochet2@post.com" && git config user.name "Rochet2" - git tag -a -m "Travis build" init install: - mysql -uroot -e 'create database test_mysql;' + - if [ "${TRAVIS_EVENT_TYPE}" == "cron" ]; then git pull https://github.com/TrinityCore/TrinityCore.git 3.3.5; fi; - mkdir bin - cd bin - cmake ../ -DWITH_WARNINGS=1 -DWITH_COREDEBUG=0 -DUSE_COREPCH=1 -DUSE_SCRIPTPCH=1 -DTOOLS=1 -DSCRIPTS=dynamic -DSERVERS=1 -DNOJEM=1 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS="-Werror" -DCMAKE_CXX_FLAGS="-Werror" -DCMAKE_C_FLAGS_DEBUG="-DNDEBUG" -DCMAKE_CXX_FLAGS_DEBUG="-DNDEBUG" -DCMAKE_INSTALL_PREFIX=check_install @@ -50,9 +51,15 @@ script: - mysql -utrinity -ptrinity hotfixes < sql/base/dev/hotfixes_database.sql - cat sql/updates/world/master/*.sql | mysql -utrinity -ptrinity world - cat sql/updates/hotfixes/master/*.sql | mysql -utrinity -ptrinity hotfixes + - cat sql/custom/auth/*.sql | mysql -utrinity -ptrinity auth + - cat sql/custom/characters/*.sql | mysql -utrinity -ptrinity characters + - cat sql/custom/world/*.sql | mysql -utrinity -ptrinity world - mysql -uroot < sql/create/drop_mysql.sql - cd bin - make -j 4 -k && make install - cd check_install/bin - ./bnetserver --version - ./worldserver --version + +after_success: + - if [ "${TRAVIS_EVENT_TYPE}" == "cron" ]; then git push https://${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git HEAD:${TRAVIS_BRANCH}; fi; From 20f2aa41234fad3bb88984f33a6775498a1c4b6f Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sun, 25 Jun 2017 01:46:34 +0300 Subject: [PATCH 09/35] Fix branch --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d40b8f667c40c..29b1f4b6a3107 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,7 @@ before_install: install: - mysql -uroot -e 'create database test_mysql;' - - if [ "${TRAVIS_EVENT_TYPE}" == "cron" ]; then git pull https://github.com/TrinityCore/TrinityCore.git 3.3.5; fi; + - if [ "${TRAVIS_EVENT_TYPE}" == "cron" ]; then git pull https://github.com/TrinityCore/TrinityCore.git master; fi; - mkdir bin - cd bin - cmake ../ -DWITH_WARNINGS=1 -DWITH_COREDEBUG=0 -DUSE_COREPCH=1 -DUSE_SCRIPTPCH=1 -DTOOLS=1 -DSCRIPTS=dynamic -DSERVERS=1 -DNOJEM=1 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS="-Werror" -DCMAKE_CXX_FLAGS="-Werror" -DCMAKE_C_FLAGS_DEBUG="-DNDEBUG" -DCMAKE_CXX_FLAGS_DEBUG="-DNDEBUG" -DCMAKE_INSTALL_PREFIX=check_install From a729ad24a7a3f504341dfe6cedb206d45ac02d26 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Wed, 3 Jan 2018 17:48:08 +0200 Subject: [PATCH 10/35] Minor adjustments --- src/server/game/Globals/ObjectMgr.cpp | 2 +- src/server/game/Globals/ObjectMgr.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index a0d18213bc2a9..c2865363cbe17 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8352,7 +8352,7 @@ void ObjectMgr::LoadCreatureOutfits() co.outfit[j] = display; else { - TC_LOG_ERROR("server.loading", ">> Creature entry %u in `creature_template_outfits` has invalid (item entry, appearance) combination: %u, %u. Value in DB: %s", entry, item_entry, appearancemodid, std::to_string(displayInfo).c_str()); + TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has invalid (item entry, appearance) combination: %u, %u. Value in DB: %s", entry, item_entry, appearancemodid, std::to_string(displayInfo).c_str()); co.outfit[j] = 0; } } diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 51bb22eacfe19..b16bd8d641fee 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -171,8 +171,10 @@ typedef std::unordered_map GameTeleContainer; struct CreatureOutfit { + // Remember to change DB query too! static constexpr uint32 max_outfit_displays = 11; static constexpr uint32 max_custom_displays = 3; + uint8 race; uint8 Class; uint8 gender; @@ -181,7 +183,7 @@ struct CreatureOutfit uint8 hair; uint8 facialhair; uint8 haircolor; - uint8 customdisplay[3]; + uint8 customdisplay[max_custom_displays]; uint32 displayId; uint32 outfit[max_outfit_displays]; }; From 31e5de9988c5419ed6b1d9398f1090273dabce77 Mon Sep 17 00:00:00 2001 From: Rangorn Date: Wed, 3 Jan 2018 20:08:19 +0200 Subject: [PATCH 11/35] Implement guildid for tabard designs --- sql/custom/world/2018_01_03_00_world_dressnpcs.sql | 2 ++ src/server/game/Globals/ObjectMgr.cpp | 5 +++-- src/server/game/Globals/ObjectMgr.h | 1 + src/server/game/Handlers/GuildHandler.cpp | 1 - src/server/game/Handlers/SpellHandler.cpp | 5 +++++ 5 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 sql/custom/world/2018_01_03_00_world_dressnpcs.sql diff --git a/sql/custom/world/2018_01_03_00_world_dressnpcs.sql b/sql/custom/world/2018_01_03_00_world_dressnpcs.sql new file mode 100644 index 0000000000000..ab2a7721bc020 --- /dev/null +++ b/sql/custom/world/2018_01_03_00_world_dressnpcs.sql @@ -0,0 +1,2 @@ +ALTER TABLE `creature_template_outfits` + ADD COLUMN `guildid` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0' AFTER `tabard`; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index c2865363cbe17..9436a195e8a8e 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8289,7 +8289,7 @@ void ObjectMgr::LoadCreatureOutfits() _creatureOutfitStore.clear(); // for reload case (test only) QueryResult result = WorldDatabase.Query("SELECT entry, race, class, gender, skin, face, hair, haircolor, facialhair, feature1, feature2, feature3, " - "head, shoulders, body, chest, waist, legs, feet, wrists, hands, tabard, back FROM creature_template_outfits"); + "head, shoulders, body, chest, waist, legs, feet, wrists, hands, tabard, back, guildid FROM creature_template_outfits"); if (!result) { @@ -8343,7 +8343,7 @@ void ObjectMgr::LoadCreatureOutfits() co.customdisplay[j] = fields[i++].GetUInt8(); for (uint32 j = 0; j < CreatureOutfit::max_outfit_displays; ++j) { - int64 displayInfo = fields[i + j].GetInt64(); + int64 displayInfo = fields[i++].GetInt64(); if (displayInfo > 0) // entry { uint32 item_entry = static_cast(displayInfo & 0xFFFFFFFF); @@ -8359,6 +8359,7 @@ void ObjectMgr::LoadCreatureOutfits() else // display co.outfit[j] = static_cast(-displayInfo); } + co.guild = fields[i++].GetUInt64(); _creatureOutfitStore[entry] = co; diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index b16bd8d641fee..080dc780e3b0c 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -184,6 +184,7 @@ struct CreatureOutfit uint8 facialhair; uint8 haircolor; uint8 customdisplay[max_custom_displays]; + uint64 guild; uint32 displayId; uint32 outfit[max_outfit_displays]; }; diff --git a/src/server/game/Handlers/GuildHandler.cpp b/src/server/game/Handlers/GuildHandler.cpp index 13670acfa3655..3c4d66d9a08c8 100644 --- a/src/server/game/Handlers/GuildHandler.cpp +++ b/src/server/game/Handlers/GuildHandler.cpp @@ -35,7 +35,6 @@ void WorldSession::HandleGuildQueryOpcode(WorldPackets::Guild::QueryGuildInfo& q GetPlayerInfo().c_str(), query.GuildGuid.ToString().c_str(), query.PlayerGuid.ToString().c_str()); if (Guild* guild = sGuildMgr->GetGuildByGuid(query.GuildGuid)) - if (guild->IsMember(query.PlayerGuid)) { guild->SendQueryResponse(this); return; diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index ac4eb7d7ef40f..f98cc4d95aa61 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -527,6 +527,11 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPackets::Spells::GetMirrorI for (uint32 i = 0; i < PLAYER_CUSTOM_DISPLAY_SIZE; ++i) mirrorImageComponentedData.CustomDisplay[i] = outfit.customdisplay[i]; mirrorImageComponentedData.GuildGUID = ObjectGuid::Empty; + if (outfit.guild) + { + if (Guild* guild = sGuildMgr->GetGuildById(outfit.guild)) + mirrorImageComponentedData.GuildGUID = guild->GetGUID(); + } mirrorImageComponentedData.ItemDisplayID.reserve(11); for (auto const& display : it->second.outfit) From 688e329a952b3662117a1585f11f650d134c7056 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Wed, 3 Jan 2018 21:15:23 +0200 Subject: [PATCH 12/35] Implement appearancemodid as a separate column --- .../world/2018_01_03_01_world_dressnpcs.sql | 12 ++++++++++++ .../world/2018_01_03_02_world_dressnpcs.sql | 11 +++++++++++ .../world/2018_01_03_03_world_dressnpcs.sql | 2 ++ src/server/game/Globals/ObjectMgr.cpp | 16 ++++++++++++---- 4 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 sql/custom/world/2018_01_03_01_world_dressnpcs.sql create mode 100644 sql/custom/world/2018_01_03_02_world_dressnpcs.sql create mode 100644 sql/custom/world/2018_01_03_03_world_dressnpcs.sql diff --git a/sql/custom/world/2018_01_03_01_world_dressnpcs.sql b/sql/custom/world/2018_01_03_01_world_dressnpcs.sql new file mode 100644 index 0000000000000..93a8a6bb54feb --- /dev/null +++ b/sql/custom/world/2018_01_03_01_world_dressnpcs.sql @@ -0,0 +1,12 @@ +ALTER TABLE `creature_template_outfits` + ADD COLUMN `head_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `head`, + ADD COLUMN `shoulders_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `shoulders`, + ADD COLUMN `body_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `body`, + ADD COLUMN `chest_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `chest`, + ADD COLUMN `waist_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `waist`, + ADD COLUMN `legs_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `legs`, + ADD COLUMN `feet_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `feet`, + ADD COLUMN `wrists_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `wrists`, + ADD COLUMN `hands_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `hands`, + ADD COLUMN `back_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `back`, + ADD COLUMN `tabard_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `tabard`; diff --git a/sql/custom/world/2018_01_03_02_world_dressnpcs.sql b/sql/custom/world/2018_01_03_02_world_dressnpcs.sql new file mode 100644 index 0000000000000..b8e0161194e12 --- /dev/null +++ b/sql/custom/world/2018_01_03_02_world_dressnpcs.sql @@ -0,0 +1,11 @@ +UPDATE `creature_template_outfits` SET `head_appearance` = (`head` >> 32), `head` = (`head` & 0xFFFFFFFF) WHERE `head` > 0; +UPDATE `creature_template_outfits` SET `shoulders_appearance` = (`shoulders` >> 32), `shoulders` = (`shoulders` & 0xFFFFFFFF) WHERE `shoulders` > 0; +UPDATE `creature_template_outfits` SET `body_appearance` = (`body` >> 32), `body` = (`body` & 0xFFFFFFFF) WHERE `body` > 0; +UPDATE `creature_template_outfits` SET `chest_appearance` = (`chest` >> 32), `chest` = (`chest` & 0xFFFFFFFF) WHERE `chest` > 0; +UPDATE `creature_template_outfits` SET `waist_appearance` = (`waist` >> 32), `waist` = (`waist` & 0xFFFFFFFF) WHERE `waist` > 0; +UPDATE `creature_template_outfits` SET `legs_appearance` = (`legs` >> 32), `legs` = (`legs` & 0xFFFFFFFF) WHERE `legs` > 0; +UPDATE `creature_template_outfits` SET `feet_appearance` = (`feet` >> 32), `feet` = (`feet` & 0xFFFFFFFF) WHERE `feet` > 0; +UPDATE `creature_template_outfits` SET `wrists_appearance` = (`wrists` >> 32), `wrists` = (`wrists` & 0xFFFFFFFF) WHERE `wrists` > 0; +UPDATE `creature_template_outfits` SET `hands_appearance` = (`hands` >> 32), `hands` = (`hands` & 0xFFFFFFFF) WHERE `hands` > 0; +UPDATE `creature_template_outfits` SET `back_appearance` = (`back` >> 32), `back` = (`back` & 0xFFFFFFFF) WHERE `back` > 0; +UPDATE `creature_template_outfits` SET `tabard_appearance` = (`tabard` >> 32), `tabard` = (`tabard` & 0xFFFFFFFF) WHERE `tabard` > 0; diff --git a/sql/custom/world/2018_01_03_03_world_dressnpcs.sql b/sql/custom/world/2018_01_03_03_world_dressnpcs.sql new file mode 100644 index 0000000000000..7512979713199 --- /dev/null +++ b/sql/custom/world/2018_01_03_03_world_dressnpcs.sql @@ -0,0 +1,2 @@ +ALTER TABLE `creature_template_outfits` + COMMENT='Use positive values for item entries and negative to use item displayid for head, shoulders etc.'; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 9436a195e8a8e..e760fa8d9116e 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8289,7 +8289,9 @@ void ObjectMgr::LoadCreatureOutfits() _creatureOutfitStore.clear(); // for reload case (test only) QueryResult result = WorldDatabase.Query("SELECT entry, race, class, gender, skin, face, hair, haircolor, facialhair, feature1, feature2, feature3, " - "head, shoulders, body, chest, waist, legs, feet, wrists, hands, tabard, back, guildid FROM creature_template_outfits"); + "head, head_appearance, shoulders, shoulders_appearance, body, body_appearance, chest, chest_appearance, waist, waist_appearance, " + "legs, legs_appearance, feet, feet_appearance, wrists, wrists_appearance, hands, hands_appearance, tabard, tabard_appearance, back, back_appearance, " + "guildid FROM creature_template_outfits"); if (!result) { @@ -8344,20 +8346,26 @@ void ObjectMgr::LoadCreatureOutfits() for (uint32 j = 0; j < CreatureOutfit::max_outfit_displays; ++j) { int64 displayInfo = fields[i++].GetInt64(); + uint32 appearancemodid = fields[i++].GetUInt32(); if (displayInfo > 0) // entry { - uint32 item_entry = static_cast(displayInfo & 0xFFFFFFFF); - uint32 appearancemodid = static_cast(displayInfo >> 32); + uint32 item_entry = static_cast(displayInfo); if (uint32 display = sDB2Manager.GetItemDisplayId(item_entry, appearancemodid)) co.outfit[j] = display; else { - TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has invalid (item entry, appearance) combination: %u, %u. Value in DB: %s", entry, item_entry, appearancemodid, std::to_string(displayInfo).c_str()); + TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has invalid (item entry, appearance) combination: %u, %u. Ignoring.", entry, item_entry, appearancemodid); co.outfit[j] = 0; } } else // display + { + if (appearancemodid) + { + TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` is using displayid (negative value), but also has appearance set (displayid, appearance): %s, %u. Ignoring appearance.", entry, std::to_string(displayInfo).c_str(), appearancemodid); + } co.outfit[j] = static_cast(-displayInfo); + } } co.guild = fields[i++].GetUInt64(); From e410e5424419dab7799b48dd9b85ca0f4b0bb651 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Wed, 3 Jan 2018 21:34:40 +0200 Subject: [PATCH 13/35] Add description column --- sql/custom/world/2018_01_03_04_world_dressnpcs.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 sql/custom/world/2018_01_03_04_world_dressnpcs.sql diff --git a/sql/custom/world/2018_01_03_04_world_dressnpcs.sql b/sql/custom/world/2018_01_03_04_world_dressnpcs.sql new file mode 100644 index 0000000000000..141c920ebd838 --- /dev/null +++ b/sql/custom/world/2018_01_03_04_world_dressnpcs.sql @@ -0,0 +1,2 @@ +ALTER TABLE `creature_template_outfits` + ADD COLUMN `description` TEXT NULL DEFAULT NULL AFTER `guildid`; From 375025968dbfaf205a9398c75375ec3a87e6c028 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Thu, 4 Jan 2018 03:08:33 +0200 Subject: [PATCH 14/35] Implement workaround for missing interaction sounds --- cmake/options.cmake | 1 + cmake/showoptions.cmake | 6 ++++ .../2018_01_04_00_hotfixes_dressnpcs.sql | 12 ++++++++ .../world/2018_01_04_00_world_dressnpcs.sql | 2 ++ .../Implementation/HotfixDatabase.cpp | 3 ++ .../Database/Implementation/HotfixDatabase.h | 2 ++ src/server/game/DataStores/DB2LoadInfo.h | 17 +++++++++++ src/server/game/DataStores/DB2Stores.cpp | 2 ++ src/server/game/DataStores/DB2Stores.h | 1 + src/server/game/DataStores/DB2Structure.h | 9 ++++++ .../game/Entities/Creature/Creature.cpp | 28 +++++++++++++++++++ src/server/game/Entities/Creature/Creature.h | 1 + src/server/game/Globals/ObjectMgr.cpp | 8 +++++- src/server/game/Globals/ObjectMgr.h | 2 ++ src/server/game/Handlers/MiscHandler.cpp | 10 +++++++ src/server/game/Handlers/NPCHandler.cpp | 5 ++++ src/server/game/Handlers/QuestHandler.cpp | 5 ++++ 17 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 sql/custom/hotfixes/2018_01_04_00_hotfixes_dressnpcs.sql create mode 100644 sql/custom/world/2018_01_04_00_world_dressnpcs.sql diff --git a/cmake/options.cmake b/cmake/options.cmake index efa60175353bb..768c3879a0958 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -51,3 +51,4 @@ option(WITH_COREDEBUG "Include additional debug-code in core" set(WITH_SOURCE_TREE "hierarchical" CACHE STRING "Build the source tree for IDE's.") set_property(CACHE WITH_SOURCE_TREE PROPERTY STRINGS no flat hierarchical hierarchical-folders) option(WITHOUT_GIT "Disable the GIT testing routines" 0) +option(DISABLE_DRESSNPCS_CORESOUNDS "Disable server side 'missing sounds' workaround" 0) diff --git a/cmake/showoptions.cmake b/cmake/showoptions.cmake index 6c060295ee021..5439bd7bd8674 100644 --- a/cmake/showoptions.cmake +++ b/cmake/showoptions.cmake @@ -126,4 +126,10 @@ if (BUILD_SHARED_LIBS) WarnAboutSpacesInBuildPath() endif() +if (DISABLE_DRESSNPCS_CORESOUNDS) + message("") + message("DressNPCs sound workaround disabled. Live without sounds or use a client side patch.") + add_definitions(-DDISABLE_DRESSNPCS_CORESOUNDS) +endif() + message("") diff --git a/sql/custom/hotfixes/2018_01_04_00_hotfixes_dressnpcs.sql b/sql/custom/hotfixes/2018_01_04_00_hotfixes_dressnpcs.sql new file mode 100644 index 0000000000000..d740677b496bf --- /dev/null +++ b/sql/custom/hotfixes/2018_01_04_00_hotfixes_dressnpcs.sql @@ -0,0 +1,12 @@ +DROP TABLE IF EXISTS `npc_sounds`; +CREATE TABLE `npc_sounds` ( + `ID` INT(10) UNSIGNED NOT NULL, + `hello` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `goodbye` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `pissed` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `ack` INT(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`) +) +COLLATE='utf8_general_ci' +ENGINE=MyISAM +; diff --git a/sql/custom/world/2018_01_04_00_world_dressnpcs.sql b/sql/custom/world/2018_01_04_00_world_dressnpcs.sql new file mode 100644 index 0000000000000..ef9393b75ca11 --- /dev/null +++ b/sql/custom/world/2018_01_04_00_world_dressnpcs.sql @@ -0,0 +1,2 @@ +ALTER TABLE `creature_template_outfits` + ADD COLUMN `npcsoundsid` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'entry from NPCSounds.dbc/db2' AFTER `entry`; diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index 325cc751a6f70..acef62ae1ba1f 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -652,6 +652,9 @@ void HotfixDatabaseConnection::DoPrepareStatements() // NamesReservedLocale.db2 PrepareStatement(HOTFIX_SEL_NAMES_RESERVED_LOCALE, "SELECT ID, Name, LocaleMask FROM names_reserved_locale ORDER BY ID DESC", CONNECTION_SYNCH); + // NPCSounds.db2 + PrepareStatement(HOTFIX_SEL_NPC_SOUNDS, "SELECT ID, hello, goodbye, pissed, ack FROM npc_sounds ORDER BY ID DESC", CONNECTION_SYNCH); + // OverrideSpellData.db2 PrepareStatement(HOTFIX_SEL_OVERRIDE_SPELL_DATA, "SELECT ID, SpellID1, SpellID2, SpellID3, SpellID4, SpellID5, SpellID6, SpellID7, SpellID8, " "SpellID9, SpellID10, PlayerActionbarFileDataID, Flags FROM override_spell_data ORDER BY ID DESC", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index 86d72a37efe58..f1a887b45dedb 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -355,6 +355,8 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_NAMES_RESERVED_LOCALE, + HOTFIX_SEL_NPC_SOUNDS, + HOTFIX_SEL_OVERRIDE_SPELL_DATA, HOTFIX_SEL_PHASE, diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h index 9b5dff88fb5b9..6d9697036ec4a 100644 --- a/src/server/game/DataStores/DB2LoadInfo.h +++ b/src/server/game/DataStores/DB2LoadInfo.h @@ -3232,6 +3232,23 @@ struct NamesReservedLocaleLoadInfo } }; +struct NPCSoundsLoadInfo +{ + static DB2LoadInfo const* Instance() + { + static DB2FieldMeta const fields[] = + { + { false, FT_INT, "ID" }, + { false, FT_INT, "hello" }, + { false, FT_INT, "goodbye" }, + { false, FT_INT, "pissed" }, + { false, FT_INT, "ack" }, + }; + static DB2LoadInfo const loadInfo(&fields[0], std::extent::value, NPCSoundsMeta::Instance(), HOTFIX_SEL_NPC_SOUNDS); + return &loadInfo; + } +}; + struct OverrideSpellDataLoadInfo { static DB2LoadInfo const* Instance() diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 165999d732fef..9bab7a5b0cf25 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -174,6 +174,7 @@ DB2Storage sNameGenStore("NameGen.db2", Nam DB2Storage sNamesProfanityStore("NamesProfanity.db2", NamesProfanityLoadInfo::Instance()); DB2Storage sNamesReservedStore("NamesReserved.db2", NamesReservedLoadInfo::Instance()); DB2Storage sNamesReservedLocaleStore("NamesReservedLocale.db2", NamesReservedLocaleLoadInfo::Instance()); +DB2Storage sNPCSoundsStore("NPCSounds.db2", NPCSoundsLoadInfo::Instance()); DB2Storage sOverrideSpellDataStore("OverrideSpellData.db2", OverrideSpellDataLoadInfo::Instance()); DB2Storage sPhaseStore("Phase.db2", PhaseLoadInfo::Instance()); DB2Storage sPhaseXPhaseGroupStore("PhaseXPhaseGroup.db2", PhaseXPhaseGroupLoadInfo::Instance()); @@ -595,6 +596,7 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale) LOAD_DB2(sNamesProfanityStore); LOAD_DB2(sNamesReservedStore); LOAD_DB2(sNamesReservedLocaleStore); + LOAD_DB2(sNPCSoundsStore); LOAD_DB2(sOverrideSpellDataStore); LOAD_DB2(sPhaseStore); LOAD_DB2(sPhaseXPhaseGroupStore); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 51bedb64d9bfb..181712953d513 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -139,6 +139,7 @@ TC_GAME_API extern DB2Storage sModifierTre TC_GAME_API extern DB2Storage sMountCapabilityStore; TC_GAME_API extern DB2Storage sMountStore; TC_GAME_API extern DB2Storage sMovieStore; +TC_GAME_API extern DB2Storage sNPCSoundsStore; TC_GAME_API extern DB2Storage sOverrideSpellDataStore; TC_GAME_API extern DB2Storage sPhaseStore; TC_GAME_API extern DB2Storage sPlayerConditionStore; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index c9a4df1d0f138..70a290b27d7d3 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -1943,6 +1943,15 @@ struct NamesReservedLocaleEntry uint8 LocaleMask; }; +struct NPCSoundsEntry +{ + uint32 ID; + uint32 hello; + uint32 goodbye; + uint32 pissed; + uint32 ack; +}; + #define MAX_OVERRIDE_SPELL 10 struct OverrideSpellDataEntry diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index e0b5951b20bbd..59a2eb5c787ed 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -244,6 +244,34 @@ void Creature::RemoveFromWorld() } } +void Creature::SendMirrorSound(Player* target, uint8 type) +{ + int32 outfitId = GetOutfit(); + if (outfitId < 0) + { + const CreatureOutfitContainer& outfits = sObjectMgr->GetCreatureOutfitMap(); + auto it = outfits.find(-outfitId); + if (it != outfits.end() && it->second.npcsoundsid) + { + if (auto const* npcsounds = sNPCSoundsStore.LookupEntry(it->second.npcsoundsid)) + { + switch (type) + { + case 0: + PlayDistanceSound(npcsounds->hello, target); + break; + case 1: + PlayDistanceSound(npcsounds->goodbye, target); + break; + case 2: + PlayDistanceSound(npcsounds->pissed, target); + break; + } + } + } + } +} + void Creature::DisappearAndDie() { DestroyForNearbyPlayers(); diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 22b2f3f9e92da..ae645c2967164 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -75,6 +75,7 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void SetOutfit(int32 outfit) { outfitId = outfit; }; int32 GetOutfit() const { return outfitId; }; bool IsMirrorImage() const { return outfitId < 0; }; + void SendMirrorSound(Player* target, uint8 type); void DisappearAndDie(); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index e760fa8d9116e..d6986a829c2e6 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8288,7 +8288,7 @@ void ObjectMgr::LoadCreatureOutfits() _creatureOutfitStore.clear(); // for reload case (test only) - QueryResult result = WorldDatabase.Query("SELECT entry, race, class, gender, skin, face, hair, haircolor, facialhair, feature1, feature2, feature3, " + QueryResult result = WorldDatabase.Query("SELECT entry, npcsoundsid, race, class, gender, skin, face, hair, haircolor, facialhair, feature1, feature2, feature3, " "head, head_appearance, shoulders, shoulders_appearance, body, body_appearance, chest, chest_appearance, waist, waist_appearance, " "legs, legs_appearance, feet, feet_appearance, wrists, wrists_appearance, hands, hands_appearance, tabard, tabard_appearance, back, back_appearance, " "guildid FROM creature_template_outfits"); @@ -8310,6 +8310,12 @@ void ObjectMgr::LoadCreatureOutfits() CreatureOutfit co; + co.npcsoundsid = fields[i++].GetUInt32(); + if (co.npcsoundsid && !sNPCSoundsStore.HasRecord(co.npcsoundsid)) + { + TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has incorrect npcsoundsid (%u). Using 0.", entry, co.npcsoundsid); + co.npcsoundsid = 0; + } co.race = fields[i++].GetUInt8(); const ChrRacesEntry* rEntry = sChrRacesStore.LookupEntry(co.race); if (!rEntry) diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 080dc780e3b0c..957927bab3afa 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -185,6 +185,8 @@ struct CreatureOutfit uint8 haircolor; uint8 customdisplay[max_custom_displays]; uint64 guild; + uint32 npcsoundsid; + uint32 displayId; uint32 outfit[max_outfit_displays]; }; diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 7d2115c618d53..b41dcbf685cf6 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -401,6 +401,11 @@ void WorldSession::HandleRequestCemeteryList(WorldPackets::Misc::RequestCemetery void WorldSession::HandleSetSelectionOpcode(WorldPackets::Misc::SetSelection& packet) { +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (packet.Selection.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(packet.Selection)) + creature->SendMirrorSound(_player, 0); +#endif _player->SetSelection(packet.Selection); } @@ -1171,6 +1176,11 @@ void WorldSession::HandlePvpPrestigeRankUp(WorldPackets::Misc::PvpPrestigeRankUp void WorldSession::HandleCloseInteraction(WorldPackets::Misc::CloseInteraction& closeInteraction) { +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (closeInteraction.SourceGuid.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(closeInteraction.SourceGuid)) + creature->SendMirrorSound(_player, 1); +#endif if (_player->PlayerTalkClass->GetInteractionData().SourceGuid == closeInteraction.SourceGuid) _player->PlayerTalkClass->GetInteractionData().Reset(); } diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 9eced0644cb98..8430a61ab1aed 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -152,6 +152,11 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPackets::NPC::TrainerBuySpel void WorldSession::HandleGossipHelloOpcode(WorldPackets::NPC::Hello& packet) { +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (packet.Unit.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(packet.Unit)) + creature->SendMirrorSound(_player, 0); +#endif Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(packet.Unit, UNIT_NPC_FLAG_GOSSIP); if (!unit) { diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 44108951a53bd..a12183855ca08 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -77,6 +77,11 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPackets::Quest::QuestGiverHe { TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_HELLO %s", packet.QuestGiverGUID.ToString().c_str()); +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (packet.QuestGiverGUID.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(packet.QuestGiverGUID)) + creature->SendMirrorSound(_player, 0); +#endif Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(packet.QuestGiverGUID, UNIT_NPC_FLAG_QUESTGIVER); if (!creature) { From 3b402299dd5b8dd8e25b38e88dff57994a4ab5ef Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Thu, 4 Jan 2018 03:29:54 +0200 Subject: [PATCH 15/35] Update readme --- src/server/scripts/Custom/DressNPCs/README.md | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/server/scripts/Custom/DressNPCs/README.md b/src/server/scripts/Custom/DressNPCs/README.md index 68b320c9ada4d..2b7bb0de41da9 100644 --- a/src/server/scripts/Custom/DressNPCs/README.md +++ b/src/server/scripts/Custom/DressNPCs/README.md @@ -1,13 +1,15 @@ # DressNPCs [![Build Status](https://travis-ci.org/Rochet2/TrinityCore.svg?branch=dressnpcs_7.x)](https://travis-ci.org/Rochet2/TrinityCore) #### About -You can make an NPC, set the displays or items you want him to have and his race and gender (defines displayID) as well as facial features and skin color. -All this is done in the database. No client edits needed. Completely server side. +This patch allows you to dress up armor on NPCs as well as choose their facial features. +Create unique looking NPCs by defining their gender, race, facial features, clothing and much more. +All this is done in the database. No client edits required. + Source: http://rochet2.github.io/Dress-NPCs.html Known bugs: -- Portraits of the NPCs may not work properly at times - a client side visual bug. -- NPCs have no sound replies when you talk to them. This is because of the models used and there is no fix without editing client. +- Portraits of the NPCs may not work properly at times - a client side visual bug (blizzard's fault). +- Normally NPCs have no sound replies when you talk to them. This is a client side limitation and can be fixed with a client patch. Additionally a **workaround has been included** which sends interaction sounds from the core unless disabled in CMake. #### Installation @@ -35,14 +37,19 @@ After compiling: - If you do not use the auto updater then run files named `*_dressnpcs.sql` from `\sql\custom` to your databases. #### Usage -Create a row to `creature_template_outfits` table with your desired race, class, gender and equipped items. -***Note*** The items can use positive value as item entry and negative for displayid. -***Note*** After wotlk the display system has changed slightly. If you want special appearance on items, find the correct displayid and use it or use an entry which has the appearance id added to it in the following way `((AppearanceModID << 32) | item_entry)`. Appearances are usually between 0 and 10. -Create an NPC. Set the `creature_template_outfits` entry to the modelID column in `creature_template`, but __make it negative__. -Clear wow cache folder, restart server and spawn the NPC. -The patch also adds `.reload creature_template_outfit` command. You can use it to reload the creature outfit table again for testing. -You should be able to reload the table with new entries of ingame creatures. Relog to update the visual look of creatures with the reloaded data. -__Using reload is not recommended.__ (not outfit nor template) Use them only for testing and debugging purposes. Not for live servers. +Before compiling the core you can choose in CMake to disable sound workaround if you have a client patch for the NPC sounds. + +To make an outfit create a row to `creature_template_outfits` table with your desired race, class, gender and equipped items. You can freely choose the entry number. +- The items can use positive value as item entry and negative for displayid. +- Appearances are usually between 0 and 10 and they define the look of the item. Different appearances are usually used by mythic and heroic versions of an item. An appearance only works when using an item entry (a positive value) for the equipped item definition. +- `guildid` refers to an actual guild from characters table and it is used to define the tabard looks of the creature if one is equipped. +- `npcsoundsid` refers to `NPCSounds.dbc/db2`. You can define what gossip replies to use for the NPC with the core side workaround for missing sounds for gossip. To create completely new sound combinations you can use hotfixes database or edit the DBC file. + +Create an NPC. Set the `creature_template_outfits` entry to the modelID column in `creature_template`, but __make it negative__. + +Clear wow cache folder, restart server and spawn the NPC. Remember to check startup errors. + +The patch also adds `.reload creature_template_outfit` command. You can use it to reload the creature outfit table for testing. Relog to update the visual look of creatures with the reloaded data. #### Bugs and Contact Report issues and similar to http://rochet2.github.io/ From 5f93ba90928a6ea91e6b5480372a665ac6cf451d Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Thu, 4 Jan 2018 15:48:32 +0200 Subject: [PATCH 16/35] Enhance reload to take effect immediately --- src/server/scripts/Commands/cs_reload.cpp | 8 ++++++++ src/server/scripts/Custom/DressNPCs/README.md | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 5b7120ab2cbbb..6e2f09b8b63c4 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -466,6 +466,14 @@ class reload_commandscript : public CommandScript { TC_LOG_INFO("misc", "Loading Creature Outfits... (`creature_template_outfits`)"); sObjectMgr->LoadCreatureOutfits(); + sMapMgr->DoForAllMaps([](Map* map) + { + for (auto e : map->GetCreatureBySpawnIdStore()) + { + if (e.second->IsMirrorImage()) + new MirrorImageUpdate(e.second); + } + }); handler->SendGlobalGMSysMessage("DB table `creature_template_outfits` reloaded."); return true; } diff --git a/src/server/scripts/Custom/DressNPCs/README.md b/src/server/scripts/Custom/DressNPCs/README.md index 2b7bb0de41da9..8778c9403e9cc 100644 --- a/src/server/scripts/Custom/DressNPCs/README.md +++ b/src/server/scripts/Custom/DressNPCs/README.md @@ -49,7 +49,7 @@ Create an NPC. Set the `creature_template_outfits` entry to the modelID column i Clear wow cache folder, restart server and spawn the NPC. Remember to check startup errors. -The patch also adds `.reload creature_template_outfit` command. You can use it to reload the creature outfit table for testing. Relog to update the visual look of creatures with the reloaded data. +The patch also adds `.reload creature_template_outfit` command. You can use it to reload the creature outfit table for testing. #### Bugs and Contact Report issues and similar to http://rochet2.github.io/ From 087952d05e6ea22fb46dd4a479eb2fbb5ddb2047 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Thu, 4 Jan 2018 20:32:01 +0200 Subject: [PATCH 17/35] Implement more sources for dressnpcs sound workaround --- src/server/game/Handlers/AuctionHouseHandler.cpp | 6 ++++++ src/server/game/Handlers/BankHandler.cpp | 5 +++++ src/server/game/Handlers/GuildHandler.cpp | 6 ++++++ src/server/game/Handlers/ItemHandler.cpp | 6 ++++++ src/server/game/Handlers/NPCHandler.cpp | 15 +++++++++++++++ src/server/game/Handlers/PetitionsHandler.cpp | 6 ++++++ src/server/game/Handlers/TaxiHandler.cpp | 6 ++++++ 7 files changed, 50 insertions(+) diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index f5a4911ec6ae9..8664deb2f2577 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -26,6 +26,7 @@ #include "Language.h" #include "Log.h" #include "Mail.h" +#include "Map.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Player.h" @@ -36,6 +37,11 @@ //void called when player click on auctioneer npc void WorldSession::HandleAuctionHelloOpcode(WorldPackets::AuctionHouse::AuctionHelloRequest& packet) { +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (packet.Guid.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(packet.Guid)) + creature->SendMirrorSound(_player, 0); +#endif Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(packet.Guid, UNIT_NPC_FLAG_AUCTIONEER); if (!unit) { diff --git a/src/server/game/Handlers/BankHandler.cpp b/src/server/game/Handlers/BankHandler.cpp index 8f8de925da219..001260780e688 100644 --- a/src/server/game/Handlers/BankHandler.cpp +++ b/src/server/game/Handlers/BankHandler.cpp @@ -60,6 +60,11 @@ void WorldSession::HandleAutoBankItemOpcode(WorldPackets::Bank::AutoBankItem& pa void WorldSession::HandleBankerActivateOpcode(WorldPackets::NPC::Hello& packet) { +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (packet.Unit.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(packet.Unit)) + creature->SendMirrorSound(_player, 0); + #endif Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(packet.Unit, UNIT_NPC_FLAG_BANKER); if (!unit) { diff --git a/src/server/game/Handlers/GuildHandler.cpp b/src/server/game/Handlers/GuildHandler.cpp index 3c4d66d9a08c8..5cf735fa8590b 100644 --- a/src/server/game/Handlers/GuildHandler.cpp +++ b/src/server/game/Handlers/GuildHandler.cpp @@ -23,6 +23,7 @@ #include "GuildMgr.h" #include "GuildPackets.h" #include "Log.h" +#include "Map.h" #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" @@ -231,6 +232,11 @@ void WorldSession::HandleGuildBankActivate(WorldPackets::Guild::GuildBankActivat TC_LOG_DEBUG("guild", "CMSG_GUILD_BANK_ACTIVATE [%s]: [%s] AllSlots: %u" , GetPlayerInfo().c_str(), packet.Banker.ToString().c_str(), packet.FullUpdate); +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (packet.Banker.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(packet.Banker)) + creature->SendMirrorSound(_player, 0); +#endif GameObject const* const go = GetPlayer()->GetGameObjectIfCanInteractWith(packet.Banker, GAMEOBJECT_TYPE_GUILD_BANK); if (!go) return; diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index 81f9d82dee892..5177015912cef 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -25,6 +25,7 @@ #include "Item.h" #include "ItemPackets.h" #include "Log.h" +#include "Map.h" #include "NPCPackets.h" #include "ObjectMgr.h" #include "Opcodes.h" @@ -578,6 +579,11 @@ void WorldSession::HandleListInventoryOpcode(WorldPackets::NPC::Hello& packet) if (!GetPlayer()->IsAlive()) return; +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (packet.Unit.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(packet.Unit)) + creature->SendMirrorSound(_player, 0); +#endif SendListInventory(packet.Unit); } diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 8430a61ab1aed..835b16c2bc1bc 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -60,6 +60,11 @@ enum StableResultCode void WorldSession::HandleTabardVendorActivateOpcode(WorldPackets::NPC::Hello& packet) { +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (packet.Unit.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(packet.Unit)) + creature->SendMirrorSound(_player, 0); +#endif Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(packet.Unit, UNIT_NPC_FLAG_TABARDDESIGNER); if (!unit) { @@ -90,6 +95,11 @@ void WorldSession::SendShowMailBox(ObjectGuid guid) void WorldSession::HandleTrainerListOpcode(WorldPackets::NPC::Hello& packet) { +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (packet.Unit.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(packet.Unit)) + creature->SendMirrorSound(_player, 0); +#endif Creature* npc = GetPlayer()->GetNPCIfCanInteractWith(packet.Unit, UNIT_NPC_FLAG_TRAINER); if (!npc) { @@ -366,6 +376,11 @@ void WorldSession::SendBindPoint(Creature* npc) void WorldSession::HandleRequestStabledPets(WorldPackets::NPC::RequestStabledPets& packet) { +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (packet.StableMaster.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(packet.StableMaster)) + creature->SendMirrorSound(_player, 0); +#endif if (!CheckStableMaster(packet.StableMaster)) return; diff --git a/src/server/game/Handlers/PetitionsHandler.cpp b/src/server/game/Handlers/PetitionsHandler.cpp index 736d6138cf46f..9ffc4d8a1bd22 100644 --- a/src/server/game/Handlers/PetitionsHandler.cpp +++ b/src/server/game/Handlers/PetitionsHandler.cpp @@ -23,6 +23,7 @@ #include "GuildMgr.h" #include "Item.h" #include "Log.h" +#include "Map.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Opcodes.h" @@ -569,6 +570,11 @@ void WorldSession::HandleTurnInPetition(WorldPackets::Petition::TurnInPetition& void WorldSession::HandlePetitionShowList(WorldPackets::Petition::PetitionShowList& packet) { +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (packet.PetitionUnit.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(packet.PetitionUnit)) + creature->SendMirrorSound(_player, 0); +#endif SendPetitionShowList(packet.PetitionUnit); } diff --git a/src/server/game/Handlers/TaxiHandler.cpp b/src/server/game/Handlers/TaxiHandler.cpp index 35e7652daec7f..504c1b47f97bd 100644 --- a/src/server/game/Handlers/TaxiHandler.cpp +++ b/src/server/game/Handlers/TaxiHandler.cpp @@ -23,6 +23,7 @@ #include "DatabaseEnv.h" #include "DB2Stores.h" #include "Log.h" +#include "Map.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Player.h" @@ -68,6 +69,11 @@ void WorldSession::SendTaxiStatus(ObjectGuid guid) void WorldSession::HandleTaxiQueryAvailableNodesOpcode(WorldPackets::Taxi::TaxiQueryAvailableNodes& taxiQueryAvailableNodes) { +#ifndef DISABLE_DRESSNPCS_CORESOUNDS + if (taxiQueryAvailableNodes.Unit.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(taxiQueryAvailableNodes.Unit)) + creature->SendMirrorSound(_player, 0); +#endif // cheating checks Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(taxiQueryAvailableNodes.Unit, UNIT_NPC_FLAG_FLIGHTMASTER); if (!unit) From 102188f98bf8e3f386316ee789f0fb2ecbc9ab07 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Tue, 16 Jan 2018 23:49:03 +0200 Subject: [PATCH 18/35] Fix no PCH compile --- src/server/game/Handlers/BankHandler.cpp | 2 ++ src/server/game/Handlers/GuildHandler.cpp | 1 + src/server/game/Handlers/MiscHandler.cpp | 2 ++ src/server/game/Handlers/PetitionsHandler.cpp | 1 + src/server/game/Handlers/QuestHandler.cpp | 1 + src/server/scripts/Commands/cs_reload.cpp | 1 + 6 files changed, 8 insertions(+) diff --git a/src/server/game/Handlers/BankHandler.cpp b/src/server/game/Handlers/BankHandler.cpp index 001260780e688..78d85e3b507c7 100644 --- a/src/server/game/Handlers/BankHandler.cpp +++ b/src/server/game/Handlers/BankHandler.cpp @@ -16,9 +16,11 @@ */ #include "BankPackets.h" +#include "Creature.h" #include "Item.h" #include "DB2Stores.h" #include "Log.h" +#include "Map.h" #include "NPCPackets.h" #include "Opcodes.h" #include "Player.h" diff --git a/src/server/game/Handlers/GuildHandler.cpp b/src/server/game/Handlers/GuildHandler.cpp index 5cf735fa8590b..bfafe357ca689 100644 --- a/src/server/game/Handlers/GuildHandler.cpp +++ b/src/server/game/Handlers/GuildHandler.cpp @@ -19,6 +19,7 @@ #include "WorldSession.h" #include "AchievementPackets.h" #include "Common.h" +#include "Creature.h" #include "Guild.h" #include "GuildMgr.h" #include "GuildPackets.h" diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index b41dcbf685cf6..7c1d748b321e5 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -27,6 +27,7 @@ #include "ClientConfigPackets.h" #include "Common.h" #include "Corpse.h" +#include "Creature.h" #include "DatabaseEnv.h" #include "DB2Stores.h" #include "GossipDef.h" @@ -37,6 +38,7 @@ #include "InstanceScript.h" #include "Language.h" #include "Log.h" +#include "Map.h" #include "MapManager.h" #include "MiscPackets.h" #include "Object.h" diff --git a/src/server/game/Handlers/PetitionsHandler.cpp b/src/server/game/Handlers/PetitionsHandler.cpp index 9ffc4d8a1bd22..4c705bc4de822 100644 --- a/src/server/game/Handlers/PetitionsHandler.cpp +++ b/src/server/game/Handlers/PetitionsHandler.cpp @@ -18,6 +18,7 @@ #include "WorldSession.h" #include "Common.h" +#include "Creature.h" #include "DatabaseEnv.h" #include "Guild.h" #include "GuildMgr.h" diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index a12183855ca08..cfbddd3b0eecf 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -27,6 +27,7 @@ #include "GossipDef.h" #include "Group.h" #include "Log.h" +#include "Map.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Player.h" diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 6e2f09b8b63c4..0463410379140 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -30,6 +30,7 @@ EndScriptData */ #include "CharacterTemplateDataStore.h" #include "Chat.h" #include "ConversationDataStore.h" +#include "Creature.h" #include "CreatureTextMgr.h" #include "DatabaseEnv.h" #include "DisableMgr.h" From 8b99b8dd60154ed2dcc13b6fafeeb2d6a660cdb8 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sat, 17 Feb 2018 20:22:57 +0200 Subject: [PATCH 19/35] Make outfits non-negative for displayid compatibility --- .../world/2018_02_17_00_world_dressnpcs.sql | 20 ++ .../game/AI/SmartScripts/SmartScript.cpp | 22 +-- src/server/game/DataStores/DB2Stores.cpp | 8 +- src/server/game/DataStores/DB2Stores.h | 3 +- src/server/game/DataStores/DB2Structure.h | 6 + .../game/Entities/Creature/Creature.cpp | 182 ++++++++++-------- src/server/game/Entities/Creature/Creature.h | 20 +- .../game/Entities/Creature/CreatureData.h | 12 +- .../game/Entities/Creature/CreatureOutfit.cpp | 26 +++ .../game/Entities/Creature/CreatureOutfit.h | 71 +++++++ src/server/game/Entities/Unit/Unit.h | 2 +- src/server/game/Globals/ObjectMgr.cpp | 151 +++++++++------ src/server/game/Globals/ObjectMgr.h | 36 +--- src/server/game/Handlers/QueryHandler.cpp | 12 +- src/server/game/Handlers/SpellHandler.cpp | 63 +++--- .../game/Spells/Auras/SpellAuraEffects.cpp | 6 +- src/server/scripts/Commands/cs_modify.cpp | 3 - src/server/scripts/Commands/cs_npc.cpp | 2 - src/server/scripts/Commands/cs_reload.cpp | 7 +- src/server/scripts/Custom/DressNPCs/README.md | 50 +++-- 20 files changed, 429 insertions(+), 273 deletions(-) create mode 100644 sql/custom/world/2018_02_17_00_world_dressnpcs.sql create mode 100644 src/server/game/Entities/Creature/CreatureOutfit.cpp create mode 100644 src/server/game/Entities/Creature/CreatureOutfit.h diff --git a/sql/custom/world/2018_02_17_00_world_dressnpcs.sql b/sql/custom/world/2018_02_17_00_world_dressnpcs.sql new file mode 100644 index 0000000000000..fbacdf65380de --- /dev/null +++ b/sql/custom/world/2018_02_17_00_world_dressnpcs.sql @@ -0,0 +1,20 @@ +ALTER TABLE `creature_template` CHANGE COLUMN `modelid1` `modelid1` BIGINT; +ALTER TABLE `creature_template` CHANGE COLUMN `modelid2` `modelid2` BIGINT; +ALTER TABLE `creature_template` CHANGE COLUMN `modelid3` `modelid3` BIGINT; +ALTER TABLE `creature_template` CHANGE COLUMN `modelid4` `modelid4` BIGINT; +UPDATE creature_template SET modelid1 = (-modelid1) + 3000000000 WHERE modelid1 < 0; +UPDATE creature_template SET modelid2 = (-modelid2) + 3000000000 WHERE modelid2 < 0; +UPDATE creature_template SET modelid3 = (-modelid3) + 3000000000 WHERE modelid3 < 0; +UPDATE creature_template SET modelid4 = (-modelid4) + 3000000000 WHERE modelid4 < 0; +ALTER TABLE `creature_template` CHANGE COLUMN `modelid1` `modelid1` INT(10) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `creature_template` CHANGE COLUMN `modelid2` `modelid2` INT(10) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `creature_template` CHANGE COLUMN `modelid3` `modelid3` INT(10) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `creature_template` CHANGE COLUMN `modelid4` `modelid4` INT(10) UNSIGNED NOT NULL DEFAULT '0'; + +ALTER TABLE `creature_template_outfits` DROP PRIMARY KEY; +UPDATE creature_template_outfits SET entry = entry + 3000000000 WHERE entry <= 0x7FFFFFFF; +ALTER TABLE `creature_template_outfits` ADD PRIMARY KEY (`entry`); + +ALTER TABLE `creature` CHANGE COLUMN `modelid` `modelid` INT(10) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `game_event_model_equip` CHANGE COLUMN `modelid` `modelid` INT(10) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `creature_model_info` CHANGE COLUMN `DisplayID` `DisplayID` INT(10) UNSIGNED NOT NULL DEFAULT '0'; diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 3da66ba449a67..f5ccf93006b36 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -411,22 +411,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(e.action.morphOrMount.creature)) { - Creature* crea = (*itr)->ToCreature(); - ASSERT(crea); - crea->SetOutfit(ObjectMgr::ChooseDisplayId(ci)); - if (crea->IsMirrorImage()) - { - new MirrorImageUpdate(crea); - } - else - { - (*itr)->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); - - uint32 displayId = sObjectMgr->GetCreatureDisplay(crea->GetOutfit()); - (*itr)->ToCreature()->SetDisplayId(displayId); - TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL: Creature entry %u, %s set displayid to %u", - (*itr)->GetEntry(), (*itr)->GetGUID().ToString().c_str(), displayId); - } + uint32 displayId = ObjectMgr::ChooseDisplayId(ci); + (*itr)->ToCreature()->SetDisplayId(displayId); + TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_MORPH_TO_ENTRY_OR_MODEL: Creature entry %u, %s set displayid to %u", + (*itr)->GetEntry(), (*itr)->GetGUID().ToString().c_str(), displayId); } } //if no param1, then use value from param2 (modelId) @@ -1295,7 +1283,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (e.action.morphOrMount.creature > 0) { if (CreatureTemplate const* cInfo = sObjectMgr->GetCreatureTemplate(e.action.morphOrMount.creature)) - (*itr)->ToUnit()->Mount(sObjectMgr->GetCreatureDisplay(ObjectMgr::ChooseDisplayId(cInfo))); + (*itr)->ToUnit()->Mount(ObjectMgr::ChooseDisplayId(cInfo)); } else (*itr)->ToUnit()->Mount(e.action.morphOrMount.model); diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 9ed1232eca2f7..4cf2202d9f58d 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -23,6 +23,7 @@ #include "IteratorPair.h" #include "Log.h" #include "ObjectDefines.h" +#include "ObjectMgr.h" #include "Regex.h" #include "Timer.h" #include "Util.h" @@ -73,7 +74,10 @@ DB2Storage sChrSpecializationStore("ChrSpec DB2Storage sCinematicCameraStore("CinematicCamera.db2", CinematicCameraLoadInfo::Instance()); DB2Storage sCinematicSequencesStore("CinematicSequences.db2", CinematicSequencesLoadInfo::Instance()); DB2Storage sConversationLineStore("ConversationLine.db2", ConversationLineLoadInfo::Instance()); -DB2Storage sCreatureDisplayInfoStore("CreatureDisplayInfo.db2", CreatureDisplayInfoLoadInfo::Instance()); +DB2Storage sCreatureDisplayInfoStoreRaw("CreatureDisplayInfo.db2", CreatureDisplayInfoLoadInfo::Instance()); +CreatureDisplayInfoStore sCreatureDisplayInfoStore; +bool CreatureDisplayInfoStore::HasRecord(uint32 id) const { return sCreatureDisplayInfoStoreRaw.HasRecord(sObjectMgr->GetRealDisplayId(id)); } +const CreatureDisplayInfoEntry * CreatureDisplayInfoStore::LookupEntry(uint32 id) const { return sCreatureDisplayInfoStoreRaw.LookupEntry(sObjectMgr->GetRealDisplayId(id)); } DB2Storage sCreatureDisplayInfoExtraStore("CreatureDisplayInfoExtra.db2", CreatureDisplayInfoExtraLoadInfo::Instance()); DB2Storage sCreatureFamilyStore("CreatureFamily.db2", CreatureFamilyLoadInfo::Instance()); DB2Storage sCreatureModelDataStore("CreatureModelData.db2", CreatureModelDataLoadInfo::Instance()); @@ -495,7 +499,7 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale) LOAD_DB2(sCinematicCameraStore); LOAD_DB2(sCinematicSequencesStore); LOAD_DB2(sConversationLineStore); - LOAD_DB2(sCreatureDisplayInfoStore); + LOAD_DB2(sCreatureDisplayInfoStoreRaw); LOAD_DB2(sCreatureDisplayInfoExtraStore); LOAD_DB2(sCreatureFamilyStore); LOAD_DB2(sCreatureModelDataStore); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 16847de1a07aa..43cf071401802 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -64,7 +64,8 @@ TC_GAME_API extern DB2Storage sChrSpeciali TC_GAME_API extern DB2Storage sCinematicCameraStore; TC_GAME_API extern DB2Storage sCinematicSequencesStore; TC_GAME_API extern DB2Storage sConversationLineStore; -TC_GAME_API extern DB2Storage sCreatureDisplayInfoStore; +TC_GAME_API extern DB2Storage sCreatureDisplayInfoStoreRaw; +TC_GAME_API extern CreatureDisplayInfoStore sCreatureDisplayInfoStore; TC_GAME_API extern DB2Storage sCreatureDisplayInfoExtraStore; TC_GAME_API extern DB2Storage sCreatureFamilyStore; TC_GAME_API extern DB2Storage sCreatureModelDataStore; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index a6819e5b2f4fe..d04cfe1750b49 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -1952,6 +1952,12 @@ struct NPCSoundsEntry uint32 ack; }; +struct CreatureDisplayInfoStore +{ + bool HasRecord(uint32 id) const; + const CreatureDisplayInfoEntry * LookupEntry(uint32 id) const; +}; + #define MAX_OVERRIDE_SPELL 10 struct OverrideSpellDataEntry diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 3b1da26b2f3a8..4fb45a503cdbc 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -24,6 +24,7 @@ #include "CreatureAI.h" #include "CreatureAISelector.h" #include "CreatureGroups.h" +#include "CreatureOutfit.h" #include "DatabaseEnv.h" #include "Formulas.h" #include "GameEventMgr.h" @@ -76,10 +77,10 @@ VendorItem const* VendorItemData::FindItemCostPair(uint32 item_id, uint32 extend return nullptr; } -int32 CreatureTemplate::GetRandomValidModelId() const +uint32 CreatureTemplate::GetRandomValidModelId() const { uint8 c = 0; - int32 modelIDs[4]; + uint32 modelIDs[4]; if (Modelid1) modelIDs[c++] = Modelid1; if (Modelid2) modelIDs[c++] = Modelid2; @@ -89,7 +90,7 @@ int32 CreatureTemplate::GetRandomValidModelId() const return ((c>0) ? modelIDs[urand(0, c-1)] : 0); } -int32 CreatureTemplate::GetFirstValidModelId() const +uint32 CreatureTemplate::GetFirstValidModelId() const { if (Modelid1) return Modelid1; if (Modelid2) return Modelid2; @@ -100,42 +101,42 @@ int32 CreatureTemplate::GetFirstValidModelId() const uint32 CreatureTemplate::GetFirstInvisibleModel() const { - CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid1)); + CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid1); if (modelInfo && modelInfo->is_trigger) - return sObjectMgr->GetCreatureDisplay(Modelid1); + return Modelid1; - modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid2)); + modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid2); if (modelInfo && modelInfo->is_trigger) - return sObjectMgr->GetCreatureDisplay(Modelid2); + return Modelid2; - modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid3)); + modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid3); if (modelInfo && modelInfo->is_trigger) - return sObjectMgr->GetCreatureDisplay(Modelid3); + return Modelid3; - modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid4)); + modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid4); if (modelInfo && modelInfo->is_trigger) - return sObjectMgr->GetCreatureDisplay(Modelid4); + return Modelid4; return 11686; } uint32 CreatureTemplate::GetFirstVisibleModel() const { - CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid1)); + CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid1); if (modelInfo && !modelInfo->is_trigger) - return sObjectMgr->GetCreatureDisplay(Modelid1); + return Modelid1; - modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid2)); + modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid2); if (modelInfo && !modelInfo->is_trigger) - return sObjectMgr->GetCreatureDisplay(Modelid2); + return Modelid2; - modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid3)); + modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid3); if (modelInfo && !modelInfo->is_trigger) - return sObjectMgr->GetCreatureDisplay(Modelid3); + return Modelid3; - modelInfo = sObjectMgr->GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(Modelid4)); + modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid4); if (modelInfo && !modelInfo->is_trigger) - return sObjectMgr->GetCreatureDisplay(Modelid4); + return Modelid4; return 17519; } @@ -178,7 +179,7 @@ _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(UI64LIT(0)), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(false), m_cannotReachTimer(0), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), -m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_focusSpell(nullptr), m_focusDelay(0), m_shouldReacquireTarget(false), m_suppressedOrientation(0.0f), outfitId(0) +m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_focusSpell(nullptr), m_focusDelay(0), m_shouldReacquireTarget(false), m_suppressedOrientation(0.0f) { m_regenTimer = CREATURE_REGEN_INTERVAL; m_valuesCount = UNIT_END; @@ -244,30 +245,45 @@ void Creature::RemoveFromWorld() } } +void Creature::SetOutfit(std::shared_ptr const & outfit) +{ + // Set new outfit + if (m_outfit) + { + // if had old outfit + // then delay displayid setting to allow equipment + // to change by using invisible model in between + SetDisplayId(CreatureOutfit::invisible_model); + m_outfit = outfit; + } + else + { + // else set new outfit directly since we change from non-outfit->outfit + m_outfit = outfit; + SetDisplayId(outfit->GetDisplayId()); + } +} + void Creature::SendMirrorSound(Player* target, uint8 type) { - int32 outfitId = GetOutfit(); - if (outfitId < 0) + std::shared_ptr const & outfit = GetOutfit(); + if (!outfit) + return; + if (!outfit->npcsoundsid) + return; + if (auto const* npcsounds = sNPCSoundsStore.LookupEntry(outfit->npcsoundsid)) { - const CreatureOutfitContainer& outfits = sObjectMgr->GetCreatureOutfitMap(); - auto it = outfits.find(-outfitId); - if (it != outfits.end() && it->second.npcsoundsid) + switch (type) { - if (auto const* npcsounds = sNPCSoundsStore.LookupEntry(it->second.npcsoundsid)) - { - switch (type) - { - case 0: - PlayDistanceSound(npcsounds->hello, target); - break; - case 1: - PlayDistanceSound(npcsounds->goodbye, target); - break; - case 2: - PlayDistanceSound(npcsounds->pissed, target); - break; - } - } + case 0: + PlayDistanceSound(npcsounds->hello, target); + break; + case 1: + PlayDistanceSound(npcsounds->goodbye, target); + break; + case 2: + PlayDistanceSound(npcsounds->pissed, target); + break; } } } @@ -376,12 +392,7 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) return false; } - SetOutfit(ObjectMgr::ChooseDisplayId(GetCreatureTemplate(), data)); - uint32 displayID = sObjectMgr->GetCreatureDisplay(GetOutfit()); - if (IsMirrorImage()) - displayID = 11686; // invisible in beginning if a mirror image - RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); - + uint32 displayID = ObjectMgr::ChooseDisplayId(GetCreatureTemplate(), data); CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelRandomGender(&displayID); if (!minfo) // Cancel load if no model defined { @@ -459,8 +470,6 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, SetUInt32Value(UNIT_FIELD_FLAGS, unitFlags); SetUInt32Value(UNIT_FIELD_FLAGS_2, unitFlags2); SetUInt32Value(UNIT_FIELD_FLAGS_3, unitFlags3); - if (IsMirrorImage()) - new MirrorImageUpdate(this); SetUInt32Value(OBJECT_DYNAMIC_FLAGS, dynamicFlags); @@ -527,6 +536,13 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, void Creature::Update(uint32 diff) { + if (m_outfit && _changesMask[UNIT_FIELD_DISPLAYID] != 1 && Unit::GetDisplayId() == CreatureOutfit::invisible_model) + { + // has outfit, displayid is invisible and displayid update already sent to clients + // set outfit display + SetDisplayId(m_outfit->GetDisplayId()); + } + if (IsAIEnabled && m_TriggerJustRespawned) { m_TriggerJustRespawned = false; @@ -1155,8 +1171,6 @@ void Creature::SaveToDB(uint32 mapid, uint64 spawnMask) CreatureData& data = sObjectMgr->NewOrExistCreatureData(m_spawnId); uint32 displayId = GetNativeDisplayId(); - if (IsMirrorImage()) - displayId = 0; // For mirror images dont save displayid, it comes from outfit uint64 npcflag = GetUInt64Value(UNIT_NPC_FLAGS); uint32 unitFlags = GetUInt32Value(UNIT_FIELD_FLAGS); uint32 unitFlags2 = GetUInt32Value(UNIT_FIELD_FLAGS_2); @@ -1167,8 +1181,8 @@ void Creature::SaveToDB(uint32 mapid, uint64 spawnMask) CreatureTemplate const* cinfo = GetCreatureTemplate(); if (cinfo) { - if (displayId == sObjectMgr->GetCreatureDisplay(cinfo->Modelid1) || displayId == sObjectMgr->GetCreatureDisplay(cinfo->Modelid2) || - displayId == sObjectMgr->GetCreatureDisplay(cinfo->Modelid3) || displayId == sObjectMgr->GetCreatureDisplay(cinfo->Modelid4)) + if (displayId == cinfo->Modelid1 || displayId == cinfo->Modelid2 || + displayId == cinfo->Modelid3 || displayId == cinfo->Modelid4) displayId = 0; if (npcflag == cinfo->npcflag) @@ -1891,8 +1905,6 @@ void Creature::Respawn(bool force) { SetDisplayId(displayID); SetNativeDisplayId(displayID); - if (IsMirrorImage()) - new MirrorImageUpdate(this); SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender); } @@ -2870,7 +2882,41 @@ void Creature::SetObjectScale(float scale) } } +uint32 Creature::GetDisplayId() const +{ + if (m_outfit && m_outfit->GetId()) + return m_outfit->GetId(); + return Unit::GetDisplayId(); +} + void Creature::SetDisplayId(uint32 modelId) +{ + if (auto const & outfit = sObjectMgr->GetOutfit(modelId)) + { + SetOutfit(outfit); + return; + } + else + { + if (!m_outfit || modelId != m_outfit->GetDisplayId()) + { + // no outfit or outfit's real modelid doesnt match modelid being set + // remove outfit and continue setting the new model + m_outfit.reset(); + SetMirrorImageFlag(false); + } + else + { + // outfit's real modelid being set + // add flags and continue setting the model + SetMirrorImageFlag(true); + } + } + + SetDisplayIdRaw(modelId); +} + +void Creature::SetDisplayIdRaw(uint32 modelId) { Unit::SetDisplayId(modelId); @@ -3038,34 +3084,6 @@ void Creature::ClearTextRepeatGroup(uint8 textGroup) groupItr->second.clear(); } -MirrorImageUpdate::MirrorImageUpdate(Creature* creature) : BasicEvent(), creature(creature) -{ - static uint32 delay = 1; - creature->m_Events.AddEvent(this, creature->m_Events.CalculateTime(delay)); - creature->SetDisplayId(11686); // invisible in beginning if a mirror image - creature->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); -} - -bool MirrorImageUpdate::Execute(uint64 /*e_time*/, uint32 /*p_time*/) -{ - // From AuraEffect::HandleAuraCloneCaster - int32 outfitId = creature->GetOutfit(); - if (outfitId < 0) - { - const CreatureOutfitContainer& outfits = sObjectMgr->GetCreatureOutfitMap(); - auto it = outfits.find(-outfitId); - if (it != outfits.end()) - { - creature->SetDisplayId(it->second.displayId); - creature->SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); - return true; - } - } - creature->SetDisplayId(creature->GetNativeDisplayId()); - creature->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); - return true; -} - bool Creature::CanGiveExperience() const { return !IsCritter() diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index c9ee9541d5fb8..3f23227114bb7 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -29,6 +29,9 @@ #include +class CreatureOutfit; +#include + class CreatureAI; class CreatureGroup; class Group; @@ -70,10 +73,12 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma void SetObjectScale(float scale) override; void SetDisplayId(uint32 modelId) override; + uint32 GetDisplayId() const final; + void SetDisplayIdRaw(uint32 modelId); - void SetOutfit(int32 outfit) { outfitId = outfit; }; - int32 GetOutfit() const { return outfitId; }; - bool IsMirrorImage() const { return outfitId < 0; }; + std::shared_ptr & GetOutfit() { return m_outfit; }; + void SetOutfit(std::shared_ptr const & outfit); + void SetMirrorImageFlag(bool on) { if (on) SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); else RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); }; void SendMirrorSound(Player* target, uint8 type); void DisappearAndDie(); @@ -417,7 +422,7 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma ObjectGuid m_suppressedTarget; // Stores the creature's "real" target while casting float m_suppressedOrientation; // Stores the creature's "real" orientation while casting - int32 outfitId; + std::shared_ptr m_outfit; CreatureTextRepeatGroup m_textRepeat; }; @@ -448,11 +453,4 @@ class TC_GAME_API ForcedDespawnDelayEvent : public BasicEvent Seconds const m_respawnTimer; }; -struct MirrorImageUpdate : BasicEvent -{ - MirrorImageUpdate(Creature* creature); - bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override; - Creature* creature; -}; - #endif diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 091bfc7aa9064..b933f6cb39909 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -305,10 +305,10 @@ struct TC_GAME_API CreatureTemplate uint32 Entry; uint32 DifficultyEntry[MAX_CREATURE_DIFFICULTIES]; uint32 KillCredit[MAX_KILL_CREDIT]; - int32 Modelid1; - int32 Modelid2; - int32 Modelid3; - int32 Modelid4; + uint32 Modelid1; + uint32 Modelid2; + uint32 Modelid3; + uint32 Modelid4; std::string Name; std::string FemaleName; std::string SubName; @@ -367,8 +367,8 @@ struct TC_GAME_API CreatureTemplate uint32 MechanicImmuneMask; uint32 flags_extra; uint32 ScriptID; - int32 GetRandomValidModelId() const; - int32 GetFirstValidModelId() const; + uint32 GetRandomValidModelId() const; + uint32 GetFirstValidModelId() const; uint32 GetFirstInvisibleModel() const; uint32 GetFirstVisibleModel() const; diff --git a/src/server/game/Entities/Creature/CreatureOutfit.cpp b/src/server/game/Entities/Creature/CreatureOutfit.cpp new file mode 100644 index 0000000000000..2134d4ea7329d --- /dev/null +++ b/src/server/game/Entities/Creature/CreatureOutfit.cpp @@ -0,0 +1,26 @@ +#include "CreatureOutfit.h" +#include "DB2Structure.h" // ChrRacesEntry +#include "DB2Stores.h" // sChrRacesStore, sDB2Manager + +CreatureOutfit::CreatureOutfit(uint8 race, Gender gender) : race(race), gender(gender) +{ + const ChrRacesEntry* rEntry = sChrRacesStore.LookupEntry(race); + if (!rEntry) + { + rEntry = sChrRacesStore.LookupEntry(RACE_HUMAN); + } + switch (gender) + { + case GENDER_FEMALE: displayId = rEntry->FemaleDisplayID; break; + default: displayId = rEntry->MaleDisplayID; break; + } +} + +CreatureOutfit& CreatureOutfit::SetItemEntry(EquipmentSlots slot, uint32 item_entry, uint32 appearancemodid) +{ + if (uint32 display = sDB2Manager.GetItemDisplayId(item_entry, appearancemodid)) + outfitdisplays[slot] = display; + else + outfitdisplays[slot] = 0; + return *this; +} diff --git a/src/server/game/Entities/Creature/CreatureOutfit.h b/src/server/game/Entities/Creature/CreatureOutfit.h new file mode 100644 index 0000000000000..f93e9bef5e064 --- /dev/null +++ b/src/server/game/Entities/Creature/CreatureOutfit.h @@ -0,0 +1,71 @@ +#ifndef CREATURE_OUTFIT_H +#define CREATURE_OUTFIT_H + +#include "Define.h" +#include "Player.h" // EquipmentSlots +#include "SharedDefines.h" // Gender +#include + +class Creature; +class WorldSession; + +class CreatureOutfit +{ +public: + friend class ObjectMgr; + + // Remember to change DB query too! + static constexpr uint32 max_custom_displays = 3; + static constexpr uint32 invisible_model = 11686; + static constexpr uint32 max_real_modelid = 0x7FFFFFFF; + static constexpr EquipmentSlots item_slots[] = + { + EQUIPMENT_SLOT_HEAD, + EQUIPMENT_SLOT_SHOULDERS, + EQUIPMENT_SLOT_BODY, + EQUIPMENT_SLOT_CHEST, + EQUIPMENT_SLOT_WAIST, + EQUIPMENT_SLOT_LEGS, + EQUIPMENT_SLOT_FEET, + EQUIPMENT_SLOT_WRISTS, + EQUIPMENT_SLOT_HANDS, + EQUIPMENT_SLOT_TABARD, + EQUIPMENT_SLOT_BACK, + }; + + static bool IsFake(uint32 modelid) { return modelid > max_real_modelid; }; + + CreatureOutfit(uint8 race, Gender gender); + + uint8 Class = 1; + uint8 face = 0; + uint8 skin = 0; + uint8 hair = 0; + uint8 facialhair = 0; + uint8 haircolor = 0; + uint8 customdisplay[max_custom_displays] = { 0 }; + uint32 outfitdisplays[EQUIPMENT_SLOT_END] = { 0 }; + uint32 npcsoundsid = 0; + uint64 guild = 0; + + uint32 GetId() const { return id; } + uint8 GetGender() const { return gender; } + uint8 GetRace() const { return race; } + uint32 GetDisplayId() const { return displayId; } + + CreatureOutfit& SetItemEntry(EquipmentSlots slot, uint32 item_entry, uint32 appearancemodid = 0); + CreatureOutfit& SetItemDisplay(EquipmentSlots slot, uint32 displayid) + { + outfitdisplays[slot] = displayid; + return *this; + } + +private: + CreatureOutfit() {}; + uint32 id = 0; + uint8 gender; + uint8 race; + uint32 displayId; +}; + +#endif diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 41a091c36e452..8185b0107320a 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1650,7 +1650,7 @@ class TC_GAME_API Unit : public WorldObject } void UpdateInterruptMask(); - uint32 GetDisplayId() const { return GetUInt32Value(UNIT_FIELD_DISPLAYID); } + virtual uint32 GetDisplayId() const { return GetUInt32Value(UNIT_FIELD_DISPLAYID); } virtual void SetDisplayId(uint32 modelId); uint32 GetNativeDisplayId() const { return GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID); } void RestoreDisplayId(); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index a8b0e7c9569f2..07a1453ad1b34 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -20,6 +20,8 @@ #include "ArenaTeamMgr.h" #include "Chat.h" #include "Containers.h" +#include "Creature.h" +#include "CreatureOutfit.h" #include "DatabaseEnv.h" #include "DB2Stores.h" #include "DisableMgr.h" @@ -459,10 +461,10 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) for (uint8 i = 0; i < MAX_KILL_CREDIT; ++i) creatureTemplate.KillCredit[i] = fields[4 + i].GetUInt32(); - creatureTemplate.Modelid1 = fields[6].GetInt32(); - creatureTemplate.Modelid2 = fields[7].GetInt32(); - creatureTemplate.Modelid3 = fields[8].GetInt32(); - creatureTemplate.Modelid4 = fields[9].GetInt32(); + creatureTemplate.Modelid1 = fields[6].GetUInt32(); + creatureTemplate.Modelid2 = fields[7].GetUInt32(); + creatureTemplate.Modelid3 = fields[8].GetUInt32(); + creatureTemplate.Modelid4 = fields[9].GetUInt32(); creatureTemplate.Name = fields[10].GetString(); creatureTemplate.FemaleName = fields[11].GetString(); creatureTemplate.SubName = fields[12].GetString(); @@ -867,66 +869,66 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) if (cInfo->Modelid1) { - CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(sObjectMgr->GetCreatureDisplay(cInfo->Modelid1)); + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid1); if (!displayEntry) { - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid1 id (%i), this can crash the client.", cInfo->Entry, cInfo->Modelid1); + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid1 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid1); const_cast(cInfo)->Modelid1 = 0; } else displayScaleEntry = displayEntry; - CreatureModelInfo const* modelInfo = GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(cInfo->Modelid1)); + CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid1); if (!modelInfo) - TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid1` = %i listed by creature (Entry: %u).", cInfo->Modelid1, cInfo->Entry); + TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid1` = %u listed by creature (Entry: %u).", cInfo->Modelid1, cInfo->Entry); } if (cInfo->Modelid2) { - CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(sObjectMgr->GetCreatureDisplay(cInfo->Modelid2)); + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid2); if (!displayEntry) { - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid2 id (%i), this can crash the client.", cInfo->Entry, cInfo->Modelid2); + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid2 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid2); const_cast(cInfo)->Modelid2 = 0; } else if (!displayScaleEntry) displayScaleEntry = displayEntry; - CreatureModelInfo const* modelInfo = GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(cInfo->Modelid2)); + CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid2); if (!modelInfo) - TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid2` = %i listed by creature (Entry: %u).", cInfo->Modelid2, cInfo->Entry); + TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid2` = %u listed by creature (Entry: %u).", cInfo->Modelid2, cInfo->Entry); } if (cInfo->Modelid3) { - CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(sObjectMgr->GetCreatureDisplay(cInfo->Modelid3)); + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid3); if (!displayEntry) { - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid3 id (%i), this can crash the client.", cInfo->Entry, cInfo->Modelid3); + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid3 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid3); const_cast(cInfo)->Modelid3 = 0; } else if (!displayScaleEntry) displayScaleEntry = displayEntry; - CreatureModelInfo const* modelInfo = GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(cInfo->Modelid3)); + CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid3); if (!modelInfo) - TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid3` = %i listed by creature (Entry: %u).", cInfo->Modelid3, cInfo->Entry); + TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid3` = %u listed by creature (Entry: %u).", cInfo->Modelid3, cInfo->Entry); } if (cInfo->Modelid4) { - CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(sObjectMgr->GetCreatureDisplay(cInfo->Modelid4)); + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid4); if (!displayEntry) { - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid4 id (%i), this can crash the client.", cInfo->Entry, cInfo->Modelid4); + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) lists non-existing Modelid4 id (%u), this can crash the client.", cInfo->Entry, cInfo->Modelid4); const_cast(cInfo)->Modelid4 = 0; } else if (!displayScaleEntry) displayScaleEntry = displayEntry; - CreatureModelInfo const* modelInfo = GetCreatureModelInfo(sObjectMgr->GetCreatureDisplay(cInfo->Modelid4)); + CreatureModelInfo const* modelInfo = GetCreatureModelInfo(cInfo->Modelid4); if (!modelInfo) - TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid4` = %i listed by creature (Entry: %u).", cInfo->Modelid4, cInfo->Entry); + TC_LOG_ERROR("sql.sql", "No model data exist for `Modelid4` = %u listed by creature (Entry: %u).", cInfo->Modelid4, cInfo->Entry); } if (!displayScaleEntry) @@ -1397,6 +1399,7 @@ void ObjectMgr::LoadEquipmentTemplates() CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelId) const { + modelId = GetRealDisplayId(modelId); CreatureModelContainer::const_iterator itr = _creatureModelStore.find(modelId); if (itr != _creatureModelStore.end()) return &(itr->second); @@ -1404,7 +1407,7 @@ CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelId) const return nullptr; } -int32 ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data /*= nullptr*/) +uint32 ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data /*= nullptr*/) { // Load creature model (display id) if (data && data->displayid) @@ -1446,6 +1449,12 @@ void ObjectMgr::ChooseCreatureFlags(CreatureTemplate const* cInfo, uint64& npcFl CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32* displayID) const { + { + uint32 displayid_temp = GetRealDisplayId(*displayID); + if (displayid_temp != *displayID) + return GetCreatureModelRandomGender(&displayid_temp); + } + CreatureModelInfo const* modelInfo = GetCreatureModelInfo(*displayID); if (!modelInfo) return nullptr; @@ -6137,7 +6146,7 @@ uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt CreatureTemplate const* mount_info = GetCreatureTemplate(mount_entry); if (mount_info) { - mount_id = sObjectMgr->GetCreatureDisplay(mount_info->GetRandomValidModelId()); + mount_id = mount_info->GetRandomValidModelId(); if (!mount_id) { TC_LOG_ERROR("sql.sql", "No displayid found for the taxi mount with the entry %u! Can't load it!", mount_entry); @@ -8278,7 +8287,7 @@ void ObjectMgr::LoadCreatureOutfits() { uint32 oldMSTime = getMSTime(); - _creatureOutfitStore.clear(); // for reload case (test only) + _creatureOutfitStore.clear(); QueryResult result = WorldDatabase.Query("SELECT entry, npcsoundsid, race, class, gender, skin, face, hair, haircolor, facialhair, feature1, feature2, feature3, " "head, head_appearance, shoulders, shoulders_appearance, body, body_appearance, chest, chest_appearance, waist, waist_appearance, " @@ -8300,48 +8309,55 @@ void ObjectMgr::LoadCreatureOutfits() uint32 i = 0; uint32 entry = fields[i++].GetUInt32(); - CreatureOutfit co; + if (!CreatureOutfit::IsFake(entry)) + { + TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has too low entry (entry <= %u). Ignoring.", entry, CreatureOutfit::max_real_modelid); + continue; + } - co.npcsoundsid = fields[i++].GetUInt32(); - if (co.npcsoundsid && !sNPCSoundsStore.HasRecord(co.npcsoundsid)) + std::shared_ptr co(new CreatureOutfit()); + + co->id = entry; + co->npcsoundsid = fields[i++].GetUInt32(); + if (co->npcsoundsid && !sNPCSoundsStore.HasRecord(co->npcsoundsid)) { - TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has incorrect npcsoundsid (%u). Using 0.", entry, co.npcsoundsid); - co.npcsoundsid = 0; + TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has incorrect npcsoundsid (%u). Using 0.", entry, co->npcsoundsid); + co->npcsoundsid = 0; } - co.race = fields[i++].GetUInt8(); - const ChrRacesEntry* rEntry = sChrRacesStore.LookupEntry(co.race); + co->race = fields[i++].GetUInt8(); + const ChrRacesEntry* rEntry = sChrRacesStore.LookupEntry(co->race); if (!rEntry) { - TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has incorrect race (%u).", entry, uint32(co.race)); + TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has incorrect race (%u).", entry, uint32(co->race)); continue; } - co.Class = fields[i++].GetUInt8(); - const ChrClassesEntry* cEntry = sChrClassesStore.LookupEntry(co.Class); + co->Class = fields[i++].GetUInt8(); + const ChrClassesEntry* cEntry = sChrClassesStore.LookupEntry(co->Class); if (!cEntry) { - TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has incorrect class (%u).", entry, uint32(co.Class)); + TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has incorrect class (%u).", entry, uint32(co->Class)); continue; } - co.gender = fields[i++].GetUInt8(); - switch (co.gender) + co->gender = fields[i++].GetUInt8(); + switch (co->gender) { - case GENDER_FEMALE: co.displayId = rEntry->FemaleDisplayID; break; - case GENDER_MALE: co.displayId = rEntry->MaleDisplayID; break; + case GENDER_FEMALE: co->displayId = rEntry->FemaleDisplayID; break; + case GENDER_MALE: co->displayId = rEntry->MaleDisplayID; break; default: - TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has invalid gender %u", entry, uint32(co.gender)); + TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has invalid gender %u", entry, uint32(co->gender)); continue; } - co.skin = fields[i++].GetUInt8(); - co.face = fields[i++].GetUInt8(); - co.hair = fields[i++].GetUInt8(); - co.haircolor = fields[i++].GetUInt8(); - co.facialhair = fields[i++].GetUInt8(); + co->skin = fields[i++].GetUInt8(); + co->face = fields[i++].GetUInt8(); + co->hair = fields[i++].GetUInt8(); + co->haircolor = fields[i++].GetUInt8(); + co->facialhair = fields[i++].GetUInt8(); for (uint32 j = 0; j < CreatureOutfit::max_custom_displays; ++j) - co.customdisplay[j] = fields[i++].GetUInt8(); - for (uint32 j = 0; j < CreatureOutfit::max_outfit_displays; ++j) + co->customdisplay[j] = fields[i++].GetUInt8(); + for (EquipmentSlots slot : CreatureOutfit::item_slots) { int64 displayInfo = fields[i++].GetInt64(); uint32 appearancemodid = fields[i++].GetUInt32(); @@ -8349,11 +8365,11 @@ void ObjectMgr::LoadCreatureOutfits() { uint32 item_entry = static_cast(displayInfo); if (uint32 display = sDB2Manager.GetItemDisplayId(item_entry, appearancemodid)) - co.outfit[j] = display; + co->outfitdisplays[slot] = display; else { TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has invalid (item entry, appearance) combination: %u, %u. Ignoring.", entry, item_entry, appearancemodid); - co.outfit[j] = 0; + co->outfitdisplays[slot] = 0; } } else // display @@ -8362,12 +8378,12 @@ void ObjectMgr::LoadCreatureOutfits() { TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` is using displayid (negative value), but also has appearance set (displayid, appearance): %s, %u. Ignoring appearance.", entry, std::to_string(displayInfo).c_str(), appearancemodid); } - co.outfit[j] = static_cast(-displayInfo); + co->outfitdisplays[slot] = static_cast(-displayInfo); } } - co.guild = fields[i++].GetUInt64(); + co->guild = fields[i++].GetUInt64(); - _creatureOutfitStore[entry] = co; + _creatureOutfitStore[co->id] = std::move(co); ++count; } @@ -8376,6 +8392,26 @@ void ObjectMgr::LoadCreatureOutfits() TC_LOG_INFO("server.loading", ">> Loaded %u creature outfits in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } +std::shared_ptr const & ObjectMgr::GetOutfit(uint32 modelid) const +{ + static std::shared_ptr empty; + if (CreatureOutfit::IsFake(modelid)) + { + auto const & outfits = GetCreatureOutfitMap(); + auto it = outfits.find(modelid); + if (it != outfits.end()) + return it->second; + } + return empty; +} + +uint32 ObjectMgr::GetRealDisplayId(uint32 modelid) const +{ + if (std::shared_ptr outfit = GetOutfit(modelid)) + return outfit->displayId; + return modelid; +} + void ObjectMgr::LoadGameTele() { uint32 oldMSTime = getMSTime(); @@ -9744,19 +9780,6 @@ PlayerInfo const* ObjectMgr::GetPlayerInfo(uint32 race, uint32 class_) const return info; } -uint32 ObjectMgr::GetCreatureDisplay(int32 modelid) const -{ - if (modelid >= 0) - return modelid; - - const CreatureOutfitContainer& outfits = GetCreatureOutfitMap(); - CreatureOutfitContainer::const_iterator it = outfits.find(-modelid); - if (it != outfits.end()) - return 11686; // invisible for mirror image - - return 0; -} - void ObjectMgr::LoadRaceAndClassExpansionRequirements() { uint32 oldMSTime = getMSTime(); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index c396baa824df0..d3b8ba9892279 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -36,6 +36,9 @@ #include #include +#include +class CreatureOutfit; + class Item; class Unit; class Vehicle; @@ -169,30 +172,6 @@ struct GameTele typedef std::unordered_map GameTeleContainer; -struct CreatureOutfit -{ - // Remember to change DB query too! - static constexpr uint32 max_outfit_displays = 11; - static constexpr uint32 max_custom_displays = 3; - - uint8 race; - uint8 Class; - uint8 gender; - uint8 face; - uint8 skin; - uint8 hair; - uint8 facialhair; - uint8 haircolor; - uint8 customdisplay[max_custom_displays]; - uint64 guild; - uint32 npcsoundsid; - - uint32 displayId; - uint32 outfit[max_outfit_displays]; -}; - -typedef std::unordered_map CreatureOutfitContainer; - enum ScriptsType { SCRIPTS_FIRST = 1, @@ -961,6 +940,8 @@ class TC_GAME_API ObjectMgr typedef std::map CharacterConversionMap; + typedef std::unordered_map> CreatureOutfitContainer; + GameObjectTemplate const* GetGameObjectTemplate(uint32 entry) const; GameObjectTemplateContainer const* GetGameObjectTemplates() const { return &_gameObjectTemplateStore; } int LoadReferenceVendor(int32 vendor, int32 item, std::set *skip_vendors); @@ -972,7 +953,7 @@ class TC_GAME_API ObjectMgr CreatureTemplateContainer const* GetCreatureTemplates() const { return &_creatureTemplateStore; } CreatureModelInfo const* GetCreatureModelInfo(uint32 modelId) const; CreatureModelInfo const* GetCreatureModelRandomGender(uint32* displayID) const; - static int32 ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data = nullptr); + static uint32 ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData const* data = nullptr); static void ChooseCreatureFlags(CreatureTemplate const* cInfo, uint64& npcFlags, uint32& unitFlags, uint32& unitFlags2, uint32& unitFlags3, uint32& dynamicFlags, CreatureData const* data = nullptr); EquipmentInfo const* GetEquipmentInfo(uint32 entry, int8& id) const; CreatureAddon const* GetCreatureAddon(ObjectGuid::LowType lowguid) const; @@ -981,7 +962,6 @@ class TC_GAME_API ObjectMgr CreatureAddon const* GetCreatureTemplateAddon(uint32 entry) const; ItemTemplate const* GetItemTemplate(uint32 entry) const; ItemTemplateContainer const* GetItemTemplateStore() const { return &_itemTemplateStore; } - uint32 GetCreatureDisplay(int32 modelid) const; InstanceTemplate const* GetInstanceTemplate(uint32 mapId) const; @@ -1256,7 +1236,6 @@ class TC_GAME_API ObjectMgr void LoadNPCSpellClickSpells(); - void LoadCreatureOutfits(); void LoadGameTele(); void LoadGossipMenu(); @@ -1486,6 +1465,9 @@ class TC_GAME_API ObjectMgr bool DeleteGameTele(std::string const& name); const CreatureOutfitContainer& GetCreatureOutfitMap() const { return _creatureOutfitStore; } + std::shared_ptr const & GetOutfit(uint32 modelid) const; + uint32 GetRealDisplayId(uint32 modelid) const; + void LoadCreatureOutfits(); Trainer::Trainer const* GetTrainer(uint32 trainerId) const; uint32 GetCreatureDefaultTrainer(uint32 creatureId) const; diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index c41d54f893e02..9aca62dd6d963 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -95,10 +95,14 @@ void WorldSession::HandleCreatureQuery(WorldPackets::Query::QueryCreature& packe for (uint32 i = 0; i < MAX_KILL_CREDIT; ++i) stats.ProxyCreatureID[i] = creatureInfo->KillCredit[i]; - stats.CreatureDisplayID[0] = sObjectMgr->GetCreatureDisplay(creatureInfo->Modelid1); - stats.CreatureDisplayID[1] = sObjectMgr->GetCreatureDisplay(creatureInfo->Modelid2); - stats.CreatureDisplayID[2] = sObjectMgr->GetCreatureDisplay(creatureInfo->Modelid3); - stats.CreatureDisplayID[3] = sObjectMgr->GetCreatureDisplay(creatureInfo->Modelid4); + // stats.CreatureDisplayID[0] = sObjectMgr->GetRealDisplayId(creatureInfo->Modelid1); + // stats.CreatureDisplayID[1] = sObjectMgr->GetRealDisplayId(creatureInfo->Modelid2); + // stats.CreatureDisplayID[2] = sObjectMgr->GetRealDisplayId(creatureInfo->Modelid3); + // stats.CreatureDisplayID[3] = sObjectMgr->GetRealDisplayId(creatureInfo->Modelid4); + stats.CreatureDisplayID[0] = creatureInfo->Modelid1; + stats.CreatureDisplayID[1] = creatureInfo->Modelid2; + stats.CreatureDisplayID[2] = creatureInfo->Modelid3; + stats.CreatureDisplayID[3] = creatureInfo->Modelid4; stats.HpMulti = creatureInfo->ModHealth; stats.EnergyMulti = creatureInfo->ModMana; diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index f98cc4d95aa61..847fe9a724eb4 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -19,6 +19,7 @@ #include "WorldSession.h" #include "CollectionMgr.h" #include "Common.h" +#include "CreatureOutfit.h" #include "DatabaseEnv.h" #include "GameObjectAI.h" #include "GameObjectPackets.h" @@ -502,44 +503,38 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPackets::Spells::GetMirrorI if (Creature* creature = unit->ToCreature()) { - int32 outfitId = creature->GetOutfit(); - if (outfitId < 0) + if (std::shared_ptr const & outfit_ptr = creature->GetOutfit()) { - const CreatureOutfitContainer& outfits = sObjectMgr->GetCreatureOutfitMap(); - CreatureOutfitContainer::const_iterator it = outfits.find(-outfitId); - if (it != outfits.end()) + CreatureOutfit const& outfit = *outfit_ptr; + WorldPackets::Spells::MirrorImageComponentedData mirrorImageComponentedData; + mirrorImageComponentedData.UnitGUID = guid; + mirrorImageComponentedData.DisplayID = outfit.GetDisplayId(); + mirrorImageComponentedData.RaceID = outfit.GetRace(); + mirrorImageComponentedData.Gender = outfit.GetGender(); + mirrorImageComponentedData.ClassID = outfit.Class; + + mirrorImageComponentedData.SkinColor = outfit.skin; + mirrorImageComponentedData.FaceVariation = outfit.face; + mirrorImageComponentedData.HairVariation = outfit.hair; + mirrorImageComponentedData.HairColor = outfit.haircolor; + mirrorImageComponentedData.BeardVariation = outfit.facialhair; + + static_assert(CreatureOutfit::max_custom_displays == PLAYER_CUSTOM_DISPLAY_SIZE, "Amount of custom displays for player has changed - change it for dressnpcs as well"); + for (uint32 i = 0; i < PLAYER_CUSTOM_DISPLAY_SIZE; ++i) + mirrorImageComponentedData.CustomDisplay[i] = outfit.customdisplay[i]; + mirrorImageComponentedData.GuildGUID = ObjectGuid::Empty; + if (outfit.guild) { - CreatureOutfit const& outfit = it->second; - WorldPackets::Spells::MirrorImageComponentedData mirrorImageComponentedData; - mirrorImageComponentedData.UnitGUID = guid; - mirrorImageComponentedData.DisplayID = outfit.displayId; - mirrorImageComponentedData.RaceID = outfit.race; - mirrorImageComponentedData.Gender = outfit.gender; - mirrorImageComponentedData.ClassID = outfit.Class; - - mirrorImageComponentedData.SkinColor = outfit.skin; - mirrorImageComponentedData.FaceVariation = outfit.face; - mirrorImageComponentedData.HairVariation = outfit.hair; - mirrorImageComponentedData.HairColor = outfit.haircolor; - mirrorImageComponentedData.BeardVariation = outfit.facialhair; - - static_assert(CreatureOutfit::max_custom_displays == PLAYER_CUSTOM_DISPLAY_SIZE, "Amount of custom displays for player has changed - change it for dressnpcs as well"); - for (uint32 i = 0; i < PLAYER_CUSTOM_DISPLAY_SIZE; ++i) - mirrorImageComponentedData.CustomDisplay[i] = outfit.customdisplay[i]; - mirrorImageComponentedData.GuildGUID = ObjectGuid::Empty; - if (outfit.guild) - { - if (Guild* guild = sGuildMgr->GetGuildById(outfit.guild)) - mirrorImageComponentedData.GuildGUID = guild->GetGUID(); - } + if (Guild* guild = sGuildMgr->GetGuildById(outfit.guild)) + mirrorImageComponentedData.GuildGUID = guild->GetGUID(); + } - mirrorImageComponentedData.ItemDisplayID.reserve(11); - for (auto const& display : it->second.outfit) - mirrorImageComponentedData.ItemDisplayID.push_back(display); + mirrorImageComponentedData.ItemDisplayID.reserve(11); + for (auto const& slot : CreatureOutfit::item_slots) + mirrorImageComponentedData.ItemDisplayID.push_back(outfit.outfitdisplays[slot]); - SendPacket(mirrorImageComponentedData.Write()); - return; - } + SendPacket(mirrorImageComponentedData.Write()); + return; } } diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 8c4723ef103a5..b3a59fbda6df4 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -2032,7 +2032,7 @@ void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode, uint32 model_id = 0; // choose a model, based on trigger flag - if (uint32 modelid = sObjectMgr->GetCreatureDisplay(sObjectMgr->ChooseDisplayId(ci))) + if (uint32 modelid = sObjectMgr->ChooseDisplayId(ci)) model_id = modelid; target->SetDisplayId(model_id); @@ -2073,7 +2073,7 @@ void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode, uint32 cr_id = target->GetAuraEffectsByType(SPELL_AURA_MOUNTED).front()->GetMiscValue(); if (CreatureTemplate const* ci = sObjectMgr->GetCreatureTemplate(cr_id)) { - uint32 displayID = sObjectMgr->GetCreatureDisplay(ObjectMgr::ChooseDisplayId(ci)); + uint32 displayID = ObjectMgr::ChooseDisplayId(ci); sObjectMgr->GetCreatureModelRandomGender(&displayID); target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, displayID); @@ -2567,7 +2567,7 @@ void AuraEffect::HandleAuraMounted(AuraApplication const* aurApp, uint8 mode, bo if (!displayId || vehicleId) { - displayId = sObjectMgr->GetCreatureDisplay(ObjectMgr::ChooseDisplayId(creatureInfo)); + displayId = ObjectMgr::ChooseDisplayId(creatureInfo); sObjectMgr->GetCreatureModelRandomGender(&displayId); } diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp index da9ea3bc7be7d..58ab003924bcf 100644 --- a/src/server/scripts/Commands/cs_modify.cpp +++ b/src/server/scripts/Commands/cs_modify.cpp @@ -821,9 +821,6 @@ class modify_commandscript : public CommandScript return false; target->SetDisplayId(display_id); - target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); - if (Creature* crea = target->ToCreature()) - crea->SetOutfit(display_id); return true; } diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index e2524f97255e7..2ecdd51b23061 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -955,8 +955,6 @@ class npc_commandscript : public CommandScript creature->SetDisplayId(displayId); creature->SetNativeDisplayId(displayId); - creature->SetOutfit(displayId); - creature->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE); creature->SaveToDB(); diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 0463410379140..78af9d771ee7c 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -31,6 +31,7 @@ EndScriptData */ #include "Chat.h" #include "ConversationDataStore.h" #include "Creature.h" +#include "CreatureOutfit.h" #include "CreatureTextMgr.h" #include "DatabaseEnv.h" #include "DisableMgr.h" @@ -471,10 +472,12 @@ class reload_commandscript : public CommandScript { for (auto e : map->GetCreatureBySpawnIdStore()) { - if (e.second->IsMirrorImage()) - new MirrorImageUpdate(e.second); + auto const & outfit = e.second->GetOutfit(); + if (outfit && outfit->GetId()) + e.second->SetDisplayId(outfit->GetId()); } }); + handler->SendGlobalGMSysMessage("DB table `creature_template_outfits` reloaded."); return true; } diff --git a/src/server/scripts/Custom/DressNPCs/README.md b/src/server/scripts/Custom/DressNPCs/README.md index 8778c9403e9cc..bbb717c671dbf 100644 --- a/src/server/scripts/Custom/DressNPCs/README.md +++ b/src/server/scripts/Custom/DressNPCs/README.md @@ -1,17 +1,18 @@ # DressNPCs [![Build Status](https://travis-ci.org/Rochet2/TrinityCore.svg?branch=dressnpcs_7.x)](https://travis-ci.org/Rochet2/TrinityCore) -#### About +## About This patch allows you to dress up armor on NPCs as well as choose their facial features. Create unique looking NPCs by defining their gender, race, facial features, clothing and much more. -All this is done in the database. No client edits required. +All this is done through the database. No client edits required. Source: http://rochet2.github.io/Dress-NPCs.html Known bugs: -- Portraits of the NPCs may not work properly at times - a client side visual bug (blizzard's fault). +- Portraits of the NPCs may not work properly at times - a client side visual bug, cannot fix. +- Some skins are not available. These are usually skins that are not available for players for a specific race. This is a client side limitation, cannot fix. - Normally NPCs have no sound replies when you talk to them. This is a client side limitation and can be fixed with a client patch. Additionally a **workaround has been included** which sends interaction sounds from the core unless disabled in CMake. -#### Installation +## Installation Available as: - Direct merge: https://github.com/Rochet2/TrinityCore/tree/dressnpcs_7.x @@ -36,20 +37,41 @@ After compiling: - TrinityCore auto updater should run needed SQLs automatically. - If you do not use the auto updater then run files named `*_dressnpcs.sql` from `\sql\custom` to your databases. -#### Usage -Before compiling the core you can choose in CMake to disable sound workaround if you have a client patch for the NPC sounds. +## Usage +- Before compiling the core you can choose in CMake to disable sound workaround if you have a client patch for the NPC sounds. The option is `DISABLE_DRESSNPCS_CORESOUNDS`. +- Remember to check server startup errors, they tell when you do things wrong. +- For some skins you must use a specific class, like for death knight skins and eyes. +- Unplayable races like naga are possible to be used as race, experiment :). +- The patch also adds `.reload creature_template_outfit` command. You can use it to reload the creature outfit table for testing. -To make an outfit create a row to `creature_template_outfits` table with your desired race, class, gender and equipped items. You can freely choose the entry number. +#### Use through DB +To make an outfit create a row to `creature_template_outfits` table with your desired race, class, gender and equipped items. +- You can freely choose the entry number, but it must be higher than `2147483647`. This is to avoid mixing with existing modelids. - The items can use positive value as item entry and negative for displayid. -- Appearances are usually between 0 and 10 and they define the look of the item. Different appearances are usually used by mythic and heroic versions of an item. An appearance only works when using an item entry (a positive value) for the equipped item definition. -- `guildid` refers to an actual guild from characters table and it is used to define the tabard looks of the creature if one is equipped. -- `npcsoundsid` refers to `NPCSounds.dbc/db2`. You can define what gossip replies to use for the NPC with the core side workaround for missing sounds for gossip. To create completely new sound combinations you can use hotfixes database or edit the DBC file. +- Appearances are usually between 0 and 10 and they define the look of the item. Different appearances are usually used by mythic and heroic versions of an item. The appearance column takes effect only when using an item entry (a positive value) for the equipped item definition. +- `guildid` refers to an actual guild from characters table and it is used to define the tabard looks of the creature if one is equipped. So you must make a guild and set a tabard for it and use it's ID in the column for the outfit. +- `npcsoundsid` refers to `NPCSounds.dbc/db2`. In this column you can define what gossip replies to use for the NPC with the core side workaround for missing sounds for gossip. To create completely new sound combinations you can use hotfixes database or edit the DBC file. -Create an NPC. Set the `creature_template_outfits` entry to the modelID column in `creature_template`, but __make it negative__. +You can use the outfit entry as modelid in creature template, smart scripts and elsewhere in the DB and core for setting a modelid/displayid, like `creature->SetDisplayId(outfitid)`. -Clear wow cache folder, restart server and spawn the NPC. Remember to check startup errors. +#### Use through C++ +You can create an outfit in C++, here is an example: +```c++ +#include "CreatureOutfit.h" // CreatureOutfit, shared_ptr +#include "Player.h" // EquipmentSlots +#include "SharedDefines.h" // Gender +#include "Creature.h" // Creature -The patch also adds `.reload creature_template_outfit` command. You can use it to reload the creature outfit table for testing. +// Create outfit +std::shared_ptr outfit(new CreatureOutfit(RACE_NIGHTELF, GENDER_MALE)); +outfit->SetItemDisplay(EQUIPMENT_SLOT_SHOULDERS, 43617); +outfit->skin = 2; -#### Bugs and Contact +// set it for a creature +creature->SetOutfit(outfit); +``` + +In C++ if you absolutely must change the displayid of a creature wears an outfit without removing the outfit you can use `creature->SetDisplayIdRaw(modelid);`. + +## Bugs and Contact Report issues and similar to http://rochet2.github.io/ From aa1965604d75014aac920f29193df83e4405392f Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sat, 17 Feb 2018 21:31:26 +0200 Subject: [PATCH 20/35] Update Example.sql --- src/server/scripts/Custom/DressNPCs/Example.sql | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/scripts/Custom/DressNPCs/Example.sql b/src/server/scripts/Custom/DressNPCs/Example.sql index ac739a6239a3f..ab0c26a4d468a 100644 --- a/src/server/scripts/Custom/DressNPCs/Example.sql +++ b/src/server/scripts/Custom/DressNPCs/Example.sql @@ -1,5 +1,5 @@ SET @NPCENTRY := 6; INSERT INTO `creature_template_outfits` (`entry`, `race`, `gender`, `skin`, `face`, `hair`, `haircolor`, `facialhair`, `head`, `shoulders`, `body`, `chest`, `waist`, `legs`, `feet`, `wrists`, `hands`, `back`, `tabard`) -VALUES (123, 11, 1, 14, 4, 10, 3, 5, -31286, -43617, 0, -26267, -26270, -26272, 0, 0, -43698, 0, 0); -UPDATE `creature_template` SET `modelid2` = -123 WHERE `entry` = @NPCENTRY; +VALUES (3000000123, 11, 1, 14, 4, 10, 3, 5, -31286, -43617, 0, -26267, -26270, -26272, 0, 0, -43698, 0, 0); +UPDATE `creature_template` SET `modelid2` = 3000000123 WHERE `entry` = @NPCENTRY; From 3009cd2a30a550a69a6f2b4cecec188e7c1b2d52 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sat, 17 Feb 2018 21:33:13 +0200 Subject: [PATCH 21/35] Update CreatureOutfit.h --- src/server/game/Entities/Creature/CreatureOutfit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Entities/Creature/CreatureOutfit.h b/src/server/game/Entities/Creature/CreatureOutfit.h index f93e9bef5e064..5941b468845a2 100644 --- a/src/server/game/Entities/Creature/CreatureOutfit.h +++ b/src/server/game/Entities/Creature/CreatureOutfit.h @@ -63,8 +63,8 @@ class CreatureOutfit private: CreatureOutfit() {}; uint32 id = 0; - uint8 gender; uint8 race; + uint8 gender; uint32 displayId; }; From 030c6ebba64b8854c5e3898d8545230510061a54 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sat, 17 Feb 2018 22:12:40 +0200 Subject: [PATCH 22/35] Update CreatureOutfit.h --- src/server/game/Entities/Creature/CreatureOutfit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Entities/Creature/CreatureOutfit.h b/src/server/game/Entities/Creature/CreatureOutfit.h index 5941b468845a2..d1d9027d34f49 100644 --- a/src/server/game/Entities/Creature/CreatureOutfit.h +++ b/src/server/game/Entities/Creature/CreatureOutfit.h @@ -9,7 +9,7 @@ class Creature; class WorldSession; -class CreatureOutfit +class TC_GAME_API CreatureOutfit { public: friend class ObjectMgr; From 54c1a57ac74197662e5db6b78352607bb89aacb6 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sat, 17 Feb 2018 23:04:34 +0200 Subject: [PATCH 23/35] Update CreatureOutfit.cpp --- src/server/game/Entities/Creature/CreatureOutfit.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/server/game/Entities/Creature/CreatureOutfit.cpp b/src/server/game/Entities/Creature/CreatureOutfit.cpp index 2134d4ea7329d..b0d399bab0d8f 100644 --- a/src/server/game/Entities/Creature/CreatureOutfit.cpp +++ b/src/server/game/Entities/Creature/CreatureOutfit.cpp @@ -2,6 +2,11 @@ #include "DB2Structure.h" // ChrRacesEntry #include "DB2Stores.h" // sChrRacesStore, sDB2Manager +constexpr uint32 CreatureOutfit::max_custom_displays; +constexpr uint32 CreatureOutfit::invisible_model; +constexpr uint32 CreatureOutfit::max_real_modelid; +constexpr EquipmentSlots CreatureOutfit::item_slots[]; + CreatureOutfit::CreatureOutfit(uint8 race, Gender gender) : race(race), gender(gender) { const ChrRacesEntry* rEntry = sChrRacesStore.LookupEntry(race); From d7b71f4d2fbda7d96250ac29460a5775b7f8f0f4 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sat, 24 Feb 2018 01:16:43 +0200 Subject: [PATCH 24/35] Fix allied races usage for outfits --- sql/custom/world/2018_02_24_00_world_dressnpcs.sql | 8 ++++++++ src/server/game/Globals/ObjectMgr.cpp | 7 +++++++ 2 files changed, 15 insertions(+) create mode 100644 sql/custom/world/2018_02_24_00_world_dressnpcs.sql diff --git a/sql/custom/world/2018_02_24_00_world_dressnpcs.sql b/sql/custom/world/2018_02_24_00_world_dressnpcs.sql new file mode 100644 index 0000000000000..70ab872fd333f --- /dev/null +++ b/sql/custom/world/2018_02_24_00_world_dressnpcs.sql @@ -0,0 +1,8 @@ +INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75078, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 55; +INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75079, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 56; +INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75080, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 55; +INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75081, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 59; +INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75082, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 15476; +INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75083, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 15475; +INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75084, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 16125; +INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75085, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 16126; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 419ae37976bc6..cee07e15474b1 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8248,6 +8248,13 @@ void ObjectMgr::LoadCreatureOutfits() _creatureOutfitStore.clear(); + for (auto* e : sChrRacesStore) + { + const char* error = "Dress NPCs requires an entry in creature_model_info for modelid %u (%s %s)"; + ASSERT(GetCreatureModelInfo(e->MaleDisplayID), error, e->MaleDisplayID, e->Name->Str[DEFAULT_LOCALE], "Male"); + ASSERT(GetCreatureModelInfo(e->FemaleDisplayID), error, e->FemaleDisplayID, e->Name->Str[DEFAULT_LOCALE], "Female"); + } + QueryResult result = WorldDatabase.Query("SELECT entry, npcsoundsid, race, class, gender, skin, face, hair, haircolor, facialhair, feature1, feature2, feature3, " "head, head_appearance, shoulders, shoulders_appearance, body, body_appearance, chest, chest_appearance, waist, waist_appearance, " "legs, legs_appearance, feet, feet_appearance, wrists, wrists_appearance, hands, hands_appearance, tabard, tabard_appearance, back, back_appearance, " From 34f398e94ce02d2d36c1eaadcc713e71a69e2744 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sun, 18 Mar 2018 01:23:13 +0200 Subject: [PATCH 25/35] Fix build --- src/server/game/Entities/Creature/CreatureOutfit.cpp | 4 ++-- src/server/game/Globals/ObjectMgr.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/server/game/Entities/Creature/CreatureOutfit.cpp b/src/server/game/Entities/Creature/CreatureOutfit.cpp index b0d399bab0d8f..37958371ef385 100644 --- a/src/server/game/Entities/Creature/CreatureOutfit.cpp +++ b/src/server/game/Entities/Creature/CreatureOutfit.cpp @@ -16,8 +16,8 @@ CreatureOutfit::CreatureOutfit(uint8 race, Gender gender) : race(race), gender(g } switch (gender) { - case GENDER_FEMALE: displayId = rEntry->FemaleDisplayID; break; - default: displayId = rEntry->MaleDisplayID; break; + case GENDER_FEMALE: displayId = rEntry->FemaleDisplayId; break; + default: displayId = rEntry->MaleDisplayId; break; } } diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index b4993d0a79f9a..8540db1f402ed 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8260,8 +8260,8 @@ void ObjectMgr::LoadCreatureOutfits() for (auto* e : sChrRacesStore) { const char* error = "Dress NPCs requires an entry in creature_model_info for modelid %u (%s %s)"; - ASSERT(GetCreatureModelInfo(e->MaleDisplayID), error, e->MaleDisplayID, e->Name->Str[DEFAULT_LOCALE], "Male"); - ASSERT(GetCreatureModelInfo(e->FemaleDisplayID), error, e->FemaleDisplayID, e->Name->Str[DEFAULT_LOCALE], "Female"); + ASSERT(GetCreatureModelInfo(e->MaleDisplayId), error, e->MaleDisplayId, e->Name->Str[DEFAULT_LOCALE], "Male"); + ASSERT(GetCreatureModelInfo(e->FemaleDisplayId), error, e->FemaleDisplayId, e->Name->Str[DEFAULT_LOCALE], "Female"); } QueryResult result = WorldDatabase.Query("SELECT entry, npcsoundsid, race, class, gender, skin, face, hair, haircolor, facialhair, feature1, feature2, feature3, " @@ -8318,8 +8318,8 @@ void ObjectMgr::LoadCreatureOutfits() co->gender = fields[i++].GetUInt8(); switch (co->gender) { - case GENDER_FEMALE: co->displayId = rEntry->FemaleDisplayID; break; - case GENDER_MALE: co->displayId = rEntry->MaleDisplayID; break; + case GENDER_FEMALE: co->displayId = rEntry->FemaleDisplayId; break; + case GENDER_MALE: co->displayId = rEntry->MaleDisplayId; break; default: TC_LOG_ERROR("server.loading", ">> Outfit entry %u in `creature_template_outfits` has invalid gender %u", entry, uint32(co->gender)); continue; From e5b0d93952ee185d67812599d0869568d27a87cf Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Fri, 19 Oct 2018 02:49:21 +0300 Subject: [PATCH 26/35] Update .travis.yml --- .travis.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 17d3a89b48b2e..ec513f6ba007a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ compiler: - clang git: - depth: 100 + depth: 1 addons: apt: @@ -32,12 +32,11 @@ services: - mysql before_install: - - git config user.email "rochet2@post.com" && git config user.name "Rochet2" + - git config user.email "travis@build.bot" && git config user.name "Travis CI" - git tag -a -m "Travis build" init install: - mysql -uroot -e 'create database test_mysql;' - - if [ "${TRAVIS_EVENT_TYPE}" == "cron" ]; then git pull https://github.com/TrinityCore/TrinityCore.git master; fi; - mkdir bin - cd bin - cmake ../ -DWITH_WARNINGS=1 -DWITH_COREDEBUG=0 -DUSE_COREPCH=1 -DUSE_SCRIPTPCH=1 -DTOOLS=1 -DSCRIPTS=dynamic -DSERVERS=1 -DNOJEM=1 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS="-Werror" -DCMAKE_CXX_FLAGS="-Werror" -DCMAKE_C_FLAGS_DEBUG="-DNDEBUG" -DCMAKE_CXX_FLAGS_DEBUG="-DNDEBUG" -DCMAKE_INSTALL_PREFIX=check_install @@ -55,15 +54,9 @@ script: - mysql -utrinity -ptrinity hotfixes < sql/base/dev/hotfixes_database.sql - cat sql/updates/world/master/*.sql | mysql -utrinity -ptrinity world - cat sql/updates/hotfixes/master/*.sql | mysql -utrinity -ptrinity hotfixes - - cat sql/custom/auth/*.sql | mysql -utrinity -ptrinity auth - - cat sql/custom/characters/*.sql | mysql -utrinity -ptrinity characters - - cat sql/custom/world/*.sql | mysql -utrinity -ptrinity world - mysql -uroot < sql/create/drop_mysql.sql - cd bin - make -j 4 -k && make install - cd check_install/bin - ./bnetserver --version - ./worldserver --version - -after_success: - - if [ "${TRAVIS_EVENT_TYPE}" == "cron" ]; then git push https://${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git HEAD:${TRAVIS_BRANCH}; fi; From db8198b458a455f4c25601de92c3a4a082554470 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 7 Jan 2019 22:28:54 +0200 Subject: [PATCH 27/35] Auto fill model info for new races --- src/server/game/Globals/ObjectMgr.cpp | 47 +++++++++++++++++---------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index f6e2e59a9bdca..42051a4855bb3 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8405,30 +8405,43 @@ void ObjectMgr::LoadCreatureOutfits() _creatureOutfitStore.clear(); - for (auto* e : sChrRacesStore) + std::map newRaceToOldRace = { + {/*RACE_NIGHTBORNE*/ 27, RACE_NIGHTELF}, + {/*RACE_HIGHMOUNTAIN_TAUREN*/ 28, RACE_TAUREN}, + {/*RACE_VOID_ELF*/ 29, RACE_BLOODELF}, + {/*RACE_LIGHTFORGED_DRAENEI*/ 30, RACE_DRAENEI}, + {/*RACE_ZANDALARI_TROLL*/ 31, RACE_TROLL}, + {/*RACE_KUL_TIRAN*/ 32, RACE_DRAENEI}, + {/*RACE_THIN_HUMAN*/ 33, RACE_NIGHTELF}, + {/*RACE_DARK_IRON_DWARF*/ 34, RACE_DWARF}, + {/*RACE_VULPERA*/ 35, RACE_GNOME}, + {/*RACE_MAGHAR_ORC*/ 36, RACE_ORC}, + }; + + for (auto const & r : newRaceToOldRace) { - if (!GetCreatureModelInfo(e->MaleDisplayId)) + auto* e1 = sChrRacesStore.AssertEntry(r.first); + auto* e2 = sChrRacesStore.AssertEntry(r.second); + if (!GetCreatureModelInfo(e1->MaleDisplayId)) { - TC_LOG_ERROR("server.loading", "Dress NPCs requires an entry in creature_model_info for modelid %u (%s Male), using hardcoded dummy info", e->MaleDisplayId, e->Name->Str[DEFAULT_LOCALE]); - auto& info = _creatureModelStore[e->MaleDisplayId]; - info.gender = GENDER_MALE; - info.displayId_other_gender = e->FemaleDisplayId; - info.is_trigger = false; - info.combat_reach = 1.0f; - info.bounding_radius = 0.5f; + auto* info = GetCreatureModelInfo(e2->MaleDisplayId); + ASSERT(info, "Dress NPCs: New race has no info for male and old race has no info either"); + _creatureModelStore[e1->MaleDisplayId] = *info; } - if (!GetCreatureModelInfo(e->FemaleDisplayId)) + if (!GetCreatureModelInfo(e1->FemaleDisplayId)) { - TC_LOG_ERROR("server.loading", "Dress NPCs requires an entry in creature_model_info for modelid %u (%s Female), using hardcoded dummy info", e->FemaleDisplayId, e->Name->Str[DEFAULT_LOCALE]); - auto& info = _creatureModelStore[e->FemaleDisplayId]; - info.gender = GENDER_FEMALE; - info.displayId_other_gender = e->MaleDisplayId; - info.is_trigger = false; - info.combat_reach = 1.0f; - info.bounding_radius = 0.5f; + auto* info = GetCreatureModelInfo(e2->FemaleDisplayId); + ASSERT(info, "Dress NPCs: New race has no info for female and old race has no info either"); + _creatureModelStore[e1->FemaleDisplayId] = *info; } } + for (auto* e : sChrRacesStore) + { + ASSERT(GetCreatureModelInfo(e->MaleDisplayId), "Dress NPCs requires an entry in creature_model_info for modelid %u (%s Male)", e->MaleDisplayId, e->Name->Str[DEFAULT_LOCALE]); + ASSERT(GetCreatureModelInfo(e->FemaleDisplayId), "Dress NPCs requires an entry in creature_model_info for modelid %u (%s Female)", e->FemaleDisplayId, e->Name->Str[DEFAULT_LOCALE]); + } + QueryResult result = WorldDatabase.Query("SELECT entry, npcsoundsid, race, class, gender, skin, face, hair, haircolor, facialhair, feature1, feature2, feature3, " "head, head_appearance, shoulders, shoulders_appearance, body, body_appearance, chest, chest_appearance, waist, waist_appearance, " "legs, legs_appearance, feet, feet_appearance, wrists, wrists_appearance, hands, hands_appearance, tabard, tabard_appearance, back, back_appearance, " From 16f1eba31bc240a7e48fd5e552bc3207c88f4d6a Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Mon, 7 Jan 2019 22:31:58 +0200 Subject: [PATCH 28/35] Create new SQLs and update readme --- .../world/2016_07_24_00_world_dressnpcs.sql | 31 ------------- .../world/2016_07_24_01_world_dressnpcs.sql | 2 - .../world/2016_11_06_00_world_dressnpcs.sql | 18 -------- .../world/2018_01_03_00_world_dressnpcs.sql | 2 - .../world/2018_01_03_01_world_dressnpcs.sql | 12 ----- .../world/2018_01_03_02_world_dressnpcs.sql | 11 ----- .../world/2018_01_03_03_world_dressnpcs.sql | 2 - .../world/2018_01_03_04_world_dressnpcs.sql | 2 - .../world/2018_01_04_00_world_dressnpcs.sql | 2 - .../world/2018_02_17_00_world_dressnpcs.sql | 20 --------- .../world/2018_02_24_00_world_dressnpcs.sql | 8 ---- .../scripts/Custom/DressNPCs/Example.sql | 4 +- src/server/scripts/Custom/DressNPCs/README.md | 10 +++-- .../Custom/DressNPCs/new_install/hotfixes.sql | 1 - .../Custom/DressNPCs/new_install/world.sql | 44 +++++++++++++++++++ 15 files changed, 52 insertions(+), 117 deletions(-) delete mode 100644 sql/custom/world/2016_07_24_00_world_dressnpcs.sql delete mode 100644 sql/custom/world/2016_07_24_01_world_dressnpcs.sql delete mode 100644 sql/custom/world/2016_11_06_00_world_dressnpcs.sql delete mode 100644 sql/custom/world/2018_01_03_00_world_dressnpcs.sql delete mode 100644 sql/custom/world/2018_01_03_01_world_dressnpcs.sql delete mode 100644 sql/custom/world/2018_01_03_02_world_dressnpcs.sql delete mode 100644 sql/custom/world/2018_01_03_03_world_dressnpcs.sql delete mode 100644 sql/custom/world/2018_01_03_04_world_dressnpcs.sql delete mode 100644 sql/custom/world/2018_01_04_00_world_dressnpcs.sql delete mode 100644 sql/custom/world/2018_02_17_00_world_dressnpcs.sql delete mode 100644 sql/custom/world/2018_02_24_00_world_dressnpcs.sql rename sql/custom/hotfixes/2018_01_04_00_hotfixes_dressnpcs.sql => src/server/scripts/Custom/DressNPCs/new_install/hotfixes.sql (90%) create mode 100644 src/server/scripts/Custom/DressNPCs/new_install/world.sql diff --git a/sql/custom/world/2016_07_24_00_world_dressnpcs.sql b/sql/custom/world/2016_07_24_00_world_dressnpcs.sql deleted file mode 100644 index 387cf774ae92f..0000000000000 --- a/sql/custom/world/2016_07_24_00_world_dressnpcs.sql +++ /dev/null @@ -1,31 +0,0 @@ -CREATE TABLE IF NOT EXISTS `creature_template_outfits` ( - `entry` INT(10) UNSIGNED NOT NULL, - `race` TINYINT(3) UNSIGNED NOT NULL DEFAULT '1', - `gender` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' COMMENT '0 for male, 1 for female', - `skin` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', - `face` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', - `hair` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', - `haircolor` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', - `facialhair` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', - `head` INT(10) NOT NULL DEFAULT '0', - `shoulders` INT(10) NOT NULL DEFAULT '0', - `body` INT(10) NOT NULL DEFAULT '0', - `chest` INT(10) NOT NULL DEFAULT '0', - `waist` INT(10) NOT NULL DEFAULT '0', - `legs` INT(10) NOT NULL DEFAULT '0', - `feet` INT(10) NOT NULL DEFAULT '0', - `wrists` INT(10) NOT NULL DEFAULT '0', - `hands` INT(10) NOT NULL DEFAULT '0', - `back` INT(10) NOT NULL DEFAULT '0', - `tabard` INT(10) NOT NULL DEFAULT '0', - PRIMARY KEY (`entry`) -) -COMMENT='Use positive values for item entries and negative to use item displayid for head, shoulders etc.' -COLLATE='utf8_general_ci' -ENGINE=InnoDB; - -ALTER TABLE `creature_template` - CHANGE COLUMN `modelid1` `modelid1` INT NOT NULL DEFAULT '0' AFTER `KillCredit2`, - CHANGE COLUMN `modelid2` `modelid2` INT NOT NULL DEFAULT '0' AFTER `modelid1`, - CHANGE COLUMN `modelid3` `modelid3` INT NOT NULL DEFAULT '0' AFTER `modelid2`, - CHANGE COLUMN `modelid4` `modelid4` INT NOT NULL DEFAULT '0' AFTER `modelid3`; diff --git a/sql/custom/world/2016_07_24_01_world_dressnpcs.sql b/sql/custom/world/2016_07_24_01_world_dressnpcs.sql deleted file mode 100644 index cd6e5b9623383..0000000000000 --- a/sql/custom/world/2016_07_24_01_world_dressnpcs.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE `creature_template_outfits` - ADD COLUMN `class` TINYINT(3) UNSIGNED NOT NULL DEFAULT '1' AFTER `race`; diff --git a/sql/custom/world/2016_11_06_00_world_dressnpcs.sql b/sql/custom/world/2016_11_06_00_world_dressnpcs.sql deleted file mode 100644 index 255748a439332..0000000000000 --- a/sql/custom/world/2016_11_06_00_world_dressnpcs.sql +++ /dev/null @@ -1,18 +0,0 @@ -ALTER TABLE `creature_template_outfits` - ADD COLUMN `feature1` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `facialhair`, - ADD COLUMN `feature2` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `feature1`, - ADD COLUMN `feature3` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `feature2`; - -ALTER TABLE `creature_template_outfits` - COMMENT='Use positive values for item entries and negative to use item displayid for head, shoulders etc.\r\nTo use special appearances either find the displayid for it or use ((AppearanceModID << 32) | item_entry). The appearance IDs are usually from 0 to 10.', - CHANGE COLUMN `head` `head` BIGINT NOT NULL DEFAULT '0' AFTER `feature3`, - CHANGE COLUMN `shoulders` `shoulders` BIGINT NOT NULL DEFAULT '0' AFTER `head`, - CHANGE COLUMN `body` `body` BIGINT NOT NULL DEFAULT '0' AFTER `shoulders`, - CHANGE COLUMN `chest` `chest` BIGINT NOT NULL DEFAULT '0' AFTER `body`, - CHANGE COLUMN `waist` `waist` BIGINT NOT NULL DEFAULT '0' AFTER `chest`, - CHANGE COLUMN `legs` `legs` BIGINT NOT NULL DEFAULT '0' AFTER `waist`, - CHANGE COLUMN `feet` `feet` BIGINT NOT NULL DEFAULT '0' AFTER `legs`, - CHANGE COLUMN `wrists` `wrists` BIGINT NOT NULL DEFAULT '0' AFTER `feet`, - CHANGE COLUMN `hands` `hands` BIGINT NOT NULL DEFAULT '0' AFTER `wrists`, - CHANGE COLUMN `back` `back` BIGINT NOT NULL DEFAULT '0' AFTER `hands`, - CHANGE COLUMN `tabard` `tabard` BIGINT NOT NULL DEFAULT '0' AFTER `back`; diff --git a/sql/custom/world/2018_01_03_00_world_dressnpcs.sql b/sql/custom/world/2018_01_03_00_world_dressnpcs.sql deleted file mode 100644 index ab2a7721bc020..0000000000000 --- a/sql/custom/world/2018_01_03_00_world_dressnpcs.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE `creature_template_outfits` - ADD COLUMN `guildid` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0' AFTER `tabard`; diff --git a/sql/custom/world/2018_01_03_01_world_dressnpcs.sql b/sql/custom/world/2018_01_03_01_world_dressnpcs.sql deleted file mode 100644 index 93a8a6bb54feb..0000000000000 --- a/sql/custom/world/2018_01_03_01_world_dressnpcs.sql +++ /dev/null @@ -1,12 +0,0 @@ -ALTER TABLE `creature_template_outfits` - ADD COLUMN `head_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `head`, - ADD COLUMN `shoulders_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `shoulders`, - ADD COLUMN `body_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `body`, - ADD COLUMN `chest_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `chest`, - ADD COLUMN `waist_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `waist`, - ADD COLUMN `legs_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `legs`, - ADD COLUMN `feet_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `feet`, - ADD COLUMN `wrists_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `wrists`, - ADD COLUMN `hands_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `hands`, - ADD COLUMN `back_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `back`, - ADD COLUMN `tabard_appearance` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `tabard`; diff --git a/sql/custom/world/2018_01_03_02_world_dressnpcs.sql b/sql/custom/world/2018_01_03_02_world_dressnpcs.sql deleted file mode 100644 index b8e0161194e12..0000000000000 --- a/sql/custom/world/2018_01_03_02_world_dressnpcs.sql +++ /dev/null @@ -1,11 +0,0 @@ -UPDATE `creature_template_outfits` SET `head_appearance` = (`head` >> 32), `head` = (`head` & 0xFFFFFFFF) WHERE `head` > 0; -UPDATE `creature_template_outfits` SET `shoulders_appearance` = (`shoulders` >> 32), `shoulders` = (`shoulders` & 0xFFFFFFFF) WHERE `shoulders` > 0; -UPDATE `creature_template_outfits` SET `body_appearance` = (`body` >> 32), `body` = (`body` & 0xFFFFFFFF) WHERE `body` > 0; -UPDATE `creature_template_outfits` SET `chest_appearance` = (`chest` >> 32), `chest` = (`chest` & 0xFFFFFFFF) WHERE `chest` > 0; -UPDATE `creature_template_outfits` SET `waist_appearance` = (`waist` >> 32), `waist` = (`waist` & 0xFFFFFFFF) WHERE `waist` > 0; -UPDATE `creature_template_outfits` SET `legs_appearance` = (`legs` >> 32), `legs` = (`legs` & 0xFFFFFFFF) WHERE `legs` > 0; -UPDATE `creature_template_outfits` SET `feet_appearance` = (`feet` >> 32), `feet` = (`feet` & 0xFFFFFFFF) WHERE `feet` > 0; -UPDATE `creature_template_outfits` SET `wrists_appearance` = (`wrists` >> 32), `wrists` = (`wrists` & 0xFFFFFFFF) WHERE `wrists` > 0; -UPDATE `creature_template_outfits` SET `hands_appearance` = (`hands` >> 32), `hands` = (`hands` & 0xFFFFFFFF) WHERE `hands` > 0; -UPDATE `creature_template_outfits` SET `back_appearance` = (`back` >> 32), `back` = (`back` & 0xFFFFFFFF) WHERE `back` > 0; -UPDATE `creature_template_outfits` SET `tabard_appearance` = (`tabard` >> 32), `tabard` = (`tabard` & 0xFFFFFFFF) WHERE `tabard` > 0; diff --git a/sql/custom/world/2018_01_03_03_world_dressnpcs.sql b/sql/custom/world/2018_01_03_03_world_dressnpcs.sql deleted file mode 100644 index 7512979713199..0000000000000 --- a/sql/custom/world/2018_01_03_03_world_dressnpcs.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE `creature_template_outfits` - COMMENT='Use positive values for item entries and negative to use item displayid for head, shoulders etc.'; diff --git a/sql/custom/world/2018_01_03_04_world_dressnpcs.sql b/sql/custom/world/2018_01_03_04_world_dressnpcs.sql deleted file mode 100644 index 141c920ebd838..0000000000000 --- a/sql/custom/world/2018_01_03_04_world_dressnpcs.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE `creature_template_outfits` - ADD COLUMN `description` TEXT NULL DEFAULT NULL AFTER `guildid`; diff --git a/sql/custom/world/2018_01_04_00_world_dressnpcs.sql b/sql/custom/world/2018_01_04_00_world_dressnpcs.sql deleted file mode 100644 index ef9393b75ca11..0000000000000 --- a/sql/custom/world/2018_01_04_00_world_dressnpcs.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE `creature_template_outfits` - ADD COLUMN `npcsoundsid` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'entry from NPCSounds.dbc/db2' AFTER `entry`; diff --git a/sql/custom/world/2018_02_17_00_world_dressnpcs.sql b/sql/custom/world/2018_02_17_00_world_dressnpcs.sql deleted file mode 100644 index fbacdf65380de..0000000000000 --- a/sql/custom/world/2018_02_17_00_world_dressnpcs.sql +++ /dev/null @@ -1,20 +0,0 @@ -ALTER TABLE `creature_template` CHANGE COLUMN `modelid1` `modelid1` BIGINT; -ALTER TABLE `creature_template` CHANGE COLUMN `modelid2` `modelid2` BIGINT; -ALTER TABLE `creature_template` CHANGE COLUMN `modelid3` `modelid3` BIGINT; -ALTER TABLE `creature_template` CHANGE COLUMN `modelid4` `modelid4` BIGINT; -UPDATE creature_template SET modelid1 = (-modelid1) + 3000000000 WHERE modelid1 < 0; -UPDATE creature_template SET modelid2 = (-modelid2) + 3000000000 WHERE modelid2 < 0; -UPDATE creature_template SET modelid3 = (-modelid3) + 3000000000 WHERE modelid3 < 0; -UPDATE creature_template SET modelid4 = (-modelid4) + 3000000000 WHERE modelid4 < 0; -ALTER TABLE `creature_template` CHANGE COLUMN `modelid1` `modelid1` INT(10) UNSIGNED NOT NULL DEFAULT '0'; -ALTER TABLE `creature_template` CHANGE COLUMN `modelid2` `modelid2` INT(10) UNSIGNED NOT NULL DEFAULT '0'; -ALTER TABLE `creature_template` CHANGE COLUMN `modelid3` `modelid3` INT(10) UNSIGNED NOT NULL DEFAULT '0'; -ALTER TABLE `creature_template` CHANGE COLUMN `modelid4` `modelid4` INT(10) UNSIGNED NOT NULL DEFAULT '0'; - -ALTER TABLE `creature_template_outfits` DROP PRIMARY KEY; -UPDATE creature_template_outfits SET entry = entry + 3000000000 WHERE entry <= 0x7FFFFFFF; -ALTER TABLE `creature_template_outfits` ADD PRIMARY KEY (`entry`); - -ALTER TABLE `creature` CHANGE COLUMN `modelid` `modelid` INT(10) UNSIGNED NOT NULL DEFAULT '0'; -ALTER TABLE `game_event_model_equip` CHANGE COLUMN `modelid` `modelid` INT(10) UNSIGNED NOT NULL DEFAULT '0'; -ALTER TABLE `creature_model_info` CHANGE COLUMN `DisplayID` `DisplayID` INT(10) UNSIGNED NOT NULL DEFAULT '0'; diff --git a/sql/custom/world/2018_02_24_00_world_dressnpcs.sql b/sql/custom/world/2018_02_24_00_world_dressnpcs.sql deleted file mode 100644 index 70ab872fd333f..0000000000000 --- a/sql/custom/world/2018_02_24_00_world_dressnpcs.sql +++ /dev/null @@ -1,8 +0,0 @@ -INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75078, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 55; -INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75079, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 56; -INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75080, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 55; -INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75081, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 59; -INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75082, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 15476; -INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75083, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 15475; -INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75084, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 16125; -INSERT IGNORE INTO `creature_model_info` (`DisplayID`, `BoundingRadius`, `CombatReach`, `DisplayID_Other_Gender`, `VerifiedBuild`) SELECT 75085, `BoundingRadius`, `CombatReach`, 0, 0 FROM `creature_model_info` WHERE `DisplayID` = 16126; diff --git a/src/server/scripts/Custom/DressNPCs/Example.sql b/src/server/scripts/Custom/DressNPCs/Example.sql index ab0c26a4d468a..629f14a47b6d0 100644 --- a/src/server/scripts/Custom/DressNPCs/Example.sql +++ b/src/server/scripts/Custom/DressNPCs/Example.sql @@ -1,5 +1,5 @@ SET @NPCENTRY := 6; -INSERT INTO `creature_template_outfits` (`entry`, `race`, `gender`, `skin`, `face`, `hair`, `haircolor`, `facialhair`, `head`, `shoulders`, `body`, `chest`, `waist`, `legs`, `feet`, `wrists`, `hands`, `back`, `tabard`) +REPLACE INTO `creature_template_outfits` (`entry`, `race`, `gender`, `skin`, `face`, `hair`, `haircolor`, `facialhair`, `head`, `shoulders`, `body`, `chest`, `waist`, `legs`, `feet`, `wrists`, `hands`, `back`, `tabard`) VALUES (3000000123, 11, 1, 14, 4, 10, 3, 5, -31286, -43617, 0, -26267, -26270, -26272, 0, 0, -43698, 0, 0); -UPDATE `creature_template` SET `modelid2` = 3000000123 WHERE `entry` = @NPCENTRY; +REPLACE INTO `creature_template_model` (`CreatureID`, `Idx`, `CreatureDisplayID`, `DisplayScale`, `Probability`, `VerifiedBuild`) VALUES (6, 1, 3000000123, 1, 1, 0); diff --git a/src/server/scripts/Custom/DressNPCs/README.md b/src/server/scripts/Custom/DressNPCs/README.md index bbb717c671dbf..3da7a9cdc5c83 100644 --- a/src/server/scripts/Custom/DressNPCs/README.md +++ b/src/server/scripts/Custom/DressNPCs/README.md @@ -33,12 +33,14 @@ Using diff: - do `git apply dressnpcs_7.x.diff` - use cmake and compile +Before compiling: +- You can choose in CMake to disable sound workaround if you have a client patch for the NPC sounds. The option is `DISABLE_DRESSNPCS_CORESOUNDS`. + After compiling: -- TrinityCore auto updater should run needed SQLs automatically. -- If you do not use the auto updater then run files named `*_dressnpcs.sql` from `\sql\custom` to your databases. +- Make sure you have already created and updated your databases to match your TC version. You can usually do this by running the worldserver once. +- Run SQL files from `src/server/scripts/Custom/DressNPCs/new_install` to their respective databases. ## Usage -- Before compiling the core you can choose in CMake to disable sound workaround if you have a client patch for the NPC sounds. The option is `DISABLE_DRESSNPCS_CORESOUNDS`. - Remember to check server startup errors, they tell when you do things wrong. - For some skins you must use a specific class, like for death knight skins and eyes. - Unplayable races like naga are possible to be used as race, experiment :). @@ -50,7 +52,7 @@ To make an outfit create a row to `creature_template_outfits` table with your de - The items can use positive value as item entry and negative for displayid. - Appearances are usually between 0 and 10 and they define the look of the item. Different appearances are usually used by mythic and heroic versions of an item. The appearance column takes effect only when using an item entry (a positive value) for the equipped item definition. - `guildid` refers to an actual guild from characters table and it is used to define the tabard looks of the creature if one is equipped. So you must make a guild and set a tabard for it and use it's ID in the column for the outfit. -- `npcsoundsid` refers to `NPCSounds.dbc/db2`. In this column you can define what gossip replies to use for the NPC with the core side workaround for missing sounds for gossip. To create completely new sound combinations you can use hotfixes database or edit the DBC file. +- `npcsoundsid` refers to `NPCSounds.dbc/db2`. In this column you can define what gossip replies to use for the NPC with the core side workaround for missing sounds for gossip. To create completely new sound combinations you can use hotfixes database `npc_sounds` table or edit the DBC file. You can use the outfit entry as modelid in creature template, smart scripts and elsewhere in the DB and core for setting a modelid/displayid, like `creature->SetDisplayId(outfitid)`. diff --git a/sql/custom/hotfixes/2018_01_04_00_hotfixes_dressnpcs.sql b/src/server/scripts/Custom/DressNPCs/new_install/hotfixes.sql similarity index 90% rename from sql/custom/hotfixes/2018_01_04_00_hotfixes_dressnpcs.sql rename to src/server/scripts/Custom/DressNPCs/new_install/hotfixes.sql index d740677b496bf..4970ffc762972 100644 --- a/sql/custom/hotfixes/2018_01_04_00_hotfixes_dressnpcs.sql +++ b/src/server/scripts/Custom/DressNPCs/new_install/hotfixes.sql @@ -1,4 +1,3 @@ -DROP TABLE IF EXISTS `npc_sounds`; CREATE TABLE `npc_sounds` ( `ID` INT(10) UNSIGNED NOT NULL, `hello` INT(10) UNSIGNED NOT NULL DEFAULT '0', diff --git a/src/server/scripts/Custom/DressNPCs/new_install/world.sql b/src/server/scripts/Custom/DressNPCs/new_install/world.sql new file mode 100644 index 0000000000000..739ae222844d5 --- /dev/null +++ b/src/server/scripts/Custom/DressNPCs/new_install/world.sql @@ -0,0 +1,44 @@ +CREATE TABLE `creature_template_outfits` ( + `entry` int(10) unsigned NOT NULL, + `npcsoundsid` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'entry from NPCSounds.dbc/db2', + `race` tinyint(3) unsigned NOT NULL DEFAULT '1', + `class` tinyint(3) unsigned NOT NULL DEFAULT '1', + `gender` tinyint(3) unsigned NOT NULL DEFAULT '0' COMMENT '0 for male, 1 for female', + `skin` tinyint(3) unsigned NOT NULL DEFAULT '0', + `face` tinyint(3) unsigned NOT NULL DEFAULT '0', + `hair` tinyint(3) unsigned NOT NULL DEFAULT '0', + `haircolor` tinyint(3) unsigned NOT NULL DEFAULT '0', + `facialhair` tinyint(3) unsigned NOT NULL DEFAULT '0', + `feature1` tinyint(3) unsigned NOT NULL DEFAULT '0', + `feature2` tinyint(3) unsigned NOT NULL DEFAULT '0', + `feature3` tinyint(3) unsigned NOT NULL DEFAULT '0', + `head` bigint(20) NOT NULL DEFAULT '0', + `head_appearance` int(10) unsigned NOT NULL DEFAULT '0', + `shoulders` bigint(20) NOT NULL DEFAULT '0', + `shoulders_appearance` int(10) unsigned NOT NULL DEFAULT '0', + `body` bigint(20) NOT NULL DEFAULT '0', + `body_appearance` int(10) unsigned NOT NULL DEFAULT '0', + `chest` bigint(20) NOT NULL DEFAULT '0', + `chest_appearance` int(10) unsigned NOT NULL DEFAULT '0', + `waist` bigint(20) NOT NULL DEFAULT '0', + `waist_appearance` int(10) unsigned NOT NULL DEFAULT '0', + `legs` bigint(20) NOT NULL DEFAULT '0', + `legs_appearance` int(10) unsigned NOT NULL DEFAULT '0', + `feet` bigint(20) NOT NULL DEFAULT '0', + `feet_appearance` int(10) unsigned NOT NULL DEFAULT '0', + `wrists` bigint(20) NOT NULL DEFAULT '0', + `wrists_appearance` int(10) unsigned NOT NULL DEFAULT '0', + `hands` bigint(20) NOT NULL DEFAULT '0', + `hands_appearance` int(10) unsigned NOT NULL DEFAULT '0', + `back` bigint(20) NOT NULL DEFAULT '0', + `back_appearance` int(10) unsigned NOT NULL DEFAULT '0', + `tabard` bigint(20) NOT NULL DEFAULT '0', + `tabard_appearance` int(10) unsigned NOT NULL DEFAULT '0', + `guildid` bigint(20) unsigned NOT NULL DEFAULT '0', + `description` text, + PRIMARY KEY (`entry`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Use positive values for item entries and negative to use item displayid for head, shoulders etc.'; + +ALTER TABLE `creature` CHANGE COLUMN `modelid` `modelid` INT(10) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `game_event_model_equip` CHANGE COLUMN `modelid` `modelid` INT(10) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `creature_model_info` CHANGE COLUMN `DisplayID` `DisplayID` INT(10) UNSIGNED NOT NULL DEFAULT '0'; From 1779ef0b132fc5b6ccd998c010fed43b7b89be95 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Tue, 8 Jan 2019 20:37:00 +0200 Subject: [PATCH 29/35] Update README.md --- src/server/scripts/Custom/DressNPCs/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/server/scripts/Custom/DressNPCs/README.md b/src/server/scripts/Custom/DressNPCs/README.md index 3da7a9cdc5c83..240fa84a4024b 100644 --- a/src/server/scripts/Custom/DressNPCs/README.md +++ b/src/server/scripts/Custom/DressNPCs/README.md @@ -1,4 +1,4 @@ -# DressNPCs [![Build Status](https://travis-ci.org/Rochet2/TrinityCore.svg?branch=dressnpcs_7.x)](https://travis-ci.org/Rochet2/TrinityCore) +# DressNPCs [![Build Status](https://travis-ci.org/Rochet2/TrinityCore.svg?branch=dressnpcs_master)](https://travis-ci.org/Rochet2/TrinityCore) ## About This patch allows you to dress up armor on NPCs as well as choose their facial features. @@ -15,22 +15,22 @@ Known bugs: ## Installation Available as: -- Direct merge: https://github.com/Rochet2/TrinityCore/tree/dressnpcs_7.x -- Diff: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:master...dressnpcs_7.x.diff -- Diff in github view: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:master...dressnpcs_7.x +- Direct merge: https://github.com/Rochet2/TrinityCore/tree/dressnpcs_master +- Diff: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:master...dressnpcs_master.diff +- Diff in github view: https://github.com/Rochet2/TrinityCore/compare/TrinityCore:master...dressnpcs_master Using direct merge: - open git bash to source location - do `git remote add rochet2 https://github.com/Rochet2/TrinityCore.git` -- do `git pull rochet2 dressnpcs_7.x` +- do `git pull rochet2 dressnpcs_master` - use cmake and compile Using diff: - DO NOT COPY THE DIFF DIRECTLY! It causes apply to fail. - download the diff by __right clicking__ the link and select __Save link as__ -- place the downloaded `dressnpcs_7.x.diff` to the source root folder +- place the downloaded `dressnpcs_master.diff` to the source root folder - open git bash to source location -- do `git apply dressnpcs_7.x.diff` +- do `git apply dressnpcs_master.diff` - use cmake and compile Before compiling: From 3646a44ce649988c481a5c534c97c9238bdee022 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Wed, 17 Jul 2019 23:52:59 +0300 Subject: [PATCH 30/35] Fix travis warning of unused variable --- src/server/game/Entities/Creature/Creature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 86cc7c049bff8..05a10e2b2fee9 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -541,7 +541,7 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, // copy paste from ClearChangesMask template -static uint32 GetUpdateFieldHolderIndex(UF::UpdateField(Derived::* field)) +static uint32 GetUpdateFieldHolderIndex(UF::UpdateField(Derived::* /*field*/)) { return Bit; } From c2fb534758145aa7bca248883f12e6552efdd6ac Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sun, 6 Oct 2019 16:20:12 +0300 Subject: [PATCH 31/35] Fix 8.2.0 dressnpcs showing white model on spawn always --- src/server/game/Entities/Creature/Creature.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 25398cd95c724..c8833f4e264ca 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -564,6 +564,9 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, SetUnitFlags(UnitFlags(unitFlags)); SetUnitFlags2(UnitFlags2(unitFlags2)); + bool needsflag = m_outfit && Unit::GetDisplayId() == m_outfit->GetDisplayId(); + if (needsflag) + SetMirrorImageFlag(true); SetUnitFlags3(UnitFlags3(unitFlags3)); SetDynamicFlags(dynamicFlags); From d287942f794a149e67f4ebe5ee3fc8a66d4d4ef5 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sun, 25 Aug 2019 00:39:08 +0300 Subject: [PATCH 32/35] fix https://github.com/Rochet2/TrinityCore/issues/109 --- .../game/Entities/Creature/Creature.cpp | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index c8833f4e264ca..e4864d2b4ed03 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -3069,18 +3069,22 @@ void Creature::SetDisplayId(uint32 modelId, float displayScale /*= 1.f*/) } else { - if (!m_outfit || modelId != m_outfit->GetDisplayId()) + if (m_outfit) { - // no outfit or outfit's real modelid doesnt match modelid being set - // remove outfit and continue setting the new model - m_outfit.reset(); - SetMirrorImageFlag(false); - } - else - { - // outfit's real modelid being set - // add flags and continue setting the model - SetMirrorImageFlag(true); + // if has outfit + if (modelId != m_outfit->GetDisplayId()) + { + // and outfit's real modelid doesnt match modelid being set + // remove outfit and continue setting the new model + m_outfit.reset(); + SetMirrorImageFlag(false); + } + else + { + // outfit's real modelid being set + // add flags and continue setting the model + SetMirrorImageFlag(true); + } } } From 12bdce1a41f36d3abb8425da21c0605196836c44 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sat, 11 Apr 2020 17:18:18 +0300 Subject: [PATCH 33/35] Fix 8.3 startup for dressnpcs --- src/server/game/Globals/ObjectMgr.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index dbb19d0ed89a5..c08199300cfdf 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -8435,6 +8435,7 @@ void ObjectMgr::LoadCreatureOutfits() {/*RACE_DARK_IRON_DWARF*/ 34, RACE_DWARF}, {/*RACE_VULPERA*/ 35, RACE_GNOME}, {/*RACE_MAGHAR_ORC*/ 36, RACE_ORC}, + {/*RACE_MECHAGNOME*/ 37, RACE_GNOME}, }; for (auto const & r : newRaceToOldRace) From 9a50e29bdd002499448d3decceef0fbe388d4e40 Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Sun, 31 May 2020 12:49:26 +0300 Subject: [PATCH 34/35] Fix build --- src/server/game/Handlers/AuctionHouseHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index 338fa2fe99f0d..a2b5f9bcf631f 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -39,8 +39,8 @@ void WorldSession::HandleAuctionBrowseQuery(WorldPackets::AuctionHouse::AuctionBrowseQuery& browseQuery) { #ifndef DISABLE_DRESSNPCS_CORESOUNDS - if (packet.Guid.IsAnyTypeCreature()) - if (Creature* creature = _player->GetMap()->GetCreature(packet.Guid)) + if (browseQuery.Auctioneer.IsAnyTypeCreature()) + if (Creature* creature = _player->GetMap()->GetCreature(browseQuery.Auctioneer)) creature->SendMirrorSound(_player, 0); #endif AuctionThrottleResult throttle = sAuctionMgr->CheckThrottle(_player, browseQuery.TaintedBy.is_initialized()); From b4c2633f1c13aa879fe83042f4eccb3c0b3c289c Mon Sep 17 00:00:00 2001 From: Rochet2 Date: Wed, 27 Jul 2022 16:07:28 +0300 Subject: [PATCH 35/35] Fix non PCH build --- src/server/scripts/Commands/cs_reload.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 4655bb9c3e0d1..6735a09e80349 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -42,6 +42,7 @@ EndScriptData */ #include "LFGMgr.h" #include "Log.h" #include "LootMgr.h" +#include "Map.h" #include "MapManager.h" #include "ObjectMgr.h" #include "SkillDiscovery.h"