diff options
-rw-r--r-- | sql/updates/hotfixes/2016_01_03_00_hotfixes.sql | 160 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.cpp | 18 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.h | 3 | ||||
-rw-r--r-- | src/server/game/Conditions/ConditionMgr.cpp | 417 | ||||
-rw-r--r-- | src/server/game/Conditions/ConditionMgr.h | 3 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.cpp | 2 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.h | 1 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Structure.h | 81 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2fmt.h | 1 | ||||
-rw-r--r-- | src/server/game/Reputation/ReputationMgr.h | 7 |
10 files changed, 692 insertions, 1 deletions
diff --git a/sql/updates/hotfixes/2016_01_03_00_hotfixes.sql b/sql/updates/hotfixes/2016_01_03_00_hotfixes.sql new file mode 100644 index 00000000000..45ab99f7084 --- /dev/null +++ b/sql/updates/hotfixes/2016_01_03_00_hotfixes.sql @@ -0,0 +1,160 @@ +-- +-- Table structure for table `player_condition` +-- + +DROP TABLE IF EXISTS `player_condition`; +CREATE TABLE `player_condition` ( + `ID` int(10) unsigned NOT NULL DEFAULT '0', + `Flags` int(10) unsigned NOT NULL DEFAULT '0', + `MinLevel` int(10) unsigned NOT NULL DEFAULT '0', + `MaxLevel` int(10) unsigned NOT NULL DEFAULT '0', + `RaceMask` int(10) unsigned NOT NULL DEFAULT '0', + `ClassMask` int(10) unsigned NOT NULL DEFAULT '0', + `Gender` int(11) NOT NULL DEFAULT '0', + `NativeGender` int(11) NOT NULL DEFAULT '0', + `SkillID1` int(10) unsigned NOT NULL DEFAULT '0', + `SkillID2` int(10) unsigned NOT NULL DEFAULT '0', + `SkillID3` int(10) unsigned NOT NULL DEFAULT '0', + `SkillID4` int(10) unsigned NOT NULL DEFAULT '0', + `MinSkill1` int(11) NOT NULL DEFAULT '0', + `MinSkill2` int(11) NOT NULL DEFAULT '0', + `MinSkill3` int(11) NOT NULL DEFAULT '0', + `MinSkill4` int(11) NOT NULL DEFAULT '0', + `MaxSkill1` int(11) NOT NULL DEFAULT '0', + `MaxSkill2` int(11) NOT NULL DEFAULT '0', + `MaxSkill3` int(11) NOT NULL DEFAULT '0', + `MaxSkill4` int(11) NOT NULL DEFAULT '0', + `SkillLogic` int(10) unsigned NOT NULL DEFAULT '0', + `LanguageID` int(10) unsigned NOT NULL DEFAULT '0', + `MinLanguage` int(10) unsigned NOT NULL DEFAULT '0', + `MaxLanguage` int(10) unsigned NOT NULL DEFAULT '0', + `MinFactionID1` int(10) unsigned NOT NULL DEFAULT '0', + `MinFactionID2` int(10) unsigned NOT NULL DEFAULT '0', + `MinFactionID3` int(10) unsigned NOT NULL DEFAULT '0', + `MaxFactionID` int(10) unsigned NOT NULL DEFAULT '0', + `MinReputation1` int(10) unsigned NOT NULL DEFAULT '0', + `MinReputation2` int(10) unsigned NOT NULL DEFAULT '0', + `MinReputation3` int(10) unsigned NOT NULL DEFAULT '0', + `MaxReputation` int(10) unsigned NOT NULL DEFAULT '0', + `ReputationLogic` int(10) unsigned NOT NULL DEFAULT '0', + `Unknown1` int(10) unsigned NOT NULL DEFAULT '0', + `MinPVPRank` int(10) unsigned NOT NULL DEFAULT '0', + `MaxPVPRank` int(10) unsigned NOT NULL DEFAULT '0', + `PvpMedal` int(10) unsigned NOT NULL DEFAULT '0', + `PrevQuestLogic` int(10) unsigned NOT NULL DEFAULT '0', + `PrevQuestID1` int(10) unsigned NOT NULL DEFAULT '0', + `PrevQuestID2` int(10) unsigned NOT NULL DEFAULT '0', + `PrevQuestID3` int(10) unsigned NOT NULL DEFAULT '0', + `PrevQuestID4` int(10) unsigned NOT NULL DEFAULT '0', + `CurrQuestLogic` int(10) unsigned NOT NULL DEFAULT '0', + `CurrQuestID1` int(10) unsigned NOT NULL DEFAULT '0', + `CurrQuestID2` int(10) unsigned NOT NULL DEFAULT '0', + `CurrQuestID3` int(10) unsigned NOT NULL DEFAULT '0', + `CurrQuestID4` int(10) unsigned NOT NULL DEFAULT '0', + `CurrentCompletedQuestLogic` int(10) unsigned NOT NULL DEFAULT '0', + `CurrentCompletedQuestID1` int(10) unsigned NOT NULL DEFAULT '0', + `CurrentCompletedQuestID2` int(10) unsigned NOT NULL DEFAULT '0', + `CurrentCompletedQuestID3` int(10) unsigned NOT NULL DEFAULT '0', + `CurrentCompletedQuestID4` int(10) unsigned NOT NULL DEFAULT '0', + `SpellLogic` int(10) unsigned NOT NULL DEFAULT '0', + `SpellID1` int(10) unsigned NOT NULL DEFAULT '0', + `SpellID2` int(10) unsigned NOT NULL DEFAULT '0', + `SpellID3` int(10) unsigned NOT NULL DEFAULT '0', + `SpellID4` int(10) unsigned NOT NULL DEFAULT '0', + `ItemLogic` int(10) unsigned NOT NULL DEFAULT '0', + `ItemID1` int(10) unsigned NOT NULL DEFAULT '0', + `ItemID2` int(10) unsigned NOT NULL DEFAULT '0', + `ItemID3` int(10) unsigned NOT NULL DEFAULT '0', + `ItemID4` int(10) unsigned NOT NULL DEFAULT '0', + `ItemCount1` int(10) unsigned NOT NULL DEFAULT '0', + `ItemCount2` int(10) unsigned NOT NULL DEFAULT '0', + `ItemCount3` int(10) unsigned NOT NULL DEFAULT '0', + `ItemCount4` int(10) unsigned NOT NULL DEFAULT '0', + `ItemFlags` int(10) unsigned NOT NULL DEFAULT '0', + `Explored1` int(10) unsigned NOT NULL DEFAULT '0', + `Explored2` int(10) unsigned NOT NULL DEFAULT '0', + `Time1` int(10) unsigned NOT NULL DEFAULT '0', + `Time2` int(10) unsigned NOT NULL DEFAULT '0', + `AuraSpellLogic` int(10) unsigned NOT NULL DEFAULT '0', + `AuraSpellID1` int(10) unsigned NOT NULL DEFAULT '0', + `AuraSpellID2` int(10) unsigned NOT NULL DEFAULT '0', + `AuraSpellID3` int(10) unsigned NOT NULL DEFAULT '0', + `AuraSpellID4` int(10) unsigned NOT NULL DEFAULT '0', + `WorldStateExpressionID` int(10) unsigned NOT NULL DEFAULT '0', + `WeatherID` int(10) unsigned NOT NULL DEFAULT '0', + `PartyStatus` int(10) unsigned NOT NULL DEFAULT '0', + `LifetimeMaxPVPRank` int(10) unsigned NOT NULL DEFAULT '0', + `AchievementLogic` int(10) unsigned NOT NULL DEFAULT '0', + `Achievement1` int(10) unsigned NOT NULL DEFAULT '0', + `Achievement2` int(10) unsigned NOT NULL DEFAULT '0', + `Achievement3` int(10) unsigned NOT NULL DEFAULT '0', + `Achievement4` int(10) unsigned NOT NULL DEFAULT '0', + `LfgLogic` int(10) unsigned NOT NULL DEFAULT '0', + `LfgStatus1` int(10) unsigned NOT NULL DEFAULT '0', + `LfgStatus2` int(10) unsigned NOT NULL DEFAULT '0', + `LfgStatus3` int(10) unsigned NOT NULL DEFAULT '0', + `LfgStatus4` int(10) unsigned NOT NULL DEFAULT '0', + `LfgCompare1` int(10) unsigned NOT NULL DEFAULT '0', + `LfgCompare2` int(10) unsigned NOT NULL DEFAULT '0', + `LfgCompare3` int(10) unsigned NOT NULL DEFAULT '0', + `LfgCompare4` int(10) unsigned NOT NULL DEFAULT '0', + `LfgValue1` int(10) unsigned NOT NULL DEFAULT '0', + `LfgValue2` int(10) unsigned NOT NULL DEFAULT '0', + `LfgValue3` int(10) unsigned NOT NULL DEFAULT '0', + `LfgValue4` int(10) unsigned NOT NULL DEFAULT '0', + `AreaLogic` int(10) unsigned NOT NULL DEFAULT '0', + `AreaID1` int(10) unsigned NOT NULL DEFAULT '0', + `AreaID2` int(10) unsigned NOT NULL DEFAULT '0', + `AreaID3` int(10) unsigned NOT NULL DEFAULT '0', + `AreaID4` int(10) unsigned NOT NULL DEFAULT '0', + `CurrencyLogic` int(10) unsigned NOT NULL DEFAULT '0', + `CurrencyID1` int(10) unsigned NOT NULL DEFAULT '0', + `CurrencyID2` int(10) unsigned NOT NULL DEFAULT '0', + `CurrencyID3` int(10) unsigned NOT NULL DEFAULT '0', + `CurrencyID4` int(10) unsigned NOT NULL DEFAULT '0', + `CurrencyCount1` int(10) unsigned NOT NULL DEFAULT '0', + `CurrencyCount2` int(10) unsigned NOT NULL DEFAULT '0', + `CurrencyCount3` int(10) unsigned NOT NULL DEFAULT '0', + `CurrencyCount4` int(10) unsigned NOT NULL DEFAULT '0', + `QuestKillID` int(10) unsigned NOT NULL DEFAULT '0', + `QuestKillLogic` int(10) unsigned NOT NULL DEFAULT '0', + `QuestKillMonster1` int(10) unsigned NOT NULL DEFAULT '0', + `QuestKillMonster2` int(10) unsigned NOT NULL DEFAULT '0', + `QuestKillMonster3` int(10) unsigned NOT NULL DEFAULT '0', + `QuestKillMonster4` int(10) unsigned NOT NULL DEFAULT '0', + `MinExpansionLevel` int(11) NOT NULL DEFAULT '0', + `MaxExpansionLevel` int(11) NOT NULL DEFAULT '0', + `MinExpansionTier` int(11) NOT NULL DEFAULT '0', + `MaxExpansionTier` int(11) NOT NULL DEFAULT '0', + `MinGuildLevel` int(10) unsigned NOT NULL DEFAULT '0', + `MaxGuildLevel` int(10) unsigned NOT NULL DEFAULT '0', + `PhaseUseFlags` int(10) unsigned NOT NULL DEFAULT '0', + `PhaseID` int(10) unsigned NOT NULL DEFAULT '0', + `PhaseGroupID` int(10) unsigned NOT NULL DEFAULT '0', + `MinAvgItemLevel` int(10) unsigned NOT NULL DEFAULT '0', + `MaxAvgItemLevel` int(10) unsigned NOT NULL DEFAULT '0', + `MinAvgEquippedItemLevel` int(10) unsigned NOT NULL DEFAULT '0', + `MaxAvgEquippedItemLevel` int(10) unsigned NOT NULL DEFAULT '0', + `ChrSpecializationIndex` int(11) NOT NULL DEFAULT '0', + `ChrSpecializationRole` int(11) NOT NULL DEFAULT '0', + `FailureDescription` text, + `PowerType` int(11) NOT NULL DEFAULT '0', + `PowerTypeComp` int(11) NOT NULL DEFAULT '0', + `PowerTypeValue` int(11) NOT NULL DEFAULT '0', + `VerifiedBuild` smallint(6) NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- +-- Table structure for table `player_condition_locale` +-- + +DROP TABLE IF EXISTS `player_condition_locale`; +CREATE TABLE `player_condition_locale` ( + `ID` int(10) unsigned NOT NULL DEFAULT '0', + `locale` varchar(4) NOT NULL, + `FailureDescription_lang` text, + `VerifiedBuild` smallint(6) NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`,`locale`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index 4a3d87d48cb..287b327033f 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -346,6 +346,24 @@ void HotfixDatabaseConnection::DoPrepareStatements() // PhaseXPhaseGroup.db2 PrepareStatement(HOTFIX_SEL_PHASE_X_PHASE_GROUP, "SELECT ID, PhaseID, PhaseGroupID FROM phase_group ORDER BY ID DESC", CONNECTION_SYNCH); + // PlayerCondition.db2 + PrepareStatement(HOTFIX_SEL_PLAYER_CONDITION, "SELECT ID, Flags, MinLevel, MaxLevel, RaceMask, ClassMask, Gender, NativeGender, SkillID1, " + "SkillID2, SkillID3, SkillID4, MinSkill1, MinSkill2, MinSkill3, MinSkill4, MaxSkill1, MaxSkill2, MaxSkill3, MaxSkill4, SkillLogic, " + "LanguageID, MinLanguage, MaxLanguage, MinFactionID1, MinFactionID2, MinFactionID3, MaxFactionID, MinReputation1, MinReputation2, " + "MinReputation3, MaxReputation, ReputationLogic, Unknown1, MinPVPRank, MaxPVPRank, PvpMedal, PrevQuestLogic, PrevQuestID1, PrevQuestID2, " + "PrevQuestID3, PrevQuestID4, CurrQuestLogic, CurrQuestID1, CurrQuestID2, CurrQuestID3, CurrQuestID4, CurrentCompletedQuestLogic, " + "CurrentCompletedQuestID1, CurrentCompletedQuestID2, CurrentCompletedQuestID3, CurrentCompletedQuestID4, SpellLogic, SpellID1, SpellID2, " + "SpellID3, SpellID4, ItemLogic, ItemID1, ItemID2, ItemID3, ItemID4, ItemCount1, ItemCount2, ItemCount3, ItemCount4, ItemFlags, Explored1, " + "Explored2, Time1, Time2, AuraSpellLogic, AuraSpellID1, AuraSpellID2, AuraSpellID3, AuraSpellID4, WorldStateExpressionID, WeatherID, " + "PartyStatus, LifetimeMaxPVPRank, AchievementLogic, Achievement1, Achievement2, Achievement3, Achievement4, LfgLogic, LfgStatus1, LfgStatus2, " + "LfgStatus3, LfgStatus4, LfgCompare1, LfgCompare2, LfgCompare3, LfgCompare4, LfgValue1, LfgValue2, LfgValue3, LfgValue4, AreaLogic, AreaID1, " + "AreaID2, AreaID3, AreaID4, CurrencyLogic, CurrencyID1, CurrencyID2, CurrencyID3, CurrencyID4, CurrencyCount1, CurrencyCount2, " + "CurrencyCount3, CurrencyCount4, QuestKillID, QuestKillLogic, QuestKillMonster1, QuestKillMonster2, QuestKillMonster3, QuestKillMonster4, " + "MinExpansionLevel, MaxExpansionLevel, MinExpansionTier, MaxExpansionTier, MinGuildLevel, MaxGuildLevel, PhaseUseFlags, PhaseID, " + "PhaseGroupID, MinAvgItemLevel, MaxAvgItemLevel, MinAvgEquippedItemLevel, MaxAvgEquippedItemLevel, ChrSpecializationIndex, " + "ChrSpecializationRole, FailureDescription, PowerType, PowerTypeComp, PowerTypeValue FROM player_condition ORDER BY ID DESC", CONNECTION_SYNCH); + PREPARE_LOCALE_STMT(HOTFIX_SEL_PLAYER_CONDITION, "SELECT ID, FailureDescription_lang FROM player_condition_locale WHERE locale = ?", CONNECTION_SYNCH); + // QuestMoneyReward.db2 PrepareStatement(HOTFIX_SEL_QUEST_MONEY_REWARD, "SELECT Level, Money1, Money2, Money3, Money4, Money5, Money6, Money7, Money8, Money9, Money10" " FROM quest_money_reward ORDER BY Level DESC", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index 812496c47f0..adc6299483c 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -202,6 +202,9 @@ enum HotfixDatabaseStatements HOTFIX_SEL_PHASE_X_PHASE_GROUP, + HOTFIX_SEL_PLAYER_CONDITION, + HOTFIX_SEL_PLAYER_CONDITION_LOCALE, + HOTFIX_SEL_QUEST_MONEY_REWARD, HOTFIX_SEL_QUEST_PACKAGE_ITEM, diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 4ca2ddec616..670cae308d6 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -19,6 +19,7 @@ #include "ConditionMgr.h" #include "AchievementMgr.h" #include "GameEventMgr.h" +#include "Group.h" #include "InstanceScript.h" #include "ObjectMgr.h" #include "Player.h" @@ -2300,3 +2301,419 @@ void ConditionMgr::Clean() AllocatedMemoryStore.clear(); } + +inline bool PlayerConditionCompare(int32 comparisonType, int32 value1, int32 value2) +{ + switch (comparisonType) + { + case 1: + return value1 == value2; + case 2: + return value1 != value2; + case 3: + return value1 > value2; + case 4: + return value1 >= value2; + case 5: + return value1 < value2; + case 6: + return value1 <= value2; + default: + break; + } + return false; +} + +template<std::size_t N> +inline bool PlayerConditionLogic(uint32 logic, std::array<bool, N>& results) +{ + static_assert(N < 16, "Logic array size must be equal to or less than 16"); + + for (std::size_t i = 0; i < results.size(); ++i) + if ((logic >> (16 + i)) & 1) + results[i] ^= true; + + bool result = results[0]; + for (std::size_t i = 1; i < results.size(); ++i) + { + switch ((logic >> (2 * (i - 1))) & 3) + { + case 1: + result = result && results[i]; + break; + case 2: + result = result || results[i]; + break; + default: + break; + } + } + + return result; +} + +bool ConditionMgr::IsPlayerMeetingCondition(Player* player, PlayerConditionEntry const* condition) +{ + if (condition->MinLevel && player->getLevel() < condition->MinLevel) + return false; + + if (condition->MaxLevel && player->getLevel() > condition->MaxLevel) + return false; + + if (condition->RaceMask && !(player->getRaceMask() & condition->RaceMask)) + return false; + + if (condition->ClassMask && !(player->getClassMask() & condition->ClassMask)) + return false; + + if (condition->Gender >= 0 && player->getGender() != condition->Gender) + return false; + + if (condition->NativeGender >= 0 && player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER) != condition->NativeGender) + return false; + + if (condition->PowerType != -1 && condition->PowerTypeComp) + { + int32 requiredPowerValue = condition->Flags & 4 ? player->GetMaxPower(Powers(condition->PowerType)) : condition->PowerTypeValue; + if (!PlayerConditionCompare(condition->PowerTypeComp, player->GetPower(Powers(condition->PowerType)), requiredPowerValue)) + return false; + } + + if (condition->ChrSpecializationIndex >= 0 || condition->ChrSpecializationRole >= 0) + { + if (ChrSpecializationEntry const* spec = sChrSpecializationStore.LookupEntry(player->GetSpecId(player->GetActiveTalentGroup()))) + { + if (condition->ChrSpecializationIndex >= 0 && spec->OrderIndex != condition->ChrSpecializationIndex) + return false; + + if (condition->ChrSpecializationRole >= 0 && spec->Role != condition->ChrSpecializationRole) + return false; + } + } + + if (condition->SkillID[0] || condition->SkillID[1] || condition->SkillID[2] || condition->SkillID[3]) + { + using SkillCount = std::extent<decltype(condition->SkillID)>; + + std::array<bool, SkillCount::value> results; + results.fill(true); + for (std::size_t i = 0; i < SkillCount::value; ++i) + { + if (condition->SkillID[i]) + { + uint16 skillValue = player->GetSkillValue(condition->SkillID[i]); + results[i] = skillValue != 0 && skillValue > condition->MinSkill[i] && skillValue < condition->MaxSkill[i]; + } + } + + if (!PlayerConditionLogic(condition->SkillLogic, results)) + return false; + } + + if (condition->LanguageID) + { + if (LanguageDesc const* lang = GetLanguageDescByID(condition->LanguageID)) + { + uint32 languageSkill = player->GetSkillValue(lang->skill_id); + if (!languageSkill && player->HasAuraTypeWithMiscvalue(SPELL_AURA_COMPREHEND_LANGUAGE, condition->LanguageID)) + languageSkill = 300; + + if (condition->MinLanguage && languageSkill < condition->MinLanguage) + return false; + + if (condition->MaxLanguage && languageSkill > condition->MaxLanguage) + return false; + } + } + + if (condition->MinFactionID[0] && condition->MinFactionID[1] && condition->MinFactionID[2] && condition->MaxFactionID) + { + if (!condition->MinFactionID[0] && !condition->MinFactionID[1] && !condition->MinFactionID[2]) + { + if (ReputationRank const* forcedRank = player->GetReputationMgr().GetForcedRankIfAny(condition->MaxFactionID)) + { + if (*forcedRank > condition->MaxReputation) + return false; + } + else if (player->GetReputationRank(condition->MaxFactionID) > condition->MaxReputation) + return false; + } + else + { + using FactionCount = std::extent<decltype(condition->MinFactionID)>; + + std::array<bool, FactionCount::value + 1> results; + results.fill(true); + for (std::size_t i = 0; i < FactionCount::value; ++i) + { + if (condition->MinFactionID[i]) + { + if (ReputationRank const* forcedRank = player->GetReputationMgr().GetForcedRankIfAny(condition->MinFactionID[i])) + results[i] = *forcedRank >= condition->MinReputation[i]; + else + results[i] = player->GetReputationRank(condition->MinFactionID[i]) >= condition->MinReputation[i]; + } + } + + if (ReputationRank const* forcedRank = player->GetReputationMgr().GetForcedRankIfAny(condition->MaxFactionID)) + results[3] = *forcedRank <= condition->MaxReputation; + else + results[3] = player->GetReputationRank(condition->MaxFactionID) <= condition->MaxReputation; + + if (!PlayerConditionLogic(condition->ReputationLogic, results)) + return false; + } + } + + if (condition->PvpMedal && !((1 << (condition->PvpMedal - 1)) & player->GetUInt32Value(PLAYER_FIELD_PVP_MEDALS))) + return false; + + if (condition->LifetimeMaxPVPRank && player->GetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_LIFETIME_MAX_PVP_RANK) != condition->LifetimeMaxPVPRank) + return false; + + if (condition->PartyStatus) + { + Group* group = player->GetGroup(); + switch (condition->PartyStatus) + { + case 1: + if (group) + return false; + break; + case 2: + if (!group) + return false; + break; + case 3: + if (!group || group->isRaidGroup()) + return false; + break; + case 4: + if (!group || !group->isRaidGroup()) + return false; + break; + case 5: + if (group && group->isRaidGroup()) + return false; + break; + default: + break; + } + } + + if (condition->PrevQuestID[0]) + { + using PrevQuestCount = std::extent<decltype(condition->PrevQuestID)>; + + std::array<bool, PrevQuestCount::value> results; + results.fill(true); + for (std::size_t i = 0; i < PrevQuestCount::value; ++i) + if (uint32 questBit = sDB2Manager.GetQuestUniqueBitFlag(condition->PrevQuestID[i])) + results[i] = (player->GetUInt32Value(PLAYER_FIELD_QUEST_COMPLETED + (questBit - 1) >> 5) & (1 << ((questBit - 1) & 31))) != 0; + + if (!PlayerConditionLogic(condition->PrevQuestLogic, results)) + return false; + } + + if (condition->CurrQuestID[0]) + { + using CurrQuestCount = std::extent<decltype(condition->CurrQuestID)>; + + std::array<bool, CurrQuestCount::value> results; + results.fill(true); + for (std::size_t i = 0; i < CurrQuestCount::value; ++i) + if (condition->CurrQuestID[i]) + results[i] = player->FindQuestSlot(condition->CurrQuestID[i]) != MAX_QUEST_LOG_SIZE; + + if (!PlayerConditionLogic(condition->CurrQuestLogic, results)) + return false; + } + + if (condition->CurrentCompletedQuestID[0]) + { + using CurrentCompletedQuestCount = std::extent<decltype(condition->CurrentCompletedQuestID)>; + + std::array<bool, CurrentCompletedQuestCount::value> results; + results.fill(true); + for (std::size_t i = 0; i < CurrentCompletedQuestCount::value; ++i) + if (condition->CurrentCompletedQuestID[i]) + results[i] = player->GetQuestStatus(condition->CurrentCompletedQuestID[i]) == QUEST_STATUS_COMPLETE; + + if (!PlayerConditionLogic(condition->CurrentCompletedQuestLogic, results)) + return false; + } + + + if (condition->SpellID[0]) + { + using SpellCount = std::extent<decltype(condition->SpellID)>; + + std::array<bool, SpellCount::value> results; + results.fill(true); + for (std::size_t i = 0; i < SpellCount::value; ++i) + if (condition->SpellID[i]) + results[i] = player->HasSpell(condition->SpellID[i]); + + if (!PlayerConditionLogic(condition->SpellLogic, results)) + return false; + } + + if (condition->ItemID[0]) + { + using ItemCount = std::extent<decltype(condition->ItemID)>; + + std::array<bool, ItemCount::value> results; + results.fill(true); + for (std::size_t i = 0; i < ItemCount::value; ++i) + if (condition->ItemID[i]) + results[i] = player->GetItemCount(condition->ItemID[i], condition->ItemFlags != 0) >= condition->ItemCount[i]; + + if (!PlayerConditionLogic(condition->ItemLogic, results)) + return false; + } + + if (condition->CurrencyID[0]) + { + using CurrencyCount = std::extent<decltype(condition->CurrencyID)>; + + std::array<bool, CurrencyCount::value> results; + results.fill(true); + for (std::size_t i = 0; i < CurrencyCount::value; ++i) + if (condition->CurrencyID[i]) + results[i] = player->GetCurrency(condition->CurrencyID[i]) >= condition->CurrencyCount[i]; + + if (!PlayerConditionLogic(condition->CurrencyLogic, results)) + return false; + } + + if (condition->Explored[0] || condition->Explored[1]) + { + using ExploredCount = std::extent<decltype(condition->Explored)>; + + for (std::size_t i = 0; i < ExploredCount::value; ++i) + { + if (condition->Explored[i]) + { + int32 exploreFlag = GetAreaFlagByAreaID(condition->Explored[i]); + if (exploreFlag != -1 && !(player->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + exploreFlag / 32) & (1 << (uint32(exploreFlag) % 32)))) + return false; + } + } + } + + if (condition->AuraSpellID[0]) + { + using AuraCount = std::extent<decltype(condition->AuraSpellID)>; + + std::array<bool, AuraCount::value> results; + results.fill(true); + for (std::size_t i = 0; i < AuraCount::value; ++i) + if (condition->AuraSpellID[i]) + results[i] = player->HasAura(condition->AuraSpellID[i]); + + if (!PlayerConditionLogic(condition->AuraSpellLogic, results)) + return false; + } + + // TODO: time condition + // TODO (or not): world state expression condition + // TODO: weather condition + + if (condition->Achievement[0]) + { + using AchievementCount = std::extent<decltype(condition->Achievement)>; + + std::array<bool, AchievementCount::value> results; + results.fill(true); + for (std::size_t i = 0; i < AchievementCount::value; ++i) + { + if (condition->Achievement[i]) + { + // if (condition->Flags & 2) { any character on account completed it } else { current character only } + // TODO: part of accountwide achievements + results[i] = player->HasAchieved(condition->Achievement[i]); + } + } + + if (!PlayerConditionLogic(condition->AchievementLogic, results)) + return false; + } + + // TODO: research lfg status for player conditions + + if (condition->AreaID[0]) + { + using AreaCount = std::extent<decltype(condition->AreaID)>; + + std::array<bool, AreaCount::value> results; + results.fill(true); + for (std::size_t i = 0; i < AreaCount::value; ++i) + if (condition->AreaID[i]) + results[i] = player->GetAreaId() == condition->AreaID[i] || player->GetZoneId() == condition->AreaID[i]; + + if (!PlayerConditionLogic(condition->AreaLogic, results)) + return false; + } + + if (condition->MinExpansionLevel != -1 && player->GetSession()->GetExpansion() < condition->MinExpansionLevel) + return false; + + if (condition->MaxExpansionLevel != -1 && player->GetSession()->GetExpansion() > condition->MaxExpansionLevel) + return false; + + if (condition->MinExpansionLevel != -1 && condition->MinExpansionTier != -1 && !player->IsGameMaster() + && (condition->MinExpansionLevel == sWorld->getIntConfig(CONFIG_EXPANSION) && condition->MinExpansionTier > 0 /*TODO: implement tier*/ + || condition->MinExpansionLevel > sWorld->getIntConfig(CONFIG_EXPANSION))) + return false; + + if (condition->PhaseID && !player->IsInPhase(condition->PhaseID)) + return false; + + if (condition->PhaseGroupID) + { + std::set<uint32> phases = sDB2Manager.GetPhasesForGroup(condition->PhaseGroupID); + if (!Trinity::Containers::Intersects(phases.begin(), phases.end(), player->GetPhases().begin(), player->GetPhases().end())) + return false; + } + + if (condition->QuestKillID) + { + Quest const* quest = sObjectMgr->GetQuestTemplate(condition->QuestKillID); + if (quest && player->GetQuestStatus(condition->QuestKillID) != QUEST_STATUS_COMPLETE) + { + using QuestKillCount = std::extent<decltype(condition->QuestKillMonster)>; + + std::array<bool, QuestKillCount::value> results; + results.fill(true); + for (std::size_t i = 0; i < QuestKillCount::value; ++i) + { + if (condition->QuestKillMonster[i]) + { + auto objectiveItr = std::find_if(quest->GetObjectives().begin(), quest->GetObjectives().end(), [condition, i](QuestObjective const& objective) -> bool + { + return objective.Type == QUEST_OBJECTIVE_MONSTER && uint32(objective.ObjectID) == condition->QuestKillMonster[i]; + }); + if (objectiveItr != quest->GetObjectives().end()) + results[i] = player->GetQuestObjectiveData(quest, objectiveItr->StorageIndex) >= objectiveItr->Amount; + } + } + + if (!PlayerConditionLogic(condition->QuestKillLogic, results)) + return false; + } + } + + if (condition->MinAvgItemLevel && uint32(std::floor(player->GetFloatValue(PLAYER_FIELD_AVG_ITEM_LEVEL))) < condition->MinAvgItemLevel) + return false; + + if (condition->MaxAvgItemLevel && uint32(std::floor(player->GetFloatValue(PLAYER_FIELD_AVG_ITEM_LEVEL))) > condition->MaxAvgItemLevel) + return false; + + if (condition->MinAvgEquippedItemLevel && uint32(std::floor(player->GetFloatValue(PLAYER_FIELD_AVG_ITEM_LEVEL + 1))) < condition->MinAvgEquippedItemLevel) + return false; + + if (condition->MaxAvgEquippedItemLevel && uint32(std::floor(player->GetFloatValue(PLAYER_FIELD_AVG_ITEM_LEVEL + 1))) > condition->MaxAvgEquippedItemLevel) + return false; + + return true; +} diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h index fa0c5038ddc..af0a2905fba 100644 --- a/src/server/game/Conditions/ConditionMgr.h +++ b/src/server/game/Conditions/ConditionMgr.h @@ -27,6 +27,7 @@ class Unit; class WorldObject; class LootTemplate; struct Condition; +struct PlayerConditionEntry; /*! Documentation on implementing a new ConditionType: Step 1: Check for the lowest free ID. Look for CONDITION_UNUSED_XX in the enum. @@ -273,6 +274,8 @@ class ConditionMgr bool IsObjectMeetingSmartEventConditions(int64 entryOrGuid, uint32 eventId, uint32 sourceType, Unit* unit, WorldObject* baseObject) const; bool IsObjectMeetingVendorItemConditions(uint32 creatureId, uint32 itemId, Player* player, Creature* vendor) const; + static bool IsPlayerMeetingCondition(Player* player, PlayerConditionEntry const* condition); + struct ConditionTypeInfo { char const* Name; diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 7e00c7ff884..9b87cdf9e42 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -97,6 +97,7 @@ DB2Storage<NamesReservedEntry> sNamesReservedStore("NamesReserv DB2Storage<NamesReservedLocaleEntry> sNamesReservedLocaleStore("NamesReservedLocale.db2", NamesReservedLocaleFormat, HOTFIX_SEL_NAMES_RESERVED_LOCALE); DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore("OverrideSpellData.db2", OverrideSpellDataFormat, HOTFIX_SEL_OVERRIDE_SPELL_DATA); DB2Storage<PhaseXPhaseGroupEntry> sPhaseXPhaseGroupStore("PhaseXPhaseGroup.db2", PhaseXPhaseGroupFormat, HOTFIX_SEL_PHASE_X_PHASE_GROUP); +DB2Storage<PlayerConditionEntry> sPlayerConditionStore("PlayerCondition.db2", PlayerConditionFormat, HOTFIX_SEL_PLAYER_CONDITION); DB2Storage<QuestMoneyRewardEntry> sQuestMoneyRewardStore("QuestMoneyReward.db2", QuestMoneyRewardFormat, HOTFIX_SEL_QUEST_MONEY_REWARD); DB2Storage<QuestPackageItemEntry> sQuestPackageItemStore("QuestPackageItem.db2", QuestPackageItemfmt, HOTFIX_SEL_QUEST_PACKAGE_ITEM); DB2Storage<QuestSortEntry> sQuestSortStore("QuestSort.db2", QuestSortFormat, HOTFIX_SEL_QUEST_SORT); @@ -271,6 +272,7 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale) LOAD_DB2(sNamesReservedLocaleStore); LOAD_DB2(sOverrideSpellDataStore); LOAD_DB2(sPhaseXPhaseGroupStore); + LOAD_DB2(sPlayerConditionStore); LOAD_DB2(sQuestMoneyRewardStore); LOAD_DB2(sQuestPackageItemStore); LOAD_DB2(sQuestSortStore); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 61b16d49634..13ffa4d84f2 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -80,6 +80,7 @@ extern DB2Storage<MailTemplateEntry> sMailTemplateStore; extern DB2Storage<ModifierTreeEntry> sModifierTreeStore; extern DB2Storage<MountCapabilityEntry> sMountCapabilityStore; extern DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore; +extern DB2Storage<PlayerConditionEntry> sPlayerConditionStore; extern DB2Storage<QuestMoneyRewardEntry> sQuestMoneyRewardStore; extern DB2Storage<QuestSortEntry> sQuestSortStore; extern DB2Storage<QuestXPEntry> sQuestXPStore; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index de66b58c862..5b00a0ff2be 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -994,6 +994,87 @@ struct PhaseXPhaseGroupEntry uint32 PhaseGroupID; }; +struct PlayerConditionEntry +{ + uint32 ID; // 0 + uint32 Flags; // 1 + uint32 MinLevel; // 2 + uint32 MaxLevel; // 3 + uint32 RaceMask; // 4 + uint32 ClassMask; // 5 + int32 Gender; // 6 + int32 NativeGender; // 7 + uint32 SkillID[4]; // 8-11 + int32 MinSkill[4]; // 12-15 + int32 MaxSkill[4]; // 16-19 + uint32 SkillLogic; // 20 + uint32 LanguageID; // 21 + uint32 MinLanguage; // 22 + uint32 MaxLanguage; // 23 + uint32 MinFactionID[3]; // 24-26 + uint32 MaxFactionID; // 27 + uint32 MinReputation[3]; // 28-30 + uint32 MaxReputation; // 31 + uint32 ReputationLogic; // 32 + uint32 Unknown1; // 33 + uint32 MinPVPRank; // 34 + uint32 MaxPVPRank; // 35 + uint32 PvpMedal; // 36 + uint32 PrevQuestLogic; // 37 + uint32 PrevQuestID[4]; // 38-41 + uint32 CurrQuestLogic; // 42 + uint32 CurrQuestID[4]; // 43-46 + uint32 CurrentCompletedQuestLogic; // 47 + uint32 CurrentCompletedQuestID[4]; // 48-51 + uint32 SpellLogic; // 52 + uint32 SpellID[4]; // 53-56 + uint32 ItemLogic; // 57 + uint32 ItemID[4]; // 58-61 + uint32 ItemCount[4]; // 62-65 + uint32 ItemFlags; // 66 + uint32 Explored[2]; // 67-68 + uint32 Time[2]; // 69-70 + uint32 AuraSpellLogic; // 71 + uint32 AuraSpellID[4]; // 72-75 + uint32 WorldStateExpressionID; // 76 + uint32 WeatherID; // 77 + uint32 PartyStatus; // 78 + uint32 LifetimeMaxPVPRank; // 79 + uint32 AchievementLogic; // 80 + uint32 Achievement[4]; // 81-84 + uint32 LfgLogic; // 85 + uint32 LfgStatus[4]; // 86-89 + uint32 LfgCompare[4]; // 90-93 + uint32 LfgValue[4]; // 94-97 + uint32 AreaLogic; // 98 + uint32 AreaID[4]; // 99-102 + uint32 CurrencyLogic; // 103 + uint32 CurrencyID[4]; // 104-107 + uint32 CurrencyCount[4]; // 108-111 + uint32 QuestKillID; // 112 + uint32 QuestKillLogic; // 113 + uint32 QuestKillMonster[4]; // 114-117 + int32 MinExpansionLevel; // 118 + int32 MaxExpansionLevel; // 119 + int32 MinExpansionTier; // 120 + int32 MaxExpansionTier; // 121 + uint32 MinGuildLevel; // 122 + uint32 MaxGuildLevel; // 123 + uint32 PhaseUseFlags; // 124 + uint32 PhaseID; // 125 + uint32 PhaseGroupID; // 126 + uint32 MinAvgItemLevel; // 127 + uint32 MaxAvgItemLevel; // 128 + uint32 MinAvgEquippedItemLevel; // 129 + uint32 MaxAvgEquippedItemLevel; // 130 + int32 ChrSpecializationIndex; // 131 + int32 ChrSpecializationRole; // 132 + LocalizedString* FailureDescription_lang; // 133 + int32 PowerType; // 134 + int32 PowerTypeComp; // 135 + int32 PowerTypeValue; // 136 +}; + struct QuestMoneyRewardEntry { uint32 Level; // 0 diff --git a/src/server/game/DataStores/DB2fmt.h b/src/server/game/DataStores/DB2fmt.h index 503c94f2053..99b7641e214 100644 --- a/src/server/game/DataStores/DB2fmt.h +++ b/src/server/game/DataStores/DB2fmt.h @@ -91,6 +91,7 @@ char const NamesReservedFormat[] = "nS"; char const NamesReservedLocaleFormat[] = "nSi"; char const OverrideSpellDataFormat[] = "niiiiiiiiiiii"; char const PhaseXPhaseGroupFormat[] = "nii"; +char const PlayerConditionFormat[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiisiii"; char const QuestMoneyRewardFormat[] = "niiiiiiiiii"; char const QuestPackageItemfmt[] = "niiii"; char const QuestSortFormat[] = "ns"; diff --git a/src/server/game/Reputation/ReputationMgr.h b/src/server/game/Reputation/ReputationMgr.h index edcaf81521d..224a928bbac 100644 --- a/src/server/game/Reputation/ReputationMgr.h +++ b/src/server/game/Reputation/ReputationMgr.h @@ -111,7 +111,12 @@ class ReputationMgr ReputationRank const* GetForcedRankIfAny(FactionTemplateEntry const* factionTemplateEntry) const { - ForcedReactions::const_iterator forceItr = _forcedReactions.find(factionTemplateEntry->Faction); + return GetForcedRankIfAny(factionTemplateEntry->Faction); + } + + ReputationRank const* GetForcedRankIfAny(uint32 factionId) const + { + ForcedReactions::const_iterator forceItr = _forcedReactions.find(factionId); return forceItr != _forcedReactions.end() ? &forceItr->second : NULL; } |