tetra-turing-hunt-engine

Everything you need to know about Maps

A Map is an area that a player can explore in the game. Tetra is only capable of rendering a text-based map wherein each ‘tile’ is occupied by a unicode character. For example;

eg_map

The entire game comprises of several such Map objects stitched together to create an immersive world. It is strongly advised to split the world into multiple Map objects instead of a single large Map.

Surfaces

Each unique unicode character used in the Map is associated with a Surface object, which can carry certain properties that change how the player can interact with that tile. We can broadly classify a Surface as being a functional unit with some interaction mechanics, or a purely aesthetic object, i.e, a Decoration (for eg. solid walls, houses, etc).

Creating a surface

A json file with a .surface suffix is used to store the metadata for each surface object. A typical surface file will look like so:

{
    "name" : "Ground",
    "character" : " ",
    "walkable" : true
}

If a surface file is created, it must have atleast these three properties, wherein walkable indicates whether the player can traverse over the tile. Additional properties may be added, accompanied by the code to handle these properties within the game to introduce new mechanics for these surfaces by editing the libs/surfaces.py

Decoration tiles

Some surface tiles may serve a purely aesthetic purpose and it becomes cumbersome to create .surface files for all such tiles. Such surfaces are classified as a Decoration which is assigned by default if the .surface file for a specific unicode character does not exist.

The equivalent property list for any <character> which is a Decoration might look like so:

{
    "name" : "Decor",
    "character" : <character>,
    "walkable" : false
}

Now that we have dealt with the building blocks of a Map, let us move on to the actual Map object.

Maps

A json file with a .map suffix is used to store the metadata for each Map object. A typical map file will look like so:

{
    "name": "Home",
    "raw" : "┌────────────┤  ├─────────────┐  ┌────────────┤  ├─────────────┐
│~~~~~~~~~~~~│  │~~~~~~~~~~~~~│  │~~~~~~~~~~~~│  │~~~~~~~~~~~~~│
│~~~~~~~~~~~~│  │~~~~~~~~~~~~~│  │~~~~~~~~~~~~│  │~~~~~~~~~~~~~│
│~~~~~~~~~~~~│  │~~~~~~~~~~~~~│  │~~~~~~~~~~~~│  │~~~~~~~~~~~~~│
┴────────────┘  └─────────────│  ┴────────────┘  └─────────────│
                                                                
┬────────────┐                │  ┬────────────┐                │
│╒╒╒╒╒╒╒╒╒╒╒╒┆                │  │╒╒╒╒╒╒╒╒╒╒╒╒┆                │
│╒╒╒╒╒╒╒╒╒╒╒╒┆                │  │╒╒╒╒╒╒╒╒╒╒╒╒┆                │
│╒╒╒╒╒╒╒╒╒╒╒╒┆                │  │╒╒╒╒╒╒╒╒╒╒╒╒┆                │
│╒╒╒╒╒╒╒╒╒╒╒╒┆                │  │╒╒╒╒╒╒╒╒╒╒╒╒┆                │
│╒╒╒╒╒╒╒╒╒╒╒╒┆                │  │╒╒╒╒╒╒╒╒╒╒╒╒┆                │
│╒╒╒╒╒╒╒╒╒╒╒╒┆                │  │╒╒╒╒╒╒╒╒╒╒╒╒┆                │
└────────────┤  ├─────────────┘  └────────────┤  ├─────────────┘",
    "init_pos": [5, 1],
    "exits":{
                "5,0" : ["Large Home", [5, 95]],
                "5,63" : ["Large Home", [5, 1]]
                },
    "items": {
        "Teleporter": [13, 47],
        "Shoe": [11, 47]
    },
    "items_on_startup": ["Teleporter"]
}

Name

This is a unique identifier for each Map, with some relevance to the area it is meant to depict.

Raw

This field carries the raw text data comprising of unicode characters, separated by newline (\n) characters to start a new line of the map.

A simple coordinate system is utilized to refer to specific tiles, namely [x, y] == [row, col]. This restrict the coordinates like so; 0 <= x <= nrows and 0 <= y <= ncols.

Init_pos

This is simply the spawn coordinates for the player, utilized only if the Map is set as the Home (the starting map when the game is launched) for the player.

Note: Do not set any coordinate over a non-walkable surface tile! This will cause the player to get stuck with no fix other than to restart the game.

Exits

This is an important field that provides information on map transitions (switching from one Map to another). For example, such a transition could be between the connection of two areas, or to enter a building, etc.

Three pieces of information are required to create a link between two Map objects for such a transition. Let us take two maps, Home (current map) and Large Home (map to transition to) for instance;

Multiple such transition points may exist, so the exits field is a dictionary with key-pair values like so:

"x_i, y_i" : ["Large Home", [x_f, y_f]]

The odd structure of this specification is due to limitations of json files, but the above example should suffice to specify your own exit points. A specification with multiple exit points might look like the following:

"exits":{
            "5,0" : ["Large Home", [5, 95]],
            "5,63" : ["Larger Home", [5, 1]],
            .
            .
            .
            "5,1" : ["Even Larger Home", [5, 95]],
            "5,60" : ["Largest Home", [5, 1]]
            },

Note: The last coordinate in the dictionary should not have a trailing , but every entry preceding it must have one. In case of a single exit point, this would look like so:

"exits":{
            "5,0" : ["Large Home", [5, 95]]
        },

Another Note: Although the process of constructing this data is cumbersome, you can use the in-built GPS gadget in the game to quickly identify the coordinates involved in these transitions.

Items

This field simply specifies the locations of various Item objects to be placed in the Map. Further details to create an Item object can be found in the Items section.

The items field is also a dictionary with key-pair values like so:

"<item name>" : [x_pos, y_pos]

A typical specification with multiple items looks like so:

"items": {
    "Teleporter": [13, 47],
    "Shoe": [11, 47],
    "Mirror": [16, 34]
}

Items on startup

This (optional)field is an array, listing the names of the items in the map that will be spawned on start up. Items specified in the previous field can be dynamically spawned/removed during the game like so (in the code files of some other item):

game.active_map.place_item("Teleporter", game)
game.active_map.remove_item("Teleporter", game)

Any items to be dynamically altered must be specified in the map file beforehand (in the previous items field); an arbitrary item created during runtime cannot be placed on the map.

Note: If the items_on_startup field is not specified, all items mentioned in the items field will be spawned on start up.

Making your own Maps

Now that we know how to provide the meta-data for a Map object, how do we create the Raw text map? It is quite cumbersome to type out each character by hand, so we have written a Map Creation Tool which is a jupyter notebook that facilitates this process. It can be found in the path /helpers/map_creation_tool.ipynb.

The Input

input_map

You can create a map visually by using any pixel art engine (we recommend https://www.pixilart.com/). Use colors instead of unicode characters to draw the map, and feed it into the Map Creation Tool to convert it to unicode.

The Output

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                             #                                   
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                           #                                   
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                           #                                   
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                           #                    %              
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                           #                   %%%             
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                           ##                 %%%%%            
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                            #                %%%%%%%           
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                            #               %@_____@%          
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                            #              %%@     @%%         
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                            ##               @     @           
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                             #               @     @           
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                             ##              @ ___ @           
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                              #              @ @ @ @           
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                              ##             @_@ @_@           
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                               ##              # #             
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~                                #            ### #             
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~               ooooo            ###         ##   ##            
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~             oo$$$$$oo            ###########     #            
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~            o$$ooooo$$o                           #            
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~           o$$ooooooo$$o                          #            
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~            o$$ooooooooo$$o                         #            
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~                  o$ooooooooooo$o                        ##            
      ~~~~~~~~~~~~~~~~~~~~                    o$ooooooooooooo$o                      ##             
        ~~~~~~~~~~~~~~~~                      o$ooooo+++ooooo$o                    ###              
        ~~~~~~~~~~~~~~~                       o$ooooo+$+ooooo$o                #####                
                                              o$ooooo+++ooooo$o              ###                    
                                              o$ooooooooooooo$o             ##                      
                                               o$ooooooooooo$o            ###                       
                                               o$$ooooooooo$$o          ###                         
                                                o$$ooooooo$$o           #                           
               ^^^^^^^^^^^^^^^^^^^^              o$$ooooo$$o           ##                           
            ^^^^^^^^^^^^^^^^^^^^^^^^^             oo$$$$$oo            #                            
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^            ooooo              #                         ###
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                            ##                       ##  
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                           ##                      #   
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                          ###                   ##   
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                            ###                ##    
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                            ####            ##     
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                              -----     ####      
  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                  #######         
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                 
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                 
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                

You will have to map each color to a specific character (based on how good it looks) and the tool will generate the raw text map that is required.

The Process

The jupyter notebook provides step-by-step instructions to generate the finale output. In fact, it also creates the .map file and corresponding .surface files for every unique tile in the map, based on a series of prompts which you will have to answer.

Things to keep in mind while designing the map