Difference between revisions of "Resizer multi-prims"

From Second Life Wiki
Jump to: navigation, search
(Created page with '{{LSL Header}} All description in first comments of the script. Maybe my English is not so good (I'm French). Hope anyone will understand. Resizer <lsl> /* ====================...')
(No difference)

Revision as of 12:10, 7 October 2010

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

Resizer <lsl> /* =================================================================================== */ /* */ /* Resizer v1.2 */ /* */ /* 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 : */ /* 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 = FALSE; // On opensim, affect TRUE 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()
   {
       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;
           iEcouteMenu = llListen( iCanalMenu = iCanalComm - 10 - (integer)llFrand( 124578 ), "", llGetOwner(), "" );
           Menu();
       }
   }

} </lsl>