Thief is one of my favourite games.
I hung around its modding community in the late 00s, as I was learning to code in order to make my own games.
I heard all about its baroque editing suite, and the quirks of its engine, designed so that Thief’s all-important aural landscape would function plausibly.

Something I continue to think about these years later is Thief’s solid universe.
In most game engines, “the world” of a level is inherently empty, and is filled up with objects by level designers.
This can result in amusing bugs like “falling through the ground” into the void beyond, or visual glitches where you can see “nothing” rendered as a “hall of mirrors”.
https://www.reddit.com/r/gamedev/comments/n6bmh7/what_is_this_rendering_bugeffect_called_when_the/
But in Thief, the world actually started off as completely solid.
It was full of “earth” until a level designer inserted a volume of air to make a room.
The game’s artists carved out levels from this solid universe, tunneling away the rooms, gardens, streets and yes, actual caves, that the player experiences.
Sean Barrett, a lead programmer on the game, describes the process:
The entire space started solid, so one brush operation was “carve out a hole in this area”–in other words “change the area covered by this brush to open”. For example you would use this to carve out a room. Another placed solid matter; you could use this to create a pillar. Another placed water, and another lava. Because space could be of 4 types (solid, air, water, or lava – oh hey, the 4 classical elements!), each operation could be considered by which output type it produced.
Moreover, we allowed these operations to be selective. For example, the “flood brush” turned air into water, but left all other types alone. This made it easier to fill an area with water–you could construct it entirely with air and then fill the lower half with water. Because of the temporal aspect, you could then go and change some of the water to “air” if you needed to. It would have been possible to make brush types that were much complicated (air turns to water, water turns to solid, solid turns to air, and lava unchanged) but this wasn’t actually useful so I think all the brush types were of the unconditional-single-type or conditional-single-type.
https://nothings.org/gamedev/thief_rendering.html#csg
I find this idea somehow comforting to meditate on.
One of the effects in the game itself is that outdoor areas feel almost underground.
Though you can see a sky overhead, it isn’t the infinitely-distant skybox of an empty-universe game; it’s a firmament, a surface you can gauge the distance of, painted with stars.
I may need to replay Thief.
Filed under:techgaming
Welcome one, welcome all to my third and final Monster Mash jam log.
(Here’s the first one and the second.)
Since I got busy during the week, this will basically summarise everything I did between Wednesday and the end of the jam last night.
I’ll also write a bit of a post-mortem about the project and how I felt it went.
I have to report, unfortunately, that I didn’t actually make it into the jam, because a midnight I was fiddling with getting the game’s itch.io page up and working.
However, I did make a game, and I had a lot of fun doing it.
Which is kind of the point of doing a jam, so I consider this mission a success!
So, what did I get up to in the latter half of the jam?
-
Added AI!
Obviously between Wednesday and Monday I wrote basically the whole AI system for tourists (the ones you eat) and rangers (the ones who are dangerous), but initially I just started out by making tourists respond to your movement.
The idea was that if you swim around fast and close enough, they’ll notice you.
You can also make noise deliberately - and of course, attacks causea lot of noise.
I brought in the event-queue code I’d written for vlrtt to simplify this process, so the backbone of AI code looks like this:
eventQueue(Monster);
event(Monster, Swim);
function Tourist::onAdd(%this, %obj) {
subscribe(%obj, Monster, Swim);
}
function Tourist::onMonsterSwim(%this, %obj, %swimPos) {
// %swimPos is the location the monster was at when it made a noise.
}
So now every 500ms, I make the monster emit a swim event by calling postEvent(Monster, Swim, %obj.getPosition())
and the tourists hear it.
They have to do some processing, of course, to make sure they don’t hear things too far away.
Here’s a video from my early AI tests:
-
Controller input!
I spent far too long on this.
I just couldn’t work out why there was no controller input in t3d-bones
despite the controller working flawlessly in the Empty template.
I eventually discovered that you have to assign $enableDirectInput = true
somewhere in your code as well as calling activateDirectInput()
.
Who’d have thought.
-
A help dialog.
It’s barebones, with just enough room to display the controls and a brief instructional message.
It can also be hidden with a keybind.
Yay.
-
More AI!
I conscripted the state machine I wrote ages ago, again for vlrtt
, to give the tourists and rangers some simple scripted behaviour.
They revolve around a simple three-state loop: relaxed
, scared
and fleeing
.
They build up through this process when they hear monstery noises, and they go downwards with time.
Of course, rangers have states alert
and pursuing
instead of scared
and fleeing
, but the state flow is similar.
For example:
new ScriptObject(TouristSMTemplate) {
transition[relaxed, monsterNoise] = scared;
transition[scared, monsterNoise] = getHelp;
transition[scared, timeOut] = relaxed;
transition[_, attackNear] = getHelp;
transition[_, attackFar] = scared;
};
There’s also some fun behaviors built around these states.
When tourists flee, they do so towards the nearest ranger, and then bring the ranger back to the location they heard the monster.
They’ll then all be worried for a little while, and then go back to whatever they were doing.
And if someone becomes scared nearby, they’ll turn to them and ask what’s up.
Kind of unhelpful, but a fun touch.

-
Eating.
You can now actually eat people.
This went in on Sunday, funnily enough.
I think I should have maybe done it much earlier.
On the other hand, it was never going to be a very complicated part of gameplay.
Basically, if you attack and a person is close enough, they’ll delete themselves.
I also prevent you from attacking if you’re under the boardwalk, which I thought was a nice touch.
Originally that would be accompanied by a head-bumping sound, but I didn’t get around to adding sound effects.
-
Being photgraphed!
Yes, on Monday I finally hacked in the ranger’s ability to make you lose the game.
This was a change from my original intent to make the rangers fire tranquilisers at you.
They’re now not park rangers but photographers, and if you attack when they’re nearby and alert, they’ll snap a photograph and cause the game to end.
I don’t have much to say about this, actually.
Other than, again, I probably should have done this a week earlier.
As it is, I didn’t get any time to playtest or balance the range at which photographers capture you, or the circumstances under which they should or shouldn’t do so.
-
More AI…
Since the way the tourists and rangers responded to you was the most important part of the gameplay, I spent a while trying to make the interactions rich.
Unfortunately I didn’t quite succeed, I don’t think.
This was mostly due to the limited navmesh support in Torque currently.
For example, I had no way of checking whether movement target points were on the navmesh, resulting in lots of trial-and-error code like this:
while(!%obj.setPathDestination(chooseRangerSpot(%obj.spot))) {}
where chooseRangerSpot
will choose a random position near the destination each time it’s called.
So you eventually get a valid path.
If you assume, as I do, that there’s a valid location somewhere near the target…
-
Particles! (Lukas would be proud.)
I really spent far too long on Friday making all the various partlcle effects happen.
There’s a small bubbly one when you make a noise to scare people, and a big splashy one when you actually attack.
And a really terrible trail when you move, so you have some visual indication of whether you can be heard.
I was quite proud of how they turned out in the end, basically being the first particle effects I’ve written by hand.
Actually, that’s a complete lie.
But they’re the first effects I’ve written for a long time.

-
An end-game GUI and cycle.
This was something that actually really excited me - figuring out the process of how to end the game completely, deleting all the objects, and showing a GUI, then recreating the entire thing to start over again.
It actually turned out to be fairly easy, but I ended up having to use a commandToServer
to restart the game, although it wasn’t necessary to end it.
It had something to do with calling GameConnection::onEnterGame
:
// After game has ended:
function resetGame(%val) {
if(%val) {
commandToServer('reset');
}
}
function serverCmdReset(%client) {
onStart();
%client.onEnterGame();
}
Anyway, now I’m worried that I understand the client/server interaction in the engine even less thanks to t3d-bones
’s minimal setup.
Maybe that’ll be a topic for a future safari…
So that’s that!
The jam is over and I have a finished product of sorts.
But there was a lot I planned and didn’t get to complete.
Like:
- Boats floating on the lake.
- Counters showing the number of people you’ve eaten, the number remaining, and the number that escaped.
- A way to actually attract people towards your location.
At the moment, tourists who are scared just freeze.
This doesn’t really help you.
I just didn’t have any way to find the closest point on a navmesh to the monster’s position, so I ended up not bothering.
- Better AI in general.
Like fleeing completely after being scared too much, and tourists wandering around between different resting spots.
- Sound!
- An intro sequence, with the camera lurking under the water’s surface, then rising up into the starting position.
A luxury, I know.
And much more besides.
Now that the jam’s over, I’ve had time to bother creating a GitHub repo with my code in it.
Feel free to poke around if you’d like to see a poorly-commented, extremely hacky example of a simple single-player game.
How do I think it went?
I guess I’m happy with how it turned out overall.
Torque certainly held up, and surprised me occasionally with its ease of use.
Like how easy it is to use a gamepad, once you’ve figured out which undocumented global variable you need to enable.
Writing this amount of code again confirmed to me how much I dislike TorqueScript as a development language.
I ended up making heavy use of schedules in some of the code, which meant duplicating calls to schedule
and writing temporary functions outside of where they were needed.
It would have been much nicer if I could have used something like Javascript’s setInterval
with an anonymous function.
I was also bitten several times by the lack of name safety - if I made a typo in a local variable name, I’d get ""
instead of an error.
Not too difficult to debug, but debugging I really didn’t have time or patience for.
It had been a while since I tried to use the COLLADA art pipeline, and this time I found it much improved for some reason.
Possibly because I wasn’t trying to make textures, simply using solid colours everywhere.
I even managed to assign different materials to different faces on the same object!
Tricky stuff.
Also, I believe Blender’s COLLADA support has improved since I last used it, so that may have helped.
So overall I was quite happy with the engine, though I did discover a couple of bugs along the way.
Which is good and bad.
I suspect the engine is rarely used without the padding of the template scaffolds, so some of these issues aren’t immediately apparent.
I also think that the engine is simply not used by many people, and those who do use it haven’t tended to share the massive amount of niggling bugs they’ve had to fix.
Which makes sense, since T3D’s development was closed up until recently.
However, I also found that the engine is just very difficult to work with in script only.
For example, I was writing my own spring physics simulator in order to make the camera work bask in my second post.
Spring physics, honestly, is exactly the kind of thing I think a good game engine should handle - I should be able to attach a couple of springs between the camera and player, and it should jut work.
I also found some inconsistency in the API and implementation of existing features.
For example, the camera’s tracking mode does not alter the camera’s transform, so you can’t actually tell where it’s looking at any given moment without redoing the maths yourself.
So, I think we have some work to do before I would call T3D completely user-friendly - just fising lots of these annoying issues and providing a sane and sensible API with good documentation.
I look forward to the challenge.
Filed under:gaming
Welcome one, welcome all to my second Monster Mash jam log.
(Here’s the first one.)
I’ve got two days to summarise, since I didn’t do an awful lot of work yesterday.
But, I:
-
Made a third-person camera controller with relative movement to the camera, all in script!
This is an ugly hack and one I don’t want to repeat.
The main challenge was the maths involved in converting the local direction we want to travel (e.g., ‘away from the camera’) into absolute coordinates to feed to the player move input.
And then realising that I needed to update that variable regularly, because the direction the player would be moving often changes constantly (when moving sideways relative to the camera).
At the moment, here’s the guts of it:
function updateMovement() {
%axes = getCameraAxes();
%front = getField(%axes, 0);
%right = getField(%axes, 1);
%moveDir = VectorAdd(VectorScale(%right, $right - $left),
VectorScale(%front, $forward - $backward));
$mvRightAction = getWord(%moveDir, 0);
$mvForwardAction = getWord(%moveDir, 1);
}
It’s fairly simple after all that effort!
Note that I had to write my own getCameraAxes
function because the camera transform doesn’t take object tracking into account.
The global variables $forward
and friends represent the states of the WASD keys.
Still to be done: controller input!
-
I also made a spring physics system to keep the camera following the player.
Currently it just uses springs and no dampers, so it kind of sucks, but it works.
It’s also all in scripts, and it’s also something I never want to do again.
That said, I’m thinking of generalising the system so that the islands and shorelines can act as springs as well, keeping the camera aligned more usefully (since at the moment, there’s no way to manually rotate the camera, which sucks a bit).
Again, here is is in full, just so you can share how painful vector maths is in TorqueScript:
function updateCamera() {
// Diff vector in x, y only, since we don't care about camera height.
%diff = getWords(VectorSub(TheMonster.getPosition(), TheCamera.getPosition()), 0, 1) SPC 0;
%dist = VectorLen(%diff);
%diff = VectorNormalize(%diff);
// Attractive/repulsive force from monster.
if(%dist < 18) {
TheCamera.camForce = VectorScale(%diff, -1 * (18 - %dist));
} else if(%dist > 22) {
TheCamera.camForce = VectorScale(%diff, %dist - 22);
}
// Drag.
TheCamera.camForce = VectorAdd(TheCamera.camForce, VectorScale(TheCamera.camVel, -1));
// Physics!
%position = VectorAdd(TheCamera.getPosition(), VectorScale(TheCamera.camVel, 1 / $MovementHz));
TheCamera.setTransform(%position SPC getWords(TheCamera.getTransform(), 3, 6));
TheCamera.camVel = VectorAdd(TheCamera.camVel, VectorScale(TheCamera.camForce, 1/50));
}
And today, I:
- Fiddled a lot with movement physics.
I discovered a big hooting bug that was screwing up character movement underwater.
It wasn’t major, just meant that the chap would always accelerate to full speed instantly.
Which, you know, I could deal with, but I just wanted a bit of smoothness to the movement.
- Added collision to the boardwalks, in order to set up a navmesh, which you can see below.
Apparently, my efforts intended to make the trees and boardwalks un-collide-able didn’t work.
Which I should probably look into, as all those meshes should be hurting performance…

- Made the water not-see-through, thanks to Azaezel pointing out the underwater fog settings. Hoorah!
- Made the shadows less rough, thanks to Steve for sharing his sun settings.
- Another bright suggestion from the #GarageGames IRC channel was to add more (by which I think they meant any) goats.
I’m very tempted… but it will probably have to wait until after the jam.
- Also, thanks to that bunch, the game has a name: “Loch Ness Monster Simulator 2014”.
It has a ring to it.
- Added colours!
After struggling yesterday and settling for a single-colour palette, I played around with the stock colours Lukas added from T2D, and found some that I liked.
This was much easier than fiddling with RGB codes!
Here’s what it looks like now:

- Yes, those are tourists you can see chilling by the lake.
And an umbrella, which I thought was cute.
They’re basically just blob-shapes, but I added arms so you can tell which direction they’re facing.
I think the park rangers will basically just be this mesh, but with hats.
They’ll tranquilise you with… I dunno, mind powers or something.
Anyway, they have no AI yet, but that’s on the agenda for tomorrow…
Till next time!
Filed under:gaming
The first day of my Monster Mash attempt has ended with some fortunate progress.
Though I’m technically cheating (the jam starts tomorrow), I absolve myself by pointing out that I’ll be having a fairly busy week with several days off anyway - and I won’t be actually submitting the game, most likely.
Since I had some time today while travelling, I thought I’d get some progress in early.
I’m using Torque 3D and Blender for everything.
To compensate for my abysmal skills at 3D modelling or anything artstic, I’ve decided to play off the theme of the jam (monster protagonists) and make a game where you can’t see your protagonist.
The idea is you’ll be a sort of ‘swamp thing’ or lake-dwelling lurker, but you’ll see the game from above the water’s surface.
The gameplay will have you slinking around, trying to snatch tourists and park rangers unawares from the lakeside.
I originally wanted to work a mechanic into it where your view would lose track of where you actually were in the lake (gradually, the inaccuracy increasing), and you’d then have to blow bubbles to recalibrate yourself before launching an attack.
The idea being that making bubbles would alert rangers and tourists to your location, so you had to carefully balance knowing where you are with everyone else knowing where you are, too.
I ditched this idea pretty soon - it would be too hard to balance, and didn’t actually sound incredibly fun.
Instead the game will be a little more straight stealth - get too close and you’ll be noticed, etc.
Anyway, here’s what I got done today, mostly in gaps between getting on and off aeroplanes:
- Drafted the single level the game will take place in, first on paper then in Blender.
It includes a lake, two islands, a boardwalk, and lots of pine trees.
So many pine trees.
Here it is so far!

- Learned how to import stuff into Torque from Blender.
I feel this isn’t something to be all that proud of, because people manage it all the time.
I’ve always had trouble with it, however.
But now my meshes are going into the game as they should.
And while I haven’t tried to work out texturing yet, I’m feeling much more confident about the whole process.
- Learned how to use
WaterPlane
settings.
Seriously, there are a lot of them.
I mostly futzed with the array of ripple textures to get a really good ooze-like look, like the deadly sludge from Portal.
Still to be done in this area: make the water less transparent.
At the moment, it’s completely see-through, which I don’t want.
- Re-learned how to get the game to capture mouse input directly (spoilers: it’s not about your
ActionMap
, you have to set noCursor
on your GameTSControl
).
- Fiddled with separate player/camera input schemes.
I discovered separating the player and camera is easy enough - you just call
setFirstPerson(false)
on the connection and it starts to view through the camera.
It gets trickier when you want to to any sort of control of both of them.
My first step now is to make the movement relative to the camera, rather than absolute.
I’m sure there’s a resource out there for that…
Thoughts:
- I’m enjoying being able to actually create stuff in Blender and put it in the game.
Not something I’ve done for a long time :P.
- I probably should be doing less manual Blender-ing of stuff like trees, and making use of the shape replicator, since that’s what it’s designed for.
I didn’t do that originally just because I was excited after having finished the boardwalks using instanced geometry, so I ust continued on doing the trees in the same fashion.
Hopefully now the level is relatively finalised I won’t have to worry too much about it.
Also, I can’t benefit from using LODs this way.
As far as I can tell.
- I messed up my export process by deciding part-way through that I should really only have part of the level geometry be collidable - i.e. not the trees or the details of the boardwalks.
I solved this quickly by just exporting different parts of the level as two different COllada meshes.
This wouldn’t be a problem if I were designing the level in T3D using replicators and placing the boardwalk instances manually, since each of them could have their own collision meshes.
Filed under:gaming