AutoIt for scripting

Posted by Atreju on Tue 22 Apr 2008 06:33 PM — 28 posts, 120,023 views.

#0
Hello, everybody.

As many other people playing muds I have recently come to the conclusion that scripting can make your virtual life much easier, esp. that concerns leveling, where a lot of routine work has to be done. Getting to the top level for the first time was quite a lot of fun, but killing the same monsters and wandering around the same rooms all over again just for the sake of getting a top level character of another class or without all these mistakes in stats or skills distribution that new players inevitably make, looked a bit boring. So I set myself to write that easy script, that just walks along the set rout, kills the set monsters and rests when the hp check says so.

As Lua was highly recommended at this site, I first started writing the script in this language, but I was disappointed to find few or scant manuals on Lua. Actually, I'm very far from programming in my real life, I graduated from a language department a few years ago. So to understand the logic and structure of a scripting language I need quite a comprehensive help. Anyway, when I spent half a day trying to figure out how the "if" argument works in Lua, I thought it would be a better idea to stick to AutoIt, another scripting language, with the help of which I have already made some programs of different type. The only problem with it was that it couldn't read lines directly from any client.

So what I’ve done, was a little function in Lua, that puts the demanded line into the clipboard:

function copy (x)
x=x-1
total_lines = GetLinesInBufferCount ()
SetClipboard(GetLineInfo (total_lines - x, 1))
end -- copy

AutoIt sends commands to the client through direct input to the command line, that is, it imitates a user typing. By sending the "/copy()" command it gets data from the game through the clipboard, processes it and sends commands back through the command line. I know how awful that must look like, but it works :)

Still, another method would evidently be much more welcome, since this clipboard/direct input interface appears somewhat abnormal, plus you can't use the computer while the script is working - for it to be able to type things into the client, the latter has to be active all the time, not in the background.

So I would appreciate any suggestions on how to get the external script and the client work together through a more convenient way then the one I have invented.

And thanks for reading the many letters above :)
#1
Honestly Not to be mean But i think it would be better to try to stick with Lua. I just started learning it and it gets easier as time goes on. Im actually working on a auto hunter and i've got it working somewhat and I just started learning lua today.

Only thing i havent got set up is for it to follow a speedwalk and pause that speedwalk anytime it finds a mob then resume it after its dead.. As of right now It randomly chooses a direction and goes.


But i just found a script that I can use for it. And as far as everything else most of it was just using triggers and timers.. I only used a few if statements and the rest of the scripting I did was just turning off and on triggers..
Amended on Tue 22 Apr 2008 07:08 PM by Toruk
Australia Forum Administrator #2
First, the book by the author of Lua is very good. This is the online version:

http://www.lua.org/pil/

You can order the hard-copy from Amazon or a similar place, if you prefer to have a book by your side.

Quote:

Anyway, when I spent half a day trying to figure out how the "if" argument works in Lua, I thought it would be a better idea to stick to AutoIt


The "if" statement works really simply. I suggest you post what you are trying if you can't make it work. You are best off using Lua rather than trying to do things with AutoIt because AutoIt won't have access to all the script functions built into the client.

As a simple example of an "if":


if health < 100 then
  print ("Oh no! health is low")
else
  print ("health is good")
end


Check out some of the many script examples of Lua on this site, it is rare to find one which does not use "if".

As for your planned bot, assuming the MUD doesn't mind that, I will give a small example in Lua of how you might get started.
Australia Forum Administrator #3
OK this might help get you started. Writing an intelligent fighting bot is not a trivial task. This might be a good start. The first trick would be to move around a set route. I am doing this below with a timer that fires every two seconds, and simply walks a predefined path. You would need to start the timer at the starting point, and make sure that the path "loops", so that when it ends, you are back at the starting point.


<timers>
  <timer 
   enabled="y" 
   second="2.00" 
   offset_second="0.00"    
   send_to="12"
>
  <send>

route = {
  "s", "s", "e", "n",  -- to butcher
  "s", "w", "n", "n",  -- back again
  }

-- stay here if we are fighting or recovering

if fighting or resting then
  return
end  -- if


loc = (loc or 0) + 1  -- next way to walk

-- if past end, go back to start

if loc &gt; #route then
  loc = 1
end -- if

Send (route [loc])
</send>

  </timer>
</timers>


In that timer I have an "if" check, that if we are fighting or resting, it doesn't move you. That lets you stay in one place while you are fighting or resting.

Now you need to know when you are fighting, so you would need some sort of trigger (eg. "A * is in the room with you") that starts a fight, and another trigger that tells you when the fight is over (eg. "The * is dead!").

The trigger that starts the fight would have to make the fighting variable true, eg.


fighting = true


And when the fight is over you need to make it false again, eg.


fighting = false


That way your movement timer knows not to move you.

You would do a similar thing to rest. eg.


if hp < 150 and not resting then
  Send ("rest")
  resting = true
end -- if


A smarter system might actually work out where to go by looking at the exits. There have been a few posts about this already, search for "wilderness walker" or such thing. I think a few people have already written systems that walk around, taking whatever exits they see, fight anything they see, and just keep going.
#4
Thanks a lot for the hints. I actually managed to create an automatic system that I wanted, and it is much more functional then before. This approach of making a complex of triggers, scripts and timers instead of a single exe was new to me, in AutoIt i had something more like a hardcode, with linear action. Now it's something much more flexible and efficient. Thanks again, for the help :)
#5
Not sure if You know this but if you have a lot of triggers and timers and such Mushclient has an auto plug in wizard which will make all your triggers and scripts into a plug in you can use and it will clear up your triggers..
#6
I'm a bit confused at this actually. I know this timer takes you through the area. So do you have to make triggers for when a creature is in the room? You mentioned a trigger setting the value fighting = true so would that be the trigger that when something is in the room that your timer moves you to it sends 'kill *' to the screen? (* being a random mob name) Also I'd like to know if there is any exception or something you would have to add if the mobs move rooms. I see this script is good and all for mobs that stay where they are at. But I want to know what you'd have to do if they move.
Australia Forum Administrator #7
You basically have to replicate your thought processes. So you move around by looking for exits, and taking an exit if you aren't fighting.

Once entering a room you look for mobs. If they are the right type (eg. level) you might choose one to fight. If the mob leaves the room (the mob you are fighting) you might follow it.

For example, if you know you are fighting the kobold, and a trigger sees:


The kobold flees west!


Then that might be your cue to go west to follow it.
#8
I've been searching the forums for hours, and I'm still quite confused on how to begin writing a script like the one above. I don't expect to be an expert scripter over night, but I don't know where to begin exactly. I have very limited experience with with Java and Ruby, so I know some basics, like what methods, variables and if/else loops are.

Most of Nick's timer method above makes sense to me, but I don't know what language it's written in, and I don't know how I would run the script through my mud client.

I plan to make bot such as this--one that wanders around killing things for me--but I don't have much knowledge of any scripting languages. I'm trying to learn lua, since most people seem to suggest it. I was wondering if there are any more excellent examples such as this, or finished scripts that I can draw intelligent solutions from.

I can write out what the script should do logically (in human english :) ) to navigate the mud and kill things, but I have no idea what lua commands I will need to accomplish these things, or even how to run a script through the mud client. (I imagine it's something like' /run myBot.lua' )

Would anyone have any advice, examples, or lua guides (hopefully as specific to making a mud bot as possible) to offer?

Thanks for the great examples on this post and many others. ;)
USA #9
Eloni said:
Most of Nick's timer method above makes sense to me, but I don't know what language it's written in, and I don't know how I would run the script through my mud client.

The language is Lua, within an XML wrapper representing the timer object. You can edit timer objects directly from the GUI (Game -> Configure -> Timers, click Add), but the XML format is used for (a) plugins and (b) giving it to other people. To import a timer like that, just copy everything (from <timers> to </timers>, inclusive) and click the Load button on the Timers dialog.

Eloni said:
I plan to make bot such as this--one that wanders around killing things for me--but I don't have much knowledge of any scripting languages. I'm trying to learn lua, since most people seem to suggest it. I was wondering if there are any more excellent examples such as this, or finished scripts that I can draw intelligent solutions from.

Make sure your MUD allows bots first. ;)

If you want to learn Lua, I recommend reading Programming in Lua [1]. Don't worry if you don't understand some of the more advanced material like metatables or coroutines, but it's a very good way to understand the language in general. You can also run arbitrary scripts within MUSHclient using the Immediate window (Ctrl+I or Game -> Immediate).

MUSHclient's scripting can be a little obscure sometimes, but that's what we (and the documentation) are here for. Play around with some smaller scripts, including using triggers/timers/aliases, so you understand how they work.

Eloni said:
Would anyone have any advice, examples, or lua guides (hopefully as specific to making a mud bot as possible) to offer?

Apart from the above, I would recommend building one piece at a time. For example, if you want a bot that "walks around and kill things", several things spring to mind that you may want to think about:

1. "Walking around": How will the bot know how to get from point A to point B? How will it know what point B is? Automappers have been done; the degree of difficulty depends on how hard it is to get a unique identifier for a room and its exits, and then creating a graph of rooms internally. ([EDIT]: Unless you just want to wander around at random like someone mentioned above.) Once you know your path, you need to handle any obstacles that come your way: doors, swimming, and anything else that can hinder movement.

2. "Kill things": How will it know what to kill? How will it kill? Should it pick up any loot that drops? Beside the obvious healing/mana/whatever keepup, if your MUD has any other afflictions, you'll want to handle those somehow. Combat systems have been done, but (at least on the MUD I play, which BTW doesn't allow botting) they're complex and varied machines. Your mileage may vary.

I don't mean to discourage you. I just want to bring your attention to these issues so you know about them ahead of time. Like I said, both parts above are possible and have been done in some form.

Best of luck!

[1] http://www.lua.org/pil/
Amended on Thu 02 Dec 2010 09:04 AM by Twisol
#10
Thanks for taking the time to type that really informative reply! It's helping a lot :]

I was in the process of writing some pseudo code for my bot, but now I'm looking at the book you linked and I'll see if I can't turn it in to LUA code.
#11
Thanks. The book was a tremendous help, and I bookmarked it for later reference.

Here's what I got so far. I haven't made the triggers that change the global variables and I've already encountered a problem. I'm not sure how to implement the wait function.



--- Coded by Eloni --!>
---AlmostLua v1.0 --!>

require "wait"

--- VARIABLES ---!>

ready = false  --(determined by health/mana/movement trigger, how to link the trigger to this script?)
fighting = false --(determined by multiple triggers that all indicate you're in combat)
currentPath = "none"
currentPathLocation = 1

recallWasSucessful = true --(determined by the string that displays when the spell is sucessful)


--- END VARIABLES ---!>


---  ARRAYS ---!>

--- paths ---!>
gardenPath = { "n", "w", "n", "down", "open door", "e" }
cityPath = { "w", "w", "w", "w", "n", "n" }
dungeonPath = { "s", "s", "open door", "down", "down", "s" }

listOfPaths = { gardenPath, cityPath, dungeonPath, }

--- end paths ---!>


basicCombat = { "kick", "b_bs", "cast fireball", "cast slow" }

---  END ARRAYS ---!>


---  METHODS ---!>


function pickRandomPath ()  --Picks a random area to go to when the method is called and sets it to currentPath
    currentPath = listOfPaths[rand(3)]
    print ("Current destination: " + currentPath)
end -- pickRandomPath

function moveOnPath ()
    send "currentPath[currentPathLocation]"
    currentPathLocation = currentPathLocation + 1
end -- moveOnPath

--Selects a random attack from the array when the method is called
function basicCombatEngine ()  
   -- (commented this out because I don't know the random function for lua)
   send "cry" -- basicCombat[rand(4)] 
end -- combatEngine

--Makes you sleep when the loop deems it necessary, 
--and resets the path to "none" so you're not walking around while you sleep
function regen () 
    currentPath = "none"
    Send ("sleep")
    require "wait"
    
--wait function doesn't fire at all (instintaneously prints both "sleep" and "wake"
--pretty sure it's implemented wrong
    
    wait.make (function ()   
      wait.time(180)
    end) 

    Send ("wake")
    ready = true
  
end -- regen

function sleepTimer()  --tried using this in regen(), didn't work
    wait.make (function ()
      wait.time(180)
    end)
end

---large if/else loop that determines your actions based on ready == true, and other variables ---!>

--somehow set it on a timer that's short enough not to get my character killed, and long enough so that 
--it can attack mobs it sees in the room (with a trigger)?

--will the "wait" function PAUSE THE ENTIRE SCRIPT and 
--keep the timer from checking to see if I need to escape from combat?

--ready = false(low hp and mana) and fighting = true, 
--so need to get out of combat quick & reset the bot

if ready == false and fighting == true and recallWasSucessful == false then 

  send "flee"
  send "cast recall"
  
--need to regen, not fighting, so: go back to start & regen
  
elseif ready == false and fighting == false and recallWasSucessful == true then 

--also sets currentPath to none, so the loop can continue after the "wait" is over
  regen()  

--if hp and mana are up, not fighting, and not on a path  
elseif ready == true and fighting == false and currentPath == "none" then 

  pickRandomPath()
  currentPathLocation = 1

--if hp and mana are up, not fighting, and ON A PATH  
elseif ready == true and fighting == false and currentPath ~= "none" then 

  moveOnPath()
  
elseif ready == true and fighting == true then

  basicCombatEngine()

end

  
---  END METHODS ---!>






The code compiles error free, but there is no pause between sleeping and resting.

I think a wait function is ideal in the regen() function, but I can't tell until I test it. I might be wrong, but: It bothers me that the bot could get attacked while sleeping, and it wouldn't be able to respond because the 'wait' function has paused the script.

Maybe a timer that runs separately from the 'waited' code that constantly checks to see if fighting = true, and if so 'unwaits' the code... somehow. I haven't experimented with timers yet, so I'm not sure how they work, but I assume they're ran from the mud, and not from inside the lua script.

Are there help files for the wait function? All I have are examples from this post:
http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=4956&page=1
Amended on Thu 02 Dec 2010 08:12 PM by Nick Gammon
Australia Forum Administrator #12
I reformatted your code a bit - the very long lines made it a bit unreadable on the forum.

First point:

You don't need to do this multiple times:


require "wait"


What "require" does is load a module (wait) if it isn't already loaded. Thus you only ever need to do it once.

Second, this won't work, as you noticed:


 Send ("sleep")
    require "wait"
    
--wait function doesn't fire at all (instintaneously prints both "sleep" and "wake"
--pretty sure it's implemented wrong
    
    wait.make (function ()   
      wait.time(180)
    end) 

    Send ("wake")
    ready = true


It prints "sleep", and then makes a coroutine which starts a timer which fires in 180 seconds. Anything after wait.time(180) will be delayed. However that is inside the scope of wait.make. Your subsequent line of "wake" is not inside that scope.

In other words:


    require "wait"
   
    wait.make (function ()   
      Send ("sleep")
      wait.time(180)
      Send ("wake")  --> after 180 seconds
    end) 



The client can't actually sleep. What it can do is defer the things inside the wait.make blocks for later.

Eloni said:

I think a wait function is ideal in the regen() function, but I can't tell until I test it. I might be wrong, but: It bothers me that the bot could get attacked while sleeping, and it wouldn't be able to respond because the 'wait' function has paused the script.


Certainly you can make all this work. In fact one large coroutine might be able to run indefinitely. Check out this thread as well:

http://www.gammon.com.au/forum/?id=4957

That describes how you can wait for something from the MUD, or a time to elapse, whichever comes sooner. So this is the ideal way of checking if you are attacked.

Something like this:


require "wait"
   
wait.make (function ()   
 
  Send ("sleep")
  mobname = wait.match ("* attacks you!", 10)

  -- if mobname, trigger matched
  if mobname then
    Send ("stand")
    Send ("cast fireball " .. mobname")
  else
  -- otherwise timed out
    Send ("cure light wounds")
  end

end) 


In this case the wait.match function returns either when the trigger matches, or some time has elapsed. This lets you react to an attack, or wait for some time.
Amended on Thu 02 Dec 2010 08:24 PM by Nick Gammon
USA #13
Eloni said:
The code compiles error free, but there is no pause between sleeping and resting.

I'm splitting hairs here, but Lua isn't a compiled language. The Lua interpreter parses the script and turns it into bytecode for its own virtual machine, which it then executes. But it's never actually compiled into machine code.

Eloni said:
I think a wait function is ideal in the regen() function, but I can't tell until I test it. I might be wrong, but: It bothers me that the bot could get attacked while sleeping, and it wouldn't be able to respond because the 'wait' function has paused the script.

Maybe a timer that runs separately from the 'waited' code that constantly checks to see if fighting = true, and if so 'unwaits' the code... somehow. I haven't experimented with timers yet, so I'm not sure how they work, but I assume they're ran from the mud, and not from inside the lua script.

Nick ninja'd me, but the reason your waiting doesn't work is because wait.make creates a coroutine and runs it. When you call wait.time() inside it, it yields that coroutine back to the original context (which would be within wait.make, which returns at that point). wait.time() creates a temporary timer within MUSHclient, and the timer's script resumes the waiting coroutine.

Also keep in mind that MUSHclient is not multithreaded. When control is within a script, nothing else happens (including triggers, aliases, timers, etc.) until the script returns back. Coroutines let you save your context within a multi-step task. In this case - as Nick showed - your first (and only) task is to wait some time and then wake up. But anything outside the wait.make() block will be executed as soon as your coroutine yields control, which you seem to send "wake" immediately.

Coroutines are really fun and highly useful, they just take some extra thought and practice to build up that essential mental model.
Australia Forum Administrator #14
Twisol said:

I'm splitting hairs here, but Lua isn't a compiled language. The Lua interpreter parses the script and turns it into bytecode for its own virtual machine, which it then executes. But it's never actually compiled into machine code.


I don't think compiling strictly means "to output machine code". In fact even gcc turns C source into intermediate code, which another step then turns into machine code.

From this page:

http://www.lua.org/pil/24.1.html

PiL said:

For each line the user enters, the program first calls luaL_loadbuffer to compile the code.


(my emphasis).

Anyway, I think we know what he means. The code "compiled" in that it didn't have syntax errors, but the runtime behaviour was not as expected.
#15
I think I understand the bit about the scope of the function. I probably understand enough to implement it.

As for the link:

http://www.gammon.com.au/forum/?id=4957

I think this is the same link I linked as my source. I wouldn't be able to make the wait function work without Nick's specific example (that he included on this thread)--because I have little idea what the code on the other thread means, and the vocabulary used to describe it was beyond my understanding.

Not to say I won't be try to google definitions, read the chapter on coroutines again and try my best to experiment with the code from the other thread.

Thanks for the all the help guys ;)
#16
Nick said that perhaps a coroutine could work in my case, but I couldn't really make sense of them based on what I read. I understand that I can yield the coroutine, but I don't know of any timers I can use to make the coroutine resume after a set period.

Speaking of timers, I attempted to use LuaAddTimer to create the same timer I made using the MushClient GUI, but I received an error message. It seemed the large multiline piece of code was the problem. The timer looked something like this:


LuaAddTimer {   name = "mytimer",
                minute = 5,
                second = 3,
                send "A GIGANTIC
		MULTI LINE
		PIECE OF CODE"
              }


I'd like to know how to make timers in my lua document so that I can use LUA to recreate them with new (random) times after every execution. So the send would look something like:


	send "  doSomething
		doSomethingElse
		timerTime = math.random(5)
		LuaAddTimer {   name="mytimer"
				minute = 0
				second = timerTime
				--etc
				}


Here's my confused attempt at making a coroutine accomplish what I want.



        --coroutine made to yield
	function newRegen()
	
	--a trigger makes fighting = true if it maches something from combat
	
		if fighting == true then 
		
			coroutine.resume(theCoroutineThatChecksIfINeedToSleepOrFight)
		
		else
		
			function timer(10)
				coroutine.resume(theCoroutineThatChecksIfINeedToSleepOrFight) 
				--somehow make a timer that makes above fire after 10 seconds
				--AKA a timer function is something I don't know how to create :(
			end
			
		
		end
	end




Here's another attempt at making it work with 'wait'.



	wait.make (function ()
	
		Send ("sleep")
		ifCombatStarts = wait.match (fighting, 10)
		
		--I don't know if 10 is part of a 'sendto'
		--'12' sends to script
		--or if 10 stands for 10 seconds?  <<this might be a stretch
		
		if ifCombatStarts == true then
			
			print ("IFCOMBATSTARTS = TRUE, REACTING NOW BY STANDING")
			Send ("stand")

		else
			
			wait.time (10) --assuming I was right about the above
			print("Time Has Elapsed, TIME TO WAKE UP")
			Send ("wake")
			
		end
		
	end)



Also I didn't expect my code to work anyway so I never tested it. However when I tried to compile something earlier I received this message. Don't know if it's something I need to worry about:



Run-time error
World: Alter Aeon
Immediate execution
C:\Program Files\MUSHclient\lua\wait.lua:159: Timers not enabled
stack traceback:
        [C]: in function 'assert'
        C:\Program Files\MUSHclient\lua\wait.lua:159: in function 'make'
        [string "Script file"]:100: in main chunk

Amended on Sat 04 Dec 2010 06:38 PM by Eloni
#17
I just realized my attempt at a coroutine makes even less sense now since the 'if check' for 'fighting' would only be called after the coroutine that checks if I was fighting has yielded.

:'( I'll be spending more time trying to understand this.
USA #18
Eloni said:
Nick said that perhaps a coroutine could work in my case, but I couldn't really make sense of them based on what I read. I understand that I can yield the coroutine, but I don't know of any timers I can use to make the coroutine resume after a set period.

Go into the Timers menu (Game -> Configure -> Timers) and click Add. You can configure a timer from there. Of course, for transient timers like what you'd use for a coroutine, creating a timer from your script is probably better (like you tried below).

Eloni said:
Speaking of timers, I attempted to use LuaAddTimer to create the same timer I made using the MushClient GUI, but I received an error message. It seemed the large multiline piece of code was the problem. The timer looked something like this:


LuaAddTimer {   name = "mytimer",
                minute = 5,
                second = 3,
                send "A GIGANTIC
		MULTI LINE
		PIECE OF CODE"
              }

"" and '' quoted strings are single-line (unless you add a single \ at the end of the line, which tells Lua to treat the next line as part of the string too). What you want here are Lua's multi-line string, delimited by [[ and ]].

Also, don't forget the '=' after 'send'. You're passing in keys and values to a function, not calling code (yet).

Eloni said:
I'd like to know how to make timers in my lua document so that I can use LUA to recreate them with new (random) times after every execution. So the send would look something like:


	send "  doSomething
		doSomethingElse
		timerTime = math.random(5)
		LuaAddTimer {   name="mytimer"
				minute = 0
				second = timerTime
				--etc
				}

Tricksy! There are a couple ways to do this, but the easiest by far is just to give your timer a label and use SetTimerOption to reconfigure what you want. That way you don't have to go through the pain of defining a timer that creates a timer that creates a timer, ad infinitum... ;)

Eloni said:
--somehow make a timer that makes above fire after 10 seconds
--AKA a timer function is something I don't know how to create :(

Go to Help -> Function list and type "Timer" into the filter box. Double-click on any of the functions in the list to view the documentation.



Eloni said:
--I don't know if 10 is part of a 'sendto'
--'12' sends to script
--or if 10 stands for 10 seconds?  <<this might be a stretch

The only purpose of wait.match() is to wait for some text to match, then resume the coroutine. There's no "send to" setting involved, unless wait.match() puts "coroutine.resume(whatever_it_is)" in the Send field, in which case it would be 12 (but that's only relevant for the immediate contents of the Send field of that particular trigger).

Indeed, if you go here [1] and scroll down, you'll see that the second parameter to wait.match() is a timeout period. If the given text isn't received in that amount of time it'll yield to the coroutine again. Timeout can be nil, in which case it'll only resume if the text comes in.

Eloni said:
Also I didn't expect my code to work anyway so I never tested it. However when I tried to compile something earlier I received this message. Don't know if it's something I need to worry about:

Run-time error
World: Alter Aeon
Immediate execution
C:\Program Files\MUSHclient\lua\wait.lua:159: Timers not enabled
stack traceback:
        [C]: in function 'assert'
        C:\Program Files\MUSHclient\lua\wait.lua:159: in function 'make'
        [string "Script file"]:100: in main chunk

Sure is: Go to Game -> Configure -> Timers and make sure the "Enable timers" checkbox is checked. Otherwise none of your timers - nor the wait library, since it uses timers - will work at all.

Eloni said:
I just realized my attempt at a coroutine makes even less sense now since the 'if check' for 'fighting' would only be called after the coroutine that checks if I was fighting has yielded.

:'( I'll be spending more time trying to understand this.

I'm impressed that you're thinking so actively about this. :) You're doing better than what 90% of people would do just because of that.

[1] http://www.gammon.com.au/forum/?id=8608
Amended on Sat 04 Dec 2010 07:13 PM by Twisol
#19

*facepalm*

So that's how you search the help files... I feel like a whole new world of options is open to me now.


Thanks a lot for the reply! I'm still trying to soak it all in and make changes. It might take a while : )
#20
Well after fooling with AddTimer, addxml.timer, and LuaAddTimer for an hour or so I finally got one to do what I wanted it to.

Here's the code, except it doesn't work as I would expect it to.

First, the timer never starts with a second value of 2, even if I put two in "". (second = "2")

Second, SetTimerOption doesn't work as expected. It does the same thing whether I use randomTen, or math.random(10). Instead of picking a random number between 1-10 it seems to pick 1 or .1 over and over, making the timer repeat 'print "TIMER WORKS"'. Also, if I inspect the timer in the GUI it has a 0 in seconds, instead of a 1. This seems significant because if you try to save a timer with "0" in all the time fields it wont' allow you. This might mean that instead of picking .1 or 1 it picks something other value that it can get away with.


LuaAddTimer {  name = "cows",
                second = 2,
		send_to = "12",
                send = [[
				print "TIMER WORKS"
				randomTen = math.random(10)
				SetTimerOption ("cows", "second", "math.random(10)")
				
				
				]]
              }


Australia Forum Administrator #21
What are you doing here? You are making a timer, that once it fires changes its own time interval to some other interval?

This won't work for a start:


SetTimerOption ("cows", "second", "math.random(10)")


Try doing this in the command window and you'll see what I mean:


/print ("math.random(10)")  --> math.random(10)


Then try:


/print (math.random(10))  --> 6


Try something like:


LuaAddTimer {  name = "cows",
                second = math.random (10),
		send_to = "12",

Australia Forum Administrator #22
As for the coroutines, I think you want to move away from "how do I pause a script?" and more towards "how do I solve my problem of healing myself after 10 seconds?".

Think more in terms of incoming events. Mainly these would be:

  • Some time elapses (use a timer for this)
  • Something happens on the MUD (use a trigger for this)
  • You, the player, want to do something (use an alias for this)


So, say for example your health is low and you are poisoned, but you can only do one cure at a time. One approach might be:


Send "cure light wounds"

DoAfter (10, "cure poison")  -- 10 seconds later, cure poison


But maybe after 5 seconds you are in a fight and you can't cure the poison. Well instead of DoAfter, which is basically a simple way of adding a timer, you can add a named timer (like what you were doing). And then if you find yourself in a fight, just delete it with DeleteTimer, and add another one later on when you are ready.

Eloni said:

I'd like to know how to make timers in my lua document so that I can use LUA to recreate them with new (random) times after every execution.


It's Lua by the way, not LUA.
Lua is not an acroynym, it is the name of something. For example, your name is not ELONI, right?

Do you really need to recreate themselves? Why not just have one timer create another one, with a different time? That might be simpler.
USA #23
Nick Gammon said:
Lua is not an acroynym, it is the name of something. For example, your name is not ELONI, right?

Lua is Portuguese for Moon. Thought I'd throw that in there.
#24
I do appreciate you guys correcting my poor vocabulary. I would hate to sound like a dolt for the rest of my life, and I'm positive it will help me understand everyone else.

Also, thanks for the explanations Nick! Now that I think about it, adding and deleting timers is a genius way to handle my wait/coroutine problem. I don't know what it is exactly, but it's like something clicked... It's a much better solution than what I had in mind and I had to make only a few (easier) changes to my code to implement it.

I've made more useful working pieces of code today than ever before thanks to you all's help. Thanks again!

No more questions for now. Surprising right? ;)
#25
So now I'm trying to make a system for movement. I seem to be getting some type of scope error, but I can't figure out what's wrong.

Here are the methods in question:


Immediate execution
[string "Timer: pathTimer"]:5: attempt to index global 'currentPath' (a nil value)
stack traceback:
        [string "Timer: pathTimer"]:5: in main chunk


If check to see that conditions are ideal for moving


...

elseif ready == true and fighting == false and timeToMove == true then
						
   timeToMove = false
					
   pickRandomPath()
							
						
   onAPath = true
					
   newMoveIdea()

...


Pick a random path.



	function pickRandomPath ()  --Picks a random area
--	to go to when the method is called and sets it to currentPath
		
			currentPath = listOfPaths[math.random (3)]
			currentPathLocation = 1 -- STARTS CHOSEN PATH OFF AT 1
				
			print ("Current destination: ")
			print ("currentPath")  --I should also note here that I'm trying to print the
									--the name of the array.  Neither "currentPath" nor 
									--currentPath work
	
	end -- pickRandomPath



Move on that path until it's complete. This contains the timer where the area occurs.


	function newMoveIdea()
	
			if moveQued == false then
				moveQued = true
				LuaAddTimer {  name = "pathTimer",
					enabled = "y",
					one_shot = "y",
					second = math.random(1,3),
					send_to = "12",
					send = [[
								print "using newMoveIdea()"	
								--a separate trigger will backstep if you try to move while in combat
								--or while out of move
								
								if currentPath[currentPathLocation] == nil then
									Send (ColourNote("white", "red", "Area path completed. what now?"))	
									
									--switchToAreaPath()  --method not made yet, it will be the same as 
									--newMoveIdea() except when currentPath[currentPathLocation] == nil
									-- it will set onAPath == false (so it can cast recall)
								elseif onAPath == true then
									Send ("currentPath[currentPathLocation]")
									currentPathLocation = currentPathLocation + 1

									moveQued = false
								end
					]]
				}
			end --if moveQued
	end --newMoveIdea()



Amended on Sun 05 Dec 2010 11:23 AM by Eloni
#26
After playing with the timers a bit I came up with a few good ideas as to how to use them. I'm proud to announce that I've made my first seemingly intelligent exp-getter-script :D

Thanks a lot for the help guys. I wouldn't have been able to do it without you.
Australia Forum Administrator #27
Eloni said:

This contains the timer where the area occurs.


You haven't said what the error is, so it's kind of hard to help.

Anyway, you may want to look up some code tidy-ups. Using the addxml.timer (which ships with MUSHclient, it isn't clear if you are using that) you can now use "true" and "false" for flags, see example below. Also use sendto.script instead of "12" for better documentation.

I'm not sure about this:


Send ("currentPath[currentPathLocation]")


Do you really mean to quote that? It will just literally send those words.

You also don't really need to do this:


if onAPath == true then


Boolean values like true and false can just be tested, eg.


if onAPath then


This is my cleaned-up version. I don't promise it will do what you want, but it illustrates how you can make the code clearer:


require "addxml"

addxml.timer {  
  name = "pathTimer",
  enabled = true,
  one_shot = true,
  second = math.random(1,3),
  send_to = sendto.script,
  send = [[
        print "using newMoveIdea()" 
        --a separate trigger will backstep if you try to move while in combat
        --or while out of move
        
        if currentPath[currentPathLocation] == nil then
          Send (ColourNote("white", "red", "Area path completed. what now?")) 
        elseif onAPath then
          Send (currentPath[currentPathLocation])
          currentPathLocation = currentPathLocation + 1

          moveQued = false
        end
  ]]
}