Getting Started with Macros – Part 2

Last month, I talked about the basics of macros, how a macro is basically just a series of commands executed in sequence. And for the majority of macros used in CC3+, it actually stops there. For example, one of the more common uses of macros are as part of drawing tools, where you draw a polygon with the tool, and then a macro built into the tool takes over and does it’s magic to the polygon, such as filling with with symbols (like trees for a forest), or automatically align the fill or other similar operations.

But of course, that the majority of macros used are rather simple doesn’t mean we doesn’t have more advanced functionality that is highly useful. For this month’s part, we’ll be talking about variables.

A variable is a way of storing data for later use, for example a value, a color, a coordinate or a string of text. For example, if you make a macro that draw polygons in alternate colors, you can ask the user for the colors at the beginning of the macro and store them in variables, and then refer to those variables later when you need to use those colors. Let us start out with such a macro as an example, and then analyze it


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
MACRO ALTERNATINGCOLORS
GCOL vFirst ^DPick first color
GCOL vSecond ^DPick second color
GP vCenter ^DPick location
FSTYLE SOLID
COLOR vFirst
CIRR 5 vCenter;
COLOR vSecond
CIRR 4 vCenter;
COLOR vFirst
CIRR 3 vCenter;
COLOR vSecond
CIRR 2 vCenter;
COLOR vFirst
CIRR 1 vCenter;
ENDM

As explained in the last installment, line 1 and 16 are just the start and end point of the macro, and required when you make a named macro in the CC3+ macro file, but not required if it is part of a drawing tool or found in a dedicated script file.

Lines 2 & 3 both uses the command CGOL. This stands for Get Color, and is the command used for getting hold of a color and storing it in a variable. In this case, we use the ^D modifier to ask the user for the color, but we could just as well have input it using a number right there in the macro. Of course, asking the user also require the user to respond to the request. Right now, if they abort it by trying to do something else, this macro will end up throwing multiple error messages. Error handling is a bit of a topic in itself which I will address later though, so for now let us just trust the user to reply properly. The colors are stored in the vFirst and vSecond variables

There are actually two ways the user can answer this prompt. One of them is to right click, which will bring up the color picker dialog (CC3+ knows this is what we need, since we ask for a color here) and then pick a color visually, or they can just type the numeric value for a color on the command line (such as 15 for white) and hit enter.

Line 4 asks for another piece of information, but this time we ask for a point (coordinate) using GP (Get Point). Again, the user can input the point either by clicking with the mouse in the drawing, or by typing a coordinate on the command line. The value the user picks is stored in the vCenter variable, and used as the placement point for our circles.

Finally, lines 6-15 do the drawing. These are basically pairs of commands that first sets the color to use using the COLOR command, alternating between our two colors, and the second line draw one of the circles in the concentric set of circles. The number you see is just the radius of the circle, drawing smaller one for each pass. (Remember we need to start with the largest one, because the last one we draw will end up on top)

 

What we have seen above is the core way variables are used in CC3+. We use a command, such as GCOL, GP or another one from that family to set the variable to a value, and when we desire to use it, we can just use the variable directly in the command instead of a regular value. CC3+ will always expand the variable to the value it represents. Because of this, you should be careful with the naming of your variables, because if you make a variable the same name as a command, it will actually take precedence over the command.

 

Let us change up our program a bit, so we can test manipulation of variables, not just set and use. This is our next example, with a few changed and added lines.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
MACRO ALTERNATINGCOLORS
GCOL vFirst ^DPick first color
GCOL vSecond vFirst+32
GP vCenter ^DPick location
GD vRadius ^DSpecify radius
GD vStep vRadius/5
FSTYLE SOLID
COLOR vFirst
CIRR vRadius vCenter;
COLOR vSecond
GD vRadius vRadius-vStep
CIRR vRadius vCenter;
COLOR vFirst
GD vRadius vRadius-vStep
CIRR vRadius vCenter;
COLOR vSecond
GD vRadius vRadius-vStep
CIRR vRadius vCenter;
COLOR vFirst
GD vRadius vRadius-vStep
CIRR vRadius vCenter;
ENDM

Most of this macro is still very similar to the previous iteration, but let us have a look at a couple of lines:

Line 3 no longer ask the user for the second color. Instead, we use the first color, and add 32 to it. Colors in CC3+ are grouped in bands of 16 colors, so that means going two bands up. Again, we have no error checking here, so we assume the user won’t pick a color from the bottom two rows. If they do, it won’t crash the macro, but we won’t have a valid second color. It is worthwhile to note here that there are no spaces before and after the + sign. CC3+ interprets spaces as a separator between parameters, but this calculation should be a single parameter, so write it without spaces. This applies to all our calculations.

Line 5 is new, and ask the user for the radius. GD (Get Distance) is used to get a length value, and let the user specify it either by clicking two points in the drawing to define the length, or write it as a number on the command line. Note that we will only collect the raw length, not the direction, so it will always be a positive value no matter where the user clicks in the drawing.

Line 6 is also new, and we use this to calculate the step, the difference between the sizes of the concentric rings. In the first example, this was hardcoded to be 1, but now we want it to be one fifth of the full radius.

In between each drawing (Lines 11,14,17,20) we also reduce the radius by the step value so each circle gets one step smaller.

 

With our second example, we’ve then shown how variables can be manipulated and worked with, without having to input from the user.

 

Interlude – Understanding Separators

At first glance, separators can look a bit confusing in CC3+ macros. For example, why are there semicolons (;) at the end of some of the lines in the macro above, but not all of them? That looks weird and not at all consistent. So, let us talk about them.

CC3+ has three separators, these being space, semicolon(;) and newline(enter). These are actually fully interchangable. So, for example, if I wish to put a command in a macro to draw a line from 0,0 to 100,100, I could write this multiple ways, for example

LINE 0,0 100,100

or

LINE;0,0;100,100

or

LINE
0,0
100,100

or

LINE 0,0;100,100

or any other combination. CC3+ is happy with either format. I am a strong proponent of one command – one line, so I don’t like to use enter mid-command,  and I find spaces more easily read than semicolons. So that is my style and the one I use in these examples. I sometimes use another style if it enhances readability in a particular situation.

This also means that you can have many commands in a row on the same line, or mix and mess it up real horribly. CC3+ knows how many parameter each command should have, so once it has read enough parameters for a command, it assumes the next things that comes is a new command, no matter if it is on the same line or a new line  or whatever. This means that this is also a fully valid way of writing the above macro

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
MACRO ALTERNATINGCOLORS
GCOL vFirst ^DPick first color;GCOL vSecond vFirst+32
GP vCenter ^DPick location;GD vRadius ^DSpecify radius;GD vStep vRadius/5
FSTYLE SOLID
COLOR vFirst CIRR vRadius vCenter;
COLOR vSecond GD vRadius vRadius-vStep CIRR vRadius vCenter;
COLOR vFirst GD vRadius vRadius-vStep CIRR vRadius vCenter;
COLOR vSecond GD vRadius vRadius-vStep CIRR vRadius vCenter;
COLOR vFirst GD vRadius vRadius-vStep CIRR vRadius vCenter;
ENDM

Not my preference, but as far as CC3+ is concerned, it is fully valid

Note that there are a few exceptions to the space/semicolon/newline rule. First of all, some commands expects spaces to be part of a string. For example, it would be difficult to write those ^D instructions to users without being able to type space. So some commands disable space handling for some parameters. You can still use semicolons and enters, but spaces is just part of the data. This will apply to most cases where a text string is expected. So in my alternate version right above, you’ll see that I stuck to spaces, but I had to use a semicolon at the end of those text strings.

The second exception isn’t really an exception at all, but related to the fact that some commands in CC3+ are self-repeating. The CIRR and LINE commands used above are two of those. If you try the LINE command normally in CC3+, you’ll notice it asks for the starting coordinates, then the ending coordinates, then it draws a line segment, but then it just goes on asking for more coordinates, letting you continue to draw segment after segment. There are three ways to end a self-repeating command, that is to start a new command, hit the esc key, or submit an empty value. The first two is a user interface thing, and not usable in a macro, but submitting an empty value is. And this is exactly what that semicolon on the end of the line is. When CC3+ sees the semicolon, it immediately sees that as the end of the current parameter, and that it should ask for the next one. Which it does. But that is the very next input from the macro? Well, that’s the newline, meaning we just submitted an empty value. CC3+ takes this as a signal to end the command. So, the way to end these self-repeating commands in macros is simply to have a semicolon on the end followed by a newline. Technically, you can do it with a space on the end and then a newline, but relying on an invisible character at the end is a recipe for bugs, so please don’t do that. Another option is to make two newlines, but to my eyes that looks more like someone who didn’t know exactly what they were doing and added newlines until it started working. I prefer the semicolon because it’s intention in that position is very clear.

Show our variables

If you need to figure out what variables are already set, use the LISTVARS command. It will show a list of all the variables set in the current instance. All variables are global to the current CC3+ instance and will survive even if you load a new map.

One question you may ask yourself when looking at that list is what type of variable are each? CC3+ doesn’t used typed variables, the content is evaluated as it is used. The various type commands (Get Color, Get Distance, etc) are only there to help you gather the value for the variable properly, but once the value is stored, they are untyped. Many people prefer to use a prefix on the variable name to show type, I’ve been using the generic v for variable, but common prefixes are xy for point, s for string, d for distance, n for integer and r for number. Another thing you may notice from the output is that CC3+ is not case sensitive for variable names (or anything else).

Setting variables

Above, I’ve shown a couple of common commands for setting variables. But there are a lot of different commands in CC3+ that gathers values and sets them in variables. For example, you can get the file name of the current file, you can get the extent of the drawing (or of a single sheet or layer), the name of the current sheet, and more. The Tome of Ultimate Mapping have a nice list in Appendix B of the Campaign Cartographer section (Page 268 in the current edition). Or if you don’t own the tome, you can also access the list of macro commands from the CC3+ help file, just expand CC3+ Reference -> Macros and pick Alphabetical List of Macro Commands.

 

If you have questions regarding the content of this article, please use the ProFantasy forums. It can take a long time before comments on the blog gets noticed, especially for older articles. The forums on the other hand, I frequent daily.

Comments are closed.