U61 (v0.4.1) - Script callbacks reference




Introduction
============


  User callbacks are lua functions which are called from the C++ engine. U61
  requires all these functions to be declared in a script, even if they do
  nothing. In these user callbacks, you may use functions from the U61 system
  API, but be carefull, you can not use any system API functions in any user
  callback, since there are some access right constraints.



user_do_curse
=============


  Declaration
  -----------

    user_do_curse(num,sent)

    * Arguments: a code previously returned by "user_new_curse", a boolean
      saying if the curse is local or comes from another player.

    * Return values: none

    * Rights: block_read, block_write, map_read, map_write

  Description
  -----------

    This function can be called int 2 cases:

    * When a square explosion has ended, and this square's location was the
      location of the current curse. If you are playing with the default theme,
      it means that the square with the blinking "?" has exploded.

    * When another player has called the "u61_send_curse" function, and the
      current player was his default victim.

    Basically, one could separate the curses into 2 categories:

    * Those who affect the player which launches them. They usually have a
      positive effect. For instance, it can be a goodie which slows your blocks
      down so it's easier for you to play. These curses are executed directly
      by "user_do_curse", the parameter "sent" being set to 0 (false).

    * Those who affect the launcher's victim. They usually have a negative
      effect. For instance, it can be a nasty curse which speeds up the blocks,
      so that they become uncontrollable. These curses are also executed by
      "user_do_curse" (with "sent" set to 0), but one has to call
      "u61_send_curse" manually to send the curse to the other player (the
      victim). This other player will receive the curse, but this time the
      "sent" parameter will be 1 (true). So the "sent" parameter is fundamental
      since it allows you to identify if the curse is to be sent to the victim
      or to be executed.

    You might wonder why I did not simply create 2 categories of curses, and
    let the C++ engine handle all this "send to victim" business. Well, I liked
    the idea of letting the scripter control what's happening. For instance,
    you may create a special curse which sends severa curses (all the ones you
    currently have for instance) to your victim. Basically, what I described
    with the "sent" parameter determining if one has to send the curse or not
    is only a suggestion. You may use the system the way you want, for
    instance, it's possible to create a special curse which forces your victim
    to send a curse to its own victim...

    Of course one of the big strenghs of U61 is that the "user_do_curse" and
    "u61_send_curse" work pretty well, even in a slow networked environment -
    at least I hope so 8-) -.

  Example
  -------

    The following example is a very simplified version of "user_do_curse". This
    function can indeed grow very big and it's a wise decision to make it call
    smaller functions. But still this purpose of this example is to remain
    simple. It has the 2 main types of curses:

    * "ID_CURSE_PLUS" is a goodie which is executed locally and increases the
      player's score.

    * "ID_CURSE_MINUS" is a nasty curse which reduces the score of the player's
      victim.

    ID_CURSE_PLUS=3
    ID_CURSE_MINUS=4
    
    function user_do_curse(num,sent)
        if (num==ID_CURSE_MINUS) then
            if (sent==0) then
                u61_send_curse(num)
            else
                u61_add_score(-1000)
            end
        elseif (num==ID_CURSE_PLUS) then
            u61_add_score(1000)
        end    
    end



user_do_shape
=============


  Declaration
  -----------

    user_do_shape(num)

    * Arguments: a number previously given by user_new_shape

    * Return values: none

    * Rights: block_read, block_write, map_read

  Description
  -----------

    This function shapes the block before it appears on the screen. You should
    not include any random behavior in this function. In fact it is a bad idea
    to include random behaviors in U61 in general, but I mention it here for it
    might be very tempting to ignore the "num" parameter and do a random number
    generator oneself. But this is bound to fail in a network game for you can
    not garantee the remote players will see the right shape. In fact the "num"
    parameter "is" what U61 sents to other players, so it's only by using it
    that you can garantee game consistency.

    A typical "user_do_shape" only needs to call the "u61_add_item" function.
    Indeed this function allows you to add squares to a block and this is what
    you generally need to do since at the very start of the function the block
    is empty, and you wish to end with a fully defined block. You should add
    least one square in evrey block, or the game might freeze.

    Of course you can make the shape generation depend on a curse flag or
    something, but I feel that these kind of things should be done in the
    "u61_new_shape" instead. For instance, if you want to block the shape 0 for
    a while, it's IMHO better not to generate a 0 output in "u61_new_shape"
    than modify temporarly the creation of shape 0 in "u61_do_shape".

    One important thing is to know that after "user_do_shape" has been
    executed, U61 always executes the "u61_center" function, which might alter
    the coordinates you have set. So you should not try to recognize a block
    later in the script by asking the coordinates of his squares, unless you
    are sure that "u61_center" can not alter them, or if you know in what way
    they have been changed. You may wonder why I chose this behavior, for it
    can seem anoying. Well, I just personnally estimate that an automatic
    "u61_center" is a comfortable thing to have, and it avoids badly centered
    blocks.

  Example
  -------

    This set of functions generates:

    * A triangle with color 0 if num is 0

    * A square with color 2 if num is 1

    * A circle with color 5 if num is 2

    function user_do_shape(num)
        if num==0 then
            triangle(0)
        elseif num==1 then
            square(2)
        else
            circle(5)
        end
    end
    
    function triangle(color)
        u61_add_item(0,0,color)
        u61_add_item(0,1,color)
        u61_add_item(1,1,color)
        u61_add_item(0,2,color)
        u61_add_item(2,2,color)
        u61_add_item(0,3,color)
        u61_add_item(1,3,color)
        u61_add_item(2,3,color)
        u61_add_item(3,3,color)
    end
    
    function square(color)
        u61_add_item(0,0,color)
        u61_add_item(1,0,color)
        u61_add_item(2,0,color)
        u61_add_item(3,0,color)
        u61_add_item(0,1,color)
        u61_add_item(3,1,color)
        u61_add_item(0,2,color)
        u61_add_item(3,2,color)
        u61_add_item(0,3,color)   
        u61_add_item(1,3,color)   
        u61_add_item(2,3,color)   
        u61_add_item(3,3,color)   
    end
    
    function circle(color)
        u61_add_item(1,0,color)
        u61_add_item(2,0,color)
        u61_add_item(0,1,color)
        u61_add_item(3,1,color)
        u61_add_item(0,2,color)
        u61_add_item(3,2,color)
        u61_add_item(1,3,color)
        u61_add_item(2,3,color)
    end



user_get_curse_name
===================


  Declaration
  -----------

    user_get_curse_name(num)

    * Arguments: a code previously returned by "user_new_curse"

    * Return values: the name of the curse, which should be shown to the player

    * Rights: map_read

  Description
  -----------

    This function has a very simple goal: provide the user with a name for each
    curse. This name will be disaplyed in the status zone, under his map. Of
    course the game will run correctly if this functions returns a blank
    string, bug it will certainly not help the players.

    The names should not be too long. At the time I write these lines, there's
    a limit of 10 characters, so if you exceed this limit the name will be
    truncated. And be carefull if your name uses lots of "W" and "M", for these
    letters are usually wider then plain "I"s.

  Example
  -------

    The following sample defines 2 curses names. A default name ("surprise")
    has been defined in case the curse is unknown, which should, it's true,
    never arrive if the code is consistent.

    function user_get_curse_name(num)
        local name
    
        name="surprise"
    
        if (num==ID_CURSE_MALEDICTION_OF_GOOLOO) then
            name="gooloo"
        elseif (num==ID_CURSE_YOULLDIE) then
            name="you'll die"
        end
    
        return name
    end



user_land
=========


  Declaration
  -----------

    user_land()

    * Arguments: none

    * Return values: none

    * Rights: block_read, block_write, map_read

  Description
  -----------

    This function is called when it's not possible to move the block down any
    more. It's role is to modify the shape of the block before it is merged
    with the map. Most of the time, this function will do nothing, but you may
    wish - in some cases - to change the shape of the block just as it lands.

    Let's take an example: you might consider that your blocks are not "solid"
    blocks, ie they break themselves into parts as they land. This is a very
    common trick in block-based games, and that's why it's available in U61.

    You could also wish to send a curse as the block lands, or modify the map.
    This is not allowed because of performances issues, which lead me not to
    give the "map_write" right to this function. Indeed this function is very
    important for the "anticipation" mode. In this mode, the user can view in
    real-time where the block will land. This feature is achieved by calling
    the "user_move_down" function until "user_block_land" is required. And I
    have decided that such operations (as they are called very very often)
    should not require a full backup of the map, which can be quite
    CPU-consuming since the map structure can be quite big. It isn't that big
    now but I expect it to grow as I will add features to U61. So basically,
    just remember that there's a good reason not to modify the map in this
    function. However, it does not mean you have no callback at all when a
    block lands. Indeed, it is possible to set up a callback on a pattern match
    by using "user_match_pattern". This should - I hope - be enough in most
    cases.

  Example
  -------

    This example shows a function that makes blocks move up of one row as they
    land. This should make them hang in the air as they land. I can't figure
    out in what kind of context it could be usefull, but it's only a theorical
    example:

    function user_land()
        u61_set_block_y(u61_get_block_y()-1)
    end



user_match_pattern
==================


  Declaration
  -----------

    user_match_pattern(match_count)

    * Arguments: the number of times "user_match_pattern" has already been
      called during last matching session.

    * Return values: 1 if some matching shapes and/or colors have been found, 0
      otherwise.

    * Rights: map_read, map_write

  Description
  -----------

    This is a fundamental function in U61. It is responsible for saying which
    squares should explode when a block lands. It is called after the block has
    landed, ie "user_land" has already been called when "user_match_pattern" is
    called. It is also responsible for incrementing the score of the player.

    You'll notice that this function has no access rights on the block. This is
    because there's no more block when this function is called. Indeed, all the
    squares of the falled block are merged with the map. This means that if
    there were 10 squares in the map and 3 squares in the block, when this
    function is called you get an "all in one"map with 13 squares. By default,
    the block squares are put "as is" on the map, but if you want a more
    accurate control, you may tweak the "user_land" function.

    The pattern to be matched can be anything, based on the position and the
    color of the map squares. You may search a completed horizontal line (as in
    the "classic") script, some sort of alignments, a shape, a sequence of
    colors, well, anything! I believe the "user_match_pattern" function is what
    makes a given set of rule realy different from the others.

    Once the pattern is matched, it is a good idea to make the squares which
    should disappear explode. Technically, you could remove them right away
    (I've not tried it still), but I just think it's more user friendly for the
    player to see the block explode. The common method is to try and find a
    pattern, and when the first pattern is found, stop the search and make the
    founded pattern explode. Then, the Lua function is exited, and the game
    keeps on going. It's take a little time for squares to explode. While
    there's at least one square exploding on the map, U61 does not make a new
    block appear. Instead, it waits for the explosion to end, and when the
    explosion is ended, it runs the "user_match_pattern" function again. This
    way, it's possible to have cascading effects, and the player can understand
    why 12 of his squares disappeared while a pattern is usually made of 3
    blocks.

    This leads to a very important point: the "match_count" parameter. This
    parameter is equal to 0 when "user_match_pattern" is called for the first
    time. Then logically, "user_match_pattern" should make some squares explode
    (if needed of course) and let the squares alone while they blow up. When
    the explosion is finished, "user_match_pattern" is called again, but this
    time "match_count" is 1. Next time it will be 2, etc... When there's no
    more pattern to match, then U61's engine gives the player a new block, and
    resets the internal value corresponding to "match_count". This is a usefull
    feature for it allows an accurate control of the score, for instance, you
    might make something that gives:

    * 100 points if there's only one pattern matched

    * 300 points if there are two pattern matched

    * 700 points if there are three pattern matched

    * etc...

  Example
  -------

    The following example finds any horizontal line which is filled with
    squares, or has only one square missing. It's very close from the pattern
    match used in the "classic" script. Note that the code does not lie
    directly in user_match_pattern but in an external function which is more
    general and can be used in another set of rules if needed.

    function user_match_pattern(match_count)
        return match_holed_line(match_count,1)
    end
    
    function match_holed_line(match_count,max_hole)
        local x
        local y
        local width
        local height
        local holes
        local lines
        
        width=u61_get_width()
        height=u61_get_height()
    
        lines=0
        y=0
        while y    <    height and lines==0 do
            holes=0
            x=0
            while x    <    width do
                if u61_get_square_color(x,y)    <    0 then
                    holes=holes+1
                end
                if not (u61_is_square_exploding(x,y)==0) then
                    holes=holes+1
                end
                x=x+1
            end
            if holes    <    max_hole then 
                delete_line(y)
                lines=lines+1
            end
            y=y+1
        end    
    
        if lines>0 then
            u61_add_score((match_count+1)*1000)    
            if (match_count>=3) then
                u61_add_antidote()
            end
        end
    
        return lines
    end



user_move_down
==============


  Declaration
  -----------

    user_move_down()

    * Arguments: none

    * Return values: none

    * Rights: block_read, block_write, map_read

  Description
  -----------

    This function looks like "user_move_left" and "user_move_right" at first
    sight, but it is quite different. Indeed the "user_move_down" function must
    imperatively move the block down in some way. The reason is that this
    function is used in the following cases:

    * The user presses the "move down" key (that's to say he presses the key
      which he has specified in his settings to be the "move down" key).

    * A certain amount of time has ellapsed, and U61 decides to make the block
      move down. How long this delay is depends on the global speed of the
      game, which can be set in the game options, and generally increases while
      the game is running. And this is why it is important that one of the
      actions performed by "user_move_down" is actually to shift the block
      down. Indeed, one of U61 basic rules - the ones you can not change within
      the script -, is that "the block falls, and when it lands on other
      blocks, it freezes". If "user_move_down" does not move the block down,
      you could get a situation where your block never freezes and you never
      get any new block. Basically, a new block is sent to the user when a call
      to "user_move_down" has created a conflict between the block's squares
      and the map's squares. It is only in this case that the block will be
      freezed and that "user_land" and "user_match_pattern" will be called.

    * The user presses the "drop" key, in this case "user_move_down" is called
      as often as possible until the block is stuck on the map. Again, if you
      ever make a "user_move_down" that does not lead to an incoherent position
      after a certain amount of calls, this function will loop for ever...

    Please note that in this function you do not need to check wether the
    operation you want to perform is possible or not. As with other
    "user_move_..." or "user_rotate_..." functions, the C++ engine
    automatically checks if something is wrong, and if there's a conflict it
    rollbacks the whole operation.

  Example
  -------

    The following function moves the block down, and increments the value of a
    global value at the same time:

    MOVE_DOWN_COUNTER=123
    
    function user_move_down()
        u61_set_block_y(u61_get_block_y()+1)
        u61_set_global(MOVE_DOWN_COUNTER,u61_get_global(MOVE_DOWN_COUNTER)+1)
    end



user_move_left
==============


  Declaration
  -----------

    user_move_left()

    * Arguments: none

    * Return values: none

    * Rights: block_read, block_write, map_read

  Description
  -----------

    This function can generally be called in 2 cases:

    * The user has pressed the "Move left" key. (which key it is physcically
      depends on the user's settings). This is the most common case, and you
      had certainly thought of it.

    * The "Rotate left" or "Rotate right" key has been pressed, and the
      "user_rotate_..." corresponding callback has put the block in such a
      state that there's a conflict between a square of the block and a square
      of the map. Therefore U61 automatically simulates a "Move left" or "Move
      right" key press, and then retries to execute the "user_rotate_...". This
      is an interesting feature since it enables the user to rotate his blocks
      even if the block is stuck to a wall.

    Apart from this last point the "user_move_left" function is very similar to
    "user_rotate_left". One could certainly imagine scripts vhere
    "user_move_left" would not translate the block on the left. And even if in
    your script "user_move_left" perfor;s a left translation of the block, it
    is possible in this function to test if a given curse is active and change
    the function's behavior. This is what the "goofy" curse (in U61 0.2.2)
    does: it inverts the effects of "user_move_left" and "user_move_right",
    which makes the game a little... harder to control!

    It's important to note that substracting 1 to the x coordinate of each
    square of the block won't make the block move to the left. Indeed after
    each call to "user_move_left" U61 centers the block (similar to a call to
    u61_center), so the only way to make a block translate is to use the
    "u61_set_block_x" function (or any associated function).

  Example
  -------

    This example function makes the block move left and down at the same time:

    function user_move_left()
        u61_set_block_x(u61_get_block_x()-1)
        u61_set_block_y(u61_get_block_y()+1)
    end



user_move_right
===============


  Declaration
  -----------

    user_move_right()

    * Arguments: none

    * Return values: none

    * Rights: block_read, block_write, map_read

  Description
  -----------

    Similar to "user_move_left" but associated to the "Move right" key.

    Like with the "user_rotate_..." functions, "user_move_right" does not have
    to be the exact contrary of "user_move_left". You are just free to imagine
    the weirdest behaviors.

  Example
  -------

    In this example, the block is shifted of 2 blocks right:

    function user_move_right()
        u61_set_block_x(u61_get_block_x()+2)
    end



user_new_curse
==============


  Declaration
  -----------

    user_new_curse(num)

    * Arguments: a random value generated by the C++ engine

    * Return values: the code of the curse that will be triggered if the
      current curse block explodes

    * Rights: block_read, map_read

  Description
  -----------

    This function is very similar to "user_new_shape". It's goal is to control
    the nature of the next curse which will be given to the player. Its only
    purpose is to return an integer which will be interpreted by
    "user_do_curse" later.

    This function is called long before the player actually matches a pattern.
    Indeed, it is required to do so since the name of the next curse must be
    displayed in the player's information board/zone.

    Of course, this "next curse" generation can be influenced by curses which
    affect the current player. For instance, you might decide that when a
    player is victim of the "THOU_SHALL_DIE" curse, then he can only get poor
    curses which have almost no effects on the opponents, and leave him in a
    hopeless position, bound to lose very soon. This is not fair practice but
    it's theorically possible 8-P

  Example
  -------

    The following example returns a curse code between 0 and 9 if the
    "ID_ZOUBIDA_MALEDICTION" curse is activated, and a code between 0 an 19 if
    not.

    ID_ZOUBIDA_MALEDICTION=5
    
    function user_new_curse(num)
        if u61_get_curse_age(ID_ZOUBIDA_MALEDICTION)    <    0 then
            num=mod(num,20)
        else
            num=mod(num,10)
        end
    
        return num
    end



user_new_shape
==============


  Declaration
  -----------

    user_new_shape(num)

    * Arguments: a random number between 0 and 1 000 000 000

    * Return values: the code that should be use for the next call to
      "user_do_shape"

    * Rights: block_read, map_read

  Description
  -----------

    This function is usefull for you to control the codes used for shape
    generation. Basically, a set of scripts might require the shape number to
    be between 0 and 6. So in this case you will return a value between 0 and
    6. The "num" argument is here to provide a randomly generated number. You
    are free to use this value or not, but keep in mind that if you don't use
    it, it will be too late in the "user_do_shape" function to use some random
    numbers. You can consider this function as an intermediate between the
    random function and the function that generates the shape. Indeed the
    number it returns is used by the C++ engine to generate an internal event
    which will be sent to all remote players.

    Tweaking this function can be very interesting if you want for instance to
    set up a mode where the player only receives blocks with shapes 34 an 125.

  Example
  -------

    This function returns a shape code between 0 and 6. More precisely, it
    returns:

    * 40% of 0

    * 20% of 1

    * 10% of 2,3,4

    * 5% of 5,6

    user_new_shape(num)
        local result
    
        num=mod(num,100)
    
        if num>=60 then
            result=0
        elseif num>=50 then
            result=1
        elseif num>=30 then
            result=2
        elseif num>=20 then
            result=3
        elseif num>=10 then
            result=4
        elseif num>=5 then
            result=5
        else
            result=6
        end
    
        return result
    end



user_rotate_left
================


  Declaration
  -----------

    user_rotate_left()

    * Arguments: none

    * Return values: none

    * Rights: block_read, block_write, map_read

  Description
  -----------

    This function is called when the user presses the "Rotate left" key (what
    key exactly it is depends his key settings).

    It may or may not perform a geometrical rotation. This is the case with the
    "classic.lua" script, but if you play with the "vertical.lua" script,
    you'll find out that rotation is in fact a "color shift". Basically,
    rotation should be any player launched function that alters your block but
    is not a plain translation.

    If after executing the code of "user_rotate_left" there's a confict between
    a block square and a map square, I mean if they have the same absolute
    position, then the operation is automatically cancelled. This is one of the
    reasons you do not get "map_write" access in this function, indeed, the
    rollback of a map, especially the act of making a copy of a whole map in
    case the operation fails would in my opinion be exagerately time consuming,
    and I like the idea that "user_rotate_left" only affects the block.

    This function is granted a "map_read" access, therefore you check if a
    curse is activated and modify the behavior of "user_rotate_left"
    dynamically.

  Example
  -------

    In order to show that "user_rotate_left" does not need to be a geometrical
    correction, this sample function performs a left/right flip of the block:

    function user_rotate_left()
        local size
        local i
      
        size=u61_get_nb_items()
        i=0
        while i    <    size do
            u61_set_item_x(i,-u61_get_item_x(i))
            i=i+1
        end
    end



user_rotate_right
=================


  Declaration
  -----------

    user_rotate_right()

    * Arguments: none

    * Return values: none

    * Rights: block_read, block_write, map_read

  Description
  -----------

    Exactly the same than "user_rotate_left". It's important to note that
    "user_rotate_left" does not need to be the contraryof "user_rotate_right".
    You may for instance decide that for a given script the "rotate left" and
    "rotate right" keys will correspond to 2 "special actions" which are by no
    way linked to each other. In fact the reason these functions are call
    "user_rotate_..." for is that in most scripts this name makes sense, and it
    would be IMHO a waste of time to redefine for each key and each script what
    label should be associated to keys.

  Example
  -------

    The following function is a function that increments the color of all the
    squares of the block:

    function user_rotate_right()
        local size
        local i
      
        size=u61_get_nb_items()
        i=0
        while i    <    size do
            u61_set_item_color(i,mod(u61_get_item_color(i)+1,8))
            i=i+1
        end
    end



user_square_blown_up
====================


  Declaration
  -----------

    user_square_blown_up(x,y)

    * Arguments: coordinates of the square which blew up

    * Return values: none

    * Rights: map_read, map_write

  Description
  -----------

    This function is called when a square has exploded. Explosions are usually
    started after a pattern match (see "user_match_pattern"), and then the C++
    engine handles the explosion by himself, without calling any script. This
    takes a little time - during which the game keeps running - and when the
    explosion is finished, after say approximately 1/2 second,
    "user_square_blown_up" is called.

    If the square that exploded had the same location that the current curse,
    then the "user_do_curse" function is launched. You do not have to do
    anything about this, it's just done automatically by the C++ engine.

    Note that a single call to "user_match_pattern" can cause several squares
    to explode, so there will be several calls to "user_square_blown_up" - one
    per square to be precise -.

    It's the responsibility of "user_square_blown_up" to change the map's
    structure after the square's explosion. If for instance the blown up square
    is to be replaced by the squares which are above it, one has to shift
    "manually" the squares down. Of course, you can decide that in some
    circumstances, the end of the explosion should cause the start of a new
    explosion elsewhere...

    When this function is called, the square is still present on the map, and
    it still has its color. I mean that you can make the difference between a
    blue and a red blown up squares. This can be usefull if you want to set up
    cascading chains of explosions. Such effects can also be obtained by
    tweaking "user_match_pattern", but you may prefer to place your code in
    "user_square_blown_up", especially if your chain of explosion really
    depends on where the previous explosion was. The important thing to
    remember is that when there are no exploding squares and no pattern to
    match left, then the player gets a new block.

  Example
  -------

    The following function shifts down all the squares that are above the blown
    up square, and changes their color to that of the disappeared square. Note
    that this function moves the "curse" square if needed.

    function user_square_blown_up(x,y)
        shift_column_down_and_color_it(x,y)
    end
    
    function shift_column_down_and_color_it(x_col,y_bottom)
        local y
        local color
    
        color=u61_get_square_color(x_col,y_bottom)
        y=y_bottom
        while u61_get_square_color(x_col,y-1)>=0 and y>0 do
            u61_set_square_color(x_col,y,color)
            y=y-1
        end
        u61_set_square_color(x_col,y,-1)
    
        if u61_get_curse_x()==x_col and u61_get_curse_y()    <    y_bottom then
            u61_set_curse_y(u61_get_curse_y()+1)
        end
    end



user_time_callback_1
====================


  Declaration
  -----------

    user_time_callback_1()

    * Arguments: none

    * Return values: none

    * Rights: block_read, block_write, map_read, map_write

  Description
  -----------

    This function is called once per second. It has all rights on the map and
    block, so it's possible to do anything with it. By default, it increments
    the score of the player by 1 point, since the longer you play, the better
    you are...

    Another typical use of this function would be to check if a curse is active
    and execute a special function if the curse is set on.

  Example
  -------

    In this example, if the "ID_BAD_LUCK" curse is active, then the player's
    score is not incremented any more.

    ID_BAD_LUCK=13
    
    function user_time_callback_1()
        if (u61_get_curse_age(ID_BAD_LUCK)    <    0) then
            u61_add_score(1)
        end    
    end



user_time_callback_10
=====================


  Declaration
  -----------

    user_time_callback_10()

    * Arguments: none

    * Return values: none

    * Rights: block_read, block_write, map_read, map_write

  Description
  -----------

    This function is called 10 times per second. It has all rights on the map
    and block, so it's possible to do anything with it.

    This is a very good place to code most of the curse effects. Indeed, doing
    something 10 times per second makes things look fast and smooth enough in
    many cases, and it's not *too* time consuming (at least not as much as
    placing the code in user_time_callback_100).

  Example
  -------

    In this example, if the "ID_SHIFT_LEFT" curse is active, then the block is
    moved to the left.

    ID_SHIFT_LEFT=33
    
    function user_time_callback_10()
        if (u61_get_curse_age(ID_SHIFT_LEFT)    <    0) then
            u61_set_block_x(u61_get_block_x()-1)
        end    
    end



user_time_callback_100
======================


  Declaration
  -----------

    user_time_callback_100()

    * Arguments: none

    * Return values: none

    * Rights: block_read, block_write, map_read, map_write

  Description
  -----------

    This function is called 100 times per second. It has all rights on the map
    and block, so it's possible to do anything with it.

    Please take care not to put any complex code here, since it's executed very
    often. For instance, if there are 5 players in a game, it will be called
    500 times per second, so if it takes 1 millisecond to be executed, then
    this function will eat up 50% of the CPU, so there won't be much left for
    other functions (which include all other Lua functions, C++ core engine,
    other tasks ran by the OS....). Basically, this function is here so that
    people who want a very accurate control of what's happening have this
    control, but in most cases one should try to use user_time_callback_10 or
    user_time_callback_1.

  Example
  -------

    In this example, if the "ID_SHIFT_RIGHT" curse is active, then the block is
    moved to the right. One could have - as in most cases - used the
    user_time_callback_10 function instead, only the block would have moved
    slower.

    ID_SHIFT_RIGHT=66
    
    function user_time_callback_100()
        if (u61_get_curse_age(ID_SHIFT_RIGHT)    <    0) then
            u61_set_block_x(u61_get_block_x()+1)
        end    
    end



user_use_antidote
=================


  Declaration
  -----------

    user_use_antidote()

    * Arguments: none

    * Return values: none

    * Rights: block_read, block_write, map_read, map_write

  Description
  -----------

    This function is called when the player presses the key associated to the
    "use antidote" function. It is a user callback since there can not be a
    generic function for this. Indeed, what has to be done for such an action
    really depends on how all the curses have been coded.

    Here are a few examples of what one could code in this function:

    * Remove the oldest curse. That's an obvious solution, when you use an
      antidote, it cures your oldest illness.

    * Remove the curses in a special order, based on the severity of the curse.
      This way one could imagine that an antidote always cures the most
      annoying curse.

    * Change the effect of curses. One could imagine special curses which the
      antidote can't really kill, but can make them less annoying, as if the
      desease were half cured.

    * Invert the effect of the antidote. Indeed, a nice curse would be to put
      the player in such a state that the use of an antidote has the opposite
      effect of what he expects.

    So as there are so many possibilities, I think it's a good thing to let the
    scripter code whatever he wants for this function. In fact, the one thing
    to remember about antidotes is that they appear in the status box of the
    player, and it's supposed to help the player.

  Example
  -------

    In this example, the antidote cures the oldest curse which affects the
    player, except for the "ID_ROTTEN_CURSE" curse. This makes the "rotten"
    curse an uncancellable curse, from an antidote point of view. Of course you
    can imagine that there are other ways to cure it, such as matching a
    special pattern for instance.

    ID_ROTTEN_CURSE=66
    
    function user_use_antidote()
        local oldest
    
        oldest=u61_get_oldest_curse(0)
        if oldest~=ID_ROTTEN_CURSE then
            u61_cancel_curse(oldest)
        end
    end

