rendered paste bodyIndex: 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];