Several of the routines are either odd logic or missing conditions/badly structured.
Each sub should always reach a return statement. This isn't necessarily true for the last posted version of your script.
Some subs make some logic assumptions that do not seem to be accurate.
If your character is human, then you find yourself each time you call the find sub when at a new location. The #FIND___ variables are set on the finditem statement, but not modified with the ignoreitem statements.
Here is your routine:
sub find
repeat
finditem HS_IS_ G_4 ;find trick or treat targets
if #findid = #charid
ignoreitem #FINDID
if #findcnt = 0 ; Teleported or all targeted, go to next rune in book.
{
ignoreitem reset ; clear queue
set %RuneLocStart %RuneLocStart + 1 ; start at first rune
if %RuneLocStart > 16
set %RuneLocStart 1
set %RuneLocEnd %RuneLocStart
gosub TM_TravelFromNamedRunebook RE %RuneLocStart %RuneLocEnd %BookName
gosub find
}
if #findrep = 2_3_4_5_6
ignoreitem #FINDID
until #FINDcnt >= 1 && #findid <> #charid
RETURN
Consider this logic where it returns #true if we went to a new location and do not yet have an NPC, but #false (no error) if we succeeded in locating an NPC.
It has the advantage of not being recursive (calling itself which can be problematic if you are not careful... and there is a limit to how many subs deep you can be in Easyuo)
sub find
set %ReturnValue #false
FindEm:
finditem HS_IS G_4
if #findid = #charid || _ , #findrep , _ in _2_3_4_5_6_
{
ignoreitem #FINDID
goto FindEm
}
if #findcnt = 0
{
ignoreitem reset ; clear queue
set %RuneLocStart %RuneLocStart + 1 ; start at first rune
if %RuneLocStart > 16
set %RuneLocStart 1
set %RuneLocEnd %RuneLocStart
gosub TM_TravelFromNamedRunebook RE %RuneLocStart %RuneLocEnd %BookName
set %ReturnValue #true
goto FindEnd
}
FindEnd:
return %ReturnValue
That also requires a modification to the mainloop:
mainloop:
gosub find
if ! #Result
gosub trickortreat
goto mainloop
You keep using the same variable for your rune book location so you advance through your NPC runes, bank runes and trash runes. Make separate variables for each type of book.
Dumpit has a few issues.
You advance the end rune location by 3 above the starting rune slot, but do not have a validation that it is still <= 16. I would either set the bank to use random travel and set the range for the book with it's own variables (do not reuse var names here) or make more resilient logic using a single rune slot location. If you are really using 16 bank runes and you wish to pass rune locations that are 3 more than the starting one, then you need to adjust the value where you reset the value to 1. I am keeping your +3 slots but changing the test to make it valid by setting it to 13.
This will pass rune slot locations in pairs as:
1 - 4
2 - 5
3 - 6
...
12 - 15
13 - 16
and then cycle back to the beginning of the rune locations.
If your settings for %savecandy is not #true and %trashcandy is not #true, then the dumpit sub is badly formed. I would argue that you either save or trash the candy (you do not want your backpack filled to the brim), so a single variable using if/else works. I chose to show it with "%savecandy" is #true or #false. Change it to guarantee a return from the sub like this:
sub dumpit
set %BankRuneLocStart %BankRuneLocStart + 1
if %BankRuneLocStart > 13
set %BankRuneLocStart 1
set %BankRuneLocEnd %BankRuneLocStart + 3
gosub TM_TravelFromNamedRunebook RE %BankRuneLocStart %BankRuneLocEnd %Bankbook
wait 5
event macro 3 0 bank
wait 10
gosub specials
if %savecandy = #true
gosub unload
else
gosub trashcan
return
Trashcan eventually will hit #findcnt = 0 and fall out of the subroutine without a proper return. Take advantage of #FINDINDEX which allows you to advance through all items returned by FINDITEM. This will apply to the subroutines Unload and Specials. Here are the 3 routines rewritten:
sub trashcan
set %TrashRuneLocStart %TrashRuneLocStart + 1
if %TrashRuneLocStart > 6
set %TrashRuneLocStart 1
gosub TM_TravelFromNamedRunebook RE %TrashRuneLocStart %TrashRuneLocStart %Trashbook
wait 5
finditem %trash G_2
if #findcnt > 0
{
set %trashid #findid
finditem %loot c_ , #backpackid
if #findcnt > 0
{
for #findindex 1 #findcnt
{
exevent drag #findid #findstack
wait 10
exevent dropc %trashid
wait 20
}
}
}
return
;C_2 Subs --------------------
sub unload
finditem %loot c_ , #backpackid
if #findcnt > 0
{
for #findindex 1 #findcnt
{
exevent drag #findid #findstack
wait 10
exevent dropc %Candystash
wait 20
}
}
return
;--------------
sub specials
finditem %specialsonly c_ , #backpackid
if #findcnt > 0
{
for #findindex 1 #findcnt
{
exevent drag #findid #findstack
wait 10
exevent dropc %Specialstash
wait 20
}
}
return
Now you need some additional variable for the various rune setup in the beginning to make sure these have initialized variables:
Make sure there is a space before any comments ';' must be ' ;' or it isn't recognized as a comment... and in the case of %trashcandy it would have a value of "#false;change to false to bank candy" which is not what you want.
Set %BookName TrickOrTreat1 ; this could be changed as you reset to the beginning rune location in a book...
Set %Bankbook Bank
Set %Trashbook Trash
Set %savecandy #true ;change to true to bank candy
;Set %trashcandy #false ;change to false to bank candy.
set %RuneLocStart 0
set %BankRuneLocStart 1
set %TrashRuneLocStart 1
ignoreitem #charid
Especially cleaning up the exits from each sub to be clean will make this less buggy. Eliminating the recursion will help smooth out a really dreadful debug situation where you exceed the number of subs that can be 'gosub'ed. Both of these kinds of logic issues make scripts become unresponsive when it happens. Then reproducing it is nearly impossible and debugging EUO scripts when the program no longer responds is enough to make anyone insane. You can stare at the logic and never see why it is flaking out because it is really about what happened in other places. So the short of it is be kind to yourself and:
1) Always make clean subroutines.
2) Ideally code it for a single exit/return.
3) Only code recursion as a last resort - find another logic structure to make your scripting life simpler.
The blocks after FINDITEM where I demonstrate "if #findcnt > 0" and "for #findindex 1 #findcnt" lets you create much simpler code. That for loop using #findindex is all the magic. That is the "top shelf" stuff that you want in your scripting.
Finally, here is my hacked version of TM's Travel routines where I fixed my block rune issue:
; This is a snippet showing how I used Recall2Destination
set %tries 0
try_library_again_buyer:
set %tries %tries + 1
gosub Recall2Destination %RunebookLibrary %LibraryRune1 %LibraryRune2
if #result = error
{
if %tries < 10
goto try_library_again_buyer
else
{
pause 15s
set %tries 0
goto try_library_again_buyer
}
}
; This is the end of that snippet.
; It would attempt to recall to a blocked location 10 times and then wait for 15 seconds and reset the 10 attempts...
sub Recall2Destination
set %Runebook %1
set %RuneNumber1 %2
set %RuneNumber2 %3
set %ReturnVal OK
set %charposxy #charposx , _ , #charposy
set %saveLPC #LPC
set #LPC 2000
set %Status Recalling
gosub Menu_Refresh
gosub TM_TravelFromRunebook %TravelMethod %RuneNumber1 %RuneNumber2 %RuneBook
set #LPC %saveLPC
if #result = #false
{
set %RecallTimer #SCNT2 + 100 ; 10 second wait... means failure to arrive
repeat
wait 1
until %charposxy <> #charposx , _ , #charposy || %RecallTimer < #SCNT2
if %RecallTimer < #SCNT2
set %ReturnVal error
}
else
set %ReturnVal error
return %ReturnVal
Here are the routines I revised in TM's travel subs. I consider it a hack with no promises other than it did solve my blocked rune issues.
;-------------------------------------------------------------------------------
; %1 = spell number
; %2 = #TARGETID or SELF or NONE
; %3 = retry count (-1 = cast until successful)
; %4 = cast delay
; %5 = recovery delay
sub TM_NewCastSpell
namespace push
namespace local NCS
set !lpc #LPC
set #LPC 100
set !whichspell %1
set !whichtarget %2
set !castretrymax %3
set !waitdelay %4
set !recovery_delay %5
set !castretry 0
set !temp_ltargetid #LTARGETID
set !temp_ltargetkind #LTARGETKIND
NewCastSpell_loop1:
if !castretrymax < 0
goto NewCastSpell_cont1
if !castretry > !castretrymax
goto NewCastSpell_end1
NewCastSpell_cont1:
gosub TM_AdvJournalSync SPELLCAST
set #LTARGETKIND 1
set #LTARGETID !whichtarget
set !tempmana #MANA
event macro 15 !whichspell ; cast the spell
wait !waitdelay
set !targettimeout #SCNT + 7
NewCastSpell_wait1:
gosub TM_AdvJournalScan SPELLCAST VALID you_have_not_yet mana your_spirit more_reagents
if #RESULT = #TRUE || #SCNT > !targettimeout
{
set !casttimeout #SCNT2 + !recovery_delay
repeat
until #SCNT2 > !casttimeout ; finish up cast delay
set !castretry !castretry + 1
goto NewCastSpell_loop1
}
if !whichtarget = NONE
goto NewCastSpell_skip1
if #TARGCURS = 1
goto NewCastSpell_targ1
goto NewCastSpell_wait1 ; wait for target cursor
NewCastSpell_targ1:
if !whichtarget = SELF
event macro 23
else
event macro 22
NewCastSpell_skip1:
wait 5
set !casttimeout #SCNT2 + !recovery_delay
NewCastSpell_skip2:
if !whichspell >= 0 && !whichspell <= 63 ; Magery
{
gosub TM_AdvJournalScan SPELLCAST VALID spell_fizzles there_is_already mana your_spirit more_reagents
}
else
{
set !cont #FALSE ; Chivalry, Necromancy, etc
finditem !whichtarget *
if !whichtarget in SELF_NONE || #FINDKIND <> -1
set !cont #TRUE
if #MANA >= !tempmana && !cont = #TRUE ; check if target is still there
set #RESULT #TRUE
else
set #RESULT #FALSE
}
repeat
until #SCNT2 > !casttimeout ; finish up cast delay
if #RESULT = #TRUE
{
if !castretrymax > -1
{
set !castretry !castretry + 1 ; %castretrymax of -1 will cast until successful
if !castretry > !castretrymax
goto NewCastSpell_end1
}
goto NewCastSpell_loop1
}
if #SCNT2 <= !casttimeout ; finish up cast delay
goto NewCastSpell_skip2
NewCastSpell_end1:
set #LTARGETID !temp_ltargetid
set #LTARGETKIND !temp_ltargetkind
set #LPC !lpc
namespace pop
return
;-------------------------------------------------------------------------------
; %1 = Method (RE, GA, SJ)
; %2 = index location within runebook (1-16)
; %3 = index location within runebook (1-16), try up to this point
; %4 = runebook item id
; returns #TRUE if error, #FALSE for no error
sub TM_TravelFromRunebook
namespace push
namespace local RFR
set #LTARGETKIND 1
set !method %1
set !locindex %2
set !locindexend %3
set !rbook %4
finditem !rbook C_ , #BACKPACKID
if !method notin RE_GA_SJ || #FINDKIND = -1
{
namespace pop
return #TRUE
}
if !locindex notin 1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16
{
namespace pop
return #TRUE
}
if !locindexend notin 1_2_3_4_5_6_7_8_9_10_11_12_13_14_15_16
{
namespace pop
return #TRUE
}
TravelFromRunebook_loop1:
set #LOBJECTID !rbook
set #LTARGETKIND 1
event macro 17 0
gosub GumpWait generic_gump generic_gump
set !runeclickx 140 ; page 1, rune 1
set !runeclickx ( #CONTPOSX + !runeclickx + ( 35 * ( ( !locindex - 1 ) / 2 ) ) )
if !locindex > 8
{
set !runeclickx 310 ; page 2, rune 1
set !runeclickx ( #CONTPOSX + !runeclickx + ( 35 * ( ( !locindex - 9 ) / 2 ) ) )
}
set !runeclicky #CONTPOSY + 196
click !runeclickx !runeclicky
wait 5
set !runeclicky #CONTPOSY + 24
set !runeclickx #CONTPOSX + 164 ; page 1 set to default
if !locindex % 2 = 0
{
set !runeclickx #CONTPOSX + 305 ; page 2 set to default
}
click !runeclickx !runeclicky
wait 5
set !oldx #CHARPOSX
set !oldy #CHARPOSY
if !method = RE
gosub TM_NewCastSpell 31 !rbook 1 10 10 ; recall until successful
if !method = GA
{
gosub TM_NewCastSpell 51 !rbook 1 10 20 ; gate until successful
set !temp_cnt #SCNT + 10
repeat
finditem KEF_OTF_JEF G_0
until #FINDKIND <> -1 || #SCNT > !temp_cnt
if #FINDKIND <> -1
{
set #LOBJECTID #FINDID
wait 10
event macro 17 0
wait 20
if #CONTNAME = generic_gump && #CONTSIZE = 420_280
{
gosub TM_AdvJournalSync SPELLCAST
set !clickx #CONTPOSX + 26
set !clicky #CONTPOSY + 261
click !clickx !clicky ; click ok
}
}
}
if !method = SJ
gosub TM_NewCastSpell 210 !rbook 1 10 30 ; sacred journey until successful
wait 30
set !tempscnt #SCNT + 10
WaitforTravel_loop1:
gosub TM_AdvJournalScan SPELLCAST VALID location_is_blocked something_is_blocking you_spirit_lacks
if #RESULT = #TRUE
{
gosub TM_AdvJournalSync SPELLCAST
set !locindex !locindex + 1
if !locindex > !locindexend
{
namespace pop
return #TRUE
}
goto TravelFromRunebook_loop1
}
if ( ( #CHARPOSX = !oldx && #CHARPOSY = !oldy ) && #SCNT < !tempscnt )
goto WaitforTravel_loop1
if #CONTNAME = generic_gump && #CONTSIZE = 452_236 ; RunUO close runebook
{
set !clickx #CONTPOSX + 120
set !clicky #CONTPOSY + 60
click !clickx !clicky mc r
wait 5
}
namespace pop
; click 401 254 n
return #FALSE
;------------------------------------------------
My blocked recall rune is not a copy/paste solution for you, but if you can follow the logic it demonstrates a potential solution.
Oracle and I have run this code probably 30,000 times or more. I am pretty sure he can also vouch that it functions as it came from my library donation script that he helped me debug.
Gaderian