v1.00
This is a fork of the original team project.
- Implemented the game renderer and user-input handling using Python and libtcod
- Built the specification parser (lexer + tokeniser) for dynamic dungeon definitions
- Developed sample maps and contributed to testing/debugging the core engine
- Added feature-set: configurable monster behaviour, spells & map generation parameters
Original Team Repository: https://github.com/farzanab/DunGen
DunGen is a dungeon crawl generator for roguelike designers. It is meant to be a tool for designers to easily test different behaviours of monsters, vary the looks of different elements, work with player abilities/spells and attributes of the different entities, without having to write or modify a lot of real code. DunGen takes a file containing just a list of specifications and definitions with some code to create a playable roguelike.
- DunGen requires Python 3.
- Check whether it's the 32-bit version or the 64-bit version of Python. This will decide which DLLs are required. Copy the two DLL files from the appropriate folder inside
DLLstodungen. - If these files are absent, an
SDL2.dll not foundexception will be thrown. - If the incompatible DLLs are copied, an
Incompatible architectureerror will be generated.
- Write a specifications file containing all the details of the map, the monsters and the abilities to be included.
- Copy it to the
dungenfolder. - Start the Prompt in the folder. Enter
python dungen.py <specs filename>. Make sure to enter the full filename including the extension. - Test the game that opens up in a new window.
A usual file for DunGen usually contains blocks of the following form. Details are given in later sections.
name : <some name>
rooms : <number>
small : <another number>
.
.
monster : <num>
<some monster name> : <num>
<another monster> : <num>
..
define player
char : <character>
colour : <one of the available colours>
hp : <num>
attack : <num>
defence: <num>
mana : <num>
define monster <name>
char : <char>
colour : <colour>
.
<stats>: ..
.
behav : <predefined or user-defined behaviour>
define monster <another name>
.
.
define behav <behav name>
...
<some 'code'>
...
define spell <key>
name : <name of spell>
dirn : <directional spell?>
cost : <num>
...
<some 'code'>
...
...
<more definitions of monsters/behavs/spells>
...
DunGen has defaults for most things.
There are defaults for most of the specifications.
There is an inbuilt monster type simply called monster.
There are 3 inbuilt monster behaviours, that will be covered later in this README.
Having all these defaults makes it very easy to make (incredibly basic) games. A file with just two lines is enough to get DunGen going. (This file is present in the samples folder as rrrbasic.txt.)
rooms : 10
monster : 40
Of course, this won't make an interesting game but it's enough to get things moving.
- Move using the arrow keys.
- When a monster is exactly one block away, attack the monster by pressing the arrow key in that direction.
- When a monster is killed, it turns to a corpse(%) and walking over it gives the player a health boost.
- Get to the door, which leads to a new map.
Each file must start with a block containing at least the number of rooms and any of the specifications shown below. (Note that this list is not complete and only includes the more important ones. For a complete list, see the Appendix.) The specifications do not have to be in any specific order.
name : <string>
player : <string>
wall_colour : <colour>
wall_char : <char>
floor_colour : <colour>
floor_char : <char>
obstacle_colour : <colour>
obstacle_char : <char>
rooms : <num>
small : <num>
medium : <num>
large : <num>
monster: <num>
<user-defined monster> : <num>
-
<string>can be any string of characters. -
<colour>must be one of the colours available. (See the Appendix at the end of this README for a list of valid colours.) -
<char>must be a single character. Space(' ') won't work. -
<num>must be a non-negative integer. (Certain fields only take positive integers. However no such fields are present in the above sample.) -
<user-defined monster>is the name of a monster type which is defined later in the file. -
nameis the name that will be displayed in the title bar. -
playeris the name of the player character. -
wall_colour,floor_colourandobstacle_colourare the colours which shall be used to represent the respective features on the map.wall_char,floor_charandobstacle_charare the characters which shall be used to represent the respective features on the map. -
roomsis the total number of rooms on the map.small,mediumandlargeare the number of rooms of that particular size. Any combination of these four will work as long as they are not incompatible (likeroomsbeing less than the sum of the other three fields). At least one of them must be present. -
The number of monsters of a particular kind is defined as shown above (
<monster type> : <num>).monsteris just a predefined monster type that can be used without defining.
The definition of a player must start with define player.
Similarly a monster definition must start with define monster <name>.
The following fields are available for both monsters and the player (except where specifically noted).
char : <char>
colour : <colour>
hp : <num>
attack : <num>
defence: <num>
behav : <behav name>
mana : <num>
charandcolourdenote how that character will be shown on the map (when alive).hp,attackanddefenceare statistics or attributes of the character.hpis the health or hitpoints.attackanddefenceare used for determining the results of an attack.behavis only for monsters.behavdecides which behaviour will be followed by the monster when it takes its turn. must be defined somewhere in the file (or must be a predefined behaviour).manais only for the player.manais the resource which will be used for casting spells (something which will be seen after a few sections in this README).
The rrbasic.txt file in samples makes use of whatever has been covered in these last two sections. The monsters only use the predefined behavs basic, scaredycat and brave for now. As always, all fields do not need to be specified in which case the default values will be taken.
Behaviours and spells are defined through some 'code'. Basic programming constructs like conditionals and loops are available. If a language like C or Java is familiar, only a few major differences have to be taken care of.
- Only integers can be handled. (This means no actual booleans. 1 or 0 is the result of a logical/relational expression.)
- Variables are declared using
var <variable>and must be initialized at declaration. - Available operators are
+,-,*,/,~,>=,<=,>,<,==,!=,&,|and!. Parentheses can also be used. Most of the operators behave the same way as in other languages. The different ones are shown below.~is the unary negation operator.-cannot be used directly for storing negative integers.&and|are the relationalANDandORoperators, not bitwise operators.
- Writing an
if-elseis done byImportant things to note include the use of braces{} (instead of parentheses()) for enclosing the condition and the absence of an actualif {<conditional expression>}{ <statements if true> } { <statements if false> }elsekeyword. The braces are always essential for the blocks of statements even if there is only one statement to be executed.
The second block may be skipped for a simpleifstatement.if {<conditional expression>}{ <statements if true> } - Writing a loop is done through
whileas shown belowLike thewhile {<condition>} { <statements to execute> }ifcondition, the condition forwhilemust be enclosed in braces and braces are necessary everywhere. - Scoping is implemented for the blocks used in
ifandwhile. However separate blocks of code enclosed by braces (which are not used iniforwhile) cannot be created.
There are also some other predefined variables and functions which allow the code to interact with the actual game variables. The specifics will be covered in the respective sections on Creating Behaviours and Creating Spells.
- The predefined variables (called 'stats' from here onwards) already contain the appropriate values taken from the actual game variables. Modifying these may or may not change the actual game variables. These can be used just like other variables.
- Some 'functions' are used to detect the presence of certain features at a particular location on the map.
- 'actions' are like functions which make changes to the monsters or the player character. For the 'functions' and 'actions' any integer arguments can also take expressions that result in an integer.
The definition of a behaviour must start with define behav <behav name>.
This must be followed by 'code' starting from a new line.
Available stats for behaviours:
health: The monster's current hp. Modifying this changes the actual hp in the game.maxhealth: The monster's maximum hp (the hp in the monster definition). Modifying this does not change the actual max hp.phealth: The player's current hp. Modifying this changes player's actual hp in the game.maxphealth: The player's maximum hp (the hp in the player definition). Modifying this does not change the actual max hp.player_distance: The player's distance from this monster. Modifying this does not change the actual distance.
These stats can be used in expressions just like other variables. For example, health > maxhealth/2 & phealth < maxphealth/2 & flag is a valid expression (where flag is a variable that has already been declared).
Available actions for behaviours:
move(<dx>, <dy>)
Moves the monster by (dx, dy). In other words, if the monster's current position on the map is (x, y), it changes it to (x + dx, y + dy) if possible.
An important thing to take care of is that the y-axis on the map points downward. So moving a monster by (0, 1) moves it one space below.move_towards()
Tries to move the monster one space closer to the player character.flee()Tries to move the monster one space away from the player character.move_random()Moves the monster one space in a random direction.attack([num])Executes the monster's attack on the player. The optional argument is the damage. If not given, the standard attack damage is considered.
These actions are executed by just calling them like functions. Some examples of valid action statements are given:
move(1, ~2);attack(maxphealth - health);attack(damage);where damage is a variable that was defined earlier
See the rbasic.txt file in samples for some behaviours and how a behaviour can be used by multiple monsters. Also see the appendix for the code for the predefined behaviours (brave, scaredycat and basic).
The definition of a behaviour must start with define spell <key>. Here is the key which must be pressed to activate the spell.
This may be followed by certain specifications:
name : <string>
cost : <num>
dirn : <bool>
nameis the name of the spell which will be displayed when using the spell.costis the amount of mana that will be used up in casting the spell.dirndetermines whether the spell is directional or non-directional. It must be one ofyes,y,true,no,norfalse.
If it is one of the first three, the spell will take a direction as input (through the arrow keys) once the spell is active before being put into effect. If it is one of the last three, the spell will not take any direction and always have the same effect. Directional spells require some more care than non-directional spells. These will be covered in the next section.
After the specifications (if any) have been specified, the code must be written for the spell.
Available stats for spells:
x: The player's x coordinate on the map. Modifying this does not change the actual x coordinate.y: The player's y coordinate on the map. Modifying this does not change the actual y coordinate.health: The player's current hp. Modifying this changes the actual hp in the game.maxhealth: The player's max hp. Modifying this changes the actual max hp in the game.mana: The player's current mana. Modifying this changes the actual mana in the game.
Available functions for spells:
is_monster(<x>, <y>)Returns 1 if a monster is present at (x, y) on the map else 0.is_wall(<x>, <y>)Returns 1 if a wall is present at (x, y) on the map else 0.in_fov(<x>, <y>)Returns 1 if the player can 'see' (x, y) on the map else 0.
These functions can be used just like any functions that return integers. They can be used in an expression or on their own wherever an expression can be used.
However the above functions are particularly useful in conditionals.
Available actions for spells:
hit(<x>, <y>, <damage>)Tries to hit whatever may be present at (x, y) on the map and reduce its hp by the damage parameter.
The above functions and stats make it possible to go over an area and hit everything which satisfies certain conditions.
For example, an area blast may go over a rectangular patch on the map centred at the player's position and hit everything which is visible. (See rbasic.txt for the code to implement area blast and some other spells.)
Directional spells are written exactly like non-directional spells. Only a couple of things need to be kept in mind.
dirnmust be specified and be one ofy,yesortrue. If it is not specified, the spell will be treated as a non-directional spell since that is the default.- The code for the spell must be written exactly as you would like the spell to work if the chosen direction is right. DunGen will take care of what to do if the other directions are chosen.
Directional spells allow for simple ranged attacks in a single direction or a region based on the direction. See basic.txt for some directional spells.
- Move using the arrow keys.
- When a monster is exactly one block away, attack the monster by pressing the arrow key in that direction.
- When a monster is killed, it turns to a corpse(%) and walking over it gives the player a health boost.
- Press the 'H' key (if it has not been used for a spell) to see the health levels of the monsters nearby.
- Press the key for a spell to execute it. Note that a spell can be cast only if the cost can be afforded. If it is a directional spell, choose a direction using the arrow keys.
- Note that in one turn, the player can either move once, attack once or cast a spell. A spell is used up if it can be afforded even if it doesn't have any visible effect.
- Get to the door, which leads to a new map.
- When a new dungeon is created, the player's health is carried forward, max health and mana increase by 5. The monsters become a bit tougher.
- Press the Esc key to quit at any time.
- If you die and wish to play again, you'll have to quit and start DunGen again with the file.
DunGen supports 36 colours. Out of these, 30 are variations of 10 basic colours (3 shades for each) and 6 are other colours.
The three shades of a basic are simply <colour>, light_<colour> and dark_<colour>.
Basic colours:
- red
- orange
- yellow
- cyan
- green
- blue
- violet
- crimson
- grey
- sepia
Other colours:
- brass
- copper
- gold
- silver
- black
- white
-
name -
player -
wall_colour -
wall_char -
floor_char -
floor_colour -
obstacle_char -
obstacle_colour -
rooms -
small -
medium -
large -
small_min -
small_max -
medium_min -
medium_max -
large_min -
large_max
These are the minimum and maximum dimensions for those room types. Note that the side of a room includes the walls, so the minimum side must be at least 3. -
map_type
Either 1 or 2.
DunGen has two map generation methods.
The default (1) uses a random algorithm to fit all the rooms in a fixed map of size 80 by 40. It tries various possibilities before giving up. Due to this, it can be incredibly slow. If it is unable to generate the map, it gives the time taken and an error message.
The other (2) first decides the map size based on the number of rooms and then fits the rooms using a grid-based approach. Thus this always succeeds on the first try. However due to much less randomness involved, the rooms are placed in a more orderly fashion than usually expected from a dungeon.
Note that neither of these is particularly useful for maps with many rooms. The first because it won't be able to generate it all even after taking up much time. The other because the map will be so big, it won't fit completely on the screen. -
health_boost
The health boost the player receives after walking over corpses. -
fov
The player's field of vision or up to how many blocks away the player can see if nothing is blocking sight.
basic
if {health > maxhealth / 2} {
if {player_distance < 2} {
attack();
} {
move_towards();
}
} {
flee();
}
scaredycat
flee();
brave
if {player_distance < 2} {
attack();
} {
move_towards();
}