From 66658d7ac9e9be7f9584e26c98a6306172bcc047 Mon Sep 17 00:00:00 2001 From: Sevi Date: Wed, 29 Nov 2023 17:25:30 +0100 Subject: [PATCH 1/4] Core/Creature: Added possibility to give creatures multiple gossip menu ids... Core/Creature: Added possibility to give creatures multiple gossip menu ids and control them via conditions ( https://github.com/TrinityCore/TrinityCore/pull/28159) Cherry-Pick: https://github.com/TrinityCore/TrinityCore/commit/36044a9470e9c39a71916e908ab6cf4f95677793 --- ...3_11_29_world_creature_template_gossip.sql | 14 ++ .../Database/Implementation/WorldDatabase.cpp | 2 +- src/server/game/Conditions/ConditionMgr.cpp | 9 +- .../game/Entities/Creature/Creature.cpp | 12 +- src/server/game/Entities/Creature/Creature.h | 5 + .../game/Entities/Creature/CreatureData.h | 2 +- src/server/game/Entities/Player/Player.cpp | 33 ++- src/server/game/Entities/Player/Player.h | 2 +- src/server/game/Globals/ObjectMgr.cpp | 194 +++++++++++------- src/server/game/Globals/ObjectMgr.h | 1 + src/server/game/Handlers/NPCHandler.cpp | 2 +- src/server/game/Handlers/QuestHandler.cpp | 4 +- src/server/game/World/World.cpp | 3 + .../the_scarlet_enclave_chapter_1.cpp | 2 +- .../IcecrownCitadel/boss_the_lich_king.cpp | 6 +- 15 files changed, 200 insertions(+), 91 deletions(-) create mode 100644 sql/updates/world/4.3.4/2023_11_29_world_creature_template_gossip.sql diff --git a/sql/updates/world/4.3.4/2023_11_29_world_creature_template_gossip.sql b/sql/updates/world/4.3.4/2023_11_29_world_creature_template_gossip.sql new file mode 100644 index 00000000000..e5bb2762e1e --- /dev/null +++ b/sql/updates/world/4.3.4/2023_11_29_world_creature_template_gossip.sql @@ -0,0 +1,14 @@ +-- +CREATE TABLE `creature_template_gossip` ( + `CreatureID` INT(10) UNSIGNED NOT NULL, + `MenuID` INT(10) UNSIGNED NOT NULL, + `VerifiedBuild` INT(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`CreatureID`, `MenuID`) USING BTREE +) +COLLATE='utf8mb4_unicode_ci' +ENGINE=InnoDB +; + +INSERT INTO `creature_template_gossip` SELECT `entry`, `gossip_menu_id`, `VerifiedBuild` FROM `creature_template` WHERE `gossip_menu_id` <> 0; + +ALTER TABLE `creature_template` DROP COLUMN `gossip_menu_id`; \ No newline at end of file diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index a27ca82c26c..7b60066bb36 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -76,7 +76,7 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, permission, help FROM command", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, femaleName, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, exp_unk, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, StaticFlags, StaticFlags2, StaticFlags3, StaticFlags4, StaticFlags5, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, femaleName, subname, IconName, minlevel, maxlevel, exp, exp_unk, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, StaticFlags, StaticFlags2, StaticFlags3, StaticFlags4, StaticFlags5, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_", CONNECTION_SYNCH); diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 15993d233a5..0d812781d44 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -1401,12 +1401,11 @@ bool ConditionMgr::addToGossipMenus(Condition* cond) const { for (GossipMenusContainer::iterator itr = pMenuBounds.first; itr != pMenuBounds.second; ++itr) { - if ((*itr).second.MenuID == cond->SourceGroup && (*itr).second.TextID == uint32(cond->SourceEntry)) - { - (*itr).second.Conditions.push_back(cond); - return true; - } + if (itr->second.MenuID == cond->SourceGroup && (itr->second.TextID == uint32(cond->SourceEntry) || cond->SourceEntry == 0)) + itr->second.Conditions.push_back(cond); } + + return true; } TC_LOG_ERROR("sql.sql", "%s GossipMenu %u not found.", cond->ToString().c_str(), cond->SourceGroup); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 256ffcf243b..b2098e4b2f7 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -282,7 +282,7 @@ m_respawnDelay(300), m_corpseDelay(60), m_wanderDistance(0.0f), m_boundaryCheckT m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(false), m_cannotReachTimer(0), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0), _cyclicSplinePathId(0), -m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), _lastDamagedTime(0), _isMissingSwimmingFlagOutOfCombat(false), _noNpcDamageBelowPctHealth(0.f) +m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), _lastDamagedTime(0), _isMissingSwimmingFlagOutOfCombat(false), _noNpcDamageBelowPctHealth(0.f), _gossipMenuId(0) { m_valuesCount = UNIT_END; @@ -1247,6 +1247,16 @@ Group* Creature::GetLootRecipientGroup() const return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup); } +uint32 Creature::GetGossipMenuId() const +{ + return _gossipMenuId; +} + +void Creature::SetGossipMenuId(uint32 gossipMenuID) +{ + _gossipMenuId = gossipMenuID; +} + void Creature::SetLootRecipient(Unit* unit, bool withGroup) { // set the player whose group should receive the right diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index f9dfb5cfa68..a7bb70e27fb 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -383,6 +383,9 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma bool HasStaticFlag(CreatureStaticFlags4 flag) const { return _staticFlags.HasFlag(flag); } bool HasStaticFlag(CreatureStaticFlags5 flag) const { return _staticFlags.HasFlag(flag); } + uint32 GetGossipMenuId() const; + void SetGossipMenuId(uint32 gossipMenuId); + bool HasSwimmingFlagOutOfCombat() const { return !_isMissingSwimmingFlagOutOfCombat; @@ -482,6 +485,8 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma bool _isMissingSwimmingFlagOutOfCombat; + uint32 _gossipMenuId; + CreatureMovementInfo _creatureMovementInfo; float _noNpcDamageBelowPctHealth; diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index c2adc0ca52e..42064541b9c 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -383,7 +383,7 @@ struct TC_GAME_API CreatureTemplate std::string FemaleName; std::string Title; std::string IconName; - uint32 GossipMenuId; + std::vector GossipMenuIds; uint8 minlevel; uint8 maxlevel; uint32 expansion; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 41bb98efaf1..423d883a020 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -13293,7 +13293,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool GossipMenuItemsMapBounds menuItemBounds = sObjectMgr->GetGossipMenuItemsMapBounds(menuId); // if default menuId and no menu options exist for this, use options from default options - if (menuItemBounds.first == menuItemBounds.second && menuId == GetDefaultGossipMenuForSource(source)) + if (menuItemBounds.first == menuItemBounds.second && menuId == GetGossipMenuForSource(source)) menuItemBounds = sObjectMgr->GetGossipMenuItemsMapBounds(0); uint32 npcflags = 0; @@ -13609,7 +13609,7 @@ uint32 Player::GetGossipTextId(WorldObject* source) if (!source) return DEFAULT_GOSSIP_MESSAGE; - return GetGossipTextId(GetDefaultGossipMenuForSource(source), source); + return GetGossipTextId(GetGossipMenuForSource(source), source); } uint32 Player::GetGossipTextId(uint32 menuId, WorldObject* source) @@ -13623,6 +13623,10 @@ uint32 Player::GetGossipTextId(uint32 menuId, WorldObject* source) for (GossipMenusContainer::const_iterator itr = menuBounds.first; itr != menuBounds.second; ++itr) { + // continue if only checks menuid instead of text + if (!itr->second.TextID) + continue; + if (sConditionMgr->IsObjectMeetToConditions(this, source, itr->second.Conditions)) textId = itr->second.TextID; } @@ -13630,12 +13634,33 @@ uint32 Player::GetGossipTextId(uint32 menuId, WorldObject* source) return textId; } -uint32 Player::GetDefaultGossipMenuForSource(WorldObject* source) +uint32 Player::GetGossipMenuForSource(WorldObject* source) { switch (source->GetTypeId()) { case TYPEID_UNIT: - return source->ToCreature()->GetCreatureTemplate()->GossipMenuId; + { + uint32 menuIdToShow = source->ToCreature()->GetGossipMenuId(); + + // if menu id is set by script + if (menuIdToShow) + return menuIdToShow; + + // otherwise pick from db based on conditions + for (uint32 menuId : source->ToCreature()->GetCreatureTemplate()->GossipMenuIds) + { + GossipMenusMapBounds menuBounds = sObjectMgr->GetGossipMenusMapBounds(menuId); + + for (GossipMenusContainer::const_iterator itr = menuBounds.first; itr != menuBounds.second; ++itr) + { + if (!sConditionMgr->IsObjectMeetToConditions(this, source, itr->second.Conditions)) + continue; + + menuIdToShow = menuId; + } + } + return menuIdToShow; + } case TYPEID_GAMEOBJECT: return source->ToGameObject()->GetGOInfo()->GetGossipMenuId(); default: diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 156ba5c4ba6..ab17d014a0c 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1320,7 +1320,7 @@ class TC_GAME_API Player : public Unit, public GridObject uint32 GetGossipTextId(uint32 menuId, WorldObject* source); uint32 GetGossipTextId(WorldObject* source); - static uint32 GetDefaultGossipMenuForSource(WorldObject* source); + uint32 GetGossipMenuForSource(WorldObject* source); /*********************************************************/ /*** QUEST SYSTEM ***/ diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index d92c32ce2cb..453036d7f6b 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -425,19 +425,19 @@ void ObjectMgr::LoadCreatureTemplates() // 0 1 2 3 4 5 6 7 8 QueryResult result = WorldDatabase.Query("SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, " - // 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - "modelid4, name, femaleName, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, exp_unk, faction, npcflag, speed_walk, speed_run, " - // 23 24 25 26 27 28 29 30 31 32 + // 9 10 11 12 13 14 15 16 17 18 19 20 21 + "modelid4, name, femaleName, subname, IconName, minlevel, maxlevel, exp, exp_unk, faction, npcflag, speed_walk, speed_run, " + // 22 23 24 25 26 27 28 29 30 31 "scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, " - // 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 + // 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 "dynamicflags, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, " - // 48 49 50 51 52 53 54 55 56 57 58 59 60 61 + // 47 48 49 50 51 52 53 54 55 56 57 58 59 60 "spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, " - // 62 63 64 65 66 67 68 69 70 71 72 + // 61 62 63 64 65 66 67 68 69 70 71 "ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, " - // 73 74 75 76 77 78 79 80 + // 72 73 74 75 76 77 78 79 "ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, " - // 81 82 83 84 85 86 87 + // 80 81 82 83 84 85 86 "flags_extra, StaticFlags, StaticFlags2, StaticFlags3, StaticFlags4, StaticFlags5, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId"); if (!result) @@ -486,83 +486,82 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) creatureTemplate.FemaleName = fields[11].GetString(); creatureTemplate.Title = fields[12].GetString(); creatureTemplate.IconName = fields[13].GetString(); - creatureTemplate.GossipMenuId = fields[14].GetUInt32(); - creatureTemplate.minlevel = fields[15].GetUInt8(); - creatureTemplate.maxlevel = fields[16].GetUInt8(); - creatureTemplate.expansion = uint32(fields[17].GetInt16()); - creatureTemplate.expansionUnknown = uint32(fields[18].GetUInt16()); - creatureTemplate.faction = uint32(fields[19].GetUInt16()); - creatureTemplate.npcflag = fields[20].GetUInt32(); - creatureTemplate.speed_walk = fields[21].GetFloat(); - creatureTemplate.speed_run = fields[22].GetFloat(); - creatureTemplate.scale = fields[23].GetFloat(); - creatureTemplate.rank = uint32(fields[24].GetUInt8()); - creatureTemplate.dmgschool = uint32(fields[25].GetInt8()); - creatureTemplate.BaseAttackTime = fields[26].GetUInt32(); - creatureTemplate.RangeAttackTime = fields[27].GetUInt32(); - creatureTemplate.BaseVariance = fields[28].GetFloat(); - creatureTemplate.RangeVariance = fields[29].GetFloat(); - creatureTemplate.unit_class = uint32(fields[30].GetUInt8()); - creatureTemplate.unit_flags = fields[31].GetUInt32(); - creatureTemplate.unit_flags2 = fields[32].GetUInt32(); - creatureTemplate.dynamicflags = fields[33].GetUInt32(); - creatureTemplate.family = CreatureFamily(uint32(fields[34].GetUInt8())); - creatureTemplate.trainer_class = fields[35].GetUInt32(); - creatureTemplate.type = uint32(fields[36].GetUInt8()); - creatureTemplate.type_flags = fields[37].GetUInt32(); - creatureTemplate.type_flags2 = fields[38].GetUInt32(); - creatureTemplate.lootid = fields[39].GetUInt32(); - creatureTemplate.pickpocketLootId = fields[40].GetUInt32(); - creatureTemplate.SkinLootId = fields[41].GetUInt32(); + creatureTemplate.minlevel = fields[14].GetUInt8(); + creatureTemplate.maxlevel = fields[15].GetUInt8(); + creatureTemplate.expansion = uint32(fields[16].GetInt16()); + creatureTemplate.expansionUnknown = uint32(fields[17].GetUInt16()); + creatureTemplate.faction = uint32(fields[18].GetUInt16()); + creatureTemplate.npcflag = fields[19].GetUInt32(); + creatureTemplate.speed_walk = fields[20].GetFloat(); + creatureTemplate.speed_run = fields[21].GetFloat(); + creatureTemplate.scale = fields[22].GetFloat(); + creatureTemplate.rank = uint32(fields[23].GetUInt8()); + creatureTemplate.dmgschool = uint32(fields[24].GetInt8()); + creatureTemplate.BaseAttackTime = fields[25].GetUInt32(); + creatureTemplate.RangeAttackTime = fields[26].GetUInt32(); + creatureTemplate.BaseVariance = fields[27].GetFloat(); + creatureTemplate.RangeVariance = fields[28].GetFloat(); + creatureTemplate.unit_class = uint32(fields[29].GetUInt8()); + creatureTemplate.unit_flags = fields[30].GetUInt32(); + creatureTemplate.unit_flags2 = fields[31].GetUInt32(); + creatureTemplate.dynamicflags = fields[32].GetUInt32(); + creatureTemplate.family = CreatureFamily(uint32(fields[33].GetUInt8())); + creatureTemplate.trainer_class = fields[34].GetUInt32(); + creatureTemplate.type = uint32(fields[35].GetUInt8()); + creatureTemplate.type_flags = fields[36].GetUInt32(); + creatureTemplate.type_flags2 = fields[37].GetUInt32(); + creatureTemplate.lootid = fields[38].GetUInt32(); + creatureTemplate.pickpocketLootId = fields[39].GetUInt32(); + creatureTemplate.SkinLootId = fields[40].GetUInt32(); for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) - creatureTemplate.resistance[i] = fields[42 + i - 1].GetInt16(); + creatureTemplate.resistance[i] = fields[41 + i - 1].GetInt16(); for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) - creatureTemplate.spells[i] = fields[48 + i].GetUInt32(); - - creatureTemplate.PetSpellDataId = fields[56].GetUInt32(); - creatureTemplate.VehicleId = fields[57].GetUInt32(); - creatureTemplate.mingold = fields[58].GetUInt32(); - creatureTemplate.maxgold = fields[59].GetUInt32(); - creatureTemplate.AIName = fields[60].GetString(); - creatureTemplate.MovementType = uint32(fields[61].GetUInt8()); + creatureTemplate.spells[i] = fields[47 + i].GetUInt32(); + + creatureTemplate.PetSpellDataId = fields[55].GetUInt32(); + creatureTemplate.VehicleId = fields[56].GetUInt32(); + creatureTemplate.mingold = fields[57].GetUInt32(); + creatureTemplate.maxgold = fields[58].GetUInt32(); + creatureTemplate.AIName = fields[59].GetString(); + creatureTemplate.MovementType = uint32(fields[60].GetUInt8()); + if (!fields[61].IsNull()) + creatureTemplate.Movement.Ground = static_cast(fields[61].GetUInt8()); + if (!fields[62].IsNull()) - creatureTemplate.Movement.Ground = static_cast(fields[62].GetUInt8()); + creatureTemplate.Movement.Swim = fields[62].GetBool(); if (!fields[63].IsNull()) - creatureTemplate.Movement.Swim = fields[63].GetBool(); + creatureTemplate.Movement.Flight = static_cast(fields[63].GetUInt8()); if (!fields[64].IsNull()) - creatureTemplate.Movement.Flight = static_cast(fields[64].GetUInt8()); + creatureTemplate.Movement.Rooted = fields[64].GetBool(); if (!fields[65].IsNull()) - creatureTemplate.Movement.Rooted = fields[65].GetBool(); + creatureTemplate.Movement.Random = static_cast(fields[65].GetUInt8()); if (!fields[66].IsNull()) - creatureTemplate.Movement.Random = static_cast(fields[66].GetUInt8()); - - if (!fields[67].IsNull()) - creatureTemplate.Movement.InteractionPauseTimer = fields[67].GetUInt32(); - - creatureTemplate.HoverHeight = fields[68].GetFloat(); - creatureTemplate.ModHealth = fields[69].GetFloat(); - creatureTemplate.ModHealthExtra = fields[70].GetFloat(); - creatureTemplate.ModMana = fields[71].GetFloat(); - creatureTemplate.ModManaExtra = fields[72].GetFloat(); - creatureTemplate.ModArmor = fields[73].GetFloat(); - creatureTemplate.ModDamage = fields[74].GetFloat(); - creatureTemplate.ModExperience = fields[75].GetFloat(); - - creatureTemplate.RacialLeader = fields[76].GetBool(); - creatureTemplate.movementId = fields[77].GetUInt32(); - creatureTemplate.RegenHealth = fields[78].GetBool(); - creatureTemplate.MechanicImmuneMask = fields[79].GetUInt32(); - creatureTemplate.SpellSchoolImmuneMask = fields[80].GetUInt32(); - creatureTemplate.flags_extra = fields[81].GetUInt32(); - creatureTemplate.StaticFlags = CreatureStaticFlagsHolder(CreatureStaticFlags(fields[82].GetUInt32()), CreatureStaticFlags2(fields[83].GetUInt32()), - CreatureStaticFlags3(fields[84].GetUInt32()), CreatureStaticFlags4(fields[85].GetUInt32()), CreatureStaticFlags5(fields[86].GetUInt32())); - creatureTemplate.ScriptID = GetScriptId(fields[87].GetCString()); + creatureTemplate.Movement.InteractionPauseTimer = fields[66].GetUInt32(); + + creatureTemplate.HoverHeight = fields[67].GetFloat(); + creatureTemplate.ModHealth = fields[68].GetFloat(); + creatureTemplate.ModHealthExtra = fields[69].GetFloat(); + creatureTemplate.ModMana = fields[70].GetFloat(); + creatureTemplate.ModManaExtra = fields[71].GetFloat(); + creatureTemplate.ModArmor = fields[72].GetFloat(); + creatureTemplate.ModDamage = fields[73].GetFloat(); + creatureTemplate.ModExperience = fields[74].GetFloat(); + + creatureTemplate.RacialLeader = fields[75].GetBool(); + creatureTemplate.movementId = fields[76].GetUInt32(); + creatureTemplate.RegenHealth = fields[77].GetBool(); + creatureTemplate.MechanicImmuneMask = fields[78].GetUInt32(); + creatureTemplate.SpellSchoolImmuneMask = fields[79].GetUInt32(); + creatureTemplate.flags_extra = fields[80].GetUInt32(); + creatureTemplate.StaticFlags = CreatureStaticFlagsHolder(CreatureStaticFlags(fields[81].GetUInt32()), CreatureStaticFlags2(fields[82].GetUInt32()), + CreatureStaticFlags3(fields[83].GetUInt32()), CreatureStaticFlags4(fields[84].GetUInt32()), CreatureStaticFlags5(fields[85].GetUInt32())); + creatureTemplate.ScriptID = GetScriptId(fields[86].GetCString()); } void ObjectMgr::LoadCreatureTemplateModels() @@ -620,6 +619,52 @@ void ObjectMgr::LoadCreatureTemplateModels() TC_LOG_INFO("server.loading", ">> Loaded %u creature template models in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } +void ObjectMgr::LoadCreatureTemplateGossip() +{ + uint32 oldMSTime = getMSTime(); + + // 0 1 + QueryResult result = WorldDatabase.Query("SELECT CreatureID, MenuID FROM creature_template_gossip"); + + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 creature template gossip definitions. DB table `creature_template_gossip` is empty."); + return; + } + + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + + uint32 creatureID = fields[0].GetUInt32(); + uint32 menuID = fields[1].GetUInt32(); + + CreatureTemplateContainer::iterator itr = _creatureTemplateStore.find(creatureID); + if (itr == _creatureTemplateStore.end()) + { + TC_LOG_ERROR("sql.sql", "creature_template_gossip has gossip definitions for creature %u but this creature doesn't exist", creatureID); + continue; + } + + GossipMenusMapBounds menuBounds = sObjectMgr->GetGossipMenusMapBounds(menuID); + if (menuBounds.first == menuBounds.second) + { + TC_LOG_ERROR("sql.sql", "creature_template_gossip has gossip definitions for menu id %u but this menu doesn't exist", menuID); + continue; + } + + CreatureTemplate& creatureTemplate = itr->second; + creatureTemplate.GossipMenuIds.push_back(menuID); + + ++count; + + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u creature template gossip menus in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + void ObjectMgr::LoadCreatureTemplateAddons() { uint32 oldMSTime = getMSTime(); @@ -1125,6 +1170,11 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) } const_cast(cInfo)->ModDamage *= Creature::_GetDamageMod(cInfo->rank); + + if (!cInfo->GossipMenuIds.empty() && !(cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP)) + TC_LOG_INFO("sql.sql", "Creature (Entry: %u) has assigned gossip menu, but npcflag does not include UNIT_NPC_FLAG_GOSSIP.", cInfo->Entry); + else if (cInfo->GossipMenuIds.empty() && cInfo->npcflag & UNIT_NPC_FLAG_GOSSIP) + TC_LOG_INFO("sql.sql", "Creature (Entry: %u) has npcflag UNIT_NPC_FLAG_GOSSIP, but gossip menu is unassigned.", cInfo->Entry); } void ObjectMgr::CheckCreatureMovement(char const* table, uint64 id, CreatureMovementData& creatureMovement) diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 489e7ab773b..9541e0e2270 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -1188,6 +1188,7 @@ class TC_GAME_API ObjectMgr void LoadCreatureSparringTemplate(); void LoadCreatureTemplate(Field* fields); void LoadCreatureTemplateModels(); + void LoadCreatureTemplateGossip(); void CheckCreatureTemplate(CreatureTemplate const* cInfo); void CheckCreatureMovement(char const* table, uint64 id, CreatureMovementData& creatureMovement); void LoadGameObjectQuestItems(); diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 4d555cd4272..3f3edf807d9 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -200,7 +200,7 @@ void WorldSession::HandleGossipHelloOpcode(WorldPackets::NPC::Hello& packet) if (!unit->AI()->GossipHello(_player)) { // _player->TalkedToCreature(unit->GetEntry(), unit->GetGUID()); - _player->PrepareGossipMenu(unit, unit->GetCreatureTemplate()->GossipMenuId, true); + _player->PrepareGossipMenu(unit, _player->GetGossipMenuForSource(unit), true); // If npc is a flightmaster who is a quest giver do not send the gossip if there is no quest if (unit->IsTaxi() && (unit->IsQuestGiver() || unit->IsGossip())) diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index 24dbd016bb0..9e82a668f03 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -98,7 +98,7 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPackets::Quest::QuestGiverHe if (creature->AI()->GossipHello(_player)) return; - _player->PrepareGossipMenu(creature, creature->GetCreatureTemplate()->GossipMenuId, true); + _player->PrepareGossipMenu(creature, _player->GetGossipMenuForSource(creature), true); _player->SendPreparedGossip(creature); } @@ -227,7 +227,7 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPackets::Quest::QuestG auto launchGossip = [&](WorldObject* worldObject) { _player->PlayerTalkClass->ClearMenus(); - _player->PrepareGossipMenu(worldObject, _player->GetDefaultGossipMenuForSource(worldObject), true); + _player->PrepareGossipMenu(worldObject, _player->GetGossipMenuForSource(worldObject), true); _player->SendPreparedGossip(worldObject); }; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index c2ca7eb7b89..51190cbf4ae 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -2131,6 +2131,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Gossip menu options..."); sObjectMgr->LoadGossipMenuItems(); + TC_LOG_INFO("server.loading", "Loading Creature Template Gossip..."); + sObjectMgr->LoadCreatureTemplateGossip(); + TC_LOG_INFO("server.loading", "Loading Creature trainers..."); sObjectMgr->LoadCreatureTrainers(); // must be after LoadGossipMenuItems diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/the_scarlet_enclave_chapter_1.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/the_scarlet_enclave_chapter_1.cpp index 959e072d13d..142a9a4e519 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/the_scarlet_enclave_chapter_1.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/the_scarlet_enclave_chapter_1.cpp @@ -647,7 +647,7 @@ class npc_death_knight_initiate : public CreatureScript if (player->IsInCombat() || me->IsInCombat()) return true; - AddGossipItemFor(player, Player::GetDefaultGossipMenuForSource(me), 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + AddGossipItemFor(player, player->GetGossipMenuForSource(me), 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); SendGossipMenuFor(player, player->GetGossipTextId(me), me->GetGUID()); } return true; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp index b7946a4dfd9..7b50b1dd921 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -363,7 +363,9 @@ enum MiscData enum Misc { DATA_PLAGUE_STACK = 70337, - DATA_VILE = 45814622 + DATA_VILE = 45814622, + + GOSSIP_MENU_START_INTRO = 10993 }; class NecroticPlagueTargetCheck @@ -1204,7 +1206,7 @@ class npc_tirion_fordring_tft : public CreatureScript bool GossipSelect(Player* /*player*/, uint32 menuId, uint32 gossipListId) override { - if (me->GetCreatureTemplate()->GossipMenuId == menuId && !gossipListId) + if (menuId == GOSSIP_MENU_START_INTRO && !gossipListId) { _events.SetPhase(PHASE_INTRO); me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); From 440c44fb1b179e3ee4ae8980d21ad047086b0a64 Mon Sep 17 00:00:00 2001 From: Sevi Date: Wed, 29 Nov 2023 17:34:36 +0100 Subject: [PATCH 2/4] DB/SQL: correct sql name --- ...ossip.sql => 2023_11_29_00_world_creature_template_gossip.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename sql/updates/world/4.3.4/{2023_11_29_world_creature_template_gossip.sql => 2023_11_29_00_world_creature_template_gossip.sql} (100%) diff --git a/sql/updates/world/4.3.4/2023_11_29_world_creature_template_gossip.sql b/sql/updates/world/4.3.4/2023_11_29_00_world_creature_template_gossip.sql similarity index 100% rename from sql/updates/world/4.3.4/2023_11_29_world_creature_template_gossip.sql rename to sql/updates/world/4.3.4/2023_11_29_00_world_creature_template_gossip.sql From 2c99ab7602f79dcdf0d8babadf1275c794fe712d Mon Sep 17 00:00:00 2001 From: Sevi Date: Wed, 29 Nov 2023 18:00:40 +0100 Subject: [PATCH 3/4] Core/Creature: set or return in the definition of the function. --- src/server/game/Entities/Creature/Creature.cpp | 10 ---------- src/server/game/Entities/Creature/Creature.h | 4 ++-- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index b2098e4b2f7..90cdd3854de 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1247,16 +1247,6 @@ Group* Creature::GetLootRecipientGroup() const return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup); } -uint32 Creature::GetGossipMenuId() const -{ - return _gossipMenuId; -} - -void Creature::SetGossipMenuId(uint32 gossipMenuID) -{ - _gossipMenuId = gossipMenuID; -} - void Creature::SetLootRecipient(Unit* unit, bool withGroup) { // set the player whose group should receive the right diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index a7bb70e27fb..70789f0a69b 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -383,8 +383,8 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma bool HasStaticFlag(CreatureStaticFlags4 flag) const { return _staticFlags.HasFlag(flag); } bool HasStaticFlag(CreatureStaticFlags5 flag) const { return _staticFlags.HasFlag(flag); } - uint32 GetGossipMenuId() const; - void SetGossipMenuId(uint32 gossipMenuId); + uint32 GetGossipMenuId() const { return _gossipMenuId; } + void SetGossipMenuId(uint32 gossipMenuId) { _gossipMenuId = gossipMenuId; } bool HasSwimmingFlagOutOfCombat() const { From 3c397ef59a0b89de22558fb54477863737df1dfb Mon Sep 17 00:00:00 2001 From: Sevi Date: Wed, 29 Nov 2023 19:17:07 +0100 Subject: [PATCH 4/4] Core/Creature: make checks happy --- src/server/game/Entities/Creature/Creature.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 70789f0a69b..5a69b7e9221 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -485,11 +485,11 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma bool _isMissingSwimmingFlagOutOfCombat; - uint32 _gossipMenuId; - CreatureMovementInfo _creatureMovementInfo; float _noNpcDamageBelowPctHealth; + + uint32 _gossipMenuId; }; class TC_GAME_API AssistDelayEvent : public BasicEvent