aboutsummaryrefslogtreecommitdiff
path: root/src/server/game
diff options
context:
space:
mode:
authortreeston <treeston.mmoc@gmail.com>2016-01-13 15:33:17 +0100
committerShauren <shauren.trinity@gmail.com>2016-03-22 22:56:43 +0100
commite2f2c70ba4ee0205dbe7f6ebb81e79dd0872d9e8 (patch)
tree462105f4bd862b5b2d0a761aa6c5912151bda68a /src/server/game
parent78885769cbc52940993064b34f347e50c018dd52 (diff)
Merge branch '3.3.5-bossboundary' into 3.3.5-base (PR #16089)
(cherry picked from commit 5b8b8c653039ec2add0b3a66468abb85e6f35054)
Diffstat (limited to 'src/server/game')
-rw-r--r--src/server/game/AI/CoreAI/GuardAI.cpp2
-rw-r--r--src/server/game/AI/CoreAI/GuardAI.h2
-rw-r--r--src/server/game/AI/CoreAI/PassiveAI.cpp6
-rw-r--r--src/server/game/AI/CoreAI/PassiveAI.h6
-rw-r--r--src/server/game/AI/CoreAI/PetAI.h2
-rw-r--r--src/server/game/AI/CoreAI/TotemAI.cpp2
-rw-r--r--src/server/game/AI/CoreAI/TotemAI.h2
-rw-r--r--src/server/game/AI/CreatureAI.cpp108
-rw-r--r--src/server/game/AI/CreatureAI.h25
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp60
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.h15
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.h2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h2
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp2
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.h2
-rw-r--r--src/server/game/Accounts/RBAC.h1
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp13
-rw-r--r--src/server/game/Entities/Creature/Creature.h1
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp4
-rw-r--r--src/server/game/Instances/InstanceScript.cpp31
-rw-r--r--src/server/game/Instances/InstanceScript.h39
-rw-r--r--src/server/game/Maps/AreaBoundary.cpp124
-rw-r--r--src/server/game/Maps/AreaBoundary.h152
-rw-r--r--src/server/game/Miscellaneous/Language.h3
26 files changed, 466 insertions, 144 deletions
diff --git a/src/server/game/AI/CoreAI/GuardAI.cpp b/src/server/game/AI/CoreAI/GuardAI.cpp
index 176dd41fdae..c7d618a9918 100644
--- a/src/server/game/AI/CoreAI/GuardAI.cpp
+++ b/src/server/game/AI/CoreAI/GuardAI.cpp
@@ -43,7 +43,7 @@ bool GuardAI::CanSeeAlways(WorldObject const* obj)
return false;
}
-void GuardAI::EnterEvadeMode()
+void GuardAI::EnterEvadeMode(EvadeReason /*why*/)
{
if (!me->IsAlive())
{
diff --git a/src/server/game/AI/CoreAI/GuardAI.h b/src/server/game/AI/CoreAI/GuardAI.h
index 01d54e47b9a..63f2750a5d4 100644
--- a/src/server/game/AI/CoreAI/GuardAI.h
+++ b/src/server/game/AI/CoreAI/GuardAI.h
@@ -31,7 +31,7 @@ class GuardAI : public ScriptedAI
static int Permissible(Creature const* creature);
bool CanSeeAlways(WorldObject const* obj) override;
- void EnterEvadeMode() override;
+ void EnterEvadeMode(EvadeReason /*why*/) override;
void JustDied(Unit* killer) override;
};
#endif
diff --git a/src/server/game/AI/CoreAI/PassiveAI.cpp b/src/server/game/AI/CoreAI/PassiveAI.cpp
index 1a6af99b11d..f283afe77a8 100644
--- a/src/server/game/AI/CoreAI/PassiveAI.cpp
+++ b/src/server/game/AI/CoreAI/PassiveAI.cpp
@@ -26,7 +26,7 @@ NullCreatureAI::NullCreatureAI(Creature* c) : CreatureAI(c) { me->SetReactState(
void PassiveAI::UpdateAI(uint32)
{
if (me->IsInCombat() && me->getAttackers().empty())
- EnterEvadeMode();
+ EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
}
void PossessedAI::AttackStart(Unit* target)
@@ -64,11 +64,11 @@ void CritterAI::DamageTaken(Unit* /*done_by*/, uint32&)
me->SetControlled(true, UNIT_STATE_FLEEING);
}
-void CritterAI::EnterEvadeMode()
+void CritterAI::EnterEvadeMode(EvadeReason why)
{
if (me->HasUnitState(UNIT_STATE_FLEEING))
me->SetControlled(false, UNIT_STATE_FLEEING);
- CreatureAI::EnterEvadeMode();
+ CreatureAI::EnterEvadeMode(why);
}
void TriggerAI::IsSummonedBy(Unit* summoner)
diff --git a/src/server/game/AI/CoreAI/PassiveAI.h b/src/server/game/AI/CoreAI/PassiveAI.h
index 28a73cff5de..bd72cd7fbe7 100644
--- a/src/server/game/AI/CoreAI/PassiveAI.h
+++ b/src/server/game/AI/CoreAI/PassiveAI.h
@@ -41,7 +41,7 @@ class PossessedAI : public CreatureAI
void MoveInLineOfSight(Unit*) override { }
void AttackStart(Unit* target) override;
void UpdateAI(uint32) override;
- void EnterEvadeMode() override { }
+ void EnterEvadeMode(EvadeReason /*why*/) override { }
void JustDied(Unit*) override;
void KilledUnit(Unit* victim) override;
@@ -57,7 +57,7 @@ class NullCreatureAI : public CreatureAI
void MoveInLineOfSight(Unit*) override { }
void AttackStart(Unit*) override { }
void UpdateAI(uint32) override { }
- void EnterEvadeMode() override { }
+ void EnterEvadeMode(EvadeReason /*why*/) override { }
void OnCharmed(bool /*apply*/) override { }
static int Permissible(const Creature*) { return PERMIT_BASE_IDLE; }
@@ -69,7 +69,7 @@ class CritterAI : public PassiveAI
explicit CritterAI(Creature* c) : PassiveAI(c) { }
void DamageTaken(Unit* done_by, uint32& /*damage*/) override;
- void EnterEvadeMode() override;
+ void EnterEvadeMode(EvadeReason why) override;
};
class TriggerAI : public NullCreatureAI
diff --git a/src/server/game/AI/CoreAI/PetAI.h b/src/server/game/AI/CoreAI/PetAI.h
index 9f220e64bfb..9c33baa9a9f 100644
--- a/src/server/game/AI/CoreAI/PetAI.h
+++ b/src/server/game/AI/CoreAI/PetAI.h
@@ -47,7 +47,7 @@ class PetAI : public CreatureAI
//
void MoveInLineOfSight(Unit* /*who*/) override { } // CreatureAI interferes with returning pets
void MoveInLineOfSight_Safe(Unit* /*who*/) { } // CreatureAI interferes with returning pets
- void EnterEvadeMode() override { } // For fleeing, pets don't use this type of Evade mechanic
+ void EnterEvadeMode(EvadeReason /*why*/) override { } // For fleeing, pets don't use this type of Evade mechanic
private:
bool _isVisible(Unit*) const;
diff --git a/src/server/game/AI/CoreAI/TotemAI.cpp b/src/server/game/AI/CoreAI/TotemAI.cpp
index 3c34ea88e39..48459ce0cd1 100644
--- a/src/server/game/AI/CoreAI/TotemAI.cpp
+++ b/src/server/game/AI/CoreAI/TotemAI.cpp
@@ -40,7 +40,7 @@ TotemAI::TotemAI(Creature* c) : CreatureAI(c), i_victimGuid()
void TotemAI::MoveInLineOfSight(Unit* /*who*/) { }
-void TotemAI::EnterEvadeMode()
+void TotemAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->CombatStop(true);
}
diff --git a/src/server/game/AI/CoreAI/TotemAI.h b/src/server/game/AI/CoreAI/TotemAI.h
index fdde303c7b2..e1d1618037f 100644
--- a/src/server/game/AI/CoreAI/TotemAI.h
+++ b/src/server/game/AI/CoreAI/TotemAI.h
@@ -33,7 +33,7 @@ class TotemAI : public CreatureAI
void MoveInLineOfSight(Unit* who) override;
void AttackStart(Unit* victim) override;
- void EnterEvadeMode() override;
+ void EnterEvadeMode(EvadeReason /*why*/) override;
void UpdateAI(uint32 diff) override;
static int Permissible(Creature const* creature);
diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp
index e94f5a037a3..8ef8940e89f 100644
--- a/src/server/game/AI/CreatureAI.cpp
+++ b/src/server/game/AI/CreatureAI.cpp
@@ -26,6 +26,7 @@
#include "MapReference.h"
#include "Player.h"
#include "CreatureTextMgr.h"
+#include "Language.h"
//Disable CreatureAI when charmed
void CreatureAI::OnCharmed(bool /*apply*/)
@@ -164,9 +165,9 @@ void CreatureAI::TriggerAlert(Unit const* who) const
me->GetMotionMaster()->MoveDistract(5 * IN_MILLISECONDS);
}
-void CreatureAI::EnterEvadeMode()
+void CreatureAI::EnterEvadeMode(EvadeReason why)
{
- if (!_EnterEvadeMode())
+ if (!_EnterEvadeMode(why))
return;
TC_LOG_DEBUG("entities.unit", "Creature %u enters evade mode.", me->GetEntry());
@@ -241,14 +242,14 @@ bool CreatureAI::UpdateVictim()
}
else if (me->getThreatManager().isThreatListEmpty())
{
- EnterEvadeMode();
+ EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
return false;
}
return true;
}
-bool CreatureAI::_EnterEvadeMode()
+bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
{
if (!me->IsAlive())
return false;
@@ -271,6 +272,105 @@ bool CreatureAI::_EnterEvadeMode()
return true;
}
+#define BOUNDARY_VISUALIZE_CREATURE 15425
+#define BOUNDARY_VISUALIZE_CREATURE_SCALE 0.25f
+#define BOUNDARY_VISUALIZE_STEP_SIZE 1
+#define BOUNDARY_VISUALIZE_FAILSAFE_LIMIT 750
+#define BOUNDARY_VISUALIZE_SPAWN_HEIGHT 5
+int32 CreatureAI::VisualizeBoundary(uint32 duration, Unit* owner, bool fill) const
+{
+ typedef std::pair<int32, int32> coordinate;
+
+ if (!owner)
+ return -1;
+
+ if (!_boundary || _boundary->empty())
+ return LANG_CREATURE_MOVEMENT_NOT_BOUNDED;
+
+ std::queue<coordinate> Q;
+ std::unordered_set<coordinate> alreadyChecked;
+ std::unordered_set<coordinate> outOfBounds;
+
+ Position startPosition = owner->GetPosition();
+ if (!CheckBoundary(&startPosition)) // fall back to creature position
+ {
+ startPosition = me->GetPosition();
+ if (!CheckBoundary(&startPosition))
+ {
+ startPosition = me->GetHomePosition();
+ if (!CheckBoundary(&startPosition)) // fall back to creature home position
+ return LANG_CREATURE_NO_INTERIOR_POINT_FOUND;
+ }
+ }
+ float spawnZ = startPosition.GetPositionZ() + BOUNDARY_VISUALIZE_SPAWN_HEIGHT;
+
+ bool boundsWarning = false;
+ Q.push({ 0,0 });
+ while (!Q.empty())
+ {
+ coordinate front = Q.front();
+ bool hasOutOfBoundsNeighbor = false;
+ for (coordinate off : std::initializer_list<coordinate>{{1,0}, {0,1}, {-1,0}, {0,-1}})
+ {
+ coordinate next(front.first + off.first, front.second + off.second);
+ if (next.first > BOUNDARY_VISUALIZE_FAILSAFE_LIMIT || next.first < -BOUNDARY_VISUALIZE_FAILSAFE_LIMIT || next.second > BOUNDARY_VISUALIZE_FAILSAFE_LIMIT || next.second < -BOUNDARY_VISUALIZE_FAILSAFE_LIMIT)
+ {
+ boundsWarning = true;
+ continue;
+ }
+ if (alreadyChecked.find(next) == alreadyChecked.end()) // never check a coordinate twice
+ {
+ Position nextPos(startPosition.GetPositionX() + next.first*BOUNDARY_VISUALIZE_STEP_SIZE, startPosition.GetPositionY() + next.second*BOUNDARY_VISUALIZE_STEP_SIZE, startPosition.GetPositionZ());
+ if (CheckBoundary(&nextPos))
+ Q.push(next);
+ else
+ {
+ outOfBounds.insert(next);
+ hasOutOfBoundsNeighbor = true;
+ }
+ alreadyChecked.insert(next);
+ }
+ else
+ if (outOfBounds.find(next) != outOfBounds.end())
+ hasOutOfBoundsNeighbor = true;
+ }
+ if (fill || hasOutOfBoundsNeighbor)
+ if (TempSummon* point = owner->SummonCreature(BOUNDARY_VISUALIZE_CREATURE, Position(startPosition.GetPositionX() + front.first*BOUNDARY_VISUALIZE_STEP_SIZE, startPosition.GetPositionY() + front.second*BOUNDARY_VISUALIZE_STEP_SIZE, spawnZ), TEMPSUMMON_TIMED_DESPAWN, duration * IN_MILLISECONDS))
+ {
+ point->SetObjectScale(BOUNDARY_VISUALIZE_CREATURE_SCALE);
+ point->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_STUNNED | UNIT_FLAG_IMMUNE_TO_NPC);
+ if (!hasOutOfBoundsNeighbor)
+ point->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ }
+ Q.pop();
+ }
+ return boundsWarning ? LANG_CREATURE_MOVEMENT_MAYBE_UNBOUNDED : 0;
+}
+
+bool CreatureAI::CheckBoundary(Position* who) const
+{
+ if (!who)
+ who = me;
+
+ if (_boundary)
+ for (CreatureBoundary::const_iterator it = _boundary->begin(); it != _boundary->end(); ++it)
+ if (!(*it)->IsWithinBoundary(who))
+ return false;
+
+ return true;
+}
+
+bool CreatureAI::CheckInRoom()
+{
+ if (CheckBoundary())
+ return true;
+ else
+ {
+ EnterEvadeMode(EVADE_REASON_BOUNDARY);
+ return false;
+ }
+}
+
Creature* CreatureAI::DoSummon(uint32 entry, const Position& pos, uint32 despawnTime, TempSummonType summonType)
{
return me->SummonCreature(entry, pos, summonType, despawnTime);
diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h
index 0a2cce723dc..3b7c489e018 100644
--- a/src/server/game/AI/CreatureAI.h
+++ b/src/server/game/AI/CreatureAI.h
@@ -21,6 +21,7 @@
#include "Creature.h"
#include "UnitAI.h"
+#include "AreaBoundary.h"
#include "Common.h"
class WorldObject;
@@ -63,6 +64,7 @@ enum SCEquip
EQUIP_UNEQUIP = 0
};
+typedef std::set<AreaBoundary const*> CreatureBoundary;
class CreatureAI : public UnitAI
{
protected:
@@ -77,10 +79,20 @@ class CreatureAI : public UnitAI
Creature* DoSummon(uint32 entry, WorldObject* obj, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
Creature* DoSummonFlyer(uint32 entry, WorldObject* obj, float flightZ, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
+ bool CheckBoundary(Position* who = nullptr) const;
+ void SetBoundary(CreatureBoundary const* boundary) { _boundary = boundary; CheckInRoom(); }
public:
+ enum EvadeReason
+ {
+ EVADE_REASON_NO_HOSTILES, // the creature's threat list is empty
+ EVADE_REASON_BOUNDARY, // the creature has moved outside its evade boundary
+ EVADE_REASON_SEQUENCE_BREAK, // this is a boss and the pre-requisite encounters for engaging it are not defeated yet
+ EVADE_REASON_OTHER
+ };
+
void Talk(uint8 id, WorldObject const* whisperTarget = nullptr);
- explicit CreatureAI(Creature* creature) : UnitAI(creature), me(creature), m_MoveInLineOfSight_locked(false) { }
+ explicit CreatureAI(Creature* creature) : UnitAI(creature), me(creature), _boundary(nullptr), m_MoveInLineOfSight_locked(false) { }
virtual ~CreatureAI() { }
@@ -96,7 +108,7 @@ class CreatureAI : public UnitAI
virtual bool CanRespawn() { return true; }
// Called for reaction at stopping attack at no attackers or targets
- virtual void EnterEvadeMode();
+ virtual void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER);
// Called for reaction at enter to combat if not in combat yet (enemy can be NULL)
virtual void EnterCombat(Unit* /*victim*/) { }
@@ -174,10 +186,17 @@ class CreatureAI : public UnitAI
virtual bool CanSeeAlways(WorldObject const* /*obj*/) { return false; }
+ // intended for encounter design/debugging. do not use for other purposes. expensive.
+ int32 VisualizeBoundary(uint32 duration, Unit* owner=nullptr, bool fill=false) const;
+ virtual bool CheckInRoom();
+ CreatureBoundary const* GetBoundary() const { return _boundary; }
+
protected:
virtual void MoveInLineOfSight(Unit* /*who*/);
- bool _EnterEvadeMode();
+ bool _EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER);
+
+ CreatureBoundary const* _boundary;
private:
bool m_MoveInLineOfSight_locked;
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index c2ddd1e9e8e..0e6836ca538 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -23,6 +23,7 @@
#include "Cell.h"
#include "CellImpl.h"
#include "ObjectMgr.h"
+#include "AreaBoundary.h"
// Spell summary for ScriptedAI::SelectSpell
struct TSpellSummary
@@ -98,7 +99,6 @@ bool SummonList::HasEntry(uint32 entry) const
ScriptedAI::ScriptedAI(Creature* creature) : CreatureAI(creature),
IsFleeing(false),
- _evadeCheckCooldown(2500),
_isCombatMovementAllowed(true)
{
_isHeroic = me->GetMap()->IsHeroic();
@@ -394,7 +394,7 @@ enum NPCs
// Hacklike storage used for misc creatures that are expected to evade of outside of a certain area.
// It is assumed the information is found elswehere and can be handled by the core. So far no luck finding such information/way to extract it.
-bool ScriptedAI::EnterEvadeIfOutOfCombatArea(uint32 const diff)
+/*bool ScriptedAI::EnterEvadeIfOutOfCombatArea(uint32 const diff)
{
if (_evadeCheckCooldown <= diff)
_evadeCheckCooldown = 2500;
@@ -438,15 +438,16 @@ bool ScriptedAI::EnterEvadeIfOutOfCombatArea(uint32 const diff)
EnterEvadeMode();
return true;
-}
+}*/
// BossAI - for instanced bosses
BossAI::BossAI(Creature* creature, uint32 bossId) : ScriptedAI(creature),
instance(creature->GetInstanceScript()),
summons(creature),
- _boundary(instance ? instance->GetBossBoundary(bossId) : NULL),
_bossId(bossId)
{
+ if (instance)
+ SetBoundary(instance->GetBossBoundary(bossId));
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
@@ -483,7 +484,7 @@ void BossAI::_EnterCombat()
// bosses do not respawn, check only on enter combat
if (!instance->CheckRequiredBosses(_bossId))
{
- EnterEvadeMode();
+ EnterEvadeMode(EVADE_REASON_SEQUENCE_BREAK);
return;
}
instance->SetBossState(_bossId, IN_PROGRESS);
@@ -507,55 +508,6 @@ void BossAI::TeleportCheaters()
target->NearTeleportTo(x, y, z, 0);
}
-bool BossAI::CheckBoundary(Unit* who)
-{
- if (!GetBoundary() || !who)
- return true;
-
- for (BossBoundaryMap::const_iterator itr = GetBoundary()->begin(); itr != GetBoundary()->end(); ++itr)
- {
- switch (itr->first)
- {
- case BOUNDARY_N:
- if (who->GetPositionX() > itr->second)
- return false;
- break;
- case BOUNDARY_S:
- if (who->GetPositionX() < itr->second)
- return false;
- break;
- case BOUNDARY_E:
- if (who->GetPositionY() < itr->second)
- return false;
- break;
- case BOUNDARY_W:
- if (who->GetPositionY() > itr->second)
- return false;
- break;
- case BOUNDARY_NW:
- if (who->GetPositionX() + who->GetPositionY() > itr->second)
- return false;
- break;
- case BOUNDARY_SE:
- if (who->GetPositionX() + who->GetPositionY() < itr->second)
- return false;
- break;
- case BOUNDARY_NE:
- if (who->GetPositionX() - who->GetPositionY() > itr->second)
- return false;
- break;
- case BOUNDARY_SW:
- if (who->GetPositionX() - who->GetPositionY() < itr->second)
- return false;
- break;
- default:
- break;
- }
- }
-
- return true;
-}
-
void BossAI::JustSummoned(Creature* summon)
{
summons.Summon(summon);
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
index 87c1a63d4e4..d3e20e8b886 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
@@ -260,8 +260,6 @@ struct ScriptedAI : public CreatureAI
void SetCombatMovement(bool allowMovement);
bool IsCombatMovementAllowed() const { return _isCombatMovementAllowed; }
- bool EnterEvadeIfOutOfCombatArea(uint32 const diff);
-
// return true for heroic mode. i.e.
// - for dungeon in mode 10-heroic,
// - for raid in mode 10-Heroic
@@ -329,7 +327,6 @@ struct ScriptedAI : public CreatureAI
private:
Difficulty _difficulty;
- uint32 _evadeCheckCooldown;
bool _isCombatMovementAllowed;
bool _isHeroic;
};
@@ -341,7 +338,6 @@ class BossAI : public ScriptedAI
virtual ~BossAI() { }
InstanceScript* const instance;
- BossBoundaryMap const* GetBoundary() const { return _boundary; }
void JustSummoned(Creature* summon) override;
void SummonedCreatureDespawn(Creature* summon) override;
@@ -368,16 +364,6 @@ class BossAI : public ScriptedAI
void _JustReachedHome() { me->setActive(false); }
void _DespawnAtEvade();
- virtual bool CheckInRoom()
- {
- if (CheckBoundary(me))
- return true;
-
- EnterEvadeMode();
- return false;
- }
-
- bool CheckBoundary(Unit* who);
void TeleportCheaters();
EventMap events;
@@ -385,7 +371,6 @@ class BossAI : public ScriptedAI
TaskScheduler scheduler;
private:
- BossBoundaryMap const* const _boundary;
uint32 const _bossId;
};
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
index cd2288364c1..6e8a2def3e3 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp
@@ -184,7 +184,7 @@ void npc_escortAI::ReturnToLastPoint()
me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z);
}
-void npc_escortAI::EnterEvadeMode()
+void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->RemoveAllAuras();
me->DeleteThreatList();
diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
index 3b7428e2b9e..673f3e671a0 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.h
@@ -66,7 +66,7 @@ struct npc_escortAI : public ScriptedAI
void ReturnToLastPoint();
- void EnterEvadeMode() override;
+ void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override;
void UpdateAI(uint32 diff) override; // the "internal" update, calls UpdateEscortAI()
virtual void UpdateEscortAI(uint32 diff); // used when it's needed to add code in update (abilities, scripted events, etc)
diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
index 3fbd6b9a834..583e99b9d10 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp
@@ -176,7 +176,7 @@ void FollowerAI::JustRespawned()
Reset();
}
-void FollowerAI::EnterEvadeMode()
+void FollowerAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->RemoveAllAuras();
me->DeleteThreatList();
diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h
index f3919ea63bc..d1c976b45c8 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.h
@@ -46,7 +46,7 @@ class FollowerAI : public ScriptedAI
void MoveInLineOfSight(Unit*) override;
- void EnterEvadeMode() override;
+ void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override;
void JustDied(Unit*) override;
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 001fd259996..2dec1d0fc4c 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -408,7 +408,7 @@ void SmartAI::MovementInform(uint32 MovementType, uint32 Data)
MovepointReached(Data);
}
-void SmartAI::EnterEvadeMode()
+void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
{
if (!me->IsAlive() || me->IsInEvadeMode())
return;
diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h
index 8454ea3b484..3abe1e5774c 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.h
+++ b/src/server/game/AI/SmartScripts/SmartAI.h
@@ -79,7 +79,7 @@ class SmartAI : public CreatureAI
void EnterCombat(Unit* enemy) override;
// Called for reaction at stopping attack at no attackers or targets
- void EnterEvadeMode() override;
+ void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER) override;
// Called when the creature is killed
void JustDied(Unit* killer) override;
diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h
index d355d4fdfd3..5dc0c0867ea 100644
--- a/src/server/game/Accounts/RBAC.h
+++ b/src/server/game/Accounts/RBAC.h
@@ -727,6 +727,7 @@ enum RBACPermissions
RBAC_PERM_COMMAND_TICKET_RESET_SUGGESTION = 833,
RBAC_PERM_COMMAND_GO_QUEST = 834,
RBAC_PERM_COMMAND_DEBUG_LOADCELLS = 835,
+ RBAC_PERM_COMMAND_DEBUG_BOUNDARY = 836,
// custom permissions 1000+
RBAC_PERM_MAX
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 594d2e81afe..47ef22b2df0 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -182,7 +182,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(),
m_groupLootTimer(0), m_PlayerDamageReq(0),
_pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0),
-m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE),
+m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE),
m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(UI64LIT(0)), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(NULL), m_creatureData(NULL), m_waypointID(0), m_path_id(0), m_formation(NULL)
@@ -586,6 +586,17 @@ void Creature::Update(uint32 diff)
LastCharmerGUID.Clear();
}
+ // periodic check to see if the creature has passed an evade boundary
+ if (IsAIEnabled && !IsInEvadeMode() && IsInCombat())
+ {
+ if (diff >= m_boundaryCheckTime)
+ {
+ AI()->CheckInRoom();
+ m_boundaryCheckTime = 2500;
+ } else
+ m_boundaryCheckTime -= diff;
+ }
+
// if periodic combat pulse is enabled and we are both in combat and in a dungeon, do this now
if (m_combatPulseDelay > 0 && IsInCombat() && GetMap()->IsDungeon())
{
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 8ae94e51451..ec9bf4aec59 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -731,6 +731,7 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning
uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance
float m_respawnradius;
+ uint32 m_boundaryCheckTime; // (msecs) remaining time for next evade boundary check
uint32 m_combatPulseTime; // (msecs) remaining time for next zone-in-combat pulse
uint32 m_combatPulseDelay; // (secs) how often the creature puts the entire zone in combat (only works in dungeons)
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 4632a32741d..9a2a955d74d 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -10746,7 +10746,7 @@ void Unit::TauntFadeOut(Unit* taunter)
if (m_ThreatManager.isThreatListEmpty())
{
if (creature->IsAIEnabled)
- creature->AI()->EnterEvadeMode();
+ creature->AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_HOSTILES);
return;
}
@@ -10887,7 +10887,7 @@ Unit* Creature::SelectVictim()
}
// enter in evade mode in other case
- AI()->EnterEvadeMode();
+ AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_HOSTILES);
return NULL;
}
diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp
index 6700ca07646..2e3ae5e4875 100644
--- a/src/server/game/Instances/InstanceScript.cpp
+++ b/src/server/game/Instances/InstanceScript.cpp
@@ -96,6 +96,13 @@ void InstanceScript::SetHeaders(std::string const& dataHeaders)
headers.push_back(header);
}
+void InstanceScript::LoadBossBoundaries(const BossBoundaryData& data)
+{
+ for (BossBoundaryEntry entry : data)
+ if (entry.bossId < bosses.size())
+ bosses[entry.bossId].boundary.insert(entry.boundary);
+}
+
void InstanceScript::LoadMinionData(const MinionData* data)
{
while (data->entry)
@@ -113,7 +120,7 @@ void InstanceScript::LoadDoorData(const DoorData* data)
while (data->entry)
{
if (data->bossId < bosses.size())
- doors.insert(std::make_pair(data->entry, DoorInfo(&bosses[data->bossId], data->type, BoundaryType(data->boundary))));
+ doors.insert(std::make_pair(data->entry, DoorInfo(&bosses[data->bossId], data->type)));
++data;
}
@@ -236,28 +243,6 @@ void InstanceScript::AddDoor(GameObject* door, bool add)
if (add)
{
data.bossInfo->door[data.type].insert(door->GetGUID());
- switch (data.boundary)
- {
- default:
- case BOUNDARY_NONE:
- break;
- case BOUNDARY_N:
- case BOUNDARY_S:
- data.bossInfo->boundary[data.boundary] = door->GetPositionX();
- break;
- case BOUNDARY_E:
- case BOUNDARY_W:
- data.bossInfo->boundary[data.boundary] = door->GetPositionY();
- break;
- case BOUNDARY_NW:
- case BOUNDARY_SE:
- data.bossInfo->boundary[data.boundary] = door->GetPositionX() + door->GetPositionY();
- break;
- case BOUNDARY_NE:
- case BOUNDARY_SW:
- data.bossInfo->boundary[data.boundary] = door->GetPositionX() - door->GetPositionY();
- break;
- }
}
else
data.bossInfo->door[data.type].erase(door->GetGUID());
diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h
index 02c9bfa408e..e77c6c00883 100644
--- a/src/server/game/Instances/InstanceScript.h
+++ b/src/server/game/Instances/InstanceScript.h
@@ -19,9 +19,11 @@
#ifndef TRINITY_INSTANCE_DATA_H
#define TRINITY_INSTANCE_DATA_H
+#include <set>
#include "ZoneScript.h"
#include "World.h"
#include "ObjectMgr.h"
+#include "CreatureAI.h"
#include "Packets/WorldStatePackets.h"
#define OUT_SAVE_INST_DATA TC_LOG_DEBUG("scripts", "Saving Instance Data for Instance %s (Map %d, Instance Id %d)", instance->GetMapName(), instance->GetId(), instance->GetInstanceId())
@@ -69,32 +71,19 @@ enum DoorType
MAX_DOOR_TYPES
};
-enum BoundaryType
-{
- BOUNDARY_NONE = 0,
- BOUNDARY_N,
- BOUNDARY_S,
- BOUNDARY_E,
- BOUNDARY_W,
- BOUNDARY_NE,
- BOUNDARY_NW,
- BOUNDARY_SE,
- BOUNDARY_SW,
- BOUNDARY_MAX_X = BOUNDARY_N,
- BOUNDARY_MIN_X = BOUNDARY_S,
- BOUNDARY_MAX_Y = BOUNDARY_W,
- BOUNDARY_MIN_Y = BOUNDARY_E
-};
-
-typedef std::map<BoundaryType, float> BossBoundaryMap;
-
struct DoorData
{
uint32 entry, bossId;
DoorType type;
- uint32 boundary;
};
+struct BossBoundaryEntry
+{
+ uint32 const bossId;
+ AreaBoundary const* const boundary;
+};
+typedef std::list<BossBoundaryEntry> BossBoundaryData;
+
struct MinionData
{
uint32 entry, bossId;
@@ -112,16 +101,15 @@ struct BossInfo
EncounterState state;
GuidSet door[MAX_DOOR_TYPES];
GuidSet minion;
- BossBoundaryMap boundary;
+ CreatureBoundary boundary;
};
struct DoorInfo
{
- explicit DoorInfo(BossInfo* _bossInfo, DoorType _type, BoundaryType _boundary)
- : bossInfo(_bossInfo), type(_type), boundary(_boundary) { }
+ explicit DoorInfo(BossInfo* _bossInfo, DoorType _type)
+ : bossInfo(_bossInfo), type(_type) { }
BossInfo* bossInfo;
DoorType type;
- BoundaryType boundary;
};
struct MinionInfo
@@ -225,7 +213,7 @@ class InstanceScript : public ZoneScript
virtual bool SetBossState(uint32 id, EncounterState state);
EncounterState GetBossState(uint32 id) const { return id < bosses.size() ? bosses[id].state : TO_BE_DECIDED; }
static std::string GetBossStateName(uint8 state);
- BossBoundaryMap const* GetBossBoundary(uint32 id) const { return id < bosses.size() ? &bosses[id].boundary : NULL; }
+ CreatureBoundary const* GetBossBoundary(uint32 id) const { return id < bosses.size() ? &bosses[id].boundary : NULL; }
// Achievement criteria additional requirements check
// NOTE: not use this if same can be checked existed requirement types from AchievementCriteriaRequirementType
@@ -255,6 +243,7 @@ class InstanceScript : public ZoneScript
protected:
void SetHeaders(std::string const& dataHeaders);
void SetBossNumber(uint32 number) { bosses.resize(number); }
+ void LoadBossBoundaries(BossBoundaryData const& data);
void LoadDoorData(DoorData const* data);
void LoadMinionData(MinionData const* data);
void LoadObjectData(ObjectData const* creatureData, ObjectData const* gameObjectData);
diff --git a/src/server/game/Maps/AreaBoundary.cpp b/src/server/game/Maps/AreaBoundary.cpp
new file mode 100644
index 00000000000..837a9959041
--- /dev/null
+++ b/src/server/game/Maps/AreaBoundary.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015-2016 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "AreaBoundary.h"
+#include "Unit.h"
+#include "TemporarySummon.h"
+
+// ---== RECTANGLE ==---
+RectangleBoundary::RectangleBoundary(float southX, float northX, float eastY, float westY, bool isInverted) :
+ AreaBoundary(BoundaryType::BOUNDARY_RECTANGLE, isInverted), _minX(southX), _maxX(northX), _minY(eastY), _maxY(westY) { }
+bool RectangleBoundary::IsWithinBoundaryArea(const Position* pos) const
+{
+ if (!pos)
+ return false;
+
+ return !(
+ pos->GetPositionX() < _minX ||
+ pos->GetPositionX() > _maxX ||
+ pos->GetPositionY() < _minY ||
+ pos->GetPositionY() > _maxY
+ );
+}
+
+
+// ---== CIRCLE ==---
+CircleBoundary::CircleBoundary(Position const& center, double radius, bool isInverted) :
+ CircleBoundary(DoublePosition(center), radius, isInverted) { }
+CircleBoundary::CircleBoundary(DoublePosition const& center, double radius, bool isInverted) :
+ AreaBoundary(BoundaryType::BOUNDARY_CIRCLE, isInverted), _center(center), _radiusSq(radius*radius) { }
+CircleBoundary::CircleBoundary(Position const& center, Position const& pointOnCircle, bool isInverted) :
+ CircleBoundary(DoublePosition(center), DoublePosition(pointOnCircle), isInverted) { }
+CircleBoundary::CircleBoundary(DoublePosition const& center, DoublePosition const& pointOnCircle, bool isInverted) :
+ AreaBoundary(BoundaryType::BOUNDARY_CIRCLE, isInverted), _center(center), _radiusSq(center.GetDoubleExactDist2dSq(pointOnCircle)) { }
+bool CircleBoundary::IsWithinBoundaryArea(const Position* pos) const
+{
+ if (!pos)
+ return false;
+
+ double offX = _center.GetDoublePositionX() - pos->GetPositionX();
+ double offY = _center.GetDoublePositionY() - pos->GetPositionY();
+ return offX*offX+offY*offY <= _radiusSq;
+}
+
+
+// ---== ELLIPSE ==---
+EllipseBoundary::EllipseBoundary(Position const& center, double radiusX, double radiusY, bool isInverted) :
+ EllipseBoundary(DoublePosition(center), radiusX, radiusY, isInverted) { }
+EllipseBoundary::EllipseBoundary(DoublePosition const& center, double radiusX, double radiusY, bool isInverted) :
+ AreaBoundary(BoundaryType::BOUNDARY_ELLIPSE, isInverted), _center(center), _radiusYSq(radiusY*radiusY), _scaleXSq(_radiusYSq / (radiusX*radiusX)) { }
+bool EllipseBoundary::IsWithinBoundaryArea(const Position* pos) const
+{
+ if (!pos)
+ return false;
+
+ double offX = _center.GetDoublePositionX()-pos->GetPositionX(), offY = _center.GetDoublePositionY()-pos->GetPositionY();
+ return (offX*offX)*_scaleXSq + (offY*offY) <= _radiusYSq;
+}
+
+
+// ---== TRIANGLE ==---
+TriangleBoundary::TriangleBoundary(Position const& pointA, Position const& pointB, Position const& pointC, bool isInverted) :
+ TriangleBoundary(DoublePosition(pointA), DoublePosition(pointB), DoublePosition(pointC), isInverted) { }
+TriangleBoundary::TriangleBoundary(DoublePosition const& pointA, DoublePosition const& pointB, DoublePosition const& pointC, bool isInverted) :
+ AreaBoundary(BoundaryType::BOUNDARY_TRIANGLE, isInverted), _a(pointA), _b(pointB), _c(pointC), _abx(_b.GetDoublePositionX()-_a.GetDoublePositionX()), _bcx(_c.GetDoublePositionX()-_b.GetDoublePositionX()), _cax(_a.GetDoublePositionX() - _c.GetDoublePositionX()), _aby(_b.GetDoublePositionY()-_a.GetDoublePositionY()), _bcy(_c.GetDoublePositionY()-_b.GetDoublePositionY()), _cay(_a.GetDoublePositionY() - _c.GetDoublePositionY()) { }
+bool TriangleBoundary::IsWithinBoundaryArea(const Position* pos) const
+{
+ if (!pos)
+ return false;
+
+ // half-plane signs
+ bool sign1 = ((-_b.GetDoublePositionX() + pos->GetPositionX()) * _aby - (-_b.GetDoublePositionY() + pos->GetPositionY()) * _abx) < 0;
+ bool sign2 = ((-_c.GetDoublePositionX() + pos->GetPositionX()) * _bcy - (-_c.GetDoublePositionY() + pos->GetPositionY()) * _bcx) < 0;
+ bool sign3 = ((-_a.GetDoublePositionX() + pos->GetPositionX()) * _cay - (-_a.GetDoublePositionY() + pos->GetPositionY()) * _cax) < 0;
+
+ // if all signs are the same, the point is inside the triangle
+ return ((sign1 == sign2) && (sign2 == sign3));
+}
+
+
+// ---== PARALLELOGRAM ==---
+ParallelogramBoundary::ParallelogramBoundary(Position const& cornerA, Position const& cornerB, Position const& cornerD, bool isInverted) :
+ ParallelogramBoundary(DoublePosition(cornerA), DoublePosition(cornerB), DoublePosition(cornerD), isInverted) { }
+ParallelogramBoundary::ParallelogramBoundary(DoublePosition const& cornerA, DoublePosition const& cornerB, DoublePosition const& cornerD, bool isInverted) :
+ AreaBoundary(BoundaryType::BOUNDARY_PARALLELOGRAM, isInverted), _a(cornerA), _b(cornerB), _d(cornerD), _c(DoublePosition(_d.GetDoublePositionX() + (_b.GetDoublePositionX() - _a.GetDoublePositionX()), _d.GetDoublePositionY() + (_b.GetDoublePositionY() - _a.GetDoublePositionY()))), _abx(_b.GetDoublePositionX() - _a.GetDoublePositionX()), _dax(_a.GetDoublePositionX() - _d.GetDoublePositionX()), _aby(_b.GetDoublePositionY() - _a.GetDoublePositionY()), _day(_a.GetDoublePositionY() - _d.GetDoublePositionY()) { }
+bool ParallelogramBoundary::IsWithinBoundaryArea(const Position* pos) const
+{
+ if (!pos)
+ return false;
+
+ // half-plane signs
+ bool sign1 = ((-_b.GetDoublePositionX() + pos->GetPositionX()) * _aby - (-_b.GetDoublePositionY() + pos->GetPositionY()) * _abx) < 0;
+ bool sign2 = ((-_a.GetDoublePositionX() + pos->GetPositionX()) * _day - (-_a.GetDoublePositionY() + pos->GetPositionY()) * _dax) < 0;
+ bool sign3 = ((-_d.GetDoublePositionY() + pos->GetPositionY()) * _abx - (-_d.GetDoublePositionX() + pos->GetPositionX()) * _aby) < 0; // AB = -CD
+ bool sign4 = ((-_c.GetDoublePositionY() + pos->GetPositionY()) * _dax - (-_c.GetDoublePositionX() + pos->GetPositionX()) * _day) < 0; // DA = -BC
+
+ // if all signs are equal, the point is inside
+ return ((sign1 == sign2) && (sign2 == sign3) && (sign3 == sign4));
+}
+
+
+// ---== Z RANGE ==---
+ZRangeBoundary::ZRangeBoundary(float minZ, float maxZ, bool isInverted) :
+ AreaBoundary(BoundaryType::BOUNDARY_Z_RANGE, isInverted), _minZ(minZ), _maxZ(maxZ) { }
+bool ZRangeBoundary::IsWithinBoundaryArea(const Position* pos) const
+{
+ if (!pos)
+ return false;
+
+ return !(pos->GetPositionZ() < _minZ || pos->GetPositionZ() > _maxZ);
+}
diff --git a/src/server/game/Maps/AreaBoundary.h b/src/server/game/Maps/AreaBoundary.h
new file mode 100644
index 00000000000..24a00962359
--- /dev/null
+++ b/src/server/game/Maps/AreaBoundary.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2015-2016 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TRINITY_AREA_BOUNDARY_H
+#define TRINITY_AREA_BOUNDARY_H
+
+#include "Position.h"
+
+class AreaBoundary
+{
+ public:
+ enum BoundaryType
+ {
+ BOUNDARY_RECTANGLE, // Rectangle aligned with the coordinate axis
+ BOUNDARY_CIRCLE,
+ BOUNDARY_ELLIPSE,
+ BOUNDARY_TRIANGLE,
+ BOUNDARY_PARALLELOGRAM,
+ BOUNDARY_Z_RANGE,
+ };
+ BoundaryType GetBoundaryType() const { return m_boundaryType; }
+ bool IsWithinBoundary(const Position* pos) const { return (IsWithinBoundaryArea(pos) != m_isInvertedBoundary); }
+
+ struct DoublePosition : Position
+ {
+ double d_positionX, d_positionY, d_positionZ;
+ DoublePosition(double x = 0, double y = 0, double z = 0, float o = 0)
+ : Position((float)x, (float)y, (float)z, o), d_positionX(x), d_positionY(y), d_positionZ(z) { }
+ DoublePosition(float x = 0, float y = 0, float z = 0, float o = 0)
+ : Position(x, y, z, o), d_positionX(x), d_positionY(y), d_positionZ(z) { }
+ DoublePosition(const Position& pos)
+ : DoublePosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()) { }
+
+ double GetDoublePositionX() const { return d_positionX; }
+ double GetDoublePositionY() const { return d_positionY; }
+ double GetDoublePositionZ() const { return d_positionZ; }
+
+ double GetDoubleExactDist2dSq(DoublePosition const& pos) const {
+ double offX = GetDoublePositionX() - pos.GetDoublePositionX();
+ double offY = GetDoublePositionY() - pos.GetDoublePositionY();
+ return (offX*offX) + (offY*offY);
+ }
+
+ Position* sync() { m_positionX = (float)d_positionX; m_positionY = (float)d_positionY; m_positionZ = (float)d_positionZ; return this; }
+ };
+
+ protected:
+ AreaBoundary(BoundaryType bType, bool isInverted) : m_boundaryType(bType), m_isInvertedBoundary(isInverted) { }
+ virtual bool IsWithinBoundaryArea(const Position* pos) const = 0;
+ const BoundaryType m_boundaryType;
+ bool m_isInvertedBoundary;
+};
+
+class RectangleBoundary : public AreaBoundary
+{
+ public:
+ // X axis is north/south, Y axis is east/west, larger values are northwest
+ RectangleBoundary(float southX, float northX, float eastY, float westY, bool isInverted = false);
+
+ protected:
+ bool IsWithinBoundaryArea(const Position* pos) const override;
+
+ private:
+ const float _minX, _maxX, _minY, _maxY;
+};
+
+class CircleBoundary : public AreaBoundary
+{
+ public:
+ CircleBoundary(Position const& center, double radius, bool isInverted = false);
+ CircleBoundary(DoublePosition const& center, double radius, bool isInverted = false);
+ CircleBoundary(Position const& center, Position const& pointOnCircle, bool isInverted = false);
+ CircleBoundary(DoublePosition const& center, DoublePosition const& pointOnCircle, bool isInverted = false);
+
+ protected:
+ bool IsWithinBoundaryArea(const Position* pos) const override;
+
+ private:
+ const DoublePosition _center;
+ const double _radiusSq;
+};
+
+class EllipseBoundary : public AreaBoundary
+{
+ public:
+ EllipseBoundary(Position const& center, double radiusX, double radiusY, bool isInverted = false);
+ EllipseBoundary(DoublePosition const& center, double radiusX, double radiusY, bool isInverted = false);
+
+ protected:
+ bool IsWithinBoundaryArea(const Position* pos) const override;
+
+ private:
+ const DoublePosition _center;
+ const double _radiusYSq, _scaleXSq;
+};
+
+class TriangleBoundary : public AreaBoundary
+{
+ public:
+ TriangleBoundary(Position const& pointA, Position const& pointB, Position const& pointC, bool isInverted = false);
+ TriangleBoundary(DoublePosition const& pointA, DoublePosition const& pointB, DoublePosition const& pointC, bool isInverted = false);
+
+ protected:
+ bool IsWithinBoundaryArea(const Position* pos) const override;
+
+ private:
+ const DoublePosition _a, _b, _c;
+ const double _abx, _bcx, _cax, _aby, _bcy, _cay;
+};
+
+class ParallelogramBoundary : public AreaBoundary
+{
+ public:
+ // Note: AB must be orthogonal to AD
+ ParallelogramBoundary(Position const& cornerA, Position const& cornerB, Position const& cornerD, bool isInverted = false);
+ ParallelogramBoundary(DoublePosition const& cornerA, DoublePosition const& cornerB, DoublePosition const& cornerD, bool isInverted = false);
+
+ protected:
+ bool IsWithinBoundaryArea(const Position* pos) const override;
+
+ private:
+ const DoublePosition _a, _b, _d, _c;
+ const double _abx, _dax, _aby, _day;
+};
+
+class ZRangeBoundary : public AreaBoundary
+{
+ public:
+ ZRangeBoundary(float minZ, float maxZ, bool isInverted = false);
+
+ protected:
+ bool IsWithinBoundaryArea(const Position* pos) const override;
+
+ private:
+ const float _minZ, _maxZ;
+};
+
+#endif //TRINITY_AREA_BOUNDARY_H
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index 4ca67fbadfc..4c3ce70fbef 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -1214,6 +1214,9 @@ enum TrinityStrings
LANG_NPCINFO_FLAGS_EXTRA = 11009,
LANG_INSTANCE_LOGIN_GAMEMASTER_EXCEPTION = 11010,
+ LANG_CREATURE_NO_INTERIOR_POINT_FOUND = 11011,
+ LANG_CREATURE_MOVEMENT_NOT_BOUNDED = 11012,
+ LANG_CREATURE_MOVEMENT_MAYBE_UNBOUNDED = 11013,
LANG_INSTANCE_BIND_MISMATCH = 11014
};
#endif