diff options
author | Rat <none@none> | 2010-06-04 23:24:48 +0200 |
---|---|---|
committer | Rat <none@none> | 2010-06-04 23:24:48 +0200 |
commit | 1426c2970f42a2d065198806f750bf5dd28d580b (patch) | |
tree | e3247fb0f1770ab214e412e0f2d6edc5b91ec8f4 /src | |
parent | 5ca00bc14d38c5ad49f0ab4500af52e645133826 (diff) |
HIGHLY EXPERIMENTAL - USE AT YOUR OWN RISK
implemented Condition System
all systems should work like before
after applying the sql converter you won't be able to apply any Updatepacks (<=up30) so do updates before this
this revesion is not threated as stable!
--HG--
branch : trunk
Diffstat (limited to 'src')
-rw-r--r-- | src/game/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/game/Chat.cpp | 3 | ||||
-rw-r--r-- | src/game/Chat.h | 3 | ||||
-rw-r--r-- | src/game/CombatAI.cpp | 64 | ||||
-rw-r--r-- | src/game/CombatAI.h | 12 | ||||
-rw-r--r-- | src/game/ConditionMgr.cpp | 1145 | ||||
-rw-r--r-- | src/game/ConditionMgr.h | 167 | ||||
-rw-r--r-- | src/game/CreatureEventAI.cpp | 9 | ||||
-rw-r--r-- | src/game/CreatureEventAIMgr.cpp | 15 | ||||
-rw-r--r-- | src/game/Item.cpp | 14 | ||||
-rw-r--r-- | src/game/Level3.cpp | 58 | ||||
-rw-r--r-- | src/game/LootMgr.cpp | 150 | ||||
-rw-r--r-- | src/game/LootMgr.h | 17 | ||||
-rw-r--r-- | src/game/ObjectMgr.cpp | 408 | ||||
-rw-r--r-- | src/game/ObjectMgr.h | 82 | ||||
-rw-r--r-- | src/game/Player.cpp | 19 | ||||
-rw-r--r-- | src/game/Player.h | 2 | ||||
-rw-r--r-- | src/game/Spell.cpp | 55 | ||||
-rw-r--r-- | src/game/SpellMgr.cpp | 168 | ||||
-rw-r--r-- | src/game/SpellMgr.h | 11 | ||||
-rw-r--r-- | src/game/World.cpp | 10 |
21 files changed, 1657 insertions, 757 deletions
diff --git a/src/game/CMakeLists.txt b/src/game/CMakeLists.txt index c425517f6cb..1ec450e9468 100644 --- a/src/game/CMakeLists.txt +++ b/src/game/CMakeLists.txt @@ -70,6 +70,8 @@ SET(game_STAT_SRCS Chat.h ChatHandler.cpp CombatHandler.cpp + ConditionMgr.cpp + ConditionMgr.h ConfusedMovementGenerator.cpp ConfusedMovementGenerator.h Corpse.cpp diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index 76a5f688d37..d3d6e63a232 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -451,6 +451,7 @@ ChatCommand * ChatHandler::getCommandTable() { "areatrigger_teleport", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAreaTriggerTeleportCommand, "", NULL }, { "autobroadcast", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAutobroadcastCommand, "", NULL }, { "command", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCommandCommand, "", NULL }, + { "conditions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadConditions, "", NULL }, { "creature_ai_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventAIScriptsCommand, "", NULL }, { "creature_ai_summons", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventAISummonsCommand, "", NULL }, { "creature_ai_texts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventAITextsCommand, "", NULL }, @@ -473,7 +474,6 @@ ChatCommand * ChatHandler::getCommandTable() { "gossip_menu_option", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGossipMenuOptionCommand, "", NULL }, { "item_enchantment_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadItemEnchantementsCommand, "", NULL }, { "item_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesItemCommand, "", NULL }, - { "item_required_target", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadItemRequiredTragetCommand, "", NULL }, { "locales_achievement_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesAchievementRewardCommand,"", NULL }, { "locales_creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesCreatureCommand, "", NULL }, { "locales_gameobject", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLocalesGameobjectCommand, "", NULL }, @@ -512,7 +512,6 @@ ChatCommand * ChatHandler::getCommandTable() { "spell_linked_spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellLinkedSpellCommand, "", NULL }, { "spell_pet_auras", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellPetAurasCommand, "", NULL }, { "spell_proc_event", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellProcEventCommand, "", NULL }, - { "spell_script_target", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellScriptTargetCommand, "", NULL }, { "spell_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellScriptsCommand, "", NULL }, { "spell_target_position", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellTargetPositionCommand, "", NULL }, { "spell_threats", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadSpellThreatsCommand, "", NULL }, diff --git a/src/game/Chat.h b/src/game/Chat.h index 9545588db7c..e05c5b23190 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -374,7 +374,6 @@ class ChatHandler bool HandleReloadGOQuestRelationsCommand(const char* args); bool HandleReloadGOQuestInvRelationsCommand(const char* args); bool HandleReloadItemEnchantementsCommand(const char* args); - bool HandleReloadItemRequiredTragetCommand(const char* args); bool HandleReloadLocalesAchievementRewardCommand(const char* args); bool HandleReloadLocalesCreatureCommand(const char* args); bool HandleReloadLocalesGameobjectCommand(const char* args); @@ -419,7 +418,6 @@ class ChatHandler bool HandleReloadSpellLinkedSpellCommand(const char* args); bool HandleReloadSpellProcEventCommand(const char* args); bool HandleReloadSpellBonusesCommand(const char* args); - bool HandleReloadSpellScriptTargetCommand(const char* args); bool HandleReloadSpellScriptsCommand(const char* args); bool HandleReloadSpellTargetPositionCommand(const char* args); bool HandleReloadSpellThreatsCommand(const char* args); @@ -428,6 +426,7 @@ class ChatHandler bool HandleReloadSpellGroupStackRulesCommand(const char* args); bool HandleReloadAuctionsCommand(const char* args); bool HandleReloadWpScriptsCommand(const char* args); + bool HandleReloadConditions(const char* args); bool HandleResetAchievementsCommand(const char * args); bool HandleResetAllCommand(const char * args); diff --git a/src/game/CombatAI.cpp b/src/game/CombatAI.cpp index 56d1e6d6a3d..0d0ff17ffd7 100644 --- a/src/game/CombatAI.cpp +++ b/src/game/CombatAI.cpp @@ -292,17 +292,79 @@ void AOEAI::UpdateAI(const uint32 /*diff*/) //VehicleAI ////////////// +VehicleAI::VehicleAI(Creature *c) : CreatureAI(c), m_vehicle(c->GetVehicleKit()), m_IsVehicleInUse(false), m_ConditionsTimer(VEHICLE_CONDITION_CHECK_TIME) +{ + LoadConditions(); + m_DoDismiss = false; + m_DismissTimer = VEHICLE_DISMISS_TIME; +} + + //NOTE: VehicleAI::UpdateAI runs even while the vehicle is mounted -void VehicleAI::UpdateAI(const uint32 /*diff*/) +void VehicleAI::UpdateAI(const uint32 diff) { + CheckConditions(diff); + + if (m_DoDismiss) + { + if (m_DismissTimer < diff) + { + m_DoDismiss = false; + me->SetVisibility(VISIBILITY_OFF); + me->ForcedDespawn(); + }else m_DismissTimer -= diff; + } } void VehicleAI::Reset() { + me->SetVisibility(VISIBILITY_ON); + m_vehicle->Reset(); } void VehicleAI::OnCharmed(bool apply) { + if (m_IsVehicleInUse && !apply && !conditions.empty())//was used and has conditions + { + m_DoDismiss = true;//needs reset + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_PLAYER_VEHICLE); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); + } + else if (apply) + m_DoDismiss = false;//in use again + m_DismissTimer = VEHICLE_DISMISS_TIME;//reset timer m_IsVehicleInUse = apply; +} + +void VehicleAI::LoadConditions() +{ + conditions = sConditionMgr.GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry()); + if (!conditions.empty()) + { + sLog.outDebug("VehicleAI::LoadConditions: loaded %u conditions", uint32(conditions.size())); + } +} + +void VehicleAI::CheckConditions(const uint32 diff) +{ + if(m_ConditionsTimer < diff) + { + if (!conditions.empty()) + { + for (SeatMap::iterator itr = m_vehicle->m_Seats.begin(); itr != m_vehicle->m_Seats.end(); ++itr) + if (Unit *passenger = itr->second.passenger) + { + if (Player* plr = passenger->ToPlayer()) + { + if (!sConditionMgr.IsPlayerMeetToConditions(plr, conditions)) + { + plr->ExitVehicle(); + return;//check other pessanger in next tick + } + } + } + } + m_ConditionsTimer = VEHICLE_CONDITION_CHECK_TIME; + } else m_ConditionsTimer -= diff; }
\ No newline at end of file diff --git a/src/game/CombatAI.h b/src/game/CombatAI.h index 40b4273d647..8626b38dd37 100644 --- a/src/game/CombatAI.h +++ b/src/game/CombatAI.h @@ -23,6 +23,7 @@ #include "CreatureAI.h" #include "CreatureAIImpl.h" +#include "ConditionMgr.h" class Creature; @@ -100,11 +101,12 @@ struct AOEAI : public CreatureAI static int Permissible(const Creature *); }; -#define VEHICLE_RESET_TIME 5000 +#define VEHICLE_CONDITION_CHECK_TIME 1000 +#define VEHICLE_DISMISS_TIME 5000 struct VehicleAI : public CreatureAI { public: - explicit VehicleAI(Creature *c) : CreatureAI(c), m_vehicle(c->GetVehicleKit()), m_IsVehicleInUse(false) {} + explicit VehicleAI(Creature *c); void UpdateAI(const uint32 diff); static int Permissible(const Creature *); @@ -116,6 +118,12 @@ struct VehicleAI : public CreatureAI private: Vehicle* m_vehicle; bool m_IsVehicleInUse; + void LoadConditions(); + void CheckConditions(const uint32 diff); + ConditionList conditions; + uint32 m_ConditionsTimer; + bool m_DoDismiss; + uint32 m_DismissTimer; }; #endif diff --git a/src/game/ConditionMgr.cpp b/src/game/ConditionMgr.cpp new file mode 100644 index 00000000000..8e5a7e5677e --- /dev/null +++ b/src/game/ConditionMgr.cpp @@ -0,0 +1,1145 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "Policies/SingletonImp.h" +#include "Player.h" +#include "SpellAuras.h" +#include "SpellMgr.h" +#include "GameEventMgr.h" +#include "ObjectMgr.h" +#include "ProgressBar.h" +#include "InstanceData.h" +#include "ConditionMgr.h" + +INSTANTIATE_SINGLETON_1(ConditionMgr); + +// Checks if player meets the condition +// Can have CONDITION_SOURCE_TYPE_NONE && !mReferenceId if called from a special event (ie: eventAI) +bool Condition::Meets(Player * player, Unit* targetOverride) +{ + if (!player) + { + sLog.outDebug("Condition player not found"); + return false; // player not present, return false + } + uint32 refId = 0; + bool condMeets = false; + bool sendErrorMsg = false; + refId = mConditionValue3;//value 3 can be a 'quick' reference + switch (mConditionType) + { + case CONDITION_NONE: + condMeets = true; // empty condition, always met + break; + case CONDITION_AURA: + condMeets = player->HasAuraEffect(mConditionValue1, mConditionValue2); + break; + case CONDITION_ITEM: + condMeets = player->HasItemCount(mConditionValue1, mConditionValue2); + break; + case CONDITION_ITEM_EQUIPPED: + condMeets = player->HasItemOrGemWithIdEquipped(mConditionValue1,1); + break; + case CONDITION_ZONEID: + condMeets = player->GetZoneId() == mConditionValue1; + break; + case CONDITION_REPUTATION_RANK: + { + FactionEntry const* faction = sFactionStore.LookupEntry(mConditionValue1); + condMeets = faction && uint32(player->GetReputationMgr().GetRank(faction)) >= int32(mConditionValue2); + break; + } + case CONDITION_ACHIEVEMENT: + { + AchievementEntry const* achievement = GetAchievementStore()->LookupEntry(mConditionValue1); + condMeets = player->GetAchievementMgr().HasAchieved(achievement); + break; + } + case CONDITION_TEAM: + condMeets = player->GetTeam() == mConditionValue1; + break; + case CONDITION_CLASS: + condMeets = player->getClass() == mConditionValue1; + break; + case CONDITION_RACE: + condMeets = player->getRace() == mConditionValue1; + break; + case CONDITION_SKILL: + condMeets = player->HasSkill(mConditionValue1) && player->GetBaseSkillValue(mConditionValue1) >= mConditionValue2; + break; + case CONDITION_QUESTREWARDED: + condMeets = player->GetQuestRewardStatus(mConditionValue1); + break; + case CONDITION_QUESTTAKEN: + { + QuestStatus status = player->GetQuestStatus(mConditionValue1); + condMeets = (status == QUEST_STATUS_INCOMPLETE); + break; + } + case CONDITION_QUEST_NONE: + { + QuestStatus status = player->GetQuestStatus(mConditionValue1); + condMeets = (status == QUEST_STATUS_NONE); + break; + } + case CONDITION_AD_COMMISSION_AURA: + { + Unit::AuraApplicationMap const& auras = player->GetAppliedAuras(); + for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) + if ((itr->second->GetBase()->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetBase()->GetSpellProto()->SpellVisual[0] == 3580) + { + condMeets = true; + break; + } + condMeets = false; + break; + } + case CONDITION_NO_AURA: + condMeets = !player->HasAuraEffect(mConditionValue1, mConditionValue2); + break; + case CONDITION_ACTIVE_EVENT: + condMeets = gameeventmgr.IsActiveEvent(mConditionValue1); + break; + case CONDITION_INSTANCE_DATA: + { + Map *map = player->GetMap(); + if (map && map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData()) + condMeets = ((InstanceMap*)map)->GetInstanceData()->GetData(mConditionValue1) == mConditionValue2; + break; + } + case CONDITION_SPELL_SCRIPT_TARGET: + condMeets = true;//spell target condition is handled in spellsystem, here it is always true + refId = 0;//cant have references! use CONDITION_SOURCE_TYPE_SPELL for it + break; + case CONDITION_CREATURE_TARGET: + { + Unit* target = player->GetSelectedUnit(); + if (targetOverride) + target = targetOverride; + if (target) + if (Creature* cTarget = target->ToCreature()) + if (cTarget->GetEntry() == mConditionValue1) + condMeets = true; + break; + } + case CONDITION_TARGET_HEALTH_BELOW_PCT: + { + Unit* target = player->GetSelectedUnit(); + if (targetOverride) + target = targetOverride; + if (target) + if ((target->GetHealth()*100 / target->GetMaxHealth()) <= mConditionValue1) + condMeets = true; + break; + } + case CONDITION_TARGET_RANGE: + { + if (Unit* target = player->GetSelectedUnit()) + if (player->GetDistance(target) >= mConditionValue1 && (!mConditionValue2 || player->GetDistance(target) <= mConditionValue2)) + condMeets = true; + break; + } + case CONDITION_MAPID: + condMeets = player->GetMapId() == mConditionValue1; + break; + case CONDITION_AREAID: + condMeets = player->GetAreaId() == mConditionValue1; + break; + case CONDITION_ITEM_TARGET: + { + condMeets = true;//handled in Item::IsTargetValidForItemUse + refId = 0;//cant have references for now + break; + } + default: + condMeets = false; + refId = 0; + break; + } + switch (mSourceType) + { + case CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET: + case CONDITION_SOURCE_TYPE_SPELL: + sendErrorMsg = true; + break; + } + + bool refMeets = false; + if (condMeets && refId)//only have to check references if 'this' is met + { + ConditionList ref = sConditionMgr.GetConditionReferences(refId); + refMeets = sConditionMgr.IsPlayerMeetToConditions(player, ref); + }else refMeets = true; + + if (sendErrorMsg && ErrorTextd && (!condMeets || !refMeets))//send special error from DB + player->m_ConditionErrorMsgId = ErrorTextd; + + return condMeets && refMeets; +} + +ConditionMgr::ConditionMgr() +{ +} + +ConditionMgr::~ConditionMgr() +{ +} + +ConditionList ConditionMgr::GetConditionReferences(uint32 refId) +{ + ConditionList conditions; + ConditionReferenceMap::const_iterator ref = m_ConditionReferenceMap.find(refId); + if (ref != m_ConditionReferenceMap.end()) + conditions = (*ref).second; + return conditions; +} + +bool ConditionMgr::IsPlayerMeetToConditionList(Player* player,const ConditionList& conditions, Unit* targetOverride) +{ + std::map<uint32, bool>ElseGroupMap; + for (ConditionList::const_iterator i = conditions.begin(); i != conditions.end(); ++i) + { + sLog.outDebug("ConditionMgr::IsPlayerMeetToConditionList condType: %u val1: %u",(*i)->mConditionType,(*i)->mConditionValue1); + if ((*i)->isLoaded()) + { + std::map<uint32, bool>::const_iterator itr = ElseGroupMap.find((*i)->mElseGroup); + if (itr == ElseGroupMap.end()) + ElseGroupMap[(*i)->mElseGroup] = true; + else if (!(*itr).second) + continue; + + if ((*i)->mReferenceId)//handle reference + { + ConditionReferenceMap::const_iterator ref = m_ConditionReferenceMap.find((*i)->mReferenceId); + if (ref != m_ConditionReferenceMap.end()) + { + if(!IsPlayerMeetToConditionList(player, (*ref).second, targetOverride)) + ElseGroupMap[(*i)->mElseGroup] = false; + }else{ + sLog.outDebug("IsPlayerMeetToConditionList: Reference template -%u not found", (*i)->mReferenceId);//checked at loading, should never happen + } + + } else//handle normal condition + { + if (!(*i)->Meets(player, targetOverride)) + ElseGroupMap[(*i)->mElseGroup] = false; + } + } + } + for (std::map<uint32, bool>::const_iterator i = ElseGroupMap.begin(); i != ElseGroupMap.end(); ++i) + if (i->second) + return true; + return false; +} + +bool ConditionMgr::IsPlayerMeetToConditions(Player* player, ConditionList conditions, Unit* targetOverride) +{ + if (conditions.empty()) + return true; + if(player) + player->m_ConditionErrorMsgId = 0; + + sLog.outDebug("ConditionMgr::IsPlayerMeetToConditions"); + bool result = IsPlayerMeetToConditionList(player, conditions, targetOverride); + + if (player && player->m_ConditionErrorMsgId && player->GetSession()) + player->GetSession()->SendNotification(player->m_ConditionErrorMsgId);//m_ConditionErrorMsgId is set only if a condition was not met + + return result; +} + +ConditionList ConditionMgr::GetConditionsForNotGroupedEntry(ConditionSourceType sType, uint32 uEntry) +{ + ConditionList spellCond; + if (sType > CONDITION_SOURCE_TYPE_NONE && sType < MAX_CONDITIONSOURCETYPE) + { + ConditionMap::const_iterator itr = m_ConditionMap.find(sType); + if (itr != m_ConditionMap.end()) + { + ConditionTypeMap::const_iterator i = (*itr).second.find(uEntry); + if (i != (*itr).second.end()) + { + spellCond = (*i).second; + sLog.outDebug("GetConditionsForNotGroupedEntry: found conditions for type %u and entry %u", uint32(sType), uEntry); + } + } + } + return spellCond; +} + +void ConditionMgr::LoadConditions(bool isReload) +{ + m_ConditionMap.clear(); // for reload case + m_ConditionReferenceMap.clear(); // for reload case + //must clear all custom handled cases (groupped types) before reload + if (isReload) + { + sLog.outString("Reseting Loot Conditions..."); + LootTemplates_Creature.ResetConditions(); + LootTemplates_Fishing.ResetConditions(); + LootTemplates_Gameobject.ResetConditions(); + LootTemplates_Item.ResetConditions(); + LootTemplates_Mail.ResetConditions(); + LootTemplates_Milling.ResetConditions(); + LootTemplates_Pickpocketing.ResetConditions(); + LootTemplates_Reference.ResetConditions(); + LootTemplates_Skinning.ResetConditions(); + LootTemplates_Disenchant.ResetConditions(); + LootTemplates_Prospecting.ResetConditions(); + LootTemplates_Spell.ResetConditions(); + + sLog.outString("Re-Loading `gossip_menu` Table for Conditions!"); + objmgr.LoadGossipMenu(); + + sLog.outString("Re-Loading `gossip_menu_option` Table for Conditions!"); + objmgr.LoadGossipMenuItems(); + } + + uint32 count = 0; + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT SourceTypeOrReferenceId, SourceGroup, SourceEntry, ElseGroup, ConditionTypeOrReference, ConditionValue1, ConditionValue2, ConditionValue3, ErrorTextId FROM conditions"); + + if (!result) + { + barGoLink bar(1); + + bar.step(); + + sLog.outString(); + sLog.outErrorDb(">> Loaded `conditions`, table is empty!"); + return; + } + + barGoLink bar(result->GetRowCount()); + + do + { + bar.step(); + + Field *fields = result->Fetch(); + + Condition* cond = new Condition(); + int32 iSourceTypeOrReferenceId = fields[0].GetInt32(); + cond->mSourceGroup = fields[1].GetUInt32(); + cond->mSourceEntry = fields[2].GetUInt32(); + cond->mElseGroup = fields[3].GetUInt32(); + int32 iConditionTypeOrReference = fields[4].GetInt32(); + cond->mConditionValue1 = fields[5].GetUInt32(); + cond->mConditionValue2 = fields[6].GetUInt32(); + cond->mConditionValue3 = fields[7].GetUInt32(); + cond->ErrorTextd = fields[8].GetUInt32(); + + if (iConditionTypeOrReference >= 0) + cond->mConditionType = ConditionType(iConditionTypeOrReference); + + if (iConditionTypeOrReference < 0)//it has a reference + { + if (iConditionTypeOrReference == iSourceTypeOrReferenceId)//self referencing, skipp + { + sLog.outErrorDb("Condition reference %i is referencing self, skipped", iSourceTypeOrReferenceId); + continue; + } + cond->mReferenceId = uint32(abs(iConditionTypeOrReference)); + + const char* rowType = "reference template"; + if (iSourceTypeOrReferenceId >= 0) + rowType = "reference"; + //check for useless data + if (cond->mConditionValue1) + sLog.outErrorDb("Condition %s %i has useless data in value1 (%u)!", rowType, iSourceTypeOrReferenceId, cond->mConditionValue1); + if (cond->mConditionValue2) + sLog.outErrorDb("Condition %s %i has useless data in value2 (%u)!", rowType, iSourceTypeOrReferenceId, cond->mConditionValue2); + if (cond->mConditionValue3) + sLog.outErrorDb("Condition %s %i has useless data in value3 (%u)!", rowType, iSourceTypeOrReferenceId, cond->mConditionValue3); + if (cond->mSourceGroup && iSourceTypeOrReferenceId < 0) + sLog.outErrorDb("Condition %s %i has useless data in SourceGroup (%u)!", rowType, iSourceTypeOrReferenceId, cond->mSourceGroup); + if (cond->mSourceEntry && iSourceTypeOrReferenceId < 0) + sLog.outErrorDb("Condition %s %i has useless data in SourceEntry (%u)!", rowType, iSourceTypeOrReferenceId, cond->mSourceEntry); + }else if (!isConditionTypeValid(cond))//doesn't have reference, validate ConditionType + continue; + + + if (iSourceTypeOrReferenceId < 0)//it is a reference template + { + uint32 uRefId = abs(iSourceTypeOrReferenceId); + if (m_ConditionReferenceMap.find(uRefId) == m_ConditionReferenceMap.end())//make sure we have a list for our conditions, based on reference id + { + ConditionList mCondList; + m_ConditionReferenceMap[uRefId] = mCondList; + } + m_ConditionReferenceMap[uRefId].push_back(cond);//add to reference storage + count++; + continue; + }//end of reference templates + + cond->mSourceType = ConditionSourceType(iSourceTypeOrReferenceId); + + //if not a reference and SourceType is invalid, skip + if (iConditionTypeOrReference >= 0 && !isSourceTypeValid(cond)) + continue; + + //Grouping is only allowed for some types (loot templates, gossip menus, gossip items) + if (cond->mSourceGroup && !isGroupable(cond->mSourceType)) + { + sLog.outErrorDb("Condition type %u has not allowed grouping %u!", uint32(cond->mSourceType), cond->mSourceGroup); + continue; + }else if (cond->mSourceGroup) + { + bool bIsDone = false; + //handle grouped conditions + switch (cond->mSourceType) + { + case CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Creature.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Disenchant.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Fishing.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Gameobject.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Item.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Mail.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Milling.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Pickpocketing.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Prospecting.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Reference.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Skinning.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE: + bIsDone = addToLootTemplate(cond, LootTemplates_Spell.GetLootForConditionFill(cond->mSourceGroup)); + break; + case CONDITION_SOURCE_TYPE_GOSSIP_MENU: + bIsDone = addToGossipMenus(cond); + break; + case CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION: + bIsDone = addToGossipMenuItems(cond); + break; + } + if (!bIsDone) + sLog.outErrorDb("Not handled grouped condition, SourceGroup %u", cond->mSourceGroup); + else + ++count; + continue; + } + + //handle not grouped conditions + //make sure we have a storage list for our SourceType + if (m_ConditionMap.find(cond->mSourceType) == m_ConditionMap.end()) + { + ConditionTypeMap mTypeMap; + m_ConditionMap[cond->mSourceType] = mTypeMap;//add new empty list for SourceType + } + + //make sure we have a condition list for our SourceType's entry + if (m_ConditionMap[cond->mSourceType].find(cond->mSourceEntry) == m_ConditionMap[cond->mSourceType].end()) + { + ConditionList mCondList; + m_ConditionMap[cond->mSourceType][cond->mSourceEntry] = mCondList; + } + + //add new Condition to storage based on Type/Entry + m_ConditionMap[cond->mSourceType][cond->mSourceEntry].push_back(cond); + ++count; + } + while (result->NextRow()); + + sLog.outString(); + sLog.outString(">> Loaded %u conditions", count); +} + +bool ConditionMgr::addToLootTemplate(Condition* cond, LootTemplate* loot) +{ + if (!loot) + { + sLog.outErrorDb("ConditionMgr: LootTemplate %u not found", cond->mSourceGroup); + return false; + } + if (loot->addConditionItem(cond)) + return true; + sLog.outErrorDb("ConditionMgr: Item %u not found in LootTemplate %u", cond->mSourceEntry, cond->mSourceGroup); + return false; +} + +bool ConditionMgr::addToGossipMenus(Condition* cond) +{ + GossipMenusMapBoundsNonConst pMenuBounds = objmgr.GetGossipMenusMapBoundsNonConst(cond->mSourceGroup); + + if (pMenuBounds.first != pMenuBounds.second) + { + for (GossipMenusMap::iterator itr = pMenuBounds.first; itr != pMenuBounds.second; ++itr) + { + if ((*itr).second.entry == cond->mSourceGroup && (*itr).second.text_id == cond->mSourceEntry) + { + (*itr).second.conditions.push_back(cond); + sLog.outDebug("addToGossipMenus: entry %u textId %u", cond->mSourceGroup, cond->mSourceEntry); + return true; + } + } + } + sLog.outErrorDb("addToGossipMenus: GossipMenu %u not found", cond->mSourceGroup); + return false; +} + +bool ConditionMgr::addToGossipMenuItems(Condition* cond) +{ + GossipMenuItemsMapBoundsNonConst pMenuItemBounds = objmgr.GetGossipMenuItemsMapBoundsNonConst(cond->mSourceGroup); + if (pMenuItemBounds.first != pMenuItemBounds.second) + { + for (GossipMenuItemsMap::iterator itr = pMenuItemBounds.first; itr != pMenuItemBounds.second; ++itr) + { + if ((*itr).second.menu_id == cond->mSourceGroup && (*itr).second.id == cond->mSourceEntry) + { + (*itr).second.conditions.push_back(cond); + //sLog.outDebug("addToGossipMenuItems: menuId %u id %u", cond->mSourceGroup, cond->mSourceEntry); + return true; + } + } + } + sLog.outErrorDb("addToGossipMenuItems: GossipMenuIt %u Item %u not found", cond->mSourceGroup, cond->mSourceEntry); + return false; +} + +bool ConditionMgr::isSourceTypeValid(Condition* cond) +{ + if (cond->mSourceType == CONDITION_SOURCE_TYPE_NONE || cond->mSourceType >= MAX_CONDITIONSOURCETYPE) + { + sLog.outErrorDb("Invalid ConditionSourceType %u in `condition` table, ignoring.", uint32(cond->mSourceType)); + return false; + } + switch (cond->mSourceType) + { + case CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE: + { + if (!LootTemplates_Creature.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `creature_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Creature.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE: + { + if (!LootTemplates_Disenchant.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `disenchant_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Disenchant.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE: + { + if (!LootTemplates_Fishing.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `fishing_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Fishing.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE: + { + if (!LootTemplates_Gameobject.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `gameobject_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Gameobject.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE: + { + if (!LootTemplates_Item.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `item_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Item.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE: + { + if (!LootTemplates_Mail.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `mail_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Mail.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE: + { + if (!LootTemplates_Milling.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `milling_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Milling.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE: + { + if (!LootTemplates_Pickpocketing.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `pickpocketing_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Pickpocketing.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE: + { + if (!LootTemplates_Prospecting.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `prospecting_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Prospecting.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE: + { + if (!LootTemplates_Reference.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `reference_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Reference.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE: + { + if (!LootTemplates_Skinning.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `skinning_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Skinning.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE: + { + if (!LootTemplates_Spell.HaveLootFor(cond->mSourceGroup)) + { + sLog.outErrorDb("SourceGroup %u in `condition` table, does not exist in `spell_loot_template`, ignoring.", cond->mSourceGroup); + return false; + } + LootTemplate* loot = LootTemplates_Spell.GetLootForConditionFill(cond->mSourceGroup); + ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(cond->mSourceEntry); + if (!pItemProto && !loot->isReference(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceType %u, SourceEntry %u in `condition` table, does not exist in `item_template`, ignoring.", cond->mSourceType, cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET: + { + if (cond->mConditionType != CONDITION_SPELL_SCRIPT_TARGET) + { + sLog.outErrorDb("SourceEntry %u in `condition` table, has ConditionType %u. Only CONDITION_SPELL_SCRIPT_TARGET(18) is valid for CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET(14), ignoring.", cond->mSourceEntry, uint32(cond->mConditionType)); + return false; + } + SpellEntry const* spellProto = sSpellStore.LookupEntry(cond->mSourceEntry); + + if (!spellProto) + { + sLog.outErrorDb("SourceEntry %u in `condition` table, does not exist in `spell.dbc`, ignoring.", cond->mSourceEntry); + return false; + } + + bool targetfound = false; + for (uint8 i = 0; i < 3; ++i) + { + if (spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENTRY_SRC || + spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_AREA_ENTRY_SRC || + spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENTRY_DST || + spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_AREA_ENTRY_DST || + spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_NEARBY_ENTRY || + spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_NEARBY_ENTRY || + spellProto->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT_NEARBY_ENTRY || + spellProto->EffectImplicitTargetB[i] == TARGET_GAMEOBJECT_NEARBY_ENTRY || + spellProto->EffectImplicitTargetA[i] == TARGET_DST_NEARBY_ENTRY || + spellProto->EffectImplicitTargetB[i] == TARGET_DST_NEARBY_ENTRY || + spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_CONE_ENTRY || + spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_CONE_ENTRY) + { + targetfound = true; + break; + } + } + if (!targetfound) + { + sLog.outErrorDb("SourceEntry %u in `condition` table does not have any implicit target TARGET_UNIT_NEARBY_ENTRY(38) or TARGET_DST_NEARBY_ENTRY (46)\ + ,TARGET_UNIT_AREA_ENTRY_SRC(7), TARGET_UNIT_AREA_ENTRY_DST(8), TARGET_UNIT_CONE_ENTRY(60), TARGET_GAMEOBJECT_NEARBY_ENTRY(40)",cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE: + { + if (!sCreatureStorage.LookupEntry<CreatureInfo>(cond->mSourceEntry)) + { + sLog.outErrorDb("SourceEntry %u in `condition` table, does not exist in `creature_template`, ignoring.", cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_SPELL: + { + SpellEntry const* spellProto = sSpellStore.LookupEntry(cond->mSourceEntry); + if (!spellProto) + { + sLog.outErrorDb("SourceEntry %u in `condition` table, does not exist in `spell.dbc`, ignoring.", cond->mSourceEntry); + return false; + } + break; + } + case CONDITION_SOURCE_TYPE_ITEM_REQUIRED_TARGET: + { + if (cond->mConditionType != CONDITION_ITEM_TARGET) + { + sLog.outErrorDb("SourceEntry %u in `condition` table, has ConditionType %u. Only CONDITION_ITEM_TARGET(24) is valid for CONDITION_SOURCE_TYPE_ITEM_REQUIRED_TARGET(18), ignoring.", cond->mSourceEntry, uint32(cond->mConditionType)); + return false; + } + ItemPrototype const *pItemProto = objmgr.GetItemPrototype(cond->mSourceEntry); + if (!pItemProto) + { + sLog.outErrorDb("SourceEntry %u in `condition` table, does not exist in `item_tamplate`, ignoring.", cond->mSourceEntry); + return false; + } + bool bIsItemSpellValid = false; + for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + if (SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(pItemProto->Spells[i].SpellId)) + { + if (pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE || + pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) + { + ConditionList conditions = sConditionMgr.GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET, pSpellInfo->Id);//script loading is done before item target loading + if (!conditions.empty()) + break; + + for (int j = 0; j < 3; ++j) + { + if (pSpellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ENEMY || + pSpellInfo->EffectImplicitTargetB[i] == TARGET_UNIT_TARGET_ENEMY || + pSpellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ANY || + pSpellInfo->EffectImplicitTargetB[i] == TARGET_UNIT_TARGET_ANY) + { + bIsItemSpellValid = true; + break; + } + } + if (bIsItemSpellValid) + break; + } + } + } + + if (!bIsItemSpellValid) + { + sLog.outErrorDb("Conditions:ITEM_REQUIRED_TARGET used by item %u does not have implicit target TARGET_CHAIN_DAMAGE(6), TARGET_DUELVSPLAYER(25), already listed in scriptTargets or doesn't have item spelltrigger.", cond->mSourceEntry); + break; + } + break; + } + case CONDITION_SOURCE_TYPE_GOSSIP_MENU: + case CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION: + case CONDITION_SOURCE_TYPE_NONE: + break; + } + return true; +} +bool ConditionMgr::isConditionTypeValid(Condition* cond) +{ + if (cond->mConditionType == CONDITION_NONE || cond->mConditionType >= MAX_CONDITION) + { + sLog.outErrorDb("Invalid ConditionType %u at SourceEntry %u in `condition` table, ignoring.", uint32(cond->mConditionType),cond->mSourceEntry); + return false; + } + switch (cond->mConditionType) + { + case CONDITION_AURA: + { + if (!sSpellStore.LookupEntry(cond->mConditionValue1)) + { + sLog.outErrorDb("Aura condition has non existing spell (Id: %d), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2 > 2) + { + sLog.outErrorDb("Aura condition has non existing effect index (%u) (must be 0..2), skipped", cond->mConditionValue2); + return false; + } + break; + } + case CONDITION_ITEM: + { + ItemPrototype const *proto = objmgr.GetItemPrototype(cond->mConditionValue1); + if (!proto) + { + sLog.outErrorDb("Item condition has non existing item (%u), skipped", cond->mConditionValue1); + return false; + } + if (!cond->mConditionValue2) + { + sLog.outErrorDb("Item condition has 0 set for item count in value2 (%u), skipped", cond->mConditionValue2); + return false; + } + break; + } + case CONDITION_ITEM_EQUIPPED: + { + ItemPrototype const *proto = objmgr.GetItemPrototype(cond->mConditionValue1); + if (!proto) + { + sLog.outErrorDb("ItemEquipped condition has non existing item (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("ItemEquipped condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_ZONEID: + { + AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(cond->mConditionValue1); + if (!areaEntry) + { + sLog.outErrorDb("Zone condition has non existing area (%u), skipped", cond->mConditionValue1); + return false; + } + if (areaEntry->zone != 0) + { + sLog.outErrorDb("Zone condition requires to be in area (%u) which is a subzone but zone expected, skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Zone condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_REPUTATION_RANK: + { + FactionEntry const* factionEntry = sFactionStore.LookupEntry(cond->mConditionValue1); + if (!factionEntry) + { + sLog.outErrorDb("Reputation condition has non existing faction (%u), skipped", cond->mConditionValue1); + return false; + } + break; + } + case CONDITION_TEAM: + { + if (cond->mConditionValue1 != ALLIANCE && cond->mConditionValue1 != HORDE) + { + sLog.outErrorDb("Team condition specifies unknown team (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Team condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_SKILL: + { + SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(cond->mConditionValue1); + if (!pSkill) + { + sLog.outErrorDb("Skill condition specifies non-existing skill (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2 < 1 || cond->mConditionValue2 > sWorld.GetConfigMaxSkillValue()) + { + sLog.outErrorDb("Skill condition specifies invalid skill value (%u), skipped", cond->mConditionValue2); + return false; + } + break; + } + case CONDITION_QUESTREWARDED: + case CONDITION_QUESTTAKEN: + case CONDITION_QUEST_NONE: + { + Quest const *Quest = objmgr.GetQuestTemplate(cond->mConditionValue1); + if (!Quest) + { + sLog.outErrorDb("Quest condition specifies non-existing quest (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_AD_COMMISSION_AURA: + { + if (cond->mConditionValue1) + sLog.outErrorDb("Quest condition has useless data in value1 (%u)!", cond->mConditionValue1); + if (cond->mConditionValue2) + sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_NO_AURA: + { + if (!sSpellStore.LookupEntry(cond->mConditionValue1)) + { + sLog.outErrorDb("Aura condition has non existing spell (Id: %d), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2 > 2) + { + sLog.outErrorDb("Aura condition has non existing effect index (%u) in value2 (must be 0..2), skipped", cond->mConditionValue2); + return false; + } + break; + } + case CONDITION_ACTIVE_EVENT: + { + GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); + if (cond->mConditionValue1 >=events.size() || !events[cond->mConditionValue1].isValid()) + { + sLog.outErrorDb("Active event condition has non existing event id (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Active event condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_ACHIEVEMENT: + { + AchievementEntry const* achievement = GetAchievementStore()->LookupEntry(cond->mConditionValue1); + if (!achievement) + { + sLog.outErrorDb("Achivemen condition has non existing achivement id (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Achivemen condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_CLASS: + { + if (cond->mConditionValue1 >= MAX_CLASSES) + { + sLog.outErrorDb("Class condition has non existing class (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Class condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_RACE: + { + if (cond->mConditionValue1 >= MAX_RACES) + { + sLog.outErrorDb("Race condition has non existing race (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Race condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_SPELL_SCRIPT_TARGET: + { + if (cond->mConditionValue1 >= MAX_SPELL_TARGET_TYPE) + { + sLog.outErrorDb("SpellTarget condition has non existing spell target type (%u), skipped", cond->mConditionValue1); + return false; + } + switch(cond->mConditionValue1) + { + case SPELL_TARGET_TYPE_GAMEOBJECT: + { + if (cond->mConditionValue2 && !sGOStorage.LookupEntry<GameObjectInfo>(cond->mConditionValue2)) + { + sLog.outErrorDb("SpellTarget condition has non existing gameobject (%u) as target, skipped", cond->mConditionValue2); + return false; + } + break; + } + case SPELL_TARGET_TYPE_CONTROLLED: + case SPELL_TARGET_TYPE_CREATURE: + case SPELL_TARGET_TYPE_DEAD: + { + if (cond->mConditionValue2 && !sCreatureStorage.LookupEntry<CreatureInfo>(cond->mConditionValue2)) + { + sLog.outErrorDb("SpellTarget condition has non existing creature template entry (%u) as target, skipped", cond->mConditionValue2); + return false; + } + const CreatureInfo* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(cond->mConditionValue2); + + if (cond->mSourceEntry == 30427 && !cInfo->SkinLootId) + { + sLog.outErrorDb("SpellTarget condition has creature entry %u as a target of spellid 30427, but this creature has no skinlootid. Gas extraction will not work!, skipped", cond->mConditionValue2); + return false; + } + break; + } + } + if (cond->mConditionValue3) + sLog.outErrorDb("SpellTarget condition has useless data in value3 (%u)!", cond->mConditionValue3); + break; + } + case CONDITION_CREATURE_TARGET: + { + if (!cond->mConditionValue1 && !sCreatureStorage.LookupEntry<CreatureInfo>(cond->mConditionValue1)) + { + sLog.outErrorDb("CreatureTarget condition has non existing creature template entry (%u) as target, skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("CreatureTarget condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_TARGET_HEALTH_BELOW_PCT: + { + if (cond->mConditionValue1 > 100) + { + sLog.outErrorDb("TargetHealthBelowPct condition has invalid data in value1 (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("TargetHealthBelowPct condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_TARGET_RANGE: + { + if (cond->mConditionValue2 && cond->mConditionValue2 < cond->mConditionValue1)//maxDist can be 0 for infinit max range + { + sLog.outErrorDb("TargetRange condition has max distance closer then min distance, skipped"); + return false; + } + break; + } + case CONDITION_MAPID: + { + MapEntry const * me = sMapStore.LookupEntry(cond->mConditionValue1); + if (!me) + { + sLog.outErrorDb("Map condition has non existing map (%u), skipped", cond->mConditionValue1); + return false; + } + if (cond->mConditionValue2) + sLog.outErrorDb("Map condition has useless data in value2 (%u)!", cond->mConditionValue2); + break; + } + case CONDITION_ITEM_TARGET: + { + if (!cond->mConditionValue1 || cond->mConditionValue1 > MAX_ITEM_REQ_TARGET_TYPE) + { + sLog.outErrorDb("ItemTarget condition has incorrect target type (%u), skipped", cond->mConditionValue1); + return false; + } + if (!cond->mConditionValue2 && !sCreatureStorage.LookupEntry<CreatureInfo>(cond->mConditionValue2)) + { + sLog.outErrorDb("ItemTarget condition has non existing creature template entry (%u) as target, skipped", cond->mConditionValue2); + return false; + } + if (cond->mConditionValue3) + sLog.outErrorDb("ItemTarget condition has useless data in value3 (%u)!", cond->mConditionValue3); + break; + } + case CONDITION_AREAID: + case CONDITION_INSTANCE_DATA: + break; + } + return true; +} diff --git a/src/game/ConditionMgr.h b/src/game/ConditionMgr.h new file mode 100644 index 00000000000..ebb7d2bb0a6 --- /dev/null +++ b/src/game/ConditionMgr.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> + * + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TRINITY_CONDITIONMGR_H +#define TRINITY_CONDITIONMGR_H + +#include "LootMgr.h" + +class Player; +class Unit; +class LootTemplate; + +enum ConditionType +{ // value1 value2 value3 + CONDITION_NONE = 0, // 0 0 0 always true + CONDITION_AURA = 1, // spell_id effindex +referenceID true if has aura of spell_id with effect effindex + CONDITION_ITEM = 2, // item_id count +referenceID true if has #count of item_ids + CONDITION_ITEM_EQUIPPED = 3, // item_id 0 +referenceID true if has item_id equipped + CONDITION_ZONEID = 4, // zone_id 0 +referenceID true if in zone_id + CONDITION_REPUTATION_RANK = 5, // faction_id min_rank +referenceID true if has min_rank for faction_id + CONDITION_TEAM = 6, // player_team 0, +referenceID 469 - Alliance, 67 - Horde) + CONDITION_SKILL = 7, // skill_id skill_value +referenceID true if has skill_value for skill_id + CONDITION_QUESTREWARDED = 8, // quest_id 0 +referenceID true if quest_id was rewarded before + CONDITION_QUESTTAKEN = 9, // quest_id 0, +referenceID true while quest active + CONDITION_AD_COMMISSION_AURA = 10, // 0 0, +referenceID true while one from AD commission aura active + CONDITION_NO_AURA = 11, // spell_id effindex +referenceID true if does not have aura of spell_id with effect effindex + CONDITION_ACTIVE_EVENT = 12, // event_id 0 +referenceID true if event is active + CONDITION_INSTANCE_DATA = 13, // entry data +referenceID true if data is set in current instance + CONDITION_QUEST_NONE = 14, // quest_id 0 +referenceID true if doesn't have quest saved + CONDITION_CLASS = 15, // class 0 +referenceID true if player's class is equal to class + CONDITION_RACE = 16, // race 0 +referenceID true if player's race is equal to race + CONDITION_ACHIEVEMENT = 17, // achievement_id 0 +referenceID true if achievement is complete + CONDITION_SPELL_SCRIPT_TARGET = 18, // SpellScriptTargetType, TargetEntry, 0 + CONDITION_CREATURE_TARGET = 19, // creature entry 0 +referenceID true if current target is creature with value1 entry + CONDITION_TARGET_HEALTH_BELOW_PCT = 20, // 0-100 0 +referenceID true if target's health is below value1 percent, false if over or no target + CONDITION_TARGET_RANGE = 21, // minDistance maxDist +referenceID true if target is closer then minDist and further then maxDist or if max is 0 then max dist is infinit + CONDITION_MAPID = 22, // map_id 0 +referenceID true if in map_id + CONDITION_AREAID = 23, // area_id 0 +referenceID true if in area_id + CONDITION_ITEM_TARGET = 24 // ItemRequiredTargetType, TargetEntry, 0 +}; + +#define MAX_CONDITION 25 // maximum value in ConditionType enum + +enum ConditionSourceType +{ + CONDITION_SOURCE_TYPE_NONE = 0,//DONE + CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE = 1,//DONE + CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE = 2,//DONE + CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE = 3,//DONE + CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE = 4,//DONE + CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE = 5,//DONE + CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE = 6,//DONE + CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE = 7,//DONE + CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE = 8,//DONE + CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE = 9,//DONE + CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE = 10,//DONE + CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE = 11,//DONE + CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE = 12,//DONE + CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET = 13,//DONE + CONDITION_SOURCE_TYPE_GOSSIP_MENU = 14,//DONE + CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION = 15,//DONE + CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE = 16,//DONE + CONDITION_SOURCE_TYPE_SPELL = 17,//DONE + CONDITION_SOURCE_TYPE_ITEM_REQUIRED_TARGET = 18//DONE +}; + +#define MAX_CONDITIONSOURCETYPE 19 + +struct Condition +{ + ConditionSourceType mSourceType; //SourceTypeOrReferenceId + uint32 mSourceGroup; + uint32 mSourceEntry; + uint32 mElseGroup; + ConditionType mConditionType; //ConditionTypeOrReference + uint32 mConditionValue1; + uint32 mConditionValue2; + uint32 mConditionValue3; + uint32 ErrorTextd; + uint32 mReferenceId; + + Condition() + { + mSourceType = CONDITION_SOURCE_TYPE_NONE; + mSourceGroup = 0; + mSourceEntry = 0; + mElseGroup = 0; + mConditionType = CONDITION_NONE; + mConditionValue1 = 0; + mConditionValue2 = 0; + mConditionValue3 = 0; + mReferenceId = 0; + ErrorTextd = 0; + } + bool Meets(Player * player, Unit* targetOverride = NULL); + bool isLoaded() { return mConditionType > CONDITION_SOURCE_TYPE_NONE || mReferenceId; } +}; + +typedef std::list<Condition*> ConditionList; +typedef std::map<uint32, ConditionList > ConditionTypeMap; +typedef std::map<ConditionSourceType, ConditionTypeMap > ConditionMap;//used for all conditions, except references + +typedef std::map<uint32, ConditionList > ConditionReferenceMap;//only used for references + +class ConditionMgr +{ + public: + ConditionMgr(); + ~ConditionMgr(); + + void LoadConditions(bool isReload = false); + bool isConditionTypeValid(Condition* cond); + ConditionList GetConditionReferences(uint32 refId); + + bool IsPlayerMeetToConditions(Player* player, ConditionList conditions, Unit* targetOverride = NULL); + ConditionList GetConditionsForNotGroupedEntry(ConditionSourceType sType, uint32 uEntry); + + protected: + ConditionMap m_ConditionMap; + ConditionReferenceMap m_ConditionReferenceMap; + + private: + bool isSourceTypeValid(Condition* cond); + bool addToLootTemplate(Condition* cond, LootTemplate* loot); + bool addToGossipMenus(Condition* cond); + bool addToGossipMenuItems(Condition* cond); + bool IsPlayerMeetToConditionList(Player* player,const ConditionList& conditions, Unit* targetOverride = NULL); + + bool isGroupable(ConditionSourceType sourceType) + { + return (sourceType == CONDITION_SOURCE_TYPE_CREATURE_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_DISENCHANT_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_FISHING_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_GAMEOBJECT_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_ITEM_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_MAIL_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_MILLING_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_PICKPOCKETING_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_PROSPECTING_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_REFERENCE_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_SKINNING_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_SPELL_LOOT_TEMPLATE || + sourceType == CONDITION_SOURCE_TYPE_GOSSIP_MENU || + sourceType == CONDITION_SOURCE_TYPE_GOSSIP_MENU_OPTION); + } +}; + +#define sConditionMgr Trinity::Singleton<ConditionMgr>::Instance() + +#endif diff --git a/src/game/CreatureEventAI.cpp b/src/game/CreatureEventAI.cpp index bbb1dde36e7..47c8e9e6ad8 100644 --- a/src/game/CreatureEventAI.cpp +++ b/src/game/CreatureEventAI.cpp @@ -32,6 +32,7 @@ #include "InstanceData.h" #include "SpellMgr.h" #include "CreatureAIImpl.h" +#include "ConditionMgr.h" bool CreatureEventAIHolder::UpdateRepeatTimer(Creature* creature, uint32 repeatMin, uint32 repeatMax) { @@ -1330,8 +1331,12 @@ void CreatureEventAI::ReceiveEmote(Player* pPlayer, uint32 text_emote) if ((*itr).Event.receive_emote.emoteId != text_emote) return; - PlayerCondition pcon((*itr).Event.receive_emote.condition,(*itr).Event.receive_emote.conditionValue1,(*itr).Event.receive_emote.conditionValue2); - if (pcon.Meets(pPlayer)) + Condition* cond = new Condition(); + cond->mConditionType = ConditionType((*itr).Event.receive_emote.condition); + cond->mConditionValue1 = (*itr).Event.receive_emote.conditionValue1; + cond->mConditionValue2 = (*itr).Event.receive_emote.conditionValue2; + + if (cond->Meets(pPlayer)) { sLog.outDebug("CreatureEventAI: ReceiveEmote CreatureEventAI: Condition ok, processing"); ProcessEvent(*itr, pPlayer); diff --git a/src/game/CreatureEventAIMgr.cpp b/src/game/CreatureEventAIMgr.cpp index 4fa7517d848..83d62ca74dc 100644 --- a/src/game/CreatureEventAIMgr.cpp +++ b/src/game/CreatureEventAIMgr.cpp @@ -26,6 +26,7 @@ #include "Policies/SingletonImp.h" #include "ObjectDefines.h" #include "GridDefines.h" +#include "ConditionMgr.h" INSTANTIATE_SINGLETON_1(CreatureEventAIMgr); @@ -359,11 +360,17 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.",temp.creature_id, i, temp.receive_emote.emoteId); continue; } - - if (!PlayerCondition::IsValid(ConditionType(temp.receive_emote.condition), temp.receive_emote.conditionValue1, temp.receive_emote.conditionValue2)) + if (temp.receive_emote.condition) { - sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not valid.",temp.creature_id, i, temp.receive_emote.condition); - continue; + Condition* cond = new Condition(); + cond->mConditionType = ConditionType(temp.receive_emote.condition); + cond->mConditionValue1 = temp.receive_emote.conditionValue1; + cond->mConditionValue2 = temp.receive_emote.conditionValue2; + if (!sConditionMgr.isConditionTypeValid(cond)) + { + sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not valid.",temp.creature_id, i, temp.receive_emote.condition); + continue; + } } if (!(temp.event_flags & EFLAG_REPEATABLE)) diff --git a/src/game/Item.cpp b/src/game/Item.cpp index 4cfd9484f9f..5c2d94d399b 100644 --- a/src/game/Item.cpp +++ b/src/game/Item.cpp @@ -26,6 +26,7 @@ #include "ItemEnchantmentMgr.h" #include "SpellMgr.h" #include "ScriptMgr.h" +#include "ConditionMgr.h" void AddItemsSetItem(Player*player,Item *item) { @@ -824,18 +825,19 @@ bool Item::IsFitToSpellRequirements(SpellEntry const* spellInfo) const bool Item::IsTargetValidForItemUse(Unit* pUnitTarget) { - ItemRequiredTargetMapBounds bounds = objmgr.GetItemRequiredTargetMapBounds(GetProto()->ItemId); - - if (bounds.first == bounds.second) + ConditionList conditions = sConditionMgr.GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_ITEM_REQUIRED_TARGET, GetProto()->ItemId); + if (conditions.empty()) return true; if (!pUnitTarget) return false; - for (ItemRequiredTargetMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr) - if (itr->second.IsFitToRequirements(pUnitTarget)) + for (ConditionList::const_iterator itr = conditions.begin(); itr != conditions.end(); ++itr) + { + ItemRequiredTarget *irt = new ItemRequiredTarget((ItemRequiredTargetType)(*itr)->mConditionValue1, (*itr)->mConditionValue2); + if (irt->IsFitToRequirements(pUnitTarget)) return true; - + } return false; } diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index fe61046897d..49efbe7fde7 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -55,6 +55,7 @@ #include "CreatureEventAIMgr.h" #include "SpellAuraEffects.h" #include "DBCEnums.h" +#include "ConditionMgr.h" bool ChatHandler::HandleAHBotOptionsCommand(const char *args) { @@ -591,6 +592,7 @@ bool ChatHandler::HandleReloadAllLootCommand(const char*) sLog.outString("Re-Loading Loot Tables..."); LoadLootTables(); SendGlobalGMSysMessage("DB tables `*_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -655,7 +657,6 @@ bool ChatHandler::HandleReloadAllSpellCommand(const char*) HandleReloadSpellLinkedSpellCommand("a"); HandleReloadSpellProcEventCommand("a"); HandleReloadSpellBonusesCommand("a"); - HandleReloadSpellScriptTargetCommand("a"); HandleReloadSpellTargetPositionCommand("a"); HandleReloadSpellThreatsCommand("a"); HandleReloadSpellGroupStackRulesCommand("a"); @@ -668,7 +669,6 @@ bool ChatHandler::HandleReloadAllItemCommand(const char*) { HandleReloadPageTextsCommand("a"); HandleReloadItemEnchantementsCommand("a"); - HandleReloadItemRequiredTragetCommand("a"); return true; } @@ -923,6 +923,7 @@ bool ChatHandler::HandleReloadGossipMenuCommand(const char*) sLog.outString("Re-Loading `gossip_menu` Table!"); objmgr.LoadGossipMenu(); SendGlobalGMSysMessage("DB table `gossip_menu` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -931,6 +932,7 @@ bool ChatHandler::HandleReloadGossipMenuOptionCommand(const char*) sLog.outString("Re-Loading `gossip_menu_option` Table!"); objmgr.LoadGossipMenuItems(); SendGlobalGMSysMessage("DB table `gossip_menu_option` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -977,6 +979,7 @@ bool ChatHandler::HandleReloadLootTemplatesCreatureCommand(const char*) LoadLootTemplates_Creature(); LootTemplates_Creature.CheckLootRefs(); SendGlobalGMSysMessage("DB table `creature_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -986,6 +989,7 @@ bool ChatHandler::HandleReloadLootTemplatesDisenchantCommand(const char*) LoadLootTemplates_Disenchant(); LootTemplates_Disenchant.CheckLootRefs(); SendGlobalGMSysMessage("DB table `disenchant_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -995,6 +999,7 @@ bool ChatHandler::HandleReloadLootTemplatesFishingCommand(const char*) LoadLootTemplates_Fishing(); LootTemplates_Fishing.CheckLootRefs(); SendGlobalGMSysMessage("DB table `fishing_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -1004,6 +1009,7 @@ bool ChatHandler::HandleReloadLootTemplatesGameobjectCommand(const char*) LoadLootTemplates_Gameobject(); LootTemplates_Gameobject.CheckLootRefs(); SendGlobalGMSysMessage("DB table `gameobject_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -1013,6 +1019,7 @@ bool ChatHandler::HandleReloadLootTemplatesItemCommand(const char*) LoadLootTemplates_Item(); LootTemplates_Item.CheckLootRefs(); SendGlobalGMSysMessage("DB table `item_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -1022,6 +1029,7 @@ bool ChatHandler::HandleReloadLootTemplatesMillingCommand(const char*) LoadLootTemplates_Milling(); LootTemplates_Milling.CheckLootRefs(); SendGlobalGMSysMessage("DB table `milling_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -1031,6 +1039,7 @@ bool ChatHandler::HandleReloadLootTemplatesPickpocketingCommand(const char*) LoadLootTemplates_Pickpocketing(); LootTemplates_Pickpocketing.CheckLootRefs(); SendGlobalGMSysMessage("DB table `pickpocketing_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -1040,6 +1049,7 @@ bool ChatHandler::HandleReloadLootTemplatesProspectingCommand(const char*) LoadLootTemplates_Prospecting(); LootTemplates_Prospecting.CheckLootRefs(); SendGlobalGMSysMessage("DB table `prospecting_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -1049,6 +1059,7 @@ bool ChatHandler::HandleReloadLootTemplatesMailCommand(const char*) LoadLootTemplates_Mail(); LootTemplates_Mail.CheckLootRefs(); SendGlobalGMSysMessage("DB table `mail_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -1057,6 +1068,7 @@ bool ChatHandler::HandleReloadLootTemplatesReferenceCommand(const char*) sLog.outString("Re-Loading Loot Tables... (`reference_loot_template`)"); LoadLootTemplates_Reference(); SendGlobalGMSysMessage("DB table `reference_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -1066,6 +1078,7 @@ bool ChatHandler::HandleReloadLootTemplatesSkinningCommand(const char*) LoadLootTemplates_Skinning(); LootTemplates_Skinning.CheckLootRefs(); SendGlobalGMSysMessage("DB table `skinning_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -1075,6 +1088,7 @@ bool ChatHandler::HandleReloadLootTemplatesSpellCommand(const char*) LoadLootTemplates_Spell(); LootTemplates_Spell.CheckLootRefs(); SendGlobalGMSysMessage("DB table `spell_loot_template` reloaded."); + sConditionMgr.LoadConditions(true); return true; } @@ -1214,14 +1228,6 @@ bool ChatHandler::HandleReloadSpellBonusesCommand(const char*) return true; } -bool ChatHandler::HandleReloadSpellScriptTargetCommand(const char*) -{ - sLog.outString("Re-Loading SpellsScriptTarget..."); - spellmgr.LoadSpellScriptTarget(); - SendGlobalGMSysMessage("DB table `spell_script_target` (spell targets selection in case specific creature/GO requirements) reloaded."); - return true; -} - bool ChatHandler::HandleReloadSpellTargetPositionCommand(const char*) { sLog.outString("Re-Loading Spell target coordinates..."); @@ -1270,14 +1276,6 @@ bool ChatHandler::HandleReloadItemEnchantementsCommand(const char*) return true; } -bool ChatHandler::HandleReloadItemRequiredTragetCommand(const char*) -{ - sLog.outString("Re-Loading Item Required Targets Table..."); - objmgr.LoadItemRequiredTarget(); - SendGlobalGMSysMessage("DB table `item_required_target` reloaded."); - return true; -} - bool ChatHandler::HandleReloadGameObjectScriptsCommand(const char* arg) { if (sWorld.IsScriptScheduled()) @@ -1546,6 +1544,14 @@ bool ChatHandler::HandleReloadAuctionsCommand(const char * /*args*/) return true; } +bool ChatHandler::HandleReloadConditions(const char* args) +{ + sLog.outString("Re-Loading Conditions..."); + sConditionMgr.LoadConditions(true); + SendGlobalGMSysMessage("Conditions reloaded."); + return true; +} + bool ChatHandler::HandleAccountSetGmLevelCommand(const char *args) { if (!*args) @@ -6687,7 +6693,11 @@ bool ChatHandler::HandleCastCommand(const char *args) SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); if (!spellInfo) + { + PSendSysMessage(LANG_COMMAND_NOSPELLFOUND); + SetSentErrorMessage(true); return false; + } if (!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer())) { @@ -6726,7 +6736,11 @@ bool ChatHandler::HandleCastBackCommand(const char *args) // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form uint32 spell = extractSpellIdFromLink((char*)args); if (!spell || !sSpellStore.LookupEntry(spell)) + { + PSendSysMessage(LANG_COMMAND_NOSPELLFOUND); + SetSentErrorMessage(true); return false; + } char* trig_str = strtok(NULL, " "); if (trig_str) @@ -6757,7 +6771,11 @@ bool ChatHandler::HandleCastDistCommand(const char *args) SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell); if (!spellInfo) + { + PSendSysMessage(LANG_COMMAND_NOSPELLFOUND); + SetSentErrorMessage(true); return false; + } if (!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer())) { @@ -6811,7 +6829,11 @@ bool ChatHandler::HandleCastTargetCommand(const char *args) // number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form uint32 spell = extractSpellIdFromLink((char*)args); if (!spell || !sSpellStore.LookupEntry(spell)) + { + PSendSysMessage(LANG_COMMAND_NOSPELLFOUND); + SetSentErrorMessage(true); return false; + } char* trig_str = strtok(NULL, " "); if (trig_str) diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp index dcbb376208b..2137c8872b5 100644 --- a/src/game/LootMgr.cpp +++ b/src/game/LootMgr.cpp @@ -64,6 +64,9 @@ class LootTemplate::LootGroup // A set of loot def void Verify(LootStore const& lootstore, uint32 id, uint8 group_id) const; void CollectLootIds(LootIdSet& set) const; void CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const; + LootStoreItemList * GetExplicitlyChancedItemList() { return &ExplicitlyChanced; } + LootStoreItemList * GetEqualChancedItemList() { return &EqualChanced; } + void CopyConditions(ConditionList conditions); private: LootStoreItemList ExplicitlyChanced; // Entries with chances defined in DB LootStoreItemList EqualChanced; // Zero chances - every entry takes the same chance @@ -99,8 +102,8 @@ void LootStore::LoadLootTable() sLog.outString("%s :", GetName()); - // 0 1 2 3 4 5 6 7 8 9 - QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT entry, item, ChanceOrQuestChance, lootmode, groupid, mincountOrRef, maxcount, lootcondition, condition_value1, condition_value2 FROM %s",GetName()); + // 0 1 2 3 4 5 6 + QueryResult_AutoPtr result = WorldDatabase.PQuery("SELECT entry, item, ChanceOrQuestChance, lootmode, groupid, mincountOrRef, maxcount FROM %s",GetName()); if (result) { @@ -118,9 +121,6 @@ void LootStore::LoadLootTable() uint8 group = fields[4].GetUInt8(); int32 mincountOrRef = fields[5].GetInt32(); int32 maxcount = fields[6].GetInt32(); - ConditionType condition = (ConditionType)fields[7].GetUInt8(); - uint32 cond_value1 = fields[8].GetUInt32(); - uint32 cond_value2 = fields[9].GetUInt32(); if (maxcount > std::numeric_limits<uint8>::max()) { @@ -128,16 +128,7 @@ void LootStore::LoadLootTable() continue; // error already printed to log/console. } - if (!PlayerCondition::IsValid(condition,cond_value1, cond_value2)) - { - sLog.outErrorDb("... in table '%s' entry %u item %u", GetName(), entry, item); - continue; // error already printed to log/console. - } - - // (condition + cond_value1/2) are converted into single conditionId - uint16 conditionId = objmgr.GetConditionId(condition, cond_value1, cond_value2); - - LootStoreItem storeitem = LootStoreItem(item, chanceOrQuestChance, lootmode, group, conditionId, mincountOrRef, maxcount); + LootStoreItem storeitem = LootStoreItem(item, chanceOrQuestChance, lootmode, group, mincountOrRef, maxcount); if (!storeitem.IsValid(*this,entry)) // Validity checks continue; @@ -195,6 +186,16 @@ bool LootStore::HaveQuestLootForPlayer(uint32 loot_id,Player* player) const return false; } +void LootStore::ResetConditions() +{ + LootTemplateMap m_LootTemplates; + for (LootTemplateMap::iterator itr = m_LootTemplates.begin(); itr != m_LootTemplates.end(); ++itr) + { + ConditionList empty; + (*itr).second->CopyConditions(empty); + } +} + LootTemplate const* LootStore::GetLootFor(uint32 loot_id) const { LootTemplateMap::const_iterator tab = m_LootTemplates.find(loot_id); @@ -205,6 +206,16 @@ LootTemplate const* LootStore::GetLootFor(uint32 loot_id) const return tab->second; } +LootTemplate* LootStore::GetLootForConditionFill(uint32 loot_id) +{ + LootTemplateMap::iterator tab = m_LootTemplates.find(loot_id); + + if (tab == m_LootTemplates.end()) + return NULL; + + return tab->second; +} + void LootStore::LoadAndCollectLootIds(LootIdSet& ids_set) { LoadLootTable(); @@ -316,9 +327,9 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const // Constructor, copies most fields from LootStoreItem and generates random count LootItem::LootItem(LootStoreItem const& li) { - itemid = li.itemid; - conditionId = li.conditionId; - + itemid = li.itemid; + conditions = li.conditions; + ItemPrototype const* proto = objmgr.GetItemPrototype(itemid); freeforall = proto && (proto->Flags & ITEM_FLAGS_PARTY_LOOT); @@ -336,8 +347,8 @@ LootItem::LootItem(LootStoreItem const& li) // Basic checks for player/item compatibility - if false no chance to see the item in the loot bool LootItem::AllowedForPlayer(Player const * player) const { - // DB conditions check - if (!objmgr.IsPlayerMeetToCondition(player,conditionId)) + // DB conditions check + if (!sConditionMgr.IsPlayerMeetToConditions(const_cast<Player*>(player), conditions)) return false; if (needs_quest) @@ -368,7 +379,7 @@ void Loot::AddItem(LootStoreItem const & item) { if (quest_items.size() < MAX_NR_QUEST_ITEMS) quest_items.push_back(LootItem(item)); - } + } else if (items.size() < MAX_NR_LOOT_ITEMS) // Non-quest drop { items.push_back(LootItem(item)); @@ -376,7 +387,7 @@ void Loot::AddItem(LootStoreItem const & item) // non-conditional one-player only items are counted here, // free for all items are counted in FillFFALoot(), // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot() - if (!item.conditionId) + if (item.conditions.empty()) { ItemPrototype const* proto = objmgr.GetItemPrototype(item.itemid); if (!proto || (proto->Flags & ITEM_FLAGS_PARTY_LOOT) == 0) @@ -512,7 +523,7 @@ QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player) for (uint8 i = 0; i < items.size(); ++i) { LootItem &item = items[i]; - if (!item.is_looted && !item.freeforall && item.conditionId && item.AllowedForPlayer(player)) + if (!item.is_looted && !item.freeforall && !item.conditions.empty() && item.AllowedForPlayer(player)) { ql->push_back(QuestItem(i)); if (!item.is_counted) @@ -649,7 +660,7 @@ LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qite } } } - else if (item->conditionId) + else if (!item->conditions.empty()) { QuestItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.find(player->GetGUIDLow()); if (itr != PlayerNonQuestNonFFAConditionalItems.end()) @@ -776,7 +787,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) // blocked rolled items and quest items, and !ffa items for (uint8 i = 0; i < l.items.size(); ++i) { - if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer)) + if (!l.items[i].is_looted && !l.items[i].freeforall && l.items[i].conditions.empty() && l.items[i].AllowedForPlayer(lv.viewer)) { uint8 slot_type; @@ -804,7 +815,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) { for (uint8 i = 0; i < l.items.size(); ++i) { - if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer)) + if (!l.items[i].is_looted && !l.items[i].freeforall && l.items[i].conditions.empty() && l.items[i].AllowedForPlayer(lv.viewer)) { if (l.roundRobinPlayer != 0 && lv.viewer->GetGUID() != l.roundRobinPlayer) // item shall not be displayed. @@ -823,7 +834,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) uint8 slot_type = (lv.permission == MASTER_PERMISSION) ? LOOT_SLOT_TYPE_MASTER : LOOT_SLOT_TYPE_ALLOW_LOOT; for (uint8 i = 0; i < l.items.size(); ++i) { - if (!l.items[i].is_looted && !l.items[i].freeforall && !l.items[i].conditionId && l.items[i].AllowedForPlayer(lv.viewer)) + if (!l.items[i].is_looted && !l.items[i].freeforall && l.items[i].conditions.empty() && l.items[i].AllowedForPlayer(lv.viewer)) { b << uint8(i) << l.items[i]; b << uint8(slot_type); @@ -954,6 +965,18 @@ bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const * player) const return false; } +void LootTemplate::LootGroup::CopyConditions(ConditionList conditions) +{ + for (LootStoreItemList::iterator i = ExplicitlyChanced.begin(); i != ExplicitlyChanced.end(); ++i) + { + i->conditions = conditions; + } + for (LootStoreItemList::iterator i = EqualChanced.begin(); i != EqualChanced.end(); ++i) + { + i->conditions = conditions; + } +} + // Rolls an item from the group (if any takes its chance) and adds the item to the loot void LootTemplate::LootGroup::Process(Loot& loot, uint16 lootMode) const { @@ -1078,7 +1101,7 @@ void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint } } -void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& /*store*/, LootIdSet* ref_set) const +void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const { for (LootStoreItemList::const_iterator ieItr=ExplicitlyChanced.begin(); ieItr != ExplicitlyChanced.end(); ++ieItr) { @@ -1120,6 +1143,15 @@ void LootTemplate::AddEntry(LootStoreItem& item) Entries.push_back(item); } +void LootTemplate::CopyConditions(ConditionList conditions) +{ + for (LootStoreItemList::iterator i = Entries.begin(); i != Entries.end(); ++i) + i->conditions = conditions; + + for (LootGroups::iterator i = Groups.begin(); i != Groups.end(); ++i) + i->CopyConditions(conditions); +} + // Rolls for every item in the template and adds the rolled items the the loot void LootTemplate::Process(Loot& loot, LootStore const& store, bool rate, uint16 lootMode, uint8 groupId) const { @@ -1165,6 +1197,8 @@ void LootTemplate::Process(Loot& loot, LootStore const& store, bool rate, uint16 if (!Referenced) continue; // Error message already printed at loading stage + const_cast<LootTemplate*>(Referenced)->CopyConditions(i->conditions);//copy conditions from referer template + for (uint32 loop = 0; loop < i->maxcount; ++loop) // Ref multiplicator Referenced->Process(loot, store, rate, lootMode, i->group); } @@ -1268,6 +1302,66 @@ void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_se for (LootGroups::const_iterator grItr = Groups.begin(); grItr != Groups.end(); ++grItr) grItr->CheckLootRefs(store,ref_set); } +bool LootTemplate::addConditionItem(Condition* cond) +{ + if (!cond || !cond->isLoaded())//should never happen, checked at loading + { + sLog.outError("LootTemplate::addConditionItem: condition is null"); + return false; + } + if (!Entries.empty()) + { + for (LootStoreItemList::iterator i = Entries.begin(); i != Entries.end(); ++i) + { + if (i->itemid == cond->mSourceEntry) + { + i->conditions.push_back(cond); + return true; + } + } + } + if (!Groups.empty()) + { + for (LootGroups::iterator groupItr = Groups.begin(); groupItr != Groups.end(); ++groupItr) + { + LootStoreItemList* itemList = (*groupItr).GetExplicitlyChancedItemList(); + if (!itemList->empty()) + { + for (LootStoreItemList::iterator i = itemList->begin(); i != itemList->end(); ++i) + { + if ((*i).itemid == cond->mSourceEntry) + { + (*i).conditions.push_back(cond); + return true; + } + } + } + itemList = (*groupItr).GetEqualChancedItemList(); + if (!itemList->empty()) + { + for (LootStoreItemList::iterator i = itemList->begin(); i != itemList->end(); ++i) + { + if ((*i).itemid == cond->mSourceEntry) + { + (*i).conditions.push_back(cond); + return true; + } + } + } + } + } + return false; +} + +bool LootTemplate::isReference(uint32 id) +{ + for (LootStoreItemList::const_iterator ieItr = Entries.begin(); ieItr != Entries.end(); ++ieItr) + { + if (ieItr->itemid == id && ieItr->mincountOrRef < 0) + return true; + } + return false;//not found or not reference +} void LoadLootTemplates_Creature() { diff --git a/src/game/LootMgr.h b/src/game/LootMgr.h index 1f443f47fb9..65fe3de22e1 100644 --- a/src/game/LootMgr.h +++ b/src/game/LootMgr.h @@ -25,6 +25,7 @@ #include "ByteBuffer.h" #include "Utilities/LinkedReference/RefManager.h" #include "SharedDefines.h" +#include "ConditionMgr.h" #include <map> #include <vector> @@ -98,6 +99,7 @@ enum LootSlotType class Player; class LootStore; +class ConditionMgr; struct LootStoreItem { @@ -108,13 +110,13 @@ struct LootStoreItem uint8 group :7; bool needs_quest :1; // quest drop (negative ChanceOrQuestChance in DB) uint8 maxcount :8; // max drop count for the item (mincountOrRef positive) or Ref multiplicator (mincountOrRef negative) - uint16 conditionId :16; // additional loot condition Id + ConditionList conditions; // additional loot condition // Constructor, converting ChanceOrQuestChance -> (chance, needs_quest) // displayid is filled in IsValid() which must be called after - LootStoreItem(uint32 _itemid, float _chanceOrQuestChance, uint16 _lootmode, uint8 _group, uint8 _conditionId, int32 _mincountOrRef, uint8 _maxcount) + LootStoreItem(uint32 _itemid, float _chanceOrQuestChance, uint16 _lootmode, uint8 _group, int32 _mincountOrRef, uint8 _maxcount) : itemid(_itemid), chance(fabs(_chanceOrQuestChance)), mincountOrRef(_mincountOrRef), lootmode(_lootmode), - group(_group), needs_quest(_chanceOrQuestChance < 0), maxcount(_maxcount), conditionId(_conditionId) + group(_group), needs_quest(_chanceOrQuestChance < 0), maxcount(_maxcount) {} bool Roll(bool rate) const; // Checks if the entry takes it's chance (at loot generation) @@ -127,7 +129,7 @@ struct LootItem uint32 itemid; uint32 randomSuffix; int32 randomPropertyId; - uint16 conditionId :16; // allow compiler pack structure + ConditionList conditions; // additional loot condition uint8 count : 8; bool is_looted : 1; bool is_blocked : 1; @@ -186,6 +188,8 @@ class LootStore bool HaveQuestLootForPlayer(uint32 loot_id,Player* player) const; LootTemplate const* GetLootFor(uint32 loot_id) const; + void ResetConditions(); + LootTemplate* GetLootForConditionFill(uint32 loot_id); char const* GetName() const { return m_name; } char const* GetEntryName() const { return m_entryName; } @@ -210,6 +214,7 @@ class LootTemplate void AddEntry(LootStoreItem& item); // Rolls for every item in the template and adds the rolled items the the loot void Process(Loot& loot, LootStore const& store, bool rate, uint16 lootMode, uint8 groupId = 0) const; + void CopyConditions(ConditionList conditions); // True if template includes at least 1 quest drop entry bool HasQuestDrop(LootTemplateMap const& store, uint8 groupId = 0) const; @@ -219,6 +224,9 @@ class LootTemplate // Checks integrity of the template void Verify(LootStore const& store, uint32 Id) const; void CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_set) const; + bool addConditionItem(Condition* cond); + bool isReference(uint32 id); + private: LootStoreItemList Entries; // not grouped only LootGroups Groups; // groups have own (optimised) processing, grouped entries go there @@ -355,6 +363,7 @@ extern LootStore LootTemplates_Item; extern LootStore LootTemplates_Mail; extern LootStore LootTemplates_Milling; extern LootStore LootTemplates_Pickpocketing; +extern LootStore LootTemplates_Reference; extern LootStore LootTemplates_Skinning; extern LootStore LootTemplates_Disenchant; extern LootStore LootTemplates_Prospecting; diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index c826c0ea441..ce86c2a4e77 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -44,7 +44,6 @@ #include "SpellAuras.h" #include "Util.h" #include "WaypointManager.h" -#include "InstanceData.h" //for condition_instance_data #include "GossipDef.h" #include "Vehicle.h" #include "AchievementMgr.h" @@ -182,9 +181,6 @@ ObjectMgr::ObjectMgr() m_guildId = 1; m_arenaTeamId = 1; m_auctionid = 1; - - // Only zero condition left, others will be added while loading DB tables - mConditions.resize(1); } ObjectMgr::~ObjectMgr() @@ -2427,107 +2423,6 @@ void ObjectMgr::LoadVehicleAccessories() sLog.outString(">> Loaded %u Vehicle Accessories", count); } -void ObjectMgr::LoadItemRequiredTarget() -{ - m_ItemRequiredTarget.clear(); // needed for reload case - - uint32 count = 0; - - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,type,targetEntry FROM item_required_target"); - - if (!result) - { - barGoLink bar(1); - - bar.step(); - - sLog.outString(); - sLog.outErrorDb(">> Loaded 0 ItemRequiredTarget. DB table `item_required_target` is empty."); - return; - } - - barGoLink bar(result->GetRowCount()); - - do - { - Field *fields = result->Fetch(); - bar.step(); - - uint32 uiItemId = fields[0].GetUInt32(); - uint32 uiType = fields[1].GetUInt32(); - uint32 uiTargetEntry = fields[2].GetUInt32(); - - ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(uiItemId); - - if (!pItemProto) - { - sLog.outErrorDb("Table `item_required_target`: Entry %u listed for TargetEntry %u does not exist in `item_template`.",uiItemId,uiTargetEntry); - continue; - } - - bool bIsItemSpellValid = false; - - for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - { - if (SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(pItemProto->Spells[i].SpellId)) - { - if (pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE || - pItemProto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) - { - SpellScriptTargetBounds bounds = spellmgr.GetSpellScriptTargetBounds(pSpellInfo->Id); - if (bounds.first != bounds.second) - break; - - for (int j = 0; j < 3; ++j) - { - if (pSpellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ENEMY || - pSpellInfo->EffectImplicitTargetB[i] == TARGET_UNIT_TARGET_ENEMY || - pSpellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ANY || - pSpellInfo->EffectImplicitTargetB[i] == TARGET_UNIT_TARGET_ANY) - { - bIsItemSpellValid = true; - break; - } - } - if (bIsItemSpellValid) - break; - } - } - } - - if (!bIsItemSpellValid) - { - sLog.outErrorDb("Table `item_required_target`: Spell used by item %u does not have implicit target TARGET_CHAIN_DAMAGE(6), TARGET_DUELVSPLAYER(25), already listed in `spell_script_target` or doesn't have item spelltrigger.",uiItemId); - continue; - } - - if (!uiType || uiType > MAX_ITEM_REQ_TARGET_TYPE) - { - sLog.outErrorDb("Table `item_required_target`: Type %u for TargetEntry %u is incorrect.",uiType,uiTargetEntry); - continue; - } - - if (!uiTargetEntry) - { - sLog.outErrorDb("Table `item_required_target`: TargetEntry == 0 for Type (%u).",uiType); - continue; - } - - if (!sCreatureStorage.LookupEntry<CreatureInfo>(uiTargetEntry)) - { - sLog.outErrorDb("Table `item_required_target`: creature template entry %u does not exist.",uiTargetEntry); - continue; - } - - m_ItemRequiredTarget.insert(ItemRequiredTargetMap::value_type(uiItemId,ItemRequiredTarget(ItemRequiredTargetType(uiType),uiTargetEntry))); - - ++count; - } while (result->NextRow()); - - sLog.outString(); - sLog.outString(">> Loaded %u Item required targets", count); -} - void ObjectMgr::LoadPetLevelInfo() { // Loading levels data @@ -7631,28 +7526,6 @@ void ObjectMgr::LoadFishingBaseSkillLevel() sLog.outString(">> Loaded %u areas for fishing base skill level", count); } -// Searches for the same condition already in Conditions store -// Returns Id if found, else adds it to Conditions and returns Id -uint16 ObjectMgr::GetConditionId(ConditionType condition, uint32 value1, uint32 value2) -{ - PlayerCondition lc = PlayerCondition(condition, value1, value2); - for (uint16 i=0; i < mConditions.size(); ++i) - { - if (lc == mConditions[i]) - return i; - } - - mConditions.push_back(lc); - - if (mConditions.size() > 0xFFFF) - { - sLog.outError("Conditions store overflow! Current and later loaded conditions will ignored!"); - return 0; - } - - return mConditions.size() - 1; -} - bool ObjectMgr::CheckDeclinedNames(std::wstring mainpart, DeclinedName const& names) { for (uint8 i =0; i < MAX_DECLINED_NAME_CASES; ++i) @@ -7675,225 +7548,6 @@ uint32 ObjectMgr::GetAreaTriggerScriptId(uint32 trigger_id) return 0; } -// Checks if player meets the condition -bool PlayerCondition::Meets(Player const * player) const -{ - if (!player) - return false; // player not present, return false - - switch (condition) - { - case CONDITION_NONE: - return true; // empty condition, always met - case CONDITION_AURA: - return player->HasAuraEffect(value1, value2); - case CONDITION_ITEM: - return player->HasItemCount(value1, value2); - case CONDITION_ITEM_EQUIPPED: - return player->HasItemOrGemWithIdEquipped(value1,1); - case CONDITION_ZONEID: - return player->GetZoneId() == value1; - case CONDITION_REPUTATION_RANK: - { - FactionEntry const* faction = sFactionStore.LookupEntry(value1); - return faction && uint32(player->GetReputationMgr().GetRank(faction)) >= int32(value2); - } - case CONDITION_ACHIEVEMENT: - { - AchievementEntry const* achievement = GetAchievementStore()->LookupEntry(value1); - return player->GetAchievementMgr().HasAchieved(achievement); - } - case CONDITION_TEAM: - return player->GetTeam() == value1; - case CONDITION_CLASS: - return player->getClass() == value1; - case CONDITION_RACE: - return player->getRace() == value1; - case CONDITION_SKILL: - return player->HasSkill(value1) && player->GetBaseSkillValue(value1) >= value2; - case CONDITION_QUESTREWARDED: - return player->GetQuestRewardStatus(value1); - case CONDITION_QUESTTAKEN: - { - QuestStatus status = player->GetQuestStatus(value1); - return (status == QUEST_STATUS_INCOMPLETE); - } - case CONDITION_QUEST_NONE: - { - QuestStatus status = player->GetQuestStatus(value1); - return (status == QUEST_STATUS_NONE); - } - case CONDITION_AD_COMMISSION_AURA: - { - Unit::AuraApplicationMap const& auras = player->GetAppliedAuras(); - for (Unit::AuraApplicationMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) - if ((itr->second->GetBase()->GetSpellProto()->Attributes & 0x1000010) && itr->second->GetBase()->GetSpellProto()->SpellVisual[0] == 3580) - return true; - return false; - } - case CONDITION_NO_AURA: - return !player->HasAuraEffect(value1, value2); - case CONDITION_ACTIVE_EVENT: - return gameeventmgr.IsActiveEvent(value1); - case CONDITION_INSTANCE_DATA: - { - Map *map = player->GetMap(); - if (map && map->IsDungeon() && ((InstanceMap*)map)->GetInstanceData()) - return ((InstanceMap*)map)->GetInstanceData()->GetData(value1) == value2; - } - default: - return false; - } -} - -// Verification of condition values validity -bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 value2) -{ - if (condition >= MAX_CONDITION) // Wrong condition type - { - sLog.outErrorDb("Condition has bad type of %u, skipped ", condition); - return false; - } - - switch (condition) - { - case CONDITION_AURA: - { - if (!sSpellStore.LookupEntry(value1)) - { - sLog.outErrorDb("Aura condition requires to have non existing spell (Id: %d), skipped", value1); - return false; - } - if (value2 > 2) - { - sLog.outErrorDb("Aura condition requires to have non existing effect index (%u) (must be 0..2), skipped", value2); - return false; - } - break; - } - case CONDITION_ITEM: - { - ItemPrototype const *proto = objmgr.GetItemPrototype(value1); - if (!proto) - { - sLog.outErrorDb("Item condition requires to have non existing item (%u), skipped", value1); - return false; - } - break; - } - case CONDITION_ITEM_EQUIPPED: - { - ItemPrototype const *proto = objmgr.GetItemPrototype(value1); - if (!proto) - { - sLog.outErrorDb("ItemEquipped condition requires to have non existing item (%u) equipped, skipped", value1); - return false; - } - break; - } - case CONDITION_ZONEID: - { - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(value1); - if (!areaEntry) - { - sLog.outErrorDb("Zone condition requires to be in non existing area (%u), skipped", value1); - return false; - } - if (areaEntry->zone != 0) - { - sLog.outErrorDb("Zone condition requires to be in area (%u) which is a subzone but zone expected, skipped", value1); - return false; - } - break; - } - case CONDITION_REPUTATION_RANK: - { - FactionEntry const* factionEntry = sFactionStore.LookupEntry(value1); - if (!factionEntry) - { - sLog.outErrorDb("Reputation condition requires to have reputation non existing faction (%u), skipped", value1); - return false; - } - break; - } - case CONDITION_TEAM: - { - if (value1 != ALLIANCE && value1 != HORDE) - { - sLog.outErrorDb("Team condition specifies unknown team (%u), skipped", value1); - return false; - } - break; - } - case CONDITION_SKILL: - { - SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(value1); - if (!pSkill) - { - sLog.outErrorDb("Skill condition specifies non-existing skill (%u), skipped", value1); - return false; - } - if (value2 < 1 || value2 > sWorld.GetConfigMaxSkillValue()) - { - sLog.outErrorDb("Skill condition specifies invalid skill value (%u), skipped", value2); - return false; - } - break; - } - case CONDITION_QUESTREWARDED: - case CONDITION_QUESTTAKEN: - { - Quest const *Quest = objmgr.GetQuestTemplate(value1); - if (!Quest) - { - sLog.outErrorDb("Quest condition specifies non-existing quest (%u), skipped", value1); - return false; - } - if (value2) - sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", value2); - break; - } - case CONDITION_AD_COMMISSION_AURA: - { - if (value1) - sLog.outErrorDb("Quest condition has useless data in value1 (%u)!", value1); - if (value2) - sLog.outErrorDb("Quest condition has useless data in value2 (%u)!", value2); - break; - } - case CONDITION_NO_AURA: - { - if (!sSpellStore.LookupEntry(value1)) - { - sLog.outErrorDb("Aura condition requires to have non existing spell (Id: %d), skipped", value1); - return false; - } - if (value2 > 2) - { - sLog.outErrorDb("Aura condition requires to have non existing effect index (%u) (must be 0..2), skipped", value2); - return false; - } - break; - } - case CONDITION_ACTIVE_EVENT: - { - GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap(); - if (value1 >=events.size() || !events[value1].isValid()) - { - sLog.outErrorDb("Active event condition requires existed event id (%u), skipped", value1); - return false; - } - break; - } - case CONDITION_INSTANCE_DATA: - //TODO: need some check - break; - case CONDITION_NONE: - break; - } - return true; -} - SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial) { switch(pSkill->categoryId) @@ -8423,8 +8077,7 @@ void ObjectMgr::LoadGossipMenu() { m_mGossipMenusMap.clear(); - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, text_id, " - "cond_1, cond_1_val_1, cond_1_val_2, cond_2, cond_2_val_1, cond_2_val_2 FROM gossip_menu"); + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry, text_id FROM gossip_menu"); if (!result) { @@ -8450,14 +8103,7 @@ void ObjectMgr::LoadGossipMenu() GossipMenus gMenu; gMenu.entry = fields[0].GetUInt32(); - gMenu.text_id = fields[1].GetUInt32(); - - ConditionType cond_1 = (ConditionType)fields[2].GetUInt32(); - uint32 cond_1_val_1 = fields[3].GetUInt32(); - uint32 cond_1_val_2 = fields[4].GetUInt32(); - ConditionType cond_2 = (ConditionType)fields[5].GetUInt32(); - uint32 cond_2_val_1 = fields[6].GetUInt32(); - uint32 cond_2_val_2 = fields[7].GetUInt32(); + gMenu.text_id = fields[1].GetUInt32(); if (!GetGossipText(gMenu.text_id)) { @@ -8465,21 +8111,6 @@ void ObjectMgr::LoadGossipMenu() continue; } - if (!PlayerCondition::IsValid(cond_1, cond_1_val_1, cond_1_val_2)) - { - sLog.outErrorDb("Table gossip_menu entry %u, invalid condition 1 for id %u", gMenu.entry, gMenu.text_id); - continue; - } - - if (!PlayerCondition::IsValid(cond_2, cond_2_val_1, cond_2_val_2)) - { - sLog.outErrorDb("Table gossip_menu entry %u, invalid condition 2 for id %u", gMenu.entry, gMenu.text_id); - continue; - } - - gMenu.cond_1 = GetConditionId(cond_1, cond_1_val_1, cond_1_val_2); - gMenu.cond_2 = GetConditionId(cond_2, cond_2_val_1, cond_2_val_2); - m_mGossipMenusMap.insert(GossipMenusMap::value_type(gMenu.entry, gMenu)); ++count; @@ -8496,10 +8127,7 @@ void ObjectMgr::LoadGossipMenuItems() QueryResult_AutoPtr result = WorldDatabase.Query( "SELECT menu_id, id, option_icon, option_text, option_id, npc_option_npcflag, " - "action_menu_id, action_poi_id, action_script_id, box_coded, box_money, box_text, " - "cond_1, cond_1_val_1, cond_1_val_2, " - "cond_2, cond_2_val_1, cond_2_val_2, " - "cond_3, cond_3_val_1, cond_3_val_2 " + "action_menu_id, action_poi_id, action_script_id, box_coded, box_money, box_text " "FROM gossip_menu_option"); if (!result) @@ -8543,32 +8171,6 @@ void ObjectMgr::LoadGossipMenuItems() gMenuItem.box_money = fields[10].GetUInt32(); gMenuItem.box_text = fields[11].GetCppString(); - ConditionType cond_1 = (ConditionType)fields[12].GetUInt32(); - uint32 cond_1_val_1 = fields[13].GetUInt32(); - uint32 cond_1_val_2 = fields[14].GetUInt32(); - ConditionType cond_2 = (ConditionType)fields[15].GetUInt32(); - uint32 cond_2_val_1 = fields[16].GetUInt32(); - uint32 cond_2_val_2 = fields[17].GetUInt32(); - ConditionType cond_3 = (ConditionType)fields[18].GetUInt32(); - uint32 cond_3_val_1 = fields[19].GetUInt32(); - uint32 cond_3_val_2 = fields[20].GetUInt32(); - - if (!PlayerCondition::IsValid(cond_1, cond_1_val_1, cond_1_val_2)) - { - sLog.outErrorDb("Table gossip_menu_option menu %u, invalid condition 1 for id %u", gMenuItem.menu_id, gMenuItem.id); - continue; - } - if (!PlayerCondition::IsValid(cond_2, cond_2_val_1, cond_2_val_2)) - { - sLog.outErrorDb("Table gossip_menu_option menu %u, invalid condition 2 for id %u", gMenuItem.menu_id, gMenuItem.id); - continue; - } - if (!PlayerCondition::IsValid(cond_3, cond_3_val_1, cond_3_val_2)) - { - sLog.outErrorDb("Table gossip_menu_option menu %u, invalid condition 3 for id %u", gMenuItem.menu_id, gMenuItem.id); - continue; - } - if (gMenuItem.option_icon >= GOSSIP_ICON_MAX) { sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u has unknown icon id %u. Replacing with GOSSIP_ICON_CHAT", gMenuItem.menu_id, gMenuItem.id, gMenuItem.option_icon); @@ -8601,10 +8203,6 @@ void ObjectMgr::LoadGossipMenuItems() gossipScriptSet.erase(gMenuItem.action_script_id); } - gMenuItem.cond_1 = GetConditionId(cond_1, cond_1_val_1, cond_1_val_2); - gMenuItem.cond_2 = GetConditionId(cond_2, cond_2_val_1, cond_2_val_2); - gMenuItem.cond_3 = GetConditionId(cond_3, cond_3_val_1, cond_3_val_2); - m_mGossipMenuItemsMap.insert(GossipMenuItemsMap::value_type(gMenuItem.menu_id, gMenuItem)); ++count; diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index 2586d987a05..79b6ffdd0eb 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -41,10 +41,11 @@ #include "Policies/Singleton.h" #include "Database/SQLStorage.h" #include "Vehicle.h" - +#include "ObjectMgr.h" #include <string> #include <map> #include <limits> +#include "ConditionMgr.h" extern SQLStorage sCreatureStorage; extern SQLStorage sCreatureDataAddonStorage; @@ -235,23 +236,22 @@ struct GossipMenuItems bool box_coded; uint32 box_money; std::string box_text; - uint16 cond_1; - uint16 cond_2; - uint16 cond_3; + ConditionList conditions; }; struct GossipMenus { uint32 entry; uint32 text_id; - uint16 cond_1; - uint16 cond_2; + ConditionList conditions; }; typedef std::multimap<uint32,GossipMenus> GossipMenusMap; typedef std::pair<GossipMenusMap::const_iterator, GossipMenusMap::const_iterator> GossipMenusMapBounds; +typedef std::pair<GossipMenusMap::iterator, GossipMenusMap::iterator> GossipMenusMapBoundsNonConst; typedef std::multimap<uint32,GossipMenuItems> GossipMenuItemsMap; typedef std::pair<GossipMenuItemsMap::const_iterator, GossipMenuItemsMap::const_iterator> GossipMenuItemsMapBounds; +typedef std::pair<GossipMenuItemsMap::iterator, GossipMenuItemsMap::iterator> GossipMenuItemsMapBoundsNonConst; struct QuestPOIPoint { @@ -300,48 +300,6 @@ struct GraveYardData }; typedef std::multimap<uint32,GraveYardData> GraveYardMap; -enum ConditionType -{ // value1 value2 for the Condition enumed - CONDITION_NONE = 0, // 0 0 - CONDITION_AURA = 1, // spell_id effindex - CONDITION_ITEM = 2, // item_id count - CONDITION_ITEM_EQUIPPED = 3, // item_id 0 - CONDITION_ZONEID = 4, // zone_id 0 - CONDITION_REPUTATION_RANK = 5, // faction_id min_rank - CONDITION_TEAM = 6, // player_team 0, (469 - Alliance 67 - Horde) - CONDITION_SKILL = 7, // skill_id skill_value - CONDITION_QUESTREWARDED = 8, // quest_id 0 - CONDITION_QUESTTAKEN = 9, // quest_id 0, for condition true while quest active. - CONDITION_AD_COMMISSION_AURA = 10, // 0 0, for condition true while one from AD commission aura active - CONDITION_NO_AURA = 11, // spell_id effindex - CONDITION_ACTIVE_EVENT = 12, // event_id - CONDITION_INSTANCE_DATA = 13, // entry data - CONDITION_QUEST_NONE = 14, // quest_id 0 - CONDITION_CLASS = 15, // class 0 - CONDITION_RACE = 16, // race 0 - CONDITION_ACHIEVEMENT = 17 // achievement_id 0 -}; - -#define MAX_CONDITION 18 // maximum value in ConditionType enum - -struct PlayerCondition -{ - ConditionType condition; // additional condition type - uint32 value1; // data for the condition - see ConditionType definition - uint32 value2; - - PlayerCondition(uint8 _condition = 0, uint32 _value1 = 0, uint32 _value2 = 0) - : condition(ConditionType(_condition)), value1(_value1), value2(_value2) {} - - static bool IsValid(ConditionType condition, uint32 value1, uint32 value2); - // Checks correctness of values - bool Meets(Player const * APlayer) const; // Checks if the player meets the condition - bool operator == (PlayerCondition const& lc) const - { - return (lc.condition == condition && lc.value1 == value1 && lc.value2 == value2); - } -}; - // NPC gossip text id typedef UNORDERED_MAP<uint32, uint32> CacheNpcTextIdMap; @@ -648,7 +606,6 @@ class ObjectMgr void LoadGameobjects(); void LoadGameobjectRespawnTimes(); void LoadItemPrototypes(); - void LoadItemRequiredTarget(); void LoadItemLocales(); void LoadQuestLocales(); void LoadNpcTextLocales(); @@ -880,15 +837,6 @@ class ObjectMgr int GetIndexForLocale(LocaleConstant loc); LocaleConstant GetLocaleForIndex(int i); - uint16 GetConditionId(ConditionType condition, uint32 value1, uint32 value2); - bool IsPlayerMeetToCondition(Player const* player, uint16 condition_id) const - { - if (condition_id >= mConditions.size()) - return false; - - return mConditions[condition_id].Meets(player); - } - GameTele const* GetGameTele(uint32 id) const { GameTeleMap::const_iterator itr = m_GameTeleMap.find(id); @@ -942,11 +890,6 @@ class ObjectMgr return SpellClickInfoMapBounds(mSpellClickInfoMap.lower_bound(creature_id),mSpellClickInfoMap.upper_bound(creature_id)); } - ItemRequiredTargetMapBounds GetItemRequiredTargetMapBounds(uint32 uiItemEntry) const - { - return ItemRequiredTargetMapBounds(m_ItemRequiredTarget.lower_bound(uiItemEntry),m_ItemRequiredTarget.upper_bound(uiItemEntry)); - } - GM_Ticket *GetGMTicket(uint64 ticketGuid) { for (GmTicketList::const_iterator i = m_GMTicketList.begin(); i != m_GMTicketList.end(); ++i) @@ -969,10 +912,19 @@ class ObjectMgr return GossipMenusMapBounds(m_mGossipMenusMap.lower_bound(uiMenuId),m_mGossipMenusMap.upper_bound(uiMenuId)); } + GossipMenusMapBoundsNonConst GetGossipMenusMapBoundsNonConst(uint32 uiMenuId) + { + return GossipMenusMapBoundsNonConst(m_mGossipMenusMap.lower_bound(uiMenuId),m_mGossipMenusMap.upper_bound(uiMenuId)); + } + GossipMenuItemsMapBounds GetGossipMenuItemsMapBounds(uint32 uiMenuId) const { return GossipMenuItemsMapBounds(m_mGossipMenuItemsMap.lower_bound(uiMenuId),m_mGossipMenuItemsMap.upper_bound(uiMenuId)); } + GossipMenuItemsMapBoundsNonConst GetGossipMenuItemsMapBoundsNonConst(uint32 uiMenuId) + { + return GossipMenuItemsMapBoundsNonConst(m_mGossipMenuItemsMap.lower_bound(uiMenuId),m_mGossipMenuItemsMap.upper_bound(uiMenuId)); + } void AddOrUpdateGMTicket(GM_Ticket &ticket, bool create = false); void _AddOrUpdateGMTicket(GM_Ticket &ticket); @@ -1109,10 +1061,6 @@ class ObjectMgr RespawnTimes mCreatureRespawnTimes; RespawnTimes mGORespawnTimes; - // Storage for Conditions. First element (index 0) is reserved for zero-condition (nothing required) - typedef std::vector<PlayerCondition> ConditionStore; - ConditionStore mConditions; - CacheNpcTextIdMap m_mCacheNpcTextIdMap; CacheVendorItemMap m_mCacheVendorItemMap; CacheTrainerSpellMap m_mCacheTrainerSpellMap; diff --git a/src/game/Player.cpp b/src/game/Player.cpp index c2ee45e73d1..43965f5646a 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -66,7 +66,7 @@ #include "AchievementMgr.h" #include "SpellAuras.h" #include "SpellAuraEffects.h" - +#include "ConditionMgr.h" #include <cmath> #define ZONE_UPDATE_INTERVAL (1*IN_MILISECONDS) @@ -503,6 +503,8 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa m_powerFraction[i] = 0; m_globalCooldowns.clear(); + + m_ConditionErrorMsgId = 0; } Player::~Player () @@ -13302,14 +13304,7 @@ void Player::PrepareGossipMenu(WorldObject *pSource, uint32 menuId, bool showQue for (GossipMenuItemsMap::const_iterator itr = pMenuItemBounds.first; itr != pMenuItemBounds.second; ++itr) { bool bCanTalk = true; - - if (itr->second.cond_1 && !objmgr.IsPlayerMeetToCondition(this, itr->second.cond_1)) - continue; - - if (itr->second.cond_2 && !objmgr.IsPlayerMeetToCondition(this, itr->second.cond_2)) - continue; - - if (itr->second.cond_3 && !objmgr.IsPlayerMeetToCondition(this, itr->second.cond_3)) + if (!sConditionMgr.IsPlayerMeetToConditions(this, itr->second.conditions)) continue; if (pSource->GetTypeId() == TYPEID_UNIT) @@ -13643,7 +13638,7 @@ uint32 Player::GetGossipTextId(uint32 menuId) for (GossipMenusMap::const_iterator itr = pMenuBounds.first; itr != pMenuBounds.second; ++itr) { - if (objmgr.IsPlayerMeetToCondition(this, itr->second.cond_1) && objmgr.IsPlayerMeetToCondition(this, itr->second.cond_2)) + if (sConditionMgr.IsPlayerMeetToConditions(this, itr->second.conditions)) textId = itr->second.text_id; } @@ -17439,8 +17434,8 @@ bool Player::CheckInstanceLoginValid() if (GetMap()->IsRaid()) { - // cannot be in raid instance without a raid group - if (!GetGroup() || !GetGroup()->isRaidGroup()) + // cannot be in raid instance without a group + if (!GetGroup()) return false; } else diff --git a/src/game/Player.h b/src/game/Player.h index 0166eb2b23d..af8c5fb971d 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -2135,6 +2135,8 @@ class Player : public Unit, public GridObject<Player> void SetHomebind(WorldLocation const& loc, uint32 area_id); + uint32 m_ConditionErrorMsgId; + // Homebind coordinates uint32 m_homebindMapId; uint16 m_homebindAreaId; diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 303cfc5ffc7..b039767c463 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -51,6 +51,7 @@ #include "Vehicle.h" #include "SpellAuraEffects.h" #include "ScriptMgr.h" +#include "ConditionMgr.h" #define SPELL_CHANNEL_UPDATE_INTERVAL (1 * IN_MILISECONDS) @@ -1683,10 +1684,10 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType) { case SPELL_TARGETS_ENTRY: { - SpellScriptTargetBounds bounds = spellmgr.GetSpellScriptTargetBounds(m_spellInfo->Id); - if (bounds.first == bounds.second) + ConditionList conditions = sConditionMgr.GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET, m_spellInfo->Id); + if (conditions.empty()) { - sLog.outDebug("Spell (ID: %u) (caster Entry: %u) does not have record in `spell_script_target`", m_spellInfo->Id, m_caster->GetEntry()); + sLog.outDebug("Spell (ID: %u) (caster Entry: %u) does not have record in `conditions` for spell script target (ConditionSourceType 14)", m_spellInfo->Id, m_caster->GetEntry()); if (IsPositiveSpell(m_spellInfo->Id)) return SearchNearbyTarget(range, SPELL_TARGETS_ALLY); else @@ -1696,13 +1697,15 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType) Creature* creatureScriptTarget = NULL; GameObject* goScriptTarget = NULL; - for (SpellScriptTarget::const_iterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) + for (ConditionList::const_iterator i_spellST = conditions.begin(); i_spellST != conditions.end(); ++i_spellST) { - switch(i_spellST->second.type) + if ((*i_spellST)->mConditionType != CONDITION_SPELL_SCRIPT_TARGET) + continue; + switch((*i_spellST)->mConditionValue1) { case SPELL_TARGET_TYPE_CONTROLLED: for (Unit::ControlList::iterator itr = m_caster->m_Controlled.begin(); itr != m_caster->m_Controlled.end(); ++itr) - if ((*itr)->GetEntry() == i_spellST->second.targetEntry && (*itr)->IsWithinDistInMap(m_caster, range)) + if ((*itr)->GetEntry() == (*i_spellST)->mConditionValue2 && (*itr)->IsWithinDistInMap(m_caster, range)) { goScriptTarget = NULL; creatureScriptTarget = (*itr)->ToCreature(); @@ -1710,9 +1713,9 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType) } break; case SPELL_TARGET_TYPE_GAMEOBJECT: - if (i_spellST->second.targetEntry) + if ((*i_spellST)->mConditionValue2) { - if (GameObject *go = m_caster->FindNearestGameObject(i_spellST->second.targetEntry, range)) + if (GameObject *go = m_caster->FindNearestGameObject((*i_spellST)->mConditionValue2, range)) { // remember found target and range, next attempt will find more near target with another entry goScriptTarget = go; @@ -1732,11 +1735,11 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType) } break; case SPELL_TARGET_TYPE_CREATURE: - if (m_targets.getUnitTarget() && m_targets.getUnitTarget()->GetEntry() == i_spellST->second.targetEntry) + if (m_targets.getUnitTarget() && m_targets.getUnitTarget()->GetEntry() == (*i_spellST)->mConditionValue2) return m_targets.getUnitTarget(); case SPELL_TARGET_TYPE_DEAD: default: - if (Creature *cre = m_caster->FindNearestCreature(i_spellST->second.targetEntry, range, i_spellST->second.type != SPELL_TARGET_TYPE_DEAD)) + if (Creature *cre = m_caster->FindNearestCreature((*i_spellST)->mConditionValue2, range, (*i_spellST)->mConditionValue1 != SPELL_TARGET_TYPE_DEAD)) { creatureScriptTarget = cre; goScriptTarget = NULL; @@ -2249,8 +2252,8 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur) std::list<Unit*> unitList; if (targetType == SPELL_TARGETS_ENTRY) { - SpellScriptTargetBounds bounds = spellmgr.GetSpellScriptTargetBounds(m_spellInfo->Id); - if (bounds.first == bounds.second) + ConditionList conditions = sConditionMgr.GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL_SCRIPT_TARGET, m_spellInfo->Id); + if (conditions.empty()) { // Custom entries // TODO: move these to sql @@ -2321,14 +2324,16 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur) // let it be done in one check? else { - for (SpellScriptTarget::const_iterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST) + for (ConditionList::const_iterator i_spellST = conditions.begin(); i_spellST != conditions.end(); ++i_spellST) { - if (i_spellST->second.type == SPELL_TARGET_TYPE_CREATURE) - SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENTRY, i_spellST->second.targetEntry); - else if (i_spellST->second.type == SPELL_TARGET_TYPE_CONTROLLED) + if ((*i_spellST)->mConditionType != CONDITION_SPELL_SCRIPT_TARGET) + continue; + if ((*i_spellST)->mConditionValue1 == SPELL_TARGET_TYPE_CREATURE) + SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENTRY, (*i_spellST)->mConditionValue2); + else if ((*i_spellST)->mConditionValue1 == SPELL_TARGET_TYPE_CONTROLLED) { for (Unit::ControlList::iterator itr = m_caster->m_Controlled.begin(); itr != m_caster->m_Controlled.end(); ++itr) - if ((*itr)->GetEntry() == i_spellST->second.targetEntry && + if ((*itr)->GetEntry() == (*i_spellST)->mConditionValue2 && /*(*itr)->IsWithinDistInMap(m_caster, radius)*/ (*itr)->IsInMap(m_caster)) // For 60243 and 52173 need skip radius check or use range (no radius entry for effect) unitList.push_back(*itr); } @@ -2545,7 +2550,20 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const * triggere return; } } - + if (m_caster->ToPlayer()) + { + //check for special spell conditions + ConditionList conditions = sConditionMgr.GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_SPELL, m_spellInfo->Id); + if (!conditions.empty()) + { + if (!sConditionMgr.IsPlayerMeetToConditions(m_caster->ToPlayer(), conditions)) + { + SendCastResult(SPELL_FAILED_DONT_REPORT); + finish(false); + return; + } + } + } if (!m_targets.HasSrc() && m_spellInfo->Targets & TARGET_FLAG_SOURCE_LOCATION) m_targets.setSrc(m_caster); @@ -2607,7 +2625,6 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const * triggere finish(false); return; } - if (m_caster->GetTypeId() == TYPEID_PLAYER) { if (objmgr.IsPlayerSpellDisabled(m_spellInfo->Id)) diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index 0feac0ef110..510fdccb98a 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -1972,174 +1972,6 @@ void SpellMgr::LoadSpellLearnSpells() sLog.outString(">> Loaded %u spell learn spells + %u found in DBC", count, dbc_count); } -void SpellMgr::LoadSpellScriptTarget() -{ - mSpellScriptTarget.clear(); // need for reload case - - uint32 count = 0; - - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT entry,type,targetEntry FROM spell_script_target"); - - if (!result) - { - barGoLink bar(1); - - bar.step(); - - sLog.outString(); - sLog.outErrorDb(">> Loaded 0 SpellScriptTarget. DB table `spell_script_target` is empty."); - return; - } - - barGoLink bar(result->GetRowCount()); - - do - { - Field *fields = result->Fetch(); - bar.step(); - - uint32 spellId = fields[0].GetUInt32(); - uint32 type = fields[1].GetUInt32(); - uint32 targetEntry = fields[2].GetUInt32(); - - SpellEntry const* spellProto = sSpellStore.LookupEntry(spellId); - - if (!spellProto) - { - sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not exist.",spellId,targetEntry); - continue; - } - - bool targetfound = false; - for (uint8 i = 0; i < 3; ++i) - { - if (spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENTRY_SRC || - spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_AREA_ENTRY_SRC || - spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENTRY_DST || - spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_AREA_ENTRY_DST || - spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_NEARBY_ENTRY || - spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_NEARBY_ENTRY || - spellProto->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT_NEARBY_ENTRY || - spellProto->EffectImplicitTargetB[i] == TARGET_GAMEOBJECT_NEARBY_ENTRY || - spellProto->EffectImplicitTargetA[i] == TARGET_DST_NEARBY_ENTRY || - spellProto->EffectImplicitTargetB[i] == TARGET_DST_NEARBY_ENTRY || - spellProto->EffectImplicitTargetA[i] == TARGET_UNIT_CONE_ENTRY || - spellProto->EffectImplicitTargetB[i] == TARGET_UNIT_CONE_ENTRY) - { - targetfound = true; - break; - } - } - if (!targetfound) - { - sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not have any implicit target TARGET_UNIT_NEARBY_ENTRY(38) or TARGET_DST_NEARBY_ENTRY (46)\ - ,TARGET_UNIT_AREA_ENTRY_SRC(7), TARGET_UNIT_AREA_ENTRY_DST(8), TARGET_UNIT_CONE_ENTRY(60), TARGET_GAMEOBJECT_NEARBY_ENTRY(40)",spellId,targetEntry); - continue; - } - - if (type >= MAX_SPELL_TARGET_TYPE) - { - sLog.outErrorDb("Table `spell_script_target`: target type %u for TargetEntry %u is incorrect.",type,targetEntry); - continue; - } - - switch(type) - { - case SPELL_TARGET_TYPE_GAMEOBJECT: - { - if (targetEntry == 0) - break; - - if (!sGOStorage.LookupEntry<GameObjectInfo>(targetEntry)) - { - sLog.outErrorDb("Table `spell_script_target`: gameobject template entry %u does not exist.", targetEntry); - continue; - } - break; - } - case SPELL_TARGET_TYPE_CONTROLLED: - if (targetEntry == 0) - sLog.outErrorDb("Table `spell_script_target`: creature template entry %u does not exist.", targetEntry); - default: - { - //players - /*if (targetEntry == 0) - { - sLog.outErrorDb("Table `spell_script_target`: target entry == 0 for not GO target type (%u).",type); - continue; - }*/ - if (targetEntry && !sCreatureStorage.LookupEntry<CreatureInfo>(targetEntry)) - { - sLog.outErrorDb("Table `spell_script_target`: creature template entry %u does not exist.", targetEntry); - continue; - } - const CreatureInfo* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(targetEntry); - - if (spellId == 30427 && !cInfo->SkinLootId) - { - sLog.outErrorDb("Table `spell_script_target` has creature %u as a target of spellid 30427, but this creature has no skinlootid. Gas extraction will not work!", cInfo->Entry); - continue; - } - break; - } - } - - mSpellScriptTarget.insert(SpellScriptTarget::value_type(spellId, SpellTargetEntry(SpellScriptTargetType(type), targetEntry))); - - ++count; - } while (result->NextRow()); - - // Check all spells - for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) - { - SpellEntry const * spellInfo = sSpellStore.LookupEntry(i); - if (!spellInfo) - continue; - - bool found = false; - for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) - { - switch (spellInfo->EffectImplicitTargetA[j]) - { - case TARGET_UNIT_AREA_ENTRY_SRC: - case TARGET_UNIT_AREA_ENTRY_DST: - case TARGET_UNIT_NEARBY_ENTRY: - case TARGET_GAMEOBJECT_NEARBY_ENTRY: - case TARGET_DST_NEARBY_ENTRY: - case TARGET_UNIT_CONE_ENTRY: - found = true; - break; - } - if (found) - break; - - switch (spellInfo->EffectImplicitTargetB[j]) - { - case TARGET_UNIT_AREA_ENTRY_SRC: - case TARGET_UNIT_AREA_ENTRY_DST: - case TARGET_UNIT_NEARBY_ENTRY: - case TARGET_GAMEOBJECT_NEARBY_ENTRY: - case TARGET_DST_NEARBY_ENTRY: - case TARGET_UNIT_CONE_ENTRY: - found = true; - break; - } - - if (found) - break; - } - if (found) - { - SpellScriptTargetBounds bounds = spellmgr.GetSpellScriptTargetBounds(i); - //if (bounds.first == bounds.second) - //sLog.outDebug("Spell (ID: %u) does not have record in `spell_script_target`", i); - } - } - - sLog.outString(); - sLog.outString(">> Loaded %u Spell Script Targets", count); -} - void SpellMgr::LoadSpellPetAuras() { mSpellPetAuraMap.clear(); // need for reload case diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index 94121d4610d..224760029e7 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -685,9 +685,6 @@ struct SpellTargetEntry uint32 targetEntry; }; -typedef std::multimap<uint32,SpellTargetEntry> SpellScriptTarget; -typedef std::pair<SpellScriptTarget::const_iterator,SpellScriptTarget::const_iterator> SpellScriptTargetBounds; - // coordinates for spells (accessed using SpellMgr functions) struct SpellTargetPosition { @@ -1216,12 +1213,6 @@ class SpellMgr bool IsSkillBonusSpell(uint32 spellId) const; bool IsSkillTypeSpell(uint32 spellId, SkillType type) const; - // Spell script targets - SpellScriptTargetBounds GetSpellScriptTargetBounds(uint32 spell_id) const - { - return SpellScriptTargetBounds(mSpellScriptTarget.lower_bound(spell_id),mSpellScriptTarget.upper_bound(spell_id)); - } - // Spell correctess for client using static bool IsSpellValid(SpellEntry const * spellInfo, Player* pl = NULL, bool msg = true); @@ -1356,7 +1347,6 @@ class SpellMgr void LoadSpellRequired(); void LoadSpellLearnSkills(); void LoadSpellLearnSpells(); - void LoadSpellScriptTarget(); void LoadSpellGroups(); void LoadSpellProcEvents(); void LoadSpellBonusess(); @@ -1377,7 +1367,6 @@ class SpellMgr bool _isPositiveSpell(uint32 spellId, bool deep) const; bool _isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) const; - SpellScriptTarget mSpellScriptTarget; SpellChainMap mSpellChains; SpellsRequiringSpellMap mSpellsReqSpell; SpellRequiredMap mSpellReq; diff --git a/src/game/World.cpp b/src/game/World.cpp index 04f0c46a7d5..1629aa61af9 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -70,6 +70,7 @@ #include "ScriptMgr.h" #include "AddonMgr.h" #include "LFGMgr.h" +#include "ConditionMgr.h" INSTANTIATE_SINGLETON_1(World); @@ -1380,12 +1381,6 @@ void World::SetInitialWorldSettings() sLog.outString("Loading Creature templates..."); objmgr.LoadCreatureTemplates(); - sLog.outString("Loading SpellsScriptTarget..."); - spellmgr.LoadSpellScriptTarget(); // must be after LoadCreatureTemplates and LoadGameobjectInfo - - sLog.outString("Loading ItemRequiredTarget..."); - objmgr.LoadItemRequiredTarget(); - sLog.outString("Loading Creature Reputation OnKill Data..."); objmgr.LoadReputationOnKill(); @@ -1578,6 +1573,9 @@ void World::SetInitialWorldSettings() sLog.outString("Loading Creature Formations..."); formation_mgr.LoadCreatureFormations(); + sLog.outString("Loading Conditions..."); + sConditionMgr.LoadConditions(); + sLog.outString("Loading GM tickets..."); objmgr.LoadGMTickets(); |