Back to Editor's Guide


Terrain in Betrayal


Random Terrain Generation

Betrayal uses a modular terrain system to generate a new forest layout in each game.  Before you do any terrain editing, it's important to understand how it works.

Zones and Doors

The map contains a bunch of similarly-sized voids in the map called "zones"; these are locations where random content can be added to the forest.  These each contain a blue preset region that defines the available area and allows it to be referenced in triggers.  Although the actual space around zones varies, the regions defining them all have the same size and shape, in order to make testing easier and avoid weird size-dependent bugs.

We don't want the routes connecting various zones to be the same in every game, so typically zones are completely closed off by trees or other obstacles, with no way to get from one to another.  Then, a series of regions called "doors" are defined (typically colored teal).  The forest generation algorithm selects some doors to be "opened" each game, which means that it removes any Felwood Tree Walls, creating a walkable pathway between two zones.

Two Zones

The terrainer needs to ensure that removing all Felwood Tree Walls from the door zone actually will create a valid pathway.  Note that there's a custom destructible in the map called a "Felwood Tree Wall (Not Removed in Doors)" that can be used to place trees that will persist inside of a door region, even if the door is "open."

A few doors rely on bridges instead of trees:

In order to support this, all destructibles except Felwood Tree Walls (either regular or the "not removed" variety) get killed if they're inside a door that remains "closed."  Thus, if the door is open, players can walk across the bridge; if the door is closed, the bridge is destroyed, cutting off this passage.

There are also a few special zones called "tokens".  These are regions that have doors going in and out of them, but which don't provide a suitable location for random forest content.  They are sometimes small, oddly-shaped, or contain obstacles.  They act sort of like hubs for the path generation.

Basically, tokens are exactly like zones, except that they don't get random content, and therefore don't have to be a particular size or shape.

Creating the Pathing

The forest generator starts at the middle of the forest (the hero start location) and works its way out one step at a time by finding all the doors that lead from an accessible location (initially just the middle) to any inaccessible location, and then opening one of those doors at random.  This causes a new area to become accessible, and the process repeats.

When it's done, this has all the zones strung together into a giant web:  all of them are accessible from the center (and therefore from each other as well), but there are no cycles or loops--only one way to get anywhere.  That's kind of boring, so the generator then loops through all the remaining doors (the ones leading between areas that are already accessible) and gives each one a random chance to either open or stay closed (currently, there's a 45% chance of each door being opened).

In order to give a very vague idea of how "close" each zone is to the middle, they're placed into one of three categories, based on the order in which they're added.  The first 35 zones become "border forest," the next 30 become "middle forest," and rest (up to 40) become "deep forest."  (Note that tokens don't count, and don't get added to any category.)

If there are more than 110 zones, not counting tokens, some of them remain inaccessible and aren't used in the current game.  At the time of this writing, there are 104 zones without tokens; this restriction just prevents the playable area from becoming too big if more zones get added.  If a LOT of zones get added, it may be prudent to add some sort of geographic segregation so that the unused portions clump, rather than being scattered in weird patterns.

Filling the Forest

Once the pathing is established (we know which doors are open or closed), it's time to start filling in the zones with interesting stuff.

The Haunt gets the first opportunity to claim particular zones for its own use; it can "reserve" however many it needs for the scenario to work (typically this ends up being about one per player).

After that, the generator selects zones for artifacts.  Artifacts are unique items that show up exactly once in each forest.  They only get placed in middle or deep forest, and there are a variety of ways they get distributed (some are monster drops, some are hidden, etc.).

Then, the generator fills in the remaining zones in each section of the forest (border, middle, and deep, in that order).  To do this, it first selects a random number of unique plans--these are special objects, events, or interesting features that only appear once, and only in a specific region of the forest.  The generator selects up to 10 in the border forest, up to 10 in the middle forest, and up to 8 in the deep forest.  Finally, the remaining zones are filled in with stock content--monsters, randomized trees, that sort of stuff (a few of them are left basically empty).

The entire process of generating the forest doesn't take place in one trigger; instead, it's done gradually, with a bunch of short countdown timers to add pauses.  This helps prevent the game from lagging at start-up, and makes sure we don't run into the thread execution limit.

Making Terrain

General Terrain

Even with this elaborate random forest generator, some terrain is static.  The locations of zones, doors, major terrain features like cliffs and rivers, and the destructibles that connect or separate the zones are all preplaced.  There's also nothing that stops you from adding other preplaced aesthetic features--as long as you don't step on the generator's toes.

The most important rule is not to block the key passages, which means that you can't put anything that blocks ground movement inside of the blue zone regions, and you need to make sure that doors continue to let people through when they're open and not when they're closed.  You should also avoid opening up passages that would allow heroes to walk between two zones without using any of the designated doors.

You should also keep in mind that stuff is going to be placed inside of the zones--sometimes fairly specific and elaborate stuff, like statues, fountains, shops, etc.  Try not to do anything to the terrain that would interfere with the random content that gets inserted into a zone.  However, the random content only gets to control things up to the zone boundary--outside of that blue box, it's fair game.

Adding New Zones and Doors

You can also define new regions to act as zones and doors.

For zones, you should probably copy and paste an existing zone region, then change it's name to use the next unused number.  Place it in a suitable location--make sure there's nothing that will block movement preplaced inside the zone, and that it's suitably surrounded by barriers out of which you can carve doors.

Doors can be any shape and size, so you can copy-paste or not, whichever you think is easier.  Either way, rename the region to "Door X Y", where X and Y are the numbers of the zones connected by the door (there's no support currently for doors that connect more than two zones--use token zones to act as transit hubs, instead).

Once you've created the regions, you need to tell the map generator about them.  This requires that you modify the "Build Zone Table" and/or "Build Door Table" triggers (in the Random Forest Generation category).  Be forewarned that these triggers are very big, and may take a while for the editor to display.

Just copy-paste a data block onto the end of the list, and modify the data as appropriate for your new regions.  For zones, you need to specify the zone region, and whether or not that zone is a token.  For doors, you need to specify the region and the numbers of the regions on either side.

Adding Unique Zones

Uniques are special pieces of content that get inserted into the zones around the map; they won't necessarily show up every game, and they won't ever show up more than once in a game (a Haunt can guarantee that a particular unique does or doesn't show up, if necessary).  Uniques are generated by triggers, so there's lots of stuff you can put in them--doodads, terrain tile changes, terrain deformations, monsters, neutral buildings, etc. are all fair game.  You can also create scripted "events" that occur when heroes enter the unique zones.

To add a unique, first select whether it's going to appear in border forest, middle forest, or deep forest, then go to the appropriate unique placement trigger (in the "Random Forest Generation" category).  Most of the trigger will be taken up by If/Then blocks that generate the various uniques.  Scroll down, near the end of the trigger, and add a new block after the last current one.

You'd insert a new unique just before the comment that says "Done."  The "___UniquesToUse" condition is there to test whether this unique has been selected in this particular game; when you create a new unique, increase the index to the next unused number.  For example, if we were adding a new unique after the one pictured above, we'd change it from 3 to 4.  (Testing Tip: if you disable this condition, your unique zone will always appear, which may make it easier to test.)

Important: Once you've done this, you should also go to the variable manager (ctrl-B) and change the initial value of the "NumberOf___Uniques" variable to reflect the fact that you just added a new one--otherwise, the algorithm won't know that your unique exists.

The second condition is just there to make sure that we haven't somehow run out of available zones in which to place uniques.

In the "Then" block, there's a comment to give us a hint what this unique is about.  The next five lines ("Set tempInt" through "Set NumUnassignedBorderForest") are selecting a random border zone that hasn't been filled yet, and removing it from the list; you probably don't want to mess with those.

After that, the variable tempInt2 will be the index number of the zone where the unique is to be placed.  You can use Zones[tempInt2] to refer to the region, and do pretty much whatever you like with it--place units, change terrain, etc. In the example above, there's no monsters or custom terrain, but we're assigning an event to the zone--this will be explained in the next section.

The main thing to watch out for is that you need to make sure you don't block off any of the doors leading into or out of the zone.  Since the doors could be anywhere along the edge of the zone, the best way to handle this is to make sure you don't place any permanent barriers too close to the edge of the zone (if it's something a hero can kill to get out of his way, then you can put it wherever you want).  If you want to generate random trees or similar obstacles, there's a special pregenerated zone called "Zone Interior Sizer" that can help with this:

Use the Region - Move action to center this region in your zone; then you can create obstacles inside the sizer and be sure that they won't end up too close to the zone's edge (if you're generating things with a larger pathing map than tree walls, you may need to take further precautions).

Also, if you place multiple obstacles, you need to be careful not to make a box that players can become trapped in.  Even if a hero can't just walk into the trap, sometimes heroes can get teleported from one zone to another.  The easiest way to do this is not to place more than 3 rectangular obstacles (you can't build a box that will trap a hero with less than 4, no matter how they're arranged).  Of course, you can use more than 3 obstacles if you're careful about how they're positioned relative to each other.

Zone Events

Look back at that trigger from the last section; the last few lines of the "Then" block are adding an event to the zone in question.  The "Ghost Item" trigger (in the "Zone Events" category) defines what the event does.  That line of custom script assigns the event described by tempTrigger to the zone with number tempInt2.  Finally, we set a flag to indicate that zone number tempInt2 has an active event (this allows us to have events that only happen once, or once per hero, rather than every time someone walks in).

The Ghost Item trigger looks like this:

You'll notice the trigger has no event--the event gets added by that line of custom script from the earlier trigger.  This trigger will run when someone enters the zone.

The reason we use JASS to set this up, rather than a simple "Add Event" action, is so that this trigger can figure out which zone it's happening in (there's no "Triggering Region" event response in GUI).  The second line of custom script here sets the variable tempInt to the number of the zone in which the event is occurring.  (The first line declares a local variable--if you don't know what that means, just ignore it for now.)

After that, we have a condition that checks whether this zone's event is active.  If it isn't, we stop executing the trigger.  If it is, then we set it to be inactive (so that the event won't happen a second time), and then we go on to actually do the event--in this case, a phantom floats across the zone for a couple of seconds, then disappears, leaving behind a gravestone (which will drop an item if the player kills it).

There's also a way to create an event that activates once per player, rather than just once ever, but it's a little more complicated.  Rather than setting ZoneEventActive[tempInt2] to true, you need to set a separate flag for each player in a simulated 2D array:

Then, we need to make a similar change to the logic at the beginning of the event trigger:

This uses a separate flag for each player, so we can make sure that each player only gets the event once.  If it's not clear to you how this works, you can just treat it as a magical incantation.

Artifact Zones

There are also some semi-unique zones created for dispensing artifacts, which are special items that show up exactly once in each game.  This is done in the "Place Artifacts" trigger.

The key constraint here is that we need to always have a number of artifact zones equaling the number of artifacts, so instead of selecting which zones we want in advance, we pick a new type of artifact zone (with replacement) for each artifact.

After this point in the trigger, there's a bunch of If/Then blocks with comparisons on tempInt that create different artifact zones.  You can add new artifact zones by increasing the upper bound of the random number assigned to tempInt and adding a new If/Then block.  Everything else is pretty much the same as making a unique zone.

Make sure that your artifact zone actually drops one (and only one) artifact at an appropriate time.  This is done by adding events to the "Artifact Drop" trigger; for more information on item drops, see <link>

Filler Zones

In addition to unique and artifact zones, there are also filler zones, created by the "Fill ___ Zones" triggers.  These create things like standard fights, ambushes, random terrain, and a few other things.

New filler zones can be added, but there isn't really a systematic procedure the way there is for unique and artifact zones, and we should beware of adding too many.


Back to Editor's Guide