Sub Pull
/doevents
/if ((${DMZ} && ${Me.InInstance}==FALSE) || !${Select[${Role},puller,pullertank,hunter,hunterpettank,pullerpettank]} || !${Pulling} || ${DPSPaused}) /return
/if (${DebugPull}) /echo DEBUGPULL Pull Enter
/declare PullTimer timer local 50
/declare PullAttempts int local 0
/declare StuckCount int local 0
/declare PullDist float local ${PullRange}
/declare tempAmmo string local ${InvSlot[ammo].Item.Name}
/declare rangedSwitch int local 0
/declare ammoSwitch int local 0
/declare WasTwistingPull bool local ${Twist}
/varset Pulled 0
/varset PullTooFar 0
/varset CantHit 0
/varset ToClose 0
/declare AutoFireOff int local ${AutoFireOn}
/declare X1 int local
/declare Y1 int local
/declare BeginMobID string local
/if (${DebugPull}) /echo DEBUGPULL Pull: PullWith:${PullWith} PullDist: ${PullDist}
| Set autofire setting off during pulls if not using ranged item to pull
/if (${AutoFireOff}) /varset AutoFireOn 0
/if (${IAmABard} && !${PullTwistOn} && ${WasTwistingPull}) {
/squelch /twist off
}
/if (!${Me.Mount.ID} && ${Me.Sitting}) /stand
/if (${Select[${Role},pullerpettank]} && ${PullRoleToggle} && ${Group.Puller.ID}!=${Me.ID}) {
/if (${Math.Distance[${Spawn[${MyTargetID}].Y},${Spawn[${MyTargetID}].X}:${CampYLoc},${CampXLoc}]}>${CampRadius}) /call TogglePullMode TurnOn
}
/varset BeginMobID ${Me.XTarget[${XTSlot}].ID}
:PullAgain
/doevents BackOff
/if (${DPSPaused}) /return
| vars used to determine if we are stuck
/varset X1 ${Int[${Me.X}]}
/varset Y1 ${Int[${Me.Y}]}
/if ((${AggroTargetID} && !${ChainPull}) || (${ChainPull} && ${Me.XTarget[${XTSlot2}].ID} || (${Me.XTarget[${XTSlot}].ID} && ${Me.XTarget[${XTSlot}].ID}!=${BeginMobID})) || || ${Target.Type.Equal[corpse]}) {
/if (${DebugPull}) /echo Pulling 1.1
/varset Pulled 1
/varset MyTargetID ${AggroTargetID}
/varset MyTargetName ${Spawn[${AggroTargetID}].CleanName}
/if (${Navigation.Active}) /nav stop
/if (${MoveTo.Moving}) /moveto off
/if (${DebugPull}) /echo DEBUGPULL Pull Aggro detected
/goto :DonePulling
}
| Exit pull and reset if timed out or wandered too far from camp
/if (${DebugPull}) /echo Pulling 1.2 PullTimer: ${PullTimer} Distance: ${Math.Distance[${CampYLoc},${CampXLoc}]} MaxRadius: ${Math.Calc[${MaxRadius}*.90]} PullAttempts: ${PullAttempts} MyTargetID: ${Spawn[${MyTargetID}].ID}
/if (${PullTimer}==0 || ${Math.Distance[${CampYLoc},${CampXLoc}]} >= ${Math.Calc[${MaxRadius}*.90]} || ${PullAttempts}>=7 && !${Spawn[${MyTargetID}].LineOfSight} && !${Select[${Role},hunter,hunterpettank]} || !${Spawn[${MyTargetID}].ID}) {
/squelch /alert add 1 id ${MyTargetID}
/if (${DebugPull}) /echo DEBUGPULL Pull: /echo Adding ${Spawn[${MyTargetID}].CleanName} ID: ${MyTargetID} to temp ignore list
/call PullReset
/if (${DebugPull}) /echo DEBUGPULL Pull: Done Pulling-Timer expired, Mob unreachable or exceeded max pull radius.
/goto :DonePulling
}
/if (((${AggroTargetID} && !${ChainPull}) || (${Me.XTarget[${XTSlot2}].ID} && ${ChainPull})) && ${Math.Distance[${CampYLoc},${CampXLoc}]} < ${CampRadius}) {
/echo Looks like mobs in camp aborting pull.
/call PullReset
/if (!${ChainPull}) /call CheckForCombat 1 Pull
/if (${DebugPull}) /echo DEBUGPULL Pull Mobs in camp detected
/return
}
/if (${PullWith.Equal[Ranged]}) {
/if (${DebugPull}) /echo DEBUGPULL Pull: Ranged
| Cursor check sometimes summoned items get stuck on cursor.
/if (${Cursor.ID}) /autoinventory
/if (${OrigRanged.NotEqual[${PullItem}]} && ${OrigRanged.NotEqual[null]}) {
/call CheckCasting 50
/exchange "${PullItem}" ranged
/varset rangedSwitch 1
/delay 5
}
/if (${tempAmmo.NotEqual[null]} && ${tempAmmo.NotEqual[${PullAmmo}]}) {
/call CheckCasting 50
/exchange "${PullAmmo}" ammo
/delay 5
/if (${tempAmmo.NotEqual[${PullAmmo}]}) /varset ammoSwitch 1
}
}
| - Filter to prevent pulling until AA/Disc/Spell is ready.
/if (${PullTimer} && (!${AggroTargetID} && !${ChainPull}) && ((!${Select[${PullWith},Melee,Pet,Ranged]} && !${Select[TRUE,${Me.SpellReady[${PullWith}]},${Me.AltAbilityReady[${PullWith}]},${Me.CombatAbilityReady[${PullWith}]}]}) || (${PullWith.Equal[Ranged]} && !${Me.RangedReady}))) /goto :PullAgain
/if (${DebugPull}) /echo DEBUGPULL Pull Starting
/varset PullAttempts 0
| Set group role puller to adjust for merc running up while pulling if soloing
/if (${Group}==1 && !${Group.Puller.Name.Equal[${Me}]} && ${Select[${Role},puller]} && ${Spawn[=${MainAssist}].Type.Equal[Mercenary]}) /call AssignGroupRole set "${Me.CleanName}" 3
/if (${Spawn[${MyTargetID}].Distance}>${PullDist} && ${Math.Distance[${Spawn[${MyTargetID}].Y},${Spawn[${MyTargetID}].X}:${CampYLoc},${CampXLoc}]}<${MaxRadius}) {
:WeThereYet
/if (${Spawn[${MyTargetID}].LineOfSight} || (!${Navigation.MeshLoaded} && ${Select[${Role},hunter,hunterpettank]})) {
/moveto id ${MyTargetID} mdist ${PullDist}
/delay 5
/if (${DebugPull}) /echo DEBUGPULL Pull LOS ${Spawn[${MyTargetID}].LineOfSight}
} else /if (${Navigation.MeshLoaded}) {
/if (!${Spawn[${MyTargetID}].LineOfSight}) {
/varset PullDist 50
/if (${DebugPull}) /echo DEBUGPULL Pull Nav/LOS ${Spawn[${MyTargetID}].LineOfSight}
} else {
/varset PullDist ${PullRange}
}
/nav ${Spawn[${MyTargetID}].X} ${Spawn[${MyTargetID}].Y} ${Spawn[${MyTargetID}].Z}
:DistanceCheck
/delay 5
/if (${PullTimer}==0) /goto :NavPullRelease
| Check for Unexpected Aggro
/if ((${AggroTargetID} && !${ChainPull}) || ((${Me.XTarget[${XTSlot2}].ID} || (${Me.XTarget[${XTSlot}].ID} && ${Me.XTarget[${XTSlot}].ID}!=${MyTargetID} && ${Me.XTarget[${XTSlot}].ID}!=${BeginMobID})) && ${ChainPull})) /goto :PullAgain
/if (${Spawn[${MyTargetID}].Distance}>${PullDist} && !${Spawn[${MyTargetID}].LineOfSight} && ((!${AggroTargetID} && !${ChainPull}) || (!${Me.XTarget[${XTSlot2}].ID} && ${Me.XTarget[${XTSlot}].ID}!=${MyTargetID} && ${ChainPull}))) /goto :DistanceCheck
:NavPullRelease
/if (${Navigation.Active} && ${Spawn[${MyTargetID}].LineOfSight}) /nav stop
}
}
/varset CantSee 0
/doevents
/if (${DPSPaused}) /return
| Extending PullTimer if moving closer or target moving && !${Select[${Role},hunter]})
/if ((${Navigation.Active} || ${MoveTo.Moving} || ${Spawn[${MyTargetID}].Speed}>25) && ${Spawn[${MyTargetID}].Distance}>${PullRange}) /varcalc PullTimer ${PullTimer}+50
| Pull counter used to in conjunction with 1s delay for timing
/varcalc PullAttempts ${PullAttempts}+1
| Try and pull again after 3 seconds or 2 seconds if target is moving
/if (${PullAttempts}>=7 || ${PullAttempts}>=3 && ${Spawn[${MyTargetID}].Speed}>25 ) {
| Make range smaller to creep closer to mob if los or moving
/varcalc PullDist ${PullDist}*.6
/delay 5
/if (${DebugPull}) /echo DEBUGPULL Pull PullDist: ${PullDist}*.6 - PullLoops: ${PullAttempts}
/goto :PullAgain
}
| 1s timer used in conjunction with PullAttempts to control pulling
/delay 10
| - Check to see if we are stuck
/if ((${Int[${Me.X}]}==${X1}) && (${Int[${Me.Y}]}==${Y1})) {
/varcalc StuckCount (${StuckCount})+1
/if (${StuckCount}>=3) {
/call Stuck
}
/if (${StuckCount}>=7 && ((!${AggroTargetID} && !${ChainPull}) || (!${Me.XTarget[${XTSlot2}].ID} && ${Me.XTarget[${XTSlot}].ID}!=${MyTargetID} && ${ChainPull}))) {
/echo I am stuck aborting pull
/squelch /alert add 1 id ${MyTargetID}
/if (${MoveTo.Moving}) /moveto off
/if (${Navigation.Active}) /nav stop
/call PullReset
/goto :DonePulling
}
}
/if (${DebugPull}) /echo DEBUGPULL Pull Loop Count: ${PullAttempts}
| Distance loop check until mob in range to pull
/if (${Spawn[${MyTargetID}].Distance}>${PullRange} && ((!${AggroTargetID} && !${ChainPull}) || (!${Me.XTarget[${XTSlot2}].ID} && ${Me.XTarget[${XTSlot}].ID}!=${MyTargetID} && ${ChainPull}))) /goto :WeThereYet
| If mob moves out of line of sight during pull try moveto mob again
/if (!${Spawn[${MyTargetID}].LineOfSight} && !${Select[${Role},hunter,hunterpettank]} && ${PullTimer}) {
/varcalc PullDist ${PullDist}*.8
/if (${DebugPull}) /echo DEBUGPULL Pull Decrease pull distance to ${PullDist}
/goto :PullAgain
}
/if ((${AggroTargetID} && !${ChainPull}) || ((${Me.XTarget[${XTSlot2}].ID} || (${Me.XTarget[${XTSlot}].ID} && ${Me.XTarget[${XTSlot}].ID}!=${MyTargetID} && ${Me.XTarget[${XTSlot}].ID}!=${BeginMobID})) && ${ChainPull})) /goto :PullAgain
/if (${Spawn[${MyTargetID}].ID} && ${Spawn[${MyTargetID}].Distance}<${PullRange} && ((!${AggroTargetID} && !${ChainPull}) || (!${Me.XTarget[${XTSlot2}].ID} && ${Me.XTarget[${XTSlot}].ID}!=${MyTargetID} && ${ChainPull}))) {
/if (${MoveTo.Moving}) /moveto off
/if (${Navigation.Active}) /nav stop
| Target mob before Aggroing
/target id ${MyTargetID}
/delay 20 ${Target.ID}==${MyTargetID}
| Validate target one more time before pulling
/call ValidateTarget
/if (${ValidTarget}==0) {
/squelch /alert add 1 id ${Target.ID}
/squelch /target clear
/echo Aborting Pull! Target invalid now! Reason:${Macro.Return}
/call PullReset
/goto :DonePulling
}
| Handle pulling with Melee setting
/if (${PullWith.Equal[Melee]} || ${PullWithAlt.Equal[Melee]} && ${ToClose}) {
/if (${DebugPull}) /echo DEBUGPULL PULL: Melee
/if (!${Select[${Role},hunter,hunterpettank]}) {
| Turn off mq2melee function so puller and pullertank doesn't attack mob on pull
/if (${Select[${Role},puller,pullertank]}) /squelch /melee melee=0
:AttackAgain
/if (${Target.ID}) /face nolook
/look 0
/attack on
/delay 5
/if (((!${AggroTargetID} && !${ChainPull}) || (!${Me.XTarget[${XTSlot2}].ID} && ${Me.XTarget[${XTSlot}].ID}!=${Target.ID} && ${ChainPull})) && ${Target.PctHPs}==100) /goto :AttackAgain
| Turn off combat so puller returns to camp
/if (${Select[${Role},puller,pullertank,pullerpettank,hunterpettank]}) {
:StopCombat
/attack off
/if (${Stick.Active}) /stick off
/squelch /target clear
| Cycle until combat off and mob is aggro'd
/if (${Me.Combat} && ((${AggroTargetID} && !${ChainPull}) || ${Target.PctHPs}<100)) /goto :StopCombat
| Turn on mq2melee function back on after pull
/if (${Select[${Role},puller,pullertank]}) /squelch /melee melee=1
}
}
/varset Pulled 1
/varset ToClose 0
| Pull with ranged
} else /if (${PullWith.Equal[Ranged]} && !${ToClose}) {
/if (${DebugPull}) /echo DEBUGPULL PULL: Ranged
:RangedAgain
/doevents
/if (${ToClose}) {
/varset PullDist 15
/goto :PullAgain
}
/if (${DPSPaused}) /return
/if ((${AggroTargetID} && !${ChainPull}) || ((${Me.XTarget[${XTSlot2}].ID} || ${Me.XTarget[${XTSlot}].ID}==${Target.ID}) && ${ChainPull})) {
/varset Pulled 1
/goto :DonePulling
}
/if (${CantHit}) {
/varset CantHit 0
/varcalc PullDist ${PullDist}*.8
/goto :PullAgain
}
/if (${CantSee}) {
/if (${Target.ID}) /squelch /face nolook
/delay 1s
/varset CantSee 0
/if (${DebugPull}) /echo Could Not see Target. Trying Again.
}
| Mod for puller to turn back to camp after /range this saves on the puller turning AFTER mob is aggroed and turns facing camp while waiting for mob to aggro.
/if (${Me.Combat}) {
/Attack off
/delay 20 !${Me.Combat}
}
/if (${Stick.Active}) /stick off
/if (${Target.ID}) /squelch /face nolook
/look 0
/delay 20 ${Me.Heading.ShortName.Equal[${Target.HeadingTo}]}
/if (${Target.ID}==${MyTargetID}) /range
/if (${Math.Distance[${CampYLoc},${CampXLoc}]} > ${CampRadius}) {
/delay 10
/if (${Target.ID}) /squelch /face nolook loc ${CampYLoc},${CampXLoc}
}
/if (!${ChainPull}) {
/delay ${Math.Calc[1+${Target.Distance}/50].Int}s ${AggroTargetID} || ${Target.Type.Equal[corpse]}
} else {
/delay ${Math.Calc[1+${Target.Distance}/50].Int}s ${Me.XTarget[${XTSlot2}].ID} || ${Me.XTarget[${XTSlot}].ID}==${Target.ID} || ${Target.Type.Equal[corpse]}
}
/if (${DebugPull}) /echo DEBUGPULL Pull: ${PullTimer} && (!${AggroTargetID} || ${Target.PctHPs}==100) ${Me.XTarget[${XTSlot2}].ID} || ${Me.XTarget[${XTSlot}].ID}==${Target.ID})
/if (${PullTimer} && ${Target.Type.NotEqual[corpse]} && ((!${AggroTargetID} && !${ChainPull}) || (!${Me.XTarget[${XTSlot2}].ID} && ${Me.XTarget[${XTSlot}].ID}!=${Target.ID} && ${ChainPull})) || !${PullTooFar}) /goto :RangedAgain
| PetPull section - Written by TreeHuginDruid for RedGuides
} else /if (${PullWith.Equal[Pet]}) {
/if (${DebugPull}) /echo DEBUGPULL Pull: Pet
/if (${Target.ID}) /face nolook
|- Ensure we are in pull range and pet is following!
/if (${Me.Pet.Stance.NotEqual[FOLLOW]}) /pet follow
| - Send in pet if I don't have a mob in extended target)
:SendInPet
/if ((!${AggroTargetID} && !${ChainPull}) || (!${Me.XTarget[${XTSlot2}].ID} && ${Me.XTarget[${XTSlot}].ID}!=${Target.ID} && ${ChainPull})) {
/pet attack
/delay 5
/doevents
/if (${Macro.Return.Equal[Ignore]}) /return
/delay 10 (${AggroTargetID} && !${ChainPull}) || ${Me.XTarget[${XTSlot2}].ID} || ${Me.XTarget[${XTSlot}].ID}==${Target.ID} || ${Target.Type.Equal[corpse]}
| Fix for HunterPetTank
/if (${Spawn[${MyTargetID}].ID} && ${Target.Type.NotEqual[corpse]} && ((!${AggroTargetID} && !${ChainPull}) || (!${Me.XTarget[${XTSlot2}].ID} && ${Me.XTarget[${XTSlot}].ID}!=${Me.Pet.ID} && ${ChainPull}))) /goto :SendInPet
}
| - If I have a extended target, flag as pulled.
/if ((${AggroTargetID} && !${ChainPull}) || ${Me.XTarget[${XTSlot2}].ID} || ${Me.XTarget[${XTSlot}].ID}==${Target.ID} || ${Target.Type.Equal[corpse]}) {
/varset Pulled 1
/if (${PetHold.Length}) /pet ${PetHold} on
/pet back off
/goto :DonePulling
}
| Pull with cast
} else {
/if (${DebugPull}) /echo DEBUGPULL Pull: Casting to pull mob
/delay 5s !${Me.Moving} || (${AggroTargetID} && !${ChainPull}) || ${Me.XTarget[${XTSlot2}].ID} || ${Me.XTarget[${XTSlot}].ID}==${Target.ID}
/if (${Target.ID}) /face nolook
/look 0
/if ((${AggroTargetID} && !${ChainPull}) || ((${Me.XTarget[${XTSlot2}].ID} || ${Me.XTarget[${XTSlot}].ID}==${Target.ID}) && ${ChainPull}) || ${Target.Type.Equal[corpse]}) /goto :PullAgain
/if (!${SpawnCount[npc los id ${Target.ID}]}) {
/varcalc PullDist ${PullDist}*.8
/goto :PullAgain
}
/if (!${Me.Moving}) /call CastWhat "${PullWith}" ${Target.ID} Pull
/if (${Macro.Return.Equal[CAST_SUCCESS]} || (${AggroTargetID} && !${ChainPull}) || ((${Me.XTarget[${XTSlot2}].ID} || ${Me.XTarget[${XTSlot}].ID}==${Target.ID}) && ${ChainPull})) /varset Pulled 1
/delay 1s (${AggroTargetID} && !${ChainPull}) || ((${Me.XTarget[${XTSlot2}].ID} || ${Me.XTarget[${XTSlot}].ID}==${Target.ID}) && ${ChainPull})
}
|- Toggle puller mode off if option enabled.
/if (${Select[${Role},pullerpettank]} && ${PullRoleToggle} && ${Group.Puller.ID}==${Me.ID}) /call TogglePullMode TurnOff
} else /if ((${AggroTargetID} && !${ChainPull}) || (${Me.XTarget[${XTSlot2}].ID} || ${Me.XTarget[${XTSlot}].ID}==${MyTargetID}) && ${ChainPull}) {
| The else /if fixes when puller stalls because puller grabs aggro with out getting to pull.
/varset Pulled 1
}
| If pull failed start over while timer > 0
/if (!${Pulled}) /goto :PullAgain
:DonePulling
| reset mq2moveutils mdist back to 10 from pull distance to ensure correct movement
/moveto mdist 10
/if (${rangedSwitch}) {
/call CheckCasting 50
/exchange "${OrigRanged}" ranged
/varset rangedSwitch 0
/delay 5
}
/if (${ammoSwitch}) {
/call CheckCasting
/if (${Cursor.ID}) /autoinventory
/exchange "${tempAmmo}" ammo
/varset ammoSwitch 0
/delay 5
}
/varset Pulling 0
| Turn autofire back on
/if (${AutoFireOff}) {
/if (${DebugPull}) /echo DEBUGPULL Pull AutoFire on
/varset AutoFireOff 0
/varset AutoFireOn 1
}
/if (${Select[${Role},hunter,hunterpettank]} && ${MyTargetID}) {
/call Combat
/call CombatReset
/call PullReset
| Try to return home if exceeding max radius while in ANY hunter mode
/if (${Math.Distance[${Me.Y},${Me.X}:${CampYLoc},${CampXLoc}]}>${Math.Calc[${MaxRadius}*.95]}) {
/if (${DebugPull}) /echo DEBUGPULL Pull Returning hunter to camp
/echo ${Role}: Reached edge of ${MaxRadius} hunting radius. Trying to return to camp.
/varset ReturnToCamp 1
/call DoWeMove
}
/if (${DebugPull}) /echo DEBUGPULL Pull Leaving hunter combat
/return
}
/if (${DebugPull}) /echo DEBUGPULL Pull Done Pulling
/if (${ReturnToCamp} && ${Pulled}) {
/if (${Target.Type.NotEqual[corpse]}) /call WaitForMob
/varset Pulled 0
}
/if (${DebugPull}) /echo DEBUGPULL Pull Leave Mob ID:${Spawn[${MyTargetID}].ID}
/return