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()_, if you don't want them modified within the subroutine. I believe because any _Actor_ or _Action_ isn't inherently modifiable through a subroutine, you'll want to pass these as a _const_, but this is simply an observation and I'm not sure if MScript actually cares - it's a good practice though.

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(,...)_ so these should be straight forward.

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.