/*
* 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",
"areatrigger"
};
// 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_UNIT, // 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_WORLDOBJECTS, // 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
CONDITION_REQ_MAP_OR_WORLDOBJECT, // 50
CONDITION_REQ_TARGET_PLAYER, // 51
CONDITION_REQ_SOURCE_WORLDOBJECT, // 52
};
// 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() : "", map ? map->GetId() : -1, source ? source->GetGuidStr().c_str() : "");
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:
{
// Third and fourth condition are optional
if (m_value3 && sConditionStorage.LookupEntry(m_value3)->Meets(target, map, source, conditionSourceType))
return true;
if (m_value4 && sConditionStorage.LookupEntry(m_value4)->Meets(target, map, source, conditionSourceType))
return true;
return sConditionStorage.LookupEntry(m_value1)->Meets(target, map, source, conditionSourceType) || sConditionStorage.LookupEntry(m_value2)->Meets(target, map, source, conditionSourceType);
}
case CONDITION_AND:
{
// Third and fourth condition are optional
bool extraConditionsSatisfied = true;
if (m_value3)
extraConditionsSatisfied = extraConditionsSatisfied && sConditionStorage.LookupEntry(m_value3)->Meets(target, map, source, conditionSourceType);
if (m_value4)
extraConditionsSatisfied = extraConditionsSatisfied && sConditionStorage.LookupEntry(m_value4)->Meets(target, map, source, conditionSourceType);
return extraConditionsSatisfied && 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 (const auto& aura : auras)
if ((aura.second->GetSpellProto()->Attributes & SPELL_ATTR_CASTABLE_WHILE_MOUNTED || aura.second->GetSpellProto()->Attributes & SPELL_ATTR_IS_ABILITY) && aura.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();
return (!m_value1 || (pPlayer->GetRaceMask() & m_value1)) && (!m_value2 || (pPlayer->GetClassMask() & m_value2));
}
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 = target ? target->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, !m_value3, m_value4 ? target->ToCreature() : nullptr));
}
case CONDITION_NEARBY_GAMEOBJECT:
{
return (bool)(target->FindNearestGameObject(m_value1, m_value2));
}
case CONDITION_QUEST_NONE:
{
Player const* pPlayer = target->ToPlayer();
return !pPlayer->IsCurrentQuest(m_value1) && !pPlayer->GetQuestRewardStatus(m_value1);
}
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:
{
Creature const* pSource = ToCreature(source);
Player const* 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 const 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());
return mapId == m_value1;
}
case CONDITION_INSTANCE_DATA:
{
Map const* pMap = map ? map : (source ? source->GetMap() : target->GetMap());
if (InstanceData const* data = pMap->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:
{
Map const* pMap = map ? map : (source ? source->GetMap() : target->GetMap());
if (ScriptedEvent const* 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:
{
Map const* 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->IsHostileTo(source);
}
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