# LScript v2.0 Release Notes

      A new function called filefind() has been added that takes
      a file name (sans path information) and scans through the
      directory paths in the application in order to locate the
      file.  The path to the file is returned.

            if((where = filefind("lw.cfg")) != nil)
            {
                ...

      This function uses those paths that are available to
      the getdir() function in order to find the file.
      A new function called filecrc() calculates the CRC-32 value
      of the contents of a file, and returns this integer value.
      LScript has a new array type known as associative arrays.
      Associative arrays are indexed using string values instead
      of integer indices.  Associative arrays are powerful
      mechanisms that can be used to simulate user-defined
      structures, such as those found in C or Pascal.

      In associative arrays, the index value used (the character
      string) is the symbol to which attributes are associated.
      The attributes are contained in the arrays that are indexed
      using the symbol.  Yes, it all sounds very confusing.  It
      should be clearer, however, in practice.

            fruit_color["apple"]    = "red";
            fruit_color["banana"]   = "yellow";
            ...
            fruit_shape["apple"]    = "round";
            fruit_shape["banana"]   = "curved";
            ...
            fruit_weight["apple"]   = 128;      // grams
            fruit_weight["banana"]  = 117.3;    // grams
            ...

      As you can see from this short example, the name of the
      instance of the structure is the index symbol ("apple",
      "banana", etc.).  The individual members of each
      structure is actually the name of each array ("fruit_color",
      "fruit_shape", etc.).

      Associative arrays can be created/initialized with the
      new associative initializer operator '$'.  These
      new operators are used in a similar fashion to the
      initializer block operators ('@').

            normal_array = @1,2,3,4@;
            assoc_array  = $ .. $;

      In order to initialize an associative array, however,
      you must use pairs of elements.  The first part of
      each pair must be a character string that is the
      associative symbol for the data found in the second part.

            fruit_color  = $ "banana", "yellow", "apple", "red" $;
      A new form of conditional expressions is available in the
      language.  Traditional conditional expressions use the if()
      test construct in a pre-condition position:

            if(<expression>>)
            {
                ...
            }

      The new in-line conditionals allow single statements to be
      conditionally evaluated:

            x = 1 if y == 15 && cos(4.35);
            ...
            error("Division by zero!") when z == 0;
            ...
            x = 1 unless y == 10 || z == 2;
            ...
            break if x == 5;
            ...
            return 5 if !upper_limit;
            ...
      There is a new implicit 'this' container that holds the
      results of the evaluation of the last expression.  This
      system is not yet fully implemented, but it allows for
      not having to handle immediate values using intermediate
      variables:

            ...
            while(!file.eof())
            {
                file.read();  // the line goes into 'this'

                // does the line begin with "PUB"
                // followed by a C++ comment?

                if(s~^PUB.*\/\/.*~)
                    info();   // display it to the user...
            }
            ...
      Boolean tests are now "short circuited", allowing expressions
      like this to be used:

            ...
            file = "C:\\CONFIG.SYS";
            f = File(file,"r") || error("Cannot open file '",file,"'");
            ...
      The LScript Generic system now supports Layout's new
      CommandSequence system, and currently has over 160 new commands
      to facilitate this.  CS commands are accessed by name, and they
      exist in LScript as functions.

            @version 2.0

            generic
            {
                AddNull();
            }

      When a CS command requires parameters, they should be provided
      as though you were calling a UDF.
      The LScript pre-preprocessor now supports a new pragma
      directive called "script".   This directive is an identifier
      that is intended to aid LightWave's LCore system to identify
      a script being added as a plug-in.

            @script modeler
            @script displace
            @script generic
            @script motion
            @script image
            @script replace
            @script master
            @script shader
            @script channel
      LScript now has in-language support for regular and
      search-and-replace expressions.  Regular expressions are powerful
      pattern matching functions that have their own special meta-
      character langauge.  This pattern-matching system originated
      on the UNIX operating system, and has permeated a great many
      places in the software world.  There is a large body of reference
      available for regular expressions, so I will not provide a
      detailed explanation of them here.  Rather, I will tell you
      how to employ them in LScript.

      To perform pattern matching using regular expression in
      LScript, you need to use the new "s~~" operator.  This operator
      instructs LScript to use the regular expression provided to
      compare against a character string to see if the pattern
      exists.

            // see if the string begins with a
            // capital "B", followed anyplace
            // by two consecutive "o"'s

            if("Bob Hood" == s~^B.*oo~)
            {
                ...

      Regular expressions can also be used to perform search-and-replace
      operations on character strings.  This mechanism is accessed
      using the "r~~~" operator.  This operator instructs LScript to
      match the pattern provided, and to replace it with the character(s)
      provided.  In order to access the search-and-replace mechanism,
      the new search-and-replace assignment operator "~=" is used:

            // a sed-like search-and-replace using the s/r operator

            if((input = File("nfa.c","r")) == nil)
                return;

            if((output = File("nfa.bak","w")) == nil)
                return;

            while(!input.eof())
            {
                line = input.read();

                // replace any "PRIVATE" strings found
                // at the start of the line (^) with
                // the characters "static "

                line ~= r~^PRIVATE~static ~;
                output.writeln(line);
            }

            input.close();
            output.close();

      Regular expression patterns can be assigned to variables, and
      those variables used in their place:

            // a sed-like search and replace using the s/r operator

            if((input = File("nfa.c","r")) == nil)
                return;

            if((output = File("nfa.bak","w")) == nil)
                return;

            sr = r~^PRIVATE~static ~;

            while(!input.eof())
            {
                line = input.read();

                line ~= sr;
                output.writeln(line);
            }

            input.close();
            output.close();

      LScript can also construct regular expressions from character
      strings using the new regexp() function.  This can be useful
      for allowing users to enter regular expression patterns to
      be used:

            // display lines in a file that begin with the letters "PUB"

            // compile a regular expression to be used
            expr = regexp("^PUB");

            // regexp() returns 'nil' if the expression fails to compile
            if(expr == nil)
                return;

            if((file = File("nfa.c","r")) == nil)
                return;

            while(!file.eof())
            {
                line = file.read();

                if(line == expr)
                    info(line);
            }

      Regular expressions also recognize the 'this' container

            ...
            while(!file.eof())
            {
                file.read();

                if(expr)
                    info();
            }
            ...
      Multidimensional arrays no longer exist as entities within
      LScript.  All arrays are now strictly linear.  However,
      arrays can be assigned to specific elements of other arrays,
      allowing multi-dimensional arrays to be constructed by the
      script as it executes.  Such user-defined multidimensional
      arrays are as dynamic as their linear counterparts, and
      elements can be added to any point at will.

            // generate a multidimensional array "on-the-fly"
            a[3,5,2] = 0;

      For efficiency, multi-dimensional arrays created in this
      fashion are not "robust."  For example, the above array
      creation generates a three-element array, whose third
      element is an array of five elements, whose fifth element
      is an array of two elements, whose second element has a
      value of zero (0).  Put graphically:

            a[1] -> nil
            a[2] -> nil
            a[3] -> [1] -> nil
                    [2] -> nil
                    [3] -> nil
                    [4] -> nil
                    [5] -> [1] -> nil
                           [2] -> 0

      To create "robust" multidimensional arrays (where all
      elements are fully populated), it must be formally
      declared as all multidimensional arrays were required
      to be in LScript v1.x:

            var     a[3][5][2];

      Mult-dimensional arrays can be constructed by piecing
      together linear arrays:

            a[5] = "Bob";
            b[3] = a;
            b[3,5,2] = 'a';     // the string "Bob" now becomes "Bab"

      Because arrays can now be members of other arrays,
      arrays-as-elements can now be passed to user-defined
      functions:

                ...
                // sub-array passing to functions

                a[5] = "Bob";
                b[2] = 99.9;
                b[3] = a;
                c[8] = b;

                info(c);
                info(c[8]);
                info(c[8,3]);
                info(c[8,3,5]);

                test(c);
                test(c[8]);
                test(c[8,3]);
                test(c[8,3,5]);
            }

            test: a
            {
                info(a);
            }
      Images loaded into Layout (through the Image Editor) can now
      be displayed using the ctlimage() function.  Image names
      prefaced by a dollar sign character ($) will be resolved by
      LScript using any loaded image files instead of trying to
      locate an image file on disc.  Names following the dollar
      sign metacharacter should exactly match that of the named
      displayed for the image by Layout.

            ...
            ctlimage("$ph_019.tga",0,0);
            ...

      Images referenced in this fashion are ignored when the script
      is compiled for run-time execution.  This means that the image
      referenced must be available from Layout when the compiled
      script is executed.
      The ctlimage() function has three new (optional) parameters
      for use in scaling images displayed with it.  The fifth
      parameter (following the optional transparency vector)
      specifies the scaling factor or size of the image's width.
      Parameter six specifies the scaling factor or size of the
      image's height.  The final parameter (7) is a boolean flag
      that instructs LScript to preserve the images aspect ratio.
      By default, the aspect ratio is not preserved.

      The numeric values that are provided for width and height
      scaling are interpreted by LScript by their type.  If you
      provide an integer value for either component, then that
      value will be considered an absolute width (in pixels) of
      the resulting image.  On ther other hand, if you pass a
      number (floating point) in that position, then LScript
      will consider it a percentage factor (where 1.0 equals 100%)
      that will be applied to the images original width or
      height to determine the final pixel width.  Because this
      mechanism is not designed to enlarge images, anything that
      causes the resulting width or height to exceed the image's
      original dimensions will be clamped to the image's
      original dimension.

            ...
            // display the image at (0,0), 40% of its original
            // width and 30% of its original height

            ctlimage("$ph_019.tga",0,0,,.4,.3);

            ...
            // display the image at (0,0), 100 pixels wide
            // and 100 pixels tall, preserving its aspect
            // ratio (which means that it will probably not
            // be exactly 100x100 when displayed)

            ctlimage("$ph_019.tga",0,0,,100,100);

            ...
            // display the image at (0,0) in its original
            // (unaltered) form

            ctlimage("$ph_019.tga",0,0);
      User-defined functions can now be called recursively.  This
      capability only extends to those functions that you yourself
      define within your script.  Pre-defined functions--those
      known to LScript, like main() or options()--cannot be called
      recursively.

      Recursion is a powerful feature, yet it can also be tricky and
      dangerous.  Unless you understand what you are doing, you can
      send LScript into an infinite loop (which will eventually lead
      to a crash of the application).  LScript attempts to support
      endless levels of recursion, however, other practical limits
      (such as memory) restrict the depth.

      Before using recursion, be sure you know what you're doing.
      Here is a Modeler LScript that uses recursion to traverse
      the entire root directory hierarchy to count the number of
      directories that exist on the drive:

            @warnings

            output;
            count;

            main
            {
                count = 0;

                chdir("\\");    // start at the root directory
                do_dir();

                info(count," directories processed!");
            }

            // a "depth-first" recursive directory traversal

            do_dir
            {
                if((directories = matchdirs(".","*.*")) == nil)
                    return;

                len = directories.size();

                for(x = 1;x <= len;x++)
                {
                    chdir(directories[x]);
                    ++count;

                    do_dir();       // <-- recursion

                    chdir("..");
                }
            }

      The LScript language now has a new iterator construct called
      foreach().  This construct is intended to simplify the process
      of iterating over a linear series of values.  It takes as
      parameters a variable to be used to contain each value, and
      an expression that will evaluate into some data type capable
      of iteration.

      The foreach() iterator accepts the following data types for
      iteration:

    integer
    number
    File Object Agent
    Array (including Modeler LScript's points[] and polygons[]
    automatic arrays, and Associative arrays)

            ...
            editbegin();

            foreach(x,polygons)
                info(x);

            editend();
            ...

            --------------------

            ...
            foreach(x,10 - 5)
                info(x);
            ...

            --------------------

            ...
            f = File("c:\\config.sys","r");
            foreach(x,f)
                info(x);
            ...

            --------------------

            ...
            obj = getfirstitem(MESH);
            foreach(x,obj)
                info(x.name);
            ...

            --------------------

            ...
            a = @1,2,3@;
            foreach(x,a)
                info(x);
            ...
      An Image Object Agent has been added to the list of Layout objects
      to which LScript provides an interface.  getfirstitem() will now
      accept either IMAGE or "IMAGE", returning the first image file
      in Layout's Image list, or 'nil' if none are loaded.  Each Image
      Object Agent contains the following attributes:

        name        the name as it appears on the interface
        isColor     a boolean indicating a color or b&w image
        width       the image's width in pixels
        height      the image's height in pixels

      Additionally, each Image Object Agent recognizes the following
      methods:

        <string>            filename(<frame>)
        <grey>              luma(<col>,<row>)
        <r>,<g>,<b>         rgb(<col>,<row>)
        <ImageObject>       next(<void>)

      The following methods are only available to LScript/PT scripts:

        <void>              needAA(<void>);
        <number>            lumaSpot(<col>,<row>,<size>,<blend>);
        <num>,<num>,<num>   rgbSpot(<col>,<row>,<size>,<blend>);

            generic
            {
                image = getfirstitem("IMAGE") || error("No images loaded!");

                foreach(i,image)
                    info(i.name,": ",.i.width,",",i.height);
            }
      Two new functions are now available in Layout LScript, called
      loadimage() and clearimage().  These functions will,
      respectively, load a new image file into Layouts image list,
      and clear that image (unload it).

            generic
            {
                image = loadimage("f:/lw/cdrom/FilmImage.tga") || error("Can't load image!");
                info(image.width,",",image.height);
                clearimage(image);
            }
      Expressions in LScript can now be chained together, when
      appropriate.

            ...
            // access a File Object Agent from an array element
            v[3] = nil;
            v[2] = File("f:/dirs.txt","r");
            info(v[2].read().isStr());
            ...

            ...
            // get the first line of a text file in one line
            line = File("f:/dirs.txt","r").read();
            info(line);
            ...

            ...
            // access a vector
            v[2] = <1,2,3>;
            info(v[2].x);
            ...

      Expression chaining cannot be used on the left of an
      assignment, because expressions can only exist on the
      right of an assignment statement.  For instance, the
      following would be invalid, and generates an error:

            ...
            v[2] = <1,2,3>;
            v[2].x = 0;
            ...
      The Associative array type been added to the data types accepted
      by the foreach() iterator.  Key values are processed in the order
      in which they are found in the internal hash tree.
      Reentrancy has been a problem for Layout LScript in certain
      situations in previous versions.  As a specific example, if
      a single plug-in is managing two or more objects (instances)
      that have dependencies on each other, then it is possible
      that the plug-in's process() function can be called when
      the plug-in is already in the process() function.  LScript
      has been unable to handle this situation because it uses
      global variables (for historical reasons).

      An instance stack has been implemented in Layout LScript v2.0
      in order to allow this reentrancy to take place in scripts.  It
      allows two or more objects to have interdependencies when they
      each have the same type of LScript plug-in applied.  Here's an
      example of an interdependency that would crash previous versions
      of Layout LScript (based on an example provided by Alexandre Bon).

        1. Add three Nulls to a scene, named "A", "B", and "C".
        2. Give Null "A" motion.
        3. Apply the Item Motion script below to Null "C", and
           tell it to watch "B".
        4. Apply this same script to Null "B" and tell it to
           watch to "A".
        5. Run the animation.

            @version 2.0
            @script motion

            parent;

            process: ma, frame, time
            {
                if(!parent) return;

                newpos = parent.param(POSITION,time);
                newpos.z -= 2;

                ma.set(POSITION,newpos);
            }

            options
            {
                if(!reqbegin("Follow The Leader"))
                    return;

                c1 = ctlallitems("Select parent",parent);

                if(reqpost())
                    parent = getvalue(c1);

                reqend();
            }
      Improvements in LScript's virtual machine code for v2.0
      make it possible now to use object-based array references
      on the left of an assignment:

            v[1] = <1,2,3>;
            v[1].x = 10;

            . . .

            t = 1;
            obj[t] = ObjectMan("Cow.lwo");
            obj[t].surfaces[t] += "_Bob";
      The Layout LScript compiler will automatically select the
      plug-in architecture for you if it detects the new "@script"
      pragma in the selected source script.
      A new debug() command has been added to the LScript
      function toolset.  This command takes no arguments, and, when
      it is encountered, it will attempt to establish a session
      with the new LSIDE LScript Debugger.

      If the LScript Debugger is already running, and is idle,
      a debug session for the current script will activate.

      If the LScript Debugger is not already running, LScript will
      attempt to locate and execute the LScript Debugger.  Once it
      is active, a debug session will begin.  LScript will look for
      the LScript Debugger binary in the same directory as the
      LightWave binaries and libraries.
      Layout CS functions that expect an item id as their argument
      (such as SelectItem() or GoalItem()) will accept a varying
      combination of arguments to aid you in designating the needed
      Layout object.

        - Integer values can be specified that are formatted as
          they are in the LightWave scene file.  These values
          are hexidecimal, so some bit twiddling will be necessary
          in order to place values in the correct locations of
          the 32-bit integer:

                // select the second Mesh object

                SelectItem((1 << 28) | 1);

        - A string value can be provided that should evaluate
          to the name of an object in the current scene.

        - An object class can be specified (MESH, LIGHT, CAMERA),
          followed by an integer that specifies the linear offset
          (1-based) of the object in the list of objects of that
          class.

                // select the third Light in the scene

                SelectItem(LIGHT,3);
    - The following Layout CS functions accept an optional character
      string to name the new object.  If a name is not provided, Layout
      will post a requester to the user.

                AddNull
                AddBone
                AddChildBone
                AddDistantLight
                AddPointLight
                AddSpotlight
                AddLinearLight
                AddAreaLight
                ReplaceWithObject
                ReplaceWithNull
                LoadObject
      Owing to changes in the underlying plug-in API for LightWave [6],
      the following LScript functions, introduced in LScript v1.x, and
      relating to surfaces, have been removed:

                _getRawSurf()
                _setRawSurf()
      A new preprocessor pragma directive has been added to control the
      equality testing of floating-point values.  The 'fpdepth' pragma
      specifies the number of digits to the right of the decimal that
      are to be considered significant when floating point values
      (numbers, vectors, etc.) are compared for equality.  The pragma
      setting has domain over the entire run-time operation of the
      script.

            @fpdepth 3
      A new object masking mechanism has been added to the language.
      This mechanism allows objects of integral data types that support
      it to perform a "masking" operation on themselves.  The exact
      result of this masking function will vary by the data type.

        - Floating-point values (numbers, vectors) will
          use the masking value to render only that number
          of digits to the right of the decimal as significant.

        - Character strings will use the masking value to
          return that number of characters from the left
          (equivalent to strleft()).

        - All other data types will evaluate to themselves
          (i.e., masking is not applicable).

      In order to mask an object, you follow the object operator
      ('.') with an integer value.  For instance, the following
      code will print the value "1.34":

            ...
            bob = 1.34398545;
            info(bob.2);
            ...

      Masking operators can only be used on variables.

      Expression chaining can also be used:

            ...
            bob = 1.34398545;
            info(bob.2.asStr().isStr());        // prints 1
            ...
      A pair of new LScript plug-ins have been added to provide
      scripting capabilities to the Layout Channel Filter plug-in
      architecture.  'ls-cf.p' and 'lsrt-cf.p' are the text and
      run-time support plug-ins for this architecture.

      The process() UDF receives three arguments.  The first
      is a Channel Access Object Agent, the second the current
      frame number, and the third is the current time index.

            ...
            process: ca, frame, time
            {
            }
            ...

      The Channel Access Object Agent exports one data member,
      'name', which represents the displayable name of the channel
      to which the script is applied.  In addition, two methods are
      available, get() and set(), for retrieving and setting
      (respectively) the channel's data.  This data is a floating-
      point value.

      The get() method requires a time index.  The set() method
      accepts the new value for the channel at the time index
      provided to the process() UDF.
      Item Motion LScripts can now optionally accept the Mesh Object
      Agent to which they belong in their create() function:

            @script motion
            ...
            create: obj
            {
                info(obj.filename);
            }
            ...

      This argument is optional and may be left off of the UDF's
      definition.
      LScript Object Agents now provide an interface to their
      individual channels.  New methods and Object types allow
      you to iterate through, and interact with, an object's
      envelope channels.

      The new firstChannel() method will return the first
      channel associated with an object.  Like the firstChild()
      method, this method must be used as the preface of an
      iteration through all channels.  The nextChannel() method
      returns the next channel in the list of channels for an
      object.  Both methods will return 'nil' if there are no
      (further) channels available.

            light = getfirstitem(LIGHT);

            c = light.firstChannel();
            while(c)
            {
                ...
                c = light.nextChannel();
            }
      A new Channel Object Agent has been added to the LScript
      OA toolset.  The new Object Agent provides a structured
      interface to individual channel functionality.  The new
      OA methods firstChannel() and nextChannel() return this
      new OA type.  In some circumstances, a Channel Object
      Agent will be provided to a UDF (as in the case of the
      create() UDF for the Channel Filter LScript).

      As of this release, the Channel Object Agent exports the
      following data members:

            name        the name of the channel as it appears
                        in the Layout interface (i.e., Graph
                        Editor)

            type        the type of the channel.  this can be
                        one of the following: NUMBER, DISTANCE,
                        PERCENT, ANGLE

      Two methods are available:

            value()     this method causes the channel to be
                        evaluated at the specified time.  the
                        return values is a floating-point number
                        that should be interpreted based upon
                        the channel's type.

            event()     this method is used to activate a callback
                        function that will be invoked whenever an
                        event occurs on the channel.  such events
                        could be the creation or deletion of
                        keyframes in the envelope, or a change in
                        the a keyframe's value.

      The following Channel Filter script illustrates the usage
      of this new Object Agent:

            @warnings
            @script channel
            @version 2.0

            create: channel
            {
                light = getfirstitem(LIGHT);

                c = light.firstChannel();
                while(c)
                {
                    last if c.name == "Position.X";
                    c = light.nextChannel();
                }

                // keep abreast of changes in this
                // channel...

                c.event("lightEvent");
            }

            process: ca, frame, time
            {
                ca.set(0.0);
            }

            lightEvent: channel,        // Channel Object Agent
                        event           // event code
            {
                // something happened to the Light's
                // "Position.X" channel

                info(event);
            }
      C-like character escape sequences for tabs and newlines
      ('\t' and '\n') are now supported in strings.
      A conversion function has been added that takes a character
      value and returns its integer equivalent.  This is handy
      for comparing non-printing characters:

            if(ascii(line[x]) == '\t')
                ...
      A conversion method called 'asAsc' has been added to the expression
      chain for converting character values:

            if(line[x].asAsc() == '\t')
                ...
      The Scene Object Agent now has two iteration methods that are
      used to cycle through the objects in the current scene that
      are selected.  The methods firstSelect() and nextSelect()
      are used in an fashion identical to other iteration methods,
      like firstChild() and nextChild() in the Layout Object Agent.

            scene = getfirstitem(SCENE);

            obj = scene.firstSelect();
            while(obj)
            {
                ...
                obj = scene.nextSelect();
            }
      The Scene Object Agent now has a data member called
      'currenttime' that holds the time index that is currently
      selected in the Layout interface.
      The Scene Object Agent now has a data member called
      'recursiondepth' that holds the integer recursion depth
      used for rendering.
      The Layout Object Agent (the Object Agent returned for any
      scene object) sports four new data members to its interface.
      Each is a boolean flag that indicates the state of the
      indicated condition:

            selected
            childrenvisible
            channelsvisible
            locked
      All Layout Object Agents now export a data member called 'genus'
      that holds the type of the object.  It holds one of MESH, LIGHT,
      CAMERA, BONE, SCENE, or CHANNEL.
      The LScript pre-processor has a new testable condition called
      'platform'.  This value is one of INTEL, ALPHA, SGI, MACINTOSH, or
      SUN.  It can be tested with either the '==' or '!=' equality
      operators.

        ...
        @if platform == ALPHA
            info("Alpha!");
        @end
        @if platform == INTEL
            info("Intel!");
        @end
        ...
      The LScript pre-processor has a new testable condition called
      'compiler'.  This value is an either/or condition.  Either it exists,
      or it doesn't.  It can be tested for existence:

        ...
        @if compiler
            ...
        @end
        ...

      Or, it can be tested for non-existence:

        ...
        @if !compiler
            ...
        @end
        ...

      This condition is useful for hiding elements from the script
      compiler that are invalid in the run-time environment:

        ...
        @if !compiler
            debug();
        @end
        ...
      The AddPlugins() CS function accepts an optional string argument
      that should be the filename of a plug-in to be added.
      Layout Object Agents have a new data member called 'id' that holds
      the integer identifier of the Layout object for which they serve
      as proxy.  This integer identifier is the same one used by Layout
      to uniquely identify objects in the scene file.

        generic
        {
            obj = getfirstitem(LIGHT);
            info(hex(obj.id));         // prints "0x20000000"
        }
      Point and polygon selections in Modeler using POINTNDX, POLYNDX,
      POLYID and POINTID are now exact.  Element positions are no longer
      a factor.  This means that now you can discretely select a single
      point, even if it occupies the same space as one or more other
      points.
      The implicit 'this' container will capture and convert into an array
      any multiple-value function returns.  Such returns are made by functions
      like parse():

            ...
            parse(" ","Today is the day");
            info(this[3]);      // prints "the"
            ...

      The values contained in the this[] array are only valid, however, until
      the next expression is evaluated.  At that time, the array will be
      reclaimed by LScript's garbage collection mechanism, and a new value
      (possibly even a new array) will be put into its place.
      The regular expression assignment operator (~=) will support both types of
      regular expression (Search, and Search-And-Replace).  In the case of Search
      regular expressions, the variable on the left of the assignment is not
      modified; rather, this mechanism enables the 'this' container to be
      populated with the tokens that match the individual patterns of the
      expression (see the next entry for an example).
      The patterns matched within a Search regular expression are implicitly
      converted into a lexeme array held by the 'this' container when those
      patterns are enclosed within parentheses.  For example, the following
      invocation of the regular expression engine will not generate a lexeme
      array in the 'this' container because no parenthetical groupings are
      employed around the search patterns:

            ...
            command ~= s~[a-zA-Z]+ .+~;

            info(this[2]);      // <-- generates an error, "undeclared array"
            ...

      However, wrapping the patterns within parentheses will cause a lexeme
      array to be generated and assigned to 'this':

            ...
            command ~= s~([a-zA-Z]+) (.+)~;

            info(this[2]);      // <-- if the pattern matched, no error
            ...

      If the pattern fails to match on the provided string value, then the
      'this' container will not contain an array.  You can code for this
      condition by performing a logical test of the success of the pattern
      match before accessing 'this' as an array:

            ...
            if(command == s~([a-zA-Z]+) (.+)~)
                info(this[2]);      // <-- safe to access this[2]
            ...
      Arrays can now be the source of an associative assignment.

            ...
            colors = @"Red","Green","Blue"@;
            (red,green) = colors;   // assigns "Red" to red, and so on
            ...

      This allows the contents of the 'this' container to be accessed
      before the contents are overwritten.

            ...
            if(command == s~([a-zA-Z]+) (.+)~)
            {
                (item1,item2) = this;
                ...
      Several new Requester functions have been added to support
      user-drawing features in the panel.  The drawing functions
      can only be called from within the script's redraw callback.

            reqredraw("callback")

                This function establishes a callback function
                within the script that will be activated whenever
                the panel itself is redrawn by the Panels system.

            drawpixel(<color>,x,y)

                Draws a pixel on the panel at the specified
                point using the specified color

            drawline(<color>,x1,y1,x2,y2)

                Draws a line on the panel between the specified
                points using the specified color

            drawbox(<color>,x,y,w,h)

                Draws a box filled with the specified color having
                the specified width and height positioned at the
                specified point

            drawborder(x,y,w,h)

                Draws an unfilled, 3D box outline at the specified
                point using the specified width and height.  If
                height is 0, then a horizontal divider line will
                be drawn.

            drawtext("text",<color>,x,y)

                Draws the provided text using the specified color
                at the specified point
      The rotation for an object's pivot point can now be accessed
      using the PIVOTROT constant to the param() method, or by
      calling the new getPivotRotation() method.
      Two new floating-point buffers are avaialble to LScript Image
      Filter plug-ins.  Known as MOTIONX and MOTIONY, these buffers
      are the vector-based motion values for objects in the scene.
      Frame start/stop/step settings have been broken into separate
      categories for preview and render.  The previewstart, previewend,
      and previewstep Scene Object Agent data members represent the
      values used by the LightWave preview system (and entered into
      the corresponding edit fields on the user interface).  The
      renderstart, renderend, and renderstep values represent those
      used when performing an actual render of the scene.

      The render settings are synonymous with the deprecated framestart,
      frameend, and framestep data members.  These
      data members will be removed in a later release of LScript
      in favor of renderstart, renderend, and renderstep.  You are
      encouraged to replace any references to framestart, frameend,
      and framestep in your existing LScript v2.0 scripts, and to
      avoid their use in any future scripts.