Index: src/game/DuelHandler.cpp =================================================================== --- src/game/DuelHandler.cpp (revision 5734) +++ src/game/DuelHandler.cpp (working copy) @@ -73,8 +73,14 @@ if(GetPlayer()->duel->startTime != 0) { GetPlayer()->CombatStop(); - if( GetPlayer()->duel->opponent ) + //force autoshot and melee cancel + GetPlayer()->SendAttackSwingCancelAttack(); + + if( GetPlayer()->duel->opponent ) + { GetPlayer()->duel->opponent->CombatStop(); + GetPlayer()->duel->opponent->SendAttackSwingCancelAttack(); + } GetPlayer()->CastSpell(GetPlayer(), 7267, true); // beg GetPlayer()->DuelComplete(DUEL_WON); return; Index: src/game/Spell.cpp =================================================================== --- src/game/Spell.cpp (revision 5734) +++ src/game/Spell.cpp (working copy) @@ -1746,6 +1746,14 @@ { m_targets = *targets; + //only intialize 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(); @@ -1800,17 +1808,13 @@ 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) + if(m_IsTriggeredSpell || m_autoRepeat ) cast(true); else { m_caster->SetCurrentCastedSpell( this ); m_selfContainer = &(m_caster->m_currentSpells[GetCurrentContainer()]); - SendSpellStart(); + SendSpellStart(); } } @@ -1828,6 +1832,9 @@ SendInterrupted(0); SendCastResult(SPELL_FAILED_INTERRUPTED); } break; + + case SPELL_STATE_IDLE: //Auto-Repeat spells + SendInterrupted(0); break; case SPELL_STATE_CASTING: { @@ -2139,9 +2146,9 @@ catrec = m_spellInfo->CategoryRecoveryTime; } - // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK) + // shoot spells (except autorepeat) use equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK) // prevent 0 cooldowns set by another way - if (rec <= 0 && catrec <= 0 && (cat == 76 || cat == 351)) + if (rec <= 0 && catrec <= 0 && (cat == 76)) rec = _player->GetAttackTime(RANGED_ATTACK); // Now we have cooldown data (if found any), time to apply mods @@ -2204,23 +2211,10 @@ // update pointers based at it's GUIDs UpdatePointers(); - if(m_targets.getUnitTargetGUID()) + if(m_targets.getUnitTargetGUID() && !m_targets.getUnitTarget()) { - if(!m_targets.getUnitTarget() || !m_targets.getUnitTarget()->isAlive()) - { - if(m_autoRepeat) - { - m_autoRepeat = false; - m_spellState = SPELL_STATE_FINISHED; - return; - } - - if(!m_targets.getUnitTarget()) - { - cancel(); - return; - } - } + cancel(); + return; } // check if the player caster has moved before the spell finished @@ -2231,8 +2225,8 @@ // always cancel for channeled spells if( m_spellState == SPELL_STATE_CASTING ) cancel(); - // don't cancel for melee, autorepeat and instant spells - else if(!m_meleeSpell && !m_autoRepeat && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)) + // don't cancel for melee and instant spells + else if(!m_meleeSpell && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) ) cancel(); } @@ -2289,8 +2283,8 @@ // channeled spell processed independently for quest targeting // cast at creature (or GO) quest objectives update at succesful cast channel finished - // ignore autorepeat/melee casts for speed (not exist quest for spells (hm... ) - if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsAutoRepeat() && !IsMeleeSpell() ) + // ignore melee casts for speed (not exist quest for spells (hm... ) + if( m_caster->GetTypeId() == TYPEID_PLAYER && !IsMeleeSpell() ) { for(std::list::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit) { Index: src/game/SpellEffects.cpp =================================================================== --- src/game/SpellEffects.cpp (revision 5734) +++ src/game/SpellEffects.cpp (working copy) @@ -1209,12 +1209,17 @@ } case 37506: // Scatter Shot { - if (m_caster->GetTypeId()!=TYPEID_PLAYER) - return; - - // break Auto Shot - m_caster->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); - return; + // break Auto Shot and "Attack" + m_caster->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); + m_caster->AttackStop(); + if (m_caster->GetTypeId()==TYPEID_PLAYER) + ((Player*)m_caster)->SendAttackSwingCancelAttack(); //force autoshot cancel + + //stop pet + if (m_caster->GetPet() && m_caster->GetPet()->getVictim() == unitTarget ) + m_caster->GetPet()->AttackStop(); + + return; } } break; Index: src/game/SpellHandler.cpp =================================================================== --- src/game/SpellHandler.cpp (revision 5734) +++ src/game/SpellHandler.cpp (working copy) @@ -722,6 +722,8 @@ Spell *spell = new Spell(_player, spellInfo, false); spell->m_cast_count = cast_count; //set count of casts + if (spell->IsAutoRepeat()) + _player->m_AutoRepeatFirstCast = true; //set autorepeat first cast spell->prepare(&targets); } @@ -811,7 +813,6 @@ void WorldSession::HandleCancelAutoRepeatSpellOpcode( WorldPacket& /*recvPacket*/) { - // may be better send SMSG_CANCEL_AUTO_REPEAT? // cancel and prepare for deleting _player->InterruptSpell(CURRENT_AUTOREPEAT_SPELL); } Index: src/game/Unit.cpp =================================================================== --- src/game/Unit.cpp (revision 5734) +++ src/game/Unit.cpp (working copy) @@ -869,10 +869,13 @@ assert(he->duel); he->SetHealth(1); - CombatStop(); // for case killed by pet + CombatStop(); // for case killed by pet + if (GetTypeId()==TYPEID_PLAYER) + ((Player*)this)->SendAttackSwingCancelAttack(); // force autoshot cancel if(he->duel->opponent!=this) he->duel->opponent->CombatStop(); he->CombatStop(); + he->SendAttackSwingCancelAttack(); he->CastSpell(he, 7267, true); // beg he->DuelComplete(DUEL_WON); @@ -3403,96 +3406,98 @@ void Unit::_UpdateAutoRepeatSpell() { - 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); - } + //check target + m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->UpdatePointers(); + if ( !(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets.getUnitTargetGUID() && + m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets.getUnitTarget()) ) + { + InterruptSpell(CURRENT_AUTOREPEAT_SPELL); + if(GetTypeId()==TYPEID_PLAYER) + ((Player*)this)->SendAttackSwingCancelAttack(); //force autoshot cancel + 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)) - { - // wee need clear target list before recasting - m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CleanupTargetList(); - 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); - } - } + if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() == SPELL_STATE_IDLE ) + { + //on First cast we need a prepare animation + if(m_AutoRepeatFirstCast) m_AutoRepeatSendSpellStart =true; + + uint8 result = m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CanCast(true); + // CanCast check + if (result) + { + // 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; + } + + //else...don't cancel "autoshot" and send result, if "cooldown" is over; except "moving" + if (isAttackReady(RANGED_ATTACK) && result!= SPELL_FAILED_MOVING) + m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SendCastResult(result); + + //if interruption is during the prepare animation we additionally need to stop animation + if (getAttackTimer(RANGED_ATTACK)<=500 && !m_AutoRepeatFirstCast) + { + m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SendInterrupted(0); + } + //now we need again our 0.5 sec.... + m_AutoRepeatFirstCast = true; + + } + + //...we CanCast, now check if we are castig melee spell, or anything else + 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_AutoRepeatFirstCast = true; + } + + else //ok, nothing casting currently + { + //handling the 0.5s + if( m_AutoRepeatFirstCast == true && getAttackTimer(RANGED_ATTACK) < 500 ) + { + setAttackTimer(RANGED_ATTACK, 500); + } + + m_AutoRepeatFirstCast = false; + + // handle graphics, 0.5sec of preparing (blizz-like) + if( getAttackTimer(RANGED_ATTACK) <= 500 && m_AutoRepeatSendSpellStart ) + { + m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->SendSpellStart(); + m_AutoRepeatSendSpellStart = false; + } + // start the spell if ready + if( isAttackReady(RANGED_ATTACK) ) + { + m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->prepare(&(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets)); + resetAttackTimer( RANGED_ATTACK ); + } + } + } + + else if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() == SPELL_STATE_DELAYED) + { + //store spell information + SpellEntry const *spellInfo = sSpellStore.LookupEntry(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id); + + m_AutoRepeatFirstCast = true; + m_AutoRepeatSendSpellStart = true; + + //create and intialize new spell with old target + Spell *p_spell = new Spell(this, spellInfo, false, 0); + p_spell->prepare(&(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_targets)); + } } void Unit::SetCurrentCastedSpell( Spell * pSpell ) @@ -3521,6 +3526,9 @@ // break autorepeat if not Auto Shot if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Category == 351) InterruptSpell(CURRENT_AUTOREPEAT_SPELL); + + //new casttime for autoshot in any case + m_AutoRepeatFirstCast = true; } } break; @@ -3544,12 +3552,7 @@ // generic autorepeats break generic non-delayed and channeled non-delayed spells InterruptSpell(CURRENT_GENERIC_SPELL,false); InterruptSpell(CURRENT_CHANNELED_SPELL,false); - } - else - { - // special action: set first cast flag for Auto Shoot - m_AutoRepeatFirstCast = true; - } + } } break; default: @@ -3572,12 +3575,8 @@ if(m_currentSpells[spellType] && (withDelayed || m_currentSpells[spellType]->getState() != SPELL_STATE_DELAYED) ) { - // send autorepeat cancel message for autorepeat spells - if (spellType == CURRENT_AUTOREPEAT_SPELL) - { - if(GetTypeId()==TYPEID_PLAYER) - ((Player*)this)->SendAutoRepeatCancel(); - } + // set first cast for autorepeat spells + if (spellType == CURRENT_AUTOREPEAT_SPELL) m_AutoRepeatFirstCast = true; if (m_currentSpells[spellType]->getState() != SPELL_STATE_FINISHED) m_currentSpells[spellType]->cancel(); @@ -3624,9 +3623,7 @@ // autorepeat spells are interrupted if they are not finished or delayed if (m_currentSpells[CURRENT_AUTOREPEAT_SPELL] && (!spell_id || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->m_spellInfo->Id==spell_id)) { - // send disable autorepeat packet in any case - if(GetTypeId()==TYPEID_PLAYER) - ((Player*)this)->SendAutoRepeatCancel(); + m_AutoRepeatFirstCast = true; if ( (m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() != SPELL_STATE_FINISHED) && (withDelayed || m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->getState() != SPELL_STATE_DELAYED) ) Index: src/game/Unit.h =================================================================== --- src/game/Unit.h (revision 5734) +++ src/game/Unit.h (working copy) @@ -947,6 +947,7 @@ float m_threatModifier[MAX_SPELL_SCHOOL]; float m_modAttackSpeedPct[3]; + bool m_AutoRepeatFirstCast; // Event handler EventProcessor m_Events; @@ -1116,7 +1117,7 @@ void _UpdateSpells(uint32 time); void _UpdateAutoRepeatSpell(); - bool m_AutoRepeatFirstCast; + bool m_AutoRepeatSendSpellStart; uint32 m_attackTimer[MAX_ATTACK];