Jump to content

Photo

A guide for how to combine Global Scripts in ZScript


  • Please log in to reply
No replies to this topic

#1 Joe123

Joe123

    Retired

  • Members

Posted 30 November 2008 - 09:06 AM

The Non-Scripter's Guide to Global Scripts
Introduction and Explanation of Purpose
From a scripter's point of view, the Global Script in Zelda Classic is a very useful tool, allowing for many different effects which will occur at any point during the game on any screen.
Because it's the only (practical) way of applying effects to the entire game, it's used quite a lot for various different purposes.

Now, that's fine if you know how to code. To someone who understands ZScript, it's easy to have your Global Script run lots of different types of effects. And the purpose of this small tutorial is to show someone who doesn't know how to code - but wants to use other people's scripts - how to have their Global Script also have more than one effect.

The main problem with this concept is that we only appear to have two Global Script slots in the compiler. So someone who doesn't understand how ZScript works is most likely to come along, see this dilema, come to the conclusion they can only have two Global Scripts, decide which ones they want, assign them both a slot and leave it at that.
Now, the idea that we are allowed only two Global Scripts - although the logical conclusion to come to - is in fact wrong (in practise).

First of all, the two slots for Global Scripts in the compiler work differently.
The first one is the slot which works as most people would expect; it allows for code which can continue running throughout the entire game.
The second slot, however, isn't the same. Any script added to the second slot will run only once - after Link dies (bare in mind however that if you're writing scripts, Link has already died by this point so you wouldn't be able to utilise this as a potion-type script).

So now we think to ourselves 'Oh no! Now we can only have one global script!'
And indeed, if you take the 'assign two global scripts you want most to the two slots' approach, only the one in the first slot will work.

But this does not mean that we can't have one global script which sits in the second slot, and runs many different effects.

So, in the way of an example:
We have three global scripts we want to use.
  • The first allows the player to Roll when 'L' is pressed.
  • The second is linked with an item script, and controls Bombchus
  • The third makes 'ladders' that the player always faces upwards whilst walking on.
We obviously can't just assign each one to a seperate slot, so what we have to do is make them into one script that has three different functions. It allows the play to roll, controls bombchus and creates ladder flags.
We then compile this script to the first slot, and we have acheived out goal of a global script with three effects.

Note- There are also two other global script slots. These slots are the first and last ones in the compiler, and are reserved by ZC for a script called '~init' and one called '~Continue'. Because of this, I will refer to the first slot we can actually use as 'Slot 2' and the other slot as 'Slot 3'.
If you're interested, the built-in '~init' and '~Continue' scripts deal with allocation of global array memory, and you can add your own functions into them if you name your global scripts as 'init' or 'Continue'.

How to Combine Global Scripts
In this guid, we'll discuss how to combine Slot 2 type global scripts.
These are scripts which run throughout the whole game.

They are usually written in one of two types:
This next part will have some code. Please don't be scared off by it, because the most complex code editing you'll have to do is copy-pasting chunks of code into different places

Type 1
The first type uses 'if' statements inside a while loop, and looks a bit like this:
Note- any lines of code that start with '//' are known as 'notes'. They are there to aid the user of the script, and have no effect at all on how it works
CODE
global script Roll{
    void run(){
        while(true){
            //Roll Functions
            if(LinkCanRoll){
                //and then here you have a large chunk of code which is what makes the rolling actually happen.
                //...
                //...
                //...
                //etc.
            }
            
        Waitframe();
        }
    }
}


With this type of global script, all of the functions for making the effect happen are included in the main part of the script.


So lets a little example excersize of how to compile two scripts of this type:
CODE
global script Roll{
    void run(){
        while(true){
            if(LinkCanRoll){
                //Rolling code
                //...
                //...
                //...
                //etc.
            }
            
        Waitframe();
        }
    }
}

global script Bombchu{
    void run(){
        while(true){
            if(Bombchus){
                //Bombchu controlling code
                //...
                //...
                //...
                //etc.
            }
            
        Waitframe();
        }
    }
}

When we look at these two scripts, we only need consider the code between 'while(true){' and 'Waitframe();'.
This chunk of code is what makes things happen. We don't need to understand it in any way.

What we do is...
Take the chunk of code from the first script, from between 'while(true){' and 'Waitframe();', copy it, and paste it into the second script. It should go after the end of the 'if(bombchucs){}' chunk of code, and before 'Waitframe();'.

So what we end up with is this:
CODE
global script Slot_2{
    void run(){
        while(true){
            if(Bombchus){
                //Bombchu controlling code
                //...
                //...
                //...
                //etc.
            }
            
            if(LinkCanRoll){
                //Rolling code
                //...
                //...
                //...
                //etc.
            }
            
        Waitframe();
        }
    }
}


Now, we have a global script that will run two different effects!
And all that we've had to do is copy one chunk of code over into the correct place.
You may also notice that I've changed the name of the script. That's just so that I know it's no longer a global script which has only one purpose, it has many.
If there are any lines of code written outside of the actual script, all you have to do is make sure all of them are included in your file afterwards. It doesn't matter where they go or what order the chunks are put in.
Personally, I prefer to keep them at the top though.

Type 2
The other type of global script which you might have in Slot_2 has a 'link' inside the 'while(true){' to 'Waitframe();' part of the code, to another part at the end.

It might look something like this:

CODE
global script Epona{
    void run(){
        while(true){
            if(Epona) RunEpona();
        Waitframe();
        }
    }
    
    void RunEpona(){
        //Epona's functions go here...
        //...
        //...
        //...
    }
}

All code can be written in either of these two forms, but the second is perhaps a little better-organised.

In this case, what we have to do is...
Take the part of the code inside the 'while(true){' to 'Waitframe();', like we did last time, and copy it over into our other global script.
Then, we have to take the chunk of code at the end, which will start with 'void' and then a name, and end with a '}', and copy this part of the code in right before the very last '}' of the script.

So, by way of an example:
CODE
global script Roll{
    void run(){
        while(true){
            if(LinkCanRoll) Roll();
            
        Waitframe();
        }
    }
    
    void Roll(){
        //Rolling code
        //...
        //...
        //...
        //etc.
    }
}

global script Bombchu{
    void run(){
        while(true){
            if(Bombchus) Bombchu();
            
        Waitframe();
        }
    }
    
    void Bombchu(){
        //Bombchu controlling code
        //...
        //...
        //...
        //etc.
    }
}


...combining these two together, we'd get some code a bit like this...

CODE
global script Slot_2{
    void run(){
        while(true){
            if(LinkCanRoll) Roll();
            if(Bombchus) Bombchu();
            
        Waitframe();
        }
    }
    
    void Roll(){
        //Rolling code
        //...
        //...
        //...
        //etc.
    }
    void Bombchu(){
        //Bombchu controlling code
        //...
        //...
        //...
        //etc.
    }
}


It's also good to note that you can combine the first type of global script with the second, using the method for the second.

If you're lucky, the person who wrote the script will have added some notes to tell you where you should start copying parts in and things.

Excersize
So now you have a go.
These next three scripts will actually work in Zelda Classic, so you can compile the code afterwards and see if you've done it right.

CODE
global script PressAforHealth{
    void run(){
        while(true){
            if(Link->InputA){
                Link->HP += 16;
                Game->PlaySound(SFX_REFILL);
            }
        Waitframe();
        }
    }
}

bool isSolid(int x,int y){
if(x<0 || x>255 || y<0 || y>175) return false;
int mask = 1111b;
if(x%16<8) mask &= 0011b;
else mask &= 1100b;
if(y%16<8) mask &= 0101b;
else mask &= 1010b;
int ret = Screen->ComboS[ComboAt(x,y)]&mask;
return ret != 0;
}

global script RunUpwards{
    void run(){
        while(true){
            if(Link->InputUp){
                if(!isSolid(Link->X+8,Link->Y-2)) Link->Y --;
            }
        Waitframe();
        }
    }
}

const int Colour = 15;
const int Radius = 20;
bool circle = true;

global script DrawCircleRoundLink{
    void run(){
        while(true){
            if(circle) Circler();
        Waitframe();
        }
    }
    void Circler(){
        Screen->Circle(6,Link->X,Link->Y,Radius,Colour,1,0,0,0,false,128);
    }
}



So then, the correct way of combining these three scripts would have been like this:
Spoiler

Note - all global scripts contain one 'Waitframe()', and they should contain one only - two or more is bad news.

And that's pretty much all there is to it.
If you're still stuck and it's not working, your best bet would be to post in Script Database Discussion, where someone should be able to help you out.


Thanks to Saffith for the idea of using the Spoiler box, and the isSolid function.


1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users