diff options
author | Shauren <shauren.trinity@gmail.com> | 2025-04-19 17:50:49 +0200 |
---|---|---|
committer | Ovahlord <dreadkiller@gmx.de> | 2025-05-11 12:09:54 +0200 |
commit | 8e0be127399e82a5082a9e740054b7e9fda259bc (patch) | |
tree | 4587d1242f8c8c01d15573aebc06af8817bc9f59 | |
parent | d01f12cc91d3443cb217ba7db0c4f42263dfc53c (diff) |
Core/Conditions: Added conditions for automatic learning spells with SkillLineAbility::AcquireMethod = 4
(cherry picked from commit 7eaa695581589e8cb9a277f9c13ad0e3daf669a7)
# Conflicts:
# sql/updates/world/cata_classic/2025_04_19_01_world.sql
# src/server/game/Conditions/ConditionMgr.cpp
-rw-r--r-- | sql/updates/world/cata_classic/2025_05_11_00_world_2025_04_19_01_world.sql | 16 | ||||
-rw-r--r-- | src/common/Utilities/Util.h | 2 | ||||
-rw-r--r-- | src/server/game/Conditions/ConditionMgr.cpp | 60 | ||||
-rw-r--r-- | src/server/game/Conditions/ConditionMgr.h | 3 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2HotfixGenerator.h | 8 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 6 | ||||
-rw-r--r-- | src/server/game/Spells/SpellMgr.cpp | 30 |
7 files changed, 78 insertions, 47 deletions
diff --git a/sql/updates/world/cata_classic/2025_05_11_00_world_2025_04_19_01_world.sql b/sql/updates/world/cata_classic/2025_05_11_00_world_2025_04_19_01_world.sql new file mode 100644 index 00000000000..a164db46d72 --- /dev/null +++ b/sql/updates/world/cata_classic/2025_05_11_00_world_2025_04_19_01_world.sql @@ -0,0 +1,16 @@ +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=35 AND `SourceEntry` IN (26154,44889,46614,46050,47629,51629,46736,46623,46727,48473,48474); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `ConditionStringValue1`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(35, 0, 26154, 0, 0, 22, 0, 609, 0, 0, '', 1, 0, 0, '', 'Learn Runeforging automatically - outside Ebon Hold'), +(35, 0, 44889, 0, 0, 56, 0, 83446, 0, 0, '', 0, 0, 0, '', 'Learn Touch of Death Notification Driver automatically - outside Exile''s Reach'), +(35, 0, 46614, 0, 0, 56, 0, 94589, 0, 0, '', 0, 0, 0, '', 'Learn Emerald Blossom automatically - outside The Forbidden Reach'), +(35, 0, 46050, 0, 0, 56, 0, 94589, 0, 0, '', 0, 0, 0, '', 'Learn Deep Breath automatically - outside The Forbidden Reach'), +(35, 0, 47629, 0, 0, 15, 0, 4096, 0, 0, '', 1, 0, 0, '', 'Learn Chosen Identity automatically - never as Evoker'), +(35, 0, 51629, 0, 0, 47, 0, 65101, 64, 0, '', 0, 0, 0, '', 'Learn Visage automatically - when An Iconic, Draconic Look is completed'), +(35, 0, 51629, 0, 1, 47, 0, 65613, 64, 0, '', 0, 0, 0, '', 'Learn Visage automatically - when An Iconic, Draconic Look is completed'), +(35, 0, 46736, 0, 0, 15, 0, 4096, 0, 0, '', 1, 0, 0, '', 'Learn Visage automatically - never as Evoker'), +(35, 0, 46623, 0, 0, 56, 0, 94589, 0, 0, '', 0, 0, 0, '', 'Learn Soar automatically - outside The Forbidden Reach'), +(35, 0, 46727, 0, 0, 56, 0, 96275, 0, 0, '', 0, 0, 0, '', 'Learn Activating Specialization automatically - outside The Forbidden Reach'), +(35, 0, 48473, 0, 0, 47, 0, 24593, 64, 0, '', 0, 0, 0, '', 'Learn Calm the Wolf automatically - when Neither Human Nor Beast is completed'), +(35, 0, 48473, 0, 1, 22, 0, 654, 0, 0, '', 1, 0, 0, '', 'Learn Calm the Wolf automatically - outside Gilneas'), +(35, 0, 48474, 0, 0, 47, 0, 24593, 64, 0, '', 0, 0, 0, '', 'Learn Calm the Wolf automatically - when Neither Human Nor Beast is completed'), +(35, 0, 48474, 0, 1, 22, 0, 654, 0, 0, '', 1, 0, 0, '', 'Learn Calm the Wolf automatically - outside Gilneas'); diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index 4907b4edbae..9ee4d1d62c0 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -530,7 +530,7 @@ constexpr typename std::underlying_type<E>::type AsUnderlyingType(E enumValue) } template<typename Ret, typename T1, typename... T> -Ret* Coalesce(T1* first, T*... rest) +constexpr Ret* Coalesce(T1* first, T*... rest) { if constexpr (sizeof...(T) > 0) return (first ? static_cast<Ret*>(first) : Coalesce<Ret>(rest...)); diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index efe0d2ec725..a5943a1a38d 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -93,7 +93,8 @@ char const* const ConditionMgr::StaticSourceTypeData[CONDITION_SOURCE_TYPE_MAX_D "Trainer Spell", "Object Visibility (by ID)", "Spawn Group", - "Player Condition" + "Player Condition", + "Skill Line Ability" }; ConditionMgr::ConditionTypeInfo const ConditionMgr::StaticConditionTypeData[CONDITION_MAX] = @@ -159,27 +160,20 @@ ConditionMgr::ConditionTypeInfo const ConditionMgr::StaticConditionTypeData[COND { "String ID", false, false, false, true } }; -ConditionSourceInfo::ConditionSourceInfo(WorldObject const* target0, WorldObject const* target1, WorldObject const* target2) +ConditionSourceInfo::ConditionSourceInfo(WorldObject const* target0, WorldObject const* target1, WorldObject const* target2) : + mConditionTargets({ target0, target1, target2 }), + mConditionMap(nullptr), + mLastFailedCondition(nullptr) { - mConditionTargets[0] = target0; - mConditionTargets[1] = target1; - mConditionTargets[2] = target2; - if (target0) - mConditionMap = target0->GetMap(); - else if (target1) - mConditionMap = target1->GetMap(); - else if (target2) - mConditionMap = target2->GetMap(); - else - mConditionMap = nullptr; - mLastFailedCondition = nullptr; + if (WorldObject const* target = Coalesce<WorldObject const>(target0, target1, target2)) + mConditionMap = target->GetMap(); } -ConditionSourceInfo::ConditionSourceInfo(Map const* map) +ConditionSourceInfo::ConditionSourceInfo(Map const* map) : + mConditionTargets(), + mConditionMap(map), + mLastFailedCondition(nullptr) { - std::fill(std::begin(mConditionTargets), std::end(mConditionTargets), nullptr); - mConditionMap = map; - mLastFailedCondition = nullptr; } std::size_t ConditionId::GetHash() const @@ -946,7 +940,7 @@ std::string Condition::ToString(bool ext /*= false*/) const } ss << "]"; - return ss.str(); + return std::move(ss).str(); } ConditionMgr::ConditionMgr() { } @@ -2067,6 +2061,22 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) const } break; } + case CONDITION_SOURCE_TYPE_SKILL_LINE_ABILITY: + { + SkillLineAbilityEntry const* skillLineAbility = sSkillLineAbilityStore.LookupEntry(cond->SourceEntry); + if (!skillLineAbility) + { + TC_LOG_ERROR("sql.sql", "{} SourceEntry in `condition` table, does not exist in SkillLineAbility.db2, ignoring.", cond->ToString()); + return false; + } + if (skillLineAbility->GetAcquireMethod() != SkillLineAbilityAcquireMethod::LearnedOrAutomaticCharLevel) + { + TC_LOG_ERROR("sql.sql", "{} in SkillLineAbility.db2 does not have AcquireMethod = {} (LearnedOrAutomaticCharLevel), ignoring.", + cond->ToString(), SkillLineAbilityAcquireMethod::LearnedOrAutomaticCharLevel); + return false; + } + break; + } case CONDITION_SOURCE_TYPE_GOSSIP_MENU: case CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION: case CONDITION_SOURCE_TYPE_SMART_EVENT: @@ -3228,7 +3238,7 @@ static int32(* const WorldStateExpressionFunctions[WSE_FUNCTION_MAX])(Map const* // WSE_FUNCTION_CLOCK_HOUR [](Map const* /*map*/, uint32 /*arg1*/, uint32 /*arg2*/) -> int32 { - uint32 currentHour = GameTime::GetDateAndTime()->tm_hour + 1; + int32 currentHour = GameTime::GetDateAndTime()->tm_hour + 1; return currentHour <= 12 ? (currentHour ? currentHour : 12) : currentHour - 12; }, @@ -3456,7 +3466,7 @@ int32 EvalSingleValue(ByteBuffer& buffer, Map const* map) } case WorldStateExpressionValueType::WorldState: { - uint32 worldStateId = buffer.read<uint32>(); + int32 worldStateId = buffer.read<int32>(); value = sWorldStateMgr->GetValue(worldStateId, map); break; } @@ -3661,7 +3671,7 @@ int32 GetUnitConditionVariable(Unit const* unit, Unit const* otherUnit, UnitCond case UnitConditionVariable::IsChannelingSpell: // this is supposed to return spell id by client code but data always has 0 or 1 return unit->GetChannelSpellId() != 0; case UnitConditionVariable::NumberOfMeleeAttackers: - return std::count_if(unit->getAttackers().begin(), unit->getAttackers().end(), [unit](Unit* attacker) + return std::ranges::count_if(unit->getAttackers(), [unit](Unit const* attacker) { float distance = std::max(unit->GetCombatReach() + attacker->GetCombatReach() + 1.3333334f, 5.0f); if (unit->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED) || attacker->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) @@ -3710,7 +3720,7 @@ int32 GetUnitConditionVariable(Unit const* unit, Unit const* otherUnit, UnitCond case UnitConditionVariable::NumberOfAttackers: return unit->getAttackers().size(); case UnitConditionVariable::NumberOfRangedAttackers: - return std::count_if(unit->getAttackers().begin(), unit->getAttackers().end(), [unit](Unit* attacker) + return std::ranges::count_if(unit->getAttackers(), [unit](Unit const* attacker) { float distance = std::max(unit->GetCombatReach() + attacker->GetCombatReach() + 1.3333334f, 5.0f); if (unit->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED) || attacker->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) @@ -3810,10 +3820,10 @@ int32 GetUnitConditionVariable(Unit const* unit, Unit const* otherUnit, UnitCond case UnitConditionVariable::IsHovering: return unit->IsHovering(); case UnitConditionVariable::HasHelpfulAuraEffect: - return value >= 0 && value < int32(TOTAL_AURAS) && std::find_if(unit->GetAuraEffectsByType(AuraType(value)).begin(), unit->GetAuraEffectsByType(AuraType(value)).end(), [unit](AuraEffect const* aurEff) + return value >= 0 && value < int32(TOTAL_AURAS) && std::ranges::any_of(unit->GetAuraEffectsByType(AuraType(value)), [unit](AuraEffect const* aurEff) { return (aurEff->GetBase()->GetApplicationOfTarget(unit->GetGUID())->GetFlags() & AFLAG_NEGATIVE) == 0; - }) != unit->GetAuraEffectsByType(AuraType(value)).end(); + }); case UnitConditionVariable::HasHelpfulAuraSchool: return unit->GetAuraApplication([value](AuraApplication const* aurApp) { diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h index eba98022709..0a618a6ec95 100644 --- a/src/server/game/Conditions/ConditionMgr.h +++ b/src/server/game/Conditions/ConditionMgr.h @@ -186,6 +186,7 @@ enum ConditionSourceType CONDITION_SOURCE_TYPE_OBJECT_ID_VISIBILITY = 32, CONDITION_SOURCE_TYPE_SPAWN_GROUP = 33, CONDITION_SOURCE_TYPE_PLAYER_CONDITION = 34, + CONDITION_SOURCE_TYPE_SKILL_LINE_ABILITY = 35, CONDITION_SOURCE_TYPE_MAX_DB_ALLOWED, CONDITION_SOURCE_TYPE_REFERENCE_CONDITION = CONDITION_SOURCE_TYPE_MAX_DB_ALLOWED, // internal, not set in db @@ -218,7 +219,7 @@ enum MaxConditionTargets struct TC_GAME_API ConditionSourceInfo { - WorldObject const* mConditionTargets[MAX_CONDITION_TARGETS]; // an array of targets available for conditions + std::array<WorldObject const*, MAX_CONDITION_TARGETS> mConditionTargets; // an array of targets available for conditions Map const* mConditionMap; Condition const* mLastFailedCondition; ConditionSourceInfo(WorldObject const* target0, WorldObject const* target1 = nullptr, WorldObject const* target2 = nullptr); diff --git a/src/server/game/DataStores/DB2HotfixGenerator.h b/src/server/game/DataStores/DB2HotfixGenerator.h index 6287160dbe3..79c926a5731 100644 --- a/src/server/game/DataStores/DB2HotfixGenerator.h +++ b/src/server/game/DataStores/DB2HotfixGenerator.h @@ -15,10 +15,11 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef DB2HotfixGenerator_h__ -#define DB2HotfixGenerator_h__ +#ifndef TRINITYCORE_DB2_HOTFIX_GENERATOR_H +#define TRINITYCORE_DB2_HOTFIX_GENERATOR_H #include "DB2Store.h" +#include <initializer_list> #include <span> class TC_GAME_API DB2HotfixGeneratorBase @@ -35,6 +36,7 @@ public: explicit DB2HotfixGenerator(DB2Storage<T>& storage) : _storage(storage), _count(0) { } void ApplyHotfix(uint32 id, void(*fixer)(T*), bool notifyClient = false) { ApplyHotfix({ &id, 1 }, fixer, notifyClient); } + void ApplyHotfix(std::initializer_list<uint32> ids, void(*fixer)(T*), bool notifyClient = false) { ApplyHotfix({ ids.begin(), ids.end() }, fixer, notifyClient); } uint32 GetAppliedHotfixesCount() const { return _count; } @@ -62,4 +64,4 @@ private: uint32 _count; }; -#endif // DB2HotfixGenerator_h__ +#endif // TRINITYCORE_DB2_HOTFIX_GENERATOR_H diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 4aaed6dc52b..6cdce3088cd 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -23756,8 +23756,10 @@ void Player::LearnSkillRewardedSpells(uint32 skillId, uint32 skillValue, Races r case SkillLineAbilityAcquireMethod::AutomaticCharLevel: break; case SkillLineAbilityAcquireMethod::LearnedOrAutomaticCharLevel: - if (!ability->GetFlags().HasFlag(SkillLineAbilityFlags::CanFallbackToLearnedOnSkillLearn) || - !spellInfo->MeetsFutureSpellPlayerCondition(this)) + // Treat as AutomaticCharLevel when conditions are met, otherwise treat it as Learned (trainer or quest) + if (spellInfo->ShowFutureSpellPlayerConditionID && !ConditionMgr::IsPlayerMeetingCondition(this, spellInfo->ShowFutureSpellPlayerConditionID)) + continue; + if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_SKILL_LINE_ABILITY, ability->ID, this)) continue; break; default: diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 6cf7597e024..3d71f8c9d9d 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -20,6 +20,7 @@ #include "BattlefieldMgr.h" #include "BattlegroundMgr.h" #include "Chat.h" +#include "DB2HotfixGenerator.h" #include "DB2Stores.h" #include "DatabaseEnv.h" #include "LanguageMgr.h" @@ -2074,19 +2075,11 @@ void SpellMgr::LoadSkillLineAbilityMap() mSkillLineAbilityMap.clear(); - uint32 count = 0; - - for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i) - { - SkillLineAbilityEntry const* SkillInfo = sSkillLineAbilityStore.LookupEntry(i); - if (!SkillInfo) - continue; - - mSkillLineAbilityMap.insert(SkillLineAbilityMap::value_type(SkillInfo->Spell, SkillInfo)); - ++count; - } + for (SkillLineAbilityEntry const* skillLineAbility : sSkillLineAbilityStore) + mSkillLineAbilityMap.emplace(skillLineAbility->Spell, skillLineAbility); - TC_LOG_INFO("server.loading", ">> Loaded {} SkillLineAbility MultiMap Data in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded {} SkillLineAbility MultiMap Data in {} ms", + mSkillLineAbilityMap.size(), GetMSTimeDiffToNow(oldMSTime)); } void SpellMgr::LoadSpellPetAuras() @@ -4827,12 +4820,19 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->MaxAffectedTargets = 1; } - if (SummonPropertiesEntry* properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(121))) + DB2HotfixGenerator summonProperties(sSummonPropertiesStore); + summonProperties.ApplyHotfix(121, [](SummonPropertiesEntry* properties) + { properties->Title = AsUnderlyingType(SummonTitle::Totem); - if (SummonPropertiesEntry* properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(647))) // 52893 + }); + summonProperties.ApplyHotfix(647, [](SummonPropertiesEntry* properties) // 52893 + { properties->Title = AsUnderlyingType(SummonTitle::Totem); - if (SummonPropertiesEntry* properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(628))) // Hungry Plaguehound + }); + summonProperties.ApplyHotfix(628, [](SummonPropertiesEntry* properties) // Hungry Plaguehound + { properties->Control = SUMMON_CATEGORY_PET; + }); TC_LOG_INFO("server.loading", ">> Loaded SpellInfo corrections in {} ms", GetMSTimeDiffToNow(oldMSTime)); } |