User Tag List

Results 1 to 9 of 9

Thread: ZScript Technical FAQ (READ BEFORE POSTING BUGS)

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Administrator DarkDragon's Avatar
    Join Date
    Oct 2001
    Posts
    6,228
    Mentioned
    70 Post(s)
    Tagged
    0 Thread(s)
    vBActivity - Stats
    Points
    11,031
    Level
    31
    vBActivity - Bars
    Lv. Percent
    8.81%

    Pointers

    Q: Pointers scare me. How do pointers work in ZScript?

    A: Pointers in ZScript are a lot "tamer" than raw C pointers. The ZScript pointer types are ffc, item, itemclass, link, game, and screen; you can only declare new pointers of the first three types.

    Each pointer type has various data members and functions that can be accessed using the -> operator, listed elsewhere. Using pointer types is not much more difficult than using simple types, with the exception of two subtleties:

    1. Pointer must point to something, or disaster will strike.
    Consider the following code:
    Code:
    item i;
    i->X = 10;
    a new item pointer, i, is declared, then immediately dereferenced. The problem with that is that i doesn't refer to anything, so who knows what item will happen to be moved by the above code: the behavior here is unspecified, and may even crash ZC. To avoid problems with these "dangling" pointers, always declare pointers as pointing to something, such as
    Code:
    item i = CreateItem(3);
    i->X=10;
    or even, if the code is enclosed in an item script,
    Code:
    item i = this;
    i->X = 10;
    2. Pointers become stale after every Waitframe().
    Suppose you have the following code:
    Code:
    item i = CreateItem(3);
    Waitframe();
    i->X = 10;
    This code works fine most of the time, but occasionally crashes ZC. Why? Well, what happens if Link picks up the item during the frame break? The item ceases to exist, and i becomes "dangling."
    To avoid this pitfall, NEVER count on pointers persisting between frame breaks. There are two exceptions. The "this" pointer in ffc scripts will stick around safely until the ffc script Quit()s. The other exception are the "global" pointers: Link, Screen, and Game. You can't read or assign to them, and they remain safe throughout the entirety of the quest.

    Pop quiz: why is it unsafe to call Waitframe() from within an item scripts? (Hint: the "this" pointer.)

  2. #2
    Administrator DarkDragon's Avatar
    Join Date
    Oct 2001
    Posts
    6,228
    Mentioned
    70 Post(s)
    Tagged
    0 Thread(s)
    vBActivity - Stats
    Points
    11,031
    Level
    31
    vBActivity - Bars
    Lv. Percent
    8.81%

    Global Variable Init Problems

    Q: Help! My global variables are not being initialized correctly, or ZC crashes on quest start!

    There are two likely causes for this "bug." Both are caused by misunderstanding how global variables are initialized.

    When you start a new quest, then, and only then, are the global variables initialized, and the order in which they are initialized is only partially specified, as described here.

    Consider the following code:
    Code:
    ffc script foo {
       void run() {}
       int a = 0;
       int b = a+10;
    }
    
    item script bar {
       int c = a+1;
    }
    What's wrong here? The foo script is perfectly fine; a is guaranteed to be initialized before b, so b will be correctly initialized to 10. However, the problem occurs in script bar: even though it comes later in the file than foo's declaration, it is NOT guaranteed that c will be intialized after a, so there's no telling what value c will assume.

    The other possible problem is illustrated in the following snippet:
    Code:
    item script foo {
       void run() {}
       int a = this->X;
    }
    Why is this snippet buggy? Well, recall that a is initialized immediately after quest start, so foo's "this" pointer is not yet pointing to anything meaningful. To correct the code, move the initialization of a inside the run method:
    Code:
    item script foo {
       void run() {a=this->X;}
       int a;
    }

  3. #3
    Administrator DarkDragon's Avatar
    Join Date
    Oct 2001
    Posts
    6,228
    Mentioned
    70 Post(s)
    Tagged
    0 Thread(s)
    vBActivity - Stats
    Points
    11,031
    Level
    31
    vBActivity - Bars
    Lv. Percent
    8.81%

    Waitframe problems

    Q: Ack! I'm trying to do something to all items each frame, but ZC acts weird or crashes!

    A: Great care must be taken whenever you invoke Waitframe() around pointers. As mentioned here, pointers become stale after each frame, and moreoever there's occasionally even greater subtlety. Consider for instance the following snippet:
    Code:
    int numitems = Screen->NumItems();
    while(true)
    {
       for(int i=0; i<numitems; i++)
       {
           item it = Screen->LoadItem(i);
           it->X++;
       }
       Waitframe();
    }
    Can you spot the problem?
    Even though I'm refreshing the item pointers each frame, I'm NOT refreshing the number of items on the screen, so if the number of items decreases (say Link picks one up), one of the LoadItem() calls will return a dangling pointer. The fix is simple; just be sure to recompute both the pointers and the number of items each frame:
    Code:
    while(true)
    {
       for(int i=0; i<Screen->NumItems(); i++)
       {
           item it = Screen->LoadItem(i);
           it->X++;
       }
       Waitframe();
    }

  4. #4
    Administrator DarkDragon's Avatar
    Join Date
    Oct 2001
    Posts
    6,228
    Mentioned
    70 Post(s)
    Tagged
    0 Thread(s)
    vBActivity - Stats
    Points
    11,031
    Level
    31
    vBActivity - Bars
    Lv. Percent
    8.81%

    Interfacing ZScript and ASM

    Q: Can I mix ASM and ZScript scripts?

    A: Yes, if you're very, very careful.

    In general adding ASM scripts in slots unused by ZScript is safe. Just realize that if you write to global or screen registers, you may be writing over values used by some of the ZScript scripts.

    After every compilation the full ASM output of the compiler is written to allegro.log, so by inspecting this output you can determine what global variables are being used by ZScript, so that you can avoid (or not avoid!) using them in your ASM scripts.

    On the other hand, directly modifying the ASM output of a ZScript compilation requires very, very great care, for several reasons:

    1. Almost all of the D registers (currently D0-D6) are reserved as important scratch spaces for things like computing the stack frame offset of local variables, and should not be overwritten by foreign code.

    2. ZScript-compiled code is heavily reliant on the stack for storing everything from local variables to parameters of function calls to this pointers. The state of the stack must thus also be preserved by foreign code.

    3. Changing the line numbers of ZScript code requires a lot of work verifying that references to those line numbers (in things like GOTO) are modified accordingly. Even more tricky is that line numbers are sometimes indirectly stored in registers, pushed onto the stack, then popped off much later and used as return values via GOTOR.

    4. If you ever decide to modify the ZScript code and recompile, you'll of course need to reapply your "patch."

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
About us
Armageddon Games is a game development group founded in 1997. We are extremely passionate about our work and our inspirations are mostly drawn from games of the 8-bit and 16-bit era.
Social