ModHQ and UI Editing

I’ve integrated the UI viewer/editor into ModHQ and fixed up some performance problems. It now loads in about 2 seconds on my (fairly low specd) laptop. It also has a nice new loading screen.


The program is essentially read-only at this stage, any changes you make with the unit prototypes or UI wont be saved back to your data files. However, all changes made through the Data Files tab will.


In this screen, double click any item to convert it to XML and open it, you can then make edits in your xml editor, and save it back to have it compiled and inserted back to the data file. Textures can be viewed through this tab, but will not successfully save back to the data file.

As before, you can check and uncheck items in the UI Editor tab to see their contents


Download: ModHQ (.Net 4 + source)



RTS4 UI Viewer

I put a little more work into the UI viewer; moved it to WPF and added some slight editing capabilities (but no way to save the file back). It will let you see a number of different UI files at different resolutions, and see all of the hidden elements from the AOM alpha.

It does not yet capture all of the data in the files, so its not possible to save back without data loss. But its probably not worth finishing off.


Download (.Net 4 + Source)


RTS4 XScript Engine

This blog post is going to be pretty boring, no pretty pictures, and its late, and I’m very tired while typing it, but it underpins a lot of the features and functionality available in AoM.

XScript powers a significant part of the data-driven systems in the BANG! engine. As mentioned earlier, random maps are created in this language, but the engine also uses XScripts to describe other routines, such as the start-up sequence, victory conditions, and artificial intelligence. The language and frameworks available are very object-oriented c-style, using functions to create and manipulate objects by passing their ID as the first parameter.

Three annoying issues I’ve found so far with the language is the lack of support for classes, the strong typing, and the strange scoping. Objects in the BANG! engine are created by calling the appropriate createX() function, and storing the ID returned. The object can be interacted with by passing this ID as the first parameter of other functions. Dynamic, or even non-primitive types are not supported, and blocks do not indicate scope; a new scope is only created inside functions.


The simplicity of this language makes parsing relatively easily, though there were some strange bits of behavior that had to be accounted for.

  • for loops must iterate one integer variable, this variable must not be declared and is implicitly declared by the for statement
  • it uses the strange syntax where “void” can be specified in the arguments of a function declaration when there are no arguments.

Originally I tokenised the code, then built an abstract syntax tree, ready to be passed to the compiler to select instructions, however after building this system, I realised there was a lot of data duplication; the set of valid tokens which may appear after a given token was required both in the tokenisation pass and in the AST generation. Removing data duplication is one programming rule I always stick with, as I find it significantly improves maintainability and reduces bugs in code, so the system was rewritten to do this tokenisation/AST generation in a single pass.

To achieve this in a flexible and reliable way, a list of token types is fed into the compiler (things like number, string, literal, controlFlow, type), and set of grammar rules (statement -> if | for | flow | funcDeclr…, if -> “if” & “(” & condition & “)”…, condition -> expression…). These grammar rules contain in the possible tokens which may appear after a given token, and are able to take into consideration the context of the parse (ie. ‘-‘ can be treated as a unary operator or subtraction, “if” can be treated as a control flow instruction, or a variable). The parser starts by attempting to parse the given source code as a block. This method is then called recursively to process statements in the source code bit-by-bit. Each rule used is then pushed to a list before processing child-rules, and popped off when parsing of the given rule is complete.

Generating pseudo-instructions

For each rule, the system calls a special piece of code to gather required input from the source code and add instructions to another list representing the required behavior. The instructions generated by sub-rules is passed to parent rules when they begin running their special routine, so that they can organise sub instructions (ie. push both literals before running the addition instruction) and extract required meta-information (ie. where the condition of an if statement ends, and the code begins)

Generating VM-instructions

After a list of instructions has been generated, their types must be resolved, and the appropriate VM instruction selected. This is done by creating a virtual stack, and pushing or popping types required by instructions as iterating over each instruction. Doing this is not flexible and most would consider ugly and unreliable, though for the given cases, it works well, and most of the time, offers the expected types on the stack, if the code were to be run, by abusing the fact that code is typically sequential. A set of VM instructions is looked up by name based on the current instructions name, and of the matches, the best VM instruction to match the types expected on the stack is selected.

As this process treats the instructions as if they were always executed sequentially, it has problems past forks and jump instructions, however fortunately, almost all of the time, the stack will be balanced before and after a conditional jump section.

RTS4 Area generation

A huge part of the generation of random maps in AoM was in the unique way areas were built. Through a series of parameters, these could be made to be arranged as anything from a single circle, to a complex fractal-like shape. When I started development of an implementation of rmBuildArea I had no idea how I should approach such a task, and went for the nearest thing I had done before; world generation for a minecraft-like game.

This involved using a augmented noise function, whereby the values nearest the centre of an area would be artificially higher, depending on the size of the area. The amount of noise contributed to each value would be proportional to how circly or noisy the area was described to be. Tiles would then be selected if their value was larger than a constant threshold. This produced a map like the following, when loading acropolis.xs:

(note this is without support for constraints, so grass patches appear everywhere)

This looked ok, but it was very different to how Age of Mythology produces its own maps, whereby areas are very varied, with the potential to have long string-like bits coming off of them

Another interesting property of their areas was that they always tried to fill exactly the number of tiles specified for each area; this means if an area is made circular, and positioned half off of the map, its effective radius would be much larger to compensate for the tiles unable to be placed.

I began thinking of how something like this could be possible with a functional system; it soon became clear that it just wasnt, a more progressive system was needed to satisfy these requrements, so instead I re-write the area code to now use a flood-fill style algorithm, where the central point of the area is chosen as the initial tile, which then adds the 4 tiles surrounding it to a list, along with a cost and noise value, and the tiles with the lowest cost are progressively processed. This means under an extreme situation, it is possible for lines or very non-circular areas to be produced, given the right properties.

This new system, as the old one did, then goes through the required blurring and threshold stages to select which tiles should be present in the final area. This appears to be implemented very similarly to how they are in BANG!, as in both engines, burring an area intensely causes the area to become very small (as if the values were being blurred with a lot of 0’s, and eventually falling below the threshold).

The terrain now better represents how it would appear in the BANG! engine, with very irregular shaped areas.

One issue which may be apparent in this screenshot is that areas seem to favor diagonal lines. This is a bug, and will need to be further investigated at some point.