Home Let's Build an RPG! Deconstructing B/X D&D Adventure Generator! B/X D&D Character Generator Various Java Contact
 

Encounter Generator Tutorial

Whee! Let's write a happy little encounter generator! It's so simple, anyone can do it! Right Bob?

First, take your 2-inch brush and sketch in an idea of what interfaces we need to implement… There are no mistakes, only happy little coincidences!

Interfaces

To write our Encounter generator, we'll write a class that implements iEncounterGenerator. Open up that file and take a look. But remember, Don't Modify the Interfaces!!!

You'll see that iEncounterGenerator extends iTextGenerator. So open up that file too…

And iTextGenerator extends iGenerator, so open up that one too……

There! Between those three files, we can see what abstract methods we need to implement in our generator. You can close the interfaces now, because we'll just base our new generator on the provided example.

The ExampleEncounter Generator

Make a copy of Generators/Encounter/ExampleEncounter.php.inc and rename it to MyNewEncounter.php.inc . Open up this file in your editor.

Ah ha, here are the guts of the business! Looks like we're finally actually implementing some code, huh? \o/

You'll see that it already has all the methods implemented from all the interfaces we just looked at. If you forget to implement one of the needed abstract methods, the test harness will catch your error.

Includes

At the top of the file, you'll see that some other stuff is included. The interface that this class implements, and some other utility libraries. You shouldn't need to change any of this.

Class Name

On line 6, change the name of the class from ExampleEncounter to MyNewEncounter. The class name must match the filename for the testharness and adventure generator framework to be able to load it. Don't worry if you forget this step. The test harness will catch the error and inform you of the problem.

Generator Name

On line 14, in the getGeneratorName() method, change the returned text to a short description of your encounter generator. This is the text that is displayed in the dropdown menu where people select what encounter generator they want to use, so don't make it too terribly long.

Generator Notes

On line 20, in the getGeneratorNotes() method, change the returned text to a longer description of your generator. Include things like source material, the range of levels that it generates encounters for, etc. Nothing currently uses this text, but at some point there will be a little ”?” button next to the dropdown menu on the adventure generatrion page that will display it in a little popup box.

Constructor

The constructor on line 23 just takes the average party level and stores it for later use. You probably don't need to change anything here. If your generator doesn't produce encounters for a particular average party level, you probably want to put an error message into the generated output text below, rather than printing one here.

Encounter Difficulty get() Accessor

The method on line 32 just returns the difficulty that was passed into the constructor, so you don't need to change this unless you are doing something weird.

Generate

The generate() method on line 37 is the meat of the class, which generates an encounter and stores it for retrieval via getText(). We'll get back to that in a moment.

getText

This method on line 65 just returns whatever encounter was last generated with generate(). You probably don't need to modify it.

Implementing the generate() Method

Alrighty then! Let's plan our generator a bit…

We'll generate encounters for levels 1-3. There will be a 3-in-6 chance of an encounter being generated.

So…. We're going to gut the generate() method and rewrite is like this:

	function generate() 
	{ 
		$result = ""; 
		// 3-in-6 chance of an encounter 
		if( d6() <= 3 ) 
		{ 
			// Generate an encounter of the appropriate level. 
			switch( $this->difficulty ) 
			{ 
				case 1: 
					$result = "Level 1 encounter."; 
					break; 
				case 2: 
					$result = "Level 2 encounter."; 
					break; 
				case 3: 
					$result = "Level 3 encounter."; 
					break; 
				default: 
					$result = "(Encounter of level $this->difficulty not supported by MyNewEncounter.)"; 
			} 
		} 
		$this->text = $result; 
	}

Now go to your browser and open up testharness.php. It'll run a shallow test and tell you if anything seems immediately wrong. Then select your module from the Encounter dropdown menu and press the Test Encounter button. This will run a deep test, 10 iterations by default. You can just stay on that page now and reload it as you develop and debug your module.

Now let's add some actual encounters.

	function generate() 
	{ 
		$result = ""; 
		// 3-in-6 chance of an encounter 
		if( d6() <= 3 ) 
		{ 
			// Generate an encounter of the appropriate level. 
			switch( $this->difficulty ) 
			{ 
				case 1: 
					switch( d6() ) 
					{ 
						case 1: 
							$result = "a bumblebee"; 
							break; 
						case 2: 
							$result = "a butterfly"; 
							break; 
						case 3: 
							$result = "an inchworm"; 
							break; 
						case 4: 
							$result = "a sneaky snake"; 
							break; 
						case 5: 
							$result = "a bunny"; 
							break; 
						case 6: 
							$result = "a horsie"; 
							break; 
					} 
					break; 
				case 2: 
					switch( d6() ) 
					{ 
						case 1: 
							$result = "Billy Idol"; 
							break; 
						case 2: 
							$result = "a dire squirrel"; 
							break; 
						case 3: 
							$result = "a hippy"; 
							break; 
						case 4: 
							$result = "a licorice monster"; 
							break; 
						case 5: 
							$result = "a creepy clown"; 
							break; 
						case 6: 
							$result = "a 2nd level minion of the evil mastermind Xentar the Black"; 
							break; 
					} 
					break; 
				case 3: 
					switch( d6() ) 
					{ 
						case 1: 
							$result = "a scary dragon"; 
							break; 
						case 2: 
							$result = "a balrog"; 
							break; 
						case 3: 
							$result = "a vampire"; 
							break; 
						case 4: 
							$result = "Jolly R. Blackburn"; 
							break; 
						case 5: 
							$result = "an undead creepy clown"; 
							break; 
						case 6: 
							$result = "The evil mastermind Xentar the Black himself"; 
							break; 
					} 
					break; 
				default: 
					$result = "(Encounter of level $this->difficulty not supported by MyNewEncounter.)"; 
			} 
		} 
		$this->text = $result; 
	}

Reload testharness.php and see if there are any errors. You can select the difficulty of the encounters that testharness generates by entering a number in the Encounter Difficulty field on the form.

So, this is all well and good, but gee whiz that is a lot of spread out code, and hard to read and manage. Let's try to clean it up a little bit!

	function generate() 
	{ 
		// A table of tables, indexed by [encounterLevel][encounter]. 
		$table = array( 
					1	=> array( 
								"a bumblebee", 
								"a butterfly", 
								"an inchworm", 
								"a sneaky snake", 
								"a bunny", 
								"a horsie" 
							), 
					2	=> array( 
								"Billy Idol", 
								"a dire squirrel", 
								"a hippy", 
								"a licorice monster", 
								"a creepy clown", 
								"a 2nd level minion of the evil mastermind Xentar the Black" 
							), 
					3	=> array( 
								"a scawwy dwagon", 
								"a balrog named Bill", 
								"a vampire", 
								"Jolly R. Blackburn", 
								"an undead creepy clown", 
								"The evil mastermind Xentar the Black himself" 
							) 
			); 
 
		$result = ""; 
		// 3-in-6 chance of an encounter 
		if( d6() <= 3 ) 
		{ 
			// If the table has in index set for the desired encounter difficulty, 
			//	pick a random encounter from the appropriate subtable.  Otherwise 
			//	set an error message. 
			if( isset($table[$this->difficulty]) ) 
				$result = $table[$this->difficulty][mt_rand(0,count($table[$this->difficulty])-1)]; 
			else 
				$result = "(Encounter of level $this->difficulty not supported by MyNewEncounter.)"; 
		} 
		if( $result != "" ) 
			$result = "<b>Encounter:</b> $result"; 
		$this->text = $result; 
	}

There! That's a little cleaner, even if the code isn't as straightforward. Run it through the testharness again to check for errors. Be sure to check the edge cases to, like encounter levels 0 and 4 and make sure that the first and last entries in the subtables are getting picked sometimes and not skipped.

So, the complete file, when we're all done, looks like this:

MyNewEncounter.php.inc
<?php 
 
include_once( "Generators/iEncounterGenerator.php.inc" ); 
include_once( "Util/Dice.php.inc" ); 
 
class MyNewEncounter implements iEncounterGenerator 
{ 
	private $text = ""; 
	private $difficulty = 1; 
 
	function getGeneratorName() 
	{ 
		// Change this to the short name of your generator. 
		return "My New Encounter"; 
	} 
 
	function getGeneratorNotes() 
	{ 
		// Insert your notes here, or return an empty string if you have none. 
		return "Just some silly tutorial encounters!"; 
	} 
 
	function __construct( $difficulty=1 ) 
	{ 
		// Sets the difficulty of the encounters that will be generated by the 
		//	generate() function. 
		if( $difficulty < 0 ) 
			$difficulty = 0; 
		$this->difficulty = $difficulty; 
	} 
 
	function getEncounterDifficulty() 
	{ 
		return $this->difficulty; 
	} 
 
	function generate() 
	{ 
		// A table of tables, indexed by [encounterLevel][encounter]. 
		$table = array( 
					1	=> array( 
								"a bumblebee", 
								"a butterfly", 
								"an inchworm", 
								"a sneaky snake", 
								"a bunny", 
								"a horsie" 
							), 
					2	=> array( 
								"Billy Idol", 
								"a dire squirrel", 
								"a hippy", 
								"a licorice monster", 
								"a creepy clown", 
								"a 2nd level minion of the evil mastermind Xentar the Black" 
							), 
					3	=> array( 
								"a scawwy dwagon", 
								"a balrog named Bill", 
								"a vampire", 
								"Jolly R. Blackburn", 
								"an undead creepy clown", 
								"The evil mastermind Xentar the Black himself" 
							) 
			); 
 
		$result = ""; 
		// 3-in-6 chance of an encounter 
		if( d6() <= 3 ) 
		{ 
			// If the table has in index set for the desired encounter difficulty, 
			//	pick a random encounter from the appropriate subtable.  Otherwise 
			//	set an error message. 
			if( isset($table[$this->difficulty]) ) 
				$result = $table[$this->difficulty][mt_rand(0,count($table[$this->difficulty])-1)]; 
			else 
				$result = "(Encounter of level $this->difficulty not supported by MyNewEncounter.)"; 
		} 
		if( $result != "" ) 
			$result = "<b>Encounter:</b> $result"; 
		$this->text = $result; 
	} 
 
	function getText() 
	{ 
		// This function returns the last result produced by generate(). 
		return $this->text; 
	} 
} 
 
?>

Generating the number appearing based on effective encounter level and other such things is an exercise left up to the reader. It's not very hard though; all just a matter of rolling on tables and building up an output string.

Thanks for reading, and we hope that you are as excited about this project as we are!

Code Submission Guidelines

We'd be ecstatic if you'd help us out with building encounter generators for many different systems and game versions. Not only does it make for funner dungeons, but you get your name listed in the credits, which is always super cool, right? :P

If you'd like to build a generator and submit it to us…

  • Please try to avoid using datafiles from on disk or any kind of DB, if you can. Just to reduce the possibility of hax and such from improperly sanitized input that ends up going into a pathname somewhere. No big deal if you need to use files on disk, just make sure you throughly sanitize any pathnames before loading anything.
  • Please clearly comment any complex portions of the code and algorithms. Treat us like we're dummies (cos we probably are, depending on our level of caffeine deprivation, lol). :P We'll need to audit the code before we add it to the site, and if we can't figure out what it's supposed to be doing, we may not be able to include it. >_>
  • Please consider using Allman-BSD style bracing like in the examples above. If you've got a serious aversion to Allman style, that's cool. We can always autoformat the code. But sometimes that doesn't work so well, so if you have no particular preference in coding style… We like Allman-BSD. :P

Please let us know if you have any plans, and what you want to work on, so that we can coordinate our work with yours as efficiently as possible! :P

Contact: Email to Leaf

adventuregenerator/test_harness_encounter_generator_tutorial.txt · Last modified: 2012/02/08 15:23 by leaf

Copyright © 2009, 2012-2013 by L. Adamson, unless otherwise stated.
If you see something here that you like, and find it useful or learn something, please consider making a quick, easy, and secure donation via PayPal.
Your support is what keeps this whole thing going!