rendered paste bodyenum CreatureSpellIndex{ CREATURE_SPELL_MIN, CREATURE_SPELL_ATTACK_1 = 0, //Normal Attack CREATURE_SPELL_ATTACK_2, CREATURE_SPELL_ATTACK_3, CREATURE_SPELL_ATTACK_4, CREATURE_SPELL_ATTACK_5, CREATURE_SPELL_ATTACK_MAX, CREATURE_SPELL_SPEED_1 = 5, //When target Run Away CREATURE_SPELL_SPEED_2, CREATURE_SPELL_SPEED_MAX, CREATURE_SPELL_STUNT_1 = 7, CREATURE_SPELL_STUNT_2, //When target Run Closer CREATURE_SPELL_STUNT_MAX, CREATURE_SPELL_HEALING_1 = 9, //When Less Life Or Friend Target CREATURE_SPELL_HEALING_2, CREATURE_SPELL_HEALING_MAX, CREATURE_SPELL_BUFF_1 = 11, //When Begin of the fight Or Friend Target CREATURE_SPELL_BUFF_2, CREATURE_SPELL_BUFF_MAX, CREATURE_SPELL_INTERRUPT_1 = 13, //Select When Target is Casting Channeling Spell CREATURE_SPELL_INTERRUPT_MAX, CREATURE_SPELL_MAX = 14};struct CreatureSpellInfo{ CreatureSpellInfo() : id(NULL), recoveryTime(0), unknow1(0),buffDuration(0),cost(0), targets(0), usingDBC(false) { range[0] = 0.0f; range[1] = 0.0f; } uint8 targets,effects; uint32 id,recoveryTime,unknow1,buffDuration,cost; float range[2]; bool usingDBC, isDelayed;};typedef std::map<uint8,CreatureSpellInfo> CreatureSpellInfoMap;struct CreatureSpellBook{ // +1 is to create Global Cool at the End of spellbook. CreatureSpellBook() : entry(0), globalCoolDown(0) { info.clear(); } uint32 entry; CreatureSpellInfoMap info; uint64 globalCoolDown; bool hasHealing() { CreatureSpellInfoMap::iterator spellItr; for(uint8 i = CREATURE_SPELL_HEALING_1; i < CREATURE_SPELL_HEALING_MAX; ++i) { spellItr = info.find(i); if( spellItr != info.end() && spellItr->second.id) return true; } return false; }};void Creature::LoadSpellInfo(){ if(m_creatureSpellBook) delete m_creatureSpellBook; QueryResult* _result = sDatabase.PQuery("SELECT * FROM creature_spellbook WHERE entry=%u",GetEntry()); if(_result) { Field *_spellInfo = _result->Fetch(); char* _spellId = NULL; m_creatureSpellBook = new CreatureSpellBook(); //spellId, recoveryTime, castingTime, buffDuration, cost, rangeMin, rangeMax for(uint8 itr = CREATURE_SPELL_MIN; itr < CREATURE_SPELL_MAX; ++itr) { const char* rawSpellInfo = _spellInfo[itr+1].GetString(); _spellId = strtok((char*)rawSpellInfo, " "); if( _spellId ) { uint32 spellId = m_creatureSpellBook->info[itr].id = atoi(_spellId); char* _recoveryTime = strtok(NULL, " "); if(_recoveryTime) m_creatureSpellBook->info[itr].recoveryTime = atoi(_recoveryTime); char* _castingTime = strtok(NULL, " "); if(_castingTime) m_creatureSpellBook->info[itr].unknow1 = atoi(_castingTime); char* _buffDuration = strtok(NULL, " "); if(_buffDuration) m_creatureSpellBook->info[itr].buffDuration = atoi(_buffDuration); char* _cost = strtok(NULL, " "); if(_cost) m_creatureSpellBook->info[itr].cost = atoi(_cost); char* _rangeMin = strtok(NULL, " "); if(_rangeMin) m_creatureSpellBook->info[itr].range[0] = atof(_rangeMin); char* _rangeMax = strtok(NULL, " "); if(_rangeMax) m_creatureSpellBook->info[itr].range[1] = atof(_rangeMax); } } delete _result; }}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; }} void UpdateAI(const uint32 diff) { if( m_creature->isDead() ) return; //Spell Casting if(spellCasting->spellIndex > -1) { Unit *target = m_creature->GetUnit( (*m_creature),m_creature->GetUInt64Value(UNIT_FIELD_TARGET) ); Unit *spellTarget = NULL; if( spellCasting->targetGUID != m_creature->GetGUID() ) spellTarget = m_creature->GetUnit((*m_creature),spellCasting->targetGUID ); else spellTarget = ((Unit*)m_creature); bool dynamicTarget = false; if(spellCasting->targetGUID != m_creature->GetGUID() && spellCasting->targetGUID != m_creature->GetUInt64Value(UNIT_FIELD_TARGET)) dynamicTarget = true; if(spellTarget) { if( !spellCasting->reached ) { if(dynamicTarget) { if( m_creature->IsWithinDistInMap(spellTarget, spellCasting->range) ) spellCasting->reached = true; else { spellCasting->reset(); return; } } else { if( spellTarget->GetGUID() == m_creature->GetGUID() || m_creature->IsWithinDistInMap(spellTarget, spellCasting->range) ) spellCasting->reached = true; else { spellCasting->runningTimer += diff; if(spellCasting->runningTimer > irand(1000*CREATURE_WAIT_REACH_RATE,2000*CREATURE_WAIT_REACH_RATE) ) { DoDoSay("Too Long To Reach Target, IsStopped:%u ",m_creature->IsStopped()); //Hack spellCasting->runningTimer = 0; /*AICast()*/ } return; } } //if( !spellCasting->reached && m_creature->IsStopped() ) // Hack //AIMotionTarget(target,1.0f); } if( spellCasting->reached ) { if(!spellCasting->started) { if(!m_creature->IsStopped() && spellCasting->spellBook->info[spellCasting->spellIndex].castingTime) m_creature->StopMoving(); AICast(spellCasting); } else if(!m_creature->m_currentSpells[CURRENT_CHANNELED_SPELL])//Spell Finish { lastAction = getMSTime(); if(!dynamicTarget && target && target->isAlive() && m_creature->hasUnitState(UNIT_STAT_FOLLOW)) AIMotionTarget(target); spellCasting->reset(); } } } else if(dynamicTarget) //Bad dynamicTarget Target spellCasting->reset(); else //Bad Target spellCasting->reset(true); return; } //If we are within range melee the target, we try to swing it with our melee weapon. if( !AICheckFriendTarget() && m_creature->SelectHostilTarget() && m_creature->isAttackReady() && m_creature->IsWithinDistInMap(m_creature->getVictim(), float(ATTACK_DISTANCE/2)) ) { m_creature->AttackerStateUpdate(m_creature->getVictim()); m_creature->resetAttackTimer(); lastAction = getMSTime(); } //Choses a Spell To Cast if( spellCasting->spellIndex < 0 && m_creature->GetUInt64Value(UNIT_FIELD_TARGET) ) AISelectSpell( spellCasting ); //Wait Time Reset Action Selected if(lastAction+irand(5000*CREATURE_WAIT_RATE,7000*CREATURE_WAIT_RATE) < getMSTime() && m_creature->GetUInt64Value(UNIT_FIELD_TARGET)) { if( spellCasting->spellIndex > -1) spellCasting->reset(); if(AICheckFriendTarget()) { Unit* friendTarget = m_creature->GetUnit((*m_creature), m_creature->GetUInt64Value(UNIT_FIELD_TARGET)); if(friendTarget && friendTarget->getVictim()) AISelectSpell(spellCasting,friendTarget->getVictim()); else m_creature->SelectHostilTarget(); } else { Unit *target = m_creature->GetUnit( (*m_creature), m_creature->GetUInt64Value(UNIT_FIELD_TARGET) ); if(target && target->isAlive()) AIMotionTarget(target); else m_creature->SelectHostilTarget(); } lastAction = getMSTime(); } }};void ScriptedAI::AISelectSpell(CreatureSpellCasting *spellCasting, Unit *dynamicTarget, bool special){ uint16 chosenSpellId; std::list<uint8> spellIndexFound; spellIndexFound.clear(); if(spellCasting->spellIndex > -1 || !spellCasting->spellBook || m_creature->HasSpellCooldown() ) return; /*if(special && m_creature->m_spellsCoolDown[CREATURE_SPELL_SPECIAL_1] < 1 ) { spellCasting->spellId = m_creature->spellBook->spells[CREATURE_SPELL_SPECIAL_1]; if(spellCasting->spellId && AICheckTakePower(spellCasting->spellId)) { Unit *creatureTarget = m_creature->GetUnit(*m_creature, m_creature->GetUInt64Value(UNIT_FIELD_TARGET)); if(spellAddon[spellCasting->spellId].targets != TARGET_SELF && creatureTarget) AIMotionTarget(creatureTarget,AIFollowRange(spellCasting->spellId)); spellCasting->castingTimer = spellAddon[spellCasting->spellId].castTime; return; } return; // Cause Special case //AICast(m_creature->getVictim(), spellId, CREATURE_SPELL_SPECIAL_1, waitDuration); }*/ 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(); 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<uint8>::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); 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 ) { chosenSpellId = 0; std::list<uint8>::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); return; } } } else if(enemyTarget) { if(!dynamicTarget) { float distance = sqrt(m_creature->GetDistance2dSq(enemyTarget)); if( !spellCasting->oldTargetRange ) spellCasting->oldTargetRange = distance; // Target Is Running Away, try speed-up to get to her. if( distance > spellCasting->oldTargetRange && (enemyTarget->hasUnitState(UNIT_STAT_MOVING) || !enemyTarget->IsStopped())) { spellCasting->oldTargetRange = distance; 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<uint8>::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(); AICast(spellCasting); spellCasting->reset(); //AIMotionTarget(enemyTarget); } else { AIMotionTarget(enemyTarget); spellCasting->targetGUID = enemyTarget->GetGUID(); } //spellCasting->spellBook->castingTimer = spellCasting->spellBook->info[(*itr)].castingTime; return; } } //Potentiel Charge, but for sure Target is comming to Us if( distance - spellCasting->oldTargetRange < -8.0f /*&& !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 ) { chosenSpellId = 0; std::list<uint8>::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); spellCasting->targetGUID = enemyTarget->GetGUID(); } //spellCasting->spellBook->castingTimer = spellCasting->spellBook->info[(*itr)].castingTime; spellCasting->targetGUID = enemyTarget->GetGUID(); return; } } } //Healing if( AIHPPercent(m_creature) < 70 ) { 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 ) { chosenSpellId = 0; std::list<uint8>::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 ) { chosenSpellId = 0; std::list<uint8>::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() ) { chosenSpellId = 0; std::list<uint8>::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->spellBook->castingTimer = spellCasting->spellBook->info[(*itr)].castingTime; spellCasting->targetGUID = enemyTarget->GetGUID(); SetSpellMaxRange(spellCasting); if(!dynamicTarget) AIMotionTarget(enemyTarget); return; } /*if(!dynamicTarget) //Debug, got to be removed... AIMotionTarget(enemyTarget,rand_norm()*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;}