/* * Copyright (C) 2005-2011 MaNGOS * Copyright (C) 2009-2011 MaNGOSZero * Copyright (C) 2017-2018 Light's Hope * * 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 "Unit.h" #include "Player.h" #include "GameEventMgr.h" #include "SpellAuras.h" #include "InstanceData.h" #include "HardcodedEvents.h" #include "World.h" char const* conditionSourceToStr[] = { "loot system", "referencing loot", "gossip menu", "gossip menu option", "EventAI", "hardcoded", "vendor", "spell_area", "scripted map event", "script action" }; // Stores what params need to be provided to each condition type. // (source, target, map) uint8 const ConditionTargetsInternal[] = { CONDITION_REQ_NONE, // -3 CONDITION_REQ_NONE, // -2 CONDITION_REQ_NONE, // -1 CONDITION_REQ_NONE, // 0 CONDITION_REQ_TARGET_UNIT, // 1 CONDITION_REQ_TARGET_PLAYER, // 2 CONDITION_REQ_TARGET_PLAYER, // 3 CONDITION_REQ_ANY_WORLDOBJECT, // 4 CONDITION_REQ_TARGET_PLAYER, // 5 CONDITION_REQ_TARGET_PLAYER, // 6 CONDITION_REQ_TARGET_PLAYER, // 7 CONDITION_REQ_TARGET_PLAYER, // 8 CONDITION_REQ_TARGET_PLAYER, // 9 CONDITION_REQ_TARGET_PLAYER, // 10 CONDITION_REQ_NONE, // 11 CONDITION_REQ_NONE, // 12 CONDITION_REQ_SOURCE_CREATURE, // 13 CONDITION_REQ_TARGET_PLAYER, // 14 CONDITION_REQ_TARGET_UNIT, // 15 CONDITION_REQ_SOURCE_WORLDOBJECT, // 16 CONDITION_REQ_TARGET_PLAYER, // 17 CONDITION_REQ_MAP_OR_WORLDOBJECT, // 18 CONDITION_REQ_TARGET_PLAYER, // 19 CONDITION_REQ_TARGET_WORLDOBJECT, // 20 CONDITION_REQ_TARGET_WORLDOBJECT, // 21 CONDITION_REQ_TARGET_PLAYER, // 22 CONDITION_REQ_TARGET_PLAYER, // 23 CONDITION_REQ_NONE, // 24 CONDITION_REQ_NONE, // 25 CONDITION_REQ_NONE, // 26 CONDITION_REQ_TARGET_WORLDOBJECT, // 27 CONDITION_REQ_TARGET_WORLDOBJECT, // 28 CONDITION_REQ_TARGET_PLAYER, // 29 CONDITION_REQ_TARGET_PLAYER, // 30 CONDITION_REQ_SOURCE_WORLDOBJECT, // 31 CONDITION_REQ_SOURCE_CREATURE, // 32 CONDITION_REQ_MAP_OR_WORLDOBJECT, // 33 CONDITION_REQ_MAP_OR_WORLDOBJECT, // 34 CONDITION_REQ_MAP_OR_WORLDOBJECT, // 35 CONDITION_REQ_MAP_OR_WORLDOBJECT, // 36 CONDITION_REQ_BOTH_WORLDOBJECTS, // 37 CONDITION_REQ_BOTH_WORLDOBJECTS, // 38 CONDITION_REQ_TARGET_WORLDOBJECT, // 39 CONDITION_REQ_TARGET_UNIT, // 40 CONDITION_REQ_TARGET_UNIT, // 41 CONDITION_REQ_TARGET_UNIT, // 42 CONDITION_REQ_TARGET_UNIT, // 43 CONDITION_REQ_BOTH_UNITS, // 44 CONDITION_REQ_TARGET_PLAYER, // 45 CONDITION_REQ_TARGET_UNIT, // 46 CONDITION_REQ_MAP_OR_WORLDOBJECT, // 47 CONDITION_REQ_TARGET_GAMEOBJECT, // 48 CONDITION_REQ_TARGET_GAMEOBJECT, // 49 }; // Starts from 4th element so that -3 will return first element. uint8 const* ConditionTargets = &ConditionTargetsInternal[3]; // Checks if player meets the condition bool ConditionEntry::Meets(WorldObject const* target, Map const* map, WorldObject const* source, ConditionSource conditionSourceType) const { DEBUG_LOG("Condition-System: Check condition %u, type %i - called from %s with params target: %s, map %i, source %s", m_entry, m_condition, conditionSourceToStr[conditionSourceType], target ? target->GetGuidStr().c_str() : "", map ? map->GetId() : -1, source ? source->GetGuidStr().c_str() : ""); if (m_flags & CONDITION_FLAG_SWAP_TARGETS) std::swap(source, target); if (!CheckParamRequirements(target, map, source)) { sLog.outErrorDb("CONDITION %u type %u used with bad parameters, called from %s, used with target: %s, map %i, source %s", m_entry, m_condition, conditionSourceToStr[conditionSourceType], target ? target->GetGuidStr().c_str() : "NULL", map ? map->GetId() : -1, source ? source->GetGuidStr().c_str() : "NULL"); return false; } bool result = Evaluate(target, map, source, conditionSourceType); if (m_flags & CONDITION_FLAG_REVERSE_RESULT) result = !result; return result; } // Actual evaluation of the condition done here. bool inline ConditionEntry::Evaluate(WorldObject const* target, Map const* map, WorldObject const* source, ConditionSource conditionSourceType) const { switch (m_condition) { case CONDITION_NOT: { // Checked on load return !sConditionStorage.LookupEntry(m_value1)->Meets(target, map, source, conditionSourceType); } case CONDITION_OR: { // Checked on load return sConditionStorage.LookupEntry(m_value1)->Meets(target, map, source, conditionSourceType) || sConditionStorage.LookupEntry(m_value2)->Meets(target, map, source, conditionSourceType); } case CONDITION_AND: { // Checked on load return sConditionStorage.LookupEntry(m_value1)->Meets(target, map, source, conditionSourceType) && sConditionStorage.LookupEntry(m_value2)->Meets(target, map, source, conditionSourceType); } case CONDITION_NONE: { return true; // empty condition, always met } case CONDITION_AURA: { return target->ToUnit()->HasAura(m_value1, SpellEffectIndex(m_value2)); } case CONDITION_ITEM: { return target->ToPlayer()->HasItemCount(m_value1, m_value2); } case CONDITION_ITEM_EQUIPPED: { return target->ToPlayer()->HasItemWithIdEquipped(m_value1, 1); } case CONDITION_AREAID: { uint32 zone, area; WorldObject const* searcher = source ? source : target; searcher->GetZoneAndAreaId(zone, area); return (zone == m_value1 || area == m_value1); } case CONDITION_REPUTATION_RANK_MIN: { FactionEntry const* faction = sObjectMgr.GetFactionEntry(m_value1); return (target->ToPlayer()->GetReputationMgr().GetRank(faction) >= ReputationRank(m_value2)); } case CONDITION_TEAM: { return (uint32(target->ToPlayer()->GetTeam()) == m_value1); } case CONDITION_SKILL: { Player const* pPlayer = target->ToPlayer(); return (pPlayer->HasSkill(m_value1) && pPlayer->GetSkillValueBase(m_value1) >= m_value2); } case CONDITION_QUESTREWARDED: { return target->ToPlayer()->GetQuestRewardStatus(m_value1); } case CONDITION_QUESTTAKEN: { return target->ToPlayer()->IsCurrentQuest(m_value1, m_value2); } case CONDITION_AD_COMMISSION_AURA: { Unit::SpellAuraHolderMap const& auras = target->ToPlayer()->GetSpellAuraHolderMap(); for (Unit::SpellAuraHolderMap::const_iterator itr = auras.begin(); itr != auras.end(); ++itr) if ((itr->second->GetSpellProto()->Attributes & SPELL_ATTR_CASTABLE_WHILE_MOUNTED || itr->second->GetSpellProto()->Attributes & SPELL_ATTR_IS_ABILITY) && itr->second->GetSpellProto()->SpellVisual == 3580) return true; return false; } case CONDITION_WAR_EFFORT_STAGE: { uint32 stage = sObjectMgr.GetSavedVariable(VAR_WE_STAGE, 0); switch (m_value2) { case 0: return stage == m_value1; case 1: return stage >= m_value1; case 2: return stage <= m_value1; } return false; } case CONDITION_ACTIVE_GAME_EVENT: { return sGameEventMgr.IsActiveEvent(m_value1); } case CONDITION_RACE_CLASS: { Player const* pPlayer = target->ToPlayer(); if ((!m_value1 || (pPlayer->getRaceMask() & m_value1)) && (!m_value2 || (pPlayer->getClassMask() & m_value2))) return true; return false; } case CONDITION_LEVEL: { Unit const* pTarget = target->ToUnit(); switch (m_value2) { case 0: return pTarget->getLevel() == m_value1; case 1: return pTarget->getLevel() >= m_value1; case 2: return pTarget->getLevel() <= m_value1; } return false; } case CONDITION_SOURCE_ENTRY: { return source->GetEntry() == m_value1; } case CONDITION_SPELL: { Player const* pPlayer = target->ToPlayer(); switch (m_value2) { case 0: return pPlayer->HasSpell(m_value1); case 1: return !pPlayer->HasSpell(m_value1); } return false; } case CONDITION_INSTANCE_SCRIPT: { Player const* pPlayer = target ? target->ToPlayer() : nullptr; if (!map) map = pPlayer ? pPlayer->GetMap() : source->GetMap(); if (InstanceData const* data = map->GetInstanceData()) return data->CheckConditionCriteriaMeet(pPlayer, m_value1, source, m_value2); return false; } case CONDITION_QUESTAVAILABLE: { return target->ToPlayer()->CanTakeQuest(sObjectMgr.GetQuestTemplate(m_value1), false); } case CONDITION_NEARBY_CREATURE: { return (bool)(target->FindNearestCreature(m_value1, m_value2)); } case CONDITION_NEARBY_GAMEOBJECT: { return (bool)(target->FindNearestGameObject(m_value1, m_value2)); } case CONDITION_QUEST_NONE: { Player const* pPlayer = target->ToPlayer(); if (!pPlayer->IsCurrentQuest(m_value1) && !pPlayer->GetQuestRewardStatus(m_value1)) return true; return false; } case CONDITION_ITEM_WITH_BANK: { return target->ToPlayer()->HasItemCount(m_value1, m_value2, true); } case CONDITION_WOW_PATCH: { switch (m_value2) { case 0: return sWorld.GetWowPatch() == m_value1; case 1: return sWorld.GetWowPatch() >= m_value1; case 2: return sWorld.GetWowPatch() <= m_value1; } return false; } case CONDITION_ESCORT: { const Creature* pSource = ToCreature(source); const Player* pTarget = ToPlayer(target); if (m_value1 & CF_ESCORT_SOURCE_DEAD) if (!pSource || pSource->isDead()) return true; if (m_value1 & CF_ESCORT_TARGET_DEAD) if (!pTarget || pTarget->isDead() || !pTarget->IsInWorld()) return true; if (m_value2) if (!pSource || !pTarget || !pSource->IsWithinDistInMap(pTarget, m_value2)) return true; return false; } case CONDITION_ACTIVE_HOLIDAY: { return sGameEventMgr.IsActiveHoliday(HolidayIds(m_value1)); } case CONDITION_GENDER: { return target->getGender() == m_value1; } case CONDITION_SKILL_BELOW: { Player const* pPlayer = target->ToPlayer(); if (m_value2 == 1) return !pPlayer->HasSkill(m_value1); else return pPlayer->HasSkill(m_value1) && pPlayer->GetSkillValueBase(m_value1) < m_value2; } case CONDITION_REPUTATION_RANK_MAX: { FactionEntry const* faction = sObjectMgr.GetFactionEntry(m_value1); return (target->ToPlayer()->GetReputationMgr().GetRank(faction) <= ReputationRank(m_value2)); } case CONDITION_HAS_FLAG: { if (m_value1 <= OBJECT_FIELD_ENTRY || m_value1 >= source->GetValuesCount()) { sLog.outError("CONDITION_HAS_FLAG call for wrong field %u (max count: %u) in object (TypeId: %u).", m_value1, source->GetValuesCount(), source->GetTypeId()); return false; } return source->HasFlag(m_value1, m_value2); } case CONDITION_LAST_WAYPOINT: { uint32 lastReachedWp = ((Creature*)source)->GetMotionMaster()->getLastReachedWaypoint(); switch (m_value2) { case 0: return m_value1 == lastReachedWp; case 1: return m_value1 >= lastReachedWp; case 2: return m_value1 <= lastReachedWp; } return false; } case CONDITION_MAP_ID: { uint32 mapId = map ? map->GetId() : (source ? source->GetMapId() : target->GetMapId()); if (mapId == m_value1) return true; return false; } case CONDITION_INSTANCE_DATA: { const Map* pMap = map ? map : (source ? source->GetMap() : target->GetMap()); if (InstanceData const* data = map->GetInstanceData()) { switch (m_value3) { case 0: return const_cast(data)->GetData(m_value1) == m_value2; case 1: return const_cast(data)->GetData(m_value1) >= m_value2; case 2: return const_cast(data)->GetData(m_value1) <= m_value2; } } return false; } case CONDITION_MAP_EVENT_DATA: { const Map* pMap = map ? map : (source ? source->GetMap() : target->GetMap()); if (const ScriptedEvent* pEvent = pMap->GetScriptedMapEvent(m_value1)) { switch (m_value4) { case 0: return pEvent->GetData(m_value2) == m_value3; case 1: return pEvent->GetData(m_value2) >= m_value3; case 2: return pEvent->GetData(m_value2) <= m_value3; } } return false; } case CONDITION_MAP_EVENT_ACTIVE: { const Map* pMap = map ? map : (source ? source->GetMap() : target->GetMap()); return pMap->GetScriptedMapEvent(m_value1); } case CONDITION_LINE_OF_SIGHT: { return source->IsWithinLOSInMap(target); } case CONDITION_DISTANCE: { uint32 distance = source->GetDistance(target); switch (m_value2) { case 0: return distance == m_value1; case 1: return distance >= m_value1; case 2: return distance <= m_value1; } return false; } case CONDITION_IS_MOVING: { return target->IsMoving(); } case CONDITION_HAS_PET: { return target->ToUnit()->GetPet(); } case CONDITION_HEALTH_PERCENT: { uint32 hp_percent = target->ToUnit()->GetHealthPercent(); switch (m_value2) { case 0: return hp_percent == m_value1; case 1: return hp_percent >= m_value1; case 2: return hp_percent <= m_value1; } return false; } case CONDITION_MANA_PERCENT: { uint32 mana_percent = target->ToUnit()->GetPowerPercent(POWER_MANA); switch (m_value2) { case 0: return mana_percent == m_value1; case 1: return mana_percent >= m_value1; case 2: return mana_percent <= m_value1; } return false; } case CONDITION_IS_IN_COMBAT: { return target->ToUnit()->isInCombat(); } case CONDITION_IS_HOSTILE_TO: { return target->ToUnit()->IsHostileTo(source->ToUnit()); } case CONDITION_IS_IN_GROUP: { return target->ToPlayer()->GetGroup(); } case CONDITION_IS_ALIVE: { return target->ToUnit()->isAlive(); } case CONDITION_MAP_EVENT_TARGETS: { bool bSatisfied = true; Map* pMap = const_cast(map ? map : (source ? source->GetMap() : target->GetMap())); if (const ScriptedEvent* pEvent = pMap->GetScriptedMapEvent(m_value1)) { for (const auto& eventTarget : pEvent->m_vTargets) { WorldObject* pObject = pMap->GetWorldObject(eventTarget.target); if (pObject) bSatisfied = bSatisfied && sConditionStorage.LookupEntry(m_value2)->Meets(pObject, map, source, conditionSourceType); if (!bSatisfied) return false; } } return bSatisfied; } case CONDITION_CANT_PATH_TO_VICTIM: { return source->ToCreature()->CantPathToVictim(); } case CONDITION_IS_PLAYER: { return m_value1 ? (target->IsUnit() ? static_cast(target)->IsCharmerOrOwnerPlayerOrPlayerItself() : false) : target->IsPlayer(); } case CONDITION_OBJECT_IS_SPAWNED: { return target->ToGameObject()->isSpawned(); } case CONDITION_OBJECT_LOOT_STATE: { return target->ToGameObject()->getLootState() == m_value1; } } return false; } // Which params must be provided to a Condition bool ConditionEntry::CheckParamRequirements(WorldObject const* target, Map const* map, WorldObject const* source) const { switch (ConditionTargets[m_condition]) { case CONDITION_REQ_NONE: return true; case CONDITION_REQ_TARGET_WORLDOBJECT: if (target) return true; return false; case CONDITION_REQ_TARGET_GAMEOBJECT: if (target && target->IsGameObject()) return true; return false; case CONDITION_REQ_TARGET_UNIT: if (target && target->IsUnit()) return true; return false; case CONDITION_REQ_TARGET_CREATURE: if (target && target->IsCreature()) return true; return false; case CONDITION_REQ_TARGET_PLAYER: if (target && target->IsPlayer()) return true; return false; case CONDITION_REQ_SOURCE_WORLDOBJECT: if (source) return true; return false; case CONDITION_REQ_SOURCE_GAMEOBJECT: if (source && source->IsGameObject()) return true; return false; case CONDITION_REQ_SOURCE_UNIT: if (source && source->IsUnit()) return true; return false; case CONDITION_REQ_SOURCE_CREATURE: if (source && source->IsCreature()) return true; return false; case CONDITION_REQ_SOURCE_PLAYER: if (source && source->IsPlayer()) return true; return false; case CONDITION_REQ_ANY_WORLDOBJECT: if (source || target) return true; return false; case CONDITION_REQ_MAP_OR_WORLDOBJECT: if (map || source || target) return true; return false; case CONDITION_REQ_BOTH_WORLDOBJECTS: if (source && target) return true; return false; case CONDITION_REQ_BOTH_GAMEOBJECTS: if (source && source->IsGameObject() && target && target->IsGameObject()) return true; return false; case CONDITION_REQ_BOTH_UNITS: if (source && source->IsUnit() && target && target->IsUnit()) return true; return false; case CONDITION_REQ_BOTH_PLAYERS: if (source && source->IsPlayer() && target && target->IsPlayer()) return true; return false; } return true; } // Verification of condition values validity bool ConditionEntry::IsValid() { switch (m_condition) { case CONDITION_NOT: { if (m_value1 >= m_entry) { sLog.outErrorDb("CONDITION_NOT (entry %u, type %d) has invalid value1 %u, must be lower than entry, skipped", m_entry, m_condition, m_value1); return false; } const ConditionEntry* condition1 = sConditionStorage.LookupEntry(m_value1); if (!condition1) { sLog.outErrorDb("CONDITION_NOT (entry %u, type %d) has value1 %u without proper condition, skipped", m_entry, m_condition, m_value1); return false; } break; } case CONDITION_OR: case CONDITION_AND: { if (m_value1 >= m_entry) { sLog.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has invalid value1 %u, must be lower than entry, skipped", m_entry, m_condition, m_value1); return false; } if (m_value2 >= m_entry) { sLog.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has invalid value2 %u, must be lower than entry, skipped", m_entry, m_condition, m_value2); return false; } const ConditionEntry* condition1 = sConditionStorage.LookupEntry(m_value1); if (!condition1) { sLog.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has value1 %u without proper condition, skipped", m_entry, m_condition, m_value1); return false; } const ConditionEntry* condition2 = sConditionStorage.LookupEntry(m_value2); if (!condition2) { sLog.outErrorDb("CONDITION _AND or _OR (entry %u, type %d) has value2 %u without proper condition, skipped", m_entry, m_condition, m_value2); return false; } break; } case CONDITION_AURA: { if (!sSpellMgr.GetSpellEntry(m_value1)) { if (!sSpellMgr.IsExistingSpellId(m_value1)) { sLog.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", m_entry, m_condition, m_value1); return false; } else { DisableCondition(); return true; } } if (m_value2 >= MAX_EFFECT_INDEX) { sLog.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing effect index (%u) (must be 0..%u), skipped", m_entry, m_condition, m_value2, MAX_EFFECT_INDEX - 1); return false; } break; } case CONDITION_ITEM: case CONDITION_ITEM_WITH_BANK: { ItemPrototype const* proto = ObjectMgr::GetItemPrototype(m_value1); if (!proto) { if (!sObjectMgr.IsExistingItemId(m_value1)) { sLog.outErrorDb("Item condition (entry %u, type %u) requires to have non existing item (%u), skipped", m_entry, m_condition, m_value1); return false; } else { DisableCondition(); return true; } } if (m_value2 < 1) { sLog.outErrorDb("Item condition (entry %u, type %u) useless with count < 1, skipped", m_entry, m_condition); return false; } break; } case CONDITION_ITEM_EQUIPPED: { ItemPrototype const* proto = ObjectMgr::GetItemPrototype(m_value1); if (!proto) { if (!sObjectMgr.IsExistingItemId(m_value1)) { sLog.outErrorDb("ItemEquipped condition (entry %u, type %u) requires to have non existing item (%u) equipped, skipped", m_entry, m_condition, m_value1); return false; } else { DisableCondition(); return true; } } break; } case CONDITION_AREAID: { const auto *areaEntry = AreaEntry::GetById(m_value1); if (!areaEntry) { sLog.outErrorDb("Zone condition (entry %u, type %u) requires to be in non existing area (%u), skipped", m_entry, m_condition, m_value1); return false; } break; } case CONDITION_REPUTATION_RANK_MIN: case CONDITION_REPUTATION_RANK_MAX: { FactionEntry const* factionEntry = sObjectMgr.GetFactionEntry(m_value1); if (!factionEntry) { sLog.outErrorDb("Reputation condition (entry %u, type %u) requires to have reputation non existing faction (%u), skipped", m_entry, m_condition, m_value1); return false; } if (m_value2 >= MAX_REPUTATION_RANK) { sLog.outErrorDb("Reputation condition (entry %u, type %u) has invalid rank requirement (value2 = %u) - must be between %u and %u, skipped", m_entry, m_condition, m_value2, MIN_REPUTATION_RANK, MAX_REPUTATION_RANK - 1); return false; } break; } case CONDITION_TEAM: { if (m_value1 != ALLIANCE && m_value1 != HORDE) { sLog.outErrorDb("Team condition (entry %u, type %u) specifies unknown team (%u), skipped", m_entry, m_condition, m_value1); return false; } break; } case CONDITION_SKILL: case CONDITION_SKILL_BELOW: { SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(m_value1); if (!pSkill) { sLog.outErrorDb("Skill condition (entry %u, type %u) specifies non-existing skill (%u), skipped", m_entry, m_condition, m_value1); return false; } if (m_value2 < 1 || m_value2 > sWorld.GetConfigMaxSkillValue()) { sLog.outErrorDb("Skill condition (entry %u, type %u) specifies invalid skill value (%u), skipped", m_entry, m_condition, m_value2); return false; } break; } case CONDITION_QUESTREWARDED: case CONDITION_QUESTTAKEN: case CONDITION_QUESTAVAILABLE: case CONDITION_QUEST_NONE: { Quest const* Quest = sObjectMgr.GetQuestTemplate(m_value1); if (!Quest) { if (!sObjectMgr.IsExistingQuestId(m_value1)) { sLog.outErrorDb("Quest condition (entry %u, type %u) specifies non-existing quest (%u), skipped", m_entry, m_condition, m_value1); return false; } else { DisableCondition(); return true; } } if (m_value2 && m_condition != CONDITION_QUESTTAKEN) sLog.outErrorDb("Quest condition (entry %u, type %u) has useless data in value2 (%u)!", m_entry, m_condition, m_value2); break; } case CONDITION_AD_COMMISSION_AURA: { if (m_value1) sLog.outErrorDb("Quest condition (entry %u, type %u) has useless data in value1 (%u)!", m_entry, m_condition, m_value1); if (m_value2) sLog.outErrorDb("Quest condition (entry %u, type %u) has useless data in value2 (%u)!", m_entry, m_condition, m_value2); break; } case CONDITION_ACTIVE_GAME_EVENT: { if (!sGameEventMgr.IsValidEvent(m_value1)) { sLog.outErrorDb("(Not)Active event condition (entry %u, type %u) requires existing event id (%u), skipped", m_entry, m_condition, m_value1); return false; } break; } case CONDITION_RACE_CLASS: { if (!m_value1 && !m_value2) { sLog.outErrorDb("Race_class condition (entry %u, type %u) has both values like 0, skipped", m_entry, m_condition); return false; } if (m_value1 && !(m_value1 & RACEMASK_ALL_PLAYABLE)) { sLog.outErrorDb("Race_class condition (entry %u, type %u) has invalid player class %u, skipped", m_entry, m_condition, m_value1); return false; } if (m_value2 && !(m_value2 & CLASSMASK_ALL_PLAYABLE)) { sLog.outErrorDb("Race_class condition (entry %u, type %u) has invalid race mask %u, skipped", m_entry, m_condition, m_value2); return false; } break; } case CONDITION_LEVEL: { if (!m_value1 || m_value1 > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)) { sLog.outErrorDb("Level condition (entry %u, type %u)has invalid level %u, skipped", m_entry, m_condition, m_value1); return false; } if (m_value2 > 2) { sLog.outErrorDb("Level condition (entry %u, type %u) has invalid argument %u (must be 0..2), skipped", m_entry, m_condition, m_value2); return false; } break; } case CONDITION_SPELL: { if (!sSpellMgr.GetSpellEntry(m_value1)) { if (!sSpellMgr.IsExistingSpellId(m_value1)) { sLog.outErrorDb("Spell condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", m_entry, m_condition, m_value1); return false; } else { DisableCondition(); return true; } } if (m_value2 > 1) { sLog.outErrorDb("Spell condition (entry %u, type %u) has invalid argument %u (must be 0..1), skipped", m_entry, m_condition, m_value2); return false; } break; } case CONDITION_NEARBY_CREATURE: { if (!sObjectMgr.GetCreatureTemplate(m_value1)) { if (!sObjectMgr.IsExistingCreatureId(m_value1)) { sLog.outErrorDb("Nearby creature condition (entry %u, type %u) specifies non-existing creature (%u), skipped", m_entry, m_condition, m_value1); return false; } else { DisableCondition(); return true; } } if (!m_value2) sLog.outErrorDb("Nearby creature condition (entry %u, type %u) used without search radius (%u)!", m_entry, m_condition, m_value2); break; } case CONDITION_NEARBY_GAMEOBJECT: { if (!sObjectMgr.GetGameObjectInfo(m_value1)) { if (!sObjectMgr.IsExistingGameObjectId(m_value1)) { sLog.outErrorDb("Nearby gameobject condition (entry %u, type %u) specifies non-existing gameobject (%u), skipped", m_entry, m_condition, m_value1); return false; } else { DisableCondition(); return true; } } if (!m_value2) sLog.outErrorDb("Nearby gameobject condition (entry %u, type %u) used without search radius (%u)!", m_entry, m_condition, m_value2); break; } case CONDITION_LAST_WAYPOINT: { if (m_value2 > 2) { sLog.outErrorDb("Last Waypoint condition (entry %u, type %u) has an invalid value in value2. (Has %u, supported 0, 1, or 2), skipping.", m_entry, m_condition, m_value2); return false; } break; } case CONDITION_GENDER: { if (m_value1 >= GENDER_NONE) { sLog.outErrorDb("Gender condition (entry %u, type %u) has an invalid value in value1. (Has %u, must be smaller than %u), skipping.", m_entry, m_condition, m_value1, GENDER_NONE); return false; } break; } case CONDITION_ESCORT: { break; } case CONDITION_WOW_PATCH: { if (m_value1 > 10) { sLog.outErrorDb("Patch condition (entry %u, type %u) has an invalid value in value1 (must be 0..10), skipping.", m_entry, m_condition, m_value1); return false; } if (m_value2 > 2) { sLog.outErrorDb("Patch condition (entry %u, type %u) has invalid argument %u (must be 0..2), skipped.", m_entry, m_condition, m_value2); return false; } break; } case CONDITION_SOURCE_ENTRY: { if (!sObjectMgr.GetCreatureTemplate(m_value1)) { if (!sObjectMgr.IsExistingCreatureId(m_value1)) { sLog.outErrorDb("NPC Entry condition (entry %u, type %u) has invalid nonexistent NPC entry %u", m_entry, m_condition, m_value2); return false; } else { DisableCondition(); return true; } } if (m_value2 < 0 || m_value2 > 1) { sLog.outErrorDb("NPC Entry condition (entry %u, type %u) has invalid criteria %u (must be 0 or 1)", m_entry, m_condition, m_value1); return false; } break; } case CONDITION_WAR_EFFORT_STAGE: { if (m_value1 < 0 || m_value1 > WAR_EFFORT_STAGE_COMPLETE) { sLog.outErrorDb("War Effort stage condition (entry %u, type %u) has invalid stage %u", m_entry, m_condition, m_value1); return false; } if (m_value2 < 0 || m_value2 > 2) { sLog.outErrorDb("War Effort stage condition (entry %u, type %u) has invalid equality %u", m_entry, m_condition, m_value2); return false; } break; } case CONDITION_MAP_ID: { if (!sMapStorage.LookupEntry(m_value1)) { sLog.outErrorDb("Current map condition (entry %u, type %u) has invalid Map ID %u", m_entry, m_condition, m_value1); return false; } break; } case CONDITION_DISTANCE: { if (m_value2 > 2) { sLog.outErrorDb("Distance condition (entry %u, type %u) has invalid argument %u (must be 0..2), skipped", m_entry, m_condition, m_value2); return false; } break; } case CONDITION_HEALTH_PERCENT: case CONDITION_MANA_PERCENT: { if ((m_value1 < 1) || (m_value1 > 100)) { sLog.outErrorDb("Health or Mana percent condition (entry %u, type %u) has invalid argument %u (must be 1..100), skipped", m_entry, m_condition, m_value1); return false; } if (m_value2 > 2) { sLog.outErrorDb("Health or Mana percent condition (entry %u, type %u) has invalid argument %u (must be 0..2), skipped", m_entry, m_condition, m_value2); return false; } break; } case CONDITION_MAP_EVENT_TARGETS: { const ConditionEntry* condition1 = sConditionStorage.LookupEntry(m_value2); if (!condition1) { sLog.outErrorDb("CONDITION_MAP_EVENT_TARGETS (entry %u, type %d) has value2 %u without proper condition, skipped", m_entry, m_condition, m_value2); return false; } break; } case CONDITION_OBJECT_LOOT_STATE: { if (m_value1 > GO_JUST_DEACTIVATED) { sLog.outErrorDb("CONDITION_OBJECT_LOOT_STATE (entry %u, type %d) has value1 %u for an invalid loot state, skipped", m_entry, m_condition, m_value1); return false; } break; } case CONDITION_NONE: case CONDITION_INSTANCE_SCRIPT: case CONDITION_ACTIVE_HOLIDAY: case CONDITION_HAS_FLAG: case CONDITION_INSTANCE_DATA: case CONDITION_MAP_EVENT_DATA: case CONDITION_MAP_EVENT_ACTIVE: case CONDITION_LINE_OF_SIGHT: case CONDITION_IS_MOVING: case CONDITION_HAS_PET: case CONDITION_IS_IN_COMBAT: case CONDITION_IS_HOSTILE_TO: case CONDITION_IS_IN_GROUP: case CONDITION_IS_ALIVE: case CONDITION_CANT_PATH_TO_VICTIM: case CONDITION_IS_PLAYER: case CONDITION_OBJECT_IS_SPAWNED: break; default: sLog.outErrorDb("Condition entry %u has bad type of %d, skipped ", m_entry, m_condition); return false; } return true; } // Check if a condition can be used without providing a player param bool ConditionEntry::CanBeUsedWithoutPlayer(uint32 entry) { ConditionEntry const* condition = sConditionStorage.LookupEntry(entry); if (!condition) return false; switch (condition->m_condition) { case CONDITION_NOT: return CanBeUsedWithoutPlayer(condition->m_value1); case CONDITION_AND: case CONDITION_OR: return CanBeUsedWithoutPlayer(condition->m_value1) && CanBeUsedWithoutPlayer(condition->m_value2); default: if (ConditionTargets[condition->m_condition] != CONDITION_REQ_TARGET_PLAYER) return true; } return false; } bool IsConditionSatisfied(uint32 conditionId, WorldObject const* target, Map const* map, WorldObject const* source, ConditionSource conditionSourceType) { if (const ConditionEntry* condition = sConditionStorage.LookupEntry(conditionId)) return condition->Meets(target, map, source, conditionSourceType); return false; }