User:Nandana Singh/nPose

From Second Life Wiki
Jump to navigation Jump to search

nPose vX.xx.xx

nPose is a system for coordinating animations between several avatars seated on the same object, such as a couch or bed. Unlike previous systems, nPose does not use poseballs to accomplish this.

Scripts, examples and updates are sent out via GridShout, which you can join by signing up http://npose.gridshout.com or by touching the GridShout sign at http://slurl.com/secondlife/Tovaris/51/206/28.

WHAT IS THIS AND WHY SHOULD I CARE?

For a long time the SL wiki has had a "UpdateSitTarget" trick that allows you to move an av seated on a prim using llSetLinkPrimitiveParams (treating the av as a linked prim). Lots of single-user furniture uses this to switch the avatar's position as he/she changes poses.

The nPose system applies this to multi-user furniture. Sit on it to pose. Click on it for a menu of poses.

There are several advantages to posing the avatar sitting on your object instead of having the avatar sit on a rezzed poseball:

1 - Blue anim permission dialogs SUCK. Animation permissions are automatically granted if the av is sitting on your prim instead of a separate poseball, so users never have to be bothered by a blue popup. This is especially important when you consider that script dialogs and animation permission dialogs use the same part of the SL screen, so it's very common for people to accidentally refuse anim permissions when they think they're clicking on the MLP menu, or accidentally click an MLP menu button when they're trying to grant animation permissions. By getting rid of the permissions dialog we eliminate this problem.

2 - When your object moves, so do the avatars. In rezzed-balls systems like MLP, the unlinked poseballs and avatars seated on them are left behind when the base moves. nPose allows you to re-arrange your furniture while sitting on it, without having to stand and re-rez the balls. Even better, nPose will allow you to build a multi pose system into a vehicle! (Think of a multi pose parade float, or a pleasure yacht, or a party bus).

3 - No need to spend part of your parcel's prim budget on rezzed poseballs.

4 - Fewer calls to llListen (previously used for communicating with poseballs) and fewer total scripts mean less lag on the sim.

INSTRUCTIONS

To set up your object with nPose, do the following:

1 - Put all the animations in your object.

2 - For each pose set, create a notecard named in the format "SET:<category>:<name>" (without the quotes). Look inside the pink nPose object for examples. You can name one of your cards with the format "DEFAULT:<category>:<name>". The core will then load that pose set when it first starts up. (See http://code.google.com/p/npose/wiki/nPoseMenuConfiguration for more elaborate menu examples).

3 - On each line in the notecard you will define an animation, position, rotation, and face anims for an avatar. Open the notecards in the pink nPose object for examples. Do not worry about setting exact positions and rotations yet. You can use the adjustment tool for that later.

4 - Add the nPose scripts to your object (Core, Dialog, Menu Control). Add a copy of the nPose Slave script for each simultaneously sitting av. (E.g., if there are only couples sets in your object, you only need two slave scripts. For an eight person square dance, you'd need eight slave scripts.)

5 - Touch your object and use the menu to select your pose set. Then click to the Admin menu and select Adjust. You will see a multicolored object rezzed for each pose in the set. You can edit and move these around to re-position avatars. Each time you move one of these handles, the base will chat out the new line with the position and rotation. Copy and paste this into the set notecard, overwriting the old line for that slot. The new position is not permanently recorded until you've saved it to the notecard.

Basic nPose scripts (Core, Dialog, Menu Control and Slave):

nPose Core

<lsl>/*//( nPose Core 1.27 )//*/

////on getting DOPOSE, read NC from string, fill in data structure, position/animate avs if present //on av sit, position/animate. So we'll need

integer DIALOG = -300000; integer DIALOG_RESPONSE = -300002; integer DIALOG_TIMEOUT = -300003;

integer DOPOSE = 200; integer ADJUST = 201; integer SWAP = 202; integer KEYBOARD_CONTROL = 203; integer DUMP = 204; integer STOPADJUST = 205; integer SYNC = 206; integer DOACTIONS = 207;

integer SLAVEOFFSET = 700000;

string cardprefix = "SET:"; string defaultprefix = "DEFAULT:"; string card; integer line; key dataid; key cardid;//for recognizing adjustments automatically

//for reading BTN notecards that have non-pose commands string btncard; integer btnline; key btnid; key clicker;//person who clicked an action button

//a 7-strided list with those columns: Anim, Pos, Rot, Faceanim, Av, SatMsg, NotSatMsg //Av is the key of the av seated in that slot, else "" //Anim and Faceanim may be tilde ("~") delimited lists. list slots; integer stride = 7;

list adjusters;//when aduster objects are rezzed, their keys are stored here in parallel to slots list

integer primcount;

integer chatchannel; integer listener;

integer rezadjusters;

integer swapcount;//increment/loop on getting SWAP message. On loading pose, rotate avs through slots by this much

list SeatedAvs() {//like AvCount() but returns a list of seated avs, starting with lowest link number and moving up from there

   list avs;
   integer linkcount = llGetNumberOfPrims();
   integer n;
   for (n = linkcount; n >= 0; n--)
   {
       key id = llGetLinkKey(n);
       if (llGetAgentSize(id) != ZERO_VECTOR)
       {
           //it's a real av. add to list
           avs = [id] + avs;//adding it this way prevents having to reverse the av list later
       }
       else
       {
           //we've gotten down to a regular prim.  Break loop and return list
           return avs;
       }
   }
   //there must not have been anyone seated.  Shouldn't ever get here but LSL doesn't know that and wants a return value
   return [];

}

integer AvCount() {

   //returns the number of avatars sitting on the object
   integer linkcount = llGetNumberOfPrims();
   integer counter = linkcount;
   while (llGetAgentSize(llGetLinkKey(counter)) != ZERO_VECTOR)
   {
       counter--;
   }
   return linkcount - counter;

}


UpdateSlots() {

   //get list of seated avs
   //stick into an empty slot in slots if there is one
   //if not, unseat the av
   list avs = SeatedAvs();
   list avqueue = avs;//a copy used for populating slots
   integer n;
   integer stop = llGetListLength(slots);
   
   for (n = 0; n < stop; n += stride)
   {
       string slot = llList2String(slots, n + 4);
       if (slot == "")
       {
           //this slot is empty.  stick the next av into it, if there's one waiting in queue
           if (llGetListLength(avqueue))
           {
               //find the first av in queue that is not already in slots and stick it into this slot
               integer counter = 0;
               integer found = FALSE;
               while (!found && llGetListLength(avqueue))
               {
                   key av = llList2Key(avqueue, 0);
                   integer slots_index = llListFindList(slots, [av]);
                   if (slots_index == -1)
                   {
                       slots = llListReplaceList(slots, [av], n + 4, n + 4);
                       found = TRUE;
                   }
                   avqueue = llDeleteSubList(avqueue, 0, 0);
               }
           }
       }
       else
       {
           //there's an av in this slot.  Make sure he/she is still seated
           integer index = llListFindList(avs, [(key)slot]);
           if (index == -1)
           {                
               //av in slots is no longer seated.  assign slot to someone who is
               if (llGetListLength(avqueue))
               {
                   //find the first av in queue that is not already in slots and stick it into this slot
                   integer counter = 0;
                   integer found = FALSE;
                   while (!found && llGetListLength(avqueue))
                   {
                       key av = llList2Key(avqueue, 0);
                       integer slots_index = llListFindList(slots, [av]);
                       if (slots_index == -1)
                       {
                           slots = llListReplaceList(slots, [av], n + 4, n + 4);
                           found = TRUE;
                       }
                       avqueue = llDeleteSubList(avqueue, 0, 0);
                   }
               }             
               else
               {
                   //there's no one in the queue to fill this slot.   Set it to blank
                   slots = llListReplaceList(slots, [""], n + 4, n + 4);                    
               }
           }
           else
           {
               //slot has an av in it, and that av is still seated.  pull them out of the queue
               integer queueindex = llListFindList(avqueue, [(key)slot]);
               if (queueindex != -1)
               {
                   avqueue = llDeleteSubList(avqueue, queueindex, queueindex);
               }
           }
       }
       
llMessageLinked(LINK_SET, SLAVEOFFSET + n / stride, llDumpList2String(llList2List(slots, n, n + 4), "