Resizer multi-prims

From Second Life Wiki
Revision as of 17:51, 16 October 2010 by Christy Mansbridge (talk | contribs) (Little change to allow compile on opensim)
Jump to navigation Jump to search

All description in first comments of the script. Maybe my English is not so good (I'm French). Hope anyone will understand.

Update on october 11th, 2010 Autodetect official LL grids or opensim

Resizer <lsl> /* =================================================================================== */ /* */ /* Resizer v1.4 */ /* */ /* License : */ /* Français */ /* OpenSource. Licence GPL 2 et plus. */ /* Modification, Copie et Transfert autorisés */ /* Sous condition de laisser le script en full perm (Modify / Copy / Transfer) */ /* */ /* English */ /* OpenSource. License GPL 2 and later. */ /* Modify, Copy and Transfer allowed if this script stay full perm */ /* */ /* Auteurs : Christy Mansbridge / Mingyar Ishtari, le 02 octobre 2010 */ /* */ /* Modifications / Updates : */ /* Author : Christy Mansbridge */ /* Date  : 11 octobre 2010 */ /* Detail : Auto detection opensim/LL grids (version 1.4) */ /* */ /* Author : */ /* Date  : */ /* Detail : */ /* */ /* Usage : */ /* Français */ /* À placer dans le prim root (prim coutouré en jaune à l'édition) */ /* Cliquer l'objet pour obtention du menu bleu. */ /* Usage restreint au seul propriétaire. */ /* */ /* English */ /* Place the script in the root prim of the build. */ /* Click the object to get the blue menu. */ /* Usage restricted to the owner. */ /* */ /* Fonctions : */ /* Français */ /* 1) Script de redimensionnement d'un objet multi-prims respectant les */ /* proportions, en taille et en position, de chaque prim. */ /* */ /* 2) Lag minimal puisqu'un seul script par objet (1 à 256 prims) */ /* Possibilité offerte de supprimer le script. */ /* */ /* 3) Communication avec d'autres objets pour synchroniser le redimensionnement */ /* Pour permettre cette communication, plusieurs critères douvent être remplis*/ /* a) L'objet doit être porté ainsi que celui/ceux à synchroniser */ /* b) Les objets doivent porter le même nom, à l'exception de la partie */ /* finale séparée du reste par un espace. */ /* Exemple : Chaussure rouge gauche et Chaussure rouge droite */ /* */ /* 4) Garantie de la conservation de l'intégrité de l'objet. */ /* a) Si un agrandissement entraine une destruction du build, */ /* Retour automatique à la taille avant agrandissement. */ /* */ /* b) Si l'objet contient au moins un huge prim le script reste inactif. */ /* Cette sécurité résevée à la grille officielle SecondLife peut être */ /* désactivée pour opensim en passant à TRUE l'affectation de la variable */ /* globale iAllowResizeHugePrims. */ /* */ /* 5) Prise en compte automatique des changements de liens (ajout/suppresion de */ /* prims ou redimensionnement manuel) */ /* */ /* 6) 18 choix de redimensionnement prédéfinis en pourcentages : */ /* -1, -2, -5, -10, -25, -50, -75, -85, -95. */ /* +1, +2, +5, +10, +25, +50, +75, +100, +200, */ /* */ /* 7) 3 choix extrèmes : Dimensionnement Minimal, Maximal et par Défaut. */ /* La dimension par défaut est celle à l'initialisation du script ou */ /* celle affectée par le menu [Set default]. */ /* */ /* 8) Détermination de la taille maximale avant déformation de la construction */ /* Pour trouver la taille maximale possible, procéder ainsi : */ /* Effectuer des agrandissements à +200% jusqu'à refus de dimensionnement */ /* Répéter l'opération avec les pourcentages inférieurs, +100% puis +75% */ /* puis 50% ... jusqu'à répétition de l'opétation à +1%. */ /* Après un refus de redimensionnement à +1% le script connait la vraie */ /* taille maximale. */ /* */ /* English */ /* 1) Script to resize a multi prims object with conservation of its integrity */ /* (size and relative position of each prim). */ /* */ /* 2) Less lag : only 1 script for an object (1 to 256 prims) */ /* Possibility to remove the script by the menu. */ /* */ /* 3) Communication with other objects to synchronize resize */ /* To allow communication, objects have to comply with few criteria : */ /* a) The object has to be worn, objects to synchronize too. */ /* b) Objects have to have same name except last part separated by space */ /* Example : Excelsior shoes Left and Excelsior shoes Right */ /* */ /* 4) Integrity waranty. */ /* a) If after increasing size the build is broken, automatic go back to */ /* previous good size. */ /* */ /* b) If, at least, one huge prim is in the build, the script is inactive. */ /* This security is only for official grid SecondLife and may be bypassed */ /* for opensim, affecting TRUE to the iAllowResizeHugePrims global variable*/ /* */ /* 5) Automatic detection of manual scale change and link changes */ /* */ /* 6) 18 predefined resize choice in percentage : */ /* -1, -2, -5, -10, -25, -50, -75, -85, -95. */ /* +1, +2, +5, +10, +25, +50, +75, +100, +200, */ /* */ /* 7) 3 other choices : Resize to Minimum, Maximum or Default size. */ /* The default size is the size at initialisation of the script or the size */ /* affected by the blue menu [Set default]. */ /* */ /* 8) How to find the real maximum size ? */ /* To find the real value : */ /* Make an increase size by +200% until the script says it fails. */ /* Then, repeat this method with +100%, then after with +75% ... */ /* After the last increase with +1% the real maximum size is found and */ /* stored by the script. */ /* */ /* =================================================================================== */

integer iAllowResizeHugePrims = -1; // You can force to FALSE on official SecondLife grids and TRUE on opensim integer iNumberOfPrims; integer iFirst; integer iLast; integer iBcl; integer iIdx; integer iDeb; integer iFin; integer iSens; float fTmp; vector vSize; vector vPos; float fMin; float fMax; float fFactor; float fDefault; integer iCanalComm = -8547548; integer iCanalMenu; integer iEcouteMenu; integer iMenuOn; integer iCurrentMenu; string sPctM1 = "1"; string sPctM2 = "2"; string sPctM3 = "5"; string sPctP1 = "1"; string sPctP2 = "2"; string sPctP3 = "5"; string sPct; integer iNbPasses; integer iHugeUsed; float fLimiteMax; integer iNoMoreMax; vector vPos1; integer iError; list lSortPrims; list lSizes; list lPos; list lElem; list lName;

// Hack to increase speed of the script (Mono) Booster() {

   for( iBcl = 0; iBcl < 5000; iBcl++ );

}

// Function to get size and/or relative position of the current prim GetSizePos() {

   vSize = llList2Vector( llGetLinkPrimitiveParams( iBcl, [ PRIM_SIZE ] ), 0 ) * fFactor;
   if( iBcl == iFirst )
   {
       if( llGetAttached() )

// Root prim and object attached to the avatar, position is local

           vPos = llGetLocalPos();
       else

// Root prim and object not attached, position is absolute position

           vPos = llGetRootPosition();
   }
   else

// Child prim, position is relative to the root prim

       vPos = ( ( llList2Vector( llGetLinkPrimitiveParams( iBcl, [ PRIM_POSITION ] ), 0 ) - llGetRootPosition() ) / llGetRootRotation() ) * fFactor;

}

// Function to control if current prim has expected size and relative position ControlSizePos( vector vRefSize, vector vRefPos ) {

   if( llVecDist( llList2Vector( llGetLinkPrimitiveParams( iBcl, [ PRIM_SIZE ] ), 0 ), vRefSize ) > 0.001 )
       iError = TRUE;
   else if( iBcl != iFirst )
   {
       if( llVecDist( ( llList2Vector( llGetLinkPrimitiveParams( iBcl, [ PRIM_POSITION ] ), 0 ) - llGetRootPosition() ) / llGetRootRotation(),
                      vRefPos ) > 0.001 )
       iError = TRUE;
   }

}

// Function to test resize validity of the current prim, if resize is over stored maximum size integer TestError() {

   if( fMax > fLimiteMax )
       ControlSizePos( vSize, vPos );
   return( iError );

}

// Function to control build integrity after resize ControlBuild( integer iBoost ) {

   if( iBoost )
       Booster();

// No individual error found // Control of all the build prim after prim

   for( iIdx = iDeb; iIdx != iFin; iIdx += iSens )
   {
       iBcl = llList2Integer( lSortPrims, iIdx );
       ControlSizePos( llList2Vector( lSizes, iIdx ) * fFactor, llList2Vector( lPos, iIdx ) * fFactor );
       if( iError )
       {
           if( iBoost )
               iIdx = iFin - iSens;
           return;
       }
   }

}

// Function to resize (only the size) each prim of the build ChangeSize( integer iCorrect ) {

   Booster();
   for( iIdx = iDeb; iIdx != iFin; iIdx += iSens )
   {
       iBcl = llList2Integer( lSortPrims, iIdx );
       GetSizePos();
       if( iCorrect )
           vPos /= fFactor;
       llSetLinkPrimitiveParamsFast( iBcl, [ PRIM_SIZE, vSize ] );
       if( TestError() )
           return;
   }

}

// Function to adapt relative position of each prim of the build ChangePos( integer iCorrect ) {

   Booster();
   for( iIdx = iDeb; iIdx != iFin; iIdx += iSens )
       if( ( iBcl = llList2Integer( lSortPrims, iIdx ) ) != iFirst )
       {
           GetSizePos();
           vPos1 = vPos / fFactor;
           if( iCorrect )
               vSize /= fFactor;
           llSetLinkPrimitiveParamsFast( iBcl, [ PRIM_POSITION, vPos, PRIM_POSITION, vPos, PRIM_POSITION, vPos ] );
           if( TestError() )
               return;
       }

}

// Function to resize and adapt relative position of each prim of the build ChangeSizePos() {

   Booster();
   for( iIdx = iDeb; iIdx != iFin; iIdx += iSens )
   {
       iBcl = llList2Integer( lSortPrims, iIdx );
       GetSizePos();
       llSetLinkPrimitiveParamsFast( iBcl, [ PRIM_SIZE, vSize, PRIM_POSITION, vPos ] );
       if( TestError() )
           return;
   }

}

// Function to resize and position each prim to the previous stored good sizes and relative positions after an error GoBackToGoodSize() {

   llOwnerSay( "Error, back to previous good size..." );
   sPct = "aborted";
   if( llRound( fFactor * 100.0 ) == 101 )
       iNoMoreMax = TRUE;
   fMin     /= fFactor;
   fMax     /= fFactor;
   iFin      = iDeb - iSens;
   iDeb      = iIdx;
   iSens    *= -1;
   fFactor   = 1.0;
   iNbPasses = 3;
   if( iNoMoreMax )
       fLimiteMax = fMax;
   do
   {
       Booster();
       iError = FALSE;
       for( iIdx = iDeb; iIdx != iFin; iIdx += iSens )
       {
           iBcl = llList2Integer( lSortPrims, iIdx );
           lElem = [ PRIM_SIZE, llList2Vector( lSizes, iIdx ) ];
           if( iBcl != iFirst )
           {
               vPos  = llList2Vector( lPos, iIdx );
               lElem += [ PRIM_POSITION, vPos, PRIM_POSITION, vPos, PRIM_POSITION, vPos ];
           }
           llSetLinkPrimitiveParamsFast( iBcl, lElem );
       }
       ControlBuild( FALSE );
   }
   while( iError && --iNbPasses );
   if( iError )
       llOwnerSay( "Sorry, the build is broken and can't be repaired." );

}

// Function to store size and relative positions of each prim after a successfull resizing SaveSizePos() {

   Booster();
   fFactor = 1.0;
   lSizes  = [];
   lPos    = [];
   for( iIdx = 0; iIdx != iNumberOfPrims; iIdx++ )
   {
       iBcl = llList2Integer( lSortPrims, iIdx );
       GetSizePos();
       lSizes += [ vSize ];
       lPos   += [ vPos  ];
   }

}

// Function to resize the build Resize( integer iMess ) { // Apply factor of resizing to the min and max size found in the build

   fMin *= fFactor;
   fMax *= fFactor;
   sPct = "done";
   if( fFactor < 1.0 )
   {

// If resize is to decrease the size, we start whith the most distant prims from the root

       iDeb  = llGetListLength( lSortPrims ) - 1;
       iFin  = -1;
       iSens = -1;
   }
   else
   {

// If resize is to increase the size, we start whith the closer prims from the root

       iDeb  = 0;
       iFin  = llGetListLength( lSortPrims );
       iSens = 1;
   }
   iError = FALSE;
   if( fFactor < 0.5 )

// if decrease is more than a factor 2, we start by changing only relative positions

       ChangePos( FALSE );
   else if( fFactor > 2.0 )

// if increase is more than a factor 2, we start by changing only sizes

       ChangeSize( FALSE );
   else

// if factor is less or equal than 2, resize is both size and position

       ChangeSizePos();
   if( ! iError )
   {
       if( fFactor < 0.5 )

// Changing positions is successfull, we change positions

           ChangeSize( TRUE );
       else if( fFactor > 2.0 )

// Changing sizes is successfull, we change sizes

           ChangePos( TRUE );
   }
   if( ! iError )
       ControlBuild( TRUE );
   if( iError )

// Restore the last good size after error

       GoBackToGoodSize();
   else
   {

// Save the sizes and relative positions of each prim

       SaveSizePos();
       if( fMax == 10.0 && ! iAllowResizeHugePrims )
       {
           iNoMoreMax = TRUE;
           fLimiteMax = fMax;
       }
   }
   if( fMax > fLimiteMax )

// New max size found

       fLimiteMax = fMax;

// Start countdown for CHANGED_SCALE event

   llResetTime();
   if( iMess )
   {

// Inform user the resize is finished and call back the menu

       llOwnerSay( "Resize " + sPct + "." );
       Menu();
   }

}

AlreadyAt( integer iMess, string sMess ) {

   if( iMess )
   {
       llOwnerSay( "Already at " + sMess + " size." );
       Menu();
   }

}

// Function to resize to the minimum size MinimumSize( integer iMess ) {

   if( ( fFactor = 0.01 / fMin ) != 1.0 )
   {
       if( iMess )
       {
           sPct = (string)llRound( ( fFactor - 1.0 ) * 100.0 );
           if( fFactor > 1.0 )
               sPct = "+" + sPct;
           llOwnerSay( "Minimum size (" + sPct + "%)..." );
       }
       Resize( iMess );
   }
   else
       AlreadyAt( iMess, "minimum" );

}

// Function to resize to the maximum size MaximumSize( integer iMess ) {

   if( ( fFactor = fLimiteMax / fMax ) != 1.0 )
   {
       if( iMess )
       {
           sPct = (string)llRound( ( fFactor - 1.0 ) * 100.0 );
           if( fFactor > 1.0 )
               sPct = "+" + sPct;
           llOwnerSay( "Maximum size (" + sPct + "%)..." );
       }
       Resize( iMess );
   }
   else
       AlreadyAt( iMess, "maximum" );

}

// Function to resize to the default size DefaultSize( integer iMess ) {

   vSize = llList2Vector( llGetLinkPrimitiveParams( iFirst, [ PRIM_SIZE ] ), 0 );
   if( ( fFactor = fDefault / vSize.x ) != 1.0 )
   {
       if( iMess )
       {
           sPct = (string)llRound( ( fFactor - 1.0 ) * 100.0 );
           if( fFactor > 1.0 )
               sPct = "+" + sPct;
           llOwnerSay( "Default size (" + sPct + "%)..." );
       }
       Resize( iMess );
   }
   else
       AlreadyAt( iMess, "default" );

}

// Close the menu FinMenu( integer iAlert ) {

   iMenuOn = FALSE;
   llSetTimerEvent( 0.0 );
   llListenRemove( iEcouteMenu );
   if( iAlert )
       llOwnerSay( "Menu timeout." );

}

// Main menu Menu() {

   llSetTimerEvent( 30.0 );
   llDialog( llGetOwner(),
             "Resizer action :\n" +
             "Quit : Leave this menu.\n" +
             "Options : Current size becomes default.\n" +
             ">> : Next set of percentages.\n" +
             "Min : Resize to minimum size.\n" +
             "Default : Return to initial size.\n" +
             "Max : Resize to maximum size.",
             [ "Quit", "Options", ">>",
               "-" + sPctM1 + "%", "Min",     "+" + sPctP1 + "%",
               "-" + sPctM2 + "%", "Default", "+" + sPctP2 + "%",
               "-" + sPctM3 + "%", "Max",     "+" + sPctP3 + "%" ],
             iCanalMenu );

}

// Function to return min or max element of vSize vector float MinMaxVal( integer iOperation ) {

   return( llListStatistics( iOperation, [ vSize.x, vSize.y, vSize.z ] ) );

}

// Initialisation of the script init() {

   Booster();
   iNumberOfPrims = llGetNumberOfPrims();
   iFirst         = (integer)( iNumberOfPrims > 1 );
   iLast          = iNumberOfPrims - (integer)( iNumberOfPrims == 1 );
   llOwnerSay( "Searching min/max prim size..." );
   fMin    = 10.0;
   fMax    = 0.01;
   fFactor = 1.0;
   for( iBcl = iFirst; ( fMax <= 10.0 || iAllowResizeHugePrims ) && iBcl <= iLast; iBcl++ )
   {
       GetSizePos();
       if( iBcl == iFirst )
       {
           fDefault = vSize.x;
           lElem = [ 0.0, iFirst ];
       }
       else
           lElem += [ llVecDist( ZERO_VECTOR, vPos ), iBcl ];
       fTmp = MinMaxVal( LIST_STAT_MIN );
       if( fTmp < fMin )
           fMin = fTmp;
       fTmp = MinMaxVal( LIST_STAT_MAX );
       if( fTmp > fMax )
           fMax = fTmp;
   }
   if( fMax > 10.0 && ! iAllowResizeHugePrims )
   {
       iHugeUsed = TRUE;
       llOwnerSay( "Script not active, object uses huge prim." );
   }
   else
   {
       llOwnerSay( "Size range : " + (string)fMin + " -> " + (string)fMax );
       iNoMoreMax = FALSE;
       iHugeUsed  = FALSE;
       llOwnerSay( "Sorting prims..." );
       lElem = llListSort( lElem, 2, TRUE );
       lSortPrims = [];
       for( iBcl = 1; iBcl < llGetListLength( lElem ); iBcl += 2 )
           lSortPrims += [ llList2Integer( lElem, iBcl ) ];
       llOwnerSay( (string)iNumberOfPrims + " prim" + llList2String( [ "", "s" ], (integer)(iNumberOfPrims > 1) ) + " sorted." );
       fLimiteMax = llListStatistics( LIST_STAT_MAX, [ 10.0, fMax ] );
       llOwnerSay( "Storing size/pos of each prim..." );
       SaveSizePos();
   }
   llOwnerSay( "Init. complete." );

}

default {

   state_entry()
   {
       if( iAllowResizeHugePrims < 0 )
           iAllowResizeHugePrims = (integer)( llSubStringIndex( llGetSimulatorHostname(), ".lindenlab.com" ) < 0 );
       init();
       llListen( iCanalComm, "", NULL_KEY, "" );
   }
   changed( integer iChange )
   {
       if(   ( iChange & CHANGED_LINK  ) ||
           ( ( iChange & CHANGED_SCALE ) &&
             llGetTime() > 5.0 / llList2Float( [ llGetRegionTimeDilation(), 0.01 ],
                                               (integer)( llGetRegionTimeDilation() < 0.01 ) ) ) )
           init();
   }
   listen( integer iChannel, string sName, key kId, string sMess )
   {
       if( iChannel == iCanalMenu )
       {
           llSetTimerEvent(  0.0 );
           if( sMess == ">>" )
           {
               if( (++iCurrentMenu) == 3 )
                   iCurrentMenu = 0;
               if( ! iCurrentMenu )
               {
                   sPctM1 = "1";
                   sPctM2 = "2";
                   sPctM3 = "5";
                   sPctP1 = "1";
                   sPctP2 = "2";
                   sPctP3 = "5";
               }
               else if( iCurrentMenu == 1 )
               {
                   sPctM1 = "10";
                   sPctM2 = "25";
                   sPctM3 = "50";
                   sPctP1 = "10";
                   sPctP2 = "25";
                   sPctP3 = "50";
               }
               else
               {
                   sPctM1 = "75";
                   sPctM2 = "85";
                   sPctM3 = "95";
                   sPctP1 = "75";
                   sPctP2 = "100";
                   sPctP3 = "200";
               }
               Menu();
           }
           else if( sMess == "Options" || sMess == " " )
           {
               llSetTimerEvent( 30.0 );
               llDialog( llGetOwner(),
                         "\nResizer options :\n \n" +
                         "Quit : Leave this menu.\n" +
                         "BACK : To main menu.\n" +
                         "Set default : Current size is default.\n" +
                         "Remove : Remove the script.",
                         [ "Quit", " ", "BACK",
                           "Set default", " ", "Remove" ],
                         iCanalMenu );
           }
           else if( sMess == "BACK" )
               Menu();
           else if( sMess == "Set default" )
           {
               vSize = llList2Vector( llGetLinkPrimitiveParams( iFirst, [ PRIM_SIZE ] ), 0 );
               fDefault = vSize.x;
               if( llGetAttached() )
                   llSay( iCanalComm, (string)llGetOwner() + "|Default|" + (string)fDefault );
               llOwnerSay( "This size is now the default size." );
               Menu();
           }
           else if( sMess == "Remove" )
           {
               llSetTimerEvent( 30.0 );
               llDialog( llGetOwner(),
                         "\nSure to remove the script from this object ?",
                         [ "Yes", "No" ],
                         iCanalMenu );
           }
           else if( sMess == "Default" )
               DefaultSize( TRUE );
           else if( sMess == "Min" )
               MinimumSize( TRUE );
           else if( sMess == "Max" )
               MaximumSize( TRUE );
           else if( ( iBcl = llListFindList( [ "+", "-" ], [ llGetSubString( sMess, 0, 0 ) ] ) ) >= 0 )
           {
               fFactor = 1.0 + (float)llGetSubString( sMess, 0, -2 ) / 100.0;
               fTmp    = llList2Float( [ llList2Float( [ 10.0, 10000.0 ], iAllowResizeHugePrims ), fLimiteMax ], iNoMoreMax );
               if( fMin * fFactor < 0.01 )
                   fFactor = 0.01 / fMin;
               else if( fMax * fFactor > fTmp )
                   fFactor = fTmp / fMax;
               if( fMin * fFactor >= 0.01 && fMax * fFactor <= fTmp && llRound( fFactor * 100.0 ) != 100 )
               {
                   sPct = (string)llRound( ( fFactor - 1.0 ) * 100.0 ) + "%";
                   if( fFactor > 1.0 )
                       sPct = "+" + sPct;
                   llOwnerSay( llList2String( [ "Increas", "Reduc" ], iBcl ) + "ing size : " +
                               sPct + llList2String( [ " (" + sMess + " asked)", "" ], (integer)(sMess == sPct) ) + "..." );
                   if( llGetAttached() )
                       llSay( iCanalComm, (string)llGetOwner() + "|Factor|" + (string)fFactor );
                   Resize( TRUE );
               }
               else
               {
                   llOwnerSay( "Out of size range." );
                   Menu();
               }
           }
           else
           {
               FinMenu( FALSE );
               if( sMess == "Yes" )
               {
                   if( llGetAttached() )
                       llSay( iCanalComm, (string)llGetOwner() + "|Remove" );
                   llRemoveInventory( llGetScriptName() );
               }
           }
       }
       else if( llGetAttached() && sName != llGetObjectName() )
       {
           lElem = llParseString2List( sName, [ " " ], [ "" ] );
           if( llGetListLength( lElem ) < 2 )
               return;
           lName = llParseString2List( llGetObjectName(), [ " " ], [ "" ] );
           if( llGetListLength( lName ) < 2 )
               return;
           if( llDumpList2String( llList2List( lElem, 0, -2 ), " " ) !=
               llDumpList2String( llList2List( lName, 0, -2 ), " " ) )
               return;
           lElem = llParseString2List( sMess, [ "|" ], [ "" ] );
           if( llList2String( lElem, 0 ) == (string)llGetOwner() )
           {
               if( llList2String( lElem, 1 ) == "Remove" )
                   llRemoveInventory( llGetScriptName() );
               else if( llList2String( lElem, 1 ) == "Factor" )
               {
                   fFactor = llList2Float( lElem, 2 );
                   Resize( FALSE );
                   llResetTime();
               }
               else if( llList2String( lElem, 1 ) == "Min" )
                   MinimumSize( FALSE );
               else if( llList2String( lElem, 1 ) == "Max" )
                   MaximumSize( FALSE );
               else if( llList2String( lElem, 1 ) == "Default" )
               {
                   if( llGetListLength( lElem ) == 2 )
                       DefaultSize( FALSE );
                   else
                       fDefault = llList2Float( lElem, 2 );
               }
           }
       }
   }
   timer()
   {
       FinMenu( TRUE );
   }
   touch_start( integer iNum )
   {
       if( llDetectedKey( 0 ) == llGetOwner() && ! iMenuOn && ! iHugeUsed )
       {

//llOwnerSay( (string)llGetFreeMemory() );

           iMenuOn     = TRUE;
           iCanalMenu  = iCanalComm - 10 - (integer)llFrand( 124578 );
           iEcouteMenu = llListen( iCanalMenu, "", llGetOwner(), "" );
           Menu();
       }
   }

} </lsl>