diff options
author | Yehonal <yehonal.azeroth@gmail.com> | 2016-08-12 00:46:43 +0200 |
---|---|---|
committer | Yehonal <yehonal.azeroth@gmail.com> | 2016-08-12 02:38:26 +0200 |
commit | b0c8eceb08b9a7688893991e5ba4a3350617e6ed (patch) | |
tree | 39a52f0bea8e1ed803dc4298fdeb8b6bd808b67d /src/game/AI/ScriptedAI/ScriptedCreature.cpp | |
parent | a73ad5cd6eefd619e9371d9b26c7e6317cacd7f7 (diff) |
Refactoring part 2 [W.I.P]
Diffstat (limited to 'src/game/AI/ScriptedAI/ScriptedCreature.cpp')
-rw-r--r-- | src/game/AI/ScriptedAI/ScriptedCreature.cpp | 668 |
1 files changed, 668 insertions, 0 deletions
diff --git a/src/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/game/AI/ScriptedAI/ScriptedCreature.cpp new file mode 100644 index 0000000000..fe2cff77ba --- /dev/null +++ b/src/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -0,0 +1,668 @@ +/* + * Copyright (C) + * + * + * + * This program is free software licensed under GPL version 2 + * Please see the included DOCS/LICENSE.TXT for more information */ + +#include "ScriptedCreature.h" +#include "Item.h" +#include "Spell.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "Cell.h" +#include "CellImpl.h" +#include "ObjectMgr.h" +#include "TemporarySummon.h" + +// Spell summary for ScriptedAI::SelectSpell +struct TSpellSummary +{ + uint8 Targets; // set of enum SelectTarget + uint8 Effects; // set of enum SelectEffect +} extern* SpellSummary; + +void SummonList::DoZoneInCombat(uint32 entry) +{ + for (StorageType::iterator i = storage_.begin(); i != storage_.end();) + { + Creature* summon = ObjectAccessor::GetCreature(*me, *i); + ++i; + if (summon && summon->IsAIEnabled + && (!entry || summon->GetEntry() == entry)) + { + summon->AI()->DoZoneInCombat(); + } + } +} + +void SummonList::DespawnEntry(uint32 entry) +{ + for (StorageType::iterator i = storage_.begin(); i != storage_.end();) + { + Creature* summon = ObjectAccessor::GetCreature(*me, *i); + if (!summon) + i = storage_.erase(i); + else if (summon->GetEntry() == entry) + { + i = storage_.erase(i); + summon->DespawnOrUnsummon(); + } + else + ++i; + } +} + +void SummonList::DespawnAll() +{ + while (!storage_.empty()) + { + Creature* summon = ObjectAccessor::GetCreature(*me, storage_.front()); + storage_.pop_front(); + if (summon) + summon->DespawnOrUnsummon(); + } +} + +void SummonList::RemoveNotExisting() +{ + for (StorageType::iterator i = storage_.begin(); i != storage_.end();) + { + if (ObjectAccessor::GetCreature(*me, *i)) + ++i; + else + i = storage_.erase(i); + } +} + +bool SummonList::HasEntry(uint32 entry) const +{ + for (StorageType::const_iterator i = storage_.begin(); i != storage_.end(); ++i) + { + Creature* summon = ObjectAccessor::GetCreature(*me, *i); + if (summon && summon->GetEntry() == entry) + return true; + } + + return false; +} + +uint32 SummonList::GetEntryCount(uint32 entry) const +{ + uint32 count = 0; + for (StorageType::const_iterator i = storage_.begin(); i != storage_.end(); ++i) + { + Creature* summon = ObjectAccessor::GetCreature(*me, *i); + if (summon && summon->GetEntry() == entry) + ++count; + } + + return count; +} + +void SummonList::Respawn() +{ + for (StorageType::iterator i = storage_.begin(); i != storage_.end();) + { + if (Creature* summon = ObjectAccessor::GetCreature(*me, *i)) + { + summon->Respawn(true); + ++i; + } + else + i = storage_.erase(i); + } +} + +Creature* SummonList::GetCreatureWithEntry(uint32 entry) const +{ + for (StorageType::const_iterator i = storage_.begin(); i != storage_.end(); ++i) + { + if (Creature* summon = ObjectAccessor::GetCreature(*me, *i)) + if (summon->GetEntry() == entry) + return summon; + } + + return NULL; +} + +ScriptedAI::ScriptedAI(Creature* creature) : CreatureAI(creature), + me(creature), + IsFleeing(false), + _evadeCheckCooldown(2500), + _isCombatMovementAllowed(true) +{ + _isHeroic = me->GetMap()->IsHeroic(); + _difficulty = Difficulty(me->GetMap()->GetSpawnMode()); +} + +void ScriptedAI::AttackStartNoMove(Unit* who) +{ + if (!who) + return; + + if (me->Attack(who, true)) + DoStartNoMovement(who); +} + +void ScriptedAI::AttackStart(Unit* who) +{ + if (IsCombatMovementAllowed()) + CreatureAI::AttackStart(who); + else + AttackStartNoMove(who); +} + +void ScriptedAI::UpdateAI(uint32 /*diff*/) +{ + //Check if we have a current target + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); +} + +void ScriptedAI::DoStartMovement(Unit* victim, float distance, float angle) +{ + if (victim) + me->GetMotionMaster()->MoveChase(victim, distance, angle); +} + +void ScriptedAI::DoStartNoMovement(Unit* victim) +{ + if (!victim) + return; + + me->GetMotionMaster()->MoveIdle(); +} + +void ScriptedAI::DoStopAttack() +{ + if (me->GetVictim()) + me->AttackStop(); +} + +void ScriptedAI::DoCastSpell(Unit* target, SpellInfo const* spellInfo, bool triggered) +{ + if (!target || me->IsNonMeleeSpellCast(false)) + return; + + me->StopMoving(); + me->CastSpell(target, spellInfo, triggered ? TRIGGERED_FULL_MASK : TRIGGERED_NONE); +} + +void ScriptedAI::DoPlaySoundToSet(WorldObject* source, uint32 soundId) +{ + if (!source) + return; + + if (!sSoundEntriesStore.LookupEntry(soundId)) + { + sLog->outError("TSCR: Invalid soundId %u used in DoPlaySoundToSet (Source: TypeId %u, GUID %u)", soundId, source->GetTypeId(), source->GetGUIDLow()); + return; + } + + source->PlayDirectSound(soundId); +} + +Creature* ScriptedAI::DoSpawnCreature(uint32 entry, float offsetX, float offsetY, float offsetZ, float angle, uint32 type, uint32 despawntime) +{ + return me->SummonCreature(entry, me->GetPositionX() + offsetX, me->GetPositionY() + offsetY, me->GetPositionZ() + offsetZ, angle, TempSummonType(type), despawntime); +} + +SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mechanic, SelectTargetType targets, uint32 powerCostMin, uint32 powerCostMax, float rangeMin, float rangeMax, SelectEffect effects) +{ + //No target so we can't cast + if (!target) + return NULL; + + //Silenced so we can't cast + if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) + return NULL; + + //Using the extended script system we first create a list of viable spells + SpellInfo const* apSpell[CREATURE_MAX_SPELLS]; + memset(apSpell, 0, CREATURE_MAX_SPELLS * sizeof(SpellInfo*)); + + uint32 spellCount = 0; + + SpellInfo const* tempSpell = NULL; + + //Check if each spell is viable(set it to null if not) + for (uint32 i = 0; i < CREATURE_MAX_SPELLS; i++) + { + tempSpell = sSpellMgr->GetSpellInfo(me->m_spells[i]); + + //This spell doesn't exist + if (!tempSpell) + continue; + + // Targets and Effects checked first as most used restrictions + //Check the spell targets if specified + if (targets && !(SpellSummary[me->m_spells[i]].Targets & (1 << (targets-1)))) + continue; + + //Check the type of spell if we are looking for a specific spell type + if (effects && !(SpellSummary[me->m_spells[i]].Effects & (1 << (effects-1)))) + continue; + + //Check for school if specified + if (school && (tempSpell->SchoolMask & school) == 0) + continue; + + //Check for spell mechanic if specified + if (mechanic && tempSpell->Mechanic != mechanic) + continue; + + //Make sure that the spell uses the requested amount of power + if (powerCostMin && tempSpell->ManaCost < powerCostMin) + continue; + + if (powerCostMax && tempSpell->ManaCost > powerCostMax) + continue; + + //Continue if we don't have the mana to actually cast this spell + if (tempSpell->ManaCost > me->GetPower(Powers(tempSpell->PowerType))) + continue; + + //Check if the spell meets our range requirements + if (rangeMin && me->GetSpellMinRangeForTarget(target, tempSpell) < rangeMin) + continue; + if (rangeMax && me->GetSpellMaxRangeForTarget(target, tempSpell) > rangeMax) + continue; + + //Check if our target is in range + if (me->IsWithinDistInMap(target, float(me->GetSpellMinRangeForTarget(target, tempSpell))) || !me->IsWithinDistInMap(target, float(me->GetSpellMaxRangeForTarget(target, tempSpell)))) + continue; + + //All good so lets add it to the spell list + apSpell[spellCount] = tempSpell; + ++spellCount; + } + + //We got our usable spells so now lets randomly pick one + if (!spellCount) + return NULL; + + return apSpell[urand(0, spellCount - 1)]; +} + +void ScriptedAI::DoResetThreat() +{ + if (!me->CanHaveThreatList() || me->getThreatManager().isThreatListEmpty()) + { + sLog->outError("DoResetThreat called for creature that either cannot have threat list or has empty threat list (me entry = %d)", me->GetEntry()); + return; + } + + me->getThreatManager().resetAllAggro(); +} + +float ScriptedAI::DoGetThreat(Unit* unit) +{ + if (!unit) + return 0.0f; + return me->getThreatManager().getThreat(unit); +} + +void ScriptedAI::DoModifyThreatPercent(Unit* unit, int32 pct) +{ + if (!unit) + return; + me->getThreatManager().modifyThreatPercent(unit, pct); +} + +void ScriptedAI::DoTeleportPlayer(Unit* unit, float x, float y, float z, float o) +{ + if (!unit) + return; + + if (Player* player = unit->ToPlayer()) + player->TeleportTo(unit->GetMapId(), x, y, z, o, TELE_TO_NOT_LEAVE_COMBAT); + else + sLog->outError("TSCR: Creature " UI64FMTD " (Entry: %u) Tried to teleport non-player unit (Type: %u GUID: " UI64FMTD ") to x: %f y:%f z: %f o: %f. Aborted.", me->GetGUID(), me->GetEntry(), unit->GetTypeId(), unit->GetGUID(), x, y, z, o); +} + +void ScriptedAI::DoTeleportAll(float x, float y, float z, float o) +{ + Map* map = me->GetMap(); + if (!map->IsDungeon()) + return; + + Map::PlayerList const& PlayerList = map->GetPlayers(); + for (Map::PlayerList::const_iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr) + if (Player* player = itr->GetSource()) + if (player->IsAlive()) + player->TeleportTo(me->GetMapId(), x, y, z, o, TELE_TO_NOT_LEAVE_COMBAT); +} + +Unit* ScriptedAI::DoSelectLowestHpFriendly(float range, uint32 minHPDiff) +{ + Unit* unit = NULL; + Trinity::MostHPMissingInRange u_check(me, range, minHPDiff); + Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange> searcher(me, unit, u_check); + me->VisitNearbyObject(range, searcher); + + return unit; +} + +std::list<Creature*> ScriptedAI::DoFindFriendlyCC(float range) +{ + std::list<Creature*> list; + Trinity::FriendlyCCedInRange u_check(me, range); + Trinity::CreatureListSearcher<Trinity::FriendlyCCedInRange> searcher(me, list, u_check); + me->VisitNearbyObject(range, searcher); + return list; +} + +std::list<Creature*> ScriptedAI::DoFindFriendlyMissingBuff(float range, uint32 uiSpellid) +{ + std::list<Creature*> list; + Trinity::FriendlyMissingBuffInRange u_check(me, range, uiSpellid); + Trinity::CreatureListSearcher<Trinity::FriendlyMissingBuffInRange> searcher(me, list, u_check); + me->VisitNearbyObject(range, searcher); + return list; +} + +Player* ScriptedAI::GetPlayerAtMinimumRange(float minimumRange) +{ + Player* player = NULL; + + CellCoord pair(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY())); + Cell cell(pair); + cell.SetNoCreate(); + + Trinity::PlayerAtMinimumRangeAway check(me, minimumRange); + Trinity::PlayerSearcher<Trinity::PlayerAtMinimumRangeAway> searcher(me, player, check); + TypeContainerVisitor<Trinity::PlayerSearcher<Trinity::PlayerAtMinimumRangeAway>, GridTypeMapContainer> visitor(searcher); + + cell.Visit(pair, visitor, *me->GetMap(), *me, minimumRange); + + return player; +} + +void ScriptedAI::SetEquipmentSlots(bool loadDefault, int32 mainHand /*= EQUIP_NO_CHANGE*/, int32 offHand /*= EQUIP_NO_CHANGE*/, int32 ranged /*= EQUIP_NO_CHANGE*/) +{ + if (loadDefault) + { + me->LoadEquipment(me->GetOriginalEquipmentId(), true); + return; + } + + if (mainHand >= 0) + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(mainHand)); + + if (offHand >= 0) + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(offHand)); + + if (ranged >= 0) + me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, uint32(ranged)); +} + +void ScriptedAI::SetCombatMovement(bool allowMovement) +{ + _isCombatMovementAllowed = allowMovement; +} + +enum eNPCs +{ + NPC_BROODLORD = 12017, + NPC_JAN_ALAI = 23578, + NPC_SARTHARION = 28860, + NPC_FREYA = 32906, +}; + +bool ScriptedAI::EnterEvadeIfOutOfCombatArea() +{ + if (me->IsInEvadeMode() || !me->IsInCombat()) + return false; + + if (_evadeCheckCooldown == time(NULL)) + return false; + _evadeCheckCooldown = time(NULL); + + if (!CheckEvadeIfOutOfCombatArea()) + return false; + + EnterEvadeMode(); + return true; +} + +Player* ScriptedAI::SelectTargetFromPlayerList(float maxdist, uint32 excludeAura, bool mustBeInLOS) const +{ + Map::PlayerList const& pList = me->GetMap()->GetPlayers(); + std::vector<Player*> tList; + for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr) + { + if (me->GetDistance(itr->GetSource()) > maxdist || !itr->GetSource()->IsAlive() || itr->GetSource()->IsGameMaster()) + continue; + if (excludeAura && itr->GetSource()->HasAura(excludeAura)) + continue; + if (mustBeInLOS && !me->IsWithinLOSInMap(itr->GetSource())) + continue; + tList.push_back(itr->GetSource()); + } + if (!tList.empty()) + return tList[urand(0,tList.size()-1)]; + else + return NULL; +} + +// 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) +{ +} + +void BossAI::_Reset() +{ + if (!me->IsAlive()) + return; + + me->ResetLootMode(); + events.Reset(); + summons.DespawnAll(); + if (instance) + instance->SetBossState(_bossId, NOT_STARTED); +} + +void BossAI::_JustDied() +{ + events.Reset(); + summons.DespawnAll(); + if (instance) + { + instance->SetBossState(_bossId, DONE); + instance->SaveToDB(); + } +} + +void BossAI::_EnterCombat() +{ + me->setActive(true); + DoZoneInCombat(); + if (instance) + { + // bosses do not respawn, check only on enter combat + if (!instance->CheckRequiredBosses(_bossId)) + { + EnterEvadeMode(); + return; + } + instance->SetBossState(_bossId, IN_PROGRESS); + } +} + +void BossAI::TeleportCheaters() +{ + float x, y, z; + me->GetPosition(x, y, z); + + ThreatContainer::StorageType threatList = me->getThreatManager().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)) + 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); + if (me->IsInCombat()) + DoZoneInCombat(summon); +} + +void BossAI::SummonedCreatureDespawn(Creature* summon) +{ + summons.Despawn(summon); +} + +void BossAI::UpdateAI(uint32 diff) +{ + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + ExecuteEvent(eventId); + + DoMeleeAttackIfReady(); +} + +// WorldBossAI - for non-instanced bosses + +WorldBossAI::WorldBossAI(Creature* creature) : + ScriptedAI(creature), + summons(creature) +{ +} + +void WorldBossAI::_Reset() +{ + if (!me->IsAlive()) + return; + + events.Reset(); + summons.DespawnAll(); +} + +void WorldBossAI::_JustDied() +{ + events.Reset(); + summons.DespawnAll(); +} + +void WorldBossAI::_EnterCombat() +{ + Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true); + if (target) + AttackStart(target); +} + +void WorldBossAI::JustSummoned(Creature* summon) +{ + summons.Summon(summon); + Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true); + if (target) + summon->AI()->AttackStart(target); +} + +void WorldBossAI::SummonedCreatureDespawn(Creature* summon) +{ + summons.Despawn(summon); +} + +void WorldBossAI::UpdateAI(uint32 diff) +{ + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + ExecuteEvent(eventId); + + DoMeleeAttackIfReady(); +} + +// SD2 grid searchers. +Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive /*= true*/) +{ + return source->FindNearestCreature(entry, maxSearchRange, alive); +} + +GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange) +{ + return source->FindNearestGameObject(entry, maxSearchRange); +} + +void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange) +{ + source->GetCreatureListWithEntryInGrid(list, entry, maxSearchRange); +} + +void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange) +{ + source->GetGameObjectListWithEntryInGrid(list, entry, maxSearchRange); +} |