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];