void ArcScriptCreatureAI::CastSpellNowNoScheduling(SpellDesc* pSpell) { if( CastSpellInternal(pSpell) ) { DelayNextAttack(CalcSpellAttackTime(pSpell)); } }
bool MoonScriptCreatureAI::CastSpellInternal(SpellDesc* pSpell, uint32 pCurrentTime) { if( pSpell == NULL) return false; //Do not cast if we are already casting if( IsCasting() ) return false; //We do not cast in special states such as : stunned, feared, silenced, charmed, asleep, confused and if they are not ignored if ( ( ~pSpell->mTargetType.mTargetFilter & TargetFilter_IgnoreSpecialStates ) && _unit->m_special_state & ( UNIT_STATE_STUN | UNIT_STATE_FEAR | UNIT_STATE_SLEEP | UNIT_STATE_SILENCE | UNIT_STATE_CHARM | UNIT_STATE_CONFUSE ) ) return false; //Do not cast if we are in cooldown uint32 CurrentTime = ( pCurrentTime == 0 ) ? (uint32)time(NULL) : pCurrentTime; if( pSpell->mLastCastTime + pSpell->mCooldown > CurrentTime ) return false; //Check range before casting Unit *Target = GetTargetForSpell( pSpell ); if( Target ) { RangeStatusPair Status; if( pSpell->mTargetType.mTargetFilter & TargetFilter_InRangeOnly || ( Status = GetSpellRangeStatusToUnit( Target, pSpell ) ).first == RangeStatus_Ok ) { //Safe-delay if we got special state flag before DelayNextAttack( CalcSpellAttackTime( pSpell ) ); //If we were running to a target, stop because we're in range now PopRunToTargetCache(); //Do emote associated with this spell RandomEmote(pSpell->mEmotes); //Cast spell now if( pSpell->mInfo ) CastSpellOnTarget(Target, pSpell->mTargetType, pSpell->mInfo, ( pSpell->mCastTime == 0 ) ? true : false); else if( pSpell->mSpellFunc ) pSpell->mSpellFunc(pSpell, this, Target, pSpell->mTargetType); else sLog.outDebug("MoonScriptCreatureAI::CastSpellInternal() : Invalid spell!\n"); //Store cast time for cooldown pSpell->mLastCastTime = CurrentTime; return true; } else if( !pSpell->mStrictRange ) //Target is out of range, run to it { PushRunToTargetCache( Target, pSpell, Status ); return false; } } //If we get here, its because the RunToTarget changed type, so its no longer valid, clear it PopRunToTargetCache(); DelayNextAttack(0); //Cancel attack delay return true; //No targets possible? Consider spell casted nonetheless }
void ArcScriptCreatureAI::AIUpdate() { SpellDesc* Spell; uint32 CurrentTime = (uint32)time(NULL); //Elapse timers for( TimerArray::iterator TimerIter = mTimers.begin(); TimerIter != mTimers.end(); ++TimerIter ) { TimerIter->second -= mAIUpdateFrequency; } //Check if we have a spell scheduled to be cast for( SpellDescList::iterator SpellIter = mScheduledSpells.begin(); SpellIter != mScheduledSpells.end(); ++SpellIter ) { Spell = (*SpellIter); if( CastSpellInternal(Spell, CurrentTime) ) //Can fail if we are already casting a spell, or if the spell is on cooldown { mScheduledSpells.erase(SpellIter); break; } } //Do not schedule spell if we are *currently* casting a non-instant cast spell if( !IsCasting() && !mRunToTargetCache ) { //Check if have queued spells that needs to be scheduled before we go back to random casting for( SpellDescList::iterator SpellIter = mQueuedSpells.begin(); SpellIter != mQueuedSpells.end(); ++SpellIter ) { Spell = (*SpellIter); mScheduledSpells.push_back(Spell); mQueuedSpells.erase(SpellIter); //Stop melee attack for a short while for scheduled spell cast if( Spell->mCastTime >= 0 ) { DelayNextAttack(mAIUpdateFrequency + CalcSpellAttackTime(Spell)); if( Spell->mCastTime > 0 ) { SetCanMove(false); SetBehavior(Behavior_Spell); } } return; //Scheduling one spell at a time, exit now } //Try our chance at casting a spell (Will actually be cast on next ai update, so we just //schedule it. This is needed to avoid next dealt melee damage while we cast the spell.) float ChanceRoll = RandomFloat(100), ChanceTotal = 0; for( SpellDescArray::iterator SpellIter = mSpells.begin(); SpellIter != mSpells.end(); ++SpellIter ) { Spell = (*SpellIter); if( Spell->mEnabled == false ) continue; if( Spell->mChance == 0 ) continue; //Check if spell won the roll if( (Spell->mChance == 100 || (ChanceRoll >= ChanceTotal && ChanceRoll < ChanceTotal + Spell->mChance)) && (Spell->mLastCastTime + Spell->mCooldown <= CurrentTime) && !IsSpellScheduled(Spell) ) { mScheduledSpells.push_back(Spell); //Stop melee attack for a short while for scheduled spell cast if( Spell->mCastTime >= 0 ) { DelayNextAttack(mAIUpdateFrequency + CalcSpellAttackTime(Spell)); if( Spell->mCastTime > 0 ) { SetCanMove(false); SetBehavior(Behavior_Spell); } } return; //Scheduling one spell at a time, exit now } else if( Spell->mChance != 100 ) ChanceTotal += Spell->mChance; //Only add spells that aren't 100% chance of casting } //Go back to default behavior since we didn't decide anything SetCanMove(true); SetBehavior(Behavior_Melee); //Random taunts if( ChanceRoll >= 95 ) RandomEmote(mOnTauntEmotes); } }