Jump to content

Photo

Basic scripting tutorial (WIP)


  • Please log in to reply
66 replies to this topic

#16 Joe123

Joe123

    Retired

  • Members

Posted 20 October 2008 - 09:29 AM

Combos are counted (by the system) from left to right, top to bottom, and assigned a single number rather than coordinates.

So the top left combo is combo 0, then the next one across is combo 1. The combo at the end of that row is combo 15, and then the one below combo 0 is combo 16.
175 is an odd number because 0 is included when counting. There are 16*11 combos on the screen (number across * number down), which comes to 176. If you then include 0 when counting, you take 1 away from that value to get 175 as the last combo in the bottom right hand corner.

Obviously that's a bit awkward for us humans; coordinates are much easier to deal with.
The ComboAt integer allows you to input coordinates, and turns them into a number that ZScript can use to find where a combo is, in a function such as Screen->ComboD[int combo];, which you can use to change the Combo ID of the number within the square brackets:
CODE
Screen->ComboD[3] = 150;

Would change the 4th combo along the top of the screen to the 150th combo in your combo table.
So what you'd do to enter coordinates instead of one value is to put Screen->ComboD[ComboAt(xcoordinate,ycoordinate)] which allows you to manipulate the combo based on coordinates, rather than the system's method.
CODE
Screen->ComboD[ComboAt(48,0)] = 150;

Achieves the same effect.

#17 Saffith

Saffith

    IPv7 user

  • Members

Posted 20 October 2008 - 09:44 AM

QUOTE(Ebola Zaire @ Oct 20 2008, 10:07 AM) View Post

Awesome. icon_thumbsup.gif Constants are cool.

What does this mean? Shouldn't the coordinate for the top-right be (0,0) and bottom-left be something like (256,176)? Single numbers don't make sense, especially not odd ones.

Yeah, like Joe123 said. They're accessed via arrays, so it'll make more sense after the next section. I'll add a note of that.

#18 Joe123

Joe123

    Retired

  • Members

Posted 20 October 2008 - 10:21 AM

Howcomes you're covering arrays before normal variable declaration?

That doesn't seem logical =S

#19 Saffith

Saffith

    IPv7 user

  • Members

Posted 20 October 2008 - 10:28 AM

Only the built-in arrays. User-defined will be quite a bit later.

#20 Joe123

Joe123

    Retired

  • Members

Posted 20 October 2008 - 10:36 AM

Ah.
Well that makes quite a lot more sense.

#21 Mitchfork

Mitchfork

    no fun. not ever.

  • Members
  • Real Name:Mitch
  • Location:Alabama

Posted 20 October 2008 - 01:45 PM

QUOTE(Joe123 @ Oct 20 2008, 09:29 AM) View Post

Combos are counted (by the system) from left to right, top to bottom, and assigned a single number rather than coordinates.

So the top left combo is combo 0, then the next one across is combo 1. The combo at the end of that row is combo 15, and then the one below combo 0 is combo 16.
175 is an odd number because 0 is included when counting. There are 16*11 combos on the screen (number across * number down), which comes to 176. If you then include 0 when counting, you take 1 away from that value to get 175 as the last combo in the bottom right hand corner.

Obviously that's a bit awkward for us humans; coordinates are much easier to deal with.
The ComboAt integer allows you to input coordinates, and turns them into a number that ZScript can use to find where a combo is, in a function such as Screen->ComboD[int combo];, which you can use to change the Combo ID of the number within the square brackets:
CODE
Screen->ComboD[3] = 150;

Would change the 4th combo along the top of the screen to the 150th combo in your combo table.
So what you'd do to enter coordinates instead of one value is to put Screen->ComboD[ComboAt(xcoordinate,ycoordinate)] which allows you to manipulate the combo based on coordinates, rather than the system's method.
CODE
Screen->ComboD[ComboAt(48,0)] = 150;

Achieves the same effect.
^So... wait, ComboAt() finds based on coordinates, and ComboD[] finds based on screen position number? Isn't that the opposite of what the tutorial says now? icon_blink.gif


#22 Saffith

Saffith

    IPv7 user

  • Members

Posted 20 October 2008 - 05:42 PM

The description could be clearer. I'll change it, and I think I'll move that bit back to the section on functions.
ComboD[] is basically a list, and ComboAt() converts coordinates into the corresponding list position.

#23 Mitchfork

Mitchfork

    no fun. not ever.

  • Members
  • Real Name:Mitch
  • Location:Alabama

Posted 20 October 2008 - 06:02 PM

Okay, just to make completely sure...
ComboAt() - You enter in coordinates and it returns a number 0-175 of the combo at that location.
ComboD[] - Changes the combo ID of a combo on the screen, but requires the 0-175 list, not coordinates.

Also, the square brackets are arrays, correct?

Think of me as idiot-proofing the tutorial. icon_razz.gif

#24 Saffith

Saffith

    IPv7 user

  • Members

Posted 20 October 2008 - 06:05 PM

Yes, that's right.

#25 Saffith

Saffith

    IPv7 user

  • Members

Posted 21 October 2008 - 06:46 PM

Functions


A function is a unit of code that performs a specific task. That's a helpful definition, isn't it? It's hard to come up with a better one, because using, or calling, a function can potentially do anything. Some functions do things in the game, like play a sound or warp Link to another screen. Others give you access to more advanced mathematical operations, such as exponential or trigonometric functions. A few will give you information, like how many enemies are onscreen. A function may also just put a complicated procedure into a form that's more convenient to use.

In zscript.txt, functions are the ones that have parentheses after their names. The parentheses may or may not be empty.
Here's an example of a simple mathematical function:
 
/**
* Returns the result of addend1 + addend2
*/
float Add(float addend1, float addend2);
This isn't a real function, but it would be easy to create.

The first part after the description, float, is the function's return type, the type of data you'll get from using it. A function can return a number, a bool, or a pointer. Some functions don't return anything at all; these have the special return type void.

After the return type is the name. When talking about functions, it's common, but not required, to include an empty pair of parentheses. You could refer to this function as either Add or Add().

Inside the parentheses are the arguments. These are inputs, data that will be used by the function to decide what to return or what to do. The arguments are listed in the usual way: type, then name. If a function requires multiple arguments, they're separated by commas. This function takes two arguments, both numbers. The names of the arguments are completely irrelevant when you're using the function; the names are only there to give you an idea of what they're used for.

Here's how you would use this function.
 
// All the function does is add two numbers, so it's equivalent to ordinary addition.
// You assign the return value to a variable the same way as anything else.
this->X = Add(5, 11); // Equivalent to this->X = 5 + 11;
Link->HP = Add(48, -16); // Equivalent to Link->HP = 48 + -16;
Add(10, 25) = this->Y; // ERROR: You can't assign a value to a function
Link->HP = Add(4, 12, 8); // ERROR: The function takes exactly two arguments; you can't use more or fewer, even if it seems logical

// You can use functions in arithmetic, too - just keep the return type in mind.
Link->Z = 5 * Add(1, 3); // Equivalent to Link->Z = 5 * (1 + 3);
Link->HP += Add(8, 8); // Equivalent to Link->HP = Link->HP + (8 + 8);

// You can pass in variables as arguments.
this->Y = Add(this->X, 32); // Equivalent to this->Y = this->X + 32;
Link->MP = Add(Link->MP, Link->MP); // Equivalent to Link->MP = Link->MP + Link->MP;

// You can use arithmetic operations. They'll be evaluated first, then the results will be used as arguments.
Link->HP = Add(Link->HP, 2 * 16); // Equivalent to Link->HP = Link->HP + (2 * 16); - The parentheses are implied
this->X = Add(5 * (2 + 4), 7 - 5); // Equivalent to this->X = (5 * (2 + 4)) + (7 - 5);

// You can even pass the results of other function calls.
Link->X = Add(Add(4, 8), 17); // Equivalent to Link->X = (4 + 8) + 17;
Link->MP = Add(Add(Add(12, 4), 32), Add(Link->HP / 16, 24)); // That's a bit much, but it's legal
In zscript.txt, you'll see global functions listed at the top, followed by those associated with different pointer types. Global functions are called using just their names, like the Add() function above. For the others, you use the pointer and arrow, just like with variables. A couple of examples of real functions:
 
Link->HP = Max(Link->HP, Link->MaxHP * 0.5); // Set Link's HP to 50% full or his current HP, whichever is greater
this->Data = Game->GetComboData(1, 0, 0); // Set the FFC's combo to that at the top-left corner of map #1
Void functions work just by being called. Since they have no return value, it'll cause an error if you try to assign one to a variable. You still need a semicolon at the end of the line.
 
Game->PlaySound(16); // Play sound effect #16
Link->Warp(12, 0x44); // Warp Link to screen 44 on DMap 12
ClearTile(10442); // Deletes tile 10442
When you call a function, variables that you pass in as arguments will not change.
 
Link->MaxHP = 64;
Link->HP = Sqrt(Link->MaxHP); // Link's HP becomes 8; his max HP is still 64
That's about all you need to know about using functions for now, but there are some special global functions that are worth singling out.

So far, every script has executed instantly, all in a single frame. To make scripts run over a period of time, you need the Waitframe() function. It'll pause the script for one frame, then pick up where it left off 1/60 of a second later. This doesn't work in item scripts or non-active glocal scripts, since they only run for one frame; the script will simply stop executing when it reaches the Waitframe().

Waitdraw() is only used in the active global script. Each frame, the global script is run before most of the frame's regular updates are done. Enemies haven't moved yet, Link hasn't started swinging his sword yet, and so forth. Waitdraw() suspends the execution of the script until after all of this has been done and just before the screen is redrawn.

The Trace() and TraceToBase() functions write a number to the allegro.log file. Similarly, TraceB() takes a Boolean argument and writes either "true" or "false." TraceNL() writes a blank line. These functions are used mainly for debugging.
 
Trace(Link->HP); // After the script runs, a line in allegro.log will tell you what Link's HP was at the time
There's also TraceS(), which writes a string, but that's a much more advanced topic.

The Quit() function makes the script stop running. There's no reason for you to use this yet.
 
Link->X += 5;
Quit();
Link->X -= 5; // This line will never run
One last note: one of the reasons for importing std.zh is that it brings in the contents of std_functions.zh, which defines many useful functions. These functions are documented in std.txt. All functions in std.zh (and any other .zh file) are global. A few of them worth highlighting:
 
void Waitframes(int n)
* Calls Waitframe() n times.

void NoAction()
* Sets all action inputs (i.e. all keys but start and map) to false.

void WaitNoAction()
void WaitNoAction(int frames)
* Combines NoAction() and Waitframe().

bool IsSideview()
* Returns true if the current screen is a side-view screen.

int Rand(int min, int max)
* Returns a random integer between min and max.

float Randf(float n)
float Randf(float n1, float n2)
* Returns a random floating-point (i.e. non-integer) number up to n or between n1 and n2.

float Clamp(float x, float low, float high)
* Returns x if it's between low and high, or low or high if not. Used to limit
* a number to a certain range.

float VectorX(int len, float angle)
float VectorY(int len, float angle)
* Used for movement at an arbitrary angle. Returns the values for X and Y velocity
* needed to move len pixels at angle degrees.

int ComboAt(int x, int y)
* Converts screen coordinates to a combo position. This will be very useful soon.
That's it for functions for now. For the time being, just look at those functions that return int, float, bool, or void. Some of the more complex ones may look intimidating, but if you understand the concepts, you'll be able to figure them out.

Now, some sample scripts...
 
// This script will play sound effect #27 (the secret sound, by default).

ffc script SecretSound
{
    void run()
    {
        Game->PlaySound(27);
    }
}
// This script will draw a quickly shrinking circle around Link on layer 5.
// You'll learn better ways to do this sort of thing later on.

ffc script CircleLink
{
    void run()
    {
        // Don't be overwhelmed by all the arguments. Just figure out what they do one by one.
        Screen->Circle(5, Link->X+8, Link->Y+8, 16, 3, 1, 0, 0, 0, false, 128);
        Waitframe();
        Screen->Circle(5, Link->X+8, Link->Y+8, 12, 3, 1, 0, 0, 0, false, 128);
        Waitframe();
        Screen->Circle(5, Link->X+8, Link->Y+8, 8, 3, 1, 0, 0, 0, false, 128);
        Waitframe();
        Screen->Circle(5, Link->X+8, Link->Y+8, 4, 3, 1, 0, 0, 0, false, 128);
    }
}
// This script uses trigonometric functions to make the FFC aim at Link, moving at one pixel per frame.

import "std.zh"

ffc script MoveTowardLink
{
    void run()
    {
        // Using functions from std.zh:
        this->Vx = VectorX(1, Angle(this->X, this->Y, Link->X, Link->Y);
        this->Vy = VectorY(1, Angle(this->X, this->Y, Link->X, Link->Y);
        
        // Here's another way to do it...
        // For angular movement at a given speed, X velocity is speed * Cos(angle),
        // and Y velocity is speed * Sin(angle).
        //this->Vx = 1 * RadianCos(ArcTan(Link->X - this->X, Link->Y - this->Y));
        //this->Vy = 1 * RadianSin(ArcTan(Link->X - this->X, Link->Y - this->Y));
    }
}
And some practice suggestions.
  • Make an FFC script that waits for ten seconds and then plays a sound.
    Spoiler
  • Write a global script that sets the cheat level higher for every ten times Link has died, but don't let it go above 4.
    Hint

    Spoiler
  • Write a script that waits for five seconds and then makes the screen flash.
    Hint

    Spoiler

Constants


Now you know how to play a sound...
 
Game->PlaySound(3);
Game->PlaySound() makes enough sense, but 3? 3 isn't a sound, it's a number. That's one of the reasons constants exist:
 
Game->PlaySound(SFX_BOMB);
Much better. This is the second reason for importing std.zh. It includes std_constants.zh, which defines the SFX_ constants and many others.

A constant is simply a number with a name. When the compiler sees SFX_BOMB, it replaces it with 3. It does exactly the same thing as before, but it makes it easier to understand by using a meaningful name instead of an arbitrary number.

You can also define your own constants.
 
const float CONSTANT_NAME = 12.3456;

ffc script ScriptName
{
    // Constants must be defined outside of the script itself.
    const int NO_GOOD = 0; // ERROR
}
To indicate that you're naming a constant, start a line with const. That's followed by the constant's data type. However, constants have to be numbers, so the only valid types are int and float. After that is the name, and then you specify the value as if you were setting a variable.

Legal names for constants are the same as legal names for scripts: any combination of letters, numbers, and underscores that does not start with a number and is not a ZScript keyword. Constants are normally named using all caps, sometimes with underscores between words. They commonly have a prefix indicating their purpose; std.zh uses the SFX_ prefix for sounds, for instance. Constant names must be unique; if you reuse a name from std.zh or another script in the same quest, it'll cause an error. Therefore, it's a good idea to give the names a prefix based on the name of the script that uses them.

The constant's value, as mentioned before, must be a number. You can't assign a constant the value of a variable or a function's return value. That's because the values are set when the script is compiled, and variables and functions can't be evaluated until the script is run. Furthermore, you can't set a constant's value to another constant, and the value can't be the result or an arithmetic operation.
 
// These are invalid.
const int SQRT_20 = Sqrt(20); // ERROR: Can't use the result of a function call
const int LINKS_MAX_HP = Link->MaxHP; // ERROR: Can't use variables
const int SOUND_TO_PLAY = SFX_BOMB; // ERROR: Can't use another constant
const int TWENTY = 2 * 10; // ERROR: Can't use arithmetic
Once you set a constant's value, it can't be changed by the script. That's why they're called constants.
 
import "std.zh"

ffc script Example
{
    void run()
    {
        SFX_OUCH = 10; // ERROR: The compiler will interpret this as 19 = 10;
    }
}
There's another reason to use constants besides making scripts more readable. They also make it easier to change how your script works. It's simpler to change the value of a constant than dig through the code to find some arbitrary number, especially if your script is large and complex or if the same number is used in several different places. This can also allow quest makers to customize your scripts without having to figure out how they work.

That's it for this section. Nothing too hard, just another way to write numbers. A couple of sample scripts...
 
// This script plays the secret sound effect.
import "std.zh"

ffc script SecretSound
{
    void run()
    {
        Game->PlaySound(SFX_SECRET);
    }
}
// This script will play a sound and make the screen shake.
// It will also disable the sword and items for as long as the screen shakes.

// Change the constants to control how long the quake lasts and what sound is played.
const int EQ_QUAKE_TIME = 60;
const int EQ_QUAKE_SOUND = 13;

ffc script Earthquake
{
    void run()
    {
        Game->PlaySound(EQ_QUAKE_SOUND);
        Screen->Quake = EQ_QUAKE_TIME;
        Link->SwordJinx = EQ_QUAKE_TIME;
        Link->ItemJinx = EQ_QUAKE_TIME;
    }
}
// This warps Link to DMap 10, screen 1F.
const int WARP_DEST_DMAP = 10;
const int WARP_DEST_SCREEN = 0x1F;

ffc script WarpLink
{
    void run()
    {
        Link->PitWarp(WARP_DEST_DMAP, WARP_DEST_SCREEN);
    }
}
For practice, try modifying earlier sample scripts to use constants.

 

Arrays


An array is like a lot of variables all put together in a list. They're used to represent lots of data that are logically grouped together. Data in an array are all of the same type and handled in the same way, and, in most cases, they all serve basically the same purpose. Link's inventory, for instance, is an array of bools. There's one bool corresponding to each possible item, which is true if Link has that item and false if he doesn't. All of these bools are combined into the array Link->Item[].

Each of the entries in the array is called an element. The position of an element in the array is called its index. You would use index 5 to access the element Link->Item[5].

Working with the elements of an array is much like working with ordinary variables. The only difference is that you must specify an index; you can't change the entire array at once.
 
Link->Item[10] = true; // Give Link item #10
Link->Item[5] = false; // Take away item #5
Game->LKeys[3] += 2; // Give Link two additional keys for level 3
Game->LKeys = 5; // ERROR: You have to specify a single array element
The number of elements in an array is its size or length. For technical reasons, the index of the first element is 0, and the index of the last element is one less than the array's size. The size of Link->Item[] is 256, since the game allows 256 items; the first element is Link->Item[0] and the last is Link->Item[255].

When using arrays, be careful to use valid indices, because the compiler can't check them. If you try to set Link->Item[300], it will successfully compile, but there's nothing sensible it can do.

Anything that gives you a number can be used for an array index.
 
Link->Item[5 + 5] = true; // Arithmetic is fine
Link->Item[Link->X] = false; // Variables work, though this one doesn't make much sense
Game->LKeys[Sqrt(64)] = 4; // Functions are fine, too
Link->Item[Game->LKeys[3]] = true; // Even array elements work
In some cases, it's obvious what number to use for the index. In Game->LKeys[], for instance, the index corresponds to the level number. Other times, however, the index is arbitrary. Each element of this->Flags[] represents one of the FFC's flags, but you couldn't guess which index goes to which flag. Fortunately, std.zh provides lots of constants for exactly this reason. Just look at zscript.txt to see which set of constants to use for a given array.
 
Link->Item[I_SWORD2] = true; // Give Link the White Sword
this->Flags[FFCF_TRANS] = true; // Make the FFC transparent
Screen->Door[DIR_UP] = D_UNLOCKED; // Unlock the door at the top of the screen
Game->Generic[GEN_HEARTPIECES] += 1; // Give Link a Heart Container Piece
That's really all there is to using arrays, but there's a set of them that warrants some elaboration. All of the combos on the screen can be accessed via arrays of Screen. Screen->ComboD[] represents the ID of each combo, Screen->ComboC[] gives you the CSets, and so forth. The combos are numbered from 0 to 175, like so:
 
[000][001][002][003][004][005][006][007][008][009][010][011][012][013][014][015]
[016][017][018][019][020][021][022][023][024][025][026][027][028][029][030][031]
[032][033][034][035][036][037][038][039][040][041][042][043][044][045][046][047]
[048][049][050][051][052][053][054][055][056][057][058][059][060][061][062][063]
[064][065][066][067][068][069][070][071][072][073][074][075][076][077][078][079]
[080][081][082][083][084][085][086][087][088][089][090][091][092][093][094][095]
[096][097][098][099][100][101][102][103][104][105][106][107][108][109][110][111]
[112][113][114][115][116][117][118][119][120][121][122][123][124][125][126][127]
[128][129][130][131][132][133][134][135][136][137][138][139][140][141][142][143]
[144][145][146][147][148][149][150][151][152][153][154][155][156][157][158][159]
[160][161][162][163][164][165][166][167][168][169][170][171][172][173][174][175]
These numbers aren't always convenient to work with, so std.zh defines the ComboAt() function to convert from x,y coordinates.
 
Screen->ComboC[ComboAt(Link->X, Link->Y)] += 1; // Change the CSet of the combo Link is standing on
this->Data = Screen->ComboD[ComboAt(this->X, this->Y)]; // Turn the FFC into the combo at its position
Be careful when working with ComboT[] (combo type), ComboI[] (inherent flag), or ComboS[] (solidity). Unlike the other combo arrays, the data these arrays represent are part of the combo definitions. Changing these will affect every instance of the same combo in the entire quest.

That's it for arrays. As usual, a few sample scripts to end with.
 
// This script will take away Link's swords.

import "std.zh"

ffc script Disarm
{
    void run()
    {
        Link->Item[I_SWORD1]=false;
        Link->Item[I_SWORD2]=false;
        Link->Item[I_SWORD3]=false;
        Link->Item[I_SWORD4]=false;
    }
}
// This script refills Link's bombs and increases the maximum by 4.

import "std.zh"

ffc script MoreBombs
{
    void run()
    {
        Game->MCounter[CR_BOMBS] += 4;
        Game->Counter[CR_BOMBS] = Game->MCounter[CR_BOMBS];
    }
}
// When this item is used, it will randomly change the CSet of the combo Link is standing on.

import "std.zh"

item script ColorChange
{
    void run()
    {
        // ZQuest only lets you select a CSet up to 11, but CSets 0-15 all work
        Screen->ComboC[ComboAt(Link->X, Link->Y)] = Rand(16);
    }
}
And one last pair of practice exercises:
  • Make an item that gives Link a key and reduces his max HP by one heart. Be sure not to take all his life, though.
    Hint

    Spoiler
  • Give Link an extra bomb for every heart he's missing from his max HP.
    Spoiler
That's the end of the basic tutorial. Nothing too difficult yet, but get some practice before moving on.

You're sure to have questions along the way; whenever you can, try finding the answers yourself rather than asking on the forums. It's good practice, and you'll probably get your answer sooner.


The intermediate tutorial is here.

Edited by Saffith, 08 December 2014 - 09:25 PM.


#26 ShadowTiger

ShadowTiger

    The Doctor Is In

  • Members

Posted 21 October 2008 - 08:45 PM

For the ComboAt() :
QUOTE
You need to be careful when changing some of these. Changing the values of elements of ComboT[] (combo type), ComboI[] (inherent flag), or ComboS[] (solidity) will change those properties for every one of that same combo in the entire quest.
IMHO, this definitely needs to be in huge, pointy bold red text.

This is because changing the properties of the combo at whatever position you're pointing to can't only change that one combo. In order to only change one combo, it'd have to make a new combo, and change that one accordingly. But it doesn't, so it just changes all of the combos of that type.

So it's definitely best to have a duplicate combo that's only used right there. It'll save you a boatload of trouble later. Either that, or script it so that it returns to its normal function before you leave the room or something.



And, may I ask: the last of those example scripts, to change the color under Link.
QUOTE
Screen->ComboC[ComboAt(Link->X, Link->Y)] = Rand(15);
Where did that 15 come from, please?


#27 Mitchfork

Mitchfork

    no fun. not ever.

  • Members
  • Real Name:Mitch
  • Location:Alabama

Posted 21 October 2008 - 10:35 PM

Great, this explains a wealth of information very well. Don't worry about the character limit- submit it to the tutorial DB in two posts, and we can merge them once all of the editing is done.

Agreed with ShadowTiger on the first point, because that behavior is rather unintuitive and I wouldn't guess that at all. At your second point, I believe that chooses a random number between 0 and 15 (the number of CSets you have).

#28 Saffith

Saffith

    IPv7 user

  • Members

Posted 21 October 2008 - 11:15 PM

Actually, I should've made it 16, since the argument is one greater than the maximum return value... icon_razz.gif


So, it's pretty much written. I should probably add some sort of conclusion, though I'm not doing too well at thinking of stuff to say in it. Before it's considered finished, I'd really like for several people to go through and, y'know, actually learn from it, and hear what they have to say.

Edited by Saffith, 24 October 2008 - 12:13 PM.


#29 Saffith

Saffith

    IPv7 user

  • Members

Posted 25 October 2008 - 12:57 AM

C'mon, anyone?
Maybe over the weekend?

#30 ShadowTiger

ShadowTiger

    The Doctor Is In

  • Members

Posted 25 October 2008 - 09:28 AM

Well, this was the topic that actually inspired me to sit down and write a script for once, so I'd say it's pretty much a success.

The only thing is, (Unrelated to the posts here.) to start to learn scripting, you ACTUALLY NEED TO SIT DOWN AND SCRIPT. It's like learning any other trade. You can sit down in Dentistry school for several decades and if you haven't touched a tooth by then, there's just something wrong.

Even writing the most basic of scripts will get you on your path to success, because it'll give you the experience necessary to know what works and what doesn't.


So yeah, I'm still thinking about that "What works and what doesn't" thread. icon_razz.gif That FAQ I mentioned a while ago. Should probably include stuff like "What deserves a semicolon and what doesn't," and "How do I tell how many brackets I need?"


0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users