Script Engine

Way back I decided that scripting should be in the game. While developing the new architecture I finally came around and did exactly that. Finally, scripting is in the game!

Text-Talk-Test03

It was… surprisingly simple.  I used JINT, a Javascript scripting interpreter for .NET by Sebastien Ros. It comes with a Nuget package, so the whole process was as simple as adding the package to the software Solution and write the following three lines of code:

js = new Engine(cfg => cfg.AllowClr(typeof(Services).Assembly));
js.Execute("var Game = importNamespace('tndwolf.ECS');");
js.Execute("var Mechanics = Game.Services.GameMechanics;");

With these I can just run “js.Execute” on a string that I have loaded and execute the commands in real time. For example I can run:

js.Execute("Mechanics.Test(0);");

To display a nice test message like “10:41:41 – DEBUG – GameMechanics.Test: Entity 0 at 3,3“.

The new architecture is also helping in making this really easy. I am using the Service Locator pattern to expose a number of services to the game world. By importing the “Services” assembly inside the Javascript engine I have exposed all the services to be accessed directly from a script (Game Mechanics in particular, using the Mechanics shorthand) while keeping the game world and its entities and systems inaccessible.

WoU_Services_2016-07-19

Did it grow since the last time?

Now Components can contain custom scripts to implement all kind of functionalities. Before that, mostly for testing, I implemented a simple parser. This parser was extremely limited and made impossible using complex combinations like the ones that I want to have for boss encounters.

As an example this was the definition of an old skill (removed a few bits for clarity):

<skill id="sk_icicle" name="Icicle" cooldown="3" priority="1" range="12">
<script type="ON_EMPTY" selfAnimation="CAST1" targetParticle="ps_icicle" script="CREATE e_ice_spear"/>
<script type="ON_TARGET" selfAnimation="CAST1" targetParticle="ps_icicle" script="DAMAGE 3 ICE CREATE e_ice_spear"/>
</skill>

Not so bad, but as you can see the effects in themselves are pretty limited, With scripting it will look like this:

<skill id="sk_icicle" name="Icicle" cooldown="3" priority="1" range="12">
<onEmpty>
Mechanics.Create("e_ice_spear", TargetX, TargetY, "ps_icicle");
Mechanics.SetAnimation(ACTOR, "CAST1");
</onEmpty>
<onTarget>
Mechanics.Damage(ACTOR, TARGET, 3, Mechanics.DAMAGE_TYPE_ICE);
Mechanics.Create("e_ice_spear", "ps_icicle");
Mechanics.SetAnimation(ACTOR, "CAST1");
<onTarget/>
</skill>

The improvement may not be readily apparent, but imagine things like stopping the game flow for a brief dialogue when a boss appear, or create attacks with specific patterns that require some math to be calculated. Without scripting the only way to do that was to insert specific exceptions inside the code. It would have been doable but that would have meant changing the code and rerunning the game every time I wanted to modify something, and creating more and more exceptions to be managed inside a code that was going to become more and more unmanageable as time went by.

And of course this also opens up a lot more opportunities to mod the game!

Probably the true complexities of scripting will be more evident later on, as I add new functionalities, but right now I was pleasantly surprised about how smooth everything went. Surely a huge part of that was thanks to JINT and its great design. If you are developing in C# I warmly suggest it for your scripting needs.

Thanks for reading.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s