diff --git a/data/sql/updates/pending_db_world/rev_1548606393549139336.sql b/data/sql/updates/pending_db_world/rev_1548606393549139336.sql new file mode 100644 index 000000000..4e3068e9b --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1548606393549139336.sql @@ -0,0 +1,20 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1548606393549139336'); + + +DROP TABLE IF EXISTS `gameobject_template_addon`; +CREATE TABLE `gameobject_template_addon`( + `entry` mediumint(8) UNSIGNED NOT NULL DEFAULT '0', + `faction` smallint(5) unsigned NOT NULL DEFAULT '0', + `flags` int(10) unsigned NOT NULL DEFAULT '0', + `mingold` mediumint(8) UNSIGNED NOT NULL DEFAULT '0', + `maxgold` mediumint(8) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`entry`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +INSERT INTO `gameobject_template_addon` (`entry`, `faction`, `flags`) +SELECT `entry`, `faction`, `flags` FROM `gameobject_template`; + +ALTER TABLE `gameobject_template` +DROP COLUMN `faction`, +DROP COLUMN `flags`, +CHANGE COLUMN `VerifiedBuild` `VerifiedBuild` smallint(5) DEFAULT '0'; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 61e81f0c1..358646267 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -287,8 +287,11 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, uint32 phaseMa SetObjectScale(goinfo->size); - SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction); - SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags); + if (GameObjectTemplateAddon const* addon = GetTemplateAddon()) + { + SetUInt32Value(GAMEOBJECT_FACTION, addon->faction); + SetUInt32Value(GAMEOBJECT_FLAGS, addon->flags); + } SetEntry(goinfo->entry); @@ -688,8 +691,9 @@ void GameObject::Update(uint32 diff) //any return here in case battleground traps // Xinef: Do not return here for summoned gos that should be deleted few lines below // Xinef: Battleground objects are treated as spawned by default - if ((GetGOInfo()->flags & GO_FLAG_NODESPAWN) && isSpawnedByDefault()) - return; + if (GameObjectTemplateAddon const* addon = GetTemplateAddon()) + if ((addon->flags & GO_FLAG_NODESPAWN) && isSpawnedByDefault()) + return; } loot.clear(); @@ -710,7 +714,8 @@ void GameObject::Update(uint32 diff) { SendObjectDeSpawnAnim(GetGUID()); //reset flags - SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags); + if (GameObjectTemplateAddon const* addon = GetTemplateAddon()) + SetUInt32Value(GAMEOBJECT_FLAGS, addon->flags); } if (!m_respawnDelayTime) @@ -736,6 +741,11 @@ void GameObject::Update(uint32 diff) sScriptMgr->OnGameObjectUpdate(this, diff); } +GameObjectTemplateAddon const* GameObject::GetTemplateAddon() const +{ + return sObjectMgr->GetGameObjectTemplateAddon(GetGOInfo()->entry); +} + void GameObject::Refresh() { // not refresh despawned not casted GO (despawned casted GO destroyed in all cases anyway) @@ -760,7 +770,9 @@ void GameObject::Delete() SendObjectDeSpawnAnim(GetGUID()); SetGoState(GO_STATE_READY); - SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags); + + if (GameObjectTemplateAddon const* addon = GetTemplateAddon()) + SetUInt32Value(GAMEOBJECT_FLAGS, addon->flags); // Xinef: if ritual gameobject is removed, clear anim spells if (GetGOInfo()->type == GAMEOBJECT_TYPE_SUMMONING_RITUAL) diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 7737ce20e..280913c9a 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -532,8 +532,19 @@ struct GameObjectTemplate } }; +// From `gameobject_template_addon` +struct GameObjectTemplateAddon +{ + uint32 entry; + uint32 faction; + uint32 flags; + uint32 mingold; + uint32 maxgold; +}; + // Benchmarked: Faster than std::map (insert/find) typedef UNORDERED_MAP GameObjectTemplateContainer; +typedef UNORDERED_MAP GameObjectTemplateAddonContainer; class OPvPCapturePoint; struct TransportAnimation; @@ -646,6 +657,7 @@ class GameObject : public WorldObject, public GridObject, public Mov virtual bool Create(uint32 guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0); void Update(uint32 p_time); GameObjectTemplate const* GetGOInfo() const { return m_goInfo; } + GameObjectTemplateAddon const* GetTemplateAddon() const; GameObjectData const* GetGOData() const { return m_goData; } GameObjectValue const* GetGOValue() const { return &m_goValue; } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 93a80b5d4..ab9319e16 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -9011,6 +9011,8 @@ void Player::SendLoot(uint64 guid, LootType loot_type) if (groupRules && !go->loot.empty()) group->UpdateLooterGuid(go); } + if (GameObjectTemplateAddon const* addon = go->GetTemplateAddon()) + loot->generateMoneyLoot(addon->mingold, addon->maxgold); if (loot_type == LOOT_FISHING) go->getFishLoot(loot, this); diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index e2f831839..79f773bb2 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -70,9 +70,13 @@ bool MotionTransport::CreateMoTrans(uint32 guidlow, uint32 entry, uint32 mapid, _triggeredArrivalEvent = false; _triggeredDepartureEvent = false; + if (GameObjectTemplateAddon const* addon = GetTemplateAddon()) + { + SetUInt32Value(GAMEOBJECT_FACTION, addon->faction); + SetUInt32Value(GAMEOBJECT_FLAGS, addon->flags); + } + SetObjectScale(goinfo->size); - SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction); - SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags); SetPathProgress(0); SetPeriod(tInfo->pathTime); SetEntry(goinfo->entry); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 1755e456f..608e06fc5 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -6437,11 +6437,11 @@ void ObjectMgr::LoadGameObjectTemplate() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 6 7 8 9 - QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, faction, flags, size, " - // 10 11 12 13 14 15 16 17 18 19 20 21 22 + // 0 1 2 3 4 5 6 7 + QueryResult result = WorldDatabase.Query("SELECT entry, type, displayId, name, IconName, castBarCaption, unk1, size, " + // 8 9 10 11 12 13 14 15 16 17 18 19 20 "Data0, Data1, Data2, Data3, Data4, Data5, Data6, Data7, Data8, Data9, Data10, Data11, Data12, " - // 23 24 25 26 27 28 29 30 31 32 33 34 35 + // 21 22 23 24 25 26 27 28 29 30 31 32 33 "Data13, Data14, Data15, Data16, Data17, Data18, Data19, Data20, Data21, Data22, Data23, AIName, ScriptName " "FROM gameobject_template"); @@ -6469,15 +6469,13 @@ void ObjectMgr::LoadGameObjectTemplate() got.IconName = fields[4].GetString(); got.castBarCaption = fields[5].GetString(); got.unk1 = fields[6].GetString(); - got.faction = uint32(fields[7].GetUInt16()); - got.flags = fields[8].GetUInt32(); - got.size = fields[9].GetFloat(); + got.size = fields[7].GetFloat(); for (uint8 i = 0; i < MAX_GAMEOBJECT_DATA; ++i) - got.raw.data[i] = fields[10 + i].GetInt32(); // data1 and data6 can be -1 + got.raw.data[i] = fields[8 + i].GetInt32(); // data1 and data6 can be -1 - got.AIName = fields[34].GetString(); - got.ScriptId = GetScriptId(fields[35].GetCString()); + got.AIName = fields[32].GetString(); + got.ScriptId = GetScriptId(fields[33].GetCString()); got.IsForQuests = false; // Checks @@ -6621,6 +6619,71 @@ void ObjectMgr::LoadGameObjectTemplate() sLog->outString(); } +void ObjectMgr::LoadGameObjectTemplateAddons() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 2 3 4 + QueryResult result = WorldDatabase.Query("SELECT entry, faction, flags, mingold, maxgold FROM gameobject_template_addon"); + + if (!result) + { + sLog->outString(">> Loaded 0 gameobject template addon definitions. DB table `gameobject_template_addon` is empty."); + sLog->outString(); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + + uint32 entry = fields[0].GetUInt32(); + + GameObjectTemplate const* got = sObjectMgr->GetGameObjectTemplate(entry); + if (!got) + { + sLog->outErrorDb( + "GameObject template (Entry: %u) does not exist but has a record in `gameobject_template_addon`", + entry); + continue; + } + + GameObjectTemplateAddon& gameObjectAddon = _gameObjectTemplateAddonStore[entry]; + gameObjectAddon.faction = uint32(fields[1].GetUInt16()); + gameObjectAddon.flags = fields[2].GetUInt32(); + gameObjectAddon.mingold = fields[3].GetUInt32(); + gameObjectAddon.maxgold = fields[4].GetUInt32(); + + // checks + if (gameObjectAddon.faction && !sFactionTemplateStore.LookupEntry(gameObjectAddon.faction)) + sLog->outErrorDb( + "GameObject (Entry: %u) has invalid faction (%u) defined in `gameobject_template_addon`.", + entry, gameObjectAddon.faction); + + if (gameObjectAddon.maxgold > 0) + { + switch (got->type) + { + case GAMEOBJECT_TYPE_CHEST: + case GAMEOBJECT_TYPE_FISHINGHOLE: + break; + default: + sLog->outErrorDb( + "GameObject (Entry %u GoType: %u) cannot be looted but has maxgold set in `gameobject_template_addon`.", + entry, got->type); + break; + } + } + + ++count; + } + while (result->NextRow()); + + sLog->outString(">> Loaded %u game object template addons in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + sLog->outString(); +} + void ObjectMgr::LoadExplorationBaseXP() { uint32 oldMSTime = getMSTime(); @@ -9013,6 +9076,15 @@ bool ObjectMgr::IsGameObjectStaticTransport(uint32 entry) return goinfo && goinfo->type == GAMEOBJECT_TYPE_TRANSPORT; } +GameObjectTemplateAddon const* ObjectMgr::GetGameObjectTemplateAddon(uint32 entry) const +{ + auto itr = _gameObjectTemplateAddonStore.find(entry); + if (itr != _gameObjectTemplateAddonStore.end()) + return &itr->second; + + return nullptr; +} + CreatureTemplate const* ObjectMgr::GetCreatureTemplate(uint32 entry) { return entry < _creatureTemplateStoreFast.size() ? _creatureTemplateStoreFast[entry] : NULL; diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 91f4914ed..94d066d83 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -715,6 +715,7 @@ class ObjectMgr int LoadReferenceVendor(int32 vendor, int32 item_id, std::set *skip_vendors); void LoadGameObjectTemplate(); + void LoadGameObjectTemplateAddons(); void AddGameobjectInfo(GameObjectTemplate* goinfo); CreatureTemplate const* GetCreatureTemplate(uint32 entry); @@ -726,6 +727,7 @@ class ObjectMgr EquipmentInfo const* GetEquipmentInfo(uint32 entry, int8& id); CreatureAddon const* GetCreatureAddon(uint32 lowguid); GameObjectAddon const* GetGameObjectAddon(uint32 lowguid); + GameObjectTemplateAddon const* GetGameObjectTemplateAddon(uint32 entry) const; CreatureAddon const* GetCreatureTemplateAddon(uint32 entry); ItemTemplate const* GetItemTemplate(uint32 entry); ItemTemplateContainer const* GetItemTemplateStore() const { return &_itemTemplateStore; } @@ -1447,6 +1449,7 @@ class ObjectMgr GameObjectDataContainer _gameObjectDataStore; GameObjectLocaleContainer _gameObjectLocaleStore; GameObjectTemplateContainer _gameObjectTemplateStore; + GameObjectTemplateAddonContainer _gameObjectTemplateAddonStore; /// Stores temp summon data grouped by summoner's entry, summoner's type and group id TempSummonDataContainer _tempSummonDataStore; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 3b1b20be1..e9108763c 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1469,6 +1469,9 @@ void World::SetInitialWorldSettings() sLog->outString("Loading Game Object Templates..."); // must be after LoadPageTexts sObjectMgr->LoadGameObjectTemplate(); + sLog->outString("Loading Game Object template addons..."); + sObjectMgr->LoadGameObjectTemplateAddons(); + sLog->outString("Loading Transport templates..."); sTransportMgr->LoadTransportTemplates();