A basic introduction to MessiahScript
Before I even begin, I’ve got to thank Rick Gibbed for providing the exact launch parameter that triggers Messiah into rebuilding it’s scripts.
I always found it odd that the scripts were included with the game, despite the fact that their inclusion wasn’t necessary in order for the game to function, and suspected for quite sometime that perhaps the game was intended to be alterable somehow.
Searching through a number of sources gave me absolutely nothing other than lines from the developers stating that support for modifications was under heavy consideration, all the way up to the Messiah’s release - okay, we didn’t get the level editor but we got their proprietary scripting language, but how the hell can we do anything with it?
After contacting Saxs himself, the lead programmer for the game, who confirmed that there was such a launch parameter, it’s great to see that 18 fucking years later there’s finally a way to modify the game.
Getting yourself running
First thing’s first, we need to address the elephant in the room. Messiah is a difficult game to get up and running. This has unfortunately almost always been the case and it’s certainly not gotten any easier as time has gone on (as I said, 18 years).
Unless the original game is made open-source, giving people an opportunity to update it, I doubt this is going to change and I’m sure we will eventually hit a point where it will simply cease working on modern systems; enjoy it while you can.
I’m sure this is going to be a big consideration for anyone who wants to invest their time in modifying Messiah and I respect that the enthusiasm is probably on the low-side.
If you don’t own the game already then it’s possible to purchase the game from a number of sources such as GOG or Steam, or even better, just buy a copy off eBay (the big box edition has an awesome design).
I would highly recommend taking a look at this thread, as it provides a number of different things you can try in order to get the game running.
Once you’ve finally got the game up and running, whether it’s with the Glide or D3D executable (the former typically seems to offer the most stable experience), you’re now set to start breaking everything!
I personally find that it tends to be the case the D3D executable is almost unusable on modern systems but the Glide executable and using an API wrapper, such as nGlide, will perform almost flawlessly and with the added bonus of forcing the game into a higher-resolution.
Where’s the code?
Now I’m sure you’re eager to get into this, so you’re going to need to know where you can find the scripts.
The scripts are all provided with the game, regardless of where you bought it from, and you’ll typically find it within your game’s directory - this is the same directory where you’ll find the executables to launch the game - and then under the directory named “MessiahScripts”.
If you’re not too sure where the game has been installed, I might suggest right clicking on the shortcut in Windows and selecting the “open file location” option - depending on how you install the game, this should in theory take you straight into the directory under which the game was installed.
Building the code
In order to get your changes into the game, the game will need to know that you want it to rebuild the scripts - this isn’t something it knows to do automatically unfortunately.
To do this, you can either create a shortcut or introduce a batch file, and all you will need to do is add the launch parameter +C for the executable. Of course, as an example, you can find the batch file that I use to launch the game available here, which I execute from the MessiahScripts directory.
When you launch the game with this parameter, it may take a little longer to startup and will give you a please wait message while it processes the MScript - if any issues occur during this then it will produce a message box with the details, so be aware that you may need to ALT + TAB out of the game if this happens in order to see what the fault was.
Syntax
While MessiahScript is slightly similar to C in it’s very basic form, there are a few major differences which make it a unique language (for example, it’s case-insensitive).
I might suggest perhaps reading up on a programming language such as C or any other language similar to C, as Messiah’s language very roughly resembles it and I’m not going to explain the entire syntax of some C-style programming language, as it goes way beyond the scope of this article.
If you’re not familiar with any programming languages, then you’re more than welcome to continue but there will be a few fundamentals I’m not going to explain, which you need a basic knowledge of if you want to do anything substantial.
Macros
Much like almost every C compiler out there, MScript supports the #include macro for including other files into your MScript.
MScript also appears to support the #define macro as well.
Conditional Statements
Both if and else are available in MScript, however else if will need to be entered as a single phrase (elseif), similar to languages such as Perl.
The for, while, do and other conditional statements are absent in MScript.
Operators can be used in any if,_ else and _elseif statements, but they can’t include spaces - so take the following example.
myvar == 1
This is invalid in MScript and will generate an error, and instead you’re expected to do.
myvar==1
Straight forward enough right? MScript appears to support all the standard operators which C supports.
There are also signs that suggest switch statements are also available however these are not used in any of the existing code, so I can’t say for sure if these work in any capacity as I haven’t used them myself.
Ending Statements
While it appears to have been standard practice, it doesn’t actually appear to be a requirement to include a semicolon at the end of any statements in MScript - but I would recommend continuing this trend.
Variables
MScript provides floats _and _vectors as the types of variables you can declare. Technically speaking, a vector in Messiah is a representation of three floating-point values, making up a position in 3D space.
One of the immediate limitations you might find with the language is that variables can’t be set directly - if you want to modify them or, heck, even declare new variables, then this will need to be done through what is essentially it’s own dedicated function.
Take the following example, where I want to create a new float variable to keep track of something in the game.
float(_declare,myvar,1);
In this case we’re declaring a new variable of type float with an initial value of 1, you could consider the structure of these functions like so.
<type>(_<operator>,<name>,<value>);
If I wanted to do something even as simple as adding two variables together, this would work like so.
float(_declare,myvar1,10);
float(_declare,myvar2,20);
float(_add,myvar1,myvar2);
This will add the value of myvar2 to myvar1.
Overall you have the following methods you can use to modify variables in MScript, which are all declared in Defines.h
_declare
_set
_seti
_add
_sub
_mul
_div
_addtime
_settoactorpos
_settotargetpos
_setactorpos
_settargetpos
_distance
_distance2
_normalize
_dotproduct
_xproduct
_direction
_copyx
_copyy
_copyz
_setx
_sety
_setz
_rnd
_copy
_vectorfromangle
_vectorfromdirflags
_and
_or
_xor
_clr
_change
_speedfromdirflags
_rotate
_setparent
_clrparent
_copyparent
_rnd2
_swapyz
_settobonepos
_inv
_target
_settocolobjpos
_subx
_suby
_subz
_addx
_addy
_addz
_shiftleft
_shiftright
_settolockedpos
_multiplywithtransmat
_settopathdest
_setpathdest
_sin
_cos
_settokfpos
_settocolpos
_round
_multiplywithobjtransmat
_settotriggerpos
_abs
_scale
_settocamtarget
_settoboneposrelative
_setplanetrignvec
_mod
_setactorangle
You may notice many of the declarations in the Defines header are prefixed with an underscore, this was likely because the header was shared between both the scripts and the engine, and the underscore was to ensure there weren’t any conflicts with reserved keywords and other fun stuff.
Keep in mind that some of these are specific to only one type of variable. This is something that I’ve documented in my example code.
Functions and Subroutines
Functions are a bit of an oddity in Messiah but there are a number of built-ins that you can use. All of these are declared in the Defines.h header.
I’ve made a start documenting the built-in functions here but contributions are welcome as there are currently a lot of gaps.
There is no support, as far as I’ve determined, for implementing your own functions, however you can implement your own “subroutines” in MScript. Subroutines in MScript will operate based on the context of the current actor - so you can essentially think of subroutines as being blocks of code embedded into your actor logic, but reusable.
Declaring a subroutine is quite straight forward.
Subroutine(Sub_Example,Parm1,const(Parm2),Parm3)
{
// do something
}
And calling a subroutine is even more straight forward.
CallSub(Sub_Example,position,SomeAction,nposition)
As you can probably see, you can even provide arguments for a subroutine - the arguments can be an Action, Actor or any variable type as far as I’ve determined so far.
You can also declare the variables as constant, by using _const(
Messiah also appears to have it’s own set of built-in subroutines that can be called, however, unlike functions and subroutines, you need to use the Call function - but the structure is then the same as if you were calling a subroutine - _Call(
Implementing Objects
Actors
Actors are widely used in MScript and are made up of two parts, the Actor and the ActorHeader.
The ActorHeader is essentially an outline of how that actor will appear and function within the game.
One of the usages of the ActorHeader is to specify what model will be used to represent the actor by using the file function - the argument for this function needs to point to a DCR file within the game’s directory ideally, but it appears that this can also point to another ActorHeader, which is useful if you want to share some properties between several different actors.
file(<path/actorheader>);
In general, it appears that the file function will only be executed the once - afterwards it seems the game will never execute it again. This is important because it comes into play for when we create our Actor.
Other functions you can use in the ActorHeader, such as type and class will essentially set specific flags for that actor which can alter it’s behaviour in the game or change how other actors see it.
A rather basic example of an ActorHeader is shown below.
ActorHeader(BulletRingOfFire)
{
file("pc\actors\weapons\fakebullet\bullet.dcr");
sharemaps(GenericWeaponPage);
type(_ActorStaticObj);
class(_CLASSBullet);
respawndelay(60); //can't be respawned for 60 ticks
rebound(0);
ColSphereSize(700);
gravity(0,-127);
ColMask(_COLAllExceptHoly);
InventoryType(_AmmoExplosion);
Health(DamageSatansRingPiece)
AIsight(100,1);
AIstrenght(100);
}
Functions, such as gravity _and _health, should be self-explanatory but unfortunately quite a few of these will require some experimentation on your part.
The Sharemaps function likely specifies the texture atlas on the GPU that the actor’s model will use - this is slightly complicated to explain, but from my observation it appears that rather than uploading textures individually to the GPU, Messiah will add textures to a shared texture atlas - these are loaded and unloaded as the player plays through the game. I would suggest some caution for this.
Moving on, the _Actor _is very much the core part of the actor, funnily enough.
As far as I’ve been able to determine, this block will get executed every tick for each instance of that actor in the game and includes all the basic logic that controls how that actor will behave in the game.
There’s so much to cover for the actor that I won’t be able to do it here but you’ll typically want to use the file function at the top of the actor body and provide the name of an ActorHeader so that the basic attributes for your actor can be declared before you begin implementing any logic.
Below I’ve provided a basic outline of how you would implement an actor.
ActorHeader(ExampleHeader)
{
file("blah\blah.dcr");
// properties go here
}
Actor(Example)
{
file(ExampleHeader);
// logic goes here
}
Actions
Actions appear to be peculiar functions that can execute an animation upon being called. Again, they use the file function but in this context it’s to determine what animation they will use. It’s possible that the file function changes it’s role depending on the extension provided by the string or the context.
Below is an example of an action that’s used for Bob, the main character featured in the game (if you don’t know who Bob is then God help you.)
action(BobTalksToGod)
{
file("pc\actors\Bob\xxx\BTlk2God.ske",100,off);
trigger(_allDIR);
nrintframes(8);
gravity(on);
intforloop(off);
break(off);
connections(BobTalksToGodLoop);
}
Unfortunately at this time I can’t say what the two arguments in the above example do - more research is going to be needed here. Perhaps the last parameter determines whether the animation will loop and the second is the time the animation will play for, this will all need to be confirmed.
Additionally most of the functions you see here will need experimentation as I have not had the opportunity to determine how it all works myself - the connections method seems to determine animations that follow and gravity will toggle the gravity simulation during the animation.
That being said, you can update the actor in any way you please within the Action.
Final Thoughts
I was really hoping I would have more time to experiment with the language but unfortunately time wasn’t on my side, but I really hope that this article will give people a good start on it.
From my first impressions, I would say that it’s certainly clear that MessiahScript was designed very much with one goal in mind. It’s certainly no UnrealScript, as it’s flexibility is quite limited by comparison and it appears to mostly be limited in scope to just actors - perhaps making it more comparable to a language such as QuakeC.
I guess this isn’t so much a fault of the language but rather a fault of the implementation, though as far as general syntax goes, MScript is certainly an odd mix of both a strict but also in some aspects, a lax design.
One of the things I was surprised to see is that both the music and input are controlled through MScript, as well as most of the general setup of the game - this on it’s own does make it perhaps slightly more versatile than QuakeC in some respects.
There’s a lot of functionality that does go unused in the MScript as well, not just referencing some of the unused actors and events that were to be featured in the game, and a few remains of functionality that I can only assume was in use when Messiah still featured a software renderer such as references to bump-mapping.
As a random bit of trivia, a line in some of the MScript appears to confirm that Messiah itself was written in C++ which probably isn’t too surprising - the late 90s saw a huge wave of developers moving from C to C++.
Again if you spot any mistakes or if there’s anything that could be improved then don’t hesitate to comment and I’ll update this accordingly.
My intention is to eventually write a follow-up to this article which will actually walk you through some of the neat things you can do with MScript but we’ll see what happens.