I'm Chris Eck, and I'm the tools developer at HBS for the Battletech project. I've recently been given permission to write up articles about some of the things I work on which I hope to post on a semi regular basis. Feel free to ask questions about these posts or give me suggestions for future topics. However, please note I am unable to answer any questions about new/unconfirmed features.
I'm going to try and maintain a weekly pace with these developer journals, but we'll see how long that lasts. Last week was mostly cool new features I can't talk about or bug fixes that aren't super interesting. Luckily JustinCase wanted me to talk a little more about the editors that are included with BattleTech so this week we're going to talk about the ContractParser and its simple Markup files.
Contract Parser
First, some history... When we started writing contracts in Battletech, they were in straight json format, but Kiva (the Lead Game Designer on Battletech) and the rest of the designers felt it was a bit too cumbersome to edit by hand. To change this, Kiva wrote up a few PHP scripts that would take some proprietary markup and turn that into json by overriding the default template for given contracts. This worked just fine for a while. However, she quickly became a bottleneck as demands on her time grew throughout the project. The tool was only setup on her machine and she was the only one that could add features to it. Eventually this bubbled up the priority list and I was asked to replace the php scripts with a tool of my own.
Enter the Contract Parser. It's a simple C# command line app that takes a directory or filename as a command line argument, and then it parses all the contract text files into json files. The way it works is pretty simple. It loads up the ContractTemplate json for that specific ContractType (SimpleBattle, CaptureBase, etc) and then loads up the specified designer's txt files and overrides individual bits of data (Objective text, Combat Dialogues, Units, etc). Then it just saves out the json to the appropriate file name.
The biggest problem I had to tackle was how to make a CommandLine application with the data I needed from the BattleTech codebase - without including ALL the code. The contract parser doesn't care about the models, the animations, the terrain, the sound effects, etc. But our code base didn't have clean lines of separation between modules. As I started including just the bits I needed, more and more dependencies started to creep in.
The ContractOverride needed access to the EncounterLayer so it could apply itself. The EncounterLayer needed access to EncounterObject, Chunks, Objectives, and Serialization libraries. Some of those wound up connecting into the SimGame and things just exploded out from there. I spent days refactoring classes so I could use them in my standalone application. I removed the dependencies that I could and then used some conditional compiling and mock classes for anything I couldn't easily separate out. It was decidedly not fun. Eventually I managed to pull that data out into a separate DLL that I could compile against and now all three editors share a link to that file.
How does it work?
The code parses each file line by line and acts like a simple state machine. Line type is identified by a name in square brackets. For simple items it only needs one line like Name, ContractType, and Salvage. But some items are more complex like Dialogue Content which need other lines to detail out the rest of the data Color, cast, emote, audio.
Here's a sample pulled from SimpleBattle_AFavorToRegret.txt:
// Dialogue
[Dialogue]Dialogue_MissionStart
[Content]Enemies detected in the area, Commander.
[Color]1|1|1|1
[Cast]castDef_DariusDefault
[Emote]Default
[Audio]NONE
[Content]Intel reports that {TEAM_TAR.FactionDef.ShortName} sent a pretty serious lance of 'Mechs.
[Color]1|1|1|1
[Cast]castDef_DariusDefault
[Emote]Default
[Audio]NONE
[Content]Also keep an eye out for harassing units from the locals.
[Color]1|1|1|1
[Cast]castDef_DariusDefault
[Emote]Default
[Audio]NONE
Comments and blank lines are skipped. The [Dialogue] node specifies which dialogue to edit by name. Now the parser is in Dialogue mode so it can only parse Content nodes. It sees a content node so starts parsing it. Now it's in Content mode and can handle Color/Cast/Emote/Audio. After that, the parser sees a Content node and can't handle that in Content Mode so it pops back out into Dialogue mode. Dialogue mode DOES know how to handle Content nodes so it parses another one. Eventually the end of the file is reached or it runs into a node it can't parse so it gets back into the root level nodes and tries to parse that. If the root level can't parse the node then it's a validation error and it reports the problem in the file and which line number.
After the contract parser runs, it shows a summary of what it processed and calls out each of the validation errors. It shows what file, what line, tells you what the problem is, and even includes the original line so you can hopefully see the problem before opening up the file. Here's what some errors might look like:
And here's a link to some more detailed documentation about the ContractParser markup. It's a little bit out of date but it still has some useful information in it. https://docs.google.com/document/d/1ePfeUph7HIGNCVZxDXDJ0fZVEiqXQLGhdPwIsgdeLdM/edit?usp=sharing
The base game Designer Text files, Contract Json files, and Contract Parser are all included with the game. If you browse to local files on steam you can open the "BattleTech_data\StreamingAssets" folder. "design\contracts" holds the Designer Text Files. "data\contracts" holds the Contract Json. And "editors" holds the ContractParser, EventEditor, and FlashpointEditor. There are some helper *.bat files there with instructions in them if you edit the file. Basically you change StreamingAssetsSource to just StreamingAssets and that should work for the built game.
Flashpoint Editor
The Flashpoint Editor already has some pretty amazing documentation available. Amechwarrior has been puzzling it out and writing up his findings in a Paradox forum post. If you're at all interested in creating your own flashpoints, head over to his post and be sure to tell him what an awesome job he did https://forum.paradoxplaza.com/forum/index.php?threads/flashpoint-authors-guide.1153590/
Hopefully this inspires a few people to take a crack at writing their own contracts. I look forward to seeing what you create.
- Eck
If there's a topic you want me to cover about a feature that's already shipped, or if you have any questions for me, feel free to ask.
Tips from your Uncle Eck
Keep your data classes really dumb. Only use them for data or you may find yourself unravelling the code base just to get at what you need.
Make your error messages as informative as possible. Say what's wrong and where it happened. It seems simple, but I've seen too many bad error messages in my day.
Links
- Previous Journal: https://www.gamedev.net/blogs/entry/2267040-battletech-developer-journal-01/
- Next Journal: https://www.gamedev.net/blogs/entry/2267126-battletech-developer-journal-03/
- Tweet: https://twitter.com/Eck314/status/1117515256962723841
Thanks so much for covering this Eck - and for the Parser document!
I particularly like your elegant solution with the error messages, not only showing the line, but also providing actual useful data (such as Salvage must be divisible by 4).
Thoroughly enjoying these Blog entries, both for the BattleTech subject and content that is applicable outside of BT.
I'll second the comments on AMechWarrior's work - what he has done with the FlashPoint editor, just through trial and error (and more than a few questions to the ever helpful HBS staff) is nothing short of amazing!