All pastes #834952 Raw Edit

Autorepeat Spells

public text v1 · immutable
#834952 ·published 2007-12-28 16:31 UTC
rendered paste body
Index: src/game/Spell.cpp
===================================================================
--- src/game/Spell.cpp	(revision 4935)
+++ src/game/Spell.cpp	(working copy)
@@ -1110,6 +1110,14 @@
 {
     m_targets = *targets;
 
+	//intialize first Auto-Repeat spell with target, further handling in Unit::_UpdateAutoRepeatspells
+	if (m_autoRepeat && m_caster->m_AutoRepeatFirstCast)
+	{	
+		m_spellState = SPELL_STATE_IDLE;
+		m_caster->SetCurrentCastedSpell( this );
+        return;
+	}
+
     m_spellState = SPELL_STATE_PREPARING;
 
     m_castPositionX = m_caster->GetPositionX();
@@ -1144,17 +1152,15 @@
         m_caster->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
     }
 
-    // do first cast of autorepeat spell with recovery time delay (like after any autocast)
-    if(IsAutoRepeat())
-        m_spellState = SPELL_STATE_FINISHED;
-
     if(m_IsTriggeredSpell)
         cast(true);
     else
     {
         m_caster->SetCurrentCastedSpell( this );
         m_selfContainer = &(m_caster->m_currentSpells[GetCurrentContainer()]);
-        SendSpellStart();
+        
+		if (m_autoRepeat) return; //graphics for Auto-Repeat spells handled in Unit::_UpdateAutoRepeatSpells
+		SendSpellStart();
     }
 }
 
@@ -1173,6 +1179,9 @@
             SendCastResult(SPELL_FAILED_INTERRUPTED);
         } break;
 
+		case SPELL_STATE_IDLE: //Auto-Repeat spells
+			SendInterrupted(0); break;
+
         case SPELL_STATE_CASTING:
         {
             for (int j = 0; j < 3; j++)
@@ -1809,7 +1818,7 @@
             }
 
             if(m_timer == 0 && !m_meleeSpell)
-                cast();
+                cast(m_autoRepeat);  //skip check for Auto-Repeat Spells, already checked in Unit::_UpdateAutoRepeatSpells
         } break;
         case SPELL_STATE_CASTING:
         {
Index: src/game/SpellEffects.cpp
===================================================================
--- src/game/SpellEffects.cpp	(revision 4935)
+++ src/game/SpellEffects.cpp	(working copy)
@@ -1140,8 +1140,9 @@
             if (m_caster->GetTypeId()!=TYPEID_PLAYER)
                 return;
 
-            // break Auto Shot
-            m_caster->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+            // break Auto Shot and "Attack"
+			((Player*)m_caster)->SendAttackSwingCancelAttack();
+            
             return;
         }
 
Index: src/game/SpellHandler.cpp
===================================================================
--- src/game/SpellHandler.cpp	(revision 4935)
+++ src/game/SpellHandler.cpp	(working copy)
@@ -734,9 +734,8 @@
 
 void WorldSession::HandleCancelAutoRepeatSpellOpcode( WorldPacket& /*recvPacket*/)
 {
-    // may be better send SMSG_CANCEL_AUTO_REPEAT?
-    // cancel and prepare for deleting
-    _player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+    // cancel and prepare for deleting, skip SendAutoRepeatCancel to avoid cancel/cast loops between server & client while changing the target
+    _player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL , false);
 }
 
 /// \todo Complete HandleCancelChanneling function
Index: src/game/Unit.cpp
===================================================================
--- src/game/Unit.cpp	(revision 4935)
+++ src/game/Unit.cpp	(working copy)
@@ -190,6 +190,9 @@
     m_charmInfo = NULL;
     m_moveRun = false;
 
+	m_AutoRepeatFirstCast = true;
+	
+
     // remove aurastates allowing special moves
     for(int i=0; i < MAX_REACTIVE; ++i)
         m_reactiveTimer[i] = 0;
@@ -2883,94 +2886,132 @@
 
 void Unit::_UpdateAutoRepeatSpell( uint32 time )
 {
-    if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() == SPELL_STATE_FINISHED)
-    {
-        //Auto Shot & Shoot
-        if( m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->AttributesEx2 == 0x000020 && GetTypeId() == TYPEID_PLAYER )
-        {
-            // Auto Shot don't require ranged weapon cooldown at first cast, wand shoot does, so the 'FINISHED' state
-            if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351)
-            {
-                // Shoot
-                resetAttackTimer( RANGED_ATTACK );
-            }
-            else
-            {
-                // Auto Shoot
-                if (m_AutoRepeatFirstCast)
-                {
-                    // first cast only with recovery time (not less)
-                    if (getAttackTimer( RANGED_ATTACK ) < m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->RecoveryTime)
-                        setAttackTimer( RANGED_ATTACK, m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->RecoveryTime);
-                    m_AutoRepeatFirstCast = false;
-                }
-                else
-                {
-                    // second or further casts
-                    resetAttackTimer( RANGED_ATTACK );
-                }
-            }
-        }
-        else
-        {
-            setAttackTimer( RANGED_ATTACK, m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->RecoveryTime);
-        }
+	if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() == SPELL_STATE_IDLE )
+    {	
+		//handle first cast
+		if (m_AutoRepeatFirstCast)
+		{	
+			m_AutoRepeatFirstCast = false;
+			m_AutoRepeatAimTime = true;
+		}
+		
+		// check movement in player case and cancel wand shooting immeadeatly
+        if(GetTypeId() == TYPEID_PLAYER && ((Player*)this)->isMoving() 
+			&& m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351)
+		{
+			m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SendCastResult(SPELL_FAILED_INTERRUPTED);
+			InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+			return;
+		}
 
-        m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->setState(SPELL_STATE_IDLE);
-    }
-    else if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() == SPELL_STATE_IDLE && isAttackReady(RANGED_ATTACK) )
-    {
-        // check if we can cast
-        if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CanCast(true) == 0)
-        {
-            // check movement in player case
-            if(GetTypeId() == TYPEID_PLAYER && ((Player*)this)->isMoving())
-            {
-                // cancel wand shooting
-                if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351)
-                    InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
-                // ELSE delay auto-repeat ranged weapon until player movement stop
-            }
-            else
-                // recheck range and req. items (ammo and gun, etc)
-            if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CheckRange(true) == 0 && m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CheckItems() == 0 )
-            {
-                // check, if we are casting melee spell (it blocks autorepeat)
-                if ( ! (m_currentSpells[CURRENT_MELEE_SPELL] &&
-                    (m_currentSpells[CURRENT_MELEE_SPELL]->getState() != SPELL_STATE_FINISHED) &&
-                    (m_currentSpells[CURRENT_MELEE_SPELL]->getState() != SPELL_STATE_DELAYED)) )
-                {
-                    // check, if we are casting something else, if no then run autorepeat spell
-                    if (!IsNonMeleeSpellCasted(false, false, true))
-                    {
-                        m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->setState(SPELL_STATE_PREPARING);
-                        m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->ReSetTimer();
-                    }
-                }
-            }
-            else
-            {
-                InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
-            }
-        }
-        else
-        {
-            InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
-        }
-    }
-    else if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() == SPELL_STATE_PREPARING)
-    {
-        // check, if some other incomplete spell exists (including melee) or ranged attack is not ready
-        if ( m_currentSpells[CURRENT_MELEE_SPELL] ||
-            m_currentSpells[CURRENT_GENERIC_SPELL] ||
-            m_currentSpells[CURRENT_CHANNELED_SPELL] ||
-            !isAttackReady(RANGED_ATTACK) )
-        {
-            // some other spell is here or ranged attack is not ready, break us to idle state
-            m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->finish(false);
-            m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->setState(SPELL_STATE_IDLE);
-        }
-    }
+
+		uint8 result = m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CanCast(true);
+        // CanCast check
+		if (result && result != SPELL_FAILED_NOT_READY)
+		{			            	
+			// cancel wand shooting and send result
+			if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351)
+			{	
+				m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SendCastResult(result);
+				InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+				return;
+			}
+			
+			//don't cancel autoshot and send result...
+			else 
+			{	
+				//..if "cooldown" is over; except "moving"
+				if (isAttackReady(RANGED_ATTACK) && result!= SPELL_FAILED_MOVING)
+					m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SendCastResult(result);
+				
+				//..if the "aimtime" is interrupted by moving send "interrupted" (off-like)
+				if (getAttackTimer(RANGED_ATTACK)<=500 && result == SPELL_FAILED_MOVING && !m_AutoRepeatAimTime)
+				{
+					m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SendCastResult(SPELL_FAILED_INTERRUPTED);
+					//interrupt "prepare" animation
+					m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SendInterrupted(0);
+				}
+
+			}
+				//now we need again time to aim at the target....
+				m_AutoRepeatAimTime = true;
+				
+		}
+		
+		// check if we are castig melee spell, or anything else	(mainly channeled spells)	
+		else if (	(m_currentSpells[CURRENT_MELEE_SPELL] &&
+						(m_currentSpells[CURRENT_MELEE_SPELL]->getState() != SPELL_STATE_FINISHED) &&
+						(m_currentSpells[CURRENT_MELEE_SPELL]->getState() != SPELL_STATE_DELAYED)
+					)	||	
+					IsNonMeleeSpellCasted(false, false, true)
+				)
+		{
+			//...of course we need again time to aim ....
+			m_AutoRepeatAimTime = true;
+			
+		}
+		else
+		{ 
+			//aimtime handling
+			if(m_AutoRepeatAimTime == true && getAttackTimer(RANGED_ATTACK) < 500) 
+			{	
+				setAttackTimer(RANGED_ATTACK, 500);
+				m_AutoRepeatAimTime = false;
+				
+				//now we need the "prepare"-animation
+				m_AutoRepeatSpellStart =true;
+				
+			}
+			
+			// handle graphics, 0.5sec of preparing (off-like) 
+			 if( getAttackTimer(RANGED_ATTACK) <= 500 && m_AutoRepeatSpellStart)
+			{
+				m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SendSpellStart();
+				m_AutoRepeatSpellStart = false;
+			}
+			
+			
+			//now we got it, ...nearly :-) 
+			else if(isAttackReady(RANGED_ATTACK) && result != SPELL_FAILED_NOT_READY ) 
+			{
+				// recheck CanCast() to avoid interruption problems in Spell::prepare
+				if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CanCast(true) == 0 )
+				{
+					// start the spell
+					m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->prepare(&(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets));
+					resetAttackTimer( RANGED_ATTACK ); 
+				}
+				else
+				{	//send interrupted message, because of interruption within the 0.5sec aimtime; btw this cancels wand shooting
+					m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SendCastResult(SPELL_FAILED_INTERRUPTED);
+					
+					//interrupt "prepare" animation
+					m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SendInterrupted(0);
+					
+					m_AutoRepeatAimTime = true;
+				}
+			 }
+		}
+		
+		
+	}
+	
+	else if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() == SPELL_STATE_DELAYED)
+	{   
+		//store information
+		uint32 spellId = m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id;
+        SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
+        
+		//create and intialize new spell with old target
+		Spell *p_spell = new Spell(this, spellInfo, false, 0);
+		
+		p_spell->m_targets = m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets;
+		p_spell->setState(SPELL_STATE_IDLE);
+		SetCurrentCastedSpell( p_spell);
+		
+		//we need a new "prepare" animation...
+		m_AutoRepeatSpellStart = true;
+	}	
 }
 
 void Unit::SetCurrentCastedSpell( Spell * pSpell )
@@ -3007,6 +3048,10 @@
                 // break autorepeat if not Auto Shot
                 if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351)
                     InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
+				//new aimtime for "Autoshot"
+				else
+					m_AutoRepeatAimTime = true;
+
             }
         } break;
 
@@ -3031,11 +3076,7 @@
                 InterruptSpell(CURRENT_GENERIC_SPELL);
                 InterruptSpell(CURRENT_CHANNELED_SPELL);
             }
-            else
-            {
-                // special action: set first cast flag for Auto Shoot
-                m_AutoRepeatFirstCast = true;
-            }
+            
         } break;
 
         default:
@@ -3052,7 +3093,7 @@
     m_currentSpells[CSpellType] = pSpell;
 }
 
-void Unit::InterruptSpell(uint32 spellType)
+void Unit::InterruptSpell(uint32 spellType, bool sendAutoRepeatCancel)
 {
     assert(spellType < CURRENT_MAX_SPELL);
 
@@ -3060,8 +3101,9 @@
     {
         // send autorepeat cancel message for autorepeat spells
         if (spellType == CURRENT_AUTOREPEAT_SPELL)
-        {
-            if(GetTypeId()==TYPEID_PLAYER)
+        {	
+			m_AutoRepeatFirstCast = true;
+            if(GetTypeId()==TYPEID_PLAYER && sendAutoRepeatCancel)
                 ((Player*)this)->SendAutoRepeatCancel();
         }
 
@@ -3110,7 +3152,9 @@
     // autorepeat spells are interrupted if they are not finished or delayed
     if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL])
     {
-        // send disable autorepeat packet in any case
+        m_AutoRepeatFirstCast = true;
+		
+		// send disable autorepeat packet in any case
         if(GetTypeId()==TYPEID_PLAYER)
             ((Player*)this)->SendAutoRepeatCancel();
 
Index: src/game/Unit.h
===================================================================
--- src/game/Unit.h	(revision 4935)
+++ src/game/Unit.h	(working copy)
@@ -1005,7 +1005,7 @@
 
         void SetCurrentCastedSpell(Spell * pSpell);
         virtual void ProhibitSpellScholl(SpellSchools /*idSchool*/, uint32 /*unTimeMs*/ ) { }
-        void InterruptSpell(uint32 spellType);
+        void InterruptSpell(uint32 spellType, bool sendAutoRepeatCancel = true);
 
         // set withDelayed to true to account delayed spells as casted
         // delayed+channeled spells are always accounted as casted
@@ -1032,6 +1032,7 @@
         float m_modResilience;
         float m_threatModifier[MAX_SPELL_SCHOOL];
         float m_modAttackSpeedPct[3];
+		bool m_AutoRepeatFirstCast;
 
         // Event handler
         EventProcessor m_Events;
@@ -1176,10 +1177,14 @@
         explicit Unit ( WorldObject *instantiator );
 
         void _UpdateSpells(uint32 time);
-
+		
         void _UpdateAutoRepeatSpell(uint32 time);
-        bool m_AutoRepeatFirstCast;
 
+		//used in _UpdateAutoRepeatSpells
+		bool m_AutoRepeatAimTime;
+		bool m_AutoRepeatSpellStart;
+        
+
         uint32 m_attackTimer[3];
 
         float m_createStats[5];