Project A Part 3: From July to October

This post is part of a series. Part two is here.

I had meant to keep a seriously thorough engineering log of my work on Project A, in order to write some nice, spaced out blog posts about it. Instead, I worked my ass off on it for three months and didn’t keep a scrap of notes. So now I’m looking through my commit log to see if I can make some coherent sense of the work I’ve done.

Here’s a great screenshot showing off some of the user-visible changes.

A screenshot showing the latest game UI.

Things that don’t have a full write-up yet: line-of-sight falloff of lighting, individual stats and needs meters, a world map which includes roads, and an inventory which handles stacked items.

World Generation

World generation was improved, massively, between this post and the last. I finally managed to implement Voronoi world generation in a way that was comprehensible to me. I’ve been writing worldgen related code for a while, and a proper Voronoi implementation always eluded me. I am whole-heartedly standing on the shoulders of Martin O’Leary, Amit Patel, and James Humphreys. It’s not completely up to snuff—it doesn’t handle biome changes, elevation, or running water—but it’s a fantastic start, and I can’t wait to add more detail to it.

Time Scaling

I’ve managed to implement proper time scaling, so that crossing a tile in the overworld takes two hours and has all the requisite effects on hunger, thirst, and other status effects.

Once you descend into an overworld tile it’s real time, and NPCs move about and time goes forward in real time. And then once you initiate combat, you’re in a turn-based system like Fallout 2 or Xcom, which makes sure that all things in everybody’s turn happens in a causally-correct order, ignoring time.

This all has to happen in the context of a game loop which may take a different number of milliseconds to update the game logic in each frame, so it will act the same whether it’s going 30FPS or 60FPS.

Before this, the entire game was turn based, so the game loop was completely unattached to the stepping forward of game logic. Now they can be dependent on each other or not depending on where the player is in the world and whether they’re in combat. If you cross a tile in the overworld, it takes “two hours” and your stats are affected as if all that time passed. But if you go into a cell and wander around for two hours (real time, as the clock is ticking) the same effects to your stats will occur. But if you’re in combat, time freezes and only ticks forward based on turns and action points.

And, so that players can see the trail of bullets, all animations (like explosions and bullets moving towards their target) cause all turns to stop until the effects of everything that happened last turn are handled.

The real-time outside of combat is great because it enables stealth. You’ll be able to see NPCs moving about, and their cone of vision, and have areas of light and dark. And if you take someone out and no one notices, combat isn’t initiated, so the flow of realtime isn’t affected.

The practical upshot of all this is that now action points can be implemented, and actions corresponding to a certain AP cost, as you can see below.

A screenshot showing the player-character moving in combat while their action points deplete.

In-Game Console and Scripting

I’ve agonized forever about how to get in-game scripting in a Java game. One day it hit me like a flash: embed a Python interpreter. I’m a huge Python fan, and being able to off-load game behavior, as well as fuck with the game while developing it, was a huge step forward for me. Game scripting comes with its dangers—hiding errors until runtime, for one—but I think the benefits will soon outweigh the disadvantages.

A screenshot of the in-game console, showing a listing of the game’s “Universe” object’s Python attributes.

In Conclusion

Do yourselves a favor and keep regular engineering logs. It will make personal retrospectives and public changelogs so much easier.