Join us in Outworldz at http://www.outworldz.com:9000 or follow us:

[Table of Contents]

Want to add a script or a project? Upload it and a half million people will see it and your name here this year.

Home   Show All
Category: Contributor: Creator
NPC TrueMirror  

TrueMirror

NPC True Mirror script//:License: CC-BY-NC-SA

Category: NPC
By : Aine Caoimhe
Created: 2015-07-24 Edited: 2015-07-24
Worlds: OpenSim

the Zip file

Download all files for TrueMirror
Contents are in zip format, with .LSL (text) source code and LSLEdit (text + Solution) formats.
Get file # 1. best-friends-left.bvh
Get file # 2. best-friends-left_mirrored.bvh
Get file # 3. Mirror IV.lsl
Get file # 4. railing-lean-left.bvh
Get file # 5. railing-lean-left_mirrored.bvh
Get file # 6. wash-hair-shower.bvh
Get file # 7. wash-hair-shower_mirrored.bvh
1 // Mirror script
2 // by Aine Caoimhe (c. LACM) July 2015
3 // Provided under Creative Commons Attribution-Non-Commercial-ShareAlike 4.0 International license.
4 // Please be sure you read and adhere to the terms of this license: https://creativecommons.org/licenses/by-nc-sa/4.0/
5
6 // Mods: Ferd Frederix tweaked it slightly to use the mirrored animations from a web site
7 // You can use the BVH Mirror web site at http://tali.appspot.com/bvh/bvhmirror.html to flip any animation around
8 // You can get free, open source animations at www.outworldz.com/download
9
10 //
11 // When an avatar sits on an object containing this script the avi will be cloned to an NPC who will also sit
12 // Paired aniamtions supplied in the object will be played, making it appear as though the NPC is the mirrored user
13 // If the sitter touches the mirror it will advance to the next pose (if there are more to play)
14 // when the sitter stands, the NPC is removed
15 // (Note: if sitter changes appearance during use the NPC will not change to copy that change...it will continue to appear as intiailly rezzed)
16 //
17 // Place this script in the ROOT prim which can either be the mirror object itself or an invisible prim placed at that location.
18 // It is assumed that the root prim's XZ-plane is the axis to be mirrored
19 //
20 // Add animations to the contents of the root prim in pairs where each pair consists of:
21 // - an animation for the sitter to play
22 // - an animation that mirrors this, that has the IDENTICAL NAME with " mirrored" (including the leading space) added after it
23 // example: "sit 1" and "sit 1 mirrored"
24 // DO NOT have a main sitter pose end with the word mirror because it would be interpretted as being a mirror pose and ignored
25 //
26 // ***** IF YOU ADD OR REMOVE ANIMATIONS YOU WILL NEED TO RESET THE SCRIPT TO PICK UP THE CHANGES!!! *****
27 //
28 // The sitter's pose position is determined by the base sitPos and sitRot settings (see user settings section below) UNLESS you
29 // overide these for an animation by placing new values in the DESCRIPTION field of the main pose in the format:
30 // <sitpose>::<sit rot> <--- note the double colon separator between position and rotation
31 // example: <0.0, 1.0, 0.0>::<0.0, 0.0, 0.0, 1.0>
32 // at which point the sitter will be moved to that sit target posision instead
33 //
34 // In all cases, the clone (mirror NPC) will be positioned exactly 180 rotated around the root prim's LOCAL Z axis
35 // If you have already rotated the mirror pose animation on axis when creating the mirror and do not need it subequently rotated
36 // when the clone NPC plays it, set the user variable mirrorRot to FALSE, otherwise leave it TRUE (this applies to all animations played
37 // so you can't use a mixture of both, sorry)
38
39 //
40 // USER SETTINGS
41
42 integer sayPoseName=FALSE; // TURE = owner will be told the name of the pose each time one is activated, FALSE = mirror will be silent
43 integer userName=FALSE; // TRUE = NPC will have the sitter's name, FALSE = NPC will have empty name
44 // use something like the Magic Sit Kit to set the value of these two
45 vector sitPos=<0.0, 1.0, 0.0>; // base sit target position to set for the main sitter relative to the mirror - NPC clone will mirror this position on the opposite side of the mirror
46 rotation sitRot=<0.0, 0.0, 0.0, 1.0>; // base sit target rotation to set for the main sitter relative to the mirror - NPC clone will mirror this on the opposite side of the mirror
47 integer mirrorRot=TRUE; // FALSE = the mirror animations you are supplying are already rotated on root; TRUE = they need to be rotated by the script
48 string baseAn="*****base__stand priority 1"; // name of a base priority 1 animation to use for underlying synch (no mirror pose of it is needed)
49 //
50 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
51 // DO NOT CHANGE ANYTHING BELOW HERE UNLESS YOU KNOW WHAT YOU'RE DOING
52 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
53 list poses;
54 list mirrors;
55 integer anIndex;
56 key user;
57 key npc;
58 integer linkNpc;
59 integer linkUser;
60
61 updatePositions()
62 {
63 vector pos=sitPos;
64 rotation rot=sitRot;
65 // see if this pose requires position override
66 string posOverride=osGetInventoryDesc(llList2String(poses,anIndex));
67 if(llSubStringIndex(posOverride,"::")>=0)
68 {
69 list orData=llParseString2List(posOverride,["::"],[]);
70 pos=llList2Vector(orData,0);
71 rot=llList2Rot(orData,1);
72 }
73 vector size = llGetAgentSize(user);
74 float fAdjust = ((((0.008906 * size.z) + -0.049831) * size.z) + 0.088967) * size.z;
75 vector newPos=(pos + <0.0, 0.0, 0.4>) - (llRot2Up(rot) * fAdjust);
76 llSetLinkPrimitiveParamsFast(linkUser, [PRIM_POS_LOCAL,newPos,PRIM_ROT_LOCAL, rot]);
77 newPos.y=-newPos.y;
78 if(mirrorRot)
79 {
80 vector aviRot=llRot2Euler(rot);
81 aviRot.z=-aviRot.z;
82 aviRot.x=-aviRot.x;
83 rot =llEuler2Rot(aviRot);
84 }
85 llSetLinkPrimitiveParamsFast(linkNpc, [PRIM_POS_LOCAL,newPos,PRIM_ROT_LOCAL, rot]);
86 if(sayPoseName) llOwnerSay("New pose: "+llList2String(poses,anIndex));
87 }
88 buildPoseList()
89 {
90 list an;
91 poses=[];
92 mirrors=[];
94 while(--i>=0) { an=[]+[llGetInventoryName(INVENTORY_ANIMATION,i)]+an; }
95 i=llListFindList(an,[baseAn]);
96 if(i==-1)
97 {
98 llOwnerSay("ERROR! Unable to locate the base priototy 1 aniamtion: "+baseAn);
99 user="ERROR";
100 }
101 else an=[]+llDeleteSubList(an,i,i);
102 i=llGetListLength(an);
103 while(--i>=0)
104 {
105 string name=llList2String(an,i);
106 if(llGetSubString(name,-7,-1)!=" mirrored") // tweak by FKB to use web site naming
107 {
108 integer m=llListFindList(an,[name+" mirrored"]);
109 if(m==-1) llOwnerSay("WARNING: found an animation with the name \""+name+"\" but no matching animation was located");
110 else
111 {
112 poses=[]+[name]+poses;
113 mirrors=[]+[name+" mirror"]+mirrors;
114 }
115 }
116 }
117 // double-check even though mismatch ought to be impossible;
118 if(llGetListLength(poses)!=llGetListLength(mirrors))
119 {
120 llOwnerSay("ERROR! Somehow build pose and mirror lists with mismatched lengths. Dump of data:\n\nPOSES: "+llDumpList2String(poses,", ")+"\nMIRRORS: "+llDumpList2String(mirrors,", "));
121 user="ERROR";
122 }
123 else
124 {
125 anIndex=0;
126 if(llGetListLength(poses)==1) llOwnerSay("Pose list built. Found only 1 mirrored pose in inventory so disabling touch advance");
127 else llOwnerSay("Pose list built. Found "+(string)llGetListLength(poses)+" mirrored pose in inventory");
128 }
129 }
130 cleanSitters()
131 {
132 user=NULL_KEY;
133 npc=NULL_KEY;
135 while(l>0)
136 {
137 key who=llGetLinkKey(l);
138 if(llGetAgentSize(who)==ZERO_VECTOR) l=0; // agents are always the last link numbers
139 else
140 {
141 if(osIsNpc(who)) osNpcRemove(who);
142 else
143 {
144 llRegionSayTo(who,0,"Sorry, the system has been reset so you will need to sit down again");
145 llUnSit(who);
146 }
147 }
148 l--;
149 }
150 }
151 default
152 {
154 {
155 buildPoseList();
156 llSitTarget(<0.0, 0.0, 0.000001>,ZERO_ROTATION);
158 if(user=="ERROR") llOwnerSay("Cannot active device until animation list building errors are correct");
159 else cleanSitters();
160 }
161 on_rez(integer foo)
162 {
164 }
166 {
167 key who=llDetectedKey(0);
168 if(who!=user)
169 {
170 if(user==NULL_KEY) llRegionSayTo(who,0,"Please sit on me to use me");
171 else llRegionSayTo(who,0,"Sorry, only the current user can touch me");
172 }
173 else
174 {
175 if(llGetListLength(poses)==1) return; // with only 1 pose no changing
176 osAvatarStopAnimation(user,llList2String(poses,anIndex));
177 osAvatarStopAnimation(npc,llList2String(mirrors,anIndex));
178 anIndex++;
179 if(anIndex>=llGetListLength(poses)) anIndex=0;
180 osAvatarPlayAnimation(user,llList2String(poses,anIndex));
181 osAvatarPlayAnimation(npc,llList2String(mirrors,anIndex));
182 updatePositions();
183 }
184 }
185 changed (integer change)
186 {
187 if(change & CHANGED_OWNER) llResetScript();
188 if(change & CHANGED_REGION_START) llResetScript();
189 if(change & CHANGED_INVENTORY) llOwnerSay("Detected a change in inventory. Remember to reset the script if you have changed the animations");
190 if(change & CHANGED_LINK)
191 {
192 // find out who is currently sitting
193 list aviSitters=[];
194 list npcSitters=[];
196 while(l>0)
197 {
198 key who=llGetLinkKey(l);
199 if(llGetAgentSize(who)==ZERO_VECTOR) l=0; // sitters are always last links
200 else
201 {
202 if(osIsNpc(who))
203 {
204 npcSitters=[]+[who]+npcSitters;
205 if(who==npc) linkNpc=l;
206 }
207 else
208 {
209 aviSitters=[]+[who]+aviSitters;
210 if(who==user) linkUser=l;
211 }
212 }
213 l--;
214 }
215 if(user==NULL_KEY)
216 {
217 l=llGetListLength(npcSitters);
218 while(--l>=0) { osNpcRemove(llList2Key(npcSitters,l)); }
219 npc=NULL_KEY;
220 if(llGetListLength(aviSitters)==0)
221 {
222 llSetClickAction(CLICK_ACTION_SIT); // back to sit as default action
223 return; // link change triggered by user standing or a change in some other linkset action
224 }
225 // getting here means a new avi sat
226 user=llList2Key(aviSitters,0);
227 osAvatarPlayAnimation(user,baseAn); // initiate base an
228 llSetClickAction(CLICK_ACTION_TOUCH); // now in use so default action should be touch
229 string first=" ";
230 string last=" ";
231 if(userName)
232 {
234 list parsedName=llParseString2List(nameToParse,[" ",".","@"],[]);
235 first=llList2String(parsedName,0);
236 last=llList2String(parsedName,1);
237 }
238 npc=osNpcCreate(first,last,llGetPos()+<0.0,0.0,1.0>,user);
239 osAvatarPlayAnimation(npc,baseAn);
240 osNpcSit(npc,llGetKey(),OS_NPC_SIT_NOW);
241 // everything else handled when NPC is detected as sitting
242 }
243 else if(user=="ERROR")
244 {
245 l=llGetListLength(npcSitters);
246 while(--l>=0) { osNpcRemove(llList2Key(npcSitters,l)); }
247 l=llGetListLength(aviSitters);
248 while(--l>=0)
249 {
250 llRegionSayTo(llList2Key(aviSitters,l),0,"Sorry, the mirror has encountered an error and needs to be reset");
251 llUnSit(llList2Key(aviSitters,l));
252 }
253 }
254 else
255 {
256 // we already have a user on record
257 if(llListFindList(aviSitters,[user])==-1)
258 {
259 // but no longer sitting so need to release animations, clear user, and remove NPC
261 {
262 osAvatarPlayAnimation(user,"Stand");
263 osAvatarStopAnimation(user,llList2Key(poses,anIndex));
264 osAvatarStopAnimation(user,baseAn);
265 }
266 user=NULL_KEY;
267 if(osIsNpc(npc))
268 {
269 osNpcRemove(npc);
270 npc=NULL_KEY;
271 }
272 llSetClickAction(CLICK_ACTION_SIT); // back to sit as default action
273 }
274 // make sure someone else didn't sit down when user was set
275 l=llGetListLength(aviSitters);
276 while(--l>=0)
277 {
278 if(llList2Key(aviSitters,l)!=user)
279 {
280 llRegionSayTo(llList2Key(aviSitters,l),0,"Sorry, the mirror is already being used by someone else");
281 llUnSit(llList2Key(aviSitters,l));
282 }
283 }
284 // prevent any NPC from sitting other than the current one on record
285 l=llGetListLength(npcSitters);
286 while(--l>=0) { if(llList2Key(npcSitters,l)!=npc) osNpcRemove(llList2Key(npcSitters,l)); }
287 if((npc!=NULL_KEY) && osIsNpc(npc))
288 {
289 // most of the time we only get here if a new NPC sat after initial creation so this should trigger start of animation
290 // need to wait briefly for base an to kick in
291 llSleep(0.25);
292 // then clear any existing AO/sit animations
293 key dontStop=llGetInventoryKey(baseAn);
294 list anToStop=llGetAnimationList(user);
295 l=llGetListLength(anToStop);
296 while(--l>=0) { if(llList2Key(anToStop,l)!=dontStop) osAvatarStopAnimation(user,llList2Key(anToStop,l)); }
297 anToStop=[]+llGetAnimationList(npc);
298 l=llGetListLength(anToStop);
299 while(--l>=0) { if(llList2Key(anToStop,l)!=dontStop) osAvatarStopAnimation(npc,llList2Key(anToStop,l)); }
300 // now start the mirrored animations
301 osAvatarPlayAnimation(user,llList2String(poses,anIndex));
302 osAvatarPlayAnimation(npc,llList2String(mirrors,anIndex));
303 updatePositions();
304 }
305 }
306 }
307 }
308 }

Back to the Best Free Tools in Second Life and OpenSim.