Lachlan's misadventures in games programming

Friday, 24 August 2012

ADOM Resurrection

8/24/2012 10:18:00 am Posted by Lachlan , , No comments
I'm trying to get a bit of support for what is one of my top 3 games of all time (with Portal 2 and Grim Fandango): ADOM, or Ancient Domains of Mystery as it tends to be known to its friends.

ADOM is one of the biggest roguelikes that there has been historically (in fact, so big that it even had its own newsgroup), and differs from most other roguelikes in its consistency of atmosphere and storyline. Fwiw - a Roguelike is a generally text-graphics based RPG game, generally with randomised dungeons, permanent death and a ruleset fairly inspired by Dungeons and Dragons. They also tend to be hard. Really hard.

Biskup's already got the base $48000 raised, and is currently up to $52268 as of writing with 7 days remaining. If it hits $55000, he'll commission a full tileset (sample below). Beyond that, there will be more races and classes added ($60000), a hopeful Steam/Desura version ($65000) and beyond that further quests.

I'd really love to see this make it to at least getting Chaos Knights as a class!

Can I encourage anyone remotely interested to check it out?

The campaign page is on


Saturday, 14 July 2012

TBA Progress

7/14/2012 04:53:00 pm Posted by Lachlan No comments

Progress. It's beginning to look and feel kind of Gamey!

I'll be referring to it as "Starship" for now until a final name is picked. Assuming that I keep going with it.

TBA Project

7/14/2012 02:04:00 am Posted by Lachlan No comments

Just some screenshots from a little project I've been working on.

We'll see what comes of it.


Thursday, 1 March 2012

Playable Levels

3/01/2012 09:02:00 am Posted by Lachlan , No comments

Yup. 7 of them. (Or 6 of them and an intro). And there will be more very soon.

I've put a hiatus on my crappy attempts at level editors, and typed them out in VS's text editor. Initial feedback has been good :)

If you're interested in trying the levels so far (in an alpha state), please pop me an email to lachlan atsign or comment on this post

Thursday, 23 February 2012

Nerdy Gentleman on Gamer Law

2/23/2012 03:46:00 pm Posted by Lachlan , , , No comments
I just my first article put up on Jas Purewel's fantastic blog GamerLaw.

In it, I discuss the current state of the R Rating for games in Australia. The introduction is reprinted below:
There is something rotten with the state of gaming in Australia. Unlike other entertainment mediums, games can not be given an R18+ rating or classification. This has two practical effects: Games that are deemed too mature (generally due to violence or sexual references) are banned, making their sale illegal, their importation illegal and in Western Australia, their possession illegal. Other games that have been generally rated R18+ equivalents in other markets (such as The Witcher 2) are downrated to MA15+ to get them through the system.
Find the full article on

In other news, I got sick of my crappy GUI controls inside Jack's editor. So - I have written network support which can be compiled in or out to provide access to the command interface (which is all the GUI did anyway), and the current state of memory. Then I'll be able to write a fairly simple companion (read: symbiont) program to provide the functions of the editor. I'm thinking I'll probably do it in C# simply because the GUI is pretty easy to program.

Always remember: If you ever find yourself custom coding your own GUI from scratch, its time to step back and do something else. Most OSs provide a decent one. And I can guarantee it is a lot easier to maintain, and far less buggier.

Friday, 17 February 2012

Jack can walk!

2/17/2012 04:06:00 pm Posted by Lachlan No comments

I've added the ability to use sprites. Jack can now actually walk!

Wednesday, 15 February 2012

Working Borders

2/15/2012 03:13:00 pm Posted by Lachlan No comments
I've finally added borders. It took far longer then intended, but they work. Below is an actual (not mockup) screenshot. Click for more detail

I have a special script file called "styles" which I define each style. Then, the feature has a string identifying which style it uses.

I'm happy that this is slowly coming along!

Quick update on graphics

2/15/2012 01:46:00 am Posted by Lachlan No comments
Quick Jack is a fool update:
I've got tiled backgrounds working on features. I might add the ability to offset the image, but not necessary. I also have sprited backgrounds that display once (as I put in yesterday).

I'll be adding borders tomorrow.

Tuesday, 14 February 2012

Form Code, and Squishing

2/14/2012 12:52:00 am Posted by Lachlan , No comments

I got squishing working! The White box (which will soon be replaced with actual sprites) can now detect being squished vertically or horizontally, and call a script.

In other news, I've started implementing actual graphics for the levels. How I'm doing it, is each feature will have a string identifying its style. The styles are stored in structs like the following:

struct FeatureStyle
SDL_Surface* surface_;
FeatureStyleMode mode_;
SDL_Rect middle_;
SDL_Rect left_edge_;
SDL_Rect top_left_corner_;
SDL_Rect top_edge_;
SDL_Rect top_right_corner_;
SDL_Rect right_edge_;
SDL_Rect bottom_right_corner_;
SDL_Rect bottom_edge_;
SDL_Rect bottom_left_corner_;

The mode_  simply defined if its just one, tiles, or tiled with borders. The SDL_Rects define where each part of the image is. The problem is, that this has just led to the biggest piece of copy-paste form-code in my recent history. I've been discussing over at StackOverflow whether there is a better way . There is a good chance I'll implement Konrad Rudolf's excellent suggestion. You can find the discussion on if you feel like contribution. It follows, unadulterated.

case M_AddFeatureStyle:
std::string descriptor = TrimQuotes(args[0]);
std::string file = TrimQuotes(args[1]);

FeatureStyleMode mode = (FeatureStyleMode)boost::lexical_cast<int>(args[2]);

SDL_Rect middle = RectZeroes;
if (args.size() >= 6 ) 
middle.x = boost::lexical_cast<int>(args[3]); 
middle.y = boost::lexical_cast<int>(args[4]);
middle.w = boost::lexical_cast<int>(args[5]);
middle.h = boost::lexical_cast<int>(args[6]);

SDL_Rect left_edge = RectZeroes;
if (args.size() >= 10 ) 
left_edge.x = boost::lexical_cast<int>(args[7]); 
left_edge.y = boost::lexical_cast<int>(args[8]);
left_edge.w = boost::lexical_cast<int>(args[9]);
left_edge.h = boost::lexical_cast<int>(args[10]);

SDL_Rect top_left_corner = RectZeroes;
if (args.size() >= 14 ) 
top_left_corner.x = boost::lexical_cast<int>(args[11]); 
top_left_corner.y = boost::lexical_cast<int>(args[12]);
top_left_corner.w = boost::lexical_cast<int>(args[13]);
top_left_corner.h = boost::lexical_cast<int>(args[14]);

SDL_Rect top_edge = RectZeroes;
if (args.size() >= 18 ) 
top_edge.x = boost::lexical_cast<int>(args[15]); 
top_edge.y = boost::lexical_cast<int>(args[16]);
top_edge.w = boost::lexical_cast<int>(args[17]);
top_edge.h = boost::lexical_cast<int>(args[18]);

SDL_Rect top_right_corner = RectZeroes;
if (args.size() >= 22 ) 
top_right_corner.x = boost::lexical_cast<int>(args[19]); 
top_right_corner.y = boost::lexical_cast<int>(args[20]);
top_right_corner.w = boost::lexical_cast<int>(args[21]);
top_right_corner.h = boost::lexical_cast<int>(args[22]);

SDL_Rect right_edge = RectZeroes;
if (args.size() >= 26 ) 
right_edge.x = boost::lexical_cast<int>(args[23]); 
right_edge.y = boost::lexical_cast<int>(args[24]);
right_edge.w = boost::lexical_cast<int>(args[25]);
right_edge.h = boost::lexical_cast<int>(args[26]);

SDL_Rect bottom_right_corner = RectZeroes;
if (args.size() >= 30 ) 
bottom_right_corner.x = boost::lexical_cast<int>(args[27]); 
bottom_right_corner.y = boost::lexical_cast<int>(args[28]);
bottom_right_corner.w = boost::lexical_cast<int>(args[29]);
bottom_right_corner.h = boost::lexical_cast<int>(args[30]);

SDL_Rect bottom_edge = RectZeroes;
if (args.size() >= 34 ) 
bottom_edge.x = boost::lexical_cast<int>(args[31]); 
bottom_edge.y = boost::lexical_cast<int>(args[32]);
bottom_edge.w = boost::lexical_cast<int>(args[33]);
bottom_edge.h = boost::lexical_cast<int>(args[34]);

SDL_Rect bottom_left_corner = RectZeroes;
if (args.size() >= 38 ) 
bottom_left_corner.x = boost::lexical_cast<int>(args[35]); 
bottom_left_corner.y = boost::lexical_cast<int>(args[36]);
bottom_left_corner.w = boost::lexical_cast<int>(args[37]);
bottom_left_corner.h = boost::lexical_cast<int>(args[38]);



Friday, 3 February 2012

Button Bar

2/03/2012 05:36:00 pm Posted by Lachlan No comments
Short post:
Logic on the button bar works! You can now click a button and it will execute a script from a file.

(Slightly later more info edit)
I've added else's to the script interpreter. I also added a couple of "status" user fields that I can store information about features on. So now, the blue button can make a platform go up and down by being attached to the following script file being called on button_down:

i 0
f_1 status1
f_1 motion = "m -19 0 0 -1000"
f_1 status1 = 1
f_1 motion = "m -19 500 0 1000"
f_1 status1 = 0

Wednesday, 1 February 2012

Button Bar

2/01/2012 06:22:00 pm Posted by Lachlan No comments

I've added buttons. They can be programmed, and with tomorrow code will work.

There can be any number of them (well, up to 254 anyway). They can be created within the game with commands like
Game add_button "Button_Down_Pic.png" "Button_Up_Pic.png" "ButtonDownScript" "ButtonUpScript"
This is  important so I can make levels work.

But, in a bit of a rush - so expect update soon.

Monday, 23 January 2012

Logical Script Interpreter

1/23/2012 12:55:00 am Posted by Lachlan , , , No comments
I've finally added some logic to my script interpreter "Puppet Master"

OK. Script interpreter is a bit of a misnomer. It more can accept commands for classes (like the game, the player or a platform), and change variables in classes. So, for instance, the following would change the platform f_1's x1 to 10
f_1 x1 = 10
I've taken the laziest reasonable possible approach to making this into as much of a language as I need: I've tacked on a basic stack - like on a reverse polish notation calculator (see So whenever I get a value from somewhere (like getting f_1 x1), it now drops it on the stack, which goes up to 6 numbers - for really no good reason other then it was simple to code and won't take much memory.

However, this still left how to put "literals" on the stack. So - if I wanted to directly put the number 5 rather then part of another one. This would violate the relatively simple parser which looks for an object name (defaulting to the puppetmaster itself if it can't find it), then the member and triggers/sets/looks at the variable. So I added 5 basic commands to the puppetmaster that simply say the type of literal you want. So to add the literal integer "234" to the stack, you would go
i 234
I'll add some arithmetic and other commands soon to allow better use to be made of the stack.

Now - how I'm handling the very basic logic (which I'll extend as need be): I've got two commands that deal with it: if_equal and endif_equal takes the top two elements off the stack, uses boost::lexical_cast<std::string> to compare them, and if equal keeps going. If not, it increases the new variable inactive_blocklevel by 1. In general, if  inactive_blocklevel = 0, the program processes the line as usual. However, if its any greater, it  will ignore any lines other then the ifs (which will increase it again, to allow nested ifs), and end (which decreases blocklevel by 1, meaning that if its gone back to 0, code will be running again). The point of this is that it can allows ifs inside of ifs. It doesn't allow the possibility of elses at the moment though - its a really fairly simple method of parsing that will do its job in this program. I'll be adding more versions of if to make up for the lack of else.

Edit: 3/02/2012
Turns out lacking else is very limiting. Hence, I added it - and it was simpler then I thought. All I did is after checking for end and if_equal's when inactive_blocklevel > 0, I added a further check that if inactive_blocklevel = 1 and the command was else, drop the inactive_blocklevel. And, vice verca, increase it if inactive_blocklevel = 0.

At some stage, I will have to write down the details of the scripting language. But it is mostly simple, and is very limited in power. However - its adequate for its job of describing the logic inside levels, and would probably continue to serve for any other games I make with a modified version of this engine. It is a bit limited by my abilities from when I started writing it though and I would probably do it different if I'd started now. But refactoring is the root of all unfinished indie games.

As you may have noticed as well from the picture, levels can now have backgrounds. This took a little longer then expected, but works well.

I'm going to start adding some of the buttons soon. I might add another function to allow BASIC style subs to be called - or at least extend the load script from file function so I can have multiple functions in a file to prevent needing to have stacks and stacks of files for a single level. I hope to have a level in two files - one of scripts, and the other of the level. GFX/Sound will be stored separately.

Also need to code the stuff to make a teleporter work as it's needed extensively in the later puzzles. But have to work out priorities. Still need to code the stuff to turn Jack into a sprite rather then a white box, for instance. Or do a few of the levels as mockups after I add buttons. We'll see. Just - the more engine I do before doing levels, the less I'll have to redo levels.

Still trying to figure out when to aim for a release. Considering aiming for mid to late February - depending if a couple of other commitments arise or not. Would prefer earlier but unsure if it's tenable. Either way, the foundation is slowly being laid.

Monday, 9 January 2012

Linux Support

1/09/2012 06:43:00 pm Posted by Lachlan , No comments

I've successfully got Jack to compile and run on Linux (screenshot below).

As I've got no familiarity with linux C++ development, I went for the Code::Blocks IDE which I found more then passable. I compiled on Mint-Linux as it has been my distro of choice, bearing similarities to both Debian (with use of APT for packaging) and Ubuntu (from whence it originally came) but somewhat lighter. I ran it inside Virtual Box.

I spent a decent chunk of time recompiling and getting headers and libraries etc. as the repository had old ones. I also spent a significant amount of time fixing up the little things that VC++ lets you get away with. And file names. I had to fix a lot of file names in #include's due to Linux's case sensitivity.

I also made some progress with the memory leak: I got tools to find it. I've installed Visual Leak Debugger which seems to work fairly well - but missed the majority of the leaks. I recompiled SDL with Visual Leak Debugger (which was more "fun" - see but then it worked perfectly. And dumped pages and pages of allocated memory going to SDL surfaces and the like. Whoops.... that'll be a fix job for a little later.

Moving Platforms!

1/09/2012 12:59:00 am Posted by Lachlan , , , No comments

Three pieces of news for Jack:

1) I have a memory leak the size of the titanic. Specifically, in the editor, probably in the gui stuff. I'll have to fun around with some duct tape to fix it later. And quickly. The editor has always gone blank after a little while. Thought it was a little bug. Until I checked the task-manager to find my little game had "borrowed" more then half of my RAM, and I was completely out. Really needs fixing.

I can type a little string in, and a platform will follow the orders. For instance:
W 5 M 100 0 50 0 X 3 R 0 0 -50 0 X 4 R 100 0 50 0

  • Wait five second
  • Move to (100, 0) at a velocity of (50, 0)
  • Wait 3 seconds, then put another wait 3 seconds at the end of the queue
  • Move to (0, 0) at a velocity of (-50, 0), and put an identical move at the end of the queue
  • Wait 4 second , then put another wait 4 seconds at the end of the queue  
  • Move to (100, 0) at a velocity of (50, 0) , and put an identical move at the end of the queue
  • Wait 3 seconds...
Its a really simple script. The idea is that there are really 4 commands: Wait (w) , Move (m) , Wait and Repeat (x) and Move and Repeat (r). Wait and Move commands just disappear after running. The repeating versions add themselves to the end of the queue. So - if I wanted a platform to move between two platforms, then move back and repeat, I would use 2 move and repeat commands, and they would just keep moving.

In absolute fairness, they are not entirely coded. I need to now code the behaviour of the actors attached to the platforms (or touching them) so it is predictable.

I'm considering setting a release date for an Alpha - giving me an absolute definitive time goal. I'm considering either the end of January, or midway through February. Problem is, whilst code is reaching completion (or at least approaching it slowly. I find it difficult to judge...), nothing else is - save for most of the design of the levels. And I'm really not sure how long it will take to use my crappy level editing tools to make my designed levels a reality. And I have no sound or music (or programming for such). And haven't programmed graphics beyond coded rectangles. May not need much work on graphics though - as I have done most of it (I think) in a blueprint style that will look similar to my last post, with a dock on the bottom with large, multicoloured, vector drawn, half top down buttons.

Current code stats:

Files 75
Lines 6072
Statements 3549
% Branches 23
% Comments 3.1
Class Defs 34
Methods/Class 9.71
Avg Stmts/Method 6.6
Max Complexity 46
Max Depth 9+
Avg Depth 1.93
Avg Complexity 2.9
Functions 12

* I don't think it's really news. Hence not counted.