Interactive Maps

Usually we make a map in CC3+, and when done, we export it to an image or print it, turning it into a static thing. This is required when we wish to use the map outside of CC3+, but it also takes away many fun things we can do with the map.
CC3+ does allow us to make really dynamic maps however, maps that change based on triggers in the map. I’ve already talked about a simple version of this in the article on Showing and Hiding Map Features, but let us take this much further and make a map with a large selection of interactive elements.

Now, before reading any further, I strongly suggest that you download the example map and give it a good try before reading further (requires DD3). Another much simpler example shows moving lights (Works without DD3).

So, I assume you tried the example map so you have a better understanding of what I am going to explain, but in short, the example map allows you to move (as in jump from spot to spot by clicking on them) a character around in a dungeon, much like a simple computer game. Clicking on various places lets you open doors and find items, and some outcomes differ depending on if you are carrying the right item or not. The map automatically moves with your character, revealing new places as you go. Some elements of the map also change as you activate them, such as statues rotating. The map itself is possibly know to you, as I reused an older map I had made for this, just to show that you can easily adapt an existing map, while saving me the work of making a new one just for this.

I also assume a basic knowledge about making hotspots here. If you need a refresher, check out the basics from the article I linked above.

Before we get into the details, another example can be seen in this forum topic, where forum user Relyt is making a dungeon map from concentric circles that can be rotated by clicking on levers in the map.

So, what kinds of interactivity can we add to a map? To answer this, we need to look at what we can do with macros in CC3+, and the answer here is a whole lot. One of the great things about CC3+ is that most commands are actually macro commands. This means that we can do almost anything in a macro that we can do manually. Macros can draw shapes, move entities, copy entities, place symbols, erase entities, set properties, hide and show sheets and layers, change the view and zoom and so on, the list is almost as long as the feature set of CC3+. There are a few things we cannot do, like manipulate effects, but all in all, we have many possibilities.

Now, CC3+ cannot run macros at arbitrary times, they can only be called by the user. In my example map, this is set up with all the various spots the user can click in the map to navigate through it, and this is done by using CC3+ hotspots. A hotspot is a defined spot (rectangle) in a map that will run a macro command when clicked. This can be a simple one-liner, like opening another map like we do in the community atlas, or it can be a rather complex macro executing a whole list of instructions. All the clickable places in my example maps are individual hotspots with their own macro that will be run. Note that the macros are completely contained in the hotspots, so you don’t need to distribute any additional macro files. The macros in the hotspots can of course call macros from the main macro file, but it is better to avoid this since this makes distribution easier. If you want multiple hotspots to call the same macro code, you can actually have one hotspot call another instead.

Here is a list of what I do with hotspots in the example map.

  • Move the character symbol
  • Move the view
  • Hiding/showing layers
  • Rotate Symbols
  • Act conditionally based on earlier user actions

Manipulating Layers

One of the important things I do in this map is manipulating layers. As you know, all entities have both a layer and a sheet. The sheet controls drawing order, what appears on top of what, as well as effects. Layers on the other hand is mostly a grouping mechanic. By strategically assigning entities to layers, we can hide or show them as desired. For example, to avoid all the hotspots in the map being available at all times (Allowing you to randomly click in the darkness), they are assigned to layers, and I only show the layers relevant to the current location of the character. Hotspots are only active as long as the layer they are on are shown. Other things I use layers for are the opening and closing of doors. I placed the open door symbol on one layer, and the closed one on the other. Then by just hiding the closed door layer and showing the open door layer, we “open” the door in the map. For example, to “open” door number 1, I run the following lines:

TOGLF DOOR 1* // Toggles the visibility of all layers starting with DOOR 1

This line toggles the visibility of all layers that starts with DOOR 1. In my map, I have two of these, DOOR 1 OPEN (which starts hidden and contains an open door symbol) and DOOR 1 CLOSED (Starts visible and contains a closed door symbol). Toggling the visibility ends up hiding the closed door and showing the open one with one easy line.

Whenever the character moves, I call a few lines that look something like this, exact lines depends on the players visibility from that location

HIDEF LOCATION*  // Hides all sheets that starts with the name LOCATION
SHOW LOCATION 2  // Shows the spesific sheet named LOCATION 2

Moving the View

I also used layers for view manipulation. CC3+ has a great command, ZOOML, that zooms to the extents of a specific layer (even if it is hidden). So, what I did was putting a rectangle around the desired view for each layer (and then put the rectangle on a hidden sheet so it isn’t visible). I recommend opening the example map, turning off effects and then showing all layers and sheets as this gives you a nice view of how I set up this. The command is simply

ZOOML LOCATION 1 // Similar to zoom extents, but zooms to the content of the layer LOCATION 1 instead
                 // of the whole map

Moving the Character

Moving the character is slightly more complex, since you need to know where to move it from and where to move it to. I solved that by storing the starting coordinates of the player in a variable on map load, and updating it as you move around. Each hotspot also contains a variable with the coordinates of that hotspot. I then move the character from the original character coordinates to the coordinates provided by the hotspot, and then update the player coordinate to the new location. The player symbol is also on a layer by itself to make it easy to select it. The code on the movement hotspots looks like this

GP x 65,77.5     // Stores the coordinates of the current "target" in the variable x.
SELSAVE          // Saves the current select method so we can restore it later
SELBYL           // Change the select method to select by layer
MOVE PLAYER;p;x  // Move all entities on the PLAYER layer from location p to location x
                 // (p is the current location of the player)
GP p x           // Updates the players current position
SELREST          // Restores the selection method

The movement target hotspots also contain the view update code discussed in previous paragraphs so we can update the view when the character moves to a new location.

Rotating Symbols

In the end room, there are three symbols that can be rotated. The statues are rotated as you click on them, and when you set the correct combination, the sarcophagus rotates to reveal the trapdoor to escape the level.

This is all done much the same way as I move the character. Each rotating element is located on a separate layer, allowing me to select it by layer, and then I use the rotate command to rotate it around a specific point. In this map I only rotate individual symbols, but you can also rotate groups of symbols. When you rotate multiple symbols, they rotate together as a unit around the rotation point and not individually. You can see an example of this in Relyt’s dungeon map which I linked earlier.

In my case, I placed the hotspots to rotate the statues on the statues themselves, but the hotspot can really rotate entities anywhere in the map. For example, it is also the hotspots on the statues that rotate the sarcophagus when the correct “code” is entered.

ROTATE STATUE 1;90;170,90 // Rotate the statue on the layer STATUE 1 by 90 degrees around the
                          // point 170,90

Act Conditionally

One of the more interesting things is conditional actions. For example, you can’t open the door into the inner chamber until you have found the key. So, how can one implement this? The trick here is to use variables to keep track of things. When the map is first loaded (or the restart button is clicked), I initialize the key variable to 0 (0 indicating the player don’t have the key, 1 indicating that he does). When a variable is set, CC3+ keep it in memory until you close the program.

GN key 0 // Stores the number 0 in the variable named key

When the player finds the key by clicking the hotspot where it is “hidden”, I update the variable to 1

GN key 1

Now, on the door, I have the following code

IFZ key LOCKED                      // Check if the variable key is 0 (zero) and if it is, jumps to
                                    // the label LOCKED (Otherwise it just continues with the next
                                    // line of code)
TOGLF DOOR 3*                       // Since we got to this line, we have the key, so toggle the door
                                    // layers to show the open door instead of the closed one
TOGL LOCATION 7                     // Toggles the visibility of location 7. Ensures that hotspots in
                                    // the room are only visible when the door is open
GOTO END                            // Jumps to the end label
:LOCKED                             // LOCKED label, ended up here if key was zero earlier
SHOW LOCATION DESCRIPTION 3 LOCKED  // Shows a message on the screen that this door is locked (Message
                                    // is just text on the specified layer)
:END                                // END label

Final Comments

The macros in my map contains a lot more stuff than presented here, but I have presented the main mechanics for handling this. I recommend you continue on by opening the example map, turn off effects and show all sheets and layers, and then show the hotspots so you can see the code contained in each hotspot. (Use numeric edit on a hotspot to read/change the code)

In most of the macros, you’ll see the below commands. These are just helpful additions to prevent screen and command line flashing when the macro runs, and some maintenance

ECOFF    // Turn off command line echo, prevents all the commands appearing on the command line as 
         // they are called
RDOFF    // Turn off automatic redraws. This prevents many visual updates as the macro runs
SELSAVE  // Saves the users current selection method. This is important since we later change the
         // selection method in the macro, and we wish to restore it to the previous value when we
         // are done. If not, the user will experience problems selecting entities, which is a bad
         // thing
SELREST  // Restores the saved selection method
RDON     // Turns automatic redraws back on
REDRAW   // Since automatic redraws have been turned off while working, perform a manual redraw now
         // (Any zoom command can replace this)
ECON     // Turns command line echo back

One of the more complex things in the map is how to move the final sarcophagus by rotating the statues to the correct positions. In my map, I handle this by keeping a variable for each statue that is updated each time it is rotated, and when the sum of these two variables is zero, I move the sarcophagus. Each variable starts at a different number, and i did set it up with a modulo calculation, which basically means that each time it reaches 4 it is reset to 0, so the numbers always range between 0 and 3. I also use these numbers to rotate the statues back to the starting positions when you restart the map.


One Response to “Interactive Maps”

  1. Excellent fun! Thank you for this Remy 😀