Click.mac - resurrect with cleric epic or spell v2.24b

Post your completed (working) macros here. Only for macros using MQ2Data syntax!

Moderator: MacroQuest Developers

User avatar
blueninja
a grimling bloodguard
a grimling bloodguard
Posts: 541
Joined: Thu Aug 28, 2003 7:03 am
Location: Göteborg, Sweden

Click.mac - resurrect with cleric epic or spell v2.24b

Post by blueninja » Fri May 07, 2004 3:45 pm

Resurrects corpses within a defined radius using the cleric epic or a spell. The order in which it resurrects is defined with profiles in the ini file.

Beta release of the converted macro, please report any bugs you find.

Uses an ini file (uses a default set of values if it can't find the ini), change CLICKINIFILE so it points to your ini. This ini file contains a few default options, a list of profiles that define the priority in which to click corpses and a list of valid player races.

Version 2.24b:
Added an option to hail corpses before they are resurrected

Added an option to face the corpse before resurrecting in case it is out of view (more than 30 degrees away from your heading). It can also use GD's turn.inc to turn more naturally. In order to use that feature you need to get turn.inc and uncomment the line that says "#include turn.inc". Turn.inc is availiable from the snippets forum

Added an option to skip corpses in case they are too far away above or below you, useful in places with several floors.

Code: Select all

| Click.mac - v2.24b - Resurrect corpses around you - by blueninja
| BETA RELEASE
|        MQ2Data port of v2.22
|    Resurrects all corpses within a radius using the cleric epic or spell
|    Resurrects high priority classes first as defined in the ini file
|
|    Usage:
|       /macro click [radius] [only] [con/nocon] [racecheck/noracecheck] [useblacklist/noblacklist]
|                    [usespell/useepic] [sit/stand] [spellname] <nameofspell> [spellslot] <X> [face/noface]
|                     [turn/noturn] [hail/nohail] [maxz] <Z> [prioritygroup0] [prioritygroup1] [prioritygroup2]...
|       /macro click buildraces
|           [radius]                   - set radius to look for corpses in, default value is in ini file
|           [only]                     - stop ressing after corpses in priority groups have been ressed
|           [con/nocon]                - toggle /consider to test if corpse is pc or npc
|           [racecheck/noracecheck]    - toggle check for race to test if corpse is pc or npc
|                                        (requires ini file)
|           [useblacklist/noblacklist] - toggle check for blacklisted corpses
|                                        (requires ini file)
|           [usespell/useepic]         - use epic or spell to resurrect
|           [sit/stand]                - toggle sitting while not casting to regen mana
|           [spellname] <nameofspell>  - name of spell to use if using spell
|           [spellslot] <X>            - spell gem number to use for spell if using spell
|           [face/noface]              - face the corpse before ressing if it's out of view
|           [turn/noturn]              - turn towards the corpse before ressing if it's out of view using GD's turn.inc
|           [hail/nohail]              - hail the corpse before ressing it
|           [maxz] <Z>                 - maximum distance to the corpse in the Z plane
|           [prioritygroupX]           - names of groups to res before others
|
|           [buildraces]               - update list of player races in ini file
|
|    Example:
|       /macro click 200 nocon dps healers
|       /macro click 200 only cr
|       /macro click usespell 150 only cr spellname resurrection spellslot 5
|       /macro click buildraces
|
|    Make sure you set CLICKINIFILE a few lines down to a valid filename

|    You probably want to run this with '/filter macros enhanced' to avoid targeting spam

| Uncomment the following line if you want to use GD's turn.inc
|#include turn.inc

#turbo

|Make sure you set this to point to a valid location
#define CLICKINIFILE click.ini
#define MEMDELAY 30
#define DEFAULTSPELL Reviviscence

#event interrupt "Your spell is interrupted."
#event notequipped "You cannot use this item unless it is equipped."
#event notmemmed "You do not seem to have that spell memorized."
#event willdecay "This corpse will decay in#*#"
#event restime "This corpse's resurrection time will expire in #*#"
#event corpsetooold "This corpse is too old to be resurrected."
#event clickedoldcorpse "This player cannot be resurrected. The corpse is too old."
#event corpseexpire "This corpse is waiting to expire."
#event couldnotrez "You were unable to restore the corpse to life, but you may have success with a later attempt."
#event youmustbestanding "You must be standing to cast a spell."
#event fizzle "Your spell fizzles!"
#event oom "Insufficient Mana to cast this spell."
#event skip "[MQ2] skip"

Sub Event_restime
|Got a message that the corpse has time left on rez timer
   /varset ispccorpse 1
/return

Sub Event_willdecay
|Since the rez time message comes before the expiry time message the
|event should fire now
   /doevents restime

|Small pause to be sure
   /if (${ispccorpse}!=1) {
      /delay 5
      /doevents restime
   }
   /varset gotconmsg 1
/return

Sub Event_corpsetooold
|Corpse too old to rez, treat as npc corpse
   /varset gotconmsg 0
/return

Sub Event_corpseexpire
|Corpse about to poof, treat as npc corpse
   /varset gotconmsg 0
/return

Sub Event_couldnotrez
   /varcalc couldnot ${couldnot}+1
/return

Sub Event_clickedoldcorpse
| Tried to resurrect a corpse that was too old
| Remove from statistics
   /varcalc clicked ${clicked}-1
   /varset status 1
/return

Sub Event_skip
   /varset status 1
   /varcalc clicked ${clicked}-1
   /call output 4 "Skipping.."
/return

Sub Event_interrupt
   /if (${Me.State.Equal[DUCK]}) {
      /varset status 1
      /varcalc clicked ${clicked}-1
      /call output 4 "Ducked, moving to next.."
   } else {
      /varset status 2
   }
/return

Sub Event_fizzle
   /varset status 2
/return

Sub Event_oom
   /varset status 3
/return

Sub Event_notequipped
   /beep
   /call output 1 "ERROR: Epic is in an inventory slot, need to equip it in primary"
   /endmacro
/return

Sub Event_notmemmed
   /call sitdown
   /if (${usespell}==0) {
      /beep
      /call output 1 "ERROR: Epic is bagged or banked"
      /endmacro
   } else {
| Memorize spell
      /call output 3 "Spell ${spellname} not memorized"
      /memspell ${spellslot} "${spellname}"
      /delay MEMDELAY
| Check if it's memmed now, otherwise quit
      /if (!${Me.Gem[${spellslot}].Name.Equal[${spellname}]}) {
         /call output 1 "Couldn't memorize spell ${spellname}"
         /endmacro
      }
      /varset status 4

   }
/return

Sub Event_youmustbestanding
   /varset status 2
   /call output 4 "Standing up.."
   /call standup
/return


Sub Main
   /declare status int outer
   /declare ispccorpse bool outer
   /declare gotconmsg bool outer
   /declare spawncount int local
   /declare radius int local
   /declare contimer timer outer
   /declare timeouts int outer
   /declare corpses int outer
   /declare skippedrace int outer
   /declare skippedcon int outer
   /declare skippedblacklist int outer

   /declare clicked int outer
   /declare couldnot int outer
|prios(x,y) - contains name of profile in (x,1) and number of filters in profile in (x,2)
   /declare prios[100,2] outer
   /declare priocount int outer
|priofilters(x,y) - y=0..prios(x,2) contains the values of the filters for prios(x,1)
   /declare priofilters[100,100] outer
   /declare only bool local
   /declare argloop int local
   /declare racecount int outer
|List of valid player races
   /declare races[100] outer
   /declare curid int outer

   /declare spellmaxrange int local

   /declare noiselevel int outer
   /declare contimeout int outer
   /declare defaultradius int local
   /declare showstats bool local
   /declare useraces bool outer
   /declare consider bool outer
   /declare usespell bool outer
   /declare spellname string outer
   /declare spellslot int outer
   /declare useblacklist bool outer
   /declare sit bool outer
   /declare hail bool outer      
   /declare face bool outer      
   /declare turn bool outer      
   /declare maxz int outer
   /varset noiselevel 0

|Read configuration
   /call getconfig "noiselevel" 2
   /varset noiselevel ${Macro.Return}
   /call getconfig "contimeout" 50
   /varset contimeout ${Macro.Return}
   /call getconfig "defaultradius" 100
   /varset defaultradius ${Macro.Return}
   /call getconfig "showstats" 0
   /varset showstats ${Bool[${Macro.Return}]}
   /call getconfig "useraces" 0
   /varset useraces ${Bool[${Macro.Return}]}
   /call getconfig "consider" 1
   /varset consider ${Bool[${Macro.Return}]}
   /call getconfig "useblacklist" 0
   /varset useblacklist ${Bool[${Macro.Return}]}
   /call getconfig "usespell" 0
   /varset usespell ${Bool[${Macro.Return}]}
   /call getconfig "spellname" DEFAULTSPELL
   /varset spellname ${Macro.Return}
   /call getconfig "spellslot" 8
   /varset spellslot ${Macro.Return}
   /call getconfig "sit" 0
   /varset sit ${Bool[${Macro.Return}]}
   /call getconfig "hail" 0
   /varset hail ${Bool[${Macro.Return}]}
   /call getconfig "face" 0
   /varset face ${Bool[${Macro.Return}]}
   /call getconfig "turn" 0
   /varset turn ${Bool[${Macro.Return}]}
   /call getconfig "maxz" 0
   /varset maxz ${Bool[${Macro.Return}]}

   /varset corpses 0
   /varset clicked 0
   /varset skippedrace 0
   /varset skippedcon 0
   /varset timeouts 0
   /varset skippedblacklist 0
   /varset couldnot 0
   /varset only 0
   /varset radius ${defaultradius}
   /varset priocount 0
   /varset spellmaxrange 200


|Check if parameter is passed
   /for argloop 0 to 10   
   /if (${Defined[Param${argloop}]}) {
      /call IsNumeric "${Param${argloop}}"
      /if (${Macro.Return}==0) {
|non-numeric argument
         /if (${String[${Param${argloop}}].Equal[only]}) {
            /varset only 1
         } else /if (${String[${Param${argloop}}].Equal[buildraces]}) {
            /call buildini
            /endmacro
         } else /if (${String[${Param${argloop}}].Equal[noracecheck]}) {
            /varset useraces FALSE
         } else /if (${String[${Param${argloop}}].Equal[racecheck]}) {
            /varset useraces TRUE
         } else /if (${String[${Param${argloop}}].Equal[noblacklist]}) {
            /varset useblacklist FALSE
         } else /if (${String[${Param${argloop}}].Equal[useblacklist]}) {
            /varset useblacklist TRUE
         } else /if (${String[${Param${argloop}}].Equal[usespell]}) {
            /varset usespell TRUE
         } else /if (${String[${Param${argloop}}].Equal[useepic]}) {
            /varset usespell FALSE
         } else /if (${String[${Param${argloop}}].Equal[sit]}) {
            /varset sit TRUE
         } else /if (${String[${Param${argloop}}].Equal[stand]}) {
            /varset sit FALSE
         } else /if (${String[${Param${argloop}}].Equal[nocon]}) {
            /varset consider FALSE
         } else /if (${String[${Param${argloop}}].Equal[con]}) {
            /varset consider TRUE
         } else /if (${String[${Param${argloop}}].Equal[hail]}) {
            /varset hail TRUE
         } else /if (${String[${Param${argloop}}].Equal[nohail]}) {
            /varset hail FALSE
         } else /if (${String[${Param${argloop}}].Equal[face]}) {
            /varset face TRUE
         } else /if (${String[${Param${argloop}}].Equal[noface]}) {
            /varset face FALSE
         } else /if (${String[${Param${argloop}}].Equal[turn]}) {
            /varset turn TRUE
         } else /if (${String[${Param${argloop}}].Equal[noturn]}) {
            /varset turn FALSE
         } else /if (${String[${Param${argloop}}].Equal[spellname]}) {
            /varcalc argloop ${argloop}+1
            /varset spellname "${Param${argloop}}"
         } else /if (${String[${Param${argloop}}].Equal[spellslot]}) {
            /varcalc argloop ${argloop}+1
            /varset spellslot "${Param${argloop}}"
         } else /if (${String[${Param${argloop}}].Equal[maxz]}) {
            /varcalc argloop ${argloop}+1
            /varset maxz ${Param${argloop}}
         } else {
            /varcalc priocount ${priocount}+1
            /varset prios[${priocount},1] "${Param${argloop}}"
            /call buildprios "${Param${argloop}}" ${priocount}
            /varset prios[${priocount},2] ${Macro.Return}
         }
            
      } else {
|Numeric argument, set radius
         /varset radius ${Param${argloop}}
      }
   } else {
|No argument passed, default
      /goto :argsdone
   }
   /next argloop

:argsdone
   /call sitdown
   /if (${usespell}==1) {
      /varset spellmaxrange ${Spell[${spellname}].Range}
   }
   /if (${radius}>${spellmaxrange}) {
      /call output 2 "WARNING: Radius larger than range of spell, setting to ${spellmaxrange}"
      /varset radius ${spellmaxrange}
   }
|If we havent found any profiles on command line, set first one to default
   /if (${priocount}==0) {
      /varset priocount 1
      /varset prios[${priocount},1] "default"
      /call buildprios "default" ${priocount}
      /varset prios[${priocount},2] ${Macro.Return}

   }

|Build an array of valid player races from ini file
   /if (${useraces}==1) {
      /varset racecount 0

      :iniloop
         /varcalc racecount ${racecount}+1
      /varset races[${racecount}] ${Ini["CLICKINIFILE",races,${String[${Int[${Math.Calc[${racecount}-1]}]}]}]}
         /if (!${Bool[${races[${racecount}]}]}) /goto :leaveloop
      /goto :iniloop
   }

:leaveloop
|Read the blacklist after the normal profiles
   /varcalc priocount ${priocount}+1
   /varset prios[${priocount},1] "blacklist"
   /call buildprios "blacklist" ${priocount}
   /varset prios[${priocount},2] ${Macro.Return}


|First loop through high priority classes..

   /varset curid 0

   /for argloop 1 to ${Math.Calc[${priocount}-1]}
    /varset spawncount 1
      :loopprio
         /doevents
         /varset status 0
         /varset curid ${Me.NearestSpawn[${spawncount},corpse range 1 70 radius ${radius}].ID}
|If we didn't get a new target move on to low priority
          /if (${curid}==NULL) /goto :clicktherest
      /varcalc spawncount ${spawncount}+1

|Check if this is a high priority corpse
         /call HasPriority ${prios[${argloop},1]} ${curid}
         /if (!${Macro.Return}==1) /goto :loopprio
         /call clickit ${curid}
|Gather statistics
         /doevents couldnotrez
      /goto :loopprio

   :clicktherest
   /next argloop

|If parameter 'only' is specified don't move on to low priority corpses
   /if (${only==1}) /goto :end
   /varset curid 0
  /varset spawncount 1

|.. then everyone else
   :restloop
      /doevents
      /varset status 0
       /varset curid ${Me.NearestSpawn[${spawncount},corpse range 1 70 radius ${radius}].ID}
|If we didn't get a new target quit
      /if (${curid}==0) /goto :end
    /varcalc spawncount ${spawncount}+1

|Check that this isn't a priority corpse
      /for argloop 1 to ${Math.Calc[${priocount}-1]}
         /call HasPriority ${prios[${argloop},1]} ${curid}
         /if (${Macro.Return}==1) /goto :restloop
      /next argloop
      /call clickit ${curid}
|Gather statistics
      /doevents couldnotrez
   /goto :restloop

:end
|Gather statistics
   /call sitdown
   /doevents couldnotrez
   /if (${showstats}==1) /call statsub

/return

Sub getconfig
|Read the value of a [config] parameter in ini file
|/call getconfig "paramname" "defaultvalue"
|Returns value
   /if (!${Defined[Param0]}) /return "false"
   /if (!${Defined[Param1]}) /return "false"

   /declare inivalue local

   /varset inivalue ${Ini["CLICKINIFILE","config","${Param0}"]}
|Not set in ini file, return default value
   /if (${String[${inivalue}].Equal[NULL]}) /varset inivalue ${Param1}
/return ${inivalue}

Sub output
|Echo that uses the noiselevel setting
   /if (!${Defined[Param0]}) /return
   /if (!${Defined[Param1]}) /return
   /if (${Param0}<=${noiselevel}) {
      /echo ${Param1}
   }
/return

Sub IsNumeric
|Returns 1 if Param0 is numeric, 0 if not
   /if (!${Defined[Param0]}) /return 0
   /if (${Param0.Length}==0) /return 0
    /declare counter int local
    /for counter 1 to ${Param0.Length}
    /if (${String[0123456789].Find[${Param0.Mid[${counter},1]}]}==NULL) /return 0
   /next counter
/return 1

Sub statsub
|Display stats
   /declare runtime float local
   /declare runstring local
   /varset runtime ${Macro.RunTime}
   /echo Corpses found: ${corpses}, ressed: ${clicked}, could not restore: ${couldnot}
   /echo Skipped race: ${skippedrace}, Skipped con: ${skippedcon}, Skipped blacklist: ${skippedblacklist}
   /varset runstring Time taken ${Int[${Math.Calc[${runtime}/60]}]} minutes ${Int[${Math.Calc[${runtime}%60]}]} seconds
   /if (${timeouts}>0) /echo Consider timeouts: ${timeouts}
   /if (${corpses}>0) /varset runstring ${runstring} (avg time per corse: ${Math.Calc[${runtime}/${corpses}]}s)
   /echo ${runstring}
/return

Sub buildprios
|Builds priority lists from ini file into priofilters(@Param1,y)
|@Param0 is the name of the profile
   /declare filters local
    /declare temp local
   /declare curfilt local
   /declare pipeloc int local
   /declare nextpipe int local
   /declare filtercount int local
   /if (!${Defined[Param0]}) /return
   /varset filters ${Ini["CLICKINIFILE",${Param0}]}
   /if (${String[${filters}].Equal[||]} || ${String[${filters}].Equal[NOTFOUND||]}) {
      /if (${Param0.Equal[default]}) {
||If default is empty, set defaults to bards and clerics
         /varset priofilters[${Param1},1] cBard
         /varset priofilters[${Param1},2] cCleric
         /return 2
      } else {
         /return -1
      }
   }

   /varset filters ${filters.Left[${Math.Calc[${filters.Length}-2]}]}
   /varset filtercount 1

   /varset nextpipe ${filters.Find[|]}
   /if (${nextpipe}==NULL) {
      /varset priofilters[${Param1},1] ${filters.Left[1]}${Ini["CLICKINIFILE",${Param0},${filters}]}
      /goto :leavebuildprios
   }

:loopfilters
   /varset curfilt ${filters.Mid[1,${Math.Calc[${nextpipe}-1]}]}
   /varset filters ${filters.Right[${Math.Calc[${filters.Length}-${nextpipe}]}]}
   /varset temp ${Ini["CLICKINIFILE",${Param0},${curfilt}]}
   /varset priofilters[${Param1},${filtercount}] ${curfilt.Left[1]}${Ini["CLICKINIFILE",${Param0},${curfilt}]}
   /varset nextpipe ${filters.Find[|]}
   /varcalc filtercount ${filtercount}+1

   /if (${nextpipe}==NULL) {
      /varset priofilters[${Param1},${filtercount}] ${filters.Left[1]}${Ini["CLICKINIFILE",${Param0},${filters}]}
      /goto :leavebuildprios
   }

   /goto :loopfilters
   
:leavebuildprios
   /varcalc filtercount ${filtercount}+1

/return ${filtercount}

Sub HasPriority
|Test if corpse with id @Param1 has high priority in profile @Param0
   /declare test local
   /declare hpcount int local
   /declare filtnum int local
   /if (!${Defined[Param0]}) {
      /varset test default
   } else {
      /varset test ${Param0}
   }
   /call findprioinarray ${test}
   /if (${prios[${Macro.Return},2]}<=0) /return 0

   /varset filtnum ${Macro.Return}

   /for hpcount 1 to ${Math.Calc[${prios[${filtnum},2]}-1]}
      /if (${priofilters[${filtnum},${hpcount}].Left[1].Equal[c]}) {
      /if (${Spawn[${Param1}].Class.Name.Equal[${priofilters[${filtnum},${hpcount}].Right[${Math.Calc[${priofilters[${filtnum},${hpcount}].Length}-1]}]}]}) {
            /return 1
         }
      } else /if (${priofilters[${filtnum},${hpcount}].Left[1].Equal[n]}) {
         /if (${Spawn[${Param1}].Name.Find[${priofilters[${filtnum},${hpcount}].Right[${Math.Calc[${priofilters[${filtnum},${hpcount}].Length}-1]}]}]}!=NULL) {
            /return 1
         }
      } else /if (${priofilters[${filtnum},${hpcount}].Left[1].Equal[g]}) {
         /if (${Spawn[${Param1}].Guild.Find[${priofilters[${filtnum},${hpcount}].Right[${Math.Calc[${priofilters[${filtnum},${hpcount}].Length}-1]}]}]}!=NULL) {
            /return 1
         }
      }

   /next hpcount
/return 0

Sub findprioinarray
|Search array for @Param0, returns the location or -1 if not found
   
   /declare counter int local
   /for counter 1 to ${priocount}
      /if (${Param0.Equal[${prios[${counter},1]}]}) /return ${counter}
   /next counter
/return -1

Sub IsPCRace
|Test if race of corpse with id @Param0 is in the array of valid races
   /declare loopcounter int local
   /for loopcounter 0 to ${racecount}
      /if (${Spawn[${Param0}].Race.Name.Equal[${races[${loopcounter}]}]}) /return 1
   /next loopcounter

/return 0

Sub clickit
   /declare counter local
   /declare giveup local
   /declare castwait local
   
|Test Z distance
   /if (${maxz}>0 && ${Spawn[${curid}].DistanceZ}>${maxz}) {
      /call output 3 "Skipping ${Spawn[${curid}].Name} because Z distance too large"
      /return
   }

|Test if valid race
   /if (${useraces}) {
      /call IsPCRace ${Param0}
      /if (${Macro.Return}==0) {
         /varcalc skippedrace ${skippedrace}+1
         /return
      }
   }


   /if (!${useblacklist}) /goto :skipblacklist
|Check if corpse is blacklisted
   /call HasPriority blacklist ${curid}
   /if (${Macro.Return}==1) {
      /call output 3 "Skipping blacklisted ${Spawn[${curid}].Name}"
      /varcalc skippedblacklist ${skippedblacklist}+1
      /return
   }

   :skipblacklist

|Target corpse
   /tar id ${curid}
   /if (${Target.ID}==NULL) /return

   /varcalc corpses ${corpses}+1

|Doevents to get stats and flush /con messages
   /doevents

   /if (${consider}) {
      /varset ispccorpse FALSE
      /varset gotconmsg FALSE

      /consider

|Set timer so we don't get stuck waiting for a /con message
      /varset contimer ${contimeout}

   :waitcon
      /doevents corpseexpire
      /doevents willdecay
      /delay 1
|After a few seconds give up and assume it's a pc corpse
      /if (${contimer}==0) {
         /call output 3 "Timeout waiting for /con message, assuming pc corpse"
         /varcalc timeouts ${timeouts}+1
         /varset gotconmsg TRUE
         /varset ispccorpse TRUE
      }
      /if (!${gotconmsg}) /goto :waitcon

      /if (!${ispccorpse}) {
         /varcalc skippedcon ${skippedcon}+1
         /call output 3 "Skipping ${Target.Name}"
         /return
      }
   }
   /varcalc clicked ${clicked}+1

|Counter to give up after 4 attempts at rezzing a corpse
|that fail either by interrupt or if we can't cast for 3 seconds
   /varset giveup 0

:tryagain
   /varcalc giveup ${giveup}+1

|Too many tries, move on to next
   /if (${giveup}>4) /return

   /varset status 0
   /varset castwait 0

|Wait until we are not casting, if we wait too long, treat as an interrupt
:waitforcast
   /if (!${Bool[${Me.Casting}]}) /goto :castit
   /delay 5
   /varcalc castwait ${castwait}+1
   /if (${castwait}>5) /goto :tryagain
   /goto :waitforcast

:castit
   /if (${Math.Abs[${Math.Calc[${Target.HeadingTo.Degrees}-${Me.Heading.Degrees}]}]}>30) {
      /if (${turn}) /call Turn
      /if (${face}) {
         /face
         :waitforface
         /if (${Math.Abs[${Math.Calc[${Target.HeadingTo.Degrees}-${Me.Heading.Degrees}]}]}>5) /goto :waitforface
      }
   }
   /call output 3 "Resurrecting ${Target.Name}"
   /if (${hail}) /keypress HAIL
   /if (!${usespell}) {
| Use epic
      /call standup
      /cast item "water sprinkler of nem ankh"
   } else {
| Use spell

    /if (${Me.Gem[${spellname}]}==NULL) {
         /call output 3 "Spell ${spellname} not memorized"
         /memspell ${spellslot} "${spellname}"
         /delay MEMDELAY
| Check if it's memmed now, otherwise quit
         /if (!${Me.Gem[${spellslot}].Name.Equal[${spellname}]}) {
            /call output 1 "Couldn't memorize spell ${spellname}"
            /endmacro
         }
    }

      :waitspellrefresh
| Check if enough mana, if not med until there is enough
      /if (${Me.CurrentMana}<${Spell[${spellname}].Mana}) /call medup

| Wait until spell is refreshed
      /if (!${Me.SpellReady[${spellname}]}) {
         /delay 1
         /goto :waitspellrefresh
      }
      /call standup

      /cast ${spellname}
   }

|Wait until not casting anymore
:castdelay
   /delay 5
   /doevents
   /if (${status}==4) {
      /varcalc giveup ${giveup}-1
      /goto :tryagain
   }
   /if (${status}==3) {
      /varcalc giveup ${giveup}-1
      /call medup
      /goto :tryagain
   }
   /if (${status}==2) /goto :tryagain
   /if (${status}==1) {
      /call sitdown
      /return
   }
   /if (${Bool[${Me.Casting}]}) /goto :castdelay

   /call sitdown

/return

Sub medup
   /call sitdown
| Loop until enough mana to cast
   :medloop
      /delay 1s
   /if (${Me.CurrentMana}<${Spell[${spellname}].Mana}) /goto :medloop

   /call standup
/return

Sub sitdown
| Don't bother sitting if full mana
   /if (${Me.PctMana}==100) /return
| Check sit setting
   /if (!${sit}) /return
| Don't try to sit if on a mount
  /if (${Me.Mount.ID}!=NULL) /return

| If not sitting, sit down
   /if (!${Me.State.Equal[SIT]}) /sit
/return

Sub standup
| Don't try to stand if on a mount
   /if (${Me.Mount.ID}!=NULL) /return
| If not standing, stand up
   /if (!${Me.State.Equal[STAND]}) /stand
/return

Sub buildini
|Loop through the players in zone and compare their race to the list of valid
|player races in ini file and add new races
   /declare currace local
   /varset racecount 0
|Read races from ini file
      :buildiniloop
         /varcalc racecount ${racecount}+1
      /varset races[${racecount}] ${Ini["CLICKINIFILE",races,${String[${Int[${Math.Calc[${racecount}-1]}]}]}]}
         /if (!${Bool[${races[${racecount}]}]}) /goto :buildleaveloop
      /goto :buildiniloop

:buildleaveloop
   /declare added int local

   /varset added 0
   /declare pccount int local

   /varset pccount 0
   /declare counter local
  /varset counter 1

|Get first pc
   /varset curid ${Me.ID}
   /varset currace ${Spawn[${curid}].Race}
:buildloop

   /varcalc pccount ${pccount}+1
|See if race is in array already
   /call buildisinarray "${String[${currace}]}"
   /if (${Macro.Return}==0) {
|New race, add to ini
      /if (${Defined[currace]} && !${currace.Equal[NULL]}) {
         /ini "CLICKINIFILE" races ${String[${Int[${Math.Calc[${racecount}-1]}]}]} "${currace}"
         /varset races[${racecount}] ${currace}
         /varcalc racecount ${racecount}+1
         /varcalc added ${added}+1
         /call output 2 "Added race: ${currace}"
      }
   }
   /varset curid ${Me.NearestSpawn[${counter},pc].ID}
   /varset currace "${Spawn[${curid}].Race}"
   /varcalc counter ${counter}+1
   /if (${curid}==NULL) /goto :buildend

   /goto :buildloop
:buildend
   /varcalc racecount ${racecount-1}
   /call output 2 "Players counted: ${pccount} Races in ini: ${racecount}, added ${added}"

/return

Sub buildisinarray(String Searchstring)
|Search array for @Param0
   
   /declare counter int local
   /for counter 1 to ${racecount}
      /if (${Searchstring.Equal[${races[${counter}]}]}) /return 1
   /next counter
/return 0

Sample .ini file

Code: Select all

# Sample config file for click.mac v2.23 by blueninja

# Script configuration
[config]

# Control how much text the script outputs, 0=silent
noiselevel=3

# How long to wait for /con messages before assuming it's a pc corpse and moving on
contimeout=50

# Default radius to look for corpses in, used if nothing else is passed on the command line
defaultradius=200

# Give statistics output after each run, bypasses noiselevel
showstats=1

# Check corpses against the table of valid player races? Can save some time but
# could also result in missing player corpses if they are of a race not specified
# in the [races] section of this file
useraces=1

# /consider corpses to see if they are pc corpses or not
consider=1

# Use the blacklist to skip corpses
useblacklist=1

# Use spell or epic
usespell=0
spellname=Reviviscence
spellslot=6

# Sit down to med between casts
sit=0

# Hail corpses before resurrecting them
hail=0

# Face corpse before resurrecting it
face=1

# Face corpse before resurrecting it using GD's turn.inc
turn=0

# Maximum distance to the corpse in the Z plane, 0 to disable
maxz=50

# Profiles used to decide the priority of corpses, click the ones in the profiles first
# the name in brackets is the name of the profile (what you would specify on the command line)
# c1,c2,n1 etc are the actual filters to match against. If they are prefixed with c,
# as in c1=Monk, it is matched against the class of the corpse. If prefixed with n, as in
# n1=buddy1 it will match against the name of the corpse. Class makes an exact match, name
# just needs to contain the string.

# Sample profile, zerg those dps melees back in
[dps]
c1=Monk
c2=Bard
c3=Rogue
c4=Ranger

# Sample profile, wipe inc, get those rezzers a box
[rez]
c1=Cleric
c2=Paladin
c3=Necromancer

# Sample profile
[healers]
c1=Cleric
c2=Druid

# Sample profile, names of people that get priority
[friends]
n1=joe
n2=burt

# Sample profile, click certain guilds first
[guild]
g1=myguild
g2=friendsguild

# Sample profile, default is the one used if no others specified on command line
[default]
c1=Cleric
c2=Bard

# Sample blacklist, these corpses will not be rezzed
[blacklist]
c1=stupidclass
n1=idiotguy
g1=evil guild
g2=stupidguild

# List of races that player characters can be
# used to avoid waiting for /con messages from npc corpses
[races]
0=Barbarian
1=Human
2=Half Elf
3=Dark Elf
4=Wood Elf
5=Iksar
6=Halfling
7=Dwarf
8=Ogre
9=Gnome
10=Vah Shir
11=Froglok
12=Imp
13=Troll
14=Erudite
15=Wolf
16=UNKNOWN RACE
17=Fire Elemental
18=Bear
19=Water Elemental
20=Earth Elemental
21=Air Elemental
22=Skeleton New
23=High Elf
Last edited by blueninja on Sun Oct 10, 2004 1:24 pm, edited 4 times in total.

Rassilon
a lesser mummy
a lesser mummy
Posts: 73
Joined: Thu Sep 04, 2003 6:34 pm

Post by Rassilon » Sun May 09, 2004 2:28 am

How can I have it 'Hail' the corpse it is rezing?

Thanks

User avatar
blueninja
a grimling bloodguard
a grimling bloodguard
Posts: 541
Joined: Thu Aug 28, 2003 7:03 am
Location: Göteborg, Sweden

Post by blueninja » Sun May 09, 2004 5:46 am

Untested but this should do the trick.

Code: Select all

   /if (${castwait}>5) /goto :tryagain 
   /goto :waitforcast 

:castit 
   /call output 3 "Resurrecting ${Target.Name}" 
   [color=red]/say Hail, ${Target.CleanName}[/color]
   /if (!${usespell}) { 

ml2517
a grimling bloodguard
a grimling bloodguard
Posts: 1216
Joined: Wed Nov 12, 2003 1:12 am

Post by ml2517 » Sun May 09, 2004 5:49 am

or this might work as well and be a bit less dangerous

Code: Select all

   /if (${castwait}>5) /goto :tryagain 
   /goto :waitforcast 

:castit 
   /call output 3 "Resurrecting ${Target.Name}" 
   /keypress HAIL   
   /if (!${usespell}) { 

User avatar
blueninja
a grimling bloodguard
a grimling bloodguard
Posts: 541
Joined: Thu Aug 28, 2003 7:03 am
Location: Göteborg, Sweden

Post by blueninja » Sun May 09, 2004 5:53 am

Well I thought about that but what if you're typing? Perhaps I should've added a check that it's not null or something.

Code: Select all

   /if (${castwait}>5) /goto :tryagain 
   /goto :waitforcast 

:castit 
   /call output 3 "Resurrecting ${Target.Name}" 
[color=red]   /if (${Target}!=NULL) /say Hail, ${Target.CleanName} [/color]
   /if (!${usespell}) { 

ml2517
a grimling bloodguard
a grimling bloodguard
Posts: 1216
Joined: Wed Nov 12, 2003 1:12 am

Post by ml2517 » Sun May 09, 2004 11:44 am

The /keypress HAIL shouldn't affect your typing at all. The 'HAIL' is one of the specific "backdoor" /keypress'es that does its work behind the scenes.

User avatar
blueninja
a grimling bloodguard
a grimling bloodguard
Posts: 541
Joined: Thu Aug 28, 2003 7:03 am
Location: Göteborg, Sweden

Post by blueninja » Sun May 09, 2004 4:16 pm

Ah yes, I didn't realize that.. It's in the new version I just posted. Thanks.

Epsilon
a lesser mummy
a lesser mummy
Posts: 61
Joined: Wed Apr 30, 2003 5:46 pm

Post by Epsilon » Tue Sep 28, 2004 7:59 am

anyone would like to update this awesome macro so it works with latest MQ?

User avatar
blueninja
a grimling bloodguard
a grimling bloodguard
Posts: 541
Joined: Thu Aug 28, 2003 7:03 am
Location: Göteborg, Sweden

Post by blueninja » Tue Sep 28, 2004 10:37 am

Hmm yeah, my guild kinda died so I haven't had much use for this in a while so I pretty much forgot about it. I'll try to get a quick update soon. Looks like it could really use a total rewrite but that would take longer so I'll start with a simple upgrade just so it works.

Epsilon
a lesser mummy
a lesser mummy
Posts: 61
Joined: Wed Apr 30, 2003 5:46 pm

Post by Epsilon » Tue Sep 28, 2004 6:37 pm

yeah that fits me perfectly

User avatar
blueninja
a grimling bloodguard
a grimling bloodguard
Posts: 541
Joined: Thu Aug 28, 2003 7:03 am
Location: Göteborg, Sweden

Post by blueninja » Sun Oct 10, 2004 1:25 pm

Epsilon wrote:anyone would like to update this awesome macro so it works with latest MQ?
Made some minor changes and it seems to work fine for me now. Try it out and let me know if something doesn't work..

Clueless_Coder
a hill giant
a hill giant
Posts: 192
Joined: Wed Aug 25, 2004 3:18 pm

Post by Clueless_Coder » Tue Oct 26, 2004 6:43 pm

First, let me say I love this macro.

Was just wondering if there was any way I could add either an announcement in a channel such as "Rezzing %T " in my guild cleric channel or even in /say, as well as send a /tell to the target (both procedures pretty much the routine in my guild for raids)

Also, is there any way, either via code or ini or cfg file, to incorprate a weapon switch routine - ala Bard macros?

I'm not a coder (as my name implies) but I'm willing to try adapting something already written to suit my needs, if I had an inkling that what I wanted to do was possible.

User avatar
blueninja
a grimling bloodguard
a grimling bloodguard
Posts: 541
Joined: Thu Aug 28, 2003 7:03 am
Location: Göteborg, Sweden

Post by blueninja » Tue Oct 26, 2004 9:28 pm

Well yeah the swap thing could be pretty easily added. Thing is my guild died a while back so I haven't raided in a while so I haven't had much use for this macro myself, hence the lack of updates.

As for the announcement stuff, the tell thing I'm sure you could add yourself easily, just add it before the /cast line. I wouldn't recommend announcing anything to public channels though, once I forgot turning off the hail thing on a raid and it took less than a minute before someone sent me a tell about how fast I was clicking people.

.. hope this makes sense cause I just got home from the pub :) ..

Chill
Contributing Member
Contributing Member
Posts: 435
Joined: Fri May 07, 2004 5:06 pm
Location: Erie, PA

Post by Chill » Mon Jan 31, 2005 3:58 am

Anyone done any work on this macro lately?

I had some ideas for this macro a while back that I never got around to adding. First was to build an array of corpses before it starts clicking. My thought was that is you go down this list you could modify it as you go. The biggest way I saw the list changing was through a hail event:

Psuedocode:

Code: Select all

Sub Event_hail
   /if (${Hailer.Class.Equal[Cleric]}) {
      /move Hailer.Target last
   } else {
      /move Hailer.Target next
/return
Had a couple other thoughts but I cant remember em atm, and its all kinda moot if no one is workin on this anyway.

gohan4
a ghoul
a ghoul
Posts: 85
Joined: Sat Dec 18, 2004 11:36 am

Post by gohan4 » Fri Aug 19, 2005 10:03 pm

Here is a woking version.

Code: Select all

| Click.mac - v2.24b - Resurrect corpses around you - by blueninja 
| BETA RELEASE 
|        MQ2Data port of v2.22 
|    Resurrects all corpses within a radius using the cleric epic or spell 
|    Resurrects high priority classes first as defined in the ini file 
| 
|    Usage: 
|       /macro click [radius] [only] [con/nocon] [racecheck/noracecheck] [useblacklist/noblacklist] 
|                    [usespell/useepic] [sit/stand] [spellname] <nameofspell> [spellslot] <X> [face/noface] 
|                     [turn/noturn] [hail/nohail] [maxz] <Z> [prioritygroup0] [prioritygroup1] [prioritygroup2]... 
|       /macro click buildraces 
|           [radius]                   - set radius to look for corpses in, default value is in ini file 
|           [only]                     - stop ressing after corpses in priority groups have been ressed 
|           [con/nocon]                - toggle /consider to test if corpse is pc or npc 
|           [racecheck/noracecheck]    - toggle check for race to test if corpse is pc or npc 
|                                        (requires ini file) 
|           [useblacklist/noblacklist] - toggle check for blacklisted corpses 
|                                        (requires ini file) 
|           [usespell/useepic]         - use epic or spell to resurrect 
|           [sit/stand]                - toggle sitting while not casting to regen mana 
|           [spellname] <nameofspell>  - name of spell to use if using spell 
|           [spellslot] <X>            - spell gem number to use for spell if using spell 
|           [face/noface]              - face the corpse before ressing if it's out of view 
|           [turn/noturn]              - turn towards the corpse before ressing if it's out of view using GD's turn.inc 
|           [hail/nohail]              - hail the corpse before ressing it 
|           [maxz] <Z>                 - maximum distance to the corpse in the Z plane 
|           [prioritygroupX]           - names of groups to res before others 
| 
|           [buildraces]               - update list of player races in ini file 
| 
|    Example: 
|       /macro click 200 nocon dps healers 
|       /macro click 200 only cr 
|       /macro click usespell 150 only cr spellname resurrection spellslot 5 
|       /macro click buildraces 
| 
|    Make sure you set CLICKINIFILE a few lines down to a valid filename 

|    You probably want to run this with '/filter macros enhanced' to avoid targeting spam 

| Uncomment the following line if you want to use GD's turn.inc 
|#include turn.inc 

#turbo 

|Make sure you set this to point to a valid location 
#define CLICKINIFILE rez.ini
#define MEMDELAY 30 
#define DEFAULTSPELL Reviviscence 

#event interrupt "Your spell is interrupted." 
#event notequipped "You cannot use this item unless it is equipped." 
#event notmemmed "You do not seem to have that spell memorized." 
#event willdecay "This corpse will decay in#*#" 
#event restime "This corpse's resurrection time will expire in #*#" 
#event corpsetooold "This corpse is too old to be resurrected." 
#event clickedoldcorpse "This player cannot be resurrected. The corpse is too old." 
#event corpseexpire "This corpse is waiting to expire." 
#event couldnotrez "You were unable to restore the corpse to life, but you may have success with a later attempt." 
#event youmustbestanding "You must be standing to cast a spell." 
#event fizzle "Your spell fizzles!" 
#event oom "Insufficient Mana to cast this spell." 
#event skip "[MQ2] skip" 

Sub Event_restime 
|Got a message that the corpse has time left on rez timer 
   /varset ispccorpse 1 
/return 

Sub Event_willdecay 
|Since the rez time message comes before the expiry time message the 
|event should fire now 
   /doevents restime 

|Small pause to be sure 
   /if (${ispccorpse}!=1) { 
      /delay 5 
      /doevents restime 
   } 
   /varset gotconmsg 1 
/return 

Sub Event_corpsetooold 
|Corpse too old to rez, treat as npc corpse 
   /varset gotconmsg 0 
/return 

Sub Event_corpseexpire 
|Corpse about to poof, treat as npc corpse 
   /varset gotconmsg 0 
/return 

Sub Event_couldnotrez 
   /varcalc couldnot ${couldnot}+1 
/return 

Sub Event_clickedoldcorpse 
| Tried to resurrect a corpse that was too old 
| Remove from statistics 
   /varcalc clicked ${clicked}-1 
   /varset status 1 
/return 

Sub Event_skip 
   /varset status 1 
   /varcalc clicked ${clicked}-1 
   /call output 4 "Skipping.." 
/return 

Sub Event_interrupt 
   /if (${Me.State.Equal[DUCK]}) { 
      /varset status 1 
      /varcalc clicked ${clicked}-1 
      /call output 4 "Ducked, moving to next.." 
   } else { 
      /varset status 2 
   } 
/return 

Sub Event_fizzle 
   /varset status 2 
/return 

Sub Event_oom 
   /varset status 3 
/return 

Sub Event_notequipped 
   /beep 
   /call output 1 "ERROR: Epic is in an inventory slot, need to equip it in primary" 
   /endmacro 
/return 

Sub Event_notmemmed 
   /call sitdown 
   /if (${usespell}==0) { 
      /beep 
      /call output 1 "ERROR: Epic is bagged or banked" 
      /endmacro 
   } else { 
| Memorize spell 
      /call output 3 "Spell ${spellname} not memorized" 
      /memspell ${spellslot} "${spellname}" 
      /delay MEMDELAY 
| Check if it's memmed now, otherwise quit 
      /if (!${Me.Gem[${spellslot}].Name.Equal[${spellname}]}) { 
         /call output 1 "Couldn't memorize spell ${spellname}" 
         /endmacro 
      } 
      /varset status 4 

   } 
/return 

Sub Event_youmustbestanding 
   /varset status 2 
   /call output 4 "Standing up.." 
   /call standup 
/return 


Sub Main 
   /echo Mass rezzing inc, is your epic equiped?
   /declare status int outer 
   /declare ispccorpse bool outer 
   /declare gotconmsg bool outer 
   /declare spawncount int local 
   /declare radius int local 
   /declare contimer timer outer 
   /declare timeouts int outer 
   /declare corpses int outer 
   /declare skippedrace int outer 
   /declare skippedcon int outer 
   /declare skippedblacklist int outer 

   /declare clicked int outer 
   /declare couldnot int outer 
|prios(x,y) - contains name of profile in (x,1) and number of filters in profile in (x,2) 
   /declare prios[100,2] outer 
   /declare priocount int outer 
|priofilters(x,y) - y=0..prios(x,2) contains the values of the filters for prios(x,1) 
   /declare priofilters[100,100] outer 
   /declare only bool local 
   /declare argloop int local 
   /declare racecount int outer 
|List of valid player races 
   /declare races[100] outer 
   /declare curid int outer 

   /declare spellmaxrange int local 

   /declare noiselevel int outer 
   /declare contimeout int outer 
   /declare defaultradius int local 
   /declare showstats bool local 
   /declare useraces bool outer 
   /declare consider bool outer 
   /declare usespell bool outer 
   /declare spellname string outer 
   /declare spellslot int outer 
   /declare useblacklist bool outer 
   /declare sit bool outer 
   /declare hail bool outer      
   /declare face bool outer      
   /declare turn bool outer      
   /declare maxz int outer 
   /varset noiselevel 0 

|Read configuration 
   /call getconfig "noiselevel" 2 
   /varset noiselevel ${Macro.Return} 
   /call getconfig "contimeout" 50 
   /varset contimeout ${Macro.Return} 
   /call getconfig "defaultradius" 100 
   /varset defaultradius ${Macro.Return} 
   /call getconfig "showstats" 0 
   /varset showstats ${Bool[${Macro.Return}]} 
   /call getconfig "useraces" 0 
   /varset useraces ${Bool[${Macro.Return}]} 
   /call getconfig "consider" 1 
   /varset consider ${Bool[${Macro.Return}]} 
   /call getconfig "useblacklist" 0 
   /varset useblacklist ${Bool[${Macro.Return}]} 
   /call getconfig "usespell" 0 
   /varset usespell ${Bool[${Macro.Return}]} 
   /call getconfig "spellname" DEFAULTSPELL 
   /varset spellname ${Macro.Return} 
   /call getconfig "spellslot" 8 
   /varset spellslot ${Macro.Return} 
   /call getconfig "sit" 0 
   /varset sit ${Bool[${Macro.Return}]} 
   /call getconfig "hail" 0 
   /varset hail ${Bool[${Macro.Return}]} 
   /call getconfig "face" 0 
   /varset face ${Bool[${Macro.Return}]} 
   /call getconfig "turn" 0 
   /varset turn ${Bool[${Macro.Return}]} 
   /call getconfig "maxz" 0 
   /varset maxz ${Bool[${Macro.Return}]} 

   /varset corpses 0 
   /varset clicked 0 
   /varset skippedrace 0 
   /varset skippedcon 0 
   /varset timeouts 0 
   /varset skippedblacklist 0 
   /varset couldnot 0 
   /varset only 0 
   /varset radius ${defaultradius} 
   /varset priocount 0 
   /varset spellmaxrange 200 


|Check if parameter is passed 
   /for argloop 0 to 10    
   /if (${Defined[Param${argloop}]}) { 
      /call IsNumeric "${Param${argloop}}" 
      /if (${Macro.Return}==0) { 
|non-numeric argument 
         /if (${Param${argloop}.Equal[only]}) { 
            /varset only 1 
         } else /if (${Param${argloop}.Equal[buildraces]}) { 
            /call buildini 
            /endmacro 
         } else /if (${Param${argloop}.Equal[noracecheck]}) { 
            /varset useraces FALSE 
         } else /if (${Param${argloop}.Equal[racecheck]}) { 
            /varset useraces TRUE 
         } else /if (${Param${argloop}.Equal[noblacklist]}) { 
            /varset useblacklist FALSE 
         } else /if (${Param${argloop}.Equal[useblacklist]}) { 
            /varset useblacklist TRUE 
         } else /if (${Param${argloop}.Equal[usespell]}) { 
            /varset usespell TRUE 
         } else /if (${Param${argloop}.Equal[useepic]}) { 
            /varset usespell FALSE 
         } else /if (${Param${argloop}.Equal[sit]}) { 
            /varset sit TRUE 
         } else /if (${Param${argloop}.Equal[stand]}) { 
            /varset sit FALSE 
         } else /if (${Param${argloop}.Equal[nocon]}) { 
            /varset consider FALSE 
         } else /if (${Param${argloop}.Equal[con]}) { 
            /varset consider TRUE 
         } else /if (${Param${argloop}.Equal[hail]}) { 
            /varset hail TRUE 
         } else /if (${Param${argloop}.Equal[nohail]}) { 
            /varset hail FALSE 
         } else /if (${Param${argloop}.Equal[face]}) { 
            /varset face TRUE 
         } else /if (${Param${argloop}.Equal[noface]}) { 
            /varset face FALSE 
         } else /if (${Param${argloop}.Equal[turn]}) { 
            /varset turn TRUE 
         } else /if (${Param${argloop}.Equal[noturn]}) { 
            /varset turn FALSE 
         } else /if (${Param${argloop}.Equal[spellname]}) { 
            /varcalc argloop ${argloop}+1 
            /varset spellname "${Param${argloop}}" 
         } else /if (${Param${argloop}.Equal[spellslot]}) { 
            /varcalc argloop ${argloop}+1 
            /varset spellslot "${Param${argloop}}" 
         } else /if (${Param${argloop}.Equal[maxz]}) { 
            /varcalc argloop ${argloop}+1 
            /varset maxz ${Param${argloop}} 
         } else { 
            /varcalc priocount ${priocount}+1 
            /varset prios[${priocount},1] "${Param${argloop}}" 
            /call buildprios "${Param${argloop}}" ${priocount} 
            /varset prios[${priocount},2] ${Macro.Return} 
         } 
            
      } else { 
|Numeric argument, set radius 
         /varset radius ${Param${argloop}} 
      } 
   } else { 
|No argument passed, default 
      /goto :argsdone 
   } 
   /next argloop 

:argsdone 
   /call sitdown 
   /if (${usespell}==1) { 
      /varset spellmaxrange ${Spell[${spellname}].Range} 
   } 
   /if (${radius}>${spellmaxrange}) { 
      /call output 2 "WARNING: Radius larger than range of spell, setting to ${spellmaxrange}" 
      /varset radius ${spellmaxrange} 
   } 
|If we havent found any profiles on command line, set first one to default 
   /if (${priocount}==0) { 
      /varset priocount 1 
      /varset prios[${priocount},1] "default" 
      /call buildprios "default" ${priocount} 
      /varset prios[${priocount},2] ${Macro.Return} 

   } 

|Build an array of valid player races from ini file 
   /if (${useraces}==1) { 
      /varset racecount 0 

      :iniloop 
         /varcalc racecount ${racecount}+1 
      /varset races[${racecount}] ${Ini["CLICKINIFILE",races,${Int[${Math.Calc[${racecount}-1]}]}]} 
         /if (!${Bool[${races[${racecount}]}]}) /goto :leaveloop 
      /goto :iniloop 
   } 

:leaveloop 
|Read the blacklist after the normal profiles 
   /varcalc priocount ${priocount}+1 
   /varset prios[${priocount},1] "blacklist" 
   /call buildprios "blacklist" ${priocount} 
   /varset prios[${priocount},2] ${Macro.Return} 


|First loop through high priority classes.. 

   /varset curid 0 

   /for argloop 1 to ${Math.Calc[${priocount}-1]} 
    /varset spawncount 1 
      :loopprio 
         /doevents 
         /varset status 0 
         /varset curid ${Me.NearestSpawn[${spawncount},corpse range 1 70 radius ${radius}].ID} 
|If we didn't get a new target move on to low priority 
          /if (${curid}==NULL) /goto :clicktherest 
      /varcalc spawncount ${spawncount}+1 

|Check if this is a high priority corpse 
         /call HasPriority ${prios[${argloop},1]} ${curid} 
         /if (!${Macro.Return}==1) /goto :loopprio 
         /call clickit ${curid} 
|Gather statistics 
         /doevents couldnotrez 
      /goto :loopprio 

   :clicktherest 
   /next argloop 

|If parameter 'only' is specified don't move on to low priority corpses 
   /if (${only==1}) /goto :end 
   /varset curid 0 
  /varset spawncount 1 

|.. then everyone else 
   :restloop 
      /doevents 
      /varset status 0 
       /varset curid ${Me.NearestSpawn[${spawncount},corpse range 1 70 radius ${radius}].ID} 
|If we didn't get a new target quit 
      /if (${curid}==0) /goto :end 
    /varcalc spawncount ${spawncount}+1 

|Check that this isn't a priority corpse 
      /for argloop 1 to ${Math.Calc[${priocount}-1]} 
         /call HasPriority ${prios[${argloop},1]} ${curid} 
         /if (${Macro.Return}==1) /goto :restloop 
      /next argloop 
      /call clickit ${curid} 
|Gather statistics 
      /doevents couldnotrez 
   /goto :restloop 

:end 
|Gather statistics 
   /call sitdown 
   /doevents couldnotrez 
   /if (${showstats}==1) /call statsub 

/return 

Sub getconfig 
|Read the value of a [config] parameter in ini file 
|/call getconfig "paramname" "defaultvalue" 
|Returns value 
   /if (!${Defined[Param0]}) /return "false" 
   /if (!${Defined[Param1]}) /return "false" 

   /declare inivalue local 

   /varset inivalue ${Ini["CLICKINIFILE","config","${Param0}"]} 
|Not set in ini file, return default value 
   /if (${inivalue.Equal[NULL]}) /varset inivalue ${Param1} 
/return ${inivalue} 

Sub output 
|Echo that uses the noiselevel setting 
   /if (!${Defined[Param0]}) /return 
   /if (!${Defined[Param1]}) /return 
   /if (${Param0}<=${noiselevel}) { 
      /echo ${Param1} 
   } 
/return 

Sub IsNumeric 
|Returns 1 if Param0 is numeric, 0 if not 
   /if (!${Defined[Param0]}) /return 0 
   /if (${Param0.Length}==0) /return 0 
    /declare counter int local 
    /for counter 1 to ${Param0.Length} 
    /if (${0123456789.Find[${Param0.Mid[${counter},1]}}==NULL) /return 0 
   /next counter 
/return 1 

Sub statsub 
|Display stats 
   /declare runtime float local 
   /declare runstring local 
   /varset runtime ${Macro.RunTime} 
   /echo Corpses found: ${corpses}, ressed: ${clicked}, could not restore: ${couldnot} 
   /echo Skipped race: ${skippedrace}, Skipped con: ${skippedcon}, Skipped blacklist: ${skippedblacklist} 
   /varset runstring Time taken ${Int[${Math.Calc[${runtime}/60]}]} minutes ${Int[${Math.Calc[${runtime}%60]}]} seconds 
   /if (${timeouts}>0) /echo Consider timeouts: ${timeouts} 
   /if (${corpses}>0) /varset runstring ${runstring} (avg time per corse: ${Math.Calc[${runtime}/${corpses}]}s) 
   /echo ${runstring} 
/return 

Sub buildprios 
|Builds priority lists from ini file into priofilters(@Param1,y) 
|@Param0 is the name of the profile 
   /declare filters local 
    /declare temp local 
   /declare curfilt local 
   /declare pipeloc int local 
   /declare nextpipe int local 
   /declare filtercount int local 
   /if (!${Defined[Param0]}) /return 
   /varset filters ${Ini["CLICKINIFILE",${Param0}]} 
   /if (${filters.Equal[||]} || ${filters.Equal[NOTFOUND||]}) { 
      /if (${Param0.Equal[default]}) { 
||If default is empty, set defaults to bards and clerics 
         /varset priofilters[${Param1},1] cBard 
         /varset priofilters[${Param1},2] cCleric 
         /return 2 
      } else { 
         /return -1 
      } 
   } 

   /varset filters ${filters.Left[${Math.Calc[${filters.Length}-2]}]} 
   /varset filtercount 1 

   /varset nextpipe ${filters.Find[|]} 
   /if (${nextpipe}==NULL) { 
      /varset priofilters[${Param1},1] ${filters.Left[1]}${Ini["CLICKINIFILE",${Param0},${filters}]} 
      /goto :leavebuildprios 
   } 

:loopfilters 
   /varset curfilt ${filters.Mid[1,${Math.Calc[${nextpipe}-1]}]} 
   /varset filters ${filters.Right[${Math.Calc[${filters.Length}-${nextpipe}]}]} 
   /varset temp ${Ini["CLICKINIFILE",${Param0},${curfilt}]} 
   /varset priofilters[${Param1},${filtercount}] ${curfilt.Left[1]}${Ini["CLICKINIFILE",${Param0},${curfilt}]} 
   /varset nextpipe ${filters.Find[|]} 
   /varcalc filtercount ${filtercount}+1 

   /if (${nextpipe}==NULL) { 
      /varset priofilters[${Param1},${filtercount}] ${filters.Left[1]}${Ini["CLICKINIFILE",${Param0},${filters}]} 
      /goto :leavebuildprios 
   } 

   /goto :loopfilters 
    
:leavebuildprios 
   /varcalc filtercount ${filtercount}+1 

/return ${filtercount} 

Sub HasPriority 
|Test if corpse with id @Param1 has high priority in profile @Param0 
   /declare test local 
   /declare hpcount int local 
   /declare filtnum int local 
   /if (!${Defined[Param0]}) { 
      /varset test default 
   } else { 
      /varset test ${Param0} 
   } 
   /call findprioinarray ${test} 
   /if (${prios[${Macro.Return},2]}<=0) /return 0 

   /varset filtnum ${Macro.Return} 

   /for hpcount 1 to ${Math.Calc[${prios[${filtnum},2]}-1]} 
      /if (${priofilters[${filtnum},${hpcount}].Left[1].Equal[c]}) { 
      /if (${Spawn[${Param1}].Class.Name.Equal[${priofilters[${filtnum},${hpcount}].Right[${Math.Calc[${priofilters[${filtnum},${hpcount}].Length}-1]}]}]}) { 
            /return 1 
         } 
      } else /if (${priofilters[${filtnum},${hpcount}].Left[1].Equal[n]}) { 
         /if (${Spawn[${Param1}].Name.Find[${priofilters[${filtnum},${hpcount}].Right[${Math.Calc[${priofilters[${filtnum},${hpcount}].Length}-1]}]}]}!=NULL) { 
            /return 1 
         } 
      } else /if (${priofilters[${filtnum},${hpcount}].Left[1].Equal[g]}) { 
         /if (${Spawn[${Param1}].Guild.Find[${priofilters[${filtnum},${hpcount}].Right[${Math.Calc[${priofilters[${filtnum},${hpcount}].Length}-1]}]}]}!=NULL) { 
            /return 1 
         } 
      } 

   /next hpcount 
/return 0 

Sub findprioinarray 
|Search array for @Param0, returns the location or -1 if not found 
    
   /declare counter int local 
   /for counter 1 to ${priocount} 
      /if (${Param0.Equal[${prios[${counter},1]}]}) /return ${counter} 
   /next counter 
/return -1 

Sub IsPCRace 
|Test if race of corpse with id @Param0 is in the array of valid races 
   /declare loopcounter int local 
   /for loopcounter 0 to ${racecount} 
      /if (${Spawn[${Param0}].Race.Name.Equal[${races[${loopcounter}]}]}) /return 1 
   /next loopcounter 

/return 0 

Sub clickit 
   /declare counter local 
   /declare giveup local 
   /declare castwait local 
    
|Test Z distance 
   /if (${maxz}>0 && ${Spawn[${curid}].DistanceZ}>${maxz}) { 
      /call output 3 "Skipping ${Spawn[${curid}].Name} because Z distance too large" 
      /return 
   } 

|Test if valid race 
   /if (${useraces}) { 
      /call IsPCRace ${Param0} 
      /if (${Macro.Return}==0) { 
         /varcalc skippedrace ${skippedrace}+1 
         /return 
      } 
   } 


   /if (!${useblacklist}) /goto :skipblacklist 
|Check if corpse is blacklisted 
   /call HasPriority blacklist ${curid} 
   /if (${Macro.Return}==1) { 
      /call output 3 "Skipping blacklisted ${Spawn[${curid}].Name}" 
      /varcalc skippedblacklist ${skippedblacklist}+1 
      /return 
   } 

   :skipblacklist 

|Target corpse 
   /tar id ${curid} 
   /if (${Target.ID}==NULL) /return 

   /varcalc corpses ${corpses}+1 

|Doevents to get stats and flush /con messages 
   /doevents 

   /if (${consider}) { 
      /varset ispccorpse FALSE 
      /varset gotconmsg FALSE 

      /consider 

|Set timer so we don't get stuck waiting for a /con message 
      /varset contimer ${contimeout} 

   :waitcon 
      /doevents corpseexpire 
      /doevents willdecay 
      /delay 1 
|After a few seconds give up and assume it's a pc corpse 
      /if (${contimer}==0) { 
         /call output 3 "Timeout waiting for /con message, assuming pc corpse" 
         /varcalc timeouts ${timeouts}+1 
         /varset gotconmsg TRUE 
         /varset ispccorpse TRUE 
      } 
      /if (!${gotconmsg}) /goto :waitcon 

      /if (!${ispccorpse}) { 
         /varcalc skippedcon ${skippedcon}+1 
         /call output 3 "Skipping ${Target.Name}" 
         /return 
      } 
   } 
   /varcalc clicked ${clicked}+1 

|Counter to give up after 4 attempts at rezzing a corpse 
|that fail either by interrupt or if we can't cast for 3 seconds 
   /varset giveup 0 

:tryagain 
   /varcalc giveup ${giveup}+1 

|Too many tries, move on to next 
   /if (${giveup}>4) /return 

   /varset status 0 
   /varset castwait 0 

|Wait until we are not casting, if we wait too long, treat as an interrupt 
:waitforcast 
   /if (!${Bool[${Me.Casting}]}) /goto :castit 
   /delay 5 
   /varcalc castwait ${castwait}+1 
   /if (${castwait}>5) /goto :tryagain 
   /goto :waitforcast 

:castit 
   /if (${Math.Abs[${Math.Calc[${Target.HeadingTo.Degrees}-${Me.Heading.Degrees}]}]}>30) { 
      /if (${turn}) /call Turn 
      /if (${face}) { 
         /face 
         :waitforface 
         /if (${Math.Abs[${Math.Calc[${Target.HeadingTo.Degrees}-${Me.Heading.Degrees}]}]}>5) /goto :waitforface 
      } 
   } 
   /call output 3 "Resurrecting ${Target.Name}" 
   /if (${hail}) /keypress HAIL 
   /if (!${usespell}) { 
| Use epic 
      /call standup 
      /cast item "water sprinkler of nem ankh" 
   } else { 
| Use spell 

    /if (${Me.Gem[${spellname}]}==NULL) { 
         /call output 3 "Spell ${spellname} not memorized" 
         /memspell ${spellslot} "${spellname}" 
         /delay MEMDELAY 
| Check if it's memmed now, otherwise quit 
         /if (!${Me.Gem[${spellslot}].Name.Equal[${spellname}]}) { 
            /call output 1 "Couldn't memorize spell ${spellname}" 
            /endmacro 
         } 
    } 

      :waitspellrefresh 
| Check if enough mana, if not med until there is enough 
      /if (${Me.CurrentMana}<${Spell[${spellname}].Mana}) /call medup 

| Wait until spell is refreshed 
      /if (!${Me.SpellReady[${spellname}]}) { 
         /delay 1 
         /goto :waitspellrefresh 
      } 
      /call standup 

      /cast ${spellname} 
   } 

|Wait until not casting anymore 
:castdelay 
   /delay 5 
   /doevents 
   /if (${status}==4) { 
      /varcalc giveup ${giveup}-1 
      /goto :tryagain 
   } 
   /if (${status}==3) { 
      /varcalc giveup ${giveup}-1 
      /call medup 
      /goto :tryagain 
   } 
   /if (${status}==2) /goto :tryagain 
   /if (${status}==1) { 
      /call sitdown 
      /return 
   } 
   /if (${Bool[${Me.Casting}]}) /goto :castdelay 

   /call sitdown 

/return 

Sub medup 
   /call sitdown 
| Loop until enough mana to cast 
   :medloop 
      /delay 1s 
   /if (${Me.CurrentMana}<${Spell[${spellname}].Mana}) /goto :medloop 

   /call standup 
/return 

Sub sitdown 
| Don't bother sitting if full mana 
   /if (${Me.PctMana}==100) /return 
| Check sit setting 
   /if (!${sit}) /return 
| Don't try to sit if on a mount 
  /if (${Me.Mount.ID}!=NULL) /return 

| If not sitting, sit down 
   /if (!${Me.State.Equal[SIT]}) /sit 
/return 

Sub standup 
| Don't try to stand if on a mount 
   /if (${Me.Mount.ID}!=NULL) /return 
| If not standing, stand up 
   /if (!${Me.State.Equal[STAND]}) /stand 
/return 

Sub buildini 
|Loop through the players in zone and compare their race to the list of valid 
|player races in ini file and add new races 
   /declare currace local 
   /varset racecount 0 
|Read races from ini file 
      :buildiniloop 
         /varcalc racecount ${racecount}+1 
      /varset races[${racecount}] ${Ini["CLICKINIFILE",races,${Int[${Math.Calc[${racecount}-1]}]}]}
         /if (!${Bool[${races[${racecount}]}]}) /goto :buildleaveloop 
      /goto :buildiniloop 

:buildleaveloop 
   /declare added int local 

   /varset added 0 
   /declare pccount int local 

   /varset pccount 0 
   /declare counter local 
  /varset counter 1 

|Get first pc 
   /varset curid ${Me.ID} 
   /varset currace ${Spawn[${curid}].Race} 
:buildloop 

   /varcalc pccount ${pccount}+1 
|See if race is in array already 
   /call buildisinarray "${currace}" 
   /if (${Macro.Return}==0) { 
|New race, add to ini 
      /if (${Defined[currace]} && !${currace.Equal[NULL]}) { 
         /ini "CLICKINIFILE" races ${Int[${Math.Calc[${racecount}-1]}]} "${curra${ce}" 
         /varset races[${racecount}] ${currace} 
         /varcalc racecount ${racecount}+1 
         /varcalc added ${added}+1 
         /call output 2 "Added race: ${currace}" 
      } 
   } 
   /varset curid ${Me.NearestSpawn[${counter},pc].ID} 
   /varset currace "${Spawn[${curid}].Race}" 
   /varcalc counter ${counter}+1 
   /if (${curid}==NULL) /goto :buildend 

   /goto :buildloop 
:buildend 
   /varcalc racecount ${racecount-1} 
   /call output 2 "Players counted: ${pccount} Races in ini: ${racecount}, added ${added}" 

/return 

Sub buildisinarray(String Searchstring) 
|Search array for @Param0 
    
   /declare counter int local 
   /for counter 1 to ${racecount} 
      /if (${Searchstring.Equal[${races[${counter}]}]}) /return 1 
   /next counter 
/return 0