This is the official home page for the game Neohack, by Ray Dillinger. When there are updates, they will be here.
Progress report, Game version O.1, Engine version 0.21.
I have never been so happy to be chased by wolves.
Being chased by wolves is normally kind of a downer, unless you happen to be hungry and carrying a gun. I mean, there isn’t much meat on ’em but they come right up to you…. Aw, who am I kidding?! I grew up with guns and near wild land, but I’ve been living in a very civilized area for about 20 years and I don’t even own guns any more. Where nobody I know has any wild land I could go hunting on, nor any problems with predators taking cattle or sheep, nor with pests getting into their feed storage etc, and there’s essentially no danger from wildlife, there’s just no need for guns. If you wanna talk about urban home defense, there’s a huge range of options from baseball bats, mattocks, pickaxes, shovels, etc. all the way up to swords. And anyway I’m not talking here about real wolves.
These wolves – the ones I’m actually happy about – are simulations in the game Neohack. They are represented by little brown Unicode ‘w’s on a terminal screen, chasing after a protagonist whose sign is ‘@’. The reason they make me happy is because they mean I’ve been successful in debugging the module interface so that now it’s possible to add monsters to Neohack without touching any of the engine code.
The module interface was something I wrote in bits and chunks over a long-ish time, but had never really put into use before. There is a fair amount of code in it, and up until this weekend, it was mostly not debugged. I had hoped to work on the inventory system this weekend, but debugging the module interface took longer than I expected.
So, briefly, I will explain how it works. Neohack itself works by initializing a few structures, then putting a game-start event on the schedule, then repeatedly pulling events off the schedule and running them until the schedule is empty. That would be a fairly short run, except that many of the events that go onto the schedule, when executed, put more events on the schedule before they finish.
The schedule itself is a classic Data-Directed structure, because the events are function closures (which you may call ‘promises’ instead depending on your programming language background). Each event is a pointer to a function of a particular type, and a bundle of arguments, assembled like a call frame but not yet called. The actual call happens when the schedule says its time has come.
A ‘sed’ script built into the makefile does simple text pattern matching on the source files to find all the function prototypes for the event functions. It manipulates them into lists, which are then #included in various places where they can be read as C syntax for function prototypes, function pointer constants (ie, the names of routines), Integer constants (ie, indices into an array of function pointers) etc.
Some functions have names that end in ‘INIT’ and the module interface starts up when the game-start event puts an event for each such function into the schedule. Each module can therefore have an ‘INIT’ event, and rely on it getting called when the game starts. That allows each module to insert things into data structures where other parts of the game will pick it up.
The structures that the ‘wolf’ module is interested in are the ones that Neohack uses to define creatures. The goal is for a stack of attributes that defines ‘wolf’ to have a place in the ‘kinds’ table, which will allow a ‘Make-critter’ event to make new wolves, and for a ‘Make-critter’ event having as an argument that entry in the ‘kinds’ table to have a place and probability in the ‘Natural Animals’ table. The ‘Natural Animals’ table is one which the map generator uses to generate creatures when populating its maps. The ‘Natural Animals’ table is one of many generation tables. Others produce things like doors, treasures, different kinds of monsters, tools, food, and so on.
So Action_Wolf_INIT does the following things; First, it defines an attribute stack for wolves. Neohack has generic actors, whose actions, interactions, and capabilities are all defined by attributes. An actor can have any number of attributes. The attributes that define wolves give them things like a speed, a reference to the AI routine they use, a map representation (the brown ‘w’), a size and mass for modeling in-game interactions, and so on. Then it adds the attribute stack, with the name ‘wolf’ and the additional information that it is a ‘species’ kind, to the kinds table, so from now on that ‘kind’ will refer to a species having that attribute stack. Then it creates an event (using the predefined event routine ‘Action_Make_Critter’ which will make two wolves, and puts that event into the ‘Natural Animals’ generation table, with an oddment, in this case, of 1000, which corresponds to a probility of 1000 out of however many oddments the Natural Animals table has when everything has eventually been added to it. And that is absolutely everything that Action_Wolf_INIT needs to do.
The map generator gets called after all the INIT events have run. It calls the ‘Natural Animals’ table to get an event, makes a copy of the event, adds a ‘to location’ argument to that copy so that the created critters will appear in a definite location on the map, and then puts the copy into the schedule as a new event.
When the event is pulled off the schedule, Action_Make_Critter is finally called. It creates actors, copies the requested attribute stack from the ‘kinds’ table into those actors making them wolves, and places each actor at the closest walkable, unoccupied tile to the ‘to location’ still available in the map when that actor is created. Then if the new actor has a ‘speed’ attribute Action_Make_Critter updates the ‘speed’ attribute so they don’t get any actions before the current game time, and schedules their first AI action.
I found lots of bugs while finally putting all this machinery into use. I probably built too much functionality without testing, which made debugging a bit harder, but I did manage to sort it all out. Maybe in another post I’ll talk about the debugging infrastructure I’ve built into it (it keeps a ‘trace’ logfile so you can see what it was trying to do just before it crashed). But anyway, now wolves are chasing me, and that makes me happy.
This is the official home page for the game Neohack, by Ray Dillinger. When there are updates, they will be here.
So… what is Neohack?
Neohack is my contribution to the “next generation” of roguelike games. It should be released in some form early in 2015. This is the first time I’ve mentioned an actual release date.
If you haven’t played roguelike games before, the basic idea is fairly simple. You are a fantasy adventurer, and your chosen task is to penetrate deep into an underworld of chambers, passages, and caverns to find a legendary item and retrieve it.
The most straightforward way to do this is to slaughter those who guard it, force your way into their stronghold, defeat an end boss, and take it by force. Simple, right? Well, maybe not.
Neohack is a tribute to and remembrance of the early computer roleplaying games and the people who initially developed the whole idea of playing games on computers. Just as importantly, they developed the idea of procedurally generated games, as games that people would want to play again and again, rather than scripted games, as stories that there’s no point in experiencing more than once. I intend to continue in that tradition, and I’m first going to use this architecture to revisit a lot of those very earliest games, in many cases re-creating their monsters, treasures, and game mechanics in subgames, branches, or minigames.
What Neohack inherits from virtually all roguelike games.
- It has randomly generated maps, treasures, and monsters. This means that it will constantly throw new stuff at you, even if you’re just playing the first level and you’ve played many first levels before.
- It has pure turn-based play. That means, when you don’t enter a command, nothing happens in the game. You can take as long to think about your next action as you need. When you take an action, time passes in the dungeon. As time passes in the dungeon, monsters get to take actions too.
- It is a single player game. Multi-player games are inconsistent with the goal of pure turn-based play, and in a game that requires a lot of thinking to play well, pure turn-based play is a necessity.
- It is strategic as well as tactical. That means you are making decisions that affect not just the next few minutes, but the rest of the game, all the time. A decision you make now will put you in a different situation all the way to the end of the game, and could even make the game unwinnable, although there will be no easy way to detect that the game is unwinnable. So you have to think about each decision.
- It has permanent death. That means, when you die the game is over. Most single-player CRPGs have the ability to restore and continue after a failure, but that’s because, since they have predetermined abilities, events and plot points, it would be boring to play the first part again. In a roguelike game, the maps, monsters, and treasures are randomly generated, so every game is different and presents new chances and new challenges. Also, because most CRPGs are tactical but not strategic the players don’t have to worry about the kind of long-term strategic mistakes that are possible in roguelike games. In a roguelike, because you’re responsible for strategic decisions, restoring after death mostly just means restoring games that are already in an unwinnable state, and therefore wasting more time before you start a game you have a chance of winning. Character death is merely a moment of transition from one game to the next.
- It is played in the console using text for a graphical display. This isn’t terribly important to the game itself, but it means that there’s a lot of flexibility in how the game can be used and, hopefully, will help make it easy to support additional platforms. As a modernization of this idea, there is full Unicode support.
Design choices Neohack inherits from some of the better examples of Roguelike games, mostly Nethack, Adom, and Dungeon Crawl Stone Soup.
- Monsters have inventories and can use items just like the player can.
- Autoexplore and similar game-automation features to make large levels manageable.
- Autotarget, Autofire, and Autocast reduce most ranged attacks to a single keystroke.
- Items and item types can be named.
- Command-to-key mappings can be changed.
- Persistent Levels. The map and monster populations of each level are determined once. Monsters that have been killed remain killed, tunnels that have been dug remain dug, and walls that have been built remain built. None of these things will change just because you leave a level and return.
- Inventory size is limited by weight and size, not by number of different item types. Small light stuff will make it possible for you to carry more stuff, just as in real life.
- Players have a character class and a skill level with respect to that class which increases during the game, and the combination of class and level determines many of their primary abilities.
Things Neohack brings to roguelike gameplay that are new, or at least unusual:
- There is no tactical or usability advantage to the player in limiting the console to 80×24 characters, so Neohack doesn’t do that. Make the console as big as you want, or within limits as small as you want, and Neohack will adjust its display to use it. A 250 column x 80 line console is entirely readable and usable on modern displays, and Neohack will use that space if it’s available.
- Because character cells on consoles are about twice as high as they are wide, because consoles are typically more pixels wide than they are tall, and because modern displays allow a heck of a lot more character cells than ancient hardware terminals anyway, Neohack assigns two console characters to each dungeon tile. This doesn’t put the nearest out-of-sight squares any further away, because the display will still typically be more dungeon tiles wide than it is tall. It allows the game to display additional and crucial information about the creatures on the visible dungeon tiles. The second character cell is used to show what each monster is equipped with, if anything, which gives you a chance to notice potentially important information such as the fact that one of the gnomes is carrying a wand of death rather than a shortsword.
- Dungeon time in Neohack is continuous, not discrete. Moving diagonally takes a bit longer than moving orthogonally. Individual actions can have the amount of time they take modified by items like speed boots (which make walking faster but not attacking) or a swift sword (which makes attacking faster but not walking). There are few if any integer ratios of movement that you can count so a lot of cheesy tactics based on exploiting simplistic timing just won’t work.
- People often complain that roguelike command sets are too big and too hard to discover; but those huge command sets can be used to support depth of play, and I didn’t want to reduce them at the expense of reducing the depth of the game. Neohack aims to reinforce rather than dilute the strengths of the roguelike genre, and the ability to choose to use items in many different ways is a fertile source of depth, so I made a deliberate choice to support a large command set. Therefore I considered it obligatory to try to make learning the command set easy. That is why Neohack does two things: first, it scrolls a help line across the bottom of the display that shows what the commands are and says what each command does. So if you’re not sure what command you need, all you have to do is wait for it to scroll into view on the bottom line. Second, I’m trying to put a “logical” control schema on the command set, where lower-case letters and digits are for simple common commands, control letters for commands related to your character’s special abilities if any, and upper-case letters for more complicated commands where you may need to make configuration choices or select actions or items from submenus. To the extent that this is successful, players when uncertain ought to have better chances of guessing commands correctly, or at least of knowing which guesses won’t cause a potentially disastrous immediate action.
- Neohack’s Data-Directed architecture easily and without touching main game logic allows complex interactions and multiple-use items which are not possible in most roguelike games and achievable only through extensive case-by-case patching of exceptions into main game logic in other games. Also the code is well documented internally and comes with a “modders guide” which serves as an introduction to the code base and architecture. This affects the ability to modify, maintain, expand, and improve Neohack. From the perspective of players, this means that bugfixes, balancing changes, and new features should be routine and quickly releasable rather than rare and riddled with new bugs.
- Rarities. Certain items and monster types in Neohack are designated as Rarities, with very small chances per game of appearing. That means that even when you’ve won, or played a hundred games and know perfectly everything you’ve ever encountered, you’ll still occasionally encounter new things you haven’t seen before. In many roguelike games, there are “artifacts” – items of such great power that the game limits their appearance to preserve game balance. Rarities are a related idea, but not the same idea. Rarities may appear at any point in the game, and may or may not be particularly powerful. The appearance of Rarities is limited to add flavor and uniqueness to each game, not to limit the potential of ultra-powerful items to break game balance.
Things that interest developers and engineers:
- License: I’m using the Artistic License, which is very close to making a public domain release except that nobody other than me is allowed to release an updated version of Neohack. If you want to make a new game using all of this code, the only thing the Artistic License requires is that you call your game something else. If you want to extract some of the code and use it for something else, or use parts of it in a different game, that’s completely fine. I make no demands, or even requests, about how you license something that contains code you get from this project; if people find it useful, that’s awesome. And if you put restrictions on your project that they don’t like, they can still get it without restrictions from me.
- Language: C. Just plain C. Old-fashioned, hardcore, dangerous, type-unsafe C. Because I like the old-fashioned, hardcore, dangerous type-unsafeness of it, for reasons that will become clear in the next paragraph.
- Coding methodology: Mainly Data Directed, with some Object-Oriented bits. Yes, I know C doesn’t have built-in support for DD or OO programming. C also doesn’t get in my way when I do DD or OO programming. Every other language I know (except Scheme, which I decided against because it would require lots of users to install a new language runtime) that supports either of these methodologies very strongly supports one in a way so hamfisted that it either makes the other difficult or forces me to do the other in a way that’s wasteful, inefficient, or stupid. In particular, so called OO languages get so pushy about making control flow depend on the type system that they make it impossible to have control flow that depends on individual values as DD requires unless you make every single value be the only member of its own type.
- Platform: I’m using Linux development environments, and I expect Neohack to compile and work on all kinds of Linuxes and Unixes without modification. I have no Windows or Mac box to test on so I’m not going to make those binaries. That said, I’m using only the standard C libraries and the widely available ncurses library. As far as I know I’m not relying on things that happen to work on GCC and Linux when those things aren’t standard for C and POSIX. So to the extent that I’ve been successful in avoiding platform-dependent constructions, and to the extent Microsoft and Apple faithfully implement POSIX, it should compile for those platforms without significant modification. Anyway, when I make it available I’ll be putting up a 64-bit Linux binary and source code.
- Mouse & GUI stuff: I will probably add mouse support sometime after the initial release, because a lot of people like that, even though I personally find mice pretty irrelevant as game controls. As time goes on, there’ll probably be a graphical version too, assuming Libtcod or NotEye pass muster in terms of quality, reliability, and not getting in the way. For now making one is an even lower priority than mouse support.