Page 1 of 1

Lua rc scripting: Ending a turn?

PostPosted: Thursday, 16th August 2018, 02:40
by arc
Hello all. I've started playing with the RC lua scripting and I've noticed that while crawl.sendkeys seems to work as i'd guess (also seemingly ignoring user macros) things like crawl.do_commands and food.eat don't seem to work the way i'd expect.

Specifically, the do_commands and food.eat seem to start the action but not finish it (advance a "turn", you.turn_is_over() returns true and attempting other actions that take game time result in errors.). Is there any way other than returning from the function to advance that turn? I can't seem to find one.

Thanks!

Re: Lua rc scripting: Ending a turn?

PostPosted: Thursday, 16th August 2018, 04:25
by chequers
Why don't you want to return from the function? Is it because you are trying to store state across multiple turns?

Re: Lua rc scripting: Ending a turn?

PostPosted: Thursday, 16th August 2018, 04:50
by arc
chequers wrote:Why don't you want to return from the function? Is it because you are trying to store state across multiple turns?


well one example is i want to do the action, check the result, and then do another action based on that.

a simple example would be look for a corpse, butcher it, <wait game time for butcher to finish>, pick up chunks.

or what if i just want to walk right,right,up,right.

I did notice there's a ready hook. do people often save state in there and then call one action per ready call? Is there a way to play nice with other peoples scripts in that case? It looks like people often use ready to call a function that does "ready stuff" for individual scripts. But what if two scripts try and do an action in the same ready call?

Ideally i'd just like to keep control while my function takes multiple turns.

Re: Lua rc scripting: Ending a turn?

PostPosted: Thursday, 16th August 2018, 05:10
by Fingolfin
I could see a system using an action queue in ready() :
  Code:
if action queue is empty:
  -- do some checks that put some actions in the queue, like check a global variable do_butcher_corpse
else
  -- pop the first item in the action queue, map it to a function and do that function (like food.eat, or crawl.do_commands

Re: Lua rc scripting: Ending a turn?

PostPosted: Thursday, 16th August 2018, 18:51
by advil
If I understand what you're asking for, there is currently no way to do what you're asking. That is, there is no way to trigger time passing on lua script line n (via input or lua api) and return control to line n+1 on your next turn. Right now what you need to do is think of building a state machine with ready() as a sort of controller; save state in a global variable and use a conditional on state in ready() to queue up the next command and state change. If you want to integrate multiple scripts, the simplest thing to try is to write a custom ready() that calls both of the (renamed) ready() functions from the two scripts, in some order -- though this won't work in general if they both try to react to the same cases. Most complicated scripts already have a pretty simple and templatic ready() so it may be easier to integrate their contents directly.

FWIW sendkeys can send multiple keys in succession but all it does is load up the macro buffer; it doesn't hand over control to crawl until your lua code finishes, and then ready() gets call again after everything is processed. This took me a long time to get my head around.

I've looked into implementing something like what you are asking, and I may work on it in the 0.23 cycle as it'd be extremely useful for writing unit tests for UI elements, but it's technically very non-trivial.

Re: Lua rc scripting: Ending a turn?

PostPosted: Thursday, 16th August 2018, 19:33
by Siegurt
You'd want something analogous to promises/async frameworks. You could roll your own in LUA to accomplish this (in the manner fingolfin suggests) but restoring your state and resuming execution isn't really very trivial if you wanted to generalize it sufficiently. However since funcitons in LUA are first class values, you can at least do something reasonable like make a table of functions and store a list of actions that refer to the table (that's probably how I would implement in LUA)

Re: Lua rc scripting: Ending a turn?

PostPosted: Thursday, 16th August 2018, 19:38
by Siegurt
Now that I think about it for another minute or two, for this kind of purpose you could just implement a .then (promise-style) object, no action table required, each .then would just reference the next action in the chain, and ready() would just pop off the top of the stack and execute it (effectively you'd have a linked list of objects)

State (such as it would be needed) could be passed down the object chain in a table removing the need for any globals except a single head promise object for ready() to pop.

Re: Lua rc scripting: Ending a turn?

PostPosted: Friday, 17th August 2018, 04:53
by arc
Thanks for the info/ideas guys and thanks advil for the clarification of what's going on.

I think I can get what i need out of the scripts now :)

Re: Lua rc scripting: Ending a turn?

PostPosted: Tuesday, 21st August 2018, 15:35
by ebering
What you want to do is instead of using ready() directly is have ready() add a macro using the api (crawl macros are Lua coroutines) and then execute that macro.

Instead of your macro being a usual ^D entered macro, it should be a coroutine you write in Lua, that does the command you're interested in and then calls yield(). Crawl will do the command, let the world react, and then re-enter your macro coroutine after the yield() call. State can be kept in the function body as usual.

There's some more magic you need to do regarding delay interrupts that I don't really know in detail and is a gap in the api documentation I prepared.

Re: Lua rc scripting: Ending a turn?

PostPosted: Tuesday, 21st August 2018, 16:01
by ebering
After talking to advil about this on IRC... this could cause crawl to crash if you use the wrong parts of the api from your coroutine. Good luck!

Re: Lua rc scripting: Ending a turn?

PostPosted: Thursday, 23rd August 2018, 03:46
by arc
Ah interesting okay. and how does one add a macro? I see a runmacro() function but no functionality to add them. Are all global lua functions eligible to be called as macros?

Is there a category of calls that may crash?