It has a lot in common with developer Big Robot’s previous game, Sir, You Are Being Hunted, a first-person stealth-em-up set in a procedurally-generated pastiche of the English countryside in which humans are pursued across bleak rural and post-industrial landscapes by aristocratic robots and their mechanical hounds. It’s like Tory Britain, but with the possibility of escape.
The planet Tölva is less Doctor Who and more Roadside Picnic. It’s had a rough time. At some point in its past debris from countless wars crashed onto its surface, leaving only traces of what was there before, and now the drones of three warring factions fight over the remaining spoils in a remote-controlled conflict, searching for something. That’s where you come in.
You’re hacking into the drone network of one of the factions, hijacking their automata so you can explore the planet and discover more about the titular Signal. That’s where the mandatory story stops and your freedom to get on with the business of playing the game begins.
Most of what you do is walk and look at things. The experience of simply traversing Tölva’s scarred hills and rubble-strewn valleys is remarkably intense. There are moments of stillness during which you warily scan the distance for threats or watch some of the planet’s wildlife sail serenely across the sky. Then a distant battle breaks the silence, as shockwaves from explosions echo off the stones, and the austere synths of the game’s soundtrack give momentum to your hike. Entering one of the map’s numerous strange places, your vision and hearing become warped and glitchy, sometimes fantastically so, and you feel a bit like the pilot of a deep sea exploration drone. It’s very reminiscent of some of the best times I had with the Stalker games.
Everything is styled after the magnificent concept art of Ian McQue, giving the feeling that you’re walking through a painting that’s been masterfully brought to life. It’s so eminently screenshot-able that I amassed a hundred or so screenshots in my playthrough. Picking out the ones to use for this post was quite a challenge.
Big Robot’s approach to bringing the McQue’s art to life is worth noting. In one video and accompanying blog post the developers discussed how they deconstructed McQue’s style, identifying the components of his ‘visual vocabulary’ from which create the modular art assets used to construct a game world. That kind of craftsmanship, attention to detail and creative pragmatism is on display all over The Signal from Tölva. It’s a fine example of how a small team of savvy and skilled-up individuals can come together to create impressive work, and absolutely worth reading about in their blog posts and various interviews.
Bots Behaving Badly
Eventually you will have to fight. In Sir combat was something to be avoided, a frantic and unfair last resort that usually ended with your death. Here combat is inevitable, necessary, and you’re much better equipped to take part in it. Your hitpoints regenerate and the price of death is low – when your current host is destroyed you just hijack a new robot body to inhabit at any strategic point controlled by your faction.
That doesn’t mean charging headlong into the fray is a valid strategy. Battles you don’t plan are usually battles you don’t win. Combat is methodical, with a high time-to-kill and tension loaded into long reload and cool-down times. Enemy and allied robots all use the same set of weapons and tools as you, so you’ll never be confronted with something you yourself can’t bring into battle.
Progression mechanics are of the sort people are probably used to from mainstream open world games, albeit limited in scope. Completing missions unlocks new weapons and powers, while the resources needed to purchase them must be salvaged from wreckages and by killing enemies. There’s not a lot of variety in the range of weapons on offer – there are five main archetypes and the arsenal is padded out with slight variations of them at different levels of effectiveness – but it’s enough to scrape by.
One of your options is the fantastically named ‘SQ-UID Phreaker’, a tool that gives you command of robots from your own faction. You have limited control here: you can tell them to follow you or move to a particular location, but not as individuals. You can’t customize the loadout of your squad members, so if you want to build the perfect team, you’ll have to assemble it by luck. Nonetheless having a squad of robo-buddies at your back as you march across the map is a comforting thing to have in what would otherwise be a very lonely, isolating game.
It’s very easy to digest. Playable in small chunks, it’s always possible to make some measurable form of progress in 15 minutes, or at least to go on a pleasant hike. Hoovering up some resources from a salvage site, searching for rarities and nuggets of story in nooks and crannies, even striking out at an enemy bunker in an attempt to capture it: all possible within a short time-frame, each its own mini-adventure with potential for surprising encounters. Dynamic, un-scripted stuff just happens without any player input: squads of robots get into scraps with one another, capturing and re-capturing territory from each others’ factions, including your own.
The planet seems to be waking up, or perhaps just turning over in its sleep. Like the irradiated and alien Zone of the Stalker games, when it is not actively hostile to your presence, it is at best merely indifferent. It appears to be a graveyard for much older things than the crashed war machines that litter its surface. It’s difficult to figure out what’s going on and why because the scraps of information you find about the world you’re in are brief and sparse. What you do uncover as the game goes on is eldritch and unsettling. Cosmic horror for the information age. If I ever do understand quite what The Signal from Tölva is about, even just on the surface, it will have taken me a while to reach those conclusions. I welcome that mystery.
Sir, You Are Being Hunted never grabbed me like this, never left such a dent. Not many games do. Actually, honestly, I think that what Big Robot have created with Tölva deserves to be celebrated as a classic in its genre. It’s smartly made and smart to play, beautiful to look at and haunting to listen to. I hope you give it a go.
As for me? Well, there’s a free expansion on the horizon and I fully expect I’ll be sucked right back into Tölva the moment it arrives.
I chose to try Premake instead of a similar tool called CMake because while CMake looks like it has more features and is more stable than Premake, using it requires learning a custom scripting language1. In Premake you write your build instructions in Lua. I’m not some kind of Lua expert – in fact, when I started on writing my Premake script I found I’d forgotten quite a lot about the language – but I have enough experience with it that I can get things done.
The Premake workflow is pretty simple: you write a Lua script which specifies what files are part of your project, the include directories, library directories and linker inputs, and so on. Premake imbibes your script and spits out .vsxproj files, makefiles or whatever your target build system uses.
For example, I have a file named ‘premake5.lua’ in the root directory of my project. It looks a bit like this. When I run Premake like so:
C:\Made\Up\Path> premake5 vs2017
It automatically finds the Lua file (because of its special name) and generates a .sln file and a bunch of .vcxproj files for my target build system (vs2017 – Visual Studio 2017). The solution is set up the way I specified, with different configurations (Debug/Release), platforms (x86/x86-64), compiler switches and so on.
Premake solves a number of problems I have:
Visual Studio’s GUI for modifying build settings is impressively bad, and the format for .sln and .vcxproj files is eye-destroying XML, so if you want to change something there’s a lot that can go wrong. With Premake, I make changes to one easy-to-read Lua script, run the tool, and can get straight back to the real work.
As a result of the above problem, changing the way my solution/project is set up is a massive pain in the butt. With Premake it’s so much easier to move files around in folders, split things apart into sub-projects, etc.
Eventually I will be going cross-platform with this project. With Premake I don’t have to set up ten different kinds of build configurations for every different build system or IDE and I don’t have to remember all the differences between the compiler and linker options on each toolchain. People working on the project on different systems can just run Premake and get going. At the moment I’m only using one build system and not taking advantage of the cross-platform capabilities of Premake, but I still make gains in that I no longer have to think about the settings of my various projects because Premake abstracts all that away.
Using Premake it was pretty easy to do the long-overdue work of reorganizing files in my mess of a project. I wish I’d known enough about Premake to use it from the beginning and in past projects. Oh well2!
In the game engine I’m working on, an Entity (or one of an Entity’s components) can sometimes be in a state where it needs to have some extra processing done. Rather than having a branch in Entity::Update, we can just store a pointer to the Entity/Component in a container along with all the others that meet the same criteria. This is existence-based processing.
For example, RenderComponents can be in a state where they own a fixture (shape) in the physics world which needs to be rotated to face the camera before rendering happens (don’t ask me why at the moment; it’s complicated). We refer to these special RenderComponents as ‘Flat Sprites’, and keep track of all of them in a std::vector of pointers:
It’s safe to do this because RenderComponent instances are dynamically allocated and do not move around in memory.
Just before rendering, we call RenderComponent::UpdateFlatSprite on each of the instances pointed to by the pointers in the container, rotating their fixture to face the camera. When adding or removing a RenderComponent from the set of Flat Sprites, we can use std::find to check for whether the pointer we’re trying to add or remove is already in the array:
// flatSprite is of type RenderComponent*
constautoit=std::find(mFlatSprites.begin(),mFlatSprites.end(),flatSprite);if(it!=mFlatSprites.end())// the pointer was found in mFlatSprites
Easy! Yet, there’s something a little bit smelly about all this: we’re using pointers instead of references.
(In case you’re unaware, references in C++ are just pointers with special semantics: they cannot (without coercion) point to null – they must always point to an actual instance. They also behave syntactically like values, so you can act upon them as you would a normal instance using the . operator instead of the -> operator (and dereference operator *) that you have to use with normal pointers.)
The problem with references is that this doesn’t compile:
This is (partly) because references cannot be default-initialised. That is, a reference must always be assigned a value in the place where it is initialised.
But I want an array of references, because I want to represent the idea that elements can never be null. Fortunately, I can do this by writing a structure that ‘wraps’ a reference, and creating an array of that. I don’t even have to write that wrapper myself, because it’s already a template in the standard library: std::reference_wrapper.
Stay with me now, because I’m closing in on the point of this post. This solution creates a new problem: what should the following code do?
// flatSprite is of type RenderComponent&
Should it compare the instance each element in the array refers to with that referred-to by flatSprite? Or should it compare the values of the underlying pointers involved – the addresses at which the RenderComponents reside – which is what I want?
Turns out the implementers of std::reference_wrapper didn’t want to make that call for you, which is a good thing. The code does not compile because bool operator==(const std::reference_wrapper<RenderComponent>, const RenderComponent&) is undefined.
I could just write a definition for that operator myself and make it compare the addresses, but I decided not to. There may be cases down the line where I want that operator to compare the values rather than the addresses, and I want to make it explicit what I’m trying to do here. Finally we’re back to that first line of code.
constautoit=FindByAddress(specialEntities,specialEntity);// except in this case it's actually:
In fact we could make it even more generic and not constrain it to only working with std::vectors by having it take two iterators, a begin and an end. Or, in the bright future, a range. I’ll leave that as an exercise for the reader.
I’ll also leave as an exercise for the reader the task of pointing out mistakes I’ve made, or why an array of references is a bad idea, or generally letting me know what you think of the code I’ve shared here either on Twitter or in the comments below. Can I really refer to my cakes and eat them?
Shout out to Elias Daler for his recent dev log, which alerted me to the existence of reference wrappers and started all of this.
Your friends die. You die. The planet dies. You don’t exactly ‘win the game’ at the end of Halo: Reach’s single-player campaign.
Watching the latest Star Wars film, Rogue One, I was struck by the similarity of its finale to that of Reach1. In Rogue One (spoilers, by the way), the heroes’ self-sacrifice allows Princess Leia to escape with the plans for the Death Star – the film ends almost immediately before A New Hope begins. In Reach, the heroes’ self-sacrifice allows the Pillar of Autumn to escape, with Cortana and the Master Chief aboard, and therefore find the first Halo ring. The last lines of Reach’s script are the first of Halo: Combat Evolved:
Captain Keyes: Cortana, all I need to know is did we lose them?
Cortana: I think we both know the answer to that.
Stories which deploy this conceit have to be prequels – at least, I can’t think of any examples which aren’t – because the only way you can get away with such a bleak ending is if the audience knows how it all works out okay in the end.
I was recently in San Francisco for five-ish days to attend Game Developer’s Conference. It was my first time in an American city – previous trips to the US have taken me only to skiing resorts. We spent most of our time in and around the conference, and only really had time for a bit of proper sightseeing on Saturday.
It’s a nice place, but very different to what I’m used to. In Scotland we barely have any tall buildings, so when I’m surrounded on all sides by skyscrapers I felt a little bit like I was going to fall into the sky, which is the most country-bumpkin thing about myself I’ve ever admitted (on this blog).