NathanKell waves @ CCC. <img src="style_emoticons/<#EMO_DIR#>/smile.gif" style="vertical-align:middle" emoid="
" border="0" alt="smile.gif" />
To expand a bit on CCC's posts:
First--I don't know how much about programming in general, or other languages, you know, kblack...I'm going to explain this in terms of C.
Regarding variables in general: POTC's script uses typed variables. So if you want to declare a variable, you must declared it of a certain type, i.e.
bool checkedgoodsails = 0;
Not including the type will result in a syntax error. (However, once declared, you may, and indeed must, use only the variable's name.)
But I'm sure you already know this; this was just in light of the abbreviated form you used in your posts. <img src="style_emoticons/<#EMO_DIR#>/smile.gif" style="vertical-align:middle" emoid="
" border="0" alt="smile.gif" />
Regarding scope (global/local etc.):
When you launch Engine.exe, the script engine loads the file program\seadogs.c, and that's it. Any other files must be #included (or added via loadsegment(), see below).
Any variables defined in any of those files--out 'in the open' that is, rather than inside functions--will be accessible anywhere in the code.
For example, most of the 'core' objects--Ships[], Characters[], etc., are declared in globals.c. However, they have handler functions that we normally use rather than referencing the objects directly.
So if you declare a variable outside of a function, and in an #included file, it is indeed a global and will work anywhere.
If you declare a global inside a file that is not included but loaded via a loadsegment() call, it will only exist while that segment is loaded (and be no longer accessible once you unloadsegment().) {fn1}
Objects are a special type of variable, one to which you can add attributes. If the object is a global, then the attribute is a global; if the object is a local variable, then the object and any attributes it contains will only be accessible within the function that creates it (and the object and all its attributes will be destroyed when the function exits).
Attributes are definitely not variables in themselves, and thus have no existence or meaning outside the objects to which they are attached.
Attributes are hierarchical, i.e. you can make trees of them. However--and this is a big caveat--they are handled internally as a list only--so the time to check, set, or otherwise deal with an attribute goes up based on the total number of attributes the object contains, not the number in the current level of the tree (let alone the expected "no penalty" since you tell the program exactly which attribute to access). And moreover, you pay the performance penalty for the total number of attributes the object contains -any- time you access -anything- in the object.
Example (and kudos to Inez, who figured this out and fixed it): the worldmap grid used in Fleets. Originally done as an attribute tree, later redone as an array.
The advantage of attributes for array-like tasks is that you can resize or otherwise manipulate the array at any time, and have as many dimensions as you want.
{fn1} I believe this is so, but I am not 100% sure--the variable may persist. Certainly extern declarations persist outside the segment in which they are declared. We should check this.
--------
And lastly, two slight clarifications to CCC's explanations:
(And a note)
Zeroeth: the note, regarding my old post on globals vs. attributes. At one time I was under the impression that adding (or removing) globals broke the ability to load and resave games (when the loaded game didn't have the variable). Scheffnow taught us otherwise. :]
To this day I have not much of a clue -what- makes 'upgrading' saves unstable--clearly adding too many variables can sometimes cause corruption or a crash on save; but there's no hard and fast rule.
So yeah, the simple answer is using attributes attached to a global object rather than adding global variables is more save-friendly--but per above using attributes has a performance penalty.
Plus this only makes a difference to code changes that occur after a game was saved; starting a new game avoids the 'trouble adding/removing globals' issue completely.
So I would say, if what you want to do can be done better with a global variable than 'piggybacking' a new attribute onto something (the object ShipLookupTable is the usual one to use. :] But it got kinda full. ), don't hesitate to use one. A bool here or there almost certainly won't make a difference.
First: preprocessor defines vs. global variables.
This:
#define SOMETHING x
is called a preprocessor define, and it works (in NORMAL environments) very simply: whenever the compiler sees SOMETHING in the code, it substitutes x in its place.
So if we define FIRSTNAME as "Nathaniel", then every time there FIRSTNAME appears in the code it gets changed to "Nathaniel".
POTC handles things slightly differently. First of all there's no compiler, of course. But the big change is you can only use a string or a number as the value. So something like
#define FIRSTNAME "Nathaniel"
#define LASTNAME "Hawk"
#define NAME FIRSTNAME + " " + LASTNAME
will crash POTC.
Let alone something like
#define MYCALL_1 myfunction(50)
Back to defines in general: they are invariant. Because they are really text subsitutes, you can't change them or otherwise treat them as variables. The upside, however, is that because of this they are not -stored- as variables either, and so their value is always what is assigned in the code (variables have their values stored on save, so when you load that game the variable will be assigned the value in the save, not the default value you assign in the code).
As long as you put them in an #included file, they will be available globally (but global here refers to their /scope/, not what they are; usually when we say global it's an abbreviation and what we really mean is 'global /variable/'); and there's no such thing as a local #define (at least in POTC--so always put them outside of functions!).
Second: references, objects, etc.
References are a special type of variable, a variable that points to another variable. They are often used to point to an object, but they can point to anything. Also, there are arefs (attribute references) can only point to objects, but they can point to any attribute in an attribute tree on that object.
Like C, you make a reference like so:
object myobj;
int myint = 5;
ref to_myobj, to_myint;
to_myobj = &myobj; //The & operators means 'the memory location of.'
to_myint = &myint;
Unlike C, you can also create references like this:
makeref(to_myobj, myobj); // note no & needed!
Also unlike C, there are attributes; and you can reference them:
myobj.attr1 = "test";
aref to_myattr;
makearef(to_myattr, myobj.attr1); // again, no &.
Footnote: We should check whether
to_myattr = &myobj.attr1;
works. I don't believe so, but don't quote me. My reasoning is that usually when you use the form someobj.someattr POTC interprets that as (the string that resides in that attribute) rather than (the attribute itself).
Note: Scheffnow's guaging tells us that using makeref is faster than directly assigning a reference (but not by -that- much).
The other key difference in POTC is that when you pass a variable to a function, and you want the function to use (i.e. reference) the original variable, not make a copy, you must do two things.
First, in the function declaration, you must use the variable type ref.
void increment_this(ref x) { x++; }
Second, when you pass a variable to that function, you must use the & operator.
//in some func:
int a = 1;
increment_this(&a);
This is directly contradictory to C, where you use the & in the function declaration and the variable's normal type.
Arefs are -always- references and thus (for some reason) don't need the &.
Ordinarily, when a function has a ref as an argument (and the variable is passed to it with &), and you want to pass that ref on to another function, you don't need to use &. However--and this is the really weird part, and in fact the opposite of how it should be--sometimes you need to even pass the ref with &.
Now, refs and arefs are mostly there to make the programmer's life easier (and allow generic functions)--you can often get by without them, by directly accessing the original variables. For example, you can use
Characters[GetMainCharacterIndex()].goodsails = 1;
rather than getting a ref to the player character and using the ref. In fact that is all that (and precisely what) GetMainCharacter() does: it returns &Characters[GetMainCharacterIndex()].