Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2025-06-28 10:46:23 +08:00
63 changed files with 998 additions and 449 deletions
+42 -6
View File
@@ -191,8 +191,26 @@ SpellCastResult UnitAI::DoCast(uint32 spellId)
{
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId))
{
bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER);
target = SelectTarget(SelectTargetMethod::Random, 0, spellInfo->GetMaxRange(false), playerOnly);
DefaultTargetSelector targetSelector(me, spellInfo->GetMaxRange(false), false, true, 0);
target = SelectTarget(SelectTargetMethod::Random, 0, [&](Unit* target) {
if (!target)
return false;
if (target->IsPlayer())
{
if (spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER))
return false;
}
else
{
if (spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER))
return false;
if (spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC) && target->IsControlledByPlayer())
return false;
}
return targetSelector(target);
});
}
break;
}
@@ -206,12 +224,30 @@ SpellCastResult UnitAI::DoCast(uint32 spellId)
{
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId))
{
bool playerOnly = spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER);
float range = spellInfo->GetMaxRange(false);
DefaultTargetSelector targetSelector(me, range, playerOnly, true, -(int32)spellId);
if (!(spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_VICTIM)
&& targetSelector(me->GetVictim()))
DefaultTargetSelector defaultTargetSelector(me, range, false, true, -(int32)spellId);
auto targetSelector = [&](Unit* target) {
if (!target)
return false;
if (target->IsPlayer())
{
if (spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER))
return false;
}
else
{
if (spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER))
return false;
if (spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC) && target->IsControlledByPlayer())
return false;
}
return defaultTargetSelector(target);
};
if (!(spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_VICTIM) && targetSelector(me->GetVictim()))
target = me->GetVictim();
else
target = SelectTarget(SelectTargetMethod::Random, 0, targetSelector);
+3 -2
View File
@@ -165,9 +165,10 @@ struct PowerUsersSelector : public Acore::unary_function<Unit*, bool>
}
};
struct FarthestTargetSelector : public Acore::unary_function<Unit*, bool>
// Simple selector based on range and Los
struct RangeSelector : public Acore::unary_function<Unit*, bool>
{
FarthestTargetSelector(Unit const* unit, float maxDist, bool playerOnly, bool inLos, float minDist = 0.f) : _me(unit), _minDist(minDist), _maxDist(maxDist), _playerOnly(playerOnly), _inLos(inLos) {}
RangeSelector(Unit const* unit, float maxDist, bool playerOnly, bool inLos, float minDist = 0.f) : _me(unit), _minDist(minDist), _maxDist(maxDist), _playerOnly(playerOnly), _inLos(inLos) {}
bool operator()(Unit const* target) const
{
+31 -25
View File
@@ -2506,12 +2506,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
case SMART_ACTION_START_CLOSEST_WAYPOINT:
{
std::vector<uint32> waypoints;
std::copy_if(e.action.closestWaypointFromList.wps.begin(), e.action.closestWaypointFromList.wps.end(),
std::back_inserter(waypoints), [](uint32 wp) { return wp != 0; });
float distanceToClosest = std::numeric_limits<float>::max();
WayPoint* closestWp = nullptr;
uint32 closestWpId = 0;
for (WorldObject* target : targets)
{
@@ -2519,29 +2515,34 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
if (IsSmart(creature))
{
for (uint32 wp : waypoints)
for (uint32 wp = e.action.startClosestWaypoint.pathId1; wp <= e.action.startClosestWaypoint.pathId2; ++wp)
{
WPPath* path = sSmartWaypointMgr->GetPath(wp);
if (!path || path->empty())
continue;
auto itrWp = path->find(0);
auto itrWp = path->find(1);
if (itrWp != path->end())
{
if (WayPoint* wp = itrWp->second)
if (WayPoint* wpData = itrWp->second)
{
float distToThisPath = creature->GetDistance(wp->x, wp->y, wp->z);
float distToThisPath = creature->GetExactDistSq(wpData->x, wpData->y, wpData->z);
if (distToThisPath < distanceToClosest)
{
distanceToClosest = distToThisPath;
closestWp = wp;
closestWpId = wp;
}
}
}
}
if (closestWp)
CAST_AI(SmartAI, creature->AI())->StartPath(false, closestWp->id, true);
if (closestWpId)
{
bool repeat = e.action.startClosestWaypoint.repeat;
bool run = e.action.startClosestWaypoint.run;
CAST_AI(SmartAI, creature->AI())->StartPath(repeat, closestWpId, run);
}
}
}
}
@@ -3303,10 +3304,16 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (e.link && e.link != e.event_id)
{
auto linked = FindLinkedEvent(e.link);
if (linked.has_value() && linked.value().get().GetEventType() == SMART_EVENT_LINK)
executionStack.emplace_back(SmartScriptFrame{ linked.value(), unit, var0, var1, bvar, spell, gob });
if (linked.has_value())
{
auto& linkedEvent = linked.value().get();
if (linkedEvent.GetEventType() == SMART_EVENT_LINK)
executionStack.emplace_back(SmartScriptFrame{ linkedEvent, unit, var0, var1, bvar, spell, gob });
else
LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {}, Link Event {} found but has wrong type (should be 61, is {}).", e.entryOrGuid, e.GetScriptType(), e.event_id, e.link, linkedEvent.GetEventType());
}
else
LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {}, Link Event {} not found or invalid, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.link);
LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {}, Link Event {} not found, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.link);
}
}
@@ -3509,7 +3516,7 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e,
case SMART_TARGET_FARTHEST:
if (me)
{
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MinDistance, 0, FarthestTargetSelector(me, e.target.farthest.maxDist, e.target.farthest.playerOnly, e.target.farthest.isInLos, e.target.farthest.minDist)))
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MinDistance, 0, RangeSelector(me, e.target.farthest.maxDist, e.target.farthest.playerOnly, e.target.farthest.isInLos, e.target.farthest.minDist)))
targets.push_back(u);
}
break;
@@ -3701,10 +3708,12 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e,
if (!units.empty() && baseObject)
for (WorldObject* unit : units)
if (IsPlayer(unit) && baseObject->IsInRange(unit, float(e.target.playerRange.minDist), float(e.target.playerRange.maxDist)))
if (IsPlayer(unit) && !unit->ToPlayer()->IsGameMaster() && baseObject->IsInRange(unit, float(e.target.playerRange.minDist), float(e.target.playerRange.maxDist)))
targets.push_back(unit);
if (e.target.playerRange.maxCount)
Acore::Containers::RandomResize(targets, e.target.playerRange.maxCount);
break;
}
case SMART_TARGET_PLAYER_DISTANCE:
@@ -4658,12 +4667,11 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
if (!targets.empty())
{
for (WorldObject* target : targets)
{
if (IsPlayer(target))
if (IsPlayer(target) && !target->ToPlayer()->IsGameMaster())
playerCount++;
}
if (playerCount >= e.event.nearPlayer.minCount)
ProcessAction(e, unit);
if (playerCount >= e.event.nearPlayer.minCount)
ProcessAction(e, unit);
}
RecalcTimer(e, e.event.nearPlayer.repeatMin, e.event.nearPlayer.repeatMax);
break;
@@ -4677,10 +4685,8 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
if (!targets.empty())
{
for (WorldObject* target : targets)
{
if (IsPlayer(target))
if (IsPlayer(target) && !target->ToPlayer()->IsGameMaster())
playerCount++;
}
if (playerCount < e.event.nearPlayerNegation.maxCount)
ProcessAction(e, unit);
@@ -806,7 +806,7 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
case SMART_ACTION_REMOVE_POWER: return sizeof(SmartAction::power);
case SMART_ACTION_GAME_EVENT_STOP: return sizeof(SmartAction::gameEventStop);
case SMART_ACTION_GAME_EVENT_START: return sizeof(SmartAction::gameEventStart);
case SMART_ACTION_START_CLOSEST_WAYPOINT: return sizeof(SmartAction::closestWaypointFromList);
case SMART_ACTION_START_CLOSEST_WAYPOINT: return sizeof(SmartAction::startClosestWaypoint);
case SMART_ACTION_RISE_UP: return sizeof(SmartAction::moveRandom);
case SMART_ACTION_RANDOM_SOUND: return sizeof(SmartAction::randomSound);
case SMART_ACTION_SET_CORPSE_DELAY: return sizeof(SmartAction::corpseDelay);
@@ -1536,15 +1536,22 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
break;
}
case SMART_ACTION_START_CLOSEST_WAYPOINT:
{
if (e.action.startClosestWaypoint.pathId1 == 0 || e.action.startClosestWaypoint.pathId2 == 0 || e.action.startClosestWaypoint.pathId2 < e.action.startClosestWaypoint.pathId1)
{
if (std::all_of(e.action.closestWaypointFromList.wps.begin(), e.action.closestWaypointFromList.wps.end(), [](uint32 wp) { return wp == 0; }))
{
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} does not have any non-zero waypoint id",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
return false;
}
break;
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} has invalid pathId1 or pathId2, it must be greater than 0 and pathId1 > pathId2",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
return false;
}
if (e.action.startClosestWaypoint.repeat > 1 || e.action.startClosestWaypoint.run > 1)
{
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} has invalid run ({}) or repeat ({}) parameter, must be 0 or 1.",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(),
e.action.startClosestWaypoint.repeat, e.action.startClosestWaypoint.run);
return false;
}
break;
}
case SMART_ACTION_INVOKER_CAST:
if (e.GetScriptType() != SMART_SCRIPT_TYPE_TIMED_ACTIONLIST && e.GetEventType() != SMART_EVENT_LINK && !EventHasInvoker(e.event.type))
{
@@ -1284,8 +1284,11 @@ struct SmartAction
struct
{
std::array<uint32, SMART_ACTION_PARAM_COUNT> wps;
} closestWaypointFromList;
uint32 pathId1;
uint32 pathId2;
uint32 repeat;
uint32 run;
} startClosestWaypoint;
struct
{
+7 -1
View File
@@ -27,7 +27,7 @@
namespace AccountMgr
{
AccountOpResult CreateAccount(std::string username, std::string password)
AccountOpResult CreateAccount(std::string username, std::string password, std::string email /*= ""*/)
{
if (utf8length(username) > MAX_ACCOUNT_STR)
return AOR_NAME_TOO_LONG; // username's too long
@@ -35,8 +35,12 @@ namespace AccountMgr
if (utf8length(password) > MAX_PASS_STR)
return AOR_PASS_TOO_LONG; // password's too long
if (utf8length(email) > MAX_EMAIL_STR)
return AOR_EMAIL_TOO_LONG; // email is too long
Utf8ToUpperOnlyLatin(username);
Utf8ToUpperOnlyLatin(password);
Utf8ToUpperOnlyLatin(email);
if (GetId(username))
return AOR_NAME_ALREADY_EXIST; // username does already exist
@@ -48,6 +52,8 @@ namespace AccountMgr
stmt->SetData(1, salt);
stmt->SetData(2, verifier);
stmt->SetData(3, uint8(sWorld->getIntConfig(CONFIG_EXPANSION)));
stmt->SetData(4, email);
stmt->SetData(5, email);
LoginDatabase.Execute(stmt);
+1 -1
View File
@@ -38,7 +38,7 @@ enum AccountOpResult
namespace AccountMgr
{
AccountOpResult CreateAccount(std::string username, std::string password);
AccountOpResult CreateAccount(std::string username, std::string password, std::string email = "");
AccountOpResult DeleteAccount(uint32 accountId);
AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword);
AccountOpResult ChangePassword(uint32 accountId, std::string newPassword);
@@ -788,9 +788,7 @@ void Creature::Update(uint32 diff)
m_moveBackwardsMovementTime = urand(MOVE_BACKWARDS_CHECK_INTERVAL, MOVE_BACKWARDS_CHECK_INTERVAL * 3);
}
else
{
m_moveBackwardsMovementTime -= diff;
}
// Circling the target
if (diff >= m_moveCircleMovementTime)
@@ -799,9 +797,17 @@ void Creature::Update(uint32 diff)
m_moveCircleMovementTime = urand(MOVE_CIRCLE_CHECK_INTERVAL, MOVE_CIRCLE_CHECK_INTERVAL * 2);
}
else
{
m_moveCircleMovementTime -= diff;
// Periodically check if able to move, if not, extend leash timer
if (diff >= m_extendLeashTime)
{
if (!CanFreeMove())
UpdateLeashExtensionTime();
m_extendLeashTime = EXTEND_LEASH_CHECK_INTERVAL;
}
else
m_extendLeashTime -= diff;
}
// Call for assistance if not disabled
@@ -396,8 +396,10 @@ public:
bool IsFreeToMove();
static constexpr uint32 MOVE_CIRCLE_CHECK_INTERVAL = 3000;
static constexpr uint32 MOVE_BACKWARDS_CHECK_INTERVAL = 2000;
static constexpr uint32 EXTEND_LEASH_CHECK_INTERVAL = 3000;
uint32 m_moveCircleMovementTime = MOVE_CIRCLE_CHECK_INTERVAL;
uint32 m_moveBackwardsMovementTime = MOVE_BACKWARDS_CHECK_INTERVAL;
uint32 m_extendLeashTime = EXTEND_LEASH_CHECK_INTERVAL;
[[nodiscard]] bool HasSwimmingFlagOutOfCombat() const
{
+7 -2
View File
@@ -13642,7 +13642,11 @@ void Player::_LoadSkills(PreparedQueryResult result)
SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skill, getRace(), getClass());
if (!rcEntry)
{
LOG_ERROR("entities.player", "Character {} has skill {} that does not exist.", GetGUID().ToString(), skill);
LOG_ERROR("entities.player", "Player {} (GUID: {}), has skill ({}) that is invalid for the race/class combination (Race: {}, Class: {}). Will be deleted.",
GetName(), GetGUID().GetCounter(), skill, getRace(), getClass());
// Mark skill for deletion in the database
mSkillStatus.insert(SkillStatusMap::value_type(skill, SkillStatusData(0, SKILL_DELETED)));
continue;
}
@@ -13663,7 +13667,8 @@ void Player::_LoadSkills(PreparedQueryResult result)
if (value == 0)
{
LOG_ERROR("entities.player", "Character {} has skill {} with value 0. Will be deleted.", GetGUID().ToString(), skill);
LOG_ERROR("entities.player", "Player {} (GUID: {}), has skill ({}) with value 0. Will be deleted.",
GetName(), GetGUID().GetCounter(), skill);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SKILL);
@@ -65,6 +65,9 @@ public:
[[nodiscard]] uint32 GetCurrentTaxiPath() const;
uint32 NextTaxiDestination()
{
if (m_TaxiDestinations.empty())
return 0;
m_TaxiDestinations.pop_front();
return GetTaxiDestination();
}
+36
View File
@@ -20114,6 +20114,24 @@ private:
AuraType _auraType;
};
class ResetToHomeOrientation : public BasicEvent
{
public:
ResetToHomeOrientation(Creature& self) : _self(self) { }
bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override
{
if (_self.IsInWorld() && _self.FindMap() && _self.IsAlive() && !_self.IsInCombat())
{
_self.SetFacingTo(_self.GetHomePosition().GetOrientation());
}
return true;
}
private:
Creature& _self;
};
void Unit::CastDelayedSpellWithPeriodicAmount(Unit* caster, uint32 spellId, AuraType auraType, int32 addAmount, uint8 effectIndex)
{
AuraEffect* aurEff = nullptr;
@@ -20360,6 +20378,24 @@ void Unit::SetFacingToObject(WorldObject* object)
init.Launch();
}
void Unit::SetTimedFacingToObject(WorldObject* object, uint32 time)
{
// never face when already moving
if (!IsStopped() || !time)
return;
/// @todo figure out under what conditions creature will move towards object instead of facing it where it currently is.
Movement::MoveSplineInit init(this);
init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZ());
init.SetFacing(GetAngle(object)); // when on transport, GetAngle will still return global coordinates (and angle) that needs transforming
init.Launch();
if (Creature* c = ToCreature())
c->m_Events.AddEvent(new ResetToHomeOrientation(*c), c->m_Events.CalculateTime(time));
else
LOG_ERROR("entities.unit", "Unit::SetTimedFacingToObject called on non-creature unit {}. This should never happen.", GetEntry());
}
bool Unit::SetWalk(bool enable)
{
if (enable == IsWalking())
+1
View File
@@ -1894,6 +1894,7 @@ public:
void SetInFront(WorldObject const* target);
void SetFacingTo(float ori);
void SetFacingToObject(WorldObject* object);
void SetTimedFacingToObject(WorldObject* object, uint32 time); // Reset to home orientation after given time
bool isInAccessiblePlaceFor(Creature const* c) const;
bool isInFrontInMap(Unit const* target, float distance, float arc = M_PI) const;
+3 -3
View File
@@ -512,7 +512,7 @@ void GameEventMgr::LoadEventCreatureData()
Field* fields = result->Fetch();
ObjectGuid::LowType guid = fields[0].Get<uint32>();
int16 eventId = fields[1].Get<int8>();
int16 eventId = fields[1].Get<int16>();
CreatureData const* data = sObjectMgr->GetCreatureData(guid);
if (!data)
@@ -562,7 +562,7 @@ void GameEventMgr::LoadEventGameObjectData()
Field* fields = result->Fetch();
ObjectGuid::LowType guid = fields[0].Get<uint32>();
int16 eventId = fields[1].Get<int8>();
int16 eventId = fields[1].Get<int16>();
int32 internal_event_id = _gameEvent.size() + eventId - 1;
@@ -1020,7 +1020,7 @@ void GameEventMgr::LoadEventPoolData()
Field* fields = result->Fetch();
uint32 entry = fields[0].Get<uint32>();
int16 eventId = fields[1].Get<int8>();
int16 eventId = fields[1].Get<int16>();
int32 internal_event_id = _gameEvent.size() + eventId - 1;
+2 -2
View File
@@ -2317,7 +2317,7 @@ void ObjectMgr::LoadCreatures()
data.movementType = fields[15].Get<uint8>();
data.spawnMask = fields[16].Get<uint8>();
data.phaseMask = fields[17].Get<uint32>();
int16 gameEvent = fields[18].Get<int8>();
int16 gameEvent = fields[18].Get<int16>();
uint32 PoolId = fields[19].Get<uint32>();
data.npcflag = fields[20].Get<uint32>();
data.unit_flags = fields[21].Get<uint32>();
@@ -2709,7 +2709,7 @@ void ObjectMgr::LoadGameobjects()
LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) that has wrong spawn mask {} including not supported difficulty modes for map (Id: {}), skip", guid, data.id, data.spawnMask, data.mapid);
data.phaseMask = fields[15].Get<uint32>();
int16 gameEvent = fields[16].Get<int8>();
int16 gameEvent = fields[16].Get<int16>();
uint32 PoolId = fields[17].Get<uint32>();
if (data.rotation.x < -1.0f || data.rotation.x > 1.0f)
+2 -2
View File
@@ -172,8 +172,8 @@ namespace Acore
template<class RET_TYPE, int CENTER_VAL>
inline RET_TYPE Compute(float x, float y, float size)
{
int gx = (int)(CENTER_VAL - x / size);
int gy = (int)(CENTER_VAL - y / size);
int gx = std::max<int>(0, (CENTER_VAL - x / size));
int gy = std::max<int>(0, (CENTER_VAL - y / size));
return RET_TYPE(gx, gy);
}
+2 -1
View File
@@ -331,7 +331,8 @@ enum AcoreStrings
LANG_COMMAND_WHISPERON = 285,
LANG_COMMAND_WHISPEROFF = 286,
LANG_COMMAND_CREATGUIDNOTFOUND = 287,
// TICKET STRINGS NEED REWRITE // 288-296 FREE
LANG_COMMAND_GONOTENOUGHSPAWNS = 288,
// TICKET STRINGS NEED REWRITE // 289-296 FREE
// END
LANG_COMMAND_WANDER_DISTANCE = 297,
@@ -153,18 +153,20 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
MovementInform(owner);
}
if (owner->movespline->Finalized())
{ // Mobs should chase you infinitely if you stop and wait every few seconds.
i_leashExtensionTimer.Update(time_diff);
if (i_leashExtensionTimer.Passed())
{
i_leashExtensionTimer.Reset(5000);
if (cOwner)
if (cOwner)
{
if (owner->movespline->Finalized() && cOwner->IsWithinMeleeRange(target))
{ // Mobs should chase you infinitely if you stop and wait every few seconds.
i_leashExtensionTimer.Update(time_diff);
if (i_leashExtensionTimer.Passed())
{
i_leashExtensionTimer.Reset(cOwner->GetAttackTime(BASE_ATTACK));
cOwner->UpdateLeashExtensionTime();
}
}
else if (i_recalculateTravel)
i_leashExtensionTimer.Reset(cOwner->GetAttackTime(BASE_ATTACK));
}
else if (i_recalculateTravel)
i_leashExtensionTimer.Reset(5000);
// if the target moved, we have to consider whether to adjust
if (!_lastTargetPosition || target->GetPosition() != _lastTargetPosition.value() || mutualChase != _mutualChase || !owner->IsWithinLOSInMap(target))
@@ -298,6 +300,7 @@ void ChaseMovementGenerator<Creature>::DoInitialize(Creature* owner)
i_path = nullptr;
_lastTargetPosition.reset();
i_recheckDistance.Reset(0);
i_leashExtensionTimer.Reset(owner->GetAttackTime(BASE_ATTACK));
owner->SetWalk(false);
owner->AddUnitState(UNIT_STATE_CHASE);
}
+1 -1
View File
@@ -24,7 +24,6 @@
class AchievementGlobalMgr;
class AchievementMgr;
class ArenaTeam;
class AuctionEntry;
class AuctionHouseMgr;
class AuctionHouseObject;
class Aura;
@@ -98,6 +97,7 @@ enum WeatherState : uint32;
struct AchievementCriteriaEntry;
struct AchievementEntry;
struct AreaTrigger;
struct AuctionEntry;
struct CompletedAchievementData;
struct Condition;
struct ConditionSourceInfo;
@@ -110,6 +110,7 @@ public:
uint8 GetCasterLevel() const { return m_casterLevel; }
bool CanApplyResilience() const { return m_applyResilience; }
float GetPctMods() const { return m_pctMods; }
void SetPctMods(float pctMods) { m_pctMods = pctMods; }
// xinef: stacking
uint32 GetAuraGroup() const { return m_auraGroup; }
+2
View File
@@ -2147,6 +2147,8 @@ uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionList*
retMask &= GRID_MAP_TYPE_MASK_CORPSE | GRID_MAP_TYPE_MASK_PLAYER;
if (m_spellInfo->HasAttribute(SPELL_ATTR3_ONLY_ON_GHOSTS))
retMask &= GRID_MAP_TYPE_MASK_PLAYER;
if (m_spellInfo->HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER))
retMask &= ~GRID_MAP_TYPE_MASK_PLAYER;
if (condList)
retMask &= sConditionMgr->GetSearcherTypeMaskForConditionList(*condList);
+13 -2
View File
@@ -1863,8 +1863,19 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta
else return SPELL_CAST_OK;
// corpseOwner and unit specific target checks
if (AttributesEx3 & SPELL_ATTR3_ONLY_ON_PLAYER && !unitTarget->ToPlayer())
return SPELL_FAILED_TARGET_NOT_PLAYER;
if (unitTarget->IsPlayer())
{
if (HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER))
return SPELL_FAILED_TARGET_IS_PLAYER;
}
else
{
if (HasAttribute(SPELL_ATTR3_ONLY_ON_PLAYER))
return SPELL_FAILED_TARGET_NOT_PLAYER;
if (HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC) && unitTarget->IsControlledByPlayer())
return SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED;
}
if (!IsAllowingDeadTarget() && !unitTarget->IsAlive())
return SPELL_FAILED_TARGETS_DEAD;