I usually just think of macros as another sort of variable: you type
- define whatever something_else
and then in the future, whenever you meant to write something_else, you can now write whatever instead. It is useful in some cases, but I've found it to be more annoying than helpful in most. Especially when trying to make sense of existing scripts - you wouldn't believe how much that is used. I'm a mathematician - lazy by nature, so I tend to only learn the core functions and use them instead of all macros (or redefinitions of existing things, because that's what they really are: the same things written in other words. Sometimes special cases of a more general function). Look at this for example:
A script in the game has this piece of code:
That looks pretty neat and all, you could almost guess what it does. The "problem" is that this function is not defined in the COMMANDS.DOC - it's not a core function. If it's not a "core function" (in the lack of a better word), then it must be a macro. Or, if you prefer to think of it that way, it's a new function that does the same thing as some combination of core functions. To find the definition of floater_good_rand(x,y), we need to go to command.h. We find this:
- define floater_good_rand(x,y) floater_good(random(x,y))
This didn't do much. floater_good(x) is not a core function. random(x,y) is though - it picks a random number between x and y, inclusive. We look some more in command.h...
- define floater_good(x) floater_type(x, FLOAT_COLOR_GOOD)
Neither floater_type nor FLOAT_COLOR_GOOD is "core". We know now that instead of writing "floater_good_rand(100,101)", we can write "floater_type(random(100,101), FLOAT_COLOR_GOOD)" just the same.
- define floater_type(x, type) floater_type_msg(mstr(x), type)
Guess what? Yep. floater_type_msg() is not not a core function either. Actually, neither is mstr(). We go on:
- define floater_type_msg(x, type) obj_floater_type(self_obj, x, type)
- define mstr(x) message_str(NAME,x)
Of coure obj_floater_type() is not a core function either (message_str is though), so it has to be defined elsewhere. So far, we've got that writing "floater_good_rand(100,101)" is exactly the same as writing "obj_floater_type(self_obj, message_str(NAME,random(100,101)), FLOAT_COLOR_GOOD)". If you remember from above, NAME is also a so called macro, which you will have to define in the ssl file yourself. It's basically just short for SCRIPT_SCRIPTNAME, so it's not much of an improvement, really. Anyway, we continue, in the determination of finding an end to this:
- define obj_floater_type(z,x,type) float_msg(z, x, FLOAT_COLOR_NORMAL + ((type - FLOAT_COLOR_NORMAL) * has_trait(TRAIT_PERK, dude_obj, PERK_empathy)))
Finally! float_msg is a core function, it is described in commands.doc! Unfortunately, a series of other... what shall we call them? Problems? Occur here. I'm talking about the FLOAT_COLOR_NORMAL, TRAIT_PERK, PERK_empathy, etc. To fully understand this, we need to do some further research (yay). has_trait is a core function which returns the "value of a given object's trait of a given Trait Type". So you plug in the trait type, who you're checking for, and the trait you want to check for, and out comes 1 if the object doesn't have the trait you looked for, and 0 if it does.
I feel this is getting more overboard than I wanted to (ORLY?), so I think I'll actually just stop here. Just to bring it to an end though, I'll say that the FLOAT_COLOR_NORMAL is actually a macro (surprise surprise!) for FLOAT_MSG_YELLOW, which is defined as an integer in define.h. Exactly how the engine works this integer is beyond me, but it would mean the same thing to just write 8 instead, as that is the value for FLOAT_MSG_YELLOW. What the last step does, is simply (although it doesn't look very simple) deciding what colour the float text should have. If the PC does not have the empathy perk, the colour will be white. If he does have the empathy perk, the colour will be yellow (I think).
In a more general situation (seeing as obj_floater_type can be called on it's own, with other input data than the one we had in our example), the colour of the float will always be yellow if the PC have the empathy perk, and if he doesn't, the colour will be the input colour. So the following would make the dude (which is to say, the PC) float "Hi!" with red text if the PC does not have the empathy perk, and yellow text if he does:
Code: obj_floater_type(self_obj, "Hi!", FLOAT_MSG_RED);
... At least, I think that's what would happen... Wink. In conclusion, the following seemingly innocent piece of code:
is actually the same as writing this:
Code: float_msg(self_obj, message_str(SCRIPT_NAME, random(100, 101)), FLOAT_MSG_YELLOW + ((FLOAT_MSG_WHITE - FLOAT_MSG_YELLOW) * has_trait(TRAIT_PERK, dude_obj, PERK_empathy)));
Much better, huh? The last part, which decides the colour, can be substantially simplified in this special case. The macros are, in the end, designed to make the code easier. It's not always very easy to find out what easy means though - there's a lot of "hidden" things they check for. In this case, they have made a rather clever function that makes the float colour consistent with everywhere else in the game, depending on if the PC has the empathy perk, and it is used in a lot of different places, not just the function we looked at. The very point of all this is that you need to understand the general process here. You must be able to find out where these pieces of code are defined, how they are defined, and then make sense of it. There's a dozen functions defined in command.h that deal with floaters only, yet there is only one core function. The core function can be used in all these situations, but the price you pay for that is a sometimes much longer piece of code. In the beginning, a long piece of code is actually to prefer, since it's a lot easier to follow if everything is written down in one place, so you don't have to follow a long trail of definitions and redefinitions in several different files just to figure out what the hell the code is doing.