Wednesday 14 March 2018

AGS Script to C#

I've never been out on New Year's Eve.  It always seemed noisy and expensive, and I've never been around people who were inclined to celebrate, so it stands to reason that I was working on XAGE on the evening of December 31st, 2017.

I'd posted some progress pics and got chatting to Scavenger, who kindly sent me the source code to his AGS game Terror of the Vampire.  After poking around for a few hours I was able to convert the bulk of the game to XAGE, albeit with a worryingly large 10K+ of C# scripting errors.

At high volume the number becomes somewhat meaningless - a missing bracket can create hundreds of syntax errors in a single class - but it was clear this was a complex game that came with a lot of challenges:

  • Massive AGS Modules like Tween were used which were not being parsed well.
  • AGS Engine plugins were used and unsupported.
  • AGS-style structs were used throughout, which were not being parsed and converted into equivalent C# classes.
  • There were gaps in the scripting API, mainly due to newer AGS functionality as part of version 3.2 - things like AudioClip and AudioChannel.

By looking at the error patterns in Visual Studio, I was able to start whittling the error count down by improving the parsing, handling new data structures, implementing API placeholders and some workarounds for various AGS quirks and oddities (especially around scope).  Two and a half months later and we're down to 81 errors following a fresh conversion.

The law of diminishing returns dictates that trying to automatically resolve these would involve a big time investment that is better spent elsewhere, given they are generally simple to solve by hand, and can often be fixed beforehand in the AGS source.  They mostly revolve around C# being somewhat stricter than AGS code:

  • Casting.  C# doesn't like to implicitly cast certain types, like a bool or char into an integer.  AGS is rather lax about typing.  The solution is to add an explicit cast. 
  • Function integrity.  AGS allows you to define a function with a return type and not actually return anything.  C# does not ("not all code paths return a value").  Solution is to fix the return values.
  • Method variable scope.  AGS allows you to define variables within inner blocks that share the same name as a variable in the outer block.  C# does not.  Solution is to fix the scope or rename the variables.

These are all simple things to fix manually, meaning we are now able to fully compile the C# solution and get in-game:

As can be seen from the screenshots, there is a lot of engine functionality that has yet to be implemented or isn't quite right, but this is where the real work begins.  I will most likely start with DynamicSprite and DrawingSurface as these are all throwing null exceptions currently and are used throughout this game.  This is where I finally start to flesh out the Engine API and turn some of those table cells green.

A big caveat is that AGS Engine plugins still remain unsupported - for the time being I added a few placeholder classes to act as a dummy implementation to allow the code to compile.  It may be that we can autogenerate C# bindings to the original C++ plugin .dll, but the simpler option may be to simply re-implement each required plugin in C# and keep everything in managed code.

Other items

  • I've foolishly committed to releasing XAGE in some form or other this calendar year.
  • There has been some promising experimentation around embedding VS Code directly into XAGE Editor for a more seamless (if diluted) user experience.
  • PUBG is still causing me an enormous amount of pain.