By DarkDragon:
The ZScript compiler is now available as an alternate way of scripting freeform combos and items. To access this new compile, go to Tools->Compile ZScript in ZQ.
Each quest now has an associated ZScript buffer. This buffer is saved with the quest. Enter a script by hand or load the contents of a file, then hit Compile to launch the ZScript compiler.
ZScript is a language heavily based off of C, with some modifications to accomodate ZC features. Consider, for instance, our first ZScript example:
Code:
int distancesq(int x1, int x2, int y1, int y2)
{
return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}
The above should compile happily. Notice that the above looks just like a normal C global function declaration; and in fact is just that in ZScript as well. I've declared a function of return type int, which takes in four int parameters.
The current built-in simple types for ZScript and int, float, bool, and void. Both int and float are provided for the sake of convenience, but both simply mean fixed-point number with 4 digits of decimal point precision.
The function above returns the square distance between (x1,y1) and (x2,y2). ZScript supports most of C's math and logic operators, and enforces the correct precedence.
You'll notice the above snippet doesn't actually DO anything when compiled; that's because we've just declared a global function, and not an actual script. Let's look at the next example:
Code:
int distancesq(int x1, int x2, int y1, int y2)
{
return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}
ffc script followlink {
int x;
int y;
void run() {
while(true)
{
x = this->X;
y = this->Y;
int dist = distancesq(Link->X, this->X, Link->Y, this->Y);
this->Vx = (Link->X - x)*Abs((Link->X-x))/dist;
this->Vy = (Link->Y - y)*Abs((Link->Y-y))/dist;
Waitframe();
}
}
}
I've now added a script to the source file. Notice the syntax: "ffc script <name> { }". The ffc specifies that this script can be assigned to an FFC Script slot in ZQ; "item" is also a supported script type. Global scripts will be added in later betas.
You'll notice I've declared two variables in script scope: x and y. Any variables declared in the scope of a script are global variables, and accessible to all scripts; you'll see this feature used in the next example.
In addition to x and y, there is a function declared in script scope: void run(). All scripts must implement this function, as this is the function that is run when the script is started. A script may have additional functions besides run(), which may be called by you explicitly, but run() is the only one called automatically.
Inside run() you can see a while loop; while and for loops are both implemented.
You can see I use several implicit "pointers" to get at various pieces of data using dereference (->) notation. "this" is a pointer whose type is dependent on the type of the scripts; since currently all scripts are ffc or item scripts, "this" refers to an ffc type, which contains the ffc's position, velocity, data, etc, or an item type. Link, Screen, and Game are global pointers that encapsulate the current state of link, the screen, and the game, respectively. A full list of the available pointer types will be listed at the bottom of this tutorial.
I said earlier that int, float, bool and void were ZScript's simple types. ZScript also has complex types, ffc, item, and itemclass, which are like pointers. They are provided implicitly through "this" pointers as described above, but can also be declared explicitly, and are returned from some library functions. Like the global pointers they can be dereferenced. For instance, in the following snippet, I move the screen's second item to x=10:
Code:
item i = Screen->LoadItem(1);
i->X = 10;
In any case, back to the FFC example. The first thing I do in the while loop is set the values of x and y. Other FFCs can't read the X position of this FFC directly, since no ASM instructions can do that currently; what I can do, though, is store the current X position in a globally accessible variable, x, each frame, and this is what I do in the above example.
Next I do some more complicated math to set the current FFC's velocity to point towards link with magnitude 1; notice that I take advantage of my global function squaredist() here. Then I call a global function, Waitframe(). Global functions are like user-defined global functions, but are provided for you by the ZScript compiler. A list of all the global functions follows this tutorial.
At this point you can run the script in a sample quest: compile the above, assign "followlink" to some FFC Script slot, and assign that slot to some FF combo, save, run ZC, and that FF should now follow link.
Unlike in ASM, you can define multiple ZScripts in one source file, and in fact it is encouraged that you do so. Here is the final sample script:
Code:
int distancesq(int x1, int x2, int y1, int y2)
{
return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}
ffc script followlink {
int x;
int y;
void run() {
while(true)
{
x = this->X;
y = this->Y;
int dist = distancesq(Link->X, this->X, Link->Y, this->Y);
this->Vx = (Link->X - x)*Abs((Link->X-x))/dist;
this->Vy = (Link->Y - y)*Abs((Link->Y-y))/dist;
Waitframe();
}
}
}
ffc script satellite {
void run() {
for(int i=0; true; i++)
{
this->X = followlink.x + 10*Sin(i);
this->Y = followlink.y + 10*Cos(i);
Waitframe();
}
}
}
I've added a new script, "satellite", which orbits the FFC defined above by using some trig functions. Notice that I can use the dot operator to access another script's global variables.
There's one more useful feature not used in the above examples: suppose a community member writes a very useful library of global functions or scripts, and you want to include them in your quest. No problem! Using the "import" keyword lets you add the contents of another file as if they were copied into the current file. So for instance if I moved the declaration of distancesq() into library.z, I could just do
import "library.z"
ff script followlink {
...
and it would compile the same.
One last postscript: as you can see when you're assigning script to slots, we've added three slots for "global" scripts. One of these scripts is run the first time you start a new quest. ZScript does a lot of behind-the-scenes work (you can declare a lot more than 9 variables in a script!) but as a consequence must be allowed to place a special auto-generated script, ~Init, in global slot 0. This is all done automatically - but I don't want you to be surprised when ZQ doesn't let you overwrite ~Init in the slot assignment dialog.
Here is a brief reference of what's currently implemented in the ZScript language:
Pointer types: (use -> to access)
ffc: (accessible using the "this" pointer, or explicit pointer)
float Data;
float CSet;
float Delay;
float X;
float Y;
float Vx;
float Vy;
float Ax;
float Ay;
bool wasTriggered();
link: (accessible through the global "Link" pointer)
float X;
float Y;
float Dir;
float HP;
float MaxHP;
float MaxMP;
float Action;
void Warp(int, int);
void PitWarp(int,int);
bool InputsEnabled;
bool ForcedUp;
bool ForcedDown;
bool ForcedLeft;
bool ForcedRight;
bool ForcedA;
bool ForcedB;
bool ForcedL;
bool ForcedR;
bool APressed();
bool BPressed();
bool LPressed();
bool RPressed();
bool UpPressed();
bool DownPressed();
bool LeftPressed();
bool RightPressed();
bool StartPressed();
screen: (accessible through the global "Screen" pointer)
float D[8];
float ComboC[176];
float ComboD[176];
float ComboF[176];
float ComboI[176];
float ComboT[176];
float ComboS[176];
int NumItems();
item LoadItem(int);
item CreateItem(int);
item: (accessible through the "this" pointer, or explicit pointer)
float X;
float Y;
float DrawType;
itemclass Class;
float Tile;
float CSet;
float FlashCSet;
int NumFrames;
int Frame;
float ASpeed;
float Delay;
bool Flash;
float Flip;
float Extend;
itemclass: (accessible through explicit pointer)
float Family;
float FamilyType;
float Amount;
float Max;
float MaxIncrement;
bool Keep;
float Counter;
game: (accessible through the "Game" pointer)
int GetCurScreen();
int GetCurMap();
int GetCurDMap();
int NumDeaths;
int Cheat;
float Time;
bool HasPlayed;
bool TimeValid;
float GuyCount[];
int ContinueScreen;
int ContinueDMap;
float Counter[];
float MCounter[];
float DCounter[];
float Generic[];
float Items[];
float LItems[];
float LKeys[];
float CurMapFlag[];
float GetMapFlag(int, int);
void SetMapFlag(int, int,float);
float GetScreenD(int, int);
void SetScreenD(int,int,float);
itemclass LoadItemClass(int);
Global Functions:
float Rand(float);
void Quit();
void Waitframe();
void Trace(float);
float Sin(float);
float Max(float,float);
float Min(float,float);
float Pow(float, float);
float InvPow(float,float);
int Factorial(int);
float Abs(int);
float Sqrt(float);
Lastly, all of the keywords implemented in the language:
script
float
for
bool
void
if
else
return
import
true
false
while
ffc
itemclass
item
Operators:
=
.
->
<<
>>
&
|
^
&&
||
!
~
++
--
<=
<
>=
>
!=
==
+
-
*
/
%