[i7] Generating a random map at start of play

Hello, everyone. Newbie to Inform and programming in general here. I’m working on a Roguelike-like game to help me learn the language, but I can’t figure out how to create a randomized map for it. I’m aware of section 3.2 in the manual, but none of the examples are quite what I want.

I’d like a map that is generated at the start of play (so that it works with a compass in the status line, and allows for wandering monsters), has two-way connections, and allows me to set the directional exits of particular rooms instead of just letting the player go in any direction they want and building the world around that, as in All Roads Lead to Mars (so, for example, the Barracks room can be created with exits to the north and west and the random generation will place it somewhere between a room with a south exit and a room with an east exit. Possibly using regions to determine ExitSouth, ExitNorthEast, etc?). Bee Chambers is fairly close to this, but it doesn’t have two-way connections. Room B may be mapped north of Room A, but you can’t go south from Room B to get back to Room A.

I’ve looked at The Reliques of Tolti-Aph (among several other pages I’ve stumbled across via Google), which seems to have what I want, though I really don’t understand it at all. I’ve also looked at Dynamic Rooms, though I want to use rooms that I’ve detailed out rather than generic, featureless ones.

I’ve been working on this mapping thing alone for a little over three weeks and I’ve all but given up on trying to work it out myself. Can anyone help me out here? Thanks in advance!

P.S. Is it possible to create a dynamic map for the player’s convenience for this sort of randomly generated dungeon? Preferably with actual graphics, though ascii will work. I’m thinking of an ever-present map in a sidebar (there’s an extension for that which I forget the name of) that reveals rooms as the player discovers them. That’d be really handy.

You might want to have a look at this page of posts.

You could consider contributing to Kerkerkruip too! We’ll always welcome new people to the team.

You could also use the Dynamic Rooms extension and lay out a grid in a when play begins rule. This would automatically handle things like going west-north-east-south taking you back to your current location.

Thanks for the replies so far, everyone. I emphasis again that I’m very new to this, so I apologize if I’m missing basics here. I’ve gone through the first ten chapters of the manual and looked through the recipe book, adding bits and pieces to tiny test projects whenever I could, but otherwise I’m pretty inexperienced at programming. Just so we’re clear, you shouldn’t feel like you’re patronizing me if you need to simplify. I don’t mind, as long as it’s useful!

@matt w
LCamelot’s code isn’t bad, though it isn’t quite right for me because of the so-called “weird geometries” noted by VictoryGibsbers. The world that the setting of my test game is relatively mundane, so non-euclidean layouts would stick out like a sore thumb. The only one I noticed in my quick run of it was that some rooms could loop (going north or south in the green room took you to the purple room, which did the same), but even that’s too much for me.

Results from the Kerkerkruip Dungeon Generation would be ideal, but that code returns a handful of errors that I can’t begin to correct because it’s well beyond my level of understanding. I admit I’m tired of fighting with this mapping business, and I’m getting to the point where I’m willing to just plug in pre-made code for the dungeon design for now so I can get to working on other parts of the game, though this code doesn’t seem to work as it is. I don’t suppose I could ask for a bit of spoon-feeding here on how to make this work? The errors after I created a room for the game to start in:

[spoiler][code]

In Book - Creating the Map, Part - Main Routines, Chapter - Resetting and Creating the Map, Section - Creating the map and collapsing passages:

Problem. In the sentence ‘while less than twelve rooms are placed or less than eight habitable rooms are placed begin’ , I was expecting that ‘less than twelve rooms are placed or less than eight habitable rooms are placed’ would be a condition. It didn’t make sense as one long phrase, but because it was divided up by ‘and’/‘or’, I tried breaking it down into smaller conditions, but that didn’t work either. ‘less than twelve rooms are placed’ was okay; ‘less than eight habitable rooms are placed’ did not make sense; so I ran out of ideas.

I was trying to match this phrase:

while (less than twelve rooms are placed or less than eight habitable rooms are placed - a condition):

This was what I found out:

less than twelve rooms are placed or less than eight habitable rooms are placed = something unrecognised

Problem. In the sentence ‘repeat with place running through placed connectable rooms begin’ , I was expecting to read a description of values, but instead found some text that I couldn’t understand - ‘placed connectable rooms’.

I was trying to match one of these phrases:

  1. repeat with (place - a new name) running through (placed connectable rooms - description of values):

  2. repeat with (place - a new name) running through (placed connectable rooms - list of values):

This was what I found out:

place = something unrecognised

placed connectable rooms = something unrecognised

Problem. In the sentence ‘if generation info is true, say “* Adding connection between [place […] ce].[line break][run paragraph on]”’ , I was expecting to read a condition, but instead found some text that I couldn’t understand - ‘generation info is true’.

I was trying to match this phrase:

if (generation info is true - a condition) , (say “* Adding connection between [place] and [further place].[line break][run paragraph on]” - a phrase)

This was what I found out:

generation info is true = something unrecognised

say “* Adding connection between [place […] ce].[line break][run paragraph on]” = an instruction to do something

In Chapter - Choosing the right room, Section - Building a list of suitable rooms:

Problem. In the sentence ‘if the difficulty level of considered room is greater than difficulty begin’ , I was expecting to read a condition, but instead found some text that I couldn’t understand - ‘difficulty level of considered room is greater than difficulty’.

I was trying to match this phrase:

if (difficulty level of considered room is greater than difficulty - a condition):

This was what I found out:

difficulty level of considered room is greater than difficulty = something unrecognised

[/code][/spoiler]

And finally, I’m not even sure what duodave is suggesting. Define some mazes? Assign rooms to be regions? Does this mean to manually make a group of hand-crafted mazes and have the game randomly select one to throw the player into at the start of play? If so, I need more randomness than that for the sake of replayability.

@Dannii
I went on a lengthy searching spree last night where I saw Kerkerkruip recommended for study, so I went and played it and I have to say; I’m impressed. The layout generation, the automap, the combat system, the excellent polish… all of it, really, is outstanding. I’d love to help out in the event that I ever become not terrible at this stuff!

@Draconis
I looked briefly over that extension, though I’m not sure it’s what I want. I could easily be wrong, but Dynamic Rooms simply generates empty rooms with a name, right? I’d like each room in my game to be one I’ve previously filled out with things the player can interact with (as well as things that have been randomly generated). So, for example, you can find a spear rack in the Armory that offers a weapon to the player. I don’t want filler rooms for the sake of having more rooms.

Oh, sorry, those posts were really a bit heavy to dump on a new programmer.

The errors you’re getting from VictorGijsbers’ code all have to do with definitions he made elsewhere; somewhere else in his code he declares that every (?) room has a difficulty level, that a room can be connectable, that a room can be habitable, and that there’s a truth state called “generation info” (which is a flag that tells it whether to print information about how it made the map). So when you try to use the code in a project that doesn’t have all those things, the compiler throws errors.

I’ll see if I can work on something a little more helpful, though I should also say that a Roguelike-like might not be the best thing to work on to learn the language and programming in general! Most Inform games use a preset map.

I figured that was the case and went through looking for other mentions of keywords like “habitable,” tentatively commenting them out and seeing if the whole piece became usable. Each time I changed something, though, it gave me a new error until it just got to the point where I realized I wouldn’t be able to tweak it without a better understanding of the language. I can’t figure out how to search for some parts of it though, like how it says “repeat with place running through …” because I don’t know what “place” means in this context and it’s such a common word that searching isn’t helpful. I’ll have to sit down and read more until I stumble across the answer (or else, keep bothering you guys with these dumb questions). I’ve had a similar problem with “to say” in the past. I know about the Index and I’ve taken a look at the sticky here regarding documentation, but it seems to me the information I want to know is always scattered about, which is odd because I also get the impression that most of the documentation is designed for quick reference as opposed to sitting down and reading it cover-to-cover. I’m sure there’s a pattern to it, I’m just not seeing it right now.

Anyway, enough of that mini-ramble.

I would appreciate it if you wouldn’t mind wrestling with it a bit. It’s fine if not; I understand that no one is here to do my homework. It’s just that I’ve been going nowhere for a while and I’d rather move on to other aspects of the gameplay for the time being instead of banging my head against this for too long, though I’d feel uncomfortable using a preset map because of the randomized nature of roguelikes. I think I also agree that I should probably work on something less complex, though the reason I started this project in particular was because I was inspired by a roguelike-like (which actually got me liking roguelikes and roguelike-likes) that was made in Inform which I very much… liked. So I’m kind of pushing for a game similar to that in which I can experiment with and hopefully put an interesting spin on. Maybe it’s not the best engine for it, but there’s something to be said for faithfulness to the original!

In other news, I’ve found the Automap extension, which should be just perfect for my mapping needs. No need to worry about that anymore.

In other, OTHER news, I have an unrelated question about doors blocking passage. All of the documentation I’ve found says that doors have to be opened before you can pass through them, though the game opens the door for me automatically when I try going their direction. I never have to open them manually like everything I’ve read says to. This is hardly relevant in a game with randomized layouts because doors are stuck in one place, but I’m still wondering why this happens? There was a rule like “cannot pass through doors” or something that I unlisted and it just makes you phase through them like a ghost. Am I also right in guessing the built-in documentation is a bit outdated? If so, is there anything in particular I should be on the look-out for that might be considered common knowledge here?

Edit because you wouldn’t spin a spin.

That’s not actually a contradiction, of course… if you have rules that prevent a door from opening, then the player can’t go through.

The “can’t go through closed doors” handles both the auto-open and the check-for-being-closed phases. You could customize it, but it’s easier to write a “check opening” rule on the door.

Right. To restate that, the documentation says that doors have to be manually opened by a separate input from the player. Trying to “go west” when there’s a door in the way should simply stop the player and return a message saying that you need to open the door first. In practice, this doesn’t happen; it quietly preludes the player’s input with “open door” before trying to go west.

This case isn’t a big deal by any means, I was just curious if it’s a case of outdated info and if I should be alert for anything else in the official documentation that might be outdated and potentially lead to a big deal.

Where are you looking in the documentation?

My extension Mobile Doors lets you randomize the placement of doors.

I’ve seen it in a few guides I’ve looked up. I know the Inform Handbook by Jim Aikin does it. On short notice, this is what I found in the official documentation.

inform7.com/learn/man/Rex108.html#e108

It’s not as explicit as I recall, though it works because that example wouldn’t exist if Inform did that by default.

Ah, cool. That could actually come in handy. Thanks for suggesting it!

Just a note – in this case “place” doesn’t have any special meaning. It’s a local variable; it means that we’re going to let “place” stand for every whatever it is in turn and then execute the next block of code. So you won’t find “place” in the documentation in any way that’ll be helpful – what you might want to look at is the section on repeat loops. (Sections 11.10 and 11.11.)

This is at least mildly annoying to find – you probably want to look at 5.13.

I’ll give a shot at making a sane dungeon generator (working on tiny projects is fun for me, not least because I usually feel like I can finish them), but probably not for a couple days.

Got it, I’ll check those sections out. Thank you for your help. I’ll be lurking around so you can just post here or send me a PM if you want to talk some more. In the meanwhile, I’m going to see what I can do about some other parts of the game. Maybe I’ll reinvent the wheel a bit and make a stat and combat system. That seems pretty achievable.

Cheers.

OK, here’s an attempt, with lots and lots of comments. As I say in the comments, I made some questionable design decisions, notably having the map placement done in a “to decide” phrase (so the phrase “if locus can be placed” actually puts locus on the map). But it does include a general way of putting rooms in and a method for making exceptions for special rooms as well.

"random generated dungeon" by Matt Weiner

A chamber is a kind of room. Some chambers are defined by the Table of Dungeons. [The chambers are the rooms that get placed randomly by the standard procedure. The Table just lets us define a bunch of them at once without having to say "Foo is a chamber." See 15.16.]

A room has a number called the maximum exits. The maximum exits of a room is usually 4. [Does what you think -- lets you make sure that certain rooms can have no more than a certain number of exits.]

Egress relates a room (called the place) to a direction (called the way) when the room-or-door the way from the place is not nothing. [Which will be true whenever you can go that way on the map. Putting doors in place randomly is pretty much impossible, but you could have a set pair of rooms with a door between them if you wrote appropriate routines for them.]
The verb to open to (it opens to, they open to, it opened to, it is opened to, it is opening to) implies the egress relation. ["Open to" and its conjugations will be getting all the work in the source code.]

After looking: say "You can go [list of directions opened to by the location]." [If we have procedurally generated exits, we'll need a procedural way of telling the player about them. If you had more detailed room descriptions you could fit something like this in the room description if you wanted, though it might be wise to include a default that did this if you hadn't written any other description of the exits.]

Table of Dungeons
chamber	maximum exits	description
Ordinary Room	3	"Nothing of interest here."
Bland Room	3	"Less of interest here."
Crossing Room	4	"This room appears to be central."
Cul-De-Sac	1	"Better go back the way you came."
Dead End	1	"Nothing happening here."
Interesting Room	4	"This room is interesting!"
Coffin Room	3	"Some coffins etc."
Dragon Room	2	"There is a fearsome dragon here guarding the treasure on the other side!"
Ho-Hum Room	3	"Yeah, it's a room."
Side To Side Room	2	"This room may or may not take you to another room."
Possible Branch Room	3	"Maybe this room will branch?"
Other Possible Branch Room	3	"Or this one might branch?"

[These rooms won't be placed by quite the same algorithm.]
The Treasure Room is a room. "Treasure, oh yeah!" The maximum exits of the Treasure Room is 1.
The Start Room is a room. "Here begins your adventure." The player is in the Start Room. The maximum exits of the Start Room is 4.
The Exit To The Next Level is a room. "You can get to the next level from here." 

A room has a number called x-coordinate. A room has a number called y-coordinate. [The x and y coordinates let us avoid having rooms clash or other impossible geometries.]
The x-coordinate of a room is usually -99. The y-coordinate of a room is usually -99. [Signifying, "off the map."]

The x-coordinate of the Start Room is 0. The y-coordinate of the Start Room is 0.

A room can be placed or unplaced. A room is usually unplaced. The Start Room is placed. [We need to keep track of which rooms we've actually put on the map.]
A room can be tried or untried. [And this will be used when we're putting a room on the map, to figure out which rooms we've placed in next to.]

[Everything else is a giant cascade of user-defined phrases...]
When play begins: construct the dungeon. [This is the big one.]

[Now the tricky and perhaps unwise thing is that I've defined a phrase that tests whether a room can be placed on the map -- and if it can, it places it. So when you hit a phrase that tests whether the room can be placed, at the end of the test if it can be placed it *is* placed, with its coordinates set and the map connections made and everything. Running the "if" clause actually makes changes.
Ordinarily it would make more sense to call a phrase to place the chamber instead. But the issue here is that we need to know whether we actually succeeded in placing the chamber. So you can think of this as really an instruction to place the chamber if you can, and report back whether you could or not.
The Kerekruip code takes care of the problem by defining a rulebook for placing rooms in the dungeon; when you define a rulebook, you can give it outcomes, so if you run the placing rules for a room (or whatever they're called) you can look at the outcome to see if the room successfully got put on a map. This would probably be a wiser approach for a more complex project.]

To construct the dungeon:
	while a chamber is unplaced: [This loop will repeat until every chamber is placed. In a lot of applications you would just do "repeat with locus running through chambers" instead, but that would always do everything in the same order; often the order they're defined in the source code. In this case we want the order to be truly random. Another way to do this would be to put the chambers in a list and sort them in random order.]
		let locus be a random unplaced chamber;
		say "Placing [locus].";
		if locus can be placed: [Here's the test that actually amounts to an instruction to place the locus if you can, and tell us whether you succeeded.]
			now locus is placed; [I think this winds up being redundant because the code for testing whether the locus can be placed actually sets the placed property. But better safe than sorry. If we never set the placed property it'd be bad, because we'd loop infinitely.]
		otherwise:
			say "resetting.";
			reset the dungeon; [if we hit a brick wall placing a room, trash the map layout and start from the beginning]
	if the exit to the next level can be placed: [and we place the exit last, so it'll on average wind up farther away]
		say "Succeeded in making the dungeon!";
	otherwise:
		say "Resetting.";
		reset the dungeon.
			
To reset the dungeon:
	repeat with locus running through rooms:
		if locus is not the Start Room, remove locus from the map; [removing it from the map is a custom phrase; see below]
	construct the dungeon.
	
To remove (locus - a room) from the map: [this is meant to make everything the way it was before. It's not really tested, since I've never been unable to place a room]
	now locus is unplaced;
	now the x-coordinate of locus is -99;
	now the y-coordinate of locus is -99;
	repeat with the way running through directions:
		if the room-or-door the way of locus is not nothing:
			change the opposite of the way exit of the room-or-door the way of the locus to nowhere; [this syntax is pretty complicated ; since "the opposite of the way" is a direction, it resolves to something like "change the north exit of the next room to nowhere," which is to say, break the map connection]
			change the way exit of the locus to nowhere.

[A "to decide whether" phrase runs until it hits a "yes" or a "no" and then returns that as the answer. (If it reaches the end without making a decision, the answer is "no.")]
To decide whether (locus - a room) can be placed:
	now every room is untried;
	while a placed room is untried: [This will go through all the placed rooms, trying them one by one, until we find one we can put the locus next to. Again, we don't want "repeat through placed rooms" because that'll always do the same order. We could put the placed rooms in a list and sort it in random order, then repeat through that.]
		let entryway be a random untried placed room; [this creates "entryway" as a temporary variable]
		now entryway is tried;
		say "trying [entryway].";
		if the number of directions opened to by entryway is at least the maximum exits of entryway: [entryway is already "filled up," so we don't try it]
			next; [This stops the iteration of the "while" loop, so we go on to pick another untried placed room -- and the one we just tried is now marked as tried]
		otherwise:
			if the locus can be placed next to the entryway: [this calls a phrase below, which not only checks to see if the locus can be placed to the entryway, it actually places the locus if it can. So if the below block of code is executing, we've already placed the locus]
				[now let's see if we can open a connection to an already placed room]
				repeat through the Table of Cardinal Directions: [This looks at one row of the Table at the time; when we say things like "direction entry" after this it means "the direction entry in this row." The Table should have been randomly sorted by some code we're running; we could sort it randomly again but I'm not sure it makes a difference]
					if the number of directions opened to by locus is at least the maximum exits of locus: [the locus is filled up, so we don't need to check anymore]
						break; [this ends the repeat loop completely; see 11.12]
					otherwise if the room-or-door the direction entry of the locus is not nothing: [we've already made a map connection in the way we're looking]
						next; [this ends this iteration of the repeat loop and goes on to the next; again, see 11.12]
					otherwise if a room is mapped at (the x-coordinate of the locus plus the x-increment entry) by (the y-coordinate of the locus plus the y-increment entry): [this checks the map grid to see if there's a room there that we could open a connection to. note that "entry" means "entry in the chosen row of the Table of Cardinal Directions," which will correspond to the direction we're checking]
						let the candidate be the room mapped at (the x-coordinate of the locus plus the x-increment entry) by (the y-coordinate of the locus plus the y-increment entry); [we just ran through all the rooms looking for this one again; if efficiency were an issue we'd need to not do that, but at least we've created "the candidate" as a local variable so we don't need to keep doing it]
						if a random chance of 1 in 2 succeeds and the number of directions opened to by the candidate is less than the maximum exits of the candidate: [if both rooms can admit another exit, flip a coin to see if you want to make the connection]
							say "Connecting [locus] to [candidate].";
							change the direction entry exit of the locus to the candidate;
							change the opposite of the direction entry exit of the candidate to the locus;
				yes; [it might be hard to follow the indentation, but this is after the "repeat" loop and inside the "if the locus can be placed next to the entryway" clause. So, no matter what happens when we try to make additional map connections, we want to say "Yes, the locus was successfully placed on the map." This ends processing of this phrase.]
	no.	[And this one is outside and after the "while a placed room is untried" loop. So if we get here we've tried to put the locus next to every room that's on the map already, and the verdict is that the locus can't be placed.]
	

[But we might want some rooms, or special kinds of room, to have special placement rules. Here I've written a rule for the Dragon Room, which has to have the Treasure Room placed next to it.
To have an exception to the general rules for "To decide whether foo can be placed," just write a phrase that has something more specific in the header. Here the variable we use is as specific as possible: It only applies to the Dragon Room! When asked to decide whether a room can be placed, Inform will run the most specific phrase it can run that matches that room.]
To decide whether (locus - the Dragon Room) can be placed:
	now every room is untried; 
	while a placed room is untried: 
		let entryway be a random untried placed room;
		now entryway is tried;
		say "trying [entryway].";
		if the number of directions opened to by entryway is at least the maximum exits of entryway:
			next;
		otherwise:
			if the locus can be placed next to the entryway: [so far all this is as before, and at this point the Dragon Room will be on the map. But we still have to see whether we can put the Treasure Room next to it]
				sort the Table of Cardinal Directions Copy Two in random order; [I think we might need another copy of the table here so we don't mess up the loop through the Table of Cardinal Directions that we're still repeating through when we try to place the Dragon Room]
				repeat through the Table of Cardinal Directions Copy Two:
					say "Trying the Treasure Room to the [direction entry].";
					unless a room is mapped at (the x-coordinate of the locus plus the x-increment entry) by (the y-coordinate of the locus plus the y-increment entry):
						place the Treasure Room to the direction entry of the locus;
						now the Treasure Room is placed;
						now the x-coordinate of the Treasure Room is the x-coordinate of the locus plus the x-increment entry;
						now the y-coordinate of the Treasure Room is the y-coordinate of the locus plus the y-increment entry;
						say "succeeded in placing Treasure Room.";
						yes;
				say "Failed to place the Treasure Room."; [at this point we have repeated through the whole Table of Cardinal Directions Copy Two without placing the Treasure Room next to the Dragon Room, which would hit a "yes" and so terminate this whole "to decide" phrase, so we have to remove the Dragon Room from the map and start over with a new location for it]
				remove the locus from the map; [I think this might actually miss a case -- say we provisionally place the Dragon Room east of the Bland Room but are then unable to place the Treasure Room. I think we now go to the next "while a placed room is untried" entry having marked Bland Room as tried, without seeing whether we might be able to place Dragon Room west of the Bland Room and then place the Treasure Room off of that. If that were to cause trouble, we'd need to figure out a way to change that.]
	no.	[Again, this is after the "while a placed room is untried" loop, so we only hit it once we've tried every placed room.]
	

[The next phrase is the "to decide" phrase that actually does stuff. It looks for available spots next to the old room, and if it finds one, it actually puts the room there.]
To decide whether (new room - a room) can be placed next to (old room - a room):
	sort the Table of Cardinal Directions in random order; [We need to do this so we aren't trying the directions in the same order every time.]
	repeat through the Table of Cardinal Directions:
		say "Trying [direction entry].";
		unless a room is mapped at (the x-coordinate of the old room plus the x-increment entry) by (the y-coordinate of the old room plus the y-increment entry): [checking the map grid]
			place the new room to the direction entry of the old room; [this is a custom phrase; see below]
			say "succeeded with [direction entry].";
			now the new room is placed;
			now the x-coordinate of the new room is the x-coordinate of the old room plus the x-increment entry;
			now the y-coordinate of the new room is the y-coordinate of the old room plus the y-increment entry; [we've marked the room as being on the map, given it its map connections, and recorded its grid location]
			yes; [again, this terminates the phrase, saying we succeeded]
	no. [This will happen if we've gone through all the directions without getting to "yes."]

To place (new room - a room) to the (way - a direction) from/of (old room - a room):
	change the way exit of the old room to the new room; [this is a rather opaque formulation -- if the way is north it amounts to "change the north exit of the old room to the new room," that is, once you go north from the old room you'll get to the new room. See section 8.5; this is a special formulation because "now the new room is mapped the way of the old room" doesn't work for apparently complicated reasons. (I forgot about this formulation and it nearly drove me to distraction.)]
	change the opposite of the way exit of the new room to the old room. [And this is similar, except now the direction is "the opposite of the way"; so if the way is north, this amounts to "change the south exit of the new room to the old room." When you're changing exits by hand like this, you have to change both of them to ensure that you don't get a one-way connection.]
			

To decide which object is the room mapped at (x - a number) by (y - a number): [we have to say "which object" rather than "which room" because the answer might be nothing]
	say "Testing [x], [y].";
	repeat with test room running through placed rooms:
		if the x-coordinate of test room is x and the y-coordinate of test room is y, decide on test room;
	decide on nothing.
	
To decide whether a room is mapped at (x - a number) by (y - a number):
	if the room mapped at x by y is nothing, no;
	yes.

Table of Cardinal Directions
direction	x-increment	y-increment
North	0	1
South	0	-1
East	1	0
West	-1	0

Table of Cardinal Directions Copy Two [we need another copy for when we need a loop within a loop]
direction	x-increment	y-increment
North	0	1
South	0	-1
East	1	0
West	-1	0

Whoa, that is spectacular! That’s way more than I was expecting!

I’ve only just received it and plugged it in for a brief test drive so I can’t offer much commentary at the moment, but you sure as hell have my appreciation. It’s unfortunately nearly midnight here, but I HAVE to delve into that tomorrow! Thank you so much for all the work you put into that; I’m genuinely impressed and eager to look at it more closely. I can already see it’s going to be very helpful (I’m used to looking at uncommented code and trying to make sense of it, so this is a breathe of fresh air, to be sure).

I’ll let you know if I have anything more to say than gushing over it! Thanks again, that really made my day. :smiley:

Thanks!

Oh, i should mention that on a test run this didn’t work with Automap. I’m not sure why. It might have had something to do with all the debug text that prints before play begins, which could be solved by deleting the debug text; or it might be that I unsuccessfully hacked my copy of Automap, which could be solved by not doing that.