Scripting p.2

Good scripting engine should:

  1. Know much about the standard game types and allow script to manipulate any public data within the context of the object.

  2. Be able to enhance standard game types with custom implementations of well-known methods (OnNewTurn(), OnIntegrityChange(), etc).

  3. Be able to enhance standard game types with custom data fields, which should be available both from the engine and from the script too.

  4. Provide means to deploy completely custom named methods and ensure their accessibility both from the main code and from the script code (Prime(), Fire(), DoStuff(), FluffyDogs()).

  5. Have capabilities of working with plug-ins. This include drawing windows and waiting for the user/AI input.

And 6. Everything should be easy as possible, and most of the service code should be automated.

— from the book of a good scripting engines, title page.

Spending time thinking about how one can make a good scripting engine, I’ve come up with these 6 points. They’re seems to be covering every scenario i can come up with. And now, after most hopeless and long debugging session I’ve ever had (after that multithreading incident), it’s finally working.

grenadeScript

After selecting the detonator and listing its abilities (not shown here) we can choose the ability Prime. And here was the first serious hurdle.

Usually scripting languages rely on the set of pre-defined functions with a custom implementations. You define them in a code, and then scriptwriters/modders could write their own implementations for it. That’s one degree of freedom. You still have to inform the engine where’s the human that can walk and where’s the house that can crumble, and be sure not to mix something up. What I’ve tried (and as for now succeeded) is to be able to use any custom function (p.4 of the GSE guide). GUI/engine lists all actions available for the object/its children and then player/AI chooses what’s most appropriate. Usually these things are done with the help of Reflection (i.e. painfully slow), but I was able to avoid that at the cost of some dumb trick, a mandatory piece of code for every script:

UseAbility(string abname) 
{
  if(abname=="Prime")
    Prime();
}

Since the script knows about the Prime() method, it can call it directly, which is WAY faster than scanning the assembly in search of the method signature. Downside is, this code can’t be hidden in the main module and have to be fitted to every script by hand.

After that we’ve reached the script and its Prime() method, we could set up the timer for 1 turn and proceed. But what if i’d want to set up the timer for 2 turns? for ten? or i’ve misclicked and want to undo my decision? Certainly the script needs some kind of variety for the input. We could go for Prime1,Prime2,…Prime10, but that would be unpleasant and wouldn’t work for more complex cases, like setting up the route for the guided missile (hehe). Scripts should be able to influence the GUI. And, since engine (and therefore scripts) know no bit about who runs the GUI and what is it eve means, they could only route their call to some plug-ins they know about.

if (!bso.Mechanics.GetBoolValue("Primed")&&!bso.Mechanics.GetBoolValue("InputCalled"))
{
    bso.Mechanics.SetBoolValue("InputCalled",true);
    object sync = new object();
    lock (sync)
    {
        gm.RequestPluginScript("SDLViewport","SDLScripts.BSO_Detonator","ShowInputWindow",bso,sync);
        Monitor.Wait(sync);
    }
    bso.Mechanics.SetBoolValue("InputCalled",false);
    if (bso.Mechanics.GetBoolValue("Primed"))
        gm.Events.CreateEvent("Nade primed!");
    else
        gm.Events.CreateEvent("Priming cancelled");
}

So, if the detonator hasn’t been primed yet and the window hasn’t been shown, we request the SDLViewport to run method ShowInputWindow of the object BSO_Detonator, which is a script object too, but one that loads and runs in the GUI context, so it can draw us a nice window with some buttons. With any button pressed, script that was waiting for a signal at the Monitor.Wait(sync) will continue execution, checks what was the result of the GUI input and then finish.

As you can see in the gif, there are buttons for setting up detonator for 1, 2 turns or cancel whatsoever.