Core/SmartAI: Various fixes and extensions for smart scripts: (#18673)
- Possible crashes fixed - Memory leak fixed - Implemented checking of vehicle conditions - Extended eventphasemask to 12 bits (sql required to change DB field type) - SMART_EVENT_GOSSIP_HELLO - added possibility to detect for gameobject reportUse call - Renamed action SMART_ACTION_SET_FLY to SMART_ACTION_SET_DISABLE_GRAVITY (to reflect actual functionality) - Added targetsLimit to action SMART_ACTION_CAST and SMART_ACTION_INVOKER_CAST to limit max amount of targets (selected randomly) - Action SMART_ACTION_TALK corrected to always work as intended - Properly call GroupEventHappens in action SMART_ACTION_CALL_GROUPEVENTHAPPENS if invoker was charmed or owned by the player - Properly utilize followAngle in action SMART_ACTION_FOLLOW (db orientation should be in degrees), but keep backward compatibility - Added action SMART_ACTION_SET_CAN_FLY (119) 0/1 - Added action SMART_ACTION_REMOVE_AURAS_BY_TYPE (120) AuraType, can be used to exit vehicle for example - Added action SMART_ACTION_SET_SIGHT_DIST (121) sightDist - Added action SMART_ACTION_FLEE (122) fleeTime - Added action SMART_ACTION_ADD_THREAT (123) +threat, -threat - Added action SMART_ACTION_LOAD_EQUIPMENT (124) equipmentId - Added action SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT (125) minId, maxId - Added action SMART_ACTION_REMOVE_ALL_GAMEOBJECTS (126), removes all owned gameobjects - Added action SMART_ACTION_STOP_MOTION (127), stopMoving, movementExpired - Extended target SMART_TARGET_HOSTILE_SECOND_AGGRO with following parameters maxdist, playerOnly, powerType + 1 - Extended target SMART_TARGET_HOSTILE_LAST_AGGRO with following parameters maxdist, playerOnly, powerType + 1 - Extended target SMART_TARGET_HOSTILE_RANDOM with following parameters maxdist, playerOnly, powerType + 1 - Extended target SMART_TARGET_HOSTILE_RANDOM_NOT_TOP with following parameters maxdist, playerOnly, powerType + 1 - Extended target SMART_TARGET_THREAT_LIST with maxdist - Extended target SMART_TARGET_OWNER_OR_SUMMONER to be able to get charmer/owner of current owner - Added new target SMART_TARGET_FARTHEST with maxDist, playerOnly, isInLos restrictions - Added SpellHit hook to GameObjectAI and extended SmartGameObjectAI to call SMART_EVENT_SPELLHIT when gameobject is hit by spell - Call GameObjectAI Reset hook on gameobject respawn (for ex. to reset one time events in smart scripts) - Fixed some logic errors in code - SmartAI Escorts properly despawn escortee if no players are in range - Disable Evading while charming creature with SmartAI - Don't call SMART_EVENT_RESPAWN for dead units before they actually respawn - Don't call SMART_EVENT_RESPAWN for not spawned gameobjects - Properly call SMART_EVENT_RESPAWN for gameobject respawn - Allow action SMART_ACTION_SET_IN_COMBAT_WITH_ZONE to utilize targetlist - Allow action SMART_ACTION_CALL_FOR_HELP to utilize targetList - Allow action SMART_ACTION_SET_INVINCIBILITY_HP_LEVEL to utilize targetList - Allow action SMART_ACTION_SET_VISIBILITY to utilize targetList - Allow action SMART_ACTION_SET_ACTIVE to utilize targetList - Allow action SMART_ACTION_ATTACK_START to select random attack target instead of first on the list - Allow gameobjects to summon gameobjects with action SMART_ACTION_SUMMON_GO - Properly store action invokers for action SMART_ACTION_WP_START, if no player invokers are found, distance despawn check won't be used - Allow action SMART_ACTION_WP_RESUME to compensate for the state the unit actually is in (eg. combat) - Allow action SMART_ACTION_MOVE_TO_POS to select random of the avaiable targets, not only the first one. - Allow action SMART_ACTION_MOVE_TO_POS to utilize x, y, z parameters as an offset to calculated coordinates - Action SMART_ACTION_RESPAWN_TARGET should never modify respawntime of already spawned gameobjects, use dedicated function - Properly delete ontime events created by SMART_ACTION_CREATE_TIMED_EVENT - If action could not be started because conditions were not satisfied, do not recalculate the waittime to action repeattime, use smaller value to recheck more frequently - Allow target SMART_TARGET_CLOSEST_PLAYER to be used by gameobjects - Allow target SMART_TARGET_OWNER_OR_SUMMONER to be used by gameobjects - Fixed SMART_EVENT_COUNTER_SET to be only called for the id that was incremented - Changed the way counters work - Protect PhaseInc from surpassing maximum phase - Added loading checks for missing NON_REPEATABLE flag if no repeatmin, repeatmax is set - Added spell validation for SMART_ACTION_CROSS_CAST
This commit is contained in:
1 parent
e09c37334d
commit
62111a8c4a
21 files changed
+830
-251
No files matched your search
@@ -302,24 +302,27 @@ void VehicleAI::LoadConditions()
|
||||
|
||||
void VehicleAI::CheckConditions(uint32 diff)
|
||||
{
|
||||
if (m_ConditionsTimer < diff)
|
||||
if (!m_HasConditions)
|
||||
return;
|
||||
|
||||
if (m_ConditionsTimer <= diff)
|
||||
{
|
||||
if (m_HasConditions)
|
||||
if (Vehicle* vehicleKit = me->GetVehicleKit())
|
||||
{
|
||||
if (Vehicle* vehicleKit = me->GetVehicleKit())
|
||||
for (SeatMap::iterator itr = vehicleKit->Seats.begin(); itr != vehicleKit->Seats.end(); ++itr)
|
||||
if (Unit* passenger = ObjectAccessor::GetUnit(*me, itr->second.Passenger.Guid))
|
||||
for (SeatMap::iterator itr = vehicleKit->Seats.begin(); itr != vehicleKit->Seats.end(); ++itr)
|
||||
if (Unit* passenger = ObjectAccessor::GetUnit(*me, itr->second.Passenger.Guid))
|
||||
{
|
||||
if (Player* player = passenger->ToPlayer())
|
||||
{
|
||||
if (Player* player = passenger->ToPlayer())
|
||||
if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry(), player, me))
|
||||
{
|
||||
if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry(), player, me))
|
||||
{
|
||||
player->ExitVehicle();
|
||||
return; // check other pessanger in next tick
|
||||
}
|
||||
player->ExitVehicle();
|
||||
return; // check other pessanger in next tick
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_ConditionsTimer = VEHICLE_CONDITION_CHECK_TIME;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -47,7 +47,7 @@ class TC_GAME_API GameObjectAI
|
||||
|
||||
static int Permissible(GameObject const* go);
|
||||
|
||||
virtual bool GossipHello(Player* /*player*/) { return false; }
|
||||
virtual bool GossipHello(Player* /*player*/, bool /*reportUse*/) { return false; }
|
||||
virtual bool GossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) { return false; }
|
||||
virtual bool GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, char const* /*code*/) { return false; }
|
||||
virtual bool QuestAccept(Player* /*player*/, Quest const* /*quest*/) { return false; }
|
||||
@@ -61,6 +61,7 @@ class TC_GAME_API GameObjectAI
|
||||
virtual void OnGameEvent(bool /*start*/, uint16 /*eventId*/) { }
|
||||
virtual void OnStateChanged(uint32 /*state*/, Unit* /*unit*/) { }
|
||||
virtual void EventInform(uint32 /*eventId*/) { }
|
||||
virtual void SpellHit(Unit* /*unit*/, const SpellInfo* /*spellInfo*/) { }
|
||||
};
|
||||
|
||||
class TC_GAME_API NullGameObjectAI : public GameObjectAI
|
||||
|
||||
@@ -348,3 +348,40 @@ bool NonTankTargetSelector::operator()(Unit const* target) const
|
||||
|
||||
return target != _source->GetVictim();
|
||||
}
|
||||
|
||||
bool PowerUsersSelector::operator()(Unit const* target) const
|
||||
{
|
||||
if (!_me || !target)
|
||||
return false;
|
||||
|
||||
if (target->getPowerType() != _power)
|
||||
return false;
|
||||
|
||||
if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
|
||||
return false;
|
||||
|
||||
if (_dist > 0.0f && !_me->IsWithinCombatRange(target, _dist))
|
||||
return false;
|
||||
|
||||
if (_dist < 0.0f && _me->IsWithinCombatRange(target, -_dist))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FarthestTargetSelector::operator()(Unit const* target) const
|
||||
{
|
||||
if (!_me || !target)
|
||||
return false;
|
||||
|
||||
if (_playerOnly && target->GetTypeId() != TYPEID_PLAYER)
|
||||
return false;
|
||||
|
||||
if (_dist > 0.0f && !_me->IsWithinCombatRange(target, _dist))
|
||||
return false;
|
||||
|
||||
if (_inLos && !_me->IsWithinLOSInMap(target))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -115,6 +115,33 @@ struct TC_GAME_API NonTankTargetSelector : public std::unary_function<Unit*, boo
|
||||
bool _playerOnly;
|
||||
};
|
||||
|
||||
// Simple selector for units using mana
|
||||
struct TC_GAME_API PowerUsersSelector
|
||||
{
|
||||
public:
|
||||
PowerUsersSelector(Unit const* unit, Powers power, float dist, bool playerOnly) : _me(unit), _power(power), _dist(dist), _playerOnly(playerOnly) { }
|
||||
bool operator()(Unit const* target) const;
|
||||
|
||||
private:
|
||||
Unit const* _me;
|
||||
Powers const _power;
|
||||
float const _dist;
|
||||
bool const _playerOnly;
|
||||
};
|
||||
|
||||
struct TC_GAME_API FarthestTargetSelector
|
||||
{
|
||||
public:
|
||||
FarthestTargetSelector(Unit const* unit, float dist, bool playerOnly, bool inLos) : _me(unit), _dist(dist), _playerOnly(playerOnly), _inLos(inLos) {}
|
||||
bool operator()(Unit const* target) const;
|
||||
|
||||
private:
|
||||
const Unit* _me;
|
||||
float _dist;
|
||||
bool _playerOnly;
|
||||
bool _inLos;
|
||||
};
|
||||
|
||||
class TC_GAME_API UnitAI
|
||||
{
|
||||
protected:
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "Group.h"
|
||||
#include "SmartAI.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Vehicle.h"
|
||||
|
||||
SmartAI::SmartAI(Creature* c) : CreatureAI(c)
|
||||
{
|
||||
@@ -36,6 +37,7 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
|
||||
mCurrentWPID = 0;//first wp id is 1 !!
|
||||
mWPReached = false;
|
||||
mWPPauseTimer = 0;
|
||||
mEscortNPCFlags = 0;
|
||||
mLastWP = nullptr;
|
||||
|
||||
mCanRepeatPath = false;
|
||||
@@ -69,6 +71,8 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
|
||||
mInvincibilityHpLevel = 0;
|
||||
|
||||
mJustReset = false;
|
||||
mConditionsTimer = 0;
|
||||
mHasConditions = sConditionMgr->HasConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, c->GetEntry());
|
||||
}
|
||||
|
||||
bool SmartAI::IsAIControlled() const
|
||||
@@ -76,7 +80,7 @@ bool SmartAI::IsAIControlled() const
|
||||
return !mIsCharmed;
|
||||
}
|
||||
|
||||
void SmartAI::UpdateDespawn(const uint32 diff)
|
||||
void SmartAI::UpdateDespawn(uint32 diff)
|
||||
{
|
||||
if (mDespawnState <= 1 || mDespawnState > 3)
|
||||
return;
|
||||
@@ -113,28 +117,39 @@ WayPoint* SmartAI::GetNextWayPoint()
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SmartAI::StartPath(bool run, uint32 path, bool repeat, Unit* /*invoker*/)
|
||||
void SmartAI::StartPath(bool run, uint32 path, bool repeat, Unit* invoker)
|
||||
{
|
||||
if (me->IsInCombat())// no wp movement in combat
|
||||
{
|
||||
TC_LOG_ERROR("misc", "SmartAI::StartPath: Creature entry %u wanted to start waypoint movement while in combat, ignoring.", me->GetEntry());
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasEscortState(SMART_ESCORT_ESCORTING))
|
||||
StopPath();
|
||||
|
||||
if (path)
|
||||
{
|
||||
if (!LoadPath(path))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mWayPoints || mWayPoints->empty())
|
||||
return;
|
||||
|
||||
AddEscortState(SMART_ESCORT_ESCORTING);
|
||||
mCanRepeatPath = repeat;
|
||||
|
||||
SetRun(run);
|
||||
|
||||
if (WayPoint* wp = GetNextWayPoint())
|
||||
{
|
||||
AddEscortState(SMART_ESCORT_ESCORTING);
|
||||
mCanRepeatPath = repeat;
|
||||
|
||||
SetRun(run);
|
||||
|
||||
if (invoker && invoker->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
mEscortNPCFlags = me->GetUInt32Value(UNIT_NPC_FLAGS);
|
||||
me->SetUInt32Value(UNIT_NPC_FLAGS, 0);
|
||||
}
|
||||
|
||||
mLastOOCPos = me->GetPosition();
|
||||
me->GetMotionMaster()->MovePoint(wp->id, wp->x, wp->y, wp->z);
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_WAYPOINT_START, nullptr, wp->id, GetScript()->GetPathId());
|
||||
@@ -205,6 +220,12 @@ void SmartAI::EndPath(bool fail)
|
||||
mWPPauseTimer = 0;
|
||||
mLastWP = nullptr;
|
||||
|
||||
if (mEscortNPCFlags)
|
||||
{
|
||||
me->SetUInt32Value(UNIT_NPC_FLAGS, mEscortNPCFlags);
|
||||
mEscortNPCFlags = 0;
|
||||
}
|
||||
|
||||
if (mCanRepeatPath)
|
||||
{
|
||||
if (IsAIControlled())
|
||||
@@ -282,12 +303,21 @@ void SmartAI::UpdatePath(const uint32 diff)
|
||||
return;
|
||||
if (mEscortInvokerCheckTimer < diff)
|
||||
{
|
||||
// Escort failed, no players in range
|
||||
if (!IsEscortInvokerInRange())
|
||||
{
|
||||
StopPath(mDespawnTime, mEscortQuestID, true);
|
||||
StopPath(0, mEscortQuestID, true);
|
||||
|
||||
// allow to properly hook out of range despawn action, which in most cases should perform the same operation as dying
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_DEATH, me);
|
||||
me->DespawnOrUnsummon(1);
|
||||
return;
|
||||
}
|
||||
mEscortInvokerCheckTimer = 1000;
|
||||
} else mEscortInvokerCheckTimer -= diff;
|
||||
}
|
||||
else
|
||||
mEscortInvokerCheckTimer -= diff;
|
||||
|
||||
// handle pause
|
||||
if (HasEscortState(SMART_ESCORT_PAUSED))
|
||||
{
|
||||
@@ -306,11 +336,13 @@ void SmartAI::UpdatePath(const uint32 diff)
|
||||
if (mLastWPIDReached == SMART_ESCORT_LAST_OOC_POINT)
|
||||
mWPReached = true;
|
||||
}
|
||||
|
||||
mWPPauseTimer = 0;
|
||||
} else {
|
||||
mWPPauseTimer -= diff;
|
||||
}
|
||||
else
|
||||
mWPPauseTimer -= diff;
|
||||
}
|
||||
|
||||
if (HasEscortState(SMART_ESCORT_RETURNING))
|
||||
{
|
||||
if (mWPReached)//reached OOC WP
|
||||
@@ -321,8 +353,10 @@ void SmartAI::UpdatePath(const uint32 diff)
|
||||
mWPReached = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!me->HasReactState(REACT_PASSIVE) && me->IsInCombat()) || HasEscortState(SMART_ESCORT_PAUSED | SMART_ESCORT_RETURNING))
|
||||
return;
|
||||
|
||||
// handle next wp
|
||||
if (mWPReached)//reached WP
|
||||
{
|
||||
@@ -341,6 +375,7 @@ void SmartAI::UpdatePath(const uint32 diff)
|
||||
|
||||
void SmartAI::UpdateAI(uint32 diff)
|
||||
{
|
||||
CheckConditions(diff);
|
||||
GetScript()->OnUpdate(diff);
|
||||
UpdatePath(diff);
|
||||
UpdateDespawn(diff);
|
||||
@@ -352,7 +387,7 @@ void SmartAI::UpdateAI(uint32 diff)
|
||||
{
|
||||
if (me->FindNearestCreature(mFollowArrivedEntry, INTERACTION_DISTANCE, true))
|
||||
{
|
||||
StopFollow();
|
||||
StopFollow(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -377,18 +412,19 @@ bool SmartAI::IsEscortInvokerInRange()
|
||||
ObjectList* targets = GetScript()->GetTargetList(SMART_ESCORT_TARGETS);
|
||||
if (targets)
|
||||
{
|
||||
float checkDist = me->GetInstanceScript() ? SMART_ESCORT_MAX_PLAYER_DIST * 2 : SMART_ESCORT_MAX_PLAYER_DIST;
|
||||
if (targets->size() == 1 && GetScript()->IsPlayer((*targets->begin())))
|
||||
{
|
||||
Player* player = (*targets->begin())->ToPlayer();
|
||||
if (me->GetDistance(player) <= SMART_ESCORT_MAX_PLAYER_DIST)
|
||||
return true;
|
||||
if (me->GetDistance(player) <= checkDist)
|
||||
return true;
|
||||
|
||||
if (Group* group = player->GetGroup())
|
||||
{
|
||||
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
|
||||
{
|
||||
Player* groupGuy = groupRef->GetSource();
|
||||
if (groupGuy->IsInMap(player) && me->GetDistance(groupGuy) <= SMART_ESCORT_MAX_PLAYER_DIST)
|
||||
if (groupGuy->IsInMap(player) && me->GetDistance(groupGuy) <= checkDist)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -399,13 +435,18 @@ bool SmartAI::IsEscortInvokerInRange()
|
||||
{
|
||||
if (GetScript()->IsPlayer((*iter)))
|
||||
{
|
||||
if (me->GetDistance((*iter)->ToPlayer()) <= SMART_ESCORT_MAX_PLAYER_DIST)
|
||||
if (me->GetDistance((*iter)->ToPlayer()) <= checkDist)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// no valid target found
|
||||
return false;
|
||||
}
|
||||
return true;//escort targets were not set, ignore range check
|
||||
|
||||
// no player invoker was stored, just ignore range check
|
||||
return true;
|
||||
}
|
||||
|
||||
void SmartAI::MovepointReached(uint32 id)
|
||||
@@ -436,8 +477,15 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsAIControlled())
|
||||
{
|
||||
me->AttackStop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_EnterEvadeMode())
|
||||
return;
|
||||
|
||||
me->AddUnitState(UNIT_STATE_EVADE);
|
||||
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_EVADE);//must be after aura clear so we can cast spells from db
|
||||
@@ -504,7 +552,7 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who)
|
||||
return false;
|
||||
|
||||
//never attack friendly
|
||||
if (me->IsFriendlyTo(who))
|
||||
if (!me->IsValidAssistTarget(who->GetVictim()))
|
||||
return false;
|
||||
|
||||
//too far away and no free sight?
|
||||
@@ -607,6 +655,14 @@ void SmartAI::JustSummoned(Creature* creature)
|
||||
|
||||
void SmartAI::AttackStart(Unit* who)
|
||||
{
|
||||
// dont allow charmed npcs to act on their own
|
||||
if (!IsAIControlled())
|
||||
{
|
||||
if (who && mCanAutoAttack)
|
||||
me->Attack(who, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (who && me->Attack(who, me->IsWithinMeleeRange(who)))
|
||||
{
|
||||
if (mCanCombatMove)
|
||||
@@ -674,9 +730,11 @@ void SmartAI::InitializeAI()
|
||||
{
|
||||
GetScript()->OnInitialize(me);
|
||||
if (!me->isDead())
|
||||
mJustReset = true;
|
||||
JustReachedHome();
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
|
||||
{
|
||||
mJustReset = true;
|
||||
JustReachedHome();
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
|
||||
}
|
||||
}
|
||||
|
||||
void SmartAI::OnCharmed(bool apply)
|
||||
@@ -731,11 +789,16 @@ void SmartAI::SetRun(bool run)
|
||||
mRun = run;
|
||||
}
|
||||
|
||||
void SmartAI::SetFly(bool fly)
|
||||
void SmartAI::SetDisableGravity(bool fly)
|
||||
{
|
||||
me->SetDisableGravity(fly);
|
||||
}
|
||||
|
||||
void SmartAI::SetCanFly(bool fly)
|
||||
{
|
||||
me->SetCanFly(fly);
|
||||
}
|
||||
|
||||
void SmartAI::SetSwim(bool swim)
|
||||
{
|
||||
me->SetSwim(swim);
|
||||
@@ -809,13 +872,13 @@ void SmartAI::SetFollow(Unit* target, float dist, float angle, uint32 credit, ui
|
||||
{
|
||||
if (!target)
|
||||
{
|
||||
StopFollow();
|
||||
StopFollow(false);
|
||||
return;
|
||||
}
|
||||
|
||||
mFollowGuid = target->GetGUID();
|
||||
mFollowDist = dist >= 0.0f ? dist : PET_FOLLOW_DIST;
|
||||
mFollowAngle = angle >= 0.0f ? angle : me->GetFollowAngle();
|
||||
mFollowDist = dist;
|
||||
mFollowAngle = angle;
|
||||
mFollowArrivedTimer = 1000;
|
||||
mFollowCredit = credit;
|
||||
mFollowArrivedEntry = end;
|
||||
@@ -824,8 +887,21 @@ void SmartAI::SetFollow(Unit* target, float dist, float angle, uint32 credit, ui
|
||||
me->GetMotionMaster()->MoveFollow(target, mFollowDist, mFollowAngle);
|
||||
}
|
||||
|
||||
void SmartAI::StopFollow()
|
||||
void SmartAI::StopFollow(bool complete)
|
||||
{
|
||||
mFollowGuid.Clear();
|
||||
mFollowDist = 0;
|
||||
mFollowAngle = 0;
|
||||
mFollowCredit = 0;
|
||||
mFollowArrivedTimer = 1000;
|
||||
mFollowArrivedEntry = 0;
|
||||
mFollowCreditType = 0;
|
||||
me->StopMoving();
|
||||
me->GetMotionMaster()->MoveIdle();
|
||||
|
||||
if (!complete)
|
||||
return;
|
||||
|
||||
if (Player* player = ObjectAccessor::GetPlayer(*me, mFollowGuid))
|
||||
{
|
||||
if (!mFollowCreditType)
|
||||
@@ -834,19 +910,11 @@ void SmartAI::StopFollow()
|
||||
player->GroupEventHappens(mFollowCredit, me);
|
||||
}
|
||||
|
||||
mFollowGuid.Clear();
|
||||
mFollowDist = 0;
|
||||
mFollowAngle = 0;
|
||||
mFollowCredit = 0;
|
||||
mFollowArrivedTimer = 1000;
|
||||
mFollowArrivedEntry = 0;
|
||||
mFollowCreditType = 0;
|
||||
SetDespawnTime(5000);
|
||||
me->StopMoving();
|
||||
me->GetMotionMaster()->MoveIdle();
|
||||
StartDespawn();
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_FOLLOW_COMPLETED);
|
||||
}
|
||||
|
||||
void SmartAI::SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker)
|
||||
{
|
||||
if (invoker)
|
||||
@@ -867,6 +935,35 @@ void SmartAI::OnSpellClick(Unit* clicker, bool& result)
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_ON_SPELLCLICK, clicker);
|
||||
}
|
||||
|
||||
void SmartAI::CheckConditions(uint32 diff)
|
||||
{
|
||||
if (!mHasConditions)
|
||||
return;
|
||||
|
||||
if (mConditionsTimer <= diff)
|
||||
{
|
||||
if (Vehicle* vehicleKit = me->GetVehicleKit())
|
||||
{
|
||||
for (SeatMap::iterator itr = vehicleKit->Seats.begin(); itr != vehicleKit->Seats.end(); ++itr)
|
||||
if (Unit* passenger = ObjectAccessor::GetUnit(*me, itr->second.Passenger.Guid))
|
||||
{
|
||||
if (Player* player = passenger->ToPlayer())
|
||||
{
|
||||
if (!sConditionMgr->IsObjectMeetingNotGroupedConditions(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry(), player, me))
|
||||
{
|
||||
player->ExitVehicle();
|
||||
return; // check other pessanger in next tick
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mConditionsTimer = 1000;
|
||||
}
|
||||
else
|
||||
mConditionsTimer -= diff;
|
||||
}
|
||||
|
||||
int SmartGameObjectAI::Permissible(const GameObject* g)
|
||||
{
|
||||
if (g->GetAIName() == "SmartGameObjectAI")
|
||||
@@ -882,20 +979,26 @@ void SmartGameObjectAI::UpdateAI(uint32 diff)
|
||||
void SmartGameObjectAI::InitializeAI()
|
||||
{
|
||||
GetScript()->OnInitialize(go);
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
|
||||
|
||||
// do not call respawn event if go is not spawned
|
||||
if (go->isSpawned())
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
|
||||
//Reset();
|
||||
}
|
||||
|
||||
void SmartGameObjectAI::Reset()
|
||||
{
|
||||
// call respawn event on reset
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
|
||||
|
||||
GetScript()->OnReset();
|
||||
}
|
||||
|
||||
// Called when a player opens a gossip dialog with the gameobject.
|
||||
bool SmartGameObjectAI::GossipHello(Player* player)
|
||||
bool SmartGameObjectAI::GossipHello(Player* player, bool reportUse)
|
||||
{
|
||||
TC_LOG_DEBUG("scripts.ai", "SmartGameObjectAI::GossipHello");
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_HELLO, player, 0, 0, false, nullptr, go);
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_HELLO, player, uint32(reportUse), 0, false, nullptr, go);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -959,6 +1062,11 @@ void SmartGameObjectAI::EventInform(uint32 eventId)
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_GO_EVENT_INFORM, nullptr, eventId);
|
||||
}
|
||||
|
||||
void SmartGameObjectAI::SpellHit(Unit* unit, const SpellInfo* spellInfo)
|
||||
{
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_SPELLHIT, unit, 0, 0, false, spellInfo);
|
||||
}
|
||||
|
||||
class SmartTrigger : public AreaTriggerScript
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -38,7 +38,7 @@ enum SmartEscortState
|
||||
|
||||
enum SmartEscortVars
|
||||
{
|
||||
SMART_ESCORT_MAX_PLAYER_DIST = 50,
|
||||
SMART_ESCORT_MAX_PLAYER_DIST = 60,
|
||||
SMART_MAX_AID_DIST = SMART_ESCORT_MAX_PLAYER_DIST / 2
|
||||
};
|
||||
|
||||
@@ -66,7 +66,7 @@ class TC_GAME_API SmartAI : public CreatureAI
|
||||
void SetCombatMove(bool on);
|
||||
bool CanCombatMove() { return mCanCombatMove; }
|
||||
void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0);
|
||||
void StopFollow();
|
||||
void StopFollow(bool complete);
|
||||
|
||||
void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker);
|
||||
SmartScript* GetScript() { return &mScript; }
|
||||
@@ -168,7 +168,9 @@ class TC_GAME_API SmartAI : public CreatureAI
|
||||
// Makes the creature run/walk
|
||||
void SetRun(bool run = true);
|
||||
|
||||
void SetFly(bool fly = true);
|
||||
void SetCanFly(bool fly = true);
|
||||
|
||||
void SetDisableGravity(bool disable = true);
|
||||
|
||||
void SetSwim(bool swim = true);
|
||||
|
||||
@@ -196,6 +198,8 @@ class TC_GAME_API SmartAI : public CreatureAI
|
||||
|
||||
void OnSpellClick(Unit* clicker, bool& result) override;
|
||||
|
||||
void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
|
||||
|
||||
private:
|
||||
bool mIsCharmed;
|
||||
uint32 mFollowCreditType;
|
||||
@@ -215,6 +219,7 @@ class TC_GAME_API SmartAI : public CreatureAI
|
||||
uint32 mLastWPIDReached;
|
||||
bool mWPReached;
|
||||
uint32 mWPPauseTimer;
|
||||
uint32 mEscortNPCFlags;
|
||||
WayPoint* mLastWP;
|
||||
Position mLastOOCPos;//set on enter combat
|
||||
uint32 GetWPCount() const { return mWayPoints ? uint32(mWayPoints->size()) : 0; }
|
||||
@@ -229,9 +234,14 @@ class TC_GAME_API SmartAI : public CreatureAI
|
||||
|
||||
uint32 mDespawnTime;
|
||||
uint32 mDespawnState;
|
||||
void UpdateDespawn(const uint32 diff);
|
||||
void UpdateDespawn(uint32 diff);
|
||||
uint32 mEscortInvokerCheckTimer;
|
||||
bool mJustReset;
|
||||
|
||||
// Vehicle conditions
|
||||
void CheckConditions(uint32 diff);
|
||||
bool mHasConditions;
|
||||
uint32 mConditionsTimer;
|
||||
};
|
||||
|
||||
class TC_GAME_API SmartGameObjectAI : public GameObjectAI
|
||||
@@ -246,7 +256,7 @@ class TC_GAME_API SmartGameObjectAI : public GameObjectAI
|
||||
SmartScript* GetScript() { return &mScript; }
|
||||
static int Permissible(const GameObject* g);
|
||||
|
||||
bool GossipHello(Player* player) override;
|
||||
bool GossipHello(Player* player, bool reportUse) override;
|
||||
bool GossipSelect(Player* player, uint32 sender, uint32 action) override;
|
||||
bool GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, const char* /*code*/) override;
|
||||
bool QuestAccept(Player* player, Quest const* quest) override;
|
||||
@@ -257,6 +267,7 @@ class TC_GAME_API SmartGameObjectAI : public GameObjectAI
|
||||
void OnGameEvent(bool start, uint16 eventId) override;
|
||||
void OnStateChanged(uint32 state, Unit* unit) override;
|
||||
void EventInform(uint32 eventId) override;
|
||||
void SpellHit(Unit* unit, const SpellInfo* spellInfo) override;
|
||||
|
||||
private:
|
||||
SmartScript mScript;
|
||||
|
||||
@@ -114,8 +114,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
case SMART_ACTION_TALK:
|
||||
{
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
Creature* talker = me;
|
||||
Player* targetPlayer = nullptr;
|
||||
Creature* talker = e.target.type == 0 ? me : nullptr;
|
||||
Unit* talkTarget = nullptr;
|
||||
|
||||
if (targets)
|
||||
@@ -125,14 +124,18 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (IsCreature(*itr) && !(*itr)->ToCreature()->IsPet()) // Prevented sending text to pets.
|
||||
{
|
||||
if (e.action.talk.useTalkTarget)
|
||||
{
|
||||
talker = me;
|
||||
talkTarget = (*itr)->ToCreature();
|
||||
}
|
||||
else
|
||||
talker = (*itr)->ToCreature();
|
||||
break;
|
||||
}
|
||||
else if (IsPlayer(*itr))
|
||||
{
|
||||
targetPlayer = (*itr)->ToPlayer();
|
||||
talker = me;
|
||||
talkTarget = (*itr)->ToPlayer();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -140,18 +143,15 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
delete targets;
|
||||
}
|
||||
|
||||
if (!talkTarget)
|
||||
talkTarget = GetLastInvoker();
|
||||
|
||||
if (!talker)
|
||||
break;
|
||||
|
||||
mTalkerEntry = talker->GetEntry();
|
||||
mLastTextID = e.action.talk.textGroupID;
|
||||
mTextTimer = e.action.talk.duration;
|
||||
|
||||
if (IsPlayer(GetLastInvoker())) // used for $vars in texts and whisper target
|
||||
talkTarget = GetLastInvoker();
|
||||
else if (targetPlayer)
|
||||
talkTarget = targetPlayer;
|
||||
|
||||
mUseTextTimer = true;
|
||||
sCreatureTextMgr->SendChat(talker, uint8(e.action.talk.textGroupID), talkTarget);
|
||||
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_TALK: talker: %s (GuidLow: %u), textGuid: %u",
|
||||
@@ -482,8 +482,17 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
if (e.action.cast.targetsLimit > 0 && targets->size() > e.action.cast.targetsLimit)
|
||||
Trinity::Containers::RandomResizeList(*targets, e.action.cast.targetsLimit);
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
{
|
||||
if (go)
|
||||
{
|
||||
// may be nullptr
|
||||
go->CastSpell((*itr)->ToUnit(), e.action.cast.spell);
|
||||
}
|
||||
|
||||
if (!IsUnit(*itr))
|
||||
continue;
|
||||
|
||||
@@ -546,6 +555,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
if (e.action.cast.targetsLimit > 0 && targets->size() > e.action.cast.targetsLimit)
|
||||
Trinity::Containers::RandomResizeList(*targets, e.action.cast.targetsLimit);
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
{
|
||||
if (!IsUnit(*itr))
|
||||
@@ -768,7 +780,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
|
||||
me->DoFleeToGetAssistance();
|
||||
|
||||
if (e.action.flee.withEmote)
|
||||
if (e.action.fleeAssist.withEmote)
|
||||
{
|
||||
Trinity::BroadcastTextBuilder builder(me, CHAT_MSG_MONSTER_EMOTE, BROADCAST_TEXT_FLEE_FOR_ASSIST);
|
||||
sCreatureTextMgr->SendChatPacket(me, builder, CHAT_MSG_MONSTER_EMOTE);
|
||||
@@ -781,9 +793,11 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (!unit)
|
||||
break;
|
||||
|
||||
if (IsPlayer(unit) && GetBaseObject())
|
||||
// If invoker was pet or charm
|
||||
Player* player = unit->GetCharmerOrOwnerPlayerOrPlayerItself();
|
||||
if (player && GetBaseObject())
|
||||
{
|
||||
unit->ToPlayer()->GroupEventHappens(e.action.quest.quest, GetBaseObject());
|
||||
player->GroupEventHappens(e.action.quest.quest, GetBaseObject());
|
||||
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_CALL_GROUPEVENTHAPPENS: Player %u, group credit for quest %u",
|
||||
unit->GetGUID().GetCounter(), e.action.quest.quest);
|
||||
}
|
||||
@@ -843,7 +857,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
{
|
||||
ENSURE_AI(SmartAI, me->AI())->StopFollow();
|
||||
ENSURE_AI(SmartAI, me->AI())->StopFollow(false);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -851,7 +865,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
{
|
||||
if (IsUnit(*itr))
|
||||
{
|
||||
ENSURE_AI(SmartAI, me->AI())->SetFollow((*itr)->ToUnit(), (float)e.action.follow.dist, (float)e.action.follow.angle, e.action.follow.credit, e.action.follow.entry, e.action.follow.creditType);
|
||||
float angle = e.action.follow.angle > 6 ? (e.action.follow.angle * M_PI / 180.0f) : e.action.follow.angle;
|
||||
ENSURE_AI(SmartAI, me->AI())->SetFollow((*itr)->ToUnit(), float(e.action.follow.dist) + 0.1f, angle, e.action.follow.credit, e.action.follow.entry, e.action.follow.creditType);
|
||||
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_FOLLOW: Creature %u following target %u",
|
||||
me->GetGUID().GetCounter(), (*itr)->GetGUID().GetCounter());
|
||||
break;
|
||||
@@ -1028,25 +1043,39 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
}
|
||||
case SMART_ACTION_SET_IN_COMBAT_WITH_ZONE:
|
||||
{
|
||||
if (me)
|
||||
{
|
||||
me->SetInCombatWithZone();
|
||||
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_IN_COMBAT_WITH_ZONE: Creature %u", me->GetGUID().GetCounter());
|
||||
}
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
if (IsCreature(*itr))
|
||||
{
|
||||
(*itr)->ToCreature()->SetInCombatWithZone();
|
||||
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_IN_COMBAT_WITH_ZONE: Creature %u, target: %u", me->GetGUID().GetCounter(), (*itr)->GetGUID().GetCounter());
|
||||
}
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_CALL_FOR_HELP:
|
||||
{
|
||||
if (me)
|
||||
{
|
||||
me->CallForHelp((float)e.action.callHelp.range);
|
||||
if (e.action.callHelp.withEmote)
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
if (IsCreature(*itr))
|
||||
{
|
||||
Trinity::BroadcastTextBuilder builder(me, CHAT_MSG_MONSTER_EMOTE, BROADCAST_TEXT_CALL_FOR_HELP);
|
||||
sCreatureTextMgr->SendChatPacket(me, builder, CHAT_MSG_MONSTER_EMOTE);
|
||||
(*itr)->ToCreature()->CallForHelp((float)e.action.callHelp.range);
|
||||
if (e.action.callHelp.withEmote)
|
||||
{
|
||||
Trinity::BroadcastTextBuilder builder(me, CHAT_MSG_MONSTER_EMOTE, BROADCAST_TEXT_CALL_FOR_HELP);
|
||||
sCreatureTextMgr->SendChatPacket(me, builder, CHAT_MSG_MONSTER_EMOTE);
|
||||
}
|
||||
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_CALL_FOR_HELP: Creature %u, target: %u", me->GetGUID().GetCounter(), (*itr)->GetGUID().GetCounter());
|
||||
}
|
||||
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_CALL_FOR_HELP: Creature %u", me->GetGUID().GetCounter());
|
||||
}
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_SHEATH:
|
||||
@@ -1147,19 +1176,26 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
}
|
||||
case SMART_ACTION_SET_INVINCIBILITY_HP_LEVEL:
|
||||
{
|
||||
if (!me)
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
SmartAI* ai = CAST_AI(SmartAI, me->AI());
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
{
|
||||
if (IsCreature(*itr))
|
||||
{
|
||||
SmartAI* ai = CAST_AI(SmartAI, (*itr)->ToCreature()->AI());
|
||||
if (!ai)
|
||||
continue;
|
||||
|
||||
if (!ai)
|
||||
break;
|
||||
|
||||
if (e.action.invincHP.percent)
|
||||
ai->SetInvincibilityHpLevel(me->CountPctFromMaxHealth(e.action.invincHP.percent));
|
||||
else
|
||||
ai->SetInvincibilityHpLevel(e.action.invincHP.minHP);
|
||||
if (e.action.invincHP.percent)
|
||||
ai->SetInvincibilityHpLevel((*itr)->ToCreature()->CountPctFromMaxHealth(e.action.invincHP.percent));
|
||||
else
|
||||
ai->SetInvincibilityHpLevel(e.action.invincHP.minHP);
|
||||
}
|
||||
}
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_DATA:
|
||||
@@ -1209,15 +1245,27 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
}
|
||||
case SMART_ACTION_SET_VISIBILITY:
|
||||
{
|
||||
if (me)
|
||||
me->SetVisible(e.action.visibility.state != 0);
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
if (IsUnit(*itr))
|
||||
(*itr)->ToUnit()->SetVisible(e.action.visibility.state ? true : false);
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_ACTIVE:
|
||||
{
|
||||
if (WorldObject* baseObj = GetBaseObject())
|
||||
baseObj->setActive(e.action.active.state != 0);
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
(*itr)->setActive(e.action.active.state ? true : false);
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_ATTACK_START:
|
||||
@@ -1229,14 +1277,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
{
|
||||
if (IsUnit(*itr))
|
||||
{
|
||||
me->AI()->AttackStart((*itr)->ToUnit());
|
||||
break;
|
||||
}
|
||||
}
|
||||
// attack random target
|
||||
if (Unit* target = Trinity::Containers::SelectRandomContainerElement(*targets)->ToUnit())
|
||||
me->AI()->AttackStart(target);
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
@@ -1280,8 +1323,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
{
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
{
|
||||
if (!IsUnit(*itr))
|
||||
continue;
|
||||
// allow gameobjects to summon gameobjects
|
||||
//if (!IsUnit(*itr))
|
||||
// continue;
|
||||
|
||||
Position pos = (*itr)->GetPositionWithOffset(Position(e.target.x, e.target.y, e.target.z, e.target.o));
|
||||
GetBaseObject()->SummonGameObject(e.action.summonGO.entry, pos, G3D::Quat(), e.action.summonGO.despawnTime);
|
||||
@@ -1375,12 +1419,20 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_FLY:
|
||||
case SMART_ACTION_SET_DISABLE_GRAVITY:
|
||||
{
|
||||
if (!IsSmart())
|
||||
break;
|
||||
|
||||
ENSURE_AI(SmartAI, me->AI())->SetFly(e.action.setFly.fly != 0);
|
||||
ENSURE_AI(SmartAI, me->AI())->SetDisableGravity(e.action.setDisableGravity.disable != 0);
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_CAN_FLY:
|
||||
{
|
||||
if (!IsSmart())
|
||||
break;
|
||||
|
||||
ENSURE_AI(SmartAI, me->AI())->SetCanFly(e.action.setFly.fly != 0);
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_RUN:
|
||||
@@ -1436,8 +1488,25 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
bool run = e.action.wpStart.run != 0;
|
||||
uint32 entry = e.action.wpStart.pathID;
|
||||
bool repeat = e.action.wpStart.repeat != 0;
|
||||
|
||||
// ensure that SMART_ESCORT_TARGETS contains at least one player reference
|
||||
bool stored = false;
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
StoreTargetList(targets, SMART_ESCORT_TARGETS);
|
||||
if (targets)
|
||||
{
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
{
|
||||
if (IsPlayer(*itr))
|
||||
{
|
||||
stored = true;
|
||||
StoreTargetList(targets, SMART_ESCORT_TARGETS);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!stored)
|
||||
delete targets;
|
||||
}
|
||||
|
||||
me->SetReactState((ReactStates)e.action.wpStart.reactState);
|
||||
ENSURE_AI(SmartAI, me->AI())->StartPath(run, entry, repeat, unit);
|
||||
|
||||
@@ -1472,7 +1541,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (!IsSmart())
|
||||
break;
|
||||
|
||||
ENSURE_AI(SmartAI, me->AI())->ResumePath();
|
||||
ENSURE_AI(SmartAI, me->AI())->SetWPPauseTimer(0);
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_ORIENTATION:
|
||||
@@ -1518,19 +1587,20 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
|
||||
WorldObject* target = nullptr;
|
||||
|
||||
if (e.GetTargetType() == SMART_TARGET_CREATURE_RANGE || e.GetTargetType() == SMART_TARGET_CREATURE_GUID ||
|
||||
e.GetTargetType() == SMART_TARGET_CREATURE_DISTANCE || e.GetTargetType() == SMART_TARGET_GAMEOBJECT_RANGE ||
|
||||
e.GetTargetType() == SMART_TARGET_GAMEOBJECT_GUID || e.GetTargetType() == SMART_TARGET_GAMEOBJECT_DISTANCE ||
|
||||
e.GetTargetType() == SMART_TARGET_CLOSEST_CREATURE || e.GetTargetType() == SMART_TARGET_CLOSEST_GAMEOBJECT ||
|
||||
e.GetTargetType() == SMART_TARGET_OWNER_OR_SUMMONER || e.GetTargetType() == SMART_TARGET_ACTION_INVOKER ||
|
||||
e.GetTargetType() == SMART_TARGET_CLOSEST_ENEMY || e.GetTargetType() == SMART_TARGET_CLOSEST_FRIENDLY)
|
||||
/*if (e.GetTargetType() == SMART_TARGET_CREATURE_RANGE || e.GetTargetType() == SMART_TARGET_CREATURE_GUID ||
|
||||
e.GetTargetType() == SMART_TARGET_CREATURE_DISTANCE || e.GetTargetType() == SMART_TARGET_GAMEOBJECT_RANGE ||
|
||||
e.GetTargetType() == SMART_TARGET_GAMEOBJECT_GUID || e.GetTargetType() == SMART_TARGET_GAMEOBJECT_DISTANCE ||
|
||||
e.GetTargetType() == SMART_TARGET_CLOSEST_CREATURE || e.GetTargetType() == SMART_TARGET_CLOSEST_GAMEOBJECT ||
|
||||
e.GetTargetType() == SMART_TARGET_OWNER_OR_SUMMONER || e.GetTargetType() == SMART_TARGET_ACTION_INVOKER ||
|
||||
e.GetTargetType() == SMART_TARGET_CLOSEST_ENEMY || e.GetTargetType() == SMART_TARGET_CLOSEST_FRIENDLY ||
|
||||
e.GetTargetType() == SMART_TARGET_SELF || e.GetTargetType() == SMART_TARGET_STORED)*/
|
||||
{
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
target = targets->front();
|
||||
delete targets;
|
||||
if (ObjectList* targets = GetTargets(e, unit))
|
||||
{
|
||||
// we want to move to random element
|
||||
target = Trinity::Containers::SelectRandomContainerElement(*targets);
|
||||
delete targets;
|
||||
}
|
||||
}
|
||||
|
||||
if (!target)
|
||||
@@ -1548,7 +1618,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
target->GetPosition(x, y, z);
|
||||
if (e.action.MoveToPos.ContactDistance > 0)
|
||||
target->GetContactPoint(me, x, y, z, e.action.MoveToPos.ContactDistance);
|
||||
me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, x, y, z, e.action.MoveToPos.disablePathfinding == 0);
|
||||
me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, x + e.target.x, y + e.target.y, z + e.target.z, e.action.MoveToPos.disablePathfinding == 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1563,7 +1633,13 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (IsCreature(*itr))
|
||||
(*itr)->ToCreature()->Respawn();
|
||||
else if (IsGameObject(*itr))
|
||||
(*itr)->ToGameObject()->SetRespawnTime(e.action.RespawnTarget.goRespawnTime);
|
||||
{
|
||||
// do not modify respawndelay of already spawned gameobjects
|
||||
if ((*itr)->ToGameObject()->isSpawnedByDefault())
|
||||
(*itr)->ToGameObject()->Respawn();
|
||||
else
|
||||
(*itr)->ToGameObject()->SetRespawnTime(e.action.RespawnTarget.goRespawnTime);
|
||||
}
|
||||
}
|
||||
|
||||
delete targets;
|
||||
@@ -1657,6 +1733,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
}
|
||||
case SMART_ACTION_TRIGGER_TIMED_EVENT:
|
||||
ProcessEventsFor((SMART_EVENT)SMART_EVENT_TIMED_EVENT_TRIGGERED, nullptr, e.action.timeEvent.id);
|
||||
|
||||
// remove this event if not repeatable
|
||||
if (e.event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE)
|
||||
mRemIDs.push_back(e.action.timeEvent.id);
|
||||
break;
|
||||
case SMART_ACTION_REMOVE_TIMED_EVENT:
|
||||
mRemIDs.push_back(e.action.timeEvent.id);
|
||||
@@ -2431,6 +2511,109 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
ENSURE_AI(SmartAI, me->AI())->SetEvadeDisabled(e.action.disableEvade.disable != 0);
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_REMOVE_AURAS_BY_TYPE: // can be used to exit vehicle for example
|
||||
{
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
if (IsUnit(*itr))
|
||||
(*itr)->ToUnit()->RemoveAurasByType((AuraType)e.action.auraType.type);
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_SIGHT_DIST:
|
||||
{
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
if (IsCreature(*itr))
|
||||
(*itr)->ToCreature()->m_SightDistance = e.action.sightDistance.dist;
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_FLEE:
|
||||
{
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
if (IsCreature(*itr))
|
||||
(*itr)->ToCreature()->GetMotionMaster()->MoveFleeing(me, e.action.flee.fleeTime);
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_ADD_THREAT:
|
||||
{
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
if (IsUnit(*itr))
|
||||
me->AddThreat((*itr)->ToUnit(), (float)e.action.threatPCT.threatINC - (float)e.action.threatPCT.threatDEC);
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_LOAD_EQUIPMENT:
|
||||
{
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
if (IsCreature(*itr))
|
||||
(*itr)->ToCreature()->LoadEquipment(e.action.loadEquipment.id, e.action.loadEquipment.force != 0);
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT:
|
||||
{
|
||||
uint32 eventId = urand(e.action.randomTimedEvent.minId, e.action.randomTimedEvent.maxId);
|
||||
ProcessEventsFor((SMART_EVENT)SMART_EVENT_TIMED_EVENT_TRIGGERED, NULL, eventId);
|
||||
break;
|
||||
}
|
||||
|
||||
case SMART_ACTION_REMOVE_ALL_GAMEOBJECTS:
|
||||
{
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
if (IsUnit(*itr))
|
||||
(*itr)->ToUnit()->RemoveAllGameObjects();
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_STOP_MOTION:
|
||||
{
|
||||
ObjectList* targets = GetTargets(e, unit);
|
||||
if (!targets)
|
||||
break;
|
||||
|
||||
for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr)
|
||||
if (IsUnit(*itr))
|
||||
{
|
||||
if (e.action.stopMotion.stopMovement)
|
||||
(*itr)->ToUnit()->StopMoving();
|
||||
if (e.action.stopMotion.movementExpired)
|
||||
(*itr)->ToUnit()->GetMotionMaster()->MovementExpired();
|
||||
}
|
||||
|
||||
delete targets;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry %d SourceType %u, Event %u, Unhandled Action type %u", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
|
||||
break;
|
||||
@@ -2448,10 +2631,14 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
|
||||
void SmartScript::ProcessTimedAction(SmartScriptHolder& e, uint32 const& min, uint32 const& max, Unit* unit, uint32 var0, uint32 var1, bool bvar, const SpellInfo* spell, GameObject* gob)
|
||||
{
|
||||
// We may want to execute action rarely and because of this if condition is not fulfilled the action will be rechecked in a long time
|
||||
if (sConditionMgr->IsObjectMeetingSmartEventConditions(e.entryOrGuid, e.event_id, e.source_type, unit, GetBaseObject()))
|
||||
{
|
||||
ProcessAction(e, unit, var0, var1, bvar, spell, gob);
|
||||
|
||||
RecalcTimer(e, min, max);
|
||||
RecalcTimer(e, min, max);
|
||||
}
|
||||
else
|
||||
RecalcTimer(e, std::min<uint32>(min, 5000), std::min<uint32>(min, 5000));
|
||||
}
|
||||
|
||||
void SmartScript::InstallTemplate(SmartScriptHolder const& e)
|
||||
@@ -2587,23 +2774,58 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
|
||||
break;
|
||||
case SMART_TARGET_HOSTILE_SECOND_AGGRO:
|
||||
if (me)
|
||||
if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_TOPAGGRO, 1))
|
||||
{
|
||||
if (e.target.hostilRandom.powerType)
|
||||
{
|
||||
if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_TOPAGGRO, 1, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0)))
|
||||
l->push_back(u);
|
||||
}
|
||||
else if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_TOPAGGRO, 1, (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0))
|
||||
l->push_back(u);
|
||||
}
|
||||
break;
|
||||
case SMART_TARGET_HOSTILE_LAST_AGGRO:
|
||||
if (me)
|
||||
if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_BOTTOMAGGRO, 0))
|
||||
{
|
||||
if (e.target.hostilRandom.powerType)
|
||||
{
|
||||
if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_BOTTOMAGGRO, 0, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0)))
|
||||
l->push_back(u);
|
||||
}
|
||||
else if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_BOTTOMAGGRO, 0, (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0))
|
||||
l->push_back(u);
|
||||
}
|
||||
break;
|
||||
case SMART_TARGET_HOSTILE_RANDOM:
|
||||
if (me)
|
||||
if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0))
|
||||
{
|
||||
if (e.target.hostilRandom.powerType)
|
||||
{
|
||||
if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0)))
|
||||
l->push_back(u);
|
||||
}
|
||||
else if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0, (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0))
|
||||
l->push_back(u);
|
||||
}
|
||||
break;
|
||||
case SMART_TARGET_HOSTILE_RANDOM_NOT_TOP:
|
||||
if (me)
|
||||
if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1))
|
||||
{
|
||||
if (e.target.hostilRandom.powerType)
|
||||
{
|
||||
if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0)))
|
||||
l->push_back(u);
|
||||
}
|
||||
else if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_RANDOM, 1, (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly != 0))
|
||||
l->push_back(u);
|
||||
}
|
||||
break;
|
||||
case SMART_TARGET_FARTHEST:
|
||||
if (me)
|
||||
{
|
||||
if (Unit* u = me->AI()->SelectTarget(SELECT_TARGET_FARTHEST, 0, FarthestTargetSelector(me, (float)e.target.farthest.maxDist, e.target.farthest.playerOnly != 0, e.target.farthest.isInLos != 0)))
|
||||
l->push_back(u);
|
||||
}
|
||||
break;
|
||||
case SMART_TARGET_ACTION_INVOKER:
|
||||
if (scriptTrigger)
|
||||
@@ -2767,7 +2989,7 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
|
||||
l->assign(objectList->begin(), objectList->end());
|
||||
}
|
||||
|
||||
return l;
|
||||
break;
|
||||
}
|
||||
case SMART_TARGET_CLOSEST_CREATURE:
|
||||
{
|
||||
@@ -2783,8 +3005,8 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
|
||||
}
|
||||
case SMART_TARGET_CLOSEST_PLAYER:
|
||||
{
|
||||
if (me)
|
||||
if (Player* target = me->SelectNearestPlayer(float(e.target.playerDistance.dist)))
|
||||
if (WorldObject* obj = GetBaseObject())
|
||||
if (Player* target = obj->SelectNearestPlayer(float(e.target.playerDistance.dist)))
|
||||
l->push_back(target);
|
||||
break;
|
||||
}
|
||||
@@ -2805,6 +3027,21 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
|
||||
if (Unit* owner = ObjectAccessor::GetUnit(*me, charmerOrOwnerGuid))
|
||||
l->push_back(owner);
|
||||
}
|
||||
else if (go)
|
||||
{
|
||||
if (Unit* owner = ObjectAccessor::GetUnit(*go, go->GetOwnerGUID()))
|
||||
l->push_back(owner);
|
||||
}
|
||||
|
||||
// Get owner of owner
|
||||
if (e.target.owner.useCharmerOrOwner && !l->empty())
|
||||
{
|
||||
Unit* owner = l->front()->ToUnit();
|
||||
l->clear();
|
||||
|
||||
if (Unit* base = ObjectAccessor::GetUnit(*owner, owner->GetCharmerOrOwnerGUID()))
|
||||
l->push_back(base);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SMART_TARGET_THREAT_LIST:
|
||||
@@ -2814,7 +3051,8 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
|
||||
ThreatContainer::StorageType threatList = me->getThreatManager().getThreatList();
|
||||
for (ThreatContainer::StorageType::const_iterator i = threatList.begin(); i != threatList.end(); ++i)
|
||||
if (Unit* temp = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
|
||||
l->push_back(temp);
|
||||
if (e.target.hostilRandom.maxDist == 0 || me->IsWithinCombatRange(temp, (float)e.target.hostilRandom.maxDist))
|
||||
l->push_back(temp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2955,6 +3193,8 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
|
||||
if (me->IsInRange(me->GetVictim(), (float)e.event.minMaxRepeat.min, (float)e.event.minMaxRepeat.max))
|
||||
ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, me->GetVictim());
|
||||
else // make it predictable
|
||||
RecalcTimer(e, 500, 500);
|
||||
break;
|
||||
}
|
||||
case SMART_EVENT_VICTIM_CASTING:
|
||||
@@ -2982,7 +3222,12 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
|
||||
Unit* target = DoSelectLowestHpFriendly((float)e.event.friendlyHealth.radius, e.event.friendlyHealth.hpDeficit);
|
||||
if (!target || !target->IsInCombat())
|
||||
{
|
||||
// if there are at least two same npcs, they will perform the same action immediately even if this is useless...
|
||||
RecalcTimer(e, 1000, 3000);
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessTimedAction(e, e.event.friendlyHealth.repeatMin, e.event.friendlyHealth.repeatMax, target);
|
||||
break;
|
||||
}
|
||||
@@ -2994,8 +3239,12 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
std::list<Creature*> pList;
|
||||
DoFindFriendlyCC(pList, (float)e.event.friendlyCC.radius);
|
||||
if (pList.empty())
|
||||
{
|
||||
// if there are at least two same npcs, they will perform the same action immediately even if this is useless...
|
||||
RecalcTimer(e, 1000, 3000);
|
||||
return;
|
||||
ProcessTimedAction(e, e.event.friendlyCC.repeatMin, e.event.friendlyCC.repeatMax, *pList.begin());
|
||||
}
|
||||
ProcessTimedAction(e, e.event.friendlyCC.repeatMin, e.event.friendlyCC.repeatMax, Trinity::Containers::SelectRandomContainerElement(pList));
|
||||
break;
|
||||
}
|
||||
case SMART_EVENT_FRIENDLY_MISSING_BUFF:
|
||||
@@ -3006,7 +3255,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
if (pList.empty())
|
||||
return;
|
||||
|
||||
ProcessTimedAction(e, e.event.missingBuff.repeatMin, e.event.missingBuff.repeatMax, *pList.begin());
|
||||
ProcessTimedAction(e, e.event.missingBuff.repeatMin, e.event.missingBuff.repeatMax, Trinity::Containers::SelectRandomContainerElement(pList));
|
||||
break;
|
||||
}
|
||||
case SMART_EVENT_HAS_AURA:
|
||||
@@ -3052,11 +3301,15 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
case SMART_EVENT_JUST_SUMMONED:
|
||||
case SMART_EVENT_RESET:
|
||||
case SMART_EVENT_JUST_CREATED:
|
||||
case SMART_EVENT_GOSSIP_HELLO:
|
||||
case SMART_EVENT_FOLLOW_COMPLETED:
|
||||
case SMART_EVENT_ON_SPELLCLICK:
|
||||
ProcessAction(e, unit, var0, var1, bvar, spell, gob);
|
||||
break;
|
||||
case SMART_EVENT_GOSSIP_HELLO:
|
||||
if (e.event.gossipHello.noReportUse && var0)
|
||||
return;
|
||||
ProcessAction(e, unit, var0, var1, bvar, spell, gob);
|
||||
break;
|
||||
case SMART_EVENT_IS_BEHIND_TARGET:
|
||||
{
|
||||
if (!me)
|
||||
@@ -3199,6 +3452,13 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
break;
|
||||
}
|
||||
case SMART_EVENT_SUMMON_DESPAWNED:
|
||||
{
|
||||
if (e.event.summoned.creature && e.event.summoned.creature != var0)
|
||||
return;
|
||||
ProcessAction(e, unit, var0);
|
||||
RecalcTimer(e, e.event.summoned.cooldownMin, e.event.summoned.cooldownMax);
|
||||
break;
|
||||
}
|
||||
case SMART_EVENT_INSTANCE_PLAYER_ENTER:
|
||||
{
|
||||
if (e.event.instancePlayerEnter.team && var0 != e.event.instancePlayerEnter.team)
|
||||
@@ -3413,8 +3673,10 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
|
||||
break;
|
||||
}
|
||||
case SMART_EVENT_COUNTER_SET:
|
||||
if (GetCounterId(e.event.counter.id) != 0 && GetCounterValue(e.event.counter.id) == e.event.counter.value)
|
||||
ProcessTimedAction(e, e.event.counter.cooldownMin, e.event.counter.cooldownMax);
|
||||
if (e.event.counter.id != var0 || GetCounterValue(e.event.counter.id) != e.event.counter.value)
|
||||
return;
|
||||
|
||||
ProcessTimedAction(e, e.event.counter.cooldownMin, e.event.counter.cooldownMax);
|
||||
break;
|
||||
default:
|
||||
TC_LOG_ERROR("sql.sql", "SmartScript::ProcessEvent: Unhandled Event type %u", e.GetEventType());
|
||||
@@ -3480,7 +3742,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
|
||||
// Delay flee for assist event if stunned or rooted
|
||||
if (e.GetActionType() == SMART_ACTION_FLEE_FOR_ASSIST)
|
||||
{
|
||||
if (me && me->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED))
|
||||
if (me && me->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_LOST_CONTROL))
|
||||
{
|
||||
e.timer = 1;
|
||||
return;
|
||||
@@ -3558,8 +3820,14 @@ void SmartScript::OnUpdate(uint32 const diff)
|
||||
UpdateTimer(*i, diff);
|
||||
|
||||
if (!mStoredEvents.empty())
|
||||
for (SmartAIEventList::iterator i = mStoredEvents.begin(); i != mStoredEvents.end(); ++i)
|
||||
UpdateTimer(*i, diff);
|
||||
{
|
||||
SmartAIEventStoredList::iterator i, icurr;
|
||||
for (i = mStoredEvents.begin(); i != mStoredEvents.end();)
|
||||
{
|
||||
icurr = i++;
|
||||
UpdateTimer(*icurr, diff);
|
||||
}
|
||||
}
|
||||
|
||||
bool needCleanup = true;
|
||||
if (!mTimedActionList.empty())
|
||||
@@ -3581,11 +3849,12 @@ void SmartScript::OnUpdate(uint32 const diff)
|
||||
|
||||
if (!mRemIDs.empty())
|
||||
{
|
||||
for (std::list<uint32>::iterator i = mRemIDs.begin(); i != mRemIDs.end(); ++i)
|
||||
{
|
||||
RemoveStoredEvent((*i));
|
||||
}
|
||||
for (auto i : mRemIDs)
|
||||
RemoveStoredEvent(i);
|
||||
|
||||
mRemIDs.clear();
|
||||
}
|
||||
|
||||
if (mUseTextTimer && me)
|
||||
{
|
||||
if (mTextTimer < diff)
|
||||
@@ -3713,18 +3982,11 @@ Unit* SmartScript::DoSelectLowestHpFriendly(float range, uint32 MinHPDiff)
|
||||
if (!me)
|
||||
return nullptr;
|
||||
|
||||
CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
|
||||
Cell cell(p);
|
||||
cell.SetNoCreate();
|
||||
|
||||
Unit* unit = nullptr;
|
||||
|
||||
Trinity::MostHPMissingInRange u_check(me, range, MinHPDiff);
|
||||
Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange> searcher(me, unit, u_check);
|
||||
|
||||
TypeContainerVisitor<Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange>, GridTypeMapContainer > grid_unit_searcher(searcher);
|
||||
|
||||
cell.Visit(p, grid_unit_searcher, *me->GetMap(), *me, range);
|
||||
me->VisitNearbyObject(range, searcher);
|
||||
return unit;
|
||||
}
|
||||
|
||||
@@ -3733,16 +3995,9 @@ void SmartScript::DoFindFriendlyCC(std::list<Creature*>& _list, float range)
|
||||
if (!me)
|
||||
return;
|
||||
|
||||
CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
|
||||
Cell cell(p);
|
||||
cell.SetNoCreate();
|
||||
|
||||
Trinity::FriendlyCCedInRange u_check(me, range);
|
||||
Trinity::CreatureListSearcher<Trinity::FriendlyCCedInRange> searcher(me, _list, u_check);
|
||||
|
||||
TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::FriendlyCCedInRange>, GridTypeMapContainer > grid_creature_searcher(searcher);
|
||||
|
||||
cell.Visit(p, grid_creature_searcher, *me->GetMap(), *me, range);
|
||||
me->VisitNearbyObject(range, searcher);
|
||||
}
|
||||
|
||||
void SmartScript::DoFindFriendlyMissingBuff(std::list<Creature*>& list, float range, uint32 spellid)
|
||||
@@ -3750,16 +4005,9 @@ void SmartScript::DoFindFriendlyMissingBuff(std::list<Creature*>& list, float ra
|
||||
if (!me)
|
||||
return;
|
||||
|
||||
CellCoord p(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY()));
|
||||
Cell cell(p);
|
||||
cell.SetNoCreate();
|
||||
|
||||
Trinity::FriendlyMissingBuffInRange u_check(me, range, spellid);
|
||||
Trinity::CreatureListSearcher<Trinity::FriendlyMissingBuffInRange> searcher(me, list, u_check);
|
||||
|
||||
TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::FriendlyMissingBuffInRange>, GridTypeMapContainer > grid_creature_searcher(searcher);
|
||||
|
||||
cell.Visit(p, grid_creature_searcher, *me->GetMap(), *me, range);
|
||||
me->VisitNearbyObject(range, searcher);
|
||||
}
|
||||
|
||||
Unit* SmartScript::DoFindClosestFriendlyInRange(float range, bool playerOnly)
|
||||
|
||||
@@ -158,29 +158,23 @@ class TC_GAME_API SmartScript
|
||||
|
||||
void StoreCounter(uint32 id, uint32 value, uint32 reset)
|
||||
{
|
||||
CounterMap::const_iterator itr = mCounterList.find(id);
|
||||
CounterMap::iterator itr = mCounterList.find(id);
|
||||
if (itr != mCounterList.end())
|
||||
{
|
||||
if (reset == 0)
|
||||
value += GetCounterValue(id);
|
||||
mCounterList.erase(id);
|
||||
itr->second += value;
|
||||
else
|
||||
itr->second = value;
|
||||
}
|
||||
else
|
||||
mCounterList.insert(std::make_pair(id, value));
|
||||
|
||||
mCounterList.insert(std::make_pair(id, value));
|
||||
ProcessEventsFor(SMART_EVENT_COUNTER_SET);
|
||||
ProcessEventsFor(SMART_EVENT_COUNTER_SET, nullptr, id);
|
||||
}
|
||||
|
||||
uint32 GetCounterId(uint32 id)
|
||||
uint32 GetCounterValue(uint32 id) const
|
||||
{
|
||||
CounterMap::iterator itr = mCounterList.find(id);
|
||||
if (itr != mCounterList.end())
|
||||
return itr->first;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 GetCounterValue(uint32 id)
|
||||
{
|
||||
CounterMap::iterator itr = mCounterList.find(id);
|
||||
CounterMap::const_iterator itr = mCounterList.find(id);
|
||||
if (itr != mCounterList.end())
|
||||
return itr->second;
|
||||
return 0;
|
||||
@@ -247,23 +241,27 @@ class TC_GAME_API SmartScript
|
||||
CounterMap mCounterList;
|
||||
|
||||
private:
|
||||
void IncPhase(int32 p = 1)
|
||||
void IncPhase(uint32 p)
|
||||
{
|
||||
if (p >= 0)
|
||||
mEventPhase += (uint32)p;
|
||||
else
|
||||
DecPhase(abs(p));
|
||||
// protect phase from overflowing
|
||||
mEventPhase = std::min<uint32>(SMART_EVENT_PHASE_12, mEventPhase + p);
|
||||
}
|
||||
|
||||
void DecPhase(int32 p = 1)
|
||||
void DecPhase(uint32 p)
|
||||
{
|
||||
if (mEventPhase > (uint32)p)
|
||||
mEventPhase -= (uint32)p;
|
||||
else
|
||||
if (p >= mEventPhase)
|
||||
mEventPhase = 0;
|
||||
else
|
||||
mEventPhase -= p;
|
||||
}
|
||||
|
||||
bool IsInPhase(uint32 p) const
|
||||
{
|
||||
if (mEventPhase == 0)
|
||||
return false;
|
||||
return ((1 << (mEventPhase - 1)) & p) != 0;
|
||||
}
|
||||
|
||||
bool IsInPhase(uint32 p) const { return ((1 << (mEventPhase - 1)) & p) != 0; }
|
||||
void SetPhase(uint32 p = 0) { mEventPhase = p; }
|
||||
|
||||
SmartAIEventList mEvents;
|
||||
@@ -279,8 +277,8 @@ class TC_GAME_API SmartScript
|
||||
uint32 mEventPhase;
|
||||
|
||||
uint32 mPathId;
|
||||
SmartAIEventList mStoredEvents;
|
||||
std::list<uint32>mRemIDs;
|
||||
SmartAIEventStoredList mStoredEvents;
|
||||
std::vector<uint32> mRemIDs;
|
||||
|
||||
uint32 mTextTimer;
|
||||
uint32 mLastTextID;
|
||||
@@ -294,7 +292,7 @@ class TC_GAME_API SmartScript
|
||||
{
|
||||
if (!mStoredEvents.empty())
|
||||
{
|
||||
for (SmartAIEventList::iterator i = mStoredEvents.begin(); i != mStoredEvents.end(); ++i)
|
||||
for (auto i = mStoredEvents.begin(); i != mStoredEvents.end(); ++i)
|
||||
{
|
||||
if (i->event_id == id)
|
||||
{
|
||||
|
||||
@@ -223,7 +223,7 @@ void SmartAIMgr::LoadSmartAIFromDB()
|
||||
temp.event_id = fields[2].GetUInt16();
|
||||
temp.link = fields[3].GetUInt16();
|
||||
temp.event.type = (SMART_EVENT)fields[4].GetUInt8();
|
||||
temp.event.event_phase_mask = fields[5].GetUInt8();
|
||||
temp.event.event_phase_mask = fields[5].GetUInt16();
|
||||
temp.event.event_chance = fields[6].GetUInt8();
|
||||
temp.event.event_flags = fields[7].GetUInt16();
|
||||
|
||||
@@ -257,6 +257,50 @@ void SmartAIMgr::LoadSmartAIFromDB()
|
||||
if (!IsEventValid(temp))
|
||||
continue;
|
||||
|
||||
// specific check for timed events
|
||||
switch (temp.event.type)
|
||||
{
|
||||
case SMART_EVENT_UPDATE:
|
||||
case SMART_EVENT_UPDATE_OOC:
|
||||
case SMART_EVENT_UPDATE_IC:
|
||||
case SMART_EVENT_HEALT_PCT:
|
||||
case SMART_EVENT_TARGET_HEALTH_PCT:
|
||||
case SMART_EVENT_MANA_PCT:
|
||||
case SMART_EVENT_TARGET_MANA_PCT:
|
||||
case SMART_EVENT_RANGE:
|
||||
case SMART_EVENT_FRIENDLY_HEALTH:
|
||||
case SMART_EVENT_FRIENDLY_HEALTH_PCT:
|
||||
case SMART_EVENT_FRIENDLY_MISSING_BUFF:
|
||||
case SMART_EVENT_HAS_AURA:
|
||||
case SMART_EVENT_TARGET_BUFFED:
|
||||
if (temp.event.minMaxRepeat.repeatMin == 0 && temp.event.minMaxRepeat.repeatMax == 0 && !(temp.event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE) && temp.source_type != SMART_SCRIPT_TYPE_TIMED_ACTIONLIST)
|
||||
{
|
||||
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
|
||||
TC_LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Entry %d SourceType %u, Event %u, Missing Repeat flag.",
|
||||
temp.entryOrGuid, temp.GetScriptType(), temp.event_id);
|
||||
}
|
||||
break;
|
||||
case SMART_EVENT_VICTIM_CASTING:
|
||||
case SMART_EVENT_IS_BEHIND_TARGET:
|
||||
if (temp.event.minMaxRepeat.min == 0 && temp.event.minMaxRepeat.max == 0 && !(temp.event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE) && temp.source_type != SMART_SCRIPT_TYPE_TIMED_ACTIONLIST)
|
||||
{
|
||||
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
|
||||
TC_LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Entry %d SourceType %u, Event %u, Missing Repeat flag.",
|
||||
temp.entryOrGuid, temp.GetScriptType(), temp.event_id);
|
||||
}
|
||||
break;
|
||||
case SMART_EVENT_FRIENDLY_IS_CC:
|
||||
if (temp.event.friendlyCC.repeatMin == 0 && temp.event.friendlyCC.repeatMax == 0 && !(temp.event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE) && temp.source_type != SMART_SCRIPT_TYPE_TIMED_ACTIONLIST)
|
||||
{
|
||||
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
|
||||
TC_LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Entry %d SourceType %u, Event %u, Missing Repeat flag.",
|
||||
temp.entryOrGuid, temp.GetScriptType(), temp.event_id);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// creature entry / guid not found in storage, create empty event list for it and increase counters
|
||||
if (mEventMap[source_type].find(temp.entryOrGuid) == mEventMap[source_type].end())
|
||||
{
|
||||
@@ -375,6 +419,7 @@ bool SmartAIMgr::IsTargetValid(SmartScriptHolder const& e)
|
||||
case SMART_TARGET_CLOSEST_FRIENDLY:
|
||||
case SMART_TARGET_STORED:
|
||||
case SMART_TARGET_LOOT_RECIPIENTS:
|
||||
case SMART_TARGET_FARTHEST:
|
||||
break;
|
||||
default:
|
||||
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Not handled target_type(%u), Entry %d SourceType %u Event %u Action %u, skipped.", e.GetTargetType(), e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
|
||||
@@ -873,6 +918,12 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_CROSS_CAST:
|
||||
{
|
||||
if (!IsSpellValid(e, e.action.crossCast.spell))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_ADD_AURA:
|
||||
case SMART_ACTION_INVOKER_CAST:
|
||||
if (!IsSpellValid(e, e.action.cast.spell))
|
||||
@@ -1036,20 +1087,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Map entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.teleport.mapID);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case SMART_ACTION_SET_COUNTER:
|
||||
if (e.action.setCounter.counterId == 0)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses wrong counterId %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setCounter.counterId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e.action.setCounter.value == 0)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses wrong value %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setCounter.value);
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
case SMART_ACTION_INSTALL_AI_TEMPLATE:
|
||||
if (e.action.installTtemplate.id >= SMARTAI_TEMPLATE_END)
|
||||
@@ -1212,6 +1249,15 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_REMOVE_AURAS_BY_TYPE:
|
||||
{
|
||||
if (e.action.auraType.type >= TOTAL_AURAS)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Entry %u SourceType %u Event %u Action %u uses invalid data type %u (value range 0-TOTAL_AURAS), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.auraType.type);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_START_CLOSEST_WAYPOINT:
|
||||
case SMART_ACTION_FOLLOW:
|
||||
case SMART_ACTION_SET_ORIENTATION:
|
||||
@@ -1236,7 +1282,8 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
case SMART_ACTION_SET_DATA:
|
||||
case SMART_ACTION_SET_VISIBILITY:
|
||||
case SMART_ACTION_WP_PAUSE:
|
||||
case SMART_ACTION_SET_FLY:
|
||||
case SMART_ACTION_SET_DISABLE_GRAVITY:
|
||||
case SMART_ACTION_SET_CAN_FLY:
|
||||
case SMART_ACTION_SET_RUN:
|
||||
case SMART_ACTION_SET_SWIM:
|
||||
case SMART_ACTION_FORCE_DESPAWN:
|
||||
@@ -1257,7 +1304,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
case SMART_ACTION_SET_NPC_FLAG:
|
||||
case SMART_ACTION_ADD_NPC_FLAG:
|
||||
case SMART_ACTION_REMOVE_NPC_FLAG:
|
||||
case SMART_ACTION_CROSS_CAST:
|
||||
case SMART_ACTION_CALL_RANDOM_TIMED_ACTIONLIST:
|
||||
case SMART_ACTION_RANDOM_MOVE:
|
||||
case SMART_ACTION_SET_UNIT_FIELD_BYTES_1:
|
||||
@@ -1282,6 +1328,13 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
case SMART_ACTION_MOVE_OFFSET:
|
||||
case SMART_ACTION_SET_CORPSE_DELAY:
|
||||
case SMART_ACTION_DISABLE_EVADE:
|
||||
case SMART_ACTION_SET_SIGHT_DIST:
|
||||
case SMART_ACTION_FLEE:
|
||||
case SMART_ACTION_ADD_THREAT:
|
||||
case SMART_ACTION_LOAD_EQUIPMENT:
|
||||
case SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT:
|
||||
case SMART_ACTION_SET_COUNTER:
|
||||
case SMART_ACTION_REMOVE_ALL_GAMEOBJECTS:
|
||||
break;
|
||||
default:
|
||||
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type(%u), event_type(%u), Entry %d SourceType %u Event %u, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
|
||||
|
||||
@@ -66,9 +66,12 @@ enum SMART_EVENT_PHASE
|
||||
SMART_EVENT_PHASE_7 = 7,
|
||||
SMART_EVENT_PHASE_8 = 8,
|
||||
SMART_EVENT_PHASE_9 = 9,
|
||||
SMART_EVENT_PHASE_MAX = 10,
|
||||
SMART_EVENT_PHASE_10 = 10,
|
||||
SMART_EVENT_PHASE_11 = 11,
|
||||
SMART_EVENT_PHASE_12 = 12,
|
||||
SMART_EVENT_PHASE_MAX = 13,
|
||||
|
||||
SMART_EVENT_PHASE_COUNT = 9
|
||||
SMART_EVENT_PHASE_COUNT = 12
|
||||
};
|
||||
|
||||
enum SMART_EVENT_PHASE_BITS
|
||||
@@ -83,7 +86,12 @@ enum SMART_EVENT_PHASE_BITS
|
||||
SMART_EVENT_PHASE_7_BIT = 64,
|
||||
SMART_EVENT_PHASE_8_BIT = 128,
|
||||
SMART_EVENT_PHASE_9_BIT = 256,
|
||||
SMART_EVENT_PHASE_ALL = SMART_EVENT_PHASE_1_BIT + SMART_EVENT_PHASE_2_BIT + SMART_EVENT_PHASE_3_BIT + SMART_EVENT_PHASE_4_BIT + SMART_EVENT_PHASE_5_BIT + SMART_EVENT_PHASE_6_BIT + SMART_EVENT_PHASE_7_BIT + SMART_EVENT_PHASE_8_BIT + SMART_EVENT_PHASE_9_BIT
|
||||
SMART_EVENT_PHASE_10_BIT = 512,
|
||||
SMART_EVENT_PHASE_11_BIT = 1024,
|
||||
SMART_EVENT_PHASE_12_BIT = 2048,
|
||||
SMART_EVENT_PHASE_ALL = SMART_EVENT_PHASE_1_BIT + SMART_EVENT_PHASE_2_BIT + SMART_EVENT_PHASE_3_BIT + SMART_EVENT_PHASE_4_BIT + SMART_EVENT_PHASE_5_BIT +
|
||||
SMART_EVENT_PHASE_6_BIT + SMART_EVENT_PHASE_7_BIT + SMART_EVENT_PHASE_8_BIT + SMART_EVENT_PHASE_9_BIT + SMART_EVENT_PHASE_10_BIT +
|
||||
SMART_EVENT_PHASE_11_BIT + SMART_EVENT_PHASE_12_BIT
|
||||
};
|
||||
|
||||
const uint32 SmartPhaseMask[SMART_EVENT_PHASE_COUNT][2] =
|
||||
@@ -96,7 +104,10 @@ const uint32 SmartPhaseMask[SMART_EVENT_PHASE_COUNT][2] =
|
||||
{SMART_EVENT_PHASE_6, SMART_EVENT_PHASE_6_BIT },
|
||||
{SMART_EVENT_PHASE_7, SMART_EVENT_PHASE_7_BIT },
|
||||
{SMART_EVENT_PHASE_8, SMART_EVENT_PHASE_8_BIT },
|
||||
{SMART_EVENT_PHASE_9, SMART_EVENT_PHASE_9_BIT }
|
||||
{SMART_EVENT_PHASE_9, SMART_EVENT_PHASE_9_BIT },
|
||||
{SMART_EVENT_PHASE_10, SMART_EVENT_PHASE_10_BIT },
|
||||
{SMART_EVENT_PHASE_11, SMART_EVENT_PHASE_11_BIT },
|
||||
{SMART_EVENT_PHASE_12, SMART_EVENT_PHASE_12_BIT }
|
||||
};
|
||||
|
||||
enum SMART_EVENT
|
||||
@@ -165,7 +176,7 @@ enum SMART_EVENT
|
||||
SMART_EVENT_LINK = 61, // INTERNAL USAGE, no params, used to link together multiple events, does not use any extra resources to iterate event lists needlessly
|
||||
SMART_EVENT_GOSSIP_SELECT = 62, // menuID, actionID
|
||||
SMART_EVENT_JUST_CREATED = 63, // none
|
||||
SMART_EVENT_GOSSIP_HELLO = 64, // none
|
||||
SMART_EVENT_GOSSIP_HELLO = 64, // noReportUse (for GOs)
|
||||
SMART_EVENT_FOLLOW_COMPLETED = 65, // none
|
||||
SMART_EVENT_DUMMY_EFFECT = 66, // spellId, effectIndex
|
||||
SMART_EVENT_IS_BEHIND_TARGET = 67, // cooldownMin, CooldownMax
|
||||
@@ -359,6 +370,11 @@ struct SmartEvent
|
||||
uint32 id;
|
||||
} timedEvent;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 noReportUse;
|
||||
} gossipHello;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 sender;
|
||||
@@ -500,7 +516,7 @@ enum SMART_ACTION
|
||||
SMART_ACTION_REMOVE_ITEM = 57, // itemID, count
|
||||
SMART_ACTION_INSTALL_AI_TEMPLATE = 58, // AITemplateID
|
||||
SMART_ACTION_SET_RUN = 59, // 0/1
|
||||
SMART_ACTION_SET_FLY = 60, // 0/1
|
||||
SMART_ACTION_SET_DISABLE_GRAVITY = 60, // 0/1
|
||||
SMART_ACTION_SET_SWIM = 61, // 0/1
|
||||
SMART_ACTION_TELEPORT = 62, // mapID,
|
||||
SMART_ACTION_SET_COUNTER = 63, // id, value, reset (0/1)
|
||||
@@ -559,8 +575,17 @@ enum SMART_ACTION
|
||||
SMART_ACTION_SET_CORPSE_DELAY = 116, // timer
|
||||
SMART_ACTION_DISABLE_EVADE = 117, // 0/1 (1 = disabled, 0 = enabled)
|
||||
SMART_ACTION_GO_SET_GO_STATE = 118, // state
|
||||
SMART_ACTION_SET_CAN_FLY = 119, // 0/1
|
||||
SMART_ACTION_REMOVE_AURAS_BY_TYPE = 120, // type
|
||||
SMART_ACTION_SET_SIGHT_DIST = 121, // sightDistance
|
||||
SMART_ACTION_FLEE = 122, // fleeTime
|
||||
SMART_ACTION_ADD_THREAT = 123, // +threat, -threat
|
||||
SMART_ACTION_LOAD_EQUIPMENT = 124, // id
|
||||
SMART_ACTION_TRIGGER_RANDOM_TIMED_EVENT = 125, // id min range, id max range
|
||||
SMART_ACTION_REMOVE_ALL_GAMEOBJECTS = 126,
|
||||
SMART_ACTION_STOP_MOTION = 127, // stopMoving, movementExpired
|
||||
|
||||
SMART_ACTION_END = 119
|
||||
SMART_ACTION_END = 128
|
||||
};
|
||||
|
||||
struct SmartAction
|
||||
@@ -629,6 +654,7 @@ struct SmartAction
|
||||
uint32 spell;
|
||||
uint32 castFlags;
|
||||
uint32 triggerFlags;
|
||||
uint32 targetsLimit;
|
||||
} cast;
|
||||
|
||||
struct
|
||||
@@ -859,6 +885,11 @@ struct SmartAction
|
||||
uint32 run;
|
||||
} setRun;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 disable;
|
||||
} setDisableGravity;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 fly;
|
||||
@@ -976,6 +1007,11 @@ struct SmartAction
|
||||
struct
|
||||
{
|
||||
uint32 withEmote;
|
||||
} fleeAssist;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 fleeTime;
|
||||
} flee;
|
||||
|
||||
struct
|
||||
@@ -1081,6 +1117,34 @@ struct SmartAction
|
||||
uint32 disable;
|
||||
} disableEvade;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 type;
|
||||
} auraType;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 dist;
|
||||
} sightDistance;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 id;
|
||||
uint32 force;
|
||||
} loadEquipment;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 minId;
|
||||
uint32 maxId;
|
||||
} randomTimedEvent;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 stopMovement;
|
||||
uint32 movementExpired;
|
||||
} stopMotion;
|
||||
|
||||
//! Note for any new future actions
|
||||
//! All parameters must have type uint32
|
||||
|
||||
@@ -1112,10 +1176,10 @@ enum SMARTAI_TARGETS
|
||||
SMART_TARGET_NONE = 0, // NONE, defaulting to invoket
|
||||
SMART_TARGET_SELF = 1, // Self cast
|
||||
SMART_TARGET_VICTIM = 2, // Our current target (ie: highest aggro)
|
||||
SMART_TARGET_HOSTILE_SECOND_AGGRO = 3, // Second highest aggro
|
||||
SMART_TARGET_HOSTILE_LAST_AGGRO = 4, // Dead last on aggro
|
||||
SMART_TARGET_HOSTILE_RANDOM = 5, // Just any random target on our threat list
|
||||
SMART_TARGET_HOSTILE_RANDOM_NOT_TOP = 6, // Any random target except top threat
|
||||
SMART_TARGET_HOSTILE_SECOND_AGGRO = 3, // Second highest aggro, maxdist, playerOnly, powerType + 1
|
||||
SMART_TARGET_HOSTILE_LAST_AGGRO = 4, // Dead last on aggro, maxdist, playerOnly, powerType + 1
|
||||
SMART_TARGET_HOSTILE_RANDOM = 5, // Just any random target on our threat list, maxdist, playerOnly, powerType + 1
|
||||
SMART_TARGET_HOSTILE_RANDOM_NOT_TOP = 6, // Any random target except top threat, maxdist, playerOnly, powerType + 1
|
||||
SMART_TARGET_ACTION_INVOKER = 7, // Unit who caused this Event to occur
|
||||
SMART_TARGET_POSITION = 8, // use xyz from event params
|
||||
SMART_TARGET_CREATURE_RANGE = 9, // CreatureEntry(0any), minDist, maxDist
|
||||
@@ -1132,13 +1196,14 @@ enum SMARTAI_TARGETS
|
||||
SMART_TARGET_CLOSEST_GAMEOBJECT = 20, // entry(0any), maxDist
|
||||
SMART_TARGET_CLOSEST_PLAYER = 21, // maxDist
|
||||
SMART_TARGET_ACTION_INVOKER_VEHICLE = 22, // Unit's vehicle who caused this Event to occur
|
||||
SMART_TARGET_OWNER_OR_SUMMONER = 23, // Unit's owner or summoner
|
||||
SMART_TARGET_THREAT_LIST = 24, // All units on creature's threat list
|
||||
SMART_TARGET_OWNER_OR_SUMMONER = 23, // Unit's owner or summoner, Use Owner/Charmer of this unit
|
||||
SMART_TARGET_THREAT_LIST = 24, // All units on creature's threat list, maxdist
|
||||
SMART_TARGET_CLOSEST_ENEMY = 25, // maxDist, playerOnly
|
||||
SMART_TARGET_CLOSEST_FRIENDLY = 26, // maxDist, playerOnly
|
||||
SMART_TARGET_LOOT_RECIPIENTS = 27, // all players that have tagged this creature (for kill credit)
|
||||
SMART_TARGET_FARTHEST = 28, // maxDist, playerOnly, isInLos
|
||||
|
||||
SMART_TARGET_END = 28
|
||||
SMART_TARGET_END = 29
|
||||
};
|
||||
|
||||
struct SmartTarget
|
||||
@@ -1158,6 +1223,20 @@ struct SmartTarget
|
||||
float x, y, z, o;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32 maxDist;
|
||||
uint32 playerOnly;
|
||||
uint32 powerType;
|
||||
} hostilRandom;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 maxDist;
|
||||
uint32 playerOnly;
|
||||
uint32 isInLos;
|
||||
} farthest;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 creature;
|
||||
@@ -1236,6 +1315,11 @@ struct SmartTarget
|
||||
uint32 playerOnly;
|
||||
} closestFriendly;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 useCharmerOrOwner;
|
||||
} owner;
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 param1;
|
||||
@@ -1507,6 +1591,7 @@ class TC_GAME_API SmartWaypointMgr
|
||||
|
||||
// all events for a single entry
|
||||
typedef std::vector<SmartScriptHolder> SmartAIEventList;
|
||||
typedef std::vector<SmartScriptHolder> SmartAIEventStoredList;
|
||||
|
||||
// all events for all entries / guids
|
||||
typedef std::unordered_map<int32, SmartAIEventList> SmartAIEventMap;
|
||||
|
||||
@@ -2059,17 +2059,6 @@ Unit* Creature::SelectNearestTargetInAttackDistance(float dist) const
|
||||
return target;
|
||||
}
|
||||
|
||||
Player* Creature::SelectNearestPlayer(float distance) const
|
||||
{
|
||||
Player* target = nullptr;
|
||||
|
||||
Trinity::NearestPlayerInObjectRangeCheck checker(this, distance);
|
||||
Trinity::PlayerLastSearcher<Trinity::NearestPlayerInObjectRangeCheck> searcher(this, target, checker);
|
||||
VisitNearbyObject(distance, searcher);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
void Creature::SendAIReaction(AiReaction reactionType)
|
||||
{
|
||||
WorldPacket data(SMSG_AI_REACTION, 12);
|
||||
|
||||
@@ -584,7 +584,6 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
|
||||
|
||||
Unit* SelectNearestTarget(float dist = 0, bool playerOnly = false) const;
|
||||
Unit* SelectNearestTargetInAttackDistance(float dist = 0) const;
|
||||
Player* SelectNearestPlayer(float distance = 0) const;
|
||||
Unit* SelectNearestHostileUnitInAggroRange(bool useLOS = false) const;
|
||||
|
||||
void DoFleeToGetAssistance();
|
||||
|
||||
@@ -513,6 +513,10 @@ void GameObject::Update(uint32 diff)
|
||||
return;
|
||||
}
|
||||
|
||||
// Call AI Reset (required for example in SmartAI to clear one time events)
|
||||
if (AI())
|
||||
AI()->Reset();
|
||||
|
||||
// Respawn timer
|
||||
uint32 poolid = GetSpawnId() ? sPoolMgr->IsPartOfAPool<GameObject>(GetSpawnId()) : 0;
|
||||
if (poolid)
|
||||
@@ -1243,7 +1247,7 @@ void GameObject::Use(Unit* user)
|
||||
if (sScriptMgr->OnGossipHello(playerUser, this))
|
||||
return;
|
||||
|
||||
if (AI()->GossipHello(playerUser))
|
||||
if (AI()->GossipHello(playerUser, false))
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2242,7 +2242,7 @@ Creature* WorldObject::SummonTrigger(float x, float y, float z, float ang, uint3
|
||||
TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
|
||||
Creature* summon = SummonCreature(WORLD_TRIGGER, x, y, z, ang, summonType, duration);
|
||||
if (!summon)
|
||||
return NULL;
|
||||
return nullptr;
|
||||
|
||||
//summon->SetName(GetName());
|
||||
if (GetTypeId() == TYPEID_PLAYER || GetTypeId() == TYPEID_UNIT)
|
||||
@@ -2281,7 +2281,7 @@ void WorldObject::SummonCreatureGroup(uint8 group, std::list<TempSummon*>* list
|
||||
|
||||
Creature* WorldObject::FindNearestCreature(uint32 entry, float range, bool alive) const
|
||||
{
|
||||
Creature* creature = NULL;
|
||||
Creature* creature = nullptr;
|
||||
Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck checker(*this, entry, alive, range);
|
||||
Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(this, creature, checker);
|
||||
VisitNearbyObject(range, searcher);
|
||||
@@ -2290,7 +2290,7 @@ Creature* WorldObject::FindNearestCreature(uint32 entry, float range, bool alive
|
||||
|
||||
GameObject* WorldObject::FindNearestGameObject(uint32 entry, float range) const
|
||||
{
|
||||
GameObject* go = NULL;
|
||||
GameObject* go = nullptr;
|
||||
Trinity::NearestGameObjectEntryInObjectRangeCheck checker(*this, entry, range);
|
||||
Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck> searcher(this, go, checker);
|
||||
VisitNearbyGridObject(range, searcher);
|
||||
@@ -2299,13 +2299,24 @@ GameObject* WorldObject::FindNearestGameObject(uint32 entry, float range) const
|
||||
|
||||
GameObject* WorldObject::FindNearestGameObjectOfType(GameobjectTypes type, float range) const
|
||||
{
|
||||
GameObject* go = NULL;
|
||||
GameObject* go = nullptr;
|
||||
Trinity::NearestGameObjectTypeInObjectRangeCheck checker(*this, type, range);
|
||||
Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectTypeInObjectRangeCheck> searcher(this, go, checker);
|
||||
VisitNearbyGridObject(range, searcher);
|
||||
return go;
|
||||
}
|
||||
|
||||
Player* WorldObject::SelectNearestPlayer(float distance) const
|
||||
{
|
||||
Player* target = nullptr;
|
||||
|
||||
Trinity::NearestPlayerInObjectRangeCheck checker(this, distance);
|
||||
Trinity::PlayerLastSearcher<Trinity::NearestPlayerInObjectRangeCheck> searcher(this, target, checker);
|
||||
VisitNearbyObject(distance, searcher);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
template <typename Container>
|
||||
void WorldObject::GetGameObjectListWithEntryInGrid(Container& gameObjectContainer, uint32 entry, float maxSearchRange /*= 250.0f*/) const
|
||||
{
|
||||
@@ -2390,7 +2401,7 @@ void WorldObject::GetNearPoint(WorldObject const* /*searcher*/, float &x, float
|
||||
void WorldObject::GetClosePoint(float &x, float &y, float &z, float size, float distance2d /*= 0*/, float angle /*= 0*/) const
|
||||
{
|
||||
// angle calculated from current orientation
|
||||
GetNearPoint(NULL, x, y, z, size, distance2d, GetOrientation() + angle);
|
||||
GetNearPoint(nullptr, x, y, z, size, distance2d, GetOrientation() + angle);
|
||||
}
|
||||
|
||||
Position WorldObject::GetNearPosition(float dist, float angle)
|
||||
|
||||
@@ -592,6 +592,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
|
||||
Creature* FindNearestCreature(uint32 entry, float range, bool alive = true) const;
|
||||
GameObject* FindNearestGameObject(uint32 entry, float range) const;
|
||||
GameObject* FindNearestGameObjectOfType(GameobjectTypes type, float range) const;
|
||||
Player* SelectNearestPlayer(float distance) const;
|
||||
|
||||
template <typename Container>
|
||||
void GetGameObjectListWithEntryInGrid(Container& gameObjectContainer, uint32 entry, float maxSearchRange = 250.0f) const;
|
||||
|
||||
@@ -333,7 +333,7 @@ void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket)
|
||||
|
||||
if (GameObject* go = GetPlayer()->GetGameObjectIfCanInteractWith(guid))
|
||||
{
|
||||
if (go->AI()->GossipHello(_player))
|
||||
if (go->AI()->GossipHello(_player, true))
|
||||
return;
|
||||
|
||||
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "Battlefield.h"
|
||||
#include "BattlefieldMgr.h"
|
||||
#include "TradeData.h"
|
||||
#include "GameObjectAI.h"
|
||||
|
||||
extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS];
|
||||
|
||||
@@ -2811,6 +2812,9 @@ void Spell::DoAllEffectOnTarget(GOTargetInfo* target)
|
||||
if (effectMask & (1 << effectNumber))
|
||||
HandleEffects(NULL, NULL, go, effectNumber, SPELL_EFFECT_HANDLE_HIT_TARGET);
|
||||
|
||||
if (go->AI())
|
||||
go->AI()->SpellHit(m_caster, m_spellInfo);
|
||||
|
||||
CallScriptOnHitHandlers();
|
||||
CallScriptAfterHitHandlers();
|
||||
}
|
||||
|
||||
@@ -1735,7 +1735,7 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype)
|
||||
if (sScriptMgr->OnGossipHello(player, gameObjTarget))
|
||||
return;
|
||||
|
||||
if (gameObjTarget->AI()->GossipHello(player))
|
||||
if (gameObjTarget->AI()->GossipHello(player, false))
|
||||
return;
|
||||
|
||||
switch (gameObjTarget->GetGoType())
|
||||
|
||||
@@ -1364,7 +1364,7 @@ class go_twilight_portal : public GameObjectScript
|
||||
}
|
||||
}
|
||||
|
||||
bool GossipHello(Player* player) override
|
||||
bool GossipHello(Player* player, bool /*reportUse*/) override
|
||||
{
|
||||
if (_spellId != 0)
|
||||
player->CastSpell(player, _spellId, true);
|
||||
|
||||
@@ -979,7 +979,7 @@ class go_celestial_planetarium_access : public GameObjectScript
|
||||
{
|
||||
}
|
||||
|
||||
bool GossipHello(Player* player) override
|
||||
bool GossipHello(Player* player, bool /*reportUse*/) override
|
||||
{
|
||||
if (go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE))
|
||||
return true;
|
||||
|
||||
@@ -839,7 +839,7 @@ class go_soulwell : public GameObjectScript
|
||||
/// _and_ CMSG_GAMEOBJECT_REPORT_USE, this GossipHello hook is called
|
||||
/// twice. The script's handling is fine as it won't remove two charges
|
||||
/// on the well. We have to find how to segregate REPORT_USE and USE.
|
||||
bool GossipHello(Player* player) override
|
||||
bool GossipHello(Player* player, bool /*reportUse*/) override
|
||||
{
|
||||
Unit* owner = go->GetOwner();
|
||||
if (!owner || owner->GetTypeId() != TYPEID_PLAYER || !player->IsInSameRaidWith(owner->ToPlayer()))
|
||||
|
||||
Reference in new issue
Block a user