feat(Core/Maps): AreaBoundary (#10525)

* cherry-picked commit (2da458c56d)
This commit is contained in:
IntelligentQuantum
2022-04-15 16:40:41 +04:30
committed by GitHub
parent c1747f2fbf
commit ab4ee71762
169 changed files with 918 additions and 628 deletions

View File

@@ -37,7 +37,7 @@ void GuardAI::Reset()
me->CastSpell(me, 18950 /*SPELL_INVISIBILITY_AND_STEALTH_DETECTION*/, true);
}
void GuardAI::EnterEvadeMode()
void GuardAI::EnterEvadeMode(EvadeReason /*why*/)
{
if (!me->IsAlive())
{

View File

@@ -30,7 +30,7 @@ public:
static int Permissible(Creature const* creature);
void Reset() override;
void EnterEvadeMode() override;
void EnterEvadeMode(EvadeReason /*why*/) override;
void JustDied(Unit* killer) override;
};
#endif

View File

@@ -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)
@@ -66,11 +66,11 @@ void CritterAI::DamageTaken(Unit*, uint32&, DamageEffectType, SpellSchoolMask)
_combatTimer = 1;
}
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);
_combatTimer = 0;
}
@@ -80,7 +80,7 @@ void CritterAI::UpdateAI(uint32 diff)
{
_combatTimer += diff;
if (_combatTimer >= 5000)
EnterEvadeMode();
EnterEvadeMode(EVADE_REASON_OTHER);
}
}

View File

@@ -41,7 +41,7 @@ public:
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 @@ public:
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(Creature const*) { return PERMIT_BASE_IDLE; }
@@ -69,7 +69,7 @@ public:
explicit CritterAI(Creature* c) : PassiveAI(c) { _combatTimer = 0; }
void DamageTaken(Unit* /*done_by*/, uint32& /*damage*/, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) override;
void EnterEvadeMode() override;
void EnterEvadeMode(EvadeReason why) override;
void UpdateAI(uint32) override;
// Xinef: Added

View File

@@ -64,7 +64,8 @@ public:
//
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
void SpellHit(Unit* caster, SpellInfo const* spellInfo) override;
void PetStopAttack() override;

View File

@@ -50,7 +50,7 @@ void TotemAI::MoveInLineOfSight(Unit* /*who*/)
{
}
void TotemAI::EnterEvadeMode()
void TotemAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->CombatStop(true);
}

View File

@@ -31,8 +31,10 @@ public:
void MoveInLineOfSight(Unit* who) override;
void AttackStart(Unit* victim) override;
void EnterEvadeMode() override;
void EnterEvadeMode(EvadeReason /*why*/) override;
void SpellHit(Unit* /*caster*/, SpellInfo const* /*spellInfo*/) override;
void DoAction(int32 param) override;
void UpdateAI(uint32 diff) override;

View File

@@ -15,6 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AreaBoundary.h"
#include "CreatureAI.h"
#include "Creature.h"
#include "CreatureAIImpl.h"
@@ -24,6 +25,7 @@
#include "MapReference.h"
#include "Player.h"
#include "Vehicle.h"
#include "Language.h"
class PhasedRespawn : public BasicEvent
{
@@ -196,9 +198,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;
LOG_DEBUG("entities.unit", "Creature {} enters evade mode.", me->GetEntry());
@@ -290,7 +292,7 @@ bool CreatureAI::UpdateVictim()
return true;
}
bool CreatureAI::_EnterEvadeMode()
bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
{
if (!me->IsAlive())
{
@@ -357,6 +359,44 @@ void CreatureAI::MoveBackwardsChecks() {
me->GetMotionMaster()->MoveBackwards(victim, moveDist);
}
bool CreatureAI::IsInBoundary(Position const* who) const
{
if (!_boundary)
return true;
if (!who)
who = me;
return (CreatureAI::IsInBounds(*_boundary, who) != _negateBoundary);
}
bool CreatureAI::IsInBounds(CreatureBoundary const& boundary, Position const* pos)
{
for (AreaBoundary const* areaBoundary : boundary)
if (!areaBoundary->IsWithinBoundary(pos))
return false;
return true;
}
bool CreatureAI::CheckInRoom()
{
if (IsInBoundary())
return true;
else
{
EnterEvadeMode(EVADE_REASON_BOUNDARY);
return false;
}
}
void CreatureAI::SetBoundary(CreatureBoundary const* boundary, bool negateBoundaries /*= false*/)
{
_boundary = boundary;
_negateBoundary = negateBoundaries;
me->DoImmediateBoundaryCheck();
}
Creature* CreatureAI::DoSummon(uint32 entry, const Position& pos, uint32 despawnTime, TempSummonType summonType)
{
return me->SummonCreature(entry, pos, summonType, despawnTime);

View File

@@ -18,6 +18,7 @@
#ifndef ACORE_CREATUREAI_H
#define ACORE_CREATUREAI_H
#include "AreaBoundary.h"
#include "Common.h"
#include "Creature.h"
#include "UnitAI.h"
@@ -29,6 +30,8 @@ class Creature;
class Player;
class SpellInfo;
typedef std::vector<AreaBoundary const*> CreatureBoundary;
#define TIME_INTERVAL_LOOK 5000
#define VISIBILITY_RANGE 10000
@@ -78,10 +81,19 @@ protected:
Creature* DoSummon(uint32 entry, Position const& pos, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
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);
public:
// EnumUtils: DESCRIBE THIS
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), _negateBoundary(false), m_MoveInLineOfSight_locked(false) { }
~CreatureAI() override {}
@@ -100,7 +112,7 @@ public:
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 nullptr)
virtual void EnterCombat(Unit* /*victim*/) {}
@@ -187,10 +199,20 @@ public:
virtual void PetStopAttack() { }
// boundary system methods
virtual bool CheckInRoom();
CreatureBoundary const* GetBoundary() const { return _boundary; }
void SetBoundary(CreatureBoundary const* boundary, bool negativeBoundaries = false);
static bool IsInBounds(CreatureBoundary const& boundary, Position const* who);
bool IsInBoundary(Position const* who = nullptr) const;
protected:
virtual void MoveInLineOfSight(Unit* /*who*/);
bool _EnterEvadeMode();
bool _EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER);
CreatureBoundary const* _boundary;
bool _negateBoundary;
private:
bool m_MoveInLineOfSight_locked;

View File

@@ -15,6 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AreaBoundary.h"
#include "ScriptedCreature.h"
#include "Cell.h"
#include "CellImpl.h"
@@ -171,7 +172,6 @@ bool SummonList::IsAnyCreatureInCombat() const
ScriptedAI::ScriptedAI(Creature* creature) : CreatureAI(creature),
me(creature),
IsFleeing(false),
_evadeCheckCooldown(2500),
_isCombatMovementAllowed(true)
{
_isHeroic = me->GetMap()->IsHeroic();
@@ -489,23 +489,6 @@ enum eNPCs
NPC_FREYA = 32906,
};
bool ScriptedAI::EnterEvadeIfOutOfCombatArea()
{
if (me->IsInEvadeMode() || !me->IsInCombat())
return false;
if (_evadeCheckCooldown == GameTime::GetGameTime().count())
return false;
_evadeCheckCooldown = GameTime::GetGameTime().count();
if (!CheckEvadeIfOutOfCombatArea())
return false;
EnterEvadeMode();
return true;
}
Player* ScriptedAI::SelectTargetFromPlayerList(float maxdist, uint32 excludeAura, bool mustBeInLOS) const
{
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
@@ -531,9 +514,10 @@ Player* ScriptedAI::SelectTargetFromPlayerList(float maxdist, uint32 excludeAura
BossAI::BossAI(Creature* creature, uint32 bossId) : ScriptedAI(creature),
instance(creature->GetInstanceScript()),
summons(creature),
_boundary(instance ? instance->GetBossBoundary(bossId) : nullptr),
_bossId(bossId)
{
if (instance)
SetBoundary(instance->GetBossBoundary(bossId));
}
void BossAI::_Reset()
@@ -583,59 +567,10 @@ void BossAI::TeleportCheaters()
ThreatContainer::StorageType threatList = me->getThreatMgr().getThreatList();
for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
if (Unit* target = (*itr)->getTarget())
if (target->GetTypeId() == TYPEID_PLAYER && !CheckBoundary(target))
if (target->GetTypeId() == TYPEID_PLAYER && !IsInBoundary(target))
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);

View File

@@ -344,7 +344,6 @@ struct ScriptedAI : public CreatureAI
void SetCombatMovement(bool allowMovement);
bool IsCombatMovementAllowed() const { return _isCombatMovementAllowed; }
bool EnterEvadeIfOutOfCombatArea();
virtual bool CheckEvadeIfOutOfCombatArea() const { return false; }
// return true for heroic mode. i.e.
@@ -416,7 +415,6 @@ struct ScriptedAI : public CreatureAI
private:
Difficulty _difficulty;
uint32 _evadeCheckCooldown;
bool _isCombatMovementAllowed;
bool _isHeroic;
};
@@ -428,7 +426,6 @@ public:
~BossAI() override {}
InstanceScript* const instance;
BossBoundaryMap const* GetBoundary() const { return _boundary; }
void JustSummoned(Creature* summon) override;
void SummonedCreatureDespawn(Creature* summon) override;
@@ -453,23 +450,12 @@ protected:
void _JustDied();
void _JustReachedHome() { me->setActive(false); }
bool CheckInRoom()
{
if (CheckBoundary(me))
return true;
EnterEvadeMode();
return false;
}
bool CheckBoundary(Unit* who);
void TeleportCheaters();
EventMap events;
SummonList summons;
private:
BossBoundaryMap const* const _boundary;
uint32 const _bossId;
};

View File

@@ -198,7 +198,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();

View File

@@ -66,7 +66,7 @@ public:
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)

View File

@@ -150,7 +150,7 @@ void FollowerAI::JustRespawned()
Reset();
}
void FollowerAI::EnterEvadeMode()
void FollowerAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->RemoveAllAuras();
me->DeleteThreatList();

View File

@@ -46,7 +46,7 @@ public:
void MoveInLineOfSight(Unit*) override;
void EnterEvadeMode() override;
void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override;
void JustDied(Unit*) override;

View File

@@ -618,7 +618,7 @@ void SmartAI::MovementInform(uint32 MovementType, uint32 Data)
MovepointReached(Data);
}
void SmartAI::EnterEvadeMode()
void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
{
// xinef: fixes strange jumps when charming SmartAI npc
if (!me->IsAlive() || me->IsInEvadeMode())

View File

@@ -80,7 +80,7 @@ public:
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;

View File

@@ -202,7 +202,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
}
Creature::Creature(bool isWorldObject): Unit(isWorldObject), MovableMapObject(), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipientGroup(0),
m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_wanderDistance(0.0f),
m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_wanderDistance(0.0f), m_boundaryCheckTime(2500),
m_transportCheckTimer(1000), lootPickPocketRestoreTime(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE),
m_spawnId(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_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false),
@@ -689,6 +689,17 @@ void Creature::Update(uint32 diff)
SelectVictim();
}
// 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;
}
Unit* owner = GetCharmerOrOwner();
if (IsCharmed() && !IsWithinDistInMap(owner, GetMap()->GetVisibilityRange(), true, false))
{

View File

@@ -285,6 +285,8 @@ public:
[[nodiscard]] float GetWanderDistance() const { return m_wanderDistance; }
void SetWanderDistance(float dist) { m_wanderDistance = dist; }
void DoImmediateBoundaryCheck() { m_boundaryCheckTime = 0; }
uint32 m_groupLootTimer; // (msecs)timer used for group loot
uint32 lootingGroupLowGUID; // used to find group which is looting corpse
@@ -402,6 +404,7 @@ protected:
uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning
uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance
float m_wanderDistance;
uint32 m_boundaryCheckTime; // (msecs) remaining time for next evade boundary check
uint16 m_transportCheckTimer;
uint32 lootPickPocketRestoreTime;

View File

@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Define.h"
#include "Item.h"
#include "Define.h"
#include "SmartEnum.h"
#include <stdexcept>

View File

@@ -14038,7 +14038,7 @@ void Unit::TauntFadeOut(Unit* taunter)
if (m_ThreatMgr.isThreatListEmpty())
{
if (creature->IsAIEnabled)
creature->AI()->EnterEvadeMode();
creature->AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_HOSTILES);
return;
}
@@ -14124,7 +14124,7 @@ Unit* Creature::SelectVictim()
for (Unit::AuraEffectList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr)
if ((*itr)->GetBase()->IsPermanent())
{
AI()->EnterEvadeMode();
AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_HOSTILES);
break;
}
return nullptr;

View File

@@ -15,9 +15,9 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Unit.h"
#include "Define.h"
#include "SmartEnum.h"
#include "Unit.h"
#include <stdexcept>
namespace Acore::Impl::EnumUtilsImpl

View File

@@ -31,6 +31,12 @@
#include "Spell.h"
#include "WorldSession.h"
BossBoundaryData::~BossBoundaryData()
{
for (const_iterator it = begin(); it != end(); ++it)
delete it->boundary;
}
void InstanceScript::SaveToDB()
{
std::string data = GetSaveData();
@@ -120,6 +126,13 @@ bool InstanceScript::IsEncounterInProgress() const
return false;
}
void InstanceScript::LoadBossBoundaries(const BossBoundaryData& data)
{
for (BossBoundaryEntry const& entry : data)
if (entry.bossId < bosses.size())
bosses[entry.bossId].boundary.push_back(entry.boundary);
}
void InstanceScript::LoadMinionData(const MinionData* data)
{
while (data->entry)
@@ -137,7 +150,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;
}
@@ -268,28 +281,6 @@ void InstanceScript::AddDoor(GameObject* door, bool add)
if (add)
{
data.bossInfo->door[data.type].insert(door);
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);

View File

@@ -18,9 +18,11 @@
#ifndef ACORE_INSTANCE_DATA_H
#define ACORE_INSTANCE_DATA_H
#include "CreatureAI.h"
#include "ObjectMgr.h"
#include "World.h"
#include "ZoneScript.h"
#include <set>
#define OUT_SAVE_INST_DATA LOG_DEBUG("scripts.ai", "Saving Instance Data for Instance {} (Map {}, Instance Id {})", instance->GetMapName(), instance->GetId(), instance->GetInstanceId())
#define OUT_SAVE_INST_DATA_COMPLETE LOG_DEBUG("scripts.ai", "Saving Instance Data for Instance {} (Map {}, Instance Id {}) completed.", instance->GetMapName(), instance->GetId(), instance->GetInstanceId())
@@ -67,30 +69,30 @@ 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;
};
struct BossBoundaryData
{
typedef std::vector<BossBoundaryEntry> StorageType;
typedef StorageType::const_iterator const_iterator;
BossBoundaryData(std::initializer_list<BossBoundaryEntry> data) : _data(data) { }
~BossBoundaryData();
const_iterator begin() const { return _data.begin(); }
const_iterator end() const { return _data.end(); }
private:
StorageType _data;
};
struct MinionData
@@ -110,16 +112,15 @@ struct BossInfo
EncounterState state;
DoorSet door[MAX_DOOR_TYPES];
MinionSet 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 +226,7 @@ public:
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 : nullptr; }
CreatureBoundary const* GetBossBoundary(uint32 id) const { return id < bosses.size() ? &bosses[id].boundary : nullptr; }
BossInfo const* GetBossInfo(uint32 id) const { return &bosses[id]; }
// Achievement criteria additional requirements check
@@ -255,6 +256,7 @@ public:
virtual void DoAction(int32 /*action*/) {}
protected:
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);

View File

@@ -0,0 +1,113 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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(isInverted), _minX(southX), _maxX(northX), _minY(eastY), _maxY(westY) { }
bool RectangleBoundary::IsWithinBoundaryArea(Position const* pos) const
{
return !(
pos->GetPositionX() < _minX ||
pos->GetPositionX() > _maxX ||
pos->GetPositionY() < _minY ||
pos->GetPositionY() > _maxY
);
}
// ---== CIRCLE ==---
CircleBoundary::CircleBoundary(Position const& center, double radius, bool isInverted) :
AreaBoundary(isInverted), _center(center), _radiusSq(radius*radius) { }
CircleBoundary::CircleBoundary(Position const& center, Position const& pointOnCircle, bool isInverted) :
AreaBoundary(isInverted), _center(center), _radiusSq(_center.GetDoubleExactDist2dSq(pointOnCircle)) { }
bool CircleBoundary::IsWithinBoundaryArea(Position const* pos) const
{
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) :
AreaBoundary(isInverted), _center(center), _radiusYSq(radiusY*radiusY), _scaleXSq(_radiusYSq / (radiusX*radiusX)) { }
bool EllipseBoundary::IsWithinBoundaryArea(Position const* pos) const
{
double offX = _center.GetDoublePositionX() - pos->GetPositionX();
double 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) :
AreaBoundary(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(Position const* pos) const
{
// 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) :
AreaBoundary(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(Position const* pos) const
{
// 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(isInverted), _minZ(minZ), _maxZ(maxZ) { }
bool ZRangeBoundary::IsWithinBoundaryArea(Position const* pos) const
{
return (_minZ <= pos->GetPositionZ() && pos->GetPositionZ() <= _maxZ);
}
// ---== UNION OF 2 BOUNDARIES ==---
BoundaryUnionBoundary::BoundaryUnionBoundary(AreaBoundary const* b1, AreaBoundary const* b2, bool isInverted) :
AreaBoundary(isInverted), _b1(b1), _b2(b2)
{
ASSERT(b1 && b2);
}
BoundaryUnionBoundary::~BoundaryUnionBoundary()
{
delete _b1;
delete _b2;
}
bool BoundaryUnionBoundary::IsWithinBoundaryArea(Position const* pos) const
{
return (_b1->IsWithinBoundary(pos) || _b2->IsWithinBoundary(pos));
}

View File

@@ -0,0 +1,168 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 ACORE_AREA_BOUNDARY_H
#define ACORE_AREA_BOUNDARY_H
#include "Position.h"
class AC_GAME_API AreaBoundary
{
public:
bool IsWithinBoundary(Position const* pos) const { return pos && (IsWithinBoundaryArea(pos) != _isInvertedBoundary); }
bool IsWithinBoundary(Position const& pos) const { return IsWithinBoundary(&pos); }
virtual ~AreaBoundary() { }
protected:
explicit AreaBoundary(bool isInverted) : _isInvertedBoundary(isInverted) { }
struct DoublePosition : Position
{
DoublePosition(double x = 0.0, double y = 0.0, double z = 0.0, float o = 0.0f)
: Position(float(x), float(y), float(z), o), DoublePosX(x), DoublePosY(y), DoublePosZ(z) { }
DoublePosition(float x, float y = 0.0f, float z = 0.0f, float o = 0.0f)
: Position(x, y, z, o), DoublePosX(x), DoublePosY(y), DoublePosZ(z) { }
DoublePosition(Position const& pos)
: Position(pos), DoublePosX(pos.m_positionX), DoublePosY(pos.m_positionY), DoublePosZ(pos.m_positionZ) { }
double GetDoublePositionX() const { return DoublePosX; }
double GetDoublePositionY() const { return DoublePosY; }
double GetDoublePositionZ() const { return DoublePosZ; }
double GetDoubleExactDist2dSq(DoublePosition const& pos) const
{
double const offX = GetDoublePositionX() - pos.GetDoublePositionX();
double const offY = GetDoublePositionY() - pos.GetDoublePositionY();
return (offX * offX) + (offY * offY);
}
Position* sync()
{
m_positionX = float(DoublePosX);
m_positionY = float(DoublePosY);
m_positionZ = float(DoublePosZ);
return this;
}
double DoublePosX;
double DoublePosY;
double DoublePosZ;
};
virtual bool IsWithinBoundaryArea(Position const* pos) const = 0;
private:
bool _isInvertedBoundary;
};
class AC_GAME_API 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(Position const* pos) const override;
private:
float const _minX, _maxX, _minY, _maxY;
};
class AC_GAME_API CircleBoundary : public AreaBoundary
{
public:
CircleBoundary(Position const& center, double radius, bool isInverted = false);
CircleBoundary(Position const& center, Position const& pointOnCircle, bool isInverted = false);
protected:
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
DoublePosition const _center;
double const _radiusSq;
};
class AC_GAME_API EllipseBoundary : public AreaBoundary
{
public:
EllipseBoundary(Position const& center, double radiusX, double radiusY, bool isInverted = false);
protected:
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
DoublePosition const _center;
double const _radiusYSq, _scaleXSq;
};
class AC_GAME_API TriangleBoundary : public AreaBoundary
{
public:
TriangleBoundary(Position const& pointA, Position const& pointB, Position const& pointC, bool isInverted = false);
protected:
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
DoublePosition const _a, _b, _c;
double const _abx, _bcx, _cax, _aby, _bcy, _cay;
};
class AC_GAME_API ParallelogramBoundary : public AreaBoundary
{
public:
// Note: AB must be orthogonal to AD
ParallelogramBoundary(Position const& cornerA, Position const& cornerB, Position const& cornerD, bool isInverted = false);
protected:
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
DoublePosition const _a, _b, _d, _c;
double const _abx, _dax, _aby, _day;
};
class AC_GAME_API ZRangeBoundary : public AreaBoundary
{
public:
ZRangeBoundary(float minZ, float maxZ, bool isInverted = false);
protected:
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
float const _minZ, _maxZ;
};
class AC_GAME_API BoundaryUnionBoundary : public AreaBoundary
{
public:
BoundaryUnionBoundary(AreaBoundary const* b1, AreaBoundary const* b2, bool isInverted = false);
protected:
virtual ~BoundaryUnionBoundary();
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
AreaBoundary const* const _b1;
AreaBoundary const* const _b2;
};
#endif //ACORE_AREA_BOUNDARY_H

View File

@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Define.h"
#include "QuestDef.h"
#include "Define.h"
#include "SmartEnum.h"
#include <stdexcept>

View File

@@ -15,9 +15,9 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "WardenCheckMgr.h"
#include "Define.h"
#include "SmartEnum.h"
#include "WardenCheckMgr.h"
#include <stdexcept>
namespace Acore::Impl::EnumUtilsImpl