I can’t believe it’s not BUILD - The Last Exterminator

Feels like a remaster of a boomer shooter that never existed
Date
Updated
Author Fix License CC0

Introduction

I would like to thank Nick for the wonderful opportunity to play the game early, do some testing and ‘virtually meet’ and chat with him about the game. It’s an absolute joy.

The demo is available on Steam. If you enjoy FPS games, especially BUILD games, please go check it out, it’s worth every moment of your time!

Going in, I wasn’t sure what to expect, but the game won me over very quickly: with the main menu.

They say first impressions are important

And The Last Exterminator absolutely nails the landing! The fast-paced music kicks in and asks “what the hell are you even waiting for, you should be shooting some roaches!”. If you’re like me, you’ll already feel home. As I was moving my pointer to the New Game button, I noticed that even the menu is like in BUILD games of old.

This is home, and it’s as beautiful as I remembered it.

Those alien roach bastards will pay

Difficulty? Hard, of course. My ego does not let me play games on anything lower. Immediately, the game communicates clearly - there’s a large, dead roach on the ground, blood and bullet holes decorating the wall and a 1911 in your hand. I don’t know what happened, but I know I must kill.

Taking a look around, reveals gas canisters. If Duke Nukem 3D was not my first game, I might’ve thought nothing of it, but I know clearly what this game wants me to do. Following-up on this, yields rewards in the form of action and more firepower.

That’s as much as I’ll transcribe the events of the game, instead of speaking more generally, because you should really check the game yourself.

Parker, a woman dedicated to her job

Parker is a bug exterminator, working for BuzzKill, which made chuckle quite a bit. The ensuing invasion did not really change her occupation, but it does make her require new tools. And she will comment on them. In fact, she will delightfully comment on almost everything. It may sound tedious, but they’re well paced and short, so they never become annoying. The little bit of her that the demo has shown made me appreciate her greatly, and I look forward towards her adventure through the bug-infested version of our world!

It is impolite to discuss a woman’s arsenal

But I must do it. The pistol you start the game with might seem like a weapon you’ll never use after getting something better, but heed my warning dear reader: that’s a trap. It’s the most precise weapon of the demo, and you can blast bastards across the map.

For more personal encounters there is the genre staple: Double-Barreled shotgun, because of course there is. However, unlike a lot of shotguns, this one does not become useless at a range longer than an arms length. It’s imprecise, but it’s a shotgun, even at greater distances, you’ve sufficiently peppered enemies with lead.

For the rapid-fire slot we have the Twin Viper. We don’t get two of them, but one has two barrels. And it’s really not an understatement to call it ‘rapid’ But the primary fire is easily overshadowed by the secondary fire which fires both barrels at once, although at a slower rate. It delivers a very satisfying amount of damage, very quickly.

Technical Stuff

Let’s be honest, I’m bad at game reviews. That little bit I wrote was just a hook to get YOU to play it, because it’s AWESOME!

What really made me want to write an article on it is the technical stuff.

The Mars Engine

That’s right, ladies and gentlemen, boys and girls - it’s a custom engine! Seeing a custom engine in the wild always makes my coffee-pump I call a “heart” flutter, and this one was a particular joy to explore and talk about.

It’s written in C++ (MSVC++ to appease the pedantic nerds), and while it started with a software renderer, Nick opted for OpenGL later because of a very simple reason: It’s gonna perform better, while looking the same.

Like most of us who write C/C++ daily, Nick has gotten a bit jaded about the state of various libraries, so he has opted for the most sane option:

Trusting no one, but yourself

I was very pleased to hear that Nick is rolling out pretty much everything himself. When I asked about the libraries used, Nick said he could list them all:

And if you’ve never heard of Mars Data File, that’s because Nick created it. Oh yeah, “rolling out his own” extends to the data formats!

Mars Data Format

It’s the most prevalent data type throughout the files. If I had to summarize it in a few words it would be “JSON, but actually good”.

The spec is available here, but I will also briefly explain it, so you can understand what I’m rambling about, even if you don’t read it.

There’s 3 data types that MDF supports: Value, Chunk and Array. The data types are called Element. MDF file is comprised of named Elements, which we then call Properties.

Values are very simple, they are essentially named constants. It’s just something stored as a string, with a label slapped onto it. No nesting.

Chunks store Elements inside themselves They are like Maps/Dictionaries, meaning that they cannot store Elements with duplicate names. It’s not a mistake to do so, but only the latest value will be stored. The MDF file root is a Chunk.

Arrays also store Elements inside themselves. They behave similar to Python’s arrays, meaning that they can store multiple element types. Main difference from the Chunk is that they allow for duplicate names. They also can contain unnamed/anonymous Elements.

This is the backbone of .m3d (the model format Mars Engine uses), .mdf (various definition files) and .map. Most of these are generated by the tools you’d usually see, like Blender, or whatever text editor you use. But .map, while still editable by hand, is technically generated in-engine.

Yes, I’m talking about

The Editor

The amazing thing about the editor is how intuitive it is. It took me about 5 minutes to figure out the basics and make a simple map I can load into. If you’ve ever used a 3D modelling or level editing tool, you also have a decent idea of how everything behaves.

I wanted to make a simple level for the purposes of this article, but I have fallen into the “this could be an actual thing” trap, so this small peek of it will have to suffice.

Levels are mainly built from 2 core types: Geo and Entity. If you’ll remember what you read 2 minutes ago, maps are MDF files. The Map editor exposes the ability for elements to have other elements as children, so both Geos can have children that are Geos and Entities. It’s also possible to make ‘empty’ nodes, that hold Geos and Entities as children, for the purpose of grouping them. Entities and Geos can both emit and receive ‘signals’, which can be used to do things like trigger scripted sequences or spawn entities.

Geo is (presumably) short for geometry. I initially made the mistake of treating them like brushes similar to what idTech-based engines use. While you can certainly treat them as such, considering the fact the editor allows you to move faces and vertices, the editor can do quite a bit more, it’s workflow can be used a lot closer to an actual 3D modelling software. To peek ahead, i’d like to talk about specific entities: lights. Levels have baked lightmaps, meaning that your GPU won’t need to cry just so you could experience some shading. Lights can be organised into Lightgroups, which have an interesting purpose. Suppose that your map is set in a skyscraper. It’s useful to organise your Geos and Entities per-floor. But at some point of the map, the player is going to destroy an electrical transformer, and all ceiling lights should go out. Well, we will need two lightmaps, then: one for all lights on and the other for ceiling lights off. But with our entities grouped per-floor, that would be inefficient, and would require a lot of manual signal wrangling. So, Lightgroups were born. You can add lights into a Lightgroup, without changing your node structure, and then apply animations, toggles and colour tints to the Lightgroup, and the lightmaps are baked a group. At the moment of writing, the only feature I’d consider missing from the Geo manipulation is CSG operations, however, that is on the TODO list for it, so I cannot complain.

That leaves us with Entities, which is everything else: The player, enemies, pickups, etc. Entities have to be defined beforehand using an MDF file. The Editor doesn’t seem to explicitly differentiate between these entities, so, they are all used very similarly. All you have to do is plop the entity down, and they’re ready. You can adjust any signals or flags they might have, e.g. enemies have an ‘Ambush’ flag defined, which behaves like Quake’s ‘Ambush’ flag, making the enemies inactive until they actually see the player. My favourite entity has to be the arcade machines, because it’s just incredibly amusing to me that a video game has a functional arcade game working in it. The games that the arcade machines run is running hand-programmed custom assembly. Oh yeah, I really wanted to talk about this: the arcade machines are fantasy consoles!

NanoVM

Where do I even start?! Nick, the madman, made a VM that behaves like a fantasy console, and instead of just keeping it to himself, or sharing it as it is, he decided to use it for arcade machines!

It’s running custom-made assembly, called currently Mars Computer Assembly, and it’s inspired by NES’s z80 implementation. I’ve not used z80, and I will try to connect the ideas to higher level langauges more people are familiar with, so I apologise in advance for strange comparisons. I’ve somewhat avoided asking Nick about this, as I like to explore things by myself, first, write down any assumptions, and then check how correct I was. So, here on out it’s observations and assumptions!

Data and Routines are divided into 2 ‘sections’, with routines starting after the user defines a ProgramSpace location.

It has some C inspirations with keywords such as .include and .define. The ‘variable’ syntax is Pascal-like, first starting with the label, followed by size, but there’s no equals sign used.

pos_x: byte 0

It seems to be able to embed png, wav and csv files into variables using .incpng, .incwav and.inccsv. The reason I say “into variables”, is because the syntax is like that of a variable, but instead of the type and value, you’d use one of the includes and path.

george: .incpng "path/to/george.png" 

Strings are a type of their own! Huzzah!

my_str: .string "This is a string!"

The asm supports subroutines, by just declaring a label, and moving to a new line

My_Subroutine:
  INC some_var ; increment a variable
  RTS ; return to wherever this subroutine was called from

There are struct-like data blocks, that look similar to subroutines, but only store variables. This is possible because they’re declared in the data block

player_pos:
  pos_x: .byte 0
  pos_y: .byte 0

And there’s much more I’ve yet to cover about it. NanoVM is going to get a lot more love, in an article I plan to do, which is all about VMs!

In Conclusion

When I started writing this article, a single person writing their own 3D engine to make their own games was incredibly and unbelievably rare. Whenever you announced to the world that you’re making a game on your own engine, it was not uncommon to hear: ”Why would you ever do that?!”.

In the mean-time, Unity announced (and slightly rolled back) their Runtime Fee and TOS changes. And while most people flocked to either Unreal or Godot, some set out to make their own engines. This article is a glimpse into the past where I thought that handmade engines are a dying species and I wanted to honour them and their creators, for putting an immeasurably large amount of effort, passion and love into making their own games, and looking awesome while doing it.

But the future of handmade engines got a bit brighter. So, I’d like this article to stand as a testament to Nick’s (and people like him) defiance at “the time of engine convergence”. And I want it to inspire you, to make your own tools. Not necessarily a game engine, but something that you would use in your daily life.

Thanks

Once again, I’d like to thank Nick for using some of his precious time on me and my numerous questions about the engine. He’s even helped me implement my own MDF parser! The best way to support The Last Exterminator and Nick is by playing the demo and wishlisting the game on Steam!