#include "config.h" #include "ScriptMgr.h" #include "../../shared/WorldPacket.h" #include "../../game/Player.h" #include "../../game/ObjectMgr.h" #include "../../game/GameObject.h" #include "../../game/GossipDef.h" #include "../../game/QuestDef.h" #include "../../game/TargetedMovementGenerator.h" #include "../../shared/Database/DBCStores.h" #include "../../mangosd/Master.h" #include "../../game/Spell.h" uint8 loglevel = 0; int nrscripts; Script *m_scripts[MAX_SCRIPTS]; //Spells Addons System //Spell Information on Exclusive script spells. void SetSpellAddon(); // Spell summary for ScriptedAI::SelectSpell struct TSpellSummary { uint8 Targets; // set of enum SelectTarget uint8 Effects; // set of enum SelectEffect } *SpellSummary; extern void AddSC_npc_generic_creature(); MANGOS_DLL_EXPORT void ScriptsFree() { for(int i=0;iName == Name ) return m_scripts[i]; } return NULL; } //******************************** //*** Functions to be Exported *** MANGOS_DLL_EXPORT bool GossipHello ( Player * player, Creature *_Creature ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_Creature->GetCreatureInfo()->ScriptName); if(!tmpscript || !tmpscript->pGossipHello) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pGossipHello(player,_Creature); } MANGOS_DLL_EXPORT bool GossipSelect( Player *player, Creature *_Creature,uint32 sender, uint32 action ) { Script *tmpscript = NULL; printf("action: %d\n",action); tmpscript = GetScriptByName(_Creature->GetCreatureInfo()->ScriptName); if(!tmpscript || !tmpscript->pGossipSelect) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pGossipSelect(player,_Creature,sender,action); } MANGOS_DLL_EXPORT bool GossipSelectWithCode( Player *player, Creature *_Creature, uint32 sender, uint32 action, char* sCode ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_Creature->GetCreatureInfo()->ScriptName); if(!tmpscript || !tmpscript->pGossipSelectWithCode) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pGossipSelectWithCode(player,_Creature,sender,action,sCode); } MANGOS_DLL_EXPORT bool QuestAccept( Player *player, Creature *_Creature, Quest *_Quest ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_Creature->GetCreatureInfo()->ScriptName); if(!tmpscript || !tmpscript->pQuestAccept) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pQuestAccept(player,_Creature,_Quest); } MANGOS_DLL_EXPORT bool QuestSelect( Player *player, Creature *_Creature, Quest *_Quest ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_Creature->GetCreatureInfo()->ScriptName); if(!tmpscript || !tmpscript->pQuestSelect) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pQuestSelect(player,_Creature,_Quest); } MANGOS_DLL_EXPORT bool QuestComplete( Player *player, Creature *_Creature, Quest *_Quest ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_Creature->GetCreatureInfo()->ScriptName); if(!tmpscript || !tmpscript->pQuestComplete) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pQuestComplete(player,_Creature,_Quest); } MANGOS_DLL_EXPORT bool ChooseReward( Player *player, Creature *_Creature, Quest *_Quest, uint32 opt ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_Creature->GetCreatureInfo()->ScriptName); if(!tmpscript || !tmpscript->pChooseReward) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pChooseReward(player,_Creature,_Quest,opt); } MANGOS_DLL_EXPORT uint32 NPCDialogStatus( Player *player, Creature *_Creature ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_Creature->GetCreatureInfo()->ScriptName); if(!tmpscript || !tmpscript->pNPCDialogStatus) return 100; player->PlayerTalkClass->ClearMenus(); return tmpscript->pNPCDialogStatus(player,_Creature); } MANGOS_DLL_EXPORT bool ItemHello( Player *player, Item *_Item, Quest *_Quest ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_Item->GetProto()->ScriptName); if(!tmpscript || !tmpscript->pItemHello) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pItemHello(player,_Item,_Quest); } MANGOS_DLL_EXPORT bool ItemQuestAccept( Player *player, Item *_Item, Quest *_Quest ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_Item->GetProto()->ScriptName); if(!tmpscript || !tmpscript->pItemQuestAccept) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pItemQuestAccept(player,_Item,_Quest); } MANGOS_DLL_EXPORT bool GOHello( Player *player, GameObject *_GO ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_GO->GetGOInfo()->ScriptName); if(!tmpscript || !tmpscript->pGOHello) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pGOHello(player,_GO); } MANGOS_DLL_EXPORT bool GOQuestAccept( Player *player, GameObject *_GO, Quest *_Quest ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_GO->GetGOInfo()->ScriptName); if(!tmpscript || !tmpscript->pGOQuestAccept) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pGOQuestAccept(player,_GO,_Quest); } MANGOS_DLL_EXPORT bool GOChooseReward( Player *player, GameObject *_GO, Quest *_Quest, uint32 opt ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_GO->GetGOInfo()->ScriptName); if(!tmpscript || !tmpscript->pGOChooseReward) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pGOChooseReward(player,_GO,_Quest,opt); } MANGOS_DLL_EXPORT bool AreaTrigger ( Player *player, Quest *_Quest, uint32 triggerID ) { Script *tmpscript = NULL; // TODO: load a script name for the areatriggers //tmpscript = GetScriptByName(); if(!tmpscript || !tmpscript->pAreaTrigger) return false; player->PlayerTalkClass->ClearMenus(); return tmpscript->pAreaTrigger(player,_Quest,triggerID); } MANGOS_DLL_EXPORT CreatureAI* GetAI(Creature *_Creature) { Script *tmpscript = GetScriptByName(_Creature->GetCreatureInfo()->ScriptName); if(!tmpscript || !tmpscript->GetAI) return NULL; return tmpscript->GetAI(_Creature); } MANGOS_DLL_EXPORT bool ItemUse( Player *player, Item* _Item, SpellCastTargets const& targets) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_Item->GetProto()->ScriptName); if(!tmpscript || !tmpscript->pItemUse) return false; return tmpscript->pItemUse(player,_Item,targets); } MANGOS_DLL_EXPORT bool ReceiveEmote( Player *player, Creature *_Creature, uint32 emote ) { Script *tmpscript = NULL; tmpscript = GetScriptByName(_Creature->GetCreatureInfo()->ScriptName); if(!tmpscript || !tmpscript->pReceiveEmote) return false; return tmpscript->pReceiveEmote(player, _Creature, emote); } MANGOS_DLL_EXPORT InstanceData* CreateInstanceData(Map *map) { Script *tmpscript = NULL; tmpscript = GetScriptByName(map->GetScript()); if(!tmpscript || !tmpscript->GetInstanceData) return false; return tmpscript->GetInstanceData(map); } bool ScriptedAI::IsVisible(Unit* who) const { if (!who) return false; return m_creature->IsWithinDistInMap(who, VISIBLE_RANGE) && who->isVisibleForOrDetect(m_creature,true); } void ScriptedAI::AttackStart(Unit* who) { if (!who) return; if (who->isTargetableForAttack()) { //Begin attack DoStartMeleeAttack(who); } } void ScriptedAI::UpdateAI(const uint32 diff) { //Check if we have a current target if( m_creature->isAlive() && m_creature->SelectHostilTarget() && m_creature->getVictim()) { //If we are within range melee the target if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) { if( m_creature->isAttackReady() ) { m_creature->AttackerStateUpdate(m_creature->getVictim()); m_creature->resetAttackTimer(); } } } } void ScriptedAI::EnterEvadeMode() { m_creature->RemoveAllAuras(); m_creature->DeleteThreatList(); m_creature->CombatStop(); DoGoHome(); } void ScriptedAI::DoStartMeleeAttack(Unit* victim) { if (!victim) return; if ( m_creature->Attack(victim) ) { m_creature->addUnitState(UNIT_STAT_FOLLOW); //m_creature->GetMotionMaster()->MovementExpired(); m_creature->GetMotionMaster()->Mutate(new TargetedMovementGenerator(*victim)); m_creature->AddThreat(victim, 0.0f); m_creature->resetAttackTimer(); if (victim->GetTypeId() == TYPEID_PLAYER) m_creature->SetLootRecipient((Player*)victim); } } void ScriptedAI::DoMeleeAttackIfReady() { //If we are within range melee the target if( m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE)) { //Make sure our attack is ready and we arn't currently casting if( m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false)) { m_creature->AttackerStateUpdate(m_creature->getVictim()); m_creature->resetAttackTimer(); } } } void ScriptedAI::DoStartRangedAttack(Unit* victim) { if (!victim) return; if (m_creature->Attack(victim)) { m_creature->AddThreat(victim, 0.0f); m_creature->resetAttackTimer(); if (victim->GetTypeId() == TYPEID_PLAYER) m_creature->SetLootRecipient((Player*)victim); } } void ScriptedAI::DoStopAttack() { if( m_creature->getVictim() != NULL ) { m_creature->AttackStop(); } } void ScriptedAI::DoCast(Unit* victim, uint32 spelId) { if (!victim || m_creature->IsNonMeleeSpellCasted(false)) return; m_creature->StopMoving(); m_creature->CastSpell(victim, spelId, false); } void ScriptedAI::DoCastSpell(Unit* who,SpellEntry const *spellInfo) { if (!who || m_creature->IsNonMeleeSpellCasted(false)) return; m_creature->StopMoving(); m_creature->CastSpell(who, spellInfo, false); } void ScriptedAI::DoSay(const char* text, uint32 language, Unit* target) { if (target)m_creature->Say(text, language, target->GetGUID()); else m_creature->Say(text, language, 0); } void ScriptedAI::DoDoSay(const char *text,...) { va_list ap; char str [1024]; va_start(ap, text); vsnprintf(str,1024,text, ap ); va_end(ap); DoSay(str,LANG_UNIVERSAL,NULL); } void ScriptedAI::DoYell(const char* text, uint32 language, Unit* target) { if (target)m_creature->Yell(text, language, target->GetGUID()); else m_creature->Yell(text, language, 0); } void ScriptedAI::DoTextEmote(const char* text, Unit* target) { if (target)m_creature->TextEmote(text, target->GetGUID()); else m_creature->TextEmote(text, 0); } void ScriptedAI::DoGoHome() { if( m_creature->isAlive() ) { float x,y,z,d; m_creature->GetRespawnCoord(x,y,z); m_creature->GetRespawnDist(d); if( m_creature->GetDistanceSq(x,y,z) > (d*d) ) { m_creature->clearUnitState(UNIT_STAT_FOLLOW); m_creature->GetMotionMaster()->TargetedHome(); } } } void ScriptedAI::DoPlaySoundToSet(Unit* unit, uint32 sound) { if (!unit) return; WorldPacket data(4); data.SetOpcode(SMSG_PLAY_SOUND); data << uint32(sound); unit->SendMessageToSet(&data,false); } void ScriptedAI::DoFaceTarget(Unit *unit) { if (!unit) return; //Face target m_creature->SetInFront(unit); } Creature* ScriptedAI::DoSpawnCreature(uint32 id, float x, float y, float z, float angle, uint32 type, uint32 despawntime) { return m_creature->SummonCreature(id,m_creature->GetPositionX() + x,m_creature->GetPositionY() + y,m_creature->GetPositionZ() + z, angle, (TempSummonType)type, despawntime); } /*Unit* ScriptedAI::SelectUnit(SelectAggroTarget target, uint32 position) { //ThreatList m_threatlist; std::list& m_threatlist = m_creature->getThreatManager().getThreatList(); std::list::iterator i = m_threatlist.begin(); std::list::reverse_iterator r = m_threatlist.rbegin(); if (position >= m_threatlist.size() || !m_threatlist.size()) return NULL; switch (target) { case SELECT_TARGET_RANDOM: advance ( i , rand()%m_threatlist.size()); return Unit::GetUnit((*m_creature),(*i)->getUnitGuid()); break; case SELECT_TARGET_TOPAGGRO: advance ( i , position); return Unit::GetUnit((*m_creature),(*i)->getUnitGuid()); break; case SELECT_TARGET_BOTTOMAGGRO: advance ( r , position); return Unit::GetUnit((*m_creature),(*r)->getUnitGuid()); break; } return NULL; }*/ /*SpellEntry const* ScriptedAI::SelectSpell(Unit* Target, int32 School, int32 Mechanic, SelectTarget Targets, uint32 PowerCostMin, uint32 PowerCostMax, float RangeMin, float RangeMax, SelectEffect Effects) { //No target so we can't cast if (!Target) return false; //Silenced so we can't cast if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) return false; //Using the extended script system we first create a list of viable spells SpellEntry const* Spell[4]; Spell[0] = 0; Spell[1] = 0; Spell[2] = 0; Spell[3] = 0; uint32 SpellCount = 0; SpellEntry const* TempSpell; SpellRangeEntry const* TempRange; //Check if each spell is viable(set it to null if not) for (uint32 i = 0; i < 4; i++) { TempSpell = GetSpellStore()->LookupEntry(m_creature->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[m_creature->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[m_creature->m_spells[i]].Effects & (1 << (Effects-1))) ) continue; //Check for school if specified if (School >= 0 && TempSpell->School != School) continue; //Check for spell mechanic if specified if (Mechanic >= 0 && 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 > m_creature->GetPower((Powers)TempSpell->powerType)) continue; //Get the Range TempRange = GetSpellRangeStore()->LookupEntry(TempSpell->rangeIndex); //Spell has invalid range store so we can't use it if (!TempRange) continue; //Check if the spell meets our range requirements if (RangeMin && TempRange->maxRange < RangeMin) continue; if (RangeMax && TempRange->maxRange > RangeMax) continue; //Check if our target is in range if (m_creature->IsWithinDistInMap(Target, TempRange->minRange) || !m_creature->IsWithinDistInMap(Target, TempRange->maxRange)) continue; //All good so lets add it to the spell list Spell[SpellCount] = TempSpell; SpellCount++; } //We got our usable spells so now lets randomly pick one if (!SpellCount) return NULL; return Spell[rand()%SpellCount]; }*/ bool ScriptedAI::CanCast(Unit* Target, SpellEntry const *Spell) { //No target so we can't cast if (!Target) return false; //Silenced so we can't cast if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) return false; //Check for power if (m_creature->GetPower((Powers)Spell->powerType) < Spell->manaCost) return false; SpellRangeEntry const *TempRange = NULL; TempRange = GetSpellRangeStore()->LookupEntry(Spell->rangeIndex); //Spell has invalid range store so we can't use it if (!TempRange) return false; //Unit is out of range of this spell if (m_creature->GetDistanceSq(Target) > TempRange->maxRange*TempRange->maxRange || m_creature->GetDistanceSq(Target) < TempRange->minRange*TempRange->minRange) return false; return true; } //New AI System /*void ScriptedAI::AISelectAction(Unit* who) { //Special Check on NULL, Called Only If Creature Do Nothing if(!who) { if( AICheckFriendTarget()) { srand(time(0)); if(!irand(0,10)) { srand(time(0)); Unit *copain = m_creature->GetUnit((*m_creature),m_creature->GetUInt64Value(UNIT_FIELD_TARGET)); if(copain->getVictim() && AIHPPercent(copain) > AIHPPercent(copain->getVictim())) AIAttackStart( copain->getVictim() ); else if(!irand(0,20)) AIMotionTarget(copain,5.0f); } return; } if( AICheckVictimTarget() ) { srand(time(0)); if( !irand(0,10) ) AIMotionTarget(m_creature->getVictim(),1.0f); return; } //EnterEvadeMode(); return; } float attackRadius = m_creature->GetAttackDistance(who); //MoveInLineOfSight Normal if( !AICheckVictimTarget() && !AICheckFriendTarget() && m_creature->IsHostileTo(who) && who->isInAccessablePlaceFor(m_creature) && m_creature->IsWithinLOSInMap(who) ) { if( m_creature->IsWithinDistInMap(who, attackRadius) ) { if(who->HasStealthAura()) who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH); AIAttackStart(who); return; } } //MoveInLineOfSight Normal, Who Is Maybe A friend if(!AICheckVictimTarget() && !AICheckFriendTarget() ) { if( m_creature->IsWithinDistInMap(who,RANGE_ASSIST ) && m_creature->IsFriendlyTo(who) && m_creature->IsWithinLOSInMap(who) ) { Unit *victimTarget = who->getVictim(); if( victimTarget && !m_creature->IsFriendlyTo(victimTarget) ) { if( AIHasHealing() && AIManaPercent(m_creature) > 10) m_creature->SetUInt64Value( UNIT_FIELD_TARGET, who->GetGUID() ); else AIAttackStart(victimTarget); } } } } */ void ScriptedAI::AISelectSpell(CreatureSpellCasting *spellCasting, Unit *dynamicTarget, uint8 specialCase) { uint16 chosenSpellId; std::list spellIndexFound; spellIndexFound.clear(); if(spellCasting->spellIndex > -1 || !spellCasting->spellBook || m_creature->HasSpellCooldown() ) return; Unit *enemyTarget = NULL; Unit *friendTarget = NULL; if(dynamicTarget) { if(m_creature->IsHostileTo(dynamicTarget)) enemyTarget = dynamicTarget; else friendTarget = dynamicTarget; } else if(AICheckFriendTarget()) friendTarget = m_creature->GetUnit(*m_creature, m_creature->GetUInt64Value(UNIT_FIELD_TARGET)); else if(AICheckVictimTarget()) enemyTarget = m_creature->getVictim(); switch( specialCase ) { case SELECTSPELL_NONE: break; case SELECTSPELL_SPEED: { spellIndexFound.clear(); for(uint8 i = CREATURE_SPELL_SPEED_1; i < CREATURE_SPELL_SPEED_MAX; i++) { CreatureSpellInfo *spellInfo = &spellCasting->spellBook->info[i]; if( spellInfo->id && !m_creature->HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) spellIndexFound.push_front(i); } if( spellIndexFound.size() > 0 ) { chosenSpellId = 0; std::list::iterator itr = spellIndexFound.begin(); for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; spellCasting->spellIndex = (*itr); chosenSpellId = spellCasting->spellBook->info[(*itr)].id; //DoDoSay("Speed: %u",chosenSpellId); SetSpellMaxRange(spellCasting); if( !(spellCasting->spellBook->info[(*itr)].targets & (1 << (SELECT_TARGET_ANY_FRIEND-1))) ) spellCasting->targetGUID = m_creature->GetGUID(); else spellCasting->targetGUID = enemyTarget->GetGUID(); } }return; case SELECTSPELL_INTERRUPT: { spellIndexFound.clear(); CreatureSpellInfo *spellInfo = &spellCasting->spellBook->info[CREATURE_SPELL_INTERRUPT_1]; if( spellInfo->id && !m_creature->HasSpellCooldown(spellInfo->id) && AICheckTakePower(CREATURE_SPELL_INTERRUPT_1) ) { spellCasting->spellIndex = CREATURE_SPELL_INTERRUPT_1; chosenSpellId = spellInfo->id; SetSpellMaxRange(spellCasting); spellCasting->targetGUID = enemyTarget->GetGUID(); } }return; } if( friendTarget ) { if( friendTarget->isAlive() ) { //heal Friend spellIndexFound.clear(); for(uint8 i = CREATURE_SPELL_HEALING_1; i < CREATURE_SPELL_HEALING_MAX; i++) { CreatureSpellInfo *spellInfo = &spellCasting->spellBook->info[i]; if( spellInfo->id && !m_creature->HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) spellIndexFound.push_front(i); } if( spellIndexFound.size() > 0 && AIHPPercent(friendTarget) < 70) { chosenSpellId = 0; std::list::iterator itr = spellIndexFound.begin(); for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; spellCasting->spellIndex = (*itr); chosenSpellId = spellCasting->spellBook->info[(*itr)].id; //DoDoSay("Healing(Friend): %u",chosenSpellId); spellCasting->targetGUID = friendTarget->GetGUID(); SetSpellMaxRange(spellCasting); if(!dynamicTarget) AIMotionTarget(friendTarget,(rand_norm()*spellCasting->range)+1.0f); return; } //Buff Friend spellIndexFound.clear(); for(uint8 i = CREATURE_SPELL_BUFF_1; i < CREATURE_SPELL_BUFF_MAX; i++) { CreatureSpellInfo *spellInfo = &spellCasting->spellBook->info[i]; if( spellInfo->id && !m_creature->HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) spellIndexFound.push_front(i); } if( spellIndexFound.size() > 0 ) { srand(getMSTime()); chosenSpellId = 0; std::list::iterator itr = spellIndexFound.begin(); for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; spellCasting->spellIndex = (*itr); chosenSpellId = spellCasting->spellBook->info[(*itr)].id; //DoDoSay("Buff Friend: %u",chosenSpellId); spellCasting->targetGUID = friendTarget->GetGUID(); SetSpellMaxRange(spellCasting); if(!dynamicTarget) AIMotionTarget(friendTarget,(rand_norm()*spellCasting->range)+1.0f); return; } } } else if(enemyTarget) { if(!dynamicTarget) { float distance = sqrt(m_creature->GetDistance2dSq(enemyTarget)); if( !spellCasting->oldTargetRange ) spellCasting->oldTargetRange = distance; // Target Is Casting a Channeled Spell if( enemyTarget->HasSpellChannelActiveTimer() ) { //Cast Intterupted Spell if we have available } // Target Is Running Away, try speed-up to get to her. // Far from what i wan... Need to correct that situation //Potentiel Charge, but for sure Target is comming to Us // Far from what i wan... Need to correct that situation if( distance < spellCasting->oldTargetRange /*&& !enemyTarget->IsStopped()*/) { spellCasting->oldTargetRange = distance; spellIndexFound.clear(); for(uint8 i = CREATURE_SPELL_STUNT_1; i < CREATURE_SPELL_STUNT_MAX; i++) { CreatureSpellInfo *spellInfo = &spellCasting->spellBook->info[i]; if( spellInfo->id && !m_creature->HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) spellIndexFound.push_front(i); } if( spellIndexFound.size() > 0 ) { srand(getMSTime()); chosenSpellId = 0; std::list::iterator itr = spellIndexFound.begin(); for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; spellCasting->spellIndex = (*itr); chosenSpellId = spellCasting->spellBook->info[(*itr)].id; //DoDoSay("Stun: %u",chosenSpellId); SetSpellMaxRange(spellCasting); if( !(spellCasting->spellBook->info[chosenSpellId].targets & (1 << (SELECT_TARGET_SELF-1))) ) spellCasting->targetGUID = m_creature->GetGUID(); else { AIMotionTarget(enemyTarget,(rand_norm()*spellCasting->range)+1.0f); spellCasting->targetGUID = enemyTarget->GetGUID(); } //spellCasting->spellBook->castingTimer = spellCasting->spellBook->info[(*itr)].castingTime; spellCasting->targetGUID = enemyTarget->GetGUID(); return; } } } //Healing if( AIHPPercent(m_creature) < irand(40,90) ) { spellIndexFound.clear(); for(uint8 i = CREATURE_SPELL_HEALING_1; i < CREATURE_SPELL_HEALING_MAX; i++) { CreatureSpellInfo *spellInfo = &spellCasting->spellBook->info[i]; if( spellInfo->id && !m_creature->HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) spellIndexFound.push_front(i); } if( spellIndexFound.size() > 0 ) { srand(getMSTime()); chosenSpellId = 0; std::list::iterator itr = spellIndexFound.begin(); for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; spellCasting->spellIndex = (*itr); chosenSpellId = spellCasting->spellBook->info[(*itr)].id; //DoDoSay("Healing: %u",chosenSpellId); //spellCasting->spellBook->castingTimer = spellCasting->spellBook->info[(*itr)].castingTime; spellCasting->targetGUID = m_creature->GetGUID(); SetSpellMaxRange(spellCasting); return; } } else //Buff { spellIndexFound.clear(); for(uint8 i = CREATURE_SPELL_BUFF_1; i < CREATURE_SPELL_BUFF_MAX; i++) { CreatureSpellInfo *spellInfo = &spellCasting->spellBook->info[i]; if( spellInfo->id && !m_creature->HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) spellIndexFound.push_front(i); } if( spellIndexFound.size() > 0 ) { srand(getMSTime()); chosenSpellId = 0; std::list::iterator itr = spellIndexFound.begin(); for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; spellCasting->spellIndex = (*itr); chosenSpellId = spellCasting->spellBook->info[(*itr)].id; //DoDoSay("Buff: %u",chosenSpellId); //spellCasting->spellBook->castingTimer = spellCasting->spellBook->info[(*itr)].castingTime; spellCasting->targetGUID = m_creature->GetGUID(); SetSpellMaxRange(spellCasting); return; } } //Attack Spell spellIndexFound.clear(); for(uint8 i = CREATURE_SPELL_ATTACK_1; i < CREATURE_SPELL_ATTACK_MAX; i++) { CreatureSpellInfo *spellInfo = &spellCasting->spellBook->info[i]; if( spellInfo->id && !m_creature->HasSpellCooldown(spellInfo->id) && AICheckTakePower(i) ) spellIndexFound.push_front(i); } if( spellIndexFound.size() ) { srand(getMSTime()); chosenSpellId = 0; std::list::iterator itr = spellIndexFound.begin(); for (uint8 randomIndex = irand(0,spellIndexFound.size()-1); 0 < randomIndex; --randomIndex)++itr; spellCasting->spellIndex = (*itr); chosenSpellId = spellCasting->spellBook->info[(*itr)].id; //DoDoSay("Attack: %u",chosenSpellId); spellCasting->targetGUID = enemyTarget->GetGUID(); SetSpellMaxRange(spellCasting); if(!dynamicTarget) AIMotionTarget(enemyTarget,spellCasting->range/1.1f); return; } if(!dynamicTarget && m_creature->IsStopped()) //Debug, got to be removed... AIMotionTarget(enemyTarget,1.5f); } } void ScriptedAI::AICast(CreatureSpellCasting *spellCasting) { Unit *target = m_creature->GetUnit((*m_creature),spellCasting->targetGUID); if( !target || !AICheckTakePower(spellCasting->spellIndex, true) ) { spellCasting->reset(); return; } m_creature->CastSpell(target, spellCasting->spellBook->info[spellCasting->spellIndex].id, false); spellCasting->started = true; } void ScriptedAI::AIMotionTarget(Unit *target, float offSet) { bool exist = false; MotionMaster* creatureMM = m_creature->GetMotionMaster(); float angle = m_creature->IsHostileTo(target) ? NULL : target->GetOrientation() + (rand_norm()*M_PI); if(creatureMM->top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE) { TargetedMovementGenerator* targetMV = static_cast*> (creatureMM->top()); if( targetMV && targetMV->GetTarget() ) { if( targetMV->GetTarget()->GetGUID() != target->GetGUID() ) { if( !m_creature->hasUnitState(UNIT_STAT_FOLLOW) ) m_creature->addUnitState(UNIT_STAT_FOLLOW); else creatureMM->MovementExpired(); creatureMM->Mutate(new TargetedMovementGenerator(*target,offSet,angle)); } else if(targetMV->GetOffSet() != offSet) targetMV->SetOffSet(offSet); } } else { m_creature->addUnitState(UNIT_STAT_FOLLOW); //Add Home, Confuse and Then Targeted m_creature->GetMotionMaster()->Mutate(new TargetedMovementGenerator(*target,offSet,angle)); } } bool ScriptedAI::AICheckFriendTarget() { //friend target not found so we reset Status of Friend Follow. if( !m_creature->getVictim() && m_creature->GetUInt64Value(UNIT_FIELD_TARGET) ) { Unit* friendTarget = m_creature->GetUnit( *m_creature, m_creature->GetUInt64Value(UNIT_FIELD_TARGET) ); if( friendTarget && friendTarget->isAlive() && friendTarget->isInCombat() && !m_creature->IsHostileTo(friendTarget) && m_creature->IsWithinDistInMap(friendTarget,35.0f)) return true; else { if(friendTarget) friendTarget->m_hasHealer = false; m_creature->clearUnitState(UNIT_STAT_FOLLOW); m_creature->SetUInt64Value(UNIT_FIELD_TARGET,0); } } return false; } bool ScriptedAI::AICheckVictimTarget() { //Check if we have a current target if( m_creature->getVictim() ) { if( m_creature->GetUInt64Value(UNIT_FIELD_TARGET) != m_creature->getVictim()->GetGUID() ) m_creature->SetUInt64Value(UNIT_FIELD_TARGET, m_creature->getVictim()->GetGUID()); if( m_creature->getVictim()->isTargetableForAttack() && m_creature->isAlive()) return true; } return false; } bool ScriptedAI::AICheckTakePower(uint8 spellIndex, bool take) { uint32 currentPowerValue; // Don't take SpellAddon PowerType, Because can be missing, so is up to us // to not put spell for mage on a warrior. Powers creaturePowerType = m_creature->getPowerType(); switch( creaturePowerType ) { case POWER_RAGE: currentPowerValue = m_creature->GetPower(POWER_RAGE)/10; break; case POWER_MANA: currentPowerValue = m_creature->GetPower(POWER_MANA); break; case POWER_FOCUS: currentPowerValue = m_creature->GetPower(POWER_FOCUS); break; case POWER_ENERGY: currentPowerValue = m_creature->GetPower(POWER_ENERGY); break; case POWER_HAPPINESS: currentPowerValue = m_creature->GetPower(POWER_HAPPINESS); break; } if( m_creature->m_creatureSpellBook->info[spellIndex].cost > currentPowerValue ) return false; if( take && !m_creature->m_creatureSpellBook->info[spellIndex].usingDBC ) switch(POWER_MANA) //We Only use this one for The Moment { case POWER_RAGE: m_creature->SetPower(creaturePowerType, (currentPowerValue - m_creature->m_creatureSpellBook->info[spellIndex].cost)*10 );break; case POWER_MANA: m_creature->SetPower(creaturePowerType, (currentPowerValue - m_creature->m_creatureSpellBook->info[spellIndex].cost) );break; case POWER_FOCUS: m_creature->SetPower(creaturePowerType, (currentPowerValue - m_creature->m_creatureSpellBook->info[spellIndex].cost) );break; case POWER_ENERGY: m_creature->SetPower(creaturePowerType, (currentPowerValue - m_creature->m_creatureSpellBook->info[spellIndex].cost) );break; case POWER_HAPPINESS: m_creature->SetPower(creaturePowerType, (currentPowerValue - m_creature->m_creatureSpellBook->info[spellIndex].cost) );break; } return true; } float ScriptedAI::SetSpellMaxRange(CreatureSpellCasting *spellCasting) { // Not OK ... float _rangeMax = spellCasting->spellBook->info[spellCasting->spellIndex].range[1] - 2.0f; spellCasting->range = _rangeMax < 5.5f ? 5.5f : _rangeMax; return spellCasting->range; } uint8 ScriptedAI::AICombatRating(Unit* unit) { uint32 rating = 0; //3 point at each 10Level, so level 56, got 15 Point rating += unit->getLevel()/10; //1 point for each 1000HP rating += unit->GetMaxHealth()/1000 > 50 ? 50 : unit->GetMaxHealth()/1000; rating += 100-AIHPPercent(unit); return uint8(rating); } //Init Function void ScriptedAI::SetSpellAddon(CreatureSpellCasting *spellCasting) { SpellEntry const* dbcSpell; SpellRangeEntry const *dbcRange; SpellDurationEntry const * dbcDuration; SpellCastTimesEntry const *dbcCastTime; CreatureSpellInfoMap::iterator itr = spellCasting->spellBook->info.begin(); if(itr == spellCasting->spellBook->info.end()) return; for(; itr != spellCasting->spellBook->info.end(); ++itr) { dbcSpell = GetSpellStore()->LookupEntry(itr->second.id); if( !dbcSpell ) continue; dbcRange = GetSpellRangeStore()->LookupEntry(dbcSpell->rangeIndex); dbcDuration = GetSpellDurationStore()->LookupEntry(dbcSpell->DurationIndex); dbcCastTime = GetCastTimesStore()->LookupEntry(dbcSpell->CastingTimeIndex); for (int j=0; j<3; j++) { //Spell targets self if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_SELF ) itr->second.targets |= 1 << (SELECT_TARGET_SELF-1); //Spell targets a single enemy if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE || dbcSpell->EffectImplicitTargetA[j] == TARGET_CURRENT_SELECTED_ENEMY ) itr->second.targets |= 1 << (SELECT_TARGET_SINGLE_ENEMY-1); //Spell targets AoE at enemy if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA || dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED ) itr->second.targets |= 1 << (SELECT_TARGET_AOE_ENEMY-1); //Spell targets an enemy if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_CHAIN_DAMAGE || dbcSpell->EffectImplicitTargetA[j] == TARGET_CURRENT_SELECTED_ENEMY || dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA || dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_INSTANT || dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_ENEMY_IN_AREA_CHANNELED ) itr->second.targets |= 1 << (SELECT_TARGET_ANY_ENEMY-1); //Spell targets a single friend(or self) if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_SELF || dbcSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_FRIEND || dbcSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_PARTY ) itr->second.targets |= 1 << (SELECT_TARGET_SINGLE_FRIEND-1); //Spell targets aoe friends if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER || dbcSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY ) itr->second.targets |= 1 << (SELECT_TARGET_AOE_FRIEND-1); //Spell targets any friend(or self) if ( dbcSpell->EffectImplicitTargetA[j] == TARGET_SELF || dbcSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_FRIEND || dbcSpell->EffectImplicitTargetA[j] == TARGET_SINGLE_PARTY || dbcSpell->EffectImplicitTargetA[j] == TARGET_ALL_PARTY_AROUND_CASTER || dbcSpell->EffectImplicitTargetA[j] == TARGET_AREAEFFECT_PARTY ) itr->second.targets |= 1 << (SELECT_TARGET_ANY_FRIEND-1); //Make sure that this spell includes a damage effect if ( dbcSpell->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE || dbcSpell->Effect[j] == SPELL_EFFECT_INSTAKILL || dbcSpell->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE || dbcSpell->Effect[j] == SPELL_EFFECT_HEALTH_LEECH ) itr->second.effects |= 1 << (SELECT_EFFECT_DAMAGE-1); //Make sure that this spell includes a healing effect if ( dbcSpell->Effect[j] == SPELL_EFFECT_HEAL || dbcSpell->Effect[j] == SPELL_EFFECT_HEAL_MAX_HEALTH || dbcSpell->Effect[j] == SPELL_EFFECT_HEAL_MECHANICAL ) itr->second.effects |= 1 << (SELECT_EFFECT_HEALING-1); //Make sure that this spell applies an aura if( dbcSpell->Effect[j] == SPELL_EFFECT_APPLY_AURA ) itr->second.effects |= 1 << (SELECT_EFFECT_AURA-1); // Buff Duration if( dbcDuration && itr->second.buffDuration < dbcDuration->Duration[j] )//DBC Info Not Good itr->second.buffDuration = dbcDuration->Duration[j]; } //We use powerCost from SQL on missing DBC value if( dbcSpell->manaCost ) { itr->second.usingDBC = true; itr->second.cost = dbcSpell->manaCost; } //CoolDown if( !itr->second.recoveryTime && dbcSpell->RecoveryTime ) itr->second.recoveryTime = dbcSpell->RecoveryTime; if(dbcRange) { //Range Min if( !itr->second.range[0] ) itr->second.range[0] = dbcRange->minRange; //Range Max if( !itr->second.range[1] )//DBC Info Not Good itr->second.range[1] = dbcRange->maxRange; } //Casttime if(dbcCastTime && dbcCastTime->CastTime) itr->second.isDelayed = true; } }